2 /* Copyright (C) 1989-2014 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
21 #include "dictionary.h"
23 #include "stringclass.h"
33 #include "macropath.h"
37 symbol default_family("T");
39 enum { ADJUST_LEFT = 0, ADJUST_BOTH = 1, ADJUST_CENTER = 3, ADJUST_RIGHT = 5 };
41 enum { HYPHEN_LAST_LINE = 2, HYPHEN_LAST_CHARS = 4, HYPHEN_FIRST_CHARS = 8 };
46 env_list(environment *e, env_list *p) : env(e), next(p) {}
50 const int NENVIRONMENTS = 10;
51 environment *env_table[NENVIRONMENTS];
52 dictionary env_dictionary(10);
54 static int next_line_number = 0;
55 extern int suppress_push;
56 extern statem *get_diversion_state();
58 charinfo *field_delimiter_char;
59 charinfo *padding_indicator_char;
61 int translate_space_to_dummy = 0;
63 class pending_output_line {
71 int last_line; // Is it the last line of the paragraph?
72 #endif /* WIDOW_CONTROL */
74 pending_output_line *next;
76 pending_output_line(node *, int, vunits, vunits, hunits, int,
77 pending_output_line * = 0);
78 ~pending_output_line();
82 friend void environment::mark_last_line();
83 friend void environment::output(node *, int, vunits, vunits, hunits, int);
84 #endif /* WIDOW_CONTROL */
87 pending_output_line::pending_output_line(node *n, int nf, vunits v, vunits pv,
89 pending_output_line *p)
90 : nd(n), no_fill(nf), was_centered(ce), vs(v), post_vs(pv), width(w),
93 #endif /* WIDOW_CONTROL */
98 pending_output_line::~pending_output_line()
100 delete_node_list(nd);
103 int pending_output_line::output()
105 if (trap_sprung_flag)
108 if (next && next->last_line && !no_fill) {
109 curdiv->need(vs + post_vs + vunits(vresolution));
110 if (trap_sprung_flag) {
111 next->last_line = 0; // Try to avoid infinite loops.
116 curenv->construct_format_state(nd, was_centered, !no_fill);
117 curdiv->output(nd, no_fill, vs, post_vs, width);
122 void environment::output(node *nd, int no_fill_flag,
123 vunits vs, vunits post_vs,
124 hunits width, int was_centered)
127 while (pending_lines) {
128 if (widow_control && !pending_lines->no_fill && !pending_lines->next)
130 if (!pending_lines->output())
132 pending_output_line *tem = pending_lines;
133 pending_lines = pending_lines->next;
136 #else /* WIDOW_CONTROL */
137 output_pending_lines();
138 #endif /* WIDOW_CONTROL */
139 if (!trap_sprung_flag && !pending_lines
141 && (!widow_control || no_fill_flag)
142 #endif /* WIDOW_CONTROL */
144 curenv->construct_format_state(nd, was_centered, !no_fill_flag);
145 curdiv->output(nd, no_fill_flag, vs, post_vs, width);
147 pending_output_line **p;
148 for (p = &pending_lines; *p; p = &(*p)->next)
150 *p = new pending_output_line(nd, no_fill_flag, vs, post_vs, width,
155 // a line from .tl goes at the head of the queue
157 void environment::output_title(node *nd, int no_fill_flag,
158 vunits vs, vunits post_vs,
161 if (!trap_sprung_flag)
162 curdiv->output(nd, no_fill_flag, vs, post_vs, width);
164 pending_lines = new pending_output_line(nd, no_fill_flag, vs, post_vs,
165 width, 0, pending_lines);
168 void environment::output_pending_lines()
170 while (pending_lines && pending_lines->output()) {
171 pending_output_line *tem = pending_lines;
172 pending_lines = pending_lines->next;
179 void environment::mark_last_line()
181 if (!widow_control || !pending_lines)
183 pending_output_line *p;
184 for (p = pending_lines; p->next; p = p->next)
190 void widow_control_request()
193 if (has_arg() && get_integer(&n))
194 curenv->widow_control = n != 0;
196 curenv->widow_control = 1;
200 #endif /* WIDOW_CONTROL */
202 /* font_size functions */
204 size_range *font_size::size_table = 0;
205 int font_size::nranges = 0;
209 int compare_ranges(const void *p1, const void *p2)
211 return ((size_range *)p1)->min - ((size_range *)p2)->min;
216 void font_size::init_size_table(int *sizes)
219 while (sizes[nranges*2] != 0)
222 size_table = new size_range[nranges];
223 for (int i = 0; i < nranges; i++) {
224 size_table[i].min = sizes[i*2];
225 size_table[i].max = sizes[i*2 + 1];
227 qsort(size_table, nranges, sizeof(size_range), compare_ranges);
230 font_size::font_size(int sp)
232 for (int i = 0; i < nranges; i++) {
233 if (sp < size_table[i].min) {
234 if (i > 0 && size_table[i].min - sp >= sp - size_table[i - 1].max)
235 p = size_table[i - 1].max;
237 p = size_table[i].min;
240 if (sp <= size_table[i].max) {
245 p = size_table[nranges - 1].max;
248 int font_size::to_units()
250 return scale(p, units_per_inch, sizescale*72);
253 // we can't do this in a static constructor because various dictionaries
254 // have to get initialized first
256 void init_environments()
258 curenv = env_table[0] = new environment("0");
263 curenv->tab_char = get_optional_char();
267 void leader_character()
269 curenv->leader_char = get_optional_char();
273 void environment::add_char(charinfo *ci)
279 // don't allow fields in dummy environments
280 else if (ci == field_delimiter_char && !dummy) {
286 else if (current_field && ci == padding_indicator_char)
288 else if (current_tab) {
289 if (tab_contents == 0)
290 tab_contents = new line_start_node;
291 if (ci != hyphen_indicator_char)
292 tab_contents = tab_contents->add_char(ci, this, &tab_width, &s, &gc_np);
294 tab_contents = tab_contents->add_discretionary_hyphen();
300 fprintf(stderr, "current line is\n");
301 line->debug_node_list();
303 if (ci != hyphen_indicator_char)
304 line = line->add_char(ci, this, &width_total, &space_total, &gc_np);
306 line = line->add_discretionary_hyphen();
309 fprintf(stderr, "now after we have added character the line is\n");
310 line->debug_node_list();
312 if ((!suppress_push) && gc_np) {
313 if (gc_np && (gc_np->state == 0)) {
314 gc_np->state = construct_state(0);
315 gc_np->push_state = get_diversion_state();
317 else if (line && (line->state == 0)) {
318 line->state = construct_state(0);
319 line->push_state = get_diversion_state();
323 fprintf(stderr, "now we have possibly added the state the line is\n");
324 line->debug_node_list();
328 node *environment::make_char_node(charinfo *ci)
330 return make_node(ci, this);
333 void environment::add_node(node *n)
337 if (!suppress_push) {
338 if (n->is_special && n->state == NULL)
339 n->state = construct_state(0);
340 n->push_state = get_diversion_state();
343 if (current_tab || current_field)
348 else if (current_tab) {
349 n->next = tab_contents;
351 tab_width += n->width();
355 if (discarding && n->discardable()) {
356 // XXX possibly: input_line_start -= n->width();
362 width_total += n->width();
363 space_total += n->nspaces();
366 construct_new_line_state(line);
370 void environment::add_hyphen_indicator()
372 if (current_tab || interrupted || current_field
373 || hyphen_indicator_char != 0)
377 line = line->add_discretionary_hyphen();
380 int environment::get_hyphenation_flags()
382 return hyphenation_flags;
385 int environment::get_hyphen_line_max()
387 return hyphen_line_max;
390 int environment::get_hyphen_line_count()
392 return hyphen_line_count;
395 int environment::get_center_lines()
400 int environment::get_right_justify_lines()
402 return right_justify_lines;
405 void environment::add_italic_correction()
409 tab_contents = tab_contents->add_italic_correction(&tab_width);
412 line = line->add_italic_correction(&width_total);
415 void environment::space_newline()
417 assert(!current_tab && !current_field);
421 hunits sw = env_space_width(this);
422 hunits ssw = env_sentence_space_width(this);
423 if (!translate_space_to_dummy) {
425 if (node_list_ends_sentence(line) == 1)
428 width_list *w = new width_list(sw, ssw);
429 if (node_list_ends_sentence(line) == 1)
430 w->next = new width_list(sw, ssw);
431 if (line != 0 && line->merge_space(x, sw, ssw)) {
435 add_node(new word_space_node(x, get_fill_color(), w));
436 possibly_break_line(0, spread_flag);
440 void environment::space()
442 space(env_space_width(this), env_sentence_space_width(this));
445 void environment::space(hunits space_width, hunits sentence_space_width)
449 if (current_field && padding_indicator_char == 0) {
453 hunits x = translate_space_to_dummy ? H0 : space_width;
454 node *p = current_tab ? tab_contents : line;
455 hunits *tp = current_tab ? &tab_width : &width_total;
456 if (p && p->nspaces() == 1 && p->width() == x
457 && node_list_ends_sentence(p->next) == 1) {
458 hunits xx = translate_space_to_dummy ? H0 : sentence_space_width;
459 if (p->merge_space(xx, space_width, sentence_space_width)) {
464 if (p && p->merge_space(x, space_width, sentence_space_width)) {
468 add_node(new word_space_node(x,
470 new width_list(space_width,
471 sentence_space_width)));
472 possibly_break_line(0, spread_flag);
476 node *do_underline_special(int);
478 void environment::set_font(symbol nm)
482 if (nm == symbol("P") || nm.is_empty()) {
483 if (family->make_definite(prev_fontno) < 0)
486 fontno = prev_fontno;
490 prev_fontno = fontno;
491 int n = symbol_fontno(nm);
493 n = next_available_font_position();
494 if (!mount_font(n, nm))
497 if (family->make_definite(n) < 0)
501 if (underline_spaces && fontno != prev_fontno) {
502 if (fontno == get_underline_fontno())
503 add_node(do_underline_special(1));
504 if (prev_fontno == get_underline_fontno())
505 add_node(do_underline_special(0));
509 void environment::set_font(int n)
513 if (is_good_fontno(n)) {
514 prev_fontno = fontno;
518 warning(WARN_FONT, "bad font number");
521 void environment::set_family(symbol fam)
525 if (fam.is_null() || fam.is_empty()) {
526 if (prev_family->make_definite(fontno) < 0)
528 font_family *tem = family;
529 family = prev_family;
533 font_family *f = lookup_family(fam);
534 if (f->make_definite(fontno) < 0)
536 prev_family = family;
541 void environment::set_size(int n)
546 font_size temp = prev_size;
549 int temp2 = prev_requested_size;
550 prev_requested_size = requested_size;
551 requested_size = temp2;
556 prev_requested_size = requested_size;
561 void environment::set_char_height(int n)
565 if (n == requested_size || n <= 0)
571 void environment::set_char_slant(int n)
578 color *environment::get_prev_glyph_color()
580 return prev_glyph_color;
583 color *environment::get_glyph_color()
588 color *environment::get_prev_fill_color()
590 return prev_fill_color;
593 color *environment::get_fill_color()
598 void environment::set_glyph_color(color *c)
602 curenv->prev_glyph_color = curenv->glyph_color;
603 curenv->glyph_color = c;
606 void environment::set_fill_color(color *c)
610 curenv->prev_fill_color = curenv->fill_color;
611 curenv->fill_color = c;
614 environment::environment(symbol nm)
616 prev_line_length((units_per_inch*13)/2),
617 line_length((units_per_inch*13)/2),
618 prev_title_length((units_per_inch*13)/2),
619 title_length((units_per_inch*13)/2),
620 prev_size(sizescale*10),
622 requested_size(sizescale*10),
623 prev_requested_size(sizescale*10),
627 sentence_space_size(12),
628 adjust_mode(ADJUST_BOTH),
631 prev_line_interrupted(0),
633 right_justify_lines(0),
634 prev_vertical_spacing(points_to_units(12)),
635 vertical_spacing(points_to_units(12)),
636 prev_post_vertical_spacing(0),
637 post_vertical_spacing(0),
638 prev_line_spacing(1),
643 have_temporary_indent(0),
647 continued_input_trap(0),
654 current_tab(TAB_NONE),
657 leader_char(charset_table['.']),
661 margin_character_flags(0),
662 margin_character_node(0),
663 margin_character_distance(points_to_units(10)),
665 number_text_separation(1),
666 line_number_indent(0),
667 line_number_multiple(1),
669 hyphenation_flags(1),
670 hyphen_line_count(0),
672 hyphenation_space(H0),
673 hyphenation_margin(H0),
678 #endif /* WIDOW_CONTROL */
679 glyph_color(&default_color),
680 prev_glyph_color(&default_color),
681 fill_color(&default_color),
682 prev_fill_color(&default_color),
685 suppress_next_eol(0),
687 tabs(units_per_inch/2, TAB_LEFT),
690 no_break_control_char('\''),
691 hyphen_indicator_char(0)
693 prev_family = family = lookup_family(default_family);
694 prev_fontno = fontno = 1;
695 if (!is_good_fontno(1))
696 fatal("font number 1 not a valid font");
697 if (family->make_definite(1) < 0)
698 fatal("invalid default family `%1'", default_family.contents());
699 prev_fontno = fontno;
702 environment::environment(const environment *e)
704 prev_line_length(e->prev_line_length),
705 line_length(e->line_length),
706 prev_title_length(e->prev_title_length),
707 title_length(e->title_length),
708 prev_size(e->prev_size),
710 requested_size(e->requested_size),
711 prev_requested_size(e->prev_requested_size),
712 char_height(e->char_height),
713 char_slant(e->char_slant),
714 prev_fontno(e->prev_fontno),
716 prev_family(e->prev_family),
718 space_size(e->space_size),
719 sentence_space_size(e->sentence_space_size),
720 adjust_mode(e->adjust_mode),
723 prev_line_interrupted(0),
725 right_justify_lines(0),
726 prev_vertical_spacing(e->prev_vertical_spacing),
727 vertical_spacing(e->vertical_spacing),
728 prev_post_vertical_spacing(e->prev_post_vertical_spacing),
729 post_vertical_spacing(e->post_vertical_spacing),
730 prev_line_spacing(e->prev_line_spacing),
731 line_spacing(e->line_spacing),
732 prev_indent(e->prev_indent),
735 have_temporary_indent(0),
739 continued_input_trap(0),
741 prev_text_length(e->prev_text_length),
745 line_tabs(e->line_tabs),
746 current_tab(TAB_NONE),
748 tab_char(e->tab_char),
749 leader_char(e->leader_char),
753 margin_character_flags(e->margin_character_flags),
754 margin_character_node(e->margin_character_node),
755 margin_character_distance(e->margin_character_distance),
757 number_text_separation(e->number_text_separation),
758 line_number_indent(e->line_number_indent),
759 line_number_multiple(e->line_number_multiple),
760 no_number_count(e->no_number_count),
761 hyphenation_flags(e->hyphenation_flags),
762 hyphen_line_count(0),
763 hyphen_line_max(e->hyphen_line_max),
764 hyphenation_space(e->hyphenation_space),
765 hyphenation_margin(e->hyphenation_margin),
769 widow_control(e->widow_control),
770 #endif /* WIDOW_CONTROL */
771 glyph_color(e->glyph_color),
772 prev_glyph_color(e->prev_glyph_color),
773 fill_color(e->fill_color),
774 prev_fill_color(e->prev_fill_color),
775 seen_space(e->seen_space),
776 seen_eol(e->seen_eol),
777 suppress_next_eol(e->suppress_next_eol),
778 seen_break(e->seen_break),
780 name(e->name), // so that eg `.if "\n[.ev]"0"' works
781 control_char(e->control_char),
782 no_break_control_char(e->no_break_control_char),
783 hyphen_indicator_char(e->hyphen_indicator_char)
787 void environment::copy(const environment *e)
789 prev_line_length = e->prev_line_length;
790 line_length = e->line_length;
791 prev_title_length = e->prev_title_length;
792 title_length = e->title_length;
793 prev_size = e->prev_size;
795 prev_requested_size = e->prev_requested_size;
796 requested_size = e->requested_size;
797 char_height = e->char_height;
798 char_slant = e->char_slant;
799 space_size = e->space_size;
800 sentence_space_size = e->sentence_space_size;
801 adjust_mode = e->adjust_mode;
804 prev_line_interrupted = 0;
806 right_justify_lines = 0;
807 prev_vertical_spacing = e->prev_vertical_spacing;
808 vertical_spacing = e->vertical_spacing;
809 prev_post_vertical_spacing = e->prev_post_vertical_spacing,
810 post_vertical_spacing = e->post_vertical_spacing,
811 prev_line_spacing = e->prev_line_spacing;
812 line_spacing = e->line_spacing;
813 prev_indent = e->prev_indent;
815 have_temporary_indent = 0;
816 temporary_indent = 0;
818 underline_spaces = 0;
819 input_trap_count = 0;
820 continued_input_trap = 0;
821 prev_text_length = e->prev_text_length;
824 input_line_start = 0;
825 control_char = e->control_char;
826 no_break_control_char = e->no_break_control_char;
827 hyphen_indicator_char = e->hyphen_indicator_char;
833 line_tabs = e->line_tabs;
834 current_tab = TAB_NONE;
836 margin_character_flags = e->margin_character_flags;
837 if (e->margin_character_node)
838 margin_character_node = e->margin_character_node->copy();
839 margin_character_distance = e->margin_character_distance;
841 number_text_separation = e->number_text_separation;
842 line_number_multiple = e->line_number_multiple;
843 line_number_indent = e->line_number_indent;
844 no_number_count = e->no_number_count;
845 tab_char = e->tab_char;
846 leader_char = e->leader_char;
847 hyphenation_flags = e->hyphenation_flags;
849 prev_fontno = e->prev_fontno;
852 prev_family = e->prev_family;
855 widow_control = e->widow_control;
856 #endif /* WIDOW_CONTROL */
857 hyphen_line_max = e->hyphen_line_max;
858 hyphen_line_count = 0;
859 hyphenation_space = e->hyphenation_space;
860 hyphenation_margin = e->hyphenation_margin;
862 glyph_color= e->glyph_color;
863 prev_glyph_color = e->prev_glyph_color;
864 fill_color = e->fill_color;
865 prev_fill_color = e->prev_fill_color;
868 environment::~environment()
871 delete_node_list(line);
872 delete_node_list(numbering_nodes);
875 hunits environment::get_input_line_position()
879 n = -input_line_start;
881 n = width_total - input_line_start;
887 void environment::set_input_line_position(hunits n)
889 input_line_start = line == 0 ? -n : width_total - n;
891 input_line_start += tab_width;
894 hunits environment::get_line_length()
899 hunits environment::get_saved_line_length()
902 return target_text_length + saved_indent;
907 vunits environment::get_vertical_spacing()
909 return vertical_spacing;
912 vunits environment::get_post_vertical_spacing()
914 return post_vertical_spacing;
917 int environment::get_line_spacing()
922 vunits environment::total_post_vertical_spacing()
924 vunits tem(post_vertical_spacing);
925 if (line_spacing > 1)
926 tem += (line_spacing - 1)*vertical_spacing;
930 int environment::get_bold()
932 return get_bold_fontno(fontno);
935 hunits environment::get_digit_width()
937 return env_digit_width(this);
940 int environment::get_adjust_mode()
945 int environment::get_fill()
950 hunits environment::get_indent()
955 hunits environment::get_saved_indent()
959 else if (have_temporary_indent)
960 return temporary_indent;
965 hunits environment::get_temporary_indent()
967 return temporary_indent;
970 hunits environment::get_title_length()
975 node *environment::get_prev_char()
977 for (node *n = current_tab ? tab_contents : line; n; n = n->next) {
978 node *last = n->last_char_node();
985 hunits environment::get_prev_char_width()
987 node *last = get_prev_char();
990 return last->width();
993 hunits environment::get_prev_char_skew()
995 node *last = get_prev_char();
1001 vunits environment::get_prev_char_height()
1003 node *last = get_prev_char();
1007 last->vertical_extent(&min, &max);
1011 vunits environment::get_prev_char_depth()
1013 node *last = get_prev_char();
1017 last->vertical_extent(&min, &max);
1021 hunits environment::get_text_length()
1023 hunits n = line == 0 ? H0 : width_total;
1029 hunits environment::get_prev_text_length()
1031 return prev_text_length;
1035 static int sb_reg_contents = 0;
1036 static int st_reg_contents = 0;
1037 static int ct_reg_contents = 0;
1038 static int rsb_reg_contents = 0;
1039 static int rst_reg_contents = 0;
1040 static int skw_reg_contents = 0;
1041 static int ssc_reg_contents = 0;
1043 void environment::width_registers()
1045 // this is used to implement \w; it sets the st, sb, ct registers
1046 vunits min = 0, max = 0, cur = 0;
1047 int character_type = 0;
1048 ssc_reg_contents = line ? line->subscript_correction().to_units() : 0;
1049 skw_reg_contents = line ? line->skew().to_units() : 0;
1050 line = reverse_node_list(line);
1051 vunits real_min = V0;
1052 vunits real_max = V0;
1054 for (node *tem = line; tem; tem = tem->next) {
1055 tem->vertical_extent(&v1, &v2);
1062 if ((cur += tem->vertical_width()) < min)
1066 character_type |= tem->character_type();
1068 line = reverse_node_list(line);
1069 st_reg_contents = -min.to_units();
1070 sb_reg_contents = -max.to_units();
1071 rst_reg_contents = -real_min.to_units();
1072 rsb_reg_contents = -real_max.to_units();
1073 ct_reg_contents = character_type;
1076 node *environment::extract_output_line()
1085 /* environment related requests */
1087 void environment_switch()
1089 int pop = 0; // 1 means pop, 2 means pop but no error message on underflow
1090 if (curenv->is_dummy())
1091 error("can't switch environments when current environment is dummy");
1092 else if (!has_arg())
1096 if (!tok.delimiter()) {
1097 // It looks like a number.
1099 if (get_integer(&n)) {
1100 if (n >= 0 && n < NENVIRONMENTS) {
1101 env_stack = new env_list(curenv, env_stack);
1102 if (env_table[n] == 0)
1103 env_table[n] = new environment(i_to_a(n));
1104 curenv = env_table[n];
1113 nm = get_long_name(1);
1117 if (!nm.is_null()) {
1118 environment *e = (environment *)env_dictionary.lookup(nm);
1120 e = new environment(nm);
1121 (void)env_dictionary.lookup(nm, e);
1123 env_stack = new env_list(curenv, env_stack);
1128 if (env_stack == 0) {
1130 error("environment stack underflow");
1133 int seen_space = curenv->seen_space;
1134 int seen_eol = curenv->seen_eol;
1135 int suppress_next_eol = curenv->suppress_next_eol;
1136 curenv = env_stack->env;
1137 curenv->seen_space = seen_space;
1138 curenv->seen_eol = seen_eol;
1139 curenv->suppress_next_eol = suppress_next_eol;
1140 env_list *tem = env_stack;
1141 env_stack = env_stack->next;
1148 void environment_copy()
1153 if (!tok.delimiter()) {
1154 // It looks like a number.
1156 if (get_integer(&n)) {
1157 if (n >= 0 && n < NENVIRONMENTS)
1164 nm = get_long_name(1);
1165 if (!e && !nm.is_null())
1166 e = (environment *)env_dictionary.lookup(nm);
1168 error("No environment to copy from");
1176 void fill_color_change()
1178 symbol s = get_name();
1180 curenv->set_fill_color(curenv->get_prev_fill_color());
1186 void glyph_color_change()
1188 symbol s = get_name();
1190 curenv->set_glyph_color(curenv->get_prev_glyph_color());
1196 static symbol P_symbol("P");
1200 symbol s = get_name();
1202 if (s.is_null() || s == P_symbol) {
1207 for (const char *p = s.contents(); p != 0 && *p != 0; p++)
1214 curenv->set_font(atoi(s.contents()));
1216 curenv->set_font(s);
1220 void family_change()
1222 symbol s = get_name();
1223 curenv->set_family(s);
1230 if (has_arg() && get_number(&n, 'z', curenv->get_requested_point_size())) {
1233 curenv->set_size(n);
1236 curenv->set_size(0);
1240 void override_sizes()
1243 int *sizes = new int[n];
1245 char *buf = read_string();
1248 char *p = strtok(buf, " \t");
1253 switch (sscanf(p, "%d-%d", &lower, &upper)) {
1258 if (lower <= upper && lower >= 0)
1262 warning(WARN_RANGE, "bad size range `%1'", p);
1266 int *old_sizes = sizes;
1267 sizes = new int[n*2];
1268 memcpy(sizes, old_sizes, n*sizeof(int));
1276 p = strtok(0, " \t");
1278 font_size::init_size_table(sizes);
1284 if (get_integer(&n)) {
1285 curenv->space_size = n;
1286 if (has_arg() && get_integer(&n))
1287 curenv->sentence_space_size = n;
1289 curenv->sentence_space_size = curenv->space_size;
1296 while (!tok.newline() && !tok.eof())
1306 while (!tok.newline() && !tok.eof())
1311 curenv->suppress_next_eol = 1;
1318 if (!has_arg() || !get_integer(&n))
1322 while (!tok.newline() && !tok.eof())
1326 curenv->right_justify_lines = 0;
1327 curenv->center_lines = n;
1328 curdiv->modified_tag.incl(MTSM_CE);
1332 void right_justify()
1335 if (!has_arg() || !get_integer(&n))
1339 while (!tok.newline() && !tok.eof())
1343 curenv->center_lines = 0;
1344 curenv->right_justify_lines = n;
1345 curdiv->modified_tag.incl(MTSM_RJ);
1352 if (has_arg() && get_hunits(&temp, 'm', curenv->line_length)) {
1354 warning(WARN_RANGE, "bad line length %1u", temp.to_units());
1359 temp = curenv->prev_line_length;
1360 curenv->prev_line_length = curenv->line_length;
1361 curenv->line_length = temp;
1362 curdiv->modified_tag.incl(MTSM_LL);
1369 if (has_arg() && get_hunits(&temp, 'm', curenv->title_length)) {
1371 warning(WARN_RANGE, "bad title length %1u", temp.to_units());
1376 temp = curenv->prev_title_length;
1377 curenv->prev_title_length = curenv->title_length;
1378 curenv->title_length = temp;
1382 void vertical_spacing()
1385 if (has_arg() && get_vunits(&temp, 'p', curenv->vertical_spacing)) {
1387 warning(WARN_RANGE, "vertical spacing must not be negative");
1392 temp = curenv->prev_vertical_spacing;
1393 curenv->prev_vertical_spacing = curenv->vertical_spacing;
1394 curenv->vertical_spacing = temp;
1398 void post_vertical_spacing()
1401 if (has_arg() && get_vunits(&temp, 'p', curenv->post_vertical_spacing)) {
1404 "post vertical spacing must be greater than or equal to 0");
1409 temp = curenv->prev_post_vertical_spacing;
1410 curenv->prev_post_vertical_spacing = curenv->post_vertical_spacing;
1411 curenv->post_vertical_spacing = temp;
1418 if (has_arg() && get_integer(&temp)) {
1420 warning(WARN_RANGE, "value %1 out of range: interpreted as 1", temp);
1425 temp = curenv->prev_line_spacing;
1426 curenv->prev_line_spacing = curenv->line_spacing;
1427 curenv->line_spacing = temp;
1434 if (has_arg() && get_hunits(&temp, 'm', curenv->indent)) {
1436 warning(WARN_RANGE, "indent cannot be negative");
1441 temp = curenv->prev_indent;
1442 while (!tok.newline() && !tok.eof())
1446 curenv->have_temporary_indent = 0;
1447 curenv->prev_indent = curenv->indent;
1448 curenv->indent = temp;
1449 curdiv->modified_tag.incl(MTSM_IN);
1453 void temporary_indent()
1457 if (!get_hunits(&temp, 'm', curenv->get_indent()))
1459 while (!tok.newline() && !tok.eof())
1464 warning(WARN_RANGE, "total indent cannot be negative");
1468 curenv->temporary_indent = temp;
1469 curenv->have_temporary_indent = 1;
1470 curdiv->modified_tag.incl(MTSM_TI);
1475 node *do_underline_special(int underline_spaces)
1478 m.append_str("x u ");
1479 m.append(underline_spaces + '0');
1480 return new special_node(m, 1);
1483 void do_underline(int underline_spaces)
1486 if (!has_arg() || !get_integer(&n))
1489 if (curenv->underline_lines > 0) {
1490 curenv->prev_fontno = curenv->fontno;
1491 curenv->fontno = curenv->pre_underline_fontno;
1492 if (underline_spaces) {
1493 curenv->underline_spaces = 0;
1494 curenv->add_node(do_underline_special(0));
1497 curenv->underline_lines = 0;
1500 curenv->underline_lines = n;
1501 curenv->pre_underline_fontno = curenv->fontno;
1502 curenv->fontno = get_underline_fontno();
1503 if (underline_spaces) {
1504 curenv->underline_spaces = 1;
1505 curenv->add_node(do_underline_special(1));
1511 void continuous_underline()
1523 curenv->control_char = '.';
1526 error("bad control character");
1528 curenv->control_char = tok.ch();
1533 void no_break_control_char()
1535 curenv->no_break_control_char = '\'';
1538 error("bad control character");
1540 curenv->no_break_control_char = tok.ch();
1545 void margin_character()
1549 charinfo *ci = tok.get_char();
1551 // Call tok.next() only after making the node so that
1552 // .mc \s+9\(br\s0 works.
1553 node *nd = curenv->make_char_node(ci);
1556 delete curenv->margin_character_node;
1557 curenv->margin_character_node = nd;
1558 curenv->margin_character_flags = MARGIN_CHARACTER_ON
1559 | MARGIN_CHARACTER_NEXT;
1561 if (has_arg() && get_hunits(&d, 'm'))
1562 curenv->margin_character_distance = d;
1566 check_missing_character();
1567 curenv->margin_character_flags &= ~MARGIN_CHARACTER_ON;
1568 if (curenv->margin_character_flags == 0) {
1569 delete curenv->margin_character_node;
1570 curenv->margin_character_node = 0;
1578 delete_node_list(curenv->numbering_nodes);
1579 curenv->numbering_nodes = 0;
1582 for (int i = '9'; i >= '0'; i--) {
1583 node *tem = make_node(charset_table[i], curenv);
1591 curenv->numbering_nodes = nd;
1592 curenv->line_number_digit_width = env_digit_width(curenv);
1594 if (!tok.delimiter()) {
1595 if (get_integer(&n, next_line_number)) {
1596 next_line_number = n;
1597 if (next_line_number < 0) {
1598 warning(WARN_RANGE, "negative line number");
1599 next_line_number = 0;
1604 while (!tok.space() && !tok.newline() && !tok.eof())
1607 if (!tok.delimiter()) {
1608 if (get_integer(&n)) {
1610 warning(WARN_RANGE, "negative or zero line number multiple");
1613 curenv->line_number_multiple = n;
1617 while (!tok.space() && !tok.newline() && !tok.eof())
1620 if (!tok.delimiter()) {
1621 if (get_integer(&n))
1622 curenv->number_text_separation = n;
1625 while (!tok.space() && !tok.newline() && !tok.eof())
1627 if (has_arg() && !tok.delimiter() && get_integer(&n))
1628 curenv->line_number_indent = n;
1638 if (has_arg() && get_integer(&n))
1639 curenv->no_number_count = n > 0 ? n : 0;
1641 curenv->no_number_count = 1;
1647 curenv->hyphenation_flags = 0;
1651 void hyphenate_request()
1654 if (has_arg() && get_integer(&n))
1655 curenv->hyphenation_flags = n;
1657 curenv->hyphenation_flags = 1;
1663 curenv->hyphen_indicator_char = get_optional_char();
1667 void hyphen_line_max_request()
1670 if (has_arg() && get_integer(&n))
1671 curenv->hyphen_line_max = n;
1673 curenv->hyphen_line_max = -1;
1677 void environment::interrupt()
1680 add_node(new transparent_dummy_node);
1685 void environment::newline()
1687 int was_centered = 0;
1688 if (underline_lines > 0) {
1689 if (--underline_lines == 0) {
1690 prev_fontno = fontno;
1691 fontno = pre_underline_fontno;
1692 if (underline_spaces) {
1693 underline_spaces = 0;
1694 add_node(do_underline_special(0));
1702 // strip trailing spaces
1703 while (line != 0 && line->discardable()) {
1704 width_total -= line->width();
1705 space_total -= line->nspaces();
1710 node *to_be_output = 0;
1711 hunits to_be_output_width;
1712 prev_line_interrupted = 0;
1715 else if (interrupted) {
1717 // see environment::final_break
1718 prev_line_interrupted = exit_started ? 2 : 1;
1720 else if (center_lines > 0) {
1722 hunits x = target_text_length - width_total;
1724 saved_indent += x/2;
1725 to_be_output = line;
1727 to_be_output_width = width_total;
1730 else if (right_justify_lines > 0) {
1731 --right_justify_lines;
1732 hunits x = target_text_length - width_total;
1735 to_be_output = line;
1736 to_be_output_width = width_total;
1742 to_be_output = line;
1743 to_be_output_width = width_total;
1746 input_line_start = line == 0 ? H0 : width_total;
1748 if (is_html && !fill) {
1749 curdiv->modified_tag.incl(MTSM_EOL);
1750 if (suppress_next_eol)
1751 suppress_next_eol = 0;
1756 output_line(to_be_output, to_be_output_width, was_centered);
1757 hyphen_line_count = 0;
1759 if (input_trap_count > 0) {
1760 if (!(continued_input_trap && prev_line_interrupted))
1761 if (--input_trap_count == 0)
1762 spring_trap(input_trap);
1766 void environment::output_line(node *n, hunits width, int was_centered)
1768 prev_text_length = width;
1769 if (margin_character_flags) {
1770 hunits d = line_length + margin_character_distance - saved_indent - width;
1772 n = new hmotion_node(d, get_fill_color(), n);
1775 margin_character_flags &= ~MARGIN_CHARACTER_NEXT;
1777 if (!margin_character_flags) {
1778 tem = margin_character_node;
1779 margin_character_node = 0;
1782 tem = margin_character_node->copy();
1785 width += tem->width();
1789 node *tem = n->next;
1794 if (!saved_indent.is_zero())
1795 nn = new hmotion_node(saved_indent, get_fill_color(), nn);
1796 width += saved_indent;
1797 if (no_number_count > 0)
1799 else if (numbering_nodes) {
1800 hunits w = (line_number_digit_width
1801 *(3+line_number_indent+number_text_separation));
1802 if (next_line_number % line_number_multiple != 0)
1803 nn = new hmotion_node(w, get_fill_color(), nn);
1806 nn = new hmotion_node(number_text_separation * line_number_digit_width,
1807 get_fill_color(), nn);
1808 x -= number_text_separation*line_number_digit_width;
1810 sprintf(buf, "%3d", next_line_number);
1811 for (char *p = strchr(buf, '\0') - 1; p >= buf && *p != ' '; --p) {
1812 node *gn = numbering_nodes;
1813 for (int count = *p - '0'; count > 0; count--)
1820 nn = new hmotion_node(x, get_fill_color(), nn);
1825 output(nn, !fill, vertical_spacing, total_post_vertical_spacing(), width,
1829 void environment::start_line()
1833 line = new line_start_node;
1834 if (have_temporary_indent) {
1835 saved_indent = temporary_indent;
1836 have_temporary_indent = 0;
1839 saved_indent = indent;
1840 target_text_length = line_length - saved_indent;
1845 hunits environment::get_hyphenation_space()
1847 return hyphenation_space;
1850 void hyphenation_space_request()
1853 if (get_hunits(&n, 'm')) {
1855 warning(WARN_RANGE, "hyphenation space cannot be negative");
1858 curenv->hyphenation_space = n;
1863 hunits environment::get_hyphenation_margin()
1865 return hyphenation_margin;
1868 void hyphenation_margin_request()
1871 if (get_hunits(&n, 'm')) {
1873 warning(WARN_RANGE, "hyphenation margin cannot be negative");
1876 curenv->hyphenation_margin = n;
1881 breakpoint *environment::choose_breakpoint()
1883 hunits x = width_total;
1884 int s = space_total;
1886 breakpoint *best_bp = 0; // the best breakpoint so far
1887 int best_bp_fits = 0;
1891 breakpoint *bp = n->get_breakpoints(x, s);
1893 if (bp->width <= target_text_length) {
1894 if (!bp->hyphenated) {
1895 breakpoint *tem = bp->next;
1898 breakpoint *tem1 = tem;
1903 // Decide whether to use the hyphenated breakpoint.
1904 && (hyphen_line_max < 0
1905 // Only choose the hyphenated breakpoint if it would not
1906 // exceed the maximum number of consecutive hyphenated
1908 || hyphen_line_count + 1 <= hyphen_line_max)
1909 && !(adjust_mode == ADJUST_BOTH
1910 // Don't choose the hyphenated breakpoint if the line
1911 // can be justified by adding no more than
1912 // hyphenation_space to any word space.
1914 && (((target_text_length - bp->width
1915 + (bp->nspaces - 1)*hresolution)/bp->nspaces)
1916 <= hyphenation_space))
1917 // Don't choose the hyphenated breakpoint if the line
1918 // is no more than hyphenation_margin short.
1919 : target_text_length - bp->width <= hyphenation_margin)) {
1928 if ((adjust_mode == ADJUST_BOTH
1929 ? hyphenation_space == H0
1930 : hyphenation_margin == H0)
1931 && (hyphen_line_max < 0
1932 || hyphen_line_count + 1 <= hyphen_line_max)) {
1933 // No need to consider a non-hyphenated breakpoint.
1936 breakpoint *tem = bp->next;
1939 breakpoint *tem1 = tem;
1945 // It fits but it's hyphenated.
1946 if (!best_bp_fits) {
1954 breakpoint *tem = bp;
1971 output_warning(WARN_BREAK, "can't break line");
1977 void environment::hyphenate_line(int start_here)
1980 hyphenation_type prev_type = line->get_hyphenation_type();
1985 for (startp = &line->next; *startp != 0; startp = &(*startp)->next) {
1986 hyphenation_type this_type = (*startp)->get_hyphenation_type();
1987 if (prev_type == HYPHEN_BOUNDARY && this_type == HYPHEN_MIDDLE)
1989 prev_type = this_type;
1993 node *tem = *startp;
1996 } while (tem != 0 && tem->get_hyphenation_type() == HYPHEN_MIDDLE);
1997 int inhibit = (tem != 0 && tem->get_hyphenation_type() == HYPHEN_INHIBIT);
1999 hyphen_list *sl = 0;
2003 while (tem != end) {
2004 sl = tem->get_hyphen_list(sl, &i);
2007 tem1->next = forward;
2011 // this is for characters like hyphen and emdash
2013 for (hyphen_list *h = sl; h; h = h->next) {
2014 h->breakable = (prev_code != 0
2016 && h->next->hyphenation_code != 0);
2017 prev_code = h->hyphenation_code;
2020 if (hyphenation_flags != 0
2022 // this may not be right if we have extra space on this line
2023 && !((hyphenation_flags & HYPHEN_LAST_LINE)
2024 && (curdiv->distance_to_next_trap()
2025 <= vertical_spacing + total_post_vertical_spacing()))
2027 hyphenate(sl, hyphenation_flags);
2028 while (forward != 0) {
2029 node *tem1 = forward;
2030 forward = forward->next;
2032 tem = tem1->add_self(tem, &sl);
2037 static node *node_list_reverse(node *n)
2049 static void distribute_space(node *n, int nspaces, hunits desired_space,
2050 int force_reverse = 0)
2052 static int reverse = 0;
2053 if (force_reverse || reverse)
2054 n = node_list_reverse(n);
2055 if (!force_reverse && nspaces > 0 && spread_limit >= 0
2056 && desired_space.to_units() > 0) {
2057 hunits em = curenv->get_size();
2058 double Ems = (double)desired_space.to_units() / nspaces
2059 / (em.is_zero() ? hresolution : em.to_units());
2060 if (Ems > spread_limit)
2061 output_warning(WARN_BREAK, "spreading %1m per space", Ems);
2063 for (node *tem = n; tem; tem = tem->next)
2064 tem->spread_space(&nspaces, &desired_space);
2065 if (force_reverse || reverse)
2066 (void)node_list_reverse(n);
2069 assert(desired_space.is_zero() && nspaces == 0);
2072 void environment::possibly_break_line(int start_here, int forced)
2074 int was_centered = center_lines > 0;
2075 if (!fill || current_tab || current_field || dummy)
2079 // When a macro follows a paragraph in fill mode, the
2080 // current line should not be empty.
2081 || (width_total - line->width()) > target_text_length)) {
2082 hyphenate_line(start_here);
2083 breakpoint *bp = choose_breakpoint();
2085 // we'll find one eventually
2089 while (*ndp != bp->nd)
2090 ndp = &(*ndp)->next;
2091 bp->nd->split(bp->index, &pre, &post);
2093 hunits extra_space_width = H0;
2094 switch(adjust_mode) {
2096 if (bp->nspaces != 0)
2097 extra_space_width = target_text_length - bp->width;
2098 else if (bp->width > 0 && target_text_length > 0
2099 && target_text_length > bp->width)
2100 output_warning(WARN_BREAK, "cannot adjust line");
2103 saved_indent += (target_text_length - bp->width)/2;
2107 saved_indent += target_text_length - bp->width;
2110 distribute_space(pre, bp->nspaces, extra_space_width);
2111 hunits output_width = bp->width + extra_space_width;
2112 input_line_start -= output_width;
2114 hyphen_line_count++;
2116 hyphen_line_count = 0;
2120 node *first_non_discardable = 0;
2122 for (tem = line; tem != 0; tem = tem->next)
2123 if (!tem->discardable())
2124 first_non_discardable = tem;
2125 node *to_be_discarded;
2126 if (first_non_discardable) {
2127 to_be_discarded = first_non_discardable->next;
2128 first_non_discardable->next = 0;
2129 for (tem = line; tem != 0; tem = tem->next) {
2130 width_total += tem->width();
2131 space_total += tem->nspaces();
2137 to_be_discarded = line;
2140 // Do output_line() here so that line will be 0 iff the
2141 // the environment will be empty.
2142 output_line(pre, output_width, was_centered);
2143 while (to_be_discarded != 0) {
2144 tem = to_be_discarded;
2145 to_be_discarded = to_be_discarded->next;
2146 input_line_start -= tem->width();
2150 if (have_temporary_indent) {
2151 saved_indent = temporary_indent;
2152 have_temporary_indent = 0;
2155 saved_indent = indent;
2156 target_text_length = line_length - saved_indent;
2162 Do the break at the end of input after the end macro (if any).
2164 Unix troff behaves as follows: if the last line is
2168 it will output foo on the current page, and bar on the next page;
2177 everything will be output on the current page. This behaviour must be
2180 The problem is that some macro packages rely on this. For example,
2181 the ATK macros have an end macro that emits \c if it needs to print a
2182 table of contents but doesn't do a 'bp in the end macro; instead the
2183 'bp is done in the bottom of page trap. This works with Unix troff,
2184 provided that the current environment is not empty at the end of the
2187 The following will make macro packages that do that sort of thing work
2188 even if the current environment is empty at the end of the input file.
2189 If the last input line used \c and this line occurred in the end macro,
2190 then we'll force everything out on the current page, but we'll make
2191 sure that the environment isn't empty so that we won't exit at the
2192 bottom of this page.
2195 void environment::final_break()
2197 if (prev_line_interrupted == 2) {
2199 add_node(new transparent_dummy_node);
2205 node *environment::make_tag(const char *nm, int i)
2209 * need to emit tag for post-grohtml
2210 * but we check to see whether we can emit specials
2212 if (curdiv == topdiv && topdiv->before_first_page)
2213 topdiv->begin_page();
2216 m.append_str("devtag:");
2217 for (const char *p = nm; *p; p++)
2218 if (!invalid_input_char((unsigned char)*p))
2222 return new special_node(m);
2227 void environment::dump_troff_state()
2230 fprintf(stderr, SPACES "register `in' = %d\n", curenv->indent.to_units());
2231 if (curenv->have_temporary_indent)
2232 fprintf(stderr, SPACES "register `ti' = %d\n",
2233 curenv->temporary_indent.to_units());
2234 fprintf(stderr, SPACES "centered lines `ce' = %d\n", curenv->center_lines);
2235 fprintf(stderr, SPACES "register `ll' = %d\n",
2236 curenv->line_length.to_units());
2237 fprintf(stderr, SPACES "fill `fi=1/nf=0' = %d\n", curenv->fill);
2238 fprintf(stderr, SPACES "page offset `po' = %d\n",
2239 topdiv->get_page_offset().to_units());
2240 fprintf(stderr, SPACES "seen_break = %d\n", curenv->seen_break);
2241 fprintf(stderr, SPACES "seen_space = %d\n", curenv->seen_space);
2246 statem *environment::construct_state(int only_eol)
2249 statem *s = new statem();
2251 s->add_tag(MTSM_IN, indent);
2252 s->add_tag(MTSM_LL, line_length);
2253 s->add_tag(MTSM_PO, topdiv->get_page_offset().to_units());
2254 s->add_tag(MTSM_RJ, right_justify_lines);
2255 if (have_temporary_indent)
2256 s->add_tag(MTSM_TI, temporary_indent);
2259 s->add_tag(MTSM_BR);
2260 if (seen_space != 0)
2261 s->add_tag(MTSM_SP, seen_space);
2266 s->add_tag(MTSM_EOL);
2267 s->add_tag(MTSM_CE, center_lines);
2276 void environment::construct_format_state(node *n, int was_centered,
2280 // find first glyph node which has a state.
2281 while (n != 0 && n->state == 0)
2283 if (n == 0 || (n->state == 0))
2285 if (seen_space != 0)
2286 n->state->add_tag(MTSM_SP, seen_space);
2287 if (seen_eol && topdiv == curdiv)
2288 n->state->add_tag(MTSM_EOL);
2292 n->state->add_tag(MTSM_CE, center_lines+1);
2294 n->state->add_tag_if_unknown(MTSM_CE, 0);
2295 n->state->add_tag_if_unknown(MTSM_FI, filling);
2298 if (n->state != 0) {
2299 n->state->sub_tag_ce();
2300 n->state->add_tag_if_unknown(MTSM_FI, filling);
2307 void environment::construct_new_line_state(node *n)
2310 // find first glyph node which has a state.
2311 while (n != 0 && n->state == 0)
2313 if (n == 0 || n->state == 0)
2315 if (seen_space != 0)
2316 n->state->add_tag(MTSM_SP, seen_space);
2317 if (seen_eol && topdiv == curdiv)
2318 n->state->add_tag(MTSM_EOL);
2324 extern int global_diverted_space;
2326 void environment::do_break(int do_spread)
2328 int was_centered = 0;
2329 if (curdiv == topdiv && topdiv->before_first_page) {
2330 topdiv->begin_page();
2336 // this is so that hyphenation works
2337 if (line->nspaces() == 0) {
2338 line = new space_node(H0, get_fill_color(), line);
2341 possibly_break_line(0, do_spread);
2343 while (line != 0 && line->discardable()) {
2344 width_total -= line->width();
2345 space_total -= line->nspaces();
2351 input_line_start = H0;
2354 switch (adjust_mode) {
2356 saved_indent += (target_text_length - width_total)/2;
2360 saved_indent += target_text_length - width_total;
2366 output_line(tem, width_total, was_centered);
2367 hyphen_line_count = 0;
2369 prev_line_interrupted = 0;
2370 #ifdef WIDOW_CONTROL
2372 output_pending_lines();
2373 #endif /* WIDOW_CONTROL */
2374 if (!global_diverted_space) {
2375 curdiv->modified_tag.incl(MTSM_BR);
2380 int environment::is_empty()
2382 return !current_tab && line == 0 && pending_lines == 0;
2385 void do_break_request(int spread)
2387 while (!tok.newline() && !tok.eof())
2390 curenv->do_break(spread);
2394 void break_request()
2396 do_break_request(0);
2399 void break_spread_request()
2401 do_break_request(1);
2406 if (curdiv == topdiv && topdiv->before_first_page) {
2407 handle_initial_title();
2411 hunits part_width[3];
2412 part[0] = part[1] = part[2] = 0;
2413 environment env(curenv);
2414 environment *oldenv = curenv;
2416 read_title_parts(part, part_width);
2418 curenv->size = env.size;
2419 curenv->prev_size = env.prev_size;
2420 curenv->requested_size = env.requested_size;
2421 curenv->prev_requested_size = env.prev_requested_size;
2422 curenv->char_height = env.char_height;
2423 curenv->char_slant = env.char_slant;
2424 curenv->fontno = env.fontno;
2425 curenv->prev_fontno = env.prev_fontno;
2426 curenv->glyph_color = env.glyph_color;
2427 curenv->prev_glyph_color = env.prev_glyph_color;
2428 curenv->fill_color = env.fill_color;
2429 curenv->prev_fill_color = env.prev_fill_color;
2438 hunits length_title(curenv->title_length);
2439 hunits f = length_title - part_width[1];
2441 n = new hmotion_node(f2 - part_width[2], curenv->get_fill_color(), n);
2449 n = new hmotion_node(f - f2 - part_width[0], curenv->get_fill_color(), n);
2457 curenv->output_title(n, !curenv->fill, curenv->vertical_spacing,
2458 curenv->total_post_vertical_spacing(), length_title);
2459 curenv->hyphen_line_count = 0;
2465 curenv->adjust_mode |= 1;
2469 curenv->adjust_mode = ADJUST_LEFT;
2472 curenv->adjust_mode = ADJUST_RIGHT;
2475 curenv->adjust_mode = ADJUST_CENTER;
2479 curenv->adjust_mode = ADJUST_BOTH;
2483 if (get_integer(&n)) {
2485 warning(WARN_RANGE, "negative adjustment mode");
2487 curenv->adjust_mode = 5;
2488 warning(WARN_RANGE, "adjustment mode `%1' out of range", n);
2491 curenv->adjust_mode = n;
2500 curenv->adjust_mode &= ~1;
2504 void do_input_trap(int continued)
2506 curenv->input_trap_count = 0;
2508 curenv->continued_input_trap = 1;
2510 if (has_arg() && get_integer(&n)) {
2513 "number of lines for input trap must be greater than zero");
2515 symbol s = get_name(1);
2517 curenv->input_trap_count = n;
2518 curenv->input_trap = s;
2530 void input_trap_continued()
2537 // must not be R or C or L or a legitimate part of a number expression
2538 const char TAB_REPEAT_CHAR = 'T';
2544 tab(hunits, tab_type);
2545 enum { BLOCK = 1024 };
2546 static tab *free_list;
2547 void *operator new(size_t);
2548 void operator delete(void *);
2551 tab *tab::free_list = 0;
2553 void *tab::operator new(size_t n)
2555 assert(n == sizeof(tab));
2557 free_list = (tab *)new char[sizeof(tab)*BLOCK];
2558 for (int i = 0; i < BLOCK - 1; i++)
2559 free_list[i].next = free_list + i + 1;
2560 free_list[BLOCK-1].next = 0;
2563 free_list = (tab *)(free_list->next);
2569 /* cfront can't cope with this. */
2572 void tab::operator delete(void *p)
2575 ((tab *)p)->next = free_list;
2576 free_list = (tab *)p;
2580 tab::tab(hunits x, tab_type t) : next(0), pos(x), type(t)
2584 tab_stops::tab_stops(hunits distance, tab_type type)
2587 repeated_list = new tab(distance, type);
2590 tab_stops::~tab_stops()
2595 tab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance)
2599 return distance_to_next_tab(curpos, distance, &nextpos);
2602 tab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance,
2607 for (tem = initial_list; tem && tem->pos <= curpos; tem = tem->next)
2610 *distance = tem->pos - curpos;
2611 *nextpos = tem->pos;
2614 if (repeated_list == 0)
2616 hunits base = lastpos;
2618 for (tem = repeated_list; tem && tem->pos + base <= curpos; tem = tem->next)
2621 *distance = tem->pos + base - curpos;
2622 *nextpos = tem->pos + base;
2625 assert(lastpos > 0);
2631 const char *tab_stops::to_string()
2633 static char *buf = 0;
2634 static int buf_size = 0;
2635 // figure out a maximum on the amount of space we can need
2638 for (p = initial_list; p; p = p->next)
2640 for (p = repeated_list; p; p = p->next)
2642 // (10 for digits + 1 for u + 1 for 'C' or 'R') + 2 for ' &' + 1 for '\0'
2643 int need = count*12 + 3;
2644 if (buf == 0 || need > buf_size) {
2648 buf = new char[buf_size];
2651 for (p = initial_list; p; p = p->next) {
2652 strcpy(ptr, i_to_a(p->pos.to_units()));
2653 ptr = strchr(ptr, '\0');
2671 *ptr++ = TAB_REPEAT_CHAR;
2672 for (p = repeated_list; p; p = p->next) {
2673 strcpy(ptr, i_to_a(p->pos.to_units()));
2674 ptr = strchr(ptr, '\0');
2695 tab_stops::tab_stops() : initial_list(0), repeated_list(0)
2699 tab_stops::tab_stops(const tab_stops &ts)
2700 : initial_list(0), repeated_list(0)
2702 tab **p = &initial_list;
2703 tab *t = ts.initial_list;
2705 *p = new tab(t->pos, t->type);
2710 t = ts.repeated_list;
2712 *p = new tab(t->pos, t->type);
2718 void tab_stops::clear()
2720 while (initial_list) {
2721 tab *tem = initial_list;
2722 initial_list = initial_list->next;
2725 while (repeated_list) {
2726 tab *tem = repeated_list;
2727 repeated_list = repeated_list->next;
2732 void tab_stops::add_tab(hunits pos, tab_type type, int repeated)
2735 for (p = repeated ? &repeated_list : &initial_list; *p; p = &(*p)->next)
2737 *p = new tab(pos, type);
2741 void tab_stops::operator=(const tab_stops &ts)
2744 tab **p = &initial_list;
2745 tab *t = ts.initial_list;
2747 *p = new tab(t->pos, t->type);
2752 t = ts.repeated_list;
2754 *p = new tab(t->pos, t->type);
2763 hunits prev_pos = 0;
2768 if (tok.ch() == TAB_REPEAT_CHAR) {
2773 if (!get_hunits(&pos, 'm', prev_pos))
2775 tab_type type = TAB_LEFT;
2776 if (tok.ch() == 'C') {
2780 else if (tok.ch() == 'R') {
2784 else if (tok.ch() == 'L') {
2787 if (pos <= prev_pos && !first)
2789 "positions of tab stops must be strictly increasing");
2791 tabs.add_tab(pos, type, repeated);
2796 curenv->tabs = tabs;
2797 curdiv->modified_tag.incl(MTSM_TA);
2801 const char *environment::get_tabs()
2803 return tabs.to_string();
2806 tab_type environment::distance_to_next_tab(hunits *distance)
2809 ? curenv->tabs.distance_to_next_tab(get_text_length(), distance)
2810 : curenv->tabs.distance_to_next_tab(get_input_line_position(), distance);
2813 tab_type environment::distance_to_next_tab(hunits *distance, hunits *leftpos)
2816 ? curenv->tabs.distance_to_next_tab(get_text_length(), distance, leftpos)
2817 : curenv->tabs.distance_to_next_tab(get_input_line_position(), distance,
2821 void field_characters()
2823 field_delimiter_char = get_optional_char();
2824 if (field_delimiter_char)
2825 padding_indicator_char = get_optional_char();
2827 padding_indicator_char = 0;
2831 void line_tabs_request()
2834 if (has_arg() && get_integer(&n))
2835 curenv->line_tabs = n != 0;
2837 curenv->line_tabs = 1;
2841 int environment::get_line_tabs()
2846 void environment::wrap_up_tab()
2853 switch (current_tab) {
2855 tab_amount = tab_distance - tab_width;
2856 line = make_tab_node(tab_amount, line);
2859 tab_amount = tab_distance - tab_width/2;
2860 line = make_tab_node(tab_amount, line);
2867 width_total += tab_amount;
2868 width_total += tab_width;
2869 if (current_field) {
2870 if (tab_precedes_field) {
2871 pre_field_width += tab_amount;
2872 tab_precedes_field = 0;
2874 field_distance -= tab_amount;
2875 field_spaces += tab_field_spaces;
2877 if (tab_contents != 0) {
2879 for (tem = tab_contents; tem->next != 0; tem = tem->next)
2882 line = tab_contents;
2884 tab_field_spaces = 0;
2888 current_tab = TAB_NONE;
2891 node *environment::make_tab_node(hunits d, node *next)
2893 if (leader_node != 0 && d < 0) {
2894 error("motion generated by leader cannot be negative");
2899 return new hmotion_node(d, 1, 0, get_fill_color(), next);
2900 node *n = new hline_node(d, leader_node, next);
2905 void environment::handle_tab(int is_leader)
2911 charinfo *ci = is_leader ? leader_char : tab_char;
2913 leader_node = ci ? make_char_node(ci) : 0;
2914 tab_type t = distance_to_next_tab(&d, &absolute);
2919 add_node(make_tag("tab L", absolute.to_units()));
2920 add_node(make_tab_node(d));
2923 add_node(make_tag("tab R", absolute.to_units()));
2926 add_node(make_tag("tab C", absolute.to_units()));
2935 tab_field_spaces = 0;
2938 void environment::start_field()
2940 assert(!current_field);
2942 if (distance_to_next_tab(&d) != TAB_NONE) {
2943 pre_field_width = get_text_length();
2947 tab_field_spaces = 0;
2948 for (node *p = line; p; p = p->next)
2953 tab_precedes_field = current_tab != TAB_NONE;
2956 error("zero field width");
2959 void environment::wrap_up_field()
2961 if (!current_tab && field_spaces == 0)
2963 hunits padding = field_distance - (get_text_length() - pre_field_width);
2964 if (current_tab && tab_field_spaces != 0) {
2965 hunits tab_padding = scale(padding,
2967 field_spaces + tab_field_spaces);
2968 padding -= tab_padding;
2969 distribute_space(tab_contents, tab_field_spaces, tab_padding, 1);
2970 tab_field_spaces = 0;
2971 tab_width += tab_padding;
2973 if (field_spaces != 0) {
2974 distribute_space(line, field_spaces, padding, 1);
2975 width_total += padding;
2977 // the start of the tab has been moved to the right by padding, so
2978 tab_distance -= padding;
2979 if (tab_distance <= H0) {
2980 // use the next tab stop instead
2981 current_tab = tabs.distance_to_next_tab(get_input_line_position()
2984 if (current_tab == TAB_NONE || current_tab == TAB_LEFT) {
2985 width_total += tab_width;
2986 if (current_tab == TAB_LEFT) {
2987 line = make_tab_node(tab_distance, line);
2988 width_total += tab_distance;
2989 current_tab = TAB_NONE;
2991 if (tab_contents != 0) {
2993 for (tem = tab_contents; tem->next != 0; tem = tem->next)
2996 line = tab_contents;
3008 void environment::add_padding()
3011 tab_contents = new space_node(H0, get_fill_color(), tab_contents);
3017 line = new space_node(H0, get_fill_color(), line);
3022 typedef int (environment::*INT_FUNCP)();
3023 typedef vunits (environment::*VUNITS_FUNCP)();
3024 typedef hunits (environment::*HUNITS_FUNCP)();
3025 typedef const char *(environment::*STRING_FUNCP)();
3027 class int_env_reg : public reg {
3030 int_env_reg(INT_FUNCP);
3031 const char *get_string();
3032 int get_value(units *val);
3035 class vunits_env_reg : public reg {
3038 vunits_env_reg(VUNITS_FUNCP f);
3039 const char *get_string();
3040 int get_value(units *val);
3044 class hunits_env_reg : public reg {
3047 hunits_env_reg(HUNITS_FUNCP f);
3048 const char *get_string();
3049 int get_value(units *val);
3052 class string_env_reg : public reg {
3055 string_env_reg(STRING_FUNCP);
3056 const char *get_string();
3059 int_env_reg::int_env_reg(INT_FUNCP f) : func(f)
3063 int int_env_reg::get_value(units *val)
3065 *val = (curenv->*func)();
3069 const char *int_env_reg::get_string()
3071 return i_to_a((curenv->*func)());
3074 vunits_env_reg::vunits_env_reg(VUNITS_FUNCP f) : func(f)
3078 int vunits_env_reg::get_value(units *val)
3080 *val = (curenv->*func)().to_units();
3084 const char *vunits_env_reg::get_string()
3086 return i_to_a((curenv->*func)().to_units());
3089 hunits_env_reg::hunits_env_reg(HUNITS_FUNCP f) : func(f)
3093 int hunits_env_reg::get_value(units *val)
3095 *val = (curenv->*func)().to_units();
3099 const char *hunits_env_reg::get_string()
3101 return i_to_a((curenv->*func)().to_units());
3104 string_env_reg::string_env_reg(STRING_FUNCP f) : func(f)
3108 const char *string_env_reg::get_string()
3110 return (curenv->*func)();
3113 class horizontal_place_reg : public general_reg {
3115 horizontal_place_reg();
3116 int get_value(units *);
3117 void set_value(units);
3120 horizontal_place_reg::horizontal_place_reg()
3124 int horizontal_place_reg::get_value(units *res)
3126 *res = curenv->get_input_line_position().to_units();
3130 void horizontal_place_reg::set_value(units n)
3132 curenv->set_input_line_position(hunits(n));
3135 int environment::get_zoom()
3137 return env_get_zoom(this);
3140 const char *environment::get_font_family_string()
3142 return family->nm.contents();
3145 const char *environment::get_glyph_color_string()
3147 return glyph_color->nm.contents();
3150 const char *environment::get_fill_color_string()
3152 return fill_color->nm.contents();
3155 const char *environment::get_font_name_string()
3157 symbol f = get_font_name(fontno, this);
3158 return f.contents();
3161 const char *environment::get_style_name_string()
3163 symbol f = get_style_name(fontno);
3164 return f.contents();
3167 const char *environment::get_name_string()
3169 return name.contents();
3172 // Convert a quantity in scaled points to ascii decimal fraction.
3174 const char *sptoa(int sp)
3177 assert(sizescale > 0);
3180 if (sp % sizescale == 0)
3181 return i_to_a(sp/sizescale);
3182 // See if 1/sizescale is exactly representable as a decimal fraction,
3183 // ie its only prime factors are 2 and 5.
3186 while ((n & 1) == 0) {
3191 while ((n % 5) == 0) {
3196 int decimal_point = power5 > power2 ? power5 : power2;
3197 if (decimal_point <= 10) {
3200 for (t = decimal_point - power2; --t >= 0;)
3202 for (t = decimal_point - power5; --t >= 0;)
3204 if (factor == 1 || sp <= INT_MAX/factor)
3205 return if_to_a(sp*factor, decimal_point);
3208 double s = double(sp)/double(sizescale);
3209 double factor = 10.0;
3211 int decimal_point = 0;
3213 double v = ceil(s*factor);
3218 } while (++decimal_point < 10);
3219 return if_to_a(int(val), decimal_point);
3222 const char *environment::get_point_size_string()
3224 return sptoa(curenv->get_point_size());
3227 const char *environment::get_requested_point_size_string()
3229 return sptoa(curenv->get_requested_point_size());
3232 void environment::print_env()
3234 // at the time of calling .pev, those values are always zero or
3237 // char_height, char_slant,
3239 // current_tab, tab_width, tab_distance
3240 // current_field, field_distance, pre_field_width, field_spaces,
3241 // tab_field_spaces, tab_precedes_field
3244 errprint(" previous line length: %1u\n", prev_line_length.to_units());
3245 errprint(" line length: %1u\n", line_length.to_units());
3246 errprint(" previous title length: %1u\n", prev_title_length.to_units());
3247 errprint(" title length: %1u\n", title_length.to_units());
3248 errprint(" previous size: %1p (%2s)\n",
3249 prev_size.to_points(), prev_size.to_scaled_points());
3250 errprint(" size: %1p (%2s)\n",
3251 size.to_points(), size.to_scaled_points());
3252 errprint(" previous requested size: %1s\n", prev_requested_size);
3253 errprint(" requested size: %1s\n", requested_size);
3254 errprint(" previous font number: %1\n", prev_fontno);
3255 errprint(" font number: %1\n", fontno);
3256 errprint(" previous family: `%1'\n", prev_family->nm.contents());
3257 errprint(" family: `%1'\n", family->nm.contents());
3258 errprint(" space size: %1/36 em\n", space_size);
3259 errprint(" sentence space size: %1/36 em\n", sentence_space_size);
3260 errprint(" previous line interrupted: %1\n",
3261 prev_line_interrupted ? "yes" : "no");
3262 errprint(" fill mode: %1\n", fill ? "on" : "off");
3263 errprint(" adjust mode: %1\n",
3264 adjust_mode == ADJUST_LEFT
3266 : adjust_mode == ADJUST_BOTH
3268 : adjust_mode == ADJUST_CENTER
3272 errprint(" lines to center: %1\n", center_lines);
3273 if (right_justify_lines)
3274 errprint(" lines to right justify: %1\n", right_justify_lines);
3275 errprint(" previous vertical spacing: %1u\n",
3276 prev_vertical_spacing.to_units());
3277 errprint(" vertical spacing: %1u\n", vertical_spacing.to_units());
3278 errprint(" previous post-vertical spacing: %1u\n",
3279 prev_post_vertical_spacing.to_units());
3280 errprint(" post-vertical spacing: %1u\n",
3281 post_vertical_spacing.to_units());
3282 errprint(" previous line spacing: %1\n", prev_line_spacing);
3283 errprint(" line spacing: %1\n", line_spacing);
3284 errprint(" previous indentation: %1u\n", prev_indent.to_units());
3285 errprint(" indentation: %1u\n", indent.to_units());
3286 errprint(" temporary indentation: %1u\n", temporary_indent.to_units());
3287 errprint(" have temporary indentation: %1\n",
3288 have_temporary_indent ? "yes" : "no");
3289 errprint(" currently used indentation: %1u\n", saved_indent.to_units());
3290 errprint(" target text length: %1u\n", target_text_length.to_units());
3291 if (underline_lines) {
3292 errprint(" lines to underline: %1\n", underline_lines);
3293 errprint(" font number before underlining: %1\n", pre_underline_fontno);
3294 errprint(" underline spaces: %1\n", underline_spaces ? "yes" : "no");
3296 if (input_trap.contents()) {
3297 errprint(" input trap macro: `%1'\n", input_trap.contents());
3298 errprint(" input trap line counter: %1\n", input_trap_count);
3299 errprint(" continued input trap: %1\n",
3300 continued_input_trap ? "yes" : "no");
3302 errprint(" previous text length: %1u\n", prev_text_length.to_units());
3303 errprint(" total width: %1u\n", width_total.to_units());
3304 errprint(" total number of spaces: %1\n", space_total);
3305 errprint(" input line start: %1u\n", input_line_start.to_units());
3306 errprint(" line tabs: %1\n", line_tabs ? "yes" : "no");
3307 errprint(" discarding: %1\n", discarding ? "yes" : "no");
3308 errprint(" spread flag set: %1\n", spread_flag ? "yes" : "no"); // \p
3309 if (margin_character_node) {
3310 errprint(" margin character flags: %1\n",
3311 margin_character_flags == MARGIN_CHARACTER_ON
3313 : margin_character_flags == MARGIN_CHARACTER_NEXT
3315 : margin_character_flags == (MARGIN_CHARACTER_ON
3316 | MARGIN_CHARACTER_NEXT)
3319 errprint(" margin character distance: %1u\n",
3320 margin_character_distance.to_units());
3322 if (numbering_nodes) {
3323 errprint(" line number digit width: %1u\n",
3324 line_number_digit_width.to_units());
3325 errprint(" separation between number and text: %1 digit spaces\n",
3326 number_text_separation);
3327 errprint(" line number indentation: %1 digit spaces\n",
3328 line_number_indent);
3329 errprint(" print line numbers every %1line%1\n",
3330 line_number_multiple > 1 ? i_to_a(line_number_multiple) : "",
3331 line_number_multiple > 1 ? "s" : "");
3332 errprint(" lines not to enumerate: %1\n", no_number_count);
3334 string hf = hyphenation_flags ? "on" : "off";
3335 if (hyphenation_flags & HYPHEN_LAST_LINE)
3336 hf += ", not last line";
3337 if (hyphenation_flags & HYPHEN_LAST_CHARS)
3338 hf += ", not last two chars";
3339 if (hyphenation_flags & HYPHEN_FIRST_CHARS)
3340 hf += ", not first two chars";
3342 errprint(" hyphenation_flags: %1\n", hf.contents());
3343 errprint(" number of consecutive hyphenated lines: %1\n",
3345 errprint(" maximum number of consecutive hyphenated lines: %1\n",
3347 errprint(" hyphenation space: %1u\n", hyphenation_space.to_units());
3348 errprint(" hyphenation margin: %1u\n", hyphenation_margin.to_units());
3349 #ifdef WIDOW_CONTROL
3350 errprint(" widow control: %1\n", widow_control ? "yes" : "no");
3351 #endif /* WIDOW_CONTROL */
3356 errprint("Current Environment:\n");
3357 curenv->print_env();
3358 for (int i = 0; i < NENVIRONMENTS; i++) {
3360 errprint("Environment %1:\n", i);
3361 if (env_table[i] != curenv)
3362 env_table[i]->print_env();
3364 errprint(" current\n");
3367 dictionary_iterator iter(env_dictionary);
3370 while (iter.get(&s, (void **)&e)) {
3371 assert(!s.is_null());
3372 errprint("Environment %1:\n", s.contents());
3376 errprint(" current\n");
3382 #define init_int_env_reg(name, func) \
3383 number_reg_dictionary.define(name, new int_env_reg(&environment::func))
3385 #define init_vunits_env_reg(name, func) \
3386 number_reg_dictionary.define(name, new vunits_env_reg(&environment::func))
3388 #define init_hunits_env_reg(name, func) \
3389 number_reg_dictionary.define(name, new hunits_env_reg(&environment::func))
3391 #define init_string_env_reg(name, func) \
3392 number_reg_dictionary.define(name, new string_env_reg(&environment::func))
3394 void init_env_requests()
3396 init_request("ad", adjust);
3397 init_request("br", break_request);
3398 init_request("brp", break_spread_request);
3399 init_request("c2", no_break_control_char);
3400 init_request("cc", control_char);
3401 init_request("ce", center);
3402 init_request("cu", continuous_underline);
3403 init_request("ev", environment_switch);
3404 init_request("evc", environment_copy);
3405 init_request("fam", family_change);
3406 init_request("fc", field_characters);
3407 init_request("fi", fill);
3408 init_request("fcolor", fill_color_change);
3409 init_request("ft", font_change);
3410 init_request("gcolor", glyph_color_change);
3411 init_request("hc", hyphen_char);
3412 init_request("hlm", hyphen_line_max_request);
3413 init_request("hy", hyphenate_request);
3414 init_request("hym", hyphenation_margin_request);
3415 init_request("hys", hyphenation_space_request);
3416 init_request("in", indent);
3417 init_request("it", input_trap);
3418 init_request("itc", input_trap_continued);
3419 init_request("lc", leader_character);
3420 init_request("linetabs", line_tabs_request);
3421 init_request("ll", line_length);
3422 init_request("ls", line_spacing);
3423 init_request("lt", title_length);
3424 init_request("mc", margin_character);
3425 init_request("na", no_adjust);
3426 init_request("nf", no_fill);
3427 init_request("nh", no_hyphenate);
3428 init_request("nm", number_lines);
3429 init_request("nn", no_number);
3430 init_request("pev", print_env);
3431 init_request("ps", point_size);
3432 init_request("pvs", post_vertical_spacing);
3433 init_request("rj", right_justify);
3434 init_request("sizes", override_sizes);
3435 init_request("ss", space_size);
3436 init_request("ta", set_tabs);
3437 init_request("ti", temporary_indent);
3438 init_request("tc", tab_character);
3439 init_request("tl", title);
3440 init_request("ul", underline);
3441 init_request("vs", vertical_spacing);
3442 #ifdef WIDOW_CONTROL
3443 init_request("wdc", widow_control_request);
3444 #endif /* WIDOW_CONTROL */
3445 init_int_env_reg(".b", get_bold);
3446 init_vunits_env_reg(".cdp", get_prev_char_depth);
3447 init_int_env_reg(".ce", get_center_lines);
3448 init_vunits_env_reg(".cht", get_prev_char_height);
3449 init_hunits_env_reg(".csk", get_prev_char_skew);
3450 init_string_env_reg(".ev", get_name_string);
3451 init_int_env_reg(".f", get_font);
3452 init_string_env_reg(".fam", get_font_family_string);
3453 init_string_env_reg(".fn", get_font_name_string);
3454 init_int_env_reg(".height", get_char_height);
3455 init_int_env_reg(".hlc", get_hyphen_line_count);
3456 init_int_env_reg(".hlm", get_hyphen_line_max);
3457 init_int_env_reg(".hy", get_hyphenation_flags);
3458 init_hunits_env_reg(".hym", get_hyphenation_margin);
3459 init_hunits_env_reg(".hys", get_hyphenation_space);
3460 init_hunits_env_reg(".i", get_indent);
3461 init_hunits_env_reg(".in", get_saved_indent);
3462 init_int_env_reg(".int", get_prev_line_interrupted);
3463 init_int_env_reg(".linetabs", get_line_tabs);
3464 init_hunits_env_reg(".lt", get_title_length);
3465 init_int_env_reg(".j", get_adjust_mode);
3466 init_hunits_env_reg(".k", get_text_length);
3467 init_int_env_reg(".L", get_line_spacing);
3468 init_hunits_env_reg(".l", get_line_length);
3469 init_hunits_env_reg(".ll", get_saved_line_length);
3470 init_string_env_reg(".M", get_fill_color_string);
3471 init_string_env_reg(".m", get_glyph_color_string);
3472 init_hunits_env_reg(".n", get_prev_text_length);
3473 init_int_env_reg(".ps", get_point_size);
3474 init_int_env_reg(".psr", get_requested_point_size);
3475 init_vunits_env_reg(".pvs", get_post_vertical_spacing);
3476 init_int_env_reg(".rj", get_right_justify_lines);
3477 init_string_env_reg(".s", get_point_size_string);
3478 init_int_env_reg(".slant", get_char_slant);
3479 init_int_env_reg(".ss", get_space_size);
3480 init_int_env_reg(".sss", get_sentence_space_size);
3481 init_string_env_reg(".sr", get_requested_point_size_string);
3482 init_string_env_reg(".sty", get_style_name_string);
3483 init_string_env_reg(".tabs", get_tabs);
3484 init_int_env_reg(".u", get_fill);
3485 init_vunits_env_reg(".v", get_vertical_spacing);
3486 init_hunits_env_reg(".w", get_prev_char_width);
3487 init_int_env_reg(".zoom", get_zoom);
3488 number_reg_dictionary.define("ct", new variable_reg(&ct_reg_contents));
3489 number_reg_dictionary.define("hp", new horizontal_place_reg);
3490 number_reg_dictionary.define("ln", new variable_reg(&next_line_number));
3491 number_reg_dictionary.define("rsb", new variable_reg(&rsb_reg_contents));
3492 number_reg_dictionary.define("rst", new variable_reg(&rst_reg_contents));
3493 number_reg_dictionary.define("sb", new variable_reg(&sb_reg_contents));
3494 number_reg_dictionary.define("skw", new variable_reg(&skw_reg_contents));
3495 number_reg_dictionary.define("ssc", new variable_reg(&ssc_reg_contents));
3496 number_reg_dictionary.define("st", new variable_reg(&st_reg_contents));
3499 // Hyphenation - TeX's hyphenation algorithm with a less fancy implementation.
3505 virtual void do_match(int len, void *val) = 0;
3506 virtual void do_delete(void *) = 0;
3507 void delete_trie_node(trie_node *);
3510 virtual ~trie(); // virtual to shut up g++
3511 void insert(const char *, int, void *);
3512 // find calls do_match for each match it finds
3513 void find(const char *pat, int patlen);
3517 class hyphen_trie : private trie {
3519 void do_match(int i, void *v);
3520 void do_delete(void *v);
3521 void insert_pattern(const char *pat, int patlen, int *num);
3522 void insert_hyphenation(dictionary *ex, const char *pat, int patlen);
3523 int hpf_getc(FILE *f);
3527 void hyphenate(const char *word, int len, int *hyphens);
3528 void read_patterns_file(const char *name, int append, dictionary *ex);
3531 struct hyphenation_language {
3533 dictionary exceptions;
3534 hyphen_trie patterns;
3535 hyphenation_language(symbol nm) : name(nm), exceptions(501) {}
3536 ~hyphenation_language() { }
3539 dictionary language_dictionary(5);
3540 hyphenation_language *current_language = 0;
3542 static void set_hyphenation_language()
3544 symbol nm = get_name(1);
3545 if (!nm.is_null()) {
3546 current_language = (hyphenation_language *)language_dictionary.lookup(nm);
3547 if (!current_language) {
3548 current_language = new hyphenation_language(nm);
3549 (void)language_dictionary.lookup(nm, (void *)current_language);
3555 const int WORD_MAX = 256; // we use unsigned char for offsets in
3556 // hyphenation exceptions
3558 static void hyphen_word()
3560 if (!current_language) {
3561 error("no current hyphenation language");
3565 char buf[WORD_MAX + 1];
3566 unsigned char pos[WORD_MAX + 2];
3569 if (tok.newline() || tok.eof())
3573 while (i < WORD_MAX && !tok.space() && !tok.newline() && !tok.eof()) {
3574 charinfo *ci = tok.get_char(1);
3580 if (ci->get_ascii_code() == '-') {
3581 if (i > 0 && (npos == 0 || pos[npos - 1] != i))
3585 unsigned char c = ci->get_hyphenation_code();
3594 unsigned char *tem = new unsigned char[npos + 1];
3595 memcpy(tem, pos, npos + 1);
3596 tem = (unsigned char *)current_language->exceptions.lookup(symbol(buf),
3610 trie_node(char, trie_node *);
3613 trie_node::trie_node(char ch, trie_node *p)
3614 : c(ch), down(0), right(p), val(0)
3625 delete_trie_node(tp);
3630 void trie::delete_trie_node(trie_node *p)
3633 delete_trie_node(p->down);
3634 delete_trie_node(p->right);
3641 void trie::insert(const char *pat, int patlen, void *val)
3643 trie_node **p = &tp;
3644 assert(patlen > 0 && pat != 0);
3646 while (*p != 0 && (*p)->c < pat[0])
3648 if (*p == 0 || (*p)->c != pat[0])
3649 *p = new trie_node(pat[0], *p);
3650 if (--patlen == 0) {
3659 void trie::find(const char *pat, int patlen)
3662 for (int i = 0; p != 0 && i < patlen; i++) {
3663 while (p != 0 && p->c < pat[i])
3665 if (p != 0 && p->c == pat[i]) {
3667 do_match(i+1, p->val);
3679 operation(int, int, operation *);
3682 operation::operation(int i, int j, operation *op)
3683 : next(op), distance(j), num(i)
3687 void hyphen_trie::insert_pattern(const char *pat, int patlen, int *num)
3690 for (int i = 0; i < patlen+1; i++)
3692 op = new operation(num[i], patlen - i, op);
3693 insert(pat, patlen, op);
3696 void hyphen_trie::insert_hyphenation(dictionary *ex, const char *pat,
3699 char buf[WORD_MAX + 1];
3700 unsigned char pos[WORD_MAX + 2];
3703 while (j < patlen) {
3704 unsigned char c = pat[j++];
3706 if (i > 0 && (npos == 0 || pos[npos - 1] != i))
3710 buf[i++] = hpf_code_table[c];
3715 unsigned char *tem = new unsigned char[npos + 1];
3716 memcpy(tem, pos, npos + 1);
3717 tem = (unsigned char *)ex->lookup(symbol(buf), tem);
3723 void hyphen_trie::hyphenate(const char *word, int len, int *hyphens)
3726 for (j = 0; j < len + 1; j++)
3728 for (j = 0; j < len - 1; j++) {
3730 find(word + j, len - j);
3734 inline int max(int m, int n)
3736 return m > n ? m : n;
3739 void hyphen_trie::do_match(int i, void *v)
3741 operation *op = (operation *)v;
3743 h[i - op->distance] = max(h[i - op->distance], op->num);
3748 void hyphen_trie::do_delete(void *v)
3750 operation *op = (operation *)v;
3752 operation *tem = op;
3758 /* We use very simple rules to parse TeX's hyphenation patterns.
3760 . `%' starts a comment even if preceded by `\'.
3762 . No support for digraphs and like `\$'.
3764 . `^^xx' (`x' is 0-9 or a-f), and `^^x' (character code of `x' in the
3765 range 0-127) are recognized; other use of `^' causes an error.
3767 . No macro expansion.
3769 . We check for the expression `\patterns{...}' (possibly with
3770 whitespace before and after the braces). Everything between the
3771 braces is taken as hyphenation patterns. Consequently, `{' and `}'
3772 are not allowed in patterns.
3774 . Similarly, `\hyphenation{...}' gives a list of hyphenation
3777 . `\endinput' is recognized also.
3779 . For backwards compatibility, if `\patterns' is missing, the
3780 whole file is treated as a list of hyphenation patterns (only
3781 recognizing `%' as the start of a comment.
3785 int hyphen_trie::hpf_getc(FILE *f)
3797 if (((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))
3798 && ((c1 >= '0' && c1 <= '9') || (c1 >= 'a' && c1 <= 'f'))) {
3799 if (c >= '0' && c <= '9')
3803 if (c1 >= '0' && c1 <= '9')
3811 if (c >= 0 && c <= 63)
3813 else if (c >= 64 && c <= 127)
3820 error("invalid ^, ^^x, or ^^xx character in hyphenation patterns file");
3824 void hyphen_trie::read_patterns_file(const char *name, int append,
3830 for (int i = 0; i < WORD_MAX; i++)
3832 int num[WORD_MAX+1];
3835 FILE *fp = mac_path->open_file(name, &path);
3837 error("can't find hyphenation patterns file `%1'", name);
3840 int c = hpf_getc(fp);
3841 int have_patterns = 0; // we've seen \patterns
3842 int final_pattern = 0; // 1 if we have a trailing closing brace
3843 int have_hyphenation = 0; // we've seen \hyphenation
3844 int final_hyphenation = 0; // 1 if we have a trailing closing brace
3845 int have_keyword = 0; // we've seen either \patterns or \hyphenation
3846 int traditional = 0; // don't handle \patterns
3849 if (c == '%') { // skip comments
3852 } while (c != EOF && c != '\n');
3854 if (c == EOF || !csspace(c))
3859 if (have_keyword || traditional) // we are done
3861 else { // rescan file in `traditional' mode
3870 if (!(c == '{' || c == '}')) { // skip braces at line start
3871 do { // scan patterns
3879 } while (i < WORD_MAX && c != EOF && !csspace(c)
3880 && c != '%' && c != '{' && c != '}');
3883 if (i >= 9 && !strncmp(buf + i - 9, "\\patterns", 9)) {
3887 if (have_patterns || have_hyphenation)
3888 error("\\patterns not allowed inside of %1 group",
3889 have_patterns ? "\\patterns" : "\\hyphenation");
3898 else if (i >= 12 && !strncmp(buf + i - 12, "\\hyphenation", 12)) {
3902 if (have_patterns || have_hyphenation)
3903 error("\\hyphenation not allowed inside of %1 group",
3904 have_patterns ? "\\patterns" : "\\hyphenation");
3906 have_hyphenation = 1;
3913 else if (strstr(buf, "\\endinput")) {
3914 if (have_patterns || have_hyphenation)
3915 error("found \\endinput inside of %1 group",
3916 have_patterns ? "\\patterns" : "\\hyphenation");
3919 else if (c == '}') {
3920 if (have_patterns) {
3925 else if (have_hyphenation) {
3926 have_hyphenation = 0;
3928 final_hyphenation = 1;
3932 else if (c == '{') {
3933 if (have_patterns || have_hyphenation)
3934 error("`{' not allowed within %1 group",
3935 have_patterns ? "\\patterns" : "\\hyphenation");
3936 c = hpf_getc(fp); // skipped if not starting \patterns
3941 if (c == '{' || c == '}')
3945 if (have_patterns || final_pattern || traditional) {
3946 for (int j = 0; j < i; j++)
3947 buf[j] = hpf_code_table[(unsigned char)buf[j]];
3948 insert_pattern(buf, i, num);
3951 else if (have_hyphenation || final_hyphenation) {
3952 insert_hyphenation(ex, buf, i);
3953 final_hyphenation = 0;
3962 void hyphenate(hyphen_list *h, unsigned flags)
3964 if (!current_language)
3967 while (h && h->hyphenation_code == 0)
3970 char hbuf[WORD_MAX + 2];
3971 char *buf = hbuf + 1;
3973 for (tem = h; tem && len < WORD_MAX; tem = tem->next) {
3974 if (tem->hyphenation_code != 0)
3975 buf[len++] = tem->hyphenation_code;
3979 hyphen_list *nexth = tem;
3983 = (unsigned char *)current_language->exceptions.lookup(buf);
3987 for (tem = h; tem != 0; tem = tem->next, i++)
3994 hbuf[0] = hbuf[len + 1] = '.';
3995 int num[WORD_MAX + 3];
3996 current_language->patterns.hyphenate(hbuf, len + 2, num);
3999 if (flags & HYPHEN_FIRST_CHARS)
4001 if (flags & HYPHEN_LAST_CHARS)
4003 for (i = 2, tem = h; i < len && tem; tem = tem->next, i++)
4012 static void do_hyphenation_patterns_file(int append)
4014 symbol name = get_long_name(1);
4015 if (!name.is_null()) {
4016 if (!current_language)
4017 error("no current hyphenation language");
4019 current_language->patterns.read_patterns_file(
4020 name.contents(), append,
4021 ¤t_language->exceptions);
4026 static void hyphenation_patterns_file()
4028 do_hyphenation_patterns_file(0);
4031 static void hyphenation_patterns_file_append()
4033 do_hyphenation_patterns_file(1);
4036 class hyphenation_language_reg : public reg {
4038 const char *get_string();
4041 const char *hyphenation_language_reg::get_string()
4043 return current_language ? current_language->name.contents() : "";
4046 void init_hyphen_requests()
4048 init_request("hw", hyphen_word);
4049 init_request("hla", set_hyphenation_language);
4050 init_request("hpf", hyphenation_patterns_file);
4051 init_request("hpfa", hyphenation_patterns_file_append);
4052 number_reg_dictionary.define(".hla", new hyphenation_language_reg);