Many: use stdin support of file_case::muxer()
[s-roff.git] / src / roff / troff / input.cpp
blob238cc6702c060a9021ecac65124001f06900981f
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007, 2008
4 Free Software Foundation, Inc.
5 Written by James Clark (jjc@jclark.com)
7 This file is part of groff.
9 groff is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation; either version 2, or (at your option) any later
12 version.
14 groff is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 for more details.
19 You should have received a copy of the GNU General Public License along
20 with groff; see the file COPYING. If not, write to the Free Software
21 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
23 #define DEBUGGING
25 #include "troff.h"
26 #include "dictionary.h"
27 #include "hvunits.h"
28 #include "stringclass.h"
29 #include "mtsm.h"
30 #include "env.h"
31 #include "file_case.h"
32 #include "request.h"
33 #include "node.h"
34 #include "token.h"
35 #include "div.h"
36 #include "reg.h"
37 #include "font.h"
38 #include "charinfo.h"
39 #include "macropath.h"
40 #include "input.h"
41 #include "defs.h"
42 #include "unicode.h"
44 // Needed for getpid() and isatty()
45 #include "posix.h"
47 #include "nonposix.h"
49 #ifdef NEED_DECLARATION_PUTENV
50 extern "C" {
51 int putenv(const char *);
53 #endif /* NEED_DECLARATION_PUTENV */
55 #define MACRO_PREFIX "tmac."
56 #define MACRO_POSTFIX ".tmac"
57 #define INITIAL_STARTUP_FILE "troffrc"
58 #define FINAL_STARTUP_FILE "troffrc-end"
59 #define DEFAULT_INPUT_STACK_LIMIT 1000
61 #ifndef DEFAULT_WARNING_MASK
62 // warnings that are enabled by default
63 #define DEFAULT_WARNING_MASK \
64 (WARN_CHAR|WARN_NUMBER|WARN_BREAK|WARN_SPACE|WARN_FONT)
65 #endif
67 // initial size of buffer for reading names; expanded as necessary
68 #define ABUF_SIZE 16
70 extern "C" const char *program_name;
71 extern "C" const char *Version_string;
73 #ifdef COLUMN
74 void init_column_requests();
75 #endif /* COLUMN */
77 static node *read_draw_node();
78 static void read_color_draw_node(token &);
79 static void push_token(const token &);
80 void copy_file();
81 #ifdef COLUMN
82 void vjustify();
83 #endif /* COLUMN */
84 void transparent_file();
86 token tok;
87 int break_flag = 0;
88 int color_flag = 1; // colors are on by default
89 static int backtrace_flag = 0;
90 #ifndef POPEN_MISSING
91 char *pipe_command = 0;
92 #endif
93 charinfo *charset_table[256];
94 unsigned char hpf_code_table[256];
96 static int warning_mask = DEFAULT_WARNING_MASK;
97 static int inhibit_errors = 0;
98 static int ignoring = 0;
100 static void enable_warning(const char *);
101 static void disable_warning(const char *);
103 static int escape_char = '\\';
104 static symbol end_macro_name;
105 static symbol blank_line_macro_name;
106 static int compatible_flag = 0;
107 int ascii_output_flag = 0;
108 int suppress_output_flag = 0;
109 int is_html = 0;
110 int begin_level = 0; // number of nested \O escapes
112 int have_input = 0; // whether \f, \F, \D'F...', \H, \m, \M,
113 // \O[345], \R, \s, or \S has been processed
114 // in token::next()
115 int old_have_input = 0; // value of have_input right before \n
116 int tcommand_flag = 0;
117 int unsafe_flag = 0; // safer by default
119 int have_string_arg = 0; // whether we have \*[foo bar...]
121 double spread_limit = -3.0 - 1.0; // negative means deactivated
123 double warn_scale;
124 char warn_scaling_indicator;
125 int debug_state = 0; // turns on debugging of the html troff state
127 search_path *mac_path = &safer_macro_path;
129 // Defaults to the current directory.
130 search_path include_search_path(0, 0, 0, 1);
132 static int get_copy(node**, int = 0, int = 0);
133 static void copy_mode_error(const char *,
134 const errarg & = empty_errarg,
135 const errarg & = empty_errarg,
136 const errarg & = empty_errarg);
138 enum read_mode { ALLOW_EMPTY, WITH_ARGS, NO_ARGS };
139 static symbol read_escape_name(read_mode = NO_ARGS);
140 static symbol read_long_escape_name(read_mode = NO_ARGS);
141 static void interpolate_string(symbol);
142 static void interpolate_string_with_args(symbol);
143 static void interpolate_macro(symbol, int = 0);
144 static void interpolate_number_format(symbol);
145 static void interpolate_environment_variable(symbol);
147 static symbol composite_glyph_name(symbol);
148 static void interpolate_arg(symbol);
149 static request_or_macro *lookup_request(symbol);
150 static int get_delim_number(units *, unsigned char);
151 static int get_delim_number(units *, unsigned char, units);
152 static symbol do_get_long_name(int, char);
153 static int get_line_arg(units *res, unsigned char si, charinfo **cp);
154 static int read_size(int *);
155 static symbol get_delim_name();
156 static void init_registers();
157 static void trapping_blank_line();
159 class input_iterator;
160 input_iterator *make_temp_iterator(const char *);
161 const char *input_char_description(int);
163 void process_input_stack();
164 void chop_macro(); // declare to avoid friend name injection
167 void set_escape_char()
169 if (has_arg()) {
170 if (tok.ch() == 0) {
171 error("bad escape character");
172 escape_char = '\\';
174 else
175 escape_char = tok.ch();
177 else
178 escape_char = '\\';
179 skip_line();
182 void escape_off()
184 escape_char = 0;
185 skip_line();
188 static int saved_escape_char = '\\';
190 void save_escape_char()
192 saved_escape_char = escape_char;
193 skip_line();
196 void restore_escape_char()
198 escape_char = saved_escape_char;
199 skip_line();
202 struct arg_list;
204 class input_iterator {
205 public:
206 input_iterator();
207 input_iterator(int is_div);
208 virtual ~input_iterator() {}
209 int get(node **);
210 friend class input_stack;
211 int is_diversion;
212 statem *diversion_state;
213 protected:
214 const unsigned char *ptr;
215 const unsigned char *eptr;
216 input_iterator *next;
217 private:
218 virtual int fill(node **);
219 virtual int peek();
220 virtual int has_args() { return 0; }
221 virtual int nargs() { return 0; }
222 virtual input_iterator *get_arg(int) { return 0; }
223 virtual arg_list *get_arg_list() { return 0; }
224 virtual symbol get_macro_name() { return NULL_SYMBOL; }
225 virtual int space_follows_arg(int) { return 0; }
226 virtual int get_break_flag() { return 0; }
227 virtual int get_location(int, const char **, int *) { return 0; }
228 virtual void backtrace() {}
229 virtual int set_location(const char *, int) { return 0; }
230 virtual int next_file(file_case *, const char *) { return 0; }
231 virtual void shift(int) {}
232 virtual int is_boundary() {return 0; }
233 virtual int is_file() { return 0; }
234 virtual int is_macro() { return 0; }
235 virtual void save_compatible_flag(int) {}
236 virtual int get_compatible_flag() { return 0; }
239 input_iterator::input_iterator()
240 : is_diversion(0), ptr(0), eptr(0)
244 input_iterator::input_iterator(int is_div)
245 : is_diversion(is_div), ptr(0), eptr(0)
249 int input_iterator::fill(node **)
251 return EOF;
254 int input_iterator::peek()
256 return EOF;
259 inline int input_iterator::get(node **p)
261 return ptr < eptr ? *ptr++ : fill(p);
264 class input_boundary : public input_iterator {
265 public:
266 int is_boundary() { return 1; }
269 class input_return_boundary : public input_iterator {
270 public:
271 int is_boundary() { return 2; }
274 class file_iterator : public input_iterator {
275 file_case *_fcp;
276 int lineno;
277 const char *filename;
278 int newline_flag;
279 int seen_escape;
280 enum { BUF_SIZE = 512 };
281 unsigned char buf[BUF_SIZE];
282 void close();
283 public:
284 file_iterator(file_case *, const char *);
285 ~file_iterator();
286 int fill(node **);
287 int peek();
288 int get_location(int, const char **, int *);
289 void backtrace();
290 int set_location(const char *, int);
291 int next_file(file_case *, const char *);
292 int is_file();
295 file_iterator::file_iterator(file_case *fcp, const char *fn)
296 : _fcp(fcp), lineno(1), filename(fn), newline_flag(0), seen_escape(0)
298 if ((font::use_charnames_in_special) && (fn != 0)) {
299 if (!the_output)
300 init_output();
301 the_output->put_filename(fn, _fcp->is_pipe());
305 file_iterator::~file_iterator()
307 if (_fcp != NULL)
308 delete _fcp;
311 void file_iterator::close()
313 if (_fcp != NULL) {
314 delete _fcp;
315 _fcp = NULL;
319 int file_iterator::is_file()
321 return 1;
324 int file_iterator::next_file(file_case *fcp, const char *s)
326 close();
327 _fcp = fcp;
328 filename = s;
329 lineno = 1;
330 newline_flag = 0;
331 seen_escape = 0;
332 ptr = 0;
333 eptr = 0;
334 return 1;
337 int file_iterator::fill(node **)
339 if (newline_flag)
340 lineno++;
341 newline_flag = 0;
342 unsigned char *p = buf;
343 ptr = p;
344 unsigned char *e = p + BUF_SIZE;
345 while (p < e) {
346 int c = _fcp->get_c();
347 if (c == EOF)
348 break;
349 if (invalid_input_char(c))
350 warning(WARN_INPUT, "invalid input character code %1", int(c));
351 else {
352 *p++ = c;
353 if (c == '\n') {
354 seen_escape = 0;
355 newline_flag = 1;
356 break;
358 seen_escape = (c == '\\');
361 if (p > buf) {
362 eptr = p;
363 return *ptr++;
365 else {
366 eptr = p;
367 return EOF;
371 int file_iterator::peek()
373 int c = _fcp->get_c();
374 while (invalid_input_char(c)) {
375 warning(WARN_INPUT, "invalid input character code %1", int(c));
376 c = _fcp->get_c();
378 if (c != EOF)
379 _fcp->unget_c(c);
380 return c;
383 int file_iterator::get_location(int /*allow_macro*/,
384 const char **filenamep, int *linenop)
386 *linenop = lineno;
387 if (filename != 0 && strcmp(filename, "-") == 0)
388 *filenamep = "<standard input>";
389 else
390 *filenamep = filename;
391 return 1;
394 void file_iterator::backtrace()
396 errprint("%1:%2: backtrace: %3 `%1'\n", filename, lineno,
397 _fcp->is_pipe() ? "process" : "file");
400 int file_iterator::set_location(const char *f, int ln)
402 if (f) {
403 filename = f;
404 if (!the_output)
405 init_output();
406 the_output->put_filename(f, 0);
408 lineno = ln;
409 return 1;
412 input_iterator nil_iterator;
414 class input_stack {
415 public:
416 static int get(node **);
417 static int peek();
418 static void push(input_iterator *);
419 static input_iterator *get_arg(int);
420 static arg_list *get_arg_list();
421 static symbol get_macro_name();
422 static int space_follows_arg(int);
423 static int get_break_flag();
424 static int nargs();
425 static int get_location(int, const char **, int *);
426 static int set_location(const char *, int);
427 static void backtrace();
428 static void backtrace_all();
429 static void next_file(file_case *, const char *);
430 static void end_file();
431 static void shift(int n);
432 static void add_boundary();
433 static void add_return_boundary();
434 static int is_return_boundary();
435 static void remove_boundary();
436 static int get_level();
437 static int get_div_level();
438 static void increase_level();
439 static void decrease_level();
440 static void clear();
441 static void pop_macro();
442 static void save_compatible_flag(int);
443 static int get_compatible_flag();
444 static statem *get_diversion_state();
445 static void check_end_diversion(input_iterator *t);
446 static int limit;
447 static int div_level;
448 static statem *diversion_state;
449 private:
450 static input_iterator *top;
451 static int level;
452 static int finish_get(node **);
453 static int finish_peek();
456 input_iterator *input_stack::top = &nil_iterator;
457 int input_stack::level = 0;
458 int input_stack::limit = DEFAULT_INPUT_STACK_LIMIT;
459 int input_stack::div_level = 0;
460 statem *input_stack::diversion_state = NULL;
461 int suppress_push=0;
464 inline int input_stack::get_level()
466 return level;
469 inline void input_stack::increase_level()
471 level++;
474 inline void input_stack::decrease_level()
476 level--;
479 inline int input_stack::get_div_level()
481 return div_level;
484 inline int input_stack::get(node **np)
486 int res = (top->ptr < top->eptr) ? *top->ptr++ : finish_get(np);
487 if (res == '\n') {
488 old_have_input = have_input;
489 have_input = 0;
491 return res;
494 int input_stack::finish_get(node **np)
496 for (;;) {
497 int c = top->fill(np);
498 if (c != EOF || top->is_boundary())
499 return c;
500 if (top == &nil_iterator)
501 break;
502 input_iterator *tem = top;
503 check_end_diversion(tem);
504 #if defined(DEBUGGING)
505 if (debug_state)
506 if (tem->is_diversion)
507 fprintf(stderr,
508 "in diversion level = %d\n", input_stack::get_div_level());
509 #endif
510 top = top->next;
511 level--;
512 delete tem;
513 if (top->ptr < top->eptr)
514 return *top->ptr++;
516 assert(level == 0);
517 return EOF;
520 inline int input_stack::peek()
522 return (top->ptr < top->eptr) ? *top->ptr : finish_peek();
525 void input_stack::check_end_diversion(input_iterator *t)
527 if (t->is_diversion) {
528 div_level--;
529 diversion_state = t->diversion_state;
533 int input_stack::finish_peek()
535 for (;;) {
536 int c = top->peek();
537 if (c != EOF || top->is_boundary())
538 return c;
539 if (top == &nil_iterator)
540 break;
541 input_iterator *tem = top;
542 check_end_diversion(tem);
543 top = top->next;
544 level--;
545 delete tem;
546 if (top->ptr < top->eptr)
547 return *top->ptr;
549 assert(level == 0);
550 return EOF;
553 void input_stack::add_boundary()
555 push(new input_boundary);
558 void input_stack::add_return_boundary()
560 push(new input_return_boundary);
563 int input_stack::is_return_boundary()
565 return top->is_boundary() == 2;
568 void input_stack::remove_boundary()
570 assert(top->is_boundary());
571 input_iterator *temp = top->next;
572 check_end_diversion(top);
574 delete top;
575 top = temp;
576 level--;
579 void input_stack::push(input_iterator *in)
581 if (in == 0)
582 return;
583 if (++level > limit && limit > 0)
584 fatal("input stack limit exceeded (probable infinite loop)");
585 in->next = top;
586 top = in;
587 if (top->is_diversion) {
588 div_level++;
589 in->diversion_state = diversion_state;
590 diversion_state = curenv->construct_state(0);
591 #if defined(DEBUGGING)
592 if (debug_state) {
593 curenv->dump_troff_state();
594 fflush(stderr);
596 #endif
598 #if defined(DEBUGGING)
599 if (debug_state)
600 if (top->is_diversion) {
601 fprintf(stderr,
602 "in diversion level = %d\n", input_stack::get_div_level());
603 fflush(stderr);
605 #endif
608 statem *get_diversion_state()
610 return input_stack::get_diversion_state();
613 statem *input_stack::get_diversion_state()
615 if (diversion_state == NULL)
616 return NULL;
617 else
618 return new statem(diversion_state);
621 input_iterator *input_stack::get_arg(int i)
623 input_iterator *p;
624 for (p = top; p != 0; p = p->next)
625 if (p->has_args())
626 return p->get_arg(i);
627 return 0;
630 arg_list *input_stack::get_arg_list()
632 input_iterator *p;
633 for (p = top; p != 0; p = p->next)
634 if (p->has_args())
635 return p->get_arg_list();
636 return 0;
639 symbol input_stack::get_macro_name()
641 input_iterator *p;
642 for (p = top; p != 0; p = p->next)
643 if (p->has_args())
644 return p->get_macro_name();
645 return NULL_SYMBOL;
648 int input_stack::space_follows_arg(int i)
650 input_iterator *p;
651 for (p = top; p != 0; p = p->next)
652 if (p->has_args())
653 return p->space_follows_arg(i);
654 return 0;
657 int input_stack::get_break_flag()
659 return top->get_break_flag();
662 void input_stack::shift(int n)
664 for (input_iterator *p = top; p; p = p->next)
665 if (p->has_args()) {
666 p->shift(n);
667 return;
671 int input_stack::nargs()
673 for (input_iterator *p =top; p != 0; p = p->next)
674 if (p->has_args())
675 return p->nargs();
676 return 0;
679 int input_stack::get_location(int allow_macro, const char **filenamep, int *linenop)
681 for (input_iterator *p = top; p; p = p->next)
682 if (p->get_location(allow_macro, filenamep, linenop))
683 return 1;
684 return 0;
687 void input_stack::backtrace()
689 const char *f;
690 int n;
691 // only backtrace down to (not including) the topmost file
692 for (input_iterator *p = top;
693 p && !p->get_location(0, &f, &n);
694 p = p->next)
695 p->backtrace();
698 void input_stack::backtrace_all()
700 for (input_iterator *p = top; p; p = p->next)
701 p->backtrace();
704 int input_stack::set_location(const char *filename, int lineno)
706 for (input_iterator *p = top; p; p = p->next)
707 if (p->set_location(filename, lineno))
708 return 1;
709 return 0;
712 void input_stack::next_file(file_case *fcp, const char *s)
714 input_iterator **pp;
715 for (pp = &top; *pp != &nil_iterator; pp = &(*pp)->next)
716 if ((*pp)->next_file(fcp, s))
717 return;
718 if (++level > limit && limit > 0)
719 fatal("input stack limit exceeded");
720 *pp = new file_iterator(fcp, s);
721 (*pp)->next = &nil_iterator;
724 void input_stack::end_file()
726 for (input_iterator **pp = &top; *pp != &nil_iterator; pp = &(*pp)->next)
727 if ((*pp)->is_file()) {
728 input_iterator *tem = *pp;
729 check_end_diversion(tem);
730 *pp = (*pp)->next;
731 delete tem;
732 level--;
733 return;
737 void input_stack::clear()
739 int nboundaries = 0;
740 while (top != &nil_iterator) {
741 if (top->is_boundary())
742 nboundaries++;
743 input_iterator *tem = top;
744 check_end_diversion(tem);
745 top = top->next;
746 level--;
747 delete tem;
749 // Keep while_request happy.
750 for (; nboundaries > 0; --nboundaries)
751 add_return_boundary();
754 void input_stack::pop_macro()
756 int nboundaries = 0;
757 int is_macro = 0;
758 do {
759 if (top->next == &nil_iterator)
760 break;
761 if (top->is_boundary())
762 nboundaries++;
763 is_macro = top->is_macro();
764 input_iterator *tem = top;
765 check_end_diversion(tem);
766 top = top->next;
767 level--;
768 delete tem;
769 } while (!is_macro);
770 // Keep while_request happy.
771 for (; nboundaries > 0; --nboundaries)
772 add_return_boundary();
775 inline void input_stack::save_compatible_flag(int f)
777 top->save_compatible_flag(f);
780 inline int input_stack::get_compatible_flag()
782 return top->get_compatible_flag();
785 void backtrace_request()
787 input_stack::backtrace_all();
788 fflush(stderr);
789 skip_line();
792 void next_file()
794 symbol nm = get_long_name();
795 while (!tok.newline() && !tok.eof())
796 tok.next();
797 if (nm.is_null())
798 input_stack::end_file();
799 else {
800 file_case *fcp = include_search_path.open_file_cautious(nm.contents(),
801 fcp->fc_const_path);
802 if (fcp != NULL) {
803 input_stack::next_file(fcp, nm.contents());
804 delete fcp;
805 } else
806 error("can't open `%1': %2", nm.contents(), strerror(errno));
808 tok.next();
811 void shift()
813 int n;
814 if (!has_arg() || !get_integer(&n))
815 n = 1;
816 input_stack::shift(n);
817 skip_line();
820 static char get_char_for_escape_name(int allow_space = 0)
822 int c = get_copy(0, 0, 1);
823 switch (c) {
824 case EOF:
825 copy_mode_error("end of input in escape name");
826 return '\0';
827 default:
828 if (!invalid_input_char(c))
829 break;
830 // fall through
831 case '\n':
832 if (c == '\n')
833 input_stack::push(make_temp_iterator("\n"));
834 // fall through
835 case ' ':
836 if (c == ' ' && allow_space)
837 break;
838 // fall through
839 case '\t':
840 case '\001':
841 case '\b':
842 copy_mode_error("%1 is not allowed in an escape name",
843 input_char_description(c));
844 return '\0';
846 return c;
849 static symbol read_two_char_escape_name()
851 char buf[3];
852 buf[0] = get_char_for_escape_name();
853 if (buf[0] != '\0') {
854 buf[1] = get_char_for_escape_name();
855 if (buf[1] == '\0')
856 buf[0] = 0;
857 else
858 buf[2] = 0;
860 return symbol(buf);
863 static symbol read_long_escape_name(read_mode mode)
865 int start_level = input_stack::get_level();
866 char abuf[ABUF_SIZE];
867 char *buf = abuf;
868 int buf_size = ABUF_SIZE;
869 int i = 0;
870 char c;
871 int have_char = 0;
872 for (;;) {
873 c = get_char_for_escape_name(have_char && mode == WITH_ARGS);
874 if (c == 0) {
875 if (buf != abuf)
876 a_delete buf;
877 return NULL_SYMBOL;
879 have_char = 1;
880 if (mode == WITH_ARGS && c == ' ')
881 break;
882 if (i + 2 > buf_size) {
883 if (buf == abuf) {
884 buf = new char[ABUF_SIZE*2];
885 memcpy(buf, abuf, buf_size);
886 buf_size = ABUF_SIZE*2;
888 else {
889 char *old_buf = buf;
890 buf = new char[buf_size*2];
891 memcpy(buf, old_buf, buf_size);
892 buf_size *= 2;
893 a_delete old_buf;
896 if (c == ']' && input_stack::get_level() == start_level)
897 break;
898 buf[i++] = c;
900 buf[i] = 0;
901 if (c == ' ')
902 have_string_arg = 1;
903 if (buf == abuf) {
904 if (i == 0) {
905 if (mode != ALLOW_EMPTY)
906 copy_mode_error("empty escape name");
907 return EMPTY_SYMBOL;
909 return symbol(abuf);
911 else {
912 symbol s(buf);
913 a_delete buf;
914 return s;
918 static symbol read_escape_name(read_mode mode)
920 char c = get_char_for_escape_name();
921 if (c == 0)
922 return NULL_SYMBOL;
923 if (c == '(')
924 return read_two_char_escape_name();
925 if (c == '[' && !compatible_flag)
926 return read_long_escape_name(mode);
927 char buf[2];
928 buf[0] = c;
929 buf[1] = '\0';
930 return symbol(buf);
933 static symbol read_increment_and_escape_name(int *incp)
935 char c = get_char_for_escape_name();
936 switch (c) {
937 case 0:
938 *incp = 0;
939 return NULL_SYMBOL;
940 case '(':
941 *incp = 0;
942 return read_two_char_escape_name();
943 case '+':
944 *incp = 1;
945 return read_escape_name();
946 case '-':
947 *incp = -1;
948 return read_escape_name();
949 case '[':
950 if (!compatible_flag) {
951 *incp = 0;
952 return read_long_escape_name();
954 break;
956 *incp = 0;
957 char buf[2];
958 buf[0] = c;
959 buf[1] = '\0';
960 return symbol(buf);
963 static int get_copy(node **nd, int defining, int handle_escape_E)
965 for (;;) {
966 int c = input_stack::get(nd);
967 if (c == PUSH_GROFF_MODE) {
968 input_stack::save_compatible_flag(compatible_flag);
969 compatible_flag = 0;
970 continue;
972 if (c == PUSH_COMP_MODE) {
973 input_stack::save_compatible_flag(compatible_flag);
974 compatible_flag = 1;
975 continue;
977 if (c == POP_GROFFCOMP_MODE) {
978 compatible_flag = input_stack::get_compatible_flag();
979 continue;
981 if (c == BEGIN_QUOTE) {
982 input_stack::increase_level();
983 continue;
985 if (c == END_QUOTE) {
986 input_stack::decrease_level();
987 continue;
989 if (c == DOUBLE_QUOTE)
990 continue;
991 if (c == ESCAPE_E && handle_escape_E)
992 c = escape_char;
993 if (c == ESCAPE_NEWLINE) {
994 if (defining)
995 return c;
996 do {
997 c = input_stack::get(nd);
998 } while (c == ESCAPE_NEWLINE);
1000 if (c != escape_char || escape_char <= 0)
1001 return c;
1002 again:
1003 c = input_stack::peek();
1004 switch(c) {
1005 case 0:
1006 return escape_char;
1007 case '"':
1008 (void)input_stack::get(0);
1009 while ((c = input_stack::get(0)) != '\n' && c != EOF)
1011 return c;
1012 case '#': // Like \" but newline is ignored.
1013 (void)input_stack::get(0);
1014 while ((c = input_stack::get(0)) != '\n')
1015 if (c == EOF)
1016 return EOF;
1017 break;
1018 case '$':
1020 (void)input_stack::get(0);
1021 symbol s = read_escape_name();
1022 if (!(s.is_null() || s.is_empty()))
1023 interpolate_arg(s);
1024 break;
1026 case '*':
1028 (void)input_stack::get(0);
1029 symbol s = read_escape_name(WITH_ARGS);
1030 if (!(s.is_null() || s.is_empty())) {
1031 if (have_string_arg) {
1032 have_string_arg = 0;
1033 interpolate_string_with_args(s);
1035 else
1036 interpolate_string(s);
1038 break;
1040 case 'a':
1041 (void)input_stack::get(0);
1042 return '\001';
1043 case 'e':
1044 (void)input_stack::get(0);
1045 return ESCAPE_e;
1046 case 'E':
1047 (void)input_stack::get(0);
1048 if (handle_escape_E)
1049 goto again;
1050 return ESCAPE_E;
1051 case 'n':
1053 (void)input_stack::get(0);
1054 int inc;
1055 symbol s = read_increment_and_escape_name(&inc);
1056 if (!(s.is_null() || s.is_empty()))
1057 interpolate_number_reg(s, inc);
1058 break;
1060 case 'g':
1062 (void)input_stack::get(0);
1063 symbol s = read_escape_name();
1064 if (!(s.is_null() || s.is_empty()))
1065 interpolate_number_format(s);
1066 break;
1068 case 't':
1069 (void)input_stack::get(0);
1070 return '\t';
1071 case 'V':
1073 (void)input_stack::get(0);
1074 symbol s = read_escape_name();
1075 if (!(s.is_null() || s.is_empty()))
1076 interpolate_environment_variable(s);
1077 break;
1079 case '\n':
1080 (void)input_stack::get(0);
1081 if (defining)
1082 return ESCAPE_NEWLINE;
1083 break;
1084 case ' ':
1085 (void)input_stack::get(0);
1086 return ESCAPE_SPACE;
1087 case '~':
1088 (void)input_stack::get(0);
1089 return ESCAPE_TILDE;
1090 case ':':
1091 (void)input_stack::get(0);
1092 return ESCAPE_COLON;
1093 case '|':
1094 (void)input_stack::get(0);
1095 return ESCAPE_BAR;
1096 case '^':
1097 (void)input_stack::get(0);
1098 return ESCAPE_CIRCUMFLEX;
1099 case '{':
1100 (void)input_stack::get(0);
1101 return ESCAPE_LEFT_BRACE;
1102 case '}':
1103 (void)input_stack::get(0);
1104 return ESCAPE_RIGHT_BRACE;
1105 case '`':
1106 (void)input_stack::get(0);
1107 return ESCAPE_LEFT_QUOTE;
1108 case '\'':
1109 (void)input_stack::get(0);
1110 return ESCAPE_RIGHT_QUOTE;
1111 case '-':
1112 (void)input_stack::get(0);
1113 return ESCAPE_HYPHEN;
1114 case '_':
1115 (void)input_stack::get(0);
1116 return ESCAPE_UNDERSCORE;
1117 case 'c':
1118 (void)input_stack::get(0);
1119 return ESCAPE_c;
1120 case '!':
1121 (void)input_stack::get(0);
1122 return ESCAPE_BANG;
1123 case '?':
1124 (void)input_stack::get(0);
1125 return ESCAPE_QUESTION;
1126 case '&':
1127 (void)input_stack::get(0);
1128 return ESCAPE_AMPERSAND;
1129 case ')':
1130 (void)input_stack::get(0);
1131 return ESCAPE_RIGHT_PARENTHESIS;
1132 case '.':
1133 (void)input_stack::get(0);
1134 return c;
1135 case '%':
1136 (void)input_stack::get(0);
1137 return ESCAPE_PERCENT;
1138 default:
1139 if (c == escape_char) {
1140 (void)input_stack::get(0);
1141 return c;
1143 else
1144 return escape_char;
1149 class non_interpreted_char_node : public node {
1150 unsigned char c;
1151 public:
1152 non_interpreted_char_node(unsigned char);
1153 node *copy();
1154 int interpret(macro *);
1155 int same(node *);
1156 const char *type();
1157 int force_tprint();
1158 int is_tag();
1161 int non_interpreted_char_node::same(node *nd)
1163 return c == ((non_interpreted_char_node *)nd)->c;
1166 const char *non_interpreted_char_node::type()
1168 return "non_interpreted_char_node";
1171 int non_interpreted_char_node::force_tprint()
1173 return 0;
1176 int non_interpreted_char_node::is_tag()
1178 return 0;
1181 non_interpreted_char_node::non_interpreted_char_node(unsigned char n) : c(n)
1183 assert(n != 0);
1186 node *non_interpreted_char_node::copy()
1188 return new non_interpreted_char_node(c);
1191 int non_interpreted_char_node::interpret(macro *mac)
1193 mac->append(c);
1194 return 1;
1197 static void do_width();
1198 static node *do_non_interpreted();
1199 static node *do_special();
1200 static node *do_suppress(symbol nm);
1201 static void do_register();
1203 dictionary color_dictionary(501);
1205 static color *lookup_color(symbol nm)
1207 assert(!nm.is_null());
1208 if (nm == default_symbol)
1209 return &default_color;
1210 color *c = (color *)color_dictionary.lookup(nm);
1211 if (c == 0)
1212 warning(WARN_COLOR, "color `%1' not defined", nm.contents());
1213 return c;
1216 void do_glyph_color(symbol nm)
1218 if (nm.is_null())
1219 return;
1220 if (nm.is_empty())
1221 curenv->set_glyph_color(curenv->get_prev_glyph_color());
1222 else {
1223 color *tem = lookup_color(nm);
1224 if (tem)
1225 curenv->set_glyph_color(tem);
1226 else
1227 (void)color_dictionary.lookup(nm, new color(nm));
1231 void do_fill_color(symbol nm)
1233 if (nm.is_null())
1234 return;
1235 if (nm.is_empty())
1236 curenv->set_fill_color(curenv->get_prev_fill_color());
1237 else {
1238 color *tem = lookup_color(nm);
1239 if (tem)
1240 curenv->set_fill_color(tem);
1241 else
1242 (void)color_dictionary.lookup(nm, new color(nm));
1246 static unsigned int get_color_element(const char *scheme, const char *col)
1248 units val;
1249 if (!get_number(&val, 'f')) {
1250 warning(WARN_COLOR, "%1 in %2 definition set to 0", col, scheme);
1251 tok.next();
1252 return 0;
1254 if (val < 0) {
1255 warning(WARN_RANGE, "%1 cannot be negative: set to 0", col);
1256 return 0;
1258 if (val > color::MAX_COLOR_VAL+1) {
1259 warning(WARN_RANGE, "%1 cannot be greater than 1", col);
1260 // we change 0x10000 to 0xffff
1261 return color::MAX_COLOR_VAL;
1263 return (unsigned int)val;
1266 static color *read_rgb(char end = 0)
1268 symbol component = do_get_long_name(0, end);
1269 if (component.is_null()) {
1270 warning(WARN_COLOR, "missing rgb color values");
1271 return 0;
1273 const char *s = component.contents();
1274 color *col = new color;
1275 if (*s == '#') {
1276 if (!col->read_rgb(s)) {
1277 warning(WARN_COLOR, "expecting rgb color definition not `%1'", s);
1278 delete col;
1279 return 0;
1282 else {
1283 if (!end)
1284 input_stack::push(make_temp_iterator(" "));
1285 input_stack::push(make_temp_iterator(s));
1286 tok.next();
1287 unsigned int r = get_color_element("rgb color", "red component");
1288 unsigned int g = get_color_element("rgb color", "green component");
1289 unsigned int b = get_color_element("rgb color", "blue component");
1290 col->set_rgb(r, g, b);
1292 return col;
1295 static color *read_cmy(char end = 0)
1297 symbol component = do_get_long_name(0, end);
1298 if (component.is_null()) {
1299 warning(WARN_COLOR, "missing cmy color values");
1300 return 0;
1302 const char *s = component.contents();
1303 color *col = new color;
1304 if (*s == '#') {
1305 if (!col->read_cmy(s)) {
1306 warning(WARN_COLOR, "expecting cmy color definition not `%1'", s);
1307 delete col;
1308 return 0;
1311 else {
1312 if (!end)
1313 input_stack::push(make_temp_iterator(" "));
1314 input_stack::push(make_temp_iterator(s));
1315 tok.next();
1316 unsigned int c = get_color_element("cmy color", "cyan component");
1317 unsigned int m = get_color_element("cmy color", "magenta component");
1318 unsigned int y = get_color_element("cmy color", "yellow component");
1319 col->set_cmy(c, m, y);
1321 return col;
1324 static color *read_cmyk(char end = 0)
1326 symbol component = do_get_long_name(0, end);
1327 if (component.is_null()) {
1328 warning(WARN_COLOR, "missing cmyk color values");
1329 return 0;
1331 const char *s = component.contents();
1332 color *col = new color;
1333 if (*s == '#') {
1334 if (!col->read_cmyk(s)) {
1335 warning(WARN_COLOR, "`expecting a cmyk color definition not `%1'", s);
1336 delete col;
1337 return 0;
1340 else {
1341 if (!end)
1342 input_stack::push(make_temp_iterator(" "));
1343 input_stack::push(make_temp_iterator(s));
1344 tok.next();
1345 unsigned int c = get_color_element("cmyk color", "cyan component");
1346 unsigned int m = get_color_element("cmyk color", "magenta component");
1347 unsigned int y = get_color_element("cmyk color", "yellow component");
1348 unsigned int k = get_color_element("cmyk color", "black component");
1349 col->set_cmyk(c, m, y, k);
1351 return col;
1354 static color *read_gray(char end = 0)
1356 symbol component = do_get_long_name(0, end);
1357 if (component.is_null()) {
1358 warning(WARN_COLOR, "missing gray values");
1359 return 0;
1361 const char *s = component.contents();
1362 color *col = new color;
1363 if (*s == '#') {
1364 if (!col->read_gray(s)) {
1365 warning(WARN_COLOR, "`expecting a gray definition not `%1'", s);
1366 delete col;
1367 return 0;
1370 else {
1371 if (!end)
1372 input_stack::push(make_temp_iterator("\n"));
1373 input_stack::push(make_temp_iterator(s));
1374 tok.next();
1375 unsigned int g = get_color_element("gray", "gray value");
1376 col->set_gray(g);
1378 return col;
1381 static void activate_color()
1383 int n;
1384 if (has_arg() && get_integer(&n))
1385 color_flag = n != 0;
1386 else
1387 color_flag = 1;
1388 skip_line();
1391 static void define_color()
1393 symbol color_name = get_long_name(1);
1394 if (color_name.is_null()) {
1395 skip_line();
1396 return;
1398 if (color_name == default_symbol) {
1399 warning(WARN_COLOR, "default color can't be redefined");
1400 skip_line();
1401 return;
1403 symbol style = get_long_name(1);
1404 if (style.is_null()) {
1405 skip_line();
1406 return;
1408 color *col;
1409 if (strcmp(style.contents(), "rgb") == 0)
1410 col = read_rgb();
1411 else if (strcmp(style.contents(), "cmyk") == 0)
1412 col = read_cmyk();
1413 else if (strcmp(style.contents(), "gray") == 0)
1414 col = read_gray();
1415 else if (strcmp(style.contents(), "grey") == 0)
1416 col = read_gray();
1417 else if (strcmp(style.contents(), "cmy") == 0)
1418 col = read_cmy();
1419 else {
1420 warning(WARN_COLOR,
1421 "unknown color space `%1'; use rgb, cmyk, gray or cmy",
1422 style.contents());
1423 skip_line();
1424 return;
1426 if (col) {
1427 col->nm = color_name;
1428 (void)color_dictionary.lookup(color_name, col);
1430 skip_line();
1433 static node *do_overstrike()
1435 token start;
1436 overstrike_node *on = new overstrike_node;
1437 int start_level = input_stack::get_level();
1438 start.next();
1439 for (;;) {
1440 tok.next();
1441 if (tok.newline() || tok.eof()) {
1442 warning(WARN_DELIM, "missing closing delimiter");
1443 input_stack::push(make_temp_iterator("\n"));
1444 break;
1446 if (tok == start
1447 && (compatible_flag || input_stack::get_level() == start_level))
1448 break;
1449 charinfo *ci = tok.get_char(1);
1450 if (ci) {
1451 node *n = curenv->make_char_node(ci);
1452 if (n)
1453 on->overstrike(n);
1456 return on;
1459 static node *do_bracket()
1461 token start;
1462 bracket_node *bn = new bracket_node;
1463 start.next();
1464 int start_level = input_stack::get_level();
1465 for (;;) {
1466 tok.next();
1467 if (tok.eof()) {
1468 warning(WARN_DELIM, "missing closing delimiter");
1469 break;
1471 if (tok.newline()) {
1472 warning(WARN_DELIM, "missing closing delimiter");
1473 input_stack::push(make_temp_iterator("\n"));
1474 break;
1476 if (tok == start
1477 && (compatible_flag || input_stack::get_level() == start_level))
1478 break;
1479 charinfo *ci = tok.get_char(1);
1480 if (ci) {
1481 node *n = curenv->make_char_node(ci);
1482 if (n)
1483 bn->bracket(n);
1486 return bn;
1489 static int do_name_test()
1491 token start;
1492 start.next();
1493 int start_level = input_stack::get_level();
1494 int bad_char = 0;
1495 int some_char = 0;
1496 for (;;) {
1497 tok.next();
1498 if (tok.newline() || tok.eof()) {
1499 warning(WARN_DELIM, "missing closing delimiter");
1500 input_stack::push(make_temp_iterator("\n"));
1501 break;
1503 if (tok == start
1504 && (compatible_flag || input_stack::get_level() == start_level))
1505 break;
1506 if (!tok.ch())
1507 bad_char = 1;
1508 some_char = 1;
1510 return some_char && !bad_char;
1513 static int do_expr_test()
1515 token start;
1516 start.next();
1517 int start_level = input_stack::get_level();
1518 if (!start.delimiter(1))
1519 return 0;
1520 tok.next();
1521 // disable all warning and error messages temporarily
1522 int saved_warning_mask = warning_mask;
1523 int saved_inhibit_errors = inhibit_errors;
1524 warning_mask = 0;
1525 inhibit_errors = 1;
1526 int dummy;
1527 int result = get_number_rigidly(&dummy, 'u');
1528 warning_mask = saved_warning_mask;
1529 inhibit_errors = saved_inhibit_errors;
1530 if (tok == start && input_stack::get_level() == start_level)
1531 return result;
1532 // ignore everything up to the delimiter in case we aren't right there
1533 for (;;) {
1534 tok.next();
1535 if (tok.newline() || tok.eof()) {
1536 warning(WARN_DELIM, "missing closing delimiter");
1537 input_stack::push(make_temp_iterator("\n"));
1538 break;
1540 if (tok == start && input_stack::get_level() == start_level)
1541 break;
1543 return 0;
1546 #if 0
1547 static node *do_zero_width()
1549 token start;
1550 start.next();
1551 int start_level = input_stack::get_level();
1552 environment env(curenv);
1553 environment *oldenv = curenv;
1554 curenv = &env;
1555 for (;;) {
1556 tok.next();
1557 if (tok.newline() || tok.eof()) {
1558 error("missing closing delimiter");
1559 break;
1561 if (tok == start
1562 && (compatible_flag || input_stack::get_level() == start_level))
1563 break;
1564 tok.process();
1566 curenv = oldenv;
1567 node *rev = env.extract_output_line();
1568 node *n = 0;
1569 while (rev) {
1570 node *tem = rev;
1571 rev = rev->next;
1572 tem->next = n;
1573 n = tem;
1575 return new zero_width_node(n);
1578 #else
1580 // It's undesirable for \Z to change environments, because then
1581 // \n(.w won't work as expected.
1583 static node *do_zero_width()
1585 node *rev = new dummy_node;
1586 token start;
1587 start.next();
1588 int start_level = input_stack::get_level();
1589 for (;;) {
1590 tok.next();
1591 if (tok.newline() || tok.eof()) {
1592 warning(WARN_DELIM, "missing closing delimiter");
1593 input_stack::push(make_temp_iterator("\n"));
1594 break;
1596 if (tok == start
1597 && (compatible_flag || input_stack::get_level() == start_level))
1598 break;
1599 if (!tok.add_to_node_list(&rev))
1600 error("invalid token in argument to \\Z");
1602 node *n = 0;
1603 while (rev) {
1604 node *tem = rev;
1605 rev = rev->next;
1606 tem->next = n;
1607 n = tem;
1609 return new zero_width_node(n);
1612 #endif
1614 token_node *node::get_token_node()
1616 return 0;
1619 class token_node : public node {
1620 public:
1621 token tk;
1622 token_node(const token &t);
1623 node *copy();
1624 token_node *get_token_node();
1625 int same(node *);
1626 const char *type();
1627 int force_tprint();
1628 int is_tag();
1631 token_node::token_node(const token &t) : tk(t)
1635 node *token_node::copy()
1637 return new token_node(tk);
1640 token_node *token_node::get_token_node()
1642 return this;
1645 int token_node::same(node *nd)
1647 return tk == ((token_node *)nd)->tk;
1650 const char *token_node::type()
1652 return "token_node";
1655 int token_node::force_tprint()
1657 return 0;
1660 int token_node::is_tag()
1662 return 0;
1665 token::token() : nd(0), type(TOKEN_EMPTY)
1669 token::~token()
1671 delete nd;
1674 token::token(const token &t)
1675 : nm(t.nm), c(t.c), val(t.val), dim(t.dim), type(t.type)
1677 // Use two statements to work around bug in SGI C++.
1678 node *tem = t.nd;
1679 nd = tem ? tem->copy() : 0;
1682 void token::operator=(const token &t)
1684 delete nd;
1685 nm = t.nm;
1686 // Use two statements to work around bug in SGI C++.
1687 node *tem = t.nd;
1688 nd = tem ? tem->copy() : 0;
1689 c = t.c;
1690 val = t.val;
1691 dim = t.dim;
1692 type = t.type;
1695 void token::skip()
1697 while (space())
1698 next();
1701 int has_arg()
1703 while (tok.space())
1704 tok.next();
1705 return !tok.newline();
1708 void token::make_space()
1710 type = TOKEN_SPACE;
1713 void token::make_newline()
1715 type = TOKEN_NEWLINE;
1718 void token::next()
1720 if (nd) {
1721 delete nd;
1722 nd = 0;
1724 units x;
1725 for (;;) {
1726 node *n = 0;
1727 int cc = input_stack::get(&n);
1728 if (cc != escape_char || escape_char == 0) {
1729 handle_normal_char:
1730 switch(cc) {
1731 case PUSH_GROFF_MODE:
1732 input_stack::save_compatible_flag(compatible_flag);
1733 compatible_flag = 0;
1734 continue;
1735 case PUSH_COMP_MODE:
1736 input_stack::save_compatible_flag(compatible_flag);
1737 compatible_flag = 1;
1738 continue;
1739 case POP_GROFFCOMP_MODE:
1740 compatible_flag = input_stack::get_compatible_flag();
1741 continue;
1742 case BEGIN_QUOTE:
1743 input_stack::increase_level();
1744 continue;
1745 case END_QUOTE:
1746 input_stack::decrease_level();
1747 continue;
1748 case DOUBLE_QUOTE:
1749 continue;
1750 case EOF:
1751 type = TOKEN_EOF;
1752 return;
1753 case TRANSPARENT_FILE_REQUEST:
1754 case TITLE_REQUEST:
1755 case COPY_FILE_REQUEST:
1756 #ifdef COLUMN
1757 case VJUSTIFY_REQUEST:
1758 #endif /* COLUMN */
1759 type = TOKEN_REQUEST;
1760 c = cc;
1761 return;
1762 case BEGIN_TRAP:
1763 type = TOKEN_BEGIN_TRAP;
1764 return;
1765 case END_TRAP:
1766 type = TOKEN_END_TRAP;
1767 return;
1768 case LAST_PAGE_EJECTOR:
1769 seen_last_page_ejector = 1;
1770 // fall through
1771 case PAGE_EJECTOR:
1772 type = TOKEN_PAGE_EJECTOR;
1773 return;
1774 case ESCAPE_PERCENT:
1775 ESCAPE_PERCENT:
1776 type = TOKEN_HYPHEN_INDICATOR;
1777 return;
1778 case ESCAPE_SPACE:
1779 ESCAPE_SPACE:
1780 type = TOKEN_UNSTRETCHABLE_SPACE;
1781 return;
1782 case ESCAPE_TILDE:
1783 ESCAPE_TILDE:
1784 type = TOKEN_STRETCHABLE_SPACE;
1785 return;
1786 case ESCAPE_COLON:
1787 ESCAPE_COLON:
1788 type = TOKEN_ZERO_WIDTH_BREAK;
1789 return;
1790 case ESCAPE_e:
1791 ESCAPE_e:
1792 type = TOKEN_ESCAPE;
1793 return;
1794 case ESCAPE_E:
1795 goto handle_escape_char;
1796 case ESCAPE_BAR:
1797 ESCAPE_BAR:
1798 type = TOKEN_NODE;
1799 nd = new hmotion_node(curenv->get_narrow_space_width(),
1800 curenv->get_fill_color());
1801 return;
1802 case ESCAPE_CIRCUMFLEX:
1803 ESCAPE_CIRCUMFLEX:
1804 type = TOKEN_NODE;
1805 nd = new hmotion_node(curenv->get_half_narrow_space_width(),
1806 curenv->get_fill_color());
1807 return;
1808 case ESCAPE_NEWLINE:
1809 have_input = 0;
1810 break;
1811 case ESCAPE_LEFT_BRACE:
1812 ESCAPE_LEFT_BRACE:
1813 type = TOKEN_LEFT_BRACE;
1814 return;
1815 case ESCAPE_RIGHT_BRACE:
1816 ESCAPE_RIGHT_BRACE:
1817 type = TOKEN_RIGHT_BRACE;
1818 return;
1819 case ESCAPE_LEFT_QUOTE:
1820 ESCAPE_LEFT_QUOTE:
1821 type = TOKEN_SPECIAL;
1822 nm = symbol("ga");
1823 return;
1824 case ESCAPE_RIGHT_QUOTE:
1825 ESCAPE_RIGHT_QUOTE:
1826 type = TOKEN_SPECIAL;
1827 nm = symbol("aa");
1828 return;
1829 case ESCAPE_HYPHEN:
1830 ESCAPE_HYPHEN:
1831 type = TOKEN_SPECIAL;
1832 nm = symbol("-");
1833 return;
1834 case ESCAPE_UNDERSCORE:
1835 ESCAPE_UNDERSCORE:
1836 type = TOKEN_SPECIAL;
1837 nm = symbol("ul");
1838 return;
1839 case ESCAPE_c:
1840 ESCAPE_c:
1841 type = TOKEN_INTERRUPT;
1842 return;
1843 case ESCAPE_BANG:
1844 ESCAPE_BANG:
1845 type = TOKEN_TRANSPARENT;
1846 return;
1847 case ESCAPE_QUESTION:
1848 ESCAPE_QUESTION:
1849 nd = do_non_interpreted();
1850 if (nd) {
1851 type = TOKEN_NODE;
1852 return;
1854 break;
1855 case ESCAPE_AMPERSAND:
1856 ESCAPE_AMPERSAND:
1857 type = TOKEN_DUMMY;
1858 return;
1859 case ESCAPE_RIGHT_PARENTHESIS:
1860 ESCAPE_RIGHT_PARENTHESIS:
1861 type = TOKEN_TRANSPARENT_DUMMY;
1862 return;
1863 case '\b':
1864 type = TOKEN_BACKSPACE;
1865 return;
1866 case ' ':
1867 type = TOKEN_SPACE;
1868 return;
1869 case '\t':
1870 type = TOKEN_TAB;
1871 return;
1872 case '\n':
1873 type = TOKEN_NEWLINE;
1874 return;
1875 case '\001':
1876 type = TOKEN_LEADER;
1877 return;
1878 case 0:
1880 assert(n != 0);
1881 token_node *tn = n->get_token_node();
1882 if (tn) {
1883 *this = tn->tk;
1884 delete tn;
1886 else {
1887 nd = n;
1888 type = TOKEN_NODE;
1891 return;
1892 default:
1893 type = TOKEN_CHAR;
1894 c = cc;
1895 return;
1898 else {
1899 handle_escape_char:
1900 cc = input_stack::get(&n);
1901 switch(cc) {
1902 case '(':
1903 nm = read_two_char_escape_name();
1904 type = TOKEN_SPECIAL;
1905 return;
1906 case EOF:
1907 type = TOKEN_EOF;
1908 error("end of input after escape character");
1909 return;
1910 case '`':
1911 goto ESCAPE_LEFT_QUOTE;
1912 case '\'':
1913 goto ESCAPE_RIGHT_QUOTE;
1914 case '-':
1915 goto ESCAPE_HYPHEN;
1916 case '_':
1917 goto ESCAPE_UNDERSCORE;
1918 case '%':
1919 goto ESCAPE_PERCENT;
1920 case ' ':
1921 goto ESCAPE_SPACE;
1922 case '0':
1923 nd = new hmotion_node(curenv->get_digit_width(),
1924 curenv->get_fill_color());
1925 type = TOKEN_NODE;
1926 return;
1927 case '|':
1928 goto ESCAPE_BAR;
1929 case '^':
1930 goto ESCAPE_CIRCUMFLEX;
1931 case '/':
1932 type = TOKEN_ITALIC_CORRECTION;
1933 return;
1934 case ',':
1935 type = TOKEN_NODE;
1936 nd = new left_italic_corrected_node;
1937 return;
1938 case '&':
1939 goto ESCAPE_AMPERSAND;
1940 case ')':
1941 goto ESCAPE_RIGHT_PARENTHESIS;
1942 case '!':
1943 goto ESCAPE_BANG;
1944 case '?':
1945 goto ESCAPE_QUESTION;
1946 case '~':
1947 goto ESCAPE_TILDE;
1948 case ':':
1949 goto ESCAPE_COLON;
1950 case '"':
1951 while ((cc = input_stack::get(0)) != '\n' && cc != EOF)
1953 if (cc == '\n')
1954 type = TOKEN_NEWLINE;
1955 else
1956 type = TOKEN_EOF;
1957 return;
1958 case '#': // Like \" but newline is ignored.
1959 while ((cc = input_stack::get(0)) != '\n')
1960 if (cc == EOF) {
1961 type = TOKEN_EOF;
1962 return;
1964 break;
1965 case '$':
1967 symbol s = read_escape_name();
1968 if (!(s.is_null() || s.is_empty()))
1969 interpolate_arg(s);
1970 break;
1972 case '*':
1974 symbol s = read_escape_name(WITH_ARGS);
1975 if (!(s.is_null() || s.is_empty())) {
1976 if (have_string_arg) {
1977 have_string_arg = 0;
1978 interpolate_string_with_args(s);
1980 else
1981 interpolate_string(s);
1983 break;
1985 case 'a':
1986 nd = new non_interpreted_char_node('\001');
1987 type = TOKEN_NODE;
1988 return;
1989 case 'A':
1990 c = '0' + do_name_test();
1991 type = TOKEN_CHAR;
1992 return;
1993 case 'b':
1994 nd = do_bracket();
1995 type = TOKEN_NODE;
1996 return;
1997 case 'B':
1998 c = '0' + do_expr_test();
1999 type = TOKEN_CHAR;
2000 return;
2001 case 'c':
2002 goto ESCAPE_c;
2003 case 'C':
2004 nm = get_delim_name();
2005 if (nm.is_null())
2006 break;
2007 type = TOKEN_SPECIAL;
2008 return;
2009 case 'd':
2010 type = TOKEN_NODE;
2011 nd = new vmotion_node(curenv->get_size() / 2,
2012 curenv->get_fill_color());
2013 return;
2014 case 'D':
2015 nd = read_draw_node();
2016 if (!nd)
2017 break;
2018 type = TOKEN_NODE;
2019 return;
2020 case 'e':
2021 goto ESCAPE_e;
2022 case 'E':
2023 goto handle_escape_char;
2024 case 'f':
2026 symbol s = read_escape_name(ALLOW_EMPTY);
2027 if (s.is_null())
2028 break;
2029 const char *p;
2030 for (p = s.contents(); *p != '\0'; p++)
2031 if (!csdigit(*p))
2032 break;
2033 if (*p || s.is_empty())
2034 curenv->set_font(s);
2035 else
2036 curenv->set_font(atoi(s.contents()));
2037 if (!compatible_flag)
2038 have_input = 1;
2039 break;
2041 case 'F':
2043 symbol s = read_escape_name(ALLOW_EMPTY);
2044 if (s.is_null())
2045 break;
2046 curenv->set_family(s);
2047 have_input = 1;
2048 break;
2050 case 'g':
2052 symbol s = read_escape_name();
2053 if (!(s.is_null() || s.is_empty()))
2054 interpolate_number_format(s);
2055 break;
2057 case 'h':
2058 if (!get_delim_number(&x, 'm'))
2059 break;
2060 type = TOKEN_NODE;
2061 nd = new hmotion_node(x, curenv->get_fill_color());
2062 return;
2063 case 'H':
2064 // don't take height increments relative to previous height if
2065 // in compatibility mode
2066 if (!compatible_flag && curenv->get_char_height()) {
2067 if (get_delim_number(&x, 'z', curenv->get_char_height()))
2068 curenv->set_char_height(x);
2070 else {
2071 if (get_delim_number(&x, 'z', curenv->get_requested_point_size()))
2072 curenv->set_char_height(x);
2074 if (!compatible_flag)
2075 have_input = 1;
2076 break;
2077 case 'k':
2078 nm = read_escape_name();
2079 if (nm.is_null() || nm.is_empty())
2080 break;
2081 type = TOKEN_MARK_INPUT;
2082 return;
2083 case 'l':
2084 case 'L':
2086 charinfo *s = 0;
2087 if (!get_line_arg(&x, (cc == 'l' ? 'm': 'v'), &s))
2088 break;
2089 if (s == 0)
2090 s = get_charinfo(cc == 'l' ? "ru" : "br");
2091 type = TOKEN_NODE;
2092 node *char_node = curenv->make_char_node(s);
2093 if (cc == 'l')
2094 nd = new hline_node(x, char_node);
2095 else
2096 nd = new vline_node(x, char_node);
2097 return;
2099 case 'm':
2100 do_glyph_color(read_escape_name(ALLOW_EMPTY));
2101 if (!compatible_flag)
2102 have_input = 1;
2103 break;
2104 case 'M':
2105 do_fill_color(read_escape_name(ALLOW_EMPTY));
2106 if (!compatible_flag)
2107 have_input = 1;
2108 break;
2109 case 'n':
2111 int inc;
2112 symbol s = read_increment_and_escape_name(&inc);
2113 if (!(s.is_null() || s.is_empty()))
2114 interpolate_number_reg(s, inc);
2115 break;
2117 case 'N':
2118 if (!get_delim_number(&val, 0))
2119 break;
2120 if (val < 0) {
2121 warning(WARN_CHAR, "invalid numbered character %1", val);
2122 break;
2124 type = TOKEN_NUMBERED_CHAR;
2125 return;
2126 case 'o':
2127 nd = do_overstrike();
2128 type = TOKEN_NODE;
2129 return;
2130 case 'O':
2131 nd = do_suppress(read_escape_name());
2132 if (!nd)
2133 break;
2134 type = TOKEN_NODE;
2135 return;
2136 case 'p':
2137 type = TOKEN_SPREAD;
2138 return;
2139 case 'r':
2140 type = TOKEN_NODE;
2141 nd = new vmotion_node(-curenv->get_size(), curenv->get_fill_color());
2142 return;
2143 case 'R':
2144 do_register();
2145 if (!compatible_flag)
2146 have_input = 1;
2147 break;
2148 case 's':
2149 if (read_size(&x))
2150 curenv->set_size(x);
2151 if (!compatible_flag)
2152 have_input = 1;
2153 break;
2154 case 'S':
2155 if (get_delim_number(&x, 0))
2156 curenv->set_char_slant(x);
2157 if (!compatible_flag)
2158 have_input = 1;
2159 break;
2160 case 't':
2161 type = TOKEN_NODE;
2162 nd = new non_interpreted_char_node('\t');
2163 return;
2164 case 'u':
2165 type = TOKEN_NODE;
2166 nd = new vmotion_node(-curenv->get_size() / 2,
2167 curenv->get_fill_color());
2168 return;
2169 case 'v':
2170 if (!get_delim_number(&x, 'v'))
2171 break;
2172 type = TOKEN_NODE;
2173 nd = new vmotion_node(x, curenv->get_fill_color());
2174 return;
2175 case 'V':
2177 symbol s = read_escape_name();
2178 if (!(s.is_null() || s.is_empty()))
2179 interpolate_environment_variable(s);
2180 break;
2182 case 'w':
2183 do_width();
2184 break;
2185 case 'x':
2186 if (!get_delim_number(&x, 'v'))
2187 break;
2188 type = TOKEN_NODE;
2189 nd = new extra_size_node(x);
2190 return;
2191 case 'X':
2192 nd = do_special();
2193 if (!nd)
2194 break;
2195 type = TOKEN_NODE;
2196 return;
2197 case 'Y':
2199 symbol s = read_escape_name();
2200 if (s.is_null() || s.is_empty())
2201 break;
2202 request_or_macro *p = lookup_request(s);
2203 macro *m = p->to_macro();
2204 if (!m) {
2205 error("can't transparently throughput a request");
2206 break;
2208 nd = new special_node(*m);
2209 type = TOKEN_NODE;
2210 return;
2212 case 'z':
2214 next();
2215 if (type == TOKEN_NODE)
2216 nd = new zero_width_node(nd);
2217 else {
2218 charinfo *ci = get_char(1);
2219 if (ci == 0)
2220 break;
2221 node *gn = curenv->make_char_node(ci);
2222 if (gn == 0)
2223 break;
2224 nd = new zero_width_node(gn);
2225 type = TOKEN_NODE;
2227 return;
2229 case 'Z':
2230 nd = do_zero_width();
2231 if (nd == 0)
2232 break;
2233 type = TOKEN_NODE;
2234 return;
2235 case '{':
2236 goto ESCAPE_LEFT_BRACE;
2237 case '}':
2238 goto ESCAPE_RIGHT_BRACE;
2239 case '\n':
2240 break;
2241 case '[':
2242 if (!compatible_flag) {
2243 symbol s = read_long_escape_name(WITH_ARGS);
2244 if (s.is_null() || s.is_empty())
2245 break;
2246 if (have_string_arg) {
2247 have_string_arg = 0;
2248 nm = composite_glyph_name(s);
2250 else {
2251 const char *gn = check_unicode_name(s.contents());
2252 if (gn) {
2253 const char *gn_decomposed = decompose_unicode(gn);
2254 if (gn_decomposed)
2255 gn = &gn_decomposed[1];
2256 const char *groff_gn = unicode_to_glyph_name(gn);
2257 if (groff_gn)
2258 nm = symbol(groff_gn);
2259 else {
2260 char *buf = new char[strlen(gn) + 1 + 1];
2261 strcpy(buf, "u");
2262 strcat(buf, gn);
2263 nm = symbol(buf);
2264 a_delete buf;
2267 else
2268 nm = symbol(s.contents());
2270 type = TOKEN_SPECIAL;
2271 return;
2273 goto handle_normal_char;
2274 default:
2275 if (cc != escape_char && cc != '.')
2276 warning(WARN_ESCAPE, "escape character ignored before %1",
2277 input_char_description(cc));
2278 goto handle_normal_char;
2284 int token::operator==(const token &t)
2286 if (type != t.type)
2287 return 0;
2288 switch(type) {
2289 case TOKEN_CHAR:
2290 return c == t.c;
2291 case TOKEN_SPECIAL:
2292 return nm == t.nm;
2293 case TOKEN_NUMBERED_CHAR:
2294 return val == t.val;
2295 default:
2296 return 1;
2300 int token::operator!=(const token &t)
2302 return !(*this == t);
2305 // is token a suitable delimiter (like ')?
2307 int token::delimiter(int err)
2309 switch(type) {
2310 case TOKEN_CHAR:
2311 switch(c) {
2312 case '0':
2313 case '1':
2314 case '2':
2315 case '3':
2316 case '4':
2317 case '5':
2318 case '6':
2319 case '7':
2320 case '8':
2321 case '9':
2322 case '+':
2323 case '-':
2324 case '/':
2325 case '*':
2326 case '%':
2327 case '<':
2328 case '>':
2329 case '=':
2330 case '&':
2331 case ':':
2332 case '(':
2333 case ')':
2334 case '.':
2335 if (err)
2336 error("cannot use character `%1' as a starting delimiter", char(c));
2337 return 0;
2338 default:
2339 return 1;
2341 case TOKEN_NODE:
2342 case TOKEN_SPACE:
2343 case TOKEN_STRETCHABLE_SPACE:
2344 case TOKEN_UNSTRETCHABLE_SPACE:
2345 case TOKEN_TAB:
2346 case TOKEN_NEWLINE:
2347 if (err)
2348 error("cannot use %1 as a starting delimiter", description());
2349 return 0;
2350 default:
2351 return 1;
2355 const char *token::description()
2357 static char buf[4];
2358 switch (type) {
2359 case TOKEN_BACKSPACE:
2360 return "a backspace character";
2361 case TOKEN_CHAR:
2362 buf[0] = '`';
2363 buf[1] = c;
2364 buf[2] = '\'';
2365 buf[3] = '\0';
2366 return buf;
2367 case TOKEN_DUMMY:
2368 return "`\\&'";
2369 case TOKEN_ESCAPE:
2370 return "`\\e'";
2371 case TOKEN_HYPHEN_INDICATOR:
2372 return "`\\%'";
2373 case TOKEN_INTERRUPT:
2374 return "`\\c'";
2375 case TOKEN_ITALIC_CORRECTION:
2376 return "`\\/'";
2377 case TOKEN_LEADER:
2378 return "a leader character";
2379 case TOKEN_LEFT_BRACE:
2380 return "`\\{'";
2381 case TOKEN_MARK_INPUT:
2382 return "`\\k'";
2383 case TOKEN_NEWLINE:
2384 return "newline";
2385 case TOKEN_NODE:
2386 return "a node";
2387 case TOKEN_NUMBERED_CHAR:
2388 return "`\\N'";
2389 case TOKEN_RIGHT_BRACE:
2390 return "`\\}'";
2391 case TOKEN_SPACE:
2392 return "a space";
2393 case TOKEN_SPECIAL:
2394 return "a special character";
2395 case TOKEN_SPREAD:
2396 return "`\\p'";
2397 case TOKEN_STRETCHABLE_SPACE:
2398 return "`\\~'";
2399 case TOKEN_UNSTRETCHABLE_SPACE:
2400 return "`\\ '";
2401 case TOKEN_TAB:
2402 return "a tab character";
2403 case TOKEN_TRANSPARENT:
2404 return "`\\!'";
2405 case TOKEN_TRANSPARENT_DUMMY:
2406 return "`\\)'";
2407 case TOKEN_ZERO_WIDTH_BREAK:
2408 return "`\\:'";
2409 case TOKEN_EOF:
2410 return "end of input";
2411 default:
2412 break;
2414 return "a magic token";
2417 void skip_line()
2419 while (!tok.newline())
2420 if (tok.eof())
2421 return;
2422 else
2423 tok.next();
2424 tok.next();
2427 void compatible()
2429 int n;
2430 if (has_arg() && get_integer(&n))
2431 compatible_flag = n != 0;
2432 else
2433 compatible_flag = 1;
2434 skip_line();
2437 static void empty_name_warning(int required)
2439 if (tok.newline() || tok.eof()) {
2440 if (required)
2441 warning(WARN_MISSING, "missing name");
2443 else if (tok.right_brace() || tok.tab()) {
2444 const char *start = tok.description();
2445 do {
2446 tok.next();
2447 } while (tok.space() || tok.right_brace() || tok.tab());
2448 if (!tok.newline() && !tok.eof())
2449 error("%1 is not allowed before an argument", start);
2450 else if (required)
2451 warning(WARN_MISSING, "missing name");
2453 else if (required)
2454 error("name expected (got %1)", tok.description());
2455 else
2456 error("name expected (got %1): treated as missing", tok.description());
2459 static void non_empty_name_warning()
2461 if (!tok.newline() && !tok.eof() && !tok.space() && !tok.tab()
2462 && !tok.right_brace()
2463 // We don't want to give a warning for .el\{
2464 && !tok.left_brace())
2465 error("%1 is not allowed in a name", tok.description());
2468 symbol get_name(int required)
2470 if (compatible_flag) {
2471 char buf[3];
2472 tok.skip();
2473 if ((buf[0] = tok.ch()) != 0) {
2474 tok.next();
2475 if ((buf[1] = tok.ch()) != 0) {
2476 buf[2] = 0;
2477 tok.make_space();
2479 else
2480 non_empty_name_warning();
2481 return symbol(buf);
2483 else {
2484 empty_name_warning(required);
2485 return NULL_SYMBOL;
2488 else
2489 return get_long_name(required);
2492 symbol get_long_name(int required)
2494 return do_get_long_name(required, 0);
2497 static symbol do_get_long_name(int required, char end)
2499 while (tok.space())
2500 tok.next();
2501 char abuf[ABUF_SIZE];
2502 char *buf = abuf;
2503 int buf_size = ABUF_SIZE;
2504 int i = 0;
2505 for (;;) {
2506 // If end != 0 we normally have to append a null byte
2507 if (i + 2 > buf_size) {
2508 if (buf == abuf) {
2509 buf = new char[ABUF_SIZE*2];
2510 memcpy(buf, abuf, buf_size);
2511 buf_size = ABUF_SIZE*2;
2513 else {
2514 char *old_buf = buf;
2515 buf = new char[buf_size*2];
2516 memcpy(buf, old_buf, buf_size);
2517 buf_size *= 2;
2518 a_delete old_buf;
2521 if ((buf[i] = tok.ch()) == 0 || buf[i] == end)
2522 break;
2523 i++;
2524 tok.next();
2526 if (i == 0) {
2527 empty_name_warning(required);
2528 return NULL_SYMBOL;
2530 if (end && buf[i] == end)
2531 buf[i+1] = '\0';
2532 else
2533 non_empty_name_warning();
2534 if (buf == abuf)
2535 return symbol(buf);
2536 else {
2537 symbol s(buf);
2538 a_delete buf;
2539 return s;
2543 void exit_troff()
2545 exit_started = 1;
2546 topdiv->set_last_page();
2547 if (!end_macro_name.is_null()) {
2548 spring_trap(end_macro_name);
2549 tok.next();
2550 process_input_stack();
2552 curenv->final_break();
2553 tok.next();
2554 process_input_stack();
2555 end_diversions();
2556 if (topdiv->get_page_length() > 0) {
2557 done_end_macro = 1;
2558 topdiv->set_ejecting();
2559 static unsigned char buf[2] = { LAST_PAGE_EJECTOR, '\0' };
2560 input_stack::push(make_temp_iterator((char *)buf));
2561 topdiv->space(topdiv->get_page_length(), 1);
2562 tok.next();
2563 process_input_stack();
2564 seen_last_page_ejector = 1; // should be set already
2565 topdiv->set_ejecting();
2566 push_page_ejector();
2567 topdiv->space(topdiv->get_page_length(), 1);
2568 tok.next();
2569 process_input_stack();
2571 // This will only happen if a trap-invoked macro starts a diversion,
2572 // or if vertical position traps have been disabled.
2573 cleanup_and_exit(0);
2576 // This implements .ex. The input stack must be cleared before calling
2577 // exit_troff().
2579 void exit_request()
2581 input_stack::clear();
2582 if (exit_started)
2583 tok.next();
2584 else
2585 exit_troff();
2588 void return_macro_request()
2590 if (has_arg() && tok.ch())
2591 input_stack::pop_macro();
2592 input_stack::pop_macro();
2593 tok.next();
2596 void end_macro()
2598 end_macro_name = get_name();
2599 skip_line();
2602 void blank_line_macro()
2604 blank_line_macro_name = get_name();
2605 skip_line();
2608 static void trapping_blank_line()
2610 if (!blank_line_macro_name.is_null())
2611 spring_trap(blank_line_macro_name);
2612 else
2613 blank_line();
2616 void do_request()
2618 int old_compatible_flag = compatible_flag;
2619 compatible_flag = 0;
2620 symbol nm = get_name();
2621 if (nm.is_null())
2622 skip_line();
2623 else
2624 interpolate_macro(nm, 1);
2625 compatible_flag = old_compatible_flag;
2626 request_or_macro *p = lookup_request(nm);
2627 macro *m = p->to_macro();
2628 if (m)
2629 tok.next();
2632 inline int possibly_handle_first_page_transition()
2634 if (topdiv->before_first_page && curdiv == topdiv && !curenv->is_dummy()) {
2635 handle_first_page_transition();
2636 return 1;
2638 else
2639 return 0;
2642 static int transparent_translate(int cc)
2644 if (!invalid_input_char(cc)) {
2645 charinfo *ci = charset_table[cc];
2646 switch (ci->get_special_translation(1)) {
2647 case charinfo::TRANSLATE_SPACE:
2648 return ' ';
2649 case charinfo::TRANSLATE_STRETCHABLE_SPACE:
2650 return ESCAPE_TILDE;
2651 case charinfo::TRANSLATE_DUMMY:
2652 return ESCAPE_AMPERSAND;
2653 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
2654 return ESCAPE_PERCENT;
2656 // This is really ugly.
2657 ci = ci->get_translation(1);
2658 if (ci) {
2659 int c = ci->get_ascii_code();
2660 if (c != '\0')
2661 return c;
2662 error("can't translate %1 to special character `%2'"
2663 " in transparent throughput",
2664 input_char_description(cc),
2665 ci->nm.contents());
2668 return cc;
2671 class int_stack {
2672 struct int_stack_element {
2673 int n;
2674 int_stack_element *next;
2675 } *top;
2676 public:
2677 int_stack();
2678 ~int_stack();
2679 void push(int);
2680 int is_empty();
2681 int pop();
2684 int_stack::int_stack()
2686 top = 0;
2689 int_stack::~int_stack()
2691 while (top != 0) {
2692 int_stack_element *temp = top;
2693 top = top->next;
2694 delete temp;
2698 int int_stack::is_empty()
2700 return top == 0;
2703 void int_stack::push(int n)
2705 int_stack_element *p = new int_stack_element;
2706 p->next = top;
2707 p->n = n;
2708 top = p;
2711 int int_stack::pop()
2713 assert(top != 0);
2714 int_stack_element *p = top;
2715 top = top->next;
2716 int n = p->n;
2717 delete p;
2718 return n;
2721 int node::reread(int *)
2723 return 0;
2726 int global_diverted_space = 0;
2728 int diverted_space_node::reread(int *bolp)
2730 global_diverted_space = 1;
2731 if (curenv->get_fill())
2732 trapping_blank_line();
2733 else
2734 curdiv->space(n);
2735 global_diverted_space = 0;
2736 *bolp = 1;
2737 return 1;
2740 int diverted_copy_file_node::reread(int *bolp)
2742 curdiv->copy_file(filename.contents());
2743 *bolp = 1;
2744 return 1;
2747 int word_space_node::reread(int *)
2749 if (unformat) {
2750 for (width_list *w = orig_width; w; w = w->next)
2751 curenv->space(w->width, w->sentence_width);
2752 unformat = 0;
2753 return 1;
2755 return 0;
2758 int unbreakable_space_node::reread(int *)
2760 return 0;
2763 int hmotion_node::reread(int *)
2765 if (unformat && was_tab) {
2766 curenv->handle_tab(0);
2767 unformat = 0;
2768 return 1;
2770 return 0;
2773 void process_input_stack()
2775 int_stack trap_bol_stack;
2776 int bol = 1;
2777 for (;;) {
2778 int suppress_next = 0;
2779 switch (tok.type) {
2780 case token::TOKEN_CHAR:
2782 unsigned char ch = tok.c;
2783 if (bol && !have_input
2784 && (ch == curenv->control_char
2785 || ch == curenv->no_break_control_char)) {
2786 break_flag = ch == curenv->control_char;
2787 // skip tabs as well as spaces here
2788 do {
2789 tok.next();
2790 } while (tok.white_space());
2791 symbol nm = get_name();
2792 #if defined(DEBUGGING)
2793 if (debug_state) {
2794 if (! nm.is_null()) {
2795 if (strcmp(nm.contents(), "test") == 0) {
2796 fprintf(stderr, "found it!\n");
2797 fflush(stderr);
2799 fprintf(stderr, "interpreting [%s]", nm.contents());
2800 if (strcmp(nm.contents(), "di") == 0 && topdiv != curdiv)
2801 fprintf(stderr, " currently in diversion: %s",
2802 curdiv->get_diversion_name());
2803 fprintf(stderr, "\n");
2804 fflush(stderr);
2807 #endif
2808 if (nm.is_null())
2809 skip_line();
2810 else {
2811 interpolate_macro(nm);
2812 #if defined(DEBUGGING)
2813 if (debug_state) {
2814 fprintf(stderr, "finished interpreting [%s] and environment state is\n", nm.contents());
2815 curenv->dump_troff_state();
2817 #endif
2819 suppress_next = 1;
2821 else {
2822 if (possibly_handle_first_page_transition())
2824 else {
2825 for (;;) {
2826 #if defined(DEBUGGING)
2827 if (debug_state) {
2828 fprintf(stderr, "found [%c]\n", ch); fflush(stderr);
2830 #endif
2831 curenv->add_char(charset_table[ch]);
2832 tok.next();
2833 if (tok.type != token::TOKEN_CHAR)
2834 break;
2835 ch = tok.c;
2837 suppress_next = 1;
2838 bol = 0;
2841 break;
2843 case token::TOKEN_TRANSPARENT:
2845 if (bol) {
2846 if (possibly_handle_first_page_transition())
2848 else {
2849 int cc;
2850 do {
2851 node *n;
2852 cc = get_copy(&n);
2853 if (cc != EOF) {
2854 if (cc != '\0')
2855 curdiv->transparent_output(transparent_translate(cc));
2856 else
2857 curdiv->transparent_output(n);
2859 } while (cc != '\n' && cc != EOF);
2860 if (cc == EOF)
2861 curdiv->transparent_output('\n');
2864 break;
2866 case token::TOKEN_NEWLINE:
2868 if (bol && !old_have_input
2869 && !curenv->get_prev_line_interrupted())
2870 trapping_blank_line();
2871 else {
2872 curenv->newline();
2873 bol = 1;
2875 break;
2877 case token::TOKEN_REQUEST:
2879 int request_code = tok.c;
2880 tok.next();
2881 switch (request_code) {
2882 case TITLE_REQUEST:
2883 title();
2884 break;
2885 case COPY_FILE_REQUEST:
2886 copy_file();
2887 break;
2888 case TRANSPARENT_FILE_REQUEST:
2889 transparent_file();
2890 break;
2891 #ifdef COLUMN
2892 case VJUSTIFY_REQUEST:
2893 vjustify();
2894 break;
2895 #endif /* COLUMN */
2896 default:
2897 assert(0);
2898 break;
2900 suppress_next = 1;
2901 break;
2903 case token::TOKEN_SPACE:
2905 if (possibly_handle_first_page_transition())
2907 else if (bol && !curenv->get_prev_line_interrupted()) {
2908 int nspaces = 0;
2909 // save space_width now so that it isn't changed by \f or \s
2910 // which we wouldn't notice here
2911 hunits space_width = curenv->get_space_width();
2912 do {
2913 nspaces += tok.nspaces();
2914 tok.next();
2915 } while (tok.space());
2916 if (tok.newline())
2917 trapping_blank_line();
2918 else {
2919 push_token(tok);
2920 curenv->do_break();
2921 curenv->add_node(new hmotion_node(space_width * nspaces,
2922 curenv->get_fill_color()));
2923 bol = 0;
2926 else {
2927 curenv->space();
2928 bol = 0;
2930 break;
2932 case token::TOKEN_EOF:
2933 return;
2934 case token::TOKEN_NODE:
2936 if (possibly_handle_first_page_transition())
2938 else if (tok.nd->reread(&bol)) {
2939 delete tok.nd;
2940 tok.nd = 0;
2942 else {
2943 curenv->add_node(tok.nd);
2944 tok.nd = 0;
2945 bol = 0;
2946 curenv->possibly_break_line(1);
2948 break;
2950 case token::TOKEN_PAGE_EJECTOR:
2952 continue_page_eject();
2953 // I think we just want to preserve bol.
2954 // bol = 1;
2955 break;
2957 case token::TOKEN_BEGIN_TRAP:
2959 trap_bol_stack.push(bol);
2960 bol = 1;
2961 have_input = 0;
2962 break;
2964 case token::TOKEN_END_TRAP:
2966 if (trap_bol_stack.is_empty())
2967 error("spurious end trap token detected!");
2968 else
2969 bol = trap_bol_stack.pop();
2970 have_input = 0;
2972 /* I'm not totally happy about this. But I can't think of any other
2973 way to do it. Doing an output_pending_lines() whenever a
2974 TOKEN_END_TRAP is detected doesn't work: for example,
2976 .wh -1i x
2977 .de x
2980 .wh -.5i y
2981 .de y
2982 .tl ''-%-''
2985 .ll .5i
2986 .sp |\n(.pu-1i-.5v
2987 a\%very\%very\%long\%word
2989 will print all but the first lines from the word immediately
2990 after the footer, rather than on the next page. */
2992 if (trap_bol_stack.is_empty())
2993 curenv->output_pending_lines();
2994 break;
2996 default:
2998 bol = 0;
2999 tok.process();
3000 break;
3003 if (!suppress_next)
3004 tok.next();
3005 trap_sprung_flag = 0;
3009 #ifdef WIDOW_CONTROL
3011 void flush_pending_lines()
3013 while (!tok.newline() && !tok.eof())
3014 tok.next();
3015 curenv->output_pending_lines();
3016 tok.next();
3019 #endif /* WIDOW_CONTROL */
3021 request_or_macro::request_or_macro()
3025 macro *request_or_macro::to_macro()
3027 return 0;
3030 request::request(REQUEST_FUNCP pp) : p(pp)
3034 void request::invoke(symbol, int)
3036 (*p)();
3039 struct char_block {
3040 enum { SIZE = 128 };
3041 unsigned char s[SIZE];
3042 char_block *next;
3043 char_block();
3046 char_block::char_block()
3047 : next(0)
3051 class char_list {
3052 public:
3053 char_list();
3054 ~char_list();
3055 void append(unsigned char);
3056 void set(unsigned char, int);
3057 unsigned char get(int);
3058 int length();
3059 private:
3060 unsigned char *ptr;
3061 int len;
3062 char_block *head;
3063 char_block *tail;
3064 friend class macro_header;
3065 friend class string_iterator;
3068 char_list::char_list()
3069 : ptr(0), len(0), head(0), tail(0)
3073 char_list::~char_list()
3075 while (head != 0) {
3076 char_block *tem = head;
3077 head = head->next;
3078 delete tem;
3082 int char_list::length()
3084 return len;
3087 void char_list::append(unsigned char c)
3089 if (tail == 0) {
3090 head = tail = new char_block;
3091 ptr = tail->s;
3093 else {
3094 if (ptr >= tail->s + char_block::SIZE) {
3095 tail->next = new char_block;
3096 tail = tail->next;
3097 ptr = tail->s;
3100 *ptr++ = c;
3101 len++;
3104 void char_list::set(unsigned char c, int offset)
3106 assert(len > offset);
3107 // optimization for access at the end
3108 int boundary = len - len % char_block::SIZE;
3109 if (offset >= boundary) {
3110 *(tail->s + offset - boundary) = c;
3111 return;
3113 char_block *tem = head;
3114 int l = 0;
3115 for (;;) {
3116 l += char_block::SIZE;
3117 if (l > offset) {
3118 *(tem->s + offset % char_block::SIZE) = c;
3119 return;
3121 tem = tem->next;
3125 unsigned char char_list::get(int offset)
3127 assert(len > offset);
3128 // optimization for access at the end
3129 int boundary = len - len % char_block::SIZE;
3130 if (offset >= boundary)
3131 return *(tail->s + offset - boundary);
3132 char_block *tem = head;
3133 int l = 0;
3134 for (;;) {
3135 l += char_block::SIZE;
3136 if (l > offset)
3137 return *(tem->s + offset % char_block::SIZE);
3138 tem = tem->next;
3142 class node_list {
3143 node *head;
3144 node *tail;
3145 public:
3146 node_list();
3147 ~node_list();
3148 void append(node *);
3149 int length();
3150 node *extract();
3152 friend class macro_header;
3153 friend class string_iterator;
3156 void node_list::append(node *n)
3158 if (head == 0) {
3159 n->next = 0;
3160 head = tail = n;
3162 else {
3163 n->next = 0;
3164 tail = tail->next = n;
3168 int node_list::length()
3170 int total = 0;
3171 for (node *n = head; n != 0; n = n->next)
3172 ++total;
3173 return total;
3176 node_list::node_list()
3178 head = tail = 0;
3181 node *node_list::extract()
3183 node *temp = head;
3184 head = tail = 0;
3185 return temp;
3188 node_list::~node_list()
3190 delete_node_list(head);
3193 class macro_header {
3194 public:
3195 int count;
3196 char_list cl;
3197 node_list nl;
3198 macro_header() { count = 1; }
3199 macro_header *copy(int);
3202 macro::~macro()
3204 if (p != 0 && --(p->count) <= 0)
3205 delete p;
3208 macro::macro()
3209 : is_a_diversion(0), is_a_string(1)
3211 if (!input_stack::get_location(1, &filename, &lineno)) {
3212 filename = 0;
3213 lineno = 0;
3215 len = 0;
3216 empty_macro = 1;
3217 p = 0;
3220 macro::macro(const macro &m)
3221 : filename(m.filename), lineno(m.lineno), len(m.len),
3222 empty_macro(m.empty_macro), is_a_diversion(m.is_a_diversion),
3223 is_a_string(m.is_a_string), p(m.p)
3225 if (p != 0)
3226 p->count++;
3229 macro::macro(int is_div)
3230 : is_a_diversion(is_div)
3232 if (!input_stack::get_location(1, &filename, &lineno)) {
3233 filename = 0;
3234 lineno = 0;
3236 len = 0;
3237 empty_macro = 1;
3238 is_a_string = 1;
3239 p = 0;
3242 int macro::is_diversion()
3244 return is_a_diversion;
3247 int macro::is_string()
3249 return is_a_string;
3252 void macro::clear_string_flag()
3254 is_a_string = 0;
3257 macro &macro::operator=(const macro &m)
3259 // don't assign object
3260 if (m.p != 0)
3261 m.p->count++;
3262 if (p != 0 && --(p->count) <= 0)
3263 delete p;
3264 p = m.p;
3265 filename = m.filename;
3266 lineno = m.lineno;
3267 len = m.len;
3268 empty_macro = m.empty_macro;
3269 is_a_diversion = m.is_a_diversion;
3270 is_a_string = m.is_a_string;
3271 return *this;
3274 void macro::append(unsigned char c)
3276 assert(c != 0);
3277 if (p == 0)
3278 p = new macro_header;
3279 if (p->cl.length() != len) {
3280 macro_header *tem = p->copy(len);
3281 if (--(p->count) <= 0)
3282 delete p;
3283 p = tem;
3285 p->cl.append(c);
3286 ++len;
3287 if (c != PUSH_GROFF_MODE && c != PUSH_COMP_MODE && c != POP_GROFFCOMP_MODE)
3288 empty_macro = 0;
3291 void macro::set(unsigned char c, int offset)
3293 assert(p != 0);
3294 assert(c != 0);
3295 p->cl.set(c, offset);
3298 unsigned char macro::get(int offset)
3300 assert(p != 0);
3301 return p->cl.get(offset);
3304 int macro::length()
3306 return len;
3309 void macro::append_str(const char *s)
3311 int i = 0;
3313 if (s) {
3314 while (s[i] != (char)0) {
3315 append(s[i]);
3316 i++;
3321 void macro::append(node *n)
3323 assert(n != 0);
3324 if (p == 0)
3325 p = new macro_header;
3326 if (p->cl.length() != len) {
3327 macro_header *tem = p->copy(len);
3328 if (--(p->count) <= 0)
3329 delete p;
3330 p = tem;
3332 p->cl.append(0);
3333 p->nl.append(n);
3334 ++len;
3335 empty_macro = 0;
3338 void macro::append_unsigned(unsigned int i)
3340 unsigned int j = i / 10;
3341 if (j != 0)
3342 append_unsigned(j);
3343 append(((unsigned char)(((int)'0') + i % 10)));
3346 void macro::append_int(int i)
3348 if (i < 0) {
3349 append('-');
3350 i = -i;
3352 append_unsigned((unsigned int)i);
3355 void macro::print_size()
3357 errprint("%1", len);
3360 // make a copy of the first n bytes
3362 macro_header *macro_header::copy(int n)
3364 macro_header *p = new macro_header;
3365 char_block *bp = cl.head;
3366 unsigned char *ptr = bp->s;
3367 node *nd = nl.head;
3368 while (--n >= 0) {
3369 if (ptr >= bp->s + char_block::SIZE) {
3370 bp = bp->next;
3371 ptr = bp->s;
3373 unsigned char c = *ptr++;
3374 p->cl.append(c);
3375 if (c == 0) {
3376 p->nl.append(nd->copy());
3377 nd = nd->next;
3380 return p;
3383 void print_macros()
3385 object_dictionary_iterator iter(request_dictionary);
3386 request_or_macro *rm;
3387 symbol s;
3388 while (iter.get(&s, (object **)&rm)) {
3389 assert(!s.is_null());
3390 macro *m = rm->to_macro();
3391 if (m) {
3392 errprint("%1\t", s.contents());
3393 m->print_size();
3394 errprint("\n");
3397 fflush(stderr);
3398 skip_line();
3401 class string_iterator : public input_iterator {
3402 macro mac;
3403 const char *how_invoked;
3404 int newline_flag;
3405 int lineno;
3406 char_block *bp;
3407 int count; // of characters remaining
3408 node *nd;
3409 int saved_compatible_flag;
3410 int with_break; // inherited from the caller
3411 protected:
3412 symbol nm;
3413 string_iterator();
3414 public:
3415 string_iterator(const macro &, const char * = 0, symbol = NULL_SYMBOL);
3416 int fill(node **);
3417 int peek();
3418 int get_location(int, const char **, int *);
3419 void backtrace();
3420 int get_break_flag() { return with_break; }
3421 void save_compatible_flag(int f) { saved_compatible_flag = f; }
3422 int get_compatible_flag() { return saved_compatible_flag; }
3423 int is_diversion();
3426 string_iterator::string_iterator(const macro &m, const char *p, symbol s)
3427 : input_iterator(m.is_a_diversion), mac(m), how_invoked(p), newline_flag(0),
3428 lineno(1), nm(s)
3430 count = mac.len;
3431 if (count != 0) {
3432 bp = mac.p->cl.head;
3433 nd = mac.p->nl.head;
3434 ptr = eptr = bp->s;
3436 else {
3437 bp = 0;
3438 nd = 0;
3439 ptr = eptr = 0;
3441 with_break = input_stack::get_break_flag();
3444 string_iterator::string_iterator()
3446 bp = 0;
3447 nd = 0;
3448 ptr = eptr = 0;
3449 newline_flag = 0;
3450 how_invoked = 0;
3451 lineno = 1;
3452 count = 0;
3453 with_break = input_stack::get_break_flag();
3456 int string_iterator::is_diversion()
3458 return mac.is_diversion();
3461 int string_iterator::fill(node **np)
3463 if (newline_flag)
3464 lineno++;
3465 newline_flag = 0;
3466 if (count <= 0)
3467 return EOF;
3468 const unsigned char *p = eptr;
3469 if (p >= bp->s + char_block::SIZE) {
3470 bp = bp->next;
3471 p = bp->s;
3473 if (*p == '\0') {
3474 if (np) {
3475 *np = nd->copy();
3476 if (is_diversion())
3477 (*np)->div_nest_level = input_stack::get_div_level();
3478 else
3479 (*np)->div_nest_level = 0;
3481 nd = nd->next;
3482 eptr = ptr = p + 1;
3483 count--;
3484 return 0;
3486 const unsigned char *e = bp->s + char_block::SIZE;
3487 if (e - p > count)
3488 e = p + count;
3489 ptr = p;
3490 while (p < e) {
3491 unsigned char c = *p;
3492 if (c == '\n' || c == ESCAPE_NEWLINE) {
3493 newline_flag = 1;
3494 p++;
3495 break;
3497 if (c == '\0')
3498 break;
3499 p++;
3501 eptr = p;
3502 count -= p - ptr;
3503 return *ptr++;
3506 int string_iterator::peek()
3508 if (count <= 0)
3509 return EOF;
3510 const unsigned char *p = eptr;
3511 if (p >= bp->s + char_block::SIZE) {
3512 p = bp->next->s;
3514 return *p;
3517 int string_iterator::get_location(int allow_macro,
3518 const char **filep, int *linep)
3520 if (!allow_macro)
3521 return 0;
3522 if (mac.filename == 0)
3523 return 0;
3524 *filep = mac.filename;
3525 *linep = mac.lineno + lineno - 1;
3526 return 1;
3529 void string_iterator::backtrace()
3531 if (mac.filename) {
3532 errprint("%1:%2: backtrace", mac.filename, mac.lineno + lineno - 1);
3533 if (how_invoked) {
3534 if (!nm.is_null())
3535 errprint(": %1 `%2'\n", how_invoked, nm.contents());
3536 else
3537 errprint(": %1\n", how_invoked);
3539 else
3540 errprint("\n");
3544 class temp_iterator : public input_iterator {
3545 unsigned char *base;
3546 temp_iterator(const char *, int len);
3547 public:
3548 ~temp_iterator();
3549 friend input_iterator *make_temp_iterator(const char *);
3552 #ifdef __GNUG__
3553 inline
3554 #endif
3555 temp_iterator::temp_iterator(const char *s, int len)
3557 base = new unsigned char[len];
3558 memcpy(base, s, len);
3559 ptr = base;
3560 eptr = base + len;
3563 temp_iterator::~temp_iterator()
3565 a_delete base;
3568 class small_temp_iterator : public input_iterator {
3569 private:
3570 small_temp_iterator(const char *, int);
3571 ~small_temp_iterator();
3572 enum { BLOCK = 16 };
3573 static small_temp_iterator *free_list;
3574 void *operator new(size_t);
3575 void operator delete(void *);
3576 enum { SIZE = 12 };
3577 unsigned char buf[SIZE];
3578 friend input_iterator *make_temp_iterator(const char *);
3581 small_temp_iterator *small_temp_iterator::free_list = 0;
3583 void *small_temp_iterator::operator new(size_t n)
3585 assert(n == sizeof(small_temp_iterator));
3586 if (!free_list) {
3587 free_list =
3588 (small_temp_iterator *)new char[sizeof(small_temp_iterator)*BLOCK];
3589 for (int i = 0; i < BLOCK - 1; i++)
3590 free_list[i].next = free_list + i + 1;
3591 free_list[BLOCK-1].next = 0;
3593 small_temp_iterator *p = free_list;
3594 free_list = (small_temp_iterator *)(free_list->next);
3595 p->next = 0;
3596 return p;
3599 #ifdef __GNUG__
3600 inline
3601 #endif
3602 void small_temp_iterator::operator delete(void *p)
3604 if (p) {
3605 ((small_temp_iterator *)p)->next = free_list;
3606 free_list = (small_temp_iterator *)p;
3610 small_temp_iterator::~small_temp_iterator()
3614 #ifdef __GNUG__
3615 inline
3616 #endif
3617 small_temp_iterator::small_temp_iterator(const char *s, int len)
3619 for (int i = 0; i < len; i++)
3620 buf[i] = s[i];
3621 ptr = buf;
3622 eptr = buf + len;
3625 input_iterator *make_temp_iterator(const char *s)
3627 if (s == 0)
3628 return new small_temp_iterator(s, 0);
3629 else {
3630 int n = strlen(s);
3631 if (n <= small_temp_iterator::SIZE)
3632 return new small_temp_iterator(s, n);
3633 else
3634 return new temp_iterator(s, n);
3638 // this is used when macros with arguments are interpolated
3640 struct arg_list {
3641 macro mac;
3642 int space_follows;
3643 arg_list *next;
3644 arg_list(const macro &, int);
3645 arg_list(const arg_list *);
3646 ~arg_list();
3649 arg_list::arg_list(const macro &m, int s) : mac(m), space_follows(s), next(0)
3653 arg_list::arg_list(const arg_list *al)
3654 : next(0)
3656 mac = al->mac;
3657 space_follows = al->space_follows;
3658 arg_list **a = &next;
3659 arg_list *p = al->next;
3660 while (p) {
3661 *a = new arg_list(p->mac, p->space_follows);
3662 p = p->next;
3663 a = &(*a)->next;
3667 arg_list::~arg_list()
3671 class macro_iterator : public string_iterator {
3672 arg_list *args;
3673 int argc;
3674 int with_break; // whether called as .foo or 'foo
3675 public:
3676 macro_iterator(symbol, macro &, const char * = "macro", int = 0);
3677 macro_iterator();
3678 ~macro_iterator();
3679 int has_args() { return 1; }
3680 input_iterator *get_arg(int);
3681 arg_list *get_arg_list();
3682 symbol get_macro_name();
3683 int space_follows_arg(int);
3684 int get_break_flag() { return with_break; }
3685 int nargs() { return argc; }
3686 void add_arg(const macro &, int);
3687 void shift(int);
3688 int is_macro() { return 1; }
3689 int is_diversion();
3692 input_iterator *macro_iterator::get_arg(int i)
3694 if (i == 0)
3695 return make_temp_iterator(nm.contents());
3696 if (i > 0 && i <= argc) {
3697 arg_list *p = args;
3698 for (int j = 1; j < i; j++) {
3699 assert(p != 0);
3700 p = p->next;
3702 return new string_iterator(p->mac);
3704 else
3705 return 0;
3708 arg_list *macro_iterator::get_arg_list()
3710 return args;
3713 symbol macro_iterator::get_macro_name()
3715 return nm;
3718 int macro_iterator::space_follows_arg(int i)
3720 if (i > 0 && i <= argc) {
3721 arg_list *p = args;
3722 for (int j = 1; j < i; j++) {
3723 assert(p != 0);
3724 p = p->next;
3726 return p->space_follows;
3728 else
3729 return 0;
3732 void macro_iterator::add_arg(const macro &m, int s)
3734 arg_list **p;
3735 for (p = &args; *p; p = &((*p)->next))
3737 *p = new arg_list(m, s);
3738 ++argc;
3741 void macro_iterator::shift(int n)
3743 while (n > 0 && argc > 0) {
3744 arg_list *tem = args;
3745 args = args->next;
3746 delete tem;
3747 --argc;
3748 --n;
3752 // This gets used by eg .if '\?xxx\?''.
3754 int operator==(const macro &m1, const macro &m2)
3756 if (m1.len != m2.len)
3757 return 0;
3758 string_iterator iter1(m1);
3759 string_iterator iter2(m2);
3760 int n = m1.len;
3761 while (--n >= 0) {
3762 node *nd1 = 0;
3763 int c1 = iter1.get(&nd1);
3764 assert(c1 != EOF);
3765 node *nd2 = 0;
3766 int c2 = iter2.get(&nd2);
3767 assert(c2 != EOF);
3768 if (c1 != c2) {
3769 if (c1 == 0)
3770 delete nd1;
3771 else if (c2 == 0)
3772 delete nd2;
3773 return 0;
3775 if (c1 == 0) {
3776 assert(nd1 != 0);
3777 assert(nd2 != 0);
3778 int are_same = nd1->type() == nd2->type() && nd1->same(nd2);
3779 delete nd1;
3780 delete nd2;
3781 if (!are_same)
3782 return 0;
3785 return 1;
3788 static void interpolate_macro(symbol nm, int no_next)
3790 request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm);
3791 if (p == 0) {
3792 int warned = 0;
3793 const char *s = nm.contents();
3794 if (strlen(s) > 2) {
3795 request_or_macro *r;
3796 char buf[3];
3797 buf[0] = s[0];
3798 buf[1] = s[1];
3799 buf[2] = '\0';
3800 r = (request_or_macro *)request_dictionary.lookup(symbol(buf));
3801 if (r) {
3802 macro *m = r->to_macro();
3803 if (!m || !m->empty())
3804 warned = warning(WARN_SPACE,
3805 "macro `%1' not defined "
3806 "(possibly missing space after `%2')",
3807 nm.contents(), buf);
3810 if (!warned) {
3811 warning(WARN_MAC, "macro `%1' not defined", nm.contents());
3812 p = new macro;
3813 request_dictionary.define(nm, p);
3816 if (p)
3817 p->invoke(nm, no_next);
3818 else {
3819 skip_line();
3820 return;
3824 static void decode_args(macro_iterator *mi)
3826 if (!tok.newline() && !tok.eof()) {
3827 node *n;
3828 int c = get_copy(&n);
3829 for (;;) {
3830 while (c == ' ')
3831 c = get_copy(&n);
3832 if (c == '\n' || c == EOF)
3833 break;
3834 macro arg;
3835 int quote_input_level = 0;
3836 int done_tab_warning = 0;
3837 arg.append(compatible_flag ? PUSH_COMP_MODE : PUSH_GROFF_MODE);
3838 // we store discarded double quotes for \$^
3839 if (c == '"') {
3840 arg.append(DOUBLE_QUOTE);
3841 quote_input_level = input_stack::get_level();
3842 c = get_copy(&n);
3844 while (c != EOF && c != '\n' && !(c == ' ' && quote_input_level == 0)) {
3845 if (quote_input_level > 0 && c == '"'
3846 && (compatible_flag
3847 || input_stack::get_level() == quote_input_level)) {
3848 arg.append(DOUBLE_QUOTE);
3849 c = get_copy(&n);
3850 if (c == '"') {
3851 arg.append(c);
3852 c = get_copy(&n);
3854 else
3855 break;
3857 else {
3858 if (c == 0)
3859 arg.append(n);
3860 else {
3861 if (c == '\t' && quote_input_level == 0 && !done_tab_warning) {
3862 warning(WARN_TAB, "tab character in unquoted macro argument");
3863 done_tab_warning = 1;
3865 arg.append(c);
3867 c = get_copy(&n);
3870 arg.append(POP_GROFFCOMP_MODE);
3871 mi->add_arg(arg, (c == ' '));
3876 static void decode_string_args(macro_iterator *mi)
3878 node *n;
3879 int c = get_copy(&n);
3880 for (;;) {
3881 while (c == ' ')
3882 c = get_copy(&n);
3883 if (c == '\n' || c == EOF) {
3884 error("missing `]'");
3885 break;
3887 if (c == ']')
3888 break;
3889 macro arg;
3890 int quote_input_level = 0;
3891 int done_tab_warning = 0;
3892 if (c == '"') {
3893 quote_input_level = input_stack::get_level();
3894 c = get_copy(&n);
3896 while (c != EOF && c != '\n'
3897 && !(c == ']' && quote_input_level == 0)
3898 && !(c == ' ' && quote_input_level == 0)) {
3899 if (quote_input_level > 0 && c == '"'
3900 && input_stack::get_level() == quote_input_level) {
3901 c = get_copy(&n);
3902 if (c == '"') {
3903 arg.append(c);
3904 c = get_copy(&n);
3906 else
3907 break;
3909 else {
3910 if (c == 0)
3911 arg.append(n);
3912 else {
3913 if (c == '\t' && quote_input_level == 0 && !done_tab_warning) {
3914 warning(WARN_TAB, "tab character in unquoted string argument");
3915 done_tab_warning = 1;
3917 arg.append(c);
3919 c = get_copy(&n);
3922 mi->add_arg(arg, (c == ' '));
3926 void macro::invoke(symbol nm, int no_next)
3928 macro_iterator *mi = new macro_iterator(nm, *this);
3929 decode_args(mi);
3930 input_stack::push(mi);
3931 // we must delay tok.next() in case the function has been called by
3932 // do_request to assure proper handling of compatible_flag
3933 if (!no_next)
3934 tok.next();
3937 macro *macro::to_macro()
3939 return this;
3942 int macro::empty()
3944 return empty_macro == 1;
3947 macro_iterator::macro_iterator(symbol s, macro &m, const char *how_called,
3948 int init_args)
3949 : string_iterator(m, how_called, s), args(0), argc(0), with_break(break_flag)
3951 if (init_args) {
3952 arg_list *al = input_stack::get_arg_list();
3953 if (al) {
3954 args = new arg_list(al);
3955 argc = input_stack::nargs();
3960 macro_iterator::macro_iterator() : args(0), argc(0), with_break(break_flag)
3964 macro_iterator::~macro_iterator()
3966 while (args != 0) {
3967 arg_list *tem = args;
3968 args = args->next;
3969 delete tem;
3973 dictionary composite_dictionary(17);
3975 void composite_request()
3977 symbol from = get_name(1);
3978 if (!from.is_null()) {
3979 const char *from_gn = glyph_name_to_unicode(from.contents());
3980 if (!from_gn) {
3981 from_gn = check_unicode_name(from.contents());
3982 if (!from_gn) {
3983 error("invalid composite glyph name `%1'", from.contents());
3984 skip_line();
3985 return;
3988 const char *from_decomposed = decompose_unicode(from_gn);
3989 if (from_decomposed)
3990 from_gn = &from_decomposed[1];
3991 symbol to = get_name(1);
3992 if (to.is_null())
3993 composite_dictionary.remove(symbol(from_gn));
3994 else {
3995 const char *to_gn = glyph_name_to_unicode(to.contents());
3996 if (!to_gn) {
3997 to_gn = check_unicode_name(to.contents());
3998 if (!to_gn) {
3999 error("invalid composite glyph name `%1'", to.contents());
4000 skip_line();
4001 return;
4004 const char *to_decomposed = decompose_unicode(to_gn);
4005 if (to_decomposed)
4006 to_gn = &to_decomposed[1];
4007 if (strcmp(from_gn, to_gn) == 0)
4008 composite_dictionary.remove(symbol(from_gn));
4009 else
4010 (void)composite_dictionary.lookup(symbol(from_gn), (void *)to_gn);
4013 skip_line();
4016 static symbol composite_glyph_name(symbol nm)
4018 macro_iterator *mi = new macro_iterator();
4019 decode_string_args(mi);
4020 input_stack::push(mi);
4021 const char *gn = glyph_name_to_unicode(nm.contents());
4022 if (!gn) {
4023 gn = check_unicode_name(nm.contents());
4024 if (!gn) {
4025 error("invalid base glyph `%1' in composite glyph name", nm.contents());
4026 return EMPTY_SYMBOL;
4029 const char *gn_decomposed = decompose_unicode(gn);
4030 string glyph_name(gn_decomposed ? &gn_decomposed[1] : gn);
4031 string gl;
4032 int n = input_stack::nargs();
4033 for (int i = 1; i <= n; i++) {
4034 glyph_name += '_';
4035 input_iterator *p = input_stack::get_arg(i);
4036 gl.clear();
4037 int c;
4038 while ((c = p->get(0)) != EOF)
4039 if (c != DOUBLE_QUOTE)
4040 gl += c;
4041 gl += '\0';
4042 const char *u = glyph_name_to_unicode(gl.contents());
4043 if (!u) {
4044 u = check_unicode_name(gl.contents());
4045 if (!u) {
4046 error("invalid component `%1' in composite glyph name",
4047 gl.contents());
4048 return EMPTY_SYMBOL;
4051 const char *decomposed = decompose_unicode(u);
4052 if (decomposed)
4053 u = &decomposed[1];
4054 void *mapped_composite = composite_dictionary.lookup(symbol(u));
4055 if (mapped_composite)
4056 u = (const char *)mapped_composite;
4057 glyph_name += u;
4059 glyph_name += '\0';
4060 const char *groff_gn = unicode_to_glyph_name(glyph_name.contents());
4061 if (groff_gn)
4062 return symbol(groff_gn);
4063 gl.clear();
4064 gl += 'u';
4065 gl += glyph_name;
4066 return symbol(gl.contents());
4069 int trap_sprung_flag = 0;
4070 int postpone_traps_flag = 0;
4071 symbol postponed_trap;
4073 void spring_trap(symbol nm)
4075 assert(!nm.is_null());
4076 trap_sprung_flag = 1;
4077 if (postpone_traps_flag) {
4078 postponed_trap = nm;
4079 return;
4081 static char buf[2] = { BEGIN_TRAP, 0 };
4082 static char buf2[2] = { END_TRAP, '\0' };
4083 input_stack::push(make_temp_iterator(buf2));
4084 request_or_macro *p = lookup_request(nm);
4085 macro *m = p->to_macro();
4086 if (m)
4087 input_stack::push(new macro_iterator(nm, *m, "trap-invoked macro"));
4088 else
4089 error("you can't invoke a request with a trap");
4090 input_stack::push(make_temp_iterator(buf));
4093 void postpone_traps()
4095 postpone_traps_flag = 1;
4098 int unpostpone_traps()
4100 postpone_traps_flag = 0;
4101 if (!postponed_trap.is_null()) {
4102 spring_trap(postponed_trap);
4103 postponed_trap = NULL_SYMBOL;
4104 return 1;
4106 else
4107 return 0;
4110 void read_request()
4112 macro_iterator *mi = new macro_iterator;
4113 int reading_from_terminal = isatty(fileno(stdin));
4114 int had_prompt = 0;
4115 if (!tok.newline() && !tok.eof()) {
4116 int c = get_copy(0);
4117 while (c == ' ')
4118 c = get_copy(0);
4119 while (c != EOF && c != '\n' && c != ' ') {
4120 if (!invalid_input_char(c)) {
4121 if (reading_from_terminal)
4122 fputc(c, stderr);
4123 had_prompt = 1;
4125 c = get_copy(0);
4127 if (c == ' ') {
4128 tok.make_space();
4129 decode_args(mi);
4132 if (reading_from_terminal) {
4133 fputc(had_prompt ? ':' : '\a', stderr);
4134 fflush(stderr);
4136 input_stack::push(mi);
4137 macro mac;
4138 int nl = 0;
4139 int c;
4140 while ((c = getchar()) != EOF) {
4141 if (invalid_input_char(c))
4142 warning(WARN_INPUT, "invalid input character code %1", int(c));
4143 else {
4144 if (c == '\n') {
4145 if (nl)
4146 break;
4147 else
4148 nl = 1;
4150 else
4151 nl = 0;
4152 mac.append(c);
4155 if (reading_from_terminal)
4156 clearerr(stdin);
4157 input_stack::push(new string_iterator(mac));
4158 tok.next();
4161 enum define_mode { DEFINE_NORMAL, DEFINE_APPEND, DEFINE_IGNORE };
4162 enum calling_mode { CALLING_NORMAL, CALLING_INDIRECT };
4163 enum comp_mode { COMP_IGNORE, COMP_DISABLE, COMP_ENABLE };
4165 void do_define_string(define_mode mode, comp_mode comp)
4167 symbol nm;
4168 node *n = 0; // pacify compiler
4169 int c;
4170 nm = get_name(1);
4171 if (nm.is_null()) {
4172 skip_line();
4173 return;
4175 if (tok.newline())
4176 c = '\n';
4177 else if (tok.tab())
4178 c = '\t';
4179 else if (!tok.space()) {
4180 error("bad string definition");
4181 skip_line();
4182 return;
4184 else
4185 c = get_copy(&n);
4186 while (c == ' ')
4187 c = get_copy(&n);
4188 if (c == '"')
4189 c = get_copy(&n);
4190 macro mac;
4191 request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm);
4192 macro *mm = rm ? rm->to_macro() : 0;
4193 if (mode == DEFINE_APPEND && mm)
4194 mac = *mm;
4195 if (comp == COMP_DISABLE)
4196 mac.append(PUSH_GROFF_MODE);
4197 else if (comp == COMP_ENABLE)
4198 mac.append(PUSH_COMP_MODE);
4199 while (c != '\n' && c != EOF) {
4200 if (c == 0)
4201 mac.append(n);
4202 else
4203 mac.append((unsigned char)c);
4204 c = get_copy(&n);
4206 if (comp == COMP_DISABLE || comp == COMP_ENABLE)
4207 mac.append(POP_GROFFCOMP_MODE);
4208 if (!mm) {
4209 mm = new macro;
4210 request_dictionary.define(nm, mm);
4212 *mm = mac;
4213 tok.next();
4216 void define_string()
4218 do_define_string(DEFINE_NORMAL,
4219 compatible_flag ? COMP_ENABLE: COMP_IGNORE);
4222 void define_nocomp_string()
4224 do_define_string(DEFINE_NORMAL, COMP_DISABLE);
4227 void append_string()
4229 do_define_string(DEFINE_APPEND,
4230 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4233 void append_nocomp_string()
4235 do_define_string(DEFINE_APPEND, COMP_DISABLE);
4238 void do_define_character(char_mode mode, const char *font_name)
4240 node *n = 0; // pacify compiler
4241 int c;
4242 tok.skip();
4243 charinfo *ci = tok.get_char(1);
4244 if (ci == 0) {
4245 skip_line();
4246 return;
4248 if (font_name) {
4249 string s(font_name);
4250 s += ' ';
4251 s += ci->nm.contents();
4252 s += '\0';
4253 ci = get_charinfo(symbol(s.contents()));
4255 tok.next();
4256 if (tok.newline())
4257 c = '\n';
4258 else if (tok.tab())
4259 c = '\t';
4260 else if (!tok.space()) {
4261 error("bad character definition");
4262 skip_line();
4263 return;
4265 else
4266 c = get_copy(&n);
4267 while (c == ' ' || c == '\t')
4268 c = get_copy(&n);
4269 if (c == '"')
4270 c = get_copy(&n);
4271 macro *m = new macro;
4272 while (c != '\n' && c != EOF) {
4273 if (c == 0)
4274 m->append(n);
4275 else
4276 m->append((unsigned char)c);
4277 c = get_copy(&n);
4279 m = ci->setx_macro(m, mode);
4280 if (m)
4281 delete m;
4282 tok.next();
4285 void define_character()
4287 do_define_character(CHAR_NORMAL);
4290 void define_fallback_character()
4292 do_define_character(CHAR_FALLBACK);
4295 void define_special_character()
4297 do_define_character(CHAR_SPECIAL);
4300 static void remove_character()
4302 tok.skip();
4303 while (!tok.newline() && !tok.eof()) {
4304 if (!tok.space() && !tok.tab()) {
4305 charinfo *ci = tok.get_char(1);
4306 if (!ci)
4307 break;
4308 macro *m = ci->set_macro(0);
4309 if (m)
4310 delete m;
4312 tok.next();
4314 skip_line();
4317 static void interpolate_string(symbol nm)
4319 request_or_macro *p = lookup_request(nm);
4320 macro *m = p->to_macro();
4321 if (!m)
4322 error("you can only invoke a string or macro using \\*");
4323 else {
4324 if (m->is_string()) {
4325 string_iterator *si = new string_iterator(*m, "string", nm);
4326 input_stack::push(si);
4328 else {
4329 // if a macro is called as a string, \$0 doesn't get changed
4330 macro_iterator *mi = new macro_iterator(input_stack::get_macro_name(),
4331 *m, "string", 1);
4332 input_stack::push(mi);
4337 static void interpolate_string_with_args(symbol s)
4339 request_or_macro *p = lookup_request(s);
4340 macro *m = p->to_macro();
4341 if (!m)
4342 error("you can only invoke a string or macro using \\*");
4343 else {
4344 macro_iterator *mi = new macro_iterator(s, *m);
4345 decode_string_args(mi);
4346 input_stack::push(mi);
4350 static void interpolate_arg(symbol nm)
4352 const char *s = nm.contents();
4353 if (!s || *s == '\0')
4354 copy_mode_error("missing argument name");
4355 else if (s[1] == 0 && csdigit(s[0]))
4356 input_stack::push(input_stack::get_arg(s[0] - '0'));
4357 else if (s[0] == '*' && s[1] == '\0') {
4358 int limit = input_stack::nargs();
4359 string args;
4360 for (int i = 1; i <= limit; i++) {
4361 input_iterator *p = input_stack::get_arg(i);
4362 int c;
4363 while ((c = p->get(0)) != EOF)
4364 if (c != DOUBLE_QUOTE)
4365 args += c;
4366 if (i != limit)
4367 args += ' ';
4369 if (limit > 0) {
4370 args += '\0';
4371 input_stack::push(make_temp_iterator(args.contents()));
4374 else if (s[0] == '@' && s[1] == '\0') {
4375 int limit = input_stack::nargs();
4376 string args;
4377 for (int i = 1; i <= limit; i++) {
4378 args += '"';
4379 args += char(BEGIN_QUOTE);
4380 input_iterator *p = input_stack::get_arg(i);
4381 int c;
4382 while ((c = p->get(0)) != EOF)
4383 if (c != DOUBLE_QUOTE)
4384 args += c;
4385 args += char(END_QUOTE);
4386 args += '"';
4387 if (i != limit)
4388 args += ' ';
4390 if (limit > 0) {
4391 args += '\0';
4392 input_stack::push(make_temp_iterator(args.contents()));
4395 else if (s[0] == '^' && s[1] == '\0') {
4396 int limit = input_stack::nargs();
4397 string args;
4398 int c = input_stack::peek();
4399 for (int i = 1; i <= limit; i++) {
4400 input_iterator *p = input_stack::get_arg(i);
4401 while ((c = p->get(0)) != EOF) {
4402 if (c == DOUBLE_QUOTE)
4403 c = '"';
4404 args += c;
4406 if (input_stack::space_follows_arg(i))
4407 args += ' ';
4409 if (limit > 0) {
4410 args += '\0';
4411 input_stack::push(make_temp_iterator(args.contents()));
4414 else {
4415 const char *p;
4416 for (p = s; *p && csdigit(*p); p++)
4418 if (*p)
4419 copy_mode_error("bad argument name `%1'", s);
4420 else
4421 input_stack::push(input_stack::get_arg(atoi(s)));
4425 void handle_first_page_transition()
4427 push_token(tok);
4428 topdiv->begin_page();
4431 // We push back a token by wrapping it up in a token_node, and
4432 // wrapping that up in a string_iterator.
4434 static void push_token(const token &t)
4436 macro m;
4437 m.append(new token_node(t));
4438 input_stack::push(new string_iterator(m));
4441 void push_page_ejector()
4443 static char buf[2] = { PAGE_EJECTOR, '\0' };
4444 input_stack::push(make_temp_iterator(buf));
4447 void handle_initial_request(unsigned char code)
4449 char buf[2];
4450 buf[0] = code;
4451 buf[1] = '\0';
4452 macro mac;
4453 mac.append(new token_node(tok));
4454 input_stack::push(new string_iterator(mac));
4455 input_stack::push(make_temp_iterator(buf));
4456 topdiv->begin_page();
4457 tok.next();
4460 void handle_initial_title()
4462 handle_initial_request(TITLE_REQUEST);
4465 // this should be local to define_macro, but cfront 1.2 doesn't support that
4466 static symbol dot_symbol(".");
4468 void do_define_macro(define_mode mode, calling_mode calling, comp_mode comp)
4470 symbol nm, term;
4471 if (calling == CALLING_INDIRECT) {
4472 symbol temp1 = get_name(1);
4473 if (temp1.is_null()) {
4474 skip_line();
4475 return;
4477 symbol temp2 = get_name();
4478 input_stack::push(make_temp_iterator("\n"));
4479 if (!temp2.is_null()) {
4480 interpolate_string(temp2);
4481 input_stack::push(make_temp_iterator(" "));
4483 interpolate_string(temp1);
4484 input_stack::push(make_temp_iterator(" "));
4485 tok.next();
4487 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4488 nm = get_name(1);
4489 if (nm.is_null()) {
4490 skip_line();
4491 return;
4494 term = get_name(); // the request that terminates the definition
4495 if (term.is_null())
4496 term = dot_symbol;
4497 while (!tok.newline() && !tok.eof())
4498 tok.next();
4499 const char *start_filename;
4500 int start_lineno;
4501 int have_start_location = input_stack::get_location(0, &start_filename,
4502 &start_lineno);
4503 node *n;
4504 // doing this here makes the line numbers come out right
4505 int c = get_copy(&n, 1);
4506 macro mac;
4507 macro *mm = 0;
4508 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4509 request_or_macro *rm =
4510 (request_or_macro *)request_dictionary.lookup(nm);
4511 if (rm)
4512 mm = rm->to_macro();
4513 if (mm && mode == DEFINE_APPEND)
4514 mac = *mm;
4516 int bol = 1;
4517 if (comp == COMP_DISABLE)
4518 mac.append(PUSH_GROFF_MODE);
4519 else if (comp == COMP_ENABLE)
4520 mac.append(PUSH_COMP_MODE);
4521 for (;;) {
4522 if (c == '\n')
4523 mac.clear_string_flag();
4524 while (c == ESCAPE_NEWLINE) {
4525 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND)
4526 mac.append(c);
4527 c = get_copy(&n, 1);
4529 if (bol && c == '.') {
4530 const char *s = term.contents();
4531 int d = 0;
4532 // see if it matches term
4533 int i = 0;
4534 if (s[0] != 0) {
4535 while ((d = get_copy(&n)) == ' ' || d == '\t')
4537 if ((unsigned char)s[0] == d) {
4538 for (i = 1; s[i] != 0; i++) {
4539 d = get_copy(&n);
4540 if ((unsigned char)s[i] != d)
4541 break;
4545 if (s[i] == 0
4546 && ((i == 2 && compatible_flag)
4547 || (d = get_copy(&n)) == ' '
4548 || d == '\n')) { // we found it
4549 if (d == '\n')
4550 tok.make_newline();
4551 else
4552 tok.make_space();
4553 if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) {
4554 if (!mm) {
4555 mm = new macro;
4556 request_dictionary.define(nm, mm);
4558 if (comp == COMP_DISABLE || comp == COMP_ENABLE)
4559 mac.append(POP_GROFFCOMP_MODE);
4560 *mm = mac;
4562 if (term != dot_symbol) {
4563 ignoring = 0;
4564 interpolate_macro(term);
4566 else
4567 skip_line();
4568 return;
4570 if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) {
4571 mac.append(c);
4572 for (int j = 0; j < i; j++)
4573 mac.append(s[j]);
4575 c = d;
4577 if (c == EOF) {
4578 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4579 if (have_start_location)
4580 error_with_file_and_line(start_filename, start_lineno,
4581 "end of file while defining macro `%1'",
4582 nm.contents());
4583 else
4584 error("end of file while defining macro `%1'", nm.contents());
4586 else {
4587 if (have_start_location)
4588 error_with_file_and_line(start_filename, start_lineno,
4589 "end of file while ignoring input lines");
4590 else
4591 error("end of file while ignoring input lines");
4593 tok.next();
4594 return;
4596 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4597 if (c == 0)
4598 mac.append(n);
4599 else
4600 mac.append(c);
4602 bol = (c == '\n');
4603 c = get_copy(&n, 1);
4607 void define_macro()
4609 do_define_macro(DEFINE_NORMAL, CALLING_NORMAL,
4610 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4613 void define_nocomp_macro()
4615 do_define_macro(DEFINE_NORMAL, CALLING_NORMAL, COMP_DISABLE);
4618 void define_indirect_macro()
4620 do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT,
4621 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4624 void define_indirect_nocomp_macro()
4626 do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT, COMP_DISABLE);
4629 void append_macro()
4631 do_define_macro(DEFINE_APPEND, CALLING_NORMAL,
4632 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4635 void append_nocomp_macro()
4637 do_define_macro(DEFINE_APPEND, CALLING_NORMAL, COMP_DISABLE);
4640 void append_indirect_macro()
4642 do_define_macro(DEFINE_APPEND, CALLING_INDIRECT,
4643 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4646 void append_indirect_nocomp_macro()
4648 do_define_macro(DEFINE_APPEND, CALLING_INDIRECT, COMP_DISABLE);
4651 void ignore()
4653 ignoring = 1;
4654 do_define_macro(DEFINE_IGNORE, CALLING_NORMAL, COMP_IGNORE);
4655 ignoring = 0;
4658 void remove_macro()
4660 for (;;) {
4661 symbol s = get_name();
4662 if (s.is_null())
4663 break;
4664 request_dictionary.remove(s);
4666 skip_line();
4669 void rename_macro()
4671 symbol s1 = get_name(1);
4672 if (!s1.is_null()) {
4673 symbol s2 = get_name(1);
4674 if (!s2.is_null())
4675 request_dictionary.rename(s1, s2);
4677 skip_line();
4680 void alias_macro()
4682 symbol s1 = get_name(1);
4683 if (!s1.is_null()) {
4684 symbol s2 = get_name(1);
4685 if (!s2.is_null()) {
4686 if (!request_dictionary.alias(s1, s2))
4687 warning(WARN_MAC, "macro `%1' not defined", s2.contents());
4690 skip_line();
4693 void chop_macro()
4695 symbol s = get_name(1);
4696 if (!s.is_null()) {
4697 request_or_macro *p = lookup_request(s);
4698 macro *m = p->to_macro();
4699 if (!m)
4700 error("cannot chop request");
4701 else if (m->empty())
4702 error("cannot chop empty macro");
4703 else {
4704 int have_restore = 0;
4705 // we have to check for additional save/restore pairs which could be
4706 // there due to empty am1 requests.
4707 for (;;) {
4708 if (m->get(m->len - 1) != POP_GROFFCOMP_MODE)
4709 break;
4710 have_restore = 1;
4711 m->len -= 1;
4712 if (m->get(m->len - 1) != PUSH_GROFF_MODE
4713 && m->get(m->len - 1) != PUSH_COMP_MODE)
4714 break;
4715 have_restore = 0;
4716 m->len -= 1;
4717 if (m->len == 0)
4718 break;
4720 if (m->len == 0)
4721 error("cannot chop empty macro");
4722 else {
4723 if (have_restore)
4724 m->set(POP_GROFFCOMP_MODE, m->len - 1);
4725 else
4726 m->len -= 1;
4730 skip_line();
4733 void substring_request()
4735 int start; // 0, 1, ..., n-1 or -1, -2, ...
4736 symbol s = get_name(1);
4737 if (!s.is_null() && get_integer(&start)) {
4738 request_or_macro *p = lookup_request(s);
4739 macro *m = p->to_macro();
4740 if (!m)
4741 error("cannot apply `substring' on a request");
4742 else {
4743 int end = -1;
4744 if (!has_arg() || get_integer(&end)) {
4745 int real_length = 0; // 1, 2, ..., n
4746 string_iterator iter1(*m);
4747 for (int l = 0; l < m->len; l++) {
4748 int c = iter1.get(0);
4749 if (c == PUSH_GROFF_MODE
4750 || c == PUSH_COMP_MODE
4751 || c == POP_GROFFCOMP_MODE)
4752 continue;
4753 if (c == EOF)
4754 break;
4755 real_length++;
4757 if (start < 0)
4758 start += real_length;
4759 if (end < 0)
4760 end += real_length;
4761 if (start > end) {
4762 int tem = start;
4763 start = end;
4764 end = tem;
4766 if (start >= real_length || end < 0) {
4767 warning(WARN_RANGE,
4768 "start and end index of substring out of range");
4769 m->len = 0;
4770 if (m->p) {
4771 if (--(m->p->count) <= 0)
4772 delete m->p;
4773 m->p = 0;
4775 skip_line();
4776 return;
4778 if (start < 0) {
4779 warning(WARN_RANGE,
4780 "start index of substring out of range, set to 0");
4781 start = 0;
4783 if (end >= real_length) {
4784 warning(WARN_RANGE,
4785 "end index of substring out of range, set to string length");
4786 end = real_length - 1;
4788 // now extract the substring
4789 string_iterator iter(*m);
4790 int i;
4791 for (i = 0; i < start; i++) {
4792 int c = iter.get(0);
4793 while (c == PUSH_GROFF_MODE
4794 || c == PUSH_COMP_MODE
4795 || c == POP_GROFFCOMP_MODE)
4796 c = iter.get(0);
4797 if (c == EOF)
4798 break;
4800 macro mac;
4801 for (; i <= end; i++) {
4802 node *nd = 0; // pacify compiler
4803 int c = iter.get(&nd);
4804 while (c == PUSH_GROFF_MODE
4805 || c == PUSH_COMP_MODE
4806 || c == POP_GROFFCOMP_MODE)
4807 c = iter.get(0);
4808 if (c == EOF)
4809 break;
4810 if (c == 0)
4811 mac.append(nd);
4812 else
4813 mac.append((unsigned char)c);
4815 *m = mac;
4819 skip_line();
4822 void length_request()
4824 symbol ret;
4825 ret = get_name(1);
4826 if (ret.is_null()) {
4827 skip_line();
4828 return;
4830 int c;
4831 node *n;
4832 if (tok.newline())
4833 c = '\n';
4834 else if (tok.tab())
4835 c = '\t';
4836 else if (!tok.space()) {
4837 error("bad string definition");
4838 skip_line();
4839 return;
4841 else
4842 c = get_copy(&n);
4843 while (c == ' ')
4844 c = get_copy(&n);
4845 if (c == '"')
4846 c = get_copy(&n);
4847 int len = 0;
4848 while (c != '\n' && c != EOF) {
4849 ++len;
4850 c = get_copy(&n);
4852 reg *r = (reg*)number_reg_dictionary.lookup(ret);
4853 if (r)
4854 r->set_value(len);
4855 else
4856 set_number_reg(ret, len);
4857 tok.next();
4860 void asciify_macro()
4862 symbol s = get_name(1);
4863 if (!s.is_null()) {
4864 request_or_macro *p = lookup_request(s);
4865 macro *m = p->to_macro();
4866 if (!m)
4867 error("cannot asciify request");
4868 else {
4869 macro am;
4870 string_iterator iter(*m);
4871 for (;;) {
4872 node *nd = 0; // pacify compiler
4873 int c = iter.get(&nd);
4874 if (c == EOF)
4875 break;
4876 if (c != 0)
4877 am.append(c);
4878 else
4879 nd->asciify(&am);
4881 *m = am;
4884 skip_line();
4887 void unformat_macro()
4889 symbol s = get_name(1);
4890 if (!s.is_null()) {
4891 request_or_macro *p = lookup_request(s);
4892 macro *m = p->to_macro();
4893 if (!m)
4894 error("cannot unformat request");
4895 else {
4896 macro am;
4897 string_iterator iter(*m);
4898 for (;;) {
4899 node *nd = 0; // pacify compiler
4900 int c = iter.get(&nd);
4901 if (c == EOF)
4902 break;
4903 if (c != 0)
4904 am.append(c);
4905 else {
4906 if (nd->set_unformat_flag())
4907 am.append(nd);
4910 *m = am;
4913 skip_line();
4916 static void interpolate_environment_variable(symbol nm)
4918 const char *s = getenv(nm.contents());
4919 if (s && *s)
4920 input_stack::push(make_temp_iterator(s));
4923 void interpolate_number_reg(symbol nm, int inc)
4925 reg *r = lookup_number_reg(nm);
4926 if (inc < 0)
4927 r->decrement();
4928 else if (inc > 0)
4929 r->increment();
4930 input_stack::push(make_temp_iterator(r->get_string()));
4933 static void interpolate_number_format(symbol nm)
4935 reg *r = (reg *)number_reg_dictionary.lookup(nm);
4936 if (r)
4937 input_stack::push(make_temp_iterator(r->get_format()));
4940 static int get_delim_number(units *n, unsigned char si, int prev_value)
4942 token start;
4943 start.next();
4944 if (start.delimiter(1)) {
4945 tok.next();
4946 if (get_number(n, si, prev_value)) {
4947 if (start != tok)
4948 warning(WARN_DELIM, "closing delimiter does not match");
4949 return 1;
4952 return 0;
4955 static int get_delim_number(units *n, unsigned char si)
4957 token start;
4958 start.next();
4959 if (start.delimiter(1)) {
4960 tok.next();
4961 if (get_number(n, si)) {
4962 if (start != tok)
4963 warning(WARN_DELIM, "closing delimiter does not match");
4964 return 1;
4967 return 0;
4970 static int get_line_arg(units *n, unsigned char si, charinfo **cp)
4972 token start;
4973 start.next();
4974 int start_level = input_stack::get_level();
4975 if (!start.delimiter(1))
4976 return 0;
4977 tok.next();
4978 if (get_number(n, si)) {
4979 if (tok.dummy() || tok.transparent_dummy())
4980 tok.next();
4981 if (!(start == tok && input_stack::get_level() == start_level)) {
4982 *cp = tok.get_char(1);
4983 tok.next();
4985 if (!(start == tok && input_stack::get_level() == start_level))
4986 warning(WARN_DELIM, "closing delimiter does not match");
4987 return 1;
4989 return 0;
4992 static int read_size(int *x)
4994 tok.next();
4995 int c = tok.ch();
4996 int inc = 0;
4997 if (c == '-') {
4998 inc = -1;
4999 tok.next();
5000 c = tok.ch();
5002 else if (c == '+') {
5003 inc = 1;
5004 tok.next();
5005 c = tok.ch();
5007 int val = 0; // pacify compiler
5008 int bad = 0;
5009 if (c == '(') {
5010 tok.next();
5011 c = tok.ch();
5012 if (!inc) {
5013 // allow an increment either before or after the left parenthesis
5014 if (c == '-') {
5015 inc = -1;
5016 tok.next();
5017 c = tok.ch();
5019 else if (c == '+') {
5020 inc = 1;
5021 tok.next();
5022 c = tok.ch();
5025 if (!csdigit(c))
5026 bad = 1;
5027 else {
5028 val = c - '0';
5029 tok.next();
5030 c = tok.ch();
5031 if (!csdigit(c))
5032 bad = 1;
5033 else {
5034 val = val*10 + (c - '0');
5035 val *= sizescale;
5039 else if (csdigit(c)) {
5040 val = c - '0';
5041 if (!inc && c != '0' && c < '4') {
5042 tok.next();
5043 c = tok.ch();
5044 if (!csdigit(c))
5045 bad = 1;
5046 else
5047 val = val*10 + (c - '0');
5049 val *= sizescale;
5051 else if (!tok.delimiter(1))
5052 return 0;
5053 else {
5054 token start(tok);
5055 tok.next();
5056 c = tok.ch();
5057 if (!inc && (c == '-' || c == '+')) {
5058 inc = c == '+' ? 1 : -1;
5059 tok.next();
5061 if (!get_number(&val, 'z'))
5062 return 0;
5063 if (!(start.ch() == '[' && tok.ch() == ']') && start != tok) {
5064 if (start.ch() == '[')
5065 error("missing `]'");
5066 else
5067 error("missing closing delimiter");
5068 return 0;
5071 if (!bad) {
5072 switch (inc) {
5073 case 0:
5074 if (val == 0) {
5075 // special case -- \s[0] and \s0 means to revert to previous size
5076 *x = 0;
5077 return 1;
5079 *x = val;
5080 break;
5081 case 1:
5082 *x = curenv->get_requested_point_size() + val;
5083 break;
5084 case -1:
5085 *x = curenv->get_requested_point_size() - val;
5086 break;
5087 default:
5088 assert(0);
5090 if (*x <= 0) {
5091 warning(WARN_RANGE,
5092 "\\s escape results in non-positive point size; set to 1");
5093 *x = 1;
5095 return 1;
5097 else {
5098 error("bad digit in point size");
5099 return 0;
5103 static symbol get_delim_name()
5105 token start;
5106 start.next();
5107 if (start.eof()) {
5108 error("end of input at start of delimited name");
5109 return NULL_SYMBOL;
5111 if (start.newline()) {
5112 error("can't delimit name with a newline");
5113 return NULL_SYMBOL;
5115 int start_level = input_stack::get_level();
5116 char abuf[ABUF_SIZE];
5117 char *buf = abuf;
5118 int buf_size = ABUF_SIZE;
5119 int i = 0;
5120 for (;;) {
5121 if (i + 1 > buf_size) {
5122 if (buf == abuf) {
5123 buf = new char[ABUF_SIZE*2];
5124 memcpy(buf, abuf, buf_size);
5125 buf_size = ABUF_SIZE*2;
5127 else {
5128 char *old_buf = buf;
5129 buf = new char[buf_size*2];
5130 memcpy(buf, old_buf, buf_size);
5131 buf_size *= 2;
5132 a_delete old_buf;
5135 tok.next();
5136 if (tok == start
5137 && (compatible_flag || input_stack::get_level() == start_level))
5138 break;
5139 if ((buf[i] = tok.ch()) == 0) {
5140 error("missing delimiter (got %1)", tok.description());
5141 if (buf != abuf)
5142 a_delete buf;
5143 return NULL_SYMBOL;
5145 i++;
5147 buf[i] = '\0';
5148 if (buf == abuf) {
5149 if (i == 0) {
5150 error("empty delimited name");
5151 return NULL_SYMBOL;
5153 else
5154 return symbol(buf);
5156 else {
5157 symbol s(buf);
5158 a_delete buf;
5159 return s;
5163 // Implement \R
5165 static void do_register()
5167 token start;
5168 start.next();
5169 if (!start.delimiter(1))
5170 return;
5171 tok.next();
5172 symbol nm = get_long_name(1);
5173 if (nm.is_null())
5174 return;
5175 while (tok.space())
5176 tok.next();
5177 reg *r = (reg *)number_reg_dictionary.lookup(nm);
5178 int prev_value;
5179 if (!r || !r->get_value(&prev_value))
5180 prev_value = 0;
5181 int val;
5182 if (!get_number(&val, 'u', prev_value))
5183 return;
5184 if (start != tok)
5185 warning(WARN_DELIM, "closing delimiter does not match");
5186 if (r)
5187 r->set_value(val);
5188 else
5189 set_number_reg(nm, val);
5192 // this implements the \w escape sequence
5194 static void do_width()
5196 token start;
5197 start.next();
5198 int start_level = input_stack::get_level();
5199 environment env(curenv);
5200 environment *oldenv = curenv;
5201 curenv = &env;
5202 for (;;) {
5203 tok.next();
5204 if (tok.eof()) {
5205 warning(WARN_DELIM, "missing closing delimiter");
5206 break;
5208 if (tok.newline()) {
5209 warning(WARN_DELIM, "missing closing delimiter");
5210 input_stack::push(make_temp_iterator("\n"));
5211 break;
5213 if (tok == start
5214 && (compatible_flag || input_stack::get_level() == start_level))
5215 break;
5216 tok.process();
5218 env.wrap_up_tab();
5219 units x = env.get_input_line_position().to_units();
5220 input_stack::push(make_temp_iterator(i_to_a(x)));
5221 env.width_registers();
5222 curenv = oldenv;
5223 have_input = 0;
5226 charinfo *page_character;
5228 void set_page_character()
5230 page_character = get_optional_char();
5231 skip_line();
5234 static const symbol percent_symbol("%");
5236 void read_title_parts(node **part, hunits *part_width)
5238 tok.skip();
5239 if (tok.newline() || tok.eof())
5240 return;
5241 token start(tok);
5242 int start_level = input_stack::get_level();
5243 tok.next();
5244 for (int i = 0; i < 3; i++) {
5245 while (!tok.newline() && !tok.eof()) {
5246 if (tok == start
5247 && (compatible_flag || input_stack::get_level() == start_level)) {
5248 tok.next();
5249 break;
5251 if (page_character != 0 && tok.get_char() == page_character)
5252 interpolate_number_reg(percent_symbol, 0);
5253 else
5254 tok.process();
5255 tok.next();
5257 curenv->wrap_up_tab();
5258 part_width[i] = curenv->get_input_line_position();
5259 part[i] = curenv->extract_output_line();
5261 while (!tok.newline() && !tok.eof())
5262 tok.next();
5265 class non_interpreted_node : public node {
5266 macro mac;
5267 public:
5268 non_interpreted_node(const macro &);
5269 int interpret(macro *);
5270 node *copy();
5271 int ends_sentence();
5272 int same(node *);
5273 const char *type();
5274 int force_tprint();
5275 int is_tag();
5278 non_interpreted_node::non_interpreted_node(const macro &m) : mac(m)
5282 int non_interpreted_node::ends_sentence()
5284 return 2;
5287 int non_interpreted_node::same(node *nd)
5289 return mac == ((non_interpreted_node *)nd)->mac;
5292 const char *non_interpreted_node::type()
5294 return "non_interpreted_node";
5297 int non_interpreted_node::force_tprint()
5299 return 0;
5302 int non_interpreted_node::is_tag()
5304 return 0;
5307 node *non_interpreted_node::copy()
5309 return new non_interpreted_node(mac);
5312 int non_interpreted_node::interpret(macro *m)
5314 string_iterator si(mac);
5315 node *n = 0; // pacify compiler
5316 for (;;) {
5317 int c = si.get(&n);
5318 if (c == EOF)
5319 break;
5320 if (c == 0)
5321 m->append(n);
5322 else
5323 m->append(c);
5325 return 1;
5328 static node *do_non_interpreted()
5330 node *n;
5331 int c;
5332 macro mac;
5333 while ((c = get_copy(&n)) != ESCAPE_QUESTION && c != EOF && c != '\n')
5334 if (c == 0)
5335 mac.append(n);
5336 else
5337 mac.append(c);
5338 if (c == EOF || c == '\n') {
5339 error("missing \\?");
5340 return 0;
5342 return new non_interpreted_node(mac);
5345 static void encode_char(macro *mac, char c)
5347 if (c == '\0') {
5348 if ((font::use_charnames_in_special) && tok.special()) {
5349 charinfo *ci = tok.get_char(1);
5350 const char *s = ci->get_symbol()->contents();
5351 if (s[0] != (char)0) {
5352 mac->append('\\');
5353 mac->append('[');
5354 int i = 0;
5355 while (s[i] != (char)0) {
5356 mac->append(s[i]);
5357 i++;
5359 mac->append(']');
5362 else if (tok.stretchable_space()
5363 || tok.unstretchable_space())
5364 mac->append(' ');
5365 else if (!(tok.hyphen_indicator()
5366 || tok.dummy()
5367 || tok.transparent_dummy()
5368 || tok.zero_width_break()))
5369 error("%1 is invalid within \\X", tok.description());
5371 else {
5372 if ((font::use_charnames_in_special) && (c == '\\')) {
5374 * add escape escape sequence
5376 mac->append(c);
5378 mac->append(c);
5382 node *do_special()
5384 token start;
5385 start.next();
5386 int start_level = input_stack::get_level();
5387 macro mac;
5388 for (tok.next();
5389 tok != start || input_stack::get_level() != start_level;
5390 tok.next()) {
5391 if (tok.eof()) {
5392 warning(WARN_DELIM, "missing closing delimiter");
5393 return 0;
5395 if (tok.newline()) {
5396 input_stack::push(make_temp_iterator("\n"));
5397 warning(WARN_DELIM, "missing closing delimiter");
5398 break;
5400 unsigned char c;
5401 if (tok.space())
5402 c = ' ';
5403 else if (tok.tab())
5404 c = '\t';
5405 else if (tok.leader())
5406 c = '\001';
5407 else if (tok.backspace())
5408 c = '\b';
5409 else
5410 c = tok.ch();
5411 encode_char(&mac, c);
5413 return new special_node(mac);
5416 void device_request()
5418 if (!tok.newline() && !tok.eof()) {
5419 int c;
5420 macro mac;
5421 for (;;) {
5422 c = get_copy(0);
5423 if (c == '"') {
5424 c = get_copy(0);
5425 break;
5427 if (c != ' ' && c != '\t')
5428 break;
5430 for (; c != '\n' && c != EOF; c = get_copy(0))
5431 mac.append(c);
5432 curenv->add_node(new special_node(mac));
5434 tok.next();
5437 void device_macro_request()
5439 symbol s = get_name(1);
5440 if (!(s.is_null() || s.is_empty())) {
5441 request_or_macro *p = lookup_request(s);
5442 macro *m = p->to_macro();
5443 if (m)
5444 curenv->add_node(new special_node(*m));
5445 else
5446 error("can't transparently throughput a request");
5448 skip_line();
5451 void output_request()
5453 if (!tok.newline() && !tok.eof()) {
5454 int c;
5455 for (;;) {
5456 c = get_copy(0);
5457 if (c == '"') {
5458 c = get_copy(0);
5459 break;
5461 if (c != ' ' && c != '\t')
5462 break;
5464 for (; c != '\n' && c != EOF; c = get_copy(0))
5465 topdiv->transparent_output(c);
5466 topdiv->transparent_output('\n');
5468 tok.next();
5471 extern int image_no; // from node.cpp
5473 static node *do_suppress(symbol nm)
5475 if (nm.is_null() || nm.is_empty()) {
5476 error("expecting an argument to escape \\O");
5477 return 0;
5479 const char *s = nm.contents();
5480 switch (*s) {
5481 case '0':
5482 if (begin_level == 0)
5483 // suppress generation of glyphs
5484 return new suppress_node(0, 0);
5485 break;
5486 case '1':
5487 if (begin_level == 0)
5488 // enable generation of glyphs
5489 return new suppress_node(1, 0);
5490 break;
5491 case '2':
5492 if (begin_level == 0)
5493 return new suppress_node(1, 1);
5494 break;
5495 case '3':
5496 have_input = 1;
5497 begin_level++;
5498 break;
5499 case '4':
5500 have_input = 1;
5501 begin_level--;
5502 break;
5503 case '5':
5505 s++; // move over '5'
5506 char position = *s;
5507 if (*s == (char)0) {
5508 error("missing position and filename in \\O");
5509 return 0;
5511 if (!(position == 'l'
5512 || position == 'r'
5513 || position == 'c'
5514 || position == 'i')) {
5515 error("l, r, c, or i position expected (got %1 in \\O)", position);
5516 return 0;
5518 s++; // onto image name
5519 if (s == (char *)0) {
5520 error("missing image name for \\O");
5521 return 0;
5523 image_no++;
5524 if (begin_level == 0)
5525 return new suppress_node(symbol(s), position, image_no);
5526 else
5527 have_input = 1;
5529 break;
5530 default:
5531 error("`%1' is an invalid argument to \\O", *s);
5533 return 0;
5536 void special_node::tprint(troff_output_file *out)
5538 tprint_start(out);
5539 string_iterator iter(mac);
5540 for (;;) {
5541 int c = iter.get(0);
5542 if (c == EOF)
5543 break;
5544 for (const char *s = ::asciify(c); *s; s++)
5545 tprint_char(out, *s);
5547 tprint_end(out);
5550 int get_file_line(const char **filename, int *lineno)
5552 return input_stack::get_location(0, filename, lineno);
5555 void line_file()
5557 int n;
5558 if (get_integer(&n)) {
5559 const char *filename = 0;
5560 if (has_arg()) {
5561 symbol s = get_long_name();
5562 filename = s.contents();
5564 (void)input_stack::set_location(filename, n-1);
5566 skip_line();
5569 static int nroff_mode = 0;
5571 static void nroff_request()
5573 nroff_mode = 1;
5574 skip_line();
5577 static void troff_request()
5579 nroff_mode = 0;
5580 skip_line();
5583 static void skip_alternative()
5585 int level = 0;
5586 // ensure that ``.if 0\{'' works as expected
5587 if (tok.left_brace())
5588 level++;
5589 int c;
5590 for (;;) {
5591 c = input_stack::get(0);
5592 if (c == EOF)
5593 break;
5594 if (c == ESCAPE_LEFT_BRACE)
5595 ++level;
5596 else if (c == ESCAPE_RIGHT_BRACE)
5597 --level;
5598 else if (c == escape_char && escape_char > 0)
5599 switch(input_stack::get(0)) {
5600 case '{':
5601 ++level;
5602 break;
5603 case '}':
5604 --level;
5605 break;
5606 case '"':
5607 while ((c = input_stack::get(0)) != '\n' && c != EOF)
5611 Note that the level can properly be < 0, eg
5613 .if 1 \{\
5614 .if 0 \{\
5615 .\}\}
5617 So don't give an error message in this case.
5619 if (level <= 0 && c == '\n')
5620 break;
5622 tok.next();
5625 static void begin_alternative()
5627 while (tok.space() || tok.left_brace())
5628 tok.next();
5631 void nop_request()
5633 while (tok.space())
5634 tok.next();
5637 static int_stack if_else_stack;
5639 int do_if_request()
5641 int invert = 0;
5642 while (tok.space())
5643 tok.next();
5644 while (tok.ch() == '!') {
5645 tok.next();
5646 invert = !invert;
5648 int result;
5649 unsigned char c = tok.ch();
5650 if (c == 't') {
5651 tok.next();
5652 result = !nroff_mode;
5654 else if (c == 'n') {
5655 tok.next();
5656 result = nroff_mode;
5658 else if (c == 'v') {
5659 tok.next();
5660 result = 0;
5662 else if (c == 'o') {
5663 result = (topdiv->get_page_number() & 1);
5664 tok.next();
5666 else if (c == 'e') {
5667 result = !(topdiv->get_page_number() & 1);
5668 tok.next();
5670 else if (c == 'd' || c == 'r') {
5671 tok.next();
5672 symbol nm = get_name(1);
5673 if (nm.is_null()) {
5674 skip_alternative();
5675 return 0;
5677 result = (c == 'd'
5678 ? request_dictionary.lookup(nm) != 0
5679 : number_reg_dictionary.lookup(nm) != 0);
5681 else if (c == 'm') {
5682 tok.next();
5683 symbol nm = get_long_name(1);
5684 if (nm.is_null()) {
5685 skip_alternative();
5686 return 0;
5688 result = (nm == default_symbol
5689 || color_dictionary.lookup(nm) != 0);
5691 else if (c == 'c') {
5692 tok.next();
5693 tok.skip();
5694 charinfo *ci = tok.get_char(1);
5695 if (ci == 0) {
5696 skip_alternative();
5697 return 0;
5699 result = character_exists(ci, curenv);
5700 tok.next();
5702 else if (c == 'F') {
5703 tok.next();
5704 symbol nm = get_long_name(1);
5705 if (nm.is_null()) {
5706 skip_alternative();
5707 return 0;
5709 result = check_font(curenv->get_family()->nm, nm);
5711 else if (c == 'S') {
5712 tok.next();
5713 symbol nm = get_long_name(1);
5714 if (nm.is_null()) {
5715 skip_alternative();
5716 return 0;
5718 result = check_style(nm);
5720 else if (tok.space())
5721 result = 0;
5722 else if (tok.delimiter()) {
5723 token delim = tok;
5724 int delim_level = input_stack::get_level();
5725 environment env1(curenv);
5726 environment env2(curenv);
5727 environment *oldenv = curenv;
5728 curenv = &env1;
5729 suppress_push = 1;
5730 for (int i = 0; i < 2; i++) {
5731 for (;;) {
5732 tok.next();
5733 if (tok.newline() || tok.eof()) {
5734 warning(WARN_DELIM, "missing closing delimiter");
5735 tok.next();
5736 curenv = oldenv;
5737 return 0;
5739 if (tok == delim
5740 && (compatible_flag || input_stack::get_level() == delim_level))
5741 break;
5742 tok.process();
5744 curenv = &env2;
5746 node *n1 = env1.extract_output_line();
5747 node *n2 = env2.extract_output_line();
5748 result = same_node_list(n1, n2);
5749 delete_node_list(n1);
5750 delete_node_list(n2);
5751 curenv = oldenv;
5752 have_input = 0;
5753 suppress_push = 0;
5754 tok.next();
5756 else {
5757 units n;
5758 if (!get_number(&n, 'u')) {
5759 skip_alternative();
5760 return 0;
5762 else
5763 result = n > 0;
5765 if (invert)
5766 result = !result;
5767 if (result)
5768 begin_alternative();
5769 else
5770 skip_alternative();
5771 return result;
5774 void if_else_request()
5776 if_else_stack.push(do_if_request());
5779 void if_request()
5781 do_if_request();
5784 void else_request()
5786 if (if_else_stack.is_empty()) {
5787 warning(WARN_EL, "unbalanced .el request");
5788 skip_alternative();
5790 else {
5791 if (if_else_stack.pop())
5792 skip_alternative();
5793 else
5794 begin_alternative();
5798 static int while_depth = 0;
5799 static int while_break_flag = 0;
5801 void while_request()
5803 macro mac;
5804 int escaped = 0;
5805 int level = 0;
5806 mac.append(new token_node(tok));
5807 for (;;) {
5808 node *n = 0; // pacify compiler
5809 int c = input_stack::get(&n);
5810 if (c == EOF)
5811 break;
5812 if (c == 0) {
5813 escaped = 0;
5814 mac.append(n);
5816 else if (escaped) {
5817 if (c == '{')
5818 level += 1;
5819 else if (c == '}')
5820 level -= 1;
5821 escaped = 0;
5822 mac.append(c);
5824 else {
5825 if (c == ESCAPE_LEFT_BRACE)
5826 level += 1;
5827 else if (c == ESCAPE_RIGHT_BRACE)
5828 level -= 1;
5829 else if (c == escape_char)
5830 escaped = 1;
5831 mac.append(c);
5832 if (c == '\n' && level <= 0)
5833 break;
5836 if (level != 0)
5837 error("unbalanced \\{ \\}");
5838 else {
5839 while_depth++;
5840 input_stack::add_boundary();
5841 for (;;) {
5842 input_stack::push(new string_iterator(mac, "while loop"));
5843 tok.next();
5844 if (!do_if_request()) {
5845 while (input_stack::get(0) != EOF)
5847 break;
5849 process_input_stack();
5850 if (while_break_flag || input_stack::is_return_boundary()) {
5851 while_break_flag = 0;
5852 break;
5855 input_stack::remove_boundary();
5856 while_depth--;
5858 tok.next();
5861 void while_break_request()
5863 if (!while_depth) {
5864 error("no while loop");
5865 skip_line();
5867 else {
5868 while_break_flag = 1;
5869 while (input_stack::get(0) != EOF)
5871 tok.next();
5875 void while_continue_request()
5877 if (!while_depth) {
5878 error("no while loop");
5879 skip_line();
5881 else {
5882 while (input_stack::get(0) != EOF)
5884 tok.next();
5888 // .so
5890 void source()
5892 symbol nm = get_long_name(1);
5893 if (nm.is_null())
5894 skip_line();
5895 else {
5896 while (!tok.newline() && !tok.eof())
5897 tok.next();
5898 file_case *fcp = include_search_path.open_file_cautious(nm.contents(),
5899 fcp->mux_unpack);
5900 if (fcp != NULL)
5901 input_stack::push(new file_iterator(fcp, nm.contents()));
5902 else
5903 error("can't open `%1': %2", nm.contents(), strerror(errno));
5904 tok.next();
5908 // like .so but use popen()
5910 void pipe_source()
5912 if (!unsafe_flag) {
5913 error(".pso request not allowed in safer mode");
5914 skip_line();
5916 else {
5917 #ifdef POPEN_MISSING
5918 error("pipes not available on this system");
5919 skip_line();
5920 #else /* not POPEN_MISSING */
5921 if (tok.newline() || tok.eof())
5922 error("missing command");
5923 else {
5924 int c;
5925 while ((c = get_copy(0)) == ' ' || c == '\t')
5927 int buf_size = 24;
5928 char *buf = new char[buf_size];
5929 int buf_used = 0;
5930 for (; c != '\n' && c != EOF; c = get_copy(0)) {
5931 const char *s = asciify(c);
5932 int slen = strlen(s);
5933 if (buf_used + slen + 1> buf_size) {
5934 char *old_buf = buf;
5935 int old_buf_size = buf_size;
5936 buf_size *= 2;
5937 buf = new char[buf_size];
5938 memcpy(buf, old_buf, old_buf_size);
5939 a_delete old_buf;
5941 strcpy(buf + buf_used, s);
5942 buf_used += slen;
5944 buf[buf_used] = '\0';
5945 errno = 0;
5946 FILE *fp = popen(buf, POPEN_RT);
5947 if (fp != NULL)
5948 input_stack::push(new file_iterator(
5949 new file_case(fp, buf, file_case::fc_pipe | file_case::fc_take_path),
5950 symbol(buf).contents()));
5951 else {
5952 error("can't open pipe to process `%1': %2", buf, strerror(errno));
5953 a_delete buf;
5956 tok.next();
5957 #endif /* not POPEN_MISSING */
5961 // .psbb
5963 static int llx_reg_contents = 0;
5964 static int lly_reg_contents = 0;
5965 static int urx_reg_contents = 0;
5966 static int ury_reg_contents = 0;
5968 struct bounding_box {
5969 int llx, lly, urx, ury;
5972 /* Parse the argument to a %%BoundingBox comment. Return 1 if it
5973 contains 4 numbers, 2 if it contains (atend), 0 otherwise. */
5975 int parse_bounding_box(char *p, bounding_box *bb)
5977 if (sscanf(p, "%d %d %d %d",
5978 &bb->llx, &bb->lly, &bb->urx, &bb->ury) == 4)
5979 return 1;
5980 else {
5981 /* The Document Structuring Conventions say that the numbers
5982 should be integers. Unfortunately some broken applications
5983 get this wrong. */
5984 double x1, x2, x3, x4;
5985 if (sscanf(p, "%lf %lf %lf %lf", &x1, &x2, &x3, &x4) == 4) {
5986 bb->llx = (int)x1;
5987 bb->lly = (int)x2;
5988 bb->urx = (int)x3;
5989 bb->ury = (int)x4;
5990 return 1;
5992 else {
5993 for (; *p == ' ' || *p == '\t'; p++)
5995 if (strncmp(p, "(atend)", 7) == 0) {
5996 return 2;
6000 bb->llx = bb->lly = bb->urx = bb->ury = 0;
6001 return 0;
6004 // This version is taken from psrm.cpp
6006 #define PS_LINE_MAX 255
6007 cset white_space("\n\r \t");
6009 int ps_get_line(char *buf, file_case *fcp, const char* filename)
6011 int c = fcp->get_c();
6012 if (c == EOF) {
6013 buf[0] = '\0';
6014 return 0;
6016 int i = 0;
6017 int err = 0;
6018 while (c != '\r' && c != '\n' && c != EOF) {
6019 if ((c < 0x1b && !white_space(c)) || c == 0x7f)
6020 error("invalid input character code %1 in `%2'", int(c), filename);
6021 else if (i < PS_LINE_MAX)
6022 buf[i++] = c;
6023 else if (!err) {
6024 err = 1;
6025 error("PostScript file `%1' is non-conforming "
6026 "because length of line exceeds 255", filename);
6028 c = fcp->get_c();
6030 buf[i++] = '\n';
6031 buf[i] = '\0';
6032 if (c == '\r') {
6033 c = fcp->get_c();
6034 if (c != EOF && c != '\n')
6035 fcp->unget_c(c);
6037 return 1;
6040 inline void assign_registers(int llx, int lly, int urx, int ury)
6042 llx_reg_contents = llx;
6043 lly_reg_contents = lly;
6044 urx_reg_contents = urx;
6045 ury_reg_contents = ury;
6048 void do_ps_file(file_case *fcp, const char* filename)
6050 bounding_box bb;
6051 int bb_at_end = 0;
6052 char buf[PS_LINE_MAX];
6053 llx_reg_contents = lly_reg_contents =
6054 urx_reg_contents = ury_reg_contents = 0;
6055 if (!ps_get_line(buf, fcp, filename)) {
6056 error("`%1' is empty", filename);
6057 return;
6059 if (strncmp("%!PS-Adobe-", buf, 11) != 0) {
6060 error("`%1' is not conforming to the Document Structuring Conventions",
6061 filename);
6062 return;
6064 while (ps_get_line(buf, fcp, filename) != 0) {
6065 // in header comments, `%X' (`X' any printable character except
6066 // whitespace) is possible too
6067 if (buf[0] == '%') {
6068 if (strncmp(buf + 1, "%EndComments", 12) == 0)
6069 break;
6070 if (white_space(buf[1]))
6071 break;
6073 else
6074 break;
6075 if (strncmp(buf + 1, "%BoundingBox:", 13) == 0) {
6076 int res = parse_bounding_box(buf + 14, &bb);
6077 if (res == 1) {
6078 assign_registers(bb.llx, bb.lly, bb.urx, bb.ury);
6079 return;
6081 else if (res == 2) {
6082 bb_at_end = 1;
6083 break;
6085 else {
6086 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
6087 filename);
6088 return;
6092 if (bb_at_end) {
6093 long offset;
6094 int last_try = 0;
6095 // in the trailer, the last BoundingBox comment is significant
6096 for (offset = 512; !last_try; offset *= 2) {
6097 int had_trailer = 0;
6098 int got_bb = 0;
6099 if (offset > 32768 || fcp->seek(-offset, fcp->seek_end) == -1) {
6100 last_try = 1;
6101 if (fcp->seek(0L, fcp->seek_set) == -1)
6102 break;
6104 while (ps_get_line(buf, fcp, filename) != 0) {
6105 if (buf[0] == '%' && buf[1] == '%') {
6106 if (!had_trailer) {
6107 if (strncmp(buf + 2, "Trailer", 7) == 0)
6108 had_trailer = 1;
6110 else {
6111 if (strncmp(buf + 2, "BoundingBox:", 12) == 0) {
6112 int res = parse_bounding_box(buf + 14, &bb);
6113 if (res == 1)
6114 got_bb = 1;
6115 else if (res == 2) {
6116 error("`(atend)' not allowed in trailer of `%1'", filename);
6117 return;
6119 else {
6120 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
6121 filename);
6122 return;
6128 if (got_bb) {
6129 assign_registers(bb.llx, bb.lly, bb.urx, bb.ury);
6130 return;
6134 error("%%%%BoundingBox comment not found in `%1'", filename);
6137 void ps_bbox_request()
6139 symbol nm = get_long_name(1);
6140 if (nm.is_null())
6141 skip_line();
6142 else {
6143 while (!tok.newline() && !tok.eof())
6144 tok.next();
6145 // PS files might contain non-printable characters, such as ^Z
6146 // and CRs not followed by an LF, so open them in binary mode.
6147 file_case *fcp = include_search_path.open_file_cautious(nm.contents(),
6148 fcp->mux_need_seek | fcp->mux_need_binary);
6149 if (fcp != NULL) {
6150 do_ps_file(fcp, nm.contents());
6151 delete fcp;
6152 } else
6153 error("can't open `%1': %2", nm.contents(), strerror(errno));
6154 tok.next();
6158 const char *asciify(int c)
6160 static char buf[3];
6161 buf[0] = escape_char == '\0' ? '\\' : escape_char;
6162 buf[1] = buf[2] = '\0';
6163 switch (c) {
6164 case ESCAPE_QUESTION:
6165 buf[1] = '?';
6166 break;
6167 case ESCAPE_AMPERSAND:
6168 buf[1] = '&';
6169 break;
6170 case ESCAPE_RIGHT_PARENTHESIS:
6171 buf[1] = ')';
6172 break;
6173 case ESCAPE_UNDERSCORE:
6174 buf[1] = '_';
6175 break;
6176 case ESCAPE_BAR:
6177 buf[1] = '|';
6178 break;
6179 case ESCAPE_CIRCUMFLEX:
6180 buf[1] = '^';
6181 break;
6182 case ESCAPE_LEFT_BRACE:
6183 buf[1] = '{';
6184 break;
6185 case ESCAPE_RIGHT_BRACE:
6186 buf[1] = '}';
6187 break;
6188 case ESCAPE_LEFT_QUOTE:
6189 buf[1] = '`';
6190 break;
6191 case ESCAPE_RIGHT_QUOTE:
6192 buf[1] = '\'';
6193 break;
6194 case ESCAPE_HYPHEN:
6195 buf[1] = '-';
6196 break;
6197 case ESCAPE_BANG:
6198 buf[1] = '!';
6199 break;
6200 case ESCAPE_c:
6201 buf[1] = 'c';
6202 break;
6203 case ESCAPE_e:
6204 buf[1] = 'e';
6205 break;
6206 case ESCAPE_E:
6207 buf[1] = 'E';
6208 break;
6209 case ESCAPE_PERCENT:
6210 buf[1] = '%';
6211 break;
6212 case ESCAPE_SPACE:
6213 buf[1] = ' ';
6214 break;
6215 case ESCAPE_TILDE:
6216 buf[1] = '~';
6217 break;
6218 case ESCAPE_COLON:
6219 buf[1] = ':';
6220 break;
6221 case PUSH_GROFF_MODE:
6222 case PUSH_COMP_MODE:
6223 case POP_GROFFCOMP_MODE:
6224 buf[0] = '\0';
6225 break;
6226 default:
6227 if (invalid_input_char(c))
6228 buf[0] = '\0';
6229 else
6230 buf[0] = c;
6231 break;
6233 return buf;
6236 const char *input_char_description(int c)
6238 switch (c) {
6239 case '\n':
6240 return "a newline character";
6241 case '\b':
6242 return "a backspace character";
6243 case '\001':
6244 return "a leader character";
6245 case '\t':
6246 return "a tab character";
6247 case ' ':
6248 return "a space character";
6249 case '\0':
6250 return "a node";
6252 static char buf[sizeof("magic character code ") + 1 + INT_DIGITS];
6253 if (invalid_input_char(c)) {
6254 const char *s = asciify(c);
6255 if (*s) {
6256 buf[0] = '`';
6257 strcpy(buf + 1, s);
6258 strcat(buf, "'");
6259 return buf;
6261 sprintf(buf, "magic character code %d", c);
6262 return buf;
6264 if (csprint(c)) {
6265 buf[0] = '`';
6266 buf[1] = c;
6267 buf[2] = '\'';
6268 return buf;
6270 sprintf(buf, "character code %d", c);
6271 return buf;
6274 void tag()
6276 if (!tok.newline() && !tok.eof()) {
6277 string s;
6278 int c;
6279 for (;;) {
6280 c = get_copy(0);
6281 if (c == '"') {
6282 c = get_copy(0);
6283 break;
6285 if (c != ' ' && c != '\t')
6286 break;
6288 s = "x X ";
6289 for (; c != '\n' && c != EOF; c = get_copy(0))
6290 s += (char)c;
6291 s += '\n';
6292 curenv->add_node(new tag_node(s, 0));
6294 tok.next();
6297 void taga()
6299 if (!tok.newline() && !tok.eof()) {
6300 string s;
6301 int c;
6302 for (;;) {
6303 c = get_copy(0);
6304 if (c == '"') {
6305 c = get_copy(0);
6306 break;
6308 if (c != ' ' && c != '\t')
6309 break;
6311 s = "x X ";
6312 for (; c != '\n' && c != EOF; c = get_copy(0))
6313 s += (char)c;
6314 s += '\n';
6315 curenv->add_node(new tag_node(s, 1));
6317 tok.next();
6320 // .tm, .tm1, and .tmc
6322 void do_terminal(int newline, int string_like)
6324 if (!tok.newline() && !tok.eof()) {
6325 int c;
6326 for (;;) {
6327 c = get_copy(0);
6328 if (string_like && c == '"') {
6329 c = get_copy(0);
6330 break;
6332 if (c != ' ' && c != '\t')
6333 break;
6335 for (; c != '\n' && c != EOF; c = get_copy(0))
6336 fputs(asciify(c), stderr);
6338 if (newline)
6339 fputc('\n', stderr);
6340 fflush(stderr);
6341 tok.next();
6344 void terminal()
6346 do_terminal(1, 0);
6349 void terminal1()
6351 do_terminal(1, 1);
6354 void terminal_continue()
6356 do_terminal(0, 1);
6359 dictionary stream_dictionary(20);
6361 void do_open(int append)
6363 symbol stream = get_name(1);
6364 if (!stream.is_null()) {
6365 symbol filename = get_long_name(1);
6366 if (!filename.is_null()) {
6367 errno = 0;
6368 FILE *fp = fopen(filename.contents(), append ? "a" : "w");
6369 if (!fp) {
6370 error("can't open `%1' for %2: %3",
6371 filename.contents(),
6372 append ? "appending" : "writing",
6373 strerror(errno));
6374 fp = (FILE *)stream_dictionary.remove(stream);
6376 else
6377 fp = (FILE *)stream_dictionary.lookup(stream, fp);
6378 if (fp)
6379 fclose(fp);
6382 skip_line();
6385 void open_request()
6387 if (!unsafe_flag) {
6388 error(".open request not allowed in safer mode");
6389 skip_line();
6391 else
6392 do_open(0);
6395 void opena_request()
6397 if (!unsafe_flag) {
6398 error(".opena request not allowed in safer mode");
6399 skip_line();
6401 else
6402 do_open(1);
6405 void close_request()
6407 symbol stream = get_name(1);
6408 if (!stream.is_null()) {
6409 FILE *fp = (FILE *)stream_dictionary.remove(stream);
6410 if (!fp)
6411 error("no stream named `%1'", stream.contents());
6412 else
6413 fclose(fp);
6415 skip_line();
6418 // .write and .writec
6420 void do_write_request(int newline)
6422 symbol stream = get_name(1);
6423 if (stream.is_null()) {
6424 skip_line();
6425 return;
6427 FILE *fp = (FILE *)stream_dictionary.lookup(stream);
6428 if (!fp) {
6429 error("no stream named `%1'", stream.contents());
6430 skip_line();
6431 return;
6433 int c;
6434 while ((c = get_copy(0)) == ' ')
6436 if (c == '"')
6437 c = get_copy(0);
6438 for (; c != '\n' && c != EOF; c = get_copy(0))
6439 fputs(asciify(c), fp);
6440 if (newline)
6441 fputc('\n', fp);
6442 fflush(fp);
6443 tok.next();
6446 void write_request()
6448 do_write_request(1);
6451 void write_request_continue()
6453 do_write_request(0);
6456 void write_macro_request()
6458 symbol stream = get_name(1);
6459 if (stream.is_null()) {
6460 skip_line();
6461 return;
6463 FILE *fp = (FILE *)stream_dictionary.lookup(stream);
6464 if (!fp) {
6465 error("no stream named `%1'", stream.contents());
6466 skip_line();
6467 return;
6469 symbol s = get_name(1);
6470 if (s.is_null()) {
6471 skip_line();
6472 return;
6474 request_or_macro *p = lookup_request(s);
6475 macro *m = p->to_macro();
6476 if (!m)
6477 error("cannot write request");
6478 else {
6479 string_iterator iter(*m);
6480 for (;;) {
6481 int c = iter.get(0);
6482 if (c == EOF)
6483 break;
6484 fputs(asciify(c), fp);
6486 fflush(fp);
6488 skip_line();
6491 void warnscale_request()
6493 if (has_arg()) {
6494 char c = tok.ch();
6495 if (c == 'u')
6496 warn_scale = 1.0;
6497 else if (c == 'i')
6498 warn_scale = (double)units_per_inch;
6499 else if (c == 'c')
6500 warn_scale = (double)units_per_inch / 2.54;
6501 else if (c == 'p')
6502 warn_scale = (double)units_per_inch / 72.0;
6503 else if (c == 'P')
6504 warn_scale = (double)units_per_inch / 6.0;
6505 else {
6506 warning(WARN_SCALE,
6507 "invalid scaling indicator `%1', using `i' instead", c);
6508 c = 'i';
6510 warn_scaling_indicator = c;
6512 skip_line();
6515 void spreadwarn_request()
6517 hunits n;
6518 if (has_arg() && get_hunits(&n, 'm')) {
6519 if (n < 0)
6520 n = 0;
6521 hunits em = curenv->get_size();
6522 spread_limit = (double)n.to_units()
6523 / (em.is_zero() ? hresolution : em.to_units());
6525 else
6526 spread_limit = -spread_limit - 1; // no arg toggles on/off without
6527 // changing value; we mirror at
6528 // -0.5 to make zero a valid value
6529 skip_line();
6532 static void init_charset_table()
6534 char buf[16];
6535 strcpy(buf, "char");
6536 for (int i = 0; i < 256; i++) {
6537 strcpy(buf + 4, i_to_a(i));
6538 charset_table[i] = get_charinfo(symbol(buf));
6539 charset_table[i]->set_ascii_code(i);
6540 if (csalpha(i))
6541 charset_table[i]->set_hyphenation_code(cmlower(i));
6543 charset_table['.']->set_flags(charinfo::ENDS_SENTENCE);
6544 charset_table['?']->set_flags(charinfo::ENDS_SENTENCE);
6545 charset_table['!']->set_flags(charinfo::ENDS_SENTENCE);
6546 charset_table['-']->set_flags(charinfo::BREAK_AFTER);
6547 charset_table['"']->set_flags(charinfo::TRANSPARENT);
6548 charset_table['\'']->set_flags(charinfo::TRANSPARENT);
6549 charset_table[')']->set_flags(charinfo::TRANSPARENT);
6550 charset_table[']']->set_flags(charinfo::TRANSPARENT);
6551 charset_table['*']->set_flags(charinfo::TRANSPARENT);
6552 get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT);
6553 get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT);
6554 get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER);
6555 get_charinfo(symbol("hy"))->set_flags(charinfo::BREAK_AFTER);
6556 get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6557 get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6558 get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6559 get_charinfo(symbol("sqrtex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6560 get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6561 get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY);
6562 page_character = charset_table['%'];
6565 static void init_hpf_code_table()
6567 for (int i = 0; i < 256; i++)
6568 hpf_code_table[i] = i;
6571 static void do_translate(int translate_transparent, int translate_input)
6573 tok.skip();
6574 while (!tok.newline() && !tok.eof()) {
6575 if (tok.space()) {
6576 // This is a really bizarre troff feature.
6577 tok.next();
6578 translate_space_to_dummy = tok.dummy();
6579 if (tok.newline() || tok.eof())
6580 break;
6581 tok.next();
6582 continue;
6584 charinfo *ci1 = tok.get_char(1);
6585 if (ci1 == 0)
6586 break;
6587 tok.next();
6588 if (tok.newline() || tok.eof()) {
6589 ci1->set_special_translation(charinfo::TRANSLATE_SPACE,
6590 translate_transparent);
6591 break;
6593 if (tok.space())
6594 ci1->set_special_translation(charinfo::TRANSLATE_SPACE,
6595 translate_transparent);
6596 else if (tok.stretchable_space())
6597 ci1->set_special_translation(charinfo::TRANSLATE_STRETCHABLE_SPACE,
6598 translate_transparent);
6599 else if (tok.dummy())
6600 ci1->set_special_translation(charinfo::TRANSLATE_DUMMY,
6601 translate_transparent);
6602 else if (tok.hyphen_indicator())
6603 ci1->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR,
6604 translate_transparent);
6605 else {
6606 charinfo *ci2 = tok.get_char(1);
6607 if (ci2 == 0)
6608 break;
6609 if (ci1 == ci2)
6610 ci1->set_translation(0, translate_transparent, translate_input);
6611 else
6612 ci1->set_translation(ci2, translate_transparent, translate_input);
6614 tok.next();
6616 skip_line();
6619 void translate()
6621 do_translate(1, 0);
6624 void translate_no_transparent()
6626 do_translate(0, 0);
6629 void translate_input()
6631 do_translate(1, 1);
6634 void char_flags()
6636 int flags;
6637 if (get_integer(&flags))
6638 while (has_arg()) {
6639 charinfo *ci = tok.get_char(1);
6640 if (ci) {
6641 charinfo *tem = ci->get_translation();
6642 if (tem)
6643 ci = tem;
6644 ci->set_flags(flags);
6646 tok.next();
6648 skip_line();
6651 void hyphenation_code()
6653 tok.skip();
6654 while (!tok.newline() && !tok.eof()) {
6655 charinfo *ci = tok.get_char(1);
6656 if (ci == 0)
6657 break;
6658 tok.next();
6659 tok.skip();
6660 unsigned char c = tok.ch();
6661 if (c == 0) {
6662 error("hyphenation code must be ordinary character");
6663 break;
6665 if (csdigit(c)) {
6666 error("hyphenation code cannot be digit");
6667 break;
6669 ci->set_hyphenation_code(c);
6670 if (ci->get_translation()
6671 && ci->get_translation()->get_translation_input())
6672 ci->get_translation()->set_hyphenation_code(c);
6673 tok.next();
6674 tok.skip();
6676 skip_line();
6679 void hyphenation_patterns_file_code()
6681 tok.skip();
6682 while (!tok.newline() && !tok.eof()) {
6683 int n1, n2;
6684 if (get_integer(&n1) && (0 <= n1 && n1 <= 255)) {
6685 if (!has_arg()) {
6686 error("missing output hyphenation code");
6687 break;
6689 if (get_integer(&n2) && (0 <= n2 && n2 <= 255)) {
6690 hpf_code_table[n1] = n2;
6691 tok.skip();
6693 else {
6694 error("output hyphenation code must be integer in the range 0..255");
6695 break;
6698 else {
6699 error("input hyphenation code must be integer in the range 0..255");
6700 break;
6703 skip_line();
6706 charinfo *token::get_char(int required)
6708 if (type == TOKEN_CHAR)
6709 return charset_table[c];
6710 if (type == TOKEN_SPECIAL)
6711 return get_charinfo(nm);
6712 if (type == TOKEN_NUMBERED_CHAR)
6713 return get_charinfo_by_number(val);
6714 if (type == TOKEN_ESCAPE) {
6715 if (escape_char != 0)
6716 return charset_table[escape_char];
6717 else {
6718 error("`\\e' used while no current escape character");
6719 return 0;
6722 if (required) {
6723 if (type == TOKEN_EOF || type == TOKEN_NEWLINE)
6724 warning(WARN_MISSING, "missing normal or special character");
6725 else
6726 error("normal or special character expected (got %1)", description());
6728 return 0;
6731 charinfo *get_optional_char()
6733 while (tok.space())
6734 tok.next();
6735 charinfo *ci = tok.get_char();
6736 if (!ci)
6737 check_missing_character();
6738 else
6739 tok.next();
6740 return ci;
6743 void check_missing_character()
6745 if (!tok.newline() && !tok.eof() && !tok.right_brace() && !tok.tab())
6746 error("normal or special character expected (got %1): "
6747 "treated as missing",
6748 tok.description());
6751 // this is for \Z
6753 int token::add_to_node_list(node **pp)
6755 hunits w;
6756 int s;
6757 node *n = 0;
6758 switch (type) {
6759 case TOKEN_CHAR:
6760 *pp = (*pp)->add_char(charset_table[c], curenv, &w, &s);
6761 break;
6762 case TOKEN_DUMMY:
6763 n = new dummy_node;
6764 break;
6765 case TOKEN_ESCAPE:
6766 if (escape_char != 0)
6767 *pp = (*pp)->add_char(charset_table[escape_char], curenv, &w, &s);
6768 break;
6769 case TOKEN_HYPHEN_INDICATOR:
6770 *pp = (*pp)->add_discretionary_hyphen();
6771 break;
6772 case TOKEN_ITALIC_CORRECTION:
6773 *pp = (*pp)->add_italic_correction(&w);
6774 break;
6775 case TOKEN_LEFT_BRACE:
6776 break;
6777 case TOKEN_MARK_INPUT:
6778 set_number_reg(nm, curenv->get_input_line_position().to_units());
6779 break;
6780 case TOKEN_NODE:
6781 n = nd;
6782 nd = 0;
6783 break;
6784 case TOKEN_NUMBERED_CHAR:
6785 *pp = (*pp)->add_char(get_charinfo_by_number(val), curenv, &w, &s);
6786 break;
6787 case TOKEN_RIGHT_BRACE:
6788 break;
6789 case TOKEN_SPACE:
6790 n = new hmotion_node(curenv->get_space_width(),
6791 curenv->get_fill_color());
6792 break;
6793 case TOKEN_SPECIAL:
6794 *pp = (*pp)->add_char(get_charinfo(nm), curenv, &w, &s);
6795 break;
6796 case TOKEN_STRETCHABLE_SPACE:
6797 n = new unbreakable_space_node(curenv->get_space_width(),
6798 curenv->get_fill_color());
6799 break;
6800 case TOKEN_UNSTRETCHABLE_SPACE:
6801 n = new space_char_hmotion_node(curenv->get_space_width(),
6802 curenv->get_fill_color());
6803 break;
6804 case TOKEN_TRANSPARENT_DUMMY:
6805 n = new transparent_dummy_node;
6806 break;
6807 case TOKEN_ZERO_WIDTH_BREAK:
6808 n = new space_node(H0, curenv->get_fill_color());
6809 n->freeze_space();
6810 n->is_escape_colon();
6811 break;
6812 default:
6813 return 0;
6815 if (n) {
6816 n->next = *pp;
6817 *pp = n;
6819 return 1;
6822 void token::process()
6824 if (possibly_handle_first_page_transition())
6825 return;
6826 switch (type) {
6827 case TOKEN_BACKSPACE:
6828 curenv->add_node(new hmotion_node(-curenv->get_space_width(),
6829 curenv->get_fill_color()));
6830 break;
6831 case TOKEN_CHAR:
6832 curenv->add_char(charset_table[c]);
6833 break;
6834 case TOKEN_DUMMY:
6835 curenv->add_node(new dummy_node);
6836 break;
6837 case TOKEN_EMPTY:
6838 assert(0);
6839 break;
6840 case TOKEN_EOF:
6841 assert(0);
6842 break;
6843 case TOKEN_ESCAPE:
6844 if (escape_char != 0)
6845 curenv->add_char(charset_table[escape_char]);
6846 break;
6847 case TOKEN_BEGIN_TRAP:
6848 case TOKEN_END_TRAP:
6849 case TOKEN_PAGE_EJECTOR:
6850 // these are all handled in process_input_stack()
6851 break;
6852 case TOKEN_HYPHEN_INDICATOR:
6853 curenv->add_hyphen_indicator();
6854 break;
6855 case TOKEN_INTERRUPT:
6856 curenv->interrupt();
6857 break;
6858 case TOKEN_ITALIC_CORRECTION:
6859 curenv->add_italic_correction();
6860 break;
6861 case TOKEN_LEADER:
6862 curenv->handle_tab(1);
6863 break;
6864 case TOKEN_LEFT_BRACE:
6865 break;
6866 case TOKEN_MARK_INPUT:
6867 set_number_reg(nm, curenv->get_input_line_position().to_units());
6868 break;
6869 case TOKEN_NEWLINE:
6870 curenv->newline();
6871 break;
6872 case TOKEN_NODE:
6873 curenv->add_node(nd);
6874 nd = 0;
6875 break;
6876 case TOKEN_NUMBERED_CHAR:
6877 curenv->add_char(get_charinfo_by_number(val));
6878 break;
6879 case TOKEN_REQUEST:
6880 // handled in process_input_stack()
6881 break;
6882 case TOKEN_RIGHT_BRACE:
6883 break;
6884 case TOKEN_SPACE:
6885 curenv->space();
6886 break;
6887 case TOKEN_SPECIAL:
6888 curenv->add_char(get_charinfo(nm));
6889 break;
6890 case TOKEN_SPREAD:
6891 curenv->spread();
6892 break;
6893 case TOKEN_STRETCHABLE_SPACE:
6894 curenv->add_node(new unbreakable_space_node(curenv->get_space_width(),
6895 curenv->get_fill_color()));
6896 break;
6897 case TOKEN_UNSTRETCHABLE_SPACE:
6898 curenv->add_node(new space_char_hmotion_node(curenv->get_space_width(),
6899 curenv->get_fill_color()));
6900 break;
6901 case TOKEN_TAB:
6902 curenv->handle_tab(0);
6903 break;
6904 case TOKEN_TRANSPARENT:
6905 break;
6906 case TOKEN_TRANSPARENT_DUMMY:
6907 curenv->add_node(new transparent_dummy_node);
6908 break;
6909 case TOKEN_ZERO_WIDTH_BREAK:
6911 node *tmp = new space_node(H0, curenv->get_fill_color());
6912 tmp->freeze_space();
6913 tmp->is_escape_colon();
6914 curenv->add_node(tmp);
6915 break;
6917 default:
6918 assert(0);
6922 class nargs_reg : public reg {
6923 public:
6924 const char *get_string();
6927 const char *nargs_reg::get_string()
6929 return i_to_a(input_stack::nargs());
6932 class lineno_reg : public reg {
6933 public:
6934 const char *get_string();
6937 const char *lineno_reg::get_string()
6939 int line;
6940 const char *file;
6941 if (!input_stack::get_location(0, &file, &line))
6942 line = 0;
6943 return i_to_a(line);
6946 class writable_lineno_reg : public general_reg {
6947 public:
6948 writable_lineno_reg();
6949 void set_value(units);
6950 int get_value(units *);
6953 writable_lineno_reg::writable_lineno_reg()
6957 int writable_lineno_reg::get_value(units *res)
6959 int line;
6960 const char *file;
6961 if (!input_stack::get_location(0, &file, &line))
6962 return 0;
6963 *res = line;
6964 return 1;
6967 void writable_lineno_reg::set_value(units n)
6969 input_stack::set_location(0, n);
6972 class filename_reg : public reg {
6973 public:
6974 const char *get_string();
6977 const char *filename_reg::get_string()
6979 int line;
6980 const char *file;
6981 if (input_stack::get_location(0, &file, &line))
6982 return file;
6983 else
6984 return 0;
6987 class break_flag_reg : public reg {
6988 public:
6989 const char *get_string();
6992 const char *break_flag_reg::get_string()
6994 return i_to_a(input_stack::get_break_flag());
6997 class constant_reg : public reg {
6998 const char *s;
6999 public:
7000 constant_reg(const char *);
7001 const char *get_string();
7004 constant_reg::constant_reg(const char *p) : s(p)
7008 const char *constant_reg::get_string()
7010 return s;
7013 constant_int_reg::constant_int_reg(int *q) : p(q)
7017 const char *constant_int_reg::get_string()
7019 return i_to_a(*p);
7022 void abort_request()
7024 int c;
7025 if (tok.eof())
7026 c = EOF;
7027 else if (tok.newline())
7028 c = '\n';
7029 else {
7030 while ((c = get_copy(0)) == ' ')
7033 if (c == EOF || c == '\n')
7034 fputs("User Abort.", stderr);
7035 else {
7036 for (; c != '\n' && c != EOF; c = get_copy(0))
7037 fputs(asciify(c), stderr);
7039 fputc('\n', stderr);
7040 cleanup_and_exit(1);
7043 char *read_string()
7045 int len = 256;
7046 char *s = new char[len];
7047 int c;
7048 while ((c = get_copy(0)) == ' ')
7050 int i = 0;
7051 while (c != '\n' && c != EOF) {
7052 if (!invalid_input_char(c)) {
7053 if (i + 2 > len) {
7054 char *tem = s;
7055 s = new char[len*2];
7056 memcpy(s, tem, len);
7057 len *= 2;
7058 a_delete tem;
7060 s[i++] = c;
7062 c = get_copy(0);
7064 s[i] = '\0';
7065 tok.next();
7066 if (i == 0) {
7067 a_delete s;
7068 return 0;
7070 return s;
7073 void pipe_output()
7075 if (!unsafe_flag) {
7076 error(".pi request not allowed in safer mode");
7077 skip_line();
7079 else {
7080 #ifdef POPEN_MISSING
7081 error("pipes not available on this system");
7082 skip_line();
7083 #else /* not POPEN_MISSING */
7084 if (the_output) {
7085 error("can't pipe: output already started");
7086 skip_line();
7088 else {
7089 char *pc;
7090 if ((pc = read_string()) == 0)
7091 error("can't pipe to empty command");
7092 if (pipe_command) {
7093 char *s = new char[strlen(pipe_command) + strlen(pc) + 1 + 1];
7094 strcpy(s, pipe_command);
7095 strcat(s, "|");
7096 strcat(s, pc);
7097 a_delete pipe_command;
7098 a_delete pc;
7099 pipe_command = s;
7101 else
7102 pipe_command = pc;
7104 #endif /* not POPEN_MISSING */
7108 static int system_status;
7110 void system_request()
7112 if (!unsafe_flag) {
7113 error(".sy request not allowed in safer mode");
7114 skip_line();
7116 else {
7117 char *command = read_string();
7118 if (!command)
7119 error("empty command");
7120 else {
7121 system_status = system(command);
7122 a_delete command;
7127 void copy_file()
7129 if (curdiv == topdiv && topdiv->before_first_page) {
7130 handle_initial_request(COPY_FILE_REQUEST);
7131 return;
7133 symbol filename = get_long_name(1);
7134 while (!tok.newline() && !tok.eof())
7135 tok.next();
7136 if (break_flag)
7137 curenv->do_break();
7138 if (!filename.is_null())
7139 curdiv->copy_file(filename.contents());
7140 tok.next();
7143 #ifdef COLUMN
7145 void vjustify()
7147 if (curdiv == topdiv && topdiv->before_first_page) {
7148 handle_initial_request(VJUSTIFY_REQUEST);
7149 return;
7151 symbol type = get_long_name(1);
7152 if (!type.is_null())
7153 curdiv->vjustify(type);
7154 skip_line();
7157 #endif /* COLUMN */
7159 void transparent_file()
7161 if (curdiv == topdiv && topdiv->before_first_page) {
7162 handle_initial_request(TRANSPARENT_FILE_REQUEST);
7163 return;
7165 symbol filename = get_long_name(1);
7166 while (!tok.newline() && !tok.eof())
7167 tok.next();
7168 if (break_flag)
7169 curenv->do_break();
7170 if (!filename.is_null()) {
7171 file_case *fcp = include_search_path
7172 .open_file_cautious(filename.contents());
7173 if (fcp == NULL)
7174 error("can't open `%1': %2", filename.contents(), strerror(errno));
7175 else {
7176 int bol = 1;
7177 for (;;) {
7178 int c = fcp->get_c();
7179 if (c == EOF)
7180 break;
7181 if (invalid_input_char(c))
7182 warning(WARN_INPUT, "invalid input character code %1", int(c));
7183 else {
7184 curdiv->transparent_output(c);
7185 bol = c == '\n';
7188 if (!bol)
7189 curdiv->transparent_output('\n');
7190 delete fcp;
7193 tok.next();
7196 class page_range {
7197 int first;
7198 int last;
7199 public:
7200 page_range *next;
7201 page_range(int, int, page_range *);
7202 int contains(int n);
7205 page_range::page_range(int i, int j, page_range *p)
7206 : first(i), last(j), next(p)
7210 int page_range::contains(int n)
7212 return n >= first && (last <= 0 || n <= last);
7215 page_range *output_page_list = 0;
7217 int in_output_page_list(int n)
7219 if (!output_page_list)
7220 return 1;
7221 for (page_range *p = output_page_list; p; p = p->next)
7222 if (p->contains(n))
7223 return 1;
7224 return 0;
7227 static void parse_output_page_list(char *p)
7229 for (;;) {
7230 int i;
7231 if (*p == '-')
7232 i = 1;
7233 else if (csdigit(*p)) {
7234 i = 0;
7236 i = i*10 + *p++ - '0';
7237 while (csdigit(*p));
7239 else
7240 break;
7241 int j;
7242 if (*p == '-') {
7243 p++;
7244 j = 0;
7245 if (csdigit(*p)) {
7247 j = j*10 + *p++ - '0';
7248 while (csdigit(*p));
7251 else
7252 j = i;
7253 if (j == 0)
7254 last_page_number = -1;
7255 else if (last_page_number >= 0 && j > last_page_number)
7256 last_page_number = j;
7257 output_page_list = new page_range(i, j, output_page_list);
7258 if (*p != ',')
7259 break;
7260 ++p;
7262 if (*p != '\0') {
7263 error("bad output page list");
7264 output_page_list = 0;
7268 static file_case *open_mac_file(const char *mac)
7270 // Try first FOOBAR.tmac, then tmac.FOOBAR
7271 char *s = new char[strlen(mac) + strlen(MACRO_POSTFIX) +1];
7272 strcpy(s, mac);
7273 strcat(s, MACRO_POSTFIX);
7275 file_case *fcp;
7276 if ((fcp = mac_path->open_file(s, fcp->fc_take_path)) == NULL) {
7277 s = new char[strlen(mac) + strlen(MACRO_PREFIX) +1];
7278 strcpy(s, MACRO_PREFIX);
7279 strcat(s, mac);
7280 fcp = mac_path->open_file(s, fcp->fc_take_path);
7282 return fcp;
7285 static void process_macro_file(const char *mac)
7287 file_case *fcp = open_mac_file(mac);
7288 if (fcp == NULL)
7289 fatal("can't find macro file %1", mac);
7290 const char *s = symbol(fcp->path()).contents();
7291 input_stack::push(new file_iterator(fcp, s));
7292 tok.next();
7293 process_input_stack();
7296 static void process_startup_file(const char *filename)
7298 search_path *orig_mac_path = mac_path;
7299 mac_path = &config_macro_path;
7300 file_case *fcp;
7301 if ((fcp = mac_path->open_file(filename)) != NULL) {
7302 input_stack::push(new file_iterator(fcp, symbol(fcp->path()).contents()));
7303 tok.next();
7304 process_input_stack();
7306 mac_path = orig_mac_path;
7309 void macro_source()
7311 symbol nm = get_long_name(1);
7312 if (nm.is_null())
7313 skip_line();
7314 else {
7315 while (!tok.newline() && !tok.eof())
7316 tok.next();
7317 // .mso doesn't (and cannot) go through open_mac_file, so we
7318 // need to do it here manually: If we have tmac.FOOBAR, try
7319 // FOOBAR.tmac and vice versa
7320 file_case *fcp;
7321 if ((fcp = mac_path->open_file(nm.contents())) == NULL) {
7322 const char *fn = nm.contents();
7324 if (strncasecmp(fn, MACRO_PREFIX, sizeof(MACRO_PREFIX) - 1) == 0) {
7325 char *s = new char[strlen(fn) + sizeof(MACRO_POSTFIX)];
7326 strcpy(s, fn + sizeof(MACRO_PREFIX) - 1);
7327 strcat(s, MACRO_POSTFIX);
7328 fcp = mac_path->open_file(s, fcp->fc_take_path);
7331 if (fcp == NULL) {
7332 if (strncasecmp(fn + strlen(fn) - sizeof(MACRO_POSTFIX) + 1,
7333 MACRO_POSTFIX, sizeof(MACRO_POSTFIX) - 1) == 0) {
7334 char *s = new char[strlen(fn) + sizeof(MACRO_PREFIX)];
7335 strcpy(s, MACRO_PREFIX);
7336 strncat(s, fn, strlen(fn) - sizeof(MACRO_POSTFIX) + 1);
7337 fcp = mac_path->open_file(s, fcp->fc_take_path);
7342 if (fcp != NULL)
7343 input_stack::push(new file_iterator(fcp, symbol(fcp->path()).contents()));
7344 else
7345 warning(WARN_FILE, "can't find macro file `%1'", nm.contents());
7346 tok.next();
7350 static void process_input_file(const char *name)
7352 file_case *fcp;
7353 if ((fcp = include_search_path.open_file_cautious(name)) == NULL) {
7354 assert(strcmp(name, "-"));
7355 fatal("can't open `%1': %2", name, strerror(errno));
7357 input_stack::push(new file_iterator(fcp, name));
7358 tok.next();
7359 process_input_stack();
7362 // make sure the_input is empty before calling this
7364 static int evaluate_expression(const char *expr, units *res)
7366 input_stack::push(make_temp_iterator(expr));
7367 tok.next();
7368 int success = get_number(res, 'u');
7369 while (input_stack::get(0) != EOF)
7371 return success;
7374 static void do_register_assignment(const char *s)
7376 const char *p = strchr(s, '=');
7377 if (!p) {
7378 char buf[2];
7379 buf[0] = s[0];
7380 buf[1] = 0;
7381 units n;
7382 if (evaluate_expression(s + 1, &n))
7383 set_number_reg(buf, n);
7385 else {
7386 char *buf = new char[p - s + 1];
7387 memcpy(buf, s, p - s);
7388 buf[p - s] = 0;
7389 units n;
7390 if (evaluate_expression(p + 1, &n))
7391 set_number_reg(buf, n);
7392 a_delete buf;
7396 static void set_string(const char *name, const char *value)
7398 macro *m = new macro;
7399 for (const char *p = value; *p; p++)
7400 if (!invalid_input_char((unsigned char)*p))
7401 m->append(*p);
7402 request_dictionary.define(name, m);
7405 static void do_string_assignment(const char *s)
7407 const char *p = strchr(s, '=');
7408 if (!p) {
7409 char buf[2];
7410 buf[0] = s[0];
7411 buf[1] = 0;
7412 set_string(buf, s + 1);
7414 else {
7415 char *buf = new char[p - s + 1];
7416 memcpy(buf, s, p - s);
7417 buf[p - s] = 0;
7418 set_string(buf, p + 1);
7419 a_delete buf;
7423 struct string_list {
7424 const char *s;
7425 string_list *next;
7426 string_list(const char *ss) : s(ss), next(0) {}
7429 #if 0
7430 static void prepend_string(const char *s, string_list **p)
7432 string_list *l = new string_list(s);
7433 l->next = *p;
7434 *p = l;
7436 #endif
7438 static void add_string(const char *s, string_list **p)
7440 while (*p)
7441 p = &((*p)->next);
7442 *p = new string_list(s);
7445 void usage(FILE *stream, const char *prog)
7447 fprintf(stream,
7448 "usage: %s -abcivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n"
7449 " -rcn -Tname -Fdir -Idir -Mdir [files...]\n",
7450 prog);
7453 int main(int argc, char **argv)
7455 program_name = argv[0];
7456 static char stderr_buf[BUFSIZ];
7457 setbuf(stderr, stderr_buf);
7458 int c;
7459 string_list *macros = 0;
7460 string_list *register_assignments = 0;
7461 string_list *string_assignments = 0;
7462 int iflag = 0;
7463 int tflag = 0;
7464 int fflag = 0;
7465 int nflag = 0;
7466 int no_rc = 0; // don't process troffrc and troffrc-end
7467 int next_page_number = 0; // pacify compiler
7468 opterr = 0;
7469 hresolution = vresolution = 1;
7470 // restore $PATH if called from groff
7471 char* groff_path = getenv("GROFF_PATH__");
7472 if (groff_path) {
7473 string e = "PATH";
7474 e += '=';
7475 if (*groff_path)
7476 e += groff_path;
7477 e += '\0';
7478 if (putenv(strsave(e.contents())))
7479 fatal("putenv failed");
7481 static const struct option long_options[] = {
7482 { "help", no_argument, 0, CHAR_MAX + 1 },
7483 { "version", no_argument, 0, 'v' },
7484 { 0, 0, 0, 0 }
7486 #if defined(DEBUGGING)
7487 #define DEBUG_OPTION "D"
7488 #endif
7489 while ((c = getopt_long(argc, argv,
7490 "abciI:vw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU"
7491 DEBUG_OPTION, long_options, 0))
7492 != EOF)
7493 switch(c) {
7494 case 'v':
7496 printf("GNU troff (groff) version %s\n", Version_string);
7497 exit(0);
7498 break;
7500 case 'I':
7501 // Search path for .psbb files
7502 // and most other non-system input files.
7503 include_search_path.command_line_dir(optarg);
7504 break;
7505 case 'T':
7506 device = optarg;
7507 tflag = 1;
7508 is_html = (strcmp(device, "html") == 0);
7509 break;
7510 case 'C':
7511 compatible_flag = 1;
7512 // fall through
7513 case 'c':
7514 color_flag = 0;
7515 break;
7516 case 'M':
7517 macro_path.command_line_dir(optarg);
7518 safer_macro_path.command_line_dir(optarg);
7519 config_macro_path.command_line_dir(optarg);
7520 break;
7521 case 'F':
7522 font::command_line_font_dir(optarg);
7523 break;
7524 case 'm':
7525 add_string(optarg, &macros);
7526 break;
7527 case 'E':
7528 inhibit_errors = 1;
7529 break;
7530 case 'R':
7531 no_rc = 1;
7532 break;
7533 case 'w':
7534 enable_warning(optarg);
7535 break;
7536 case 'W':
7537 disable_warning(optarg);
7538 break;
7539 case 'i':
7540 iflag = 1;
7541 break;
7542 case 'b':
7543 backtrace_flag = 1;
7544 break;
7545 case 'a':
7546 ascii_output_flag = 1;
7547 break;
7548 case 'z':
7549 suppress_output_flag = 1;
7550 break;
7551 case 'n':
7552 if (sscanf(optarg, "%d", &next_page_number) == 1)
7553 nflag++;
7554 else
7555 error("bad page number");
7556 break;
7557 case 'o':
7558 parse_output_page_list(optarg);
7559 break;
7560 case 'd':
7561 if (*optarg == '\0')
7562 error("`-d' requires non-empty argument");
7563 else
7564 add_string(optarg, &string_assignments);
7565 break;
7566 case 'r':
7567 if (*optarg == '\0')
7568 error("`-r' requires non-empty argument");
7569 else
7570 add_string(optarg, &register_assignments);
7571 break;
7572 case 'f':
7573 default_family = symbol(optarg);
7574 fflag = 1;
7575 break;
7576 case 'q':
7577 case 's':
7578 case 't':
7579 // silently ignore these
7580 break;
7581 case 'U':
7582 unsafe_flag = 1; // unsafe behaviour
7583 break;
7584 #if defined(DEBUGGING)
7585 case 'D':
7586 debug_state = 1;
7587 break;
7588 #endif
7589 case CHAR_MAX + 1: // --help
7590 usage(stdout, argv[0]);
7591 exit(0);
7592 break;
7593 case '?':
7594 usage(stderr, argv[0]);
7595 exit(1);
7596 break; // never reached
7597 default:
7598 assert(0);
7600 if (unsafe_flag)
7601 mac_path = &macro_path;
7602 set_string(".T", device);
7603 init_charset_table();
7604 init_hpf_code_table();
7605 if (!font::load_desc())
7606 fatal("sorry, I can't continue");
7607 units_per_inch = font::res;
7608 hresolution = font::hor;
7609 vresolution = font::vert;
7610 sizescale = font::sizescale;
7611 tcommand_flag = font::tcommand;
7612 warn_scale = (double)units_per_inch;
7613 warn_scaling_indicator = 'i';
7614 if (!fflag && font::family != 0 && *font::family != '\0')
7615 default_family = symbol(font::family);
7616 font_size::init_size_table(font::sizes);
7617 int i;
7618 int j = 1;
7619 if (font::style_table) {
7620 for (i = 0; font::style_table[i]; i++)
7621 mount_style(j++, symbol(font::style_table[i]));
7623 for (i = 0; font::font_name_table[i]; i++, j++)
7624 // In the DESC file a font name of 0 (zero) means leave this
7625 // position empty.
7626 if (strcmp(font::font_name_table[i], "0") != 0)
7627 mount_font(j, symbol(font::font_name_table[i]));
7628 curdiv = topdiv = new top_level_diversion;
7629 if (nflag)
7630 topdiv->set_next_page_number(next_page_number);
7631 init_input_requests();
7632 init_env_requests();
7633 init_div_requests();
7634 #ifdef COLUMN
7635 init_column_requests();
7636 #endif /* COLUMN */
7637 init_node_requests();
7638 number_reg_dictionary.define(".T", new constant_reg(tflag ? "1" : "0"));
7639 init_registers();
7640 init_reg_requests();
7641 init_hyphen_requests();
7642 init_environments();
7643 while (string_assignments) {
7644 do_string_assignment(string_assignments->s);
7645 string_list *tem = string_assignments;
7646 string_assignments = string_assignments->next;
7647 delete tem;
7649 while (register_assignments) {
7650 do_register_assignment(register_assignments->s);
7651 string_list *tem = register_assignments;
7652 register_assignments = register_assignments->next;
7653 delete tem;
7655 if (!no_rc)
7656 process_startup_file(INITIAL_STARTUP_FILE);
7657 while (macros) {
7658 process_macro_file(macros->s);
7659 string_list *tem = macros;
7660 macros = macros->next;
7661 delete tem;
7663 if (!no_rc)
7664 process_startup_file(FINAL_STARTUP_FILE);
7665 for (i = optind; i < argc; i++)
7666 process_input_file(argv[i]);
7667 if (optind >= argc || iflag)
7668 process_input_file("-");
7669 exit_troff();
7670 return 0; // not reached
7673 void warn_request()
7675 int n;
7676 if (has_arg() && get_integer(&n)) {
7677 if (n & ~WARN_TOTAL) {
7678 warning(WARN_RANGE, "warning mask must be between 0 and %1", WARN_TOTAL);
7679 n &= WARN_TOTAL;
7681 warning_mask = n;
7683 else
7684 warning_mask = WARN_TOTAL;
7685 skip_line();
7688 static void init_registers()
7690 #ifdef LONG_FOR_TIME_T
7691 long
7692 #else /* not LONG_FOR_TIME_T */
7693 time_t
7694 #endif /* not LONG_FOR_TIME_T */
7695 t = time(0);
7696 // Use struct here to work around misfeature in old versions of g++.
7697 struct tm *tt = localtime(&t);
7698 set_number_reg("seconds", int(tt->tm_sec));
7699 set_number_reg("minutes", int(tt->tm_min));
7700 set_number_reg("hours", int(tt->tm_hour));
7701 set_number_reg("dw", int(tt->tm_wday + 1));
7702 set_number_reg("dy", int(tt->tm_mday));
7703 set_number_reg("mo", int(tt->tm_mon + 1));
7704 set_number_reg("year", int(1900 + tt->tm_year));
7705 set_number_reg("yr", int(tt->tm_year));
7706 set_number_reg("$$", getpid());
7707 number_reg_dictionary.define(".A",
7708 new constant_reg(ascii_output_flag
7709 ? "1"
7710 : "0"));
7714 * registers associated with \O
7717 static int output_reg_minx_contents = -1;
7718 static int output_reg_miny_contents = -1;
7719 static int output_reg_maxx_contents = -1;
7720 static int output_reg_maxy_contents = -1;
7722 void check_output_limits(int x, int y)
7724 if ((output_reg_minx_contents == -1) || (x < output_reg_minx_contents))
7725 output_reg_minx_contents = x;
7726 if (x > output_reg_maxx_contents)
7727 output_reg_maxx_contents = x;
7728 if ((output_reg_miny_contents == -1) || (y < output_reg_miny_contents))
7729 output_reg_miny_contents = y;
7730 if (y > output_reg_maxy_contents)
7731 output_reg_maxy_contents = y;
7734 void reset_output_registers()
7736 output_reg_minx_contents = -1;
7737 output_reg_miny_contents = -1;
7738 output_reg_maxx_contents = -1;
7739 output_reg_maxy_contents = -1;
7742 void get_output_registers(int *minx, int *miny, int *maxx, int *maxy)
7744 *minx = output_reg_minx_contents;
7745 *miny = output_reg_miny_contents;
7746 *maxx = output_reg_maxx_contents;
7747 *maxy = output_reg_maxy_contents;
7750 void init_input_requests()
7752 init_request("ab", abort_request);
7753 init_request("als", alias_macro);
7754 init_request("am", append_macro);
7755 init_request("am1", append_nocomp_macro);
7756 init_request("ami", append_indirect_macro);
7757 init_request("ami1", append_indirect_nocomp_macro);
7758 init_request("as", append_string);
7759 init_request("as1", append_nocomp_string);
7760 init_request("asciify", asciify_macro);
7761 init_request("backtrace", backtrace_request);
7762 init_request("blm", blank_line_macro);
7763 init_request("break", while_break_request);
7764 init_request("cf", copy_file);
7765 init_request("cflags", char_flags);
7766 init_request("char", define_character);
7767 init_request("chop", chop_macro);
7768 init_request("close", close_request);
7769 init_request("color", activate_color);
7770 init_request("composite", composite_request);
7771 init_request("continue", while_continue_request);
7772 init_request("cp", compatible);
7773 init_request("de", define_macro);
7774 init_request("de1", define_nocomp_macro);
7775 init_request("defcolor", define_color);
7776 init_request("dei", define_indirect_macro);
7777 init_request("dei1", define_indirect_nocomp_macro);
7778 init_request("device", device_request);
7779 init_request("devicem", device_macro_request);
7780 init_request("do", do_request);
7781 init_request("ds", define_string);
7782 init_request("ds1", define_nocomp_string);
7783 init_request("ec", set_escape_char);
7784 init_request("ecr", restore_escape_char);
7785 init_request("ecs", save_escape_char);
7786 init_request("el", else_request);
7787 init_request("em", end_macro);
7788 init_request("eo", escape_off);
7789 init_request("ex", exit_request);
7790 init_request("fchar", define_fallback_character);
7791 #ifdef WIDOW_CONTROL
7792 init_request("fpl", flush_pending_lines);
7793 #endif /* WIDOW_CONTROL */
7794 init_request("hcode", hyphenation_code);
7795 init_request("hpfcode", hyphenation_patterns_file_code);
7796 init_request("ie", if_else_request);
7797 init_request("if", if_request);
7798 init_request("ig", ignore);
7799 init_request("length", length_request);
7800 init_request("lf", line_file);
7801 init_request("mso", macro_source);
7802 init_request("nop", nop_request);
7803 init_request("nroff", nroff_request);
7804 init_request("nx", next_file);
7805 init_request("open", open_request);
7806 init_request("opena", opena_request);
7807 init_request("output", output_request);
7808 init_request("pc", set_page_character);
7809 init_request("pi", pipe_output);
7810 init_request("pm", print_macros);
7811 init_request("psbb", ps_bbox_request);
7812 #ifndef POPEN_MISSING
7813 init_request("pso", pipe_source);
7814 #endif /* not POPEN_MISSING */
7815 init_request("rchar", remove_character);
7816 init_request("rd", read_request);
7817 init_request("return", return_macro_request);
7818 init_request("rm", remove_macro);
7819 init_request("rn", rename_macro);
7820 init_request("schar", define_special_character);
7821 init_request("shift", shift);
7822 init_request("so", source);
7823 init_request("spreadwarn", spreadwarn_request);
7824 init_request("substring", substring_request);
7825 init_request("sy", system_request);
7826 init_request("tag", tag);
7827 init_request("taga", taga);
7828 init_request("tm", terminal);
7829 init_request("tm1", terminal1);
7830 init_request("tmc", terminal_continue);
7831 init_request("tr", translate);
7832 init_request("trf", transparent_file);
7833 init_request("trin", translate_input);
7834 init_request("trnt", translate_no_transparent);
7835 init_request("troff", troff_request);
7836 init_request("unformat", unformat_macro);
7837 #ifdef COLUMN
7838 init_request("vj", vjustify);
7839 #endif /* COLUMN */
7840 init_request("warn", warn_request);
7841 init_request("warnscale", warnscale_request);
7842 init_request("while", while_request);
7843 init_request("write", write_request);
7844 init_request("writec", write_request_continue);
7845 init_request("writem", write_macro_request);
7846 number_reg_dictionary.define(".$", new nargs_reg);
7847 number_reg_dictionary.define(".br", new break_flag_reg);
7848 number_reg_dictionary.define(".C", new constant_int_reg(&compatible_flag));
7849 number_reg_dictionary.define(".O", new variable_reg(&begin_level));
7850 number_reg_dictionary.define(".c", new lineno_reg);
7851 number_reg_dictionary.define(".color", new constant_int_reg(&color_flag));
7852 number_reg_dictionary.define(".F", new filename_reg);
7853 number_reg_dictionary.define(".g", new constant_reg("1"));
7854 number_reg_dictionary.define(".H", new constant_int_reg(&hresolution));
7855 number_reg_dictionary.define(".R", new constant_reg("10000"));
7856 number_reg_dictionary.define(".U", new constant_int_reg(&unsafe_flag));
7857 number_reg_dictionary.define(".V", new constant_int_reg(&vresolution));
7858 number_reg_dictionary.define(".warn", new constant_int_reg(&warning_mask));
7859 extern const char *major_version;
7860 number_reg_dictionary.define(".x", new constant_reg(major_version));
7861 extern const char *revision;
7862 number_reg_dictionary.define(".Y", new constant_reg(revision));
7863 extern const char *minor_version;
7864 number_reg_dictionary.define(".y", new constant_reg(minor_version));
7865 number_reg_dictionary.define("c.", new writable_lineno_reg);
7866 number_reg_dictionary.define("llx", new variable_reg(&llx_reg_contents));
7867 number_reg_dictionary.define("lly", new variable_reg(&lly_reg_contents));
7868 number_reg_dictionary.define("opmaxx",
7869 new variable_reg(&output_reg_maxx_contents));
7870 number_reg_dictionary.define("opmaxy",
7871 new variable_reg(&output_reg_maxy_contents));
7872 number_reg_dictionary.define("opminx",
7873 new variable_reg(&output_reg_minx_contents));
7874 number_reg_dictionary.define("opminy",
7875 new variable_reg(&output_reg_miny_contents));
7876 number_reg_dictionary.define("slimit",
7877 new variable_reg(&input_stack::limit));
7878 number_reg_dictionary.define("systat", new variable_reg(&system_status));
7879 number_reg_dictionary.define("urx", new variable_reg(&urx_reg_contents));
7880 number_reg_dictionary.define("ury", new variable_reg(&ury_reg_contents));
7883 object_dictionary request_dictionary(501);
7885 void init_request(const char *s, REQUEST_FUNCP f)
7887 request_dictionary.define(s, new request(f));
7890 static request_or_macro *lookup_request(symbol nm)
7892 assert(!nm.is_null());
7893 request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm);
7894 if (p == 0) {
7895 warning(WARN_MAC, "macro `%1' not defined", nm.contents());
7896 p = new macro;
7897 request_dictionary.define(nm, p);
7899 return p;
7902 node *charinfo_to_node_list(charinfo *ci, const environment *envp)
7904 // Don't interpret character definitions in compatible mode.
7905 int old_compatible_flag = compatible_flag;
7906 compatible_flag = 0;
7907 int old_escape_char = escape_char;
7908 escape_char = '\\';
7909 macro *mac = ci->set_macro(0);
7910 assert(mac != 0);
7911 environment *oldenv = curenv;
7912 environment env(envp);
7913 curenv = &env;
7914 curenv->set_composite();
7915 token old_tok = tok;
7916 input_stack::add_boundary();
7917 string_iterator *si =
7918 new string_iterator(*mac, "composite character", ci->nm);
7919 input_stack::push(si);
7920 // we don't use process_input_stack, because we don't want to recognise
7921 // requests
7922 for (;;) {
7923 tok.next();
7924 if (tok.eof())
7925 break;
7926 if (tok.newline()) {
7927 error("composite character mustn't contain newline");
7928 while (!tok.eof())
7929 tok.next();
7930 break;
7932 else
7933 tok.process();
7935 node *n = curenv->extract_output_line();
7936 input_stack::remove_boundary();
7937 ci->set_macro(mac);
7938 tok = old_tok;
7939 curenv = oldenv;
7940 compatible_flag = old_compatible_flag;
7941 escape_char = old_escape_char;
7942 have_input = 0;
7943 return n;
7946 static node *read_draw_node()
7948 token start;
7949 start.next();
7950 if (!start.delimiter(1)){
7951 do {
7952 tok.next();
7953 } while (tok != start && !tok.newline() && !tok.eof());
7955 else {
7956 tok.next();
7957 if (tok == start)
7958 error("missing argument");
7959 else {
7960 unsigned char type = tok.ch();
7961 if (type == 'F') {
7962 read_color_draw_node(start);
7963 return 0;
7965 tok.next();
7966 int maxpoints = 10;
7967 hvpair *point = new hvpair[maxpoints];
7968 int npoints = 0;
7969 int no_last_v = 0;
7970 int err = 0;
7971 int i;
7972 for (i = 0; tok != start; i++) {
7973 if (i == maxpoints) {
7974 hvpair *oldpoint = point;
7975 point = new hvpair[maxpoints*2];
7976 for (int j = 0; j < maxpoints; j++)
7977 point[j] = oldpoint[j];
7978 maxpoints *= 2;
7979 a_delete oldpoint;
7981 if (!get_hunits(&point[i].h,
7982 type == 'f' || type == 't' ? 'u' : 'm')) {
7983 err = 1;
7984 break;
7986 ++npoints;
7987 tok.skip();
7988 point[i].v = V0;
7989 if (tok == start) {
7990 no_last_v = 1;
7991 break;
7993 if (!get_vunits(&point[i].v, 'v')) {
7994 err = 1;
7995 break;
7997 tok.skip();
7999 while (tok != start && !tok.newline() && !tok.eof())
8000 tok.next();
8001 if (!err) {
8002 switch (type) {
8003 case 'l':
8004 if (npoints != 1 || no_last_v) {
8005 error("two arguments needed for line");
8006 npoints = 1;
8008 break;
8009 case 'c':
8010 if (npoints != 1 || !no_last_v) {
8011 error("one argument needed for circle");
8012 npoints = 1;
8013 point[0].v = V0;
8015 break;
8016 case 'e':
8017 if (npoints != 1 || no_last_v) {
8018 error("two arguments needed for ellipse");
8019 npoints = 1;
8021 break;
8022 case 'a':
8023 if (npoints != 2 || no_last_v) {
8024 error("four arguments needed for arc");
8025 npoints = 2;
8027 break;
8028 case '~':
8029 if (no_last_v)
8030 error("even number of arguments needed for spline");
8031 break;
8032 case 'f':
8033 if (npoints != 1 || !no_last_v) {
8034 error("one argument needed for gray shade");
8035 npoints = 1;
8036 point[0].v = V0;
8038 default:
8039 // silently pass it through
8040 break;
8042 draw_node *dn = new draw_node(type, point, npoints,
8043 curenv->get_font_size(),
8044 curenv->get_glyph_color(),
8045 curenv->get_fill_color());
8046 a_delete point;
8047 return dn;
8049 else {
8050 a_delete point;
8054 return 0;
8057 static void read_color_draw_node(token &start)
8059 tok.next();
8060 if (tok == start) {
8061 error("missing color scheme");
8062 return;
8064 unsigned char scheme = tok.ch();
8065 tok.next();
8066 color *col = 0;
8067 char end = start.ch();
8068 switch (scheme) {
8069 case 'c':
8070 col = read_cmy(end);
8071 break;
8072 case 'd':
8073 col = &default_color;
8074 break;
8075 case 'g':
8076 col = read_gray(end);
8077 break;
8078 case 'k':
8079 col = read_cmyk(end);
8080 break;
8081 case 'r':
8082 col = read_rgb(end);
8083 break;
8085 if (col)
8086 curenv->set_fill_color(col);
8087 while (tok != start) {
8088 if (tok.newline() || tok.eof()) {
8089 warning(WARN_DELIM, "missing closing delimiter");
8090 input_stack::push(make_temp_iterator("\n"));
8091 break;
8093 tok.next();
8095 have_input = 1;
8098 static struct {
8099 const char *name;
8100 int mask;
8101 } warning_table[] = {
8102 { "char", WARN_CHAR },
8103 { "range", WARN_RANGE },
8104 { "break", WARN_BREAK },
8105 { "delim", WARN_DELIM },
8106 { "el", WARN_EL },
8107 { "scale", WARN_SCALE },
8108 { "number", WARN_NUMBER },
8109 { "syntax", WARN_SYNTAX },
8110 { "tab", WARN_TAB },
8111 { "right-brace", WARN_RIGHT_BRACE },
8112 { "missing", WARN_MISSING },
8113 { "input", WARN_INPUT },
8114 { "escape", WARN_ESCAPE },
8115 { "space", WARN_SPACE },
8116 { "font", WARN_FONT },
8117 { "di", WARN_DI },
8118 { "mac", WARN_MAC },
8119 { "reg", WARN_REG },
8120 { "ig", WARN_IG },
8121 { "color", WARN_COLOR },
8122 { "all", WARN_TOTAL & ~(WARN_DI | WARN_MAC | WARN_REG) },
8123 { "w", WARN_TOTAL },
8124 { "default", DEFAULT_WARNING_MASK },
8127 static int lookup_warning(const char *name)
8129 for (unsigned int i = 0;
8130 i < sizeof(warning_table)/sizeof(warning_table[0]);
8131 i++)
8132 if (strcmp(name, warning_table[i].name) == 0)
8133 return warning_table[i].mask;
8134 return 0;
8137 static void enable_warning(const char *name)
8139 int mask = lookup_warning(name);
8140 if (mask)
8141 warning_mask |= mask;
8142 else
8143 error("unknown warning `%1'", name);
8146 static void disable_warning(const char *name)
8148 int mask = lookup_warning(name);
8149 if (mask)
8150 warning_mask &= ~mask;
8151 else
8152 error("unknown warning `%1'", name);
8155 static void copy_mode_error(const char *format,
8156 const errarg &arg1,
8157 const errarg &arg2,
8158 const errarg &arg3)
8160 if (ignoring) {
8161 static const char prefix[] = "(in ignored input) ";
8162 char *s = new char[sizeof(prefix) + strlen(format)];
8163 strcpy(s, prefix);
8164 strcat(s, format);
8165 warning(WARN_IG, s, arg1, arg2, arg3);
8166 a_delete s;
8168 else
8169 error(format, arg1, arg2, arg3);
8172 enum error_type { WARNING, OUTPUT_WARNING, ERROR, FATAL };
8174 static void do_error(error_type type,
8175 const char *format,
8176 const errarg &arg1,
8177 const errarg &arg2,
8178 const errarg &arg3)
8180 const char *filename;
8181 int lineno;
8182 if (inhibit_errors && type < FATAL)
8183 return;
8184 if (backtrace_flag)
8185 input_stack::backtrace();
8186 if (!get_file_line(&filename, &lineno))
8187 filename = 0;
8188 if (filename)
8189 errprint("%1:%2: ", filename, lineno);
8190 else if (program_name)
8191 fprintf(stderr, "%s: ", program_name);
8192 switch (type) {
8193 case FATAL:
8194 fputs("fatal error: ", stderr);
8195 break;
8196 case ERROR:
8197 break;
8198 case WARNING:
8199 fputs("warning: ", stderr);
8200 break;
8201 case OUTPUT_WARNING:
8202 double fromtop = topdiv->get_vertical_position().to_units() / warn_scale;
8203 fprintf(stderr, "warning [p %d, %.1f%c",
8204 topdiv->get_page_number(), fromtop, warn_scaling_indicator);
8205 if (topdiv != curdiv) {
8206 double fromtop1 = curdiv->get_vertical_position().to_units()
8207 / warn_scale;
8208 fprintf(stderr, ", div `%s', %.1f%c",
8209 curdiv->get_diversion_name(), fromtop1, warn_scaling_indicator);
8211 fprintf(stderr, "]: ");
8212 break;
8214 errprint(format, arg1, arg2, arg3);
8215 fputc('\n', stderr);
8216 fflush(stderr);
8217 if (type == FATAL)
8218 cleanup_and_exit(1);
8221 int warning(warning_type t,
8222 const char *format,
8223 const errarg &arg1,
8224 const errarg &arg2,
8225 const errarg &arg3)
8227 if ((t & warning_mask) != 0) {
8228 do_error(WARNING, format, arg1, arg2, arg3);
8229 return 1;
8231 else
8232 return 0;
8235 int output_warning(warning_type t,
8236 const char *format,
8237 const errarg &arg1,
8238 const errarg &arg2,
8239 const errarg &arg3)
8241 if ((t & warning_mask) != 0) {
8242 do_error(OUTPUT_WARNING, format, arg1, arg2, arg3);
8243 return 1;
8245 else
8246 return 0;
8249 void error(const char *format,
8250 const errarg &arg1,
8251 const errarg &arg2,
8252 const errarg &arg3)
8254 do_error(ERROR, format, arg1, arg2, arg3);
8257 void fatal(const char *format,
8258 const errarg &arg1,
8259 const errarg &arg2,
8260 const errarg &arg3)
8262 do_error(FATAL, format, arg1, arg2, arg3);
8265 void fatal_with_file_and_line(const char *filename, int lineno,
8266 const char *format,
8267 const errarg &arg1,
8268 const errarg &arg2,
8269 const errarg &arg3)
8271 fprintf(stderr, "%s:%d: fatal error: ", filename, lineno);
8272 errprint(format, arg1, arg2, arg3);
8273 fputc('\n', stderr);
8274 fflush(stderr);
8275 cleanup_and_exit(1);
8278 void error_with_file_and_line(const char *filename, int lineno,
8279 const char *format,
8280 const errarg &arg1,
8281 const errarg &arg2,
8282 const errarg &arg3)
8284 fprintf(stderr, "%s:%d: error: ", filename, lineno);
8285 errprint(format, arg1, arg2, arg3);
8286 fputc('\n', stderr);
8287 fflush(stderr);
8290 dictionary charinfo_dictionary(501);
8292 charinfo *get_charinfo(symbol nm)
8294 void *p = charinfo_dictionary.lookup(nm);
8295 if (p != 0)
8296 return (charinfo *)p;
8297 charinfo *cp = new charinfo(nm);
8298 (void)charinfo_dictionary.lookup(nm, cp);
8299 return cp;
8302 int charinfo::next_index = 0;
8304 charinfo::charinfo(symbol s)
8305 : translation(0), mac(0), special_translation(TRANSLATE_NONE),
8306 hyphenation_code(0), flags(0), ascii_code(0), asciify_code(0),
8307 not_found(0), transparent_translate(1), translate_input(0),
8308 mode(CHAR_NORMAL), nm(s)
8310 index = next_index++;
8311 number = -1;
8314 void charinfo::set_hyphenation_code(unsigned char c)
8316 hyphenation_code = c;
8319 void charinfo::set_translation(charinfo *ci, int tt, int ti)
8321 translation = ci;
8322 if (ci && ti) {
8323 if (hyphenation_code != 0)
8324 ci->set_hyphenation_code(hyphenation_code);
8325 if (asciify_code != 0)
8326 ci->set_asciify_code(asciify_code);
8327 else if (ascii_code != 0)
8328 ci->set_asciify_code(ascii_code);
8329 ci->set_translation_input();
8331 special_translation = TRANSLATE_NONE;
8332 transparent_translate = tt;
8335 void charinfo::set_special_translation(int c, int tt)
8337 special_translation = c;
8338 translation = 0;
8339 transparent_translate = tt;
8342 void charinfo::set_ascii_code(unsigned char c)
8344 ascii_code = c;
8347 void charinfo::set_asciify_code(unsigned char c)
8349 asciify_code = c;
8352 macro *charinfo::set_macro(macro *m)
8354 macro *tem = mac;
8355 mac = m;
8356 return tem;
8359 macro *charinfo::setx_macro(macro *m, char_mode cm)
8361 macro *tem = mac;
8362 mac = m;
8363 mode = cm;
8364 return tem;
8367 void charinfo::set_number(int n)
8369 assert(n >= 0);
8370 number = n;
8373 int charinfo::get_number()
8375 assert(number >= 0);
8376 return number;
8379 symbol UNNAMED_SYMBOL("---");
8381 // For numbered characters not between 0 and 255, we make a symbol out
8382 // of the number and store them in this dictionary.
8384 dictionary numbered_charinfo_dictionary(11);
8386 charinfo *get_charinfo_by_number(int n)
8388 static charinfo *number_table[256];
8390 if (n >= 0 && n < 256) {
8391 charinfo *ci = number_table[n];
8392 if (!ci) {
8393 ci = new charinfo(UNNAMED_SYMBOL);
8394 ci->set_number(n);
8395 number_table[n] = ci;
8397 return ci;
8399 else {
8400 symbol ns(i_to_a(n));
8401 charinfo *ci = (charinfo *)numbered_charinfo_dictionary.lookup(ns);
8402 if (!ci) {
8403 ci = new charinfo(UNNAMED_SYMBOL);
8404 ci->set_number(n);
8405 (void)numbered_charinfo_dictionary.lookup(ns, ci);
8407 return ci;
8411 // This overrides the same function from libgroff; while reading font
8412 // definition files it puts single-letter glyph names into `charset_table'
8413 // and converts glyph names of the form `\x' (`x' a single letter) into `x'.
8414 // Consequently, symbol("x") refers to glyph name `\x', not `x'.
8416 glyph *name_to_glyph(const char *nm)
8418 charinfo *ci;
8419 if (nm[1] == 0)
8420 ci = charset_table[nm[0] & 0xff];
8421 else if (nm[0] == '\\' && nm[2] == 0)
8422 ci = get_charinfo(symbol(nm + 1));
8423 else
8424 ci = get_charinfo(symbol(nm));
8425 return ci->as_glyph();
8428 glyph *number_to_glyph(int n)
8430 return get_charinfo_by_number(n)->as_glyph();
8433 const char *glyph_to_name(glyph *g)
8435 charinfo *ci = (charinfo *)g; // Every glyph is actually a charinfo.
8436 return (ci->nm != UNNAMED_SYMBOL ? ci->nm.contents() : NULL);