2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
23 #include "dictionary.h"
32 #include "macropath.h"
36 symbol
default_family("T");
38 enum { ADJUST_LEFT
= 0, ADJUST_BOTH
= 1, ADJUST_CENTER
= 3, ADJUST_RIGHT
= 5 };
40 enum { HYPHEN_LAST_LINE
= 2, HYPHEN_LAST_CHARS
= 4, HYPHEN_FIRST_CHARS
= 8 };
45 env_list(environment
*e
, env_list
*p
) : env(e
), next(p
) {}
49 const int NENVIRONMENTS
= 10;
50 environment
*env_table
[NENVIRONMENTS
];
51 dictionary
env_dictionary(10);
53 static int next_line_number
= 0;
55 charinfo
*field_delimiter_char
;
56 charinfo
*padding_indicator_char
;
58 int translate_space_to_dummy
= 0;
60 class pending_output_line
{
67 int last_line
; // Is it the last line of the paragraph?
68 #endif /* WIDOW_CONTROL */
70 pending_output_line
*next
;
72 pending_output_line(node
*, int, vunits
, vunits
, hunits
,
73 pending_output_line
* = 0);
74 ~pending_output_line();
78 friend void environment::mark_last_line();
79 friend void environment::output(node
*, int, vunits
, vunits
, hunits
);
80 #endif /* WIDOW_CONTROL */
83 pending_output_line::pending_output_line(node
*n
, int nf
, vunits v
, vunits pv
,
84 hunits w
, pending_output_line
*p
)
85 : nd(n
), no_fill(nf
), vs(v
), post_vs(pv
), width(w
),
88 #endif /* WIDOW_CONTROL */
93 pending_output_line::~pending_output_line()
98 int pending_output_line::output()
100 if (trap_sprung_flag
)
103 if (next
&& next
->last_line
&& !no_fill
) {
104 curdiv
->need(vs
+ post_vs
+ vunits(vresolution
));
105 if (trap_sprung_flag
) {
106 next
->last_line
= 0; // Try to avoid infinite loops.
111 curdiv
->output(nd
, no_fill
, vs
, post_vs
, width
);
116 void environment::output(node
*nd
, int no_fill
, vunits vs
, vunits post_vs
,
120 while (pending_lines
) {
121 if (widow_control
&& !pending_lines
->no_fill
&& !pending_lines
->next
)
123 if (!pending_lines
->output())
125 pending_output_line
*tem
= pending_lines
;
126 pending_lines
= pending_lines
->next
;
129 #else /* WIDOW_CONTROL */
130 output_pending_lines();
131 #endif /* WIDOW_CONTROL */
132 if (!trap_sprung_flag
&& !pending_lines
134 && (!widow_control
|| no_fill
)
135 #endif /* WIDOW_CONTROL */
137 curdiv
->output(nd
, no_fill
, vs
, post_vs
, width
);
140 pending_output_line
**p
;
141 for (p
= &pending_lines
; *p
; p
= &(*p
)->next
)
143 *p
= new pending_output_line(nd
, no_fill
, vs
, post_vs
, width
);
147 // a line from .tl goes at the head of the queue
149 void environment::output_title(node
*nd
, int no_fill
, vunits vs
,
150 vunits post_vs
, hunits width
)
152 if (!trap_sprung_flag
)
153 curdiv
->output(nd
, no_fill
, vs
, post_vs
, width
);
155 pending_lines
= new pending_output_line(nd
, no_fill
, vs
, post_vs
, width
,
159 void environment::output_pending_lines()
161 while (pending_lines
&& pending_lines
->output()) {
162 pending_output_line
*tem
= pending_lines
;
163 pending_lines
= pending_lines
->next
;
170 void environment::mark_last_line()
172 if (!widow_control
|| !pending_lines
)
174 for (pending_output_line
*p
= pending_lines
; p
->next
; p
= p
->next
)
180 void widow_control_request()
183 if (has_arg() && get_integer(&n
))
184 curenv
->widow_control
= n
!= 0;
186 curenv
->widow_control
= 1;
190 #endif /* WIDOW_CONTROL */
192 /* font_size functions */
194 size_range
*font_size::size_table
= 0;
195 int font_size::nranges
= 0;
199 int compare_ranges(const void *p1
, const void *p2
)
201 return ((size_range
*)p1
)->min
- ((size_range
*)p2
)->min
;
206 void font_size::init_size_table(int *sizes
)
209 while (sizes
[nranges
*2] != 0)
212 size_table
= new size_range
[nranges
];
213 for (int i
= 0; i
< nranges
; i
++) {
214 size_table
[i
].min
= sizes
[i
*2];
215 size_table
[i
].max
= sizes
[i
*2 + 1];
217 qsort(size_table
, nranges
, sizeof(size_range
), compare_ranges
);
220 font_size::font_size(int sp
)
222 for (int i
= 0; i
< nranges
; i
++) {
223 if (sp
< size_table
[i
].min
) {
224 if (i
> 0 && size_table
[i
].min
- sp
>= sp
- size_table
[i
- 1].max
)
225 p
= size_table
[i
- 1].max
;
227 p
= size_table
[i
].min
;
230 if (sp
<= size_table
[i
].max
) {
235 p
= size_table
[nranges
- 1].max
;
238 int font_size::to_units()
240 return scale(p
, units_per_inch
, sizescale
*72);
243 // we can't do this in a static constructor because various dictionaries
244 // have to get initialized first
246 void init_environments()
248 curenv
= env_table
[0] = new environment("0");
253 curenv
->tab_char
= get_optional_char();
257 void leader_character()
259 curenv
->leader_char
= get_optional_char();
263 void environment::add_char(charinfo
*ci
)
268 // don't allow fields in dummy environments
269 else if (ci
== field_delimiter_char
&& !dummy
) {
275 else if (current_field
&& ci
== padding_indicator_char
)
277 else if (current_tab
) {
278 if (tab_contents
== 0)
279 tab_contents
= new line_start_node
;
280 if (ci
!= hyphen_indicator_char
)
281 tab_contents
= tab_contents
->add_char(ci
, this, &tab_width
, &s
);
283 tab_contents
= tab_contents
->add_discretionary_hyphen();
288 if (ci
!= hyphen_indicator_char
)
289 line
= line
->add_char(ci
, this, &width_total
, &space_total
);
291 line
= line
->add_discretionary_hyphen();
295 node
*environment::make_char_node(charinfo
*ci
)
297 return make_node(ci
, this);
300 void environment::add_node(node
*n
)
304 if (current_tab
|| current_field
)
309 else if (current_tab
) {
310 n
->next
= tab_contents
;
312 tab_width
+= n
->width();
316 if (discarding
&& n
->discardable()) {
317 // XXX possibly: input_line_start -= n->width();
323 width_total
+= n
->width();
324 space_total
+= n
->nspaces();
331 void environment::add_hyphen_indicator()
333 if (current_tab
|| interrupted
|| current_field
334 || hyphen_indicator_char
!= 0)
338 line
= line
->add_discretionary_hyphen();
341 int environment::get_hyphenation_flags()
343 return hyphenation_flags
;
346 int environment::get_hyphen_line_max()
348 return hyphen_line_max
;
351 int environment::get_hyphen_line_count()
353 return hyphen_line_count
;
356 int environment::get_center_lines()
361 int environment::get_right_justify_lines()
363 return right_justify_lines
;
366 void environment::add_italic_correction()
370 tab_contents
= tab_contents
->add_italic_correction(&tab_width
);
373 line
= line
->add_italic_correction(&width_total
);
376 void environment::space_newline()
378 assert(!current_tab
&& !current_field
);
382 hunits sw
= env_space_width(this);
383 hunits ssw
= env_sentence_space_width(this);
384 if (!translate_space_to_dummy
) {
386 if (node_list_ends_sentence(line
) == 1)
389 width_list
*w
= new width_list(sw
, ssw
);
390 if (node_list_ends_sentence(line
) == 1)
391 w
->next
= new width_list(sw
, ssw
);
392 if (line
!= 0 && line
->merge_space(x
, sw
, ssw
)) {
396 add_node(new word_space_node(x
, get_fill_color(), w
));
397 possibly_break_line(0, spread_flag
);
401 void environment::space()
403 space(env_space_width(this), env_sentence_space_width(this));
406 void environment::space(hunits space_width
, hunits sentence_space_width
)
410 if (current_field
&& padding_indicator_char
== 0) {
414 hunits x
= translate_space_to_dummy
? H0
: space_width
;
415 node
*p
= current_tab
? tab_contents
: line
;
416 hunits
*tp
= current_tab
? &tab_width
: &width_total
;
417 if (p
&& p
->nspaces() == 1 && p
->width() == x
418 && node_list_ends_sentence(p
->next
) == 1) {
419 hunits xx
= translate_space_to_dummy
? H0
: sentence_space_width
;
420 if (p
->merge_space(xx
, space_width
, sentence_space_width
)) {
425 if (p
&& p
->merge_space(x
, space_width
, sentence_space_width
)) {
429 add_node(new word_space_node(x
,
431 new width_list(space_width
,
432 sentence_space_width
)));
433 possibly_break_line(0, spread_flag
);
437 node
*do_underline_special(int);
439 void environment::set_font(symbol nm
)
443 if (nm
== symbol("P") || nm
.is_empty()) {
444 if (family
->make_definite(prev_fontno
) < 0)
447 fontno
= prev_fontno
;
451 prev_fontno
= fontno
;
452 int n
= symbol_fontno(nm
);
454 n
= next_available_font_position();
455 if (!mount_font(n
, nm
))
458 if (family
->make_definite(n
) < 0)
462 if (underline_spaces
&& fontno
!= prev_fontno
) {
463 if (fontno
== get_underline_fontno())
464 add_node(do_underline_special(1));
465 if (prev_fontno
== get_underline_fontno())
466 add_node(do_underline_special(0));
470 void environment::set_font(int n
)
474 if (is_good_fontno(n
)) {
475 prev_fontno
= fontno
;
479 warning(WARN_FONT
, "bad font number");
482 void environment::set_family(symbol fam
)
486 if (fam
.is_null() || fam
.is_empty()) {
487 if (prev_family
->make_definite(fontno
) < 0)
489 font_family
*tem
= family
;
490 family
= prev_family
;
494 font_family
*f
= lookup_family(fam
);
495 if (f
->make_definite(fontno
) < 0)
497 prev_family
= family
;
502 void environment::set_size(int n
)
507 font_size temp
= prev_size
;
510 int temp2
= prev_requested_size
;
511 prev_requested_size
= requested_size
;
512 requested_size
= temp2
;
517 prev_requested_size
= requested_size
;
522 void environment::set_char_height(int n
)
526 if (n
== requested_size
|| n
<= 0)
532 void environment::set_char_slant(int n
)
539 color
*environment::get_prev_glyph_color()
541 return prev_glyph_color
;
544 color
*environment::get_glyph_color()
549 color
*environment::get_prev_fill_color()
551 return prev_fill_color
;
554 color
*environment::get_fill_color()
559 void environment::set_glyph_color(color
*c
)
563 curenv
->prev_glyph_color
= curenv
->glyph_color
;
564 curenv
->glyph_color
= c
;
567 void environment::set_fill_color(color
*c
)
571 curenv
->prev_fill_color
= curenv
->fill_color
;
572 curenv
->fill_color
= c
;
575 environment::environment(symbol nm
)
577 prev_line_length((units_per_inch
*13)/2),
578 line_length((units_per_inch
*13)/2),
579 prev_title_length((units_per_inch
*13)/2),
580 title_length((units_per_inch
*13)/2),
581 prev_size(sizescale
*10),
583 requested_size(sizescale
*10),
584 prev_requested_size(sizescale
*10),
588 sentence_space_size(12),
589 adjust_mode(ADJUST_BOTH
),
592 prev_line_interrupted(0),
594 right_justify_lines(0),
595 prev_vertical_spacing(points_to_units(12)),
596 vertical_spacing(points_to_units(12)),
597 prev_post_vertical_spacing(0),
598 post_vertical_spacing(0),
599 prev_line_spacing(1),
604 have_temporary_indent(0),
608 continued_input_trap(0),
614 tabs(units_per_inch
/2, TAB_LEFT
),
616 current_tab(TAB_NONE
),
619 leader_char(charset_table
['.']),
623 margin_character_flags(0),
624 margin_character_node(0),
625 margin_character_distance(points_to_units(10)),
627 number_text_separation(1),
628 line_number_indent(0),
629 line_number_multiple(1),
631 hyphenation_flags(1),
632 hyphen_line_count(0),
634 hyphenation_space(H0
),
635 hyphenation_margin(H0
),
640 #endif /* WIDOW_CONTROL */
643 glyph_color(&default_color
),
644 prev_glyph_color(&default_color
),
645 fill_color(&default_color
),
646 prev_fill_color(&default_color
),
649 no_break_control_char('\''),
650 hyphen_indicator_char(0)
652 prev_family
= family
= lookup_family(default_family
);
653 prev_fontno
= fontno
= 1;
654 if (!is_good_fontno(1))
655 fatal("font number 1 not a valid font");
656 if (family
->make_definite(1) < 0)
657 fatal("invalid default family `%1'", default_family
.contents());
658 prev_fontno
= fontno
;
661 environment::environment(const environment
*e
)
663 prev_line_length(e
->prev_line_length
),
664 line_length(e
->line_length
),
665 prev_title_length(e
->prev_title_length
),
666 title_length(e
->title_length
),
667 prev_size(e
->prev_size
),
669 requested_size(e
->requested_size
),
670 prev_requested_size(e
->prev_requested_size
),
671 char_height(e
->char_height
),
672 char_slant(e
->char_slant
),
673 prev_fontno(e
->prev_fontno
),
675 prev_family(e
->prev_family
),
677 space_size(e
->space_size
),
678 sentence_space_size(e
->sentence_space_size
),
679 adjust_mode(e
->adjust_mode
),
682 prev_line_interrupted(0),
684 right_justify_lines(0),
685 prev_vertical_spacing(e
->prev_vertical_spacing
),
686 vertical_spacing(e
->vertical_spacing
),
687 prev_post_vertical_spacing(e
->prev_post_vertical_spacing
),
688 post_vertical_spacing(e
->post_vertical_spacing
),
689 prev_line_spacing(e
->prev_line_spacing
),
690 line_spacing(e
->line_spacing
),
691 prev_indent(e
->prev_indent
),
694 have_temporary_indent(0),
698 continued_input_trap(0),
700 prev_text_length(e
->prev_text_length
),
705 line_tabs(e
->line_tabs
),
706 current_tab(TAB_NONE
),
708 tab_char(e
->tab_char
),
709 leader_char(e
->leader_char
),
713 margin_character_flags(e
->margin_character_flags
),
714 margin_character_node(e
->margin_character_node
),
715 margin_character_distance(e
->margin_character_distance
),
717 number_text_separation(e
->number_text_separation
),
718 line_number_indent(e
->line_number_indent
),
719 line_number_multiple(e
->line_number_multiple
),
720 no_number_count(e
->no_number_count
),
721 hyphenation_flags(e
->hyphenation_flags
),
722 hyphen_line_count(0),
723 hyphen_line_max(e
->hyphen_line_max
),
724 hyphenation_space(e
->hyphenation_space
),
725 hyphenation_margin(e
->hyphenation_margin
),
729 widow_control(e
->widow_control
),
730 #endif /* WIDOW_CONTROL */
733 glyph_color(e
->glyph_color
),
734 prev_glyph_color(e
->prev_glyph_color
),
735 fill_color(e
->fill_color
),
736 prev_fill_color(e
->prev_fill_color
),
737 name(e
->name
), // so that eg `.if "\n[.ev]"0"' works
738 control_char(e
->control_char
),
739 no_break_control_char(e
->no_break_control_char
),
740 hyphen_indicator_char(e
->hyphen_indicator_char
)
744 void environment::copy(const environment
*e
)
746 prev_line_length
= e
->prev_line_length
;
747 line_length
= e
->line_length
;
748 prev_title_length
= e
->prev_title_length
;
749 title_length
= e
->title_length
;
750 prev_size
= e
->prev_size
;
752 prev_requested_size
= e
->prev_requested_size
;
753 requested_size
= e
->requested_size
;
754 char_height
= e
->char_height
;
755 char_slant
= e
->char_slant
;
756 space_size
= e
->space_size
;
757 sentence_space_size
= e
->sentence_space_size
;
758 adjust_mode
= e
->adjust_mode
;
761 prev_line_interrupted
= 0;
763 right_justify_lines
= 0;
764 prev_vertical_spacing
= e
->prev_vertical_spacing
;
765 vertical_spacing
= e
->vertical_spacing
;
766 prev_post_vertical_spacing
= e
->prev_post_vertical_spacing
,
767 post_vertical_spacing
= e
->post_vertical_spacing
,
768 prev_line_spacing
= e
->prev_line_spacing
;
769 line_spacing
= e
->line_spacing
;
770 prev_indent
= e
->prev_indent
;
772 have_temporary_indent
= 0;
773 temporary_indent
= 0;
775 underline_spaces
= 0;
776 input_trap_count
= 0;
777 continued_input_trap
= 0;
778 prev_text_length
= e
->prev_text_length
;
781 input_line_start
= 0;
782 control_char
= e
->control_char
;
783 no_break_control_char
= e
->no_break_control_char
;
784 hyphen_indicator_char
= e
->hyphen_indicator_char
;
790 line_tabs
= e
->line_tabs
;
791 current_tab
= TAB_NONE
;
793 margin_character_flags
= e
->margin_character_flags
;
794 margin_character_node
= e
->margin_character_node
;
795 margin_character_distance
= e
->margin_character_distance
;
797 number_text_separation
= e
->number_text_separation
;
798 line_number_multiple
= e
->line_number_multiple
;
799 line_number_indent
= e
->line_number_indent
;
800 no_number_count
= e
->no_number_count
;
801 tab_char
= e
->tab_char
;
802 leader_char
= e
->leader_char
;
803 hyphenation_flags
= e
->hyphenation_flags
;
805 prev_fontno
= e
->prev_fontno
;
808 prev_family
= e
->prev_family
;
811 widow_control
= e
->widow_control
;
812 #endif /* WIDOW_CONTROL */
813 hyphen_line_max
= e
->hyphen_line_max
;
814 hyphen_line_count
= 0;
815 hyphenation_space
= e
->hyphenation_space
;
816 hyphenation_margin
= e
->hyphenation_margin
;
818 ignore_next_eol
= e
->ignore_next_eol
;
819 emitted_node
= e
->emitted_node
;
820 glyph_color
= e
->glyph_color
;
821 prev_glyph_color
= e
->prev_glyph_color
;
822 fill_color
= e
->fill_color
;
823 prev_fill_color
= e
->prev_fill_color
;
826 environment::~environment()
829 delete_node_list(line
);
830 delete_node_list(numbering_nodes
);
833 hunits
environment::get_input_line_position()
837 n
= -input_line_start
;
839 n
= width_total
- input_line_start
;
845 void environment::set_input_line_position(hunits n
)
847 input_line_start
= line
== 0 ? -n
: width_total
- n
;
849 input_line_start
+= tab_width
;
852 hunits
environment::get_line_length()
857 hunits
environment::get_saved_line_length()
860 return target_text_length
+ saved_indent
;
865 vunits
environment::get_vertical_spacing()
867 return vertical_spacing
;
870 vunits
environment::get_post_vertical_spacing()
872 return post_vertical_spacing
;
875 int environment::get_line_spacing()
880 vunits
environment::total_post_vertical_spacing()
882 vunits
tem(post_vertical_spacing
);
883 if (line_spacing
> 1)
884 tem
+= (line_spacing
- 1)*vertical_spacing
;
888 int environment::get_bold()
890 return get_bold_fontno(fontno
);
893 hunits
environment::get_digit_width()
895 return env_digit_width(this);
898 int environment::get_adjust_mode()
903 int environment::get_fill()
908 hunits
environment::get_indent()
913 hunits
environment::get_saved_indent()
917 else if (have_temporary_indent
)
918 return temporary_indent
;
923 hunits
environment::get_temporary_indent()
925 return temporary_indent
;
928 hunits
environment::get_title_length()
933 node
*environment::get_prev_char()
935 for (node
*n
= current_tab
? tab_contents
: line
; n
; n
= n
->next
) {
936 node
*last
= n
->last_char_node();
943 hunits
environment::get_prev_char_width()
945 node
*last
= get_prev_char();
948 return last
->width();
951 hunits
environment::get_prev_char_skew()
953 node
*last
= get_prev_char();
959 vunits
environment::get_prev_char_height()
961 node
*last
= get_prev_char();
965 last
->vertical_extent(&min
, &max
);
969 vunits
environment::get_prev_char_depth()
971 node
*last
= get_prev_char();
975 last
->vertical_extent(&min
, &max
);
979 hunits
environment::get_text_length()
981 hunits n
= line
== 0 ? H0
: width_total
;
987 hunits
environment::get_prev_text_length()
989 return prev_text_length
;
993 static int sb_reg_contents
= 0;
994 static int st_reg_contents
= 0;
995 static int ct_reg_contents
= 0;
996 static int rsb_reg_contents
= 0;
997 static int rst_reg_contents
= 0;
998 static int skw_reg_contents
= 0;
999 static int ssc_reg_contents
= 0;
1001 void environment::width_registers()
1003 // this is used to implement \w; it sets the st, sb, ct registers
1004 vunits min
= 0, max
= 0, cur
= 0;
1005 int character_type
= 0;
1006 ssc_reg_contents
= line
? line
->subscript_correction().to_units() : 0;
1007 skw_reg_contents
= line
? line
->skew().to_units() : 0;
1008 line
= reverse_node_list(line
);
1009 vunits real_min
= V0
;
1010 vunits real_max
= V0
;
1012 for (node
*tem
= line
; tem
; tem
= tem
->next
) {
1013 tem
->vertical_extent(&v1
, &v2
);
1020 if ((cur
+= tem
->vertical_width()) < min
)
1024 character_type
|= tem
->character_type();
1026 line
= reverse_node_list(line
);
1027 st_reg_contents
= -min
.to_units();
1028 sb_reg_contents
= -max
.to_units();
1029 rst_reg_contents
= -real_min
.to_units();
1030 rsb_reg_contents
= -real_max
.to_units();
1031 ct_reg_contents
= character_type
;
1034 node
*environment::extract_output_line()
1043 /* environment related requests */
1045 void environment_switch()
1047 int pop
= 0; // 1 means pop, 2 means pop but no error message on underflow
1048 if (curenv
->is_dummy())
1049 error("can't switch environments when current environment is dummy");
1050 else if (!has_arg())
1054 if (!tok
.delimiter()) {
1055 // It looks like a number.
1057 if (get_integer(&n
)) {
1058 if (n
>= 0 && n
< NENVIRONMENTS
) {
1059 env_stack
= new env_list(curenv
, env_stack
);
1060 if (env_table
[n
] == 0)
1061 env_table
[n
] = new environment(i_to_a(n
));
1062 curenv
= env_table
[n
];
1071 nm
= get_long_name(1);
1075 if (!nm
.is_null()) {
1076 environment
*e
= (environment
*)env_dictionary
.lookup(nm
);
1078 e
= new environment(nm
);
1079 (void)env_dictionary
.lookup(nm
, e
);
1081 env_stack
= new env_list(curenv
, env_stack
);
1086 if (env_stack
== 0) {
1088 error("environment stack underflow");
1091 curenv
= env_stack
->env
;
1092 env_list
*tem
= env_stack
;
1093 env_stack
= env_stack
->next
;
1100 void environment_copy()
1105 if (!tok
.delimiter()) {
1106 // It looks like a number.
1108 if (get_integer(&n
)) {
1109 if (n
>= 0 && n
< NENVIRONMENTS
)
1116 nm
= get_long_name(1);
1117 if (!e
&& !nm
.is_null())
1118 e
= (environment
*)env_dictionary
.lookup(nm
);
1120 error("No environment to copy from");
1128 static symbol
P_symbol("P");
1132 symbol s
= get_name();
1134 if (s
.is_null() || s
== P_symbol
) {
1139 for (const char *p
= s
.contents(); p
!= 0 && *p
!= 0; p
++)
1146 curenv
->set_font(atoi(s
.contents()));
1148 curenv
->set_font(s
);
1152 void family_change()
1154 symbol s
= get_name();
1155 curenv
->set_family(s
);
1162 if (has_arg() && get_number(&n
, 'z', curenv
->get_requested_point_size())) {
1165 curenv
->set_size(n
);
1166 curenv
->add_html_tag(0, ".ps", n
);
1169 curenv
->set_size(0);
1173 void override_sizes()
1176 int *sizes
= new int[n
];
1178 char *buf
= read_string();
1181 char *p
= strtok(buf
, " \t");
1186 switch (sscanf(p
, "%d-%d", &lower
, &upper
)) {
1191 if (lower
<= upper
&& lower
>= 0)
1195 warning(WARN_RANGE
, "bad size range `%1'", p
);
1199 int *old_sizes
= sizes
;
1200 sizes
= new int[n
*2];
1201 memcpy(sizes
, old_sizes
, n
*sizeof(int));
1209 p
= strtok(0, " \t");
1211 font_size::init_size_table(sizes
);
1217 if (get_integer(&n
)) {
1218 curenv
->space_size
= n
;
1219 if (has_arg() && get_integer(&n
))
1220 curenv
->sentence_space_size
= n
;
1222 curenv
->sentence_space_size
= curenv
->space_size
;
1229 while (!tok
.newline() && !tok
.eof())
1234 curenv
->add_html_tag(1, ".fi");
1235 curenv
->add_html_tag(0, ".br");
1241 while (!tok
.newline() && !tok
.eof())
1246 curenv
->add_html_tag(1, ".nf");
1247 curenv
->add_html_tag(0, ".br");
1248 curenv
->add_html_tag(0, ".po", topdiv
->get_page_offset().to_units());
1255 if (!has_arg() || !get_integer(&n
))
1259 while (!tok
.newline() && !tok
.eof())
1263 curenv
->right_justify_lines
= 0;
1264 curenv
->center_lines
= n
;
1265 curenv
->add_html_tag(1, ".ce", n
);
1269 void right_justify()
1272 if (!has_arg() || !get_integer(&n
))
1276 while (!tok
.newline() && !tok
.eof())
1280 curenv
->center_lines
= 0;
1281 curenv
->right_justify_lines
= n
;
1282 curenv
->add_html_tag(1, ".rj", n
);
1289 if (has_arg() && get_hunits(&temp
, 'm', curenv
->line_length
)) {
1291 warning(WARN_RANGE
, "bad line length %1u", temp
.to_units());
1296 temp
= curenv
->prev_line_length
;
1297 curenv
->prev_line_length
= curenv
->line_length
;
1298 curenv
->line_length
= temp
;
1299 curenv
->add_html_tag(1, ".ll", temp
.to_units());
1306 if (has_arg() && get_hunits(&temp
, 'm', curenv
->title_length
)) {
1308 warning(WARN_RANGE
, "bad title length %1u", temp
.to_units());
1313 temp
= curenv
->prev_title_length
;
1314 curenv
->prev_title_length
= curenv
->title_length
;
1315 curenv
->title_length
= temp
;
1319 void vertical_spacing()
1322 if (has_arg() && get_vunits(&temp
, 'p', curenv
->vertical_spacing
)) {
1324 warning(WARN_RANGE
, "vertical spacing must not be negative");
1329 temp
= curenv
->prev_vertical_spacing
;
1330 curenv
->prev_vertical_spacing
= curenv
->vertical_spacing
;
1331 curenv
->vertical_spacing
= temp
;
1335 void post_vertical_spacing()
1338 if (has_arg() && get_vunits(&temp
, 'p', curenv
->post_vertical_spacing
)) {
1341 "post vertical spacing must be greater than or equal to 0");
1346 temp
= curenv
->prev_post_vertical_spacing
;
1347 curenv
->prev_post_vertical_spacing
= curenv
->post_vertical_spacing
;
1348 curenv
->post_vertical_spacing
= temp
;
1355 if (has_arg() && get_integer(&temp
)) {
1357 warning(WARN_RANGE
, "value %1 out of range: interpreted as 1", temp
);
1362 temp
= curenv
->prev_line_spacing
;
1363 curenv
->prev_line_spacing
= curenv
->line_spacing
;
1364 curenv
->line_spacing
= temp
;
1371 if (has_arg() && get_hunits(&temp
, 'm', curenv
->indent
)) {
1373 warning(WARN_RANGE
, "indent cannot be negative");
1378 temp
= curenv
->prev_indent
;
1379 while (!tok
.newline() && !tok
.eof())
1383 curenv
->have_temporary_indent
= 0;
1384 curenv
->prev_indent
= curenv
->indent
;
1385 curenv
->indent
= temp
;
1387 curenv
->add_html_tag(1, ".in", temp
.to_units());
1391 void temporary_indent()
1395 if (!get_hunits(&temp
, 'm', curenv
->get_indent()))
1397 while (!tok
.newline() && !tok
.eof())
1402 warning(WARN_RANGE
, "total indent cannot be negative");
1406 curenv
->temporary_indent
= temp
;
1407 curenv
->have_temporary_indent
= 1;
1408 curenv
->add_html_tag(1, ".ti", temp
.to_units());
1413 node
*do_underline_special(int underline_spaces
)
1416 m
.append_str("x u ");
1417 m
.append(underline_spaces
+ '0');
1418 return new special_node(m
, 1);
1421 void do_underline(int underline_spaces
)
1424 if (!has_arg() || !get_integer(&n
))
1427 if (curenv
->underline_lines
> 0) {
1428 curenv
->prev_fontno
= curenv
->fontno
;
1429 curenv
->fontno
= curenv
->pre_underline_fontno
;
1430 if (underline_spaces
) {
1431 curenv
->underline_spaces
= 0;
1432 curenv
->add_node(do_underline_special(0));
1435 curenv
->underline_lines
= 0;
1438 curenv
->underline_lines
= n
;
1439 curenv
->pre_underline_fontno
= curenv
->fontno
;
1440 curenv
->fontno
= get_underline_fontno();
1441 if (underline_spaces
) {
1442 curenv
->underline_spaces
= 1;
1443 curenv
->add_node(do_underline_special(1));
1449 void continuous_underline()
1461 curenv
->control_char
= '.';
1464 error("bad control character");
1466 curenv
->control_char
= tok
.ch();
1471 void no_break_control_char()
1473 curenv
->no_break_control_char
= '\'';
1476 error("bad control character");
1478 curenv
->no_break_control_char
= tok
.ch();
1483 void margin_character()
1487 charinfo
*ci
= tok
.get_char();
1489 // Call tok.next() only after making the node so that
1490 // .mc \s+9\(br\s0 works.
1491 node
*nd
= curenv
->make_char_node(ci
);
1494 delete curenv
->margin_character_node
;
1495 curenv
->margin_character_node
= nd
;
1496 curenv
->margin_character_flags
= (MARGIN_CHARACTER_ON
1497 |MARGIN_CHARACTER_NEXT
);
1499 if (has_arg() && get_hunits(&d
, 'm'))
1500 curenv
->margin_character_distance
= d
;
1504 check_missing_character();
1505 curenv
->margin_character_flags
&= ~MARGIN_CHARACTER_ON
;
1506 if (curenv
->margin_character_flags
== 0) {
1507 delete curenv
->margin_character_node
;
1508 curenv
->margin_character_node
= 0;
1516 delete_node_list(curenv
->numbering_nodes
);
1517 curenv
->numbering_nodes
= 0;
1520 for (int i
= '9'; i
>= '0'; i
--) {
1521 node
*tem
= make_node(charset_table
[i
], curenv
);
1529 curenv
->numbering_nodes
= nd
;
1530 curenv
->line_number_digit_width
= env_digit_width(curenv
);
1532 if (!tok
.delimiter()) {
1533 if (get_integer(&n
, next_line_number
)) {
1534 next_line_number
= n
;
1535 if (next_line_number
< 0) {
1536 warning(WARN_RANGE
, "negative line number");
1537 next_line_number
= 0;
1542 while (!tok
.space() && !tok
.newline() && !tok
.eof())
1545 if (!tok
.delimiter()) {
1546 if (get_integer(&n
)) {
1548 warning(WARN_RANGE
, "negative or zero line number multiple");
1551 curenv
->line_number_multiple
= n
;
1555 while (!tok
.space() && !tok
.newline() && !tok
.eof())
1558 if (!tok
.delimiter()) {
1559 if (get_integer(&n
))
1560 curenv
->number_text_separation
= n
;
1563 while (!tok
.space() && !tok
.newline() && !tok
.eof())
1565 if (has_arg() && !tok
.delimiter() && get_integer(&n
))
1566 curenv
->line_number_indent
= n
;
1576 if (has_arg() && get_integer(&n
))
1577 curenv
->no_number_count
= n
> 0 ? n
: 0;
1579 curenv
->no_number_count
= 1;
1585 curenv
->hyphenation_flags
= 0;
1589 void hyphenate_request()
1592 if (has_arg() && get_integer(&n
))
1593 curenv
->hyphenation_flags
= n
;
1595 curenv
->hyphenation_flags
= 1;
1601 curenv
->hyphen_indicator_char
= get_optional_char();
1605 void hyphen_line_max_request()
1608 if (has_arg() && get_integer(&n
))
1609 curenv
->hyphen_line_max
= n
;
1611 curenv
->hyphen_line_max
= -1;
1615 void environment::interrupt()
1618 add_node(new transparent_dummy_node
);
1623 void environment::newline()
1625 if (underline_lines
> 0) {
1626 if (--underline_lines
== 0) {
1627 prev_fontno
= fontno
;
1628 fontno
= pre_underline_fontno
;
1629 if (underline_spaces
) {
1630 underline_spaces
= 0;
1631 add_node(do_underline_special(0));
1639 // strip trailing spaces
1640 while (line
!= 0 && line
->discardable()) {
1641 width_total
-= line
->width();
1642 space_total
-= line
->nspaces();
1647 node
*to_be_output
= 0;
1648 hunits to_be_output_width
;
1649 prev_line_interrupted
= 0;
1652 else if (interrupted
) {
1654 // see environment::final_break
1655 prev_line_interrupted
= exit_started
? 2 : 1;
1657 else if (center_lines
> 0) {
1659 hunits x
= target_text_length
- width_total
;
1661 saved_indent
+= x
/2;
1662 to_be_output
= line
;
1664 node
*n
= make_html_tag("eol.ce");
1665 n
->next
= to_be_output
;
1668 to_be_output_width
= width_total
;
1671 else if (right_justify_lines
> 0) {
1672 --right_justify_lines
;
1673 hunits x
= target_text_length
- width_total
;
1676 to_be_output
= line
;
1677 to_be_output_width
= width_total
;
1683 to_be_output
= line
;
1684 to_be_output_width
= width_total
;
1687 input_line_start
= line
== 0 ? H0
: width_total
;
1689 if (is_html
&& !fill
) {
1690 if (curdiv
== topdiv
) {
1691 node
*n
= make_html_tag("eol");
1693 n
->next
= to_be_output
;
1697 output_line(to_be_output
, to_be_output_width
);
1698 hyphen_line_count
= 0;
1700 if (input_trap_count
> 0) {
1701 if (!(continued_input_trap
&& prev_line_interrupted
))
1702 if (--input_trap_count
== 0)
1703 spring_trap(input_trap
);
1707 void environment::output_line(node
*n
, hunits width
)
1709 prev_text_length
= width
;
1710 if (margin_character_flags
) {
1711 hunits d
= line_length
+ margin_character_distance
- saved_indent
- width
;
1713 n
= new hmotion_node(d
, get_fill_color(), n
);
1716 margin_character_flags
&= ~MARGIN_CHARACTER_NEXT
;
1718 if (!margin_character_flags
) {
1719 tem
= margin_character_node
;
1720 margin_character_node
= 0;
1723 tem
= margin_character_node
->copy();
1726 width
+= tem
->width();
1730 node
*tem
= n
->next
;
1735 if (!saved_indent
.is_zero())
1736 nn
= new hmotion_node(saved_indent
, get_fill_color(), nn
);
1737 width
+= saved_indent
;
1738 if (no_number_count
> 0)
1740 else if (numbering_nodes
) {
1741 hunits w
= (line_number_digit_width
1742 *(3+line_number_indent
+number_text_separation
));
1743 if (next_line_number
% line_number_multiple
!= 0)
1744 nn
= new hmotion_node(w
, get_fill_color(), nn
);
1747 nn
= new hmotion_node(number_text_separation
* line_number_digit_width
,
1748 get_fill_color(), nn
);
1749 x
-= number_text_separation
*line_number_digit_width
;
1751 sprintf(buf
, "%3d", next_line_number
);
1752 for (char *p
= strchr(buf
, '\0') - 1; p
>= buf
&& *p
!= ' '; --p
) {
1753 node
*gn
= numbering_nodes
;
1754 for (int count
= *p
- '0'; count
> 0; count
--)
1761 nn
= new hmotion_node(x
, get_fill_color(), nn
);
1766 output(nn
, !fill
, vertical_spacing
, total_post_vertical_spacing(), width
);
1769 void environment::start_line()
1773 line
= new line_start_node
;
1774 if (have_temporary_indent
) {
1775 saved_indent
= temporary_indent
;
1776 have_temporary_indent
= 0;
1779 saved_indent
= indent
;
1780 target_text_length
= line_length
- saved_indent
;
1785 hunits
environment::get_hyphenation_space()
1787 return hyphenation_space
;
1790 void hyphenation_space_request()
1793 if (get_hunits(&n
, 'm')) {
1795 warning(WARN_RANGE
, "hyphenation space cannot be negative");
1798 curenv
->hyphenation_space
= n
;
1803 hunits
environment::get_hyphenation_margin()
1805 return hyphenation_margin
;
1808 void hyphenation_margin_request()
1811 if (get_hunits(&n
, 'm')) {
1813 warning(WARN_RANGE
, "hyphenation margin cannot be negative");
1816 curenv
->hyphenation_margin
= n
;
1821 breakpoint
*environment::choose_breakpoint()
1823 hunits x
= width_total
;
1824 int s
= space_total
;
1826 breakpoint
*best_bp
= 0; // the best breakpoint so far
1827 int best_bp_fits
= 0;
1831 breakpoint
*bp
= n
->get_breakpoints(x
, s
);
1833 if (bp
->width
<= target_text_length
) {
1834 if (!bp
->hyphenated
) {
1835 breakpoint
*tem
= bp
->next
;
1838 breakpoint
*tem1
= tem
;
1843 // Decide whether to use the hyphenated breakpoint.
1844 && (hyphen_line_max
< 0
1845 // Only choose the hyphenated breakpoint if it would not
1846 // exceed the maximum number of consecutive hyphenated
1848 || hyphen_line_count
+ 1 <= hyphen_line_max
)
1849 && !(adjust_mode
== ADJUST_BOTH
1850 // Don't choose the hyphenated breakpoint if the line
1851 // can be justified by adding no more than
1852 // hyphenation_space to any word space.
1854 && (((target_text_length
- bp
->width
1855 + (bp
->nspaces
- 1)*hresolution
)/bp
->nspaces
)
1856 <= hyphenation_space
))
1857 // Don't choose the hyphenated breakpoint if the line
1858 // is no more than hyphenation_margin short.
1859 : target_text_length
- bp
->width
<= hyphenation_margin
)) {
1868 if ((adjust_mode
== ADJUST_BOTH
1869 ? hyphenation_space
== H0
1870 : hyphenation_margin
== H0
)
1871 && (hyphen_line_max
< 0
1872 || hyphen_line_count
+ 1 <= hyphen_line_max
)) {
1873 // No need to consider a non-hyphenated breakpoint.
1876 breakpoint
*tem
= bp
->next
;
1879 breakpoint
*tem1
= tem
;
1885 // It fits but it's hyphenated.
1886 if (!best_bp_fits
) {
1894 breakpoint
*tem
= bp
;
1911 output_warning(WARN_BREAK
, "can't break line");
1917 void environment::hyphenate_line(int start_here
)
1920 hyphenation_type prev_type
= line
->get_hyphenation_type();
1925 for (startp
= &line
->next
; *startp
!= 0; startp
= &(*startp
)->next
) {
1926 hyphenation_type this_type
= (*startp
)->get_hyphenation_type();
1927 if (prev_type
== HYPHEN_BOUNDARY
&& this_type
== HYPHEN_MIDDLE
)
1929 prev_type
= this_type
;
1933 node
*tem
= *startp
;
1936 } while (tem
!= 0 && tem
->get_hyphenation_type() == HYPHEN_MIDDLE
);
1937 int inhibit
= (tem
!= 0 && tem
->get_hyphenation_type() == HYPHEN_INHIBIT
);
1939 hyphen_list
*sl
= 0;
1943 while (tem
!= end
) {
1944 sl
= tem
->get_hyphen_list(sl
, &i
);
1947 tem1
->next
= forward
;
1951 // this is for characters like hyphen and emdash
1953 for (hyphen_list
*h
= sl
; h
; h
= h
->next
) {
1954 h
->breakable
= (prev_code
!= 0
1956 && h
->next
->hyphenation_code
!= 0);
1957 prev_code
= h
->hyphenation_code
;
1960 if (hyphenation_flags
!= 0
1962 // this may not be right if we have extra space on this line
1963 && !((hyphenation_flags
& HYPHEN_LAST_LINE
)
1964 && (curdiv
->distance_to_next_trap()
1965 <= vertical_spacing
+ total_post_vertical_spacing()))
1967 hyphenate(sl
, hyphenation_flags
);
1968 while (forward
!= 0) {
1969 node
*tem1
= forward
;
1970 forward
= forward
->next
;
1972 tem
= tem1
->add_self(tem
, &sl
);
1977 static node
*node_list_reverse(node
*n
)
1989 static void distribute_space(node
*n
, int nspaces
, hunits desired_space
,
1990 int force_reverse
= 0)
1992 static int reverse
= 0;
1993 if (force_reverse
|| reverse
)
1994 n
= node_list_reverse(n
);
1995 if (!force_reverse
&& nspaces
> 0 && spread_limit
>= 0
1996 && desired_space
.to_units() > 0) {
1997 hunits em
= curenv
->get_size();
1998 double Ems
= (double)desired_space
.to_units() / nspaces
1999 / (em
.is_zero() ? hresolution
: em
.to_units());
2000 if (Ems
> spread_limit
)
2001 output_warning(WARN_BREAK
, "spreading %1m per space", Ems
);
2003 for (node
*tem
= n
; tem
; tem
= tem
->next
)
2004 tem
->spread_space(&nspaces
, &desired_space
);
2005 if (force_reverse
|| reverse
)
2006 (void)node_list_reverse(n
);
2009 assert(desired_space
.is_zero() && nspaces
== 0);
2012 void environment::possibly_break_line(int start_here
, int forced
)
2014 if (!fill
|| current_tab
|| current_field
|| dummy
)
2018 // When a macro follows a paragraph in fill mode, the
2019 // current line should not be empty.
2020 || (width_total
- line
->width()) > target_text_length
)) {
2021 hyphenate_line(start_here
);
2022 breakpoint
*bp
= choose_breakpoint();
2024 // we'll find one eventually
2028 while (*ndp
!= bp
->nd
)
2029 ndp
= &(*ndp
)->next
;
2030 bp
->nd
->split(bp
->index
, &pre
, &post
);
2032 hunits extra_space_width
= H0
;
2033 switch(adjust_mode
) {
2035 if (bp
->nspaces
!= 0)
2036 extra_space_width
= target_text_length
- bp
->width
;
2037 else if (bp
->width
> 0 && target_text_length
> 0
2038 && target_text_length
> bp
->width
)
2039 output_warning(WARN_BREAK
, "cannot adjust line");
2042 saved_indent
+= (target_text_length
- bp
->width
)/2;
2045 saved_indent
+= target_text_length
- bp
->width
;
2048 distribute_space(pre
, bp
->nspaces
, extra_space_width
);
2049 hunits output_width
= bp
->width
+ extra_space_width
;
2050 input_line_start
-= output_width
;
2052 hyphen_line_count
++;
2054 hyphen_line_count
= 0;
2058 node
*first_non_discardable
= 0;
2060 for (tem
= line
; tem
!= 0; tem
= tem
->next
)
2061 if (!tem
->discardable())
2062 first_non_discardable
= tem
;
2063 node
*to_be_discarded
;
2064 if (first_non_discardable
) {
2065 to_be_discarded
= first_non_discardable
->next
;
2066 first_non_discardable
->next
= 0;
2067 for (tem
= line
; tem
!= 0; tem
= tem
->next
) {
2068 width_total
+= tem
->width();
2069 space_total
+= tem
->nspaces();
2075 to_be_discarded
= line
;
2078 // Do output_line() here so that line will be 0 iff the
2079 // the environment will be empty.
2080 output_line(pre
, output_width
);
2081 while (to_be_discarded
!= 0) {
2082 tem
= to_be_discarded
;
2083 to_be_discarded
= to_be_discarded
->next
;
2084 input_line_start
-= tem
->width();
2088 if (have_temporary_indent
) {
2089 saved_indent
= temporary_indent
;
2090 have_temporary_indent
= 0;
2093 saved_indent
= indent
;
2094 target_text_length
= line_length
- saved_indent
;
2100 Do the break at the end of input after the end macro (if any).
2102 Unix troff behaves as follows: if the last line is
2106 it will output foo on the current page, and bar on the next page;
2115 everything will be output on the current page. This behaviour must be
2118 The problem is that some macro packages rely on this. For example,
2119 the ATK macros have an end macro that emits \c if it needs to print a
2120 table of contents but doesn't do a 'bp in the end macro; instead the
2121 'bp is done in the bottom of page trap. This works with Unix troff,
2122 provided that the current environment is not empty at the end of the
2125 The following will make macro packages that do that sort of thing work
2126 even if the current environment is empty at the end of the input file.
2127 If the last input line used \c and this line occurred in the end macro,
2128 then we'll force everything out on the current page, but we'll make
2129 sure that the environment isn't empty so that we won't exit at the
2130 bottom of this page.
2133 void environment::final_break()
2135 if (prev_line_interrupted
== 2) {
2137 add_node(new transparent_dummy_node
);
2144 * add_html_tag - emits a special html-tag: to help post-grohtml understand
2145 * the key troff commands
2148 void environment::add_html_tag(int force
, const char *name
)
2150 if (!force
&& (curdiv
!= topdiv
))
2155 * need to emit tag for post-grohtml
2156 * but we check to see whether we can emit specials
2158 if (curdiv
== topdiv
&& topdiv
->before_first_page
)
2159 topdiv
->begin_page();
2160 macro
*m
= new macro
;
2161 m
->append_str("html-tag:");
2162 for (const char *p
= name
; *p
; p
++)
2163 if (!invalid_input_char((unsigned char)*p
))
2165 curdiv
->output(new special_node(*m
), 1, 0, 0, 0);
2166 if (strcmp(name
, ".nf") == 0)
2167 curenv
->ignore_next_eol
= 1;
2172 * add_html_tag - emits a special html-tag: to help post-grohtml understand
2173 * the key troff commands, it appends a string representation
2177 void environment::add_html_tag(int force
, const char *name
, int i
)
2179 if (!force
&& (curdiv
!= topdiv
))
2184 * need to emit tag for post-grohtml
2185 * but we check to see whether we can emit specials
2187 if (curdiv
== topdiv
&& topdiv
->before_first_page
)
2188 topdiv
->begin_page();
2189 macro
*m
= new macro
;
2190 m
->append_str("html-tag:");
2191 for (const char *p
= name
; *p
; p
++)
2192 if (!invalid_input_char((unsigned char)*p
))
2196 node
*n
= new special_node(*m
);
2197 curdiv
->output(n
, 1, 0, 0, 0);
2202 * add_html_tag_tabs - emits the tab settings for post-grohtml
2205 void environment::add_html_tag_tabs(int force
)
2207 if (!force
&& (curdiv
!= topdiv
))
2212 * need to emit tag for post-grohtml
2213 * but we check to see whether we can emit specials
2215 if (curdiv
== topdiv
&& topdiv
->before_first_page
)
2216 topdiv
->begin_page();
2217 macro
*m
= new macro
;
2220 m
->append_str("html-tag:.ta ");
2222 t
= curenv
->tabs
.distance_to_next_tab(l
, &d
);
2226 m
->append_str(" L ");
2227 m
->append_int(l
.to_units());
2230 m
->append_str(" C ");
2231 m
->append_int(l
.to_units());
2234 m
->append_str(" R ");
2235 m
->append_int(l
.to_units());
2240 } while ((t
!= TAB_NONE
) && (l
< get_line_length()));
2241 curdiv
->output(new special_node(*m
), 1, 0, 0, 0);
2245 node
*environment::make_html_tag(const char *name
, int i
)
2249 * need to emit tag for post-grohtml
2250 * but we check to see whether we can emit specials
2252 if (curdiv
== topdiv
&& topdiv
->before_first_page
)
2253 topdiv
->begin_page();
2254 macro
*m
= new macro
;
2255 m
->append_str("html-tag:");
2256 for (const char *p
= name
; *p
; p
++)
2257 if (!invalid_input_char((unsigned char)*p
))
2261 return new special_node(*m
);
2266 node
*environment::make_html_tag(const char *name
)
2270 * need to emit tag for post-grohtml
2271 * but we check to see whether we can emit specials
2273 if (curdiv
== topdiv
&& topdiv
->before_first_page
)
2274 topdiv
->begin_page();
2275 macro
*m
= new macro
;
2276 m
->append_str("html-tag:");
2277 for (const char *p
= name
; *p
; p
++)
2278 if (!invalid_input_char((unsigned char)*p
))
2280 return new special_node(*m
);
2285 void environment::do_break(int spread
)
2287 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
2288 topdiv
->begin_page();
2294 // this is so that hyphenation works
2295 line
= new space_node(H0
, get_fill_color(), line
);
2297 possibly_break_line(0, spread
);
2299 while (line
!= 0 && line
->discardable()) {
2300 width_total
-= line
->width();
2301 space_total
-= line
->nspaces();
2307 input_line_start
= H0
;
2310 switch (adjust_mode
) {
2312 saved_indent
+= (target_text_length
- width_total
)/2;
2315 saved_indent
+= target_text_length
- width_total
;
2321 output_line(tem
, width_total
);
2322 hyphen_line_count
= 0;
2324 prev_line_interrupted
= 0;
2325 #ifdef WIDOW_CONTROL
2327 output_pending_lines();
2328 #endif /* WIDOW_CONTROL */
2331 int environment::is_empty()
2333 return !current_tab
&& line
== 0 && pending_lines
== 0;
2336 void do_break_request(int spread
)
2338 while (!tok
.newline() && !tok
.eof())
2341 curenv
->do_break(spread
);
2342 curenv
->add_html_tag(0, ".br");
2347 void break_request()
2349 do_break_request(0);
2352 void break_spread_request()
2354 do_break_request(1);
2359 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
2360 handle_initial_title();
2364 hunits part_width
[3];
2365 part
[0] = part
[1] = part
[2] = 0;
2366 environment
env(curenv
);
2367 environment
*oldenv
= curenv
;
2369 read_title_parts(part
, part_width
);
2371 curenv
->size
= env
.size
;
2372 curenv
->prev_size
= env
.prev_size
;
2373 curenv
->requested_size
= env
.requested_size
;
2374 curenv
->prev_requested_size
= env
.prev_requested_size
;
2375 curenv
->char_height
= env
.char_height
;
2376 curenv
->char_slant
= env
.char_slant
;
2377 curenv
->fontno
= env
.fontno
;
2378 curenv
->prev_fontno
= env
.prev_fontno
;
2379 curenv
->glyph_color
= env
.glyph_color
;
2380 curenv
->prev_glyph_color
= env
.prev_glyph_color
;
2381 curenv
->fill_color
= env
.fill_color
;
2382 curenv
->prev_fill_color
= env
.prev_fill_color
;
2391 hunits
title_length(curenv
->title_length
);
2392 hunits f
= title_length
- part_width
[1];
2394 n
= new hmotion_node(f2
- part_width
[2], curenv
->get_fill_color(), n
);
2402 n
= new hmotion_node(f
- f2
- part_width
[0], curenv
->get_fill_color(), n
);
2410 curenv
->output_title(n
, !curenv
->fill
, curenv
->vertical_spacing
,
2411 curenv
->total_post_vertical_spacing(), title_length
);
2412 curenv
->hyphen_line_count
= 0;
2418 curenv
->adjust_mode
|= 1;
2422 curenv
->adjust_mode
= ADJUST_LEFT
;
2425 curenv
->adjust_mode
= ADJUST_RIGHT
;
2428 curenv
->adjust_mode
= ADJUST_CENTER
;
2432 curenv
->adjust_mode
= ADJUST_BOTH
;
2436 if (get_integer(&n
)) {
2438 warning(WARN_RANGE
, "negative adjustment mode");
2440 curenv
->adjust_mode
= 5;
2441 warning(WARN_RANGE
, "adjustment mode `%1' out of range", n
);
2444 curenv
->adjust_mode
= n
;
2453 curenv
->adjust_mode
&= ~1;
2457 void do_input_trap(int continued
)
2459 curenv
->input_trap_count
= 0;
2461 curenv
->continued_input_trap
= 1;
2463 if (has_arg() && get_integer(&n
)) {
2466 "number of lines for input trap must be greater than zero");
2468 symbol s
= get_name(1);
2470 curenv
->input_trap_count
= n
;
2471 curenv
->input_trap
= s
;
2483 void input_trap_continued()
2490 // must not be R or C or L or a legitimate part of a number expression
2491 const char TAB_REPEAT_CHAR
= 'T';
2497 tab(hunits
, tab_type
);
2498 enum { BLOCK
= 1024 };
2499 static tab
*free_list
;
2500 void *operator new(size_t);
2501 void operator delete(void *);
2504 tab
*tab::free_list
= 0;
2506 void *tab::operator new(size_t n
)
2508 assert(n
== sizeof(tab
));
2510 free_list
= (tab
*)new char[sizeof(tab
)*BLOCK
];
2511 for (int i
= 0; i
< BLOCK
- 1; i
++)
2512 free_list
[i
].next
= free_list
+ i
+ 1;
2513 free_list
[BLOCK
-1].next
= 0;
2516 free_list
= (tab
*)(free_list
->next
);
2522 /* cfront can't cope with this. */
2525 void tab::operator delete(void *p
)
2528 ((tab
*)p
)->next
= free_list
;
2529 free_list
= (tab
*)p
;
2533 tab::tab(hunits x
, tab_type t
) : next(0), pos(x
), type(t
)
2537 tab_stops::tab_stops(hunits distance
, tab_type type
)
2540 repeated_list
= new tab(distance
, type
);
2543 tab_stops::~tab_stops()
2548 tab_type
tab_stops::distance_to_next_tab(hunits curpos
, hunits
*distance
)
2552 return distance_to_next_tab(curpos
, distance
, &nextpos
);
2555 tab_type
tab_stops::distance_to_next_tab(hunits curpos
, hunits
*distance
,
2560 for (tem
= initial_list
; tem
&& tem
->pos
<= curpos
; tem
= tem
->next
)
2563 *distance
= tem
->pos
- curpos
;
2564 *nextpos
= tem
->pos
;
2567 if (repeated_list
== 0)
2569 hunits base
= lastpos
;
2571 for (tem
= repeated_list
; tem
&& tem
->pos
+ base
<= curpos
; tem
= tem
->next
)
2574 *distance
= tem
->pos
+ base
- curpos
;
2575 *nextpos
= tem
->pos
+ base
;
2578 assert(lastpos
> 0);
2584 const char *tab_stops::to_string()
2586 static char *buf
= 0;
2587 static int buf_size
= 0;
2588 // figure out a maximum on the amount of space we can need
2591 for (p
= initial_list
; p
; p
= p
->next
)
2593 for (p
= repeated_list
; p
; p
= p
->next
)
2595 // (10 for digits + 1 for u + 1 for 'C' or 'R') + 2 for ' &' + 1 for '\0'
2596 int need
= count
*12 + 3;
2597 if (buf
== 0 || need
> buf_size
) {
2601 buf
= new char[buf_size
];
2604 for (p
= initial_list
; p
; p
= p
->next
) {
2605 strcpy(ptr
, i_to_a(p
->pos
.to_units()));
2606 ptr
= strchr(ptr
, '\0');
2624 *ptr
++ = TAB_REPEAT_CHAR
;
2625 for (p
= repeated_list
; p
; p
= p
->next
) {
2626 strcpy(ptr
, i_to_a(p
->pos
.to_units()));
2627 ptr
= strchr(ptr
, '\0');
2648 tab_stops::tab_stops() : initial_list(0), repeated_list(0)
2652 tab_stops::tab_stops(const tab_stops
&ts
)
2653 : initial_list(0), repeated_list(0)
2655 tab
**p
= &initial_list
;
2656 tab
*t
= ts
.initial_list
;
2658 *p
= new tab(t
->pos
, t
->type
);
2663 t
= ts
.repeated_list
;
2665 *p
= new tab(t
->pos
, t
->type
);
2671 void tab_stops::clear()
2673 while (initial_list
) {
2674 tab
*tem
= initial_list
;
2675 initial_list
= initial_list
->next
;
2678 while (repeated_list
) {
2679 tab
*tem
= repeated_list
;
2680 repeated_list
= repeated_list
->next
;
2685 void tab_stops::add_tab(hunits pos
, tab_type type
, int repeated
)
2688 for (p
= repeated
? &repeated_list
: &initial_list
; *p
; p
= &(*p
)->next
)
2690 *p
= new tab(pos
, type
);
2694 void tab_stops::operator=(const tab_stops
&ts
)
2697 tab
**p
= &initial_list
;
2698 tab
*t
= ts
.initial_list
;
2700 *p
= new tab(t
->pos
, t
->type
);
2705 t
= ts
.repeated_list
;
2707 *p
= new tab(t
->pos
, t
->type
);
2716 hunits prev_pos
= 0;
2721 if (tok
.ch() == TAB_REPEAT_CHAR
) {
2726 if (!get_hunits(&pos
, 'm', prev_pos
))
2728 tab_type type
= TAB_LEFT
;
2729 if (tok
.ch() == 'C') {
2733 else if (tok
.ch() == 'R') {
2737 else if (tok
.ch() == 'L') {
2740 if (pos
<= prev_pos
&& !first
)
2742 "positions of tab stops must be strictly increasing");
2744 tabs
.add_tab(pos
, type
, repeated
);
2749 curenv
->tabs
= tabs
;
2750 curenv
->add_html_tag_tabs(1);
2754 const char *environment::get_tabs()
2756 return tabs
.to_string();
2759 tab_type
environment::distance_to_next_tab(hunits
*distance
)
2762 ? curenv
->tabs
.distance_to_next_tab(get_text_length(), distance
)
2763 : curenv
->tabs
.distance_to_next_tab(get_input_line_position(), distance
);
2766 tab_type
environment::distance_to_next_tab(hunits
*distance
, hunits
*leftpos
)
2769 ? curenv
->tabs
.distance_to_next_tab(get_text_length(), distance
, leftpos
)
2770 : curenv
->tabs
.distance_to_next_tab(get_input_line_position(), distance
,
2774 void field_characters()
2776 field_delimiter_char
= get_optional_char();
2777 if (field_delimiter_char
)
2778 padding_indicator_char
= get_optional_char();
2780 padding_indicator_char
= 0;
2784 void line_tabs_request()
2787 if (has_arg() && get_integer(&n
))
2788 curenv
->line_tabs
= n
!= 0;
2790 curenv
->line_tabs
= 1;
2794 int environment::get_line_tabs()
2799 void environment::wrap_up_tab()
2806 switch (current_tab
) {
2808 tab_amount
= tab_distance
- tab_width
;
2809 line
= make_tab_node(tab_amount
, line
);
2812 tab_amount
= tab_distance
- tab_width
/2;
2813 line
= make_tab_node(tab_amount
, line
);
2820 width_total
+= tab_amount
;
2821 width_total
+= tab_width
;
2822 if (current_field
) {
2823 if (tab_precedes_field
) {
2824 pre_field_width
+= tab_amount
;
2825 tab_precedes_field
= 0;
2827 field_distance
-= tab_amount
;
2828 field_spaces
+= tab_field_spaces
;
2830 if (tab_contents
!= 0) {
2832 for (tem
= tab_contents
; tem
->next
!= 0; tem
= tem
->next
)
2835 line
= tab_contents
;
2837 tab_field_spaces
= 0;
2841 current_tab
= TAB_NONE
;
2844 node
*environment::make_tab_node(hunits d
, node
*next
)
2846 if (leader_node
!= 0 && d
< 0) {
2847 error("motion generated by leader cannot be negative");
2852 return new hmotion_node(d
, 1, 0, get_fill_color(), next
);
2853 node
*n
= new hline_node(d
, leader_node
, next
);
2858 void environment::handle_tab(int is_leader
)
2864 charinfo
*ci
= is_leader
? leader_char
: tab_char
;
2866 leader_node
= ci
? make_char_node(ci
) : 0;
2867 tab_type t
= distance_to_next_tab(&d
, &abs
);
2872 add_node(make_tab_node(d
));
2873 add_node(make_html_tag("tab L", abs
.to_units()));
2876 add_node(make_html_tag("tab R", abs
.to_units()));
2879 add_node(make_html_tag("tab C", abs
.to_units()));
2888 tab_field_spaces
= 0;
2891 void environment::start_field()
2893 assert(!current_field
);
2895 if (distance_to_next_tab(&d
) != TAB_NONE
) {
2896 pre_field_width
= get_text_length();
2900 tab_field_spaces
= 0;
2901 for (node
*p
= line
; p
; p
= p
->next
)
2906 tab_precedes_field
= current_tab
!= TAB_NONE
;
2909 error("zero field width");
2912 void environment::wrap_up_field()
2914 if (!current_tab
&& field_spaces
== 0)
2916 hunits padding
= field_distance
- (get_text_length() - pre_field_width
);
2917 if (current_tab
&& tab_field_spaces
!= 0) {
2918 hunits tab_padding
= scale(padding
,
2920 field_spaces
+ tab_field_spaces
);
2921 padding
-= tab_padding
;
2922 distribute_space(tab_contents
, tab_field_spaces
, tab_padding
, 1);
2923 tab_field_spaces
= 0;
2924 tab_width
+= tab_padding
;
2926 if (field_spaces
!= 0) {
2927 distribute_space(line
, field_spaces
, padding
, 1);
2928 width_total
+= padding
;
2930 // the start of the tab has been moved to the right by padding, so
2931 tab_distance
-= padding
;
2932 if (tab_distance
<= H0
) {
2933 // use the next tab stop instead
2934 current_tab
= tabs
.distance_to_next_tab(get_input_line_position()
2937 if (current_tab
== TAB_NONE
|| current_tab
== TAB_LEFT
) {
2938 width_total
+= tab_width
;
2939 if (current_tab
== TAB_LEFT
) {
2940 line
= make_tab_node(tab_distance
, line
);
2941 width_total
+= tab_distance
;
2942 current_tab
= TAB_NONE
;
2944 if (tab_contents
!= 0) {
2946 for (tem
= tab_contents
; tem
->next
!= 0; tem
= tem
->next
)
2949 line
= tab_contents
;
2961 void environment::add_padding()
2964 tab_contents
= new space_node(H0
, get_fill_color(), tab_contents
);
2970 line
= new space_node(H0
, get_fill_color(), line
);
2975 typedef int (environment::*INT_FUNCP
)();
2976 typedef vunits (environment::*VUNITS_FUNCP
)();
2977 typedef hunits (environment::*HUNITS_FUNCP
)();
2978 typedef const char *(environment::*STRING_FUNCP
)();
2980 class int_env_reg
: public reg
{
2983 int_env_reg(INT_FUNCP
);
2984 const char *get_string();
2985 int get_value(units
*val
);
2988 class vunits_env_reg
: public reg
{
2991 vunits_env_reg(VUNITS_FUNCP f
);
2992 const char *get_string();
2993 int get_value(units
*val
);
2997 class hunits_env_reg
: public reg
{
3000 hunits_env_reg(HUNITS_FUNCP f
);
3001 const char *get_string();
3002 int get_value(units
*val
);
3005 class string_env_reg
: public reg
{
3008 string_env_reg(STRING_FUNCP
);
3009 const char *get_string();
3012 int_env_reg::int_env_reg(INT_FUNCP f
) : func(f
)
3016 int int_env_reg::get_value(units
*val
)
3018 *val
= (curenv
->*func
)();
3022 const char *int_env_reg::get_string()
3024 return i_to_a((curenv
->*func
)());
3027 vunits_env_reg::vunits_env_reg(VUNITS_FUNCP f
) : func(f
)
3031 int vunits_env_reg::get_value(units
*val
)
3033 *val
= (curenv
->*func
)().to_units();
3037 const char *vunits_env_reg::get_string()
3039 return i_to_a((curenv
->*func
)().to_units());
3042 hunits_env_reg::hunits_env_reg(HUNITS_FUNCP f
) : func(f
)
3046 int hunits_env_reg::get_value(units
*val
)
3048 *val
= (curenv
->*func
)().to_units();
3052 const char *hunits_env_reg::get_string()
3054 return i_to_a((curenv
->*func
)().to_units());
3057 string_env_reg::string_env_reg(STRING_FUNCP f
) : func(f
)
3061 const char *string_env_reg::get_string()
3063 return (curenv
->*func
)();
3066 class horizontal_place_reg
: public general_reg
{
3068 horizontal_place_reg();
3069 int get_value(units
*);
3070 void set_value(units
);
3073 horizontal_place_reg::horizontal_place_reg()
3077 int horizontal_place_reg::get_value(units
*res
)
3079 *res
= curenv
->get_input_line_position().to_units();
3083 void horizontal_place_reg::set_value(units n
)
3085 curenv
->set_input_line_position(hunits(n
));
3088 const char *environment::get_font_family_string()
3090 return family
->nm
.contents();
3093 const char *environment::get_glyph_color_string()
3095 return glyph_color
->nm
.contents();
3098 const char *environment::get_fill_color_string()
3100 return fill_color
->nm
.contents();
3103 const char *environment::get_font_name_string()
3105 symbol f
= get_font_name(fontno
, this);
3106 return f
.contents();
3109 const char *environment::get_name_string()
3111 return name
.contents();
3114 // Convert a quantity in scaled points to ascii decimal fraction.
3116 const char *sptoa(int sp
)
3119 assert(sizescale
> 0);
3122 if (sp
% sizescale
== 0)
3123 return i_to_a(sp
/sizescale
);
3124 // See if 1/sizescale is exactly representable as a decimal fraction,
3125 // ie its only prime factors are 2 and 5.
3128 while ((n
& 1) == 0) {
3133 while ((n
% 5) == 0) {
3138 int decimal_point
= power5
> power2
? power5
: power2
;
3139 if (decimal_point
<= 10) {
3142 for (t
= decimal_point
- power2
; --t
>= 0;)
3144 for (t
= decimal_point
- power5
; --t
>= 0;)
3146 if (factor
== 1 || sp
<= INT_MAX
/factor
)
3147 return if_to_a(sp
*factor
, decimal_point
);
3150 double s
= double(sp
)/double(sizescale
);
3151 double factor
= 10.0;
3153 int decimal_point
= 0;
3155 double v
= ceil(s
*factor
);
3160 } while (++decimal_point
< 10);
3161 return if_to_a(int(val
), decimal_point
);
3164 const char *environment::get_point_size_string()
3166 return sptoa(curenv
->get_point_size());
3169 const char *environment::get_requested_point_size_string()
3171 return sptoa(curenv
->get_requested_point_size());
3174 #define init_int_env_reg(name, func) \
3175 number_reg_dictionary.define(name, new int_env_reg(&environment::func))
3177 #define init_vunits_env_reg(name, func) \
3178 number_reg_dictionary.define(name, new vunits_env_reg(&environment::func))
3180 #define init_hunits_env_reg(name, func) \
3181 number_reg_dictionary.define(name, new hunits_env_reg(&environment::func))
3183 #define init_string_env_reg(name, func) \
3184 number_reg_dictionary.define(name, new string_env_reg(&environment::func))
3186 void init_env_requests()
3188 init_request("ad", adjust
);
3189 init_request("br", break_request
);
3190 init_request("brp", break_spread_request
);
3191 init_request("c2", no_break_control_char
);
3192 init_request("cc", control_char
);
3193 init_request("ce", center
);
3194 init_request("cu", continuous_underline
);
3195 init_request("ev", environment_switch
);
3196 init_request("evc", environment_copy
);
3197 init_request("fam", family_change
);
3198 init_request("fc", field_characters
);
3199 init_request("fi", fill
);
3200 init_request("ft", font_change
);
3201 init_request("hc", hyphen_char
);
3202 init_request("hlm", hyphen_line_max_request
);
3203 init_request("hy", hyphenate_request
);
3204 init_request("hym", hyphenation_margin_request
);
3205 init_request("hys", hyphenation_space_request
);
3206 init_request("in", indent
);
3207 init_request("it", input_trap
);
3208 init_request("itc", input_trap_continued
);
3209 init_request("lc", leader_character
);
3210 init_request("linetabs", line_tabs_request
);
3211 init_request("ll", line_length
);
3212 init_request("ls", line_spacing
);
3213 init_request("lt", title_length
);
3214 init_request("mc", margin_character
);
3215 init_request("na", no_adjust
);
3216 init_request("nf", no_fill
);
3217 init_request("nh", no_hyphenate
);
3218 init_request("nm", number_lines
);
3219 init_request("nn", no_number
);
3220 init_request("ps", point_size
);
3221 init_request("pvs", post_vertical_spacing
);
3222 init_request("rj", right_justify
);
3223 init_request("sizes", override_sizes
);
3224 init_request("ss", space_size
);
3225 init_request("ta", set_tabs
);
3226 init_request("ti", temporary_indent
);
3227 init_request("tc", tab_character
);
3228 init_request("tl", title
);
3229 init_request("ul", underline
);
3230 init_request("vs", vertical_spacing
);
3231 #ifdef WIDOW_CONTROL
3232 init_request("wdc", widow_control_request
);
3233 #endif /* WIDOW_CONTROL */
3234 init_int_env_reg(".b", get_bold
);
3235 init_vunits_env_reg(".cdp", get_prev_char_depth
);
3236 init_int_env_reg(".ce", get_center_lines
);
3237 init_vunits_env_reg(".cht", get_prev_char_height
);
3238 init_hunits_env_reg(".csk", get_prev_char_skew
);
3239 init_string_env_reg(".ev", get_name_string
);
3240 init_int_env_reg(".f", get_font
);
3241 init_string_env_reg(".fam", get_font_family_string
);
3242 init_string_env_reg(".fn", get_font_name_string
);
3243 init_int_env_reg(".height", get_char_height
);
3244 init_int_env_reg(".hlc", get_hyphen_line_count
);
3245 init_int_env_reg(".hlm", get_hyphen_line_max
);
3246 init_int_env_reg(".hy", get_hyphenation_flags
);
3247 init_hunits_env_reg(".hym", get_hyphenation_margin
);
3248 init_hunits_env_reg(".hys", get_hyphenation_space
);
3249 init_hunits_env_reg(".i", get_indent
);
3250 init_hunits_env_reg(".in", get_saved_indent
);
3251 init_int_env_reg(".int", get_prev_line_interrupted
);
3252 init_int_env_reg(".linetabs", get_line_tabs
);
3253 init_hunits_env_reg(".lt", get_title_length
);
3254 init_int_env_reg(".j", get_adjust_mode
);
3255 init_hunits_env_reg(".k", get_text_length
);
3256 init_int_env_reg(".L", get_line_spacing
);
3257 init_hunits_env_reg(".l", get_line_length
);
3258 init_hunits_env_reg(".ll", get_saved_line_length
);
3259 init_string_env_reg(".M", get_fill_color_string
);
3260 init_string_env_reg(".m", get_glyph_color_string
);
3261 init_hunits_env_reg(".n", get_prev_text_length
);
3262 init_int_env_reg(".ps", get_point_size
);
3263 init_int_env_reg(".psr", get_requested_point_size
);
3264 init_vunits_env_reg(".pvs", get_post_vertical_spacing
);
3265 init_int_env_reg(".rj", get_right_justify_lines
);
3266 init_string_env_reg(".s", get_point_size_string
);
3267 init_int_env_reg(".slant", get_char_slant
);
3268 init_int_env_reg(".ss", get_space_size
);
3269 init_int_env_reg(".sss", get_sentence_space_size
);
3270 init_string_env_reg(".sr", get_requested_point_size_string
);
3271 init_string_env_reg(".tabs", get_tabs
);
3272 init_int_env_reg(".u", get_fill
);
3273 init_vunits_env_reg(".v", get_vertical_spacing
);
3274 init_hunits_env_reg(".w", get_prev_char_width
);
3275 number_reg_dictionary
.define("ct", new variable_reg(&ct_reg_contents
));
3276 number_reg_dictionary
.define("hp", new horizontal_place_reg
);
3277 number_reg_dictionary
.define("ln", new variable_reg(&next_line_number
));
3278 number_reg_dictionary
.define("rsb", new variable_reg(&rsb_reg_contents
));
3279 number_reg_dictionary
.define("rst", new variable_reg(&rst_reg_contents
));
3280 number_reg_dictionary
.define("sb", new variable_reg(&sb_reg_contents
));
3281 number_reg_dictionary
.define("skw", new variable_reg(&skw_reg_contents
));
3282 number_reg_dictionary
.define("ssc", new variable_reg(&ssc_reg_contents
));
3283 number_reg_dictionary
.define("st", new variable_reg(&st_reg_contents
));
3286 // Hyphenation - TeX's hyphenation algorithm with a less fancy implementation.
3292 virtual void do_match(int len
, void *val
) = 0;
3293 virtual void do_delete(void *) = 0;
3294 void delete_trie_node(trie_node
*);
3297 virtual ~trie(); // virtual to shut up g++
3298 void insert(const char *, int, void *);
3299 // find calls do_match for each match it finds
3300 void find(const char *pat
, int patlen
);
3304 class hyphen_trie
: private trie
{
3306 void do_match(int i
, void *v
);
3307 void do_delete(void *v
);
3308 void insert_pattern(const char *pat
, int patlen
, int *num
);
3309 void insert_hyphenation(dictionary
*ex
, const char *pat
, int patlen
);
3310 int hpf_getc(FILE *f
);
3314 void hyphenate(const char *word
, int len
, int *hyphens
);
3315 void read_patterns_file(const char *name
, int append
, dictionary
*ex
);
3318 struct hyphenation_language
{
3320 dictionary exceptions
;
3321 hyphen_trie patterns
;
3322 hyphenation_language(symbol nm
) : name(nm
), exceptions(501) {}
3323 ~hyphenation_language() { }
3326 dictionary
language_dictionary(5);
3327 hyphenation_language
*current_language
= 0;
3329 static void set_hyphenation_language()
3331 symbol nm
= get_name(1);
3332 if (!nm
.is_null()) {
3333 current_language
= (hyphenation_language
*)language_dictionary
.lookup(nm
);
3334 if (!current_language
) {
3335 current_language
= new hyphenation_language(nm
);
3336 (void)language_dictionary
.lookup(nm
, (void *)current_language
);
3342 const int WORD_MAX
= 256; // we use unsigned char for offsets in
3343 // hyphenation exceptions
3345 static void hyphen_word()
3347 if (!current_language
) {
3348 error("no current hyphenation language");
3352 char buf
[WORD_MAX
+ 1];
3353 unsigned char pos
[WORD_MAX
+ 2];
3356 if (tok
.newline() || tok
.eof())
3360 while (i
< WORD_MAX
&& !tok
.space() && !tok
.newline() && !tok
.eof()) {
3361 charinfo
*ci
= tok
.get_char(1);
3367 if (ci
->get_ascii_code() == '-') {
3368 if (i
> 0 && (npos
== 0 || pos
[npos
- 1] != i
))
3372 int c
= ci
->get_hyphenation_code();
3381 unsigned char *tem
= new unsigned char[npos
+ 1];
3382 memcpy(tem
, pos
, npos
+ 1);
3383 tem
= (unsigned char *)current_language
->exceptions
.lookup(symbol(buf
),
3397 trie_node(char, trie_node
*);
3400 trie_node::trie_node(char ch
, trie_node
*p
)
3401 : c(ch
), down(0), right(p
), val(0)
3412 delete_trie_node(tp
);
3417 void trie::delete_trie_node(trie_node
*p
)
3420 delete_trie_node(p
->down
);
3421 delete_trie_node(p
->right
);
3428 void trie::insert(const char *pat
, int patlen
, void *val
)
3430 trie_node
**p
= &tp
;
3431 assert(patlen
> 0 && pat
!= 0);
3433 while (*p
!= 0 && (*p
)->c
< pat
[0])
3435 if (*p
== 0 || (*p
)->c
!= pat
[0])
3436 *p
= new trie_node(pat
[0], *p
);
3437 if (--patlen
== 0) {
3446 void trie::find(const char *pat
, int patlen
)
3449 for (int i
= 0; p
!= 0 && i
< patlen
; i
++) {
3450 while (p
!= 0 && p
->c
< pat
[i
])
3452 if (p
!= 0 && p
->c
== pat
[i
]) {
3454 do_match(i
+1, p
->val
);
3466 operation(int, int, operation
*);
3469 operation::operation(int i
, int j
, operation
*op
)
3470 : next(op
), distance(j
), num(i
)
3474 void hyphen_trie::insert_pattern(const char *pat
, int patlen
, int *num
)
3477 for (int i
= 0; i
< patlen
+1; i
++)
3479 op
= new operation(num
[i
], patlen
- i
, op
);
3480 insert(pat
, patlen
, op
);
3483 void hyphen_trie::insert_hyphenation(dictionary
*ex
, const char *pat
,
3486 char buf
[WORD_MAX
+ 1];
3487 unsigned char pos
[WORD_MAX
+ 2];
3490 while (j
< patlen
) {
3491 unsigned char c
= pat
[j
++];
3493 if (i
> 0 && (npos
== 0 || pos
[npos
- 1] != i
))
3497 buf
[i
++] = hpf_code_table
[c
];
3502 unsigned char *tem
= new unsigned char[npos
+ 1];
3503 memcpy(tem
, pos
, npos
+ 1);
3504 tem
= (unsigned char *)ex
->lookup(symbol(buf
), tem
);
3510 void hyphen_trie::hyphenate(const char *word
, int len
, int *hyphens
)
3513 for (j
= 0; j
< len
+ 1; j
++)
3515 for (j
= 0; j
< len
- 1; j
++) {
3517 find(word
+ j
, len
- j
);
3521 inline int max(int m
, int n
)
3523 return m
> n
? m
: n
;
3526 void hyphen_trie::do_match(int i
, void *v
)
3528 operation
*op
= (operation
*)v
;
3530 h
[i
- op
->distance
] = max(h
[i
- op
->distance
], op
->num
);
3535 void hyphen_trie::do_delete(void *v
)
3537 operation
*op
= (operation
*)v
;
3539 operation
*tem
= op
;
3545 /* We use very simple rules to parse TeX's hyphenation patterns.
3547 . `%' starts a comment even if preceded by `\'.
3549 . No support for digraphs and like `\$'.
3551 . `^^xx' (`x' is 0-9 or a-f), and `^^x' (character code of `x' in the
3552 range 0-127) are recognized; other use of `^' causes an error.
3554 . No macro expansion.
3556 . We check for the expression `\patterns{...}' (possibly with
3557 whitespace before and after the braces). Everything between the
3558 braces is taken as hyphenation patterns. Consequently, `{' and `}'
3559 are not allowed in patterns.
3561 . Similarly, `\hyphenation{...}' gives a list of hyphenation
3564 . `\endinput' is recognized also.
3566 . For backwards compatibility, if `\patterns' is missing, the
3567 whole file is treated as a list of hyphenation patterns (only
3568 recognizing `%' as the start of a comment.
3572 int hyphen_trie::hpf_getc(FILE *f
)
3584 if (((c
>= '0' && c
<= '9') || (c
>= 'a' && c
<= 'f'))
3585 && ((c1
>= '0' && c1
<= '9') || (c1
>= 'a' && c1
<= 'f'))) {
3586 if (c
>= '0' && c
<= '9')
3590 if (c1
>= '0' && c1
<= '9')
3598 if (c
>= 0 && c
<= 63)
3600 else if (c
>= 64 && c
<= 127)
3607 error("invalid ^, ^^x, or ^^xx character in hyphenation patterns file");
3611 void hyphen_trie::read_patterns_file(const char *name
, int append
,
3617 for (int i
= 0; i
< WORD_MAX
; i
++)
3619 int num
[WORD_MAX
+1];
3622 FILE *fp
= mac_path
->open_file(name
, &path
);
3624 error("can't find hyphenation patterns file `%1'", name
);
3627 int c
= hpf_getc(fp
);
3628 int have_patterns
= 0; // we've seen \patterns
3629 int final_pattern
= 0; // 1 if we have a trailing closing brace
3630 int have_hyphenation
= 0; // we've seen \hyphenation
3631 int final_hyphenation
= 0; // 1 if we have a trailing closing brace
3632 int have_keyword
= 0; // we've seen either \patterns or \hyphenation
3633 int traditional
= 0; // don't handle \patterns
3636 if (c
== '%') { // skip comments
3639 } while (c
!= EOF
&& c
!= '\n');
3641 if (c
== EOF
|| !csspace(c
))
3646 if (have_keyword
|| traditional
) // we are done
3648 else { // rescan file in `traditional' mode
3657 if (!(c
== '{' || c
== '}')) { // skip braces at line start
3658 do { // scan patterns
3666 } while (i
< WORD_MAX
&& c
!= EOF
&& !csspace(c
)
3667 && c
!= '%' && c
!= '{' && c
!= '}');
3670 if (i
>= 9 && !strncmp(buf
+ i
- 9, "\\patterns", 9)) {
3674 if (have_patterns
|| have_hyphenation
)
3675 error("\\patterns not allowed inside of %1 group",
3676 have_patterns
? "\\patterns" : "\\hyphenation");
3685 else if (i
>= 12 && !strncmp(buf
+ i
- 12, "\\hyphenation", 12)) {
3689 if (have_patterns
|| have_hyphenation
)
3690 error("\\hyphenation not allowed inside of %1 group",
3691 have_patterns
? "\\patterns" : "\\hyphenation");
3693 have_hyphenation
= 1;
3700 else if (strstr(buf
, "\\endinput")) {
3701 if (have_patterns
|| have_hyphenation
)
3702 error("found \\endinput inside of %1 group",
3703 have_patterns
? "\\patterns" : "\\hyphenation");
3706 else if (c
== '}') {
3707 if (have_patterns
) {
3712 else if (have_hyphenation
) {
3713 have_hyphenation
= 0;
3715 final_hyphenation
= 1;
3719 else if (c
== '{') {
3720 if (have_patterns
|| have_hyphenation
)
3721 error("`{' not allowed within %1 group",
3722 have_patterns
? "\\patterns" : "\\hyphenation");
3723 c
= hpf_getc(fp
); // skipped if not starting \patterns
3728 if (c
== '{' || c
== '}')
3732 if (have_patterns
|| final_pattern
|| traditional
) {
3733 for (int j
= 0; j
< i
; j
++)
3734 buf
[j
] = hpf_code_table
[(unsigned char)buf
[j
]];
3735 insert_pattern(buf
, i
, num
);
3738 else if (have_hyphenation
|| final_hyphenation
) {
3739 insert_hyphenation(ex
, buf
, i
);
3740 final_hyphenation
= 0;
3749 void hyphenate(hyphen_list
*h
, unsigned flags
)
3751 if (!current_language
)
3754 while (h
&& h
->hyphenation_code
== 0)
3757 char hbuf
[WORD_MAX
+2];
3758 char *buf
= hbuf
+ 1;
3760 for (tem
= h
; tem
&& len
< WORD_MAX
; tem
= tem
->next
) {
3761 if (tem
->hyphenation_code
!= 0)
3762 buf
[len
++] = tem
->hyphenation_code
;
3766 hyphen_list
*nexth
= tem
;
3770 = (unsigned char *)current_language
->exceptions
.lookup(buf
);
3774 for (tem
= h
; tem
!= 0; tem
= tem
->next
, i
++)
3781 hbuf
[0] = hbuf
[len
+1] = '.';
3782 int num
[WORD_MAX
+3];
3783 current_language
->patterns
.hyphenate(hbuf
, len
+2, num
);
3790 for (i
= 2, tem
= h
; i
< len
&& tem
; tem
= tem
->next
, i
++)
3799 static void do_hyphenation_patterns_file(int append
)
3801 symbol name
= get_long_name(1);
3802 if (!name
.is_null()) {
3803 if (!current_language
)
3804 error("no current hyphenation language");
3806 current_language
->patterns
.read_patterns_file(
3807 name
.contents(), append
,
3808 ¤t_language
->exceptions
);
3813 static void hyphenation_patterns_file()
3815 do_hyphenation_patterns_file(0);
3818 static void hyphenation_patterns_file_append()
3820 do_hyphenation_patterns_file(1);
3823 class hyphenation_language_reg
: public reg
{
3825 const char *get_string();
3828 const char *hyphenation_language_reg::get_string()
3830 return current_language
? current_language
->name
.contents() : "";
3833 void init_hyphen_requests()
3835 init_request("hw", hyphen_word
);
3836 init_request("hla", set_hyphenation_language
);
3837 init_request("hpf", hyphenation_patterns_file
);
3838 init_request("hpfa", hyphenation_patterns_file_append
);
3839 number_reg_dictionary
.define(".hla", new hyphenation_language_reg
);