PageRenderTime 187ms CodeModel.GetById 41ms app.highlight 130ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/groff/src/roff/troff/env.cpp

https://bitbucket.org/freebsd/freebsd-head/
C++ | 3893 lines | 3496 code | 263 blank | 134 comment | 897 complexity | c07b344fa412b4f06e5daa000c8d6c87 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1// -*- C++ -*-
   2/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005
   3   Free Software Foundation, Inc.
   4     Written by James Clark (jjc@jclark.com)
   5
   6This file is part of groff.
   7
   8groff is free software; you can redistribute it and/or modify it under
   9the terms of the GNU General Public License as published by the Free
  10Software Foundation; either version 2, or (at your option) any later
  11version.
  12
  13groff is distributed in the hope that it will be useful, but WITHOUT ANY
  14WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  16for more details.
  17
  18You should have received a copy of the GNU General Public License along
  19with groff; see the file COPYING.  If not, write to the Free Software
  20Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
  21
  22#include "troff.h"
  23#include "dictionary.h"
  24#include "hvunits.h"
  25#include "stringclass.h"
  26#include "mtsm.h"
  27#include "env.h"
  28#include "request.h"
  29#include "node.h"
  30#include "token.h"
  31#include "div.h"
  32#include "reg.h"
  33#include "charinfo.h"
  34#include "macropath.h"
  35#include "input.h"
  36#include <math.h>
  37
  38symbol default_family("T");
  39
  40enum { ADJUST_LEFT = 0, ADJUST_BOTH = 1, ADJUST_CENTER = 3, ADJUST_RIGHT = 5 };
  41
  42enum { HYPHEN_LAST_LINE = 2, HYPHEN_LAST_CHARS = 4, HYPHEN_FIRST_CHARS = 8 };
  43
  44struct env_list {
  45  environment *env;
  46  env_list *next;
  47  env_list(environment *e, env_list *p) : env(e), next(p) {}
  48};
  49
  50env_list *env_stack;
  51const int NENVIRONMENTS = 10;
  52environment *env_table[NENVIRONMENTS];
  53dictionary env_dictionary(10);
  54environment *curenv;
  55static int next_line_number = 0;
  56extern int suppress_push;
  57extern statem *get_diversion_state();
  58
  59charinfo *field_delimiter_char;
  60charinfo *padding_indicator_char;
  61
  62int translate_space_to_dummy = 0;
  63
  64class pending_output_line {
  65  node *nd;
  66  int no_fill;
  67  int was_centered;
  68  vunits vs;
  69  vunits post_vs;
  70  hunits width;
  71#ifdef WIDOW_CONTROL
  72  int last_line;		// Is it the last line of the paragraph?
  73#endif /* WIDOW_CONTROL */
  74public:
  75  pending_output_line *next;
  76
  77  pending_output_line(node *, int, vunits, vunits, hunits, int,
  78		      pending_output_line * = 0);
  79  ~pending_output_line();
  80  int output();
  81
  82#ifdef WIDOW_CONTROL
  83  friend void environment::mark_last_line();
  84  friend void environment::output(node *, int, vunits, vunits, hunits, int);
  85#endif /* WIDOW_CONTROL */
  86};
  87
  88pending_output_line::pending_output_line(node *n, int nf, vunits v, vunits pv,
  89					 hunits w, int ce,
  90					 pending_output_line *p)
  91: nd(n), no_fill(nf), was_centered(ce), vs(v), post_vs(pv), width(w),
  92#ifdef WIDOW_CONTROL
  93  last_line(0),
  94#endif /* WIDOW_CONTROL */
  95  next(p)
  96{
  97}
  98
  99pending_output_line::~pending_output_line()
 100{
 101  delete_node_list(nd);
 102}
 103
 104int pending_output_line::output()
 105{
 106  if (trap_sprung_flag)
 107    return 0;
 108#ifdef WIDOW_CONTROL
 109  if (next && next->last_line && !no_fill) {
 110    curdiv->need(vs + post_vs + vunits(vresolution));
 111    if (trap_sprung_flag) {
 112      next->last_line = 0;	// Try to avoid infinite loops.
 113      return 0;
 114    }
 115  }
 116#endif
 117  curenv->construct_format_state(nd, was_centered, !no_fill);
 118  curdiv->output(nd, no_fill, vs, post_vs, width);
 119  nd = 0;
 120  return 1;
 121}
 122
 123void environment::output(node *nd, int no_fill_flag,
 124			 vunits vs, vunits post_vs,
 125			 hunits width, int was_centered)
 126{
 127#ifdef WIDOW_CONTROL
 128  while (pending_lines) {
 129    if (widow_control && !pending_lines->no_fill && !pending_lines->next)
 130      break;
 131    if (!pending_lines->output())
 132      break;
 133    pending_output_line *tem = pending_lines;
 134    pending_lines = pending_lines->next;
 135    delete tem;
 136  }
 137#else /* WIDOW_CONTROL */
 138  output_pending_lines();
 139#endif /* WIDOW_CONTROL */
 140  if (!trap_sprung_flag && !pending_lines
 141#ifdef WIDOW_CONTROL
 142      && (!widow_control || no_fill_flag)
 143#endif /* WIDOW_CONTROL */
 144      ) {
 145    curenv->construct_format_state(nd, was_centered, !no_fill_flag);
 146    curdiv->output(nd, no_fill_flag, vs, post_vs, width);
 147  } else {
 148    pending_output_line **p;
 149    for (p = &pending_lines; *p; p = &(*p)->next)
 150      ;
 151    *p = new pending_output_line(nd, no_fill_flag, vs, post_vs, width,
 152				 was_centered);
 153  }
 154}
 155
 156// a line from .tl goes at the head of the queue
 157
 158void environment::output_title(node *nd, int no_fill_flag,
 159			       vunits vs, vunits post_vs,
 160			       hunits width)
 161{
 162  if (!trap_sprung_flag)
 163    curdiv->output(nd, no_fill_flag, vs, post_vs, width);
 164  else
 165    pending_lines = new pending_output_line(nd, no_fill_flag, vs, post_vs,
 166					    width, 0, pending_lines);
 167}
 168
 169void environment::output_pending_lines()
 170{
 171  while (pending_lines && pending_lines->output()) {
 172    pending_output_line *tem = pending_lines;
 173    pending_lines = pending_lines->next;
 174    delete tem;
 175  }
 176}
 177
 178#ifdef WIDOW_CONTROL
 179
 180void environment::mark_last_line()
 181{
 182  if (!widow_control || !pending_lines)
 183    return;
 184  pending_output_line *p;
 185  for (p = pending_lines; p->next; p = p->next)
 186    ;
 187  if (!p->no_fill)
 188    p->last_line = 1;
 189}
 190
 191void widow_control_request()
 192{
 193  int n;
 194  if (has_arg() && get_integer(&n))
 195    curenv->widow_control = n != 0;
 196  else
 197    curenv->widow_control = 1;
 198  skip_line();
 199}
 200
 201#endif /* WIDOW_CONTROL */
 202
 203/* font_size functions */
 204
 205size_range *font_size::size_table = 0;
 206int font_size::nranges = 0;
 207
 208extern "C" {
 209
 210int compare_ranges(const void *p1, const void *p2)
 211{
 212  return ((size_range *)p1)->min - ((size_range *)p2)->min;
 213}
 214
 215}
 216
 217void font_size::init_size_table(int *sizes)
 218{
 219  nranges = 0;
 220  while (sizes[nranges*2] != 0)
 221    nranges++;
 222  assert(nranges > 0);
 223  size_table = new size_range[nranges];
 224  for (int i = 0; i < nranges; i++) {
 225    size_table[i].min = sizes[i*2];
 226    size_table[i].max = sizes[i*2 + 1];
 227  }
 228  qsort(size_table, nranges, sizeof(size_range), compare_ranges);
 229}
 230
 231font_size::font_size(int sp)
 232{
 233  for (int i = 0; i < nranges; i++) {
 234    if (sp < size_table[i].min) {
 235      if (i > 0 && size_table[i].min - sp >= sp - size_table[i - 1].max)
 236	p = size_table[i - 1].max;
 237      else
 238	p = size_table[i].min;
 239      return;
 240    }
 241    if (sp <= size_table[i].max) {
 242      p = sp;
 243      return;
 244    }
 245  }
 246  p = size_table[nranges - 1].max;
 247}
 248
 249int font_size::to_units()
 250{
 251  return scale(p, units_per_inch, sizescale*72);
 252}
 253
 254// we can't do this in a static constructor because various dictionaries
 255// have to get initialized first
 256
 257void init_environments()
 258{
 259  curenv = env_table[0] = new environment("0");
 260}
 261
 262void tab_character()
 263{
 264  curenv->tab_char = get_optional_char();
 265  skip_line();
 266}
 267
 268void leader_character()
 269{
 270  curenv->leader_char = get_optional_char();
 271  skip_line();
 272}
 273
 274void environment::add_char(charinfo *ci)
 275{
 276  int s;
 277  node *gc_np = 0;
 278  if (interrupted)
 279    ;
 280  // don't allow fields in dummy environments
 281  else if (ci == field_delimiter_char && !dummy) {
 282    if (current_field)
 283      wrap_up_field();
 284    else
 285      start_field();
 286  }
 287  else if (current_field && ci == padding_indicator_char)
 288    add_padding();
 289  else if (current_tab) {
 290    if (tab_contents == 0)
 291      tab_contents = new line_start_node;
 292    if (ci != hyphen_indicator_char)
 293      tab_contents = tab_contents->add_char(ci, this, &tab_width, &s, &gc_np);
 294    else
 295      tab_contents = tab_contents->add_discretionary_hyphen();
 296  }
 297  else {
 298    if (line == 0)
 299      start_line();
 300#if 0
 301    fprintf(stderr, "current line is\n");
 302    line->debug_node_list();
 303#endif
 304    if (ci != hyphen_indicator_char)
 305      line = line->add_char(ci, this, &width_total, &space_total, &gc_np);
 306    else
 307      line = line->add_discretionary_hyphen();
 308  }
 309#if 0
 310  fprintf(stderr, "now after we have added character the line is\n");
 311  line->debug_node_list();
 312#endif
 313  if ((!suppress_push) && gc_np) {
 314    if (gc_np && (gc_np->state == 0)) {
 315      gc_np->state = construct_state(0);
 316      gc_np->push_state = get_diversion_state();
 317    }
 318    else if (line && (line->state == 0)) {
 319      line->state = construct_state(0);
 320      line->push_state = get_diversion_state();
 321    }
 322  }
 323#if 0
 324  fprintf(stderr, "now we have possibly added the state the line is\n");
 325  line->debug_node_list();
 326#endif
 327}
 328
 329node *environment::make_char_node(charinfo *ci)
 330{
 331  return make_node(ci, this);
 332}
 333
 334void environment::add_node(node *n)
 335{
 336  if (n == 0)
 337    return;
 338  if (!suppress_push) {
 339    if (n->is_special && n->state == NULL)
 340      n->state = construct_state(0);
 341    n->push_state = get_diversion_state();
 342  }
 343
 344  if (current_tab || current_field)
 345    n->freeze_space();
 346  if (interrupted) {
 347    delete n;
 348  }
 349  else if (current_tab) {
 350    n->next = tab_contents;
 351    tab_contents = n;
 352    tab_width += n->width();
 353  }
 354  else {
 355    if (line == 0) {
 356      if (discarding && n->discardable()) {
 357	// XXX possibly: input_line_start -= n->width();
 358	delete n;
 359	return;
 360      }
 361      start_line();
 362    }
 363    width_total += n->width();
 364    space_total += n->nspaces();
 365    n->next = line;
 366    line = n;
 367    construct_new_line_state(line);
 368  }
 369}
 370
 371void environment::add_hyphen_indicator()
 372{
 373  if (current_tab || interrupted || current_field
 374      || hyphen_indicator_char != 0)
 375    return;
 376  if (line == 0)
 377    start_line();
 378  line = line->add_discretionary_hyphen();
 379}
 380
 381int environment::get_hyphenation_flags()
 382{
 383  return hyphenation_flags;
 384}
 385
 386int environment::get_hyphen_line_max()
 387{
 388  return hyphen_line_max;
 389}
 390
 391int environment::get_hyphen_line_count()
 392{
 393  return hyphen_line_count;
 394}
 395
 396int environment::get_center_lines()
 397{
 398  return center_lines;
 399}
 400
 401int environment::get_right_justify_lines()
 402{
 403  return right_justify_lines;
 404}
 405
 406void environment::add_italic_correction()
 407{
 408  if (current_tab) {
 409    if (tab_contents)
 410      tab_contents = tab_contents->add_italic_correction(&tab_width);
 411  }
 412  else if (line)
 413    line = line->add_italic_correction(&width_total);
 414}
 415
 416void environment::space_newline()
 417{
 418  assert(!current_tab && !current_field);
 419  if (interrupted)
 420    return;
 421  hunits x = H0;
 422  hunits sw = env_space_width(this);
 423  hunits ssw = env_sentence_space_width(this);
 424  if (!translate_space_to_dummy) {
 425    x = sw;
 426    if (node_list_ends_sentence(line) == 1)
 427      x += ssw;
 428  }
 429  width_list *w = new width_list(sw, ssw);
 430  if (node_list_ends_sentence(line) == 1)
 431    w->next = new width_list(sw, ssw);
 432  if (line != 0 && line->merge_space(x, sw, ssw)) {
 433    width_total += x;
 434    return;
 435  }
 436  add_node(new word_space_node(x, get_fill_color(), w));
 437  possibly_break_line(0, spread_flag);
 438  spread_flag = 0;
 439}
 440
 441void environment::space()
 442{
 443  space(env_space_width(this), env_sentence_space_width(this));
 444}
 445
 446void environment::space(hunits space_width, hunits sentence_space_width)
 447{
 448  if (interrupted)
 449    return;
 450  if (current_field && padding_indicator_char == 0) {
 451    add_padding();
 452    return;
 453  }
 454  hunits x = translate_space_to_dummy ? H0 : space_width;
 455  node *p = current_tab ? tab_contents : line;
 456  hunits *tp = current_tab ? &tab_width : &width_total;
 457  if (p && p->nspaces() == 1 && p->width() == x
 458      && node_list_ends_sentence(p->next) == 1) {
 459    hunits xx = translate_space_to_dummy ? H0 : sentence_space_width;
 460    if (p->merge_space(xx, space_width, sentence_space_width)) {
 461      *tp += xx;
 462      return;
 463    }
 464  }
 465  if (p && p->merge_space(x, space_width, sentence_space_width)) {
 466    *tp += x;
 467    return;
 468  }
 469  add_node(new word_space_node(x,
 470			       get_fill_color(),
 471			       new width_list(space_width,
 472					      sentence_space_width)));
 473  possibly_break_line(0, spread_flag);
 474  spread_flag = 0;
 475}
 476
 477node *do_underline_special(int);
 478
 479void environment::set_font(symbol nm)
 480{
 481  if (interrupted)
 482    return;
 483  if (nm == symbol("P") || nm.is_empty()) {
 484    if (family->make_definite(prev_fontno) < 0)
 485      return;
 486    int tem = fontno;
 487    fontno = prev_fontno;
 488    prev_fontno = tem;
 489  }
 490  else {
 491    prev_fontno = fontno;
 492    int n = symbol_fontno(nm);
 493    if (n < 0) {
 494      n = next_available_font_position();
 495      if (!mount_font(n, nm))
 496	return;
 497    }
 498    if (family->make_definite(n) < 0)
 499      return;
 500    fontno = n;
 501  }
 502  if (underline_spaces && fontno != prev_fontno) {
 503    if (fontno == get_underline_fontno())
 504      add_node(do_underline_special(1));
 505    if (prev_fontno == get_underline_fontno())
 506      add_node(do_underline_special(0));
 507  }
 508}
 509
 510void environment::set_font(int n)
 511{
 512  if (interrupted)
 513    return;
 514  if (is_good_fontno(n)) {
 515    prev_fontno = fontno;
 516    fontno = n;
 517  }
 518  else
 519    warning(WARN_FONT, "bad font number");
 520}
 521
 522void environment::set_family(symbol fam)
 523{
 524  if (interrupted)
 525    return;
 526  if (fam.is_null() || fam.is_empty()) {
 527    if (prev_family->make_definite(fontno) < 0)
 528      return;
 529    font_family *tem = family;
 530    family = prev_family;
 531    prev_family = tem;
 532  }
 533  else {
 534    font_family *f = lookup_family(fam);
 535    if (f->make_definite(fontno) < 0)
 536      return;
 537    prev_family = family;
 538    family = f;
 539  }
 540}
 541
 542void environment::set_size(int n)
 543{
 544  if (interrupted)
 545    return;
 546  if (n == 0) {
 547    font_size temp = prev_size;
 548    prev_size = size;
 549    size = temp;
 550    int temp2 = prev_requested_size;
 551    prev_requested_size = requested_size;
 552    requested_size = temp2;
 553  }
 554  else {
 555    prev_size = size;
 556    size = font_size(n);
 557    prev_requested_size = requested_size;
 558    requested_size = n;
 559  }
 560}
 561
 562void environment::set_char_height(int n)
 563{
 564  if (interrupted)
 565    return;
 566  if (n == requested_size || n <= 0)
 567    char_height = 0;
 568  else
 569    char_height = n;
 570}
 571
 572void environment::set_char_slant(int n)
 573{
 574  if (interrupted)
 575    return;
 576  char_slant = n;
 577}
 578
 579color *environment::get_prev_glyph_color()
 580{
 581  return prev_glyph_color;
 582}
 583
 584color *environment::get_glyph_color()
 585{
 586  return glyph_color;
 587}
 588
 589color *environment::get_prev_fill_color()
 590{
 591  return prev_fill_color;
 592}
 593
 594color *environment::get_fill_color()
 595{
 596  return fill_color;
 597}
 598
 599void environment::set_glyph_color(color *c)
 600{
 601  if (interrupted)
 602    return;
 603  curenv->prev_glyph_color = curenv->glyph_color;
 604  curenv->glyph_color = c;
 605}
 606
 607void environment::set_fill_color(color *c)
 608{
 609  if (interrupted)
 610    return;
 611  curenv->prev_fill_color = curenv->fill_color;
 612  curenv->fill_color = c;
 613}
 614
 615environment::environment(symbol nm)
 616: dummy(0),
 617  prev_line_length((units_per_inch*13)/2),
 618  line_length((units_per_inch*13)/2),
 619  prev_title_length((units_per_inch*13)/2),
 620  title_length((units_per_inch*13)/2),
 621  prev_size(sizescale*10),
 622  size(sizescale*10),
 623  requested_size(sizescale*10),
 624  prev_requested_size(sizescale*10),
 625  char_height(0),
 626  char_slant(0),
 627  space_size(12),
 628  sentence_space_size(12),
 629  adjust_mode(ADJUST_BOTH),
 630  fill(1),
 631  interrupted(0),
 632  prev_line_interrupted(0),
 633  center_lines(0),
 634  right_justify_lines(0),
 635  prev_vertical_spacing(points_to_units(12)),
 636  vertical_spacing(points_to_units(12)),
 637  prev_post_vertical_spacing(0),
 638  post_vertical_spacing(0),
 639  prev_line_spacing(1),
 640  line_spacing(1),
 641  prev_indent(0),
 642  indent(0),
 643  temporary_indent(0),
 644  have_temporary_indent(0),
 645  underline_lines(0),
 646  underline_spaces(0),
 647  input_trap_count(0),
 648  continued_input_trap(0),
 649  line(0),
 650  prev_text_length(0),
 651  width_total(0),
 652  space_total(0),
 653  input_line_start(0),
 654  line_tabs(0),
 655  current_tab(TAB_NONE),
 656  leader_node(0),
 657  tab_char(0),
 658  leader_char(charset_table['.']),
 659  current_field(0),
 660  discarding(0),
 661  spread_flag(0),
 662  margin_character_flags(0),
 663  margin_character_node(0),
 664  margin_character_distance(points_to_units(10)),
 665  numbering_nodes(0),
 666  number_text_separation(1),
 667  line_number_indent(0),
 668  line_number_multiple(1),
 669  no_number_count(0),
 670  hyphenation_flags(1),
 671  hyphen_line_count(0),
 672  hyphen_line_max(-1),
 673  hyphenation_space(H0),
 674  hyphenation_margin(H0),
 675  composite(0),
 676  pending_lines(0),
 677#ifdef WIDOW_CONTROL
 678  widow_control(0),
 679#endif /* WIDOW_CONTROL */
 680  glyph_color(&default_color),
 681  prev_glyph_color(&default_color),
 682  fill_color(&default_color),
 683  prev_fill_color(&default_color),
 684  seen_space(0),
 685  seen_eol(0),
 686  suppress_next_eol(0),
 687  seen_break(0),
 688  tabs(units_per_inch/2, TAB_LEFT),
 689  name(nm),
 690  control_char('.'),
 691  no_break_control_char('\''),
 692  hyphen_indicator_char(0)
 693{
 694  prev_family = family = lookup_family(default_family);
 695  prev_fontno = fontno = 1;
 696  if (!is_good_fontno(1))
 697    fatal("font number 1 not a valid font");
 698  if (family->make_definite(1) < 0)
 699    fatal("invalid default family `%1'", default_family.contents());
 700  prev_fontno = fontno;
 701}
 702
 703environment::environment(const environment *e)
 704: dummy(1),
 705  prev_line_length(e->prev_line_length),
 706  line_length(e->line_length),
 707  prev_title_length(e->prev_title_length),
 708  title_length(e->title_length),
 709  prev_size(e->prev_size),
 710  size(e->size),
 711  requested_size(e->requested_size),
 712  prev_requested_size(e->prev_requested_size),
 713  char_height(e->char_height),
 714  char_slant(e->char_slant),
 715  prev_fontno(e->prev_fontno),
 716  fontno(e->fontno),
 717  prev_family(e->prev_family),
 718  family(e->family),
 719  space_size(e->space_size),
 720  sentence_space_size(e->sentence_space_size),
 721  adjust_mode(e->adjust_mode),
 722  fill(e->fill),
 723  interrupted(0),
 724  prev_line_interrupted(0),
 725  center_lines(0),
 726  right_justify_lines(0),
 727  prev_vertical_spacing(e->prev_vertical_spacing),
 728  vertical_spacing(e->vertical_spacing),
 729  prev_post_vertical_spacing(e->prev_post_vertical_spacing),
 730  post_vertical_spacing(e->post_vertical_spacing),
 731  prev_line_spacing(e->prev_line_spacing),
 732  line_spacing(e->line_spacing),
 733  prev_indent(e->prev_indent),
 734  indent(e->indent),
 735  temporary_indent(0),
 736  have_temporary_indent(0),
 737  underline_lines(0),
 738  underline_spaces(0),
 739  input_trap_count(0),
 740  continued_input_trap(0),
 741  line(0),
 742  prev_text_length(e->prev_text_length),
 743  width_total(0),
 744  space_total(0),
 745  input_line_start(0),
 746  line_tabs(e->line_tabs),
 747  current_tab(TAB_NONE),
 748  leader_node(0),
 749  tab_char(e->tab_char),
 750  leader_char(e->leader_char),
 751  current_field(0),
 752  discarding(0),
 753  spread_flag(0),
 754  margin_character_flags(e->margin_character_flags),
 755  margin_character_node(e->margin_character_node),
 756  margin_character_distance(e->margin_character_distance),
 757  numbering_nodes(0),
 758  number_text_separation(e->number_text_separation),
 759  line_number_indent(e->line_number_indent),
 760  line_number_multiple(e->line_number_multiple),
 761  no_number_count(e->no_number_count),
 762  hyphenation_flags(e->hyphenation_flags),
 763  hyphen_line_count(0),
 764  hyphen_line_max(e->hyphen_line_max),
 765  hyphenation_space(e->hyphenation_space),
 766  hyphenation_margin(e->hyphenation_margin),
 767  composite(0),
 768  pending_lines(0),
 769#ifdef WIDOW_CONTROL
 770  widow_control(e->widow_control),
 771#endif /* WIDOW_CONTROL */
 772  glyph_color(e->glyph_color),
 773  prev_glyph_color(e->prev_glyph_color),
 774  fill_color(e->fill_color),
 775  prev_fill_color(e->prev_fill_color),
 776  seen_space(e->seen_space),
 777  seen_eol(e->seen_eol),
 778  suppress_next_eol(e->suppress_next_eol),
 779  seen_break(e->seen_break),
 780  tabs(e->tabs),
 781  name(e->name),		// so that eg `.if "\n[.ev]"0"' works
 782  control_char(e->control_char),
 783  no_break_control_char(e->no_break_control_char),
 784  hyphen_indicator_char(e->hyphen_indicator_char)
 785{
 786}
 787
 788void environment::copy(const environment *e)
 789{
 790  prev_line_length = e->prev_line_length;
 791  line_length = e->line_length;
 792  prev_title_length = e->prev_title_length;
 793  title_length = e->title_length;
 794  prev_size = e->prev_size;
 795  size = e->size;
 796  prev_requested_size = e->prev_requested_size;
 797  requested_size = e->requested_size;
 798  char_height = e->char_height;
 799  char_slant = e->char_slant;
 800  space_size = e->space_size;
 801  sentence_space_size = e->sentence_space_size;
 802  adjust_mode = e->adjust_mode;
 803  fill = e->fill;
 804  interrupted = 0;
 805  prev_line_interrupted = 0;
 806  center_lines = 0;
 807  right_justify_lines = 0;
 808  prev_vertical_spacing = e->prev_vertical_spacing;
 809  vertical_spacing = e->vertical_spacing;
 810  prev_post_vertical_spacing = e->prev_post_vertical_spacing,
 811  post_vertical_spacing = e->post_vertical_spacing,
 812  prev_line_spacing = e->prev_line_spacing;
 813  line_spacing = e->line_spacing;
 814  prev_indent = e->prev_indent;
 815  indent = e->indent;
 816  have_temporary_indent = 0;
 817  temporary_indent = 0;
 818  underline_lines = 0;
 819  underline_spaces = 0;
 820  input_trap_count = 0;
 821  continued_input_trap = 0;
 822  prev_text_length = e->prev_text_length;
 823  width_total = 0;
 824  space_total = 0;
 825  input_line_start = 0;
 826  control_char = e->control_char;
 827  no_break_control_char = e->no_break_control_char;
 828  hyphen_indicator_char = e->hyphen_indicator_char;
 829  spread_flag = 0;
 830  line = 0;
 831  pending_lines = 0;
 832  discarding = 0;
 833  tabs = e->tabs;
 834  line_tabs = e->line_tabs;
 835  current_tab = TAB_NONE;
 836  current_field = 0;
 837  margin_character_flags = e->margin_character_flags;
 838  margin_character_node = e->margin_character_node;
 839  margin_character_distance = e->margin_character_distance;
 840  numbering_nodes = 0;
 841  number_text_separation = e->number_text_separation;
 842  line_number_multiple = e->line_number_multiple;
 843  line_number_indent = e->line_number_indent;
 844  no_number_count = e->no_number_count;
 845  tab_char = e->tab_char;
 846  leader_char = e->leader_char;
 847  hyphenation_flags = e->hyphenation_flags;
 848  fontno = e->fontno;
 849  prev_fontno = e->prev_fontno;
 850  dummy = e->dummy;
 851  family = e->family;
 852  prev_family = e->prev_family;
 853  leader_node = 0;
 854#ifdef WIDOW_CONTROL
 855  widow_control = e->widow_control;
 856#endif /* WIDOW_CONTROL */
 857  hyphen_line_max = e->hyphen_line_max;
 858  hyphen_line_count = 0;
 859  hyphenation_space = e->hyphenation_space;
 860  hyphenation_margin = e->hyphenation_margin;
 861  composite = 0;
 862  glyph_color= e->glyph_color;
 863  prev_glyph_color = e->prev_glyph_color;
 864  fill_color = e->fill_color;
 865  prev_fill_color = e->prev_fill_color;
 866}
 867
 868environment::~environment()
 869{
 870  delete leader_node;
 871  delete_node_list(line);
 872  delete_node_list(numbering_nodes);
 873}
 874
 875hunits environment::get_input_line_position()
 876{
 877  hunits n;
 878  if (line == 0)
 879    n = -input_line_start;
 880  else
 881    n = width_total - input_line_start;
 882  if (current_tab)
 883    n += tab_width;
 884  return n;
 885}
 886
 887void environment::set_input_line_position(hunits n)
 888{
 889  input_line_start = line == 0 ? -n : width_total - n;
 890  if (current_tab)
 891    input_line_start += tab_width;
 892}
 893
 894hunits environment::get_line_length()
 895{
 896  return line_length;
 897}
 898
 899hunits environment::get_saved_line_length()
 900{
 901  if (line)
 902    return target_text_length + saved_indent;
 903  else
 904    return line_length;
 905}
 906
 907vunits environment::get_vertical_spacing()
 908{
 909  return vertical_spacing;
 910}
 911
 912vunits environment::get_post_vertical_spacing()
 913{
 914  return post_vertical_spacing;
 915}
 916
 917int environment::get_line_spacing()
 918{
 919  return line_spacing;
 920}
 921
 922vunits environment::total_post_vertical_spacing()
 923{
 924  vunits tem(post_vertical_spacing);
 925  if (line_spacing > 1)
 926    tem += (line_spacing - 1)*vertical_spacing;
 927  return tem;
 928}
 929
 930int environment::get_bold()
 931{
 932  return get_bold_fontno(fontno);
 933}
 934
 935hunits environment::get_digit_width()
 936{
 937  return env_digit_width(this);
 938} 
 939
 940int environment::get_adjust_mode()
 941{
 942  return adjust_mode;
 943}
 944
 945int environment::get_fill()
 946{
 947  return fill;
 948}
 949
 950hunits environment::get_indent()
 951{
 952  return indent;
 953}
 954
 955hunits environment::get_saved_indent()
 956{
 957  if (line)
 958    return saved_indent;
 959  else if (have_temporary_indent)
 960    return temporary_indent;
 961  else
 962    return indent;
 963}
 964
 965hunits environment::get_temporary_indent()
 966{
 967  return temporary_indent;
 968}
 969
 970hunits environment::get_title_length()
 971{
 972  return title_length;
 973}
 974
 975node *environment::get_prev_char()
 976{
 977  for (node *n = current_tab ? tab_contents : line; n; n = n->next) {
 978    node *last = n->last_char_node();
 979    if (last)
 980      return last;
 981  }
 982  return 0;
 983}
 984
 985hunits environment::get_prev_char_width()
 986{
 987  node *last = get_prev_char();
 988  if (!last)
 989    return H0;
 990  return last->width();
 991}
 992
 993hunits environment::get_prev_char_skew()
 994{
 995  node *last = get_prev_char();
 996  if (!last)
 997    return H0;
 998  return last->skew();
 999}
1000
1001vunits environment::get_prev_char_height()
1002{
1003  node *last = get_prev_char();
1004  if (!last)
1005    return V0;
1006  vunits min, max;
1007  last->vertical_extent(&min, &max);
1008  return -min;
1009}
1010
1011vunits environment::get_prev_char_depth()
1012{
1013  node *last = get_prev_char();
1014  if (!last)
1015    return V0;
1016  vunits min, max;
1017  last->vertical_extent(&min, &max);
1018  return max;
1019}
1020
1021hunits environment::get_text_length()
1022{
1023  hunits n = line == 0 ? H0 : width_total;
1024  if (current_tab)
1025    n += tab_width;
1026  return n;
1027}
1028
1029hunits environment::get_prev_text_length()
1030{
1031  return prev_text_length;
1032}
1033
1034
1035static int sb_reg_contents = 0;
1036static int st_reg_contents = 0;
1037static int ct_reg_contents = 0;
1038static int rsb_reg_contents = 0;
1039static int rst_reg_contents = 0;
1040static int skw_reg_contents = 0;
1041static int ssc_reg_contents = 0;
1042
1043void environment::width_registers()
1044{
1045  // this is used to implement \w; it sets the st, sb, ct registers
1046  vunits min = 0, max = 0, cur = 0;
1047  int character_type = 0;
1048  ssc_reg_contents = line ? line->subscript_correction().to_units() : 0;
1049  skw_reg_contents = line ? line->skew().to_units() : 0;
1050  line = reverse_node_list(line);
1051  vunits real_min = V0;
1052  vunits real_max = V0;
1053  vunits v1, v2;
1054  for (node *tem = line; tem; tem = tem->next) {
1055    tem->vertical_extent(&v1, &v2);
1056    v1 += cur;
1057    if (v1 < real_min)
1058      real_min = v1;
1059    v2 += cur;
1060    if (v2 > real_max)
1061      real_max = v2;
1062    if ((cur += tem->vertical_width()) < min)
1063      min = cur;
1064    else if (cur > max)
1065      max = cur;
1066    character_type |= tem->character_type();
1067  }
1068  line = reverse_node_list(line);
1069  st_reg_contents = -min.to_units();
1070  sb_reg_contents = -max.to_units();
1071  rst_reg_contents = -real_min.to_units();
1072  rsb_reg_contents = -real_max.to_units();
1073  ct_reg_contents = character_type;
1074}
1075
1076node *environment::extract_output_line()
1077{
1078  if (current_tab)
1079    wrap_up_tab();
1080  node *n = line;
1081  line = 0;
1082  return n;
1083}
1084
1085/* environment related requests */
1086
1087void environment_switch()
1088{
1089  int pop = 0;	// 1 means pop, 2 means pop but no error message on underflow
1090  if (curenv->is_dummy())
1091    error("can't switch environments when current environment is dummy");
1092  else if (!has_arg())
1093    pop = 1;
1094  else {
1095    symbol nm;
1096    if (!tok.delimiter()) {
1097      // It looks like a number.
1098      int n;
1099      if (get_integer(&n)) {
1100	if (n >= 0 && n < NENVIRONMENTS) {
1101	  env_stack = new env_list(curenv, env_stack);
1102	  if (env_table[n] == 0)
1103	    env_table[n] = new environment(i_to_a(n));
1104	  curenv = env_table[n];
1105	}
1106	else
1107	  nm = i_to_a(n);
1108      }
1109      else
1110	pop = 2;
1111    }
1112    else {
1113      nm = get_long_name(1);
1114      if (nm.is_null())
1115	pop = 2;
1116    }
1117    if (!nm.is_null()) {
1118      environment *e = (environment *)env_dictionary.lookup(nm);
1119      if (!e) {
1120	e = new environment(nm);
1121	(void)env_dictionary.lookup(nm, e);
1122      }
1123      env_stack = new env_list(curenv, env_stack);
1124      curenv = e;
1125    }
1126  }
1127  if (pop) {
1128    if (env_stack == 0) {
1129      if (pop == 1)
1130	error("environment stack underflow");
1131    }
1132    else {
1133      int seen_space = curenv->seen_space;
1134      int seen_eol   = curenv->seen_eol;
1135      int suppress_next_eol = curenv->suppress_next_eol;
1136      curenv = env_stack->env;
1137      curenv->seen_space = seen_space;
1138      curenv->seen_eol   = seen_eol;
1139      curenv->suppress_next_eol = suppress_next_eol;
1140      env_list *tem = env_stack;
1141      env_stack = env_stack->next;
1142      delete tem;
1143    }
1144  }
1145  skip_line();
1146}
1147
1148void environment_copy()
1149{
1150  symbol nm;
1151  environment *e=0;
1152  tok.skip();
1153  if (!tok.delimiter()) {
1154    // It looks like a number.
1155    int n;
1156    if (get_integer(&n)) {
1157      if (n >= 0 && n < NENVIRONMENTS)
1158	e = env_table[n];
1159      else
1160	nm = i_to_a(n);
1161    }
1162  }
1163  else
1164    nm = get_long_name(1);
1165  if (!e && !nm.is_null())
1166    e = (environment *)env_dictionary.lookup(nm);
1167  if (e == 0) {
1168    error("No environment to copy from");
1169    return;
1170  }
1171  else
1172    curenv->copy(e);
1173  skip_line();
1174}
1175
1176void fill_color_change()
1177{
1178  symbol s = get_name();
1179  if (s.is_null())
1180    curenv->set_fill_color(curenv->get_prev_fill_color());
1181  else
1182    do_fill_color(s);
1183  skip_line();
1184}
1185
1186void glyph_color_change()
1187{
1188  symbol s = get_name();
1189  if (s.is_null())
1190    curenv->set_glyph_color(curenv->get_prev_glyph_color());
1191  else
1192    do_glyph_color(s);
1193  skip_line();
1194}
1195
1196static symbol P_symbol("P");
1197
1198void font_change()
1199{
1200  symbol s = get_name();
1201  int is_number = 1;
1202  if (s.is_null() || s == P_symbol) {
1203    s = P_symbol;
1204    is_number = 0;
1205  }
1206  else {
1207    for (const char *p = s.contents(); p != 0 && *p != 0; p++)
1208      if (!csdigit(*p)) {
1209	is_number = 0;
1210	break;
1211      }
1212  }
1213  if (is_number)
1214    curenv->set_font(atoi(s.contents()));
1215  else
1216    curenv->set_font(s);
1217  skip_line();
1218}
1219
1220void family_change()
1221{
1222  symbol s = get_name();
1223  curenv->set_family(s);
1224  skip_line();
1225}
1226
1227void point_size()
1228{
1229  int n;
1230  if (has_arg() && get_number(&n, 'z', curenv->get_requested_point_size())) {
1231    if (n <= 0)
1232      n = 1;
1233    curenv->set_size(n);
1234  }
1235  else
1236    curenv->set_size(0);
1237  skip_line();
1238}
1239
1240void override_sizes()
1241{
1242  int n = 16;
1243  int *sizes = new int[n];
1244  int i = 0;
1245  char *buf = read_string();
1246  if (!buf)
1247    return;
1248  char *p = strtok(buf, " \t");
1249  for (;;) {
1250    if (!p)
1251      break;
1252    int lower, upper;
1253    switch (sscanf(p, "%d-%d", &lower, &upper)) {
1254    case 1:
1255      upper = lower;
1256      // fall through
1257    case 2:
1258      if (lower <= upper && lower >= 0)
1259	break;
1260      // fall through
1261    default:
1262      warning(WARN_RANGE, "bad size range `%1'", p);
1263      return;
1264    }
1265    if (i + 2 > n) {
1266      int *old_sizes = sizes;
1267      sizes = new int[n*2];
1268      memcpy(sizes, old_sizes, n*sizeof(int));
1269      n *= 2;
1270      a_delete old_sizes;
1271    }
1272    sizes[i++] = lower;
1273    if (lower == 0)
1274      break;
1275    sizes[i++] = upper;
1276    p = strtok(0, " \t");
1277  }
1278  font_size::init_size_table(sizes);
1279}
1280
1281void space_size()
1282{
1283  int n;
1284  if (get_integer(&n)) {
1285    curenv->space_size = n;
1286    if (has_arg() && get_integer(&n))
1287      curenv->sentence_space_size = n;
1288    else
1289      curenv->sentence_space_size = curenv->space_size;
1290  }
1291  skip_line();
1292}
1293
1294void fill()
1295{
1296  while (!tok.newline() && !tok.eof())
1297    tok.next();
1298  if (break_flag)
1299    curenv->do_break();
1300  curenv->fill = 1;
1301  tok.next();
1302}
1303
1304void no_fill()
1305{
1306  while (!tok.newline() && !tok.eof())
1307    tok.next();
1308  if (break_flag)
1309    curenv->do_break();
1310  curenv->fill = 0;
1311  curenv->suppress_next_eol = 1;
1312  tok.next();
1313}
1314
1315void center()
1316{
1317  int n;
1318  if (!has_arg() || !get_integer(&n))
1319    n = 1;
1320  else if (n < 0)
1321    n = 0;
1322  while (!tok.newline() && !tok.eof())
1323    tok.next();
1324  if (break_flag)
1325    curenv->do_break();
1326  curenv->right_justify_lines = 0;
1327  curenv->center_lines = n;
1328  curdiv->modified_tag.incl(MTSM_CE);
1329  tok.next();
1330}
1331
1332void right_justify()
1333{
1334  int n;
1335  if (!has_arg() || !get_integer(&n))
1336    n = 1;
1337  else if (n < 0)
1338    n = 0;
1339  while (!tok.newline() && !tok.eof())
1340    tok.next();
1341  if (break_flag)
1342    curenv->do_break();
1343  curenv->center_lines = 0;
1344  curenv->right_justify_lines = n;
1345  curdiv->modified_tag.incl(MTSM_RJ);
1346  tok.next();
1347}
1348
1349void line_length()
1350{
1351  hunits temp;
1352  if (has_arg() && get_hunits(&temp, 'm', curenv->line_length)) {
1353    if (temp < H0) {
1354      warning(WARN_RANGE, "bad line length %1u", temp.to_units());
1355      temp = H0;
1356    }
1357  }
1358  else
1359    temp = curenv->prev_line_length;
1360  curenv->prev_line_length = curenv->line_length;
1361  curenv->line_length = temp;
1362  curdiv->modified_tag.incl(MTSM_LL);
1363  skip_line();
1364}
1365
1366void title_length()
1367{
1368  hunits temp;
1369  if (has_arg() && get_hunits(&temp, 'm', curenv->title_length)) {
1370    if (temp < H0) {
1371      warning(WARN_RANGE, "bad title length %1u", temp.to_units());
1372      temp = H0;
1373    }
1374  }
1375  else
1376    temp = curenv->prev_title_length;
1377  curenv->prev_title_length = curenv->title_length;
1378  curenv->title_length = temp;
1379  skip_line();
1380}
1381
1382void vertical_spacing()
1383{
1384  vunits temp;
1385  if (has_arg() && get_vunits(&temp, 'p', curenv->vertical_spacing)) {
1386    if (temp < V0) {
1387      warning(WARN_RANGE, "vertical spacing must not be negative");
1388      temp = vresolution;
1389    }
1390  }
1391  else
1392    temp = curenv->prev_vertical_spacing;
1393  curenv->prev_vertical_spacing = curenv->vertical_spacing;
1394  curenv->vertical_spacing = temp;
1395  skip_line();
1396}
1397
1398void post_vertical_spacing()
1399{
1400  vunits temp;
1401  if (has_arg() && get_vunits(&temp, 'p', curenv->post_vertical_spacing)) {
1402    if (temp < V0) {
1403      warning(WARN_RANGE,
1404	      "post vertical spacing must be greater than or equal to 0");
1405      temp = V0;
1406    }
1407  }
1408  else
1409    temp = curenv->prev_post_vertical_spacing;
1410  curenv->prev_post_vertical_spacing = curenv->post_vertical_spacing;
1411  curenv->post_vertical_spacing = temp;
1412  skip_line();
1413}
1414
1415void line_spacing()
1416{
1417  int temp;
1418  if (has_arg() && get_integer(&temp)) {
1419    if (temp < 1) {
1420      warning(WARN_RANGE, "value %1 out of range: interpreted as 1", temp);
1421      temp = 1;
1422    }
1423  }
1424  else
1425    temp = curenv->prev_line_spacing;
1426  curenv->prev_line_spacing = curenv->line_spacing;
1427  curenv->line_spacing = temp;
1428  skip_line();
1429}
1430
1431void indent()
1432{
1433  hunits temp;
1434  if (has_arg() && get_hunits(&temp, 'm', curenv->indent)) {
1435    if (temp < H0) {
1436      warning(WARN_RANGE, "indent cannot be negative");
1437      temp = H0;
1438    }
1439  }
1440  else
1441    temp = curenv->prev_indent;
1442  while (!tok.newline() && !tok.eof())
1443    tok.next();
1444  if (break_flag)
1445    curenv->do_break();
1446  curenv->have_temporary_indent = 0;
1447  curenv->prev_indent = curenv->indent;
1448  curenv->indent = temp;
1449  curdiv->modified_tag.incl(MTSM_IN);
1450  tok.next();
1451}
1452
1453void temporary_indent()
1454{
1455  int err = 0;
1456  hunits temp;
1457  if (!get_hunits(&temp, 'm', curenv->get_indent()))
1458    err = 1;
1459  while (!tok.newline() && !tok.eof())
1460    tok.next();
1461  if (break_flag)
1462    curenv->do_break();
1463  if (temp < H0) {
1464    warning(WARN_RANGE, "total indent cannot be negative");
1465    temp = H0;
1466  }
1467  if (!err) {
1468    curenv->temporary_indent = temp;
1469    curenv->have_temporary_indent = 1;
1470    curdiv->modified_tag.incl(MTSM_TI);
1471  }
1472  tok.next();
1473}
1474
1475node *do_underline_special(int underline_spaces)
1476{
1477  macro m;
1478  m.append_str("x u ");
1479  m.append(underline_spaces + '0');
1480  return new special_node(m, 1);
1481}
1482
1483void do_underline(int underline_spaces)
1484{
1485  int n;
1486  if (!has_arg() || !get_integer(&n))
1487    n = 1;
1488  if (n <= 0) {
1489    if (curenv->underline_lines > 0) {
1490      curenv->prev_fontno = curenv->fontno;
1491      curenv->fontno = curenv->pre_underline_fontno;
1492      if (underline_spaces) {
1493	curenv->underline_spaces = 0;
1494	curenv->add_node(do_underline_special(0));
1495      }
1496    }
1497    curenv->underline_lines = 0;
1498  }
1499  else {
1500    curenv->underline_lines = n;
1501    curenv->pre_underline_fontno = curenv->fontno;
1502    curenv->fontno = get_underline_fontno();
1503    if (underline_spaces) {
1504      curenv->underline_spaces = 1;
1505      curenv->add_node(do_underline_special(1));
1506    }
1507  }
1508  skip_line();
1509}
1510
1511void continuous_underline()
1512{
1513  do_underline(1);
1514}
1515
1516void underline()
1517{
1518  do_underline(0);
1519}
1520
1521void control_char()
1522{
1523  curenv->control_char = '.';
1524  if (has_arg()) {
1525    if (tok.ch() == 0)
1526      error("bad control character");
1527    else
1528      curenv->control_char = tok.ch();
1529  }
1530  skip_line();
1531}
1532
1533void no_break_control_char()
1534{
1535  curenv->no_break_control_char = '\'';
1536  if (has_arg()) {
1537    if (tok.ch() == 0)
1538      error("bad control character");
1539    else
1540      curenv->no_break_control_char = tok.ch();
1541  }
1542  skip_line();
1543}
1544
1545void margin_character()
1546{
1547  while (tok.space())
1548    tok.next();
1549  charinfo *ci = tok.get_char();
1550  if (ci) {
1551    // Call tok.next() only after making the node so that
1552    // .mc \s+9\(br\s0 works.
1553    node *nd = curenv->make_char_node(ci);
1554    tok.next();
1555    if (nd) {
1556      delete curenv->margin_character_node;
1557      curenv->margin_character_node = nd;
1558      curenv->margin_character_flags = (MARGIN_CHARACTER_ON
1559					|MARGIN_CHARACTER_NEXT);
1560      hunits d;
1561      if (has_arg() && get_hunits(&d, 'm'))
1562	curenv->margin_character_distance = d;
1563    }
1564  }
1565  else {
1566    check_missing_character();
1567    curenv->margin_character_flags &= ~MARGIN_CHARACTER_ON;
1568    if (curenv->margin_character_flags == 0) {
1569      delete curenv->margin_character_node;
1570      curenv->margin_character_node = 0;
1571    }
1572  }
1573  skip_line();
1574}
1575
1576void number_lines()
1577{
1578  delete_node_list(curenv->numbering_nodes);
1579  curenv->numbering_nodes = 0;
1580  if (has_arg()) {
1581    node *nd = 0;
1582    for (int i = '9'; i >= '0'; i--) {
1583      node *tem = make_node(charset_table[i], curenv);
1584      if (!tem) {
1585	skip_line();
1586	return;
1587      }
1588      tem->next = nd;
1589      nd = tem;
1590    }
1591    curenv->numbering_nodes = nd;
1592    curenv->line_number_digit_width = env_digit_width(curenv);
1593    int n;
1594    if (!tok.delimiter()) {
1595      if (get_integer(&n, next_line_number)) {
1596	next_line_number = n;
1597	if (next_line_number < 0) {
1598	  warning(WARN_RANGE, "negative line number");
1599	  next_line_number = 0;
1600	}
1601      }
1602    }
1603    else
1604      while (!tok.space() && !tok.newline() && !tok.eof())
1605	tok.next();
1606    if (has_arg()) {
1607      if (!tok.delimiter()) {
1608	if (get_integer(&n)) {
1609	  if (n <= 0) {
1610	    warning(WARN_RANGE, "negative or zero line number multiple");
1611	  }
1612	  else
1613	    curenv->line_number_multiple = n;
1614	}
1615      }
1616      else
1617	while (!tok.space() && !tok.newline() && !tok.eof())
1618	  tok.next();
1619      if (has_arg()) {
1620	if (!tok.delimiter()) {
1621	  if (get_integer(&n))
1622	    curenv->number_text_separation = n;
1623	}
1624	else
1625	  while (!tok.space() && !tok.newline() && !tok.eof())
1626	    tok.next();
1627	if (has_arg() && !tok.delimiter() && get_integer(&n))
1628	  curenv->line_number_indent = n;
1629      }
1630    }
1631  }
1632  skip_line();
1633}
1634
1635void no_number()
1636{
1637  int n;
1638  if (has_arg() && get_integer(&n))
1639    curenv->no_number_count = n > 0 ? n : 0;
1640  else
1641    curenv->no_number_count = 1;
1642  skip_line();
1643}
1644
1645void no_hyphenate()
1646{
1647  curenv->hyphenation_flags = 0;
1648  skip_line();
1649}
1650
1651void hyphenate_request()
1652{
1653  int n;
1654  if (has_arg() && get_integer(&n))
1655    curenv->hyphenation_flags = n;
1656  else
1657    curenv->hyphenation_flags = 1;
1658  skip_line();
1659}
1660
1661void hyphen_char()
1662{
1663  curenv->hyphen_indicator_char = get_optional_char();
1664  skip_line();
1665}
1666
1667void hyphen_line_max_request()
1668{
1669  int n;
1670  if (has_arg() && get_integer(&n))
1671    curenv->hyphen_line_max = n;
1672  else
1673    curenv->hyphen_line_max = -1;
1674  skip_line();
1675}
1676
1677void environment::interrupt()
1678{
1679  if (!dummy) {
1680    add_node(new transparent_dummy_node);
1681    interrupted = 1;
1682  }
1683}
1684
1685void environment::newline()
1686{
1687  int was_centered = 0;
1688  if (underline_lines > 0) {
1689    if (--underline_lines == 0) {
1690      prev_fontno = fontno;
1691      fontno = pre_underline_fontno;
1692      if (underline_spaces) {
1693        underline_spaces = 0;
1694        add_node(do_underline_special(0));
1695      }
1696    }
1697  }
1698  if (current_field)
1699    wrap_up_field();
1700  if (current_tab)
1701    wrap_up_tab();
1702  // strip trailing spaces
1703  while (line != 0 && line->discardable()) {
1704    width_total -= line->width();
1705    space_total -= line->nspaces();
1706    node *tem = line;
1707    line = line->next;
1708    delete tem;
1709  }
1710  node *to_be_output = 0;
1711  hunits to_be_output_width;
1712  prev_line_interrupted = 0;
1713  if (dummy)
1714    space_newline();
1715  else if (interrupted) {
1716    interrupted = 0;
1717    // see environment::final_break
1718    prev_line_interrupted = exit_started ? 2 : 1;
1719  }
1720  else if (center_lines > 0) {
1721    --center_lines;
1722    hunits x = target_text_length - width_total;
1723    if (x > H0)
1724      saved_indent += x/2;
1725    to_be_output = line;
1726    was_centered = 1;
1727    to_be_output_width = width_total;
1728    line = 0;
1729  }
1730  else if (right_justify_lines > 0) {
1731    --right_justify_lines;
1732    hunits x = target_text_length - width_total;
1733    if (x > H0)
1734      saved_indent += x;
1735    to_be_output = line;
1736    to_be_output_width = width_total;
1737    line = 0;
1738  }
1739  else if (fill)
1740    space_newline();
1741  else {
1742    to_be_output = line;
1743    to_be_output_width = width_total;
1744    line = 0;
1745  }
1746  input_line_start = line == 0 ? H0 : width_total;
1747  if (to_be_output) {
1748    if (is_html && !fill) {
1749      curdiv->modified_tag.incl(MTSM_EOL);
1750      if (suppress_next_eol)
1751	suppress_next_eol = 0;
1752      else
1753	seen_eol = 1;
1754    }
1755
1756    output_line(to_be_output, to_be_output_width, was_centered);
1757    hyphen_line_count = 0;
1758  }
1759  if (input_trap_count > 0) {
1760    if (!(continued_input_trap && prev_line_interrupted))
1761      if (--input_trap_count == 0)
1762	spring_trap(input_trap);
1763  }
1764}
1765
1766void environment::output_line(node *n, hunits width, int was_centered)
1767{
1768  prev_text_length = width;
1769  if (margin_character_flags) {
1770    hunits d = line_length + margin_character_distance - saved_indent - width;
1771    if (d > 0) {
1772      n = new hmotion_node(d, get_fill_color(), n);
1773      width += d;
1774    }
1775    margin_character_flags &= ~MARGIN_CHARACTER_NEXT;
1776    node *tem;
1777    if (!margin_character_flags) {
1778      tem = margin_character_node;
1779      margin_character_node = 0;
1780    }
1781    else
1782      tem = margin_character_node->copy();
1783    tem->next = n;
1784    n = tem;
1785    width += tem->width();
1786  }
1787  node *nn = 0;
1788  while (n != 0) {
1789    node *tem = n->next;
1790    n->next = nn;
1791    nn = n;
1792    n = tem;
1793  }
1794  if (!saved_indent.is_zero())
1795    nn = new hmotion_node(saved_indent, get_fill_color(), nn);
1796  width += saved_indent;
1797  if (no_number_count > 0)
1798    --no_number_count;
1799  else if (numbering_nodes) {
1800    hunits w = (line_number_digit_width
1801		*(3+line_number_indent+number_text_separation));
1802    if (next_line_number % line_number_multiple != 0)
1803      nn = new hmotion_node(w, get_fill_color(), nn);
1804    else {
1805      hunits x = w;
1806      nn = new hmotion_node(number_text_separation * line_number_digit_width,
1807			    get_fill_color(), nn);
1808      x -= number_text_separation*line_number_digit_width;
1809      char buf[30];
1810      sprintf(buf, "%3d", next_line_number);
1811      for (char *p = strchr(buf, '\0') - 1; p >= buf && *p != ' '; --p) {
1812	node *gn = numbering_nodes;
1813	for (int count = *p - '0'; count > 0; count--)
1814	  gn = gn->next;
1815	gn = gn->copy();
1816	x -= gn->width();
1817	gn->next = nn;
1818	nn = gn;
1819      }
1820      nn = new hmotion_node(x, get_fill_color(), nn);
1821    }
1822    width += w;
1823    ++next_line_number;
1824  }
1825  output(nn, !fill, vertical_spacing, total_post_vertical_spacing(), width,
1826	 was_centered);
1827}
1828
1829void environment::start_line()
1830{
1831  assert(line == 0);
1832  discarding = 0;
1833  line = new line_start_node;
1834  if (have_temporary_indent) {
1835    saved_indent = temporary_indent;
1836    have_temporary_indent = 0;
1837  }
1838  else
1839    saved_indent = indent;
1840  target_text_length = line_length - saved_indent;
1841  width_total = H0;
1842  space_total = 0;
1843}
1844
1845hunits environment::get_hyphenation_space()
1846{
1847  return hyphenation_space;
1848}
1849
1850void hyphenation_space_request()
1851{
1852  hunits n;
1853  if (get_hunits(&n, 'm')) {
1854    if (n < H0) {
1855      warning(WARN_RANGE, "hyphenation space cannot be negative");
1856      n = H0;
1857    }
1858    curenv->hyphenation_space = n;
1859  }
1860  skip_line();
1861}
1862
1863hunits environment::get_hyphenation_margin()
1864{
1865  return hyphenation_margin;
1866}
1867
1868void hyphenation_margin_request()
1869{
1870  hunits n;
1871  if (get_hunits(&n, 'm')) {
1872    if (n < H0) {
1873      warning(WARN_RANGE, "hyphenation margin cannot be negative");
1874      n = H0;
1875    }
1876    curenv->hyphenation_margin = n;
1877  }
1878  skip_line();
1879}
1880
1881breakpoint *environment::choose_breakpoint()
1882{
1883  hunits x = width_total;
1884  int s = space_total;
1885  node *n = line;
1886  breakpoint *best_bp = 0;	// the best breakpoint so far
1887  int best_bp_fits = 0;
1888  while (n != 0) {
1889    x -= n->width();
1890    s -= n->nspaces();
1891    breakpoint *bp = n->get_breakpoints(x, s);
1892    while (bp != 0) {
1893      if (bp->width <= target_text_length) {
1894	if (!bp->hyphenated) {
1895	  breakpoint *tem = bp->next;
1896	  bp->next = 0;
1897	  while (tem != 0) {
1898	    breakpoint *tem1 = tem;
1899	    tem = tem->next;
1900	    delete tem1;
1901	  }
1902	  if (best_bp_fits
1903	      // Decide whether to use the hyphenated breakpoint.
1904	      && (hyphen_line_max < 0
1905		  // Only choose the hyphenated breakpoint if it would not
1906		  // exceed the maximum number of consecutive hyphenated
1907		  // lines.
1908		  || hyphen_line_count + 1 <= hyphen_line_max)
1909	      && !(adjust_mode == ADJUST_BOTH
1910		   // Don't choose the hyphenated breakpoint if the line
1911		   // can be justified by adding no more than
1912		   // hyphenation_space to any word space.
1913		   ? (bp->nspaces > 0
1914		      && (((target_text_length - bp->width
1915			    + (bp->nspaces - 1)*hresolution)/bp->nspaces)
1916			  <= hyphenation_space))
1917		   // Don't choose the hyphenated breakpoint if the line
1918		   // is no more than hyphenation_margin short.
1919		   : target_text_length - bp->width <= hyphenation_margin)) {
1920	    delete bp;
1921	    return best_bp;
1922	  }
1923	  if (best_bp)
1924	    delete best_bp;
1925	  return bp;
1926	}
1927	else {
1928	  if ((adjust_mode == ADJUST_BOTH
1929	       ? hyphenation_space == H0
1930	       : hyphenation_margin == H0)
1931	      && (hyphen_line_max < 0
1932		  || hyphen_line_count + 1 <= hyphen_line_max)) {
1933	    // No need to consider a non-hyphenated breakpoint.
1934	    if (best_bp)
1935	      delete best_bp;
1936	    breakpoint *tem = bp->next;
1937	    bp->next = 0;
1938	    while (tem != 0) {
1939	      breakpoint *tem1 = tem;
1940	      tem = tem->next;
1941	      delete tem1;
1942	    }
1943	    return bp;
1944	  }
1945	  // It fits but it's hyphenated.
1946	  if (!best_bp_fits) {
1947	    if (best_bp)
1948	      delete best_bp;
1949	    best_bp = bp;
1950	    bp = bp->next;
1951	    best_bp_fits = 1;
1952	  }
1953	  else {
1954	    breakpoint *tem = bp;
1955	    bp = bp->next;
1956	    delete tem;
1957	  }
1958	}
1959      }
1960      else {
1961	if (best_bp)
1962	  delete best_bp;
1963	best_bp = bp;
1964	bp = bp->next;
1965      }
1966    }
1967    n = n->next;
1968  }
1969  if (best_bp) {
1970    if (!best_bp_fits)
1971      output_warning(WARN_BREAK, "can't break line");
1972    return best_bp;
1973  }
1974  return 0;
1975}
1976
1977void environment::hyphenate_line(int start_here)
1978{
1979  assert(line != 0);
1980  hyphenation_type prev_type = line->get_hyphenation_type();
1981  node **startp;
1982  if (start_here)
1983    startp = &line;
1984  else
1985    for (startp = &line->next; *startp != 0; startp = &(*startp)->next) {
1986      hyphenation_type this_type = (*startp)->get_hyphenation_type();
1987      if (prev_type == HYPHEN_BOUNDARY && this_type == HYPHEN_MIDDLE)
1988	break;
1989      prev_type = this_type;
1990    }
1991  if (*startp == 0)
1992    return;
1993  node *tem = *startp;
1994  do {
1995    tem = tem->next;
1996  } while (tem != 0 && tem->get_hyphenation_type() == HYPHEN_MIDDLE);
1997  int inhibit = (tem != 0 && tem->get_hyphenation_type() == HYPHEN_INHIBIT);
1998  node *end = tem;
1999  hyphen_list *sl = 0;
2000  tem = *startp;
2001  node *forward = 0;
2002  int i = 0;
2003  while (tem != end) {
2004    sl = tem->get_hyphen_list(sl, &i);
2005    node *tem1 = tem;
2006    tem = tem->next;
2007    tem1->next = forward;
2008    forward = tem1;
2009  }
2010  if (!inhibit) {
2011    // this is for characters like hyphen and emdash
2012    int prev_code = 0;
2013    for (hyphen_list *h = sl; h; h = h->next) {
2014      h->breakable = (prev_code != 0
2015		      && h->next != 0
2016		      && h->next->hyphenation_code != 0);
2017      prev_code = h->hyphenation_code;
2018    }
2019  }
2020  if (hyphenation_flags != 0
2021      && !inhibit
2022      // this may not be right if we have extra space on this line
2023      && !((hyphenation_flags & HYPHEN_LAST_LINE)
2024	   && (curdiv->distance_to_next_trap()
2025	       <= vertical_spacing + total_post_vertical_spacing()))
2026      && i >= 4)
2027    hyphenate(sl, hyphenation_flags);
2028  while (forward != 0) {
2029    node *tem1 = forward;
2030    forward = forward->next;
2031    tem1->next = 0;
2032    tem = tem1->add_self(tem, &sl);
2033  }
2034  *startp = tem;
2035}
2036
2037static node *node_list_reverse(node *n)
2038{
2039  node *res = 0;
2040  while (n) {
2041    node *tem = n;
2042    n = n->next;
2043    tem->next = res;
2044    res = tem;
2045  }
2046  return res;
2047}
2048
2049static void distribute_space(node *n, int nspaces, hunits desired_space,
2050			     int force_reverse = 0)
2051{
2052  static int reverse = 0;
2053  if (force_reverse || reverse)
2054    n = node_list_reverse(n);
2055  if (!force_reverse && nspaces > 0 && spread_limit >= 0
2056      && desired_space.to_units() > 0) {
2057    hunits em = curenv->get_size();
2058    double Ems = (double)desired_space.to_units() / nspaces
2059		 / (em.is_zero() ? hresolution : em.to_units());
2060    if (Ems > spread_limit)
2061      output_warning(WARN_BREAK, "spreading %1m per space", Ems);
2062  }
2063  for (node *tem = n; tem; tem = tem->next)
2064    tem->spread_space(&nspaces, &desired_space);
2065  if (force_reverse || reverse)
2066    (void)node_list_reverse(n);
2067  if (!force_reverse)
2068    reverse = !reverse;
2069  assert(desired_space.is_zero() && nspaces == 0);
2070}
2071
2072void environment::possibly_break_line(int start_here, int forced)
2073{
2074  int was_centered = center_lines > 0;
2075  if (!fill || current_tab || current_field || dummy)
2076    return;
2077  while (line != 0
2078	 && (forced
2079	     // When a macro follows a paragraph in fill mode, the
2080	     // current line should not be empty.
2081	     || (width_total - line->width()) > target_text_length)) {
2082    hyphenate_line(start_here);
2083    breakpoint *bp = choose_breakpoint();
2084    if (bp == 0)
2085      // we'll find one eventually
2086      return;
2087    node *pre, *post;
2088    node **ndp = &line;
2089    while (*ndp != bp->nd)
2090      ndp = &(*ndp)->next;
2091    bp->nd->split(bp->index, &pre, &post);
2092    *ndp = post;
2093    hunits extra_space_width = H0;
2094    switch(adjust_mode) {
2095    case ADJUST_BOTH:
2096      if (bp->nspaces != 0)
2097	extra_space_width = target_text_length - bp->width;
2098      else if (bp->width > 0 && target_text_length > 0
2099	       && target_text_length > bp->width)
2100	output_warning(WARN_BREAK, "cannot adjust line");
2101      break;
2102    case ADJUST_CENTER:
2103      saved_indent += (target_text_length - bp->width)/2;
2104      was_centered = 1;
2105      break;
2106    case ADJUST_RIGHT:
2107      saved_indent += target_text_length - bp->width;
2108      break;
2109    }
2110    distribute_space(pre, bp->nspaces, extra_space_width);
2111    hunits output_width = bp->width + extra_space_width;
2112    input_line_start -= output_width;
2113    if (bp->hyphenated)
2114      hyphen_line_count++;
2115    else
2116      hyphen_line_count = 0;
2117    delete bp;
2118    space_total = 0;
2119    width_total = 0;
2120    node *first_non_discardable = 0;
2121    node *tem;
2122    for (tem = line; tem != 0; tem = tem->next)
2123      if (!tem->discardable())
2124	first_non_discardable = tem;
2125    node *to_be_discarded;
2126    if (first_non_discardable) {
2127      to_be_discarded = first_non_discardable->next;
2128      first_non_discardable->next = 0;
2129      for (tem = line; tem != 0; tem = tem->next) {
2130	width_total += tem->width();
2131	space_total += tem->nspaces();
2132      }
2133      discarding = 0;
2134    }
2135    else {
2136      discarding = 1;
2137      to_be_discarded = line;
2138      line = 0;
2139    }
2140    // Do output_line() here so that line will be 0 iff the
2141    // the environment will be empty.
2142    output_line(pre, output_width, was_centered);
2143    while (to_be_discarded != 0) {
2144      tem = to_be_discarded;
2145      to_be_discarded = to_be_discarded->next;
2146      input_line_start -= tem->width();
2147     

Large files files are truncated, but you can click here to view the full file