2 /* Copyright (C) 1989-2014 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
23 #include "dictionary.h"
25 #include "stringclass.h"
35 #include "macropath.h"
40 // Needed for getpid() and isatty()
45 #ifdef NEED_DECLARATION_PUTENV
47 int putenv(const char *);
49 #endif /* NEED_DECLARATION_PUTENV */
51 #define MACRO_PREFIX "tmac."
52 #define MACRO_POSTFIX ".tmac"
53 #define INITIAL_STARTUP_FILE "troffrc"
54 #define FINAL_STARTUP_FILE "troffrc-end"
55 #define DEFAULT_INPUT_STACK_LIMIT 1000
57 #ifndef DEFAULT_WARNING_MASK
58 // warnings that are enabled by default
59 #define DEFAULT_WARNING_MASK \
60 (WARN_CHAR|WARN_NUMBER|WARN_BREAK|WARN_SPACE|WARN_FONT|WARN_FILE)
63 // initial size of buffer for reading names; expanded as necessary
66 extern "C" const char *program_name;
67 extern "C" const char *Version_string;
70 void init_column_requests();
73 static node *read_draw_node();
74 static void read_color_draw_node(token &);
75 static void push_token(const token &);
80 void transparent_file();
85 int color_flag = 1; // colors are on by default
86 static int backtrace_flag = 0;
88 char *pipe_command = 0;
90 charinfo *charset_table[256];
91 unsigned char hpf_code_table[256];
93 static int warning_mask = DEFAULT_WARNING_MASK;
94 static int inhibit_errors = 0;
95 static int ignoring = 0;
97 static void enable_warning(const char *);
98 static void disable_warning(const char *);
100 static int escape_char = '\\';
101 static symbol end_macro_name;
102 static symbol blank_line_macro_name;
103 static symbol leading_spaces_macro_name;
104 static int compatible_flag = 0;
105 int ascii_output_flag = 0;
106 int suppress_output_flag = 0;
108 int begin_level = 0; // number of nested \O escapes
110 int have_input = 0; // whether \f, \F, \D'F...', \H, \m, \M,
111 // \O[345], \R, \s, or \S has been processed
113 int old_have_input = 0; // value of have_input right before \n
114 int tcommand_flag = 0;
115 int unsafe_flag = 0; // safer by default
117 int have_string_arg = 0; // whether we have \*[foo bar...]
119 double spread_limit = -3.0 - 1.0; // negative means deactivated
122 char warn_scaling_indicator;
123 int debug_state = 0; // turns on debugging of the html troff state
125 search_path *mac_path = &safer_macro_path;
127 // Defaults to the current directory.
128 search_path include_search_path(0, 0, 0, 1);
130 static int get_copy(node**, int = 0, int = 0);
131 static void copy_mode_error(const char *,
132 const errarg & = empty_errarg,
133 const errarg & = empty_errarg,
134 const errarg & = empty_errarg);
136 enum read_mode { ALLOW_EMPTY, WITH_ARGS, NO_ARGS };
137 static symbol read_escape_name(read_mode = NO_ARGS);
138 static symbol read_long_escape_name(read_mode = NO_ARGS);
139 static void interpolate_string(symbol);
140 static void interpolate_string_with_args(symbol);
141 static void interpolate_macro(symbol, int = 0);
142 static void interpolate_number_format(symbol);
143 static void interpolate_environment_variable(symbol);
145 static symbol composite_glyph_name(symbol);
146 static void interpolate_arg(symbol);
147 static request_or_macro *lookup_request(symbol);
148 static int get_delim_number(units *, unsigned char);
149 static int get_delim_number(units *, unsigned char, units);
150 static symbol do_get_long_name(int, char);
151 static int get_line_arg(units *res, unsigned char si, charinfo **cp);
152 static int read_size(int *);
153 static symbol get_delim_name();
154 static void init_registers();
155 static void trapping_blank_line();
157 // this is for gcc 2.95 with old versions of libstdc++
158 #define input_iterator my_input_iterator
160 class input_iterator;
161 input_iterator *make_temp_iterator(const char *);
162 const char *input_char_description(int);
164 void process_input_stack();
165 void chop_macro(); // declare to avoid friend name injection
168 void set_escape_char()
172 error("bad escape character");
176 escape_char = tok.ch();
189 static int saved_escape_char = '\\';
191 void save_escape_char()
193 saved_escape_char = escape_char;
197 void restore_escape_char()
199 escape_char = saved_escape_char;
205 class input_iterator {
208 input_iterator(int is_div);
209 virtual ~input_iterator() {}
211 friend class input_stack;
213 statem *diversion_state;
215 const unsigned char *ptr;
216 const unsigned char *eptr;
217 input_iterator *next;
219 virtual int fill(node **);
221 virtual int has_args() { return 0; }
222 virtual int nargs() { return 0; }
223 virtual input_iterator *get_arg(int) { return 0; }
224 virtual arg_list *get_arg_list() { return 0; }
225 virtual symbol get_macro_name() { return NULL_SYMBOL; }
226 virtual int space_follows_arg(int) { return 0; }
227 virtual int get_break_flag() { return 0; }
228 virtual int get_location(int, const char **, int *) { return 0; }
229 virtual void backtrace() {}
230 virtual int set_location(const char *, int) { return 0; }
231 virtual int next_file(FILE *, const char *) { return 0; }
232 virtual void shift(int) {}
233 virtual int is_boundary() {return 0; }
234 virtual int is_file() { return 0; }
235 virtual int is_macro() { return 0; }
236 virtual void save_compatible_flag(int) {}
237 virtual int get_compatible_flag() { return 0; }
240 input_iterator::input_iterator()
241 : is_diversion(0), ptr(0), eptr(0)
245 input_iterator::input_iterator(int is_div)
246 : is_diversion(is_div), ptr(0), eptr(0)
250 int input_iterator::fill(node **)
255 int input_iterator::peek()
260 inline int input_iterator::get(node **p)
262 return ptr < eptr ? *ptr++ : fill(p);
265 class input_boundary : public input_iterator {
267 int is_boundary() { return 1; }
270 class input_return_boundary : public input_iterator {
272 int is_boundary() { return 2; }
275 class file_iterator : public input_iterator {
278 const char *filename;
282 enum { BUF_SIZE = 512 };
283 unsigned char buf[BUF_SIZE];
286 file_iterator(FILE *, const char *, int = 0);
290 int get_location(int, const char **, int *);
292 int set_location(const char *, int);
293 int next_file(FILE *, const char *);
297 file_iterator::file_iterator(FILE *f, const char *fn, int po)
298 : fp(f), lineno(1), filename(fn), popened(po),
299 newline_flag(0), seen_escape(0)
301 if ((font::use_charnames_in_special) && (fn != 0)) {
304 the_output->put_filename(fn, po);
308 file_iterator::~file_iterator()
313 void file_iterator::close()
317 #ifndef POPEN_MISSING
320 #endif /* not POPEN_MISSING */
325 int file_iterator::is_file()
330 int file_iterator::next_file(FILE *f, const char *s)
344 int file_iterator::fill(node **)
349 unsigned char *p = buf;
351 unsigned char *e = p + BUF_SIZE;
356 if (invalid_input_char(c))
357 warning(WARN_INPUT, "invalid input character code %1", int(c));
365 seen_escape = (c == '\\');
378 int file_iterator::peek()
381 while (invalid_input_char(c)) {
382 warning(WARN_INPUT, "invalid input character code %1", int(c));
390 int file_iterator::get_location(int /*allow_macro*/,
391 const char **filenamep, int *linenop)
394 if (filename != 0 && strcmp(filename, "-") == 0)
395 *filenamep = "<standard input>";
397 *filenamep = filename;
401 void file_iterator::backtrace()
403 errprint("%1:%2: backtrace: %3 `%1'\n", filename, lineno,
404 popened ? "process" : "file");
407 int file_iterator::set_location(const char *f, int ln)
413 the_output->put_filename(f, 0);
419 input_iterator nil_iterator;
423 static int get(node **);
425 static void push(input_iterator *);
426 static input_iterator *get_arg(int);
427 static arg_list *get_arg_list();
428 static symbol get_macro_name();
429 static int space_follows_arg(int);
430 static int get_break_flag();
432 static int get_location(int, const char **, int *);
433 static int set_location(const char *, int);
434 static void backtrace();
435 static void backtrace_all();
436 static void next_file(FILE *, const char *);
437 static void end_file();
438 static void shift(int n);
439 static void add_boundary();
440 static void add_return_boundary();
441 static int is_return_boundary();
442 static void remove_boundary();
443 static int get_level();
444 static int get_div_level();
445 static void increase_level();
446 static void decrease_level();
448 static void pop_macro();
449 static void save_compatible_flag(int);
450 static int get_compatible_flag();
451 static statem *get_diversion_state();
452 static void check_end_diversion(input_iterator *t);
454 static int div_level;
455 static statem *diversion_state;
457 static input_iterator *top;
459 static int finish_get(node **);
460 static int finish_peek();
463 input_iterator *input_stack::top = &nil_iterator;
464 int input_stack::level = 0;
465 int input_stack::limit = DEFAULT_INPUT_STACK_LIMIT;
466 int input_stack::div_level = 0;
467 statem *input_stack::diversion_state = NULL;
471 inline int input_stack::get_level()
476 inline void input_stack::increase_level()
481 inline void input_stack::decrease_level()
486 inline int input_stack::get_div_level()
491 inline int input_stack::get(node **np)
493 int res = (top->ptr < top->eptr) ? *top->ptr++ : finish_get(np);
495 old_have_input = have_input;
501 int input_stack::finish_get(node **np)
504 int c = top->fill(np);
505 if (c != EOF || top->is_boundary())
507 if (top == &nil_iterator)
509 input_iterator *tem = top;
510 check_end_diversion(tem);
511 #if defined(DEBUGGING)
513 if (tem->is_diversion)
515 "in diversion level = %d\n", input_stack::get_div_level());
520 if (top->ptr < top->eptr)
527 inline int input_stack::peek()
529 return (top->ptr < top->eptr) ? *top->ptr : finish_peek();
532 void input_stack::check_end_diversion(input_iterator *t)
534 if (t->is_diversion) {
537 delete diversion_state;
538 diversion_state = t->diversion_state;
542 int input_stack::finish_peek()
546 if (c != EOF || top->is_boundary())
548 if (top == &nil_iterator)
550 input_iterator *tem = top;
551 check_end_diversion(tem);
555 if (top->ptr < top->eptr)
562 void input_stack::add_boundary()
564 push(new input_boundary);
567 void input_stack::add_return_boundary()
569 push(new input_return_boundary);
572 int input_stack::is_return_boundary()
574 return top->is_boundary() == 2;
577 void input_stack::remove_boundary()
579 assert(top->is_boundary());
580 input_iterator *temp = top->next;
581 check_end_diversion(top);
588 void input_stack::push(input_iterator *in)
592 if (++level > limit && limit > 0)
593 fatal("input stack limit exceeded (probable infinite loop)");
596 if (top->is_diversion) {
598 in->diversion_state = diversion_state;
599 diversion_state = curenv->construct_state(0);
600 #if defined(DEBUGGING)
602 curenv->dump_troff_state();
607 #if defined(DEBUGGING)
609 if (top->is_diversion) {
611 "in diversion level = %d\n", input_stack::get_div_level());
617 statem *get_diversion_state()
619 return input_stack::get_diversion_state();
622 statem *input_stack::get_diversion_state()
624 if (diversion_state == NULL)
627 return new statem(diversion_state);
630 input_iterator *input_stack::get_arg(int i)
633 for (p = top; p != 0; p = p->next)
635 return p->get_arg(i);
639 arg_list *input_stack::get_arg_list()
642 for (p = top; p != 0; p = p->next)
644 return p->get_arg_list();
648 symbol input_stack::get_macro_name()
651 for (p = top; p != 0; p = p->next)
653 return p->get_macro_name();
657 int input_stack::space_follows_arg(int i)
660 for (p = top; p != 0; p = p->next)
662 return p->space_follows_arg(i);
666 int input_stack::get_break_flag()
668 return top->get_break_flag();
671 void input_stack::shift(int n)
673 for (input_iterator *p = top; p; p = p->next)
680 int input_stack::nargs()
682 for (input_iterator *p =top; p != 0; p = p->next)
688 int input_stack::get_location(int allow_macro, const char **filenamep, int *linenop)
690 for (input_iterator *p = top; p; p = p->next)
691 if (p->get_location(allow_macro, filenamep, linenop))
696 void input_stack::backtrace()
700 // only backtrace down to (not including) the topmost file
701 for (input_iterator *p = top;
702 p && !p->get_location(0, &f, &n);
707 void input_stack::backtrace_all()
709 for (input_iterator *p = top; p; p = p->next)
713 int input_stack::set_location(const char *filename, int lineno)
715 for (input_iterator *p = top; p; p = p->next)
716 if (p->set_location(filename, lineno))
721 void input_stack::next_file(FILE *fp, const char *s)
724 for (pp = ⊤ *pp != &nil_iterator; pp = &(*pp)->next)
725 if ((*pp)->next_file(fp, s))
727 if (++level > limit && limit > 0)
728 fatal("input stack limit exceeded");
729 *pp = new file_iterator(fp, s);
730 (*pp)->next = &nil_iterator;
733 void input_stack::end_file()
735 for (input_iterator **pp = ⊤ *pp != &nil_iterator; pp = &(*pp)->next)
736 if ((*pp)->is_file()) {
737 input_iterator *tem = *pp;
738 check_end_diversion(tem);
746 void input_stack::clear()
749 while (top != &nil_iterator) {
750 if (top->is_boundary())
752 input_iterator *tem = top;
753 check_end_diversion(tem);
758 // Keep while_request happy.
759 for (; nboundaries > 0; --nboundaries)
760 add_return_boundary();
763 void input_stack::pop_macro()
768 if (top->next == &nil_iterator)
770 if (top->is_boundary())
772 is_macro = top->is_macro();
773 input_iterator *tem = top;
774 check_end_diversion(tem);
779 // Keep while_request happy.
780 for (; nboundaries > 0; --nboundaries)
781 add_return_boundary();
784 inline void input_stack::save_compatible_flag(int f)
786 top->save_compatible_flag(f);
789 inline int input_stack::get_compatible_flag()
791 return top->get_compatible_flag();
794 void backtrace_request()
796 input_stack::backtrace_all();
803 symbol nm = get_long_name();
804 while (!tok.newline() && !tok.eof())
807 input_stack::end_file();
810 FILE *fp = include_search_path.open_file_cautious(nm.contents());
812 error("can't open `%1': %2", nm.contents(), strerror(errno));
814 input_stack::next_file(fp, nm.contents());
822 if (!has_arg() || !get_integer(&n))
824 input_stack::shift(n);
828 static char get_char_for_escape_name(int allow_space = 0)
830 int c = get_copy(0, 0, 1);
833 copy_mode_error("end of input in escape name");
836 if (!invalid_input_char(c))
841 input_stack::push(make_temp_iterator("\n"));
844 if (c == ' ' && allow_space)
850 copy_mode_error("%1 is not allowed in an escape name",
851 input_char_description(c));
857 static symbol read_two_char_escape_name()
860 buf[0] = get_char_for_escape_name();
861 if (buf[0] != '\0') {
862 buf[1] = get_char_for_escape_name();
871 static symbol read_long_escape_name(read_mode mode)
873 int start_level = input_stack::get_level();
874 char abuf[ABUF_SIZE];
876 int buf_size = ABUF_SIZE;
881 c = get_char_for_escape_name(have_char && mode == WITH_ARGS);
888 if (mode == WITH_ARGS && c == ' ')
890 if (i + 2 > buf_size) {
892 buf = new char[ABUF_SIZE*2];
893 memcpy(buf, abuf, buf_size);
894 buf_size = ABUF_SIZE*2;
898 buf = new char[buf_size*2];
899 memcpy(buf, old_buf, buf_size);
904 if (c == ']' && input_stack::get_level() == start_level)
913 if (mode != ALLOW_EMPTY)
914 copy_mode_error("empty escape name");
926 static symbol read_escape_name(read_mode mode)
928 char c = get_char_for_escape_name();
932 return read_two_char_escape_name();
933 if (c == '[' && !compatible_flag)
934 return read_long_escape_name(mode);
941 static symbol read_increment_and_escape_name(int *incp)
943 char c = get_char_for_escape_name();
950 return read_two_char_escape_name();
953 return read_escape_name();
956 return read_escape_name();
958 if (!compatible_flag) {
960 return read_long_escape_name();
971 static int get_copy(node **nd, int defining, int handle_escape_E)
974 int c = input_stack::get(nd);
975 if (c == PUSH_GROFF_MODE) {
976 input_stack::save_compatible_flag(compatible_flag);
980 if (c == PUSH_COMP_MODE) {
981 input_stack::save_compatible_flag(compatible_flag);
985 if (c == POP_GROFFCOMP_MODE) {
986 compatible_flag = input_stack::get_compatible_flag();
989 if (c == BEGIN_QUOTE) {
990 input_stack::increase_level();
993 if (c == END_QUOTE) {
994 input_stack::decrease_level();
997 if (c == DOUBLE_QUOTE)
999 if (c == ESCAPE_E && handle_escape_E)
1001 if (c == ESCAPE_NEWLINE) {
1005 c = input_stack::get(nd);
1006 } while (c == ESCAPE_NEWLINE);
1008 if (c != escape_char || escape_char <= 0)
1011 c = input_stack::peek();
1016 (void)input_stack::get(0);
1017 while ((c = input_stack::get(0)) != '\n' && c != EOF)
1020 case '#': // Like \" but newline is ignored.
1021 (void)input_stack::get(0);
1022 while ((c = input_stack::get(0)) != '\n')
1028 (void)input_stack::get(0);
1029 symbol s = read_escape_name();
1030 if (!(s.is_null() || s.is_empty()))
1036 (void)input_stack::get(0);
1037 symbol s = read_escape_name(WITH_ARGS);
1038 if (!(s.is_null() || s.is_empty())) {
1039 if (have_string_arg) {
1040 have_string_arg = 0;
1041 interpolate_string_with_args(s);
1044 interpolate_string(s);
1049 (void)input_stack::get(0);
1052 (void)input_stack::get(0);
1055 (void)input_stack::get(0);
1056 if (handle_escape_E)
1061 (void)input_stack::get(0);
1063 symbol s = read_increment_and_escape_name(&inc);
1064 if (!(s.is_null() || s.is_empty()))
1065 interpolate_number_reg(s, inc);
1070 (void)input_stack::get(0);
1071 symbol s = read_escape_name();
1072 if (!(s.is_null() || s.is_empty()))
1073 interpolate_number_format(s);
1077 (void)input_stack::get(0);
1081 (void)input_stack::get(0);
1082 symbol s = read_escape_name();
1083 if (!(s.is_null() || s.is_empty()))
1084 interpolate_environment_variable(s);
1088 (void)input_stack::get(0);
1090 return ESCAPE_NEWLINE;
1093 (void)input_stack::get(0);
1094 return ESCAPE_SPACE;
1096 (void)input_stack::get(0);
1097 return ESCAPE_TILDE;
1099 (void)input_stack::get(0);
1100 return ESCAPE_COLON;
1102 (void)input_stack::get(0);
1105 (void)input_stack::get(0);
1106 return ESCAPE_CIRCUMFLEX;
1108 (void)input_stack::get(0);
1109 return ESCAPE_LEFT_BRACE;
1111 (void)input_stack::get(0);
1112 return ESCAPE_RIGHT_BRACE;
1114 (void)input_stack::get(0);
1115 return ESCAPE_LEFT_QUOTE;
1117 (void)input_stack::get(0);
1118 return ESCAPE_RIGHT_QUOTE;
1120 (void)input_stack::get(0);
1121 return ESCAPE_HYPHEN;
1123 (void)input_stack::get(0);
1124 return ESCAPE_UNDERSCORE;
1126 (void)input_stack::get(0);
1129 (void)input_stack::get(0);
1132 (void)input_stack::get(0);
1133 return ESCAPE_QUESTION;
1135 (void)input_stack::get(0);
1136 return ESCAPE_AMPERSAND;
1138 (void)input_stack::get(0);
1139 return ESCAPE_RIGHT_PARENTHESIS;
1141 (void)input_stack::get(0);
1144 (void)input_stack::get(0);
1145 return ESCAPE_PERCENT;
1147 if (c == escape_char) {
1148 (void)input_stack::get(0);
1157 class non_interpreted_char_node : public node {
1160 non_interpreted_char_node(unsigned char);
1162 int interpret(macro *);
1169 int non_interpreted_char_node::same(node *nd)
1171 return c == ((non_interpreted_char_node *)nd)->c;
1174 const char *non_interpreted_char_node::type()
1176 return "non_interpreted_char_node";
1179 int non_interpreted_char_node::force_tprint()
1184 int non_interpreted_char_node::is_tag()
1189 non_interpreted_char_node::non_interpreted_char_node(unsigned char n) : c(n)
1194 node *non_interpreted_char_node::copy()
1196 return new non_interpreted_char_node(c);
1199 int non_interpreted_char_node::interpret(macro *mac)
1205 static void do_width();
1206 static node *do_non_interpreted();
1207 static node *do_special();
1208 static node *do_suppress(symbol nm);
1209 static void do_register();
1211 dictionary color_dictionary(501);
1213 static color *lookup_color(symbol nm)
1215 assert(!nm.is_null());
1216 if (nm == default_symbol)
1217 return &default_color;
1218 color *c = (color *)color_dictionary.lookup(nm);
1220 warning(WARN_COLOR, "color `%1' not defined", nm.contents());
1224 void do_glyph_color(symbol nm)
1229 curenv->set_glyph_color(curenv->get_prev_glyph_color());
1231 color *tem = lookup_color(nm);
1233 curenv->set_glyph_color(tem);
1235 (void)color_dictionary.lookup(nm, new color(nm));
1239 void do_fill_color(symbol nm)
1244 curenv->set_fill_color(curenv->get_prev_fill_color());
1246 color *tem = lookup_color(nm);
1248 curenv->set_fill_color(tem);
1250 (void)color_dictionary.lookup(nm, new color(nm));
1254 static unsigned int get_color_element(const char *scheme, const char *col)
1257 if (!get_number(&val, 'f')) {
1258 warning(WARN_COLOR, "%1 in %2 definition set to 0", col, scheme);
1263 warning(WARN_RANGE, "%1 cannot be negative: set to 0", col);
1266 if (val > color::MAX_COLOR_VAL+1) {
1267 warning(WARN_RANGE, "%1 cannot be greater than 1", col);
1268 // we change 0x10000 to 0xffff
1269 return color::MAX_COLOR_VAL;
1271 return (unsigned int)val;
1274 static color *read_rgb(char end = 0)
1276 symbol component = do_get_long_name(0, end);
1277 if (component.is_null()) {
1278 warning(WARN_COLOR, "missing rgb color values");
1281 const char *s = component.contents();
1282 color *col = new color;
1284 if (!col->read_rgb(s)) {
1285 warning(WARN_COLOR, "expecting rgb color definition not `%1'", s);
1292 input_stack::push(make_temp_iterator(" "));
1293 input_stack::push(make_temp_iterator(s));
1295 unsigned int r = get_color_element("rgb color", "red component");
1296 unsigned int g = get_color_element("rgb color", "green component");
1297 unsigned int b = get_color_element("rgb color", "blue component");
1298 col->set_rgb(r, g, b);
1303 static color *read_cmy(char end = 0)
1305 symbol component = do_get_long_name(0, end);
1306 if (component.is_null()) {
1307 warning(WARN_COLOR, "missing cmy color values");
1310 const char *s = component.contents();
1311 color *col = new color;
1313 if (!col->read_cmy(s)) {
1314 warning(WARN_COLOR, "expecting cmy color definition not `%1'", s);
1321 input_stack::push(make_temp_iterator(" "));
1322 input_stack::push(make_temp_iterator(s));
1324 unsigned int c = get_color_element("cmy color", "cyan component");
1325 unsigned int m = get_color_element("cmy color", "magenta component");
1326 unsigned int y = get_color_element("cmy color", "yellow component");
1327 col->set_cmy(c, m, y);
1332 static color *read_cmyk(char end = 0)
1334 symbol component = do_get_long_name(0, end);
1335 if (component.is_null()) {
1336 warning(WARN_COLOR, "missing cmyk color values");
1339 const char *s = component.contents();
1340 color *col = new color;
1342 if (!col->read_cmyk(s)) {
1343 warning(WARN_COLOR, "`expecting a cmyk color definition not `%1'", s);
1350 input_stack::push(make_temp_iterator(" "));
1351 input_stack::push(make_temp_iterator(s));
1353 unsigned int c = get_color_element("cmyk color", "cyan component");
1354 unsigned int m = get_color_element("cmyk color", "magenta component");
1355 unsigned int y = get_color_element("cmyk color", "yellow component");
1356 unsigned int k = get_color_element("cmyk color", "black component");
1357 col->set_cmyk(c, m, y, k);
1362 static color *read_gray(char end = 0)
1364 symbol component = do_get_long_name(0, end);
1365 if (component.is_null()) {
1366 warning(WARN_COLOR, "missing gray values");
1369 const char *s = component.contents();
1370 color *col = new color;
1372 if (!col->read_gray(s)) {
1373 warning(WARN_COLOR, "`expecting a gray definition not `%1'", s);
1380 input_stack::push(make_temp_iterator("\n"));
1381 input_stack::push(make_temp_iterator(s));
1383 unsigned int g = get_color_element("gray", "gray value");
1389 static void activate_color()
1392 if (has_arg() && get_integer(&n))
1393 color_flag = n != 0;
1399 static void define_color()
1401 symbol color_name = get_long_name(1);
1402 if (color_name.is_null()) {
1406 if (color_name == default_symbol) {
1407 warning(WARN_COLOR, "default color can't be redefined");
1411 symbol style = get_long_name(1);
1412 if (style.is_null()) {
1417 if (strcmp(style.contents(), "rgb") == 0)
1419 else if (strcmp(style.contents(), "cmyk") == 0)
1421 else if (strcmp(style.contents(), "gray") == 0)
1423 else if (strcmp(style.contents(), "grey") == 0)
1425 else if (strcmp(style.contents(), "cmy") == 0)
1429 "unknown color space `%1'; use rgb, cmyk, gray or cmy",
1435 col->nm = color_name;
1436 (void)color_dictionary.lookup(color_name, col);
1441 node *do_overstrike()
1444 overstrike_node *on = new overstrike_node;
1445 int start_level = input_stack::get_level();
1449 if (tok.newline() || tok.eof()) {
1450 warning(WARN_DELIM, "missing closing delimiter");
1451 input_stack::push(make_temp_iterator("\n"));
1455 && (compatible_flag || input_stack::get_level() == start_level))
1457 if (tok.horizontal_space())
1458 on->overstrike(tok.nd->copy());
1459 else if (tok.unstretchable_space())
1461 node *n = new hmotion_node(curenv->get_space_width(),
1462 curenv->get_fill_color());
1466 charinfo *ci = tok.get_char(1);
1468 node *n = curenv->make_char_node(ci);
1477 static node *do_bracket()
1480 bracket_node *bn = new bracket_node;
1482 int start_level = input_stack::get_level();
1486 warning(WARN_DELIM, "missing closing delimiter");
1489 if (tok.newline()) {
1490 warning(WARN_DELIM, "missing closing delimiter");
1491 input_stack::push(make_temp_iterator("\n"));
1495 && (compatible_flag || input_stack::get_level() == start_level))
1497 charinfo *ci = tok.get_char(1);
1499 node *n = curenv->make_char_node(ci);
1507 static int do_name_test()
1511 int start_level = input_stack::get_level();
1516 if (tok.newline() || tok.eof()) {
1517 warning(WARN_DELIM, "missing closing delimiter");
1518 input_stack::push(make_temp_iterator("\n"));
1522 && (compatible_flag || input_stack::get_level() == start_level))
1528 return some_char && !bad_char;
1531 static int do_expr_test()
1535 int start_level = input_stack::get_level();
1536 if (!start.delimiter(1))
1539 // disable all warning and error messages temporarily
1540 int saved_warning_mask = warning_mask;
1541 int saved_inhibit_errors = inhibit_errors;
1545 int result = get_number_rigidly(&dummy, 'u');
1546 warning_mask = saved_warning_mask;
1547 inhibit_errors = saved_inhibit_errors;
1548 if (tok == start && input_stack::get_level() == start_level)
1550 // ignore everything up to the delimiter in case we aren't right there
1553 if (tok.newline() || tok.eof()) {
1554 warning(WARN_DELIM, "missing closing delimiter");
1555 input_stack::push(make_temp_iterator("\n"));
1558 if (tok == start && input_stack::get_level() == start_level)
1565 static node *do_zero_width()
1569 int start_level = input_stack::get_level();
1570 environment env(curenv);
1571 environment *oldenv = curenv;
1575 if (tok.newline() || tok.eof()) {
1576 error("missing closing delimiter");
1580 && (compatible_flag || input_stack::get_level() == start_level))
1585 node *rev = env.extract_output_line();
1593 return new zero_width_node(n);
1598 // It's undesirable for \Z to change environments, because then
1599 // \n(.w won't work as expected.
1601 static node *do_zero_width()
1603 node *rev = new dummy_node;
1606 int start_level = input_stack::get_level();
1609 if (tok.newline() || tok.eof()) {
1610 warning(WARN_DELIM, "missing closing delimiter");
1611 input_stack::push(make_temp_iterator("\n"));
1615 && (compatible_flag || input_stack::get_level() == start_level))
1617 if (!tok.add_to_node_list(&rev))
1618 error("invalid token in argument to \\Z");
1627 return new zero_width_node(n);
1632 token_node *node::get_token_node()
1637 class token_node : public node {
1640 token_node(const token &t);
1642 token_node *get_token_node();
1649 token_node::token_node(const token &t) : tk(t)
1653 node *token_node::copy()
1655 return new token_node(tk);
1658 token_node *token_node::get_token_node()
1663 int token_node::same(node *nd)
1665 return tk == ((token_node *)nd)->tk;
1668 const char *token_node::type()
1670 return "token_node";
1673 int token_node::force_tprint()
1678 int token_node::is_tag()
1683 token::token() : nd(0), type(TOKEN_EMPTY)
1692 token::token(const token &t)
1693 : nm(t.nm), c(t.c), val(t.val), dim(t.dim), type(t.type)
1695 // Use two statements to work around bug in SGI C++.
1697 nd = tem ? tem->copy() : 0;
1700 void token::operator=(const token &t)
1704 // Use two statements to work around bug in SGI C++.
1706 nd = tem ? tem->copy() : 0;
1723 return !tok.newline();
1726 void token::make_space()
1731 void token::make_newline()
1733 type = TOKEN_NEWLINE;
1745 int cc = input_stack::get(&n);
1746 if (cc != escape_char || escape_char == 0) {
1749 case PUSH_GROFF_MODE:
1750 input_stack::save_compatible_flag(compatible_flag);
1751 compatible_flag = 0;
1753 case PUSH_COMP_MODE:
1754 input_stack::save_compatible_flag(compatible_flag);
1755 compatible_flag = 1;
1757 case POP_GROFFCOMP_MODE:
1758 compatible_flag = input_stack::get_compatible_flag();
1761 input_stack::increase_level();
1764 input_stack::decrease_level();
1771 case TRANSPARENT_FILE_REQUEST:
1773 case COPY_FILE_REQUEST:
1775 case VJUSTIFY_REQUEST:
1777 type = TOKEN_REQUEST;
1781 type = TOKEN_BEGIN_TRAP;
1784 type = TOKEN_END_TRAP;
1786 case LAST_PAGE_EJECTOR:
1787 seen_last_page_ejector = 1;
1790 type = TOKEN_PAGE_EJECTOR;
1792 case ESCAPE_PERCENT:
1794 type = TOKEN_HYPHEN_INDICATOR;
1798 type = TOKEN_UNSTRETCHABLE_SPACE;
1802 type = TOKEN_STRETCHABLE_SPACE;
1806 type = TOKEN_ZERO_WIDTH_BREAK;
1810 type = TOKEN_ESCAPE;
1813 goto handle_escape_char;
1816 type = TOKEN_HORIZONTAL_SPACE;
1817 nd = new hmotion_node(curenv->get_narrow_space_width(),
1818 curenv->get_fill_color());
1820 case ESCAPE_CIRCUMFLEX:
1822 type = TOKEN_HORIZONTAL_SPACE;
1823 nd = new hmotion_node(curenv->get_half_narrow_space_width(),
1824 curenv->get_fill_color());
1826 case ESCAPE_NEWLINE:
1829 case ESCAPE_LEFT_BRACE:
1831 type = TOKEN_LEFT_BRACE;
1833 case ESCAPE_RIGHT_BRACE:
1835 type = TOKEN_RIGHT_BRACE;
1837 case ESCAPE_LEFT_QUOTE:
1839 type = TOKEN_SPECIAL;
1842 case ESCAPE_RIGHT_QUOTE:
1844 type = TOKEN_SPECIAL;
1849 type = TOKEN_SPECIAL;
1852 case ESCAPE_UNDERSCORE:
1854 type = TOKEN_SPECIAL;
1859 type = TOKEN_INTERRUPT;
1863 type = TOKEN_TRANSPARENT;
1865 case ESCAPE_QUESTION:
1867 nd = do_non_interpreted();
1873 case ESCAPE_AMPERSAND:
1877 case ESCAPE_RIGHT_PARENTHESIS:
1878 ESCAPE_RIGHT_PARENTHESIS:
1879 type = TOKEN_TRANSPARENT_DUMMY;
1882 type = TOKEN_BACKSPACE;
1891 type = TOKEN_NEWLINE;
1894 type = TOKEN_LEADER;
1899 token_node *tn = n->get_token_node();
1918 cc = input_stack::get(&n);
1921 nm = read_two_char_escape_name();
1922 type = TOKEN_SPECIAL;
1926 error("end of input after escape character");
1929 goto ESCAPE_LEFT_QUOTE;
1931 goto ESCAPE_RIGHT_QUOTE;
1935 goto ESCAPE_UNDERSCORE;
1937 goto ESCAPE_PERCENT;
1941 nd = new hmotion_node(curenv->get_digit_width(),
1942 curenv->get_fill_color());
1943 type = TOKEN_HORIZONTAL_SPACE;
1948 goto ESCAPE_CIRCUMFLEX;
1950 type = TOKEN_ITALIC_CORRECTION;
1954 nd = new left_italic_corrected_node;
1957 goto ESCAPE_AMPERSAND;
1959 goto ESCAPE_RIGHT_PARENTHESIS;
1963 goto ESCAPE_QUESTION;
1969 while ((cc = input_stack::get(0)) != '\n' && cc != EOF)
1972 type = TOKEN_NEWLINE;
1976 case '#': // Like \" but newline is ignored.
1977 while ((cc = input_stack::get(0)) != '\n')
1985 symbol s = read_escape_name();
1986 if (!(s.is_null() || s.is_empty()))
1992 symbol s = read_escape_name(WITH_ARGS);
1993 if (!(s.is_null() || s.is_empty())) {
1994 if (have_string_arg) {
1995 have_string_arg = 0;
1996 interpolate_string_with_args(s);
1999 interpolate_string(s);
2004 nd = new non_interpreted_char_node('\001');
2008 c = '0' + do_name_test();
2016 c = '0' + do_expr_test();
2022 nm = get_delim_name();
2025 type = TOKEN_SPECIAL;
2029 nd = new vmotion_node(curenv->get_size() / 2,
2030 curenv->get_fill_color());
2033 nd = read_draw_node();
2041 goto handle_escape_char;
2044 symbol s = read_escape_name(ALLOW_EMPTY);
2048 for (p = s.contents(); *p != '\0'; p++)
2051 if (*p || s.is_empty())
2052 curenv->set_font(s);
2054 curenv->set_font(atoi(s.contents()));
2055 if (!compatible_flag)
2061 symbol s = read_escape_name(ALLOW_EMPTY);
2064 curenv->set_family(s);
2070 symbol s = read_escape_name();
2071 if (!(s.is_null() || s.is_empty()))
2072 interpolate_number_format(s);
2076 if (!get_delim_number(&x, 'm'))
2078 type = TOKEN_HORIZONTAL_SPACE;
2079 nd = new hmotion_node(x, curenv->get_fill_color());
2082 // don't take height increments relative to previous height if
2083 // in compatibility mode
2084 if (!compatible_flag && curenv->get_char_height()) {
2085 if (get_delim_number(&x, 'z', curenv->get_char_height()))
2086 curenv->set_char_height(x);
2089 if (get_delim_number(&x, 'z', curenv->get_requested_point_size()))
2090 curenv->set_char_height(x);
2092 if (!compatible_flag)
2096 nm = read_escape_name();
2097 if (nm.is_null() || nm.is_empty())
2099 type = TOKEN_MARK_INPUT;
2105 if (!get_line_arg(&x, (cc == 'l' ? 'm': 'v'), &s))
2108 s = get_charinfo(cc == 'l' ? "ru" : "br");
2110 node *char_node = curenv->make_char_node(s);
2112 nd = new hline_node(x, char_node);
2114 nd = new vline_node(x, char_node);
2118 do_glyph_color(read_escape_name(ALLOW_EMPTY));
2119 if (!compatible_flag)
2123 do_fill_color(read_escape_name(ALLOW_EMPTY));
2124 if (!compatible_flag)
2130 symbol s = read_increment_and_escape_name(&inc);
2131 if (!(s.is_null() || s.is_empty()))
2132 interpolate_number_reg(s, inc);
2136 if (!get_delim_number(&val, 0))
2139 warning(WARN_CHAR, "invalid numbered character %1", val);
2142 type = TOKEN_NUMBERED_CHAR;
2145 nd = do_overstrike();
2149 nd = do_suppress(read_escape_name());
2155 type = TOKEN_SPREAD;
2159 nd = new vmotion_node(-curenv->get_size(), curenv->get_fill_color());
2163 if (!compatible_flag)
2168 curenv->set_size(x);
2169 if (!compatible_flag)
2173 if (get_delim_number(&x, 0))
2174 curenv->set_char_slant(x);
2175 if (!compatible_flag)
2180 nd = new non_interpreted_char_node('\t');
2184 nd = new vmotion_node(-curenv->get_size() / 2,
2185 curenv->get_fill_color());
2188 if (!get_delim_number(&x, 'v'))
2191 nd = new vmotion_node(x, curenv->get_fill_color());
2195 symbol s = read_escape_name();
2196 if (!(s.is_null() || s.is_empty()))
2197 interpolate_environment_variable(s);
2204 if (!get_delim_number(&x, 'v'))
2207 nd = new extra_size_node(x);
2217 symbol s = read_escape_name();
2218 if (s.is_null() || s.is_empty())
2220 request_or_macro *p = lookup_request(s);
2221 macro *m = p->to_macro();
2223 error("can't transparently throughput a request");
2226 nd = new special_node(*m);
2233 if (type == TOKEN_NODE || type == TOKEN_HORIZONTAL_SPACE)
2234 nd = new zero_width_node(nd);
2236 charinfo *ci = get_char(1);
2239 node *gn = curenv->make_char_node(ci);
2242 nd = new zero_width_node(gn);
2248 nd = do_zero_width();
2254 goto ESCAPE_LEFT_BRACE;
2256 goto ESCAPE_RIGHT_BRACE;
2260 if (!compatible_flag) {
2261 symbol s = read_long_escape_name(WITH_ARGS);
2262 if (s.is_null() || s.is_empty())
2264 if (have_string_arg) {
2265 have_string_arg = 0;
2266 nm = composite_glyph_name(s);
2269 const char *gn = check_unicode_name(s.contents());
2271 const char *gn_decomposed = decompose_unicode(gn);
2273 gn = &gn_decomposed[1];
2274 const char *groff_gn = unicode_to_glyph_name(gn);
2276 nm = symbol(groff_gn);
2278 char *buf = new char[strlen(gn) + 1 + 1];
2286 nm = symbol(s.contents());
2288 type = TOKEN_SPECIAL;
2291 goto handle_normal_char;
2293 if (cc != escape_char && cc != '.')
2294 warning(WARN_ESCAPE, "escape character ignored before %1",
2295 input_char_description(cc));
2296 goto handle_normal_char;
2302 int token::operator==(const token &t)
2311 case TOKEN_NUMBERED_CHAR:
2312 return val == t.val;
2318 int token::operator!=(const token &t)
2320 return !(*this == t);
2323 // is token a suitable delimiter (like ')?
2325 int token::delimiter(int err)
2354 error("cannot use character `%1' as a starting delimiter", char(c));
2360 // the user doesn't know what a node is
2362 error("missing argument or invalid starting delimiter");
2365 case TOKEN_STRETCHABLE_SPACE:
2366 case TOKEN_UNSTRETCHABLE_SPACE:
2367 case TOKEN_HORIZONTAL_SPACE:
2371 error("cannot use %1 as a starting delimiter", description());
2378 const char *token::description()
2382 case TOKEN_BACKSPACE:
2383 return "a backspace character";
2394 case TOKEN_HYPHEN_INDICATOR:
2396 case TOKEN_INTERRUPT:
2398 case TOKEN_ITALIC_CORRECTION:
2401 return "a leader character";
2402 case TOKEN_LEFT_BRACE:
2404 case TOKEN_MARK_INPUT:
2410 case TOKEN_NUMBERED_CHAR:
2412 case TOKEN_RIGHT_BRACE:
2417 return "a special character";
2420 case TOKEN_STRETCHABLE_SPACE:
2422 case TOKEN_UNSTRETCHABLE_SPACE:
2424 case TOKEN_HORIZONTAL_SPACE:
2425 return "a horizontal space";
2427 return "a tab character";
2428 case TOKEN_TRANSPARENT:
2430 case TOKEN_TRANSPARENT_DUMMY:
2432 case TOKEN_ZERO_WIDTH_BREAK:
2435 return "end of input";
2439 return "a magic token";
2444 while (!tok.newline())
2455 if (has_arg() && get_integer(&n))
2456 compatible_flag = n != 0;
2458 compatible_flag = 1;
2462 static void empty_name_warning(int required)
2464 if (tok.newline() || tok.eof()) {
2466 warning(WARN_MISSING, "missing name");
2468 else if (tok.right_brace() || tok.tab()) {
2469 const char *start = tok.description();
2472 } while (tok.space() || tok.right_brace() || tok.tab());
2473 if (!tok.newline() && !tok.eof())
2474 error("%1 is not allowed before an argument", start);
2476 warning(WARN_MISSING, "missing name");
2479 error("name expected (got %1)", tok.description());
2481 error("name expected (got %1): treated as missing", tok.description());
2484 static void non_empty_name_warning()
2486 if (!tok.newline() && !tok.eof() && !tok.space() && !tok.tab()
2487 && !tok.right_brace()
2488 // We don't want to give a warning for .el\{
2489 && !tok.left_brace())
2490 error("%1 is not allowed in a name", tok.description());
2493 symbol get_name(int required)
2495 if (compatible_flag) {
2498 if ((buf[0] = tok.ch()) != 0) {
2500 if ((buf[1] = tok.ch()) != 0) {
2505 non_empty_name_warning();
2509 empty_name_warning(required);
2514 return get_long_name(required);
2517 symbol get_long_name(int required)
2519 return do_get_long_name(required, 0);
2522 static symbol do_get_long_name(int required, char end)
2526 char abuf[ABUF_SIZE];
2528 int buf_size = ABUF_SIZE;
2531 // If end != 0 we normally have to append a null byte
2532 if (i + 2 > buf_size) {
2534 buf = new char[ABUF_SIZE*2];
2535 memcpy(buf, abuf, buf_size);
2536 buf_size = ABUF_SIZE*2;
2539 char *old_buf = buf;
2540 buf = new char[buf_size*2];
2541 memcpy(buf, old_buf, buf_size);
2546 if ((buf[i] = tok.ch()) == 0 || buf[i] == end)
2552 empty_name_warning(required);
2555 if (end && buf[i] == end)
2558 non_empty_name_warning();
2571 topdiv->set_last_page();
2572 if (!end_macro_name.is_null()) {
2573 spring_trap(end_macro_name);
2575 process_input_stack();
2577 curenv->final_break();
2579 process_input_stack();
2581 if (topdiv->get_page_length() > 0) {
2583 topdiv->set_ejecting();
2584 static unsigned char buf[2] = { LAST_PAGE_EJECTOR, '\0' };
2585 input_stack::push(make_temp_iterator((char *)buf));
2586 topdiv->space(topdiv->get_page_length(), 1);
2588 process_input_stack();
2589 seen_last_page_ejector = 1; // should be set already
2590 topdiv->set_ejecting();
2591 push_page_ejector();
2592 topdiv->space(topdiv->get_page_length(), 1);
2594 process_input_stack();
2596 // This will only happen if a trap-invoked macro starts a diversion,
2597 // or if vertical position traps have been disabled.
2598 cleanup_and_exit(0);
2601 // This implements .ex. The input stack must be cleared before calling
2606 input_stack::clear();
2613 void return_macro_request()
2615 if (has_arg() && tok.ch())
2616 input_stack::pop_macro();
2617 input_stack::pop_macro();
2623 end_macro_name = get_name();
2627 void blank_line_macro()
2629 blank_line_macro_name = get_name();
2633 void leading_spaces_macro()
2635 leading_spaces_macro_name = get_name();
2639 static void trapping_blank_line()
2641 if (!blank_line_macro_name.is_null())
2642 spring_trap(blank_line_macro_name);
2649 int old_compatible_flag = compatible_flag;
2650 compatible_flag = 0;
2651 symbol nm = get_name();
2655 interpolate_macro(nm, 1);
2656 compatible_flag = old_compatible_flag;
2657 request_or_macro *p = lookup_request(nm);
2658 macro *m = p->to_macro();
2663 inline int possibly_handle_first_page_transition()
2665 if (topdiv->before_first_page && curdiv == topdiv && !curenv->is_dummy()) {
2666 handle_first_page_transition();
2673 static int transparent_translate(int cc)
2675 if (!invalid_input_char(cc)) {
2676 charinfo *ci = charset_table[cc];
2677 switch (ci->get_special_translation(1)) {
2678 case charinfo::TRANSLATE_SPACE:
2680 case charinfo::TRANSLATE_STRETCHABLE_SPACE:
2681 return ESCAPE_TILDE;
2682 case charinfo::TRANSLATE_DUMMY:
2683 return ESCAPE_AMPERSAND;
2684 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
2685 return ESCAPE_PERCENT;
2687 // This is really ugly.
2688 ci = ci->get_translation(1);
2690 int c = ci->get_ascii_code();
2693 error("can't translate %1 to special character `%2'"
2694 " in transparent throughput",
2695 input_char_description(cc),
2703 struct int_stack_element {
2705 int_stack_element *next;
2715 int_stack::int_stack()
2720 int_stack::~int_stack()
2723 int_stack_element *temp = top;
2729 int int_stack::is_empty()
2734 void int_stack::push(int n)
2736 int_stack_element *p = new int_stack_element;
2742 int int_stack::pop()
2745 int_stack_element *p = top;
2752 int node::reread(int *)
2757 int global_diverted_space = 0;
2759 int diverted_space_node::reread(int *bolp)
2761 global_diverted_space = 1;
2762 if (curenv->get_fill())
2763 trapping_blank_line();
2766 global_diverted_space = 0;
2771 int diverted_copy_file_node::reread(int *bolp)
2773 curdiv->copy_file(filename.contents());
2778 int word_space_node::reread(int *)
2781 for (width_list *w = orig_width; w; w = w->next)
2782 curenv->space(w->width, w->sentence_width);
2789 int unbreakable_space_node::reread(int *)
2794 int hmotion_node::reread(int *)
2796 if (unformat && was_tab) {
2797 curenv->handle_tab(0);
2804 static int leading_spaces_number = 0;
2805 static int leading_spaces_space = 0;
2807 void process_input_stack()
2809 int_stack trap_bol_stack;
2812 int suppress_next = 0;
2814 case token::TOKEN_CHAR:
2816 unsigned char ch = tok.c;
2817 if (bol && !have_input
2818 && (ch == curenv->control_char
2819 || ch == curenv->no_break_control_char)) {
2820 break_flag = ch == curenv->control_char;
2821 // skip tabs as well as spaces here
2824 } while (tok.white_space());
2825 symbol nm = get_name();
2826 #if defined(DEBUGGING)
2828 if (! nm.is_null()) {
2829 if (strcmp(nm.contents(), "test") == 0) {
2830 fprintf(stderr, "found it!\n");
2833 fprintf(stderr, "interpreting [%s]", nm.contents());
2834 if (strcmp(nm.contents(), "di") == 0 && topdiv != curdiv)
2835 fprintf(stderr, " currently in diversion: %s",
2836 curdiv->get_diversion_name());
2837 fprintf(stderr, "\n");
2845 interpolate_macro(nm);
2846 #if defined(DEBUGGING)
2848 fprintf(stderr, "finished interpreting [%s] and environment state is\n", nm.contents());
2849 curenv->dump_troff_state();
2856 if (possibly_handle_first_page_transition())
2860 #if defined(DEBUGGING)
2862 fprintf(stderr, "found [%c]\n", ch); fflush(stderr);
2865 curenv->add_char(charset_table[ch]);
2867 if (tok.type != token::TOKEN_CHAR)
2877 case token::TOKEN_TRANSPARENT:
2880 if (possibly_handle_first_page_transition())
2889 curdiv->transparent_output(transparent_translate(cc));
2891 curdiv->transparent_output(n);
2893 } while (cc != '\n' && cc != EOF);
2895 curdiv->transparent_output('\n');
2900 case token::TOKEN_NEWLINE:
2902 if (bol && !old_have_input
2903 && !curenv->get_prev_line_interrupted())
2904 trapping_blank_line();
2911 case token::TOKEN_REQUEST:
2913 int request_code = tok.c;
2915 switch (request_code) {
2919 case COPY_FILE_REQUEST:
2922 case TRANSPARENT_FILE_REQUEST:
2926 case VJUSTIFY_REQUEST:
2937 case token::TOKEN_SPACE:
2939 if (possibly_handle_first_page_transition())
2941 else if (bol && !curenv->get_prev_line_interrupted()) {
2943 // save space_width now so that it isn't changed by \f or \s
2944 // which we wouldn't notice here
2945 hunits space_width = curenv->get_space_width();
2947 nspaces += tok.nspaces();
2949 } while (tok.space());
2951 trapping_blank_line();
2954 leading_spaces_number = nspaces;
2955 leading_spaces_space = space_width.to_units() * nspaces;
2956 if (!leading_spaces_macro_name.is_null())
2957 spring_trap(leading_spaces_macro_name);
2960 curenv->add_node(new hmotion_node(space_width * nspaces,
2961 curenv->get_fill_color()));
2972 case token::TOKEN_EOF:
2974 case token::TOKEN_NODE:
2975 case token::TOKEN_HORIZONTAL_SPACE:
2977 if (possibly_handle_first_page_transition())
2979 else if (tok.nd->reread(&bol)) {
2984 curenv->add_node(tok.nd);
2987 curenv->possibly_break_line(1);
2991 case token::TOKEN_PAGE_EJECTOR:
2993 continue_page_eject();
2994 // I think we just want to preserve bol.
2998 case token::TOKEN_BEGIN_TRAP:
3000 trap_bol_stack.push(bol);
3005 case token::TOKEN_END_TRAP:
3007 if (trap_bol_stack.is_empty())
3008 error("spurious end trap token detected!");
3010 bol = trap_bol_stack.pop();
3013 /* I'm not totally happy about this. But I can't think of any other
3014 way to do it. Doing an output_pending_lines() whenever a
3015 TOKEN_END_TRAP is detected doesn't work: for example,
3028 a\%very\%very\%long\%word
3030 will print all but the first lines from the word immediately
3031 after the footer, rather than on the next page. */
3033 if (trap_bol_stack.is_empty())
3034 curenv->output_pending_lines();
3046 trap_sprung_flag = 0;
3050 #ifdef WIDOW_CONTROL
3052 void flush_pending_lines()
3054 while (!tok.newline() && !tok.eof())
3056 curenv->output_pending_lines();
3060 #endif /* WIDOW_CONTROL */
3062 request_or_macro::request_or_macro()
3066 macro *request_or_macro::to_macro()
3071 request::request(REQUEST_FUNCP pp) : p(pp)
3075 void request::invoke(symbol, int)
3081 enum { SIZE = 128 };
3082 unsigned char s[SIZE];
3087 char_block::char_block()
3096 void append(unsigned char);
3097 void set(unsigned char, int);
3098 unsigned char get(int);
3105 friend class macro_header;
3106 friend class string_iterator;
3109 char_list::char_list()
3110 : ptr(0), len(0), head(0), tail(0)
3114 char_list::~char_list()
3117 char_block *tem = head;
3123 int char_list::length()
3128 void char_list::append(unsigned char c)
3131 head = tail = new char_block;
3135 if (ptr >= tail->s + char_block::SIZE) {
3136 tail->next = new char_block;
3145 void char_list::set(unsigned char c, int offset)
3147 assert(len > offset);
3148 // optimization for access at the end
3149 int boundary = len - len % char_block::SIZE;
3150 if (offset >= boundary) {
3151 *(tail->s + offset - boundary) = c;
3154 char_block *tem = head;
3157 l += char_block::SIZE;
3159 *(tem->s + offset % char_block::SIZE) = c;
3166 unsigned char char_list::get(int offset)
3168 assert(len > offset);
3169 // optimization for access at the end
3170 int boundary = len - len % char_block::SIZE;
3171 if (offset >= boundary)
3172 return *(tail->s + offset - boundary);
3173 char_block *tem = head;
3176 l += char_block::SIZE;
3178 return *(tem->s + offset % char_block::SIZE);
3189 void append(node *);
3193 friend class macro_header;
3194 friend class string_iterator;
3197 void node_list::append(node *n)
3205 tail = tail->next = n;
3209 int node_list::length()
3212 for (node *n = head; n != 0; n = n->next)
3217 node_list::node_list()
3222 node *node_list::extract()
3229 node_list::~node_list()
3231 delete_node_list(head);
3234 class macro_header {
3239 macro_header() { count = 1; }
3240 macro_header *copy(int);
3245 if (p != 0 && --(p->count) <= 0)
3250 : is_a_diversion(0), is_a_string(1)
3252 if (!input_stack::get_location(1, &filename, &lineno)) {
3261 macro::macro(const macro &m)
3262 : filename(m.filename), lineno(m.lineno), len(m.len),
3263 empty_macro(m.empty_macro), is_a_diversion(m.is_a_diversion),
3264 is_a_string(m.is_a_string), p(m.p)
3270 macro::macro(int is_div)
3271 : is_a_diversion(is_div)
3273 if (!input_stack::get_location(1, &filename, &lineno)) {
3283 int macro::is_diversion()
3285 return is_a_diversion;
3288 int macro::is_string()
3293 void macro::clear_string_flag()
3298 macro ¯o::operator=(const macro &m)
3300 // don't assign object
3303 if (p != 0 && --(p->count) <= 0)
3306 filename = m.filename;
3309 empty_macro = m.empty_macro;
3310 is_a_diversion = m.is_a_diversion;
3311 is_a_string = m.is_a_string;
3315 void macro::append(unsigned char c)
3319 p = new macro_header;
3320 if (p->cl.length() != len) {
3321 macro_header *tem = p->copy(len);
3322 if (--(p->count) <= 0)
3328 if (c != PUSH_GROFF_MODE && c != PUSH_COMP_MODE && c != POP_GROFFCOMP_MODE)
3332 void macro::set(unsigned char c, int offset)
3336 p->cl.set(c, offset);
3339 unsigned char macro::get(int offset)
3342 return p->cl.get(offset);
3350 void macro::append_str(const char *s)
3355 while (s[i] != (char)0) {
3362 void macro::append(node *n)
3366 p = new macro_header;
3367 if (p->cl.length() != len) {
3368 macro_header *tem = p->copy(len);
3369 if (--(p->count) <= 0)
3379 void macro::append_unsigned(unsigned int i)
3381 unsigned int j = i / 10;
3384 append(((unsigned char)(((int)'0') + i % 10)));
3387 void macro::append_int(int i)
3393 append_unsigned((unsigned int)i);
3396 void macro::print_size()
3398 errprint("%1", len);
3401 // make a copy of the first n bytes
3403 macro_header *macro_header::copy(int n)
3405 macro_header *p = new macro_header;
3406 char_block *bp = cl.head;
3407 unsigned char *ptr = bp->s;
3410 if (ptr >= bp->s + char_block::SIZE) {
3414 unsigned char c = *ptr++;
3417 p->nl.append(nd->copy());
3426 object_dictionary_iterator iter(request_dictionary);
3427 request_or_macro *rm;
3429 while (iter.get(&s, (object **)&rm)) {
3430 assert(!s.is_null());
3431 macro *m = rm->to_macro();
3433 errprint("%1\t", s.contents());
3442 class string_iterator : public input_iterator {
3444 const char *how_invoked;
3448 int count; // of characters remaining
3450 int saved_compatible_flag;
3451 int with_break; // inherited from the caller
3456 string_iterator(const macro &, const char * = 0, symbol = NULL_SYMBOL);
3459 int get_location(int, const char **, int *);
3461 int get_break_flag() { return with_break; }
3462 void save_compatible_flag(int f) { saved_compatible_flag = f; }
3463 int get_compatible_flag() { return saved_compatible_flag; }
3467 string_iterator::string_iterator(const macro &m, const char *p, symbol s)
3468 : input_iterator(m.is_a_diversion), mac(m), how_invoked(p), newline_flag(0),
3473 bp = mac.p->cl.head;
3474 nd = mac.p->nl.head;
3482 with_break = input_stack::get_break_flag();
3485 string_iterator::string_iterator()
3494 with_break = input_stack::get_break_flag();
3497 int string_iterator::is_diversion()
3499 return mac.is_diversion();
3502 int string_iterator::fill(node **np)
3509 const unsigned char *p = eptr;
3510 if (p >= bp->s + char_block::SIZE) {
3518 (*np)->div_nest_level = input_stack::get_div_level();
3520 (*np)->div_nest_level = 0;
3527 const unsigned char *e = bp->s + char_block::SIZE;
3532 unsigned char c = *p;
3533 if (c == '\n' || c == ESCAPE_NEWLINE) {
3547 int string_iterator::peek()
3551 const unsigned char *p = eptr;
3552 if (p >= bp->s + char_block::SIZE) {
3558 int string_iterator::get_location(int allow_macro,
3559 const char **filep, int *linep)
3563 if (mac.filename == 0)
3565 *filep = mac.filename;
3566 *linep = mac.lineno + lineno - 1;
3570 void string_iterator::backtrace()
3573 errprint("%1:%2: backtrace", mac.filename, mac.lineno + lineno - 1);
3576 errprint(": %1 `%2'\n", how_invoked, nm.contents());
3578 errprint(": %1\n", how_invoked);
3585 class temp_iterator : public input_iterator {
3586 unsigned char *base;
3587 temp_iterator(const char *, int len);
3590 friend input_iterator *make_temp_iterator(const char *);
3596 temp_iterator::temp_iterator(const char *s, int len)
3598 base = new unsigned char[len];
3599 memcpy(base, s, len);
3604 temp_iterator::~temp_iterator()
3609 class small_temp_iterator : public input_iterator {
3611 small_temp_iterator(const char *, int);
3612 ~small_temp_iterator();
3613 enum { BLOCK = 16 };
3614 static small_temp_iterator *free_list;
3615 void *operator new(size_t);
3616 void operator delete(void *);
3618 unsigned char buf[SIZE];
3619 friend input_iterator *make_temp_iterator(const char *);
3622 small_temp_iterator *small_temp_iterator::free_list = 0;
3624 void *small_temp_iterator::operator new(size_t n)
3626 assert(n == sizeof(small_temp_iterator));
3629 (small_temp_iterator *)new char[sizeof(small_temp_iterator)*BLOCK];
3630 for (int i = 0; i < BLOCK - 1; i++)
3631 free_list[i].next = free_list + i + 1;
3632 free_list[BLOCK-1].next = 0;
3634 small_temp_iterator *p = free_list;
3635 free_list = (small_temp_iterator *)(free_list->next);
3643 void small_temp_iterator::operator delete(void *p)
3646 ((small_temp_iterator *)p)->next = free_list;
3647 free_list = (small_temp_iterator *)p;
3651 small_temp_iterator::~small_temp_iterator()
3658 small_temp_iterator::small_temp_iterator(const char *s, int len)
3660 for (int i = 0; i < len; i++)
3666 input_iterator *make_temp_iterator(const char *s)
3669 return new small_temp_iterator(s, 0);
3672 if (n <= small_temp_iterator::SIZE)
3673 return new small_temp_iterator(s, n);
3675 return new temp_iterator(s, n);
3679 // this is used when macros with arguments are interpolated
3685 arg_list(const macro &, int);
3686 arg_list(const arg_list *);
3690 arg_list::arg_list(const macro &m, int s) : mac(m), space_follows(s), next(0)
3694 arg_list::arg_list(const arg_list *al)
3698 space_follows = al->space_follows;
3699 arg_list **a = &next;
3700 arg_list *p = al->next;
3702 *a = new arg_list(p->mac, p->space_follows);
3708 arg_list::~arg_list()
3712 class macro_iterator : public string_iterator {
3715 int with_break; // whether called as .foo or 'foo
3717 macro_iterator(symbol, macro &, const char * = "macro", int = 0);
3720 int has_args() { return 1; }
3721 input_iterator *get_arg(int);
3722 arg_list *get_arg_list();
3723 symbol get_macro_name();
3724 int space_follows_arg(int);
3725 int get_break_flag() { return with_break; }
3726 int nargs() { return argc; }
3727 void add_arg(const macro &, int);
3729 int is_macro() { return 1; }
3733 input_iterator *macro_iterator::get_arg(int i)
3736 return make_temp_iterator(nm.contents());
3737 if (i > 0 && i <= argc) {
3739 for (int j = 1; j < i; j++) {
3743 return new string_iterator(p->mac);
3749 arg_list *macro_iterator::get_arg_list()
3754 symbol macro_iterator::get_macro_name()
3759 int macro_iterator::space_follows_arg(int i)
3761 if (i > 0 && i <= argc) {
3763 for (int j = 1; j < i; j++) {
3767 return p->space_follows;
3773 void macro_iterator::add_arg(const macro &m, int s)
3776 for (p = &args; *p; p = &((*p)->next))
3778 *p = new arg_list(m, s);
3782 void macro_iterator::shift(int n)
3784 while (n > 0 && argc > 0) {
3785 arg_list *tem = args;
3793 // This gets used by eg .if '\?xxx\?''.
3795 int operator==(const macro &m1, const macro &m2)
3797 if (m1.len != m2.len)
3799 string_iterator iter1(m1);
3800 string_iterator iter2(m2);
3804 int c1 = iter1.get(&nd1);
3807 int c2 = iter2.get(&nd2);
3819 int are_same = nd1->type() == nd2->type() && nd1->same(nd2);
3829 static void interpolate_macro(symbol nm, int no_next)
3831 request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm);
3834 const char *s = nm.contents();
3835 if (strlen(s) > 2) {
3836 request_or_macro *r;
3841 r = (request_or_macro *)request_dictionary.lookup(symbol(buf));
3843 macro *m = r->to_macro();
3844 if (!m || !m->empty())
3845 warned = warning(WARN_SPACE,
3846 "macro `%1' not defined "
3847 "(possibly missing space after `%2')",
3848 nm.contents(), buf);
3852 warning(WARN_MAC, "macro `%1' not defined", nm.contents());
3854 request_dictionary.define(nm, p);
3858 p->invoke(nm, no_next);
3865 static void decode_args(macro_iterator *mi)
3867 if (!tok.newline() && !tok.eof()) {
3869 int c = get_copy(&n);
3873 if (c == '\n' || c == EOF)
3876 int quote_input_level = 0;
3877 int done_tab_warning = 0;
3878 arg.append(compatible_flag ? PUSH_COMP_MODE : PUSH_GROFF_MODE);
3879 // we store discarded double quotes for \$^
3881 arg.append(DOUBLE_QUOTE);
3882 quote_input_level = input_stack::get_level();
3885 while (c != EOF && c != '\n' && !(c == ' ' && quote_input_level == 0)) {
3886 if (quote_input_level > 0 && c == '"'
3888 || input_stack::get_level() == quote_input_level)) {
3889 arg.append(DOUBLE_QUOTE);
3902 if (c == '\t' && quote_input_level == 0 && !done_tab_warning) {
3903 warning(WARN_TAB, "tab character in unquoted macro argument");
3904 done_tab_warning = 1;
3911 arg.append(POP_GROFFCOMP_MODE);
3912 mi->add_arg(arg, (c == ' '));
3917 static void decode_string_args(macro_iterator *mi)
3920 int c = get_copy(&n);
3924 if (c == '\n' || c == EOF) {
3925 error("missing `]'");
3931 int quote_input_level = 0;
3932 int done_tab_warning = 0;
3934 quote_input_level = input_stack::get_level();
3937 while (c != EOF && c != '\n'
3938 && !(c == ']' && quote_input_level == 0)
3939 && !(c == ' ' && quote_input_level == 0)) {
3940 if (quote_input_level > 0 && c == '"'
3941 && input_stack::get_level() == quote_input_level) {
3954 if (c == '\t' && quote_input_level == 0 && !done_tab_warning) {
3955 warning(WARN_TAB, "tab character in unquoted string argument");
3956 done_tab_warning = 1;
3963 mi->add_arg(arg, (c == ' '));
3967 void macro::invoke(symbol nm, int no_next)
3969 macro_iterator *mi = new macro_iterator(nm, *this);
3971 input_stack::push(mi);
3972 // we must delay tok.next() in case the function has been called by
3973 // do_request to assure proper handling of compatible_flag
3978 macro *macro::to_macro()
3985 return empty_macro == 1;
3988 macro_iterator::macro_iterator(symbol s, macro &m, const char *how_called,
3990 : string_iterator(m, how_called, s), args(0), argc(0), with_break(break_flag)
3993 arg_list *al = input_stack::get_arg_list();
3995 args = new arg_list(al);
3996 argc = input_stack::nargs();
4001 macro_iterator::macro_iterator() : args(0), argc(0), with_break(break_flag)
4005 macro_iterator::~macro_iterator()
4008 arg_list *tem = args;
4014 dictionary composite_dictionary(17);
4016 void composite_request()
4018 symbol from = get_name(1);
4019 if (!from.is_null()) {
4020 const char *from_gn = glyph_name_to_unicode(from.contents());
4022 from_gn = check_unicode_name(from.contents());
4024 error("invalid composite glyph name `%1'", from.contents());
4029 const char *from_decomposed = decompose_unicode(from_gn);
4030 if (from_decomposed)
4031 from_gn = &from_decomposed[1];
4032 symbol to = get_name(1);
4034 composite_dictionary.remove(symbol(from_gn));
4036 const char *to_gn = glyph_name_to_unicode(to.contents());
4038 to_gn = check_unicode_name(to.contents());
4040 error("invalid composite glyph name `%1'", to.contents());
4045 const char *to_decomposed = decompose_unicode(to_gn);
4047 to_gn = &to_decomposed[1];
4048 if (strcmp(from_gn, to_gn) == 0)
4049 composite_dictionary.remove(symbol(from_gn));
4051 (void)composite_dictionary.lookup(symbol(from_gn), (void *)to_gn);
4057 static symbol composite_glyph_name(symbol nm)
4059 macro_iterator *mi = new macro_iterator();
4060 decode_string_args(mi);
4061 input_stack::push(mi);
4062 const char *gn = glyph_name_to_unicode(nm.contents());
4064 gn = check_unicode_name(nm.contents());
4066 error("invalid base glyph `%1' in composite glyph name", nm.contents());
4067 return EMPTY_SYMBOL;
4070 const char *gn_decomposed = decompose_unicode(gn);
4071 string glyph_name(gn_decomposed ? &gn_decomposed[1] : gn);
4073 int n = input_stack::nargs();
4074 for (int i = 1; i <= n; i++) {
4076 input_iterator *p = input_stack::get_arg(i);
4079 while ((c = p->get(0)) != EOF)
4080 if (c != DOUBLE_QUOTE)
4083 const char *u = glyph_name_to_unicode(gl.contents());
4085 u = check_unicode_name(gl.contents());
4087 error("invalid component `%1' in composite glyph name",
4089 return EMPTY_SYMBOL;
4092 const char *decomposed = decompose_unicode(u);
4095 void *mapped_composite = composite_dictionary.lookup(symbol(u));
4096 if (mapped_composite)
4097 u = (const char *)mapped_composite;
4101 const char *groff_gn = unicode_to_glyph_name(glyph_name.contents());
4103 return symbol(groff_gn);
4107 return symbol(gl.contents());
4110 int trap_sprung_flag = 0;
4111 int postpone_traps_flag = 0;
4112 symbol postponed_trap;
4114 void spring_trap(symbol nm)
4116 assert(!nm.is_null());
4117 trap_sprung_flag = 1;
4118 if (postpone_traps_flag) {
4119 postponed_trap = nm;
4122 static char buf[2] = { BEGIN_TRAP, '\0' };
4123 static char buf2[2] = { END_TRAP, '\0' };
4124 input_stack::push(make_temp_iterator(buf2));
4125 request_or_macro *p = lookup_request(nm);
4126 macro *m = p->to_macro();
4128 input_stack::push(new macro_iterator(nm, *m, "trap-invoked macro"));
4130 error("you can't invoke a request with a trap");
4131 input_stack::push(make_temp_iterator(buf));
4134 void postpone_traps()
4136 postpone_traps_flag = 1;
4139 int unpostpone_traps()
4141 postpone_traps_flag = 0;
4142 if (!postponed_trap.is_null()) {
4143 spring_trap(postponed_trap);
4144 postponed_trap = NULL_SYMBOL;
4153 macro_iterator *mi = new macro_iterator;
4154 int reading_from_terminal = isatty(fileno(stdin));
4156 if (!tok.newline() && !tok.eof()) {
4157 int c = get_copy(0);
4160 while (c != EOF && c != '\n' && c != ' ') {
4161 if (!invalid_input_char(c)) {
4162 if (reading_from_terminal)
4173 if (reading_from_terminal) {
4174 fputc(had_prompt ? ':' : '\a', stderr);
4177 input_stack::push(mi);
4181 while ((c = getchar()) != EOF) {
4182 if (invalid_input_char(c))
4183 warning(WARN_INPUT, "invalid input character code %1", int(c));
4196 if (reading_from_terminal)
4198 input_stack::push(new string_iterator(mac));
4202 enum define_mode { DEFINE_NORMAL, DEFINE_APPEND, DEFINE_IGNORE };
4203 enum calling_mode { CALLING_NORMAL, CALLING_INDIRECT };
4204 enum comp_mode { COMP_IGNORE, COMP_DISABLE, COMP_ENABLE };
4206 void do_define_string(define_mode mode, comp_mode comp)
4209 node *n = 0; // pacify compiler
4220 else if (!tok.space()) {
4221 error("bad string definition");
4232 request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm);
4233 macro *mm = rm ? rm->to_macro() : 0;
4234 if (mode == DEFINE_APPEND && mm)
4236 if (comp == COMP_DISABLE)
4237 mac.append(PUSH_GROFF_MODE);
4238 else if (comp == COMP_ENABLE)
4239 mac.append(PUSH_COMP_MODE);
4240 while (c != '\n' && c != EOF) {
4244 mac.append((unsigned char)c);
4247 if (comp == COMP_DISABLE || comp == COMP_ENABLE)
4248 mac.append(POP_GROFFCOMP_MODE);
4251 request_dictionary.define(nm, mm);
4257 void define_string()
4259 do_define_string(DEFINE_NORMAL,
4260 compatible_flag ? COMP_ENABLE: COMP_IGNORE);
4263 void define_nocomp_string()
4265 do_define_string(DEFINE_NORMAL, COMP_DISABLE);
4268 void append_string()
4270 do_define_string(DEFINE_APPEND,
4271 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4274 void append_nocomp_string()
4276 do_define_string(DEFINE_APPEND, COMP_DISABLE);
4279 void do_define_character(char_mode mode, const char *font_name)
4281 node *n = 0; // pacify compiler
4284 charinfo *ci = tok.get_char(1);
4290 string s(font_name);
4292 s += ci->nm.contents();
4294 ci = get_charinfo(symbol(s.contents()));
4301 else if (!tok.space()) {
4302 error("bad character definition");
4308 while (c == ' ' || c == '\t')
4312 macro *m = new macro;
4313 while (c != '\n' && c != EOF) {
4317 m->append((unsigned char)c);
4320 m = ci->setx_macro(m, mode);
4326 void define_character()
4328 do_define_character(CHAR_NORMAL);
4331 void define_fallback_character()
4333 do_define_character(CHAR_FALLBACK);
4336 void define_special_character()
4338 do_define_character(CHAR_SPECIAL);
4341 static void remove_character()
4344 while (!tok.newline() && !tok.eof()) {
4345 if (!tok.space() && !tok.tab()) {
4346 charinfo *ci = tok.get_char(1);
4349 macro *m = ci->set_macro(0);
4358 static void interpolate_string(symbol nm)
4360 request_or_macro *p = lookup_request(nm);
4361 macro *m = p->to_macro();
4363 error("you can only invoke a string or macro using \\*");
4365 if (m->is_string()) {
4366 string_iterator *si = new string_iterator(*m, "string", nm);
4367 input_stack::push(si);
4370 // if a macro is called as a string, \$0 doesn't get changed
4371 macro_iterator *mi = new macro_iterator(input_stack::get_macro_name(),
4373 input_stack::push(mi);
4378 static void interpolate_string_with_args(symbol s)
4380 request_or_macro *p = lookup_request(s);
4381 macro *m = p->to_macro();
4383 error("you can only invoke a string or macro using \\*");
4385 macro_iterator *mi = new macro_iterator(s, *m);
4386 decode_string_args(mi);
4387 input_stack::push(mi);
4391 static void interpolate_arg(symbol nm)
4393 const char *s = nm.contents();
4394 if (!s || *s == '\0')
4395 copy_mode_error("missing argument name");
4396 else if (s[1] == 0 && csdigit(s[0]))
4397 input_stack::push(input_stack::get_arg(s[0] - '0'));
4398 else if (s[0] == '*' && s[1] == '\0') {
4399 int limit = input_stack::nargs();
4401 for (int i = 1; i <= limit; i++) {
4402 input_iterator *p = input_stack::get_arg(i);
4404 while ((c = p->get(0)) != EOF)
4405 if (c != DOUBLE_QUOTE)
4413 input_stack::push(make_temp_iterator(args.contents()));
4416 else if (s[0] == '@' && s[1] == '\0') {
4417 int limit = input_stack::nargs();
4419 for (int i = 1; i <= limit; i++) {
4421 args += char(BEGIN_QUOTE);
4422 input_iterator *p = input_stack::get_arg(i);
4424 while ((c = p->get(0)) != EOF)
4425 if (c != DOUBLE_QUOTE)
4427 args += char(END_QUOTE);
4435 input_stack::push(make_temp_iterator(args.contents()));
4438 else if (s[0] == '^' && s[1] == '\0') {
4439 int limit = input_stack::nargs();
4441 int c = input_stack::peek();
4442 for (int i = 1; i <= limit; i++) {
4443 input_iterator *p = input_stack::get_arg(i);
4444 while ((c = p->get(0)) != EOF) {
4445 if (c == DOUBLE_QUOTE)
4449 if (input_stack::space_follows_arg(i))
4455 input_stack::push(make_temp_iterator(args.contents()));
4460 for (p = s; *p && csdigit(*p); p++)
4463 copy_mode_error("bad argument name `%1'", s);
4465 input_stack::push(input_stack::get_arg(atoi(s)));
4469 void handle_first_page_transition()
4472 topdiv->begin_page();
4475 // We push back a token by wrapping it up in a token_node, and
4476 // wrapping that up in a string_iterator.
4478 static void push_token(const token &t)
4481 m.append(new token_node(t));
4482 input_stack::push(new string_iterator(m));
4485 void push_page_ejector()
4487 static char buf[2] = { PAGE_EJECTOR, '\0' };
4488 input_stack::push(make_temp_iterator(buf));
4491 void handle_initial_request(unsigned char code)
4497 mac.append(new token_node(tok));
4498 input_stack::push(new string_iterator(mac));
4499 input_stack::push(make_temp_iterator(buf));
4500 topdiv->begin_page();
4504 void handle_initial_title()
4506 handle_initial_request(TITLE_REQUEST);
4509 // this should be local to define_macro, but cfront 1.2 doesn't support that
4510 static symbol dot_symbol(".");
4512 void do_define_macro(define_mode mode, calling_mode calling, comp_mode comp)
4515 if (calling == CALLING_INDIRECT) {
4516 symbol temp1 = get_name(1);
4517 if (temp1.is_null()) {
4521 symbol temp2 = get_name();
4522 input_stack::push(make_temp_iterator("\n"));
4523 if (!temp2.is_null()) {
4524 interpolate_string(temp2);
4525 input_stack::push(make_temp_iterator(" "));
4527 interpolate_string(temp1);
4528 input_stack::push(make_temp_iterator(" "));
4531 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4538 term = get_name(); // the request that terminates the definition
4541 while (!tok.newline() && !tok.eof())
4543 const char *start_filename;
4545 int have_start_location = input_stack::get_location(0, &start_filename,
4548 // doing this here makes the line numbers come out right
4549 int c = get_copy(&n, 1);
4552 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4553 request_or_macro *rm =
4554 (request_or_macro *)request_dictionary.lookup(nm);
4556 mm = rm->to_macro();
4557 if (mm && mode == DEFINE_APPEND)
4561 if (comp == COMP_DISABLE)
4562 mac.append(PUSH_GROFF_MODE);
4563 else if (comp == COMP_ENABLE)
4564 mac.append(PUSH_COMP_MODE);
4567 mac.clear_string_flag();
4568 while (c == ESCAPE_NEWLINE) {
4569 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND)
4571 c = get_copy(&n, 1);
4573 if (bol && c == '.') {
4574 const char *s = term.contents();
4576 // see if it matches term
4579 while ((d = get_copy(&n)) == ' ' || d == '\t')
4581 if ((unsigned char)s[0] == d) {
4582 for (i = 1; s[i] != 0; i++) {
4584 if ((unsigned char)s[i] != d)
4590 && ((i == 2 && compatible_flag)
4591 || (d = get_copy(&n)) == ' '
4592 || d == '\n')) { // we found it
4597 if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) {
4600 request_dictionary.define(nm, mm);
4602 if (comp == COMP_DISABLE || comp == COMP_ENABLE)
4603 mac.append(POP_GROFFCOMP_MODE);
4606 if (term != dot_symbol) {
4608 interpolate_macro(term);
4614 if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) {
4616 for (int j = 0; j < i; j++)
4622 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4623 if (have_start_location)
4624 error_with_file_and_line(start_filename, start_lineno,
4625 "end of file while defining macro `%1'",
4628 error("end of file while defining macro `%1'", nm.contents());
4631 if (have_start_location)
4632 error_with_file_and_line(start_filename, start_lineno,
4633 "end of file while ignoring input lines");
4635 error("end of file while ignoring input lines");
4640 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4647 c = get_copy(&n, 1);
4653 do_define_macro(DEFINE_NORMAL, CALLING_NORMAL,
4654 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4657 void define_nocomp_macro()
4659 do_define_macro(DEFINE_NORMAL, CALLING_NORMAL, COMP_DISABLE);
4662 void define_indirect_macro()
4664 do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT,
4665 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4668 void define_indirect_nocomp_macro()
4670 do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT, COMP_DISABLE);
4675 do_define_macro(DEFINE_APPEND, CALLING_NORMAL,
4676 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4679 void append_nocomp_macro()
4681 do_define_macro(DEFINE_APPEND, CALLING_NORMAL, COMP_DISABLE);
4684 void append_indirect_macro()
4686 do_define_macro(DEFINE_APPEND, CALLING_INDIRECT,
4687 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4690 void append_indirect_nocomp_macro()
4692 do_define_macro(DEFINE_APPEND, CALLING_INDIRECT, COMP_DISABLE);
4698 do_define_macro(DEFINE_IGNORE, CALLING_NORMAL, COMP_IGNORE);
4705 symbol s = get_name();
4708 request_dictionary.remove(s);
4715 symbol s1 = get_name(1);
4716 if (!s1.is_null()) {
4717 symbol s2 = get_name(1);
4719 request_dictionary.rename(s1, s2);
4726 symbol s1 = get_name(1);
4727 if (!s1.is_null()) {
4728 symbol s2 = get_name(1);
4729 if (!s2.is_null()) {
4730 if (!request_dictionary.alias(s1, s2))
4731 warning(WARN_MAC, "macro `%1' not defined", s2.contents());
4739 symbol s = get_name(1);
4741 request_or_macro *p = lookup_request(s);
4742 macro *m = p->to_macro();
4744 error("cannot chop request");
4745 else if (m->empty())
4746 error("cannot chop empty macro");
4748 int have_restore = 0;
4749 // we have to check for additional save/restore pairs which could be
4750 // there due to empty am1 requests.
4752 if (m->get(m->len - 1) != POP_GROFFCOMP_MODE)
4756 if (m->get(m->len - 1) != PUSH_GROFF_MODE
4757 && m->get(m->len - 1) != PUSH_COMP_MODE)
4765 error("cannot chop empty macro");
4768 m->set(POP_GROFFCOMP_MODE, m->len - 1);
4777 void substring_request()
4779 int start; // 0, 1, ..., n-1 or -1, -2, ...
4780 symbol s = get_name(1);
4781 if (!s.is_null() && get_integer(&start)) {
4782 request_or_macro *p = lookup_request(s);
4783 macro *m = p->to_macro();
4785 error("cannot apply `substring' on a request");
4788 if (!has_arg() || get_integer(&end)) {
4789 int real_length = 0; // 1, 2, ..., n
4790 string_iterator iter1(*m);
4791 for (int l = 0; l < m->len; l++) {
4792 int c = iter1.get(0);
4793 if (c == PUSH_GROFF_MODE
4794 || c == PUSH_COMP_MODE
4795 || c == POP_GROFFCOMP_MODE)
4802 start += real_length;
4810 if (start >= real_length || end < 0) {
4812 "start and end index of substring out of range");
4815 if (--(m->p->count) <= 0)
4824 "start index of substring out of range, set to 0");
4827 if (end >= real_length) {
4829 "end index of substring out of range, set to string length");
4830 end = real_length - 1;
4832 // now extract the substring
4833 string_iterator iter(*m);
4835 for (i = 0; i < start; i++) {
4836 int c = iter.get(0);
4837 while (c == PUSH_GROFF_MODE
4838 || c == PUSH_COMP_MODE
4839 || c == POP_GROFFCOMP_MODE)
4845 for (; i <= end; i++) {
4846 node *nd = 0; // pacify compiler
4847 int c = iter.get(&nd);
4848 while (c == PUSH_GROFF_MODE
4849 || c == PUSH_COMP_MODE
4850 || c == POP_GROFFCOMP_MODE)
4857 mac.append((unsigned char)c);
4866 void length_request()
4870 if (ret.is_null()) {
4880 else if (!tok.space()) {
4881 error("bad string definition");
4892 while (c != '\n' && c != EOF) {
4896 reg *r = (reg*)number_reg_dictionary.lookup(ret);
4900 set_number_reg(ret, len);
4904 void asciify_macro()
4906 symbol s = get_name(1);
4908 request_or_macro *p = lookup_request(s);
4909 macro *m = p->to_macro();
4911 error("cannot asciify request");
4914 string_iterator iter(*m);
4916 node *nd = 0; // pacify compiler
4917 int c = iter.get(&nd);
4931 void unformat_macro()
4933 symbol s = get_name(1);
4935 request_or_macro *p = lookup_request(s);
4936 macro *m = p->to_macro();
4938 error("cannot unformat request");
4941 string_iterator iter(*m);
4943 node *nd = 0; // pacify compiler
4944 int c = iter.get(&nd);
4950 if (nd->set_unformat_flag())
4960 static void interpolate_environment_variable(symbol nm)
4962 const char *s = getenv(nm.contents());
4964 input_stack::push(make_temp_iterator(s));
4967 void interpolate_number_reg(symbol nm, int inc)
4969 reg *r = lookup_number_reg(nm);
4974 input_stack::push(make_temp_iterator(r->get_string()));
4977 static void interpolate_number_format(symbol nm)
4979 reg *r = (reg *)number_reg_dictionary.lookup(nm);
4981 input_stack::push(make_temp_iterator(r->get_format()));
4984 static int get_delim_number(units *n, unsigned char si, int prev_value)
4988 if (start.delimiter(1)) {
4990 if (get_number(n, si, prev_value)) {
4992 warning(WARN_DELIM, "closing delimiter does not match");
4999 static int get_delim_number(units *n, unsigned char si)
5003 if (start.delimiter(1)) {
5005 if (get_number(n, si)) {
5007 warning(WARN_DELIM, "closing delimiter does not match");
5014 static int get_line_arg(units *n, unsigned char si, charinfo **cp)
5018 int start_level = input_stack::get_level();
5019 if (!start.delimiter(1))
5022 if (get_number(n, si)) {
5023 if (tok.dummy() || tok.transparent_dummy())
5025 if (!(start == tok && input_stack::get_level() == start_level)) {
5026 *cp = tok.get_char(1);
5029 if (!(start == tok && input_stack::get_level() == start_level))
5030 warning(WARN_DELIM, "closing delimiter does not match");
5036 static int read_size(int *x)
5046 else if (c == '+') {
5051 int val = 0; // pacify compiler
5057 // allow an increment either before or after the left parenthesis
5063 else if (c == '+') {
5078 val = val*10 + (c - '0');
5083 else if (csdigit(c)) {
5085 if (!inc && c != '0' && c < '4') {
5091 val = val*10 + (c - '0');
5095 else if (!tok.delimiter(1))
5101 if (!inc && (c == '-' || c == '+')) {
5102 inc = c == '+' ? 1 : -1;
5105 if (!get_number(&val, 'z'))
5107 if (!(start.ch() == '[' && tok.ch() == ']') && start != tok) {
5108 if (start.ch() == '[')
5109 error("missing `]'");
5111 error("missing closing delimiter");
5119 // special case -- \s[0] and \s0 means to revert to previous size
5126 *x = curenv->get_requested_point_size() + val;
5129 *x = curenv->get_requested_point_size() - val;
5136 "\\s escape results in non-positive point size; set to 1");
5142 error("bad digit in point size");
5147 static symbol get_delim_name()
5152 error("end of input at start of delimited name");
5155 if (start.newline()) {
5156 error("can't delimit name with a newline");
5159 int start_level = input_stack::get_level();
5160 char abuf[ABUF_SIZE];
5162 int buf_size = ABUF_SIZE;
5165 if (i + 1 > buf_size) {
5167 buf = new char[ABUF_SIZE*2];
5168 memcpy(buf, abuf, buf_size);
5169 buf_size = ABUF_SIZE*2;
5172 char *old_buf = buf;
5173 buf = new char[buf_size*2];
5174 memcpy(buf, old_buf, buf_size);
5181 && (compatible_flag || input_stack::get_level() == start_level))
5183 if ((buf[i] = tok.ch()) == 0) {
5184 error("missing delimiter (got %1)", tok.description());
5194 error("empty delimited name");
5209 static void do_register()
5213 if (!start.delimiter(1))
5216 symbol nm = get_long_name(1);
5221 reg *r = (reg *)number_reg_dictionary.lookup(nm);
5223 if (!r || !r->get_value(&prev_value))
5226 if (!get_number(&val, 'u', prev_value))
5229 warning(WARN_DELIM, "closing delimiter does not match");
5233 set_number_reg(nm, val);
5236 // this implements the \w escape sequence
5238 static void do_width()
5242 int start_level = input_stack::get_level();
5243 environment env(curenv);
5244 environment *oldenv = curenv;
5249 warning(WARN_DELIM, "missing closing delimiter");
5252 if (tok.newline()) {
5253 warning(WARN_DELIM, "missing closing delimiter");
5254 input_stack::push(make_temp_iterator("\n"));
5258 && (compatible_flag || input_stack::get_level() == start_level))
5263 units x = env.get_input_line_position().to_units();
5264 input_stack::push(make_temp_iterator(i_to_a(x)));
5265 env.width_registers();
5270 charinfo *page_character;
5272 void set_page_character()
5274 page_character = get_optional_char();
5278 static const symbol percent_symbol("%");
5280 void read_title_parts(node **part, hunits *part_width)
5283 if (tok.newline() || tok.eof())
5286 int start_level = input_stack::get_level();
5288 for (int i = 0; i < 3; i++) {
5289 while (!tok.newline() && !tok.eof()) {
5291 && (compatible_flag || input_stack::get_level() == start_level)) {
5295 if (page_character != 0 && tok.get_char() == page_character)
5296 interpolate_number_reg(percent_symbol, 0);
5301 curenv->wrap_up_tab();
5302 part_width[i] = curenv->get_input_line_position();
5303 part[i] = curenv->extract_output_line();
5305 while (!tok.newline() && !tok.eof())
5309 class non_interpreted_node : public node {
5312 non_interpreted_node(const macro &);
5313 int interpret(macro *);
5315 int ends_sentence();
5322 non_interpreted_node::non_interpreted_node(const macro &m) : mac(m)
5326 int non_interpreted_node::ends_sentence()
5331 int non_interpreted_node::same(node *nd)
5333 return mac == ((non_interpreted_node *)nd)->mac;
5336 const char *non_interpreted_node::type()
5338 return "non_interpreted_node";
5341 int non_interpreted_node::force_tprint()
5346 int non_interpreted_node::is_tag()
5351 node *non_interpreted_node::copy()
5353 return new non_interpreted_node(mac);
5356 int non_interpreted_node::interpret(macro *m)
5358 string_iterator si(mac);
5359 node *n = 0; // pacify compiler
5372 static node *do_non_interpreted()
5377 while ((c = get_copy(&n)) != ESCAPE_QUESTION && c != EOF && c != '\n')
5382 if (c == EOF || c == '\n') {
5383 error("missing \\?");
5386 return new non_interpreted_node(mac);
5389 static void encode_char(macro *mac, char c)
5392 if ((font::use_charnames_in_special) && tok.special()) {
5393 charinfo *ci = tok.get_char(1);
5394 const char *s = ci->get_symbol()->contents();
5395 if (s[0] != (char)0) {
5399 while (s[i] != (char)0) {
5406 else if (tok.stretchable_space()
5407 || tok.unstretchable_space())
5409 else if (!(tok.hyphen_indicator()
5411 || tok.transparent_dummy()
5412 || tok.zero_width_break()))
5413 error("%1 is invalid within \\X", tok.description());
5416 if ((font::use_charnames_in_special) && (c == '\\')) {
5418 * add escape escape sequence
5430 int start_level = input_stack::get_level();
5433 tok != start || input_stack::get_level() != start_level;
5436 warning(WARN_DELIM, "missing closing delimiter");
5439 if (tok.newline()) {
5440 input_stack::push(make_temp_iterator("\n"));
5441 warning(WARN_DELIM, "missing closing delimiter");
5449 else if (tok.leader())
5451 else if (tok.backspace())
5455 encode_char(&mac, c);
5457 return new special_node(mac);
5460 void device_request()
5462 if (!tok.newline() && !tok.eof()) {
5471 if (c != ' ' && c != '\t')
5474 for (; c != '\n' && c != EOF; c = get_copy(0))
5476 curenv->add_node(new special_node(mac));
5481 void device_macro_request()
5483 symbol s = get_name(1);
5484 if (!(s.is_null() || s.is_empty())) {
5485 request_or_macro *p = lookup_request(s);
5486 macro *m = p->to_macro();
5488 curenv->add_node(new special_node(*m));
5490 error("can't transparently throughput a request");
5495 void output_request()
5497 if (!tok.newline() && !tok.eof()) {
5505 if (c != ' ' && c != '\t')
5508 for (; c != '\n' && c != EOF; c = get_copy(0))
5509 topdiv->transparent_output(c);
5510 topdiv->transparent_output('\n');
5515 extern int image_no; // from node.cpp
5517 static node *do_suppress(symbol nm)
5519 if (nm.is_null() || nm.is_empty()) {
5520 error("expecting an argument to escape \\O");
5523 const char *s = nm.contents();
5526 if (begin_level == 0)
5527 // suppress generation of glyphs
5528 return new suppress_node(0, 0);
5531 if (begin_level == 0)
5532 // enable generation of glyphs
5533 return new suppress_node(1, 0);
5536 if (begin_level == 0)
5537 return new suppress_node(1, 1);
5549 s++; // move over '5'
5551 if (*s == (char)0) {
5552 error("missing position and filename in \\O");
5555 if (!(position == 'l'
5558 || position == 'i')) {
5559 error("l, r, c, or i position expected (got %1 in \\O)", position);
5562 s++; // onto image name
5563 if (s == (char *)0) {
5564 error("missing image name for \\O");
5568 if (begin_level == 0)
5569 return new suppress_node(symbol(s), position, image_no);
5575 error("`%1' is an invalid argument to \\O", *s);
5580 void special_node::tprint(troff_output_file *out)
5583 string_iterator iter(mac);
5585 int c = iter.get(0);
5588 for (const char *s = ::asciify(c); *s; s++)
5589 tprint_char(out, *s);
5594 int get_file_line(const char **filename, int *lineno)
5596 return input_stack::get_location(0, filename, lineno);
5602 if (get_integer(&n)) {
5603 const char *filename = 0;
5605 symbol s = get_long_name();
5606 filename = s.contents();
5608 (void)input_stack::set_location(filename, n-1);
5613 static int nroff_mode = 0;
5615 static void nroff_request()
5621 static void troff_request()
5627 static void skip_alternative()
5630 // ensure that ``.if 0\{'' works as expected
5631 if (tok.left_brace())
5635 c = input_stack::get(0);
5638 if (c == ESCAPE_LEFT_BRACE)
5640 else if (c == ESCAPE_RIGHT_BRACE)
5642 else if (c == escape_char && escape_char > 0)
5643 switch(input_stack::get(0)) {
5651 while ((c = input_stack::get(0)) != '\n' && c != EOF)
5655 Note that the level can properly be < 0, eg
5661 So don't give an error message in this case.
5663 if (level <= 0 && c == '\n')
5669 static void begin_alternative()
5671 while (tok.space() || tok.left_brace())
5681 static int_stack if_else_stack;
5688 while (tok.ch() == '!') {
5693 unsigned char c = tok.ch();
5696 result = !nroff_mode;
5698 else if (c == 'n') {
5700 result = nroff_mode;
5702 else if (c == 'v') {
5706 else if (c == 'o') {
5707 result = (topdiv->get_page_number() & 1);
5710 else if (c == 'e') {
5711 result = !(topdiv->get_page_number() & 1);
5714 else if (c == 'd' || c == 'r') {
5716 symbol nm = get_name(1);
5722 ? request_dictionary.lookup(nm) != 0
5723 : number_reg_dictionary.lookup(nm) != 0);
5725 else if (c == 'm') {
5727 symbol nm = get_long_name(1);
5732 result = (nm == default_symbol
5733 || color_dictionary.lookup(nm) != 0);
5735 else if (c == 'c') {
5738 charinfo *ci = tok.get_char(1);
5743 result = character_exists(ci, curenv);
5746 else if (c == 'F') {
5748 symbol nm = get_long_name(1);
5753 result = check_font(curenv->get_family()->nm, nm);
5755 else if (c == 'S') {
5757 symbol nm = get_long_name(1);
5762 result = check_style(nm);
5764 else if (tok.space())
5766 else if (tok.delimiter()) {
5768 int delim_level = input_stack::get_level();
5769 environment env1(curenv);
5770 environment env2(curenv);
5771 environment *oldenv = curenv;
5774 for (int i = 0; i < 2; i++) {
5777 if (tok.newline() || tok.eof()) {
5778 warning(WARN_DELIM, "missing closing delimiter");
5784 && (compatible_flag || input_stack::get_level() == delim_level))
5790 node *n1 = env1.extract_output_line();
5791 node *n2 = env2.extract_output_line();
5792 result = same_node_list(n1, n2);
5793 delete_node_list(n1);
5794 delete_node_list(n2);
5802 if (!get_number(&n, 'u')) {
5812 begin_alternative();
5818 void if_else_request()
5820 if_else_stack.push(do_if_request());
5830 if (if_else_stack.is_empty()) {
5831 warning(WARN_EL, "unbalanced .el request");
5835 if (if_else_stack.pop())
5838 begin_alternative();
5842 static int while_depth = 0;
5843 static int while_break_flag = 0;
5845 void while_request()
5850 mac.append(new token_node(tok));
5852 node *n = 0; // pacify compiler
5853 int c = input_stack::get(&n);
5869 if (c == ESCAPE_LEFT_BRACE)
5871 else if (c == ESCAPE_RIGHT_BRACE)
5873 else if (c == escape_char)
5876 if (c == '\n' && level <= 0)
5881 error("unbalanced \\{ \\}");
5884 input_stack::add_boundary();
5886 input_stack::push(new string_iterator(mac, "while loop"));
5888 if (!do_if_request()) {
5889 while (input_stack::get(0) != EOF)
5893 process_input_stack();
5894 if (while_break_flag || input_stack::is_return_boundary()) {
5895 while_break_flag = 0;
5899 input_stack::remove_boundary();
5905 void while_break_request()
5908 error("no while loop");
5912 while_break_flag = 1;
5913 while (input_stack::get(0) != EOF)
5919 void while_continue_request()
5922 error("no while loop");
5926 while (input_stack::get(0) != EOF)
5936 symbol nm = get_long_name(1);
5940 while (!tok.newline() && !tok.eof())
5943 FILE *fp = include_search_path.open_file_cautious(nm.contents());
5945 input_stack::push(new file_iterator(fp, nm.contents()));
5947 error("can't open `%1': %2", nm.contents(), strerror(errno));
5952 // like .so but use popen()
5957 error(".pso request not allowed in safer mode");
5961 #ifdef POPEN_MISSING
5962 error("pipes not available on this system");
5964 #else /* not POPEN_MISSING */
5965 if (tok.newline() || tok.eof())
5966 error("missing command");
5969 while ((c = get_copy(0)) == ' ' || c == '\t')
5972 char *buf = new char[buf_size];
5974 for (; c != '\n' && c != EOF; c = get_copy(0)) {
5975 const char *s = asciify(c);
5976 int slen = strlen(s);
5977 if (buf_used + slen + 1> buf_size) {
5978 char *old_buf = buf;
5979 int old_buf_size = buf_size;
5981 buf = new char[buf_size];
5982 memcpy(buf, old_buf, old_buf_size);
5985 strcpy(buf + buf_used, s);
5988 buf[buf_used] = '\0';
5990 FILE *fp = popen(buf, POPEN_RT);
5992 input_stack::push(new file_iterator(fp, symbol(buf).contents(), 1));
5994 error("can't open pipe to process `%1': %2", buf, strerror(errno));
5998 #endif /* not POPEN_MISSING */
6004 // Extract bounding box limits from PostScript file, and assign
6005 // them to the following four gtroff registers:--
6007 static int llx_reg_contents = 0;
6008 static int lly_reg_contents = 0;
6009 static int urx_reg_contents = 0;
6010 static int ury_reg_contents = 0;
6012 // Manifest constants to specify the status of bounding box range
6013 // acquisition; (note that PSBB_RANGE_IS_BAD is also suitable for
6014 // assignment as a default ordinate property value).
6016 #define PSBB_RANGE_IS_BAD 0
6017 #define PSBB_RANGE_IS_SET 1
6018 #define PSBB_RANGE_AT_END 2
6020 // Maximum input line length, for DSC conformance, and options to
6021 // control how it will be enforced; caller should select either of
6022 // DSC_LINE_MAX_IGNORED, to allow partial line collection spread
6023 // across multiple calls, or DSC_LINE_MAX_ENFORCE, to truncate
6024 // excess length lines at the DSC limit.
6026 // Note that DSC_LINE_MAX_CHECKED is reserved for internal use by
6027 // ps_locator::get_line(), and should not be specified in any call;
6028 // also, handling of DSC_LINE_MAX_IGNORED, as a get_line() option,
6029 // is currently unimplemented.
6031 #define DSC_LINE_MAX 255
6032 #define DSC_LINE_MAX_IGNORED -1
6033 #define DSC_LINE_MAX_ENFORCE 0
6034 #define DSC_LINE_MAX_CHECKED 1
6036 // Input characters to be considered as white space, when reading
6037 // PostScript file comments.
6039 cset white_space("\n\r \t");
6041 // Class psbb_locator
6043 // This locally declared and implemented class provides the methods
6044 // to be used for retrieval of bounding box properties from a specified
6045 // PostScript or PDF file.
6050 // Only the class constructor is exposed publicly; instantiation of
6051 // a class object will retrieve the requisite bounding box properties
6052 // from the specified file, and assign them to gtroff registers.
6054 psbb_locator(const char *);
6058 const char *filename;
6059 char buf[2 + DSC_LINE_MAX];
6060 int llx, lly, urx, ury;
6062 // CRLF handling hook, for get_line() function.
6066 // Private method functions facilitate implementation of the
6067 // class constructor; none are used in any other context.
6070 inline bool get_header_comment(void);
6071 inline const char *context_args(const char *);
6072 inline const char *context_args(const char *, const char *);
6073 inline const char *bounding_box_args(void);
6074 int parse_bounding_box(const char *);
6075 inline void assign_registers(void);
6076 inline int skip_to_trailer(void);
6079 // psbb_locator class constructor.
6081 psbb_locator::psbb_locator(const char *fname):
6082 filename(fname), llx(0), lly(0), urx(0), ury(0), lastc(EOF)
6084 // PS files might contain non-printable characters, such as ^Z
6085 // and CRs not followed by an LF, so open them in binary mode.
6087 fp = include_search_path.open_file_cautious(filename, 0, FOPEN_RB);
6089 // After successfully opening the file, acquire the first
6090 // line, whence we may determine the file format...
6092 if (get_line(DSC_LINE_MAX_ENFORCE) == 0)
6094 // ...except in the case of an empty file, which we are
6095 // unable to process further.
6097 error("`%1' is empty", filename);
6100 else if (context_args("%PDF-")) {
6101 // TODO: PDF files specify a /MediaBox, as the equivalent
6102 // of %%BoundingBox; we must implement a handler for this.
6106 else if (context_args("%!PS-Adobe-")) {
6108 // PostScript files -- strictly, we expect EPS -- should
6109 // specify a %%BoundingBox comment; locate it, initially
6110 // expecting to find it in the comments header...
6112 const char *context = NULL;
6113 while ((context == NULL) && get_header_comment()) {
6114 if ((context = bounding_box_args()) != NULL) {
6116 // When the "%%BoundingBox" comment is found, it may simply
6117 // specify the bounding box property values, or it may defer
6118 // assignment to a similar trailer comment...
6120 int status = parse_bounding_box(context);
6121 if (status == PSBB_RANGE_AT_END) {
6123 // ...in which case we must locate the trailer, and search
6124 // for the appropriate specification within it.
6126 if (skip_to_trailer() > 0) {
6127 while ((context = bounding_box_args()) == NULL
6128 && get_line(DSC_LINE_MAX_ENFORCE) > 0)
6130 if (context != NULL) {
6132 // When we find a bounding box specification here...
6134 if ((status = parse_bounding_box(context)) == PSBB_RANGE_AT_END)
6136 // ...we must ensure it is not a further attempt to defer
6137 // assignment to a trailer, (which we are already parsing).
6139 error("`(atend)' not allowed in trailer of `%1'", filename);
6143 // The trailer could not be found, so there is no context in
6144 // which a trailing %%BoundingBox comment might be located.
6148 if (status == PSBB_RANGE_IS_BAD) {
6150 // This arises when we found a %%BoundingBox comment, but
6151 // we were unable to extract a valid set of range values from
6152 // it; all we can do is diagnose this.
6154 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
6159 if (context == NULL)
6161 // Conversely, this arises when no value specifying %%BoundingBox
6162 // comment has been found, in any appropriate location...
6164 error("%%%%BoundingBox comment not found in `%1'", filename);
6167 // ...while this indicates that there was no appropriate file format
6168 // identifier, on the first line of the input file.
6170 error("`%1' does not conform to the Document Structuring Conventions",
6173 // Regardless of success or failure of bounding box property acquisition,
6174 // we did successfully open an input file, so we must now close it...
6179 // ...but in this case, we did not successfully open any input file.
6181 error("can't open `%1': %2", filename, strerror(errno));
6183 // Irrespective of whether or not we were able to successfully acquire the
6184 // bounding box properties, we ALWAYS update the associated gtroff registers.
6189 // psbb_locator::parse_bounding_box()
6191 // Parse the argument to a %%BoundingBox comment, returning:
6192 // PSBB_RANGE_IS_SET if it contains four numbers,
6193 // PSBB_RANGE_AT_END if it contains "(atend)", or
6194 // PSBB_RANGE_IS_BAD otherwise.
6196 int psbb_locator::parse_bounding_box(const char *context)
6198 // The Document Structuring Conventions say that the numbers
6199 // should be integers.
6201 int status = PSBB_RANGE_IS_SET;
6202 if (sscanf(context, "%d %d %d %d", &llx, &lly, &urx, &ury) != 4) {
6204 // Unfortunately some broken applications get this wrong;
6205 // try to parse them as doubles instead...
6207 double x1, x2, x3, x4;
6208 if (sscanf(context, "%lf %lf %lf %lf", &x1, &x2, &x3, &x4) == 4) {
6215 // ...but if we can't parse four numbers, skip over any
6216 // initial white space...
6218 while (*context == '\x20' || *context == '\t')
6221 // ...before checking for "(atend)", and setting the
6222 // appropriate exit status accordingly.
6224 status = (context_args("(atend)", context) == NULL)
6225 ? llx = lly = urx = ury = PSBB_RANGE_IS_BAD
6226 : PSBB_RANGE_AT_END;
6232 // ps_locator::get_line()
6234 // Collect an input record from a PostScript or PDF file.
6237 // buf pointer to caller's input buffer.
6238 // fp FILE stream pointer, whence input is read.
6239 // filename name of input file, (for diagnostic use only).
6240 // dscopt DSC_LINE_MAX_ENFORCE or DSC_LINE_MAX_IGNORED.
6242 // Returns the number of input characters stored into caller's
6243 // buffer, or zero at end of input stream.
6245 // FIXME: Currently, get_line() always scans an entire line of
6246 // input, but returns only as much as will fit in caller's buffer;
6247 // the return value is always a positive integer, or zero, with no
6248 // way of indicating to caller, that there was more data than the
6249 // buffer could accommodate. A future enhancement could mitigate
6250 // this, returning a negative value in the event of truncation, or
6251 // even allowing for piecewise retrieval of excessively long lines
6252 // in successive reads; (this may be necessary to properly support
6253 // DSC_LINE_MAX_IGNORED, which is currently unimplemented).
6255 int psbb_locator::get_line(int dscopt)
6259 // Collect input characters into caller's buffer, until we
6260 // encounter a line terminator, or end of file...
6262 while (((c = getc(fp)) != '\n') && (c != '\r') && (c != EOF)) {
6263 if ((((lastc = c) < 0x1b) && !white_space(c)) || (c == 0x7f))
6265 // ...rejecting any which may be designated as invalid.
6267 error("invalid input character code %1 in `%2'", int(c), filename);
6269 // On reading a valid input character, and when there is
6270 // room in caller's buffer...
6272 else if (count < DSC_LINE_MAX)
6278 // We have a valid input character, but it will not fit
6279 // into caller's buffer; if enforcing DSC conformity...
6281 else if (dscopt == DSC_LINE_MAX_ENFORCE) {
6283 // ...diagnose and truncate.
6285 dscopt = DSC_LINE_MAX_CHECKED;
6286 error("PostScript file `%1' is non-conforming "
6287 "because length of line exceeds 255", filename);
6290 // Reading LF may be a special case: when it immediately
6291 // follows a CR which terminated the preceding input line,
6292 // we deem it to complete a CRLF terminator for the already
6293 // collected preceding line; discard it, and restart input
6294 // collection for the current line.
6296 } while ((lastc == '\r') && ((lastc = c) == '\n'));
6298 // For each collected input line, record its actual terminator,
6299 // substitute our preferred LF terminator...
6301 if (((lastc = c) != EOF) || (count > 0))
6302 buf[count++] = '\n';
6304 // ...and append the required C-string (NUL) terminator, before
6305 // returning the actual count of input characters stored.
6311 // psbb_locator::context_args()
6314 // tag literal text to be matched at start of input line
6316 // Returns a pointer to the trailing substring of the current
6317 // input line, following an initial substring matching the "tag"
6318 // argument, or NULL if "tag" is not matched.
6320 inline const char *psbb_locator::context_args(const char *tag)
6322 return context_args(tag, buf);
6325 // psbb_locator::context_args()
6327 // Overloaded variant of the preceding function, operating on
6328 // an alternative input buffer, (which may represent a terminal
6329 // substring of the psbb_locator's primary input line buffer).
6332 // tag literal text to be matched at start of buffer
6333 // buf pointer to text to be checked for "tag" match
6335 // Returns a pointer to the trailing substring of the specified
6336 // text buffer, following an initial substring matching the "tag"
6337 // argument, or NULL if "tag" is not matched.
6339 inline const char *psbb_locator::context_args(const char *tag, const char *buf)
6341 size_t len = strlen(tag);
6342 return (strncmp(tag, buf, len) == 0) ? buf + len : NULL;
6345 // psbb_locator::bounding_box_args()
6347 // Returns a pointer to the arguments string, within the current
6348 // input line, when this represents a PostScript "%%BoundingBox:"
6349 // comment, or NULL otherwise.
6351 inline const char *psbb_locator::bounding_box_args(void)
6353 return context_args("%%BoundingBox:");
6356 // psbb_locator::assign_registers()
6358 // Copies the bounding box properties established within the
6359 // class object, to the associated gtroff registers.
6361 inline void psbb_locator::assign_registers(void)
6363 llx_reg_contents = llx;
6364 lly_reg_contents = lly;
6365 urx_reg_contents = urx;
6366 ury_reg_contents = ury;
6369 // psbb_locator::get_header_comment()
6371 // Fetch a line of PostScript input; return true if it complies with
6372 // the formatting requirements for header comments, and it is not an
6373 // "%%EndComments" line; otherwise return false.
6375 inline bool psbb_locator::get_header_comment(void)
6378 // The first necessary requirement, for returning true,
6379 // is that the input line is not empty, (i.e. not EOF).
6381 get_line(DSC_LINE_MAX_ENFORCE) != 0
6383 // In header comments, `%X' (`X' any printable character
6384 // except whitespace) is also acceptable.
6386 && (buf[0] == '%') && !white_space(buf[1])
6388 // Finally, the input line must not say "%%EndComments".
6390 && context_args("%%EndComments") == NULL;
6393 // psbb_locator::skip_to_trailer()
6395 // Reposition the PostScript input stream, such that the next get_line()
6396 // will retrieve the first line, if any, following a "%%Trailer" comment;
6397 // returns a positive integer value if the "%%Trailer" comment is found,
6398 // or zero if it is not.
6400 inline int psbb_locator::skip_to_trailer(void)
6402 // Begin by considering a chunk of the input file starting 512 bytes
6403 // before its end, and search it for a "%%Trailer" comment; if none is
6404 // found, incrementally double the chunk size while it remains within
6405 // a 32768L byte range, and search again...
6407 for (ssize_t offset = 512L; offset > 0L; offset <<= 1) {
6409 if ((offset > 32768L) || ((failed = fseek(fp, -offset, SEEK_END)) != 0))
6411 // ...ultimately resetting the offset to zero, and simply seeking
6412 // to the start of the file, to terminate the cycle and do a "last
6413 // ditch" search of the entire file, if any backward seek fails, or
6414 // if we reach the arbitrary 32768L byte range limit.
6416 failed = fseek(fp, offset = 0L, SEEK_SET);
6418 // Following each successful seek...
6422 // ...perform a search by reading lines from the input stream...
6424 do { status = get_line(DSC_LINE_MAX_ENFORCE);
6426 // ...until we either exhaust the available stream data, or
6427 // we have located a "%%Trailer" comment line.
6429 } while ((status != 0) && (context_args("%%Trailer") == NULL));
6432 // We found the "%%Trailer" comment, so we may immediately
6433 // return, with the stream positioned appropriately...
6438 // ...otherwise, we report that no "%%Trailer" comment was found.
6443 // ps_bbox_request()
6445 // Handle the .psbb request.
6447 void ps_bbox_request()
6449 // Parse input line, to extract file name.
6451 symbol nm = get_long_name(1);
6454 // No file name specified: ignore the entire request.
6458 // File name acquired: swallow the rest of the line.
6460 while (!tok.newline() && !tok.eof())
6464 // Update {llx,lly,urx,ury}_reg_contents:
6465 // declaring this class instance achieves this, as an
6466 // intentional side effect of object construction.
6468 psbb_locator do_ps_file(nm.contents());
6470 // All done for .psbb; move on, to continue
6471 // input stream processing.
6477 const char *asciify(int c)
6480 buf[0] = escape_char == '\0' ? '\\' : escape_char;
6481 buf[1] = buf[2] = '\0';
6483 case ESCAPE_QUESTION:
6486 case ESCAPE_AMPERSAND:
6489 case ESCAPE_RIGHT_PARENTHESIS:
6492 case ESCAPE_UNDERSCORE:
6498 case ESCAPE_CIRCUMFLEX:
6501 case ESCAPE_LEFT_BRACE:
6504 case ESCAPE_RIGHT_BRACE:
6507 case ESCAPE_LEFT_QUOTE:
6510 case ESCAPE_RIGHT_QUOTE:
6528 case ESCAPE_PERCENT:
6540 case PUSH_GROFF_MODE:
6541 case PUSH_COMP_MODE:
6542 case POP_GROFFCOMP_MODE:
6546 if (invalid_input_char(c))
6555 const char *input_char_description(int c)
6559 return "a newline character";
6561 return "a backspace character";
6563 return "a leader character";
6565 return "a tab character";
6567 return "a space character";
6571 static char buf[sizeof("magic character code ") + 1 + INT_DIGITS];
6572 if (invalid_input_char(c)) {
6573 const char *s = asciify(c);
6580 sprintf(buf, "magic character code %d", c);
6589 sprintf(buf, "character code %d", c);
6595 if (!tok.newline() && !tok.eof()) {
6604 if (c != ' ' && c != '\t')
6608 for (; c != '\n' && c != EOF; c = get_copy(0))
6611 curenv->add_node(new tag_node(s, 0));
6618 if (!tok.newline() && !tok.eof()) {
6627 if (c != ' ' && c != '\t')
6631 for (; c != '\n' && c != EOF; c = get_copy(0))
6634 curenv->add_node(new tag_node(s, 1));
6639 // .tm, .tm1, and .tmc
6641 void do_terminal(int newline, int string_like)
6643 if (!tok.newline() && !tok.eof()) {
6647 if (string_like && c == '"') {
6651 if (c != ' ' && c != '\t')
6654 for (; c != '\n' && c != EOF; c = get_copy(0))
6655 fputs(asciify(c), stderr);
6658 fputc('\n', stderr);
6673 void terminal_continue()
6678 dictionary stream_dictionary(20);
6680 void do_open(int append)
6682 symbol stream = get_name(1);
6683 if (!stream.is_null()) {
6684 symbol filename = get_long_name(1);
6685 if (!filename.is_null()) {
6687 FILE *fp = fopen(filename.contents(), append ? "a" : "w");
6689 error("can't open `%1' for %2: %3",
6690 filename.contents(),
6691 append ? "appending" : "writing",
6693 fp = (FILE *)stream_dictionary.remove(stream);
6696 fp = (FILE *)stream_dictionary.lookup(stream, fp);
6707 error(".open request not allowed in safer mode");
6714 void opena_request()
6717 error(".opena request not allowed in safer mode");
6724 void close_request()
6726 symbol stream = get_name(1);
6727 if (!stream.is_null()) {
6728 FILE *fp = (FILE *)stream_dictionary.remove(stream);
6730 error("no stream named `%1'", stream.contents());
6737 // .write and .writec
6739 void do_write_request(int newline)
6741 symbol stream = get_name(1);
6742 if (stream.is_null()) {
6746 FILE *fp = (FILE *)stream_dictionary.lookup(stream);
6748 error("no stream named `%1'", stream.contents());
6753 while ((c = get_copy(0)) == ' ')
6757 for (; c != '\n' && c != EOF; c = get_copy(0))
6758 fputs(asciify(c), fp);
6765 void write_request()
6767 do_write_request(1);
6770 void write_request_continue()
6772 do_write_request(0);
6775 void write_macro_request()
6777 symbol stream = get_name(1);
6778 if (stream.is_null()) {
6782 FILE *fp = (FILE *)stream_dictionary.lookup(stream);
6784 error("no stream named `%1'", stream.contents());
6788 symbol s = get_name(1);
6793 request_or_macro *p = lookup_request(s);
6794 macro *m = p->to_macro();
6796 error("cannot write request");
6798 string_iterator iter(*m);
6800 int c = iter.get(0);
6803 fputs(asciify(c), fp);
6810 void warnscale_request()
6817 warn_scale = (double)units_per_inch;
6819 warn_scale = (double)units_per_inch / 2.54;
6821 warn_scale = (double)units_per_inch / 72.0;
6823 warn_scale = (double)units_per_inch / 6.0;
6826 "invalid scaling indicator `%1', using `i' instead", c);
6829 warn_scaling_indicator = c;
6834 void spreadwarn_request()
6837 if (has_arg() && get_hunits(&n, 'm')) {
6840 hunits em = curenv->get_size();
6841 spread_limit = (double)n.to_units()
6842 / (em.is_zero() ? hresolution : em.to_units());
6845 spread_limit = -spread_limit - 1; // no arg toggles on/off without
6846 // changing value; we mirror at
6847 // -0.5 to make zero a valid value
6851 static void init_charset_table()
6854 strcpy(buf, "char");
6855 for (int i = 0; i < 256; i++) {
6856 strcpy(buf + 4, i_to_a(i));
6857 charset_table[i] = get_charinfo(symbol(buf));
6858 charset_table[i]->set_ascii_code(i);
6860 charset_table[i]->set_hyphenation_code(cmlower(i));
6862 charset_table['.']->set_flags(charinfo::ENDS_SENTENCE);
6863 charset_table['?']->set_flags(charinfo::ENDS_SENTENCE);
6864 charset_table['!']->set_flags(charinfo::ENDS_SENTENCE);
6865 charset_table['-']->set_flags(charinfo::BREAK_AFTER);
6866 charset_table['"']->set_flags(charinfo::TRANSPARENT);
6867 charset_table['\'']->set_flags(charinfo::TRANSPARENT);
6868 charset_table[')']->set_flags(charinfo::TRANSPARENT);
6869 charset_table[']']->set_flags(charinfo::TRANSPARENT);
6870 charset_table['*']->set_flags(charinfo::TRANSPARENT);
6871 get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT);
6872 get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT);
6873 get_charinfo(symbol("cq"))->set_flags(charinfo::TRANSPARENT);
6874 get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER);
6875 get_charinfo(symbol("hy"))->set_flags(charinfo::BREAK_AFTER);
6876 get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6877 get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6878 get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6879 get_charinfo(symbol("sqrtex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6880 get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6881 get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY);
6882 page_character = charset_table['%'];
6885 static void init_hpf_code_table()
6887 for (int i = 0; i < 256; i++)
6888 hpf_code_table[i] = cmlower(i);
6891 static void do_translate(int translate_transparent, int translate_input)
6894 while (!tok.newline() && !tok.eof()) {
6896 // This is a really bizarre troff feature.
6898 translate_space_to_dummy = tok.dummy();
6899 if (tok.newline() || tok.eof())
6904 charinfo *ci1 = tok.get_char(1);
6908 if (tok.newline() || tok.eof()) {
6909 ci1->set_special_translation(charinfo::TRANSLATE_SPACE,
6910 translate_transparent);
6914 ci1->set_special_translation(charinfo::TRANSLATE_SPACE,
6915 translate_transparent);
6916 else if (tok.stretchable_space())
6917 ci1->set_special_translation(charinfo::TRANSLATE_STRETCHABLE_SPACE,
6918 translate_transparent);
6919 else if (tok.dummy())
6920 ci1->set_special_translation(charinfo::TRANSLATE_DUMMY,
6921 translate_transparent);
6922 else if (tok.hyphen_indicator())
6923 ci1->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR,
6924 translate_transparent);
6926 charinfo *ci2 = tok.get_char(1);
6930 ci1->set_translation(0, translate_transparent, translate_input);
6932 ci1->set_translation(ci2, translate_transparent, translate_input);
6944 void translate_no_transparent()
6949 void translate_input()
6957 if (get_integer(&flags))
6959 charinfo *ci = tok.get_char(1);
6961 charinfo *tem = ci->get_translation();
6964 ci->set_flags(flags);
6971 void hyphenation_code()
6974 while (!tok.newline() && !tok.eof()) {
6975 charinfo *ci = tok.get_char(1);
6980 unsigned char c = tok.ch();
6982 error("hyphenation code must be ordinary character");
6986 error("hyphenation code cannot be digit");
6989 ci->set_hyphenation_code(c);
6990 if (ci->get_translation()
6991 && ci->get_translation()->get_translation_input())
6992 ci->get_translation()->set_hyphenation_code(c);
6999 void hyphenation_patterns_file_code()
7002 while (!tok.newline() && !tok.eof()) {
7004 if (get_integer(&n1) && (0 <= n1 && n1 <= 255)) {
7006 error("missing output hyphenation code");
7009 if (get_integer(&n2) && (0 <= n2 && n2 <= 255)) {
7010 hpf_code_table[n1] = n2;
7014 error("output hyphenation code must be integer in the range 0..255");
7019 error("input hyphenation code must be integer in the range 0..255");
7026 dictionary char_class_dictionary(501);
7031 symbol nm = get_name(1);
7036 charinfo *ci = get_charinfo(nm);
7037 charinfo *child1 = 0, *child2 = 0;
7038 while (!tok.newline() && !tok.eof()) {
7040 if (child1 != 0 && tok.ch() == '-') {
7042 child2 = tok.get_char(1);
7044 warning(WARN_MISSING,
7045 "missing end of character range in class `%1'",
7050 if (child1->is_class() || child2->is_class()) {
7051 warning(WARN_SYNTAX,
7052 "nested character class is not allowed in range definition");
7056 int u1 = child1->get_unicode_code();
7057 int u2 = child2->get_unicode_code();
7059 warning(WARN_SYNTAX,
7060 "invalid start value in character range");
7065 warning(WARN_SYNTAX,
7066 "invalid end value in character range");
7070 ci->add_to_class(u1, u2);
7071 child1 = child2 = 0;
7073 else if (child1 != 0) {
7074 if (child1->is_class()) {
7076 warning(WARN_SYNTAX, "invalid cyclic class nesting");
7080 ci->add_to_class(child1);
7083 int u1 = child1->get_unicode_code();
7085 warning(WARN_SYNTAX,
7086 "invalid character value in class `%1'",
7091 ci->add_to_class(u1);
7095 child1 = tok.get_char(1);
7104 if (child1->is_class()) {
7106 warning(WARN_SYNTAX, "invalid cyclic class nesting");
7110 ci->add_to_class(child1);
7113 int u1 = child1->get_unicode_code();
7115 warning(WARN_SYNTAX,
7116 "invalid character value in class `%1'",
7121 ci->add_to_class(u1);
7125 if (!ci->is_class()) {
7126 warning(WARN_SYNTAX,
7127 "empty class definition for `%1'",
7132 (void)char_class_dictionary.lookup(nm, ci);
7136 charinfo *token::get_char(int required)
7138 if (type == TOKEN_CHAR)
7139 return charset_table[c];
7140 if (type == TOKEN_SPECIAL)
7141 return get_charinfo(nm);
7142 if (type == TOKEN_NUMBERED_CHAR)
7143 return get_charinfo_by_number(val);
7144 if (type == TOKEN_ESCAPE) {
7145 if (escape_char != 0)
7146 return charset_table[escape_char];
7148 error("`\\e' used while no current escape character");
7153 if (type == TOKEN_EOF || type == TOKEN_NEWLINE)
7154 warning(WARN_MISSING, "missing normal or special character");
7156 error("normal or special character expected (got %1)", description());
7161 charinfo *get_optional_char()
7165 charinfo *ci = tok.get_char();
7167 check_missing_character();
7173 void check_missing_character()
7175 if (!tok.newline() && !tok.eof() && !tok.right_brace() && !tok.tab())
7176 error("normal or special character expected (got %1): "
7177 "treated as missing",
7183 int token::add_to_node_list(node **pp)
7190 *pp = (*pp)->add_char(charset_table[c], curenv, &w, &s);
7196 if (escape_char != 0)
7197 *pp = (*pp)->add_char(charset_table[escape_char], curenv, &w, &s);
7199 case TOKEN_HYPHEN_INDICATOR:
7200 *pp = (*pp)->add_discretionary_hyphen();
7202 case TOKEN_ITALIC_CORRECTION:
7203 *pp = (*pp)->add_italic_correction(&w);
7205 case TOKEN_LEFT_BRACE:
7207 case TOKEN_MARK_INPUT:
7208 set_number_reg(nm, curenv->get_input_line_position().to_units());
7211 case TOKEN_HORIZONTAL_SPACE:
7215 case TOKEN_NUMBERED_CHAR:
7216 *pp = (*pp)->add_char(get_charinfo_by_number(val), curenv, &w, &s);
7218 case TOKEN_RIGHT_BRACE:
7221 n = new hmotion_node(curenv->get_space_width(),
7222 curenv->get_fill_color());
7225 *pp = (*pp)->add_char(get_charinfo(nm), curenv, &w, &s);
7227 case TOKEN_STRETCHABLE_SPACE:
7228 n = new unbreakable_space_node(curenv->get_space_width(),
7229 curenv->get_fill_color());
7231 case TOKEN_UNSTRETCHABLE_SPACE:
7232 n = new space_char_hmotion_node(curenv->get_space_width(),
7233 curenv->get_fill_color());
7235 case TOKEN_TRANSPARENT_DUMMY:
7236 n = new transparent_dummy_node;
7238 case TOKEN_ZERO_WIDTH_BREAK:
7239 n = new space_node(H0, curenv->get_fill_color());
7241 n->is_escape_colon();
7253 void token::process()
7255 if (possibly_handle_first_page_transition())
7258 case TOKEN_BACKSPACE:
7259 curenv->add_node(new hmotion_node(-curenv->get_space_width(),
7260 curenv->get_fill_color()));
7263 curenv->add_char(charset_table[c]);
7266 curenv->add_node(new dummy_node);
7275 if (escape_char != 0)
7276 curenv->add_char(charset_table[escape_char]);
7278 case TOKEN_BEGIN_TRAP:
7279 case TOKEN_END_TRAP:
7280 case TOKEN_PAGE_EJECTOR:
7281 // these are all handled in process_input_stack()
7283 case TOKEN_HYPHEN_INDICATOR:
7284 curenv->add_hyphen_indicator();
7286 case TOKEN_INTERRUPT:
7287 curenv->interrupt();
7289 case TOKEN_ITALIC_CORRECTION:
7290 curenv->add_italic_correction();
7293 curenv->handle_tab(1);
7295 case TOKEN_LEFT_BRACE:
7297 case TOKEN_MARK_INPUT:
7298 set_number_reg(nm, curenv->get_input_line_position().to_units());
7304 case TOKEN_HORIZONTAL_SPACE:
7305 curenv->add_node(nd);
7308 case TOKEN_NUMBERED_CHAR:
7309 curenv->add_char(get_charinfo_by_number(val));
7312 // handled in process_input_stack()
7314 case TOKEN_RIGHT_BRACE:
7320 curenv->add_char(get_charinfo(nm));
7325 case TOKEN_STRETCHABLE_SPACE:
7326 curenv->add_node(new unbreakable_space_node(curenv->get_space_width(),
7327 curenv->get_fill_color()));
7329 case TOKEN_UNSTRETCHABLE_SPACE:
7330 curenv->add_node(new space_char_hmotion_node(curenv->get_space_width(),
7331 curenv->get_fill_color()));
7334 curenv->handle_tab(0);
7336 case TOKEN_TRANSPARENT:
7338 case TOKEN_TRANSPARENT_DUMMY:
7339 curenv->add_node(new transparent_dummy_node);
7341 case TOKEN_ZERO_WIDTH_BREAK:
7343 node *tmp = new space_node(H0, curenv->get_fill_color());
7344 tmp->freeze_space();
7345 tmp->is_escape_colon();
7346 curenv->add_node(tmp);
7354 class nargs_reg : public reg {
7356 const char *get_string();
7359 const char *nargs_reg::get_string()
7361 return i_to_a(input_stack::nargs());
7364 class lineno_reg : public reg {
7366 const char *get_string();
7369 const char *lineno_reg::get_string()
7373 if (!input_stack::get_location(0, &file, &line))
7375 return i_to_a(line);
7378 class writable_lineno_reg : public general_reg {
7380 writable_lineno_reg();
7381 void set_value(units);
7382 int get_value(units *);
7385 writable_lineno_reg::writable_lineno_reg()
7389 int writable_lineno_reg::get_value(units *res)
7393 if (!input_stack::get_location(0, &file, &line))
7399 void writable_lineno_reg::set_value(units n)
7401 input_stack::set_location(0, n);
7404 class filename_reg : public reg {
7406 const char *get_string();
7409 const char *filename_reg::get_string()
7413 if (input_stack::get_location(0, &file, &line))
7419 class break_flag_reg : public reg {
7421 const char *get_string();
7424 const char *break_flag_reg::get_string()
7426 return i_to_a(input_stack::get_break_flag());
7429 class constant_reg : public reg {
7432 constant_reg(const char *);
7433 const char *get_string();
7436 constant_reg::constant_reg(const char *p) : s(p)
7440 const char *constant_reg::get_string()
7445 constant_int_reg::constant_int_reg(int *q) : p(q)
7449 const char *constant_int_reg::get_string()
7454 void abort_request()
7459 else if (tok.newline())
7462 while ((c = get_copy(0)) == ' ')
7465 if (c == EOF || c == '\n')
7466 fputs("User Abort.", stderr);
7468 for (; c != '\n' && c != EOF; c = get_copy(0))
7469 fputs(asciify(c), stderr);
7471 fputc('\n', stderr);
7472 cleanup_and_exit(1);
7478 char *s = new char[len];
7480 while ((c = get_copy(0)) == ' ')
7483 while (c != '\n' && c != EOF) {
7484 if (!invalid_input_char(c)) {
7487 s = new char[len*2];
7488 memcpy(s, tem, len);
7508 error(".pi request not allowed in safer mode");
7512 #ifdef POPEN_MISSING
7513 error("pipes not available on this system");
7515 #else /* not POPEN_MISSING */
7517 error("can't pipe: output already started");
7522 if ((pc = read_string()) == 0)
7523 error("can't pipe to empty command");
7525 char *s = new char[strlen(pipe_command) + strlen(pc) + 1 + 1];
7526 strcpy(s, pipe_command);
7529 a_delete pipe_command;
7536 #endif /* not POPEN_MISSING */
7540 static int system_status;
7542 void system_request()
7545 error(".sy request not allowed in safer mode");
7549 char *command = read_string();
7551 error("empty command");
7553 system_status = system(command);
7561 if (curdiv == topdiv && topdiv->before_first_page) {
7562 handle_initial_request(COPY_FILE_REQUEST);
7565 symbol filename = get_long_name(1);
7566 while (!tok.newline() && !tok.eof())
7570 if (!filename.is_null())
7571 curdiv->copy_file(filename.contents());
7579 if (curdiv == topdiv && topdiv->before_first_page) {
7580 handle_initial_request(VJUSTIFY_REQUEST);
7583 symbol type = get_long_name(1);
7584 if (!type.is_null())
7585 curdiv->vjustify(type);
7591 void transparent_file()
7593 if (curdiv == topdiv && topdiv->before_first_page) {
7594 handle_initial_request(TRANSPARENT_FILE_REQUEST);
7597 symbol filename = get_long_name(1);
7598 while (!tok.newline() && !tok.eof())
7602 if (!filename.is_null()) {
7604 FILE *fp = include_search_path.open_file_cautious(filename.contents());
7606 error("can't open `%1': %2", filename.contents(), strerror(errno));
7613 if (invalid_input_char(c))
7614 warning(WARN_INPUT, "invalid input character code %1", int(c));
7616 curdiv->transparent_output(c);
7621 curdiv->transparent_output('\n');
7633 page_range(int, int, page_range *);
7634 int contains(int n);
7637 page_range::page_range(int i, int j, page_range *p)
7638 : first(i), last(j), next(p)
7642 int page_range::contains(int n)
7644 return n >= first && (last <= 0 || n <= last);
7647 page_range *output_page_list = 0;
7649 int in_output_page_list(int n)
7651 if (!output_page_list)
7653 for (page_range *p = output_page_list; p; p = p->next)
7659 static void parse_output_page_list(char *p)
7665 else if (csdigit(*p)) {
7668 i = i*10 + *p++ - '0';
7669 while (csdigit(*p));
7679 j = j*10 + *p++ - '0';
7680 while (csdigit(*p));
7686 last_page_number = -1;
7687 else if (last_page_number >= 0 && j > last_page_number)
7688 last_page_number = j;
7689 output_page_list = new page_range(i, j, output_page_list);
7695 error("bad output page list");
7696 output_page_list = 0;
7700 static FILE *open_mac_file(const char *mac, char **path)
7702 // Try first FOOBAR.tmac, then tmac.FOOBAR
7703 char *s1 = new char[strlen(mac)+strlen(MACRO_POSTFIX)+1];
7705 strcat(s1, MACRO_POSTFIX);
7706 FILE *fp = mac_path->open_file(s1, path);
7709 char *s2 = new char[strlen(mac)+strlen(MACRO_PREFIX)+1];
7710 strcpy(s2, MACRO_PREFIX);
7712 fp = mac_path->open_file(s2, path);
7718 static void process_macro_file(const char *mac)
7721 FILE *fp = open_mac_file(mac, &path);
7723 fatal("can't find macro file %1", mac);
7724 const char *s = symbol(path).contents();
7726 input_stack::push(new file_iterator(fp, s));
7728 process_input_stack();
7731 static void process_startup_file(const char *filename)
7734 search_path *orig_mac_path = mac_path;
7735 mac_path = &config_macro_path;
7736 FILE *fp = mac_path->open_file(filename, &path);
7738 input_stack::push(new file_iterator(fp, symbol(path).contents()));
7741 process_input_stack();
7743 mac_path = orig_mac_path;
7748 symbol nm = get_long_name(1);
7752 while (!tok.newline() && !tok.eof())
7755 FILE *fp = mac_path->open_file(nm.contents(), &path);
7756 // .mso doesn't (and cannot) go through open_mac_file, so we
7757 // need to do it here manually: If we have tmac.FOOBAR, try
7758 // FOOBAR.tmac and vice versa
7760 const char *fn = nm.contents();
7761 if (strncasecmp(fn, MACRO_PREFIX, sizeof(MACRO_PREFIX) - 1) == 0) {
7762 char *s = new char[strlen(fn) + sizeof(MACRO_POSTFIX)];
7763 strcpy(s, fn + sizeof(MACRO_PREFIX) - 1);
7764 strcat(s, MACRO_POSTFIX);
7765 fp = mac_path->open_file(s, &path);
7769 if (strncasecmp(fn + strlen(fn) - sizeof(MACRO_POSTFIX) + 1,
7770 MACRO_POSTFIX, sizeof(MACRO_POSTFIX) - 1) == 0) {
7771 char *s = new char[strlen(fn) + sizeof(MACRO_PREFIX)];
7772 strcpy(s, MACRO_PREFIX);
7773 strncat(s, fn, strlen(fn) - sizeof(MACRO_POSTFIX) + 1);
7774 fp = mac_path->open_file(s, &path);
7780 input_stack::push(new file_iterator(fp, symbol(path).contents()));
7784 warning(WARN_FILE, "can't find macro file `%1'", nm.contents());
7789 static void process_input_file(const char *name)
7792 if (strcmp(name, "-") == 0) {
7798 fp = include_search_path.open_file_cautious(name);
7800 fatal("can't open `%1': %2", name, strerror(errno));
7802 input_stack::push(new file_iterator(fp, name));
7804 process_input_stack();
7807 // make sure the_input is empty before calling this
7809 static int evaluate_expression(const char *expr, units *res)
7811 input_stack::push(make_temp_iterator(expr));
7813 int success = get_number(res, 'u');
7814 while (input_stack::get(0) != EOF)
7819 static void do_register_assignment(const char *s)
7821 const char *p = strchr(s, '=');
7827 if (evaluate_expression(s + 1, &n))
7828 set_number_reg(buf, n);
7831 char *buf = new char[p - s + 1];
7832 memcpy(buf, s, p - s);
7835 if (evaluate_expression(p + 1, &n))
7836 set_number_reg(buf, n);
7841 static void set_string(const char *name, const char *value)
7843 macro *m = new macro;
7844 for (const char *p = value; *p; p++)
7845 if (!invalid_input_char((unsigned char)*p))
7847 request_dictionary.define(name, m);
7850 static void do_string_assignment(const char *s)
7852 const char *p = strchr(s, '=');
7857 set_string(buf, s + 1);
7860 char *buf = new char[p - s + 1];
7861 memcpy(buf, s, p - s);
7863 set_string(buf, p + 1);
7868 struct string_list {
7871 string_list(const char *ss) : s(ss), next(0) {}
7875 static void prepend_string(const char *s, string_list **p)
7877 string_list *l = new string_list(s);
7883 static void add_string(const char *s, string_list **p)
7887 *p = new string_list(s);
7890 void usage(FILE *stream, const char *prog)
7893 "usage: %s -abcivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n"
7894 " -rcn -Tname -Fdir -Idir -Mdir [files...]\n",
7898 int main(int argc, char **argv)
7900 program_name = argv[0];
7901 static char stderr_buf[BUFSIZ];
7902 setbuf(stderr, stderr_buf);
7904 string_list *macros = 0;
7905 string_list *register_assignments = 0;
7906 string_list *string_assignments = 0;
7911 int no_rc = 0; // don't process troffrc and troffrc-end
7912 int next_page_number = 0; // pacify compiler
7914 hresolution = vresolution = 1;
7915 // restore $PATH if called from groff
7916 char* groff_path = getenv("GROFF_PATH__");
7923 if (putenv(strsave(e.contents())))
7924 fatal("putenv failed");
7926 setlocale(LC_CTYPE, "");
7927 static const struct option long_options[] = {
7928 { "help", no_argument, 0, CHAR_MAX + 1 },
7929 { "version", no_argument, 0, 'v' },
7932 #if defined(DEBUGGING)
7933 #define DEBUG_OPTION "D"
7935 while ((c = getopt_long(argc, argv,
7936 "abciI:vw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU"
7937 DEBUG_OPTION, long_options, 0))
7942 printf("GNU troff (groff) version %s\n", Version_string);
7947 // Search path for .psbb files
7948 // and most other non-system input files.
7949 include_search_path.command_line_dir(optarg);
7954 is_html = (strcmp(device, "html") == 0);
7957 compatible_flag = 1;
7963 macro_path.command_line_dir(optarg);
7964 safer_macro_path.command_line_dir(optarg);
7965 config_macro_path.command_line_dir(optarg);
7968 font::command_line_font_dir(optarg);
7971 add_string(optarg, ¯os);
7980 enable_warning(optarg);
7983 disable_warning(optarg);
7992 ascii_output_flag = 1;
7995 suppress_output_flag = 1;
7998 if (sscanf(optarg, "%d", &next_page_number) == 1)
8001 error("bad page number");
8004 parse_output_page_list(optarg);
8007 if (*optarg == '\0')
8008 error("`-d' requires non-empty argument");
8010 add_string(optarg, &string_assignments);
8013 if (*optarg == '\0')
8014 error("`-r' requires non-empty argument");
8016 add_string(optarg, ®ister_assignments);
8019 default_family = symbol(optarg);
8025 // silently ignore these
8028 unsafe_flag = 1; // unsafe behaviour
8030 #if defined(DEBUGGING)
8035 case CHAR_MAX + 1: // --help
8036 usage(stdout, argv[0]);
8040 usage(stderr, argv[0]);
8042 break; // never reached
8047 mac_path = ¯o_path;
8048 set_string(".T", device);
8049 init_charset_table();
8050 init_hpf_code_table();
8051 if (!font::load_desc())
8052 fatal("sorry, I can't continue");
8053 units_per_inch = font::res;
8054 hresolution = font::hor;
8055 vresolution = font::vert;
8056 sizescale = font::sizescale;
8057 tcommand_flag = font::tcommand;
8058 warn_scale = (double)units_per_inch;
8059 warn_scaling_indicator = 'i';
8060 if (!fflag && font::family != 0 && *font::family != '\0')
8061 default_family = symbol(font::family);
8062 font_size::init_size_table(font::sizes);
8065 if (font::style_table) {
8066 for (i = 0; font::style_table[i]; i++)
8067 mount_style(j++, symbol(font::style_table[i]));
8069 for (i = 0; font::font_name_table[i]; i++, j++)
8070 // In the DESC file a font name of 0 (zero) means leave this
8072 if (strcmp(font::font_name_table[i], "0") != 0)
8073 mount_font(j, symbol(font::font_name_table[i]));
8074 curdiv = topdiv = new top_level_diversion;
8076 topdiv->set_next_page_number(next_page_number);
8077 init_input_requests();
8078 init_env_requests();
8079 init_div_requests();
8081 init_column_requests();
8083 init_node_requests();
8084 number_reg_dictionary.define(".T", new constant_reg(tflag ? "1" : "0"));
8086 init_reg_requests();
8087 init_hyphen_requests();
8088 init_environments();
8089 while (string_assignments) {
8090 do_string_assignment(string_assignments->s);
8091 string_list *tem = string_assignments;
8092 string_assignments = string_assignments->next;
8095 while (register_assignments) {
8096 do_register_assignment(register_assignments->s);
8097 string_list *tem = register_assignments;
8098 register_assignments = register_assignments->next;
8102 process_startup_file(INITIAL_STARTUP_FILE);
8104 process_macro_file(macros->s);
8105 string_list *tem = macros;
8106 macros = macros->next;
8110 process_startup_file(FINAL_STARTUP_FILE);
8111 for (i = optind; i < argc; i++)
8112 process_input_file(argv[i]);
8113 if (optind >= argc || iflag)
8114 process_input_file("-");
8116 return 0; // not reached
8122 if (has_arg() && get_integer(&n)) {
8123 if (n & ~WARN_TOTAL) {
8124 warning(WARN_RANGE, "warning mask must be between 0 and %1", WARN_TOTAL);
8130 warning_mask = WARN_TOTAL;
8134 static void init_registers()
8136 #ifdef LONG_FOR_TIME_T
8138 #else /* not LONG_FOR_TIME_T */
8140 #endif /* not LONG_FOR_TIME_T */
8142 // Use struct here to work around misfeature in old versions of g++.
8143 struct tm *tt = localtime(&t);
8144 set_number_reg("seconds", int(tt->tm_sec));
8145 set_number_reg("minutes", int(tt->tm_min));
8146 set_number_reg("hours", int(tt->tm_hour));
8147 set_number_reg("dw", int(tt->tm_wday + 1));
8148 set_number_reg("dy", int(tt->tm_mday));
8149 set_number_reg("mo", int(tt->tm_mon + 1));
8150 set_number_reg("year", int(1900 + tt->tm_year));
8151 set_number_reg("yr", int(tt->tm_year));
8152 set_number_reg("$$", getpid());
8153 number_reg_dictionary.define(".A",
8154 new constant_reg(ascii_output_flag
8160 * registers associated with \O
8163 static int output_reg_minx_contents = -1;
8164 static int output_reg_miny_contents = -1;
8165 static int output_reg_maxx_contents = -1;
8166 static int output_reg_maxy_contents = -1;
8168 void check_output_limits(int x, int y)
8170 if ((output_reg_minx_contents == -1) || (x < output_reg_minx_contents))
8171 output_reg_minx_contents = x;
8172 if (x > output_reg_maxx_contents)
8173 output_reg_maxx_contents = x;
8174 if ((output_reg_miny_contents == -1) || (y < output_reg_miny_contents))
8175 output_reg_miny_contents = y;
8176 if (y > output_reg_maxy_contents)
8177 output_reg_maxy_contents = y;
8180 void reset_output_registers()
8182 output_reg_minx_contents = -1;
8183 output_reg_miny_contents = -1;
8184 output_reg_maxx_contents = -1;
8185 output_reg_maxy_contents = -1;
8188 void get_output_registers(int *minx, int *miny, int *maxx, int *maxy)
8190 *minx = output_reg_minx_contents;
8191 *miny = output_reg_miny_contents;
8192 *maxx = output_reg_maxx_contents;
8193 *maxy = output_reg_maxy_contents;
8196 void init_input_requests()
8198 init_request("ab", abort_request);
8199 init_request("als", alias_macro);
8200 init_request("am", append_macro);
8201 init_request("am1", append_nocomp_macro);
8202 init_request("ami", append_indirect_macro);
8203 init_request("ami1", append_indirect_nocomp_macro);
8204 init_request("as", append_string);
8205 init_request("as1", append_nocomp_string);
8206 init_request("asciify", asciify_macro);
8207 init_request("backtrace", backtrace_request);
8208 init_request("blm", blank_line_macro);
8209 init_request("break", while_break_request);
8210 init_request("cf", copy_file);
8211 init_request("cflags", char_flags);
8212 init_request("char", define_character);
8213 init_request("chop", chop_macro);
8214 init_request("class", define_class);
8215 init_request("close", close_request);
8216 init_request("color", activate_color);
8217 init_request("composite", composite_request);
8218 init_request("continue", while_continue_request);
8219 init_request("cp", compatible);
8220 init_request("de", define_macro);
8221 init_request("de1", define_nocomp_macro);
8222 init_request("defcolor", define_color);
8223 init_request("dei", define_indirect_macro);
8224 init_request("dei1", define_indirect_nocomp_macro);
8225 init_request("device", device_request);
8226 init_request("devicem", device_macro_request);
8227 init_request("do", do_request);
8228 init_request("ds", define_string);
8229 init_request("ds1", define_nocomp_string);
8230 init_request("ec", set_escape_char);
8231 init_request("ecr", restore_escape_char);
8232 init_request("ecs", save_escape_char);
8233 init_request("el", else_request);
8234 init_request("em", end_macro);
8235 init_request("eo", escape_off);
8236 init_request("ex", exit_request);
8237 init_request("fchar", define_fallback_character);
8238 #ifdef WIDOW_CONTROL
8239 init_request("fpl", flush_pending_lines);
8240 #endif /* WIDOW_CONTROL */
8241 init_request("hcode", hyphenation_code);
8242 init_request("hpfcode", hyphenation_patterns_file_code);
8243 init_request("ie", if_else_request);
8244 init_request("if", if_request);
8245 init_request("ig", ignore);
8246 init_request("length", length_request);
8247 init_request("lf", line_file);
8248 init_request("lsm", leading_spaces_macro);
8249 init_request("mso", macro_source);
8250 init_request("nop", nop_request);
8251 init_request("nroff", nroff_request);
8252 init_request("nx", next_file);
8253 init_request("open", open_request);
8254 init_request("opena", opena_request);
8255 init_request("output", output_request);
8256 init_request("pc", set_page_character);
8257 init_request("pi", pipe_output);
8258 init_request("pm", print_macros);
8259 init_request("psbb", ps_bbox_request);
8260 #ifndef POPEN_MISSING
8261 init_request("pso", pipe_source);
8262 #endif /* not POPEN_MISSING */
8263 init_request("rchar", remove_character);
8264 init_request("rd", read_request);
8265 init_request("return", return_macro_request);
8266 init_request("rm", remove_macro);
8267 init_request("rn", rename_macro);
8268 init_request("schar", define_special_character);
8269 init_request("shift", shift);
8270 init_request("so", source);
8271 init_request("spreadwarn", spreadwarn_request);
8272 init_request("substring", substring_request);
8273 init_request("sy", system_request);
8274 init_request("tag", tag);
8275 init_request("taga", taga);
8276 init_request("tm", terminal);
8277 init_request("tm1", terminal1);
8278 init_request("tmc", terminal_continue);
8279 init_request("tr", translate);
8280 init_request("trf", transparent_file);
8281 init_request("trin", translate_input);
8282 init_request("trnt", translate_no_transparent);
8283 init_request("troff", troff_request);
8284 init_request("unformat", unformat_macro);
8286 init_request("vj", vjustify);
8288 init_request("warn", warn_request);
8289 init_request("warnscale", warnscale_request);
8290 init_request("while", while_request);
8291 init_request("write", write_request);
8292 init_request("writec", write_request_continue);
8293 init_request("writem", write_macro_request);
8294 number_reg_dictionary.define(".$", new nargs_reg);
8295 number_reg_dictionary.define(".br", new break_flag_reg);
8296 number_reg_dictionary.define(".C", new constant_int_reg(&compatible_flag));
8297 number_reg_dictionary.define(".O", new variable_reg(&begin_level));
8298 number_reg_dictionary.define(".c", new lineno_reg);
8299 number_reg_dictionary.define(".color", new constant_int_reg(&color_flag));
8300 number_reg_dictionary.define(".F", new filename_reg);
8301 number_reg_dictionary.define(".g", new constant_reg("1"));
8302 number_reg_dictionary.define(".H", new constant_int_reg(&hresolution));
8303 number_reg_dictionary.define(".R", new constant_reg("10000"));
8304 number_reg_dictionary.define(".U", new constant_int_reg(&unsafe_flag));
8305 number_reg_dictionary.define(".V", new constant_int_reg(&vresolution));
8306 number_reg_dictionary.define(".warn", new constant_int_reg(&warning_mask));
8307 extern const char *major_version;
8308 number_reg_dictionary.define(".x", new constant_reg(major_version));
8309 extern const char *revision;
8310 number_reg_dictionary.define(".Y", new constant_reg(revision));
8311 extern const char *minor_version;
8312 number_reg_dictionary.define(".y", new constant_reg(minor_version));
8313 number_reg_dictionary.define("c.", new writable_lineno_reg);
8314 number_reg_dictionary.define("llx", new variable_reg(&llx_reg_contents));
8315 number_reg_dictionary.define("lly", new variable_reg(&lly_reg_contents));
8316 number_reg_dictionary.define("lsn", new variable_reg(&leading_spaces_number));
8317 number_reg_dictionary.define("lss", new variable_reg(&leading_spaces_space));
8318 number_reg_dictionary.define("opmaxx",
8319 new variable_reg(&output_reg_maxx_contents));
8320 number_reg_dictionary.define("opmaxy",
8321 new variable_reg(&output_reg_maxy_contents));
8322 number_reg_dictionary.define("opminx",
8323 new variable_reg(&output_reg_minx_contents));
8324 number_reg_dictionary.define("opminy",
8325 new variable_reg(&output_reg_miny_contents));
8326 number_reg_dictionary.define("slimit",
8327 new variable_reg(&input_stack::limit));
8328 number_reg_dictionary.define("systat", new variable_reg(&system_status));
8329 number_reg_dictionary.define("urx", new variable_reg(&urx_reg_contents));
8330 number_reg_dictionary.define("ury", new variable_reg(&ury_reg_contents));
8333 object_dictionary request_dictionary(501);
8335 void init_request(const char *s, REQUEST_FUNCP f)
8337 request_dictionary.define(s, new request(f));
8340 static request_or_macro *lookup_request(symbol nm)
8342 assert(!nm.is_null());
8343 request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm);
8345 warning(WARN_MAC, "macro `%1' not defined", nm.contents());
8347 request_dictionary.define(nm, p);
8352 node *charinfo_to_node_list(charinfo *ci, const environment *envp)
8354 // Don't interpret character definitions in compatible mode.
8355 int old_compatible_flag = compatible_flag;
8356 compatible_flag = 0;
8357 int old_escape_char = escape_char;
8359 macro *mac = ci->set_macro(0);
8361 environment *oldenv = curenv;
8362 environment env(envp);
8364 curenv->set_composite();
8365 token old_tok = tok;
8366 input_stack::add_boundary();
8367 string_iterator *si =
8368 new string_iterator(*mac, "composite character", ci->nm);
8369 input_stack::push(si);
8370 // we don't use process_input_stack, because we don't want to recognise
8376 if (tok.newline()) {
8377 error("composite character mustn't contain newline");
8385 node *n = curenv->extract_output_line();
8386 input_stack::remove_boundary();
8390 compatible_flag = old_compatible_flag;
8391 escape_char = old_escape_char;
8396 static node *read_draw_node()
8400 if (!start.delimiter(1)){
8403 } while (tok != start && !tok.newline() && !tok.eof());
8408 error("missing argument");
8410 unsigned char type = tok.ch();
8412 read_color_draw_node(start);
8417 hvpair *point = new hvpair[maxpoints];
8422 for (i = 0; tok != start; i++) {
8423 if (i == maxpoints) {
8424 hvpair *oldpoint = point;
8425 point = new hvpair[maxpoints*2];
8426 for (int j = 0; j < maxpoints; j++)
8427 point[j] = oldpoint[j];
8431 if (!get_hunits(&point[i].h,
8432 type == 'f' || type == 't' ? 'u' : 'm')) {
8443 if (!get_vunits(&point[i].v, 'v')) {
8449 while (tok != start && !tok.newline() && !tok.eof())
8454 if (npoints != 1 || no_last_v) {
8455 error("two arguments needed for line");
8460 if (npoints != 1 || !no_last_v) {
8461 error("one argument needed for circle");
8467 if (npoints != 1 || no_last_v) {
8468 error("two arguments needed for ellipse");
8473 if (npoints != 2 || no_last_v) {
8474 error("four arguments needed for arc");
8480 error("even number of arguments needed for spline");
8483 if (npoints != 1 || !no_last_v) {
8484 error("one argument needed for gray shade");
8489 // silently pass it through
8492 draw_node *dn = new draw_node(type, point, npoints,
8493 curenv->get_font_size(),
8494 curenv->get_glyph_color(),
8495 curenv->get_fill_color());
8507 static void read_color_draw_node(token &start)
8511 error("missing color scheme");
8514 unsigned char scheme = tok.ch();
8517 char end = start.ch();
8520 col = read_cmy(end);
8523 col = &default_color;
8526 col = read_gray(end);
8529 col = read_cmyk(end);
8532 col = read_rgb(end);
8536 curenv->set_fill_color(col);
8537 while (tok != start) {
8538 if (tok.newline() || tok.eof()) {
8539 warning(WARN_DELIM, "missing closing delimiter");
8540 input_stack::push(make_temp_iterator("\n"));
8551 } warning_table[] = {
8552 { "char", WARN_CHAR },
8553 { "range", WARN_RANGE },
8554 { "break", WARN_BREAK },
8555 { "delim", WARN_DELIM },
8557 { "scale", WARN_SCALE },
8558 { "number", WARN_NUMBER },
8559 { "syntax", WARN_SYNTAX },
8560 { "tab", WARN_TAB },
8561 { "right-brace", WARN_RIGHT_BRACE },
8562 { "missing", WARN_MISSING },
8563 { "input", WARN_INPUT },
8564 { "escape", WARN_ESCAPE },
8565 { "space", WARN_SPACE },
8566 { "font", WARN_FONT },
8568 { "mac", WARN_MAC },
8569 { "reg", WARN_REG },
8571 { "color", WARN_COLOR },
8572 { "file", WARN_FILE },
8573 { "all", WARN_TOTAL & ~(WARN_DI | WARN_MAC | WARN_REG) },
8574 { "w", WARN_TOTAL },
8575 { "default", DEFAULT_WARNING_MASK },
8578 static int lookup_warning(const char *name)
8580 for (unsigned int i = 0;
8581 i < sizeof(warning_table)/sizeof(warning_table[0]);
8583 if (strcmp(name, warning_table[i].name) == 0)
8584 return warning_table[i].mask;
8588 static void enable_warning(const char *name)
8590 int mask = lookup_warning(name);
8592 warning_mask |= mask;
8594 error("unknown warning `%1'", name);
8597 static void disable_warning(const char *name)
8599 int mask = lookup_warning(name);
8601 warning_mask &= ~mask;
8603 error("unknown warning `%1'", name);
8606 static void copy_mode_error(const char *format,
8612 static const char prefix[] = "(in ignored input) ";
8613 char *s = new char[sizeof(prefix) + strlen(format)];
8616 warning(WARN_IG, s, arg1, arg2, arg3);
8620 error(format, arg1, arg2, arg3);
8623 enum error_type { WARNING, OUTPUT_WARNING, ERROR, FATAL };
8625 static void do_error(error_type type,
8631 const char *filename;
8633 if (inhibit_errors && type < FATAL)
8636 input_stack::backtrace();
8637 if (!get_file_line(&filename, &lineno))
8640 errprint("%1:%2: ", filename, lineno);
8641 else if (program_name)
8642 fprintf(stderr, "%s: ", program_name);
8645 fputs("fatal error: ", stderr);
8650 fputs("warning: ", stderr);
8652 case OUTPUT_WARNING:
8653 double fromtop = topdiv->get_vertical_position().to_units() / warn_scale;
8654 fprintf(stderr, "warning [p %d, %.1f%c",
8655 topdiv->get_page_number(), fromtop, warn_scaling_indicator);
8656 if (topdiv != curdiv) {
8657 double fromtop1 = curdiv->get_vertical_position().to_units()
8659 fprintf(stderr, ", div `%s', %.1f%c",
8660 curdiv->get_diversion_name(), fromtop1, warn_scaling_indicator);
8662 fprintf(stderr, "]: ");
8665 errprint(format, arg1, arg2, arg3);
8666 fputc('\n', stderr);
8669 cleanup_and_exit(1);
8672 int warning(warning_type t,
8678 if ((t & warning_mask) != 0) {
8679 do_error(WARNING, format, arg1, arg2, arg3);
8686 int output_warning(warning_type t,
8692 if ((t & warning_mask) != 0) {
8693 do_error(OUTPUT_WARNING, format, arg1, arg2, arg3);
8700 void error(const char *format,
8705 do_error(ERROR, format, arg1, arg2, arg3);
8708 void fatal(const char *format,
8713 do_error(FATAL, format, arg1, arg2, arg3);
8716 void fatal_with_file_and_line(const char *filename, int lineno,
8722 fprintf(stderr, "%s:%d: fatal error: ", filename, lineno);
8723 errprint(format, arg1, arg2, arg3);
8724 fputc('\n', stderr);
8726 cleanup_and_exit(1);
8729 void error_with_file_and_line(const char *filename, int lineno,
8735 fprintf(stderr, "%s:%d: error: ", filename, lineno);
8736 errprint(format, arg1, arg2, arg3);
8737 fputc('\n', stderr);
8741 dictionary charinfo_dictionary(501);
8743 charinfo *get_charinfo(symbol nm)
8745 void *p = charinfo_dictionary.lookup(nm);
8747 return (charinfo *)p;
8748 charinfo *cp = new charinfo(nm);
8749 (void)charinfo_dictionary.lookup(nm, cp);
8753 int charinfo::next_index = 0;
8755 charinfo::charinfo(symbol s)
8756 : translation(0), mac(0), special_translation(TRANSLATE_NONE),
8757 hyphenation_code(0), flags(0), ascii_code(0), asciify_code(0),
8758 not_found(0), transparent_translate(1), translate_input(0),
8759 mode(CHAR_NORMAL), nm(s)
8761 index = next_index++;
8766 int charinfo::get_unicode_code()
8768 if (ascii_code != '\0')
8770 return glyph_to_unicode(this);
8773 void charinfo::set_hyphenation_code(unsigned char c)
8775 hyphenation_code = c;
8778 void charinfo::set_translation(charinfo *ci, int tt, int ti)
8782 if (hyphenation_code != 0)
8783 ci->set_hyphenation_code(hyphenation_code);
8784 if (asciify_code != 0)
8785 ci->set_asciify_code(asciify_code);
8786 else if (ascii_code != 0)
8787 ci->set_asciify_code(ascii_code);
8788 ci->set_translation_input();
8790 special_translation = TRANSLATE_NONE;
8791 transparent_translate = tt;
8794 // Recompute flags for all entries in the charinfo dictionary.
8797 dictionary_iterator iter(charinfo_dictionary);
8800 while (iter.get(&s, (void **)&ci)) {
8801 assert(!s.is_null());
8807 // Get the union of all flags affecting this charinfo.
8808 void charinfo::get_flags()
8810 dictionary_iterator iter(char_class_dictionary);
8813 while (iter.get(&s, (void **)&ci)) {
8814 assert(!s.is_null());
8815 if (ci->contains(get_unicode_code())) {
8816 #if defined(DEBUGGING)
8818 fprintf(stderr, "charinfo::get_flags %p %s %d\n",
8819 (void *)ci, ci->nm.contents(), ci->flags);
8826 void charinfo::set_special_translation(int c, int tt)
8828 special_translation = c;
8830 transparent_translate = tt;
8833 void charinfo::set_ascii_code(unsigned char c)
8838 void charinfo::set_asciify_code(unsigned char c)
8843 macro *charinfo::set_macro(macro *m)
8850 macro *charinfo::setx_macro(macro *m, char_mode cm)
8858 void charinfo::set_number(int n)
8864 int charinfo::get_number()
8866 assert(number >= 0);
8870 bool charinfo::contains(int c, bool already_called)
8872 if (already_called) {
8873 warning(WARN_SYNTAX,
8874 "cyclic nested class detected while processing character code %1",
8878 std::vector<std::pair<int, int> >::const_iterator ranges_iter;
8879 ranges_iter = ranges.begin();
8880 while (ranges_iter != ranges.end()) {
8881 if (c >= ranges_iter->first && c <= ranges_iter->second) {
8882 #if defined(DEBUGGING)
8884 fprintf(stderr, "charinfo::contains(%d)\n", c);
8891 std::vector<charinfo *>::const_iterator nested_iter;
8892 nested_iter = nested_classes.begin();
8893 while (nested_iter != nested_classes.end()) {
8894 if ((*nested_iter)->contains(c, true))
8902 bool charinfo::contains(symbol s, bool already_called)
8904 if (already_called) {
8905 warning(WARN_SYNTAX,
8906 "cyclic nested class detected while processing symbol %1",
8910 const char *unicode = glyph_name_to_unicode(s.contents());
8911 if (unicode != NULL && strchr(unicode, '_') == NULL) {
8913 int c = (int)strtol(unicode, &ignore, 16);
8914 return contains(c, true);
8920 bool charinfo::contains(charinfo *, bool)
8926 symbol UNNAMED_SYMBOL("---");
8928 // For numbered characters not between 0 and 255, we make a symbol out
8929 // of the number and store them in this dictionary.
8931 dictionary numbered_charinfo_dictionary(11);
8933 charinfo *get_charinfo_by_number(int n)
8935 static charinfo *number_table[256];
8937 if (n >= 0 && n < 256) {
8938 charinfo *ci = number_table[n];
8940 ci = new charinfo(UNNAMED_SYMBOL);
8942 number_table[n] = ci;
8947 symbol ns(i_to_a(n));
8948 charinfo *ci = (charinfo *)numbered_charinfo_dictionary.lookup(ns);
8950 ci = new charinfo(UNNAMED_SYMBOL);
8952 (void)numbered_charinfo_dictionary.lookup(ns, ci);
8958 // This overrides the same function from libgroff; while reading font
8959 // definition files it puts single-letter glyph names into `charset_table'
8960 // and converts glyph names of the form `\x' (`x' a single letter) into `x'.
8961 // Consequently, symbol("x") refers to glyph name `\x', not `x'.
8963 glyph *name_to_glyph(const char *nm)
8967 ci = charset_table[nm[0] & 0xff];
8968 else if (nm[0] == '\\' && nm[2] == 0)
8969 ci = get_charinfo(symbol(nm + 1));
8971 ci = get_charinfo(symbol(nm));
8972 return ci->as_glyph();
8975 glyph *number_to_glyph(int n)
8977 return get_charinfo_by_number(n)->as_glyph();
8980 const char *glyph_to_name(glyph *g)
8982 charinfo *ci = (charinfo *)g; // Every glyph is actually a charinfo.
8983 return (ci->nm != UNNAMED_SYMBOL ? ci->nm.contents() : NULL);