2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
7 groff 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 groff 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, 675 Mass Ave, Cambridge, MA 02139, USA. */
23 #include "dictionary.h"
34 symbol
default_family("T");
36 enum { ADJUST_LEFT
= 0, ADJUST_BOTH
= 1, ADJUST_CENTER
= 3, ADJUST_RIGHT
= 5 };
38 enum { HYPHEN_LAST_LINE
= 2, HYPHEN_LAST_CHARS
= 4, HYPHEN_FIRST_CHARS
= 8 };
43 env_list(environment
*e
, env_list
*p
) : env(e
), next(p
) {}
47 const int NENVIRONMENTS
= 10;
48 environment
*env_table
[NENVIRONMENTS
];
49 dictionary
env_dictionary(10);
51 static int next_line_number
= 0;
53 charinfo
*field_delimiter_char
;
54 charinfo
*padding_indicator_char
;
56 int translate_space_to_dummy
= 0;
58 class pending_output_line
{
65 int last_line
; // Is it the last line of the paragraph?
66 #endif /* WIDOW_CONTROL */
68 pending_output_line
*next
;
70 pending_output_line(node
*, int, vunits
, int, hunits
,
71 pending_output_line
* = 0);
72 ~pending_output_line();
76 friend void environment::mark_last_line();
77 friend void environment::output(node
*, int, vunits
, int, hunits
);
78 #endif /* WIDOW_CONTROL */
81 pending_output_line::pending_output_line(node
*n
, int nf
, vunits v
, int l
,
82 hunits w
, pending_output_line
*p
)
83 : nd(n
), no_fill(nf
), vs(v
), ls(l
), width(w
),
86 #endif /* WIDOW_CONTROL */
91 pending_output_line::~pending_output_line()
96 int pending_output_line::output()
101 if (next
&& next
->last_line
&& !no_fill
) {
102 curdiv
->need(vs
*ls
+ vunits(vresolution
));
103 if (trap_sprung_flag
) {
104 next
->last_line
= 0; // Try to avoid infinite loops.
109 curdiv
->output(nd
, no_fill
, vs
, ls
, width
);
114 void environment::output(node
*nd
, int no_fill
, vunits vs
, int ls
,
118 while (pending_lines
) {
119 if (widow_control
&& !pending_lines
->no_fill
&& !pending_lines
->next
)
121 if (!pending_lines
->output())
123 pending_output_line
*tem
= pending_lines
;
124 pending_lines
= pending_lines
->next
;
127 #else /* WIDOW_CONTROL */
128 output_pending_lines();
129 #endif /* WIDOW_CONTROL */
130 if (!trap_sprung_flag
&& !pending_lines
132 && (!widow_control
|| no_fill
)
133 #endif /* WIDOW_CONTROL */
135 curdiv
->output(nd
, no_fill
, vs
, ls
, width
);
137 for (pending_output_line
**p
= &pending_lines
; *p
; p
= &(*p
)->next
)
139 *p
= new pending_output_line(nd
, no_fill
, vs
, ls
, width
);
143 // a line from .tl goes at the head of the queue
145 void environment::output_title(node
*nd
, int no_fill
, vunits vs
, int ls
,
148 if (!trap_sprung_flag
)
149 curdiv
->output(nd
, no_fill
, vs
, ls
, width
);
151 pending_lines
= new pending_output_line(nd
, no_fill
, vs
, ls
, width
,
155 void environment::output_pending_lines()
157 while (pending_lines
&& pending_lines
->output()) {
158 pending_output_line
*tem
= pending_lines
;
159 pending_lines
= pending_lines
->next
;
166 void environment::mark_last_line()
168 if (!widow_control
|| !pending_lines
)
170 for (pending_output_line
*p
= pending_lines
; p
->next
; p
= p
->next
)
176 void widow_control_request()
181 curenv
->widow_control
= n
!= 0;
184 curenv
->widow_control
= 1;
188 #endif /* WIDOW_CONTROL */
190 /* font_size functions */
192 size_range
*font_size::size_table
= 0;
193 int font_size::nranges
= 0;
197 static int compare_ranges(const void *p1
, const void *p2
)
199 return ((size_range
*)p1
)->min
- ((size_range
*)p2
)->min
;
204 void font_size::init_size_table(int *sizes
)
207 while (sizes
[nranges
*2] != 0)
210 size_table
= new size_range
[nranges
];
211 for (int i
= 0; i
< nranges
; i
++) {
212 size_table
[i
].min
= sizes
[i
*2];
213 size_table
[i
].max
= sizes
[i
*2 + 1];
215 qsort(size_table
, nranges
, sizeof(size_range
), compare_ranges
);
218 font_size::font_size(int sp
)
220 for (int i
= 0; i
< nranges
; i
++) {
221 if (sp
< size_table
[i
].min
) {
222 if (i
> 0 && size_table
[i
].min
- sp
>= sp
- size_table
[i
- 1].max
)
223 p
= size_table
[i
- 1].max
;
225 p
= size_table
[i
].min
;
228 if (sp
<= size_table
[i
].max
) {
233 p
= size_table
[nranges
- 1].max
;
236 int font_size::to_units()
238 return scale(p
, units_per_inch
, sizescale
*72);
241 // we can't do this in a static constructor because various dictionaries
242 // have to get initialized first
244 void init_environments()
246 curenv
= env_table
[0] = new environment("0");
251 curenv
->tab_char
= get_optional_char();
255 void leader_character()
257 curenv
->leader_char
= get_optional_char();
261 void environment::add_char(charinfo
*ci
)
265 // don't allow fields in dummy environments
266 else if (ci
== field_delimiter_char
&& !dummy
) {
272 else if (current_field
&& ci
== padding_indicator_char
)
274 else if (current_tab
) {
275 if (tab_contents
== 0)
276 tab_contents
= new line_start_node
;
277 if (ci
!= hyphen_indicator_char
)
278 tab_contents
= tab_contents
->add_char(ci
, this, &tab_width
);
280 tab_contents
= tab_contents
->add_discretionary_hyphen();
285 if (ci
!= hyphen_indicator_char
)
286 line
= line
->add_char(ci
, this, &width_total
);
288 line
= line
->add_discretionary_hyphen();
292 node
*environment::make_char_node(charinfo
*ci
)
294 return make_node(ci
, this);
297 void environment::add_node(node
*n
)
300 if (current_tab
|| current_field
)
305 else if (current_tab
) {
306 n
->next
= tab_contents
;
308 tab_width
+= n
->width();
312 if (discarding
&& n
->discardable()) {
313 // XXX possibly: input_line_start -= n->width();
319 width_total
+= n
->width();
320 space_total
+= n
->nspaces();
327 void environment::add_hyphen_indicator()
329 if (current_tab
|| interrupted
|| current_field
330 || hyphen_indicator_char
!= 0)
334 line
= line
->add_discretionary_hyphen();
337 int environment::get_hyphenation_flags()
339 return hyphenation_flags
;
342 int environment::get_hyphen_line_max()
344 return hyphen_line_max
;
347 int environment::get_hyphen_line_count()
349 return hyphen_line_count
;
352 int environment::get_center_lines()
357 int environment::get_right_justify_lines()
359 return right_justify_lines
;
362 void environment::add_italic_correction()
366 tab_contents
= tab_contents
->add_italic_correction(&tab_width
);
369 line
= line
->add_italic_correction(&width_total
);
372 void environment::space_newline()
374 assert(!current_tab
&& !current_field
);
378 if (!translate_space_to_dummy
) {
379 x
= env_space_width(this);
380 if (node_list_ends_sentence(line
) == 1)
381 x
+= env_sentence_space_width(this);
383 if (line
!= 0 && line
->merge_space(x
)) {
387 add_node(new word_space_node(x
));
388 possibly_break_line(spread_flag
);
392 void environment::space()
396 if (current_field
&& padding_indicator_char
== 0) {
400 hunits x
= translate_space_to_dummy
? H0
: env_space_width(this);
401 node
*p
= current_tab
? tab_contents
: line
;
402 hunits
*tp
= current_tab
? &tab_width
: &width_total
;
403 if (p
&& p
->nspaces() == 1 && p
->width() == x
404 && node_list_ends_sentence(p
->next
) == 1) {
405 hunits xx
= translate_space_to_dummy
? H0
: env_sentence_space_width(this);
406 if (p
->merge_space(xx
)) {
411 if (p
&& p
->merge_space(x
)) {
415 add_node(new word_space_node(x
));
416 possibly_break_line(spread_flag
);
420 void environment::set_font(symbol nm
)
424 if (nm
== symbol("P")) {
425 if (family
->make_definite(prev_fontno
) < 0)
428 fontno
= prev_fontno
;
432 int n
= symbol_fontno(nm
);
434 n
= next_available_font_position();
435 if (!mount_font(n
, nm
))
438 if (family
->make_definite(n
) < 0)
440 prev_fontno
= fontno
;
445 void environment::set_font(int n
)
449 if (is_good_fontno(n
)) {
450 prev_fontno
= fontno
;
454 error("bad font number");
457 void environment::set_family(symbol fam
)
460 if (prev_family
->make_definite(fontno
) < 0)
462 font_family
*tem
= family
;
463 family
= prev_family
;
467 font_family
*f
= lookup_family(fam
);
468 if (f
->make_definite(fontno
) < 0)
470 prev_family
= family
;
475 void environment::set_size(int n
)
480 font_size temp
= prev_size
;
483 int temp2
= prev_requested_size
;
484 prev_requested_size
= requested_size
;
485 requested_size
= temp2
;
490 prev_requested_size
= requested_size
;
495 void environment::set_char_height(int n
)
499 if (n
== requested_size
|| n
<= 0)
505 void environment::set_char_slant(int n
)
512 environment::environment(symbol nm
)
514 prev_line_length((units_per_inch
*13)/2),
515 line_length((units_per_inch
*13)/2),
516 prev_title_length((units_per_inch
*13)/2),
517 title_length((units_per_inch
*13)/2),
518 prev_size(sizescale
*10),
520 requested_size(sizescale
*10),
521 prev_requested_size(sizescale
*10),
525 sentence_space_size(12),
526 adjust_mode(ADJUST_BOTH
),
529 prev_line_interrupted(0),
531 right_justify_lines(0),
532 prev_vertical_spacing(points_to_units(12)),
533 vertical_spacing(points_to_units(12)),
534 prev_line_spacing(1),
538 have_temporary_indent(0),
547 no_break_control_char('\''),
548 hyphen_indicator_char(0),
553 tabs(units_per_inch
/2, TAB_LEFT
),
554 current_tab(TAB_NONE
),
556 margin_character_node(0),
557 margin_character_distance(points_to_units(10)),
559 number_text_separation(1),
560 line_number_multiple(1),
561 line_number_indent(0),
564 leader_char(charset_table
['.']),
565 hyphenation_flags(1),
570 #endif /* WIDOW_CONTROL */
571 hyphen_line_count(0),
573 hyphenation_space(H0
),
574 hyphenation_margin(H0
),
577 prev_family
= family
= lookup_family(default_family
);
578 prev_fontno
= fontno
= 1;
579 if (!is_good_fontno(1))
580 fatal("font number 1 not a valid font");
581 if (family
->make_definite(1) < 0)
582 fatal("invalid default family `%1'", default_family
.contents());
583 prev_fontno
= fontno
;
586 environment::environment(const environment
*e
)
587 : name(e
->name
), // so that eg `.if "\n[.ev]"0"' works
588 prev_line_length(e
->prev_line_length
),
589 line_length(e
->line_length
),
590 prev_title_length(e
->prev_title_length
),
591 title_length(e
->title_length
),
592 prev_size(e
->prev_size
),
594 prev_requested_size(e
->prev_requested_size
),
595 requested_size(e
->requested_size
),
596 char_height(e
->char_height
),
597 char_slant(e
->char_slant
),
598 space_size(e
->space_size
),
599 sentence_space_size(e
->sentence_space_size
),
600 adjust_mode(e
->adjust_mode
),
603 prev_line_interrupted(0),
605 right_justify_lines(0),
606 prev_vertical_spacing(e
->prev_vertical_spacing
),
607 vertical_spacing(e
->vertical_spacing
),
608 prev_line_spacing(e
->prev_line_spacing
),
609 line_spacing(e
->line_spacing
),
610 prev_indent(e
->prev_indent
),
612 have_temporary_indent(0),
616 prev_text_length(e
->prev_text_length
),
620 control_char(e
->control_char
),
621 no_break_control_char(e
->no_break_control_char
),
622 hyphen_indicator_char(e
->hyphen_indicator_char
),
628 current_tab(TAB_NONE
),
630 margin_character_node(e
->margin_character_node
),
631 margin_character_distance(e
->margin_character_distance
),
633 number_text_separation(e
->number_text_separation
),
634 line_number_multiple(e
->line_number_multiple
),
635 line_number_indent(e
->line_number_indent
),
636 no_number_count(e
->no_number_count
),
637 tab_char(e
->tab_char
),
638 leader_char(e
->leader_char
),
639 hyphenation_flags(e
->hyphenation_flags
),
641 prev_fontno(e
->prev_fontno
),
644 prev_family(e
->prev_family
),
647 widow_control(e
->widow_control
),
648 #endif /* WIDOW_CONTROL */
649 hyphen_line_max(e
->hyphen_line_max
),
650 hyphen_line_count(0),
651 hyphenation_space(e
->hyphenation_space
),
652 hyphenation_margin(e
->hyphenation_margin
),
657 environment::~environment()
660 delete_node_list(line
);
661 delete_node_list(numbering_nodes
);
664 hunits
environment::get_input_line_position()
668 n
= -input_line_start
;
670 n
= width_total
- input_line_start
;
676 void environment::set_input_line_position(hunits n
)
678 input_line_start
= line
== 0 ? -n
: width_total
- n
;
680 input_line_start
+= tab_width
;
683 hunits
environment::get_line_length()
688 hunits
environment::get_saved_line_length()
691 return target_text_length
+ saved_indent
;
696 vunits
environment::get_vertical_spacing()
698 return vertical_spacing
;
701 int environment::get_line_spacing()
706 int environment::get_bold()
708 return get_bold_fontno(fontno
);
711 hunits
environment::get_digit_width()
713 return env_digit_width(this);
716 int environment::get_adjust_mode()
721 int environment::get_fill()
726 hunits
environment::get_indent()
731 hunits
environment::get_saved_indent()
735 else if (have_temporary_indent
)
736 return temporary_indent
;
741 hunits
environment::get_temporary_indent()
743 return temporary_indent
;
746 hunits
environment::get_title_length()
751 node
*environment::get_prev_char()
753 for (node
*n
= current_tab
? tab_contents
: line
; n
; n
= n
->next
) {
754 node
*last
= n
->last_char_node();
761 hunits
environment::get_prev_char_width()
763 node
*last
= get_prev_char();
766 return last
->width();
769 hunits
environment::get_prev_char_skew()
771 node
*last
= get_prev_char();
777 vunits
environment::get_prev_char_height()
779 node
*last
= get_prev_char();
783 last
->vertical_extent(&min
, &max
);
787 vunits
environment::get_prev_char_depth()
789 node
*last
= get_prev_char();
793 last
->vertical_extent(&min
, &max
);
797 hunits
environment::get_text_length()
799 hunits n
= line
== 0 ? H0
: width_total
;
805 hunits
environment::get_prev_text_length()
807 return prev_text_length
;
811 static int sb_reg_contents
= 0;
812 static int st_reg_contents
= 0;
813 static int ct_reg_contents
= 0;
814 static int rsb_reg_contents
= 0;
815 static int rst_reg_contents
= 0;
816 static int skw_reg_contents
= 0;
817 static int ssc_reg_contents
= 0;
819 void environment::width_registers()
821 // this is used to implement \w; it sets the st, sb, ct registers
822 vunits min
= 0, max
= 0, cur
= 0;
823 int character_type
= 0;
824 ssc_reg_contents
= line
? line
->subscript_correction().to_units() : 0;
825 skw_reg_contents
= line
? line
->skew().to_units() : 0;
826 line
= reverse_node_list(line
);
827 vunits real_min
= V0
;
828 vunits real_max
= V0
;
830 for (node
*tem
= line
; tem
; tem
= tem
->next
) {
831 tem
->vertical_extent(&v1
, &v2
);
838 if ((cur
+= tem
->vertical_width()) < min
)
842 character_type
|= tem
->character_type();
844 line
= reverse_node_list(line
);
845 st_reg_contents
= -min
.to_units();
846 sb_reg_contents
= -max
.to_units();
847 rst_reg_contents
= -real_min
.to_units();
848 rsb_reg_contents
= -real_max
.to_units();
849 ct_reg_contents
= character_type
;
852 node
*environment::extract_output_line()
861 /* environment related requests */
863 void environment_switch()
865 int pop
= 0; // 1 means pop, 2 means pop but no error message on underflow
866 if (curenv
->is_dummy())
867 error("can't switch environments when current environment is dummy");
872 if (!tok
.delimiter()) {
873 // It looks like a number.
875 if (get_integer(&n
)) {
876 if (n
>= 0 && n
< NENVIRONMENTS
) {
877 env_stack
= new env_list(curenv
, env_stack
);
878 if (env_table
[n
] == 0)
879 env_table
[n
] = new environment(itoa(n
));
880 curenv
= env_table
[n
];
889 nm
= get_long_name(1);
894 environment
*e
= (environment
*)env_dictionary
.lookup(nm
);
896 e
= new environment(nm
);
897 (void)env_dictionary
.lookup(nm
, e
);
899 env_stack
= new env_list(curenv
, env_stack
);
904 if (env_stack
== 0) {
906 error("environment stack underflow");
909 curenv
= env_stack
->env
;
910 env_list
*tem
= env_stack
;
911 env_stack
= env_stack
->next
;
919 static symbol
P_symbol("P");
923 symbol s
= get_name();
925 if (s
.is_null() || s
== P_symbol
) {
930 for (const char *p
= s
.contents(); p
!= 0 && *p
!= 0; p
++)
937 curenv
->set_font(atoi(s
.contents()));
945 symbol s
= get_name(1);
947 curenv
->set_family(s
);
956 if (get_number(&n
, 0, curenv
->get_requested_point_size()/sizescale
)) {
973 if (get_number(&n
, 'z', curenv
->get_requested_point_size())) {
987 if (get_integer(&n
)) {
988 curenv
->space_size
= n
;
991 curenv
->sentence_space_size
= n
;
994 curenv
->sentence_space_size
= curenv
->space_size
;
1001 while (!tok
.newline() && !tok
.eof())
1011 while (!tok
.newline() && !tok
.eof())
1022 if (!has_arg() || !get_integer(&n
))
1024 while (!tok
.newline() && !tok
.eof())
1028 curenv
->right_justify_lines
= 0;
1029 curenv
->center_lines
= n
;
1033 void right_justify()
1036 if (!has_arg() || !get_integer(&n
))
1038 while (!tok
.newline() && !tok
.eof())
1042 curenv
->center_lines
= 0;
1043 curenv
->right_justify_lines
= n
;
1051 hunits temp
= curenv
->line_length
;
1052 curenv
->line_length
= curenv
->prev_line_length
;
1053 curenv
->prev_line_length
= temp
;
1055 else if (get_hunits(&temp
, 'm', curenv
->line_length
)) {
1057 warning(WARN_RANGE
, "bad line length %1u", temp
.to_units());
1060 curenv
->prev_line_length
= curenv
->line_length
;
1061 curenv
->line_length
= temp
;
1070 hunits temp
= curenv
->title_length
;
1071 curenv
->title_length
= curenv
->prev_title_length
;
1072 curenv
->prev_title_length
= temp
;
1074 else if (get_hunits(&temp
, 'm', curenv
->title_length
)) {
1076 warning(WARN_RANGE
, "bad title length %1u", temp
.to_units());
1079 curenv
->prev_title_length
= curenv
->title_length
;
1080 curenv
->title_length
= temp
;
1085 void vertical_spacing()
1088 vunits temp
= curenv
->vertical_spacing
;
1089 curenv
->vertical_spacing
= curenv
->prev_vertical_spacing
;
1090 curenv
->prev_vertical_spacing
= temp
;
1094 if (get_vunits(&temp
, 'p', curenv
->vertical_spacing
)) {
1096 warning(WARN_RANGE
, "vertical spacing must be greater than 0");
1099 curenv
->prev_vertical_spacing
= curenv
->vertical_spacing
;
1100 curenv
->vertical_spacing
= temp
;
1110 temp
= curenv
->line_spacing
;
1111 curenv
->line_spacing
= curenv
->prev_line_spacing
;
1112 curenv
->prev_line_spacing
= temp
;
1114 else if (get_integer(&temp
)) {
1116 warning(WARN_RANGE
, "value %1 out of range: interpreted as 1", temp
);
1119 curenv
->prev_line_spacing
= curenv
->line_spacing
;
1120 curenv
->line_spacing
= temp
;
1130 temp
= curenv
->prev_indent
;
1131 else if (!get_hunits(&temp
, 'm', curenv
->indent
))
1133 while (!tok
.newline() && !tok
.eof())
1138 warning(WARN_RANGE
, "indent cannot be negative");
1142 curenv
->have_temporary_indent
= 0;
1143 curenv
->prev_indent
= curenv
->indent
;
1144 curenv
->indent
= temp
;
1149 void temporary_indent()
1153 if (!get_hunits(&temp
, 'm', curenv
->get_indent()))
1155 while (!tok
.newline() && !tok
.eof())
1160 warning(WARN_RANGE
, "total indent cannot be negative");
1164 curenv
->temporary_indent
= temp
;
1165 curenv
->have_temporary_indent
= 1;
1175 else if (!get_integer(&n
))
1178 if (curenv
->underline_lines
> 0) {
1179 curenv
->prev_fontno
= curenv
->fontno
;
1180 curenv
->fontno
= curenv
->pre_underline_fontno
;
1182 curenv
->underline_lines
= 0;
1185 curenv
->underline_lines
= n
;
1186 curenv
->pre_underline_fontno
= curenv
->fontno
;
1187 curenv
->fontno
= get_underline_fontno();
1196 curenv
->control_char
= '.';
1197 else if (tok
.ch() == 0)
1198 error("bad control character");
1200 curenv
->control_char
= tok
.ch();
1204 void no_break_control_char()
1207 curenv
->no_break_control_char
= '\'';
1208 else if (tok
.ch() == 0)
1209 error("bad control character");
1211 curenv
->no_break_control_char
= tok
.ch();
1216 void margin_character()
1218 if (curenv
->margin_character_node
) {
1219 delete curenv
->margin_character_node
;
1220 curenv
->margin_character_node
= 0;
1222 charinfo
*ci
= get_optional_char();
1224 curenv
->margin_character_node
= curenv
->make_char_node(ci
);
1226 if (curenv
->margin_character_node
&& has_arg() && get_hunits(&d
, 'm'))
1227 curenv
->margin_character_distance
= d
;
1234 delete_node_list(curenv
->numbering_nodes
);
1235 curenv
->numbering_nodes
= 0;
1237 if (has_arg() && get_integer(&n
, next_line_number
)) {
1238 next_line_number
= n
;
1239 if (next_line_number
< 0) {
1240 warning(WARN_RANGE
, "negative line number");
1241 next_line_number
= 0;
1244 for (int i
= '9'; i
>= '0'; i
--) {
1245 node
*tem
= make_node(charset_table
[i
], curenv
);
1253 curenv
->numbering_nodes
= nd
;
1254 curenv
->line_number_digit_width
= env_digit_width(curenv
);
1256 if (!tok
.delimiter()) {
1257 if (get_integer(&n
)) {
1259 warning(WARN_RANGE
, "negative or zero line number multiple");
1262 curenv
->line_number_multiple
= n
;
1266 while (!tok
.space() && !tok
.newline() && !tok
.eof())
1269 if (!tok
.delimiter()) {
1270 if (get_integer(&n
))
1271 curenv
->number_text_separation
= n
;
1274 while (!tok
.space() && !tok
.newline() && !tok
.eof())
1276 if (has_arg() && !tok
.delimiter() && get_integer(&n
))
1277 curenv
->line_number_indent
= n
;
1288 if (get_integer(&n
))
1289 curenv
->no_number_count
= n
> 0 ? n
: 0;
1292 curenv
->no_number_count
= 1;
1298 curenv
->hyphenation_flags
= 0;
1302 void hyphenate_request()
1306 if (get_integer(&n
))
1307 curenv
->hyphenation_flags
= n
;
1310 curenv
->hyphenation_flags
= 1;
1316 curenv
->hyphen_indicator_char
= get_optional_char();
1320 void hyphen_line_max_request()
1324 if (get_integer(&n
))
1325 curenv
->hyphen_line_max
= n
;
1328 curenv
->hyphen_line_max
= -1;
1332 void environment::interrupt()
1335 add_node(new transparent_dummy_node
);
1340 void environment::newline()
1342 if (underline_lines
> 0) {
1343 if (--underline_lines
== 0) {
1344 prev_fontno
= fontno
;
1345 fontno
= pre_underline_fontno
;
1352 // strip trailing spaces
1353 while (line
!= 0 && line
->discardable()) {
1354 width_total
-= line
->width();
1355 space_total
-= line
->nspaces();
1360 node
*to_be_output
= 0;
1361 hunits to_be_output_width
;
1362 prev_line_interrupted
= 0;
1365 else if (interrupted
) {
1367 // see environment::final_break
1368 prev_line_interrupted
= exit_started
? 2 : 1;
1370 else if (center_lines
> 0) {
1372 hunits x
= target_text_length
- width_total
;
1374 saved_indent
+= x
/2;
1375 to_be_output
= line
;
1376 to_be_output_width
= width_total
;
1379 else if (right_justify_lines
> 0) {
1380 --right_justify_lines
;
1381 hunits x
= target_text_length
- width_total
;
1384 to_be_output
= line
;
1385 to_be_output_width
= width_total
;
1391 to_be_output
= line
;
1392 to_be_output_width
= width_total
;
1395 input_line_start
= line
== 0 ? H0
: width_total
;
1397 output_line(to_be_output
, to_be_output_width
);
1398 hyphen_line_count
= 0;
1400 if (input_trap_count
> 0) {
1401 if (--input_trap_count
== 0)
1402 spring_trap(input_trap
);
1406 void environment::output_line(node
*n
, hunits width
)
1408 prev_text_length
= width
;
1409 if (margin_character_node
) {
1410 hunits d
= line_length
+ margin_character_distance
- saved_indent
- width
;
1412 n
= new hmotion_node(d
, n
);
1415 node
*tem
= margin_character_node
->copy();
1418 width
+= tem
->width();
1422 node
*tem
= n
->next
;
1427 if (!saved_indent
.is_zero())
1428 nn
= new hmotion_node(saved_indent
, nn
);
1429 width
+= saved_indent
;
1430 if (no_number_count
> 0)
1432 else if (numbering_nodes
) {
1433 hunits w
= (line_number_digit_width
1434 *(3+line_number_indent
+number_text_separation
));
1435 if (next_line_number
% line_number_multiple
!= 0)
1436 nn
= new hmotion_node(w
, nn
);
1439 nn
= new hmotion_node(number_text_separation
*line_number_digit_width
,
1441 x
-= number_text_separation
*line_number_digit_width
;
1443 sprintf(buf
, "%3d", next_line_number
);
1444 for (char *p
= strchr(buf
, '\0') - 1; p
>= buf
&& *p
!= ' '; --p
) {
1445 node
*gn
= numbering_nodes
;
1446 for (int count
= *p
- '0'; count
> 0; count
--)
1453 nn
= new hmotion_node(x
, nn
);
1458 output(nn
, !fill
, vertical_spacing
, line_spacing
, width
);
1461 void environment::start_line()
1465 line
= new line_start_node
;
1466 if (have_temporary_indent
) {
1467 saved_indent
= temporary_indent
;
1468 have_temporary_indent
= 0;
1471 saved_indent
= indent
;
1472 target_text_length
= line_length
- saved_indent
;
1477 hunits
environment::get_hyphenation_space()
1479 return hyphenation_space
;
1482 void hyphenation_space_request()
1485 if (get_hunits(&n
, 'm')) {
1487 warning(WARN_RANGE
, "hyphenation space cannot be negative");
1490 curenv
->hyphenation_space
= n
;
1495 hunits
environment::get_hyphenation_margin()
1497 return hyphenation_margin
;
1500 void hyphenation_margin_request()
1503 if (get_hunits(&n
, 'm')) {
1505 warning(WARN_RANGE
, "hyphenation margin cannot be negative");
1508 curenv
->hyphenation_margin
= n
;
1513 breakpoint
*environment::choose_breakpoint()
1515 hunits x
= width_total
;
1516 int s
= space_total
;
1518 breakpoint
*best_bp
= 0; // the best breakpoint so far
1519 int best_bp_fits
= 0;
1523 breakpoint
*bp
= n
->get_breakpoints(x
, s
);
1525 if (bp
->width
<= target_text_length
) {
1526 if (!bp
->hyphenated
) {
1527 breakpoint
*tem
= bp
->next
;
1530 breakpoint
*tem1
= tem
;
1535 // Decide whether to use the hyphenated breakpoint.
1536 && (hyphen_line_max
< 0
1537 // Only choose the hyphenated breakpoint if it would not
1538 // exceed the maximum number of consecutive hyphenated
1540 || hyphen_line_count
+ 1 <= hyphen_line_max
)
1541 && !(adjust_mode
== ADJUST_BOTH
1542 // Don't choose the hyphenated breakpoint if the line
1543 // can be justified by adding no more than
1544 // hyphenation_space to any word space.
1546 && (((target_text_length
- bp
->width
1547 + (bp
->nspaces
- 1)*hresolution
)/bp
->nspaces
)
1548 <= hyphenation_space
))
1549 // Don't choose the hyphenated breakpoint if the line
1550 // is no more than hyphenation_margin short.
1551 : target_text_length
- bp
->width
<= hyphenation_margin
)) {
1560 if ((adjust_mode
== ADJUST_BOTH
1561 ? hyphenation_space
== H0
1562 : hyphenation_margin
== H0
)
1563 && (hyphen_line_max
< 0
1564 || hyphen_line_count
+ 1 <= hyphen_line_max
)) {
1565 // No need to consider a non-hyphenated breakpoint.
1570 // It fits but it's hyphenated.
1571 if (!best_bp_fits
) {
1579 breakpoint
*tem
= bp
;
1596 warning(WARN_BREAK
, "can't break line");
1602 void environment::hyphenate_line()
1606 hyphenation_type prev_type
= line
->get_hyphenation_type();
1607 for (node
**startp
= &line
->next
; *startp
!= 0; startp
= &(*startp
)->next
) {
1608 hyphenation_type this_type
= (*startp
)->get_hyphenation_type();
1609 if (prev_type
== HYPHEN_BOUNDARY
&& this_type
== HYPHEN_MIDDLE
)
1611 prev_type
= this_type
;
1615 node
*tem
= *startp
;
1620 } while (tem
!= 0 && tem
->get_hyphenation_type() == HYPHEN_MIDDLE
);
1621 int inhibit
= (tem
!= 0 && tem
->get_hyphenation_type() == HYPHEN_INHIBIT
);
1623 hyphen_list
*sl
= 0;
1626 while (tem
!= end
) {
1627 sl
= tem
->get_hyphen_list(sl
);
1630 tem1
->next
= forward
;
1634 // this is for characters like hyphen and emdash
1636 for (hyphen_list
*h
= sl
; h
; h
= h
->next
) {
1637 h
->breakable
= (prev_code
!= 0
1639 && h
->next
->hyphenation_code
!= 0);
1640 prev_code
= h
->hyphenation_code
;
1643 if (hyphenation_flags
!= 0
1645 // this may not be right if we have extra space on this line
1646 && !((hyphenation_flags
& HYPHEN_LAST_LINE
)
1647 && curdiv
->distance_to_next_trap() <= line_spacing
*vertical_spacing
)
1649 hyphenate(sl
, hyphenation_flags
);
1650 while (forward
!= 0) {
1651 node
*tem1
= forward
;
1652 forward
= forward
->next
;
1654 tem
= tem1
->add_self(tem
, &sl
);
1659 static node
*node_list_reverse(node
*n
)
1671 static void distribute_space(node
*n
, int nspaces
, hunits desired_space
,
1672 int force_reverse
= 0)
1674 static int reverse
= 0;
1675 if (force_reverse
|| reverse
)
1676 n
= node_list_reverse(n
);
1677 for (node
*tem
= n
; tem
; tem
= tem
->next
)
1678 tem
->spread_space(&nspaces
, &desired_space
);
1679 if (force_reverse
|| reverse
)
1680 (void)node_list_reverse(n
);
1683 assert(desired_space
.is_zero() && nspaces
== 0);
1686 void environment::possibly_break_line(int forced
)
1688 if (!fill
|| current_tab
|| current_field
|| dummy
)
1690 while (line
!= 0 && (forced
|| width_total
> target_text_length
)) {
1692 breakpoint
*bp
= choose_breakpoint();
1694 // we'll find one eventually
1698 while (*ndp
!= bp
->nd
)
1699 ndp
= &(*ndp
)->next
;
1700 bp
->nd
->split(bp
->index
, &pre
, &post
);
1702 hunits extra_space_width
= H0
;
1703 switch(adjust_mode
) {
1705 if (bp
->nspaces
!= 0)
1706 extra_space_width
= target_text_length
- bp
->width
;
1709 saved_indent
+= (target_text_length
- bp
->width
)/2;
1712 saved_indent
+= target_text_length
- bp
->width
;
1715 distribute_space(pre
, bp
->nspaces
, extra_space_width
);
1716 hunits output_width
= bp
->width
+ extra_space_width
;
1717 input_line_start
-= output_width
;
1719 hyphen_line_count
++;
1721 hyphen_line_count
= 0;
1725 node
*first_non_discardable
= 0;
1726 for (node
*tem
= line
; tem
!= 0; tem
= tem
->next
)
1727 if (!tem
->discardable())
1728 first_non_discardable
= tem
;
1729 node
*to_be_discarded
;
1730 if (first_non_discardable
) {
1731 to_be_discarded
= first_non_discardable
->next
;
1732 first_non_discardable
->next
= 0;
1733 for (tem
= line
; tem
!= 0; tem
= tem
->next
) {
1734 width_total
+= tem
->width();
1735 space_total
+= tem
->nspaces();
1741 to_be_discarded
= line
;
1744 // Do output_line() here so that line will be 0 iff the
1745 // the environment will be empty.
1746 output_line(pre
, output_width
);
1747 while (to_be_discarded
!= 0) {
1748 tem
= to_be_discarded
;
1749 to_be_discarded
= to_be_discarded
->next
;
1750 input_line_start
-= tem
->width();
1754 if (have_temporary_indent
) {
1755 saved_indent
= temporary_indent
;
1756 have_temporary_indent
= 0;
1759 saved_indent
= indent
;
1760 target_text_length
= line_length
- saved_indent
;
1766 Do the break at the end of input after the end macro (if any).
1768 Unix troff behaves as follows: if the last line is
1772 it will output foo on the current page, and bar on the next page;
1781 everything will be output on the current page. This behaviour must be
1784 The problem is that some macro packages rely on this. For example,
1785 the ATK macros have an end macro that emits \c if it needs to print a
1786 table of contents but doesn't do a 'bp in the end macro; instead the
1787 'bp is done in the bottom of page trap. This works with Unix troff,
1788 provided that the current environment is not empty at the end of the
1791 The following will make macro packages that do that sort of thing work
1792 even if the current environment is empty at the end of the input file.
1793 If the last input line used \c and this line occurred in the end macro,
1794 then we'll force everything out on the current page, but we'll make
1795 sure that the environment isn't empty so that we won't exit at the
1796 bottom of this page.
1799 void environment::final_break()
1801 if (prev_line_interrupted
== 2) {
1803 add_node(new transparent_dummy_node
);
1809 void environment::do_break()
1811 if (curdiv
== topdiv
&& !topdiv
->first_page_begun
) {
1812 topdiv
->begin_page();
1818 line
= new space_node(H0
, line
); // this is so that hyphenation works
1820 possibly_break_line();
1822 while (line
!= 0 && line
->discardable()) {
1823 width_total
-= line
->width();
1824 space_total
-= line
->nspaces();
1830 input_line_start
= H0
;
1833 switch (adjust_mode
) {
1835 saved_indent
+= (target_text_length
- width_total
)/2;
1838 saved_indent
+= target_text_length
- width_total
;
1844 output_line(tem
, width_total
);
1845 hyphen_line_count
= 0;
1847 prev_line_interrupted
= 0;
1848 #ifdef WIDOW_CONTROL
1850 output_pending_lines();
1851 #endif /* WIDOW_CONTROL */
1854 int environment::is_empty()
1856 return !current_tab
&& line
== 0 && pending_lines
== 0;
1859 void break_request()
1861 while (!tok
.newline() && !tok
.eof())
1870 if (curdiv
== topdiv
&& !topdiv
->first_page_begun
) {
1871 handle_initial_title();
1875 hunits part_width
[3];
1876 part
[0] = part
[1] = part
[2] = 0;
1877 environment
env(curenv
);
1878 environment
*oldenv
= curenv
;
1880 read_title_parts(part
, part_width
);
1882 curenv
->size
= env
.size
;
1883 curenv
->prev_size
= env
.prev_size
;
1884 curenv
->requested_size
= env
.requested_size
;
1885 curenv
->prev_requested_size
= env
.prev_requested_size
;
1886 curenv
->char_height
= env
.char_height
;
1887 curenv
->char_slant
= env
.char_slant
;
1888 curenv
->fontno
= env
.fontno
;
1889 curenv
->prev_fontno
= env
.prev_fontno
;
1898 hunits
title_length(curenv
->title_length
);
1899 hunits f
= title_length
- part_width
[1];
1901 n
= new hmotion_node(f2
- part_width
[2], n
);
1909 n
= new hmotion_node(f
- f2
- part_width
[0], n
);
1917 curenv
->output_title(n
, !curenv
->fill
, curenv
->vertical_spacing
,
1918 curenv
->line_spacing
, title_length
);
1919 curenv
->hyphen_line_count
= 0;
1926 curenv
->adjust_mode
|= 1;
1930 curenv
->adjust_mode
= ADJUST_LEFT
;
1933 curenv
->adjust_mode
= ADJUST_RIGHT
;
1936 curenv
->adjust_mode
= ADJUST_CENTER
;
1940 curenv
->adjust_mode
= ADJUST_BOTH
;
1944 if (get_integer(&n
)) {
1945 if (0 <= n
&& n
<= 5)
1946 curenv
->adjust_mode
= n
;
1948 warning(WARN_RANGE
, "adjustment mode `%1' out of range", n
);
1956 curenv
->adjust_mode
&= ~1;
1964 curenv
->input_trap_count
= 0;
1965 else if (get_integer(&n
)) {
1967 error("number of lines for input trap must be greater than zero");
1969 symbol s
= get_name(1);
1971 curenv
->input_trap_count
= n
;
1972 curenv
->input_trap
= s
;
1981 // must not be R or C or L or a legitimate part of a number expression
1982 const char TAB_REPEAT_CHAR
= 'T';
1988 tab(hunits
, tab_type
);
1989 enum { BLOCK
= 1024 };
1990 static tab
*free_list
;
1991 void *operator new(size_t);
1992 void operator delete(void *);
1995 tab
*tab::free_list
= 0;
1997 void *tab::operator new(size_t n
)
1999 assert(n
== sizeof(tab
));
2001 free_list
= (tab
*)new char[sizeof(tab
)*BLOCK
];
2002 for (int i
= 0; i
< BLOCK
- 1; i
++)
2003 free_list
[i
].next
= free_list
+ i
+ 1;
2004 free_list
[BLOCK
-1].next
= 0;
2007 free_list
= (tab
*)(free_list
->next
);
2013 /* cfront can't cope with this. */
2016 void tab::operator delete(void *p
)
2019 ((tab
*)p
)->next
= free_list
;
2020 free_list
= (tab
*)p
;
2024 tab::tab(hunits x
, tab_type t
) : next(0), pos(x
), type(t
)
2028 tab_stops::tab_stops(hunits distance
, tab_type type
)
2031 repeated_list
= new tab(distance
, type
);
2034 tab_stops::~tab_stops()
2039 tab_type
tab_stops::distance_to_next_tab(hunits curpos
, hunits
*distance
)
2042 for (tab
*tem
= initial_list
; tem
&& tem
->pos
<= curpos
; tem
= tem
->next
)
2045 *distance
= tem
->pos
- curpos
;
2048 if (repeated_list
== 0)
2050 hunits base
= lastpos
;
2052 for (tem
= repeated_list
; tem
&& tem
->pos
+ base
<= curpos
; tem
= tem
->next
)
2055 *distance
= tem
->pos
+ base
- curpos
;
2058 assert(lastpos
> 0);
2064 const char *tab_stops::to_string()
2066 static char *buf
= 0;
2067 static int buf_size
= 0;
2068 // figure out a maximum on the amount of space we can need
2070 for (tab
*p
= initial_list
; p
; p
= p
->next
)
2072 for (p
= repeated_list
; p
; p
= p
->next
)
2074 // (10 for digits + 1 for u + 1 for 'C' or 'R') + 2 for ' &' + 1 for '\0'
2075 int need
= count
*12 + 3;
2076 if (buf
== 0 || need
> buf_size
) {
2080 buf
= new char[buf_size
];
2083 for (p
= initial_list
; p
; p
= p
->next
) {
2084 strcpy(ptr
, itoa(p
->pos
.to_units()));
2085 ptr
= strchr(ptr
, '\0');
2103 *ptr
++ = TAB_REPEAT_CHAR
;
2104 for (p
= repeated_list
; p
; p
= p
->next
) {
2105 strcpy(ptr
, itoa(p
->pos
.to_units()));
2106 ptr
= strchr(ptr
, '\0');
2127 tab_stops::tab_stops() : initial_list(0), repeated_list(0)
2131 tab_stops::tab_stops(const tab_stops
&ts
)
2132 : initial_list(0), repeated_list(0)
2134 tab
**p
= &initial_list
;
2135 tab
*t
= ts
.initial_list
;
2137 *p
= new tab(t
->pos
, t
->type
);
2142 t
= ts
.repeated_list
;
2144 *p
= new tab(t
->pos
, t
->type
);
2150 void tab_stops::clear()
2152 while (initial_list
) {
2153 tab
*tem
= initial_list
;
2154 initial_list
= initial_list
->next
;
2157 while (repeated_list
) {
2158 tab
*tem
= repeated_list
;
2159 repeated_list
= repeated_list
->next
;
2164 void tab_stops::add_tab(hunits pos
, tab_type type
, int repeated
)
2166 for (tab
**p
= repeated
? &repeated_list
: &initial_list
; *p
; p
= &(*p
)->next
)
2168 *p
= new tab(pos
, type
);
2172 void tab_stops::operator=(const tab_stops
&ts
)
2175 tab
**p
= &initial_list
;
2176 tab
*t
= ts
.initial_list
;
2178 *p
= new tab(t
->pos
, t
->type
);
2183 t
= ts
.repeated_list
;
2185 *p
= new tab(t
->pos
, t
->type
);
2194 hunits prev_pos
= 0;
2199 if (tok
.ch() == TAB_REPEAT_CHAR
) {
2204 if (!get_hunits(&pos
, 'm', prev_pos
))
2206 tab_type type
= TAB_LEFT
;
2207 if (tok
.ch() == 'C') {
2211 else if (tok
.ch() == 'R') {
2215 else if (tok
.ch() == 'L') {
2218 if (pos
<= prev_pos
&& !first
)
2220 "positions of tab stops must be strictly increasing");
2222 tabs
.add_tab(pos
, type
, repeated
);
2227 curenv
->tabs
= tabs
;
2231 const char *environment::get_tabs()
2233 return tabs
.to_string();
2237 tab_stops saved_tabs
;
2241 saved_tabs
= curenv
->tabs
;
2247 curenv
->tabs
= saved_tabs
;
2252 tab_type
environment::distance_to_next_tab(hunits
*distance
)
2254 return curenv
->tabs
.distance_to_next_tab(get_input_line_position(), distance
);
2257 void field_characters()
2259 field_delimiter_char
= get_optional_char();
2260 if (field_delimiter_char
)
2261 padding_indicator_char
= get_optional_char();
2263 padding_indicator_char
= 0;
2267 void environment::wrap_up_tab()
2274 switch (current_tab
) {
2276 tab_amount
= tab_distance
- tab_width
;
2277 line
= make_tab_node(tab_amount
, line
);
2280 tab_amount
= tab_distance
- tab_width
/2;
2281 line
= make_tab_node(tab_amount
, line
);
2288 width_total
+= tab_amount
;
2289 width_total
+= tab_width
;
2290 if (current_field
) {
2291 if (tab_precedes_field
) {
2292 pre_field_width
+= tab_amount
;
2293 tab_precedes_field
= 0;
2295 field_distance
-= tab_amount
;
2296 field_spaces
+= tab_field_spaces
;
2298 if (tab_contents
!= 0) {
2299 for (node
*tem
= tab_contents
; tem
->next
!= 0; tem
= tem
->next
)
2302 line
= tab_contents
;
2304 tab_field_spaces
= 0;
2308 current_tab
= TAB_NONE
;
2311 node
*environment::make_tab_node(hunits d
, node
*next
)
2313 if (leader_node
!= 0 && d
< 0) {
2314 error("motion generated by leader cannot be negative");
2319 return new hmotion_node(d
, next
);
2320 node
*n
= new hline_node(d
, leader_node
, next
);
2325 void environment::handle_tab(int is_leader
)
2330 charinfo
*ci
= is_leader
? leader_char
: tab_char
;
2332 leader_node
= ci
? make_char_node(ci
) : 0;
2333 tab_type t
= distance_to_next_tab(&d
);
2338 add_node(make_tab_node(d
));
2346 tab_field_spaces
= 0;
2353 void environment::start_field()
2355 assert(!current_field
);
2357 if (distance_to_next_tab(&d
) != TAB_NONE
) {
2358 pre_field_width
= get_text_length();
2362 tab_field_spaces
= 0;
2363 for (node
*p
= line
; p
; p
= p
->next
)
2368 tab_precedes_field
= current_tab
!= TAB_NONE
;
2371 error("zero field width");
2374 void environment::wrap_up_field()
2376 if (!current_tab
&& field_spaces
== 0)
2378 hunits padding
= field_distance
- (get_text_length() - pre_field_width
);
2379 if (current_tab
&& tab_field_spaces
!= 0) {
2380 hunits tab_padding
= scale(padding
,
2382 field_spaces
+ tab_field_spaces
);
2383 padding
-= tab_padding
;
2384 distribute_space(tab_contents
, tab_field_spaces
, tab_padding
, 1);
2385 tab_field_spaces
= 0;
2386 tab_width
+= tab_padding
;
2388 if (field_spaces
!= 0) {
2389 distribute_space(line
, field_spaces
, padding
, 1);
2390 width_total
+= padding
;
2392 // the start of the tab has been moved to the right by padding, so
2393 tab_distance
-= padding
;
2394 if (tab_distance
<= H0
) {
2395 // use the next tab stop instead
2396 current_tab
= tabs
.distance_to_next_tab(get_input_line_position()
2399 if (current_tab
== TAB_NONE
|| current_tab
== TAB_LEFT
) {
2400 width_total
+= tab_width
;
2401 if (current_tab
== TAB_LEFT
) {
2402 line
= make_tab_node(tab_distance
, line
);
2403 width_total
+= tab_distance
;
2404 current_tab
= TAB_NONE
;
2406 if (tab_contents
!= 0) {
2407 for (node
*tem
= tab_contents
; tem
->next
!= 0; tem
= tem
->next
)
2410 line
= tab_contents
;
2422 void environment::add_padding()
2425 tab_contents
= new space_node(H0
, tab_contents
);
2431 line
= new space_node(H0
, line
);
2436 typedef int (environment::*INT_FUNCP
)();
2437 typedef vunits (environment::*VUNITS_FUNCP
)();
2438 typedef hunits (environment::*HUNITS_FUNCP
)();
2439 typedef const char *(environment::*STRING_FUNCP
)();
2441 class int_env_reg
: public reg
{
2444 int_env_reg(INT_FUNCP
);
2445 const char *get_string();
2446 int get_value(units
*val
);
2449 class vunits_env_reg
: public reg
{
2452 vunits_env_reg(VUNITS_FUNCP f
);
2453 const char *get_string();
2454 int get_value(units
*val
);
2458 class hunits_env_reg
: public reg
{
2461 hunits_env_reg(HUNITS_FUNCP f
);
2462 const char *get_string();
2463 int get_value(units
*val
);
2466 class string_env_reg
: public reg
{
2469 string_env_reg(STRING_FUNCP
);
2470 const char *get_string();
2473 int_env_reg::int_env_reg(INT_FUNCP f
) : func(f
)
2477 int int_env_reg::get_value(units
*val
)
2479 *val
= (curenv
->*func
)();
2483 const char *int_env_reg::get_string()
2485 return itoa((curenv
->*func
)());
2488 vunits_env_reg::vunits_env_reg(VUNITS_FUNCP f
) : func(f
)
2492 int vunits_env_reg::get_value(units
*val
)
2494 *val
= (curenv
->*func
)().to_units();
2498 const char *vunits_env_reg::get_string()
2500 return itoa((curenv
->*func
)().to_units());
2503 hunits_env_reg::hunits_env_reg(HUNITS_FUNCP f
) : func(f
)
2507 int hunits_env_reg::get_value(units
*val
)
2509 *val
= (curenv
->*func
)().to_units();
2513 const char *hunits_env_reg::get_string()
2515 return itoa((curenv
->*func
)().to_units());
2518 string_env_reg::string_env_reg(STRING_FUNCP f
) : func(f
)
2522 const char *string_env_reg::get_string()
2524 return (curenv
->*func
)();
2527 class horizontal_place_reg
: public general_reg
{
2529 horizontal_place_reg();
2530 int get_value(units
*);
2531 void set_value(units
);
2534 horizontal_place_reg::horizontal_place_reg()
2538 int horizontal_place_reg::get_value(units
*res
)
2540 *res
= curenv
->get_input_line_position().to_units();
2544 void horizontal_place_reg::set_value(units n
)
2546 curenv
->set_input_line_position(hunits(n
));
2549 const char *environment::get_font_family_string()
2551 return family
->nm
.contents();
2554 const char *environment::get_name_string()
2556 return name
.contents();
2559 // Convert a quantity in scaled points to ascii decimal fraction.
2561 const char *sptoa(int sp
)
2564 assert(sizescale
> 0);
2567 if (sp
% sizescale
== 0)
2568 return itoa(sp
/sizescale
);
2569 // See if 1/sizescale is exactly representable as a decimal fraction,
2570 // ie its only prime factors are 2 and 5.
2573 while ((n
& 1) == 0) {
2578 while ((n
% 5) == 0) {
2583 int decimal_point
= power5
> power2
? power5
: power2
;
2584 if (decimal_point
<= 10) {
2587 for (t
= decimal_point
- power2
; --t
>= 0;)
2589 for (t
= decimal_point
- power5
; --t
>= 0;)
2591 if (factor
== 1 || sp
<= INT_MAX
/factor
)
2592 return iftoa(sp
*factor
, decimal_point
);
2595 double s
= double(sp
)/double(sizescale
);
2596 double factor
= 10.0;
2598 int decimal_point
= 0;
2600 double v
= ceil(s
*factor
);
2605 } while (++decimal_point
< 10);
2606 return iftoa(int(val
), decimal_point
);
2609 const char *environment::get_point_size_string()
2611 return sptoa(curenv
->get_point_size());
2614 const char *environment::get_requested_point_size_string()
2616 return sptoa(curenv
->get_requested_point_size());
2619 #define init_int_env_reg(name, func) \
2620 number_reg_dictionary.define(name, new int_env_reg(&environment::func))
2622 #define init_vunits_env_reg(name, func) \
2623 number_reg_dictionary.define(name, new vunits_env_reg(&environment::func))
2625 #define init_hunits_env_reg(name, func) \
2626 number_reg_dictionary.define(name, new hunits_env_reg(&environment::func))
2628 #define init_string_env_reg(name, func) \
2629 number_reg_dictionary.define(name, new string_env_reg(&environment::func))
2631 void init_env_requests()
2633 init_request("it", input_trap
);
2634 init_request("ad", adjust
);
2635 init_request("na", no_adjust
);
2636 init_request("ev", environment_switch
);
2637 init_request("lt", title_length
);
2638 init_request("ps", point_size
);
2639 init_request("ft", font_change
);
2640 init_request("fam", family_change
);
2641 init_request("ss", space_size
);
2642 init_request("fi", fill
);
2643 init_request("nf", no_fill
);
2644 init_request("ce", center
);
2645 init_request("rj", right_justify
);
2646 init_request("vs", vertical_spacing
);
2647 init_request("ls", line_spacing
);
2648 init_request("ll", line_length
);
2649 init_request("in", indent
);
2650 init_request("ti", temporary_indent
);
2651 init_request("ul", underline
);
2652 init_request("cu", underline
);
2653 init_request("cc", control_char
);
2654 init_request("c2", no_break_control_char
);
2655 init_request("br", break_request
);
2656 init_request("tl", title
);
2657 init_request("ta", set_tabs
);
2658 init_request("fc", field_characters
);
2659 init_request("mc", margin_character
);
2660 init_request("nn", no_number
);
2661 init_request("nm", number_lines
);
2662 init_request("tc", tab_character
);
2663 init_request("lc", leader_character
);
2664 init_request("hy", hyphenate_request
);
2665 init_request("hc", hyphen_char
);
2666 init_request("nh", no_hyphenate
);
2667 init_request("hlm", hyphen_line_max_request
);
2668 #ifdef WIDOW_CONTROL
2669 init_request("wdc", widow_control_request
);
2670 #endif /* WIDOW_CONTROL */
2672 init_request("tas", tabs_save
);
2673 init_request("tar", tabs_restore
);
2675 init_request("hys", hyphenation_space_request
);
2676 init_request("hym", hyphenation_margin_request
);
2677 init_int_env_reg(".f", get_font
);
2678 init_int_env_reg(".b", get_bold
);
2679 init_hunits_env_reg(".i", get_indent
);
2680 init_hunits_env_reg(".in", get_saved_indent
);
2681 init_int_env_reg(".j", get_adjust_mode
);
2682 init_hunits_env_reg(".k", get_text_length
);
2683 init_hunits_env_reg(".l", get_line_length
);
2684 init_hunits_env_reg(".ll", get_saved_line_length
);
2685 init_int_env_reg(".L", get_line_spacing
);
2686 init_hunits_env_reg(".n", get_prev_text_length
);
2687 init_string_env_reg(".s", get_point_size_string
);
2688 init_string_env_reg(".sr", get_requested_point_size_string
);
2689 init_int_env_reg(".ps", get_point_size
);
2690 init_int_env_reg(".psr", get_requested_point_size
);
2691 init_int_env_reg(".u", get_fill
);
2692 init_vunits_env_reg(".v", get_vertical_spacing
);
2693 init_hunits_env_reg(".w", get_prev_char_width
);
2694 init_int_env_reg(".ss", get_space_size
);
2695 init_int_env_reg(".sss", get_sentence_space_size
);
2696 init_string_env_reg(".fam", get_font_family_string
);
2697 init_string_env_reg(".ev", get_name_string
);
2698 init_int_env_reg(".hy", get_hyphenation_flags
);
2699 init_int_env_reg(".hlm", get_hyphen_line_max
);
2700 init_int_env_reg(".hlc", get_hyphen_line_count
);
2701 init_hunits_env_reg(".lt", get_title_length
);
2702 init_string_env_reg(".tabs", get_tabs
);
2703 init_hunits_env_reg(".csk", get_prev_char_skew
);
2704 init_vunits_env_reg(".cht", get_prev_char_height
);
2705 init_vunits_env_reg(".cdp", get_prev_char_depth
);
2706 init_int_env_reg(".ce", get_center_lines
);
2707 init_int_env_reg(".rj", get_right_justify_lines
);
2708 init_hunits_env_reg(".hys", get_hyphenation_space
);
2709 init_hunits_env_reg(".hym", get_hyphenation_margin
);
2710 number_reg_dictionary
.define("ln", new variable_reg(&next_line_number
));
2711 number_reg_dictionary
.define("ct", new variable_reg(&ct_reg_contents
));
2712 number_reg_dictionary
.define("sb", new variable_reg(&sb_reg_contents
));
2713 number_reg_dictionary
.define("st", new variable_reg(&st_reg_contents
));
2714 number_reg_dictionary
.define("rsb", new variable_reg(&rsb_reg_contents
));
2715 number_reg_dictionary
.define("rst", new variable_reg(&rst_reg_contents
));
2716 number_reg_dictionary
.define("ssc", new variable_reg(&ssc_reg_contents
));
2717 number_reg_dictionary
.define("skw", new variable_reg(&skw_reg_contents
));
2718 number_reg_dictionary
.define("hp", new horizontal_place_reg
);
2721 // Hyphenation - TeX's hyphenation algorithm with a less fancy implementation.
2723 const int WORD_MAX
= 1024;
2725 dictionary
exception_dictionary(501);
2727 static void hyphen_word()
2729 char buf
[WORD_MAX
+ 1];
2730 unsigned char pos
[WORD_MAX
+ 2];
2733 if (tok
.newline() || tok
.eof())
2737 while (i
< WORD_MAX
&& !tok
.space() && !tok
.newline() && !tok
.eof()) {
2738 charinfo
*ci
= tok
.get_char(1);
2744 if (ci
->get_ascii_code() == '-') {
2745 if (i
> 0 && (npos
== 0 || pos
[npos
- 1] != i
))
2749 int c
= ci
->get_hyphenation_code();
2758 unsigned char *tem
= new unsigned char[npos
+ 1];
2759 memcpy(tem
, pos
, npos
+1);
2760 tem
= (unsigned char *)exception_dictionary
.lookup(symbol(buf
), tem
);
2772 virtual void do_match(int len
, void *val
) = 0;
2775 void insert(const char *, int, void *);
2776 // find calls do_match for each match it finds
2777 void find(const char *pat
, int patlen
);
2786 trie_node(char, trie_node
*);
2790 trie_node::trie_node(char ch
, trie_node
*p
)
2791 : c(ch
), right(p
), down(0), val(0)
2795 trie_node::~trie_node()
2813 void trie::insert(const char *pat
, int patlen
, void *val
)
2815 trie_node
**p
= &tp
;
2816 assert(patlen
> 0 && pat
!= 0);
2818 while (*p
!= 0 && (*p
)->c
< pat
[0])
2820 if (*p
== 0 || (*p
)->c
!= pat
[0])
2821 *p
= new trie_node(pat
[0], *p
);
2822 if (--patlen
== 0) {
2831 void trie::find(const char *pat
, int patlen
)
2834 for (int i
= 0; p
!= 0 && i
< patlen
; i
++) {
2835 while (p
!= 0 && p
->c
< pat
[i
])
2837 if (p
!= 0 && p
->c
== pat
[i
]) {
2839 do_match(i
+1, p
->val
);
2847 class hyphen_trie
: private trie
{
2849 void do_match(int i
, void *v
);
2850 void insert_pattern(const char *pat
, int patlen
, int *num
);
2853 void hyphenate(const char *word
, int len
, int *hyphens
);
2854 void read_patterns_file(const char *name
);
2861 operation(int, int, operation
*);
2864 operation::operation(int i
, int j
, operation
*op
)
2865 : num(i
), distance(j
), next(op
)
2869 void hyphen_trie::insert_pattern(const char *pat
, int patlen
, int *num
)
2872 for (int i
= 0; i
< patlen
+1; i
++)
2874 op
= new operation(num
[i
], patlen
- i
, op
);
2875 insert(pat
, patlen
, op
);
2878 void hyphen_trie::hyphenate(const char *word
, int len
, int *hyphens
)
2880 for (int j
= 0; j
< len
+1; j
++)
2882 for (j
= 0; j
< len
- 1; j
++) {
2884 find(word
+ j
, len
- j
);
2888 inline int max(int m
, int n
)
2890 return m
> n
? m
: n
;
2893 void hyphen_trie::do_match(int i
, void *v
)
2895 operation
*op
= (operation
*)v
;
2897 h
[i
- op
->distance
] = max(h
[i
- op
->distance
], op
->num
);
2903 void hyphen_trie::read_patterns_file(const char *name
)
2907 int num
[WORD_MAX
+1];
2909 FILE *fp
= fopen(name
, "r");
2911 fatal("can't open hyphenation patterns file `%1': %2",
2912 name
, strerror(errno
));
2915 while (c
!= EOF
&& csspace(c
))
2929 } while (i
< WORD_MAX
&& c
!= EOF
&& !csspace(c
));
2930 insert_pattern(buf
, i
, num
);
2937 void hyphenate(hyphen_list
*h
, unsigned flags
)
2939 while (h
&& h
->hyphenation_code
== 0)
2942 char hbuf
[WORD_MAX
+2];
2943 char *buf
= hbuf
+ 1;
2944 for (hyphen_list
*tem
= h
; tem
&& len
< WORD_MAX
; tem
= tem
->next
) {
2945 if (tem
->hyphenation_code
!= 0)
2946 buf
[len
++] = tem
->hyphenation_code
;
2952 unsigned char *pos
= (unsigned char *)exception_dictionary
.lookup(buf
);
2956 for (tem
= h
; tem
!= 0; tem
= tem
->next
, i
++)
2963 hbuf
[0] = hbuf
[len
+1] = '.';
2964 int num
[WORD_MAX
+3];
2965 ht
.hyphenate(hbuf
, len
+2, num
);
2972 for (i
= 2, tem
= h
; i
< len
&& tem
; tem
= tem
->next
, i
++)
2979 void read_hyphen_file(const char *name
)
2981 ht
.read_patterns_file(name
);
2984 void init_hyphen_requests()
2986 init_request("hw", hyphen_word
);