2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004
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. */
23 #include "dictionary.h"
32 #include "macropath.h"
36 symbol
default_family("T");
38 enum { ADJUST_LEFT
= 0, ADJUST_BOTH
= 1, ADJUST_CENTER
= 3, ADJUST_RIGHT
= 5 };
40 enum { HYPHEN_LAST_LINE
= 2, HYPHEN_LAST_CHARS
= 4, HYPHEN_FIRST_CHARS
= 8 };
45 env_list(environment
*e
, env_list
*p
) : env(e
), next(p
) {}
49 const int NENVIRONMENTS
= 10;
50 environment
*env_table
[NENVIRONMENTS
];
51 dictionary
env_dictionary(10);
53 static int next_line_number
= 0;
55 charinfo
*field_delimiter_char
;
56 charinfo
*padding_indicator_char
;
58 int translate_space_to_dummy
= 0;
60 class pending_output_line
{
67 int last_line
; // Is it the last line of the paragraph?
68 #endif /* WIDOW_CONTROL */
70 pending_output_line
*next
;
72 pending_output_line(node
*, int, vunits
, vunits
, hunits
,
73 pending_output_line
* = 0);
74 ~pending_output_line();
78 friend void environment::mark_last_line();
79 friend void environment::output(node
*, int, vunits
, vunits
, hunits
);
80 #endif /* WIDOW_CONTROL */
83 pending_output_line::pending_output_line(node
*n
, int nf
, vunits v
, vunits pv
,
84 hunits w
, pending_output_line
*p
)
85 : nd(n
), no_fill(nf
), vs(v
), post_vs(pv
), width(w
),
88 #endif /* WIDOW_CONTROL */
93 pending_output_line::~pending_output_line()
98 int pending_output_line::output()
100 if (trap_sprung_flag
)
103 if (next
&& next
->last_line
&& !no_fill
) {
104 curdiv
->need(vs
+ post_vs
+ vunits(vresolution
));
105 if (trap_sprung_flag
) {
106 next
->last_line
= 0; // Try to avoid infinite loops.
111 curdiv
->output(nd
, no_fill
, vs
, post_vs
, width
);
116 void environment::output(node
*nd
, int no_fill_flag
,
117 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_flag
)
136 #endif /* WIDOW_CONTROL */
138 curdiv
->output(nd
, no_fill_flag
, 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_flag
, 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_flag
,
151 vunits vs
, vunits post_vs
,
154 if (!trap_sprung_flag
)
155 curdiv
->output(nd
, no_fill_flag
, vs
, post_vs
, width
);
157 pending_lines
= new pending_output_line(nd
, no_fill_flag
, vs
, post_vs
,
158 width
, pending_lines
);
161 void environment::output_pending_lines()
163 while (pending_lines
&& pending_lines
->output()) {
164 pending_output_line
*tem
= pending_lines
;
165 pending_lines
= pending_lines
->next
;
172 void environment::mark_last_line()
174 if (!widow_control
|| !pending_lines
)
176 for (pending_output_line
*p
= pending_lines
; p
->next
; p
= p
->next
)
182 void widow_control_request()
185 if (has_arg() && get_integer(&n
))
186 curenv
->widow_control
= n
!= 0;
188 curenv
->widow_control
= 1;
192 #endif /* WIDOW_CONTROL */
194 /* font_size functions */
196 size_range
*font_size::size_table
= 0;
197 int font_size::nranges
= 0;
201 int compare_ranges(const void *p1
, const void *p2
)
203 return ((size_range
*)p1
)->min
- ((size_range
*)p2
)->min
;
208 void font_size::init_size_table(int *sizes
)
211 while (sizes
[nranges
*2] != 0)
214 size_table
= new size_range
[nranges
];
215 for (int i
= 0; i
< nranges
; i
++) {
216 size_table
[i
].min
= sizes
[i
*2];
217 size_table
[i
].max
= sizes
[i
*2 + 1];
219 qsort(size_table
, nranges
, sizeof(size_range
), compare_ranges
);
222 font_size::font_size(int sp
)
224 for (int i
= 0; i
< nranges
; i
++) {
225 if (sp
< size_table
[i
].min
) {
226 if (i
> 0 && size_table
[i
].min
- sp
>= sp
- size_table
[i
- 1].max
)
227 p
= size_table
[i
- 1].max
;
229 p
= size_table
[i
].min
;
232 if (sp
<= size_table
[i
].max
) {
237 p
= size_table
[nranges
- 1].max
;
240 int font_size::to_units()
242 return scale(p
, units_per_inch
, sizescale
*72);
245 // we can't do this in a static constructor because various dictionaries
246 // have to get initialized first
248 void init_environments()
250 curenv
= env_table
[0] = new environment("0");
255 curenv
->tab_char
= get_optional_char();
259 void leader_character()
261 curenv
->leader_char
= get_optional_char();
265 void environment::add_char(charinfo
*ci
)
270 // don't allow fields in dummy environments
271 else if (ci
== field_delimiter_char
&& !dummy
) {
277 else if (current_field
&& ci
== padding_indicator_char
)
279 else if (current_tab
) {
280 if (tab_contents
== 0)
281 tab_contents
= new line_start_node
;
282 if (ci
!= hyphen_indicator_char
)
283 tab_contents
= tab_contents
->add_char(ci
, this, &tab_width
, &s
);
285 tab_contents
= tab_contents
->add_discretionary_hyphen();
290 if (ci
!= hyphen_indicator_char
)
291 line
= line
->add_char(ci
, this, &width_total
, &space_total
);
293 line
= line
->add_discretionary_hyphen();
297 node
*environment::make_char_node(charinfo
*ci
)
299 return make_node(ci
, this);
302 void environment::add_node(node
*n
)
306 if (current_tab
|| current_field
)
311 else if (current_tab
) {
312 n
->next
= tab_contents
;
314 tab_width
+= n
->width();
318 if (discarding
&& n
->discardable()) {
319 // XXX possibly: input_line_start -= n->width();
325 width_total
+= n
->width();
326 space_total
+= n
->nspaces();
333 void environment::add_hyphen_indicator()
335 if (current_tab
|| interrupted
|| current_field
336 || hyphen_indicator_char
!= 0)
340 line
= line
->add_discretionary_hyphen();
343 int environment::get_hyphenation_flags()
345 return hyphenation_flags
;
348 int environment::get_hyphen_line_max()
350 return hyphen_line_max
;
353 int environment::get_hyphen_line_count()
355 return hyphen_line_count
;
358 int environment::get_center_lines()
363 int environment::get_right_justify_lines()
365 return right_justify_lines
;
368 void environment::add_italic_correction()
372 tab_contents
= tab_contents
->add_italic_correction(&tab_width
);
375 line
= line
->add_italic_correction(&width_total
);
378 void environment::space_newline()
380 assert(!current_tab
&& !current_field
);
384 hunits sw
= env_space_width(this);
385 hunits ssw
= env_sentence_space_width(this);
386 if (!translate_space_to_dummy
) {
388 if (node_list_ends_sentence(line
) == 1)
391 width_list
*w
= new width_list(sw
, ssw
);
392 if (node_list_ends_sentence(line
) == 1)
393 w
->next
= new width_list(sw
, ssw
);
394 if (line
!= 0 && line
->merge_space(x
, sw
, ssw
)) {
398 add_node(new word_space_node(x
, get_fill_color(), w
));
399 possibly_break_line(0, spread_flag
);
403 void environment::space()
405 space(env_space_width(this), env_sentence_space_width(this));
408 void environment::space(hunits space_width
, hunits sentence_space_width
)
412 if (current_field
&& padding_indicator_char
== 0) {
416 hunits x
= translate_space_to_dummy
? H0
: space_width
;
417 node
*p
= current_tab
? tab_contents
: line
;
418 hunits
*tp
= current_tab
? &tab_width
: &width_total
;
419 if (p
&& p
->nspaces() == 1 && p
->width() == x
420 && node_list_ends_sentence(p
->next
) == 1) {
421 hunits xx
= translate_space_to_dummy
? H0
: sentence_space_width
;
422 if (p
->merge_space(xx
, space_width
, sentence_space_width
)) {
427 if (p
&& p
->merge_space(x
, space_width
, sentence_space_width
)) {
431 add_node(new word_space_node(x
,
433 new width_list(space_width
,
434 sentence_space_width
)));
435 possibly_break_line(0, spread_flag
);
439 node
*do_underline_special(int);
441 void environment::set_font(symbol nm
)
445 if (nm
== symbol("P") || nm
.is_empty()) {
446 if (family
->make_definite(prev_fontno
) < 0)
449 fontno
= prev_fontno
;
453 prev_fontno
= fontno
;
454 int n
= symbol_fontno(nm
);
456 n
= next_available_font_position();
457 if (!mount_font(n
, nm
))
460 if (family
->make_definite(n
) < 0)
464 if (underline_spaces
&& fontno
!= prev_fontno
) {
465 if (fontno
== get_underline_fontno())
466 add_node(do_underline_special(1));
467 if (prev_fontno
== get_underline_fontno())
468 add_node(do_underline_special(0));
472 void environment::set_font(int n
)
476 if (is_good_fontno(n
)) {
477 prev_fontno
= fontno
;
481 warning(WARN_FONT
, "bad font number");
484 void environment::set_family(symbol fam
)
488 if (fam
.is_null() || fam
.is_empty()) {
489 if (prev_family
->make_definite(fontno
) < 0)
491 font_family
*tem
= family
;
492 family
= prev_family
;
496 font_family
*f
= lookup_family(fam
);
497 if (f
->make_definite(fontno
) < 0)
499 prev_family
= family
;
504 void environment::set_size(int n
)
509 font_size temp
= prev_size
;
512 int temp2
= prev_requested_size
;
513 prev_requested_size
= requested_size
;
514 requested_size
= temp2
;
519 prev_requested_size
= requested_size
;
524 void environment::set_char_height(int n
)
528 if (n
== requested_size
|| n
<= 0)
534 void environment::set_char_slant(int n
)
541 color
*environment::get_prev_glyph_color()
543 return prev_glyph_color
;
546 color
*environment::get_glyph_color()
551 color
*environment::get_prev_fill_color()
553 return prev_fill_color
;
556 color
*environment::get_fill_color()
561 void environment::set_glyph_color(color
*c
)
565 curenv
->prev_glyph_color
= curenv
->glyph_color
;
566 curenv
->glyph_color
= c
;
569 void environment::set_fill_color(color
*c
)
573 curenv
->prev_fill_color
= curenv
->fill_color
;
574 curenv
->fill_color
= c
;
577 environment::environment(symbol nm
)
579 prev_line_length((units_per_inch
*13)/2),
580 line_length((units_per_inch
*13)/2),
581 prev_title_length((units_per_inch
*13)/2),
582 title_length((units_per_inch
*13)/2),
583 prev_size(sizescale
*10),
585 requested_size(sizescale
*10),
586 prev_requested_size(sizescale
*10),
590 sentence_space_size(12),
591 adjust_mode(ADJUST_BOTH
),
594 prev_line_interrupted(0),
596 right_justify_lines(0),
597 prev_vertical_spacing(points_to_units(12)),
598 vertical_spacing(points_to_units(12)),
599 prev_post_vertical_spacing(0),
600 post_vertical_spacing(0),
601 prev_line_spacing(1),
606 have_temporary_indent(0),
610 continued_input_trap(0),
616 tabs(units_per_inch
/2, TAB_LEFT
),
618 current_tab(TAB_NONE
),
621 leader_char(charset_table
['.']),
625 margin_character_flags(0),
626 margin_character_node(0),
627 margin_character_distance(points_to_units(10)),
629 number_text_separation(1),
630 line_number_indent(0),
631 line_number_multiple(1),
633 hyphenation_flags(1),
634 hyphen_line_count(0),
636 hyphenation_space(H0
),
637 hyphenation_margin(H0
),
642 #endif /* WIDOW_CONTROL */
645 glyph_color(&default_color
),
646 prev_glyph_color(&default_color
),
647 fill_color(&default_color
),
648 prev_fill_color(&default_color
),
651 no_break_control_char('\''),
652 hyphen_indicator_char(0)
654 prev_family
= family
= lookup_family(default_family
);
655 prev_fontno
= fontno
= 1;
656 if (!is_good_fontno(1))
657 fatal("font number 1 not a valid font");
658 if (family
->make_definite(1) < 0)
659 fatal("invalid default family `%1'", default_family
.contents());
660 prev_fontno
= fontno
;
663 environment::environment(const environment
*e
)
665 prev_line_length(e
->prev_line_length
),
666 line_length(e
->line_length
),
667 prev_title_length(e
->prev_title_length
),
668 title_length(e
->title_length
),
669 prev_size(e
->prev_size
),
671 requested_size(e
->requested_size
),
672 prev_requested_size(e
->prev_requested_size
),
673 char_height(e
->char_height
),
674 char_slant(e
->char_slant
),
675 prev_fontno(e
->prev_fontno
),
677 prev_family(e
->prev_family
),
679 space_size(e
->space_size
),
680 sentence_space_size(e
->sentence_space_size
),
681 adjust_mode(e
->adjust_mode
),
684 prev_line_interrupted(0),
686 right_justify_lines(0),
687 prev_vertical_spacing(e
->prev_vertical_spacing
),
688 vertical_spacing(e
->vertical_spacing
),
689 prev_post_vertical_spacing(e
->prev_post_vertical_spacing
),
690 post_vertical_spacing(e
->post_vertical_spacing
),
691 prev_line_spacing(e
->prev_line_spacing
),
692 line_spacing(e
->line_spacing
),
693 prev_indent(e
->prev_indent
),
696 have_temporary_indent(0),
700 continued_input_trap(0),
702 prev_text_length(e
->prev_text_length
),
707 line_tabs(e
->line_tabs
),
708 current_tab(TAB_NONE
),
710 tab_char(e
->tab_char
),
711 leader_char(e
->leader_char
),
715 margin_character_flags(e
->margin_character_flags
),
716 margin_character_node(e
->margin_character_node
),
717 margin_character_distance(e
->margin_character_distance
),
719 number_text_separation(e
->number_text_separation
),
720 line_number_indent(e
->line_number_indent
),
721 line_number_multiple(e
->line_number_multiple
),
722 no_number_count(e
->no_number_count
),
723 hyphenation_flags(e
->hyphenation_flags
),
724 hyphen_line_count(0),
725 hyphen_line_max(e
->hyphen_line_max
),
726 hyphenation_space(e
->hyphenation_space
),
727 hyphenation_margin(e
->hyphenation_margin
),
731 widow_control(e
->widow_control
),
732 #endif /* WIDOW_CONTROL */
735 glyph_color(e
->glyph_color
),
736 prev_glyph_color(e
->prev_glyph_color
),
737 fill_color(e
->fill_color
),
738 prev_fill_color(e
->prev_fill_color
),
739 name(e
->name
), // so that eg `.if "\n[.ev]"0"' works
740 control_char(e
->control_char
),
741 no_break_control_char(e
->no_break_control_char
),
742 hyphen_indicator_char(e
->hyphen_indicator_char
)
746 void environment::copy(const environment
*e
)
748 prev_line_length
= e
->prev_line_length
;
749 line_length
= e
->line_length
;
750 prev_title_length
= e
->prev_title_length
;
751 title_length
= e
->title_length
;
752 prev_size
= e
->prev_size
;
754 prev_requested_size
= e
->prev_requested_size
;
755 requested_size
= e
->requested_size
;
756 char_height
= e
->char_height
;
757 char_slant
= e
->char_slant
;
758 space_size
= e
->space_size
;
759 sentence_space_size
= e
->sentence_space_size
;
760 adjust_mode
= e
->adjust_mode
;
763 prev_line_interrupted
= 0;
765 right_justify_lines
= 0;
766 prev_vertical_spacing
= e
->prev_vertical_spacing
;
767 vertical_spacing
= e
->vertical_spacing
;
768 prev_post_vertical_spacing
= e
->prev_post_vertical_spacing
,
769 post_vertical_spacing
= e
->post_vertical_spacing
,
770 prev_line_spacing
= e
->prev_line_spacing
;
771 line_spacing
= e
->line_spacing
;
772 prev_indent
= e
->prev_indent
;
774 have_temporary_indent
= 0;
775 temporary_indent
= 0;
777 underline_spaces
= 0;
778 input_trap_count
= 0;
779 continued_input_trap
= 0;
780 prev_text_length
= e
->prev_text_length
;
783 input_line_start
= 0;
784 control_char
= e
->control_char
;
785 no_break_control_char
= e
->no_break_control_char
;
786 hyphen_indicator_char
= e
->hyphen_indicator_char
;
792 line_tabs
= e
->line_tabs
;
793 current_tab
= TAB_NONE
;
795 margin_character_flags
= e
->margin_character_flags
;
796 margin_character_node
= e
->margin_character_node
;
797 margin_character_distance
= e
->margin_character_distance
;
799 number_text_separation
= e
->number_text_separation
;
800 line_number_multiple
= e
->line_number_multiple
;
801 line_number_indent
= e
->line_number_indent
;
802 no_number_count
= e
->no_number_count
;
803 tab_char
= e
->tab_char
;
804 leader_char
= e
->leader_char
;
805 hyphenation_flags
= e
->hyphenation_flags
;
807 prev_fontno
= e
->prev_fontno
;
810 prev_family
= e
->prev_family
;
813 widow_control
= e
->widow_control
;
814 #endif /* WIDOW_CONTROL */
815 hyphen_line_max
= e
->hyphen_line_max
;
816 hyphen_line_count
= 0;
817 hyphenation_space
= e
->hyphenation_space
;
818 hyphenation_margin
= e
->hyphenation_margin
;
820 ignore_next_eol
= e
->ignore_next_eol
;
821 emitted_node
= e
->emitted_node
;
822 glyph_color
= e
->glyph_color
;
823 prev_glyph_color
= e
->prev_glyph_color
;
824 fill_color
= e
->fill_color
;
825 prev_fill_color
= e
->prev_fill_color
;
828 environment::~environment()
831 delete_node_list(line
);
832 delete_node_list(numbering_nodes
);
835 hunits
environment::get_input_line_position()
839 n
= -input_line_start
;
841 n
= width_total
- input_line_start
;
847 void environment::set_input_line_position(hunits n
)
849 input_line_start
= line
== 0 ? -n
: width_total
- n
;
851 input_line_start
+= tab_width
;
854 hunits
environment::get_line_length()
859 hunits
environment::get_saved_line_length()
862 return target_text_length
+ saved_indent
;
867 vunits
environment::get_vertical_spacing()
869 return vertical_spacing
;
872 vunits
environment::get_post_vertical_spacing()
874 return post_vertical_spacing
;
877 int environment::get_line_spacing()
882 vunits
environment::total_post_vertical_spacing()
884 vunits
tem(post_vertical_spacing
);
885 if (line_spacing
> 1)
886 tem
+= (line_spacing
- 1)*vertical_spacing
;
890 int environment::get_bold()
892 return get_bold_fontno(fontno
);
895 hunits
environment::get_digit_width()
897 return env_digit_width(this);
900 int environment::get_adjust_mode()
905 int environment::get_fill()
910 hunits
environment::get_indent()
915 hunits
environment::get_saved_indent()
919 else if (have_temporary_indent
)
920 return temporary_indent
;
925 hunits
environment::get_temporary_indent()
927 return temporary_indent
;
930 hunits
environment::get_title_length()
935 node
*environment::get_prev_char()
937 for (node
*n
= current_tab
? tab_contents
: line
; n
; n
= n
->next
) {
938 node
*last
= n
->last_char_node();
945 hunits
environment::get_prev_char_width()
947 node
*last
= get_prev_char();
950 return last
->width();
953 hunits
environment::get_prev_char_skew()
955 node
*last
= get_prev_char();
961 vunits
environment::get_prev_char_height()
963 node
*last
= get_prev_char();
967 last
->vertical_extent(&min
, &max
);
971 vunits
environment::get_prev_char_depth()
973 node
*last
= get_prev_char();
977 last
->vertical_extent(&min
, &max
);
981 hunits
environment::get_text_length()
983 hunits n
= line
== 0 ? H0
: width_total
;
989 hunits
environment::get_prev_text_length()
991 return prev_text_length
;
995 static int sb_reg_contents
= 0;
996 static int st_reg_contents
= 0;
997 static int ct_reg_contents
= 0;
998 static int rsb_reg_contents
= 0;
999 static int rst_reg_contents
= 0;
1000 static int skw_reg_contents
= 0;
1001 static int ssc_reg_contents
= 0;
1003 void environment::width_registers()
1005 // this is used to implement \w; it sets the st, sb, ct registers
1006 vunits min
= 0, max
= 0, cur
= 0;
1007 int character_type
= 0;
1008 ssc_reg_contents
= line
? line
->subscript_correction().to_units() : 0;
1009 skw_reg_contents
= line
? line
->skew().to_units() : 0;
1010 line
= reverse_node_list(line
);
1011 vunits real_min
= V0
;
1012 vunits real_max
= V0
;
1014 for (node
*tem
= line
; tem
; tem
= tem
->next
) {
1015 tem
->vertical_extent(&v1
, &v2
);
1022 if ((cur
+= tem
->vertical_width()) < min
)
1026 character_type
|= tem
->character_type();
1028 line
= reverse_node_list(line
);
1029 st_reg_contents
= -min
.to_units();
1030 sb_reg_contents
= -max
.to_units();
1031 rst_reg_contents
= -real_min
.to_units();
1032 rsb_reg_contents
= -real_max
.to_units();
1033 ct_reg_contents
= character_type
;
1036 node
*environment::extract_output_line()
1045 /* environment related requests */
1047 void environment_switch()
1049 int pop
= 0; // 1 means pop, 2 means pop but no error message on underflow
1050 if (curenv
->is_dummy())
1051 error("can't switch environments when current environment is dummy");
1052 else if (!has_arg())
1056 if (!tok
.delimiter()) {
1057 // It looks like a number.
1059 if (get_integer(&n
)) {
1060 if (n
>= 0 && n
< NENVIRONMENTS
) {
1061 env_stack
= new env_list(curenv
, env_stack
);
1062 if (env_table
[n
] == 0)
1063 env_table
[n
] = new environment(i_to_a(n
));
1064 curenv
= env_table
[n
];
1073 nm
= get_long_name(1);
1077 if (!nm
.is_null()) {
1078 environment
*e
= (environment
*)env_dictionary
.lookup(nm
);
1080 e
= new environment(nm
);
1081 (void)env_dictionary
.lookup(nm
, e
);
1083 env_stack
= new env_list(curenv
, env_stack
);
1088 if (env_stack
== 0) {
1090 error("environment stack underflow");
1093 curenv
= env_stack
->env
;
1094 env_list
*tem
= env_stack
;
1095 env_stack
= env_stack
->next
;
1102 void environment_copy()
1107 if (!tok
.delimiter()) {
1108 // It looks like a number.
1110 if (get_integer(&n
)) {
1111 if (n
>= 0 && n
< NENVIRONMENTS
)
1118 nm
= get_long_name(1);
1119 if (!e
&& !nm
.is_null())
1120 e
= (environment
*)env_dictionary
.lookup(nm
);
1122 error("No environment to copy from");
1130 static symbol
P_symbol("P");
1134 symbol s
= get_name();
1136 if (s
.is_null() || s
== P_symbol
) {
1141 for (const char *p
= s
.contents(); p
!= 0 && *p
!= 0; p
++)
1148 curenv
->set_font(atoi(s
.contents()));
1150 curenv
->set_font(s
);
1154 void family_change()
1156 symbol s
= get_name();
1157 curenv
->set_family(s
);
1164 if (has_arg() && get_number(&n
, 'z', curenv
->get_requested_point_size())) {
1167 curenv
->set_size(n
);
1168 curenv
->add_html_tag(0, ".ps", n
);
1171 curenv
->set_size(0);
1175 void override_sizes()
1178 int *sizes
= new int[n
];
1180 char *buf
= read_string();
1183 char *p
= strtok(buf
, " \t");
1188 switch (sscanf(p
, "%d-%d", &lower
, &upper
)) {
1193 if (lower
<= upper
&& lower
>= 0)
1197 warning(WARN_RANGE
, "bad size range `%1'", p
);
1201 int *old_sizes
= sizes
;
1202 sizes
= new int[n
*2];
1203 memcpy(sizes
, old_sizes
, n
*sizeof(int));
1211 p
= strtok(0, " \t");
1213 font_size::init_size_table(sizes
);
1219 if (get_integer(&n
)) {
1220 curenv
->space_size
= n
;
1221 if (has_arg() && get_integer(&n
))
1222 curenv
->sentence_space_size
= n
;
1224 curenv
->sentence_space_size
= curenv
->space_size
;
1231 while (!tok
.newline() && !tok
.eof())
1236 curenv
->add_html_tag(1, ".fi");
1237 curenv
->add_html_tag(0, ".br");
1243 while (!tok
.newline() && !tok
.eof())
1248 curenv
->add_html_tag(1, ".nf");
1249 curenv
->add_html_tag(0, ".br");
1250 curenv
->add_html_tag(0, ".po", topdiv
->get_page_offset().to_units());
1257 if (!has_arg() || !get_integer(&n
))
1261 while (!tok
.newline() && !tok
.eof())
1265 curenv
->right_justify_lines
= 0;
1266 curenv
->center_lines
= n
;
1267 curenv
->add_html_tag(1, ".ce", n
);
1271 void right_justify()
1274 if (!has_arg() || !get_integer(&n
))
1278 while (!tok
.newline() && !tok
.eof())
1282 curenv
->center_lines
= 0;
1283 curenv
->right_justify_lines
= n
;
1284 curenv
->add_html_tag(1, ".rj", n
);
1291 if (has_arg() && get_hunits(&temp
, 'm', curenv
->line_length
)) {
1293 warning(WARN_RANGE
, "bad line length %1u", temp
.to_units());
1298 temp
= curenv
->prev_line_length
;
1299 curenv
->prev_line_length
= curenv
->line_length
;
1300 curenv
->line_length
= temp
;
1301 curenv
->add_html_tag(1, ".ll", temp
.to_units());
1308 if (has_arg() && get_hunits(&temp
, 'm', curenv
->title_length
)) {
1310 warning(WARN_RANGE
, "bad title length %1u", temp
.to_units());
1315 temp
= curenv
->prev_title_length
;
1316 curenv
->prev_title_length
= curenv
->title_length
;
1317 curenv
->title_length
= temp
;
1321 void vertical_spacing()
1324 if (has_arg() && get_vunits(&temp
, 'p', curenv
->vertical_spacing
)) {
1326 warning(WARN_RANGE
, "vertical spacing must not be negative");
1331 temp
= curenv
->prev_vertical_spacing
;
1332 curenv
->prev_vertical_spacing
= curenv
->vertical_spacing
;
1333 curenv
->vertical_spacing
= temp
;
1337 void post_vertical_spacing()
1340 if (has_arg() && get_vunits(&temp
, 'p', curenv
->post_vertical_spacing
)) {
1343 "post vertical spacing must be greater than or equal to 0");
1348 temp
= curenv
->prev_post_vertical_spacing
;
1349 curenv
->prev_post_vertical_spacing
= curenv
->post_vertical_spacing
;
1350 curenv
->post_vertical_spacing
= temp
;
1357 if (has_arg() && get_integer(&temp
)) {
1359 warning(WARN_RANGE
, "value %1 out of range: interpreted as 1", temp
);
1364 temp
= curenv
->prev_line_spacing
;
1365 curenv
->prev_line_spacing
= curenv
->line_spacing
;
1366 curenv
->line_spacing
= temp
;
1373 if (has_arg() && get_hunits(&temp
, 'm', curenv
->indent
)) {
1375 warning(WARN_RANGE
, "indent cannot be negative");
1380 temp
= curenv
->prev_indent
;
1381 while (!tok
.newline() && !tok
.eof())
1385 curenv
->have_temporary_indent
= 0;
1386 curenv
->prev_indent
= curenv
->indent
;
1387 curenv
->indent
= temp
;
1389 curenv
->add_html_tag(1, ".in", temp
.to_units());
1393 void temporary_indent()
1397 if (!get_hunits(&temp
, 'm', curenv
->get_indent()))
1399 while (!tok
.newline() && !tok
.eof())
1404 warning(WARN_RANGE
, "total indent cannot be negative");
1408 curenv
->temporary_indent
= temp
;
1409 curenv
->have_temporary_indent
= 1;
1410 curenv
->add_html_tag(1, ".ti", temp
.to_units());
1415 node
*do_underline_special(int underline_spaces
)
1418 m
.append_str("x u ");
1419 m
.append(underline_spaces
+ '0');
1420 return new special_node(m
, 1);
1423 void do_underline(int underline_spaces
)
1426 if (!has_arg() || !get_integer(&n
))
1429 if (curenv
->underline_lines
> 0) {
1430 curenv
->prev_fontno
= curenv
->fontno
;
1431 curenv
->fontno
= curenv
->pre_underline_fontno
;
1432 if (underline_spaces
) {
1433 curenv
->underline_spaces
= 0;
1434 curenv
->add_node(do_underline_special(0));
1437 curenv
->underline_lines
= 0;
1440 curenv
->underline_lines
= n
;
1441 curenv
->pre_underline_fontno
= curenv
->fontno
;
1442 curenv
->fontno
= get_underline_fontno();
1443 if (underline_spaces
) {
1444 curenv
->underline_spaces
= 1;
1445 curenv
->add_node(do_underline_special(1));
1451 void continuous_underline()
1463 curenv
->control_char
= '.';
1466 error("bad control character");
1468 curenv
->control_char
= tok
.ch();
1473 void no_break_control_char()
1475 curenv
->no_break_control_char
= '\'';
1478 error("bad control character");
1480 curenv
->no_break_control_char
= tok
.ch();
1485 void margin_character()
1489 charinfo
*ci
= tok
.get_char();
1491 // Call tok.next() only after making the node so that
1492 // .mc \s+9\(br\s0 works.
1493 node
*nd
= curenv
->make_char_node(ci
);
1496 delete curenv
->margin_character_node
;
1497 curenv
->margin_character_node
= nd
;
1498 curenv
->margin_character_flags
= (MARGIN_CHARACTER_ON
1499 |MARGIN_CHARACTER_NEXT
);
1501 if (has_arg() && get_hunits(&d
, 'm'))
1502 curenv
->margin_character_distance
= d
;
1506 check_missing_character();
1507 curenv
->margin_character_flags
&= ~MARGIN_CHARACTER_ON
;
1508 if (curenv
->margin_character_flags
== 0) {
1509 delete curenv
->margin_character_node
;
1510 curenv
->margin_character_node
= 0;
1518 delete_node_list(curenv
->numbering_nodes
);
1519 curenv
->numbering_nodes
= 0;
1522 for (int i
= '9'; i
>= '0'; i
--) {
1523 node
*tem
= make_node(charset_table
[i
], curenv
);
1531 curenv
->numbering_nodes
= nd
;
1532 curenv
->line_number_digit_width
= env_digit_width(curenv
);
1534 if (!tok
.delimiter()) {
1535 if (get_integer(&n
, next_line_number
)) {
1536 next_line_number
= n
;
1537 if (next_line_number
< 0) {
1538 warning(WARN_RANGE
, "negative line number");
1539 next_line_number
= 0;
1544 while (!tok
.space() && !tok
.newline() && !tok
.eof())
1547 if (!tok
.delimiter()) {
1548 if (get_integer(&n
)) {
1550 warning(WARN_RANGE
, "negative or zero line number multiple");
1553 curenv
->line_number_multiple
= n
;
1557 while (!tok
.space() && !tok
.newline() && !tok
.eof())
1560 if (!tok
.delimiter()) {
1561 if (get_integer(&n
))
1562 curenv
->number_text_separation
= n
;
1565 while (!tok
.space() && !tok
.newline() && !tok
.eof())
1567 if (has_arg() && !tok
.delimiter() && get_integer(&n
))
1568 curenv
->line_number_indent
= n
;
1578 if (has_arg() && get_integer(&n
))
1579 curenv
->no_number_count
= n
> 0 ? n
: 0;
1581 curenv
->no_number_count
= 1;
1587 curenv
->hyphenation_flags
= 0;
1591 void hyphenate_request()
1594 if (has_arg() && get_integer(&n
))
1595 curenv
->hyphenation_flags
= n
;
1597 curenv
->hyphenation_flags
= 1;
1603 curenv
->hyphen_indicator_char
= get_optional_char();
1607 void hyphen_line_max_request()
1610 if (has_arg() && get_integer(&n
))
1611 curenv
->hyphen_line_max
= n
;
1613 curenv
->hyphen_line_max
= -1;
1617 void environment::interrupt()
1620 add_node(new transparent_dummy_node
);
1625 void environment::newline()
1627 if (underline_lines
> 0) {
1628 if (--underline_lines
== 0) {
1629 prev_fontno
= fontno
;
1630 fontno
= pre_underline_fontno
;
1631 if (underline_spaces
) {
1632 underline_spaces
= 0;
1633 add_node(do_underline_special(0));
1641 // strip trailing spaces
1642 while (line
!= 0 && line
->discardable()) {
1643 width_total
-= line
->width();
1644 space_total
-= line
->nspaces();
1649 node
*to_be_output
= 0;
1650 hunits to_be_output_width
;
1651 prev_line_interrupted
= 0;
1654 else if (interrupted
) {
1656 // see environment::final_break
1657 prev_line_interrupted
= exit_started
? 2 : 1;
1659 else if (center_lines
> 0) {
1661 hunits x
= target_text_length
- width_total
;
1663 saved_indent
+= x
/2;
1664 to_be_output
= line
;
1666 node
*n
= make_html_tag("eol.ce");
1667 n
->next
= to_be_output
;
1670 to_be_output_width
= width_total
;
1673 else if (right_justify_lines
> 0) {
1674 --right_justify_lines
;
1675 hunits x
= target_text_length
- width_total
;
1678 to_be_output
= line
;
1679 to_be_output_width
= width_total
;
1685 to_be_output
= line
;
1686 to_be_output_width
= width_total
;
1689 input_line_start
= line
== 0 ? H0
: width_total
;
1691 if (is_html
&& !fill
) {
1692 if (curdiv
== topdiv
) {
1693 node
*n
= make_html_tag("eol");
1695 n
->next
= to_be_output
;
1699 output_line(to_be_output
, to_be_output_width
);
1700 hyphen_line_count
= 0;
1702 if (input_trap_count
> 0) {
1703 if (!(continued_input_trap
&& prev_line_interrupted
))
1704 if (--input_trap_count
== 0)
1705 spring_trap(input_trap
);
1709 void environment::output_line(node
*n
, hunits width
)
1711 prev_text_length
= width
;
1712 if (margin_character_flags
) {
1713 hunits d
= line_length
+ margin_character_distance
- saved_indent
- width
;
1715 n
= new hmotion_node(d
, get_fill_color(), n
);
1718 margin_character_flags
&= ~MARGIN_CHARACTER_NEXT
;
1720 if (!margin_character_flags
) {
1721 tem
= margin_character_node
;
1722 margin_character_node
= 0;
1725 tem
= margin_character_node
->copy();
1728 width
+= tem
->width();
1732 node
*tem
= n
->next
;
1737 if (!saved_indent
.is_zero())
1738 nn
= new hmotion_node(saved_indent
, get_fill_color(), nn
);
1739 width
+= saved_indent
;
1740 if (no_number_count
> 0)
1742 else if (numbering_nodes
) {
1743 hunits w
= (line_number_digit_width
1744 *(3+line_number_indent
+number_text_separation
));
1745 if (next_line_number
% line_number_multiple
!= 0)
1746 nn
= new hmotion_node(w
, get_fill_color(), nn
);
1749 nn
= new hmotion_node(number_text_separation
* line_number_digit_width
,
1750 get_fill_color(), nn
);
1751 x
-= number_text_separation
*line_number_digit_width
;
1753 sprintf(buf
, "%3d", next_line_number
);
1754 for (char *p
= strchr(buf
, '\0') - 1; p
>= buf
&& *p
!= ' '; --p
) {
1755 node
*gn
= numbering_nodes
;
1756 for (int count
= *p
- '0'; count
> 0; count
--)
1763 nn
= new hmotion_node(x
, get_fill_color(), nn
);
1768 output(nn
, !fill
, vertical_spacing
, total_post_vertical_spacing(), width
);
1771 void environment::start_line()
1775 line
= new line_start_node
;
1776 if (have_temporary_indent
) {
1777 saved_indent
= temporary_indent
;
1778 have_temporary_indent
= 0;
1781 saved_indent
= indent
;
1782 target_text_length
= line_length
- saved_indent
;
1787 hunits
environment::get_hyphenation_space()
1789 return hyphenation_space
;
1792 void hyphenation_space_request()
1795 if (get_hunits(&n
, 'm')) {
1797 warning(WARN_RANGE
, "hyphenation space cannot be negative");
1800 curenv
->hyphenation_space
= n
;
1805 hunits
environment::get_hyphenation_margin()
1807 return hyphenation_margin
;
1810 void hyphenation_margin_request()
1813 if (get_hunits(&n
, 'm')) {
1815 warning(WARN_RANGE
, "hyphenation margin cannot be negative");
1818 curenv
->hyphenation_margin
= n
;
1823 breakpoint
*environment::choose_breakpoint()
1825 hunits x
= width_total
;
1826 int s
= space_total
;
1828 breakpoint
*best_bp
= 0; // the best breakpoint so far
1829 int best_bp_fits
= 0;
1833 breakpoint
*bp
= n
->get_breakpoints(x
, s
);
1835 if (bp
->width
<= target_text_length
) {
1836 if (!bp
->hyphenated
) {
1837 breakpoint
*tem
= bp
->next
;
1840 breakpoint
*tem1
= tem
;
1845 // Decide whether to use the hyphenated breakpoint.
1846 && (hyphen_line_max
< 0
1847 // Only choose the hyphenated breakpoint if it would not
1848 // exceed the maximum number of consecutive hyphenated
1850 || hyphen_line_count
+ 1 <= hyphen_line_max
)
1851 && !(adjust_mode
== ADJUST_BOTH
1852 // Don't choose the hyphenated breakpoint if the line
1853 // can be justified by adding no more than
1854 // hyphenation_space to any word space.
1856 && (((target_text_length
- bp
->width
1857 + (bp
->nspaces
- 1)*hresolution
)/bp
->nspaces
)
1858 <= hyphenation_space
))
1859 // Don't choose the hyphenated breakpoint if the line
1860 // is no more than hyphenation_margin short.
1861 : target_text_length
- bp
->width
<= hyphenation_margin
)) {
1870 if ((adjust_mode
== ADJUST_BOTH
1871 ? hyphenation_space
== H0
1872 : hyphenation_margin
== H0
)
1873 && (hyphen_line_max
< 0
1874 || hyphen_line_count
+ 1 <= hyphen_line_max
)) {
1875 // No need to consider a non-hyphenated breakpoint.
1878 breakpoint
*tem
= bp
->next
;
1881 breakpoint
*tem1
= tem
;
1887 // It fits but it's hyphenated.
1888 if (!best_bp_fits
) {
1896 breakpoint
*tem
= bp
;
1913 output_warning(WARN_BREAK
, "can't break line");
1919 void environment::hyphenate_line(int start_here
)
1922 hyphenation_type prev_type
= line
->get_hyphenation_type();
1927 for (startp
= &line
->next
; *startp
!= 0; startp
= &(*startp
)->next
) {
1928 hyphenation_type this_type
= (*startp
)->get_hyphenation_type();
1929 if (prev_type
== HYPHEN_BOUNDARY
&& this_type
== HYPHEN_MIDDLE
)
1931 prev_type
= this_type
;
1935 node
*tem
= *startp
;
1938 } while (tem
!= 0 && tem
->get_hyphenation_type() == HYPHEN_MIDDLE
);
1939 int inhibit
= (tem
!= 0 && tem
->get_hyphenation_type() == HYPHEN_INHIBIT
);
1941 hyphen_list
*sl
= 0;
1945 while (tem
!= end
) {
1946 sl
= tem
->get_hyphen_list(sl
, &i
);
1949 tem1
->next
= forward
;
1953 // this is for characters like hyphen and emdash
1955 for (hyphen_list
*h
= sl
; h
; h
= h
->next
) {
1956 h
->breakable
= (prev_code
!= 0
1958 && h
->next
->hyphenation_code
!= 0);
1959 prev_code
= h
->hyphenation_code
;
1962 if (hyphenation_flags
!= 0
1964 // this may not be right if we have extra space on this line
1965 && !((hyphenation_flags
& HYPHEN_LAST_LINE
)
1966 && (curdiv
->distance_to_next_trap()
1967 <= vertical_spacing
+ total_post_vertical_spacing()))
1969 hyphenate(sl
, hyphenation_flags
);
1970 while (forward
!= 0) {
1971 node
*tem1
= forward
;
1972 forward
= forward
->next
;
1974 tem
= tem1
->add_self(tem
, &sl
);
1979 static node
*node_list_reverse(node
*n
)
1991 static void distribute_space(node
*n
, int nspaces
, hunits desired_space
,
1992 int force_reverse
= 0)
1994 static int reverse
= 0;
1995 if (force_reverse
|| reverse
)
1996 n
= node_list_reverse(n
);
1997 if (!force_reverse
&& nspaces
> 0 && spread_limit
>= 0
1998 && desired_space
.to_units() > 0) {
1999 hunits em
= curenv
->get_size();
2000 double Ems
= (double)desired_space
.to_units() / nspaces
2001 / (em
.is_zero() ? hresolution
: em
.to_units());
2002 if (Ems
> spread_limit
)
2003 output_warning(WARN_BREAK
, "spreading %1m per space", Ems
);
2005 for (node
*tem
= n
; tem
; tem
= tem
->next
)
2006 tem
->spread_space(&nspaces
, &desired_space
);
2007 if (force_reverse
|| reverse
)
2008 (void)node_list_reverse(n
);
2011 assert(desired_space
.is_zero() && nspaces
== 0);
2014 void environment::possibly_break_line(int start_here
, int forced
)
2016 if (!fill
|| current_tab
|| current_field
|| dummy
)
2020 // When a macro follows a paragraph in fill mode, the
2021 // current line should not be empty.
2022 || (width_total
- line
->width()) > target_text_length
)) {
2023 hyphenate_line(start_here
);
2024 breakpoint
*bp
= choose_breakpoint();
2026 // we'll find one eventually
2030 while (*ndp
!= bp
->nd
)
2031 ndp
= &(*ndp
)->next
;
2032 bp
->nd
->split(bp
->index
, &pre
, &post
);
2034 hunits extra_space_width
= H0
;
2035 switch(adjust_mode
) {
2037 if (bp
->nspaces
!= 0)
2038 extra_space_width
= target_text_length
- bp
->width
;
2039 else if (bp
->width
> 0 && target_text_length
> 0
2040 && target_text_length
> bp
->width
)
2041 output_warning(WARN_BREAK
, "cannot adjust line");
2044 saved_indent
+= (target_text_length
- bp
->width
)/2;
2047 saved_indent
+= target_text_length
- bp
->width
;
2050 distribute_space(pre
, bp
->nspaces
, extra_space_width
);
2051 hunits output_width
= bp
->width
+ extra_space_width
;
2052 input_line_start
-= output_width
;
2054 hyphen_line_count
++;
2056 hyphen_line_count
= 0;
2060 node
*first_non_discardable
= 0;
2062 for (tem
= line
; tem
!= 0; tem
= tem
->next
)
2063 if (!tem
->discardable())
2064 first_non_discardable
= tem
;
2065 node
*to_be_discarded
;
2066 if (first_non_discardable
) {
2067 to_be_discarded
= first_non_discardable
->next
;
2068 first_non_discardable
->next
= 0;
2069 for (tem
= line
; tem
!= 0; tem
= tem
->next
) {
2070 width_total
+= tem
->width();
2071 space_total
+= tem
->nspaces();
2077 to_be_discarded
= line
;
2080 // Do output_line() here so that line will be 0 iff the
2081 // the environment will be empty.
2082 output_line(pre
, output_width
);
2083 while (to_be_discarded
!= 0) {
2084 tem
= to_be_discarded
;
2085 to_be_discarded
= to_be_discarded
->next
;
2086 input_line_start
-= tem
->width();
2090 if (have_temporary_indent
) {
2091 saved_indent
= temporary_indent
;
2092 have_temporary_indent
= 0;
2095 saved_indent
= indent
;
2096 target_text_length
= line_length
- saved_indent
;
2102 Do the break at the end of input after the end macro (if any).
2104 Unix troff behaves as follows: if the last line is
2108 it will output foo on the current page, and bar on the next page;
2117 everything will be output on the current page. This behaviour must be
2120 The problem is that some macro packages rely on this. For example,
2121 the ATK macros have an end macro that emits \c if it needs to print a
2122 table of contents but doesn't do a 'bp in the end macro; instead the
2123 'bp is done in the bottom of page trap. This works with Unix troff,
2124 provided that the current environment is not empty at the end of the
2127 The following will make macro packages that do that sort of thing work
2128 even if the current environment is empty at the end of the input file.
2129 If the last input line used \c and this line occurred in the end macro,
2130 then we'll force everything out on the current page, but we'll make
2131 sure that the environment isn't empty so that we won't exit at the
2132 bottom of this page.
2135 void environment::final_break()
2137 if (prev_line_interrupted
== 2) {
2139 add_node(new transparent_dummy_node
);
2146 * add_html_tag - emits a special html-tag: to help post-grohtml understand
2147 * the key troff commands
2150 void environment::add_html_tag(int force
, const char *nm
)
2152 if (!force
&& (curdiv
!= topdiv
))
2157 * need to emit tag for post-grohtml
2158 * but we check to see whether we can emit specials
2160 if (curdiv
== topdiv
&& topdiv
->before_first_page
)
2161 topdiv
->begin_page();
2162 macro
*m
= new macro
;
2163 m
->append_str("html-tag:");
2164 for (const char *p
= nm
; *p
; p
++)
2165 if (!invalid_input_char((unsigned char)*p
))
2167 curdiv
->output(new special_node(*m
), 1, 0, 0, 0);
2168 if (strcmp(nm
, ".nf") == 0)
2169 curenv
->ignore_next_eol
= 1;
2174 * add_html_tag - emits a special html-tag: to help post-grohtml understand
2175 * the key troff commands, it appends a string representation
2179 void environment::add_html_tag(int force
, const char *nm
, int i
)
2181 if (!force
&& (curdiv
!= topdiv
))
2186 * need to emit tag for post-grohtml
2187 * but we check to see whether we can emit specials
2189 if (curdiv
== topdiv
&& topdiv
->before_first_page
)
2190 topdiv
->begin_page();
2191 macro
*m
= new macro
;
2192 m
->append_str("html-tag:");
2193 for (const char *p
= nm
; *p
; p
++)
2194 if (!invalid_input_char((unsigned char)*p
))
2198 node
*n
= new special_node(*m
);
2199 curdiv
->output(n
, 1, 0, 0, 0);
2204 * add_html_tag_tabs - emits the tab settings for post-grohtml
2207 void environment::add_html_tag_tabs(int force
)
2209 if (!force
&& (curdiv
!= topdiv
))
2214 * need to emit tag for post-grohtml
2215 * but we check to see whether we can emit specials
2217 if (curdiv
== topdiv
&& topdiv
->before_first_page
)
2218 topdiv
->begin_page();
2219 macro
*m
= new macro
;
2222 m
->append_str("html-tag:.ta ");
2224 t
= curenv
->tabs
.distance_to_next_tab(l
, &d
);
2228 m
->append_str(" L ");
2229 m
->append_int(l
.to_units());
2232 m
->append_str(" C ");
2233 m
->append_int(l
.to_units());
2236 m
->append_str(" R ");
2237 m
->append_int(l
.to_units());
2242 } while ((t
!= TAB_NONE
) && (l
< get_line_length()));
2243 curdiv
->output(new special_node(*m
), 1, 0, 0, 0);
2247 node
*environment::make_html_tag(const char *nm
, int i
)
2251 * need to emit tag for post-grohtml
2252 * but we check to see whether we can emit specials
2254 if (curdiv
== topdiv
&& topdiv
->before_first_page
)
2255 topdiv
->begin_page();
2256 macro
*m
= new macro
;
2257 m
->append_str("html-tag:");
2258 for (const char *p
= nm
; *p
; p
++)
2259 if (!invalid_input_char((unsigned char)*p
))
2263 return new special_node(*m
);
2268 node
*environment::make_html_tag(const char *nm
)
2272 * need to emit tag for post-grohtml
2273 * but we check to see whether we can emit specials
2275 if (curdiv
== topdiv
&& topdiv
->before_first_page
)
2276 topdiv
->begin_page();
2277 macro
*m
= new macro
;
2278 m
->append_str("html-tag:");
2279 for (const char *p
= nm
; *p
; p
++)
2280 if (!invalid_input_char((unsigned char)*p
))
2282 return new special_node(*m
);
2287 void environment::do_break(int do_spread
)
2289 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
2290 topdiv
->begin_page();
2296 // this is so that hyphenation works
2297 line
= new space_node(H0
, get_fill_color(), line
);
2299 possibly_break_line(0, do_spread
);
2301 while (line
!= 0 && line
->discardable()) {
2302 width_total
-= line
->width();
2303 space_total
-= line
->nspaces();
2309 input_line_start
= H0
;
2312 switch (adjust_mode
) {
2314 saved_indent
+= (target_text_length
- width_total
)/2;
2317 saved_indent
+= target_text_length
- width_total
;
2323 output_line(tem
, width_total
);
2324 hyphen_line_count
= 0;
2326 prev_line_interrupted
= 0;
2327 #ifdef WIDOW_CONTROL
2329 output_pending_lines();
2330 #endif /* WIDOW_CONTROL */
2333 int environment::is_empty()
2335 return !current_tab
&& line
== 0 && pending_lines
== 0;
2338 void do_break_request(int spread
)
2340 while (!tok
.newline() && !tok
.eof())
2343 curenv
->do_break(spread
);
2344 curenv
->add_html_tag(0, ".br");
2349 void break_request()
2351 do_break_request(0);
2354 void break_spread_request()
2356 do_break_request(1);
2361 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
2362 handle_initial_title();
2366 hunits part_width
[3];
2367 part
[0] = part
[1] = part
[2] = 0;
2368 environment
env(curenv
);
2369 environment
*oldenv
= curenv
;
2371 read_title_parts(part
, part_width
);
2373 curenv
->size
= env
.size
;
2374 curenv
->prev_size
= env
.prev_size
;
2375 curenv
->requested_size
= env
.requested_size
;
2376 curenv
->prev_requested_size
= env
.prev_requested_size
;
2377 curenv
->char_height
= env
.char_height
;
2378 curenv
->char_slant
= env
.char_slant
;
2379 curenv
->fontno
= env
.fontno
;
2380 curenv
->prev_fontno
= env
.prev_fontno
;
2381 curenv
->glyph_color
= env
.glyph_color
;
2382 curenv
->prev_glyph_color
= env
.prev_glyph_color
;
2383 curenv
->fill_color
= env
.fill_color
;
2384 curenv
->prev_fill_color
= env
.prev_fill_color
;
2393 hunits
length_title(curenv
->title_length
);
2394 hunits f
= length_title
- part_width
[1];
2396 n
= new hmotion_node(f2
- part_width
[2], curenv
->get_fill_color(), n
);
2404 n
= new hmotion_node(f
- f2
- part_width
[0], curenv
->get_fill_color(), n
);
2412 curenv
->output_title(n
, !curenv
->fill
, curenv
->vertical_spacing
,
2413 curenv
->total_post_vertical_spacing(), length_title
);
2414 curenv
->hyphen_line_count
= 0;
2420 curenv
->adjust_mode
|= 1;
2424 curenv
->adjust_mode
= ADJUST_LEFT
;
2427 curenv
->adjust_mode
= ADJUST_RIGHT
;
2430 curenv
->adjust_mode
= ADJUST_CENTER
;
2434 curenv
->adjust_mode
= ADJUST_BOTH
;
2438 if (get_integer(&n
)) {
2440 warning(WARN_RANGE
, "negative adjustment mode");
2442 curenv
->adjust_mode
= 5;
2443 warning(WARN_RANGE
, "adjustment mode `%1' out of range", n
);
2446 curenv
->adjust_mode
= n
;
2455 curenv
->adjust_mode
&= ~1;
2459 void do_input_trap(int continued
)
2461 curenv
->input_trap_count
= 0;
2463 curenv
->continued_input_trap
= 1;
2465 if (has_arg() && get_integer(&n
)) {
2468 "number of lines for input trap must be greater than zero");
2470 symbol s
= get_name(1);
2472 curenv
->input_trap_count
= n
;
2473 curenv
->input_trap
= s
;
2485 void input_trap_continued()
2492 // must not be R or C or L or a legitimate part of a number expression
2493 const char TAB_REPEAT_CHAR
= 'T';
2499 tab(hunits
, tab_type
);
2500 enum { BLOCK
= 1024 };
2501 static tab
*free_list
;
2502 void *operator new(size_t);
2503 void operator delete(void *);
2506 tab
*tab::free_list
= 0;
2508 void *tab::operator new(size_t n
)
2510 assert(n
== sizeof(tab
));
2512 free_list
= (tab
*)new char[sizeof(tab
)*BLOCK
];
2513 for (int i
= 0; i
< BLOCK
- 1; i
++)
2514 free_list
[i
].next
= free_list
+ i
+ 1;
2515 free_list
[BLOCK
-1].next
= 0;
2518 free_list
= (tab
*)(free_list
->next
);
2524 /* cfront can't cope with this. */
2527 void tab::operator delete(void *p
)
2530 ((tab
*)p
)->next
= free_list
;
2531 free_list
= (tab
*)p
;
2535 tab::tab(hunits x
, tab_type t
) : next(0), pos(x
), type(t
)
2539 tab_stops::tab_stops(hunits distance
, tab_type type
)
2542 repeated_list
= new tab(distance
, type
);
2545 tab_stops::~tab_stops()
2550 tab_type
tab_stops::distance_to_next_tab(hunits curpos
, hunits
*distance
)
2554 return distance_to_next_tab(curpos
, distance
, &nextpos
);
2557 tab_type
tab_stops::distance_to_next_tab(hunits curpos
, hunits
*distance
,
2562 for (tem
= initial_list
; tem
&& tem
->pos
<= curpos
; tem
= tem
->next
)
2565 *distance
= tem
->pos
- curpos
;
2566 *nextpos
= tem
->pos
;
2569 if (repeated_list
== 0)
2571 hunits base
= lastpos
;
2573 for (tem
= repeated_list
; tem
&& tem
->pos
+ base
<= curpos
; tem
= tem
->next
)
2576 *distance
= tem
->pos
+ base
- curpos
;
2577 *nextpos
= tem
->pos
+ base
;
2580 assert(lastpos
> 0);
2586 const char *tab_stops::to_string()
2588 static char *buf
= 0;
2589 static int buf_size
= 0;
2590 // figure out a maximum on the amount of space we can need
2593 for (p
= initial_list
; p
; p
= p
->next
)
2595 for (p
= repeated_list
; p
; p
= p
->next
)
2597 // (10 for digits + 1 for u + 1 for 'C' or 'R') + 2 for ' &' + 1 for '\0'
2598 int need
= count
*12 + 3;
2599 if (buf
== 0 || need
> buf_size
) {
2603 buf
= new char[buf_size
];
2606 for (p
= initial_list
; p
; p
= p
->next
) {
2607 strcpy(ptr
, i_to_a(p
->pos
.to_units()));
2608 ptr
= strchr(ptr
, '\0');
2626 *ptr
++ = TAB_REPEAT_CHAR
;
2627 for (p
= repeated_list
; p
; p
= p
->next
) {
2628 strcpy(ptr
, i_to_a(p
->pos
.to_units()));
2629 ptr
= strchr(ptr
, '\0');
2650 tab_stops::tab_stops() : initial_list(0), repeated_list(0)
2654 tab_stops::tab_stops(const tab_stops
&ts
)
2655 : initial_list(0), repeated_list(0)
2657 tab
**p
= &initial_list
;
2658 tab
*t
= ts
.initial_list
;
2660 *p
= new tab(t
->pos
, t
->type
);
2665 t
= ts
.repeated_list
;
2667 *p
= new tab(t
->pos
, t
->type
);
2673 void tab_stops::clear()
2675 while (initial_list
) {
2676 tab
*tem
= initial_list
;
2677 initial_list
= initial_list
->next
;
2680 while (repeated_list
) {
2681 tab
*tem
= repeated_list
;
2682 repeated_list
= repeated_list
->next
;
2687 void tab_stops::add_tab(hunits pos
, tab_type type
, int repeated
)
2690 for (p
= repeated
? &repeated_list
: &initial_list
; *p
; p
= &(*p
)->next
)
2692 *p
= new tab(pos
, type
);
2696 void tab_stops::operator=(const tab_stops
&ts
)
2699 tab
**p
= &initial_list
;
2700 tab
*t
= ts
.initial_list
;
2702 *p
= new tab(t
->pos
, t
->type
);
2707 t
= ts
.repeated_list
;
2709 *p
= new tab(t
->pos
, t
->type
);
2718 hunits prev_pos
= 0;
2723 if (tok
.ch() == TAB_REPEAT_CHAR
) {
2728 if (!get_hunits(&pos
, 'm', prev_pos
))
2730 tab_type type
= TAB_LEFT
;
2731 if (tok
.ch() == 'C') {
2735 else if (tok
.ch() == 'R') {
2739 else if (tok
.ch() == 'L') {
2742 if (pos
<= prev_pos
&& !first
)
2744 "positions of tab stops must be strictly increasing");
2746 tabs
.add_tab(pos
, type
, repeated
);
2751 curenv
->tabs
= tabs
;
2752 curenv
->add_html_tag_tabs(1);
2756 const char *environment::get_tabs()
2758 return tabs
.to_string();
2761 tab_type
environment::distance_to_next_tab(hunits
*distance
)
2764 ? curenv
->tabs
.distance_to_next_tab(get_text_length(), distance
)
2765 : curenv
->tabs
.distance_to_next_tab(get_input_line_position(), distance
);
2768 tab_type
environment::distance_to_next_tab(hunits
*distance
, hunits
*leftpos
)
2771 ? curenv
->tabs
.distance_to_next_tab(get_text_length(), distance
, leftpos
)
2772 : curenv
->tabs
.distance_to_next_tab(get_input_line_position(), distance
,
2776 void field_characters()
2778 field_delimiter_char
= get_optional_char();
2779 if (field_delimiter_char
)
2780 padding_indicator_char
= get_optional_char();
2782 padding_indicator_char
= 0;
2786 void line_tabs_request()
2789 if (has_arg() && get_integer(&n
))
2790 curenv
->line_tabs
= n
!= 0;
2792 curenv
->line_tabs
= 1;
2796 int environment::get_line_tabs()
2801 void environment::wrap_up_tab()
2808 switch (current_tab
) {
2810 tab_amount
= tab_distance
- tab_width
;
2811 line
= make_tab_node(tab_amount
, line
);
2814 tab_amount
= tab_distance
- tab_width
/2;
2815 line
= make_tab_node(tab_amount
, line
);
2822 width_total
+= tab_amount
;
2823 width_total
+= tab_width
;
2824 if (current_field
) {
2825 if (tab_precedes_field
) {
2826 pre_field_width
+= tab_amount
;
2827 tab_precedes_field
= 0;
2829 field_distance
-= tab_amount
;
2830 field_spaces
+= tab_field_spaces
;
2832 if (tab_contents
!= 0) {
2834 for (tem
= tab_contents
; tem
->next
!= 0; tem
= tem
->next
)
2837 line
= tab_contents
;
2839 tab_field_spaces
= 0;
2843 current_tab
= TAB_NONE
;
2846 node
*environment::make_tab_node(hunits d
, node
*next
)
2848 if (leader_node
!= 0 && d
< 0) {
2849 error("motion generated by leader cannot be negative");
2854 return new hmotion_node(d
, 1, 0, get_fill_color(), next
);
2855 node
*n
= new hline_node(d
, leader_node
, next
);
2860 void environment::handle_tab(int is_leader
)
2866 charinfo
*ci
= is_leader
? leader_char
: tab_char
;
2868 leader_node
= ci
? make_char_node(ci
) : 0;
2869 tab_type t
= distance_to_next_tab(&d
, &absolute
);
2874 add_node(make_tab_node(d
));
2875 add_node(make_html_tag("tab L", absolute
.to_units()));
2878 add_node(make_html_tag("tab R", absolute
.to_units()));
2881 add_node(make_html_tag("tab C", absolute
.to_units()));
2890 tab_field_spaces
= 0;
2893 void environment::start_field()
2895 assert(!current_field
);
2897 if (distance_to_next_tab(&d
) != TAB_NONE
) {
2898 pre_field_width
= get_text_length();
2902 tab_field_spaces
= 0;
2903 for (node
*p
= line
; p
; p
= p
->next
)
2908 tab_precedes_field
= current_tab
!= TAB_NONE
;
2911 error("zero field width");
2914 void environment::wrap_up_field()
2916 if (!current_tab
&& field_spaces
== 0)
2918 hunits padding
= field_distance
- (get_text_length() - pre_field_width
);
2919 if (current_tab
&& tab_field_spaces
!= 0) {
2920 hunits tab_padding
= scale(padding
,
2922 field_spaces
+ tab_field_spaces
);
2923 padding
-= tab_padding
;
2924 distribute_space(tab_contents
, tab_field_spaces
, tab_padding
, 1);
2925 tab_field_spaces
= 0;
2926 tab_width
+= tab_padding
;
2928 if (field_spaces
!= 0) {
2929 distribute_space(line
, field_spaces
, padding
, 1);
2930 width_total
+= padding
;
2932 // the start of the tab has been moved to the right by padding, so
2933 tab_distance
-= padding
;
2934 if (tab_distance
<= H0
) {
2935 // use the next tab stop instead
2936 current_tab
= tabs
.distance_to_next_tab(get_input_line_position()
2939 if (current_tab
== TAB_NONE
|| current_tab
== TAB_LEFT
) {
2940 width_total
+= tab_width
;
2941 if (current_tab
== TAB_LEFT
) {
2942 line
= make_tab_node(tab_distance
, line
);
2943 width_total
+= tab_distance
;
2944 current_tab
= TAB_NONE
;
2946 if (tab_contents
!= 0) {
2948 for (tem
= tab_contents
; tem
->next
!= 0; tem
= tem
->next
)
2951 line
= tab_contents
;
2963 void environment::add_padding()
2966 tab_contents
= new space_node(H0
, get_fill_color(), tab_contents
);
2972 line
= new space_node(H0
, get_fill_color(), line
);
2977 typedef int (environment::*INT_FUNCP
)();
2978 typedef vunits (environment::*VUNITS_FUNCP
)();
2979 typedef hunits (environment::*HUNITS_FUNCP
)();
2980 typedef const char *(environment::*STRING_FUNCP
)();
2982 class int_env_reg
: public reg
{
2985 int_env_reg(INT_FUNCP
);
2986 const char *get_string();
2987 int get_value(units
*val
);
2990 class vunits_env_reg
: public reg
{
2993 vunits_env_reg(VUNITS_FUNCP f
);
2994 const char *get_string();
2995 int get_value(units
*val
);
2999 class hunits_env_reg
: public reg
{
3002 hunits_env_reg(HUNITS_FUNCP f
);
3003 const char *get_string();
3004 int get_value(units
*val
);
3007 class string_env_reg
: public reg
{
3010 string_env_reg(STRING_FUNCP
);
3011 const char *get_string();
3014 int_env_reg::int_env_reg(INT_FUNCP f
) : func(f
)
3018 int int_env_reg::get_value(units
*val
)
3020 *val
= (curenv
->*func
)();
3024 const char *int_env_reg::get_string()
3026 return i_to_a((curenv
->*func
)());
3029 vunits_env_reg::vunits_env_reg(VUNITS_FUNCP f
) : func(f
)
3033 int vunits_env_reg::get_value(units
*val
)
3035 *val
= (curenv
->*func
)().to_units();
3039 const char *vunits_env_reg::get_string()
3041 return i_to_a((curenv
->*func
)().to_units());
3044 hunits_env_reg::hunits_env_reg(HUNITS_FUNCP f
) : func(f
)
3048 int hunits_env_reg::get_value(units
*val
)
3050 *val
= (curenv
->*func
)().to_units();
3054 const char *hunits_env_reg::get_string()
3056 return i_to_a((curenv
->*func
)().to_units());
3059 string_env_reg::string_env_reg(STRING_FUNCP f
) : func(f
)
3063 const char *string_env_reg::get_string()
3065 return (curenv
->*func
)();
3068 class horizontal_place_reg
: public general_reg
{
3070 horizontal_place_reg();
3071 int get_value(units
*);
3072 void set_value(units
);
3075 horizontal_place_reg::horizontal_place_reg()
3079 int horizontal_place_reg::get_value(units
*res
)
3081 *res
= curenv
->get_input_line_position().to_units();
3085 void horizontal_place_reg::set_value(units n
)
3087 curenv
->set_input_line_position(hunits(n
));
3090 const char *environment::get_font_family_string()
3092 return family
->nm
.contents();
3095 const char *environment::get_glyph_color_string()
3097 return glyph_color
->nm
.contents();
3100 const char *environment::get_fill_color_string()
3102 return fill_color
->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("ad", adjust
);
3191 init_request("br", break_request
);
3192 init_request("brp", break_spread_request
);
3193 init_request("c2", no_break_control_char
);
3194 init_request("cc", control_char
);
3195 init_request("ce", center
);
3196 init_request("cu", continuous_underline
);
3197 init_request("ev", environment_switch
);
3198 init_request("evc", environment_copy
);
3199 init_request("fam", family_change
);
3200 init_request("fc", field_characters
);
3201 init_request("fi", fill
);
3202 init_request("ft", font_change
);
3203 init_request("hc", hyphen_char
);
3204 init_request("hlm", hyphen_line_max_request
);
3205 init_request("hy", hyphenate_request
);
3206 init_request("hym", hyphenation_margin_request
);
3207 init_request("hys", hyphenation_space_request
);
3208 init_request("in", indent
);
3209 init_request("it", input_trap
);
3210 init_request("itc", input_trap_continued
);
3211 init_request("lc", leader_character
);
3212 init_request("linetabs", line_tabs_request
);
3213 init_request("ll", line_length
);
3214 init_request("ls", line_spacing
);
3215 init_request("lt", title_length
);
3216 init_request("mc", margin_character
);
3217 init_request("na", no_adjust
);
3218 init_request("nf", no_fill
);
3219 init_request("nh", no_hyphenate
);
3220 init_request("nm", number_lines
);
3221 init_request("nn", no_number
);
3222 init_request("ps", point_size
);
3223 init_request("pvs", post_vertical_spacing
);
3224 init_request("rj", right_justify
);
3225 init_request("sizes", override_sizes
);
3226 init_request("ss", space_size
);
3227 init_request("ta", set_tabs
);
3228 init_request("ti", temporary_indent
);
3229 init_request("tc", tab_character
);
3230 init_request("tl", title
);
3231 init_request("ul", underline
);
3232 init_request("vs", vertical_spacing
);
3233 #ifdef WIDOW_CONTROL
3234 init_request("wdc", widow_control_request
);
3235 #endif /* WIDOW_CONTROL */
3236 init_int_env_reg(".b", get_bold
);
3237 init_vunits_env_reg(".cdp", get_prev_char_depth
);
3238 init_int_env_reg(".ce", get_center_lines
);
3239 init_vunits_env_reg(".cht", get_prev_char_height
);
3240 init_hunits_env_reg(".csk", get_prev_char_skew
);
3241 init_string_env_reg(".ev", get_name_string
);
3242 init_int_env_reg(".f", get_font
);
3243 init_string_env_reg(".fam", get_font_family_string
);
3244 init_string_env_reg(".fn", get_font_name_string
);
3245 init_int_env_reg(".height", get_char_height
);
3246 init_int_env_reg(".hlc", get_hyphen_line_count
);
3247 init_int_env_reg(".hlm", get_hyphen_line_max
);
3248 init_int_env_reg(".hy", get_hyphenation_flags
);
3249 init_hunits_env_reg(".hym", get_hyphenation_margin
);
3250 init_hunits_env_reg(".hys", get_hyphenation_space
);
3251 init_hunits_env_reg(".i", get_indent
);
3252 init_hunits_env_reg(".in", get_saved_indent
);
3253 init_int_env_reg(".int", get_prev_line_interrupted
);
3254 init_int_env_reg(".linetabs", get_line_tabs
);
3255 init_hunits_env_reg(".lt", get_title_length
);
3256 init_int_env_reg(".j", get_adjust_mode
);
3257 init_hunits_env_reg(".k", get_text_length
);
3258 init_int_env_reg(".L", get_line_spacing
);
3259 init_hunits_env_reg(".l", get_line_length
);
3260 init_hunits_env_reg(".ll", get_saved_line_length
);
3261 init_string_env_reg(".M", get_fill_color_string
);
3262 init_string_env_reg(".m", get_glyph_color_string
);
3263 init_hunits_env_reg(".n", get_prev_text_length
);
3264 init_int_env_reg(".ps", get_point_size
);
3265 init_int_env_reg(".psr", get_requested_point_size
);
3266 init_vunits_env_reg(".pvs", get_post_vertical_spacing
);
3267 init_int_env_reg(".rj", get_right_justify_lines
);
3268 init_string_env_reg(".s", get_point_size_string
);
3269 init_int_env_reg(".slant", get_char_slant
);
3270 init_int_env_reg(".ss", get_space_size
);
3271 init_int_env_reg(".sss", get_sentence_space_size
);
3272 init_string_env_reg(".sr", get_requested_point_size_string
);
3273 init_string_env_reg(".tabs", get_tabs
);
3274 init_int_env_reg(".u", get_fill
);
3275 init_vunits_env_reg(".v", get_vertical_spacing
);
3276 init_hunits_env_reg(".w", get_prev_char_width
);
3277 number_reg_dictionary
.define("ct", new variable_reg(&ct_reg_contents
));
3278 number_reg_dictionary
.define("hp", new horizontal_place_reg
);
3279 number_reg_dictionary
.define("ln", new variable_reg(&next_line_number
));
3280 number_reg_dictionary
.define("rsb", new variable_reg(&rsb_reg_contents
));
3281 number_reg_dictionary
.define("rst", new variable_reg(&rst_reg_contents
));
3282 number_reg_dictionary
.define("sb", new variable_reg(&sb_reg_contents
));
3283 number_reg_dictionary
.define("skw", new variable_reg(&skw_reg_contents
));
3284 number_reg_dictionary
.define("ssc", new variable_reg(&ssc_reg_contents
));
3285 number_reg_dictionary
.define("st", new variable_reg(&st_reg_contents
));
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 for (int i
= 0; i
< WORD_MAX
; i
++)
3621 int num
[WORD_MAX
+1];
3624 FILE *fp
= mac_path
->open_file(name
, &path
);
3626 error("can't find hyphenation patterns file `%1'", name
);
3629 int c
= hpf_getc(fp
);
3630 int have_patterns
= 0; // we've seen \patterns
3631 int final_pattern
= 0; // 1 if we have a trailing closing brace
3632 int have_hyphenation
= 0; // we've seen \hyphenation
3633 int final_hyphenation
= 0; // 1 if we have a trailing closing brace
3634 int have_keyword
= 0; // we've seen either \patterns or \hyphenation
3635 int traditional
= 0; // don't handle \patterns
3638 if (c
== '%') { // skip comments
3641 } while (c
!= EOF
&& c
!= '\n');
3643 if (c
== EOF
|| !csspace(c
))
3648 if (have_keyword
|| traditional
) // we are done
3650 else { // rescan file in `traditional' mode
3659 if (!(c
== '{' || c
== '}')) { // skip braces at line start
3660 do { // scan patterns
3668 } while (i
< WORD_MAX
&& c
!= EOF
&& !csspace(c
)
3669 && c
!= '%' && c
!= '{' && c
!= '}');
3672 if (i
>= 9 && !strncmp(buf
+ i
- 9, "\\patterns", 9)) {
3676 if (have_patterns
|| have_hyphenation
)
3677 error("\\patterns not allowed inside of %1 group",
3678 have_patterns
? "\\patterns" : "\\hyphenation");
3687 else if (i
>= 12 && !strncmp(buf
+ i
- 12, "\\hyphenation", 12)) {
3691 if (have_patterns
|| have_hyphenation
)
3692 error("\\hyphenation not allowed inside of %1 group",
3693 have_patterns
? "\\patterns" : "\\hyphenation");
3695 have_hyphenation
= 1;
3702 else if (strstr(buf
, "\\endinput")) {
3703 if (have_patterns
|| have_hyphenation
)
3704 error("found \\endinput inside of %1 group",
3705 have_patterns
? "\\patterns" : "\\hyphenation");
3708 else if (c
== '}') {
3709 if (have_patterns
) {
3714 else if (have_hyphenation
) {
3715 have_hyphenation
= 0;
3717 final_hyphenation
= 1;
3721 else if (c
== '{') {
3722 if (have_patterns
|| have_hyphenation
)
3723 error("`{' not allowed within %1 group",
3724 have_patterns
? "\\patterns" : "\\hyphenation");
3725 c
= hpf_getc(fp
); // skipped if not starting \patterns
3730 if (c
== '{' || c
== '}')
3734 if (have_patterns
|| final_pattern
|| traditional
) {
3735 for (int j
= 0; j
< i
; j
++)
3736 buf
[j
] = hpf_code_table
[(unsigned char)buf
[j
]];
3737 insert_pattern(buf
, i
, num
);
3740 else if (have_hyphenation
|| final_hyphenation
) {
3741 insert_hyphenation(ex
, buf
, i
);
3742 final_hyphenation
= 0;
3751 void hyphenate(hyphen_list
*h
, unsigned flags
)
3753 if (!current_language
)
3756 while (h
&& h
->hyphenation_code
== 0)
3759 char hbuf
[WORD_MAX
+2];
3760 char *buf
= hbuf
+ 1;
3762 for (tem
= h
; tem
&& len
< WORD_MAX
; tem
= tem
->next
) {
3763 if (tem
->hyphenation_code
!= 0)
3764 buf
[len
++] = tem
->hyphenation_code
;
3768 hyphen_list
*nexth
= tem
;
3772 = (unsigned char *)current_language
->exceptions
.lookup(buf
);
3776 for (tem
= h
; tem
!= 0; tem
= tem
->next
, i
++)
3783 hbuf
[0] = hbuf
[len
+1] = '.';
3784 int num
[WORD_MAX
+3];
3785 current_language
->patterns
.hyphenate(hbuf
, len
+2, num
);
3792 for (i
= 2, tem
= h
; i
< len
&& tem
; tem
= tem
->next
, i
++)
3801 static void do_hyphenation_patterns_file(int append
)
3803 symbol name
= get_long_name(1);
3804 if (!name
.is_null()) {
3805 if (!current_language
)
3806 error("no current hyphenation language");
3808 current_language
->patterns
.read_patterns_file(
3809 name
.contents(), append
,
3810 ¤t_language
->exceptions
);
3815 static void hyphenation_patterns_file()
3817 do_hyphenation_patterns_file(0);
3820 static void hyphenation_patterns_file_append()
3822 do_hyphenation_patterns_file(1);
3825 class hyphenation_language_reg
: public reg
{
3827 const char *get_string();
3830 const char *hyphenation_language_reg::get_string()
3832 return current_language
? current_language
->name
.contents() : "";
3835 void init_hyphen_requests()
3837 init_request("hw", hyphen_word
);
3838 init_request("hla", set_hyphenation_language
);
3839 init_request("hpf", hyphenation_patterns_file
);
3840 init_request("hpfa", hyphenation_patterns_file_append
);
3841 number_reg_dictionary
.define(".hla", new hyphenation_language_reg
);