2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
25 #include "dictionary.h"
27 #include "stringclass.h"
36 #include "macropath.h"
42 // Needed for getpid() and isatty()
47 #ifdef NEED_DECLARATION_PUTENV
49 int putenv(const char *);
51 #endif /* NEED_DECLARATION_PUTENV */
53 #define MACRO_PREFIX "tmac."
54 #define MACRO_POSTFIX ".tmac"
55 #define INITIAL_STARTUP_FILE "troffrc"
56 #define FINAL_STARTUP_FILE "troffrc-end"
57 #define DEFAULT_INPUT_STACK_LIMIT 1000
59 #ifndef DEFAULT_WARNING_MASK
60 // warnings that are enabled by default
61 #define DEFAULT_WARNING_MASK \
62 (WARN_CHAR|WARN_NUMBER|WARN_BREAK|WARN_SPACE|WARN_FONT)
65 // initial size of buffer for reading names; expanded as necessary
68 extern "C" const char *program_name;
69 extern "C" const char *Version_string;
72 void init_column_requests();
75 static node *read_draw_node();
76 static void read_color_draw_node(token &);
77 static void push_token(const token &);
82 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 int compatible_flag = 0;
105 int ascii_output_flag = 0;
106 int suppress_output_flag = 0;
108 int begin_level = 0; // number of nested \O escapes
110 int have_input = 0; // whether \f, \F, \D'F...', \H, \m, \M,
111 // \R, \s, or \S has been processed in
113 int old_have_input = 0; // value of have_input right before \n
114 int tcommand_flag = 0;
115 int safer_flag = 1; // safer by default
117 int have_string_arg = 0; // whether we have \*[foo bar...]
119 double spread_limit = -3.0 - 1.0; // negative means deactivated
122 char warn_scaling_indicator;
123 int debug_state = 0; // turns on debugging of the html troff state
125 search_path *mac_path = &safer_macro_path;
127 // Defaults to the current directory.
128 search_path include_search_path(0, 0, 0, 1);
130 static int get_copy(node**, int = 0);
131 static void copy_mode_error(const char *,
132 const errarg & = empty_errarg,
133 const errarg & = empty_errarg,
134 const errarg & = empty_errarg);
136 enum read_mode { ALLOW_EMPTY, WITH_ARGS, NO_ARGS };
137 static symbol read_escape_name(read_mode mode = NO_ARGS);
138 static symbol read_long_escape_name(read_mode mode = NO_ARGS);
139 static void interpolate_string(symbol);
140 static void interpolate_string_with_args(symbol);
141 static void interpolate_macro(symbol);
142 static void interpolate_number_format(symbol);
143 static void interpolate_environment_variable(symbol);
145 static symbol composite_glyph_name(symbol);
146 static void interpolate_arg(symbol);
147 static request_or_macro *lookup_request(symbol);
148 static int get_delim_number(units *, unsigned char);
149 static int get_delim_number(units *, unsigned char, units);
150 static symbol do_get_long_name(int, char);
151 static int get_line_arg(units *res, unsigned char si, charinfo **cp);
152 static int read_size(int *);
153 static symbol get_delim_name();
154 static void init_registers();
155 static void trapping_blank_line();
157 class input_iterator;
158 input_iterator *make_temp_iterator(const char *);
159 const char *input_char_description(int);
161 void process_input_stack();
162 void chop_macro(); // declare to avoid friend name injection
165 void set_escape_char()
169 error("bad escape character");
173 escape_char = tok.ch();
186 static int saved_escape_char = '\\';
188 void save_escape_char()
190 saved_escape_char = escape_char;
194 void restore_escape_char()
196 escape_char = saved_escape_char;
200 class input_iterator {
203 input_iterator(int is_div);
204 virtual ~input_iterator() {}
206 friend class input_stack;
208 statem *diversion_state;
210 const unsigned char *ptr;
211 const unsigned char *eptr;
212 input_iterator *next;
214 virtual int fill(node **);
216 virtual int has_args() { return 0; }
217 virtual int nargs() { return 0; }
218 virtual input_iterator *get_arg(int) { return 0; }
219 virtual int get_location(int, const char **, int *) { return 0; }
220 virtual void backtrace() {}
221 virtual int set_location(const char *, int) { return 0; }
222 virtual int next_file(FILE *, const char *) { return 0; }
223 virtual void shift(int) {}
224 virtual int is_boundary() {return 0; }
225 virtual int is_file() { return 0; }
226 virtual int is_macro() { return 0; }
227 virtual void save_compatible_flag(int) {}
228 virtual int get_compatible_flag() { return 0; }
231 input_iterator::input_iterator()
232 : is_diversion(0), ptr(0), eptr(0)
236 input_iterator::input_iterator(int is_div)
237 : is_diversion(is_div), ptr(0), eptr(0)
241 int input_iterator::fill(node **)
246 int input_iterator::peek()
251 inline int input_iterator::get(node **p)
253 return ptr < eptr ? *ptr++ : fill(p);
256 class input_boundary : public input_iterator {
258 int is_boundary() { return 1; }
261 class input_return_boundary : public input_iterator {
263 int is_boundary() { return 2; }
266 class file_iterator : public input_iterator {
269 const char *filename;
273 enum { BUF_SIZE = 512 };
274 unsigned char buf[BUF_SIZE];
277 file_iterator(FILE *, const char *, int = 0);
281 int get_location(int, const char **, int *);
283 int set_location(const char *, int);
284 int next_file(FILE *, const char *);
288 file_iterator::file_iterator(FILE *f, const char *fn, int po)
289 : fp(f), lineno(1), filename(fn), popened(po),
290 newline_flag(0), seen_escape(0)
292 if ((font::use_charnames_in_special) && (fn != 0)) {
295 the_output->put_filename(fn);
299 file_iterator::~file_iterator()
304 void file_iterator::close()
308 #ifndef POPEN_MISSING
311 #endif /* not POPEN_MISSING */
316 int file_iterator::is_file()
321 int file_iterator::next_file(FILE *f, const char *s)
335 int file_iterator::fill(node **)
340 unsigned char *p = buf;
342 unsigned char *e = p + BUF_SIZE;
347 if (invalid_input_char(c))
348 warning(WARN_INPUT, "invalid input character code %1", int(c));
356 seen_escape = (c == '\\');
369 int file_iterator::peek()
372 while (invalid_input_char(c)) {
373 warning(WARN_INPUT, "invalid input character code %1", int(c));
381 int file_iterator::get_location(int /*allow_macro*/,
382 const char **filenamep, int *linenop)
385 if (filename != 0 && strcmp(filename, "-") == 0)
386 *filenamep = "<standard input>";
388 *filenamep = filename;
392 void file_iterator::backtrace()
394 errprint("%1:%2: backtrace: %3 `%1'\n", filename, lineno,
395 popened ? "process" : "file");
398 int file_iterator::set_location(const char *f, int ln)
404 the_output->put_filename(f);
410 input_iterator nil_iterator;
414 static int get(node **);
416 static void push(input_iterator *);
417 static input_iterator *get_arg(int);
419 static int get_location(int, const char **, int *);
420 static int set_location(const char *, int);
421 static void backtrace();
422 static void backtrace_all();
423 static void next_file(FILE *, const char *);
424 static void end_file();
425 static void shift(int n);
426 static void add_boundary();
427 static void add_return_boundary();
428 static int is_return_boundary();
429 static void remove_boundary();
430 static int get_level();
431 static int get_div_level();
432 static void increase_level();
433 static void decrease_level();
435 static void pop_macro();
436 static void save_compatible_flag(int);
437 static int get_compatible_flag();
438 static statem *get_diversion_state();
439 static void check_end_diversion(input_iterator *t);
441 static int div_level;
442 static statem *diversion_state;
444 static input_iterator *top;
446 static int finish_get(node **);
447 static int finish_peek();
450 input_iterator *input_stack::top = &nil_iterator;
451 int input_stack::level = 0;
452 int input_stack::limit = DEFAULT_INPUT_STACK_LIMIT;
453 int input_stack::div_level = 0;
454 statem *input_stack::diversion_state = NULL;
458 inline int input_stack::get_level()
463 inline void input_stack::increase_level()
468 inline void input_stack::decrease_level()
473 inline int input_stack::get_div_level()
478 inline int input_stack::get(node **np)
480 int res = (top->ptr < top->eptr) ? *top->ptr++ : finish_get(np);
482 old_have_input = have_input;
488 int input_stack::finish_get(node **np)
491 int c = top->fill(np);
492 if (c != EOF || top->is_boundary())
494 if (top == &nil_iterator)
496 input_iterator *tem = top;
497 check_end_diversion(tem);
498 #if defined(DEBUGGING)
500 if (tem->is_diversion)
502 "in diversion level = %d\n", input_stack::get_div_level());
507 if (top->ptr < top->eptr)
514 inline int input_stack::peek()
516 return (top->ptr < top->eptr) ? *top->ptr : finish_peek();
519 void input_stack::check_end_diversion(input_iterator *t)
521 if (t->is_diversion) {
523 diversion_state = t->diversion_state;
527 int input_stack::finish_peek()
531 if (c != EOF || top->is_boundary())
533 if (top == &nil_iterator)
535 input_iterator *tem = top;
536 check_end_diversion(tem);
540 if (top->ptr < top->eptr)
547 void input_stack::add_boundary()
549 push(new input_boundary);
552 void input_stack::add_return_boundary()
554 push(new input_return_boundary);
557 int input_stack::is_return_boundary()
559 return top->is_boundary() == 2;
562 void input_stack::remove_boundary()
564 assert(top->is_boundary());
565 input_iterator *temp = top->next;
566 check_end_diversion(top);
573 void input_stack::push(input_iterator *in)
577 if (++level > limit && limit > 0)
578 fatal("input stack limit exceeded (probable infinite loop)");
581 if (top->is_diversion) {
583 in->diversion_state = diversion_state;
584 diversion_state = curenv->construct_state(0);
585 #if defined(DEBUGGING)
587 curenv->dump_troff_state();
592 #if defined(DEBUGGING)
594 if (top->is_diversion) {
596 "in diversion level = %d\n", input_stack::get_div_level());
602 statem *get_diversion_state()
604 return input_stack::get_diversion_state();
607 statem *input_stack::get_diversion_state()
609 if (diversion_state == NULL)
612 return new statem(diversion_state);
615 input_iterator *input_stack::get_arg(int i)
618 for (p = top; p != 0; p = p->next)
620 return p->get_arg(i);
624 void input_stack::shift(int n)
626 for (input_iterator *p = top; p; p = p->next)
633 int input_stack::nargs()
635 for (input_iterator *p =top; p != 0; p = p->next)
641 int input_stack::get_location(int allow_macro, const char **filenamep, int *linenop)
643 for (input_iterator *p = top; p; p = p->next)
644 if (p->get_location(allow_macro, filenamep, linenop))
649 void input_stack::backtrace()
653 // only backtrace down to (not including) the topmost file
654 for (input_iterator *p = top;
655 p && !p->get_location(0, &f, &n);
660 void input_stack::backtrace_all()
662 for (input_iterator *p = top; p; p = p->next)
666 int input_stack::set_location(const char *filename, int lineno)
668 for (input_iterator *p = top; p; p = p->next)
669 if (p->set_location(filename, lineno))
674 void input_stack::next_file(FILE *fp, const char *s)
677 for (pp = ⊤ *pp != &nil_iterator; pp = &(*pp)->next)
678 if ((*pp)->next_file(fp, s))
680 if (++level > limit && limit > 0)
681 fatal("input stack limit exceeded");
682 *pp = new file_iterator(fp, s);
683 (*pp)->next = &nil_iterator;
686 void input_stack::end_file()
688 for (input_iterator **pp = ⊤ *pp != &nil_iterator; pp = &(*pp)->next)
689 if ((*pp)->is_file()) {
690 input_iterator *tem = *pp;
691 check_end_diversion(tem);
699 void input_stack::clear()
702 while (top != &nil_iterator) {
703 if (top->is_boundary())
705 input_iterator *tem = top;
706 check_end_diversion(tem);
711 // Keep while_request happy.
712 for (; nboundaries > 0; --nboundaries)
713 add_return_boundary();
716 void input_stack::pop_macro()
721 if (top->next == &nil_iterator)
723 if (top->is_boundary())
725 is_macro = top->is_macro();
726 input_iterator *tem = top;
727 check_end_diversion(tem);
732 // Keep while_request happy.
733 for (; nboundaries > 0; --nboundaries)
734 add_return_boundary();
737 inline void input_stack::save_compatible_flag(int f)
739 top->save_compatible_flag(f);
742 inline int input_stack::get_compatible_flag()
744 return top->get_compatible_flag();
747 void backtrace_request()
749 input_stack::backtrace_all();
756 symbol nm = get_long_name();
757 while (!tok.newline() && !tok.eof())
760 input_stack::end_file();
763 FILE *fp = include_search_path.open_file_cautious(nm.contents());
765 error("can't open `%1': %2", nm.contents(), strerror(errno));
767 input_stack::next_file(fp, nm.contents());
775 if (!has_arg() || !get_integer(&n))
777 input_stack::shift(n);
781 static char get_char_for_escape_name(int allow_space = 0)
786 copy_mode_error("end of input in escape name");
789 if (!invalid_input_char(c))
794 input_stack::push(make_temp_iterator("\n"));
797 if (c == ' ' && allow_space)
803 copy_mode_error("%1 is not allowed in an escape name",
804 input_char_description(c));
810 static symbol read_two_char_escape_name()
813 buf[0] = get_char_for_escape_name();
814 if (buf[0] != '\0') {
815 buf[1] = get_char_for_escape_name();
824 static symbol read_long_escape_name(read_mode mode)
826 int start_level = input_stack::get_level();
827 char abuf[ABUF_SIZE];
829 int buf_size = ABUF_SIZE;
834 c = get_char_for_escape_name(have_char && mode == WITH_ARGS);
841 if (mode == WITH_ARGS && c == ' ')
843 if (i + 2 > buf_size) {
845 buf = new char[ABUF_SIZE*2];
846 memcpy(buf, abuf, buf_size);
847 buf_size = ABUF_SIZE*2;
851 buf = new char[buf_size*2];
852 memcpy(buf, old_buf, buf_size);
857 if (c == ']' && input_stack::get_level() == start_level)
866 if (mode != ALLOW_EMPTY)
867 copy_mode_error("empty escape name");
879 static symbol read_escape_name(read_mode mode)
881 char c = get_char_for_escape_name();
885 return read_two_char_escape_name();
886 if (c == '[' && !compatible_flag)
887 return read_long_escape_name(mode);
894 static symbol read_increment_and_escape_name(int *incp)
896 char c = get_char_for_escape_name();
903 return read_two_char_escape_name();
906 return read_escape_name();
909 return read_escape_name();
911 if (!compatible_flag) {
913 return read_long_escape_name();
924 static int get_copy(node **nd, int defining)
927 int c = input_stack::get(nd);
928 if (c == PUSH_GROFF_MODE) {
929 input_stack::save_compatible_flag(compatible_flag);
933 if (c == PUSH_COMP_MODE) {
934 input_stack::save_compatible_flag(compatible_flag);
938 if (c == POP_GROFFCOMP_MODE) {
939 compatible_flag = input_stack::get_compatible_flag();
942 if (c == BEGIN_QUOTE) {
943 input_stack::increase_level();
946 if (c == END_QUOTE) {
947 input_stack::decrease_level();
950 if (c == ESCAPE_NEWLINE) {
954 c = input_stack::get(nd);
955 } while (c == ESCAPE_NEWLINE);
957 if (c != escape_char || escape_char <= 0)
959 c = input_stack::peek();
964 (void)input_stack::get(0);
965 while ((c = input_stack::get(0)) != '\n' && c != EOF)
968 case '#': // Like \" but newline is ignored.
969 (void)input_stack::get(0);
970 while ((c = input_stack::get(0)) != '\n')
976 (void)input_stack::get(0);
977 symbol s = read_escape_name();
978 if (!(s.is_null() || s.is_empty()))
984 (void)input_stack::get(0);
985 symbol s = read_escape_name(WITH_ARGS);
986 if (!(s.is_null() || s.is_empty())) {
987 if (have_string_arg) {
989 interpolate_string_with_args(s);
992 interpolate_string(s);
997 (void)input_stack::get(0);
1000 (void)input_stack::get(0);
1003 (void)input_stack::get(0);
1007 (void)input_stack::get(0);
1009 symbol s = read_increment_and_escape_name(&inc);
1010 if (!(s.is_null() || s.is_empty()))
1011 interpolate_number_reg(s, inc);
1016 (void)input_stack::get(0);
1017 symbol s = read_escape_name();
1018 if (!(s.is_null() || s.is_empty()))
1019 interpolate_number_format(s);
1023 (void)input_stack::get(0);
1027 (void)input_stack::get(0);
1028 symbol s = read_escape_name();
1029 if (!(s.is_null() || s.is_empty()))
1030 interpolate_environment_variable(s);
1034 (void)input_stack::get(0);
1036 return ESCAPE_NEWLINE;
1039 (void)input_stack::get(0);
1040 return ESCAPE_SPACE;
1042 (void)input_stack::get(0);
1043 return ESCAPE_TILDE;
1045 (void)input_stack::get(0);
1046 return ESCAPE_COLON;
1048 (void)input_stack::get(0);
1051 (void)input_stack::get(0);
1052 return ESCAPE_CIRCUMFLEX;
1054 (void)input_stack::get(0);
1055 return ESCAPE_LEFT_BRACE;
1057 (void)input_stack::get(0);
1058 return ESCAPE_RIGHT_BRACE;
1060 (void)input_stack::get(0);
1061 return ESCAPE_LEFT_QUOTE;
1063 (void)input_stack::get(0);
1064 return ESCAPE_RIGHT_QUOTE;
1066 (void)input_stack::get(0);
1067 return ESCAPE_HYPHEN;
1069 (void)input_stack::get(0);
1070 return ESCAPE_UNDERSCORE;
1072 (void)input_stack::get(0);
1075 (void)input_stack::get(0);
1078 (void)input_stack::get(0);
1079 return ESCAPE_QUESTION;
1081 (void)input_stack::get(0);
1082 return ESCAPE_AMPERSAND;
1084 (void)input_stack::get(0);
1085 return ESCAPE_RIGHT_PARENTHESIS;
1087 (void)input_stack::get(0);
1090 (void)input_stack::get(0);
1091 return ESCAPE_PERCENT;
1093 if (c == escape_char) {
1094 (void)input_stack::get(0);
1103 class non_interpreted_char_node : public node {
1106 non_interpreted_char_node(unsigned char);
1108 int interpret(macro *);
1115 int non_interpreted_char_node::same(node *nd)
1117 return c == ((non_interpreted_char_node *)nd)->c;
1120 const char *non_interpreted_char_node::type()
1122 return "non_interpreted_char_node";
1125 int non_interpreted_char_node::force_tprint()
1130 int non_interpreted_char_node::is_tag()
1135 non_interpreted_char_node::non_interpreted_char_node(unsigned char n) : c(n)
1140 node *non_interpreted_char_node::copy()
1142 return new non_interpreted_char_node(c);
1145 int non_interpreted_char_node::interpret(macro *mac)
1151 static void do_width();
1152 static node *do_non_interpreted();
1153 static node *do_special();
1154 static node *do_suppress(symbol nm);
1155 static void do_register();
1157 dictionary color_dictionary(501);
1159 static color *lookup_color(symbol nm)
1161 assert(!nm.is_null());
1162 if (nm == default_symbol)
1163 return &default_color;
1164 color *c = (color *)color_dictionary.lookup(nm);
1166 warning(WARN_COLOR, "color `%1' not defined", nm.contents());
1170 void do_glyph_color(symbol nm)
1175 curenv->set_glyph_color(curenv->get_prev_glyph_color());
1177 color *tem = lookup_color(nm);
1179 curenv->set_glyph_color(tem);
1181 (void)color_dictionary.lookup(nm, new color(nm));
1185 void do_fill_color(symbol nm)
1190 curenv->set_fill_color(curenv->get_prev_fill_color());
1192 color *tem = lookup_color(nm);
1194 curenv->set_fill_color(tem);
1196 (void)color_dictionary.lookup(nm, new color(nm));
1200 static unsigned int get_color_element(const char *scheme, const char *col)
1203 if (!get_number(&val, 'f')) {
1204 warning(WARN_COLOR, "%1 in %2 definition set to 0", col, scheme);
1209 warning(WARN_RANGE, "%1 cannot be negative: set to 0", col);
1212 if (val > color::MAX_COLOR_VAL+1) {
1213 warning(WARN_RANGE, "%1 cannot be greater than 1", col);
1214 // we change 0x10000 to 0xffff
1215 return color::MAX_COLOR_VAL;
1217 return (unsigned int)val;
1220 static color *read_rgb(char end = 0)
1222 symbol component = do_get_long_name(0, end);
1223 if (component.is_null()) {
1224 warning(WARN_COLOR, "missing rgb color values");
1227 const char *s = component.contents();
1228 color *col = new color;
1230 if (!col->read_rgb(s)) {
1231 warning(WARN_COLOR, "expecting rgb color definition not `%1'", s);
1238 input_stack::push(make_temp_iterator(" "));
1239 input_stack::push(make_temp_iterator(s));
1241 unsigned int r = get_color_element("rgb color", "red component");
1242 unsigned int g = get_color_element("rgb color", "green component");
1243 unsigned int b = get_color_element("rgb color", "blue component");
1244 col->set_rgb(r, g, b);
1249 static color *read_cmy(char end = 0)
1251 symbol component = do_get_long_name(0, end);
1252 if (component.is_null()) {
1253 warning(WARN_COLOR, "missing cmy color values");
1256 const char *s = component.contents();
1257 color *col = new color;
1259 if (!col->read_cmy(s)) {
1260 warning(WARN_COLOR, "expecting cmy color definition not `%1'", s);
1267 input_stack::push(make_temp_iterator(" "));
1268 input_stack::push(make_temp_iterator(s));
1270 unsigned int c = get_color_element("cmy color", "cyan component");
1271 unsigned int m = get_color_element("cmy color", "magenta component");
1272 unsigned int y = get_color_element("cmy color", "yellow component");
1273 col->set_cmy(c, m, y);
1278 static color *read_cmyk(char end = 0)
1280 symbol component = do_get_long_name(0, end);
1281 if (component.is_null()) {
1282 warning(WARN_COLOR, "missing cmyk color values");
1285 const char *s = component.contents();
1286 color *col = new color;
1288 if (!col->read_cmyk(s)) {
1289 warning(WARN_COLOR, "`expecting a cmyk color definition not `%1'", s);
1296 input_stack::push(make_temp_iterator(" "));
1297 input_stack::push(make_temp_iterator(s));
1299 unsigned int c = get_color_element("cmyk color", "cyan component");
1300 unsigned int m = get_color_element("cmyk color", "magenta component");
1301 unsigned int y = get_color_element("cmyk color", "yellow component");
1302 unsigned int k = get_color_element("cmyk color", "black component");
1303 col->set_cmyk(c, m, y, k);
1308 static color *read_gray(char end = 0)
1310 symbol component = do_get_long_name(0, end);
1311 if (component.is_null()) {
1312 warning(WARN_COLOR, "missing gray values");
1315 const char *s = component.contents();
1316 color *col = new color;
1318 if (!col->read_gray(s)) {
1319 warning(WARN_COLOR, "`expecting a gray definition not `%1'", s);
1326 input_stack::push(make_temp_iterator("\n"));
1327 input_stack::push(make_temp_iterator(s));
1329 unsigned int g = get_color_element("gray", "gray value");
1335 static void activate_color()
1338 if (has_arg() && get_integer(&n))
1339 color_flag = n != 0;
1345 static void define_color()
1347 symbol color_name = get_long_name(1);
1348 if (color_name.is_null()) {
1352 if (color_name == default_symbol) {
1353 warning(WARN_COLOR, "default color can't be redefined");
1357 symbol style = get_long_name(1);
1358 if (style.is_null()) {
1363 if (strcmp(style.contents(), "rgb") == 0)
1365 else if (strcmp(style.contents(), "cmyk") == 0)
1367 else if (strcmp(style.contents(), "gray") == 0)
1369 else if (strcmp(style.contents(), "grey") == 0)
1371 else if (strcmp(style.contents(), "cmy") == 0)
1375 "unknown color space `%1'; use rgb, cmyk, gray or cmy",
1381 col->nm = color_name;
1382 (void)color_dictionary.lookup(color_name, col);
1387 static node *do_overstrike()
1390 overstrike_node *on = new overstrike_node;
1391 int start_level = input_stack::get_level();
1395 if (tok.newline() || tok.eof()) {
1396 warning(WARN_DELIM, "missing closing delimiter");
1397 input_stack::push(make_temp_iterator("\n"));
1401 && (compatible_flag || input_stack::get_level() == start_level))
1403 charinfo *ci = tok.get_char(1);
1405 node *n = curenv->make_char_node(ci);
1413 static node *do_bracket()
1416 bracket_node *bn = new bracket_node;
1418 int start_level = input_stack::get_level();
1422 warning(WARN_DELIM, "missing closing delimiter");
1425 if (tok.newline()) {
1426 warning(WARN_DELIM, "missing closing delimiter");
1427 input_stack::push(make_temp_iterator("\n"));
1431 && (compatible_flag || input_stack::get_level() == start_level))
1433 charinfo *ci = tok.get_char(1);
1435 node *n = curenv->make_char_node(ci);
1443 static int do_name_test()
1447 int start_level = input_stack::get_level();
1452 if (tok.newline() || tok.eof()) {
1453 warning(WARN_DELIM, "missing closing delimiter");
1454 input_stack::push(make_temp_iterator("\n"));
1458 && (compatible_flag || input_stack::get_level() == start_level))
1464 return some_char && !bad_char;
1467 static int do_expr_test()
1471 int start_level = input_stack::get_level();
1472 if (!start.delimiter(1))
1475 // disable all warning and error messages temporarily
1476 int saved_warning_mask = warning_mask;
1477 int saved_inhibit_errors = inhibit_errors;
1481 int result = get_number_rigidly(&dummy, 'u');
1482 warning_mask = saved_warning_mask;
1483 inhibit_errors = saved_inhibit_errors;
1484 if (tok == start && input_stack::get_level() == start_level)
1486 // ignore everything up to the delimiter in case we aren't right there
1489 if (tok.newline() || tok.eof()) {
1490 warning(WARN_DELIM, "missing closing delimiter");
1491 input_stack::push(make_temp_iterator("\n"));
1494 if (tok == start && input_stack::get_level() == start_level)
1501 static node *do_zero_width()
1505 int start_level = input_stack::get_level();
1506 environment env(curenv);
1507 environment *oldenv = curenv;
1511 if (tok.newline() || tok.eof()) {
1512 error("missing closing delimiter");
1516 && (compatible_flag || input_stack::get_level() == start_level))
1521 node *rev = env.extract_output_line();
1529 return new zero_width_node(n);
1534 // It's undesirable for \Z to change environments, because then
1535 // \n(.w won't work as expected.
1537 static node *do_zero_width()
1539 node *rev = new dummy_node;
1542 int start_level = input_stack::get_level();
1545 if (tok.newline() || tok.eof()) {
1546 warning(WARN_DELIM, "missing closing delimiter");
1547 input_stack::push(make_temp_iterator("\n"));
1551 && (compatible_flag || input_stack::get_level() == start_level))
1553 if (!tok.add_to_node_list(&rev))
1554 error("invalid token in argument to \\Z");
1563 return new zero_width_node(n);
1568 token_node *node::get_token_node()
1573 class token_node : public node {
1576 token_node(const token &t);
1578 token_node *get_token_node();
1585 token_node::token_node(const token &t) : tk(t)
1589 node *token_node::copy()
1591 return new token_node(tk);
1594 token_node *token_node::get_token_node()
1599 int token_node::same(node *nd)
1601 return tk == ((token_node *)nd)->tk;
1604 const char *token_node::type()
1606 return "token_node";
1609 int token_node::force_tprint()
1614 int token_node::is_tag()
1619 token::token() : nd(0), type(TOKEN_EMPTY)
1628 token::token(const token &t)
1629 : nm(t.nm), c(t.c), val(t.val), dim(t.dim), type(t.type)
1631 // Use two statements to work around bug in SGI C++.
1633 nd = tem ? tem->copy() : 0;
1636 void token::operator=(const token &t)
1640 // Use two statements to work around bug in SGI C++.
1642 nd = tem ? tem->copy() : 0;
1659 return !tok.newline();
1662 void token::make_space()
1667 void token::make_newline()
1669 type = TOKEN_NEWLINE;
1681 int cc = input_stack::get(&n);
1682 if (cc != escape_char || escape_char == 0) {
1685 case PUSH_GROFF_MODE:
1686 input_stack::save_compatible_flag(compatible_flag);
1687 compatible_flag = 0;
1689 case PUSH_COMP_MODE:
1690 input_stack::save_compatible_flag(compatible_flag);
1691 compatible_flag = 1;
1693 case POP_GROFFCOMP_MODE:
1694 compatible_flag = input_stack::get_compatible_flag();
1697 input_stack::increase_level();
1700 input_stack::decrease_level();
1705 case TRANSPARENT_FILE_REQUEST:
1707 case COPY_FILE_REQUEST:
1709 case VJUSTIFY_REQUEST:
1711 type = TOKEN_REQUEST;
1715 type = TOKEN_BEGIN_TRAP;
1718 type = TOKEN_END_TRAP;
1720 case LAST_PAGE_EJECTOR:
1721 seen_last_page_ejector = 1;
1724 type = TOKEN_PAGE_EJECTOR;
1726 case ESCAPE_PERCENT:
1728 type = TOKEN_HYPHEN_INDICATOR;
1732 type = TOKEN_UNSTRETCHABLE_SPACE;
1736 type = TOKEN_STRETCHABLE_SPACE;
1740 type = TOKEN_ZERO_WIDTH_BREAK;
1744 type = TOKEN_ESCAPE;
1747 goto handle_escape_char;
1751 nd = new hmotion_node(curenv->get_narrow_space_width(),
1752 curenv->get_fill_color());
1754 case ESCAPE_CIRCUMFLEX:
1757 nd = new hmotion_node(curenv->get_half_narrow_space_width(),
1758 curenv->get_fill_color());
1760 case ESCAPE_NEWLINE:
1763 case ESCAPE_LEFT_BRACE:
1765 type = TOKEN_LEFT_BRACE;
1767 case ESCAPE_RIGHT_BRACE:
1769 type = TOKEN_RIGHT_BRACE;
1771 case ESCAPE_LEFT_QUOTE:
1773 type = TOKEN_SPECIAL;
1776 case ESCAPE_RIGHT_QUOTE:
1778 type = TOKEN_SPECIAL;
1783 type = TOKEN_SPECIAL;
1786 case ESCAPE_UNDERSCORE:
1788 type = TOKEN_SPECIAL;
1793 type = TOKEN_INTERRUPT;
1797 type = TOKEN_TRANSPARENT;
1799 case ESCAPE_QUESTION:
1801 nd = do_non_interpreted();
1807 case ESCAPE_AMPERSAND:
1811 case ESCAPE_RIGHT_PARENTHESIS:
1812 ESCAPE_RIGHT_PARENTHESIS:
1813 type = TOKEN_TRANSPARENT_DUMMY;
1816 type = TOKEN_BACKSPACE;
1825 type = TOKEN_NEWLINE;
1828 type = TOKEN_LEADER;
1833 token_node *tn = n->get_token_node();
1852 cc = input_stack::get(&n);
1855 nm = read_two_char_escape_name();
1856 type = TOKEN_SPECIAL;
1860 error("end of input after escape character");
1863 goto ESCAPE_LEFT_QUOTE;
1865 goto ESCAPE_RIGHT_QUOTE;
1869 goto ESCAPE_UNDERSCORE;
1871 goto ESCAPE_PERCENT;
1875 nd = new hmotion_node(curenv->get_digit_width(),
1876 curenv->get_fill_color());
1882 goto ESCAPE_CIRCUMFLEX;
1884 type = TOKEN_ITALIC_CORRECTION;
1888 nd = new left_italic_corrected_node;
1891 goto ESCAPE_AMPERSAND;
1893 goto ESCAPE_RIGHT_PARENTHESIS;
1897 goto ESCAPE_QUESTION;
1903 while ((cc = input_stack::get(0)) != '\n' && cc != EOF)
1906 type = TOKEN_NEWLINE;
1910 case '#': // Like \" but newline is ignored.
1911 while ((cc = input_stack::get(0)) != '\n')
1919 symbol s = read_escape_name();
1920 if (!(s.is_null() || s.is_empty()))
1926 symbol s = read_escape_name(WITH_ARGS);
1927 if (!(s.is_null() || s.is_empty())) {
1928 if (have_string_arg) {
1929 have_string_arg = 0;
1930 interpolate_string_with_args(s);
1933 interpolate_string(s);
1938 nd = new non_interpreted_char_node('\001');
1942 c = '0' + do_name_test();
1950 c = '0' + do_expr_test();
1956 nm = get_delim_name();
1959 type = TOKEN_SPECIAL;
1963 nd = new vmotion_node(curenv->get_size() / 2,
1964 curenv->get_fill_color());
1967 nd = read_draw_node();
1975 goto handle_escape_char;
1978 symbol s = read_escape_name(ALLOW_EMPTY);
1982 for (p = s.contents(); *p != '\0'; p++)
1985 if (*p || s.is_empty())
1986 curenv->set_font(s);
1988 curenv->set_font(atoi(s.contents()));
1989 if (!compatible_flag)
1995 symbol s = read_escape_name(ALLOW_EMPTY);
1998 curenv->set_family(s);
2004 symbol s = read_escape_name();
2005 if (!(s.is_null() || s.is_empty()))
2006 interpolate_number_format(s);
2010 if (!get_delim_number(&x, 'm'))
2013 nd = new hmotion_node(x, curenv->get_fill_color());
2016 // don't take height increments relative to previous height if
2017 // in compatibility mode
2018 if (!compatible_flag && curenv->get_char_height())
2020 if (get_delim_number(&x, 'z', curenv->get_char_height()))
2021 curenv->set_char_height(x);
2025 if (get_delim_number(&x, 'z', curenv->get_requested_point_size()))
2026 curenv->set_char_height(x);
2028 if (!compatible_flag)
2032 nm = read_escape_name();
2033 if (nm.is_null() || nm.is_empty())
2035 type = TOKEN_MARK_INPUT;
2041 if (!get_line_arg(&x, (cc == 'l' ? 'm': 'v'), &s))
2044 s = get_charinfo(cc == 'l' ? "ru" : "br");
2046 node *char_node = curenv->make_char_node(s);
2048 nd = new hline_node(x, char_node);
2050 nd = new vline_node(x, char_node);
2054 do_glyph_color(read_escape_name(ALLOW_EMPTY));
2055 if (!compatible_flag)
2059 do_fill_color(read_escape_name(ALLOW_EMPTY));
2060 if (!compatible_flag)
2066 symbol s = read_increment_and_escape_name(&inc);
2067 if (!(s.is_null() || s.is_empty()))
2068 interpolate_number_reg(s, inc);
2072 if (!get_delim_number(&val, 0))
2074 type = TOKEN_NUMBERED_CHAR;
2077 nd = do_overstrike();
2081 nd = do_suppress(read_escape_name());
2087 type = TOKEN_SPREAD;
2091 nd = new vmotion_node(-curenv->get_size(), curenv->get_fill_color());
2095 if (!compatible_flag)
2100 curenv->set_size(x);
2101 if (!compatible_flag)
2105 if (get_delim_number(&x, 0))
2106 curenv->set_char_slant(x);
2107 if (!compatible_flag)
2112 nd = new non_interpreted_char_node('\t');
2116 nd = new vmotion_node(-curenv->get_size() / 2,
2117 curenv->get_fill_color());
2120 if (!get_delim_number(&x, 'v'))
2123 nd = new vmotion_node(x, curenv->get_fill_color());
2127 symbol s = read_escape_name();
2128 if (!(s.is_null() || s.is_empty()))
2129 interpolate_environment_variable(s);
2136 if (!get_delim_number(&x, 'v'))
2139 nd = new extra_size_node(x);
2149 symbol s = read_escape_name();
2150 if (s.is_null() || s.is_empty())
2152 request_or_macro *p = lookup_request(s);
2153 macro *m = p->to_macro();
2155 error("can't transparently throughput a request");
2158 nd = new special_node(*m);
2165 if (type == TOKEN_NODE)
2166 nd = new zero_width_node(nd);
2168 charinfo *ci = get_char(1);
2171 node *gn = curenv->make_char_node(ci);
2174 nd = new zero_width_node(gn);
2180 nd = do_zero_width();
2186 goto ESCAPE_LEFT_BRACE;
2188 goto ESCAPE_RIGHT_BRACE;
2192 if (!compatible_flag) {
2193 symbol s = read_long_escape_name(WITH_ARGS);
2194 if (s.is_null() || s.is_empty())
2196 if (have_string_arg) {
2197 have_string_arg = 0;
2198 nm = composite_glyph_name(s);
2201 const char *gn = check_unicode_name(s.contents());
2203 const char *gn_decomposed = decompose_unicode(gn);
2205 gn = &gn_decomposed[1];
2206 const char *groff_gn = unicode_to_glyph_name(gn);
2208 nm = symbol(groff_gn);
2210 char *buf = new char[strlen(gn) + 1 + 1];
2218 nm = symbol(s.contents());
2220 type = TOKEN_SPECIAL;
2223 goto handle_normal_char;
2225 if (cc != escape_char && cc != '.')
2226 warning(WARN_ESCAPE, "escape character ignored before %1",
2227 input_char_description(cc));
2228 goto handle_normal_char;
2234 int token::operator==(const token &t)
2243 case TOKEN_NUMBERED_CHAR:
2244 return val == t.val;
2250 int token::operator!=(const token &t)
2252 return !(*this == t);
2255 // is token a suitable delimiter (like ')?
2257 int token::delimiter(int err)
2286 error("cannot use character `%1' as a starting delimiter", char(c));
2293 case TOKEN_STRETCHABLE_SPACE:
2294 case TOKEN_UNSTRETCHABLE_SPACE:
2298 error("cannot use %1 as a starting delimiter", description());
2305 const char *token::description()
2309 case TOKEN_BACKSPACE:
2310 return "a backspace character";
2321 case TOKEN_HYPHEN_INDICATOR:
2323 case TOKEN_INTERRUPT:
2325 case TOKEN_ITALIC_CORRECTION:
2328 return "a leader character";
2329 case TOKEN_LEFT_BRACE:
2331 case TOKEN_MARK_INPUT:
2337 case TOKEN_NUMBERED_CHAR:
2339 case TOKEN_RIGHT_BRACE:
2344 return "a special character";
2347 case TOKEN_STRETCHABLE_SPACE:
2349 case TOKEN_UNSTRETCHABLE_SPACE:
2352 return "a tab character";
2353 case TOKEN_TRANSPARENT:
2355 case TOKEN_TRANSPARENT_DUMMY:
2357 case TOKEN_ZERO_WIDTH_BREAK:
2360 return "end of input";
2364 return "a magic token";
2369 while (!tok.newline())
2380 if (has_arg() && get_integer(&n))
2381 compatible_flag = n != 0;
2383 compatible_flag = 1;
2387 static void empty_name_warning(int required)
2389 if (tok.newline() || tok.eof()) {
2391 warning(WARN_MISSING, "missing name");
2393 else if (tok.right_brace() || tok.tab()) {
2394 const char *start = tok.description();
2397 } while (tok.space() || tok.right_brace() || tok.tab());
2398 if (!tok.newline() && !tok.eof())
2399 error("%1 is not allowed before an argument", start);
2401 warning(WARN_MISSING, "missing name");
2404 error("name expected (got %1)", tok.description());
2406 error("name expected (got %1): treated as missing", tok.description());
2409 static void non_empty_name_warning()
2411 if (!tok.newline() && !tok.eof() && !tok.space() && !tok.tab()
2412 && !tok.right_brace()
2413 // We don't want to give a warning for .el\{
2414 && !tok.left_brace())
2415 error("%1 is not allowed in a name", tok.description());
2418 symbol get_name(int required)
2420 if (compatible_flag) {
2423 if ((buf[0] = tok.ch()) != 0) {
2425 if ((buf[1] = tok.ch()) != 0) {
2430 non_empty_name_warning();
2434 empty_name_warning(required);
2439 return get_long_name(required);
2442 symbol get_long_name(int required)
2444 return do_get_long_name(required, 0);
2447 static symbol do_get_long_name(int required, char end)
2451 char abuf[ABUF_SIZE];
2453 int buf_size = ABUF_SIZE;
2456 // If end != 0 we normally have to append a null byte
2457 if (i + 2 > buf_size) {
2459 buf = new char[ABUF_SIZE*2];
2460 memcpy(buf, abuf, buf_size);
2461 buf_size = ABUF_SIZE*2;
2464 char *old_buf = buf;
2465 buf = new char[buf_size*2];
2466 memcpy(buf, old_buf, buf_size);
2471 if ((buf[i] = tok.ch()) == 0 || buf[i] == end)
2477 empty_name_warning(required);
2480 if (end && buf[i] == end)
2483 non_empty_name_warning();
2496 topdiv->set_last_page();
2497 if (!end_macro_name.is_null()) {
2498 spring_trap(end_macro_name);
2500 process_input_stack();
2502 curenv->final_break();
2504 process_input_stack();
2506 if (topdiv->get_page_length() > 0) {
2508 topdiv->set_ejecting();
2509 static unsigned char buf[2] = { LAST_PAGE_EJECTOR, '\0' };
2510 input_stack::push(make_temp_iterator((char *)buf));
2511 topdiv->space(topdiv->get_page_length(), 1);
2513 process_input_stack();
2514 seen_last_page_ejector = 1; // should be set already
2515 topdiv->set_ejecting();
2516 push_page_ejector();
2517 topdiv->space(topdiv->get_page_length(), 1);
2519 process_input_stack();
2521 // This will only happen if a trap-invoked macro starts a diversion,
2522 // or if vertical position traps have been disabled.
2523 cleanup_and_exit(0);
2526 // This implements .ex. The input stack must be cleared before calling
2531 input_stack::clear();
2538 void return_macro_request()
2540 if (has_arg() && tok.ch())
2541 input_stack::pop_macro();
2542 input_stack::pop_macro();
2548 end_macro_name = get_name();
2552 void blank_line_macro()
2554 blank_line_macro_name = get_name();
2558 static void trapping_blank_line()
2560 if (!blank_line_macro_name.is_null())
2561 spring_trap(blank_line_macro_name);
2568 int old_compatible_flag = compatible_flag;
2569 compatible_flag = 0;
2570 symbol nm = get_name();
2574 interpolate_macro(nm);
2575 compatible_flag = old_compatible_flag;
2578 inline int possibly_handle_first_page_transition()
2580 if (topdiv->before_first_page && curdiv == topdiv && !curenv->is_dummy()) {
2581 handle_first_page_transition();
2588 static int transparent_translate(int cc)
2590 if (!invalid_input_char(cc)) {
2591 charinfo *ci = charset_table[cc];
2592 switch (ci->get_special_translation(1)) {
2593 case charinfo::TRANSLATE_SPACE:
2595 case charinfo::TRANSLATE_STRETCHABLE_SPACE:
2596 return ESCAPE_TILDE;
2597 case charinfo::TRANSLATE_DUMMY:
2598 return ESCAPE_AMPERSAND;
2599 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
2600 return ESCAPE_PERCENT;
2602 // This is really ugly.
2603 ci = ci->get_translation(1);
2605 int c = ci->get_ascii_code();
2608 error("can't translate %1 to special character `%2'"
2609 " in transparent throughput",
2610 input_char_description(cc),
2618 struct int_stack_element {
2620 int_stack_element *next;
2630 int_stack::int_stack()
2635 int_stack::~int_stack()
2638 int_stack_element *temp = top;
2644 int int_stack::is_empty()
2649 void int_stack::push(int n)
2651 int_stack_element *p = new int_stack_element;
2657 int int_stack::pop()
2660 int_stack_element *p = top;
2667 int node::reread(int *)
2672 int global_diverted_space = 0;
2674 int diverted_space_node::reread(int *bolp)
2676 global_diverted_space = 1;
2677 if (curenv->get_fill())
2678 trapping_blank_line();
2681 global_diverted_space = 0;
2686 int diverted_copy_file_node::reread(int *bolp)
2688 curdiv->copy_file(filename.contents());
2693 int word_space_node::reread(int *)
2696 for (width_list *w = orig_width; w; w = w->next)
2697 curenv->space(w->width, w->sentence_width);
2704 int unbreakable_space_node::reread(int *)
2709 int hmotion_node::reread(int *)
2711 if (unformat && was_tab) {
2712 curenv->handle_tab(0);
2719 void process_input_stack()
2721 int_stack trap_bol_stack;
2724 int suppress_next = 0;
2726 case token::TOKEN_CHAR:
2728 unsigned char ch = tok.c;
2729 if (bol && !have_input
2730 && (ch == curenv->control_char
2731 || ch == curenv->no_break_control_char)) {
2732 break_flag = ch == curenv->control_char;
2733 // skip tabs as well as spaces here
2736 } while (tok.white_space());
2737 symbol nm = get_name();
2738 #if defined(DEBUGGING)
2740 if (! nm.is_null()) {
2741 if (strcmp(nm.contents(), "test") == 0) {
2742 fprintf(stderr, "found it!\n");
2745 fprintf(stderr, "interpreting [%s]", nm.contents());
2746 if (strcmp(nm.contents(), "di") == 0 && topdiv != curdiv)
2747 fprintf(stderr, " currently in diversion: %s",
2748 curdiv->get_diversion_name());
2749 fprintf(stderr, "\n");
2757 interpolate_macro(nm);
2758 #if defined(DEBUGGING)
2760 fprintf(stderr, "finished interpreting [%s] and environment state is\n", nm.contents());
2761 curenv->dump_troff_state();
2768 if (possibly_handle_first_page_transition())
2772 #if defined(DEBUGGING)
2774 fprintf(stderr, "found [%c]\n", ch); fflush(stderr);
2777 curenv->add_char(charset_table[ch]);
2779 if (tok.type != token::TOKEN_CHAR)
2789 case token::TOKEN_TRANSPARENT:
2792 if (possibly_handle_first_page_transition())
2801 curdiv->transparent_output(transparent_translate(cc));
2803 curdiv->transparent_output(n);
2804 } while (cc != '\n' && cc != EOF);
2806 curdiv->transparent_output('\n');
2811 case token::TOKEN_NEWLINE:
2813 if (bol && !old_have_input
2814 && !curenv->get_prev_line_interrupted())
2815 trapping_blank_line();
2822 case token::TOKEN_REQUEST:
2824 int request_code = tok.c;
2826 switch (request_code) {
2830 case COPY_FILE_REQUEST:
2833 case TRANSPARENT_FILE_REQUEST:
2837 case VJUSTIFY_REQUEST:
2848 case token::TOKEN_SPACE:
2850 if (possibly_handle_first_page_transition())
2852 else if (bol && !curenv->get_prev_line_interrupted()) {
2854 // save space_width now so that it isn't changed by \f or \s
2855 // which we wouldn't notice here
2856 hunits space_width = curenv->get_space_width();
2858 nspaces += tok.nspaces();
2860 } while (tok.space());
2862 trapping_blank_line();
2866 curenv->add_node(new hmotion_node(space_width * nspaces,
2867 curenv->get_fill_color()));
2877 case token::TOKEN_EOF:
2879 case token::TOKEN_NODE:
2881 if (possibly_handle_first_page_transition())
2883 else if (tok.nd->reread(&bol)) {
2888 curenv->add_node(tok.nd);
2891 curenv->possibly_break_line(1);
2895 case token::TOKEN_PAGE_EJECTOR:
2897 continue_page_eject();
2898 // I think we just want to preserve bol.
2902 case token::TOKEN_BEGIN_TRAP:
2904 trap_bol_stack.push(bol);
2909 case token::TOKEN_END_TRAP:
2911 if (trap_bol_stack.is_empty())
2912 error("spurious end trap token detected!");
2914 bol = trap_bol_stack.pop();
2917 /* I'm not totally happy about this. But I can't think of any other
2918 way to do it. Doing an output_pending_lines() whenever a
2919 TOKEN_END_TRAP is detected doesn't work: for example,
2932 a\%very\%very\%long\%word
2934 will print all but the first lines from the word immediately
2935 after the footer, rather than on the next page. */
2937 if (trap_bol_stack.is_empty())
2938 curenv->output_pending_lines();
2950 trap_sprung_flag = 0;
2954 #ifdef WIDOW_CONTROL
2956 void flush_pending_lines()
2958 while (!tok.newline() && !tok.eof())
2960 curenv->output_pending_lines();
2964 #endif /* WIDOW_CONTROL */
2966 request_or_macro::request_or_macro()
2970 macro *request_or_macro::to_macro()
2975 request::request(REQUEST_FUNCP pp) : p(pp)
2979 void request::invoke(symbol)
2985 enum { SIZE = 128 };
2986 unsigned char s[SIZE];
2991 char_block::char_block()
3000 void append(unsigned char);
3001 void set(unsigned char, int);
3002 unsigned char get(int);
3009 friend class macro_header;
3010 friend class string_iterator;
3013 char_list::char_list()
3014 : ptr(0), len(0), head(0), tail(0)
3018 char_list::~char_list()
3021 char_block *tem = head;
3027 int char_list::length()
3032 void char_list::append(unsigned char c)
3035 head = tail = new char_block;
3039 if (ptr >= tail->s + char_block::SIZE) {
3040 tail->next = new char_block;
3049 void char_list::set(unsigned char c, int offset)
3051 assert(len > offset);
3052 // optimization for access at the end
3053 int boundary = len - len % char_block::SIZE;
3054 if (offset >= boundary) {
3055 *(tail->s + offset - boundary) = c;
3058 char_block *tem = head;
3061 l += char_block::SIZE;
3063 *(tem->s + offset % char_block::SIZE) = c;
3070 unsigned char char_list::get(int offset)
3072 assert(len > offset);
3073 // optimization for access at the end
3074 int boundary = len - len % char_block::SIZE;
3075 if (offset >= boundary)
3076 return *(tail->s + offset - boundary);
3077 char_block *tem = head;
3080 l += char_block::SIZE;
3082 return *(tem->s + offset % char_block::SIZE);
3093 void append(node *);
3097 friend class macro_header;
3098 friend class string_iterator;
3101 void node_list::append(node *n)
3109 tail = tail->next = n;
3113 int node_list::length()
3116 for (node *n = head; n != 0; n = n->next)
3121 node_list::node_list()
3126 node *node_list::extract()
3133 node_list::~node_list()
3135 delete_node_list(head);
3138 class macro_header {
3143 macro_header() { count = 1; }
3144 macro_header *copy(int);
3149 if (p != 0 && --(p->count) <= 0)
3156 if (!input_stack::get_location(1, &filename, &lineno)) {
3165 macro::macro(const macro &m)
3166 : filename(m.filename), lineno(m.lineno), len(m.len),
3167 empty_macro(m.empty_macro), is_a_diversion(m.is_a_diversion), p(m.p)
3173 macro::macro(int is_div)
3174 : is_a_diversion(is_div)
3176 if (!input_stack::get_location(1, &filename, &lineno)) {
3185 int macro::is_diversion()
3187 return is_a_diversion;
3190 macro ¯o::operator=(const macro &m)
3192 // don't assign object
3195 if (p != 0 && --(p->count) <= 0)
3198 filename = m.filename;
3201 empty_macro = m.empty_macro;
3202 is_a_diversion = m.is_a_diversion;
3206 void macro::append(unsigned char c)
3210 p = new macro_header;
3211 if (p->cl.length() != len) {
3212 macro_header *tem = p->copy(len);
3213 if (--(p->count) <= 0)
3219 if (c != PUSH_GROFF_MODE && c != PUSH_COMP_MODE && c != POP_GROFFCOMP_MODE)
3223 void macro::set(unsigned char c, int offset)
3227 p->cl.set(c, offset);
3230 unsigned char macro::get(int offset)
3233 return p->cl.get(offset);
3241 void macro::append_str(const char *s)
3246 while (s[i] != (char)0) {
3253 void macro::append(node *n)
3257 p = new macro_header;
3258 if (p->cl.length() != len) {
3259 macro_header *tem = p->copy(len);
3260 if (--(p->count) <= 0)
3270 void macro::append_unsigned(unsigned int i)
3272 unsigned int j = i / 10;
3275 append(((unsigned char)(((int)'0') + i % 10)));
3278 void macro::append_int(int i)
3284 append_unsigned((unsigned int)i);
3287 void macro::print_size()
3289 errprint("%1", len);
3292 // make a copy of the first n bytes
3294 macro_header *macro_header::copy(int n)
3296 macro_header *p = new macro_header;
3297 char_block *bp = cl.head;
3298 unsigned char *ptr = bp->s;
3301 if (ptr >= bp->s + char_block::SIZE) {
3305 unsigned char c = *ptr++;
3308 p->nl.append(nd->copy());
3317 object_dictionary_iterator iter(request_dictionary);
3318 request_or_macro *rm;
3320 while (iter.get(&s, (object **)&rm)) {
3321 assert(!s.is_null());
3322 macro *m = rm->to_macro();
3324 errprint("%1\t", s.contents());
3333 class string_iterator : public input_iterator {
3335 const char *how_invoked;
3339 int count; // of characters remaining
3341 int saved_compatible_flag;
3346 string_iterator(const macro &m, const char *p = 0, symbol s = NULL_SYMBOL);
3349 int get_location(int, const char **, int *);
3351 void save_compatible_flag(int f) { saved_compatible_flag = f; }
3352 int get_compatible_flag() { return saved_compatible_flag; }
3356 string_iterator::string_iterator(const macro &m, const char *p, symbol s)
3357 : input_iterator(m.is_a_diversion), mac(m), how_invoked(p), newline_flag(0),
3362 bp = mac.p->cl.head;
3363 nd = mac.p->nl.head;
3373 string_iterator::string_iterator()
3384 int string_iterator::is_diversion()
3386 return mac.is_diversion();
3389 int string_iterator::fill(node **np)
3396 const unsigned char *p = eptr;
3397 if (p >= bp->s + char_block::SIZE) {
3405 (*np)->div_nest_level = input_stack::get_div_level();
3407 (*np)->div_nest_level = 0;
3414 const unsigned char *e = bp->s + char_block::SIZE;
3419 unsigned char c = *p;
3420 if (c == '\n' || c == ESCAPE_NEWLINE) {
3434 int string_iterator::peek()
3438 const unsigned char *p = eptr;
3439 if (p >= bp->s + char_block::SIZE) {
3445 int string_iterator::get_location(int allow_macro,
3446 const char **filep, int *linep)
3450 if (mac.filename == 0)
3452 *filep = mac.filename;
3453 *linep = mac.lineno + lineno - 1;
3457 void string_iterator::backtrace()
3460 errprint("%1:%2: backtrace", mac.filename, mac.lineno + lineno - 1);
3463 errprint(": %1 `%2'\n", how_invoked, nm.contents());
3465 errprint(": %1\n", how_invoked);
3472 class temp_iterator : public input_iterator {
3473 unsigned char *base;
3474 temp_iterator(const char *, int len);
3477 friend input_iterator *make_temp_iterator(const char *);
3483 temp_iterator::temp_iterator(const char *s, int len)
3485 base = new unsigned char[len];
3486 memcpy(base, s, len);
3491 temp_iterator::~temp_iterator()
3496 class small_temp_iterator : public input_iterator {
3498 small_temp_iterator(const char *, int);
3499 ~small_temp_iterator();
3500 enum { BLOCK = 16 };
3501 static small_temp_iterator *free_list;
3502 void *operator new(size_t);
3503 void operator delete(void *);
3505 unsigned char buf[SIZE];
3506 friend input_iterator *make_temp_iterator(const char *);
3509 small_temp_iterator *small_temp_iterator::free_list = 0;
3511 void *small_temp_iterator::operator new(size_t n)
3513 assert(n == sizeof(small_temp_iterator));
3516 (small_temp_iterator *)new char[sizeof(small_temp_iterator)*BLOCK];
3517 for (int i = 0; i < BLOCK - 1; i++)
3518 free_list[i].next = free_list + i + 1;
3519 free_list[BLOCK-1].next = 0;
3521 small_temp_iterator *p = free_list;
3522 free_list = (small_temp_iterator *)(free_list->next);
3530 void small_temp_iterator::operator delete(void *p)
3533 ((small_temp_iterator *)p)->next = free_list;
3534 free_list = (small_temp_iterator *)p;
3538 small_temp_iterator::~small_temp_iterator()
3545 small_temp_iterator::small_temp_iterator(const char *s, int len)
3547 for (int i = 0; i < len; i++)
3553 input_iterator *make_temp_iterator(const char *s)
3556 return new small_temp_iterator(s, 0);
3559 if (n <= small_temp_iterator::SIZE)
3560 return new small_temp_iterator(s, n);
3562 return new temp_iterator(s, n);
3566 // this is used when macros with arguments are interpolated
3571 arg_list(const macro &);
3575 arg_list::arg_list(const macro &m) : mac(m), next(0)
3579 arg_list::~arg_list()
3583 class macro_iterator : public string_iterator {
3587 macro_iterator(symbol, macro &, const char *how_invoked = "macro");
3590 int has_args() { return 1; }
3591 input_iterator *get_arg(int i);
3592 int nargs() { return argc; }
3593 void add_arg(const macro &m);
3595 int is_macro() { return 1; }
3599 input_iterator *macro_iterator::get_arg(int i)
3602 return make_temp_iterator(nm.contents());
3603 if (i > 0 && i <= argc) {
3605 for (int j = 1; j < i; j++) {
3609 return new string_iterator(p->mac);
3615 void macro_iterator::add_arg(const macro &m)
3618 for (p = &args; *p; p = &((*p)->next))
3620 *p = new arg_list(m);
3624 void macro_iterator::shift(int n)
3626 while (n > 0 && argc > 0) {
3627 arg_list *tem = args;
3635 // This gets used by eg .if '\?xxx\?''.
3637 int operator==(const macro &m1, const macro &m2)
3639 if (m1.len != m2.len)
3641 string_iterator iter1(m1);
3642 string_iterator iter2(m2);
3646 int c1 = iter1.get(&nd1);
3649 int c2 = iter2.get(&nd2);
3661 int are_same = nd1->type() == nd2->type() && nd1->same(nd2);
3671 static void interpolate_macro(symbol nm)
3673 request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm);
3676 const char *s = nm.contents();
3677 if (strlen(s) > 2) {
3678 request_or_macro *r;
3683 r = (request_or_macro *)request_dictionary.lookup(symbol(buf));
3685 macro *m = r->to_macro();
3686 if (!m || !m->empty())
3687 warned = warning(WARN_SPACE,
3688 "macro `%1' not defined "
3689 "(probably missing space after `%2')",
3690 nm.contents(), buf);
3694 warning(WARN_MAC, "macro `%1' not defined", nm.contents());
3696 request_dictionary.define(nm, p);
3707 static void decode_args(macro_iterator *mi)
3709 if (!tok.newline() && !tok.eof()) {
3711 int c = get_copy(&n);
3715 if (c == '\n' || c == EOF)
3718 int quote_input_level = 0;
3719 int done_tab_warning = 0;
3721 quote_input_level = input_stack::get_level();
3724 arg.append(compatible_flag ? PUSH_COMP_MODE : PUSH_GROFF_MODE);
3725 while (c != EOF && c != '\n' && !(c == ' ' && quote_input_level == 0)) {
3726 if (quote_input_level > 0 && c == '"'
3728 || input_stack::get_level() == quote_input_level)) {
3741 if (c == '\t' && quote_input_level == 0 && !done_tab_warning) {
3742 warning(WARN_TAB, "tab character in unquoted macro argument");
3743 done_tab_warning = 1;
3750 arg.append(POP_GROFFCOMP_MODE);
3756 static void decode_string_args(macro_iterator *mi)
3759 int c = get_copy(&n);
3763 if (c == '\n' || c == EOF) {
3764 error("missing `]'");
3770 int quote_input_level = 0;
3771 int done_tab_warning = 0;
3773 quote_input_level = input_stack::get_level();
3776 while (c != EOF && c != '\n'
3777 && !(c == ']' && quote_input_level == 0)
3778 && !(c == ' ' && quote_input_level == 0)) {
3779 if (quote_input_level > 0 && c == '"'
3780 && input_stack::get_level() == quote_input_level) {
3793 if (c == '\t' && quote_input_level == 0 && !done_tab_warning) {
3794 warning(WARN_TAB, "tab character in unquoted string argument");
3795 done_tab_warning = 1;
3806 void macro::invoke(symbol nm)
3808 macro_iterator *mi = new macro_iterator(nm, *this);
3810 input_stack::push(mi);
3814 macro *macro::to_macro()
3821 return empty_macro == 1;
3824 macro_iterator::macro_iterator(symbol s, macro &m, const char *how_called)
3825 : string_iterator(m, how_called, s), args(0), argc(0)
3829 macro_iterator::macro_iterator() : args(0), argc(0)
3833 macro_iterator::~macro_iterator()
3836 arg_list *tem = args;
3842 dictionary composite_dictionary(17);
3844 void composite_request()
3846 symbol from = get_name(1);
3847 if (!from.is_null()) {
3848 const char *from_gn = glyph_name_to_unicode(from.contents());
3850 from_gn = check_unicode_name(from.contents());
3852 error("invalid composite glyph name `%1'", from.contents());
3857 const char *from_decomposed = decompose_unicode(from_gn);
3858 if (from_decomposed)
3859 from_gn = &from_decomposed[1];
3860 symbol to = get_name(1);
3862 composite_dictionary.remove(symbol(from_gn));
3864 const char *to_gn = glyph_name_to_unicode(to.contents());
3866 to_gn = check_unicode_name(to.contents());
3868 error("invalid composite glyph name `%1'", to.contents());
3873 const char *to_decomposed = decompose_unicode(to_gn);
3875 to_gn = &to_decomposed[1];
3876 if (strcmp(from_gn, to_gn) == 0)
3877 composite_dictionary.remove(symbol(from_gn));
3879 (void)composite_dictionary.lookup(symbol(from_gn), (void *)to_gn);
3885 static symbol composite_glyph_name(symbol nm)
3887 macro_iterator *mi = new macro_iterator();
3888 decode_string_args(mi);
3889 input_stack::push(mi);
3890 const char *gn = glyph_name_to_unicode(nm.contents());
3892 gn = check_unicode_name(nm.contents());
3894 error("invalid base glyph `%1' in composite glyph name", nm.contents());
3895 return EMPTY_SYMBOL;
3898 const char *gn_decomposed = decompose_unicode(gn);
3899 string glyph_name(gn_decomposed ? &gn_decomposed[1] : gn);
3901 int n = input_stack::nargs();
3902 for (int i = 1; i <= n; i++) {
3904 input_iterator *p = input_stack::get_arg(i);
3907 while ((c = p->get(0)) != EOF)
3910 const char *u = glyph_name_to_unicode(gl.contents());
3912 u = check_unicode_name(gl.contents());
3914 error("invalid component `%1' in composite glyph name",
3916 return EMPTY_SYMBOL;
3919 const char *decomposed = decompose_unicode(u);
3922 void *mapped_composite = composite_dictionary.lookup(symbol(u));
3923 if (mapped_composite)
3924 u = (const char *)mapped_composite;
3928 const char *groff_gn = unicode_to_glyph_name(glyph_name.contents());
3930 return symbol(groff_gn);
3934 return symbol(gl.contents());
3937 int trap_sprung_flag = 0;
3938 int postpone_traps_flag = 0;
3939 symbol postponed_trap;
3941 void spring_trap(symbol nm)
3943 assert(!nm.is_null());
3944 trap_sprung_flag = 1;
3945 if (postpone_traps_flag) {
3946 postponed_trap = nm;
3949 static char buf[2] = { BEGIN_TRAP, 0 };
3950 static char buf2[2] = { END_TRAP, '\0' };
3951 input_stack::push(make_temp_iterator(buf2));
3952 request_or_macro *p = lookup_request(nm);
3953 macro *m = p->to_macro();
3955 input_stack::push(new macro_iterator(nm, *m, "trap-invoked macro"));
3957 error("you can't invoke a request with a trap");
3958 input_stack::push(make_temp_iterator(buf));
3961 void postpone_traps()
3963 postpone_traps_flag = 1;
3966 int unpostpone_traps()
3968 postpone_traps_flag = 0;
3969 if (!postponed_trap.is_null()) {
3970 spring_trap(postponed_trap);
3971 postponed_trap = NULL_SYMBOL;
3980 macro_iterator *mi = new macro_iterator;
3981 int reading_from_terminal = isatty(fileno(stdin));
3983 if (!tok.newline() && !tok.eof()) {
3984 int c = get_copy(0);
3987 while (c != EOF && c != '\n' && c != ' ') {
3988 if (!invalid_input_char(c)) {
3989 if (reading_from_terminal)
4000 if (reading_from_terminal) {
4001 fputc(had_prompt ? ':' : '\a', stderr);
4004 input_stack::push(mi);
4008 while ((c = getchar()) != EOF) {
4009 if (invalid_input_char(c))
4010 warning(WARN_INPUT, "invalid input character code %1", int(c));
4023 if (reading_from_terminal)
4025 input_stack::push(new string_iterator(mac));
4029 enum define_mode { DEFINE_NORMAL, DEFINE_APPEND, DEFINE_IGNORE };
4030 enum calling_mode { CALLING_NORMAL, CALLING_INDIRECT };
4031 enum comp_mode { COMP_IGNORE, COMP_DISABLE, COMP_ENABLE };
4033 void do_define_string(define_mode mode, comp_mode comp)
4036 node *n = 0; // pacify compiler
4047 else if (!tok.space()) {
4048 error("bad string definition");
4059 request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm);
4060 macro *mm = rm ? rm->to_macro() : 0;
4061 if (mode == DEFINE_APPEND && mm)
4063 if (comp == COMP_DISABLE)
4064 mac.append(PUSH_GROFF_MODE);
4065 else if (comp == COMP_ENABLE)
4066 mac.append(PUSH_COMP_MODE);
4067 while (c != '\n' && c != EOF) {
4071 mac.append((unsigned char)c);
4076 request_dictionary.define(nm, mm);
4078 if (comp == COMP_DISABLE || comp == COMP_ENABLE)
4079 mac.append(POP_GROFFCOMP_MODE);
4084 void define_string()
4086 do_define_string(DEFINE_NORMAL,
4087 compatible_flag ? COMP_ENABLE: COMP_IGNORE);
4090 void define_nocomp_string()
4092 do_define_string(DEFINE_NORMAL, COMP_DISABLE);
4095 void append_string()
4097 do_define_string(DEFINE_APPEND,
4098 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4101 void append_nocomp_string()
4103 do_define_string(DEFINE_APPEND, COMP_DISABLE);
4106 void do_define_character(char_mode mode, const char *font_name)
4108 node *n = 0; // pacify compiler
4111 charinfo *ci = tok.get_char(1);
4117 string s(font_name);
4119 s += ci->nm.contents();
4121 ci = get_charinfo(symbol(s.contents()));
4128 else if (!tok.space()) {
4129 error("bad character definition");
4135 while (c == ' ' || c == '\t')
4139 macro *m = new macro;
4140 while (c != '\n' && c != EOF) {
4144 m->append((unsigned char)c);
4147 m = ci->setx_macro(m, mode);
4153 void define_character()
4155 do_define_character(CHAR_NORMAL);
4158 void define_fallback_character()
4160 do_define_character(CHAR_FALLBACK);
4163 void define_special_character()
4165 do_define_character(CHAR_SPECIAL);
4168 static void remove_character()
4171 while (!tok.newline() && !tok.eof()) {
4172 if (!tok.space() && !tok.tab()) {
4173 charinfo *ci = tok.get_char(1);
4176 macro *m = ci->set_macro(0);
4185 static void interpolate_string(symbol nm)
4187 request_or_macro *p = lookup_request(nm);
4188 macro *m = p->to_macro();
4190 error("you can only invoke a string or macro using \\*");
4192 string_iterator *si = new string_iterator(*m, "string", nm);
4193 input_stack::push(si);
4197 static void interpolate_string_with_args(symbol s)
4199 request_or_macro *p = lookup_request(s);
4200 macro *m = p->to_macro();
4202 error("you can only invoke a string or macro using \\*");
4204 macro_iterator *mi = new macro_iterator(s, *m);
4205 decode_string_args(mi);
4206 input_stack::push(mi);
4210 static void interpolate_arg(symbol nm)
4212 const char *s = nm.contents();
4213 if (!s || *s == '\0')
4214 copy_mode_error("missing argument name");
4215 else if (s[1] == 0 && csdigit(s[0]))
4216 input_stack::push(input_stack::get_arg(s[0] - '0'));
4217 else if (s[0] == '*' && s[1] == '\0') {
4218 int limit = input_stack::nargs();
4220 for (int i = 1; i <= limit; i++) {
4221 input_iterator *p = input_stack::get_arg(i);
4223 while ((c = p->get(0)) != EOF)
4230 input_stack::push(make_temp_iterator(args.contents()));
4233 else if (s[0] == '@' && s[1] == '\0') {
4234 int limit = input_stack::nargs();
4236 for (int i = 1; i <= limit; i++) {
4238 args += BEGIN_QUOTE;
4239 input_iterator *p = input_stack::get_arg(i);
4241 while ((c = p->get(0)) != EOF)
4250 input_stack::push(make_temp_iterator(args.contents()));
4255 for (p = s; *p && csdigit(*p); p++)
4258 copy_mode_error("bad argument name `%1'", s);
4260 input_stack::push(input_stack::get_arg(atoi(s)));
4264 void handle_first_page_transition()
4267 topdiv->begin_page();
4270 // We push back a token by wrapping it up in a token_node, and
4271 // wrapping that up in a string_iterator.
4273 static void push_token(const token &t)
4276 m.append(new token_node(t));
4277 input_stack::push(new string_iterator(m));
4280 void push_page_ejector()
4282 static char buf[2] = { PAGE_EJECTOR, '\0' };
4283 input_stack::push(make_temp_iterator(buf));
4286 void handle_initial_request(unsigned char code)
4292 mac.append(new token_node(tok));
4293 input_stack::push(new string_iterator(mac));
4294 input_stack::push(make_temp_iterator(buf));
4295 topdiv->begin_page();
4299 void handle_initial_title()
4301 handle_initial_request(TITLE_REQUEST);
4304 // this should be local to define_macro, but cfront 1.2 doesn't support that
4305 static symbol dot_symbol(".");
4307 void do_define_macro(define_mode mode, calling_mode calling, comp_mode comp)
4310 if (calling == CALLING_INDIRECT) {
4311 symbol temp1 = get_name(1);
4312 if (temp1.is_null()) {
4316 symbol temp2 = get_name();
4317 input_stack::push(make_temp_iterator("\n"));
4318 if (!temp2.is_null()) {
4319 interpolate_string(temp2);
4320 input_stack::push(make_temp_iterator(" "));
4322 interpolate_string(temp1);
4323 input_stack::push(make_temp_iterator(" "));
4326 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4333 term = get_name(); // the request that terminates the definition
4336 while (!tok.newline() && !tok.eof())
4338 const char *start_filename;
4340 int have_start_location = input_stack::get_location(0, &start_filename,
4343 // doing this here makes the line numbers come out right
4344 int c = get_copy(&n, 1);
4347 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4348 request_or_macro *rm =
4349 (request_or_macro *)request_dictionary.lookup(nm);
4351 mm = rm->to_macro();
4352 if (mm && mode == DEFINE_APPEND)
4356 if (comp == COMP_DISABLE)
4357 mac.append(PUSH_GROFF_MODE);
4358 else if (comp == COMP_ENABLE)
4359 mac.append(PUSH_COMP_MODE);
4361 while (c == ESCAPE_NEWLINE) {
4362 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND)
4364 c = get_copy(&n, 1);
4366 if (bol && c == '.') {
4367 const char *s = term.contents();
4369 // see if it matches term
4372 while ((d = get_copy(&n)) == ' ' || d == '\t')
4374 if ((unsigned char)s[0] == d) {
4375 for (i = 1; s[i] != 0; i++) {
4377 if ((unsigned char)s[i] != d)
4383 && ((i == 2 && compatible_flag)
4384 || (d = get_copy(&n)) == ' '
4385 || d == '\n')) { // we found it
4390 if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) {
4393 request_dictionary.define(nm, mm);
4395 if (comp == COMP_DISABLE || comp == COMP_ENABLE)
4396 mac.append(POP_GROFFCOMP_MODE);
4399 if (term != dot_symbol) {
4401 interpolate_macro(term);
4407 if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) {
4409 for (int j = 0; j < i; j++)
4415 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4416 if (have_start_location)
4417 error_with_file_and_line(start_filename, start_lineno,
4418 "end of file while defining macro `%1'",
4421 error("end of file while defining macro `%1'", nm.contents());
4424 if (have_start_location)
4425 error_with_file_and_line(start_filename, start_lineno,
4426 "end of file while ignoring input lines");
4428 error("end of file while ignoring input lines");
4433 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4440 c = get_copy(&n, 1);
4446 do_define_macro(DEFINE_NORMAL, CALLING_NORMAL,
4447 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4450 void define_nocomp_macro()
4452 do_define_macro(DEFINE_NORMAL, CALLING_NORMAL, COMP_DISABLE);
4455 void define_indirect_macro()
4457 do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT,
4458 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4461 void define_indirect_nocomp_macro()
4463 do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT, COMP_DISABLE);
4468 do_define_macro(DEFINE_APPEND, CALLING_NORMAL,
4469 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4472 void append_nocomp_macro()
4474 do_define_macro(DEFINE_APPEND, CALLING_NORMAL, COMP_DISABLE);
4477 void append_indirect_macro()
4479 do_define_macro(DEFINE_APPEND, CALLING_INDIRECT,
4480 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4483 void append_indirect_nocomp_macro()
4485 do_define_macro(DEFINE_APPEND, CALLING_INDIRECT, COMP_DISABLE);
4491 do_define_macro(DEFINE_IGNORE, CALLING_NORMAL, COMP_IGNORE);
4498 symbol s = get_name();
4501 request_dictionary.remove(s);
4508 symbol s1 = get_name(1);
4509 if (!s1.is_null()) {
4510 symbol s2 = get_name(1);
4512 request_dictionary.rename(s1, s2);
4519 symbol s1 = get_name(1);
4520 if (!s1.is_null()) {
4521 symbol s2 = get_name(1);
4522 if (!s2.is_null()) {
4523 if (!request_dictionary.alias(s1, s2))
4524 warning(WARN_MAC, "macro `%1' not defined", s2.contents());
4532 symbol s = get_name(1);
4534 request_or_macro *p = lookup_request(s);
4535 macro *m = p->to_macro();
4537 error("cannot chop request");
4538 else if (m->empty())
4539 error("cannot chop empty macro");
4541 int have_restore = 0;
4542 // we have to check for additional save/restore pairs which could be
4543 // there due to empty am1 requests.
4545 if (m->get(m->len - 1) != POP_GROFFCOMP_MODE)
4549 if (m->get(m->len - 1) != PUSH_GROFF_MODE
4550 && m->get(m->len - 1) != PUSH_COMP_MODE)
4558 error("cannot chop empty macro");
4561 m->set(POP_GROFFCOMP_MODE, m->len - 1);
4570 void substring_request()
4572 int start; // 0, 1, ..., n-1 or -1, -2, ...
4573 symbol s = get_name(1);
4574 if (!s.is_null() && get_integer(&start)) {
4575 request_or_macro *p = lookup_request(s);
4576 macro *m = p->to_macro();
4578 error("cannot apply `substring' on a request");
4581 if (!has_arg() || get_integer(&end)) {
4582 int real_length = 0; // 1, 2, ..., n
4583 string_iterator iter1(*m);
4584 for (int l = 0; l < m->len; l++) {
4585 int c = iter1.get(0);
4586 if (c == PUSH_GROFF_MODE
4587 || c == PUSH_COMP_MODE
4588 || c == POP_GROFFCOMP_MODE)
4595 start += real_length;
4603 if (start >= real_length || end < 0) {
4605 "start and end index of substring out of range");
4608 if (--(m->p->count) <= 0)
4617 "start index of substring out of range, set to 0");
4620 if (end >= real_length) {
4622 "end index of substring out of range, set to string length");
4623 end = real_length - 1;
4625 // now extract the substring
4626 string_iterator iter(*m);
4628 for (i = 0; i < start; i++) {
4629 int c = iter.get(0);
4630 while (c == PUSH_GROFF_MODE
4631 || c == PUSH_COMP_MODE
4632 || c == POP_GROFFCOMP_MODE)
4638 for (; i <= end; i++) {
4639 node *nd = 0; // pacify compiler
4640 int c = iter.get(&nd);
4641 while (c == PUSH_GROFF_MODE
4642 || c == PUSH_COMP_MODE
4643 || c == POP_GROFFCOMP_MODE)
4650 mac.append((unsigned char)c);
4659 void length_request()
4663 if (ret.is_null()) {
4673 else if (!tok.space()) {
4674 error("bad string definition");
4685 while (c != '\n' && c != EOF) {
4689 reg *r = (reg*)number_reg_dictionary.lookup(ret);
4693 set_number_reg(ret, len);
4697 void asciify_macro()
4699 symbol s = get_name(1);
4701 request_or_macro *p = lookup_request(s);
4702 macro *m = p->to_macro();
4704 error("cannot asciify request");
4707 string_iterator iter(*m);
4709 node *nd = 0; // pacify compiler
4710 int c = iter.get(&nd);
4724 void unformat_macro()
4726 symbol s = get_name(1);
4728 request_or_macro *p = lookup_request(s);
4729 macro *m = p->to_macro();
4731 error("cannot unformat request");
4734 string_iterator iter(*m);
4736 node *nd = 0; // pacify compiler
4737 int c = iter.get(&nd);
4743 if (nd->set_unformat_flag())
4753 static void interpolate_environment_variable(symbol nm)
4755 const char *s = getenv(nm.contents());
4757 input_stack::push(make_temp_iterator(s));
4760 void interpolate_number_reg(symbol nm, int inc)
4762 reg *r = lookup_number_reg(nm);
4767 input_stack::push(make_temp_iterator(r->get_string()));
4770 static void interpolate_number_format(symbol nm)
4772 reg *r = (reg *)number_reg_dictionary.lookup(nm);
4774 input_stack::push(make_temp_iterator(r->get_format()));
4777 static int get_delim_number(units *n, unsigned char si, int prev_value)
4781 if (start.delimiter(1)) {
4783 if (get_number(n, si, prev_value)) {
4785 warning(WARN_DELIM, "closing delimiter does not match");
4792 static int get_delim_number(units *n, unsigned char si)
4796 if (start.delimiter(1)) {
4798 if (get_number(n, si)) {
4800 warning(WARN_DELIM, "closing delimiter does not match");
4807 static int get_line_arg(units *n, unsigned char si, charinfo **cp)
4811 int start_level = input_stack::get_level();
4812 if (!start.delimiter(1))
4815 if (get_number(n, si)) {
4816 if (tok.dummy() || tok.transparent_dummy())
4818 if (!(start == tok && input_stack::get_level() == start_level)) {
4819 *cp = tok.get_char(1);
4822 if (!(start == tok && input_stack::get_level() == start_level))
4823 warning(WARN_DELIM, "closing delimiter does not match");
4829 static int read_size(int *x)
4839 else if (c == '+') {
4844 int val = 0; // pacify compiler
4850 // allow an increment either before or after the left parenthesis
4856 else if (c == '+') {
4871 val = val*10 + (c - '0');
4876 else if (csdigit(c)) {
4878 if (!inc && c != '0' && c < '4') {
4884 val = val*10 + (c - '0');
4888 else if (!tok.delimiter(1))
4894 ? get_number(&val, 'z')
4895 : get_number(&val, 'z', curenv->get_requested_point_size())))
4897 if (!(start.ch() == '[' && tok.ch() == ']') && start != tok) {
4898 if (start.ch() == '[')
4899 error("missing `]'");
4901 error("missing closing delimiter");
4909 // special case -- \s[0] and \s0 means to revert to previous size
4916 *x = curenv->get_requested_point_size() + val;
4919 *x = curenv->get_requested_point_size() - val;
4926 "\\s request results in non-positive point size; set to 1");
4932 error("bad digit in point size");
4937 static symbol get_delim_name()
4942 error("end of input at start of delimited name");
4945 if (start.newline()) {
4946 error("can't delimit name with a newline");
4949 int start_level = input_stack::get_level();
4950 char abuf[ABUF_SIZE];
4952 int buf_size = ABUF_SIZE;
4955 if (i + 1 > buf_size) {
4957 buf = new char[ABUF_SIZE*2];
4958 memcpy(buf, abuf, buf_size);
4959 buf_size = ABUF_SIZE*2;
4962 char *old_buf = buf;
4963 buf = new char[buf_size*2];
4964 memcpy(buf, old_buf, buf_size);
4971 && (compatible_flag || input_stack::get_level() == start_level))
4973 if ((buf[i] = tok.ch()) == 0) {
4974 error("missing delimiter (got %1)", tok.description());
4984 error("empty delimited name");
4999 static void do_register()
5003 if (!start.delimiter(1))
5006 symbol nm = get_long_name(1);
5011 reg *r = (reg *)number_reg_dictionary.lookup(nm);
5013 if (!r || !r->get_value(&prev_value))
5016 if (!get_number(&val, 'u', prev_value))
5019 warning(WARN_DELIM, "closing delimiter does not match");
5023 set_number_reg(nm, val);
5026 // this implements the \w escape sequence
5028 static void do_width()
5032 int start_level = input_stack::get_level();
5033 environment env(curenv);
5034 environment *oldenv = curenv;
5039 warning(WARN_DELIM, "missing closing delimiter");
5042 if (tok.newline()) {
5043 warning(WARN_DELIM, "missing closing delimiter");
5044 input_stack::push(make_temp_iterator("\n"));
5048 && (compatible_flag || input_stack::get_level() == start_level))
5053 units x = env.get_input_line_position().to_units();
5054 input_stack::push(make_temp_iterator(i_to_a(x)));
5055 env.width_registers();
5060 charinfo *page_character;
5062 void set_page_character()
5064 page_character = get_optional_char();
5068 static const symbol percent_symbol("%");
5070 void read_title_parts(node **part, hunits *part_width)
5073 if (tok.newline() || tok.eof())
5076 int start_level = input_stack::get_level();
5078 for (int i = 0; i < 3; i++) {
5079 while (!tok.newline() && !tok.eof()) {
5081 && (compatible_flag || input_stack::get_level() == start_level)) {
5085 if (page_character != 0 && tok.get_char() == page_character)
5086 interpolate_number_reg(percent_symbol, 0);
5091 curenv->wrap_up_tab();
5092 part_width[i] = curenv->get_input_line_position();
5093 part[i] = curenv->extract_output_line();
5095 while (!tok.newline() && !tok.eof())
5099 class non_interpreted_node : public node {
5102 non_interpreted_node(const macro &);
5103 int interpret(macro *);
5105 int ends_sentence();
5112 non_interpreted_node::non_interpreted_node(const macro &m) : mac(m)
5116 int non_interpreted_node::ends_sentence()
5121 int non_interpreted_node::same(node *nd)
5123 return mac == ((non_interpreted_node *)nd)->mac;
5126 const char *non_interpreted_node::type()
5128 return "non_interpreted_node";
5131 int non_interpreted_node::force_tprint()
5136 int non_interpreted_node::is_tag()
5141 node *non_interpreted_node::copy()
5143 return new non_interpreted_node(mac);
5146 int non_interpreted_node::interpret(macro *m)
5148 string_iterator si(mac);
5149 node *n = 0; // pacify compiler
5162 static node *do_non_interpreted()
5167 while ((c = get_copy(&n)) != ESCAPE_QUESTION && c != EOF && c != '\n')
5172 if (c == EOF || c == '\n') {
5173 error("missing \\?");
5176 return new non_interpreted_node(mac);
5179 static void encode_char(macro *mac, char c)
5182 if ((font::use_charnames_in_special) && tok.special()) {
5183 charinfo *ci = tok.get_char(1);
5184 const char *s = ci->get_symbol()->contents();
5185 if (s[0] != (char)0) {
5189 while (s[i] != (char)0) {
5197 else if (tok.stretchable_space()
5198 || tok.unstretchable_space())
5200 else if (!(tok.hyphen_indicator()
5202 || tok.transparent_dummy()
5203 || tok.zero_width_break()))
5204 error("%1 is invalid within \\X", tok.description());
5207 if ((font::use_charnames_in_special) && (c == '\\')) {
5209 * add escape escape sequence
5221 int start_level = input_stack::get_level();
5224 tok != start || input_stack::get_level() != start_level;
5227 warning(WARN_DELIM, "missing closing delimiter");
5230 if (tok.newline()) {
5231 input_stack::push(make_temp_iterator("\n"));
5232 warning(WARN_DELIM, "missing closing delimiter");
5240 else if (tok.leader())
5242 else if (tok.backspace())
5246 encode_char(&mac, c);
5248 return new special_node(mac);
5251 void output_request()
5253 if (!tok.newline() && !tok.eof()) {
5261 if (c != ' ' && c != '\t')
5264 for (; c != '\n' && c != EOF; c = get_copy(0))
5265 topdiv->transparent_output(c);
5266 topdiv->transparent_output('\n');
5271 extern int image_no; // from node.cpp
5273 static node *do_suppress(symbol nm)
5275 if (nm.is_null() || nm.is_empty()) {
5276 error("expecting an argument to escape \\O");
5279 const char *s = nm.contents();
5282 if (begin_level == 0)
5283 // suppress generation of glyphs
5284 return new suppress_node(0, 0);
5287 if (begin_level == 0)
5288 // enable generation of glyphs
5289 return new suppress_node(1, 0);
5292 if (begin_level == 0)
5293 return new suppress_node(1, 1);
5303 s++; // move over '5'
5305 if (*s == (char)0) {
5306 error("missing position and filename in \\O");
5309 if (!(position == 'l'
5312 || position == 'i')) {
5313 error("l, r, c, or i position expected (got %1 in \\O)", position);
5316 s++; // onto image name
5317 if (s == (char *)0) {
5318 error("missing image name for \\O");
5322 if (begin_level == 0)
5323 return new suppress_node(symbol(s), position, image_no);
5327 error("`%1' is an invalid argument to \\O", *s);
5332 void special_node::tprint(troff_output_file *out)
5335 string_iterator iter(mac);
5337 int c = iter.get(0);
5340 for (const char *s = ::asciify(c); *s; s++)
5341 tprint_char(out, *s);
5346 int get_file_line(const char **filename, int *lineno)
5348 return input_stack::get_location(0, filename, lineno);
5354 if (get_integer(&n)) {
5355 const char *filename = 0;
5357 symbol s = get_long_name();
5358 filename = s.contents();
5360 (void)input_stack::set_location(filename, n-1);
5365 static int nroff_mode = 0;
5367 static void nroff_request()
5373 static void troff_request()
5379 static void skip_alternative()
5382 // ensure that ``.if 0\{'' works as expected
5383 if (tok.left_brace())
5387 c = input_stack::get(0);
5390 if (c == ESCAPE_LEFT_BRACE)
5392 else if (c == ESCAPE_RIGHT_BRACE)
5394 else if (c == escape_char && escape_char > 0)
5395 switch(input_stack::get(0)) {
5403 while ((c = input_stack::get(0)) != '\n' && c != EOF)
5407 Note that the level can properly be < 0, eg
5413 So don't give an error message in this case.
5415 if (level <= 0 && c == '\n')
5421 static void begin_alternative()
5423 while (tok.space() || tok.left_brace())
5433 static int_stack if_else_stack;
5440 while (tok.ch() == '!') {
5445 unsigned char c = tok.ch();
5448 result = !nroff_mode;
5450 else if (c == 'n') {
5452 result = nroff_mode;
5454 else if (c == 'v') {
5458 else if (c == 'o') {
5459 result = (topdiv->get_page_number() & 1);
5462 else if (c == 'e') {
5463 result = !(topdiv->get_page_number() & 1);
5466 else if (c == 'd' || c == 'r') {
5468 symbol nm = get_name(1);
5474 ? request_dictionary.lookup(nm) != 0
5475 : number_reg_dictionary.lookup(nm) != 0);
5477 else if (c == 'm') {
5479 symbol nm = get_long_name(1);
5484 result = (nm == default_symbol
5485 || color_dictionary.lookup(nm) != 0);
5487 else if (c == 'c') {
5490 charinfo *ci = tok.get_char(1);
5495 result = character_exists(ci, curenv);
5498 else if (c == 'F') {
5500 symbol nm = get_long_name(1);
5505 result = check_font(curenv->get_family()->nm, nm);
5507 else if (c == 'S') {
5509 symbol nm = get_long_name(1);
5514 result = check_style(nm);
5516 else if (tok.space())
5518 else if (tok.delimiter()) {
5520 int delim_level = input_stack::get_level();
5521 environment env1(curenv);
5522 environment env2(curenv);
5523 environment *oldenv = curenv;
5526 for (int i = 0; i < 2; i++) {
5529 if (tok.newline() || tok.eof()) {
5530 warning(WARN_DELIM, "missing closing delimiter");
5536 && (compatible_flag || input_stack::get_level() == delim_level))
5542 node *n1 = env1.extract_output_line();
5543 node *n2 = env2.extract_output_line();
5544 result = same_node_list(n1, n2);
5545 delete_node_list(n1);
5546 delete_node_list(n2);
5554 if (!get_number(&n, 'u')) {
5564 begin_alternative();
5570 void if_else_request()
5572 if_else_stack.push(do_if_request());
5582 if (if_else_stack.is_empty()) {
5583 warning(WARN_EL, "unbalanced .el request");
5587 if (if_else_stack.pop())
5590 begin_alternative();
5594 static int while_depth = 0;
5595 static int while_break_flag = 0;
5597 void while_request()
5602 mac.append(new token_node(tok));
5604 node *n = 0; // pacify compiler
5605 int c = input_stack::get(&n);
5621 if (c == ESCAPE_LEFT_BRACE)
5623 else if (c == ESCAPE_RIGHT_BRACE)
5625 else if (c == escape_char)
5628 if (c == '\n' && level <= 0)
5633 error("unbalanced \\{ \\}");
5636 input_stack::add_boundary();
5638 input_stack::push(new string_iterator(mac, "while loop"));
5640 if (!do_if_request()) {
5641 while (input_stack::get(0) != EOF)
5645 process_input_stack();
5646 if (while_break_flag || input_stack::is_return_boundary()) {
5647 while_break_flag = 0;
5651 input_stack::remove_boundary();
5657 void while_break_request()
5660 error("no while loop");
5664 while_break_flag = 1;
5665 while (input_stack::get(0) != EOF)
5671 void while_continue_request()
5674 error("no while loop");
5678 while (input_stack::get(0) != EOF)
5688 symbol nm = get_long_name(1);
5692 while (!tok.newline() && !tok.eof())
5695 FILE *fp = include_search_path.open_file_cautious(nm.contents());
5697 input_stack::push(new file_iterator(fp, nm.contents()));
5699 error("can't open `%1': %2", nm.contents(), strerror(errno));
5704 // like .so but use popen()
5709 error(".pso request not allowed in safer mode");
5713 #ifdef POPEN_MISSING
5714 error("pipes not available on this system");
5716 #else /* not POPEN_MISSING */
5717 if (tok.newline() || tok.eof())
5718 error("missing command");
5721 while ((c = get_copy(0)) == ' ' || c == '\t')
5724 char *buf = new char[buf_size];
5726 for (; c != '\n' && c != EOF; c = get_copy(0)) {
5727 const char *s = asciify(c);
5728 int slen = strlen(s);
5729 if (buf_used + slen + 1> buf_size) {
5730 char *old_buf = buf;
5731 int old_buf_size = buf_size;
5733 buf = new char[buf_size];
5734 memcpy(buf, old_buf, old_buf_size);
5737 strcpy(buf + buf_used, s);
5740 buf[buf_used] = '\0';
5742 FILE *fp = popen(buf, POPEN_RT);
5744 input_stack::push(new file_iterator(fp, symbol(buf).contents(), 1));
5746 error("can't open pipe to process `%1': %2", buf, strerror(errno));
5750 #endif /* not POPEN_MISSING */
5756 static int llx_reg_contents = 0;
5757 static int lly_reg_contents = 0;
5758 static int urx_reg_contents = 0;
5759 static int ury_reg_contents = 0;
5761 struct bounding_box {
5762 int llx, lly, urx, ury;
5765 /* Parse the argument to a %%BoundingBox comment. Return 1 if it
5766 contains 4 numbers, 2 if it contains (atend), 0 otherwise. */
5768 int parse_bounding_box(char *p, bounding_box *bb)
5770 if (sscanf(p, "%d %d %d %d",
5771 &bb->llx, &bb->lly, &bb->urx, &bb->ury) == 4)
5774 /* The Document Structuring Conventions say that the numbers
5775 should be integers. Unfortunately some broken applications
5777 double x1, x2, x3, x4;
5778 if (sscanf(p, "%lf %lf %lf %lf", &x1, &x2, &x3, &x4) == 4) {
5786 for (; *p == ' ' || *p == '\t'; p++)
5788 if (strncmp(p, "(atend)", 7) == 0) {
5793 bb->llx = bb->lly = bb->urx = bb->ury = 0;
5797 // This version is taken from psrm.cpp
5799 #define PS_LINE_MAX 255
5800 cset white_space("\n\r \t");
5802 int ps_get_line(char *buf, FILE *fp, const char* filename)
5811 while (c != '\r' && c != '\n' && c != EOF) {
5812 if ((c < 0x1b && !white_space(c)) || c == 0x7f)
5813 error("invalid input character code %1 in `%2'", int(c), filename);
5814 else if (i < PS_LINE_MAX)
5818 error("PostScript file `%1' is non-conforming "
5819 "because length of line exceeds 255", filename);
5827 if (c != EOF && c != '\n')
5833 inline void assign_registers(int llx, int lly, int urx, int ury)
5835 llx_reg_contents = llx;
5836 lly_reg_contents = lly;
5837 urx_reg_contents = urx;
5838 ury_reg_contents = ury;
5841 void do_ps_file(FILE *fp, const char* filename)
5845 char buf[PS_LINE_MAX];
5846 llx_reg_contents = lly_reg_contents =
5847 urx_reg_contents = ury_reg_contents = 0;
5848 if (!ps_get_line(buf, fp, filename)) {
5849 error("`%1' is empty", filename);
5852 if (strncmp("%!PS-Adobe-", buf, 11) != 0) {
5853 error("`%1' is not conforming to the Document Structuring Conventions",
5857 while (ps_get_line(buf, fp, filename) != 0) {
5858 if (buf[0] != '%' || buf[1] != '%'
5859 || strncmp(buf + 2, "EndComments", 11) == 0)
5861 if (strncmp(buf + 2, "BoundingBox:", 12) == 0) {
5862 int res = parse_bounding_box(buf + 14, &bb);
5864 assign_registers(bb.llx, bb.lly, bb.urx, bb.ury);
5867 else if (res == 2) {
5872 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
5881 /* in the trailer, the last BoundingBox comment is significant */
5882 for (offset = 512; !last_try; offset *= 2) {
5883 int had_trailer = 0;
5885 if (offset > 32768 || fseek(fp, -offset, 2) == -1) {
5887 if (fseek(fp, 0L, 0) == -1)
5890 while (ps_get_line(buf, fp, filename) != 0) {
5891 if (buf[0] == '%' && buf[1] == '%') {
5893 if (strncmp(buf + 2, "Trailer", 7) == 0)
5897 if (strncmp(buf + 2, "BoundingBox:", 12) == 0) {
5898 int res = parse_bounding_box(buf + 14, &bb);
5901 else if (res == 2) {
5902 error("`(atend)' not allowed in trailer of `%1'", filename);
5906 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
5915 assign_registers(bb.llx, bb.lly, bb.urx, bb.ury);
5920 error("%%%%BoundingBox comment not found in `%1'", filename);
5923 void ps_bbox_request()
5925 symbol nm = get_long_name(1);
5929 while (!tok.newline() && !tok.eof())
5932 // PS files might contain non-printable characters, such as ^Z
5933 // and CRs not followed by an LF, so open them in binary mode.
5934 FILE *fp = include_search_path.open_file_cautious(nm.contents(),
5937 do_ps_file(fp, nm.contents());
5941 error("can't open `%1': %2", nm.contents(), strerror(errno));
5946 const char *asciify(int c)
5949 buf[0] = escape_char == '\0' ? '\\' : escape_char;
5950 buf[1] = buf[2] = '\0';
5952 case ESCAPE_QUESTION:
5955 case ESCAPE_AMPERSAND:
5958 case ESCAPE_RIGHT_PARENTHESIS:
5961 case ESCAPE_UNDERSCORE:
5967 case ESCAPE_CIRCUMFLEX:
5970 case ESCAPE_LEFT_BRACE:
5973 case ESCAPE_RIGHT_BRACE:
5976 case ESCAPE_LEFT_QUOTE:
5979 case ESCAPE_RIGHT_QUOTE:
5997 case ESCAPE_PERCENT:
6009 case PUSH_GROFF_MODE:
6010 case PUSH_COMP_MODE:
6011 case POP_GROFFCOMP_MODE:
6015 if (invalid_input_char(c))
6024 const char *input_char_description(int c)
6028 return "a newline character";
6030 return "a backspace character";
6032 return "a leader character";
6034 return "a tab character";
6036 return "a space character";
6040 static char buf[sizeof("magic character code ") + 1 + INT_DIGITS];
6041 if (invalid_input_char(c)) {
6042 const char *s = asciify(c);
6049 sprintf(buf, "magic character code %d", c);
6058 sprintf(buf, "character code %d", c);
6064 if (!tok.newline() && !tok.eof()) {
6073 if (c != ' ' && c != '\t')
6077 for (; c != '\n' && c != EOF; c = get_copy(0))
6080 curenv->add_node(new tag_node(s, 0));
6087 if (!tok.newline() && !tok.eof()) {
6096 if (c != ' ' && c != '\t')
6100 for (; c != '\n' && c != EOF; c = get_copy(0))
6103 curenv->add_node(new tag_node(s, 1));
6108 // .tm, .tm1, and .tmc
6110 void do_terminal(int newline, int string_like)
6112 if (!tok.newline() && !tok.eof()) {
6116 if (string_like && c == '"') {
6120 if (c != ' ' && c != '\t')
6123 for (; c != '\n' && c != EOF; c = get_copy(0))
6124 fputs(asciify(c), stderr);
6127 fputc('\n', stderr);
6142 void terminal_continue()
6147 dictionary stream_dictionary(20);
6149 void do_open(int append)
6151 symbol stream = get_name(1);
6152 if (!stream.is_null()) {
6153 symbol filename = get_long_name(1);
6154 if (!filename.is_null()) {
6156 FILE *fp = fopen(filename.contents(), append ? "a" : "w");
6158 error("can't open `%1' for %2: %3",
6159 filename.contents(),
6160 append ? "appending" : "writing",
6162 fp = (FILE *)stream_dictionary.remove(stream);
6165 fp = (FILE *)stream_dictionary.lookup(stream, fp);
6176 error(".open request not allowed in safer mode");
6183 void opena_request()
6186 error(".opena request not allowed in safer mode");
6193 void close_request()
6195 symbol stream = get_name(1);
6196 if (!stream.is_null()) {
6197 FILE *fp = (FILE *)stream_dictionary.remove(stream);
6199 error("no stream named `%1'", stream.contents());
6206 // .write and .writec
6208 void do_write_request(int newline)
6210 symbol stream = get_name(1);
6211 if (stream.is_null()) {
6215 FILE *fp = (FILE *)stream_dictionary.lookup(stream);
6217 error("no stream named `%1'", stream.contents());
6222 while ((c = get_copy(0)) == ' ')
6226 for (; c != '\n' && c != EOF; c = get_copy(0))
6227 fputs(asciify(c), fp);
6234 void write_request()
6236 do_write_request(1);
6239 void write_request_continue()
6241 do_write_request(0);
6244 void write_macro_request()
6246 symbol stream = get_name(1);
6247 if (stream.is_null()) {
6251 FILE *fp = (FILE *)stream_dictionary.lookup(stream);
6253 error("no stream named `%1'", stream.contents());
6257 symbol s = get_name(1);
6262 request_or_macro *p = lookup_request(s);
6263 macro *m = p->to_macro();
6265 error("cannot write request");
6267 string_iterator iter(*m);
6269 int c = iter.get(0);
6272 fputs(asciify(c), fp);
6279 void warnscale_request()
6286 warn_scale = (double)units_per_inch;
6288 warn_scale = (double)units_per_inch / 2.54;
6290 warn_scale = (double)units_per_inch / 72.0;
6292 warn_scale = (double)units_per_inch / 6.0;
6295 "invalid scaling indicator `%1', using `i' instead", c);
6298 warn_scaling_indicator = c;
6303 void spreadwarn_request()
6306 if (has_arg() && get_hunits(&n, 'm')) {
6309 hunits em = curenv->get_size();
6310 spread_limit = (double)n.to_units()
6311 / (em.is_zero() ? hresolution : em.to_units());
6314 spread_limit = -spread_limit - 1; // no arg toggles on/off without
6315 // changing value; we mirror at
6316 // -0.5 to make zero a valid value
6320 static void init_charset_table()
6323 strcpy(buf, "char");
6324 for (int i = 0; i < 256; i++) {
6325 strcpy(buf + 4, i_to_a(i));
6326 charset_table[i] = get_charinfo(symbol(buf));
6327 charset_table[i]->set_ascii_code(i);
6329 charset_table[i]->set_hyphenation_code(cmlower(i));
6331 charset_table['.']->set_flags(charinfo::ENDS_SENTENCE);
6332 charset_table['?']->set_flags(charinfo::ENDS_SENTENCE);
6333 charset_table['!']->set_flags(charinfo::ENDS_SENTENCE);
6334 charset_table['-']->set_flags(charinfo::BREAK_AFTER);
6335 charset_table['"']->set_flags(charinfo::TRANSPARENT);
6336 charset_table['\'']->set_flags(charinfo::TRANSPARENT);
6337 charset_table[')']->set_flags(charinfo::TRANSPARENT);
6338 charset_table[']']->set_flags(charinfo::TRANSPARENT);
6339 charset_table['*']->set_flags(charinfo::TRANSPARENT);
6340 get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT);
6341 get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT);
6342 get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER);
6343 get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6344 get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6345 get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6346 get_charinfo(symbol("sqrtex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6347 get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6348 get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY);
6349 page_character = charset_table['%'];
6352 static void init_hpf_code_table()
6354 for (int i = 0; i < 256; i++)
6355 hpf_code_table[i] = i;
6358 static void do_translate(int translate_transparent, int translate_input)
6361 while (!tok.newline() && !tok.eof()) {
6363 // This is a really bizarre troff feature.
6365 translate_space_to_dummy = tok.dummy();
6366 if (tok.newline() || tok.eof())
6371 charinfo *ci1 = tok.get_char(1);
6375 if (tok.newline() || tok.eof()) {
6376 ci1->set_special_translation(charinfo::TRANSLATE_SPACE,
6377 translate_transparent);
6381 ci1->set_special_translation(charinfo::TRANSLATE_SPACE,
6382 translate_transparent);
6383 else if (tok.stretchable_space())
6384 ci1->set_special_translation(charinfo::TRANSLATE_STRETCHABLE_SPACE,
6385 translate_transparent);
6386 else if (tok.dummy())
6387 ci1->set_special_translation(charinfo::TRANSLATE_DUMMY,
6388 translate_transparent);
6389 else if (tok.hyphen_indicator())
6390 ci1->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR,
6391 translate_transparent);
6393 charinfo *ci2 = tok.get_char(1);
6397 ci1->set_translation(0, translate_transparent, translate_input);
6399 ci1->set_translation(ci2, translate_transparent, translate_input);
6411 void translate_no_transparent()
6416 void translate_input()
6424 if (get_integer(&flags))
6426 charinfo *ci = tok.get_char(1);
6428 charinfo *tem = ci->get_translation();
6431 ci->set_flags(flags);
6438 void hyphenation_code()
6441 while (!tok.newline() && !tok.eof()) {
6442 charinfo *ci = tok.get_char(1);
6447 unsigned char c = tok.ch();
6449 error("hyphenation code must be ordinary character");
6453 error("hyphenation code cannot be digit");
6456 ci->set_hyphenation_code(c);
6457 if (ci->get_translation()
6458 && ci->get_translation()->get_translation_input())
6459 ci->get_translation()->set_hyphenation_code(c);
6466 void hyphenation_patterns_file_code()
6469 while (!tok.newline() && !tok.eof()) {
6471 if (get_integer(&n1) && (0 <= n1 && n1 <= 255)) {
6473 error("missing output hyphenation code");
6476 if (get_integer(&n2) && (0 <= n2 && n2 <= 255)) {
6477 hpf_code_table[n1] = n2;
6481 error("output hyphenation code must be integer in the range 0..255");
6486 error("input hyphenation code must be integer in the range 0..255");
6493 charinfo *token::get_char(int required)
6495 if (type == TOKEN_CHAR)
6496 return charset_table[c];
6497 if (type == TOKEN_SPECIAL)
6498 return get_charinfo(nm);
6499 if (type == TOKEN_NUMBERED_CHAR)
6500 return get_charinfo_by_number(val);
6501 if (type == TOKEN_ESCAPE) {
6502 if (escape_char != 0)
6503 return charset_table[escape_char];
6505 error("`\\e' used while no current escape character");
6510 if (type == TOKEN_EOF || type == TOKEN_NEWLINE)
6511 warning(WARN_MISSING, "missing normal or special character");
6513 error("normal or special character expected (got %1)", description());
6518 charinfo *get_optional_char()
6522 charinfo *ci = tok.get_char();
6524 check_missing_character();
6530 void check_missing_character()
6532 if (!tok.newline() && !tok.eof() && !tok.right_brace() && !tok.tab())
6533 error("normal or special character expected (got %1): "
6534 "treated as missing",
6540 int token::add_to_node_list(node **pp)
6547 *pp = (*pp)->add_char(charset_table[c], curenv, &w, &s);
6553 if (escape_char != 0)
6554 *pp = (*pp)->add_char(charset_table[escape_char], curenv, &w, &s);
6556 case TOKEN_HYPHEN_INDICATOR:
6557 *pp = (*pp)->add_discretionary_hyphen();
6559 case TOKEN_ITALIC_CORRECTION:
6560 *pp = (*pp)->add_italic_correction(&w);
6562 case TOKEN_LEFT_BRACE:
6564 case TOKEN_MARK_INPUT:
6565 set_number_reg(nm, curenv->get_input_line_position().to_units());
6571 case TOKEN_NUMBERED_CHAR:
6572 *pp = (*pp)->add_char(get_charinfo_by_number(val), curenv, &w, &s);
6574 case TOKEN_RIGHT_BRACE:
6577 n = new hmotion_node(curenv->get_space_width(),
6578 curenv->get_fill_color());
6581 *pp = (*pp)->add_char(get_charinfo(nm), curenv, &w, &s);
6583 case TOKEN_STRETCHABLE_SPACE:
6584 n = new unbreakable_space_node(curenv->get_space_width(),
6585 curenv->get_fill_color());
6587 case TOKEN_UNSTRETCHABLE_SPACE:
6588 n = new space_char_hmotion_node(curenv->get_space_width(),
6589 curenv->get_fill_color());
6591 case TOKEN_TRANSPARENT_DUMMY:
6592 n = new transparent_dummy_node;
6594 case TOKEN_ZERO_WIDTH_BREAK:
6595 n = new space_node(H0, curenv->get_fill_color());
6597 n->is_escape_colon();
6609 void token::process()
6611 if (possibly_handle_first_page_transition())
6614 case TOKEN_BACKSPACE:
6615 curenv->add_node(new hmotion_node(-curenv->get_space_width(),
6616 curenv->get_fill_color()));
6619 curenv->add_char(charset_table[c]);
6622 curenv->add_node(new dummy_node);
6631 if (escape_char != 0)
6632 curenv->add_char(charset_table[escape_char]);
6634 case TOKEN_BEGIN_TRAP:
6635 case TOKEN_END_TRAP:
6636 case TOKEN_PAGE_EJECTOR:
6637 // these are all handled in process_input_stack()
6639 case TOKEN_HYPHEN_INDICATOR:
6640 curenv->add_hyphen_indicator();
6642 case TOKEN_INTERRUPT:
6643 curenv->interrupt();
6645 case TOKEN_ITALIC_CORRECTION:
6646 curenv->add_italic_correction();
6649 curenv->handle_tab(1);
6651 case TOKEN_LEFT_BRACE:
6653 case TOKEN_MARK_INPUT:
6654 set_number_reg(nm, curenv->get_input_line_position().to_units());
6660 curenv->add_node(nd);
6663 case TOKEN_NUMBERED_CHAR:
6664 curenv->add_char(get_charinfo_by_number(val));
6667 // handled in process_input_stack()
6669 case TOKEN_RIGHT_BRACE:
6675 curenv->add_char(get_charinfo(nm));
6680 case TOKEN_STRETCHABLE_SPACE:
6681 curenv->add_node(new unbreakable_space_node(curenv->get_space_width(),
6682 curenv->get_fill_color()));
6684 case TOKEN_UNSTRETCHABLE_SPACE:
6685 curenv->add_node(new space_char_hmotion_node(curenv->get_space_width(),
6686 curenv->get_fill_color()));
6689 curenv->handle_tab(0);
6691 case TOKEN_TRANSPARENT:
6693 case TOKEN_TRANSPARENT_DUMMY:
6694 curenv->add_node(new transparent_dummy_node);
6696 case TOKEN_ZERO_WIDTH_BREAK:
6698 node *tmp = new space_node(H0, curenv->get_fill_color());
6699 tmp->freeze_space();
6700 tmp->is_escape_colon();
6701 curenv->add_node(tmp);
6709 class nargs_reg : public reg {
6711 const char *get_string();
6714 const char *nargs_reg::get_string()
6716 return i_to_a(input_stack::nargs());
6719 class lineno_reg : public reg {
6721 const char *get_string();
6724 const char *lineno_reg::get_string()
6728 if (!input_stack::get_location(0, &file, &line))
6730 return i_to_a(line);
6733 class writable_lineno_reg : public general_reg {
6735 writable_lineno_reg();
6736 void set_value(units);
6737 int get_value(units *);
6740 writable_lineno_reg::writable_lineno_reg()
6744 int writable_lineno_reg::get_value(units *res)
6748 if (!input_stack::get_location(0, &file, &line))
6754 void writable_lineno_reg::set_value(units n)
6756 input_stack::set_location(0, n);
6759 class filename_reg : public reg {
6761 const char *get_string();
6764 const char *filename_reg::get_string()
6768 if (input_stack::get_location(0, &file, &line))
6774 class constant_reg : public reg {
6777 constant_reg(const char *);
6778 const char *get_string();
6781 constant_reg::constant_reg(const char *p) : s(p)
6785 const char *constant_reg::get_string()
6790 constant_int_reg::constant_int_reg(int *q) : p(q)
6794 const char *constant_int_reg::get_string()
6799 void abort_request()
6804 else if (tok.newline())
6807 while ((c = get_copy(0)) == ' ')
6810 if (c == EOF || c == '\n')
6811 fputs("User Abort.", stderr);
6813 for (; c != '\n' && c != EOF; c = get_copy(0))
6814 fputs(asciify(c), stderr);
6816 fputc('\n', stderr);
6817 cleanup_and_exit(1);
6823 char *s = new char[len];
6825 while ((c = get_copy(0)) == ' ')
6828 while (c != '\n' && c != EOF) {
6829 if (!invalid_input_char(c)) {
6832 s = new char[len*2];
6833 memcpy(s, tem, len);
6853 error(".pi request not allowed in safer mode");
6857 #ifdef POPEN_MISSING
6858 error("pipes not available on this system");
6860 #else /* not POPEN_MISSING */
6862 error("can't pipe: output already started");
6867 if ((pc = read_string()) == 0)
6868 error("can't pipe to empty command");
6870 char *s = new char[strlen(pipe_command) + strlen(pc) + 1 + 1];
6871 strcpy(s, pipe_command);
6874 a_delete pipe_command;
6881 #endif /* not POPEN_MISSING */
6885 static int system_status;
6887 void system_request()
6890 error(".sy request not allowed in safer mode");
6894 char *command = read_string();
6896 error("empty command");
6898 system_status = system(command);
6906 if (curdiv == topdiv && topdiv->before_first_page) {
6907 handle_initial_request(COPY_FILE_REQUEST);
6910 symbol filename = get_long_name(1);
6911 while (!tok.newline() && !tok.eof())
6915 if (!filename.is_null())
6916 curdiv->copy_file(filename.contents());
6924 if (curdiv == topdiv && topdiv->before_first_page) {
6925 handle_initial_request(VJUSTIFY_REQUEST);
6928 symbol type = get_long_name(1);
6929 if (!type.is_null())
6930 curdiv->vjustify(type);
6936 void transparent_file()
6938 if (curdiv == topdiv && topdiv->before_first_page) {
6939 handle_initial_request(TRANSPARENT_FILE_REQUEST);
6942 symbol filename = get_long_name(1);
6943 while (!tok.newline() && !tok.eof())
6947 if (!filename.is_null()) {
6949 FILE *fp = include_search_path.open_file_cautious(filename.contents());
6951 error("can't open `%1': %2", filename.contents(), strerror(errno));
6958 if (invalid_input_char(c))
6959 warning(WARN_INPUT, "invalid input character code %1", int(c));
6961 curdiv->transparent_output(c);
6966 curdiv->transparent_output('\n');
6978 page_range(int, int, page_range *);
6979 int contains(int n);
6982 page_range::page_range(int i, int j, page_range *p)
6983 : first(i), last(j), next(p)
6987 int page_range::contains(int n)
6989 return n >= first && (last <= 0 || n <= last);
6992 page_range *output_page_list = 0;
6994 int in_output_page_list(int n)
6996 if (!output_page_list)
6998 for (page_range *p = output_page_list; p; p = p->next)
7004 static void parse_output_page_list(char *p)
7010 else if (csdigit(*p)) {
7013 i = i*10 + *p++ - '0';
7014 while (csdigit(*p));
7024 j = j*10 + *p++ - '0';
7025 while (csdigit(*p));
7031 last_page_number = -1;
7032 else if (last_page_number >= 0 && j > last_page_number)
7033 last_page_number = j;
7034 output_page_list = new page_range(i, j, output_page_list);
7040 error("bad output page list");
7041 output_page_list = 0;
7045 static FILE *open_mac_file(const char *mac, char **path)
7047 // Try first FOOBAR.tmac, then tmac.FOOBAR
7048 char *s1 = new char[strlen(mac)+strlen(MACRO_POSTFIX)+1];
7050 strcat(s1, MACRO_POSTFIX);
7051 FILE *fp = mac_path->open_file(s1, path);
7054 char *s2 = new char[strlen(mac)+strlen(MACRO_PREFIX)+1];
7055 strcpy(s2, MACRO_PREFIX);
7057 fp = mac_path->open_file(s2, path);
7063 static void process_macro_file(const char *mac)
7066 FILE *fp = open_mac_file(mac, &path);
7068 fatal("can't find macro file %1", mac);
7069 const char *s = symbol(path).contents();
7071 input_stack::push(new file_iterator(fp, s));
7073 process_input_stack();
7076 static void process_startup_file(const char *filename)
7079 search_path *orig_mac_path = mac_path;
7080 mac_path = &config_macro_path;
7081 FILE *fp = mac_path->open_file(filename, &path);
7083 input_stack::push(new file_iterator(fp, symbol(path).contents()));
7086 process_input_stack();
7088 mac_path = orig_mac_path;
7093 symbol nm = get_long_name(1);
7097 while (!tok.newline() && !tok.eof())
7100 FILE *fp = mac_path->open_file(nm.contents(), &path);
7101 // .mso doesn't (and cannot) go through open_mac_file, so we
7102 // need to do it here manually: If we have tmac.FOOBAR, try
7103 // FOOBAR.tmac and vice versa
7105 const char *fn = nm.contents();
7106 if (strncasecmp(fn, MACRO_PREFIX, sizeof(MACRO_PREFIX) - 1) == 0) {
7107 char *s = new char[strlen(fn) + sizeof(MACRO_POSTFIX)];
7108 strcpy(s, fn + sizeof(MACRO_PREFIX) - 1);
7109 strcat(s, MACRO_POSTFIX);
7110 fp = mac_path->open_file(s, &path);
7114 if (strncasecmp(fn + strlen(fn) - sizeof(MACRO_POSTFIX) + 1,
7115 MACRO_POSTFIX, sizeof(MACRO_POSTFIX) - 1) == 0) {
7116 char *s = new char[strlen(fn) + sizeof(MACRO_PREFIX)];
7117 strcpy(s, MACRO_PREFIX);
7118 strncat(s, fn, strlen(fn) - sizeof(MACRO_POSTFIX) + 1);
7119 fp = mac_path->open_file(s, &path);
7125 input_stack::push(new file_iterator(fp, symbol(path).contents()));
7129 error("can't find macro file `%1'", nm.contents());
7134 static void process_input_file(const char *name)
7137 if (strcmp(name, "-") == 0) {
7143 fp = include_search_path.open_file_cautious(name);
7145 fatal("can't open `%1': %2", name, strerror(errno));
7147 input_stack::push(new file_iterator(fp, name));
7149 process_input_stack();
7152 // make sure the_input is empty before calling this
7154 static int evaluate_expression(const char *expr, units *res)
7156 input_stack::push(make_temp_iterator(expr));
7158 int success = get_number(res, 'u');
7159 while (input_stack::get(0) != EOF)
7164 static void do_register_assignment(const char *s)
7166 const char *p = strchr(s, '=');
7172 if (evaluate_expression(s + 1, &n))
7173 set_number_reg(buf, n);
7176 char *buf = new char[p - s + 1];
7177 memcpy(buf, s, p - s);
7180 if (evaluate_expression(p + 1, &n))
7181 set_number_reg(buf, n);
7186 static void set_string(const char *name, const char *value)
7188 macro *m = new macro;
7189 for (const char *p = value; *p; p++)
7190 if (!invalid_input_char((unsigned char)*p))
7192 request_dictionary.define(name, m);
7195 static void do_string_assignment(const char *s)
7197 const char *p = strchr(s, '=');
7202 set_string(buf, s + 1);
7205 char *buf = new char[p - s + 1];
7206 memcpy(buf, s, p - s);
7208 set_string(buf, p + 1);
7213 struct string_list {
7216 string_list(const char *ss) : s(ss), next(0) {}
7220 static void prepend_string(const char *s, string_list **p)
7222 string_list *l = new string_list(s);
7228 static void add_string(const char *s, string_list **p)
7232 *p = new string_list(s);
7235 void usage(FILE *stream, const char *prog)
7238 "usage: %s -abcivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n"
7239 " -rcn -Tname -Fdir -Idir -Mdir [files...]\n",
7243 int main(int argc, char **argv)
7245 program_name = argv[0];
7246 static char stderr_buf[BUFSIZ];
7247 setbuf(stderr, stderr_buf);
7249 string_list *macros = 0;
7250 string_list *register_assignments = 0;
7251 string_list *string_assignments = 0;
7256 int no_rc = 0; // don't process troffrc and troffrc-end
7257 int next_page_number = 0; // pacify compiler
7259 hresolution = vresolution = 1;
7260 // restore $PATH if called from groff
7261 char* groff_path = getenv("GROFF_PATH__");
7268 if (putenv(strsave(e.contents())))
7269 fatal("putenv failed");
7271 static const struct option long_options[] = {
7272 { "help", no_argument, 0, CHAR_MAX + 1 },
7273 { "version", no_argument, 0, 'v' },
7276 #if defined(DEBUGGING)
7277 #define DEBUG_OPTION "D"
7279 while ((c = getopt_long(argc, argv,
7280 "abciI:vw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU"
7281 DEBUG_OPTION, long_options, 0))
7286 printf("GNU troff (groff) version %s\n", Version_string);
7291 // Search path for .psbb files
7292 // and most other non-system input files.
7293 include_search_path.command_line_dir(optarg);
7298 is_html = (strcmp(device, "html") == 0);
7301 compatible_flag = 1;
7307 macro_path.command_line_dir(optarg);
7308 safer_macro_path.command_line_dir(optarg);
7309 config_macro_path.command_line_dir(optarg);
7312 font::command_line_font_dir(optarg);
7315 add_string(optarg, ¯os);
7324 enable_warning(optarg);
7327 disable_warning(optarg);
7336 ascii_output_flag = 1;
7339 suppress_output_flag = 1;
7342 if (sscanf(optarg, "%d", &next_page_number) == 1)
7345 error("bad page number");
7348 parse_output_page_list(optarg);
7351 if (*optarg == '\0')
7352 error("`-d' requires non-empty argument");
7354 add_string(optarg, &string_assignments);
7357 if (*optarg == '\0')
7358 error("`-r' requires non-empty argument");
7360 add_string(optarg, ®ister_assignments);
7363 default_family = symbol(optarg);
7369 // silently ignore these
7372 safer_flag = 0; // unsafe behaviour
7374 #if defined(DEBUGGING)
7379 case CHAR_MAX + 1: // --help
7380 usage(stdout, argv[0]);
7384 usage(stderr, argv[0]);
7386 break; // never reached
7391 mac_path = ¯o_path;
7392 set_string(".T", device);
7393 init_charset_table();
7394 init_hpf_code_table();
7395 if (!font::load_desc())
7396 fatal("sorry, I can't continue");
7397 units_per_inch = font::res;
7398 hresolution = font::hor;
7399 vresolution = font::vert;
7400 sizescale = font::sizescale;
7401 tcommand_flag = font::tcommand;
7402 warn_scale = (double)units_per_inch;
7403 warn_scaling_indicator = 'i';
7404 if (!fflag && font::family != 0 && *font::family != '\0')
7405 default_family = symbol(font::family);
7406 font_size::init_size_table(font::sizes);
7409 if (font::style_table) {
7410 for (i = 0; font::style_table[i]; i++)
7411 mount_style(j++, symbol(font::style_table[i]));
7413 for (i = 0; font::font_name_table[i]; i++, j++)
7414 // In the DESC file a font name of 0 (zero) means leave this
7416 if (strcmp(font::font_name_table[i], "0") != 0)
7417 mount_font(j, symbol(font::font_name_table[i]));
7418 curdiv = topdiv = new top_level_diversion;
7420 topdiv->set_next_page_number(next_page_number);
7421 init_input_requests();
7422 init_env_requests();
7423 init_div_requests();
7425 init_column_requests();
7427 init_node_requests();
7428 number_reg_dictionary.define(".T", new constant_reg(tflag ? "1" : "0"));
7430 init_reg_requests();
7431 init_hyphen_requests();
7432 init_environments();
7433 while (string_assignments) {
7434 do_string_assignment(string_assignments->s);
7435 string_list *tem = string_assignments;
7436 string_assignments = string_assignments->next;
7439 while (register_assignments) {
7440 do_register_assignment(register_assignments->s);
7441 string_list *tem = register_assignments;
7442 register_assignments = register_assignments->next;
7446 process_startup_file(INITIAL_STARTUP_FILE);
7448 process_macro_file(macros->s);
7449 string_list *tem = macros;
7450 macros = macros->next;
7454 process_startup_file(FINAL_STARTUP_FILE);
7455 for (i = optind; i < argc; i++)
7456 process_input_file(argv[i]);
7457 if (optind >= argc || iflag)
7458 process_input_file("-");
7460 return 0; // not reached
7466 if (has_arg() && get_integer(&n)) {
7467 if (n & ~WARN_TOTAL) {
7468 warning(WARN_RANGE, "warning mask must be between 0 and %1", WARN_TOTAL);
7474 warning_mask = WARN_TOTAL;
7478 static void init_registers()
7480 #ifdef LONG_FOR_TIME_T
7482 #else /* not LONG_FOR_TIME_T */
7484 #endif /* not LONG_FOR_TIME_T */
7486 // Use struct here to work around misfeature in old versions of g++.
7487 struct tm *tt = localtime(&t);
7488 set_number_reg("seconds", int(tt->tm_sec));
7489 set_number_reg("minutes", int(tt->tm_min));
7490 set_number_reg("hours", int(tt->tm_hour));
7491 set_number_reg("dw", int(tt->tm_wday + 1));
7492 set_number_reg("dy", int(tt->tm_mday));
7493 set_number_reg("mo", int(tt->tm_mon + 1));
7494 set_number_reg("year", int(1900 + tt->tm_year));
7495 set_number_reg("yr", int(tt->tm_year));
7496 set_number_reg("$$", getpid());
7497 number_reg_dictionary.define(".A",
7498 new constant_reg(ascii_output_flag
7504 * registers associated with \O
7507 static int output_reg_minx_contents = -1;
7508 static int output_reg_miny_contents = -1;
7509 static int output_reg_maxx_contents = -1;
7510 static int output_reg_maxy_contents = -1;
7512 void check_output_limits(int x, int y)
7514 if ((output_reg_minx_contents == -1) || (x < output_reg_minx_contents))
7515 output_reg_minx_contents = x;
7516 if (x > output_reg_maxx_contents)
7517 output_reg_maxx_contents = x;
7518 if ((output_reg_miny_contents == -1) || (y < output_reg_miny_contents))
7519 output_reg_miny_contents = y;
7520 if (y > output_reg_maxy_contents)
7521 output_reg_maxy_contents = y;
7524 void reset_output_registers()
7526 output_reg_minx_contents = -1;
7527 output_reg_miny_contents = -1;
7528 output_reg_maxx_contents = -1;
7529 output_reg_maxy_contents = -1;
7532 void get_output_registers(int *minx, int *miny, int *maxx, int *maxy)
7534 *minx = output_reg_minx_contents;
7535 *miny = output_reg_miny_contents;
7536 *maxx = output_reg_maxx_contents;
7537 *maxy = output_reg_maxy_contents;
7540 void init_input_requests()
7542 init_request("ab", abort_request);
7543 init_request("als", alias_macro);
7544 init_request("am", append_macro);
7545 init_request("am1", append_nocomp_macro);
7546 init_request("ami", append_indirect_macro);
7547 init_request("ami1", append_indirect_nocomp_macro);
7548 init_request("as", append_string);
7549 init_request("as1", append_nocomp_string);
7550 init_request("asciify", asciify_macro);
7551 init_request("backtrace", backtrace_request);
7552 init_request("blm", blank_line_macro);
7553 init_request("break", while_break_request);
7554 init_request("cf", copy_file);
7555 init_request("cflags", char_flags);
7556 init_request("char", define_character);
7557 init_request("chop", chop_macro);
7558 init_request("close", close_request);
7559 init_request("color", activate_color);
7560 init_request("composite", composite_request);
7561 init_request("continue", while_continue_request);
7562 init_request("cp", compatible);
7563 init_request("de", define_macro);
7564 init_request("de1", define_nocomp_macro);
7565 init_request("defcolor", define_color);
7566 init_request("dei", define_indirect_macro);
7567 init_request("dei1", define_indirect_nocomp_macro);
7568 init_request("do", do_request);
7569 init_request("ds", define_string);
7570 init_request("ds1", define_nocomp_string);
7571 init_request("ec", set_escape_char);
7572 init_request("ecr", restore_escape_char);
7573 init_request("ecs", save_escape_char);
7574 init_request("el", else_request);
7575 init_request("em", end_macro);
7576 init_request("eo", escape_off);
7577 init_request("ex", exit_request);
7578 init_request("fchar", define_fallback_character);
7579 #ifdef WIDOW_CONTROL
7580 init_request("fpl", flush_pending_lines);
7581 #endif /* WIDOW_CONTROL */
7582 init_request("hcode", hyphenation_code);
7583 init_request("hpfcode", hyphenation_patterns_file_code);
7584 init_request("ie", if_else_request);
7585 init_request("if", if_request);
7586 init_request("ig", ignore);
7587 init_request("length", length_request);
7588 init_request("lf", line_file);
7589 init_request("mso", macro_source);
7590 init_request("nop", nop_request);
7591 init_request("nroff", nroff_request);
7592 init_request("nx", next_file);
7593 init_request("open", open_request);
7594 init_request("opena", opena_request);
7595 init_request("output", output_request);
7596 init_request("pc", set_page_character);
7597 init_request("pi", pipe_output);
7598 init_request("pm", print_macros);
7599 init_request("psbb", ps_bbox_request);
7600 #ifndef POPEN_MISSING
7601 init_request("pso", pipe_source);
7602 #endif /* not POPEN_MISSING */
7603 init_request("rchar", remove_character);
7604 init_request("rd", read_request);
7605 init_request("return", return_macro_request);
7606 init_request("rm", remove_macro);
7607 init_request("rn", rename_macro);
7608 init_request("schar", define_special_character);
7609 init_request("shift", shift);
7610 init_request("so", source);
7611 init_request("spreadwarn", spreadwarn_request);
7612 init_request("substring", substring_request);
7613 init_request("sy", system_request);
7614 init_request("tag", tag);
7615 init_request("taga", taga);
7616 init_request("tm", terminal);
7617 init_request("tm1", terminal1);
7618 init_request("tmc", terminal_continue);
7619 init_request("tr", translate);
7620 init_request("trf", transparent_file);
7621 init_request("trin", translate_input);
7622 init_request("trnt", translate_no_transparent);
7623 init_request("troff", troff_request);
7624 init_request("unformat", unformat_macro);
7626 init_request("vj", vjustify);
7628 init_request("warn", warn_request);
7629 init_request("warnscale", warnscale_request);
7630 init_request("while", while_request);
7631 init_request("write", write_request);
7632 init_request("writec", write_request_continue);
7633 init_request("writem", write_macro_request);
7634 number_reg_dictionary.define(".$", new nargs_reg);
7635 number_reg_dictionary.define(".C", new constant_int_reg(&compatible_flag));
7636 number_reg_dictionary.define(".c", new lineno_reg);
7637 number_reg_dictionary.define(".color", new constant_int_reg(&color_flag));
7638 number_reg_dictionary.define(".F", new filename_reg);
7639 number_reg_dictionary.define(".g", new constant_reg("1"));
7640 number_reg_dictionary.define(".H", new constant_int_reg(&hresolution));
7641 number_reg_dictionary.define(".R", new constant_reg("10000"));
7642 number_reg_dictionary.define(".U", new constant_int_reg(&safer_flag));
7643 number_reg_dictionary.define(".V", new constant_int_reg(&vresolution));
7644 number_reg_dictionary.define(".warn", new constant_int_reg(&warning_mask));
7645 extern const char *major_version;
7646 number_reg_dictionary.define(".x", new constant_reg(major_version));
7647 extern const char *revision;
7648 number_reg_dictionary.define(".Y", new constant_reg(revision));
7649 extern const char *minor_version;
7650 number_reg_dictionary.define(".y", new constant_reg(minor_version));
7651 number_reg_dictionary.define("c.", new writable_lineno_reg);
7652 number_reg_dictionary.define("llx", new variable_reg(&llx_reg_contents));
7653 number_reg_dictionary.define("lly", new variable_reg(&lly_reg_contents));
7654 number_reg_dictionary.define("opmaxx",
7655 new variable_reg(&output_reg_maxx_contents));
7656 number_reg_dictionary.define("opmaxy",
7657 new variable_reg(&output_reg_maxy_contents));
7658 number_reg_dictionary.define("opminx",
7659 new variable_reg(&output_reg_minx_contents));
7660 number_reg_dictionary.define("opminy",
7661 new variable_reg(&output_reg_miny_contents));
7662 number_reg_dictionary.define("slimit",
7663 new variable_reg(&input_stack::limit));
7664 number_reg_dictionary.define("systat", new variable_reg(&system_status));
7665 number_reg_dictionary.define("urx", new variable_reg(&urx_reg_contents));
7666 number_reg_dictionary.define("ury", new variable_reg(&ury_reg_contents));
7669 object_dictionary request_dictionary(501);
7671 void init_request(const char *s, REQUEST_FUNCP f)
7673 request_dictionary.define(s, new request(f));
7676 static request_or_macro *lookup_request(symbol nm)
7678 assert(!nm.is_null());
7679 request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm);
7681 warning(WARN_MAC, "macro `%1' not defined", nm.contents());
7683 request_dictionary.define(nm, p);
7688 node *charinfo_to_node_list(charinfo *ci, const environment *envp)
7690 // Don't interpret character definitions in compatible mode.
7691 int old_compatible_flag = compatible_flag;
7692 compatible_flag = 0;
7693 int old_escape_char = escape_char;
7695 macro *mac = ci->set_macro(0);
7697 environment *oldenv = curenv;
7698 environment env(envp);
7700 curenv->set_composite();
7701 token old_tok = tok;
7702 input_stack::add_boundary();
7703 string_iterator *si =
7704 new string_iterator(*mac, "composite character", ci->nm);
7705 input_stack::push(si);
7706 // we don't use process_input_stack, because we don't want to recognise
7712 if (tok.newline()) {
7713 error("composite character mustn't contain newline");
7721 node *n = curenv->extract_output_line();
7722 input_stack::remove_boundary();
7726 compatible_flag = old_compatible_flag;
7727 escape_char = old_escape_char;
7732 static node *read_draw_node()
7736 if (!start.delimiter(1)){
7739 } while (tok != start && !tok.newline() && !tok.eof());
7744 error("missing argument");
7746 unsigned char type = tok.ch();
7748 read_color_draw_node(start);
7753 hvpair *point = new hvpair[maxpoints];
7758 for (i = 0; tok != start; i++) {
7759 if (i == maxpoints) {
7760 hvpair *oldpoint = point;
7761 point = new hvpair[maxpoints*2];
7762 for (int j = 0; j < maxpoints; j++)
7763 point[j] = oldpoint[j];
7767 if (!get_hunits(&point[i].h,
7768 type == 'f' || type == 't' ? 'u' : 'm')) {
7779 if (!get_vunits(&point[i].v, 'v')) {
7785 while (tok != start && !tok.newline() && !tok.eof())
7790 if (npoints != 1 || no_last_v) {
7791 error("two arguments needed for line");
7796 if (npoints != 1 || !no_last_v) {
7797 error("one argument needed for circle");
7803 if (npoints != 1 || no_last_v) {
7804 error("two arguments needed for ellipse");
7809 if (npoints != 2 || no_last_v) {
7810 error("four arguments needed for arc");
7816 error("even number of arguments needed for spline");
7819 if (npoints != 1 || !no_last_v) {
7820 error("one argument needed for gray shade");
7825 // silently pass it through
7828 draw_node *dn = new draw_node(type, point, npoints,
7829 curenv->get_font_size(),
7830 curenv->get_glyph_color(),
7831 curenv->get_fill_color());
7843 static void read_color_draw_node(token &start)
7847 error("missing color scheme");
7850 unsigned char scheme = tok.ch();
7853 char end = start.ch();
7856 col = read_cmy(end);
7859 col = &default_color;
7862 col = read_gray(end);
7865 col = read_cmyk(end);
7868 col = read_rgb(end);
7872 curenv->set_fill_color(col);
7873 while (tok != start) {
7874 if (tok.newline() || tok.eof()) {
7875 warning(WARN_DELIM, "missing closing delimiter");
7876 input_stack::push(make_temp_iterator("\n"));
7887 } warning_table[] = {
7888 { "char", WARN_CHAR },
7889 { "range", WARN_RANGE },
7890 { "break", WARN_BREAK },
7891 { "delim", WARN_DELIM },
7893 { "scale", WARN_SCALE },
7894 { "number", WARN_NUMBER },
7895 { "syntax", WARN_SYNTAX },
7896 { "tab", WARN_TAB },
7897 { "right-brace", WARN_RIGHT_BRACE },
7898 { "missing", WARN_MISSING },
7899 { "input", WARN_INPUT },
7900 { "escape", WARN_ESCAPE },
7901 { "space", WARN_SPACE },
7902 { "font", WARN_FONT },
7904 { "mac", WARN_MAC },
7905 { "reg", WARN_REG },
7907 { "color", WARN_COLOR },
7908 { "all", WARN_TOTAL & ~(WARN_DI | WARN_MAC | WARN_REG) },
7909 { "w", WARN_TOTAL },
7910 { "default", DEFAULT_WARNING_MASK },
7913 static int lookup_warning(const char *name)
7915 for (unsigned int i = 0;
7916 i < sizeof(warning_table)/sizeof(warning_table[0]);
7918 if (strcmp(name, warning_table[i].name) == 0)
7919 return warning_table[i].mask;
7923 static void enable_warning(const char *name)
7925 int mask = lookup_warning(name);
7927 warning_mask |= mask;
7929 error("unknown warning `%1'", name);
7932 static void disable_warning(const char *name)
7934 int mask = lookup_warning(name);
7936 warning_mask &= ~mask;
7938 error("unknown warning `%1'", name);
7941 static void copy_mode_error(const char *format,
7947 static const char prefix[] = "(in ignored input) ";
7948 char *s = new char[sizeof(prefix) + strlen(format)];
7951 warning(WARN_IG, s, arg1, arg2, arg3);
7955 error(format, arg1, arg2, arg3);
7958 enum error_type { WARNING, OUTPUT_WARNING, ERROR, FATAL };
7960 static void do_error(error_type type,
7966 const char *filename;
7968 if (inhibit_errors && type < FATAL)
7971 input_stack::backtrace();
7972 if (!get_file_line(&filename, &lineno))
7975 errprint("%1:%2: ", filename, lineno);
7976 else if (program_name)
7977 fprintf(stderr, "%s: ", program_name);
7980 fputs("fatal error: ", stderr);
7985 fputs("warning: ", stderr);
7987 case OUTPUT_WARNING:
7988 double fromtop = topdiv->get_vertical_position().to_units() / warn_scale;
7989 fprintf(stderr, "warning [p %d, %.1f%c",
7990 topdiv->get_page_number(), fromtop, warn_scaling_indicator);
7991 if (topdiv != curdiv) {
7992 double fromtop1 = curdiv->get_vertical_position().to_units()
7994 fprintf(stderr, ", div `%s', %.1f%c",
7995 curdiv->get_diversion_name(), fromtop1, warn_scaling_indicator);
7997 fprintf(stderr, "]: ");
8000 errprint(format, arg1, arg2, arg3);
8001 fputc('\n', stderr);
8004 cleanup_and_exit(1);
8007 int warning(warning_type t,
8013 if ((t & warning_mask) != 0) {
8014 do_error(WARNING, format, arg1, arg2, arg3);
8021 int output_warning(warning_type t,
8027 if ((t & warning_mask) != 0) {
8028 do_error(OUTPUT_WARNING, format, arg1, arg2, arg3);
8035 void error(const char *format,
8040 do_error(ERROR, format, arg1, arg2, arg3);
8043 void fatal(const char *format,
8048 do_error(FATAL, format, arg1, arg2, arg3);
8051 void fatal_with_file_and_line(const char *filename, int lineno,
8057 fprintf(stderr, "%s:%d: fatal error: ", filename, lineno);
8058 errprint(format, arg1, arg2, arg3);
8059 fputc('\n', stderr);
8061 cleanup_and_exit(1);
8064 void error_with_file_and_line(const char *filename, int lineno,
8070 fprintf(stderr, "%s:%d: error: ", filename, lineno);
8071 errprint(format, arg1, arg2, arg3);
8072 fputc('\n', stderr);
8076 dictionary charinfo_dictionary(501);
8078 charinfo *get_charinfo(symbol nm)
8080 void *p = charinfo_dictionary.lookup(nm);
8082 return (charinfo *)p;
8083 charinfo *cp = new charinfo(nm);
8084 (void)charinfo_dictionary.lookup(nm, cp);
8088 int charinfo::next_index = 0;
8090 charinfo::charinfo(symbol s)
8091 : translation(0), mac(0), special_translation(TRANSLATE_NONE),
8092 hyphenation_code(0), flags(0), ascii_code(0), asciify_code(0),
8093 not_found(0), transparent_translate(1), translate_input(0),
8094 mode(CHAR_NORMAL), nm(s)
8096 index = next_index++;
8099 void charinfo::set_hyphenation_code(unsigned char c)
8101 hyphenation_code = c;
8104 void charinfo::set_translation(charinfo *ci, int tt, int ti)
8108 if (hyphenation_code != 0)
8109 ci->set_hyphenation_code(hyphenation_code);
8110 if (asciify_code != 0)
8111 ci->set_asciify_code(asciify_code);
8112 else if (ascii_code != 0)
8113 ci->set_asciify_code(ascii_code);
8114 ci->set_translation_input();
8116 special_translation = TRANSLATE_NONE;
8117 transparent_translate = tt;
8120 void charinfo::set_special_translation(int c, int tt)
8122 special_translation = c;
8124 transparent_translate = tt;
8127 void charinfo::set_ascii_code(unsigned char c)
8132 void charinfo::set_asciify_code(unsigned char c)
8137 macro *charinfo::set_macro(macro *m)
8144 macro *charinfo::setx_macro(macro *m, char_mode cm)
8152 void charinfo::set_number(int n)
8158 int charinfo::get_number()
8160 assert(flags & NUMBERED);
8164 symbol UNNAMED_SYMBOL("---");
8166 // For numbered characters not between 0 and 255, we make a symbol out
8167 // of the number and store them in this dictionary.
8169 dictionary numbered_charinfo_dictionary(11);
8171 charinfo *get_charinfo_by_number(int n)
8173 static charinfo *number_table[256];
8175 if (n >= 0 && n < 256) {
8176 charinfo *ci = number_table[n];
8178 ci = new charinfo(UNNAMED_SYMBOL);
8180 number_table[n] = ci;
8185 symbol ns(i_to_a(n));
8186 charinfo *ci = (charinfo *)numbered_charinfo_dictionary.lookup(ns);
8188 ci = new charinfo(UNNAMED_SYMBOL);
8190 (void)numbered_charinfo_dictionary.lookup(ns, ci);
8196 int font::name_to_index(const char *nm)
8200 ci = charset_table[nm[0] & 0xff];
8201 else if (nm[0] == '\\' && nm[2] == 0)
8202 ci = get_charinfo(symbol(nm + 1));
8204 ci = get_charinfo(symbol(nm));
8208 return ci->get_index();
8211 int font::number_to_index(int n)
8213 return get_charinfo_by_number(n)->get_index();