2 /* Copyright (C) 1989-2018 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"
41 // Needed for getpid() and isatty()
46 #ifdef NEED_DECLARATION_PUTENV
48 int putenv(const char *);
50 #endif /* NEED_DECLARATION_PUTENV */
52 #define MACRO_PREFIX "tmac."
53 #define MACRO_POSTFIX ".tmac"
54 #define INITIAL_STARTUP_FILE "troffrc"
55 #define FINAL_STARTUP_FILE "troffrc-end"
56 #define DEFAULT_INPUT_STACK_LIMIT 1000
58 #ifndef DEFAULT_WARNING_MASK
59 // warnings that are enabled by default
60 #define DEFAULT_WARNING_MASK \
61 (WARN_CHAR|WARN_NUMBER|WARN_BREAK|WARN_SPACE|WARN_FONT|WARN_FILE)
64 // initial size of buffer for reading names; expanded as necessary
67 extern "C" const char *program_name;
68 extern "C" const char *Version_string;
71 void init_column_requests();
74 static node *read_draw_node();
75 static void read_color_draw_node(token &);
76 static void push_token(const token &);
81 void transparent_file();
86 int color_flag = 1; // colors are on by default
87 static int backtrace_flag = 0;
89 char *pipe_command = 0;
91 charinfo *charset_table[256];
92 unsigned char hpf_code_table[256];
94 static int warning_mask = DEFAULT_WARNING_MASK;
95 static int inhibit_errors = 0;
96 static int ignoring = 0;
98 static void enable_warning(const char *);
99 static void disable_warning(const char *);
101 static int escape_char = '\\';
102 static symbol end_macro_name;
103 static symbol blank_line_macro_name;
104 static symbol leading_spaces_macro_name;
105 static int compatible_flag = 0;
106 int ascii_output_flag = 0;
107 int suppress_output_flag = 0;
109 int begin_level = 0; // number of nested \O escapes
111 int have_input = 0; // whether \f, \F, \D'F...', \H, \m, \M,
112 // \O[345], \R, \s, or \S has been processed
114 int old_have_input = 0; // value of have_input right before \n
115 int tcommand_flag = 0;
116 int unsafe_flag = 0; // safer by default
118 int have_string_arg = 0; // whether we have \*[foo bar...]
120 double spread_limit = -3.0 - 1.0; // negative means deactivated
123 char warn_scaling_indicator;
124 int debug_state = 0; // turns on debugging of the html troff state
126 search_path *mac_path = &safer_macro_path;
128 // Defaults to the current directory.
129 search_path include_search_path(0, 0, 0, 1);
131 static int get_copy(node**, int = 0, int = 0);
132 static void copy_mode_error(const char *,
133 const errarg & = empty_errarg,
134 const errarg & = empty_errarg,
135 const errarg & = empty_errarg);
137 enum read_mode { ALLOW_EMPTY, WITH_ARGS, NO_ARGS };
138 static symbol read_escape_name(read_mode = NO_ARGS);
139 static symbol read_long_escape_name(read_mode = NO_ARGS);
140 static void interpolate_string(symbol);
141 static void interpolate_string_with_args(symbol);
142 static void interpolate_macro(symbol, int = 0);
143 static void interpolate_number_format(symbol);
144 static void interpolate_environment_variable(symbol);
146 static symbol composite_glyph_name(symbol);
147 static void interpolate_arg(symbol);
148 static request_or_macro *lookup_request(symbol);
149 static int get_delim_number(units *, unsigned char);
150 static int get_delim_number(units *, unsigned char, units);
151 static symbol do_get_long_name(int, char);
152 static int get_line_arg(units *res, unsigned char si, charinfo **cp);
153 static int read_size(int *);
154 static symbol get_delim_name();
155 static void init_registers();
156 static void trapping_blank_line();
158 // this is for gcc 2.95 with old versions of libstdc++
159 #define input_iterator my_input_iterator
161 class input_iterator;
162 input_iterator *make_temp_iterator(const char *);
163 const char *input_char_description(int);
165 void process_input_stack();
166 void chop_macro(); // declare to avoid friend name injection
169 void set_escape_char()
173 error("bad escape character");
177 escape_char = tok.ch();
190 static int saved_escape_char = '\\';
192 void save_escape_char()
194 saved_escape_char = escape_char;
198 void restore_escape_char()
200 escape_char = saved_escape_char;
206 class input_iterator {
209 input_iterator(int is_div);
210 virtual ~input_iterator() {}
212 friend class input_stack;
214 statem *diversion_state;
216 const unsigned char *ptr;
217 const unsigned char *eptr;
218 input_iterator *next;
220 virtual int fill(node **);
222 virtual int has_args() { return 0; }
223 virtual int nargs() { return 0; }
224 virtual input_iterator *get_arg(int) { return 0; }
225 virtual arg_list *get_arg_list() { return 0; }
226 virtual symbol get_macro_name() { return NULL_SYMBOL; }
227 virtual int space_follows_arg(int) { return 0; }
228 virtual int get_break_flag() { return 0; }
229 virtual int get_location(int, const char **, int *) { return 0; }
230 virtual void backtrace() {}
231 virtual int set_location(const char *, int) { return 0; }
232 virtual int next_file(FILE *, const char *) { return 0; }
233 virtual void shift(int) {}
234 virtual int is_boundary() {return 0; }
235 virtual int is_file() { return 0; }
236 virtual int is_macro() { return 0; }
237 virtual void save_compatible_flag(int) {}
238 virtual int get_compatible_flag() { return 0; }
241 input_iterator::input_iterator()
242 : is_diversion(0), ptr(0), eptr(0)
246 input_iterator::input_iterator(int is_div)
247 : is_diversion(is_div), ptr(0), eptr(0)
251 int input_iterator::fill(node **)
256 int input_iterator::peek()
261 inline int input_iterator::get(node **p)
263 return ptr < eptr ? *ptr++ : fill(p);
266 class input_boundary : public input_iterator {
268 int is_boundary() { return 1; }
271 class input_return_boundary : public input_iterator {
273 int is_boundary() { return 2; }
276 class file_iterator : public input_iterator {
279 const char *filename;
283 enum { BUF_SIZE = 512 };
284 unsigned char buf[BUF_SIZE];
287 file_iterator(FILE *, const char *, int = 0);
291 int get_location(int, const char **, int *);
293 int set_location(const char *, int);
294 int next_file(FILE *, const char *);
298 file_iterator::file_iterator(FILE *f, const char *fn, int po)
299 : fp(f), lineno(1), filename(fn), popened(po),
300 newline_flag(0), seen_escape(0)
302 if ((font::use_charnames_in_special) && (fn != 0)) {
305 the_output->put_filename(fn, po);
309 file_iterator::~file_iterator()
314 void file_iterator::close()
318 #ifndef POPEN_MISSING
321 #endif /* not POPEN_MISSING */
326 int file_iterator::is_file()
331 int file_iterator::next_file(FILE *f, const char *s)
345 int file_iterator::fill(node **)
350 unsigned char *p = buf;
352 unsigned char *e = p + BUF_SIZE;
357 if (invalid_input_char(c))
358 warning(WARN_INPUT, "invalid input character code %1", int(c));
366 seen_escape = (c == '\\');
379 int file_iterator::peek()
382 while (invalid_input_char(c)) {
383 warning(WARN_INPUT, "invalid input character code %1", int(c));
391 int file_iterator::get_location(int /*allow_macro*/,
392 const char **filenamep, int *linenop)
395 if (filename != 0 && strcmp(filename, "-") == 0)
396 *filenamep = "<standard input>";
398 *filenamep = filename;
402 void file_iterator::backtrace()
404 errprint("%1:%2: backtrace: %3 '%1'\n", filename, lineno,
405 popened ? "process" : "file");
408 int file_iterator::set_location(const char *f, int ln)
414 the_output->put_filename(f, 0);
420 input_iterator nil_iterator;
424 static int get(node **);
426 static void push(input_iterator *);
427 static input_iterator *get_arg(int);
428 static arg_list *get_arg_list();
429 static symbol get_macro_name();
430 static int space_follows_arg(int);
431 static int get_break_flag();
433 static int get_location(int, const char **, int *);
434 static int set_location(const char *, int);
435 static void backtrace();
436 static void backtrace_all();
437 static void next_file(FILE *, const char *);
438 static void end_file();
439 static void shift(int n);
440 static void add_boundary();
441 static void add_return_boundary();
442 static int is_return_boundary();
443 static void remove_boundary();
444 static int get_level();
445 static int get_div_level();
446 static void increase_level();
447 static void decrease_level();
449 static void pop_macro();
450 static void save_compatible_flag(int);
451 static int get_compatible_flag();
452 static statem *get_diversion_state();
453 static void check_end_diversion(input_iterator *t);
455 static int div_level;
456 static statem *diversion_state;
458 static input_iterator *top;
460 static int finish_get(node **);
461 static int finish_peek();
464 input_iterator *input_stack::top = &nil_iterator;
465 int input_stack::level = 0;
466 int input_stack::limit = DEFAULT_INPUT_STACK_LIMIT;
467 int input_stack::div_level = 0;
468 statem *input_stack::diversion_state = NULL;
472 inline int input_stack::get_level()
477 inline void input_stack::increase_level()
482 inline void input_stack::decrease_level()
487 inline int input_stack::get_div_level()
492 inline int input_stack::get(node **np)
494 int res = (top->ptr < top->eptr) ? *top->ptr++ : finish_get(np);
496 old_have_input = have_input;
502 int input_stack::finish_get(node **np)
505 int c = top->fill(np);
506 if (c != EOF || top->is_boundary())
508 if (top == &nil_iterator)
510 input_iterator *tem = top;
511 check_end_diversion(tem);
512 #if defined(DEBUGGING)
514 if (tem->is_diversion)
516 "in diversion level = %d\n", input_stack::get_div_level());
521 if (top->ptr < top->eptr)
528 inline int input_stack::peek()
530 return (top->ptr < top->eptr) ? *top->ptr : finish_peek();
533 void input_stack::check_end_diversion(input_iterator *t)
535 if (t->is_diversion) {
538 delete diversion_state;
539 diversion_state = t->diversion_state;
543 int input_stack::finish_peek()
547 if (c != EOF || top->is_boundary())
549 if (top == &nil_iterator)
551 input_iterator *tem = top;
552 check_end_diversion(tem);
556 if (top->ptr < top->eptr)
563 void input_stack::add_boundary()
565 push(new input_boundary);
568 void input_stack::add_return_boundary()
570 push(new input_return_boundary);
573 int input_stack::is_return_boundary()
575 return top->is_boundary() == 2;
578 void input_stack::remove_boundary()
580 assert(top->is_boundary());
581 input_iterator *temp = top->next;
582 check_end_diversion(top);
589 void input_stack::push(input_iterator *in)
593 if (++level > limit && limit > 0)
594 fatal("input stack limit exceeded (probable infinite loop)");
597 if (top->is_diversion) {
599 in->diversion_state = diversion_state;
600 diversion_state = curenv->construct_state(0);
601 #if defined(DEBUGGING)
603 curenv->dump_troff_state();
608 #if defined(DEBUGGING)
610 if (top->is_diversion) {
612 "in diversion level = %d\n", input_stack::get_div_level());
618 statem *get_diversion_state()
620 return input_stack::get_diversion_state();
623 statem *input_stack::get_diversion_state()
625 if (diversion_state == NULL)
628 return new statem(diversion_state);
631 input_iterator *input_stack::get_arg(int i)
634 for (p = top; p != 0; p = p->next)
636 return p->get_arg(i);
640 arg_list *input_stack::get_arg_list()
643 for (p = top; p != 0; p = p->next)
645 return p->get_arg_list();
649 symbol input_stack::get_macro_name()
652 for (p = top; p != 0; p = p->next)
654 return p->get_macro_name();
658 int input_stack::space_follows_arg(int i)
661 for (p = top; p != 0; p = p->next)
663 return p->space_follows_arg(i);
667 int input_stack::get_break_flag()
669 return top->get_break_flag();
672 void input_stack::shift(int n)
674 for (input_iterator *p = top; p; p = p->next)
681 int input_stack::nargs()
683 for (input_iterator *p =top; p != 0; p = p->next)
689 int input_stack::get_location(int allow_macro, const char **filenamep, int *linenop)
691 for (input_iterator *p = top; p; p = p->next)
692 if (p->get_location(allow_macro, filenamep, linenop))
697 void input_stack::backtrace()
701 // only backtrace down to (not including) the topmost file
702 for (input_iterator *p = top;
703 p && !p->get_location(0, &f, &n);
708 void input_stack::backtrace_all()
710 for (input_iterator *p = top; p; p = p->next)
714 int input_stack::set_location(const char *filename, int lineno)
716 for (input_iterator *p = top; p; p = p->next)
717 if (p->set_location(filename, lineno))
722 void input_stack::next_file(FILE *fp, const char *s)
725 for (pp = ⊤ *pp != &nil_iterator; pp = &(*pp)->next)
726 if ((*pp)->next_file(fp, s))
728 if (++level > limit && limit > 0)
729 fatal("input stack limit exceeded");
730 *pp = new file_iterator(fp, s);
731 (*pp)->next = &nil_iterator;
734 void input_stack::end_file()
736 for (input_iterator **pp = ⊤ *pp != &nil_iterator; pp = &(*pp)->next)
737 if ((*pp)->is_file()) {
738 input_iterator *tem = *pp;
739 check_end_diversion(tem);
747 void input_stack::clear()
750 while (top != &nil_iterator) {
751 if (top->is_boundary())
753 input_iterator *tem = top;
754 check_end_diversion(tem);
759 // Keep while_request happy.
760 for (; nboundaries > 0; --nboundaries)
761 add_return_boundary();
764 void input_stack::pop_macro()
769 if (top->next == &nil_iterator)
771 if (top->is_boundary())
773 is_macro = top->is_macro();
774 input_iterator *tem = top;
775 check_end_diversion(tem);
780 // Keep while_request happy.
781 for (; nboundaries > 0; --nboundaries)
782 add_return_boundary();
785 inline void input_stack::save_compatible_flag(int f)
787 top->save_compatible_flag(f);
790 inline int input_stack::get_compatible_flag()
792 return top->get_compatible_flag();
795 void backtrace_request()
797 input_stack::backtrace_all();
804 symbol nm = get_long_name();
805 while (!tok.newline() && !tok.eof())
808 input_stack::end_file();
811 FILE *fp = include_search_path.open_file_cautious(nm.contents());
813 error("can't open '%1': %2", nm.contents(), strerror(errno));
815 input_stack::next_file(fp, nm.contents());
823 if (!has_arg() || !get_integer(&n))
825 input_stack::shift(n);
829 static char get_char_for_escape_name(int allow_space = 0)
831 int c = get_copy(0, 0, 1);
834 copy_mode_error("end of input in escape name");
837 if (!invalid_input_char(c))
842 input_stack::push(make_temp_iterator("\n"));
845 if (c == ' ' && allow_space)
851 copy_mode_error("%1 is not allowed in an escape name",
852 input_char_description(c));
858 static symbol read_two_char_escape_name()
861 buf[0] = get_char_for_escape_name();
862 if (buf[0] != '\0') {
863 buf[1] = get_char_for_escape_name();
872 static symbol read_long_escape_name(read_mode mode)
874 int start_level = input_stack::get_level();
875 char abuf[ABUF_SIZE];
877 int buf_size = ABUF_SIZE;
882 c = get_char_for_escape_name(have_char && mode == WITH_ARGS);
889 if (mode == WITH_ARGS && c == ' ')
891 if (i + 2 > buf_size) {
893 buf = new char[ABUF_SIZE*2];
894 memcpy(buf, abuf, buf_size);
895 buf_size = ABUF_SIZE*2;
899 buf = new char[buf_size*2];
900 memcpy(buf, old_buf, buf_size);
905 if (c == ']' && input_stack::get_level() == start_level)
914 if (mode != ALLOW_EMPTY)
915 copy_mode_error("empty escape name");
927 static symbol read_escape_name(read_mode mode)
929 char c = get_char_for_escape_name();
933 return read_two_char_escape_name();
934 if (c == '[' && !compatible_flag)
935 return read_long_escape_name(mode);
942 static symbol read_increment_and_escape_name(int *incp)
944 char c = get_char_for_escape_name();
951 return read_two_char_escape_name();
954 return read_escape_name();
957 return read_escape_name();
959 if (!compatible_flag) {
961 return read_long_escape_name();
972 static int get_copy(node **nd, int defining, int handle_escape_E)
975 int c = input_stack::get(nd);
976 if (c == PUSH_GROFF_MODE) {
977 input_stack::save_compatible_flag(compatible_flag);
981 if (c == PUSH_COMP_MODE) {
982 input_stack::save_compatible_flag(compatible_flag);
986 if (c == POP_GROFFCOMP_MODE) {
987 compatible_flag = input_stack::get_compatible_flag();
990 if (c == BEGIN_QUOTE) {
991 input_stack::increase_level();
994 if (c == END_QUOTE) {
995 input_stack::decrease_level();
998 if (c == DOUBLE_QUOTE)
1000 if (c == ESCAPE_E && handle_escape_E)
1002 if (c == ESCAPE_NEWLINE) {
1006 c = input_stack::get(nd);
1007 } while (c == ESCAPE_NEWLINE);
1009 if (c != escape_char || escape_char <= 0)
1012 c = input_stack::peek();
1017 (void)input_stack::get(0);
1018 while ((c = input_stack::get(0)) != '\n' && c != EOF)
1021 case '#': // Like \" but newline is ignored.
1022 (void)input_stack::get(0);
1023 while ((c = input_stack::get(0)) != '\n')
1029 (void)input_stack::get(0);
1030 symbol s = read_escape_name();
1031 if (!(s.is_null() || s.is_empty()))
1037 (void)input_stack::get(0);
1038 symbol s = read_escape_name(WITH_ARGS);
1039 if (!(s.is_null() || s.is_empty())) {
1040 if (have_string_arg) {
1041 have_string_arg = 0;
1042 interpolate_string_with_args(s);
1045 interpolate_string(s);
1050 (void)input_stack::get(0);
1053 (void)input_stack::get(0);
1056 (void)input_stack::get(0);
1057 if (handle_escape_E)
1062 (void)input_stack::get(0);
1064 symbol s = read_increment_and_escape_name(&inc);
1065 if (!(s.is_null() || s.is_empty()))
1066 interpolate_number_reg(s, inc);
1071 (void)input_stack::get(0);
1072 symbol s = read_escape_name();
1073 if (!(s.is_null() || s.is_empty()))
1074 interpolate_number_format(s);
1078 (void)input_stack::get(0);
1082 (void)input_stack::get(0);
1083 symbol s = read_escape_name();
1084 if (!(s.is_null() || s.is_empty()))
1085 interpolate_environment_variable(s);
1089 (void)input_stack::get(0);
1091 return ESCAPE_NEWLINE;
1094 (void)input_stack::get(0);
1095 return ESCAPE_SPACE;
1097 (void)input_stack::get(0);
1098 return ESCAPE_TILDE;
1100 (void)input_stack::get(0);
1101 return ESCAPE_COLON;
1103 (void)input_stack::get(0);
1106 (void)input_stack::get(0);
1107 return ESCAPE_CIRCUMFLEX;
1109 (void)input_stack::get(0);
1110 return ESCAPE_LEFT_BRACE;
1112 (void)input_stack::get(0);
1113 return ESCAPE_RIGHT_BRACE;
1115 (void)input_stack::get(0);
1116 return ESCAPE_LEFT_QUOTE;
1118 (void)input_stack::get(0);
1119 return ESCAPE_RIGHT_QUOTE;
1121 (void)input_stack::get(0);
1122 return ESCAPE_HYPHEN;
1124 (void)input_stack::get(0);
1125 return ESCAPE_UNDERSCORE;
1127 (void)input_stack::get(0);
1130 (void)input_stack::get(0);
1133 (void)input_stack::get(0);
1134 return ESCAPE_QUESTION;
1136 (void)input_stack::get(0);
1137 return ESCAPE_AMPERSAND;
1139 (void)input_stack::get(0);
1140 return ESCAPE_RIGHT_PARENTHESIS;
1142 (void)input_stack::get(0);
1145 (void)input_stack::get(0);
1146 return ESCAPE_PERCENT;
1148 if (c == escape_char) {
1149 (void)input_stack::get(0);
1158 class non_interpreted_char_node : public node {
1161 non_interpreted_char_node(unsigned char);
1163 int interpret(macro *);
1170 int non_interpreted_char_node::same(node *nd)
1172 return c == ((non_interpreted_char_node *)nd)->c;
1175 const char *non_interpreted_char_node::type()
1177 return "non_interpreted_char_node";
1180 int non_interpreted_char_node::force_tprint()
1185 int non_interpreted_char_node::is_tag()
1190 non_interpreted_char_node::non_interpreted_char_node(unsigned char n) : c(n)
1195 node *non_interpreted_char_node::copy()
1197 return new non_interpreted_char_node(c);
1200 int non_interpreted_char_node::interpret(macro *mac)
1206 static void do_width();
1207 static node *do_non_interpreted();
1208 static node *do_special();
1209 static node *do_suppress(symbol nm);
1210 static void do_register();
1212 dictionary color_dictionary(501);
1214 static color *lookup_color(symbol nm)
1216 assert(!nm.is_null());
1217 if (nm == default_symbol)
1218 return &default_color;
1219 color *c = (color *)color_dictionary.lookup(nm);
1221 warning(WARN_COLOR, "color '%1' not defined", nm.contents());
1225 void do_glyph_color(symbol nm)
1230 curenv->set_glyph_color(curenv->get_prev_glyph_color());
1232 color *tem = lookup_color(nm);
1234 curenv->set_glyph_color(tem);
1236 (void)color_dictionary.lookup(nm, new color(nm));
1240 void do_fill_color(symbol nm)
1245 curenv->set_fill_color(curenv->get_prev_fill_color());
1247 color *tem = lookup_color(nm);
1249 curenv->set_fill_color(tem);
1251 (void)color_dictionary.lookup(nm, new color(nm));
1255 static unsigned int get_color_element(const char *scheme, const char *col)
1258 if (!get_number(&val, 'f')) {
1259 warning(WARN_COLOR, "%1 in %2 definition set to 0", col, scheme);
1264 warning(WARN_RANGE, "%1 cannot be negative: set to 0", col);
1267 if (val > color::MAX_COLOR_VAL+1) {
1268 warning(WARN_RANGE, "%1 cannot be greater than 1", col);
1269 // we change 0x10000 to 0xffff
1270 return color::MAX_COLOR_VAL;
1272 return (unsigned int)val;
1275 static color *read_rgb(char end = 0)
1277 symbol component = do_get_long_name(0, end);
1278 if (component.is_null()) {
1279 warning(WARN_COLOR, "missing rgb color values");
1282 const char *s = component.contents();
1283 color *col = new color;
1285 if (!col->read_rgb(s)) {
1286 warning(WARN_COLOR, "expecting rgb color definition not '%1'", s);
1293 input_stack::push(make_temp_iterator(" "));
1294 input_stack::push(make_temp_iterator(s));
1296 unsigned int r = get_color_element("rgb color", "red component");
1297 unsigned int g = get_color_element("rgb color", "green component");
1298 unsigned int b = get_color_element("rgb color", "blue component");
1299 col->set_rgb(r, g, b);
1304 static color *read_cmy(char end = 0)
1306 symbol component = do_get_long_name(0, end);
1307 if (component.is_null()) {
1308 warning(WARN_COLOR, "missing cmy color values");
1311 const char *s = component.contents();
1312 color *col = new color;
1314 if (!col->read_cmy(s)) {
1315 warning(WARN_COLOR, "expecting cmy color definition not '%1'", s);
1322 input_stack::push(make_temp_iterator(" "));
1323 input_stack::push(make_temp_iterator(s));
1325 unsigned int c = get_color_element("cmy color", "cyan component");
1326 unsigned int m = get_color_element("cmy color", "magenta component");
1327 unsigned int y = get_color_element("cmy color", "yellow component");
1328 col->set_cmy(c, m, y);
1333 static color *read_cmyk(char end = 0)
1335 symbol component = do_get_long_name(0, end);
1336 if (component.is_null()) {
1337 warning(WARN_COLOR, "missing cmyk color values");
1340 const char *s = component.contents();
1341 color *col = new color;
1343 if (!col->read_cmyk(s)) {
1344 warning(WARN_COLOR, "expecting a cmyk color definition not '%1'", s);
1351 input_stack::push(make_temp_iterator(" "));
1352 input_stack::push(make_temp_iterator(s));
1354 unsigned int c = get_color_element("cmyk color", "cyan component");
1355 unsigned int m = get_color_element("cmyk color", "magenta component");
1356 unsigned int y = get_color_element("cmyk color", "yellow component");
1357 unsigned int k = get_color_element("cmyk color", "black component");
1358 col->set_cmyk(c, m, y, k);
1363 static color *read_gray(char end = 0)
1365 symbol component = do_get_long_name(0, end);
1366 if (component.is_null()) {
1367 warning(WARN_COLOR, "missing gray values");
1370 const char *s = component.contents();
1371 color *col = new color;
1373 if (!col->read_gray(s)) {
1374 warning(WARN_COLOR, "expecting a gray definition not '%1'", s);
1381 input_stack::push(make_temp_iterator("\n"));
1382 input_stack::push(make_temp_iterator(s));
1384 unsigned int g = get_color_element("gray", "gray value");
1390 static void activate_color()
1393 if (has_arg() && get_integer(&n))
1394 color_flag = n != 0;
1400 static void define_color()
1402 symbol color_name = get_long_name(1);
1403 if (color_name.is_null()) {
1407 if (color_name == default_symbol) {
1408 warning(WARN_COLOR, "default color can't be redefined");
1412 symbol style = get_long_name(1);
1413 if (style.is_null()) {
1418 if (strcmp(style.contents(), "rgb") == 0)
1420 else if (strcmp(style.contents(), "cmyk") == 0)
1422 else if (strcmp(style.contents(), "gray") == 0)
1424 else if (strcmp(style.contents(), "grey") == 0)
1426 else if (strcmp(style.contents(), "cmy") == 0)
1430 "unknown color space '%1'; use rgb, cmyk, gray or cmy",
1436 col->nm = color_name;
1437 (void)color_dictionary.lookup(color_name, col);
1442 node *do_overstrike()
1445 overstrike_node *on = new overstrike_node;
1446 int start_level = input_stack::get_level();
1450 if (tok.newline() || tok.eof()) {
1451 warning(WARN_DELIM, "missing closing delimiter");
1452 input_stack::push(make_temp_iterator("\n"));
1456 && (compatible_flag || input_stack::get_level() == start_level))
1458 if (tok.horizontal_space())
1459 on->overstrike(tok.nd->copy());
1460 else if (tok.unstretchable_space())
1462 node *n = new hmotion_node(curenv->get_space_width(),
1463 curenv->get_fill_color());
1467 charinfo *ci = tok.get_char(1);
1469 node *n = curenv->make_char_node(ci);
1478 static node *do_bracket()
1481 bracket_node *bn = new bracket_node;
1483 int start_level = input_stack::get_level();
1487 warning(WARN_DELIM, "missing closing delimiter");
1490 if (tok.newline()) {
1491 warning(WARN_DELIM, "missing closing delimiter");
1492 input_stack::push(make_temp_iterator("\n"));
1496 && (compatible_flag || input_stack::get_level() == start_level))
1498 charinfo *ci = tok.get_char(1);
1500 node *n = curenv->make_char_node(ci);
1508 static int do_name_test()
1512 int start_level = input_stack::get_level();
1517 if (tok.newline() || tok.eof()) {
1518 warning(WARN_DELIM, "missing closing delimiter");
1519 input_stack::push(make_temp_iterator("\n"));
1523 && (compatible_flag || input_stack::get_level() == start_level))
1529 return some_char && !bad_char;
1532 static int do_expr_test()
1536 int start_level = input_stack::get_level();
1537 if (!start.delimiter(1))
1540 // disable all warning and error messages temporarily
1541 int saved_warning_mask = warning_mask;
1542 int saved_inhibit_errors = inhibit_errors;
1546 int result = get_number_rigidly(&dummy, 'u');
1547 warning_mask = saved_warning_mask;
1548 inhibit_errors = saved_inhibit_errors;
1549 if (tok == start && input_stack::get_level() == start_level)
1551 // ignore everything up to the delimiter in case we aren't right there
1554 if (tok.newline() || tok.eof()) {
1555 warning(WARN_DELIM, "missing closing delimiter");
1556 input_stack::push(make_temp_iterator("\n"));
1559 if (tok == start && input_stack::get_level() == start_level)
1566 static node *do_zero_width()
1570 int start_level = input_stack::get_level();
1571 environment env(curenv);
1572 environment *oldenv = curenv;
1576 if (tok.newline() || tok.eof()) {
1577 error("missing closing delimiter");
1581 && (compatible_flag || input_stack::get_level() == start_level))
1586 node *rev = env.extract_output_line();
1594 return new zero_width_node(n);
1599 // It's undesirable for \Z to change environments, because then
1600 // \n(.w won't work as expected.
1602 static node *do_zero_width()
1604 node *rev = new dummy_node;
1607 int start_level = input_stack::get_level();
1610 if (tok.newline() || tok.eof()) {
1611 warning(WARN_DELIM, "missing closing delimiter");
1612 input_stack::push(make_temp_iterator("\n"));
1616 && (compatible_flag || input_stack::get_level() == start_level))
1618 if (!tok.add_to_node_list(&rev))
1619 error("invalid token in argument to \\Z");
1628 return new zero_width_node(n);
1633 token_node *node::get_token_node()
1638 class token_node : public node {
1641 token_node(const token &t);
1643 token_node *get_token_node();
1650 token_node::token_node(const token &t) : tk(t)
1654 node *token_node::copy()
1656 return new token_node(tk);
1659 token_node *token_node::get_token_node()
1664 int token_node::same(node *nd)
1666 return tk == ((token_node *)nd)->tk;
1669 const char *token_node::type()
1671 return "token_node";
1674 int token_node::force_tprint()
1679 int token_node::is_tag()
1684 token::token() : nd(0), type(TOKEN_EMPTY)
1693 token::token(const token &t)
1694 : nm(t.nm), c(t.c), val(t.val), dim(t.dim), type(t.type)
1696 // Use two statements to work around bug in SGI C++.
1698 nd = tem ? tem->copy() : 0;
1701 void token::operator=(const token &t)
1705 // Use two statements to work around bug in SGI C++.
1707 nd = tem ? tem->copy() : 0;
1724 return !tok.newline();
1727 void token::make_space()
1732 void token::make_newline()
1734 type = TOKEN_NEWLINE;
1746 int cc = input_stack::get(&n);
1747 if (cc != escape_char || escape_char == 0) {
1750 case PUSH_GROFF_MODE:
1751 input_stack::save_compatible_flag(compatible_flag);
1752 compatible_flag = 0;
1754 case PUSH_COMP_MODE:
1755 input_stack::save_compatible_flag(compatible_flag);
1756 compatible_flag = 1;
1758 case POP_GROFFCOMP_MODE:
1759 compatible_flag = input_stack::get_compatible_flag();
1762 input_stack::increase_level();
1765 input_stack::decrease_level();
1772 case TRANSPARENT_FILE_REQUEST:
1774 case COPY_FILE_REQUEST:
1776 case VJUSTIFY_REQUEST:
1778 type = TOKEN_REQUEST;
1782 type = TOKEN_BEGIN_TRAP;
1785 type = TOKEN_END_TRAP;
1787 case LAST_PAGE_EJECTOR:
1788 seen_last_page_ejector = 1;
1791 type = TOKEN_PAGE_EJECTOR;
1793 case ESCAPE_PERCENT:
1795 type = TOKEN_HYPHEN_INDICATOR;
1799 type = TOKEN_UNSTRETCHABLE_SPACE;
1803 type = TOKEN_STRETCHABLE_SPACE;
1807 type = TOKEN_ZERO_WIDTH_BREAK;
1811 type = TOKEN_ESCAPE;
1814 goto handle_escape_char;
1817 type = TOKEN_HORIZONTAL_SPACE;
1818 nd = new hmotion_node(curenv->get_narrow_space_width(),
1819 curenv->get_fill_color());
1821 case ESCAPE_CIRCUMFLEX:
1823 type = TOKEN_HORIZONTAL_SPACE;
1824 nd = new hmotion_node(curenv->get_half_narrow_space_width(),
1825 curenv->get_fill_color());
1827 case ESCAPE_NEWLINE:
1830 case ESCAPE_LEFT_BRACE:
1832 type = TOKEN_LEFT_BRACE;
1834 case ESCAPE_RIGHT_BRACE:
1836 type = TOKEN_RIGHT_BRACE;
1838 case ESCAPE_LEFT_QUOTE:
1840 type = TOKEN_SPECIAL;
1843 case ESCAPE_RIGHT_QUOTE:
1845 type = TOKEN_SPECIAL;
1850 type = TOKEN_SPECIAL;
1853 case ESCAPE_UNDERSCORE:
1855 type = TOKEN_SPECIAL;
1860 type = TOKEN_INTERRUPT;
1864 type = TOKEN_TRANSPARENT;
1866 case ESCAPE_QUESTION:
1868 nd = do_non_interpreted();
1874 case ESCAPE_AMPERSAND:
1878 case ESCAPE_RIGHT_PARENTHESIS:
1879 ESCAPE_RIGHT_PARENTHESIS:
1880 type = TOKEN_TRANSPARENT_DUMMY;
1883 type = TOKEN_BACKSPACE;
1892 type = TOKEN_NEWLINE;
1895 type = TOKEN_LEADER;
1900 token_node *tn = n->get_token_node();
1919 cc = input_stack::get(&n);
1922 nm = read_two_char_escape_name();
1923 type = TOKEN_SPECIAL;
1927 error("end of input after escape character");
1930 goto ESCAPE_LEFT_QUOTE;
1932 goto ESCAPE_RIGHT_QUOTE;
1936 goto ESCAPE_UNDERSCORE;
1938 goto ESCAPE_PERCENT;
1942 nd = new hmotion_node(curenv->get_digit_width(),
1943 curenv->get_fill_color());
1944 type = TOKEN_HORIZONTAL_SPACE;
1949 goto ESCAPE_CIRCUMFLEX;
1951 type = TOKEN_ITALIC_CORRECTION;
1955 nd = new left_italic_corrected_node;
1958 goto ESCAPE_AMPERSAND;
1960 goto ESCAPE_RIGHT_PARENTHESIS;
1964 goto ESCAPE_QUESTION;
1970 while ((cc = input_stack::get(0)) != '\n' && cc != EOF)
1973 type = TOKEN_NEWLINE;
1977 case '#': // Like \" but newline is ignored.
1978 while ((cc = input_stack::get(0)) != '\n')
1986 symbol s = read_escape_name();
1987 if (!(s.is_null() || s.is_empty()))
1993 symbol s = read_escape_name(WITH_ARGS);
1994 if (!(s.is_null() || s.is_empty())) {
1995 if (have_string_arg) {
1996 have_string_arg = 0;
1997 interpolate_string_with_args(s);
2000 interpolate_string(s);
2005 nd = new non_interpreted_char_node('\001');
2009 c = '0' + do_name_test();
2017 c = '0' + do_expr_test();
2023 nm = get_delim_name();
2026 type = TOKEN_SPECIAL;
2030 nd = new vmotion_node(curenv->get_size() / 2,
2031 curenv->get_fill_color());
2034 nd = read_draw_node();
2042 goto handle_escape_char;
2045 symbol s = read_escape_name(ALLOW_EMPTY);
2049 for (p = s.contents(); *p != '\0'; p++)
2052 if (*p || s.is_empty())
2053 curenv->set_font(s);
2055 curenv->set_font(atoi(s.contents()));
2056 if (!compatible_flag)
2062 symbol s = read_escape_name(ALLOW_EMPTY);
2065 curenv->set_family(s);
2071 symbol s = read_escape_name();
2072 if (!(s.is_null() || s.is_empty()))
2073 interpolate_number_format(s);
2077 if (!get_delim_number(&x, 'm'))
2079 type = TOKEN_HORIZONTAL_SPACE;
2080 nd = new hmotion_node(x, curenv->get_fill_color());
2083 // don't take height increments relative to previous height if
2084 // in compatibility mode
2085 if (!compatible_flag && curenv->get_char_height()) {
2086 if (get_delim_number(&x, 'z', curenv->get_char_height()))
2087 curenv->set_char_height(x);
2090 if (get_delim_number(&x, 'z', curenv->get_requested_point_size()))
2091 curenv->set_char_height(x);
2093 if (!compatible_flag)
2097 nm = read_escape_name();
2098 if (nm.is_null() || nm.is_empty())
2100 type = TOKEN_MARK_INPUT;
2106 if (!get_line_arg(&x, (cc == 'l' ? 'm': 'v'), &s))
2109 s = get_charinfo(cc == 'l' ? "ru" : "br");
2111 node *char_node = curenv->make_char_node(s);
2113 nd = new hline_node(x, char_node);
2115 nd = new vline_node(x, char_node);
2119 do_glyph_color(read_escape_name(ALLOW_EMPTY));
2120 if (!compatible_flag)
2124 do_fill_color(read_escape_name(ALLOW_EMPTY));
2125 if (!compatible_flag)
2131 symbol s = read_increment_and_escape_name(&inc);
2132 if (!(s.is_null() || s.is_empty()))
2133 interpolate_number_reg(s, inc);
2137 if (!get_delim_number(&val, 0))
2140 warning(WARN_CHAR, "invalid numbered character %1", val);
2143 type = TOKEN_NUMBERED_CHAR;
2146 nd = do_overstrike();
2150 nd = do_suppress(read_escape_name());
2156 type = TOKEN_SPREAD;
2160 nd = new vmotion_node(-curenv->get_size(), curenv->get_fill_color());
2164 if (!compatible_flag)
2169 curenv->set_size(x);
2170 if (!compatible_flag)
2174 if (get_delim_number(&x, 0))
2175 curenv->set_char_slant(x);
2176 if (!compatible_flag)
2181 nd = new non_interpreted_char_node('\t');
2185 nd = new vmotion_node(-curenv->get_size() / 2,
2186 curenv->get_fill_color());
2189 if (!get_delim_number(&x, 'v'))
2192 nd = new vmotion_node(x, curenv->get_fill_color());
2196 symbol s = read_escape_name();
2197 if (!(s.is_null() || s.is_empty()))
2198 interpolate_environment_variable(s);
2205 if (!get_delim_number(&x, 'v'))
2208 nd = new extra_size_node(x);
2218 symbol s = read_escape_name();
2219 if (s.is_null() || s.is_empty())
2221 request_or_macro *p = lookup_request(s);
2222 macro *m = p->to_macro();
2224 error("can't transparently throughput a request");
2227 nd = new special_node(*m);
2234 if (type == TOKEN_NODE || type == TOKEN_HORIZONTAL_SPACE)
2235 nd = new zero_width_node(nd);
2237 charinfo *ci = get_char(1);
2240 node *gn = curenv->make_char_node(ci);
2243 nd = new zero_width_node(gn);
2249 nd = do_zero_width();
2255 goto ESCAPE_LEFT_BRACE;
2257 goto ESCAPE_RIGHT_BRACE;
2261 if (!compatible_flag) {
2262 symbol s = read_long_escape_name(WITH_ARGS);
2263 if (s.is_null() || s.is_empty())
2265 if (have_string_arg) {
2266 have_string_arg = 0;
2267 nm = composite_glyph_name(s);
2270 const char *gn = check_unicode_name(s.contents());
2272 const char *gn_decomposed = decompose_unicode(gn);
2274 gn = &gn_decomposed[1];
2275 const char *groff_gn = unicode_to_glyph_name(gn);
2277 nm = symbol(groff_gn);
2279 char *buf = new char[strlen(gn) + 1 + 1];
2287 nm = symbol(s.contents());
2289 type = TOKEN_SPECIAL;
2292 goto handle_normal_char;
2294 if (cc != escape_char && cc != '.')
2295 warning(WARN_ESCAPE, "escape character ignored before %1",
2296 input_char_description(cc));
2297 goto handle_normal_char;
2303 int token::operator==(const token &t)
2312 case TOKEN_NUMBERED_CHAR:
2313 return val == t.val;
2319 int token::operator!=(const token &t)
2321 return !(*this == t);
2324 // is token a suitable delimiter (like ')?
2326 int token::delimiter(int err)
2355 error("cannot use character '%1' as a starting delimiter", char(c));
2361 // the user doesn't know what a node is
2363 error("missing argument or invalid starting delimiter");
2366 case TOKEN_STRETCHABLE_SPACE:
2367 case TOKEN_UNSTRETCHABLE_SPACE:
2368 case TOKEN_HORIZONTAL_SPACE:
2372 error("cannot use %1 as a starting delimiter", description());
2379 const char *token::description()
2383 case TOKEN_BACKSPACE:
2384 return "a backspace character";
2395 case TOKEN_HYPHEN_INDICATOR:
2397 case TOKEN_INTERRUPT:
2399 case TOKEN_ITALIC_CORRECTION:
2402 return "a leader character";
2403 case TOKEN_LEFT_BRACE:
2405 case TOKEN_MARK_INPUT:
2411 case TOKEN_NUMBERED_CHAR:
2413 case TOKEN_RIGHT_BRACE:
2418 return "a special character";
2421 case TOKEN_STRETCHABLE_SPACE:
2423 case TOKEN_UNSTRETCHABLE_SPACE:
2425 case TOKEN_HORIZONTAL_SPACE:
2426 return "a horizontal space";
2428 return "a tab character";
2429 case TOKEN_TRANSPARENT:
2431 case TOKEN_TRANSPARENT_DUMMY:
2433 case TOKEN_ZERO_WIDTH_BREAK:
2436 return "end of input";
2440 return "a magic token";
2445 while (!tok.newline())
2456 if (has_arg() && get_integer(&n))
2457 compatible_flag = n != 0;
2459 compatible_flag = 1;
2463 static void empty_name_warning(int required)
2465 if (tok.newline() || tok.eof()) {
2467 warning(WARN_MISSING, "missing name");
2469 else if (tok.right_brace() || tok.tab()) {
2470 const char *start = tok.description();
2473 } while (tok.space() || tok.right_brace() || tok.tab());
2474 if (!tok.newline() && !tok.eof())
2475 error("%1 is not allowed before an argument", start);
2477 warning(WARN_MISSING, "missing name");
2480 error("name expected (got %1)", tok.description());
2482 error("name expected (got %1): treated as missing", tok.description());
2485 static void non_empty_name_warning()
2487 if (!tok.newline() && !tok.eof() && !tok.space() && !tok.tab()
2488 && !tok.right_brace()
2489 // We don't want to give a warning for .el\{
2490 && !tok.left_brace())
2491 error("%1 is not allowed in a name", tok.description());
2494 symbol get_name(int required)
2496 if (compatible_flag) {
2499 if ((buf[0] = tok.ch()) != 0) {
2501 if ((buf[1] = tok.ch()) != 0) {
2506 non_empty_name_warning();
2510 empty_name_warning(required);
2515 return get_long_name(required);
2518 symbol get_long_name(int required)
2520 return do_get_long_name(required, 0);
2523 static symbol do_get_long_name(int required, char end)
2527 char abuf[ABUF_SIZE];
2529 int buf_size = ABUF_SIZE;
2532 // If end != 0 we normally have to append a null byte
2533 if (i + 2 > buf_size) {
2535 buf = new char[ABUF_SIZE*2];
2536 memcpy(buf, abuf, buf_size);
2537 buf_size = ABUF_SIZE*2;
2540 char *old_buf = buf;
2541 buf = new char[buf_size*2];
2542 memcpy(buf, old_buf, buf_size);
2547 if ((buf[i] = tok.ch()) == 0 || buf[i] == end)
2553 empty_name_warning(required);
2556 if (end && buf[i] == end)
2559 non_empty_name_warning();
2572 topdiv->set_last_page();
2573 if (!end_macro_name.is_null()) {
2574 spring_trap(end_macro_name);
2576 process_input_stack();
2578 curenv->final_break();
2580 process_input_stack();
2582 if (topdiv->get_page_length() > 0) {
2584 topdiv->set_ejecting();
2585 static unsigned char buf[2] = { LAST_PAGE_EJECTOR, '\0' };
2586 input_stack::push(make_temp_iterator((char *)buf));
2587 topdiv->space(topdiv->get_page_length(), 1);
2589 process_input_stack();
2590 seen_last_page_ejector = 1; // should be set already
2591 topdiv->set_ejecting();
2592 push_page_ejector();
2593 topdiv->space(topdiv->get_page_length(), 1);
2595 process_input_stack();
2597 // This will only happen if a trap-invoked macro starts a diversion,
2598 // or if vertical position traps have been disabled.
2599 cleanup_and_exit(0);
2602 // This implements .ex. The input stack must be cleared before calling
2607 input_stack::clear();
2614 void return_macro_request()
2616 if (has_arg() && tok.ch())
2617 input_stack::pop_macro();
2618 input_stack::pop_macro();
2624 end_macro_name = get_name();
2628 void blank_line_macro()
2630 blank_line_macro_name = get_name();
2634 void leading_spaces_macro()
2636 leading_spaces_macro_name = get_name();
2640 static void trapping_blank_line()
2642 if (!blank_line_macro_name.is_null())
2643 spring_trap(blank_line_macro_name);
2650 int old_compatible_flag = compatible_flag;
2651 compatible_flag = 0;
2652 symbol nm = get_name();
2656 interpolate_macro(nm, 1);
2657 compatible_flag = old_compatible_flag;
2658 request_or_macro *p = lookup_request(nm);
2659 macro *m = p->to_macro();
2664 inline int possibly_handle_first_page_transition()
2666 if (topdiv->before_first_page && curdiv == topdiv && !curenv->is_dummy()) {
2667 handle_first_page_transition();
2674 static int transparent_translate(int cc)
2676 if (!invalid_input_char(cc)) {
2677 charinfo *ci = charset_table[cc];
2678 switch (ci->get_special_translation(1)) {
2679 case charinfo::TRANSLATE_SPACE:
2681 case charinfo::TRANSLATE_STRETCHABLE_SPACE:
2682 return ESCAPE_TILDE;
2683 case charinfo::TRANSLATE_DUMMY:
2684 return ESCAPE_AMPERSAND;
2685 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
2686 return ESCAPE_PERCENT;
2688 // This is really ugly.
2689 ci = ci->get_translation(1);
2691 int c = ci->get_ascii_code();
2694 error("can't translate %1 to special character '%2'"
2695 " in transparent throughput",
2696 input_char_description(cc),
2704 struct int_stack_element {
2706 int_stack_element *next;
2716 int_stack::int_stack()
2721 int_stack::~int_stack()
2724 int_stack_element *temp = top;
2730 int int_stack::is_empty()
2735 void int_stack::push(int n)
2737 int_stack_element *p = new int_stack_element;
2743 int int_stack::pop()
2746 int_stack_element *p = top;
2753 int node::reread(int *)
2758 int global_diverted_space = 0;
2760 int diverted_space_node::reread(int *bolp)
2762 global_diverted_space = 1;
2763 if (curenv->get_fill())
2764 trapping_blank_line();
2767 global_diverted_space = 0;
2772 int diverted_copy_file_node::reread(int *bolp)
2774 curdiv->copy_file(filename.contents());
2779 int word_space_node::reread(int *)
2782 for (width_list *w = orig_width; w; w = w->next)
2783 curenv->space(w->width, w->sentence_width);
2790 int unbreakable_space_node::reread(int *)
2795 int hmotion_node::reread(int *)
2797 if (unformat && was_tab) {
2798 curenv->handle_tab(0);
2805 static int leading_spaces_number = 0;
2806 static int leading_spaces_space = 0;
2808 void process_input_stack()
2810 int_stack trap_bol_stack;
2813 int suppress_next = 0;
2815 case token::TOKEN_CHAR:
2817 unsigned char ch = tok.c;
2818 if (bol && !have_input
2819 && (ch == curenv->control_char
2820 || ch == curenv->no_break_control_char)) {
2821 break_flag = ch == curenv->control_char;
2822 // skip tabs as well as spaces here
2825 } while (tok.white_space());
2826 symbol nm = get_name();
2827 #if defined(DEBUGGING)
2829 if (! nm.is_null()) {
2830 if (strcmp(nm.contents(), "test") == 0) {
2831 fprintf(stderr, "found it!\n");
2834 fprintf(stderr, "interpreting [%s]", nm.contents());
2835 if (strcmp(nm.contents(), "di") == 0 && topdiv != curdiv)
2836 fprintf(stderr, " currently in diversion: %s",
2837 curdiv->get_diversion_name());
2838 fprintf(stderr, "\n");
2846 interpolate_macro(nm);
2847 #if defined(DEBUGGING)
2849 fprintf(stderr, "finished interpreting [%s] and environment state is\n", nm.contents());
2850 curenv->dump_troff_state();
2857 if (possibly_handle_first_page_transition())
2861 #if defined(DEBUGGING)
2863 fprintf(stderr, "found [%c]\n", ch); fflush(stderr);
2866 curenv->add_char(charset_table[ch]);
2868 if (tok.type != token::TOKEN_CHAR)
2878 case token::TOKEN_TRANSPARENT:
2881 if (possibly_handle_first_page_transition())
2890 curdiv->transparent_output(transparent_translate(cc));
2892 curdiv->transparent_output(n);
2894 } while (cc != '\n' && cc != EOF);
2896 curdiv->transparent_output('\n');
2901 case token::TOKEN_NEWLINE:
2903 if (bol && !old_have_input
2904 && !curenv->get_prev_line_interrupted())
2905 trapping_blank_line();
2912 case token::TOKEN_REQUEST:
2914 int request_code = tok.c;
2916 switch (request_code) {
2920 case COPY_FILE_REQUEST:
2923 case TRANSPARENT_FILE_REQUEST:
2927 case VJUSTIFY_REQUEST:
2938 case token::TOKEN_SPACE:
2940 if (possibly_handle_first_page_transition())
2942 else if (bol && !curenv->get_prev_line_interrupted()) {
2944 // save space_width now so that it isn't changed by \f or \s
2945 // which we wouldn't notice here
2946 hunits space_width = curenv->get_space_width();
2948 nspaces += tok.nspaces();
2950 } while (tok.space());
2952 trapping_blank_line();
2955 leading_spaces_number = nspaces;
2956 leading_spaces_space = space_width.to_units() * nspaces;
2957 if (!leading_spaces_macro_name.is_null())
2958 spring_trap(leading_spaces_macro_name);
2961 curenv->add_node(new hmotion_node(space_width * nspaces,
2962 curenv->get_fill_color()));
2973 case token::TOKEN_EOF:
2975 case token::TOKEN_NODE:
2976 case token::TOKEN_HORIZONTAL_SPACE:
2978 if (possibly_handle_first_page_transition())
2980 else if (tok.nd->reread(&bol)) {
2985 curenv->add_node(tok.nd);
2988 curenv->possibly_break_line(1);
2992 case token::TOKEN_PAGE_EJECTOR:
2994 continue_page_eject();
2995 // I think we just want to preserve bol.
2999 case token::TOKEN_BEGIN_TRAP:
3001 trap_bol_stack.push(bol);
3006 case token::TOKEN_END_TRAP:
3008 if (trap_bol_stack.is_empty())
3009 error("spurious end trap token detected!");
3011 bol = trap_bol_stack.pop();
3014 /* I'm not totally happy about this. But I can't think of any other
3015 way to do it. Doing an output_pending_lines() whenever a
3016 TOKEN_END_TRAP is detected doesn't work: for example,
3029 a\%very\%very\%long\%word
3031 will print all but the first lines from the word immediately
3032 after the footer, rather than on the next page. */
3034 if (trap_bol_stack.is_empty())
3035 curenv->output_pending_lines();
3047 trap_sprung_flag = 0;
3051 #ifdef WIDOW_CONTROL
3053 void flush_pending_lines()
3055 while (!tok.newline() && !tok.eof())
3057 curenv->output_pending_lines();
3061 #endif /* WIDOW_CONTROL */
3063 request_or_macro::request_or_macro()
3067 macro *request_or_macro::to_macro()
3072 request::request(REQUEST_FUNCP pp) : p(pp)
3076 void request::invoke(symbol, int)
3082 enum { SIZE = 128 };
3083 unsigned char s[SIZE];
3088 char_block::char_block()
3097 void append(unsigned char);
3098 void set(unsigned char, int);
3099 unsigned char get(int);
3106 friend class macro_header;
3107 friend class string_iterator;
3110 char_list::char_list()
3111 : ptr(0), len(0), head(0), tail(0)
3115 char_list::~char_list()
3118 char_block *tem = head;
3124 int char_list::length()
3129 void char_list::append(unsigned char c)
3132 head = tail = new char_block;
3136 if (ptr >= tail->s + char_block::SIZE) {
3137 tail->next = new char_block;
3146 void char_list::set(unsigned char c, int offset)
3148 assert(len > offset);
3149 // optimization for access at the end
3150 int boundary = len - len % char_block::SIZE;
3151 if (offset >= boundary) {
3152 *(tail->s + offset - boundary) = c;
3155 char_block *tem = head;
3158 l += char_block::SIZE;
3160 *(tem->s + offset % char_block::SIZE) = c;
3167 unsigned char char_list::get(int offset)
3169 assert(len > offset);
3170 // optimization for access at the end
3171 int boundary = len - len % char_block::SIZE;
3172 if (offset >= boundary)
3173 return *(tail->s + offset - boundary);
3174 char_block *tem = head;
3177 l += char_block::SIZE;
3179 return *(tem->s + offset % char_block::SIZE);
3190 void append(node *);
3194 friend class macro_header;
3195 friend class string_iterator;
3198 void node_list::append(node *n)
3206 tail = tail->next = n;
3210 int node_list::length()
3213 for (node *n = head; n != 0; n = n->next)
3218 node_list::node_list()
3223 node *node_list::extract()
3230 node_list::~node_list()
3232 delete_node_list(head);
3235 class macro_header {
3240 macro_header() { count = 1; }
3241 macro_header *copy(int);
3246 if (p != 0 && --(p->count) <= 0)
3251 : is_a_diversion(0), is_a_string(1)
3253 if (!input_stack::get_location(1, &filename, &lineno)) {
3262 macro::macro(const macro &m)
3263 : filename(m.filename), lineno(m.lineno), len(m.len),
3264 empty_macro(m.empty_macro), is_a_diversion(m.is_a_diversion),
3265 is_a_string(m.is_a_string), p(m.p)
3271 macro::macro(int is_div)
3272 : is_a_diversion(is_div)
3274 if (!input_stack::get_location(1, &filename, &lineno)) {
3284 int macro::is_diversion()
3286 return is_a_diversion;
3289 int macro::is_string()
3294 void macro::clear_string_flag()
3299 macro ¯o::operator=(const macro &m)
3301 // don't assign object
3304 if (p != 0 && --(p->count) <= 0)
3307 filename = m.filename;
3310 empty_macro = m.empty_macro;
3311 is_a_diversion = m.is_a_diversion;
3312 is_a_string = m.is_a_string;
3316 void macro::append(unsigned char c)
3320 p = new macro_header;
3321 if (p->cl.length() != len) {
3322 macro_header *tem = p->copy(len);
3323 if (--(p->count) <= 0)
3329 if (c != PUSH_GROFF_MODE && c != PUSH_COMP_MODE && c != POP_GROFFCOMP_MODE)
3333 void macro::set(unsigned char c, int offset)
3337 p->cl.set(c, offset);
3340 unsigned char macro::get(int offset)
3343 return p->cl.get(offset);
3351 void macro::append_str(const char *s)
3356 while (s[i] != (char)0) {
3363 void macro::append(node *n)
3367 p = new macro_header;
3368 if (p->cl.length() != len) {
3369 macro_header *tem = p->copy(len);
3370 if (--(p->count) <= 0)
3380 void macro::append_unsigned(unsigned int i)
3382 unsigned int j = i / 10;
3385 append(((unsigned char)(((int)'0') + i % 10)));
3388 void macro::append_int(int i)
3394 append_unsigned((unsigned int)i);
3397 void macro::print_size()
3399 errprint("%1", len);
3402 // make a copy of the first n bytes
3404 macro_header *macro_header::copy(int n)
3406 macro_header *p = new macro_header;
3407 char_block *bp = cl.head;
3408 unsigned char *ptr = bp->s;
3411 if (ptr >= bp->s + char_block::SIZE) {
3415 unsigned char c = *ptr++;
3418 p->nl.append(nd->copy());
3427 object_dictionary_iterator iter(request_dictionary);
3428 request_or_macro *rm;
3430 while (iter.get(&s, (object **)&rm)) {
3431 assert(!s.is_null());
3432 macro *m = rm->to_macro();
3434 errprint("%1\t", s.contents());
3443 class string_iterator : public input_iterator {
3445 const char *how_invoked;
3449 int count; // of characters remaining
3451 int saved_compatible_flag;
3452 int with_break; // inherited from the caller
3457 string_iterator(const macro &, const char * = 0, symbol = NULL_SYMBOL);
3460 int get_location(int, const char **, int *);
3462 int get_break_flag() { return with_break; }
3463 void save_compatible_flag(int f) { saved_compatible_flag = f; }
3464 int get_compatible_flag() { return saved_compatible_flag; }
3468 string_iterator::string_iterator(const macro &m, const char *p, symbol s)
3469 : input_iterator(m.is_a_diversion), mac(m), how_invoked(p), newline_flag(0),
3474 bp = mac.p->cl.head;
3475 nd = mac.p->nl.head;
3483 with_break = input_stack::get_break_flag();
3486 string_iterator::string_iterator()
3495 with_break = input_stack::get_break_flag();
3498 int string_iterator::is_diversion()
3500 return mac.is_diversion();
3503 int string_iterator::fill(node **np)
3510 const unsigned char *p = eptr;
3511 if (p >= bp->s + char_block::SIZE) {
3519 (*np)->div_nest_level = input_stack::get_div_level();
3521 (*np)->div_nest_level = 0;
3528 const unsigned char *e = bp->s + char_block::SIZE;
3533 unsigned char c = *p;
3534 if (c == '\n' || c == ESCAPE_NEWLINE) {
3548 int string_iterator::peek()
3552 const unsigned char *p = eptr;
3553 if (p >= bp->s + char_block::SIZE) {
3559 int string_iterator::get_location(int allow_macro,
3560 const char **filep, int *linep)
3564 if (mac.filename == 0)
3566 *filep = mac.filename;
3567 *linep = mac.lineno + lineno - 1;
3571 void string_iterator::backtrace()
3574 errprint("%1:%2: backtrace", mac.filename, mac.lineno + lineno - 1);
3577 errprint(": %1 '%2'\n", how_invoked, nm.contents());
3579 errprint(": %1\n", how_invoked);
3586 class temp_iterator : public input_iterator {
3587 unsigned char *base;
3588 temp_iterator(const char *, int len);
3591 friend input_iterator *make_temp_iterator(const char *);
3597 temp_iterator::temp_iterator(const char *s, int len)
3599 base = new unsigned char[len];
3601 memcpy(base, s, len);
3606 temp_iterator::~temp_iterator()
3612 input_iterator *make_temp_iterator(const char *s)
3615 return new temp_iterator(s, 0);
3618 return new temp_iterator(s, n);
3622 // this is used when macros with arguments are interpolated
3628 arg_list(const macro &, int);
3629 arg_list(const arg_list *);
3633 arg_list::arg_list(const macro &m, int s) : mac(m), space_follows(s), next(0)
3637 arg_list::arg_list(const arg_list *al)
3641 space_follows = al->space_follows;
3642 arg_list **a = &next;
3643 arg_list *p = al->next;
3645 *a = new arg_list(p->mac, p->space_follows);
3651 arg_list::~arg_list()
3655 class macro_iterator : public string_iterator {
3658 int with_break; // whether called as .foo or 'foo
3660 macro_iterator(symbol, macro &, const char * = "macro", int = 0);
3663 int has_args() { return 1; }
3664 input_iterator *get_arg(int);
3665 arg_list *get_arg_list();
3666 symbol get_macro_name();
3667 int space_follows_arg(int);
3668 int get_break_flag() { return with_break; }
3669 int nargs() { return argc; }
3670 void add_arg(const macro &, int);
3672 int is_macro() { return 1; }
3676 input_iterator *macro_iterator::get_arg(int i)
3679 return make_temp_iterator(nm.contents());
3680 if (i > 0 && i <= argc) {
3682 for (int j = 1; j < i; j++) {
3686 return new string_iterator(p->mac);
3692 arg_list *macro_iterator::get_arg_list()
3697 symbol macro_iterator::get_macro_name()
3702 int macro_iterator::space_follows_arg(int i)
3704 if (i > 0 && i <= argc) {
3706 for (int j = 1; j < i; j++) {
3710 return p->space_follows;
3716 void macro_iterator::add_arg(const macro &m, int s)
3719 for (p = &args; *p; p = &((*p)->next))
3721 *p = new arg_list(m, s);
3725 void macro_iterator::shift(int n)
3727 while (n > 0 && argc > 0) {
3728 arg_list *tem = args;
3736 // This gets used by, e.g., .if '\?xxx\?''.
3738 int operator==(const macro &m1, const macro &m2)
3740 if (m1.len != m2.len)
3742 string_iterator iter1(m1);
3743 string_iterator iter2(m2);
3747 int c1 = iter1.get(&nd1);
3750 int c2 = iter2.get(&nd2);
3762 int are_same = nd1->type() == nd2->type() && nd1->same(nd2);
3772 static void interpolate_macro(symbol nm, int no_next)
3774 request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm);
3777 const char *s = nm.contents();
3778 if (strlen(s) > 2) {
3779 request_or_macro *r;
3784 r = (request_or_macro *)request_dictionary.lookup(symbol(buf));
3786 macro *m = r->to_macro();
3787 if (!m || !m->empty())
3788 warned = warning(WARN_SPACE,
3789 "macro '%1' not defined "
3790 "(possibly missing space after '%2')",
3791 nm.contents(), buf);
3795 warning(WARN_MAC, "macro '%1' not defined", nm.contents());
3797 request_dictionary.define(nm, p);
3801 p->invoke(nm, no_next);
3808 static void decode_args(macro_iterator *mi)
3810 if (!tok.newline() && !tok.eof()) {
3812 int c = get_copy(&n);
3816 if (c == '\n' || c == EOF)
3819 int quote_input_level = 0;
3820 int done_tab_warning = 0;
3821 arg.append(compatible_flag ? PUSH_COMP_MODE : PUSH_GROFF_MODE);
3822 // we store discarded double quotes for \$^
3824 arg.append(DOUBLE_QUOTE);
3825 quote_input_level = input_stack::get_level();
3828 while (c != EOF && c != '\n' && !(c == ' ' && quote_input_level == 0)) {
3829 if (quote_input_level > 0 && c == '"'
3831 || input_stack::get_level() == quote_input_level)) {
3832 arg.append(DOUBLE_QUOTE);
3845 if (c == '\t' && quote_input_level == 0 && !done_tab_warning) {
3846 warning(WARN_TAB, "tab character in unquoted macro argument");
3847 done_tab_warning = 1;
3854 arg.append(POP_GROFFCOMP_MODE);
3855 mi->add_arg(arg, (c == ' '));
3860 static void decode_string_args(macro_iterator *mi)
3863 int c = get_copy(&n);
3867 if (c == '\n' || c == EOF) {
3868 error("missing ']'");
3874 int quote_input_level = 0;
3875 int done_tab_warning = 0;
3877 quote_input_level = input_stack::get_level();
3880 while (c != EOF && c != '\n'
3881 && !(c == ']' && quote_input_level == 0)
3882 && !(c == ' ' && quote_input_level == 0)) {
3883 if (quote_input_level > 0 && c == '"'
3884 && input_stack::get_level() == quote_input_level) {
3897 if (c == '\t' && quote_input_level == 0 && !done_tab_warning) {
3898 warning(WARN_TAB, "tab character in unquoted string argument");
3899 done_tab_warning = 1;
3906 mi->add_arg(arg, (c == ' '));
3910 void macro::invoke(symbol nm, int no_next)
3912 macro_iterator *mi = new macro_iterator(nm, *this);
3914 input_stack::push(mi);
3915 // we must delay tok.next() in case the function has been called by
3916 // do_request to assure proper handling of compatible_flag
3921 macro *macro::to_macro()
3928 return empty_macro == 1;
3931 macro_iterator::macro_iterator(symbol s, macro &m, const char *how_called,
3933 : string_iterator(m, how_called, s), args(0), argc(0), with_break(break_flag)
3936 arg_list *al = input_stack::get_arg_list();
3938 args = new arg_list(al);
3939 argc = input_stack::nargs();
3944 macro_iterator::macro_iterator() : args(0), argc(0), with_break(break_flag)
3948 macro_iterator::~macro_iterator()
3951 arg_list *tem = args;
3957 dictionary composite_dictionary(17);
3959 void composite_request()
3961 symbol from = get_name(1);
3962 if (!from.is_null()) {
3963 const char *from_gn = glyph_name_to_unicode(from.contents());
3965 from_gn = check_unicode_name(from.contents());
3967 error("invalid composite glyph name '%1'", from.contents());
3972 const char *from_decomposed = decompose_unicode(from_gn);
3973 if (from_decomposed)
3974 from_gn = &from_decomposed[1];
3975 symbol to = get_name(1);
3977 composite_dictionary.remove(symbol(from_gn));
3979 const char *to_gn = glyph_name_to_unicode(to.contents());
3981 to_gn = check_unicode_name(to.contents());
3983 error("invalid composite glyph name '%1'", to.contents());
3988 const char *to_decomposed = decompose_unicode(to_gn);
3990 to_gn = &to_decomposed[1];
3991 if (strcmp(from_gn, to_gn) == 0)
3992 composite_dictionary.remove(symbol(from_gn));
3994 (void)composite_dictionary.lookup(symbol(from_gn), (void *)to_gn);
4000 static symbol composite_glyph_name(symbol nm)
4002 macro_iterator *mi = new macro_iterator();
4003 decode_string_args(mi);
4004 input_stack::push(mi);
4005 const char *gn = glyph_name_to_unicode(nm.contents());
4007 gn = check_unicode_name(nm.contents());
4009 error("invalid base glyph '%1' in composite glyph name", nm.contents());
4010 return EMPTY_SYMBOL;
4013 const char *gn_decomposed = decompose_unicode(gn);
4014 string glyph_name(gn_decomposed ? &gn_decomposed[1] : gn);
4016 int n = input_stack::nargs();
4017 for (int i = 1; i <= n; i++) {
4019 input_iterator *p = input_stack::get_arg(i);
4022 while ((c = p->get(0)) != EOF)
4023 if (c != DOUBLE_QUOTE)
4026 const char *u = glyph_name_to_unicode(gl.contents());
4028 u = check_unicode_name(gl.contents());
4030 error("invalid component '%1' in composite glyph name",
4032 return EMPTY_SYMBOL;
4035 const char *decomposed = decompose_unicode(u);
4038 void *mapped_composite = composite_dictionary.lookup(symbol(u));
4039 if (mapped_composite)
4040 u = (const char *)mapped_composite;
4044 const char *groff_gn = unicode_to_glyph_name(glyph_name.contents());
4046 return symbol(groff_gn);
4050 return symbol(gl.contents());
4053 int trap_sprung_flag = 0;
4054 int postpone_traps_flag = 0;
4055 symbol postponed_trap;
4057 void spring_trap(symbol nm)
4059 assert(!nm.is_null());
4060 trap_sprung_flag = 1;
4061 if (postpone_traps_flag) {
4062 postponed_trap = nm;
4065 static char buf[2] = { BEGIN_TRAP, '\0' };
4066 static char buf2[2] = { END_TRAP, '\0' };
4067 input_stack::push(make_temp_iterator(buf2));
4068 request_or_macro *p = lookup_request(nm);
4069 macro *m = p->to_macro();
4071 input_stack::push(new macro_iterator(nm, *m, "trap-invoked macro"));
4073 error("you can't invoke a request with a trap");
4074 input_stack::push(make_temp_iterator(buf));
4077 void postpone_traps()
4079 postpone_traps_flag = 1;
4082 int unpostpone_traps()
4084 postpone_traps_flag = 0;
4085 if (!postponed_trap.is_null()) {
4086 spring_trap(postponed_trap);
4087 postponed_trap = NULL_SYMBOL;
4096 macro_iterator *mi = new macro_iterator;
4097 int reading_from_terminal = isatty(fileno(stdin));
4099 if (!tok.newline() && !tok.eof()) {
4100 int c = get_copy(0);
4103 while (c != EOF && c != '\n' && c != ' ') {
4104 if (!invalid_input_char(c)) {
4105 if (reading_from_terminal)
4116 if (reading_from_terminal) {
4117 fputc(had_prompt ? ':' : '\a', stderr);
4120 input_stack::push(mi);
4124 while ((c = getchar()) != EOF) {
4125 if (invalid_input_char(c))
4126 warning(WARN_INPUT, "invalid input character code %1", int(c));
4139 if (reading_from_terminal)
4141 input_stack::push(new string_iterator(mac));
4145 enum define_mode { DEFINE_NORMAL, DEFINE_APPEND, DEFINE_IGNORE };
4146 enum calling_mode { CALLING_NORMAL, CALLING_INDIRECT };
4147 enum comp_mode { COMP_IGNORE, COMP_DISABLE, COMP_ENABLE };
4149 void do_define_string(define_mode mode, comp_mode comp)
4152 node *n = 0; // pacify compiler
4163 else if (!tok.space()) {
4164 error("bad string definition");
4175 request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm);
4176 macro *mm = rm ? rm->to_macro() : 0;
4177 if (mode == DEFINE_APPEND && mm)
4179 if (comp == COMP_DISABLE)
4180 mac.append(PUSH_GROFF_MODE);
4181 else if (comp == COMP_ENABLE)
4182 mac.append(PUSH_COMP_MODE);
4183 while (c != '\n' && c != EOF) {
4187 mac.append((unsigned char)c);
4190 if (comp == COMP_DISABLE || comp == COMP_ENABLE)
4191 mac.append(POP_GROFFCOMP_MODE);
4194 request_dictionary.define(nm, mm);
4200 void define_string()
4202 do_define_string(DEFINE_NORMAL,
4203 compatible_flag ? COMP_ENABLE: COMP_IGNORE);
4206 void define_nocomp_string()
4208 do_define_string(DEFINE_NORMAL, COMP_DISABLE);
4211 void append_string()
4213 do_define_string(DEFINE_APPEND,
4214 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4217 void append_nocomp_string()
4219 do_define_string(DEFINE_APPEND, COMP_DISABLE);
4222 void do_define_character(char_mode mode, const char *font_name)
4224 node *n = 0; // pacify compiler
4227 charinfo *ci = tok.get_char(1);
4233 string s(font_name);
4235 s += ci->nm.contents();
4237 ci = get_charinfo(symbol(s.contents()));
4244 else if (!tok.space()) {
4245 error("bad character definition");
4251 while (c == ' ' || c == '\t')
4255 macro *m = new macro;
4256 while (c != '\n' && c != EOF) {
4260 m->append((unsigned char)c);
4263 m = ci->setx_macro(m, mode);
4269 void define_character()
4271 do_define_character(CHAR_NORMAL);
4274 void define_fallback_character()
4276 do_define_character(CHAR_FALLBACK);
4279 void define_special_character()
4281 do_define_character(CHAR_SPECIAL);
4284 static void remove_character()
4287 while (!tok.newline() && !tok.eof()) {
4288 if (!tok.space() && !tok.tab()) {
4289 charinfo *ci = tok.get_char(1);
4292 macro *m = ci->set_macro(0);
4301 static void interpolate_string(symbol nm)
4303 request_or_macro *p = lookup_request(nm);
4304 macro *m = p->to_macro();
4306 error("you can only invoke a string or macro using \\*");
4308 if (m->is_string()) {
4309 string_iterator *si = new string_iterator(*m, "string", nm);
4310 input_stack::push(si);
4313 // if a macro is called as a string, \$0 doesn't get changed
4314 macro_iterator *mi = new macro_iterator(input_stack::get_macro_name(),
4316 input_stack::push(mi);
4321 static void interpolate_string_with_args(symbol s)
4323 request_or_macro *p = lookup_request(s);
4324 macro *m = p->to_macro();
4326 error("you can only invoke a string or macro using \\*");
4328 macro_iterator *mi = new macro_iterator(s, *m);
4329 decode_string_args(mi);
4330 input_stack::push(mi);
4334 static void interpolate_arg(symbol nm)
4336 const char *s = nm.contents();
4337 if (!s || *s == '\0')
4338 copy_mode_error("missing argument name");
4339 else if (s[1] == 0 && csdigit(s[0]))
4340 input_stack::push(input_stack::get_arg(s[0] - '0'));
4341 else if (s[0] == '*' && s[1] == '\0') {
4342 int limit = input_stack::nargs();
4344 for (int i = 1; i <= limit; i++) {
4345 input_iterator *p = input_stack::get_arg(i);
4347 while ((c = p->get(0)) != EOF)
4348 if (c != DOUBLE_QUOTE)
4356 input_stack::push(make_temp_iterator(args.contents()));
4359 else if (s[0] == '@' && s[1] == '\0') {
4360 int limit = input_stack::nargs();
4362 for (int i = 1; i <= limit; i++) {
4364 args += char(BEGIN_QUOTE);
4365 input_iterator *p = input_stack::get_arg(i);
4367 while ((c = p->get(0)) != EOF)
4368 if (c != DOUBLE_QUOTE)
4370 args += char(END_QUOTE);
4378 input_stack::push(make_temp_iterator(args.contents()));
4381 else if (s[0] == '^' && s[1] == '\0') {
4382 int limit = input_stack::nargs();
4384 int c = input_stack::peek();
4385 for (int i = 1; i <= limit; i++) {
4386 input_iterator *p = input_stack::get_arg(i);
4387 while ((c = p->get(0)) != EOF) {
4388 if (c == DOUBLE_QUOTE)
4392 if (input_stack::space_follows_arg(i))
4398 input_stack::push(make_temp_iterator(args.contents()));
4403 for (p = s; *p && csdigit(*p); p++)
4406 copy_mode_error("bad argument name '%1'", s);
4408 input_stack::push(input_stack::get_arg(atoi(s)));
4412 void handle_first_page_transition()
4415 topdiv->begin_page();
4418 // We push back a token by wrapping it up in a token_node, and
4419 // wrapping that up in a string_iterator.
4421 static void push_token(const token &t)
4424 m.append(new token_node(t));
4425 input_stack::push(new string_iterator(m));
4428 void push_page_ejector()
4430 static char buf[2] = { PAGE_EJECTOR, '\0' };
4431 input_stack::push(make_temp_iterator(buf));
4434 void handle_initial_request(unsigned char code)
4440 mac.append(new token_node(tok));
4441 input_stack::push(new string_iterator(mac));
4442 input_stack::push(make_temp_iterator(buf));
4443 topdiv->begin_page();
4447 void handle_initial_title()
4449 handle_initial_request(TITLE_REQUEST);
4452 // this should be local to define_macro, but cfront 1.2 doesn't support that
4453 static symbol dot_symbol(".");
4455 void do_define_macro(define_mode mode, calling_mode calling, comp_mode comp)
4458 if (calling == CALLING_INDIRECT) {
4459 symbol temp1 = get_name(1);
4460 if (temp1.is_null()) {
4464 symbol temp2 = get_name();
4465 input_stack::push(make_temp_iterator("\n"));
4466 if (!temp2.is_null()) {
4467 interpolate_string(temp2);
4468 input_stack::push(make_temp_iterator(" "));
4470 interpolate_string(temp1);
4471 input_stack::push(make_temp_iterator(" "));
4474 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4481 term = get_name(); // the request that terminates the definition
4484 while (!tok.newline() && !tok.eof())
4486 const char *start_filename;
4488 int have_start_location = input_stack::get_location(0, &start_filename,
4491 // doing this here makes the line numbers come out right
4492 int c = get_copy(&n, 1);
4495 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4496 request_or_macro *rm =
4497 (request_or_macro *)request_dictionary.lookup(nm);
4499 mm = rm->to_macro();
4500 if (mm && mode == DEFINE_APPEND)
4504 if (comp == COMP_DISABLE)
4505 mac.append(PUSH_GROFF_MODE);
4506 else if (comp == COMP_ENABLE)
4507 mac.append(PUSH_COMP_MODE);
4510 mac.clear_string_flag();
4511 while (c == ESCAPE_NEWLINE) {
4512 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND)
4514 c = get_copy(&n, 1);
4516 if (bol && c == '.') {
4517 const char *s = term.contents();
4519 // see if it matches term
4522 while ((d = get_copy(&n)) == ' ' || d == '\t')
4524 if ((unsigned char)s[0] == d) {
4525 for (i = 1; s[i] != 0; i++) {
4527 if ((unsigned char)s[i] != d)
4533 && ((i == 2 && compatible_flag)
4534 || (d = get_copy(&n)) == ' '
4535 || d == '\n')) { // we found it
4540 if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) {
4543 request_dictionary.define(nm, mm);
4545 if (comp == COMP_DISABLE || comp == COMP_ENABLE)
4546 mac.append(POP_GROFFCOMP_MODE);
4549 if (term != dot_symbol) {
4551 interpolate_macro(term);
4557 if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) {
4559 for (int j = 0; j < i; j++)
4565 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4566 if (have_start_location)
4567 error_with_file_and_line(start_filename, start_lineno,
4568 "end of file while defining macro '%1'",
4571 error("end of file while defining macro '%1'", nm.contents());
4574 if (have_start_location)
4575 error_with_file_and_line(start_filename, start_lineno,
4576 "end of file while ignoring input lines");
4578 error("end of file while ignoring input lines");
4583 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4590 c = get_copy(&n, 1);
4596 do_define_macro(DEFINE_NORMAL, CALLING_NORMAL,
4597 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4600 void define_nocomp_macro()
4602 do_define_macro(DEFINE_NORMAL, CALLING_NORMAL, COMP_DISABLE);
4605 void define_indirect_macro()
4607 do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT,
4608 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4611 void define_indirect_nocomp_macro()
4613 do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT, COMP_DISABLE);
4618 do_define_macro(DEFINE_APPEND, CALLING_NORMAL,
4619 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4622 void append_nocomp_macro()
4624 do_define_macro(DEFINE_APPEND, CALLING_NORMAL, COMP_DISABLE);
4627 void append_indirect_macro()
4629 do_define_macro(DEFINE_APPEND, CALLING_INDIRECT,
4630 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4633 void append_indirect_nocomp_macro()
4635 do_define_macro(DEFINE_APPEND, CALLING_INDIRECT, COMP_DISABLE);
4641 do_define_macro(DEFINE_IGNORE, CALLING_NORMAL, COMP_IGNORE);
4648 symbol s = get_name();
4651 request_dictionary.remove(s);
4658 symbol s1 = get_name(1);
4659 if (!s1.is_null()) {
4660 symbol s2 = get_name(1);
4662 request_dictionary.rename(s1, s2);
4669 symbol s1 = get_name(1);
4670 if (!s1.is_null()) {
4671 symbol s2 = get_name(1);
4672 if (!s2.is_null()) {
4673 if (!request_dictionary.alias(s1, s2))
4674 warning(WARN_MAC, "macro '%1' not defined", s2.contents());
4682 symbol s = get_name(1);
4684 request_or_macro *p = lookup_request(s);
4685 macro *m = p->to_macro();
4687 error("cannot chop request");
4688 else if (m->empty())
4689 error("cannot chop empty macro");
4691 int have_restore = 0;
4692 // we have to check for additional save/restore pairs which could be
4693 // there due to empty am1 requests.
4695 if (m->get(m->len - 1) != POP_GROFFCOMP_MODE)
4699 if (m->get(m->len - 1) != PUSH_GROFF_MODE
4700 && m->get(m->len - 1) != PUSH_COMP_MODE)
4708 error("cannot chop empty macro");
4711 m->set(POP_GROFFCOMP_MODE, m->len - 1);
4720 void substring_request()
4722 int start; // 0, 1, ..., n-1 or -1, -2, ...
4723 symbol s = get_name(1);
4724 if (!s.is_null() && get_integer(&start)) {
4725 request_or_macro *p = lookup_request(s);
4726 macro *m = p->to_macro();
4728 error("cannot apply 'substring' on a request");
4731 if (!has_arg() || get_integer(&end)) {
4732 int real_length = 0; // 1, 2, ..., n
4733 string_iterator iter1(*m);
4734 for (int l = 0; l < m->len; l++) {
4735 int c = iter1.get(0);
4736 if (c == PUSH_GROFF_MODE
4737 || c == PUSH_COMP_MODE
4738 || c == POP_GROFFCOMP_MODE)
4745 start += real_length;
4753 if (start >= real_length || end < 0) {
4755 "start and end index of substring out of range");
4758 if (--(m->p->count) <= 0)
4767 "start index of substring out of range, set to 0");
4770 if (end >= real_length) {
4772 "end index of substring out of range, set to string length");
4773 end = real_length - 1;
4775 // now extract the substring
4776 string_iterator iter(*m);
4778 for (i = 0; i < start; i++) {
4779 int c = iter.get(0);
4780 while (c == PUSH_GROFF_MODE
4781 || c == PUSH_COMP_MODE
4782 || c == POP_GROFFCOMP_MODE)
4788 for (; i <= end; i++) {
4789 node *nd = 0; // pacify compiler
4790 int c = iter.get(&nd);
4791 while (c == PUSH_GROFF_MODE
4792 || c == PUSH_COMP_MODE
4793 || c == POP_GROFFCOMP_MODE)
4800 mac.append((unsigned char)c);
4809 void length_request()
4813 if (ret.is_null()) {
4823 else if (!tok.space()) {
4824 error("bad string definition");
4835 while (c != '\n' && c != EOF) {
4839 reg *r = (reg*)number_reg_dictionary.lookup(ret);
4843 set_number_reg(ret, len);
4847 void asciify_macro()
4849 symbol s = get_name(1);
4851 request_or_macro *p = lookup_request(s);
4852 macro *m = p->to_macro();
4854 error("cannot asciify request");
4857 string_iterator iter(*m);
4859 node *nd = 0; // pacify compiler
4860 int c = iter.get(&nd);
4874 void unformat_macro()
4876 symbol s = get_name(1);
4878 request_or_macro *p = lookup_request(s);
4879 macro *m = p->to_macro();
4881 error("cannot unformat request");
4884 string_iterator iter(*m);
4886 node *nd = 0; // pacify compiler
4887 int c = iter.get(&nd);
4893 if (nd->set_unformat_flag())
4903 static void interpolate_environment_variable(symbol nm)
4905 const char *s = getenv(nm.contents());
4907 input_stack::push(make_temp_iterator(s));
4910 void interpolate_number_reg(symbol nm, int inc)
4912 reg *r = lookup_number_reg(nm);
4917 input_stack::push(make_temp_iterator(r->get_string()));
4920 static void interpolate_number_format(symbol nm)
4922 reg *r = (reg *)number_reg_dictionary.lookup(nm);
4924 input_stack::push(make_temp_iterator(r->get_format()));
4927 static int get_delim_number(units *n, unsigned char si, int prev_value)
4931 if (start.delimiter(1)) {
4933 if (get_number(n, si, prev_value)) {
4935 warning(WARN_DELIM, "closing delimiter does not match");
4942 static int get_delim_number(units *n, unsigned char si)
4946 if (start.delimiter(1)) {
4948 if (get_number(n, si)) {
4950 warning(WARN_DELIM, "closing delimiter does not match");
4957 static int get_line_arg(units *n, unsigned char si, charinfo **cp)
4961 int start_level = input_stack::get_level();
4962 if (!start.delimiter(1))
4965 if (get_number(n, si)) {
4966 if (tok.dummy() || tok.transparent_dummy())
4968 if (!(start == tok && input_stack::get_level() == start_level)) {
4969 *cp = tok.get_char(1);
4972 if (!(start == tok && input_stack::get_level() == start_level))
4973 warning(WARN_DELIM, "closing delimiter does not match");
4979 static int read_size(int *x)
4989 else if (c == '+') {
4994 int val = 0; // pacify compiler
5000 // allow an increment either before or after the left parenthesis
5006 else if (c == '+') {
5021 val = val*10 + (c - '0');
5026 else if (csdigit(c)) {
5028 if (!inc && c != '0' && c < '4') {
5034 val = val*10 + (c - '0');
5038 else if (!tok.delimiter(1))
5044 if (!inc && (c == '-' || c == '+')) {
5045 inc = c == '+' ? 1 : -1;
5048 if (!get_number(&val, 'z'))
5050 if (!(start.ch() == '[' && tok.ch() == ']') && start != tok) {
5051 if (start.ch() == '[')
5052 error("missing ']'");
5054 error("missing closing delimiter");
5062 // special case -- \s[0] and \s0 means to revert to previous size
5069 *x = curenv->get_requested_point_size() + val;
5072 *x = curenv->get_requested_point_size() - val;
5079 "\\s escape results in non-positive point size; set to 1");
5085 error("bad digit in point size");
5090 static symbol get_delim_name()
5095 error("end of input at start of delimited name");
5098 if (start.newline()) {
5099 error("can't delimit name with a newline");
5102 int start_level = input_stack::get_level();
5103 char abuf[ABUF_SIZE];
5105 int buf_size = ABUF_SIZE;
5108 if (i + 1 > buf_size) {
5110 buf = new char[ABUF_SIZE*2];
5111 memcpy(buf, abuf, buf_size);
5112 buf_size = ABUF_SIZE*2;
5115 char *old_buf = buf;
5116 buf = new char[buf_size*2];
5117 memcpy(buf, old_buf, buf_size);
5124 && (compatible_flag || input_stack::get_level() == start_level))
5126 if ((buf[i] = tok.ch()) == 0) {
5127 error("missing delimiter (got %1)", tok.description());
5137 error("empty delimited name");
5152 static void do_register()
5156 if (!start.delimiter(1))
5159 symbol nm = get_long_name(1);
5164 reg *r = (reg *)number_reg_dictionary.lookup(nm);
5166 if (!r || !r->get_value(&prev_value))
5169 if (!get_number(&val, 'u', prev_value))
5172 warning(WARN_DELIM, "closing delimiter does not match");
5176 set_number_reg(nm, val);
5179 // this implements the \w escape sequence
5181 static void do_width()
5185 int start_level = input_stack::get_level();
5186 environment env(curenv);
5187 environment *oldenv = curenv;
5192 warning(WARN_DELIM, "missing closing delimiter");
5195 if (tok.newline()) {
5196 warning(WARN_DELIM, "missing closing delimiter");
5197 input_stack::push(make_temp_iterator("\n"));
5201 && (compatible_flag || input_stack::get_level() == start_level))
5206 units x = env.get_input_line_position().to_units();
5207 input_stack::push(make_temp_iterator(i_to_a(x)));
5208 env.width_registers();
5213 charinfo *page_character;
5215 void set_page_character()
5217 page_character = get_optional_char();
5221 static const symbol percent_symbol("%");
5223 void read_title_parts(node **part, hunits *part_width)
5226 if (tok.newline() || tok.eof())
5229 int start_level = input_stack::get_level();
5231 for (int i = 0; i < 3; i++) {
5232 while (!tok.newline() && !tok.eof()) {
5234 && (compatible_flag || input_stack::get_level() == start_level)) {
5238 if (page_character != 0 && tok.get_char() == page_character)
5239 interpolate_number_reg(percent_symbol, 0);
5244 curenv->wrap_up_tab();
5245 part_width[i] = curenv->get_input_line_position();
5246 part[i] = curenv->extract_output_line();
5248 while (!tok.newline() && !tok.eof())
5252 class non_interpreted_node : public node {
5255 non_interpreted_node(const macro &);
5256 int interpret(macro *);
5258 int ends_sentence();
5265 non_interpreted_node::non_interpreted_node(const macro &m) : mac(m)
5269 int non_interpreted_node::ends_sentence()
5274 int non_interpreted_node::same(node *nd)
5276 return mac == ((non_interpreted_node *)nd)->mac;
5279 const char *non_interpreted_node::type()
5281 return "non_interpreted_node";
5284 int non_interpreted_node::force_tprint()
5289 int non_interpreted_node::is_tag()
5294 node *non_interpreted_node::copy()
5296 return new non_interpreted_node(mac);
5299 int non_interpreted_node::interpret(macro *m)
5301 string_iterator si(mac);
5302 node *n = 0; // pacify compiler
5315 static node *do_non_interpreted()
5320 while ((c = get_copy(&n)) != ESCAPE_QUESTION && c != EOF && c != '\n')
5325 if (c == EOF || c == '\n') {
5326 error("missing \\?");
5329 return new non_interpreted_node(mac);
5332 static void encode_char(macro *mac, char c)
5335 if ((font::use_charnames_in_special) && tok.special()) {
5336 charinfo *ci = tok.get_char(1);
5337 const char *s = ci->get_symbol()->contents();
5338 if (s[0] != (char)0) {
5342 while (s[i] != (char)0) {
5349 else if (tok.stretchable_space()
5350 || tok.unstretchable_space())
5352 else if (!(tok.hyphen_indicator()
5354 || tok.transparent_dummy()
5355 || tok.zero_width_break()))
5356 error("%1 is invalid within \\X", tok.description());
5359 if ((font::use_charnames_in_special) && (c == '\\')) {
5361 * add escape escape sequence
5373 int start_level = input_stack::get_level();
5376 tok != start || input_stack::get_level() != start_level;
5379 warning(WARN_DELIM, "missing closing delimiter");
5382 if (tok.newline()) {
5383 input_stack::push(make_temp_iterator("\n"));
5384 warning(WARN_DELIM, "missing closing delimiter");
5392 else if (tok.leader())
5394 else if (tok.backspace())
5398 encode_char(&mac, c);
5400 return new special_node(mac);
5403 void device_request()
5405 if (!tok.newline() && !tok.eof()) {
5414 if (c != ' ' && c != '\t')
5417 for (; c != '\n' && c != EOF; c = get_copy(0))
5419 curenv->add_node(new special_node(mac));
5424 void device_macro_request()
5426 symbol s = get_name(1);
5427 if (!(s.is_null() || s.is_empty())) {
5428 request_or_macro *p = lookup_request(s);
5429 macro *m = p->to_macro();
5431 curenv->add_node(new special_node(*m));
5433 error("can't transparently throughput a request");
5438 void output_request()
5440 if (!tok.newline() && !tok.eof()) {
5448 if (c != ' ' && c != '\t')
5451 for (; c != '\n' && c != EOF; c = get_copy(0))
5452 topdiv->transparent_output(c);
5453 topdiv->transparent_output('\n');
5458 extern int image_no; // from node.cpp
5460 static node *do_suppress(symbol nm)
5462 if (nm.is_null() || nm.is_empty()) {
5463 error("expecting an argument to escape \\O");
5466 const char *s = nm.contents();
5469 if (begin_level == 0)
5470 // suppress generation of glyphs
5471 return new suppress_node(0, 0);
5474 if (begin_level == 0)
5475 // enable generation of glyphs
5476 return new suppress_node(1, 0);
5479 if (begin_level == 0)
5480 return new suppress_node(1, 1);
5492 s++; // move over '5'
5494 if (*s == (char)0) {
5495 error("missing position and filename in \\O");
5498 if (!(position == 'l'
5501 || position == 'i')) {
5502 error("l, r, c, or i position expected (got %1 in \\O)", position);
5505 s++; // onto image name
5506 if (s == (char *)0) {
5507 error("missing image name for \\O");
5511 if (begin_level == 0)
5512 return new suppress_node(symbol(s), position, image_no);
5518 error("'%1' is an invalid argument to \\O", *s);
5523 void special_node::tprint(troff_output_file *out)
5526 string_iterator iter(mac);
5528 int c = iter.get(0);
5531 for (const char *s = ::asciify(c); *s; s++)
5532 tprint_char(out, *s);
5537 int get_file_line(const char **filename, int *lineno)
5539 return input_stack::get_location(0, filename, lineno);
5545 if (get_integer(&n)) {
5546 const char *filename = 0;
5548 symbol s = get_long_name();
5549 filename = s.contents();
5551 (void)input_stack::set_location(filename, n-1);
5556 static int nroff_mode = 0;
5558 static void nroff_request()
5564 static void troff_request()
5570 static void skip_alternative()
5573 // ensure that ".if 0\{" works as expected
5574 if (tok.left_brace())
5578 c = input_stack::get(0);
5581 if (c == ESCAPE_LEFT_BRACE)
5583 else if (c == ESCAPE_RIGHT_BRACE)
5585 else if (c == escape_char && escape_char > 0)
5586 switch(input_stack::get(0)) {
5594 while ((c = input_stack::get(0)) != '\n' && c != EOF)
5598 Note that the level can properly be < 0, e.g.
5604 So don't give an error message in this case.
5606 if (level <= 0 && c == '\n')
5612 static void begin_alternative()
5614 while (tok.space() || tok.left_brace())
5624 static int_stack if_else_stack;
5631 while (tok.ch() == '!') {
5636 unsigned char c = tok.ch();
5639 result = !nroff_mode;
5641 else if (c == 'n') {
5643 result = nroff_mode;
5645 else if (c == 'v') {
5649 else if (c == 'o') {
5650 result = (topdiv->get_page_number() & 1);
5653 else if (c == 'e') {
5654 result = !(topdiv->get_page_number() & 1);
5657 else if (c == 'd' || c == 'r') {
5659 symbol nm = get_name(1);
5665 ? request_dictionary.lookup(nm) != 0
5666 : number_reg_dictionary.lookup(nm) != 0);
5668 else if (c == 'm') {
5670 symbol nm = get_long_name(1);
5675 result = (nm == default_symbol
5676 || color_dictionary.lookup(nm) != 0);
5678 else if (c == 'c') {
5681 charinfo *ci = tok.get_char(1);
5686 result = character_exists(ci, curenv);
5689 else if (c == 'F') {
5691 symbol nm = get_long_name(1);
5696 result = check_font(curenv->get_family()->nm, nm);
5698 else if (c == 'S') {
5700 symbol nm = get_long_name(1);
5705 result = check_style(nm);
5707 else if (tok.space())
5709 else if (tok.delimiter()) {
5711 int delim_level = input_stack::get_level();
5712 environment env1(curenv);
5713 environment env2(curenv);
5714 environment *oldenv = curenv;
5717 for (int i = 0; i < 2; i++) {
5720 if (tok.newline() || tok.eof()) {
5721 warning(WARN_DELIM, "missing closing delimiter");
5727 && (compatible_flag || input_stack::get_level() == delim_level))
5733 node *n1 = env1.extract_output_line();
5734 node *n2 = env2.extract_output_line();
5735 result = same_node_list(n1, n2);
5736 delete_node_list(n1);
5737 delete_node_list(n2);
5745 if (!get_number(&n, 'u')) {
5755 begin_alternative();
5761 void if_else_request()
5763 if_else_stack.push(do_if_request());
5773 if (if_else_stack.is_empty()) {
5774 warning(WARN_EL, "unbalanced .el request");
5778 if (if_else_stack.pop())
5781 begin_alternative();
5785 static int while_depth = 0;
5786 static int while_break_flag = 0;
5788 void while_request()
5793 mac.append(new token_node(tok));
5795 node *n = 0; // pacify compiler
5796 int c = input_stack::get(&n);
5812 if (c == ESCAPE_LEFT_BRACE)
5814 else if (c == ESCAPE_RIGHT_BRACE)
5816 else if (c == escape_char)
5819 if (c == '\n' && level <= 0)
5824 error("unbalanced \\{ \\}");
5827 input_stack::add_boundary();
5829 input_stack::push(new string_iterator(mac, "while loop"));
5831 if (!do_if_request()) {
5832 while (input_stack::get(0) != EOF)
5836 process_input_stack();
5837 if (while_break_flag || input_stack::is_return_boundary()) {
5838 while_break_flag = 0;
5842 input_stack::remove_boundary();
5848 void while_break_request()
5851 error("no while loop");
5855 while_break_flag = 1;
5856 while (input_stack::get(0) != EOF)
5862 void while_continue_request()
5865 error("no while loop");
5869 while (input_stack::get(0) != EOF)
5879 symbol nm = get_long_name(1);
5883 while (!tok.newline() && !tok.eof())
5886 FILE *fp = include_search_path.open_file_cautious(nm.contents());
5888 input_stack::push(new file_iterator(fp, nm.contents()));
5890 error("can't open '%1': %2", nm.contents(), strerror(errno));
5895 // like .so but use popen()
5900 error(".pso request not allowed in safer mode");
5904 #ifdef POPEN_MISSING
5905 error("pipes not available on this system");
5907 #else /* not POPEN_MISSING */
5908 if (tok.newline() || tok.eof())
5909 error("missing command");
5912 while ((c = get_copy(0)) == ' ' || c == '\t')
5915 char *buf = new char[buf_size];
5917 for (; c != '\n' && c != EOF; c = get_copy(0)) {
5918 const char *s = asciify(c);
5919 int slen = strlen(s);
5920 if (buf_used + slen + 1> buf_size) {
5921 char *old_buf = buf;
5922 int old_buf_size = buf_size;
5924 buf = new char[buf_size];
5925 memcpy(buf, old_buf, old_buf_size);
5928 strcpy(buf + buf_used, s);
5931 buf[buf_used] = '\0';
5933 FILE *fp = popen(buf, POPEN_RT);
5935 input_stack::push(new file_iterator(fp, symbol(buf).contents(), 1));
5937 error("can't open pipe to process '%1': %2", buf, strerror(errno));
5941 #endif /* not POPEN_MISSING */
5947 // Extract bounding box limits from PostScript file, and assign
5948 // them to the following four gtroff registers:--
5950 static int llx_reg_contents = 0;
5951 static int lly_reg_contents = 0;
5952 static int urx_reg_contents = 0;
5953 static int ury_reg_contents = 0;
5955 // Manifest constants to specify the status of bounding box range
5956 // acquisition; (note that PSBB_RANGE_IS_BAD is also suitable for
5957 // assignment as a default ordinate property value).
5959 #define PSBB_RANGE_IS_BAD 0
5960 #define PSBB_RANGE_IS_SET 1
5961 #define PSBB_RANGE_AT_END 2
5963 // Maximum input line length, for DSC conformance, and options to
5964 // control how it will be enforced; caller should select either of
5965 // DSC_LINE_MAX_IGNORED, to allow partial line collection spread
5966 // across multiple calls, or DSC_LINE_MAX_ENFORCE, to truncate
5967 // excess length lines at the DSC limit.
5969 // Note that DSC_LINE_MAX_CHECKED is reserved for internal use by
5970 // ps_locator::get_line(), and should not be specified in any call;
5971 // also, handling of DSC_LINE_MAX_IGNORED, as a get_line() option,
5972 // is currently unimplemented.
5974 #define DSC_LINE_MAX 255
5975 #define DSC_LINE_MAX_IGNORED -1
5976 #define DSC_LINE_MAX_ENFORCE 0
5977 #define DSC_LINE_MAX_CHECKED 1
5979 // Input characters to be considered as white space, when reading
5980 // PostScript file comments.
5982 cset white_space("\n\r \t");
5984 // Class psbb_locator
5986 // This locally declared and implemented class provides the methods
5987 // to be used for retrieval of bounding box properties from a specified
5988 // PostScript or PDF file.
5993 // Only the class constructor is exposed publicly; instantiation of
5994 // a class object will retrieve the requisite bounding box properties
5995 // from the specified file, and assign them to gtroff registers.
5997 psbb_locator(const char *);
6001 const char *filename;
6002 char buf[2 + DSC_LINE_MAX];
6003 int llx, lly, urx, ury;
6005 // CRLF handling hook, for get_line() function.
6009 // Private method functions facilitate implementation of the
6010 // class constructor; none are used in any other context.
6013 inline bool get_header_comment(void);
6014 inline const char *context_args(const char *);
6015 inline const char *context_args(const char *, const char *);
6016 inline const char *bounding_box_args(void);
6017 int parse_bounding_box(const char *);
6018 inline void assign_registers(void);
6019 inline int skip_to_trailer(void);
6022 // psbb_locator class constructor.
6024 psbb_locator::psbb_locator(const char *fname):
6025 filename(fname), llx(0), lly(0), urx(0), ury(0), lastc(EOF)
6027 // PS files might contain non-printable characters, such as ^Z
6028 // and CRs not followed by an LF, so open them in binary mode.
6030 fp = include_search_path.open_file_cautious(filename, 0, FOPEN_RB);
6032 // After successfully opening the file, acquire the first
6033 // line, whence we may determine the file format...
6035 if (get_line(DSC_LINE_MAX_ENFORCE) == 0)
6037 // ...except in the case of an empty file, which we are
6038 // unable to process further.
6040 error("'%1' is empty", filename);
6043 else if (context_args("%PDF-")) {
6044 // TODO: PDF files specify a /MediaBox, as the equivalent
6045 // of %%BoundingBox; we must implement a handler for this.
6049 else if (context_args("%!PS-Adobe-")) {
6051 // PostScript files -- strictly, we expect EPS -- should
6052 // specify a %%BoundingBox comment; locate it, initially
6053 // expecting to find it in the comments header...
6055 const char *context = NULL;
6056 while ((context == NULL) && get_header_comment()) {
6057 if ((context = bounding_box_args()) != NULL) {
6059 // When the "%%BoundingBox" comment is found, it may simply
6060 // specify the bounding box property values, or it may defer
6061 // assignment to a similar trailer comment...
6063 int status = parse_bounding_box(context);
6064 if (status == PSBB_RANGE_AT_END) {
6066 // ...in which case we must locate the trailer, and search
6067 // for the appropriate specification within it.
6069 if (skip_to_trailer() > 0) {
6070 while ((context = bounding_box_args()) == NULL
6071 && get_line(DSC_LINE_MAX_ENFORCE) > 0)
6073 if (context != NULL) {
6075 // When we find a bounding box specification here...
6077 if ((status = parse_bounding_box(context)) == PSBB_RANGE_AT_END)
6079 // ...we must ensure it is not a further attempt to defer
6080 // assignment to a trailer, (which we are already parsing).
6082 error("'(atend)' not allowed in trailer of '%1'", filename);
6086 // The trailer could not be found, so there is no context in
6087 // which a trailing %%BoundingBox comment might be located.
6091 if (status == PSBB_RANGE_IS_BAD) {
6093 // This arises when we found a %%BoundingBox comment, but
6094 // we were unable to extract a valid set of range values from
6095 // it; all we can do is diagnose this.
6097 error("the arguments to the %%%%BoundingBox comment in '%1' are bad",
6102 if (context == NULL)
6104 // Conversely, this arises when no value specifying %%BoundingBox
6105 // comment has been found, in any appropriate location...
6107 error("%%%%BoundingBox comment not found in '%1'", filename);
6110 // ...while this indicates that there was no appropriate file format
6111 // identifier, on the first line of the input file.
6113 error("'%1' does not conform to the Document Structuring Conventions",
6116 // Regardless of success or failure of bounding box property acquisition,
6117 // we did successfully open an input file, so we must now close it...
6122 // ...but in this case, we did not successfully open any input file.
6124 error("can't open '%1': %2", filename, strerror(errno));
6126 // Irrespective of whether or not we were able to successfully acquire the
6127 // bounding box properties, we ALWAYS update the associated gtroff registers.
6132 // psbb_locator::parse_bounding_box()
6134 // Parse the argument to a %%BoundingBox comment, returning:
6135 // PSBB_RANGE_IS_SET if it contains four numbers,
6136 // PSBB_RANGE_AT_END if it contains "(atend)", or
6137 // PSBB_RANGE_IS_BAD otherwise.
6139 int psbb_locator::parse_bounding_box(const char *context)
6141 // The Document Structuring Conventions say that the numbers
6142 // should be integers.
6144 int status = PSBB_RANGE_IS_SET;
6145 if (sscanf(context, "%d %d %d %d", &llx, &lly, &urx, &ury) != 4) {
6147 // Unfortunately some broken applications get this wrong;
6148 // try to parse them as doubles instead...
6150 double x1, x2, x3, x4;
6151 if (sscanf(context, "%lf %lf %lf %lf", &x1, &x2, &x3, &x4) == 4) {
6158 // ...but if we can't parse four numbers, skip over any
6159 // initial white space...
6161 while (*context == '\x20' || *context == '\t')
6164 // ...before checking for "(atend)", and setting the
6165 // appropriate exit status accordingly.
6167 status = (context_args("(atend)", context) == NULL)
6168 ? llx = lly = urx = ury = PSBB_RANGE_IS_BAD
6169 : PSBB_RANGE_AT_END;
6175 // ps_locator::get_line()
6177 // Collect an input record from a PostScript or PDF file.
6180 // buf pointer to caller's input buffer.
6181 // fp FILE stream pointer, whence input is read.
6182 // filename name of input file, (for diagnostic use only).
6183 // dscopt DSC_LINE_MAX_ENFORCE or DSC_LINE_MAX_IGNORED.
6185 // Returns the number of input characters stored into caller's
6186 // buffer, or zero at end of input stream.
6188 // FIXME: Currently, get_line() always scans an entire line of
6189 // input, but returns only as much as will fit in caller's buffer;
6190 // the return value is always a positive integer, or zero, with no
6191 // way of indicating to caller, that there was more data than the
6192 // buffer could accommodate. A future enhancement could mitigate
6193 // this, returning a negative value in the event of truncation, or
6194 // even allowing for piecewise retrieval of excessively long lines
6195 // in successive reads; (this may be necessary to properly support
6196 // DSC_LINE_MAX_IGNORED, which is currently unimplemented).
6198 int psbb_locator::get_line(int dscopt)
6202 // Collect input characters into caller's buffer, until we
6203 // encounter a line terminator, or end of file...
6205 while (((c = getc(fp)) != '\n') && (c != '\r') && (c != EOF)) {
6206 if ((((lastc = c) < 0x1b) && !white_space(c)) || (c == 0x7f))
6208 // ...rejecting any which may be designated as invalid.
6210 error("invalid input character code %1 in '%2'", int(c), filename);
6212 // On reading a valid input character, and when there is
6213 // room in caller's buffer...
6215 else if (count < DSC_LINE_MAX)
6221 // We have a valid input character, but it will not fit
6222 // into caller's buffer; if enforcing DSC conformity...
6224 else if (dscopt == DSC_LINE_MAX_ENFORCE) {
6226 // ...diagnose and truncate.
6228 dscopt = DSC_LINE_MAX_CHECKED;
6229 error("PostScript file '%1' is non-conforming "
6230 "because length of line exceeds 255", filename);
6233 // Reading LF may be a special case: when it immediately
6234 // follows a CR which terminated the preceding input line,
6235 // we deem it to complete a CRLF terminator for the already
6236 // collected preceding line; discard it, and restart input
6237 // collection for the current line.
6239 } while ((lastc == '\r') && ((lastc = c) == '\n'));
6241 // For each collected input line, record its actual terminator,
6242 // substitute our preferred LF terminator...
6244 if (((lastc = c) != EOF) || (count > 0))
6245 buf[count++] = '\n';
6247 // ...and append the required C-string (NUL) terminator, before
6248 // returning the actual count of input characters stored.
6254 // psbb_locator::context_args()
6257 // tag literal text to be matched at start of input line
6259 // Returns a pointer to the trailing substring of the current
6260 // input line, following an initial substring matching the "tag"
6261 // argument, or NULL if "tag" is not matched.
6263 inline const char *psbb_locator::context_args(const char *tag)
6265 return context_args(tag, buf);
6268 // psbb_locator::context_args()
6270 // Overloaded variant of the preceding function, operating on
6271 // an alternative input buffer, (which may represent a terminal
6272 // substring of the psbb_locator's primary input line buffer).
6275 // tag literal text to be matched at start of buffer
6276 // p pointer to text to be checked for "tag" match
6278 // Returns a pointer to the trailing substring of the specified
6279 // text buffer, following an initial substring matching the "tag"
6280 // argument, or NULL if "tag" is not matched.
6282 inline const char *psbb_locator::context_args(const char *tag, const char *p)
6284 size_t len = strlen(tag);
6285 return (strncmp(tag, p, len) == 0) ? p + len : NULL;
6288 // psbb_locator::bounding_box_args()
6290 // Returns a pointer to the arguments string, within the current
6291 // input line, when this represents a PostScript "%%BoundingBox:"
6292 // comment, or NULL otherwise.
6294 inline const char *psbb_locator::bounding_box_args(void)
6296 return context_args("%%BoundingBox:");
6299 // psbb_locator::assign_registers()
6301 // Copies the bounding box properties established within the
6302 // class object, to the associated gtroff registers.
6304 inline void psbb_locator::assign_registers(void)
6306 llx_reg_contents = llx;
6307 lly_reg_contents = lly;
6308 urx_reg_contents = urx;
6309 ury_reg_contents = ury;
6312 // psbb_locator::get_header_comment()
6314 // Fetch a line of PostScript input; return true if it complies with
6315 // the formatting requirements for header comments, and it is not an
6316 // "%%EndComments" line; otherwise return false.
6318 inline bool psbb_locator::get_header_comment(void)
6321 // The first necessary requirement, for returning true,
6322 // is that the input line is not empty, (i.e. not EOF).
6324 get_line(DSC_LINE_MAX_ENFORCE) != 0
6326 // In header comments, '%X' ('X' any printable character
6327 // except whitespace) is also acceptable.
6329 && (buf[0] == '%') && !white_space(buf[1])
6331 // Finally, the input line must not say "%%EndComments".
6333 && context_args("%%EndComments") == NULL;
6336 // psbb_locator::skip_to_trailer()
6338 // Reposition the PostScript input stream, such that the next get_line()
6339 // will retrieve the first line, if any, following a "%%Trailer" comment;
6340 // returns a positive integer value if the "%%Trailer" comment is found,
6341 // or zero if it is not.
6343 inline int psbb_locator::skip_to_trailer(void)
6345 // Begin by considering a chunk of the input file starting 512 bytes
6346 // before its end, and search it for a "%%Trailer" comment; if none is
6347 // found, incrementally double the chunk size while it remains within
6348 // a 32768L byte range, and search again...
6350 for (ssize_t offset = 512L; offset > 0L; offset <<= 1) {
6352 if ((offset > 32768L) || ((failed = fseek(fp, -offset, SEEK_END)) != 0))
6354 // ...ultimately resetting the offset to zero, and simply seeking
6355 // to the start of the file, to terminate the cycle and do a "last
6356 // ditch" search of the entire file, if any backward seek fails, or
6357 // if we reach the arbitrary 32768L byte range limit.
6359 failed = fseek(fp, offset = 0L, SEEK_SET);
6361 // Following each successful seek...
6365 // ...perform a search by reading lines from the input stream...
6367 do { status = get_line(DSC_LINE_MAX_ENFORCE);
6369 // ...until we either exhaust the available stream data, or
6370 // we have located a "%%Trailer" comment line.
6372 } while ((status != 0) && (context_args("%%Trailer") == NULL));
6375 // We found the "%%Trailer" comment, so we may immediately
6376 // return, with the stream positioned appropriately...
6381 // ...otherwise, we report that no "%%Trailer" comment was found.
6386 // ps_bbox_request()
6388 // Handle the .psbb request.
6390 void ps_bbox_request()
6392 // Parse input line, to extract file name.
6394 symbol nm = get_long_name(1);
6397 // No file name specified: ignore the entire request.
6401 // File name acquired: swallow the rest of the line.
6403 while (!tok.newline() && !tok.eof())
6407 // Update {llx,lly,urx,ury}_reg_contents:
6408 // declaring this class instance achieves this, as an
6409 // intentional side effect of object construction.
6411 psbb_locator do_ps_file(nm.contents());
6413 // All done for .psbb; move on, to continue
6414 // input stream processing.
6420 const char *asciify(int c)
6423 buf[0] = escape_char == '\0' ? '\\' : escape_char;
6424 buf[1] = buf[2] = '\0';
6426 case ESCAPE_QUESTION:
6429 case ESCAPE_AMPERSAND:
6432 case ESCAPE_RIGHT_PARENTHESIS:
6435 case ESCAPE_UNDERSCORE:
6441 case ESCAPE_CIRCUMFLEX:
6444 case ESCAPE_LEFT_BRACE:
6447 case ESCAPE_RIGHT_BRACE:
6450 case ESCAPE_LEFT_QUOTE:
6453 case ESCAPE_RIGHT_QUOTE:
6471 case ESCAPE_PERCENT:
6483 case PUSH_GROFF_MODE:
6484 case PUSH_COMP_MODE:
6485 case POP_GROFFCOMP_MODE:
6489 if (invalid_input_char(c))
6498 const char *input_char_description(int c)
6502 return "a newline character";
6504 return "a backspace character";
6506 return "a leader character";
6508 return "a tab character";
6510 return "a space character";
6514 static char buf[sizeof("magic character code ") + 1 + INT_DIGITS];
6515 if (invalid_input_char(c)) {
6516 const char *s = asciify(c);
6523 sprintf(buf, "magic character code %d", c);
6532 sprintf(buf, "character code %d", c);
6538 if (!tok.newline() && !tok.eof()) {
6547 if (c != ' ' && c != '\t')
6551 for (; c != '\n' && c != EOF; c = get_copy(0))
6554 curenv->add_node(new tag_node(s, 0));
6561 if (!tok.newline() && !tok.eof()) {
6570 if (c != ' ' && c != '\t')
6574 for (; c != '\n' && c != EOF; c = get_copy(0))
6577 curenv->add_node(new tag_node(s, 1));
6582 // .tm, .tm1, and .tmc
6584 void do_terminal(int newline, int string_like)
6586 if (!tok.newline() && !tok.eof()) {
6590 if (string_like && c == '"') {
6594 if (c != ' ' && c != '\t')
6597 for (; c != '\n' && c != EOF; c = get_copy(0))
6598 fputs(asciify(c), stderr);
6601 fputc('\n', stderr);
6616 void terminal_continue()
6621 dictionary stream_dictionary(20);
6623 void do_open(int append)
6625 symbol stream = get_name(1);
6626 if (!stream.is_null()) {
6627 symbol filename = get_long_name(1);
6628 if (!filename.is_null()) {
6630 FILE *fp = fopen(filename.contents(), append ? "a" : "w");
6632 error("can't open '%1' for %2: %3",
6633 filename.contents(),
6634 append ? "appending" : "writing",
6636 fp = (FILE *)stream_dictionary.remove(stream);
6639 fp = (FILE *)stream_dictionary.lookup(stream, fp);
6650 error(".open request not allowed in safer mode");
6657 void opena_request()
6660 error(".opena request not allowed in safer mode");
6667 void close_request()
6669 symbol stream = get_name(1);
6670 if (!stream.is_null()) {
6671 FILE *fp = (FILE *)stream_dictionary.remove(stream);
6673 error("no stream named '%1'", stream.contents());
6680 // .write and .writec
6682 void do_write_request(int newline)
6684 symbol stream = get_name(1);
6685 if (stream.is_null()) {
6689 FILE *fp = (FILE *)stream_dictionary.lookup(stream);
6691 error("no stream named '%1'", stream.contents());
6696 while ((c = get_copy(0)) == ' ')
6700 for (; c != '\n' && c != EOF; c = get_copy(0))
6701 fputs(asciify(c), fp);
6708 void write_request()
6710 do_write_request(1);
6713 void write_request_continue()
6715 do_write_request(0);
6718 void write_macro_request()
6720 symbol stream = get_name(1);
6721 if (stream.is_null()) {
6725 FILE *fp = (FILE *)stream_dictionary.lookup(stream);
6727 error("no stream named '%1'", stream.contents());
6731 symbol s = get_name(1);
6736 request_or_macro *p = lookup_request(s);
6737 macro *m = p->to_macro();
6739 error("cannot write request");
6741 string_iterator iter(*m);
6743 int c = iter.get(0);
6746 fputs(asciify(c), fp);
6753 void warnscale_request()
6760 warn_scale = (double)units_per_inch;
6762 warn_scale = (double)units_per_inch / 2.54;
6764 warn_scale = (double)units_per_inch / 72.0;
6766 warn_scale = (double)units_per_inch / 6.0;
6769 "invalid scaling indicator '%1', using 'i' instead", c);
6772 warn_scaling_indicator = c;
6777 void spreadwarn_request()
6780 if (has_arg() && get_hunits(&n, 'm')) {
6783 hunits em = curenv->get_size();
6784 spread_limit = (double)n.to_units()
6785 / (em.is_zero() ? hresolution : em.to_units());
6788 spread_limit = -spread_limit - 1; // no arg toggles on/off without
6789 // changing value; we mirror at
6790 // -0.5 to make zero a valid value
6794 static void init_charset_table()
6797 strcpy(buf, "char");
6798 for (int i = 0; i < 256; i++) {
6799 strcpy(buf + 4, i_to_a(i));
6800 charset_table[i] = get_charinfo(symbol(buf));
6801 charset_table[i]->set_ascii_code(i);
6803 charset_table[i]->set_hyphenation_code(cmlower(i));
6805 charset_table['.']->set_flags(charinfo::ENDS_SENTENCE);
6806 charset_table['?']->set_flags(charinfo::ENDS_SENTENCE);
6807 charset_table['!']->set_flags(charinfo::ENDS_SENTENCE);
6808 charset_table['-']->set_flags(charinfo::BREAK_AFTER);
6809 charset_table['"']->set_flags(charinfo::TRANSPARENT);
6810 charset_table['\'']->set_flags(charinfo::TRANSPARENT);
6811 charset_table[')']->set_flags(charinfo::TRANSPARENT);
6812 charset_table[']']->set_flags(charinfo::TRANSPARENT);
6813 charset_table['*']->set_flags(charinfo::TRANSPARENT);
6814 get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT);
6815 get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT);
6816 get_charinfo(symbol("cq"))->set_flags(charinfo::TRANSPARENT);
6817 get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER);
6818 get_charinfo(symbol("hy"))->set_flags(charinfo::BREAK_AFTER);
6819 get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6820 get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6821 get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6822 get_charinfo(symbol("sqrtex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6823 get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6824 get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY);
6825 page_character = charset_table['%'];
6828 static void init_hpf_code_table()
6830 for (int i = 0; i < 256; i++)
6831 hpf_code_table[i] = cmlower(i);
6834 static void do_translate(int translate_transparent, int translate_input)
6837 while (!tok.newline() && !tok.eof()) {
6839 // This is a really bizarre troff feature.
6841 translate_space_to_dummy = tok.dummy();
6842 if (tok.newline() || tok.eof())
6847 charinfo *ci1 = tok.get_char(1);
6851 if (tok.newline() || tok.eof()) {
6852 ci1->set_special_translation(charinfo::TRANSLATE_SPACE,
6853 translate_transparent);
6857 ci1->set_special_translation(charinfo::TRANSLATE_SPACE,
6858 translate_transparent);
6859 else if (tok.stretchable_space())
6860 ci1->set_special_translation(charinfo::TRANSLATE_STRETCHABLE_SPACE,
6861 translate_transparent);
6862 else if (tok.dummy())
6863 ci1->set_special_translation(charinfo::TRANSLATE_DUMMY,
6864 translate_transparent);
6865 else if (tok.hyphen_indicator())
6866 ci1->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR,
6867 translate_transparent);
6869 charinfo *ci2 = tok.get_char(1);
6873 ci1->set_translation(0, translate_transparent, translate_input);
6875 ci1->set_translation(ci2, translate_transparent, translate_input);
6887 void translate_no_transparent()
6892 void translate_input()
6900 if (get_integer(&flags))
6902 charinfo *ci = tok.get_char(1);
6904 charinfo *tem = ci->get_translation();
6907 ci->set_flags(flags);
6914 void hyphenation_code()
6917 while (!tok.newline() && !tok.eof()) {
6918 charinfo *ci = tok.get_char(1);
6923 unsigned char c = tok.ch();
6925 error("hyphenation code must be ordinary character");
6929 error("hyphenation code cannot be digit");
6932 ci->set_hyphenation_code(c);
6933 if (ci->get_translation()
6934 && ci->get_translation()->get_translation_input())
6935 ci->get_translation()->set_hyphenation_code(c);
6942 void hyphenation_patterns_file_code()
6945 while (!tok.newline() && !tok.eof()) {
6947 if (get_integer(&n1) && (0 <= n1 && n1 <= 255)) {
6949 error("missing output hyphenation code");
6952 if (get_integer(&n2) && (0 <= n2 && n2 <= 255)) {
6953 hpf_code_table[n1] = n2;
6957 error("output hyphenation code must be integer in the range 0..255");
6962 error("input hyphenation code must be integer in the range 0..255");
6969 dictionary char_class_dictionary(501);
6974 symbol nm = get_name(1);
6979 charinfo *ci = get_charinfo(nm);
6980 charinfo *child1 = 0, *child2 = 0;
6981 while (!tok.newline() && !tok.eof()) {
6983 if (child1 != 0 && tok.ch() == '-') {
6985 child2 = tok.get_char(1);
6987 warning(WARN_MISSING,
6988 "missing end of character range in class '%1'",
6993 if (child1->is_class() || child2->is_class()) {
6994 warning(WARN_SYNTAX,
6995 "nested character class is not allowed in range definition");
6999 int u1 = child1->get_unicode_code();
7000 int u2 = child2->get_unicode_code();
7002 warning(WARN_SYNTAX,
7003 "invalid start value in character range");
7008 warning(WARN_SYNTAX,
7009 "invalid end value in character range");
7013 ci->add_to_class(u1, u2);
7014 child1 = child2 = 0;
7016 else if (child1 != 0) {
7017 if (child1->is_class()) {
7019 warning(WARN_SYNTAX, "invalid cyclic class nesting");
7023 ci->add_to_class(child1);
7026 int u1 = child1->get_unicode_code();
7028 warning(WARN_SYNTAX,
7029 "invalid character value in class '%1'",
7034 ci->add_to_class(u1);
7038 child1 = tok.get_char(1);
7047 if (child1->is_class()) {
7049 warning(WARN_SYNTAX, "invalid cyclic class nesting");
7053 ci->add_to_class(child1);
7056 int u1 = child1->get_unicode_code();
7058 warning(WARN_SYNTAX,
7059 "invalid character value in class '%1'",
7064 ci->add_to_class(u1);
7068 if (!ci->is_class()) {
7069 warning(WARN_SYNTAX,
7070 "empty class definition for '%1'",
7075 (void)char_class_dictionary.lookup(nm, ci);
7079 charinfo *token::get_char(int required)
7081 if (type == TOKEN_CHAR)
7082 return charset_table[c];
7083 if (type == TOKEN_SPECIAL)
7084 return get_charinfo(nm);
7085 if (type == TOKEN_NUMBERED_CHAR)
7086 return get_charinfo_by_number(val);
7087 if (type == TOKEN_ESCAPE) {
7088 if (escape_char != 0)
7089 return charset_table[escape_char];
7091 error("'\\e' used while no current escape character");
7096 if (type == TOKEN_EOF || type == TOKEN_NEWLINE)
7097 warning(WARN_MISSING, "missing normal or special character");
7099 error("normal or special character expected (got %1)", description());
7104 charinfo *get_optional_char()
7108 charinfo *ci = tok.get_char();
7110 check_missing_character();
7116 void check_missing_character()
7118 if (!tok.newline() && !tok.eof() && !tok.right_brace() && !tok.tab())
7119 error("normal or special character expected (got %1): "
7120 "treated as missing",
7126 int token::add_to_node_list(node **pp)
7133 *pp = (*pp)->add_char(charset_table[c], curenv, &w, &s);
7139 if (escape_char != 0)
7140 *pp = (*pp)->add_char(charset_table[escape_char], curenv, &w, &s);
7142 case TOKEN_HYPHEN_INDICATOR:
7143 *pp = (*pp)->add_discretionary_hyphen();
7145 case TOKEN_ITALIC_CORRECTION:
7146 *pp = (*pp)->add_italic_correction(&w);
7148 case TOKEN_LEFT_BRACE:
7150 case TOKEN_MARK_INPUT:
7151 set_number_reg(nm, curenv->get_input_line_position().to_units());
7154 case TOKEN_HORIZONTAL_SPACE:
7158 case TOKEN_NUMBERED_CHAR:
7159 *pp = (*pp)->add_char(get_charinfo_by_number(val), curenv, &w, &s);
7161 case TOKEN_RIGHT_BRACE:
7164 n = new hmotion_node(curenv->get_space_width(),
7165 curenv->get_fill_color());
7168 *pp = (*pp)->add_char(get_charinfo(nm), curenv, &w, &s);
7170 case TOKEN_STRETCHABLE_SPACE:
7171 n = new unbreakable_space_node(curenv->get_space_width(),
7172 curenv->get_fill_color());
7174 case TOKEN_UNSTRETCHABLE_SPACE:
7175 n = new space_char_hmotion_node(curenv->get_space_width(),
7176 curenv->get_fill_color());
7178 case TOKEN_TRANSPARENT_DUMMY:
7179 n = new transparent_dummy_node;
7181 case TOKEN_ZERO_WIDTH_BREAK:
7182 n = new space_node(H0, curenv->get_fill_color());
7184 n->is_escape_colon();
7196 void token::process()
7198 if (possibly_handle_first_page_transition())
7201 case TOKEN_BACKSPACE:
7202 curenv->add_node(new hmotion_node(-curenv->get_space_width(),
7203 curenv->get_fill_color()));
7206 curenv->add_char(charset_table[c]);
7209 curenv->add_node(new dummy_node);
7218 if (escape_char != 0)
7219 curenv->add_char(charset_table[escape_char]);
7221 case TOKEN_BEGIN_TRAP:
7222 case TOKEN_END_TRAP:
7223 case TOKEN_PAGE_EJECTOR:
7224 // these are all handled in process_input_stack()
7226 case TOKEN_HYPHEN_INDICATOR:
7227 curenv->add_hyphen_indicator();
7229 case TOKEN_INTERRUPT:
7230 curenv->interrupt();
7232 case TOKEN_ITALIC_CORRECTION:
7233 curenv->add_italic_correction();
7236 curenv->handle_tab(1);
7238 case TOKEN_LEFT_BRACE:
7240 case TOKEN_MARK_INPUT:
7241 set_number_reg(nm, curenv->get_input_line_position().to_units());
7247 case TOKEN_HORIZONTAL_SPACE:
7248 curenv->add_node(nd);
7251 case TOKEN_NUMBERED_CHAR:
7252 curenv->add_char(get_charinfo_by_number(val));
7255 // handled in process_input_stack()
7257 case TOKEN_RIGHT_BRACE:
7263 curenv->add_char(get_charinfo(nm));
7268 case TOKEN_STRETCHABLE_SPACE:
7269 curenv->add_node(new unbreakable_space_node(curenv->get_space_width(),
7270 curenv->get_fill_color()));
7272 case TOKEN_UNSTRETCHABLE_SPACE:
7273 curenv->add_node(new space_char_hmotion_node(curenv->get_space_width(),
7274 curenv->get_fill_color()));
7277 curenv->handle_tab(0);
7279 case TOKEN_TRANSPARENT:
7281 case TOKEN_TRANSPARENT_DUMMY:
7282 curenv->add_node(new transparent_dummy_node);
7284 case TOKEN_ZERO_WIDTH_BREAK:
7286 node *tmp = new space_node(H0, curenv->get_fill_color());
7287 tmp->freeze_space();
7288 tmp->is_escape_colon();
7289 curenv->add_node(tmp);
7297 class nargs_reg : public reg {
7299 const char *get_string();
7302 const char *nargs_reg::get_string()
7304 return i_to_a(input_stack::nargs());
7307 class lineno_reg : public reg {
7309 const char *get_string();
7312 const char *lineno_reg::get_string()
7316 if (!input_stack::get_location(0, &file, &line))
7318 return i_to_a(line);
7321 class writable_lineno_reg : public general_reg {
7323 writable_lineno_reg();
7324 void set_value(units);
7325 int get_value(units *);
7328 writable_lineno_reg::writable_lineno_reg()
7332 int writable_lineno_reg::get_value(units *res)
7336 if (!input_stack::get_location(0, &file, &line))
7342 void writable_lineno_reg::set_value(units n)
7344 input_stack::set_location(0, n);
7347 class filename_reg : public reg {
7349 const char *get_string();
7352 const char *filename_reg::get_string()
7356 if (input_stack::get_location(0, &file, &line))
7362 class break_flag_reg : public reg {
7364 const char *get_string();
7367 const char *break_flag_reg::get_string()
7369 return i_to_a(input_stack::get_break_flag());
7372 class constant_reg : public reg {
7375 constant_reg(const char *);
7376 const char *get_string();
7379 constant_reg::constant_reg(const char *p) : s(p)
7383 const char *constant_reg::get_string()
7388 constant_int_reg::constant_int_reg(int *q) : p(q)
7392 const char *constant_int_reg::get_string()
7397 void abort_request()
7402 else if (tok.newline())
7405 while ((c = get_copy(0)) == ' ')
7408 if (c == EOF || c == '\n')
7409 fputs("User Abort.", stderr);
7411 for (; c != '\n' && c != EOF; c = get_copy(0))
7412 fputs(asciify(c), stderr);
7414 fputc('\n', stderr);
7415 cleanup_and_exit(1);
7421 char *s = new char[len];
7423 while ((c = get_copy(0)) == ' ')
7426 while (c != '\n' && c != EOF) {
7427 if (!invalid_input_char(c)) {
7430 s = new char[len*2];
7431 memcpy(s, tem, len);
7451 error(".pi request not allowed in safer mode");
7455 #ifdef POPEN_MISSING
7456 error("pipes not available on this system");
7458 #else /* not POPEN_MISSING */
7460 error("can't pipe: output already started");
7465 if ((pc = read_string()) == 0)
7466 error("can't pipe to empty command");
7468 char *s = new char[strlen(pipe_command) + strlen(pc) + 1 + 1];
7469 strcpy(s, pipe_command);
7472 a_delete pipe_command;
7479 #endif /* not POPEN_MISSING */
7483 static int system_status;
7485 void system_request()
7488 error(".sy request not allowed in safer mode");
7492 char *command = read_string();
7494 error("empty command");
7496 system_status = system(command);
7504 if (curdiv == topdiv && topdiv->before_first_page) {
7505 handle_initial_request(COPY_FILE_REQUEST);
7508 symbol filename = get_long_name(1);
7509 while (!tok.newline() && !tok.eof())
7513 if (!filename.is_null())
7514 curdiv->copy_file(filename.contents());
7522 if (curdiv == topdiv && topdiv->before_first_page) {
7523 handle_initial_request(VJUSTIFY_REQUEST);
7526 symbol type = get_long_name(1);
7527 if (!type.is_null())
7528 curdiv->vjustify(type);
7534 void transparent_file()
7536 if (curdiv == topdiv && topdiv->before_first_page) {
7537 handle_initial_request(TRANSPARENT_FILE_REQUEST);
7540 symbol filename = get_long_name(1);
7541 while (!tok.newline() && !tok.eof())
7545 if (!filename.is_null()) {
7547 FILE *fp = include_search_path.open_file_cautious(filename.contents());
7549 error("can't open '%1': %2", filename.contents(), strerror(errno));
7556 if (invalid_input_char(c))
7557 warning(WARN_INPUT, "invalid input character code %1", int(c));
7559 curdiv->transparent_output(c);
7564 curdiv->transparent_output('\n');
7576 page_range(int, int, page_range *);
7577 int contains(int n);
7580 page_range::page_range(int i, int j, page_range *p)
7581 : first(i), last(j), next(p)
7585 int page_range::contains(int n)
7587 return n >= first && (last <= 0 || n <= last);
7590 page_range *output_page_list = 0;
7592 int in_output_page_list(int n)
7594 if (!output_page_list)
7596 for (page_range *p = output_page_list; p; p = p->next)
7602 static void parse_output_page_list(char *p)
7608 else if (csdigit(*p)) {
7611 i = i*10 + *p++ - '0';
7612 while (csdigit(*p));
7622 j = j*10 + *p++ - '0';
7623 while (csdigit(*p));
7629 last_page_number = -1;
7630 else if (last_page_number >= 0 && j > last_page_number)
7631 last_page_number = j;
7632 output_page_list = new page_range(i, j, output_page_list);
7638 error("bad output page list");
7639 output_page_list = 0;
7643 static FILE *open_mac_file(const char *mac, char **path)
7645 // Try first FOOBAR.tmac, then tmac.FOOBAR
7646 char *s1 = new char[strlen(mac)+strlen(MACRO_POSTFIX)+1];
7648 strcat(s1, MACRO_POSTFIX);
7649 FILE *fp = mac_path->open_file(s1, path);
7652 char *s2 = new char[strlen(mac)+strlen(MACRO_PREFIX)+1];
7653 strcpy(s2, MACRO_PREFIX);
7655 fp = mac_path->open_file(s2, path);
7661 static void process_macro_file(const char *mac)
7664 FILE *fp = open_mac_file(mac, &path);
7666 fatal("can't find macro file %1", mac);
7667 const char *s = symbol(path).contents();
7669 input_stack::push(new file_iterator(fp, s));
7671 process_input_stack();
7674 static void process_startup_file(const char *filename)
7677 search_path *orig_mac_path = mac_path;
7678 mac_path = &config_macro_path;
7679 FILE *fp = mac_path->open_file(filename, &path);
7681 input_stack::push(new file_iterator(fp, symbol(path).contents()));
7684 process_input_stack();
7686 mac_path = orig_mac_path;
7691 symbol nm = get_long_name(1);
7695 while (!tok.newline() && !tok.eof())
7698 FILE *fp = mac_path->open_file(nm.contents(), &path);
7699 // .mso doesn't (and cannot) go through open_mac_file, so we
7700 // need to do it here manually: If we have tmac.FOOBAR, try
7701 // FOOBAR.tmac and vice versa
7703 const char *fn = nm.contents();
7704 size_t fnlen = strlen(fn);
7705 if (strncasecmp(fn, MACRO_PREFIX, sizeof(MACRO_PREFIX) - 1) == 0) {
7706 char *s = new char[fnlen + sizeof(MACRO_POSTFIX)];
7707 strcpy(s, fn + sizeof(MACRO_PREFIX) - 1);
7708 strcat(s, MACRO_POSTFIX);
7709 fp = mac_path->open_file(s, &path);
7713 if (strncasecmp(fn + fnlen - sizeof(MACRO_POSTFIX) + 1,
7714 MACRO_POSTFIX, sizeof(MACRO_POSTFIX) - 1) == 0) {
7715 char *s = new char[fnlen + sizeof(MACRO_PREFIX)];
7716 strcpy(s, MACRO_PREFIX);
7717 strncat(s, fn, fnlen - sizeof(MACRO_POSTFIX) + 1);
7718 fp = mac_path->open_file(s, &path);
7724 input_stack::push(new file_iterator(fp, symbol(path).contents()));
7728 warning(WARN_FILE, "can't find macro file '%1'", nm.contents());
7733 static void process_input_file(const char *name)
7736 if (strcmp(name, "-") == 0) {
7742 fp = include_search_path.open_file_cautious(name);
7744 fatal("can't open '%1': %2", name, strerror(errno));
7746 input_stack::push(new file_iterator(fp, name));
7748 process_input_stack();
7751 // make sure the_input is empty before calling this
7753 static int evaluate_expression(const char *expr, units *res)
7755 input_stack::push(make_temp_iterator(expr));
7757 int success = get_number(res, 'u');
7758 while (input_stack::get(0) != EOF)
7763 static void do_register_assignment(const char *s)
7765 const char *p = strchr(s, '=');
7771 if (evaluate_expression(s + 1, &n))
7772 set_number_reg(buf, n);
7775 char *buf = new char[p - s + 1];
7776 memcpy(buf, s, p - s);
7779 if (evaluate_expression(p + 1, &n))
7780 set_number_reg(buf, n);
7785 static void set_string(const char *name, const char *value)
7787 macro *m = new macro;
7788 for (const char *p = value; *p; p++)
7789 if (!invalid_input_char((unsigned char)*p))
7791 request_dictionary.define(name, m);
7794 static void do_string_assignment(const char *s)
7796 const char *p = strchr(s, '=');
7801 set_string(buf, s + 1);
7804 char *buf = new char[p - s + 1];
7805 memcpy(buf, s, p - s);
7807 set_string(buf, p + 1);
7812 struct string_list {
7815 string_list(const char *ss) : s(ss), next(0) {}
7819 static void prepend_string(const char *s, string_list **p)
7821 string_list *l = new string_list(s);
7827 static void add_string(const char *s, string_list **p)
7831 *p = new string_list(s);
7834 void usage(FILE *stream, const char *prog)
7837 "usage: %s -abcivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n"
7838 " -rcn -Tname -Fdir -Idir -Mdir [files...]\n",
7842 int main(int argc, char **argv)
7844 program_name = argv[0];
7845 static char stderr_buf[BUFSIZ];
7846 setbuf(stderr, stderr_buf);
7848 string_list *macros = 0;
7849 string_list *register_assignments = 0;
7850 string_list *string_assignments = 0;
7855 int no_rc = 0; // don't process troffrc and troffrc-end
7856 int next_page_number = 0; // pacify compiler
7858 hresolution = vresolution = 1;
7859 // restore $PATH if called from groff
7860 char* groff_path = getenv("GROFF_PATH__");
7867 if (putenv(strsave(e.contents())))
7868 fatal("putenv failed");
7870 setlocale(LC_CTYPE, "");
7871 static const struct option long_options[] = {
7872 { "help", no_argument, 0, CHAR_MAX + 1 },
7873 { "version", no_argument, 0, 'v' },
7876 #if defined(DEBUGGING)
7877 #define DEBUG_OPTION "D"
7879 while ((c = getopt_long(argc, argv,
7880 "abciI:vw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU"
7881 DEBUG_OPTION, long_options, 0))
7886 printf("GNU troff (groff) version %s\n", Version_string);
7891 // Search path for .psbb files
7892 // and most other non-system input files.
7893 include_search_path.command_line_dir(optarg);
7898 is_html = (strcmp(device, "html") == 0);
7901 compatible_flag = 1;
7907 macro_path.command_line_dir(optarg);
7908 safer_macro_path.command_line_dir(optarg);
7909 config_macro_path.command_line_dir(optarg);
7912 font::command_line_font_dir(optarg);
7915 add_string(optarg, ¯os);
7924 enable_warning(optarg);
7927 disable_warning(optarg);
7936 ascii_output_flag = 1;
7939 suppress_output_flag = 1;
7942 if (sscanf(optarg, "%d", &next_page_number) == 1)
7945 error("bad page number");
7948 parse_output_page_list(optarg);
7951 if (*optarg == '\0')
7952 error("'-d' requires non-empty argument");
7954 add_string(optarg, &string_assignments);
7957 if (*optarg == '\0')
7958 error("'-r' requires non-empty argument");
7960 add_string(optarg, ®ister_assignments);
7963 default_family = symbol(optarg);
7969 // silently ignore these
7972 unsafe_flag = 1; // unsafe behaviour
7974 #if defined(DEBUGGING)
7979 case CHAR_MAX + 1: // --help
7980 usage(stdout, argv[0]);
7984 usage(stderr, argv[0]);
7986 break; // never reached
7991 mac_path = ¯o_path;
7992 set_string(".T", device);
7993 init_charset_table();
7994 init_hpf_code_table();
7995 if (!font::load_desc())
7996 fatal("sorry, I can't continue");
7997 units_per_inch = font::res;
7998 hresolution = font::hor;
7999 vresolution = font::vert;
8000 sizescale = font::sizescale;
8001 tcommand_flag = font::tcommand;
8002 warn_scale = (double)units_per_inch;
8003 warn_scaling_indicator = 'i';
8004 if (!fflag && font::family != 0 && *font::family != '\0')
8005 default_family = symbol(font::family);
8006 font_size::init_size_table(font::sizes);
8009 if (font::style_table) {
8010 for (i = 0; font::style_table[i]; i++)
8011 mount_style(j++, symbol(font::style_table[i]));
8013 for (i = 0; font::font_name_table[i]; i++, j++)
8014 // In the DESC file a font name of 0 (zero) means leave this
8016 if (strcmp(font::font_name_table[i], "0") != 0)
8017 mount_font(j, symbol(font::font_name_table[i]));
8018 curdiv = topdiv = new top_level_diversion;
8020 topdiv->set_next_page_number(next_page_number);
8021 init_input_requests();
8022 init_env_requests();
8023 init_div_requests();
8025 init_column_requests();
8027 init_node_requests();
8028 number_reg_dictionary.define(".T", new constant_reg(tflag ? "1" : "0"));
8030 init_reg_requests();
8031 init_hyphen_requests();
8032 init_environments();
8033 while (string_assignments) {
8034 do_string_assignment(string_assignments->s);
8035 string_list *tem = string_assignments;
8036 string_assignments = string_assignments->next;
8039 while (register_assignments) {
8040 do_register_assignment(register_assignments->s);
8041 string_list *tem = register_assignments;
8042 register_assignments = register_assignments->next;
8046 process_startup_file(INITIAL_STARTUP_FILE);
8048 process_macro_file(macros->s);
8049 string_list *tem = macros;
8050 macros = macros->next;
8054 process_startup_file(FINAL_STARTUP_FILE);
8055 for (i = optind; i < argc; i++)
8056 process_input_file(argv[i]);
8057 if (optind >= argc || iflag)
8058 process_input_file("-");
8060 return 0; // not reached
8066 if (has_arg() && get_integer(&n)) {
8067 if (n & ~WARN_TOTAL) {
8068 warning(WARN_RANGE, "warning mask must be between 0 and %1", WARN_TOTAL);
8074 warning_mask = WARN_TOTAL;
8078 static void init_registers()
8080 #ifdef LONG_FOR_TIME_T
8082 #else /* not LONG_FOR_TIME_T */
8084 #endif /* not LONG_FOR_TIME_T */
8086 // Use struct here to work around misfeature in old versions of g++.
8087 struct tm *tt = localtime(&t);
8088 set_number_reg("seconds", int(tt->tm_sec));
8089 set_number_reg("minutes", int(tt->tm_min));
8090 set_number_reg("hours", int(tt->tm_hour));
8091 set_number_reg("dw", int(tt->tm_wday + 1));
8092 set_number_reg("dy", int(tt->tm_mday));
8093 set_number_reg("mo", int(tt->tm_mon + 1));
8094 set_number_reg("year", int(1900 + tt->tm_year));
8095 set_number_reg("yr", int(tt->tm_year));
8096 set_number_reg("$$", getpid());
8097 number_reg_dictionary.define(".A",
8098 new constant_reg(ascii_output_flag
8104 * registers associated with \O
8107 static int output_reg_minx_contents = -1;
8108 static int output_reg_miny_contents = -1;
8109 static int output_reg_maxx_contents = -1;
8110 static int output_reg_maxy_contents = -1;
8112 void check_output_limits(int x, int y)
8114 if ((output_reg_minx_contents == -1) || (x < output_reg_minx_contents))
8115 output_reg_minx_contents = x;
8116 if (x > output_reg_maxx_contents)
8117 output_reg_maxx_contents = x;
8118 if ((output_reg_miny_contents == -1) || (y < output_reg_miny_contents))
8119 output_reg_miny_contents = y;
8120 if (y > output_reg_maxy_contents)
8121 output_reg_maxy_contents = y;
8124 void reset_output_registers()
8126 output_reg_minx_contents = -1;
8127 output_reg_miny_contents = -1;
8128 output_reg_maxx_contents = -1;
8129 output_reg_maxy_contents = -1;
8132 void get_output_registers(int *minx, int *miny, int *maxx, int *maxy)
8134 *minx = output_reg_minx_contents;
8135 *miny = output_reg_miny_contents;
8136 *maxx = output_reg_maxx_contents;
8137 *maxy = output_reg_maxy_contents;
8140 void init_input_requests()
8142 init_request("ab", abort_request);
8143 init_request("als", alias_macro);
8144 init_request("am", append_macro);
8145 init_request("am1", append_nocomp_macro);
8146 init_request("ami", append_indirect_macro);
8147 init_request("ami1", append_indirect_nocomp_macro);
8148 init_request("as", append_string);
8149 init_request("as1", append_nocomp_string);
8150 init_request("asciify", asciify_macro);
8151 init_request("backtrace", backtrace_request);
8152 init_request("blm", blank_line_macro);
8153 init_request("break", while_break_request);
8154 init_request("cf", copy_file);
8155 init_request("cflags", char_flags);
8156 init_request("char", define_character);
8157 init_request("chop", chop_macro);
8158 init_request("class", define_class);
8159 init_request("close", close_request);
8160 init_request("color", activate_color);
8161 init_request("composite", composite_request);
8162 init_request("continue", while_continue_request);
8163 init_request("cp", compatible);
8164 init_request("de", define_macro);
8165 init_request("de1", define_nocomp_macro);
8166 init_request("defcolor", define_color);
8167 init_request("dei", define_indirect_macro);
8168 init_request("dei1", define_indirect_nocomp_macro);
8169 init_request("device", device_request);
8170 init_request("devicem", device_macro_request);
8171 init_request("do", do_request);
8172 init_request("ds", define_string);
8173 init_request("ds1", define_nocomp_string);
8174 init_request("ec", set_escape_char);
8175 init_request("ecr", restore_escape_char);
8176 init_request("ecs", save_escape_char);
8177 init_request("el", else_request);
8178 init_request("em", end_macro);
8179 init_request("eo", escape_off);
8180 init_request("ex", exit_request);
8181 init_request("fchar", define_fallback_character);
8182 #ifdef WIDOW_CONTROL
8183 init_request("fpl", flush_pending_lines);
8184 #endif /* WIDOW_CONTROL */
8185 init_request("hcode", hyphenation_code);
8186 init_request("hpfcode", hyphenation_patterns_file_code);
8187 init_request("ie", if_else_request);
8188 init_request("if", if_request);
8189 init_request("ig", ignore);
8190 init_request("length", length_request);
8191 init_request("lf", line_file);
8192 init_request("lsm", leading_spaces_macro);
8193 init_request("mso", macro_source);
8194 init_request("nop", nop_request);
8195 init_request("nroff", nroff_request);
8196 init_request("nx", next_file);
8197 init_request("open", open_request);
8198 init_request("opena", opena_request);
8199 init_request("output", output_request);
8200 init_request("pc", set_page_character);
8201 init_request("pi", pipe_output);
8202 init_request("pm", print_macros);
8203 init_request("psbb", ps_bbox_request);
8204 #ifndef POPEN_MISSING
8205 init_request("pso", pipe_source);
8206 #endif /* not POPEN_MISSING */
8207 init_request("rchar", remove_character);
8208 init_request("rd", read_request);
8209 init_request("return", return_macro_request);
8210 init_request("rm", remove_macro);
8211 init_request("rn", rename_macro);
8212 init_request("schar", define_special_character);
8213 init_request("shift", shift);
8214 init_request("so", source);
8215 init_request("spreadwarn", spreadwarn_request);
8216 init_request("substring", substring_request);
8217 init_request("sy", system_request);
8218 init_request("tag", tag);
8219 init_request("taga", taga);
8220 init_request("tm", terminal);
8221 init_request("tm1", terminal1);
8222 init_request("tmc", terminal_continue);
8223 init_request("tr", translate);
8224 init_request("trf", transparent_file);
8225 init_request("trin", translate_input);
8226 init_request("trnt", translate_no_transparent);
8227 init_request("troff", troff_request);
8228 init_request("unformat", unformat_macro);
8230 init_request("vj", vjustify);
8232 init_request("warn", warn_request);
8233 init_request("warnscale", warnscale_request);
8234 init_request("while", while_request);
8235 init_request("write", write_request);
8236 init_request("writec", write_request_continue);
8237 init_request("writem", write_macro_request);
8238 number_reg_dictionary.define(".$", new nargs_reg);
8239 number_reg_dictionary.define(".br", new break_flag_reg);
8240 number_reg_dictionary.define(".C", new constant_int_reg(&compatible_flag));
8241 number_reg_dictionary.define(".O", new variable_reg(&begin_level));
8242 number_reg_dictionary.define(".c", new lineno_reg);
8243 number_reg_dictionary.define(".color", new constant_int_reg(&color_flag));
8244 number_reg_dictionary.define(".F", new filename_reg);
8245 number_reg_dictionary.define(".g", new constant_reg("1"));
8246 number_reg_dictionary.define(".H", new constant_int_reg(&hresolution));
8247 number_reg_dictionary.define(".R", new constant_reg("10000"));
8248 number_reg_dictionary.define(".U", new constant_int_reg(&unsafe_flag));
8249 number_reg_dictionary.define(".V", new constant_int_reg(&vresolution));
8250 number_reg_dictionary.define(".warn", new constant_int_reg(&warning_mask));
8251 extern const char *major_version;
8252 number_reg_dictionary.define(".x", new constant_reg(major_version));
8253 extern const char *revision;
8254 number_reg_dictionary.define(".Y", new constant_reg(revision));
8255 extern const char *minor_version;
8256 number_reg_dictionary.define(".y", new constant_reg(minor_version));
8257 number_reg_dictionary.define("c.", new writable_lineno_reg);
8258 number_reg_dictionary.define("llx", new variable_reg(&llx_reg_contents));
8259 number_reg_dictionary.define("lly", new variable_reg(&lly_reg_contents));
8260 number_reg_dictionary.define("lsn", new variable_reg(&leading_spaces_number));
8261 number_reg_dictionary.define("lss", new variable_reg(&leading_spaces_space));
8262 number_reg_dictionary.define("opmaxx",
8263 new variable_reg(&output_reg_maxx_contents));
8264 number_reg_dictionary.define("opmaxy",
8265 new variable_reg(&output_reg_maxy_contents));
8266 number_reg_dictionary.define("opminx",
8267 new variable_reg(&output_reg_minx_contents));
8268 number_reg_dictionary.define("opminy",
8269 new variable_reg(&output_reg_miny_contents));
8270 number_reg_dictionary.define("slimit",
8271 new variable_reg(&input_stack::limit));
8272 number_reg_dictionary.define("systat", new variable_reg(&system_status));
8273 number_reg_dictionary.define("urx", new variable_reg(&urx_reg_contents));
8274 number_reg_dictionary.define("ury", new variable_reg(&ury_reg_contents));
8277 object_dictionary request_dictionary(501);
8279 void init_request(const char *s, REQUEST_FUNCP f)
8281 request_dictionary.define(s, new request(f));
8284 static request_or_macro *lookup_request(symbol nm)
8286 assert(!nm.is_null());
8287 request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm);
8289 warning(WARN_MAC, "macro '%1' not defined", nm.contents());
8291 request_dictionary.define(nm, p);
8296 node *charinfo_to_node_list(charinfo *ci, const environment *envp)
8298 // Don't interpret character definitions in compatible mode.
8299 int old_compatible_flag = compatible_flag;
8300 compatible_flag = 0;
8301 int old_escape_char = escape_char;
8303 macro *mac = ci->set_macro(0);
8305 environment *oldenv = curenv;
8306 environment env(envp);
8308 curenv->set_composite();
8309 token old_tok = tok;
8310 input_stack::add_boundary();
8311 string_iterator *si =
8312 new string_iterator(*mac, "composite character", ci->nm);
8313 input_stack::push(si);
8314 // we don't use process_input_stack, because we don't want to recognise
8320 if (tok.newline()) {
8321 error("composite character mustn't contain newline");
8329 node *n = curenv->extract_output_line();
8330 input_stack::remove_boundary();
8334 compatible_flag = old_compatible_flag;
8335 escape_char = old_escape_char;
8340 static node *read_draw_node()
8344 if (!start.delimiter(1)){
8347 } while (tok != start && !tok.newline() && !tok.eof());
8352 error("missing argument");
8354 unsigned char type = tok.ch();
8356 read_color_draw_node(start);
8361 hvpair *point = new hvpair[maxpoints];
8366 for (i = 0; tok != start; i++) {
8367 if (i == maxpoints) {
8368 hvpair *oldpoint = point;
8369 point = new hvpair[maxpoints*2];
8370 for (int j = 0; j < maxpoints; j++)
8371 point[j] = oldpoint[j];
8375 if (!get_hunits(&point[i].h,
8376 type == 'f' || type == 't' ? 'u' : 'm')) {
8387 if (!get_vunits(&point[i].v, 'v')) {
8393 while (tok != start && !tok.newline() && !tok.eof())
8398 if (npoints != 1 || no_last_v) {
8399 error("two arguments needed for line");
8404 if (npoints != 1 || !no_last_v) {
8405 error("one argument needed for circle");
8411 if (npoints != 1 || no_last_v) {
8412 error("two arguments needed for ellipse");
8417 if (npoints != 2 || no_last_v) {
8418 error("four arguments needed for arc");
8424 error("even number of arguments needed for spline");
8427 if (npoints != 1 || !no_last_v) {
8428 error("one argument needed for gray shade");
8433 // silently pass it through
8436 draw_node *dn = new draw_node(type, point, npoints,
8437 curenv->get_font_size(),
8438 curenv->get_glyph_color(),
8439 curenv->get_fill_color());
8451 static void read_color_draw_node(token &start)
8455 error("missing color scheme");
8458 unsigned char scheme = tok.ch();
8461 char end = start.ch();
8464 col = read_cmy(end);
8467 col = &default_color;
8470 col = read_gray(end);
8473 col = read_cmyk(end);
8476 col = read_rgb(end);
8480 curenv->set_fill_color(col);
8481 while (tok != start) {
8482 if (tok.newline() || tok.eof()) {
8483 warning(WARN_DELIM, "missing closing delimiter");
8484 input_stack::push(make_temp_iterator("\n"));
8495 } warning_table[] = {
8496 { "char", WARN_CHAR },
8497 { "range", WARN_RANGE },
8498 { "break", WARN_BREAK },
8499 { "delim", WARN_DELIM },
8501 { "scale", WARN_SCALE },
8502 { "number", WARN_NUMBER },
8503 { "syntax", WARN_SYNTAX },
8504 { "tab", WARN_TAB },
8505 { "right-brace", WARN_RIGHT_BRACE },
8506 { "missing", WARN_MISSING },
8507 { "input", WARN_INPUT },
8508 { "escape", WARN_ESCAPE },
8509 { "space", WARN_SPACE },
8510 { "font", WARN_FONT },
8512 { "mac", WARN_MAC },
8513 { "reg", WARN_REG },
8515 { "color", WARN_COLOR },
8516 { "file", WARN_FILE },
8517 { "all", WARN_TOTAL & ~(WARN_DI | WARN_MAC | WARN_REG) },
8518 { "w", WARN_TOTAL },
8519 { "default", DEFAULT_WARNING_MASK },
8522 static int lookup_warning(const char *name)
8524 for (unsigned int i = 0;
8525 i < sizeof(warning_table)/sizeof(warning_table[0]);
8527 if (strcmp(name, warning_table[i].name) == 0)
8528 return warning_table[i].mask;
8532 static void enable_warning(const char *name)
8534 int mask = lookup_warning(name);
8536 warning_mask |= mask;
8538 error("unknown warning '%1'", name);
8541 static void disable_warning(const char *name)
8543 int mask = lookup_warning(name);
8545 warning_mask &= ~mask;
8547 error("unknown warning '%1'", name);
8550 static void copy_mode_error(const char *format,
8556 static const char prefix[] = "(in ignored input) ";
8557 char *s = new char[sizeof(prefix) + strlen(format)];
8560 warning(WARN_IG, s, arg1, arg2, arg3);
8564 error(format, arg1, arg2, arg3);
8567 enum error_type { WARNING, OUTPUT_WARNING, ERROR, FATAL };
8569 static void do_error(error_type type,
8575 const char *filename;
8577 if (inhibit_errors && type < FATAL)
8580 input_stack::backtrace();
8581 if (!get_file_line(&filename, &lineno))
8585 errprint("%1: %2:%3: ", program_name, filename, lineno);
8587 errprint("%1:%2: ", filename, lineno);
8588 else if (program_name)
8589 fprintf(stderr, "%s: ", program_name);
8592 fputs("fatal error: ", stderr);
8597 fputs("warning: ", stderr);
8599 case OUTPUT_WARNING:
8600 double fromtop = topdiv->get_vertical_position().to_units() / warn_scale;
8601 fprintf(stderr, "warning [p %d, %.1f%c",
8602 topdiv->get_page_number(), fromtop, warn_scaling_indicator);
8603 if (topdiv != curdiv) {
8604 double fromtop1 = curdiv->get_vertical_position().to_units()
8606 fprintf(stderr, ", div '%s', %.1f%c",
8607 curdiv->get_diversion_name(), fromtop1, warn_scaling_indicator);
8609 fprintf(stderr, "]: ");
8612 errprint(format, arg1, arg2, arg3);
8613 fputc('\n', stderr);
8616 cleanup_and_exit(1);
8619 int warning(warning_type t,
8625 if ((t & warning_mask) != 0) {
8626 do_error(WARNING, format, arg1, arg2, arg3);
8633 int output_warning(warning_type t,
8639 if ((t & warning_mask) != 0) {
8640 do_error(OUTPUT_WARNING, format, arg1, arg2, arg3);
8647 void error(const char *format,
8652 do_error(ERROR, format, arg1, arg2, arg3);
8655 void fatal(const char *format,
8660 do_error(FATAL, format, arg1, arg2, arg3);
8663 void fatal_with_file_and_line(const char *filename, int lineno,
8669 fprintf(stderr, "%s:%d: fatal error: ", filename, lineno);
8670 errprint(format, arg1, arg2, arg3);
8671 fputc('\n', stderr);
8673 cleanup_and_exit(1);
8676 void error_with_file_and_line(const char *filename, int lineno,
8682 fprintf(stderr, "%s:%d: error: ", filename, lineno);
8683 errprint(format, arg1, arg2, arg3);
8684 fputc('\n', stderr);
8688 dictionary charinfo_dictionary(501);
8690 charinfo *get_charinfo(symbol nm)
8692 void *p = charinfo_dictionary.lookup(nm);
8694 return (charinfo *)p;
8695 charinfo *cp = new charinfo(nm);
8696 (void)charinfo_dictionary.lookup(nm, cp);
8700 int charinfo::next_index = 0;
8702 charinfo::charinfo(symbol s)
8703 : translation(0), mac(0), special_translation(TRANSLATE_NONE),
8704 hyphenation_code(0), flags(0), ascii_code(0), asciify_code(0),
8705 not_found(0), transparent_translate(1), translate_input(0),
8706 mode(CHAR_NORMAL), nm(s)
8708 index = next_index++;
8713 int charinfo::get_unicode_code()
8715 if (ascii_code != '\0')
8717 return glyph_to_unicode(this);
8720 void charinfo::set_hyphenation_code(unsigned char c)
8722 hyphenation_code = c;
8725 void charinfo::set_translation(charinfo *ci, int tt, int ti)
8729 if (hyphenation_code != 0)
8730 ci->set_hyphenation_code(hyphenation_code);
8731 if (asciify_code != 0)
8732 ci->set_asciify_code(asciify_code);
8733 else if (ascii_code != 0)
8734 ci->set_asciify_code(ascii_code);
8735 ci->set_translation_input();
8737 special_translation = TRANSLATE_NONE;
8738 transparent_translate = tt;
8741 // Recompute flags for all entries in the charinfo dictionary.
8744 dictionary_iterator iter(charinfo_dictionary);
8747 while (iter.get(&s, (void **)&ci)) {
8748 assert(!s.is_null());
8754 // Get the union of all flags affecting this charinfo.
8755 void charinfo::get_flags()
8757 dictionary_iterator iter(char_class_dictionary);
8760 while (iter.get(&s, (void **)&ci)) {
8761 assert(!s.is_null());
8762 if (ci->contains(get_unicode_code())) {
8763 #if defined(DEBUGGING)
8765 fprintf(stderr, "charinfo::get_flags %p %s %d\n",
8766 (void *)ci, ci->nm.contents(), ci->flags);
8773 void charinfo::set_special_translation(int c, int tt)
8775 special_translation = c;
8777 transparent_translate = tt;
8780 void charinfo::set_ascii_code(unsigned char c)
8785 void charinfo::set_asciify_code(unsigned char c)
8790 macro *charinfo::set_macro(macro *m)
8797 macro *charinfo::setx_macro(macro *m, char_mode cm)
8805 void charinfo::set_number(int n)
8811 int charinfo::get_number()
8813 assert(number >= 0);
8817 bool charinfo::contains(int c, bool already_called)
8819 if (already_called) {
8820 warning(WARN_SYNTAX,
8821 "cyclic nested class detected while processing character code %1",
8825 std::vector<std::pair<int, int> >::const_iterator ranges_iter;
8826 ranges_iter = ranges.begin();
8827 while (ranges_iter != ranges.end()) {
8828 if (c >= ranges_iter->first && c <= ranges_iter->second) {
8829 #if defined(DEBUGGING)
8831 fprintf(stderr, "charinfo::contains(%d)\n", c);
8838 std::vector<charinfo *>::const_iterator nested_iter;
8839 nested_iter = nested_classes.begin();
8840 while (nested_iter != nested_classes.end()) {
8841 if ((*nested_iter)->contains(c, true))
8849 bool charinfo::contains(symbol s, bool already_called)
8851 if (already_called) {
8852 warning(WARN_SYNTAX,
8853 "cyclic nested class detected while processing symbol %1",
8857 const char *unicode = glyph_name_to_unicode(s.contents());
8858 if (unicode != NULL && strchr(unicode, '_') == NULL) {
8860 int c = (int)strtol(unicode, &ignore, 16);
8861 return contains(c, true);
8867 bool charinfo::contains(charinfo *, bool)
8873 symbol UNNAMED_SYMBOL("---");
8875 // For numbered characters not between 0 and 255, we make a symbol out
8876 // of the number and store them in this dictionary.
8878 dictionary numbered_charinfo_dictionary(11);
8880 charinfo *get_charinfo_by_number(int n)
8882 static charinfo *number_table[256];
8884 if (n >= 0 && n < 256) {
8885 charinfo *ci = number_table[n];
8887 ci = new charinfo(UNNAMED_SYMBOL);
8889 number_table[n] = ci;
8894 symbol ns(i_to_a(n));
8895 charinfo *ci = (charinfo *)numbered_charinfo_dictionary.lookup(ns);
8897 ci = new charinfo(UNNAMED_SYMBOL);
8899 (void)numbered_charinfo_dictionary.lookup(ns, ci);
8905 // This overrides the same function from libgroff; while reading font
8906 // definition files it puts single-letter glyph names into 'charset_table'
8907 // and converts glyph names of the form '\x' ('x' a single letter) into 'x'.
8908 // Consequently, symbol("x") refers to glyph name '\x', not 'x'.
8910 glyph *name_to_glyph(const char *nm)
8914 ci = charset_table[nm[0] & 0xff];
8915 else if (nm[0] == '\\' && nm[2] == 0)
8916 ci = get_charinfo(symbol(nm + 1));
8918 ci = get_charinfo(symbol(nm));
8919 return ci->as_glyph();
8922 glyph *number_to_glyph(int n)
8924 return get_charinfo_by_number(n)->as_glyph();
8927 const char *glyph_to_name(glyph *g)
8929 charinfo *ci = (charinfo *)g; // Every glyph is actually a charinfo.
8930 return (ci->nm != UNNAMED_SYMBOL ? ci->nm.contents() : NULL);