2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005,
4 Free Software Foundation, Inc.
5 Written by James Clark (jjc@jclark.com)
7 This file is part of groff.
9 groff is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation; either version 2, or (at your option) any later
14 groff is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 You should have received a copy of the GNU General Public License along
20 with groff; see the file COPYING. If not, write to the Free Software
21 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
24 #include "dictionary.h"
26 #include "stringclass.h"
29 #include "file_case.h"
37 #include "macropath.h"
41 symbol
default_family("T");
43 enum { ADJUST_LEFT
= 0, ADJUST_BOTH
= 1, ADJUST_CENTER
= 3, ADJUST_RIGHT
= 5 };
45 enum { HYPHEN_LAST_LINE
= 2, HYPHEN_LAST_CHARS
= 4, HYPHEN_FIRST_CHARS
= 8 };
50 env_list(environment
*e
, env_list
*p
) : env(e
), next(p
) {}
54 const int NENVIRONMENTS
= 10;
55 environment
*env_table
[NENVIRONMENTS
];
56 dictionary
env_dictionary(10);
58 static int next_line_number
= 0;
59 extern int suppress_push
;
60 extern statem
*get_diversion_state();
62 charinfo
*field_delimiter_char
;
63 charinfo
*padding_indicator_char
;
65 int translate_space_to_dummy
= 0;
67 class pending_output_line
{
75 int last_line
; // Is it the last line of the paragraph?
76 #endif /* WIDOW_CONTROL */
78 pending_output_line
*next
;
80 pending_output_line(node
*, int, vunits
, vunits
, hunits
, int,
81 pending_output_line
* = 0);
82 ~pending_output_line();
86 friend void environment::mark_last_line();
87 friend void environment::output(node
*, int, vunits
, vunits
, hunits
, int);
88 #endif /* WIDOW_CONTROL */
91 pending_output_line::pending_output_line(node
*n
, int nf
, vunits v
, vunits pv
,
93 pending_output_line
*p
)
94 : nd(n
), no_fill(nf
), was_centered(ce
), vs(v
), post_vs(pv
), width(w
),
97 #endif /* WIDOW_CONTROL */
102 pending_output_line::~pending_output_line()
104 delete_node_list(nd
);
107 int pending_output_line::output()
109 if (trap_sprung_flag
)
112 if (next
&& next
->last_line
&& !no_fill
) {
113 curdiv
->need(vs
+ post_vs
+ vunits(vresolution
));
114 if (trap_sprung_flag
) {
115 next
->last_line
= 0; // Try to avoid infinite loops.
120 curenv
->construct_format_state(nd
, was_centered
, !no_fill
);
121 curdiv
->output(nd
, no_fill
, vs
, post_vs
, width
);
126 void environment::output(node
*nd
, int no_fill_flag
,
127 vunits vs
, vunits post_vs
,
128 hunits width
, int was_centered
)
131 while (pending_lines
) {
132 if (widow_control
&& !pending_lines
->no_fill
&& !pending_lines
->next
)
134 if (!pending_lines
->output())
136 pending_output_line
*tem
= pending_lines
;
137 pending_lines
= pending_lines
->next
;
140 #else /* WIDOW_CONTROL */
141 output_pending_lines();
142 #endif /* WIDOW_CONTROL */
143 if (!trap_sprung_flag
&& !pending_lines
145 && (!widow_control
|| no_fill_flag
)
146 #endif /* WIDOW_CONTROL */
148 curenv
->construct_format_state(nd
, was_centered
, !no_fill_flag
);
149 curdiv
->output(nd
, no_fill_flag
, vs
, post_vs
, width
);
151 pending_output_line
**p
;
152 for (p
= &pending_lines
; *p
; p
= &(*p
)->next
)
154 *p
= new pending_output_line(nd
, no_fill_flag
, vs
, post_vs
, width
,
159 // a line from .tl goes at the head of the queue
161 void environment::output_title(node
*nd
, int no_fill_flag
,
162 vunits vs
, vunits post_vs
,
165 if (!trap_sprung_flag
)
166 curdiv
->output(nd
, no_fill_flag
, vs
, post_vs
, width
);
168 pending_lines
= new pending_output_line(nd
, no_fill_flag
, vs
, post_vs
,
169 width
, 0, pending_lines
);
172 void environment::output_pending_lines()
174 while (pending_lines
&& pending_lines
->output()) {
175 pending_output_line
*tem
= pending_lines
;
176 pending_lines
= pending_lines
->next
;
183 void environment::mark_last_line()
185 if (!widow_control
|| !pending_lines
)
187 pending_output_line
*p
;
188 for (p
= pending_lines
; p
->next
; p
= p
->next
)
194 void widow_control_request()
197 if (has_arg() && get_integer(&n
))
198 curenv
->widow_control
= n
!= 0;
200 curenv
->widow_control
= 1;
204 #endif /* WIDOW_CONTROL */
206 /* font_size functions */
208 size_range
*font_size::size_table
= 0;
209 int font_size::nranges
= 0;
213 int compare_ranges(const void *p1
, const void *p2
)
215 return ((size_range
*)p1
)->min
- ((size_range
*)p2
)->min
;
220 void font_size::init_size_table(int *sizes
)
223 while (sizes
[nranges
*2] != 0)
226 size_table
= new size_range
[nranges
];
227 for (int i
= 0; i
< nranges
; i
++) {
228 size_table
[i
].min
= sizes
[i
*2];
229 size_table
[i
].max
= sizes
[i
*2 + 1];
231 qsort(size_table
, nranges
, sizeof(size_range
), compare_ranges
);
234 font_size::font_size(int sp
)
236 for (int i
= 0; i
< nranges
; i
++) {
237 if (sp
< size_table
[i
].min
) {
238 if (i
> 0 && size_table
[i
].min
- sp
>= sp
- size_table
[i
- 1].max
)
239 p
= size_table
[i
- 1].max
;
241 p
= size_table
[i
].min
;
244 if (sp
<= size_table
[i
].max
) {
249 p
= size_table
[nranges
- 1].max
;
252 int font_size::to_units()
254 return scale(p
, units_per_inch
, sizescale
*72);
257 // we can't do this in a static constructor because various dictionaries
258 // have to get initialized first
260 void init_environments()
262 curenv
= env_table
[0] = new environment("0");
267 curenv
->tab_char
= get_optional_char();
271 void leader_character()
273 curenv
->leader_char
= get_optional_char();
277 void environment::add_char(charinfo
*ci
)
283 // don't allow fields in dummy environments
284 else if (ci
== field_delimiter_char
&& !dummy
) {
290 else if (current_field
&& ci
== padding_indicator_char
)
292 else if (current_tab
) {
293 if (tab_contents
== 0)
294 tab_contents
= new line_start_node
;
295 if (ci
!= hyphen_indicator_char
)
296 tab_contents
= tab_contents
->add_char(ci
, this, &tab_width
, &s
, &gc_np
);
298 tab_contents
= tab_contents
->add_discretionary_hyphen();
304 fprintf(stderr
, "current line is\n");
305 line
->debug_node_list();
307 if (ci
!= hyphen_indicator_char
)
308 line
= line
->add_char(ci
, this, &width_total
, &space_total
, &gc_np
);
310 line
= line
->add_discretionary_hyphen();
313 fprintf(stderr
, "now after we have added character the line is\n");
314 line
->debug_node_list();
316 if ((!suppress_push
) && gc_np
) {
317 if (gc_np
&& (gc_np
->state
== 0)) {
318 gc_np
->state
= construct_state(0);
319 gc_np
->push_state
= get_diversion_state();
321 else if (line
&& (line
->state
== 0)) {
322 line
->state
= construct_state(0);
323 line
->push_state
= get_diversion_state();
327 fprintf(stderr
, "now we have possibly added the state the line is\n");
328 line
->debug_node_list();
332 node
*environment::make_char_node(charinfo
*ci
)
334 return make_node(ci
, this);
337 void environment::add_node(node
*n
)
341 if (!suppress_push
) {
342 if (n
->is_special
&& n
->state
== NULL
)
343 n
->state
= construct_state(0);
344 n
->push_state
= get_diversion_state();
347 if (current_tab
|| current_field
)
352 else if (current_tab
) {
353 n
->next
= tab_contents
;
355 tab_width
+= n
->width();
359 if (discarding
&& n
->discardable()) {
360 // XXX possibly: input_line_start -= n->width();
366 width_total
+= n
->width();
367 space_total
+= n
->nspaces();
370 construct_new_line_state(line
);
374 void environment::add_hyphen_indicator()
376 if (current_tab
|| interrupted
|| current_field
377 || hyphen_indicator_char
!= 0)
381 line
= line
->add_discretionary_hyphen();
384 int environment::get_hyphenation_flags()
386 return hyphenation_flags
;
389 int environment::get_hyphen_line_max()
391 return hyphen_line_max
;
394 int environment::get_hyphen_line_count()
396 return hyphen_line_count
;
399 int environment::get_center_lines()
404 int environment::get_right_justify_lines()
406 return right_justify_lines
;
409 void environment::add_italic_correction()
413 tab_contents
= tab_contents
->add_italic_correction(&tab_width
);
416 line
= line
->add_italic_correction(&width_total
);
419 void environment::space_newline()
421 assert(!current_tab
&& !current_field
);
425 hunits sw
= env_space_width(this);
426 hunits ssw
= env_sentence_space_width(this);
427 if (!translate_space_to_dummy
) {
429 if (node_list_ends_sentence(line
) == 1)
432 width_list
*w
= new width_list(sw
, ssw
);
433 if (node_list_ends_sentence(line
) == 1)
434 w
->next
= new width_list(sw
, ssw
);
435 if (line
!= 0 && line
->merge_space(x
, sw
, ssw
)) {
439 add_node(new word_space_node(x
, get_fill_color(), w
));
440 possibly_break_line(0, spread_flag
);
444 void environment::space()
446 space(env_space_width(this), env_sentence_space_width(this));
449 void environment::space(hunits space_width
, hunits sentence_space_width
)
453 if (current_field
&& padding_indicator_char
== 0) {
457 hunits x
= translate_space_to_dummy
? H0
: space_width
;
458 node
*p
= current_tab
? tab_contents
: line
;
459 hunits
*tp
= current_tab
? &tab_width
: &width_total
;
460 if (p
&& p
->nspaces() == 1 && p
->width() == x
461 && node_list_ends_sentence(p
->next
) == 1) {
462 hunits xx
= translate_space_to_dummy
? H0
: sentence_space_width
;
463 if (p
->merge_space(xx
, space_width
, sentence_space_width
)) {
468 if (p
&& p
->merge_space(x
, space_width
, sentence_space_width
)) {
472 add_node(new word_space_node(x
,
474 new width_list(space_width
,
475 sentence_space_width
)));
476 possibly_break_line(0, spread_flag
);
480 node
*do_underline_special(int);
482 void environment::set_font(symbol nm
)
486 if (nm
== symbol("P") || nm
.is_empty()) {
487 if (family
->make_definite(prev_fontno
) < 0)
490 fontno
= prev_fontno
;
494 prev_fontno
= fontno
;
495 int n
= symbol_fontno(nm
);
497 n
= next_available_font_position();
498 if (!mount_font(n
, nm
))
501 if (family
->make_definite(n
) < 0)
505 if (underline_spaces
&& fontno
!= prev_fontno
) {
506 if (fontno
== get_underline_fontno())
507 add_node(do_underline_special(1));
508 if (prev_fontno
== get_underline_fontno())
509 add_node(do_underline_special(0));
513 void environment::set_font(int n
)
517 if (is_good_fontno(n
)) {
518 prev_fontno
= fontno
;
522 warning(WARN_FONT
, "bad font number");
525 void environment::set_family(symbol fam
)
529 if (fam
.is_null() || fam
.is_empty()) {
530 if (prev_family
->make_definite(fontno
) < 0)
532 font_family
*tem
= family
;
533 family
= prev_family
;
537 font_family
*f
= lookup_family(fam
);
538 if (f
->make_definite(fontno
) < 0)
540 prev_family
= family
;
545 void environment::set_size(int n
)
550 font_size temp
= prev_size
;
553 int temp2
= prev_requested_size
;
554 prev_requested_size
= requested_size
;
555 requested_size
= temp2
;
560 prev_requested_size
= requested_size
;
565 void environment::set_char_height(int n
)
569 if (n
== requested_size
|| n
<= 0)
575 void environment::set_char_slant(int n
)
582 color
*environment::get_prev_glyph_color()
584 return prev_glyph_color
;
587 color
*environment::get_glyph_color()
592 color
*environment::get_prev_fill_color()
594 return prev_fill_color
;
597 color
*environment::get_fill_color()
602 void environment::set_glyph_color(color
*c
)
606 curenv
->prev_glyph_color
= curenv
->glyph_color
;
607 curenv
->glyph_color
= c
;
610 void environment::set_fill_color(color
*c
)
614 curenv
->prev_fill_color
= curenv
->fill_color
;
615 curenv
->fill_color
= c
;
618 environment::environment(symbol nm
)
620 prev_line_length((units_per_inch
*13)/2),
621 line_length((units_per_inch
*13)/2),
622 prev_title_length((units_per_inch
*13)/2),
623 title_length((units_per_inch
*13)/2),
624 prev_size(sizescale
*10),
626 requested_size(sizescale
*10),
627 prev_requested_size(sizescale
*10),
631 sentence_space_size(12),
632 adjust_mode(ADJUST_BOTH
),
635 prev_line_interrupted(0),
637 right_justify_lines(0),
638 prev_vertical_spacing(points_to_units(12)),
639 vertical_spacing(points_to_units(12)),
640 prev_post_vertical_spacing(0),
641 post_vertical_spacing(0),
642 prev_line_spacing(1),
647 have_temporary_indent(0),
651 continued_input_trap(0),
658 current_tab(TAB_NONE
),
661 leader_char(charset_table
['.']),
665 margin_character_flags(0),
666 margin_character_node(0),
667 margin_character_distance(points_to_units(10)),
669 number_text_separation(1),
670 line_number_indent(0),
671 line_number_multiple(1),
673 hyphenation_flags(1),
674 hyphen_line_count(0),
676 hyphenation_space(H0
),
677 hyphenation_margin(H0
),
682 #endif /* WIDOW_CONTROL */
683 glyph_color(&default_color
),
684 prev_glyph_color(&default_color
),
685 fill_color(&default_color
),
686 prev_fill_color(&default_color
),
689 suppress_next_eol(0),
691 tabs(units_per_inch
/2, TAB_LEFT
),
694 no_break_control_char('\''),
695 hyphen_indicator_char(0)
697 prev_family
= family
= lookup_family(default_family
);
698 prev_fontno
= fontno
= 1;
699 if (!is_good_fontno(1))
700 fatal("font number 1 not a valid font");
701 if (family
->make_definite(1) < 0)
702 fatal("invalid default family `%1'", default_family
.contents());
703 prev_fontno
= fontno
;
706 environment::environment(const environment
*e
)
708 prev_line_length(e
->prev_line_length
),
709 line_length(e
->line_length
),
710 prev_title_length(e
->prev_title_length
),
711 title_length(e
->title_length
),
712 prev_size(e
->prev_size
),
714 requested_size(e
->requested_size
),
715 prev_requested_size(e
->prev_requested_size
),
716 char_height(e
->char_height
),
717 char_slant(e
->char_slant
),
718 prev_fontno(e
->prev_fontno
),
720 prev_family(e
->prev_family
),
722 space_size(e
->space_size
),
723 sentence_space_size(e
->sentence_space_size
),
724 adjust_mode(e
->adjust_mode
),
727 prev_line_interrupted(0),
729 right_justify_lines(0),
730 prev_vertical_spacing(e
->prev_vertical_spacing
),
731 vertical_spacing(e
->vertical_spacing
),
732 prev_post_vertical_spacing(e
->prev_post_vertical_spacing
),
733 post_vertical_spacing(e
->post_vertical_spacing
),
734 prev_line_spacing(e
->prev_line_spacing
),
735 line_spacing(e
->line_spacing
),
736 prev_indent(e
->prev_indent
),
739 have_temporary_indent(0),
743 continued_input_trap(0),
745 prev_text_length(e
->prev_text_length
),
749 line_tabs(e
->line_tabs
),
750 current_tab(TAB_NONE
),
752 tab_char(e
->tab_char
),
753 leader_char(e
->leader_char
),
757 margin_character_flags(e
->margin_character_flags
),
758 margin_character_node(e
->margin_character_node
),
759 margin_character_distance(e
->margin_character_distance
),
761 number_text_separation(e
->number_text_separation
),
762 line_number_indent(e
->line_number_indent
),
763 line_number_multiple(e
->line_number_multiple
),
764 no_number_count(e
->no_number_count
),
765 hyphenation_flags(e
->hyphenation_flags
),
766 hyphen_line_count(0),
767 hyphen_line_max(e
->hyphen_line_max
),
768 hyphenation_space(e
->hyphenation_space
),
769 hyphenation_margin(e
->hyphenation_margin
),
773 widow_control(e
->widow_control
),
774 #endif /* WIDOW_CONTROL */
775 glyph_color(e
->glyph_color
),
776 prev_glyph_color(e
->prev_glyph_color
),
777 fill_color(e
->fill_color
),
778 prev_fill_color(e
->prev_fill_color
),
779 seen_space(e
->seen_space
),
780 seen_eol(e
->seen_eol
),
781 suppress_next_eol(e
->suppress_next_eol
),
782 seen_break(e
->seen_break
),
784 name(e
->name
), // so that eg `.if "\n[.ev]"0"' works
785 control_char(e
->control_char
),
786 no_break_control_char(e
->no_break_control_char
),
787 hyphen_indicator_char(e
->hyphen_indicator_char
)
791 void environment::copy(const environment
*e
)
793 prev_line_length
= e
->prev_line_length
;
794 line_length
= e
->line_length
;
795 prev_title_length
= e
->prev_title_length
;
796 title_length
= e
->title_length
;
797 prev_size
= e
->prev_size
;
799 prev_requested_size
= e
->prev_requested_size
;
800 requested_size
= e
->requested_size
;
801 char_height
= e
->char_height
;
802 char_slant
= e
->char_slant
;
803 space_size
= e
->space_size
;
804 sentence_space_size
= e
->sentence_space_size
;
805 adjust_mode
= e
->adjust_mode
;
808 prev_line_interrupted
= 0;
810 right_justify_lines
= 0;
811 prev_vertical_spacing
= e
->prev_vertical_spacing
;
812 vertical_spacing
= e
->vertical_spacing
;
813 prev_post_vertical_spacing
= e
->prev_post_vertical_spacing
,
814 post_vertical_spacing
= e
->post_vertical_spacing
,
815 prev_line_spacing
= e
->prev_line_spacing
;
816 line_spacing
= e
->line_spacing
;
817 prev_indent
= e
->prev_indent
;
819 have_temporary_indent
= 0;
820 temporary_indent
= 0;
822 underline_spaces
= 0;
823 input_trap_count
= 0;
824 continued_input_trap
= 0;
825 prev_text_length
= e
->prev_text_length
;
828 input_line_start
= 0;
829 control_char
= e
->control_char
;
830 no_break_control_char
= e
->no_break_control_char
;
831 hyphen_indicator_char
= e
->hyphen_indicator_char
;
837 line_tabs
= e
->line_tabs
;
838 current_tab
= TAB_NONE
;
840 margin_character_flags
= e
->margin_character_flags
;
841 if (e
->margin_character_node
)
842 margin_character_node
= e
->margin_character_node
->copy();
843 margin_character_distance
= e
->margin_character_distance
;
845 number_text_separation
= e
->number_text_separation
;
846 line_number_multiple
= e
->line_number_multiple
;
847 line_number_indent
= e
->line_number_indent
;
848 no_number_count
= e
->no_number_count
;
849 tab_char
= e
->tab_char
;
850 leader_char
= e
->leader_char
;
851 hyphenation_flags
= e
->hyphenation_flags
;
853 prev_fontno
= e
->prev_fontno
;
856 prev_family
= e
->prev_family
;
859 widow_control
= e
->widow_control
;
860 #endif /* WIDOW_CONTROL */
861 hyphen_line_max
= e
->hyphen_line_max
;
862 hyphen_line_count
= 0;
863 hyphenation_space
= e
->hyphenation_space
;
864 hyphenation_margin
= e
->hyphenation_margin
;
866 glyph_color
= e
->glyph_color
;
867 prev_glyph_color
= e
->prev_glyph_color
;
868 fill_color
= e
->fill_color
;
869 prev_fill_color
= e
->prev_fill_color
;
872 environment::~environment()
875 delete_node_list(line
);
876 delete_node_list(numbering_nodes
);
879 hunits
environment::get_input_line_position()
883 n
= -input_line_start
;
885 n
= width_total
- input_line_start
;
891 void environment::set_input_line_position(hunits n
)
893 input_line_start
= line
== 0 ? -n
: width_total
- n
;
895 input_line_start
+= tab_width
;
898 hunits
environment::get_line_length()
903 hunits
environment::get_saved_line_length()
906 return target_text_length
+ saved_indent
;
911 vunits
environment::get_vertical_spacing()
913 return vertical_spacing
;
916 vunits
environment::get_post_vertical_spacing()
918 return post_vertical_spacing
;
921 int environment::get_line_spacing()
926 vunits
environment::total_post_vertical_spacing()
928 vunits
tem(post_vertical_spacing
);
929 if (line_spacing
> 1)
930 tem
+= (line_spacing
- 1)*vertical_spacing
;
934 int environment::get_bold()
936 return get_bold_fontno(fontno
);
939 hunits
environment::get_digit_width()
941 return env_digit_width(this);
944 int environment::get_adjust_mode()
949 int environment::get_fill()
954 hunits
environment::get_indent()
959 hunits
environment::get_saved_indent()
963 else if (have_temporary_indent
)
964 return temporary_indent
;
969 hunits
environment::get_temporary_indent()
971 return temporary_indent
;
974 hunits
environment::get_title_length()
979 node
*environment::get_prev_char()
981 for (node
*n
= current_tab
? tab_contents
: line
; n
; n
= n
->next
) {
982 node
*last
= n
->last_char_node();
989 hunits
environment::get_prev_char_width()
991 node
*last
= get_prev_char();
994 return last
->width();
997 hunits
environment::get_prev_char_skew()
999 node
*last
= get_prev_char();
1002 return last
->skew();
1005 vunits
environment::get_prev_char_height()
1007 node
*last
= get_prev_char();
1011 last
->vertical_extent(&min
, &max
);
1015 vunits
environment::get_prev_char_depth()
1017 node
*last
= get_prev_char();
1021 last
->vertical_extent(&min
, &max
);
1025 hunits
environment::get_text_length()
1027 hunits n
= line
== 0 ? H0
: width_total
;
1033 hunits
environment::get_prev_text_length()
1035 return prev_text_length
;
1039 static int sb_reg_contents
= 0;
1040 static int st_reg_contents
= 0;
1041 static int ct_reg_contents
= 0;
1042 static int rsb_reg_contents
= 0;
1043 static int rst_reg_contents
= 0;
1044 static int skw_reg_contents
= 0;
1045 static int ssc_reg_contents
= 0;
1047 void environment::width_registers()
1049 // this is used to implement \w; it sets the st, sb, ct registers
1050 vunits min
= 0, max
= 0, cur
= 0;
1051 int character_type
= 0;
1052 ssc_reg_contents
= line
? line
->subscript_correction().to_units() : 0;
1053 skw_reg_contents
= line
? line
->skew().to_units() : 0;
1054 line
= reverse_node_list(line
);
1055 vunits real_min
= V0
;
1056 vunits real_max
= V0
;
1058 for (node
*tem
= line
; tem
; tem
= tem
->next
) {
1059 tem
->vertical_extent(&v1
, &v2
);
1066 if ((cur
+= tem
->vertical_width()) < min
)
1070 character_type
|= tem
->character_type();
1072 line
= reverse_node_list(line
);
1073 st_reg_contents
= -min
.to_units();
1074 sb_reg_contents
= -max
.to_units();
1075 rst_reg_contents
= -real_min
.to_units();
1076 rsb_reg_contents
= -real_max
.to_units();
1077 ct_reg_contents
= character_type
;
1080 node
*environment::extract_output_line()
1089 /* environment related requests */
1091 void environment_switch()
1093 int pop
= 0; // 1 means pop, 2 means pop but no error message on underflow
1094 if (curenv
->is_dummy())
1095 error("can't switch environments when current environment is dummy");
1096 else if (!has_arg())
1100 if (!tok
.delimiter()) {
1101 // It looks like a number.
1103 if (get_integer(&n
)) {
1104 if (n
>= 0 && n
< NENVIRONMENTS
) {
1105 env_stack
= new env_list(curenv
, env_stack
);
1106 if (env_table
[n
] == 0)
1107 env_table
[n
] = new environment(i_to_a(n
));
1108 curenv
= env_table
[n
];
1117 nm
= get_long_name(1);
1121 if (!nm
.is_null()) {
1122 environment
*e
= (environment
*)env_dictionary
.lookup(nm
);
1124 e
= new environment(nm
);
1125 (void)env_dictionary
.lookup(nm
, e
);
1127 env_stack
= new env_list(curenv
, env_stack
);
1132 if (env_stack
== 0) {
1134 error("environment stack underflow");
1137 int seen_space
= curenv
->seen_space
;
1138 int seen_eol
= curenv
->seen_eol
;
1139 int suppress_next_eol
= curenv
->suppress_next_eol
;
1140 curenv
= env_stack
->env
;
1141 curenv
->seen_space
= seen_space
;
1142 curenv
->seen_eol
= seen_eol
;
1143 curenv
->suppress_next_eol
= suppress_next_eol
;
1144 env_list
*tem
= env_stack
;
1145 env_stack
= env_stack
->next
;
1152 void environment_copy()
1157 if (!tok
.delimiter()) {
1158 // It looks like a number.
1160 if (get_integer(&n
)) {
1161 if (n
>= 0 && n
< NENVIRONMENTS
)
1168 nm
= get_long_name(1);
1169 if (!e
&& !nm
.is_null())
1170 e
= (environment
*)env_dictionary
.lookup(nm
);
1172 error("No environment to copy from");
1180 void fill_color_change()
1182 symbol s
= get_name();
1184 curenv
->set_fill_color(curenv
->get_prev_fill_color());
1190 void glyph_color_change()
1192 symbol s
= get_name();
1194 curenv
->set_glyph_color(curenv
->get_prev_glyph_color());
1200 static symbol
P_symbol("P");
1204 symbol s
= get_name();
1206 if (s
.is_null() || s
== P_symbol
) {
1211 for (const char *p
= s
.contents(); p
!= 0 && *p
!= 0; p
++)
1218 curenv
->set_font(atoi(s
.contents()));
1220 curenv
->set_font(s
);
1224 void family_change()
1226 symbol s
= get_name();
1227 curenv
->set_family(s
);
1234 if (has_arg() && get_number(&n
, 'z', curenv
->get_requested_point_size())) {
1237 curenv
->set_size(n
);
1240 curenv
->set_size(0);
1244 void override_sizes()
1247 int *sizes
= new int[n
];
1249 char *buf
= read_string();
1252 char *p
= strtok(buf
, " \t");
1257 switch (sscanf(p
, "%d-%d", &lower
, &upper
)) {
1262 if (lower
<= upper
&& lower
>= 0)
1266 warning(WARN_RANGE
, "bad size range `%1'", p
);
1270 int *old_sizes
= sizes
;
1271 sizes
= new int[n
*2];
1272 memcpy(sizes
, old_sizes
, n
*sizeof(int));
1280 p
= strtok(0, " \t");
1282 font_size::init_size_table(sizes
);
1288 if (get_integer(&n
)) {
1289 curenv
->space_size
= n
;
1290 if (has_arg() && get_integer(&n
))
1291 curenv
->sentence_space_size
= n
;
1293 curenv
->sentence_space_size
= curenv
->space_size
;
1300 while (!tok
.newline() && !tok
.eof())
1310 while (!tok
.newline() && !tok
.eof())
1315 curenv
->suppress_next_eol
= 1;
1322 if (!has_arg() || !get_integer(&n
))
1326 while (!tok
.newline() && !tok
.eof())
1330 curenv
->right_justify_lines
= 0;
1331 curenv
->center_lines
= n
;
1332 curdiv
->modified_tag
.incl(MTSM_CE
);
1336 void right_justify()
1339 if (!has_arg() || !get_integer(&n
))
1343 while (!tok
.newline() && !tok
.eof())
1347 curenv
->center_lines
= 0;
1348 curenv
->right_justify_lines
= n
;
1349 curdiv
->modified_tag
.incl(MTSM_RJ
);
1356 if (has_arg() && get_hunits(&temp
, 'm', curenv
->line_length
)) {
1358 warning(WARN_RANGE
, "bad line length %1u", temp
.to_units());
1363 temp
= curenv
->prev_line_length
;
1364 curenv
->prev_line_length
= curenv
->line_length
;
1365 curenv
->line_length
= temp
;
1366 curdiv
->modified_tag
.incl(MTSM_LL
);
1373 if (has_arg() && get_hunits(&temp
, 'm', curenv
->title_length
)) {
1375 warning(WARN_RANGE
, "bad title length %1u", temp
.to_units());
1380 temp
= curenv
->prev_title_length
;
1381 curenv
->prev_title_length
= curenv
->title_length
;
1382 curenv
->title_length
= temp
;
1386 void vertical_spacing()
1389 if (has_arg() && get_vunits(&temp
, 'p', curenv
->vertical_spacing
)) {
1391 warning(WARN_RANGE
, "vertical spacing must not be negative");
1396 temp
= curenv
->prev_vertical_spacing
;
1397 curenv
->prev_vertical_spacing
= curenv
->vertical_spacing
;
1398 curenv
->vertical_spacing
= temp
;
1402 void post_vertical_spacing()
1405 if (has_arg() && get_vunits(&temp
, 'p', curenv
->post_vertical_spacing
)) {
1408 "post vertical spacing must be greater than or equal to 0");
1413 temp
= curenv
->prev_post_vertical_spacing
;
1414 curenv
->prev_post_vertical_spacing
= curenv
->post_vertical_spacing
;
1415 curenv
->post_vertical_spacing
= temp
;
1422 if (has_arg() && get_integer(&temp
)) {
1424 warning(WARN_RANGE
, "value %1 out of range: interpreted as 1", temp
);
1429 temp
= curenv
->prev_line_spacing
;
1430 curenv
->prev_line_spacing
= curenv
->line_spacing
;
1431 curenv
->line_spacing
= temp
;
1438 if (has_arg() && get_hunits(&temp
, 'm', curenv
->indent
)) {
1440 warning(WARN_RANGE
, "indent cannot be negative");
1445 temp
= curenv
->prev_indent
;
1446 while (!tok
.newline() && !tok
.eof())
1450 curenv
->have_temporary_indent
= 0;
1451 curenv
->prev_indent
= curenv
->indent
;
1452 curenv
->indent
= temp
;
1453 curdiv
->modified_tag
.incl(MTSM_IN
);
1457 void temporary_indent()
1461 if (!get_hunits(&temp
, 'm', curenv
->get_indent()))
1463 while (!tok
.newline() && !tok
.eof())
1468 warning(WARN_RANGE
, "total indent cannot be negative");
1472 curenv
->temporary_indent
= temp
;
1473 curenv
->have_temporary_indent
= 1;
1474 curdiv
->modified_tag
.incl(MTSM_TI
);
1479 node
*do_underline_special(int underline_spaces
)
1482 m
.append_str("x u ");
1483 m
.append(underline_spaces
+ '0');
1484 return new special_node(m
, 1);
1487 void do_underline(int underline_spaces
)
1490 if (!has_arg() || !get_integer(&n
))
1493 if (curenv
->underline_lines
> 0) {
1494 curenv
->prev_fontno
= curenv
->fontno
;
1495 curenv
->fontno
= curenv
->pre_underline_fontno
;
1496 if (underline_spaces
) {
1497 curenv
->underline_spaces
= 0;
1498 curenv
->add_node(do_underline_special(0));
1501 curenv
->underline_lines
= 0;
1504 curenv
->underline_lines
= n
;
1505 curenv
->pre_underline_fontno
= curenv
->fontno
;
1506 curenv
->fontno
= get_underline_fontno();
1507 if (underline_spaces
) {
1508 curenv
->underline_spaces
= 1;
1509 curenv
->add_node(do_underline_special(1));
1515 void continuous_underline()
1527 curenv
->control_char
= '.';
1530 error("bad control character");
1532 curenv
->control_char
= tok
.ch();
1537 void no_break_control_char()
1539 curenv
->no_break_control_char
= '\'';
1542 error("bad control character");
1544 curenv
->no_break_control_char
= tok
.ch();
1549 void margin_character()
1553 charinfo
*ci
= tok
.get_char();
1555 // Call tok.next() only after making the node so that
1556 // .mc \s+9\(br\s0 works.
1557 node
*nd
= curenv
->make_char_node(ci
);
1560 delete curenv
->margin_character_node
;
1561 curenv
->margin_character_node
= nd
;
1562 curenv
->margin_character_flags
= MARGIN_CHARACTER_ON
1563 | MARGIN_CHARACTER_NEXT
;
1565 if (has_arg() && get_hunits(&d
, 'm'))
1566 curenv
->margin_character_distance
= d
;
1570 check_missing_character();
1571 curenv
->margin_character_flags
&= ~MARGIN_CHARACTER_ON
;
1572 if (curenv
->margin_character_flags
== 0) {
1573 delete curenv
->margin_character_node
;
1574 curenv
->margin_character_node
= 0;
1582 delete_node_list(curenv
->numbering_nodes
);
1583 curenv
->numbering_nodes
= 0;
1586 for (int i
= '9'; i
>= '0'; i
--) {
1587 node
*tem
= make_node(charset_table
[i
], curenv
);
1595 curenv
->numbering_nodes
= nd
;
1596 curenv
->line_number_digit_width
= env_digit_width(curenv
);
1598 if (!tok
.delimiter()) {
1599 if (get_integer(&n
, next_line_number
)) {
1600 next_line_number
= n
;
1601 if (next_line_number
< 0) {
1602 warning(WARN_RANGE
, "negative line number");
1603 next_line_number
= 0;
1608 while (!tok
.space() && !tok
.newline() && !tok
.eof())
1611 if (!tok
.delimiter()) {
1612 if (get_integer(&n
)) {
1614 warning(WARN_RANGE
, "negative or zero line number multiple");
1617 curenv
->line_number_multiple
= n
;
1621 while (!tok
.space() && !tok
.newline() && !tok
.eof())
1624 if (!tok
.delimiter()) {
1625 if (get_integer(&n
))
1626 curenv
->number_text_separation
= n
;
1629 while (!tok
.space() && !tok
.newline() && !tok
.eof())
1631 if (has_arg() && !tok
.delimiter() && get_integer(&n
))
1632 curenv
->line_number_indent
= n
;
1642 if (has_arg() && get_integer(&n
))
1643 curenv
->no_number_count
= n
> 0 ? n
: 0;
1645 curenv
->no_number_count
= 1;
1651 curenv
->hyphenation_flags
= 0;
1655 void hyphenate_request()
1658 if (has_arg() && get_integer(&n
))
1659 curenv
->hyphenation_flags
= n
;
1661 curenv
->hyphenation_flags
= 1;
1667 curenv
->hyphen_indicator_char
= get_optional_char();
1671 void hyphen_line_max_request()
1674 if (has_arg() && get_integer(&n
))
1675 curenv
->hyphen_line_max
= n
;
1677 curenv
->hyphen_line_max
= -1;
1681 void environment::interrupt()
1684 add_node(new transparent_dummy_node
);
1689 void environment::newline()
1691 int was_centered
= 0;
1692 if (underline_lines
> 0) {
1693 if (--underline_lines
== 0) {
1694 prev_fontno
= fontno
;
1695 fontno
= pre_underline_fontno
;
1696 if (underline_spaces
) {
1697 underline_spaces
= 0;
1698 add_node(do_underline_special(0));
1706 // strip trailing spaces
1707 while (line
!= 0 && line
->discardable()) {
1708 width_total
-= line
->width();
1709 space_total
-= line
->nspaces();
1714 node
*to_be_output
= 0;
1715 hunits to_be_output_width
;
1716 prev_line_interrupted
= 0;
1719 else if (interrupted
) {
1721 // see environment::final_break
1722 prev_line_interrupted
= exit_started
? 2 : 1;
1724 else if (center_lines
> 0) {
1726 hunits x
= target_text_length
- width_total
;
1728 saved_indent
+= x
/2;
1729 to_be_output
= line
;
1731 to_be_output_width
= width_total
;
1734 else if (right_justify_lines
> 0) {
1735 --right_justify_lines
;
1736 hunits x
= target_text_length
- width_total
;
1739 to_be_output
= line
;
1740 to_be_output_width
= width_total
;
1746 to_be_output
= line
;
1747 to_be_output_width
= width_total
;
1750 input_line_start
= line
== 0 ? H0
: width_total
;
1752 if (is_html
&& !fill
) {
1753 curdiv
->modified_tag
.incl(MTSM_EOL
);
1754 if (suppress_next_eol
)
1755 suppress_next_eol
= 0;
1760 output_line(to_be_output
, to_be_output_width
, was_centered
);
1761 hyphen_line_count
= 0;
1763 if (input_trap_count
> 0) {
1764 if (!(continued_input_trap
&& prev_line_interrupted
))
1765 if (--input_trap_count
== 0)
1766 spring_trap(input_trap
);
1770 void environment::output_line(node
*n
, hunits width
, int was_centered
)
1772 prev_text_length
= width
;
1773 if (margin_character_flags
) {
1774 hunits d
= line_length
+ margin_character_distance
- saved_indent
- width
;
1776 n
= new hmotion_node(d
, get_fill_color(), n
);
1779 margin_character_flags
&= ~MARGIN_CHARACTER_NEXT
;
1781 if (!margin_character_flags
) {
1782 tem
= margin_character_node
;
1783 margin_character_node
= 0;
1786 tem
= margin_character_node
->copy();
1789 width
+= tem
->width();
1793 node
*tem
= n
->next
;
1798 if (!saved_indent
.is_zero())
1799 nn
= new hmotion_node(saved_indent
, get_fill_color(), nn
);
1800 width
+= saved_indent
;
1801 if (no_number_count
> 0)
1803 else if (numbering_nodes
) {
1804 hunits w
= (line_number_digit_width
1805 *(3+line_number_indent
+number_text_separation
));
1806 if (next_line_number
% line_number_multiple
!= 0)
1807 nn
= new hmotion_node(w
, get_fill_color(), nn
);
1810 nn
= new hmotion_node(number_text_separation
* line_number_digit_width
,
1811 get_fill_color(), nn
);
1812 x
-= number_text_separation
*line_number_digit_width
;
1814 sprintf(buf
, "%3d", next_line_number
);
1815 for (char *p
= strchr(buf
, '\0') - 1; p
>= buf
&& *p
!= ' '; --p
) {
1816 node
*gn
= numbering_nodes
;
1817 for (int count
= *p
- '0'; count
> 0; count
--)
1824 nn
= new hmotion_node(x
, get_fill_color(), nn
);
1829 output(nn
, !fill
, vertical_spacing
, total_post_vertical_spacing(), width
,
1833 void environment::start_line()
1837 line
= new line_start_node
;
1838 if (have_temporary_indent
) {
1839 saved_indent
= temporary_indent
;
1840 have_temporary_indent
= 0;
1843 saved_indent
= indent
;
1844 target_text_length
= line_length
- saved_indent
;
1849 hunits
environment::get_hyphenation_space()
1851 return hyphenation_space
;
1854 void hyphenation_space_request()
1857 if (get_hunits(&n
, 'm')) {
1859 warning(WARN_RANGE
, "hyphenation space cannot be negative");
1862 curenv
->hyphenation_space
= n
;
1867 hunits
environment::get_hyphenation_margin()
1869 return hyphenation_margin
;
1872 void hyphenation_margin_request()
1875 if (get_hunits(&n
, 'm')) {
1877 warning(WARN_RANGE
, "hyphenation margin cannot be negative");
1880 curenv
->hyphenation_margin
= n
;
1885 breakpoint
*environment::choose_breakpoint()
1887 hunits x
= width_total
;
1888 int s
= space_total
;
1890 breakpoint
*best_bp
= 0; // the best breakpoint so far
1891 int best_bp_fits
= 0;
1895 breakpoint
*bp
= n
->get_breakpoints(x
, s
);
1897 if (bp
->width
<= target_text_length
) {
1898 if (!bp
->hyphenated
) {
1899 breakpoint
*tem
= bp
->next
;
1902 breakpoint
*tem1
= tem
;
1907 // Decide whether to use the hyphenated breakpoint.
1908 && (hyphen_line_max
< 0
1909 // Only choose the hyphenated breakpoint if it would not
1910 // exceed the maximum number of consecutive hyphenated
1912 || hyphen_line_count
+ 1 <= hyphen_line_max
)
1913 && !(adjust_mode
== ADJUST_BOTH
1914 // Don't choose the hyphenated breakpoint if the line
1915 // can be justified by adding no more than
1916 // hyphenation_space to any word space.
1918 && (((target_text_length
- bp
->width
1919 + (bp
->nspaces
- 1)*hresolution
)/bp
->nspaces
)
1920 <= hyphenation_space
))
1921 // Don't choose the hyphenated breakpoint if the line
1922 // is no more than hyphenation_margin short.
1923 : target_text_length
- bp
->width
<= hyphenation_margin
)) {
1932 if ((adjust_mode
== ADJUST_BOTH
1933 ? hyphenation_space
== H0
1934 : hyphenation_margin
== H0
)
1935 && (hyphen_line_max
< 0
1936 || hyphen_line_count
+ 1 <= hyphen_line_max
)) {
1937 // No need to consider a non-hyphenated breakpoint.
1940 breakpoint
*tem
= bp
->next
;
1943 breakpoint
*tem1
= tem
;
1949 // It fits but it's hyphenated.
1950 if (!best_bp_fits
) {
1958 breakpoint
*tem
= bp
;
1975 output_warning(WARN_BREAK
, "can't break line");
1981 void environment::hyphenate_line(int start_here
)
1984 hyphenation_type prev_type
= line
->get_hyphenation_type();
1989 for (startp
= &line
->next
; *startp
!= 0; startp
= &(*startp
)->next
) {
1990 hyphenation_type this_type
= (*startp
)->get_hyphenation_type();
1991 if (prev_type
== HYPHEN_BOUNDARY
&& this_type
== HYPHEN_MIDDLE
)
1993 prev_type
= this_type
;
1997 node
*tem
= *startp
;
2000 } while (tem
!= 0 && tem
->get_hyphenation_type() == HYPHEN_MIDDLE
);
2001 int inhibit
= (tem
!= 0 && tem
->get_hyphenation_type() == HYPHEN_INHIBIT
);
2003 hyphen_list
*sl
= 0;
2007 while (tem
!= end
) {
2008 sl
= tem
->get_hyphen_list(sl
, &i
);
2011 tem1
->next
= forward
;
2015 // this is for characters like hyphen and emdash
2017 for (hyphen_list
*h
= sl
; h
; h
= h
->next
) {
2018 h
->breakable
= (prev_code
!= 0
2020 && h
->next
->hyphenation_code
!= 0);
2021 prev_code
= h
->hyphenation_code
;
2024 if (hyphenation_flags
!= 0
2026 // this may not be right if we have extra space on this line
2027 && !((hyphenation_flags
& HYPHEN_LAST_LINE
)
2028 && (curdiv
->distance_to_next_trap()
2029 <= vertical_spacing
+ total_post_vertical_spacing()))
2031 hyphenate(sl
, hyphenation_flags
);
2032 while (forward
!= 0) {
2033 node
*tem1
= forward
;
2034 forward
= forward
->next
;
2036 tem
= tem1
->add_self(tem
, &sl
);
2041 static node
*node_list_reverse(node
*n
)
2053 static void distribute_space(node
*n
, int nspaces
, hunits desired_space
,
2054 int force_reverse
= 0)
2056 static int reverse
= 0;
2057 if (force_reverse
|| reverse
)
2058 n
= node_list_reverse(n
);
2059 if (!force_reverse
&& nspaces
> 0 && spread_limit
>= 0
2060 && desired_space
.to_units() > 0) {
2061 hunits em
= curenv
->get_size();
2062 double Ems
= (double)desired_space
.to_units() / nspaces
2063 / (em
.is_zero() ? hresolution
: em
.to_units());
2064 if (Ems
> spread_limit
)
2065 output_warning(WARN_BREAK
, "spreading %1m per space", Ems
);
2067 for (node
*tem
= n
; tem
; tem
= tem
->next
)
2068 tem
->spread_space(&nspaces
, &desired_space
);
2069 if (force_reverse
|| reverse
)
2070 (void)node_list_reverse(n
);
2073 assert(desired_space
.is_zero() && nspaces
== 0);
2076 void environment::possibly_break_line(int start_here
, int forced
)
2078 int was_centered
= center_lines
> 0;
2079 if (!fill
|| current_tab
|| current_field
|| dummy
)
2083 // When a macro follows a paragraph in fill mode, the
2084 // current line should not be empty.
2085 || (width_total
- line
->width()) > target_text_length
)) {
2086 hyphenate_line(start_here
);
2087 breakpoint
*bp
= choose_breakpoint();
2089 // we'll find one eventually
2093 while (*ndp
!= bp
->nd
)
2094 ndp
= &(*ndp
)->next
;
2095 bp
->nd
->split(bp
->index
, &pre
, &post
);
2097 hunits extra_space_width
= H0
;
2098 switch(adjust_mode
) {
2100 if (bp
->nspaces
!= 0)
2101 extra_space_width
= target_text_length
- bp
->width
;
2102 else if (bp
->width
> 0 && target_text_length
> 0
2103 && target_text_length
> bp
->width
)
2104 output_warning(WARN_BREAK
, "cannot adjust line");
2107 saved_indent
+= (target_text_length
- bp
->width
)/2;
2111 saved_indent
+= target_text_length
- bp
->width
;
2114 distribute_space(pre
, bp
->nspaces
, extra_space_width
);
2115 hunits output_width
= bp
->width
+ extra_space_width
;
2116 input_line_start
-= output_width
;
2118 hyphen_line_count
++;
2120 hyphen_line_count
= 0;
2124 node
*first_non_discardable
= 0;
2126 for (tem
= line
; tem
!= 0; tem
= tem
->next
)
2127 if (!tem
->discardable())
2128 first_non_discardable
= tem
;
2129 node
*to_be_discarded
;
2130 if (first_non_discardable
) {
2131 to_be_discarded
= first_non_discardable
->next
;
2132 first_non_discardable
->next
= 0;
2133 for (tem
= line
; tem
!= 0; tem
= tem
->next
) {
2134 width_total
+= tem
->width();
2135 space_total
+= tem
->nspaces();
2141 to_be_discarded
= line
;
2144 // Do output_line() here so that line will be 0 iff the
2145 // the environment will be empty.
2146 output_line(pre
, output_width
, was_centered
);
2147 while (to_be_discarded
!= 0) {
2148 tem
= to_be_discarded
;
2149 to_be_discarded
= to_be_discarded
->next
;
2150 input_line_start
-= tem
->width();
2154 if (have_temporary_indent
) {
2155 saved_indent
= temporary_indent
;
2156 have_temporary_indent
= 0;
2159 saved_indent
= indent
;
2160 target_text_length
= line_length
- saved_indent
;
2166 Do the break at the end of input after the end macro (if any).
2168 Unix troff behaves as follows: if the last line is
2172 it will output foo on the current page, and bar on the next page;
2181 everything will be output on the current page. This behaviour must be
2184 The problem is that some macro packages rely on this. For example,
2185 the ATK macros have an end macro that emits \c if it needs to print a
2186 table of contents but doesn't do a 'bp in the end macro; instead the
2187 'bp is done in the bottom of page trap. This works with Unix troff,
2188 provided that the current environment is not empty at the end of the
2191 The following will make macro packages that do that sort of thing work
2192 even if the current environment is empty at the end of the input file.
2193 If the last input line used \c and this line occurred in the end macro,
2194 then we'll force everything out on the current page, but we'll make
2195 sure that the environment isn't empty so that we won't exit at the
2196 bottom of this page.
2199 void environment::final_break()
2201 if (prev_line_interrupted
== 2) {
2203 add_node(new transparent_dummy_node
);
2209 node
*environment::make_tag(const char *nm
, int i
)
2213 * need to emit tag for post-grohtml
2214 * but we check to see whether we can emit specials
2216 if (curdiv
== topdiv
&& topdiv
->before_first_page
)
2217 topdiv
->begin_page();
2218 macro
*m
= new macro
;
2219 m
->append_str("devtag:");
2220 for (const char *p
= nm
; *p
; p
++)
2221 if (!invalid_input_char((unsigned char)*p
))
2225 return new special_node(*m
);
2230 void environment::dump_troff_state()
2233 fprintf(stderr
, SPACES
"register `in' = %d\n", curenv
->indent
.to_units());
2234 if (curenv
->have_temporary_indent
)
2235 fprintf(stderr
, SPACES
"register `ti' = %d\n",
2236 curenv
->temporary_indent
.to_units());
2237 fprintf(stderr
, SPACES
"centered lines `ce' = %d\n", curenv
->center_lines
);
2238 fprintf(stderr
, SPACES
"register `ll' = %d\n",
2239 curenv
->line_length
.to_units());
2240 fprintf(stderr
, SPACES
"fill `fi=1/nf=0' = %d\n", curenv
->fill
);
2241 fprintf(stderr
, SPACES
"page offset `po' = %d\n",
2242 topdiv
->get_page_offset().to_units());
2243 fprintf(stderr
, SPACES
"seen_break = %d\n", curenv
->seen_break
);
2244 fprintf(stderr
, SPACES
"seen_space = %d\n", curenv
->seen_space
);
2249 statem
*environment::construct_state(int only_eol
)
2252 statem
*s
= new statem();
2254 s
->add_tag(MTSM_IN
, indent
);
2255 s
->add_tag(MTSM_LL
, line_length
);
2256 s
->add_tag(MTSM_PO
, topdiv
->get_page_offset().to_units());
2257 s
->add_tag(MTSM_RJ
, right_justify_lines
);
2258 if (have_temporary_indent
)
2259 s
->add_tag(MTSM_TI
, temporary_indent
);
2262 s
->add_tag(MTSM_BR
);
2263 if (seen_space
!= 0)
2264 s
->add_tag(MTSM_SP
, seen_space
);
2269 s
->add_tag(MTSM_EOL
);
2270 s
->add_tag(MTSM_CE
, center_lines
);
2279 void environment::construct_format_state(node
*n
, int was_centered
,
2283 // find first glyph node which has a state.
2284 while (n
!= 0 && n
->state
== 0)
2286 if (n
== 0 || (n
->state
== 0))
2288 if (seen_space
!= 0)
2289 n
->state
->add_tag(MTSM_SP
, seen_space
);
2290 if (seen_eol
&& topdiv
== curdiv
)
2291 n
->state
->add_tag(MTSM_EOL
);
2295 n
->state
->add_tag(MTSM_CE
, center_lines
+1);
2297 n
->state
->add_tag_if_unknown(MTSM_CE
, 0);
2298 n
->state
->add_tag_if_unknown(MTSM_FI
, filling
);
2301 if (n
->state
!= 0) {
2302 n
->state
->sub_tag_ce();
2303 n
->state
->add_tag_if_unknown(MTSM_FI
, filling
);
2310 void environment::construct_new_line_state(node
*n
)
2313 // find first glyph node which has a state.
2314 while (n
!= 0 && n
->state
== 0)
2316 if (n
== 0 || n
->state
== 0)
2318 if (seen_space
!= 0)
2319 n
->state
->add_tag(MTSM_SP
, seen_space
);
2320 if (seen_eol
&& topdiv
== curdiv
)
2321 n
->state
->add_tag(MTSM_EOL
);
2327 extern int global_diverted_space
;
2329 void environment::do_break(int do_spread
)
2331 int was_centered
= 0;
2332 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
2333 topdiv
->begin_page();
2339 // this is so that hyphenation works
2340 if (line
->nspaces() == 0) {
2341 line
= new space_node(H0
, get_fill_color(), line
);
2344 possibly_break_line(0, do_spread
);
2346 while (line
!= 0 && line
->discardable()) {
2347 width_total
-= line
->width();
2348 space_total
-= line
->nspaces();
2354 input_line_start
= H0
;
2357 switch (adjust_mode
) {
2359 saved_indent
+= (target_text_length
- width_total
)/2;
2363 saved_indent
+= target_text_length
- width_total
;
2369 output_line(tem
, width_total
, was_centered
);
2370 hyphen_line_count
= 0;
2372 prev_line_interrupted
= 0;
2373 #ifdef WIDOW_CONTROL
2375 output_pending_lines();
2376 #endif /* WIDOW_CONTROL */
2377 if (!global_diverted_space
) {
2378 curdiv
->modified_tag
.incl(MTSM_BR
);
2383 int environment::is_empty()
2385 return !current_tab
&& line
== 0 && pending_lines
== 0;
2388 void do_break_request(int spread
)
2390 while (!tok
.newline() && !tok
.eof())
2393 curenv
->do_break(spread
);
2397 void break_request()
2399 do_break_request(0);
2402 void break_spread_request()
2404 do_break_request(1);
2409 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
2410 handle_initial_title();
2414 hunits part_width
[3];
2415 part
[0] = part
[1] = part
[2] = 0;
2416 environment
env(curenv
);
2417 environment
*oldenv
= curenv
;
2419 read_title_parts(part
, part_width
);
2421 curenv
->size
= env
.size
;
2422 curenv
->prev_size
= env
.prev_size
;
2423 curenv
->requested_size
= env
.requested_size
;
2424 curenv
->prev_requested_size
= env
.prev_requested_size
;
2425 curenv
->char_height
= env
.char_height
;
2426 curenv
->char_slant
= env
.char_slant
;
2427 curenv
->fontno
= env
.fontno
;
2428 curenv
->prev_fontno
= env
.prev_fontno
;
2429 curenv
->glyph_color
= env
.glyph_color
;
2430 curenv
->prev_glyph_color
= env
.prev_glyph_color
;
2431 curenv
->fill_color
= env
.fill_color
;
2432 curenv
->prev_fill_color
= env
.prev_fill_color
;
2441 hunits
length_title(curenv
->title_length
);
2442 hunits f
= length_title
- part_width
[1];
2444 n
= new hmotion_node(f2
- part_width
[2], curenv
->get_fill_color(), n
);
2452 n
= new hmotion_node(f
- f2
- part_width
[0], curenv
->get_fill_color(), n
);
2460 curenv
->output_title(n
, !curenv
->fill
, curenv
->vertical_spacing
,
2461 curenv
->total_post_vertical_spacing(), length_title
);
2462 curenv
->hyphen_line_count
= 0;
2468 curenv
->adjust_mode
|= 1;
2472 curenv
->adjust_mode
= ADJUST_LEFT
;
2475 curenv
->adjust_mode
= ADJUST_RIGHT
;
2478 curenv
->adjust_mode
= ADJUST_CENTER
;
2482 curenv
->adjust_mode
= ADJUST_BOTH
;
2486 if (get_integer(&n
)) {
2488 warning(WARN_RANGE
, "negative adjustment mode");
2490 curenv
->adjust_mode
= 5;
2491 warning(WARN_RANGE
, "adjustment mode `%1' out of range", n
);
2494 curenv
->adjust_mode
= n
;
2503 curenv
->adjust_mode
&= ~1;
2507 void do_input_trap(int continued
)
2509 curenv
->input_trap_count
= 0;
2511 curenv
->continued_input_trap
= 1;
2513 if (has_arg() && get_integer(&n
)) {
2516 "number of lines for input trap must be greater than zero");
2518 symbol s
= get_name(1);
2520 curenv
->input_trap_count
= n
;
2521 curenv
->input_trap
= s
;
2533 void input_trap_continued()
2540 // must not be R or C or L or a legitimate part of a number expression
2541 const char TAB_REPEAT_CHAR
= 'T';
2547 tab(hunits
, tab_type
);
2548 enum { BLOCK
= 1024 };
2549 static tab
*free_list
;
2550 void *operator new(size_t);
2551 void operator delete(void *);
2554 tab
*tab::free_list
= 0;
2556 void *tab::operator new(size_t n
)
2558 assert(n
== sizeof(tab
));
2560 free_list
= (tab
*)new char[sizeof(tab
)*BLOCK
];
2561 for (int i
= 0; i
< BLOCK
- 1; i
++)
2562 free_list
[i
].next
= free_list
+ i
+ 1;
2563 free_list
[BLOCK
-1].next
= 0;
2566 free_list
= (tab
*)(free_list
->next
);
2572 /* cfront can't cope with this. */
2575 void tab::operator delete(void *p
)
2578 ((tab
*)p
)->next
= free_list
;
2579 free_list
= (tab
*)p
;
2583 tab::tab(hunits x
, tab_type t
) : next(0), pos(x
), type(t
)
2587 tab_stops::tab_stops(hunits distance
, tab_type type
)
2590 repeated_list
= new tab(distance
, type
);
2593 tab_stops::~tab_stops()
2598 tab_type
tab_stops::distance_to_next_tab(hunits curpos
, hunits
*distance
)
2602 return distance_to_next_tab(curpos
, distance
, &nextpos
);
2605 tab_type
tab_stops::distance_to_next_tab(hunits curpos
, hunits
*distance
,
2610 for (tem
= initial_list
; tem
&& tem
->pos
<= curpos
; tem
= tem
->next
)
2613 *distance
= tem
->pos
- curpos
;
2614 *nextpos
= tem
->pos
;
2617 if (repeated_list
== 0)
2619 hunits base
= lastpos
;
2621 for (tem
= repeated_list
; tem
&& tem
->pos
+ base
<= curpos
; tem
= tem
->next
)
2624 *distance
= tem
->pos
+ base
- curpos
;
2625 *nextpos
= tem
->pos
+ base
;
2628 assert(lastpos
> 0);
2634 const char *tab_stops::to_string()
2636 static char *buf
= 0;
2637 static int buf_size
= 0;
2638 // figure out a maximum on the amount of space we can need
2641 for (p
= initial_list
; p
; p
= p
->next
)
2643 for (p
= repeated_list
; p
; p
= p
->next
)
2645 // (10 for digits + 1 for u + 1 for 'C' or 'R') + 2 for ' &' + 1 for '\0'
2646 int need
= count
*12 + 3;
2647 if (buf
== 0 || need
> buf_size
) {
2651 buf
= new char[buf_size
];
2654 for (p
= initial_list
; p
; p
= p
->next
) {
2655 strcpy(ptr
, i_to_a(p
->pos
.to_units()));
2656 ptr
= strchr(ptr
, '\0');
2674 *ptr
++ = TAB_REPEAT_CHAR
;
2675 for (p
= repeated_list
; p
; p
= p
->next
) {
2676 strcpy(ptr
, i_to_a(p
->pos
.to_units()));
2677 ptr
= strchr(ptr
, '\0');
2698 tab_stops::tab_stops() : initial_list(0), repeated_list(0)
2702 tab_stops::tab_stops(const tab_stops
&ts
)
2703 : initial_list(0), repeated_list(0)
2705 tab
**p
= &initial_list
;
2706 tab
*t
= ts
.initial_list
;
2708 *p
= new tab(t
->pos
, t
->type
);
2713 t
= ts
.repeated_list
;
2715 *p
= new tab(t
->pos
, t
->type
);
2721 void tab_stops::clear()
2723 while (initial_list
) {
2724 tab
*tem
= initial_list
;
2725 initial_list
= initial_list
->next
;
2728 while (repeated_list
) {
2729 tab
*tem
= repeated_list
;
2730 repeated_list
= repeated_list
->next
;
2735 void tab_stops::add_tab(hunits pos
, tab_type type
, int repeated
)
2738 for (p
= repeated
? &repeated_list
: &initial_list
; *p
; p
= &(*p
)->next
)
2740 *p
= new tab(pos
, type
);
2744 void tab_stops::operator=(const tab_stops
&ts
)
2747 tab
**p
= &initial_list
;
2748 tab
*t
= ts
.initial_list
;
2750 *p
= new tab(t
->pos
, t
->type
);
2755 t
= ts
.repeated_list
;
2757 *p
= new tab(t
->pos
, t
->type
);
2766 hunits prev_pos
= 0;
2771 if (tok
.ch() == TAB_REPEAT_CHAR
) {
2776 if (!get_hunits(&pos
, 'm', prev_pos
))
2778 tab_type type
= TAB_LEFT
;
2779 if (tok
.ch() == 'C') {
2783 else if (tok
.ch() == 'R') {
2787 else if (tok
.ch() == 'L') {
2790 if (pos
<= prev_pos
&& !first
)
2792 "positions of tab stops must be strictly increasing");
2794 tabs
.add_tab(pos
, type
, repeated
);
2799 curenv
->tabs
= tabs
;
2800 curdiv
->modified_tag
.incl(MTSM_TA
);
2804 const char *environment::get_tabs()
2806 return tabs
.to_string();
2809 tab_type
environment::distance_to_next_tab(hunits
*distance
)
2812 ? curenv
->tabs
.distance_to_next_tab(get_text_length(), distance
)
2813 : curenv
->tabs
.distance_to_next_tab(get_input_line_position(), distance
);
2816 tab_type
environment::distance_to_next_tab(hunits
*distance
, hunits
*leftpos
)
2819 ? curenv
->tabs
.distance_to_next_tab(get_text_length(), distance
, leftpos
)
2820 : curenv
->tabs
.distance_to_next_tab(get_input_line_position(), distance
,
2824 void field_characters()
2826 field_delimiter_char
= get_optional_char();
2827 if (field_delimiter_char
)
2828 padding_indicator_char
= get_optional_char();
2830 padding_indicator_char
= 0;
2834 void line_tabs_request()
2837 if (has_arg() && get_integer(&n
))
2838 curenv
->line_tabs
= n
!= 0;
2840 curenv
->line_tabs
= 1;
2844 int environment::get_line_tabs()
2849 void environment::wrap_up_tab()
2856 switch (current_tab
) {
2858 tab_amount
= tab_distance
- tab_width
;
2859 line
= make_tab_node(tab_amount
, line
);
2862 tab_amount
= tab_distance
- tab_width
/2;
2863 line
= make_tab_node(tab_amount
, line
);
2870 width_total
+= tab_amount
;
2871 width_total
+= tab_width
;
2872 if (current_field
) {
2873 if (tab_precedes_field
) {
2874 pre_field_width
+= tab_amount
;
2875 tab_precedes_field
= 0;
2877 field_distance
-= tab_amount
;
2878 field_spaces
+= tab_field_spaces
;
2880 if (tab_contents
!= 0) {
2882 for (tem
= tab_contents
; tem
->next
!= 0; tem
= tem
->next
)
2885 line
= tab_contents
;
2887 tab_field_spaces
= 0;
2891 current_tab
= TAB_NONE
;
2894 node
*environment::make_tab_node(hunits d
, node
*next
)
2896 if (leader_node
!= 0 && d
< 0) {
2897 error("motion generated by leader cannot be negative");
2902 return new hmotion_node(d
, 1, 0, get_fill_color(), next
);
2903 node
*n
= new hline_node(d
, leader_node
, next
);
2908 void environment::handle_tab(int is_leader
)
2914 charinfo
*ci
= is_leader
? leader_char
: tab_char
;
2916 leader_node
= ci
? make_char_node(ci
) : 0;
2917 tab_type t
= distance_to_next_tab(&d
, &absolute
);
2922 add_node(make_tag("tab L", absolute
.to_units()));
2923 add_node(make_tab_node(d
));
2926 add_node(make_tag("tab R", absolute
.to_units()));
2929 add_node(make_tag("tab C", absolute
.to_units()));
2938 tab_field_spaces
= 0;
2941 void environment::start_field()
2943 assert(!current_field
);
2945 if (distance_to_next_tab(&d
) != TAB_NONE
) {
2946 pre_field_width
= get_text_length();
2950 tab_field_spaces
= 0;
2951 for (node
*p
= line
; p
; p
= p
->next
)
2956 tab_precedes_field
= current_tab
!= TAB_NONE
;
2959 error("zero field width");
2962 void environment::wrap_up_field()
2964 if (!current_tab
&& field_spaces
== 0)
2966 hunits padding
= field_distance
- (get_text_length() - pre_field_width
);
2967 if (current_tab
&& tab_field_spaces
!= 0) {
2968 hunits tab_padding
= scale(padding
,
2970 field_spaces
+ tab_field_spaces
);
2971 padding
-= tab_padding
;
2972 distribute_space(tab_contents
, tab_field_spaces
, tab_padding
, 1);
2973 tab_field_spaces
= 0;
2974 tab_width
+= tab_padding
;
2976 if (field_spaces
!= 0) {
2977 distribute_space(line
, field_spaces
, padding
, 1);
2978 width_total
+= padding
;
2980 // the start of the tab has been moved to the right by padding, so
2981 tab_distance
-= padding
;
2982 if (tab_distance
<= H0
) {
2983 // use the next tab stop instead
2984 current_tab
= tabs
.distance_to_next_tab(get_input_line_position()
2987 if (current_tab
== TAB_NONE
|| current_tab
== TAB_LEFT
) {
2988 width_total
+= tab_width
;
2989 if (current_tab
== TAB_LEFT
) {
2990 line
= make_tab_node(tab_distance
, line
);
2991 width_total
+= tab_distance
;
2992 current_tab
= TAB_NONE
;
2994 if (tab_contents
!= 0) {
2996 for (tem
= tab_contents
; tem
->next
!= 0; tem
= tem
->next
)
2999 line
= tab_contents
;
3011 void environment::add_padding()
3014 tab_contents
= new space_node(H0
, get_fill_color(), tab_contents
);
3020 line
= new space_node(H0
, get_fill_color(), line
);
3025 typedef int (environment::*INT_FUNCP
)();
3026 typedef vunits (environment::*VUNITS_FUNCP
)();
3027 typedef hunits (environment::*HUNITS_FUNCP
)();
3028 typedef const char *(environment::*STRING_FUNCP
)();
3030 class int_env_reg
: public reg
{
3033 int_env_reg(INT_FUNCP
);
3034 const char *get_string();
3035 int get_value(units
*val
);
3038 class vunits_env_reg
: public reg
{
3041 vunits_env_reg(VUNITS_FUNCP f
);
3042 const char *get_string();
3043 int get_value(units
*val
);
3047 class hunits_env_reg
: public reg
{
3050 hunits_env_reg(HUNITS_FUNCP f
);
3051 const char *get_string();
3052 int get_value(units
*val
);
3055 class string_env_reg
: public reg
{
3058 string_env_reg(STRING_FUNCP
);
3059 const char *get_string();
3062 int_env_reg::int_env_reg(INT_FUNCP f
) : func(f
)
3066 int int_env_reg::get_value(units
*val
)
3068 *val
= (curenv
->*func
)();
3072 const char *int_env_reg::get_string()
3074 return i_to_a((curenv
->*func
)());
3077 vunits_env_reg::vunits_env_reg(VUNITS_FUNCP f
) : func(f
)
3081 int vunits_env_reg::get_value(units
*val
)
3083 *val
= (curenv
->*func
)().to_units();
3087 const char *vunits_env_reg::get_string()
3089 return i_to_a((curenv
->*func
)().to_units());
3092 hunits_env_reg::hunits_env_reg(HUNITS_FUNCP f
) : func(f
)
3096 int hunits_env_reg::get_value(units
*val
)
3098 *val
= (curenv
->*func
)().to_units();
3102 const char *hunits_env_reg::get_string()
3104 return i_to_a((curenv
->*func
)().to_units());
3107 string_env_reg::string_env_reg(STRING_FUNCP f
) : func(f
)
3111 const char *string_env_reg::get_string()
3113 return (curenv
->*func
)();
3116 class horizontal_place_reg
: public general_reg
{
3118 horizontal_place_reg();
3119 int get_value(units
*);
3120 void set_value(units
);
3123 horizontal_place_reg::horizontal_place_reg()
3127 int horizontal_place_reg::get_value(units
*res
)
3129 *res
= curenv
->get_input_line_position().to_units();
3133 void horizontal_place_reg::set_value(units n
)
3135 curenv
->set_input_line_position(hunits(n
));
3138 int environment::get_zoom()
3140 return env_get_zoom(this);
3143 const char *environment::get_font_family_string()
3145 return family
->nm
.contents();
3148 const char *environment::get_glyph_color_string()
3150 return glyph_color
->nm
.contents();
3153 const char *environment::get_fill_color_string()
3155 return fill_color
->nm
.contents();
3158 const char *environment::get_font_name_string()
3160 symbol f
= get_font_name(fontno
, this);
3161 return f
.contents();
3164 const char *environment::get_style_name_string()
3166 symbol f
= get_style_name(fontno
);
3167 return f
.contents();
3170 const char *environment::get_name_string()
3172 return name
.contents();
3175 // Convert a quantity in scaled points to ascii decimal fraction.
3177 const char *sptoa(int sp
)
3180 assert(sizescale
> 0);
3183 if (sp
% sizescale
== 0)
3184 return i_to_a(sp
/sizescale
);
3185 // See if 1/sizescale is exactly representable as a decimal fraction,
3186 // ie its only prime factors are 2 and 5.
3189 while ((n
& 1) == 0) {
3194 while ((n
% 5) == 0) {
3199 int decimal_point
= power5
> power2
? power5
: power2
;
3200 if (decimal_point
<= 10) {
3203 for (t
= decimal_point
- power2
; --t
>= 0;)
3205 for (t
= decimal_point
- power5
; --t
>= 0;)
3207 if (factor
== 1 || sp
<= INT_MAX
/factor
)
3208 return if_to_a(sp
*factor
, decimal_point
);
3211 double s
= double(sp
)/double(sizescale
);
3212 double factor
= 10.0;
3214 int decimal_point
= 0;
3216 double v
= ceil(s
*factor
);
3221 } while (++decimal_point
< 10);
3222 return if_to_a(int(val
), decimal_point
);
3225 const char *environment::get_point_size_string()
3227 return sptoa(curenv
->get_point_size());
3230 const char *environment::get_requested_point_size_string()
3232 return sptoa(curenv
->get_requested_point_size());
3235 void environment::print_env()
3237 // at the time of calling .pev, those values are always zero or
3240 // char_height, char_slant,
3242 // current_tab, tab_width, tab_distance
3243 // current_field, field_distance, pre_field_width, field_spaces,
3244 // tab_field_spaces, tab_precedes_field
3247 errprint(" previous line length: %1u\n", prev_line_length
.to_units());
3248 errprint(" line length: %1u\n", line_length
.to_units());
3249 errprint(" previous title length: %1u\n", prev_title_length
.to_units());
3250 errprint(" title length: %1u\n", title_length
.to_units());
3251 errprint(" previous size: %1p (%2s)\n",
3252 prev_size
.to_points(), prev_size
.to_scaled_points());
3253 errprint(" size: %1p (%2s)\n",
3254 size
.to_points(), size
.to_scaled_points());
3255 errprint(" previous requested size: %1s\n", prev_requested_size
);
3256 errprint(" requested size: %1s\n", requested_size
);
3257 errprint(" previous font number: %1\n", prev_fontno
);
3258 errprint(" font number: %1\n", fontno
);
3259 errprint(" previous family: `%1'\n", prev_family
->nm
.contents());
3260 errprint(" family: `%1'\n", family
->nm
.contents());
3261 errprint(" space size: %1/36 em\n", space_size
);
3262 errprint(" sentence space size: %1/36 em\n", sentence_space_size
);
3263 errprint(" previous line interrupted: %1\n",
3264 prev_line_interrupted
? "yes" : "no");
3265 errprint(" fill mode: %1\n", fill
? "on" : "off");
3266 errprint(" adjust mode: %1\n",
3267 adjust_mode
== ADJUST_LEFT
3269 : adjust_mode
== ADJUST_BOTH
3271 : adjust_mode
== ADJUST_CENTER
3275 errprint(" lines to center: %1\n", center_lines
);
3276 if (right_justify_lines
)
3277 errprint(" lines to right justify: %1\n", right_justify_lines
);
3278 errprint(" previous vertical spacing: %1u\n",
3279 prev_vertical_spacing
.to_units());
3280 errprint(" vertical spacing: %1u\n", vertical_spacing
.to_units());
3281 errprint(" previous post-vertical spacing: %1u\n",
3282 prev_post_vertical_spacing
.to_units());
3283 errprint(" post-vertical spacing: %1u\n",
3284 post_vertical_spacing
.to_units());
3285 errprint(" previous line spacing: %1\n", prev_line_spacing
);
3286 errprint(" line spacing: %1\n", line_spacing
);
3287 errprint(" previous indentation: %1u\n", prev_indent
.to_units());
3288 errprint(" indentation: %1u\n", indent
.to_units());
3289 errprint(" temporary indentation: %1u\n", temporary_indent
.to_units());
3290 errprint(" have temporary indentation: %1\n",
3291 have_temporary_indent
? "yes" : "no");
3292 errprint(" currently used indentation: %1u\n", saved_indent
.to_units());
3293 errprint(" target text length: %1u\n", target_text_length
.to_units());
3294 if (underline_lines
) {
3295 errprint(" lines to underline: %1\n", underline_lines
);
3296 errprint(" font number before underlining: %1\n", pre_underline_fontno
);
3297 errprint(" underline spaces: %1\n", underline_spaces
? "yes" : "no");
3299 if (input_trap
.contents()) {
3300 errprint(" input trap macro: `%1'\n", input_trap
.contents());
3301 errprint(" input trap line counter: %1\n", input_trap_count
);
3302 errprint(" continued input trap: %1\n",
3303 continued_input_trap
? "yes" : "no");
3305 errprint(" previous text length: %1u\n", prev_text_length
.to_units());
3306 errprint(" total width: %1u\n", width_total
.to_units());
3307 errprint(" total number of spaces: %1\n", space_total
);
3308 errprint(" input line start: %1u\n", input_line_start
.to_units());
3309 errprint(" line tabs: %1\n", line_tabs
? "yes" : "no");
3310 errprint(" discarding: %1\n", discarding
? "yes" : "no");
3311 errprint(" spread flag set: %1\n", spread_flag
? "yes" : "no"); // \p
3312 if (margin_character_node
) {
3313 errprint(" margin character flags: %1\n",
3314 margin_character_flags
== MARGIN_CHARACTER_ON
3316 : margin_character_flags
== MARGIN_CHARACTER_NEXT
3318 : margin_character_flags
== MARGIN_CHARACTER_ON
3319 | MARGIN_CHARACTER_NEXT
3322 errprint(" margin character distance: %1u\n",
3323 margin_character_distance
.to_units());
3325 if (numbering_nodes
) {
3326 errprint(" line number digit width: %1u\n",
3327 line_number_digit_width
.to_units());
3328 errprint(" separation between number and text: %1 digit spaces\n",
3329 number_text_separation
);
3330 errprint(" line number indentation: %1 digit spaces\n",
3331 line_number_indent
);
3332 errprint(" print line numbers every %1line%1\n",
3333 line_number_multiple
> 1 ? i_to_a(line_number_multiple
) : "",
3334 line_number_multiple
> 1 ? "s" : "");
3335 errprint(" lines not to enumerate: %1\n", no_number_count
);
3337 string hf
= hyphenation_flags
? "on" : "off";
3338 if (hyphenation_flags
& HYPHEN_LAST_LINE
)
3339 hf
+= ", not last line";
3340 if (hyphenation_flags
& HYPHEN_LAST_CHARS
)
3341 hf
+= ", not last two chars";
3342 if (hyphenation_flags
& HYPHEN_FIRST_CHARS
)
3343 hf
+= ", not first two chars";
3345 errprint(" hyphenation_flags: %1\n", hf
.contents());
3346 errprint(" number of consecutive hyphenated lines: %1\n",
3348 errprint(" maximum number of consecutive hyphenated lines: %1\n",
3350 errprint(" hyphenation space: %1u\n", hyphenation_space
.to_units());
3351 errprint(" hyphenation margin: %1u\n", hyphenation_margin
.to_units());
3352 #ifdef WIDOW_CONTROL
3353 errprint(" widow control: %1\n", widow_control
? "yes" : "no");
3354 #endif /* WIDOW_CONTROL */
3359 errprint("Current Environment:\n");
3360 curenv
->print_env();
3361 for (int i
= 0; i
< NENVIRONMENTS
; i
++) {
3363 errprint("Environment %1:\n", i
);
3364 if (env_table
[i
] != curenv
)
3365 env_table
[i
]->print_env();
3367 errprint(" current\n");
3370 dictionary_iterator
iter(env_dictionary
);
3373 while (iter
.get(&s
, (void **)&e
)) {
3374 assert(!s
.is_null());
3375 errprint("Environment %1:\n", s
.contents());
3379 errprint(" current\n");
3385 #define init_int_env_reg(name, func) \
3386 number_reg_dictionary.define(name, new int_env_reg(&environment::func))
3388 #define init_vunits_env_reg(name, func) \
3389 number_reg_dictionary.define(name, new vunits_env_reg(&environment::func))
3391 #define init_hunits_env_reg(name, func) \
3392 number_reg_dictionary.define(name, new hunits_env_reg(&environment::func))
3394 #define init_string_env_reg(name, func) \
3395 number_reg_dictionary.define(name, new string_env_reg(&environment::func))
3397 void init_env_requests()
3399 init_request("ad", adjust
);
3400 init_request("br", break_request
);
3401 init_request("brp", break_spread_request
);
3402 init_request("c2", no_break_control_char
);
3403 init_request("cc", control_char
);
3404 init_request("ce", center
);
3405 init_request("cu", continuous_underline
);
3406 init_request("ev", environment_switch
);
3407 init_request("evc", environment_copy
);
3408 init_request("fam", family_change
);
3409 init_request("fc", field_characters
);
3410 init_request("fi", fill
);
3411 init_request("fcolor", fill_color_change
);
3412 init_request("ft", font_change
);
3413 init_request("gcolor", glyph_color_change
);
3414 init_request("hc", hyphen_char
);
3415 init_request("hlm", hyphen_line_max_request
);
3416 init_request("hy", hyphenate_request
);
3417 init_request("hym", hyphenation_margin_request
);
3418 init_request("hys", hyphenation_space_request
);
3419 init_request("in", indent
);
3420 init_request("it", input_trap
);
3421 init_request("itc", input_trap_continued
);
3422 init_request("lc", leader_character
);
3423 init_request("linetabs", line_tabs_request
);
3424 init_request("ll", line_length
);
3425 init_request("ls", line_spacing
);
3426 init_request("lt", title_length
);
3427 init_request("mc", margin_character
);
3428 init_request("na", no_adjust
);
3429 init_request("nf", no_fill
);
3430 init_request("nh", no_hyphenate
);
3431 init_request("nm", number_lines
);
3432 init_request("nn", no_number
);
3433 init_request("pev", print_env
);
3434 init_request("ps", point_size
);
3435 init_request("pvs", post_vertical_spacing
);
3436 init_request("rj", right_justify
);
3437 init_request("sizes", override_sizes
);
3438 init_request("ss", space_size
);
3439 init_request("ta", set_tabs
);
3440 init_request("ti", temporary_indent
);
3441 init_request("tc", tab_character
);
3442 init_request("tl", title
);
3443 init_request("ul", underline
);
3444 init_request("vs", vertical_spacing
);
3445 #ifdef WIDOW_CONTROL
3446 init_request("wdc", widow_control_request
);
3447 #endif /* WIDOW_CONTROL */
3448 init_int_env_reg(".b", get_bold
);
3449 init_vunits_env_reg(".cdp", get_prev_char_depth
);
3450 init_int_env_reg(".ce", get_center_lines
);
3451 init_vunits_env_reg(".cht", get_prev_char_height
);
3452 init_hunits_env_reg(".csk", get_prev_char_skew
);
3453 init_string_env_reg(".ev", get_name_string
);
3454 init_int_env_reg(".f", get_font
);
3455 init_string_env_reg(".fam", get_font_family_string
);
3456 init_string_env_reg(".fn", get_font_name_string
);
3457 init_int_env_reg(".height", get_char_height
);
3458 init_int_env_reg(".hlc", get_hyphen_line_count
);
3459 init_int_env_reg(".hlm", get_hyphen_line_max
);
3460 init_int_env_reg(".hy", get_hyphenation_flags
);
3461 init_hunits_env_reg(".hym", get_hyphenation_margin
);
3462 init_hunits_env_reg(".hys", get_hyphenation_space
);
3463 init_hunits_env_reg(".i", get_indent
);
3464 init_hunits_env_reg(".in", get_saved_indent
);
3465 init_int_env_reg(".int", get_prev_line_interrupted
);
3466 init_int_env_reg(".linetabs", get_line_tabs
);
3467 init_hunits_env_reg(".lt", get_title_length
);
3468 init_int_env_reg(".j", get_adjust_mode
);
3469 init_hunits_env_reg(".k", get_text_length
);
3470 init_int_env_reg(".L", get_line_spacing
);
3471 init_hunits_env_reg(".l", get_line_length
);
3472 init_hunits_env_reg(".ll", get_saved_line_length
);
3473 init_string_env_reg(".M", get_fill_color_string
);
3474 init_string_env_reg(".m", get_glyph_color_string
);
3475 init_hunits_env_reg(".n", get_prev_text_length
);
3476 init_int_env_reg(".ps", get_point_size
);
3477 init_int_env_reg(".psr", get_requested_point_size
);
3478 init_vunits_env_reg(".pvs", get_post_vertical_spacing
);
3479 init_int_env_reg(".rj", get_right_justify_lines
);
3480 init_string_env_reg(".s", get_point_size_string
);
3481 init_int_env_reg(".slant", get_char_slant
);
3482 init_int_env_reg(".ss", get_space_size
);
3483 init_int_env_reg(".sss", get_sentence_space_size
);
3484 init_string_env_reg(".sr", get_requested_point_size_string
);
3485 init_string_env_reg(".sty", get_style_name_string
);
3486 init_string_env_reg(".tabs", get_tabs
);
3487 init_int_env_reg(".u", get_fill
);
3488 init_vunits_env_reg(".v", get_vertical_spacing
);
3489 init_hunits_env_reg(".w", get_prev_char_width
);
3490 init_int_env_reg(".zoom", get_zoom
);
3491 number_reg_dictionary
.define("ct", new variable_reg(&ct_reg_contents
));
3492 number_reg_dictionary
.define("hp", new horizontal_place_reg
);
3493 number_reg_dictionary
.define("ln", new variable_reg(&next_line_number
));
3494 number_reg_dictionary
.define("rsb", new variable_reg(&rsb_reg_contents
));
3495 number_reg_dictionary
.define("rst", new variable_reg(&rst_reg_contents
));
3496 number_reg_dictionary
.define("sb", new variable_reg(&sb_reg_contents
));
3497 number_reg_dictionary
.define("skw", new variable_reg(&skw_reg_contents
));
3498 number_reg_dictionary
.define("ssc", new variable_reg(&ssc_reg_contents
));
3499 number_reg_dictionary
.define("st", new variable_reg(&st_reg_contents
));
3502 // Hyphenation - TeX's hyphenation algorithm with a less fancy implementation.
3508 virtual void do_match(int len
, void *val
) = 0;
3509 virtual void do_delete(void *) = 0;
3510 void delete_trie_node(trie_node
*);
3513 virtual ~trie(); // virtual to shut up g++
3514 void insert(const char *, int, void *);
3515 // find calls do_match for each match it finds
3516 void find(const char *pat
, int patlen
);
3520 class hyphen_trie
: private trie
{
3522 void do_match(int i
, void *v
);
3523 void do_delete(void *v
);
3524 void insert_pattern(const char *pat
, int patlen
, int *num
);
3525 void insert_hyphenation(dictionary
*ex
, const char *pat
, int patlen
);
3526 int hpf_getc(file_case
*fcp
);
3530 void hyphenate(const char *word
, int len
, int *hyphens
);
3531 void read_patterns_file(const char *name
, int append
, dictionary
*ex
);
3534 struct hyphenation_language
{
3536 dictionary exceptions
;
3537 hyphen_trie patterns
;
3538 hyphenation_language(symbol nm
) : name(nm
), exceptions(501) {}
3539 ~hyphenation_language() { }
3542 dictionary
language_dictionary(5);
3543 hyphenation_language
*current_language
= 0;
3545 static void set_hyphenation_language()
3547 symbol nm
= get_name(1);
3548 if (!nm
.is_null()) {
3549 current_language
= (hyphenation_language
*)language_dictionary
.lookup(nm
);
3550 if (!current_language
) {
3551 current_language
= new hyphenation_language(nm
);
3552 (void)language_dictionary
.lookup(nm
, (void *)current_language
);
3558 const int WORD_MAX
= 256; // we use unsigned char for offsets in
3559 // hyphenation exceptions
3561 static void hyphen_word()
3563 if (!current_language
) {
3564 error("no current hyphenation language");
3568 char buf
[WORD_MAX
+ 1];
3569 unsigned char pos
[WORD_MAX
+ 2];
3572 if (tok
.newline() || tok
.eof())
3576 while (i
< WORD_MAX
&& !tok
.space() && !tok
.newline() && !tok
.eof()) {
3577 charinfo
*ci
= tok
.get_char(1);
3583 if (ci
->get_ascii_code() == '-') {
3584 if (i
> 0 && (npos
== 0 || pos
[npos
- 1] != i
))
3588 unsigned char c
= ci
->get_hyphenation_code();
3597 unsigned char *tem
= new unsigned char[npos
+ 1];
3598 memcpy(tem
, pos
, npos
+ 1);
3599 tem
= (unsigned char *)current_language
->exceptions
.lookup(symbol(buf
),
3613 trie_node(char, trie_node
*);
3616 trie_node::trie_node(char ch
, trie_node
*p
)
3617 : c(ch
), down(0), right(p
), val(0)
3628 delete_trie_node(tp
);
3633 void trie::delete_trie_node(trie_node
*p
)
3636 delete_trie_node(p
->down
);
3637 delete_trie_node(p
->right
);
3644 void trie::insert(const char *pat
, int patlen
, void *val
)
3646 trie_node
**p
= &tp
;
3647 assert(patlen
> 0 && pat
!= 0);
3649 while (*p
!= 0 && (*p
)->c
< pat
[0])
3651 if (*p
== 0 || (*p
)->c
!= pat
[0])
3652 *p
= new trie_node(pat
[0], *p
);
3653 if (--patlen
== 0) {
3662 void trie::find(const char *pat
, int patlen
)
3665 for (int i
= 0; p
!= 0 && i
< patlen
; i
++) {
3666 while (p
!= 0 && p
->c
< pat
[i
])
3668 if (p
!= 0 && p
->c
== pat
[i
]) {
3670 do_match(i
+1, p
->val
);
3682 operation(int, int, operation
*);
3685 operation::operation(int i
, int j
, operation
*op
)
3686 : next(op
), distance(j
), num(i
)
3690 void hyphen_trie::insert_pattern(const char *pat
, int patlen
, int *num
)
3693 for (int i
= 0; i
< patlen
+1; i
++)
3695 op
= new operation(num
[i
], patlen
- i
, op
);
3696 insert(pat
, patlen
, op
);
3699 void hyphen_trie::insert_hyphenation(dictionary
*ex
, const char *pat
,
3702 char buf
[WORD_MAX
+ 1];
3703 unsigned char pos
[WORD_MAX
+ 2];
3706 while (j
< patlen
) {
3707 unsigned char c
= pat
[j
++];
3709 if (i
> 0 && (npos
== 0 || pos
[npos
- 1] != i
))
3713 buf
[i
++] = hpf_code_table
[c
];
3718 unsigned char *tem
= new unsigned char[npos
+ 1];
3719 memcpy(tem
, pos
, npos
+ 1);
3720 tem
= (unsigned char *)ex
->lookup(symbol(buf
), tem
);
3726 void hyphen_trie::hyphenate(const char *word
, int len
, int *hyphens
)
3729 for (j
= 0; j
< len
+ 1; j
++)
3731 for (j
= 0; j
< len
- 1; j
++) {
3733 find(word
+ j
, len
- j
);
3737 inline int max(int m
, int n
)
3739 return m
> n
? m
: n
;
3742 void hyphen_trie::do_match(int i
, void *v
)
3744 operation
*op
= (operation
*)v
;
3746 h
[i
- op
->distance
] = max(h
[i
- op
->distance
], op
->num
);
3751 void hyphen_trie::do_delete(void *v
)
3753 operation
*op
= (operation
*)v
;
3755 operation
*tem
= op
;
3761 /* We use very simple rules to parse TeX's hyphenation patterns.
3763 . `%' starts a comment even if preceded by `\'.
3765 . No support for digraphs and like `\$'.
3767 . `^^xx' (`x' is 0-9 or a-f), and `^^x' (character code of `x' in the
3768 range 0-127) are recognized; other use of `^' causes an error.
3770 . No macro expansion.
3772 . We check for the expression `\patterns{...}' (possibly with
3773 whitespace before and after the braces). Everything between the
3774 braces is taken as hyphenation patterns. Consequently, `{' and `}'
3775 are not allowed in patterns.
3777 . Similarly, `\hyphenation{...}' gives a list of hyphenation
3780 . `\endinput' is recognized also.
3782 . For backwards compatibility, if `\patterns' is missing, the
3783 whole file is treated as a list of hyphenation patterns (only
3784 recognizing `%' as the start of a comment.
3788 int hyphen_trie::hpf_getc(file_case
*fcp
)
3790 int c
= fcp
->get_c();
3800 if (((c
>= '0' && c
<= '9') || (c
>= 'a' && c
<= 'f'))
3801 && ((c1
>= '0' && c1
<= '9') || (c1
>= 'a' && c1
<= 'f'))) {
3802 if (c
>= '0' && c
<= '9')
3806 if (c1
>= '0' && c1
<= '9')
3814 if (c
>= 0 && c
<= 63)
3816 else if (c
>= 64 && c
<= 127)
3823 error("invalid ^, ^^x, or ^^xx character in hyphenation patterns file");
3827 void hyphen_trie::read_patterns_file(const char *name
, int append
,
3834 for (int i
= 0; i
< WORD_MAX
; i
++)
3836 int num
[WORD_MAX
+1];
3837 int have_patterns
= 0; // we've seen \patterns
3838 int final_pattern
= 0; // 1 if we have a trailing closing brace
3839 int have_hyphenation
= 0; // we've seen \hyphenation
3840 int final_hyphenation
= 0; // 1 if we have a trailing closing brace
3841 int have_keyword
= 0; // we've seen either \patterns or \hyphenation
3842 int traditional
= 0; // don't handle \patterns
3845 if ((fcp
= mac_path
->open_file(name
, fcp
->fc_const_path
)) == NULL
) {
3846 error("can't find hyphenation patterns file `%1'", name
);
3850 for (c
= hpf_getc(fcp
);;) {
3852 if (c
== '%') { // skip comments
3855 } while (c
!= EOF
&& c
!= '\n');
3857 if (c
== EOF
|| !csspace(c
))
3862 if (have_keyword
|| traditional
) // we are done
3864 else { // rescan file in `traditional' mode
3873 if (!(c
== '{' || c
== '}')) { // skip braces at line start
3874 do { // scan patterns
3882 } while (i
< WORD_MAX
&& c
!= EOF
&& !csspace(c
)
3883 && c
!= '%' && c
!= '{' && c
!= '}');
3886 if (i
>= 9 && !strncmp(buf
+ i
- 9, "\\patterns", 9)) {
3890 if (have_patterns
|| have_hyphenation
)
3891 error("\\patterns not allowed inside of %1 group",
3892 have_patterns
? "\\patterns" : "\\hyphenation");
3901 else if (i
>= 12 && !strncmp(buf
+ i
- 12, "\\hyphenation", 12)) {
3905 if (have_patterns
|| have_hyphenation
)
3906 error("\\hyphenation not allowed inside of %1 group",
3907 have_patterns
? "\\patterns" : "\\hyphenation");
3909 have_hyphenation
= 1;
3916 else if (strstr(buf
, "\\endinput")) {
3917 if (have_patterns
|| have_hyphenation
)
3918 error("found \\endinput inside of %1 group",
3919 have_patterns
? "\\patterns" : "\\hyphenation");
3922 else if (c
== '}') {
3923 if (have_patterns
) {
3928 else if (have_hyphenation
) {
3929 have_hyphenation
= 0;
3931 final_hyphenation
= 1;
3935 else if (c
== '{') {
3936 if (have_patterns
|| have_hyphenation
)
3937 error("`{' not allowed within %1 group",
3938 have_patterns
? "\\patterns" : "\\hyphenation");
3939 c
= hpf_getc(fcp
); // skipped if not starting \patterns
3944 if (c
== '{' || c
== '}')
3948 if (have_patterns
|| final_pattern
|| traditional
) {
3949 for (int j
= 0; j
< i
; j
++)
3950 buf
[j
] = hpf_code_table
[(unsigned char)buf
[j
]];
3951 insert_pattern(buf
, i
, num
);
3954 else if (have_hyphenation
|| final_hyphenation
) {
3955 insert_hyphenation(ex
, buf
, i
);
3956 final_hyphenation
= 0;
3966 void hyphenate(hyphen_list
*h
, unsigned flags
)
3968 if (!current_language
)
3971 while (h
&& h
->hyphenation_code
== 0)
3974 char hbuf
[WORD_MAX
+ 2];
3975 char *buf
= hbuf
+ 1;
3977 for (tem
= h
; tem
&& len
< WORD_MAX
; tem
= tem
->next
) {
3978 if (tem
->hyphenation_code
!= 0)
3979 buf
[len
++] = tem
->hyphenation_code
;
3983 hyphen_list
*nexth
= tem
;
3987 = (unsigned char *)current_language
->exceptions
.lookup(buf
);
3991 for (tem
= h
; tem
!= 0; tem
= tem
->next
, i
++)
3998 hbuf
[0] = hbuf
[len
+ 1] = '.';
3999 int num
[WORD_MAX
+ 3];
4000 current_language
->patterns
.hyphenate(hbuf
, len
+ 2, num
);
4003 if (flags
& HYPHEN_FIRST_CHARS
)
4005 if (flags
& HYPHEN_LAST_CHARS
)
4007 for (i
= 2, tem
= h
; i
< len
&& tem
; tem
= tem
->next
, i
++)
4016 static void do_hyphenation_patterns_file(int append
)
4018 symbol name
= get_long_name(1);
4019 if (!name
.is_null()) {
4020 if (!current_language
)
4021 error("no current hyphenation language");
4023 current_language
->patterns
.read_patterns_file(
4024 name
.contents(), append
,
4025 ¤t_language
->exceptions
);
4030 static void hyphenation_patterns_file()
4032 do_hyphenation_patterns_file(0);
4035 static void hyphenation_patterns_file_append()
4037 do_hyphenation_patterns_file(1);
4040 class hyphenation_language_reg
: public reg
{
4042 const char *get_string();
4045 const char *hyphenation_language_reg::get_string()
4047 return current_language
? current_language
->name
.contents() : "";
4050 void init_hyphen_requests()
4052 init_request("hw", hyphen_word
);
4053 init_request("hla", set_hyphenation_language
);
4054 init_request("hpf", hyphenation_patterns_file
);
4055 init_request("hpfa", hyphenation_patterns_file_append
);
4056 number_reg_dictionary
.define(".hla", new hyphenation_language_reg
);