1114402Sru// -*- C++ -*-
2151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005
3114402Sru   Free Software Foundation, Inc.
4114402Sru     Written by James Clark (jjc@jclark.com)
5114402Sru
6114402SruThis file is part of groff.
7114402Sru
8114402Srugroff is free software; you can redistribute it and/or modify it under
9114402Sruthe terms of the GNU General Public License as published by the Free
10114402SruSoftware Foundation; either version 2, or (at your option) any later
11114402Sruversion.
12114402Sru
13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
15114402SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16114402Srufor more details.
17114402Sru
18114402SruYou should have received a copy of the GNU General Public License along
19114402Sruwith groff; see the file COPYING.  If not, write to the Free Software
20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21114402Sru
22114402Sru#include "troff.h"
23114402Sru#include "dictionary.h"
24114402Sru#include "hvunits.h"
25151497Sru#include "stringclass.h"
26151497Sru#include "mtsm.h"
27114402Sru#include "env.h"
28114402Sru#include "request.h"
29114402Sru#include "node.h"
30114402Sru#include "token.h"
31114402Sru#include "div.h"
32114402Sru#include "reg.h"
33114402Sru#include "charinfo.h"
34114402Sru#include "macropath.h"
35114402Sru#include "input.h"
36114402Sru#include <math.h>
37114402Sru
38114402Srusymbol default_family("T");
39114402Sru
40114402Sruenum { ADJUST_LEFT = 0, ADJUST_BOTH = 1, ADJUST_CENTER = 3, ADJUST_RIGHT = 5 };
41114402Sru
42114402Sruenum { HYPHEN_LAST_LINE = 2, HYPHEN_LAST_CHARS = 4, HYPHEN_FIRST_CHARS = 8 };
43114402Sru
44114402Srustruct env_list {
45114402Sru  environment *env;
46114402Sru  env_list *next;
47114402Sru  env_list(environment *e, env_list *p) : env(e), next(p) {}
48114402Sru};
49114402Sru
50114402Sruenv_list *env_stack;
51114402Sruconst int NENVIRONMENTS = 10;
52114402Sruenvironment *env_table[NENVIRONMENTS];
53114402Srudictionary env_dictionary(10);
54114402Sruenvironment *curenv;
55114402Srustatic int next_line_number = 0;
56151497Sruextern int suppress_push;
57151497Sruextern statem *get_diversion_state();
58114402Sru
59114402Srucharinfo *field_delimiter_char;
60114402Srucharinfo *padding_indicator_char;
61114402Sru
62114402Sruint translate_space_to_dummy = 0;
63114402Sru
64114402Sruclass pending_output_line {
65114402Sru  node *nd;
66114402Sru  int no_fill;
67151497Sru  int was_centered;
68114402Sru  vunits vs;
69114402Sru  vunits post_vs;
70114402Sru  hunits width;
71114402Sru#ifdef WIDOW_CONTROL
72114402Sru  int last_line;		// Is it the last line of the paragraph?
73114402Sru#endif /* WIDOW_CONTROL */
74114402Srupublic:
75114402Sru  pending_output_line *next;
76114402Sru
77151497Sru  pending_output_line(node *, int, vunits, vunits, hunits, int,
78114402Sru		      pending_output_line * = 0);
79114402Sru  ~pending_output_line();
80114402Sru  int output();
81114402Sru
82114402Sru#ifdef WIDOW_CONTROL
83114402Sru  friend void environment::mark_last_line();
84151497Sru  friend void environment::output(node *, int, vunits, vunits, hunits, int);
85114402Sru#endif /* WIDOW_CONTROL */
86114402Sru};
87114402Sru
88114402Srupending_output_line::pending_output_line(node *n, int nf, vunits v, vunits pv,
89151497Sru					 hunits w, int ce,
90151497Sru					 pending_output_line *p)
91151497Sru: nd(n), no_fill(nf), was_centered(ce), vs(v), post_vs(pv), width(w),
92114402Sru#ifdef WIDOW_CONTROL
93114402Sru  last_line(0),
94114402Sru#endif /* WIDOW_CONTROL */
95114402Sru  next(p)
96114402Sru{
97114402Sru}
98114402Sru
99114402Srupending_output_line::~pending_output_line()
100114402Sru{
101114402Sru  delete_node_list(nd);
102114402Sru}
103114402Sru
104114402Sruint pending_output_line::output()
105114402Sru{
106114402Sru  if (trap_sprung_flag)
107114402Sru    return 0;
108114402Sru#ifdef WIDOW_CONTROL
109114402Sru  if (next && next->last_line && !no_fill) {
110114402Sru    curdiv->need(vs + post_vs + vunits(vresolution));
111114402Sru    if (trap_sprung_flag) {
112114402Sru      next->last_line = 0;	// Try to avoid infinite loops.
113114402Sru      return 0;
114114402Sru    }
115114402Sru  }
116114402Sru#endif
117151497Sru  curenv->construct_format_state(nd, was_centered, !no_fill);
118114402Sru  curdiv->output(nd, no_fill, vs, post_vs, width);
119114402Sru  nd = 0;
120114402Sru  return 1;
121114402Sru}
122114402Sru
123151497Sruvoid environment::output(node *nd, int no_fill_flag,
124151497Sru			 vunits vs, vunits post_vs,
125151497Sru			 hunits width, int was_centered)
126114402Sru{
127114402Sru#ifdef WIDOW_CONTROL
128114402Sru  while (pending_lines) {
129114402Sru    if (widow_control && !pending_lines->no_fill && !pending_lines->next)
130114402Sru      break;
131114402Sru    if (!pending_lines->output())
132114402Sru      break;
133114402Sru    pending_output_line *tem = pending_lines;
134114402Sru    pending_lines = pending_lines->next;
135114402Sru    delete tem;
136114402Sru  }
137114402Sru#else /* WIDOW_CONTROL */
138114402Sru  output_pending_lines();
139114402Sru#endif /* WIDOW_CONTROL */
140114402Sru  if (!trap_sprung_flag && !pending_lines
141114402Sru#ifdef WIDOW_CONTROL
142151497Sru      && (!widow_control || no_fill_flag)
143114402Sru#endif /* WIDOW_CONTROL */
144114402Sru      ) {
145151497Sru    curenv->construct_format_state(nd, was_centered, !no_fill_flag);
146151497Sru    curdiv->output(nd, no_fill_flag, vs, post_vs, width);
147114402Sru  } else {
148114402Sru    pending_output_line **p;
149114402Sru    for (p = &pending_lines; *p; p = &(*p)->next)
150114402Sru      ;
151151497Sru    *p = new pending_output_line(nd, no_fill_flag, vs, post_vs, width,
152151497Sru				 was_centered);
153114402Sru  }
154114402Sru}
155114402Sru
156114402Sru// a line from .tl goes at the head of the queue
157114402Sru
158151497Sruvoid environment::output_title(node *nd, int no_fill_flag,
159151497Sru			       vunits vs, vunits post_vs,
160151497Sru			       hunits width)
161114402Sru{
162114402Sru  if (!trap_sprung_flag)
163151497Sru    curdiv->output(nd, no_fill_flag, vs, post_vs, width);
164114402Sru  else
165151497Sru    pending_lines = new pending_output_line(nd, no_fill_flag, vs, post_vs,
166151497Sru					    width, 0, pending_lines);
167114402Sru}
168114402Sru
169114402Sruvoid environment::output_pending_lines()
170114402Sru{
171114402Sru  while (pending_lines && pending_lines->output()) {
172114402Sru    pending_output_line *tem = pending_lines;
173114402Sru    pending_lines = pending_lines->next;
174114402Sru    delete tem;
175114402Sru  }
176114402Sru}
177114402Sru
178114402Sru#ifdef WIDOW_CONTROL
179114402Sru
180114402Sruvoid environment::mark_last_line()
181114402Sru{
182114402Sru  if (!widow_control || !pending_lines)
183114402Sru    return;
184151497Sru  pending_output_line *p;
185151497Sru  for (p = pending_lines; p->next; p = p->next)
186114402Sru    ;
187114402Sru  if (!p->no_fill)
188114402Sru    p->last_line = 1;
189114402Sru}
190114402Sru
191114402Sruvoid widow_control_request()
192114402Sru{
193114402Sru  int n;
194114402Sru  if (has_arg() && get_integer(&n))
195114402Sru    curenv->widow_control = n != 0;
196114402Sru  else
197114402Sru    curenv->widow_control = 1;
198114402Sru  skip_line();
199114402Sru}
200114402Sru
201114402Sru#endif /* WIDOW_CONTROL */
202114402Sru
203114402Sru/* font_size functions */
204114402Sru
205114402Srusize_range *font_size::size_table = 0;
206114402Sruint font_size::nranges = 0;
207114402Sru
208114402Sruextern "C" {
209114402Sru
210114402Sruint compare_ranges(const void *p1, const void *p2)
211114402Sru{
212114402Sru  return ((size_range *)p1)->min - ((size_range *)p2)->min;
213114402Sru}
214114402Sru
215114402Sru}
216114402Sru
217114402Sruvoid font_size::init_size_table(int *sizes)
218114402Sru{
219114402Sru  nranges = 0;
220114402Sru  while (sizes[nranges*2] != 0)
221114402Sru    nranges++;
222114402Sru  assert(nranges > 0);
223114402Sru  size_table = new size_range[nranges];
224114402Sru  for (int i = 0; i < nranges; i++) {
225114402Sru    size_table[i].min = sizes[i*2];
226114402Sru    size_table[i].max = sizes[i*2 + 1];
227114402Sru  }
228114402Sru  qsort(size_table, nranges, sizeof(size_range), compare_ranges);
229114402Sru}
230114402Sru
231114402Srufont_size::font_size(int sp)
232114402Sru{
233114402Sru  for (int i = 0; i < nranges; i++) {
234114402Sru    if (sp < size_table[i].min) {
235114402Sru      if (i > 0 && size_table[i].min - sp >= sp - size_table[i - 1].max)
236114402Sru	p = size_table[i - 1].max;
237114402Sru      else
238114402Sru	p = size_table[i].min;
239114402Sru      return;
240114402Sru    }
241114402Sru    if (sp <= size_table[i].max) {
242114402Sru      p = sp;
243114402Sru      return;
244114402Sru    }
245114402Sru  }
246114402Sru  p = size_table[nranges - 1].max;
247114402Sru}
248114402Sru
249114402Sruint font_size::to_units()
250114402Sru{
251114402Sru  return scale(p, units_per_inch, sizescale*72);
252114402Sru}
253114402Sru
254114402Sru// we can't do this in a static constructor because various dictionaries
255114402Sru// have to get initialized first
256114402Sru
257114402Sruvoid init_environments()
258114402Sru{
259114402Sru  curenv = env_table[0] = new environment("0");
260114402Sru}
261114402Sru
262114402Sruvoid tab_character()
263114402Sru{
264114402Sru  curenv->tab_char = get_optional_char();
265114402Sru  skip_line();
266114402Sru}
267114402Sru
268114402Sruvoid leader_character()
269114402Sru{
270114402Sru  curenv->leader_char = get_optional_char();
271114402Sru  skip_line();
272114402Sru}
273114402Sru
274114402Sruvoid environment::add_char(charinfo *ci)
275114402Sru{
276114402Sru  int s;
277151497Sru  node *gc_np = 0;
278114402Sru  if (interrupted)
279114402Sru    ;
280114402Sru  // don't allow fields in dummy environments
281114402Sru  else if (ci == field_delimiter_char && !dummy) {
282114402Sru    if (current_field)
283114402Sru      wrap_up_field();
284114402Sru    else
285114402Sru      start_field();
286114402Sru  }
287114402Sru  else if (current_field && ci == padding_indicator_char)
288114402Sru    add_padding();
289114402Sru  else if (current_tab) {
290114402Sru    if (tab_contents == 0)
291114402Sru      tab_contents = new line_start_node;
292114402Sru    if (ci != hyphen_indicator_char)
293151497Sru      tab_contents = tab_contents->add_char(ci, this, &tab_width, &s, &gc_np);
294114402Sru    else
295114402Sru      tab_contents = tab_contents->add_discretionary_hyphen();
296114402Sru  }
297114402Sru  else {
298114402Sru    if (line == 0)
299114402Sru      start_line();
300151497Sru#if 0
301151497Sru    fprintf(stderr, "current line is\n");
302151497Sru    line->debug_node_list();
303151497Sru#endif
304114402Sru    if (ci != hyphen_indicator_char)
305151497Sru      line = line->add_char(ci, this, &width_total, &space_total, &gc_np);
306114402Sru    else
307114402Sru      line = line->add_discretionary_hyphen();
308114402Sru  }
309151497Sru#if 0
310151497Sru  fprintf(stderr, "now after we have added character the line is\n");
311151497Sru  line->debug_node_list();
312151497Sru#endif
313151497Sru  if ((!suppress_push) && gc_np) {
314151497Sru    if (gc_np && (gc_np->state == 0)) {
315151497Sru      gc_np->state = construct_state(0);
316151497Sru      gc_np->push_state = get_diversion_state();
317151497Sru    }
318151497Sru    else if (line && (line->state == 0)) {
319151497Sru      line->state = construct_state(0);
320151497Sru      line->push_state = get_diversion_state();
321151497Sru    }
322151497Sru  }
323151497Sru#if 0
324151497Sru  fprintf(stderr, "now we have possibly added the state the line is\n");
325151497Sru  line->debug_node_list();
326151497Sru#endif
327114402Sru}
328114402Sru
329114402Srunode *environment::make_char_node(charinfo *ci)
330114402Sru{
331114402Sru  return make_node(ci, this);
332114402Sru}
333114402Sru
334114402Sruvoid environment::add_node(node *n)
335114402Sru{
336114402Sru  if (n == 0)
337114402Sru    return;
338151497Sru  if (!suppress_push) {
339151497Sru    if (n->is_special && n->state == NULL)
340151497Sru      n->state = construct_state(0);
341151497Sru    n->push_state = get_diversion_state();
342151497Sru  }
343151497Sru
344114402Sru  if (current_tab || current_field)
345114402Sru    n->freeze_space();
346114402Sru  if (interrupted) {
347114402Sru    delete n;
348114402Sru  }
349114402Sru  else if (current_tab) {
350114402Sru    n->next = tab_contents;
351114402Sru    tab_contents = n;
352114402Sru    tab_width += n->width();
353114402Sru  }
354114402Sru  else {
355114402Sru    if (line == 0) {
356114402Sru      if (discarding && n->discardable()) {
357114402Sru	// XXX possibly: input_line_start -= n->width();
358114402Sru	delete n;
359114402Sru	return;
360114402Sru      }
361114402Sru      start_line();
362114402Sru    }
363114402Sru    width_total += n->width();
364114402Sru    space_total += n->nspaces();
365114402Sru    n->next = line;
366114402Sru    line = n;
367151497Sru    construct_new_line_state(line);
368114402Sru  }
369114402Sru}
370114402Sru
371114402Sruvoid environment::add_hyphen_indicator()
372114402Sru{
373114402Sru  if (current_tab || interrupted || current_field
374114402Sru      || hyphen_indicator_char != 0)
375114402Sru    return;
376114402Sru  if (line == 0)
377114402Sru    start_line();
378114402Sru  line = line->add_discretionary_hyphen();
379114402Sru}
380114402Sru
381114402Sruint environment::get_hyphenation_flags()
382114402Sru{
383114402Sru  return hyphenation_flags;
384114402Sru}
385114402Sru
386114402Sruint environment::get_hyphen_line_max()
387114402Sru{
388114402Sru  return hyphen_line_max;
389114402Sru}
390114402Sru
391114402Sruint environment::get_hyphen_line_count()
392114402Sru{
393114402Sru  return hyphen_line_count;
394114402Sru}
395114402Sru
396114402Sruint environment::get_center_lines()
397114402Sru{
398114402Sru  return center_lines;
399114402Sru}
400114402Sru
401114402Sruint environment::get_right_justify_lines()
402114402Sru{
403114402Sru  return right_justify_lines;
404114402Sru}
405114402Sru
406114402Sruvoid environment::add_italic_correction()
407114402Sru{
408114402Sru  if (current_tab) {
409114402Sru    if (tab_contents)
410114402Sru      tab_contents = tab_contents->add_italic_correction(&tab_width);
411114402Sru  }
412114402Sru  else if (line)
413114402Sru    line = line->add_italic_correction(&width_total);
414114402Sru}
415114402Sru
416114402Sruvoid environment::space_newline()
417114402Sru{
418114402Sru  assert(!current_tab && !current_field);
419114402Sru  if (interrupted)
420114402Sru    return;
421114402Sru  hunits x = H0;
422114402Sru  hunits sw = env_space_width(this);
423114402Sru  hunits ssw = env_sentence_space_width(this);
424114402Sru  if (!translate_space_to_dummy) {
425114402Sru    x = sw;
426114402Sru    if (node_list_ends_sentence(line) == 1)
427114402Sru      x += ssw;
428114402Sru  }
429114402Sru  width_list *w = new width_list(sw, ssw);
430114402Sru  if (node_list_ends_sentence(line) == 1)
431114402Sru    w->next = new width_list(sw, ssw);
432114402Sru  if (line != 0 && line->merge_space(x, sw, ssw)) {
433114402Sru    width_total += x;
434114402Sru    return;
435114402Sru  }
436114402Sru  add_node(new word_space_node(x, get_fill_color(), w));
437114402Sru  possibly_break_line(0, spread_flag);
438114402Sru  spread_flag = 0;
439114402Sru}
440114402Sru
441114402Sruvoid environment::space()
442114402Sru{
443114402Sru  space(env_space_width(this), env_sentence_space_width(this));
444114402Sru}
445114402Sru
446114402Sruvoid environment::space(hunits space_width, hunits sentence_space_width)
447114402Sru{
448114402Sru  if (interrupted)
449114402Sru    return;
450114402Sru  if (current_field && padding_indicator_char == 0) {
451114402Sru    add_padding();
452114402Sru    return;
453114402Sru  }
454114402Sru  hunits x = translate_space_to_dummy ? H0 : space_width;
455114402Sru  node *p = current_tab ? tab_contents : line;
456114402Sru  hunits *tp = current_tab ? &tab_width : &width_total;
457114402Sru  if (p && p->nspaces() == 1 && p->width() == x
458114402Sru      && node_list_ends_sentence(p->next) == 1) {
459114402Sru    hunits xx = translate_space_to_dummy ? H0 : sentence_space_width;
460114402Sru    if (p->merge_space(xx, space_width, sentence_space_width)) {
461114402Sru      *tp += xx;
462114402Sru      return;
463114402Sru    }
464114402Sru  }
465114402Sru  if (p && p->merge_space(x, space_width, sentence_space_width)) {
466114402Sru    *tp += x;
467114402Sru    return;
468114402Sru  }
469114402Sru  add_node(new word_space_node(x,
470114402Sru			       get_fill_color(),
471114402Sru			       new width_list(space_width,
472114402Sru					      sentence_space_width)));
473114402Sru  possibly_break_line(0, spread_flag);
474114402Sru  spread_flag = 0;
475114402Sru}
476114402Sru
477114402Srunode *do_underline_special(int);
478114402Sru
479114402Sruvoid environment::set_font(symbol nm)
480114402Sru{
481114402Sru  if (interrupted)
482114402Sru    return;
483114402Sru  if (nm == symbol("P") || nm.is_empty()) {
484114402Sru    if (family->make_definite(prev_fontno) < 0)
485114402Sru      return;
486114402Sru    int tem = fontno;
487114402Sru    fontno = prev_fontno;
488114402Sru    prev_fontno = tem;
489114402Sru  }
490114402Sru  else {
491114402Sru    prev_fontno = fontno;
492114402Sru    int n = symbol_fontno(nm);
493114402Sru    if (n < 0) {
494114402Sru      n = next_available_font_position();
495114402Sru      if (!mount_font(n, nm))
496114402Sru	return;
497114402Sru    }
498114402Sru    if (family->make_definite(n) < 0)
499114402Sru      return;
500114402Sru    fontno = n;
501114402Sru  }
502114402Sru  if (underline_spaces && fontno != prev_fontno) {
503114402Sru    if (fontno == get_underline_fontno())
504114402Sru      add_node(do_underline_special(1));
505114402Sru    if (prev_fontno == get_underline_fontno())
506114402Sru      add_node(do_underline_special(0));
507114402Sru  }
508114402Sru}
509114402Sru
510114402Sruvoid environment::set_font(int n)
511114402Sru{
512114402Sru  if (interrupted)
513114402Sru    return;
514114402Sru  if (is_good_fontno(n)) {
515114402Sru    prev_fontno = fontno;
516114402Sru    fontno = n;
517114402Sru  }
518114402Sru  else
519114402Sru    warning(WARN_FONT, "bad font number");
520114402Sru}
521114402Sru
522114402Sruvoid environment::set_family(symbol fam)
523114402Sru{
524114402Sru  if (interrupted)
525114402Sru    return;
526114402Sru  if (fam.is_null() || fam.is_empty()) {
527114402Sru    if (prev_family->make_definite(fontno) < 0)
528114402Sru      return;
529114402Sru    font_family *tem = family;
530114402Sru    family = prev_family;
531114402Sru    prev_family = tem;
532114402Sru  }
533114402Sru  else {
534114402Sru    font_family *f = lookup_family(fam);
535114402Sru    if (f->make_definite(fontno) < 0)
536114402Sru      return;
537114402Sru    prev_family = family;
538114402Sru    family = f;
539114402Sru  }
540114402Sru}
541114402Sru
542114402Sruvoid environment::set_size(int n)
543114402Sru{
544114402Sru  if (interrupted)
545114402Sru    return;
546114402Sru  if (n == 0) {
547114402Sru    font_size temp = prev_size;
548114402Sru    prev_size = size;
549114402Sru    size = temp;
550114402Sru    int temp2 = prev_requested_size;
551114402Sru    prev_requested_size = requested_size;
552114402Sru    requested_size = temp2;
553114402Sru  }
554114402Sru  else {
555114402Sru    prev_size = size;
556114402Sru    size = font_size(n);
557114402Sru    prev_requested_size = requested_size;
558114402Sru    requested_size = n;
559114402Sru  }
560114402Sru}
561114402Sru
562114402Sruvoid environment::set_char_height(int n)
563114402Sru{
564114402Sru  if (interrupted)
565114402Sru    return;
566114402Sru  if (n == requested_size || n <= 0)
567114402Sru    char_height = 0;
568114402Sru  else
569114402Sru    char_height = n;
570114402Sru}
571114402Sru
572114402Sruvoid environment::set_char_slant(int n)
573114402Sru{
574114402Sru  if (interrupted)
575114402Sru    return;
576114402Sru  char_slant = n;
577114402Sru}
578114402Sru
579114402Srucolor *environment::get_prev_glyph_color()
580114402Sru{
581114402Sru  return prev_glyph_color;
582114402Sru}
583114402Sru
584114402Srucolor *environment::get_glyph_color()
585114402Sru{
586114402Sru  return glyph_color;
587114402Sru}
588114402Sru
589114402Srucolor *environment::get_prev_fill_color()
590114402Sru{
591114402Sru  return prev_fill_color;
592114402Sru}
593114402Sru
594114402Srucolor *environment::get_fill_color()
595114402Sru{
596114402Sru  return fill_color;
597114402Sru}
598114402Sru
599114402Sruvoid environment::set_glyph_color(color *c)
600114402Sru{
601114402Sru  if (interrupted)
602114402Sru    return;
603114402Sru  curenv->prev_glyph_color = curenv->glyph_color;
604114402Sru  curenv->glyph_color = c;
605114402Sru}
606114402Sru
607114402Sruvoid environment::set_fill_color(color *c)
608114402Sru{
609114402Sru  if (interrupted)
610114402Sru    return;
611114402Sru  curenv->prev_fill_color = curenv->fill_color;
612114402Sru  curenv->fill_color = c;
613114402Sru}
614114402Sru
615114402Sruenvironment::environment(symbol nm)
616114402Sru: dummy(0),
617114402Sru  prev_line_length((units_per_inch*13)/2),
618114402Sru  line_length((units_per_inch*13)/2),
619114402Sru  prev_title_length((units_per_inch*13)/2),
620114402Sru  title_length((units_per_inch*13)/2),
621114402Sru  prev_size(sizescale*10),
622114402Sru  size(sizescale*10),
623114402Sru  requested_size(sizescale*10),
624114402Sru  prev_requested_size(sizescale*10),
625114402Sru  char_height(0),
626114402Sru  char_slant(0),
627114402Sru  space_size(12),
628114402Sru  sentence_space_size(12),
629114402Sru  adjust_mode(ADJUST_BOTH),
630114402Sru  fill(1),
631114402Sru  interrupted(0),
632114402Sru  prev_line_interrupted(0),
633114402Sru  center_lines(0),
634114402Sru  right_justify_lines(0),
635114402Sru  prev_vertical_spacing(points_to_units(12)),
636114402Sru  vertical_spacing(points_to_units(12)),
637114402Sru  prev_post_vertical_spacing(0),
638114402Sru  post_vertical_spacing(0),
639114402Sru  prev_line_spacing(1),
640114402Sru  line_spacing(1),
641114402Sru  prev_indent(0),
642114402Sru  indent(0),
643114402Sru  temporary_indent(0),
644114402Sru  have_temporary_indent(0),
645114402Sru  underline_lines(0),
646114402Sru  underline_spaces(0),
647114402Sru  input_trap_count(0),
648114402Sru  continued_input_trap(0),
649114402Sru  line(0),
650114402Sru  prev_text_length(0),
651114402Sru  width_total(0),
652114402Sru  space_total(0),
653114402Sru  input_line_start(0),
654114402Sru  line_tabs(0),
655114402Sru  current_tab(TAB_NONE),
656114402Sru  leader_node(0),
657114402Sru  tab_char(0),
658114402Sru  leader_char(charset_table['.']),
659114402Sru  current_field(0),
660114402Sru  discarding(0),
661114402Sru  spread_flag(0),
662114402Sru  margin_character_flags(0),
663114402Sru  margin_character_node(0),
664114402Sru  margin_character_distance(points_to_units(10)),
665114402Sru  numbering_nodes(0),
666114402Sru  number_text_separation(1),
667114402Sru  line_number_indent(0),
668114402Sru  line_number_multiple(1),
669114402Sru  no_number_count(0),
670114402Sru  hyphenation_flags(1),
671114402Sru  hyphen_line_count(0),
672114402Sru  hyphen_line_max(-1),
673114402Sru  hyphenation_space(H0),
674114402Sru  hyphenation_margin(H0),
675114402Sru  composite(0),
676114402Sru  pending_lines(0),
677114402Sru#ifdef WIDOW_CONTROL
678114402Sru  widow_control(0),
679114402Sru#endif /* WIDOW_CONTROL */
680114402Sru  glyph_color(&default_color),
681114402Sru  prev_glyph_color(&default_color),
682114402Sru  fill_color(&default_color),
683114402Sru  prev_fill_color(&default_color),
684151497Sru  seen_space(0),
685151497Sru  seen_eol(0),
686151497Sru  suppress_next_eol(0),
687151497Sru  seen_break(0),
688151497Sru  tabs(units_per_inch/2, TAB_LEFT),
689114402Sru  name(nm),
690114402Sru  control_char('.'),
691114402Sru  no_break_control_char('\''),
692114402Sru  hyphen_indicator_char(0)
693114402Sru{
694114402Sru  prev_family = family = lookup_family(default_family);
695114402Sru  prev_fontno = fontno = 1;
696114402Sru  if (!is_good_fontno(1))
697114402Sru    fatal("font number 1 not a valid font");
698114402Sru  if (family->make_definite(1) < 0)
699114402Sru    fatal("invalid default family `%1'", default_family.contents());
700114402Sru  prev_fontno = fontno;
701114402Sru}
702114402Sru
703114402Sruenvironment::environment(const environment *e)
704114402Sru: dummy(1),
705114402Sru  prev_line_length(e->prev_line_length),
706114402Sru  line_length(e->line_length),
707114402Sru  prev_title_length(e->prev_title_length),
708114402Sru  title_length(e->title_length),
709114402Sru  prev_size(e->prev_size),
710114402Sru  size(e->size),
711114402Sru  requested_size(e->requested_size),
712114402Sru  prev_requested_size(e->prev_requested_size),
713114402Sru  char_height(e->char_height),
714114402Sru  char_slant(e->char_slant),
715114402Sru  prev_fontno(e->prev_fontno),
716114402Sru  fontno(e->fontno),
717114402Sru  prev_family(e->prev_family),
718114402Sru  family(e->family),
719114402Sru  space_size(e->space_size),
720114402Sru  sentence_space_size(e->sentence_space_size),
721114402Sru  adjust_mode(e->adjust_mode),
722114402Sru  fill(e->fill),
723114402Sru  interrupted(0),
724114402Sru  prev_line_interrupted(0),
725114402Sru  center_lines(0),
726114402Sru  right_justify_lines(0),
727114402Sru  prev_vertical_spacing(e->prev_vertical_spacing),
728114402Sru  vertical_spacing(e->vertical_spacing),
729114402Sru  prev_post_vertical_spacing(e->prev_post_vertical_spacing),
730114402Sru  post_vertical_spacing(e->post_vertical_spacing),
731114402Sru  prev_line_spacing(e->prev_line_spacing),
732114402Sru  line_spacing(e->line_spacing),
733114402Sru  prev_indent(e->prev_indent),
734114402Sru  indent(e->indent),
735114402Sru  temporary_indent(0),
736114402Sru  have_temporary_indent(0),
737114402Sru  underline_lines(0),
738114402Sru  underline_spaces(0),
739114402Sru  input_trap_count(0),
740114402Sru  continued_input_trap(0),
741114402Sru  line(0),
742114402Sru  prev_text_length(e->prev_text_length),
743114402Sru  width_total(0),
744114402Sru  space_total(0),
745114402Sru  input_line_start(0),
746114402Sru  line_tabs(e->line_tabs),
747114402Sru  current_tab(TAB_NONE),
748114402Sru  leader_node(0),
749114402Sru  tab_char(e->tab_char),
750114402Sru  leader_char(e->leader_char),
751114402Sru  current_field(0),
752114402Sru  discarding(0),
753114402Sru  spread_flag(0),
754114402Sru  margin_character_flags(e->margin_character_flags),
755114402Sru  margin_character_node(e->margin_character_node),
756114402Sru  margin_character_distance(e->margin_character_distance),
757114402Sru  numbering_nodes(0),
758114402Sru  number_text_separation(e->number_text_separation),
759114402Sru  line_number_indent(e->line_number_indent),
760114402Sru  line_number_multiple(e->line_number_multiple),
761114402Sru  no_number_count(e->no_number_count),
762114402Sru  hyphenation_flags(e->hyphenation_flags),
763114402Sru  hyphen_line_count(0),
764114402Sru  hyphen_line_max(e->hyphen_line_max),
765114402Sru  hyphenation_space(e->hyphenation_space),
766114402Sru  hyphenation_margin(e->hyphenation_margin),
767114402Sru  composite(0),
768114402Sru  pending_lines(0),
769114402Sru#ifdef WIDOW_CONTROL
770114402Sru  widow_control(e->widow_control),
771114402Sru#endif /* WIDOW_CONTROL */
772114402Sru  glyph_color(e->glyph_color),
773114402Sru  prev_glyph_color(e->prev_glyph_color),
774114402Sru  fill_color(e->fill_color),
775114402Sru  prev_fill_color(e->prev_fill_color),
776151497Sru  seen_space(e->seen_space),
777151497Sru  seen_eol(e->seen_eol),
778151497Sru  suppress_next_eol(e->suppress_next_eol),
779151497Sru  seen_break(e->seen_break),
780151497Sru  tabs(e->tabs),
781114402Sru  name(e->name),		// so that eg `.if "\n[.ev]"0"' works
782114402Sru  control_char(e->control_char),
783114402Sru  no_break_control_char(e->no_break_control_char),
784114402Sru  hyphen_indicator_char(e->hyphen_indicator_char)
785114402Sru{
786114402Sru}
787114402Sru
788114402Sruvoid environment::copy(const environment *e)
789114402Sru{
790114402Sru  prev_line_length = e->prev_line_length;
791114402Sru  line_length = e->line_length;
792114402Sru  prev_title_length = e->prev_title_length;
793114402Sru  title_length = e->title_length;
794114402Sru  prev_size = e->prev_size;
795114402Sru  size = e->size;
796114402Sru  prev_requested_size = e->prev_requested_size;
797114402Sru  requested_size = e->requested_size;
798114402Sru  char_height = e->char_height;
799114402Sru  char_slant = e->char_slant;
800114402Sru  space_size = e->space_size;
801114402Sru  sentence_space_size = e->sentence_space_size;
802114402Sru  adjust_mode = e->adjust_mode;
803114402Sru  fill = e->fill;
804114402Sru  interrupted = 0;
805114402Sru  prev_line_interrupted = 0;
806114402Sru  center_lines = 0;
807114402Sru  right_justify_lines = 0;
808114402Sru  prev_vertical_spacing = e->prev_vertical_spacing;
809114402Sru  vertical_spacing = e->vertical_spacing;
810114402Sru  prev_post_vertical_spacing = e->prev_post_vertical_spacing,
811114402Sru  post_vertical_spacing = e->post_vertical_spacing,
812114402Sru  prev_line_spacing = e->prev_line_spacing;
813114402Sru  line_spacing = e->line_spacing;
814114402Sru  prev_indent = e->prev_indent;
815114402Sru  indent = e->indent;
816114402Sru  have_temporary_indent = 0;
817114402Sru  temporary_indent = 0;
818114402Sru  underline_lines = 0;
819114402Sru  underline_spaces = 0;
820114402Sru  input_trap_count = 0;
821114402Sru  continued_input_trap = 0;
822114402Sru  prev_text_length = e->prev_text_length;
823114402Sru  width_total = 0;
824114402Sru  space_total = 0;
825114402Sru  input_line_start = 0;
826114402Sru  control_char = e->control_char;
827114402Sru  no_break_control_char = e->no_break_control_char;
828114402Sru  hyphen_indicator_char = e->hyphen_indicator_char;
829114402Sru  spread_flag = 0;
830114402Sru  line = 0;
831114402Sru  pending_lines = 0;
832114402Sru  discarding = 0;
833114402Sru  tabs = e->tabs;
834114402Sru  line_tabs = e->line_tabs;
835114402Sru  current_tab = TAB_NONE;
836114402Sru  current_field = 0;
837114402Sru  margin_character_flags = e->margin_character_flags;
838114402Sru  margin_character_node = e->margin_character_node;
839114402Sru  margin_character_distance = e->margin_character_distance;
840114402Sru  numbering_nodes = 0;
841114402Sru  number_text_separation = e->number_text_separation;
842114402Sru  line_number_multiple = e->line_number_multiple;
843114402Sru  line_number_indent = e->line_number_indent;
844114402Sru  no_number_count = e->no_number_count;
845114402Sru  tab_char = e->tab_char;
846114402Sru  leader_char = e->leader_char;
847114402Sru  hyphenation_flags = e->hyphenation_flags;
848114402Sru  fontno = e->fontno;
849114402Sru  prev_fontno = e->prev_fontno;
850114402Sru  dummy = e->dummy;
851114402Sru  family = e->family;
852114402Sru  prev_family = e->prev_family;
853114402Sru  leader_node = 0;
854114402Sru#ifdef WIDOW_CONTROL
855114402Sru  widow_control = e->widow_control;
856114402Sru#endif /* WIDOW_CONTROL */
857114402Sru  hyphen_line_max = e->hyphen_line_max;
858114402Sru  hyphen_line_count = 0;
859114402Sru  hyphenation_space = e->hyphenation_space;
860114402Sru  hyphenation_margin = e->hyphenation_margin;
861114402Sru  composite = 0;
862114402Sru  glyph_color= e->glyph_color;
863114402Sru  prev_glyph_color = e->prev_glyph_color;
864114402Sru  fill_color = e->fill_color;
865114402Sru  prev_fill_color = e->prev_fill_color;
866114402Sru}
867114402Sru
868114402Sruenvironment::~environment()
869114402Sru{
870114402Sru  delete leader_node;
871114402Sru  delete_node_list(line);
872114402Sru  delete_node_list(numbering_nodes);
873114402Sru}
874114402Sru
875114402Sruhunits environment::get_input_line_position()
876114402Sru{
877114402Sru  hunits n;
878114402Sru  if (line == 0)
879114402Sru    n = -input_line_start;
880114402Sru  else
881114402Sru    n = width_total - input_line_start;
882114402Sru  if (current_tab)
883114402Sru    n += tab_width;
884114402Sru  return n;
885114402Sru}
886114402Sru
887114402Sruvoid environment::set_input_line_position(hunits n)
888114402Sru{
889114402Sru  input_line_start = line == 0 ? -n : width_total - n;
890114402Sru  if (current_tab)
891114402Sru    input_line_start += tab_width;
892114402Sru}
893114402Sru
894114402Sruhunits environment::get_line_length()
895114402Sru{
896114402Sru  return line_length;
897114402Sru}
898114402Sru
899114402Sruhunits environment::get_saved_line_length()
900114402Sru{
901114402Sru  if (line)
902114402Sru    return target_text_length + saved_indent;
903114402Sru  else
904114402Sru    return line_length;
905114402Sru}
906114402Sru
907114402Sruvunits environment::get_vertical_spacing()
908114402Sru{
909114402Sru  return vertical_spacing;
910114402Sru}
911114402Sru
912114402Sruvunits environment::get_post_vertical_spacing()
913114402Sru{
914114402Sru  return post_vertical_spacing;
915114402Sru}
916114402Sru
917114402Sruint environment::get_line_spacing()
918114402Sru{
919114402Sru  return line_spacing;
920114402Sru}
921114402Sru
922114402Sruvunits environment::total_post_vertical_spacing()
923114402Sru{
924114402Sru  vunits tem(post_vertical_spacing);
925114402Sru  if (line_spacing > 1)
926114402Sru    tem += (line_spacing - 1)*vertical_spacing;
927114402Sru  return tem;
928114402Sru}
929114402Sru
930114402Sruint environment::get_bold()
931114402Sru{
932114402Sru  return get_bold_fontno(fontno);
933114402Sru}
934114402Sru
935114402Sruhunits environment::get_digit_width()
936114402Sru{
937114402Sru  return env_digit_width(this);
938114402Sru}
939114402Sru
940114402Sruint environment::get_adjust_mode()
941114402Sru{
942114402Sru  return adjust_mode;
943114402Sru}
944114402Sru
945114402Sruint environment::get_fill()
946114402Sru{
947114402Sru  return fill;
948114402Sru}
949114402Sru
950114402Sruhunits environment::get_indent()
951114402Sru{
952114402Sru  return indent;
953114402Sru}
954114402Sru
955114402Sruhunits environment::get_saved_indent()
956114402Sru{
957114402Sru  if (line)
958114402Sru    return saved_indent;
959114402Sru  else if (have_temporary_indent)
960114402Sru    return temporary_indent;
961114402Sru  else
962114402Sru    return indent;
963114402Sru}
964114402Sru
965114402Sruhunits environment::get_temporary_indent()
966114402Sru{
967114402Sru  return temporary_indent;
968114402Sru}
969114402Sru
970114402Sruhunits environment::get_title_length()
971114402Sru{
972114402Sru  return title_length;
973114402Sru}
974114402Sru
975114402Srunode *environment::get_prev_char()
976114402Sru{
977114402Sru  for (node *n = current_tab ? tab_contents : line; n; n = n->next) {
978114402Sru    node *last = n->last_char_node();
979114402Sru    if (last)
980114402Sru      return last;
981114402Sru  }
982114402Sru  return 0;
983114402Sru}
984114402Sru
985114402Sruhunits environment::get_prev_char_width()
986114402Sru{
987114402Sru  node *last = get_prev_char();
988114402Sru  if (!last)
989114402Sru    return H0;
990114402Sru  return last->width();
991114402Sru}
992114402Sru
993114402Sruhunits environment::get_prev_char_skew()
994114402Sru{
995114402Sru  node *last = get_prev_char();
996114402Sru  if (!last)
997114402Sru    return H0;
998114402Sru  return last->skew();
999114402Sru}
1000114402Sru
1001114402Sruvunits environment::get_prev_char_height()
1002114402Sru{
1003114402Sru  node *last = get_prev_char();
1004114402Sru  if (!last)
1005114402Sru    return V0;
1006114402Sru  vunits min, max;
1007114402Sru  last->vertical_extent(&min, &max);
1008114402Sru  return -min;
1009114402Sru}
1010114402Sru
1011114402Sruvunits environment::get_prev_char_depth()
1012114402Sru{
1013114402Sru  node *last = get_prev_char();
1014114402Sru  if (!last)
1015114402Sru    return V0;
1016114402Sru  vunits min, max;
1017114402Sru  last->vertical_extent(&min, &max);
1018114402Sru  return max;
1019114402Sru}
1020114402Sru
1021114402Sruhunits environment::get_text_length()
1022114402Sru{
1023114402Sru  hunits n = line == 0 ? H0 : width_total;
1024114402Sru  if (current_tab)
1025114402Sru    n += tab_width;
1026114402Sru  return n;
1027114402Sru}
1028114402Sru
1029114402Sruhunits environment::get_prev_text_length()
1030114402Sru{
1031114402Sru  return prev_text_length;
1032114402Sru}
1033114402Sru
1034114402Sru
1035114402Srustatic int sb_reg_contents = 0;
1036114402Srustatic int st_reg_contents = 0;
1037114402Srustatic int ct_reg_contents = 0;
1038114402Srustatic int rsb_reg_contents = 0;
1039114402Srustatic int rst_reg_contents = 0;
1040114402Srustatic int skw_reg_contents = 0;
1041114402Srustatic int ssc_reg_contents = 0;
1042114402Sru
1043114402Sruvoid environment::width_registers()
1044114402Sru{
1045114402Sru  // this is used to implement \w; it sets the st, sb, ct registers
1046114402Sru  vunits min = 0, max = 0, cur = 0;
1047114402Sru  int character_type = 0;
1048114402Sru  ssc_reg_contents = line ? line->subscript_correction().to_units() : 0;
1049114402Sru  skw_reg_contents = line ? line->skew().to_units() : 0;
1050114402Sru  line = reverse_node_list(line);
1051114402Sru  vunits real_min = V0;
1052114402Sru  vunits real_max = V0;
1053114402Sru  vunits v1, v2;
1054114402Sru  for (node *tem = line; tem; tem = tem->next) {
1055114402Sru    tem->vertical_extent(&v1, &v2);
1056114402Sru    v1 += cur;
1057114402Sru    if (v1 < real_min)
1058114402Sru      real_min = v1;
1059114402Sru    v2 += cur;
1060114402Sru    if (v2 > real_max)
1061114402Sru      real_max = v2;
1062114402Sru    if ((cur += tem->vertical_width()) < min)
1063114402Sru      min = cur;
1064114402Sru    else if (cur > max)
1065114402Sru      max = cur;
1066114402Sru    character_type |= tem->character_type();
1067114402Sru  }
1068114402Sru  line = reverse_node_list(line);
1069114402Sru  st_reg_contents = -min.to_units();
1070114402Sru  sb_reg_contents = -max.to_units();
1071114402Sru  rst_reg_contents = -real_min.to_units();
1072114402Sru  rsb_reg_contents = -real_max.to_units();
1073114402Sru  ct_reg_contents = character_type;
1074114402Sru}
1075114402Sru
1076114402Srunode *environment::extract_output_line()
1077114402Sru{
1078114402Sru  if (current_tab)
1079114402Sru    wrap_up_tab();
1080114402Sru  node *n = line;
1081114402Sru  line = 0;
1082114402Sru  return n;
1083114402Sru}
1084114402Sru
1085114402Sru/* environment related requests */
1086114402Sru
1087114402Sruvoid environment_switch()
1088114402Sru{
1089114402Sru  int pop = 0;	// 1 means pop, 2 means pop but no error message on underflow
1090114402Sru  if (curenv->is_dummy())
1091114402Sru    error("can't switch environments when current environment is dummy");
1092114402Sru  else if (!has_arg())
1093114402Sru    pop = 1;
1094114402Sru  else {
1095114402Sru    symbol nm;
1096114402Sru    if (!tok.delimiter()) {
1097114402Sru      // It looks like a number.
1098114402Sru      int n;
1099114402Sru      if (get_integer(&n)) {
1100114402Sru	if (n >= 0 && n < NENVIRONMENTS) {
1101114402Sru	  env_stack = new env_list(curenv, env_stack);
1102114402Sru	  if (env_table[n] == 0)
1103114402Sru	    env_table[n] = new environment(i_to_a(n));
1104114402Sru	  curenv = env_table[n];
1105114402Sru	}
1106114402Sru	else
1107114402Sru	  nm = i_to_a(n);
1108114402Sru      }
1109114402Sru      else
1110114402Sru	pop = 2;
1111114402Sru    }
1112114402Sru    else {
1113114402Sru      nm = get_long_name(1);
1114114402Sru      if (nm.is_null())
1115114402Sru	pop = 2;
1116114402Sru    }
1117114402Sru    if (!nm.is_null()) {
1118114402Sru      environment *e = (environment *)env_dictionary.lookup(nm);
1119114402Sru      if (!e) {
1120114402Sru	e = new environment(nm);
1121114402Sru	(void)env_dictionary.lookup(nm, e);
1122114402Sru      }
1123114402Sru      env_stack = new env_list(curenv, env_stack);
1124114402Sru      curenv = e;
1125114402Sru    }
1126114402Sru  }
1127114402Sru  if (pop) {
1128114402Sru    if (env_stack == 0) {
1129114402Sru      if (pop == 1)
1130114402Sru	error("environment stack underflow");
1131114402Sru    }
1132114402Sru    else {
1133151497Sru      int seen_space = curenv->seen_space;
1134151497Sru      int seen_eol   = curenv->seen_eol;
1135151497Sru      int suppress_next_eol = curenv->suppress_next_eol;
1136114402Sru      curenv = env_stack->env;
1137151497Sru      curenv->seen_space = seen_space;
1138151497Sru      curenv->seen_eol   = seen_eol;
1139151497Sru      curenv->suppress_next_eol = suppress_next_eol;
1140114402Sru      env_list *tem = env_stack;
1141114402Sru      env_stack = env_stack->next;
1142114402Sru      delete tem;
1143114402Sru    }
1144114402Sru  }
1145114402Sru  skip_line();
1146114402Sru}
1147114402Sru
1148114402Sruvoid environment_copy()
1149114402Sru{
1150114402Sru  symbol nm;
1151114402Sru  environment *e=0;
1152114402Sru  tok.skip();
1153114402Sru  if (!tok.delimiter()) {
1154114402Sru    // It looks like a number.
1155114402Sru    int n;
1156114402Sru    if (get_integer(&n)) {
1157114402Sru      if (n >= 0 && n < NENVIRONMENTS)
1158114402Sru	e = env_table[n];
1159114402Sru      else
1160114402Sru	nm = i_to_a(n);
1161114402Sru    }
1162114402Sru  }
1163114402Sru  else
1164114402Sru    nm = get_long_name(1);
1165114402Sru  if (!e && !nm.is_null())
1166114402Sru    e = (environment *)env_dictionary.lookup(nm);
1167114402Sru  if (e == 0) {
1168114402Sru    error("No environment to copy from");
1169114402Sru    return;
1170114402Sru  }
1171114402Sru  else
1172114402Sru    curenv->copy(e);
1173114402Sru  skip_line();
1174114402Sru}
1175114402Sru
1176151497Sruvoid fill_color_change()
1177151497Sru{
1178151497Sru  symbol s = get_name();
1179151497Sru  if (s.is_null())
1180151497Sru    curenv->set_fill_color(curenv->get_prev_fill_color());
1181151497Sru  else
1182151497Sru    do_fill_color(s);
1183151497Sru  skip_line();
1184151497Sru}
1185151497Sru
1186151497Sruvoid glyph_color_change()
1187151497Sru{
1188151497Sru  symbol s = get_name();
1189151497Sru  if (s.is_null())
1190151497Sru    curenv->set_glyph_color(curenv->get_prev_glyph_color());
1191151497Sru  else
1192151497Sru    do_glyph_color(s);
1193151497Sru  skip_line();
1194151497Sru}
1195151497Sru
1196114402Srustatic symbol P_symbol("P");
1197114402Sru
1198114402Sruvoid font_change()
1199114402Sru{
1200114402Sru  symbol s = get_name();
1201114402Sru  int is_number = 1;
1202114402Sru  if (s.is_null() || s == P_symbol) {
1203114402Sru    s = P_symbol;
1204114402Sru    is_number = 0;
1205114402Sru  }
1206114402Sru  else {
1207114402Sru    for (const char *p = s.contents(); p != 0 && *p != 0; p++)
1208114402Sru      if (!csdigit(*p)) {
1209114402Sru	is_number = 0;
1210114402Sru	break;
1211114402Sru      }
1212114402Sru  }
1213114402Sru  if (is_number)
1214114402Sru    curenv->set_font(atoi(s.contents()));
1215114402Sru  else
1216114402Sru    curenv->set_font(s);
1217114402Sru  skip_line();
1218114402Sru}
1219114402Sru
1220114402Sruvoid family_change()
1221114402Sru{
1222114402Sru  symbol s = get_name();
1223114402Sru  curenv->set_family(s);
1224114402Sru  skip_line();
1225114402Sru}
1226114402Sru
1227114402Sruvoid point_size()
1228114402Sru{
1229114402Sru  int n;
1230114402Sru  if (has_arg() && get_number(&n, 'z', curenv->get_requested_point_size())) {
1231114402Sru    if (n <= 0)
1232114402Sru      n = 1;
1233114402Sru    curenv->set_size(n);
1234114402Sru  }
1235114402Sru  else
1236114402Sru    curenv->set_size(0);
1237114402Sru  skip_line();
1238114402Sru}
1239114402Sru
1240114402Sruvoid override_sizes()
1241114402Sru{
1242114402Sru  int n = 16;
1243114402Sru  int *sizes = new int[n];
1244114402Sru  int i = 0;
1245114402Sru  char *buf = read_string();
1246114402Sru  if (!buf)
1247114402Sru    return;
1248114402Sru  char *p = strtok(buf, " \t");
1249114402Sru  for (;;) {
1250114402Sru    if (!p)
1251114402Sru      break;
1252114402Sru    int lower, upper;
1253114402Sru    switch (sscanf(p, "%d-%d", &lower, &upper)) {
1254114402Sru    case 1:
1255114402Sru      upper = lower;
1256114402Sru      // fall through
1257114402Sru    case 2:
1258114402Sru      if (lower <= upper && lower >= 0)
1259114402Sru	break;
1260114402Sru      // fall through
1261114402Sru    default:
1262114402Sru      warning(WARN_RANGE, "bad size range `%1'", p);
1263114402Sru      return;
1264114402Sru    }
1265114402Sru    if (i + 2 > n) {
1266114402Sru      int *old_sizes = sizes;
1267114402Sru      sizes = new int[n*2];
1268114402Sru      memcpy(sizes, old_sizes, n*sizeof(int));
1269114402Sru      n *= 2;
1270114402Sru      a_delete old_sizes;
1271114402Sru    }
1272114402Sru    sizes[i++] = lower;
1273114402Sru    if (lower == 0)
1274114402Sru      break;
1275114402Sru    sizes[i++] = upper;
1276114402Sru    p = strtok(0, " \t");
1277114402Sru  }
1278114402Sru  font_size::init_size_table(sizes);
1279114402Sru}
1280114402Sru
1281114402Sruvoid space_size()
1282114402Sru{
1283114402Sru  int n;
1284114402Sru  if (get_integer(&n)) {
1285114402Sru    curenv->space_size = n;
1286114402Sru    if (has_arg() && get_integer(&n))
1287114402Sru      curenv->sentence_space_size = n;
1288114402Sru    else
1289114402Sru      curenv->sentence_space_size = curenv->space_size;
1290114402Sru  }
1291114402Sru  skip_line();
1292114402Sru}
1293114402Sru
1294114402Sruvoid fill()
1295114402Sru{
1296114402Sru  while (!tok.newline() && !tok.eof())
1297114402Sru    tok.next();
1298114402Sru  if (break_flag)
1299114402Sru    curenv->do_break();
1300114402Sru  curenv->fill = 1;
1301114402Sru  tok.next();
1302114402Sru}
1303114402Sru
1304114402Sruvoid no_fill()
1305114402Sru{
1306114402Sru  while (!tok.newline() && !tok.eof())
1307114402Sru    tok.next();
1308114402Sru  if (break_flag)
1309114402Sru    curenv->do_break();
1310114402Sru  curenv->fill = 0;
1311151497Sru  curenv->suppress_next_eol = 1;
1312114402Sru  tok.next();
1313114402Sru}
1314114402Sru
1315114402Sruvoid center()
1316114402Sru{
1317114402Sru  int n;
1318114402Sru  if (!has_arg() || !get_integer(&n))
1319114402Sru    n = 1;
1320114402Sru  else if (n < 0)
1321114402Sru    n = 0;
1322114402Sru  while (!tok.newline() && !tok.eof())
1323114402Sru    tok.next();
1324114402Sru  if (break_flag)
1325114402Sru    curenv->do_break();
1326114402Sru  curenv->right_justify_lines = 0;
1327114402Sru  curenv->center_lines = n;
1328151497Sru  curdiv->modified_tag.incl(MTSM_CE);
1329114402Sru  tok.next();
1330114402Sru}
1331114402Sru
1332114402Sruvoid right_justify()
1333114402Sru{
1334114402Sru  int n;
1335114402Sru  if (!has_arg() || !get_integer(&n))
1336114402Sru    n = 1;
1337114402Sru  else if (n < 0)
1338114402Sru    n = 0;
1339114402Sru  while (!tok.newline() && !tok.eof())
1340114402Sru    tok.next();
1341114402Sru  if (break_flag)
1342114402Sru    curenv->do_break();
1343114402Sru  curenv->center_lines = 0;
1344114402Sru  curenv->right_justify_lines = n;
1345151497Sru  curdiv->modified_tag.incl(MTSM_RJ);
1346114402Sru  tok.next();
1347114402Sru}
1348114402Sru
1349114402Sruvoid line_length()
1350114402Sru{
1351114402Sru  hunits temp;
1352114402Sru  if (has_arg() && get_hunits(&temp, 'm', curenv->line_length)) {
1353114402Sru    if (temp < H0) {
1354114402Sru      warning(WARN_RANGE, "bad line length %1u", temp.to_units());
1355114402Sru      temp = H0;
1356114402Sru    }
1357114402Sru  }
1358114402Sru  else
1359114402Sru    temp = curenv->prev_line_length;
1360114402Sru  curenv->prev_line_length = curenv->line_length;
1361114402Sru  curenv->line_length = temp;
1362151497Sru  curdiv->modified_tag.incl(MTSM_LL);
1363114402Sru  skip_line();
1364114402Sru}
1365114402Sru
1366114402Sruvoid title_length()
1367114402Sru{
1368114402Sru  hunits temp;
1369114402Sru  if (has_arg() && get_hunits(&temp, 'm', curenv->title_length)) {
1370114402Sru    if (temp < H0) {
1371114402Sru      warning(WARN_RANGE, "bad title length %1u", temp.to_units());
1372114402Sru      temp = H0;
1373114402Sru    }
1374114402Sru  }
1375114402Sru  else
1376114402Sru    temp = curenv->prev_title_length;
1377114402Sru  curenv->prev_title_length = curenv->title_length;
1378114402Sru  curenv->title_length = temp;
1379114402Sru  skip_line();
1380114402Sru}
1381114402Sru
1382114402Sruvoid vertical_spacing()
1383114402Sru{
1384114402Sru  vunits temp;
1385114402Sru  if (has_arg() && get_vunits(&temp, 'p', curenv->vertical_spacing)) {
1386114402Sru    if (temp < V0) {
1387114402Sru      warning(WARN_RANGE, "vertical spacing must not be negative");
1388114402Sru      temp = vresolution;
1389114402Sru    }
1390114402Sru  }
1391114402Sru  else
1392114402Sru    temp = curenv->prev_vertical_spacing;
1393114402Sru  curenv->prev_vertical_spacing = curenv->vertical_spacing;
1394114402Sru  curenv->vertical_spacing = temp;
1395114402Sru  skip_line();
1396114402Sru}
1397114402Sru
1398114402Sruvoid post_vertical_spacing()
1399114402Sru{
1400114402Sru  vunits temp;
1401114402Sru  if (has_arg() && get_vunits(&temp, 'p', curenv->post_vertical_spacing)) {
1402114402Sru    if (temp < V0) {
1403114402Sru      warning(WARN_RANGE,
1404114402Sru	      "post vertical spacing must be greater than or equal to 0");
1405114402Sru      temp = V0;
1406114402Sru    }
1407114402Sru  }
1408114402Sru  else
1409114402Sru    temp = curenv->prev_post_vertical_spacing;
1410114402Sru  curenv->prev_post_vertical_spacing = curenv->post_vertical_spacing;
1411114402Sru  curenv->post_vertical_spacing = temp;
1412114402Sru  skip_line();
1413114402Sru}
1414114402Sru
1415114402Sruvoid line_spacing()
1416114402Sru{
1417114402Sru  int temp;
1418114402Sru  if (has_arg() && get_integer(&temp)) {
1419114402Sru    if (temp < 1) {
1420114402Sru      warning(WARN_RANGE, "value %1 out of range: interpreted as 1", temp);
1421114402Sru      temp = 1;
1422114402Sru    }
1423114402Sru  }
1424114402Sru  else
1425114402Sru    temp = curenv->prev_line_spacing;
1426114402Sru  curenv->prev_line_spacing = curenv->line_spacing;
1427114402Sru  curenv->line_spacing = temp;
1428114402Sru  skip_line();
1429114402Sru}
1430114402Sru
1431114402Sruvoid indent()
1432114402Sru{
1433114402Sru  hunits temp;
1434114402Sru  if (has_arg() && get_hunits(&temp, 'm', curenv->indent)) {
1435114402Sru    if (temp < H0) {
1436114402Sru      warning(WARN_RANGE, "indent cannot be negative");
1437114402Sru      temp = H0;
1438114402Sru    }
1439114402Sru  }
1440114402Sru  else
1441114402Sru    temp = curenv->prev_indent;
1442114402Sru  while (!tok.newline() && !tok.eof())
1443114402Sru    tok.next();
1444114402Sru  if (break_flag)
1445114402Sru    curenv->do_break();
1446114402Sru  curenv->have_temporary_indent = 0;
1447114402Sru  curenv->prev_indent = curenv->indent;
1448114402Sru  curenv->indent = temp;
1449151497Sru  curdiv->modified_tag.incl(MTSM_IN);
1450114402Sru  tok.next();
1451114402Sru}
1452114402Sru
1453114402Sruvoid temporary_indent()
1454114402Sru{
1455114402Sru  int err = 0;
1456114402Sru  hunits temp;
1457114402Sru  if (!get_hunits(&temp, 'm', curenv->get_indent()))
1458114402Sru    err = 1;
1459114402Sru  while (!tok.newline() && !tok.eof())
1460114402Sru    tok.next();
1461114402Sru  if (break_flag)
1462114402Sru    curenv->do_break();
1463114402Sru  if (temp < H0) {
1464114402Sru    warning(WARN_RANGE, "total indent cannot be negative");
1465114402Sru    temp = H0;
1466114402Sru  }
1467114402Sru  if (!err) {
1468114402Sru    curenv->temporary_indent = temp;
1469114402Sru    curenv->have_temporary_indent = 1;
1470151497Sru    curdiv->modified_tag.incl(MTSM_TI);
1471114402Sru  }
1472114402Sru  tok.next();
1473114402Sru}
1474114402Sru
1475114402Srunode *do_underline_special(int underline_spaces)
1476114402Sru{
1477114402Sru  macro m;
1478114402Sru  m.append_str("x u ");
1479114402Sru  m.append(underline_spaces + '0');
1480114402Sru  return new special_node(m, 1);
1481114402Sru}
1482114402Sru
1483114402Sruvoid do_underline(int underline_spaces)
1484114402Sru{
1485114402Sru  int n;
1486114402Sru  if (!has_arg() || !get_integer(&n))
1487114402Sru    n = 1;
1488114402Sru  if (n <= 0) {
1489114402Sru    if (curenv->underline_lines > 0) {
1490114402Sru      curenv->prev_fontno = curenv->fontno;
1491114402Sru      curenv->fontno = curenv->pre_underline_fontno;
1492114402Sru      if (underline_spaces) {
1493114402Sru	curenv->underline_spaces = 0;
1494114402Sru	curenv->add_node(do_underline_special(0));
1495114402Sru      }
1496114402Sru    }
1497114402Sru    curenv->underline_lines = 0;
1498114402Sru  }
1499114402Sru  else {
1500114402Sru    curenv->underline_lines = n;
1501114402Sru    curenv->pre_underline_fontno = curenv->fontno;
1502114402Sru    curenv->fontno = get_underline_fontno();
1503114402Sru    if (underline_spaces) {
1504114402Sru      curenv->underline_spaces = 1;
1505114402Sru      curenv->add_node(do_underline_special(1));
1506114402Sru    }
1507114402Sru  }
1508114402Sru  skip_line();
1509114402Sru}
1510114402Sru
1511114402Sruvoid continuous_underline()
1512114402Sru{
1513114402Sru  do_underline(1);
1514114402Sru}
1515114402Sru
1516114402Sruvoid underline()
1517114402Sru{
1518114402Sru  do_underline(0);
1519114402Sru}
1520114402Sru
1521114402Sruvoid control_char()
1522114402Sru{
1523114402Sru  curenv->control_char = '.';
1524114402Sru  if (has_arg()) {
1525114402Sru    if (tok.ch() == 0)
1526114402Sru      error("bad control character");
1527114402Sru    else
1528114402Sru      curenv->control_char = tok.ch();
1529114402Sru  }
1530114402Sru  skip_line();
1531114402Sru}
1532114402Sru
1533114402Sruvoid no_break_control_char()
1534114402Sru{
1535114402Sru  curenv->no_break_control_char = '\'';
1536114402Sru  if (has_arg()) {
1537114402Sru    if (tok.ch() == 0)
1538114402Sru      error("bad control character");
1539114402Sru    else
1540114402Sru      curenv->no_break_control_char = tok.ch();
1541114402Sru  }
1542114402Sru  skip_line();
1543114402Sru}
1544114402Sru
1545114402Sruvoid margin_character()
1546114402Sru{
1547114402Sru  while (tok.space())
1548114402Sru    tok.next();
1549114402Sru  charinfo *ci = tok.get_char();
1550114402Sru  if (ci) {
1551114402Sru    // Call tok.next() only after making the node so that
1552114402Sru    // .mc \s+9\(br\s0 works.
1553114402Sru    node *nd = curenv->make_char_node(ci);
1554114402Sru    tok.next();
1555114402Sru    if (nd) {
1556114402Sru      delete curenv->margin_character_node;
1557114402Sru      curenv->margin_character_node = nd;
1558114402Sru      curenv->margin_character_flags = (MARGIN_CHARACTER_ON
1559114402Sru					|MARGIN_CHARACTER_NEXT);
1560114402Sru      hunits d;
1561114402Sru      if (has_arg() && get_hunits(&d, 'm'))
1562114402Sru	curenv->margin_character_distance = d;
1563114402Sru    }
1564114402Sru  }
1565114402Sru  else {
1566114402Sru    check_missing_character();
1567114402Sru    curenv->margin_character_flags &= ~MARGIN_CHARACTER_ON;
1568114402Sru    if (curenv->margin_character_flags == 0) {
1569114402Sru      delete curenv->margin_character_node;
1570114402Sru      curenv->margin_character_node = 0;
1571114402Sru    }
1572114402Sru  }
1573114402Sru  skip_line();
1574114402Sru}
1575114402Sru
1576114402Sruvoid number_lines()
1577114402Sru{
1578114402Sru  delete_node_list(curenv->numbering_nodes);
1579114402Sru  curenv->numbering_nodes = 0;
1580114402Sru  if (has_arg()) {
1581114402Sru    node *nd = 0;
1582114402Sru    for (int i = '9'; i >= '0'; i--) {
1583114402Sru      node *tem = make_node(charset_table[i], curenv);
1584114402Sru      if (!tem) {
1585114402Sru	skip_line();
1586114402Sru	return;
1587114402Sru      }
1588114402Sru      tem->next = nd;
1589114402Sru      nd = tem;
1590114402Sru    }
1591114402Sru    curenv->numbering_nodes = nd;
1592114402Sru    curenv->line_number_digit_width = env_digit_width(curenv);
1593114402Sru    int n;
1594114402Sru    if (!tok.delimiter()) {
1595114402Sru      if (get_integer(&n, next_line_number)) {
1596114402Sru	next_line_number = n;
1597114402Sru	if (next_line_number < 0) {
1598114402Sru	  warning(WARN_RANGE, "negative line number");
1599114402Sru	  next_line_number = 0;
1600114402Sru	}
1601114402Sru      }
1602114402Sru    }
1603114402Sru    else
1604114402Sru      while (!tok.space() && !tok.newline() && !tok.eof())
1605114402Sru	tok.next();
1606114402Sru    if (has_arg()) {
1607114402Sru      if (!tok.delimiter()) {
1608114402Sru	if (get_integer(&n)) {
1609114402Sru	  if (n <= 0) {
1610114402Sru	    warning(WARN_RANGE, "negative or zero line number multiple");
1611114402Sru	  }
1612114402Sru	  else
1613114402Sru	    curenv->line_number_multiple = n;
1614114402Sru	}
1615114402Sru      }
1616114402Sru      else
1617114402Sru	while (!tok.space() && !tok.newline() && !tok.eof())
1618114402Sru	  tok.next();
1619114402Sru      if (has_arg()) {
1620114402Sru	if (!tok.delimiter()) {
1621114402Sru	  if (get_integer(&n))
1622114402Sru	    curenv->number_text_separation = n;
1623114402Sru	}
1624114402Sru	else
1625114402Sru	  while (!tok.space() && !tok.newline() && !tok.eof())
1626114402Sru	    tok.next();
1627114402Sru	if (has_arg() && !tok.delimiter() && get_integer(&n))
1628114402Sru	  curenv->line_number_indent = n;
1629114402Sru      }
1630114402Sru    }
1631114402Sru  }
1632114402Sru  skip_line();
1633114402Sru}
1634114402Sru
1635114402Sruvoid no_number()
1636114402Sru{
1637114402Sru  int n;
1638114402Sru  if (has_arg() && get_integer(&n))
1639114402Sru    curenv->no_number_count = n > 0 ? n : 0;
1640114402Sru  else
1641114402Sru    curenv->no_number_count = 1;
1642114402Sru  skip_line();
1643114402Sru}
1644114402Sru
1645114402Sruvoid no_hyphenate()
1646114402Sru{
1647114402Sru  curenv->hyphenation_flags = 0;
1648114402Sru  skip_line();
1649114402Sru}
1650114402Sru
1651114402Sruvoid hyphenate_request()
1652114402Sru{
1653114402Sru  int n;
1654114402Sru  if (has_arg() && get_integer(&n))
1655114402Sru    curenv->hyphenation_flags = n;
1656114402Sru  else
1657114402Sru    curenv->hyphenation_flags = 1;
1658114402Sru  skip_line();
1659114402Sru}
1660114402Sru
1661114402Sruvoid hyphen_char()
1662114402Sru{
1663114402Sru  curenv->hyphen_indicator_char = get_optional_char();
1664114402Sru  skip_line();
1665114402Sru}
1666114402Sru
1667114402Sruvoid hyphen_line_max_request()
1668114402Sru{
1669114402Sru  int n;
1670114402Sru  if (has_arg() && get_integer(&n))
1671114402Sru    curenv->hyphen_line_max = n;
1672114402Sru  else
1673114402Sru    curenv->hyphen_line_max = -1;
1674114402Sru  skip_line();
1675114402Sru}
1676114402Sru
1677114402Sruvoid environment::interrupt()
1678114402Sru{
1679114402Sru  if (!dummy) {
1680114402Sru    add_node(new transparent_dummy_node);
1681114402Sru    interrupted = 1;
1682114402Sru  }
1683114402Sru}
1684114402Sru
1685114402Sruvoid environment::newline()
1686114402Sru{
1687151497Sru  int was_centered = 0;
1688114402Sru  if (underline_lines > 0) {
1689114402Sru    if (--underline_lines == 0) {
1690114402Sru      prev_fontno = fontno;
1691114402Sru      fontno = pre_underline_fontno;
1692114402Sru      if (underline_spaces) {
1693114402Sru        underline_spaces = 0;
1694114402Sru        add_node(do_underline_special(0));
1695114402Sru      }
1696114402Sru    }
1697114402Sru  }
1698114402Sru  if (current_field)
1699114402Sru    wrap_up_field();
1700114402Sru  if (current_tab)
1701114402Sru    wrap_up_tab();
1702114402Sru  // strip trailing spaces
1703114402Sru  while (line != 0 && line->discardable()) {
1704114402Sru    width_total -= line->width();
1705114402Sru    space_total -= line->nspaces();
1706114402Sru    node *tem = line;
1707114402Sru    line = line->next;
1708114402Sru    delete tem;
1709114402Sru  }
1710114402Sru  node *to_be_output = 0;
1711114402Sru  hunits to_be_output_width;
1712114402Sru  prev_line_interrupted = 0;
1713114402Sru  if (dummy)
1714114402Sru    space_newline();
1715114402Sru  else if (interrupted) {
1716114402Sru    interrupted = 0;
1717114402Sru    // see environment::final_break
1718114402Sru    prev_line_interrupted = exit_started ? 2 : 1;
1719114402Sru  }
1720114402Sru  else if (center_lines > 0) {
1721114402Sru    --center_lines;
1722114402Sru    hunits x = target_text_length - width_total;
1723114402Sru    if (x > H0)
1724114402Sru      saved_indent += x/2;
1725114402Sru    to_be_output = line;
1726151497Sru    was_centered = 1;
1727114402Sru    to_be_output_width = width_total;
1728114402Sru    line = 0;
1729114402Sru  }
1730114402Sru  else if (right_justify_lines > 0) {
1731114402Sru    --right_justify_lines;
1732114402Sru    hunits x = target_text_length - width_total;
1733114402Sru    if (x > H0)
1734114402Sru      saved_indent += x;
1735114402Sru    to_be_output = line;
1736114402Sru    to_be_output_width = width_total;
1737114402Sru    line = 0;
1738114402Sru  }
1739114402Sru  else if (fill)
1740114402Sru    space_newline();
1741114402Sru  else {
1742114402Sru    to_be_output = line;
1743114402Sru    to_be_output_width = width_total;
1744114402Sru    line = 0;
1745114402Sru  }
1746114402Sru  input_line_start = line == 0 ? H0 : width_total;
1747114402Sru  if (to_be_output) {
1748114402Sru    if (is_html && !fill) {
1749151497Sru      curdiv->modified_tag.incl(MTSM_EOL);
1750151497Sru      if (suppress_next_eol)
1751151497Sru	suppress_next_eol = 0;
1752151497Sru      else
1753151497Sru	seen_eol = 1;
1754151497Sru    }
1755114402Sru
1756151497Sru    output_line(to_be_output, to_be_output_width, was_centered);
1757114402Sru    hyphen_line_count = 0;
1758114402Sru  }
1759114402Sru  if (input_trap_count > 0) {
1760114402Sru    if (!(continued_input_trap && prev_line_interrupted))
1761114402Sru      if (--input_trap_count == 0)
1762114402Sru	spring_trap(input_trap);
1763114402Sru  }
1764114402Sru}
1765114402Sru
1766151497Sruvoid environment::output_line(node *n, hunits width, int was_centered)
1767114402Sru{
1768114402Sru  prev_text_length = width;
1769114402Sru  if (margin_character_flags) {
1770114402Sru    hunits d = line_length + margin_character_distance - saved_indent - width;
1771114402Sru    if (d > 0) {
1772114402Sru      n = new hmotion_node(d, get_fill_color(), n);
1773114402Sru      width += d;
1774114402Sru    }
1775114402Sru    margin_character_flags &= ~MARGIN_CHARACTER_NEXT;
1776114402Sru    node *tem;
1777114402Sru    if (!margin_character_flags) {
1778114402Sru      tem = margin_character_node;
1779114402Sru      margin_character_node = 0;
1780114402Sru    }
1781114402Sru    else
1782114402Sru      tem = margin_character_node->copy();
1783114402Sru    tem->next = n;
1784114402Sru    n = tem;
1785114402Sru    width += tem->width();
1786114402Sru  }
1787114402Sru  node *nn = 0;
1788114402Sru  while (n != 0) {
1789114402Sru    node *tem = n->next;
1790114402Sru    n->next = nn;
1791114402Sru    nn = n;
1792114402Sru    n = tem;
1793114402Sru  }
1794114402Sru  if (!saved_indent.is_zero())
1795114402Sru    nn = new hmotion_node(saved_indent, get_fill_color(), nn);
1796114402Sru  width += saved_indent;
1797114402Sru  if (no_number_count > 0)
1798114402Sru    --no_number_count;
1799114402Sru  else if (numbering_nodes) {
1800114402Sru    hunits w = (line_number_digit_width
1801114402Sru		*(3+line_number_indent+number_text_separation));
1802114402Sru    if (next_line_number % line_number_multiple != 0)
1803114402Sru      nn = new hmotion_node(w, get_fill_color(), nn);
1804114402Sru    else {
1805114402Sru      hunits x = w;
1806114402Sru      nn = new hmotion_node(number_text_separation * line_number_digit_width,
1807114402Sru			    get_fill_color(), nn);
1808114402Sru      x -= number_text_separation*line_number_digit_width;
1809114402Sru      char buf[30];
1810114402Sru      sprintf(buf, "%3d", next_line_number);
1811114402Sru      for (char *p = strchr(buf, '\0') - 1; p >= buf && *p != ' '; --p) {
1812114402Sru	node *gn = numbering_nodes;
1813114402Sru	for (int count = *p - '0'; count > 0; count--)
1814114402Sru	  gn = gn->next;
1815114402Sru	gn = gn->copy();
1816114402Sru	x -= gn->width();
1817114402Sru	gn->next = nn;
1818114402Sru	nn = gn;
1819114402Sru      }
1820114402Sru      nn = new hmotion_node(x, get_fill_color(), nn);
1821114402Sru    }
1822114402Sru    width += w;
1823114402Sru    ++next_line_number;
1824114402Sru  }
1825151497Sru  output(nn, !fill, vertical_spacing, total_post_vertical_spacing(), width,
1826151497Sru	 was_centered);
1827114402Sru}
1828114402Sru
1829114402Sruvoid environment::start_line()
1830114402Sru{
1831114402Sru  assert(line == 0);
1832114402Sru  discarding = 0;
1833114402Sru  line = new line_start_node;
1834114402Sru  if (have_temporary_indent) {
1835114402Sru    saved_indent = temporary_indent;
1836114402Sru    have_temporary_indent = 0;
1837114402Sru  }
1838114402Sru  else
1839114402Sru    saved_indent = indent;
1840114402Sru  target_text_length = line_length - saved_indent;
1841114402Sru  width_total = H0;
1842114402Sru  space_total = 0;
1843114402Sru}
1844114402Sru
1845114402Sruhunits environment::get_hyphenation_space()
1846114402Sru{
1847114402Sru  return hyphenation_space;
1848114402Sru}
1849114402Sru
1850114402Sruvoid hyphenation_space_request()
1851114402Sru{
1852114402Sru  hunits n;
1853114402Sru  if (get_hunits(&n, 'm')) {
1854114402Sru    if (n < H0) {
1855114402Sru      warning(WARN_RANGE, "hyphenation space cannot be negative");
1856114402Sru      n = H0;
1857114402Sru    }
1858114402Sru    curenv->hyphenation_space = n;
1859114402Sru  }
1860114402Sru  skip_line();
1861114402Sru}
1862114402Sru
1863114402Sruhunits environment::get_hyphenation_margin()
1864114402Sru{
1865114402Sru  return hyphenation_margin;
1866114402Sru}
1867114402Sru
1868114402Sruvoid hyphenation_margin_request()
1869114402Sru{
1870114402Sru  hunits n;
1871114402Sru  if (get_hunits(&n, 'm')) {
1872114402Sru    if (n < H0) {
1873114402Sru      warning(WARN_RANGE, "hyphenation margin cannot be negative");
1874114402Sru      n = H0;
1875114402Sru    }
1876114402Sru    curenv->hyphenation_margin = n;
1877114402Sru  }
1878114402Sru  skip_line();
1879114402Sru}
1880114402Sru
1881114402Srubreakpoint *environment::choose_breakpoint()
1882114402Sru{
1883114402Sru  hunits x = width_total;
1884114402Sru  int s = space_total;
1885114402Sru  node *n = line;
1886114402Sru  breakpoint *best_bp = 0;	// the best breakpoint so far
1887114402Sru  int best_bp_fits = 0;
1888114402Sru  while (n != 0) {
1889114402Sru    x -= n->width();
1890114402Sru    s -= n->nspaces();
1891114402Sru    breakpoint *bp = n->get_breakpoints(x, s);
1892114402Sru    while (bp != 0) {
1893114402Sru      if (bp->width <= target_text_length) {
1894114402Sru	if (!bp->hyphenated) {
1895114402Sru	  breakpoint *tem = bp->next;
1896114402Sru	  bp->next = 0;
1897114402Sru	  while (tem != 0) {
1898114402Sru	    breakpoint *tem1 = tem;
1899114402Sru	    tem = tem->next;
1900114402Sru	    delete tem1;
1901114402Sru	  }
1902114402Sru	  if (best_bp_fits
1903114402Sru	      // Decide whether to use the hyphenated breakpoint.
1904114402Sru	      && (hyphen_line_max < 0
1905114402Sru		  // Only choose the hyphenated breakpoint if it would not
1906114402Sru		  // exceed the maximum number of consecutive hyphenated
1907114402Sru		  // lines.
1908114402Sru		  || hyphen_line_count + 1 <= hyphen_line_max)
1909114402Sru	      && !(adjust_mode == ADJUST_BOTH
1910114402Sru		   // Don't choose the hyphenated breakpoint if the line
1911114402Sru		   // can be justified by adding no more than
1912114402Sru		   // hyphenation_space to any word space.
1913114402Sru		   ? (bp->nspaces > 0
1914114402Sru		      && (((target_text_length - bp->width
1915114402Sru			    + (bp->nspaces - 1)*hresolution)/bp->nspaces)
1916114402Sru			  <= hyphenation_space))
1917114402Sru		   // Don't choose the hyphenated breakpoint if the line
1918114402Sru		   // is no more than hyphenation_margin short.
1919114402Sru		   : target_text_length - bp->width <= hyphenation_margin)) {
1920114402Sru	    delete bp;
1921114402Sru	    return best_bp;
1922114402Sru	  }
1923114402Sru	  if (best_bp)
1924114402Sru	    delete best_bp;
1925114402Sru	  return bp;
1926114402Sru	}
1927114402Sru	else {
1928114402Sru	  if ((adjust_mode == ADJUST_BOTH
1929114402Sru	       ? hyphenation_space == H0
1930114402Sru	       : hyphenation_margin == H0)
1931114402Sru	      && (hyphen_line_max < 0
1932114402Sru		  || hyphen_line_count + 1 <= hyphen_line_max)) {
1933114402Sru	    // No need to consider a non-hyphenated breakpoint.
1934114402Sru	    if (best_bp)
1935114402Sru	      delete best_bp;
1936114402Sru	    breakpoint *tem = bp->next;
1937114402Sru	    bp->next = 0;
1938114402Sru	    while (tem != 0) {
1939114402Sru	      breakpoint *tem1 = tem;
1940114402Sru	      tem = tem->next;
1941114402Sru	      delete tem1;
1942114402Sru	    }
1943114402Sru	    return bp;
1944114402Sru	  }
1945114402Sru	  // It fits but it's hyphenated.
1946114402Sru	  if (!best_bp_fits) {
1947114402Sru	    if (best_bp)
1948114402Sru	      delete best_bp;
1949114402Sru	    best_bp = bp;
1950114402Sru	    bp = bp->next;
1951114402Sru	    best_bp_fits = 1;
1952114402Sru	  }
1953114402Sru	  else {
1954114402Sru	    breakpoint *tem = bp;
1955114402Sru	    bp = bp->next;
1956114402Sru	    delete tem;
1957114402Sru	  }
1958114402Sru	}
1959114402Sru      }
1960114402Sru      else {
1961114402Sru	if (best_bp)
1962114402Sru	  delete best_bp;
1963114402Sru	best_bp = bp;
1964114402Sru	bp = bp->next;
1965114402Sru      }
1966114402Sru    }
1967114402Sru    n = n->next;
1968114402Sru  }
1969114402Sru  if (best_bp) {
1970114402Sru    if (!best_bp_fits)
1971114402Sru      output_warning(WARN_BREAK, "can't break line");
1972114402Sru    return best_bp;
1973114402Sru  }
1974114402Sru  return 0;
1975114402Sru}
1976114402Sru
1977114402Sruvoid environment::hyphenate_line(int start_here)
1978114402Sru{
1979114402Sru  assert(line != 0);
1980114402Sru  hyphenation_type prev_type = line->get_hyphenation_type();
1981114402Sru  node **startp;
1982114402Sru  if (start_here)
1983114402Sru    startp = &line;
1984114402Sru  else
1985114402Sru    for (startp = &line->next; *startp != 0; startp = &(*startp)->next) {
1986114402Sru      hyphenation_type this_type = (*startp)->get_hyphenation_type();
1987114402Sru      if (prev_type == HYPHEN_BOUNDARY && this_type == HYPHEN_MIDDLE)
1988114402Sru	break;
1989114402Sru      prev_type = this_type;
1990114402Sru    }
1991114402Sru  if (*startp == 0)
1992114402Sru    return;
1993114402Sru  node *tem = *startp;
1994114402Sru  do {
1995114402Sru    tem = tem->next;
1996114402Sru  } while (tem != 0 && tem->get_hyphenation_type() == HYPHEN_MIDDLE);
1997114402Sru  int inhibit = (tem != 0 && tem->get_hyphenation_type() == HYPHEN_INHIBIT);
1998114402Sru  node *end = tem;
1999114402Sru  hyphen_list *sl = 0;
2000114402Sru  tem = *startp;
2001114402Sru  node *forward = 0;
2002114402Sru  int i = 0;
2003114402Sru  while (tem != end) {
2004114402Sru    sl = tem->get_hyphen_list(sl, &i);
2005114402Sru    node *tem1 = tem;
2006114402Sru    tem = tem->next;
2007114402Sru    tem1->next = forward;
2008114402Sru    forward = tem1;
2009114402Sru  }
2010114402Sru  if (!inhibit) {
2011114402Sru    // this is for characters like hyphen and emdash
2012114402Sru    int prev_code = 0;
2013114402Sru    for (hyphen_list *h = sl; h; h = h->next) {
2014114402Sru      h->breakable = (prev_code != 0
2015114402Sru		      && h->next != 0
2016114402Sru		      && h->next->hyphenation_code != 0);
2017114402Sru      prev_code = h->hyphenation_code;
2018114402Sru    }
2019114402Sru  }
2020114402Sru  if (hyphenation_flags != 0
2021114402Sru      && !inhibit
2022114402Sru      // this may not be right if we have extra space on this line
2023114402Sru      && !((hyphenation_flags & HYPHEN_LAST_LINE)
2024114402Sru	   && (curdiv->distance_to_next_trap()
2025114402Sru	       <= vertical_spacing + total_post_vertical_spacing()))
2026114402Sru      && i >= 4)
2027114402Sru    hyphenate(sl, hyphenation_flags);
2028114402Sru  while (forward != 0) {
2029114402Sru    node *tem1 = forward;
2030114402Sru    forward = forward->next;
2031114402Sru    tem1->next = 0;
2032114402Sru    tem = tem1->add_self(tem, &sl);
2033114402Sru  }
2034114402Sru  *startp = tem;
2035114402Sru}
2036114402Sru
2037114402Srustatic node *node_list_reverse(node *n)
2038114402Sru{
2039114402Sru  node *res = 0;
2040114402Sru  while (n) {
2041114402Sru    node *tem = n;
2042114402Sru    n = n->next;
2043114402Sru    tem->next = res;
2044114402Sru    res = tem;
2045114402Sru  }
2046114402Sru  return res;
2047114402Sru}
2048114402Sru
2049114402Srustatic void distribute_space(node *n, int nspaces, hunits desired_space,
2050114402Sru			     int force_reverse = 0)
2051114402Sru{
2052114402Sru  static int reverse = 0;
2053114402Sru  if (force_reverse || reverse)
2054114402Sru    n = node_list_reverse(n);
2055114402Sru  if (!force_reverse && nspaces > 0 && spread_limit >= 0
2056114402Sru      && desired_space.to_units() > 0) {
2057114402Sru    hunits em = curenv->get_size();
2058114402Sru    double Ems = (double)desired_space.to_units() / nspaces
2059114402Sru		 / (em.is_zero() ? hresolution : em.to_units());
2060114402Sru    if (Ems > spread_limit)
2061114402Sru      output_warning(WARN_BREAK, "spreading %1m per space", Ems);
2062114402Sru  }
2063114402Sru  for (node *tem = n; tem; tem = tem->next)
2064114402Sru    tem->spread_space(&nspaces, &desired_space);
2065114402Sru  if (force_reverse || reverse)
2066114402Sru    (void)node_list_reverse(n);
2067114402Sru  if (!force_reverse)
2068114402Sru    reverse = !reverse;
2069114402Sru  assert(desired_space.is_zero() && nspaces == 0);
2070114402Sru}
2071114402Sru
2072114402Sruvoid environment::possibly_break_line(int start_here, int forced)
2073114402Sru{
2074151497Sru  int was_centered = center_lines > 0;
2075114402Sru  if (!fill || current_tab || current_field || dummy)
2076114402Sru    return;
2077114402Sru  while (line != 0
2078114402Sru	 && (forced
2079114402Sru	     // When a macro follows a paragraph in fill mode, the
2080114402Sru	     // current line should not be empty.
2081114402Sru	     || (width_total - line->width()) > target_text_length)) {
2082114402Sru    hyphenate_line(start_here);
2083114402Sru    breakpoint *bp = choose_breakpoint();
2084114402Sru    if (bp == 0)
2085114402Sru      // we'll find one eventually
2086114402Sru      return;
2087114402Sru    node *pre, *post;
2088114402Sru    node **ndp = &line;
2089114402Sru    while (*ndp != bp->nd)
2090114402Sru      ndp = &(*ndp)->next;
2091114402Sru    bp->nd->split(bp->index, &pre, &post);
2092114402Sru    *ndp = post;
2093114402Sru    hunits extra_space_width = H0;
2094114402Sru    switch(adjust_mode) {
2095114402Sru    case ADJUST_BOTH:
2096114402Sru      if (bp->nspaces != 0)
2097114402Sru	extra_space_width = target_text_length - bp->width;
2098114402Sru      else if (bp->width > 0 && target_text_length > 0
2099114402Sru	       && target_text_length > bp->width)
2100114402Sru	output_warning(WARN_BREAK, "cannot adjust line");
2101114402Sru      break;
2102114402Sru    case ADJUST_CENTER:
2103114402Sru      saved_indent += (target_text_length - bp->width)/2;
2104151497Sru      was_centered = 1;
2105114402Sru      break;
2106114402Sru    case ADJUST_RIGHT:
2107114402Sru      saved_indent += target_text_length - bp->width;
2108114402Sru      break;
2109114402Sru    }
2110114402Sru    distribute_space(pre, bp->nspaces, extra_space_width);
2111114402Sru    hunits output_width = bp->width + extra_space_width;
2112114402Sru    input_line_start -= output_width;
2113114402Sru    if (bp->hyphenated)
2114114402Sru      hyphen_line_count++;
2115114402Sru    else
2116114402Sru      hyphen_line_count = 0;
2117114402Sru    delete bp;
2118114402Sru    space_total = 0;
2119114402Sru    width_total = 0;
2120114402Sru    node *first_non_discardable = 0;
2121114402Sru    node *tem;
2122114402Sru    for (tem = line; tem != 0; tem = tem->next)
2123114402Sru      if (!tem->discardable())
2124114402Sru	first_non_discardable = tem;
2125114402Sru    node *to_be_discarded;
2126114402Sru    if (first_non_discardable) {
2127114402Sru      to_be_discarded = first_non_discardable->next;
2128114402Sru      first_non_discardable->next = 0;
2129114402Sru      for (tem = line; tem != 0; tem = tem->next) {
2130114402Sru	width_total += tem->width();
2131114402Sru	space_total += tem->nspaces();
2132114402Sru      }
2133114402Sru      discarding = 0;
2134114402Sru    }
2135114402Sru    else {
2136114402Sru      discarding = 1;
2137114402Sru      to_be_discarded = line;
2138114402Sru      line = 0;
2139114402Sru    }
2140114402Sru    // Do output_line() here so that line will be 0 iff the
2141114402Sru    // the environment will be empty.
2142151497Sru    output_line(pre, output_width, was_centered);
2143114402Sru    while (to_be_discarded != 0) {
2144114402Sru      tem = to_be_discarded;
2145114402Sru      to_be_discarded = to_be_discarded->next;
2146114402Sru      input_line_start -= tem->width();
2147114402Sru      delete tem;
2148114402Sru    }
2149114402Sru    if (line != 0) {
2150114402Sru      if (have_temporary_indent) {
2151114402Sru	saved_indent = temporary_indent;
2152114402Sru	have_temporary_indent = 0;
2153114402Sru      }
2154114402Sru      else
2155114402Sru	saved_indent = indent;
2156114402Sru      target_text_length = line_length - saved_indent;
2157114402Sru    }
2158114402Sru  }
2159114402Sru}
2160114402Sru
2161114402Sru/*
2162114402SruDo the break at the end of input after the end macro (if any).
2163114402Sru
2164114402SruUnix troff behaves as follows:  if the last line is
2165114402Sru
2166114402Srufoo bar\c
2167114402Sru
2168114402Sruit will output foo on the current page, and bar on the next page;
2169114402Sruif the last line is
2170114402Sru
2171114402Srufoo\c
2172114402Sru
2173114402Sruor
2174114402Sru
2175114402Srufoo bar
2176114402Sru
2177114402Srueverything will be output on the current page.  This behaviour must be
2178114402Sruconsidered a bug.
2179114402Sru
2180114402SruThe problem is that some macro packages rely on this.  For example,
2181114402Sruthe ATK macros have an end macro that emits \c if it needs to print a
2182114402Srutable of contents but doesn't do a 'bp in the end macro; instead the
2183114402Sru'bp is done in the bottom of page trap.  This works with Unix troff,
2184114402Sruprovided that the current environment is not empty at the end of the
2185114402Sruinput file.
2186114402Sru
2187114402SruThe following will make macro packages that do that sort of thing work
2188114402Srueven if the current environment is empty at the end of the input file.
2189114402SruIf the last input line used \c and this line occurred in the end macro,
2190114402Sruthen we'll force everything out on the current page, but we'll make
2191114402Srusure that the environment isn't empty so that we won't exit at the
2192114402Srubottom of this page.
2193114402Sru*/
2194114402Sru
2195114402Sruvoid environment::final_break()
2196114402Sru{
2197114402Sru  if (prev_line_interrupted == 2) {
2198114402Sru    do_break();
2199114402Sru    add_node(new transparent_dummy_node);
2200114402Sru  }
2201114402Sru  else
2202114402Sru    do_break();
2203114402Sru}
2204114402Sru
2205151497Srunode *environment::make_tag(const char *nm, int i)
2206114402Sru{
2207114402Sru  if (is_html) {
2208114402Sru    /*
2209114402Sru     * need to emit tag for post-grohtml
2210114402Sru     * but we check to see whether we can emit specials
2211114402Sru     */
2212114402Sru    if (curdiv == topdiv && topdiv->before_first_page)
2213114402Sru      topdiv->begin_page();
2214114402Sru    macro *m = new macro;
2215151497Sru    m->append_str("devtag:");
2216151497Sru    for (const char *p = nm; *p; p++)
2217114402Sru      if (!invalid_input_char((unsigned char)*p))
2218114402Sru	m->append(*p);
2219151497Sru    m->append(' ');
2220151497Sru    m->append_int(i);
2221151497Sru    return new special_node(*m);
2222114402Sru  }
2223151497Sru  return 0;
2224114402Sru}
2225114402Sru
2226151497Sruvoid environment::dump_troff_state()
2227114402Sru{
2228151497Sru#define SPACES "                                            "
2229151497Sru  fprintf(stderr, SPACES "register `in' = %d\n", curenv->indent.to_units());
2230151497Sru  if (curenv->have_temporary_indent)
2231151497Sru    fprintf(stderr, SPACES "register `ti' = %d\n",
2232151497Sru	    curenv->temporary_indent.to_units());
2233151497Sru  fprintf(stderr, SPACES "centered lines `ce' = %d\n", curenv->center_lines);
2234151497Sru  fprintf(stderr, SPACES "register `ll' = %d\n",
2235151497Sru	  curenv->line_length.to_units());
2236151497Sru  fprintf(stderr, SPACES "fill `fi=1/nf=0' = %d\n", curenv->fill);
2237151497Sru  fprintf(stderr, SPACES "page offset `po' = %d\n",
2238151497Sru	  topdiv->get_page_offset().to_units());
2239151497Sru  fprintf(stderr, SPACES "seen_break = %d\n", curenv->seen_break);
2240151497Sru  fprintf(stderr, SPACES "seen_space = %d\n", curenv->seen_space);
2241151497Sru  fflush(stderr);
2242151497Sru#undef SPACES
2243114402Sru}
2244114402Sru
2245151497Srustatem *environment::construct_state(int only_eol)
2246114402Sru{
2247114402Sru  if (is_html) {
2248151497Sru    statem *s = new statem();
2249151497Sru    if (!only_eol) {
2250151497Sru      s->add_tag(MTSM_IN, indent);
2251151497Sru      s->add_tag(MTSM_LL, line_length);
2252151497Sru      s->add_tag(MTSM_PO, topdiv->get_page_offset().to_units());
2253151497Sru      s->add_tag(MTSM_RJ, right_justify_lines);
2254151497Sru      if (have_temporary_indent)
2255151497Sru	s->add_tag(MTSM_TI, temporary_indent);
2256151497Sru      s->add_tag_ta();
2257151497Sru      if (seen_break)
2258151497Sru	s->add_tag(MTSM_BR);
2259151497Sru      if (seen_space != 0)
2260151497Sru	s->add_tag(MTSM_SP, seen_space);
2261151497Sru      seen_break = 0;
2262151497Sru      seen_space = 0;
2263151497Sru    }
2264151497Sru    if (seen_eol) {
2265151497Sru      s->add_tag(MTSM_EOL);
2266151497Sru      s->add_tag(MTSM_CE, center_lines);
2267151497Sru    }
2268151497Sru    seen_eol = 0;
2269151497Sru    return s;
2270114402Sru  }
2271151497Sru  else
2272151497Sru    return NULL;
2273114402Sru}
2274114402Sru
2275151497Sruvoid environment::construct_format_state(node *n, int was_centered,
2276151497Sru					 int filling)
2277114402Sru{
2278114402Sru  if (is_html) {
2279151497Sru    // find first glyph node which has a state.
2280151497Sru    while (n != 0 && n->state == 0)
2281151497Sru      n = n->next;
2282151497Sru    if (n == 0 || (n->state == 0))
2283151497Sru      return;
2284151497Sru    if (seen_space != 0)
2285151497Sru      n->state->add_tag(MTSM_SP, seen_space);
2286151497Sru    if (seen_eol && topdiv == curdiv)
2287151497Sru      n->state->add_tag(MTSM_EOL);
2288151497Sru    seen_space = 0;
2289151497Sru    seen_eol = 0;
2290151497Sru    if (was_centered)
2291151497Sru      n->state->add_tag(MTSM_CE, center_lines+1);
2292151497Sru    else
2293151497Sru      n->state->add_tag_if_unknown(MTSM_CE, 0);
2294151497Sru    n->state->add_tag_if_unknown(MTSM_FI, filling);
2295151497Sru    n = n->next;
2296151497Sru    while (n != 0) {
2297151497Sru      if (n->state != 0) {
2298151497Sru	n->state->sub_tag_ce();
2299151497Sru	n->state->add_tag_if_unknown(MTSM_FI, filling);
2300151497Sru      }
2301151497Sru      n = n->next;
2302151497Sru    }
2303114402Sru  }
2304114402Sru}
2305114402Sru
2306151497Sruvoid environment::construct_new_line_state(node *n)
2307114402Sru{
2308114402Sru  if (is_html) {
2309151497Sru    // find first glyph node which has a state.
2310151497Sru    while (n != 0 && n->state == 0)
2311151497Sru      n = n->next;
2312151497Sru    if (n == 0 || n->state == 0)
2313151497Sru      return;
2314151497Sru    if (seen_space != 0)
2315151497Sru      n->state->add_tag(MTSM_SP, seen_space);
2316151497Sru    if (seen_eol && topdiv == curdiv)
2317151497Sru      n->state->add_tag(MTSM_EOL);
2318151497Sru    seen_space = 0;
2319151497Sru    seen_eol = 0;
2320114402Sru  }
2321114402Sru}
2322114402Sru
2323151497Sruextern int global_diverted_space;
2324151497Sru
2325151497Sruvoid environment::do_break(int do_spread)
2326114402Sru{
2327151497Sru  int was_centered = 0;
2328114402Sru  if (curdiv == topdiv && topdiv->before_first_page) {
2329114402Sru    topdiv->begin_page();
2330114402Sru    return;
2331114402Sru  }
2332114402Sru  if (current_tab)
2333114402Sru    wrap_up_tab();
2334114402Sru  if (line) {
2335114402Sru    // this is so that hyphenation works
2336114402Sru    line = new space_node(H0, get_fill_color(), line);
2337114402Sru    space_total++;
2338151497Sru    possibly_break_line(0, do_spread);
2339114402Sru  }
2340114402Sru  while (line != 0 && line->discardable()) {
2341114402Sru    width_total -= line->width();
2342114402Sru    space_total -= line->nspaces();
2343114402Sru    node *tem = line;
2344114402Sru    line = line->next;
2345114402Sru    delete tem;
2346114402Sru  }
2347114402Sru  discarding = 0;
2348114402Sru  input_line_start = H0;
2349114402Sru  if (line != 0) {
2350114402Sru    if (fill) {
2351114402Sru      switch (adjust_mode) {
2352114402Sru      case ADJUST_CENTER:
2353114402Sru	saved_indent += (target_text_length - width_total)/2;
2354151497Sru	was_centered = 1;
2355114402Sru	break;
2356114402Sru      case ADJUST_RIGHT:
2357114402Sru	saved_indent += target_text_length - width_total;
2358114402Sru	break;
2359114402Sru      }
2360114402Sru    }
2361114402Sru    node *tem = line;
2362114402Sru    line = 0;
2363151497Sru    output_line(tem, width_total, was_centered);
2364114402Sru    hyphen_line_count = 0;
2365114402Sru  }
2366114402Sru  prev_line_interrupted = 0;
2367114402Sru#ifdef WIDOW_CONTROL
2368114402Sru  mark_last_line();
2369114402Sru  output_pending_lines();
2370114402Sru#endif /* WIDOW_CONTROL */
2371151497Sru  if (!global_diverted_space) {
2372151497Sru    curdiv->modified_tag.incl(MTSM_BR);
2373151497Sru    seen_break = 1;
2374151497Sru  }
2375114402Sru}
2376114402Sru
2377114402Sruint environment::is_empty()
2378114402Sru{
2379114402Sru  return !current_tab && line == 0 && pending_lines == 0;
2380114402Sru}
2381114402Sru
2382114402Sruvoid do_break_request(int spread)
2383114402Sru{
2384114402Sru  while (!tok.newline() && !tok.eof())
2385114402Sru    tok.next();
2386151497Sru  if (break_flag)
2387114402Sru    curenv->do_break(spread);
2388114402Sru  tok.next();
2389114402Sru}
2390114402Sru
2391114402Sruvoid break_request()
2392114402Sru{
2393114402Sru  do_break_request(0);
2394114402Sru}
2395114402Sru
2396114402Sruvoid break_spread_request()
2397114402Sru{
2398114402Sru  do_break_request(1);
2399114402Sru}
2400114402Sru
2401114402Sruvoid title()
2402114402Sru{
2403114402Sru  if (curdiv == topdiv && topdiv->before_first_page) {
2404114402Sru    handle_initial_title();
2405114402Sru    return;
2406114402Sru  }
2407114402Sru  node *part[3];
2408114402Sru  hunits part_width[3];
2409114402Sru  part[0] = part[1] = part[2] = 0;
2410114402Sru  environment env(curenv);
2411114402Sru  environment *oldenv = curenv;
2412114402Sru  curenv = &env;
2413114402Sru  read_title_parts(part, part_width);
2414114402Sru  curenv = oldenv;
2415114402Sru  curenv->size = env.size;
2416114402Sru  curenv->prev_size = env.prev_size;
2417114402Sru  curenv->requested_size = env.requested_size;
2418114402Sru  curenv->prev_requested_size = env.prev_requested_size;
2419114402Sru  curenv->char_height = env.char_height;
2420114402Sru  curenv->char_slant = env.char_slant;
2421114402Sru  curenv->fontno = env.fontno;
2422114402Sru  curenv->prev_fontno = env.prev_fontno;
2423114402Sru  curenv->glyph_color = env.glyph_color;
2424114402Sru  curenv->prev_glyph_color = env.prev_glyph_color;
2425114402Sru  curenv->fill_color = env.fill_color;
2426114402Sru  curenv->prev_fill_color = env.prev_fill_color;
2427114402Sru  node *n = 0;
2428114402Sru  node *p = part[2];
2429114402Sru  while (p != 0) {
2430114402Sru    node *tem = p;
2431114402Sru    p = p->next;
2432114402Sru    tem->next = n;
2433114402Sru    n = tem;
2434114402Sru  }
2435151497Sru  hunits length_title(curenv->title_length);
2436151497Sru  hunits f = length_title - part_width[1];
2437114402Sru  hunits f2 = f/2;
2438114402Sru  n = new hmotion_node(f2 - part_width[2], curenv->get_fill_color(), n);
2439114402Sru  p = part[1];
2440114402Sru  while (p != 0) {
2441114402Sru    node *tem = p;
2442114402Sru    p = p->next;
2443114402Sru    tem->next = n;
2444114402Sru    n = tem;
2445114402Sru  }
2446114402Sru  n = new hmotion_node(f - f2 - part_width[0], curenv->get_fill_color(), n);
2447114402Sru  p = part[0];
2448114402Sru  while (p != 0) {
2449114402Sru    node *tem = p;
2450114402Sru    p = p->next;
2451114402Sru    tem->next = n;
2452114402Sru    n = tem;
2453114402Sru  }
2454114402Sru  curenv->output_title(n, !curenv->fill, curenv->vertical_spacing,
2455151497Sru		       curenv->total_post_vertical_spacing(), length_title);
2456114402Sru  curenv->hyphen_line_count = 0;
2457114402Sru  tok.next();
2458114402Sru}
2459114402Sru
2460114402Sruvoid adjust()
2461114402Sru{
2462114402Sru  curenv->adjust_mode |= 1;
2463114402Sru  if (has_arg()) {
2464114402Sru    switch (tok.ch()) {
2465114402Sru    case 'l':
2466114402Sru      curenv->adjust_mode = ADJUST_LEFT;
2467114402Sru      break;
2468114402Sru    case 'r':
2469114402Sru      curenv->adjust_mode = ADJUST_RIGHT;
2470114402Sru      break;
2471114402Sru    case 'c':
2472114402Sru      curenv->adjust_mode = ADJUST_CENTER;
2473114402Sru      break;
2474114402Sru    case 'b':
2475114402Sru    case 'n':
2476114402Sru      curenv->adjust_mode = ADJUST_BOTH;
2477114402Sru      break;
2478114402Sru    default:
2479114402Sru      int n;
2480114402Sru      if (get_integer(&n)) {
2481114402Sru	if (n < 0)
2482114402Sru	  warning(WARN_RANGE, "negative adjustment mode");
2483114402Sru	else if (n > 5) {
2484114402Sru	  curenv->adjust_mode = 5;
2485114402Sru	  warning(WARN_RANGE, "adjustment mode `%1' out of range", n);
2486114402Sru	}
2487114402Sru	else
2488114402Sru	  curenv->adjust_mode = n;
2489114402Sru      }
2490114402Sru    }
2491114402Sru  }
2492114402Sru  skip_line();
2493114402Sru}
2494114402Sru
2495114402Sruvoid no_adjust()
2496114402Sru{
2497114402Sru  curenv->adjust_mode &= ~1;
2498114402Sru  skip_line();
2499114402Sru}
2500114402Sru
2501114402Sruvoid do_input_trap(int continued)
2502114402Sru{
2503114402Sru  curenv->input_trap_count = 0;
2504114402Sru  if (continued)
2505114402Sru    curenv->continued_input_trap = 1;
2506114402Sru  int n;
2507114402Sru  if (has_arg() && get_integer(&n)) {
2508114402Sru    if (n <= 0)
2509114402Sru      warning(WARN_RANGE,
2510114402Sru	      "number of lines for input trap must be greater than zero");
2511114402Sru    else {
2512114402Sru      symbol s = get_name(1);
2513114402Sru      if (!s.is_null()) {
2514114402Sru	curenv->input_trap_count = n;
2515114402Sru	curenv->input_trap = s;
2516114402Sru      }
2517114402Sru    }
2518114402Sru  }
2519114402Sru  skip_line();
2520114402Sru}
2521114402Sru
2522114402Sruvoid input_trap()
2523114402Sru{
2524114402Sru  do_input_trap(0);
2525114402Sru}
2526114402Sru
2527114402Sruvoid input_trap_continued()
2528114402Sru{
2529114402Sru  do_input_trap(1);
2530114402Sru}
2531114402Sru
2532114402Sru/* tabs */
2533114402Sru
2534114402Sru// must not be R or C or L or a legitimate part of a number expression
2535114402Sruconst char TAB_REPEAT_CHAR = 'T';
2536114402Sru
2537114402Srustruct tab {
2538114402Sru  tab *next;
2539114402Sru  hunits pos;
2540114402Sru  tab_type type;
2541114402Sru  tab(hunits, tab_type);
2542114402Sru  enum { BLOCK = 1024 };
2543114402Sru  static tab *free_list;
2544114402Sru  void *operator new(size_t);
2545114402Sru  void operator delete(void *);
2546114402Sru};
2547114402Sru
2548114402Srutab *tab::free_list = 0;
2549114402Sru
2550114402Sruvoid *tab::operator new(size_t n)
2551114402Sru{
2552114402Sru  assert(n == sizeof(tab));
2553114402Sru  if (!free_list) {
2554114402Sru    free_list = (tab *)new char[sizeof(tab)*BLOCK];
2555114402Sru    for (int i = 0; i < BLOCK - 1; i++)
2556114402Sru      free_list[i].next = free_list + i + 1;
2557114402Sru    free_list[BLOCK-1].next = 0;
2558114402Sru  }
2559114402Sru  tab *p = free_list;
2560114402Sru  free_list = (tab *)(free_list->next);
2561114402Sru  p->next = 0;
2562114402Sru  return p;
2563114402Sru}
2564114402Sru
2565114402Sru#ifdef __GNUG__
2566114402Sru/* cfront can't cope with this. */
2567114402Sruinline
2568114402Sru#endif
2569114402Sruvoid tab::operator delete(void *p)
2570114402Sru{
2571114402Sru  if (p) {
2572114402Sru    ((tab *)p)->next = free_list;
2573114402Sru    free_list = (tab *)p;
2574114402Sru  }
2575114402Sru}
2576114402Sru
2577114402Srutab::tab(hunits x, tab_type t) : next(0), pos(x), type(t)
2578114402Sru{
2579114402Sru}
2580114402Sru
2581114402Srutab_stops::tab_stops(hunits distance, tab_type type)
2582114402Sru: initial_list(0)
2583114402Sru{
2584114402Sru  repeated_list = new tab(distance, type);
2585114402Sru}
2586114402Sru
2587114402Srutab_stops::~tab_stops()
2588114402Sru{
2589114402Sru  clear();
2590114402Sru}
2591114402Sru
2592114402Srutab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance)
2593114402Sru{
2594114402Sru  hunits nextpos;
2595114402Sru
2596114402Sru  return distance_to_next_tab(curpos, distance, &nextpos);
2597114402Sru}
2598114402Sru
2599114402Srutab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance,
2600114402Sru					 hunits *nextpos)
2601114402Sru{
2602114402Sru  hunits lastpos = 0;
2603114402Sru  tab *tem;
2604114402Sru  for (tem = initial_list; tem && tem->pos <= curpos; tem = tem->next)
2605114402Sru    lastpos = tem->pos;
2606114402Sru  if (tem) {
2607114402Sru    *distance = tem->pos - curpos;
2608114402Sru    *nextpos  = tem->pos;
2609114402Sru    return tem->type;
2610114402Sru  }
2611114402Sru  if (repeated_list == 0)
2612114402Sru    return TAB_NONE;
2613114402Sru  hunits base = lastpos;
2614114402Sru  for (;;) {
2615114402Sru    for (tem = repeated_list; tem && tem->pos + base <= curpos; tem = tem->next)
2616114402Sru      lastpos = tem->pos;
2617114402Sru    if (tem) {
2618114402Sru      *distance = tem->pos + base - curpos;
2619114402Sru      *nextpos  = tem->pos + base;
2620114402Sru      return tem->type;
2621114402Sru    }
2622114402Sru    assert(lastpos > 0);
2623114402Sru    base += lastpos;
2624114402Sru  }
2625114402Sru  return TAB_NONE;
2626114402Sru}
2627114402Sru
2628114402Sruconst char *tab_stops::to_string()
2629114402Sru{
2630114402Sru  static char *buf = 0;
2631114402Sru  static int buf_size = 0;
2632114402Sru  // figure out a maximum on the amount of space we can need
2633114402Sru  int count = 0;
2634114402Sru  tab *p;
2635114402Sru  for (p = initial_list; p; p = p->next)
2636114402Sru    ++count;
2637114402Sru  for (p = repeated_list; p; p = p->next)
2638114402Sru    ++count;
2639114402Sru  // (10 for digits + 1 for u + 1 for 'C' or 'R') + 2 for ' &' + 1 for '\0'
2640114402Sru  int need = count*12 + 3;
2641114402Sru  if (buf == 0 || need > buf_size) {
2642114402Sru    if (buf)
2643114402Sru      a_delete buf;
2644114402Sru    buf_size = need;
2645114402Sru    buf = new char[buf_size];
2646114402Sru  }
2647114402Sru  char *ptr = buf;
2648114402Sru  for (p = initial_list; p; p = p->next) {
2649114402Sru    strcpy(ptr, i_to_a(p->pos.to_units()));
2650114402Sru    ptr = strchr(ptr, '\0');
2651114402Sru    *ptr++ = 'u';
2652114402Sru    *ptr = '\0';
2653114402Sru    switch (p->type) {
2654114402Sru    case TAB_LEFT:
2655114402Sru      break;
2656114402Sru    case TAB_RIGHT:
2657114402Sru      *ptr++ = 'R';
2658114402Sru      break;
2659114402Sru    case TAB_CENTER:
2660114402Sru      *ptr++ = 'C';
2661114402Sru      break;
2662114402Sru    case TAB_NONE:
2663114402Sru    default:
2664114402Sru      assert(0);
2665114402Sru    }
2666114402Sru  }
2667114402Sru  if (repeated_list)
2668114402Sru    *ptr++ = TAB_REPEAT_CHAR;
2669114402Sru  for (p = repeated_list; p; p = p->next) {
2670114402Sru    strcpy(ptr, i_to_a(p->pos.to_units()));
2671114402Sru    ptr = strchr(ptr, '\0');
2672114402Sru    *ptr++ = 'u';
2673114402Sru    *ptr = '\0';
2674114402Sru    switch (p->type) {
2675114402Sru    case TAB_LEFT:
2676114402Sru      break;
2677114402Sru    case TAB_RIGHT:
2678114402Sru      *ptr++ = 'R';
2679114402Sru      break;
2680114402Sru    case TAB_CENTER:
2681114402Sru      *ptr++ = 'C';
2682114402Sru      break;
2683114402Sru    case TAB_NONE:
2684114402Sru    default:
2685114402Sru      assert(0);
2686114402Sru    }
2687114402Sru  }
2688114402Sru  *ptr++ = '\0';
2689114402Sru  return buf;
2690114402Sru}
2691114402Sru
2692114402Srutab_stops::tab_stops() : initial_list(0), repeated_list(0)
2693114402Sru{
2694114402Sru}
2695114402Sru
2696114402Srutab_stops::tab_stops(const tab_stops &ts)
2697114402Sru: initial_list(0), repeated_list(0)
2698114402Sru{
2699114402Sru  tab **p = &initial_list;
2700114402Sru  tab *t = ts.initial_list;
2701114402Sru  while (t) {
2702114402Sru    *p = new tab(t->pos, t->type);
2703114402Sru    t = t->next;
2704114402Sru    p = &(*p)->next;
2705114402Sru  }
2706114402Sru  p = &repeated_list;
2707114402Sru  t = ts.repeated_list;
2708114402Sru  while (t) {
2709114402Sru    *p = new tab(t->pos, t->type);
2710114402Sru    t = t->next;
2711114402Sru    p = &(*p)->next;
2712114402Sru  }
2713114402Sru}
2714114402Sru
2715114402Sruvoid tab_stops::clear()
2716114402Sru{
2717114402Sru  while (initial_list) {
2718114402Sru    tab *tem = initial_list;
2719114402Sru    initial_list = initial_list->next;
2720114402Sru    delete tem;
2721114402Sru  }
2722114402Sru  while (repeated_list) {
2723114402Sru    tab *tem = repeated_list;
2724114402Sru    repeated_list = repeated_list->next;
2725114402Sru    delete tem;
2726114402Sru  }
2727114402Sru}
2728114402Sru
2729114402Sruvoid tab_stops::add_tab(hunits pos, tab_type type, int repeated)
2730114402Sru{
2731114402Sru  tab **p;
2732114402Sru  for (p = repeated ? &repeated_list : &initial_list; *p; p = &(*p)->next)
2733114402Sru    ;
2734114402Sru  *p = new tab(pos, type);
2735114402Sru}
2736114402Sru
2737114402Sru
2738114402Sruvoid tab_stops::operator=(const tab_stops &ts)
2739114402Sru{
2740114402Sru  clear();
2741114402Sru  tab **p = &initial_list;
2742114402Sru  tab *t = ts.initial_list;
2743114402Sru  while (t) {
2744114402Sru    *p = new tab(t->pos, t->type);
2745114402Sru    t = t->next;
2746114402Sru    p = &(*p)->next;
2747114402Sru  }
2748114402Sru  p = &repeated_list;
2749114402Sru  t = ts.repeated_list;
2750114402Sru  while (t) {
2751114402Sru    *p = new tab(t->pos, t->type);
2752114402Sru    t = t->next;
2753114402Sru    p = &(*p)->next;
2754114402Sru  }
2755114402Sru}
2756114402Sru
2757114402Sruvoid set_tabs()
2758114402Sru{
2759114402Sru  hunits pos;
2760114402Sru  hunits prev_pos = 0;
2761114402Sru  int first = 1;
2762114402Sru  int repeated = 0;
2763114402Sru  tab_stops tabs;
2764114402Sru  while (has_arg()) {
2765114402Sru    if (tok.ch() == TAB_REPEAT_CHAR) {
2766114402Sru      tok.next();
2767114402Sru      repeated = 1;
2768114402Sru      prev_pos = 0;
2769114402Sru    }
2770114402Sru    if (!get_hunits(&pos, 'm', prev_pos))
2771114402Sru      break;
2772114402Sru    tab_type type = TAB_LEFT;
2773114402Sru    if (tok.ch() == 'C') {
2774114402Sru      tok.next();
2775114402Sru      type = TAB_CENTER;
2776114402Sru    }
2777114402Sru    else if (tok.ch() == 'R') {
2778114402Sru      tok.next();
2779114402Sru      type = TAB_RIGHT;
2780114402Sru    }
2781114402Sru    else if (tok.ch() == 'L') {
2782114402Sru      tok.next();
2783114402Sru    }
2784114402Sru    if (pos <= prev_pos && !first)
2785114402Sru      warning(WARN_RANGE,
2786114402Sru	      "positions of tab stops must be strictly increasing");
2787114402Sru    else {
2788114402Sru      tabs.add_tab(pos, type, repeated);
2789114402Sru      prev_pos = pos;
2790114402Sru      first = 0;
2791114402Sru    }
2792114402Sru  }
2793114402Sru  curenv->tabs = tabs;
2794151497Sru  curdiv->modified_tag.incl(MTSM_TA);
2795114402Sru  skip_line();
2796114402Sru}
2797114402Sru
2798114402Sruconst char *environment::get_tabs()
2799114402Sru{
2800114402Sru  return tabs.to_string();
2801114402Sru}
2802114402Sru
2803114402Srutab_type environment::distance_to_next_tab(hunits *distance)
2804114402Sru{
2805114402Sru  return line_tabs
2806114402Sru    ? curenv->tabs.distance_to_next_tab(get_text_length(), distance)
2807114402Sru    : curenv->tabs.distance_to_next_tab(get_input_line_position(), distance);
2808114402Sru}
2809114402Sru
2810114402Srutab_type environment::distance_to_next_tab(hunits *distance, hunits *leftpos)
2811114402Sru{
2812114402Sru  return line_tabs
2813114402Sru    ? curenv->tabs.distance_to_next_tab(get_text_length(), distance, leftpos)
2814114402Sru    : curenv->tabs.distance_to_next_tab(get_input_line_position(), distance,
2815114402Sru					leftpos);
2816114402Sru}
2817114402Sru
2818114402Sruvoid field_characters()
2819114402Sru{
2820114402Sru  field_delimiter_char = get_optional_char();
2821114402Sru  if (field_delimiter_char)
2822114402Sru    padding_indicator_char = get_optional_char();
2823114402Sru  else
2824114402Sru    padding_indicator_char = 0;
2825114402Sru  skip_line();
2826114402Sru}
2827114402Sru
2828114402Sruvoid line_tabs_request()
2829114402Sru{
2830114402Sru  int n;
2831114402Sru  if (has_arg() && get_integer(&n))
2832114402Sru    curenv->line_tabs = n != 0;
2833114402Sru  else
2834114402Sru    curenv->line_tabs = 1;
2835114402Sru  skip_line();
2836114402Sru}
2837114402Sru
2838114402Sruint environment::get_line_tabs()
2839114402Sru{
2840114402Sru  return line_tabs;
2841114402Sru}
2842114402Sru
2843114402Sruvoid environment::wrap_up_tab()
2844114402Sru{
2845114402Sru  if (!current_tab)
2846114402Sru    return;
2847114402Sru  if (line == 0)
2848114402Sru    start_line();
2849114402Sru  hunits tab_amount;
2850114402Sru  switch (current_tab) {
2851114402Sru  case TAB_RIGHT:
2852114402Sru    tab_amount = tab_distance - tab_width;
2853114402Sru    line = make_tab_node(tab_amount, line);
2854114402Sru    break;
2855114402Sru  case TAB_CENTER:
2856114402Sru    tab_amount = tab_distance - tab_width/2;
2857114402Sru    line = make_tab_node(tab_amount, line);
2858114402Sru    break;
2859114402Sru  case TAB_NONE:
2860114402Sru  case TAB_LEFT:
2861114402Sru  default:
2862114402Sru    assert(0);
2863114402Sru  }
2864114402Sru  width_total += tab_amount;
2865114402Sru  width_total += tab_width;
2866114402Sru  if (current_field) {
2867114402Sru    if (tab_precedes_field) {
2868114402Sru      pre_field_width += tab_amount;
2869114402Sru      tab_precedes_field = 0;
2870114402Sru    }
2871114402Sru    field_distance -= tab_amount;
2872114402Sru    field_spaces += tab_field_spaces;
2873114402Sru  }
2874114402Sru  if (tab_contents != 0) {
2875114402Sru    node *tem;
2876114402Sru    for (tem = tab_contents; tem->next != 0; tem = tem->next)
2877114402Sru      ;
2878114402Sru    tem->next = line;
2879114402Sru    line = tab_contents;
2880114402Sru  }
2881114402Sru  tab_field_spaces = 0;
2882114402Sru  tab_contents = 0;
2883114402Sru  tab_width = H0;
2884114402Sru  tab_distance = H0;
2885114402Sru  current_tab = TAB_NONE;
2886114402Sru}
2887114402Sru
2888114402Srunode *environment::make_tab_node(hunits d, node *next)
2889114402Sru{
2890114402Sru  if (leader_node != 0 && d < 0) {
2891114402Sru    error("motion generated by leader cannot be negative");
2892114402Sru    delete leader_node;
2893114402Sru    leader_node = 0;
2894114402Sru  }
2895114402Sru  if (!leader_node)
2896114402Sru    return new hmotion_node(d, 1, 0, get_fill_color(), next);
2897114402Sru  node *n = new hline_node(d, leader_node, next);
2898114402Sru  leader_node = 0;
2899114402Sru  return n;
2900114402Sru}
2901114402Sru
2902114402Sruvoid environment::handle_tab(int is_leader)
2903114402Sru{
2904114402Sru  hunits d;
2905151497Sru  hunits absolute;
2906114402Sru  if (current_tab)
2907114402Sru    wrap_up_tab();
2908114402Sru  charinfo *ci = is_leader ? leader_char : tab_char;
2909114402Sru  delete leader_node;
2910114402Sru  leader_node = ci ? make_char_node(ci) : 0;
2911151497Sru  tab_type t = distance_to_next_tab(&d, &absolute);
2912114402Sru  switch (t) {
2913114402Sru  case TAB_NONE:
2914114402Sru    return;
2915114402Sru  case TAB_LEFT:
2916151497Sru    add_node(make_tag("tab L", absolute.to_units()));
2917114402Sru    add_node(make_tab_node(d));
2918114402Sru    return;
2919114402Sru  case TAB_RIGHT:
2920151497Sru    add_node(make_tag("tab R", absolute.to_units()));
2921114402Sru    break;
2922114402Sru  case TAB_CENTER:
2923151497Sru    add_node(make_tag("tab C", absolute.to_units()));
2924114402Sru    break;
2925114402Sru  default:
2926114402Sru    assert(0);
2927114402Sru  }
2928114402Sru  tab_width = 0;
2929114402Sru  tab_distance = d;
2930114402Sru  tab_contents = 0;
2931114402Sru  current_tab = t;
2932114402Sru  tab_field_spaces = 0;
2933114402Sru}
2934114402Sru
2935114402Sruvoid environment::start_field()
2936114402Sru{
2937114402Sru  assert(!current_field);
2938114402Sru  hunits d;
2939114402Sru  if (distance_to_next_tab(&d) != TAB_NONE) {
2940114402Sru    pre_field_width = get_text_length();
2941114402Sru    field_distance = d;
2942114402Sru    current_field = 1;
2943114402Sru    field_spaces = 0;
2944114402Sru    tab_field_spaces = 0;
2945114402Sru    for (node *p = line; p; p = p->next)
2946114402Sru      if (p->nspaces()) {
2947114402Sru	p->freeze_space();
2948114402Sru	space_total--;
2949114402Sru      }
2950114402Sru    tab_precedes_field = current_tab != TAB_NONE;
2951114402Sru  }
2952114402Sru  else
2953114402Sru    error("zero field width");
2954114402Sru}
2955114402Sru
2956114402Sruvoid environment::wrap_up_field()
2957114402Sru{
2958114402Sru  if (!current_tab && field_spaces == 0)
2959114402Sru    add_padding();
2960114402Sru  hunits padding = field_distance - (get_text_length() - pre_field_width);
2961114402Sru  if (current_tab && tab_field_spaces != 0) {
2962114402Sru    hunits tab_padding = scale(padding,
2963114402Sru			       tab_field_spaces,
2964114402Sru			       field_spaces + tab_field_spaces);
2965114402Sru    padding -= tab_padding;
2966114402Sru    distribute_space(tab_contents, tab_field_spaces, tab_padding, 1);
2967114402Sru    tab_field_spaces = 0;
2968114402Sru    tab_width += tab_padding;
2969114402Sru  }
2970114402Sru  if (field_spaces != 0) {
2971114402Sru    distribute_space(line, field_spaces, padding, 1);
2972114402Sru    width_total += padding;
2973114402Sru    if (current_tab) {
2974114402Sru      // the start of the tab has been moved to the right by padding, so
2975114402Sru      tab_distance -= padding;
2976114402Sru      if (tab_distance <= H0) {
2977114402Sru	// use the next tab stop instead
2978114402Sru	current_tab = tabs.distance_to_next_tab(get_input_line_position()
2979114402Sru						- tab_width,
2980114402Sru						&tab_distance);
2981114402Sru	if (current_tab == TAB_NONE || current_tab == TAB_LEFT) {
2982114402Sru	  width_total += tab_width;
2983114402Sru	  if (current_tab == TAB_LEFT) {
2984114402Sru	    line = make_tab_node(tab_distance, line);
2985114402Sru	    width_total += tab_distance;
2986114402Sru	    current_tab = TAB_NONE;
2987114402Sru	  }
2988114402Sru	  if (tab_contents != 0) {
2989114402Sru	    node *tem;
2990114402Sru	    for (tem = tab_contents; tem->next != 0; tem = tem->next)
2991114402Sru	      ;
2992114402Sru	    tem->next = line;
2993114402Sru	    line = tab_contents;
2994114402Sru	    tab_contents = 0;
2995114402Sru	  }
2996114402Sru	  tab_width = H0;
2997114402Sru	  tab_distance = H0;
2998114402Sru	}
2999114402Sru      }
3000114402Sru    }
3001114402Sru  }
3002114402Sru  current_field = 0;
3003114402Sru}
3004114402Sru
3005114402Sruvoid environment::add_padding()
3006114402Sru{
3007114402Sru  if (current_tab) {
3008114402Sru    tab_contents = new space_node(H0, get_fill_color(), tab_contents);
3009114402Sru    tab_field_spaces++;
3010114402Sru  }
3011114402Sru  else {
3012114402Sru    if (line == 0)
3013114402Sru      start_line();
3014114402Sru    line = new space_node(H0, get_fill_color(), line);
3015114402Sru    field_spaces++;
3016114402Sru  }
3017114402Sru}
3018114402Sru
3019114402Srutypedef int (environment::*INT_FUNCP)();
3020114402Srutypedef vunits (environment::*VUNITS_FUNCP)();
3021114402Srutypedef hunits (environment::*HUNITS_FUNCP)();
3022114402Srutypedef const char *(environment::*STRING_FUNCP)();
3023114402Sru
3024114402Sruclass int_env_reg : public reg {
3025114402Sru  INT_FUNCP func;
3026114402Sru public:
3027114402Sru  int_env_reg(INT_FUNCP);
3028114402Sru  const char *get_string();
3029114402Sru  int get_value(units *val);
3030114402Sru};
3031114402Sru
3032114402Sruclass vunits_env_reg : public reg {
3033114402Sru  VUNITS_FUNCP func;
3034114402Sru public:
3035114402Sru  vunits_env_reg(VUNITS_FUNCP f);
3036114402Sru  const char *get_string();
3037114402Sru  int get_value(units *val);
3038114402Sru};
3039114402Sru
3040114402Sru
3041114402Sruclass hunits_env_reg : public reg {
3042114402Sru  HUNITS_FUNCP func;
3043114402Sru public:
3044114402Sru  hunits_env_reg(HUNITS_FUNCP f);
3045114402Sru  const char *get_string();
3046114402Sru  int get_value(units *val);
3047114402Sru};
3048114402Sru
3049114402Sruclass string_env_reg : public reg {
3050114402Sru  STRING_FUNCP func;
3051114402Srupublic:
3052114402Sru  string_env_reg(STRING_FUNCP);
3053114402Sru  const char *get_string();
3054114402Sru};
3055114402Sru
3056114402Sruint_env_reg::int_env_reg(INT_FUNCP f) : func(f)
3057114402Sru{
3058114402Sru}
3059114402Sru
3060114402Sruint int_env_reg::get_value(units *val)
3061114402Sru{
3062114402Sru  *val = (curenv->*func)();
3063114402Sru  return 1;
3064114402Sru}
3065114402Sru
3066114402Sruconst char *int_env_reg::get_string()
3067114402Sru{
3068114402Sru  return i_to_a((curenv->*func)());
3069114402Sru}
3070114402Sru
3071114402Sruvunits_env_reg::vunits_env_reg(VUNITS_FUNCP f) : func(f)
3072114402Sru{
3073114402Sru}
3074114402Sru
3075114402Sruint vunits_env_reg::get_value(units *val)
3076114402Sru{
3077114402Sru  *val = (curenv->*func)().to_units();
3078114402Sru  return 1;
3079114402Sru}
3080114402Sru
3081114402Sruconst char *vunits_env_reg::get_string()
3082114402Sru{
3083114402Sru  return i_to_a((curenv->*func)().to_units());
3084114402Sru}
3085114402Sru
3086114402Sruhunits_env_reg::hunits_env_reg(HUNITS_FUNCP f) : func(f)
3087114402Sru{
3088114402Sru}
3089114402Sru
3090114402Sruint hunits_env_reg::get_value(units *val)
3091114402Sru{
3092114402Sru  *val = (curenv->*func)().to_units();
3093114402Sru  return 1;
3094114402Sru}
3095114402Sru
3096114402Sruconst char *hunits_env_reg::get_string()
3097114402Sru{
3098114402Sru  return i_to_a((curenv->*func)().to_units());
3099114402Sru}
3100114402Sru
3101114402Srustring_env_reg::string_env_reg(STRING_FUNCP f) : func(f)
3102114402Sru{
3103114402Sru}
3104114402Sru
3105114402Sruconst char *string_env_reg::get_string()
3106114402Sru{
3107114402Sru  return (curenv->*func)();
3108114402Sru}
3109114402Sru
3110114402Sruclass horizontal_place_reg : public general_reg {
3111114402Srupublic:
3112114402Sru  horizontal_place_reg();
3113114402Sru  int get_value(units *);
3114114402Sru  void set_value(units);
3115114402Sru};
3116114402Sru
3117114402Sruhorizontal_place_reg::horizontal_place_reg()
3118114402Sru{
3119114402Sru}
3120114402Sru
3121114402Sruint horizontal_place_reg::get_value(units *res)
3122114402Sru{
3123114402Sru  *res = curenv->get_input_line_position().to_units();
3124114402Sru  return 1;
3125114402Sru}
3126114402Sru
3127114402Sruvoid horizontal_place_reg::set_value(units n)
3128114402Sru{
3129114402Sru  curenv->set_input_line_position(hunits(n));
3130114402Sru}
3131114402Sru
3132114402Sruconst char *environment::get_font_family_string()
3133114402Sru{
3134114402Sru  return family->nm.contents();
3135114402Sru}
3136114402Sru
3137151497Sruconst char *environment::get_glyph_color_string()
3138151497Sru{
3139151497Sru  return glyph_color->nm.contents();
3140151497Sru}
3141151497Sru
3142151497Sruconst char *environment::get_fill_color_string()
3143151497Sru{
3144151497Sru  return fill_color->nm.contents();
3145151497Sru}
3146151497Sru
3147114402Sruconst char *environment::get_font_name_string()
3148114402Sru{
3149114402Sru  symbol f = get_font_name(fontno, this);
3150114402Sru  return f.contents();
3151114402Sru}
3152114402Sru
3153151497Sruconst char *environment::get_style_name_string()
3154151497Sru{
3155151497Sru  symbol f = get_style_name(fontno);
3156151497Sru  return f.contents();
3157151497Sru}
3158151497Sru
3159114402Sruconst char *environment::get_name_string()
3160114402Sru{
3161114402Sru  return name.contents();
3162114402Sru}
3163114402Sru
3164114402Sru// Convert a quantity in scaled points to ascii decimal fraction.
3165114402Sru
3166114402Sruconst char *sptoa(int sp)
3167114402Sru{
3168114402Sru  assert(sp > 0);
3169114402Sru  assert(sizescale > 0);
3170114402Sru  if (sizescale == 1)
3171114402Sru    return i_to_a(sp);
3172114402Sru  if (sp % sizescale == 0)
3173114402Sru    return i_to_a(sp/sizescale);
3174114402Sru  // See if 1/sizescale is exactly representable as a decimal fraction,
3175114402Sru  // ie its only prime factors are 2 and 5.
3176114402Sru  int n = sizescale;
3177114402Sru  int power2 = 0;
3178114402Sru  while ((n & 1) == 0) {
3179114402Sru    n >>= 1;
3180114402Sru    power2++;
3181114402Sru  }
3182114402Sru  int power5 = 0;
3183114402Sru  while ((n % 5) == 0) {
3184114402Sru    n /= 5;
3185114402Sru    power5++;
3186114402Sru  }
3187114402Sru  if (n == 1) {
3188114402Sru    int decimal_point = power5 > power2 ? power5 : power2;
3189114402Sru    if (decimal_point <= 10) {
3190114402Sru      int factor = 1;
3191114402Sru      int t;
3192114402Sru      for (t = decimal_point - power2; --t >= 0;)
3193114402Sru	factor *= 2;
3194114402Sru      for (t = decimal_point - power5; --t >= 0;)
3195114402Sru	factor *= 5;
3196114402Sru      if (factor == 1 || sp <= INT_MAX/factor)
3197114402Sru	return if_to_a(sp*factor, decimal_point);
3198114402Sru    }
3199114402Sru  }
3200114402Sru  double s = double(sp)/double(sizescale);
3201114402Sru  double factor = 10.0;
3202114402Sru  double val = s;
3203114402Sru  int decimal_point = 0;
3204114402Sru  do  {
3205114402Sru    double v = ceil(s*factor);
3206114402Sru    if (v > INT_MAX)
3207114402Sru      break;
3208114402Sru    val = v;
3209114402Sru    factor *= 10.0;
3210114402Sru  } while (++decimal_point < 10);
3211114402Sru  return if_to_a(int(val), decimal_point);
3212114402Sru}
3213114402Sru
3214114402Sruconst char *environment::get_point_size_string()
3215114402Sru{
3216114402Sru  return sptoa(curenv->get_point_size());
3217114402Sru}
3218114402Sru
3219114402Sruconst char *environment::get_requested_point_size_string()
3220114402Sru{
3221114402Sru  return sptoa(curenv->get_requested_point_size());
3222114402Sru}
3223114402Sru
3224114402Sru#define init_int_env_reg(name, func) \
3225114402Sru  number_reg_dictionary.define(name, new int_env_reg(&environment::func))
3226114402Sru
3227114402Sru#define init_vunits_env_reg(name, func) \
3228114402Sru  number_reg_dictionary.define(name, new vunits_env_reg(&environment::func))
3229114402Sru
3230114402Sru#define init_hunits_env_reg(name, func) \
3231114402Sru  number_reg_dictionary.define(name, new hunits_env_reg(&environment::func))
3232114402Sru
3233114402Sru#define init_string_env_reg(name, func) \
3234114402Sru  number_reg_dictionary.define(name, new string_env_reg(&environment::func))
3235114402Sru
3236114402Sruvoid init_env_requests()
3237114402Sru{
3238114402Sru  init_request("ad", adjust);
3239114402Sru  init_request("br", break_request);
3240114402Sru  init_request("brp", break_spread_request);
3241114402Sru  init_request("c2", no_break_control_char);
3242114402Sru  init_request("cc", control_char);
3243114402Sru  init_request("ce", center);
3244114402Sru  init_request("cu", continuous_underline);
3245114402Sru  init_request("ev", environment_switch);
3246114402Sru  init_request("evc", environment_copy);
3247114402Sru  init_request("fam", family_change);
3248114402Sru  init_request("fc", field_characters);
3249114402Sru  init_request("fi", fill);
3250151497Sru  init_request("fcolor", fill_color_change);
3251114402Sru  init_request("ft", font_change);
3252151497Sru  init_request("gcolor", glyph_color_change);
3253114402Sru  init_request("hc", hyphen_char);
3254114402Sru  init_request("hlm", hyphen_line_max_request);
3255114402Sru  init_request("hy", hyphenate_request);
3256114402Sru  init_request("hym", hyphenation_margin_request);
3257114402Sru  init_request("hys", hyphenation_space_request);
3258114402Sru  init_request("in", indent);
3259114402Sru  init_request("it", input_trap);
3260114402Sru  init_request("itc", input_trap_continued);
3261114402Sru  init_request("lc", leader_character);
3262114402Sru  init_request("linetabs", line_tabs_request);
3263114402Sru  init_request("ll", line_length);
3264114402Sru  init_request("ls", line_spacing);
3265114402Sru  init_request("lt", title_length);
3266114402Sru  init_request("mc", margin_character);
3267114402Sru  init_request("na", no_adjust);
3268114402Sru  init_request("nf", no_fill);
3269114402Sru  init_request("nh", no_hyphenate);
3270114402Sru  init_request("nm", number_lines);
3271114402Sru  init_request("nn", no_number);
3272114402Sru  init_request("ps", point_size);
3273114402Sru  init_request("pvs", post_vertical_spacing);
3274114402Sru  init_request("rj", right_justify);
3275114402Sru  init_request("sizes", override_sizes);
3276114402Sru  init_request("ss", space_size);
3277114402Sru  init_request("ta", set_tabs);
3278114402Sru  init_request("ti", temporary_indent);
3279114402Sru  init_request("tc", tab_character);
3280114402Sru  init_request("tl", title);
3281114402Sru  init_request("ul", underline);
3282114402Sru  init_request("vs", vertical_spacing);
3283114402Sru#ifdef WIDOW_CONTROL
3284114402Sru  init_request("wdc", widow_control_request);
3285114402Sru#endif /* WIDOW_CONTROL */
3286114402Sru  init_int_env_reg(".b", get_bold);
3287114402Sru  init_vunits_env_reg(".cdp", get_prev_char_depth);
3288114402Sru  init_int_env_reg(".ce", get_center_lines);
3289114402Sru  init_vunits_env_reg(".cht", get_prev_char_height);
3290114402Sru  init_hunits_env_reg(".csk", get_prev_char_skew);
3291114402Sru  init_string_env_reg(".ev", get_name_string);
3292114402Sru  init_int_env_reg(".f", get_font);
3293114402Sru  init_string_env_reg(".fam", get_font_family_string);
3294114402Sru  init_string_env_reg(".fn", get_font_name_string);
3295114402Sru  init_int_env_reg(".height", get_char_height);
3296114402Sru  init_int_env_reg(".hlc", get_hyphen_line_count);
3297114402Sru  init_int_env_reg(".hlm", get_hyphen_line_max);
3298114402Sru  init_int_env_reg(".hy", get_hyphenation_flags);
3299114402Sru  init_hunits_env_reg(".hym", get_hyphenation_margin);
3300114402Sru  init_hunits_env_reg(".hys", get_hyphenation_space);
3301114402Sru  init_hunits_env_reg(".i", get_indent);
3302114402Sru  init_hunits_env_reg(".in", get_saved_indent);
3303114402Sru  init_int_env_reg(".int", get_prev_line_interrupted);
3304114402Sru  init_int_env_reg(".linetabs", get_line_tabs);
3305114402Sru  init_hunits_env_reg(".lt", get_title_length);
3306114402Sru  init_int_env_reg(".j", get_adjust_mode);
3307114402Sru  init_hunits_env_reg(".k", get_text_length);
3308114402Sru  init_int_env_reg(".L", get_line_spacing);
3309114402Sru  init_hunits_env_reg(".l", get_line_length);
3310114402Sru  init_hunits_env_reg(".ll", get_saved_line_length);
3311151497Sru  init_string_env_reg(".M", get_fill_color_string);
3312151497Sru  init_string_env_reg(".m", get_glyph_color_string);
3313114402Sru  init_hunits_env_reg(".n", get_prev_text_length);
3314114402Sru  init_int_env_reg(".ps", get_point_size);
3315114402Sru  init_int_env_reg(".psr", get_requested_point_size);
3316114402Sru  init_vunits_env_reg(".pvs", get_post_vertical_spacing);
3317114402Sru  init_int_env_reg(".rj", get_right_justify_lines);
3318114402Sru  init_string_env_reg(".s", get_point_size_string);
3319114402Sru  init_int_env_reg(".slant", get_char_slant);
3320114402Sru  init_int_env_reg(".ss", get_space_size);
3321114402Sru  init_int_env_reg(".sss", get_sentence_space_size);
3322114402Sru  init_string_env_reg(".sr", get_requested_point_size_string);
3323151497Sru  init_string_env_reg(".sty", get_style_name_string);
3324114402Sru  init_string_env_reg(".tabs", get_tabs);
3325114402Sru  init_int_env_reg(".u", get_fill);
3326114402Sru  init_vunits_env_reg(".v", get_vertical_spacing);
3327114402Sru  init_hunits_env_reg(".w", get_prev_char_width);
3328114402Sru  number_reg_dictionary.define("ct", new variable_reg(&ct_reg_contents));
3329114402Sru  number_reg_dictionary.define("hp", new horizontal_place_reg);
3330114402Sru  number_reg_dictionary.define("ln", new variable_reg(&next_line_number));
3331114402Sru  number_reg_dictionary.define("rsb", new variable_reg(&rsb_reg_contents));
3332114402Sru  number_reg_dictionary.define("rst", new variable_reg(&rst_reg_contents));
3333114402Sru  number_reg_dictionary.define("sb", new variable_reg(&sb_reg_contents));
3334114402Sru  number_reg_dictionary.define("skw", new variable_reg(&skw_reg_contents));
3335114402Sru  number_reg_dictionary.define("ssc", new variable_reg(&ssc_reg_contents));
3336114402Sru  number_reg_dictionary.define("st", new variable_reg(&st_reg_contents));
3337114402Sru}
3338114402Sru
3339114402Sru// Hyphenation - TeX's hyphenation algorithm with a less fancy implementation.
3340114402Sru
3341114402Srustruct trie_node;
3342114402Sru
3343114402Sruclass trie {
3344114402Sru  trie_node *tp;
3345114402Sru  virtual void do_match(int len, void *val) = 0;
3346114402Sru  virtual void do_delete(void *) = 0;
3347114402Sru  void delete_trie_node(trie_node *);
3348114402Srupublic:
3349114402Sru  trie() : tp(0) {}
3350114402Sru  virtual ~trie();		// virtual to shut up g++
3351114402Sru  void insert(const char *, int, void *);
3352114402Sru  // find calls do_match for each match it finds
3353114402Sru  void find(const char *pat, int patlen);
3354114402Sru  void clear();
3355114402Sru};
3356114402Sru
3357114402Sruclass hyphen_trie : private trie {
3358114402Sru  int *h;
3359114402Sru  void do_match(int i, void *v);
3360114402Sru  void do_delete(void *v);
3361114402Sru  void insert_pattern(const char *pat, int patlen, int *num);
3362114402Sru  void insert_hyphenation(dictionary *ex, const char *pat, int patlen);
3363114402Sru  int hpf_getc(FILE *f);
3364114402Srupublic:
3365114402Sru  hyphen_trie() {}
3366114402Sru  ~hyphen_trie() {}
3367114402Sru  void hyphenate(const char *word, int len, int *hyphens);
3368114402Sru  void read_patterns_file(const char *name, int append, dictionary *ex);
3369114402Sru};
3370114402Sru
3371114402Srustruct hyphenation_language {
3372114402Sru  symbol name;
3373114402Sru  dictionary exceptions;
3374114402Sru  hyphen_trie patterns;
3375114402Sru  hyphenation_language(symbol nm) : name(nm), exceptions(501) {}
3376114402Sru  ~hyphenation_language() { }
3377114402Sru};
3378114402Sru
3379114402Srudictionary language_dictionary(5);
3380114402Sruhyphenation_language *current_language = 0;
3381114402Sru
3382114402Srustatic void set_hyphenation_language()
3383114402Sru{
3384114402Sru  symbol nm = get_name(1);
3385114402Sru  if (!nm.is_null()) {
3386114402Sru    current_language = (hyphenation_language *)language_dictionary.lookup(nm);
3387114402Sru    if (!current_language) {
3388114402Sru      current_language = new hyphenation_language(nm);
3389114402Sru      (void)language_dictionary.lookup(nm, (void *)current_language);
3390114402Sru    }
3391114402Sru  }
3392114402Sru  skip_line();
3393114402Sru}
3394114402Sru
3395114402Sruconst int WORD_MAX = 256;	// we use unsigned char for offsets in
3396114402Sru				// hyphenation exceptions
3397114402Sru
3398114402Srustatic void hyphen_word()
3399114402Sru{
3400114402Sru  if (!current_language) {
3401114402Sru    error("no current hyphenation language");
3402114402Sru    skip_line();
3403114402Sru    return;
3404114402Sru  }
3405114402Sru  char buf[WORD_MAX + 1];
3406114402Sru  unsigned char pos[WORD_MAX + 2];
3407114402Sru  for (;;) {
3408114402Sru    tok.skip();
3409114402Sru    if (tok.newline() || tok.eof())
3410114402Sru      break;
3411114402Sru    int i = 0;
3412114402Sru    int npos = 0;
3413114402Sru    while (i < WORD_MAX && !tok.space() && !tok.newline() && !tok.eof()) {
3414114402Sru      charinfo *ci = tok.get_char(1);
3415114402Sru      if (ci == 0) {
3416114402Sru	skip_line();
3417114402Sru	return;
3418114402Sru      }
3419114402Sru      tok.next();
3420114402Sru      if (ci->get_ascii_code() == '-') {
3421114402Sru	if (i > 0 && (npos == 0 || pos[npos - 1] != i))
3422114402Sru	  pos[npos++] = i;
3423114402Sru      }
3424114402Sru      else {
3425151497Sru	unsigned char c = ci->get_hyphenation_code();
3426114402Sru	if (c == 0)
3427114402Sru	  break;
3428114402Sru	buf[i++] = c;
3429114402Sru      }
3430114402Sru    }
3431114402Sru    if (i > 0) {
3432114402Sru      pos[npos] = 0;
3433114402Sru      buf[i] = 0;
3434114402Sru      unsigned char *tem = new unsigned char[npos + 1];
3435114402Sru      memcpy(tem, pos, npos + 1);
3436114402Sru      tem = (unsigned char *)current_language->exceptions.lookup(symbol(buf),
3437114402Sru								 tem);
3438114402Sru      if (tem)
3439114402Sru	a_delete tem;
3440114402Sru    }
3441114402Sru  }
3442114402Sru  skip_line();
3443114402Sru}
3444114402Sru
3445114402Srustruct trie_node {
3446114402Sru  char c;
3447114402Sru  trie_node *down;
3448114402Sru  trie_node *right;
3449114402Sru  void *val;
3450114402Sru  trie_node(char, trie_node *);
3451114402Sru};
3452114402Sru
3453114402Srutrie_node::trie_node(char ch, trie_node *p)
3454114402Sru: c(ch), down(0), right(p), val(0)
3455114402Sru{
3456114402Sru}
3457114402Sru
3458114402Srutrie::~trie()
3459114402Sru{
3460114402Sru  clear();
3461114402Sru}
3462114402Sru
3463114402Sruvoid trie::clear()
3464114402Sru{
3465114402Sru  delete_trie_node(tp);
3466114402Sru  tp = 0;
3467114402Sru}
3468114402Sru
3469114402Sru
3470114402Sruvoid trie::delete_trie_node(trie_node *p)
3471114402Sru{
3472114402Sru  if (p) {
3473114402Sru    delete_trie_node(p->down);
3474114402Sru    delete_trie_node(p->right);
3475114402Sru    if (p->val)
3476114402Sru      do_delete(p->val);
3477114402Sru    delete p;
3478114402Sru  }
3479114402Sru}
3480114402Sru
3481114402Sruvoid trie::insert(const char *pat, int patlen, void *val)
3482114402Sru{
3483114402Sru  trie_node **p = &tp;
3484114402Sru  assert(patlen > 0 && pat != 0);
3485114402Sru  for (;;) {
3486114402Sru    while (*p != 0 && (*p)->c < pat[0])
3487114402Sru      p = &((*p)->right);
3488114402Sru    if (*p == 0 || (*p)->c != pat[0])
3489114402Sru      *p = new trie_node(pat[0], *p);
3490114402Sru    if (--patlen == 0) {
3491114402Sru      (*p)->val = val;
3492114402Sru      break;
3493114402Sru    }
3494114402Sru    ++pat;
3495114402Sru    p = &((*p)->down);
3496114402Sru  }
3497114402Sru}
3498114402Sru
3499114402Sruvoid trie::find(const char *pat, int patlen)
3500114402Sru{
3501114402Sru  trie_node *p = tp;
3502114402Sru  for (int i = 0; p != 0 && i < patlen; i++) {
3503114402Sru    while (p != 0 && p->c < pat[i])
3504114402Sru      p = p->right;
3505114402Sru    if (p != 0 && p->c == pat[i]) {
3506114402Sru      if (p->val != 0)
3507114402Sru	do_match(i+1, p->val);
3508114402Sru      p = p->down;
3509114402Sru    }
3510114402Sru    else
3511114402Sru      break;
3512114402Sru  }
3513114402Sru}
3514114402Sru
3515114402Srustruct operation {
3516114402Sru  operation *next;
3517114402Sru  short distance;
3518114402Sru  short num;
3519114402Sru  operation(int, int, operation *);
3520114402Sru};
3521114402Sru
3522114402Sruoperation::operation(int i, int j, operation *op)
3523114402Sru: next(op), distance(j), num(i)
3524114402Sru{
3525114402Sru}
3526114402Sru
3527114402Sruvoid hyphen_trie::insert_pattern(const char *pat, int patlen, int *num)
3528114402Sru{
3529114402Sru  operation *op = 0;
3530114402Sru  for (int i = 0; i < patlen+1; i++)
3531114402Sru    if (num[i] != 0)
3532114402Sru      op = new operation(num[i], patlen - i, op);
3533114402Sru  insert(pat, patlen, op);
3534114402Sru}
3535114402Sru
3536114402Sruvoid hyphen_trie::insert_hyphenation(dictionary *ex, const char *pat,
3537114402Sru				     int patlen)
3538114402Sru{
3539114402Sru  char buf[WORD_MAX + 1];
3540114402Sru  unsigned char pos[WORD_MAX + 2];
3541114402Sru  int i = 0, j = 0;
3542114402Sru  int npos = 0;
3543114402Sru  while (j < patlen) {
3544114402Sru    unsigned char c = pat[j++];
3545114402Sru    if (c == '-') {
3546114402Sru      if (i > 0 && (npos == 0 || pos[npos - 1] != i))
3547114402Sru	pos[npos++] = i;
3548114402Sru    }
3549114402Sru    else
3550114402Sru      buf[i++] = hpf_code_table[c];
3551114402Sru  }
3552114402Sru  if (i > 0) {
3553114402Sru    pos[npos] = 0;
3554114402Sru    buf[i] = 0;
3555114402Sru    unsigned char *tem = new unsigned char[npos + 1];
3556114402Sru    memcpy(tem, pos, npos + 1);
3557114402Sru    tem = (unsigned char *)ex->lookup(symbol(buf), tem);
3558114402Sru    if (tem)
3559114402Sru      a_delete tem;
3560114402Sru  }
3561114402Sru}
3562114402Sru
3563114402Sruvoid hyphen_trie::hyphenate(const char *word, int len, int *hyphens)
3564114402Sru{
3565114402Sru  int j;
3566114402Sru  for (j = 0; j < len + 1; j++)
3567114402Sru    hyphens[j] = 0;
3568114402Sru  for (j = 0; j < len - 1; j++) {
3569114402Sru    h = hyphens + j;
3570114402Sru    find(word + j, len - j);
3571114402Sru  }
3572114402Sru}
3573114402Sru
3574114402Sruinline int max(int m, int n)
3575114402Sru{
3576114402Sru  return m > n ? m : n;
3577114402Sru}
3578114402Sru
3579114402Sruvoid hyphen_trie::do_match(int i, void *v)
3580114402Sru{
3581114402Sru  operation *op = (operation *)v;
3582114402Sru  while (op != 0) {
3583114402Sru    h[i - op->distance] = max(h[i - op->distance], op->num);
3584114402Sru    op = op->next;
3585114402Sru  }
3586114402Sru}
3587114402Sru
3588114402Sruvoid hyphen_trie::do_delete(void *v)
3589114402Sru{
3590114402Sru  operation *op = (operation *)v;
3591114402Sru  while (op) {
3592114402Sru    operation *tem = op;
3593114402Sru    op = tem->next;
3594114402Sru    delete tem;
3595114402Sru  }
3596114402Sru}
3597114402Sru
3598114402Sru/* We use very simple rules to parse TeX's hyphenation patterns.
3599114402Sru
3600114402Sru   . `%' starts a comment even if preceded by `\'.
3601114402Sru
3602114402Sru   . No support for digraphs and like `\$'.
3603114402Sru
3604114402Sru   . `^^xx' (`x' is 0-9 or a-f), and `^^x' (character code of `x' in the
3605114402Sru     range 0-127) are recognized; other use of `^' causes an error.
3606114402Sru
3607114402Sru   . No macro expansion.
3608114402Sru
3609114402Sru   . We check for the expression `\patterns{...}' (possibly with
3610114402Sru     whitespace before and after the braces).  Everything between the
3611114402Sru     braces is taken as hyphenation patterns.  Consequently, `{' and `}'
3612114402Sru     are not allowed in patterns.
3613114402Sru
3614114402Sru   . Similarly, `\hyphenation{...}' gives a list of hyphenation
3615114402Sru     exceptions.
3616114402Sru
3617114402Sru   . `\endinput' is recognized also.
3618114402Sru
3619114402Sru   . For backwards compatibility, if `\patterns' is missing, the
3620114402Sru     whole file is treated as a list of hyphenation patterns (only
3621114402Sru     recognizing `%' as the start of a comment.
3622114402Sru
3623114402Sru*/
3624114402Sru
3625114402Sruint hyphen_trie::hpf_getc(FILE *f)
3626114402Sru{
3627114402Sru  int c = getc(f);
3628114402Sru  int c1;
3629114402Sru  int cc = 0;
3630114402Sru  if (c != '^')
3631114402Sru    return c;
3632114402Sru  c = getc(f);
3633114402Sru  if (c != '^')
3634114402Sru    goto fail;
3635114402Sru  c = getc(f);
3636114402Sru  c1 = getc(f);
3637114402Sru  if (((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))
3638114402Sru      && ((c1 >= '0' && c1 <= '9') || (c1 >= 'a' && c1 <= 'f'))) {
3639114402Sru    if (c >= '0' && c <= '9')
3640114402Sru      c -= '0';
3641114402Sru    else
3642114402Sru      c = c - 'a' + 10;
3643114402Sru    if (c1 >= '0' && c1 <= '9')
3644114402Sru      c1 -= '0';
3645114402Sru    else
3646114402Sru      c1 = c1 - 'a' + 10;
3647114402Sru    cc = c * 16 + c1;
3648114402Sru  }
3649114402Sru  else {
3650114402Sru    ungetc(c1, f);
3651114402Sru    if (c >= 0 && c <= 63)
3652114402Sru      cc = c + 64;
3653114402Sru    else if (c >= 64 && c <= 127)
3654114402Sru      cc = c - 64;
3655114402Sru    else
3656114402Sru      goto fail;
3657114402Sru  }
3658114402Sru  return cc;
3659114402Srufail:
3660114402Sru  error("invalid ^, ^^x, or ^^xx character in hyphenation patterns file");
3661114402Sru  return c;
3662114402Sru}
3663114402Sru
3664114402Sruvoid hyphen_trie::read_patterns_file(const char *name, int append,
3665114402Sru				     dictionary *ex)
3666114402Sru{
3667114402Sru  if (!append)
3668114402Sru    clear();
3669114402Sru  char buf[WORD_MAX];
3670114402Sru  for (int i = 0; i < WORD_MAX; i++)
3671114402Sru    buf[i] = 0;
3672114402Sru  int num[WORD_MAX+1];
3673114402Sru  errno = 0;
3674114402Sru  char *path = 0;
3675114402Sru  FILE *fp = mac_path->open_file(name, &path);
3676114402Sru  if (fp == 0) {
3677114402Sru    error("can't find hyphenation patterns file `%1'", name);
3678114402Sru    return;
3679114402Sru  }
3680114402Sru  int c = hpf_getc(fp);
3681114402Sru  int have_patterns = 0;	// we've seen \patterns
3682114402Sru  int final_pattern = 0;	// 1 if we have a trailing closing brace
3683114402Sru  int have_hyphenation = 0;	// we've seen \hyphenation
3684114402Sru  int final_hyphenation = 0;	// 1 if we have a trailing closing brace
3685114402Sru  int have_keyword = 0;		// we've seen either \patterns or \hyphenation
3686114402Sru  int traditional = 0;		// don't handle \patterns
3687114402Sru  for (;;) {
3688114402Sru    for (;;) {
3689114402Sru      if (c == '%') {		// skip comments
3690114402Sru	do {
3691114402Sru	  c = getc(fp);
3692114402Sru	} while (c != EOF && c != '\n');
3693114402Sru      }
3694114402Sru      if (c == EOF || !csspace(c))
3695114402Sru	break;
3696114402Sru      c = hpf_getc(fp);
3697114402Sru    }
3698114402Sru    if (c == EOF) {
3699114402Sru      if (have_keyword || traditional)	// we are done
3700114402Sru	break;
3701114402Sru      else {				// rescan file in `traditional' mode
3702114402Sru	rewind(fp);
3703114402Sru	traditional = 1;
3704114402Sru	c = hpf_getc(fp);
3705114402Sru	continue;
3706114402Sru      }
3707114402Sru    }
3708114402Sru    int i = 0;
3709114402Sru    num[0] = 0;
3710114402Sru    if (!(c == '{' || c == '}')) {	// skip braces at line start
3711114402Sru      do {				// scan patterns
3712114402Sru	if (csdigit(c))
3713114402Sru	  num[i] = c - '0';
3714114402Sru	else {
3715114402Sru	  buf[i++] = c;
3716114402Sru	  num[i] = 0;
3717114402Sru	}
3718114402Sru	c = hpf_getc(fp);
3719114402Sru      } while (i < WORD_MAX && c != EOF && !csspace(c)
3720114402Sru	       && c != '%' && c != '{' && c != '}');
3721114402Sru    }
3722114402Sru    if (!traditional) {
3723114402Sru      if (i >= 9 && !strncmp(buf + i - 9, "\\patterns", 9)) {
3724114402Sru	while (csspace(c))
3725114402Sru	  c = hpf_getc(fp);
3726114402Sru	if (c == '{') {
3727114402Sru	  if (have_patterns || have_hyphenation)
3728151497Sru	    error("\\patterns not allowed inside of %1 group",
3729151497Sru		  have_patterns ? "\\patterns" : "\\hyphenation");
3730114402Sru	  else {
3731114402Sru	    have_patterns = 1;
3732114402Sru	    have_keyword = 1;
3733114402Sru	  }
3734114402Sru	  c = hpf_getc(fp);
3735114402Sru	  continue;
3736114402Sru	}
3737114402Sru      }
3738114402Sru      else if (i >= 12 && !strncmp(buf + i - 12, "\\hyphenation", 12)) {
3739114402Sru	while (csspace(c))
3740114402Sru	  c = hpf_getc(fp);
3741114402Sru	if (c == '{') {
3742114402Sru	  if (have_patterns || have_hyphenation)
3743151497Sru	    error("\\hyphenation not allowed inside of %1 group",
3744151497Sru		  have_patterns ? "\\patterns" : "\\hyphenation");
3745114402Sru	  else {
3746114402Sru	    have_hyphenation = 1;
3747114402Sru	    have_keyword = 1;
3748114402Sru	  }
3749114402Sru	  c = hpf_getc(fp);
3750114402Sru	  continue;
3751114402Sru	}
3752114402Sru      }
3753114402Sru      else if (strstr(buf, "\\endinput")) {
3754114402Sru	if (have_patterns || have_hyphenation)
3755114402Sru	  error("found \\endinput inside of %1 group",
3756114402Sru		have_patterns ? "\\patterns" : "\\hyphenation");
3757114402Sru	break;
3758114402Sru      }
3759114402Sru      else if (c == '}') {
3760114402Sru	if (have_patterns) {
3761114402Sru	  have_patterns = 0;
3762114402Sru	  if (i > 0)
3763114402Sru	    final_pattern = 1;
3764114402Sru	}
3765114402Sru	else if (have_hyphenation) {
3766114402Sru	  have_hyphenation = 0;
3767114402Sru	  if (i > 0)
3768114402Sru	    final_hyphenation = 1;
3769114402Sru	}
3770114402Sru	c = hpf_getc(fp);
3771114402Sru      }
3772151497Sru      else if (c == '{') {
3773151497Sru	if (have_patterns || have_hyphenation)
3774151497Sru	  error("`{' not allowed within %1 group",
3775151497Sru		have_patterns ? "\\patterns" : "\\hyphenation");
3776151497Sru	c = hpf_getc(fp);		// skipped if not starting \patterns
3777151497Sru					// or \hyphenation
3778151497Sru      }
3779114402Sru    }
3780151497Sru    else {
3781151497Sru      if (c == '{' || c == '}')
3782151497Sru	c = hpf_getc(fp);
3783151497Sru    }
3784114402Sru    if (i > 0) {
3785114402Sru      if (have_patterns || final_pattern || traditional) {
3786114402Sru	for (int j = 0; j < i; j++)
3787114402Sru	  buf[j] = hpf_code_table[(unsigned char)buf[j]];
3788114402Sru	insert_pattern(buf, i, num);
3789114402Sru	final_pattern = 0;
3790114402Sru      }
3791114402Sru      else if (have_hyphenation || final_hyphenation) {
3792114402Sru	insert_hyphenation(ex, buf, i);
3793114402Sru	final_hyphenation = 0;
3794114402Sru      }
3795114402Sru    }
3796114402Sru  }
3797114402Sru  fclose(fp);
3798114402Sru  a_delete path;
3799114402Sru  return;
3800114402Sru}
3801114402Sru
3802114402Sruvoid hyphenate(hyphen_list *h, unsigned flags)
3803114402Sru{
3804114402Sru  if (!current_language)
3805114402Sru    return;
3806114402Sru  while (h) {
3807114402Sru    while (h && h->hyphenation_code == 0)
3808114402Sru      h = h->next;
3809114402Sru    int len = 0;
3810114402Sru    char hbuf[WORD_MAX+2];
3811114402Sru    char *buf = hbuf + 1;
3812114402Sru    hyphen_list *tem;
3813114402Sru    for (tem = h; tem && len < WORD_MAX; tem = tem->next) {
3814114402Sru      if (tem->hyphenation_code != 0)
3815114402Sru	buf[len++] = tem->hyphenation_code;
3816114402Sru      else
3817114402Sru	break;
3818114402Sru    }
3819114402Sru    hyphen_list *nexth = tem;
3820114402Sru    if (len > 2) {
3821114402Sru      buf[len] = 0;
3822114402Sru      unsigned char *pos
3823114402Sru	= (unsigned char *)current_language->exceptions.lookup(buf);
3824114402Sru      if (pos != 0) {
3825114402Sru	int j = 0;
3826114402Sru	int i = 1;
3827114402Sru	for (tem = h; tem != 0; tem = tem->next, i++)
3828114402Sru	  if (pos[j] == i) {
3829114402Sru	    tem->hyphen = 1;
3830114402Sru	    j++;
3831114402Sru	  }
3832114402Sru      }
3833114402Sru      else {
3834114402Sru	hbuf[0] = hbuf[len+1] = '.';
3835114402Sru	int num[WORD_MAX+3];
3836114402Sru	current_language->patterns.hyphenate(hbuf, len+2, num);
3837114402Sru	int i;
3838114402Sru	num[2] = 0;
3839114402Sru	if (flags & 8)
3840114402Sru	  num[3] = 0;
3841114402Sru	if (flags & 4)
3842114402Sru	  --len;
3843114402Sru	for (i = 2, tem = h; i < len && tem; tem = tem->next, i++)
3844114402Sru	  if (num[i] & 1)
3845114402Sru	    tem->hyphen = 1;
3846114402Sru      }
3847114402Sru    }
3848114402Sru    h = nexth;
3849114402Sru  }
3850114402Sru}
3851114402Sru
3852114402Srustatic void do_hyphenation_patterns_file(int append)
3853114402Sru{
3854114402Sru  symbol name = get_long_name(1);
3855114402Sru  if (!name.is_null()) {
3856114402Sru    if (!current_language)
3857114402Sru      error("no current hyphenation language");
3858114402Sru    else
3859114402Sru      current_language->patterns.read_patterns_file(
3860114402Sru			  name.contents(), append,
3861114402Sru			  &current_language->exceptions);
3862114402Sru  }
3863114402Sru  skip_line();
3864114402Sru}
3865114402Sru
3866114402Srustatic void hyphenation_patterns_file()
3867114402Sru{
3868114402Sru  do_hyphenation_patterns_file(0);
3869114402Sru}
3870114402Sru
3871114402Srustatic void hyphenation_patterns_file_append()
3872114402Sru{
3873114402Sru  do_hyphenation_patterns_file(1);
3874114402Sru}
3875114402Sru
3876114402Sruclass hyphenation_language_reg : public reg {
3877114402Srupublic:
3878114402Sru  const char *get_string();
3879114402Sru};
3880114402Sru
3881114402Sruconst char *hyphenation_language_reg::get_string()
3882114402Sru{
3883114402Sru  return current_language ? current_language->name.contents() : "";
3884114402Sru}
3885114402Sru
3886114402Sruvoid init_hyphen_requests()
3887114402Sru{
3888114402Sru  init_request("hw", hyphen_word);
3889114402Sru  init_request("hla", set_hyphenation_language);
3890114402Sru  init_request("hpf", hyphenation_patterns_file);
3891114402Sru  init_request("hpfa", hyphenation_patterns_file_append);
3892114402Sru  number_reg_dictionary.define(".hla", new hyphenation_language_reg);
3893114402Sru}
3894