2 * Copyright (c) 2014 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
4 * Copyright (C) 1989 - 1992, 2000 - 2008 Free Software Foundation, Inc.
5 * Written by James Clark (jjc@jclark.com)
7 * This is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2, or (at your option) any later
12 * This is distributed in the hope that it will be useful, but WITHOUT ANY
13 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 * You should have received a copy of the GNU General Public License along
18 * with groff; see the file COPYING. If not, write to the Free Software
19 * Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA.
21 #define DEBUGGING // FIXME
24 #include "troff-config.h"
27 #include "file_case.h"
29 #include "macropath.h"
32 #include "stringclass.h"
36 #include "dictionary.h"
48 // warnings that are enabled by default
49 #ifndef DEFAULT_WARNING_MASK
50 # define DEFAULT_WARNING_MASK \
51 (WARN_CHAR|WARN_NUMBER|WARN_BREAK|WARN_SPACE|WARN_FONT|WARN_FILE)
54 // initial size of buffer for reading names; expanded as necessary
57 extern "C" const char *program_name
;
60 void init_column_requests();
63 static node
*read_draw_node();
64 static void read_color_draw_node(token
&);
65 static void push_token(const token
&);
70 void transparent_file();
74 int color_flag
= 1; // colors are on by default
75 static int backtrace_flag
= 0;
77 char *pipe_command
= 0;
79 charinfo
*charset_table
[UCHAR_MAX
+1];
80 unsigned char hpf_code_table
[UCHAR_MAX
+1];
82 static int warning_mask
= DEFAULT_WARNING_MASK
;
83 static int inhibit_errors
= 0;
84 static int ignoring
= 0;
86 static void enable_warning(const char *);
87 static void disable_warning(const char *);
89 static int escape_char
= '\\';
90 static symbol end_macro_name
;
91 static symbol blank_line_macro_name
;
92 static int compatible_flag
= 0;
93 int ascii_output_flag
= 0;
94 int suppress_output_flag
= 0;
96 int begin_level
= 0; // number of nested \O escapes
98 int have_input
= 0; // whether \f, \F, \D'F...', \H, \m, \M,
99 // \O[345], \R, \s, or \S has been processed
101 int old_have_input
= 0; // value of have_input right before \n
102 int tcommand_flag
= 0;
103 int unsafe_flag
= 0; // safer by default
105 int have_string_arg
= 0; // whether we have \*[foo bar...]
107 double spread_limit
= -3.0 - 1.0; // negative means deactivated
110 char warn_scaling_indicator
;
111 int debug_state
= 0; // turns on debugging of the html troff state
113 search_path
*mac_path
= &safer_macro_path
;
115 // Defaults to the current directory.
116 search_path
include_search_path(0, 0, 0, 1);
118 static int get_copy(node
**, int = 0, int = 0);
119 static void copy_mode_error(const char *,
120 const errarg
& = empty_errarg
,
121 const errarg
& = empty_errarg
,
122 const errarg
& = empty_errarg
);
124 enum read_mode
{ ALLOW_EMPTY
, WITH_ARGS
, NO_ARGS
};
125 static symbol
read_escape_name(read_mode
= NO_ARGS
);
126 static symbol
read_long_escape_name(read_mode
= NO_ARGS
);
127 static void interpolate_string(symbol
);
128 static void interpolate_string_with_args(symbol
);
129 static void interpolate_macro(symbol
, int = 0);
130 static void interpolate_number_format(symbol
);
131 static void interpolate_environment_variable(symbol
);
133 static symbol
composite_glyph_name(symbol
);
134 static void interpolate_arg(symbol
);
135 static request_or_macro
*lookup_request(symbol
);
136 static int get_delim_number(units
*, unsigned char);
137 static int get_delim_number(units
*, unsigned char, units
);
138 static symbol
do_get_long_name(int, char);
139 static int get_line_arg(units
*res
, unsigned char si
, charinfo
**cp
);
140 static int read_size(int *);
141 static symbol
get_delim_name();
142 static void init_registers();
143 static void trapping_blank_line();
145 class input_iterator
;
146 input_iterator
*make_temp_iterator(const char *);
147 const char *input_char_description(int);
149 void process_input_stack();
150 void chop_macro(); // declare to avoid friend name injection
152 void set_escape_char()
156 error("bad escape character");
160 escape_char
= tok
.ch();
173 static int saved_escape_char
= '\\';
175 void save_escape_char()
177 saved_escape_char
= escape_char
;
181 void restore_escape_char()
183 escape_char
= saved_escape_char
;
191 friend class input_stack
;
195 statem
*diversion_state
;
198 input_iterator(int is_div
);
199 virtual ~input_iterator() {}
203 const unsigned char *ptr
;
204 const unsigned char *eptr
;
205 input_iterator
*next
;
207 private: // FIXME (almost) abstract virtuals in private:?? ok, but.. ??
208 virtual int fill(node
**);
210 virtual int has_args() { return 0; }
211 virtual int nargs() { return 0; }
212 virtual input_iterator
*get_arg(int) { return 0; }
213 virtual arg_list
*get_arg_list() { return 0; }
214 virtual symbol
get_macro_name() { return NULL_SYMBOL
; }
215 virtual int space_follows_arg(int) { return 0; }
216 virtual int get_break_flag() { return 0; }
217 virtual int get_location(int, const char **, int *) { return 0; }
218 virtual void backtrace() {}
219 virtual int set_location(const char *, int) { return 0; }
220 virtual int next_file(file_case
*, const char *) { return 0; }
221 virtual void shift(int) {}
222 virtual int is_boundary() {return 0; }
223 virtual int is_file() { return 0; }
224 virtual int is_macro() { return 0; }
225 virtual void save_compatible_flag(int) {}
226 virtual int get_compatible_flag() { return 0; }
229 input_iterator::input_iterator()
230 : is_diversion(0), ptr(0), eptr(0)
234 input_iterator::input_iterator(int is_div
)
235 : is_diversion(is_div
), ptr(0), eptr(0)
239 int input_iterator::fill(node
**)
244 int input_iterator::peek()
249 inline int input_iterator::get(node
**p
)
251 return ptr
< eptr
? *ptr
++ : fill(p
);
255 : public input_iterator
258 int is_boundary() { return 1; }
261 class input_return_boundary
262 : public input_iterator
265 int is_boundary() { return 2; }
269 : public input_iterator
273 const char *filename
;
276 enum { BUF_SIZE
= 512 };
277 unsigned char buf
[BUF_SIZE
];
282 file_iterator(file_case
*, const char *);
286 int get_location(int, const char **, int *);
288 int set_location(const char *, int);
289 int next_file(file_case
*, const char *);
293 file_iterator::file_iterator(file_case
*fcp
, const char *fn
)
294 : _fcp(fcp
), lineno(1), filename(fn
), newline_flag(0), seen_escape(0)
296 if ((font::use_charnames_in_special
) && (fn
!= 0)) {
299 the_output
->put_filename(fn
, _fcp
->is_pipe());
303 file_iterator::~file_iterator()
309 void file_iterator::close()
317 int file_iterator::is_file()
322 int file_iterator::next_file(file_case
*fcp
, const char *s
)
335 int file_iterator::fill(node
**)
340 unsigned char *p
= buf
;
342 unsigned char *e
= p
+ BUF_SIZE
;
344 int c
= _fcp
->get_c();
347 if (invalid_input_char(c
))
348 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
356 seen_escape
= (c
== '\\');
369 int file_iterator::peek()
371 int c
= _fcp
->get_c();
372 while (invalid_input_char(c
)) {
373 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
381 int file_iterator::get_location(int /*allow_macro*/,
382 const char **filenamep
, int *linenop
)
385 if (filename
!= 0 && strcmp(filename
, "-") == 0)
386 *filenamep
= "<standard input>";
388 *filenamep
= filename
;
392 void file_iterator::backtrace()
394 errprint("%1:%2: backtrace: %3 `%1'\n", filename
, lineno
,
395 _fcp
->is_pipe() ? "process" : "file");
398 int file_iterator::set_location(const char *f
, int ln
)
404 the_output
->put_filename(f
, 0);
410 input_iterator nil_iterator
;
414 static input_iterator
*top
;
416 static int finish_get(node
**);
417 static int finish_peek();
420 static int get(node
**);
422 static void push(input_iterator
*);
423 static input_iterator
*get_arg(int);
424 static arg_list
*get_arg_list();
425 static symbol
get_macro_name();
426 static int space_follows_arg(int);
427 static int get_break_flag();
429 static int get_location(int, const char **, int *);
430 static int set_location(const char *, int);
431 static void backtrace();
432 static void backtrace_all();
433 static void next_file(file_case
*, const char *);
434 static void end_file();
435 static void shift(int n
);
436 static void add_boundary();
437 static void add_return_boundary();
438 static int is_return_boundary();
439 static void remove_boundary();
440 static int get_level();
441 static int get_div_level();
442 static void increase_level();
443 static void decrease_level();
445 static void pop_macro();
446 static void save_compatible_flag(int);
447 static int get_compatible_flag();
448 static statem
*get_diversion_state();
449 static void check_end_diversion(input_iterator
*t
);
451 static int div_level
;
452 static statem
*diversion_state
;
455 input_iterator
*input_stack::top
= &nil_iterator
;
456 int input_stack::level
= 0;
457 int input_stack::limit
= DEFAULT_INPUT_STACK_LIMIT
;
458 int input_stack::div_level
= 0;
459 statem
*input_stack::diversion_state
= NULL
;
462 inline int input_stack::get_level()
467 inline void input_stack::increase_level()
472 inline void input_stack::decrease_level()
477 inline int input_stack::get_div_level()
482 inline int input_stack::get(node
**np
)
484 int res
= (top
->ptr
< top
->eptr
) ? *top
->ptr
++ : finish_get(np
);
486 old_have_input
= have_input
;
492 int input_stack::finish_get(node
**np
)
495 int c
= top
->fill(np
);
496 if (c
!= EOF
|| top
->is_boundary())
498 if (top
== &nil_iterator
)
500 input_iterator
*tem
= top
;
501 check_end_diversion(tem
);
502 #if defined(DEBUGGING)
504 if (tem
->is_diversion
)
506 "in diversion level = %d\n", input_stack::get_div_level());
511 if (top
->ptr
< top
->eptr
)
518 inline int input_stack::peek()
520 return (top
->ptr
< top
->eptr
) ? *top
->ptr
: finish_peek();
523 void input_stack::check_end_diversion(input_iterator
*t
)
525 if (t
->is_diversion
) {
527 diversion_state
= t
->diversion_state
;
531 int input_stack::finish_peek()
535 if (c
!= EOF
|| top
->is_boundary())
537 if (top
== &nil_iterator
)
539 input_iterator
*tem
= top
;
540 check_end_diversion(tem
);
544 if (top
->ptr
< top
->eptr
)
551 void input_stack::add_boundary()
553 push(new input_boundary
);
556 void input_stack::add_return_boundary()
558 push(new input_return_boundary
);
561 int input_stack::is_return_boundary()
563 return top
->is_boundary() == 2;
566 void input_stack::remove_boundary()
568 assert(top
->is_boundary());
569 input_iterator
*temp
= top
->next
;
570 check_end_diversion(top
);
577 void input_stack::push(input_iterator
*in
)
581 if (++level
> limit
&& limit
> 0)
582 fatal("input stack limit exceeded (probable infinite loop)");
585 if (top
->is_diversion
) {
587 in
->diversion_state
= diversion_state
;
588 diversion_state
= curenv
->construct_state(0);
589 #if defined(DEBUGGING)
591 curenv
->dump_troff_state();
596 #if defined(DEBUGGING)
598 if (top
->is_diversion
) {
600 "in diversion level = %d\n", input_stack::get_div_level());
606 statem
*get_diversion_state()
608 return input_stack::get_diversion_state();
611 statem
*input_stack::get_diversion_state()
613 if (diversion_state
== NULL
)
616 return new statem(diversion_state
);
619 input_iterator
*input_stack::get_arg(int i
)
622 for (p
= top
; p
!= 0; p
= p
->next
)
624 return p
->get_arg(i
);
628 arg_list
*input_stack::get_arg_list()
631 for (p
= top
; p
!= 0; p
= p
->next
)
633 return p
->get_arg_list();
637 symbol
input_stack::get_macro_name()
640 for (p
= top
; p
!= 0; p
= p
->next
)
642 return p
->get_macro_name();
646 int input_stack::space_follows_arg(int i
)
649 for (p
= top
; p
!= 0; p
= p
->next
)
651 return p
->space_follows_arg(i
);
655 int input_stack::get_break_flag()
657 return top
->get_break_flag();
660 void input_stack::shift(int n
)
662 for (input_iterator
*p
= top
; p
; p
= p
->next
)
669 int input_stack::nargs()
671 for (input_iterator
*p
=top
; p
!= 0; p
= p
->next
)
677 int input_stack::get_location(int allow_macro
, const char **filenamep
, int *linenop
)
679 for (input_iterator
*p
= top
; p
; p
= p
->next
)
680 if (p
->get_location(allow_macro
, filenamep
, linenop
))
685 void input_stack::backtrace()
689 // only backtrace down to (not including) the topmost file
690 for (input_iterator
*p
= top
;
691 p
&& !p
->get_location(0, &f
, &n
);
696 void input_stack::backtrace_all()
698 for (input_iterator
*p
= top
; p
; p
= p
->next
)
702 int input_stack::set_location(const char *filename
, int lineno
)
704 for (input_iterator
*p
= top
; p
; p
= p
->next
)
705 if (p
->set_location(filename
, lineno
))
710 void input_stack::next_file(file_case
*fcp
, const char *s
)
713 for (pp
= &top
; *pp
!= &nil_iterator
; pp
= &(*pp
)->next
)
714 if ((*pp
)->next_file(fcp
, s
))
716 if (++level
> limit
&& limit
> 0)
717 fatal("input stack limit exceeded");
718 *pp
= new file_iterator(fcp
, s
);
719 (*pp
)->next
= &nil_iterator
;
722 void input_stack::end_file()
724 for (input_iterator
**pp
= &top
; *pp
!= &nil_iterator
; pp
= &(*pp
)->next
)
725 if ((*pp
)->is_file()) {
726 input_iterator
*tem
= *pp
;
727 check_end_diversion(tem
);
735 void input_stack::clear()
738 while (top
!= &nil_iterator
) {
739 if (top
->is_boundary())
741 input_iterator
*tem
= top
;
742 check_end_diversion(tem
);
747 // Keep while_request happy.
748 for (; nboundaries
> 0; --nboundaries
)
749 add_return_boundary();
752 void input_stack::pop_macro()
757 if (top
->next
== &nil_iterator
)
759 if (top
->is_boundary())
761 is_macro
= top
->is_macro();
762 input_iterator
*tem
= top
;
763 check_end_diversion(tem
);
768 // Keep while_request happy.
769 for (; nboundaries
> 0; --nboundaries
)
770 add_return_boundary();
773 inline void input_stack::save_compatible_flag(int f
)
775 top
->save_compatible_flag(f
);
778 inline int input_stack::get_compatible_flag()
780 return top
->get_compatible_flag();
783 void backtrace_request()
785 input_stack::backtrace_all();
792 symbol nm
= get_long_name();
793 while (!tok
.newline() && !tok
.eof())
796 input_stack::end_file();
798 file_case
*fcp
= include_search_path
.open_file_cautious(nm
.contents(),
801 input_stack::next_file(fcp
, nm
.contents());
803 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
811 if (!has_arg() || !get_integer(&n
))
813 input_stack::shift(n
);
817 static char get_char_for_escape_name(int allow_space
= 0)
819 int c
= get_copy(0, 0, 1);
822 copy_mode_error("end of input in escape name");
825 if (!invalid_input_char(c
))
830 input_stack::push(make_temp_iterator("\n"));
833 if (c
== ' ' && allow_space
)
839 copy_mode_error("%1 is not allowed in an escape name",
840 input_char_description(c
));
846 static symbol
read_two_char_escape_name()
849 buf
[0] = get_char_for_escape_name();
850 if (buf
[0] != '\0') {
851 buf
[1] = get_char_for_escape_name();
860 static symbol
read_long_escape_name(read_mode mode
)
862 int start_level
= input_stack::get_level();
863 char abuf
[ABUF_SIZE
];
865 int buf_size
= ABUF_SIZE
;
870 c
= get_char_for_escape_name(have_char
&& mode
== WITH_ARGS
);
877 if (mode
== WITH_ARGS
&& c
== ' ')
879 if (i
+ 2 > buf_size
) {
881 buf
= new char[ABUF_SIZE
*2];
882 memcpy(buf
, abuf
, buf_size
);
883 buf_size
= ABUF_SIZE
*2;
887 buf
= new char[buf_size
*2];
888 memcpy(buf
, old_buf
, buf_size
);
893 if (c
== ']' && input_stack::get_level() == start_level
)
902 if (mode
!= ALLOW_EMPTY
)
903 copy_mode_error("empty escape name");
915 static symbol
read_escape_name(read_mode mode
)
917 char c
= get_char_for_escape_name();
921 return read_two_char_escape_name();
922 if (c
== '[' && !compatible_flag
)
923 return read_long_escape_name(mode
);
930 static symbol
read_increment_and_escape_name(int *incp
)
932 char c
= get_char_for_escape_name();
939 return read_two_char_escape_name();
942 return read_escape_name();
945 return read_escape_name();
947 if (!compatible_flag
) {
949 return read_long_escape_name();
960 static int get_copy(node
**nd
, int defining
, int handle_escape_E
)
963 int c
= input_stack::get(nd
);
964 if (c
== PUSH_GROFF_MODE
) {
965 input_stack::save_compatible_flag(compatible_flag
);
969 if (c
== PUSH_COMP_MODE
) {
970 input_stack::save_compatible_flag(compatible_flag
);
974 if (c
== POP_GROFFCOMP_MODE
) {
975 compatible_flag
= input_stack::get_compatible_flag();
978 if (c
== BEGIN_QUOTE
) {
979 input_stack::increase_level();
982 if (c
== END_QUOTE
) {
983 input_stack::decrease_level();
986 if (c
== DOUBLE_QUOTE
)
988 if (c
== ESCAPE_E
&& handle_escape_E
)
990 if (c
== ESCAPE_NEWLINE
) {
994 c
= input_stack::get(nd
);
995 } while (c
== ESCAPE_NEWLINE
);
997 if (c
!= escape_char
|| escape_char
<= 0)
1000 c
= input_stack::peek();
1005 (void)input_stack::get(0);
1006 while ((c
= input_stack::get(0)) != '\n' && c
!= EOF
)
1009 case '#': // Like \" but newline is ignored.
1010 (void)input_stack::get(0);
1011 while ((c
= input_stack::get(0)) != '\n')
1017 (void)input_stack::get(0);
1018 symbol s
= read_escape_name();
1019 if (!(s
.is_null() || s
.is_empty()))
1025 (void)input_stack::get(0);
1026 symbol s
= read_escape_name(WITH_ARGS
);
1027 if (!(s
.is_null() || s
.is_empty())) {
1028 if (have_string_arg
) {
1029 have_string_arg
= 0;
1030 interpolate_string_with_args(s
);
1033 interpolate_string(s
);
1038 (void)input_stack::get(0);
1041 (void)input_stack::get(0);
1044 (void)input_stack::get(0);
1045 if (handle_escape_E
)
1050 (void)input_stack::get(0);
1052 symbol s
= read_increment_and_escape_name(&inc
);
1053 if (!(s
.is_null() || s
.is_empty()))
1054 interpolate_number_reg(s
, inc
);
1059 (void)input_stack::get(0);
1060 symbol s
= read_escape_name();
1061 if (!(s
.is_null() || s
.is_empty()))
1062 interpolate_number_format(s
);
1066 (void)input_stack::get(0);
1070 (void)input_stack::get(0);
1071 symbol s
= read_escape_name();
1072 if (!(s
.is_null() || s
.is_empty()))
1073 interpolate_environment_variable(s
);
1077 (void)input_stack::get(0);
1079 return ESCAPE_NEWLINE
;
1082 (void)input_stack::get(0);
1083 return ESCAPE_SPACE
;
1085 (void)input_stack::get(0);
1086 return ESCAPE_TILDE
;
1088 (void)input_stack::get(0);
1089 return ESCAPE_COLON
;
1091 (void)input_stack::get(0);
1094 (void)input_stack::get(0);
1095 return ESCAPE_CIRCUMFLEX
;
1097 (void)input_stack::get(0);
1098 return ESCAPE_LEFT_BRACE
;
1100 (void)input_stack::get(0);
1101 return ESCAPE_RIGHT_BRACE
;
1103 (void)input_stack::get(0);
1104 return ESCAPE_LEFT_QUOTE
;
1106 (void)input_stack::get(0);
1107 return ESCAPE_RIGHT_QUOTE
;
1109 (void)input_stack::get(0);
1110 return ESCAPE_HYPHEN
;
1112 (void)input_stack::get(0);
1113 return ESCAPE_UNDERSCORE
;
1115 (void)input_stack::get(0);
1118 (void)input_stack::get(0);
1121 (void)input_stack::get(0);
1122 return ESCAPE_QUESTION
;
1124 (void)input_stack::get(0);
1125 return ESCAPE_AMPERSAND
;
1127 (void)input_stack::get(0);
1128 return ESCAPE_RIGHT_PARENTHESIS
;
1130 (void)input_stack::get(0);
1133 (void)input_stack::get(0);
1134 return ESCAPE_PERCENT
;
1136 if (c
== escape_char
) {
1137 (void)input_stack::get(0);
1146 class non_interpreted_char_node
1152 non_interpreted_char_node(unsigned char);
1154 int interpret(macro
*);
1161 int non_interpreted_char_node::same(node
*nd
)
1163 return c
== ((non_interpreted_char_node
*)nd
)->c
;
1166 const char *non_interpreted_char_node::type()
1168 return "non_interpreted_char_node";
1171 int non_interpreted_char_node::force_tprint()
1176 int non_interpreted_char_node::is_tag()
1181 non_interpreted_char_node::non_interpreted_char_node(unsigned char n
) : c(n
)
1186 node
*non_interpreted_char_node::copy()
1188 return new non_interpreted_char_node(c
);
1191 int non_interpreted_char_node::interpret(macro
*mac
)
1197 static void do_width();
1198 static node
*do_non_interpreted();
1199 static node
*do_special();
1200 static node
*do_suppress(symbol nm
);
1201 static void do_register();
1203 dictionary
color_dictionary(501);
1205 static color
*lookup_color(symbol nm
)
1207 assert(!nm
.is_null());
1208 if (nm
== default_symbol
)
1209 return &default_color
;
1210 color
*c
= (color
*)color_dictionary
.lookup(nm
);
1212 warning(WARN_COLOR
, "color `%1' not defined", nm
.contents());
1216 void do_glyph_color(symbol nm
)
1221 curenv
->set_glyph_color(curenv
->get_prev_glyph_color());
1223 color
*tem
= lookup_color(nm
);
1225 curenv
->set_glyph_color(tem
);
1227 (void)color_dictionary
.lookup(nm
, new color(nm
));
1231 void do_fill_color(symbol nm
)
1236 curenv
->set_fill_color(curenv
->get_prev_fill_color());
1238 color
*tem
= lookup_color(nm
);
1240 curenv
->set_fill_color(tem
);
1242 (void)color_dictionary
.lookup(nm
, new color(nm
));
1246 static unsigned int get_color_element(const char *scheme
, const char *col
)
1249 if (!get_number(&val
, 'f')) {
1250 warning(WARN_COLOR
, "%1 in %2 definition set to 0", col
, scheme
);
1255 warning(WARN_RANGE
, "%1 cannot be negative: set to 0", col
);
1258 if (val
> color::MAX_COLOR_VAL
+1) {
1259 warning(WARN_RANGE
, "%1 cannot be greater than 1", col
);
1260 // we change 0x10000 to 0xffff
1261 return color::MAX_COLOR_VAL
;
1263 return (unsigned int)val
;
1266 static color
*read_rgb(char end
= 0)
1268 symbol component
= do_get_long_name(0, end
);
1269 if (component
.is_null()) {
1270 warning(WARN_COLOR
, "missing rgb color values");
1273 const char *s
= component
.contents();
1274 color
*col
= new color
;
1276 if (!col
->read_rgb(s
)) {
1277 warning(WARN_COLOR
, "expecting rgb color definition not `%1'", s
);
1284 input_stack::push(make_temp_iterator(" "));
1285 input_stack::push(make_temp_iterator(s
));
1287 unsigned int r
= get_color_element("rgb color", "red component");
1288 unsigned int g
= get_color_element("rgb color", "green component");
1289 unsigned int b
= get_color_element("rgb color", "blue component");
1290 col
->set_rgb(r
, g
, b
);
1295 static color
*read_cmy(char end
= 0)
1297 symbol component
= do_get_long_name(0, end
);
1298 if (component
.is_null()) {
1299 warning(WARN_COLOR
, "missing cmy color values");
1302 const char *s
= component
.contents();
1303 color
*col
= new color
;
1305 if (!col
->read_cmy(s
)) {
1306 warning(WARN_COLOR
, "expecting cmy color definition not `%1'", s
);
1313 input_stack::push(make_temp_iterator(" "));
1314 input_stack::push(make_temp_iterator(s
));
1316 unsigned int c
= get_color_element("cmy color", "cyan component");
1317 unsigned int m
= get_color_element("cmy color", "magenta component");
1318 unsigned int y
= get_color_element("cmy color", "yellow component");
1319 col
->set_cmy(c
, m
, y
);
1324 static color
*read_cmyk(char end
= 0)
1326 symbol component
= do_get_long_name(0, end
);
1327 if (component
.is_null()) {
1328 warning(WARN_COLOR
, "missing cmyk color values");
1331 const char *s
= component
.contents();
1332 color
*col
= new color
;
1334 if (!col
->read_cmyk(s
)) {
1335 warning(WARN_COLOR
, "`expecting a cmyk color definition not `%1'", s
);
1342 input_stack::push(make_temp_iterator(" "));
1343 input_stack::push(make_temp_iterator(s
));
1345 unsigned int c
= get_color_element("cmyk color", "cyan component");
1346 unsigned int m
= get_color_element("cmyk color", "magenta component");
1347 unsigned int y
= get_color_element("cmyk color", "yellow component");
1348 unsigned int k
= get_color_element("cmyk color", "black component");
1349 col
->set_cmyk(c
, m
, y
, k
);
1354 static color
*read_gray(char end
= 0)
1356 symbol component
= do_get_long_name(0, end
);
1357 if (component
.is_null()) {
1358 warning(WARN_COLOR
, "missing gray values");
1361 const char *s
= component
.contents();
1362 color
*col
= new color
;
1364 if (!col
->read_gray(s
)) {
1365 warning(WARN_COLOR
, "`expecting a gray definition not `%1'", s
);
1372 input_stack::push(make_temp_iterator("\n"));
1373 input_stack::push(make_temp_iterator(s
));
1375 unsigned int g
= get_color_element("gray", "gray value");
1381 static void activate_color()
1384 if (has_arg() && get_integer(&n
))
1385 color_flag
= n
!= 0;
1391 static void define_color()
1393 symbol color_name
= get_long_name(1);
1394 if (color_name
.is_null()) {
1398 if (color_name
== default_symbol
) {
1399 warning(WARN_COLOR
, "default color can't be redefined");
1403 symbol style
= get_long_name(1);
1404 if (style
.is_null()) {
1409 if (strcmp(style
.contents(), "rgb") == 0)
1411 else if (strcmp(style
.contents(), "cmyk") == 0)
1413 else if (strcmp(style
.contents(), "gray") == 0)
1415 else if (strcmp(style
.contents(), "grey") == 0)
1417 else if (strcmp(style
.contents(), "cmy") == 0)
1421 "unknown color space `%1'; use rgb, cmyk, gray or cmy",
1427 col
->nm
= color_name
;
1428 (void)color_dictionary
.lookup(color_name
, col
);
1433 static node
*do_overstrike()
1436 overstrike_node
*on
= new overstrike_node
;
1437 int start_level
= input_stack::get_level();
1441 if (tok
.newline() || tok
.eof()) {
1442 warning(WARN_DELIM
, "missing closing delimiter");
1443 input_stack::push(make_temp_iterator("\n"));
1447 && (compatible_flag
|| input_stack::get_level() == start_level
))
1449 charinfo
*ci
= tok
.get_char(1);
1451 node
*n
= curenv
->make_char_node(ci
);
1459 static node
*do_bracket()
1462 bracket_node
*bn
= new bracket_node
;
1464 int start_level
= input_stack::get_level();
1468 warning(WARN_DELIM
, "missing closing delimiter");
1471 if (tok
.newline()) {
1472 warning(WARN_DELIM
, "missing closing delimiter");
1473 input_stack::push(make_temp_iterator("\n"));
1477 && (compatible_flag
|| input_stack::get_level() == start_level
))
1479 charinfo
*ci
= tok
.get_char(1);
1481 node
*n
= curenv
->make_char_node(ci
);
1489 static int do_name_test()
1493 int start_level
= input_stack::get_level();
1498 if (tok
.newline() || tok
.eof()) {
1499 warning(WARN_DELIM
, "missing closing delimiter");
1500 input_stack::push(make_temp_iterator("\n"));
1504 && (compatible_flag
|| input_stack::get_level() == start_level
))
1510 return some_char
&& !bad_char
;
1513 static int do_expr_test()
1517 int start_level
= input_stack::get_level();
1518 if (!start
.delimiter(1))
1521 // disable all warning and error messages temporarily
1522 int saved_warning_mask
= warning_mask
;
1523 int saved_inhibit_errors
= inhibit_errors
;
1527 int result
= get_number_rigidly(&dummy
, 'u');
1528 warning_mask
= saved_warning_mask
;
1529 inhibit_errors
= saved_inhibit_errors
;
1530 if (tok
== start
&& input_stack::get_level() == start_level
)
1532 // ignore everything up to the delimiter in case we aren't right there
1535 if (tok
.newline() || tok
.eof()) {
1536 warning(WARN_DELIM
, "missing closing delimiter");
1537 input_stack::push(make_temp_iterator("\n"));
1540 if (tok
== start
&& input_stack::get_level() == start_level
)
1547 static node
*do_zero_width()
1551 int start_level
= input_stack::get_level();
1552 environment
env(curenv
);
1553 environment
*oldenv
= curenv
;
1557 if (tok
.newline() || tok
.eof()) {
1558 error("missing closing delimiter");
1562 && (compatible_flag
|| input_stack::get_level() == start_level
))
1567 node
*rev
= env
.extract_output_line();
1575 return new zero_width_node(n
);
1580 // It's undesirable for \Z to change environments, because then
1581 // \n(.w won't work as expected.
1583 static node
*do_zero_width()
1585 node
*rev
= new dummy_node
;
1588 int start_level
= input_stack::get_level();
1591 if (tok
.newline() || tok
.eof()) {
1592 warning(WARN_DELIM
, "missing closing delimiter");
1593 input_stack::push(make_temp_iterator("\n"));
1597 && (compatible_flag
|| input_stack::get_level() == start_level
))
1599 if (!tok
.add_to_node_list(&rev
))
1600 error("invalid token in argument to \\Z");
1609 return new zero_width_node(n
);
1614 token_node
*node::get_token_node()
1625 token_node(const token
&t
);
1627 token_node
*get_token_node();
1634 token_node::token_node(const token
&t
) : tk(t
)
1638 node
*token_node::copy()
1640 return new token_node(tk
);
1643 token_node
*token_node::get_token_node()
1648 int token_node::same(node
*nd
)
1650 return tk
== ((token_node
*)nd
)->tk
;
1653 const char *token_node::type()
1655 return "token_node";
1658 int token_node::force_tprint()
1663 int token_node::is_tag()
1668 token::token() : nd(0), type(TOKEN_EMPTY
)
1677 token::token(const token
&t
)
1678 : nm(t
.nm
), c(t
.c
), val(t
.val
), dim(t
.dim
), type(t
.type
)
1680 // Use two statements to work around bug in SGI C++.
1682 nd
= tem
? tem
->copy() : 0;
1685 void token::operator=(const token
&t
)
1689 // Use two statements to work around bug in SGI C++.
1691 nd
= tem
? tem
->copy() : 0;
1708 return !tok
.newline();
1711 void token::make_space()
1716 void token::make_newline()
1718 type
= TOKEN_NEWLINE
;
1730 int cc
= input_stack::get(&n
);
1731 if (cc
!= escape_char
|| escape_char
== 0) {
1734 case PUSH_GROFF_MODE
:
1735 input_stack::save_compatible_flag(compatible_flag
);
1736 compatible_flag
= 0;
1738 case PUSH_COMP_MODE
:
1739 input_stack::save_compatible_flag(compatible_flag
);
1740 compatible_flag
= 1;
1742 case POP_GROFFCOMP_MODE
:
1743 compatible_flag
= input_stack::get_compatible_flag();
1746 input_stack::increase_level();
1749 input_stack::decrease_level();
1756 case TRANSPARENT_FILE_REQUEST
:
1758 case COPY_FILE_REQUEST
:
1760 case VJUSTIFY_REQUEST
:
1762 type
= TOKEN_REQUEST
;
1766 type
= TOKEN_BEGIN_TRAP
;
1769 type
= TOKEN_END_TRAP
;
1771 case LAST_PAGE_EJECTOR
:
1772 seen_last_page_ejector
= 1;
1775 type
= TOKEN_PAGE_EJECTOR
;
1777 case ESCAPE_PERCENT
:
1779 type
= TOKEN_HYPHEN_INDICATOR
;
1783 type
= TOKEN_UNSTRETCHABLE_SPACE
;
1787 type
= TOKEN_STRETCHABLE_SPACE
;
1791 type
= TOKEN_ZERO_WIDTH_BREAK
;
1795 type
= TOKEN_ESCAPE
;
1798 goto handle_escape_char
;
1802 nd
= new hmotion_node(curenv
->get_narrow_space_width(),
1803 curenv
->get_fill_color());
1805 case ESCAPE_CIRCUMFLEX
:
1808 nd
= new hmotion_node(curenv
->get_half_narrow_space_width(),
1809 curenv
->get_fill_color());
1811 case ESCAPE_NEWLINE
:
1814 case ESCAPE_LEFT_BRACE
:
1816 type
= TOKEN_LEFT_BRACE
;
1818 case ESCAPE_RIGHT_BRACE
:
1820 type
= TOKEN_RIGHT_BRACE
;
1822 case ESCAPE_LEFT_QUOTE
:
1824 type
= TOKEN_SPECIAL
;
1827 case ESCAPE_RIGHT_QUOTE
:
1829 type
= TOKEN_SPECIAL
;
1834 type
= TOKEN_SPECIAL
;
1837 case ESCAPE_UNDERSCORE
:
1839 type
= TOKEN_SPECIAL
;
1844 type
= TOKEN_INTERRUPT
;
1848 type
= TOKEN_TRANSPARENT
;
1850 case ESCAPE_QUESTION
:
1852 nd
= do_non_interpreted();
1858 case ESCAPE_AMPERSAND
:
1862 case ESCAPE_RIGHT_PARENTHESIS
:
1863 ESCAPE_RIGHT_PARENTHESIS
:
1864 type
= TOKEN_TRANSPARENT_DUMMY
;
1867 type
= TOKEN_BACKSPACE
;
1876 type
= TOKEN_NEWLINE
;
1879 type
= TOKEN_LEADER
;
1884 token_node
*tn
= n
->get_token_node();
1903 cc
= input_stack::get(&n
);
1906 nm
= read_two_char_escape_name();
1907 type
= TOKEN_SPECIAL
;
1911 error("end of input after escape character");
1914 goto ESCAPE_LEFT_QUOTE
;
1916 goto ESCAPE_RIGHT_QUOTE
;
1920 goto ESCAPE_UNDERSCORE
;
1922 goto ESCAPE_PERCENT
;
1926 nd
= new hmotion_node(curenv
->get_digit_width(),
1927 curenv
->get_fill_color());
1933 goto ESCAPE_CIRCUMFLEX
;
1935 type
= TOKEN_ITALIC_CORRECTION
;
1939 nd
= new left_italic_corrected_node
;
1942 goto ESCAPE_AMPERSAND
;
1944 goto ESCAPE_RIGHT_PARENTHESIS
;
1948 goto ESCAPE_QUESTION
;
1954 while ((cc
= input_stack::get(0)) != '\n' && cc
!= EOF
)
1957 type
= TOKEN_NEWLINE
;
1961 case '#': // Like \" but newline is ignored.
1962 while ((cc
= input_stack::get(0)) != '\n')
1970 symbol s
= read_escape_name();
1971 if (!(s
.is_null() || s
.is_empty()))
1977 symbol s
= read_escape_name(WITH_ARGS
);
1978 if (!(s
.is_null() || s
.is_empty())) {
1979 if (have_string_arg
) {
1980 have_string_arg
= 0;
1981 interpolate_string_with_args(s
);
1984 interpolate_string(s
);
1989 nd
= new non_interpreted_char_node('\001');
1993 c
= '0' + do_name_test();
2001 c
= '0' + do_expr_test();
2007 nm
= get_delim_name();
2010 type
= TOKEN_SPECIAL
;
2014 nd
= new vmotion_node(curenv
->get_size() / 2,
2015 curenv
->get_fill_color());
2018 nd
= read_draw_node();
2026 goto handle_escape_char
;
2029 symbol s
= read_escape_name(ALLOW_EMPTY
);
2033 for (p
= s
.contents(); *p
!= '\0'; p
++)
2036 if (*p
|| s
.is_empty())
2037 curenv
->set_font(s
);
2039 curenv
->set_font(atoi(s
.contents()));
2040 if (!compatible_flag
)
2046 symbol s
= read_escape_name(ALLOW_EMPTY
);
2049 curenv
->set_family(s
);
2055 symbol s
= read_escape_name();
2056 if (!(s
.is_null() || s
.is_empty()))
2057 interpolate_number_format(s
);
2061 if (!get_delim_number(&x
, 'm'))
2064 nd
= new hmotion_node(x
, curenv
->get_fill_color());
2067 // don't take height increments relative to previous height if
2068 // in compatibility mode
2069 if (!compatible_flag
&& curenv
->get_char_height()) {
2070 if (get_delim_number(&x
, 'z', curenv
->get_char_height()))
2071 curenv
->set_char_height(x
);
2074 if (get_delim_number(&x
, 'z', curenv
->get_requested_point_size()))
2075 curenv
->set_char_height(x
);
2077 if (!compatible_flag
)
2081 nm
= read_escape_name();
2082 if (nm
.is_null() || nm
.is_empty())
2084 type
= TOKEN_MARK_INPUT
;
2090 if (!get_line_arg(&x
, (cc
== 'l' ? 'm': 'v'), &s
))
2093 s
= get_charinfo(cc
== 'l' ? "ru" : "br");
2095 node
*char_node
= curenv
->make_char_node(s
);
2097 nd
= new hline_node(x
, char_node
);
2099 nd
= new vline_node(x
, char_node
);
2103 do_glyph_color(read_escape_name(ALLOW_EMPTY
));
2104 if (!compatible_flag
)
2108 do_fill_color(read_escape_name(ALLOW_EMPTY
));
2109 if (!compatible_flag
)
2115 symbol s
= read_increment_and_escape_name(&inc
);
2116 if (!(s
.is_null() || s
.is_empty()))
2117 interpolate_number_reg(s
, inc
);
2121 if (!get_delim_number(&val
, 0))
2124 warning(WARN_CHAR
, "invalid numbered character %1", val
);
2127 type
= TOKEN_NUMBERED_CHAR
;
2130 nd
= do_overstrike();
2134 nd
= do_suppress(read_escape_name());
2140 type
= TOKEN_SPREAD
;
2144 nd
= new vmotion_node(-curenv
->get_size(), curenv
->get_fill_color());
2148 if (!compatible_flag
)
2153 curenv
->set_size(x
);
2154 if (!compatible_flag
)
2158 if (get_delim_number(&x
, 0))
2159 curenv
->set_char_slant(x
);
2160 if (!compatible_flag
)
2165 nd
= new non_interpreted_char_node('\t');
2169 nd
= new vmotion_node(-curenv
->get_size() / 2,
2170 curenv
->get_fill_color());
2173 if (!get_delim_number(&x
, 'v'))
2176 nd
= new vmotion_node(x
, curenv
->get_fill_color());
2180 symbol s
= read_escape_name();
2181 if (!(s
.is_null() || s
.is_empty()))
2182 interpolate_environment_variable(s
);
2189 if (!get_delim_number(&x
, 'v'))
2192 nd
= new extra_size_node(x
);
2202 symbol s
= read_escape_name();
2203 if (s
.is_null() || s
.is_empty())
2205 request_or_macro
*p
= lookup_request(s
);
2206 macro
*m
= p
->to_macro();
2208 error("can't transparently throughput a request");
2211 nd
= new special_node(*m
);
2218 if (type
== TOKEN_NODE
)
2219 nd
= new zero_width_node(nd
);
2221 charinfo
*ci
= get_char(1);
2224 node
*gn
= curenv
->make_char_node(ci
);
2227 nd
= new zero_width_node(gn
);
2233 nd
= do_zero_width();
2239 goto ESCAPE_LEFT_BRACE
;
2241 goto ESCAPE_RIGHT_BRACE
;
2245 if (!compatible_flag
) {
2246 symbol s
= read_long_escape_name(WITH_ARGS
);
2247 if (s
.is_null() || s
.is_empty())
2249 if (have_string_arg
) {
2250 have_string_arg
= 0;
2251 nm
= composite_glyph_name(s
);
2254 const char *gn
= check_unicode_name(s
.contents());
2256 const char *gn_decomposed
= decompose_unicode(gn
);
2258 gn
= &gn_decomposed
[1];
2259 const char *groff_gn
= unicode_to_glyph_name(gn
);
2261 nm
= symbol(groff_gn
);
2263 char *buf
= new char[strlen(gn
) + 1 + 1];
2271 nm
= symbol(s
.contents());
2273 type
= TOKEN_SPECIAL
;
2276 goto handle_normal_char
;
2278 if (cc
!= escape_char
&& cc
!= '.')
2279 warning(WARN_ESCAPE
, "escape character ignored before %1",
2280 input_char_description(cc
));
2281 goto handle_normal_char
;
2287 int token::operator==(const token
&t
)
2296 case TOKEN_NUMBERED_CHAR
:
2297 return val
== t
.val
;
2303 int token::operator!=(const token
&t
)
2305 return !(*this == t
);
2308 // is token a suitable delimiter (like ')?
2310 int token::delimiter(int err
)
2339 error("cannot use character `%1' as a starting delimiter", char(c
));
2345 // the user doesn't know what a node is
2347 error("missing argument or invalid starting delimiter");
2350 case TOKEN_STRETCHABLE_SPACE
:
2351 case TOKEN_UNSTRETCHABLE_SPACE
:
2355 error("cannot use %1 as a starting delimiter", description());
2362 const char *token::description()
2366 case TOKEN_BACKSPACE
:
2367 return "a backspace character";
2378 case TOKEN_HYPHEN_INDICATOR
:
2380 case TOKEN_INTERRUPT
:
2382 case TOKEN_ITALIC_CORRECTION
:
2385 return "a leader character";
2386 case TOKEN_LEFT_BRACE
:
2388 case TOKEN_MARK_INPUT
:
2394 case TOKEN_NUMBERED_CHAR
:
2396 case TOKEN_RIGHT_BRACE
:
2401 return "a special character";
2404 case TOKEN_STRETCHABLE_SPACE
:
2406 case TOKEN_UNSTRETCHABLE_SPACE
:
2409 return "a tab character";
2410 case TOKEN_TRANSPARENT
:
2412 case TOKEN_TRANSPARENT_DUMMY
:
2414 case TOKEN_ZERO_WIDTH_BREAK
:
2417 return "end of input";
2421 return "a magic token";
2426 while (!tok
.newline())
2437 if (has_arg() && get_integer(&n
))
2438 compatible_flag
= n
!= 0;
2440 compatible_flag
= 1;
2444 static void empty_name_warning(int required
)
2446 if (tok
.newline() || tok
.eof()) {
2448 warning(WARN_MISSING
, "missing name");
2450 else if (tok
.right_brace() || tok
.tab()) {
2451 const char *start
= tok
.description();
2454 } while (tok
.space() || tok
.right_brace() || tok
.tab());
2455 if (!tok
.newline() && !tok
.eof())
2456 error("%1 is not allowed before an argument", start
);
2458 warning(WARN_MISSING
, "missing name");
2461 error("name expected (got %1)", tok
.description());
2463 error("name expected (got %1): treated as missing", tok
.description());
2466 static void non_empty_name_warning()
2468 if (!tok
.newline() && !tok
.eof() && !tok
.space() && !tok
.tab()
2469 && !tok
.right_brace()
2470 // We don't want to give a warning for .el\{
2471 && !tok
.left_brace())
2472 error("%1 is not allowed in a name", tok
.description());
2475 symbol
get_name(int required
)
2477 if (compatible_flag
) {
2480 if ((buf
[0] = tok
.ch()) != 0) {
2482 if ((buf
[1] = tok
.ch()) != 0) {
2487 non_empty_name_warning();
2491 empty_name_warning(required
);
2496 return get_long_name(required
);
2499 symbol
get_long_name(int required
)
2501 return do_get_long_name(required
, 0);
2504 static symbol
do_get_long_name(int required
, char end
)
2508 char abuf
[ABUF_SIZE
];
2510 int buf_size
= ABUF_SIZE
;
2513 // If end != 0 we normally have to append a null byte
2514 if (i
+ 2 > buf_size
) {
2516 buf
= new char[ABUF_SIZE
*2];
2517 memcpy(buf
, abuf
, buf_size
);
2518 buf_size
= ABUF_SIZE
*2;
2521 char *old_buf
= buf
;
2522 buf
= new char[buf_size
*2];
2523 memcpy(buf
, old_buf
, buf_size
);
2528 if ((buf
[i
] = tok
.ch()) == 0 || buf
[i
] == end
)
2534 empty_name_warning(required
);
2537 if (end
&& buf
[i
] == end
)
2540 non_empty_name_warning();
2553 topdiv
->set_last_page();
2554 if (!end_macro_name
.is_null()) {
2555 spring_trap(end_macro_name
);
2557 process_input_stack();
2559 curenv
->final_break();
2561 process_input_stack();
2563 if (topdiv
->get_page_length() > 0) {
2565 topdiv
->set_ejecting();
2566 static unsigned char buf
[2] = { LAST_PAGE_EJECTOR
, '\0' };
2567 input_stack::push(make_temp_iterator((char *)buf
));
2568 topdiv
->space(topdiv
->get_page_length(), 1);
2570 process_input_stack();
2571 seen_last_page_ejector
= 1; // should be set already
2572 topdiv
->set_ejecting();
2573 push_page_ejector();
2574 topdiv
->space(topdiv
->get_page_length(), 1);
2576 process_input_stack();
2578 // This will only happen if a trap-invoked macro starts a diversion,
2579 // or if vertical position traps have been disabled.
2580 cleanup_and_exit(0);
2583 // This implements .ex. The input stack must be cleared before calling
2588 input_stack::clear();
2595 void return_macro_request()
2597 if (has_arg() && tok
.ch())
2598 input_stack::pop_macro();
2599 input_stack::pop_macro();
2605 end_macro_name
= get_name();
2609 void blank_line_macro()
2611 blank_line_macro_name
= get_name();
2615 static void trapping_blank_line()
2617 if (!blank_line_macro_name
.is_null())
2618 spring_trap(blank_line_macro_name
);
2625 int old_compatible_flag
= compatible_flag
;
2626 compatible_flag
= 0;
2627 symbol nm
= get_name();
2631 interpolate_macro(nm
, 1);
2632 compatible_flag
= old_compatible_flag
;
2633 request_or_macro
*p
= lookup_request(nm
);
2634 macro
*m
= p
->to_macro();
2639 inline int possibly_handle_first_page_transition()
2641 if (topdiv
->before_first_page
&& curdiv
== topdiv
&& !curenv
->is_dummy()) {
2642 handle_first_page_transition();
2649 static int transparent_translate(int cc
)
2651 if (!invalid_input_char(cc
)) {
2652 charinfo
*ci
= charset_table
[cc
];
2653 switch (ci
->get_special_translation(1)) {
2654 case charinfo::TRANSLATE_SPACE
:
2656 case charinfo::TRANSLATE_STRETCHABLE_SPACE
:
2657 return ESCAPE_TILDE
;
2658 case charinfo::TRANSLATE_DUMMY
:
2659 return ESCAPE_AMPERSAND
;
2660 case charinfo::TRANSLATE_HYPHEN_INDICATOR
:
2661 return ESCAPE_PERCENT
;
2663 // This is really ugly.
2664 ci
= ci
->get_translation(1);
2666 int c
= ci
->get_ascii_code();
2669 error("can't translate %1 to special character `%2'"
2670 " in transparent throughput",
2671 input_char_description(cc
),
2679 struct int_stack_element
{
2681 int_stack_element
*next
;
2691 int_stack::int_stack()
2696 int_stack::~int_stack()
2699 int_stack_element
*temp
= top
;
2705 int int_stack::is_empty()
2710 void int_stack::push(int n
)
2712 int_stack_element
*p
= new int_stack_element
;
2718 int int_stack::pop()
2721 int_stack_element
*p
= top
;
2728 int node::reread(int *)
2733 int global_diverted_space
= 0;
2735 int diverted_space_node::reread(int *bolp
)
2737 global_diverted_space
= 1;
2738 if (curenv
->get_fill())
2739 trapping_blank_line();
2742 global_diverted_space
= 0;
2747 int diverted_copy_file_node::reread(int *bolp
)
2749 curdiv
->copy_file(filename
.contents());
2754 int word_space_node::reread(int *)
2757 for (width_list
*w
= orig_width
; w
; w
= w
->next
)
2758 curenv
->space(w
->width
, w
->sentence_width
);
2765 int unbreakable_space_node::reread(int *)
2770 int hmotion_node::reread(int *)
2772 if (unformat
&& was_tab
) {
2773 curenv
->handle_tab(0);
2780 void process_input_stack()
2782 int_stack trap_bol_stack
;
2785 int suppress_next
= 0;
2787 case token::TOKEN_CHAR
:
2789 unsigned char ch
= tok
.c
;
2790 if (bol
&& !have_input
2791 && (ch
== curenv
->control_char
2792 || ch
== curenv
->no_break_control_char
)) {
2793 break_flag
= ch
== curenv
->control_char
;
2794 // skip tabs as well as spaces here
2797 } while (tok
.white_space());
2798 symbol nm
= get_name();
2799 #if defined(DEBUGGING)
2801 if (! nm
.is_null()) {
2802 if (strcmp(nm
.contents(), "test") == 0) {
2803 fprintf(stderr
, "found it!\n");
2806 fprintf(stderr
, "interpreting [%s]", nm
.contents());
2807 if (strcmp(nm
.contents(), "di") == 0 && topdiv
!= curdiv
)
2808 fprintf(stderr
, " currently in diversion: %s",
2809 curdiv
->get_diversion_name());
2810 fprintf(stderr
, "\n");
2818 interpolate_macro(nm
);
2819 #if defined(DEBUGGING)
2821 fprintf(stderr
, "finished interpreting [%s] and environment state is\n", nm
.contents());
2822 curenv
->dump_troff_state();
2829 if (possibly_handle_first_page_transition())
2833 #if defined(DEBUGGING)
2835 fprintf(stderr
, "found [%c]\n", ch
); fflush(stderr
);
2838 curenv
->add_char(charset_table
[ch
]);
2840 if (tok
.type
!= token::TOKEN_CHAR
)
2850 case token::TOKEN_TRANSPARENT
:
2853 if (possibly_handle_first_page_transition())
2862 curdiv
->transparent_output(transparent_translate(cc
));
2864 curdiv
->transparent_output(n
);
2866 } while (cc
!= '\n' && cc
!= EOF
);
2868 curdiv
->transparent_output('\n');
2873 case token::TOKEN_NEWLINE
:
2875 if (bol
&& !old_have_input
2876 && !curenv
->get_prev_line_interrupted())
2877 trapping_blank_line();
2884 case token::TOKEN_REQUEST
:
2886 int request_code
= tok
.c
;
2888 switch (request_code
) {
2892 case COPY_FILE_REQUEST
:
2895 case TRANSPARENT_FILE_REQUEST
:
2899 case VJUSTIFY_REQUEST
:
2910 case token::TOKEN_SPACE
:
2912 if (possibly_handle_first_page_transition())
2914 else if (bol
&& !curenv
->get_prev_line_interrupted()) {
2916 // save space_width now so that it isn't changed by \f or \s
2917 // which we wouldn't notice here
2918 hunits space_width
= curenv
->get_space_width();
2920 nspaces
+= tok
.nspaces();
2922 } while (tok
.space());
2924 trapping_blank_line();
2928 curenv
->add_node(new hmotion_node(space_width
* nspaces
,
2929 curenv
->get_fill_color()));
2939 case token::TOKEN_EOF
:
2941 case token::TOKEN_NODE
:
2943 if (possibly_handle_first_page_transition())
2945 else if (tok
.nd
->reread(&bol
)) {
2950 curenv
->add_node(tok
.nd
);
2953 curenv
->possibly_break_line(1);
2957 case token::TOKEN_PAGE_EJECTOR
:
2959 continue_page_eject();
2960 // I think we just want to preserve bol.
2964 case token::TOKEN_BEGIN_TRAP
:
2966 trap_bol_stack
.push(bol
);
2971 case token::TOKEN_END_TRAP
:
2973 if (trap_bol_stack
.is_empty())
2974 error("spurious end trap token detected!");
2976 bol
= trap_bol_stack
.pop();
2979 /* I'm not totally happy about this. But I can't think of any other
2980 way to do it. Doing an output_pending_lines() whenever a
2981 TOKEN_END_TRAP is detected doesn't work: for example,
2994 a\%very\%very\%long\%word
2996 will print all but the first lines from the word immediately
2997 after the footer, rather than on the next page. */
2999 if (trap_bol_stack
.is_empty())
3000 curenv
->output_pending_lines();
3012 trap_sprung_flag
= 0;
3016 #ifdef WIDOW_CONTROL
3018 void flush_pending_lines()
3020 while (!tok
.newline() && !tok
.eof())
3022 curenv
->output_pending_lines();
3026 #endif /* WIDOW_CONTROL */
3028 request_or_macro::request_or_macro()
3032 macro
*request_or_macro::to_macro()
3037 request::request(REQUEST_FUNCP pp
) : p(pp
)
3041 void request::invoke(symbol
, int)
3049 enum { SIZE
= 128 };
3050 unsigned char s
[SIZE
];
3056 char_block::char_block()
3063 friend class macro_header
;
3064 friend class string_iterator
;
3074 void append(unsigned char);
3075 void set(unsigned char, int);
3076 unsigned char get(int);
3080 char_list::char_list()
3081 : ptr(0), len(0), head(0), tail(0)
3085 char_list::~char_list()
3088 char_block
*tem
= head
;
3094 int char_list::length()
3099 void char_list::append(unsigned char c
)
3102 head
= tail
= new char_block
;
3106 if (ptr
>= tail
->s
+ char_block::SIZE
) {
3107 tail
->next
= new char_block
;
3116 void char_list::set(unsigned char c
, int offset
)
3118 assert(len
> offset
);
3119 // optimization for access at the end
3120 int boundary
= len
- len
% char_block::SIZE
;
3121 if (offset
>= boundary
) {
3122 *(tail
->s
+ offset
- boundary
) = c
;
3125 char_block
*tem
= head
;
3128 l
+= char_block::SIZE
;
3130 *(tem
->s
+ offset
% char_block::SIZE
) = c
;
3137 unsigned char char_list::get(int offset
)
3139 assert(len
> offset
);
3140 // optimization for access at the end
3141 int boundary
= len
- len
% char_block::SIZE
;
3142 if (offset
>= boundary
)
3143 return *(tail
->s
+ offset
- boundary
);
3144 char_block
*tem
= head
;
3147 l
+= char_block::SIZE
;
3149 return *(tem
->s
+ offset
% char_block::SIZE
);
3156 friend class macro_header
;
3157 friend class string_iterator
;
3165 void append(node
*);
3170 void node_list::append(node
*n
)
3178 tail
= tail
->next
= n
;
3182 int node_list::length()
3185 for (node
*n
= head
; n
!= 0; n
= n
->next
)
3190 node_list::node_list()
3195 node
*node_list::extract()
3202 node_list::~node_list()
3204 delete_node_list(head
);
3214 macro_header() { count
= 1; }
3215 macro_header
*copy(int);
3220 if (p
!= 0 && --(p
->count
) <= 0)
3225 : is_a_diversion(0), is_a_string(1)
3227 if (!input_stack::get_location(1, &filename
, &lineno
)) {
3236 macro::macro(const macro
&m
)
3237 : request_or_macro(), filename(m
.filename
), lineno(m
.lineno
), len(m
.len
),
3238 empty_macro(m
.empty_macro
), is_a_diversion(m
.is_a_diversion
),
3239 is_a_string(m
.is_a_string
), p(m
.p
)
3245 macro::macro(int is_div
)
3246 : is_a_diversion(is_div
)
3248 if (!input_stack::get_location(1, &filename
, &lineno
)) {
3258 int macro::is_diversion()
3260 return is_a_diversion
;
3263 int macro::is_string()
3268 void macro::clear_string_flag()
3273 macro
¯o::operator=(const macro
&m
)
3275 // don't assign object
3278 if (p
!= 0 && --(p
->count
) <= 0)
3281 filename
= m
.filename
;
3284 empty_macro
= m
.empty_macro
;
3285 is_a_diversion
= m
.is_a_diversion
;
3286 is_a_string
= m
.is_a_string
;
3290 void macro::append(unsigned char c
)
3294 p
= new macro_header
;
3295 if (p
->cl
.length() != len
) {
3296 macro_header
*tem
= p
->copy(len
);
3297 if (--(p
->count
) <= 0)
3303 if (c
!= PUSH_GROFF_MODE
&& c
!= PUSH_COMP_MODE
&& c
!= POP_GROFFCOMP_MODE
)
3307 void macro::set(unsigned char c
, int offset
)
3311 p
->cl
.set(c
, offset
);
3314 unsigned char macro::get(int offset
)
3317 return p
->cl
.get(offset
);
3325 void macro::append_str(const char *s
)
3330 while (s
[i
] != (char)0) {
3337 void macro::append(node
*n
)
3341 p
= new macro_header
;
3342 if (p
->cl
.length() != len
) {
3343 macro_header
*tem
= p
->copy(len
);
3344 if (--(p
->count
) <= 0)
3354 void macro::append_unsigned(unsigned int i
)
3356 unsigned int j
= i
/ 10;
3359 append(((unsigned char)(((int)'0') + i
% 10)));
3362 void macro::append_int(int i
)
3368 append_unsigned((unsigned int)i
);
3371 void macro::print_size()
3373 errprint("%1", len
);
3376 // make a copy of the first n bytes
3378 macro_header
*macro_header::copy(int n
)
3380 macro_header
*p
= new macro_header
;
3381 char_block
*bp
= cl
.head
;
3382 unsigned char *ptr
= bp
->s
;
3385 if (ptr
>= bp
->s
+ char_block::SIZE
) {
3389 unsigned char c
= *ptr
++;
3392 p
->nl
.append(nd
->copy());
3401 object_dictionary_iterator
iter(request_dictionary
);
3402 request_or_macro
*rm
;
3404 while (iter
.get(&s
, (object
**)&rm
)) {
3405 assert(!s
.is_null());
3406 macro
*m
= rm
->to_macro();
3408 errprint("%1\t", s
.contents());
3417 class string_iterator
3418 : public input_iterator
3421 const char *how_invoked
;
3425 int count
; // of characters remaining
3427 int saved_compatible_flag
;
3428 int with_break
; // inherited from the caller
3436 string_iterator(const macro
&, const char * = 0, symbol
= NULL_SYMBOL
);
3439 int get_location(int, const char **, int *);
3441 int get_break_flag() { return with_break
; }
3442 void save_compatible_flag(int f
) { saved_compatible_flag
= f
; }
3443 int get_compatible_flag() { return saved_compatible_flag
; }
3447 string_iterator::string_iterator(const macro
&m
, const char *p
, symbol s
)
3448 : input_iterator(m
.is_a_diversion
), mac(m
), how_invoked(p
), newline_flag(0),
3453 bp
= mac
.p
->cl
.head
;
3454 nd
= mac
.p
->nl
.head
;
3462 with_break
= input_stack::get_break_flag();
3465 string_iterator::string_iterator()
3474 with_break
= input_stack::get_break_flag();
3477 int string_iterator::is_diversion()
3479 return mac
.is_diversion();
3482 int string_iterator::fill(node
**np
)
3489 const unsigned char *p
= eptr
;
3490 if (p
>= bp
->s
+ char_block::SIZE
) {
3498 (*np
)->div_nest_level
= input_stack::get_div_level();
3500 (*np
)->div_nest_level
= 0;
3507 const unsigned char *e
= bp
->s
+ char_block::SIZE
;
3512 unsigned char c
= *p
;
3513 if (c
== '\n' || c
== ESCAPE_NEWLINE
) {
3527 int string_iterator::peek()
3531 const unsigned char *p
= eptr
;
3532 if (p
>= bp
->s
+ char_block::SIZE
) {
3538 int string_iterator::get_location(int allow_macro
,
3539 const char **filep
, int *linep
)
3543 if (mac
.filename
== 0)
3545 *filep
= mac
.filename
;
3546 *linep
= mac
.lineno
+ lineno
- 1;
3550 void string_iterator::backtrace()
3553 errprint("%1:%2: backtrace", mac
.filename
, mac
.lineno
+ lineno
- 1);
3556 errprint(": %1 `%2'\n", how_invoked
, nm
.contents());
3558 errprint(": %1\n", how_invoked
);
3566 : public input_iterator
3568 friend input_iterator
*make_temp_iterator(const char *);
3570 unsigned char *base
;
3572 temp_iterator(const char *, int len
);
3578 temp_iterator::temp_iterator(const char *s
, int len
)
3580 base
= new unsigned char[len
];
3581 memcpy(base
, s
, len
);
3586 temp_iterator::~temp_iterator()
3591 class small_temp_iterator
3592 : public input_iterator
3594 friend input_iterator
*make_temp_iterator(const char *);
3596 static small_temp_iterator
*free_list
;
3598 enum { BLOCK
= 16 };
3600 unsigned char buf
[SIZE
];
3602 private: // TODO class all private; ok (has friend: public static? !!), but
3603 small_temp_iterator(const char *, int);
3604 ~small_temp_iterator();
3606 void *operator new(size_t);
3607 void operator delete(void *);
3610 small_temp_iterator
*small_temp_iterator::free_list
= 0;
3612 void *small_temp_iterator::operator new(size_t n
) // TODO -> object cache
3614 assert(n
== sizeof(small_temp_iterator
));
3617 (small_temp_iterator
*)new char[sizeof(small_temp_iterator
)*BLOCK
];
3618 for (int i
= 0; i
< BLOCK
- 1; i
++)
3619 free_list
[i
].next
= free_list
+ i
+ 1;
3620 free_list
[BLOCK
-1].next
= 0;
3622 small_temp_iterator
*p
= free_list
;
3623 free_list
= (small_temp_iterator
*)(free_list
->next
);
3629 void small_temp_iterator::operator delete(void *p
) // TODO -> nope
3632 ((small_temp_iterator
*)p
)->next
= free_list
;
3633 free_list
= (small_temp_iterator
*)p
;
3637 small_temp_iterator::~small_temp_iterator()
3642 small_temp_iterator::small_temp_iterator(const char *s
, int len
)
3644 for (int i
= 0; i
< len
; i
++)
3650 input_iterator
*make_temp_iterator(const char *s
)
3653 return new small_temp_iterator(s
, 0);
3656 if (n
<= small_temp_iterator::SIZE
)
3657 return new small_temp_iterator(s
, n
);
3659 return new temp_iterator(s
, n
);
3663 // this is used when macros with arguments are interpolated
3672 arg_list(const macro
&, int);
3673 arg_list(const arg_list
*);
3677 arg_list::arg_list(const macro
&m
, int s
) : mac(m
), space_follows(s
), next(0)
3681 arg_list::arg_list(const arg_list
*al
)
3685 space_follows
= al
->space_follows
;
3686 arg_list
**a
= &next
;
3687 arg_list
*p
= al
->next
;
3689 *a
= new arg_list(p
->mac
, p
->space_follows
);
3695 arg_list::~arg_list()
3699 class macro_iterator
3700 : public string_iterator
3704 int with_break
; // whether called as .foo or 'foo
3707 macro_iterator(symbol
, macro
&, const char * = "macro", int = 0);
3710 int has_args() { return 1; }
3711 input_iterator
*get_arg(int);
3712 arg_list
*get_arg_list();
3713 symbol
get_macro_name();
3714 int space_follows_arg(int);
3715 int get_break_flag() { return with_break
; }
3716 int nargs() { return argc
; }
3717 void add_arg(const macro
&, int);
3719 int is_macro() { return 1; }
3723 input_iterator
*macro_iterator::get_arg(int i
)
3726 return make_temp_iterator(nm
.contents());
3727 if (i
> 0 && i
<= argc
) {
3729 for (int j
= 1; j
< i
; j
++) {
3733 return new string_iterator(p
->mac
);
3739 arg_list
*macro_iterator::get_arg_list()
3744 symbol
macro_iterator::get_macro_name()
3749 int macro_iterator::space_follows_arg(int i
)
3751 if (i
> 0 && i
<= argc
) {
3753 for (int j
= 1; j
< i
; j
++) {
3757 return p
->space_follows
;
3763 void macro_iterator::add_arg(const macro
&m
, int s
)
3766 for (p
= &args
; *p
; p
= &((*p
)->next
))
3768 *p
= new arg_list(m
, s
);
3772 void macro_iterator::shift(int n
)
3774 while (n
> 0 && argc
> 0) {
3775 arg_list
*tem
= args
;
3783 // This gets used by eg .if '\?xxx\?''.
3784 int operator==(const macro
&m1
, const macro
&m2
)
3786 if (m1
.len
!= m2
.len
)
3788 string_iterator
iter1(m1
);
3789 string_iterator
iter2(m2
);
3793 int c1
= iter1
.get(&nd1
);
3796 int c2
= iter2
.get(&nd2
);
3808 int are_same
= nd1
->type() == nd2
->type() && nd1
->same(nd2
);
3818 static void interpolate_macro(symbol nm
, int no_next
)
3820 request_or_macro
*p
= (request_or_macro
*)request_dictionary
.lookup(nm
);
3823 const char *s
= nm
.contents();
3824 if (strlen(s
) > 2) {
3825 request_or_macro
*r
;
3830 r
= (request_or_macro
*)request_dictionary
.lookup(symbol(buf
));
3832 macro
*m
= r
->to_macro();
3833 if (!m
|| !m
->empty())
3834 warned
= warning(WARN_SPACE
,
3835 "macro `%1' not defined "
3836 "(possibly missing space after `%2')",
3837 nm
.contents(), buf
);
3841 warning(WARN_MAC
, "macro `%1' not defined", nm
.contents());
3843 request_dictionary
.define(nm
, p
);
3847 p
->invoke(nm
, no_next
);
3854 static void decode_args(macro_iterator
*mi
)
3856 if (!tok
.newline() && !tok
.eof()) {
3858 int c
= get_copy(&n
);
3862 if (c
== '\n' || c
== EOF
)
3865 int quote_input_level
= 0;
3866 int done_tab_warning
= 0;
3867 arg
.append(compatible_flag
? PUSH_COMP_MODE
: PUSH_GROFF_MODE
);
3868 // we store discarded double quotes for \$^
3870 arg
.append(DOUBLE_QUOTE
);
3871 quote_input_level
= input_stack::get_level();
3874 while (c
!= EOF
&& c
!= '\n' && !(c
== ' ' && quote_input_level
== 0)) {
3875 if (quote_input_level
> 0 && c
== '"'
3877 || input_stack::get_level() == quote_input_level
)) {
3878 arg
.append(DOUBLE_QUOTE
);
3891 if (c
== '\t' && quote_input_level
== 0 && !done_tab_warning
) {
3892 warning(WARN_TAB
, "tab character in unquoted macro argument");
3893 done_tab_warning
= 1;
3900 arg
.append(POP_GROFFCOMP_MODE
);
3901 mi
->add_arg(arg
, (c
== ' '));
3906 static void decode_string_args(macro_iterator
*mi
)
3909 int c
= get_copy(&n
);
3913 if (c
== '\n' || c
== EOF
) {
3914 error("missing `]'");
3920 int quote_input_level
= 0;
3921 int done_tab_warning
= 0;
3923 quote_input_level
= input_stack::get_level();
3926 while (c
!= EOF
&& c
!= '\n'
3927 && !(c
== ']' && quote_input_level
== 0)
3928 && !(c
== ' ' && quote_input_level
== 0)) {
3929 if (quote_input_level
> 0 && c
== '"'
3930 && input_stack::get_level() == quote_input_level
) {
3943 if (c
== '\t' && quote_input_level
== 0 && !done_tab_warning
) {
3944 warning(WARN_TAB
, "tab character in unquoted string argument");
3945 done_tab_warning
= 1;
3952 mi
->add_arg(arg
, (c
== ' '));
3956 void macro::invoke(symbol nm
, int no_next
)
3958 macro_iterator
*mi
= new macro_iterator(nm
, *this);
3960 input_stack::push(mi
);
3961 // we must delay tok.next() in case the function has been called by
3962 // do_request to assure proper handling of compatible_flag
3967 macro
*macro::to_macro()
3974 return empty_macro
== 1;
3977 macro_iterator::macro_iterator(symbol s
, macro
&m
, const char *how_called
,
3979 : string_iterator(m
, how_called
, s
), args(0), argc(0), with_break(break_flag
)
3982 arg_list
*al
= input_stack::get_arg_list();
3984 args
= new arg_list(al
);
3985 argc
= input_stack::nargs();
3990 macro_iterator::macro_iterator() : args(0), argc(0), with_break(break_flag
)
3994 macro_iterator::~macro_iterator()
3997 arg_list
*tem
= args
;
4003 dictionary
composite_dictionary(17);
4005 void composite_request()
4007 symbol from
= get_name(1);
4008 if (!from
.is_null()) {
4009 const char *from_gn
= glyph_name_to_unicode(from
.contents());
4011 from_gn
= check_unicode_name(from
.contents());
4013 error("invalid composite glyph name `%1'", from
.contents());
4018 const char *from_decomposed
= decompose_unicode(from_gn
);
4019 if (from_decomposed
)
4020 from_gn
= &from_decomposed
[1];
4021 symbol to
= get_name(1);
4023 composite_dictionary
.remove(symbol(from_gn
));
4025 const char *to_gn
= glyph_name_to_unicode(to
.contents());
4027 to_gn
= check_unicode_name(to
.contents());
4029 error("invalid composite glyph name `%1'", to
.contents());
4034 const char *to_decomposed
= decompose_unicode(to_gn
);
4036 to_gn
= &to_decomposed
[1];
4037 if (strcmp(from_gn
, to_gn
) == 0)
4038 composite_dictionary
.remove(symbol(from_gn
));
4040 (void)composite_dictionary
.lookup(symbol(from_gn
), (void *)to_gn
);
4046 static symbol
composite_glyph_name(symbol nm
)
4048 macro_iterator
*mi
= new macro_iterator();
4049 decode_string_args(mi
);
4050 input_stack::push(mi
);
4051 const char *gn
= glyph_name_to_unicode(nm
.contents());
4053 gn
= check_unicode_name(nm
.contents());
4055 error("invalid base glyph `%1' in composite glyph name", nm
.contents());
4056 return EMPTY_SYMBOL
;
4059 const char *gn_decomposed
= decompose_unicode(gn
);
4060 string
glyph_name(gn_decomposed
? &gn_decomposed
[1] : gn
);
4062 int n
= input_stack::nargs();
4063 for (int i
= 1; i
<= n
; i
++) {
4065 input_iterator
*p
= input_stack::get_arg(i
);
4068 while ((c
= p
->get(0)) != EOF
)
4069 if (c
!= DOUBLE_QUOTE
)
4072 const char *u
= glyph_name_to_unicode(gl
.contents());
4074 u
= check_unicode_name(gl
.contents());
4076 error("invalid component `%1' in composite glyph name",
4078 return EMPTY_SYMBOL
;
4081 const char *decomposed
= decompose_unicode(u
);
4084 void *mapped_composite
= composite_dictionary
.lookup(symbol(u
));
4085 if (mapped_composite
)
4086 u
= (const char *)mapped_composite
;
4090 const char *groff_gn
= unicode_to_glyph_name(glyph_name
.contents());
4092 return symbol(groff_gn
);
4096 return symbol(gl
.contents());
4099 int trap_sprung_flag
= 0;
4100 int postpone_traps_flag
= 0;
4101 symbol postponed_trap
;
4103 void spring_trap(symbol nm
)
4105 assert(!nm
.is_null());
4106 trap_sprung_flag
= 1;
4107 if (postpone_traps_flag
) {
4108 postponed_trap
= nm
;
4111 static char buf
[2] = { BEGIN_TRAP
, 0 };
4112 static char buf2
[2] = { END_TRAP
, '\0' };
4113 input_stack::push(make_temp_iterator(buf2
));
4114 request_or_macro
*p
= lookup_request(nm
);
4115 macro
*m
= p
->to_macro();
4117 input_stack::push(new macro_iterator(nm
, *m
, "trap-invoked macro"));
4119 error("you can't invoke a request with a trap");
4120 input_stack::push(make_temp_iterator(buf
));
4123 void postpone_traps()
4125 postpone_traps_flag
= 1;
4128 int unpostpone_traps()
4130 postpone_traps_flag
= 0;
4131 if (!postponed_trap
.is_null()) {
4132 spring_trap(postponed_trap
);
4133 postponed_trap
= NULL_SYMBOL
;
4142 macro_iterator
*mi
= new macro_iterator
;
4143 int reading_from_terminal
= isatty(fileno(stdin
));
4145 if (!tok
.newline() && !tok
.eof()) {
4146 int c
= get_copy(0);
4149 while (c
!= EOF
&& c
!= '\n' && c
!= ' ') {
4150 if (!invalid_input_char(c
)) {
4151 if (reading_from_terminal
)
4162 if (reading_from_terminal
) {
4163 fputc(had_prompt
? ':' : '\a', stderr
);
4166 input_stack::push(mi
);
4170 while ((c
= getchar()) != EOF
) {
4171 if (invalid_input_char(c
))
4172 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
4185 if (reading_from_terminal
)
4187 input_stack::push(new string_iterator(mac
));
4191 enum define_mode
{ DEFINE_NORMAL
, DEFINE_APPEND
, DEFINE_IGNORE
};
4192 enum calling_mode
{ CALLING_NORMAL
, CALLING_INDIRECT
};
4193 enum comp_mode
{ COMP_IGNORE
, COMP_DISABLE
, COMP_ENABLE
};
4195 void do_define_string(define_mode mode
, comp_mode comp
)
4198 node
*n
= 0; // pacify compiler
4209 else if (!tok
.space()) {
4210 error("bad string definition");
4221 request_or_macro
*rm
= (request_or_macro
*)request_dictionary
.lookup(nm
);
4222 macro
*mm
= rm
? rm
->to_macro() : 0;
4223 if (mode
== DEFINE_APPEND
&& mm
)
4225 if (comp
== COMP_DISABLE
)
4226 mac
.append(PUSH_GROFF_MODE
);
4227 else if (comp
== COMP_ENABLE
)
4228 mac
.append(PUSH_COMP_MODE
);
4229 while (c
!= '\n' && c
!= EOF
) {
4233 mac
.append((unsigned char)c
);
4236 if (comp
== COMP_DISABLE
|| comp
== COMP_ENABLE
)
4237 mac
.append(POP_GROFFCOMP_MODE
);
4240 request_dictionary
.define(nm
, mm
);
4246 void define_string()
4248 do_define_string(DEFINE_NORMAL
,
4249 compatible_flag
? COMP_ENABLE
: COMP_IGNORE
);
4252 void define_nocomp_string()
4254 do_define_string(DEFINE_NORMAL
, COMP_DISABLE
);
4257 void append_string()
4259 do_define_string(DEFINE_APPEND
,
4260 compatible_flag
? COMP_ENABLE
: COMP_IGNORE
);
4263 void append_nocomp_string()
4265 do_define_string(DEFINE_APPEND
, COMP_DISABLE
);
4268 void do_define_character(char_mode mode
, const char *font_name
)
4270 node
*n
= 0; // pacify compiler
4273 charinfo
*ci
= tok
.get_char(1);
4279 string
s(font_name
);
4281 s
+= ci
->nm
.contents();
4283 ci
= get_charinfo(symbol(s
.contents()));
4290 else if (!tok
.space()) {
4291 error("bad character definition");
4297 while (c
== ' ' || c
== '\t')
4301 macro
*m
= new macro
;
4302 while (c
!= '\n' && c
!= EOF
) {
4306 m
->append((unsigned char)c
);
4309 m
= ci
->setx_macro(m
, mode
);
4315 void define_character()
4317 do_define_character(CHAR_NORMAL
);
4320 void define_fallback_character()
4322 do_define_character(CHAR_FALLBACK
);
4325 void define_special_character()
4327 do_define_character(CHAR_SPECIAL
);
4330 static void remove_character()
4333 while (!tok
.newline() && !tok
.eof()) {
4334 if (!tok
.space() && !tok
.tab()) {
4335 charinfo
*ci
= tok
.get_char(1);
4338 macro
*m
= ci
->set_macro(0);
4347 static void interpolate_string(symbol nm
)
4349 request_or_macro
*p
= lookup_request(nm
);
4350 macro
*m
= p
->to_macro();
4352 error("you can only invoke a string or macro using \\*");
4354 if (m
->is_string()) {
4355 string_iterator
*si
= new string_iterator(*m
, "string", nm
);
4356 input_stack::push(si
);
4359 // if a macro is called as a string, \$0 doesn't get changed
4360 macro_iterator
*mi
= new macro_iterator(input_stack::get_macro_name(),
4362 input_stack::push(mi
);
4367 static void interpolate_string_with_args(symbol s
)
4369 request_or_macro
*p
= lookup_request(s
);
4370 macro
*m
= p
->to_macro();
4372 error("you can only invoke a string or macro using \\*");
4374 macro_iterator
*mi
= new macro_iterator(s
, *m
);
4375 decode_string_args(mi
);
4376 input_stack::push(mi
);
4380 static void interpolate_arg(symbol nm
)
4382 const char *s
= nm
.contents();
4383 if (!s
|| *s
== '\0')
4384 copy_mode_error("missing argument name");
4385 else if (s
[1] == 0 && csdigit(s
[0]))
4386 input_stack::push(input_stack::get_arg(s
[0] - '0'));
4387 else if (s
[0] == '*' && s
[1] == '\0') {
4388 int limit
= input_stack::nargs();
4390 for (int i
= 1; i
<= limit
; i
++) {
4391 input_iterator
*p
= input_stack::get_arg(i
);
4393 while ((c
= p
->get(0)) != EOF
)
4394 if (c
!= DOUBLE_QUOTE
)
4401 input_stack::push(make_temp_iterator(args
.contents()));
4404 else if (s
[0] == '@' && s
[1] == '\0') {
4405 int limit
= input_stack::nargs();
4407 for (int i
= 1; i
<= limit
; i
++) {
4409 args
+= char(BEGIN_QUOTE
);
4410 input_iterator
*p
= input_stack::get_arg(i
);
4412 while ((c
= p
->get(0)) != EOF
)
4413 if (c
!= DOUBLE_QUOTE
)
4415 args
+= char(END_QUOTE
);
4422 input_stack::push(make_temp_iterator(args
.contents()));
4425 else if (s
[0] == '^' && s
[1] == '\0') {
4426 int limit
= input_stack::nargs();
4428 int c
= input_stack::peek();
4429 for (int i
= 1; i
<= limit
; i
++) {
4430 input_iterator
*p
= input_stack::get_arg(i
);
4431 while ((c
= p
->get(0)) != EOF
) {
4432 if (c
== DOUBLE_QUOTE
)
4436 if (input_stack::space_follows_arg(i
))
4441 input_stack::push(make_temp_iterator(args
.contents()));
4446 for (p
= s
; *p
&& csdigit(*p
); p
++)
4449 copy_mode_error("bad argument name `%1'", s
);
4451 input_stack::push(input_stack::get_arg(atoi(s
)));
4455 void handle_first_page_transition()
4458 topdiv
->begin_page();
4461 // We push back a token by wrapping it up in a token_node, and
4462 // wrapping that up in a string_iterator.
4464 static void push_token(const token
&t
)
4467 m
.append(new token_node(t
));
4468 input_stack::push(new string_iterator(m
));
4471 void push_page_ejector()
4473 static char buf
[2] = { PAGE_EJECTOR
, '\0' };
4474 input_stack::push(make_temp_iterator(buf
));
4477 void handle_initial_request(unsigned char code
)
4483 mac
.append(new token_node(tok
));
4484 input_stack::push(new string_iterator(mac
));
4485 input_stack::push(make_temp_iterator(buf
));
4486 topdiv
->begin_page();
4490 void handle_initial_title()
4492 handle_initial_request(TITLE_REQUEST
);
4495 // this should be local to define_macro, but cfront 1.2 doesn't support that
4496 static symbol
dot_symbol(".");
4498 void do_define_macro(define_mode mode
, calling_mode calling
, comp_mode comp
)
4501 if (calling
== CALLING_INDIRECT
) {
4502 symbol temp1
= get_name(1);
4503 if (temp1
.is_null()) {
4507 symbol temp2
= get_name();
4508 input_stack::push(make_temp_iterator("\n"));
4509 if (!temp2
.is_null()) {
4510 interpolate_string(temp2
);
4511 input_stack::push(make_temp_iterator(" "));
4513 interpolate_string(temp1
);
4514 input_stack::push(make_temp_iterator(" "));
4517 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4524 term
= get_name(); // the request that terminates the definition
4527 while (!tok
.newline() && !tok
.eof())
4529 const char *start_filename
;
4531 int have_start_location
= input_stack::get_location(0, &start_filename
,
4534 // doing this here makes the line numbers come out right
4535 int c
= get_copy(&n
, 1);
4538 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4539 request_or_macro
*rm
=
4540 (request_or_macro
*)request_dictionary
.lookup(nm
);
4542 mm
= rm
->to_macro();
4543 if (mm
&& mode
== DEFINE_APPEND
)
4547 if (comp
== COMP_DISABLE
)
4548 mac
.append(PUSH_GROFF_MODE
);
4549 else if (comp
== COMP_ENABLE
)
4550 mac
.append(PUSH_COMP_MODE
);
4553 mac
.clear_string_flag();
4554 while (c
== ESCAPE_NEWLINE
) {
4555 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
)
4557 c
= get_copy(&n
, 1);
4559 if (bol
&& c
== '.') {
4560 const char *s
= term
.contents();
4562 // see if it matches term
4565 while ((d
= get_copy(&n
)) == ' ' || d
== '\t')
4567 if ((unsigned char)s
[0] == d
) {
4568 for (i
= 1; s
[i
] != 0; i
++) {
4570 if ((unsigned char)s
[i
] != d
)
4576 && ((i
== 2 && compatible_flag
)
4577 || (d
= get_copy(&n
)) == ' '
4578 || d
== '\n')) { // we found it
4583 if (mode
== DEFINE_APPEND
|| mode
== DEFINE_NORMAL
) {
4586 request_dictionary
.define(nm
, mm
);
4588 if (comp
== COMP_DISABLE
|| comp
== COMP_ENABLE
)
4589 mac
.append(POP_GROFFCOMP_MODE
);
4592 if (term
!= dot_symbol
) {
4594 interpolate_macro(term
);
4600 if (mode
== DEFINE_APPEND
|| mode
== DEFINE_NORMAL
) {
4602 for (int j
= 0; j
< i
; j
++)
4608 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4609 if (have_start_location
)
4610 error_with_file_and_line(start_filename
, start_lineno
,
4611 "end of file while defining macro `%1'",
4614 error("end of file while defining macro `%1'", nm
.contents());
4617 if (have_start_location
)
4618 error_with_file_and_line(start_filename
, start_lineno
,
4619 "end of file while ignoring input lines");
4621 error("end of file while ignoring input lines");
4626 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
4633 c
= get_copy(&n
, 1);
4639 do_define_macro(DEFINE_NORMAL
, CALLING_NORMAL
,
4640 compatible_flag
? COMP_ENABLE
: COMP_IGNORE
);
4643 void define_nocomp_macro()
4645 do_define_macro(DEFINE_NORMAL
, CALLING_NORMAL
, COMP_DISABLE
);
4648 void define_indirect_macro()
4650 do_define_macro(DEFINE_NORMAL
, CALLING_INDIRECT
,
4651 compatible_flag
? COMP_ENABLE
: COMP_IGNORE
);
4654 void define_indirect_nocomp_macro()
4656 do_define_macro(DEFINE_NORMAL
, CALLING_INDIRECT
, COMP_DISABLE
);
4661 do_define_macro(DEFINE_APPEND
, CALLING_NORMAL
,
4662 compatible_flag
? COMP_ENABLE
: COMP_IGNORE
);
4665 void append_nocomp_macro()
4667 do_define_macro(DEFINE_APPEND
, CALLING_NORMAL
, COMP_DISABLE
);
4670 void append_indirect_macro()
4672 do_define_macro(DEFINE_APPEND
, CALLING_INDIRECT
,
4673 compatible_flag
? COMP_ENABLE
: COMP_IGNORE
);
4676 void append_indirect_nocomp_macro()
4678 do_define_macro(DEFINE_APPEND
, CALLING_INDIRECT
, COMP_DISABLE
);
4684 do_define_macro(DEFINE_IGNORE
, CALLING_NORMAL
, COMP_IGNORE
);
4691 symbol s
= get_name();
4694 request_dictionary
.remove(s
);
4701 symbol s1
= get_name(1);
4702 if (!s1
.is_null()) {
4703 symbol s2
= get_name(1);
4705 request_dictionary
.rename(s1
, s2
);
4712 symbol s1
= get_name(1);
4713 if (!s1
.is_null()) {
4714 symbol s2
= get_name(1);
4715 if (!s2
.is_null()) {
4716 if (!request_dictionary
.alias(s1
, s2
))
4717 warning(WARN_MAC
, "macro `%1' not defined", s2
.contents());
4725 symbol s
= get_name(1);
4727 request_or_macro
*p
= lookup_request(s
);
4728 macro
*m
= p
->to_macro();
4730 error("cannot chop request");
4731 else if (m
->empty())
4732 error("cannot chop empty macro");
4734 int have_restore
= 0;
4735 // we have to check for additional save/restore pairs which could be
4736 // there due to empty am1 requests.
4738 if (m
->get(m
->len
- 1) != POP_GROFFCOMP_MODE
)
4742 if (m
->get(m
->len
- 1) != PUSH_GROFF_MODE
4743 && m
->get(m
->len
- 1) != PUSH_COMP_MODE
)
4751 error("cannot chop empty macro");
4754 m
->set(POP_GROFFCOMP_MODE
, m
->len
- 1);
4763 void substring_request()
4765 int start
; // 0, 1, ..., n-1 or -1, -2, ...
4766 symbol s
= get_name(1);
4767 if (!s
.is_null() && get_integer(&start
)) {
4768 request_or_macro
*p
= lookup_request(s
);
4769 macro
*m
= p
->to_macro();
4771 error("cannot apply `substring' on a request");
4774 if (!has_arg() || get_integer(&end
)) {
4775 int real_length
= 0; // 1, 2, ..., n
4776 string_iterator
iter1(*m
);
4777 for (int l
= 0; l
< m
->len
; l
++) {
4778 int c
= iter1
.get(0);
4779 if (c
== PUSH_GROFF_MODE
4780 || c
== PUSH_COMP_MODE
4781 || c
== POP_GROFFCOMP_MODE
)
4788 start
+= real_length
;
4796 if (start
>= real_length
|| end
< 0) {
4798 "start and end index of substring out of range");
4801 if (--(m
->p
->count
) <= 0)
4810 "start index of substring out of range, set to 0");
4813 if (end
>= real_length
) {
4815 "end index of substring out of range, set to string length");
4816 end
= real_length
- 1;
4818 // now extract the substring
4819 string_iterator
iter(*m
);
4821 for (i
= 0; i
< start
; i
++) {
4822 int c
= iter
.get(0);
4823 while (c
== PUSH_GROFF_MODE
4824 || c
== PUSH_COMP_MODE
4825 || c
== POP_GROFFCOMP_MODE
)
4831 for (; i
<= end
; i
++) {
4832 node
*nd
= 0; // pacify compiler
4833 int c
= iter
.get(&nd
);
4834 while (c
== PUSH_GROFF_MODE
4835 || c
== PUSH_COMP_MODE
4836 || c
== POP_GROFFCOMP_MODE
)
4843 mac
.append((unsigned char)c
);
4852 void length_request()
4856 if (ret
.is_null()) {
4866 else if (!tok
.space()) {
4867 error("bad string definition");
4878 while (c
!= '\n' && c
!= EOF
) {
4882 reg
*r
= (reg
*)number_reg_dictionary
.lookup(ret
);
4886 set_number_reg(ret
, len
);
4890 void asciify_macro()
4892 symbol s
= get_name(1);
4894 request_or_macro
*p
= lookup_request(s
);
4895 macro
*m
= p
->to_macro();
4897 error("cannot asciify request");
4900 string_iterator
iter(*m
);
4902 node
*nd
= 0; // pacify compiler
4903 int c
= iter
.get(&nd
);
4917 void unformat_macro()
4919 symbol s
= get_name(1);
4921 request_or_macro
*p
= lookup_request(s
);
4922 macro
*m
= p
->to_macro();
4924 error("cannot unformat request");
4927 string_iterator
iter(*m
);
4929 node
*nd
= 0; // pacify compiler
4930 int c
= iter
.get(&nd
);
4936 if (nd
->set_unformat_flag())
4946 static void interpolate_environment_variable(symbol nm
)
4948 const char *s
= getenv(nm
.contents());
4950 input_stack::push(make_temp_iterator(s
));
4953 void interpolate_number_reg(symbol nm
, int inc
)
4955 reg
*r
= lookup_number_reg(nm
);
4960 input_stack::push(make_temp_iterator(r
->get_string()));
4963 static void interpolate_number_format(symbol nm
)
4965 reg
*r
= (reg
*)number_reg_dictionary
.lookup(nm
);
4967 input_stack::push(make_temp_iterator(r
->get_format()));
4970 static int get_delim_number(units
*n
, unsigned char si
, int prev_value
)
4974 if (start
.delimiter(1)) {
4976 if (get_number(n
, si
, prev_value
)) {
4978 warning(WARN_DELIM
, "closing delimiter does not match");
4985 static int get_delim_number(units
*n
, unsigned char si
)
4989 if (start
.delimiter(1)) {
4991 if (get_number(n
, si
)) {
4993 warning(WARN_DELIM
, "closing delimiter does not match");
5000 static int get_line_arg(units
*n
, unsigned char si
, charinfo
**cp
)
5004 int start_level
= input_stack::get_level();
5005 if (!start
.delimiter(1))
5008 if (get_number(n
, si
)) {
5009 if (tok
.dummy() || tok
.transparent_dummy())
5011 if (!(start
== tok
&& input_stack::get_level() == start_level
)) {
5012 *cp
= tok
.get_char(1);
5015 if (!(start
== tok
&& input_stack::get_level() == start_level
))
5016 warning(WARN_DELIM
, "closing delimiter does not match");
5022 static int read_size(int *x
)
5032 else if (c
== '+') {
5037 int val
= 0; // pacify compiler
5043 // allow an increment either before or after the left parenthesis
5049 else if (c
== '+') {
5064 val
= val
*10 + (c
- '0');
5069 else if (csdigit(c
)) {
5071 if (!inc
&& c
!= '0' && c
< '4') {
5077 val
= val
*10 + (c
- '0');
5081 else if (!tok
.delimiter(1))
5087 if (!inc
&& (c
== '-' || c
== '+')) {
5088 inc
= c
== '+' ? 1 : -1;
5091 if (!get_number(&val
, 'z'))
5093 if (!(start
.ch() == '[' && tok
.ch() == ']') && start
!= tok
) {
5094 if (start
.ch() == '[')
5095 error("missing `]'");
5097 error("missing closing delimiter");
5105 // special case -- \s[0] and \s0 means to revert to previous size
5112 *x
= curenv
->get_requested_point_size() + val
;
5115 *x
= curenv
->get_requested_point_size() - val
;
5122 "\\s escape results in non-positive point size; set to 1");
5128 error("bad digit in point size");
5133 static symbol
get_delim_name()
5138 error("end of input at start of delimited name");
5141 if (start
.newline()) {
5142 error("can't delimit name with a newline");
5145 int start_level
= input_stack::get_level();
5146 char abuf
[ABUF_SIZE
];
5148 int buf_size
= ABUF_SIZE
;
5151 if (i
+ 1 > buf_size
) {
5153 buf
= new char[ABUF_SIZE
*2];
5154 memcpy(buf
, abuf
, buf_size
);
5155 buf_size
= ABUF_SIZE
*2;
5158 char *old_buf
= buf
;
5159 buf
= new char[buf_size
*2];
5160 memcpy(buf
, old_buf
, buf_size
);
5167 && (compatible_flag
|| input_stack::get_level() == start_level
))
5169 if ((buf
[i
] = tok
.ch()) == 0) {
5170 error("missing delimiter (got %1)", tok
.description());
5180 error("empty delimited name");
5195 static void do_register()
5199 if (!start
.delimiter(1))
5202 symbol nm
= get_long_name(1);
5207 reg
*r
= (reg
*)number_reg_dictionary
.lookup(nm
);
5209 if (!r
|| !r
->get_value(&prev_value
))
5212 if (!get_number(&val
, 'u', prev_value
))
5215 warning(WARN_DELIM
, "closing delimiter does not match");
5219 set_number_reg(nm
, val
);
5222 // this implements the \w escape sequence
5224 static void do_width()
5228 int start_level
= input_stack::get_level();
5229 environment
env(curenv
);
5230 environment
*oldenv
= curenv
;
5235 warning(WARN_DELIM
, "missing closing delimiter");
5238 if (tok
.newline()) {
5239 warning(WARN_DELIM
, "missing closing delimiter");
5240 input_stack::push(make_temp_iterator("\n"));
5244 && (compatible_flag
|| input_stack::get_level() == start_level
))
5249 units x
= env
.get_input_line_position().to_units();
5250 input_stack::push(make_temp_iterator(i_to_a(x
)));
5251 env
.width_registers();
5256 charinfo
*page_character
;
5258 void set_page_character()
5260 page_character
= get_optional_char();
5264 static const symbol
percent_symbol("%");
5266 void read_title_parts(node
**part
, hunits
*part_width
)
5269 if (tok
.newline() || tok
.eof())
5272 int start_level
= input_stack::get_level();
5274 for (int i
= 0; i
< 3; i
++) {
5275 while (!tok
.newline() && !tok
.eof()) {
5277 && (compatible_flag
|| input_stack::get_level() == start_level
)) {
5281 if (page_character
!= 0 && tok
.get_char() == page_character
)
5282 interpolate_number_reg(percent_symbol
, 0);
5287 curenv
->wrap_up_tab();
5288 part_width
[i
] = curenv
->get_input_line_position();
5289 part
[i
] = curenv
->extract_output_line();
5291 while (!tok
.newline() && !tok
.eof())
5295 class non_interpreted_node
: public node
{
5298 non_interpreted_node(const macro
&);
5299 int interpret(macro
*);
5301 int ends_sentence();
5308 non_interpreted_node::non_interpreted_node(const macro
&m
) : mac(m
)
5312 int non_interpreted_node::ends_sentence()
5317 int non_interpreted_node::same(node
*nd
)
5319 return mac
== ((non_interpreted_node
*)nd
)->mac
;
5322 const char *non_interpreted_node::type()
5324 return "non_interpreted_node";
5327 int non_interpreted_node::force_tprint()
5332 int non_interpreted_node::is_tag()
5337 node
*non_interpreted_node::copy()
5339 return new non_interpreted_node(mac
);
5342 int non_interpreted_node::interpret(macro
*m
)
5344 string_iterator
si(mac
);
5345 node
*n
= 0; // pacify compiler
5358 static node
*do_non_interpreted()
5363 while ((c
= get_copy(&n
)) != ESCAPE_QUESTION
&& c
!= EOF
&& c
!= '\n')
5368 if (c
== EOF
|| c
== '\n') {
5369 error("missing \\?");
5372 return new non_interpreted_node(mac
);
5375 static void encode_char(macro
*mac
, char c
)
5378 if ((font::use_charnames_in_special
) && tok
.special()) {
5379 charinfo
*ci
= tok
.get_char(1);
5380 const char *s
= ci
->get_symbol()->contents();
5381 if (s
[0] != (char)0) {
5385 while (s
[i
] != (char)0) {
5392 else if (tok
.stretchable_space()
5393 || tok
.unstretchable_space())
5395 else if (!(tok
.hyphen_indicator()
5397 || tok
.transparent_dummy()
5398 || tok
.zero_width_break()))
5399 error("%1 is invalid within \\X", tok
.description());
5402 if ((font::use_charnames_in_special
) && (c
== '\\')) {
5404 * add escape escape sequence
5416 int start_level
= input_stack::get_level();
5419 tok
!= start
|| input_stack::get_level() != start_level
;
5422 warning(WARN_DELIM
, "missing closing delimiter");
5425 if (tok
.newline()) {
5426 input_stack::push(make_temp_iterator("\n"));
5427 warning(WARN_DELIM
, "missing closing delimiter");
5435 else if (tok
.leader())
5437 else if (tok
.backspace())
5441 encode_char(&mac
, c
);
5443 return new special_node(mac
);
5446 void device_request()
5448 if (!tok
.newline() && !tok
.eof()) {
5457 if (c
!= ' ' && c
!= '\t')
5460 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
5462 curenv
->add_node(new special_node(mac
));
5467 void device_macro_request()
5469 symbol s
= get_name(1);
5470 if (!(s
.is_null() || s
.is_empty())) {
5471 request_or_macro
*p
= lookup_request(s
);
5472 macro
*m
= p
->to_macro();
5474 curenv
->add_node(new special_node(*m
));
5476 error("can't transparently throughput a request");
5481 void output_request()
5483 if (!tok
.newline() && !tok
.eof()) {
5491 if (c
!= ' ' && c
!= '\t')
5494 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
5495 topdiv
->transparent_output(c
);
5496 topdiv
->transparent_output('\n');
5501 extern int image_no
; // from node.cpp
5503 static node
*do_suppress(symbol nm
)
5505 if (nm
.is_null() || nm
.is_empty()) {
5506 error("expecting an argument to escape \\O");
5509 const char *s
= nm
.contents();
5512 if (begin_level
== 0)
5513 // suppress generation of glyphs
5514 return new suppress_node(0, 0);
5517 if (begin_level
== 0)
5518 // enable generation of glyphs
5519 return new suppress_node(1, 0);
5522 if (begin_level
== 0)
5523 return new suppress_node(1, 1);
5535 s
++; // move over '5'
5537 if (*s
== (char)0) {
5538 error("missing position and filename in \\O");
5541 if (!(position
== 'l'
5544 || position
== 'i')) {
5545 error("l, r, c, or i position expected (got %1 in \\O)", position
);
5548 s
++; // onto image name
5549 if (s
== (char *)0) {
5550 error("missing image name for \\O");
5554 if (begin_level
== 0)
5555 return new suppress_node(symbol(s
), position
, image_no
);
5561 error("`%1' is an invalid argument to \\O", *s
);
5566 void special_node::tprint(troff_output_file
*out
)
5569 string_iterator
iter(mac
);
5571 int c
= iter
.get(0);
5574 for (const char *s
= ::asciify(c
); *s
; s
++)
5575 tprint_char(out
, *s
);
5580 int get_file_line(const char **filename
, int *lineno
)
5582 return input_stack::get_location(0, filename
, lineno
);
5588 if (get_integer(&n
)) {
5589 const char *filename
= 0;
5591 symbol s
= get_long_name();
5592 filename
= s
.contents();
5594 (void)input_stack::set_location(filename
, n
-1);
5599 static int nroff_mode
= 0;
5601 static void nroff_request()
5607 static void troff_request()
5613 static void skip_alternative()
5616 // ensure that ``.if 0\{'' works as expected
5617 if (tok
.left_brace())
5621 c
= input_stack::get(0);
5624 if (c
== ESCAPE_LEFT_BRACE
)
5626 else if (c
== ESCAPE_RIGHT_BRACE
)
5628 else if (c
== escape_char
&& escape_char
> 0)
5629 switch(input_stack::get(0)) {
5637 while ((c
= input_stack::get(0)) != '\n' && c
!= EOF
)
5641 Note that the level can properly be < 0, eg
5647 So don't give an error message in this case.
5649 if (level
<= 0 && c
== '\n')
5655 static void begin_alternative()
5657 while (tok
.space() || tok
.left_brace())
5667 static int_stack if_else_stack
;
5674 while (tok
.ch() == '!') {
5679 unsigned char c
= tok
.ch();
5682 result
= !nroff_mode
;
5684 else if (c
== 'n') {
5686 result
= nroff_mode
;
5688 else if (c
== 'v') {
5692 else if (c
== 'o') {
5693 result
= (topdiv
->get_page_number() & 1);
5696 else if (c
== 'e') {
5697 result
= !(topdiv
->get_page_number() & 1);
5700 else if (c
== 'd' || c
== 'r') {
5702 symbol nm
= get_name(1);
5708 ? request_dictionary
.lookup(nm
) != 0
5709 : number_reg_dictionary
.lookup(nm
) != 0);
5711 else if (c
== 'm') {
5713 symbol nm
= get_long_name(1);
5718 result
= (nm
== default_symbol
5719 || color_dictionary
.lookup(nm
) != 0);
5721 else if (c
== 'c') {
5724 charinfo
*ci
= tok
.get_char(1);
5729 result
= character_exists(ci
, curenv
);
5732 else if (c
== 'F') {
5734 symbol nm
= get_long_name(1);
5739 result
= check_font(curenv
->get_family()->nm
, nm
);
5741 else if (c
== 'S') {
5743 symbol nm
= get_long_name(1);
5748 result
= check_style(nm
);
5750 else if (tok
.space())
5752 else if (tok
.delimiter()) {
5754 int delim_level
= input_stack::get_level();
5755 environment
env1(curenv
);
5756 environment
env2(curenv
);
5757 environment
*oldenv
= curenv
;
5760 for (int i
= 0; i
< 2; i
++) {
5763 if (tok
.newline() || tok
.eof()) {
5764 warning(WARN_DELIM
, "missing closing delimiter");
5770 && (compatible_flag
|| input_stack::get_level() == delim_level
))
5776 node
*n1
= env1
.extract_output_line();
5777 node
*n2
= env2
.extract_output_line();
5778 result
= same_node_list(n1
, n2
);
5779 delete_node_list(n1
);
5780 delete_node_list(n2
);
5788 if (!get_number(&n
, 'u')) {
5798 begin_alternative();
5804 void if_else_request()
5806 if_else_stack
.push(do_if_request());
5816 if (if_else_stack
.is_empty()) {
5817 warning(WARN_EL
, "unbalanced .el request");
5821 if (if_else_stack
.pop())
5824 begin_alternative();
5828 static int while_depth
= 0;
5829 static int while_break_flag
= 0;
5831 void while_request()
5836 mac
.append(new token_node(tok
));
5838 node
*n
= 0; // pacify compiler
5839 int c
= input_stack::get(&n
);
5855 if (c
== ESCAPE_LEFT_BRACE
)
5857 else if (c
== ESCAPE_RIGHT_BRACE
)
5859 else if (c
== escape_char
)
5862 if (c
== '\n' && level
<= 0)
5867 error("unbalanced \\{ \\}");
5870 input_stack::add_boundary();
5872 input_stack::push(new string_iterator(mac
, "while loop"));
5874 if (!do_if_request()) {
5875 while (input_stack::get(0) != EOF
)
5879 process_input_stack();
5880 if (while_break_flag
|| input_stack::is_return_boundary()) {
5881 while_break_flag
= 0;
5885 input_stack::remove_boundary();
5891 void while_break_request()
5894 error("no while loop");
5898 while_break_flag
= 1;
5899 while (input_stack::get(0) != EOF
)
5905 void while_continue_request()
5908 error("no while loop");
5912 while (input_stack::get(0) != EOF
)
5921 symbol nm
= get_long_name(1);
5925 while (!tok
.newline() && !tok
.eof())
5927 file_case
*fcp
= include_search_path
.open_file_cautious(nm
.contents(),
5930 input_stack::push(new file_iterator(fcp
, nm
.contents()));
5932 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
5937 // like .so but use popen()
5941 error(".pso request not allowed in safer mode");
5945 #ifdef POPEN_MISSING
5946 error("pipes not available on this system");
5948 #else /* not POPEN_MISSING */
5949 if (tok
.newline() || tok
.eof())
5950 error("missing command");
5953 while ((c
= get_copy(0)) == ' ' || c
== '\t')
5956 char *buf
= new char[buf_size
];
5958 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0)) {
5959 const char *s
= asciify(c
);
5960 int slen
= strlen(s
);
5961 if (buf_used
+ slen
+ 1> buf_size
) {
5962 char *old_buf
= buf
;
5963 int old_buf_size
= buf_size
;
5965 buf
= new char[buf_size
];
5966 memcpy(buf
, old_buf
, old_buf_size
);
5969 strcpy(buf
+ buf_used
, s
);
5972 buf
[buf_used
] = '\0';
5974 FILE *fp
= popen(buf
, POPEN_RT
);
5976 input_stack::push(new file_iterator(
5977 new file_case(fp
, buf
, file_case::fc_pipe
| file_case::fc_take_path
),
5978 symbol(buf
).contents()));
5980 error("can't open pipe to process `%1': %2", buf
, strerror(errno
));
5985 #endif /* not POPEN_MISSING */
5991 static int llx_reg_contents
= 0;
5992 static int lly_reg_contents
= 0;
5993 static int urx_reg_contents
= 0;
5994 static int ury_reg_contents
= 0;
5996 struct bounding_box
{
5997 int llx
, lly
, urx
, ury
;
6000 /* Parse the argument to a %%BoundingBox comment. Return 1 if it
6001 contains 4 numbers, 2 if it contains (atend), 0 otherwise. */
6003 int parse_bounding_box(char *p
, bounding_box
*bb
)
6005 if (sscanf(p
, "%d %d %d %d",
6006 &bb
->llx
, &bb
->lly
, &bb
->urx
, &bb
->ury
) == 4)
6009 /* The Document Structuring Conventions say that the numbers
6010 should be integers. Unfortunately some broken applications
6012 double x1
, x2
, x3
, x4
;
6013 if (sscanf(p
, "%lf %lf %lf %lf", &x1
, &x2
, &x3
, &x4
) == 4) {
6021 for (; *p
== ' ' || *p
== '\t'; p
++)
6023 if (strncmp(p
, "(atend)", 7) == 0) {
6028 bb
->llx
= bb
->lly
= bb
->urx
= bb
->ury
= 0;
6032 // This version is taken from psrm.cpp FIXME DO IT LIKE
6034 #define PS_LINE_MAX 255
6035 cset
white_space("\n\r \t");
6037 int ps_get_line(char *buf
, file_case
*fcp
, const char* filename
)
6039 int c
= fcp
->get_c();
6046 while (c
!= '\r' && c
!= '\n' && c
!= EOF
) {
6047 if ((c
< 0x1b && !white_space(c
)) || c
== 0x7f)
6048 error("invalid input character code %1 in `%2'", int(c
), filename
);
6049 else if (i
< PS_LINE_MAX
)
6053 error("PostScript file `%1' is non-conforming "
6054 "because length of line exceeds 255", filename
);
6062 if (c
!= EOF
&& c
!= '\n')
6068 inline void assign_registers(int llx
, int lly
, int urx
, int ury
)
6070 llx_reg_contents
= llx
;
6071 lly_reg_contents
= lly
;
6072 urx_reg_contents
= urx
;
6073 ury_reg_contents
= ury
;
6076 void do_ps_file(file_case
*fcp
, const char* filename
)
6080 char buf
[PS_LINE_MAX
+ 1 /* NL */ +1];
6081 llx_reg_contents
= lly_reg_contents
=
6082 urx_reg_contents
= ury_reg_contents
= 0;
6083 if (!ps_get_line(buf
, fcp
, filename
)) {
6084 error("`%1' is empty", filename
);
6087 if (strncmp("%!PS-Adobe-", buf
, 11) != 0) {
6088 error("`%1' is not conforming to the Document Structuring Conventions",
6092 while (ps_get_line(buf
, fcp
, filename
) != 0) {
6093 // in header comments, `%X' (`X' any printable character except
6094 // whitespace) is possible too
6095 if (buf
[0] == '%') {
6096 if (strncmp(buf
+ 1, "%EndComments", 12) == 0)
6098 if (white_space(buf
[1]))
6103 if (strncmp(buf
+ 1, "%BoundingBox:", 13) == 0) {
6104 int res
= parse_bounding_box(buf
+ 14, &bb
);
6106 assign_registers(bb
.llx
, bb
.lly
, bb
.urx
, bb
.ury
);
6109 else if (res
== 2) {
6114 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
6123 // in the trailer, the last BoundingBox comment is significant
6124 for (offset
= 512; !last_try
; offset
*= 2) {
6125 int had_trailer
= 0;
6127 if (offset
> 32768 || fcp
->seek(-offset
, fcp
->seek_end
) == -1) {
6129 if (fcp
->seek(0L, fcp
->seek_set
) == -1)
6132 while (ps_get_line(buf
, fcp
, filename
) != 0) {
6133 if (buf
[0] == '%' && buf
[1] == '%') {
6135 if (strncmp(buf
+ 2, "Trailer", 7) == 0)
6139 if (strncmp(buf
+ 2, "BoundingBox:", 12) == 0) {
6140 int res
= parse_bounding_box(buf
+ 14, &bb
);
6143 else if (res
== 2) {
6144 error("`(atend)' not allowed in trailer of `%1'", filename
);
6148 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
6157 assign_registers(bb
.llx
, bb
.lly
, bb
.urx
, bb
.ury
);
6162 error("%%%%BoundingBox comment not found in `%1'", filename
);
6165 void ps_bbox_request()
6167 symbol nm
= get_long_name(1);
6171 while (!tok
.newline() && !tok
.eof())
6173 // PS files might contain non-printable characters, such as ^Z
6174 // and CRs not followed by an LF, so open them in binary mode.
6175 file_case
*fcp
= include_search_path
.open_file_cautious(nm
.contents(),
6176 fcp
->mux_need_seek
| fcp
->mux_need_binary
);
6178 do_ps_file(fcp
, nm
.contents());
6181 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
6186 const char *asciify(int c
)
6189 buf
[0] = escape_char
== '\0' ? '\\' : escape_char
;
6190 buf
[1] = buf
[2] = '\0';
6192 case ESCAPE_QUESTION
:
6195 case ESCAPE_AMPERSAND
:
6198 case ESCAPE_RIGHT_PARENTHESIS
:
6201 case ESCAPE_UNDERSCORE
:
6207 case ESCAPE_CIRCUMFLEX
:
6210 case ESCAPE_LEFT_BRACE
:
6213 case ESCAPE_RIGHT_BRACE
:
6216 case ESCAPE_LEFT_QUOTE
:
6219 case ESCAPE_RIGHT_QUOTE
:
6237 case ESCAPE_PERCENT
:
6249 case PUSH_GROFF_MODE
:
6250 case PUSH_COMP_MODE
:
6251 case POP_GROFFCOMP_MODE
:
6255 if (invalid_input_char(c
))
6264 const char *input_char_description(int c
)
6268 return "a newline character";
6270 return "a backspace character";
6272 return "a leader character";
6274 return "a tab character";
6276 return "a space character";
6280 static char buf
[sizeof("magic character code ") + 1 + INT_DIGITS
];
6281 if (invalid_input_char(c
)) {
6282 const char *s
= asciify(c
);
6289 sprintf(buf
, "magic character code %d", c
);
6298 sprintf(buf
, "character code %d", c
);
6304 if (!tok
.newline() && !tok
.eof()) {
6313 if (c
!= ' ' && c
!= '\t')
6317 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
6320 curenv
->add_node(new tag_node(s
, 0));
6327 if (!tok
.newline() && !tok
.eof()) {
6336 if (c
!= ' ' && c
!= '\t')
6340 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
6343 curenv
->add_node(new tag_node(s
, 1));
6348 // .tm, .tm1, and .tmc
6350 void do_terminal(int newline
, int string_like
)
6352 if (!tok
.newline() && !tok
.eof()) {
6356 if (string_like
&& c
== '"') {
6360 if (c
!= ' ' && c
!= '\t')
6363 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
6364 fputs(asciify(c
), stderr
);
6367 fputc('\n', stderr
);
6382 void terminal_continue()
6387 dictionary
stream_dictionary(20);
6389 void do_open(int append
)
6391 symbol stream
= get_name(1);
6392 if (!stream
.is_null()) {
6393 symbol filename
= get_long_name(1);
6394 if (!filename
.is_null()) {
6396 FILE *fp
= fopen(filename
.contents(), append
? "a" : "w");
6398 error("can't open `%1' for %2: %3",
6399 filename
.contents(),
6400 append
? "appending" : "writing",
6402 fp
= (FILE *)stream_dictionary
.remove(stream
);
6405 fp
= (FILE *)stream_dictionary
.lookup(stream
, fp
);
6416 error(".open request not allowed in safer mode");
6423 void opena_request()
6426 error(".opena request not allowed in safer mode");
6433 void close_request()
6435 symbol stream
= get_name(1);
6436 if (!stream
.is_null()) {
6437 FILE *fp
= (FILE *)stream_dictionary
.remove(stream
);
6439 error("no stream named `%1'", stream
.contents());
6446 // .write and .writec
6448 void do_write_request(int newline
)
6450 symbol stream
= get_name(1);
6451 if (stream
.is_null()) {
6455 FILE *fp
= (FILE *)stream_dictionary
.lookup(stream
);
6457 error("no stream named `%1'", stream
.contents());
6462 while ((c
= get_copy(0)) == ' ')
6466 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
6467 fputs(asciify(c
), fp
);
6474 void write_request()
6476 do_write_request(1);
6479 void write_request_continue()
6481 do_write_request(0);
6484 void write_macro_request()
6486 symbol stream
= get_name(1);
6487 if (stream
.is_null()) {
6491 FILE *fp
= (FILE *)stream_dictionary
.lookup(stream
);
6493 error("no stream named `%1'", stream
.contents());
6497 symbol s
= get_name(1);
6502 request_or_macro
*p
= lookup_request(s
);
6503 macro
*m
= p
->to_macro();
6505 error("cannot write request");
6507 string_iterator
iter(*m
);
6509 int c
= iter
.get(0);
6512 fputs(asciify(c
), fp
);
6519 void warnscale_request()
6526 warn_scale
= (double)units_per_inch
;
6528 warn_scale
= (double)units_per_inch
/ 2.54;
6530 warn_scale
= (double)units_per_inch
/ 72.0;
6532 warn_scale
= (double)units_per_inch
/ 6.0;
6535 "invalid scaling indicator `%1', using `i' instead", c
);
6538 warn_scaling_indicator
= c
;
6543 void spreadwarn_request()
6546 if (has_arg() && get_hunits(&n
, 'm')) {
6549 hunits em
= curenv
->get_size();
6550 spread_limit
= (double)n
.to_units()
6551 / (em
.is_zero() ? hresolution
: em
.to_units());
6554 spread_limit
= -spread_limit
- 1; // no arg toggles on/off without
6555 // changing value; we mirror at
6556 // -0.5 to make zero a valid value
6560 static void init_charset_table()
6563 strcpy(buf
, "char");
6564 for (int i
= 0; i
<= UCHAR_MAX
; i
++) {
6565 strcpy(buf
+ 4, i_to_a(i
));
6566 charset_table
[i
] = get_charinfo(symbol(buf
));
6567 charset_table
[i
]->set_ascii_code(i
);
6569 charset_table
[i
]->set_hyphenation_code(cmlower(i
));
6571 charset_table
['.']->set_flags(charinfo::ENDS_SENTENCE
);
6572 charset_table
['?']->set_flags(charinfo::ENDS_SENTENCE
);
6573 charset_table
['!']->set_flags(charinfo::ENDS_SENTENCE
);
6574 charset_table
['-']->set_flags(charinfo::BREAK_AFTER
);
6575 charset_table
['"']->set_flags(charinfo::TRANSPARENT
);
6576 charset_table
['\'']->set_flags(charinfo::TRANSPARENT
);
6577 charset_table
[')']->set_flags(charinfo::TRANSPARENT
);
6578 charset_table
[']']->set_flags(charinfo::TRANSPARENT
);
6579 charset_table
['*']->set_flags(charinfo::TRANSPARENT
);
6580 get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT
);
6581 get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT
);
6582 get_charinfo(symbol("cq"))->set_flags(charinfo::TRANSPARENT
);
6583 get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER
);
6584 get_charinfo(symbol("hy"))->set_flags(charinfo::BREAK_AFTER
);
6585 get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6586 get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6587 get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6588 get_charinfo(symbol("sqrtex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6589 get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
6590 get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY
);
6591 page_character
= charset_table
['%'];
6594 static void init_hpf_code_table()
6596 for (unsigned char i
= 0; i
<= UCHAR_MAX
; ++i
)
6597 hpf_code_table
[i
] = cmlower(i
);
6600 static void do_translate(int translate_transparent
, int translate_input
)
6603 while (!tok
.newline() && !tok
.eof()) {
6605 // This is a really bizarre troff feature.
6607 translate_space_to_dummy
= tok
.dummy();
6608 if (tok
.newline() || tok
.eof())
6613 charinfo
*ci1
= tok
.get_char(1);
6617 if (tok
.newline() || tok
.eof()) {
6618 ci1
->set_special_translation(charinfo::TRANSLATE_SPACE
,
6619 translate_transparent
);
6623 ci1
->set_special_translation(charinfo::TRANSLATE_SPACE
,
6624 translate_transparent
);
6625 else if (tok
.stretchable_space())
6626 ci1
->set_special_translation(charinfo::TRANSLATE_STRETCHABLE_SPACE
,
6627 translate_transparent
);
6628 else if (tok
.dummy())
6629 ci1
->set_special_translation(charinfo::TRANSLATE_DUMMY
,
6630 translate_transparent
);
6631 else if (tok
.hyphen_indicator())
6632 ci1
->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR
,
6633 translate_transparent
);
6635 charinfo
*ci2
= tok
.get_char(1);
6639 ci1
->set_translation(0, translate_transparent
, translate_input
);
6641 ci1
->set_translation(ci2
, translate_transparent
, translate_input
);
6653 void translate_no_transparent()
6658 void translate_input()
6666 if (get_integer(&flags
))
6668 charinfo
*ci
= tok
.get_char(1);
6670 charinfo
*tem
= ci
->get_translation();
6673 ci
->set_flags(flags
);
6680 void hyphenation_code()
6683 while (!tok
.newline() && !tok
.eof()) {
6684 charinfo
*ci
= tok
.get_char(1);
6689 unsigned char c
= tok
.ch();
6691 error("hyphenation code must be ordinary character");
6695 error("hyphenation code cannot be digit");
6698 ci
->set_hyphenation_code(c
);
6699 if (ci
->get_translation()
6700 && ci
->get_translation()->get_translation_input())
6701 ci
->get_translation()->set_hyphenation_code(c
);
6708 void hyphenation_patterns_file_code()
6711 while (!tok
.newline() && !tok
.eof()) {
6713 if (get_integer(&n1
) && (0 <= n1
&& n1
<= 255)) {
6715 error("missing output hyphenation code");
6718 if (get_integer(&n2
) && (0 <= n2
&& n2
<= 255)) {
6719 hpf_code_table
[n1
] = n2
;
6723 error("output hyphenation code must be integer in the range 0..255");
6728 error("input hyphenation code must be integer in the range 0..255");
6735 charinfo
*token::get_char(int required
)
6737 if (type
== TOKEN_CHAR
)
6738 return charset_table
[c
];
6739 if (type
== TOKEN_SPECIAL
)
6740 return get_charinfo(nm
);
6741 if (type
== TOKEN_NUMBERED_CHAR
)
6742 return get_charinfo_by_number(val
);
6743 if (type
== TOKEN_ESCAPE
) {
6744 if (escape_char
!= 0)
6745 return charset_table
[escape_char
];
6747 error("`\\e' used while no current escape character");
6752 if (type
== TOKEN_EOF
|| type
== TOKEN_NEWLINE
)
6753 warning(WARN_MISSING
, "missing normal or special character");
6755 error("normal or special character expected (got %1)", description());
6760 charinfo
*get_optional_char()
6764 charinfo
*ci
= tok
.get_char();
6766 check_missing_character();
6772 void check_missing_character()
6774 if (!tok
.newline() && !tok
.eof() && !tok
.right_brace() && !tok
.tab())
6775 error("normal or special character expected (got %1): "
6776 "treated as missing",
6782 int token::add_to_node_list(node
**pp
)
6789 *pp
= (*pp
)->add_char(charset_table
[c
], curenv
, &w
, &s
);
6795 if (escape_char
!= 0)
6796 *pp
= (*pp
)->add_char(charset_table
[escape_char
], curenv
, &w
, &s
);
6798 case TOKEN_HYPHEN_INDICATOR
:
6799 *pp
= (*pp
)->add_discretionary_hyphen();
6801 case TOKEN_ITALIC_CORRECTION
:
6802 *pp
= (*pp
)->add_italic_correction(&w
);
6804 case TOKEN_LEFT_BRACE
:
6806 case TOKEN_MARK_INPUT
:
6807 set_number_reg(nm
, curenv
->get_input_line_position().to_units());
6813 case TOKEN_NUMBERED_CHAR
:
6814 *pp
= (*pp
)->add_char(get_charinfo_by_number(val
), curenv
, &w
, &s
);
6816 case TOKEN_RIGHT_BRACE
:
6819 n
= new hmotion_node(curenv
->get_space_width(),
6820 curenv
->get_fill_color());
6823 *pp
= (*pp
)->add_char(get_charinfo(nm
), curenv
, &w
, &s
);
6825 case TOKEN_STRETCHABLE_SPACE
:
6826 n
= new unbreakable_space_node(curenv
->get_space_width(),
6827 curenv
->get_fill_color());
6829 case TOKEN_UNSTRETCHABLE_SPACE
:
6830 n
= new space_char_hmotion_node(curenv
->get_space_width(),
6831 curenv
->get_fill_color());
6833 case TOKEN_TRANSPARENT_DUMMY
:
6834 n
= new transparent_dummy_node
;
6836 case TOKEN_ZERO_WIDTH_BREAK
:
6837 n
= new space_node(H0
, curenv
->get_fill_color());
6839 n
->is_escape_colon();
6851 void token::process()
6853 if (possibly_handle_first_page_transition())
6856 case TOKEN_BACKSPACE
:
6857 curenv
->add_node(new hmotion_node(-curenv
->get_space_width(),
6858 curenv
->get_fill_color()));
6861 curenv
->add_char(charset_table
[c
]);
6864 curenv
->add_node(new dummy_node
);
6873 if (escape_char
!= 0)
6874 curenv
->add_char(charset_table
[escape_char
]);
6876 case TOKEN_BEGIN_TRAP
:
6877 case TOKEN_END_TRAP
:
6878 case TOKEN_PAGE_EJECTOR
:
6879 // these are all handled in process_input_stack()
6881 case TOKEN_HYPHEN_INDICATOR
:
6882 curenv
->add_hyphen_indicator();
6884 case TOKEN_INTERRUPT
:
6885 curenv
->interrupt();
6887 case TOKEN_ITALIC_CORRECTION
:
6888 curenv
->add_italic_correction();
6891 curenv
->handle_tab(1);
6893 case TOKEN_LEFT_BRACE
:
6895 case TOKEN_MARK_INPUT
:
6896 set_number_reg(nm
, curenv
->get_input_line_position().to_units());
6902 curenv
->add_node(nd
);
6905 case TOKEN_NUMBERED_CHAR
:
6906 curenv
->add_char(get_charinfo_by_number(val
));
6909 // handled in process_input_stack()
6911 case TOKEN_RIGHT_BRACE
:
6917 curenv
->add_char(get_charinfo(nm
));
6922 case TOKEN_STRETCHABLE_SPACE
:
6923 curenv
->add_node(new unbreakable_space_node(curenv
->get_space_width(),
6924 curenv
->get_fill_color()));
6926 case TOKEN_UNSTRETCHABLE_SPACE
:
6927 curenv
->add_node(new space_char_hmotion_node(curenv
->get_space_width(),
6928 curenv
->get_fill_color()));
6931 curenv
->handle_tab(0);
6933 case TOKEN_TRANSPARENT
:
6935 case TOKEN_TRANSPARENT_DUMMY
:
6936 curenv
->add_node(new transparent_dummy_node
);
6938 case TOKEN_ZERO_WIDTH_BREAK
:
6940 node
*tmp
= new space_node(H0
, curenv
->get_fill_color());
6941 tmp
->freeze_space();
6942 tmp
->is_escape_colon();
6943 curenv
->add_node(tmp
);
6955 const char *get_string();
6958 const char *nargs_reg::get_string()
6960 return i_to_a(input_stack::nargs());
6967 const char *get_string();
6970 const char *lineno_reg::get_string()
6974 if (!input_stack::get_location(0, &file
, &line
))
6976 return i_to_a(line
);
6979 class writable_lineno_reg
6980 : public general_reg
6983 writable_lineno_reg();
6984 void set_value(units
);
6985 int get_value(units
*);
6988 writable_lineno_reg::writable_lineno_reg()
6992 int writable_lineno_reg::get_value(units
*res
)
6996 if (!input_stack::get_location(0, &file
, &line
))
7002 void writable_lineno_reg::set_value(units n
)
7004 input_stack::set_location(0, n
);
7011 const char *get_string();
7014 const char *filename_reg::get_string()
7018 if (input_stack::get_location(0, &file
, &line
))
7024 class break_flag_reg
7028 const char *get_string();
7031 const char *break_flag_reg::get_string()
7033 return i_to_a(input_stack::get_break_flag());
7042 constant_reg(const char *);
7043 const char *get_string();
7046 constant_reg::constant_reg(const char *p
) : s(p
)
7050 const char *constant_reg::get_string()
7055 constant_int_reg::constant_int_reg(int *q
) : p(q
)
7059 const char *constant_int_reg::get_string()
7064 void abort_request()
7069 else if (tok
.newline())
7072 while ((c
= get_copy(0)) == ' ')
7075 if (c
== EOF
|| c
== '\n')
7076 fputs("User Abort.", stderr
);
7078 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(0))
7079 fputs(asciify(c
), stderr
);
7081 fputc('\n', stderr
);
7082 cleanup_and_exit(1);
7088 char *s
= new char[len
];
7090 while ((c
= get_copy(0)) == ' ')
7093 while (c
!= '\n' && c
!= EOF
) {
7094 if (!invalid_input_char(c
)) {
7097 s
= new char[len
*2];
7098 memcpy(s
, tem
, len
);
7118 error(".pi request not allowed in safer mode");
7122 #ifdef POPEN_MISSING
7123 error("pipes not available on this system");
7125 #else /* not POPEN_MISSING */
7127 error("can't pipe: output already started");
7132 if ((pc
= read_string()) == 0)
7133 error("can't pipe to empty command");
7135 char *s
= new char[strlen(pipe_command
) + strlen(pc
) + 1 + 1];
7136 strcpy(s
, pipe_command
);
7139 a_delete pipe_command
;
7146 #endif /* not POPEN_MISSING */
7150 static int system_status
;
7152 void system_request()
7155 error(".sy request not allowed in safer mode");
7159 char *command
= read_string();
7161 error("empty command");
7163 system_status
= system(command
);
7171 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
7172 handle_initial_request(COPY_FILE_REQUEST
);
7175 symbol filename
= get_long_name(1);
7176 while (!tok
.newline() && !tok
.eof())
7180 if (!filename
.is_null())
7181 curdiv
->copy_file(filename
.contents());
7188 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
7189 handle_initial_request(VJUSTIFY_REQUEST
);
7192 symbol type
= get_long_name(1);
7193 if (!type
.is_null())
7194 curdiv
->vjustify(type
);
7199 void transparent_file()
7201 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
7202 handle_initial_request(TRANSPARENT_FILE_REQUEST
);
7205 symbol filename
= get_long_name(1);
7206 while (!tok
.newline() && !tok
.eof())
7210 if (!filename
.is_null()) {
7211 file_case
*fcp
= include_search_path
7212 .open_file_cautious(filename
.contents());
7214 error("can't open `%1': %2", filename
.contents(), strerror(errno
));
7218 int c
= fcp
->get_c();
7221 if (invalid_input_char(c
))
7222 warning(WARN_INPUT
, "invalid input character code %1", int(c
));
7224 curdiv
->transparent_output(c
);
7229 curdiv
->transparent_output('\n');
7244 page_range(int, int, page_range
*);
7245 int contains(int n
);
7248 page_range::page_range(int i
, int j
, page_range
*p
)
7249 : first(i
), last(j
), next(p
)
7253 int page_range::contains(int n
)
7255 return n
>= first
&& (last
<= 0 || n
<= last
);
7258 page_range
*output_page_list
= 0;
7260 int in_output_page_list(int n
)
7262 if (!output_page_list
)
7264 for (page_range
*p
= output_page_list
; p
; p
= p
->next
)
7270 static void parse_output_page_list(char *p
)
7276 else if (csdigit(*p
)) {
7279 i
= i
*10 + *p
++ - '0';
7280 while (csdigit(*p
));
7290 j
= j
*10 + *p
++ - '0';
7291 while (csdigit(*p
));
7297 last_page_number
= -1;
7298 else if (last_page_number
>= 0 && j
> last_page_number
)
7299 last_page_number
= j
;
7300 output_page_list
= new page_range(i
, j
, output_page_list
);
7306 error("bad output page list");
7307 output_page_list
= 0;
7311 static file_case
*open_mac_file(const char *mac
)
7313 // Try first FOOBAR.tmac, then tmac.FOOBAR
7314 char *s
= new char[strlen(mac
) + strlen(MACRO_POSTFIX
) +1];
7316 strcat(s
, MACRO_POSTFIX
);
7319 if ((fcp
= mac_path
->open_file(s
, fcp
->fc_take_path
)) == NULL
) {
7320 s
= new char[strlen(mac
) + strlen(MACRO_PREFIX
) +1];
7321 strcpy(s
, MACRO_PREFIX
);
7323 fcp
= mac_path
->open_file(s
, fcp
->fc_take_path
);
7328 static void process_macro_file(const char *mac
)
7330 file_case
*fcp
= open_mac_file(mac
);
7332 fatal("can't find macro file %1", mac
);
7333 const char *s
= symbol(fcp
->path()).contents();
7334 input_stack::push(new file_iterator(fcp
, s
));
7336 process_input_stack();
7339 static void process_startup_file(const char *filename
)
7341 search_path
*orig_mac_path
= mac_path
;
7342 mac_path
= &config_macro_path
;
7344 if ((fcp
= mac_path
->open_file(filename
)) != NULL
) {
7345 input_stack::push(new file_iterator(fcp
, symbol(fcp
->path()).contents()));
7347 process_input_stack();
7349 mac_path
= orig_mac_path
;
7354 symbol nm
= get_long_name(1);
7358 while (!tok
.newline() && !tok
.eof())
7360 // .mso doesn't (and cannot) go through open_mac_file, so we
7361 // need to do it here manually: If we have tmac.FOOBAR, try
7362 // FOOBAR.tmac and vice versa
7364 if ((fcp
= mac_path
->open_file(nm
.contents())) == NULL
) {
7365 const char *fn
= nm
.contents();
7367 if (strncasecmp(fn
, MACRO_PREFIX
, sizeof(MACRO_PREFIX
) - 1) == 0) {
7368 char *s
= new char[strlen(fn
) + sizeof(MACRO_POSTFIX
)];
7369 strcpy(s
, fn
+ sizeof(MACRO_PREFIX
) - 1);
7370 strcat(s
, MACRO_POSTFIX
);
7371 fcp
= mac_path
->open_file(s
, fcp
->fc_take_path
);
7375 if (strncasecmp(fn
+ strlen(fn
) - sizeof(MACRO_POSTFIX
) + 1,
7376 MACRO_POSTFIX
, sizeof(MACRO_POSTFIX
) - 1) == 0) {
7377 char *s
= new char[strlen(fn
) + sizeof(MACRO_PREFIX
)];
7378 strcpy(s
, MACRO_PREFIX
);
7379 strncat(s
, fn
, strlen(fn
) - sizeof(MACRO_POSTFIX
) + 1);
7380 fcp
= mac_path
->open_file(s
, fcp
->fc_take_path
);
7386 input_stack::push(new file_iterator(fcp
, symbol(fcp
->path()).contents()));
7388 warning(WARN_FILE
, "can't find macro file `%1'", nm
.contents());
7393 static void process_input_file(const char *name
)
7396 if ((fcp
= include_search_path
.open_file_cautious(name
)) == NULL
) {
7397 assert(strcmp(name
, "-"));
7398 fatal("can't open `%1': %2", name
, strerror(errno
));
7400 input_stack::push(new file_iterator(fcp
, name
));
7402 process_input_stack();
7405 // make sure the_input is empty before calling this
7407 static int evaluate_expression(const char *expr
, units
*res
)
7409 input_stack::push(make_temp_iterator(expr
));
7411 int success
= get_number(res
, 'u');
7412 while (input_stack::get(0) != EOF
)
7417 static void do_register_assignment(const char *s
)
7419 const char *p
= strchr(s
, '=');
7425 if (evaluate_expression(s
+ 1, &n
))
7426 set_number_reg(buf
, n
);
7429 char *buf
= new char[p
- s
+ 1];
7430 memcpy(buf
, s
, p
- s
);
7433 if (evaluate_expression(p
+ 1, &n
))
7434 set_number_reg(buf
, n
);
7439 static void set_string(const char *name
, const char *value
)
7441 macro
*m
= new macro
;
7442 for (const char *p
= value
; *p
; p
++)
7443 if (!invalid_input_char((unsigned char)*p
))
7445 request_dictionary
.define(name
, m
);
7448 static void do_string_assignment(const char *s
)
7450 const char *p
= strchr(s
, '=');
7455 set_string(buf
, s
+ 1);
7458 char *buf
= new char[p
- s
+ 1];
7459 memcpy(buf
, s
, p
- s
);
7461 set_string(buf
, p
+ 1);
7472 string_list(const char *ss
) : s(ss
), next(0) {}
7476 static void prepend_string(const char *s
, string_list
**p
)
7478 string_list
*l
= new string_list(s
);
7484 static void add_string(const char *s
, string_list
**p
)
7488 *p
= new string_list(s
);
7491 void usage(FILE *stream
, const char *prog
)
7494 "Synopsis: %s -abcivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n"
7495 " -rcn -Tname -Fdir -Idir -Mdir [files...]\n",
7499 int main(int argc
, char **argv
)
7501 program_name
= argv
[0];
7502 static char stderr_buf
[BUFSIZ
];
7503 setbuf(stderr
, stderr_buf
);
7505 string_list
*macros
= 0;
7506 string_list
*register_assignments
= 0;
7507 string_list
*string_assignments
= 0;
7512 int no_rc
= 0; // don't process troffrc and troffrc-end
7513 int next_page_number
= 0; // pacify compiler
7515 hresolution
= vresolution
= 1;
7516 // restore $PATH if called from groff
7517 char* groff_path
= getenv(U_ROFF_PATH__
);
7524 if (putenv(strsave(e
.contents())))
7525 fatal("putenv failed");
7527 static const struct option long_options
[] = {
7528 { "help", no_argument
, 0, CHAR_MAX
+ 1 },
7529 { "version", no_argument
, 0, 'v' },
7533 # define DEBUG_OPTION "D"
7535 while ((c
= getopt_long(argc
, argv
,
7536 "abciI:vw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU"
7537 DEBUG_OPTION
, long_options
, 0))
7542 printf(L_TROFF
" (" T_ROFF
") v" VERSION
);
7547 // Search path for .psbb files
7548 // and most other non-system input files.
7549 include_search_path
.command_line_dir(optarg
);
7554 is_html
= (strcmp(device
, "html") == 0);
7557 compatible_flag
= 1;
7563 macro_path
.command_line_dir(optarg
);
7564 safer_macro_path
.command_line_dir(optarg
);
7565 config_macro_path
.command_line_dir(optarg
);
7568 font::command_line_font_dir(optarg
);
7571 add_string(optarg
, ¯os
);
7580 enable_warning(optarg
);
7583 disable_warning(optarg
);
7592 ascii_output_flag
= 1;
7595 suppress_output_flag
= 1;
7598 if (sscanf(optarg
, "%d", &next_page_number
) == 1)
7601 error("bad page number");
7604 parse_output_page_list(optarg
);
7607 if (*optarg
== '\0')
7608 error("`-d' requires non-empty argument");
7610 add_string(optarg
, &string_assignments
);
7613 if (*optarg
== '\0')
7614 error("`-r' requires non-empty argument");
7616 add_string(optarg
, ®ister_assignments
);
7619 default_family
= symbol(optarg
);
7625 // silently ignore these
7628 unsafe_flag
= 1; // unsafe behaviour
7635 case CHAR_MAX
+ 1: // --help
7636 usage(stdout
, argv
[0]);
7640 usage(stderr
, argv
[0]);
7642 break; // never reached
7647 mac_path
= ¯o_path
;
7648 set_string(".T", device
);
7649 init_charset_table();
7650 init_hpf_code_table();
7651 if (!font::load_desc())
7652 fatal("sorry, I can't continue");
7653 units_per_inch
= font::res
;
7654 hresolution
= font::hor
;
7655 vresolution
= font::vert
;
7656 sizescale
= font::sizescale
;
7657 tcommand_flag
= font::tcommand
;
7658 warn_scale
= (double)units_per_inch
;
7659 warn_scaling_indicator
= 'i';
7660 if (!fflag
&& font::family
!= 0 && *font::family
!= '\0')
7661 default_family
= symbol(font::family
);
7662 font_size::init_size_table(font::sizes
);
7665 if (font::style_table
) {
7666 for (i
= 0; font::style_table
[i
]; i
++)
7667 mount_style(j
++, symbol(font::style_table
[i
]));
7669 for (i
= 0; font::font_name_table
[i
]; i
++, j
++)
7670 // In the DESC file a font name of 0 (zero) means leave this
7672 if (strcmp(font::font_name_table
[i
], "0") != 0)
7673 mount_font(j
, symbol(font::font_name_table
[i
]));
7674 curdiv
= topdiv
= new top_level_diversion
;
7676 topdiv
->set_next_page_number(next_page_number
);
7677 init_input_requests();
7678 init_env_requests();
7679 init_div_requests();
7681 init_column_requests();
7683 init_node_requests();
7684 number_reg_dictionary
.define(".T", new constant_reg(tflag
? "1" : "0"));
7686 init_reg_requests();
7687 init_hyphen_requests();
7688 init_environments();
7689 while (string_assignments
) {
7690 do_string_assignment(string_assignments
->s
);
7691 string_list
*tem
= string_assignments
;
7692 string_assignments
= string_assignments
->next
;
7695 while (register_assignments
) {
7696 do_register_assignment(register_assignments
->s
);
7697 string_list
*tem
= register_assignments
;
7698 register_assignments
= register_assignments
->next
;
7702 process_startup_file(INITIAL_STARTUP_FILE
);
7704 process_macro_file(macros
->s
);
7705 string_list
*tem
= macros
;
7706 macros
= macros
->next
;
7710 process_startup_file(FINAL_STARTUP_FILE
);
7711 for (i
= optind
; i
< argc
; i
++)
7712 process_input_file(argv
[i
]);
7713 if (optind
>= argc
|| iflag
)
7714 process_input_file("-");
7716 return 0; // not reached
7722 if (has_arg() && get_integer(&n
)) {
7723 if (n
& ~WARN_TOTAL
) {
7724 warning(WARN_RANGE
, "warning mask must be between 0 and %1", WARN_TOTAL
);
7730 warning_mask
= WARN_TOTAL
;
7734 static void init_registers()
7736 #ifdef LONG_FOR_TIME_T
7738 #else /* not LONG_FOR_TIME_T */
7740 #endif /* not LONG_FOR_TIME_T */
7742 // Use struct here to work around misfeature in old versions of g++.
7743 struct tm
*tt
= localtime(&t
);
7744 set_number_reg("seconds", int(tt
->tm_sec
));
7745 set_number_reg("minutes", int(tt
->tm_min
));
7746 set_number_reg("hours", int(tt
->tm_hour
));
7747 set_number_reg("dw", int(tt
->tm_wday
+ 1));
7748 set_number_reg("dy", int(tt
->tm_mday
));
7749 set_number_reg("mo", int(tt
->tm_mon
+ 1));
7750 set_number_reg("year", int(1900 + tt
->tm_year
));
7751 set_number_reg("yr", int(tt
->tm_year
));
7752 set_number_reg("$$", getpid());
7753 number_reg_dictionary
.define(".A",
7754 new constant_reg(ascii_output_flag
7760 * registers associated with \O
7763 static int output_reg_minx_contents
= -1;
7764 static int output_reg_miny_contents
= -1;
7765 static int output_reg_maxx_contents
= -1;
7766 static int output_reg_maxy_contents
= -1;
7768 void check_output_limits(int x
, int y
)
7770 if ((output_reg_minx_contents
== -1) || (x
< output_reg_minx_contents
))
7771 output_reg_minx_contents
= x
;
7772 if (x
> output_reg_maxx_contents
)
7773 output_reg_maxx_contents
= x
;
7774 if ((output_reg_miny_contents
== -1) || (y
< output_reg_miny_contents
))
7775 output_reg_miny_contents
= y
;
7776 if (y
> output_reg_maxy_contents
)
7777 output_reg_maxy_contents
= y
;
7780 void reset_output_registers()
7782 output_reg_minx_contents
= -1;
7783 output_reg_miny_contents
= -1;
7784 output_reg_maxx_contents
= -1;
7785 output_reg_maxy_contents
= -1;
7788 void get_output_registers(int *minx
, int *miny
, int *maxx
, int *maxy
)
7790 *minx
= output_reg_minx_contents
;
7791 *miny
= output_reg_miny_contents
;
7792 *maxx
= output_reg_maxx_contents
;
7793 *maxy
= output_reg_maxy_contents
;
7796 void init_input_requests()
7798 init_request("ab", abort_request
);
7799 init_request("als", alias_macro
);
7800 init_request("am", append_macro
);
7801 init_request("am1", append_nocomp_macro
);
7802 init_request("ami", append_indirect_macro
);
7803 init_request("ami1", append_indirect_nocomp_macro
);
7804 init_request("as", append_string
);
7805 init_request("as1", append_nocomp_string
);
7806 init_request("asciify", asciify_macro
);
7807 init_request("backtrace", backtrace_request
);
7808 init_request("blm", blank_line_macro
);
7809 init_request("break", while_break_request
);
7810 init_request("cf", copy_file
);
7811 init_request("cflags", char_flags
);
7812 init_request("char", define_character
);
7813 init_request("chop", chop_macro
);
7814 init_request("close", close_request
);
7815 init_request("color", activate_color
);
7816 init_request("composite", composite_request
);
7817 init_request("continue", while_continue_request
);
7818 init_request("cp", compatible
);
7819 init_request("de", define_macro
);
7820 init_request("de1", define_nocomp_macro
);
7821 init_request("defcolor", define_color
);
7822 init_request("dei", define_indirect_macro
);
7823 init_request("dei1", define_indirect_nocomp_macro
);
7824 init_request("device", device_request
);
7825 init_request("devicem", device_macro_request
);
7826 init_request("do", do_request
);
7827 init_request("ds", define_string
);
7828 init_request("ds1", define_nocomp_string
);
7829 init_request("ec", set_escape_char
);
7830 init_request("ecr", restore_escape_char
);
7831 init_request("ecs", save_escape_char
);
7832 init_request("el", else_request
);
7833 init_request("em", end_macro
);
7834 init_request("eo", escape_off
);
7835 init_request("ex", exit_request
);
7836 init_request("fchar", define_fallback_character
);
7837 #ifdef WIDOW_CONTROL
7838 init_request("fpl", flush_pending_lines
);
7840 init_request("hcode", hyphenation_code
);
7841 init_request("hpfcode", hyphenation_patterns_file_code
);
7842 init_request("ie", if_else_request
);
7843 init_request("if", if_request
);
7844 init_request("ig", ignore
);
7845 init_request("length", length_request
);
7846 init_request("lf", line_file
);
7847 init_request("mso", macro_source
);
7848 init_request("nop", nop_request
);
7849 init_request("nroff", nroff_request
);
7850 init_request("nx", next_file
);
7851 init_request("open", open_request
);
7852 init_request("opena", opena_request
);
7853 init_request("output", output_request
);
7854 init_request("pc", set_page_character
);
7855 init_request("pi", pipe_output
);
7856 init_request("pm", print_macros
);
7857 init_request("psbb", ps_bbox_request
);
7858 #ifndef POPEN_MISSING
7859 init_request("pso", pipe_source
);
7861 init_request("rchar", remove_character
);
7862 init_request("rd", read_request
);
7863 init_request("return", return_macro_request
);
7864 init_request("rm", remove_macro
);
7865 init_request("rn", rename_macro
);
7866 init_request("schar", define_special_character
);
7867 init_request("shift", shift
);
7868 init_request("so", source
);
7869 init_request("spreadwarn", spreadwarn_request
);
7870 init_request("substring", substring_request
);
7871 init_request("sy", system_request
);
7872 init_request("tag", tag
);
7873 init_request("taga", taga
);
7874 init_request("tm", terminal
);
7875 init_request("tm1", terminal1
);
7876 init_request("tmc", terminal_continue
);
7877 init_request("tr", translate
);
7878 init_request("trf", transparent_file
);
7879 init_request("trin", translate_input
);
7880 init_request("trnt", translate_no_transparent
);
7881 init_request("troff", troff_request
);
7882 init_request("unformat", unformat_macro
);
7884 init_request("vj", vjustify
);
7886 init_request("warn", warn_request
);
7887 init_request("warnscale", warnscale_request
);
7888 init_request("while", while_request
);
7889 init_request("write", write_request
);
7890 init_request("writec", write_request_continue
);
7891 init_request("writem", write_macro_request
);
7892 number_reg_dictionary
.define(".$", new nargs_reg
);
7893 number_reg_dictionary
.define(".br", new break_flag_reg
);
7894 number_reg_dictionary
.define(".C", new constant_int_reg(&compatible_flag
));
7895 number_reg_dictionary
.define(".O", new variable_reg(&begin_level
));
7896 number_reg_dictionary
.define(".c", new lineno_reg
);
7897 number_reg_dictionary
.define(".color", new constant_int_reg(&color_flag
));
7898 number_reg_dictionary
.define(".F", new filename_reg
);
7899 number_reg_dictionary
.define(".g", new constant_reg("1"));
7900 number_reg_dictionary
.define(".H", new constant_int_reg(&hresolution
));
7901 number_reg_dictionary
.define(".R", new constant_reg("10000"));
7902 number_reg_dictionary
.define(".U", new constant_int_reg(&unsafe_flag
));
7903 number_reg_dictionary
.define(".V", new constant_int_reg(&vresolution
));
7904 number_reg_dictionary
.define(".warn", new constant_int_reg(&warning_mask
));
7905 extern const char *major_version
;
7906 number_reg_dictionary
.define(".x", new constant_reg(major_version
));
7907 extern const char *revision
;
7908 number_reg_dictionary
.define(".Y", new constant_reg(revision
));
7909 extern const char *minor_version
;
7910 number_reg_dictionary
.define(".y", new constant_reg(minor_version
));
7911 number_reg_dictionary
.define("c.", new writable_lineno_reg
);
7912 number_reg_dictionary
.define("llx", new variable_reg(&llx_reg_contents
));
7913 number_reg_dictionary
.define("lly", new variable_reg(&lly_reg_contents
));
7914 number_reg_dictionary
.define("opmaxx",
7915 new variable_reg(&output_reg_maxx_contents
));
7916 number_reg_dictionary
.define("opmaxy",
7917 new variable_reg(&output_reg_maxy_contents
));
7918 number_reg_dictionary
.define("opminx",
7919 new variable_reg(&output_reg_minx_contents
));
7920 number_reg_dictionary
.define("opminy",
7921 new variable_reg(&output_reg_miny_contents
));
7922 number_reg_dictionary
.define("slimit",
7923 new variable_reg(&input_stack::limit
));
7924 number_reg_dictionary
.define("systat", new variable_reg(&system_status
));
7925 number_reg_dictionary
.define("urx", new variable_reg(&urx_reg_contents
));
7926 number_reg_dictionary
.define("ury", new variable_reg(&ury_reg_contents
));
7929 object_dictionary
request_dictionary(501);
7931 void init_request(const char *s
, REQUEST_FUNCP f
)
7933 request_dictionary
.define(s
, new request(f
));
7936 static request_or_macro
*lookup_request(symbol nm
)
7938 assert(!nm
.is_null());
7939 request_or_macro
*p
= (request_or_macro
*)request_dictionary
.lookup(nm
);
7941 warning(WARN_MAC
, "macro `%1' not defined", nm
.contents());
7943 request_dictionary
.define(nm
, p
);
7948 node
*charinfo_to_node_list(charinfo
*ci
, const environment
*envp
)
7950 // Don't interpret character definitions in compatible mode.
7951 int old_compatible_flag
= compatible_flag
;
7952 compatible_flag
= 0;
7953 int old_escape_char
= escape_char
;
7955 macro
*mac
= ci
->set_macro(0);
7957 environment
*oldenv
= curenv
;
7958 environment
env(envp
);
7960 curenv
->set_composite();
7961 token old_tok
= tok
;
7962 input_stack::add_boundary();
7963 string_iterator
*si
=
7964 new string_iterator(*mac
, "composite character", ci
->nm
);
7965 input_stack::push(si
);
7966 // we don't use process_input_stack, because we don't want to recognise
7972 if (tok
.newline()) {
7973 error("composite character mustn't contain newline");
7981 node
*n
= curenv
->extract_output_line();
7982 input_stack::remove_boundary();
7986 compatible_flag
= old_compatible_flag
;
7987 escape_char
= old_escape_char
;
7992 static node
*read_draw_node()
7996 if (!start
.delimiter(1)){
7999 } while (tok
!= start
&& !tok
.newline() && !tok
.eof());
8004 error("missing argument");
8006 unsigned char type
= tok
.ch();
8008 read_color_draw_node(start
);
8013 hvpair
*point
= new hvpair
[maxpoints
];
8018 for (i
= 0; tok
!= start
; i
++) {
8019 if (i
== maxpoints
) {
8020 hvpair
*oldpoint
= point
;
8021 point
= new hvpair
[maxpoints
*2];
8022 for (int j
= 0; j
< maxpoints
; j
++)
8023 point
[j
] = oldpoint
[j
];
8027 if (!get_hunits(&point
[i
].h
,
8028 type
== 'f' || type
== 't' ? 'u' : 'm')) {
8039 if (!get_vunits(&point
[i
].v
, 'v')) {
8045 while (tok
!= start
&& !tok
.newline() && !tok
.eof())
8050 if (npoints
!= 1 || no_last_v
) {
8051 error("two arguments needed for line");
8056 if (npoints
!= 1 || !no_last_v
) {
8057 error("one argument needed for circle");
8063 if (npoints
!= 1 || no_last_v
) {
8064 error("two arguments needed for ellipse");
8069 if (npoints
!= 2 || no_last_v
) {
8070 error("four arguments needed for arc");
8076 error("even number of arguments needed for spline");
8079 if (npoints
!= 1 || !no_last_v
) {
8080 error("one argument needed for gray shade");
8085 // silently pass it through
8088 draw_node
*dn
= new draw_node(type
, point
, npoints
,
8089 curenv
->get_font_size(),
8090 curenv
->get_glyph_color(),
8091 curenv
->get_fill_color());
8103 static void read_color_draw_node(token
&start
)
8107 error("missing color scheme");
8110 unsigned char scheme
= tok
.ch();
8113 char end
= start
.ch();
8116 col
= read_cmy(end
);
8119 col
= &default_color
;
8122 col
= read_gray(end
);
8125 col
= read_cmyk(end
);
8128 col
= read_rgb(end
);
8132 curenv
->set_fill_color(col
);
8133 while (tok
!= start
) {
8134 if (tok
.newline() || tok
.eof()) {
8135 warning(WARN_DELIM
, "missing closing delimiter");
8136 input_stack::push(make_temp_iterator("\n"));
8147 } warning_table
[] = { // FIXME const? make fun-local; use jumptable??
8148 { "char", WARN_CHAR
},
8149 { "range", WARN_RANGE
},
8150 { "break", WARN_BREAK
},
8151 { "delim", WARN_DELIM
},
8153 { "scale", WARN_SCALE
},
8154 { "number", WARN_NUMBER
},
8155 { "syntax", WARN_SYNTAX
},
8156 { "tab", WARN_TAB
},
8157 { "right-brace", WARN_RIGHT_BRACE
},
8158 { "missing", WARN_MISSING
},
8159 { "input", WARN_INPUT
},
8160 { "escape", WARN_ESCAPE
},
8161 { "space", WARN_SPACE
},
8162 { "font", WARN_FONT
},
8164 { "mac", WARN_MAC
},
8165 { "reg", WARN_REG
},
8167 { "color", WARN_COLOR
},
8168 { "file", WARN_FILE
},
8169 { "all", WARN_TOTAL
& ~(WARN_DI
| WARN_MAC
| WARN_REG
) },
8170 { "w", WARN_TOTAL
},
8171 { "default", DEFAULT_WARNING_MASK
},
8174 static int lookup_warning(const char *name
)
8177 for (size_t i
= 0; i
< NELEM(warning_table
); ++i
)
8178 if (!strcmp(name
, warning_table
[i
].name
)) {
8179 rv
= warning_table
[i
].mask
;
8185 static void enable_warning(const char *name
)
8187 int mask
= lookup_warning(name
);
8189 warning_mask
|= mask
;
8191 error("unknown warning `%1'", name
);
8194 static void disable_warning(const char *name
)
8196 int mask
= lookup_warning(name
);
8198 warning_mask
&= ~mask
;
8200 error("unknown warning `%1'", name
);
8203 static void copy_mode_error(const char *format
,
8209 static const char prefix
[] = "(in ignored input) ";
8210 char *s
= new char[sizeof(prefix
) + strlen(format
)];
8213 warning(WARN_IG
, s
, arg1
, arg2
, arg3
);
8217 error(format
, arg1
, arg2
, arg3
);
8220 enum error_type
{ WARNING
, OUTPUT_WARNING
, ERROR
, FATAL
};
8222 static void do_error(error_type type
,
8228 const char *filename
;
8230 if (inhibit_errors
&& type
< FATAL
)
8233 input_stack::backtrace();
8234 if (!get_file_line(&filename
, &lineno
))
8237 errprint("%1:%2: ", filename
, lineno
);
8238 else if (program_name
)
8239 fprintf(stderr
, "%s: ", program_name
);
8242 fputs("fatal error: ", stderr
);
8247 fputs("warning: ", stderr
);
8249 case OUTPUT_WARNING
:
8250 double fromtop
= topdiv
->get_vertical_position().to_units() / warn_scale
;
8251 fprintf(stderr
, "warning [p %d, %.1f%c",
8252 topdiv
->get_page_number(), fromtop
, warn_scaling_indicator
);
8253 if (topdiv
!= curdiv
) {
8254 double fromtop1
= curdiv
->get_vertical_position().to_units()
8256 fprintf(stderr
, ", div `%s', %.1f%c",
8257 curdiv
->get_diversion_name(), fromtop1
, warn_scaling_indicator
);
8259 fprintf(stderr
, "]: ");
8262 errprint(format
, arg1
, arg2
, arg3
);
8263 fputc('\n', stderr
);
8266 cleanup_and_exit(1);
8269 int warning(warning_type t
,
8275 if ((t
& warning_mask
) != 0) {
8276 do_error(WARNING
, format
, arg1
, arg2
, arg3
);
8283 int output_warning(warning_type t
,
8289 if ((t
& warning_mask
) != 0) {
8290 do_error(OUTPUT_WARNING
, format
, arg1
, arg2
, arg3
);
8297 void error(const char *format
,
8302 do_error(ERROR
, format
, arg1
, arg2
, arg3
);
8305 void fatal(const char *format
,
8310 do_error(FATAL
, format
, arg1
, arg2
, arg3
);
8313 void fatal_with_file_and_line(const char *filename
, int lineno
,
8319 fprintf(stderr
, "%s:%d: fatal error: ", filename
, lineno
);
8320 errprint(format
, arg1
, arg2
, arg3
);
8321 fputc('\n', stderr
);
8323 cleanup_and_exit(1);
8326 void error_with_file_and_line(const char *filename
, int lineno
,
8332 fprintf(stderr
, "%s:%d: error: ", filename
, lineno
);
8333 errprint(format
, arg1
, arg2
, arg3
);
8334 fputc('\n', stderr
);
8338 dictionary
charinfo_dictionary(501);
8340 charinfo
*get_charinfo(symbol nm
)
8342 void *p
= charinfo_dictionary
.lookup(nm
);
8344 return (charinfo
*)p
;
8345 charinfo
*cp
= new charinfo(nm
);
8346 (void)charinfo_dictionary
.lookup(nm
, cp
);
8350 int charinfo::next_index
= 0;
8352 charinfo::charinfo(symbol s
)
8353 : translation(0), mac(0), special_translation(TRANSLATE_NONE
),
8354 hyphenation_code(0), flags(0), ascii_code(0), asciify_code(0),
8355 not_found(0), transparent_translate(1), translate_input(0),
8356 mode(CHAR_NORMAL
), nm(s
)
8358 index
= next_index
++;
8362 void charinfo::set_hyphenation_code(unsigned char c
)
8364 hyphenation_code
= c
;
8367 void charinfo::set_translation(charinfo
*ci
, int tt
, int ti
)
8371 if (hyphenation_code
!= 0)
8372 ci
->set_hyphenation_code(hyphenation_code
);
8373 if (asciify_code
!= 0)
8374 ci
->set_asciify_code(asciify_code
);
8375 else if (ascii_code
!= 0)
8376 ci
->set_asciify_code(ascii_code
);
8377 ci
->set_translation_input();
8379 special_translation
= TRANSLATE_NONE
;
8380 transparent_translate
= tt
;
8383 void charinfo::set_special_translation(int c
, int tt
)
8385 special_translation
= c
;
8387 transparent_translate
= tt
;
8390 void charinfo::set_ascii_code(unsigned char c
)
8395 void charinfo::set_asciify_code(unsigned char c
)
8400 macro
*charinfo::set_macro(macro
*m
)
8407 macro
*charinfo::setx_macro(macro
*m
, char_mode cm
)
8415 void charinfo::set_number(int n
)
8421 int charinfo::get_number()
8423 assert(number
>= 0);
8427 symbol
UNNAMED_SYMBOL("---");
8429 // For numbered characters not between 0 and 255, we make a symbol out
8430 // of the number and store them in this dictionary.
8432 dictionary
numbered_charinfo_dictionary(11);
8434 charinfo
*get_charinfo_by_number(int n
)
8436 static charinfo
*number_table
[UCHAR_MAX
+1];
8438 if (n
>= 0 && n
<= UCHAR_MAX
) {
8439 charinfo
*ci
= number_table
[n
];
8441 ci
= new charinfo(UNNAMED_SYMBOL
);
8443 number_table
[n
] = ci
;
8448 symbol
ns(i_to_a(n
));
8449 charinfo
*ci
= (charinfo
*)numbered_charinfo_dictionary
.lookup(ns
);
8451 ci
= new charinfo(UNNAMED_SYMBOL
);
8453 (void)numbered_charinfo_dictionary
.lookup(ns
, ci
);
8459 // This overrides the same function from libgroff; while reading font
8460 // definition files it puts single-letter glyph names into `charset_table'
8461 // and converts glyph names of the form `\x' (`x' a single letter) into `x'.
8462 // Consequently, symbol("x") refers to glyph name `\x', not `x'.
8464 glyph
*name_to_glyph(const char *nm
)
8468 ci
= charset_table
[nm
[0] & 0xff];
8469 else if (nm
[0] == '\\' && nm
[2] == 0)
8470 ci
= get_charinfo(symbol(nm
+ 1));
8472 ci
= get_charinfo(symbol(nm
));
8473 return ci
->as_glyph();
8476 glyph
*number_to_glyph(int n
)
8478 return get_charinfo_by_number(n
)->as_glyph();
8481 const char *glyph_to_name(glyph
*g
)
8483 charinfo
*ci
= (charinfo
*)g
; // Every glyph is actually a charinfo.
8484 return (ci
->nm
!= UNNAMED_SYMBOL
? ci
->nm
.contents() : NULL
);