2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
24 #include "dictionary.h"
33 #include "stringclass.h"
35 #include "macropath.h"
39 // Needed for getpid() and isatty()
44 #ifdef NEED_DECLARATION_PUTENV
46 int putenv(const char *);
48 #endif /* NEED_DECLARATION_PUTENV */
50 #define MACRO_PREFIX "tmac."
51 #define MACRO_POSTFIX ".tmac"
52 #define INITIAL_STARTUP_FILE "troffrc"
53 #define FINAL_STARTUP_FILE "troffrc-end"
54 #define DEFAULT_INPUT_STACK_LIMIT 1000
56 #ifndef DEFAULT_WARNING_MASK
57 // warnings that are enabled by default
58 #define DEFAULT_WARNING_MASK \
59 (WARN_CHAR|WARN_NUMBER|WARN_BREAK|WARN_SPACE|WARN_FONT)
62 // initial size of buffer for reading names; expanded as necessary
65 extern "C" const char *Version_string;
68 void init_column_requests();
71 static node *read_draw_node();
72 void handle_first_page_transition();
73 static void push_token(const token &);
78 void transparent_file();
79 void process_input_stack();
81 const char *program_name = 0;
84 int color_flag = 1; // colors are on by default
85 static int backtrace_flag = 0;
87 char *pipe_command = 0;
89 charinfo *charset_table[256];
90 unsigned char hpf_code_table[256];
92 static int warning_mask = DEFAULT_WARNING_MASK;
93 static int inhibit_errors = 0;
94 static int ignoring = 0;
96 static void enable_warning(const char *);
97 static void disable_warning(const char *);
99 static int escape_char = '\\';
100 static symbol end_macro_name;
101 static symbol blank_line_macro_name;
102 static int compatible_flag = 0;
103 int ascii_output_flag = 0;
104 int suppress_output_flag = 0;
106 int begin_level = 0; // number of nested .begin requests
108 int have_input = 0; // whether \f, \H, \R, \s, or \S has
109 // been processed in token::next()
110 int tcommand_flag = 0;
111 int safer_flag = 1; // safer by default
113 int have_string_arg = 0; // whether we have \*[foo bar...]
115 double spread_limit = -3.0 - 1.0; // negative means deactivated
118 char warn_scaling_indicator;
120 search_path *mac_path = &safer_macro_path;
122 static int get_copy(node**, int = 0);
123 static void copy_mode_error(const char *,
124 const errarg & = empty_errarg,
125 const errarg & = empty_errarg,
126 const errarg & = empty_errarg);
128 enum read_mode { ALLOW_EMPTY, WITH_ARGS, NO_ARGS };
129 static symbol read_escape_name(read_mode mode = NO_ARGS);
130 static symbol read_long_escape_name(read_mode mode = NO_ARGS);
131 static void interpolate_string(symbol);
132 static void interpolate_string_with_args(symbol);
133 static void interpolate_macro(symbol);
134 static void interpolate_number_format(symbol);
135 static void interpolate_environment_variable(symbol);
137 static void interpolate_arg(symbol);
138 static request_or_macro *lookup_request(symbol);
139 static int get_delim_number(units *, int);
140 static int get_delim_number(units *, int, units);
141 static int get_line_arg(units *res, int si, charinfo **cp);
142 static int read_size(int *);
143 static symbol get_delim_name();
144 static void init_registers();
145 static void trapping_blank_line();
147 struct input_iterator;
148 input_iterator *make_temp_iterator(const char *);
149 const char *input_char_description(int);
152 void set_escape_char()
156 error("bad escape character");
160 escape_char = tok.ch();
173 static int saved_escape_char = '\\';
175 void save_escape_char()
177 saved_escape_char = escape_char;
181 void restore_escape_char()
183 escape_char = saved_escape_char;
187 class input_iterator {
190 virtual ~input_iterator() {}
192 friend class input_stack;
194 const unsigned char *ptr;
195 const unsigned char *eptr;
196 input_iterator *next;
198 virtual int fill(node **);
200 virtual int has_args() { return 0; }
201 virtual int nargs() { return 0; }
202 virtual input_iterator *get_arg(int) { return 0; }
203 virtual int get_location(int, const char **, int *) { return 0; }
204 virtual void backtrace() {}
205 virtual int set_location(const char *, int) { return 0; }
206 virtual int next_file(FILE *, const char *) { return 0; }
207 virtual void shift(int) {}
208 virtual int is_boundary() {return 0; }
209 virtual int internal_level() { return 0; }
210 virtual int is_file() { return 0; }
211 virtual int is_macro() { return 0; }
212 virtual void save_compatible_flag(int) {}
213 virtual int get_compatible_flag() { return 0; }
216 input_iterator::input_iterator()
221 int input_iterator::fill(node **)
226 int input_iterator::peek()
231 inline int input_iterator::get(node **p)
233 return ptr < eptr ? *ptr++ : fill(p);
236 class input_boundary : public input_iterator {
238 int is_boundary() { return 1; }
241 class input_return_boundary : public input_iterator {
243 int is_boundary() { return 2; }
246 class file_iterator : public input_iterator {
249 const char *filename;
253 enum { BUF_SIZE = 512 };
254 unsigned char buf[BUF_SIZE];
257 file_iterator(FILE *, const char *, int = 0);
261 int get_location(int, const char **, int *);
263 int set_location(const char *, int);
264 int next_file(FILE *, const char *);
268 file_iterator::file_iterator(FILE *f, const char *fn, int po)
269 : fp(f), lineno(1), filename(fn), popened(po),
270 newline_flag(0), seen_escape(0)
272 if ((font::use_charnames_in_special) && (fn != 0)) {
275 the_output->put_filename(fn);
279 file_iterator::~file_iterator()
284 void file_iterator::close()
288 #ifndef POPEN_MISSING
291 #endif /* not POPEN_MISSING */
296 int file_iterator::is_file()
301 int file_iterator::next_file(FILE *f, const char *s)
315 int file_iterator::fill(node **)
320 unsigned char *p = buf;
322 unsigned char *e = p + BUF_SIZE;
327 if (invalid_input_char(c))
328 warning(WARN_INPUT, "invalid input character code %1", int(c));
336 seen_escape = (c == '\\');
349 int file_iterator::peek()
352 while (invalid_input_char(c)) {
353 warning(WARN_INPUT, "invalid input character code %1", int(c));
361 int file_iterator::get_location(int /*allow_macro*/,
362 const char **filenamep, int *linenop)
365 if (filename != 0 && strcmp(filename, "-") == 0)
366 *filenamep = "<standard input>";
368 *filenamep = filename;
372 void file_iterator::backtrace()
374 errprint("%1:%2: backtrace: %3 `%1'\n", filename, lineno,
375 popened ? "process" : "file");
378 int file_iterator::set_location(const char *f, int ln)
384 the_output->put_filename(f);
390 input_iterator nil_iterator;
394 static int get(node **);
396 static void push(input_iterator *);
397 static input_iterator *get_arg(int);
399 static int get_location(int, const char **, int *);
400 static int set_location(const char *, int);
401 static void backtrace();
402 static void backtrace_all();
403 static void next_file(FILE *, const char *);
404 static void end_file();
405 static void shift(int n);
406 static void add_boundary();
407 static void add_return_boundary();
408 static int is_return_boundary();
409 static void remove_boundary();
410 static int get_level();
412 static void pop_macro();
413 static void save_compatible_flag(int);
414 static int get_compatible_flag();
418 static input_iterator *top;
421 static int finish_get(node **);
422 static int finish_peek();
425 input_iterator *input_stack::top = &nil_iterator;
426 int input_stack::level = 0;
427 int input_stack::limit = DEFAULT_INPUT_STACK_LIMIT;
429 inline int input_stack::get_level()
431 return level + top->internal_level();
434 inline int input_stack::get(node **np)
436 return (top->ptr < top->eptr) ? *top->ptr++ : finish_get(np);
439 int input_stack::finish_get(node **np)
442 int c = top->fill(np);
443 if (c != EOF || top->is_boundary())
445 if (top == &nil_iterator)
447 input_iterator *tem = top;
451 if (top->ptr < top->eptr)
458 inline int input_stack::peek()
460 return (top->ptr < top->eptr) ? *top->ptr : finish_peek();
463 int input_stack::finish_peek()
467 if (c != EOF || top->is_boundary())
469 if (top == &nil_iterator)
471 input_iterator *tem = top;
475 if (top->ptr < top->eptr)
482 void input_stack::add_boundary()
484 push(new input_boundary);
487 void input_stack::add_return_boundary()
489 push(new input_return_boundary);
492 int input_stack::is_return_boundary()
494 return top->is_boundary() == 2;
497 void input_stack::remove_boundary()
499 assert(top->is_boundary());
500 input_iterator *temp = top->next;
506 void input_stack::push(input_iterator *in)
510 if (++level > limit && limit > 0)
511 fatal("input stack limit exceeded (probable infinite loop)");
516 input_iterator *input_stack::get_arg(int i)
519 for (p = top; p != 0; p = p->next)
521 return p->get_arg(i);
525 void input_stack::shift(int n)
527 for (input_iterator *p = top; p; p = p->next)
534 int input_stack::nargs()
536 for (input_iterator *p =top; p != 0; p = p->next)
542 int input_stack::get_location(int allow_macro, const char **filenamep, int *linenop)
544 for (input_iterator *p = top; p; p = p->next)
545 if (p->get_location(allow_macro, filenamep, linenop))
550 void input_stack::backtrace()
554 // only backtrace down to (not including) the topmost file
555 for (input_iterator *p = top;
556 p && !p->get_location(0, &f, &n);
561 void input_stack::backtrace_all()
563 for (input_iterator *p = top; p; p = p->next)
567 int input_stack::set_location(const char *filename, int lineno)
569 for (input_iterator *p = top; p; p = p->next)
570 if (p->set_location(filename, lineno))
575 void input_stack::next_file(FILE *fp, const char *s)
578 for (pp = ⊤ *pp != &nil_iterator; pp = &(*pp)->next)
579 if ((*pp)->next_file(fp, s))
581 if (++level > limit && limit > 0)
582 fatal("input stack limit exceeded");
583 *pp = new file_iterator(fp, s);
584 (*pp)->next = &nil_iterator;
587 void input_stack::end_file()
589 for (input_iterator **pp = ⊤ *pp != &nil_iterator; pp = &(*pp)->next)
590 if ((*pp)->is_file()) {
591 input_iterator *tem = *pp;
599 void input_stack::clear()
602 while (top != &nil_iterator) {
603 if (top->is_boundary())
605 input_iterator *tem = top;
610 // Keep while_request happy.
611 for (; nboundaries > 0; --nboundaries)
612 add_return_boundary();
615 void input_stack::pop_macro()
620 if (top->next == &nil_iterator)
622 if (top->is_boundary())
624 is_macro = top->is_macro();
625 input_iterator *tem = top;
630 // Keep while_request happy.
631 for (; nboundaries > 0; --nboundaries)
632 add_return_boundary();
635 inline void input_stack::save_compatible_flag(int f)
637 top->save_compatible_flag(f);
640 inline int input_stack::get_compatible_flag()
642 return top->get_compatible_flag();
645 void backtrace_request()
647 input_stack::backtrace_all();
654 symbol nm = get_long_name(0);
655 while (!tok.newline() && !tok.eof())
658 input_stack::end_file();
661 FILE *fp = fopen(nm.contents(), "r");
663 error("can't open `%1': %2", nm.contents(), strerror(errno));
665 input_stack::next_file(fp, nm.contents());
673 if (!has_arg() || !get_integer(&n))
675 input_stack::shift(n);
679 static int get_char_for_escape_name(int allow_space = 0)
684 copy_mode_error("end of input in escape name");
687 if (!invalid_input_char(c))
692 input_stack::push(make_temp_iterator("\n"));
695 if (c == ' ' && allow_space)
701 copy_mode_error("%1 is not allowed in an escape name",
702 input_char_description(c));
708 static symbol read_two_char_escape_name()
711 buf[0] = get_char_for_escape_name();
712 if (buf[0] != '\0') {
713 buf[1] = get_char_for_escape_name();
722 static symbol read_long_escape_name(read_mode mode)
724 int start_level = input_stack::get_level();
725 char abuf[ABUF_SIZE];
727 int buf_size = ABUF_SIZE;
732 c = get_char_for_escape_name(have_char && mode == WITH_ARGS);
739 if (mode == WITH_ARGS && c == ' ')
741 if (i + 2 > buf_size) {
743 buf = new char[ABUF_SIZE*2];
744 memcpy(buf, abuf, buf_size);
745 buf_size = ABUF_SIZE*2;
749 buf = new char[buf_size*2];
750 memcpy(buf, old_buf, buf_size);
755 if (c == ']' && input_stack::get_level() == start_level)
764 if (mode != ALLOW_EMPTY)
765 copy_mode_error("empty escape name");
777 static symbol read_escape_name(read_mode mode)
779 int c = get_char_for_escape_name();
783 return read_two_char_escape_name();
784 if (c == '[' && !compatible_flag)
785 return read_long_escape_name(mode);
792 static symbol read_increment_and_escape_name(int *incp)
794 int c = get_char_for_escape_name();
801 return read_two_char_escape_name();
804 return read_escape_name();
807 return read_escape_name();
809 if (!compatible_flag) {
811 return read_long_escape_name();
822 static int get_copy(node **nd, int defining)
825 int c = input_stack::get(nd);
826 if (c == ESCAPE_NEWLINE) {
830 c = input_stack::get(nd);
831 } while (c == ESCAPE_NEWLINE);
833 if (c != escape_char || escape_char <= 0)
835 c = input_stack::peek();
840 (void)input_stack::get(0);
841 while ((c = input_stack::get(0)) != '\n' && c != EOF)
844 case '#': // Like \" but newline is ignored.
845 (void)input_stack::get(0);
846 while ((c = input_stack::get(0)) != '\n')
852 (void)input_stack::get(0);
853 symbol s = read_escape_name();
854 if (!(s.is_null() || s.is_empty()))
860 (void)input_stack::get(0);
861 symbol s = read_escape_name(WITH_ARGS);
862 if (!(s.is_null() || s.is_empty())) {
863 if (have_string_arg) {
865 interpolate_string_with_args(s);
868 interpolate_string(s);
873 (void)input_stack::get(0);
876 (void)input_stack::get(0);
879 (void)input_stack::get(0);
883 (void)input_stack::get(0);
885 symbol s = read_increment_and_escape_name(&inc);
886 if (!(s.is_null() || s.is_empty()))
887 interpolate_number_reg(s, inc);
892 (void)input_stack::get(0);
893 symbol s = read_escape_name();
894 if (!(s.is_null() || s.is_empty()))
895 interpolate_number_format(s);
899 (void)input_stack::get(0);
903 (void)input_stack::get(0);
904 symbol s = read_escape_name();
905 if (!(s.is_null() || s.is_empty()))
906 interpolate_environment_variable(s);
910 (void)input_stack::get(0);
912 return ESCAPE_NEWLINE;
915 (void)input_stack::get(0);
918 (void)input_stack::get(0);
921 (void)input_stack::get(0);
924 (void)input_stack::get(0);
927 (void)input_stack::get(0);
928 return ESCAPE_CIRCUMFLEX;
930 (void)input_stack::get(0);
931 return ESCAPE_LEFT_BRACE;
933 (void)input_stack::get(0);
934 return ESCAPE_RIGHT_BRACE;
936 (void)input_stack::get(0);
937 return ESCAPE_LEFT_QUOTE;
939 (void)input_stack::get(0);
940 return ESCAPE_RIGHT_QUOTE;
942 (void)input_stack::get(0);
943 return ESCAPE_HYPHEN;
945 (void)input_stack::get(0);
946 return ESCAPE_UNDERSCORE;
948 (void)input_stack::get(0);
951 (void)input_stack::get(0);
954 (void)input_stack::get(0);
955 return ESCAPE_QUESTION;
957 (void)input_stack::get(0);
958 return ESCAPE_AMPERSAND;
960 (void)input_stack::get(0);
961 return ESCAPE_RIGHT_PARENTHESIS;
963 (void)input_stack::get(0);
966 (void)input_stack::get(0);
967 return ESCAPE_PERCENT;
969 if (c == escape_char) {
970 (void)input_stack::get(0);
979 class non_interpreted_char_node : public node {
982 non_interpreted_char_node(unsigned char);
984 int interpret(macro *);
990 int non_interpreted_char_node::same(node *nd)
992 return c == ((non_interpreted_char_node *)nd)->c;
995 const char *non_interpreted_char_node::type()
997 return "non_interpreted_char_node";
1000 int non_interpreted_char_node::force_tprint()
1005 non_interpreted_char_node::non_interpreted_char_node(unsigned char n) : c(n)
1010 node *non_interpreted_char_node::copy()
1012 return new non_interpreted_char_node(c);
1015 int non_interpreted_char_node::interpret(macro *mac)
1021 static void do_width();
1022 static node *do_non_interpreted();
1023 static node *do_special();
1024 static node *do_suppress(symbol nm);
1025 static void do_register();
1027 dictionary color_dictionary(501);
1028 static symbol default_symbol("default");
1030 static color *lookup_color(symbol nm)
1032 assert(!nm.is_null());
1033 if (nm == default_symbol)
1034 return &default_color;
1035 color *c = (color *)color_dictionary.lookup(nm);
1037 warning(WARN_COLOR, "`%1' not defined", nm.contents());
1041 void do_glyph_color(symbol nm)
1046 curenv->set_glyph_color(curenv->get_prev_glyph_color());
1048 color *tem = lookup_color(nm);
1050 curenv->set_glyph_color(tem);
1052 (void)color_dictionary.lookup(nm, new color);
1056 void do_fill_color(symbol nm)
1061 curenv->set_fill_color(curenv->get_prev_fill_color());
1063 color *tem = lookup_color(nm);
1065 curenv->set_fill_color(tem);
1067 (void)color_dictionary.lookup(nm, new color);
1071 static unsigned int get_color_element(const char *scheme, const char *col)
1074 if (!get_number(&val, 'f')) {
1075 warning(WARN_COLOR, "%1 in %2 definition set to 0", col, scheme);
1080 warning(WARN_RANGE, "%1 cannot be negative: set to 0", col);
1083 if (val > color::MAX_COLOR_VAL+1) {
1084 warning(WARN_RANGE, "%1 cannot be greater than 1", col);
1085 // we change 0x10000 to 0xffff
1086 return color::MAX_COLOR_VAL;
1088 return (unsigned int)val;
1091 static color *read_rgb()
1093 symbol component = get_long_name(0);
1094 if (component.is_null()) {
1095 warning(WARN_COLOR, "missing rgb color values");
1098 const char *s = component.contents();
1099 color *col = new color;
1101 if (!col->read_rgb(s)) {
1102 warning(WARN_COLOR, "expecting rgb color definition not `%1'", s);
1108 input_stack::push(make_temp_iterator(" "));
1109 input_stack::push(make_temp_iterator(s));
1111 unsigned int r = get_color_element("rgb color", "red component");
1112 unsigned int g = get_color_element("rgb color", "green component");
1113 unsigned int b = get_color_element("rgb color", "blue component");
1114 col->set_rgb(r, g, b);
1119 static color *read_cmy()
1121 symbol component = get_long_name(0);
1122 if (component.is_null()) {
1123 warning(WARN_COLOR, "missing cmy color values");
1126 const char *s = component.contents();
1127 color *col = new color;
1129 if (!col->read_cmy(s)) {
1130 warning(WARN_COLOR, "expecting cmy color definition not `%1'", s);
1136 input_stack::push(make_temp_iterator(" "));
1137 input_stack::push(make_temp_iterator(s));
1139 unsigned int c = get_color_element("cmy color", "cyan component");
1140 unsigned int m = get_color_element("cmy color", "magenta component");
1141 unsigned int y = get_color_element("cmy color", "yellow component");
1142 col->set_cmy(c, m, y);
1147 static color *read_cmyk()
1149 symbol component = get_long_name(0);
1150 if (component.is_null()) {
1151 warning(WARN_COLOR, "missing cmyk color values");
1154 const char *s = component.contents();
1155 color *col = new color;
1157 if (!col->read_cmyk(s)) {
1158 warning(WARN_COLOR, "`expecting a cmyk color definition not `%1'", s);
1164 input_stack::push(make_temp_iterator(" "));
1165 input_stack::push(make_temp_iterator(s));
1167 unsigned int c = get_color_element("cmyk color", "cyan component");
1168 unsigned int m = get_color_element("cmyk color", "magenta component");
1169 unsigned int y = get_color_element("cmyk color", "yellow component");
1170 unsigned int k = get_color_element("cmyk color", "black component");
1171 col->set_cmyk(c, m, y, k);
1176 static color *read_gray()
1178 symbol component = get_long_name(0);
1179 if (component.is_null()) {
1180 warning(WARN_COLOR, "missing gray values");
1183 const char *s = component.contents();
1184 color *col = new color;
1186 if (!col->read_gray(s)) {
1187 warning(WARN_COLOR, "`expecting a gray definition not `%1'", s);
1193 input_stack::push(make_temp_iterator("\n"));
1194 input_stack::push(make_temp_iterator(s));
1196 unsigned int g = get_color_element("gray", "gray value");
1202 static void activate_color()
1205 if (has_arg() && get_integer(&n))
1206 color_flag = n != 0;
1212 static void define_color()
1214 symbol color_name = get_long_name(1);
1215 if (color_name.is_null()) {
1219 if (color_name == default_symbol) {
1220 warning(WARN_COLOR, "default color can't be redefined");
1224 symbol style = get_long_name(1);
1225 if (style.is_null()) {
1230 if (strcmp(style.contents(), "rgb") == 0)
1232 else if (strcmp(style.contents(), "cmyk") == 0)
1234 else if (strcmp(style.contents(), "gray") == 0)
1236 else if (strcmp(style.contents(), "grey") == 0)
1238 else if (strcmp(style.contents(), "cmy") == 0)
1242 "unknown color space `%1'; use rgb, cmyk, gray or cmy",
1248 (void)color_dictionary.lookup(color_name, col);
1252 static node *do_overstrike()
1255 overstrike_node *on = new overstrike_node;
1256 int start_level = input_stack::get_level();
1260 if (tok.newline() || tok.eof()) {
1261 warning(WARN_DELIM, "missing closing delimiter");
1265 && (compatible_flag || input_stack::get_level() == start_level))
1267 charinfo *ci = tok.get_char(1);
1269 node *n = curenv->make_char_node(ci);
1277 static node *do_bracket()
1280 bracket_node *bn = new bracket_node;
1282 int start_level = input_stack::get_level();
1286 warning(WARN_DELIM, "missing closing delimiter");
1289 if (tok.newline()) {
1290 warning(WARN_DELIM, "missing closing delimiter");
1291 input_stack::push(make_temp_iterator("\n"));
1295 && (compatible_flag || input_stack::get_level() == start_level))
1297 charinfo *ci = tok.get_char(1);
1299 node *n = curenv->make_char_node(ci);
1307 static int do_name_test()
1311 int start_level = input_stack::get_level();
1316 if (tok.newline() || tok.eof()) {
1317 warning(WARN_DELIM, "missing closing delimiter");
1321 && (compatible_flag || input_stack::get_level() == start_level))
1327 return some_char && !bad_char;
1330 static int do_expr_test()
1334 int start_level = input_stack::get_level();
1335 if (!start.delimiter(1))
1338 // disable all warning and error messages temporarily
1339 int saved_warning_mask = warning_mask;
1340 int saved_inhibit_errors = inhibit_errors;
1344 int result = get_number_rigidly(&dummy, 'u');
1345 warning_mask = saved_warning_mask;
1346 inhibit_errors = saved_inhibit_errors;
1347 if (tok == start && input_stack::get_level() == start_level)
1349 // ignore everything up to the delimiter in case we aren't right there
1352 if (tok.newline() || tok.eof()) {
1353 warning(WARN_DELIM, "missing closing delimiter");
1356 if (tok == start && input_stack::get_level() == start_level)
1363 static node *do_zero_width()
1367 int start_level = input_stack::get_level();
1368 environment env(curenv);
1369 environment *oldenv = curenv;
1373 if (tok.newline() || tok.eof()) {
1374 error("missing closing delimiter");
1378 && (compatible_flag || input_stack::get_level() == start_level))
1383 node *rev = env.extract_output_line();
1391 return new zero_width_node(n);
1396 // It's undesirable for \Z to change environments, because then
1397 // \n(.w won't work as expected.
1399 static node *do_zero_width()
1401 node *rev = new dummy_node;
1404 int start_level = input_stack::get_level();
1407 if (tok.newline() || tok.eof()) {
1408 warning(WARN_DELIM, "missing closing delimiter");
1412 && (compatible_flag || input_stack::get_level() == start_level))
1414 if (!tok.add_to_node_list(&rev))
1415 error("invalid token in argument to \\Z");
1424 return new zero_width_node(n);
1429 token_node *node::get_token_node()
1434 class token_node : public node {
1437 token_node(const token &t);
1439 token_node *get_token_node();
1445 token_node::token_node(const token &t) : tk(t)
1449 node *token_node::copy()
1451 return new token_node(tk);
1454 token_node *token_node::get_token_node()
1459 int token_node::same(node *nd)
1461 return tk == ((token_node *)nd)->tk;
1464 const char *token_node::type()
1466 return "token_node";
1469 int token_node::force_tprint()
1474 token::token() : nd(0), type(TOKEN_EMPTY)
1483 token::token(const token &t)
1484 : nm(t.nm), c(t.c), val(t.val), dim(t.dim), type(t.type)
1486 // Use two statements to work around bug in SGI C++.
1488 nd = tem ? tem->copy() : 0;
1491 void token::operator=(const token &t)
1495 // Use two statements to work around bug in SGI C++.
1497 nd = tem ? tem->copy() : 0;
1514 return !tok.newline();
1517 void token::make_space()
1522 void token::make_newline()
1524 type = TOKEN_NEWLINE;
1536 int cc = input_stack::get(&n);
1537 if (cc != escape_char || escape_char == 0) {
1540 case COMPATIBLE_SAVE:
1541 input_stack::save_compatible_flag(compatible_flag);
1542 compatible_flag = 0;
1544 case COMPATIBLE_RESTORE:
1545 compatible_flag = input_stack::get_compatible_flag();
1550 case TRANSPARENT_FILE_REQUEST:
1552 case COPY_FILE_REQUEST:
1554 case VJUSTIFY_REQUEST:
1556 type = TOKEN_REQUEST;
1560 type = TOKEN_BEGIN_TRAP;
1563 type = TOKEN_END_TRAP;
1565 case LAST_PAGE_EJECTOR:
1566 seen_last_page_ejector = 1;
1569 type = TOKEN_PAGE_EJECTOR;
1571 case ESCAPE_PERCENT:
1573 type = TOKEN_HYPHEN_INDICATOR;
1577 type = TOKEN_UNSTRETCHABLE_SPACE;
1581 type = TOKEN_STRETCHABLE_SPACE;
1585 type = TOKEN_ZERO_WIDTH_BREAK;
1589 type = TOKEN_ESCAPE;
1592 goto handle_escape_char;
1596 nd = new hmotion_node(curenv->get_narrow_space_width(),
1597 curenv->get_fill_color());
1599 case ESCAPE_CIRCUMFLEX:
1602 nd = new hmotion_node(curenv->get_half_narrow_space_width(),
1603 curenv->get_fill_color());
1605 case ESCAPE_NEWLINE:
1607 case ESCAPE_LEFT_BRACE:
1609 type = TOKEN_LEFT_BRACE;
1611 case ESCAPE_RIGHT_BRACE:
1613 type = TOKEN_RIGHT_BRACE;
1615 case ESCAPE_LEFT_QUOTE:
1617 type = TOKEN_SPECIAL;
1620 case ESCAPE_RIGHT_QUOTE:
1622 type = TOKEN_SPECIAL;
1627 type = TOKEN_SPECIAL;
1630 case ESCAPE_UNDERSCORE:
1632 type = TOKEN_SPECIAL;
1637 type = TOKEN_INTERRUPT;
1641 type = TOKEN_TRANSPARENT;
1643 case ESCAPE_QUESTION:
1645 nd = do_non_interpreted();
1651 case ESCAPE_AMPERSAND:
1655 case ESCAPE_RIGHT_PARENTHESIS:
1656 ESCAPE_RIGHT_PARENTHESIS:
1657 type = TOKEN_TRANSPARENT_DUMMY;
1660 type = TOKEN_BACKSPACE;
1669 type = TOKEN_NEWLINE;
1672 type = TOKEN_LEADER;
1677 token_node *tn = n->get_token_node();
1696 cc = input_stack::get(0);
1699 nm = read_two_char_escape_name();
1700 type = TOKEN_SPECIAL;
1704 error("end of input after escape character");
1707 goto ESCAPE_LEFT_QUOTE;
1709 goto ESCAPE_RIGHT_QUOTE;
1713 goto ESCAPE_UNDERSCORE;
1715 goto ESCAPE_PERCENT;
1719 nd = new hmotion_node(curenv->get_digit_width(),
1720 curenv->get_fill_color());
1726 goto ESCAPE_CIRCUMFLEX;
1728 type = TOKEN_ITALIC_CORRECTION;
1732 nd = new left_italic_corrected_node;
1735 goto ESCAPE_AMPERSAND;
1737 goto ESCAPE_RIGHT_PARENTHESIS;
1741 goto ESCAPE_QUESTION;
1747 while ((cc = input_stack::get(0)) != '\n' && cc != EOF)
1750 type = TOKEN_NEWLINE;
1754 case '#': // Like \" but newline is ignored.
1755 while ((cc = input_stack::get(0)) != '\n')
1763 symbol nm = read_escape_name();
1764 if (!(nm.is_null() || nm.is_empty()))
1765 interpolate_arg(nm);
1770 symbol nm = read_escape_name(WITH_ARGS);
1771 if (!(nm.is_null() || nm.is_empty())) {
1772 if (have_string_arg) {
1773 have_string_arg = 0;
1774 interpolate_string_with_args(nm);
1777 interpolate_string(nm);
1782 nd = new non_interpreted_char_node('\001');
1786 c = '0' + do_name_test();
1794 c = '0' + do_expr_test();
1800 nm = get_delim_name();
1803 type = TOKEN_SPECIAL;
1807 nd = new vmotion_node(curenv->get_size() / 2,
1808 curenv->get_fill_color());
1811 nd = read_draw_node();
1819 goto handle_escape_char;
1822 symbol s = read_escape_name(ALLOW_EMPTY);
1826 for (p = s.contents(); *p != '\0'; p++)
1829 if (*p || s.is_empty())
1830 curenv->set_font(s);
1832 curenv->set_font(atoi(s.contents()));
1833 if (!compatible_flag)
1839 symbol s = read_escape_name(ALLOW_EMPTY);
1842 curenv->set_family(s);
1847 symbol s = read_escape_name();
1848 if (!(s.is_null() || s.is_empty()))
1849 interpolate_number_format(s);
1853 if (!get_delim_number(&x, 'm'))
1856 nd = new hmotion_node(x, curenv->get_fill_color());
1859 // don't take height increments relative to previous height if
1860 // in compatibility mode
1861 if (!compatible_flag && curenv->get_char_height())
1863 if (get_delim_number(&x, 'z', curenv->get_char_height()))
1864 curenv->set_char_height(x);
1868 if (get_delim_number(&x, 'z', curenv->get_requested_point_size()))
1869 curenv->set_char_height(x);
1871 if (!compatible_flag)
1875 nm = read_escape_name();
1876 if (nm.is_null() || nm.is_empty())
1878 type = TOKEN_MARK_INPUT;
1884 if (!get_line_arg(&x, (cc == 'l' ? 'm': 'v'), &s))
1887 s = get_charinfo(cc == 'l' ? "ru" : "br");
1889 node *n = curenv->make_char_node(s);
1891 nd = new hline_node(x, n);
1893 nd = new vline_node(x, n);
1897 do_glyph_color(read_escape_name(ALLOW_EMPTY));
1898 if (!compatible_flag)
1902 do_fill_color(read_escape_name(ALLOW_EMPTY));
1903 if (!compatible_flag)
1909 symbol nm = read_increment_and_escape_name(&inc);
1910 if (!(nm.is_null() || nm.is_empty()))
1911 interpolate_number_reg(nm, inc);
1915 if (!get_delim_number(&val, 0))
1917 type = TOKEN_NUMBERED_CHAR;
1920 nd = do_overstrike();
1924 nd = do_suppress(read_escape_name());
1930 type = TOKEN_SPREAD;
1934 nd = new vmotion_node(-curenv->get_size(), curenv->get_fill_color());
1938 if (!compatible_flag)
1943 curenv->set_size(x);
1944 if (!compatible_flag)
1948 if (get_delim_number(&x, 0))
1949 curenv->set_char_slant(x);
1950 if (!compatible_flag)
1955 nd = new non_interpreted_char_node('\t');
1959 nd = new vmotion_node(-curenv->get_size() / 2,
1960 curenv->get_fill_color());
1963 if (!get_delim_number(&x, 'v'))
1966 nd = new vmotion_node(x, curenv->get_fill_color());
1970 symbol nm = read_escape_name();
1971 if (!(nm.is_null() || nm.is_empty()))
1972 interpolate_environment_variable(nm);
1979 if (!get_delim_number(&x, 'v'))
1982 nd = new extra_size_node(x);
1992 symbol s = read_escape_name();
1993 if (s.is_null() || s.is_empty())
1995 request_or_macro *p = lookup_request(s);
1996 macro *m = p->to_macro();
1998 error("can't transparently throughput a request");
2001 nd = new special_node(*m);
2008 if (type == TOKEN_NODE)
2009 nd = new zero_width_node(nd);
2011 charinfo *ci = get_char(1);
2014 node *gn = curenv->make_char_node(ci);
2017 nd = new zero_width_node(gn);
2023 nd = do_zero_width();
2029 goto ESCAPE_LEFT_BRACE;
2031 goto ESCAPE_RIGHT_BRACE;
2035 if (!compatible_flag) {
2036 nm = read_long_escape_name();
2037 if (nm.is_null() || nm.is_empty())
2039 type = TOKEN_SPECIAL;
2042 goto handle_normal_char;
2044 if (cc != escape_char && cc != '.')
2045 warning(WARN_ESCAPE, "escape character ignored before %1",
2046 input_char_description(cc));
2047 goto handle_normal_char;
2053 int token::operator==(const token &t)
2062 case TOKEN_NUMBERED_CHAR:
2063 return val == t.val;
2069 int token::operator!=(const token &t)
2071 return !(*this == t);
2074 // is token a suitable delimiter (like ')?
2076 int token::delimiter(int err)
2105 error("cannot use character `%1' as a starting delimiter", char(c));
2112 case TOKEN_STRETCHABLE_SPACE:
2113 case TOKEN_UNSTRETCHABLE_SPACE:
2117 error("cannot use %1 as a starting delimiter", description());
2124 const char *token::description()
2128 case TOKEN_BACKSPACE:
2129 return "a backspace character";
2140 case TOKEN_HYPHEN_INDICATOR:
2142 case TOKEN_INTERRUPT:
2144 case TOKEN_ITALIC_CORRECTION:
2147 return "a leader character";
2148 case TOKEN_LEFT_BRACE:
2150 case TOKEN_MARK_INPUT:
2156 case TOKEN_NUMBERED_CHAR:
2158 case TOKEN_RIGHT_BRACE:
2163 return "a special character";
2166 case TOKEN_STRETCHABLE_SPACE:
2168 case TOKEN_UNSTRETCHABLE_SPACE:
2171 return "a tab character";
2172 case TOKEN_TRANSPARENT:
2174 case TOKEN_TRANSPARENT_DUMMY:
2176 case TOKEN_ZERO_WIDTH_BREAK:
2179 return "end of input";
2183 return "a magic token";
2188 while (!tok.newline())
2199 if (has_arg() && get_integer(&n))
2200 compatible_flag = n != 0;
2202 compatible_flag = 1;
2206 static void empty_name_warning(int required)
2208 if (tok.newline() || tok.eof()) {
2210 warning(WARN_MISSING, "missing name");
2212 else if (tok.right_brace() || tok.tab()) {
2213 const char *start = tok.description();
2216 } while (tok.space() || tok.right_brace() || tok.tab());
2217 if (!tok.newline() && !tok.eof())
2218 error("%1 is not allowed before an argument", start);
2220 warning(WARN_MISSING, "missing name");
2223 error("name expected (got %1)", tok.description());
2225 error("name expected (got %1): treated as missing", tok.description());
2228 static void non_empty_name_warning()
2230 if (!tok.newline() && !tok.eof() && !tok.space() && !tok.tab()
2231 && !tok.right_brace()
2232 // We don't want to give a warning for .el\{
2233 && !tok.left_brace())
2234 error("%1 is not allowed in a name", tok.description());
2237 symbol get_name(int required)
2239 if (compatible_flag) {
2242 if ((buf[0] = tok.ch()) != 0) {
2244 if ((buf[1] = tok.ch()) != 0) {
2249 non_empty_name_warning();
2253 empty_name_warning(required);
2258 return get_long_name(required);
2261 symbol get_long_name(int required)
2265 char abuf[ABUF_SIZE];
2267 int buf_size = ABUF_SIZE;
2270 if (i + 1 > buf_size) {
2272 buf = new char[ABUF_SIZE*2];
2273 memcpy(buf, abuf, buf_size);
2274 buf_size = ABUF_SIZE*2;
2277 char *old_buf = buf;
2278 buf = new char[buf_size*2];
2279 memcpy(buf, old_buf, buf_size);
2284 if ((buf[i] = tok.ch()) == 0)
2290 empty_name_warning(required);
2293 non_empty_name_warning();
2306 topdiv->set_last_page();
2307 if (!end_macro_name.is_null()) {
2308 spring_trap(end_macro_name);
2310 process_input_stack();
2312 curenv->final_break();
2314 process_input_stack();
2316 if (topdiv->get_page_length() > 0) {
2318 topdiv->set_ejecting();
2319 static unsigned char buf[2] = { LAST_PAGE_EJECTOR, '\0' };
2320 input_stack::push(make_temp_iterator((char *)buf));
2321 topdiv->space(topdiv->get_page_length(), 1);
2323 process_input_stack();
2324 seen_last_page_ejector = 1; // should be set already
2325 topdiv->set_ejecting();
2326 push_page_ejector();
2327 topdiv->space(topdiv->get_page_length(), 1);
2329 process_input_stack();
2331 // This will only happen if a trap-invoked macro starts a diversion,
2332 // or if vertical position traps have been disabled.
2333 cleanup_and_exit(0);
2336 // This implements .ex. The input stack must be cleared before calling
2341 input_stack::clear();
2348 void return_macro_request()
2350 input_stack::pop_macro();
2356 end_macro_name = get_name();
2360 void blank_line_macro()
2362 blank_line_macro_name = get_name();
2366 static void trapping_blank_line()
2368 if (!blank_line_macro_name.is_null())
2369 spring_trap(blank_line_macro_name);
2376 int old_compatible_flag = compatible_flag;
2377 compatible_flag = 0;
2378 symbol nm = get_name();
2382 interpolate_macro(nm);
2383 compatible_flag = old_compatible_flag;
2386 inline int possibly_handle_first_page_transition()
2388 if (topdiv->before_first_page && curdiv == topdiv && !curenv->is_dummy()) {
2389 handle_first_page_transition();
2396 static int transparent_translate(int cc)
2398 if (!invalid_input_char(cc)) {
2399 charinfo *ci = charset_table[cc];
2400 switch (ci->get_special_translation(1)) {
2401 case charinfo::TRANSLATE_SPACE:
2403 case charinfo::TRANSLATE_STRETCHABLE_SPACE:
2404 return ESCAPE_TILDE;
2405 case charinfo::TRANSLATE_DUMMY:
2406 return ESCAPE_AMPERSAND;
2407 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
2408 return ESCAPE_PERCENT;
2410 // This is really ugly.
2411 ci = ci->get_translation(1);
2413 int c = ci->get_ascii_code();
2416 error("can't translate %1 to special character `%2'"
2417 " in transparent throughput",
2418 input_char_description(cc),
2426 struct int_stack_element {
2428 int_stack_element *next;
2438 int_stack::int_stack()
2443 int_stack::~int_stack()
2446 int_stack_element *temp = top;
2452 int int_stack::is_empty()
2457 void int_stack::push(int n)
2459 int_stack_element *p = new int_stack_element;
2465 int int_stack::pop()
2468 int_stack_element *p = top;
2475 int node::reread(int *)
2480 int diverted_space_node::reread(int *bolp)
2482 if (curenv->get_fill())
2483 trapping_blank_line();
2490 int diverted_copy_file_node::reread(int *bolp)
2492 curdiv->copy_file(filename.contents());
2497 int word_space_node::reread(int *bolp)
2500 for (width_list *w = orig_width; w; w = w->next)
2501 curenv->space(w->width, w->sentence_width);
2508 int unbreakable_space_node::reread(int *)
2513 int hmotion_node::reread(int *bolp)
2515 if (unformat && was_tab) {
2516 curenv->handle_tab(0);
2523 void process_input_stack()
2525 int_stack trap_bol_stack;
2528 int suppress_next = 0;
2530 case token::TOKEN_CHAR:
2532 unsigned char ch = tok.c;
2533 if (bol && !have_input
2534 && (ch == curenv->control_char
2535 || ch == curenv->no_break_control_char)) {
2536 break_flag = ch == curenv->control_char;
2537 // skip tabs as well as spaces here
2540 } while (tok.white_space());
2541 symbol nm = get_name();
2545 interpolate_macro(nm);
2550 if (possibly_handle_first_page_transition())
2554 curenv->add_char(charset_table[ch]);
2556 if (tok.type != token::TOKEN_CHAR)
2566 case token::TOKEN_TRANSPARENT:
2569 if (possibly_handle_first_page_transition())
2578 curdiv->transparent_output(transparent_translate(cc));
2580 curdiv->transparent_output(n);
2581 } while (cc != '\n' && cc != EOF);
2583 curdiv->transparent_output('\n');
2588 case token::TOKEN_NEWLINE:
2590 if (bol && !have_input
2591 && !curenv->get_prev_line_interrupted())
2592 trapping_blank_line();
2600 case token::TOKEN_REQUEST:
2602 int request_code = tok.c;
2604 switch (request_code) {
2608 case COPY_FILE_REQUEST:
2611 case TRANSPARENT_FILE_REQUEST:
2615 case VJUSTIFY_REQUEST:
2627 case token::TOKEN_SPACE:
2629 if (possibly_handle_first_page_transition())
2631 else if (bol && !curenv->get_prev_line_interrupted()) {
2633 // save space_width now so that it isn't changed by \f or \s
2634 // which we wouldn't notice here
2635 hunits space_width = curenv->get_space_width();
2637 nspaces += tok.nspaces();
2639 } while (tok.space());
2641 trapping_blank_line();
2645 curenv->add_node(new hmotion_node(space_width * nspaces,
2646 curenv->get_fill_color()));
2656 case token::TOKEN_EOF:
2658 case token::TOKEN_NODE:
2660 if (possibly_handle_first_page_transition())
2662 else if (tok.nd->reread(&bol)) {
2667 curenv->add_node(tok.nd);
2670 curenv->possibly_break_line(1);
2674 case token::TOKEN_PAGE_EJECTOR:
2676 continue_page_eject();
2677 // I think we just want to preserve bol.
2681 case token::TOKEN_BEGIN_TRAP:
2683 trap_bol_stack.push(bol);
2688 case token::TOKEN_END_TRAP:
2690 if (trap_bol_stack.is_empty())
2691 error("spurious end trap token detected!");
2693 bol = trap_bol_stack.pop();
2695 /* I'm not totally happy about this. But I can't think of any other
2696 way to do it. Doing an output_pending_lines() whenever a
2697 TOKEN_END_TRAP is detected doesn't work: for example,
2710 a\%very\%very\%long\%word
2712 will print all but the first lines from the word immediately
2713 after the footer, rather than on the next page. */
2715 if (trap_bol_stack.is_empty())
2716 curenv->output_pending_lines();
2728 trap_sprung_flag = 0;
2732 #ifdef WIDOW_CONTROL
2734 void flush_pending_lines()
2736 while (!tok.newline() && !tok.eof())
2738 curenv->output_pending_lines();
2742 #endif /* WIDOW_CONTROL */
2744 request_or_macro::request_or_macro()
2748 macro *request_or_macro::to_macro()
2753 request::request(REQUEST_FUNCP pp) : p(pp)
2757 void request::invoke(symbol)
2763 enum { SIZE = 128 };
2764 unsigned char s[SIZE];
2769 char_block::char_block()
2778 void append(unsigned char);
2779 void set(unsigned char, int);
2780 unsigned char get(int);
2787 friend class macro_header;
2788 friend class string_iterator;
2791 char_list::char_list()
2792 : ptr(0), len(0), head(0), tail(0)
2796 char_list::~char_list()
2799 char_block *tem = head;
2805 int char_list::length()
2810 void char_list::append(unsigned char c)
2813 head = tail = new char_block;
2817 if (ptr >= tail->s + char_block::SIZE) {
2818 tail->next = new char_block;
2827 void char_list::set(unsigned char c, int offset)
2829 assert(len > offset);
2830 // optimization for access at the end
2831 int boundary = len - len % char_block::SIZE;
2832 if (offset >= boundary) {
2833 *(tail->s + offset - boundary) = c;
2836 char_block *tem = head;
2839 l += char_block::SIZE;
2841 *(tem->s + offset % char_block::SIZE) = c;
2848 unsigned char char_list::get(int offset)
2850 assert(len > offset);
2851 // optimization for access at the end
2852 int boundary = len - len % char_block::SIZE;
2853 if (offset >= boundary)
2854 return *(tail->s + offset - boundary);
2855 char_block *tem = head;
2858 l += char_block::SIZE;
2860 return *(tem->s + offset % char_block::SIZE);
2871 void append(node *);
2875 friend class macro_header;
2876 friend class string_iterator;
2879 void node_list::append(node *n)
2887 tail = tail->next = n;
2891 int node_list::length()
2894 for (node *n = head; n != 0; n = n->next)
2899 node_list::node_list()
2904 node *node_list::extract()
2911 node_list::~node_list()
2913 delete_node_list(head);
2916 struct macro_header {
2921 macro_header() { count = 1; }
2922 macro_header *copy(int);
2927 if (p != 0 && --(p->count) <= 0)
2933 if (!input_stack::get_location(1, &filename, &lineno)) {
2942 macro::macro(const macro &m)
2943 : p(m.p), filename(m.filename), lineno(m.lineno), len(m.len),
2944 empty_macro(m.empty_macro)
2950 macro ¯o::operator=(const macro &m)
2952 // don't assign object
2955 if (p != 0 && --(p->count) <= 0)
2958 filename = m.filename;
2961 empty_macro = m.empty_macro;
2965 void macro::append(unsigned char c)
2969 p = new macro_header;
2970 if (p->cl.length() != len) {
2971 macro_header *tem = p->copy(len);
2972 if (--(p->count) <= 0)
2978 if (c != COMPATIBLE_SAVE && c != COMPATIBLE_RESTORE)
2982 void macro::set(unsigned char c, int offset)
2986 p->cl.set(c, offset);
2989 unsigned char macro::get(int offset)
2992 return p->cl.get(offset);
3000 void macro::append_str(const char *s)
3005 while (s[i] != (char)0) {
3012 void macro::append(node *n)
3016 p = new macro_header;
3017 if (p->cl.length() != len) {
3018 macro_header *tem = p->copy(len);
3019 if (--(p->count) <= 0)
3029 void macro::append_unsigned(unsigned int i)
3031 unsigned int j = i / 10;
3034 append(((unsigned char)(((int)'0') + i % 10)));
3037 void macro::append_int(int i)
3043 append_unsigned((unsigned int)i);
3046 void macro::print_size()
3048 errprint("%1", len);
3051 // make a copy of the first n bytes
3053 macro_header *macro_header::copy(int n)
3055 macro_header *p = new macro_header;
3056 char_block *bp = cl.head;
3057 unsigned char *ptr = bp->s;
3060 if (ptr >= bp->s + char_block::SIZE) {
3067 p->nl.append(nd->copy());
3076 object_dictionary_iterator iter(request_dictionary);
3077 request_or_macro *rm;
3079 while (iter.get(&s, (object **)&rm)) {
3080 assert(!s.is_null());
3081 macro *m = rm->to_macro();
3083 errprint("%1\t", s.contents());
3092 class string_iterator : public input_iterator {
3094 const char *how_invoked;
3098 int count; // of characters remaining
3100 int saved_compatible_flag;
3105 string_iterator(const macro &m, const char *p = 0, symbol s = NULL_SYMBOL);
3108 int get_location(int, const char **, int *);
3110 void save_compatible_flag(int f) { saved_compatible_flag = f; }
3111 int get_compatible_flag() { return saved_compatible_flag; }
3114 string_iterator::string_iterator(const macro &m, const char *p, symbol s)
3115 : mac(m), how_invoked(p),
3116 newline_flag(0), lineno(1), nm(s)
3120 bp = mac.p->cl.head;
3121 nd = mac.p->nl.head;
3131 string_iterator::string_iterator()
3142 int string_iterator::fill(node **np)
3149 const unsigned char *p = eptr;
3150 if (p >= bp->s + char_block::SIZE) {
3162 const unsigned char *e = bp->s + char_block::SIZE;
3167 unsigned char c = *p;
3168 if (c == '\n' || c == ESCAPE_NEWLINE) {
3182 int string_iterator::peek()
3186 const unsigned char *p = eptr;
3187 if (p >= bp->s + char_block::SIZE) {
3193 int string_iterator::get_location(int allow_macro,
3194 const char **filep, int *linep)
3198 if (mac.filename == 0)
3200 *filep = mac.filename;
3201 *linep = mac.lineno + lineno - 1;
3205 void string_iterator::backtrace()
3208 errprint("%1:%2: backtrace", mac.filename, mac.lineno + lineno - 1);
3211 errprint(": %1 `%2'\n", how_invoked, nm.contents());
3213 errprint(": %1\n", how_invoked);
3220 class temp_iterator : public input_iterator {
3221 unsigned char *base;
3222 temp_iterator(const char *, int len);
3225 friend input_iterator *make_temp_iterator(const char *);
3231 temp_iterator::temp_iterator(const char *s, int len)
3233 base = new unsigned char[len];
3234 memcpy(base, s, len);
3239 temp_iterator::~temp_iterator()
3244 class small_temp_iterator : public input_iterator {
3246 small_temp_iterator(const char *, int);
3247 ~small_temp_iterator();
3248 enum { BLOCK = 16 };
3249 static small_temp_iterator *free_list;
3250 void *operator new(size_t);
3251 void operator delete(void *);
3253 unsigned char buf[SIZE];
3254 friend input_iterator *make_temp_iterator(const char *);
3257 small_temp_iterator *small_temp_iterator::free_list = 0;
3259 void *small_temp_iterator::operator new(size_t n)
3261 assert(n == sizeof(small_temp_iterator));
3264 (small_temp_iterator *)new char[sizeof(small_temp_iterator)*BLOCK];
3265 for (int i = 0; i < BLOCK - 1; i++)
3266 free_list[i].next = free_list + i + 1;
3267 free_list[BLOCK-1].next = 0;
3269 small_temp_iterator *p = free_list;
3270 free_list = (small_temp_iterator *)(free_list->next);
3278 void small_temp_iterator::operator delete(void *p)
3281 ((small_temp_iterator *)p)->next = free_list;
3282 free_list = (small_temp_iterator *)p;
3286 small_temp_iterator::~small_temp_iterator()
3293 small_temp_iterator::small_temp_iterator(const char *s, int len)
3295 for (int i = 0; i < len; i++)
3301 input_iterator *make_temp_iterator(const char *s)
3304 return new small_temp_iterator(s, 0);
3307 if (n <= small_temp_iterator::SIZE)
3308 return new small_temp_iterator(s, n);
3310 return new temp_iterator(s, n);
3314 // this is used when macros with arguments are interpolated
3319 arg_list(const macro &);
3323 arg_list::arg_list(const macro &m) : mac(m), next(0)
3327 arg_list::~arg_list()
3331 class macro_iterator : public string_iterator {
3335 macro_iterator(symbol, macro &, const char *how_invoked = "macro");
3338 int has_args() { return 1; }
3339 input_iterator *get_arg(int i);
3340 int nargs() { return argc; }
3341 void add_arg(const macro &m);
3343 int is_macro() { return 1; }
3346 input_iterator *macro_iterator::get_arg(int i)
3349 return make_temp_iterator(nm.contents());
3350 if (i > 0 && i <= argc) {
3352 for (int j = 1; j < i; j++) {
3356 return new string_iterator(p->mac);
3362 void macro_iterator::add_arg(const macro &m)
3365 for (p = &args; *p; p = &((*p)->next))
3367 *p = new arg_list(m);
3371 void macro_iterator::shift(int n)
3373 while (n > 0 && argc > 0) {
3374 arg_list *tem = args;
3382 // This gets used by eg .if '\?xxx\?''.
3384 int operator==(const macro &m1, const macro &m2)
3386 if (m1.len != m2.len)
3388 string_iterator iter1(m1);
3389 string_iterator iter2(m2);
3393 int c1 = iter1.get(&nd1);
3396 int c2 = iter2.get(&nd2);
3408 int are_same = nd1->type() == nd2->type() && nd1->same(nd2);
3418 static void interpolate_macro(symbol nm)
3420 request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm);
3423 const char *s = nm.contents();
3424 if (strlen(s) > 2) {
3425 request_or_macro *r;
3430 r = (request_or_macro *)request_dictionary.lookup(symbol(buf));
3432 macro *m = r->to_macro();
3433 if (!m || !m->empty())
3434 warned = warning(WARN_SPACE,
3435 "`%1' not defined (probable missing space after `%2')",
3436 nm.contents(), buf);
3440 warning(WARN_MAC, "`%1' not defined", nm.contents());
3442 request_dictionary.define(nm, p);
3453 static void decode_args(macro_iterator *mi)
3455 if (!tok.newline() && !tok.eof()) {
3457 int c = get_copy(&n);
3461 if (c == '\n' || c == EOF)
3464 int quote_input_level = 0;
3465 int done_tab_warning = 0;
3467 quote_input_level = input_stack::get_level();
3470 while (c != EOF && c != '\n' && !(c == ' ' && quote_input_level == 0)) {
3471 if (quote_input_level > 0 && c == '\"'
3473 || input_stack::get_level() == quote_input_level)) {
3486 if (c == '\t' && quote_input_level == 0 && !done_tab_warning) {
3487 warning(WARN_TAB, "tab character in unquoted macro argument");
3488 done_tab_warning = 1;
3500 static void decode_string_args(macro_iterator *mi)
3503 int c = get_copy(&n);
3507 if (c == '\n' || c == EOF) {
3508 error("missing `]'");
3514 int quote_input_level = 0;
3515 int done_tab_warning = 0;
3517 quote_input_level = input_stack::get_level();
3520 while (c != EOF && c != '\n'
3521 && !(c == ']' && quote_input_level == 0)
3522 && !(c == ' ' && quote_input_level == 0)) {
3523 if (quote_input_level > 0 && c == '\"'
3524 && input_stack::get_level() == quote_input_level) {
3537 if (c == '\t' && quote_input_level == 0 && !done_tab_warning) {
3538 warning(WARN_TAB, "tab character in unquoted string argument");
3539 done_tab_warning = 1;
3550 void macro::invoke(symbol nm)
3552 macro_iterator *mi = new macro_iterator(nm, *this);
3554 input_stack::push(mi);
3558 macro *macro::to_macro()
3565 return empty_macro == 1;
3568 macro_iterator::macro_iterator(symbol s, macro &m, const char *how_invoked)
3569 : string_iterator(m, how_invoked, s), args(0), argc(0)
3573 macro_iterator::macro_iterator() : args(0), argc(0)
3577 macro_iterator::~macro_iterator()
3580 arg_list *tem = args;
3586 int trap_sprung_flag = 0;
3587 int postpone_traps_flag = 0;
3588 symbol postponed_trap;
3590 void spring_trap(symbol nm)
3592 assert(!nm.is_null());
3593 trap_sprung_flag = 1;
3594 if (postpone_traps_flag) {
3595 postponed_trap = nm;
3598 static char buf[2] = { BEGIN_TRAP, 0 };
3599 static char buf2[2] = { END_TRAP, '\0' };
3600 input_stack::push(make_temp_iterator(buf2));
3601 request_or_macro *p = lookup_request(nm);
3602 macro *m = p->to_macro();
3604 input_stack::push(new macro_iterator(nm, *m, "trap-invoked macro"));
3606 error("you can't invoke a request with a trap");
3607 input_stack::push(make_temp_iterator(buf));
3610 void postpone_traps()
3612 postpone_traps_flag = 1;
3615 int unpostpone_traps()
3617 postpone_traps_flag = 0;
3618 if (!postponed_trap.is_null()) {
3619 spring_trap(postponed_trap);
3620 postponed_trap = NULL_SYMBOL;
3629 macro_iterator *mi = new macro_iterator;
3630 int reading_from_terminal = isatty(fileno(stdin));
3632 if (!tok.newline() && !tok.eof()) {
3633 int c = get_copy(0);
3636 while (c != EOF && c != '\n' && c != ' ') {
3637 if (!invalid_input_char(c)) {
3638 if (reading_from_terminal)
3649 if (reading_from_terminal) {
3650 fputc(had_prompt ? ':' : '\a', stderr);
3653 input_stack::push(mi);
3657 while ((c = getchar()) != EOF) {
3658 if (invalid_input_char(c))
3659 warning(WARN_INPUT, "invalid input character code %1", int(c));
3672 if (reading_from_terminal)
3674 input_stack::push(new string_iterator(mac));
3678 enum define_mode { DEFINE_NORMAL, DEFINE_APPEND, DEFINE_IGNORE };
3679 enum calling_mode { CALLING_NORMAL, CALLING_INDIRECT, CALLING_DISABLE_COMP };
3681 void do_define_string(define_mode mode, calling_mode calling)
3695 else if (!tok.space()) {
3696 error("bad string definition");
3707 request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm);
3708 macro *mm = rm ? rm->to_macro() : 0;
3709 if (mode == DEFINE_APPEND && mm)
3711 if (calling == CALLING_DISABLE_COMP)
3712 mac.append(COMPATIBLE_SAVE);
3713 while (c != '\n' && c != EOF) {
3717 mac.append((unsigned char)c);
3722 request_dictionary.define(nm, mm);
3724 if (calling == CALLING_DISABLE_COMP)
3725 mac.append(COMPATIBLE_RESTORE);
3730 void define_string()
3732 do_define_string(DEFINE_NORMAL, CALLING_NORMAL);
3735 void define_nocomp_string()
3737 do_define_string(DEFINE_NORMAL, CALLING_DISABLE_COMP);
3740 void append_string()
3742 do_define_string(DEFINE_APPEND, CALLING_NORMAL);
3745 void append_nocomp_string()
3747 do_define_string(DEFINE_APPEND, CALLING_DISABLE_COMP);
3750 void do_define_character(int fallback)
3755 charinfo *ci = tok.get_char(1);
3765 else if (!tok.space()) {
3766 error("bad character definition");
3772 while (c == ' ' || c == '\t')
3776 macro *m = new macro;
3777 while (c != '\n' && c != EOF) {
3781 m->append((unsigned char)c);
3784 m = ci->set_macro(m, fallback);
3790 void define_character()
3792 do_define_character(0);
3795 void define_fallback_character()
3797 do_define_character(1);
3800 static void remove_character()
3803 while (!tok.newline() && !tok.eof()) {
3804 if (!tok.space() && !tok.tab()) {
3805 charinfo *ci = tok.get_char(1);
3808 macro *m = ci->set_macro(0);
3817 static void interpolate_string(symbol nm)
3819 request_or_macro *p = lookup_request(nm);
3820 macro *m = p->to_macro();
3822 error("you can only invoke a string or macro using \\*");
3824 string_iterator *si = new string_iterator(*m, "string", nm);
3825 input_stack::push(si);
3829 static void interpolate_string_with_args(symbol s)
3831 request_or_macro *p = lookup_request(s);
3832 macro *m = p->to_macro();
3834 error("you can only invoke a string or macro using \\*");
3836 macro_iterator *mi = new macro_iterator(s, *m);
3837 decode_string_args(mi);
3838 input_stack::push(mi);
3842 /* This class is used for the implementation of \$@. It is used for
3843 each of the closing double quotes. It artificially increases the
3844 input level by 2, so that the closing double quote will appear to have
3845 the same input level as the opening quote. */
3847 class end_quote_iterator : public input_iterator {
3848 unsigned char buf[1];
3850 end_quote_iterator();
3851 ~end_quote_iterator() { }
3852 int internal_level() { return 2; }
3855 end_quote_iterator::end_quote_iterator()
3862 static void interpolate_arg(symbol nm)
3864 const char *s = nm.contents();
3865 if (!s || *s == '\0')
3866 copy_mode_error("missing argument name");
3867 else if (s[1] == 0 && csdigit(s[0]))
3868 input_stack::push(input_stack::get_arg(s[0] - '0'));
3869 else if (s[0] == '*' && s[1] == '\0') {
3870 for (int i = input_stack::nargs(); i > 0; i--) {
3871 input_stack::push(input_stack::get_arg(i));
3873 input_stack::push(make_temp_iterator(" "));
3876 else if (s[0] == '@' && s[1] == '\0') {
3877 for (int i = input_stack::nargs(); i > 0; i--) {
3878 input_stack::push(new end_quote_iterator);
3879 input_stack::push(input_stack::get_arg(i));
3880 input_stack::push(make_temp_iterator(i == 1 ? "\"" : " \""));
3885 for (p = s; *p && csdigit(*p); p++)
3888 copy_mode_error("bad argument name `%1'", s);
3890 input_stack::push(input_stack::get_arg(atoi(s)));
3894 void handle_first_page_transition()
3897 topdiv->begin_page();
3900 // We push back a token by wrapping it up in a token_node, and
3901 // wrapping that up in a string_iterator.
3903 static void push_token(const token &t)
3906 m.append(new token_node(t));
3907 input_stack::push(new string_iterator(m));
3910 void push_page_ejector()
3912 static char buf[2] = { PAGE_EJECTOR, '\0' };
3913 input_stack::push(make_temp_iterator(buf));
3916 void handle_initial_request(unsigned char code)
3922 mac.append(new token_node(tok));
3923 input_stack::push(new string_iterator(mac));
3924 input_stack::push(make_temp_iterator(buf));
3925 topdiv->begin_page();
3929 void handle_initial_title()
3931 handle_initial_request(TITLE_REQUEST);
3934 // this should be local to define_macro, but cfront 1.2 doesn't support that
3935 static symbol dot_symbol(".");
3937 void do_define_macro(define_mode mode, calling_mode calling)
3940 if (calling == CALLING_INDIRECT) {
3941 symbol temp1 = get_name(1);
3942 if (temp1.is_null()) {
3946 symbol temp2 = get_name();
3947 input_stack::push(make_temp_iterator("\n"));
3948 if (!temp2.is_null()) {
3949 interpolate_string(temp2);
3950 input_stack::push(make_temp_iterator(" "));
3952 interpolate_string(temp1);
3953 input_stack::push(make_temp_iterator(" "));
3956 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
3963 term = get_name(); // the request that terminates the definition
3966 while (!tok.newline() && !tok.eof())
3968 const char *start_filename;
3970 int have_start_location = input_stack::get_location(0, &start_filename,
3973 // doing this here makes the line numbers come out right
3974 int c = get_copy(&n, 1);
3977 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
3978 request_or_macro *rm =
3979 (request_or_macro *)request_dictionary.lookup(nm);
3981 mm = rm->to_macro();
3982 if (mm && mode == DEFINE_APPEND)
3986 if (calling == CALLING_DISABLE_COMP)
3987 mac.append(COMPATIBLE_SAVE);
3989 while (c == ESCAPE_NEWLINE) {
3990 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND)
3992 c = get_copy(&n, 1);
3994 if (bol && c == '.') {
3995 const char *s = term.contents();
3997 // see if it matches term
4000 while ((d = get_copy(&n)) == ' ' || d == '\t')
4002 if ((unsigned char)s[0] == d) {
4003 for (i = 1; s[i] != 0; i++) {
4005 if ((unsigned char)s[i] != d)
4011 && ((i == 2 && compatible_flag)
4012 || (d = get_copy(&n)) == ' '
4013 || d == '\n')) { // we found it
4018 if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) {
4021 request_dictionary.define(nm, mm);
4023 if (calling == CALLING_DISABLE_COMP)
4024 mac.append(COMPATIBLE_RESTORE);
4027 if (term != dot_symbol) {
4029 interpolate_macro(term);
4035 if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) {
4037 for (int j = 0; j < i; j++)
4043 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4044 if (have_start_location)
4045 error_with_file_and_line(start_filename, start_lineno,
4046 "end of file while defining macro `%1'",
4049 error("end of file while defining macro `%1'", nm.contents());
4052 if (have_start_location)
4053 error_with_file_and_line(start_filename, start_lineno,
4054 "end of file while ignoring input lines");
4056 error("end of file while ignoring input lines");
4061 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4068 c = get_copy(&n, 1);
4074 do_define_macro(DEFINE_NORMAL, CALLING_NORMAL);
4077 void define_nocomp_macro()
4079 do_define_macro(DEFINE_NORMAL, CALLING_DISABLE_COMP);
4082 void define_indirect_macro()
4084 do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT);
4089 do_define_macro(DEFINE_APPEND, CALLING_NORMAL);
4092 void append_indirect_macro()
4094 do_define_macro(DEFINE_APPEND, CALLING_INDIRECT);
4097 void append_nocomp_macro()
4099 do_define_macro(DEFINE_APPEND, CALLING_DISABLE_COMP);
4105 do_define_macro(DEFINE_IGNORE, CALLING_NORMAL);
4112 symbol s = get_name();
4115 request_dictionary.remove(s);
4122 symbol s1 = get_name(1);
4123 if (!s1.is_null()) {
4124 symbol s2 = get_name(1);
4126 request_dictionary.rename(s1, s2);
4133 symbol s1 = get_name(1);
4134 if (!s1.is_null()) {
4135 symbol s2 = get_name(1);
4136 if (!s2.is_null()) {
4137 if (!request_dictionary.alias(s1, s2))
4138 warning(WARN_MAC, "`%1' not defined", s2.contents());
4146 symbol s = get_name(1);
4148 request_or_macro *p = lookup_request(s);
4149 macro *m = p->to_macro();
4151 error("cannot chop request");
4152 else if (m->empty())
4153 error("cannot chop empty macro");
4155 int have_restore = 0;
4156 // we have to check for additional save/restore pairs which could be
4157 // there due to empty am1 requests.
4159 if (m->get(m->len - 1) != COMPATIBLE_RESTORE)
4163 if (m->get(m->len - 1) != COMPATIBLE_SAVE)
4171 error("cannot chop empty macro");
4174 m->set(COMPATIBLE_RESTORE, m->len - 1);
4183 void substring_request()
4185 int start; // 0, 1, ..., n-1 or -1, -2, ...
4186 symbol s = get_name(1);
4187 if (!s.is_null() && get_integer(&start)) {
4188 request_or_macro *p = lookup_request(s);
4189 macro *m = p->to_macro();
4191 error("cannot apply `substring' on a request");
4194 if (!has_arg() || get_integer(&end)) {
4195 int real_length = 0; // 1, 2, ..., n
4196 string_iterator iter1(*m);
4197 for (int l = 0; l < m->len; l++) {
4198 int c = iter1.get(0);
4199 if (c == COMPATIBLE_SAVE || c == COMPATIBLE_RESTORE)
4206 start += real_length;
4214 if (start >= real_length || end < 0) {
4216 "start and end index of substring out of range");
4219 if (--(m->p->count) <= 0)
4228 "start index of substring out of range, set to 0");
4231 if (end >= real_length) {
4233 "end index of substring out of range, set to string length");
4234 end = real_length - 1;
4236 // now extract the substring
4237 string_iterator iter(*m);
4239 for (i = 0; i < start; i++) {
4240 int c = iter.get(0);
4241 while (c == COMPATIBLE_SAVE || c == COMPATIBLE_RESTORE)
4247 for (; i <= end; i++) {
4249 int c = iter.get(&nd);
4250 while (c == COMPATIBLE_SAVE || c == COMPATIBLE_RESTORE)
4257 mac.append((unsigned char)c);
4266 void length_request()
4270 if (ret.is_null()) {
4280 else if (!tok.space()) {
4281 error("bad string definition");
4292 while (c != '\n' && c != EOF) {
4296 reg *r = (reg*)number_reg_dictionary.lookup(ret);
4300 set_number_reg(ret, len);
4304 void asciify_macro()
4306 symbol s = get_name(1);
4308 request_or_macro *p = lookup_request(s);
4309 macro *m = p->to_macro();
4311 error("cannot asciify request");
4314 string_iterator iter(*m);
4317 int c = iter.get(&nd);
4331 void unformat_macro()
4333 symbol s = get_name(1);
4335 request_or_macro *p = lookup_request(s);
4336 macro *m = p->to_macro();
4338 error("cannot unformat request");
4341 string_iterator iter(*m);
4344 int c = iter.get(&nd);
4350 if (nd->set_unformat_flag())
4360 static void interpolate_environment_variable(symbol nm)
4362 const char *s = getenv(nm.contents());
4364 input_stack::push(make_temp_iterator(s));
4367 void interpolate_number_reg(symbol nm, int inc)
4369 reg *r = lookup_number_reg(nm);
4374 input_stack::push(make_temp_iterator(r->get_string()));
4377 static void interpolate_number_format(symbol nm)
4379 reg *r = (reg *)number_reg_dictionary.lookup(nm);
4381 input_stack::push(make_temp_iterator(r->get_format()));
4384 static int get_delim_number(units *n, int si, int prev_value)
4388 if (start.delimiter(1)) {
4390 if (get_number(n, si, prev_value)) {
4392 warning(WARN_DELIM, "closing delimiter does not match");
4399 static int get_delim_number(units *n, int si)
4403 if (start.delimiter(1)) {
4405 if (get_number(n, si)) {
4407 warning(WARN_DELIM, "closing delimiter does not match");
4414 static int get_line_arg(units *n, int si, charinfo **cp)
4418 int start_level = input_stack::get_level();
4419 if (!start.delimiter(1))
4422 if (get_number(n, si)) {
4423 if (tok.dummy() || tok.transparent_dummy())
4425 if (!(start == tok && input_stack::get_level() == start_level)) {
4426 *cp = tok.get_char(1);
4429 if (!(start == tok && input_stack::get_level() == start_level))
4430 warning(WARN_DELIM, "closing delimiter does not match");
4436 static int read_size(int *x)
4446 else if (c == '+') {
4457 // allow an increment either before or after the left parenthesis
4463 else if (c == '+') {
4478 val = val*10 + (c - '0');
4483 else if (csdigit(c)) {
4485 if (!inc && c != '0' && c < '4') {
4491 val = val*10 + (c - '0');
4495 else if (!tok.delimiter(1))
4501 ? get_number(&val, 'z')
4502 : get_number(&val, 'z', curenv->get_requested_point_size())))
4504 if (!(start.ch() == '[' && tok.ch() == ']') && start != tok) {
4505 if (start.ch() == '[')
4506 error("missing `]'");
4508 error("missing closing delimiter");
4516 // special case -- \s[0] and \s0 means to revert to previous size
4523 *x = curenv->get_requested_point_size() + val;
4526 *x = curenv->get_requested_point_size() - val;
4533 "\\s request results in non-positive point size; set to 1");
4539 error("bad digit in point size");
4544 static symbol get_delim_name()
4549 error("end of input at start of delimited name");
4552 if (start.newline()) {
4553 error("can't delimit name with a newline");
4556 int start_level = input_stack::get_level();
4557 char abuf[ABUF_SIZE];
4559 int buf_size = ABUF_SIZE;
4562 if (i + 1 > buf_size) {
4564 buf = new char[ABUF_SIZE*2];
4565 memcpy(buf, abuf, buf_size);
4566 buf_size = ABUF_SIZE*2;
4569 char *old_buf = buf;
4570 buf = new char[buf_size*2];
4571 memcpy(buf, old_buf, buf_size);
4578 && (compatible_flag || input_stack::get_level() == start_level))
4580 if ((buf[i] = tok.ch()) == 0) {
4581 error("missing delimiter (got %1)", tok.description());
4591 error("empty delimited name");
4606 static void do_register()
4610 if (!start.delimiter(1))
4613 symbol nm = get_long_name(1);
4618 reg *r = (reg *)number_reg_dictionary.lookup(nm);
4620 if (!r || !r->get_value(&prev_value))
4623 if (!get_number(&val, 'u', prev_value))
4626 warning(WARN_DELIM, "closing delimiter does not match");
4630 set_number_reg(nm, val);
4633 // this implements the \w escape sequence
4635 static void do_width()
4639 int start_level = input_stack::get_level();
4640 environment env(curenv);
4641 environment *oldenv = curenv;
4646 warning(WARN_DELIM, "missing closing delimiter");
4649 if (tok.newline()) {
4650 warning(WARN_DELIM, "missing closing delimiter");
4651 input_stack::push(make_temp_iterator("\n"));
4655 && (compatible_flag || input_stack::get_level() == start_level))
4660 units x = env.get_input_line_position().to_units();
4661 input_stack::push(make_temp_iterator(i_to_a(x)));
4662 env.width_registers();
4666 charinfo *page_character;
4668 void set_page_character()
4670 page_character = get_optional_char();
4674 static const symbol percent_symbol("%");
4676 void read_title_parts(node **part, hunits *part_width)
4679 if (tok.newline() || tok.eof())
4682 int start_level = input_stack::get_level();
4684 for (int i = 0; i < 3; i++) {
4685 while (!tok.newline() && !tok.eof()) {
4687 && (compatible_flag || input_stack::get_level() == start_level)) {
4691 if (page_character != 0 && tok.get_char() == page_character)
4692 interpolate_number_reg(percent_symbol, 0);
4697 curenv->wrap_up_tab();
4698 part_width[i] = curenv->get_input_line_position();
4699 part[i] = curenv->extract_output_line();
4701 while (!tok.newline() && !tok.eof())
4705 class non_interpreted_node : public node {
4708 non_interpreted_node(const macro &);
4709 int interpret(macro *);
4716 non_interpreted_node::non_interpreted_node(const macro &m) : mac(m)
4720 int non_interpreted_node::same(node *nd)
4722 return mac == ((non_interpreted_node *)nd)->mac;
4725 const char *non_interpreted_node::type()
4727 return "non_interpreted_node";
4730 int non_interpreted_node::force_tprint()
4735 node *non_interpreted_node::copy()
4737 return new non_interpreted_node(mac);
4740 int non_interpreted_node::interpret(macro *m)
4742 string_iterator si(mac);
4756 static node *do_non_interpreted()
4761 while ((c = get_copy(&n)) != ESCAPE_QUESTION && c != EOF && c != '\n')
4766 if (c == EOF || c == '\n') {
4767 error("missing \\?");
4770 return new non_interpreted_node(mac);
4773 static void encode_char(macro *mac, char c)
4776 if ((font::use_charnames_in_special) && tok.special()) {
4777 charinfo *ci = tok.get_char(1);
4778 const char *s = ci->get_symbol()->contents();
4779 if (s[0] != (char)0) {
4783 while (s[i] != (char)0) {
4791 else if (tok.stretchable_space()
4792 || tok.unstretchable_space())
4794 else if (!(tok.hyphen_indicator()
4796 || tok.transparent_dummy()
4797 || tok.zero_width_break()))
4798 error("%1 is invalid within \\X", tok.description());
4801 if ((font::use_charnames_in_special) && (c == '\\')) {
4803 * add escape escape sequence
4815 int start_level = input_stack::get_level();
4818 tok != start || input_stack::get_level() != start_level;
4821 warning(WARN_DELIM, "missing closing delimiter");
4824 if (tok.newline()) {
4825 input_stack::push(make_temp_iterator("\n"));
4826 warning(WARN_DELIM, "missing closing delimiter");
4834 else if (tok.leader())
4836 else if (tok.backspace())
4840 encode_char(&mac, c);
4842 return new special_node(mac);
4845 void output_request()
4847 if (!tok.newline() && !tok.eof()) {
4855 if (c != ' ' && c != '\t')
4858 for (; c != '\n' && c != EOF; c = get_copy(0))
4859 topdiv->transparent_output(c);
4860 topdiv->transparent_output('\n');
4865 extern int image_no; // from node.cc
4867 static node *do_suppress(symbol nm)
4869 if (nm.is_null() || nm.is_empty()) {
4870 error("expecting an argument to escape \\O");
4873 const char *s = nm.contents();
4876 if (begin_level == 0)
4877 // suppress generation of glyphs
4878 return new suppress_node(0, 0);
4881 if (begin_level == 0)
4882 // enable generation of glyphs
4883 return new suppress_node(1, 0);
4886 if (begin_level == 0)
4887 return new suppress_node(1, 1);
4897 s++; // move over '5'
4899 if (*s == (char)0) {
4900 error("missing position and filename in \\O");
4903 if (!(position == 'l'
4906 || position == 'i')) {
4907 error("l, r, c, or i position expected (got %1 in \\O)", position);
4910 s++; // onto image name
4911 if (s == (char *)0) {
4912 error("missing image name for \\O");
4916 if (begin_level == 0)
4917 return new suppress_node(symbol(s), position, image_no);
4921 error("`%1' is an invalid argument to \\O", *s);
4926 void special_node::tprint(troff_output_file *out)
4929 string_iterator iter(mac);
4931 int c = iter.get(0);
4934 for (const char *s = ::asciify(c); *s; s++)
4935 tprint_char(out, *s);
4940 int get_file_line(const char **filename, int *lineno)
4942 return input_stack::get_location(0, filename, lineno);
4948 if (get_integer(&n)) {
4949 const char *filename = 0;
4951 symbol s = get_long_name();
4952 filename = s.contents();
4954 (void)input_stack::set_location(filename, n-1);
4959 static int nroff_mode = 0;
4961 static void nroff_request()
4967 static void troff_request()
4973 static void skip_alternative()
4976 // ensure that ``.if 0\{'' works as expected
4977 if (tok.left_brace())
4981 c = input_stack::get(0);
4984 if (c == ESCAPE_LEFT_BRACE)
4986 else if (c == ESCAPE_RIGHT_BRACE)
4988 else if (c == escape_char && escape_char > 0)
4989 switch(input_stack::get(0)) {
4997 while ((c = input_stack::get(0)) != '\n' && c != EOF)
5001 Note that the level can properly be < 0, eg
5007 So don't give an error message in this case.
5009 if (level <= 0 && c == '\n')
5015 static void begin_alternative()
5017 while (tok.space() || tok.left_brace())
5027 static int_stack if_else_stack;
5034 while (tok.ch() == '!') {
5039 unsigned char c = tok.ch();
5042 result = !nroff_mode;
5044 else if (c == 'n') {
5046 result = nroff_mode;
5048 else if (c == 'v') {
5052 else if (c == 'o') {
5053 result = (topdiv->get_page_number() & 1);
5056 else if (c == 'e') {
5057 result = !(topdiv->get_page_number() & 1);
5060 else if (c == 'd' || c == 'r') {
5062 symbol nm = get_name(1);
5068 ? request_dictionary.lookup(nm) != 0
5069 : number_reg_dictionary.lookup(nm) != 0);
5071 else if (c == 'm') {
5073 symbol nm = get_long_name(1);
5078 result = (nm == default_symbol
5079 || color_dictionary.lookup(nm) != 0);
5081 else if (c == 'c') {
5084 charinfo *ci = tok.get_char(1);
5089 result = character_exists(ci, curenv);
5092 else if (tok.space())
5094 else if (tok.delimiter()) {
5096 int delim_level = input_stack::get_level();
5097 environment env1(curenv);
5098 environment env2(curenv);
5099 environment *oldenv = curenv;
5101 for (int i = 0; i < 2; i++) {
5104 if (tok.newline() || tok.eof()) {
5105 warning(WARN_DELIM, "missing closing delimiter");
5111 && (compatible_flag || input_stack::get_level() == delim_level))
5117 node *n1 = env1.extract_output_line();
5118 node *n2 = env2.extract_output_line();
5119 result = same_node_list(n1, n2);
5120 delete_node_list(n1);
5121 delete_node_list(n2);
5127 if (!get_number(&n, 'u')) {
5137 begin_alternative();
5143 void if_else_request()
5145 if_else_stack.push(do_if_request());
5155 if (if_else_stack.is_empty()) {
5156 warning(WARN_EL, "unbalanced .el request");
5160 if (if_else_stack.pop())
5163 begin_alternative();
5167 static int while_depth = 0;
5168 static int while_break_flag = 0;
5170 void while_request()
5175 mac.append(new token_node(tok));
5178 int c = input_stack::get(&n);
5194 if (c == ESCAPE_LEFT_BRACE)
5196 else if (c == ESCAPE_RIGHT_BRACE)
5198 else if (c == escape_char)
5201 if (c == '\n' && level <= 0)
5206 error("unbalanced \\{ \\}");
5209 input_stack::add_boundary();
5211 input_stack::push(new string_iterator(mac, "while loop"));
5213 if (!do_if_request()) {
5214 while (input_stack::get(0) != EOF)
5218 process_input_stack();
5219 if (while_break_flag || input_stack::is_return_boundary()) {
5220 while_break_flag = 0;
5224 input_stack::remove_boundary();
5230 void while_break_request()
5233 error("no while loop");
5237 while_break_flag = 1;
5238 while (input_stack::get(0) != EOF)
5244 void while_continue_request()
5247 error("no while loop");
5251 while (input_stack::get(0) != EOF)
5261 symbol nm = get_long_name(1);
5265 while (!tok.newline() && !tok.eof())
5268 FILE *fp = fopen(nm.contents(), "r");
5270 input_stack::push(new file_iterator(fp, nm.contents()));
5272 error("can't open `%1': %2", nm.contents(), strerror(errno));
5277 // like .so but use popen()
5282 error(".pso request not allowed in safer mode");
5286 #ifdef POPEN_MISSING
5287 error("pipes not available on this system");
5289 #else /* not POPEN_MISSING */
5290 if (tok.newline() || tok.eof())
5291 error("missing command");
5294 while ((c = get_copy(0)) == ' ' || c == '\t')
5297 char *buf = new char[buf_size];
5299 for (; c != '\n' && c != EOF; c = get_copy(0)) {
5300 const char *s = asciify(c);
5301 int slen = strlen(s);
5302 if (buf_used + slen + 1> buf_size) {
5303 char *old_buf = buf;
5304 int old_buf_size = buf_size;
5306 buf = new char[buf_size];
5307 memcpy(buf, old_buf, old_buf_size);
5310 strcpy(buf + buf_used, s);
5313 buf[buf_used] = '\0';
5315 FILE *fp = popen(buf, POPEN_RT);
5317 input_stack::push(new file_iterator(fp, symbol(buf).contents(), 1));
5319 error("can't open pipe to process `%1': %2", buf, strerror(errno));
5323 #endif /* not POPEN_MISSING */
5329 static int llx_reg_contents = 0;
5330 static int lly_reg_contents = 0;
5331 static int urx_reg_contents = 0;
5332 static int ury_reg_contents = 0;
5334 struct bounding_box {
5335 int llx, lly, urx, ury;
5338 /* Parse the argument to a %%BoundingBox comment. Return 1 if it
5339 contains 4 numbers, 2 if it contains (atend), 0 otherwise. */
5341 int parse_bounding_box(char *p, bounding_box *bb)
5343 if (sscanf(p, "%d %d %d %d",
5344 &bb->llx, &bb->lly, &bb->urx, &bb->ury) == 4)
5347 /* The Document Structuring Conventions say that the numbers
5348 should be integers. Unfortunately some broken applications
5350 double x1, x2, x3, x4;
5351 if (sscanf(p, "%lf %lf %lf %lf", &x1, &x2, &x3, &x4) == 4) {
5359 for (; *p == ' ' || *p == '\t'; p++)
5361 if (strncmp(p, "(atend)", 7) == 0) {
5366 bb->llx = bb->lly = bb->urx = bb->ury = 0;
5370 // This version is taken from psrm.cc
5372 #define PS_LINE_MAX 255
5373 cset white_space("\n\r \t");
5375 int ps_get_line(char *buf, FILE *fp, const char* filename)
5384 while (c != '\r' && c != '\n' && c != EOF) {
5385 if ((c < 0x1b && !white_space(c)) || c == 0x7f)
5386 error("invalid input character code %1 in `%2'", int(c), filename);
5387 else if (i < PS_LINE_MAX)
5391 error("PostScript file `%1' is non-conforming "
5392 "because length of line exceeds 255", filename);
5400 if (c != EOF && c != '\n')
5406 inline void assign_registers(int llx, int lly, int urx, int ury)
5408 llx_reg_contents = llx;
5409 lly_reg_contents = lly;
5410 urx_reg_contents = urx;
5411 ury_reg_contents = ury;
5414 void do_ps_file(FILE *fp, const char* filename)
5418 char buf[PS_LINE_MAX];
5419 llx_reg_contents = lly_reg_contents =
5420 urx_reg_contents = ury_reg_contents = 0;
5421 if (!ps_get_line(buf, fp, filename)) {
5422 error("`%1' is empty", filename);
5425 if (strncmp("%!PS-Adobe-", buf, 11) != 0) {
5426 error("`%1' is not conforming to the Document Structuring Conventions",
5430 while (ps_get_line(buf, fp, filename) != 0) {
5431 if (buf[0] != '%' || buf[1] != '%'
5432 || strncmp(buf + 2, "EndComments", 11) == 0)
5434 if (strncmp(buf + 2, "BoundingBox:", 12) == 0) {
5435 int res = parse_bounding_box(buf + 14, &bb);
5437 assign_registers(bb.llx, bb.lly, bb.urx, bb.ury);
5440 else if (res == 2) {
5445 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
5454 /* in the trailer, the last BoundingBox comment is significant */
5455 for (offset = 512; !last_try; offset *= 2) {
5456 int had_trailer = 0;
5458 if (offset > 32768 || fseek(fp, -offset, 2) == -1) {
5460 if (fseek(fp, 0L, 0) == -1)
5463 while (ps_get_line(buf, fp, filename) != 0) {
5464 if (buf[0] == '%' && buf[1] == '%') {
5466 if (strncmp(buf + 2, "Trailer", 7) == 0)
5470 if (strncmp(buf + 2, "BoundingBox:", 12) == 0) {
5471 int res = parse_bounding_box(buf + 14, &bb);
5474 else if (res == 2) {
5475 error("`(atend)' not allowed in trailer of `%1'", filename);
5479 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
5488 assign_registers(bb.llx, bb.lly, bb.urx, bb.ury);
5493 error("%%%%BoundingBox comment not found in `%1'", filename);
5496 void ps_bbox_request()
5498 symbol nm = get_long_name(1);
5502 while (!tok.newline() && !tok.eof())
5505 // PS files might contain non-printable characters, such as ^Z
5506 // and CRs not followed by an LF, so open them in binary mode.
5507 FILE *fp = fopen(nm.contents(), FOPEN_RB);
5509 do_ps_file(fp, nm.contents());
5513 error("can't open `%1': %2", nm.contents(), strerror(errno));
5518 const char *asciify(int c)
5521 buf[0] = escape_char == '\0' ? '\\' : escape_char;
5522 buf[1] = buf[2] = '\0';
5524 case ESCAPE_QUESTION:
5527 case ESCAPE_AMPERSAND:
5530 case ESCAPE_RIGHT_PARENTHESIS:
5533 case ESCAPE_UNDERSCORE:
5539 case ESCAPE_CIRCUMFLEX:
5542 case ESCAPE_LEFT_BRACE:
5545 case ESCAPE_RIGHT_BRACE:
5548 case ESCAPE_LEFT_QUOTE:
5551 case ESCAPE_RIGHT_QUOTE:
5569 case ESCAPE_PERCENT:
5581 case COMPATIBLE_SAVE:
5582 case COMPATIBLE_RESTORE:
5586 if (invalid_input_char(c))
5595 const char *input_char_description(int c)
5599 return "a newline character";
5601 return "a backspace character";
5603 return "a leader character";
5605 return "a tab character";
5607 return "a space character";
5611 static char buf[sizeof("magic character code ") + 1 + INT_DIGITS];
5612 if (invalid_input_char(c)) {
5613 const char *s = asciify(c);
5620 sprintf(buf, "magic character code %d", c);
5629 sprintf(buf, "character code %d", c);
5633 // .tm, .tm1, and .tmc
5635 void do_terminal(int newline, int string_like)
5637 if (!tok.newline() && !tok.eof()) {
5641 if (string_like && c == '"') {
5645 if (c != ' ' && c != '\t')
5648 for (; c != '\n' && c != EOF; c = get_copy(0))
5649 fputs(asciify(c), stderr);
5652 fputc('\n', stderr);
5667 void terminal_continue()
5672 dictionary stream_dictionary(20);
5674 void do_open(int append)
5676 symbol stream = get_name(1);
5677 if (!stream.is_null()) {
5678 symbol filename = get_long_name(1);
5679 if (!filename.is_null()) {
5681 FILE *fp = fopen(filename.contents(), append ? "a" : "w");
5683 error("can't open `%1' for %2: %3",
5684 filename.contents(),
5685 append ? "appending" : "writing",
5687 fp = (FILE *)stream_dictionary.remove(stream);
5690 fp = (FILE *)stream_dictionary.lookup(stream, fp);
5701 error(".open request not allowed in safer mode");
5708 void opena_request()
5711 error(".opena request not allowed in safer mode");
5718 void close_request()
5720 symbol stream = get_name(1);
5721 if (!stream.is_null()) {
5722 FILE *fp = (FILE *)stream_dictionary.remove(stream);
5724 error("no stream named `%1'", stream.contents());
5731 // .write and .writec
5733 void do_write_request(int newline)
5735 symbol stream = get_name(1);
5736 if (stream.is_null()) {
5740 FILE *fp = (FILE *)stream_dictionary.lookup(stream);
5742 error("no stream named `%1'", stream.contents());
5747 while ((c = get_copy(0)) == ' ')
5751 for (; c != '\n' && c != EOF; c = get_copy(0))
5752 fputs(asciify(c), fp);
5759 void write_request()
5761 do_write_request(1);
5764 void write_request_continue()
5766 do_write_request(0);
5769 void write_macro_request()
5771 symbol stream = get_name(1);
5772 if (stream.is_null()) {
5776 FILE *fp = (FILE *)stream_dictionary.lookup(stream);
5778 error("no stream named `%1'", stream.contents());
5782 symbol s = get_name(1);
5787 request_or_macro *p = lookup_request(s);
5788 macro *m = p->to_macro();
5790 error("cannot write request");
5792 string_iterator iter(*m);
5794 int c = iter.get(0);
5797 fputs(asciify(c), fp);
5804 void warnscale_request()
5811 warn_scale = (double)units_per_inch;
5813 warn_scale = (double)units_per_inch / 2.54;
5815 warn_scale = (double)units_per_inch / 72.0;
5817 warn_scale = (double)units_per_inch / 6.0;
5820 "invalid scaling indicator `%1', using `i' instead", c);
5823 warn_scaling_indicator = c;
5828 void spreadwarn_request()
5831 if (has_arg() && get_hunits(&n, 'm')) {
5834 hunits em = curenv->get_size();
5835 spread_limit = (double)n.to_units()
5836 / (em.is_zero() ? hresolution : em.to_units());
5839 spread_limit = -spread_limit - 1; // no arg toggles on/off without
5840 // changing value; we mirror at
5841 // -0.5 to make zero a valid value
5845 static void init_charset_table()
5848 strcpy(buf, "char");
5849 for (int i = 0; i < 256; i++) {
5850 strcpy(buf + 4, i_to_a(i));
5851 charset_table[i] = get_charinfo(symbol(buf));
5852 charset_table[i]->set_ascii_code(i);
5854 charset_table[i]->set_hyphenation_code(cmlower(i));
5856 charset_table['.']->set_flags(charinfo::ENDS_SENTENCE);
5857 charset_table['?']->set_flags(charinfo::ENDS_SENTENCE);
5858 charset_table['!']->set_flags(charinfo::ENDS_SENTENCE);
5859 charset_table['-']->set_flags(charinfo::BREAK_AFTER);
5860 charset_table['"']->set_flags(charinfo::TRANSPARENT);
5861 charset_table['\'']->set_flags(charinfo::TRANSPARENT);
5862 charset_table[')']->set_flags(charinfo::TRANSPARENT);
5863 charset_table[']']->set_flags(charinfo::TRANSPARENT);
5864 charset_table['*']->set_flags(charinfo::TRANSPARENT);
5865 get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT);
5866 get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT);
5867 get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER);
5868 get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
5869 get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
5870 get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
5871 get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
5872 get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY);
5873 page_character = charset_table['%'];
5876 static void init_hpf_code_table()
5878 for (int i = 0; i < 256; i++)
5879 hpf_code_table[i] = i;
5882 static void do_translate(int translate_transparent, int translate_input)
5885 while (!tok.newline() && !tok.eof()) {
5887 // This is a really bizarre troff feature.
5889 translate_space_to_dummy = tok.dummy();
5890 if (tok.newline() || tok.eof())
5895 charinfo *ci1 = tok.get_char(1);
5899 if (tok.newline() || tok.eof()) {
5900 ci1->set_special_translation(charinfo::TRANSLATE_SPACE,
5901 translate_transparent);
5905 ci1->set_special_translation(charinfo::TRANSLATE_SPACE,
5906 translate_transparent);
5907 else if (tok.stretchable_space())
5908 ci1->set_special_translation(charinfo::TRANSLATE_STRETCHABLE_SPACE,
5909 translate_transparent);
5910 else if (tok.dummy())
5911 ci1->set_special_translation(charinfo::TRANSLATE_DUMMY,
5912 translate_transparent);
5913 else if (tok.hyphen_indicator())
5914 ci1->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR,
5915 translate_transparent);
5917 charinfo *ci2 = tok.get_char(1);
5921 ci1->set_translation(0, translate_transparent, translate_input);
5923 ci1->set_translation(ci2, translate_transparent, translate_input);
5935 void translate_no_transparent()
5940 void translate_input()
5948 if (get_integer(&flags))
5950 charinfo *ci = tok.get_char(1);
5952 charinfo *tem = ci->get_translation();
5955 ci->set_flags(flags);
5962 void hyphenation_code()
5965 while (!tok.newline() && !tok.eof()) {
5966 charinfo *ci = tok.get_char(1);
5971 unsigned char c = tok.ch();
5973 error("hyphenation code must be ordinary character");
5977 error("hyphenation code cannot be digit");
5980 ci->set_hyphenation_code(c);
5981 if (ci->get_translation()
5982 && ci->get_translation()->get_translation_input())
5983 ci->get_translation()->set_hyphenation_code(c);
5990 void hyphenation_patterns_file_code()
5993 while (!tok.newline() && !tok.eof()) {
5995 if (get_integer(&n1) && (0 <= n1 && n1 <= 255)) {
5997 error("missing output hyphenation code");
6000 if (get_integer(&n2) && (0 <= n2 && n2 <= 255)) {
6001 hpf_code_table[n1] = n2;
6005 error("output hyphenation code must be integer in the range 0..255");
6010 error("input hyphenation code must be integer in the range 0..255");
6017 charinfo *token::get_char(int required)
6019 if (type == TOKEN_CHAR)
6020 return charset_table[c];
6021 if (type == TOKEN_SPECIAL)
6022 return get_charinfo(nm);
6023 if (type == TOKEN_NUMBERED_CHAR)
6024 return get_charinfo_by_number(val);
6025 if (type == TOKEN_ESCAPE) {
6026 if (escape_char != 0)
6027 return charset_table[escape_char];
6029 error("`\\e' used while no current escape character");
6034 if (type == TOKEN_EOF || type == TOKEN_NEWLINE)
6035 warning(WARN_MISSING, "missing normal or special character");
6037 error("normal or special character expected (got %1)", description());
6042 charinfo *get_optional_char()
6046 charinfo *ci = tok.get_char();
6048 check_missing_character();
6054 void check_missing_character()
6056 if (!tok.newline() && !tok.eof() && !tok.right_brace() && !tok.tab())
6057 error("normal or special character expected (got %1): "
6058 "treated as missing",
6064 int token::add_to_node_list(node **pp)
6071 *pp = (*pp)->add_char(charset_table[c], curenv, &w, &s);
6077 if (escape_char != 0)
6078 *pp = (*pp)->add_char(charset_table[escape_char], curenv, &w, &s);
6080 case TOKEN_HYPHEN_INDICATOR:
6081 *pp = (*pp)->add_discretionary_hyphen();
6083 case TOKEN_ITALIC_CORRECTION:
6084 *pp = (*pp)->add_italic_correction(&w);
6086 case TOKEN_LEFT_BRACE:
6088 case TOKEN_MARK_INPUT:
6089 set_number_reg(nm, curenv->get_input_line_position().to_units());
6095 case TOKEN_NUMBERED_CHAR:
6096 *pp = (*pp)->add_char(get_charinfo_by_number(val), curenv, &w, &s);
6098 case TOKEN_RIGHT_BRACE:
6101 n = new hmotion_node(curenv->get_space_width(),
6102 curenv->get_fill_color());
6105 *pp = (*pp)->add_char(get_charinfo(nm), curenv, &w, &s);
6107 case TOKEN_STRETCHABLE_SPACE:
6108 n = new unbreakable_space_node(curenv->get_space_width(),
6109 curenv->get_fill_color());
6111 case TOKEN_UNSTRETCHABLE_SPACE:
6112 n = new space_char_hmotion_node(curenv->get_space_width(),
6113 curenv->get_fill_color());
6115 case TOKEN_TRANSPARENT_DUMMY:
6116 n = new transparent_dummy_node;
6118 case TOKEN_ZERO_WIDTH_BREAK:
6119 n = new space_node(H0, curenv->get_fill_color());
6121 n->is_escape_colon();
6133 void token::process()
6135 if (possibly_handle_first_page_transition())
6138 case TOKEN_BACKSPACE:
6139 curenv->add_node(new hmotion_node(-curenv->get_space_width(),
6140 curenv->get_fill_color()));
6143 curenv->add_char(charset_table[c]);
6146 curenv->add_node(new dummy_node);
6155 if (escape_char != 0)
6156 curenv->add_char(charset_table[escape_char]);
6158 case TOKEN_BEGIN_TRAP:
6159 case TOKEN_END_TRAP:
6160 case TOKEN_PAGE_EJECTOR:
6161 // these are all handled in process_input_stack()
6163 case TOKEN_HYPHEN_INDICATOR:
6164 curenv->add_hyphen_indicator();
6166 case TOKEN_INTERRUPT:
6167 curenv->interrupt();
6169 case TOKEN_ITALIC_CORRECTION:
6170 curenv->add_italic_correction();
6173 curenv->handle_tab(1);
6175 case TOKEN_LEFT_BRACE:
6177 case TOKEN_MARK_INPUT:
6178 set_number_reg(nm, curenv->get_input_line_position().to_units());
6184 curenv->add_node(nd);
6187 case TOKEN_NUMBERED_CHAR:
6188 curenv->add_char(get_charinfo_by_number(val));
6191 // handled in process_input_stack()
6193 case TOKEN_RIGHT_BRACE:
6199 curenv->add_char(get_charinfo(nm));
6204 case TOKEN_STRETCHABLE_SPACE:
6205 curenv->add_node(new unbreakable_space_node(curenv->get_space_width(),
6206 curenv->get_fill_color()));
6208 case TOKEN_UNSTRETCHABLE_SPACE:
6209 curenv->add_node(new space_char_hmotion_node(curenv->get_space_width(),
6210 curenv->get_fill_color()));
6213 curenv->handle_tab(0);
6215 case TOKEN_TRANSPARENT:
6217 case TOKEN_TRANSPARENT_DUMMY:
6218 curenv->add_node(new transparent_dummy_node);
6220 case TOKEN_ZERO_WIDTH_BREAK:
6222 node *tmp = new space_node(H0, curenv->get_fill_color());
6223 tmp->freeze_space();
6224 tmp->is_escape_colon();
6225 curenv->add_node(tmp);
6233 class nargs_reg : public reg {
6235 const char *get_string();
6238 const char *nargs_reg::get_string()
6240 return i_to_a(input_stack::nargs());
6243 class lineno_reg : public reg {
6245 const char *get_string();
6248 const char *lineno_reg::get_string()
6252 if (!input_stack::get_location(0, &file, &line))
6254 return i_to_a(line);
6257 class writable_lineno_reg : public general_reg {
6259 writable_lineno_reg();
6260 void set_value(units);
6261 int get_value(units *);
6264 writable_lineno_reg::writable_lineno_reg()
6268 int writable_lineno_reg::get_value(units *res)
6272 if (!input_stack::get_location(0, &file, &line))
6278 void writable_lineno_reg::set_value(units n)
6280 input_stack::set_location(0, n);
6283 class filename_reg : public reg {
6285 const char *get_string();
6288 const char *filename_reg::get_string()
6292 if (input_stack::get_location(0, &file, &line))
6298 class constant_reg : public reg {
6301 constant_reg(const char *);
6302 const char *get_string();
6305 constant_reg::constant_reg(const char *p) : s(p)
6309 const char *constant_reg::get_string()
6314 constant_int_reg::constant_int_reg(int *q) : p(q)
6318 const char *constant_int_reg::get_string()
6323 void abort_request()
6328 else if (tok.newline())
6331 while ((c = get_copy(0)) == ' ')
6334 if (c == EOF || c == '\n')
6335 fputs("User Abort.", stderr);
6337 for (; c != '\n' && c != EOF; c = get_copy(0))
6338 fputs(asciify(c), stderr);
6340 fputc('\n', stderr);
6341 cleanup_and_exit(1);
6347 char *s = new char[len];
6349 while ((c = get_copy(0)) == ' ')
6352 while (c != '\n' && c != EOF) {
6353 if (!invalid_input_char(c)) {
6356 s = new char[len*2];
6357 memcpy(s, tem, len);
6377 error(".pi request not allowed in safer mode");
6381 #ifdef POPEN_MISSING
6382 error("pipes not available on this system");
6384 #else /* not POPEN_MISSING */
6386 error("can't pipe: output already started");
6391 if ((pc = read_string()) == 0)
6392 error("can't pipe to empty command");
6394 char *s = new char[strlen(pipe_command) + strlen(pc) + 1 + 1];
6395 strcpy(s, pipe_command);
6398 a_delete pipe_command;
6405 #endif /* not POPEN_MISSING */
6409 static int system_status;
6411 void system_request()
6414 error(".sy request not allowed in safer mode");
6418 char *command = read_string();
6420 error("empty command");
6422 system_status = system(command);
6430 if (curdiv == topdiv && topdiv->before_first_page) {
6431 handle_initial_request(COPY_FILE_REQUEST);
6434 symbol filename = get_long_name(1);
6435 while (!tok.newline() && !tok.eof())
6439 if (!filename.is_null())
6440 curdiv->copy_file(filename.contents());
6448 if (curdiv == topdiv && topdiv->before_first_page) {
6449 handle_initial_request(VJUSTIFY_REQUEST);
6452 symbol type = get_long_name(1);
6453 if (!type.is_null())
6454 curdiv->vjustify(type);
6460 void transparent_file()
6462 if (curdiv == topdiv && topdiv->before_first_page) {
6463 handle_initial_request(TRANSPARENT_FILE_REQUEST);
6466 symbol filename = get_long_name(1);
6467 while (!tok.newline() && !tok.eof())
6471 if (!filename.is_null()) {
6473 FILE *fp = fopen(filename.contents(), "r");
6475 error("can't open `%1': %2", filename.contents(), strerror(errno));
6482 if (invalid_input_char(c))
6483 warning(WARN_INPUT, "invalid input character code %1", int(c));
6485 curdiv->transparent_output(c);
6490 curdiv->transparent_output('\n');
6502 page_range(int, int, page_range *);
6503 int contains(int n);
6506 page_range::page_range(int i, int j, page_range *p)
6507 : first(i), last(j), next(p)
6511 int page_range::contains(int n)
6513 return n >= first && (last <= 0 || n <= last);
6516 page_range *output_page_list = 0;
6518 int in_output_page_list(int n)
6520 if (!output_page_list)
6522 for (page_range *p = output_page_list; p; p = p->next)
6528 static void parse_output_page_list(char *p)
6534 else if (csdigit(*p)) {
6537 i = i*10 + *p++ - '0';
6538 while (csdigit(*p));
6548 j = j*10 + *p++ - '0';
6549 while (csdigit(*p));
6555 last_page_number = -1;
6556 else if (last_page_number >= 0 && j > last_page_number)
6557 last_page_number = j;
6558 output_page_list = new page_range(i, j, output_page_list);
6564 error("bad output page list");
6565 output_page_list = 0;
6569 static FILE *open_mac_file(const char *mac, char **path)
6571 // Try first FOOBAR.tmac, then tmac.FOOBAR
6572 char *s1 = new char[strlen(mac)+strlen(MACRO_POSTFIX)+1];
6574 strcat(s1, MACRO_POSTFIX);
6575 FILE *fp = mac_path->open_file(s1, path);
6578 char *s2 = new char[strlen(mac)+strlen(MACRO_PREFIX)+1];
6579 strcpy(s2, MACRO_PREFIX);
6581 fp = mac_path->open_file(s2, path);
6587 static void process_macro_file(const char *mac)
6590 FILE *fp = open_mac_file(mac, &path);
6592 fatal("can't find macro file %1", mac);
6593 const char *s = symbol(path).contents();
6595 input_stack::push(new file_iterator(fp, s));
6597 process_input_stack();
6600 static void process_startup_file(char *filename)
6603 search_path *orig_mac_path = mac_path;
6604 mac_path = &config_macro_path;
6605 FILE *fp = mac_path->open_file(filename, &path);
6607 input_stack::push(new file_iterator(fp, symbol(path).contents()));
6610 process_input_stack();
6612 mac_path = orig_mac_path;
6617 symbol nm = get_long_name(1);
6621 while (!tok.newline() && !tok.eof())
6624 FILE *fp = mac_path->open_file(nm.contents(), &path);
6625 // .mso doesn't (and cannot) go through open_mac_file, so we
6626 // need to do it here manually: If we have tmac.FOOBAR, try
6627 // FOOBAR.tmac and vice versa
6629 const char *fn = nm.contents();
6630 if (strncasecmp(fn, MACRO_PREFIX, sizeof(MACRO_PREFIX) - 1) == 0) {
6631 char *s = new char[strlen(fn) + sizeof(MACRO_POSTFIX)];
6632 strcpy(s, fn + sizeof(MACRO_PREFIX) - 1);
6633 strcat(s, MACRO_POSTFIX);
6634 fp = mac_path->open_file(s, &path);
6638 if (strncasecmp(fn + strlen(fn) - sizeof(MACRO_POSTFIX) + 1,
6639 MACRO_POSTFIX, sizeof(MACRO_POSTFIX) - 1) == 0) {
6640 char *s = new char[strlen(fn) + sizeof(MACRO_PREFIX)];
6641 strcpy(s, MACRO_PREFIX);
6642 strncat(s, fn, strlen(fn) - sizeof(MACRO_POSTFIX) + 1);
6643 fp = mac_path->open_file(s, &path);
6649 input_stack::push(new file_iterator(fp, symbol(path).contents()));
6653 error("can't find macro file `%1'", nm.contents());
6658 static void process_input_file(const char *name)
6661 if (strcmp(name, "-") == 0) {
6667 fp = fopen(name, "r");
6669 fatal("can't open `%1': %2", name, strerror(errno));
6671 input_stack::push(new file_iterator(fp, name));
6673 process_input_stack();
6676 // make sure the_input is empty before calling this
6678 static int evaluate_expression(const char *expr, units *res)
6680 input_stack::push(make_temp_iterator(expr));
6682 int success = get_number(res, 'u');
6683 while (input_stack::get(0) != EOF)
6688 static void do_register_assignment(const char *s)
6690 const char *p = strchr(s, '=');
6696 if (evaluate_expression(s + 1, &n))
6697 set_number_reg(buf, n);
6700 char *buf = new char[p - s + 1];
6701 memcpy(buf, s, p - s);
6704 if (evaluate_expression(p + 1, &n))
6705 set_number_reg(buf, n);
6710 static void set_string(const char *name, const char *value)
6712 macro *m = new macro;
6713 for (const char *p = value; *p; p++)
6714 if (!invalid_input_char((unsigned char)*p))
6716 request_dictionary.define(name, m);
6719 static void do_string_assignment(const char *s)
6721 const char *p = strchr(s, '=');
6726 set_string(buf, s + 1);
6729 char *buf = new char[p - s + 1];
6730 memcpy(buf, s, p - s);
6732 set_string(buf, p + 1);
6737 struct string_list {
6740 string_list(const char *ss) : s(ss), next(0) {}
6744 static void prepend_string(const char *s, string_list **p)
6746 string_list *l = new string_list(s);
6752 static void add_string(const char *s, string_list **p)
6756 *p = new string_list(s);
6759 void usage(FILE *stream, const char *prog)
6762 "usage: %s -abcivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n"
6763 " -rcn -Tname -Fdir -Mdir [files...]\n",
6767 int main(int argc, char **argv)
6769 program_name = argv[0];
6770 static char stderr_buf[BUFSIZ];
6771 setbuf(stderr, stderr_buf);
6773 string_list *macros = 0;
6774 string_list *register_assignments = 0;
6775 string_list *string_assignments = 0;
6780 int no_rc = 0; // don't process troffrc and troffrc-end
6781 int next_page_number;
6783 hresolution = vresolution = 1;
6784 // restore $PATH if called from groff
6785 char* groff_path = getenv("GROFF_PATH__");
6792 if (putenv(strsave(e.contents())))
6793 fatal("putenv failed");
6795 static const struct option long_options[] = {
6796 { "help", no_argument, 0, CHAR_MAX + 1 },
6797 { "version", no_argument, 0, 'v' },
6800 while ((c = getopt_long(argc, argv, "abcivw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU",
6806 printf("GNU troff (groff) version %s\n", Version_string);
6813 is_html = (strcmp(device, "html") == 0);
6816 compatible_flag = 1;
6822 macro_path.command_line_dir(optarg);
6823 safer_macro_path.command_line_dir(optarg);
6824 config_macro_path.command_line_dir(optarg);
6827 font::command_line_font_dir(optarg);
6830 add_string(optarg, ¯os);
6839 enable_warning(optarg);
6842 disable_warning(optarg);
6851 ascii_output_flag = 1;
6854 suppress_output_flag = 1;
6857 if (sscanf(optarg, "%d", &next_page_number) == 1)
6860 error("bad page number");
6863 parse_output_page_list(optarg);
6866 if (*optarg == '\0')
6867 error("`-d' requires non-empty argument");
6869 add_string(optarg, &string_assignments);
6872 if (*optarg == '\0')
6873 error("`-r' requires non-empty argument");
6875 add_string(optarg, ®ister_assignments);
6878 default_family = symbol(optarg);
6884 // silently ignore these
6887 safer_flag = 0; // unsafe behaviour
6889 case CHAR_MAX + 1: // --help
6890 usage(stdout, argv[0]);
6894 usage(stderr, argv[0]);
6896 break; // never reached
6901 mac_path = ¯o_path;
6902 set_string(".T", device);
6903 init_charset_table();
6904 init_hpf_code_table();
6905 if (!font::load_desc())
6906 fatal("sorry, I can't continue");
6907 units_per_inch = font::res;
6908 hresolution = font::hor;
6909 vresolution = font::vert;
6910 sizescale = font::sizescale;
6911 tcommand_flag = font::tcommand;
6912 warn_scale = (double)units_per_inch;
6913 warn_scaling_indicator = 'i';
6914 if (!fflag && font::family != 0 && *font::family != '\0')
6915 default_family = symbol(font::family);
6916 font_size::init_size_table(font::sizes);
6919 if (font::style_table) {
6920 for (i = 0; font::style_table[i]; i++)
6921 mount_style(j++, symbol(font::style_table[i]));
6923 for (i = 0; font::font_name_table[i]; i++, j++)
6924 // In the DESC file a font name of 0 (zero) means leave this
6926 if (strcmp(font::font_name_table[i], "0") != 0)
6927 mount_font(j, symbol(font::font_name_table[i]));
6928 curdiv = topdiv = new top_level_diversion;
6930 topdiv->set_next_page_number(next_page_number);
6931 init_input_requests();
6932 init_env_requests();
6933 init_div_requests();
6935 init_column_requests();
6937 init_node_requests();
6938 number_reg_dictionary.define(".T", new constant_reg(tflag ? "1" : "0"));
6940 init_reg_requests();
6941 init_hyphen_requests();
6942 init_environments();
6943 while (string_assignments) {
6944 do_string_assignment(string_assignments->s);
6945 string_list *tem = string_assignments;
6946 string_assignments = string_assignments->next;
6949 while (register_assignments) {
6950 do_register_assignment(register_assignments->s);
6951 string_list *tem = register_assignments;
6952 register_assignments = register_assignments->next;
6956 process_startup_file(INITIAL_STARTUP_FILE);
6958 process_macro_file(macros->s);
6959 string_list *tem = macros;
6960 macros = macros->next;
6964 process_startup_file(FINAL_STARTUP_FILE);
6965 for (i = optind; i < argc; i++)
6966 process_input_file(argv[i]);
6967 if (optind >= argc || iflag)
6968 process_input_file("-");
6970 return 0; // not reached
6976 if (has_arg() && get_integer(&n)) {
6977 if (n & ~WARN_TOTAL) {
6978 warning(WARN_RANGE, "warning mask must be between 0 and %1", WARN_TOTAL);
6984 warning_mask = WARN_TOTAL;
6988 static void init_registers()
6990 #ifdef LONG_FOR_TIME_T
6992 #else /* not LONG_FOR_TIME_T */
6994 #endif /* not LONG_FOR_TIME_T */
6996 // Use struct here to work around misfeature in old versions of g++.
6997 struct tm *tt = localtime(&t);
6998 set_number_reg("seconds", int(tt->tm_sec));
6999 set_number_reg("minutes", int(tt->tm_min));
7000 set_number_reg("hours", int(tt->tm_hour));
7001 set_number_reg("dw", int(tt->tm_wday + 1));
7002 set_number_reg("dy", int(tt->tm_mday));
7003 set_number_reg("mo", int(tt->tm_mon + 1));
7004 set_number_reg("year", int(1900 + tt->tm_year));
7005 set_number_reg("yr", int(tt->tm_year));
7006 set_number_reg("$$", getpid());
7007 number_reg_dictionary.define(".A",
7008 new constant_reg(ascii_output_flag
7014 * registers associated with \O
7017 static int output_reg_minx_contents = -1;
7018 static int output_reg_miny_contents = -1;
7019 static int output_reg_maxx_contents = -1;
7020 static int output_reg_maxy_contents = -1;
7022 void check_output_limits(int x, int y)
7024 if ((output_reg_minx_contents == -1) || (x < output_reg_minx_contents))
7025 output_reg_minx_contents = x;
7026 if (x > output_reg_maxx_contents)
7027 output_reg_maxx_contents = x;
7028 if ((output_reg_miny_contents == -1) || (y < output_reg_miny_contents))
7029 output_reg_miny_contents = y;
7030 if (y > output_reg_maxy_contents)
7031 output_reg_maxy_contents = y;
7034 void reset_output_registers(int miny)
7036 // fprintf(stderr, "reset_output_registers\n");
7037 output_reg_minx_contents = -1;
7038 output_reg_miny_contents = -1;
7039 output_reg_maxx_contents = -1;
7040 output_reg_maxy_contents = -1;
7043 void get_output_registers(int *minx, int *miny, int *maxx, int *maxy)
7045 *minx = output_reg_minx_contents;
7046 *miny = output_reg_miny_contents;
7047 *maxx = output_reg_maxx_contents;
7048 *maxy = output_reg_maxy_contents;
7051 void init_input_requests()
7053 init_request("ab", abort_request);
7054 init_request("als", alias_macro);
7055 init_request("am", append_macro);
7056 init_request("am1", append_nocomp_macro);
7057 init_request("ami", append_indirect_macro);
7058 init_request("as", append_string);
7059 init_request("as1", append_nocomp_string);
7060 init_request("asciify", asciify_macro);
7061 init_request("backtrace", backtrace_request);
7062 init_request("blm", blank_line_macro);
7063 init_request("break", while_break_request);
7064 init_request("cf", copy_file);
7065 init_request("cflags", char_flags);
7066 init_request("char", define_character);
7067 init_request("chop", chop_macro);
7068 init_request("close", close_request);
7069 init_request("color", activate_color);
7070 init_request("continue", while_continue_request);
7071 init_request("cp", compatible);
7072 init_request("de", define_macro);
7073 init_request("de1", define_nocomp_macro);
7074 init_request("defcolor", define_color);
7075 init_request("dei", define_indirect_macro);
7076 init_request("do", do_request);
7077 init_request("ds", define_string);
7078 init_request("ds1", define_nocomp_string);
7079 init_request("ec", set_escape_char);
7080 init_request("ecr", restore_escape_char);
7081 init_request("ecs", save_escape_char);
7082 init_request("el", else_request);
7083 init_request("em", end_macro);
7084 init_request("eo", escape_off);
7085 init_request("ex", exit_request);
7086 init_request("fchar", define_fallback_character);
7087 #ifdef WIDOW_CONTROL
7088 init_request("fpl", flush_pending_lines);
7089 #endif /* WIDOW_CONTROL */
7090 init_request("hcode", hyphenation_code);
7091 init_request("hpfcode", hyphenation_patterns_file_code);
7092 init_request("ie", if_else_request);
7093 init_request("if", if_request);
7094 init_request("ig", ignore);
7095 init_request("length", length_request);
7096 init_request("lf", line_file);
7097 init_request("mso", macro_source);
7098 init_request("nop", nop_request);
7099 init_request("nx", next_file);
7100 init_request("open", open_request);
7101 init_request("opena", opena_request);
7102 init_request("output", output_request);
7103 init_request("pc", set_page_character);
7104 init_request("pi", pipe_output);
7105 init_request("pm", print_macros);
7106 init_request("psbb", ps_bbox_request);
7107 #ifndef POPEN_MISSING
7108 init_request("pso", pipe_source);
7109 #endif /* not POPEN_MISSING */
7110 init_request("rchar", remove_character);
7111 init_request("rd", read_request);
7112 init_request("return", return_macro_request);
7113 init_request("rm", remove_macro);
7114 init_request("rn", rename_macro);
7115 init_request("shift", shift);
7116 init_request("so", source);
7117 init_request("spreadwarn", spreadwarn_request);
7118 init_request("substring", substring_request);
7119 init_request("sy", system_request);
7120 init_request("tm", terminal);
7121 init_request("tm1", terminal1);
7122 init_request("tmc", terminal_continue);
7123 init_request("tr", translate);
7124 init_request("trf", transparent_file);
7125 init_request("trin", translate_input);
7126 init_request("trnt", translate_no_transparent);
7127 init_request("unformat", unformat_macro);
7128 init_request("warn", warn_request);
7129 init_request("while", while_request);
7130 init_request("write", write_request);
7131 init_request("writec", write_request_continue);
7132 init_request("writem", write_macro_request);
7133 init_request("nroff", nroff_request);
7134 init_request("troff", troff_request);
7136 init_request("vj", vjustify);
7138 init_request("warnscale", warnscale_request);
7139 number_reg_dictionary.define(".$", new nargs_reg);
7140 number_reg_dictionary.define(".C", new constant_int_reg(&compatible_flag));
7141 number_reg_dictionary.define(".F", new filename_reg);
7142 number_reg_dictionary.define(".H", new constant_int_reg(&hresolution));
7143 number_reg_dictionary.define(".R", new constant_reg("10000"));
7144 number_reg_dictionary.define(".V", new constant_int_reg(&vresolution));
7145 extern const char *revision;
7146 number_reg_dictionary.define(".Y", new constant_reg(revision));
7147 number_reg_dictionary.define(".c", new lineno_reg);
7148 number_reg_dictionary.define(".g", new constant_reg("1"));
7149 number_reg_dictionary.define(".color", new constant_int_reg(&color_flag));
7150 number_reg_dictionary.define(".warn", new constant_int_reg(&warning_mask));
7151 extern const char *major_version;
7152 number_reg_dictionary.define(".x", new constant_reg(major_version));
7153 extern const char *minor_version;
7154 number_reg_dictionary.define(".y", new constant_reg(minor_version));
7155 number_reg_dictionary.define("c.", new writable_lineno_reg);
7156 number_reg_dictionary.define("llx", new variable_reg(&llx_reg_contents));
7157 number_reg_dictionary.define("lly", new variable_reg(&lly_reg_contents));
7158 number_reg_dictionary.define("opmaxx",
7159 new variable_reg(&output_reg_maxx_contents));
7160 number_reg_dictionary.define("opmaxy",
7161 new variable_reg(&output_reg_maxy_contents));
7162 number_reg_dictionary.define("opminx",
7163 new variable_reg(&output_reg_minx_contents));
7164 number_reg_dictionary.define("opminy",
7165 new variable_reg(&output_reg_miny_contents));
7166 number_reg_dictionary.define("slimit",
7167 new variable_reg(&input_stack::limit));
7168 number_reg_dictionary.define("systat", new variable_reg(&system_status));
7169 number_reg_dictionary.define("urx", new variable_reg(&urx_reg_contents));
7170 number_reg_dictionary.define("ury", new variable_reg(&ury_reg_contents));
7173 object_dictionary request_dictionary(501);
7175 void init_request(const char *s, REQUEST_FUNCP f)
7177 request_dictionary.define(s, new request(f));
7180 static request_or_macro *lookup_request(symbol nm)
7182 assert(!nm.is_null());
7183 request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm);
7185 warning(WARN_MAC, "`%1' not defined", nm.contents());
7187 request_dictionary.define(nm, p);
7192 node *charinfo_to_node_list(charinfo *ci, const environment *envp)
7194 // Don't interpret character definitions in compatible mode.
7195 int old_compatible_flag = compatible_flag;
7196 compatible_flag = 0;
7197 int old_escape_char = escape_char;
7199 macro *mac = ci->set_macro(0);
7201 environment *oldenv = curenv;
7202 environment env(envp);
7204 curenv->set_composite();
7205 token old_tok = tok;
7206 input_stack::add_boundary();
7207 string_iterator *si =
7208 new string_iterator(*mac, "composite character", ci->nm);
7209 input_stack::push(si);
7210 // we don't use process_input_stack, because we don't want to recognise
7216 if (tok.newline()) {
7217 error("composite character mustn't contain newline");
7225 node *n = curenv->extract_output_line();
7226 input_stack::remove_boundary();
7230 compatible_flag = old_compatible_flag;
7231 escape_char = old_escape_char;
7235 static node *read_draw_node()
7239 if (!start.delimiter(1)){
7242 } while (tok != start && !tok.newline() && !tok.eof());
7247 error("missing argument");
7249 unsigned char type = tok.ch();
7252 hvpair *point = new hvpair[maxpoints];
7257 for (i = 0; tok != start; i++) {
7258 if (i == maxpoints) {
7259 hvpair *oldpoint = point;
7260 point = new hvpair[maxpoints*2];
7261 for (int j = 0; j < maxpoints; j++)
7262 point[j] = oldpoint[j];
7266 if (!get_hunits(&point[i].h,
7267 type == 'f' || type == 't' ? 'u' : 'm')) {
7278 if (!get_vunits(&point[i].v, 'v')) {
7284 while (tok != start && !tok.newline() && !tok.eof())
7289 if (npoints != 1 || no_last_v) {
7290 error("two arguments needed for line");
7295 if (npoints != 1 || !no_last_v) {
7296 error("one argument needed for circle");
7302 if (npoints != 1 || no_last_v) {
7303 error("two arguments needed for ellipse");
7308 if (npoints != 2 || no_last_v) {
7309 error("four arguments needed for arc");
7315 error("even number of arguments needed for spline");
7318 if (npoints != 1 || !no_last_v) {
7319 error("one argument needed for gray shade");
7324 // silently pass it through
7327 draw_node *dn = new draw_node(type, point, npoints,
7328 curenv->get_font_size(),
7329 curenv->get_glyph_color(),
7330 curenv->get_fill_color());
7345 } warning_table[] = {
7346 { "char", WARN_CHAR },
7347 { "range", WARN_RANGE },
7348 { "break", WARN_BREAK },
7349 { "delim", WARN_DELIM },
7351 { "scale", WARN_SCALE },
7352 { "number", WARN_NUMBER },
7353 { "syntax", WARN_SYNTAX },
7354 { "tab", WARN_TAB },
7355 { "right-brace", WARN_RIGHT_BRACE },
7356 { "missing", WARN_MISSING },
7357 { "input", WARN_INPUT },
7358 { "escape", WARN_ESCAPE },
7359 { "space", WARN_SPACE },
7360 { "font", WARN_FONT },
7362 { "mac", WARN_MAC },
7363 { "reg", WARN_REG },
7365 { "color", WARN_COLOR },
7366 { "all", WARN_TOTAL & ~(WARN_DI | WARN_MAC | WARN_REG) },
7367 { "w", WARN_TOTAL },
7368 { "default", DEFAULT_WARNING_MASK },
7371 static int lookup_warning(const char *name)
7373 for (unsigned int i = 0;
7374 i < sizeof(warning_table)/sizeof(warning_table[0]);
7376 if (strcmp(name, warning_table[i].name) == 0)
7377 return warning_table[i].mask;
7381 static void enable_warning(const char *name)
7383 int mask = lookup_warning(name);
7385 warning_mask |= mask;
7387 error("unknown warning `%1'", name);
7390 static void disable_warning(const char *name)
7392 int mask = lookup_warning(name);
7394 warning_mask &= ~mask;
7396 error("unknown warning `%1'", name);
7399 static void copy_mode_error(const char *format,
7405 static const char prefix[] = "(in ignored input) ";
7406 char *s = new char[sizeof(prefix) + strlen(format)];
7409 warning(WARN_IG, s, arg1, arg2, arg3);
7413 error(format, arg1, arg2, arg3);
7416 enum error_type { WARNING, OUTPUT_WARNING, ERROR, FATAL };
7418 static void do_error(error_type type,
7424 const char *filename;
7426 if (inhibit_errors && type < FATAL)
7429 input_stack::backtrace();
7430 if (!get_file_line(&filename, &lineno))
7433 errprint("%1:%2: ", filename, lineno);
7434 else if (program_name)
7435 fprintf(stderr, "%s: ", program_name);
7438 fputs("fatal error: ", stderr);
7443 fputs("warning: ", stderr);
7445 case OUTPUT_WARNING:
7446 double fromtop = topdiv->get_vertical_position().to_units() / warn_scale;
7447 fprintf(stderr, "warning [p %d, %.1f%c",
7448 topdiv->get_page_number(), fromtop, warn_scaling_indicator);
7449 if (topdiv != curdiv) {
7450 double fromtop1 = curdiv->get_vertical_position().to_units()
7452 fprintf(stderr, ", div `%s', %.1f%c",
7453 curdiv->get_diversion_name(), fromtop1, warn_scaling_indicator);
7455 fprintf(stderr, "]: ");
7458 errprint(format, arg1, arg2, arg3);
7459 fputc('\n', stderr);
7462 cleanup_and_exit(1);
7465 int warning(warning_type t,
7471 if ((t & warning_mask) != 0) {
7472 do_error(WARNING, format, arg1, arg2, arg3);
7479 int output_warning(warning_type t,
7485 if ((t & warning_mask) != 0) {
7486 do_error(OUTPUT_WARNING, format, arg1, arg2, arg3);
7493 void error(const char *format,
7498 do_error(ERROR, format, arg1, arg2, arg3);
7501 void fatal(const char *format,
7506 do_error(FATAL, format, arg1, arg2, arg3);
7509 void fatal_with_file_and_line(const char *filename, int lineno,
7515 fprintf(stderr, "%s:%d: fatal error: ", filename, lineno);
7516 errprint(format, arg1, arg2, arg3);
7517 fputc('\n', stderr);
7519 cleanup_and_exit(1);
7522 void error_with_file_and_line(const char *filename, int lineno,
7528 fprintf(stderr, "%s:%d: error: ", filename, lineno);
7529 errprint(format, arg1, arg2, arg3);
7530 fputc('\n', stderr);
7534 dictionary charinfo_dictionary(501);
7536 charinfo *get_charinfo(symbol nm)
7538 void *p = charinfo_dictionary.lookup(nm);
7540 return (charinfo *)p;
7541 charinfo *cp = new charinfo(nm);
7542 (void)charinfo_dictionary.lookup(nm, cp);
7546 int charinfo::next_index = 0;
7548 charinfo::charinfo(symbol s)
7549 : translation(0), mac(0), special_translation(TRANSLATE_NONE),
7550 hyphenation_code(0), flags(0), ascii_code(0), asciify_code(0),
7551 not_found(0), transparent_translate(1), translate_input(0),
7554 index = next_index++;
7557 void charinfo::set_hyphenation_code(unsigned char c)
7559 hyphenation_code = c;
7562 void charinfo::set_translation(charinfo *ci, int tt, int ti)
7566 if (hyphenation_code != 0)
7567 ci->set_hyphenation_code(hyphenation_code);
7568 if (asciify_code != 0)
7569 ci->set_asciify_code(asciify_code);
7570 else if (ascii_code != 0)
7571 ci->set_asciify_code(ascii_code);
7572 ci->set_translation_input();
7574 special_translation = TRANSLATE_NONE;
7575 transparent_translate = tt;
7578 void charinfo::set_special_translation(int c, int tt)
7580 special_translation = c;
7582 transparent_translate = tt;
7585 void charinfo::set_ascii_code(unsigned char c)
7590 void charinfo::set_asciify_code(unsigned char c)
7595 macro *charinfo::set_macro(macro *m, int f)
7603 void charinfo::set_number(int n)
7609 int charinfo::get_number()
7611 assert(flags & NUMBERED);
7615 symbol UNNAMED_SYMBOL("---");
7617 // For numbered characters not between 0 and 255, we make a symbol out
7618 // of the number and store them in this dictionary.
7620 dictionary numbered_charinfo_dictionary(11);
7622 charinfo *get_charinfo_by_number(int n)
7624 static charinfo *number_table[256];
7626 if (n >= 0 && n < 256) {
7627 charinfo *ci = number_table[n];
7629 ci = new charinfo(UNNAMED_SYMBOL);
7631 number_table[n] = ci;
7636 symbol ns(i_to_a(n));
7637 charinfo *ci = (charinfo *)numbered_charinfo_dictionary.lookup(ns);
7639 ci = new charinfo(UNNAMED_SYMBOL);
7641 numbered_charinfo_dictionary.lookup(ns, ci);
7647 int font::name_to_index(const char *nm)
7651 ci = charset_table[nm[0] & 0xff];
7652 else if (nm[0] == '\\' && nm[2] == 0)
7653 ci = get_charinfo(symbol(nm + 1));
7655 ci = get_charinfo(symbol(nm));
7659 return ci->get_index();
7662 int font::number_to_index(int n)
7664 return get_charinfo_by_number(n)->get_index();