2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003
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(0, ".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 not be negative");
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.
1877 breakpoint
*tem
= bp
->next
;
1880 breakpoint
*tem1
= tem
;
1886 // It fits but it's hyphenated.
1887 if (!best_bp_fits
) {
1895 breakpoint
*tem
= bp
;
1912 output_warning(WARN_BREAK
, "can't break line");
1918 void environment::hyphenate_line(int start_here
)
1921 hyphenation_type prev_type
= line
->get_hyphenation_type();
1926 for (startp
= &line
->next
; *startp
!= 0; startp
= &(*startp
)->next
) {
1927 hyphenation_type this_type
= (*startp
)->get_hyphenation_type();
1928 if (prev_type
== HYPHEN_BOUNDARY
&& this_type
== HYPHEN_MIDDLE
)
1930 prev_type
= this_type
;
1934 node
*tem
= *startp
;
1937 } while (tem
!= 0 && tem
->get_hyphenation_type() == HYPHEN_MIDDLE
);
1938 int inhibit
= (tem
!= 0 && tem
->get_hyphenation_type() == HYPHEN_INHIBIT
);
1940 hyphen_list
*sl
= 0;
1944 while (tem
!= end
) {
1945 sl
= tem
->get_hyphen_list(sl
, &i
);
1948 tem1
->next
= forward
;
1952 // this is for characters like hyphen and emdash
1954 for (hyphen_list
*h
= sl
; h
; h
= h
->next
) {
1955 h
->breakable
= (prev_code
!= 0
1957 && h
->next
->hyphenation_code
!= 0);
1958 prev_code
= h
->hyphenation_code
;
1961 if (hyphenation_flags
!= 0
1963 // this may not be right if we have extra space on this line
1964 && !((hyphenation_flags
& HYPHEN_LAST_LINE
)
1965 && (curdiv
->distance_to_next_trap()
1966 <= vertical_spacing
+ total_post_vertical_spacing()))
1968 hyphenate(sl
, hyphenation_flags
);
1969 while (forward
!= 0) {
1970 node
*tem1
= forward
;
1971 forward
= forward
->next
;
1973 tem
= tem1
->add_self(tem
, &sl
);
1978 static node
*node_list_reverse(node
*n
)
1990 static void distribute_space(node
*n
, int nspaces
, hunits desired_space
,
1991 int force_reverse
= 0)
1993 static int reverse
= 0;
1994 if (force_reverse
|| reverse
)
1995 n
= node_list_reverse(n
);
1996 if (!force_reverse
&& nspaces
> 0 && spread_limit
>= 0
1997 && desired_space
.to_units() > 0) {
1998 hunits em
= curenv
->get_size();
1999 double Ems
= (double)desired_space
.to_units() / nspaces
2000 / (em
.is_zero() ? hresolution
: em
.to_units());
2001 if (Ems
> spread_limit
)
2002 output_warning(WARN_BREAK
, "spreading %1m per space", Ems
);
2004 for (node
*tem
= n
; tem
; tem
= tem
->next
)
2005 tem
->spread_space(&nspaces
, &desired_space
);
2006 if (force_reverse
|| reverse
)
2007 (void)node_list_reverse(n
);
2010 assert(desired_space
.is_zero() && nspaces
== 0);
2013 void environment::possibly_break_line(int start_here
, int forced
)
2015 if (!fill
|| current_tab
|| current_field
|| dummy
)
2019 // When a macro follows a paragraph in fill mode, the
2020 // current line should not be empty.
2021 || (width_total
- line
->width()) > target_text_length
)) {
2022 hyphenate_line(start_here
);
2023 breakpoint
*bp
= choose_breakpoint();
2025 // we'll find one eventually
2029 while (*ndp
!= bp
->nd
)
2030 ndp
= &(*ndp
)->next
;
2031 bp
->nd
->split(bp
->index
, &pre
, &post
);
2033 hunits extra_space_width
= H0
;
2034 switch(adjust_mode
) {
2036 if (bp
->nspaces
!= 0)
2037 extra_space_width
= target_text_length
- bp
->width
;
2038 else if (bp
->width
> 0 && target_text_length
> 0
2039 && target_text_length
> bp
->width
)
2040 output_warning(WARN_BREAK
, "cannot adjust line");
2043 saved_indent
+= (target_text_length
- bp
->width
)/2;
2046 saved_indent
+= target_text_length
- bp
->width
;
2049 distribute_space(pre
, bp
->nspaces
, extra_space_width
);
2050 hunits output_width
= bp
->width
+ extra_space_width
;
2051 input_line_start
-= output_width
;
2053 hyphen_line_count
++;
2055 hyphen_line_count
= 0;
2059 node
*first_non_discardable
= 0;
2061 for (tem
= line
; tem
!= 0; tem
= tem
->next
)
2062 if (!tem
->discardable())
2063 first_non_discardable
= tem
;
2064 node
*to_be_discarded
;
2065 if (first_non_discardable
) {
2066 to_be_discarded
= first_non_discardable
->next
;
2067 first_non_discardable
->next
= 0;
2068 for (tem
= line
; tem
!= 0; tem
= tem
->next
) {
2069 width_total
+= tem
->width();
2070 space_total
+= tem
->nspaces();
2076 to_be_discarded
= line
;
2079 // Do output_line() here so that line will be 0 iff the
2080 // the environment will be empty.
2081 output_line(pre
, output_width
);
2082 while (to_be_discarded
!= 0) {
2083 tem
= to_be_discarded
;
2084 to_be_discarded
= to_be_discarded
->next
;
2085 input_line_start
-= tem
->width();
2089 if (have_temporary_indent
) {
2090 saved_indent
= temporary_indent
;
2091 have_temporary_indent
= 0;
2094 saved_indent
= indent
;
2095 target_text_length
= line_length
- saved_indent
;
2101 Do the break at the end of input after the end macro (if any).
2103 Unix troff behaves as follows: if the last line is
2107 it will output foo on the current page, and bar on the next page;
2116 everything will be output on the current page. This behaviour must be
2119 The problem is that some macro packages rely on this. For example,
2120 the ATK macros have an end macro that emits \c if it needs to print a
2121 table of contents but doesn't do a 'bp in the end macro; instead the
2122 'bp is done in the bottom of page trap. This works with Unix troff,
2123 provided that the current environment is not empty at the end of the
2126 The following will make macro packages that do that sort of thing work
2127 even if the current environment is empty at the end of the input file.
2128 If the last input line used \c and this line occurred in the end macro,
2129 then we'll force everything out on the current page, but we'll make
2130 sure that the environment isn't empty so that we won't exit at the
2131 bottom of this page.
2134 void environment::final_break()
2136 if (prev_line_interrupted
== 2) {
2138 add_node(new transparent_dummy_node
);
2145 * add_html_tag - emits a special html-tag: to help post-grohtml understand
2146 * the key troff commands
2149 void environment::add_html_tag(int force
, const char *name
)
2151 if (!force
&& (curdiv
!= topdiv
))
2156 * need to emit tag for post-grohtml
2157 * but we check to see whether we can emit specials
2159 if (curdiv
== topdiv
&& topdiv
->before_first_page
)
2160 topdiv
->begin_page();
2161 macro
*m
= new macro
;
2162 m
->append_str("html-tag:");
2163 for (const char *p
= name
; *p
; p
++)
2164 if (!invalid_input_char((unsigned char)*p
))
2166 curdiv
->output(new special_node(*m
), 1, 0, 0, 0);
2167 if (strcmp(name
, ".nf") == 0)
2168 curenv
->ignore_next_eol
= 1;
2173 * add_html_tag - emits a special html-tag: to help post-grohtml understand
2174 * the key troff commands, it appends a string representation
2178 void environment::add_html_tag(int force
, const char *name
, int i
)
2180 if (!force
&& (curdiv
!= topdiv
))
2185 * need to emit tag for post-grohtml
2186 * but we check to see whether we can emit specials
2188 if (curdiv
== topdiv
&& topdiv
->before_first_page
)
2189 topdiv
->begin_page();
2190 macro
*m
= new macro
;
2191 m
->append_str("html-tag:");
2192 for (const char *p
= name
; *p
; p
++)
2193 if (!invalid_input_char((unsigned char)*p
))
2197 node
*n
= new special_node(*m
);
2198 curdiv
->output(n
, 1, 0, 0, 0);
2203 * add_html_tag_tabs - emits the tab settings for post-grohtml
2206 void environment::add_html_tag_tabs(int force
)
2208 if (!force
&& (curdiv
!= topdiv
))
2213 * need to emit tag for post-grohtml
2214 * but we check to see whether we can emit specials
2216 if (curdiv
== topdiv
&& topdiv
->before_first_page
)
2217 topdiv
->begin_page();
2218 macro
*m
= new macro
;
2221 m
->append_str("html-tag:.ta ");
2223 t
= curenv
->tabs
.distance_to_next_tab(l
, &d
);
2227 m
->append_str(" L ");
2228 m
->append_int(l
.to_units());
2231 m
->append_str(" C ");
2232 m
->append_int(l
.to_units());
2235 m
->append_str(" R ");
2236 m
->append_int(l
.to_units());
2241 } while ((t
!= TAB_NONE
) && (l
< get_line_length()));
2242 curdiv
->output(new special_node(*m
), 1, 0, 0, 0);
2246 node
*environment::make_html_tag(const char *name
, int i
)
2250 * need to emit tag for post-grohtml
2251 * but we check to see whether we can emit specials
2253 if (curdiv
== topdiv
&& topdiv
->before_first_page
)
2254 topdiv
->begin_page();
2255 macro
*m
= new macro
;
2256 m
->append_str("html-tag:");
2257 for (const char *p
= name
; *p
; p
++)
2258 if (!invalid_input_char((unsigned char)*p
))
2262 return new special_node(*m
);
2267 node
*environment::make_html_tag(const char *name
)
2271 * need to emit tag for post-grohtml
2272 * but we check to see whether we can emit specials
2274 if (curdiv
== topdiv
&& topdiv
->before_first_page
)
2275 topdiv
->begin_page();
2276 macro
*m
= new macro
;
2277 m
->append_str("html-tag:");
2278 for (const char *p
= name
; *p
; p
++)
2279 if (!invalid_input_char((unsigned char)*p
))
2281 return new special_node(*m
);
2286 void environment::do_break(int spread
)
2288 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
2289 topdiv
->begin_page();
2295 // this is so that hyphenation works
2296 line
= new space_node(H0
, get_fill_color(), line
);
2298 possibly_break_line(0, spread
);
2300 while (line
!= 0 && line
->discardable()) {
2301 width_total
-= line
->width();
2302 space_total
-= line
->nspaces();
2308 input_line_start
= H0
;
2311 switch (adjust_mode
) {
2313 saved_indent
+= (target_text_length
- width_total
)/2;
2316 saved_indent
+= target_text_length
- width_total
;
2322 output_line(tem
, width_total
);
2323 hyphen_line_count
= 0;
2325 prev_line_interrupted
= 0;
2326 #ifdef WIDOW_CONTROL
2328 output_pending_lines();
2329 #endif /* WIDOW_CONTROL */
2332 int environment::is_empty()
2334 return !current_tab
&& line
== 0 && pending_lines
== 0;
2337 void do_break_request(int spread
)
2339 while (!tok
.newline() && !tok
.eof())
2342 curenv
->do_break(spread
);
2343 curenv
->add_html_tag(0, ".br");
2348 void break_request()
2350 do_break_request(0);
2353 void break_spread_request()
2355 do_break_request(1);
2360 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
2361 handle_initial_title();
2365 hunits part_width
[3];
2366 part
[0] = part
[1] = part
[2] = 0;
2367 environment
env(curenv
);
2368 environment
*oldenv
= curenv
;
2370 read_title_parts(part
, part_width
);
2372 curenv
->size
= env
.size
;
2373 curenv
->prev_size
= env
.prev_size
;
2374 curenv
->requested_size
= env
.requested_size
;
2375 curenv
->prev_requested_size
= env
.prev_requested_size
;
2376 curenv
->char_height
= env
.char_height
;
2377 curenv
->char_slant
= env
.char_slant
;
2378 curenv
->fontno
= env
.fontno
;
2379 curenv
->prev_fontno
= env
.prev_fontno
;
2380 curenv
->glyph_color
= env
.glyph_color
;
2381 curenv
->prev_glyph_color
= env
.prev_glyph_color
;
2382 curenv
->fill_color
= env
.fill_color
;
2383 curenv
->prev_fill_color
= env
.prev_fill_color
;
2392 hunits
title_length(curenv
->title_length
);
2393 hunits f
= title_length
- part_width
[1];
2395 n
= new hmotion_node(f2
- part_width
[2], curenv
->get_fill_color(), n
);
2403 n
= new hmotion_node(f
- f2
- part_width
[0], curenv
->get_fill_color(), n
);
2411 curenv
->output_title(n
, !curenv
->fill
, curenv
->vertical_spacing
,
2412 curenv
->total_post_vertical_spacing(), title_length
);
2413 curenv
->hyphen_line_count
= 0;
2419 curenv
->adjust_mode
|= 1;
2423 curenv
->adjust_mode
= ADJUST_LEFT
;
2426 curenv
->adjust_mode
= ADJUST_RIGHT
;
2429 curenv
->adjust_mode
= ADJUST_CENTER
;
2433 curenv
->adjust_mode
= ADJUST_BOTH
;
2437 if (get_integer(&n
)) {
2439 warning(WARN_RANGE
, "negative adjustment mode");
2441 curenv
->adjust_mode
= 5;
2442 warning(WARN_RANGE
, "adjustment mode `%1' out of range", n
);
2445 curenv
->adjust_mode
= n
;
2454 curenv
->adjust_mode
&= ~1;
2458 void do_input_trap(int continued
)
2460 curenv
->input_trap_count
= 0;
2462 curenv
->continued_input_trap
= 1;
2464 if (has_arg() && get_integer(&n
)) {
2467 "number of lines for input trap must be greater than zero");
2469 symbol s
= get_name(1);
2471 curenv
->input_trap_count
= n
;
2472 curenv
->input_trap
= s
;
2484 void input_trap_continued()
2491 // must not be R or C or L or a legitimate part of a number expression
2492 const char TAB_REPEAT_CHAR
= 'T';
2498 tab(hunits
, tab_type
);
2499 enum { BLOCK
= 1024 };
2500 static tab
*free_list
;
2501 void *operator new(size_t);
2502 void operator delete(void *);
2505 tab
*tab::free_list
= 0;
2507 void *tab::operator new(size_t n
)
2509 assert(n
== sizeof(tab
));
2511 free_list
= (tab
*)new char[sizeof(tab
)*BLOCK
];
2512 for (int i
= 0; i
< BLOCK
- 1; i
++)
2513 free_list
[i
].next
= free_list
+ i
+ 1;
2514 free_list
[BLOCK
-1].next
= 0;
2517 free_list
= (tab
*)(free_list
->next
);
2523 /* cfront can't cope with this. */
2526 void tab::operator delete(void *p
)
2529 ((tab
*)p
)->next
= free_list
;
2530 free_list
= (tab
*)p
;
2534 tab::tab(hunits x
, tab_type t
) : next(0), pos(x
), type(t
)
2538 tab_stops::tab_stops(hunits distance
, tab_type type
)
2541 repeated_list
= new tab(distance
, type
);
2544 tab_stops::~tab_stops()
2549 tab_type
tab_stops::distance_to_next_tab(hunits curpos
, hunits
*distance
)
2553 return distance_to_next_tab(curpos
, distance
, &nextpos
);
2556 tab_type
tab_stops::distance_to_next_tab(hunits curpos
, hunits
*distance
,
2561 for (tem
= initial_list
; tem
&& tem
->pos
<= curpos
; tem
= tem
->next
)
2564 *distance
= tem
->pos
- curpos
;
2565 *nextpos
= tem
->pos
;
2568 if (repeated_list
== 0)
2570 hunits base
= lastpos
;
2572 for (tem
= repeated_list
; tem
&& tem
->pos
+ base
<= curpos
; tem
= tem
->next
)
2575 *distance
= tem
->pos
+ base
- curpos
;
2576 *nextpos
= tem
->pos
+ base
;
2579 assert(lastpos
> 0);
2585 const char *tab_stops::to_string()
2587 static char *buf
= 0;
2588 static int buf_size
= 0;
2589 // figure out a maximum on the amount of space we can need
2592 for (p
= initial_list
; p
; p
= p
->next
)
2594 for (p
= repeated_list
; p
; p
= p
->next
)
2596 // (10 for digits + 1 for u + 1 for 'C' or 'R') + 2 for ' &' + 1 for '\0'
2597 int need
= count
*12 + 3;
2598 if (buf
== 0 || need
> buf_size
) {
2602 buf
= new char[buf_size
];
2605 for (p
= initial_list
; p
; p
= p
->next
) {
2606 strcpy(ptr
, i_to_a(p
->pos
.to_units()));
2607 ptr
= strchr(ptr
, '\0');
2625 *ptr
++ = TAB_REPEAT_CHAR
;
2626 for (p
= repeated_list
; p
; p
= p
->next
) {
2627 strcpy(ptr
, i_to_a(p
->pos
.to_units()));
2628 ptr
= strchr(ptr
, '\0');
2649 tab_stops::tab_stops() : initial_list(0), repeated_list(0)
2653 tab_stops::tab_stops(const tab_stops
&ts
)
2654 : initial_list(0), repeated_list(0)
2656 tab
**p
= &initial_list
;
2657 tab
*t
= ts
.initial_list
;
2659 *p
= new tab(t
->pos
, t
->type
);
2664 t
= ts
.repeated_list
;
2666 *p
= new tab(t
->pos
, t
->type
);
2672 void tab_stops::clear()
2674 while (initial_list
) {
2675 tab
*tem
= initial_list
;
2676 initial_list
= initial_list
->next
;
2679 while (repeated_list
) {
2680 tab
*tem
= repeated_list
;
2681 repeated_list
= repeated_list
->next
;
2686 void tab_stops::add_tab(hunits pos
, tab_type type
, int repeated
)
2689 for (p
= repeated
? &repeated_list
: &initial_list
; *p
; p
= &(*p
)->next
)
2691 *p
= new tab(pos
, type
);
2695 void tab_stops::operator=(const tab_stops
&ts
)
2698 tab
**p
= &initial_list
;
2699 tab
*t
= ts
.initial_list
;
2701 *p
= new tab(t
->pos
, t
->type
);
2706 t
= ts
.repeated_list
;
2708 *p
= new tab(t
->pos
, t
->type
);
2717 hunits prev_pos
= 0;
2722 if (tok
.ch() == TAB_REPEAT_CHAR
) {
2727 if (!get_hunits(&pos
, 'm', prev_pos
))
2729 tab_type type
= TAB_LEFT
;
2730 if (tok
.ch() == 'C') {
2734 else if (tok
.ch() == 'R') {
2738 else if (tok
.ch() == 'L') {
2741 if (pos
<= prev_pos
&& !first
)
2743 "positions of tab stops must be strictly increasing");
2745 tabs
.add_tab(pos
, type
, repeated
);
2750 curenv
->tabs
= tabs
;
2751 curenv
->add_html_tag_tabs(1);
2755 const char *environment::get_tabs()
2757 return tabs
.to_string();
2760 tab_type
environment::distance_to_next_tab(hunits
*distance
)
2763 ? curenv
->tabs
.distance_to_next_tab(get_text_length(), distance
)
2764 : curenv
->tabs
.distance_to_next_tab(get_input_line_position(), distance
);
2767 tab_type
environment::distance_to_next_tab(hunits
*distance
, hunits
*leftpos
)
2770 ? curenv
->tabs
.distance_to_next_tab(get_text_length(), distance
, leftpos
)
2771 : curenv
->tabs
.distance_to_next_tab(get_input_line_position(), distance
,
2775 void field_characters()
2777 field_delimiter_char
= get_optional_char();
2778 if (field_delimiter_char
)
2779 padding_indicator_char
= get_optional_char();
2781 padding_indicator_char
= 0;
2785 void line_tabs_request()
2788 if (has_arg() && get_integer(&n
))
2789 curenv
->line_tabs
= n
!= 0;
2791 curenv
->line_tabs
= 1;
2795 int environment::get_line_tabs()
2800 void environment::wrap_up_tab()
2807 switch (current_tab
) {
2809 tab_amount
= tab_distance
- tab_width
;
2810 line
= make_tab_node(tab_amount
, line
);
2813 tab_amount
= tab_distance
- tab_width
/2;
2814 line
= make_tab_node(tab_amount
, line
);
2821 width_total
+= tab_amount
;
2822 width_total
+= tab_width
;
2823 if (current_field
) {
2824 if (tab_precedes_field
) {
2825 pre_field_width
+= tab_amount
;
2826 tab_precedes_field
= 0;
2828 field_distance
-= tab_amount
;
2829 field_spaces
+= tab_field_spaces
;
2831 if (tab_contents
!= 0) {
2833 for (tem
= tab_contents
; tem
->next
!= 0; tem
= tem
->next
)
2836 line
= tab_contents
;
2838 tab_field_spaces
= 0;
2842 current_tab
= TAB_NONE
;
2845 node
*environment::make_tab_node(hunits d
, node
*next
)
2847 if (leader_node
!= 0 && d
< 0) {
2848 error("motion generated by leader cannot be negative");
2853 return new hmotion_node(d
, 1, 0, get_fill_color(), next
);
2854 node
*n
= new hline_node(d
, leader_node
, next
);
2859 void environment::handle_tab(int is_leader
)
2865 charinfo
*ci
= is_leader
? leader_char
: tab_char
;
2867 leader_node
= ci
? make_char_node(ci
) : 0;
2868 tab_type t
= distance_to_next_tab(&d
, &abs
);
2873 add_node(make_tab_node(d
));
2874 add_node(make_html_tag("tab L", abs
.to_units()));
2877 add_node(make_html_tag("tab R", abs
.to_units()));
2880 add_node(make_html_tag("tab C", abs
.to_units()));
2889 tab_field_spaces
= 0;
2892 void environment::start_field()
2894 assert(!current_field
);
2896 if (distance_to_next_tab(&d
) != TAB_NONE
) {
2897 pre_field_width
= get_text_length();
2901 tab_field_spaces
= 0;
2902 for (node
*p
= line
; p
; p
= p
->next
)
2907 tab_precedes_field
= current_tab
!= TAB_NONE
;
2910 error("zero field width");
2913 void environment::wrap_up_field()
2915 if (!current_tab
&& field_spaces
== 0)
2917 hunits padding
= field_distance
- (get_text_length() - pre_field_width
);
2918 if (current_tab
&& tab_field_spaces
!= 0) {
2919 hunits tab_padding
= scale(padding
,
2921 field_spaces
+ tab_field_spaces
);
2922 padding
-= tab_padding
;
2923 distribute_space(tab_contents
, tab_field_spaces
, tab_padding
, 1);
2924 tab_field_spaces
= 0;
2925 tab_width
+= tab_padding
;
2927 if (field_spaces
!= 0) {
2928 distribute_space(line
, field_spaces
, padding
, 1);
2929 width_total
+= padding
;
2931 // the start of the tab has been moved to the right by padding, so
2932 tab_distance
-= padding
;
2933 if (tab_distance
<= H0
) {
2934 // use the next tab stop instead
2935 current_tab
= tabs
.distance_to_next_tab(get_input_line_position()
2938 if (current_tab
== TAB_NONE
|| current_tab
== TAB_LEFT
) {
2939 width_total
+= tab_width
;
2940 if (current_tab
== TAB_LEFT
) {
2941 line
= make_tab_node(tab_distance
, line
);
2942 width_total
+= tab_distance
;
2943 current_tab
= TAB_NONE
;
2945 if (tab_contents
!= 0) {
2947 for (tem
= tab_contents
; tem
->next
!= 0; tem
= tem
->next
)
2950 line
= tab_contents
;
2962 void environment::add_padding()
2965 tab_contents
= new space_node(H0
, get_fill_color(), tab_contents
);
2971 line
= new space_node(H0
, get_fill_color(), line
);
2976 typedef int (environment::*INT_FUNCP
)();
2977 typedef vunits (environment::*VUNITS_FUNCP
)();
2978 typedef hunits (environment::*HUNITS_FUNCP
)();
2979 typedef const char *(environment::*STRING_FUNCP
)();
2981 class int_env_reg
: public reg
{
2984 int_env_reg(INT_FUNCP
);
2985 const char *get_string();
2986 int get_value(units
*val
);
2989 class vunits_env_reg
: public reg
{
2992 vunits_env_reg(VUNITS_FUNCP f
);
2993 const char *get_string();
2994 int get_value(units
*val
);
2998 class hunits_env_reg
: public reg
{
3001 hunits_env_reg(HUNITS_FUNCP f
);
3002 const char *get_string();
3003 int get_value(units
*val
);
3006 class string_env_reg
: public reg
{
3009 string_env_reg(STRING_FUNCP
);
3010 const char *get_string();
3013 int_env_reg::int_env_reg(INT_FUNCP f
) : func(f
)
3017 int int_env_reg::get_value(units
*val
)
3019 *val
= (curenv
->*func
)();
3023 const char *int_env_reg::get_string()
3025 return i_to_a((curenv
->*func
)());
3028 vunits_env_reg::vunits_env_reg(VUNITS_FUNCP f
) : func(f
)
3032 int vunits_env_reg::get_value(units
*val
)
3034 *val
= (curenv
->*func
)().to_units();
3038 const char *vunits_env_reg::get_string()
3040 return i_to_a((curenv
->*func
)().to_units());
3043 hunits_env_reg::hunits_env_reg(HUNITS_FUNCP f
) : func(f
)
3047 int hunits_env_reg::get_value(units
*val
)
3049 *val
= (curenv
->*func
)().to_units();
3053 const char *hunits_env_reg::get_string()
3055 return i_to_a((curenv
->*func
)().to_units());
3058 string_env_reg::string_env_reg(STRING_FUNCP f
) : func(f
)
3062 const char *string_env_reg::get_string()
3064 return (curenv
->*func
)();
3067 class horizontal_place_reg
: public general_reg
{
3069 horizontal_place_reg();
3070 int get_value(units
*);
3071 void set_value(units
);
3074 horizontal_place_reg::horizontal_place_reg()
3078 int horizontal_place_reg::get_value(units
*res
)
3080 *res
= curenv
->get_input_line_position().to_units();
3084 void horizontal_place_reg::set_value(units n
)
3086 curenv
->set_input_line_position(hunits(n
));
3089 const char *environment::get_font_family_string()
3091 return family
->nm
.contents();
3094 const char *environment::get_font_name_string()
3096 symbol f
= get_font_name(fontno
, this);
3097 return f
.contents();
3100 const char *environment::get_name_string()
3102 return name
.contents();
3105 // Convert a quantity in scaled points to ascii decimal fraction.
3107 const char *sptoa(int sp
)
3110 assert(sizescale
> 0);
3113 if (sp
% sizescale
== 0)
3114 return i_to_a(sp
/sizescale
);
3115 // See if 1/sizescale is exactly representable as a decimal fraction,
3116 // ie its only prime factors are 2 and 5.
3119 while ((n
& 1) == 0) {
3124 while ((n
% 5) == 0) {
3129 int decimal_point
= power5
> power2
? power5
: power2
;
3130 if (decimal_point
<= 10) {
3133 for (t
= decimal_point
- power2
; --t
>= 0;)
3135 for (t
= decimal_point
- power5
; --t
>= 0;)
3137 if (factor
== 1 || sp
<= INT_MAX
/factor
)
3138 return if_to_a(sp
*factor
, decimal_point
);
3141 double s
= double(sp
)/double(sizescale
);
3142 double factor
= 10.0;
3144 int decimal_point
= 0;
3146 double v
= ceil(s
*factor
);
3151 } while (++decimal_point
< 10);
3152 return if_to_a(int(val
), decimal_point
);
3155 const char *environment::get_point_size_string()
3157 return sptoa(curenv
->get_point_size());
3160 const char *environment::get_requested_point_size_string()
3162 return sptoa(curenv
->get_requested_point_size());
3165 #define init_int_env_reg(name, func) \
3166 number_reg_dictionary.define(name, new int_env_reg(&environment::func))
3168 #define init_vunits_env_reg(name, func) \
3169 number_reg_dictionary.define(name, new vunits_env_reg(&environment::func))
3171 #define init_hunits_env_reg(name, func) \
3172 number_reg_dictionary.define(name, new hunits_env_reg(&environment::func))
3174 #define init_string_env_reg(name, func) \
3175 number_reg_dictionary.define(name, new string_env_reg(&environment::func))
3177 void init_env_requests()
3179 init_request("ad", adjust
);
3180 init_request("br", break_request
);
3181 init_request("brp", break_spread_request
);
3182 init_request("c2", no_break_control_char
);
3183 init_request("cc", control_char
);
3184 init_request("ce", center
);
3185 init_request("cu", continuous_underline
);
3186 init_request("ev", environment_switch
);
3187 init_request("evc", environment_copy
);
3188 init_request("fam", family_change
);
3189 init_request("fc", field_characters
);
3190 init_request("fi", fill
);
3191 init_request("ft", font_change
);
3192 init_request("hc", hyphen_char
);
3193 init_request("hlm", hyphen_line_max_request
);
3194 init_request("hy", hyphenate_request
);
3195 init_request("hym", hyphenation_margin_request
);
3196 init_request("hys", hyphenation_space_request
);
3197 init_request("in", indent
);
3198 init_request("it", input_trap
);
3199 init_request("itc", input_trap_continued
);
3200 init_request("lc", leader_character
);
3201 init_request("linetabs", line_tabs_request
);
3202 init_request("ll", line_length
);
3203 init_request("ls", line_spacing
);
3204 init_request("lt", title_length
);
3205 init_request("mc", margin_character
);
3206 init_request("na", no_adjust
);
3207 init_request("nf", no_fill
);
3208 init_request("nh", no_hyphenate
);
3209 init_request("nm", number_lines
);
3210 init_request("nn", no_number
);
3211 init_request("ps", point_size
);
3212 init_request("pvs", post_vertical_spacing
);
3213 init_request("rj", right_justify
);
3214 init_request("sizes", override_sizes
);
3215 init_request("ss", space_size
);
3216 init_request("ta", set_tabs
);
3217 init_request("ti", temporary_indent
);
3218 init_request("tc", tab_character
);
3219 init_request("tl", title
);
3220 init_request("ul", underline
);
3221 init_request("vs", vertical_spacing
);
3222 #ifdef WIDOW_CONTROL
3223 init_request("wdc", widow_control_request
);
3224 #endif /* WIDOW_CONTROL */
3225 init_int_env_reg(".b", get_bold
);
3226 init_vunits_env_reg(".cdp", get_prev_char_depth
);
3227 init_int_env_reg(".ce", get_center_lines
);
3228 init_vunits_env_reg(".cht", get_prev_char_height
);
3229 init_hunits_env_reg(".csk", get_prev_char_skew
);
3230 init_string_env_reg(".ev", get_name_string
);
3231 init_int_env_reg(".f", get_font
);
3232 init_string_env_reg(".fam", get_font_family_string
);
3233 init_string_env_reg(".fn", get_font_name_string
);
3234 init_int_env_reg(".height", get_char_height
);
3235 init_int_env_reg(".hlc", get_hyphen_line_count
);
3236 init_int_env_reg(".hlm", get_hyphen_line_max
);
3237 init_int_env_reg(".hy", get_hyphenation_flags
);
3238 init_hunits_env_reg(".hym", get_hyphenation_margin
);
3239 init_hunits_env_reg(".hys", get_hyphenation_space
);
3240 init_hunits_env_reg(".i", get_indent
);
3241 init_hunits_env_reg(".in", get_saved_indent
);
3242 init_int_env_reg(".int", get_prev_line_interrupted
);
3243 init_int_env_reg(".linetabs", get_line_tabs
);
3244 init_hunits_env_reg(".lt", get_title_length
);
3245 init_int_env_reg(".j", get_adjust_mode
);
3246 init_hunits_env_reg(".k", get_text_length
);
3247 init_int_env_reg(".L", get_line_spacing
);
3248 init_hunits_env_reg(".l", get_line_length
);
3249 init_hunits_env_reg(".ll", get_saved_line_length
);
3250 init_hunits_env_reg(".n", get_prev_text_length
);
3251 init_int_env_reg(".ps", get_point_size
);
3252 init_int_env_reg(".psr", get_requested_point_size
);
3253 init_vunits_env_reg(".pvs", get_post_vertical_spacing
);
3254 init_int_env_reg(".rj", get_right_justify_lines
);
3255 init_string_env_reg(".s", get_point_size_string
);
3256 init_int_env_reg(".slant", get_char_slant
);
3257 init_int_env_reg(".ss", get_space_size
);
3258 init_int_env_reg(".sss", get_sentence_space_size
);
3259 init_string_env_reg(".sr", get_requested_point_size_string
);
3260 init_string_env_reg(".tabs", get_tabs
);
3261 init_int_env_reg(".u", get_fill
);
3262 init_vunits_env_reg(".v", get_vertical_spacing
);
3263 init_hunits_env_reg(".w", get_prev_char_width
);
3264 number_reg_dictionary
.define("ct", new variable_reg(&ct_reg_contents
));
3265 number_reg_dictionary
.define("hp", new horizontal_place_reg
);
3266 number_reg_dictionary
.define("ln", new variable_reg(&next_line_number
));
3267 number_reg_dictionary
.define("rsb", new variable_reg(&rsb_reg_contents
));
3268 number_reg_dictionary
.define("rst", new variable_reg(&rst_reg_contents
));
3269 number_reg_dictionary
.define("sb", new variable_reg(&sb_reg_contents
));
3270 number_reg_dictionary
.define("skw", new variable_reg(&skw_reg_contents
));
3271 number_reg_dictionary
.define("ssc", new variable_reg(&ssc_reg_contents
));
3272 number_reg_dictionary
.define("st", new variable_reg(&st_reg_contents
));
3275 // Hyphenation - TeX's hyphenation algorithm with a less fancy implementation.
3281 virtual void do_match(int len
, void *val
) = 0;
3282 virtual void do_delete(void *) = 0;
3283 void delete_trie_node(trie_node
*);
3286 virtual ~trie(); // virtual to shut up g++
3287 void insert(const char *, int, void *);
3288 // find calls do_match for each match it finds
3289 void find(const char *pat
, int patlen
);
3293 class hyphen_trie
: private trie
{
3295 void do_match(int i
, void *v
);
3296 void do_delete(void *v
);
3297 void insert_pattern(const char *pat
, int patlen
, int *num
);
3298 void insert_hyphenation(dictionary
*ex
, const char *pat
, int patlen
);
3299 int hpf_getc(FILE *f
);
3303 void hyphenate(const char *word
, int len
, int *hyphens
);
3304 void read_patterns_file(const char *name
, int append
, dictionary
*ex
);
3307 struct hyphenation_language
{
3309 dictionary exceptions
;
3310 hyphen_trie patterns
;
3311 hyphenation_language(symbol nm
) : name(nm
), exceptions(501) {}
3312 ~hyphenation_language() { }
3315 dictionary
language_dictionary(5);
3316 hyphenation_language
*current_language
= 0;
3318 static void set_hyphenation_language()
3320 symbol nm
= get_name(1);
3321 if (!nm
.is_null()) {
3322 current_language
= (hyphenation_language
*)language_dictionary
.lookup(nm
);
3323 if (!current_language
) {
3324 current_language
= new hyphenation_language(nm
);
3325 (void)language_dictionary
.lookup(nm
, (void *)current_language
);
3331 const int WORD_MAX
= 256; // we use unsigned char for offsets in
3332 // hyphenation exceptions
3334 static void hyphen_word()
3336 if (!current_language
) {
3337 error("no current hyphenation language");
3341 char buf
[WORD_MAX
+ 1];
3342 unsigned char pos
[WORD_MAX
+ 2];
3345 if (tok
.newline() || tok
.eof())
3349 while (i
< WORD_MAX
&& !tok
.space() && !tok
.newline() && !tok
.eof()) {
3350 charinfo
*ci
= tok
.get_char(1);
3356 if (ci
->get_ascii_code() == '-') {
3357 if (i
> 0 && (npos
== 0 || pos
[npos
- 1] != i
))
3361 int c
= ci
->get_hyphenation_code();
3370 unsigned char *tem
= new unsigned char[npos
+ 1];
3371 memcpy(tem
, pos
, npos
+ 1);
3372 tem
= (unsigned char *)current_language
->exceptions
.lookup(symbol(buf
),
3386 trie_node(char, trie_node
*);
3389 trie_node::trie_node(char ch
, trie_node
*p
)
3390 : c(ch
), down(0), right(p
), val(0)
3401 delete_trie_node(tp
);
3406 void trie::delete_trie_node(trie_node
*p
)
3409 delete_trie_node(p
->down
);
3410 delete_trie_node(p
->right
);
3417 void trie::insert(const char *pat
, int patlen
, void *val
)
3419 trie_node
**p
= &tp
;
3420 assert(patlen
> 0 && pat
!= 0);
3422 while (*p
!= 0 && (*p
)->c
< pat
[0])
3424 if (*p
== 0 || (*p
)->c
!= pat
[0])
3425 *p
= new trie_node(pat
[0], *p
);
3426 if (--patlen
== 0) {
3435 void trie::find(const char *pat
, int patlen
)
3438 for (int i
= 0; p
!= 0 && i
< patlen
; i
++) {
3439 while (p
!= 0 && p
->c
< pat
[i
])
3441 if (p
!= 0 && p
->c
== pat
[i
]) {
3443 do_match(i
+1, p
->val
);
3455 operation(int, int, operation
*);
3458 operation::operation(int i
, int j
, operation
*op
)
3459 : next(op
), distance(j
), num(i
)
3463 void hyphen_trie::insert_pattern(const char *pat
, int patlen
, int *num
)
3466 for (int i
= 0; i
< patlen
+1; i
++)
3468 op
= new operation(num
[i
], patlen
- i
, op
);
3469 insert(pat
, patlen
, op
);
3472 void hyphen_trie::insert_hyphenation(dictionary
*ex
, const char *pat
,
3475 char buf
[WORD_MAX
+ 1];
3476 unsigned char pos
[WORD_MAX
+ 2];
3479 while (j
< patlen
) {
3480 unsigned char c
= pat
[j
++];
3482 if (i
> 0 && (npos
== 0 || pos
[npos
- 1] != i
))
3486 buf
[i
++] = hpf_code_table
[c
];
3491 unsigned char *tem
= new unsigned char[npos
+ 1];
3492 memcpy(tem
, pos
, npos
+ 1);
3493 tem
= (unsigned char *)ex
->lookup(symbol(buf
), tem
);
3499 void hyphen_trie::hyphenate(const char *word
, int len
, int *hyphens
)
3502 for (j
= 0; j
< len
+ 1; j
++)
3504 for (j
= 0; j
< len
- 1; j
++) {
3506 find(word
+ j
, len
- j
);
3510 inline int max(int m
, int n
)
3512 return m
> n
? m
: n
;
3515 void hyphen_trie::do_match(int i
, void *v
)
3517 operation
*op
= (operation
*)v
;
3519 h
[i
- op
->distance
] = max(h
[i
- op
->distance
], op
->num
);
3524 void hyphen_trie::do_delete(void *v
)
3526 operation
*op
= (operation
*)v
;
3528 operation
*tem
= op
;
3534 /* We use very simple rules to parse TeX's hyphenation patterns.
3536 . `%' starts a comment even if preceded by `\'.
3538 . No support for digraphs and like `\$'.
3540 . `^^xx' (`x' is 0-9 or a-f), and `^^x' (character code of `x' in the
3541 range 0-127) are recognized; other use of `^' causes an error.
3543 . No macro expansion.
3545 . We check for the expression `\patterns{...}' (possibly with
3546 whitespace before and after the braces). Everything between the
3547 braces is taken as hyphenation patterns. Consequently, `{' and `}'
3548 are not allowed in patterns.
3550 . Similarly, `\hyphenation{...}' gives a list of hyphenation
3553 . `\endinput' is recognized also.
3555 . For backwards compatibility, if `\patterns' is missing, the
3556 whole file is treated as a list of hyphenation patterns (only
3557 recognizing `%' as the start of a comment.
3561 int hyphen_trie::hpf_getc(FILE *f
)
3573 if (((c
>= '0' && c
<= '9') || (c
>= 'a' && c
<= 'f'))
3574 && ((c1
>= '0' && c1
<= '9') || (c1
>= 'a' && c1
<= 'f'))) {
3575 if (c
>= '0' && c
<= '9')
3579 if (c1
>= '0' && c1
<= '9')
3587 if (c
>= 0 && c
<= 63)
3589 else if (c
>= 64 && c
<= 127)
3596 error("invalid ^, ^^x, or ^^xx character in hyphenation patterns file");
3600 void hyphen_trie::read_patterns_file(const char *name
, int append
,
3606 for (int i
= 0; i
< WORD_MAX
; i
++)
3608 int num
[WORD_MAX
+1];
3611 FILE *fp
= mac_path
->open_file(name
, &path
);
3613 error("can't find hyphenation patterns file `%1'", name
);
3616 int c
= hpf_getc(fp
);
3617 int have_patterns
= 0; // we've seen \patterns
3618 int final_pattern
= 0; // 1 if we have a trailing closing brace
3619 int have_hyphenation
= 0; // we've seen \hyphenation
3620 int final_hyphenation
= 0; // 1 if we have a trailing closing brace
3621 int have_keyword
= 0; // we've seen either \patterns or \hyphenation
3622 int traditional
= 0; // don't handle \patterns
3625 if (c
== '%') { // skip comments
3628 } while (c
!= EOF
&& c
!= '\n');
3630 if (c
== EOF
|| !csspace(c
))
3635 if (have_keyword
|| traditional
) // we are done
3637 else { // rescan file in `traditional' mode
3646 if (!(c
== '{' || c
== '}')) { // skip braces at line start
3647 do { // scan patterns
3655 } while (i
< WORD_MAX
&& c
!= EOF
&& !csspace(c
)
3656 && c
!= '%' && c
!= '{' && c
!= '}');
3659 if (i
>= 9 && !strncmp(buf
+ i
- 9, "\\patterns", 9)) {
3663 if (have_patterns
|| have_hyphenation
)
3664 error("\\patterns not allowed inside of %1 group",
3665 have_patterns
? "\\patterns" : "\\hyphenation");
3674 else if (i
>= 12 && !strncmp(buf
+ i
- 12, "\\hyphenation", 12)) {
3678 if (have_patterns
|| have_hyphenation
)
3679 error("\\hyphenation not allowed inside of %1 group",
3680 have_patterns
? "\\patterns" : "\\hyphenation");
3682 have_hyphenation
= 1;
3689 else if (strstr(buf
, "\\endinput")) {
3690 if (have_patterns
|| have_hyphenation
)
3691 error("found \\endinput inside of %1 group",
3692 have_patterns
? "\\patterns" : "\\hyphenation");
3695 else if (c
== '}') {
3696 if (have_patterns
) {
3701 else if (have_hyphenation
) {
3702 have_hyphenation
= 0;
3704 final_hyphenation
= 1;
3708 else if (c
== '{') {
3709 if (have_patterns
|| have_hyphenation
)
3710 error("`{' not allowed within %1 group",
3711 have_patterns
? "\\patterns" : "\\hyphenation");
3712 c
= hpf_getc(fp
); // skipped if not starting \patterns
3717 if (c
== '{' || c
== '}')
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 ¤t_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
);