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