groff: update vendor branch to v1.20.1
[dragonfly.git] / contrib / groff / src / roff / troff / env.cpp
CommitLineData
92d0a6a6 1// -*- C++ -*-
4d3e9548
JL
2/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2009
92d0a6a6
JR
4 Free Software Foundation, Inc.
5 Written by James Clark (jjc@jclark.com)
6
7This file is part of groff.
8
9groff is free software; you can redistribute it and/or modify it under
10the terms of the GNU General Public License as published by the Free
4d3e9548
JL
11Software Foundation, either version 3 of the License, or
12(at your option) any later version.
92d0a6a6
JR
13
14groff is distributed in the hope that it will be useful, but WITHOUT ANY
15WARRANTY; without even the implied warranty of MERCHANTABILITY or
16FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17for more details.
18
4d3e9548
JL
19You should have received a copy of the GNU General Public License
20along with this program. If not, see <http://www.gnu.org/licenses/>. */
92d0a6a6
JR
21
22#include "troff.h"
23#include "dictionary.h"
24#include "hvunits.h"
465b256c
JR
25#include "stringclass.h"
26#include "mtsm.h"
92d0a6a6
JR
27#include "env.h"
28#include "request.h"
29#include "node.h"
30#include "token.h"
31#include "div.h"
32#include "reg.h"
4d3e9548 33#include "font.h"
92d0a6a6
JR
34#include "charinfo.h"
35#include "macropath.h"
36#include "input.h"
37#include <math.h>
38
39symbol default_family("T");
40
41enum { ADJUST_LEFT = 0, ADJUST_BOTH = 1, ADJUST_CENTER = 3, ADJUST_RIGHT = 5 };
42
43enum { HYPHEN_LAST_LINE = 2, HYPHEN_LAST_CHARS = 4, HYPHEN_FIRST_CHARS = 8 };
44
45struct env_list {
46 environment *env;
47 env_list *next;
48 env_list(environment *e, env_list *p) : env(e), next(p) {}
49};
50
51env_list *env_stack;
52const int NENVIRONMENTS = 10;
53environment *env_table[NENVIRONMENTS];
54dictionary env_dictionary(10);
55environment *curenv;
56static int next_line_number = 0;
465b256c
JR
57extern int suppress_push;
58extern statem *get_diversion_state();
92d0a6a6
JR
59
60charinfo *field_delimiter_char;
61charinfo *padding_indicator_char;
62
63int translate_space_to_dummy = 0;
64
65class pending_output_line {
66 node *nd;
67 int no_fill;
465b256c 68 int was_centered;
92d0a6a6
JR
69 vunits vs;
70 vunits post_vs;
71 hunits width;
72#ifdef WIDOW_CONTROL
73 int last_line; // Is it the last line of the paragraph?
74#endif /* WIDOW_CONTROL */
75public:
76 pending_output_line *next;
77
465b256c 78 pending_output_line(node *, int, vunits, vunits, hunits, int,
92d0a6a6
JR
79 pending_output_line * = 0);
80 ~pending_output_line();
81 int output();
82
83#ifdef WIDOW_CONTROL
84 friend void environment::mark_last_line();
465b256c 85 friend void environment::output(node *, int, vunits, vunits, hunits, int);
92d0a6a6
JR
86#endif /* WIDOW_CONTROL */
87};
88
89pending_output_line::pending_output_line(node *n, int nf, vunits v, vunits pv,
465b256c
JR
90 hunits w, int ce,
91 pending_output_line *p)
92: nd(n), no_fill(nf), was_centered(ce), vs(v), post_vs(pv), width(w),
92d0a6a6
JR
93#ifdef WIDOW_CONTROL
94 last_line(0),
95#endif /* WIDOW_CONTROL */
96 next(p)
97{
98}
99
100pending_output_line::~pending_output_line()
101{
102 delete_node_list(nd);
103}
104
105int pending_output_line::output()
106{
107 if (trap_sprung_flag)
108 return 0;
109#ifdef WIDOW_CONTROL
110 if (next && next->last_line && !no_fill) {
111 curdiv->need(vs + post_vs + vunits(vresolution));
112 if (trap_sprung_flag) {
113 next->last_line = 0; // Try to avoid infinite loops.
114 return 0;
115 }
116 }
117#endif
465b256c 118 curenv->construct_format_state(nd, was_centered, !no_fill);
92d0a6a6
JR
119 curdiv->output(nd, no_fill, vs, post_vs, width);
120 nd = 0;
121 return 1;
122}
123
124void environment::output(node *nd, int no_fill_flag,
125 vunits vs, vunits post_vs,
465b256c 126 hunits width, int was_centered)
92d0a6a6
JR
127{
128#ifdef WIDOW_CONTROL
129 while (pending_lines) {
130 if (widow_control && !pending_lines->no_fill && !pending_lines->next)
131 break;
132 if (!pending_lines->output())
133 break;
134 pending_output_line *tem = pending_lines;
135 pending_lines = pending_lines->next;
136 delete tem;
137 }
138#else /* WIDOW_CONTROL */
139 output_pending_lines();
140#endif /* WIDOW_CONTROL */
141 if (!trap_sprung_flag && !pending_lines
142#ifdef WIDOW_CONTROL
143 && (!widow_control || no_fill_flag)
144#endif /* WIDOW_CONTROL */
145 ) {
465b256c 146 curenv->construct_format_state(nd, was_centered, !no_fill_flag);
92d0a6a6 147 curdiv->output(nd, no_fill_flag, vs, post_vs, width);
92d0a6a6
JR
148 } else {
149 pending_output_line **p;
150 for (p = &pending_lines; *p; p = &(*p)->next)
151 ;
465b256c
JR
152 *p = new pending_output_line(nd, no_fill_flag, vs, post_vs, width,
153 was_centered);
92d0a6a6
JR
154 }
155}
156
157// a line from .tl goes at the head of the queue
158
159void environment::output_title(node *nd, int no_fill_flag,
160 vunits vs, vunits post_vs,
161 hunits width)
162{
163 if (!trap_sprung_flag)
164 curdiv->output(nd, no_fill_flag, vs, post_vs, width);
165 else
166 pending_lines = new pending_output_line(nd, no_fill_flag, vs, post_vs,
465b256c 167 width, 0, pending_lines);
92d0a6a6
JR
168}
169
170void environment::output_pending_lines()
171{
172 while (pending_lines && pending_lines->output()) {
173 pending_output_line *tem = pending_lines;
174 pending_lines = pending_lines->next;
175 delete tem;
176 }
177}
178
179#ifdef WIDOW_CONTROL
180
181void environment::mark_last_line()
182{
183 if (!widow_control || !pending_lines)
184 return;
185 pending_output_line *p;
186 for (p = pending_lines; p->next; p = p->next)
187 ;
188 if (!p->no_fill)
189 p->last_line = 1;
190}
191
192void widow_control_request()
193{
194 int n;
195 if (has_arg() && get_integer(&n))
196 curenv->widow_control = n != 0;
197 else
198 curenv->widow_control = 1;
199 skip_line();
200}
201
202#endif /* WIDOW_CONTROL */
203
204/* font_size functions */
205
206size_range *font_size::size_table = 0;
207int font_size::nranges = 0;
208
209extern "C" {
210
211int compare_ranges(const void *p1, const void *p2)
212{
213 return ((size_range *)p1)->min - ((size_range *)p2)->min;
214}
215
216}
217
218void font_size::init_size_table(int *sizes)
219{
220 nranges = 0;
221 while (sizes[nranges*2] != 0)
222 nranges++;
223 assert(nranges > 0);
224 size_table = new size_range[nranges];
225 for (int i = 0; i < nranges; i++) {
226 size_table[i].min = sizes[i*2];
227 size_table[i].max = sizes[i*2 + 1];
228 }
229 qsort(size_table, nranges, sizeof(size_range), compare_ranges);
230}
231
232font_size::font_size(int sp)
233{
234 for (int i = 0; i < nranges; i++) {
235 if (sp < size_table[i].min) {
236 if (i > 0 && size_table[i].min - sp >= sp - size_table[i - 1].max)
237 p = size_table[i - 1].max;
238 else
239 p = size_table[i].min;
240 return;
241 }
242 if (sp <= size_table[i].max) {
243 p = sp;
244 return;
245 }
246 }
247 p = size_table[nranges - 1].max;
248}
249
250int font_size::to_units()
251{
252 return scale(p, units_per_inch, sizescale*72);
253}
254
255// we can't do this in a static constructor because various dictionaries
256// have to get initialized first
257
258void init_environments()
259{
260 curenv = env_table[0] = new environment("0");
261}
262
263void tab_character()
264{
265 curenv->tab_char = get_optional_char();
266 skip_line();
267}
268
269void leader_character()
270{
271 curenv->leader_char = get_optional_char();
272 skip_line();
273}
274
275void environment::add_char(charinfo *ci)
276{
277 int s;
465b256c 278 node *gc_np = 0;
92d0a6a6
JR
279 if (interrupted)
280 ;
281 // don't allow fields in dummy environments
282 else if (ci == field_delimiter_char && !dummy) {
283 if (current_field)
284 wrap_up_field();
285 else
286 start_field();
287 }
288 else if (current_field && ci == padding_indicator_char)
289 add_padding();
290 else if (current_tab) {
291 if (tab_contents == 0)
292 tab_contents = new line_start_node;
293 if (ci != hyphen_indicator_char)
465b256c 294 tab_contents = tab_contents->add_char(ci, this, &tab_width, &s, &gc_np);
92d0a6a6
JR
295 else
296 tab_contents = tab_contents->add_discretionary_hyphen();
297 }
298 else {
299 if (line == 0)
300 start_line();
465b256c
JR
301#if 0
302 fprintf(stderr, "current line is\n");
303 line->debug_node_list();
304#endif
92d0a6a6 305 if (ci != hyphen_indicator_char)
465b256c 306 line = line->add_char(ci, this, &width_total, &space_total, &gc_np);
92d0a6a6
JR
307 else
308 line = line->add_discretionary_hyphen();
309 }
465b256c
JR
310#if 0
311 fprintf(stderr, "now after we have added character the line is\n");
312 line->debug_node_list();
313#endif
314 if ((!suppress_push) && gc_np) {
315 if (gc_np && (gc_np->state == 0)) {
316 gc_np->state = construct_state(0);
317 gc_np->push_state = get_diversion_state();
318 }
319 else if (line && (line->state == 0)) {
320 line->state = construct_state(0);
321 line->push_state = get_diversion_state();
322 }
323 }
324#if 0
325 fprintf(stderr, "now we have possibly added the state the line is\n");
326 line->debug_node_list();
327#endif
92d0a6a6
JR
328}
329
330node *environment::make_char_node(charinfo *ci)
331{
332 return make_node(ci, this);
333}
334
335void environment::add_node(node *n)
336{
337 if (n == 0)
338 return;
465b256c
JR
339 if (!suppress_push) {
340 if (n->is_special && n->state == NULL)
341 n->state = construct_state(0);
342 n->push_state = get_diversion_state();
343 }
344
92d0a6a6
JR
345 if (current_tab || current_field)
346 n->freeze_space();
347 if (interrupted) {
348 delete n;
349 }
350 else if (current_tab) {
351 n->next = tab_contents;
352 tab_contents = n;
353 tab_width += n->width();
354 }
355 else {
356 if (line == 0) {
357 if (discarding && n->discardable()) {
358 // XXX possibly: input_line_start -= n->width();
359 delete n;
360 return;
361 }
362 start_line();
363 }
364 width_total += n->width();
365 space_total += n->nspaces();
366 n->next = line;
367 line = n;
465b256c 368 construct_new_line_state(line);
92d0a6a6
JR
369 }
370}
371
92d0a6a6
JR
372void environment::add_hyphen_indicator()
373{
374 if (current_tab || interrupted || current_field
375 || hyphen_indicator_char != 0)
376 return;
377 if (line == 0)
378 start_line();
379 line = line->add_discretionary_hyphen();
380}
381
382int environment::get_hyphenation_flags()
383{
384 return hyphenation_flags;
385}
386
387int environment::get_hyphen_line_max()
388{
389 return hyphen_line_max;
390}
391
392int environment::get_hyphen_line_count()
393{
394 return hyphen_line_count;
395}
396
397int environment::get_center_lines()
398{
399 return center_lines;
400}
401
402int environment::get_right_justify_lines()
403{
404 return right_justify_lines;
405}
406
407void environment::add_italic_correction()
408{
409 if (current_tab) {
410 if (tab_contents)
411 tab_contents = tab_contents->add_italic_correction(&tab_width);
412 }
413 else if (line)
414 line = line->add_italic_correction(&width_total);
415}
416
417void environment::space_newline()
418{
419 assert(!current_tab && !current_field);
420 if (interrupted)
421 return;
422 hunits x = H0;
423 hunits sw = env_space_width(this);
424 hunits ssw = env_sentence_space_width(this);
425 if (!translate_space_to_dummy) {
426 x = sw;
427 if (node_list_ends_sentence(line) == 1)
428 x += ssw;
429 }
430 width_list *w = new width_list(sw, ssw);
431 if (node_list_ends_sentence(line) == 1)
432 w->next = new width_list(sw, ssw);
433 if (line != 0 && line->merge_space(x, sw, ssw)) {
434 width_total += x;
435 return;
436 }
437 add_node(new word_space_node(x, get_fill_color(), w));
438 possibly_break_line(0, spread_flag);
439 spread_flag = 0;
440}
441
442void environment::space()
443{
444 space(env_space_width(this), env_sentence_space_width(this));
445}
446
447void environment::space(hunits space_width, hunits sentence_space_width)
448{
449 if (interrupted)
450 return;
451 if (current_field && padding_indicator_char == 0) {
452 add_padding();
453 return;
454 }
455 hunits x = translate_space_to_dummy ? H0 : space_width;
456 node *p = current_tab ? tab_contents : line;
457 hunits *tp = current_tab ? &tab_width : &width_total;
458 if (p && p->nspaces() == 1 && p->width() == x
459 && node_list_ends_sentence(p->next) == 1) {
460 hunits xx = translate_space_to_dummy ? H0 : sentence_space_width;
461 if (p->merge_space(xx, space_width, sentence_space_width)) {
462 *tp += xx;
463 return;
464 }
465 }
466 if (p && p->merge_space(x, space_width, sentence_space_width)) {
467 *tp += x;
468 return;
469 }
470 add_node(new word_space_node(x,
471 get_fill_color(),
472 new width_list(space_width,
473 sentence_space_width)));
474 possibly_break_line(0, spread_flag);
475 spread_flag = 0;
476}
477
478node *do_underline_special(int);
479
480void environment::set_font(symbol nm)
481{
482 if (interrupted)
483 return;
484 if (nm == symbol("P") || nm.is_empty()) {
485 if (family->make_definite(prev_fontno) < 0)
486 return;
487 int tem = fontno;
488 fontno = prev_fontno;
489 prev_fontno = tem;
490 }
491 else {
492 prev_fontno = fontno;
493 int n = symbol_fontno(nm);
494 if (n < 0) {
495 n = next_available_font_position();
496 if (!mount_font(n, nm))
497 return;
498 }
499 if (family->make_definite(n) < 0)
500 return;
501 fontno = n;
502 }
503 if (underline_spaces && fontno != prev_fontno) {
504 if (fontno == get_underline_fontno())
505 add_node(do_underline_special(1));
506 if (prev_fontno == get_underline_fontno())
507 add_node(do_underline_special(0));
508 }
509}
510
511void environment::set_font(int n)
512{
513 if (interrupted)
514 return;
515 if (is_good_fontno(n)) {
516 prev_fontno = fontno;
517 fontno = n;
518 }
519 else
520 warning(WARN_FONT, "bad font number");
521}
522
523void environment::set_family(symbol fam)
524{
525 if (interrupted)
526 return;
527 if (fam.is_null() || fam.is_empty()) {
528 if (prev_family->make_definite(fontno) < 0)
529 return;
530 font_family *tem = family;
531 family = prev_family;
532 prev_family = tem;
533 }
534 else {
535 font_family *f = lookup_family(fam);
536 if (f->make_definite(fontno) < 0)
537 return;
538 prev_family = family;
539 family = f;
540 }
541}
542
543void environment::set_size(int n)
544{
545 if (interrupted)
546 return;
547 if (n == 0) {
548 font_size temp = prev_size;
549 prev_size = size;
550 size = temp;
551 int temp2 = prev_requested_size;
552 prev_requested_size = requested_size;
553 requested_size = temp2;
554 }
555 else {
556 prev_size = size;
557 size = font_size(n);
558 prev_requested_size = requested_size;
559 requested_size = n;
560 }
561}
562
563void environment::set_char_height(int n)
564{
565 if (interrupted)
566 return;
567 if (n == requested_size || n <= 0)
568 char_height = 0;
569 else
570 char_height = n;
571}
572
573void environment::set_char_slant(int n)
574{
575 if (interrupted)
576 return;
577 char_slant = n;
578}
579
580color *environment::get_prev_glyph_color()
581{
582 return prev_glyph_color;
583}
584
585color *environment::get_glyph_color()
586{
587 return glyph_color;
588}
589
590color *environment::get_prev_fill_color()
591{
592 return prev_fill_color;
593}
594
595color *environment::get_fill_color()
596{
597 return fill_color;
598}
599
600void environment::set_glyph_color(color *c)
601{
602 if (interrupted)
603 return;
604 curenv->prev_glyph_color = curenv->glyph_color;
605 curenv->glyph_color = c;
606}
607
608void environment::set_fill_color(color *c)
609{
610 if (interrupted)
611 return;
612 curenv->prev_fill_color = curenv->fill_color;
613 curenv->fill_color = c;
614}
615
616environment::environment(symbol nm)
617: dummy(0),
618 prev_line_length((units_per_inch*13)/2),
619 line_length((units_per_inch*13)/2),
620 prev_title_length((units_per_inch*13)/2),
621 title_length((units_per_inch*13)/2),
622 prev_size(sizescale*10),
623 size(sizescale*10),
624 requested_size(sizescale*10),
625 prev_requested_size(sizescale*10),
626 char_height(0),
627 char_slant(0),
628 space_size(12),
629 sentence_space_size(12),
630 adjust_mode(ADJUST_BOTH),
631 fill(1),
632 interrupted(0),
633 prev_line_interrupted(0),
634 center_lines(0),
635 right_justify_lines(0),
636 prev_vertical_spacing(points_to_units(12)),
637 vertical_spacing(points_to_units(12)),
638 prev_post_vertical_spacing(0),
639 post_vertical_spacing(0),
640 prev_line_spacing(1),
641 line_spacing(1),
642 prev_indent(0),
643 indent(0),
644 temporary_indent(0),
645 have_temporary_indent(0),
646 underline_lines(0),
647 underline_spaces(0),
648 input_trap_count(0),
649 continued_input_trap(0),
650 line(0),
651 prev_text_length(0),
652 width_total(0),
653 space_total(0),
654 input_line_start(0),
92d0a6a6
JR
655 line_tabs(0),
656 current_tab(TAB_NONE),
657 leader_node(0),
658 tab_char(0),
659 leader_char(charset_table['.']),
660 current_field(0),
661 discarding(0),
662 spread_flag(0),
663 margin_character_flags(0),
664 margin_character_node(0),
665 margin_character_distance(points_to_units(10)),
666 numbering_nodes(0),
667 number_text_separation(1),
668 line_number_indent(0),
669 line_number_multiple(1),
670 no_number_count(0),
671 hyphenation_flags(1),
672 hyphen_line_count(0),
673 hyphen_line_max(-1),
674 hyphenation_space(H0),
675 hyphenation_margin(H0),
676 composite(0),
677 pending_lines(0),
678#ifdef WIDOW_CONTROL
679 widow_control(0),
680#endif /* WIDOW_CONTROL */
92d0a6a6
JR
681 glyph_color(&default_color),
682 prev_glyph_color(&default_color),
683 fill_color(&default_color),
684 prev_fill_color(&default_color),
465b256c
JR
685 seen_space(0),
686 seen_eol(0),
687 suppress_next_eol(0),
688 seen_break(0),
689 tabs(units_per_inch/2, TAB_LEFT),
92d0a6a6
JR
690 name(nm),
691 control_char('.'),
692 no_break_control_char('\''),
693 hyphen_indicator_char(0)
694{
695 prev_family = family = lookup_family(default_family);
696 prev_fontno = fontno = 1;
697 if (!is_good_fontno(1))
698 fatal("font number 1 not a valid font");
699 if (family->make_definite(1) < 0)
700 fatal("invalid default family `%1'", default_family.contents());
701 prev_fontno = fontno;
702}
703
704environment::environment(const environment *e)
705: dummy(1),
706 prev_line_length(e->prev_line_length),
707 line_length(e->line_length),
708 prev_title_length(e->prev_title_length),
709 title_length(e->title_length),
710 prev_size(e->prev_size),
711 size(e->size),
712 requested_size(e->requested_size),
713 prev_requested_size(e->prev_requested_size),
714 char_height(e->char_height),
715 char_slant(e->char_slant),
716 prev_fontno(e->prev_fontno),
717 fontno(e->fontno),
718 prev_family(e->prev_family),
719 family(e->family),
720 space_size(e->space_size),
721 sentence_space_size(e->sentence_space_size),
722 adjust_mode(e->adjust_mode),
723 fill(e->fill),
724 interrupted(0),
725 prev_line_interrupted(0),
726 center_lines(0),
727 right_justify_lines(0),
728 prev_vertical_spacing(e->prev_vertical_spacing),
729 vertical_spacing(e->vertical_spacing),
730 prev_post_vertical_spacing(e->prev_post_vertical_spacing),
731 post_vertical_spacing(e->post_vertical_spacing),
732 prev_line_spacing(e->prev_line_spacing),
733 line_spacing(e->line_spacing),
734 prev_indent(e->prev_indent),
735 indent(e->indent),
736 temporary_indent(0),
737 have_temporary_indent(0),
738 underline_lines(0),
739 underline_spaces(0),
740 input_trap_count(0),
741 continued_input_trap(0),
742 line(0),
743 prev_text_length(e->prev_text_length),
744 width_total(0),
745 space_total(0),
746 input_line_start(0),
92d0a6a6
JR
747 line_tabs(e->line_tabs),
748 current_tab(TAB_NONE),
749 leader_node(0),
750 tab_char(e->tab_char),
751 leader_char(e->leader_char),
752 current_field(0),
753 discarding(0),
754 spread_flag(0),
755 margin_character_flags(e->margin_character_flags),
756 margin_character_node(e->margin_character_node),
757 margin_character_distance(e->margin_character_distance),
758 numbering_nodes(0),
759 number_text_separation(e->number_text_separation),
760 line_number_indent(e->line_number_indent),
761 line_number_multiple(e->line_number_multiple),
762 no_number_count(e->no_number_count),
763 hyphenation_flags(e->hyphenation_flags),
764 hyphen_line_count(0),
765 hyphen_line_max(e->hyphen_line_max),
766 hyphenation_space(e->hyphenation_space),
767 hyphenation_margin(e->hyphenation_margin),
768 composite(0),
769 pending_lines(0),
770#ifdef WIDOW_CONTROL
771 widow_control(e->widow_control),
772#endif /* WIDOW_CONTROL */
92d0a6a6
JR
773 glyph_color(e->glyph_color),
774 prev_glyph_color(e->prev_glyph_color),
775 fill_color(e->fill_color),
776 prev_fill_color(e->prev_fill_color),
465b256c
JR
777 seen_space(e->seen_space),
778 seen_eol(e->seen_eol),
779 suppress_next_eol(e->suppress_next_eol),
780 seen_break(e->seen_break),
781 tabs(e->tabs),
92d0a6a6
JR
782 name(e->name), // so that eg `.if "\n[.ev]"0"' works
783 control_char(e->control_char),
784 no_break_control_char(e->no_break_control_char),
785 hyphen_indicator_char(e->hyphen_indicator_char)
786{
787}
788
789void environment::copy(const environment *e)
790{
791 prev_line_length = e->prev_line_length;
792 line_length = e->line_length;
793 prev_title_length = e->prev_title_length;
794 title_length = e->title_length;
795 prev_size = e->prev_size;
796 size = e->size;
797 prev_requested_size = e->prev_requested_size;
798 requested_size = e->requested_size;
799 char_height = e->char_height;
800 char_slant = e->char_slant;
801 space_size = e->space_size;
802 sentence_space_size = e->sentence_space_size;
803 adjust_mode = e->adjust_mode;
804 fill = e->fill;
805 interrupted = 0;
806 prev_line_interrupted = 0;
807 center_lines = 0;
808 right_justify_lines = 0;
809 prev_vertical_spacing = e->prev_vertical_spacing;
810 vertical_spacing = e->vertical_spacing;
811 prev_post_vertical_spacing = e->prev_post_vertical_spacing,
812 post_vertical_spacing = e->post_vertical_spacing,
813 prev_line_spacing = e->prev_line_spacing;
814 line_spacing = e->line_spacing;
815 prev_indent = e->prev_indent;
816 indent = e->indent;
817 have_temporary_indent = 0;
818 temporary_indent = 0;
819 underline_lines = 0;
820 underline_spaces = 0;
821 input_trap_count = 0;
822 continued_input_trap = 0;
823 prev_text_length = e->prev_text_length;
824 width_total = 0;
825 space_total = 0;
826 input_line_start = 0;
827 control_char = e->control_char;
828 no_break_control_char = e->no_break_control_char;
829 hyphen_indicator_char = e->hyphen_indicator_char;
830 spread_flag = 0;
831 line = 0;
832 pending_lines = 0;
833 discarding = 0;
834 tabs = e->tabs;
835 line_tabs = e->line_tabs;
836 current_tab = TAB_NONE;
837 current_field = 0;
838 margin_character_flags = e->margin_character_flags;
4d3e9548
JL
839 if (e->margin_character_node)
840 margin_character_node = e->margin_character_node->copy();
92d0a6a6
JR
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;
92d0a6a6
JR
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
870environment::~environment()
871{
872 delete leader_node;
873 delete_node_list(line);
874 delete_node_list(numbering_nodes);
875}
876
877hunits 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
889void 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
896hunits environment::get_line_length()
897{
898 return line_length;
899}
900
901hunits environment::get_saved_line_length()
902{
903 if (line)
904 return target_text_length + saved_indent;
905 else
906 return line_length;
907}
908
909vunits environment::get_vertical_spacing()
910{
911 return vertical_spacing;
912}
913
914vunits environment::get_post_vertical_spacing()
915{
916 return post_vertical_spacing;
917}
918
919int environment::get_line_spacing()
920{
921 return line_spacing;
922}
923
924vunits 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
932int environment::get_bold()
933{
934 return get_bold_fontno(fontno);
935}
936
937hunits environment::get_digit_width()
938{
939 return env_digit_width(this);
940}
941
942int environment::get_adjust_mode()
943{
944 return adjust_mode;
945}
946
947int environment::get_fill()
948{
949 return fill;
950}
951
952hunits environment::get_indent()
953{
954 return indent;
955}
956
957hunits 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
967hunits environment::get_temporary_indent()
968{
969 return temporary_indent;
970}
971
972hunits environment::get_title_length()
973{
974 return title_length;
975}
976
977node *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
987hunits environment::get_prev_char_width()
988{
989 node *last = get_prev_char();
990 if (!last)
991 return H0;
992 return last->width();
993}
994
995hunits environment::get_prev_char_skew()
996{
997 node *last = get_prev_char();
998 if (!last)
999 return H0;
1000 return last->skew();
1001}
1002
1003vunits 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
1013vunits 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
1023hunits 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
1031hunits environment::get_prev_text_length()
1032{
1033 return prev_text_length;
1034}
1035
1036
1037static int sb_reg_contents = 0;
1038static int st_reg_contents = 0;
1039static int ct_reg_contents = 0;
1040static int rsb_reg_contents = 0;
1041static int rst_reg_contents = 0;
1042static int skw_reg_contents = 0;
1043static int ssc_reg_contents = 0;
1044
1045void 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
1078node *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
1089void 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 {
465b256c
JR
1135 int seen_space = curenv->seen_space;
1136 int seen_eol = curenv->seen_eol;
1137 int suppress_next_eol = curenv->suppress_next_eol;
92d0a6a6 1138 curenv = env_stack->env;
465b256c
JR
1139 curenv->seen_space = seen_space;
1140 curenv->seen_eol = seen_eol;
1141 curenv->suppress_next_eol = suppress_next_eol;
92d0a6a6
JR
1142 env_list *tem = env_stack;
1143 env_stack = env_stack->next;
1144 delete tem;
1145 }
1146 }
1147 skip_line();
1148}
1149
1150void 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
465b256c
JR
1178void 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
1188void 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
92d0a6a6
JR
1198static symbol P_symbol("P");
1199
1200void 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
1222void family_change()
1223{
1224 symbol s = get_name();
1225 curenv->set_family(s);
1226 skip_line();
1227}
1228
1229void 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);
92d0a6a6
JR
1236 }
1237 else
1238 curenv->set_size(0);
1239 skip_line();
1240}
1241
1242void 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
1283void 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
1296void fill()
1297{
1298 while (!tok.newline() && !tok.eof())
1299 tok.next();
1300 if (break_flag)
1301 curenv->do_break();
1302 curenv->fill = 1;
92d0a6a6
JR
1303 tok.next();
1304}
1305
1306void no_fill()
1307{
1308 while (!tok.newline() && !tok.eof())
1309 tok.next();
1310 if (break_flag)
1311 curenv->do_break();
1312 curenv->fill = 0;
465b256c 1313 curenv->suppress_next_eol = 1;
92d0a6a6
JR
1314 tok.next();
1315}
1316
1317void 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;
465b256c 1330 curdiv->modified_tag.incl(MTSM_CE);
92d0a6a6
JR
1331 tok.next();
1332}
1333
1334void 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;
465b256c 1347 curdiv->modified_tag.incl(MTSM_RJ);
92d0a6a6
JR
1348 tok.next();
1349}
1350
1351void 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;
465b256c 1364 curdiv->modified_tag.incl(MTSM_LL);
92d0a6a6
JR
1365 skip_line();
1366}
1367
1368void 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
1384void 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
1400void 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
1417void 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
1433void 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;
465b256c 1451 curdiv->modified_tag.incl(MTSM_IN);
92d0a6a6
JR
1452 tok.next();
1453}
1454
1455void 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;
465b256c 1472 curdiv->modified_tag.incl(MTSM_TI);
92d0a6a6
JR
1473 }
1474 tok.next();
1475}
1476
1477node *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
1485void 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
1513void continuous_underline()
1514{
1515 do_underline(1);
1516}
1517
1518void underline()
1519{
1520 do_underline(0);
1521}
1522
1523void 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
1535void 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
1547void 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;
4d3e9548
JL
1560 curenv->margin_character_flags = MARGIN_CHARACTER_ON
1561 | MARGIN_CHARACTER_NEXT;
92d0a6a6
JR
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
1578void 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
1637void 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
1647void no_hyphenate()
1648{
1649 curenv->hyphenation_flags = 0;
1650 skip_line();
1651}
1652
1653void 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
1663void hyphen_char()
1664{
1665 curenv->hyphen_indicator_char = get_optional_char();
1666 skip_line();
1667}
1668
1669void 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
1679void environment::interrupt()
1680{
1681 if (!dummy) {
1682 add_node(new transparent_dummy_node);
1683 interrupted = 1;
1684 }
1685}
1686
1687void environment::newline()
1688{
465b256c 1689 int was_centered = 0;
92d0a6a6
JR
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;
465b256c 1728 was_centered = 1;
92d0a6a6
JR
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) {
465b256c
JR
1751 curdiv->modified_tag.incl(MTSM_EOL);
1752 if (suppress_next_eol)
1753 suppress_next_eol = 0;
1754 else
1755 seen_eol = 1;
92d0a6a6 1756 }
465b256c
JR
1757
1758 output_line(to_be_output, to_be_output_width, was_centered);
92d0a6a6
JR
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
465b256c 1768void environment::output_line(node *n, hunits width, int was_centered)
92d0a6a6
JR
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 }
465b256c
JR
1827 output(nn, !fill, vertical_spacing, total_post_vertical_spacing(), width,
1828 was_centered);
92d0a6a6
JR
1829}
1830
1831void 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
1847hunits environment::get_hyphenation_space()
1848{
1849 return hyphenation_space;
1850}
1851
1852void 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
1865hunits environment::get_hyphenation_margin()
1866{
1867 return hyphenation_margin;
1868}
1869
1870void 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
1883breakpoint *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
1979void 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
2039static 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
2051static 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
2074void environment::possibly_break_line(int start_here, int forced)
2075{
465b256c 2076 int was_centered = center_lines > 0;
92d0a6a6
JR
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;
465b256c 2106 was_centered = 1;
92d0a6a6
JR
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.
465b256c 2144 output_line(pre, output_width, was_centered);
92d0a6a6
JR
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/*
2164Do the break at the end of input after the end macro (if any).
2165
2166Unix troff behaves as follows: if the last line is
2167
2168foo bar\c
2169
2170it will output foo on the current page, and bar on the next page;
2171if the last line is
2172
2173foo\c
2174
2175or
2176
2177foo bar
2178
2179everything will be output on the current page. This behaviour must be
2180considered a bug.
2181
2182The problem is that some macro packages rely on this. For example,
2183the ATK macros have an end macro that emits \c if it needs to print a
2184table 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,
2186provided that the current environment is not empty at the end of the
2187input file.
2188
2189The following will make macro packages that do that sort of thing work
2190even if the current environment is empty at the end of the input file.
2191If the last input line used \c and this line occurred in the end macro,
2192then we'll force everything out on the current page, but we'll make
2193sure that the environment isn't empty so that we won't exit at the
2194bottom of this page.
2195*/
2196
2197void 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
465b256c 2207node *environment::make_tag(const char *nm, int i)
92d0a6a6 2208{
92d0a6a6
JR
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;
465b256c 2217 m->append_str("devtag:");
92d0a6a6
JR
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);
465b256c 2223 return new special_node(*m);
92d0a6a6 2224 }
465b256c 2225 return 0;
92d0a6a6
JR
2226}
2227
465b256c 2228void environment::dump_troff_state()
92d0a6a6 2229{
465b256c
JR
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}
92d0a6a6 2246
465b256c
JR
2247statem *environment::construct_state(int only_eol)
2248{
92d0a6a6 2249 if (is_html) {
465b256c
JR
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;
92d0a6a6 2272 }
465b256c
JR
2273 else
2274 return NULL;
92d0a6a6
JR
2275}
2276
465b256c
JR
2277void environment::construct_format_state(node *n, int was_centered,
2278 int filling)
92d0a6a6
JR
2279{
2280 if (is_html) {
465b256c
JR
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 }
92d0a6a6 2305 }
92d0a6a6
JR
2306}
2307
465b256c 2308void environment::construct_new_line_state(node *n)
92d0a6a6
JR
2309{
2310 if (is_html) {
465b256c
JR
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;
92d0a6a6 2322 }
92d0a6a6
JR
2323}
2324
465b256c
JR
2325extern int global_diverted_space;
2326
92d0a6a6
JR
2327void environment::do_break(int do_spread)
2328{
465b256c 2329 int was_centered = 0;
92d0a6a6
JR
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
4d3e9548
JL
2338 if (line->nspaces() == 0) {
2339 line = new space_node(H0, get_fill_color(), line);
2340 space_total++;
2341 }
92d0a6a6
JR
2342 possibly_break_line(0, do_spread);
2343 }
2344 while (line != 0 && line->discardable()) {
2345 width_total -= line->width();
2346 space_total -= line->nspaces();
2347 node *tem = line;
2348 line = line->next;
2349 delete tem;
2350 }
2351 discarding = 0;
2352 input_line_start = H0;
2353 if (line != 0) {
2354 if (fill) {
2355 switch (adjust_mode) {
2356 case ADJUST_CENTER:
2357 saved_indent += (target_text_length - width_total)/2;
465b256c 2358 was_centered = 1;
92d0a6a6
JR
2359 break;
2360 case ADJUST_RIGHT:
2361 saved_indent += target_text_length - width_total;
2362 break;
2363 }
2364 }
2365 node *tem = line;
2366 line = 0;
465b256c 2367 output_line(tem, width_total, was_centered);
92d0a6a6
JR
2368 hyphen_line_count = 0;
2369 }
2370 prev_line_interrupted = 0;
2371#ifdef WIDOW_CONTROL
2372 mark_last_line();
2373 output_pending_lines();
2374#endif /* WIDOW_CONTROL */
465b256c
JR
2375 if (!global_diverted_space) {
2376 curdiv->modified_tag.incl(MTSM_BR);
2377 seen_break = 1;
2378 }
92d0a6a6
JR
2379}
2380
2381int environment::is_empty()
2382{
2383 return !current_tab && line == 0 && pending_lines == 0;
2384}
2385
2386void do_break_request(int spread)
2387{
2388 while (!tok.newline() && !tok.eof())
2389 tok.next();
465b256c 2390 if (break_flag)
92d0a6a6 2391 curenv->do_break(spread);
92d0a6a6
JR
2392 tok.next();
2393}
2394
2395void break_request()
2396{
2397 do_break_request(0);
2398}
2399
2400void break_spread_request()
2401{
2402 do_break_request(1);
2403}
2404
2405void title()
2406{
2407 if (curdiv == topdiv && topdiv->before_first_page) {
2408 handle_initial_title();
2409 return;
2410 }
2411 node *part[3];
2412 hunits part_width[3];
2413 part[0] = part[1] = part[2] = 0;
2414 environment env(curenv);
2415 environment *oldenv = curenv;
2416 curenv = &env;
2417 read_title_parts(part, part_width);
2418 curenv = oldenv;
2419 curenv->size = env.size;
2420 curenv->prev_size = env.prev_size;
2421 curenv->requested_size = env.requested_size;
2422 curenv->prev_requested_size = env.prev_requested_size;
2423 curenv->char_height = env.char_height;
2424 curenv->char_slant = env.char_slant;
2425 curenv->fontno = env.fontno;
2426 curenv->prev_fontno = env.prev_fontno;
2427 curenv->glyph_color = env.glyph_color;
2428 curenv->prev_glyph_color = env.prev_glyph_color;
2429 curenv->fill_color = env.fill_color;
2430 curenv->prev_fill_color = env.prev_fill_color;
2431 node *n = 0;
2432 node *p = part[2];
2433 while (p != 0) {
2434 node *tem = p;
2435 p = p->next;
2436 tem->next = n;
2437 n = tem;
2438 }
2439 hunits length_title(curenv->title_length);
2440 hunits f = length_title - part_width[1];
2441 hunits f2 = f/2;
2442 n = new hmotion_node(f2 - part_width[2], curenv->get_fill_color(), n);
2443 p = part[1];
2444 while (p != 0) {
2445 node *tem = p;
2446 p = p->next;
2447 tem->next = n;
2448 n = tem;
2449 }
2450 n = new hmotion_node(f - f2 - part_width[0], curenv->get_fill_color(), n);
2451 p = part[0];
2452 while (p != 0) {
2453 node *tem = p;
2454 p = p->next;
2455 tem->next = n;
2456 n = tem;
2457 }
2458 curenv->output_title(n, !curenv->fill, curenv->vertical_spacing,
2459 curenv->total_post_vertical_spacing(), length_title);
2460 curenv->hyphen_line_count = 0;
2461 tok.next();
2462}
2463
2464void adjust()
2465{
2466 curenv->adjust_mode |= 1;
2467 if (has_arg()) {
2468 switch (tok.ch()) {
2469 case 'l':
2470 curenv->adjust_mode = ADJUST_LEFT;
2471 break;
2472 case 'r':
2473 curenv->adjust_mode = ADJUST_RIGHT;
2474 break;
2475 case 'c':
2476 curenv->adjust_mode = ADJUST_CENTER;
2477 break;
2478 case 'b':
2479 case 'n':
2480 curenv->adjust_mode = ADJUST_BOTH;
2481 break;
2482 default:
2483 int n;
2484 if (get_integer(&n)) {
2485 if (n < 0)
2486 warning(WARN_RANGE, "negative adjustment mode");
2487 else if (n > 5) {
2488 curenv->adjust_mode = 5;
2489 warning(WARN_RANGE, "adjustment mode `%1' out of range", n);
2490 }
2491 else
2492 curenv->adjust_mode = n;
2493 }
2494 }
2495 }
2496 skip_line();
2497}
2498
2499void no_adjust()
2500{
2501 curenv->adjust_mode &= ~1;
2502 skip_line();
2503}
2504
2505void do_input_trap(int continued)
2506{
2507 curenv->input_trap_count = 0;
2508 if (continued)
2509 curenv->continued_input_trap = 1;
2510 int n;
2511 if (has_arg() && get_integer(&n)) {
2512 if (n <= 0)
2513 warning(WARN_RANGE,
2514 "number of lines for input trap must be greater than zero");
2515 else {
2516 symbol s = get_name(1);
2517 if (!s.is_null()) {
2518 curenv->input_trap_count = n;
2519 curenv->input_trap = s;
2520 }
2521 }
2522 }
2523 skip_line();
2524}
2525
2526void input_trap()
2527{
2528 do_input_trap(0);
2529}
2530
2531void input_trap_continued()
2532{
2533 do_input_trap(1);
2534}
2535
2536/* tabs */
2537
2538// must not be R or C or L or a legitimate part of a number expression
2539const char TAB_REPEAT_CHAR = 'T';
2540
2541struct tab {
2542 tab *next;
2543 hunits pos;
2544 tab_type type;
2545 tab(hunits, tab_type);
2546 enum { BLOCK = 1024 };
2547 static tab *free_list;
2548 void *operator new(size_t);
2549 void operator delete(void *);
2550};
2551
2552tab *tab::free_list = 0;
2553
2554void *tab::operator new(size_t n)
2555{
2556 assert(n == sizeof(tab));
2557 if (!free_list) {
2558 free_list = (tab *)new char[sizeof(tab)*BLOCK];
2559 for (int i = 0; i < BLOCK - 1; i++)
2560 free_list[i].next = free_list + i + 1;
2561 free_list[BLOCK-1].next = 0;
2562 }
2563 tab *p = free_list;
2564 free_list = (tab *)(free_list->next);
2565 p->next = 0;
2566 return p;
2567}
2568
2569#ifdef __GNUG__
2570/* cfront can't cope with this. */
2571inline
2572#endif
2573void tab::operator delete(void *p)
2574{
2575 if (p) {
2576 ((tab *)p)->next = free_list;
2577 free_list = (tab *)p;
2578 }
2579}
2580
2581tab::tab(hunits x, tab_type t) : next(0), pos(x), type(t)
2582{
2583}
2584
2585tab_stops::tab_stops(hunits distance, tab_type type)
2586: initial_list(0)
2587{
2588 repeated_list = new tab(distance, type);
2589}
2590
2591tab_stops::~tab_stops()
2592{
2593 clear();
2594}
2595
2596tab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance)
2597{
2598 hunits nextpos;
2599
2600 return distance_to_next_tab(curpos, distance, &nextpos);
2601}
2602
2603tab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance,
2604 hunits *nextpos)
2605{
2606 hunits lastpos = 0;
2607 tab *tem;
2608 for (tem = initial_list; tem && tem->pos <= curpos; tem = tem->next)
2609 lastpos = tem->pos;
2610 if (tem) {
2611 *distance = tem->pos - curpos;
2612 *nextpos = tem->pos;
2613 return tem->type;
2614 }
2615 if (repeated_list == 0)
2616 return TAB_NONE;
2617 hunits base = lastpos;
2618 for (;;) {
2619 for (tem = repeated_list; tem && tem->pos + base <= curpos; tem = tem->next)
2620 lastpos = tem->pos;
2621 if (tem) {
2622 *distance = tem->pos + base - curpos;
2623 *nextpos = tem->pos + base;
2624 return tem->type;
2625 }
2626 assert(lastpos > 0);
2627 base += lastpos;
2628 }
2629 return TAB_NONE;
2630}
2631
2632const char *tab_stops::to_string()
2633{
2634 static char *buf = 0;
2635 static int buf_size = 0;
2636 // figure out a maximum on the amount of space we can need
2637 int count = 0;
2638 tab *p;
2639 for (p = initial_list; p; p = p->next)
2640 ++count;
2641 for (p = repeated_list; p; p = p->next)
2642 ++count;
2643 // (10 for digits + 1 for u + 1 for 'C' or 'R') + 2 for ' &' + 1 for '\0'
2644 int need = count*12 + 3;
2645 if (buf == 0 || need > buf_size) {
2646 if (buf)
2647 a_delete buf;
2648 buf_size = need;
2649 buf = new char[buf_size];
2650 }
2651 char *ptr = buf;
2652 for (p = initial_list; p; p = p->next) {
2653 strcpy(ptr, i_to_a(p->pos.to_units()));
2654 ptr = strchr(ptr, '\0');
2655 *ptr++ = 'u';
2656 *ptr = '\0';
2657 switch (p->type) {
2658 case TAB_LEFT:
2659 break;
2660 case TAB_RIGHT:
2661 *ptr++ = 'R';
2662 break;
2663 case TAB_CENTER:
2664 *ptr++ = 'C';
2665 break;
2666 case TAB_NONE:
2667 default:
2668 assert(0);
2669 }
2670 }
2671 if (repeated_list)
2672 *ptr++ = TAB_REPEAT_CHAR;
2673 for (p = repeated_list; p; p = p->next) {
2674 strcpy(ptr, i_to_a(p->pos.to_units()));
2675 ptr = strchr(ptr, '\0');
2676 *ptr++ = 'u';
2677 *ptr = '\0';
2678 switch (p->type) {
2679 case TAB_LEFT:
2680 break;
2681 case TAB_RIGHT:
2682 *ptr++ = 'R';
2683 break;
2684 case TAB_CENTER:
2685 *ptr++ = 'C';
2686 break;
2687 case TAB_NONE:
2688 default:
2689 assert(0);
2690 }
2691 }
2692 *ptr++ = '\0';
2693 return buf;
2694}
2695
2696tab_stops::tab_stops() : initial_list(0), repeated_list(0)
2697{
2698}
2699
2700tab_stops::tab_stops(const tab_stops &ts)
2701: initial_list(0), repeated_list(0)
2702{
2703 tab **p = &initial_list;
2704 tab *t = ts.initial_list;
2705 while (t) {
2706 *p = new tab(t->pos, t->type);
2707 t = t->next;
2708 p = &(*p)->next;
2709 }
2710 p = &repeated_list;
2711 t = ts.repeated_list;
2712 while (t) {
2713 *p = new tab(t->pos, t->type);
2714 t = t->next;
2715 p = &(*p)->next;
2716 }
2717}
2718
2719void tab_stops::clear()
2720{
2721 while (initial_list) {
2722 tab *tem = initial_list;
2723 initial_list = initial_list->next;
2724 delete tem;
2725 }
2726 while (repeated_list) {
2727 tab *tem = repeated_list;
2728 repeated_list = repeated_list->next;
2729 delete tem;
2730 }
2731}
2732
2733void tab_stops::add_tab(hunits pos, tab_type type, int repeated)
2734{
2735 tab **p;
2736 for (p = repeated ? &repeated_list : &initial_list; *p; p = &(*p)->next)
2737 ;
2738 *p = new tab(pos, type);
2739}
2740
2741
2742void tab_stops::operator=(const tab_stops &ts)
2743{
2744 clear();
2745 tab **p = &initial_list;
2746 tab *t = ts.initial_list;
2747 while (t) {
2748 *p = new tab(t->pos, t->type);
2749 t = t->next;
2750 p = &(*p)->next;
2751 }
2752 p = &repeated_list;
2753 t = ts.repeated_list;
2754 while (t) {
2755 *p = new tab(t->pos, t->type);
2756 t = t->next;
2757 p = &(*p)->next;
2758 }
2759}
2760
2761void set_tabs()
2762{
2763 hunits pos;
2764 hunits prev_pos = 0;
2765 int first = 1;
2766 int repeated = 0;
2767 tab_stops tabs;
2768 while (has_arg()) {
2769 if (tok.ch() == TAB_REPEAT_CHAR) {
2770 tok.next();
2771 repeated = 1;
2772 prev_pos = 0;
2773 }
2774 if (!get_hunits(&pos, 'm', prev_pos))
2775 break;
2776 tab_type type = TAB_LEFT;
2777 if (tok.ch() == 'C') {
2778 tok.next();
2779 type = TAB_CENTER;
2780 }
2781 else if (tok.ch() == 'R') {
2782 tok.next();
2783 type = TAB_RIGHT;
2784 }
2785 else if (tok.ch() == 'L') {
2786 tok.next();
2787 }
2788 if (pos <= prev_pos && !first)
2789 warning(WARN_RANGE,
2790 "positions of tab stops must be strictly increasing");
2791 else {
2792 tabs.add_tab(pos, type, repeated);
2793 prev_pos = pos;
2794 first = 0;
2795 }
2796 }
2797 curenv->tabs = tabs;
465b256c 2798 curdiv->modified_tag.incl(MTSM_TA);
92d0a6a6
JR
2799 skip_line();
2800}
2801
2802const char *environment::get_tabs()
2803{
2804 return tabs.to_string();
2805}
2806
2807tab_type environment::distance_to_next_tab(hunits *distance)
2808{
2809 return line_tabs
2810 ? curenv->tabs.distance_to_next_tab(get_text_length(), distance)
2811 : curenv->tabs.distance_to_next_tab(get_input_line_position(), distance);
2812}
2813
2814tab_type environment::distance_to_next_tab(hunits *distance, hunits *leftpos)
2815{
2816 return line_tabs
2817 ? curenv->tabs.distance_to_next_tab(get_text_length(), distance, leftpos)
2818 : curenv->tabs.distance_to_next_tab(get_input_line_position(), distance,
2819 leftpos);
2820}
2821
2822void field_characters()
2823{
2824 field_delimiter_char = get_optional_char();
2825 if (field_delimiter_char)
2826 padding_indicator_char = get_optional_char();
2827 else
2828 padding_indicator_char = 0;
2829 skip_line();
2830}
2831
2832void line_tabs_request()
2833{
2834 int n;
2835 if (has_arg() && get_integer(&n))
2836 curenv->line_tabs = n != 0;
2837 else
2838 curenv->line_tabs = 1;
2839 skip_line();
2840}
2841
2842int environment::get_line_tabs()
2843{
2844 return line_tabs;
2845}
2846
2847void environment::wrap_up_tab()
2848{
2849 if (!current_tab)
2850 return;
2851 if (line == 0)
2852 start_line();
2853 hunits tab_amount;
2854 switch (current_tab) {
2855 case TAB_RIGHT:
2856 tab_amount = tab_distance - tab_width;
2857 line = make_tab_node(tab_amount, line);
2858 break;
2859 case TAB_CENTER:
2860 tab_amount = tab_distance - tab_width/2;
2861 line = make_tab_node(tab_amount, line);
2862 break;
2863 case TAB_NONE:
2864 case TAB_LEFT:
2865 default:
2866 assert(0);
2867 }
2868 width_total += tab_amount;
2869 width_total += tab_width;
2870 if (current_field) {
2871 if (tab_precedes_field) {
2872 pre_field_width += tab_amount;
2873 tab_precedes_field = 0;
2874 }
2875 field_distance -= tab_amount;
2876 field_spaces += tab_field_spaces;
2877 }
2878 if (tab_contents != 0) {
2879 node *tem;
2880 for (tem = tab_contents; tem->next != 0; tem = tem->next)
2881 ;
2882 tem->next = line;
2883 line = tab_contents;
2884 }
2885 tab_field_spaces = 0;
2886 tab_contents = 0;
2887 tab_width = H0;
2888 tab_distance = H0;
2889 current_tab = TAB_NONE;
2890}
2891
2892node *environment::make_tab_node(hunits d, node *next)
2893{
2894 if (leader_node != 0 && d < 0) {
2895 error("motion generated by leader cannot be negative");
2896 delete leader_node;
2897 leader_node = 0;
2898 }
2899 if (!leader_node)
2900 return new hmotion_node(d, 1, 0, get_fill_color(), next);
2901 node *n = new hline_node(d, leader_node, next);
2902 leader_node = 0;
2903 return n;
2904}
2905
2906void environment::handle_tab(int is_leader)
2907{
2908 hunits d;
2909 hunits absolute;
2910 if (current_tab)
2911 wrap_up_tab();
2912 charinfo *ci = is_leader ? leader_char : tab_char;
2913 delete leader_node;
2914 leader_node = ci ? make_char_node(ci) : 0;
2915 tab_type t = distance_to_next_tab(&d, &absolute);
2916 switch (t) {
2917 case TAB_NONE:
2918 return;
2919 case TAB_LEFT:
465b256c 2920 add_node(make_tag("tab L", absolute.to_units()));
92d0a6a6 2921 add_node(make_tab_node(d));
92d0a6a6
JR
2922 return;
2923 case TAB_RIGHT:
465b256c 2924 add_node(make_tag("tab R", absolute.to_units()));
92d0a6a6
JR
2925 break;
2926 case TAB_CENTER:
465b256c 2927 add_node(make_tag("tab C", absolute.to_units()));
92d0a6a6
JR
2928 break;
2929 default:
2930 assert(0);
2931 }
2932 tab_width = 0;
2933 tab_distance = d;
2934 tab_contents = 0;
2935 current_tab = t;
2936 tab_field_spaces = 0;
2937}
2938
2939void environment::start_field()
2940{
2941 assert(!current_field);
2942 hunits d;
2943 if (distance_to_next_tab(&d) != TAB_NONE) {
2944 pre_field_width = get_text_length();
2945 field_distance = d;
2946 current_field = 1;
2947 field_spaces = 0;
2948 tab_field_spaces = 0;
2949 for (node *p = line; p; p = p->next)
2950 if (p->nspaces()) {
2951 p->freeze_space();
2952 space_total--;
2953 }
2954 tab_precedes_field = current_tab != TAB_NONE;
2955 }
2956 else
2957 error("zero field width");
2958}
2959
2960void environment::wrap_up_field()
2961{
2962 if (!current_tab && field_spaces == 0)
2963 add_padding();
2964 hunits padding = field_distance - (get_text_length() - pre_field_width);
2965 if (current_tab && tab_field_spaces != 0) {
2966 hunits tab_padding = scale(padding,
2967 tab_field_spaces,
2968 field_spaces + tab_field_spaces);
2969 padding -= tab_padding;
2970 distribute_space(tab_contents, tab_field_spaces, tab_padding, 1);
2971 tab_field_spaces = 0;
2972 tab_width += tab_padding;
2973 }
2974 if (field_spaces != 0) {
2975 distribute_space(line, field_spaces, padding, 1);
2976 width_total += padding;
2977 if (current_tab) {
2978 // the start of the tab has been moved to the right by padding, so
2979 tab_distance -= padding;
2980 if (tab_distance <= H0) {
2981 // use the next tab stop instead
2982 current_tab = tabs.distance_to_next_tab(get_input_line_position()
2983 - tab_width,
2984 &tab_distance);
2985 if (current_tab == TAB_NONE || current_tab == TAB_LEFT) {
2986 width_total += tab_width;
2987 if (current_tab == TAB_LEFT) {
2988 line = make_tab_node(tab_distance, line);
2989 width_total += tab_distance;
2990 current_tab = TAB_NONE;
2991 }
2992 if (tab_contents != 0) {
2993 node *tem;
2994 for (tem = tab_contents; tem->next != 0; tem = tem->next)
2995 ;
2996 tem->next = line;
2997 line = tab_contents;
2998 tab_contents = 0;
2999 }
3000 tab_width = H0;
3001 tab_distance = H0;
3002 }
3003 }
3004 }
3005 }
3006 current_field = 0;
3007}
3008
3009void environment::add_padding()
3010{
3011 if (current_tab) {
3012 tab_contents = new space_node(H0, get_fill_color(), tab_contents);
3013 tab_field_spaces++;
3014 }
3015 else {
3016 if (line == 0)
3017 start_line();
3018 line = new space_node(H0, get_fill_color(), line);
3019 field_spaces++;
3020 }
3021}
3022
3023typedef int (environment::*INT_FUNCP)();
3024typedef vunits (environment::*VUNITS_FUNCP)();
3025typedef hunits (environment::*HUNITS_FUNCP)();
3026typedef const char *(environment::*STRING_FUNCP)();
3027
3028class int_env_reg : public reg {
3029 INT_FUNCP func;
3030 public:
3031 int_env_reg(INT_FUNCP);
3032 const char *get_string();
3033 int get_value(units *val);
3034};
3035
3036class vunits_env_reg : public reg {
3037 VUNITS_FUNCP func;
3038 public:
3039 vunits_env_reg(VUNITS_FUNCP f);
3040 const char *get_string();
3041 int get_value(units *val);
3042};
3043
3044
3045class hunits_env_reg : public reg {
3046 HUNITS_FUNCP func;
3047 public:
3048 hunits_env_reg(HUNITS_FUNCP f);
3049 const char *get_string();
3050 int get_value(units *val);
3051};
3052
3053class string_env_reg : public reg {
3054 STRING_FUNCP func;
3055public:
3056 string_env_reg(STRING_FUNCP);
3057 const char *get_string();
3058};
3059
3060int_env_reg::int_env_reg(INT_FUNCP f) : func(f)
3061{
3062}
3063
3064int int_env_reg::get_value(units *val)
3065{
3066 *val = (curenv->*func)();
3067 return 1;
3068}
3069
3070const char *int_env_reg::get_string()
3071{
3072 return i_to_a((curenv->*func)());
3073}
3074
3075vunits_env_reg::vunits_env_reg(VUNITS_FUNCP f) : func(f)
3076{
3077}
3078
3079int vunits_env_reg::get_value(units *val)
3080{
3081 *val = (curenv->*func)().to_units();
3082 return 1;
3083}
3084
3085const char *vunits_env_reg::get_string()
3086{
3087 return i_to_a((curenv->*func)().to_units());
3088}
3089
3090hunits_env_reg::hunits_env_reg(HUNITS_FUNCP f) : func(f)
3091{
3092}
3093
3094int hunits_env_reg::get_value(units *val)
3095{
3096 *val = (curenv->*func)().to_units();
3097 return 1;
3098}
3099
3100const char *hunits_env_reg::get_string()
3101{
3102 return i_to_a((curenv->*func)().to_units());
3103}
3104
3105string_env_reg::string_env_reg(STRING_FUNCP f) : func(f)
3106{
3107}
3108
3109const char *string_env_reg::get_string()
3110{
3111 return (curenv->*func)();
3112}
3113
3114class horizontal_place_reg : public general_reg {
3115public:
3116 horizontal_place_reg();
3117 int get_value(units *);
3118 void set_value(units);
3119};
3120
3121horizontal_place_reg::horizontal_place_reg()
3122{
3123}
3124
3125int horizontal_place_reg::get_value(units *res)
3126{
3127 *res = curenv->get_input_line_position().to_units();
3128 return 1;
3129}
3130
3131void horizontal_place_reg::set_value(units n)
3132{
3133 curenv->set_input_line_position(hunits(n));
3134}
3135
4d3e9548
JL
3136int environment::get_zoom()
3137{
3138 return env_get_zoom(this);
3139}
3140
92d0a6a6
JR
3141const char *environment::get_font_family_string()
3142{
3143 return family->nm.contents();
3144}
3145
3146const char *environment::get_glyph_color_string()
3147{
3148 return glyph_color->nm.contents();
3149}
3150
3151const char *environment::get_fill_color_string()
3152{
3153 return fill_color->nm.contents();
3154}
3155
3156const char *environment::get_font_name_string()
3157{
3158 symbol f = get_font_name(fontno, this);
3159 return f.contents();
3160}
3161
465b256c
JR
3162const char *environment::get_style_name_string()
3163{
3164 symbol f = get_style_name(fontno);
3165 return f.contents();
3166}
3167
92d0a6a6
JR
3168const char *environment::get_name_string()
3169{
3170 return name.contents();
3171}
3172
3173// Convert a quantity in scaled points to ascii decimal fraction.
3174
3175const char *sptoa(int sp)
3176{
3177 assert(sp > 0);
3178 assert(sizescale > 0);
3179 if (sizescale == 1)
3180 return i_to_a(sp);
3181 if (sp % sizescale == 0)
3182 return i_to_a(sp/sizescale);
3183 // See if 1/sizescale is exactly representable as a decimal fraction,
3184 // ie its only prime factors are 2 and 5.
3185 int n = sizescale;
3186 int power2 = 0;
3187 while ((n & 1) == 0) {
3188 n >>= 1;
3189 power2++;
3190 }
3191 int power5 = 0;
3192 while ((n % 5) == 0) {
3193 n /= 5;
3194 power5++;
3195 }
3196 if (n == 1) {
3197 int decimal_point = power5 > power2 ? power5 : power2;
3198 if (decimal_point <= 10) {
3199 int factor = 1;
3200 int t;
3201 for (t = decimal_point - power2; --t >= 0;)
3202 factor *= 2;
3203 for (t = decimal_point - power5; --t >= 0;)
3204 factor *= 5;
3205 if (factor == 1 || sp <= INT_MAX/factor)
3206 return if_to_a(sp*factor, decimal_point);
3207 }
3208 }
3209 double s = double(sp)/double(sizescale);
3210 double factor = 10.0;
3211 double val = s;
3212 int decimal_point = 0;
3213 do {
3214 double v = ceil(s*factor);
3215 if (v > INT_MAX)
3216 break;
3217 val = v;
3218 factor *= 10.0;
3219 } while (++decimal_point < 10);
3220 return if_to_a(int(val), decimal_point);
3221}
3222
3223const char *environment::get_point_size_string()
3224{
3225 return sptoa(curenv->get_point_size());
3226}
3227
3228const char *environment::get_requested_point_size_string()
3229{
3230 return sptoa(curenv->get_requested_point_size());
3231}
3232
4d3e9548
JL
3233void environment::print_env()
3234{
3235 // at the time of calling .pev, those values are always zero or
3236 // meaningless:
3237 //
3238 // char_height, char_slant,
3239 // interrupted
3240 // current_tab, tab_width, tab_distance
3241 // current_field, field_distance, pre_field_width, field_spaces,
3242 // tab_field_spaces, tab_precedes_field
3243 // composite
3244 //
3245 errprint(" previous line length: %1u\n", prev_line_length.to_units());
3246 errprint(" line length: %1u\n", line_length.to_units());
3247 errprint(" previous title length: %1u\n", prev_title_length.to_units());
3248 errprint(" title length: %1u\n", title_length.to_units());
3249 errprint(" previous size: %1p (%2s)\n",
3250 prev_size.to_points(), prev_size.to_scaled_points());
3251 errprint(" size: %1p (%2s)\n",
3252 size.to_points(), size.to_scaled_points());
3253 errprint(" previous requested size: %1s\n", prev_requested_size);
3254 errprint(" requested size: %1s\n", requested_size);
3255 errprint(" previous font number: %1\n", prev_fontno);
3256 errprint(" font number: %1\n", fontno);
3257 errprint(" previous family: `%1'\n", prev_family->nm.contents());
3258 errprint(" family: `%1'\n", family->nm.contents());
3259 errprint(" space size: %1/36 em\n", space_size);
3260 errprint(" sentence space size: %1/36 em\n", sentence_space_size);
3261 errprint(" previous line interrupted: %1\n",
3262 prev_line_interrupted ? "yes" : "no");
3263 errprint(" fill mode: %1\n", fill ? "on" : "off");
3264 errprint(" adjust mode: %1\n",
3265 adjust_mode == ADJUST_LEFT
3266 ? "left"
3267 : adjust_mode == ADJUST_BOTH
3268 ? "both"
3269 : adjust_mode == ADJUST_CENTER
3270 ? "center"
3271 : "right");
3272 if (center_lines)
3273 errprint(" lines to center: %1\n", center_lines);
3274 if (right_justify_lines)
3275 errprint(" lines to right justify: %1\n", right_justify_lines);
3276 errprint(" previous vertical spacing: %1u\n",
3277 prev_vertical_spacing.to_units());
3278 errprint(" vertical spacing: %1u\n", vertical_spacing.to_units());
3279 errprint(" previous post-vertical spacing: %1u\n",
3280 prev_post_vertical_spacing.to_units());
3281 errprint(" post-vertical spacing: %1u\n",
3282 post_vertical_spacing.to_units());
3283 errprint(" previous line spacing: %1\n", prev_line_spacing);
3284 errprint(" line spacing: %1\n", line_spacing);
3285 errprint(" previous indentation: %1u\n", prev_indent.to_units());
3286 errprint(" indentation: %1u\n", indent.to_units());
3287 errprint(" temporary indentation: %1u\n", temporary_indent.to_units());
3288 errprint(" have temporary indentation: %1\n",
3289 have_temporary_indent ? "yes" : "no");
3290 errprint(" currently used indentation: %1u\n", saved_indent.to_units());
3291 errprint(" target text length: %1u\n", target_text_length.to_units());
3292 if (underline_lines) {
3293 errprint(" lines to underline: %1\n", underline_lines);
3294 errprint(" font number before underlining: %1\n", pre_underline_fontno);
3295 errprint(" underline spaces: %1\n", underline_spaces ? "yes" : "no");
3296 }
3297 if (input_trap.contents()) {
3298 errprint(" input trap macro: `%1'\n", input_trap.contents());
3299 errprint(" input trap line counter: %1\n", input_trap_count);
3300 errprint(" continued input trap: %1\n",
3301 continued_input_trap ? "yes" : "no");
3302 }
3303 errprint(" previous text length: %1u\n", prev_text_length.to_units());
3304 errprint(" total width: %1u\n", width_total.to_units());
3305 errprint(" total number of spaces: %1\n", space_total);
3306 errprint(" input line start: %1u\n", input_line_start.to_units());
3307 errprint(" line tabs: %1\n", line_tabs ? "yes" : "no");
3308 errprint(" discarding: %1\n", discarding ? "yes" : "no");
3309 errprint(" spread flag set: %1\n", spread_flag ? "yes" : "no"); // \p
3310 if (margin_character_node) {
3311 errprint(" margin character flags: %1\n",
3312 margin_character_flags == MARGIN_CHARACTER_ON
3313 ? "on"
3314 : margin_character_flags == MARGIN_CHARACTER_NEXT
3315 ? "next"
3316 : margin_character_flags == MARGIN_CHARACTER_ON
3317 | MARGIN_CHARACTER_NEXT
3318 ? "on, next"
3319 : "none");
3320 errprint(" margin character distance: %1u\n",
3321 margin_character_distance.to_units());
3322 }
3323 if (numbering_nodes) {
3324 errprint(" line number digit width: %1u\n",
3325 line_number_digit_width.to_units());
3326 errprint(" separation between number and text: %1 digit spaces\n",
3327 number_text_separation);
3328 errprint(" line number indentation: %1 digit spaces\n",
3329 line_number_indent);
3330 errprint(" print line numbers every %1line%1\n",
3331 line_number_multiple > 1 ? i_to_a(line_number_multiple) : "",
3332 line_number_multiple > 1 ? "s" : "");
3333 errprint(" lines not to enumerate: %1\n", no_number_count);
3334 }
3335 string hf = hyphenation_flags ? "on" : "off";
3336 if (hyphenation_flags & HYPHEN_LAST_LINE)
3337 hf += ", not last line";
3338 if (hyphenation_flags & HYPHEN_LAST_CHARS)
3339 hf += ", not last two chars";
3340 if (hyphenation_flags & HYPHEN_FIRST_CHARS)
3341 hf += ", not first two chars";
3342 hf += '\0';
3343 errprint(" hyphenation_flags: %1\n", hf.contents());
3344 errprint(" number of consecutive hyphenated lines: %1\n",
3345 hyphen_line_count);
3346 errprint(" maximum number of consecutive hyphenated lines: %1\n",
3347 hyphen_line_max);
3348 errprint(" hyphenation space: %1u\n", hyphenation_space.to_units());
3349 errprint(" hyphenation margin: %1u\n", hyphenation_margin.to_units());
3350#ifdef WIDOW_CONTROL
3351 errprint(" widow control: %1\n", widow_control ? "yes" : "no");
3352#endif /* WIDOW_CONTROL */
3353}
3354
3355void print_env()
3356{
3357 errprint("Current Environment:\n");
3358 curenv->print_env();
3359 for (int i = 0; i < NENVIRONMENTS; i++) {
3360 if (env_table[i]) {
3361 errprint("Environment %1:\n", i);
3362 if (env_table[i] != curenv)
3363 env_table[i]->print_env();
3364 else
3365 errprint(" current\n");
3366 }
3367 }
3368 dictionary_iterator iter(env_dictionary);
3369 symbol s;
3370 environment *e;
3371 while (iter.get(&s, (void **)&e)) {
3372 assert(!s.is_null());
3373 errprint("Environment %1:\n", s.contents());
3374 if (e != curenv)
3375 e->print_env();
3376 else
3377 errprint(" current\n");
3378 }
3379 fflush(stderr);
3380 skip_line();
3381}
3382
92d0a6a6
JR
3383#define init_int_env_reg(name, func) \
3384 number_reg_dictionary.define(name, new int_env_reg(&environment::func))
3385
3386#define init_vunits_env_reg(name, func) \
3387 number_reg_dictionary.define(name, new vunits_env_reg(&environment::func))
3388
3389#define init_hunits_env_reg(name, func) \
3390 number_reg_dictionary.define(name, new hunits_env_reg(&environment::func))
3391
3392#define init_string_env_reg(name, func) \
3393 number_reg_dictionary.define(name, new string_env_reg(&environment::func))
3394
3395void init_env_requests()
3396{
3397 init_request("ad", adjust);
3398 init_request("br", break_request);
3399 init_request("brp", break_spread_request);
3400 init_request("c2", no_break_control_char);
3401 init_request("cc", control_char);
3402 init_request("ce", center);
3403 init_request("cu", continuous_underline);
3404 init_request("ev", environment_switch);
3405 init_request("evc", environment_copy);
3406 init_request("fam", family_change);
3407 init_request("fc", field_characters);
3408 init_request("fi", fill);
465b256c 3409 init_request("fcolor", fill_color_change);
92d0a6a6 3410 init_request("ft", font_change);
465b256c 3411 init_request("gcolor", glyph_color_change);
92d0a6a6
JR
3412 init_request("hc", hyphen_char);
3413 init_request("hlm", hyphen_line_max_request);
3414 init_request("hy", hyphenate_request);
3415 init_request("hym", hyphenation_margin_request);
3416 init_request("hys", hyphenation_space_request);
3417 init_request("in", indent);
3418 init_request("it", input_trap);
3419 init_request("itc", input_trap_continued);
3420 init_request("lc", leader_character);
3421 init_request("linetabs", line_tabs_request);
3422 init_request("ll", line_length);
3423 init_request("ls", line_spacing);
3424 init_request("lt", title_length);
3425 init_request("mc", margin_character);
3426 init_request("na", no_adjust);
3427 init_request("nf", no_fill);
3428 init_request("nh", no_hyphenate);
3429 init_request("nm", number_lines);
3430 init_request("nn", no_number);
4d3e9548 3431 init_request("pev", print_env);
92d0a6a6
JR
3432 init_request("ps", point_size);
3433 init_request("pvs", post_vertical_spacing);
3434 init_request("rj", right_justify);
3435 init_request("sizes", override_sizes);
3436 init_request("ss", space_size);
3437 init_request("ta", set_tabs);
3438 init_request("ti", temporary_indent);
3439 init_request("tc", tab_character);
3440 init_request("tl", title);
3441 init_request("ul", underline);
3442 init_request("vs", vertical_spacing);
3443#ifdef WIDOW_CONTROL
3444 init_request("wdc", widow_control_request);
3445#endif /* WIDOW_CONTROL */
3446 init_int_env_reg(".b", get_bold);
3447 init_vunits_env_reg(".cdp", get_prev_char_depth);
3448 init_int_env_reg(".ce", get_center_lines);
3449 init_vunits_env_reg(".cht", get_prev_char_height);
3450 init_hunits_env_reg(".csk", get_prev_char_skew);
3451 init_string_env_reg(".ev", get_name_string);
3452 init_int_env_reg(".f", get_font);
3453 init_string_env_reg(".fam", get_font_family_string);
3454 init_string_env_reg(".fn", get_font_name_string);
3455 init_int_env_reg(".height", get_char_height);
3456 init_int_env_reg(".hlc", get_hyphen_line_count);
3457 init_int_env_reg(".hlm", get_hyphen_line_max);
3458 init_int_env_reg(".hy", get_hyphenation_flags);
3459 init_hunits_env_reg(".hym", get_hyphenation_margin);
3460 init_hunits_env_reg(".hys", get_hyphenation_space);
3461 init_hunits_env_reg(".i", get_indent);
3462 init_hunits_env_reg(".in", get_saved_indent);
3463 init_int_env_reg(".int", get_prev_line_interrupted);
3464 init_int_env_reg(".linetabs", get_line_tabs);
3465 init_hunits_env_reg(".lt", get_title_length);
3466 init_int_env_reg(".j", get_adjust_mode);
3467 init_hunits_env_reg(".k", get_text_length);
3468 init_int_env_reg(".L", get_line_spacing);
3469 init_hunits_env_reg(".l", get_line_length);
3470 init_hunits_env_reg(".ll", get_saved_line_length);
3471 init_string_env_reg(".M", get_fill_color_string);
3472 init_string_env_reg(".m", get_glyph_color_string);
3473 init_hunits_env_reg(".n", get_prev_text_length);
3474 init_int_env_reg(".ps", get_point_size);
3475 init_int_env_reg(".psr", get_requested_point_size);
3476 init_vunits_env_reg(".pvs", get_post_vertical_spacing);
3477 init_int_env_reg(".rj", get_right_justify_lines);
3478 init_string_env_reg(".s", get_point_size_string);
3479 init_int_env_reg(".slant", get_char_slant);
3480 init_int_env_reg(".ss", get_space_size);
3481 init_int_env_reg(".sss", get_sentence_space_size);
3482 init_string_env_reg(".sr", get_requested_point_size_string);
465b256c 3483 init_string_env_reg(".sty", get_style_name_string);
92d0a6a6
JR
3484 init_string_env_reg(".tabs", get_tabs);
3485 init_int_env_reg(".u", get_fill);
3486 init_vunits_env_reg(".v", get_vertical_spacing);
3487 init_hunits_env_reg(".w", get_prev_char_width);
4d3e9548 3488 init_int_env_reg(".zoom", get_zoom);
92d0a6a6
JR
3489 number_reg_dictionary.define("ct", new variable_reg(&ct_reg_contents));
3490 number_reg_dictionary.define("hp", new horizontal_place_reg);
3491 number_reg_dictionary.define("ln", new variable_reg(&next_line_number));
3492 number_reg_dictionary.define("rsb", new variable_reg(&rsb_reg_contents));
3493 number_reg_dictionary.define("rst", new variable_reg(&rst_reg_contents));
3494 number_reg_dictionary.define("sb", new variable_reg(&sb_reg_contents));
3495 number_reg_dictionary.define("skw", new variable_reg(&skw_reg_contents));
3496 number_reg_dictionary.define("ssc", new variable_reg(&ssc_reg_contents));
3497 number_reg_dictionary.define("st", new variable_reg(&st_reg_contents));
3498}
3499
3500// Hyphenation - TeX's hyphenation algorithm with a less fancy implementation.
3501
3502struct trie_node;
3503
3504class trie {
3505 trie_node *tp;
3506 virtual void do_match(int len, void *val) = 0;
3507 virtual void do_delete(void *) = 0;
3508 void delete_trie_node(trie_node *);
3509public:
3510 trie() : tp(0) {}
3511 virtual ~trie(); // virtual to shut up g++
3512 void insert(const char *, int, void *);
3513 // find calls do_match for each match it finds
3514 void find(const char *pat, int patlen);
3515 void clear();
3516};
3517
3518class hyphen_trie : private trie {
3519 int *h;
3520 void do_match(int i, void *v);
3521 void do_delete(void *v);
3522 void insert_pattern(const char *pat, int patlen, int *num);
3523 void insert_hyphenation(dictionary *ex, const char *pat, int patlen);
3524 int hpf_getc(FILE *f);
3525public:
3526 hyphen_trie() {}
3527 ~hyphen_trie() {}
3528 void hyphenate(const char *word, int len, int *hyphens);
3529 void read_patterns_file(const char *name, int append, dictionary *ex);
3530};
3531
3532struct hyphenation_language {
3533 symbol name;
3534 dictionary exceptions;
3535 hyphen_trie patterns;
3536 hyphenation_language(symbol nm) : name(nm), exceptions(501) {}
3537 ~hyphenation_language() { }
3538};
3539
3540dictionary language_dictionary(5);
3541hyphenation_language *current_language = 0;
3542
3543static void set_hyphenation_language()
3544{
3545 symbol nm = get_name(1);
3546 if (!nm.is_null()) {
3547 current_language = (hyphenation_language *)language_dictionary.lookup(nm);
3548 if (!current_language) {
3549 current_language = new hyphenation_language(nm);
3550 (void)language_dictionary.lookup(nm, (void *)current_language);
3551 }
3552 }
3553 skip_line();
3554}
3555
3556const int WORD_MAX = 256; // we use unsigned char for offsets in
3557 // hyphenation exceptions
3558
3559static void hyphen_word()
3560{
3561 if (!current_language) {
3562 error("no current hyphenation language");
3563 skip_line();
3564 return;
3565 }
3566 char buf[WORD_MAX + 1];
3567 unsigned char pos[WORD_MAX + 2];
3568 for (;;) {
3569 tok.skip();
3570 if (tok.newline() || tok.eof())
3571 break;
3572 int i = 0;
3573 int npos = 0;
3574 while (i < WORD_MAX && !tok.space() && !tok.newline() && !tok.eof()) {
3575 charinfo *ci = tok.get_char(1);
3576 if (ci == 0) {
3577 skip_line();
3578 return;
3579 }
3580 tok.next();
3581 if (ci->get_ascii_code() == '-') {
3582 if (i > 0 && (npos == 0 || pos[npos - 1] != i))
3583 pos[npos++] = i;
3584 }
3585 else {
3586 unsigned char c = ci->get_hyphenation_code();
3587 if (c == 0)
3588 break;
3589 buf[i++] = c;
3590 }
3591 }
3592 if (i > 0) {
3593 pos[npos] = 0;
3594 buf[i] = 0;
3595 unsigned char *tem = new unsigned char[npos + 1];
3596 memcpy(tem, pos, npos + 1);
3597 tem = (unsigned char *)current_language->exceptions.lookup(symbol(buf),
3598 tem);
3599 if (tem)
3600 a_delete tem;
3601 }
3602 }
3603 skip_line();
3604}
3605
3606struct trie_node {
3607 char c;
3608 trie_node *down;
3609 trie_node *right;
3610 void *val;
3611 trie_node(char, trie_node *);
3612};
3613
3614trie_node::trie_node(char ch, trie_node *p)
3615: c(ch), down(0), right(p), val(0)
3616{
3617}
3618
3619trie::~trie()
3620{
3621 clear();
3622}
3623
3624void trie::clear()
3625{
3626 delete_trie_node(tp);
3627 tp = 0;
3628}
3629
3630
3631void trie::delete_trie_node(trie_node *p)
3632{
3633 if (p) {
3634 delete_trie_node(p->down);
3635 delete_trie_node(p->right);
3636 if (p->val)
3637 do_delete(p->val);
3638 delete p;
3639 }
3640}
3641
3642void trie::insert(const char *pat, int patlen, void *val)
3643{
3644 trie_node **p = &tp;
3645 assert(patlen > 0 && pat != 0);
3646 for (;;) {
3647 while (*p != 0 && (*p)->c < pat[0])
3648 p = &((*p)->right);
3649 if (*p == 0 || (*p)->c != pat[0])
3650 *p = new trie_node(pat[0], *p);
3651 if (--patlen == 0) {
3652 (*p)->val = val;
3653 break;
3654 }
3655 ++pat;
3656 p = &((*p)->down);
3657 }
3658}
3659
3660void trie::find(const char *pat, int patlen)
3661{
3662 trie_node *p = tp;
3663 for (int i = 0; p != 0 && i < patlen; i++) {
3664 while (p != 0 && p->c < pat[i])
3665 p = p->right;
3666 if (p != 0 && p->c == pat[i]) {
3667 if (p->val != 0)
3668 do_match(i+1, p->val);
3669 p = p->down;
3670 }
3671 else
3672 break;
3673 }
3674}
3675
3676struct operation {
3677 operation *next;
3678 short distance;
3679 short num;
3680 operation(int, int, operation *);
3681};
3682
3683operation::operation(int i, int j, operation *op)
3684: next(op), distance(j), num(i)
3685{
3686}
3687
3688void hyphen_trie::insert_pattern(const char *pat, int patlen, int *num)
3689{
3690 operation *op = 0;
3691 for (int i = 0; i < patlen+1; i++)
3692 if (num[i] != 0)
3693 op = new operation(num[i], patlen - i, op);
3694 insert(pat, patlen, op);
3695}
3696
3697void hyphen_trie::insert_hyphenation(dictionary *ex, const char *pat,
3698 int patlen)
3699{
3700 char buf[WORD_MAX + 1];
3701 unsigned char pos[WORD_MAX + 2];
3702 int i = 0, j = 0;
3703 int npos = 0;
3704 while (j < patlen) {
3705 unsigned char c = pat[j++];
3706 if (c == '-') {
3707 if (i > 0 && (npos == 0 || pos[npos - 1] != i))
3708 pos[npos++] = i;
3709 }
3710 else
3711 buf[i++] = hpf_code_table[c];
3712 }
3713 if (i > 0) {
3714 pos[npos] = 0;
3715 buf[i] = 0;
3716 unsigned char *tem = new unsigned char[npos + 1];
3717 memcpy(tem, pos, npos + 1);
3718 tem = (unsigned char *)ex->lookup(symbol(buf), tem);
3719 if (tem)
3720 a_delete tem;
3721 }
3722}
3723
3724void hyphen_trie::hyphenate(const char *word, int len, int *hyphens)
3725{
3726 int j;
3727 for (j = 0; j < len + 1; j++)
3728 hyphens[j] = 0;
3729 for (j = 0; j < len - 1; j++) {
3730 h = hyphens + j;
3731 find(word + j, len - j);
3732 }
3733}
3734
3735inline int max(int m, int n)
3736{
3737 return m > n ? m : n;
3738}
3739
3740void hyphen_trie::do_match(int i, void *v)
3741{
3742 operation *op = (operation *)v;
3743 while (op != 0) {
3744 h[i - op->distance] = max(h[i - op->distance], op->num);
3745 op = op->next;
3746 }
3747}
3748
3749void hyphen_trie::do_delete(void *v)
3750{
3751 operation *op = (operation *)v;
3752 while (op) {
3753 operation *tem = op;
3754 op = tem->next;
3755 delete tem;
3756 }
3757}
3758
3759/* We use very simple rules to parse TeX's hyphenation patterns.
3760
3761 . `%' starts a comment even if preceded by `\'.
3762
3763 . No support for digraphs and like `\$'.
3764
3765 . `^^xx' (`x' is 0-9 or a-f), and `^^x' (character code of `x' in the
3766 range 0-127) are recognized; other use of `^' causes an error.
3767
3768 . No macro expansion.
3769
3770 . We check for the expression `\patterns{...}' (possibly with
3771 whitespace before and after the braces). Everything between the
3772 braces is taken as hyphenation patterns. Consequently, `{' and `}'
3773 are not allowed in patterns.
3774
3775 . Similarly, `\hyphenation{...}' gives a list of hyphenation
3776 exceptions.
3777
3778 . `\endinput' is recognized also.
3779
3780 . For backwards compatibility, if `\patterns' is missing, the
3781 whole file is treated as a list of hyphenation patterns (only
3782 recognizing `%' as the start of a comment.
3783
3784*/
3785
3786int hyphen_trie::hpf_getc(FILE *f)
3787{
3788 int c = getc(f);
3789 int c1;
3790 int cc = 0;
3791 if (c != '^')
3792 return c;
3793 c = getc(f);
3794 if (c != '^')
3795 goto fail;
3796 c = getc(f);
3797 c1 = getc(f);
3798 if (((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))
3799 && ((c1 >= '0' && c1 <= '9') || (c1 >= 'a' && c1 <= 'f'))) {
3800 if (c >= '0' && c <= '9')
3801 c -= '0';
3802 else
3803 c = c - 'a' + 10;
3804 if (c1 >= '0' && c1 <= '9')
3805 c1 -= '0';
3806 else
3807 c1 = c1 - 'a' + 10;
3808 cc = c * 16 + c1;
3809 }
3810 else {
3811 ungetc(c1, f);
3812 if (c >= 0 && c <= 63)
3813 cc = c + 64;
3814 else if (c >= 64 && c <= 127)
3815 cc = c - 64;
3816 else
3817 goto fail;
3818 }
3819 return cc;
3820fail:
3821 error("invalid ^, ^^x, or ^^xx character in hyphenation patterns file");
3822 return c;
3823}
3824
3825void hyphen_trie::read_patterns_file(const char *name, int append,
3826 dictionary *ex)
3827{
3828 if (!append)
3829 clear();
3830 char buf[WORD_MAX];
3831 for (int i = 0; i < WORD_MAX; i++)
3832 buf[i] = 0;
3833 int num[WORD_MAX+1];
3834 errno = 0;
3835 char *path = 0;
3836 FILE *fp = mac_path->open_file(name, &path);
3837 if (fp == 0) {
3838 error("can't find hyphenation patterns file `%1'", name);
3839 return;
3840 }
3841 int c = hpf_getc(fp);
3842 int have_patterns = 0; // we've seen \patterns
3843 int final_pattern = 0; // 1 if we have a trailing closing brace
3844 int have_hyphenation = 0; // we've seen \hyphenation
3845 int final_hyphenation = 0; // 1 if we have a trailing closing brace
3846 int have_keyword = 0; // we've seen either \patterns or \hyphenation
3847 int traditional = 0; // don't handle \patterns
3848 for (;;) {
3849 for (;;) {
3850 if (c == '%') { // skip comments
3851 do {
3852 c = getc(fp);
3853 } while (c != EOF && c != '\n');
3854 }
3855 if (c == EOF || !csspace(c))
3856 break;
3857 c = hpf_getc(fp);
3858 }
3859 if (c == EOF) {
3860 if (have_keyword || traditional) // we are done
3861 break;
3862 else { // rescan file in `traditional' mode
3863 rewind(fp);
3864 traditional = 1;
3865 c = hpf_getc(fp);
3866 continue;
3867 }
3868 }
3869 int i = 0;
3870 num[0] = 0;
3871 if (!(c == '{' || c == '}')) { // skip braces at line start
3872 do { // scan patterns
3873 if (csdigit(c))
3874 num[i] = c - '0';
3875 else {
3876 buf[i++] = c;
3877 num[i] = 0;
3878 }
3879 c = hpf_getc(fp);
3880 } while (i < WORD_MAX && c != EOF && !csspace(c)
3881 && c != '%' && c != '{' && c != '}');
3882 }
3883 if (!traditional) {
3884 if (i >= 9 && !strncmp(buf + i - 9, "\\patterns", 9)) {
3885 while (csspace(c))
3886 c = hpf_getc(fp);
3887 if (c == '{') {
3888 if (have_patterns || have_hyphenation)
3889 error("\\patterns not allowed inside of %1 group",
3890 have_patterns ? "\\patterns" : "\\hyphenation");
3891 else {
3892 have_patterns = 1;
3893 have_keyword = 1;
3894 }
3895 c = hpf_getc(fp);
3896 continue;
3897 }
3898 }
3899 else if (i >= 12 && !strncmp(buf + i - 12, "\\hyphenation", 12)) {
3900 while (csspace(c))
3901 c = hpf_getc(fp);
3902 if (c == '{') {
3903 if (have_patterns || have_hyphenation)
3904 error("\\hyphenation not allowed inside of %1 group",
3905 have_patterns ? "\\patterns" : "\\hyphenation");
3906 else {
3907 have_hyphenation = 1;
3908 have_keyword = 1;
3909 }
3910 c = hpf_getc(fp);
3911 continue;
3912 }
3913 }
3914 else if (strstr(buf, "\\endinput")) {
3915 if (have_patterns || have_hyphenation)
3916 error("found \\endinput inside of %1 group",
3917 have_patterns ? "\\patterns" : "\\hyphenation");
3918 break;
3919 }
3920 else if (c == '}') {
3921 if (have_patterns) {
3922 have_patterns = 0;
3923 if (i > 0)
3924 final_pattern = 1;
3925 }
3926 else if (have_hyphenation) {
3927 have_hyphenation = 0;
3928 if (i > 0)
3929 final_hyphenation = 1;
3930 }
3931 c = hpf_getc(fp);
3932 }
3933 else if (c == '{') {
3934 if (have_patterns || have_hyphenation)
3935 error("`{' not allowed within %1 group",
3936 have_patterns ? "\\patterns" : "\\hyphenation");
3937 c = hpf_getc(fp); // skipped if not starting \patterns
3938 // or \hyphenation
3939 }
3940 }
3941 else {
3942 if (c == '{' || c == '}')
3943 c = hpf_getc(fp);
3944 }
3945 if (i > 0) {
3946 if (have_patterns || final_pattern || traditional) {
3947 for (int j = 0; j < i; j++)
3948 buf[j] = hpf_code_table[(unsigned char)buf[j]];
3949 insert_pattern(buf, i, num);
3950 final_pattern = 0;
3951 }
3952 else if (have_hyphenation || final_hyphenation) {
3953 insert_hyphenation(ex, buf, i);
3954 final_hyphenation = 0;
3955 }
3956 }
3957 }
3958 fclose(fp);
3959 a_delete path;
3960 return;
3961}
3962
3963void hyphenate(hyphen_list *h, unsigned flags)
3964{
3965 if (!current_language)
3966 return;
3967 while (h) {
3968 while (h && h->hyphenation_code == 0)
3969 h = h->next;
3970 int len = 0;
4d3e9548 3971 char hbuf[WORD_MAX + 2];
92d0a6a6
JR
3972 char *buf = hbuf + 1;
3973 hyphen_list *tem;
3974 for (tem = h; tem && len < WORD_MAX; tem = tem->next) {
3975 if (tem->hyphenation_code != 0)
3976 buf[len++] = tem->hyphenation_code;
3977 else
3978 break;
3979 }
3980 hyphen_list *nexth = tem;
3981 if (len > 2) {
3982 buf[len] = 0;
3983 unsigned char *pos
3984 = (unsigned char *)current_language->exceptions.lookup(buf);
3985 if (pos != 0) {
3986 int j = 0;
3987 int i = 1;
3988 for (tem = h; tem != 0; tem = tem->next, i++)
3989 if (pos[j] == i) {
3990 tem->hyphen = 1;
3991 j++;
3992 }
3993 }
3994 else {
4d3e9548
JL
3995 hbuf[0] = hbuf[len + 1] = '.';
3996 int num[WORD_MAX + 3];
3997 current_language->patterns.hyphenate(hbuf, len + 2, num);
92d0a6a6
JR
3998 int i;
3999 num[2] = 0;
4d3e9548 4000 if (flags & HYPHEN_FIRST_CHARS)
92d0a6a6 4001 num[3] = 0;
4d3e9548 4002 if (flags & HYPHEN_LAST_CHARS)
92d0a6a6
JR
4003 --len;
4004 for (i = 2, tem = h; i < len && tem; tem = tem->next, i++)
4005 if (num[i] & 1)
4006 tem->hyphen = 1;
4007 }
4008 }
4009 h = nexth;
4010 }
4011}
4012
4013static void do_hyphenation_patterns_file(int append)
4014{
4015 symbol name = get_long_name(1);
4016 if (!name.is_null()) {
4017 if (!current_language)
4018 error("no current hyphenation language");
4019 else
4020 current_language->patterns.read_patterns_file(
4021 name.contents(), append,
4022 &current_language->exceptions);
4023 }
4024 skip_line();
4025}
4026
4027static void hyphenation_patterns_file()
4028{
4029 do_hyphenation_patterns_file(0);
4030}
4031
4032static void hyphenation_patterns_file_append()
4033{
4034 do_hyphenation_patterns_file(1);
4035}
4036
4037class hyphenation_language_reg : public reg {
4038public:
4039 const char *get_string();
4040};
4041
4042const char *hyphenation_language_reg::get_string()
4043{
4044 return current_language ? current_language->name.contents() : "";
4045}
4046
4047void init_hyphen_requests()
4048{
4049 init_request("hw", hyphen_word);
4050 init_request("hla", set_hyphenation_language);
4051 init_request("hpf", hyphenation_patterns_file);
4052 init_request("hpfa", hyphenation_patterns_file_append);
4053 number_reg_dictionary.define(".hla", new hyphenation_language_reg);
4054}