2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
23 #include "dictionary.h"
33 #include "searchpath.h"
34 #include "macropath.h"
37 // Needed for getpid().
45 #else /* not ISATTY_MISSING */
50 #endif /* not isatty */
51 #endif /* not ISATTY_MISSING */
53 #define USAGE_EXIT_CODE 1
54 #define MACRO_PREFIX "tmac."
55 #define INITIAL_STARTUP_FILE "troffrc"
56 #define FINAL_STARTUP_FILE "troffrc-end"
57 #define DEFAULT_INPUT_STACK_LIMIT 1000
59 #ifndef DEFAULT_WARNING_MASK
60 // warnings that are enabled by default
61 #define DEFAULT_WARNING_MASK \
62 (WARN_CHAR|WARN_NUMBER|WARN_BREAK|WARN_SPACE|WARN_FONT)
65 // initial size of buffer for reading names; expanded as necessary
69 void init_column_requests();
72 static node
*read_draw_node();
73 void handle_first_page_transition();
74 static void push_token(const token
&);
80 void transparent_file();
82 const char *program_name
= 0;
85 static int backtrace_flag
= 0;
87 char *pipe_command
= 0;
89 charinfo
*charset_table
[256];
91 static int warning_mask
= DEFAULT_WARNING_MASK
;
92 static int inhibit_errors
= 0;
93 static int ignoring
= 0;
95 static void enable_warning(const char *);
96 static void disable_warning(const char *);
98 static int escape_char
= '\\';
99 static symbol end_macro_name
;
100 static symbol blank_line_macro_name
;
101 static int compatible_flag
= 0;
102 int ascii_output_flag
= 0;
103 int suppress_output_flag
= 0;
106 int tcommand_flag
= 0;
108 static int get_copy(node
**, int = 0);
109 static void copy_mode_error(const char *,
110 const errarg
& = empty_errarg
,
111 const errarg
& = empty_errarg
,
112 const errarg
& = empty_errarg
);
114 static symbol
read_escape_name();
115 static void interpolate_string(symbol
);
116 static void interpolate_macro(symbol
);
117 static void interpolate_number_format(symbol
);
118 static void interpolate_environment_variable(symbol
);
120 static void interpolate_arg(symbol
);
121 static request_or_macro
*lookup_request(symbol
);
122 static int get_delim_number(units
*, int);
123 static int get_delim_number(units
*, int, units
);
124 static int get_line_arg(units
*res
, int si
, charinfo
**cp
);
125 static int read_size(int *);
126 static symbol
get_delim_name();
127 static void init_registers();
128 static void trapping_blank_line();
130 struct input_iterator
;
131 input_iterator
*make_temp_iterator(const char *);
132 const char *input_char_description(int);
134 #ifndef IS_EBCDIC_HOST
136 const int ESCAPE_QUESTION
= 015;
137 const int BEGIN_TRAP
= 016;
138 const int END_TRAP
= 017;
139 const int PAGE_EJECTOR
= 020;
140 const int ESCAPE_NEWLINE
= 021;
141 const int ESCAPE_AMPERSAND
= 022;
142 const int ESCAPE_UNDERSCORE
= 023;
143 const int ESCAPE_BAR
= 024;
144 const int ESCAPE_CIRCUMFLEX
= 025;
145 const int ESCAPE_LEFT_BRACE
= 026;
146 const int ESCAPE_RIGHT_BRACE
= 027;
147 const int ESCAPE_LEFT_QUOTE
= 030;
148 const int ESCAPE_RIGHT_QUOTE
= 031;
149 const int ESCAPE_HYPHEN
= 032;
150 const int ESCAPE_BANG
= 033;
151 const int ESCAPE_c
= 034;
152 const int ESCAPE_e
= 035;
153 const int ESCAPE_PERCENT
= 036;
154 const int ESCAPE_SPACE
= 037;
156 const int TITLE_REQUEST
= 0200;
157 const int COPY_FILE_REQUEST
= 0201;
158 const int TRANSPARENT_FILE_REQUEST
= 0202;
160 const int VJUSTIFY_REQUEST
= 0203;
162 const int ESCAPE_E
= 0204;
163 const int LAST_PAGE_EJECTOR
= 0205;
164 const int ESCAPE_RIGHT_PARENTHESIS
= 0206;
166 #else /* IS_EBCDIC_HOST */
168 const int ESCAPE_QUESTION
= 010;
169 const int BEGIN_TRAP
= 011;
170 const int END_TRAP
= 013;
171 const int PAGE_EJECTOR
= 015;
172 const int ESCAPE_NEWLINE
= 016;
173 const int ESCAPE_AMPERSAND
= 017;
174 const int ESCAPE_UNDERSCORE
= 020;
175 const int ESCAPE_BAR
= 021;
176 const int ESCAPE_CIRCUMFLEX
= 022;
177 const int ESCAPE_LEFT_BRACE
= 023;
178 const int ESCAPE_RIGHT_BRACE
= 024;
179 const int ESCAPE_LEFT_QUOTE
= 027;
180 const int ESCAPE_RIGHT_QUOTE
= 030;
181 const int ESCAPE_HYPHEN
= 031;
182 const int ESCAPE_BANG
= 032;
183 const int ESCAPE_c
= 033;
184 const int ESCAPE_e
= 034;
185 const int ESCAPE_PERCENT
= 035;
186 const int ESCAPE_SPACE
= 036;
188 const int TITLE_REQUEST
= 060;
189 const int COPY_FILE_REQUEST
= 061;
190 const int TRANSPARENT_FILE_REQUEST
= 062;
192 const int VJUSTIFY_REQUEST
= 063;
194 const int ESCAPE_E
= 064;
195 const int LAST_PAGE_EJECTOR
= 065;
196 const int ESCAPE_RIGHT_PARENTHESIS
= 066;
198 #endif /* IS_EBCDIC_HOST */
201 void set_escape_char()
205 error("bad escape character");
209 escape_char
= tok
.ch();
222 class input_iterator
{
225 virtual ~input_iterator();
227 friend class input_stack
;
229 const unsigned char *ptr
;
230 const unsigned char *eptr
;
231 input_iterator
*next
;
233 virtual int fill(node
**);
235 virtual int has_args() { return 0; }
236 virtual int nargs() { return 0; }
237 virtual input_iterator
*get_arg(int) { return NULL
; }
238 virtual int get_location(int, const char **, int *)
240 virtual void backtrace() {}
241 virtual int set_location(const char *, int)
243 virtual int next_file(FILE *, const char *) { return 0; }
244 virtual void shift(int) {}
245 virtual int is_boundary();
246 virtual int internal_level() { return 0; }
247 virtual int is_file() { return 0; }
250 input_iterator::input_iterator()
255 input_iterator::~input_iterator()
259 int input_iterator::fill(node
**)
264 int input_iterator::peek()
269 int input_iterator::is_boundary()
274 inline int input_iterator::get(node
**p
)
276 return ptr
< eptr
? *ptr
++ : fill(p
);
280 class input_boundary
: public input_iterator
{
282 int is_boundary() { return 1; }
285 class file_iterator
: public input_iterator
{
288 const char *filename
;
291 enum { BUF_SIZE
= 512 };
292 unsigned char buf
[BUF_SIZE
];
295 file_iterator(FILE *, const char *, int = 0);
299 int get_location(int, const char **, int *);
301 int set_location(const char *, int);
302 int next_file(FILE *, const char *);
306 file_iterator::file_iterator(FILE *f
, const char *fn
, int po
)
307 : fp(f
), lineno(1), filename(fn
), popened(po
), newline_flag(0)
309 if ((font::use_charnames_in_special
) && (fn
!= 0)) {
312 the_output
->put_filename(fn
);
316 file_iterator::~file_iterator()
321 void file_iterator::close()
325 #ifndef POPEN_MISSING
328 #endif /* not POPEN_MISSING */
333 int file_iterator::is_file()
338 int file_iterator::next_file(FILE *f
, const char *s
)
351 int file_iterator::fill(node
**)
356 unsigned char *p
= buf
;
358 unsigned char *e
= p
+ BUF_SIZE
;
363 if (illegal_input_char(c
))
364 warning(WARN_INPUT
, "illegal input character code %1", int(c
));
383 int file_iterator::peek()
386 while (illegal_input_char(c
)) {
387 warning(WARN_INPUT
, "illegal input character code %1", int(c
));
395 int file_iterator::get_location(int /*allow_macro*/,
396 const char **filenamep
, int *linenop
)
399 if (filename
!= 0 && strcmp(filename
, "-") == 0)
400 *filenamep
= "<standard input>";
402 *filenamep
= filename
;
406 void file_iterator::backtrace()
408 errprint("%1:%2: backtrace: %3 `%1'\n", filename
, lineno
,
409 popened
? "process" : "file");
412 int file_iterator::set_location(const char *f
, int ln
)
418 the_output
->put_filename(f
);
424 input_iterator nil_iterator
;
428 static int get(node
**);
430 static void push(input_iterator
*);
431 static input_iterator
*get_arg(int);
433 static int get_location(int, const char **, int *);
434 static int set_location(const char *, int);
435 static void backtrace();
436 static void backtrace_all();
437 static void next_file(FILE *, const char *);
438 static void end_file();
439 static void shift(int n
);
440 static void add_boundary();
441 static void remove_boundary();
442 static int get_level();
447 static input_iterator
*top
;
450 static int finish_get(node
**);
451 static int finish_peek();
454 input_iterator
*input_stack::top
= &nil_iterator
;
455 int input_stack::level
= 0;
456 int input_stack::limit
= DEFAULT_INPUT_STACK_LIMIT
;
458 inline int input_stack::get_level()
460 return level
+ top
->internal_level();
463 inline int input_stack::get(node
**np
)
465 return (top
->ptr
< top
->eptr
) ? *top
->ptr
++ : finish_get(np
);
468 int input_stack::finish_get(node
**np
)
471 int c
= top
->fill(np
);
472 if (c
!= EOF
|| top
->is_boundary())
474 if (top
== &nil_iterator
)
476 input_iterator
*tem
= top
;
480 if (top
->ptr
< top
->eptr
)
487 inline int input_stack::peek()
489 return (top
->ptr
< top
->eptr
) ? *top
->ptr
: finish_peek();
492 int input_stack::finish_peek()
496 if (c
!= EOF
|| top
->is_boundary())
498 if (top
== &nil_iterator
)
500 input_iterator
*tem
= top
;
504 if (top
->ptr
< top
->eptr
)
511 void input_stack::add_boundary()
513 push(new input_boundary
);
516 void input_stack::remove_boundary()
518 assert(top
->is_boundary());
519 input_iterator
*temp
= top
->next
;
525 void input_stack::push(input_iterator
*in
)
529 if (++level
> limit
&& limit
> 0)
530 fatal("input stack limit exceeded (probable infinite loop)");
535 input_iterator
*input_stack::get_arg(int i
)
538 for (p
= top
; p
!= NULL
; p
= p
->next
)
540 return p
->get_arg(i
);
544 void input_stack::shift(int n
)
546 for (input_iterator
*p
= top
; p
; p
= p
->next
)
553 int input_stack::nargs()
555 for (input_iterator
*p
=top
; p
!= 0; p
= p
->next
)
561 int input_stack::get_location(int allow_macro
, const char **filenamep
, int *linenop
)
563 for (input_iterator
*p
= top
; p
; p
= p
->next
)
564 if (p
->get_location(allow_macro
, filenamep
, linenop
))
569 void input_stack::backtrace()
573 // only backtrace down to (not including) the topmost file
574 for (input_iterator
*p
= top
;
575 p
&& !p
->get_location(0, &f
, &n
);
580 void input_stack::backtrace_all()
582 for (input_iterator
*p
= top
; p
; p
= p
->next
)
586 int input_stack::set_location(const char *filename
, int lineno
)
588 for (input_iterator
*p
= top
; p
; p
= p
->next
)
589 if (p
->set_location(filename
, lineno
))
594 void input_stack::next_file(FILE *fp
, const char *s
)
597 for (pp
= &top
; *pp
!= &nil_iterator
; pp
= &(*pp
)->next
)
598 if ((*pp
)->next_file(fp
, s
))
600 if (++level
> limit
&& limit
> 0)
601 fatal("input stack limit exceeded");
602 *pp
= new file_iterator(fp
, s
);
603 (*pp
)->next
= &nil_iterator
;
606 void input_stack::end_file()
608 for (input_iterator
**pp
= &top
; *pp
!= &nil_iterator
; pp
= &(*pp
)->next
)
609 if ((*pp
)->is_file()) {
610 input_iterator
*tem
= *pp
;
618 void input_stack::clear()
621 while (top
!= &nil_iterator
) {
622 if (top
->is_boundary())
624 input_iterator
*tem
= top
;
629 // Keep while_request happy.
630 for (; nboundaries
> 0; --nboundaries
)
634 void backtrace_request()
636 input_stack::backtrace_all();
643 symbol nm
= get_long_name(0);
644 while (!tok
.newline() && !tok
.eof())
647 input_stack::end_file();
650 FILE *fp
= fopen(nm
.contents(), "r");
652 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
654 input_stack::next_file(fp
, nm
.contents());
662 if (!has_arg() || !get_integer(&n
))
664 input_stack::shift(n
);
668 static int get_char_for_escape_name()
670 int c
= get_copy(NULL
);
673 copy_mode_error("end of input in escape name");
676 if (!illegal_input_char(c
))
681 input_stack::push(make_temp_iterator("\n"));
686 copy_mode_error("%1 is not allowed in an escape name",
687 input_char_description(c
));
693 static symbol
read_two_char_escape_name()
696 buf
[0] = get_char_for_escape_name();
697 if (buf
[0] != '\0') {
698 buf
[1] = get_char_for_escape_name();
707 static symbol
read_long_escape_name()
709 int start_level
= input_stack::get_level();
710 char abuf
[ABUF_SIZE
];
712 int buf_size
= ABUF_SIZE
;
715 int c
= get_char_for_escape_name();
721 if (i
+ 2 > buf_size
) {
723 buf
= new char [ABUF_SIZE
*2];
724 memcpy(buf
, abuf
, buf_size
);
725 buf_size
= ABUF_SIZE
*2;
729 buf
= new char[buf_size
*2];
730 memcpy(buf
, old_buf
, buf_size
);
735 if (c
== ']' && input_stack::get_level() == start_level
)
742 copy_mode_error("empty escape name");
754 static symbol
read_escape_name()
756 int c
= get_char_for_escape_name();
760 return read_two_char_escape_name();
761 if (c
== '[' && !compatible_flag
)
762 return read_long_escape_name();
769 static symbol
read_increment_and_escape_name(int *incp
)
771 int c
= get_char_for_escape_name();
778 return read_two_char_escape_name();
781 return read_escape_name();
784 return read_escape_name();
786 if (!compatible_flag
) {
788 return read_long_escape_name();
799 static int get_copy(node
**nd
, int defining
)
802 int c
= input_stack::get(nd
);
803 if (c
== ESCAPE_NEWLINE
) {
807 c
= input_stack::get(nd
);
808 } while (c
== ESCAPE_NEWLINE
);
810 if (c
!= escape_char
|| escape_char
<= 0)
812 c
= input_stack::peek();
817 (void)input_stack::get(NULL
);
818 while ((c
= input_stack::get(NULL
)) != '\n' && c
!= EOF
)
821 case '#': // Like \" but newline is ignored.
822 (void)input_stack::get(NULL
);
823 while ((c
= input_stack::get(NULL
)) != '\n')
829 (void)input_stack::get(NULL
);
830 symbol s
= read_escape_name();
837 (void)input_stack::get(NULL
);
838 symbol s
= read_escape_name();
840 interpolate_string(s
);
844 (void)input_stack::get(NULL
);
847 (void)input_stack::get(NULL
);
850 (void)input_stack::get(NULL
);
854 (void)input_stack::get(NULL
);
856 symbol s
= read_increment_and_escape_name(&inc
);
858 interpolate_number_reg(s
, inc
);
863 (void)input_stack::get(NULL
);
864 symbol s
= read_escape_name();
866 interpolate_number_format(s
);
870 (void)input_stack::get(NULL
);
874 (void)input_stack::get(NULL
);
875 symbol s
= read_escape_name();
877 interpolate_environment_variable(s
);
881 (void)input_stack::get(NULL
);
883 return ESCAPE_NEWLINE
;
886 (void)input_stack::get(NULL
);
889 (void)input_stack::get(NULL
);
892 (void)input_stack::get(NULL
);
893 return ESCAPE_CIRCUMFLEX
;
895 (void)input_stack::get(NULL
);
896 return ESCAPE_LEFT_BRACE
;
898 (void)input_stack::get(NULL
);
899 return ESCAPE_RIGHT_BRACE
;
901 (void)input_stack::get(NULL
);
902 return ESCAPE_LEFT_QUOTE
;
904 (void)input_stack::get(NULL
);
905 return ESCAPE_RIGHT_QUOTE
;
907 (void)input_stack::get(NULL
);
908 return ESCAPE_HYPHEN
;
910 (void)input_stack::get(NULL
);
911 return ESCAPE_UNDERSCORE
;
913 (void)input_stack::get(NULL
);
916 (void)input_stack::get(NULL
);
919 (void)input_stack::get(NULL
);
920 return ESCAPE_QUESTION
;
922 (void)input_stack::get(NULL
);
923 return ESCAPE_AMPERSAND
;
925 (void)input_stack::get(NULL
);
926 return ESCAPE_RIGHT_PARENTHESIS
;
928 (void)input_stack::get(NULL
);
931 (void)input_stack::get(NULL
);
932 return ESCAPE_PERCENT
;
934 if (c
== escape_char
) {
935 (void)input_stack::get(NULL
);
944 class non_interpreted_char_node
: public node
{
947 non_interpreted_char_node(unsigned char);
949 int interpret(macro
*);
954 int non_interpreted_char_node::same(node
*nd
)
956 return c
== ((non_interpreted_char_node
*)nd
)->c
;
959 const char *non_interpreted_char_node::type()
961 return "non_interpreted_char_node";
964 non_interpreted_char_node::non_interpreted_char_node(unsigned char n
) : c(n
)
969 node
*non_interpreted_char_node::copy()
971 return new non_interpreted_char_node(c
);
974 int non_interpreted_char_node::interpret(macro
*mac
)
980 static void do_width();
981 static node
*do_non_interpreted();
982 static node
*do_special();
983 static void do_register();
985 static node
*do_overstrike()
988 overstrike_node
*on
= new overstrike_node
;
991 while (tok
!= start
) {
992 if (tok
.newline() || tok
.eof()) {
993 warning(WARN_DELIM
, "missing closing delimiter");
996 charinfo
*ci
= tok
.get_char(1);
998 node
*n
= curenv
->make_char_node(ci
);
1007 static node
*do_bracket()
1010 bracket_node
*bn
= new bracket_node
;
1013 while (tok
!= start
) {
1015 warning(WARN_DELIM
, "missing closing delimiter");
1018 if (tok
.newline()) {
1019 warning(WARN_DELIM
, "missing closing delimiter");
1020 input_stack::push(make_temp_iterator("\n"));
1023 charinfo
*ci
= tok
.get_char(1);
1025 node
*n
= curenv
->make_char_node(ci
);
1034 static int do_name_test()
1038 int start_level
= input_stack::get_level();
1043 if (tok
.newline() || tok
.eof()) {
1044 warning(WARN_DELIM
, "missing closing delimiter");
1048 && (compatible_flag
|| input_stack::get_level() == start_level
))
1054 return some_char
&& !bad_char
;
1058 static node
*do_zero_width()
1062 int start_level
= input_stack::get_level();
1063 environment
env(curenv
);
1064 environment
*oldenv
= curenv
;
1068 if (tok
.newline() || tok
.eof()) {
1069 error("missing closing delimiter");
1073 && (compatible_flag
|| input_stack::get_level() == start_level
))
1078 node
*rev
= env
.extract_output_line();
1086 return new zero_width_node(n
);
1091 // It's undesirable for \Z to change environments, because then
1092 // \n(.w won't work as expected.
1094 static node
*do_zero_width()
1096 node
*rev
= new dummy_node
;
1099 int start_level
= input_stack::get_level();
1102 if (tok
.newline() || tok
.eof()) {
1103 warning(WARN_DELIM
, "missing closing delimiter");
1107 && (compatible_flag
|| input_stack::get_level() == start_level
))
1109 if (!tok
.add_to_node_list(&rev
))
1110 error("illegal token in argument to \\Z");
1119 return new zero_width_node(n
);
1124 token_node
*node::get_token_node()
1129 class token_node
: public node
{
1132 token_node(const token
&t
);
1134 token_node
*get_token_node();
1139 token_node::token_node(const token
&t
) : tk(t
)
1143 node
*token_node::copy()
1145 return new token_node(tk
);
1148 token_node
*token_node::get_token_node()
1153 int token_node::same(node
*nd
)
1155 return tk
== ((token_node
*)nd
)->tk
;
1158 const char *token_node::type()
1160 return "token_node";
1163 token::token() : nd(0), type(TOKEN_EMPTY
)
1172 token::token(const token
&t
)
1173 : nm(t
.nm
), c(t
.c
), val(t
.val
), dim(t
.dim
), type(t
.type
)
1175 // Use two statements to work around bug in SGI C++.
1177 nd
= tem
? tem
->copy() : 0;
1180 void token::operator=(const token
&t
)
1184 // Use two statements to work around bug in SGI C++.
1186 nd
= tem
? tem
->copy() : 0;
1203 return !tok
.newline();
1206 void token::make_space()
1211 void token::make_newline()
1213 type
= TOKEN_NEWLINE
;
1225 int cc
= input_stack::get(&n
);
1226 if (cc
!= escape_char
|| escape_char
== 0) {
1232 case TRANSPARENT_FILE_REQUEST
:
1234 case COPY_FILE_REQUEST
:
1236 case VJUSTIFY_REQUEST
:
1238 type
= TOKEN_REQUEST
;
1242 type
= TOKEN_BEGIN_TRAP
;
1245 type
= TOKEN_END_TRAP
;
1247 case LAST_PAGE_EJECTOR
:
1248 seen_last_page_ejector
= 1;
1251 type
= TOKEN_PAGE_EJECTOR
;
1253 case ESCAPE_PERCENT
:
1255 type
= TOKEN_HYPHEN_INDICATOR
;
1260 nd
= new space_char_hmotion_node(curenv
->get_space_width());
1264 type
= TOKEN_ESCAPE
;
1267 goto handle_escape_char
;
1271 nd
= new hmotion_node(curenv
->get_narrow_space_width());
1273 case ESCAPE_CIRCUMFLEX
:
1276 nd
= new hmotion_node(curenv
->get_half_narrow_space_width());
1278 case ESCAPE_NEWLINE
:
1280 case ESCAPE_LEFT_BRACE
:
1282 type
= TOKEN_LEFT_BRACE
;
1284 case ESCAPE_RIGHT_BRACE
:
1286 type
= TOKEN_RIGHT_BRACE
;
1288 case ESCAPE_LEFT_QUOTE
:
1290 type
= TOKEN_SPECIAL
;
1293 case ESCAPE_RIGHT_QUOTE
:
1295 type
= TOKEN_SPECIAL
;
1300 type
= TOKEN_SPECIAL
;
1303 case ESCAPE_UNDERSCORE
:
1305 type
= TOKEN_SPECIAL
;
1310 type
= TOKEN_INTERRUPT
;
1314 type
= TOKEN_TRANSPARENT
;
1316 case ESCAPE_QUESTION
:
1318 nd
= do_non_interpreted();
1324 case ESCAPE_AMPERSAND
:
1328 case ESCAPE_RIGHT_PARENTHESIS
:
1329 ESCAPE_RIGHT_PARENTHESIS
:
1331 nd
= new transparent_dummy_node
;
1334 type
= TOKEN_BACKSPACE
;
1343 type
= TOKEN_NEWLINE
;
1346 type
= TOKEN_LEADER
;
1351 token_node
*tn
= n
->get_token_node();
1370 cc
= input_stack::get(NULL
);
1373 nm
= read_two_char_escape_name();
1374 type
= TOKEN_SPECIAL
;
1378 error("end of input after escape character");
1381 goto ESCAPE_LEFT_QUOTE
;
1383 goto ESCAPE_RIGHT_QUOTE
;
1387 goto ESCAPE_UNDERSCORE
;
1389 goto ESCAPE_PERCENT
;
1393 nd
= new hmotion_node(curenv
->get_digit_width());
1399 goto ESCAPE_CIRCUMFLEX
;
1401 type
= TOKEN_ITALIC_CORRECTION
;
1405 nd
= new left_italic_corrected_node
;
1408 goto ESCAPE_AMPERSAND
;
1410 goto ESCAPE_RIGHT_PARENTHESIS
;
1414 goto ESCAPE_QUESTION
;
1416 nd
= new unbreakable_space_node(curenv
->get_space_width());
1420 while ((cc
= input_stack::get(NULL
)) != '\n' && cc
!= EOF
)
1423 type
= TOKEN_NEWLINE
;
1427 case '#': // Like \" but newline is ignored.
1428 while ((cc
= input_stack::get(NULL
)) != '\n')
1436 symbol nm
= read_escape_name();
1438 interpolate_arg(nm
);
1443 symbol nm
= read_escape_name();
1445 interpolate_string(nm
);
1449 nd
= new non_interpreted_char_node('\001');
1453 c
= '0' + do_name_test();
1463 nm
= get_delim_name();
1466 type
= TOKEN_SPECIAL
;
1470 nd
= new vmotion_node(curenv
->get_size()/2);
1473 nd
= read_draw_node();
1481 goto handle_escape_char
;
1484 symbol s
= read_escape_name();
1488 for (p
= s
.contents(); *p
!= '\0'; p
++)
1492 curenv
->set_font(s
);
1494 curenv
->set_font(atoi(s
.contents()));
1499 symbol s
= read_escape_name();
1501 interpolate_number_format(s
);
1505 if (!get_delim_number(&x
, 'm'))
1508 nd
= new hmotion_node(x
);
1511 if (get_delim_number(&x
, 'z', curenv
->get_requested_point_size()))
1512 curenv
->set_char_height(x
);
1515 nm
= read_escape_name();
1518 type
= TOKEN_MARK_INPUT
;
1524 if (!get_line_arg(&x
, (cc
== 'l' ? 'm': 'v'), &s
))
1527 s
= get_charinfo(cc
== 'l' ? "ru" : "br");
1529 node
*n
= curenv
->make_char_node(s
);
1531 nd
= new hline_node(x
, n
);
1533 nd
= new vline_node(x
, n
);
1539 symbol nm
= read_increment_and_escape_name(&inc
);
1541 interpolate_number_reg(nm
, inc
);
1545 if (!get_delim_number(&val
, 0))
1547 type
= TOKEN_NUMBERED_CHAR
;
1550 nd
= do_overstrike();
1554 type
= TOKEN_SPREAD
;
1558 nd
= new vmotion_node(-curenv
->get_size());
1565 curenv
->set_size(x
);
1568 if (get_delim_number(&x
, 0))
1569 curenv
->set_char_slant(x
);
1573 nd
= new non_interpreted_char_node('\t');
1577 nd
= new vmotion_node(-curenv
->get_size()/2);
1580 if (!get_delim_number(&x
, 'v'))
1583 nd
= new vmotion_node(x
);
1587 symbol nm
= read_escape_name();
1589 interpolate_environment_variable(nm
);
1596 if (!get_delim_number(&x
, 'v'))
1599 nd
= new extra_size_node(x
);
1609 symbol s
= read_escape_name();
1612 request_or_macro
*p
= lookup_request(s
);
1613 macro
*m
= p
->to_macro();
1615 error("can't transparently throughput a request");
1618 nd
= new special_node(*m
);
1625 if (type
== TOKEN_NODE
)
1626 nd
= new zero_width_node(nd
);
1628 charinfo
*ci
= get_char(1);
1631 node
*gn
= curenv
->make_char_node(ci
);
1634 nd
= new zero_width_node(gn
);
1640 nd
= do_zero_width();
1646 goto ESCAPE_LEFT_BRACE
;
1648 goto ESCAPE_RIGHT_BRACE
;
1652 if (!compatible_flag
) {
1653 nm
= read_long_escape_name();
1656 type
= TOKEN_SPECIAL
;
1659 goto handle_normal_char
;
1661 if (cc
!= escape_char
&& cc
!= '.')
1662 warning(WARN_ESCAPE
, "escape character ignored before %1",
1663 input_char_description(cc
));
1664 goto handle_normal_char
;
1670 int token::operator==(const token
&t
)
1679 case TOKEN_NUMBERED_CHAR
:
1680 return val
== t
.val
;
1686 int token::operator!=(const token
&t
)
1688 return !(*this == t
);
1691 // is token a suitable delimiter (like ')?
1693 int token::delimiter(int err
)
1722 error("cannot use character `%1' as a starting delimiter", char(c
));
1732 error("cannot use %1 as a starting delimiter", description());
1739 const char *token::description()
1743 case TOKEN_BACKSPACE
:
1744 return "a backspace character";
1755 case TOKEN_HYPHEN_INDICATOR
:
1757 case TOKEN_INTERRUPT
:
1759 case TOKEN_ITALIC_CORRECTION
:
1762 return "a leader character";
1763 case TOKEN_LEFT_BRACE
:
1765 case TOKEN_MARK_INPUT
:
1771 case TOKEN_NUMBERED_CHAR
:
1773 case TOKEN_RIGHT_BRACE
:
1778 return "a special character";
1782 return "a tab character";
1783 case TOKEN_TRANSPARENT
:
1786 return "end of input";
1790 return "a magic token";
1795 while (!tok
.newline())
1806 if (has_arg() && get_integer(&n
))
1807 compatible_flag
= n
!= 0;
1809 compatible_flag
= 1;
1813 static void empty_name_warning(int required
)
1815 if (tok
.newline() || tok
.eof()) {
1817 warning(WARN_MISSING
, "missing name");
1819 else if (tok
.right_brace() || tok
.tab()) {
1820 const char *start
= tok
.description();
1823 } while (tok
.space() || tok
.right_brace() || tok
.tab());
1824 if (!tok
.newline() && !tok
.eof())
1825 error("%1 is not allowed before an argument", start
);
1827 warning(WARN_MISSING
, "missing name");
1830 error("name expected (got %1)", tok
.description());
1832 error("name expected (got %1): treated as missing", tok
.description());
1835 static void non_empty_name_warning()
1837 if (!tok
.newline() && !tok
.eof() && !tok
.space() && !tok
.tab()
1838 && !tok
.right_brace()
1839 // We don't want to give a warning for .el\{
1840 && !tok
.left_brace())
1841 error("%1 is not allowed in a name", tok
.description());
1844 symbol
get_name(int required
)
1846 if (compatible_flag
) {
1849 if ((buf
[0] = tok
.ch()) != 0) {
1851 if ((buf
[1] = tok
.ch()) != 0) {
1856 non_empty_name_warning();
1860 empty_name_warning(required
);
1865 return get_long_name(required
);
1868 symbol
get_long_name(int required
)
1872 char abuf
[ABUF_SIZE
];
1874 int buf_size
= ABUF_SIZE
;
1877 if (i
+ 1 > buf_size
) {
1879 buf
= new char [ABUF_SIZE
*2];
1880 memcpy(buf
, abuf
, buf_size
);
1881 buf_size
= ABUF_SIZE
*2;
1884 char *old_buf
= buf
;
1885 buf
= new char[buf_size
*2];
1886 memcpy(buf
, old_buf
, buf_size
);
1891 if ((buf
[i
] = tok
.ch()) == 0)
1897 empty_name_warning(required
);
1900 non_empty_name_warning();
1913 topdiv
->set_last_page();
1914 if (!end_macro_name
.is_null()) {
1915 spring_trap(end_macro_name
);
1917 process_input_stack();
1919 curenv
->final_break();
1921 process_input_stack();
1924 topdiv
->set_ejecting();
1925 static unsigned char buf
[2] = { LAST_PAGE_EJECTOR
, '\0' };
1926 input_stack::push(make_temp_iterator((char *)buf
));
1927 topdiv
->space(topdiv
->get_page_length(), 1);
1929 process_input_stack();
1930 seen_last_page_ejector
= 1; // should be set already
1931 topdiv
->set_ejecting();
1932 push_page_ejector();
1933 topdiv
->space(topdiv
->get_page_length(), 1);
1935 process_input_stack();
1936 // This will only happen if a trap-invoked macro starts a diversion,
1937 // or if vertical position traps have been disabled.
1938 cleanup_and_exit(0);
1941 // This implements .ex. The input stack must be cleared before calling
1946 input_stack::clear();
1955 end_macro_name
= get_name();
1959 void blank_line_macro()
1961 blank_line_macro_name
= get_name();
1965 static void trapping_blank_line()
1967 if (!blank_line_macro_name
.is_null())
1968 spring_trap(blank_line_macro_name
);
1975 int saved_compatible_flag
= compatible_flag
;
1976 compatible_flag
= 0;
1977 symbol nm
= get_name();
1981 interpolate_macro(nm
);
1982 compatible_flag
= saved_compatible_flag
;
1985 inline int possibly_handle_first_page_transition()
1987 if (topdiv
->before_first_page
&& curdiv
== topdiv
&& !curenv
->is_dummy()) {
1988 handle_first_page_transition();
1995 static int transparent_translate(int cc
)
1997 if (!illegal_input_char(cc
)) {
1998 charinfo
*ci
= charset_table
[cc
];
1999 switch (ci
->get_special_translation(1)) {
2000 case charinfo::TRANSLATE_SPACE
:
2002 case charinfo::TRANSLATE_DUMMY
:
2003 return ESCAPE_AMPERSAND
;
2004 case charinfo::TRANSLATE_HYPHEN_INDICATOR
:
2005 return ESCAPE_PERCENT
;
2007 // This is really ugly.
2008 ci
= ci
->get_translation(1);
2010 int c
= ci
->get_ascii_code();
2013 error("can't translate %1 to special character `%2'"
2014 " in transparent throughput",
2015 input_char_description(cc
),
2023 struct int_stack_element
{
2025 int_stack_element
*next
;
2035 int_stack::int_stack()
2040 int_stack::~int_stack()
2043 int_stack_element
*temp
= top
;
2050 int int_stack::is_empty()
2055 void int_stack::push(int n
)
2057 int_stack_element
*p
= new int_stack_element
;
2064 int int_stack::pop()
2067 int_stack_element
*p
= top
;
2074 int node::reread(int *)
2079 int diverted_space_node::reread(int *bolp
)
2081 if (curenv
->get_fill())
2082 trapping_blank_line();
2089 int diverted_copy_file_node::reread(int *bolp
)
2091 curdiv
->copy_file(filename
.contents());
2096 void process_input_stack()
2098 int_stack trap_bol_stack
;
2101 int suppress_next
= 0;
2103 case token::TOKEN_CHAR
:
2105 unsigned char ch
= tok
.c
;
2107 (ch
== curenv
->control_char
2108 || ch
== curenv
->no_break_control_char
)) {
2109 break_flag
= ch
== curenv
->control_char
;
2110 // skip tabs as well as spaces here
2113 } while (tok
.white_space());
2114 symbol nm
= get_name();
2118 interpolate_macro(nm
);
2122 if (possibly_handle_first_page_transition())
2126 curenv
->add_char(charset_table
[ch
]);
2128 if (tok
.type
!= token::TOKEN_CHAR
)
2138 case token::TOKEN_TRANSPARENT
:
2141 if (possibly_handle_first_page_transition())
2150 curdiv
->transparent_output(transparent_translate(cc
));
2152 curdiv
->transparent_output(n
);
2153 } while (cc
!= '\n' && cc
!= EOF
);
2155 curdiv
->transparent_output('\n');
2160 case token::TOKEN_NEWLINE
:
2162 if (bol
&& !curenv
->get_prev_line_interrupted())
2163 trapping_blank_line();
2170 case token::TOKEN_REQUEST
:
2172 int request_code
= tok
.c
;
2174 switch (request_code
) {
2178 case COPY_FILE_REQUEST
:
2181 case TRANSPARENT_FILE_REQUEST
:
2185 case VJUSTIFY_REQUEST
:
2196 case token::TOKEN_SPACE
:
2198 if (possibly_handle_first_page_transition())
2200 else if (bol
&& !curenv
->get_prev_line_interrupted()) {
2203 nspaces
+= tok
.nspaces();
2205 } while (tok
.space());
2207 trapping_blank_line();
2211 curenv
->add_node(new hmotion_node(curenv
->get_space_width()*nspaces
));
2221 case token::TOKEN_EOF
:
2223 case token::TOKEN_NODE
:
2225 if (possibly_handle_first_page_transition())
2227 else if (tok
.nd
->reread(&bol
)) {
2232 curenv
->add_node(tok
.nd
);
2238 case token::TOKEN_PAGE_EJECTOR
:
2240 continue_page_eject();
2241 // I think we just want to preserve bol.
2245 case token::TOKEN_BEGIN_TRAP
:
2247 trap_bol_stack
.push(bol
);
2251 case token::TOKEN_END_TRAP
:
2253 if (trap_bol_stack
.is_empty())
2254 error("spurious end trap token detected!");
2256 bol
= trap_bol_stack
.pop();
2258 /* I'm not totally happy about this. But I can't think of any other
2259 way to do it. Doing an output_pending_lines() whenever a
2260 TOKEN_END_TRAP is detected doesn't work: for example,
2273 a\%very\%very\%long\%word
2275 will print all but the first lines from the word immediately
2276 after the footer, rather than on the next page. */
2278 if (trap_bol_stack
.is_empty())
2279 curenv
->output_pending_lines();
2291 trap_sprung_flag
= 0;
2295 #ifdef WIDOW_CONTROL
2297 void flush_pending_lines()
2299 while (!tok
.newline() && !tok
.eof())
2301 curenv
->output_pending_lines();
2305 #endif /* WIDOW_CONTROL */
2307 request_or_macro::request_or_macro()
2311 macro
*request_or_macro::to_macro()
2316 request::request(REQUEST_FUNCP pp
) : p(pp
)
2320 void request::invoke(symbol
)
2326 enum { SIZE
= 128 };
2327 unsigned char s
[SIZE
];
2332 char_block::char_block()
2341 void append(unsigned char);
2348 friend class macro_header
;
2349 friend class string_iterator
;
2352 char_list::char_list()
2353 : ptr(0), len(0), head(0), tail(0)
2357 char_list::~char_list()
2360 char_block
*tem
= head
;
2366 int char_list::length()
2371 void char_list::append(unsigned char c
)
2374 head
= tail
= new char_block
;
2378 if (ptr
>= tail
->s
+ char_block::SIZE
) {
2379 tail
->next
= new char_block
;
2394 void append(node
*);
2398 friend class macro_header
;
2399 friend class string_iterator
;
2402 void node_list::append(node
*n
)
2410 tail
= tail
->next
= n
;
2414 int node_list::length()
2417 for (node
*n
= head
; n
!= 0; n
= n
->next
)
2422 node_list::node_list()
2427 node
*node_list::extract()
2435 node_list::~node_list()
2437 delete_node_list(head
);
2440 struct macro_header
{
2445 macro_header() { count
= 1; }
2446 macro_header
*copy(int);
2452 if (p
!= 0 && --(p
->count
) <= 0)
2458 if (!input_stack::get_location(1, &filename
, &lineno
)) {
2466 macro::macro(const macro
&m
)
2467 : p(m
.p
), filename(m
.filename
), lineno(m
.lineno
), length(m
.length
)
2473 macro
¯o::operator=(const macro
&m
)
2475 // don't assign object
2478 if (p
!= 0 && --(p
->count
) <= 0)
2481 filename
= m
.filename
;
2487 void macro::append(unsigned char c
)
2491 p
= new macro_header
;
2492 if (p
->cl
.length() != length
) {
2493 macro_header
*tem
= p
->copy(length
);
2494 if (--(p
->count
) <= 0)
2502 void macro::append(node
*n
)
2506 p
= new macro_header
;
2507 if (p
->cl
.length() != length
) {
2508 macro_header
*tem
= p
->copy(length
);
2509 if (--(p
->count
) <= 0)
2518 void macro::print_size()
2520 errprint("%1", length
);
2523 // make a copy of the first n bytes
2525 macro_header
*macro_header::copy(int n
)
2527 macro_header
*p
= new macro_header
;
2528 char_block
*bp
= cl
.head
;
2529 unsigned char *ptr
= bp
->s
;
2532 if (ptr
>= bp
->s
+ char_block::SIZE
) {
2539 p
->nl
.append(nd
->copy());
2548 object_dictionary_iterator
iter(request_dictionary
);
2549 request_or_macro
*rm
;
2551 while (iter
.get(&s
, (object
**)&rm
)) {
2552 assert(!s
.is_null());
2553 macro
*m
= rm
->to_macro();
2555 errprint("%1\t", s
.contents());
2564 class string_iterator
: public input_iterator
{
2566 const char *how_invoked
;
2570 int count
; // of characters remaining
2576 string_iterator(const macro
&m
, const char *p
= 0, symbol s
= NULL_SYMBOL
);
2579 int get_location(int, const char **, int *);
2583 string_iterator::string_iterator(const macro
&m
, const char *p
, symbol s
)
2584 : mac(m
), how_invoked(p
), newline_flag(0), lineno(1), nm(s
)
2588 bp
= mac
.p
->cl
.head
;
2589 nd
= mac
.p
->nl
.head
;
2599 string_iterator::string_iterator()
2610 int string_iterator::fill(node
**np
)
2617 const unsigned char *p
= eptr
;
2618 if (p
>= bp
->s
+ char_block::SIZE
) {
2630 const unsigned char *e
= bp
->s
+ char_block::SIZE
;
2635 unsigned char c
= *p
;
2636 if (c
== '\n' || c
== ESCAPE_NEWLINE
) {
2650 int string_iterator::peek()
2654 const unsigned char *p
= eptr
;
2657 if (p
>= bp
->s
+ char_block::SIZE
) {
2663 int string_iterator::get_location(int allow_macro
,
2664 const char **filep
, int *linep
)
2668 if (mac
.filename
== 0)
2670 *filep
= mac
.filename
;
2671 *linep
= mac
.lineno
+ lineno
- 1;
2675 void string_iterator::backtrace()
2678 errprint("%1:%2: backtrace", mac
.filename
, mac
.lineno
+ lineno
- 1);
2681 errprint(": %1 `%2'\n", how_invoked
, nm
.contents());
2683 errprint(": %1\n", how_invoked
);
2690 class temp_iterator
: public input_iterator
{
2691 unsigned char *base
;
2692 temp_iterator(const char *, int len
);
2695 friend input_iterator
*make_temp_iterator(const char *);
2701 temp_iterator::temp_iterator(const char *s
, int len
)
2703 base
= new unsigned char[len
];
2704 memcpy(base
, s
, len
);
2709 temp_iterator::~temp_iterator()
2714 class small_temp_iterator
: public input_iterator
{
2716 small_temp_iterator(const char *, int);
2717 ~small_temp_iterator();
2718 enum { BLOCK
= 16 };
2719 static small_temp_iterator
*free_list
;
2720 void *operator new(size_t);
2721 void operator delete(void *);
2723 unsigned char buf
[SIZE
];
2724 friend input_iterator
*make_temp_iterator(const char *);
2727 small_temp_iterator
*small_temp_iterator::free_list
= 0;
2729 void *small_temp_iterator::operator new(size_t n
)
2731 assert(n
== sizeof(small_temp_iterator
));
2733 free_list
= (small_temp_iterator
*)new char[sizeof(small_temp_iterator
)*BLOCK
];
2734 for (int i
= 0; i
< BLOCK
- 1; i
++)
2735 free_list
[i
].next
= free_list
+ i
+ 1;
2736 free_list
[BLOCK
-1].next
= 0;
2738 small_temp_iterator
*p
= free_list
;
2739 free_list
= (small_temp_iterator
*)(free_list
->next
);
2747 void small_temp_iterator::operator delete(void *p
)
2750 ((small_temp_iterator
*)p
)->next
= free_list
;
2751 free_list
= (small_temp_iterator
*)p
;
2755 small_temp_iterator::~small_temp_iterator()
2763 small_temp_iterator::small_temp_iterator(const char *s
, int len
)
2765 for (int i
= 0; i
< len
; i
++)
2771 input_iterator
*make_temp_iterator(const char *s
)
2774 return new small_temp_iterator(s
, 0);
2777 if (n
<= small_temp_iterator::SIZE
)
2778 return new small_temp_iterator(s
, n
);
2780 return new temp_iterator(s
, n
);
2784 // this is used when macros are interpolated using the .macro_name notation
2789 arg_list(const macro
&);
2793 arg_list::arg_list(const macro
&m
) : mac(m
), next(0)
2797 arg_list::~arg_list()
2801 class macro_iterator
: public string_iterator
{
2805 macro_iterator(symbol
, macro
&, const char *how_invoked
= "macro");
2808 int has_args() { return 1; }
2809 input_iterator
*get_arg(int i
);
2810 int nargs() { return argc
; }
2811 void add_arg(const macro
&m
);
2815 input_iterator
*macro_iterator::get_arg(int i
)
2818 return make_temp_iterator(nm
.contents());
2819 if (i
> 0 && i
<= argc
) {
2821 for (int j
= 1; j
< i
; j
++) {
2825 return new string_iterator(p
->mac
);
2831 void macro_iterator::add_arg(const macro
&m
)
2834 for (p
= &args
; *p
; p
= &((*p
)->next
))
2836 *p
= new arg_list(m
);
2840 void macro_iterator::shift(int n
)
2842 while (n
> 0 && argc
> 0) {
2843 arg_list
*tem
= args
;
2851 // This gets used by eg .if '\?xxx\?''.
2853 int operator==(const macro
&m1
, const macro
&m2
)
2855 if (m1
.length
!= m2
.length
)
2857 string_iterator
iter1(m1
);
2858 string_iterator
iter2(m2
);
2862 int c1
= iter1
.get(&nd1
);
2865 int c2
= iter2
.get(&nd2
);
2877 int are_same
= nd1
->type() == nd2
->type() && nd1
->same(nd2
);
2887 static void interpolate_macro(symbol nm
)
2889 request_or_macro
*p
= (request_or_macro
*)request_dictionary
.lookup(nm
);
2892 const char *s
= nm
.contents();
2893 if (strlen(s
) > 2) {
2894 request_or_macro
*r
;
2899 r
= (request_or_macro
*)request_dictionary
.lookup(symbol(buf
));
2901 macro
*m
= r
->to_macro();
2902 if (!m
|| !m
->empty())
2903 warned
= warning(WARN_SPACE
,
2904 "`%1' not defined (probable missing space after `%2')",
2905 nm
.contents(), buf
);
2909 warning(WARN_MAC
, "`%1' not defined", nm
.contents());
2911 request_dictionary
.define(nm
, p
);
2922 static void decode_args(macro_iterator
*mi
)
2924 if (!tok
.newline() && !tok
.eof()) {
2926 int c
= get_copy(&n
);
2930 if (c
== '\n' || c
== EOF
)
2933 int quote_input_level
= 0;
2934 int done_tab_warning
= 0;
2936 quote_input_level
= input_stack::get_level();
2939 while (c
!= EOF
&& c
!= '\n' && !(c
== ' ' && quote_input_level
== 0)) {
2940 if (quote_input_level
> 0 && c
== '\"'
2942 || input_stack::get_level() == quote_input_level
)) {
2955 if (c
== '\t' && quote_input_level
== 0 && !done_tab_warning
) {
2956 warning(WARN_TAB
, "tab character in unquoted macro argument");
2957 done_tab_warning
= 1;
2969 void macro::invoke(symbol nm
)
2971 macro_iterator
*mi
= new macro_iterator(nm
, *this);
2973 input_stack::push(mi
);
2977 macro
*macro::to_macro()
2987 macro_iterator::macro_iterator(symbol s
, macro
&m
, const char *how_invoked
)
2988 : string_iterator(m
, how_invoked
, s
), args(0), argc(0)
2992 macro_iterator::macro_iterator() : args(0), argc(0)
2996 macro_iterator::~macro_iterator()
2999 arg_list
*tem
= args
;
3005 int trap_sprung_flag
= 0;
3006 int postpone_traps_flag
= 0;
3007 symbol postponed_trap
;
3009 void spring_trap(symbol nm
)
3011 assert(!nm
.is_null());
3012 trap_sprung_flag
= 1;
3013 if (postpone_traps_flag
) {
3014 postponed_trap
= nm
;
3017 static char buf
[2] = { BEGIN_TRAP
, 0 };
3018 static char buf2
[2] = { END_TRAP
, '\0' };
3019 input_stack::push(make_temp_iterator(buf2
));
3020 request_or_macro
*p
= lookup_request(nm
);
3021 macro
*m
= p
->to_macro();
3023 input_stack::push(new macro_iterator(nm
, *m
, "trap-invoked macro"));
3025 error("you can't invoke a request with a trap");
3026 input_stack::push(make_temp_iterator(buf
));
3029 void postpone_traps()
3031 postpone_traps_flag
= 1;
3034 int unpostpone_traps()
3036 postpone_traps_flag
= 0;
3037 if (!postponed_trap
.is_null()) {
3038 spring_trap(postponed_trap
);
3039 postponed_trap
= NULL_SYMBOL
;
3048 macro_iterator
*mi
= new macro_iterator
;
3049 int reading_from_terminal
= isatty(fileno(stdin
));
3051 if (!tok
.newline() && !tok
.eof()) {
3052 int c
= get_copy(NULL
);
3055 while (c
!= EOF
&& c
!= '\n' && c
!= ' ') {
3056 if (!illegal_input_char(c
)) {
3057 if (reading_from_terminal
)
3068 if (reading_from_terminal
) {
3069 fputc(had_prompt
? ':' : '\a', stderr
);
3072 input_stack::push(mi
);
3076 while ((c
= getchar()) != EOF
) {
3077 if (illegal_input_char(c
))
3078 warning(WARN_INPUT
, "illegal input character code %1", int(c
));
3091 if (reading_from_terminal
)
3093 input_stack::push(new string_iterator(mac
));
3097 void do_define_string(int append
)
3111 else if (!tok
.space()) {
3112 error("bad string definition");
3123 request_or_macro
*rm
= (request_or_macro
*)request_dictionary
.lookup(nm
);
3124 macro
*mm
= rm
? rm
->to_macro() : 0;
3127 while (c
!= '\n' && c
!= EOF
) {
3131 mac
.append((unsigned char)c
);
3136 request_dictionary
.define(nm
, mm
);
3142 void define_string()
3144 do_define_string(0);
3147 void append_string()
3149 do_define_string(1);
3152 void define_character()
3157 charinfo
*ci
= tok
.get_char(1);
3167 else if (!tok
.space()) {
3168 error("bad character definition");
3174 while (c
== ' ' || c
== '\t')
3178 macro
*m
= new macro
;
3179 while (c
!= '\n' && c
!= EOF
) {
3183 m
->append((unsigned char)c
);
3186 m
= ci
->set_macro(m
);
3193 static void remove_character()
3196 while (!tok
.newline() && !tok
.eof()) {
3197 if (!tok
.space() && !tok
.tab()) {
3198 charinfo
*ci
= tok
.get_char(1);
3201 macro
*m
= ci
->set_macro(0);
3210 static void interpolate_string(symbol nm
)
3212 request_or_macro
*p
= lookup_request(nm
);
3213 macro
*m
= p
->to_macro();
3215 error("you can only invoke a string using \\*");
3217 string_iterator
*si
= new string_iterator(*m
, "string", nm
);
3218 input_stack::push(si
);
3222 /* This class is used for the implementation of \$@. It is used for
3223 each of the closing double quotes. It artificially increases the
3224 input level by 2, so that the closing double quote will appear to have
3225 the same input level as the opening quote. */
3227 class end_quote_iterator
: public input_iterator
{
3228 unsigned char buf
[1];
3230 end_quote_iterator();
3231 ~end_quote_iterator() { }
3232 int internal_level() { return 2; }
3235 end_quote_iterator::end_quote_iterator()
3242 static void interpolate_arg(symbol nm
)
3244 const char *s
= nm
.contents();
3245 if (!s
|| *s
== '\0')
3246 copy_mode_error("missing argument name");
3247 else if (s
[1] == 0 && csdigit(s
[0]))
3248 input_stack::push(input_stack::get_arg(s
[0] - '0'));
3249 else if (s
[0] == '*' && s
[1] == '\0') {
3250 for (int i
= input_stack::nargs(); i
> 0; i
--) {
3251 input_stack::push(input_stack::get_arg(i
));
3253 input_stack::push(make_temp_iterator(" "));
3256 else if (s
[0] == '@' && s
[1] == '\0') {
3257 for (int i
= input_stack::nargs(); i
> 0; i
--) {
3258 input_stack::push(new end_quote_iterator
);
3259 input_stack::push(input_stack::get_arg(i
));
3260 input_stack::push(make_temp_iterator(i
== 1 ? "\"" : " \""));
3265 for (p
= s
; *p
&& csdigit(*p
); p
++)
3268 copy_mode_error("bad argument name `%1'", s
);
3270 input_stack::push(input_stack::get_arg(atoi(s
)));
3274 void handle_first_page_transition()
3277 topdiv
->begin_page();
3280 // We push back a token by wrapping it up in a token_node, and
3281 // wrapping that up in a string_iterator.
3283 static void push_token(const token
&t
)
3286 m
.append(new token_node(t
));
3287 input_stack::push(new string_iterator(m
));
3290 void push_page_ejector()
3292 static char buf
[2] = { PAGE_EJECTOR
, '\0' };
3293 input_stack::push(make_temp_iterator(buf
));
3296 void handle_initial_request(unsigned char code
)
3302 mac
.append(new token_node(tok
));
3303 input_stack::push(new string_iterator(mac
));
3304 input_stack::push(make_temp_iterator(buf
));
3305 topdiv
->begin_page();
3309 void handle_initial_title()
3311 handle_initial_request(TITLE_REQUEST
);
3314 // this should be local to define_macro, but cfront 1.2 doesn't support that
3315 static symbol
dot_symbol(".");
3317 enum define_mode
{ DEFINE_NORMAL
, DEFINE_APPEND
, DEFINE_IGNORE
};
3319 void do_define_macro(define_mode mode
, int indirect
)
3323 symbol temp1
= get_name(1);
3324 if (temp1
.is_null()) {
3328 symbol temp2
= get_name();
3329 input_stack::push(make_temp_iterator("\n"));
3330 if (!temp2
.is_null()) {
3331 interpolate_string(temp2
);
3332 input_stack::push(make_temp_iterator(" "));
3334 interpolate_string(temp1
);
3335 input_stack::push(make_temp_iterator(" "));
3338 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
3345 term
= get_name(); // the request that terminates the definition
3348 while (!tok
.newline() && !tok
.eof())
3350 const char *start_filename
;
3352 int have_start_location
= input_stack::get_location(0, &start_filename
,
3355 // doing this here makes the line numbers come out right
3356 int c
= get_copy(&n
, 1);
3359 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
3360 request_or_macro
*rm
=
3361 (request_or_macro
*)request_dictionary
.lookup(nm
);
3363 mm
= rm
->to_macro();
3364 if (mm
&& mode
== DEFINE_APPEND
)
3369 while (c
== ESCAPE_NEWLINE
) {
3370 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
)
3372 c
= get_copy(&n
, 1);
3374 if (bol
&& c
== '.') {
3375 const char *s
= term
.contents();
3377 // see if it matches term
3379 for (i
= 0; s
[i
] != 0; i
++) {
3381 if ((unsigned char)s
[i
] != d
)
3385 && ((i
== 2 && compatible_flag
)
3386 || (d
= get_copy(&n
)) == ' '
3387 || d
== '\n')) { // we found it
3392 if (mode
== DEFINE_APPEND
|| mode
== DEFINE_NORMAL
) {
3395 request_dictionary
.define(nm
, mm
);
3399 if (term
!= dot_symbol
) {
3401 interpolate_macro(term
);
3407 if (mode
== DEFINE_APPEND
|| mode
== DEFINE_NORMAL
) {
3409 for (int j
= 0; j
< i
; j
++)
3415 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
3416 if (have_start_location
)
3417 error_with_file_and_line(start_filename
, start_lineno
,
3418 "end of file while defining macro `%1'",
3421 error("end of file while defining macro `%1'", nm
.contents());
3424 if (have_start_location
)
3425 error_with_file_and_line(start_filename
, start_lineno
,
3426 "end of file while ignoring input lines");
3428 error("end of file while ignoring input lines");
3433 if (mode
== DEFINE_NORMAL
|| mode
== DEFINE_APPEND
) {
3440 c
= get_copy(&n
, 1);
3446 do_define_macro(DEFINE_NORMAL
, 0);
3449 void define_indirect_macro()
3451 do_define_macro(DEFINE_NORMAL
, 1);
3456 do_define_macro(DEFINE_APPEND
, 0);
3462 do_define_macro(DEFINE_IGNORE
, 0);
3469 symbol s
= get_name();
3472 request_dictionary
.remove(s
);
3479 symbol s1
= get_name(1);
3480 if (!s1
.is_null()) {
3481 symbol s2
= get_name(1);
3483 request_dictionary
.rename(s1
, s2
);
3490 symbol s1
= get_name(1);
3491 if (!s1
.is_null()) {
3492 symbol s2
= get_name(1);
3493 if (!s2
.is_null()) {
3494 if (!request_dictionary
.alias(s1
, s2
))
3495 warning(WARN_MAC
, "`%1' not defined", s2
.contents());
3503 symbol s
= get_name(1);
3505 request_or_macro
*p
= lookup_request(s
);
3506 macro
*m
= p
->to_macro();
3508 error("cannot chop request");
3509 else if (m
->length
== 0)
3510 error("cannot chop empty macro");
3517 void substring_macro()
3520 symbol s
= get_name(1);
3521 if (!s
.is_null() && get_integer(&start
)) {
3522 request_or_macro
*p
= lookup_request(s
);
3523 macro
*m
= p
->to_macro();
3525 error("cannot substring request");
3532 if (!has_arg() || get_integer(&end
)) {
3542 if (start
>= m
->length
|| start
== end
) {
3545 if (--(m
->p
->count
) <= 0)
3550 else if (start
== 0)
3553 string_iterator
iter(*m
);
3555 for (i
= 0; i
< start
; i
++)
3556 if (iter
.get(0) == EOF
)
3559 for (; i
< end
; i
++) {
3561 int c
= iter
.get(&nd
);
3567 mac
.append((unsigned char)c
);
3581 if (ret
.is_null()) {
3591 else if (!tok
.space()) {
3592 error("bad string definition");
3603 while (c
!= '\n' && c
!= EOF
) {
3608 reg
*r
= (reg
*)number_reg_dictionary
.lookup(ret
);
3612 set_number_reg(ret
, len
);
3615 void asciify_macro()
3617 symbol s
= get_name(1);
3619 request_or_macro
*p
= lookup_request(s
);
3620 macro
*m
= p
->to_macro();
3622 error("cannot asciify request");
3625 string_iterator
iter(*m
);
3628 int c
= iter
.get(&nd
);
3642 static void interpolate_environment_variable(symbol nm
)
3644 const char *s
= getenv(nm
.contents());
3646 input_stack::push(make_temp_iterator(s
));
3649 void interpolate_number_reg(symbol nm
, int inc
)
3651 reg
*r
= lookup_number_reg(nm
);
3656 input_stack::push(make_temp_iterator(r
->get_string()));
3659 static void interpolate_number_format(symbol nm
)
3661 reg
*r
= (reg
*)number_reg_dictionary
.lookup(nm
);
3663 input_stack::push(make_temp_iterator(r
->get_format()));
3666 static int get_delim_number(units
*n
, int si
, int prev_value
)
3670 if (start
.delimiter(1)) {
3672 if (get_number(n
, si
, prev_value
)) {
3674 warning(WARN_DELIM
, "closing delimiter does not match");
3681 static int get_delim_number(units
*n
, int si
)
3685 if (start
.delimiter(1)) {
3687 if (get_number(n
, si
)) {
3689 warning(WARN_DELIM
, "closing delimiter does not match");
3696 static int get_line_arg(units
*n
, int si
, charinfo
**cp
)
3700 if (start
.delimiter(1)) {
3702 if (get_number(n
, si
)) {
3706 *cp
= tok
.get_char(1);
3710 warning(WARN_DELIM
, "closing delimiter does not match");
3717 static int read_size(int *x
)
3727 else if (c
== '+') {
3738 // allow an increment either before or after the left parenthesis
3744 else if (c
== '+') {
3759 val
= val
*10 + (c
- '0');
3764 else if (csdigit(c
)) {
3766 if (!inc
&& c
!= '0' && c
< '4') {
3772 val
= val
*10 + (c
- '0');
3776 else if (!tok
.delimiter(1))
3782 ? get_number(&val
, 'z')
3783 : get_number(&val
, 'z', curenv
->get_requested_point_size())))
3785 if (!(start
.ch() == '[' && tok
.ch() == ']') && start
!= tok
) {
3786 if (start
.ch() == '[')
3787 error("missing `]'");
3789 error("missing closing delimiter");
3799 *x
= curenv
->get_requested_point_size() + val
;
3802 *x
= curenv
->get_requested_point_size() - val
;
3810 error("bad digit in point size");
3815 static symbol
get_delim_name()
3820 error("end of input at start of delimited name");
3823 if (start
.newline()) {
3824 error("can't delimit name with a newline");
3827 int start_level
= input_stack::get_level();
3828 char abuf
[ABUF_SIZE
];
3830 int buf_size
= ABUF_SIZE
;
3833 if (i
+ 1 > buf_size
) {
3835 buf
= new char [ABUF_SIZE
*2];
3836 memcpy(buf
, abuf
, buf_size
);
3837 buf_size
= ABUF_SIZE
*2;
3840 char *old_buf
= buf
;
3841 buf
= new char[buf_size
*2];
3842 memcpy(buf
, old_buf
, buf_size
);
3849 && (compatible_flag
|| input_stack::get_level() == start_level
))
3851 if ((buf
[i
] = tok
.ch()) == 0) {
3852 error("missing delimiter (got %1)", tok
.description());
3862 error("empty delimited name");
3878 static void do_register()
3882 if (!start
.delimiter(1))
3885 symbol nm
= get_long_name(1);
3890 reg
*r
= (reg
*)number_reg_dictionary
.lookup(nm
);
3892 if (!r
|| !r
->get_value(&prev_value
))
3895 if (!get_number(&val
, 'u', prev_value
))
3898 warning(WARN_DELIM
, "closing delimiter does not match");
3902 set_number_reg(nm
, val
);
3905 // this implements the \w escape sequence
3907 static void do_width()
3911 int start_level
= input_stack::get_level();
3912 environment
env(curenv
);
3913 environment
*oldenv
= curenv
;
3918 warning(WARN_DELIM
, "missing closing delimiter");
3921 if (tok
.newline()) {
3922 warning(WARN_DELIM
, "missing closing delimiter");
3923 input_stack::push(make_temp_iterator("\n"));
3927 && (compatible_flag
|| input_stack::get_level() == start_level
))
3932 units x
= env
.get_input_line_position().to_units();
3933 input_stack::push(make_temp_iterator(i_to_a(x
)));
3934 env
.width_registers();
3938 charinfo
*page_character
;
3940 void set_page_character()
3942 page_character
= get_optional_char();
3946 static const symbol
percent_symbol("%");
3948 void read_title_parts(node
**part
, hunits
*part_width
)
3951 if (tok
.newline() || tok
.eof())
3954 int start_level
= input_stack::get_level();
3956 for (int i
= 0; i
< 3; i
++) {
3957 while (!tok
.newline() && !tok
.eof()) {
3959 && (compatible_flag
|| input_stack::get_level() == start_level
)) {
3963 if (page_character
!= 0 && tok
.get_char() == page_character
)
3964 interpolate_number_reg(percent_symbol
, 0);
3969 curenv
->wrap_up_tab();
3970 part_width
[i
] = curenv
->get_input_line_position();
3971 part
[i
] = curenv
->extract_output_line();
3973 while (!tok
.newline() && !tok
.eof())
3977 class non_interpreted_node
: public node
{
3980 non_interpreted_node(const macro
&);
3981 int interpret(macro
*);
3987 non_interpreted_node::non_interpreted_node(const macro
&m
) : mac(m
)
3991 int non_interpreted_node::same(node
*nd
)
3993 return mac
== ((non_interpreted_node
*)nd
)->mac
;
3996 const char *non_interpreted_node::type()
3998 return "non_interpreted_node";
4001 node
*non_interpreted_node::copy()
4003 return new non_interpreted_node(mac
);
4006 int non_interpreted_node::interpret(macro
*m
)
4008 string_iterator
si(mac
);
4022 static node
*do_non_interpreted()
4027 while ((c
= get_copy(&n
)) != ESCAPE_QUESTION
&& c
!= EOF
&& c
!= '\n')
4032 if (c
== EOF
|| c
== '\n') {
4033 error("missing \\?");
4036 return new non_interpreted_node(mac
);
4039 static void encode_char (macro
*mac
, char c
)
4042 if ((font::use_charnames_in_special
) && tok
.special()) {
4043 charinfo
*ci
=tok
.get_char(1);
4044 const char *s
=ci
->get_symbol()->contents();
4046 if (s
[0] != (char)0) {
4050 while (s
[i
] != (char)0) {
4058 error("%1 is illegal within \\X", tok
.description());
4061 if ((font::use_charnames_in_special
) && (c
== '\\')) {
4063 * add escape escape sequence
4075 int start_level
= input_stack::get_level();
4078 tok
!= start
|| input_stack::get_level() != start_level
;
4081 warning(WARN_DELIM
, "missing closing delimiter");
4084 if (tok
.newline()) {
4085 input_stack::push(make_temp_iterator("\n"));
4086 warning(WARN_DELIM
, "missing closing delimiter");
4094 else if (tok
.leader())
4096 else if (tok
.backspace())
4100 encode_char(&mac
, c
);
4102 return new special_node(mac
);
4105 void special_node::tprint(troff_output_file
*out
)
4108 string_iterator
iter(mac
);
4110 int c
= iter
.get(NULL
);
4113 for (const char *s
= ::asciify(c
); *s
; s
++)
4114 tprint_char(out
, *s
);
4119 int get_file_line(const char **filename
, int *lineno
)
4121 return input_stack::get_location(0, filename
, lineno
);
4127 if (get_integer(&n
)) {
4128 const char *filename
= 0;
4130 symbol s
= get_long_name();
4131 filename
= s
.contents();
4133 (void)input_stack::set_location(filename
, n
-1);
4138 static int nroff_mode
= 0;
4140 static void nroff_request()
4146 static void troff_request()
4152 static void skip_alternative()
4155 // ensure that ``.if 0\{'' works as expected
4156 if (tok
.left_brace())
4160 c
= input_stack::get(NULL
);
4163 if (c
== ESCAPE_LEFT_BRACE
)
4165 else if (c
== ESCAPE_RIGHT_BRACE
)
4167 else if (c
== escape_char
&& escape_char
> 0)
4168 switch(input_stack::get(NULL
)) {
4176 while ((c
= input_stack::get(NULL
)) != '\n' && c
!= EOF
)
4180 Note that the level can properly be < 0, eg
4186 So don't give an error message in this case.
4188 if (level
<= 0 && c
== '\n')
4194 static void begin_alternative()
4196 while (tok
.space() || tok
.left_brace())
4206 static int_stack if_else_stack
;
4213 while (tok
.ch() == '!') {
4218 unsigned char c
= tok
.ch();
4221 result
= !nroff_mode
;
4223 else if (c
== 'n') {
4225 result
= nroff_mode
;
4227 else if (c
== 'v') {
4231 else if (c
== 'o') {
4232 result
= (topdiv
->get_page_number() & 1);
4235 else if (c
== 'e') {
4236 result
= !(topdiv
->get_page_number() & 1);
4239 else if (c
== 'd' || c
== 'r') {
4241 symbol nm
= get_name(1);
4247 ? request_dictionary
.lookup(nm
) != 0
4248 : number_reg_dictionary
.lookup(nm
) != 0);
4250 else if (c
== 'c') {
4253 charinfo
*ci
= tok
.get_char(1);
4258 result
= character_exists(ci
, curenv
);
4261 else if (tok
.space())
4263 else if (tok
.delimiter()) {
4265 int delim_level
= input_stack::get_level();
4266 environment
env1(curenv
);
4267 environment
env2(curenv
);
4268 environment
*oldenv
= curenv
;
4270 for (int i
= 0; i
< 2; i
++) {
4273 if (tok
.newline() || tok
.eof()) {
4274 warning(WARN_DELIM
, "missing closing delimiter");
4280 && (compatible_flag
|| input_stack::get_level() == delim_level
))
4286 node
*n1
= env1
.extract_output_line();
4287 node
*n2
= env2
.extract_output_line();
4288 result
= same_node_list(n1
, n2
);
4289 delete_node_list(n1
);
4290 delete_node_list(n2
);
4296 if (!get_number(&n
, 'u')) {
4306 begin_alternative();
4312 void if_else_request()
4314 if_else_stack
.push(do_if_request());
4324 if (if_else_stack
.is_empty()) {
4325 warning(WARN_EL
, "unbalanced .el request");
4329 if (if_else_stack
.pop())
4332 begin_alternative();
4336 static int while_depth
= 0;
4337 static int while_break_flag
= 0;
4339 void while_request()
4344 mac
.append(new token_node(tok
));
4347 int c
= input_stack::get(&n
);
4363 if (c
== ESCAPE_LEFT_BRACE
)
4365 else if (c
== ESCAPE_RIGHT_BRACE
)
4367 else if (c
== escape_char
)
4370 if (c
== '\n' && level
<= 0)
4375 error("unbalanced \\{ \\}");
4378 input_stack::add_boundary();
4380 input_stack::push(new string_iterator(mac
, "while loop"));
4382 if (!do_if_request()) {
4383 while (input_stack::get(NULL
) != EOF
)
4387 process_input_stack();
4388 if (while_break_flag
) {
4389 while_break_flag
= 0;
4393 input_stack::remove_boundary();
4399 void while_break_request()
4402 error("no while loop");
4406 while_break_flag
= 1;
4407 while (input_stack::get(NULL
) != EOF
)
4413 void while_continue_request()
4416 error("no while loop");
4420 while (input_stack::get(NULL
) != EOF
)
4430 symbol nm
= get_long_name(1);
4434 while (!tok
.newline() && !tok
.eof())
4437 FILE *fp
= fopen(nm
.contents(), "r");
4439 input_stack::push(new file_iterator(fp
, nm
.contents()));
4441 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
4446 // like .so but use popen()
4450 #ifdef POPEN_MISSING
4451 error("pipes not available on this system");
4453 #else /* not POPEN_MISSING */
4454 if (tok
.newline() || tok
.eof())
4455 error("missing command");
4458 while ((c
= get_copy(NULL
)) == ' ' || c
== '\t')
4461 char *buf
= new char[buf_size
];
4463 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(NULL
)) {
4464 const char *s
= asciify(c
);
4465 int slen
= strlen(s
);
4466 if (buf_used
+ slen
+ 1> buf_size
) {
4467 char *old_buf
= buf
;
4468 int old_buf_size
= buf_size
;
4470 buf
= new char[buf_size
];
4471 memcpy(buf
, old_buf
, old_buf_size
);
4474 strcpy(buf
+ buf_used
, s
);
4477 buf
[buf_used
] = '\0';
4479 FILE *fp
= popen(buf
, POPEN_RT
);
4481 input_stack::push(new file_iterator(fp
, symbol(buf
).contents(), 1));
4483 error("can't open pipe to process `%1': %2", buf
, strerror(errno
));
4487 #endif /* not POPEN_MISSING */
4493 static int llx_reg_contents
= 0;
4494 static int lly_reg_contents
= 0;
4495 static int urx_reg_contents
= 0;
4496 static int ury_reg_contents
= 0;
4498 struct bounding_box
{
4499 int llx
, lly
, urx
, ury
;
4502 /* Parse the argument to a %%BoundingBox comment. Return 1 if it
4503 contains 4 numbers, 2 if it contains (atend), 0 otherwise. */
4505 int parse_bounding_box(char *p
, bounding_box
*bb
)
4507 if (sscanf(p
, "%d %d %d %d",
4508 &bb
->llx
, &bb
->lly
, &bb
->urx
, &bb
->ury
) == 4)
4511 /* The Document Structuring Conventions say that the numbers
4512 should be integers. Unfortunately some broken applications
4514 double x1
, x2
, x3
, x4
;
4515 if (sscanf(p
, "%lf %lf %lf %lf", &x1
, &x2
, &x3
, &x4
) == 4) {
4523 for (; *p
== ' ' || *p
== '\t'; p
++)
4525 if (strncmp(p
, "(atend)", 7) == 0) {
4530 bb
->llx
= bb
->lly
= bb
->urx
= bb
->ury
= 0;
4534 // This version is taken from psrm.cc
4536 #define PS_LINE_MAX 255
4537 cset
white_space("\n\r \t");
4539 int ps_get_line(char *buf
, FILE *fp
, const char* filename
)
4548 while (c
!= '\r' && c
!= '\n' && c
!= EOF
) {
4549 if ((c
< 0x1b && !white_space(c
)) || c
== 0x7f)
4550 error("illegal input character code %1 in `%2'", int(c
), filename
);
4551 else if (i
< PS_LINE_MAX
)
4555 error("PostScript file `%1' is non-conforming "
4556 "because length of line exceeds 255", filename
);
4564 if (c
!= EOF
&& c
!= '\n')
4570 inline void assign_registers(int llx
, int lly
, int urx
, int ury
)
4572 llx_reg_contents
= llx
;
4573 lly_reg_contents
= lly
;
4574 urx_reg_contents
= urx
;
4575 ury_reg_contents
= ury
;
4578 void do_ps_file(FILE *fp
, const char* filename
)
4582 char buf
[PS_LINE_MAX
];
4583 llx_reg_contents
= lly_reg_contents
=
4584 urx_reg_contents
= ury_reg_contents
= 0;
4585 if (!ps_get_line(buf
, fp
, filename
)) {
4586 error("`%1' is empty", filename
);
4589 if (strncmp("%!PS-Adobe-", buf
, 11) != 0) {
4590 error("`%1' is not conforming to the Document Structuring Conventions",
4594 while (ps_get_line(buf
, fp
, filename
) != 0) {
4595 if (buf
[0] != '%' || buf
[1] != '%'
4596 || strncmp(buf
+ 2, "EndComments", 11) == 0)
4598 if (strncmp(buf
+ 2, "BoundingBox:", 12) == 0) {
4599 int res
= parse_bounding_box(buf
+ 14, &bb
);
4601 assign_registers(bb
.llx
, bb
.lly
, bb
.urx
, bb
.ury
);
4603 } else if (res
== 2) {
4608 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
4617 /* in the trailer, the last BoundingBox comment is significant */
4618 for (offset
= 512; !last_try
; offset
*= 2) {
4619 int had_trailer
= 0;
4621 if (offset
> 32768 || fseek(fp
, -offset
, 2) == -1) {
4623 if (fseek(fp
, 0L, 0) == -1)
4626 while (ps_get_line(buf
, fp
, filename
) != 0) {
4627 if (buf
[0] == '%' && buf
[1] == '%') {
4629 if (strncmp(buf
+ 2, "Trailer", 7) == 0)
4633 if (strncmp(buf
+ 2, "BoundingBox:", 12) == 0) {
4634 int res
= parse_bounding_box(buf
+ 14, &bb
);
4637 else if (res
== 2) {
4638 error("`(atend)' not allowed in trailer of `%1'", filename
);
4642 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
4651 assign_registers(bb
.llx
, bb
.lly
, bb
.urx
, bb
.ury
);
4656 error("%%%%BoundingBox comment not found in `%1'", filename
);
4659 void ps_bbox_request()
4661 symbol nm
= get_long_name(1);
4665 while (!tok
.newline() && !tok
.eof())
4668 // PS files might contain non-printable characters, such as ^Z
4669 // and CRs not followed by an LF, so open them in binary mode.
4670 FILE *fp
= fopen(nm
.contents(), FOPEN_RB
);
4672 do_ps_file(fp
, nm
.contents());
4676 error("can't open `%1': %2", nm
.contents(), strerror(errno
));
4681 const char *asciify(int c
)
4684 buf
[0] = escape_char
== '\0' ? '\\' : escape_char
;
4685 buf
[1] = buf
[2] = '\0';
4687 case ESCAPE_QUESTION
:
4690 case ESCAPE_AMPERSAND
:
4693 case ESCAPE_UNDERSCORE
:
4699 case ESCAPE_CIRCUMFLEX
:
4702 case ESCAPE_LEFT_BRACE
:
4705 case ESCAPE_RIGHT_BRACE
:
4708 case ESCAPE_LEFT_QUOTE
:
4711 case ESCAPE_RIGHT_QUOTE
:
4729 case ESCAPE_PERCENT
:
4736 if (illegal_input_char(c
))
4746 const char *input_char_description(int c
)
4750 return "a newline character";
4752 return "a backspace character";
4754 return "a leader character";
4756 return "a tab character";
4758 return "a space character";
4762 static char buf
[sizeof("magic character code ") + 1 + INT_DIGITS
];
4763 if (illegal_input_char(c
)) {
4764 const char *s
= asciify(c
);
4771 sprintf(buf
, "magic character code %d", c
);
4780 sprintf(buf
, "character code %d", c
);
4784 // .tm, .tm1, and .tmc
4786 void do_terminal(int newline
, int string_like
)
4788 if (!tok
.newline() && !tok
.eof()) {
4792 if (string_like
&& c
== '"') {
4796 if (c
!= ' ' && c
!= '\t')
4799 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(NULL
))
4800 fputs(asciify(c
), stderr
);
4803 fputc('\n', stderr
);
4818 void terminal_continue()
4823 dictionary
stream_dictionary(20);
4825 void do_open(int append
)
4827 symbol stream
= get_name(1);
4828 if (!stream
.is_null()) {
4829 symbol filename
= get_long_name(1);
4830 if (!filename
.is_null()) {
4832 FILE *fp
= fopen(filename
.contents(), append
? "a" : "w");
4834 error("can't open `%1' for %2: %3",
4835 filename
.contents(),
4836 append
? "appending" : "writing",
4838 fp
= (FILE *)stream_dictionary
.remove(stream
);
4841 fp
= (FILE *)stream_dictionary
.lookup(stream
, fp
);
4854 void opena_request()
4859 void close_request()
4861 symbol stream
= get_name(1);
4862 if (!stream
.is_null()) {
4863 FILE *fp
= (FILE *)stream_dictionary
.remove(stream
);
4865 error("no stream named `%1'", stream
.contents());
4872 void write_request()
4874 symbol stream
= get_name(1);
4875 if (stream
.is_null()) {
4879 FILE *fp
= (FILE *)stream_dictionary
.lookup(stream
);
4881 error("no stream named `%1'", stream
.contents());
4886 while ((c
= get_copy(NULL
)) == ' ')
4890 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(NULL
))
4891 fputs(asciify(c
), fp
);
4897 void write_macro_request()
4899 symbol stream
= get_name(1);
4900 if (stream
.is_null()) {
4904 FILE *fp
= (FILE *)stream_dictionary
.lookup(stream
);
4906 error("no stream named `%1'", stream
.contents());
4910 symbol s
= get_name(1);
4915 request_or_macro
*p
= lookup_request(s
);
4916 macro
*m
= p
->to_macro();
4918 error("cannot write request");
4920 string_iterator
iter(*m
);
4922 int c
= iter
.get(0);
4925 fputs(asciify(c
), fp
);
4932 static void init_charset_table()
4935 strcpy(buf
, "char");
4936 for (int i
= 0; i
< 256; i
++) {
4937 strcpy(buf
+ 4, i_to_a(i
));
4938 charset_table
[i
] = get_charinfo(symbol(buf
));
4939 charset_table
[i
]->set_ascii_code(i
);
4941 charset_table
[i
]->set_hyphenation_code(cmlower(i
));
4943 charset_table
['.']->set_flags(charinfo::ENDS_SENTENCE
);
4944 charset_table
['?']->set_flags(charinfo::ENDS_SENTENCE
);
4945 charset_table
['!']->set_flags(charinfo::ENDS_SENTENCE
);
4946 charset_table
['-']->set_flags(charinfo::BREAK_AFTER
);
4947 charset_table
['"']->set_flags(charinfo::TRANSPARENT
);
4948 charset_table
['\'']->set_flags(charinfo::TRANSPARENT
);
4949 charset_table
[')']->set_flags(charinfo::TRANSPARENT
);
4950 charset_table
[']']->set_flags(charinfo::TRANSPARENT
);
4951 charset_table
['*']->set_flags(charinfo::TRANSPARENT
);
4952 get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT
);
4953 get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT
);
4954 get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER
);
4955 get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
4956 get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
4957 get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
4958 get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY
);
4959 get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY
);
4960 page_character
= charset_table
['%'];
4964 void do_translate(int translate_transparent
)
4967 while (!tok
.newline() && !tok
.eof()) {
4969 // This is a really bizarre troff feature.
4971 translate_space_to_dummy
= tok
.dummy();
4972 if (tok
.newline() || tok
.eof())
4977 charinfo
*ci1
= tok
.get_char(1);
4981 if (tok
.newline() || tok
.eof()) {
4982 ci1
->set_special_translation(charinfo::TRANSLATE_SPACE
,
4983 translate_transparent
);
4987 ci1
->set_special_translation(charinfo::TRANSLATE_SPACE
,
4988 translate_transparent
);
4989 else if (tok
.dummy())
4990 ci1
->set_special_translation(charinfo::TRANSLATE_DUMMY
,
4991 translate_transparent
);
4992 else if (tok
.hyphen_indicator())
4993 ci1
->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR
,
4994 translate_transparent
);
4996 charinfo
*ci2
= tok
.get_char(1);
5000 ci1
->set_translation(0, translate_transparent
);
5002 ci1
->set_translation(ci2
, translate_transparent
);
5014 void translate_no_transparent()
5022 if (get_integer(&flags
))
5024 charinfo
*ci
= tok
.get_char(1);
5026 charinfo
*tem
= ci
->get_translation();
5029 ci
->set_flags(flags
);
5036 void hyphenation_code()
5039 while (!tok
.newline() && !tok
.eof()) {
5040 charinfo
*ci
= tok
.get_char(1);
5045 unsigned char c
= tok
.ch();
5047 error("hyphenation code must be ordinary character");
5051 error("hyphenation code cannot be digit");
5054 ci
->set_hyphenation_code(c
);
5061 charinfo
*token::get_char(int required
)
5063 if (type
== TOKEN_CHAR
)
5064 return charset_table
[c
];
5065 if (type
== TOKEN_SPECIAL
)
5066 return get_charinfo(nm
);
5067 if (type
== TOKEN_NUMBERED_CHAR
)
5068 return get_charinfo_by_number(val
);
5069 if (type
== TOKEN_ESCAPE
) {
5070 if (escape_char
!= 0)
5071 return charset_table
[escape_char
];
5073 error("`\\e' used while no current escape character");
5078 if (type
== TOKEN_EOF
|| type
== TOKEN_NEWLINE
)
5079 warning(WARN_MISSING
, "missing normal or special character");
5081 error("normal or special character expected (got %1)", description());
5086 charinfo
*get_optional_char()
5090 charinfo
*ci
= tok
.get_char();
5092 check_missing_character();
5098 void check_missing_character()
5100 if (!tok
.newline() && !tok
.eof() && !tok
.right_brace() && !tok
.tab())
5101 error("normal or special character expected (got %1): "
5102 "treated as missing",
5106 int token::add_to_node_list(node
**pp
)
5112 *pp
= (*pp
)->add_char(charset_table
[c
], curenv
, &w
);
5118 if (escape_char
!= 0)
5119 *pp
= (*pp
)->add_char(charset_table
[escape_char
], curenv
, &w
);
5121 case TOKEN_HYPHEN_INDICATOR
:
5122 *pp
= (*pp
)->add_discretionary_hyphen();
5124 case TOKEN_ITALIC_CORRECTION
:
5125 *pp
= (*pp
)->add_italic_correction(&w
);
5127 case TOKEN_LEFT_BRACE
:
5129 case TOKEN_MARK_INPUT
:
5130 set_number_reg(nm
, curenv
->get_input_line_position().to_units());
5136 case TOKEN_NUMBERED_CHAR
:
5137 *pp
= (*pp
)->add_char(get_charinfo_by_number(val
), curenv
, &w
);
5139 case TOKEN_RIGHT_BRACE
:
5142 n
= new hmotion_node(curenv
->get_space_width());
5145 *pp
= (*pp
)->add_char(get_charinfo(nm
), curenv
, &w
);
5157 void token::process()
5159 if (possibly_handle_first_page_transition())
5162 case TOKEN_BACKSPACE
:
5163 curenv
->add_node(new hmotion_node(-curenv
->get_space_width()));
5166 curenv
->add_char(charset_table
[c
]);
5169 curenv
->add_node(new dummy_node
);
5178 if (escape_char
!= 0)
5179 curenv
->add_char(charset_table
[escape_char
]);
5181 case TOKEN_BEGIN_TRAP
:
5182 case TOKEN_END_TRAP
:
5183 case TOKEN_PAGE_EJECTOR
:
5184 // these are all handled in process_input_stack()
5186 case TOKEN_HYPHEN_INDICATOR
:
5187 curenv
->add_hyphen_indicator();
5189 case TOKEN_INTERRUPT
:
5190 curenv
->interrupt();
5192 case TOKEN_ITALIC_CORRECTION
:
5193 curenv
->add_italic_correction();
5196 curenv
->handle_tab(1);
5198 case TOKEN_LEFT_BRACE
:
5200 case TOKEN_MARK_INPUT
:
5201 set_number_reg(nm
, curenv
->get_input_line_position().to_units());
5207 curenv
->add_node(nd
);
5210 case TOKEN_NUMBERED_CHAR
:
5211 curenv
->add_char(get_charinfo_by_number(val
));
5214 // handled in process_input_stack
5216 case TOKEN_RIGHT_BRACE
:
5222 curenv
->add_char(get_charinfo(nm
));
5228 curenv
->handle_tab(0);
5230 case TOKEN_TRANSPARENT
:
5237 class nargs_reg
: public reg
{
5239 const char *get_string();
5242 const char *nargs_reg::get_string()
5244 return i_to_a(input_stack::nargs());
5247 class lineno_reg
: public reg
{
5249 const char *get_string();
5252 const char *lineno_reg::get_string()
5256 if (!input_stack::get_location(0, &file
, &line
))
5258 return i_to_a(line
);
5262 class writable_lineno_reg
: public general_reg
{
5264 writable_lineno_reg();
5265 void set_value(units
);
5266 int get_value(units
*);
5269 writable_lineno_reg::writable_lineno_reg()
5273 int writable_lineno_reg::get_value(units
*res
)
5277 if (!input_stack::get_location(0, &file
, &line
))
5283 void writable_lineno_reg::set_value(units n
)
5285 input_stack::set_location(0, n
);
5288 class filename_reg
: public reg
{
5290 const char *get_string();
5293 const char *filename_reg::get_string()
5297 if (input_stack::get_location(0, &file
, &line
))
5304 class constant_reg
: public reg
{
5307 constant_reg(const char *);
5308 const char *get_string();
5311 constant_reg::constant_reg(const char *p
) : s(p
)
5315 const char *constant_reg::get_string()
5320 constant_int_reg::constant_int_reg(int *q
) : p(q
)
5324 const char *constant_int_reg::get_string()
5329 void abort_request()
5334 else if (tok
.newline())
5337 while ((c
= get_copy(0)) == ' ')
5340 if (c
== EOF
|| c
== '\n')
5341 fputs("User Abort.", stderr
);
5343 for (; c
!= '\n' && c
!= EOF
; c
= get_copy(NULL
))
5344 fputs(asciify(c
), stderr
);
5346 fputc('\n', stderr
);
5347 cleanup_and_exit(1);
5353 char *s
= new char[len
];
5355 while ((c
= get_copy(0)) == ' ')
5358 while (c
!= '\n' && c
!= EOF
) {
5359 if (!illegal_input_char(c
)) {
5362 s
= new char[len
*2];
5363 memcpy(s
, tem
, len
);
5382 #ifdef POPEN_MISSING
5383 error("pipes not available on this system");
5385 #else /* not POPEN_MISSING */
5387 error("can't pipe: output already started");
5391 if ((pipe_command
= read_string()) == 0)
5392 error("can't pipe to empty command");
5394 #endif /* not POPEN_MISSING */
5397 static int system_status
;
5399 void system_request()
5401 char *command
= read_string();
5403 error("empty command");
5405 system_status
= system(command
);
5412 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
5413 handle_initial_request(COPY_FILE_REQUEST
);
5416 symbol filename
= get_long_name(1);
5417 while (!tok
.newline() && !tok
.eof())
5421 if (!filename
.is_null())
5422 curdiv
->copy_file(filename
.contents());
5430 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
5431 handle_initial_request(VJUSTIFY_REQUEST
);
5434 symbol type
= get_long_name(1);
5435 if (!type
.is_null())
5436 curdiv
->vjustify(type
);
5442 void transparent_file()
5444 if (curdiv
== topdiv
&& topdiv
->before_first_page
) {
5445 handle_initial_request(TRANSPARENT_FILE_REQUEST
);
5448 symbol filename
= get_long_name(1);
5449 while (!tok
.newline() && !tok
.eof())
5453 if (!filename
.is_null()) {
5455 FILE *fp
= fopen(filename
.contents(), "r");
5457 error("can't open `%1': %2", filename
.contents(), strerror(errno
));
5464 if (illegal_input_char(c
))
5465 warning(WARN_INPUT
, "illegal input character code %1", int(c
));
5467 curdiv
->transparent_output(c
);
5472 curdiv
->transparent_output('\n');
5484 page_range(int, int, page_range
*);
5485 int contains(int n
);
5488 page_range::page_range(int i
, int j
, page_range
*p
)
5489 : first(i
), last(j
), next(p
)
5493 int page_range::contains(int n
)
5495 return n
>= first
&& (last
<= 0 || n
<= last
);
5498 page_range
*output_page_list
= 0;
5500 int in_output_page_list(int n
)
5502 if (!output_page_list
)
5504 for (page_range
*p
= output_page_list
; p
; p
= p
->next
)
5510 static void parse_output_page_list(char *p
)
5516 else if (csdigit(*p
)) {
5519 i
= i
*10 + *p
++ - '0';
5520 while (csdigit(*p
));
5530 j
= j
*10 + *p
++ - '0';
5531 while (csdigit(*p
));
5537 last_page_number
= -1;
5538 else if (last_page_number
>= 0 && j
> last_page_number
)
5539 last_page_number
= j
;
5540 output_page_list
= new page_range(i
, j
, output_page_list
);
5546 error("bad output page list");
5547 output_page_list
= 0;
5551 static FILE *open_mac_file(const char *mac
, char **path
)
5553 char *s
= new char[strlen(mac
)+strlen(MACRO_PREFIX
)+1];
5554 strcpy(s
, MACRO_PREFIX
);
5556 FILE *fp
= macro_path
.open_file(s
, path
);
5557 // Try FOOBAR.tmac if tmac.FOOBAR failed. Some tmac.* file names
5558 // clash after truncation to 8+3 DOS limits, so this allows to
5559 // rename them to *.tmac instead.
5563 strncat(s
, MACRO_PREFIX
, strcspn(MACRO_PREFIX
, "."));
5564 fp
= macro_path
.open_file(s
, path
);
5570 static void process_macro_file(const char *mac
)
5573 FILE *fp
= open_mac_file(mac
, &path
);
5575 fatal("can't find macro file %1", mac
);
5576 const char *s
= symbol(path
).contents();
5578 input_stack::push(new file_iterator(fp
, s
));
5580 process_input_stack();
5583 static void process_startup_file(char *filename
)
5586 FILE *fp
= macro_path
.open_file(filename
, &path
);
5588 input_stack::push(new file_iterator(fp
, symbol(path
).contents()));
5591 process_input_stack();
5597 symbol nm
= get_long_name(1);
5601 while (!tok
.newline() && !tok
.eof())
5604 FILE *fp
= macro_path
.open_file(nm
.contents(), &path
);
5605 // .mso doesn't (and cannot) go through open_mac_file, so we
5606 // need to do it here manually...
5608 const char *fn
= nm
.contents();
5609 if (strncasecmp(fn
, MACRO_PREFIX
, sizeof(MACRO_PREFIX
) - 1) == 0) {
5610 char *s
= new char[strlen(fn
) + 1];
5611 strcpy(s
, fn
+ sizeof(MACRO_PREFIX
) - 1);
5613 strncat(s
, MACRO_PREFIX
, strcspn(MACRO_PREFIX
, "."));
5614 fp
= macro_path
.open_file(s
, &path
);
5619 input_stack::push(new file_iterator(fp
, symbol(path
).contents()));
5623 error("can't find macro file `%1'", nm
.contents());
5628 static void process_input_file(const char *name
)
5631 if (strcmp(name
, "-") == 0) {
5637 fp
= fopen(name
, "r");
5639 fatal("can't open `%1': %2", name
, strerror(errno
));
5641 input_stack::push(new file_iterator(fp
, name
));
5643 process_input_stack();
5646 // make sure the_input is empty before calling this
5648 static int evaluate_expression(const char *expr
, units
*res
)
5650 input_stack::push(make_temp_iterator(expr
));
5652 int success
= get_number(res
, 'u');
5653 while (input_stack::get(NULL
) != EOF
)
5658 static void do_register_assignment(const char *s
)
5660 const char *p
= strchr(s
, '=');
5666 if (evaluate_expression(s
+ 1, &n
))
5667 set_number_reg(buf
, n
);
5670 char *buf
= new char[p
- s
+ 1];
5671 memcpy(buf
, s
, p
- s
);
5674 if (evaluate_expression(p
+ 1, &n
))
5675 set_number_reg(buf
, n
);
5680 static void set_string(const char *name
, const char *value
)
5682 macro
*m
= new macro
;
5683 for (const char *p
= value
; *p
; p
++)
5684 if (!illegal_input_char((unsigned char)*p
))
5686 request_dictionary
.define(name
, m
);
5690 static void do_string_assignment(const char *s
)
5692 const char *p
= strchr(s
, '=');
5697 set_string(buf
, s
+ 1);
5700 char *buf
= new char[p
- s
+ 1];
5701 memcpy(buf
, s
, p
- s
);
5703 set_string(buf
, p
+ 1);
5708 struct string_list
{
5711 string_list(const char *ss
) : s(ss
), next(0) {}
5714 static void prepend_string(const char *s
, string_list
**p
)
5716 string_list
*l
= new string_list(s
);
5721 static void add_string(const char *s
, string_list
**p
)
5725 *p
= new string_list(s
);
5728 void usage(const char *prog
)
5731 "usage: %1 -abivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n"
5732 " -rcn -Tname -Fdir -Mdir [files...]\n",
5734 exit(USAGE_EXIT_CODE
);
5737 int main(int argc
, char **argv
)
5739 program_name
= argv
[0];
5740 static char stderr_buf
[BUFSIZ
];
5741 setbuf(stderr
, stderr_buf
);
5743 string_list
*macros
= 0;
5744 string_list
*register_assignments
= 0;
5745 string_list
*string_assignments
= 0;
5750 int safer_flag
= 1; // safer by default
5751 int no_rc
= 0; // don't process troffrc and troffrc-end
5752 int next_page_number
;
5754 hresolution
= vresolution
= 1;
5755 while ((c
= getopt(argc
, argv
, "abivw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU"))
5760 extern const char *Version_string
;
5761 fprintf(stderr
, "GNU troff version %s\n", Version_string
);
5768 is_html2
= (strcmp(device
, "html2") == 0);
5771 compatible_flag
= 1;
5774 macro_path
.command_line_dir(optarg
);
5777 font::command_line_font_dir(optarg
);
5780 add_string(optarg
, ¯os
);
5789 enable_warning(optarg
);
5792 disable_warning(optarg
);
5801 ascii_output_flag
= 1;
5804 suppress_output_flag
= 1;
5807 if (sscanf(optarg
, "%d", &next_page_number
) == 1)
5810 error("bad page number");
5813 parse_output_page_list(optarg
);
5816 if (*optarg
== '\0')
5817 error("`-d' requires non-empty argument");
5819 add_string(optarg
, &string_assignments
);
5822 if (*optarg
== '\0')
5823 error("`-r' requires non-empty argument");
5825 add_string(optarg
, ®ister_assignments
);
5828 default_family
= symbol(optarg
);
5834 // silently ignore these
5837 safer_flag
= 0; // unsafe behaviour
5841 break; // never reached
5845 set_string(".T", device
);
5846 init_charset_table();
5847 if (!font::load_desc())
5848 fatal("sorry, I can't continue");
5849 units_per_inch
= font::res
;
5850 hresolution
= font::hor
;
5851 vresolution
= font::vert
;
5852 sizescale
= font::sizescale
;
5853 tcommand_flag
= font::tcommand
;
5854 if (!fflag
&& font::family
!= 0 && *font::family
!= '\0')
5855 default_family
= symbol(font::family
);
5856 font_size::init_size_table(font::sizes
);
5859 if (font::style_table
) {
5860 for (i
= 0; font::style_table
[i
]; i
++)
5861 mount_style(j
++, symbol(font::style_table
[i
]));
5863 for (i
= 0; font::font_name_table
[i
]; i
++, j
++)
5864 // In the DESC file a font name of 0 (zero) means leave this
5866 if (strcmp(font::font_name_table
[i
], "0") != 0)
5867 mount_font(j
, symbol(font::font_name_table
[i
]));
5868 curdiv
= topdiv
= new top_level_diversion
;
5870 topdiv
->set_next_page_number(next_page_number
);
5871 init_input_requests();
5872 init_env_requests();
5873 init_div_requests();
5875 init_column_requests();
5877 init_node_requests();
5878 init_output_requests();
5879 number_reg_dictionary
.define(".T", new constant_reg(tflag
? "1" : "0"));
5881 init_reg_requests();
5882 init_hyphen_requests();
5883 init_environments();
5884 while (string_assignments
) {
5885 do_string_assignment(string_assignments
->s
);
5886 string_list
*tem
= string_assignments
;
5887 string_assignments
= string_assignments
->next
;
5890 while (register_assignments
) {
5891 do_register_assignment(register_assignments
->s
);
5892 string_list
*tem
= register_assignments
;
5893 register_assignments
= register_assignments
->next
;
5897 process_startup_file(INITIAL_STARTUP_FILE
);
5899 prepend_string("safer", ¯os
);
5901 process_macro_file(macros
->s
);
5902 string_list
*tem
= macros
;
5903 macros
= macros
->next
;
5907 process_startup_file(FINAL_STARTUP_FILE
);
5908 for (i
= optind
; i
< argc
; i
++)
5909 process_input_file(argv
[i
]);
5910 if (optind
>= argc
|| iflag
)
5911 process_input_file("-");
5913 return 0; // not reached
5919 if (has_arg() && get_integer(&n
)) {
5920 if (n
& ~WARN_TOTAL
) {
5921 warning(WARN_RANGE
, "warning mask must be between 0 and %1", WARN_TOTAL
);
5927 warning_mask
= WARN_TOTAL
;
5931 static void init_registers()
5933 #ifdef LONG_FOR_TIME_T
5935 #else /* not LONG_FOR_TIME_T */
5937 #endif /* not LONG_FOR_TIME_T */
5939 // Use struct here to work around misfeature in old versions of g++.
5940 struct tm
*tt
= localtime(&t
);
5941 set_number_reg("dw", int(tt
->tm_wday
+ 1));
5942 set_number_reg("dy", int(tt
->tm_mday
));
5943 set_number_reg("mo", int(tt
->tm_mon
+ 1));
5944 set_number_reg("year", int(1900 + tt
->tm_year
));
5945 set_number_reg("yr", int(tt
->tm_year
));
5946 set_number_reg("$$", getpid());
5947 number_reg_dictionary
.define(".A",
5948 new constant_reg(ascii_output_flag
5954 * .output request and associated registers
5957 static int output_reg_minx_contents
= -1;
5958 static int output_reg_miny_contents
= -1;
5959 static int output_reg_maxx_contents
= -1;
5960 static int output_reg_maxy_contents
= -1;
5962 void check_output_limits (int x
, int y
)
5964 if ((output_reg_minx_contents
== -1) || (x
< output_reg_minx_contents
)) {
5965 output_reg_minx_contents
= x
;
5967 if (x
> output_reg_maxx_contents
) {
5968 output_reg_maxx_contents
= x
;
5970 if ((output_reg_miny_contents
== -1) || (y
< output_reg_miny_contents
)) {
5971 output_reg_miny_contents
= y
;
5973 if (y
> output_reg_maxy_contents
) {
5974 output_reg_maxy_contents
= y
;
5976 // fprintf(stderr, "x = %d y=%d miny=%d maxy=%d\n", x, y, output_reg_miny_contents, output_reg_maxy_contents);
5979 void reset_output_registers()
5981 // fprintf(stderr, "reset_output_registers\n");
5982 output_reg_minx_contents
= -1;
5983 output_reg_miny_contents
= -1;
5984 output_reg_maxx_contents
= -1;
5985 output_reg_maxy_contents
= -1;
5988 void output_request()
5993 if (! get_integer(&n
)) {
5994 error("missing integer argument for output request");
6009 error("missing argument for output request");
6014 void init_output_requests()
6016 init_request("output", output_request
);
6019 void init_input_requests()
6021 init_request("ds", define_string
);
6022 init_request("as", append_string
);
6023 init_request("de", define_macro
);
6024 init_request("dei", define_indirect_macro
);
6025 init_request("am", append_macro
);
6026 init_request("ig", ignore
);
6027 init_request("rm", remove_macro
);
6028 init_request("rn", rename_macro
);
6029 init_request("nop", nop_request
);
6030 init_request("if", if_request
);
6031 init_request("ie", if_else_request
);
6032 init_request("el", else_request
);
6033 init_request("so", source
);
6034 init_request("nx", next_file
);
6035 init_request("pm", print_macros
);
6036 init_request("eo", escape_off
);
6037 init_request("ec", set_escape_char
);
6038 init_request("pc", set_page_character
);
6039 init_request("tm", terminal
);
6040 init_request("tm1", terminal1
);
6041 init_request("tmc", terminal_continue
);
6042 init_request("ex", exit_request
);
6043 init_request("em", end_macro
);
6044 init_request("blm", blank_line_macro
);
6045 init_request("tr", translate
);
6046 init_request("trnt", translate_no_transparent
);
6047 init_request("ab", abort_request
);
6048 init_request("pi", pipe_output
);
6049 init_request("cf", copy_file
);
6050 init_request("sy", system_request
);
6051 init_request("lf", line_file
);
6052 init_request("cflags", char_flags
);
6053 init_request("shift", shift
);
6054 init_request("rd", read_request
);
6055 init_request("cp", compatible
);
6056 init_request("char", define_character
);
6057 init_request("rchar", remove_character
);
6058 init_request("hcode", hyphenation_code
);
6059 init_request("while", while_request
);
6060 init_request("break", while_break_request
);
6061 init_request("continue", while_continue_request
);
6062 init_request("als", alias_macro
);
6063 init_request("backtrace", backtrace_request
);
6064 init_request("chop", chop_macro
);
6065 init_request("substring", substring_macro
);
6066 init_request("length", length_macro
);
6067 init_request("asciify", asciify_macro
);
6068 init_request("warn", warn_request
);
6069 init_request("open", open_request
);
6070 init_request("opena", opena_request
);
6071 init_request("close", close_request
);
6072 init_request("write", write_request
);
6073 init_request("writem", write_macro_request
);
6074 init_request("trf", transparent_file
);
6075 #ifdef WIDOW_CONTROL
6076 init_request("fpl", flush_pending_lines
);
6077 #endif /* WIDOW_CONTROL */
6078 init_request("nroff", nroff_request
);
6079 init_request("troff", troff_request
);
6081 init_request("vj", vjustify
);
6083 init_request("mso", macro_source
);
6084 init_request("do", do_request
);
6085 #ifndef POPEN_MISSING
6086 init_request("pso", pipe_source
);
6087 #endif /* not POPEN_MISSING */
6088 init_request("psbb", ps_bbox_request
);
6089 number_reg_dictionary
.define("systat", new variable_reg(&system_status
));
6090 number_reg_dictionary
.define("slimit",
6091 new variable_reg(&input_stack::limit
));
6092 number_reg_dictionary
.define(".$", new nargs_reg
);
6093 number_reg_dictionary
.define(".c", new lineno_reg
);
6094 number_reg_dictionary
.define("c.", new writable_lineno_reg
);
6095 number_reg_dictionary
.define(".F", new filename_reg
);
6096 number_reg_dictionary
.define(".C", new constant_int_reg(&compatible_flag
));
6097 number_reg_dictionary
.define(".H", new constant_int_reg(&hresolution
));
6098 number_reg_dictionary
.define(".V", new constant_int_reg(&vresolution
));
6099 number_reg_dictionary
.define(".R", new constant_reg("10000"));
6100 extern const char *major_version
;
6101 number_reg_dictionary
.define(".x", new constant_reg(major_version
));
6102 extern const char *minor_version
;
6103 number_reg_dictionary
.define(".y", new constant_reg(minor_version
));
6104 extern const char *revision
;
6105 number_reg_dictionary
.define(".Y", new constant_reg(revision
));
6106 number_reg_dictionary
.define(".g", new constant_reg("1"));
6107 number_reg_dictionary
.define(".warn", new constant_int_reg(&warning_mask
));
6108 number_reg_dictionary
.define("llx", new variable_reg(&llx_reg_contents
));
6109 number_reg_dictionary
.define("lly", new variable_reg(&lly_reg_contents
));
6110 number_reg_dictionary
.define("urx", new variable_reg(&urx_reg_contents
));
6111 number_reg_dictionary
.define("ury", new variable_reg(&ury_reg_contents
));
6112 number_reg_dictionary
.define("opminx", new variable_reg(&output_reg_minx_contents
));
6113 number_reg_dictionary
.define("opminy", new variable_reg(&output_reg_miny_contents
));
6114 number_reg_dictionary
.define("opmaxx", new variable_reg(&output_reg_maxx_contents
));
6115 number_reg_dictionary
.define("opmaxy", new variable_reg(&output_reg_maxy_contents
));
6118 object_dictionary
request_dictionary(501);
6120 void init_request(const char *s
, REQUEST_FUNCP f
)
6122 request_dictionary
.define(s
, new request(f
));
6125 static request_or_macro
*lookup_request(symbol nm
)
6127 assert(!nm
.is_null());
6128 request_or_macro
*p
= (request_or_macro
*)request_dictionary
.lookup(nm
);
6130 warning(WARN_MAC
, "`%1' not defined", nm
.contents());
6132 request_dictionary
.define(nm
, p
);
6138 node
*charinfo_to_node_list(charinfo
*ci
, const environment
*envp
)
6140 // Don't interpret character definitions in compatible mode.
6141 int old_compatible_flag
= compatible_flag
;
6142 compatible_flag
= 0;
6143 int old_escape_char
= escape_char
;
6145 macro
*mac
= ci
->set_macro(0);
6147 environment
*oldenv
= curenv
;
6148 environment
env(envp
);
6150 curenv
->set_composite();
6151 token old_tok
= tok
;
6152 input_stack::add_boundary();
6153 string_iterator
*si
= new string_iterator(*mac
, "composite character", ci
->nm
);
6154 input_stack::push(si
);
6155 // we don't use process_input_stack, because we don't want to recognise
6161 if (tok
.newline()) {
6162 error("composite character mustn't contain newline");
6170 node
*n
= curenv
->extract_output_line();
6171 input_stack::remove_boundary();
6175 compatible_flag
= old_compatible_flag
;
6176 escape_char
= old_escape_char
;
6180 static node
*read_draw_node()
6184 if (!start
.delimiter(1)){
6187 } while (tok
!= start
&& !tok
.newline() && !tok
.eof());
6192 error("missing argument");
6194 unsigned char type
= tok
.ch();
6197 hvpair
*point
= new hvpair
[maxpoints
];
6202 for (i
= 0; tok
!= start
; i
++) {
6203 if (i
== maxpoints
) {
6204 hvpair
*oldpoint
= point
;
6205 point
= new hvpair
[maxpoints
*2];
6206 for (int j
= 0; j
< maxpoints
; j
++)
6207 point
[j
] = oldpoint
[j
];
6211 if (!get_hunits(&point
[i
].h
,
6212 type
== 'f' || type
== 't' ? 'u' : 'm')) {
6223 if (!get_vunits(&point
[i
].v
, 'v')) {
6229 while (tok
!= start
&& !tok
.newline() && !tok
.eof())
6234 if (npoints
!= 1 || no_last_v
) {
6235 error("two arguments needed for line");
6240 if (npoints
!= 1 || !no_last_v
) {
6241 error("one argument needed for circle");
6247 if (npoints
!= 1 || no_last_v
) {
6248 error("two arguments needed for ellipse");
6253 if (npoints
!= 2 || no_last_v
) {
6254 error("four arguments needed for arc");
6260 error("even number of arguments needed for spline");
6263 // silently pass it through
6266 draw_node
*dn
= new draw_node(type
, point
, npoints
,
6267 curenv
->get_font_size());
6282 } warning_table
[] = {
6283 { "char", WARN_CHAR
},
6284 { "range", WARN_RANGE
},
6285 { "break", WARN_BREAK
},
6286 { "delim", WARN_DELIM
},
6288 { "scale", WARN_SCALE
},
6289 { "number", WARN_NUMBER
},
6290 { "syntax", WARN_SYNTAX
},
6291 { "tab", WARN_TAB
},
6292 { "right-brace", WARN_RIGHT_BRACE
},
6293 { "missing", WARN_MISSING
},
6294 { "input", WARN_INPUT
},
6295 { "escape", WARN_ESCAPE
},
6296 { "space", WARN_SPACE
},
6297 { "font", WARN_FONT
},
6299 { "mac", WARN_MAC
},
6300 { "reg", WARN_REG
},
6302 { "all", WARN_TOTAL
& ~(WARN_DI
| WARN_MAC
| WARN_REG
) },
6303 { "w", WARN_TOTAL
},
6304 { "default", DEFAULT_WARNING_MASK
},
6307 static int lookup_warning(const char *name
)
6310 i
< sizeof(warning_table
)/sizeof(warning_table
[0]);
6312 if (strcmp(name
, warning_table
[i
].name
) == 0)
6313 return warning_table
[i
].mask
;
6317 static void enable_warning(const char *name
)
6319 int mask
= lookup_warning(name
);
6321 warning_mask
|= mask
;
6323 error("unknown warning `%1'", name
);
6326 static void disable_warning(const char *name
)
6328 int mask
= lookup_warning(name
);
6330 warning_mask
&= ~mask
;
6332 error("unknown warning `%1'", name
);
6335 static void copy_mode_error(const char *format
,
6341 static const char prefix
[] = "(in ignored input) ";
6342 char *s
= new char[sizeof(prefix
) + strlen(format
)];
6345 warning(WARN_IG
, s
, arg1
, arg2
, arg3
);
6349 error(format
, arg1
, arg2
, arg3
);
6352 enum error_type
{ WARNING
, ERROR
, FATAL
};
6354 static void do_error(error_type type
,
6360 const char *filename
;
6362 if (inhibit_errors
&& type
< FATAL
)
6365 input_stack::backtrace();
6366 if (!get_file_line(&filename
, &lineno
))
6369 errprint("%1:%2: ", filename
, lineno
);
6370 else if (program_name
)
6371 fprintf(stderr
, "%s: ", program_name
);
6374 fputs("fatal error: ", stderr
);
6379 fputs("warning: ", stderr
);
6382 errprint(format
, arg1
, arg2
, arg3
);
6383 fputc('\n', stderr
);
6386 cleanup_and_exit(1);
6389 int warning(warning_type t
,
6395 if ((t
& warning_mask
) != 0) {
6396 do_error(WARNING
, format
, arg1
, arg2
, arg3
);
6403 void error(const char *format
,
6408 do_error(ERROR
, format
, arg1
, arg2
, arg3
);
6411 void fatal(const char *format
,
6416 do_error(FATAL
, format
, arg1
, arg2
, arg3
);
6419 void fatal_with_file_and_line(const char *filename
, int lineno
,
6425 fprintf(stderr
, "%s:%d: fatal error: ", filename
, lineno
);
6426 errprint(format
, arg1
, arg2
, arg3
);
6427 fputc('\n', stderr
);
6429 cleanup_and_exit(1);
6432 void error_with_file_and_line(const char *filename
, int lineno
,
6438 fprintf(stderr
, "%s:%d: error: ", filename
, lineno
);
6439 errprint(format
, arg1
, arg2
, arg3
);
6440 fputc('\n', stderr
);
6444 dictionary
charinfo_dictionary(501);
6446 charinfo
*get_charinfo(symbol nm
)
6448 void *p
= charinfo_dictionary
.lookup(nm
);
6450 return (charinfo
*)p
;
6451 charinfo
*cp
= new charinfo(nm
);
6452 (void)charinfo_dictionary
.lookup(nm
, cp
);
6456 int charinfo::next_index
= 0;
6458 charinfo::charinfo(symbol s
)
6459 : translation(0), mac(0), special_translation(TRANSLATE_NONE
),
6460 hyphenation_code(0), flags(0), ascii_code(0), not_found(0),
6461 transparent_translate(1), nm(s
)
6463 index
= next_index
++;
6466 void charinfo::set_hyphenation_code(unsigned char c
)
6468 hyphenation_code
= c
;
6471 void charinfo::set_translation(charinfo
*ci
, int tt
)
6474 special_translation
= TRANSLATE_NONE
;
6475 transparent_translate
= tt
;
6478 void charinfo::set_special_translation(int c
, int tt
)
6480 special_translation
= c
;
6482 transparent_translate
= tt
;
6485 void charinfo::set_ascii_code(unsigned char c
)
6490 macro
*charinfo::set_macro(macro
*m
)
6497 void charinfo::set_number(int n
)
6503 int charinfo::get_number()
6505 assert(flags
& NUMBERED
);
6509 symbol
UNNAMED_SYMBOL("---");
6511 // For numbered characters not between 0 and 255, we make a symbol out
6512 // of the number and store them in this dictionary.
6514 dictionary
numbered_charinfo_dictionary(11);
6516 charinfo
*get_charinfo_by_number(int n
)
6518 static charinfo
*number_table
[256];
6520 if (n
>= 0 && n
< 256) {
6521 charinfo
*ci
= number_table
[n
];
6523 ci
= new charinfo(UNNAMED_SYMBOL
);
6525 number_table
[n
] = ci
;
6530 symbol
ns(i_to_a(n
));
6531 charinfo
*ci
= (charinfo
*)numbered_charinfo_dictionary
.lookup(ns
);
6533 ci
= new charinfo(UNNAMED_SYMBOL
);
6535 numbered_charinfo_dictionary
.lookup(ns
, ci
);
6541 int font::name_to_index(const char *nm
)
6545 ci
= charset_table
[nm
[0] & 0xff];
6546 else if (nm
[0] == '\\' && nm
[2] == 0)
6547 ci
= get_charinfo(symbol(nm
+ 1));
6549 ci
= get_charinfo(symbol(nm
));
6553 return ci
->get_index();
6556 int font::number_to_index(int n
)
6558 return get_charinfo_by_number(n
)->get_index();