2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005,
4 Free Software Foundation, Inc.
5 Written by James Clark (jjc@jclark.com)
7 This file is part of groff.
9 groff is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 groff is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>. */
23 #include "dictionary.h"
25 #include "stringclass.h"
35 #include "macropath.h"
39 symbol default_family("T");
41 enum { ADJUST_LEFT = 0, ADJUST_BOTH = 1, ADJUST_CENTER = 3, ADJUST_RIGHT = 5 };
43 enum { HYPHEN_LAST_LINE = 2, HYPHEN_LAST_CHARS = 4, HYPHEN_FIRST_CHARS = 8 };
48 env_list(environment *e, env_list *p) : env(e), next(p) {}
52 const int NENVIRONMENTS = 10;
53 environment *env_table[NENVIRONMENTS];
54 dictionary env_dictionary(10);
56 static int next_line_number = 0;
57 extern int suppress_push;
58 extern statem *get_diversion_state();
60 charinfo *field_delimiter_char;
61 charinfo *padding_indicator_char;
63 int translate_space_to_dummy = 0;
65 class pending_output_line {
73 int last_line; // Is it the last line of the paragraph?
74 #endif /* WIDOW_CONTROL */
76 pending_output_line *next;
78 pending_output_line(node *, int, vunits, vunits, hunits, int,
79 pending_output_line * = 0);
80 ~pending_output_line();
84 friend void environment::mark_last_line();
85 friend void environment::output(node *, int, vunits, vunits, hunits, int);
86 #endif /* WIDOW_CONTROL */
89 pending_output_line::pending_output_line(node *n, int nf, vunits v, vunits pv,
91 pending_output_line *p)
92 : nd(n), no_fill(nf), was_centered(ce), vs(v), post_vs(pv), width(w),
95 #endif /* WIDOW_CONTROL */
100 pending_output_line::~pending_output_line()
102 delete_node_list(nd);
105 int pending_output_line::output()
107 if (trap_sprung_flag)
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.
118 curenv->construct_format_state(nd, was_centered, !no_fill);
119 curdiv->output(nd, no_fill, vs, post_vs, width);
124 void environment::output(node *nd, int no_fill_flag,
125 vunits vs, vunits post_vs,
126 hunits width, int was_centered)
129 while (pending_lines) {
130 if (widow_control && !pending_lines->no_fill && !pending_lines->next)
132 if (!pending_lines->output())
134 pending_output_line *tem = pending_lines;
135 pending_lines = pending_lines->next;
138 #else /* WIDOW_CONTROL */
139 output_pending_lines();
140 #endif /* WIDOW_CONTROL */
141 if (!trap_sprung_flag && !pending_lines
143 && (!widow_control || no_fill_flag)
144 #endif /* WIDOW_CONTROL */
146 curenv->construct_format_state(nd, was_centered, !no_fill_flag);
147 curdiv->output(nd, no_fill_flag, vs, post_vs, width);
149 pending_output_line **p;
150 for (p = &pending_lines; *p; p = &(*p)->next)
152 *p = new pending_output_line(nd, no_fill_flag, vs, post_vs, width,
157 // a line from .tl goes at the head of the queue
159 void environment::output_title(node *nd, int no_fill_flag,
160 vunits vs, vunits post_vs,
163 if (!trap_sprung_flag)
164 curdiv->output(nd, no_fill_flag, vs, post_vs, width);
166 pending_lines = new pending_output_line(nd, no_fill_flag, vs, post_vs,
167 width, 0, pending_lines);
170 void environment::output_pending_lines()
172 while (pending_lines && pending_lines->output()) {
173 pending_output_line *tem = pending_lines;
174 pending_lines = pending_lines->next;
181 void environment::mark_last_line()
183 if (!widow_control || !pending_lines)
185 pending_output_line *p;
186 for (p = pending_lines; p->next; p = p->next)
192 void widow_control_request()
195 if (has_arg() && get_integer(&n))
196 curenv->widow_control = n != 0;
198 curenv->widow_control = 1;
202 #endif /* WIDOW_CONTROL */
204 /* font_size functions */
206 size_range *font_size::size_table = 0;
207 int font_size::nranges = 0;
211 int compare_ranges(const void *p1, const void *p2)
213 return ((size_range *)p1)->min - ((size_range *)p2)->min;
218 void font_size::init_size_table(int *sizes)
221 while (sizes[nranges*2] != 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];
229 qsort(size_table, nranges, sizeof(size_range), compare_ranges);
232 font_size::font_size(int sp)
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;
239 p = size_table[i].min;
242 if (sp <= size_table[i].max) {
247 p = size_table[nranges - 1].max;
250 int font_size::to_units()
252 return scale(p, units_per_inch, sizescale*72);
255 // we can't do this in a static constructor because various dictionaries
256 // have to get initialized first
258 void init_environments()
260 curenv = env_table[0] = new environment("0");
265 curenv->tab_char = get_optional_char();
269 void leader_character()
271 curenv->leader_char = get_optional_char();
275 void environment::add_char(charinfo *ci)
281 // don't allow fields in dummy environments
282 else if (ci == field_delimiter_char && !dummy) {
288 else if (current_field && ci == padding_indicator_char)
290 else if (current_tab) {
291 if (tab_contents == 0)
292 tab_contents = new line_start_node;
293 if (ci != hyphen_indicator_char)
294 tab_contents = tab_contents->add_char(ci, this, &tab_width, &s, &gc_np);
296 tab_contents = tab_contents->add_discretionary_hyphen();
302 fprintf(stderr, "current line is\n");
303 line->debug_node_list();
305 if (ci != hyphen_indicator_char)
306 line = line->add_char(ci, this, &width_total, &space_total, &gc_np);
308 line = line->add_discretionary_hyphen();
311 fprintf(stderr, "now after we have added character the line is\n");
312 line->debug_node_list();
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();
319 else if (line && (line->state == 0)) {
320 line->state = construct_state(0);
321 line->push_state = get_diversion_state();
325 fprintf(stderr, "now we have possibly added the state the line is\n");
326 line->debug_node_list();
330 node *environment::make_char_node(charinfo *ci)
332 return make_node(ci, this);
335 void environment::add_node(node *n)
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();
345 if (current_tab || current_field)
350 else if (current_tab) {
351 n->next = tab_contents;
353 tab_width += n->width();
357 if (discarding && n->discardable()) {
358 // XXX possibly: input_line_start -= n->width();
364 width_total += n->width();
365 space_total += n->nspaces();
368 construct_new_line_state(line);
372 void environment::add_hyphen_indicator()
374 if (current_tab || interrupted || current_field
375 || hyphen_indicator_char != 0)
379 line = line->add_discretionary_hyphen();
382 int environment::get_hyphenation_flags()
384 return hyphenation_flags;
387 int environment::get_hyphen_line_max()
389 return hyphen_line_max;
392 int environment::get_hyphen_line_count()
394 return hyphen_line_count;
397 int environment::get_center_lines()
402 int environment::get_right_justify_lines()
404 return right_justify_lines;
407 void environment::add_italic_correction()
411 tab_contents = tab_contents->add_italic_correction(&tab_width);
414 line = line->add_italic_correction(&width_total);
417 void environment::space_newline()
419 assert(!current_tab && !current_field);
423 hunits sw = env_space_width(this);
424 hunits ssw = env_sentence_space_width(this);
425 if (!translate_space_to_dummy) {
427 if (node_list_ends_sentence(line) == 1)
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)) {
437 add_node(new word_space_node(x, get_fill_color(), w));
438 possibly_break_line(0, spread_flag);
442 void environment::space()
444 space(env_space_width(this), env_sentence_space_width(this));
447 void environment::space(hunits space_width, hunits sentence_space_width)
451 if (current_field && padding_indicator_char == 0) {
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)) {
466 if (p && p->merge_space(x, space_width, sentence_space_width)) {
470 add_node(new word_space_node(x,
472 new width_list(space_width,
473 sentence_space_width)));
474 possibly_break_line(0, spread_flag);
478 node *do_underline_special(int);
480 void environment::set_font(symbol nm)
484 if (nm == symbol("P") || nm.is_empty()) {
485 if (family->make_definite(prev_fontno) < 0)
488 fontno = prev_fontno;
492 prev_fontno = fontno;
493 int n = symbol_fontno(nm);
495 n = next_available_font_position();
496 if (!mount_font(n, nm))
499 if (family->make_definite(n) < 0)
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));
511 void environment::set_font(int n)
515 if (is_good_fontno(n)) {
516 prev_fontno = fontno;
520 warning(WARN_FONT, "bad font number");
523 void environment::set_family(symbol fam)
527 if (fam.is_null() || fam.is_empty()) {
528 if (prev_family->make_definite(fontno) < 0)
530 font_family *tem = family;
531 family = prev_family;
535 font_family *f = lookup_family(fam);
536 if (f->make_definite(fontno) < 0)
538 prev_family = family;
543 void environment::set_size(int n)
548 font_size temp = prev_size;
551 int temp2 = prev_requested_size;
552 prev_requested_size = requested_size;
553 requested_size = temp2;
558 prev_requested_size = requested_size;
563 void environment::set_char_height(int n)
567 if (n == requested_size || n <= 0)
573 void environment::set_char_slant(int n)
580 color *environment::get_prev_glyph_color()
582 return prev_glyph_color;
585 color *environment::get_glyph_color()
590 color *environment::get_prev_fill_color()
592 return prev_fill_color;
595 color *environment::get_fill_color()
600 void environment::set_glyph_color(color *c)
604 curenv->prev_glyph_color = curenv->glyph_color;
605 curenv->glyph_color = c;
608 void environment::set_fill_color(color *c)
612 curenv->prev_fill_color = curenv->fill_color;
613 curenv->fill_color = c;
616 environment::environment(symbol nm)
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),
624 requested_size(sizescale*10),
625 prev_requested_size(sizescale*10),
629 sentence_space_size(12),
630 adjust_mode(ADJUST_BOTH),
633 prev_line_interrupted(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),
645 have_temporary_indent(0),
649 continued_input_trap(0),
656 current_tab(TAB_NONE),
659 leader_char(charset_table['.']),
663 margin_character_flags(0),
664 margin_character_node(0),
665 margin_character_distance(points_to_units(10)),
667 number_text_separation(1),
668 line_number_indent(0),
669 line_number_multiple(1),
671 hyphenation_flags(1),
672 hyphen_line_count(0),
674 hyphenation_space(H0),
675 hyphenation_margin(H0),
680 #endif /* WIDOW_CONTROL */
681 glyph_color(&default_color),
682 prev_glyph_color(&default_color),
683 fill_color(&default_color),
684 prev_fill_color(&default_color),
687 suppress_next_eol(0),
689 tabs(units_per_inch/2, TAB_LEFT),
692 no_break_control_char('\''),
693 hyphen_indicator_char(0)
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;
704 environment::environment(const environment *e)
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),
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),
718 prev_family(e->prev_family),
720 space_size(e->space_size),
721 sentence_space_size(e->sentence_space_size),
722 adjust_mode(e->adjust_mode),
725 prev_line_interrupted(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),
737 have_temporary_indent(0),
741 continued_input_trap(0),
743 prev_text_length(e->prev_text_length),
747 line_tabs(e->line_tabs),
748 current_tab(TAB_NONE),
750 tab_char(e->tab_char),
751 leader_char(e->leader_char),
755 margin_character_flags(e->margin_character_flags),
756 margin_character_node(e->margin_character_node),
757 margin_character_distance(e->margin_character_distance),
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),
771 widow_control(e->widow_control),
772 #endif /* WIDOW_CONTROL */
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),
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),
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)
789 void environment::copy(const environment *e)
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;
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;
806 prev_line_interrupted = 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;
817 have_temporary_indent = 0;
818 temporary_indent = 0;
820 underline_spaces = 0;
821 input_trap_count = 0;
822 continued_input_trap = 0;
823 prev_text_length = e->prev_text_length;
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;
835 line_tabs = e->line_tabs;
836 current_tab = TAB_NONE;
838 margin_character_flags = e->margin_character_flags;
839 if (e->margin_character_node)
840 margin_character_node = e->margin_character_node->copy();
841 margin_character_distance = e->margin_character_distance;
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;
851 prev_fontno = e->prev_fontno;
854 prev_family = e->prev_family;
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;
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;
870 environment::~environment()
873 delete_node_list(line);
874 delete_node_list(numbering_nodes);
877 hunits environment::get_input_line_position()
881 n = -input_line_start;
883 n = width_total - input_line_start;
889 void environment::set_input_line_position(hunits n)
891 input_line_start = line == 0 ? -n : width_total - n;
893 input_line_start += tab_width;
896 hunits environment::get_line_length()
901 hunits environment::get_saved_line_length()
904 return target_text_length + saved_indent;
909 vunits environment::get_vertical_spacing()
911 return vertical_spacing;
914 vunits environment::get_post_vertical_spacing()
916 return post_vertical_spacing;
919 int environment::get_line_spacing()
924 vunits environment::total_post_vertical_spacing()
926 vunits tem(post_vertical_spacing);
927 if (line_spacing > 1)
928 tem += (line_spacing - 1)*vertical_spacing;
932 int environment::get_bold()
934 return get_bold_fontno(fontno);
937 hunits environment::get_digit_width()
939 return env_digit_width(this);
942 int environment::get_adjust_mode()
947 int environment::get_fill()
952 hunits environment::get_indent()
957 hunits environment::get_saved_indent()
961 else if (have_temporary_indent)
962 return temporary_indent;
967 hunits environment::get_temporary_indent()
969 return temporary_indent;
972 hunits environment::get_title_length()
977 node *environment::get_prev_char()
979 for (node *n = current_tab ? tab_contents : line; n; n = n->next) {
980 node *last = n->last_char_node();
987 hunits environment::get_prev_char_width()
989 node *last = get_prev_char();
992 return last->width();
995 hunits environment::get_prev_char_skew()
997 node *last = get_prev_char();
1000 return last->skew();
1003 vunits environment::get_prev_char_height()
1005 node *last = get_prev_char();
1009 last->vertical_extent(&min, &max);
1013 vunits environment::get_prev_char_depth()
1015 node *last = get_prev_char();
1019 last->vertical_extent(&min, &max);
1023 hunits environment::get_text_length()
1025 hunits n = line == 0 ? H0 : width_total;
1031 hunits environment::get_prev_text_length()
1033 return prev_text_length;
1037 static int sb_reg_contents = 0;
1038 static int st_reg_contents = 0;
1039 static int ct_reg_contents = 0;
1040 static int rsb_reg_contents = 0;
1041 static int rst_reg_contents = 0;
1042 static int skw_reg_contents = 0;
1043 static int ssc_reg_contents = 0;
1045 void environment::width_registers()
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;
1056 for (node *tem = line; tem; tem = tem->next) {
1057 tem->vertical_extent(&v1, &v2);
1064 if ((cur += tem->vertical_width()) < min)
1068 character_type |= tem->character_type();
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;
1078 node *environment::extract_output_line()
1087 /* environment related requests */
1089 void environment_switch()
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())
1098 if (!tok.delimiter()) {
1099 // It looks like a number.
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];
1115 nm = get_long_name(1);
1119 if (!nm.is_null()) {
1120 environment *e = (environment *)env_dictionary.lookup(nm);
1122 e = new environment(nm);
1123 (void)env_dictionary.lookup(nm, e);
1125 env_stack = new env_list(curenv, env_stack);
1130 if (env_stack == 0) {
1132 error("environment stack underflow");
1135 int seen_space = curenv->seen_space;
1136 int seen_eol = curenv->seen_eol;
1137 int suppress_next_eol = curenv->suppress_next_eol;
1138 curenv = env_stack->env;
1139 curenv->seen_space = seen_space;
1140 curenv->seen_eol = seen_eol;
1141 curenv->suppress_next_eol = suppress_next_eol;
1142 env_list *tem = env_stack;
1143 env_stack = env_stack->next;
1150 void environment_copy()
1155 if (!tok.delimiter()) {
1156 // It looks like a number.
1158 if (get_integer(&n)) {
1159 if (n >= 0 && n < NENVIRONMENTS)
1166 nm = get_long_name(1);
1167 if (!e && !nm.is_null())
1168 e = (environment *)env_dictionary.lookup(nm);
1170 error("No environment to copy from");
1178 void fill_color_change()
1180 symbol s = get_name();
1182 curenv->set_fill_color(curenv->get_prev_fill_color());
1188 void glyph_color_change()
1190 symbol s = get_name();
1192 curenv->set_glyph_color(curenv->get_prev_glyph_color());
1198 static symbol P_symbol("P");
1202 symbol s = get_name();
1204 if (s.is_null() || s == P_symbol) {
1209 for (const char *p = s.contents(); p != 0 && *p != 0; p++)
1216 curenv->set_font(atoi(s.contents()));
1218 curenv->set_font(s);
1222 void family_change()
1224 symbol s = get_name();
1225 curenv->set_family(s);
1232 if (has_arg() && get_number(&n, 'z', curenv->get_requested_point_size())) {
1235 curenv->set_size(n);
1238 curenv->set_size(0);
1242 void override_sizes()
1245 int *sizes = new int[n];
1247 char *buf = read_string();
1250 char *p = strtok(buf, " \t");
1255 switch (sscanf(p, "%d-%d", &lower, &upper)) {
1260 if (lower <= upper && lower >= 0)
1264 warning(WARN_RANGE, "bad size range `%1'", p);
1268 int *old_sizes = sizes;
1269 sizes = new int[n*2];
1270 memcpy(sizes, old_sizes, n*sizeof(int));
1278 p = strtok(0, " \t");
1280 font_size::init_size_table(sizes);
1286 if (get_integer(&n)) {
1287 curenv->space_size = n;
1288 if (has_arg() && get_integer(&n))
1289 curenv->sentence_space_size = n;
1291 curenv->sentence_space_size = curenv->space_size;
1298 while (!tok.newline() && !tok.eof())
1308 while (!tok.newline() && !tok.eof())
1313 curenv->suppress_next_eol = 1;
1320 if (!has_arg() || !get_integer(&n))
1324 while (!tok.newline() && !tok.eof())
1328 curenv->right_justify_lines = 0;
1329 curenv->center_lines = n;
1330 curdiv->modified_tag.incl(MTSM_CE);
1334 void right_justify()
1337 if (!has_arg() || !get_integer(&n))
1341 while (!tok.newline() && !tok.eof())
1345 curenv->center_lines = 0;
1346 curenv->right_justify_lines = n;
1347 curdiv->modified_tag.incl(MTSM_RJ);
1354 if (has_arg() && get_hunits(&temp, 'm', curenv->line_length)) {
1356 warning(WARN_RANGE, "bad line length %1u", temp.to_units());
1361 temp = curenv->prev_line_length;
1362 curenv->prev_line_length = curenv->line_length;
1363 curenv->line_length = temp;
1364 curdiv->modified_tag.incl(MTSM_LL);
1371 if (has_arg() && get_hunits(&temp, 'm', curenv->title_length)) {
1373 warning(WARN_RANGE, "bad title length %1u", temp.to_units());
1378 temp = curenv->prev_title_length;
1379 curenv->prev_title_length = curenv->title_length;
1380 curenv->title_length = temp;
1384 void vertical_spacing()
1387 if (has_arg() && get_vunits(&temp, 'p', curenv->vertical_spacing)) {
1389 warning(WARN_RANGE, "vertical spacing must not be negative");
1394 temp = curenv->prev_vertical_spacing;
1395 curenv->prev_vertical_spacing = curenv->vertical_spacing;
1396 curenv->vertical_spacing = temp;
1400 void post_vertical_spacing()
1403 if (has_arg() && get_vunits(&temp, 'p', curenv->post_vertical_spacing)) {
1406 "post vertical spacing must be greater than or equal to 0");
1411 temp = curenv->prev_post_vertical_spacing;
1412 curenv->prev_post_vertical_spacing = curenv->post_vertical_spacing;
1413 curenv->post_vertical_spacing = temp;
1420 if (has_arg() && get_integer(&temp)) {
1422 warning(WARN_RANGE, "value %1 out of range: interpreted as 1", temp);
1427 temp = curenv->prev_line_spacing;
1428 curenv->prev_line_spacing = curenv->line_spacing;
1429 curenv->line_spacing = temp;
1436 if (has_arg() && get_hunits(&temp, 'm', curenv->indent)) {
1438 warning(WARN_RANGE, "indent cannot be negative");
1443 temp = curenv->prev_indent;
1444 while (!tok.newline() && !tok.eof())
1448 curenv->have_temporary_indent = 0;
1449 curenv->prev_indent = curenv->indent;
1450 curenv->indent = temp;
1451 curdiv->modified_tag.incl(MTSM_IN);
1455 void temporary_indent()
1459 if (!get_hunits(&temp, 'm', curenv->get_indent()))
1461 while (!tok.newline() && !tok.eof())
1466 warning(WARN_RANGE, "total indent cannot be negative");
1470 curenv->temporary_indent = temp;
1471 curenv->have_temporary_indent = 1;
1472 curdiv->modified_tag.incl(MTSM_TI);
1477 node *do_underline_special(int underline_spaces)
1480 m.append_str("x u ");
1481 m.append(underline_spaces + '0');
1482 return new special_node(m, 1);
1485 void do_underline(int underline_spaces)
1488 if (!has_arg() || !get_integer(&n))
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));
1499 curenv->underline_lines = 0;
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));
1513 void continuous_underline()
1525 curenv->control_char = '.';
1528 error("bad control character");
1530 curenv->control_char = tok.ch();
1535 void no_break_control_char()
1537 curenv->no_break_control_char = '\'';
1540 error("bad control character");
1542 curenv->no_break_control_char = tok.ch();
1547 void margin_character()
1551 charinfo *ci = tok.get_char();
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);
1558 delete curenv->margin_character_node;
1559 curenv->margin_character_node = nd;
1560 curenv->margin_character_flags = MARGIN_CHARACTER_ON
1561 | MARGIN_CHARACTER_NEXT;
1563 if (has_arg() && get_hunits(&d, 'm'))
1564 curenv->margin_character_distance = d;
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;
1580 delete_node_list(curenv->numbering_nodes);
1581 curenv->numbering_nodes = 0;
1584 for (int i = '9'; i >= '0'; i--) {
1585 node *tem = make_node(charset_table[i], curenv);
1593 curenv->numbering_nodes = nd;
1594 curenv->line_number_digit_width = env_digit_width(curenv);
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;
1606 while (!tok.space() && !tok.newline() && !tok.eof())
1609 if (!tok.delimiter()) {
1610 if (get_integer(&n)) {
1612 warning(WARN_RANGE, "negative or zero line number multiple");
1615 curenv->line_number_multiple = n;
1619 while (!tok.space() && !tok.newline() && !tok.eof())
1622 if (!tok.delimiter()) {
1623 if (get_integer(&n))
1624 curenv->number_text_separation = n;
1627 while (!tok.space() && !tok.newline() && !tok.eof())
1629 if (has_arg() && !tok.delimiter() && get_integer(&n))
1630 curenv->line_number_indent = n;
1640 if (has_arg() && get_integer(&n))
1641 curenv->no_number_count = n > 0 ? n : 0;
1643 curenv->no_number_count = 1;
1649 curenv->hyphenation_flags = 0;
1653 void hyphenate_request()
1656 if (has_arg() && get_integer(&n))
1657 curenv->hyphenation_flags = n;
1659 curenv->hyphenation_flags = 1;
1665 curenv->hyphen_indicator_char = get_optional_char();
1669 void hyphen_line_max_request()
1672 if (has_arg() && get_integer(&n))
1673 curenv->hyphen_line_max = n;
1675 curenv->hyphen_line_max = -1;
1679 void environment::interrupt()
1682 add_node(new transparent_dummy_node);
1687 void environment::newline()
1689 int was_centered = 0;
1690 if (underline_lines > 0) {
1691 if (--underline_lines == 0) {
1692 prev_fontno = fontno;
1693 fontno = pre_underline_fontno;
1694 if (underline_spaces) {
1695 underline_spaces = 0;
1696 add_node(do_underline_special(0));
1704 // strip trailing spaces
1705 while (line != 0 && line->discardable()) {
1706 width_total -= line->width();
1707 space_total -= line->nspaces();
1712 node *to_be_output = 0;
1713 hunits to_be_output_width;
1714 prev_line_interrupted = 0;
1717 else if (interrupted) {
1719 // see environment::final_break
1720 prev_line_interrupted = exit_started ? 2 : 1;
1722 else if (center_lines > 0) {
1724 hunits x = target_text_length - width_total;
1726 saved_indent += x/2;
1727 to_be_output = line;
1729 to_be_output_width = width_total;
1732 else if (right_justify_lines > 0) {
1733 --right_justify_lines;
1734 hunits x = target_text_length - width_total;
1737 to_be_output = line;
1738 to_be_output_width = width_total;
1744 to_be_output = line;
1745 to_be_output_width = width_total;
1748 input_line_start = line == 0 ? H0 : width_total;
1750 if (is_html && !fill) {
1751 curdiv->modified_tag.incl(MTSM_EOL);
1752 if (suppress_next_eol)
1753 suppress_next_eol = 0;
1758 output_line(to_be_output, to_be_output_width, was_centered);
1759 hyphen_line_count = 0;
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);
1768 void environment::output_line(node *n, hunits width, int was_centered)
1770 prev_text_length = width;
1771 if (margin_character_flags) {
1772 hunits d = line_length + margin_character_distance - saved_indent - width;
1774 n = new hmotion_node(d, get_fill_color(), n);
1777 margin_character_flags &= ~MARGIN_CHARACTER_NEXT;
1779 if (!margin_character_flags) {
1780 tem = margin_character_node;
1781 margin_character_node = 0;
1784 tem = margin_character_node->copy();
1787 width += tem->width();
1791 node *tem = n->next;
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)
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);
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;
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--)
1822 nn = new hmotion_node(x, get_fill_color(), nn);
1827 output(nn, !fill, vertical_spacing, total_post_vertical_spacing(), width,
1831 void environment::start_line()
1835 line = new line_start_node;
1836 if (have_temporary_indent) {
1837 saved_indent = temporary_indent;
1838 have_temporary_indent = 0;
1841 saved_indent = indent;
1842 target_text_length = line_length - saved_indent;
1847 hunits environment::get_hyphenation_space()
1849 return hyphenation_space;
1852 void hyphenation_space_request()
1855 if (get_hunits(&n, 'm')) {
1857 warning(WARN_RANGE, "hyphenation space cannot be negative");
1860 curenv->hyphenation_space = n;
1865 hunits environment::get_hyphenation_margin()
1867 return hyphenation_margin;
1870 void hyphenation_margin_request()
1873 if (get_hunits(&n, 'm')) {
1875 warning(WARN_RANGE, "hyphenation margin cannot be negative");
1878 curenv->hyphenation_margin = n;
1883 breakpoint *environment::choose_breakpoint()
1885 hunits x = width_total;
1886 int s = space_total;
1888 breakpoint *best_bp = 0; // the best breakpoint so far
1889 int best_bp_fits = 0;
1893 breakpoint *bp = n->get_breakpoints(x, s);
1895 if (bp->width <= target_text_length) {
1896 if (!bp->hyphenated) {
1897 breakpoint *tem = bp->next;
1900 breakpoint *tem1 = tem;
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
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.
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)) {
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.
1938 breakpoint *tem = bp->next;
1941 breakpoint *tem1 = tem;
1947 // It fits but it's hyphenated.
1948 if (!best_bp_fits) {
1956 breakpoint *tem = bp;
1973 output_warning(WARN_BREAK, "can't break line");
1979 void environment::hyphenate_line(int start_here)
1982 hyphenation_type prev_type = line->get_hyphenation_type();
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)
1991 prev_type = this_type;
1995 node *tem = *startp;
1998 } while (tem != 0 && tem->get_hyphenation_type() == HYPHEN_MIDDLE);
1999 int inhibit = (tem != 0 && tem->get_hyphenation_type() == HYPHEN_INHIBIT);
2001 hyphen_list *sl = 0;
2005 while (tem != end) {
2006 sl = tem->get_hyphen_list(sl, &i);
2009 tem1->next = forward;
2013 // this is for characters like hyphen and emdash
2015 for (hyphen_list *h = sl; h; h = h->next) {
2016 h->breakable = (prev_code != 0
2018 && h->next->hyphenation_code != 0);
2019 prev_code = h->hyphenation_code;
2022 if (hyphenation_flags != 0
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()))
2029 hyphenate(sl, hyphenation_flags);
2030 while (forward != 0) {
2031 node *tem1 = forward;
2032 forward = forward->next;
2034 tem = tem1->add_self(tem, &sl);
2039 static node *node_list_reverse(node *n)
2051 static void distribute_space(node *n, int nspaces, hunits desired_space,
2052 int force_reverse = 0)
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);
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);
2071 assert(desired_space.is_zero() && nspaces == 0);
2074 void environment::possibly_break_line(int start_here, int forced)
2076 int was_centered = center_lines > 0;
2077 if (!fill || current_tab || current_field || dummy)
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();
2087 // we'll find one eventually
2091 while (*ndp != bp->nd)
2092 ndp = &(*ndp)->next;
2093 bp->nd->split(bp->index, &pre, &post);
2095 hunits extra_space_width = H0;
2096 switch(adjust_mode) {
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");
2105 saved_indent += (target_text_length - bp->width)/2;
2109 saved_indent += target_text_length - bp->width;
2112 distribute_space(pre, bp->nspaces, extra_space_width);
2113 hunits output_width = bp->width + extra_space_width;
2114 input_line_start -= output_width;
2116 hyphen_line_count++;
2118 hyphen_line_count = 0;
2122 node *first_non_discardable = 0;
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();
2139 to_be_discarded = line;
2142 // Do output_line() here so that line will be 0 iff the
2143 // the environment will be empty.
2144 output_line(pre, output_width, was_centered);
2145 while (to_be_discarded != 0) {
2146 tem = to_be_discarded;
2147 to_be_discarded = to_be_discarded->next;
2148 input_line_start -= tem->width();
2152 if (have_temporary_indent) {
2153 saved_indent = temporary_indent;
2154 have_temporary_indent = 0;
2157 saved_indent = indent;
2158 target_text_length = line_length - saved_indent;
2164 Do the break at the end of input after the end macro (if any).
2166 Unix troff behaves as follows: if the last line is
2170 it will output foo on the current page, and bar on the next page;
2179 everything will be output on the current page. This behaviour must be
2182 The problem is that some macro packages rely on this. For example,
2183 the ATK macros have an end macro that emits \c if it needs to print a
2184 table of contents but doesn't do a 'bp in the end macro; instead the
2185 'bp is done in the bottom of page trap. This works with Unix troff,
2186 provided that the current environment is not empty at the end of the
2189 The following will make macro packages that do that sort of thing work
2190 even if the current environment is empty at the end of the input file.
2191 If the last input line used \c and this line occurred in the end macro,
2192 then we'll force everything out on the current page, but we'll make
2193 sure that the environment isn't empty so that we won't exit at the
2194 bottom of this page.
2197 void environment::final_break()
2199 if (prev_line_interrupted == 2) {
2201 add_node(new transparent_dummy_node);
2207 node *environment::make_tag(const char *nm, int i)
2211 * need to emit tag for post-grohtml
2212 * but we check to see whether we can emit specials
2214 if (curdiv == topdiv && topdiv->before_first_page)
2215 topdiv->begin_page();
2216 macro *m = new macro;
2217 m->append_str("devtag:");
2218 for (const char *p = nm; *p; p++)
2219 if (!invalid_input_char((unsigned char)*p))
2223 return new special_node(*m);
2228 void environment::dump_troff_state()
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);
2247 statem *environment::construct_state(int only_eol)
2250 statem *s = new statem();
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);
2260 s->add_tag(MTSM_BR);
2261 if (seen_space != 0)
2262 s->add_tag(MTSM_SP, seen_space);
2267 s->add_tag(MTSM_EOL);
2268 s->add_tag(MTSM_CE, center_lines);
2277 void environment::construct_format_state(node *n, int was_centered,
2281 // find first glyph node which has a state.
2282 while (n != 0 && n->state == 0)
2284 if (n == 0 || (n->state == 0))
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);
2293 n->state->add_tag(MTSM_CE, center_lines+1);
2295 n->state->add_tag_if_unknown(MTSM_CE, 0);
2296 n->state->add_tag_if_unknown(MTSM_FI, filling);
2299 if (n->state != 0) {
2300 n->state->sub_tag_ce();
2301 n->state->add_tag_if_unknown(MTSM_FI, filling);
2308 void environment::construct_new_line_state(node *n)
2311 // find first glyph node which has a state.
2312 while (n != 0 && n->state == 0)
2314 if (n == 0 || n->state == 0)
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);
2325 extern int global_diverted_space;
2327 void environment::do_break(int do_spread)
2329 int was_centered = 0;
2330 if (curdiv == topdiv && topdiv->before_first_page) {
2331 topdiv->begin_page();
2337 // this is so that hyphenation works
2338 if (line->nspaces() == 0) {
2339 line = new space_node(H0, get_fill_color(), line);
2342 possibly_break_line(0, do_spread);
2344 while (line != 0 && line->discardable()) {
2345 width_total -= line->width();
2346 space_total -= line->nspaces();
2352 input_line_start = H0;
2355 switch (adjust_mode) {
2357 saved_indent += (target_text_length - width_total)/2;
2361 saved_indent += target_text_length - width_total;
2367 output_line(tem, width_total, was_centered);
2368 hyphen_line_count = 0;
2370 prev_line_interrupted = 0;
2371 #ifdef WIDOW_CONTROL
2373 output_pending_lines();
2374 #endif /* WIDOW_CONTROL */
2375 if (!global_diverted_space) {
2376 curdiv->modified_tag.incl(MTSM_BR);
2381 int environment::is_empty()
2383 return !current_tab && line == 0 && pending_lines == 0;
2386 void do_break_request(int spread)
2388 while (!tok.newline() && !tok.eof())
2391 curenv->do_break(spread);
2395 void break_request()
2397 do_break_request(0);
2400 void break_spread_request()
2402 do_break_request(1);
2407 if (curdiv == topdiv && topdiv->before_first_page) {
2408 handle_initial_title();
2412 hunits part_width[3];
2413 part[0] = part[1] = part[2] = 0;
2414 environment env(curenv);
2415 environment *oldenv = curenv;
2417 read_title_parts(part, part_width);
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;
2439 hunits length_title(curenv->title_length);
2440 hunits f = length_title - part_width[1];
2442 n = new hmotion_node(f2 - part_width[2], curenv->get_fill_color(), n);
2450 n = new hmotion_node(f - f2 - part_width[0], curenv->get_fill_color(), n);
2458 curenv->output_title(n, !curenv->fill, curenv->vertical_spacing,
2459 curenv->total_post_vertical_spacing(), length_title);
2460 curenv->hyphen_line_count = 0;
2466 curenv->adjust_mode |= 1;
2470 curenv->adjust_mode = ADJUST_LEFT;
2473 curenv->adjust_mode = ADJUST_RIGHT;
2476 curenv->adjust_mode = ADJUST_CENTER;
2480 curenv->adjust_mode = ADJUST_BOTH;
2484 if (get_integer(&n)) {
2486 warning(WARN_RANGE, "negative adjustment mode");
2488 curenv->adjust_mode = 5;
2489 warning(WARN_RANGE, "adjustment mode `%1' out of range", n);
2492 curenv->adjust_mode = n;
2501 curenv->adjust_mode &= ~1;
2505 void do_input_trap(int continued)
2507 curenv->input_trap_count = 0;
2509 curenv->continued_input_trap = 1;
2511 if (has_arg() && get_integer(&n)) {
2514 "number of lines for input trap must be greater than zero");
2516 symbol s = get_name(1);
2518 curenv->input_trap_count = n;
2519 curenv->input_trap = s;
2531 void input_trap_continued()
2538 // must not be R or C or L or a legitimate part of a number expression
2539 const char TAB_REPEAT_CHAR = 'T';
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 *);
2552 tab *tab::free_list = 0;
2554 void *tab::operator new(size_t n)
2556 assert(n == sizeof(tab));
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;
2564 free_list = (tab *)(free_list->next);
2570 /* cfront can't cope with this. */
2573 void tab::operator delete(void *p)
2576 ((tab *)p)->next = free_list;
2577 free_list = (tab *)p;
2581 tab::tab(hunits x, tab_type t) : next(0), pos(x), type(t)
2585 tab_stops::tab_stops(hunits distance, tab_type type)
2588 repeated_list = new tab(distance, type);
2591 tab_stops::~tab_stops()
2596 tab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance)
2600 return distance_to_next_tab(curpos, distance, &nextpos);
2603 tab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance,
2608 for (tem = initial_list; tem && tem->pos <= curpos; tem = tem->next)
2611 *distance = tem->pos - curpos;
2612 *nextpos = tem->pos;
2615 if (repeated_list == 0)
2617 hunits base = lastpos;
2619 for (tem = repeated_list; tem && tem->pos + base <= curpos; tem = tem->next)
2622 *distance = tem->pos + base - curpos;
2623 *nextpos = tem->pos + base;
2626 assert(lastpos > 0);
2632 const char *tab_stops::to_string()
2634 static char *buf = 0;
2635 static int buf_size = 0;
2636 // figure out a maximum on the amount of space we can need
2639 for (p = initial_list; p; p = p->next)
2641 for (p = repeated_list; p; p = p->next)
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) {
2649 buf = new char[buf_size];
2652 for (p = initial_list; p; p = p->next) {
2653 strcpy(ptr, i_to_a(p->pos.to_units()));
2654 ptr = strchr(ptr, '\0');
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');
2696 tab_stops::tab_stops() : initial_list(0), repeated_list(0)
2700 tab_stops::tab_stops(const tab_stops &ts)
2701 : initial_list(0), repeated_list(0)
2703 tab **p = &initial_list;
2704 tab *t = ts.initial_list;
2706 *p = new tab(t->pos, t->type);
2711 t = ts.repeated_list;
2713 *p = new tab(t->pos, t->type);
2719 void tab_stops::clear()
2721 while (initial_list) {
2722 tab *tem = initial_list;
2723 initial_list = initial_list->next;
2726 while (repeated_list) {
2727 tab *tem = repeated_list;
2728 repeated_list = repeated_list->next;
2733 void tab_stops::add_tab(hunits pos, tab_type type, int repeated)
2736 for (p = repeated ? &repeated_list : &initial_list; *p; p = &(*p)->next)
2738 *p = new tab(pos, type);
2742 void tab_stops::operator=(const tab_stops &ts)
2745 tab **p = &initial_list;
2746 tab *t = ts.initial_list;
2748 *p = new tab(t->pos, t->type);
2753 t = ts.repeated_list;
2755 *p = new tab(t->pos, t->type);
2764 hunits prev_pos = 0;
2769 if (tok.ch() == TAB_REPEAT_CHAR) {
2774 if (!get_hunits(&pos, 'm', prev_pos))
2776 tab_type type = TAB_LEFT;
2777 if (tok.ch() == 'C') {
2781 else if (tok.ch() == 'R') {
2785 else if (tok.ch() == 'L') {
2788 if (pos <= prev_pos && !first)
2790 "positions of tab stops must be strictly increasing");
2792 tabs.add_tab(pos, type, repeated);
2797 curenv->tabs = tabs;
2798 curdiv->modified_tag.incl(MTSM_TA);
2802 const char *environment::get_tabs()
2804 return tabs.to_string();
2807 tab_type environment::distance_to_next_tab(hunits *distance)
2810 ? curenv->tabs.distance_to_next_tab(get_text_length(), distance)
2811 : curenv->tabs.distance_to_next_tab(get_input_line_position(), distance);
2814 tab_type environment::distance_to_next_tab(hunits *distance, hunits *leftpos)
2817 ? curenv->tabs.distance_to_next_tab(get_text_length(), distance, leftpos)
2818 : curenv->tabs.distance_to_next_tab(get_input_line_position(), distance,
2822 void field_characters()
2824 field_delimiter_char = get_optional_char();
2825 if (field_delimiter_char)
2826 padding_indicator_char = get_optional_char();
2828 padding_indicator_char = 0;
2832 void line_tabs_request()
2835 if (has_arg() && get_integer(&n))
2836 curenv->line_tabs = n != 0;
2838 curenv->line_tabs = 1;
2842 int environment::get_line_tabs()
2847 void environment::wrap_up_tab()
2854 switch (current_tab) {
2856 tab_amount = tab_distance - tab_width;
2857 line = make_tab_node(tab_amount, line);
2860 tab_amount = tab_distance - tab_width/2;
2861 line = make_tab_node(tab_amount, line);
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;
2875 field_distance -= tab_amount;
2876 field_spaces += tab_field_spaces;
2878 if (tab_contents != 0) {
2880 for (tem = tab_contents; tem->next != 0; tem = tem->next)
2883 line = tab_contents;
2885 tab_field_spaces = 0;
2889 current_tab = TAB_NONE;
2892 node *environment::make_tab_node(hunits d, node *next)
2894 if (leader_node != 0 && d < 0) {
2895 error("motion generated by leader cannot be negative");
2900 return new hmotion_node(d, 1, 0, get_fill_color(), next);
2901 node *n = new hline_node(d, leader_node, next);
2906 void environment::handle_tab(int is_leader)
2912 charinfo *ci = is_leader ? leader_char : tab_char;
2914 leader_node = ci ? make_char_node(ci) : 0;
2915 tab_type t = distance_to_next_tab(&d, &absolute);
2920 add_node(make_tag("tab L", absolute.to_units()));
2921 add_node(make_tab_node(d));
2924 add_node(make_tag("tab R", absolute.to_units()));
2927 add_node(make_tag("tab C", absolute.to_units()));
2936 tab_field_spaces = 0;
2939 void environment::start_field()
2941 assert(!current_field);
2943 if (distance_to_next_tab(&d) != TAB_NONE) {
2944 pre_field_width = get_text_length();
2948 tab_field_spaces = 0;
2949 for (node *p = line; p; p = p->next)
2954 tab_precedes_field = current_tab != TAB_NONE;
2957 error("zero field width");
2960 void environment::wrap_up_field()
2962 if (!current_tab && field_spaces == 0)
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,
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;
2974 if (field_spaces != 0) {
2975 distribute_space(line, field_spaces, padding, 1);
2976 width_total += padding;
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()
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;
2992 if (tab_contents != 0) {
2994 for (tem = tab_contents; tem->next != 0; tem = tem->next)
2997 line = tab_contents;
3009 void environment::add_padding()
3012 tab_contents = new space_node(H0, get_fill_color(), tab_contents);
3018 line = new space_node(H0, get_fill_color(), line);
3023 typedef int (environment::*INT_FUNCP)();
3024 typedef vunits (environment::*VUNITS_FUNCP)();
3025 typedef hunits (environment::*HUNITS_FUNCP)();
3026 typedef const char *(environment::*STRING_FUNCP)();
3028 class int_env_reg : public reg {
3031 int_env_reg(INT_FUNCP);
3032 const char *get_string();
3033 int get_value(units *val);
3036 class vunits_env_reg : public reg {
3039 vunits_env_reg(VUNITS_FUNCP f);
3040 const char *get_string();
3041 int get_value(units *val);
3045 class hunits_env_reg : public reg {
3048 hunits_env_reg(HUNITS_FUNCP f);
3049 const char *get_string();
3050 int get_value(units *val);
3053 class string_env_reg : public reg {
3056 string_env_reg(STRING_FUNCP);
3057 const char *get_string();
3060 int_env_reg::int_env_reg(INT_FUNCP f) : func(f)
3064 int int_env_reg::get_value(units *val)
3066 *val = (curenv->*func)();
3070 const char *int_env_reg::get_string()
3072 return i_to_a((curenv->*func)());
3075 vunits_env_reg::vunits_env_reg(VUNITS_FUNCP f) : func(f)
3079 int vunits_env_reg::get_value(units *val)
3081 *val = (curenv->*func)().to_units();
3085 const char *vunits_env_reg::get_string()
3087 return i_to_a((curenv->*func)().to_units());
3090 hunits_env_reg::hunits_env_reg(HUNITS_FUNCP f) : func(f)
3094 int hunits_env_reg::get_value(units *val)
3096 *val = (curenv->*func)().to_units();
3100 const char *hunits_env_reg::get_string()
3102 return i_to_a((curenv->*func)().to_units());
3105 string_env_reg::string_env_reg(STRING_FUNCP f) : func(f)
3109 const char *string_env_reg::get_string()
3111 return (curenv->*func)();
3114 class horizontal_place_reg : public general_reg {
3116 horizontal_place_reg();
3117 int get_value(units *);
3118 void set_value(units);
3121 horizontal_place_reg::horizontal_place_reg()
3125 int horizontal_place_reg::get_value(units *res)
3127 *res = curenv->get_input_line_position().to_units();
3131 void horizontal_place_reg::set_value(units n)
3133 curenv->set_input_line_position(hunits(n));
3136 int environment::get_zoom()
3138 return env_get_zoom(this);
3141 const char *environment::get_font_family_string()
3143 return family->nm.contents();
3146 const char *environment::get_glyph_color_string()
3148 return glyph_color->nm.contents();
3151 const char *environment::get_fill_color_string()
3153 return fill_color->nm.contents();
3156 const char *environment::get_font_name_string()
3158 symbol f = get_font_name(fontno, this);
3159 return f.contents();
3162 const char *environment::get_style_name_string()
3164 symbol f = get_style_name(fontno);
3165 return f.contents();
3168 const char *environment::get_name_string()
3170 return name.contents();
3173 // Convert a quantity in scaled points to ascii decimal fraction.
3175 const char *sptoa(int sp)
3178 assert(sizescale > 0);
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.
3187 while ((n & 1) == 0) {
3192 while ((n % 5) == 0) {
3197 int decimal_point = power5 > power2 ? power5 : power2;
3198 if (decimal_point <= 10) {
3201 for (t = decimal_point - power2; --t >= 0;)
3203 for (t = decimal_point - power5; --t >= 0;)
3205 if (factor == 1 || sp <= INT_MAX/factor)
3206 return if_to_a(sp*factor, decimal_point);
3209 double s = double(sp)/double(sizescale);
3210 double factor = 10.0;
3212 int decimal_point = 0;
3214 double v = ceil(s*factor);
3219 } while (++decimal_point < 10);
3220 return if_to_a(int(val), decimal_point);
3223 const char *environment::get_point_size_string()
3225 return sptoa(curenv->get_point_size());
3228 const char *environment::get_requested_point_size_string()
3230 return sptoa(curenv->get_requested_point_size());
3233 void environment::print_env()
3235 // at the time of calling .pev, those values are always zero or
3238 // char_height, char_slant,
3240 // current_tab, tab_width, tab_distance
3241 // current_field, field_distance, pre_field_width, field_spaces,
3242 // tab_field_spaces, tab_precedes_field
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
3267 : adjust_mode == ADJUST_BOTH
3269 : adjust_mode == ADJUST_CENTER
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");
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");
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
3314 : margin_character_flags == MARGIN_CHARACTER_NEXT
3316 : margin_character_flags == MARGIN_CHARACTER_ON
3317 | MARGIN_CHARACTER_NEXT
3320 errprint(" margin character distance: %1u\n",
3321 margin_character_distance.to_units());
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);
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";
3343 errprint(" hyphenation_flags: %1\n", hf.contents());
3344 errprint(" number of consecutive hyphenated lines: %1\n",
3346 errprint(" maximum number of consecutive hyphenated lines: %1\n",
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 */
3357 errprint("Current Environment:\n");
3358 curenv->print_env();
3359 for (int i = 0; i < NENVIRONMENTS; i++) {
3361 errprint("Environment %1:\n", i);
3362 if (env_table[i] != curenv)
3363 env_table[i]->print_env();
3365 errprint(" current\n");
3368 dictionary_iterator iter(env_dictionary);
3371 while (iter.get(&s, (void **)&e)) {
3372 assert(!s.is_null());
3373 errprint("Environment %1:\n", s.contents());
3377 errprint(" current\n");
3383 #define init_int_env_reg(name, func) \
3384 number_reg_dictionary.define(name, new int_env_reg(&environment::func))
3386 #define init_vunits_env_reg(name, func) \
3387 number_reg_dictionary.define(name, new vunits_env_reg(&environment::func))
3389 #define init_hunits_env_reg(name, func) \
3390 number_reg_dictionary.define(name, new hunits_env_reg(&environment::func))
3392 #define init_string_env_reg(name, func) \
3393 number_reg_dictionary.define(name, new string_env_reg(&environment::func))
3395 void init_env_requests()
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);
3409 init_request("fcolor", fill_color_change);
3410 init_request("ft", font_change);
3411 init_request("gcolor", glyph_color_change);
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);
3431 init_request("pev", print_env);
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);
3483 init_string_env_reg(".sty", get_style_name_string);
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);
3488 init_int_env_reg(".zoom", get_zoom);
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));
3500 // Hyphenation - TeX's hyphenation algorithm with a less fancy implementation.
3506 virtual void do_match(int len, void *val) = 0;
3507 virtual void do_delete(void *) = 0;
3508 void delete_trie_node(trie_node *);
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);
3518 class hyphen_trie : private trie {
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);
3528 void hyphenate(const char *word, int len, int *hyphens);
3529 void read_patterns_file(const char *name, int append, dictionary *ex);
3532 struct hyphenation_language {
3534 dictionary exceptions;
3535 hyphen_trie patterns;
3536 hyphenation_language(symbol nm) : name(nm), exceptions(501) {}
3537 ~hyphenation_language() { }
3540 dictionary language_dictionary(5);
3541 hyphenation_language *current_language = 0;
3543 static void set_hyphenation_language()
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);
3556 const int WORD_MAX = 256; // we use unsigned char for offsets in
3557 // hyphenation exceptions
3559 static void hyphen_word()
3561 if (!current_language) {
3562 error("no current hyphenation language");
3566 char buf[WORD_MAX + 1];
3567 unsigned char pos[WORD_MAX + 2];
3570 if (tok.newline() || tok.eof())
3574 while (i < WORD_MAX && !tok.space() && !tok.newline() && !tok.eof()) {
3575 charinfo *ci = tok.get_char(1);
3581 if (ci->get_ascii_code() == '-') {
3582 if (i > 0 && (npos == 0 || pos[npos - 1] != i))
3586 unsigned char c = ci->get_hyphenation_code();
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),
3611 trie_node(char, trie_node *);
3614 trie_node::trie_node(char ch, trie_node *p)
3615 : c(ch), down(0), right(p), val(0)
3626 delete_trie_node(tp);
3631 void trie::delete_trie_node(trie_node *p)
3634 delete_trie_node(p->down);
3635 delete_trie_node(p->right);
3642 void trie::insert(const char *pat, int patlen, void *val)
3644 trie_node **p = &tp;
3645 assert(patlen > 0 && pat != 0);
3647 while (*p != 0 && (*p)->c < pat[0])
3649 if (*p == 0 || (*p)->c != pat[0])
3650 *p = new trie_node(pat[0], *p);
3651 if (--patlen == 0) {
3660 void trie::find(const char *pat, int patlen)
3663 for (int i = 0; p != 0 && i < patlen; i++) {
3664 while (p != 0 && p->c < pat[i])
3666 if (p != 0 && p->c == pat[i]) {
3668 do_match(i+1, p->val);
3680 operation(int, int, operation *);
3683 operation::operation(int i, int j, operation *op)
3684 : next(op), distance(j), num(i)
3688 void hyphen_trie::insert_pattern(const char *pat, int patlen, int *num)
3691 for (int i = 0; i < patlen+1; i++)
3693 op = new operation(num[i], patlen - i, op);
3694 insert(pat, patlen, op);
3697 void hyphen_trie::insert_hyphenation(dictionary *ex, const char *pat,
3700 char buf[WORD_MAX + 1];
3701 unsigned char pos[WORD_MAX + 2];
3704 while (j < patlen) {
3705 unsigned char c = pat[j++];
3707 if (i > 0 && (npos == 0 || pos[npos - 1] != i))
3711 buf[i++] = hpf_code_table[c];
3716 unsigned char *tem = new unsigned char[npos + 1];
3717 memcpy(tem, pos, npos + 1);
3718 tem = (unsigned char *)ex->lookup(symbol(buf), tem);
3724 void hyphen_trie::hyphenate(const char *word, int len, int *hyphens)
3727 for (j = 0; j < len + 1; j++)
3729 for (j = 0; j < len - 1; j++) {
3731 find(word + j, len - j);
3735 inline int max(int m, int n)
3737 return m > n ? m : n;
3740 void hyphen_trie::do_match(int i, void *v)
3742 operation *op = (operation *)v;
3744 h[i - op->distance] = max(h[i - op->distance], op->num);
3749 void hyphen_trie::do_delete(void *v)
3751 operation *op = (operation *)v;
3753 operation *tem = op;
3759 /* We use very simple rules to parse TeX's hyphenation patterns.
3761 . `%' starts a comment even if preceded by `\'.
3763 . No support for digraphs and like `\$'.
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.
3768 . No macro expansion.
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.
3775 . Similarly, `\hyphenation{...}' gives a list of hyphenation
3778 . `\endinput' is recognized also.
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.
3786 int hyphen_trie::hpf_getc(FILE *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')
3804 if (c1 >= '0' && c1 <= '9')
3812 if (c >= 0 && c <= 63)
3814 else if (c >= 64 && c <= 127)
3821 error("invalid ^, ^^x, or ^^xx character in hyphenation patterns file");
3825 void hyphen_trie::read_patterns_file(const char *name, int append,
3831 for (int i = 0; i < WORD_MAX; i++)
3833 int num[WORD_MAX+1];
3836 FILE *fp = mac_path->open_file(name, &path);
3838 error("can't find hyphenation patterns file `%1'", name);
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
3850 if (c == '%') { // skip comments
3853 } while (c != EOF && c != '\n');
3855 if (c == EOF || !csspace(c))
3860 if (have_keyword || traditional) // we are done
3862 else { // rescan file in `traditional' mode
3871 if (!(c == '{' || c == '}')) { // skip braces at line start
3872 do { // scan patterns
3880 } while (i < WORD_MAX && c != EOF && !csspace(c)
3881 && c != '%' && c != '{' && c != '}');
3884 if (i >= 9 && !strncmp(buf + i - 9, "\\patterns", 9)) {
3888 if (have_patterns || have_hyphenation)
3889 error("\\patterns not allowed inside of %1 group",
3890 have_patterns ? "\\patterns" : "\\hyphenation");
3899 else if (i >= 12 && !strncmp(buf + i - 12, "\\hyphenation", 12)) {
3903 if (have_patterns || have_hyphenation)
3904 error("\\hyphenation not allowed inside of %1 group",
3905 have_patterns ? "\\patterns" : "\\hyphenation");
3907 have_hyphenation = 1;
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");
3920 else if (c == '}') {
3921 if (have_patterns) {
3926 else if (have_hyphenation) {
3927 have_hyphenation = 0;
3929 final_hyphenation = 1;
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
3942 if (c == '{' || c == '}')
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);
3952 else if (have_hyphenation || final_hyphenation) {
3953 insert_hyphenation(ex, buf, i);
3954 final_hyphenation = 0;
3963 void hyphenate(hyphen_list *h, unsigned flags)
3965 if (!current_language)
3968 while (h && h->hyphenation_code == 0)
3971 char hbuf[WORD_MAX + 2];
3972 char *buf = hbuf + 1;
3974 for (tem = h; tem && len < WORD_MAX; tem = tem->next) {
3975 if (tem->hyphenation_code != 0)
3976 buf[len++] = tem->hyphenation_code;
3980 hyphen_list *nexth = tem;
3984 = (unsigned char *)current_language->exceptions.lookup(buf);
3988 for (tem = h; tem != 0; tem = tem->next, i++)
3995 hbuf[0] = hbuf[len + 1] = '.';
3996 int num[WORD_MAX + 3];
3997 current_language->patterns.hyphenate(hbuf, len + 2, num);
4000 if (flags & HYPHEN_FIRST_CHARS)
4002 if (flags & HYPHEN_LAST_CHARS)
4004 for (i = 2, tem = h; i < len && tem; tem = tem->next, i++)
4013 static void do_hyphenation_patterns_file(int append)
4015 symbol name = get_long_name(1);
4016 if (!name.is_null()) {
4017 if (!current_language)
4018 error("no current hyphenation language");
4020 current_language->patterns.read_patterns_file(
4021 name.contents(), append,
4022 ¤t_language->exceptions);
4027 static void hyphenation_patterns_file()
4029 do_hyphenation_patterns_file(0);
4032 static void hyphenation_patterns_file_append()
4034 do_hyphenation_patterns_file(1);
4037 class hyphenation_language_reg : public reg {
4039 const char *get_string();
4042 const char *hyphenation_language_reg::get_string()
4044 return current_language ? current_language->name.contents() : "";
4047 void init_hyphen_requests()
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);