2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
23 #include "dictionary.h"
25 #include "stringclass.h"
34 #include "macropath.h"
38 symbol default_family("T");
40 enum { ADJUST_LEFT = 0, ADJUST_BOTH = 1, ADJUST_CENTER = 3, ADJUST_RIGHT = 5 };
42 enum { HYPHEN_LAST_LINE = 2, HYPHEN_LAST_CHARS = 4, HYPHEN_FIRST_CHARS = 8 };
47 env_list(environment *e, env_list *p) : env(e), next(p) {}
51 const int NENVIRONMENTS = 10;
52 environment *env_table[NENVIRONMENTS];
53 dictionary env_dictionary(10);
55 static int next_line_number = 0;
56 extern int suppress_push;
57 extern statem *get_diversion_state();
59 charinfo *field_delimiter_char;
60 charinfo *padding_indicator_char;
62 int translate_space_to_dummy = 0;
64 class pending_output_line {
72 int last_line; // Is it the last line of the paragraph?
73 #endif /* WIDOW_CONTROL */
75 pending_output_line *next;
77 pending_output_line(node *, int, vunits, vunits, hunits, int,
78 pending_output_line * = 0);
79 ~pending_output_line();
83 friend void environment::mark_last_line();
84 friend void environment::output(node *, int, vunits, vunits, hunits, int);
85 #endif /* WIDOW_CONTROL */
88 pending_output_line::pending_output_line(node *n, int nf, vunits v, vunits pv,
90 pending_output_line *p)
91 : nd(n), no_fill(nf), was_centered(ce), vs(v), post_vs(pv), width(w),
94 #endif /* WIDOW_CONTROL */
99 pending_output_line::~pending_output_line()
101 delete_node_list(nd);
104 int pending_output_line::output()
106 if (trap_sprung_flag)
109 if (next && next->last_line && !no_fill) {
110 curdiv->need(vs + post_vs + vunits(vresolution));
111 if (trap_sprung_flag) {
112 next->last_line = 0; // Try to avoid infinite loops.
117 curenv->construct_format_state(nd, was_centered, !no_fill);
118 curdiv->output(nd, no_fill, vs, post_vs, width);
123 void environment::output(node *nd, int no_fill_flag,
124 vunits vs, vunits post_vs,
125 hunits width, int was_centered)
128 while (pending_lines) {
129 if (widow_control && !pending_lines->no_fill && !pending_lines->next)
131 if (!pending_lines->output())
133 pending_output_line *tem = pending_lines;
134 pending_lines = pending_lines->next;
137 #else /* WIDOW_CONTROL */
138 output_pending_lines();
139 #endif /* WIDOW_CONTROL */
140 if (!trap_sprung_flag && !pending_lines
142 && (!widow_control || no_fill_flag)
143 #endif /* WIDOW_CONTROL */
145 curenv->construct_format_state(nd, was_centered, !no_fill_flag);
146 curdiv->output(nd, no_fill_flag, vs, post_vs, width);
148 pending_output_line **p;
149 for (p = &pending_lines; *p; p = &(*p)->next)
151 *p = new pending_output_line(nd, no_fill_flag, vs, post_vs, width,
156 // a line from .tl goes at the head of the queue
158 void environment::output_title(node *nd, int no_fill_flag,
159 vunits vs, vunits post_vs,
162 if (!trap_sprung_flag)
163 curdiv->output(nd, no_fill_flag, vs, post_vs, width);
165 pending_lines = new pending_output_line(nd, no_fill_flag, vs, post_vs,
166 width, 0, pending_lines);
169 void environment::output_pending_lines()
171 while (pending_lines && pending_lines->output()) {
172 pending_output_line *tem = pending_lines;
173 pending_lines = pending_lines->next;
180 void environment::mark_last_line()
182 if (!widow_control || !pending_lines)
184 pending_output_line *p;
185 for (p = pending_lines; p->next; p = p->next)
191 void widow_control_request()
194 if (has_arg() && get_integer(&n))
195 curenv->widow_control = n != 0;
197 curenv->widow_control = 1;
201 #endif /* WIDOW_CONTROL */
203 /* font_size functions */
205 size_range *font_size::size_table = 0;
206 int font_size::nranges = 0;
210 int compare_ranges(const void *p1, const void *p2)
212 return ((size_range *)p1)->min - ((size_range *)p2)->min;
217 void font_size::init_size_table(int *sizes)
220 while (sizes[nranges*2] != 0)
223 size_table = new size_range[nranges];
224 for (int i = 0; i < nranges; i++) {
225 size_table[i].min = sizes[i*2];
226 size_table[i].max = sizes[i*2 + 1];
228 qsort(size_table, nranges, sizeof(size_range), compare_ranges);
231 font_size::font_size(int sp)
233 for (int i = 0; i < nranges; i++) {
234 if (sp < size_table[i].min) {
235 if (i > 0 && size_table[i].min - sp >= sp - size_table[i - 1].max)
236 p = size_table[i - 1].max;
238 p = size_table[i].min;
241 if (sp <= size_table[i].max) {
246 p = size_table[nranges - 1].max;
249 int font_size::to_units()
251 return scale(p, units_per_inch, sizescale*72);
254 // we can't do this in a static constructor because various dictionaries
255 // have to get initialized first
257 void init_environments()
259 curenv = env_table[0] = new environment("0");
264 curenv->tab_char = get_optional_char();
268 void leader_character()
270 curenv->leader_char = get_optional_char();
274 void environment::add_char(charinfo *ci)
280 // don't allow fields in dummy environments
281 else if (ci == field_delimiter_char && !dummy) {
287 else if (current_field && ci == padding_indicator_char)
289 else if (current_tab) {
290 if (tab_contents == 0)
291 tab_contents = new line_start_node;
292 if (ci != hyphen_indicator_char)
293 tab_contents = tab_contents->add_char(ci, this, &tab_width, &s, &gc_np);
295 tab_contents = tab_contents->add_discretionary_hyphen();
301 fprintf(stderr, "current line is\n");
302 line->debug_node_list();
304 if (ci != hyphen_indicator_char)
305 line = line->add_char(ci, this, &width_total, &space_total, &gc_np);
307 line = line->add_discretionary_hyphen();
310 fprintf(stderr, "now after we have added character the line is\n");
311 line->debug_node_list();
313 if ((!suppress_push) && gc_np) {
314 if (gc_np && (gc_np->state == 0)) {
315 gc_np->state = construct_state(0);
316 gc_np->push_state = get_diversion_state();
318 else if (line && (line->state == 0)) {
319 line->state = construct_state(0);
320 line->push_state = get_diversion_state();
324 fprintf(stderr, "now we have possibly added the state the line is\n");
325 line->debug_node_list();
329 node *environment::make_char_node(charinfo *ci)
331 return make_node(ci, this);
334 void environment::add_node(node *n)
338 if (!suppress_push) {
339 if (n->is_special && n->state == NULL)
340 n->state = construct_state(0);
341 n->push_state = get_diversion_state();
344 if (current_tab || current_field)
349 else if (current_tab) {
350 n->next = tab_contents;
352 tab_width += n->width();
356 if (discarding && n->discardable()) {
357 // XXX possibly: input_line_start -= n->width();
363 width_total += n->width();
364 space_total += n->nspaces();
367 construct_new_line_state(line);
371 void environment::add_hyphen_indicator()
373 if (current_tab || interrupted || current_field
374 || hyphen_indicator_char != 0)
378 line = line->add_discretionary_hyphen();
381 int environment::get_hyphenation_flags()
383 return hyphenation_flags;
386 int environment::get_hyphen_line_max()
388 return hyphen_line_max;
391 int environment::get_hyphen_line_count()
393 return hyphen_line_count;
396 int environment::get_center_lines()
401 int environment::get_right_justify_lines()
403 return right_justify_lines;
406 void environment::add_italic_correction()
410 tab_contents = tab_contents->add_italic_correction(&tab_width);
413 line = line->add_italic_correction(&width_total);
416 void environment::space_newline()
418 assert(!current_tab && !current_field);
422 hunits sw = env_space_width(this);
423 hunits ssw = env_sentence_space_width(this);
424 if (!translate_space_to_dummy) {
426 if (node_list_ends_sentence(line) == 1)
429 width_list *w = new width_list(sw, ssw);
430 if (node_list_ends_sentence(line) == 1)
431 w->next = new width_list(sw, ssw);
432 if (line != 0 && line->merge_space(x, sw, ssw)) {
436 add_node(new word_space_node(x, get_fill_color(), w));
437 possibly_break_line(0, spread_flag);
441 void environment::space()
443 space(env_space_width(this), env_sentence_space_width(this));
446 void environment::space(hunits space_width, hunits sentence_space_width)
450 if (current_field && padding_indicator_char == 0) {
454 hunits x = translate_space_to_dummy ? H0 : space_width;
455 node *p = current_tab ? tab_contents : line;
456 hunits *tp = current_tab ? &tab_width : &width_total;
457 if (p && p->nspaces() == 1 && p->width() == x
458 && node_list_ends_sentence(p->next) == 1) {
459 hunits xx = translate_space_to_dummy ? H0 : sentence_space_width;
460 if (p->merge_space(xx, space_width, sentence_space_width)) {
465 if (p && p->merge_space(x, space_width, sentence_space_width)) {
469 add_node(new word_space_node(x,
471 new width_list(space_width,
472 sentence_space_width)));
473 possibly_break_line(0, spread_flag);
477 node *do_underline_special(int);
479 void environment::set_font(symbol nm)
483 if (nm == symbol("P") || nm.is_empty()) {
484 if (family->make_definite(prev_fontno) < 0)
487 fontno = prev_fontno;
491 prev_fontno = fontno;
492 int n = symbol_fontno(nm);
494 n = next_available_font_position();
495 if (!mount_font(n, nm))
498 if (family->make_definite(n) < 0)
502 if (underline_spaces && fontno != prev_fontno) {
503 if (fontno == get_underline_fontno())
504 add_node(do_underline_special(1));
505 if (prev_fontno == get_underline_fontno())
506 add_node(do_underline_special(0));
510 void environment::set_font(int n)
514 if (is_good_fontno(n)) {
515 prev_fontno = fontno;
519 warning(WARN_FONT, "bad font number");
522 void environment::set_family(symbol fam)
526 if (fam.is_null() || fam.is_empty()) {
527 if (prev_family->make_definite(fontno) < 0)
529 font_family *tem = family;
530 family = prev_family;
534 font_family *f = lookup_family(fam);
535 if (f->make_definite(fontno) < 0)
537 prev_family = family;
542 void environment::set_size(int n)
547 font_size temp = prev_size;
550 int temp2 = prev_requested_size;
551 prev_requested_size = requested_size;
552 requested_size = temp2;
557 prev_requested_size = requested_size;
562 void environment::set_char_height(int n)
566 if (n == requested_size || n <= 0)
572 void environment::set_char_slant(int n)
579 color *environment::get_prev_glyph_color()
581 return prev_glyph_color;
584 color *environment::get_glyph_color()
589 color *environment::get_prev_fill_color()
591 return prev_fill_color;
594 color *environment::get_fill_color()
599 void environment::set_glyph_color(color *c)
603 curenv->prev_glyph_color = curenv->glyph_color;
604 curenv->glyph_color = c;
607 void environment::set_fill_color(color *c)
611 curenv->prev_fill_color = curenv->fill_color;
612 curenv->fill_color = c;
615 environment::environment(symbol nm)
617 prev_line_length((units_per_inch*13)/2),
618 line_length((units_per_inch*13)/2),
619 prev_title_length((units_per_inch*13)/2),
620 title_length((units_per_inch*13)/2),
621 prev_size(sizescale*10),
623 requested_size(sizescale*10),
624 prev_requested_size(sizescale*10),
628 sentence_space_size(12),
629 adjust_mode(ADJUST_BOTH),
632 prev_line_interrupted(0),
634 right_justify_lines(0),
635 prev_vertical_spacing(points_to_units(12)),
636 vertical_spacing(points_to_units(12)),
637 prev_post_vertical_spacing(0),
638 post_vertical_spacing(0),
639 prev_line_spacing(1),
644 have_temporary_indent(0),
648 continued_input_trap(0),
655 current_tab(TAB_NONE),
658 leader_char(charset_table['.']),
662 margin_character_flags(0),
663 margin_character_node(0),
664 margin_character_distance(points_to_units(10)),
666 number_text_separation(1),
667 line_number_indent(0),
668 line_number_multiple(1),
670 hyphenation_flags(1),
671 hyphen_line_count(0),
673 hyphenation_space(H0),
674 hyphenation_margin(H0),
679 #endif /* WIDOW_CONTROL */
680 glyph_color(&default_color),
681 prev_glyph_color(&default_color),
682 fill_color(&default_color),
683 prev_fill_color(&default_color),
686 suppress_next_eol(0),
688 tabs(units_per_inch/2, TAB_LEFT),
691 no_break_control_char('\''),
692 hyphen_indicator_char(0)
694 prev_family = family = lookup_family(default_family);
695 prev_fontno = fontno = 1;
696 if (!is_good_fontno(1))
697 fatal("font number 1 not a valid font");
698 if (family->make_definite(1) < 0)
699 fatal("invalid default family `%1'", default_family.contents());
700 prev_fontno = fontno;
703 environment::environment(const environment *e)
705 prev_line_length(e->prev_line_length),
706 line_length(e->line_length),
707 prev_title_length(e->prev_title_length),
708 title_length(e->title_length),
709 prev_size(e->prev_size),
711 requested_size(e->requested_size),
712 prev_requested_size(e->prev_requested_size),
713 char_height(e->char_height),
714 char_slant(e->char_slant),
715 prev_fontno(e->prev_fontno),
717 prev_family(e->prev_family),
719 space_size(e->space_size),
720 sentence_space_size(e->sentence_space_size),
721 adjust_mode(e->adjust_mode),
724 prev_line_interrupted(0),
726 right_justify_lines(0),
727 prev_vertical_spacing(e->prev_vertical_spacing),
728 vertical_spacing(e->vertical_spacing),
729 prev_post_vertical_spacing(e->prev_post_vertical_spacing),
730 post_vertical_spacing(e->post_vertical_spacing),
731 prev_line_spacing(e->prev_line_spacing),
732 line_spacing(e->line_spacing),
733 prev_indent(e->prev_indent),
736 have_temporary_indent(0),
740 continued_input_trap(0),
742 prev_text_length(e->prev_text_length),
746 line_tabs(e->line_tabs),
747 current_tab(TAB_NONE),
749 tab_char(e->tab_char),
750 leader_char(e->leader_char),
754 margin_character_flags(e->margin_character_flags),
755 margin_character_node(e->margin_character_node),
756 margin_character_distance(e->margin_character_distance),
758 number_text_separation(e->number_text_separation),
759 line_number_indent(e->line_number_indent),
760 line_number_multiple(e->line_number_multiple),
761 no_number_count(e->no_number_count),
762 hyphenation_flags(e->hyphenation_flags),
763 hyphen_line_count(0),
764 hyphen_line_max(e->hyphen_line_max),
765 hyphenation_space(e->hyphenation_space),
766 hyphenation_margin(e->hyphenation_margin),
770 widow_control(e->widow_control),
771 #endif /* WIDOW_CONTROL */
772 glyph_color(e->glyph_color),
773 prev_glyph_color(e->prev_glyph_color),
774 fill_color(e->fill_color),
775 prev_fill_color(e->prev_fill_color),
776 seen_space(e->seen_space),
777 seen_eol(e->seen_eol),
778 suppress_next_eol(e->suppress_next_eol),
779 seen_break(e->seen_break),
781 name(e->name), // so that eg `.if "\n[.ev]"0"' works
782 control_char(e->control_char),
783 no_break_control_char(e->no_break_control_char),
784 hyphen_indicator_char(e->hyphen_indicator_char)
788 void environment::copy(const environment *e)
790 prev_line_length = e->prev_line_length;
791 line_length = e->line_length;
792 prev_title_length = e->prev_title_length;
793 title_length = e->title_length;
794 prev_size = e->prev_size;
796 prev_requested_size = e->prev_requested_size;
797 requested_size = e->requested_size;
798 char_height = e->char_height;
799 char_slant = e->char_slant;
800 space_size = e->space_size;
801 sentence_space_size = e->sentence_space_size;
802 adjust_mode = e->adjust_mode;
805 prev_line_interrupted = 0;
807 right_justify_lines = 0;
808 prev_vertical_spacing = e->prev_vertical_spacing;
809 vertical_spacing = e->vertical_spacing;
810 prev_post_vertical_spacing = e->prev_post_vertical_spacing,
811 post_vertical_spacing = e->post_vertical_spacing,
812 prev_line_spacing = e->prev_line_spacing;
813 line_spacing = e->line_spacing;
814 prev_indent = e->prev_indent;
816 have_temporary_indent = 0;
817 temporary_indent = 0;
819 underline_spaces = 0;
820 input_trap_count = 0;
821 continued_input_trap = 0;
822 prev_text_length = e->prev_text_length;
825 input_line_start = 0;
826 control_char = e->control_char;
827 no_break_control_char = e->no_break_control_char;
828 hyphen_indicator_char = e->hyphen_indicator_char;
834 line_tabs = e->line_tabs;
835 current_tab = TAB_NONE;
837 margin_character_flags = e->margin_character_flags;
838 margin_character_node = e->margin_character_node;
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();
2214 macro *m = new macro;
2215 m->append_str("devtag:");
2216 for (const char *p = nm; *p; p++)
2217 if (!invalid_input_char((unsigned char)*p))
2221 return new special_node(*m);
2226 void environment::dump_troff_state()
2229 fprintf(stderr, SPACES "register `in' = %d\n", curenv->indent.to_units());
2230 if (curenv->have_temporary_indent)
2231 fprintf(stderr, SPACES "register `ti' = %d\n",
2232 curenv->temporary_indent.to_units());
2233 fprintf(stderr, SPACES "centered lines `ce' = %d\n", curenv->center_lines);
2234 fprintf(stderr, SPACES "register `ll' = %d\n",
2235 curenv->line_length.to_units());
2236 fprintf(stderr, SPACES "fill `fi=1/nf=0' = %d\n", curenv->fill);
2237 fprintf(stderr, SPACES "page offset `po' = %d\n",
2238 topdiv->get_page_offset().to_units());
2239 fprintf(stderr, SPACES "seen_break = %d\n", curenv->seen_break);
2240 fprintf(stderr, SPACES "seen_space = %d\n", curenv->seen_space);
2245 statem *environment::construct_state(int only_eol)
2248 statem *s = new statem();
2250 s->add_tag(MTSM_IN, indent);
2251 s->add_tag(MTSM_LL, line_length);
2252 s->add_tag(MTSM_PO, topdiv->get_page_offset().to_units());
2253 s->add_tag(MTSM_RJ, right_justify_lines);
2254 if (have_temporary_indent)
2255 s->add_tag(MTSM_TI, temporary_indent);
2258 s->add_tag(MTSM_BR);
2259 if (seen_space != 0)
2260 s->add_tag(MTSM_SP, seen_space);
2265 s->add_tag(MTSM_EOL);
2266 s->add_tag(MTSM_CE, center_lines);
2275 void environment::construct_format_state(node *n, int was_centered,
2279 // find first glyph node which has a state.
2280 while (n != 0 && n->state == 0)
2282 if (n == 0 || (n->state == 0))
2284 if (seen_space != 0)
2285 n->state->add_tag(MTSM_SP, seen_space);
2286 if (seen_eol && topdiv == curdiv)
2287 n->state->add_tag(MTSM_EOL);
2291 n->state->add_tag(MTSM_CE, center_lines+1);
2293 n->state->add_tag_if_unknown(MTSM_CE, 0);
2294 n->state->add_tag_if_unknown(MTSM_FI, filling);
2297 if (n->state != 0) {
2298 n->state->sub_tag_ce();
2299 n->state->add_tag_if_unknown(MTSM_FI, filling);
2306 void environment::construct_new_line_state(node *n)
2309 // find first glyph node which has a state.
2310 while (n != 0 && n->state == 0)
2312 if (n == 0 || n->state == 0)
2314 if (seen_space != 0)
2315 n->state->add_tag(MTSM_SP, seen_space);
2316 if (seen_eol && topdiv == curdiv)
2317 n->state->add_tag(MTSM_EOL);
2323 extern int global_diverted_space;
2325 void environment::do_break(int do_spread)
2327 int was_centered = 0;
2328 if (curdiv == topdiv && topdiv->before_first_page) {
2329 topdiv->begin_page();
2335 // this is so that hyphenation works
2336 line = new space_node(H0, get_fill_color(), line);
2338 possibly_break_line(0, do_spread);
2340 while (line != 0 && line->discardable()) {
2341 width_total -= line->width();
2342 space_total -= line->nspaces();
2348 input_line_start = H0;
2351 switch (adjust_mode) {
2353 saved_indent += (target_text_length - width_total)/2;
2357 saved_indent += target_text_length - width_total;
2363 output_line(tem, width_total, was_centered);
2364 hyphen_line_count = 0;
2366 prev_line_interrupted = 0;
2367 #ifdef WIDOW_CONTROL
2369 output_pending_lines();
2370 #endif /* WIDOW_CONTROL */
2371 if (!global_diverted_space) {
2372 curdiv->modified_tag.incl(MTSM_BR);
2377 int environment::is_empty()
2379 return !current_tab && line == 0 && pending_lines == 0;
2382 void do_break_request(int spread)
2384 while (!tok.newline() && !tok.eof())
2387 curenv->do_break(spread);
2391 void break_request()
2393 do_break_request(0);
2396 void break_spread_request()
2398 do_break_request(1);
2403 if (curdiv == topdiv && topdiv->before_first_page) {
2404 handle_initial_title();
2408 hunits part_width[3];
2409 part[0] = part[1] = part[2] = 0;
2410 environment env(curenv);
2411 environment *oldenv = curenv;
2413 read_title_parts(part, part_width);
2415 curenv->size = env.size;
2416 curenv->prev_size = env.prev_size;
2417 curenv->requested_size = env.requested_size;
2418 curenv->prev_requested_size = env.prev_requested_size;
2419 curenv->char_height = env.char_height;
2420 curenv->char_slant = env.char_slant;
2421 curenv->fontno = env.fontno;
2422 curenv->prev_fontno = env.prev_fontno;
2423 curenv->glyph_color = env.glyph_color;
2424 curenv->prev_glyph_color = env.prev_glyph_color;
2425 curenv->fill_color = env.fill_color;
2426 curenv->prev_fill_color = env.prev_fill_color;
2435 hunits length_title(curenv->title_length);
2436 hunits f = length_title - part_width[1];
2438 n = new hmotion_node(f2 - part_width[2], curenv->get_fill_color(), n);
2446 n = new hmotion_node(f - f2 - part_width[0], curenv->get_fill_color(), n);
2454 curenv->output_title(n, !curenv->fill, curenv->vertical_spacing,
2455 curenv->total_post_vertical_spacing(), length_title);
2456 curenv->hyphen_line_count = 0;
2462 curenv->adjust_mode |= 1;
2466 curenv->adjust_mode = ADJUST_LEFT;
2469 curenv->adjust_mode = ADJUST_RIGHT;
2472 curenv->adjust_mode = ADJUST_CENTER;
2476 curenv->adjust_mode = ADJUST_BOTH;
2480 if (get_integer(&n)) {
2482 warning(WARN_RANGE, "negative adjustment mode");
2484 curenv->adjust_mode = 5;
2485 warning(WARN_RANGE, "adjustment mode `%1' out of range", n);
2488 curenv->adjust_mode = n;
2497 curenv->adjust_mode &= ~1;
2501 void do_input_trap(int continued)
2503 curenv->input_trap_count = 0;
2505 curenv->continued_input_trap = 1;
2507 if (has_arg() && get_integer(&n)) {
2510 "number of lines for input trap must be greater than zero");
2512 symbol s = get_name(1);
2514 curenv->input_trap_count = n;
2515 curenv->input_trap = s;
2527 void input_trap_continued()
2534 // must not be R or C or L or a legitimate part of a number expression
2535 const char TAB_REPEAT_CHAR = 'T';
2541 tab(hunits, tab_type);
2542 enum { BLOCK = 1024 };
2543 static tab *free_list;
2544 void *operator new(size_t);
2545 void operator delete(void *);
2548 tab *tab::free_list = 0;
2550 void *tab::operator new(size_t n)
2552 assert(n == sizeof(tab));
2554 free_list = (tab *)new char[sizeof(tab)*BLOCK];
2555 for (int i = 0; i < BLOCK - 1; i++)
2556 free_list[i].next = free_list + i + 1;
2557 free_list[BLOCK-1].next = 0;
2560 free_list = (tab *)(free_list->next);
2566 /* cfront can't cope with this. */
2569 void tab::operator delete(void *p)
2572 ((tab *)p)->next = free_list;
2573 free_list = (tab *)p;
2577 tab::tab(hunits x, tab_type t) : next(0), pos(x), type(t)
2581 tab_stops::tab_stops(hunits distance, tab_type type)
2584 repeated_list = new tab(distance, type);
2587 tab_stops::~tab_stops()
2592 tab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance)
2596 return distance_to_next_tab(curpos, distance, &nextpos);
2599 tab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance,
2604 for (tem = initial_list; tem && tem->pos <= curpos; tem = tem->next)
2607 *distance = tem->pos - curpos;
2608 *nextpos = tem->pos;
2611 if (repeated_list == 0)
2613 hunits base = lastpos;
2615 for (tem = repeated_list; tem && tem->pos + base <= curpos; tem = tem->next)
2618 *distance = tem->pos + base - curpos;
2619 *nextpos = tem->pos + base;
2622 assert(lastpos > 0);
2628 const char *tab_stops::to_string()
2630 static char *buf = 0;
2631 static int buf_size = 0;
2632 // figure out a maximum on the amount of space we can need
2635 for (p = initial_list; p; p = p->next)
2637 for (p = repeated_list; p; p = p->next)
2639 // (10 for digits + 1 for u + 1 for 'C' or 'R') + 2 for ' &' + 1 for '\0'
2640 int need = count*12 + 3;
2641 if (buf == 0 || need > buf_size) {
2645 buf = new char[buf_size];
2648 for (p = initial_list; p; p = p->next) {
2649 strcpy(ptr, i_to_a(p->pos.to_units()));
2650 ptr = strchr(ptr, '\0');
2668 *ptr++ = TAB_REPEAT_CHAR;
2669 for (p = repeated_list; p; p = p->next) {
2670 strcpy(ptr, i_to_a(p->pos.to_units()));
2671 ptr = strchr(ptr, '\0');
2692 tab_stops::tab_stops() : initial_list(0), repeated_list(0)
2696 tab_stops::tab_stops(const tab_stops &ts)
2697 : initial_list(0), repeated_list(0)
2699 tab **p = &initial_list;
2700 tab *t = ts.initial_list;
2702 *p = new tab(t->pos, t->type);
2707 t = ts.repeated_list;
2709 *p = new tab(t->pos, t->type);
2715 void tab_stops::clear()
2717 while (initial_list) {
2718 tab *tem = initial_list;
2719 initial_list = initial_list->next;
2722 while (repeated_list) {
2723 tab *tem = repeated_list;
2724 repeated_list = repeated_list->next;
2729 void tab_stops::add_tab(hunits pos, tab_type type, int repeated)
2732 for (p = repeated ? &repeated_list : &initial_list; *p; p = &(*p)->next)
2734 *p = new tab(pos, type);
2738 void tab_stops::operator=(const tab_stops &ts)
2741 tab **p = &initial_list;
2742 tab *t = ts.initial_list;
2744 *p = new tab(t->pos, t->type);
2749 t = ts.repeated_list;
2751 *p = new tab(t->pos, t->type);
2760 hunits prev_pos = 0;
2765 if (tok.ch() == TAB_REPEAT_CHAR) {
2770 if (!get_hunits(&pos, 'm', prev_pos))
2772 tab_type type = TAB_LEFT;
2773 if (tok.ch() == 'C') {
2777 else if (tok.ch() == 'R') {
2781 else if (tok.ch() == 'L') {
2784 if (pos <= prev_pos && !first)
2786 "positions of tab stops must be strictly increasing");
2788 tabs.add_tab(pos, type, repeated);
2793 curenv->tabs = tabs;
2794 curdiv->modified_tag.incl(MTSM_TA);
2798 const char *environment::get_tabs()
2800 return tabs.to_string();
2803 tab_type environment::distance_to_next_tab(hunits *distance)
2806 ? curenv->tabs.distance_to_next_tab(get_text_length(), distance)
2807 : curenv->tabs.distance_to_next_tab(get_input_line_position(), distance);
2810 tab_type environment::distance_to_next_tab(hunits *distance, hunits *leftpos)
2813 ? curenv->tabs.distance_to_next_tab(get_text_length(), distance, leftpos)
2814 : curenv->tabs.distance_to_next_tab(get_input_line_position(), distance,
2818 void field_characters()
2820 field_delimiter_char = get_optional_char();
2821 if (field_delimiter_char)
2822 padding_indicator_char = get_optional_char();
2824 padding_indicator_char = 0;
2828 void line_tabs_request()
2831 if (has_arg() && get_integer(&n))
2832 curenv->line_tabs = n != 0;
2834 curenv->line_tabs = 1;
2838 int environment::get_line_tabs()
2843 void environment::wrap_up_tab()
2850 switch (current_tab) {
2852 tab_amount = tab_distance - tab_width;
2853 line = make_tab_node(tab_amount, line);
2856 tab_amount = tab_distance - tab_width/2;
2857 line = make_tab_node(tab_amount, line);
2864 width_total += tab_amount;
2865 width_total += tab_width;
2866 if (current_field) {
2867 if (tab_precedes_field) {
2868 pre_field_width += tab_amount;
2869 tab_precedes_field = 0;
2871 field_distance -= tab_amount;
2872 field_spaces += tab_field_spaces;
2874 if (tab_contents != 0) {
2876 for (tem = tab_contents; tem->next != 0; tem = tem->next)
2879 line = tab_contents;
2881 tab_field_spaces = 0;
2885 current_tab = TAB_NONE;
2888 node *environment::make_tab_node(hunits d, node *next)
2890 if (leader_node != 0 && d < 0) {
2891 error("motion generated by leader cannot be negative");
2896 return new hmotion_node(d, 1, 0, get_fill_color(), next);
2897 node *n = new hline_node(d, leader_node, next);
2902 void environment::handle_tab(int is_leader)
2908 charinfo *ci = is_leader ? leader_char : tab_char;
2910 leader_node = ci ? make_char_node(ci) : 0;
2911 tab_type t = distance_to_next_tab(&d, &absolute);
2916 add_node(make_tag("tab L", absolute.to_units()));
2917 add_node(make_tab_node(d));
2920 add_node(make_tag("tab R", absolute.to_units()));
2923 add_node(make_tag("tab C", absolute.to_units()));
2932 tab_field_spaces = 0;
2935 void environment::start_field()
2937 assert(!current_field);
2939 if (distance_to_next_tab(&d) != TAB_NONE) {
2940 pre_field_width = get_text_length();
2944 tab_field_spaces = 0;
2945 for (node *p = line; p; p = p->next)
2950 tab_precedes_field = current_tab != TAB_NONE;
2953 error("zero field width");
2956 void environment::wrap_up_field()
2958 if (!current_tab && field_spaces == 0)
2960 hunits padding = field_distance - (get_text_length() - pre_field_width);
2961 if (current_tab && tab_field_spaces != 0) {
2962 hunits tab_padding = scale(padding,
2964 field_spaces + tab_field_spaces);
2965 padding -= tab_padding;
2966 distribute_space(tab_contents, tab_field_spaces, tab_padding, 1);
2967 tab_field_spaces = 0;
2968 tab_width += tab_padding;
2970 if (field_spaces != 0) {
2971 distribute_space(line, field_spaces, padding, 1);
2972 width_total += padding;
2974 // the start of the tab has been moved to the right by padding, so
2975 tab_distance -= padding;
2976 if (tab_distance <= H0) {
2977 // use the next tab stop instead
2978 current_tab = tabs.distance_to_next_tab(get_input_line_position()
2981 if (current_tab == TAB_NONE || current_tab == TAB_LEFT) {
2982 width_total += tab_width;
2983 if (current_tab == TAB_LEFT) {
2984 line = make_tab_node(tab_distance, line);
2985 width_total += tab_distance;
2986 current_tab = TAB_NONE;
2988 if (tab_contents != 0) {
2990 for (tem = tab_contents; tem->next != 0; tem = tem->next)
2993 line = tab_contents;
3005 void environment::add_padding()
3008 tab_contents = new space_node(H0, get_fill_color(), tab_contents);
3014 line = new space_node(H0, get_fill_color(), line);
3019 typedef int (environment::*INT_FUNCP)();
3020 typedef vunits (environment::*VUNITS_FUNCP)();
3021 typedef hunits (environment::*HUNITS_FUNCP)();
3022 typedef const char *(environment::*STRING_FUNCP)();
3024 class int_env_reg : public reg {
3027 int_env_reg(INT_FUNCP);
3028 const char *get_string();
3029 int get_value(units *val);
3032 class vunits_env_reg : public reg {
3035 vunits_env_reg(VUNITS_FUNCP f);
3036 const char *get_string();
3037 int get_value(units *val);
3041 class hunits_env_reg : public reg {
3044 hunits_env_reg(HUNITS_FUNCP f);
3045 const char *get_string();
3046 int get_value(units *val);
3049 class string_env_reg : public reg {
3052 string_env_reg(STRING_FUNCP);
3053 const char *get_string();
3056 int_env_reg::int_env_reg(INT_FUNCP f) : func(f)
3060 int int_env_reg::get_value(units *val)
3062 *val = (curenv->*func)();
3066 const char *int_env_reg::get_string()
3068 return i_to_a((curenv->*func)());
3071 vunits_env_reg::vunits_env_reg(VUNITS_FUNCP f) : func(f)
3075 int vunits_env_reg::get_value(units *val)
3077 *val = (curenv->*func)().to_units();
3081 const char *vunits_env_reg::get_string()
3083 return i_to_a((curenv->*func)().to_units());
3086 hunits_env_reg::hunits_env_reg(HUNITS_FUNCP f) : func(f)
3090 int hunits_env_reg::get_value(units *val)
3092 *val = (curenv->*func)().to_units();
3096 const char *hunits_env_reg::get_string()
3098 return i_to_a((curenv->*func)().to_units());
3101 string_env_reg::string_env_reg(STRING_FUNCP f) : func(f)
3105 const char *string_env_reg::get_string()
3107 return (curenv->*func)();
3110 class horizontal_place_reg : public general_reg {
3112 horizontal_place_reg();
3113 int get_value(units *);
3114 void set_value(units);
3117 horizontal_place_reg::horizontal_place_reg()
3121 int horizontal_place_reg::get_value(units *res)
3123 *res = curenv->get_input_line_position().to_units();
3127 void horizontal_place_reg::set_value(units n)
3129 curenv->set_input_line_position(hunits(n));
3132 const char *environment::get_font_family_string()
3134 return family->nm.contents();
3137 const char *environment::get_glyph_color_string()
3139 return glyph_color->nm.contents();
3142 const char *environment::get_fill_color_string()
3144 return fill_color->nm.contents();
3147 const char *environment::get_font_name_string()
3149 symbol f = get_font_name(fontno, this);
3150 return f.contents();
3153 const char *environment::get_style_name_string()
3155 symbol f = get_style_name(fontno);
3156 return f.contents();
3159 const char *environment::get_name_string()
3161 return name.contents();
3164 // Convert a quantity in scaled points to ascii decimal fraction.
3166 const char *sptoa(int sp)
3169 assert(sizescale > 0);
3172 if (sp % sizescale == 0)
3173 return i_to_a(sp/sizescale);
3174 // See if 1/sizescale is exactly representable as a decimal fraction,
3175 // ie its only prime factors are 2 and 5.
3178 while ((n & 1) == 0) {
3183 while ((n % 5) == 0) {
3188 int decimal_point = power5 > power2 ? power5 : power2;
3189 if (decimal_point <= 10) {
3192 for (t = decimal_point - power2; --t >= 0;)
3194 for (t = decimal_point - power5; --t >= 0;)
3196 if (factor == 1 || sp <= INT_MAX/factor)
3197 return if_to_a(sp*factor, decimal_point);
3200 double s = double(sp)/double(sizescale);
3201 double factor = 10.0;
3203 int decimal_point = 0;
3205 double v = ceil(s*factor);
3210 } while (++decimal_point < 10);
3211 return if_to_a(int(val), decimal_point);
3214 const char *environment::get_point_size_string()
3216 return sptoa(curenv->get_point_size());
3219 const char *environment::get_requested_point_size_string()
3221 return sptoa(curenv->get_requested_point_size());
3224 #define init_int_env_reg(name, func) \
3225 number_reg_dictionary.define(name, new int_env_reg(&environment::func))
3227 #define init_vunits_env_reg(name, func) \
3228 number_reg_dictionary.define(name, new vunits_env_reg(&environment::func))
3230 #define init_hunits_env_reg(name, func) \
3231 number_reg_dictionary.define(name, new hunits_env_reg(&environment::func))
3233 #define init_string_env_reg(name, func) \
3234 number_reg_dictionary.define(name, new string_env_reg(&environment::func))
3236 void init_env_requests()
3238 init_request("ad", adjust);
3239 init_request("br", break_request);
3240 init_request("brp", break_spread_request);
3241 init_request("c2", no_break_control_char);
3242 init_request("cc", control_char);
3243 init_request("ce", center);
3244 init_request("cu", continuous_underline);
3245 init_request("ev", environment_switch);
3246 init_request("evc", environment_copy);
3247 init_request("fam", family_change);
3248 init_request("fc", field_characters);
3249 init_request("fi", fill);
3250 init_request("fcolor", fill_color_change);
3251 init_request("ft", font_change);
3252 init_request("gcolor", glyph_color_change);
3253 init_request("hc", hyphen_char);
3254 init_request("hlm", hyphen_line_max_request);
3255 init_request("hy", hyphenate_request);
3256 init_request("hym", hyphenation_margin_request);
3257 init_request("hys", hyphenation_space_request);
3258 init_request("in", indent);
3259 init_request("it", input_trap);
3260 init_request("itc", input_trap_continued);
3261 init_request("lc", leader_character);
3262 init_request("linetabs", line_tabs_request);
3263 init_request("ll", line_length);
3264 init_request("ls", line_spacing);
3265 init_request("lt", title_length);
3266 init_request("mc", margin_character);
3267 init_request("na", no_adjust);
3268 init_request("nf", no_fill);
3269 init_request("nh", no_hyphenate);
3270 init_request("nm", number_lines);
3271 init_request("nn", no_number);
3272 init_request("ps", point_size);
3273 init_request("pvs", post_vertical_spacing);
3274 init_request("rj", right_justify);
3275 init_request("sizes", override_sizes);
3276 init_request("ss", space_size);
3277 init_request("ta", set_tabs);
3278 init_request("ti", temporary_indent);
3279 init_request("tc", tab_character);
3280 init_request("tl", title);
3281 init_request("ul", underline);
3282 init_request("vs", vertical_spacing);
3283 #ifdef WIDOW_CONTROL
3284 init_request("wdc", widow_control_request);
3285 #endif /* WIDOW_CONTROL */
3286 init_int_env_reg(".b", get_bold);
3287 init_vunits_env_reg(".cdp", get_prev_char_depth);
3288 init_int_env_reg(".ce", get_center_lines);
3289 init_vunits_env_reg(".cht", get_prev_char_height);
3290 init_hunits_env_reg(".csk", get_prev_char_skew);
3291 init_string_env_reg(".ev", get_name_string);
3292 init_int_env_reg(".f", get_font);
3293 init_string_env_reg(".fam", get_font_family_string);
3294 init_string_env_reg(".fn", get_font_name_string);
3295 init_int_env_reg(".height", get_char_height);
3296 init_int_env_reg(".hlc", get_hyphen_line_count);
3297 init_int_env_reg(".hlm", get_hyphen_line_max);
3298 init_int_env_reg(".hy", get_hyphenation_flags);
3299 init_hunits_env_reg(".hym", get_hyphenation_margin);
3300 init_hunits_env_reg(".hys", get_hyphenation_space);
3301 init_hunits_env_reg(".i", get_indent);
3302 init_hunits_env_reg(".in", get_saved_indent);
3303 init_int_env_reg(".int", get_prev_line_interrupted);
3304 init_int_env_reg(".linetabs", get_line_tabs);
3305 init_hunits_env_reg(".lt", get_title_length);
3306 init_int_env_reg(".j", get_adjust_mode);
3307 init_hunits_env_reg(".k", get_text_length);
3308 init_int_env_reg(".L", get_line_spacing);
3309 init_hunits_env_reg(".l", get_line_length);
3310 init_hunits_env_reg(".ll", get_saved_line_length);
3311 init_string_env_reg(".M", get_fill_color_string);
3312 init_string_env_reg(".m", get_glyph_color_string);
3313 init_hunits_env_reg(".n", get_prev_text_length);
3314 init_int_env_reg(".ps", get_point_size);
3315 init_int_env_reg(".psr", get_requested_point_size);
3316 init_vunits_env_reg(".pvs", get_post_vertical_spacing);
3317 init_int_env_reg(".rj", get_right_justify_lines);
3318 init_string_env_reg(".s", get_point_size_string);
3319 init_int_env_reg(".slant", get_char_slant);
3320 init_int_env_reg(".ss", get_space_size);
3321 init_int_env_reg(".sss", get_sentence_space_size);
3322 init_string_env_reg(".sr", get_requested_point_size_string);
3323 init_string_env_reg(".sty", get_style_name_string);
3324 init_string_env_reg(".tabs", get_tabs);
3325 init_int_env_reg(".u", get_fill);
3326 init_vunits_env_reg(".v", get_vertical_spacing);
3327 init_hunits_env_reg(".w", get_prev_char_width);
3328 number_reg_dictionary.define("ct", new variable_reg(&ct_reg_contents));
3329 number_reg_dictionary.define("hp", new horizontal_place_reg);
3330 number_reg_dictionary.define("ln", new variable_reg(&next_line_number));
3331 number_reg_dictionary.define("rsb", new variable_reg(&rsb_reg_contents));
3332 number_reg_dictionary.define("rst", new variable_reg(&rst_reg_contents));
3333 number_reg_dictionary.define("sb", new variable_reg(&sb_reg_contents));
3334 number_reg_dictionary.define("skw", new variable_reg(&skw_reg_contents));
3335 number_reg_dictionary.define("ssc", new variable_reg(&ssc_reg_contents));
3336 number_reg_dictionary.define("st", new variable_reg(&st_reg_contents));
3339 // Hyphenation - TeX's hyphenation algorithm with a less fancy implementation.
3345 virtual void do_match(int len, void *val) = 0;
3346 virtual void do_delete(void *) = 0;
3347 void delete_trie_node(trie_node *);
3350 virtual ~trie(); // virtual to shut up g++
3351 void insert(const char *, int, void *);
3352 // find calls do_match for each match it finds
3353 void find(const char *pat, int patlen);
3357 class hyphen_trie : private trie {
3359 void do_match(int i, void *v);
3360 void do_delete(void *v);
3361 void insert_pattern(const char *pat, int patlen, int *num);
3362 void insert_hyphenation(dictionary *ex, const char *pat, int patlen);
3363 int hpf_getc(FILE *f);
3367 void hyphenate(const char *word, int len, int *hyphens);
3368 void read_patterns_file(const char *name, int append, dictionary *ex);
3371 struct hyphenation_language {
3373 dictionary exceptions;
3374 hyphen_trie patterns;
3375 hyphenation_language(symbol nm) : name(nm), exceptions(501) {}
3376 ~hyphenation_language() { }
3379 dictionary language_dictionary(5);
3380 hyphenation_language *current_language = 0;
3382 static void set_hyphenation_language()
3384 symbol nm = get_name(1);
3385 if (!nm.is_null()) {
3386 current_language = (hyphenation_language *)language_dictionary.lookup(nm);
3387 if (!current_language) {
3388 current_language = new hyphenation_language(nm);
3389 (void)language_dictionary.lookup(nm, (void *)current_language);
3395 const int WORD_MAX = 256; // we use unsigned char for offsets in
3396 // hyphenation exceptions
3398 static void hyphen_word()
3400 if (!current_language) {
3401 error("no current hyphenation language");
3405 char buf[WORD_MAX + 1];
3406 unsigned char pos[WORD_MAX + 2];
3409 if (tok.newline() || tok.eof())
3413 while (i < WORD_MAX && !tok.space() && !tok.newline() && !tok.eof()) {
3414 charinfo *ci = tok.get_char(1);
3420 if (ci->get_ascii_code() == '-') {
3421 if (i > 0 && (npos == 0 || pos[npos - 1] != i))
3425 unsigned char c = ci->get_hyphenation_code();
3434 unsigned char *tem = new unsigned char[npos + 1];
3435 memcpy(tem, pos, npos + 1);
3436 tem = (unsigned char *)current_language->exceptions.lookup(symbol(buf),
3450 trie_node(char, trie_node *);
3453 trie_node::trie_node(char ch, trie_node *p)
3454 : c(ch), down(0), right(p), val(0)
3465 delete_trie_node(tp);
3470 void trie::delete_trie_node(trie_node *p)
3473 delete_trie_node(p->down);
3474 delete_trie_node(p->right);
3481 void trie::insert(const char *pat, int patlen, void *val)
3483 trie_node **p = &tp;
3484 assert(patlen > 0 && pat != 0);
3486 while (*p != 0 && (*p)->c < pat[0])
3488 if (*p == 0 || (*p)->c != pat[0])
3489 *p = new trie_node(pat[0], *p);
3490 if (--patlen == 0) {
3499 void trie::find(const char *pat, int patlen)
3502 for (int i = 0; p != 0 && i < patlen; i++) {
3503 while (p != 0 && p->c < pat[i])
3505 if (p != 0 && p->c == pat[i]) {
3507 do_match(i+1, p->val);
3519 operation(int, int, operation *);
3522 operation::operation(int i, int j, operation *op)
3523 : next(op), distance(j), num(i)
3527 void hyphen_trie::insert_pattern(const char *pat, int patlen, int *num)
3530 for (int i = 0; i < patlen+1; i++)
3532 op = new operation(num[i], patlen - i, op);
3533 insert(pat, patlen, op);
3536 void hyphen_trie::insert_hyphenation(dictionary *ex, const char *pat,
3539 char buf[WORD_MAX + 1];
3540 unsigned char pos[WORD_MAX + 2];
3543 while (j < patlen) {
3544 unsigned char c = pat[j++];
3546 if (i > 0 && (npos == 0 || pos[npos - 1] != i))
3550 buf[i++] = hpf_code_table[c];
3555 unsigned char *tem = new unsigned char[npos + 1];
3556 memcpy(tem, pos, npos + 1);
3557 tem = (unsigned char *)ex->lookup(symbol(buf), tem);
3563 void hyphen_trie::hyphenate(const char *word, int len, int *hyphens)
3566 for (j = 0; j < len + 1; j++)
3568 for (j = 0; j < len - 1; j++) {
3570 find(word + j, len - j);
3574 inline int max(int m, int n)
3576 return m > n ? m : n;
3579 void hyphen_trie::do_match(int i, void *v)
3581 operation *op = (operation *)v;
3583 h[i - op->distance] = max(h[i - op->distance], op->num);
3588 void hyphen_trie::do_delete(void *v)
3590 operation *op = (operation *)v;
3592 operation *tem = op;
3598 /* We use very simple rules to parse TeX's hyphenation patterns.
3600 . `%' starts a comment even if preceded by `\'.
3602 . No support for digraphs and like `\$'.
3604 . `^^xx' (`x' is 0-9 or a-f), and `^^x' (character code of `x' in the
3605 range 0-127) are recognized; other use of `^' causes an error.
3607 . No macro expansion.
3609 . We check for the expression `\patterns{...}' (possibly with
3610 whitespace before and after the braces). Everything between the
3611 braces is taken as hyphenation patterns. Consequently, `{' and `}'
3612 are not allowed in patterns.
3614 . Similarly, `\hyphenation{...}' gives a list of hyphenation
3617 . `\endinput' is recognized also.
3619 . For backwards compatibility, if `\patterns' is missing, the
3620 whole file is treated as a list of hyphenation patterns (only
3621 recognizing `%' as the start of a comment.
3625 int hyphen_trie::hpf_getc(FILE *f)
3637 if (((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))
3638 && ((c1 >= '0' && c1 <= '9') || (c1 >= 'a' && c1 <= 'f'))) {
3639 if (c >= '0' && c <= '9')
3643 if (c1 >= '0' && c1 <= '9')
3651 if (c >= 0 && c <= 63)
3653 else if (c >= 64 && c <= 127)
3660 error("invalid ^, ^^x, or ^^xx character in hyphenation patterns file");
3664 void hyphen_trie::read_patterns_file(const char *name, int append,
3670 for (int i = 0; i < WORD_MAX; i++)
3672 int num[WORD_MAX+1];
3675 FILE *fp = mac_path->open_file(name, &path);
3677 error("can't find hyphenation patterns file `%1'", name);
3680 int c = hpf_getc(fp);
3681 int have_patterns = 0; // we've seen \patterns
3682 int final_pattern = 0; // 1 if we have a trailing closing brace
3683 int have_hyphenation = 0; // we've seen \hyphenation
3684 int final_hyphenation = 0; // 1 if we have a trailing closing brace
3685 int have_keyword = 0; // we've seen either \patterns or \hyphenation
3686 int traditional = 0; // don't handle \patterns
3689 if (c == '%') { // skip comments
3692 } while (c != EOF && c != '\n');
3694 if (c == EOF || !csspace(c))
3699 if (have_keyword || traditional) // we are done
3701 else { // rescan file in `traditional' mode
3710 if (!(c == '{' || c == '}')) { // skip braces at line start
3711 do { // scan patterns
3719 } while (i < WORD_MAX && c != EOF && !csspace(c)
3720 && c != '%' && c != '{' && c != '}');
3723 if (i >= 9 && !strncmp(buf + i - 9, "\\patterns", 9)) {
3727 if (have_patterns || have_hyphenation)
3728 error("\\patterns not allowed inside of %1 group",
3729 have_patterns ? "\\patterns" : "\\hyphenation");
3738 else if (i >= 12 && !strncmp(buf + i - 12, "\\hyphenation", 12)) {
3742 if (have_patterns || have_hyphenation)
3743 error("\\hyphenation not allowed inside of %1 group",
3744 have_patterns ? "\\patterns" : "\\hyphenation");
3746 have_hyphenation = 1;
3753 else if (strstr(buf, "\\endinput")) {
3754 if (have_patterns || have_hyphenation)
3755 error("found \\endinput inside of %1 group",
3756 have_patterns ? "\\patterns" : "\\hyphenation");
3759 else if (c == '}') {
3760 if (have_patterns) {
3765 else if (have_hyphenation) {
3766 have_hyphenation = 0;
3768 final_hyphenation = 1;
3772 else if (c == '{') {
3773 if (have_patterns || have_hyphenation)
3774 error("`{' not allowed within %1 group",
3775 have_patterns ? "\\patterns" : "\\hyphenation");
3776 c = hpf_getc(fp); // skipped if not starting \patterns
3781 if (c == '{' || c == '}')
3785 if (have_patterns || final_pattern || traditional) {
3786 for (int j = 0; j < i; j++)
3787 buf[j] = hpf_code_table[(unsigned char)buf[j]];
3788 insert_pattern(buf, i, num);
3791 else if (have_hyphenation || final_hyphenation) {
3792 insert_hyphenation(ex, buf, i);
3793 final_hyphenation = 0;
3802 void hyphenate(hyphen_list *h, unsigned flags)
3804 if (!current_language)
3807 while (h && h->hyphenation_code == 0)
3810 char hbuf[WORD_MAX+2];
3811 char *buf = hbuf + 1;
3813 for (tem = h; tem && len < WORD_MAX; tem = tem->next) {
3814 if (tem->hyphenation_code != 0)
3815 buf[len++] = tem->hyphenation_code;
3819 hyphen_list *nexth = tem;
3823 = (unsigned char *)current_language->exceptions.lookup(buf);
3827 for (tem = h; tem != 0; tem = tem->next, i++)
3834 hbuf[0] = hbuf[len+1] = '.';
3835 int num[WORD_MAX+3];
3836 current_language->patterns.hyphenate(hbuf, len+2, num);
3843 for (i = 2, tem = h; i < len && tem; tem = tem->next, i++)
3852 static void do_hyphenation_patterns_file(int append)
3854 symbol name = get_long_name(1);
3855 if (!name.is_null()) {
3856 if (!current_language)
3857 error("no current hyphenation language");
3859 current_language->patterns.read_patterns_file(
3860 name.contents(), append,
3861 ¤t_language->exceptions);
3866 static void hyphenation_patterns_file()
3868 do_hyphenation_patterns_file(0);
3871 static void hyphenation_patterns_file_append()
3873 do_hyphenation_patterns_file(1);
3876 class hyphenation_language_reg : public reg {
3878 const char *get_string();
3881 const char *hyphenation_language_reg::get_string()
3883 return current_language ? current_language->name.contents() : "";
3886 void init_hyphen_requests()
3888 init_request("hw", hyphen_word);
3889 init_request("hla", set_hyphenation_language);
3890 init_request("hpf", hyphenation_patterns_file);
3891 init_request("hpfa", hyphenation_patterns_file_append);
3892 number_reg_dictionary.define(".hla", new hyphenation_language_reg);