2 * Copyright (c) 2014 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
4 * Copyright (C) 1989 - 1992, 2000 - 2006 Free Software Foundation, Inc.
5 * Written by James Clark (jjc@jclark.com)
7 * This is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2, or (at your option) any later
12 * This is distributed in the hope that it will be useful, but WITHOUT ANY
13 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 * You should have received a copy of the GNU General Public License along
18 * with groff; see the file COPYING. If not, write to the Free Software
19 * Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA.
23 #include "troff-config.h"
27 #include "file_case.h"
29 #include "macropath.h"
30 #include "stringclass.h"
33 #include "dictionary.h"
45 symbol
default_family("T");
47 enum { ADJUST_LEFT
= 0, ADJUST_BOTH
= 1, ADJUST_CENTER
= 3, ADJUST_RIGHT
= 5 };
49 enum { HYPHEN_LAST_LINE
= 2, HYPHEN_LAST_CHARS
= 4, HYPHEN_FIRST_CHARS
= 8 };
57 env_list(environment
*e
, env_list
*p
) : env(e
), next(p
) {}
61 environment
*env_table
[NENVIRONMENTS
];
62 dictionary
env_dictionary(NENVIRONMENTS
);
64 static int next_line_number
= 0;
65 extern int suppress_push
;
66 extern statem
*get_diversion_state();
68 charinfo
*field_delimiter_char
;
69 charinfo
*padding_indicator_char
;
71 int translate_space_to_dummy
= 0;
73 class pending_output_line
76 friend void environment::mark_last_line();
77 friend void environment::output(node
*, int, vunits
, vunits
, hunits
, int);
87 int last_line
; // Is it the last line of the paragraph?
91 pending_output_line
*next
;
93 pending_output_line(node
*, int, vunits
, vunits
, hunits
, int,
94 pending_output_line
* = 0);
95 ~pending_output_line();
99 pending_output_line::pending_output_line(node
*n
, int nf
, vunits v
, vunits pv
,
101 pending_output_line
*p
)
102 : nd(n
), no_fill(nf
), was_centered(ce
), vs(v
), post_vs(pv
), width(w
),
110 pending_output_line::~pending_output_line()
112 delete_node_list(nd
);
115 int pending_output_line::output()
117 if (trap_sprung_flag
)
120 if (next
&& next
->last_line
&& !no_fill
) {
121 curdiv
->need(vs
+ post_vs
+ vunits(vresolution
));
122 if (trap_sprung_flag
) {
123 next
->last_line
= 0; // Try to avoid infinite loops.
128 curenv
->construct_format_state(nd
, was_centered
, !no_fill
);
129 curdiv
->output(nd
, no_fill
, vs
, post_vs
, width
);
134 void environment::output(node
*nd
, int no_fill_flag
,
135 vunits vs
, vunits post_vs
,
136 hunits width
, int was_centered
)
139 while (pending_lines
) {
140 if (widow_control
&& !pending_lines
->no_fill
&& !pending_lines
->next
)
142 if (!pending_lines
->output())
144 pending_output_line
*tem
= pending_lines
;
145 pending_lines
= pending_lines
->next
;
149 output_pending_lines();
151 if (!trap_sprung_flag
&& !pending_lines
153 && (!widow_control
|| no_fill_flag
)
156 curenv
->construct_format_state(nd
, was_centered
, !no_fill_flag
);
157 curdiv
->output(nd
, no_fill_flag
, vs
, post_vs
, width
);
159 pending_output_line
**p
;
160 for (p
= &pending_lines
; *p
; p
= &(*p
)->next
)
162 *p
= new pending_output_line(nd
, no_fill_flag
, vs
, post_vs
, width
,
167 // a line from .tl goes at the head of the queue
169 void environment::output_title(node
*nd
, int no_fill_flag
,
170 vunits vs
, vunits post_vs
,
173 if (!trap_sprung_flag
)
174 curdiv
->output(nd
, no_fill_flag
, vs
, post_vs
, width
);
176 pending_lines
= new pending_output_line(nd
, no_fill_flag
, vs
, post_vs
,
177 width
, 0, pending_lines
);
180 void environment::output_pending_lines()
182 while (pending_lines
&& pending_lines
->output()) {
183 pending_output_line
*tem
= pending_lines
;
184 pending_lines
= pending_lines
->next
;
190 void environment::mark_last_line()
192 if (!widow_control
|| !pending_lines
)
194 pending_output_line
*p
;
195 for (p
= pending_lines
; p
->next
; p
= p
->next
)
201 void widow_control_request()
204 if (has_arg() && get_integer(&n
))
205 curenv
->widow_control
= n
!= 0;
207 curenv
->widow_control
= 1;
210 #endif /* WIDOW_CONTROL */
212 /* font_size functions */
214 size_range
*font_size::size_table
= 0;
215 int font_size::nranges
= 0;
218 int compare_ranges(const void *p1
, const void *p2
)
220 return ((size_range
*)p1
)->min
- ((size_range
*)p2
)->min
;
224 void font_size::init_size_table(int *sizes
)
227 while (sizes
[nranges
*2] != 0)
230 size_table
= new size_range
[nranges
];
231 for (int i
= 0; i
< nranges
; i
++) {
232 size_table
[i
].min
= sizes
[i
*2];
233 size_table
[i
].max
= sizes
[i
*2 + 1];
235 qsort(size_table
, nranges
, sizeof(size_range
), compare_ranges
);
238 font_size::font_size(int sp
)
240 for (int i
= 0; i
< nranges
; i
++) {
241 if (sp
< size_table
[i
].min
) {
242 if (i
> 0 && size_table
[i
].min
- sp
>= sp
- size_table
[i
- 1].max
)
243 p
= size_table
[i
- 1].max
;
245 p
= size_table
[i
].min
;
248 if (sp
<= size_table
[i
].max
) {
253 p
= size_table
[nranges
- 1].max
;
256 int font_size::to_units()
258 return scale(p
, units_per_inch
, sizescale
*72);
261 // we can't do this in a static constructor because various dictionaries
262 // have to get initialized first
264 void init_environments()
266 curenv
= env_table
[0] = new environment("0");
271 curenv
->tab_char
= get_optional_char();
275 void leader_character()
277 curenv
->leader_char
= get_optional_char();
281 void environment::add_char(charinfo
*ci
)
287 // don't allow fields in dummy environments
288 else if (ci
== field_delimiter_char
&& !dummy
) {
294 else if (current_field
&& ci
== padding_indicator_char
)
296 else if (current_tab
) {
297 if (tab_contents
== 0)
298 tab_contents
= new line_start_node
;
299 if (ci
!= hyphen_indicator_char
)
300 tab_contents
= tab_contents
->add_char(ci
, this, &tab_width
, &s
, &gc_np
);
302 tab_contents
= tab_contents
->add_discretionary_hyphen();
308 fprintf(stderr
, "current line is\n");
309 line
->debug_node_list();
311 if (ci
!= hyphen_indicator_char
)
312 line
= line
->add_char(ci
, this, &width_total
, &space_total
, &gc_np
);
314 line
= line
->add_discretionary_hyphen();
317 fprintf(stderr
, "now after we have added character the line is\n");
318 line
->debug_node_list();
320 if ((!suppress_push
) && gc_np
) {
321 if (gc_np
&& (gc_np
->state
== 0)) {
322 gc_np
->state
= construct_state(0);
323 gc_np
->push_state
= get_diversion_state();
325 else if (line
&& (line
->state
== 0)) {
326 line
->state
= construct_state(0);
327 line
->push_state
= get_diversion_state();
331 fprintf(stderr
, "now we have possibly added the state the line is\n");
332 line
->debug_node_list();
336 node
*environment::make_char_node(charinfo
*ci
)
338 return make_node(ci
, this);
341 void environment::add_node(node
*n
)
345 if (!suppress_push
) {
346 if (n
->is_special
&& n
->state
== NULL
)
347 n
->state
= construct_state(0);
348 n
->push_state
= get_diversion_state();
351 if (current_tab
|| current_field
)
356 else if (current_tab
) {
357 n
->next
= tab_contents
;
359 tab_width
+= n
->width();
363 if (discarding
&& n
->discardable()) {
364 // XXX possibly: input_line_start -= n->width();
370 width_total
+= n
->width();
371 space_total
+= n
->nspaces();
374 construct_new_line_state(line
);
378 void environment::add_hyphen_indicator()
380 if (current_tab
|| interrupted
|| current_field
381 || hyphen_indicator_char
!= 0)
385 line
= line
->add_discretionary_hyphen();
388 int environment::get_hyphenation_flags()
390 return hyphenation_flags
;
393 int environment::get_hyphen_line_max()
395 return hyphen_line_max
;
398 int environment::get_hyphen_line_count()
400 return hyphen_line_count
;
403 int environment::get_center_lines()
408 int environment::get_right_justify_lines()
410 return right_justify_lines
;
413 void environment::add_italic_correction()
417 tab_contents
= tab_contents
->add_italic_correction(&tab_width
);
420 line
= line
->add_italic_correction(&width_total
);
423 void environment::space_newline()
425 assert(!current_tab
&& !current_field
);
429 hunits sw
= env_space_width(this);
430 hunits ssw
= env_sentence_space_width(this);
431 if (!translate_space_to_dummy
) {
433 if (node_list_ends_sentence(line
) == 1)
436 width_list
*w
= new width_list(sw
, ssw
);
437 if (node_list_ends_sentence(line
) == 1)
438 w
->next
= new width_list(sw
, ssw
);
439 if (line
!= 0 && line
->merge_space(x
, sw
, ssw
)) {
443 add_node(new word_space_node(x
, get_fill_color(), w
));
444 possibly_break_line(0, spread_flag
);
448 void environment::space()
450 space(env_space_width(this), env_sentence_space_width(this));
453 void environment::space(hunits space_width
, hunits sentence_space_width
)
457 if (current_field
&& padding_indicator_char
== 0) {
461 hunits x
= translate_space_to_dummy
? H0
: space_width
;
462 node
*p
= current_tab
? tab_contents
: line
;
463 hunits
*tp
= current_tab
? &tab_width
: &width_total
;
464 if (p
&& p
->nspaces() == 1 && p
->width() == x
465 && node_list_ends_sentence(p
->next
) == 1) {
466 hunits xx
= translate_space_to_dummy
? H0
: sentence_space_width
;
467 if (p
->merge_space(xx
, space_width
, sentence_space_width
)) {
472 if (p
&& p
->merge_space(x
, space_width
, sentence_space_width
)) {
476 add_node(new word_space_node(x
,
478 new width_list(space_width
,
479 sentence_space_width
)));
480 possibly_break_line(0, spread_flag
);
484 node
*do_underline_special(int);
486 void environment::set_font(symbol nm
)
490 if (nm
== symbol("P") || nm
.is_empty()) {
491 if (family
->make_definite(prev_fontno
) < 0)
494 fontno
= prev_fontno
;
498 prev_fontno
= fontno
;
499 int n
= symbol_fontno(nm
);
501 n
= next_available_font_position();
502 if (!mount_font(n
, nm
))
505 if (family
->make_definite(n
) < 0)
509 if (underline_spaces
&& fontno
!= prev_fontno
) {
510 if (fontno
== get_underline_fontno())
511 add_node(do_underline_special(1));
512 if (prev_fontno
== get_underline_fontno())
513 add_node(do_underline_special(0));
517 void environment::set_font(int n
)
521 if (is_good_fontno(n
)) {
522 prev_fontno
= fontno
;
526 warning(WARN_FONT
, "bad font number");
529 void environment::set_family(symbol fam
)
533 if (fam
.is_null() || fam
.is_empty()) {
534 if (prev_family
->make_definite(fontno
) < 0)
536 font_family
*tem
= family
;
537 family
= prev_family
;
541 font_family
*f
= lookup_family(fam
);
542 if (f
->make_definite(fontno
) < 0)
544 prev_family
= family
;
549 void environment::set_size(int n
)
554 font_size temp
= prev_size
;
557 int temp2
= prev_requested_size
;
558 prev_requested_size
= requested_size
;
559 requested_size
= temp2
;
564 prev_requested_size
= requested_size
;
569 void environment::set_char_height(int n
)
573 if (n
== requested_size
|| n
<= 0)
579 void environment::set_char_slant(int n
)
586 color
*environment::get_prev_glyph_color()
588 return prev_glyph_color
;
591 color
*environment::get_glyph_color()
596 color
*environment::get_prev_fill_color()
598 return prev_fill_color
;
601 color
*environment::get_fill_color()
606 void environment::set_glyph_color(color
*c
)
610 curenv
->prev_glyph_color
= curenv
->glyph_color
;
611 curenv
->glyph_color
= c
;
614 void environment::set_fill_color(color
*c
)
618 curenv
->prev_fill_color
= curenv
->fill_color
;
619 curenv
->fill_color
= c
;
622 environment::environment(symbol nm
)
624 prev_line_length((units_per_inch
*13)/2),
625 line_length((units_per_inch
*13)/2),
626 prev_title_length((units_per_inch
*13)/2),
627 title_length((units_per_inch
*13)/2),
628 prev_size(sizescale
*10),
630 requested_size(sizescale
*10),
631 prev_requested_size(sizescale
*10),
635 sentence_space_size(12),
636 adjust_mode(ADJUST_BOTH
),
639 prev_line_interrupted(0),
641 right_justify_lines(0),
642 prev_vertical_spacing(points_to_units(12)),
643 vertical_spacing(points_to_units(12)),
644 prev_post_vertical_spacing(0),
645 post_vertical_spacing(0),
646 prev_line_spacing(1),
651 have_temporary_indent(0),
655 continued_input_trap(0),
662 current_tab(TAB_NONE
),
665 leader_char(charset_table
['.']),
669 margin_character_flags(0),
670 margin_character_node(0),
671 margin_character_distance(points_to_units(10)),
673 number_text_separation(1),
674 line_number_indent(0),
675 line_number_multiple(1),
677 hyphenation_flags(1),
678 hyphen_line_count(0),
680 hyphenation_space(H0
),
681 hyphenation_margin(H0
),
686 #endif /* WIDOW_CONTROL */
687 glyph_color(&default_color
),
688 prev_glyph_color(&default_color
),
689 fill_color(&default_color
),
690 prev_fill_color(&default_color
),
693 suppress_next_eol(0),
695 tabs(units_per_inch
/2, TAB_LEFT
),
698 no_break_control_char('\''),
699 hyphen_indicator_char(0)
701 prev_family
= family
= lookup_family(default_family
);
702 prev_fontno
= fontno
= 1;
703 if (!is_good_fontno(1))
704 fatal("font number 1 not a valid font");
705 if (family
->make_definite(1) < 0)
706 fatal("invalid default family `%1'", default_family
.contents());
707 prev_fontno
= fontno
;
710 environment::environment(const environment
*e
)
712 prev_line_length(e
->prev_line_length
),
713 line_length(e
->line_length
),
714 prev_title_length(e
->prev_title_length
),
715 title_length(e
->title_length
),
716 prev_size(e
->prev_size
),
718 requested_size(e
->requested_size
),
719 prev_requested_size(e
->prev_requested_size
),
720 char_height(e
->char_height
),
721 char_slant(e
->char_slant
),
722 prev_fontno(e
->prev_fontno
),
724 prev_family(e
->prev_family
),
726 space_size(e
->space_size
),
727 sentence_space_size(e
->sentence_space_size
),
728 adjust_mode(e
->adjust_mode
),
731 prev_line_interrupted(0),
733 right_justify_lines(0),
734 prev_vertical_spacing(e
->prev_vertical_spacing
),
735 vertical_spacing(e
->vertical_spacing
),
736 prev_post_vertical_spacing(e
->prev_post_vertical_spacing
),
737 post_vertical_spacing(e
->post_vertical_spacing
),
738 prev_line_spacing(e
->prev_line_spacing
),
739 line_spacing(e
->line_spacing
),
740 prev_indent(e
->prev_indent
),
743 have_temporary_indent(0),
747 continued_input_trap(0),
749 prev_text_length(e
->prev_text_length
),
753 line_tabs(e
->line_tabs
),
754 current_tab(TAB_NONE
),
756 tab_char(e
->tab_char
),
757 leader_char(e
->leader_char
),
761 margin_character_flags(e
->margin_character_flags
),
762 margin_character_node(e
->margin_character_node
),
763 margin_character_distance(e
->margin_character_distance
),
765 number_text_separation(e
->number_text_separation
),
766 line_number_indent(e
->line_number_indent
),
767 line_number_multiple(e
->line_number_multiple
),
768 no_number_count(e
->no_number_count
),
769 hyphenation_flags(e
->hyphenation_flags
),
770 hyphen_line_count(0),
771 hyphen_line_max(e
->hyphen_line_max
),
772 hyphenation_space(e
->hyphenation_space
),
773 hyphenation_margin(e
->hyphenation_margin
),
777 widow_control(e
->widow_control
),
778 #endif /* WIDOW_CONTROL */
779 glyph_color(e
->glyph_color
),
780 prev_glyph_color(e
->prev_glyph_color
),
781 fill_color(e
->fill_color
),
782 prev_fill_color(e
->prev_fill_color
),
783 seen_space(e
->seen_space
),
784 seen_eol(e
->seen_eol
),
785 suppress_next_eol(e
->suppress_next_eol
),
786 seen_break(e
->seen_break
),
788 name(e
->name
), // so that eg `.if "\n[.ev]"0"' works
789 control_char(e
->control_char
),
790 no_break_control_char(e
->no_break_control_char
),
791 hyphen_indicator_char(e
->hyphen_indicator_char
)
795 void environment::copy(const environment
*e
)
797 prev_line_length
= e
->prev_line_length
;
798 line_length
= e
->line_length
;
799 prev_title_length
= e
->prev_title_length
;
800 title_length
= e
->title_length
;
801 prev_size
= e
->prev_size
;
803 prev_requested_size
= e
->prev_requested_size
;
804 requested_size
= e
->requested_size
;
805 char_height
= e
->char_height
;
806 char_slant
= e
->char_slant
;
807 space_size
= e
->space_size
;
808 sentence_space_size
= e
->sentence_space_size
;
809 adjust_mode
= e
->adjust_mode
;
812 prev_line_interrupted
= 0;
814 right_justify_lines
= 0;
815 prev_vertical_spacing
= e
->prev_vertical_spacing
;
816 vertical_spacing
= e
->vertical_spacing
;
817 prev_post_vertical_spacing
= e
->prev_post_vertical_spacing
,
818 post_vertical_spacing
= e
->post_vertical_spacing
,
819 prev_line_spacing
= e
->prev_line_spacing
;
820 line_spacing
= e
->line_spacing
;
821 prev_indent
= e
->prev_indent
;
823 have_temporary_indent
= 0;
824 temporary_indent
= 0;
826 underline_spaces
= 0;
827 input_trap_count
= 0;
828 continued_input_trap
= 0;
829 prev_text_length
= e
->prev_text_length
;
832 input_line_start
= 0;
833 control_char
= e
->control_char
;
834 no_break_control_char
= e
->no_break_control_char
;
835 hyphen_indicator_char
= e
->hyphen_indicator_char
;
841 line_tabs
= e
->line_tabs
;
842 current_tab
= TAB_NONE
;
844 margin_character_flags
= e
->margin_character_flags
;
845 if (e
->margin_character_node
)
846 margin_character_node
= e
->margin_character_node
->copy();
847 margin_character_distance
= e
->margin_character_distance
;
849 number_text_separation
= e
->number_text_separation
;
850 line_number_multiple
= e
->line_number_multiple
;
851 line_number_indent
= e
->line_number_indent
;
852 no_number_count
= e
->no_number_count
;
853 tab_char
= e
->tab_char
;
854 leader_char
= e
->leader_char
;
855 hyphenation_flags
= e
->hyphenation_flags
;
857 prev_fontno
= e
->prev_fontno
;
860 prev_family
= e
->prev_family
;
863 widow_control
= e
->widow_control
;
864 #endif /* WIDOW_CONTROL */
865 hyphen_line_max
= e
->hyphen_line_max
;
866 hyphen_line_count
= 0;
867 hyphenation_space
= e
->hyphenation_space
;
868 hyphenation_margin
= e
->hyphenation_margin
;
870 glyph_color
= e
->glyph_color
;
871 prev_glyph_color
= e
->prev_glyph_color
;
872 fill_color
= e
->fill_color
;
873 prev_fill_color
= e
->prev_fill_color
;
876 environment::~environment()
879 delete_node_list(line
);
880 delete_node_list(numbering_nodes
);
883 hunits
environment::get_input_line_position()
887 n
= -input_line_start
;
889 n
= width_total
- input_line_start
;
895 void environment::set_input_line_position(hunits n
)
897 input_line_start
= line
== 0 ? -n
: width_total
- n
;
899 input_line_start
+= tab_width
;
902 hunits
environment::get_line_length()
907 hunits
environment::get_saved_line_length()
910 return target_text_length
+ saved_indent
;
915 vunits
environment::get_vertical_spacing()
917 return vertical_spacing
;
920 vunits
environment::get_post_vertical_spacing()
922 return post_vertical_spacing
;
925 int environment::get_line_spacing()
930 vunits
environment::total_post_vertical_spacing()
932 vunits
tem(post_vertical_spacing
);
933 if (line_spacing
> 1)
934 tem
+= (line_spacing
- 1)*vertical_spacing
;
938 int environment::get_bold()
940 return get_bold_fontno(fontno
);
943 hunits
environment::get_digit_width()
945 return env_digit_width(this);
948 int environment::get_adjust_mode()
953 int environment::get_fill()
958 hunits
environment::get_indent()
963 hunits
environment::get_saved_indent()
967 else if (have_temporary_indent
)
968 return temporary_indent
;
973 hunits
environment::get_temporary_indent()
975 return temporary_indent
;
978 hunits
environment::get_title_length()
983 node
*environment::get_prev_char()
985 for (node
*n
= current_tab
? tab_contents
: line
; n
; n
= n
->next
) {
986 node
*last
= n
->last_char_node();
993 hunits
environment::get_prev_char_width()
995 node
*last
= get_prev_char();
998 return last
->width();
1001 hunits
environment::get_prev_char_skew()
1003 node
*last
= get_prev_char();
1006 return last
->skew();
1009 vunits
environment::get_prev_char_height()
1011 node
*last
= get_prev_char();
1015 last
->vertical_extent(&min
, &max
);
1019 vunits
environment::get_prev_char_depth()
1021 node
*last
= get_prev_char();
1025 last
->vertical_extent(&min
, &max
);
1029 hunits
environment::get_text_length()
1031 hunits n
= line
== 0 ? H0
: width_total
;
1037 hunits
environment::get_prev_text_length()
1039 return prev_text_length
;
1043 static int sb_reg_contents
= 0;
1044 static int st_reg_contents
= 0;
1045 static int ct_reg_contents
= 0;
1046 static int rsb_reg_contents
= 0;
1047 static int rst_reg_contents
= 0;
1048 static int skw_reg_contents
= 0;
1049 static int ssc_reg_contents
= 0;
1051 void environment::width_registers()
1053 // this is used to implement \w; it sets the st, sb, ct registers
1054 vunits min
= 0, max
= 0, cur
= 0;
1055 int character_type
= 0;
1056 ssc_reg_contents
= line
? line
->subscript_correction().to_units() : 0;
1057 skw_reg_contents
= line
? line
->skew().to_units() : 0;
1058 line
= reverse_node_list(line
);
1059 vunits real_min
= V0
;
1060 vunits real_max
= V0
;
1062 for (node
*tem
= line
; tem
; tem
= tem
->next
) {
1063 tem
->vertical_extent(&v1
, &v2
);
1070 if ((cur
+= tem
->vertical_width()) < min
)
1074 character_type
|= tem
->character_type();
1076 line
= reverse_node_list(line
);
1077 st_reg_contents
= -min
.to_units();
1078 sb_reg_contents
= -max
.to_units();
1079 rst_reg_contents
= -real_min
.to_units();
1080 rsb_reg_contents
= -real_max
.to_units();
1081 ct_reg_contents
= character_type
;
1084 node
*environment::extract_output_line()
1093 /* environment related requests */
1095 void environment_switch()
1097 int pop
= 0; // 1 means pop, 2 means pop but no error message on underflow
1098 if (curenv
->is_dummy())
1099 error("can't switch environments when current environment is dummy");
1100 else if (!has_arg())
1104 if (!tok
.delimiter()) {
1105 // It looks like a number.
1107 if (get_integer(&n
)) {
1108 if (n
>= 0 && n
< NENVIRONMENTS
) {
1109 env_stack
= new env_list(curenv
, env_stack
);
1110 if (env_table
[n
] == 0)
1111 env_table
[n
] = new environment(i_to_a(n
));
1112 curenv
= env_table
[n
];
1121 nm
= get_long_name(1);
1125 if (!nm
.is_null()) {
1126 environment
*e
= (environment
*)env_dictionary
.lookup(nm
);
1128 e
= new environment(nm
);
1129 (void)env_dictionary
.lookup(nm
, e
);
1131 env_stack
= new env_list(curenv
, env_stack
);
1136 if (env_stack
== 0) {
1138 error("environment stack underflow");
1141 int seen_space
= curenv
->seen_space
;
1142 int seen_eol
= curenv
->seen_eol
;
1143 int suppress_next_eol
= curenv
->suppress_next_eol
;
1144 curenv
= env_stack
->env
;
1145 curenv
->seen_space
= seen_space
;
1146 curenv
->seen_eol
= seen_eol
;
1147 curenv
->suppress_next_eol
= suppress_next_eol
;
1148 env_list
*tem
= env_stack
;
1149 env_stack
= env_stack
->next
;
1156 void environment_copy()
1161 if (!tok
.delimiter()) {
1162 // It looks like a number.
1164 if (get_integer(&n
)) {
1165 if (n
>= 0 && n
< NENVIRONMENTS
)
1172 nm
= get_long_name(1);
1173 if (!e
&& !nm
.is_null())
1174 e
= (environment
*)env_dictionary
.lookup(nm
);
1176 error("No environment to copy from");
1184 void fill_color_change()
1186 symbol s
= get_name();
1188 curenv
->set_fill_color(curenv
->get_prev_fill_color());
1194 void glyph_color_change()
1196 symbol s
= get_name();
1198 curenv
->set_glyph_color(curenv
->get_prev_glyph_color());
1204 static symbol
P_symbol("P");
1208 symbol s
= get_name();
1210 if (s
.is_null() || s
== P_symbol
) {
1215 for (const char *p
= s
.contents(); p
!= 0 && *p
!= 0; p
++)
1222 curenv
->set_font(atoi(s
.contents()));
1224 curenv
->set_font(s
);
1228 void family_change()
1230 symbol s
= get_name();
1231 curenv
->set_family(s
);
1238 if (has_arg() && get_number(&n
, 'z', curenv
->get_requested_point_size())) {
1241 curenv
->set_size(n
);
1244 curenv
->set_size(0);
1248 void override_sizes()
1251 int *sizes
= new int[n
];
1253 char *buf
= read_string();
1256 char *p
= strtok(buf
, " \t");
1261 switch (sscanf(p
, "%d-%d", &lower
, &upper
)) {
1266 if (lower
<= upper
&& lower
>= 0)
1270 warning(WARN_RANGE
, "bad size range `%1'", p
);
1274 int *old_sizes
= sizes
;
1275 sizes
= new int[n
*2];
1276 memcpy(sizes
, old_sizes
, n
*sizeof(int));
1284 p
= strtok(0, " \t");
1286 font_size::init_size_table(sizes
);
1292 if (get_integer(&n
)) {
1293 curenv
->space_size
= n
;
1294 if (has_arg() && get_integer(&n
))
1295 curenv
->sentence_space_size
= n
;
1297 curenv
->sentence_space_size
= curenv
->space_size
;
1304 while (!tok
.newline() && !tok
.eof())
1314 while (!tok
.newline() && !tok
.eof())
1319 curenv
->suppress_next_eol
= 1;
1326 if (!has_arg() || !get_integer(&n
))
1330 while (!tok
.newline() && !tok
.eof())
1334 curenv
->right_justify_lines
= 0;
1335 curenv
->center_lines
= n
;
1336 curdiv
->modified_tag
.incl(MTSM_CE
);
1340 void right_justify()
1343 if (!has_arg() || !get_integer(&n
))
1347 while (!tok
.newline() && !tok
.eof())
1351 curenv
->center_lines
= 0;
1352 curenv
->right_justify_lines
= n
;
1353 curdiv
->modified_tag
.incl(MTSM_RJ
);
1360 if (has_arg() && get_hunits(&temp
, 'm', curenv
->line_length
)) {
1362 warning(WARN_RANGE
, "bad line length %1u", temp
.to_units());
1367 temp
= curenv
->prev_line_length
;
1368 curenv
->prev_line_length
= curenv
->line_length
;
1369 curenv
->line_length
= temp
;
1370 curdiv
->modified_tag
.incl(MTSM_LL
);
1377 if (has_arg() && get_hunits(&temp
, 'm', curenv
->title_length
)) {
1379 warning(WARN_RANGE
, "bad title length %1u", temp
.to_units());
1384 temp
= curenv
->prev_title_length
;
1385 curenv
->prev_title_length
= curenv
->title_length
;
1386 curenv
->title_length
= temp
;
1390 void vertical_spacing()
1393 if (has_arg() && get_vunits(&temp
, 'p', curenv
->vertical_spacing
)) {
1395 warning(WARN_RANGE
, "vertical spacing must not be negative");
1400 temp
= curenv
->prev_vertical_spacing
;
1401 curenv
->prev_vertical_spacing
= curenv
->vertical_spacing
;
1402 curenv
->vertical_spacing
= temp
;
1406 void post_vertical_spacing()
1409 if (has_arg() && get_vunits(&temp
, 'p', curenv
->post_vertical_spacing
)) {
1412 "post vertical spacing must be greater than or equal to 0");
1417 temp
= curenv
->prev_post_vertical_spacing
;
1418 curenv
->prev_post_vertical_spacing
= curenv
->post_vertical_spacing
;
1419 curenv
->post_vertical_spacing
= temp
;
1426 if (has_arg() && get_integer(&temp
)) {
1428 warning(WARN_RANGE
, "value %1 out of range: interpreted as 1", temp
);
1433 temp
= curenv
->prev_line_spacing
;
1434 curenv
->prev_line_spacing
= curenv
->line_spacing
;
1435 curenv
->line_spacing
= temp
;
1442 if (has_arg() && get_hunits(&temp
, 'm', curenv
->indent
)) {
1444 warning(WARN_RANGE
, "indent cannot be negative");
1449 temp
= curenv
->prev_indent
;
1450 while (!tok
.newline() && !tok
.eof())
1454 curenv
->have_temporary_indent
= 0;
1455 curenv
->prev_indent
= curenv
->indent
;
1456 curenv
->indent
= temp
;
1457 curdiv
->modified_tag
.incl(MTSM_IN
);
1461 void temporary_indent()
1465 if (!get_hunits(&temp
, 'm', curenv
->get_indent()))
1467 while (!tok
.newline() && !tok
.eof())
1472 warning(WARN_RANGE
, "total indent cannot be negative");
1476 curenv
->temporary_indent
= temp
;
1477 curenv
->have_temporary_indent
= 1;
1478 curdiv
->modified_tag
.incl(MTSM_TI
);
1483 node
*do_underline_special(int underline_spaces
)
1486 m
.append_str("x u ");
1487 m
.append(underline_spaces
+ '0');
1488 return new special_node(m
, 1);
1491 void do_underline(int underline_spaces
)
1494 if (!has_arg() || !get_integer(&n
))
1497 if (curenv
->underline_lines
> 0) {
1498 curenv
->prev_fontno
= curenv
->fontno
;
1499 curenv
->fontno
= curenv
->pre_underline_fontno
;
1500 if (underline_spaces
) {
1501 curenv
->underline_spaces
= 0;
1502 curenv
->add_node(do_underline_special(0));
1505 curenv
->underline_lines
= 0;
1508 curenv
->underline_lines
= n
;
1509 curenv
->pre_underline_fontno
= curenv
->fontno
;
1510 curenv
->fontno
= get_underline_fontno();
1511 if (underline_spaces
) {
1512 curenv
->underline_spaces
= 1;
1513 curenv
->add_node(do_underline_special(1));
1519 void continuous_underline()
1531 curenv
->control_char
= '.';
1534 error("bad control character");
1536 curenv
->control_char
= tok
.ch();
1541 void no_break_control_char()
1543 curenv
->no_break_control_char
= '\'';
1546 error("bad control character");
1548 curenv
->no_break_control_char
= tok
.ch();
1553 void margin_character()
1557 charinfo
*ci
= tok
.get_char();
1559 // Call tok.next() only after making the node so that
1560 // .mc \s+9\(br\s0 works.
1561 node
*nd
= curenv
->make_char_node(ci
);
1564 delete curenv
->margin_character_node
;
1565 curenv
->margin_character_node
= nd
;
1566 curenv
->margin_character_flags
= MARGIN_CHARACTER_ON
1567 | MARGIN_CHARACTER_NEXT
;
1569 if (has_arg() && get_hunits(&d
, 'm'))
1570 curenv
->margin_character_distance
= d
;
1574 check_missing_character();
1575 curenv
->margin_character_flags
&= ~MARGIN_CHARACTER_ON
;
1576 if (curenv
->margin_character_flags
== 0) {
1577 delete curenv
->margin_character_node
;
1578 curenv
->margin_character_node
= 0;
1586 delete_node_list(curenv
->numbering_nodes
);
1587 curenv
->numbering_nodes
= 0;
1590 for (int i
= '9'; i
>= '0'; i
--) {
1591 node
*tem
= make_node(charset_table
[i
], curenv
);
1599 curenv
->numbering_nodes
= nd
;
1600 curenv
->line_number_digit_width
= env_digit_width(curenv
);
1602 if (!tok
.delimiter()) {
1603 if (get_integer(&n
, next_line_number
)) {
1604 next_line_number
= n
;
1605 if (next_line_number
< 0) {
1606 warning(WARN_RANGE
, "negative line number");
1607 next_line_number
= 0;
1612 while (!tok
.space() && !tok
.newline() && !tok
.eof())
1615 if (!tok
.delimiter()) {
1616 if (get_integer(&n
)) {
1618 warning(WARN_RANGE
, "negative or zero line number multiple");
1621 curenv
->line_number_multiple
= n
;
1625 while (!tok
.space() && !tok
.newline() && !tok
.eof())
1628 if (!tok
.delimiter()) {
1629 if (get_integer(&n
))
1630 curenv
->number_text_separation
= n
;
1633 while (!tok
.space() && !tok
.newline() && !tok
.eof())
1635 if (has_arg() && !tok
.delimiter() && get_integer(&n
))
1636 curenv
->line_number_indent
= n
;
1646 if (has_arg() && get_integer(&n
))
1647 curenv
->no_number_count
= n
> 0 ? n
: 0;
1649 curenv
->no_number_count
= 1;
1655 curenv
->hyphenation_flags
= 0;
1659 void hyphenate_request()
1662 if (has_arg() && get_integer(&n
))
1663 curenv
->hyphenation_flags
= n
;
1665 curenv
->hyphenation_flags
= 1;
1671 curenv
->hyphen_indicator_char
= get_optional_char();
1675 void hyphen_line_max_request()
1678 if (has_arg() && get_integer(&n
))
1679 curenv
->hyphen_line_max
= n
;
1681 curenv
->hyphen_line_max
= -1;
1685 void environment::interrupt()
1688 add_node(new transparent_dummy_node
);
1693 void environment::newline()
1695 int was_centered
= 0;
1696 if (underline_lines
> 0) {
1697 if (--underline_lines
== 0) {
1698 prev_fontno
= fontno
;
1699 fontno
= pre_underline_fontno
;
1700 if (underline_spaces
) {
1701 underline_spaces
= 0;
1702 add_node(do_underline_special(0));
1710 // strip trailing spaces
1711 while (line
!= 0 && line
->discardable()) {
1712 width_total
-= line
->width();
1713 space_total
-= line
->nspaces();
1718 node
*to_be_output
= 0;
1719 hunits to_be_output_width
;
1720 prev_line_interrupted
= 0;
1723 else if (interrupted
) {
1725 // see environment::final_break
1726 prev_line_interrupted
= exit_started
? 2 : 1;
1728 else if (center_lines
> 0) {
1730 hunits x
= target_text_length
- width_total
;
1732 saved_indent
+= x
/2;
1733 to_be_output
= line
;
1735 to_be_output_width
= width_total
;
1738 else if (right_justify_lines
> 0) {
1739 --right_justify_lines
;
1740 hunits x
= target_text_length
- width_total
;
1743 to_be_output
= line
;
1744 to_be_output_width
= width_total
;
1750 to_be_output
= line
;
1751 to_be_output_width
= width_total
;
1754 input_line_start
= line
== 0 ? H0
: width_total
;
1756 if (is_html
&& !fill
) {
1757 curdiv
->modified_tag
.incl(MTSM_EOL
);
1758 if (suppress_next_eol
)
1759 suppress_next_eol
= 0;
1764 output_line(to_be_output
, to_be_output_width
, was_centered
);
1765 hyphen_line_count
= 0;
1767 if (input_trap_count
> 0) {
1768 if (!(continued_input_trap
&& prev_line_interrupted
))
1769 if (--input_trap_count
== 0)
1770 spring_trap(input_trap
);
1774 void environment::output_line(node
*n
, hunits width
, int was_centered
)
1776 prev_text_length
= width
;
1777 if (margin_character_flags
) {
1778 hunits d
= line_length
+ margin_character_distance
- saved_indent
- width
;
1780 n
= new hmotion_node(d
, get_fill_color(), n
);
1783 margin_character_flags
&= ~MARGIN_CHARACTER_NEXT
;
1785 if (!margin_character_flags
) {
1786 tem
= margin_character_node
;
1787 margin_character_node
= 0;
1790 tem
= margin_character_node
->copy();
1793 width
+= tem
->width();
1797 node
*tem
= n
->next
;
1802 if (!saved_indent
.is_zero())
1803 nn
= new hmotion_node(saved_indent
, get_fill_color(), nn
);
1804 width
+= saved_indent
;
1805 if (no_number_count
> 0)
1807 else if (numbering_nodes
) {
1808 hunits w
= (line_number_digit_width
1809 *(3+line_number_indent
+number_text_separation
));
1810 if (next_line_number
% line_number_multiple
!= 0)
1811 nn
= new hmotion_node(w
, get_fill_color(), nn
);
1814 nn
= new hmotion_node(number_text_separation
* line_number_digit_width
,
1815 get_fill_color(), nn
);
1816 x
-= number_text_separation
*line_number_digit_width
;
1818 sprintf(buf
, "%3d", next_line_number
);
1819 for (char *p
= strchr(buf
, '\0') - 1; p
>= buf
&& *p
!= ' '; --p
) {
1820 node
*gn
= numbering_nodes
;
1821 for (int count
= *p
- '0'; count
> 0; count
--)
1828 nn
= new hmotion_node(x
, get_fill_color(), nn
);
1833 output(nn
, !fill
, vertical_spacing
, total_post_vertical_spacing(), width
,
1837 void environment::start_line()
1841 line
= new line_start_node
;
1842 if (have_temporary_indent
) {
1843 saved_indent
= temporary_indent
;
1844 have_temporary_indent
= 0;
1847 saved_indent
= indent
;
1848 target_text_length
= line_length
- saved_indent
;
1853 hunits
environment::get_hyphenation_space()
1855 return hyphenation_space
;
1858 void hyphenation_space_request()
1861 if (get_hunits(&n
, 'm')) {
1863 warning(WARN_RANGE
, "hyphenation space cannot be negative");
1866 curenv
->hyphenation_space
= n
;
1871 hunits
environment::get_hyphenation_margin()
1873 return hyphenation_margin
;
1876 void hyphenation_margin_request()
1879 if (get_hunits(&n
, 'm')) {
1881 warning(WARN_RANGE
, "hyphenation margin cannot be negative");
1884 curenv
->hyphenation_margin
= n
;
1889 breakpoint
*environment::choose_breakpoint()
1891 hunits x
= width_total
;
1892 int s
= space_total
;
1894 breakpoint
*best_bp
= 0; // the best breakpoint so far
1895 int best_bp_fits
= 0;
1899 breakpoint
*bp
= n
->get_breakpoints(x
, s
);
1901 if (bp
->width
<= target_text_length
) {
1902 if (!bp
->hyphenated
) {
1903 breakpoint
*tem
= bp
->next
;
1906 breakpoint
*tem1
= tem
;
1911 // Decide whether to use the hyphenated breakpoint.
1912 && (hyphen_line_max
< 0
1913 // Only choose the hyphenated breakpoint if it would not
1914 // exceed the maximum number of consecutive hyphenated
1916 || hyphen_line_count
+ 1 <= hyphen_line_max
)
1917 && !(adjust_mode
== ADJUST_BOTH
1918 // Don't choose the hyphenated breakpoint if the line
1919 // can be justified by adding no more than
1920 // hyphenation_space to any word space.
1922 && (((target_text_length
- bp
->width
1923 + (bp
->nspaces
- 1)*hresolution
)/bp
->nspaces
)
1924 <= hyphenation_space
))
1925 // Don't choose the hyphenated breakpoint if the line
1926 // is no more than hyphenation_margin short.
1927 : target_text_length
- bp
->width
<= hyphenation_margin
)) {
1936 if ((adjust_mode
== ADJUST_BOTH
1937 ? hyphenation_space
== H0
1938 : hyphenation_margin
== H0
)
1939 && (hyphen_line_max
< 0
1940 || hyphen_line_count
+ 1 <= hyphen_line_max
)) {
1941 // No need to consider a non-hyphenated breakpoint.
1944 breakpoint
*tem
= bp
->next
;
1947 breakpoint
*tem1
= tem
;
1953 // It fits but it's hyphenated.
1954 if (!best_bp_fits
) {
1962 breakpoint
*tem
= bp
;
1979 output_warning(WARN_BREAK
, "can't break line");
1985 void environment::hyphenate_line(int start_here
)
1988 hyphenation_type prev_type
= line
->get_hyphenation_type();
1993 for (startp
= &line
->next
; *startp
!= 0; startp
= &(*startp
)->next
) {
1994 hyphenation_type this_type
= (*startp
)->get_hyphenation_type();
1995 if (prev_type
== HYPHEN_BOUNDARY
&& this_type
== HYPHEN_MIDDLE
)
1997 prev_type
= this_type
;
2001 node
*tem
= *startp
;
2004 } while (tem
!= 0 && tem
->get_hyphenation_type() == HYPHEN_MIDDLE
);
2005 int inhibit
= (tem
!= 0 && tem
->get_hyphenation_type() == HYPHEN_INHIBIT
);
2007 hyphen_list
*sl
= 0;
2011 while (tem
!= end
) {
2012 sl
= tem
->get_hyphen_list(sl
, &i
);
2015 tem1
->next
= forward
;
2019 // this is for characters like hyphen and emdash
2021 for (hyphen_list
*h
= sl
; h
; h
= h
->next
) {
2022 h
->breakable
= (prev_code
!= 0
2024 && h
->next
->hyphenation_code
!= 0);
2025 prev_code
= h
->hyphenation_code
;
2028 if (hyphenation_flags
!= 0
2030 // this may not be right if we have extra space on this line
2031 && !((hyphenation_flags
& HYPHEN_LAST_LINE
)
2032 && (curdiv
->distance_to_next_trap()
2033 <= vertical_spacing
+ total_post_vertical_spacing()))
2035 hyphenate(sl
, hyphenation_flags
);
2036 while (forward
!= 0) {
2037 node
*tem1
= forward
;
2038 forward
= forward
->next
;
2040 tem
= tem1
->add_self(tem
, &sl
);
2045 static node
*node_list_reverse(node
*n
)
2057 static void distribute_space(node
*n
, int nspaces
, hunits desired_space
,
2058 int force_reverse
= 0)
2060 static int reverse
= 0;
2061 if (force_reverse
|| reverse
)
2062 n
= node_list_reverse(n
);
2063 if (!force_reverse
&& nspaces
> 0 && spread_limit
>= 0
2064 && desired_space
.to_units() > 0) {
2065 hunits em
= curenv
->get_size();
2066 double Ems
= (double)desired_space
.to_units() / nspaces
2067 / (em
.is_zero() ? hresolution
: em
.to_units());
2068 if (Ems
> spread_limit
)
2069 output_warning(WARN_BREAK
, "spreading %1m per space", Ems
);
2071 for (node
*tem
= n
; tem
; tem
= tem
->next
)
2072 tem
->spread_space(&nspaces
, &desired_space
);
2073 if (force_reverse
|| reverse
)
2074 (void)node_list_reverse(n
);
2077 assert(desired_space
.is_zero() && nspaces
== 0);
2080 void environment::possibly_break_line(int start_here
, int forced
)
2082 int was_centered
= center_lines
> 0;
2083 if (!fill
|| current_tab
|| current_field
|| dummy
)
2087 // When a macro follows a paragraph in fill mode, the
2088 // current line should not be empty.
2089 || (width_total
- line
->width()) > target_text_length
)) {
2090 hyphenate_line(start_here
);
2091 breakpoint
*bp
= choose_breakpoint();
2093 // we'll find one eventually
2097 while (*ndp
!= bp
->nd
)
2098 ndp
= &(*ndp
)->next
;
2099 bp
->nd
->split(bp
->index
, &pre
, &post
);
2101 hunits extra_space_width
= H0
;
2102 switch(adjust_mode
) {
2104 if (bp
->nspaces
!= 0)
2105 extra_space_width
= target_text_length
- bp
->width
;
2106 else if (bp
->width
> 0 && target_text_length
> 0
2107 && target_text_length
> bp
->width
)
2108 output_warning(WARN_BREAK
, "cannot adjust line");
2111 saved_indent
+= (target_text_length
- bp
->width
)/2;
2115 saved_indent
+= target_text_length
- bp
->width
;
2118 distribute_space(pre
, bp
->nspaces
, extra_space_width
);
2119 hunits output_width
= bp
->width
+ extra_space_width
;
2120 input_line_start
-= output_width
;
2122 hyphen_line_count
++;
2124 hyphen_line_count
= 0;
2128 node
*first_non_discardable
= 0;
2130 for (tem
= line
; tem
!= 0; tem
= tem
->next
)
2131 if (!tem
->discardable())
2132 first_non_discardable
= tem
;
2133 node
*to_be_discarded
;
2134 if (first_non_discardable
) {
2135 to_be_discarded
= first_non_discardable
->next
;
2136 first_non_discardable
->next
= 0;
2137 for (tem
= line
; tem
!= 0; tem
= tem
->next
) {
2138 width_total
+= tem
->width();
2139 space_total
+= tem
->nspaces();
2145 to_be_discarded
= line
;
2148 // Do output_line() here so that line will be 0 iff the
2149 // the environment will be empty.
2150 output_line(pre
, output_width
, was_centered
);
2151 while (to_be_discarded
!= 0) {
2152 tem
= to_be_discarded
;
2153 to_be_discarded
= to_be_discarded
->next
;
2154 input_line_start
-= tem
->width();
2158 if (have_temporary_indent
) {
2159 saved_indent
= temporary_indent
;
2160 have_temporary_indent
= 0;
2163 saved_indent
= indent
;
2164 target_text_length
= line_length
- saved_indent
;
2170 Do the break at the end of input after the end macro (if any).
2172 Unix troff behaves as follows: if the last line is
2176 it will output foo on the current page, and bar on the next page;
2185 everything will be output on the current page. This behaviour must be
2188 The problem is that some macro packages rely on this. For example,
2189 the ATK macros have an end macro that emits \c if it needs to print a
2190 table of contents but doesn't do a 'bp in the end macro; instead the
2191 'bp is done in the bottom of page trap. This works with Unix troff,
2192 provided that the current environment is not empty at the end of the
2195 The following will make macro packages that do that sort of thing work
2196 even if the current environment is empty at the end of the input file.
2197 If the last input line used \c and this line occurred in the end macro,
2198 then we'll force everything out on the current page, but we'll make
2199 sure that the environment isn't empty so that we won't exit at the
2200 bottom of this page.
2203 void environment::final_break()
2205 if (prev_line_interrupted
== 2) {
2207 add_node(new transparent_dummy_node
);
2213 node
*environment::make_tag(const char *nm
, int i
)
2217 * need to emit tag for post-grohtml
2218 * but we check to see whether we can emit specials
2220 if (curdiv
== topdiv
&& topdiv
->before_first_page
)
2221 topdiv
->begin_page();
2222 macro
*m
= new macro
;
2223 m
->append_str("devtag:");
2224 for (const char *p
= nm
; *p
; p
++)
2225 if (!invalid_input_char((unsigned char)*p
))
2229 return new special_node(*m
);
2234 void environment::dump_troff_state()
2237 fprintf(stderr
, SPACES
"register `in' = %d\n", curenv
->indent
.to_units());
2238 if (curenv
->have_temporary_indent
)
2239 fprintf(stderr
, SPACES
"register `ti' = %d\n",
2240 curenv
->temporary_indent
.to_units());
2241 fprintf(stderr
, SPACES
"centered lines `ce' = %d\n", curenv
->center_lines
);
2242 fprintf(stderr
, SPACES
"register `ll' = %d\n",
2243 curenv
->line_length
.to_units());
2244 fprintf(stderr
, SPACES
"fill `fi=1/nf=0' = %d\n", curenv
->fill
);
2245 fprintf(stderr
, SPACES
"page offset `po' = %d\n",
2246 topdiv
->get_page_offset().to_units());
2247 fprintf(stderr
, SPACES
"seen_break = %d\n", curenv
->seen_break
);
2248 fprintf(stderr
, SPACES
"seen_space = %d\n", curenv
->seen_space
);
2253 statem
*environment::construct_state(int only_eol
)
2256 statem
*s
= new statem();
2258 s
->add_tag(MTSM_IN
, indent
);
2259 s
->add_tag(MTSM_LL
, line_length
);
2260 s
->add_tag(MTSM_PO
, topdiv
->get_page_offset().to_units());
2261 s
->add_tag(MTSM_RJ
, right_justify_lines
);
2262 if (have_temporary_indent
)
2263 s
->add_tag(MTSM_TI
, temporary_indent
);
2266 s
->add_tag(MTSM_BR
);
2267 if (seen_space
!= 0)
2268 s
->add_tag(MTSM_SP
, seen_space
);
2273 s
->add_tag(MTSM_EOL
);
2274 s
->add_tag(MTSM_CE
, center_lines
);
2283 void environment::construct_format_state(node
*n
, int was_centered
,
2287 // find first glyph node which has a state.
2288 while (n
!= 0 && n
->state
== 0)
2290 if (n
== 0 || (n
->state
== 0))
2292 if (seen_space
!= 0)
2293 n
->state
->add_tag(MTSM_SP
, seen_space
);
2294 if (seen_eol
&& topdiv
== curdiv
)
2295 n
->state
->add_tag(MTSM_EOL
);
2299 n
->state
->add_tag(MTSM_CE
, center_lines
+1);
2301 n
->state
->add_tag_if_unknown(MTSM_CE
, 0);
2302 n
->state
->add_tag_if_unknown(MTSM_FI
, filling
);
2305 if (n
->state
!= 0) {
2306 n
->state
->sub_tag_ce();
2307 n
->state
->add_tag_if_unknown(MTSM_FI
, filling
);
2314 void environment::construct_new_line_state(node
*n
)
2317 // find first glyph node which has a state.
2318 while (n
!= 0 && n
->state
== 0)
2320 if (n
== 0 || n
->state
== 0)
2322 if (seen_space
!= 0)
2323 n
->state
->add_tag(MTSM_SP
, seen_space
);
2324 if (seen_eol
&& topdiv
== curdiv
)
2325 n
->state
->add_tag(MTSM_EOL
);
2331 extern int global_diverted_space
;
2333 void environment::do_break(int do_spread
)
2335 int was_centered
= 0;
2336 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
2337 topdiv
->begin_page();
2343 // this is so that hyphenation works
2344 if (line
->nspaces() == 0) {
2345 line
= new space_node(H0
, get_fill_color(), line
);
2348 possibly_break_line(0, do_spread
);
2350 while (line
!= 0 && line
->discardable()) {
2351 width_total
-= line
->width();
2352 space_total
-= line
->nspaces();
2358 input_line_start
= H0
;
2361 switch (adjust_mode
) {
2363 saved_indent
+= (target_text_length
- width_total
)/2;
2367 saved_indent
+= target_text_length
- width_total
;
2373 output_line(tem
, width_total
, was_centered
);
2374 hyphen_line_count
= 0;
2376 prev_line_interrupted
= 0;
2377 #ifdef WIDOW_CONTROL
2379 output_pending_lines();
2380 #endif /* WIDOW_CONTROL */
2381 if (!global_diverted_space
) {
2382 curdiv
->modified_tag
.incl(MTSM_BR
);
2387 int environment::is_empty()
2389 return !current_tab
&& line
== 0 && pending_lines
== 0;
2392 void do_break_request(int spread
)
2394 while (!tok
.newline() && !tok
.eof())
2397 curenv
->do_break(spread
);
2401 void break_request()
2403 do_break_request(0);
2406 void break_spread_request()
2408 do_break_request(1);
2413 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
2414 handle_initial_title();
2418 hunits part_width
[3];
2419 part
[0] = part
[1] = part
[2] = 0;
2420 environment
env(curenv
);
2421 environment
*oldenv
= curenv
;
2423 read_title_parts(part
, part_width
);
2425 curenv
->size
= env
.size
;
2426 curenv
->prev_size
= env
.prev_size
;
2427 curenv
->requested_size
= env
.requested_size
;
2428 curenv
->prev_requested_size
= env
.prev_requested_size
;
2429 curenv
->char_height
= env
.char_height
;
2430 curenv
->char_slant
= env
.char_slant
;
2431 curenv
->fontno
= env
.fontno
;
2432 curenv
->prev_fontno
= env
.prev_fontno
;
2433 curenv
->glyph_color
= env
.glyph_color
;
2434 curenv
->prev_glyph_color
= env
.prev_glyph_color
;
2435 curenv
->fill_color
= env
.fill_color
;
2436 curenv
->prev_fill_color
= env
.prev_fill_color
;
2445 hunits
length_title(curenv
->title_length
);
2446 hunits f
= length_title
- part_width
[1];
2448 n
= new hmotion_node(f2
- part_width
[2], curenv
->get_fill_color(), n
);
2456 n
= new hmotion_node(f
- f2
- part_width
[0], curenv
->get_fill_color(), n
);
2464 curenv
->output_title(n
, !curenv
->fill
, curenv
->vertical_spacing
,
2465 curenv
->total_post_vertical_spacing(), length_title
);
2466 curenv
->hyphen_line_count
= 0;
2472 curenv
->adjust_mode
|= 1;
2476 curenv
->adjust_mode
= ADJUST_LEFT
;
2479 curenv
->adjust_mode
= ADJUST_RIGHT
;
2482 curenv
->adjust_mode
= ADJUST_CENTER
;
2486 curenv
->adjust_mode
= ADJUST_BOTH
;
2490 if (get_integer(&n
)) {
2492 warning(WARN_RANGE
, "negative adjustment mode");
2494 curenv
->adjust_mode
= 5;
2495 warning(WARN_RANGE
, "adjustment mode `%1' out of range", n
);
2498 curenv
->adjust_mode
= n
;
2507 curenv
->adjust_mode
&= ~1;
2511 void do_input_trap(int continued
)
2513 curenv
->input_trap_count
= 0;
2515 curenv
->continued_input_trap
= 1;
2517 if (has_arg() && get_integer(&n
)) {
2520 "number of lines for input trap must be greater than zero");
2522 symbol s
= get_name(1);
2524 curenv
->input_trap_count
= n
;
2525 curenv
->input_trap
= s
;
2537 void input_trap_continued()
2544 // must not be R or C or L or a legitimate part of a number expression
2545 const char TAB_REPEAT_CHAR
= 'T';
2551 tab(hunits
, tab_type
);
2552 enum { BLOCK
= 1024 };
2553 static tab
*free_list
;
2554 void *operator new(size_t);
2555 void operator delete(void *);
2558 tab
*tab::free_list
= 0;
2560 void *tab::operator new(size_t n
)
2562 assert(n
== sizeof(tab
));
2564 free_list
= (tab
*)new char[sizeof(tab
)*BLOCK
];
2565 for (int i
= 0; i
< BLOCK
- 1; i
++)
2566 free_list
[i
].next
= free_list
+ i
+ 1;
2567 free_list
[BLOCK
-1].next
= 0;
2570 free_list
= (tab
*)(free_list
->next
);
2576 /* cfront can't cope with this. */
2579 void tab::operator delete(void *p
)
2582 ((tab
*)p
)->next
= free_list
;
2583 free_list
= (tab
*)p
;
2587 tab::tab(hunits x
, tab_type t
) : next(0), pos(x
), type(t
)
2591 tab_stops::tab_stops(hunits distance
, tab_type type
)
2594 repeated_list
= new tab(distance
, type
);
2597 tab_stops::~tab_stops()
2602 tab_type
tab_stops::distance_to_next_tab(hunits curpos
, hunits
*distance
)
2606 return distance_to_next_tab(curpos
, distance
, &nextpos
);
2609 tab_type
tab_stops::distance_to_next_tab(hunits curpos
, hunits
*distance
,
2614 for (tem
= initial_list
; tem
&& tem
->pos
<= curpos
; tem
= tem
->next
)
2617 *distance
= tem
->pos
- curpos
;
2618 *nextpos
= tem
->pos
;
2621 if (repeated_list
== 0)
2623 hunits base
= lastpos
;
2625 for (tem
= repeated_list
; tem
&& tem
->pos
+ base
<= curpos
; tem
= tem
->next
)
2628 *distance
= tem
->pos
+ base
- curpos
;
2629 *nextpos
= tem
->pos
+ base
;
2632 assert(lastpos
> 0);
2638 const char *tab_stops::to_string()
2640 static char *buf
= 0;
2641 static int buf_size
= 0;
2642 // figure out a maximum on the amount of space we can need
2645 for (p
= initial_list
; p
; p
= p
->next
)
2647 for (p
= repeated_list
; p
; p
= p
->next
)
2649 // (10 for digits + 1 for u + 1 for 'C' or 'R') + 2 for ' &' + 1 for '\0'
2650 int need
= count
*12 + 3;
2651 if (buf
== 0 || need
> buf_size
) {
2655 buf
= new char[buf_size
];
2658 for (p
= initial_list
; p
; p
= p
->next
) {
2659 strcpy(ptr
, i_to_a(p
->pos
.to_units()));
2660 ptr
= strchr(ptr
, '\0');
2678 *ptr
++ = TAB_REPEAT_CHAR
;
2679 for (p
= repeated_list
; p
; p
= p
->next
) {
2680 strcpy(ptr
, i_to_a(p
->pos
.to_units()));
2681 ptr
= strchr(ptr
, '\0');
2702 tab_stops::tab_stops() : initial_list(0), repeated_list(0)
2706 tab_stops::tab_stops(const tab_stops
&ts
)
2707 : initial_list(0), repeated_list(0)
2709 tab
**p
= &initial_list
;
2710 tab
*t
= ts
.initial_list
;
2712 *p
= new tab(t
->pos
, t
->type
);
2717 t
= ts
.repeated_list
;
2719 *p
= new tab(t
->pos
, t
->type
);
2725 void tab_stops::clear()
2727 while (initial_list
) {
2728 tab
*tem
= initial_list
;
2729 initial_list
= initial_list
->next
;
2732 while (repeated_list
) {
2733 tab
*tem
= repeated_list
;
2734 repeated_list
= repeated_list
->next
;
2739 void tab_stops::add_tab(hunits pos
, tab_type type
, int repeated
)
2742 for (p
= repeated
? &repeated_list
: &initial_list
; *p
; p
= &(*p
)->next
)
2744 *p
= new tab(pos
, type
);
2748 void tab_stops::operator=(const tab_stops
&ts
)
2751 tab
**p
= &initial_list
;
2752 tab
*t
= ts
.initial_list
;
2754 *p
= new tab(t
->pos
, t
->type
);
2759 t
= ts
.repeated_list
;
2761 *p
= new tab(t
->pos
, t
->type
);
2770 hunits prev_pos
= 0;
2775 if (tok
.ch() == TAB_REPEAT_CHAR
) {
2780 if (!get_hunits(&pos
, 'm', prev_pos
))
2782 tab_type type
= TAB_LEFT
;
2783 if (tok
.ch() == 'C') {
2787 else if (tok
.ch() == 'R') {
2791 else if (tok
.ch() == 'L') {
2794 if (pos
<= prev_pos
&& !first
)
2796 "positions of tab stops must be strictly increasing");
2798 tabs
.add_tab(pos
, type
, repeated
);
2803 curenv
->tabs
= tabs
;
2804 curdiv
->modified_tag
.incl(MTSM_TA
);
2808 const char *environment::get_tabs()
2810 return tabs
.to_string();
2813 tab_type
environment::distance_to_next_tab(hunits
*distance
)
2816 ? curenv
->tabs
.distance_to_next_tab(get_text_length(), distance
)
2817 : curenv
->tabs
.distance_to_next_tab(get_input_line_position(), distance
);
2820 tab_type
environment::distance_to_next_tab(hunits
*distance
, hunits
*leftpos
)
2823 ? curenv
->tabs
.distance_to_next_tab(get_text_length(), distance
, leftpos
)
2824 : curenv
->tabs
.distance_to_next_tab(get_input_line_position(), distance
,
2828 void field_characters()
2830 field_delimiter_char
= get_optional_char();
2831 if (field_delimiter_char
)
2832 padding_indicator_char
= get_optional_char();
2834 padding_indicator_char
= 0;
2838 void line_tabs_request()
2841 if (has_arg() && get_integer(&n
))
2842 curenv
->line_tabs
= n
!= 0;
2844 curenv
->line_tabs
= 1;
2848 int environment::get_line_tabs()
2853 void environment::wrap_up_tab()
2860 switch (current_tab
) {
2862 tab_amount
= tab_distance
- tab_width
;
2863 line
= make_tab_node(tab_amount
, line
);
2866 tab_amount
= tab_distance
- tab_width
/2;
2867 line
= make_tab_node(tab_amount
, line
);
2874 width_total
+= tab_amount
;
2875 width_total
+= tab_width
;
2876 if (current_field
) {
2877 if (tab_precedes_field
) {
2878 pre_field_width
+= tab_amount
;
2879 tab_precedes_field
= 0;
2881 field_distance
-= tab_amount
;
2882 field_spaces
+= tab_field_spaces
;
2884 if (tab_contents
!= 0) {
2886 for (tem
= tab_contents
; tem
->next
!= 0; tem
= tem
->next
)
2889 line
= tab_contents
;
2891 tab_field_spaces
= 0;
2895 current_tab
= TAB_NONE
;
2898 node
*environment::make_tab_node(hunits d
, node
*next
)
2900 if (leader_node
!= 0 && d
< 0) {
2901 error("motion generated by leader cannot be negative");
2906 return new hmotion_node(d
, 1, 0, get_fill_color(), next
);
2907 node
*n
= new hline_node(d
, leader_node
, next
);
2912 void environment::handle_tab(int is_leader
)
2918 charinfo
*ci
= is_leader
? leader_char
: tab_char
;
2920 leader_node
= ci
? make_char_node(ci
) : 0;
2921 tab_type t
= distance_to_next_tab(&d
, &absolute
);
2926 add_node(make_tag("tab L", absolute
.to_units()));
2927 add_node(make_tab_node(d
));
2930 add_node(make_tag("tab R", absolute
.to_units()));
2933 add_node(make_tag("tab C", absolute
.to_units()));
2942 tab_field_spaces
= 0;
2945 void environment::start_field()
2947 assert(!current_field
);
2949 if (distance_to_next_tab(&d
) != TAB_NONE
) {
2950 pre_field_width
= get_text_length();
2954 tab_field_spaces
= 0;
2955 for (node
*p
= line
; p
; p
= p
->next
)
2960 tab_precedes_field
= current_tab
!= TAB_NONE
;
2963 error("zero field width");
2966 void environment::wrap_up_field()
2968 if (!current_tab
&& field_spaces
== 0)
2970 hunits padding
= field_distance
- (get_text_length() - pre_field_width
);
2971 if (current_tab
&& tab_field_spaces
!= 0) {
2972 hunits tab_padding
= scale(padding
,
2974 field_spaces
+ tab_field_spaces
);
2975 padding
-= tab_padding
;
2976 distribute_space(tab_contents
, tab_field_spaces
, tab_padding
, 1);
2977 tab_field_spaces
= 0;
2978 tab_width
+= tab_padding
;
2980 if (field_spaces
!= 0) {
2981 distribute_space(line
, field_spaces
, padding
, 1);
2982 width_total
+= padding
;
2984 // the start of the tab has been moved to the right by padding, so
2985 tab_distance
-= padding
;
2986 if (tab_distance
<= H0
) {
2987 // use the next tab stop instead
2988 current_tab
= tabs
.distance_to_next_tab(get_input_line_position()
2991 if (current_tab
== TAB_NONE
|| current_tab
== TAB_LEFT
) {
2992 width_total
+= tab_width
;
2993 if (current_tab
== TAB_LEFT
) {
2994 line
= make_tab_node(tab_distance
, line
);
2995 width_total
+= tab_distance
;
2996 current_tab
= TAB_NONE
;
2998 if (tab_contents
!= 0) {
3000 for (tem
= tab_contents
; tem
->next
!= 0; tem
= tem
->next
)
3003 line
= tab_contents
;
3015 void environment::add_padding()
3018 tab_contents
= new space_node(H0
, get_fill_color(), tab_contents
);
3024 line
= new space_node(H0
, get_fill_color(), line
);
3029 typedef int (environment::*INT_FUNCP
)();
3030 typedef vunits (environment::*VUNITS_FUNCP
)();
3031 typedef hunits (environment::*HUNITS_FUNCP
)();
3032 typedef const char *(environment::*STRING_FUNCP
)();
3040 int_env_reg(INT_FUNCP
);
3041 const char *get_string();
3042 int get_value(units
*val
);
3045 class vunits_env_reg
3051 vunits_env_reg(VUNITS_FUNCP f
);
3052 const char *get_string();
3053 int get_value(units
*val
);
3056 class hunits_env_reg
3062 hunits_env_reg(HUNITS_FUNCP f
);
3063 const char *get_string();
3064 int get_value(units
*val
);
3067 class string_env_reg
3073 string_env_reg(STRING_FUNCP
);
3074 const char *get_string();
3077 int_env_reg::int_env_reg(INT_FUNCP f
) : func(f
)
3081 int int_env_reg::get_value(units
*val
)
3083 *val
= (curenv
->*func
)();
3087 const char *int_env_reg::get_string()
3089 return i_to_a((curenv
->*func
)());
3092 vunits_env_reg::vunits_env_reg(VUNITS_FUNCP f
) : func(f
)
3096 int vunits_env_reg::get_value(units
*val
)
3098 *val
= (curenv
->*func
)().to_units();
3102 const char *vunits_env_reg::get_string()
3104 return i_to_a((curenv
->*func
)().to_units());
3107 hunits_env_reg::hunits_env_reg(HUNITS_FUNCP f
) : func(f
)
3111 int hunits_env_reg::get_value(units
*val
)
3113 *val
= (curenv
->*func
)().to_units();
3117 const char *hunits_env_reg::get_string()
3119 return i_to_a((curenv
->*func
)().to_units());
3122 string_env_reg::string_env_reg(STRING_FUNCP f
) : func(f
)
3126 const char *string_env_reg::get_string()
3128 return (curenv
->*func
)();
3131 class horizontal_place_reg
3132 : public general_reg
3135 horizontal_place_reg();
3136 int get_value(units
*);
3137 void set_value(units
);
3140 horizontal_place_reg::horizontal_place_reg()
3144 int horizontal_place_reg::get_value(units
*res
)
3146 *res
= curenv
->get_input_line_position().to_units();
3150 void horizontal_place_reg::set_value(units n
)
3152 curenv
->set_input_line_position(hunits(n
));
3155 int environment::get_zoom()
3157 return env_get_zoom(this);
3160 const char *environment::get_font_family_string()
3162 return family
->nm
.contents();
3165 const char *environment::get_glyph_color_string()
3167 return glyph_color
->nm
.contents();
3170 const char *environment::get_fill_color_string()
3172 return fill_color
->nm
.contents();
3175 const char *environment::get_font_name_string()
3177 symbol f
= get_font_name(fontno
, this);
3178 return f
.contents();
3181 const char *environment::get_style_name_string()
3183 symbol f
= get_style_name(fontno
);
3184 return f
.contents();
3187 const char *environment::get_name_string()
3189 return name
.contents();
3192 // Convert a quantity in scaled points to ascii decimal fraction.
3194 const char *sptoa(int sp
)
3197 assert(sizescale
> 0);
3200 if (sp
% sizescale
== 0)
3201 return i_to_a(sp
/sizescale
);
3202 // See if 1/sizescale is exactly representable as a decimal fraction,
3203 // ie its only prime factors are 2 and 5.
3206 while ((n
& 1) == 0) {
3211 while ((n
% 5) == 0) {
3216 int decimal_point
= power5
> power2
? power5
: power2
;
3217 if (decimal_point
<= 10) {
3220 for (t
= decimal_point
- power2
; --t
>= 0;)
3222 for (t
= decimal_point
- power5
; --t
>= 0;)
3224 if (factor
== 1 || sp
<= INT_MAX
/factor
)
3225 return if_to_a(sp
*factor
, decimal_point
);
3228 double s
= double(sp
)/double(sizescale
);
3229 double factor
= 10.0;
3231 int decimal_point
= 0;
3233 double v
= ceil(s
*factor
);
3238 } while (++decimal_point
< 10);
3239 return if_to_a(int(val
), decimal_point
);
3242 const char *environment::get_point_size_string()
3244 return sptoa(curenv
->get_point_size());
3247 const char *environment::get_requested_point_size_string()
3249 return sptoa(curenv
->get_requested_point_size());
3252 void environment::print_env()
3254 // at the time of calling .pev, those values are always zero or
3257 // char_height, char_slant,
3259 // current_tab, tab_width, tab_distance
3260 // current_field, field_distance, pre_field_width, field_spaces,
3261 // tab_field_spaces, tab_precedes_field
3264 errprint(" previous line length: %1u\n", prev_line_length
.to_units());
3265 errprint(" line length: %1u\n", line_length
.to_units());
3266 errprint(" previous title length: %1u\n", prev_title_length
.to_units());
3267 errprint(" title length: %1u\n", title_length
.to_units());
3268 errprint(" previous size: %1p (%2s)\n",
3269 prev_size
.to_points(), prev_size
.to_scaled_points());
3270 errprint(" size: %1p (%2s)\n",
3271 size
.to_points(), size
.to_scaled_points());
3272 errprint(" previous requested size: %1s\n", prev_requested_size
);
3273 errprint(" requested size: %1s\n", requested_size
);
3274 errprint(" previous font number: %1\n", prev_fontno
);
3275 errprint(" font number: %1\n", fontno
);
3276 errprint(" previous family: `%1'\n", prev_family
->nm
.contents());
3277 errprint(" family: `%1'\n", family
->nm
.contents());
3278 errprint(" space size: %1/36 em\n", space_size
);
3279 errprint(" sentence space size: %1/36 em\n", sentence_space_size
);
3280 errprint(" previous line interrupted: %1\n",
3281 prev_line_interrupted
? "yes" : "no");
3282 errprint(" fill mode: %1\n", fill
? "on" : "off");
3283 errprint(" adjust mode: %1\n",
3284 adjust_mode
== ADJUST_LEFT
3286 : adjust_mode
== ADJUST_BOTH
3288 : adjust_mode
== ADJUST_CENTER
3292 errprint(" lines to center: %1\n", center_lines
);
3293 if (right_justify_lines
)
3294 errprint(" lines to right justify: %1\n", right_justify_lines
);
3295 errprint(" previous vertical spacing: %1u\n",
3296 prev_vertical_spacing
.to_units());
3297 errprint(" vertical spacing: %1u\n", vertical_spacing
.to_units());
3298 errprint(" previous post-vertical spacing: %1u\n",
3299 prev_post_vertical_spacing
.to_units());
3300 errprint(" post-vertical spacing: %1u\n",
3301 post_vertical_spacing
.to_units());
3302 errprint(" previous line spacing: %1\n", prev_line_spacing
);
3303 errprint(" line spacing: %1\n", line_spacing
);
3304 errprint(" previous indentation: %1u\n", prev_indent
.to_units());
3305 errprint(" indentation: %1u\n", indent
.to_units());
3306 errprint(" temporary indentation: %1u\n", temporary_indent
.to_units());
3307 errprint(" have temporary indentation: %1\n",
3308 have_temporary_indent
? "yes" : "no");
3309 errprint(" currently used indentation: %1u\n", saved_indent
.to_units());
3310 errprint(" target text length: %1u\n", target_text_length
.to_units());
3311 if (underline_lines
) {
3312 errprint(" lines to underline: %1\n", underline_lines
);
3313 errprint(" font number before underlining: %1\n", pre_underline_fontno
);
3314 errprint(" underline spaces: %1\n", underline_spaces
? "yes" : "no");
3316 if (input_trap
.contents()) {
3317 errprint(" input trap macro: `%1'\n", input_trap
.contents());
3318 errprint(" input trap line counter: %1\n", input_trap_count
);
3319 errprint(" continued input trap: %1\n",
3320 continued_input_trap
? "yes" : "no");
3322 errprint(" previous text length: %1u\n", prev_text_length
.to_units());
3323 errprint(" total width: %1u\n", width_total
.to_units());
3324 errprint(" total number of spaces: %1\n", space_total
);
3325 errprint(" input line start: %1u\n", input_line_start
.to_units());
3326 errprint(" line tabs: %1\n", line_tabs
? "yes" : "no");
3327 errprint(" discarding: %1\n", discarding
? "yes" : "no");
3328 errprint(" spread flag set: %1\n", spread_flag
? "yes" : "no"); // \p
3329 if (margin_character_node
) {
3330 errprint(" margin character flags: %1\n",
3331 margin_character_flags
== MARGIN_CHARACTER_ON
3333 : margin_character_flags
== MARGIN_CHARACTER_NEXT
3335 : margin_character_flags
== MARGIN_CHARACTER_ON
3336 | MARGIN_CHARACTER_NEXT
3339 errprint(" margin character distance: %1u\n",
3340 margin_character_distance
.to_units());
3342 if (numbering_nodes
) {
3343 errprint(" line number digit width: %1u\n",
3344 line_number_digit_width
.to_units());
3345 errprint(" separation between number and text: %1 digit spaces\n",
3346 number_text_separation
);
3347 errprint(" line number indentation: %1 digit spaces\n",
3348 line_number_indent
);
3349 errprint(" print line numbers every %1line%1\n",
3350 line_number_multiple
> 1 ? i_to_a(line_number_multiple
) : "",
3351 line_number_multiple
> 1 ? "s" : "");
3352 errprint(" lines not to enumerate: %1\n", no_number_count
);
3354 string hf
= hyphenation_flags
? "on" : "off";
3355 if (hyphenation_flags
& HYPHEN_LAST_LINE
)
3356 hf
+= ", not last line";
3357 if (hyphenation_flags
& HYPHEN_LAST_CHARS
)
3358 hf
+= ", not last two chars";
3359 if (hyphenation_flags
& HYPHEN_FIRST_CHARS
)
3360 hf
+= ", not first two chars";
3362 errprint(" hyphenation_flags: %1\n", hf
.contents());
3363 errprint(" number of consecutive hyphenated lines: %1\n",
3365 errprint(" maximum number of consecutive hyphenated lines: %1\n",
3367 errprint(" hyphenation space: %1u\n", hyphenation_space
.to_units());
3368 errprint(" hyphenation margin: %1u\n", hyphenation_margin
.to_units());
3369 #ifdef WIDOW_CONTROL
3370 errprint(" widow control: %1\n", widow_control
? "yes" : "no");
3376 errprint("Current Environment:\n");
3377 curenv
->print_env();
3378 for (int i
= 0; i
< NENVIRONMENTS
; i
++) {
3380 errprint("Environment %1:\n", i
);
3381 if (env_table
[i
] != curenv
)
3382 env_table
[i
]->print_env();
3384 errprint(" current\n");
3387 dictionary_iterator
iter(env_dictionary
);
3390 while (iter
.get(&s
, (void **)&e
)) {
3391 assert(!s
.is_null());
3392 errprint("Environment %1:\n", s
.contents());
3396 errprint(" current\n");
3402 #define init_int_env_reg(name, func) \
3403 number_reg_dictionary.define(name, new int_env_reg(&environment::func))
3405 #define init_vunits_env_reg(name, func) \
3406 number_reg_dictionary.define(name, new vunits_env_reg(&environment::func))
3408 #define init_hunits_env_reg(name, func) \
3409 number_reg_dictionary.define(name, new hunits_env_reg(&environment::func))
3411 #define init_string_env_reg(name, func) \
3412 number_reg_dictionary.define(name, new string_env_reg(&environment::func))
3414 void init_env_requests()
3416 init_request("ad", adjust
);
3417 init_request("br", break_request
);
3418 init_request("brp", break_spread_request
);
3419 init_request("c2", no_break_control_char
);
3420 init_request("cc", control_char
);
3421 init_request("ce", center
);
3422 init_request("cu", continuous_underline
);
3423 init_request("ev", environment_switch
);
3424 init_request("evc", environment_copy
);
3425 init_request("fam", family_change
);
3426 init_request("fc", field_characters
);
3427 init_request("fi", fill
);
3428 init_request("fcolor", fill_color_change
);
3429 init_request("ft", font_change
);
3430 init_request("gcolor", glyph_color_change
);
3431 init_request("hc", hyphen_char
);
3432 init_request("hlm", hyphen_line_max_request
);
3433 init_request("hy", hyphenate_request
);
3434 init_request("hym", hyphenation_margin_request
);
3435 init_request("hys", hyphenation_space_request
);
3436 init_request("in", indent
);
3437 init_request("it", input_trap
);
3438 init_request("itc", input_trap_continued
);
3439 init_request("lc", leader_character
);
3440 init_request("linetabs", line_tabs_request
);
3441 init_request("ll", line_length
);
3442 init_request("ls", line_spacing
);
3443 init_request("lt", title_length
);
3444 init_request("mc", margin_character
);
3445 init_request("na", no_adjust
);
3446 init_request("nf", no_fill
);
3447 init_request("nh", no_hyphenate
);
3448 init_request("nm", number_lines
);
3449 init_request("nn", no_number
);
3450 init_request("pev", print_env
);
3451 init_request("ps", point_size
);
3452 init_request("pvs", post_vertical_spacing
);
3453 init_request("rj", right_justify
);
3454 init_request("sizes", override_sizes
);
3455 init_request("ss", space_size
);
3456 init_request("ta", set_tabs
);
3457 init_request("ti", temporary_indent
);
3458 init_request("tc", tab_character
);
3459 init_request("tl", title
);
3460 init_request("ul", underline
);
3461 init_request("vs", vertical_spacing
);
3462 #ifdef WIDOW_CONTROL
3463 init_request("wdc", widow_control_request
);
3464 #endif /* WIDOW_CONTROL */
3465 init_int_env_reg(".b", get_bold
);
3466 init_vunits_env_reg(".cdp", get_prev_char_depth
);
3467 init_int_env_reg(".ce", get_center_lines
);
3468 init_vunits_env_reg(".cht", get_prev_char_height
);
3469 init_hunits_env_reg(".csk", get_prev_char_skew
);
3470 init_string_env_reg(".ev", get_name_string
);
3471 init_int_env_reg(".f", get_font
);
3472 init_string_env_reg(".fam", get_font_family_string
);
3473 init_string_env_reg(".fn", get_font_name_string
);
3474 init_int_env_reg(".height", get_char_height
);
3475 init_int_env_reg(".hlc", get_hyphen_line_count
);
3476 init_int_env_reg(".hlm", get_hyphen_line_max
);
3477 init_int_env_reg(".hy", get_hyphenation_flags
);
3478 init_hunits_env_reg(".hym", get_hyphenation_margin
);
3479 init_hunits_env_reg(".hys", get_hyphenation_space
);
3480 init_hunits_env_reg(".i", get_indent
);
3481 init_hunits_env_reg(".in", get_saved_indent
);
3482 init_int_env_reg(".int", get_prev_line_interrupted
);
3483 init_int_env_reg(".linetabs", get_line_tabs
);
3484 init_hunits_env_reg(".lt", get_title_length
);
3485 init_int_env_reg(".j", get_adjust_mode
);
3486 init_hunits_env_reg(".k", get_text_length
);
3487 init_int_env_reg(".L", get_line_spacing
);
3488 init_hunits_env_reg(".l", get_line_length
);
3489 init_hunits_env_reg(".ll", get_saved_line_length
);
3490 init_string_env_reg(".M", get_fill_color_string
);
3491 init_string_env_reg(".m", get_glyph_color_string
);
3492 init_hunits_env_reg(".n", get_prev_text_length
);
3493 init_int_env_reg(".ps", get_point_size
);
3494 init_int_env_reg(".psr", get_requested_point_size
);
3495 init_vunits_env_reg(".pvs", get_post_vertical_spacing
);
3496 init_int_env_reg(".rj", get_right_justify_lines
);
3497 init_string_env_reg(".s", get_point_size_string
);
3498 init_int_env_reg(".slant", get_char_slant
);
3499 init_int_env_reg(".ss", get_space_size
);
3500 init_int_env_reg(".sss", get_sentence_space_size
);
3501 init_string_env_reg(".sr", get_requested_point_size_string
);
3502 init_string_env_reg(".sty", get_style_name_string
);
3503 init_string_env_reg(".tabs", get_tabs
);
3504 init_int_env_reg(".u", get_fill
);
3505 init_vunits_env_reg(".v", get_vertical_spacing
);
3506 init_hunits_env_reg(".w", get_prev_char_width
);
3507 init_int_env_reg(".zoom", get_zoom
);
3508 number_reg_dictionary
.define("ct", new variable_reg(&ct_reg_contents
));
3509 number_reg_dictionary
.define("hp", new horizontal_place_reg
);
3510 number_reg_dictionary
.define("ln", new variable_reg(&next_line_number
));
3511 number_reg_dictionary
.define("rsb", new variable_reg(&rsb_reg_contents
));
3512 number_reg_dictionary
.define("rst", new variable_reg(&rst_reg_contents
));
3513 number_reg_dictionary
.define("sb", new variable_reg(&sb_reg_contents
));
3514 number_reg_dictionary
.define("skw", new variable_reg(&skw_reg_contents
));
3515 number_reg_dictionary
.define("ssc", new variable_reg(&ssc_reg_contents
));
3516 number_reg_dictionary
.define("st", new variable_reg(&st_reg_contents
));
3519 // Hyphenation - TeX's hyphenation algorithm with a less fancy implementation.
3527 virtual void do_match(int len
, void *val
) = 0;
3528 virtual void do_delete(void *) = 0;
3529 void delete_trie_node(trie_node
*);
3533 virtual ~trie(); // virtual to shut up g++
3534 void insert(const char *, int, void *);
3535 // find calls do_match for each match it finds
3536 void find(const char *pat
, int patlen
);
3545 void do_match(int i
, void *v
);
3546 void do_delete(void *v
);
3547 void insert_pattern(const char *pat
, int patlen
, int *num
);
3548 void insert_hyphenation(dictionary
*ex
, const char *pat
, int patlen
);
3549 int hpf_getc(file_case
*fcp
);
3554 void hyphenate(const char *word
, int len
, int *hyphens
);
3555 void read_patterns_file(const char *name
, int append
, dictionary
*ex
);
3558 class hyphenation_language
3562 dictionary exceptions
;
3563 hyphen_trie patterns
;
3565 hyphenation_language(symbol nm
) : name(nm
), exceptions(501) {}
3566 ~hyphenation_language() { }
3569 dictionary
language_dictionary(5);
3570 hyphenation_language
*current_language
= 0;
3572 static void set_hyphenation_language()
3574 symbol nm
= get_name(1);
3575 if (!nm
.is_null()) {
3576 current_language
= (hyphenation_language
*)language_dictionary
.lookup(nm
);
3577 if (!current_language
) {
3578 current_language
= new hyphenation_language(nm
);
3579 (void)language_dictionary
.lookup(nm
, (void *)current_language
);
3585 const int WORD_MAX
= 256; // we use unsigned char for offsets in
3586 // hyphenation exceptions
3588 static void hyphen_word()
3590 if (!current_language
) {
3591 error("no current hyphenation language");
3595 char buf
[WORD_MAX
+ 1];
3596 unsigned char pos
[WORD_MAX
+ 2];
3599 if (tok
.newline() || tok
.eof())
3603 while (i
< WORD_MAX
&& !tok
.space() && !tok
.newline() && !tok
.eof()) {
3604 charinfo
*ci
= tok
.get_char(1);
3610 if (ci
->get_ascii_code() == '-') {
3611 if (i
> 0 && (npos
== 0 || pos
[npos
- 1] != i
))
3615 unsigned char c
= ci
->get_hyphenation_code();
3624 unsigned char *tem
= new unsigned char[npos
+ 1];
3625 memcpy(tem
, pos
, npos
+ 1);
3626 tem
= (unsigned char *)current_language
->exceptions
.lookup(symbol(buf
),
3643 trie_node(char, trie_node
*);
3646 trie_node::trie_node(char ch
, trie_node
*p
)
3647 : c(ch
), down(0), right(p
), val(0)
3658 delete_trie_node(tp
);
3662 void trie::delete_trie_node(trie_node
*p
)
3665 delete_trie_node(p
->down
);
3666 delete_trie_node(p
->right
);
3673 void trie::insert(const char *pat
, int patlen
, void *val
)
3675 trie_node
**p
= &tp
;
3676 assert(patlen
> 0 && pat
!= 0);
3678 while (*p
!= 0 && (*p
)->c
< pat
[0])
3680 if (*p
== 0 || (*p
)->c
!= pat
[0])
3681 *p
= new trie_node(pat
[0], *p
);
3682 if (--patlen
== 0) {
3691 void trie::find(const char *pat
, int patlen
)
3694 for (int i
= 0; p
!= 0 && i
< patlen
; i
++) {
3695 while (p
!= 0 && p
->c
< pat
[i
])
3697 if (p
!= 0 && p
->c
== pat
[i
]) {
3699 do_match(i
+1, p
->val
);
3714 operation(int, int, operation
*);
3717 operation::operation(int i
, int j
, operation
*op
)
3718 : next(op
), distance(j
), num(i
)
3722 void hyphen_trie::insert_pattern(const char *pat
, int patlen
, int *num
)
3725 for (int i
= 0; i
< patlen
+1; i
++)
3727 op
= new operation(num
[i
], patlen
- i
, op
);
3728 insert(pat
, patlen
, op
);
3731 void hyphen_trie::insert_hyphenation(dictionary
*ex
, const char *pat
,
3734 char buf
[WORD_MAX
+ 1];
3735 unsigned char pos
[WORD_MAX
+ 2];
3738 while (j
< patlen
) {
3739 unsigned char c
= pat
[j
++];
3741 if (i
> 0 && (npos
== 0 || pos
[npos
- 1] != i
))
3745 buf
[i
++] = hpf_code_table
[c
];
3750 unsigned char *tem
= new unsigned char[npos
+ 1];
3751 memcpy(tem
, pos
, npos
+ 1);
3752 tem
= (unsigned char *)ex
->lookup(symbol(buf
), tem
);
3758 void hyphen_trie::hyphenate(const char *word
, int len
, int *hyphens
)
3761 for (j
= 0; j
< len
+ 1; j
++)
3763 for (j
= 0; j
< len
- 1; j
++) {
3765 find(word
+ j
, len
- j
);
3769 inline int max(int m
, int n
)
3771 return m
> n
? m
: n
;
3774 void hyphen_trie::do_match(int i
, void *v
)
3776 operation
*op
= (operation
*)v
;
3778 h
[i
- op
->distance
] = max(h
[i
- op
->distance
], op
->num
);
3783 void hyphen_trie::do_delete(void *v
)
3785 operation
*op
= (operation
*)v
;
3787 operation
*tem
= op
;
3793 /* We use very simple rules to parse TeX's hyphenation patterns.
3795 . `%' starts a comment even if preceded by `\'.
3797 . No support for digraphs and like `\$'.
3799 . `^^xx' (`x' is 0-9 or a-f), and `^^x' (character code of `x' in the
3800 range 0-127) are recognized; other use of `^' causes an error.
3802 . No macro expansion.
3804 . We check for the expression `\patterns{...}' (possibly with
3805 whitespace before and after the braces). Everything between the
3806 braces is taken as hyphenation patterns. Consequently, `{' and `}'
3807 are not allowed in patterns.
3809 . Similarly, `\hyphenation{...}' gives a list of hyphenation
3812 . `\endinput' is recognized also.
3814 . For backwards compatibility, if `\patterns' is missing, the
3815 whole file is treated as a list of hyphenation patterns (only
3816 recognizing `%' as the start of a comment.
3820 int hyphen_trie::hpf_getc(file_case
*fcp
)
3822 int c
= fcp
->get_c();
3832 if (((c
>= '0' && c
<= '9') || (c
>= 'a' && c
<= 'f'))
3833 && ((c1
>= '0' && c1
<= '9') || (c1
>= 'a' && c1
<= 'f'))) {
3834 if (c
>= '0' && c
<= '9')
3838 if (c1
>= '0' && c1
<= '9')
3846 if (c
>= 0 && c
<= 63)
3848 else if (c
>= 64 && c
<= 127)
3855 error("invalid ^, ^^x, or ^^xx character in hyphenation patterns file");
3859 void hyphen_trie::read_patterns_file(const char *name
, int append
,
3866 for (int i
= 0; i
< WORD_MAX
; i
++)
3868 int num
[WORD_MAX
+1];
3869 int have_patterns
= 0; // we've seen \patterns
3870 int final_pattern
= 0; // 1 if we have a trailing closing brace
3871 int have_hyphenation
= 0; // we've seen \hyphenation
3872 int final_hyphenation
= 0; // 1 if we have a trailing closing brace
3873 int have_keyword
= 0; // we've seen either \patterns or \hyphenation
3874 int traditional
= 0; // don't handle \patterns
3877 if ((fcp
= mac_path
->open_file(name
, fcp
->fc_const_path
)) == NULL
) {
3878 error("can't find hyphenation patterns file `%1'", name
);
3882 for (c
= hpf_getc(fcp
);;) {
3884 if (c
== '%') { // skip comments
3887 } while (c
!= EOF
&& c
!= '\n');
3889 if (c
== EOF
|| !csspace(c
))
3894 if (have_keyword
|| traditional
) // we are done
3896 else { // rescan file in `traditional' mode
3905 if (!(c
== '{' || c
== '}')) { // skip braces at line start
3906 do { // scan patterns
3914 } while (i
< WORD_MAX
&& c
!= EOF
&& !csspace(c
)
3915 && c
!= '%' && c
!= '{' && c
!= '}');
3918 if (i
>= 9 && !strncmp(buf
+ i
- 9, "\\patterns", 9)) {
3922 if (have_patterns
|| have_hyphenation
)
3923 error("\\patterns not allowed inside of %1 group",
3924 have_patterns
? "\\patterns" : "\\hyphenation");
3933 else if (i
>= 12 && !strncmp(buf
+ i
- 12, "\\hyphenation", 12)) {
3937 if (have_patterns
|| have_hyphenation
)
3938 error("\\hyphenation not allowed inside of %1 group",
3939 have_patterns
? "\\patterns" : "\\hyphenation");
3941 have_hyphenation
= 1;
3948 else if (strstr(buf
, "\\endinput")) {
3949 if (have_patterns
|| have_hyphenation
)
3950 error("found \\endinput inside of %1 group",
3951 have_patterns
? "\\patterns" : "\\hyphenation");
3954 else if (c
== '}') {
3955 if (have_patterns
) {
3960 else if (have_hyphenation
) {
3961 have_hyphenation
= 0;
3963 final_hyphenation
= 1;
3967 else if (c
== '{') {
3968 if (have_patterns
|| have_hyphenation
)
3969 error("`{' not allowed within %1 group",
3970 have_patterns
? "\\patterns" : "\\hyphenation");
3971 c
= hpf_getc(fcp
); // skipped if not starting \patterns
3976 if (c
== '{' || c
== '}')
3980 if (have_patterns
|| final_pattern
|| traditional
) {
3981 for (int j
= 0; j
< i
; j
++)
3982 buf
[j
] = hpf_code_table
[(unsigned char)buf
[j
]];
3983 insert_pattern(buf
, i
, num
);
3986 else if (have_hyphenation
|| final_hyphenation
) {
3987 insert_hyphenation(ex
, buf
, i
);
3988 final_hyphenation
= 0;
3998 void hyphenate(hyphen_list
*h
, unsigned flags
)
4000 if (!current_language
)
4003 while (h
&& h
->hyphenation_code
== 0)
4006 char hbuf
[WORD_MAX
+ 2];
4007 char *buf
= hbuf
+ 1;
4009 for (tem
= h
; tem
&& len
< WORD_MAX
; tem
= tem
->next
) {
4010 if (tem
->hyphenation_code
!= 0)
4011 buf
[len
++] = tem
->hyphenation_code
;
4015 hyphen_list
*nexth
= tem
;
4019 = (unsigned char *)current_language
->exceptions
.lookup(buf
);
4023 for (tem
= h
; tem
!= 0; tem
= tem
->next
, i
++)
4030 hbuf
[0] = hbuf
[len
+ 1] = '.';
4031 int num
[WORD_MAX
+ 3];
4032 current_language
->patterns
.hyphenate(hbuf
, len
+ 2, num
);
4035 if (flags
& HYPHEN_FIRST_CHARS
)
4037 if (flags
& HYPHEN_LAST_CHARS
)
4039 for (i
= 2, tem
= h
; i
< len
&& tem
; tem
= tem
->next
, i
++)
4048 static void do_hyphenation_patterns_file(int append
)
4050 symbol name
= get_long_name(1);
4051 if (!name
.is_null()) {
4052 if (!current_language
)
4053 error("no current hyphenation language");
4055 current_language
->patterns
.read_patterns_file(
4056 name
.contents(), append
,
4057 ¤t_language
->exceptions
);
4062 static void hyphenation_patterns_file()
4064 do_hyphenation_patterns_file(0);
4067 static void hyphenation_patterns_file_append()
4069 do_hyphenation_patterns_file(1);
4072 class hyphenation_language_reg
: public reg
{
4074 const char *get_string();
4077 const char *hyphenation_language_reg::get_string()
4079 return current_language
? current_language
->name
.contents() : "";
4082 void init_hyphen_requests()
4084 init_request("hw", hyphen_word
);
4085 init_request("hla", set_hyphenation_language
);
4086 init_request("hpf", hyphenation_patterns_file
);
4087 init_request("hpfa", hyphenation_patterns_file_append
);
4088 number_reg_dictionary
.define(".hla", new hyphenation_language_reg
);