1 /*	$NetBSD: env.cpp,v 1.1.1.1 2016/01/13 18:41:48 christos Exp $	*/
2 
3 // -*- C++ -*-
4 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005
5    Free Software Foundation, Inc.
6      Written by James Clark (jjc@jclark.com)
7 
8 This file is part of groff.
9 
10 groff is free software; you can redistribute it and/or modify it under
11 the terms of the GNU General Public License as published by the Free
12 Software Foundation; either version 2, or (at your option) any later
13 version.
14 
15 groff is distributed in the hope that it will be useful, but WITHOUT ANY
16 WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
18 for more details.
19 
20 You should have received a copy of the GNU General Public License along
21 with groff; see the file COPYING.  If not, write to the Free Software
22 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
23 
24 #include "troff.h"
25 #include "dictionary.h"
26 #include "hvunits.h"
27 #include "stringclass.h"
28 #include "mtsm.h"
29 #include "env.h"
30 #include "request.h"
31 #include "node.h"
32 #include "token.h"
33 #include "div.h"
34 #include "reg.h"
35 #include "charinfo.h"
36 #include "macropath.h"
37 #include "input.h"
38 #include <math.h>
39 
40 symbol default_family("T");
41 
42 enum { ADJUST_LEFT = 0, ADJUST_BOTH = 1, ADJUST_CENTER = 3, ADJUST_RIGHT = 5 };
43 
44 enum { HYPHEN_LAST_LINE = 2, HYPHEN_LAST_CHARS = 4, HYPHEN_FIRST_CHARS = 8 };
45 
46 struct env_list {
47   environment *env;
48   env_list *next;
env_listenv_list49   env_list(environment *e, env_list *p) : env(e), next(p) {}
50 };
51 
52 env_list *env_stack;
53 const int NENVIRONMENTS = 10;
54 environment *env_table[NENVIRONMENTS];
55 dictionary env_dictionary(10);
56 environment *curenv;
57 static int next_line_number = 0;
58 extern int suppress_push;
59 extern statem *get_diversion_state();
60 
61 charinfo *field_delimiter_char;
62 charinfo *padding_indicator_char;
63 
64 int translate_space_to_dummy = 0;
65 
66 class pending_output_line {
67   node *nd;
68   int no_fill;
69   int was_centered;
70   vunits vs;
71   vunits post_vs;
72   hunits width;
73 #ifdef WIDOW_CONTROL
74   int last_line;		// Is it the last line of the paragraph?
75 #endif /* WIDOW_CONTROL */
76 public:
77   pending_output_line *next;
78 
79   pending_output_line(node *, int, vunits, vunits, hunits, int,
80 		      pending_output_line * = 0);
81   ~pending_output_line();
82   int output();
83 
84 #ifdef WIDOW_CONTROL
85   friend void environment::mark_last_line();
86   friend void environment::output(node *, int, vunits, vunits, hunits, int);
87 #endif /* WIDOW_CONTROL */
88 };
89 
pending_output_line(node * n,int nf,vunits v,vunits pv,hunits w,int ce,pending_output_line * p)90 pending_output_line::pending_output_line(node *n, int nf, vunits v, vunits pv,
91 					 hunits w, int ce,
92 					 pending_output_line *p)
93 : nd(n), no_fill(nf), was_centered(ce), vs(v), post_vs(pv), width(w),
94 #ifdef WIDOW_CONTROL
95   last_line(0),
96 #endif /* WIDOW_CONTROL */
97   next(p)
98 {
99 }
100 
~pending_output_line()101 pending_output_line::~pending_output_line()
102 {
103   delete_node_list(nd);
104 }
105 
output()106 int pending_output_line::output()
107 {
108   if (trap_sprung_flag)
109     return 0;
110 #ifdef WIDOW_CONTROL
111   if (next && next->last_line && !no_fill) {
112     curdiv->need(vs + post_vs + vunits(vresolution));
113     if (trap_sprung_flag) {
114       next->last_line = 0;	// Try to avoid infinite loops.
115       return 0;
116     }
117   }
118 #endif
119   curenv->construct_format_state(nd, was_centered, !no_fill);
120   curdiv->output(nd, no_fill, vs, post_vs, width);
121   nd = 0;
122   return 1;
123 }
124 
output(node * nd,int no_fill_flag,vunits vs,vunits post_vs,hunits width,int was_centered)125 void environment::output(node *nd, int no_fill_flag,
126 			 vunits vs, vunits post_vs,
127 			 hunits width, int was_centered)
128 {
129 #ifdef WIDOW_CONTROL
130   while (pending_lines) {
131     if (widow_control && !pending_lines->no_fill && !pending_lines->next)
132       break;
133     if (!pending_lines->output())
134       break;
135     pending_output_line *tem = pending_lines;
136     pending_lines = pending_lines->next;
137     delete tem;
138   }
139 #else /* WIDOW_CONTROL */
140   output_pending_lines();
141 #endif /* WIDOW_CONTROL */
142   if (!trap_sprung_flag && !pending_lines
143 #ifdef WIDOW_CONTROL
144       && (!widow_control || no_fill_flag)
145 #endif /* WIDOW_CONTROL */
146       ) {
147     curenv->construct_format_state(nd, was_centered, !no_fill_flag);
148     curdiv->output(nd, no_fill_flag, vs, post_vs, width);
149   } else {
150     pending_output_line **p;
151     for (p = &pending_lines; *p; p = &(*p)->next)
152       ;
153     *p = new pending_output_line(nd, no_fill_flag, vs, post_vs, width,
154 				 was_centered);
155   }
156 }
157 
158 // a line from .tl goes at the head of the queue
159 
output_title(node * nd,int no_fill_flag,vunits vs,vunits post_vs,hunits width)160 void environment::output_title(node *nd, int no_fill_flag,
161 			       vunits vs, vunits post_vs,
162 			       hunits width)
163 {
164   if (!trap_sprung_flag)
165     curdiv->output(nd, no_fill_flag, vs, post_vs, width);
166   else
167     pending_lines = new pending_output_line(nd, no_fill_flag, vs, post_vs,
168 					    width, 0, pending_lines);
169 }
170 
output_pending_lines()171 void environment::output_pending_lines()
172 {
173   while (pending_lines && pending_lines->output()) {
174     pending_output_line *tem = pending_lines;
175     pending_lines = pending_lines->next;
176     delete tem;
177   }
178 }
179 
180 #ifdef WIDOW_CONTROL
181 
mark_last_line()182 void environment::mark_last_line()
183 {
184   if (!widow_control || !pending_lines)
185     return;
186   pending_output_line *p;
187   for (p = pending_lines; p->next; p = p->next)
188     ;
189   if (!p->no_fill)
190     p->last_line = 1;
191 }
192 
widow_control_request()193 void widow_control_request()
194 {
195   int n;
196   if (has_arg() && get_integer(&n))
197     curenv->widow_control = n != 0;
198   else
199     curenv->widow_control = 1;
200   skip_line();
201 }
202 
203 #endif /* WIDOW_CONTROL */
204 
205 /* font_size functions */
206 
207 size_range *font_size::size_table = 0;
208 int font_size::nranges = 0;
209 
210 extern "C" {
211 
compare_ranges(const void * p1,const void * p2)212 int compare_ranges(const void *p1, const void *p2)
213 {
214   return ((size_range *)p1)->min - ((size_range *)p2)->min;
215 }
216 
217 }
218 
init_size_table(int * sizes)219 void font_size::init_size_table(int *sizes)
220 {
221   nranges = 0;
222   while (sizes[nranges*2] != 0)
223     nranges++;
224   assert(nranges > 0);
225   size_table = new size_range[nranges];
226   for (int i = 0; i < nranges; i++) {
227     size_table[i].min = sizes[i*2];
228     size_table[i].max = sizes[i*2 + 1];
229   }
230   qsort(size_table, nranges, sizeof(size_range), compare_ranges);
231 }
232 
font_size(int sp)233 font_size::font_size(int sp)
234 {
235   for (int i = 0; i < nranges; i++) {
236     if (sp < size_table[i].min) {
237       if (i > 0 && size_table[i].min - sp >= sp - size_table[i - 1].max)
238 	p = size_table[i - 1].max;
239       else
240 	p = size_table[i].min;
241       return;
242     }
243     if (sp <= size_table[i].max) {
244       p = sp;
245       return;
246     }
247   }
248   p = size_table[nranges - 1].max;
249 }
250 
to_units()251 int font_size::to_units()
252 {
253   return scale(p, units_per_inch, sizescale*72);
254 }
255 
256 // we can't do this in a static constructor because various dictionaries
257 // have to get initialized first
258 
init_environments()259 void init_environments()
260 {
261   curenv = env_table[0] = new environment("0");
262 }
263 
tab_character()264 void tab_character()
265 {
266   curenv->tab_char = get_optional_char();
267   skip_line();
268 }
269 
leader_character()270 void leader_character()
271 {
272   curenv->leader_char = get_optional_char();
273   skip_line();
274 }
275 
add_char(charinfo * ci)276 void environment::add_char(charinfo *ci)
277 {
278   int s;
279   node *gc_np = 0;
280   if (interrupted)
281     ;
282   // don't allow fields in dummy environments
283   else if (ci == field_delimiter_char && !dummy) {
284     if (current_field)
285       wrap_up_field();
286     else
287       start_field();
288   }
289   else if (current_field && ci == padding_indicator_char)
290     add_padding();
291   else if (current_tab) {
292     if (tab_contents == 0)
293       tab_contents = new line_start_node;
294     if (ci != hyphen_indicator_char)
295       tab_contents = tab_contents->add_char(ci, this, &tab_width, &s, &gc_np);
296     else
297       tab_contents = tab_contents->add_discretionary_hyphen();
298   }
299   else {
300     if (line == 0)
301       start_line();
302 #if 0
303     fprintf(stderr, "current line is\n");
304     line->debug_node_list();
305 #endif
306     if (ci != hyphen_indicator_char)
307       line = line->add_char(ci, this, &width_total, &space_total, &gc_np);
308     else
309       line = line->add_discretionary_hyphen();
310   }
311 #if 0
312   fprintf(stderr, "now after we have added character the line is\n");
313   line->debug_node_list();
314 #endif
315   if ((!suppress_push) && gc_np) {
316     if (gc_np && (gc_np->state == 0)) {
317       gc_np->state = construct_state(0);
318       gc_np->push_state = get_diversion_state();
319     }
320     else if (line && (line->state == 0)) {
321       line->state = construct_state(0);
322       line->push_state = get_diversion_state();
323     }
324   }
325 #if 0
326   fprintf(stderr, "now we have possibly added the state the line is\n");
327   line->debug_node_list();
328 #endif
329 }
330 
make_char_node(charinfo * ci)331 node *environment::make_char_node(charinfo *ci)
332 {
333   return make_node(ci, this);
334 }
335 
add_node(node * n)336 void environment::add_node(node *n)
337 {
338   if (n == 0)
339     return;
340   if (!suppress_push) {
341     if (n->is_special && n->state == NULL)
342       n->state = construct_state(0);
343     n->push_state = get_diversion_state();
344   }
345 
346   if (current_tab || current_field)
347     n->freeze_space();
348   if (interrupted) {
349     delete n;
350   }
351   else if (current_tab) {
352     n->next = tab_contents;
353     tab_contents = n;
354     tab_width += n->width();
355   }
356   else {
357     if (line == 0) {
358       if (discarding && n->discardable()) {
359 	// XXX possibly: input_line_start -= n->width();
360 	delete n;
361 	return;
362       }
363       start_line();
364     }
365     width_total += n->width();
366     space_total += n->nspaces();
367     n->next = line;
368     line = n;
369     construct_new_line_state(line);
370   }
371 }
372 
add_hyphen_indicator()373 void environment::add_hyphen_indicator()
374 {
375   if (current_tab || interrupted || current_field
376       || hyphen_indicator_char != 0)
377     return;
378   if (line == 0)
379     start_line();
380   line = line->add_discretionary_hyphen();
381 }
382 
get_hyphenation_flags()383 int environment::get_hyphenation_flags()
384 {
385   return hyphenation_flags;
386 }
387 
get_hyphen_line_max()388 int environment::get_hyphen_line_max()
389 {
390   return hyphen_line_max;
391 }
392 
get_hyphen_line_count()393 int environment::get_hyphen_line_count()
394 {
395   return hyphen_line_count;
396 }
397 
get_center_lines()398 int environment::get_center_lines()
399 {
400   return center_lines;
401 }
402 
get_right_justify_lines()403 int environment::get_right_justify_lines()
404 {
405   return right_justify_lines;
406 }
407 
add_italic_correction()408 void environment::add_italic_correction()
409 {
410   if (current_tab) {
411     if (tab_contents)
412       tab_contents = tab_contents->add_italic_correction(&tab_width);
413   }
414   else if (line)
415     line = line->add_italic_correction(&width_total);
416 }
417 
space_newline()418 void environment::space_newline()
419 {
420   assert(!current_tab && !current_field);
421   if (interrupted)
422     return;
423   hunits x = H0;
424   hunits sw = env_space_width(this);
425   hunits ssw = env_sentence_space_width(this);
426   if (!translate_space_to_dummy) {
427     x = sw;
428     if (node_list_ends_sentence(line) == 1)
429       x += ssw;
430   }
431   width_list *w = new width_list(sw, ssw);
432   if (node_list_ends_sentence(line) == 1)
433     w->next = new width_list(sw, ssw);
434   if (line != 0 && line->merge_space(x, sw, ssw)) {
435     width_total += x;
436     return;
437   }
438   add_node(new word_space_node(x, get_fill_color(), w));
439   possibly_break_line(0, spread_flag);
440   spread_flag = 0;
441 }
442 
space()443 void environment::space()
444 {
445   space(env_space_width(this), env_sentence_space_width(this));
446 }
447 
space(hunits space_width,hunits sentence_space_width)448 void environment::space(hunits space_width, hunits sentence_space_width)
449 {
450   if (interrupted)
451     return;
452   if (current_field && padding_indicator_char == 0) {
453     add_padding();
454     return;
455   }
456   hunits x = translate_space_to_dummy ? H0 : space_width;
457   node *p = current_tab ? tab_contents : line;
458   hunits *tp = current_tab ? &tab_width : &width_total;
459   if (p && p->nspaces() == 1 && p->width() == x
460       && node_list_ends_sentence(p->next) == 1) {
461     hunits xx = translate_space_to_dummy ? H0 : sentence_space_width;
462     if (p->merge_space(xx, space_width, sentence_space_width)) {
463       *tp += xx;
464       return;
465     }
466   }
467   if (p && p->merge_space(x, space_width, sentence_space_width)) {
468     *tp += x;
469     return;
470   }
471   add_node(new word_space_node(x,
472 			       get_fill_color(),
473 			       new width_list(space_width,
474 					      sentence_space_width)));
475   possibly_break_line(0, spread_flag);
476   spread_flag = 0;
477 }
478 
479 node *do_underline_special(int);
480 
set_font(symbol nm)481 void environment::set_font(symbol nm)
482 {
483   if (interrupted)
484     return;
485   if (nm == symbol("P") || nm.is_empty()) {
486     if (family->make_definite(prev_fontno) < 0)
487       return;
488     int tem = fontno;
489     fontno = prev_fontno;
490     prev_fontno = tem;
491   }
492   else {
493     prev_fontno = fontno;
494     int n = symbol_fontno(nm);
495     if (n < 0) {
496       n = next_available_font_position();
497       if (!mount_font(n, nm))
498 	return;
499     }
500     if (family->make_definite(n) < 0)
501       return;
502     fontno = n;
503   }
504   if (underline_spaces && fontno != prev_fontno) {
505     if (fontno == get_underline_fontno())
506       add_node(do_underline_special(1));
507     if (prev_fontno == get_underline_fontno())
508       add_node(do_underline_special(0));
509   }
510 }
511 
set_font(int n)512 void environment::set_font(int n)
513 {
514   if (interrupted)
515     return;
516   if (is_good_fontno(n)) {
517     prev_fontno = fontno;
518     fontno = n;
519   }
520   else
521     warning(WARN_FONT, "bad font number");
522 }
523 
set_family(symbol fam)524 void environment::set_family(symbol fam)
525 {
526   if (interrupted)
527     return;
528   if (fam.is_null() || fam.is_empty()) {
529     if (prev_family->make_definite(fontno) < 0)
530       return;
531     font_family *tem = family;
532     family = prev_family;
533     prev_family = tem;
534   }
535   else {
536     font_family *f = lookup_family(fam);
537     if (f->make_definite(fontno) < 0)
538       return;
539     prev_family = family;
540     family = f;
541   }
542 }
543 
set_size(int n)544 void environment::set_size(int n)
545 {
546   if (interrupted)
547     return;
548   if (n == 0) {
549     font_size temp = prev_size;
550     prev_size = size;
551     size = temp;
552     int temp2 = prev_requested_size;
553     prev_requested_size = requested_size;
554     requested_size = temp2;
555   }
556   else {
557     prev_size = size;
558     size = font_size(n);
559     prev_requested_size = requested_size;
560     requested_size = n;
561   }
562 }
563 
set_char_height(int n)564 void environment::set_char_height(int n)
565 {
566   if (interrupted)
567     return;
568   if (n == requested_size || n <= 0)
569     char_height = 0;
570   else
571     char_height = n;
572 }
573 
set_char_slant(int n)574 void environment::set_char_slant(int n)
575 {
576   if (interrupted)
577     return;
578   char_slant = n;
579 }
580 
get_prev_glyph_color()581 color *environment::get_prev_glyph_color()
582 {
583   return prev_glyph_color;
584 }
585 
get_glyph_color()586 color *environment::get_glyph_color()
587 {
588   return glyph_color;
589 }
590 
get_prev_fill_color()591 color *environment::get_prev_fill_color()
592 {
593   return prev_fill_color;
594 }
595 
get_fill_color()596 color *environment::get_fill_color()
597 {
598   return fill_color;
599 }
600 
set_glyph_color(color * c)601 void environment::set_glyph_color(color *c)
602 {
603   if (interrupted)
604     return;
605   curenv->prev_glyph_color = curenv->glyph_color;
606   curenv->glyph_color = c;
607 }
608 
set_fill_color(color * c)609 void environment::set_fill_color(color *c)
610 {
611   if (interrupted)
612     return;
613   curenv->prev_fill_color = curenv->fill_color;
614   curenv->fill_color = c;
615 }
616 
environment(symbol nm)617 environment::environment(symbol nm)
618 : dummy(0),
619   prev_line_length((units_per_inch*13)/2),
620   line_length((units_per_inch*13)/2),
621   prev_title_length((units_per_inch*13)/2),
622   title_length((units_per_inch*13)/2),
623   prev_size(sizescale*10),
624   size(sizescale*10),
625   requested_size(sizescale*10),
626   prev_requested_size(sizescale*10),
627   char_height(0),
628   char_slant(0),
629   space_size(12),
630   sentence_space_size(12),
631   adjust_mode(ADJUST_BOTH),
632   fill(1),
633   interrupted(0),
634   prev_line_interrupted(0),
635   center_lines(0),
636   right_justify_lines(0),
637   prev_vertical_spacing(points_to_units(12)),
638   vertical_spacing(points_to_units(12)),
639   prev_post_vertical_spacing(0),
640   post_vertical_spacing(0),
641   prev_line_spacing(1),
642   line_spacing(1),
643   prev_indent(0),
644   indent(0),
645   temporary_indent(0),
646   have_temporary_indent(0),
647   underline_lines(0),
648   underline_spaces(0),
649   input_trap_count(0),
650   continued_input_trap(0),
651   line(0),
652   prev_text_length(0),
653   width_total(0),
654   space_total(0),
655   input_line_start(0),
656   line_tabs(0),
657   current_tab(TAB_NONE),
658   leader_node(0),
659   tab_char(0),
660   leader_char(charset_table['.']),
661   current_field(0),
662   discarding(0),
663   spread_flag(0),
664   margin_character_flags(0),
665   margin_character_node(0),
666   margin_character_distance(points_to_units(10)),
667   numbering_nodes(0),
668   number_text_separation(1),
669   line_number_indent(0),
670   line_number_multiple(1),
671   no_number_count(0),
672   hyphenation_flags(1),
673   hyphen_line_count(0),
674   hyphen_line_max(-1),
675   hyphenation_space(H0),
676   hyphenation_margin(H0),
677   composite(0),
678   pending_lines(0),
679 #ifdef WIDOW_CONTROL
680   widow_control(0),
681 #endif /* WIDOW_CONTROL */
682   glyph_color(&default_color),
683   prev_glyph_color(&default_color),
684   fill_color(&default_color),
685   prev_fill_color(&default_color),
686   seen_space(0),
687   seen_eol(0),
688   suppress_next_eol(0),
689   seen_break(0),
690   tabs(units_per_inch/2, TAB_LEFT),
691   name(nm),
692   control_char('.'),
693   no_break_control_char('\''),
694   hyphen_indicator_char(0)
695 {
696   prev_family = family = lookup_family(default_family);
697   prev_fontno = fontno = 1;
698   if (!is_good_fontno(1))
699     fatal("font number 1 not a valid font");
700   if (family->make_definite(1) < 0)
701     fatal("invalid default family `%1'", default_family.contents());
702   prev_fontno = fontno;
703 }
704 
environment(const environment * e)705 environment::environment(const environment *e)
706 : dummy(1),
707   prev_line_length(e->prev_line_length),
708   line_length(e->line_length),
709   prev_title_length(e->prev_title_length),
710   title_length(e->title_length),
711   prev_size(e->prev_size),
712   size(e->size),
713   requested_size(e->requested_size),
714   prev_requested_size(e->prev_requested_size),
715   char_height(e->char_height),
716   char_slant(e->char_slant),
717   prev_fontno(e->prev_fontno),
718   fontno(e->fontno),
719   prev_family(e->prev_family),
720   family(e->family),
721   space_size(e->space_size),
722   sentence_space_size(e->sentence_space_size),
723   adjust_mode(e->adjust_mode),
724   fill(e->fill),
725   interrupted(0),
726   prev_line_interrupted(0),
727   center_lines(0),
728   right_justify_lines(0),
729   prev_vertical_spacing(e->prev_vertical_spacing),
730   vertical_spacing(e->vertical_spacing),
731   prev_post_vertical_spacing(e->prev_post_vertical_spacing),
732   post_vertical_spacing(e->post_vertical_spacing),
733   prev_line_spacing(e->prev_line_spacing),
734   line_spacing(e->line_spacing),
735   prev_indent(e->prev_indent),
736   indent(e->indent),
737   temporary_indent(0),
738   have_temporary_indent(0),
739   underline_lines(0),
740   underline_spaces(0),
741   input_trap_count(0),
742   continued_input_trap(0),
743   line(0),
744   prev_text_length(e->prev_text_length),
745   width_total(0),
746   space_total(0),
747   input_line_start(0),
748   line_tabs(e->line_tabs),
749   current_tab(TAB_NONE),
750   leader_node(0),
751   tab_char(e->tab_char),
752   leader_char(e->leader_char),
753   current_field(0),
754   discarding(0),
755   spread_flag(0),
756   margin_character_flags(e->margin_character_flags),
757   margin_character_node(e->margin_character_node),
758   margin_character_distance(e->margin_character_distance),
759   numbering_nodes(0),
760   number_text_separation(e->number_text_separation),
761   line_number_indent(e->line_number_indent),
762   line_number_multiple(e->line_number_multiple),
763   no_number_count(e->no_number_count),
764   hyphenation_flags(e->hyphenation_flags),
765   hyphen_line_count(0),
766   hyphen_line_max(e->hyphen_line_max),
767   hyphenation_space(e->hyphenation_space),
768   hyphenation_margin(e->hyphenation_margin),
769   composite(0),
770   pending_lines(0),
771 #ifdef WIDOW_CONTROL
772   widow_control(e->widow_control),
773 #endif /* WIDOW_CONTROL */
774   glyph_color(e->glyph_color),
775   prev_glyph_color(e->prev_glyph_color),
776   fill_color(e->fill_color),
777   prev_fill_color(e->prev_fill_color),
778   seen_space(e->seen_space),
779   seen_eol(e->seen_eol),
780   suppress_next_eol(e->suppress_next_eol),
781   seen_break(e->seen_break),
782   tabs(e->tabs),
783   name(e->name),		// so that eg `.if "\n[.ev]"0"' works
784   control_char(e->control_char),
785   no_break_control_char(e->no_break_control_char),
786   hyphen_indicator_char(e->hyphen_indicator_char)
787 {
788 }
789 
copy(const environment * e)790 void environment::copy(const environment *e)
791 {
792   prev_line_length = e->prev_line_length;
793   line_length = e->line_length;
794   prev_title_length = e->prev_title_length;
795   title_length = e->title_length;
796   prev_size = e->prev_size;
797   size = e->size;
798   prev_requested_size = e->prev_requested_size;
799   requested_size = e->requested_size;
800   char_height = e->char_height;
801   char_slant = e->char_slant;
802   space_size = e->space_size;
803   sentence_space_size = e->sentence_space_size;
804   adjust_mode = e->adjust_mode;
805   fill = e->fill;
806   interrupted = 0;
807   prev_line_interrupted = 0;
808   center_lines = 0;
809   right_justify_lines = 0;
810   prev_vertical_spacing = e->prev_vertical_spacing;
811   vertical_spacing = e->vertical_spacing;
812   prev_post_vertical_spacing = e->prev_post_vertical_spacing,
813   post_vertical_spacing = e->post_vertical_spacing,
814   prev_line_spacing = e->prev_line_spacing;
815   line_spacing = e->line_spacing;
816   prev_indent = e->prev_indent;
817   indent = e->indent;
818   have_temporary_indent = 0;
819   temporary_indent = 0;
820   underline_lines = 0;
821   underline_spaces = 0;
822   input_trap_count = 0;
823   continued_input_trap = 0;
824   prev_text_length = e->prev_text_length;
825   width_total = 0;
826   space_total = 0;
827   input_line_start = 0;
828   control_char = e->control_char;
829   no_break_control_char = e->no_break_control_char;
830   hyphen_indicator_char = e->hyphen_indicator_char;
831   spread_flag = 0;
832   line = 0;
833   pending_lines = 0;
834   discarding = 0;
835   tabs = e->tabs;
836   line_tabs = e->line_tabs;
837   current_tab = TAB_NONE;
838   current_field = 0;
839   margin_character_flags = e->margin_character_flags;
840   margin_character_node = e->margin_character_node;
841   margin_character_distance = e->margin_character_distance;
842   numbering_nodes = 0;
843   number_text_separation = e->number_text_separation;
844   line_number_multiple = e->line_number_multiple;
845   line_number_indent = e->line_number_indent;
846   no_number_count = e->no_number_count;
847   tab_char = e->tab_char;
848   leader_char = e->leader_char;
849   hyphenation_flags = e->hyphenation_flags;
850   fontno = e->fontno;
851   prev_fontno = e->prev_fontno;
852   dummy = e->dummy;
853   family = e->family;
854   prev_family = e->prev_family;
855   leader_node = 0;
856 #ifdef WIDOW_CONTROL
857   widow_control = e->widow_control;
858 #endif /* WIDOW_CONTROL */
859   hyphen_line_max = e->hyphen_line_max;
860   hyphen_line_count = 0;
861   hyphenation_space = e->hyphenation_space;
862   hyphenation_margin = e->hyphenation_margin;
863   composite = 0;
864   glyph_color= e->glyph_color;
865   prev_glyph_color = e->prev_glyph_color;
866   fill_color = e->fill_color;
867   prev_fill_color = e->prev_fill_color;
868 }
869 
~environment()870 environment::~environment()
871 {
872   delete leader_node;
873   delete_node_list(line);
874   delete_node_list(numbering_nodes);
875 }
876 
get_input_line_position()877 hunits environment::get_input_line_position()
878 {
879   hunits n;
880   if (line == 0)
881     n = -input_line_start;
882   else
883     n = width_total - input_line_start;
884   if (current_tab)
885     n += tab_width;
886   return n;
887 }
888 
set_input_line_position(hunits n)889 void environment::set_input_line_position(hunits n)
890 {
891   input_line_start = line == 0 ? -n : width_total - n;
892   if (current_tab)
893     input_line_start += tab_width;
894 }
895 
get_line_length()896 hunits environment::get_line_length()
897 {
898   return line_length;
899 }
900 
get_saved_line_length()901 hunits environment::get_saved_line_length()
902 {
903   if (line)
904     return target_text_length + saved_indent;
905   else
906     return line_length;
907 }
908 
get_vertical_spacing()909 vunits environment::get_vertical_spacing()
910 {
911   return vertical_spacing;
912 }
913 
get_post_vertical_spacing()914 vunits environment::get_post_vertical_spacing()
915 {
916   return post_vertical_spacing;
917 }
918 
get_line_spacing()919 int environment::get_line_spacing()
920 {
921   return line_spacing;
922 }
923 
total_post_vertical_spacing()924 vunits environment::total_post_vertical_spacing()
925 {
926   vunits tem(post_vertical_spacing);
927   if (line_spacing > 1)
928     tem += (line_spacing - 1)*vertical_spacing;
929   return tem;
930 }
931 
get_bold()932 int environment::get_bold()
933 {
934   return get_bold_fontno(fontno);
935 }
936 
get_digit_width()937 hunits environment::get_digit_width()
938 {
939   return env_digit_width(this);
940 }
941 
get_adjust_mode()942 int environment::get_adjust_mode()
943 {
944   return adjust_mode;
945 }
946 
get_fill()947 int environment::get_fill()
948 {
949   return fill;
950 }
951 
get_indent()952 hunits environment::get_indent()
953 {
954   return indent;
955 }
956 
get_saved_indent()957 hunits environment::get_saved_indent()
958 {
959   if (line)
960     return saved_indent;
961   else if (have_temporary_indent)
962     return temporary_indent;
963   else
964     return indent;
965 }
966 
get_temporary_indent()967 hunits environment::get_temporary_indent()
968 {
969   return temporary_indent;
970 }
971 
get_title_length()972 hunits environment::get_title_length()
973 {
974   return title_length;
975 }
976 
get_prev_char()977 node *environment::get_prev_char()
978 {
979   for (node *n = current_tab ? tab_contents : line; n; n = n->next) {
980     node *last = n->last_char_node();
981     if (last)
982       return last;
983   }
984   return 0;
985 }
986 
get_prev_char_width()987 hunits environment::get_prev_char_width()
988 {
989   node *last = get_prev_char();
990   if (!last)
991     return H0;
992   return last->width();
993 }
994 
get_prev_char_skew()995 hunits environment::get_prev_char_skew()
996 {
997   node *last = get_prev_char();
998   if (!last)
999     return H0;
1000   return last->skew();
1001 }
1002 
get_prev_char_height()1003 vunits environment::get_prev_char_height()
1004 {
1005   node *last = get_prev_char();
1006   if (!last)
1007     return V0;
1008   vunits min, max;
1009   last->vertical_extent(&min, &max);
1010   return -min;
1011 }
1012 
get_prev_char_depth()1013 vunits environment::get_prev_char_depth()
1014 {
1015   node *last = get_prev_char();
1016   if (!last)
1017     return V0;
1018   vunits min, max;
1019   last->vertical_extent(&min, &max);
1020   return max;
1021 }
1022 
get_text_length()1023 hunits environment::get_text_length()
1024 {
1025   hunits n = line == 0 ? H0 : width_total;
1026   if (current_tab)
1027     n += tab_width;
1028   return n;
1029 }
1030 
get_prev_text_length()1031 hunits environment::get_prev_text_length()
1032 {
1033   return prev_text_length;
1034 }
1035 
1036 
1037 static int sb_reg_contents = 0;
1038 static int st_reg_contents = 0;
1039 static int ct_reg_contents = 0;
1040 static int rsb_reg_contents = 0;
1041 static int rst_reg_contents = 0;
1042 static int skw_reg_contents = 0;
1043 static int ssc_reg_contents = 0;
1044 
width_registers()1045 void environment::width_registers()
1046 {
1047   // this is used to implement \w; it sets the st, sb, ct registers
1048   vunits min = 0, max = 0, cur = 0;
1049   int character_type = 0;
1050   ssc_reg_contents = line ? line->subscript_correction().to_units() : 0;
1051   skw_reg_contents = line ? line->skew().to_units() : 0;
1052   line = reverse_node_list(line);
1053   vunits real_min = V0;
1054   vunits real_max = V0;
1055   vunits v1, v2;
1056   for (node *tem = line; tem; tem = tem->next) {
1057     tem->vertical_extent(&v1, &v2);
1058     v1 += cur;
1059     if (v1 < real_min)
1060       real_min = v1;
1061     v2 += cur;
1062     if (v2 > real_max)
1063       real_max = v2;
1064     if ((cur += tem->vertical_width()) < min)
1065       min = cur;
1066     else if (cur > max)
1067       max = cur;
1068     character_type |= tem->character_type();
1069   }
1070   line = reverse_node_list(line);
1071   st_reg_contents = -min.to_units();
1072   sb_reg_contents = -max.to_units();
1073   rst_reg_contents = -real_min.to_units();
1074   rsb_reg_contents = -real_max.to_units();
1075   ct_reg_contents = character_type;
1076 }
1077 
extract_output_line()1078 node *environment::extract_output_line()
1079 {
1080   if (current_tab)
1081     wrap_up_tab();
1082   node *n = line;
1083   line = 0;
1084   return n;
1085 }
1086 
1087 /* environment related requests */
1088 
environment_switch()1089 void environment_switch()
1090 {
1091   int pop = 0;	// 1 means pop, 2 means pop but no error message on underflow
1092   if (curenv->is_dummy())
1093     error("can't switch environments when current environment is dummy");
1094   else if (!has_arg())
1095     pop = 1;
1096   else {
1097     symbol nm;
1098     if (!tok.delimiter()) {
1099       // It looks like a number.
1100       int n;
1101       if (get_integer(&n)) {
1102 	if (n >= 0 && n < NENVIRONMENTS) {
1103 	  env_stack = new env_list(curenv, env_stack);
1104 	  if (env_table[n] == 0)
1105 	    env_table[n] = new environment(i_to_a(n));
1106 	  curenv = env_table[n];
1107 	}
1108 	else
1109 	  nm = i_to_a(n);
1110       }
1111       else
1112 	pop = 2;
1113     }
1114     else {
1115       nm = get_long_name(1);
1116       if (nm.is_null())
1117 	pop = 2;
1118     }
1119     if (!nm.is_null()) {
1120       environment *e = (environment *)env_dictionary.lookup(nm);
1121       if (!e) {
1122 	e = new environment(nm);
1123 	(void)env_dictionary.lookup(nm, e);
1124       }
1125       env_stack = new env_list(curenv, env_stack);
1126       curenv = e;
1127     }
1128   }
1129   if (pop) {
1130     if (env_stack == 0) {
1131       if (pop == 1)
1132 	error("environment stack underflow");
1133     }
1134     else {
1135       int seen_space = curenv->seen_space;
1136       int seen_eol   = curenv->seen_eol;
1137       int suppress_next_eol = curenv->suppress_next_eol;
1138       curenv = env_stack->env;
1139       curenv->seen_space = seen_space;
1140       curenv->seen_eol   = seen_eol;
1141       curenv->suppress_next_eol = suppress_next_eol;
1142       env_list *tem = env_stack;
1143       env_stack = env_stack->next;
1144       delete tem;
1145     }
1146   }
1147   skip_line();
1148 }
1149 
environment_copy()1150 void environment_copy()
1151 {
1152   symbol nm;
1153   environment *e=0;
1154   tok.skip();
1155   if (!tok.delimiter()) {
1156     // It looks like a number.
1157     int n;
1158     if (get_integer(&n)) {
1159       if (n >= 0 && n < NENVIRONMENTS)
1160 	e = env_table[n];
1161       else
1162 	nm = i_to_a(n);
1163     }
1164   }
1165   else
1166     nm = get_long_name(1);
1167   if (!e && !nm.is_null())
1168     e = (environment *)env_dictionary.lookup(nm);
1169   if (e == 0) {
1170     error("No environment to copy from");
1171     return;
1172   }
1173   else
1174     curenv->copy(e);
1175   skip_line();
1176 }
1177 
fill_color_change()1178 void fill_color_change()
1179 {
1180   symbol s = get_name();
1181   if (s.is_null())
1182     curenv->set_fill_color(curenv->get_prev_fill_color());
1183   else
1184     do_fill_color(s);
1185   skip_line();
1186 }
1187 
glyph_color_change()1188 void glyph_color_change()
1189 {
1190   symbol s = get_name();
1191   if (s.is_null())
1192     curenv->set_glyph_color(curenv->get_prev_glyph_color());
1193   else
1194     do_glyph_color(s);
1195   skip_line();
1196 }
1197 
1198 static symbol P_symbol("P");
1199 
font_change()1200 void font_change()
1201 {
1202   symbol s = get_name();
1203   int is_number = 1;
1204   if (s.is_null() || s == P_symbol) {
1205     s = P_symbol;
1206     is_number = 0;
1207   }
1208   else {
1209     for (const char *p = s.contents(); p != 0 && *p != 0; p++)
1210       if (!csdigit(*p)) {
1211 	is_number = 0;
1212 	break;
1213       }
1214   }
1215   if (is_number)
1216     curenv->set_font(atoi(s.contents()));
1217   else
1218     curenv->set_font(s);
1219   skip_line();
1220 }
1221 
family_change()1222 void family_change()
1223 {
1224   symbol s = get_name();
1225   curenv->set_family(s);
1226   skip_line();
1227 }
1228 
point_size()1229 void point_size()
1230 {
1231   int n;
1232   if (has_arg() && get_number(&n, 'z', curenv->get_requested_point_size())) {
1233     if (n <= 0)
1234       n = 1;
1235     curenv->set_size(n);
1236   }
1237   else
1238     curenv->set_size(0);
1239   skip_line();
1240 }
1241 
override_sizes()1242 void override_sizes()
1243 {
1244   int n = 16;
1245   int *sizes = new int[n];
1246   int i = 0;
1247   char *buf = read_string();
1248   if (!buf)
1249     return;
1250   char *p = strtok(buf, " \t");
1251   for (;;) {
1252     if (!p)
1253       break;
1254     int lower, upper;
1255     switch (sscanf(p, "%d-%d", &lower, &upper)) {
1256     case 1:
1257       upper = lower;
1258       // fall through
1259     case 2:
1260       if (lower <= upper && lower >= 0)
1261 	break;
1262       // fall through
1263     default:
1264       warning(WARN_RANGE, "bad size range `%1'", p);
1265       return;
1266     }
1267     if (i + 2 > n) {
1268       int *old_sizes = sizes;
1269       sizes = new int[n*2];
1270       memcpy(sizes, old_sizes, n*sizeof(int));
1271       n *= 2;
1272       a_delete old_sizes;
1273     }
1274     sizes[i++] = lower;
1275     if (lower == 0)
1276       break;
1277     sizes[i++] = upper;
1278     p = strtok(0, " \t");
1279   }
1280   font_size::init_size_table(sizes);
1281 }
1282 
space_size()1283 void space_size()
1284 {
1285   int n;
1286   if (get_integer(&n)) {
1287     curenv->space_size = n;
1288     if (has_arg() && get_integer(&n))
1289       curenv->sentence_space_size = n;
1290     else
1291       curenv->sentence_space_size = curenv->space_size;
1292   }
1293   skip_line();
1294 }
1295 
fill()1296 void fill()
1297 {
1298   while (!tok.newline() && !tok.eof())
1299     tok.next();
1300   if (break_flag)
1301     curenv->do_break();
1302   curenv->fill = 1;
1303   tok.next();
1304 }
1305 
no_fill()1306 void no_fill()
1307 {
1308   while (!tok.newline() && !tok.eof())
1309     tok.next();
1310   if (break_flag)
1311     curenv->do_break();
1312   curenv->fill = 0;
1313   curenv->suppress_next_eol = 1;
1314   tok.next();
1315 }
1316 
center()1317 void center()
1318 {
1319   int n;
1320   if (!has_arg() || !get_integer(&n))
1321     n = 1;
1322   else if (n < 0)
1323     n = 0;
1324   while (!tok.newline() && !tok.eof())
1325     tok.next();
1326   if (break_flag)
1327     curenv->do_break();
1328   curenv->right_justify_lines = 0;
1329   curenv->center_lines = n;
1330   curdiv->modified_tag.incl(MTSM_CE);
1331   tok.next();
1332 }
1333 
right_justify()1334 void right_justify()
1335 {
1336   int n;
1337   if (!has_arg() || !get_integer(&n))
1338     n = 1;
1339   else if (n < 0)
1340     n = 0;
1341   while (!tok.newline() && !tok.eof())
1342     tok.next();
1343   if (break_flag)
1344     curenv->do_break();
1345   curenv->center_lines = 0;
1346   curenv->right_justify_lines = n;
1347   curdiv->modified_tag.incl(MTSM_RJ);
1348   tok.next();
1349 }
1350 
line_length()1351 void line_length()
1352 {
1353   hunits temp;
1354   if (has_arg() && get_hunits(&temp, 'm', curenv->line_length)) {
1355     if (temp < H0) {
1356       warning(WARN_RANGE, "bad line length %1u", temp.to_units());
1357       temp = H0;
1358     }
1359   }
1360   else
1361     temp = curenv->prev_line_length;
1362   curenv->prev_line_length = curenv->line_length;
1363   curenv->line_length = temp;
1364   curdiv->modified_tag.incl(MTSM_LL);
1365   skip_line();
1366 }
1367 
title_length()1368 void title_length()
1369 {
1370   hunits temp;
1371   if (has_arg() && get_hunits(&temp, 'm', curenv->title_length)) {
1372     if (temp < H0) {
1373       warning(WARN_RANGE, "bad title length %1u", temp.to_units());
1374       temp = H0;
1375     }
1376   }
1377   else
1378     temp = curenv->prev_title_length;
1379   curenv->prev_title_length = curenv->title_length;
1380   curenv->title_length = temp;
1381   skip_line();
1382 }
1383 
vertical_spacing()1384 void vertical_spacing()
1385 {
1386   vunits temp;
1387   if (has_arg() && get_vunits(&temp, 'p', curenv->vertical_spacing)) {
1388     if (temp < V0) {
1389       warning(WARN_RANGE, "vertical spacing must not be negative");
1390       temp = vresolution;
1391     }
1392   }
1393   else
1394     temp = curenv->prev_vertical_spacing;
1395   curenv->prev_vertical_spacing = curenv->vertical_spacing;
1396   curenv->vertical_spacing = temp;
1397   skip_line();
1398 }
1399 
post_vertical_spacing()1400 void post_vertical_spacing()
1401 {
1402   vunits temp;
1403   if (has_arg() && get_vunits(&temp, 'p', curenv->post_vertical_spacing)) {
1404     if (temp < V0) {
1405       warning(WARN_RANGE,
1406 	      "post vertical spacing must be greater than or equal to 0");
1407       temp = V0;
1408     }
1409   }
1410   else
1411     temp = curenv->prev_post_vertical_spacing;
1412   curenv->prev_post_vertical_spacing = curenv->post_vertical_spacing;
1413   curenv->post_vertical_spacing = temp;
1414   skip_line();
1415 }
1416 
line_spacing()1417 void line_spacing()
1418 {
1419   int temp;
1420   if (has_arg() && get_integer(&temp)) {
1421     if (temp < 1) {
1422       warning(WARN_RANGE, "value %1 out of range: interpreted as 1", temp);
1423       temp = 1;
1424     }
1425   }
1426   else
1427     temp = curenv->prev_line_spacing;
1428   curenv->prev_line_spacing = curenv->line_spacing;
1429   curenv->line_spacing = temp;
1430   skip_line();
1431 }
1432 
indent()1433 void indent()
1434 {
1435   hunits temp;
1436   if (has_arg() && get_hunits(&temp, 'm', curenv->indent)) {
1437     if (temp < H0) {
1438       warning(WARN_RANGE, "indent cannot be negative");
1439       temp = H0;
1440     }
1441   }
1442   else
1443     temp = curenv->prev_indent;
1444   while (!tok.newline() && !tok.eof())
1445     tok.next();
1446   if (break_flag)
1447     curenv->do_break();
1448   curenv->have_temporary_indent = 0;
1449   curenv->prev_indent = curenv->indent;
1450   curenv->indent = temp;
1451   curdiv->modified_tag.incl(MTSM_IN);
1452   tok.next();
1453 }
1454 
temporary_indent()1455 void temporary_indent()
1456 {
1457   int err = 0;
1458   hunits temp;
1459   if (!get_hunits(&temp, 'm', curenv->get_indent()))
1460     err = 1;
1461   while (!tok.newline() && !tok.eof())
1462     tok.next();
1463   if (break_flag)
1464     curenv->do_break();
1465   if (temp < H0) {
1466     warning(WARN_RANGE, "total indent cannot be negative");
1467     temp = H0;
1468   }
1469   if (!err) {
1470     curenv->temporary_indent = temp;
1471     curenv->have_temporary_indent = 1;
1472     curdiv->modified_tag.incl(MTSM_TI);
1473   }
1474   tok.next();
1475 }
1476 
do_underline_special(int underline_spaces)1477 node *do_underline_special(int underline_spaces)
1478 {
1479   macro m;
1480   m.append_str("x u ");
1481   m.append(underline_spaces + '0');
1482   return new special_node(m, 1);
1483 }
1484 
do_underline(int underline_spaces)1485 void do_underline(int underline_spaces)
1486 {
1487   int n;
1488   if (!has_arg() || !get_integer(&n))
1489     n = 1;
1490   if (n <= 0) {
1491     if (curenv->underline_lines > 0) {
1492       curenv->prev_fontno = curenv->fontno;
1493       curenv->fontno = curenv->pre_underline_fontno;
1494       if (underline_spaces) {
1495 	curenv->underline_spaces = 0;
1496 	curenv->add_node(do_underline_special(0));
1497       }
1498     }
1499     curenv->underline_lines = 0;
1500   }
1501   else {
1502     curenv->underline_lines = n;
1503     curenv->pre_underline_fontno = curenv->fontno;
1504     curenv->fontno = get_underline_fontno();
1505     if (underline_spaces) {
1506       curenv->underline_spaces = 1;
1507       curenv->add_node(do_underline_special(1));
1508     }
1509   }
1510   skip_line();
1511 }
1512 
continuous_underline()1513 void continuous_underline()
1514 {
1515   do_underline(1);
1516 }
1517 
underline()1518 void underline()
1519 {
1520   do_underline(0);
1521 }
1522 
control_char()1523 void control_char()
1524 {
1525   curenv->control_char = '.';
1526   if (has_arg()) {
1527     if (tok.ch() == 0)
1528       error("bad control character");
1529     else
1530       curenv->control_char = tok.ch();
1531   }
1532   skip_line();
1533 }
1534 
no_break_control_char()1535 void no_break_control_char()
1536 {
1537   curenv->no_break_control_char = '\'';
1538   if (has_arg()) {
1539     if (tok.ch() == 0)
1540       error("bad control character");
1541     else
1542       curenv->no_break_control_char = tok.ch();
1543   }
1544   skip_line();
1545 }
1546 
margin_character()1547 void margin_character()
1548 {
1549   while (tok.space())
1550     tok.next();
1551   charinfo *ci = tok.get_char();
1552   if (ci) {
1553     // Call tok.next() only after making the node so that
1554     // .mc \s+9\(br\s0 works.
1555     node *nd = curenv->make_char_node(ci);
1556     tok.next();
1557     if (nd) {
1558       delete curenv->margin_character_node;
1559       curenv->margin_character_node = nd;
1560       curenv->margin_character_flags = (MARGIN_CHARACTER_ON
1561 					|MARGIN_CHARACTER_NEXT);
1562       hunits d;
1563       if (has_arg() && get_hunits(&d, 'm'))
1564 	curenv->margin_character_distance = d;
1565     }
1566   }
1567   else {
1568     check_missing_character();
1569     curenv->margin_character_flags &= ~MARGIN_CHARACTER_ON;
1570     if (curenv->margin_character_flags == 0) {
1571       delete curenv->margin_character_node;
1572       curenv->margin_character_node = 0;
1573     }
1574   }
1575   skip_line();
1576 }
1577 
number_lines()1578 void number_lines()
1579 {
1580   delete_node_list(curenv->numbering_nodes);
1581   curenv->numbering_nodes = 0;
1582   if (has_arg()) {
1583     node *nd = 0;
1584     for (int i = '9'; i >= '0'; i--) {
1585       node *tem = make_node(charset_table[i], curenv);
1586       if (!tem) {
1587 	skip_line();
1588 	return;
1589       }
1590       tem->next = nd;
1591       nd = tem;
1592     }
1593     curenv->numbering_nodes = nd;
1594     curenv->line_number_digit_width = env_digit_width(curenv);
1595     int n;
1596     if (!tok.delimiter()) {
1597       if (get_integer(&n, next_line_number)) {
1598 	next_line_number = n;
1599 	if (next_line_number < 0) {
1600 	  warning(WARN_RANGE, "negative line number");
1601 	  next_line_number = 0;
1602 	}
1603       }
1604     }
1605     else
1606       while (!tok.space() && !tok.newline() && !tok.eof())
1607 	tok.next();
1608     if (has_arg()) {
1609       if (!tok.delimiter()) {
1610 	if (get_integer(&n)) {
1611 	  if (n <= 0) {
1612 	    warning(WARN_RANGE, "negative or zero line number multiple");
1613 	  }
1614 	  else
1615 	    curenv->line_number_multiple = n;
1616 	}
1617       }
1618       else
1619 	while (!tok.space() && !tok.newline() && !tok.eof())
1620 	  tok.next();
1621       if (has_arg()) {
1622 	if (!tok.delimiter()) {
1623 	  if (get_integer(&n))
1624 	    curenv->number_text_separation = n;
1625 	}
1626 	else
1627 	  while (!tok.space() && !tok.newline() && !tok.eof())
1628 	    tok.next();
1629 	if (has_arg() && !tok.delimiter() && get_integer(&n))
1630 	  curenv->line_number_indent = n;
1631       }
1632     }
1633   }
1634   skip_line();
1635 }
1636 
no_number()1637 void no_number()
1638 {
1639   int n;
1640   if (has_arg() && get_integer(&n))
1641     curenv->no_number_count = n > 0 ? n : 0;
1642   else
1643     curenv->no_number_count = 1;
1644   skip_line();
1645 }
1646 
no_hyphenate()1647 void no_hyphenate()
1648 {
1649   curenv->hyphenation_flags = 0;
1650   skip_line();
1651 }
1652 
hyphenate_request()1653 void hyphenate_request()
1654 {
1655   int n;
1656   if (has_arg() && get_integer(&n))
1657     curenv->hyphenation_flags = n;
1658   else
1659     curenv->hyphenation_flags = 1;
1660   skip_line();
1661 }
1662 
hyphen_char()1663 void hyphen_char()
1664 {
1665   curenv->hyphen_indicator_char = get_optional_char();
1666   skip_line();
1667 }
1668 
hyphen_line_max_request()1669 void hyphen_line_max_request()
1670 {
1671   int n;
1672   if (has_arg() && get_integer(&n))
1673     curenv->hyphen_line_max = n;
1674   else
1675     curenv->hyphen_line_max = -1;
1676   skip_line();
1677 }
1678 
interrupt()1679 void environment::interrupt()
1680 {
1681   if (!dummy) {
1682     add_node(new transparent_dummy_node);
1683     interrupted = 1;
1684   }
1685 }
1686 
newline()1687 void environment::newline()
1688 {
1689   int was_centered = 0;
1690   if (underline_lines > 0) {
1691     if (--underline_lines == 0) {
1692       prev_fontno = fontno;
1693       fontno = pre_underline_fontno;
1694       if (underline_spaces) {
1695         underline_spaces = 0;
1696         add_node(do_underline_special(0));
1697       }
1698     }
1699   }
1700   if (current_field)
1701     wrap_up_field();
1702   if (current_tab)
1703     wrap_up_tab();
1704   // strip trailing spaces
1705   while (line != 0 && line->discardable()) {
1706     width_total -= line->width();
1707     space_total -= line->nspaces();
1708     node *tem = line;
1709     line = line->next;
1710     delete tem;
1711   }
1712   node *to_be_output = 0;
1713   hunits to_be_output_width;
1714   prev_line_interrupted = 0;
1715   if (dummy)
1716     space_newline();
1717   else if (interrupted) {
1718     interrupted = 0;
1719     // see environment::final_break
1720     prev_line_interrupted = exit_started ? 2 : 1;
1721   }
1722   else if (center_lines > 0) {
1723     --center_lines;
1724     hunits x = target_text_length - width_total;
1725     if (x > H0)
1726       saved_indent += x/2;
1727     to_be_output = line;
1728     was_centered = 1;
1729     to_be_output_width = width_total;
1730     line = 0;
1731   }
1732   else if (right_justify_lines > 0) {
1733     --right_justify_lines;
1734     hunits x = target_text_length - width_total;
1735     if (x > H0)
1736       saved_indent += x;
1737     to_be_output = line;
1738     to_be_output_width = width_total;
1739     line = 0;
1740   }
1741   else if (fill)
1742     space_newline();
1743   else {
1744     to_be_output = line;
1745     to_be_output_width = width_total;
1746     line = 0;
1747   }
1748   input_line_start = line == 0 ? H0 : width_total;
1749   if (to_be_output) {
1750     if (is_html && !fill) {
1751       curdiv->modified_tag.incl(MTSM_EOL);
1752       if (suppress_next_eol)
1753 	suppress_next_eol = 0;
1754       else
1755 	seen_eol = 1;
1756     }
1757 
1758     output_line(to_be_output, to_be_output_width, was_centered);
1759     hyphen_line_count = 0;
1760   }
1761   if (input_trap_count > 0) {
1762     if (!(continued_input_trap && prev_line_interrupted))
1763       if (--input_trap_count == 0)
1764 	spring_trap(input_trap);
1765   }
1766 }
1767 
output_line(node * n,hunits width,int was_centered)1768 void environment::output_line(node *n, hunits width, int was_centered)
1769 {
1770   prev_text_length = width;
1771   if (margin_character_flags) {
1772     hunits d = line_length + margin_character_distance - saved_indent - width;
1773     if (d > 0) {
1774       n = new hmotion_node(d, get_fill_color(), n);
1775       width += d;
1776     }
1777     margin_character_flags &= ~MARGIN_CHARACTER_NEXT;
1778     node *tem;
1779     if (!margin_character_flags) {
1780       tem = margin_character_node;
1781       margin_character_node = 0;
1782     }
1783     else
1784       tem = margin_character_node->copy();
1785     tem->next = n;
1786     n = tem;
1787     width += tem->width();
1788   }
1789   node *nn = 0;
1790   while (n != 0) {
1791     node *tem = n->next;
1792     n->next = nn;
1793     nn = n;
1794     n = tem;
1795   }
1796   if (!saved_indent.is_zero())
1797     nn = new hmotion_node(saved_indent, get_fill_color(), nn);
1798   width += saved_indent;
1799   if (no_number_count > 0)
1800     --no_number_count;
1801   else if (numbering_nodes) {
1802     hunits w = (line_number_digit_width
1803 		*(3+line_number_indent+number_text_separation));
1804     if (next_line_number % line_number_multiple != 0)
1805       nn = new hmotion_node(w, get_fill_color(), nn);
1806     else {
1807       hunits x = w;
1808       nn = new hmotion_node(number_text_separation * line_number_digit_width,
1809 			    get_fill_color(), nn);
1810       x -= number_text_separation*line_number_digit_width;
1811       char buf[30];
1812       sprintf(buf, "%3d", next_line_number);
1813       for (char *p = strchr(buf, '\0') - 1; p >= buf && *p != ' '; --p) {
1814 	node *gn = numbering_nodes;
1815 	for (int count = *p - '0'; count > 0; count--)
1816 	  gn = gn->next;
1817 	gn = gn->copy();
1818 	x -= gn->width();
1819 	gn->next = nn;
1820 	nn = gn;
1821       }
1822       nn = new hmotion_node(x, get_fill_color(), nn);
1823     }
1824     width += w;
1825     ++next_line_number;
1826   }
1827   output(nn, !fill, vertical_spacing, total_post_vertical_spacing(), width,
1828 	 was_centered);
1829 }
1830 
start_line()1831 void environment::start_line()
1832 {
1833   assert(line == 0);
1834   discarding = 0;
1835   line = new line_start_node;
1836   if (have_temporary_indent) {
1837     saved_indent = temporary_indent;
1838     have_temporary_indent = 0;
1839   }
1840   else
1841     saved_indent = indent;
1842   target_text_length = line_length - saved_indent;
1843   width_total = H0;
1844   space_total = 0;
1845 }
1846 
get_hyphenation_space()1847 hunits environment::get_hyphenation_space()
1848 {
1849   return hyphenation_space;
1850 }
1851 
hyphenation_space_request()1852 void hyphenation_space_request()
1853 {
1854   hunits n;
1855   if (get_hunits(&n, 'm')) {
1856     if (n < H0) {
1857       warning(WARN_RANGE, "hyphenation space cannot be negative");
1858       n = H0;
1859     }
1860     curenv->hyphenation_space = n;
1861   }
1862   skip_line();
1863 }
1864 
get_hyphenation_margin()1865 hunits environment::get_hyphenation_margin()
1866 {
1867   return hyphenation_margin;
1868 }
1869 
hyphenation_margin_request()1870 void hyphenation_margin_request()
1871 {
1872   hunits n;
1873   if (get_hunits(&n, 'm')) {
1874     if (n < H0) {
1875       warning(WARN_RANGE, "hyphenation margin cannot be negative");
1876       n = H0;
1877     }
1878     curenv->hyphenation_margin = n;
1879   }
1880   skip_line();
1881 }
1882 
choose_breakpoint()1883 breakpoint *environment::choose_breakpoint()
1884 {
1885   hunits x = width_total;
1886   int s = space_total;
1887   node *n = line;
1888   breakpoint *best_bp = 0;	// the best breakpoint so far
1889   int best_bp_fits = 0;
1890   while (n != 0) {
1891     x -= n->width();
1892     s -= n->nspaces();
1893     breakpoint *bp = n->get_breakpoints(x, s);
1894     while (bp != 0) {
1895       if (bp->width <= target_text_length) {
1896 	if (!bp->hyphenated) {
1897 	  breakpoint *tem = bp->next;
1898 	  bp->next = 0;
1899 	  while (tem != 0) {
1900 	    breakpoint *tem1 = tem;
1901 	    tem = tem->next;
1902 	    delete tem1;
1903 	  }
1904 	  if (best_bp_fits
1905 	      // Decide whether to use the hyphenated breakpoint.
1906 	      && (hyphen_line_max < 0
1907 		  // Only choose the hyphenated breakpoint if it would not
1908 		  // exceed the maximum number of consecutive hyphenated
1909 		  // lines.
1910 		  || hyphen_line_count + 1 <= hyphen_line_max)
1911 	      && !(adjust_mode == ADJUST_BOTH
1912 		   // Don't choose the hyphenated breakpoint if the line
1913 		   // can be justified by adding no more than
1914 		   // hyphenation_space to any word space.
1915 		   ? (bp->nspaces > 0
1916 		      && (((target_text_length - bp->width
1917 			    + (bp->nspaces - 1)*hresolution)/bp->nspaces)
1918 			  <= hyphenation_space))
1919 		   // Don't choose the hyphenated breakpoint if the line
1920 		   // is no more than hyphenation_margin short.
1921 		   : target_text_length - bp->width <= hyphenation_margin)) {
1922 	    delete bp;
1923 	    return best_bp;
1924 	  }
1925 	  if (best_bp)
1926 	    delete best_bp;
1927 	  return bp;
1928 	}
1929 	else {
1930 	  if ((adjust_mode == ADJUST_BOTH
1931 	       ? hyphenation_space == H0
1932 	       : hyphenation_margin == H0)
1933 	      && (hyphen_line_max < 0
1934 		  || hyphen_line_count + 1 <= hyphen_line_max)) {
1935 	    // No need to consider a non-hyphenated breakpoint.
1936 	    if (best_bp)
1937 	      delete best_bp;
1938 	    breakpoint *tem = bp->next;
1939 	    bp->next = 0;
1940 	    while (tem != 0) {
1941 	      breakpoint *tem1 = tem;
1942 	      tem = tem->next;
1943 	      delete tem1;
1944 	    }
1945 	    return bp;
1946 	  }
1947 	  // It fits but it's hyphenated.
1948 	  if (!best_bp_fits) {
1949 	    if (best_bp)
1950 	      delete best_bp;
1951 	    best_bp = bp;
1952 	    bp = bp->next;
1953 	    best_bp_fits = 1;
1954 	  }
1955 	  else {
1956 	    breakpoint *tem = bp;
1957 	    bp = bp->next;
1958 	    delete tem;
1959 	  }
1960 	}
1961       }
1962       else {
1963 	if (best_bp)
1964 	  delete best_bp;
1965 	best_bp = bp;
1966 	bp = bp->next;
1967       }
1968     }
1969     n = n->next;
1970   }
1971   if (best_bp) {
1972     if (!best_bp_fits)
1973       output_warning(WARN_BREAK, "can't break line");
1974     return best_bp;
1975   }
1976   return 0;
1977 }
1978 
hyphenate_line(int start_here)1979 void environment::hyphenate_line(int start_here)
1980 {
1981   assert(line != 0);
1982   hyphenation_type prev_type = line->get_hyphenation_type();
1983   node **startp;
1984   if (start_here)
1985     startp = &line;
1986   else
1987     for (startp = &line->next; *startp != 0; startp = &(*startp)->next) {
1988       hyphenation_type this_type = (*startp)->get_hyphenation_type();
1989       if (prev_type == HYPHEN_BOUNDARY && this_type == HYPHEN_MIDDLE)
1990 	break;
1991       prev_type = this_type;
1992     }
1993   if (*startp == 0)
1994     return;
1995   node *tem = *startp;
1996   do {
1997     tem = tem->next;
1998   } while (tem != 0 && tem->get_hyphenation_type() == HYPHEN_MIDDLE);
1999   int inhibit = (tem != 0 && tem->get_hyphenation_type() == HYPHEN_INHIBIT);
2000   node *end = tem;
2001   hyphen_list *sl = 0;
2002   tem = *startp;
2003   node *forward = 0;
2004   int i = 0;
2005   while (tem != end) {
2006     sl = tem->get_hyphen_list(sl, &i);
2007     node *tem1 = tem;
2008     tem = tem->next;
2009     tem1->next = forward;
2010     forward = tem1;
2011   }
2012   if (!inhibit) {
2013     // this is for characters like hyphen and emdash
2014     int prev_code = 0;
2015     for (hyphen_list *h = sl; h; h = h->next) {
2016       h->breakable = (prev_code != 0
2017 		      && h->next != 0
2018 		      && h->next->hyphenation_code != 0);
2019       prev_code = h->hyphenation_code;
2020     }
2021   }
2022   if (hyphenation_flags != 0
2023       && !inhibit
2024       // this may not be right if we have extra space on this line
2025       && !((hyphenation_flags & HYPHEN_LAST_LINE)
2026 	   && (curdiv->distance_to_next_trap()
2027 	       <= vertical_spacing + total_post_vertical_spacing()))
2028       && i >= 4)
2029     hyphenate(sl, hyphenation_flags);
2030   while (forward != 0) {
2031     node *tem1 = forward;
2032     forward = forward->next;
2033     tem1->next = 0;
2034     tem = tem1->add_self(tem, &sl);
2035   }
2036   *startp = tem;
2037 }
2038 
node_list_reverse(node * n)2039 static node *node_list_reverse(node *n)
2040 {
2041   node *res = 0;
2042   while (n) {
2043     node *tem = n;
2044     n = n->next;
2045     tem->next = res;
2046     res = tem;
2047   }
2048   return res;
2049 }
2050 
distribute_space(node * n,int nspaces,hunits desired_space,int force_reverse=0)2051 static void distribute_space(node *n, int nspaces, hunits desired_space,
2052 			     int force_reverse = 0)
2053 {
2054   static int reverse = 0;
2055   if (force_reverse || reverse)
2056     n = node_list_reverse(n);
2057   if (!force_reverse && nspaces > 0 && spread_limit >= 0
2058       && desired_space.to_units() > 0) {
2059     hunits em = curenv->get_size();
2060     double Ems = (double)desired_space.to_units() / nspaces
2061 		 / (em.is_zero() ? hresolution : em.to_units());
2062     if (Ems > spread_limit)
2063       output_warning(WARN_BREAK, "spreading %1m per space", Ems);
2064   }
2065   for (node *tem = n; tem; tem = tem->next)
2066     tem->spread_space(&nspaces, &desired_space);
2067   if (force_reverse || reverse)
2068     (void)node_list_reverse(n);
2069   if (!force_reverse)
2070     reverse = !reverse;
2071   assert(desired_space.is_zero() && nspaces == 0);
2072 }
2073 
possibly_break_line(int start_here,int forced)2074 void environment::possibly_break_line(int start_here, int forced)
2075 {
2076   int was_centered = center_lines > 0;
2077   if (!fill || current_tab || current_field || dummy)
2078     return;
2079   while (line != 0
2080 	 && (forced
2081 	     // When a macro follows a paragraph in fill mode, the
2082 	     // current line should not be empty.
2083 	     || (width_total - line->width()) > target_text_length)) {
2084     hyphenate_line(start_here);
2085     breakpoint *bp = choose_breakpoint();
2086     if (bp == 0)
2087       // we'll find one eventually
2088       return;
2089     node *pre, *post;
2090     node **ndp = &line;
2091     while (*ndp != bp->nd)
2092       ndp = &(*ndp)->next;
2093     bp->nd->split(bp->index, &pre, &post);
2094     *ndp = post;
2095     hunits extra_space_width = H0;
2096     switch(adjust_mode) {
2097     case ADJUST_BOTH:
2098       if (bp->nspaces != 0)
2099 	extra_space_width = target_text_length - bp->width;
2100       else if (bp->width > 0 && target_text_length > 0
2101 	       && target_text_length > bp->width)
2102 	output_warning(WARN_BREAK, "cannot adjust line");
2103       break;
2104     case ADJUST_CENTER:
2105       saved_indent += (target_text_length - bp->width)/2;
2106       was_centered = 1;
2107       break;
2108     case ADJUST_RIGHT:
2109       saved_indent += target_text_length - bp->width;
2110       break;
2111     }
2112     distribute_space(pre, bp->nspaces, extra_space_width);
2113     hunits output_width = bp->width + extra_space_width;
2114     input_line_start -= output_width;
2115     if (bp->hyphenated)
2116       hyphen_line_count++;
2117     else
2118       hyphen_line_count = 0;
2119     delete bp;
2120     space_total = 0;
2121     width_total = 0;
2122     node *first_non_discardable = 0;
2123     node *tem;
2124     for (tem = line; tem != 0; tem = tem->next)
2125       if (!tem->discardable())
2126 	first_non_discardable = tem;
2127     node *to_be_discarded;
2128     if (first_non_discardable) {
2129       to_be_discarded = first_non_discardable->next;
2130       first_non_discardable->next = 0;
2131       for (tem = line; tem != 0; tem = tem->next) {
2132 	width_total += tem->width();
2133 	space_total += tem->nspaces();
2134       }
2135       discarding = 0;
2136     }
2137     else {
2138       discarding = 1;
2139       to_be_discarded = line;
2140       line = 0;
2141     }
2142     // Do output_line() here so that line will be 0 iff the
2143     // the environment will be empty.
2144     output_line(pre, output_width, was_centered);
2145     while (to_be_discarded != 0) {
2146       tem = to_be_discarded;
2147       to_be_discarded = to_be_discarded->next;
2148       input_line_start -= tem->width();
2149       delete tem;
2150     }
2151     if (line != 0) {
2152       if (have_temporary_indent) {
2153 	saved_indent = temporary_indent;
2154 	have_temporary_indent = 0;
2155       }
2156       else
2157 	saved_indent = indent;
2158       target_text_length = line_length - saved_indent;
2159     }
2160   }
2161 }
2162 
2163 /*
2164 Do the break at the end of input after the end macro (if any).
2165 
2166 Unix troff behaves as follows:  if the last line is
2167 
2168 foo bar\c
2169 
2170 it will output foo on the current page, and bar on the next page;
2171 if the last line is
2172 
2173 foo\c
2174 
2175 or
2176 
2177 foo bar
2178 
2179 everything will be output on the current page.  This behaviour must be
2180 considered a bug.
2181 
2182 The problem is that some macro packages rely on this.  For example,
2183 the ATK macros have an end macro that emits \c if it needs to print a
2184 table of contents but doesn't do a 'bp in the end macro; instead the
2185 'bp is done in the bottom of page trap.  This works with Unix troff,
2186 provided that the current environment is not empty at the end of the
2187 input file.
2188 
2189 The following will make macro packages that do that sort of thing work
2190 even if the current environment is empty at the end of the input file.
2191 If the last input line used \c and this line occurred in the end macro,
2192 then we'll force everything out on the current page, but we'll make
2193 sure that the environment isn't empty so that we won't exit at the
2194 bottom of this page.
2195 */
2196 
final_break()2197 void environment::final_break()
2198 {
2199   if (prev_line_interrupted == 2) {
2200     do_break();
2201     add_node(new transparent_dummy_node);
2202   }
2203   else
2204     do_break();
2205 }
2206 
make_tag(const char * nm,int i)2207 node *environment::make_tag(const char *nm, int i)
2208 {
2209   if (is_html) {
2210     /*
2211      * need to emit tag for post-grohtml
2212      * but we check to see whether we can emit specials
2213      */
2214     if (curdiv == topdiv && topdiv->before_first_page)
2215       topdiv->begin_page();
2216     macro *m = new macro;
2217     m->append_str("devtag:");
2218     for (const char *p = nm; *p; p++)
2219       if (!invalid_input_char((unsigned char)*p))
2220 	m->append(*p);
2221     m->append(' ');
2222     m->append_int(i);
2223     return new special_node(*m);
2224   }
2225   return 0;
2226 }
2227 
dump_troff_state()2228 void environment::dump_troff_state()
2229 {
2230 #define SPACES "                                            "
2231   fprintf(stderr, SPACES "register `in' = %d\n", curenv->indent.to_units());
2232   if (curenv->have_temporary_indent)
2233     fprintf(stderr, SPACES "register `ti' = %d\n",
2234 	    curenv->temporary_indent.to_units());
2235   fprintf(stderr, SPACES "centered lines `ce' = %d\n", curenv->center_lines);
2236   fprintf(stderr, SPACES "register `ll' = %d\n",
2237 	  curenv->line_length.to_units());
2238   fprintf(stderr, SPACES "fill `fi=1/nf=0' = %d\n", curenv->fill);
2239   fprintf(stderr, SPACES "page offset `po' = %d\n",
2240 	  topdiv->get_page_offset().to_units());
2241   fprintf(stderr, SPACES "seen_break = %d\n", curenv->seen_break);
2242   fprintf(stderr, SPACES "seen_space = %d\n", curenv->seen_space);
2243   fflush(stderr);
2244 #undef SPACES
2245 }
2246 
construct_state(int only_eol)2247 statem *environment::construct_state(int only_eol)
2248 {
2249   if (is_html) {
2250     statem *s = new statem();
2251     if (!only_eol) {
2252       s->add_tag(MTSM_IN, indent);
2253       s->add_tag(MTSM_LL, line_length);
2254       s->add_tag(MTSM_PO, topdiv->get_page_offset().to_units());
2255       s->add_tag(MTSM_RJ, right_justify_lines);
2256       if (have_temporary_indent)
2257 	s->add_tag(MTSM_TI, temporary_indent);
2258       s->add_tag_ta();
2259       if (seen_break)
2260 	s->add_tag(MTSM_BR);
2261       if (seen_space != 0)
2262 	s->add_tag(MTSM_SP, seen_space);
2263       seen_break = 0;
2264       seen_space = 0;
2265     }
2266     if (seen_eol) {
2267       s->add_tag(MTSM_EOL);
2268       s->add_tag(MTSM_CE, center_lines);
2269     }
2270     seen_eol = 0;
2271     return s;
2272   }
2273   else
2274     return NULL;
2275 }
2276 
construct_format_state(node * n,int was_centered,int filling)2277 void environment::construct_format_state(node *n, int was_centered,
2278 					 int filling)
2279 {
2280   if (is_html) {
2281     // find first glyph node which has a state.
2282     while (n != 0 && n->state == 0)
2283       n = n->next;
2284     if (n == 0 || (n->state == 0))
2285       return;
2286     if (seen_space != 0)
2287       n->state->add_tag(MTSM_SP, seen_space);
2288     if (seen_eol && topdiv == curdiv)
2289       n->state->add_tag(MTSM_EOL);
2290     seen_space = 0;
2291     seen_eol = 0;
2292     if (was_centered)
2293       n->state->add_tag(MTSM_CE, center_lines+1);
2294     else
2295       n->state->add_tag_if_unknown(MTSM_CE, 0);
2296     n->state->add_tag_if_unknown(MTSM_FI, filling);
2297     n = n->next;
2298     while (n != 0) {
2299       if (n->state != 0) {
2300 	n->state->sub_tag_ce();
2301 	n->state->add_tag_if_unknown(MTSM_FI, filling);
2302       }
2303       n = n->next;
2304     }
2305   }
2306 }
2307 
construct_new_line_state(node * n)2308 void environment::construct_new_line_state(node *n)
2309 {
2310   if (is_html) {
2311     // find first glyph node which has a state.
2312     while (n != 0 && n->state == 0)
2313       n = n->next;
2314     if (n == 0 || n->state == 0)
2315       return;
2316     if (seen_space != 0)
2317       n->state->add_tag(MTSM_SP, seen_space);
2318     if (seen_eol && topdiv == curdiv)
2319       n->state->add_tag(MTSM_EOL);
2320     seen_space = 0;
2321     seen_eol = 0;
2322   }
2323 }
2324 
2325 extern int global_diverted_space;
2326 
do_break(int do_spread)2327 void environment::do_break(int do_spread)
2328 {
2329   int was_centered = 0;
2330   if (curdiv == topdiv && topdiv->before_first_page) {
2331     topdiv->begin_page();
2332     return;
2333   }
2334   if (current_tab)
2335     wrap_up_tab();
2336   if (line) {
2337     // this is so that hyphenation works
2338     line = new space_node(H0, get_fill_color(), line);
2339     space_total++;
2340     possibly_break_line(0, do_spread);
2341   }
2342   while (line != 0 && line->discardable()) {
2343     width_total -= line->width();
2344     space_total -= line->nspaces();
2345     node *tem = line;
2346     line = line->next;
2347     delete tem;
2348   }
2349   discarding = 0;
2350   input_line_start = H0;
2351   if (line != 0) {
2352     if (fill) {
2353       switch (adjust_mode) {
2354       case ADJUST_CENTER:
2355 	saved_indent += (target_text_length - width_total)/2;
2356 	was_centered = 1;
2357 	break;
2358       case ADJUST_RIGHT:
2359 	saved_indent += target_text_length - width_total;
2360 	break;
2361       }
2362     }
2363     node *tem = line;
2364     line = 0;
2365     output_line(tem, width_total, was_centered);
2366     hyphen_line_count = 0;
2367   }
2368   prev_line_interrupted = 0;
2369 #ifdef WIDOW_CONTROL
2370   mark_last_line();
2371   output_pending_lines();
2372 #endif /* WIDOW_CONTROL */
2373   if (!global_diverted_space) {
2374     curdiv->modified_tag.incl(MTSM_BR);
2375     seen_break = 1;
2376   }
2377 }
2378 
is_empty()2379 int environment::is_empty()
2380 {
2381   return !current_tab && line == 0 && pending_lines == 0;
2382 }
2383 
do_break_request(int spread)2384 void do_break_request(int spread)
2385 {
2386   while (!tok.newline() && !tok.eof())
2387     tok.next();
2388   if (break_flag)
2389     curenv->do_break(spread);
2390   tok.next();
2391 }
2392 
break_request()2393 void break_request()
2394 {
2395   do_break_request(0);
2396 }
2397 
break_spread_request()2398 void break_spread_request()
2399 {
2400   do_break_request(1);
2401 }
2402 
title()2403 void title()
2404 {
2405   if (curdiv == topdiv && topdiv->before_first_page) {
2406     handle_initial_title();
2407     return;
2408   }
2409   node *part[3];
2410   hunits part_width[3];
2411   part[0] = part[1] = part[2] = 0;
2412   environment env(curenv);
2413   environment *oldenv = curenv;
2414   curenv = &env;
2415   read_title_parts(part, part_width);
2416   curenv = oldenv;
2417   curenv->size = env.size;
2418   curenv->prev_size = env.prev_size;
2419   curenv->requested_size = env.requested_size;
2420   curenv->prev_requested_size = env.prev_requested_size;
2421   curenv->char_height = env.char_height;
2422   curenv->char_slant = env.char_slant;
2423   curenv->fontno = env.fontno;
2424   curenv->prev_fontno = env.prev_fontno;
2425   curenv->glyph_color = env.glyph_color;
2426   curenv->prev_glyph_color = env.prev_glyph_color;
2427   curenv->fill_color = env.fill_color;
2428   curenv->prev_fill_color = env.prev_fill_color;
2429   node *n = 0;
2430   node *p = part[2];
2431   while (p != 0) {
2432     node *tem = p;
2433     p = p->next;
2434     tem->next = n;
2435     n = tem;
2436   }
2437   hunits length_title(curenv->title_length);
2438   hunits f = length_title - part_width[1];
2439   hunits f2 = f/2;
2440   n = new hmotion_node(f2 - part_width[2], curenv->get_fill_color(), n);
2441   p = part[1];
2442   while (p != 0) {
2443     node *tem = p;
2444     p = p->next;
2445     tem->next = n;
2446     n = tem;
2447   }
2448   n = new hmotion_node(f - f2 - part_width[0], curenv->get_fill_color(), n);
2449   p = part[0];
2450   while (p != 0) {
2451     node *tem = p;
2452     p = p->next;
2453     tem->next = n;
2454     n = tem;
2455   }
2456   curenv->output_title(n, !curenv->fill, curenv->vertical_spacing,
2457 		       curenv->total_post_vertical_spacing(), length_title);
2458   curenv->hyphen_line_count = 0;
2459   tok.next();
2460 }
2461 
adjust()2462 void adjust()
2463 {
2464   curenv->adjust_mode |= 1;
2465   if (has_arg()) {
2466     switch (tok.ch()) {
2467     case 'l':
2468       curenv->adjust_mode = ADJUST_LEFT;
2469       break;
2470     case 'r':
2471       curenv->adjust_mode = ADJUST_RIGHT;
2472       break;
2473     case 'c':
2474       curenv->adjust_mode = ADJUST_CENTER;
2475       break;
2476     case 'b':
2477     case 'n':
2478       curenv->adjust_mode = ADJUST_BOTH;
2479       break;
2480     default:
2481       int n;
2482       if (get_integer(&n)) {
2483 	if (n < 0)
2484 	  warning(WARN_RANGE, "negative adjustment mode");
2485 	else if (n > 5) {
2486 	  curenv->adjust_mode = 5;
2487 	  warning(WARN_RANGE, "adjustment mode `%1' out of range", n);
2488 	}
2489 	else
2490 	  curenv->adjust_mode = n;
2491       }
2492     }
2493   }
2494   skip_line();
2495 }
2496 
no_adjust()2497 void no_adjust()
2498 {
2499   curenv->adjust_mode &= ~1;
2500   skip_line();
2501 }
2502 
do_input_trap(int continued)2503 void do_input_trap(int continued)
2504 {
2505   curenv->input_trap_count = 0;
2506   if (continued)
2507     curenv->continued_input_trap = 1;
2508   int n;
2509   if (has_arg() && get_integer(&n)) {
2510     if (n <= 0)
2511       warning(WARN_RANGE,
2512 	      "number of lines for input trap must be greater than zero");
2513     else {
2514       symbol s = get_name(1);
2515       if (!s.is_null()) {
2516 	curenv->input_trap_count = n;
2517 	curenv->input_trap = s;
2518       }
2519     }
2520   }
2521   skip_line();
2522 }
2523 
input_trap()2524 void input_trap()
2525 {
2526   do_input_trap(0);
2527 }
2528 
input_trap_continued()2529 void input_trap_continued()
2530 {
2531   do_input_trap(1);
2532 }
2533 
2534 /* tabs */
2535 
2536 // must not be R or C or L or a legitimate part of a number expression
2537 const char TAB_REPEAT_CHAR = 'T';
2538 
2539 struct tab {
2540   tab *next;
2541   hunits pos;
2542   tab_type type;
2543   tab(hunits, tab_type);
2544   enum { BLOCK = 1024 };
2545   static tab *free_list;
2546   void *operator new(size_t);
2547   void operator delete(void *);
2548 };
2549 
2550 tab *tab::free_list = 0;
2551 
operator new(size_t n)2552 void *tab::operator new(size_t n)
2553 {
2554   assert(n == sizeof(tab));
2555   if (!free_list) {
2556     free_list = (tab *)new char[sizeof(tab)*BLOCK];
2557     for (int i = 0; i < BLOCK - 1; i++)
2558       free_list[i].next = free_list + i + 1;
2559     free_list[BLOCK-1].next = 0;
2560   }
2561   tab *p = free_list;
2562   free_list = (tab *)(free_list->next);
2563   p->next = 0;
2564   return p;
2565 }
2566 
2567 #ifdef __GNUG__
2568 /* cfront can't cope with this. */
2569 inline
2570 #endif
operator delete(void * p)2571 void tab::operator delete(void *p)
2572 {
2573   if (p) {
2574     ((tab *)p)->next = free_list;
2575     free_list = (tab *)p;
2576   }
2577 }
2578 
tab(hunits x,tab_type t)2579 tab::tab(hunits x, tab_type t) : next(0), pos(x), type(t)
2580 {
2581 }
2582 
tab_stops(hunits distance,tab_type type)2583 tab_stops::tab_stops(hunits distance, tab_type type)
2584 : initial_list(0)
2585 {
2586   repeated_list = new tab(distance, type);
2587 }
2588 
~tab_stops()2589 tab_stops::~tab_stops()
2590 {
2591   clear();
2592 }
2593 
distance_to_next_tab(hunits curpos,hunits * distance)2594 tab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance)
2595 {
2596   hunits nextpos;
2597 
2598   return distance_to_next_tab(curpos, distance, &nextpos);
2599 }
2600 
distance_to_next_tab(hunits curpos,hunits * distance,hunits * nextpos)2601 tab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance,
2602 					 hunits *nextpos)
2603 {
2604   hunits lastpos = 0;
2605   tab *tem;
2606   for (tem = initial_list; tem && tem->pos <= curpos; tem = tem->next)
2607     lastpos = tem->pos;
2608   if (tem) {
2609     *distance = tem->pos - curpos;
2610     *nextpos  = tem->pos;
2611     return tem->type;
2612   }
2613   if (repeated_list == 0)
2614     return TAB_NONE;
2615   hunits base = lastpos;
2616   for (;;) {
2617     for (tem = repeated_list; tem && tem->pos + base <= curpos; tem = tem->next)
2618       lastpos = tem->pos;
2619     if (tem) {
2620       *distance = tem->pos + base - curpos;
2621       *nextpos  = tem->pos + base;
2622       return tem->type;
2623     }
2624     assert(lastpos > 0);
2625     base += lastpos;
2626   }
2627   return TAB_NONE;
2628 }
2629 
to_string()2630 const char *tab_stops::to_string()
2631 {
2632   static char *buf = 0;
2633   static int buf_size = 0;
2634   // figure out a maximum on the amount of space we can need
2635   int count = 0;
2636   tab *p;
2637   for (p = initial_list; p; p = p->next)
2638     ++count;
2639   for (p = repeated_list; p; p = p->next)
2640     ++count;
2641   // (10 for digits + 1 for u + 1 for 'C' or 'R') + 2 for ' &' + 1 for '\0'
2642   int need = count*12 + 3;
2643   if (buf == 0 || need > buf_size) {
2644     if (buf)
2645       a_delete buf;
2646     buf_size = need;
2647     buf = new char[buf_size];
2648   }
2649   char *ptr = buf;
2650   for (p = initial_list; p; p = p->next) {
2651     strcpy(ptr, i_to_a(p->pos.to_units()));
2652     ptr = strchr(ptr, '\0');
2653     *ptr++ = 'u';
2654     *ptr = '\0';
2655     switch (p->type) {
2656     case TAB_LEFT:
2657       break;
2658     case TAB_RIGHT:
2659       *ptr++ = 'R';
2660       break;
2661     case TAB_CENTER:
2662       *ptr++ = 'C';
2663       break;
2664     case TAB_NONE:
2665     default:
2666       assert(0);
2667     }
2668   }
2669   if (repeated_list)
2670     *ptr++ = TAB_REPEAT_CHAR;
2671   for (p = repeated_list; p; p = p->next) {
2672     strcpy(ptr, i_to_a(p->pos.to_units()));
2673     ptr = strchr(ptr, '\0');
2674     *ptr++ = 'u';
2675     *ptr = '\0';
2676     switch (p->type) {
2677     case TAB_LEFT:
2678       break;
2679     case TAB_RIGHT:
2680       *ptr++ = 'R';
2681       break;
2682     case TAB_CENTER:
2683       *ptr++ = 'C';
2684       break;
2685     case TAB_NONE:
2686     default:
2687       assert(0);
2688     }
2689   }
2690   *ptr++ = '\0';
2691   return buf;
2692 }
2693 
tab_stops()2694 tab_stops::tab_stops() : initial_list(0), repeated_list(0)
2695 {
2696 }
2697 
tab_stops(const tab_stops & ts)2698 tab_stops::tab_stops(const tab_stops &ts)
2699 : initial_list(0), repeated_list(0)
2700 {
2701   tab **p = &initial_list;
2702   tab *t = ts.initial_list;
2703   while (t) {
2704     *p = new tab(t->pos, t->type);
2705     t = t->next;
2706     p = &(*p)->next;
2707   }
2708   p = &repeated_list;
2709   t = ts.repeated_list;
2710   while (t) {
2711     *p = new tab(t->pos, t->type);
2712     t = t->next;
2713     p = &(*p)->next;
2714   }
2715 }
2716 
clear()2717 void tab_stops::clear()
2718 {
2719   while (initial_list) {
2720     tab *tem = initial_list;
2721     initial_list = initial_list->next;
2722     delete tem;
2723   }
2724   while (repeated_list) {
2725     tab *tem = repeated_list;
2726     repeated_list = repeated_list->next;
2727     delete tem;
2728   }
2729 }
2730 
add_tab(hunits pos,tab_type type,int repeated)2731 void tab_stops::add_tab(hunits pos, tab_type type, int repeated)
2732 {
2733   tab **p;
2734   for (p = repeated ? &repeated_list : &initial_list; *p; p = &(*p)->next)
2735     ;
2736   *p = new tab(pos, type);
2737 }
2738 
2739 
operator =(const tab_stops & ts)2740 void tab_stops::operator=(const tab_stops &ts)
2741 {
2742   clear();
2743   tab **p = &initial_list;
2744   tab *t = ts.initial_list;
2745   while (t) {
2746     *p = new tab(t->pos, t->type);
2747     t = t->next;
2748     p = &(*p)->next;
2749   }
2750   p = &repeated_list;
2751   t = ts.repeated_list;
2752   while (t) {
2753     *p = new tab(t->pos, t->type);
2754     t = t->next;
2755     p = &(*p)->next;
2756   }
2757 }
2758 
set_tabs()2759 void set_tabs()
2760 {
2761   hunits pos;
2762   hunits prev_pos = 0;
2763   int first = 1;
2764   int repeated = 0;
2765   tab_stops tabs;
2766   while (has_arg()) {
2767     if (tok.ch() == TAB_REPEAT_CHAR) {
2768       tok.next();
2769       repeated = 1;
2770       prev_pos = 0;
2771     }
2772     if (!get_hunits(&pos, 'm', prev_pos))
2773       break;
2774     tab_type type = TAB_LEFT;
2775     if (tok.ch() == 'C') {
2776       tok.next();
2777       type = TAB_CENTER;
2778     }
2779     else if (tok.ch() == 'R') {
2780       tok.next();
2781       type = TAB_RIGHT;
2782     }
2783     else if (tok.ch() == 'L') {
2784       tok.next();
2785     }
2786     if (pos <= prev_pos && !first)
2787       warning(WARN_RANGE,
2788 	      "positions of tab stops must be strictly increasing");
2789     else {
2790       tabs.add_tab(pos, type, repeated);
2791       prev_pos = pos;
2792       first = 0;
2793     }
2794   }
2795   curenv->tabs = tabs;
2796   curdiv->modified_tag.incl(MTSM_TA);
2797   skip_line();
2798 }
2799 
get_tabs()2800 const char *environment::get_tabs()
2801 {
2802   return tabs.to_string();
2803 }
2804 
distance_to_next_tab(hunits * distance)2805 tab_type environment::distance_to_next_tab(hunits *distance)
2806 {
2807   return line_tabs
2808     ? curenv->tabs.distance_to_next_tab(get_text_length(), distance)
2809     : curenv->tabs.distance_to_next_tab(get_input_line_position(), distance);
2810 }
2811 
distance_to_next_tab(hunits * distance,hunits * leftpos)2812 tab_type environment::distance_to_next_tab(hunits *distance, hunits *leftpos)
2813 {
2814   return line_tabs
2815     ? curenv->tabs.distance_to_next_tab(get_text_length(), distance, leftpos)
2816     : curenv->tabs.distance_to_next_tab(get_input_line_position(), distance,
2817 					leftpos);
2818 }
2819 
field_characters()2820 void field_characters()
2821 {
2822   field_delimiter_char = get_optional_char();
2823   if (field_delimiter_char)
2824     padding_indicator_char = get_optional_char();
2825   else
2826     padding_indicator_char = 0;
2827   skip_line();
2828 }
2829 
line_tabs_request()2830 void line_tabs_request()
2831 {
2832   int n;
2833   if (has_arg() && get_integer(&n))
2834     curenv->line_tabs = n != 0;
2835   else
2836     curenv->line_tabs = 1;
2837   skip_line();
2838 }
2839 
get_line_tabs()2840 int environment::get_line_tabs()
2841 {
2842   return line_tabs;
2843 }
2844 
wrap_up_tab()2845 void environment::wrap_up_tab()
2846 {
2847   if (!current_tab)
2848     return;
2849   if (line == 0)
2850     start_line();
2851   hunits tab_amount;
2852   switch (current_tab) {
2853   case TAB_RIGHT:
2854     tab_amount = tab_distance - tab_width;
2855     line = make_tab_node(tab_amount, line);
2856     break;
2857   case TAB_CENTER:
2858     tab_amount = tab_distance - tab_width/2;
2859     line = make_tab_node(tab_amount, line);
2860     break;
2861   case TAB_NONE:
2862   case TAB_LEFT:
2863   default:
2864     assert(0);
2865   }
2866   width_total += tab_amount;
2867   width_total += tab_width;
2868   if (current_field) {
2869     if (tab_precedes_field) {
2870       pre_field_width += tab_amount;
2871       tab_precedes_field = 0;
2872     }
2873     field_distance -= tab_amount;
2874     field_spaces += tab_field_spaces;
2875   }
2876   if (tab_contents != 0) {
2877     node *tem;
2878     for (tem = tab_contents; tem->next != 0; tem = tem->next)
2879       ;
2880     tem->next = line;
2881     line = tab_contents;
2882   }
2883   tab_field_spaces = 0;
2884   tab_contents = 0;
2885   tab_width = H0;
2886   tab_distance = H0;
2887   current_tab = TAB_NONE;
2888 }
2889 
make_tab_node(hunits d,node * next)2890 node *environment::make_tab_node(hunits d, node *next)
2891 {
2892   if (leader_node != 0 && d < 0) {
2893     error("motion generated by leader cannot be negative");
2894     delete leader_node;
2895     leader_node = 0;
2896   }
2897   if (!leader_node)
2898     return new hmotion_node(d, 1, 0, get_fill_color(), next);
2899   node *n = new hline_node(d, leader_node, next);
2900   leader_node = 0;
2901   return n;
2902 }
2903 
handle_tab(int is_leader)2904 void environment::handle_tab(int is_leader)
2905 {
2906   hunits d;
2907   hunits absolute;
2908   if (current_tab)
2909     wrap_up_tab();
2910   charinfo *ci = is_leader ? leader_char : tab_char;
2911   delete leader_node;
2912   leader_node = ci ? make_char_node(ci) : 0;
2913   tab_type t = distance_to_next_tab(&d, &absolute);
2914   switch (t) {
2915   case TAB_NONE:
2916     return;
2917   case TAB_LEFT:
2918     add_node(make_tag("tab L", absolute.to_units()));
2919     add_node(make_tab_node(d));
2920     return;
2921   case TAB_RIGHT:
2922     add_node(make_tag("tab R", absolute.to_units()));
2923     break;
2924   case TAB_CENTER:
2925     add_node(make_tag("tab C", absolute.to_units()));
2926     break;
2927   default:
2928     assert(0);
2929   }
2930   tab_width = 0;
2931   tab_distance = d;
2932   tab_contents = 0;
2933   current_tab = t;
2934   tab_field_spaces = 0;
2935 }
2936 
start_field()2937 void environment::start_field()
2938 {
2939   assert(!current_field);
2940   hunits d;
2941   if (distance_to_next_tab(&d) != TAB_NONE) {
2942     pre_field_width = get_text_length();
2943     field_distance = d;
2944     current_field = 1;
2945     field_spaces = 0;
2946     tab_field_spaces = 0;
2947     for (node *p = line; p; p = p->next)
2948       if (p->nspaces()) {
2949 	p->freeze_space();
2950 	space_total--;
2951       }
2952     tab_precedes_field = current_tab != TAB_NONE;
2953   }
2954   else
2955     error("zero field width");
2956 }
2957 
wrap_up_field()2958 void environment::wrap_up_field()
2959 {
2960   if (!current_tab && field_spaces == 0)
2961     add_padding();
2962   hunits padding = field_distance - (get_text_length() - pre_field_width);
2963   if (current_tab && tab_field_spaces != 0) {
2964     hunits tab_padding = scale(padding,
2965 			       tab_field_spaces,
2966 			       field_spaces + tab_field_spaces);
2967     padding -= tab_padding;
2968     distribute_space(tab_contents, tab_field_spaces, tab_padding, 1);
2969     tab_field_spaces = 0;
2970     tab_width += tab_padding;
2971   }
2972   if (field_spaces != 0) {
2973     distribute_space(line, field_spaces, padding, 1);
2974     width_total += padding;
2975     if (current_tab) {
2976       // the start of the tab has been moved to the right by padding, so
2977       tab_distance -= padding;
2978       if (tab_distance <= H0) {
2979 	// use the next tab stop instead
2980 	current_tab = tabs.distance_to_next_tab(get_input_line_position()
2981 						- tab_width,
2982 						&tab_distance);
2983 	if (current_tab == TAB_NONE || current_tab == TAB_LEFT) {
2984 	  width_total += tab_width;
2985 	  if (current_tab == TAB_LEFT) {
2986 	    line = make_tab_node(tab_distance, line);
2987 	    width_total += tab_distance;
2988 	    current_tab = TAB_NONE;
2989 	  }
2990 	  if (tab_contents != 0) {
2991 	    node *tem;
2992 	    for (tem = tab_contents; tem->next != 0; tem = tem->next)
2993 	      ;
2994 	    tem->next = line;
2995 	    line = tab_contents;
2996 	    tab_contents = 0;
2997 	  }
2998 	  tab_width = H0;
2999 	  tab_distance = H0;
3000 	}
3001       }
3002     }
3003   }
3004   current_field = 0;
3005 }
3006 
add_padding()3007 void environment::add_padding()
3008 {
3009   if (current_tab) {
3010     tab_contents = new space_node(H0, get_fill_color(), tab_contents);
3011     tab_field_spaces++;
3012   }
3013   else {
3014     if (line == 0)
3015       start_line();
3016     line = new space_node(H0, get_fill_color(), line);
3017     field_spaces++;
3018   }
3019 }
3020 
3021 typedef int (environment::*INT_FUNCP)();
3022 typedef vunits (environment::*VUNITS_FUNCP)();
3023 typedef hunits (environment::*HUNITS_FUNCP)();
3024 typedef const char *(environment::*STRING_FUNCP)();
3025 
3026 class int_env_reg : public reg {
3027   INT_FUNCP func;
3028  public:
3029   int_env_reg(INT_FUNCP);
3030   const char *get_string();
3031   int get_value(units *val);
3032 };
3033 
3034 class vunits_env_reg : public reg {
3035   VUNITS_FUNCP func;
3036  public:
3037   vunits_env_reg(VUNITS_FUNCP f);
3038   const char *get_string();
3039   int get_value(units *val);
3040 };
3041 
3042 
3043 class hunits_env_reg : public reg {
3044   HUNITS_FUNCP func;
3045  public:
3046   hunits_env_reg(HUNITS_FUNCP f);
3047   const char *get_string();
3048   int get_value(units *val);
3049 };
3050 
3051 class string_env_reg : public reg {
3052   STRING_FUNCP func;
3053 public:
3054   string_env_reg(STRING_FUNCP);
3055   const char *get_string();
3056 };
3057 
int_env_reg(INT_FUNCP f)3058 int_env_reg::int_env_reg(INT_FUNCP f) : func(f)
3059 {
3060 }
3061 
get_value(units * val)3062 int int_env_reg::get_value(units *val)
3063 {
3064   *val = (curenv->*func)();
3065   return 1;
3066 }
3067 
get_string()3068 const char *int_env_reg::get_string()
3069 {
3070   return i_to_a((curenv->*func)());
3071 }
3072 
vunits_env_reg(VUNITS_FUNCP f)3073 vunits_env_reg::vunits_env_reg(VUNITS_FUNCP f) : func(f)
3074 {
3075 }
3076 
get_value(units * val)3077 int vunits_env_reg::get_value(units *val)
3078 {
3079   *val = (curenv->*func)().to_units();
3080   return 1;
3081 }
3082 
get_string()3083 const char *vunits_env_reg::get_string()
3084 {
3085   return i_to_a((curenv->*func)().to_units());
3086 }
3087 
hunits_env_reg(HUNITS_FUNCP f)3088 hunits_env_reg::hunits_env_reg(HUNITS_FUNCP f) : func(f)
3089 {
3090 }
3091 
get_value(units * val)3092 int hunits_env_reg::get_value(units *val)
3093 {
3094   *val = (curenv->*func)().to_units();
3095   return 1;
3096 }
3097 
get_string()3098 const char *hunits_env_reg::get_string()
3099 {
3100   return i_to_a((curenv->*func)().to_units());
3101 }
3102 
string_env_reg(STRING_FUNCP f)3103 string_env_reg::string_env_reg(STRING_FUNCP f) : func(f)
3104 {
3105 }
3106 
get_string()3107 const char *string_env_reg::get_string()
3108 {
3109   return (curenv->*func)();
3110 }
3111 
3112 class horizontal_place_reg : public general_reg {
3113 public:
3114   horizontal_place_reg();
3115   int get_value(units *);
3116   void set_value(units);
3117 };
3118 
horizontal_place_reg()3119 horizontal_place_reg::horizontal_place_reg()
3120 {
3121 }
3122 
get_value(units * res)3123 int horizontal_place_reg::get_value(units *res)
3124 {
3125   *res = curenv->get_input_line_position().to_units();
3126   return 1;
3127 }
3128 
set_value(units n)3129 void horizontal_place_reg::set_value(units n)
3130 {
3131   curenv->set_input_line_position(hunits(n));
3132 }
3133 
get_font_family_string()3134 const char *environment::get_font_family_string()
3135 {
3136   return family->nm.contents();
3137 }
3138 
get_glyph_color_string()3139 const char *environment::get_glyph_color_string()
3140 {
3141   return glyph_color->nm.contents();
3142 }
3143 
get_fill_color_string()3144 const char *environment::get_fill_color_string()
3145 {
3146   return fill_color->nm.contents();
3147 }
3148 
get_font_name_string()3149 const char *environment::get_font_name_string()
3150 {
3151   symbol f = get_font_name(fontno, this);
3152   return f.contents();
3153 }
3154 
get_style_name_string()3155 const char *environment::get_style_name_string()
3156 {
3157   symbol f = get_style_name(fontno);
3158   return f.contents();
3159 }
3160 
get_name_string()3161 const char *environment::get_name_string()
3162 {
3163   return name.contents();
3164 }
3165 
3166 // Convert a quantity in scaled points to ascii decimal fraction.
3167 
sptoa(int sp)3168 const char *sptoa(int sp)
3169 {
3170   assert(sp > 0);
3171   assert(sizescale > 0);
3172   if (sizescale == 1)
3173     return i_to_a(sp);
3174   if (sp % sizescale == 0)
3175     return i_to_a(sp/sizescale);
3176   // See if 1/sizescale is exactly representable as a decimal fraction,
3177   // ie its only prime factors are 2 and 5.
3178   int n = sizescale;
3179   int power2 = 0;
3180   while ((n & 1) == 0) {
3181     n >>= 1;
3182     power2++;
3183   }
3184   int power5 = 0;
3185   while ((n % 5) == 0) {
3186     n /= 5;
3187     power5++;
3188   }
3189   if (n == 1) {
3190     int decimal_point = power5 > power2 ? power5 : power2;
3191     if (decimal_point <= 10) {
3192       int factor = 1;
3193       int t;
3194       for (t = decimal_point - power2; --t >= 0;)
3195 	factor *= 2;
3196       for (t = decimal_point - power5; --t >= 0;)
3197 	factor *= 5;
3198       if (factor == 1 || sp <= INT_MAX/factor)
3199 	return if_to_a(sp*factor, decimal_point);
3200     }
3201   }
3202   double s = double(sp)/double(sizescale);
3203   double factor = 10.0;
3204   double val = s;
3205   int decimal_point = 0;
3206   do  {
3207     double v = ceil(s*factor);
3208     if (v > INT_MAX)
3209       break;
3210     val = v;
3211     factor *= 10.0;
3212   } while (++decimal_point < 10);
3213   return if_to_a(int(val), decimal_point);
3214 }
3215 
get_point_size_string()3216 const char *environment::get_point_size_string()
3217 {
3218   return sptoa(curenv->get_point_size());
3219 }
3220 
get_requested_point_size_string()3221 const char *environment::get_requested_point_size_string()
3222 {
3223   return sptoa(curenv->get_requested_point_size());
3224 }
3225 
3226 #define init_int_env_reg(name, func) \
3227   number_reg_dictionary.define(name, new int_env_reg(&environment::func))
3228 
3229 #define init_vunits_env_reg(name, func) \
3230   number_reg_dictionary.define(name, new vunits_env_reg(&environment::func))
3231 
3232 #define init_hunits_env_reg(name, func) \
3233   number_reg_dictionary.define(name, new hunits_env_reg(&environment::func))
3234 
3235 #define init_string_env_reg(name, func) \
3236   number_reg_dictionary.define(name, new string_env_reg(&environment::func))
3237 
init_env_requests()3238 void init_env_requests()
3239 {
3240   init_request("ad", adjust);
3241   init_request("br", break_request);
3242   init_request("brp", break_spread_request);
3243   init_request("c2", no_break_control_char);
3244   init_request("cc", control_char);
3245   init_request("ce", center);
3246   init_request("cu", continuous_underline);
3247   init_request("ev", environment_switch);
3248   init_request("evc", environment_copy);
3249   init_request("fam", family_change);
3250   init_request("fc", field_characters);
3251   init_request("fi", fill);
3252   init_request("fcolor", fill_color_change);
3253   init_request("ft", font_change);
3254   init_request("gcolor", glyph_color_change);
3255   init_request("hc", hyphen_char);
3256   init_request("hlm", hyphen_line_max_request);
3257   init_request("hy", hyphenate_request);
3258   init_request("hym", hyphenation_margin_request);
3259   init_request("hys", hyphenation_space_request);
3260   init_request("in", indent);
3261   init_request("it", input_trap);
3262   init_request("itc", input_trap_continued);
3263   init_request("lc", leader_character);
3264   init_request("linetabs", line_tabs_request);
3265   init_request("ll", line_length);
3266   init_request("ls", line_spacing);
3267   init_request("lt", title_length);
3268   init_request("mc", margin_character);
3269   init_request("na", no_adjust);
3270   init_request("nf", no_fill);
3271   init_request("nh", no_hyphenate);
3272   init_request("nm", number_lines);
3273   init_request("nn", no_number);
3274   init_request("ps", point_size);
3275   init_request("pvs", post_vertical_spacing);
3276   init_request("rj", right_justify);
3277   init_request("sizes", override_sizes);
3278   init_request("ss", space_size);
3279   init_request("ta", set_tabs);
3280   init_request("ti", temporary_indent);
3281   init_request("tc", tab_character);
3282   init_request("tl", title);
3283   init_request("ul", underline);
3284   init_request("vs", vertical_spacing);
3285 #ifdef WIDOW_CONTROL
3286   init_request("wdc", widow_control_request);
3287 #endif /* WIDOW_CONTROL */
3288   init_int_env_reg(".b", get_bold);
3289   init_vunits_env_reg(".cdp", get_prev_char_depth);
3290   init_int_env_reg(".ce", get_center_lines);
3291   init_vunits_env_reg(".cht", get_prev_char_height);
3292   init_hunits_env_reg(".csk", get_prev_char_skew);
3293   init_string_env_reg(".ev", get_name_string);
3294   init_int_env_reg(".f", get_font);
3295   init_string_env_reg(".fam", get_font_family_string);
3296   init_string_env_reg(".fn", get_font_name_string);
3297   init_int_env_reg(".height", get_char_height);
3298   init_int_env_reg(".hlc", get_hyphen_line_count);
3299   init_int_env_reg(".hlm", get_hyphen_line_max);
3300   init_int_env_reg(".hy", get_hyphenation_flags);
3301   init_hunits_env_reg(".hym", get_hyphenation_margin);
3302   init_hunits_env_reg(".hys", get_hyphenation_space);
3303   init_hunits_env_reg(".i", get_indent);
3304   init_hunits_env_reg(".in", get_saved_indent);
3305   init_int_env_reg(".int", get_prev_line_interrupted);
3306   init_int_env_reg(".linetabs", get_line_tabs);
3307   init_hunits_env_reg(".lt", get_title_length);
3308   init_int_env_reg(".j", get_adjust_mode);
3309   init_hunits_env_reg(".k", get_text_length);
3310   init_int_env_reg(".L", get_line_spacing);
3311   init_hunits_env_reg(".l", get_line_length);
3312   init_hunits_env_reg(".ll", get_saved_line_length);
3313   init_string_env_reg(".M", get_fill_color_string);
3314   init_string_env_reg(".m", get_glyph_color_string);
3315   init_hunits_env_reg(".n", get_prev_text_length);
3316   init_int_env_reg(".ps", get_point_size);
3317   init_int_env_reg(".psr", get_requested_point_size);
3318   init_vunits_env_reg(".pvs", get_post_vertical_spacing);
3319   init_int_env_reg(".rj", get_right_justify_lines);
3320   init_string_env_reg(".s", get_point_size_string);
3321   init_int_env_reg(".slant", get_char_slant);
3322   init_int_env_reg(".ss", get_space_size);
3323   init_int_env_reg(".sss", get_sentence_space_size);
3324   init_string_env_reg(".sr", get_requested_point_size_string);
3325   init_string_env_reg(".sty", get_style_name_string);
3326   init_string_env_reg(".tabs", get_tabs);
3327   init_int_env_reg(".u", get_fill);
3328   init_vunits_env_reg(".v", get_vertical_spacing);
3329   init_hunits_env_reg(".w", get_prev_char_width);
3330   number_reg_dictionary.define("ct", new variable_reg(&ct_reg_contents));
3331   number_reg_dictionary.define("hp", new horizontal_place_reg);
3332   number_reg_dictionary.define("ln", new variable_reg(&next_line_number));
3333   number_reg_dictionary.define("rsb", new variable_reg(&rsb_reg_contents));
3334   number_reg_dictionary.define("rst", new variable_reg(&rst_reg_contents));
3335   number_reg_dictionary.define("sb", new variable_reg(&sb_reg_contents));
3336   number_reg_dictionary.define("skw", new variable_reg(&skw_reg_contents));
3337   number_reg_dictionary.define("ssc", new variable_reg(&ssc_reg_contents));
3338   number_reg_dictionary.define("st", new variable_reg(&st_reg_contents));
3339 }
3340 
3341 // Hyphenation - TeX's hyphenation algorithm with a less fancy implementation.
3342 
3343 struct trie_node;
3344 
3345 class trie {
3346   trie_node *tp;
3347   virtual void do_match(int len, void *val) = 0;
3348   virtual void do_delete(void *) = 0;
3349   void delete_trie_node(trie_node *);
3350 public:
trie()3351   trie() : tp(0) {}
3352   virtual ~trie();		// virtual to shut up g++
3353   void insert(const char *, int, void *);
3354   // find calls do_match for each match it finds
3355   void find(const char *pat, int patlen);
3356   void clear();
3357 };
3358 
3359 class hyphen_trie : private trie {
3360   int *h;
3361   void do_match(int i, void *v);
3362   void do_delete(void *v);
3363   void insert_pattern(const char *pat, int patlen, int *num);
3364   void insert_hyphenation(dictionary *ex, const char *pat, int patlen);
3365   int hpf_getc(FILE *f);
3366 public:
hyphen_trie()3367   hyphen_trie() {}
~hyphen_trie()3368   ~hyphen_trie() {}
3369   void hyphenate(const char *word, int len, int *hyphens);
3370   void read_patterns_file(const char *name, int append, dictionary *ex);
3371 };
3372 
3373 struct hyphenation_language {
3374   symbol name;
3375   dictionary exceptions;
3376   hyphen_trie patterns;
hyphenation_languagehyphenation_language3377   hyphenation_language(symbol nm) : name(nm), exceptions(501) {}
~hyphenation_languagehyphenation_language3378   ~hyphenation_language() { }
3379 };
3380 
3381 dictionary language_dictionary(5);
3382 hyphenation_language *current_language = 0;
3383 
set_hyphenation_language()3384 static void set_hyphenation_language()
3385 {
3386   symbol nm = get_name(1);
3387   if (!nm.is_null()) {
3388     current_language = (hyphenation_language *)language_dictionary.lookup(nm);
3389     if (!current_language) {
3390       current_language = new hyphenation_language(nm);
3391       (void)language_dictionary.lookup(nm, (void *)current_language);
3392     }
3393   }
3394   skip_line();
3395 }
3396 
3397 const int WORD_MAX = 256;	// we use unsigned char for offsets in
3398 				// hyphenation exceptions
3399 
hyphen_word()3400 static void hyphen_word()
3401 {
3402   if (!current_language) {
3403     error("no current hyphenation language");
3404     skip_line();
3405     return;
3406   }
3407   char buf[WORD_MAX + 1];
3408   unsigned char pos[WORD_MAX + 2];
3409   for (;;) {
3410     tok.skip();
3411     if (tok.newline() || tok.eof())
3412       break;
3413     int i = 0;
3414     int npos = 0;
3415     while (i < WORD_MAX && !tok.space() && !tok.newline() && !tok.eof()) {
3416       charinfo *ci = tok.get_char(1);
3417       if (ci == 0) {
3418 	skip_line();
3419 	return;
3420       }
3421       tok.next();
3422       if (ci->get_ascii_code() == '-') {
3423 	if (i > 0 && (npos == 0 || pos[npos - 1] != i))
3424 	  pos[npos++] = i;
3425       }
3426       else {
3427 	unsigned char c = ci->get_hyphenation_code();
3428 	if (c == 0)
3429 	  break;
3430 	buf[i++] = c;
3431       }
3432     }
3433     if (i > 0) {
3434       pos[npos] = 0;
3435       buf[i] = 0;
3436       unsigned char *tem = new unsigned char[npos + 1];
3437       memcpy(tem, pos, npos + 1);
3438       tem = (unsigned char *)current_language->exceptions.lookup(symbol(buf),
3439 								 tem);
3440       if (tem)
3441 	a_delete tem;
3442     }
3443   }
3444   skip_line();
3445 }
3446 
3447 struct trie_node {
3448   char c;
3449   trie_node *down;
3450   trie_node *right;
3451   void *val;
3452   trie_node(char, trie_node *);
3453 };
3454 
trie_node(char ch,trie_node * p)3455 trie_node::trie_node(char ch, trie_node *p)
3456 : c(ch), down(0), right(p), val(0)
3457 {
3458 }
3459 
~trie()3460 trie::~trie()
3461 {
3462   clear();
3463 }
3464 
clear()3465 void trie::clear()
3466 {
3467   delete_trie_node(tp);
3468   tp = 0;
3469 }
3470 
3471 
delete_trie_node(trie_node * p)3472 void trie::delete_trie_node(trie_node *p)
3473 {
3474   if (p) {
3475     delete_trie_node(p->down);
3476     delete_trie_node(p->right);
3477     if (p->val)
3478       do_delete(p->val);
3479     delete p;
3480   }
3481 }
3482 
insert(const char * pat,int patlen,void * val)3483 void trie::insert(const char *pat, int patlen, void *val)
3484 {
3485   trie_node **p = &tp;
3486   assert(patlen > 0 && pat != 0);
3487   for (;;) {
3488     while (*p != 0 && (*p)->c < pat[0])
3489       p = &((*p)->right);
3490     if (*p == 0 || (*p)->c != pat[0])
3491       *p = new trie_node(pat[0], *p);
3492     if (--patlen == 0) {
3493       (*p)->val = val;
3494       break;
3495     }
3496     ++pat;
3497     p = &((*p)->down);
3498   }
3499 }
3500 
find(const char * pat,int patlen)3501 void trie::find(const char *pat, int patlen)
3502 {
3503   trie_node *p = tp;
3504   for (int i = 0; p != 0 && i < patlen; i++) {
3505     while (p != 0 && p->c < pat[i])
3506       p = p->right;
3507     if (p != 0 && p->c == pat[i]) {
3508       if (p->val != 0)
3509 	do_match(i+1, p->val);
3510       p = p->down;
3511     }
3512     else
3513       break;
3514   }
3515 }
3516 
3517 struct operation {
3518   operation *next;
3519   short distance;
3520   short num;
3521   operation(int, int, operation *);
3522 };
3523 
operation(int i,int j,operation * op)3524 operation::operation(int i, int j, operation *op)
3525 : next(op), distance(j), num(i)
3526 {
3527 }
3528 
insert_pattern(const char * pat,int patlen,int * num)3529 void hyphen_trie::insert_pattern(const char *pat, int patlen, int *num)
3530 {
3531   operation *op = 0;
3532   for (int i = 0; i < patlen+1; i++)
3533     if (num[i] != 0)
3534       op = new operation(num[i], patlen - i, op);
3535   insert(pat, patlen, op);
3536 }
3537 
insert_hyphenation(dictionary * ex,const char * pat,int patlen)3538 void hyphen_trie::insert_hyphenation(dictionary *ex, const char *pat,
3539 				     int patlen)
3540 {
3541   char buf[WORD_MAX + 1];
3542   unsigned char pos[WORD_MAX + 2];
3543   int i = 0, j = 0;
3544   int npos = 0;
3545   while (j < patlen) {
3546     unsigned char c = pat[j++];
3547     if (c == '-') {
3548       if (i > 0 && (npos == 0 || pos[npos - 1] != i))
3549 	pos[npos++] = i;
3550     }
3551     else
3552       buf[i++] = hpf_code_table[c];
3553   }
3554   if (i > 0) {
3555     pos[npos] = 0;
3556     buf[i] = 0;
3557     unsigned char *tem = new unsigned char[npos + 1];
3558     memcpy(tem, pos, npos + 1);
3559     tem = (unsigned char *)ex->lookup(symbol(buf), tem);
3560     if (tem)
3561       a_delete tem;
3562   }
3563 }
3564 
hyphenate(const char * word,int len,int * hyphens)3565 void hyphen_trie::hyphenate(const char *word, int len, int *hyphens)
3566 {
3567   int j;
3568   for (j = 0; j < len + 1; j++)
3569     hyphens[j] = 0;
3570   for (j = 0; j < len - 1; j++) {
3571     h = hyphens + j;
3572     find(word + j, len - j);
3573   }
3574 }
3575 
max(int m,int n)3576 inline int max(int m, int n)
3577 {
3578   return m > n ? m : n;
3579 }
3580 
do_match(int i,void * v)3581 void hyphen_trie::do_match(int i, void *v)
3582 {
3583   operation *op = (operation *)v;
3584   while (op != 0) {
3585     h[i - op->distance] = max(h[i - op->distance], op->num);
3586     op = op->next;
3587   }
3588 }
3589 
do_delete(void * v)3590 void hyphen_trie::do_delete(void *v)
3591 {
3592   operation *op = (operation *)v;
3593   while (op) {
3594     operation *tem = op;
3595     op = tem->next;
3596     delete tem;
3597   }
3598 }
3599 
3600 /* We use very simple rules to parse TeX's hyphenation patterns.
3601 
3602    . `%' starts a comment even if preceded by `\'.
3603 
3604    . No support for digraphs and like `\$'.
3605 
3606    . `^^xx' (`x' is 0-9 or a-f), and `^^x' (character code of `x' in the
3607      range 0-127) are recognized; other use of `^' causes an error.
3608 
3609    . No macro expansion.
3610 
3611    . We check for the expression `\patterns{...}' (possibly with
3612      whitespace before and after the braces).  Everything between the
3613      braces is taken as hyphenation patterns.  Consequently, `{' and `}'
3614      are not allowed in patterns.
3615 
3616    . Similarly, `\hyphenation{...}' gives a list of hyphenation
3617      exceptions.
3618 
3619    . `\endinput' is recognized also.
3620 
3621    . For backwards compatibility, if `\patterns' is missing, the
3622      whole file is treated as a list of hyphenation patterns (only
3623      recognizing `%' as the start of a comment.
3624 
3625 */
3626 
hpf_getc(FILE * f)3627 int hyphen_trie::hpf_getc(FILE *f)
3628 {
3629   int c = getc(f);
3630   int c1;
3631   int cc = 0;
3632   if (c != '^')
3633     return c;
3634   c = getc(f);
3635   if (c != '^')
3636     goto fail;
3637   c = getc(f);
3638   c1 = getc(f);
3639   if (((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))
3640       && ((c1 >= '0' && c1 <= '9') || (c1 >= 'a' && c1 <= 'f'))) {
3641     if (c >= '0' && c <= '9')
3642       c -= '0';
3643     else
3644       c = c - 'a' + 10;
3645     if (c1 >= '0' && c1 <= '9')
3646       c1 -= '0';
3647     else
3648       c1 = c1 - 'a' + 10;
3649     cc = c * 16 + c1;
3650   }
3651   else {
3652     ungetc(c1, f);
3653     if (c >= 0 && c <= 63)
3654       cc = c + 64;
3655     else if (c >= 64 && c <= 127)
3656       cc = c - 64;
3657     else
3658       goto fail;
3659   }
3660   return cc;
3661 fail:
3662   error("invalid ^, ^^x, or ^^xx character in hyphenation patterns file");
3663   return c;
3664 }
3665 
read_patterns_file(const char * name,int append,dictionary * ex)3666 void hyphen_trie::read_patterns_file(const char *name, int append,
3667 				     dictionary *ex)
3668 {
3669   if (!append)
3670     clear();
3671   char buf[WORD_MAX];
3672   for (int i = 0; i < WORD_MAX; i++)
3673     buf[i] = 0;
3674   int num[WORD_MAX+1];
3675   errno = 0;
3676   char *path = 0;
3677   FILE *fp = mac_path->open_file(name, &path);
3678   if (fp == 0) {
3679     error("can't find hyphenation patterns file `%1'", name);
3680     return;
3681   }
3682   int c = hpf_getc(fp);
3683   int have_patterns = 0;	// we've seen \patterns
3684   int final_pattern = 0;	// 1 if we have a trailing closing brace
3685   int have_hyphenation = 0;	// we've seen \hyphenation
3686   int final_hyphenation = 0;	// 1 if we have a trailing closing brace
3687   int have_keyword = 0;		// we've seen either \patterns or \hyphenation
3688   int traditional = 0;		// don't handle \patterns
3689   for (;;) {
3690     for (;;) {
3691       if (c == '%') {		// skip comments
3692 	do {
3693 	  c = getc(fp);
3694 	} while (c != EOF && c != '\n');
3695       }
3696       if (c == EOF || !csspace(c))
3697 	break;
3698       c = hpf_getc(fp);
3699     }
3700     if (c == EOF) {
3701       if (have_keyword || traditional)	// we are done
3702 	break;
3703       else {				// rescan file in `traditional' mode
3704 	rewind(fp);
3705 	traditional = 1;
3706 	c = hpf_getc(fp);
3707 	continue;
3708       }
3709     }
3710     int i = 0;
3711     num[0] = 0;
3712     if (!(c == '{' || c == '}')) {	// skip braces at line start
3713       do {				// scan patterns
3714 	if (csdigit(c))
3715 	  num[i] = c - '0';
3716 	else {
3717 	  buf[i++] = c;
3718 	  num[i] = 0;
3719 	}
3720 	c = hpf_getc(fp);
3721       } while (i < WORD_MAX && c != EOF && !csspace(c)
3722 	       && c != '%' && c != '{' && c != '}');
3723     }
3724     if (!traditional) {
3725       if (i >= 9 && !strncmp(buf + i - 9, "\\patterns", 9)) {
3726 	while (csspace(c))
3727 	  c = hpf_getc(fp);
3728 	if (c == '{') {
3729 	  if (have_patterns || have_hyphenation)
3730 	    error("\\patterns not allowed inside of %1 group",
3731 		  have_patterns ? "\\patterns" : "\\hyphenation");
3732 	  else {
3733 	    have_patterns = 1;
3734 	    have_keyword = 1;
3735 	  }
3736 	  c = hpf_getc(fp);
3737 	  continue;
3738 	}
3739       }
3740       else if (i >= 12 && !strncmp(buf + i - 12, "\\hyphenation", 12)) {
3741 	while (csspace(c))
3742 	  c = hpf_getc(fp);
3743 	if (c == '{') {
3744 	  if (have_patterns || have_hyphenation)
3745 	    error("\\hyphenation not allowed inside of %1 group",
3746 		  have_patterns ? "\\patterns" : "\\hyphenation");
3747 	  else {
3748 	    have_hyphenation = 1;
3749 	    have_keyword = 1;
3750 	  }
3751 	  c = hpf_getc(fp);
3752 	  continue;
3753 	}
3754       }
3755       else if (strstr(buf, "\\endinput")) {
3756 	if (have_patterns || have_hyphenation)
3757 	  error("found \\endinput inside of %1 group",
3758 		have_patterns ? "\\patterns" : "\\hyphenation");
3759 	break;
3760       }
3761       else if (c == '}') {
3762 	if (have_patterns) {
3763 	  have_patterns = 0;
3764 	  if (i > 0)
3765 	    final_pattern = 1;
3766 	}
3767 	else if (have_hyphenation) {
3768 	  have_hyphenation = 0;
3769 	  if (i > 0)
3770 	    final_hyphenation = 1;
3771 	}
3772 	c = hpf_getc(fp);
3773       }
3774       else if (c == '{') {
3775 	if (have_patterns || have_hyphenation)
3776 	  error("`{' not allowed within %1 group",
3777 		have_patterns ? "\\patterns" : "\\hyphenation");
3778 	c = hpf_getc(fp);		// skipped if not starting \patterns
3779 					// or \hyphenation
3780       }
3781     }
3782     else {
3783       if (c == '{' || c == '}')
3784 	c = hpf_getc(fp);
3785     }
3786     if (i > 0) {
3787       if (have_patterns || final_pattern || traditional) {
3788 	for (int j = 0; j < i; j++)
3789 	  buf[j] = hpf_code_table[(unsigned char)buf[j]];
3790 	insert_pattern(buf, i, num);
3791 	final_pattern = 0;
3792       }
3793       else if (have_hyphenation || final_hyphenation) {
3794 	insert_hyphenation(ex, buf, i);
3795 	final_hyphenation = 0;
3796       }
3797     }
3798   }
3799   fclose(fp);
3800   a_delete path;
3801   return;
3802 }
3803 
hyphenate(hyphen_list * h,unsigned flags)3804 void hyphenate(hyphen_list *h, unsigned flags)
3805 {
3806   if (!current_language)
3807     return;
3808   while (h) {
3809     while (h && h->hyphenation_code == 0)
3810       h = h->next;
3811     int len = 0;
3812     char hbuf[WORD_MAX+2];
3813     char *buf = hbuf + 1;
3814     hyphen_list *tem;
3815     for (tem = h; tem && len < WORD_MAX; tem = tem->next) {
3816       if (tem->hyphenation_code != 0)
3817 	buf[len++] = tem->hyphenation_code;
3818       else
3819 	break;
3820     }
3821     hyphen_list *nexth = tem;
3822     if (len > 2) {
3823       buf[len] = 0;
3824       unsigned char *pos
3825 	= (unsigned char *)current_language->exceptions.lookup(buf);
3826       if (pos != 0) {
3827 	int j = 0;
3828 	int i = 1;
3829 	for (tem = h; tem != 0; tem = tem->next, i++)
3830 	  if (pos[j] == i) {
3831 	    tem->hyphen = 1;
3832 	    j++;
3833 	  }
3834       }
3835       else {
3836 	hbuf[0] = hbuf[len+1] = '.';
3837 	int num[WORD_MAX+3];
3838 	current_language->patterns.hyphenate(hbuf, len+2, num);
3839 	int i;
3840 	num[2] = 0;
3841 	if (flags & 8)
3842 	  num[3] = 0;
3843 	if (flags & 4)
3844 	  --len;
3845 	for (i = 2, tem = h; i < len && tem; tem = tem->next, i++)
3846 	  if (num[i] & 1)
3847 	    tem->hyphen = 1;
3848       }
3849     }
3850     h = nexth;
3851   }
3852 }
3853 
do_hyphenation_patterns_file(int append)3854 static void do_hyphenation_patterns_file(int append)
3855 {
3856   symbol name = get_long_name(1);
3857   if (!name.is_null()) {
3858     if (!current_language)
3859       error("no current hyphenation language");
3860     else
3861       current_language->patterns.read_patterns_file(
3862 			  name.contents(), append,
3863 			  &current_language->exceptions);
3864   }
3865   skip_line();
3866 }
3867 
hyphenation_patterns_file()3868 static void hyphenation_patterns_file()
3869 {
3870   do_hyphenation_patterns_file(0);
3871 }
3872 
hyphenation_patterns_file_append()3873 static void hyphenation_patterns_file_append()
3874 {
3875   do_hyphenation_patterns_file(1);
3876 }
3877 
3878 class hyphenation_language_reg : public reg {
3879 public:
3880   const char *get_string();
3881 };
3882 
get_string()3883 const char *hyphenation_language_reg::get_string()
3884 {
3885   return current_language ? current_language->name.contents() : "";
3886 }
3887 
init_hyphen_requests()3888 void init_hyphen_requests()
3889 {
3890   init_request("hw", hyphen_word);
3891   init_request("hla", set_hyphenation_language);
3892   init_request("hpf", hyphenation_patterns_file);
3893   init_request("hpfa", hyphenation_patterns_file_append);
3894   number_reg_dictionary.define(".hla", new hyphenation_language_reg);
3895 }
3896