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