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