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