2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002
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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
24 #include "dictionary.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;
56 charinfo *field_delimiter_char;
57 charinfo *padding_indicator_char;
59 int translate_space_to_dummy = 0;
61 class pending_output_line {
68 int last_line; // Is it the last line of the paragraph?
69 #endif /* WIDOW_CONTROL */
71 pending_output_line *next;
73 pending_output_line(node *, int, vunits, vunits, hunits,
74 pending_output_line * = 0);
75 ~pending_output_line();
79 friend void environment::mark_last_line();
80 friend void environment::output(node *, int, vunits, vunits, hunits);
81 #endif /* WIDOW_CONTROL */
84 pending_output_line::pending_output_line(node *n, int nf, vunits v, vunits pv,
85 hunits w, pending_output_line *p)
86 : nd(n), no_fill(nf), vs(v), post_vs(pv), width(w),
89 #endif /* WIDOW_CONTROL */
94 pending_output_line::~pending_output_line()
99 int pending_output_line::output()
101 if (trap_sprung_flag)
104 if (next && next->last_line && !no_fill) {
105 curdiv->need(vs + post_vs + vunits(vresolution));
106 if (trap_sprung_flag) {
107 next->last_line = 0; // Try to avoid infinite loops.
112 curdiv->output(nd, no_fill, vs, post_vs, width);
117 void environment::output(node *nd, int no_fill, vunits vs, vunits post_vs,
121 while (pending_lines) {
122 if (widow_control && !pending_lines->no_fill && !pending_lines->next)
124 if (!pending_lines->output())
126 pending_output_line *tem = pending_lines;
127 pending_lines = pending_lines->next;
130 #else /* WIDOW_CONTROL */
131 output_pending_lines();
132 #endif /* WIDOW_CONTROL */
133 if (!trap_sprung_flag && !pending_lines
135 && (!widow_control || no_fill)
136 #endif /* WIDOW_CONTROL */
138 curdiv->output(nd, no_fill, vs, post_vs, width);
141 pending_output_line **p;
142 for (p = &pending_lines; *p; p = &(*p)->next)
144 *p = new pending_output_line(nd, no_fill, vs, post_vs, width);
148 // a line from .tl goes at the head of the queue
150 void environment::output_title(node *nd, int no_fill, vunits vs,
151 vunits post_vs, hunits width)
153 if (!trap_sprung_flag)
154 curdiv->output(nd, no_fill, vs, post_vs, width);
156 pending_lines = new pending_output_line(nd, no_fill, vs, post_vs, width,
160 void environment::output_pending_lines()
162 while (pending_lines && pending_lines->output()) {
163 pending_output_line *tem = pending_lines;
164 pending_lines = pending_lines->next;
171 void environment::mark_last_line()
173 if (!widow_control || !pending_lines)
175 for (pending_output_line *p = pending_lines; p->next; p = p->next)
181 void widow_control_request()
184 if (has_arg() && get_integer(&n))
185 curenv->widow_control = n != 0;
187 curenv->widow_control = 1;
191 #endif /* WIDOW_CONTROL */
193 /* font_size functions */
195 size_range *font_size::size_table = 0;
196 int font_size::nranges = 0;
200 int compare_ranges(const void *p1, const void *p2)
202 return ((size_range *)p1)->min - ((size_range *)p2)->min;
207 void font_size::init_size_table(int *sizes)
210 while (sizes[nranges*2] != 0)
213 size_table = new size_range[nranges];
214 for (int i = 0; i < nranges; i++) {
215 size_table[i].min = sizes[i*2];
216 size_table[i].max = sizes[i*2 + 1];
218 qsort(size_table, nranges, sizeof(size_range), compare_ranges);
221 font_size::font_size(int sp)
223 for (int i = 0; i < nranges; i++) {
224 if (sp < size_table[i].min) {
225 if (i > 0 && size_table[i].min - sp >= sp - size_table[i - 1].max)
226 p = size_table[i - 1].max;
228 p = size_table[i].min;
231 if (sp <= size_table[i].max) {
236 p = size_table[nranges - 1].max;
239 int font_size::to_units()
241 return scale(p, units_per_inch, sizescale*72);
244 // we can't do this in a static constructor because various dictionaries
245 // have to get initialized first
247 void init_environments()
249 curenv = env_table[0] = new environment("0");
254 curenv->tab_char = get_optional_char();
258 void leader_character()
260 curenv->leader_char = get_optional_char();
264 void environment::add_char(charinfo *ci)
269 // don't allow fields in dummy environments
270 else if (ci == field_delimiter_char && !dummy) {
276 else if (current_field && ci == padding_indicator_char)
278 else if (current_tab) {
279 if (tab_contents == 0)
280 tab_contents = new line_start_node;
281 if (ci != hyphen_indicator_char)
282 tab_contents = tab_contents->add_char(ci, this, &tab_width, &s);
284 tab_contents = tab_contents->add_discretionary_hyphen();
289 if (ci != hyphen_indicator_char)
290 line = line->add_char(ci, this, &width_total, &space_total);
292 line = line->add_discretionary_hyphen();
296 node *environment::make_char_node(charinfo *ci)
298 return make_node(ci, this);
301 void environment::add_node(node *n)
305 if (current_tab || current_field)
310 else if (current_tab) {
311 n->next = tab_contents;
313 tab_width += n->width();
317 if (discarding && n->discardable()) {
318 // XXX possibly: input_line_start -= n->width();
324 width_total += n->width();
325 space_total += n->nspaces();
332 void environment::add_hyphen_indicator()
334 if (current_tab || interrupted || current_field
335 || hyphen_indicator_char != 0)
339 line = line->add_discretionary_hyphen();
342 int environment::get_hyphenation_flags()
344 return hyphenation_flags;
347 int environment::get_hyphen_line_max()
349 return hyphen_line_max;
352 int environment::get_hyphen_line_count()
354 return hyphen_line_count;
357 int environment::get_center_lines()
362 int environment::get_right_justify_lines()
364 return right_justify_lines;
367 void environment::add_italic_correction()
371 tab_contents = tab_contents->add_italic_correction(&tab_width);
374 line = line->add_italic_correction(&width_total);
377 void environment::space_newline()
379 assert(!current_tab && !current_field);
383 hunits sw = env_space_width(this);
384 hunits ssw = env_sentence_space_width(this);
385 if (!translate_space_to_dummy) {
387 if (node_list_ends_sentence(line) == 1)
390 width_list *w = new width_list(sw, ssw);
391 if (node_list_ends_sentence(line) == 1)
392 w->next = new width_list(sw, ssw);
393 if (line != 0 && line->merge_space(x, sw, ssw)) {
397 add_node(new word_space_node(x, get_fill_color(), w));
398 possibly_break_line(0, spread_flag);
402 void environment::space()
404 space(env_space_width(this), env_sentence_space_width(this));
407 void environment::space(hunits space_width, hunits sentence_space_width)
411 if (current_field && padding_indicator_char == 0) {
415 hunits x = translate_space_to_dummy ? H0 : space_width;
416 node *p = current_tab ? tab_contents : line;
417 hunits *tp = current_tab ? &tab_width : &width_total;
418 if (p && p->nspaces() == 1 && p->width() == x
419 && node_list_ends_sentence(p->next) == 1) {
420 hunits xx = translate_space_to_dummy ? H0 : sentence_space_width;
421 if (p->merge_space(xx, space_width, sentence_space_width)) {
426 if (p && p->merge_space(x, space_width, sentence_space_width)) {
430 add_node(new word_space_node(x,
432 new width_list(space_width,
433 sentence_space_width)));
434 possibly_break_line(0, spread_flag);
438 node *do_underline_special(int);
440 void environment::set_font(symbol nm)
444 if (nm == symbol("P") || nm.is_empty()) {
445 if (family->make_definite(prev_fontno) < 0)
448 fontno = prev_fontno;
452 prev_fontno = fontno;
453 int n = symbol_fontno(nm);
455 n = next_available_font_position();
456 if (!mount_font(n, nm))
459 if (family->make_definite(n) < 0)
463 if (underline_spaces && fontno != prev_fontno) {
464 if (fontno == get_underline_fontno())
465 add_node(do_underline_special(1));
466 if (prev_fontno == get_underline_fontno())
467 add_node(do_underline_special(0));
471 void environment::set_font(int n)
475 if (is_good_fontno(n)) {
476 prev_fontno = fontno;
480 warning(WARN_FONT, "bad font number");
483 void environment::set_family(symbol fam)
487 if (fam.is_null() || fam.is_empty()) {
488 if (prev_family->make_definite(fontno) < 0)
490 font_family *tem = family;
491 family = prev_family;
495 font_family *f = lookup_family(fam);
496 if (f->make_definite(fontno) < 0)
498 prev_family = family;
503 void environment::set_size(int n)
508 font_size temp = prev_size;
511 int temp2 = prev_requested_size;
512 prev_requested_size = requested_size;
513 requested_size = temp2;
518 prev_requested_size = requested_size;
523 void environment::set_char_height(int n)
527 if (n == requested_size || n <= 0)
533 void environment::set_char_slant(int n)
540 color *environment::get_prev_glyph_color()
542 return prev_glyph_color;
545 color *environment::get_glyph_color()
550 color *environment::get_prev_fill_color()
552 return prev_fill_color;
555 color *environment::get_fill_color()
560 void environment::set_glyph_color(color *c)
564 curenv->prev_glyph_color = curenv->glyph_color;
565 curenv->glyph_color = c;
568 void environment::set_fill_color(color *c)
572 curenv->prev_fill_color = curenv->fill_color;
573 curenv->fill_color = c;
576 environment::environment(symbol nm)
578 prev_line_length((units_per_inch*13)/2),
579 line_length((units_per_inch*13)/2),
580 prev_title_length((units_per_inch*13)/2),
581 title_length((units_per_inch*13)/2),
582 prev_size(sizescale*10),
584 requested_size(sizescale*10),
585 prev_requested_size(sizescale*10),
589 sentence_space_size(12),
590 adjust_mode(ADJUST_BOTH),
593 prev_line_interrupted(0),
595 right_justify_lines(0),
596 prev_vertical_spacing(points_to_units(12)),
597 vertical_spacing(points_to_units(12)),
598 prev_post_vertical_spacing(0),
599 post_vertical_spacing(0),
600 prev_line_spacing(1),
605 have_temporary_indent(0),
609 continued_input_trap(0),
615 tabs(units_per_inch/2, TAB_LEFT),
617 current_tab(TAB_NONE),
620 leader_char(charset_table['.']),
624 margin_character_flags(0),
625 margin_character_node(0),
626 margin_character_distance(points_to_units(10)),
628 number_text_separation(1),
629 line_number_indent(0),
630 line_number_multiple(1),
632 hyphenation_flags(1),
633 hyphen_line_count(0),
635 hyphenation_space(H0),
636 hyphenation_margin(H0),
641 #endif /* WIDOW_CONTROL */
644 glyph_color(&default_color),
645 prev_glyph_color(&default_color),
646 fill_color(&default_color),
647 prev_fill_color(&default_color),
650 no_break_control_char('\''),
651 hyphen_indicator_char(0)
653 prev_family = family = lookup_family(default_family);
654 prev_fontno = fontno = 1;
655 if (!is_good_fontno(1))
656 fatal("font number 1 not a valid font");
657 if (family->make_definite(1) < 0)
658 fatal("invalid default family `%1'", default_family.contents());
659 prev_fontno = fontno;
662 environment::environment(const environment *e)
664 prev_line_length(e->prev_line_length),
665 line_length(e->line_length),
666 prev_title_length(e->prev_title_length),
667 title_length(e->title_length),
668 prev_size(e->prev_size),
670 requested_size(e->requested_size),
671 prev_requested_size(e->prev_requested_size),
672 char_height(e->char_height),
673 char_slant(e->char_slant),
674 prev_fontno(e->prev_fontno),
676 prev_family(e->prev_family),
678 space_size(e->space_size),
679 sentence_space_size(e->sentence_space_size),
680 adjust_mode(e->adjust_mode),
683 prev_line_interrupted(0),
685 right_justify_lines(0),
686 prev_vertical_spacing(e->prev_vertical_spacing),
687 vertical_spacing(e->vertical_spacing),
688 prev_post_vertical_spacing(e->prev_post_vertical_spacing),
689 post_vertical_spacing(e->post_vertical_spacing),
690 prev_line_spacing(e->prev_line_spacing),
691 line_spacing(e->line_spacing),
692 prev_indent(e->prev_indent),
695 have_temporary_indent(0),
699 continued_input_trap(0),
701 prev_text_length(e->prev_text_length),
706 line_tabs(e->line_tabs),
707 current_tab(TAB_NONE),
709 tab_char(e->tab_char),
710 leader_char(e->leader_char),
714 margin_character_flags(e->margin_character_flags),
715 margin_character_node(e->margin_character_node),
716 margin_character_distance(e->margin_character_distance),
718 number_text_separation(e->number_text_separation),
719 line_number_indent(e->line_number_indent),
720 line_number_multiple(e->line_number_multiple),
721 no_number_count(e->no_number_count),
722 hyphenation_flags(e->hyphenation_flags),
723 hyphen_line_count(0),
724 hyphen_line_max(e->hyphen_line_max),
725 hyphenation_space(e->hyphenation_space),
726 hyphenation_margin(e->hyphenation_margin),
730 widow_control(e->widow_control),
731 #endif /* WIDOW_CONTROL */
734 glyph_color(e->glyph_color),
735 prev_glyph_color(e->prev_glyph_color),
736 fill_color(e->fill_color),
737 prev_fill_color(e->prev_fill_color),
738 name(e->name), // so that eg `.if "\n[.ev]"0"' works
739 control_char(e->control_char),
740 no_break_control_char(e->no_break_control_char),
741 hyphen_indicator_char(e->hyphen_indicator_char)
745 void environment::copy(const environment *e)
747 prev_line_length = e->prev_line_length;
748 line_length = e->line_length;
749 prev_title_length = e->prev_title_length;
750 title_length = e->title_length;
751 prev_size = e->prev_size;
753 prev_requested_size = e->prev_requested_size;
754 requested_size = e->requested_size;
755 char_height = e->char_height;
756 char_slant = e->char_slant;
757 space_size = e->space_size;
758 sentence_space_size = e->sentence_space_size;
759 adjust_mode = e->adjust_mode;
762 prev_line_interrupted = 0;
764 right_justify_lines = 0;
765 prev_vertical_spacing = e->prev_vertical_spacing;
766 vertical_spacing = e->vertical_spacing;
767 prev_post_vertical_spacing = e->prev_post_vertical_spacing,
768 post_vertical_spacing = e->post_vertical_spacing,
769 prev_line_spacing = e->prev_line_spacing;
770 line_spacing = e->line_spacing;
771 prev_indent = e->prev_indent;
773 have_temporary_indent = 0;
774 temporary_indent = 0;
776 underline_spaces = 0;
777 input_trap_count = 0;
778 continued_input_trap = 0;
779 prev_text_length = e->prev_text_length;
782 input_line_start = 0;
783 control_char = e->control_char;
784 no_break_control_char = e->no_break_control_char;
785 hyphen_indicator_char = e->hyphen_indicator_char;
791 line_tabs = e->line_tabs;
792 current_tab = TAB_NONE;
794 margin_character_flags = e->margin_character_flags;
795 margin_character_node = e->margin_character_node;
796 margin_character_distance = e->margin_character_distance;
798 number_text_separation = e->number_text_separation;
799 line_number_multiple = e->line_number_multiple;
800 line_number_indent = e->line_number_indent;
801 no_number_count = e->no_number_count;
802 tab_char = e->tab_char;
803 leader_char = e->leader_char;
804 hyphenation_flags = e->hyphenation_flags;
806 prev_fontno = e->prev_fontno;
809 prev_family = e->prev_family;
812 widow_control = e->widow_control;
813 #endif /* WIDOW_CONTROL */
814 hyphen_line_max = e->hyphen_line_max;
815 hyphen_line_count = 0;
816 hyphenation_space = e->hyphenation_space;
817 hyphenation_margin = e->hyphenation_margin;
819 ignore_next_eol = e->ignore_next_eol;
820 emitted_node = e->emitted_node;
821 glyph_color= e->glyph_color;
822 prev_glyph_color = e->prev_glyph_color;
823 fill_color = e->fill_color;
824 prev_fill_color = e->prev_fill_color;
827 environment::~environment()
830 delete_node_list(line);
831 delete_node_list(numbering_nodes);
834 hunits environment::get_input_line_position()
838 n = -input_line_start;
840 n = width_total - input_line_start;
846 void environment::set_input_line_position(hunits n)
848 input_line_start = line == 0 ? -n : width_total - n;
850 input_line_start += tab_width;
853 hunits environment::get_line_length()
858 hunits environment::get_saved_line_length()
861 return target_text_length + saved_indent;
866 vunits environment::get_vertical_spacing()
868 return vertical_spacing;
871 vunits environment::get_post_vertical_spacing()
873 return post_vertical_spacing;
876 int environment::get_line_spacing()
881 vunits environment::total_post_vertical_spacing()
883 vunits tem(post_vertical_spacing);
884 if (line_spacing > 1)
885 tem += (line_spacing - 1)*vertical_spacing;
889 int environment::get_bold()
891 return get_bold_fontno(fontno);
894 hunits environment::get_digit_width()
896 return env_digit_width(this);
899 int environment::get_adjust_mode()
904 int environment::get_fill()
909 hunits environment::get_indent()
914 hunits environment::get_saved_indent()
918 else if (have_temporary_indent)
919 return temporary_indent;
924 hunits environment::get_temporary_indent()
926 return temporary_indent;
929 hunits environment::get_title_length()
934 node *environment::get_prev_char()
936 for (node *n = current_tab ? tab_contents : line; n; n = n->next) {
937 node *last = n->last_char_node();
944 hunits environment::get_prev_char_width()
946 node *last = get_prev_char();
949 return last->width();
952 hunits environment::get_prev_char_skew()
954 node *last = get_prev_char();
960 vunits environment::get_prev_char_height()
962 node *last = get_prev_char();
966 last->vertical_extent(&min, &max);
970 vunits environment::get_prev_char_depth()
972 node *last = get_prev_char();
976 last->vertical_extent(&min, &max);
980 hunits environment::get_text_length()
982 hunits n = line == 0 ? H0 : width_total;
988 hunits environment::get_prev_text_length()
990 return prev_text_length;
994 static int sb_reg_contents = 0;
995 static int st_reg_contents = 0;
996 static int ct_reg_contents = 0;
997 static int rsb_reg_contents = 0;
998 static int rst_reg_contents = 0;
999 static int skw_reg_contents = 0;
1000 static int ssc_reg_contents = 0;
1002 void environment::width_registers()
1004 // this is used to implement \w; it sets the st, sb, ct registers
1005 vunits min = 0, max = 0, cur = 0;
1006 int character_type = 0;
1007 ssc_reg_contents = line ? line->subscript_correction().to_units() : 0;
1008 skw_reg_contents = line ? line->skew().to_units() : 0;
1009 line = reverse_node_list(line);
1010 vunits real_min = V0;
1011 vunits real_max = V0;
1013 for (node *tem = line; tem; tem = tem->next) {
1014 tem->vertical_extent(&v1, &v2);
1021 if ((cur += tem->vertical_width()) < min)
1025 character_type |= tem->character_type();
1027 line = reverse_node_list(line);
1028 st_reg_contents = -min.to_units();
1029 sb_reg_contents = -max.to_units();
1030 rst_reg_contents = -real_min.to_units();
1031 rsb_reg_contents = -real_max.to_units();
1032 ct_reg_contents = character_type;
1035 node *environment::extract_output_line()
1044 /* environment related requests */
1046 void environment_switch()
1048 int pop = 0; // 1 means pop, 2 means pop but no error message on underflow
1049 if (curenv->is_dummy())
1050 error("can't switch environments when current environment is dummy");
1051 else if (!has_arg())
1055 if (!tok.delimiter()) {
1056 // It looks like a number.
1058 if (get_integer(&n)) {
1059 if (n >= 0 && n < NENVIRONMENTS) {
1060 env_stack = new env_list(curenv, env_stack);
1061 if (env_table[n] == 0)
1062 env_table[n] = new environment(i_to_a(n));
1063 curenv = env_table[n];
1072 nm = get_long_name(1);
1076 if (!nm.is_null()) {
1077 environment *e = (environment *)env_dictionary.lookup(nm);
1079 e = new environment(nm);
1080 (void)env_dictionary.lookup(nm, e);
1082 env_stack = new env_list(curenv, env_stack);
1087 if (env_stack == 0) {
1089 error("environment stack underflow");
1092 curenv = env_stack->env;
1093 env_list *tem = env_stack;
1094 env_stack = env_stack->next;
1101 void environment_copy()
1106 if (!tok.delimiter()) {
1107 // It looks like a number.
1109 if (get_integer(&n)) {
1110 if (n >= 0 && n < NENVIRONMENTS)
1117 nm = get_long_name(1);
1118 if (!e && !nm.is_null())
1119 e = (environment *)env_dictionary.lookup(nm);
1121 error("No environment to copy from");
1129 static symbol P_symbol("P");
1133 symbol s = get_name();
1135 if (s.is_null() || s == P_symbol) {
1140 for (const char *p = s.contents(); p != 0 && *p != 0; p++)
1147 curenv->set_font(atoi(s.contents()));
1149 curenv->set_font(s);
1153 void family_change()
1155 symbol s = get_name();
1156 curenv->set_family(s);
1163 if (has_arg() && get_number(&n, 'z', curenv->get_requested_point_size())) {
1166 curenv->set_size(n);
1167 curenv->add_html_tag(1, ".ps", n);
1170 curenv->set_size(0);
1174 void override_sizes()
1177 int *sizes = new int[n];
1179 char *buf = read_string();
1182 char *p = strtok(buf, " \t");
1187 switch (sscanf(p, "%d-%d", &lower, &upper)) {
1192 if (lower <= upper && lower >= 0)
1196 warning(WARN_RANGE, "bad size range `%1'", p);
1200 int *old_sizes = sizes;
1201 sizes = new int[n*2];
1202 memcpy(sizes, old_sizes, n*sizeof(int));
1210 p = strtok(0, " \t");
1212 font_size::init_size_table(sizes);
1218 if (get_integer(&n)) {
1219 curenv->space_size = n;
1220 if (has_arg() && get_integer(&n))
1221 curenv->sentence_space_size = n;
1223 curenv->sentence_space_size = curenv->space_size;
1230 while (!tok.newline() && !tok.eof())
1235 curenv->add_html_tag(1, ".fi");
1236 curenv->add_html_tag(0, ".br");
1242 while (!tok.newline() && !tok.eof())
1247 curenv->add_html_tag(1, ".nf");
1248 curenv->add_html_tag(0, ".br");
1249 curenv->add_html_tag(0, ".po", topdiv->get_page_offset().to_units());
1256 if (!has_arg() || !get_integer(&n))
1260 while (!tok.newline() && !tok.eof())
1264 curenv->right_justify_lines = 0;
1265 curenv->center_lines = n;
1266 curenv->add_html_tag(1, ".ce", n);
1270 void right_justify()
1273 if (!has_arg() || !get_integer(&n))
1277 while (!tok.newline() && !tok.eof())
1281 curenv->center_lines = 0;
1282 curenv->right_justify_lines = n;
1283 curenv->add_html_tag(1, ".rj", n);
1290 if (has_arg() && get_hunits(&temp, 'm', curenv->line_length)) {
1292 warning(WARN_RANGE, "bad line length %1u", temp.to_units());
1297 temp = curenv->prev_line_length;
1298 curenv->prev_line_length = curenv->line_length;
1299 curenv->line_length = temp;
1300 curenv->add_html_tag(1, ".ll", temp.to_units());
1307 if (has_arg() && get_hunits(&temp, 'm', curenv->title_length)) {
1309 warning(WARN_RANGE, "bad title length %1u", temp.to_units());
1314 temp = curenv->prev_title_length;
1315 curenv->prev_title_length = curenv->title_length;
1316 curenv->title_length = temp;
1320 void vertical_spacing()
1323 if (has_arg() && get_vunits(&temp, 'p', curenv->vertical_spacing)) {
1325 warning(WARN_RANGE, "vertical spacing must be greater than 0");
1330 temp = curenv->prev_vertical_spacing;
1331 curenv->prev_vertical_spacing = curenv->vertical_spacing;
1332 curenv->vertical_spacing = temp;
1336 void post_vertical_spacing()
1339 if (has_arg() && get_vunits(&temp, 'p', curenv->post_vertical_spacing)) {
1342 "post vertical spacing must be greater than or equal to 0");
1347 temp = curenv->prev_post_vertical_spacing;
1348 curenv->prev_post_vertical_spacing = curenv->post_vertical_spacing;
1349 curenv->post_vertical_spacing = temp;
1356 if (has_arg() && get_integer(&temp)) {
1358 warning(WARN_RANGE, "value %1 out of range: interpreted as 1", temp);
1363 temp = curenv->prev_line_spacing;
1364 curenv->prev_line_spacing = curenv->line_spacing;
1365 curenv->line_spacing = temp;
1372 if (has_arg() && get_hunits(&temp, 'm', curenv->indent)) {
1374 warning(WARN_RANGE, "indent cannot be negative");
1379 temp = curenv->prev_indent;
1380 while (!tok.newline() && !tok.eof())
1384 curenv->have_temporary_indent = 0;
1385 curenv->prev_indent = curenv->indent;
1386 curenv->indent = temp;
1388 curenv->add_html_tag(1, ".in", temp.to_units());
1392 void temporary_indent()
1396 if (!get_hunits(&temp, 'm', curenv->get_indent()))
1398 while (!tok.newline() && !tok.eof())
1403 warning(WARN_RANGE, "total indent cannot be negative");
1407 curenv->temporary_indent = temp;
1408 curenv->have_temporary_indent = 1;
1409 curenv->add_html_tag(1, ".ti", temp.to_units());
1414 node *do_underline_special(int underline_spaces)
1417 m.append_str("x u ");
1418 m.append(underline_spaces + '0');
1419 return new special_node(m, 1);
1422 void do_underline(int underline_spaces)
1425 if (!has_arg() || !get_integer(&n))
1428 if (curenv->underline_lines > 0) {
1429 curenv->prev_fontno = curenv->fontno;
1430 curenv->fontno = curenv->pre_underline_fontno;
1431 if (underline_spaces) {
1432 curenv->underline_spaces = 0;
1433 curenv->add_node(do_underline_special(0));
1436 curenv->underline_lines = 0;
1439 curenv->underline_lines = n;
1440 curenv->pre_underline_fontno = curenv->fontno;
1441 curenv->fontno = get_underline_fontno();
1442 if (underline_spaces) {
1443 curenv->underline_spaces = 1;
1444 curenv->add_node(do_underline_special(1));
1450 void continuous_underline()
1462 curenv->control_char = '.';
1465 error("bad control character");
1467 curenv->control_char = tok.ch();
1472 void no_break_control_char()
1474 curenv->no_break_control_char = '\'';
1477 error("bad control character");
1479 curenv->no_break_control_char = tok.ch();
1484 void margin_character()
1488 charinfo *ci = tok.get_char();
1490 // Call tok.next() only after making the node so that
1491 // .mc \s+9\(br\s0 works.
1492 node *nd = curenv->make_char_node(ci);
1495 delete curenv->margin_character_node;
1496 curenv->margin_character_node = nd;
1497 curenv->margin_character_flags = (MARGIN_CHARACTER_ON
1498 |MARGIN_CHARACTER_NEXT);
1500 if (has_arg() && get_hunits(&d, 'm'))
1501 curenv->margin_character_distance = d;
1505 check_missing_character();
1506 curenv->margin_character_flags &= ~MARGIN_CHARACTER_ON;
1507 if (curenv->margin_character_flags == 0) {
1508 delete curenv->margin_character_node;
1509 curenv->margin_character_node = 0;
1517 delete_node_list(curenv->numbering_nodes);
1518 curenv->numbering_nodes = 0;
1521 for (int i = '9'; i >= '0'; i--) {
1522 node *tem = make_node(charset_table[i], curenv);
1530 curenv->numbering_nodes = nd;
1531 curenv->line_number_digit_width = env_digit_width(curenv);
1533 if (!tok.delimiter()) {
1534 if (get_integer(&n, next_line_number)) {
1535 next_line_number = n;
1536 if (next_line_number < 0) {
1537 warning(WARN_RANGE, "negative line number");
1538 next_line_number = 0;
1543 while (!tok.space() && !tok.newline() && !tok.eof())
1546 if (!tok.delimiter()) {
1547 if (get_integer(&n)) {
1549 warning(WARN_RANGE, "negative or zero line number multiple");
1552 curenv->line_number_multiple = n;
1556 while (!tok.space() && !tok.newline() && !tok.eof())
1559 if (!tok.delimiter()) {
1560 if (get_integer(&n))
1561 curenv->number_text_separation = n;
1564 while (!tok.space() && !tok.newline() && !tok.eof())
1566 if (has_arg() && !tok.delimiter() && get_integer(&n))
1567 curenv->line_number_indent = n;
1577 if (has_arg() && get_integer(&n))
1578 curenv->no_number_count = n > 0 ? n : 0;
1580 curenv->no_number_count = 1;
1586 curenv->hyphenation_flags = 0;
1590 void hyphenate_request()
1593 if (has_arg() && get_integer(&n))
1594 curenv->hyphenation_flags = n;
1596 curenv->hyphenation_flags = 1;
1602 curenv->hyphen_indicator_char = get_optional_char();
1606 void hyphen_line_max_request()
1609 if (has_arg() && get_integer(&n))
1610 curenv->hyphen_line_max = n;
1612 curenv->hyphen_line_max = -1;
1616 void environment::interrupt()
1619 add_node(new transparent_dummy_node);
1624 void environment::newline()
1626 if (underline_lines > 0) {
1627 if (--underline_lines == 0) {
1628 prev_fontno = fontno;
1629 fontno = pre_underline_fontno;
1630 if (underline_spaces) {
1631 underline_spaces = 0;
1632 add_node(do_underline_special(0));
1640 // strip trailing spaces
1641 while (line != 0 && line->discardable()) {
1642 width_total -= line->width();
1643 space_total -= line->nspaces();
1648 node *to_be_output = 0;
1649 hunits to_be_output_width;
1650 prev_line_interrupted = 0;
1653 else if (interrupted) {
1655 // see environment::final_break
1656 prev_line_interrupted = exit_started ? 2 : 1;
1658 else if (center_lines > 0) {
1660 hunits x = target_text_length - width_total;
1662 saved_indent += x/2;
1663 to_be_output = line;
1665 node *n = make_html_tag("eol.ce");
1666 n->next = to_be_output;
1669 to_be_output_width = width_total;
1672 else if (right_justify_lines > 0) {
1673 --right_justify_lines;
1674 hunits x = target_text_length - width_total;
1677 to_be_output = line;
1678 to_be_output_width = width_total;
1684 to_be_output = line;
1685 to_be_output_width = width_total;
1688 input_line_start = line == 0 ? H0 : width_total;
1690 if (is_html && !fill) {
1691 if (curdiv == topdiv) {
1692 node *n = make_html_tag("eol");
1694 n->next = to_be_output;
1698 output_line(to_be_output, to_be_output_width);
1699 hyphen_line_count = 0;
1701 if (input_trap_count > 0) {
1702 if (!(continued_input_trap && prev_line_interrupted))
1703 if (--input_trap_count == 0)
1704 spring_trap(input_trap);
1708 void environment::output_line(node *n, hunits width)
1710 prev_text_length = width;
1711 if (margin_character_flags) {
1712 hunits d = line_length + margin_character_distance - saved_indent - width;
1714 n = new hmotion_node(d, get_fill_color(), n);
1717 margin_character_flags &= ~MARGIN_CHARACTER_NEXT;
1719 if (!margin_character_flags) {
1720 tem = margin_character_node;
1721 margin_character_node = 0;
1724 tem = margin_character_node->copy();
1727 width += tem->width();
1731 node *tem = n->next;
1736 if (!saved_indent.is_zero())
1737 nn = new hmotion_node(saved_indent, get_fill_color(), nn);
1738 width += saved_indent;
1739 if (no_number_count > 0)
1741 else if (numbering_nodes) {
1742 hunits w = (line_number_digit_width
1743 *(3+line_number_indent+number_text_separation));
1744 if (next_line_number % line_number_multiple != 0)
1745 nn = new hmotion_node(w, get_fill_color(), nn);
1748 nn = new hmotion_node(number_text_separation * line_number_digit_width,
1749 get_fill_color(), nn);
1750 x -= number_text_separation*line_number_digit_width;
1752 sprintf(buf, "%3d", next_line_number);
1753 for (char *p = strchr(buf, '\0') - 1; p >= buf && *p != ' '; --p) {
1754 node *gn = numbering_nodes;
1755 for (int count = *p - '0'; count > 0; count--)
1762 nn = new hmotion_node(x, get_fill_color(), nn);
1767 output(nn, !fill, vertical_spacing, total_post_vertical_spacing(), width);
1770 void environment::start_line()
1774 line = new line_start_node;
1775 if (have_temporary_indent) {
1776 saved_indent = temporary_indent;
1777 have_temporary_indent = 0;
1780 saved_indent = indent;
1781 target_text_length = line_length - saved_indent;
1786 hunits environment::get_hyphenation_space()
1788 return hyphenation_space;
1791 void hyphenation_space_request()
1794 if (get_hunits(&n, 'm')) {
1796 warning(WARN_RANGE, "hyphenation space cannot be negative");
1799 curenv->hyphenation_space = n;
1804 hunits environment::get_hyphenation_margin()
1806 return hyphenation_margin;
1809 void hyphenation_margin_request()
1812 if (get_hunits(&n, 'm')) {
1814 warning(WARN_RANGE, "hyphenation margin cannot be negative");
1817 curenv->hyphenation_margin = n;
1822 breakpoint *environment::choose_breakpoint()
1824 hunits x = width_total;
1825 int s = space_total;
1827 breakpoint *best_bp = 0; // the best breakpoint so far
1828 int best_bp_fits = 0;
1832 breakpoint *bp = n->get_breakpoints(x, s);
1834 if (bp->width <= target_text_length) {
1835 if (!bp->hyphenated) {
1836 breakpoint *tem = bp->next;
1839 breakpoint *tem1 = tem;
1844 // Decide whether to use the hyphenated breakpoint.
1845 && (hyphen_line_max < 0
1846 // Only choose the hyphenated breakpoint if it would not
1847 // exceed the maximum number of consecutive hyphenated
1849 || hyphen_line_count + 1 <= hyphen_line_max)
1850 && !(adjust_mode == ADJUST_BOTH
1851 // Don't choose the hyphenated breakpoint if the line
1852 // can be justified by adding no more than
1853 // hyphenation_space to any word space.
1855 && (((target_text_length - bp->width
1856 + (bp->nspaces - 1)*hresolution)/bp->nspaces)
1857 <= hyphenation_space))
1858 // Don't choose the hyphenated breakpoint if the line
1859 // is no more than hyphenation_margin short.
1860 : target_text_length - bp->width <= hyphenation_margin)) {
1869 if ((adjust_mode == ADJUST_BOTH
1870 ? hyphenation_space == H0
1871 : hyphenation_margin == H0)
1872 && (hyphen_line_max < 0
1873 || hyphen_line_count + 1 <= hyphen_line_max)) {
1874 // No need to consider a non-hyphenated breakpoint.
1879 // It fits but it's hyphenated.
1880 if (!best_bp_fits) {
1888 breakpoint *tem = bp;
1905 output_warning(WARN_BREAK, "can't break line");
1911 void environment::hyphenate_line(int start_here)
1915 hyphenation_type prev_type = line->get_hyphenation_type();
1920 for (startp = &line->next; *startp != 0; startp = &(*startp)->next) {
1921 hyphenation_type this_type = (*startp)->get_hyphenation_type();
1922 if (prev_type == HYPHEN_BOUNDARY && this_type == HYPHEN_MIDDLE)
1924 prev_type = this_type;
1928 node *tem = *startp;
1933 } while (tem != 0 && tem->get_hyphenation_type() == HYPHEN_MIDDLE);
1934 int inhibit = (tem != 0 && tem->get_hyphenation_type() == HYPHEN_INHIBIT);
1936 hyphen_list *sl = 0;
1939 while (tem != end) {
1940 sl = tem->get_hyphen_list(sl);
1943 tem1->next = forward;
1947 // this is for characters like hyphen and emdash
1949 for (hyphen_list *h = sl; h; h = h->next) {
1950 h->breakable = (prev_code != 0
1952 && h->next->hyphenation_code != 0);
1953 prev_code = h->hyphenation_code;
1956 if (hyphenation_flags != 0
1958 // this may not be right if we have extra space on this line
1959 && !((hyphenation_flags & HYPHEN_LAST_LINE)
1960 && (curdiv->distance_to_next_trap()
1961 <= vertical_spacing + total_post_vertical_spacing()))
1963 hyphenate(sl, hyphenation_flags);
1964 while (forward != 0) {
1965 node *tem1 = forward;
1966 forward = forward->next;
1968 tem = tem1->add_self(tem, &sl);
1973 static node *node_list_reverse(node *n)
1985 static void distribute_space(node *n, int nspaces, hunits desired_space,
1986 int force_reverse = 0)
1988 static int reverse = 0;
1989 if (force_reverse || reverse)
1990 n = node_list_reverse(n);
1991 if (!force_reverse && nspaces > 0 && spread_limit >= 0
1992 && desired_space.to_units() > 0) {
1993 hunits em = curenv->get_size();
1994 double Ems = (double)desired_space.to_units() / nspaces
1995 / (em.is_zero() ? hresolution : em.to_units());
1996 if (Ems > spread_limit)
1997 output_warning(WARN_BREAK, "spreading %1m per space", Ems);
1999 for (node *tem = n; tem; tem = tem->next)
2000 tem->spread_space(&nspaces, &desired_space);
2001 if (force_reverse || reverse)
2002 (void)node_list_reverse(n);
2005 assert(desired_space.is_zero() && nspaces == 0);
2008 void environment::possibly_break_line(int start_here, int forced)
2010 if (!fill || current_tab || current_field || dummy)
2014 // When a macro follows a paragraph in fill mode, the
2015 // current line should not be empty.
2016 || (width_total - line->width()) > target_text_length)) {
2017 hyphenate_line(start_here);
2018 breakpoint *bp = choose_breakpoint();
2020 // we'll find one eventually
2024 while (*ndp != bp->nd)
2025 ndp = &(*ndp)->next;
2026 bp->nd->split(bp->index, &pre, &post);
2028 hunits extra_space_width = H0;
2029 switch(adjust_mode) {
2031 if (bp->nspaces != 0)
2032 extra_space_width = target_text_length - bp->width;
2033 else if (bp->width > 0 && target_text_length > 0
2034 && target_text_length > bp->width)
2035 output_warning(WARN_BREAK, "cannot adjust line");
2038 saved_indent += (target_text_length - bp->width)/2;
2041 saved_indent += target_text_length - bp->width;
2044 distribute_space(pre, bp->nspaces, extra_space_width);
2045 hunits output_width = bp->width + extra_space_width;
2046 input_line_start -= output_width;
2048 hyphen_line_count++;
2050 hyphen_line_count = 0;
2054 node *first_non_discardable = 0;
2056 for (tem = line; tem != 0; tem = tem->next)
2057 if (!tem->discardable())
2058 first_non_discardable = tem;
2059 node *to_be_discarded;
2060 if (first_non_discardable) {
2061 to_be_discarded = first_non_discardable->next;
2062 first_non_discardable->next = 0;
2063 for (tem = line; tem != 0; tem = tem->next) {
2064 width_total += tem->width();
2065 space_total += tem->nspaces();
2071 to_be_discarded = line;
2074 // Do output_line() here so that line will be 0 iff the
2075 // the environment will be empty.
2076 output_line(pre, output_width);
2077 while (to_be_discarded != 0) {
2078 tem = to_be_discarded;
2079 to_be_discarded = to_be_discarded->next;
2080 input_line_start -= tem->width();
2084 if (have_temporary_indent) {
2085 saved_indent = temporary_indent;
2086 have_temporary_indent = 0;
2089 saved_indent = indent;
2090 target_text_length = line_length - saved_indent;
2096 Do the break at the end of input after the end macro (if any).
2098 Unix troff behaves as follows: if the last line is
2102 it will output foo on the current page, and bar on the next page;
2111 everything will be output on the current page. This behaviour must be
2114 The problem is that some macro packages rely on this. For example,
2115 the ATK macros have an end macro that emits \c if it needs to print a
2116 table of contents but doesn't do a 'bp in the end macro; instead the
2117 'bp is done in the bottom of page trap. This works with Unix troff,
2118 provided that the current environment is not empty at the end of the
2121 The following will make macro packages that do that sort of thing work
2122 even if the current environment is empty at the end of the input file.
2123 If the last input line used \c and this line occurred in the end macro,
2124 then we'll force everything out on the current page, but we'll make
2125 sure that the environment isn't empty so that we won't exit at the
2126 bottom of this page.
2129 void environment::final_break()
2131 if (prev_line_interrupted == 2) {
2133 add_node(new transparent_dummy_node);
2140 * add_html_tag - emits a special html-tag: to help post-grohtml understand
2141 * the key troff commands
2144 void environment::add_html_tag(int force, const char *name)
2146 if (!force && (curdiv != topdiv))
2151 * need to emit tag for post-grohtml
2152 * but we check to see whether we can emit specials
2154 if (curdiv == topdiv && topdiv->before_first_page)
2155 topdiv->begin_page();
2156 macro *m = new macro;
2157 m->append_str("html-tag:");
2158 for (const char *p = name; *p; p++)
2159 if (!invalid_input_char((unsigned char)*p))
2161 curdiv->output(new special_node(*m), 1, 0, 0, 0);
2162 if (strcmp(name, ".nf") == 0)
2163 curenv->ignore_next_eol = 1;
2168 * add_html_tag - emits a special html-tag: to help post-grohtml understand
2169 * the key troff commands, it appends a string representation
2173 void environment::add_html_tag(int force, const char *name, int i)
2175 if (!force && (curdiv != topdiv))
2180 * need to emit tag for post-grohtml
2181 * but we check to see whether we can emit specials
2183 if (curdiv == topdiv && topdiv->before_first_page)
2184 topdiv->begin_page();
2185 macro *m = new macro;
2186 m->append_str("html-tag:");
2187 for (const char *p = name; *p; p++)
2188 if (!invalid_input_char((unsigned char)*p))
2192 node *n = new special_node(*m);
2193 curdiv->output(n, 1, 0, 0, 0);
2198 * add_html_tag_tabs - emits the tab settings for post-grohtml
2201 void environment::add_html_tag_tabs(int force)
2203 if (!force && (curdiv != topdiv))
2208 * need to emit tag for post-grohtml
2209 * but we check to see whether we can emit specials
2211 if (curdiv == topdiv && topdiv->before_first_page)
2212 topdiv->begin_page();
2213 macro *m = new macro;
2216 m->append_str("html-tag:.ta ");
2218 t = curenv->tabs.distance_to_next_tab(l, &d);
2222 m->append_str(" L ");
2223 m->append_int(l.to_units());
2226 m->append_str(" C ");
2227 m->append_int(l.to_units());
2230 m->append_str(" R ");
2231 m->append_int(l.to_units());
2236 } while ((t != TAB_NONE) && (l < get_line_length()));
2237 curdiv->output(new special_node(*m), 1, 0, 0, 0);
2241 node *environment::make_html_tag(const char *name, int i)
2245 * need to emit tag for post-grohtml
2246 * but we check to see whether we can emit specials
2248 if (curdiv == topdiv && topdiv->before_first_page)
2249 topdiv->begin_page();
2250 macro *m = new macro;
2251 m->append_str("html-tag:");
2252 for (const char *p = name; *p; p++)
2253 if (!invalid_input_char((unsigned char)*p))
2257 return new special_node(*m);
2262 node *environment::make_html_tag(const char *name)
2266 * need to emit tag for post-grohtml
2267 * but we check to see whether we can emit specials
2269 if (curdiv == topdiv && topdiv->before_first_page)
2270 topdiv->begin_page();
2271 macro *m = new macro;
2272 m->append_str("html-tag:");
2273 for (const char *p = name; *p; p++)
2274 if (!invalid_input_char((unsigned char)*p))
2276 return new special_node(*m);
2281 void environment::do_break(int spread)
2283 if (curdiv == topdiv && topdiv->before_first_page) {
2284 topdiv->begin_page();
2290 // this is so that hyphenation works
2291 line = new space_node(H0, get_fill_color(), line);
2293 possibly_break_line(0, spread);
2295 while (line != 0 && line->discardable()) {
2296 width_total -= line->width();
2297 space_total -= line->nspaces();
2303 input_line_start = H0;
2306 switch (adjust_mode) {
2308 saved_indent += (target_text_length - width_total)/2;
2311 saved_indent += target_text_length - width_total;
2317 output_line(tem, width_total);
2318 hyphen_line_count = 0;
2320 prev_line_interrupted = 0;
2321 #ifdef WIDOW_CONTROL
2323 output_pending_lines();
2324 #endif /* WIDOW_CONTROL */
2327 int environment::is_empty()
2329 return !current_tab && line == 0 && pending_lines == 0;
2332 void do_break_request(int spread)
2334 while (!tok.newline() && !tok.eof())
2337 curenv->do_break(spread);
2338 curenv->add_html_tag(0, ".br");
2343 void break_request()
2345 do_break_request(0);
2348 void break_spread_request()
2350 do_break_request(1);
2355 if (curdiv == topdiv && topdiv->before_first_page) {
2356 handle_initial_title();
2360 hunits part_width[3];
2361 part[0] = part[1] = part[2] = 0;
2362 environment env(curenv);
2363 environment *oldenv = curenv;
2365 read_title_parts(part, part_width);
2367 curenv->size = env.size;
2368 curenv->prev_size = env.prev_size;
2369 curenv->requested_size = env.requested_size;
2370 curenv->prev_requested_size = env.prev_requested_size;
2371 curenv->char_height = env.char_height;
2372 curenv->char_slant = env.char_slant;
2373 curenv->fontno = env.fontno;
2374 curenv->prev_fontno = env.prev_fontno;
2375 curenv->glyph_color = env.glyph_color;
2376 curenv->prev_glyph_color = env.prev_glyph_color;
2377 curenv->fill_color = env.fill_color;
2378 curenv->prev_fill_color = env.prev_fill_color;
2387 hunits title_length(curenv->title_length);
2388 hunits f = title_length - part_width[1];
2390 n = new hmotion_node(f2 - part_width[2], curenv->get_fill_color(), n);
2398 n = new hmotion_node(f - f2 - part_width[0], curenv->get_fill_color(), n);
2406 curenv->output_title(n, !curenv->fill, curenv->vertical_spacing,
2407 curenv->total_post_vertical_spacing(), title_length);
2408 curenv->hyphen_line_count = 0;
2414 curenv->adjust_mode |= 1;
2418 curenv->adjust_mode = ADJUST_LEFT;
2421 curenv->adjust_mode = ADJUST_RIGHT;
2424 curenv->adjust_mode = ADJUST_CENTER;
2428 curenv->adjust_mode = ADJUST_BOTH;
2432 if (get_integer(&n)) {
2434 warning(WARN_RANGE, "negative adjustment mode");
2436 curenv->adjust_mode = 5;
2437 warning(WARN_RANGE, "adjustment mode `%1' out of range", n);
2440 curenv->adjust_mode = n;
2449 curenv->adjust_mode &= ~1;
2453 void do_input_trap(int continued)
2455 curenv->input_trap_count = 0;
2457 curenv->continued_input_trap = 1;
2459 if (has_arg() && get_integer(&n)) {
2462 "number of lines for input trap must be greater than zero");
2464 symbol s = get_name(1);
2466 curenv->input_trap_count = n;
2467 curenv->input_trap = s;
2479 void input_trap_continued()
2486 // must not be R or C or L or a legitimate part of a number expression
2487 const char TAB_REPEAT_CHAR = 'T';
2493 tab(hunits, tab_type);
2494 enum { BLOCK = 1024 };
2495 static tab *free_list;
2496 void *operator new(size_t);
2497 void operator delete(void *);
2500 tab *tab::free_list = 0;
2502 void *tab::operator new(size_t n)
2504 assert(n == sizeof(tab));
2506 free_list = (tab *)new char[sizeof(tab)*BLOCK];
2507 for (int i = 0; i < BLOCK - 1; i++)
2508 free_list[i].next = free_list + i + 1;
2509 free_list[BLOCK-1].next = 0;
2512 free_list = (tab *)(free_list->next);
2518 /* cfront can't cope with this. */
2521 void tab::operator delete(void *p)
2524 ((tab *)p)->next = free_list;
2525 free_list = (tab *)p;
2529 tab::tab(hunits x, tab_type t) : next(0), pos(x), type(t)
2533 tab_stops::tab_stops(hunits distance, tab_type type)
2536 repeated_list = new tab(distance, type);
2539 tab_stops::~tab_stops()
2544 tab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance)
2548 return distance_to_next_tab(curpos, distance, &nextpos);
2551 tab_type tab_stops::distance_to_next_tab(hunits curpos, hunits *distance,
2556 for (tem = initial_list; tem && tem->pos <= curpos; tem = tem->next)
2559 *distance = tem->pos - curpos;
2560 *nextpos = tem->pos;
2563 if (repeated_list == 0)
2565 hunits base = lastpos;
2567 for (tem = repeated_list; tem && tem->pos + base <= curpos; tem = tem->next)
2570 *distance = tem->pos + base - curpos;
2571 *nextpos = tem->pos + base;
2574 assert(lastpos > 0);
2580 const char *tab_stops::to_string()
2582 static char *buf = 0;
2583 static int buf_size = 0;
2584 // figure out a maximum on the amount of space we can need
2587 for (p = initial_list; p; p = p->next)
2589 for (p = repeated_list; p; p = p->next)
2591 // (10 for digits + 1 for u + 1 for 'C' or 'R') + 2 for ' &' + 1 for '\0'
2592 int need = count*12 + 3;
2593 if (buf == 0 || need > buf_size) {
2597 buf = new char[buf_size];
2600 for (p = initial_list; p; p = p->next) {
2601 strcpy(ptr, i_to_a(p->pos.to_units()));
2602 ptr = strchr(ptr, '\0');
2620 *ptr++ = TAB_REPEAT_CHAR;
2621 for (p = repeated_list; p; p = p->next) {
2622 strcpy(ptr, i_to_a(p->pos.to_units()));
2623 ptr = strchr(ptr, '\0');
2644 tab_stops::tab_stops() : initial_list(0), repeated_list(0)
2648 tab_stops::tab_stops(const tab_stops &ts)
2649 : initial_list(0), repeated_list(0)
2651 tab **p = &initial_list;
2652 tab *t = ts.initial_list;
2654 *p = new tab(t->pos, t->type);
2659 t = ts.repeated_list;
2661 *p = new tab(t->pos, t->type);
2667 void tab_stops::clear()
2669 while (initial_list) {
2670 tab *tem = initial_list;
2671 initial_list = initial_list->next;
2674 while (repeated_list) {
2675 tab *tem = repeated_list;
2676 repeated_list = repeated_list->next;
2681 void tab_stops::add_tab(hunits pos, tab_type type, int repeated)
2684 for (p = repeated ? &repeated_list : &initial_list; *p; p = &(*p)->next)
2686 *p = new tab(pos, type);
2690 void tab_stops::operator=(const tab_stops &ts)
2693 tab **p = &initial_list;
2694 tab *t = ts.initial_list;
2696 *p = new tab(t->pos, t->type);
2701 t = ts.repeated_list;
2703 *p = new tab(t->pos, t->type);
2712 hunits prev_pos = 0;
2717 if (tok.ch() == TAB_REPEAT_CHAR) {
2722 if (!get_hunits(&pos, 'm', prev_pos))
2724 tab_type type = TAB_LEFT;
2725 if (tok.ch() == 'C') {
2729 else if (tok.ch() == 'R') {
2733 else if (tok.ch() == 'L') {
2736 if (pos <= prev_pos && !first)
2738 "positions of tab stops must be strictly increasing");
2740 tabs.add_tab(pos, type, repeated);
2745 curenv->tabs = tabs;
2746 curenv->add_html_tag_tabs(1);
2750 const char *environment::get_tabs()
2752 return tabs.to_string();
2756 tab_stops saved_tabs;
2760 saved_tabs = curenv->tabs;
2766 curenv->tabs = saved_tabs;
2771 tab_type environment::distance_to_next_tab(hunits *distance)
2774 ? curenv->tabs.distance_to_next_tab(get_text_length(), distance)
2775 : curenv->tabs.distance_to_next_tab(get_input_line_position(), distance);
2778 tab_type environment::distance_to_next_tab(hunits *distance, hunits *leftpos)
2781 ? curenv->tabs.distance_to_next_tab(get_text_length(), distance, leftpos)
2782 : curenv->tabs.distance_to_next_tab(get_input_line_position(), distance,
2786 void field_characters()
2788 field_delimiter_char = get_optional_char();
2789 if (field_delimiter_char)
2790 padding_indicator_char = get_optional_char();
2792 padding_indicator_char = 0;
2796 void line_tabs_request()
2799 if (has_arg() && get_integer(&n))
2800 curenv->line_tabs = n != 0;
2802 curenv->line_tabs = 1;
2806 int environment::get_line_tabs()
2811 void environment::wrap_up_tab()
2818 switch (current_tab) {
2820 tab_amount = tab_distance - tab_width;
2821 line = make_tab_node(tab_amount, line);
2824 tab_amount = tab_distance - tab_width/2;
2825 line = make_tab_node(tab_amount, line);
2832 width_total += tab_amount;
2833 width_total += tab_width;
2834 if (current_field) {
2835 if (tab_precedes_field) {
2836 pre_field_width += tab_amount;
2837 tab_precedes_field = 0;
2839 field_distance -= tab_amount;
2840 field_spaces += tab_field_spaces;
2842 if (tab_contents != 0) {
2844 for (tem = tab_contents; tem->next != 0; tem = tem->next)
2847 line = tab_contents;
2849 tab_field_spaces = 0;
2853 current_tab = TAB_NONE;
2856 node *environment::make_tab_node(hunits d, node *next)
2858 if (leader_node != 0 && d < 0) {
2859 error("motion generated by leader cannot be negative");
2864 return new hmotion_node(d, 1, 0, get_fill_color(), next);
2865 node *n = new hline_node(d, leader_node, next);
2870 void environment::handle_tab(int is_leader)
2876 charinfo *ci = is_leader ? leader_char : tab_char;
2878 leader_node = ci ? make_char_node(ci) : 0;
2879 tab_type t = distance_to_next_tab(&d, &abs);
2884 add_node(make_tab_node(d));
2885 add_node(make_html_tag("tab L", abs.to_units()));
2888 add_node(make_html_tag("tab R", abs.to_units()));
2891 add_node(make_html_tag("tab C", abs.to_units()));
2900 tab_field_spaces = 0;
2903 void environment::start_field()
2905 assert(!current_field);
2907 if (distance_to_next_tab(&d) != TAB_NONE) {
2908 pre_field_width = get_text_length();
2912 tab_field_spaces = 0;
2913 for (node *p = line; p; p = p->next)
2918 tab_precedes_field = current_tab != TAB_NONE;
2921 error("zero field width");
2924 void environment::wrap_up_field()
2926 if (!current_tab && field_spaces == 0)
2928 hunits padding = field_distance - (get_text_length() - pre_field_width);
2929 if (current_tab && tab_field_spaces != 0) {
2930 hunits tab_padding = scale(padding,
2932 field_spaces + tab_field_spaces);
2933 padding -= tab_padding;
2934 distribute_space(tab_contents, tab_field_spaces, tab_padding, 1);
2935 tab_field_spaces = 0;
2936 tab_width += tab_padding;
2938 if (field_spaces != 0) {
2939 distribute_space(line, field_spaces, padding, 1);
2940 width_total += padding;
2942 // the start of the tab has been moved to the right by padding, so
2943 tab_distance -= padding;
2944 if (tab_distance <= H0) {
2945 // use the next tab stop instead
2946 current_tab = tabs.distance_to_next_tab(get_input_line_position()
2949 if (current_tab == TAB_NONE || current_tab == TAB_LEFT) {
2950 width_total += tab_width;
2951 if (current_tab == TAB_LEFT) {
2952 line = make_tab_node(tab_distance, line);
2953 width_total += tab_distance;
2954 current_tab = TAB_NONE;
2956 if (tab_contents != 0) {
2958 for (tem = tab_contents; tem->next != 0; tem = tem->next)
2961 line = tab_contents;
2973 void environment::add_padding()
2976 tab_contents = new space_node(H0, get_fill_color(), tab_contents);
2982 line = new space_node(H0, get_fill_color(), line);
2987 typedef int (environment::*INT_FUNCP)();
2988 typedef vunits (environment::*VUNITS_FUNCP)();
2989 typedef hunits (environment::*HUNITS_FUNCP)();
2990 typedef const char *(environment::*STRING_FUNCP)();
2992 class int_env_reg : public reg {
2995 int_env_reg(INT_FUNCP);
2996 const char *get_string();
2997 int get_value(units *val);
3000 class vunits_env_reg : public reg {
3003 vunits_env_reg(VUNITS_FUNCP f);
3004 const char *get_string();
3005 int get_value(units *val);
3009 class hunits_env_reg : public reg {
3012 hunits_env_reg(HUNITS_FUNCP f);
3013 const char *get_string();
3014 int get_value(units *val);
3017 class string_env_reg : public reg {
3020 string_env_reg(STRING_FUNCP);
3021 const char *get_string();
3024 int_env_reg::int_env_reg(INT_FUNCP f) : func(f)
3028 int int_env_reg::get_value(units *val)
3030 *val = (curenv->*func)();
3034 const char *int_env_reg::get_string()
3036 return i_to_a((curenv->*func)());
3039 vunits_env_reg::vunits_env_reg(VUNITS_FUNCP f) : func(f)
3043 int vunits_env_reg::get_value(units *val)
3045 *val = (curenv->*func)().to_units();
3049 const char *vunits_env_reg::get_string()
3051 return i_to_a((curenv->*func)().to_units());
3054 hunits_env_reg::hunits_env_reg(HUNITS_FUNCP f) : func(f)
3058 int hunits_env_reg::get_value(units *val)
3060 *val = (curenv->*func)().to_units();
3064 const char *hunits_env_reg::get_string()
3066 return i_to_a((curenv->*func)().to_units());
3069 string_env_reg::string_env_reg(STRING_FUNCP f) : func(f)
3073 const char *string_env_reg::get_string()
3075 return (curenv->*func)();
3078 class horizontal_place_reg : public general_reg {
3080 horizontal_place_reg();
3081 int get_value(units *);
3082 void set_value(units);
3085 horizontal_place_reg::horizontal_place_reg()
3089 int horizontal_place_reg::get_value(units *res)
3091 *res = curenv->get_input_line_position().to_units();
3095 void horizontal_place_reg::set_value(units n)
3097 curenv->set_input_line_position(hunits(n));
3100 const char *environment::get_font_family_string()
3102 return family->nm.contents();
3105 const char *environment::get_font_name_string()
3107 symbol f = get_font_name(fontno, this);
3108 return f.contents();
3111 const char *environment::get_name_string()
3113 return name.contents();
3116 // Convert a quantity in scaled points to ascii decimal fraction.
3118 const char *sptoa(int sp)
3121 assert(sizescale > 0);
3124 if (sp % sizescale == 0)
3125 return i_to_a(sp/sizescale);
3126 // See if 1/sizescale is exactly representable as a decimal fraction,
3127 // ie its only prime factors are 2 and 5.
3130 while ((n & 1) == 0) {
3135 while ((n % 5) == 0) {
3140 int decimal_point = power5 > power2 ? power5 : power2;
3141 if (decimal_point <= 10) {
3144 for (t = decimal_point - power2; --t >= 0;)
3146 for (t = decimal_point - power5; --t >= 0;)
3148 if (factor == 1 || sp <= INT_MAX/factor)
3149 return if_to_a(sp*factor, decimal_point);
3152 double s = double(sp)/double(sizescale);
3153 double factor = 10.0;
3155 int decimal_point = 0;
3157 double v = ceil(s*factor);
3162 } while (++decimal_point < 10);
3163 return if_to_a(int(val), decimal_point);
3166 const char *environment::get_point_size_string()
3168 return sptoa(curenv->get_point_size());
3171 const char *environment::get_requested_point_size_string()
3173 return sptoa(curenv->get_requested_point_size());
3176 #define init_int_env_reg(name, func) \
3177 number_reg_dictionary.define(name, new int_env_reg(&environment::func))
3179 #define init_vunits_env_reg(name, func) \
3180 number_reg_dictionary.define(name, new vunits_env_reg(&environment::func))
3182 #define init_hunits_env_reg(name, func) \
3183 number_reg_dictionary.define(name, new hunits_env_reg(&environment::func))
3185 #define init_string_env_reg(name, func) \
3186 number_reg_dictionary.define(name, new string_env_reg(&environment::func))
3188 void init_env_requests()
3190 init_request("it", input_trap);
3191 init_request("itc", input_trap_continued);
3192 init_request("ad", adjust);
3193 init_request("na", no_adjust);
3194 init_request("ev", environment_switch);
3195 init_request("evc", environment_copy);
3196 init_request("lt", title_length);
3197 init_request("ps", point_size);
3198 init_request("sizes", override_sizes);
3199 init_request("ft", font_change);
3200 init_request("fam", family_change);
3201 init_request("ss", space_size);
3202 init_request("fi", fill);
3203 init_request("nf", no_fill);
3204 init_request("ce", center);
3205 init_request("rj", right_justify);
3206 init_request("vs", vertical_spacing);
3207 init_request("ls", line_spacing);
3208 init_request("ll", line_length);
3209 init_request("in", indent);
3210 init_request("ti", temporary_indent);
3211 init_request("ul", underline);
3212 init_request("cu", continuous_underline);
3213 init_request("cc", control_char);
3214 init_request("c2", no_break_control_char);
3215 init_request("br", break_request);
3216 init_request("brp", break_spread_request);
3217 init_request("tl", title);
3218 init_request("ta", set_tabs);
3219 init_request("linetabs", line_tabs_request);
3220 init_request("fc", field_characters);
3221 init_request("mc", margin_character);
3222 init_request("nn", no_number);
3223 init_request("nm", number_lines);
3224 init_request("tc", tab_character);
3225 init_request("lc", leader_character);
3226 init_request("hy", hyphenate_request);
3227 init_request("hc", hyphen_char);
3228 init_request("nh", no_hyphenate);
3229 init_request("hlm", hyphen_line_max_request);
3230 #ifdef WIDOW_CONTROL
3231 init_request("wdc", widow_control_request);
3232 #endif /* WIDOW_CONTROL */
3234 init_request("tas", tabs_save);
3235 init_request("tar", tabs_restore);
3237 init_request("hys", hyphenation_space_request);
3238 init_request("hym", hyphenation_margin_request);
3239 init_request("pvs", post_vertical_spacing);
3240 init_int_env_reg(".f", get_font);
3241 init_int_env_reg(".b", get_bold);
3242 init_hunits_env_reg(".i", get_indent);
3243 init_hunits_env_reg(".in", get_saved_indent);
3244 init_int_env_reg(".int", get_prev_line_interrupted);
3245 init_int_env_reg(".j", get_adjust_mode);
3246 init_hunits_env_reg(".k", get_text_length);
3247 init_hunits_env_reg(".l", get_line_length);
3248 init_hunits_env_reg(".ll", get_saved_line_length);
3249 init_int_env_reg(".L", get_line_spacing);
3250 init_hunits_env_reg(".n", get_prev_text_length);
3251 init_string_env_reg(".s", get_point_size_string);
3252 init_string_env_reg(".sr", get_requested_point_size_string);
3253 init_int_env_reg(".ps", get_point_size);
3254 init_int_env_reg(".psr", get_requested_point_size);
3255 init_int_env_reg(".u", get_fill);
3256 init_vunits_env_reg(".v", get_vertical_spacing);
3257 init_vunits_env_reg(".pvs", get_post_vertical_spacing);
3258 init_hunits_env_reg(".w", get_prev_char_width);
3259 init_int_env_reg(".ss", get_space_size);
3260 init_int_env_reg(".sss", get_sentence_space_size);
3261 init_string_env_reg(".fam", get_font_family_string);
3262 init_string_env_reg(".fn", get_font_name_string);
3263 init_string_env_reg(".ev", get_name_string);
3264 init_int_env_reg(".hy", get_hyphenation_flags);
3265 init_int_env_reg(".hlm", get_hyphen_line_max);
3266 init_int_env_reg(".hlc", get_hyphen_line_count);
3267 init_hunits_env_reg(".lt", get_title_length);
3268 init_string_env_reg(".tabs", get_tabs);
3269 init_int_env_reg(".linetabs", get_line_tabs);
3270 init_hunits_env_reg(".csk", get_prev_char_skew);
3271 init_vunits_env_reg(".cht", get_prev_char_height);
3272 init_vunits_env_reg(".cdp", get_prev_char_depth);
3273 init_int_env_reg(".ce", get_center_lines);
3274 init_int_env_reg(".rj", get_right_justify_lines);
3275 init_hunits_env_reg(".hys", get_hyphenation_space);
3276 init_hunits_env_reg(".hym", get_hyphenation_margin);
3277 number_reg_dictionary.define("ln", new variable_reg(&next_line_number));
3278 number_reg_dictionary.define("ct", new variable_reg(&ct_reg_contents));
3279 number_reg_dictionary.define("sb", new variable_reg(&sb_reg_contents));
3280 number_reg_dictionary.define("st", new variable_reg(&st_reg_contents));
3281 number_reg_dictionary.define("rsb", new variable_reg(&rsb_reg_contents));
3282 number_reg_dictionary.define("rst", new variable_reg(&rst_reg_contents));
3283 number_reg_dictionary.define("ssc", new variable_reg(&ssc_reg_contents));
3284 number_reg_dictionary.define("skw", new variable_reg(&skw_reg_contents));
3285 number_reg_dictionary.define("hp", new horizontal_place_reg);
3288 // Hyphenation - TeX's hyphenation algorithm with a less fancy implementation.
3294 virtual void do_match(int len, void *val) = 0;
3295 virtual void do_delete(void *) = 0;
3296 void delete_trie_node(trie_node *);
3299 virtual ~trie(); // virtual to shut up g++
3300 void insert(const char *, int, void *);
3301 // find calls do_match for each match it finds
3302 void find(const char *pat, int patlen);
3306 class hyphen_trie : private trie {
3308 void do_match(int i, void *v);
3309 void do_delete(void *v);
3310 void insert_pattern(const char *pat, int patlen, int *num);
3311 void insert_hyphenation(dictionary ex, const char *pat, int patlen);
3312 int hpf_getc(FILE *f);
3316 void hyphenate(const char *word, int len, int *hyphens);
3317 void read_patterns_file(const char *name, int append, dictionary ex);
3320 struct hyphenation_language {
3322 dictionary exceptions;
3323 hyphen_trie patterns;
3324 hyphenation_language(symbol nm) : name(nm), exceptions(501) {}
3325 ~hyphenation_language() { }
3328 dictionary language_dictionary(5);
3329 hyphenation_language *current_language = 0;
3331 static void set_hyphenation_language()
3333 symbol nm = get_name(1);
3334 if (!nm.is_null()) {
3335 current_language = (hyphenation_language *)language_dictionary.lookup(nm);
3336 if (!current_language) {
3337 current_language = new hyphenation_language(nm);
3338 (void)language_dictionary.lookup(nm, (void *)current_language);
3344 const int WORD_MAX = 256; // we use unsigned char for offsets in
3345 // hyphenation exceptions
3347 static void hyphen_word()
3349 if (!current_language) {
3350 error("no current hyphenation language");
3354 char buf[WORD_MAX + 1];
3355 unsigned char pos[WORD_MAX + 2];
3358 if (tok.newline() || tok.eof())
3362 while (i < WORD_MAX && !tok.space() && !tok.newline() && !tok.eof()) {
3363 charinfo *ci = tok.get_char(1);
3369 if (ci->get_ascii_code() == '-') {
3370 if (i > 0 && (npos == 0 || pos[npos - 1] != i))
3374 int c = ci->get_hyphenation_code();
3383 unsigned char *tem = new unsigned char[npos + 1];
3384 memcpy(tem, pos, npos + 1);
3385 tem = (unsigned char *)current_language->exceptions.lookup(symbol(buf),
3399 trie_node(char, trie_node *);
3402 trie_node::trie_node(char ch, trie_node *p)
3403 : c(ch), down(0), right(p), val(0)
3414 delete_trie_node(tp);
3419 void trie::delete_trie_node(trie_node *p)
3422 delete_trie_node(p->down);
3423 delete_trie_node(p->right);
3430 void trie::insert(const char *pat, int patlen, void *val)
3432 trie_node **p = &tp;
3433 assert(patlen > 0 && pat != 0);
3435 while (*p != 0 && (*p)->c < pat[0])
3437 if (*p == 0 || (*p)->c != pat[0])
3438 *p = new trie_node(pat[0], *p);
3439 if (--patlen == 0) {
3448 void trie::find(const char *pat, int patlen)
3451 for (int i = 0; p != 0 && i < patlen; i++) {
3452 while (p != 0 && p->c < pat[i])
3454 if (p != 0 && p->c == pat[i]) {
3456 do_match(i+1, p->val);
3468 operation(int, int, operation *);
3471 operation::operation(int i, int j, operation *op)
3472 : next(op), distance(j), num(i)
3476 void hyphen_trie::insert_pattern(const char *pat, int patlen, int *num)
3479 for (int i = 0; i < patlen+1; i++)
3481 op = new operation(num[i], patlen - i, op);
3482 insert(pat, patlen, op);
3485 void hyphen_trie::insert_hyphenation(dictionary ex, const char *pat,
3488 char buf[WORD_MAX + 1];
3489 unsigned char pos[WORD_MAX + 2];
3492 while (j < patlen) {
3493 unsigned char c = pat[j++];
3495 if (i > 0 && (npos == 0 || pos[npos - 1] != i))
3499 buf[i++] = hpf_code_table[c];
3504 unsigned char *tem = new unsigned char[npos + 1];
3505 memcpy(tem, pos, npos + 1);
3506 tem = (unsigned char *)ex.lookup(symbol(buf), tem);
3512 void hyphen_trie::hyphenate(const char *word, int len, int *hyphens)
3515 for (j = 0; j < len + 1; j++)
3517 for (j = 0; j < len - 1; j++) {
3519 find(word + j, len - j);
3523 inline int max(int m, int n)
3525 return m > n ? m : n;
3528 void hyphen_trie::do_match(int i, void *v)
3530 operation *op = (operation *)v;
3532 h[i - op->distance] = max(h[i - op->distance], op->num);
3537 void hyphen_trie::do_delete(void *v)
3539 operation *op = (operation *)v;
3541 operation *tem = op;
3547 /* We use very simple rules to parse TeX's hyphenation patterns.
3549 . `%' starts a comment even if preceded by `\'.
3551 . No support for digraphs and like `\$'.
3553 . `^^xx' (`x' is 0-9 or a-f), and `^^x' (character code of `x' in the
3554 range 0-127) are recognized; other use of `^' causes an error.
3556 . No macro expansion.
3558 . We check for the expression `\patterns{...}' (possibly with
3559 whitespace before and after the braces). Everything between the
3560 braces is taken as hyphenation patterns. Consequently, `{' and `}'
3561 are not allowed in patterns.
3563 . Similarly, `\hyphenation{...}' gives a list of hyphenation
3566 . `\endinput' is recognized also.
3568 . For backwards compatibility, if `\patterns' is missing, the
3569 whole file is treated as a list of hyphenation patterns (only
3570 recognizing `%' as the start of a comment.
3574 int hyphen_trie::hpf_getc(FILE *f)
3586 if (((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))
3587 && ((c1 >= '0' && c1 <= '9') || (c1 >= 'a' && c1 <= 'f'))) {
3588 if (c >= '0' && c <= '9')
3592 if (c1 >= '0' && c1 <= '9')
3600 if (c >= 0 && c <= 63)
3602 else if (c >= 64 && c <= 127)
3609 error("invalid ^, ^^x, or ^^xx character in hyphenation patterns file");
3613 void hyphen_trie::read_patterns_file(const char *name, int append,
3619 int num[WORD_MAX+1];
3622 FILE *fp = mac_path->open_file(name, &path);
3624 error("can't find hyphenation patterns file `%1'", name);
3627 int c = hpf_getc(fp);
3628 int have_patterns = 0; // we've seen \patterns
3629 int final_pattern = 0; // 1 if we have a trailing closing brace
3630 int have_hyphenation = 0; // we've seen \hyphenation
3631 int final_hyphenation = 0; // 1 if we have a trailing closing brace
3632 int have_keyword = 0; // we've seen either \patterns or \hyphenation
3633 int traditional = 0; // don't handle \patterns
3636 if (c == '%') { // skip comments
3639 } while (c != EOF && c != '\n');
3641 if (c == EOF || !csspace(c))
3646 if (have_keyword || traditional) // we are done
3648 else { // rescan file in `traditional' mode
3657 if (!(c == '{' || c == '}')) { // skip braces at line start
3658 do { // scan patterns
3666 } while (i < WORD_MAX && c != EOF && !csspace(c)
3667 && c != '%' && c != '{' && c != '}');
3670 if (i >= 9 && !strncmp(buf + i - 9, "\\patterns", 9)) {
3674 if (have_patterns || have_hyphenation)
3675 error("`{' not allowed inside of \\patterns or \\hyphenation");
3684 else if (i >= 12 && !strncmp(buf + i - 12, "\\hyphenation", 12)) {
3688 if (have_patterns || have_hyphenation)
3689 error("`{' not allowed inside of \\patterns or \\hyphenation");
3691 have_hyphenation = 1;
3698 else if (strstr(buf, "\\endinput")) {
3699 if (have_patterns || have_hyphenation)
3700 error("found \\endinput inside of %1 group",
3701 have_patterns ? "\\patterns" : "\\hyphenation");
3704 else if (c == '}') {
3705 if (have_patterns) {
3710 else if (have_hyphenation) {
3711 have_hyphenation = 0;
3713 final_hyphenation = 1;
3717 else if (c == '{') // skipped if not starting \patterns
3718 c = hpf_getc(fp); // or \hyphenation
3721 if (have_patterns || final_pattern || traditional) {
3722 for (int j = 0; j < i; j++)
3723 buf[j] = hpf_code_table[(unsigned char)buf[j]];
3724 insert_pattern(buf, i, num);
3727 else if (have_hyphenation || final_hyphenation) {
3728 insert_hyphenation(ex, buf, i);
3729 final_hyphenation = 0;
3738 void hyphenate(hyphen_list *h, unsigned flags)
3740 if (!current_language)
3743 while (h && h->hyphenation_code == 0)
3746 char hbuf[WORD_MAX+2];
3747 char *buf = hbuf + 1;
3749 for (tem = h; tem && len < WORD_MAX; tem = tem->next) {
3750 if (tem->hyphenation_code != 0)
3751 buf[len++] = tem->hyphenation_code;
3755 hyphen_list *nexth = tem;
3759 = (unsigned char *)current_language->exceptions.lookup(buf);
3763 for (tem = h; tem != 0; tem = tem->next, i++)
3770 hbuf[0] = hbuf[len+1] = '.';
3771 int num[WORD_MAX+3];
3772 current_language->patterns.hyphenate(hbuf, len+2, num);
3779 for (i = 2, tem = h; i < len && tem; tem = tem->next, i++)
3788 static void do_hyphenation_patterns_file(int append)
3790 symbol name = get_long_name(1);
3791 if (!name.is_null()) {
3792 if (!current_language)
3793 error("no current hyphenation language");
3795 current_language->patterns.read_patterns_file(
3796 name.contents(), append,
3797 current_language->exceptions);
3802 static void hyphenation_patterns_file()
3804 do_hyphenation_patterns_file(0);
3807 static void hyphenation_patterns_file_append()
3809 do_hyphenation_patterns_file(1);
3812 class hyphenation_language_reg : public reg {
3814 const char *get_string();
3817 const char *hyphenation_language_reg::get_string()
3819 return current_language ? current_language->name.contents() : "";
3822 void init_hyphen_requests()
3824 init_request("hw", hyphen_word);
3825 init_request("hla", set_hyphenation_language);
3826 init_request("hpf", hyphenation_patterns_file);
3827 init_request("hpfa", hyphenation_patterns_file_append);
3828 number_reg_dictionary.define(".hla", new hyphenation_language_reg);