Fix double delete for file_case usage..
[s-roff.git] / src / roff / troff / input.cpp
blobadef5a3b704bc1d3c879cd36ed13c7f8ae4c3108
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 else
805 error("can't open `%1': %2", nm.contents(), strerror(errno));
807 tok.next();
810 void shift()
812 int n;
813 if (!has_arg() || !get_integer(&n))
814 n = 1;
815 input_stack::shift(n);
816 skip_line();
819 static char get_char_for_escape_name(int allow_space = 0)
821 int c = get_copy(0, 0, 1);
822 switch (c) {
823 case EOF:
824 copy_mode_error("end of input in escape name");
825 return '\0';
826 default:
827 if (!invalid_input_char(c))
828 break;
829 // fall through
830 case '\n':
831 if (c == '\n')
832 input_stack::push(make_temp_iterator("\n"));
833 // fall through
834 case ' ':
835 if (c == ' ' && allow_space)
836 break;
837 // fall through
838 case '\t':
839 case '\001':
840 case '\b':
841 copy_mode_error("%1 is not allowed in an escape name",
842 input_char_description(c));
843 return '\0';
845 return c;
848 static symbol read_two_char_escape_name()
850 char buf[3];
851 buf[0] = get_char_for_escape_name();
852 if (buf[0] != '\0') {
853 buf[1] = get_char_for_escape_name();
854 if (buf[1] == '\0')
855 buf[0] = 0;
856 else
857 buf[2] = 0;
859 return symbol(buf);
862 static symbol read_long_escape_name(read_mode mode)
864 int start_level = input_stack::get_level();
865 char abuf[ABUF_SIZE];
866 char *buf = abuf;
867 int buf_size = ABUF_SIZE;
868 int i = 0;
869 char c;
870 int have_char = 0;
871 for (;;) {
872 c = get_char_for_escape_name(have_char && mode == WITH_ARGS);
873 if (c == 0) {
874 if (buf != abuf)
875 a_delete buf;
876 return NULL_SYMBOL;
878 have_char = 1;
879 if (mode == WITH_ARGS && c == ' ')
880 break;
881 if (i + 2 > buf_size) {
882 if (buf == abuf) {
883 buf = new char[ABUF_SIZE*2];
884 memcpy(buf, abuf, buf_size);
885 buf_size = ABUF_SIZE*2;
887 else {
888 char *old_buf = buf;
889 buf = new char[buf_size*2];
890 memcpy(buf, old_buf, buf_size);
891 buf_size *= 2;
892 a_delete old_buf;
895 if (c == ']' && input_stack::get_level() == start_level)
896 break;
897 buf[i++] = c;
899 buf[i] = 0;
900 if (c == ' ')
901 have_string_arg = 1;
902 if (buf == abuf) {
903 if (i == 0) {
904 if (mode != ALLOW_EMPTY)
905 copy_mode_error("empty escape name");
906 return EMPTY_SYMBOL;
908 return symbol(abuf);
910 else {
911 symbol s(buf);
912 a_delete buf;
913 return s;
917 static symbol read_escape_name(read_mode mode)
919 char c = get_char_for_escape_name();
920 if (c == 0)
921 return NULL_SYMBOL;
922 if (c == '(')
923 return read_two_char_escape_name();
924 if (c == '[' && !compatible_flag)
925 return read_long_escape_name(mode);
926 char buf[2];
927 buf[0] = c;
928 buf[1] = '\0';
929 return symbol(buf);
932 static symbol read_increment_and_escape_name(int *incp)
934 char c = get_char_for_escape_name();
935 switch (c) {
936 case 0:
937 *incp = 0;
938 return NULL_SYMBOL;
939 case '(':
940 *incp = 0;
941 return read_two_char_escape_name();
942 case '+':
943 *incp = 1;
944 return read_escape_name();
945 case '-':
946 *incp = -1;
947 return read_escape_name();
948 case '[':
949 if (!compatible_flag) {
950 *incp = 0;
951 return read_long_escape_name();
953 break;
955 *incp = 0;
956 char buf[2];
957 buf[0] = c;
958 buf[1] = '\0';
959 return symbol(buf);
962 static int get_copy(node **nd, int defining, int handle_escape_E)
964 for (;;) {
965 int c = input_stack::get(nd);
966 if (c == PUSH_GROFF_MODE) {
967 input_stack::save_compatible_flag(compatible_flag);
968 compatible_flag = 0;
969 continue;
971 if (c == PUSH_COMP_MODE) {
972 input_stack::save_compatible_flag(compatible_flag);
973 compatible_flag = 1;
974 continue;
976 if (c == POP_GROFFCOMP_MODE) {
977 compatible_flag = input_stack::get_compatible_flag();
978 continue;
980 if (c == BEGIN_QUOTE) {
981 input_stack::increase_level();
982 continue;
984 if (c == END_QUOTE) {
985 input_stack::decrease_level();
986 continue;
988 if (c == DOUBLE_QUOTE)
989 continue;
990 if (c == ESCAPE_E && handle_escape_E)
991 c = escape_char;
992 if (c == ESCAPE_NEWLINE) {
993 if (defining)
994 return c;
995 do {
996 c = input_stack::get(nd);
997 } while (c == ESCAPE_NEWLINE);
999 if (c != escape_char || escape_char <= 0)
1000 return c;
1001 again:
1002 c = input_stack::peek();
1003 switch(c) {
1004 case 0:
1005 return escape_char;
1006 case '"':
1007 (void)input_stack::get(0);
1008 while ((c = input_stack::get(0)) != '\n' && c != EOF)
1010 return c;
1011 case '#': // Like \" but newline is ignored.
1012 (void)input_stack::get(0);
1013 while ((c = input_stack::get(0)) != '\n')
1014 if (c == EOF)
1015 return EOF;
1016 break;
1017 case '$':
1019 (void)input_stack::get(0);
1020 symbol s = read_escape_name();
1021 if (!(s.is_null() || s.is_empty()))
1022 interpolate_arg(s);
1023 break;
1025 case '*':
1027 (void)input_stack::get(0);
1028 symbol s = read_escape_name(WITH_ARGS);
1029 if (!(s.is_null() || s.is_empty())) {
1030 if (have_string_arg) {
1031 have_string_arg = 0;
1032 interpolate_string_with_args(s);
1034 else
1035 interpolate_string(s);
1037 break;
1039 case 'a':
1040 (void)input_stack::get(0);
1041 return '\001';
1042 case 'e':
1043 (void)input_stack::get(0);
1044 return ESCAPE_e;
1045 case 'E':
1046 (void)input_stack::get(0);
1047 if (handle_escape_E)
1048 goto again;
1049 return ESCAPE_E;
1050 case 'n':
1052 (void)input_stack::get(0);
1053 int inc;
1054 symbol s = read_increment_and_escape_name(&inc);
1055 if (!(s.is_null() || s.is_empty()))
1056 interpolate_number_reg(s, inc);
1057 break;
1059 case 'g':
1061 (void)input_stack::get(0);
1062 symbol s = read_escape_name();
1063 if (!(s.is_null() || s.is_empty()))
1064 interpolate_number_format(s);
1065 break;
1067 case 't':
1068 (void)input_stack::get(0);
1069 return '\t';
1070 case 'V':
1072 (void)input_stack::get(0);
1073 symbol s = read_escape_name();
1074 if (!(s.is_null() || s.is_empty()))
1075 interpolate_environment_variable(s);
1076 break;
1078 case '\n':
1079 (void)input_stack::get(0);
1080 if (defining)
1081 return ESCAPE_NEWLINE;
1082 break;
1083 case ' ':
1084 (void)input_stack::get(0);
1085 return ESCAPE_SPACE;
1086 case '~':
1087 (void)input_stack::get(0);
1088 return ESCAPE_TILDE;
1089 case ':':
1090 (void)input_stack::get(0);
1091 return ESCAPE_COLON;
1092 case '|':
1093 (void)input_stack::get(0);
1094 return ESCAPE_BAR;
1095 case '^':
1096 (void)input_stack::get(0);
1097 return ESCAPE_CIRCUMFLEX;
1098 case '{':
1099 (void)input_stack::get(0);
1100 return ESCAPE_LEFT_BRACE;
1101 case '}':
1102 (void)input_stack::get(0);
1103 return ESCAPE_RIGHT_BRACE;
1104 case '`':
1105 (void)input_stack::get(0);
1106 return ESCAPE_LEFT_QUOTE;
1107 case '\'':
1108 (void)input_stack::get(0);
1109 return ESCAPE_RIGHT_QUOTE;
1110 case '-':
1111 (void)input_stack::get(0);
1112 return ESCAPE_HYPHEN;
1113 case '_':
1114 (void)input_stack::get(0);
1115 return ESCAPE_UNDERSCORE;
1116 case 'c':
1117 (void)input_stack::get(0);
1118 return ESCAPE_c;
1119 case '!':
1120 (void)input_stack::get(0);
1121 return ESCAPE_BANG;
1122 case '?':
1123 (void)input_stack::get(0);
1124 return ESCAPE_QUESTION;
1125 case '&':
1126 (void)input_stack::get(0);
1127 return ESCAPE_AMPERSAND;
1128 case ')':
1129 (void)input_stack::get(0);
1130 return ESCAPE_RIGHT_PARENTHESIS;
1131 case '.':
1132 (void)input_stack::get(0);
1133 return c;
1134 case '%':
1135 (void)input_stack::get(0);
1136 return ESCAPE_PERCENT;
1137 default:
1138 if (c == escape_char) {
1139 (void)input_stack::get(0);
1140 return c;
1142 else
1143 return escape_char;
1148 class non_interpreted_char_node : public node {
1149 unsigned char c;
1150 public:
1151 non_interpreted_char_node(unsigned char);
1152 node *copy();
1153 int interpret(macro *);
1154 int same(node *);
1155 const char *type();
1156 int force_tprint();
1157 int is_tag();
1160 int non_interpreted_char_node::same(node *nd)
1162 return c == ((non_interpreted_char_node *)nd)->c;
1165 const char *non_interpreted_char_node::type()
1167 return "non_interpreted_char_node";
1170 int non_interpreted_char_node::force_tprint()
1172 return 0;
1175 int non_interpreted_char_node::is_tag()
1177 return 0;
1180 non_interpreted_char_node::non_interpreted_char_node(unsigned char n) : c(n)
1182 assert(n != 0);
1185 node *non_interpreted_char_node::copy()
1187 return new non_interpreted_char_node(c);
1190 int non_interpreted_char_node::interpret(macro *mac)
1192 mac->append(c);
1193 return 1;
1196 static void do_width();
1197 static node *do_non_interpreted();
1198 static node *do_special();
1199 static node *do_suppress(symbol nm);
1200 static void do_register();
1202 dictionary color_dictionary(501);
1204 static color *lookup_color(symbol nm)
1206 assert(!nm.is_null());
1207 if (nm == default_symbol)
1208 return &default_color;
1209 color *c = (color *)color_dictionary.lookup(nm);
1210 if (c == 0)
1211 warning(WARN_COLOR, "color `%1' not defined", nm.contents());
1212 return c;
1215 void do_glyph_color(symbol nm)
1217 if (nm.is_null())
1218 return;
1219 if (nm.is_empty())
1220 curenv->set_glyph_color(curenv->get_prev_glyph_color());
1221 else {
1222 color *tem = lookup_color(nm);
1223 if (tem)
1224 curenv->set_glyph_color(tem);
1225 else
1226 (void)color_dictionary.lookup(nm, new color(nm));
1230 void do_fill_color(symbol nm)
1232 if (nm.is_null())
1233 return;
1234 if (nm.is_empty())
1235 curenv->set_fill_color(curenv->get_prev_fill_color());
1236 else {
1237 color *tem = lookup_color(nm);
1238 if (tem)
1239 curenv->set_fill_color(tem);
1240 else
1241 (void)color_dictionary.lookup(nm, new color(nm));
1245 static unsigned int get_color_element(const char *scheme, const char *col)
1247 units val;
1248 if (!get_number(&val, 'f')) {
1249 warning(WARN_COLOR, "%1 in %2 definition set to 0", col, scheme);
1250 tok.next();
1251 return 0;
1253 if (val < 0) {
1254 warning(WARN_RANGE, "%1 cannot be negative: set to 0", col);
1255 return 0;
1257 if (val > color::MAX_COLOR_VAL+1) {
1258 warning(WARN_RANGE, "%1 cannot be greater than 1", col);
1259 // we change 0x10000 to 0xffff
1260 return color::MAX_COLOR_VAL;
1262 return (unsigned int)val;
1265 static color *read_rgb(char end = 0)
1267 symbol component = do_get_long_name(0, end);
1268 if (component.is_null()) {
1269 warning(WARN_COLOR, "missing rgb color values");
1270 return 0;
1272 const char *s = component.contents();
1273 color *col = new color;
1274 if (*s == '#') {
1275 if (!col->read_rgb(s)) {
1276 warning(WARN_COLOR, "expecting rgb color definition not `%1'", s);
1277 delete col;
1278 return 0;
1281 else {
1282 if (!end)
1283 input_stack::push(make_temp_iterator(" "));
1284 input_stack::push(make_temp_iterator(s));
1285 tok.next();
1286 unsigned int r = get_color_element("rgb color", "red component");
1287 unsigned int g = get_color_element("rgb color", "green component");
1288 unsigned int b = get_color_element("rgb color", "blue component");
1289 col->set_rgb(r, g, b);
1291 return col;
1294 static color *read_cmy(char end = 0)
1296 symbol component = do_get_long_name(0, end);
1297 if (component.is_null()) {
1298 warning(WARN_COLOR, "missing cmy color values");
1299 return 0;
1301 const char *s = component.contents();
1302 color *col = new color;
1303 if (*s == '#') {
1304 if (!col->read_cmy(s)) {
1305 warning(WARN_COLOR, "expecting cmy color definition not `%1'", s);
1306 delete col;
1307 return 0;
1310 else {
1311 if (!end)
1312 input_stack::push(make_temp_iterator(" "));
1313 input_stack::push(make_temp_iterator(s));
1314 tok.next();
1315 unsigned int c = get_color_element("cmy color", "cyan component");
1316 unsigned int m = get_color_element("cmy color", "magenta component");
1317 unsigned int y = get_color_element("cmy color", "yellow component");
1318 col->set_cmy(c, m, y);
1320 return col;
1323 static color *read_cmyk(char end = 0)
1325 symbol component = do_get_long_name(0, end);
1326 if (component.is_null()) {
1327 warning(WARN_COLOR, "missing cmyk color values");
1328 return 0;
1330 const char *s = component.contents();
1331 color *col = new color;
1332 if (*s == '#') {
1333 if (!col->read_cmyk(s)) {
1334 warning(WARN_COLOR, "`expecting a cmyk color definition not `%1'", s);
1335 delete col;
1336 return 0;
1339 else {
1340 if (!end)
1341 input_stack::push(make_temp_iterator(" "));
1342 input_stack::push(make_temp_iterator(s));
1343 tok.next();
1344 unsigned int c = get_color_element("cmyk color", "cyan component");
1345 unsigned int m = get_color_element("cmyk color", "magenta component");
1346 unsigned int y = get_color_element("cmyk color", "yellow component");
1347 unsigned int k = get_color_element("cmyk color", "black component");
1348 col->set_cmyk(c, m, y, k);
1350 return col;
1353 static color *read_gray(char end = 0)
1355 symbol component = do_get_long_name(0, end);
1356 if (component.is_null()) {
1357 warning(WARN_COLOR, "missing gray values");
1358 return 0;
1360 const char *s = component.contents();
1361 color *col = new color;
1362 if (*s == '#') {
1363 if (!col->read_gray(s)) {
1364 warning(WARN_COLOR, "`expecting a gray definition not `%1'", s);
1365 delete col;
1366 return 0;
1369 else {
1370 if (!end)
1371 input_stack::push(make_temp_iterator("\n"));
1372 input_stack::push(make_temp_iterator(s));
1373 tok.next();
1374 unsigned int g = get_color_element("gray", "gray value");
1375 col->set_gray(g);
1377 return col;
1380 static void activate_color()
1382 int n;
1383 if (has_arg() && get_integer(&n))
1384 color_flag = n != 0;
1385 else
1386 color_flag = 1;
1387 skip_line();
1390 static void define_color()
1392 symbol color_name = get_long_name(1);
1393 if (color_name.is_null()) {
1394 skip_line();
1395 return;
1397 if (color_name == default_symbol) {
1398 warning(WARN_COLOR, "default color can't be redefined");
1399 skip_line();
1400 return;
1402 symbol style = get_long_name(1);
1403 if (style.is_null()) {
1404 skip_line();
1405 return;
1407 color *col;
1408 if (strcmp(style.contents(), "rgb") == 0)
1409 col = read_rgb();
1410 else if (strcmp(style.contents(), "cmyk") == 0)
1411 col = read_cmyk();
1412 else if (strcmp(style.contents(), "gray") == 0)
1413 col = read_gray();
1414 else if (strcmp(style.contents(), "grey") == 0)
1415 col = read_gray();
1416 else if (strcmp(style.contents(), "cmy") == 0)
1417 col = read_cmy();
1418 else {
1419 warning(WARN_COLOR,
1420 "unknown color space `%1'; use rgb, cmyk, gray or cmy",
1421 style.contents());
1422 skip_line();
1423 return;
1425 if (col) {
1426 col->nm = color_name;
1427 (void)color_dictionary.lookup(color_name, col);
1429 skip_line();
1432 static node *do_overstrike()
1434 token start;
1435 overstrike_node *on = new overstrike_node;
1436 int start_level = input_stack::get_level();
1437 start.next();
1438 for (;;) {
1439 tok.next();
1440 if (tok.newline() || tok.eof()) {
1441 warning(WARN_DELIM, "missing closing delimiter");
1442 input_stack::push(make_temp_iterator("\n"));
1443 break;
1445 if (tok == start
1446 && (compatible_flag || input_stack::get_level() == start_level))
1447 break;
1448 charinfo *ci = tok.get_char(1);
1449 if (ci) {
1450 node *n = curenv->make_char_node(ci);
1451 if (n)
1452 on->overstrike(n);
1455 return on;
1458 static node *do_bracket()
1460 token start;
1461 bracket_node *bn = new bracket_node;
1462 start.next();
1463 int start_level = input_stack::get_level();
1464 for (;;) {
1465 tok.next();
1466 if (tok.eof()) {
1467 warning(WARN_DELIM, "missing closing delimiter");
1468 break;
1470 if (tok.newline()) {
1471 warning(WARN_DELIM, "missing closing delimiter");
1472 input_stack::push(make_temp_iterator("\n"));
1473 break;
1475 if (tok == start
1476 && (compatible_flag || input_stack::get_level() == start_level))
1477 break;
1478 charinfo *ci = tok.get_char(1);
1479 if (ci) {
1480 node *n = curenv->make_char_node(ci);
1481 if (n)
1482 bn->bracket(n);
1485 return bn;
1488 static int do_name_test()
1490 token start;
1491 start.next();
1492 int start_level = input_stack::get_level();
1493 int bad_char = 0;
1494 int some_char = 0;
1495 for (;;) {
1496 tok.next();
1497 if (tok.newline() || tok.eof()) {
1498 warning(WARN_DELIM, "missing closing delimiter");
1499 input_stack::push(make_temp_iterator("\n"));
1500 break;
1502 if (tok == start
1503 && (compatible_flag || input_stack::get_level() == start_level))
1504 break;
1505 if (!tok.ch())
1506 bad_char = 1;
1507 some_char = 1;
1509 return some_char && !bad_char;
1512 static int do_expr_test()
1514 token start;
1515 start.next();
1516 int start_level = input_stack::get_level();
1517 if (!start.delimiter(1))
1518 return 0;
1519 tok.next();
1520 // disable all warning and error messages temporarily
1521 int saved_warning_mask = warning_mask;
1522 int saved_inhibit_errors = inhibit_errors;
1523 warning_mask = 0;
1524 inhibit_errors = 1;
1525 int dummy;
1526 int result = get_number_rigidly(&dummy, 'u');
1527 warning_mask = saved_warning_mask;
1528 inhibit_errors = saved_inhibit_errors;
1529 if (tok == start && input_stack::get_level() == start_level)
1530 return result;
1531 // ignore everything up to the delimiter in case we aren't right there
1532 for (;;) {
1533 tok.next();
1534 if (tok.newline() || tok.eof()) {
1535 warning(WARN_DELIM, "missing closing delimiter");
1536 input_stack::push(make_temp_iterator("\n"));
1537 break;
1539 if (tok == start && input_stack::get_level() == start_level)
1540 break;
1542 return 0;
1545 #if 0
1546 static node *do_zero_width()
1548 token start;
1549 start.next();
1550 int start_level = input_stack::get_level();
1551 environment env(curenv);
1552 environment *oldenv = curenv;
1553 curenv = &env;
1554 for (;;) {
1555 tok.next();
1556 if (tok.newline() || tok.eof()) {
1557 error("missing closing delimiter");
1558 break;
1560 if (tok == start
1561 && (compatible_flag || input_stack::get_level() == start_level))
1562 break;
1563 tok.process();
1565 curenv = oldenv;
1566 node *rev = env.extract_output_line();
1567 node *n = 0;
1568 while (rev) {
1569 node *tem = rev;
1570 rev = rev->next;
1571 tem->next = n;
1572 n = tem;
1574 return new zero_width_node(n);
1577 #else
1579 // It's undesirable for \Z to change environments, because then
1580 // \n(.w won't work as expected.
1582 static node *do_zero_width()
1584 node *rev = new dummy_node;
1585 token start;
1586 start.next();
1587 int start_level = input_stack::get_level();
1588 for (;;) {
1589 tok.next();
1590 if (tok.newline() || tok.eof()) {
1591 warning(WARN_DELIM, "missing closing delimiter");
1592 input_stack::push(make_temp_iterator("\n"));
1593 break;
1595 if (tok == start
1596 && (compatible_flag || input_stack::get_level() == start_level))
1597 break;
1598 if (!tok.add_to_node_list(&rev))
1599 error("invalid token in argument to \\Z");
1601 node *n = 0;
1602 while (rev) {
1603 node *tem = rev;
1604 rev = rev->next;
1605 tem->next = n;
1606 n = tem;
1608 return new zero_width_node(n);
1611 #endif
1613 token_node *node::get_token_node()
1615 return 0;
1618 class token_node : public node {
1619 public:
1620 token tk;
1621 token_node(const token &t);
1622 node *copy();
1623 token_node *get_token_node();
1624 int same(node *);
1625 const char *type();
1626 int force_tprint();
1627 int is_tag();
1630 token_node::token_node(const token &t) : tk(t)
1634 node *token_node::copy()
1636 return new token_node(tk);
1639 token_node *token_node::get_token_node()
1641 return this;
1644 int token_node::same(node *nd)
1646 return tk == ((token_node *)nd)->tk;
1649 const char *token_node::type()
1651 return "token_node";
1654 int token_node::force_tprint()
1656 return 0;
1659 int token_node::is_tag()
1661 return 0;
1664 token::token() : nd(0), type(TOKEN_EMPTY)
1668 token::~token()
1670 delete nd;
1673 token::token(const token &t)
1674 : nm(t.nm), c(t.c), val(t.val), dim(t.dim), type(t.type)
1676 // Use two statements to work around bug in SGI C++.
1677 node *tem = t.nd;
1678 nd = tem ? tem->copy() : 0;
1681 void token::operator=(const token &t)
1683 delete nd;
1684 nm = t.nm;
1685 // Use two statements to work around bug in SGI C++.
1686 node *tem = t.nd;
1687 nd = tem ? tem->copy() : 0;
1688 c = t.c;
1689 val = t.val;
1690 dim = t.dim;
1691 type = t.type;
1694 void token::skip()
1696 while (space())
1697 next();
1700 int has_arg()
1702 while (tok.space())
1703 tok.next();
1704 return !tok.newline();
1707 void token::make_space()
1709 type = TOKEN_SPACE;
1712 void token::make_newline()
1714 type = TOKEN_NEWLINE;
1717 void token::next()
1719 if (nd) {
1720 delete nd;
1721 nd = 0;
1723 units x;
1724 for (;;) {
1725 node *n = 0;
1726 int cc = input_stack::get(&n);
1727 if (cc != escape_char || escape_char == 0) {
1728 handle_normal_char:
1729 switch(cc) {
1730 case PUSH_GROFF_MODE:
1731 input_stack::save_compatible_flag(compatible_flag);
1732 compatible_flag = 0;
1733 continue;
1734 case PUSH_COMP_MODE:
1735 input_stack::save_compatible_flag(compatible_flag);
1736 compatible_flag = 1;
1737 continue;
1738 case POP_GROFFCOMP_MODE:
1739 compatible_flag = input_stack::get_compatible_flag();
1740 continue;
1741 case BEGIN_QUOTE:
1742 input_stack::increase_level();
1743 continue;
1744 case END_QUOTE:
1745 input_stack::decrease_level();
1746 continue;
1747 case DOUBLE_QUOTE:
1748 continue;
1749 case EOF:
1750 type = TOKEN_EOF;
1751 return;
1752 case TRANSPARENT_FILE_REQUEST:
1753 case TITLE_REQUEST:
1754 case COPY_FILE_REQUEST:
1755 #ifdef COLUMN
1756 case VJUSTIFY_REQUEST:
1757 #endif /* COLUMN */
1758 type = TOKEN_REQUEST;
1759 c = cc;
1760 return;
1761 case BEGIN_TRAP:
1762 type = TOKEN_BEGIN_TRAP;
1763 return;
1764 case END_TRAP:
1765 type = TOKEN_END_TRAP;
1766 return;
1767 case LAST_PAGE_EJECTOR:
1768 seen_last_page_ejector = 1;
1769 // fall through
1770 case PAGE_EJECTOR:
1771 type = TOKEN_PAGE_EJECTOR;
1772 return;
1773 case ESCAPE_PERCENT:
1774 ESCAPE_PERCENT:
1775 type = TOKEN_HYPHEN_INDICATOR;
1776 return;
1777 case ESCAPE_SPACE:
1778 ESCAPE_SPACE:
1779 type = TOKEN_UNSTRETCHABLE_SPACE;
1780 return;
1781 case ESCAPE_TILDE:
1782 ESCAPE_TILDE:
1783 type = TOKEN_STRETCHABLE_SPACE;
1784 return;
1785 case ESCAPE_COLON:
1786 ESCAPE_COLON:
1787 type = TOKEN_ZERO_WIDTH_BREAK;
1788 return;
1789 case ESCAPE_e:
1790 ESCAPE_e:
1791 type = TOKEN_ESCAPE;
1792 return;
1793 case ESCAPE_E:
1794 goto handle_escape_char;
1795 case ESCAPE_BAR:
1796 ESCAPE_BAR:
1797 type = TOKEN_NODE;
1798 nd = new hmotion_node(curenv->get_narrow_space_width(),
1799 curenv->get_fill_color());
1800 return;
1801 case ESCAPE_CIRCUMFLEX:
1802 ESCAPE_CIRCUMFLEX:
1803 type = TOKEN_NODE;
1804 nd = new hmotion_node(curenv->get_half_narrow_space_width(),
1805 curenv->get_fill_color());
1806 return;
1807 case ESCAPE_NEWLINE:
1808 have_input = 0;
1809 break;
1810 case ESCAPE_LEFT_BRACE:
1811 ESCAPE_LEFT_BRACE:
1812 type = TOKEN_LEFT_BRACE;
1813 return;
1814 case ESCAPE_RIGHT_BRACE:
1815 ESCAPE_RIGHT_BRACE:
1816 type = TOKEN_RIGHT_BRACE;
1817 return;
1818 case ESCAPE_LEFT_QUOTE:
1819 ESCAPE_LEFT_QUOTE:
1820 type = TOKEN_SPECIAL;
1821 nm = symbol("ga");
1822 return;
1823 case ESCAPE_RIGHT_QUOTE:
1824 ESCAPE_RIGHT_QUOTE:
1825 type = TOKEN_SPECIAL;
1826 nm = symbol("aa");
1827 return;
1828 case ESCAPE_HYPHEN:
1829 ESCAPE_HYPHEN:
1830 type = TOKEN_SPECIAL;
1831 nm = symbol("-");
1832 return;
1833 case ESCAPE_UNDERSCORE:
1834 ESCAPE_UNDERSCORE:
1835 type = TOKEN_SPECIAL;
1836 nm = symbol("ul");
1837 return;
1838 case ESCAPE_c:
1839 ESCAPE_c:
1840 type = TOKEN_INTERRUPT;
1841 return;
1842 case ESCAPE_BANG:
1843 ESCAPE_BANG:
1844 type = TOKEN_TRANSPARENT;
1845 return;
1846 case ESCAPE_QUESTION:
1847 ESCAPE_QUESTION:
1848 nd = do_non_interpreted();
1849 if (nd) {
1850 type = TOKEN_NODE;
1851 return;
1853 break;
1854 case ESCAPE_AMPERSAND:
1855 ESCAPE_AMPERSAND:
1856 type = TOKEN_DUMMY;
1857 return;
1858 case ESCAPE_RIGHT_PARENTHESIS:
1859 ESCAPE_RIGHT_PARENTHESIS:
1860 type = TOKEN_TRANSPARENT_DUMMY;
1861 return;
1862 case '\b':
1863 type = TOKEN_BACKSPACE;
1864 return;
1865 case ' ':
1866 type = TOKEN_SPACE;
1867 return;
1868 case '\t':
1869 type = TOKEN_TAB;
1870 return;
1871 case '\n':
1872 type = TOKEN_NEWLINE;
1873 return;
1874 case '\001':
1875 type = TOKEN_LEADER;
1876 return;
1877 case 0:
1879 assert(n != 0);
1880 token_node *tn = n->get_token_node();
1881 if (tn) {
1882 *this = tn->tk;
1883 delete tn;
1885 else {
1886 nd = n;
1887 type = TOKEN_NODE;
1890 return;
1891 default:
1892 type = TOKEN_CHAR;
1893 c = cc;
1894 return;
1897 else {
1898 handle_escape_char:
1899 cc = input_stack::get(&n);
1900 switch(cc) {
1901 case '(':
1902 nm = read_two_char_escape_name();
1903 type = TOKEN_SPECIAL;
1904 return;
1905 case EOF:
1906 type = TOKEN_EOF;
1907 error("end of input after escape character");
1908 return;
1909 case '`':
1910 goto ESCAPE_LEFT_QUOTE;
1911 case '\'':
1912 goto ESCAPE_RIGHT_QUOTE;
1913 case '-':
1914 goto ESCAPE_HYPHEN;
1915 case '_':
1916 goto ESCAPE_UNDERSCORE;
1917 case '%':
1918 goto ESCAPE_PERCENT;
1919 case ' ':
1920 goto ESCAPE_SPACE;
1921 case '0':
1922 nd = new hmotion_node(curenv->get_digit_width(),
1923 curenv->get_fill_color());
1924 type = TOKEN_NODE;
1925 return;
1926 case '|':
1927 goto ESCAPE_BAR;
1928 case '^':
1929 goto ESCAPE_CIRCUMFLEX;
1930 case '/':
1931 type = TOKEN_ITALIC_CORRECTION;
1932 return;
1933 case ',':
1934 type = TOKEN_NODE;
1935 nd = new left_italic_corrected_node;
1936 return;
1937 case '&':
1938 goto ESCAPE_AMPERSAND;
1939 case ')':
1940 goto ESCAPE_RIGHT_PARENTHESIS;
1941 case '!':
1942 goto ESCAPE_BANG;
1943 case '?':
1944 goto ESCAPE_QUESTION;
1945 case '~':
1946 goto ESCAPE_TILDE;
1947 case ':':
1948 goto ESCAPE_COLON;
1949 case '"':
1950 while ((cc = input_stack::get(0)) != '\n' && cc != EOF)
1952 if (cc == '\n')
1953 type = TOKEN_NEWLINE;
1954 else
1955 type = TOKEN_EOF;
1956 return;
1957 case '#': // Like \" but newline is ignored.
1958 while ((cc = input_stack::get(0)) != '\n')
1959 if (cc == EOF) {
1960 type = TOKEN_EOF;
1961 return;
1963 break;
1964 case '$':
1966 symbol s = read_escape_name();
1967 if (!(s.is_null() || s.is_empty()))
1968 interpolate_arg(s);
1969 break;
1971 case '*':
1973 symbol s = read_escape_name(WITH_ARGS);
1974 if (!(s.is_null() || s.is_empty())) {
1975 if (have_string_arg) {
1976 have_string_arg = 0;
1977 interpolate_string_with_args(s);
1979 else
1980 interpolate_string(s);
1982 break;
1984 case 'a':
1985 nd = new non_interpreted_char_node('\001');
1986 type = TOKEN_NODE;
1987 return;
1988 case 'A':
1989 c = '0' + do_name_test();
1990 type = TOKEN_CHAR;
1991 return;
1992 case 'b':
1993 nd = do_bracket();
1994 type = TOKEN_NODE;
1995 return;
1996 case 'B':
1997 c = '0' + do_expr_test();
1998 type = TOKEN_CHAR;
1999 return;
2000 case 'c':
2001 goto ESCAPE_c;
2002 case 'C':
2003 nm = get_delim_name();
2004 if (nm.is_null())
2005 break;
2006 type = TOKEN_SPECIAL;
2007 return;
2008 case 'd':
2009 type = TOKEN_NODE;
2010 nd = new vmotion_node(curenv->get_size() / 2,
2011 curenv->get_fill_color());
2012 return;
2013 case 'D':
2014 nd = read_draw_node();
2015 if (!nd)
2016 break;
2017 type = TOKEN_NODE;
2018 return;
2019 case 'e':
2020 goto ESCAPE_e;
2021 case 'E':
2022 goto handle_escape_char;
2023 case 'f':
2025 symbol s = read_escape_name(ALLOW_EMPTY);
2026 if (s.is_null())
2027 break;
2028 const char *p;
2029 for (p = s.contents(); *p != '\0'; p++)
2030 if (!csdigit(*p))
2031 break;
2032 if (*p || s.is_empty())
2033 curenv->set_font(s);
2034 else
2035 curenv->set_font(atoi(s.contents()));
2036 if (!compatible_flag)
2037 have_input = 1;
2038 break;
2040 case 'F':
2042 symbol s = read_escape_name(ALLOW_EMPTY);
2043 if (s.is_null())
2044 break;
2045 curenv->set_family(s);
2046 have_input = 1;
2047 break;
2049 case 'g':
2051 symbol s = read_escape_name();
2052 if (!(s.is_null() || s.is_empty()))
2053 interpolate_number_format(s);
2054 break;
2056 case 'h':
2057 if (!get_delim_number(&x, 'm'))
2058 break;
2059 type = TOKEN_NODE;
2060 nd = new hmotion_node(x, curenv->get_fill_color());
2061 return;
2062 case 'H':
2063 // don't take height increments relative to previous height if
2064 // in compatibility mode
2065 if (!compatible_flag && curenv->get_char_height()) {
2066 if (get_delim_number(&x, 'z', curenv->get_char_height()))
2067 curenv->set_char_height(x);
2069 else {
2070 if (get_delim_number(&x, 'z', curenv->get_requested_point_size()))
2071 curenv->set_char_height(x);
2073 if (!compatible_flag)
2074 have_input = 1;
2075 break;
2076 case 'k':
2077 nm = read_escape_name();
2078 if (nm.is_null() || nm.is_empty())
2079 break;
2080 type = TOKEN_MARK_INPUT;
2081 return;
2082 case 'l':
2083 case 'L':
2085 charinfo *s = 0;
2086 if (!get_line_arg(&x, (cc == 'l' ? 'm': 'v'), &s))
2087 break;
2088 if (s == 0)
2089 s = get_charinfo(cc == 'l' ? "ru" : "br");
2090 type = TOKEN_NODE;
2091 node *char_node = curenv->make_char_node(s);
2092 if (cc == 'l')
2093 nd = new hline_node(x, char_node);
2094 else
2095 nd = new vline_node(x, char_node);
2096 return;
2098 case 'm':
2099 do_glyph_color(read_escape_name(ALLOW_EMPTY));
2100 if (!compatible_flag)
2101 have_input = 1;
2102 break;
2103 case 'M':
2104 do_fill_color(read_escape_name(ALLOW_EMPTY));
2105 if (!compatible_flag)
2106 have_input = 1;
2107 break;
2108 case 'n':
2110 int inc;
2111 symbol s = read_increment_and_escape_name(&inc);
2112 if (!(s.is_null() || s.is_empty()))
2113 interpolate_number_reg(s, inc);
2114 break;
2116 case 'N':
2117 if (!get_delim_number(&val, 0))
2118 break;
2119 if (val < 0) {
2120 warning(WARN_CHAR, "invalid numbered character %1", val);
2121 break;
2123 type = TOKEN_NUMBERED_CHAR;
2124 return;
2125 case 'o':
2126 nd = do_overstrike();
2127 type = TOKEN_NODE;
2128 return;
2129 case 'O':
2130 nd = do_suppress(read_escape_name());
2131 if (!nd)
2132 break;
2133 type = TOKEN_NODE;
2134 return;
2135 case 'p':
2136 type = TOKEN_SPREAD;
2137 return;
2138 case 'r':
2139 type = TOKEN_NODE;
2140 nd = new vmotion_node(-curenv->get_size(), curenv->get_fill_color());
2141 return;
2142 case 'R':
2143 do_register();
2144 if (!compatible_flag)
2145 have_input = 1;
2146 break;
2147 case 's':
2148 if (read_size(&x))
2149 curenv->set_size(x);
2150 if (!compatible_flag)
2151 have_input = 1;
2152 break;
2153 case 'S':
2154 if (get_delim_number(&x, 0))
2155 curenv->set_char_slant(x);
2156 if (!compatible_flag)
2157 have_input = 1;
2158 break;
2159 case 't':
2160 type = TOKEN_NODE;
2161 nd = new non_interpreted_char_node('\t');
2162 return;
2163 case 'u':
2164 type = TOKEN_NODE;
2165 nd = new vmotion_node(-curenv->get_size() / 2,
2166 curenv->get_fill_color());
2167 return;
2168 case 'v':
2169 if (!get_delim_number(&x, 'v'))
2170 break;
2171 type = TOKEN_NODE;
2172 nd = new vmotion_node(x, curenv->get_fill_color());
2173 return;
2174 case 'V':
2176 symbol s = read_escape_name();
2177 if (!(s.is_null() || s.is_empty()))
2178 interpolate_environment_variable(s);
2179 break;
2181 case 'w':
2182 do_width();
2183 break;
2184 case 'x':
2185 if (!get_delim_number(&x, 'v'))
2186 break;
2187 type = TOKEN_NODE;
2188 nd = new extra_size_node(x);
2189 return;
2190 case 'X':
2191 nd = do_special();
2192 if (!nd)
2193 break;
2194 type = TOKEN_NODE;
2195 return;
2196 case 'Y':
2198 symbol s = read_escape_name();
2199 if (s.is_null() || s.is_empty())
2200 break;
2201 request_or_macro *p = lookup_request(s);
2202 macro *m = p->to_macro();
2203 if (!m) {
2204 error("can't transparently throughput a request");
2205 break;
2207 nd = new special_node(*m);
2208 type = TOKEN_NODE;
2209 return;
2211 case 'z':
2213 next();
2214 if (type == TOKEN_NODE)
2215 nd = new zero_width_node(nd);
2216 else {
2217 charinfo *ci = get_char(1);
2218 if (ci == 0)
2219 break;
2220 node *gn = curenv->make_char_node(ci);
2221 if (gn == 0)
2222 break;
2223 nd = new zero_width_node(gn);
2224 type = TOKEN_NODE;
2226 return;
2228 case 'Z':
2229 nd = do_zero_width();
2230 if (nd == 0)
2231 break;
2232 type = TOKEN_NODE;
2233 return;
2234 case '{':
2235 goto ESCAPE_LEFT_BRACE;
2236 case '}':
2237 goto ESCAPE_RIGHT_BRACE;
2238 case '\n':
2239 break;
2240 case '[':
2241 if (!compatible_flag) {
2242 symbol s = read_long_escape_name(WITH_ARGS);
2243 if (s.is_null() || s.is_empty())
2244 break;
2245 if (have_string_arg) {
2246 have_string_arg = 0;
2247 nm = composite_glyph_name(s);
2249 else {
2250 const char *gn = check_unicode_name(s.contents());
2251 if (gn) {
2252 const char *gn_decomposed = decompose_unicode(gn);
2253 if (gn_decomposed)
2254 gn = &gn_decomposed[1];
2255 const char *groff_gn = unicode_to_glyph_name(gn);
2256 if (groff_gn)
2257 nm = symbol(groff_gn);
2258 else {
2259 char *buf = new char[strlen(gn) + 1 + 1];
2260 strcpy(buf, "u");
2261 strcat(buf, gn);
2262 nm = symbol(buf);
2263 a_delete buf;
2266 else
2267 nm = symbol(s.contents());
2269 type = TOKEN_SPECIAL;
2270 return;
2272 goto handle_normal_char;
2273 default:
2274 if (cc != escape_char && cc != '.')
2275 warning(WARN_ESCAPE, "escape character ignored before %1",
2276 input_char_description(cc));
2277 goto handle_normal_char;
2283 int token::operator==(const token &t)
2285 if (type != t.type)
2286 return 0;
2287 switch(type) {
2288 case TOKEN_CHAR:
2289 return c == t.c;
2290 case TOKEN_SPECIAL:
2291 return nm == t.nm;
2292 case TOKEN_NUMBERED_CHAR:
2293 return val == t.val;
2294 default:
2295 return 1;
2299 int token::operator!=(const token &t)
2301 return !(*this == t);
2304 // is token a suitable delimiter (like ')?
2306 int token::delimiter(int err)
2308 switch(type) {
2309 case TOKEN_CHAR:
2310 switch(c) {
2311 case '0':
2312 case '1':
2313 case '2':
2314 case '3':
2315 case '4':
2316 case '5':
2317 case '6':
2318 case '7':
2319 case '8':
2320 case '9':
2321 case '+':
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 if (err)
2335 error("cannot use character `%1' as a starting delimiter", char(c));
2336 return 0;
2337 default:
2338 return 1;
2340 case TOKEN_NODE:
2341 case TOKEN_SPACE:
2342 case TOKEN_STRETCHABLE_SPACE:
2343 case TOKEN_UNSTRETCHABLE_SPACE:
2344 case TOKEN_TAB:
2345 case TOKEN_NEWLINE:
2346 if (err)
2347 error("cannot use %1 as a starting delimiter", description());
2348 return 0;
2349 default:
2350 return 1;
2354 const char *token::description()
2356 static char buf[4];
2357 switch (type) {
2358 case TOKEN_BACKSPACE:
2359 return "a backspace character";
2360 case TOKEN_CHAR:
2361 buf[0] = '`';
2362 buf[1] = c;
2363 buf[2] = '\'';
2364 buf[3] = '\0';
2365 return buf;
2366 case TOKEN_DUMMY:
2367 return "`\\&'";
2368 case TOKEN_ESCAPE:
2369 return "`\\e'";
2370 case TOKEN_HYPHEN_INDICATOR:
2371 return "`\\%'";
2372 case TOKEN_INTERRUPT:
2373 return "`\\c'";
2374 case TOKEN_ITALIC_CORRECTION:
2375 return "`\\/'";
2376 case TOKEN_LEADER:
2377 return "a leader character";
2378 case TOKEN_LEFT_BRACE:
2379 return "`\\{'";
2380 case TOKEN_MARK_INPUT:
2381 return "`\\k'";
2382 case TOKEN_NEWLINE:
2383 return "newline";
2384 case TOKEN_NODE:
2385 return "a node";
2386 case TOKEN_NUMBERED_CHAR:
2387 return "`\\N'";
2388 case TOKEN_RIGHT_BRACE:
2389 return "`\\}'";
2390 case TOKEN_SPACE:
2391 return "a space";
2392 case TOKEN_SPECIAL:
2393 return "a special character";
2394 case TOKEN_SPREAD:
2395 return "`\\p'";
2396 case TOKEN_STRETCHABLE_SPACE:
2397 return "`\\~'";
2398 case TOKEN_UNSTRETCHABLE_SPACE:
2399 return "`\\ '";
2400 case TOKEN_TAB:
2401 return "a tab character";
2402 case TOKEN_TRANSPARENT:
2403 return "`\\!'";
2404 case TOKEN_TRANSPARENT_DUMMY:
2405 return "`\\)'";
2406 case TOKEN_ZERO_WIDTH_BREAK:
2407 return "`\\:'";
2408 case TOKEN_EOF:
2409 return "end of input";
2410 default:
2411 break;
2413 return "a magic token";
2416 void skip_line()
2418 while (!tok.newline())
2419 if (tok.eof())
2420 return;
2421 else
2422 tok.next();
2423 tok.next();
2426 void compatible()
2428 int n;
2429 if (has_arg() && get_integer(&n))
2430 compatible_flag = n != 0;
2431 else
2432 compatible_flag = 1;
2433 skip_line();
2436 static void empty_name_warning(int required)
2438 if (tok.newline() || tok.eof()) {
2439 if (required)
2440 warning(WARN_MISSING, "missing name");
2442 else if (tok.right_brace() || tok.tab()) {
2443 const char *start = tok.description();
2444 do {
2445 tok.next();
2446 } while (tok.space() || tok.right_brace() || tok.tab());
2447 if (!tok.newline() && !tok.eof())
2448 error("%1 is not allowed before an argument", start);
2449 else if (required)
2450 warning(WARN_MISSING, "missing name");
2452 else if (required)
2453 error("name expected (got %1)", tok.description());
2454 else
2455 error("name expected (got %1): treated as missing", tok.description());
2458 static void non_empty_name_warning()
2460 if (!tok.newline() && !tok.eof() && !tok.space() && !tok.tab()
2461 && !tok.right_brace()
2462 // We don't want to give a warning for .el\{
2463 && !tok.left_brace())
2464 error("%1 is not allowed in a name", tok.description());
2467 symbol get_name(int required)
2469 if (compatible_flag) {
2470 char buf[3];
2471 tok.skip();
2472 if ((buf[0] = tok.ch()) != 0) {
2473 tok.next();
2474 if ((buf[1] = tok.ch()) != 0) {
2475 buf[2] = 0;
2476 tok.make_space();
2478 else
2479 non_empty_name_warning();
2480 return symbol(buf);
2482 else {
2483 empty_name_warning(required);
2484 return NULL_SYMBOL;
2487 else
2488 return get_long_name(required);
2491 symbol get_long_name(int required)
2493 return do_get_long_name(required, 0);
2496 static symbol do_get_long_name(int required, char end)
2498 while (tok.space())
2499 tok.next();
2500 char abuf[ABUF_SIZE];
2501 char *buf = abuf;
2502 int buf_size = ABUF_SIZE;
2503 int i = 0;
2504 for (;;) {
2505 // If end != 0 we normally have to append a null byte
2506 if (i + 2 > buf_size) {
2507 if (buf == abuf) {
2508 buf = new char[ABUF_SIZE*2];
2509 memcpy(buf, abuf, buf_size);
2510 buf_size = ABUF_SIZE*2;
2512 else {
2513 char *old_buf = buf;
2514 buf = new char[buf_size*2];
2515 memcpy(buf, old_buf, buf_size);
2516 buf_size *= 2;
2517 a_delete old_buf;
2520 if ((buf[i] = tok.ch()) == 0 || buf[i] == end)
2521 break;
2522 i++;
2523 tok.next();
2525 if (i == 0) {
2526 empty_name_warning(required);
2527 return NULL_SYMBOL;
2529 if (end && buf[i] == end)
2530 buf[i+1] = '\0';
2531 else
2532 non_empty_name_warning();
2533 if (buf == abuf)
2534 return symbol(buf);
2535 else {
2536 symbol s(buf);
2537 a_delete buf;
2538 return s;
2542 void exit_troff()
2544 exit_started = 1;
2545 topdiv->set_last_page();
2546 if (!end_macro_name.is_null()) {
2547 spring_trap(end_macro_name);
2548 tok.next();
2549 process_input_stack();
2551 curenv->final_break();
2552 tok.next();
2553 process_input_stack();
2554 end_diversions();
2555 if (topdiv->get_page_length() > 0) {
2556 done_end_macro = 1;
2557 topdiv->set_ejecting();
2558 static unsigned char buf[2] = { LAST_PAGE_EJECTOR, '\0' };
2559 input_stack::push(make_temp_iterator((char *)buf));
2560 topdiv->space(topdiv->get_page_length(), 1);
2561 tok.next();
2562 process_input_stack();
2563 seen_last_page_ejector = 1; // should be set already
2564 topdiv->set_ejecting();
2565 push_page_ejector();
2566 topdiv->space(topdiv->get_page_length(), 1);
2567 tok.next();
2568 process_input_stack();
2570 // This will only happen if a trap-invoked macro starts a diversion,
2571 // or if vertical position traps have been disabled.
2572 cleanup_and_exit(0);
2575 // This implements .ex. The input stack must be cleared before calling
2576 // exit_troff().
2578 void exit_request()
2580 input_stack::clear();
2581 if (exit_started)
2582 tok.next();
2583 else
2584 exit_troff();
2587 void return_macro_request()
2589 if (has_arg() && tok.ch())
2590 input_stack::pop_macro();
2591 input_stack::pop_macro();
2592 tok.next();
2595 void end_macro()
2597 end_macro_name = get_name();
2598 skip_line();
2601 void blank_line_macro()
2603 blank_line_macro_name = get_name();
2604 skip_line();
2607 static void trapping_blank_line()
2609 if (!blank_line_macro_name.is_null())
2610 spring_trap(blank_line_macro_name);
2611 else
2612 blank_line();
2615 void do_request()
2617 int old_compatible_flag = compatible_flag;
2618 compatible_flag = 0;
2619 symbol nm = get_name();
2620 if (nm.is_null())
2621 skip_line();
2622 else
2623 interpolate_macro(nm, 1);
2624 compatible_flag = old_compatible_flag;
2625 request_or_macro *p = lookup_request(nm);
2626 macro *m = p->to_macro();
2627 if (m)
2628 tok.next();
2631 inline int possibly_handle_first_page_transition()
2633 if (topdiv->before_first_page && curdiv == topdiv && !curenv->is_dummy()) {
2634 handle_first_page_transition();
2635 return 1;
2637 else
2638 return 0;
2641 static int transparent_translate(int cc)
2643 if (!invalid_input_char(cc)) {
2644 charinfo *ci = charset_table[cc];
2645 switch (ci->get_special_translation(1)) {
2646 case charinfo::TRANSLATE_SPACE:
2647 return ' ';
2648 case charinfo::TRANSLATE_STRETCHABLE_SPACE:
2649 return ESCAPE_TILDE;
2650 case charinfo::TRANSLATE_DUMMY:
2651 return ESCAPE_AMPERSAND;
2652 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
2653 return ESCAPE_PERCENT;
2655 // This is really ugly.
2656 ci = ci->get_translation(1);
2657 if (ci) {
2658 int c = ci->get_ascii_code();
2659 if (c != '\0')
2660 return c;
2661 error("can't translate %1 to special character `%2'"
2662 " in transparent throughput",
2663 input_char_description(cc),
2664 ci->nm.contents());
2667 return cc;
2670 class int_stack {
2671 struct int_stack_element {
2672 int n;
2673 int_stack_element *next;
2674 } *top;
2675 public:
2676 int_stack();
2677 ~int_stack();
2678 void push(int);
2679 int is_empty();
2680 int pop();
2683 int_stack::int_stack()
2685 top = 0;
2688 int_stack::~int_stack()
2690 while (top != 0) {
2691 int_stack_element *temp = top;
2692 top = top->next;
2693 delete temp;
2697 int int_stack::is_empty()
2699 return top == 0;
2702 void int_stack::push(int n)
2704 int_stack_element *p = new int_stack_element;
2705 p->next = top;
2706 p->n = n;
2707 top = p;
2710 int int_stack::pop()
2712 assert(top != 0);
2713 int_stack_element *p = top;
2714 top = top->next;
2715 int n = p->n;
2716 delete p;
2717 return n;
2720 int node::reread(int *)
2722 return 0;
2725 int global_diverted_space = 0;
2727 int diverted_space_node::reread(int *bolp)
2729 global_diverted_space = 1;
2730 if (curenv->get_fill())
2731 trapping_blank_line();
2732 else
2733 curdiv->space(n);
2734 global_diverted_space = 0;
2735 *bolp = 1;
2736 return 1;
2739 int diverted_copy_file_node::reread(int *bolp)
2741 curdiv->copy_file(filename.contents());
2742 *bolp = 1;
2743 return 1;
2746 int word_space_node::reread(int *)
2748 if (unformat) {
2749 for (width_list *w = orig_width; w; w = w->next)
2750 curenv->space(w->width, w->sentence_width);
2751 unformat = 0;
2752 return 1;
2754 return 0;
2757 int unbreakable_space_node::reread(int *)
2759 return 0;
2762 int hmotion_node::reread(int *)
2764 if (unformat && was_tab) {
2765 curenv->handle_tab(0);
2766 unformat = 0;
2767 return 1;
2769 return 0;
2772 void process_input_stack()
2774 int_stack trap_bol_stack;
2775 int bol = 1;
2776 for (;;) {
2777 int suppress_next = 0;
2778 switch (tok.type) {
2779 case token::TOKEN_CHAR:
2781 unsigned char ch = tok.c;
2782 if (bol && !have_input
2783 && (ch == curenv->control_char
2784 || ch == curenv->no_break_control_char)) {
2785 break_flag = ch == curenv->control_char;
2786 // skip tabs as well as spaces here
2787 do {
2788 tok.next();
2789 } while (tok.white_space());
2790 symbol nm = get_name();
2791 #if defined(DEBUGGING)
2792 if (debug_state) {
2793 if (! nm.is_null()) {
2794 if (strcmp(nm.contents(), "test") == 0) {
2795 fprintf(stderr, "found it!\n");
2796 fflush(stderr);
2798 fprintf(stderr, "interpreting [%s]", nm.contents());
2799 if (strcmp(nm.contents(), "di") == 0 && topdiv != curdiv)
2800 fprintf(stderr, " currently in diversion: %s",
2801 curdiv->get_diversion_name());
2802 fprintf(stderr, "\n");
2803 fflush(stderr);
2806 #endif
2807 if (nm.is_null())
2808 skip_line();
2809 else {
2810 interpolate_macro(nm);
2811 #if defined(DEBUGGING)
2812 if (debug_state) {
2813 fprintf(stderr, "finished interpreting [%s] and environment state is\n", nm.contents());
2814 curenv->dump_troff_state();
2816 #endif
2818 suppress_next = 1;
2820 else {
2821 if (possibly_handle_first_page_transition())
2823 else {
2824 for (;;) {
2825 #if defined(DEBUGGING)
2826 if (debug_state) {
2827 fprintf(stderr, "found [%c]\n", ch); fflush(stderr);
2829 #endif
2830 curenv->add_char(charset_table[ch]);
2831 tok.next();
2832 if (tok.type != token::TOKEN_CHAR)
2833 break;
2834 ch = tok.c;
2836 suppress_next = 1;
2837 bol = 0;
2840 break;
2842 case token::TOKEN_TRANSPARENT:
2844 if (bol) {
2845 if (possibly_handle_first_page_transition())
2847 else {
2848 int cc;
2849 do {
2850 node *n;
2851 cc = get_copy(&n);
2852 if (cc != EOF) {
2853 if (cc != '\0')
2854 curdiv->transparent_output(transparent_translate(cc));
2855 else
2856 curdiv->transparent_output(n);
2858 } while (cc != '\n' && cc != EOF);
2859 if (cc == EOF)
2860 curdiv->transparent_output('\n');
2863 break;
2865 case token::TOKEN_NEWLINE:
2867 if (bol && !old_have_input
2868 && !curenv->get_prev_line_interrupted())
2869 trapping_blank_line();
2870 else {
2871 curenv->newline();
2872 bol = 1;
2874 break;
2876 case token::TOKEN_REQUEST:
2878 int request_code = tok.c;
2879 tok.next();
2880 switch (request_code) {
2881 case TITLE_REQUEST:
2882 title();
2883 break;
2884 case COPY_FILE_REQUEST:
2885 copy_file();
2886 break;
2887 case TRANSPARENT_FILE_REQUEST:
2888 transparent_file();
2889 break;
2890 #ifdef COLUMN
2891 case VJUSTIFY_REQUEST:
2892 vjustify();
2893 break;
2894 #endif /* COLUMN */
2895 default:
2896 assert(0);
2897 break;
2899 suppress_next = 1;
2900 break;
2902 case token::TOKEN_SPACE:
2904 if (possibly_handle_first_page_transition())
2906 else if (bol && !curenv->get_prev_line_interrupted()) {
2907 int nspaces = 0;
2908 // save space_width now so that it isn't changed by \f or \s
2909 // which we wouldn't notice here
2910 hunits space_width = curenv->get_space_width();
2911 do {
2912 nspaces += tok.nspaces();
2913 tok.next();
2914 } while (tok.space());
2915 if (tok.newline())
2916 trapping_blank_line();
2917 else {
2918 push_token(tok);
2919 curenv->do_break();
2920 curenv->add_node(new hmotion_node(space_width * nspaces,
2921 curenv->get_fill_color()));
2922 bol = 0;
2925 else {
2926 curenv->space();
2927 bol = 0;
2929 break;
2931 case token::TOKEN_EOF:
2932 return;
2933 case token::TOKEN_NODE:
2935 if (possibly_handle_first_page_transition())
2937 else if (tok.nd->reread(&bol)) {
2938 delete tok.nd;
2939 tok.nd = 0;
2941 else {
2942 curenv->add_node(tok.nd);
2943 tok.nd = 0;
2944 bol = 0;
2945 curenv->possibly_break_line(1);
2947 break;
2949 case token::TOKEN_PAGE_EJECTOR:
2951 continue_page_eject();
2952 // I think we just want to preserve bol.
2953 // bol = 1;
2954 break;
2956 case token::TOKEN_BEGIN_TRAP:
2958 trap_bol_stack.push(bol);
2959 bol = 1;
2960 have_input = 0;
2961 break;
2963 case token::TOKEN_END_TRAP:
2965 if (trap_bol_stack.is_empty())
2966 error("spurious end trap token detected!");
2967 else
2968 bol = trap_bol_stack.pop();
2969 have_input = 0;
2971 /* I'm not totally happy about this. But I can't think of any other
2972 way to do it. Doing an output_pending_lines() whenever a
2973 TOKEN_END_TRAP is detected doesn't work: for example,
2975 .wh -1i x
2976 .de x
2979 .wh -.5i y
2980 .de y
2981 .tl ''-%-''
2984 .ll .5i
2985 .sp |\n(.pu-1i-.5v
2986 a\%very\%very\%long\%word
2988 will print all but the first lines from the word immediately
2989 after the footer, rather than on the next page. */
2991 if (trap_bol_stack.is_empty())
2992 curenv->output_pending_lines();
2993 break;
2995 default:
2997 bol = 0;
2998 tok.process();
2999 break;
3002 if (!suppress_next)
3003 tok.next();
3004 trap_sprung_flag = 0;
3008 #ifdef WIDOW_CONTROL
3010 void flush_pending_lines()
3012 while (!tok.newline() && !tok.eof())
3013 tok.next();
3014 curenv->output_pending_lines();
3015 tok.next();
3018 #endif /* WIDOW_CONTROL */
3020 request_or_macro::request_or_macro()
3024 macro *request_or_macro::to_macro()
3026 return 0;
3029 request::request(REQUEST_FUNCP pp) : p(pp)
3033 void request::invoke(symbol, int)
3035 (*p)();
3038 struct char_block {
3039 enum { SIZE = 128 };
3040 unsigned char s[SIZE];
3041 char_block *next;
3042 char_block();
3045 char_block::char_block()
3046 : next(0)
3050 class char_list {
3051 public:
3052 char_list();
3053 ~char_list();
3054 void append(unsigned char);
3055 void set(unsigned char, int);
3056 unsigned char get(int);
3057 int length();
3058 private:
3059 unsigned char *ptr;
3060 int len;
3061 char_block *head;
3062 char_block *tail;
3063 friend class macro_header;
3064 friend class string_iterator;
3067 char_list::char_list()
3068 : ptr(0), len(0), head(0), tail(0)
3072 char_list::~char_list()
3074 while (head != 0) {
3075 char_block *tem = head;
3076 head = head->next;
3077 delete tem;
3081 int char_list::length()
3083 return len;
3086 void char_list::append(unsigned char c)
3088 if (tail == 0) {
3089 head = tail = new char_block;
3090 ptr = tail->s;
3092 else {
3093 if (ptr >= tail->s + char_block::SIZE) {
3094 tail->next = new char_block;
3095 tail = tail->next;
3096 ptr = tail->s;
3099 *ptr++ = c;
3100 len++;
3103 void char_list::set(unsigned char c, int offset)
3105 assert(len > offset);
3106 // optimization for access at the end
3107 int boundary = len - len % char_block::SIZE;
3108 if (offset >= boundary) {
3109 *(tail->s + offset - boundary) = c;
3110 return;
3112 char_block *tem = head;
3113 int l = 0;
3114 for (;;) {
3115 l += char_block::SIZE;
3116 if (l > offset) {
3117 *(tem->s + offset % char_block::SIZE) = c;
3118 return;
3120 tem = tem->next;
3124 unsigned char char_list::get(int offset)
3126 assert(len > offset);
3127 // optimization for access at the end
3128 int boundary = len - len % char_block::SIZE;
3129 if (offset >= boundary)
3130 return *(tail->s + offset - boundary);
3131 char_block *tem = head;
3132 int l = 0;
3133 for (;;) {
3134 l += char_block::SIZE;
3135 if (l > offset)
3136 return *(tem->s + offset % char_block::SIZE);
3137 tem = tem->next;
3141 class node_list {
3142 node *head;
3143 node *tail;
3144 public:
3145 node_list();
3146 ~node_list();
3147 void append(node *);
3148 int length();
3149 node *extract();
3151 friend class macro_header;
3152 friend class string_iterator;
3155 void node_list::append(node *n)
3157 if (head == 0) {
3158 n->next = 0;
3159 head = tail = n;
3161 else {
3162 n->next = 0;
3163 tail = tail->next = n;
3167 int node_list::length()
3169 int total = 0;
3170 for (node *n = head; n != 0; n = n->next)
3171 ++total;
3172 return total;
3175 node_list::node_list()
3177 head = tail = 0;
3180 node *node_list::extract()
3182 node *temp = head;
3183 head = tail = 0;
3184 return temp;
3187 node_list::~node_list()
3189 delete_node_list(head);
3192 class macro_header {
3193 public:
3194 int count;
3195 char_list cl;
3196 node_list nl;
3197 macro_header() { count = 1; }
3198 macro_header *copy(int);
3201 macro::~macro()
3203 if (p != 0 && --(p->count) <= 0)
3204 delete p;
3207 macro::macro()
3208 : is_a_diversion(0), is_a_string(1)
3210 if (!input_stack::get_location(1, &filename, &lineno)) {
3211 filename = 0;
3212 lineno = 0;
3214 len = 0;
3215 empty_macro = 1;
3216 p = 0;
3219 macro::macro(const macro &m)
3220 : filename(m.filename), lineno(m.lineno), len(m.len),
3221 empty_macro(m.empty_macro), is_a_diversion(m.is_a_diversion),
3222 is_a_string(m.is_a_string), p(m.p)
3224 if (p != 0)
3225 p->count++;
3228 macro::macro(int is_div)
3229 : is_a_diversion(is_div)
3231 if (!input_stack::get_location(1, &filename, &lineno)) {
3232 filename = 0;
3233 lineno = 0;
3235 len = 0;
3236 empty_macro = 1;
3237 is_a_string = 1;
3238 p = 0;
3241 int macro::is_diversion()
3243 return is_a_diversion;
3246 int macro::is_string()
3248 return is_a_string;
3251 void macro::clear_string_flag()
3253 is_a_string = 0;
3256 macro &macro::operator=(const macro &m)
3258 // don't assign object
3259 if (m.p != 0)
3260 m.p->count++;
3261 if (p != 0 && --(p->count) <= 0)
3262 delete p;
3263 p = m.p;
3264 filename = m.filename;
3265 lineno = m.lineno;
3266 len = m.len;
3267 empty_macro = m.empty_macro;
3268 is_a_diversion = m.is_a_diversion;
3269 is_a_string = m.is_a_string;
3270 return *this;
3273 void macro::append(unsigned char c)
3275 assert(c != 0);
3276 if (p == 0)
3277 p = new macro_header;
3278 if (p->cl.length() != len) {
3279 macro_header *tem = p->copy(len);
3280 if (--(p->count) <= 0)
3281 delete p;
3282 p = tem;
3284 p->cl.append(c);
3285 ++len;
3286 if (c != PUSH_GROFF_MODE && c != PUSH_COMP_MODE && c != POP_GROFFCOMP_MODE)
3287 empty_macro = 0;
3290 void macro::set(unsigned char c, int offset)
3292 assert(p != 0);
3293 assert(c != 0);
3294 p->cl.set(c, offset);
3297 unsigned char macro::get(int offset)
3299 assert(p != 0);
3300 return p->cl.get(offset);
3303 int macro::length()
3305 return len;
3308 void macro::append_str(const char *s)
3310 int i = 0;
3312 if (s) {
3313 while (s[i] != (char)0) {
3314 append(s[i]);
3315 i++;
3320 void macro::append(node *n)
3322 assert(n != 0);
3323 if (p == 0)
3324 p = new macro_header;
3325 if (p->cl.length() != len) {
3326 macro_header *tem = p->copy(len);
3327 if (--(p->count) <= 0)
3328 delete p;
3329 p = tem;
3331 p->cl.append(0);
3332 p->nl.append(n);
3333 ++len;
3334 empty_macro = 0;
3337 void macro::append_unsigned(unsigned int i)
3339 unsigned int j = i / 10;
3340 if (j != 0)
3341 append_unsigned(j);
3342 append(((unsigned char)(((int)'0') + i % 10)));
3345 void macro::append_int(int i)
3347 if (i < 0) {
3348 append('-');
3349 i = -i;
3351 append_unsigned((unsigned int)i);
3354 void macro::print_size()
3356 errprint("%1", len);
3359 // make a copy of the first n bytes
3361 macro_header *macro_header::copy(int n)
3363 macro_header *p = new macro_header;
3364 char_block *bp = cl.head;
3365 unsigned char *ptr = bp->s;
3366 node *nd = nl.head;
3367 while (--n >= 0) {
3368 if (ptr >= bp->s + char_block::SIZE) {
3369 bp = bp->next;
3370 ptr = bp->s;
3372 unsigned char c = *ptr++;
3373 p->cl.append(c);
3374 if (c == 0) {
3375 p->nl.append(nd->copy());
3376 nd = nd->next;
3379 return p;
3382 void print_macros()
3384 object_dictionary_iterator iter(request_dictionary);
3385 request_or_macro *rm;
3386 symbol s;
3387 while (iter.get(&s, (object **)&rm)) {
3388 assert(!s.is_null());
3389 macro *m = rm->to_macro();
3390 if (m) {
3391 errprint("%1\t", s.contents());
3392 m->print_size();
3393 errprint("\n");
3396 fflush(stderr);
3397 skip_line();
3400 class string_iterator : public input_iterator {
3401 macro mac;
3402 const char *how_invoked;
3403 int newline_flag;
3404 int lineno;
3405 char_block *bp;
3406 int count; // of characters remaining
3407 node *nd;
3408 int saved_compatible_flag;
3409 int with_break; // inherited from the caller
3410 protected:
3411 symbol nm;
3412 string_iterator();
3413 public:
3414 string_iterator(const macro &, const char * = 0, symbol = NULL_SYMBOL);
3415 int fill(node **);
3416 int peek();
3417 int get_location(int, const char **, int *);
3418 void backtrace();
3419 int get_break_flag() { return with_break; }
3420 void save_compatible_flag(int f) { saved_compatible_flag = f; }
3421 int get_compatible_flag() { return saved_compatible_flag; }
3422 int is_diversion();
3425 string_iterator::string_iterator(const macro &m, const char *p, symbol s)
3426 : input_iterator(m.is_a_diversion), mac(m), how_invoked(p), newline_flag(0),
3427 lineno(1), nm(s)
3429 count = mac.len;
3430 if (count != 0) {
3431 bp = mac.p->cl.head;
3432 nd = mac.p->nl.head;
3433 ptr = eptr = bp->s;
3435 else {
3436 bp = 0;
3437 nd = 0;
3438 ptr = eptr = 0;
3440 with_break = input_stack::get_break_flag();
3443 string_iterator::string_iterator()
3445 bp = 0;
3446 nd = 0;
3447 ptr = eptr = 0;
3448 newline_flag = 0;
3449 how_invoked = 0;
3450 lineno = 1;
3451 count = 0;
3452 with_break = input_stack::get_break_flag();
3455 int string_iterator::is_diversion()
3457 return mac.is_diversion();
3460 int string_iterator::fill(node **np)
3462 if (newline_flag)
3463 lineno++;
3464 newline_flag = 0;
3465 if (count <= 0)
3466 return EOF;
3467 const unsigned char *p = eptr;
3468 if (p >= bp->s + char_block::SIZE) {
3469 bp = bp->next;
3470 p = bp->s;
3472 if (*p == '\0') {
3473 if (np) {
3474 *np = nd->copy();
3475 if (is_diversion())
3476 (*np)->div_nest_level = input_stack::get_div_level();
3477 else
3478 (*np)->div_nest_level = 0;
3480 nd = nd->next;
3481 eptr = ptr = p + 1;
3482 count--;
3483 return 0;
3485 const unsigned char *e = bp->s + char_block::SIZE;
3486 if (e - p > count)
3487 e = p + count;
3488 ptr = p;
3489 while (p < e) {
3490 unsigned char c = *p;
3491 if (c == '\n' || c == ESCAPE_NEWLINE) {
3492 newline_flag = 1;
3493 p++;
3494 break;
3496 if (c == '\0')
3497 break;
3498 p++;
3500 eptr = p;
3501 count -= p - ptr;
3502 return *ptr++;
3505 int string_iterator::peek()
3507 if (count <= 0)
3508 return EOF;
3509 const unsigned char *p = eptr;
3510 if (p >= bp->s + char_block::SIZE) {
3511 p = bp->next->s;
3513 return *p;
3516 int string_iterator::get_location(int allow_macro,
3517 const char **filep, int *linep)
3519 if (!allow_macro)
3520 return 0;
3521 if (mac.filename == 0)
3522 return 0;
3523 *filep = mac.filename;
3524 *linep = mac.lineno + lineno - 1;
3525 return 1;
3528 void string_iterator::backtrace()
3530 if (mac.filename) {
3531 errprint("%1:%2: backtrace", mac.filename, mac.lineno + lineno - 1);
3532 if (how_invoked) {
3533 if (!nm.is_null())
3534 errprint(": %1 `%2'\n", how_invoked, nm.contents());
3535 else
3536 errprint(": %1\n", how_invoked);
3538 else
3539 errprint("\n");
3543 class temp_iterator : public input_iterator {
3544 unsigned char *base;
3545 temp_iterator(const char *, int len);
3546 public:
3547 ~temp_iterator();
3548 friend input_iterator *make_temp_iterator(const char *);
3551 #ifdef __GNUG__
3552 inline
3553 #endif
3554 temp_iterator::temp_iterator(const char *s, int len)
3556 base = new unsigned char[len];
3557 memcpy(base, s, len);
3558 ptr = base;
3559 eptr = base + len;
3562 temp_iterator::~temp_iterator()
3564 a_delete base;
3567 class small_temp_iterator : public input_iterator {
3568 private:
3569 small_temp_iterator(const char *, int);
3570 ~small_temp_iterator();
3571 enum { BLOCK = 16 };
3572 static small_temp_iterator *free_list;
3573 void *operator new(size_t);
3574 void operator delete(void *);
3575 enum { SIZE = 12 };
3576 unsigned char buf[SIZE];
3577 friend input_iterator *make_temp_iterator(const char *);
3580 small_temp_iterator *small_temp_iterator::free_list = 0;
3582 void *small_temp_iterator::operator new(size_t n)
3584 assert(n == sizeof(small_temp_iterator));
3585 if (!free_list) {
3586 free_list =
3587 (small_temp_iterator *)new char[sizeof(small_temp_iterator)*BLOCK];
3588 for (int i = 0; i < BLOCK - 1; i++)
3589 free_list[i].next = free_list + i + 1;
3590 free_list[BLOCK-1].next = 0;
3592 small_temp_iterator *p = free_list;
3593 free_list = (small_temp_iterator *)(free_list->next);
3594 p->next = 0;
3595 return p;
3598 #ifdef __GNUG__
3599 inline
3600 #endif
3601 void small_temp_iterator::operator delete(void *p)
3603 if (p) {
3604 ((small_temp_iterator *)p)->next = free_list;
3605 free_list = (small_temp_iterator *)p;
3609 small_temp_iterator::~small_temp_iterator()
3613 #ifdef __GNUG__
3614 inline
3615 #endif
3616 small_temp_iterator::small_temp_iterator(const char *s, int len)
3618 for (int i = 0; i < len; i++)
3619 buf[i] = s[i];
3620 ptr = buf;
3621 eptr = buf + len;
3624 input_iterator *make_temp_iterator(const char *s)
3626 if (s == 0)
3627 return new small_temp_iterator(s, 0);
3628 else {
3629 int n = strlen(s);
3630 if (n <= small_temp_iterator::SIZE)
3631 return new small_temp_iterator(s, n);
3632 else
3633 return new temp_iterator(s, n);
3637 // this is used when macros with arguments are interpolated
3639 struct arg_list {
3640 macro mac;
3641 int space_follows;
3642 arg_list *next;
3643 arg_list(const macro &, int);
3644 arg_list(const arg_list *);
3645 ~arg_list();
3648 arg_list::arg_list(const macro &m, int s) : mac(m), space_follows(s), next(0)
3652 arg_list::arg_list(const arg_list *al)
3653 : next(0)
3655 mac = al->mac;
3656 space_follows = al->space_follows;
3657 arg_list **a = &next;
3658 arg_list *p = al->next;
3659 while (p) {
3660 *a = new arg_list(p->mac, p->space_follows);
3661 p = p->next;
3662 a = &(*a)->next;
3666 arg_list::~arg_list()
3670 class macro_iterator : public string_iterator {
3671 arg_list *args;
3672 int argc;
3673 int with_break; // whether called as .foo or 'foo
3674 public:
3675 macro_iterator(symbol, macro &, const char * = "macro", int = 0);
3676 macro_iterator();
3677 ~macro_iterator();
3678 int has_args() { return 1; }
3679 input_iterator *get_arg(int);
3680 arg_list *get_arg_list();
3681 symbol get_macro_name();
3682 int space_follows_arg(int);
3683 int get_break_flag() { return with_break; }
3684 int nargs() { return argc; }
3685 void add_arg(const macro &, int);
3686 void shift(int);
3687 int is_macro() { return 1; }
3688 int is_diversion();
3691 input_iterator *macro_iterator::get_arg(int i)
3693 if (i == 0)
3694 return make_temp_iterator(nm.contents());
3695 if (i > 0 && i <= argc) {
3696 arg_list *p = args;
3697 for (int j = 1; j < i; j++) {
3698 assert(p != 0);
3699 p = p->next;
3701 return new string_iterator(p->mac);
3703 else
3704 return 0;
3707 arg_list *macro_iterator::get_arg_list()
3709 return args;
3712 symbol macro_iterator::get_macro_name()
3714 return nm;
3717 int macro_iterator::space_follows_arg(int i)
3719 if (i > 0 && i <= argc) {
3720 arg_list *p = args;
3721 for (int j = 1; j < i; j++) {
3722 assert(p != 0);
3723 p = p->next;
3725 return p->space_follows;
3727 else
3728 return 0;
3731 void macro_iterator::add_arg(const macro &m, int s)
3733 arg_list **p;
3734 for (p = &args; *p; p = &((*p)->next))
3736 *p = new arg_list(m, s);
3737 ++argc;
3740 void macro_iterator::shift(int n)
3742 while (n > 0 && argc > 0) {
3743 arg_list *tem = args;
3744 args = args->next;
3745 delete tem;
3746 --argc;
3747 --n;
3751 // This gets used by eg .if '\?xxx\?''.
3753 int operator==(const macro &m1, const macro &m2)
3755 if (m1.len != m2.len)
3756 return 0;
3757 string_iterator iter1(m1);
3758 string_iterator iter2(m2);
3759 int n = m1.len;
3760 while (--n >= 0) {
3761 node *nd1 = 0;
3762 int c1 = iter1.get(&nd1);
3763 assert(c1 != EOF);
3764 node *nd2 = 0;
3765 int c2 = iter2.get(&nd2);
3766 assert(c2 != EOF);
3767 if (c1 != c2) {
3768 if (c1 == 0)
3769 delete nd1;
3770 else if (c2 == 0)
3771 delete nd2;
3772 return 0;
3774 if (c1 == 0) {
3775 assert(nd1 != 0);
3776 assert(nd2 != 0);
3777 int are_same = nd1->type() == nd2->type() && nd1->same(nd2);
3778 delete nd1;
3779 delete nd2;
3780 if (!are_same)
3781 return 0;
3784 return 1;
3787 static void interpolate_macro(symbol nm, int no_next)
3789 request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm);
3790 if (p == 0) {
3791 int warned = 0;
3792 const char *s = nm.contents();
3793 if (strlen(s) > 2) {
3794 request_or_macro *r;
3795 char buf[3];
3796 buf[0] = s[0];
3797 buf[1] = s[1];
3798 buf[2] = '\0';
3799 r = (request_or_macro *)request_dictionary.lookup(symbol(buf));
3800 if (r) {
3801 macro *m = r->to_macro();
3802 if (!m || !m->empty())
3803 warned = warning(WARN_SPACE,
3804 "macro `%1' not defined "
3805 "(possibly missing space after `%2')",
3806 nm.contents(), buf);
3809 if (!warned) {
3810 warning(WARN_MAC, "macro `%1' not defined", nm.contents());
3811 p = new macro;
3812 request_dictionary.define(nm, p);
3815 if (p)
3816 p->invoke(nm, no_next);
3817 else {
3818 skip_line();
3819 return;
3823 static void decode_args(macro_iterator *mi)
3825 if (!tok.newline() && !tok.eof()) {
3826 node *n;
3827 int c = get_copy(&n);
3828 for (;;) {
3829 while (c == ' ')
3830 c = get_copy(&n);
3831 if (c == '\n' || c == EOF)
3832 break;
3833 macro arg;
3834 int quote_input_level = 0;
3835 int done_tab_warning = 0;
3836 arg.append(compatible_flag ? PUSH_COMP_MODE : PUSH_GROFF_MODE);
3837 // we store discarded double quotes for \$^
3838 if (c == '"') {
3839 arg.append(DOUBLE_QUOTE);
3840 quote_input_level = input_stack::get_level();
3841 c = get_copy(&n);
3843 while (c != EOF && c != '\n' && !(c == ' ' && quote_input_level == 0)) {
3844 if (quote_input_level > 0 && c == '"'
3845 && (compatible_flag
3846 || input_stack::get_level() == quote_input_level)) {
3847 arg.append(DOUBLE_QUOTE);
3848 c = get_copy(&n);
3849 if (c == '"') {
3850 arg.append(c);
3851 c = get_copy(&n);
3853 else
3854 break;
3856 else {
3857 if (c == 0)
3858 arg.append(n);
3859 else {
3860 if (c == '\t' && quote_input_level == 0 && !done_tab_warning) {
3861 warning(WARN_TAB, "tab character in unquoted macro argument");
3862 done_tab_warning = 1;
3864 arg.append(c);
3866 c = get_copy(&n);
3869 arg.append(POP_GROFFCOMP_MODE);
3870 mi->add_arg(arg, (c == ' '));
3875 static void decode_string_args(macro_iterator *mi)
3877 node *n;
3878 int c = get_copy(&n);
3879 for (;;) {
3880 while (c == ' ')
3881 c = get_copy(&n);
3882 if (c == '\n' || c == EOF) {
3883 error("missing `]'");
3884 break;
3886 if (c == ']')
3887 break;
3888 macro arg;
3889 int quote_input_level = 0;
3890 int done_tab_warning = 0;
3891 if (c == '"') {
3892 quote_input_level = input_stack::get_level();
3893 c = get_copy(&n);
3895 while (c != EOF && c != '\n'
3896 && !(c == ']' && quote_input_level == 0)
3897 && !(c == ' ' && quote_input_level == 0)) {
3898 if (quote_input_level > 0 && c == '"'
3899 && input_stack::get_level() == quote_input_level) {
3900 c = get_copy(&n);
3901 if (c == '"') {
3902 arg.append(c);
3903 c = get_copy(&n);
3905 else
3906 break;
3908 else {
3909 if (c == 0)
3910 arg.append(n);
3911 else {
3912 if (c == '\t' && quote_input_level == 0 && !done_tab_warning) {
3913 warning(WARN_TAB, "tab character in unquoted string argument");
3914 done_tab_warning = 1;
3916 arg.append(c);
3918 c = get_copy(&n);
3921 mi->add_arg(arg, (c == ' '));
3925 void macro::invoke(symbol nm, int no_next)
3927 macro_iterator *mi = new macro_iterator(nm, *this);
3928 decode_args(mi);
3929 input_stack::push(mi);
3930 // we must delay tok.next() in case the function has been called by
3931 // do_request to assure proper handling of compatible_flag
3932 if (!no_next)
3933 tok.next();
3936 macro *macro::to_macro()
3938 return this;
3941 int macro::empty()
3943 return empty_macro == 1;
3946 macro_iterator::macro_iterator(symbol s, macro &m, const char *how_called,
3947 int init_args)
3948 : string_iterator(m, how_called, s), args(0), argc(0), with_break(break_flag)
3950 if (init_args) {
3951 arg_list *al = input_stack::get_arg_list();
3952 if (al) {
3953 args = new arg_list(al);
3954 argc = input_stack::nargs();
3959 macro_iterator::macro_iterator() : args(0), argc(0), with_break(break_flag)
3963 macro_iterator::~macro_iterator()
3965 while (args != 0) {
3966 arg_list *tem = args;
3967 args = args->next;
3968 delete tem;
3972 dictionary composite_dictionary(17);
3974 void composite_request()
3976 symbol from = get_name(1);
3977 if (!from.is_null()) {
3978 const char *from_gn = glyph_name_to_unicode(from.contents());
3979 if (!from_gn) {
3980 from_gn = check_unicode_name(from.contents());
3981 if (!from_gn) {
3982 error("invalid composite glyph name `%1'", from.contents());
3983 skip_line();
3984 return;
3987 const char *from_decomposed = decompose_unicode(from_gn);
3988 if (from_decomposed)
3989 from_gn = &from_decomposed[1];
3990 symbol to = get_name(1);
3991 if (to.is_null())
3992 composite_dictionary.remove(symbol(from_gn));
3993 else {
3994 const char *to_gn = glyph_name_to_unicode(to.contents());
3995 if (!to_gn) {
3996 to_gn = check_unicode_name(to.contents());
3997 if (!to_gn) {
3998 error("invalid composite glyph name `%1'", to.contents());
3999 skip_line();
4000 return;
4003 const char *to_decomposed = decompose_unicode(to_gn);
4004 if (to_decomposed)
4005 to_gn = &to_decomposed[1];
4006 if (strcmp(from_gn, to_gn) == 0)
4007 composite_dictionary.remove(symbol(from_gn));
4008 else
4009 (void)composite_dictionary.lookup(symbol(from_gn), (void *)to_gn);
4012 skip_line();
4015 static symbol composite_glyph_name(symbol nm)
4017 macro_iterator *mi = new macro_iterator();
4018 decode_string_args(mi);
4019 input_stack::push(mi);
4020 const char *gn = glyph_name_to_unicode(nm.contents());
4021 if (!gn) {
4022 gn = check_unicode_name(nm.contents());
4023 if (!gn) {
4024 error("invalid base glyph `%1' in composite glyph name", nm.contents());
4025 return EMPTY_SYMBOL;
4028 const char *gn_decomposed = decompose_unicode(gn);
4029 string glyph_name(gn_decomposed ? &gn_decomposed[1] : gn);
4030 string gl;
4031 int n = input_stack::nargs();
4032 for (int i = 1; i <= n; i++) {
4033 glyph_name += '_';
4034 input_iterator *p = input_stack::get_arg(i);
4035 gl.clear();
4036 int c;
4037 while ((c = p->get(0)) != EOF)
4038 if (c != DOUBLE_QUOTE)
4039 gl += c;
4040 gl += '\0';
4041 const char *u = glyph_name_to_unicode(gl.contents());
4042 if (!u) {
4043 u = check_unicode_name(gl.contents());
4044 if (!u) {
4045 error("invalid component `%1' in composite glyph name",
4046 gl.contents());
4047 return EMPTY_SYMBOL;
4050 const char *decomposed = decompose_unicode(u);
4051 if (decomposed)
4052 u = &decomposed[1];
4053 void *mapped_composite = composite_dictionary.lookup(symbol(u));
4054 if (mapped_composite)
4055 u = (const char *)mapped_composite;
4056 glyph_name += u;
4058 glyph_name += '\0';
4059 const char *groff_gn = unicode_to_glyph_name(glyph_name.contents());
4060 if (groff_gn)
4061 return symbol(groff_gn);
4062 gl.clear();
4063 gl += 'u';
4064 gl += glyph_name;
4065 return symbol(gl.contents());
4068 int trap_sprung_flag = 0;
4069 int postpone_traps_flag = 0;
4070 symbol postponed_trap;
4072 void spring_trap(symbol nm)
4074 assert(!nm.is_null());
4075 trap_sprung_flag = 1;
4076 if (postpone_traps_flag) {
4077 postponed_trap = nm;
4078 return;
4080 static char buf[2] = { BEGIN_TRAP, 0 };
4081 static char buf2[2] = { END_TRAP, '\0' };
4082 input_stack::push(make_temp_iterator(buf2));
4083 request_or_macro *p = lookup_request(nm);
4084 macro *m = p->to_macro();
4085 if (m)
4086 input_stack::push(new macro_iterator(nm, *m, "trap-invoked macro"));
4087 else
4088 error("you can't invoke a request with a trap");
4089 input_stack::push(make_temp_iterator(buf));
4092 void postpone_traps()
4094 postpone_traps_flag = 1;
4097 int unpostpone_traps()
4099 postpone_traps_flag = 0;
4100 if (!postponed_trap.is_null()) {
4101 spring_trap(postponed_trap);
4102 postponed_trap = NULL_SYMBOL;
4103 return 1;
4105 else
4106 return 0;
4109 void read_request()
4111 macro_iterator *mi = new macro_iterator;
4112 int reading_from_terminal = isatty(fileno(stdin));
4113 int had_prompt = 0;
4114 if (!tok.newline() && !tok.eof()) {
4115 int c = get_copy(0);
4116 while (c == ' ')
4117 c = get_copy(0);
4118 while (c != EOF && c != '\n' && c != ' ') {
4119 if (!invalid_input_char(c)) {
4120 if (reading_from_terminal)
4121 fputc(c, stderr);
4122 had_prompt = 1;
4124 c = get_copy(0);
4126 if (c == ' ') {
4127 tok.make_space();
4128 decode_args(mi);
4131 if (reading_from_terminal) {
4132 fputc(had_prompt ? ':' : '\a', stderr);
4133 fflush(stderr);
4135 input_stack::push(mi);
4136 macro mac;
4137 int nl = 0;
4138 int c;
4139 while ((c = getchar()) != EOF) {
4140 if (invalid_input_char(c))
4141 warning(WARN_INPUT, "invalid input character code %1", int(c));
4142 else {
4143 if (c == '\n') {
4144 if (nl)
4145 break;
4146 else
4147 nl = 1;
4149 else
4150 nl = 0;
4151 mac.append(c);
4154 if (reading_from_terminal)
4155 clearerr(stdin);
4156 input_stack::push(new string_iterator(mac));
4157 tok.next();
4160 enum define_mode { DEFINE_NORMAL, DEFINE_APPEND, DEFINE_IGNORE };
4161 enum calling_mode { CALLING_NORMAL, CALLING_INDIRECT };
4162 enum comp_mode { COMP_IGNORE, COMP_DISABLE, COMP_ENABLE };
4164 void do_define_string(define_mode mode, comp_mode comp)
4166 symbol nm;
4167 node *n = 0; // pacify compiler
4168 int c;
4169 nm = get_name(1);
4170 if (nm.is_null()) {
4171 skip_line();
4172 return;
4174 if (tok.newline())
4175 c = '\n';
4176 else if (tok.tab())
4177 c = '\t';
4178 else if (!tok.space()) {
4179 error("bad string definition");
4180 skip_line();
4181 return;
4183 else
4184 c = get_copy(&n);
4185 while (c == ' ')
4186 c = get_copy(&n);
4187 if (c == '"')
4188 c = get_copy(&n);
4189 macro mac;
4190 request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm);
4191 macro *mm = rm ? rm->to_macro() : 0;
4192 if (mode == DEFINE_APPEND && mm)
4193 mac = *mm;
4194 if (comp == COMP_DISABLE)
4195 mac.append(PUSH_GROFF_MODE);
4196 else if (comp == COMP_ENABLE)
4197 mac.append(PUSH_COMP_MODE);
4198 while (c != '\n' && c != EOF) {
4199 if (c == 0)
4200 mac.append(n);
4201 else
4202 mac.append((unsigned char)c);
4203 c = get_copy(&n);
4205 if (comp == COMP_DISABLE || comp == COMP_ENABLE)
4206 mac.append(POP_GROFFCOMP_MODE);
4207 if (!mm) {
4208 mm = new macro;
4209 request_dictionary.define(nm, mm);
4211 *mm = mac;
4212 tok.next();
4215 void define_string()
4217 do_define_string(DEFINE_NORMAL,
4218 compatible_flag ? COMP_ENABLE: COMP_IGNORE);
4221 void define_nocomp_string()
4223 do_define_string(DEFINE_NORMAL, COMP_DISABLE);
4226 void append_string()
4228 do_define_string(DEFINE_APPEND,
4229 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4232 void append_nocomp_string()
4234 do_define_string(DEFINE_APPEND, COMP_DISABLE);
4237 void do_define_character(char_mode mode, const char *font_name)
4239 node *n = 0; // pacify compiler
4240 int c;
4241 tok.skip();
4242 charinfo *ci = tok.get_char(1);
4243 if (ci == 0) {
4244 skip_line();
4245 return;
4247 if (font_name) {
4248 string s(font_name);
4249 s += ' ';
4250 s += ci->nm.contents();
4251 s += '\0';
4252 ci = get_charinfo(symbol(s.contents()));
4254 tok.next();
4255 if (tok.newline())
4256 c = '\n';
4257 else if (tok.tab())
4258 c = '\t';
4259 else if (!tok.space()) {
4260 error("bad character definition");
4261 skip_line();
4262 return;
4264 else
4265 c = get_copy(&n);
4266 while (c == ' ' || c == '\t')
4267 c = get_copy(&n);
4268 if (c == '"')
4269 c = get_copy(&n);
4270 macro *m = new macro;
4271 while (c != '\n' && c != EOF) {
4272 if (c == 0)
4273 m->append(n);
4274 else
4275 m->append((unsigned char)c);
4276 c = get_copy(&n);
4278 m = ci->setx_macro(m, mode);
4279 if (m)
4280 delete m;
4281 tok.next();
4284 void define_character()
4286 do_define_character(CHAR_NORMAL);
4289 void define_fallback_character()
4291 do_define_character(CHAR_FALLBACK);
4294 void define_special_character()
4296 do_define_character(CHAR_SPECIAL);
4299 static void remove_character()
4301 tok.skip();
4302 while (!tok.newline() && !tok.eof()) {
4303 if (!tok.space() && !tok.tab()) {
4304 charinfo *ci = tok.get_char(1);
4305 if (!ci)
4306 break;
4307 macro *m = ci->set_macro(0);
4308 if (m)
4309 delete m;
4311 tok.next();
4313 skip_line();
4316 static void interpolate_string(symbol nm)
4318 request_or_macro *p = lookup_request(nm);
4319 macro *m = p->to_macro();
4320 if (!m)
4321 error("you can only invoke a string or macro using \\*");
4322 else {
4323 if (m->is_string()) {
4324 string_iterator *si = new string_iterator(*m, "string", nm);
4325 input_stack::push(si);
4327 else {
4328 // if a macro is called as a string, \$0 doesn't get changed
4329 macro_iterator *mi = new macro_iterator(input_stack::get_macro_name(),
4330 *m, "string", 1);
4331 input_stack::push(mi);
4336 static void interpolate_string_with_args(symbol s)
4338 request_or_macro *p = lookup_request(s);
4339 macro *m = p->to_macro();
4340 if (!m)
4341 error("you can only invoke a string or macro using \\*");
4342 else {
4343 macro_iterator *mi = new macro_iterator(s, *m);
4344 decode_string_args(mi);
4345 input_stack::push(mi);
4349 static void interpolate_arg(symbol nm)
4351 const char *s = nm.contents();
4352 if (!s || *s == '\0')
4353 copy_mode_error("missing argument name");
4354 else if (s[1] == 0 && csdigit(s[0]))
4355 input_stack::push(input_stack::get_arg(s[0] - '0'));
4356 else if (s[0] == '*' && s[1] == '\0') {
4357 int limit = input_stack::nargs();
4358 string args;
4359 for (int i = 1; i <= limit; i++) {
4360 input_iterator *p = input_stack::get_arg(i);
4361 int c;
4362 while ((c = p->get(0)) != EOF)
4363 if (c != DOUBLE_QUOTE)
4364 args += c;
4365 if (i != limit)
4366 args += ' ';
4368 if (limit > 0) {
4369 args += '\0';
4370 input_stack::push(make_temp_iterator(args.contents()));
4373 else if (s[0] == '@' && s[1] == '\0') {
4374 int limit = input_stack::nargs();
4375 string args;
4376 for (int i = 1; i <= limit; i++) {
4377 args += '"';
4378 args += char(BEGIN_QUOTE);
4379 input_iterator *p = input_stack::get_arg(i);
4380 int c;
4381 while ((c = p->get(0)) != EOF)
4382 if (c != DOUBLE_QUOTE)
4383 args += c;
4384 args += char(END_QUOTE);
4385 args += '"';
4386 if (i != limit)
4387 args += ' ';
4389 if (limit > 0) {
4390 args += '\0';
4391 input_stack::push(make_temp_iterator(args.contents()));
4394 else if (s[0] == '^' && s[1] == '\0') {
4395 int limit = input_stack::nargs();
4396 string args;
4397 int c = input_stack::peek();
4398 for (int i = 1; i <= limit; i++) {
4399 input_iterator *p = input_stack::get_arg(i);
4400 while ((c = p->get(0)) != EOF) {
4401 if (c == DOUBLE_QUOTE)
4402 c = '"';
4403 args += c;
4405 if (input_stack::space_follows_arg(i))
4406 args += ' ';
4408 if (limit > 0) {
4409 args += '\0';
4410 input_stack::push(make_temp_iterator(args.contents()));
4413 else {
4414 const char *p;
4415 for (p = s; *p && csdigit(*p); p++)
4417 if (*p)
4418 copy_mode_error("bad argument name `%1'", s);
4419 else
4420 input_stack::push(input_stack::get_arg(atoi(s)));
4424 void handle_first_page_transition()
4426 push_token(tok);
4427 topdiv->begin_page();
4430 // We push back a token by wrapping it up in a token_node, and
4431 // wrapping that up in a string_iterator.
4433 static void push_token(const token &t)
4435 macro m;
4436 m.append(new token_node(t));
4437 input_stack::push(new string_iterator(m));
4440 void push_page_ejector()
4442 static char buf[2] = { PAGE_EJECTOR, '\0' };
4443 input_stack::push(make_temp_iterator(buf));
4446 void handle_initial_request(unsigned char code)
4448 char buf[2];
4449 buf[0] = code;
4450 buf[1] = '\0';
4451 macro mac;
4452 mac.append(new token_node(tok));
4453 input_stack::push(new string_iterator(mac));
4454 input_stack::push(make_temp_iterator(buf));
4455 topdiv->begin_page();
4456 tok.next();
4459 void handle_initial_title()
4461 handle_initial_request(TITLE_REQUEST);
4464 // this should be local to define_macro, but cfront 1.2 doesn't support that
4465 static symbol dot_symbol(".");
4467 void do_define_macro(define_mode mode, calling_mode calling, comp_mode comp)
4469 symbol nm, term;
4470 if (calling == CALLING_INDIRECT) {
4471 symbol temp1 = get_name(1);
4472 if (temp1.is_null()) {
4473 skip_line();
4474 return;
4476 symbol temp2 = get_name();
4477 input_stack::push(make_temp_iterator("\n"));
4478 if (!temp2.is_null()) {
4479 interpolate_string(temp2);
4480 input_stack::push(make_temp_iterator(" "));
4482 interpolate_string(temp1);
4483 input_stack::push(make_temp_iterator(" "));
4484 tok.next();
4486 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4487 nm = get_name(1);
4488 if (nm.is_null()) {
4489 skip_line();
4490 return;
4493 term = get_name(); // the request that terminates the definition
4494 if (term.is_null())
4495 term = dot_symbol;
4496 while (!tok.newline() && !tok.eof())
4497 tok.next();
4498 const char *start_filename;
4499 int start_lineno;
4500 int have_start_location = input_stack::get_location(0, &start_filename,
4501 &start_lineno);
4502 node *n;
4503 // doing this here makes the line numbers come out right
4504 int c = get_copy(&n, 1);
4505 macro mac;
4506 macro *mm = 0;
4507 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4508 request_or_macro *rm =
4509 (request_or_macro *)request_dictionary.lookup(nm);
4510 if (rm)
4511 mm = rm->to_macro();
4512 if (mm && mode == DEFINE_APPEND)
4513 mac = *mm;
4515 int bol = 1;
4516 if (comp == COMP_DISABLE)
4517 mac.append(PUSH_GROFF_MODE);
4518 else if (comp == COMP_ENABLE)
4519 mac.append(PUSH_COMP_MODE);
4520 for (;;) {
4521 if (c == '\n')
4522 mac.clear_string_flag();
4523 while (c == ESCAPE_NEWLINE) {
4524 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND)
4525 mac.append(c);
4526 c = get_copy(&n, 1);
4528 if (bol && c == '.') {
4529 const char *s = term.contents();
4530 int d = 0;
4531 // see if it matches term
4532 int i = 0;
4533 if (s[0] != 0) {
4534 while ((d = get_copy(&n)) == ' ' || d == '\t')
4536 if ((unsigned char)s[0] == d) {
4537 for (i = 1; s[i] != 0; i++) {
4538 d = get_copy(&n);
4539 if ((unsigned char)s[i] != d)
4540 break;
4544 if (s[i] == 0
4545 && ((i == 2 && compatible_flag)
4546 || (d = get_copy(&n)) == ' '
4547 || d == '\n')) { // we found it
4548 if (d == '\n')
4549 tok.make_newline();
4550 else
4551 tok.make_space();
4552 if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) {
4553 if (!mm) {
4554 mm = new macro;
4555 request_dictionary.define(nm, mm);
4557 if (comp == COMP_DISABLE || comp == COMP_ENABLE)
4558 mac.append(POP_GROFFCOMP_MODE);
4559 *mm = mac;
4561 if (term != dot_symbol) {
4562 ignoring = 0;
4563 interpolate_macro(term);
4565 else
4566 skip_line();
4567 return;
4569 if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) {
4570 mac.append(c);
4571 for (int j = 0; j < i; j++)
4572 mac.append(s[j]);
4574 c = d;
4576 if (c == EOF) {
4577 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4578 if (have_start_location)
4579 error_with_file_and_line(start_filename, start_lineno,
4580 "end of file while defining macro `%1'",
4581 nm.contents());
4582 else
4583 error("end of file while defining macro `%1'", nm.contents());
4585 else {
4586 if (have_start_location)
4587 error_with_file_and_line(start_filename, start_lineno,
4588 "end of file while ignoring input lines");
4589 else
4590 error("end of file while ignoring input lines");
4592 tok.next();
4593 return;
4595 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4596 if (c == 0)
4597 mac.append(n);
4598 else
4599 mac.append(c);
4601 bol = (c == '\n');
4602 c = get_copy(&n, 1);
4606 void define_macro()
4608 do_define_macro(DEFINE_NORMAL, CALLING_NORMAL,
4609 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4612 void define_nocomp_macro()
4614 do_define_macro(DEFINE_NORMAL, CALLING_NORMAL, COMP_DISABLE);
4617 void define_indirect_macro()
4619 do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT,
4620 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4623 void define_indirect_nocomp_macro()
4625 do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT, COMP_DISABLE);
4628 void append_macro()
4630 do_define_macro(DEFINE_APPEND, CALLING_NORMAL,
4631 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4634 void append_nocomp_macro()
4636 do_define_macro(DEFINE_APPEND, CALLING_NORMAL, COMP_DISABLE);
4639 void append_indirect_macro()
4641 do_define_macro(DEFINE_APPEND, CALLING_INDIRECT,
4642 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4645 void append_indirect_nocomp_macro()
4647 do_define_macro(DEFINE_APPEND, CALLING_INDIRECT, COMP_DISABLE);
4650 void ignore()
4652 ignoring = 1;
4653 do_define_macro(DEFINE_IGNORE, CALLING_NORMAL, COMP_IGNORE);
4654 ignoring = 0;
4657 void remove_macro()
4659 for (;;) {
4660 symbol s = get_name();
4661 if (s.is_null())
4662 break;
4663 request_dictionary.remove(s);
4665 skip_line();
4668 void rename_macro()
4670 symbol s1 = get_name(1);
4671 if (!s1.is_null()) {
4672 symbol s2 = get_name(1);
4673 if (!s2.is_null())
4674 request_dictionary.rename(s1, s2);
4676 skip_line();
4679 void alias_macro()
4681 symbol s1 = get_name(1);
4682 if (!s1.is_null()) {
4683 symbol s2 = get_name(1);
4684 if (!s2.is_null()) {
4685 if (!request_dictionary.alias(s1, s2))
4686 warning(WARN_MAC, "macro `%1' not defined", s2.contents());
4689 skip_line();
4692 void chop_macro()
4694 symbol s = get_name(1);
4695 if (!s.is_null()) {
4696 request_or_macro *p = lookup_request(s);
4697 macro *m = p->to_macro();
4698 if (!m)
4699 error("cannot chop request");
4700 else if (m->empty())
4701 error("cannot chop empty macro");
4702 else {
4703 int have_restore = 0;
4704 // we have to check for additional save/restore pairs which could be
4705 // there due to empty am1 requests.
4706 for (;;) {
4707 if (m->get(m->len - 1) != POP_GROFFCOMP_MODE)
4708 break;
4709 have_restore = 1;
4710 m->len -= 1;
4711 if (m->get(m->len - 1) != PUSH_GROFF_MODE
4712 && m->get(m->len - 1) != PUSH_COMP_MODE)
4713 break;
4714 have_restore = 0;
4715 m->len -= 1;
4716 if (m->len == 0)
4717 break;
4719 if (m->len == 0)
4720 error("cannot chop empty macro");
4721 else {
4722 if (have_restore)
4723 m->set(POP_GROFFCOMP_MODE, m->len - 1);
4724 else
4725 m->len -= 1;
4729 skip_line();
4732 void substring_request()
4734 int start; // 0, 1, ..., n-1 or -1, -2, ...
4735 symbol s = get_name(1);
4736 if (!s.is_null() && get_integer(&start)) {
4737 request_or_macro *p = lookup_request(s);
4738 macro *m = p->to_macro();
4739 if (!m)
4740 error("cannot apply `substring' on a request");
4741 else {
4742 int end = -1;
4743 if (!has_arg() || get_integer(&end)) {
4744 int real_length = 0; // 1, 2, ..., n
4745 string_iterator iter1(*m);
4746 for (int l = 0; l < m->len; l++) {
4747 int c = iter1.get(0);
4748 if (c == PUSH_GROFF_MODE
4749 || c == PUSH_COMP_MODE
4750 || c == POP_GROFFCOMP_MODE)
4751 continue;
4752 if (c == EOF)
4753 break;
4754 real_length++;
4756 if (start < 0)
4757 start += real_length;
4758 if (end < 0)
4759 end += real_length;
4760 if (start > end) {
4761 int tem = start;
4762 start = end;
4763 end = tem;
4765 if (start >= real_length || end < 0) {
4766 warning(WARN_RANGE,
4767 "start and end index of substring out of range");
4768 m->len = 0;
4769 if (m->p) {
4770 if (--(m->p->count) <= 0)
4771 delete m->p;
4772 m->p = 0;
4774 skip_line();
4775 return;
4777 if (start < 0) {
4778 warning(WARN_RANGE,
4779 "start index of substring out of range, set to 0");
4780 start = 0;
4782 if (end >= real_length) {
4783 warning(WARN_RANGE,
4784 "end index of substring out of range, set to string length");
4785 end = real_length - 1;
4787 // now extract the substring
4788 string_iterator iter(*m);
4789 int i;
4790 for (i = 0; i < start; i++) {
4791 int c = iter.get(0);
4792 while (c == PUSH_GROFF_MODE
4793 || c == PUSH_COMP_MODE
4794 || c == POP_GROFFCOMP_MODE)
4795 c = iter.get(0);
4796 if (c == EOF)
4797 break;
4799 macro mac;
4800 for (; i <= end; i++) {
4801 node *nd = 0; // pacify compiler
4802 int c = iter.get(&nd);
4803 while (c == PUSH_GROFF_MODE
4804 || c == PUSH_COMP_MODE
4805 || c == POP_GROFFCOMP_MODE)
4806 c = iter.get(0);
4807 if (c == EOF)
4808 break;
4809 if (c == 0)
4810 mac.append(nd);
4811 else
4812 mac.append((unsigned char)c);
4814 *m = mac;
4818 skip_line();
4821 void length_request()
4823 symbol ret;
4824 ret = get_name(1);
4825 if (ret.is_null()) {
4826 skip_line();
4827 return;
4829 int c;
4830 node *n;
4831 if (tok.newline())
4832 c = '\n';
4833 else if (tok.tab())
4834 c = '\t';
4835 else if (!tok.space()) {
4836 error("bad string definition");
4837 skip_line();
4838 return;
4840 else
4841 c = get_copy(&n);
4842 while (c == ' ')
4843 c = get_copy(&n);
4844 if (c == '"')
4845 c = get_copy(&n);
4846 int len = 0;
4847 while (c != '\n' && c != EOF) {
4848 ++len;
4849 c = get_copy(&n);
4851 reg *r = (reg*)number_reg_dictionary.lookup(ret);
4852 if (r)
4853 r->set_value(len);
4854 else
4855 set_number_reg(ret, len);
4856 tok.next();
4859 void asciify_macro()
4861 symbol s = get_name(1);
4862 if (!s.is_null()) {
4863 request_or_macro *p = lookup_request(s);
4864 macro *m = p->to_macro();
4865 if (!m)
4866 error("cannot asciify request");
4867 else {
4868 macro am;
4869 string_iterator iter(*m);
4870 for (;;) {
4871 node *nd = 0; // pacify compiler
4872 int c = iter.get(&nd);
4873 if (c == EOF)
4874 break;
4875 if (c != 0)
4876 am.append(c);
4877 else
4878 nd->asciify(&am);
4880 *m = am;
4883 skip_line();
4886 void unformat_macro()
4888 symbol s = get_name(1);
4889 if (!s.is_null()) {
4890 request_or_macro *p = lookup_request(s);
4891 macro *m = p->to_macro();
4892 if (!m)
4893 error("cannot unformat request");
4894 else {
4895 macro am;
4896 string_iterator iter(*m);
4897 for (;;) {
4898 node *nd = 0; // pacify compiler
4899 int c = iter.get(&nd);
4900 if (c == EOF)
4901 break;
4902 if (c != 0)
4903 am.append(c);
4904 else {
4905 if (nd->set_unformat_flag())
4906 am.append(nd);
4909 *m = am;
4912 skip_line();
4915 static void interpolate_environment_variable(symbol nm)
4917 const char *s = getenv(nm.contents());
4918 if (s && *s)
4919 input_stack::push(make_temp_iterator(s));
4922 void interpolate_number_reg(symbol nm, int inc)
4924 reg *r = lookup_number_reg(nm);
4925 if (inc < 0)
4926 r->decrement();
4927 else if (inc > 0)
4928 r->increment();
4929 input_stack::push(make_temp_iterator(r->get_string()));
4932 static void interpolate_number_format(symbol nm)
4934 reg *r = (reg *)number_reg_dictionary.lookup(nm);
4935 if (r)
4936 input_stack::push(make_temp_iterator(r->get_format()));
4939 static int get_delim_number(units *n, unsigned char si, int prev_value)
4941 token start;
4942 start.next();
4943 if (start.delimiter(1)) {
4944 tok.next();
4945 if (get_number(n, si, prev_value)) {
4946 if (start != tok)
4947 warning(WARN_DELIM, "closing delimiter does not match");
4948 return 1;
4951 return 0;
4954 static int get_delim_number(units *n, unsigned char si)
4956 token start;
4957 start.next();
4958 if (start.delimiter(1)) {
4959 tok.next();
4960 if (get_number(n, si)) {
4961 if (start != tok)
4962 warning(WARN_DELIM, "closing delimiter does not match");
4963 return 1;
4966 return 0;
4969 static int get_line_arg(units *n, unsigned char si, charinfo **cp)
4971 token start;
4972 start.next();
4973 int start_level = input_stack::get_level();
4974 if (!start.delimiter(1))
4975 return 0;
4976 tok.next();
4977 if (get_number(n, si)) {
4978 if (tok.dummy() || tok.transparent_dummy())
4979 tok.next();
4980 if (!(start == tok && input_stack::get_level() == start_level)) {
4981 *cp = tok.get_char(1);
4982 tok.next();
4984 if (!(start == tok && input_stack::get_level() == start_level))
4985 warning(WARN_DELIM, "closing delimiter does not match");
4986 return 1;
4988 return 0;
4991 static int read_size(int *x)
4993 tok.next();
4994 int c = tok.ch();
4995 int inc = 0;
4996 if (c == '-') {
4997 inc = -1;
4998 tok.next();
4999 c = tok.ch();
5001 else if (c == '+') {
5002 inc = 1;
5003 tok.next();
5004 c = tok.ch();
5006 int val = 0; // pacify compiler
5007 int bad = 0;
5008 if (c == '(') {
5009 tok.next();
5010 c = tok.ch();
5011 if (!inc) {
5012 // allow an increment either before or after the left parenthesis
5013 if (c == '-') {
5014 inc = -1;
5015 tok.next();
5016 c = tok.ch();
5018 else if (c == '+') {
5019 inc = 1;
5020 tok.next();
5021 c = tok.ch();
5024 if (!csdigit(c))
5025 bad = 1;
5026 else {
5027 val = c - '0';
5028 tok.next();
5029 c = tok.ch();
5030 if (!csdigit(c))
5031 bad = 1;
5032 else {
5033 val = val*10 + (c - '0');
5034 val *= sizescale;
5038 else if (csdigit(c)) {
5039 val = c - '0';
5040 if (!inc && c != '0' && c < '4') {
5041 tok.next();
5042 c = tok.ch();
5043 if (!csdigit(c))
5044 bad = 1;
5045 else
5046 val = val*10 + (c - '0');
5048 val *= sizescale;
5050 else if (!tok.delimiter(1))
5051 return 0;
5052 else {
5053 token start(tok);
5054 tok.next();
5055 c = tok.ch();
5056 if (!inc && (c == '-' || c == '+')) {
5057 inc = c == '+' ? 1 : -1;
5058 tok.next();
5060 if (!get_number(&val, 'z'))
5061 return 0;
5062 if (!(start.ch() == '[' && tok.ch() == ']') && start != tok) {
5063 if (start.ch() == '[')
5064 error("missing `]'");
5065 else
5066 error("missing closing delimiter");
5067 return 0;
5070 if (!bad) {
5071 switch (inc) {
5072 case 0:
5073 if (val == 0) {
5074 // special case -- \s[0] and \s0 means to revert to previous size
5075 *x = 0;
5076 return 1;
5078 *x = val;
5079 break;
5080 case 1:
5081 *x = curenv->get_requested_point_size() + val;
5082 break;
5083 case -1:
5084 *x = curenv->get_requested_point_size() - val;
5085 break;
5086 default:
5087 assert(0);
5089 if (*x <= 0) {
5090 warning(WARN_RANGE,
5091 "\\s escape results in non-positive point size; set to 1");
5092 *x = 1;
5094 return 1;
5096 else {
5097 error("bad digit in point size");
5098 return 0;
5102 static symbol get_delim_name()
5104 token start;
5105 start.next();
5106 if (start.eof()) {
5107 error("end of input at start of delimited name");
5108 return NULL_SYMBOL;
5110 if (start.newline()) {
5111 error("can't delimit name with a newline");
5112 return NULL_SYMBOL;
5114 int start_level = input_stack::get_level();
5115 char abuf[ABUF_SIZE];
5116 char *buf = abuf;
5117 int buf_size = ABUF_SIZE;
5118 int i = 0;
5119 for (;;) {
5120 if (i + 1 > buf_size) {
5121 if (buf == abuf) {
5122 buf = new char[ABUF_SIZE*2];
5123 memcpy(buf, abuf, buf_size);
5124 buf_size = ABUF_SIZE*2;
5126 else {
5127 char *old_buf = buf;
5128 buf = new char[buf_size*2];
5129 memcpy(buf, old_buf, buf_size);
5130 buf_size *= 2;
5131 a_delete old_buf;
5134 tok.next();
5135 if (tok == start
5136 && (compatible_flag || input_stack::get_level() == start_level))
5137 break;
5138 if ((buf[i] = tok.ch()) == 0) {
5139 error("missing delimiter (got %1)", tok.description());
5140 if (buf != abuf)
5141 a_delete buf;
5142 return NULL_SYMBOL;
5144 i++;
5146 buf[i] = '\0';
5147 if (buf == abuf) {
5148 if (i == 0) {
5149 error("empty delimited name");
5150 return NULL_SYMBOL;
5152 else
5153 return symbol(buf);
5155 else {
5156 symbol s(buf);
5157 a_delete buf;
5158 return s;
5162 // Implement \R
5164 static void do_register()
5166 token start;
5167 start.next();
5168 if (!start.delimiter(1))
5169 return;
5170 tok.next();
5171 symbol nm = get_long_name(1);
5172 if (nm.is_null())
5173 return;
5174 while (tok.space())
5175 tok.next();
5176 reg *r = (reg *)number_reg_dictionary.lookup(nm);
5177 int prev_value;
5178 if (!r || !r->get_value(&prev_value))
5179 prev_value = 0;
5180 int val;
5181 if (!get_number(&val, 'u', prev_value))
5182 return;
5183 if (start != tok)
5184 warning(WARN_DELIM, "closing delimiter does not match");
5185 if (r)
5186 r->set_value(val);
5187 else
5188 set_number_reg(nm, val);
5191 // this implements the \w escape sequence
5193 static void do_width()
5195 token start;
5196 start.next();
5197 int start_level = input_stack::get_level();
5198 environment env(curenv);
5199 environment *oldenv = curenv;
5200 curenv = &env;
5201 for (;;) {
5202 tok.next();
5203 if (tok.eof()) {
5204 warning(WARN_DELIM, "missing closing delimiter");
5205 break;
5207 if (tok.newline()) {
5208 warning(WARN_DELIM, "missing closing delimiter");
5209 input_stack::push(make_temp_iterator("\n"));
5210 break;
5212 if (tok == start
5213 && (compatible_flag || input_stack::get_level() == start_level))
5214 break;
5215 tok.process();
5217 env.wrap_up_tab();
5218 units x = env.get_input_line_position().to_units();
5219 input_stack::push(make_temp_iterator(i_to_a(x)));
5220 env.width_registers();
5221 curenv = oldenv;
5222 have_input = 0;
5225 charinfo *page_character;
5227 void set_page_character()
5229 page_character = get_optional_char();
5230 skip_line();
5233 static const symbol percent_symbol("%");
5235 void read_title_parts(node **part, hunits *part_width)
5237 tok.skip();
5238 if (tok.newline() || tok.eof())
5239 return;
5240 token start(tok);
5241 int start_level = input_stack::get_level();
5242 tok.next();
5243 for (int i = 0; i < 3; i++) {
5244 while (!tok.newline() && !tok.eof()) {
5245 if (tok == start
5246 && (compatible_flag || input_stack::get_level() == start_level)) {
5247 tok.next();
5248 break;
5250 if (page_character != 0 && tok.get_char() == page_character)
5251 interpolate_number_reg(percent_symbol, 0);
5252 else
5253 tok.process();
5254 tok.next();
5256 curenv->wrap_up_tab();
5257 part_width[i] = curenv->get_input_line_position();
5258 part[i] = curenv->extract_output_line();
5260 while (!tok.newline() && !tok.eof())
5261 tok.next();
5264 class non_interpreted_node : public node {
5265 macro mac;
5266 public:
5267 non_interpreted_node(const macro &);
5268 int interpret(macro *);
5269 node *copy();
5270 int ends_sentence();
5271 int same(node *);
5272 const char *type();
5273 int force_tprint();
5274 int is_tag();
5277 non_interpreted_node::non_interpreted_node(const macro &m) : mac(m)
5281 int non_interpreted_node::ends_sentence()
5283 return 2;
5286 int non_interpreted_node::same(node *nd)
5288 return mac == ((non_interpreted_node *)nd)->mac;
5291 const char *non_interpreted_node::type()
5293 return "non_interpreted_node";
5296 int non_interpreted_node::force_tprint()
5298 return 0;
5301 int non_interpreted_node::is_tag()
5303 return 0;
5306 node *non_interpreted_node::copy()
5308 return new non_interpreted_node(mac);
5311 int non_interpreted_node::interpret(macro *m)
5313 string_iterator si(mac);
5314 node *n = 0; // pacify compiler
5315 for (;;) {
5316 int c = si.get(&n);
5317 if (c == EOF)
5318 break;
5319 if (c == 0)
5320 m->append(n);
5321 else
5322 m->append(c);
5324 return 1;
5327 static node *do_non_interpreted()
5329 node *n;
5330 int c;
5331 macro mac;
5332 while ((c = get_copy(&n)) != ESCAPE_QUESTION && c != EOF && c != '\n')
5333 if (c == 0)
5334 mac.append(n);
5335 else
5336 mac.append(c);
5337 if (c == EOF || c == '\n') {
5338 error("missing \\?");
5339 return 0;
5341 return new non_interpreted_node(mac);
5344 static void encode_char(macro *mac, char c)
5346 if (c == '\0') {
5347 if ((font::use_charnames_in_special) && tok.special()) {
5348 charinfo *ci = tok.get_char(1);
5349 const char *s = ci->get_symbol()->contents();
5350 if (s[0] != (char)0) {
5351 mac->append('\\');
5352 mac->append('[');
5353 int i = 0;
5354 while (s[i] != (char)0) {
5355 mac->append(s[i]);
5356 i++;
5358 mac->append(']');
5361 else if (tok.stretchable_space()
5362 || tok.unstretchable_space())
5363 mac->append(' ');
5364 else if (!(tok.hyphen_indicator()
5365 || tok.dummy()
5366 || tok.transparent_dummy()
5367 || tok.zero_width_break()))
5368 error("%1 is invalid within \\X", tok.description());
5370 else {
5371 if ((font::use_charnames_in_special) && (c == '\\')) {
5373 * add escape escape sequence
5375 mac->append(c);
5377 mac->append(c);
5381 node *do_special()
5383 token start;
5384 start.next();
5385 int start_level = input_stack::get_level();
5386 macro mac;
5387 for (tok.next();
5388 tok != start || input_stack::get_level() != start_level;
5389 tok.next()) {
5390 if (tok.eof()) {
5391 warning(WARN_DELIM, "missing closing delimiter");
5392 return 0;
5394 if (tok.newline()) {
5395 input_stack::push(make_temp_iterator("\n"));
5396 warning(WARN_DELIM, "missing closing delimiter");
5397 break;
5399 unsigned char c;
5400 if (tok.space())
5401 c = ' ';
5402 else if (tok.tab())
5403 c = '\t';
5404 else if (tok.leader())
5405 c = '\001';
5406 else if (tok.backspace())
5407 c = '\b';
5408 else
5409 c = tok.ch();
5410 encode_char(&mac, c);
5412 return new special_node(mac);
5415 void device_request()
5417 if (!tok.newline() && !tok.eof()) {
5418 int c;
5419 macro mac;
5420 for (;;) {
5421 c = get_copy(0);
5422 if (c == '"') {
5423 c = get_copy(0);
5424 break;
5426 if (c != ' ' && c != '\t')
5427 break;
5429 for (; c != '\n' && c != EOF; c = get_copy(0))
5430 mac.append(c);
5431 curenv->add_node(new special_node(mac));
5433 tok.next();
5436 void device_macro_request()
5438 symbol s = get_name(1);
5439 if (!(s.is_null() || s.is_empty())) {
5440 request_or_macro *p = lookup_request(s);
5441 macro *m = p->to_macro();
5442 if (m)
5443 curenv->add_node(new special_node(*m));
5444 else
5445 error("can't transparently throughput a request");
5447 skip_line();
5450 void output_request()
5452 if (!tok.newline() && !tok.eof()) {
5453 int c;
5454 for (;;) {
5455 c = get_copy(0);
5456 if (c == '"') {
5457 c = get_copy(0);
5458 break;
5460 if (c != ' ' && c != '\t')
5461 break;
5463 for (; c != '\n' && c != EOF; c = get_copy(0))
5464 topdiv->transparent_output(c);
5465 topdiv->transparent_output('\n');
5467 tok.next();
5470 extern int image_no; // from node.cpp
5472 static node *do_suppress(symbol nm)
5474 if (nm.is_null() || nm.is_empty()) {
5475 error("expecting an argument to escape \\O");
5476 return 0;
5478 const char *s = nm.contents();
5479 switch (*s) {
5480 case '0':
5481 if (begin_level == 0)
5482 // suppress generation of glyphs
5483 return new suppress_node(0, 0);
5484 break;
5485 case '1':
5486 if (begin_level == 0)
5487 // enable generation of glyphs
5488 return new suppress_node(1, 0);
5489 break;
5490 case '2':
5491 if (begin_level == 0)
5492 return new suppress_node(1, 1);
5493 break;
5494 case '3':
5495 have_input = 1;
5496 begin_level++;
5497 break;
5498 case '4':
5499 have_input = 1;
5500 begin_level--;
5501 break;
5502 case '5':
5504 s++; // move over '5'
5505 char position = *s;
5506 if (*s == (char)0) {
5507 error("missing position and filename in \\O");
5508 return 0;
5510 if (!(position == 'l'
5511 || position == 'r'
5512 || position == 'c'
5513 || position == 'i')) {
5514 error("l, r, c, or i position expected (got %1 in \\O)", position);
5515 return 0;
5517 s++; // onto image name
5518 if (s == (char *)0) {
5519 error("missing image name for \\O");
5520 return 0;
5522 image_no++;
5523 if (begin_level == 0)
5524 return new suppress_node(symbol(s), position, image_no);
5525 else
5526 have_input = 1;
5528 break;
5529 default:
5530 error("`%1' is an invalid argument to \\O", *s);
5532 return 0;
5535 void special_node::tprint(troff_output_file *out)
5537 tprint_start(out);
5538 string_iterator iter(mac);
5539 for (;;) {
5540 int c = iter.get(0);
5541 if (c == EOF)
5542 break;
5543 for (const char *s = ::asciify(c); *s; s++)
5544 tprint_char(out, *s);
5546 tprint_end(out);
5549 int get_file_line(const char **filename, int *lineno)
5551 return input_stack::get_location(0, filename, lineno);
5554 void line_file()
5556 int n;
5557 if (get_integer(&n)) {
5558 const char *filename = 0;
5559 if (has_arg()) {
5560 symbol s = get_long_name();
5561 filename = s.contents();
5563 (void)input_stack::set_location(filename, n-1);
5565 skip_line();
5568 static int nroff_mode = 0;
5570 static void nroff_request()
5572 nroff_mode = 1;
5573 skip_line();
5576 static void troff_request()
5578 nroff_mode = 0;
5579 skip_line();
5582 static void skip_alternative()
5584 int level = 0;
5585 // ensure that ``.if 0\{'' works as expected
5586 if (tok.left_brace())
5587 level++;
5588 int c;
5589 for (;;) {
5590 c = input_stack::get(0);
5591 if (c == EOF)
5592 break;
5593 if (c == ESCAPE_LEFT_BRACE)
5594 ++level;
5595 else if (c == ESCAPE_RIGHT_BRACE)
5596 --level;
5597 else if (c == escape_char && escape_char > 0)
5598 switch(input_stack::get(0)) {
5599 case '{':
5600 ++level;
5601 break;
5602 case '}':
5603 --level;
5604 break;
5605 case '"':
5606 while ((c = input_stack::get(0)) != '\n' && c != EOF)
5610 Note that the level can properly be < 0, eg
5612 .if 1 \{\
5613 .if 0 \{\
5614 .\}\}
5616 So don't give an error message in this case.
5618 if (level <= 0 && c == '\n')
5619 break;
5621 tok.next();
5624 static void begin_alternative()
5626 while (tok.space() || tok.left_brace())
5627 tok.next();
5630 void nop_request()
5632 while (tok.space())
5633 tok.next();
5636 static int_stack if_else_stack;
5638 int do_if_request()
5640 int invert = 0;
5641 while (tok.space())
5642 tok.next();
5643 while (tok.ch() == '!') {
5644 tok.next();
5645 invert = !invert;
5647 int result;
5648 unsigned char c = tok.ch();
5649 if (c == 't') {
5650 tok.next();
5651 result = !nroff_mode;
5653 else if (c == 'n') {
5654 tok.next();
5655 result = nroff_mode;
5657 else if (c == 'v') {
5658 tok.next();
5659 result = 0;
5661 else if (c == 'o') {
5662 result = (topdiv->get_page_number() & 1);
5663 tok.next();
5665 else if (c == 'e') {
5666 result = !(topdiv->get_page_number() & 1);
5667 tok.next();
5669 else if (c == 'd' || c == 'r') {
5670 tok.next();
5671 symbol nm = get_name(1);
5672 if (nm.is_null()) {
5673 skip_alternative();
5674 return 0;
5676 result = (c == 'd'
5677 ? request_dictionary.lookup(nm) != 0
5678 : number_reg_dictionary.lookup(nm) != 0);
5680 else if (c == 'm') {
5681 tok.next();
5682 symbol nm = get_long_name(1);
5683 if (nm.is_null()) {
5684 skip_alternative();
5685 return 0;
5687 result = (nm == default_symbol
5688 || color_dictionary.lookup(nm) != 0);
5690 else if (c == 'c') {
5691 tok.next();
5692 tok.skip();
5693 charinfo *ci = tok.get_char(1);
5694 if (ci == 0) {
5695 skip_alternative();
5696 return 0;
5698 result = character_exists(ci, curenv);
5699 tok.next();
5701 else if (c == 'F') {
5702 tok.next();
5703 symbol nm = get_long_name(1);
5704 if (nm.is_null()) {
5705 skip_alternative();
5706 return 0;
5708 result = check_font(curenv->get_family()->nm, nm);
5710 else if (c == 'S') {
5711 tok.next();
5712 symbol nm = get_long_name(1);
5713 if (nm.is_null()) {
5714 skip_alternative();
5715 return 0;
5717 result = check_style(nm);
5719 else if (tok.space())
5720 result = 0;
5721 else if (tok.delimiter()) {
5722 token delim = tok;
5723 int delim_level = input_stack::get_level();
5724 environment env1(curenv);
5725 environment env2(curenv);
5726 environment *oldenv = curenv;
5727 curenv = &env1;
5728 suppress_push = 1;
5729 for (int i = 0; i < 2; i++) {
5730 for (;;) {
5731 tok.next();
5732 if (tok.newline() || tok.eof()) {
5733 warning(WARN_DELIM, "missing closing delimiter");
5734 tok.next();
5735 curenv = oldenv;
5736 return 0;
5738 if (tok == delim
5739 && (compatible_flag || input_stack::get_level() == delim_level))
5740 break;
5741 tok.process();
5743 curenv = &env2;
5745 node *n1 = env1.extract_output_line();
5746 node *n2 = env2.extract_output_line();
5747 result = same_node_list(n1, n2);
5748 delete_node_list(n1);
5749 delete_node_list(n2);
5750 curenv = oldenv;
5751 have_input = 0;
5752 suppress_push = 0;
5753 tok.next();
5755 else {
5756 units n;
5757 if (!get_number(&n, 'u')) {
5758 skip_alternative();
5759 return 0;
5761 else
5762 result = n > 0;
5764 if (invert)
5765 result = !result;
5766 if (result)
5767 begin_alternative();
5768 else
5769 skip_alternative();
5770 return result;
5773 void if_else_request()
5775 if_else_stack.push(do_if_request());
5778 void if_request()
5780 do_if_request();
5783 void else_request()
5785 if (if_else_stack.is_empty()) {
5786 warning(WARN_EL, "unbalanced .el request");
5787 skip_alternative();
5789 else {
5790 if (if_else_stack.pop())
5791 skip_alternative();
5792 else
5793 begin_alternative();
5797 static int while_depth = 0;
5798 static int while_break_flag = 0;
5800 void while_request()
5802 macro mac;
5803 int escaped = 0;
5804 int level = 0;
5805 mac.append(new token_node(tok));
5806 for (;;) {
5807 node *n = 0; // pacify compiler
5808 int c = input_stack::get(&n);
5809 if (c == EOF)
5810 break;
5811 if (c == 0) {
5812 escaped = 0;
5813 mac.append(n);
5815 else if (escaped) {
5816 if (c == '{')
5817 level += 1;
5818 else if (c == '}')
5819 level -= 1;
5820 escaped = 0;
5821 mac.append(c);
5823 else {
5824 if (c == ESCAPE_LEFT_BRACE)
5825 level += 1;
5826 else if (c == ESCAPE_RIGHT_BRACE)
5827 level -= 1;
5828 else if (c == escape_char)
5829 escaped = 1;
5830 mac.append(c);
5831 if (c == '\n' && level <= 0)
5832 break;
5835 if (level != 0)
5836 error("unbalanced \\{ \\}");
5837 else {
5838 while_depth++;
5839 input_stack::add_boundary();
5840 for (;;) {
5841 input_stack::push(new string_iterator(mac, "while loop"));
5842 tok.next();
5843 if (!do_if_request()) {
5844 while (input_stack::get(0) != EOF)
5846 break;
5848 process_input_stack();
5849 if (while_break_flag || input_stack::is_return_boundary()) {
5850 while_break_flag = 0;
5851 break;
5854 input_stack::remove_boundary();
5855 while_depth--;
5857 tok.next();
5860 void while_break_request()
5862 if (!while_depth) {
5863 error("no while loop");
5864 skip_line();
5866 else {
5867 while_break_flag = 1;
5868 while (input_stack::get(0) != EOF)
5870 tok.next();
5874 void while_continue_request()
5876 if (!while_depth) {
5877 error("no while loop");
5878 skip_line();
5880 else {
5881 while (input_stack::get(0) != EOF)
5883 tok.next();
5887 // .so
5889 void source()
5891 symbol nm = get_long_name(1);
5892 if (nm.is_null())
5893 skip_line();
5894 else {
5895 while (!tok.newline() && !tok.eof())
5896 tok.next();
5897 file_case *fcp = include_search_path.open_file_cautious(nm.contents(),
5898 fcp->mux_unpack);
5899 if (fcp != NULL)
5900 input_stack::push(new file_iterator(fcp, nm.contents()));
5901 else
5902 error("can't open `%1': %2", nm.contents(), strerror(errno));
5903 tok.next();
5907 // like .so but use popen()
5909 void pipe_source()
5911 if (!unsafe_flag) {
5912 error(".pso request not allowed in safer mode");
5913 skip_line();
5915 else {
5916 #ifdef POPEN_MISSING
5917 error("pipes not available on this system");
5918 skip_line();
5919 #else /* not POPEN_MISSING */
5920 if (tok.newline() || tok.eof())
5921 error("missing command");
5922 else {
5923 int c;
5924 while ((c = get_copy(0)) == ' ' || c == '\t')
5926 int buf_size = 24;
5927 char *buf = new char[buf_size];
5928 int buf_used = 0;
5929 for (; c != '\n' && c != EOF; c = get_copy(0)) {
5930 const char *s = asciify(c);
5931 int slen = strlen(s);
5932 if (buf_used + slen + 1> buf_size) {
5933 char *old_buf = buf;
5934 int old_buf_size = buf_size;
5935 buf_size *= 2;
5936 buf = new char[buf_size];
5937 memcpy(buf, old_buf, old_buf_size);
5938 a_delete old_buf;
5940 strcpy(buf + buf_used, s);
5941 buf_used += slen;
5943 buf[buf_used] = '\0';
5944 errno = 0;
5945 FILE *fp = popen(buf, POPEN_RT);
5946 if (fp != NULL)
5947 input_stack::push(new file_iterator(
5948 new file_case(fp, buf, file_case::fc_pipe | file_case::fc_take_path),
5949 symbol(buf).contents()));
5950 else {
5951 error("can't open pipe to process `%1': %2", buf, strerror(errno));
5952 a_delete buf;
5955 tok.next();
5956 #endif /* not POPEN_MISSING */
5960 // .psbb
5962 static int llx_reg_contents = 0;
5963 static int lly_reg_contents = 0;
5964 static int urx_reg_contents = 0;
5965 static int ury_reg_contents = 0;
5967 struct bounding_box {
5968 int llx, lly, urx, ury;
5971 /* Parse the argument to a %%BoundingBox comment. Return 1 if it
5972 contains 4 numbers, 2 if it contains (atend), 0 otherwise. */
5974 int parse_bounding_box(char *p, bounding_box *bb)
5976 if (sscanf(p, "%d %d %d %d",
5977 &bb->llx, &bb->lly, &bb->urx, &bb->ury) == 4)
5978 return 1;
5979 else {
5980 /* The Document Structuring Conventions say that the numbers
5981 should be integers. Unfortunately some broken applications
5982 get this wrong. */
5983 double x1, x2, x3, x4;
5984 if (sscanf(p, "%lf %lf %lf %lf", &x1, &x2, &x3, &x4) == 4) {
5985 bb->llx = (int)x1;
5986 bb->lly = (int)x2;
5987 bb->urx = (int)x3;
5988 bb->ury = (int)x4;
5989 return 1;
5991 else {
5992 for (; *p == ' ' || *p == '\t'; p++)
5994 if (strncmp(p, "(atend)", 7) == 0) {
5995 return 2;
5999 bb->llx = bb->lly = bb->urx = bb->ury = 0;
6000 return 0;
6003 // This version is taken from psrm.cpp
6005 #define PS_LINE_MAX 255
6006 cset white_space("\n\r \t");
6008 int ps_get_line(char *buf, file_case *fcp, const char* filename)
6010 int c = fcp->get_c();
6011 if (c == EOF) {
6012 buf[0] = '\0';
6013 return 0;
6015 int i = 0;
6016 int err = 0;
6017 while (c != '\r' && c != '\n' && c != EOF) {
6018 if ((c < 0x1b && !white_space(c)) || c == 0x7f)
6019 error("invalid input character code %1 in `%2'", int(c), filename);
6020 else if (i < PS_LINE_MAX)
6021 buf[i++] = c;
6022 else if (!err) {
6023 err = 1;
6024 error("PostScript file `%1' is non-conforming "
6025 "because length of line exceeds 255", filename);
6027 c = fcp->get_c();
6029 buf[i++] = '\n';
6030 buf[i] = '\0';
6031 if (c == '\r') {
6032 c = fcp->get_c();
6033 if (c != EOF && c != '\n')
6034 fcp->unget_c(c);
6036 return 1;
6039 inline void assign_registers(int llx, int lly, int urx, int ury)
6041 llx_reg_contents = llx;
6042 lly_reg_contents = lly;
6043 urx_reg_contents = urx;
6044 ury_reg_contents = ury;
6047 void do_ps_file(file_case *fcp, const char* filename)
6049 bounding_box bb;
6050 int bb_at_end = 0;
6051 char buf[PS_LINE_MAX];
6052 llx_reg_contents = lly_reg_contents =
6053 urx_reg_contents = ury_reg_contents = 0;
6054 if (!ps_get_line(buf, fcp, filename)) {
6055 error("`%1' is empty", filename);
6056 return;
6058 if (strncmp("%!PS-Adobe-", buf, 11) != 0) {
6059 error("`%1' is not conforming to the Document Structuring Conventions",
6060 filename);
6061 return;
6063 while (ps_get_line(buf, fcp, filename) != 0) {
6064 // in header comments, `%X' (`X' any printable character except
6065 // whitespace) is possible too
6066 if (buf[0] == '%') {
6067 if (strncmp(buf + 1, "%EndComments", 12) == 0)
6068 break;
6069 if (white_space(buf[1]))
6070 break;
6072 else
6073 break;
6074 if (strncmp(buf + 1, "%BoundingBox:", 13) == 0) {
6075 int res = parse_bounding_box(buf + 14, &bb);
6076 if (res == 1) {
6077 assign_registers(bb.llx, bb.lly, bb.urx, bb.ury);
6078 return;
6080 else if (res == 2) {
6081 bb_at_end = 1;
6082 break;
6084 else {
6085 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
6086 filename);
6087 return;
6091 if (bb_at_end) {
6092 long offset;
6093 int last_try = 0;
6094 // in the trailer, the last BoundingBox comment is significant
6095 for (offset = 512; !last_try; offset *= 2) {
6096 int had_trailer = 0;
6097 int got_bb = 0;
6098 if (offset > 32768 || fcp->seek(-offset, fcp->seek_end) == -1) {
6099 last_try = 1;
6100 if (fcp->seek(0L, fcp->seek_set) == -1)
6101 break;
6103 while (ps_get_line(buf, fcp, filename) != 0) {
6104 if (buf[0] == '%' && buf[1] == '%') {
6105 if (!had_trailer) {
6106 if (strncmp(buf + 2, "Trailer", 7) == 0)
6107 had_trailer = 1;
6109 else {
6110 if (strncmp(buf + 2, "BoundingBox:", 12) == 0) {
6111 int res = parse_bounding_box(buf + 14, &bb);
6112 if (res == 1)
6113 got_bb = 1;
6114 else if (res == 2) {
6115 error("`(atend)' not allowed in trailer of `%1'", filename);
6116 return;
6118 else {
6119 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
6120 filename);
6121 return;
6127 if (got_bb) {
6128 assign_registers(bb.llx, bb.lly, bb.urx, bb.ury);
6129 return;
6133 error("%%%%BoundingBox comment not found in `%1'", filename);
6136 void ps_bbox_request()
6138 symbol nm = get_long_name(1);
6139 if (nm.is_null())
6140 skip_line();
6141 else {
6142 while (!tok.newline() && !tok.eof())
6143 tok.next();
6144 // PS files might contain non-printable characters, such as ^Z
6145 // and CRs not followed by an LF, so open them in binary mode.
6146 file_case *fcp = include_search_path.open_file_cautious(nm.contents(),
6147 fcp->mux_need_seek | fcp->mux_need_binary);
6148 if (fcp != NULL) {
6149 do_ps_file(fcp, nm.contents());
6150 delete fcp;
6151 } else
6152 error("can't open `%1': %2", nm.contents(), strerror(errno));
6153 tok.next();
6157 const char *asciify(int c)
6159 static char buf[3];
6160 buf[0] = escape_char == '\0' ? '\\' : escape_char;
6161 buf[1] = buf[2] = '\0';
6162 switch (c) {
6163 case ESCAPE_QUESTION:
6164 buf[1] = '?';
6165 break;
6166 case ESCAPE_AMPERSAND:
6167 buf[1] = '&';
6168 break;
6169 case ESCAPE_RIGHT_PARENTHESIS:
6170 buf[1] = ')';
6171 break;
6172 case ESCAPE_UNDERSCORE:
6173 buf[1] = '_';
6174 break;
6175 case ESCAPE_BAR:
6176 buf[1] = '|';
6177 break;
6178 case ESCAPE_CIRCUMFLEX:
6179 buf[1] = '^';
6180 break;
6181 case ESCAPE_LEFT_BRACE:
6182 buf[1] = '{';
6183 break;
6184 case ESCAPE_RIGHT_BRACE:
6185 buf[1] = '}';
6186 break;
6187 case ESCAPE_LEFT_QUOTE:
6188 buf[1] = '`';
6189 break;
6190 case ESCAPE_RIGHT_QUOTE:
6191 buf[1] = '\'';
6192 break;
6193 case ESCAPE_HYPHEN:
6194 buf[1] = '-';
6195 break;
6196 case ESCAPE_BANG:
6197 buf[1] = '!';
6198 break;
6199 case ESCAPE_c:
6200 buf[1] = 'c';
6201 break;
6202 case ESCAPE_e:
6203 buf[1] = 'e';
6204 break;
6205 case ESCAPE_E:
6206 buf[1] = 'E';
6207 break;
6208 case ESCAPE_PERCENT:
6209 buf[1] = '%';
6210 break;
6211 case ESCAPE_SPACE:
6212 buf[1] = ' ';
6213 break;
6214 case ESCAPE_TILDE:
6215 buf[1] = '~';
6216 break;
6217 case ESCAPE_COLON:
6218 buf[1] = ':';
6219 break;
6220 case PUSH_GROFF_MODE:
6221 case PUSH_COMP_MODE:
6222 case POP_GROFFCOMP_MODE:
6223 buf[0] = '\0';
6224 break;
6225 default:
6226 if (invalid_input_char(c))
6227 buf[0] = '\0';
6228 else
6229 buf[0] = c;
6230 break;
6232 return buf;
6235 const char *input_char_description(int c)
6237 switch (c) {
6238 case '\n':
6239 return "a newline character";
6240 case '\b':
6241 return "a backspace character";
6242 case '\001':
6243 return "a leader character";
6244 case '\t':
6245 return "a tab character";
6246 case ' ':
6247 return "a space character";
6248 case '\0':
6249 return "a node";
6251 static char buf[sizeof("magic character code ") + 1 + INT_DIGITS];
6252 if (invalid_input_char(c)) {
6253 const char *s = asciify(c);
6254 if (*s) {
6255 buf[0] = '`';
6256 strcpy(buf + 1, s);
6257 strcat(buf, "'");
6258 return buf;
6260 sprintf(buf, "magic character code %d", c);
6261 return buf;
6263 if (csprint(c)) {
6264 buf[0] = '`';
6265 buf[1] = c;
6266 buf[2] = '\'';
6267 return buf;
6269 sprintf(buf, "character code %d", c);
6270 return buf;
6273 void tag()
6275 if (!tok.newline() && !tok.eof()) {
6276 string s;
6277 int c;
6278 for (;;) {
6279 c = get_copy(0);
6280 if (c == '"') {
6281 c = get_copy(0);
6282 break;
6284 if (c != ' ' && c != '\t')
6285 break;
6287 s = "x X ";
6288 for (; c != '\n' && c != EOF; c = get_copy(0))
6289 s += (char)c;
6290 s += '\n';
6291 curenv->add_node(new tag_node(s, 0));
6293 tok.next();
6296 void taga()
6298 if (!tok.newline() && !tok.eof()) {
6299 string s;
6300 int c;
6301 for (;;) {
6302 c = get_copy(0);
6303 if (c == '"') {
6304 c = get_copy(0);
6305 break;
6307 if (c != ' ' && c != '\t')
6308 break;
6310 s = "x X ";
6311 for (; c != '\n' && c != EOF; c = get_copy(0))
6312 s += (char)c;
6313 s += '\n';
6314 curenv->add_node(new tag_node(s, 1));
6316 tok.next();
6319 // .tm, .tm1, and .tmc
6321 void do_terminal(int newline, int string_like)
6323 if (!tok.newline() && !tok.eof()) {
6324 int c;
6325 for (;;) {
6326 c = get_copy(0);
6327 if (string_like && c == '"') {
6328 c = get_copy(0);
6329 break;
6331 if (c != ' ' && c != '\t')
6332 break;
6334 for (; c != '\n' && c != EOF; c = get_copy(0))
6335 fputs(asciify(c), stderr);
6337 if (newline)
6338 fputc('\n', stderr);
6339 fflush(stderr);
6340 tok.next();
6343 void terminal()
6345 do_terminal(1, 0);
6348 void terminal1()
6350 do_terminal(1, 1);
6353 void terminal_continue()
6355 do_terminal(0, 1);
6358 dictionary stream_dictionary(20);
6360 void do_open(int append)
6362 symbol stream = get_name(1);
6363 if (!stream.is_null()) {
6364 symbol filename = get_long_name(1);
6365 if (!filename.is_null()) {
6366 errno = 0;
6367 FILE *fp = fopen(filename.contents(), append ? "a" : "w");
6368 if (!fp) {
6369 error("can't open `%1' for %2: %3",
6370 filename.contents(),
6371 append ? "appending" : "writing",
6372 strerror(errno));
6373 fp = (FILE *)stream_dictionary.remove(stream);
6375 else
6376 fp = (FILE *)stream_dictionary.lookup(stream, fp);
6377 if (fp)
6378 fclose(fp);
6381 skip_line();
6384 void open_request()
6386 if (!unsafe_flag) {
6387 error(".open request not allowed in safer mode");
6388 skip_line();
6390 else
6391 do_open(0);
6394 void opena_request()
6396 if (!unsafe_flag) {
6397 error(".opena request not allowed in safer mode");
6398 skip_line();
6400 else
6401 do_open(1);
6404 void close_request()
6406 symbol stream = get_name(1);
6407 if (!stream.is_null()) {
6408 FILE *fp = (FILE *)stream_dictionary.remove(stream);
6409 if (!fp)
6410 error("no stream named `%1'", stream.contents());
6411 else
6412 fclose(fp);
6414 skip_line();
6417 // .write and .writec
6419 void do_write_request(int newline)
6421 symbol stream = get_name(1);
6422 if (stream.is_null()) {
6423 skip_line();
6424 return;
6426 FILE *fp = (FILE *)stream_dictionary.lookup(stream);
6427 if (!fp) {
6428 error("no stream named `%1'", stream.contents());
6429 skip_line();
6430 return;
6432 int c;
6433 while ((c = get_copy(0)) == ' ')
6435 if (c == '"')
6436 c = get_copy(0);
6437 for (; c != '\n' && c != EOF; c = get_copy(0))
6438 fputs(asciify(c), fp);
6439 if (newline)
6440 fputc('\n', fp);
6441 fflush(fp);
6442 tok.next();
6445 void write_request()
6447 do_write_request(1);
6450 void write_request_continue()
6452 do_write_request(0);
6455 void write_macro_request()
6457 symbol stream = get_name(1);
6458 if (stream.is_null()) {
6459 skip_line();
6460 return;
6462 FILE *fp = (FILE *)stream_dictionary.lookup(stream);
6463 if (!fp) {
6464 error("no stream named `%1'", stream.contents());
6465 skip_line();
6466 return;
6468 symbol s = get_name(1);
6469 if (s.is_null()) {
6470 skip_line();
6471 return;
6473 request_or_macro *p = lookup_request(s);
6474 macro *m = p->to_macro();
6475 if (!m)
6476 error("cannot write request");
6477 else {
6478 string_iterator iter(*m);
6479 for (;;) {
6480 int c = iter.get(0);
6481 if (c == EOF)
6482 break;
6483 fputs(asciify(c), fp);
6485 fflush(fp);
6487 skip_line();
6490 void warnscale_request()
6492 if (has_arg()) {
6493 char c = tok.ch();
6494 if (c == 'u')
6495 warn_scale = 1.0;
6496 else if (c == 'i')
6497 warn_scale = (double)units_per_inch;
6498 else if (c == 'c')
6499 warn_scale = (double)units_per_inch / 2.54;
6500 else if (c == 'p')
6501 warn_scale = (double)units_per_inch / 72.0;
6502 else if (c == 'P')
6503 warn_scale = (double)units_per_inch / 6.0;
6504 else {
6505 warning(WARN_SCALE,
6506 "invalid scaling indicator `%1', using `i' instead", c);
6507 c = 'i';
6509 warn_scaling_indicator = c;
6511 skip_line();
6514 void spreadwarn_request()
6516 hunits n;
6517 if (has_arg() && get_hunits(&n, 'm')) {
6518 if (n < 0)
6519 n = 0;
6520 hunits em = curenv->get_size();
6521 spread_limit = (double)n.to_units()
6522 / (em.is_zero() ? hresolution : em.to_units());
6524 else
6525 spread_limit = -spread_limit - 1; // no arg toggles on/off without
6526 // changing value; we mirror at
6527 // -0.5 to make zero a valid value
6528 skip_line();
6531 static void init_charset_table()
6533 char buf[16];
6534 strcpy(buf, "char");
6535 for (int i = 0; i < 256; i++) {
6536 strcpy(buf + 4, i_to_a(i));
6537 charset_table[i] = get_charinfo(symbol(buf));
6538 charset_table[i]->set_ascii_code(i);
6539 if (csalpha(i))
6540 charset_table[i]->set_hyphenation_code(cmlower(i));
6542 charset_table['.']->set_flags(charinfo::ENDS_SENTENCE);
6543 charset_table['?']->set_flags(charinfo::ENDS_SENTENCE);
6544 charset_table['!']->set_flags(charinfo::ENDS_SENTENCE);
6545 charset_table['-']->set_flags(charinfo::BREAK_AFTER);
6546 charset_table['"']->set_flags(charinfo::TRANSPARENT);
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 get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT);
6552 get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT);
6553 get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER);
6554 get_charinfo(symbol("hy"))->set_flags(charinfo::BREAK_AFTER);
6555 get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6556 get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6557 get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6558 get_charinfo(symbol("sqrtex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6559 get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6560 get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY);
6561 page_character = charset_table['%'];
6564 static void init_hpf_code_table()
6566 for (int i = 0; i < 256; i++)
6567 hpf_code_table[i] = i;
6570 static void do_translate(int translate_transparent, int translate_input)
6572 tok.skip();
6573 while (!tok.newline() && !tok.eof()) {
6574 if (tok.space()) {
6575 // This is a really bizarre troff feature.
6576 tok.next();
6577 translate_space_to_dummy = tok.dummy();
6578 if (tok.newline() || tok.eof())
6579 break;
6580 tok.next();
6581 continue;
6583 charinfo *ci1 = tok.get_char(1);
6584 if (ci1 == 0)
6585 break;
6586 tok.next();
6587 if (tok.newline() || tok.eof()) {
6588 ci1->set_special_translation(charinfo::TRANSLATE_SPACE,
6589 translate_transparent);
6590 break;
6592 if (tok.space())
6593 ci1->set_special_translation(charinfo::TRANSLATE_SPACE,
6594 translate_transparent);
6595 else if (tok.stretchable_space())
6596 ci1->set_special_translation(charinfo::TRANSLATE_STRETCHABLE_SPACE,
6597 translate_transparent);
6598 else if (tok.dummy())
6599 ci1->set_special_translation(charinfo::TRANSLATE_DUMMY,
6600 translate_transparent);
6601 else if (tok.hyphen_indicator())
6602 ci1->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR,
6603 translate_transparent);
6604 else {
6605 charinfo *ci2 = tok.get_char(1);
6606 if (ci2 == 0)
6607 break;
6608 if (ci1 == ci2)
6609 ci1->set_translation(0, translate_transparent, translate_input);
6610 else
6611 ci1->set_translation(ci2, translate_transparent, translate_input);
6613 tok.next();
6615 skip_line();
6618 void translate()
6620 do_translate(1, 0);
6623 void translate_no_transparent()
6625 do_translate(0, 0);
6628 void translate_input()
6630 do_translate(1, 1);
6633 void char_flags()
6635 int flags;
6636 if (get_integer(&flags))
6637 while (has_arg()) {
6638 charinfo *ci = tok.get_char(1);
6639 if (ci) {
6640 charinfo *tem = ci->get_translation();
6641 if (tem)
6642 ci = tem;
6643 ci->set_flags(flags);
6645 tok.next();
6647 skip_line();
6650 void hyphenation_code()
6652 tok.skip();
6653 while (!tok.newline() && !tok.eof()) {
6654 charinfo *ci = tok.get_char(1);
6655 if (ci == 0)
6656 break;
6657 tok.next();
6658 tok.skip();
6659 unsigned char c = tok.ch();
6660 if (c == 0) {
6661 error("hyphenation code must be ordinary character");
6662 break;
6664 if (csdigit(c)) {
6665 error("hyphenation code cannot be digit");
6666 break;
6668 ci->set_hyphenation_code(c);
6669 if (ci->get_translation()
6670 && ci->get_translation()->get_translation_input())
6671 ci->get_translation()->set_hyphenation_code(c);
6672 tok.next();
6673 tok.skip();
6675 skip_line();
6678 void hyphenation_patterns_file_code()
6680 tok.skip();
6681 while (!tok.newline() && !tok.eof()) {
6682 int n1, n2;
6683 if (get_integer(&n1) && (0 <= n1 && n1 <= 255)) {
6684 if (!has_arg()) {
6685 error("missing output hyphenation code");
6686 break;
6688 if (get_integer(&n2) && (0 <= n2 && n2 <= 255)) {
6689 hpf_code_table[n1] = n2;
6690 tok.skip();
6692 else {
6693 error("output hyphenation code must be integer in the range 0..255");
6694 break;
6697 else {
6698 error("input hyphenation code must be integer in the range 0..255");
6699 break;
6702 skip_line();
6705 charinfo *token::get_char(int required)
6707 if (type == TOKEN_CHAR)
6708 return charset_table[c];
6709 if (type == TOKEN_SPECIAL)
6710 return get_charinfo(nm);
6711 if (type == TOKEN_NUMBERED_CHAR)
6712 return get_charinfo_by_number(val);
6713 if (type == TOKEN_ESCAPE) {
6714 if (escape_char != 0)
6715 return charset_table[escape_char];
6716 else {
6717 error("`\\e' used while no current escape character");
6718 return 0;
6721 if (required) {
6722 if (type == TOKEN_EOF || type == TOKEN_NEWLINE)
6723 warning(WARN_MISSING, "missing normal or special character");
6724 else
6725 error("normal or special character expected (got %1)", description());
6727 return 0;
6730 charinfo *get_optional_char()
6732 while (tok.space())
6733 tok.next();
6734 charinfo *ci = tok.get_char();
6735 if (!ci)
6736 check_missing_character();
6737 else
6738 tok.next();
6739 return ci;
6742 void check_missing_character()
6744 if (!tok.newline() && !tok.eof() && !tok.right_brace() && !tok.tab())
6745 error("normal or special character expected (got %1): "
6746 "treated as missing",
6747 tok.description());
6750 // this is for \Z
6752 int token::add_to_node_list(node **pp)
6754 hunits w;
6755 int s;
6756 node *n = 0;
6757 switch (type) {
6758 case TOKEN_CHAR:
6759 *pp = (*pp)->add_char(charset_table[c], curenv, &w, &s);
6760 break;
6761 case TOKEN_DUMMY:
6762 n = new dummy_node;
6763 break;
6764 case TOKEN_ESCAPE:
6765 if (escape_char != 0)
6766 *pp = (*pp)->add_char(charset_table[escape_char], curenv, &w, &s);
6767 break;
6768 case TOKEN_HYPHEN_INDICATOR:
6769 *pp = (*pp)->add_discretionary_hyphen();
6770 break;
6771 case TOKEN_ITALIC_CORRECTION:
6772 *pp = (*pp)->add_italic_correction(&w);
6773 break;
6774 case TOKEN_LEFT_BRACE:
6775 break;
6776 case TOKEN_MARK_INPUT:
6777 set_number_reg(nm, curenv->get_input_line_position().to_units());
6778 break;
6779 case TOKEN_NODE:
6780 n = nd;
6781 nd = 0;
6782 break;
6783 case TOKEN_NUMBERED_CHAR:
6784 *pp = (*pp)->add_char(get_charinfo_by_number(val), curenv, &w, &s);
6785 break;
6786 case TOKEN_RIGHT_BRACE:
6787 break;
6788 case TOKEN_SPACE:
6789 n = new hmotion_node(curenv->get_space_width(),
6790 curenv->get_fill_color());
6791 break;
6792 case TOKEN_SPECIAL:
6793 *pp = (*pp)->add_char(get_charinfo(nm), curenv, &w, &s);
6794 break;
6795 case TOKEN_STRETCHABLE_SPACE:
6796 n = new unbreakable_space_node(curenv->get_space_width(),
6797 curenv->get_fill_color());
6798 break;
6799 case TOKEN_UNSTRETCHABLE_SPACE:
6800 n = new space_char_hmotion_node(curenv->get_space_width(),
6801 curenv->get_fill_color());
6802 break;
6803 case TOKEN_TRANSPARENT_DUMMY:
6804 n = new transparent_dummy_node;
6805 break;
6806 case TOKEN_ZERO_WIDTH_BREAK:
6807 n = new space_node(H0, curenv->get_fill_color());
6808 n->freeze_space();
6809 n->is_escape_colon();
6810 break;
6811 default:
6812 return 0;
6814 if (n) {
6815 n->next = *pp;
6816 *pp = n;
6818 return 1;
6821 void token::process()
6823 if (possibly_handle_first_page_transition())
6824 return;
6825 switch (type) {
6826 case TOKEN_BACKSPACE:
6827 curenv->add_node(new hmotion_node(-curenv->get_space_width(),
6828 curenv->get_fill_color()));
6829 break;
6830 case TOKEN_CHAR:
6831 curenv->add_char(charset_table[c]);
6832 break;
6833 case TOKEN_DUMMY:
6834 curenv->add_node(new dummy_node);
6835 break;
6836 case TOKEN_EMPTY:
6837 assert(0);
6838 break;
6839 case TOKEN_EOF:
6840 assert(0);
6841 break;
6842 case TOKEN_ESCAPE:
6843 if (escape_char != 0)
6844 curenv->add_char(charset_table[escape_char]);
6845 break;
6846 case TOKEN_BEGIN_TRAP:
6847 case TOKEN_END_TRAP:
6848 case TOKEN_PAGE_EJECTOR:
6849 // these are all handled in process_input_stack()
6850 break;
6851 case TOKEN_HYPHEN_INDICATOR:
6852 curenv->add_hyphen_indicator();
6853 break;
6854 case TOKEN_INTERRUPT:
6855 curenv->interrupt();
6856 break;
6857 case TOKEN_ITALIC_CORRECTION:
6858 curenv->add_italic_correction();
6859 break;
6860 case TOKEN_LEADER:
6861 curenv->handle_tab(1);
6862 break;
6863 case TOKEN_LEFT_BRACE:
6864 break;
6865 case TOKEN_MARK_INPUT:
6866 set_number_reg(nm, curenv->get_input_line_position().to_units());
6867 break;
6868 case TOKEN_NEWLINE:
6869 curenv->newline();
6870 break;
6871 case TOKEN_NODE:
6872 curenv->add_node(nd);
6873 nd = 0;
6874 break;
6875 case TOKEN_NUMBERED_CHAR:
6876 curenv->add_char(get_charinfo_by_number(val));
6877 break;
6878 case TOKEN_REQUEST:
6879 // handled in process_input_stack()
6880 break;
6881 case TOKEN_RIGHT_BRACE:
6882 break;
6883 case TOKEN_SPACE:
6884 curenv->space();
6885 break;
6886 case TOKEN_SPECIAL:
6887 curenv->add_char(get_charinfo(nm));
6888 break;
6889 case TOKEN_SPREAD:
6890 curenv->spread();
6891 break;
6892 case TOKEN_STRETCHABLE_SPACE:
6893 curenv->add_node(new unbreakable_space_node(curenv->get_space_width(),
6894 curenv->get_fill_color()));
6895 break;
6896 case TOKEN_UNSTRETCHABLE_SPACE:
6897 curenv->add_node(new space_char_hmotion_node(curenv->get_space_width(),
6898 curenv->get_fill_color()));
6899 break;
6900 case TOKEN_TAB:
6901 curenv->handle_tab(0);
6902 break;
6903 case TOKEN_TRANSPARENT:
6904 break;
6905 case TOKEN_TRANSPARENT_DUMMY:
6906 curenv->add_node(new transparent_dummy_node);
6907 break;
6908 case TOKEN_ZERO_WIDTH_BREAK:
6910 node *tmp = new space_node(H0, curenv->get_fill_color());
6911 tmp->freeze_space();
6912 tmp->is_escape_colon();
6913 curenv->add_node(tmp);
6914 break;
6916 default:
6917 assert(0);
6921 class nargs_reg : public reg {
6922 public:
6923 const char *get_string();
6926 const char *nargs_reg::get_string()
6928 return i_to_a(input_stack::nargs());
6931 class lineno_reg : public reg {
6932 public:
6933 const char *get_string();
6936 const char *lineno_reg::get_string()
6938 int line;
6939 const char *file;
6940 if (!input_stack::get_location(0, &file, &line))
6941 line = 0;
6942 return i_to_a(line);
6945 class writable_lineno_reg : public general_reg {
6946 public:
6947 writable_lineno_reg();
6948 void set_value(units);
6949 int get_value(units *);
6952 writable_lineno_reg::writable_lineno_reg()
6956 int writable_lineno_reg::get_value(units *res)
6958 int line;
6959 const char *file;
6960 if (!input_stack::get_location(0, &file, &line))
6961 return 0;
6962 *res = line;
6963 return 1;
6966 void writable_lineno_reg::set_value(units n)
6968 input_stack::set_location(0, n);
6971 class filename_reg : public reg {
6972 public:
6973 const char *get_string();
6976 const char *filename_reg::get_string()
6978 int line;
6979 const char *file;
6980 if (input_stack::get_location(0, &file, &line))
6981 return file;
6982 else
6983 return 0;
6986 class break_flag_reg : public reg {
6987 public:
6988 const char *get_string();
6991 const char *break_flag_reg::get_string()
6993 return i_to_a(input_stack::get_break_flag());
6996 class constant_reg : public reg {
6997 const char *s;
6998 public:
6999 constant_reg(const char *);
7000 const char *get_string();
7003 constant_reg::constant_reg(const char *p) : s(p)
7007 const char *constant_reg::get_string()
7009 return s;
7012 constant_int_reg::constant_int_reg(int *q) : p(q)
7016 const char *constant_int_reg::get_string()
7018 return i_to_a(*p);
7021 void abort_request()
7023 int c;
7024 if (tok.eof())
7025 c = EOF;
7026 else if (tok.newline())
7027 c = '\n';
7028 else {
7029 while ((c = get_copy(0)) == ' ')
7032 if (c == EOF || c == '\n')
7033 fputs("User Abort.", stderr);
7034 else {
7035 for (; c != '\n' && c != EOF; c = get_copy(0))
7036 fputs(asciify(c), stderr);
7038 fputc('\n', stderr);
7039 cleanup_and_exit(1);
7042 char *read_string()
7044 int len = 256;
7045 char *s = new char[len];
7046 int c;
7047 while ((c = get_copy(0)) == ' ')
7049 int i = 0;
7050 while (c != '\n' && c != EOF) {
7051 if (!invalid_input_char(c)) {
7052 if (i + 2 > len) {
7053 char *tem = s;
7054 s = new char[len*2];
7055 memcpy(s, tem, len);
7056 len *= 2;
7057 a_delete tem;
7059 s[i++] = c;
7061 c = get_copy(0);
7063 s[i] = '\0';
7064 tok.next();
7065 if (i == 0) {
7066 a_delete s;
7067 return 0;
7069 return s;
7072 void pipe_output()
7074 if (!unsafe_flag) {
7075 error(".pi request not allowed in safer mode");
7076 skip_line();
7078 else {
7079 #ifdef POPEN_MISSING
7080 error("pipes not available on this system");
7081 skip_line();
7082 #else /* not POPEN_MISSING */
7083 if (the_output) {
7084 error("can't pipe: output already started");
7085 skip_line();
7087 else {
7088 char *pc;
7089 if ((pc = read_string()) == 0)
7090 error("can't pipe to empty command");
7091 if (pipe_command) {
7092 char *s = new char[strlen(pipe_command) + strlen(pc) + 1 + 1];
7093 strcpy(s, pipe_command);
7094 strcat(s, "|");
7095 strcat(s, pc);
7096 a_delete pipe_command;
7097 a_delete pc;
7098 pipe_command = s;
7100 else
7101 pipe_command = pc;
7103 #endif /* not POPEN_MISSING */
7107 static int system_status;
7109 void system_request()
7111 if (!unsafe_flag) {
7112 error(".sy request not allowed in safer mode");
7113 skip_line();
7115 else {
7116 char *command = read_string();
7117 if (!command)
7118 error("empty command");
7119 else {
7120 system_status = system(command);
7121 a_delete command;
7126 void copy_file()
7128 if (curdiv == topdiv && topdiv->before_first_page) {
7129 handle_initial_request(COPY_FILE_REQUEST);
7130 return;
7132 symbol filename = get_long_name(1);
7133 while (!tok.newline() && !tok.eof())
7134 tok.next();
7135 if (break_flag)
7136 curenv->do_break();
7137 if (!filename.is_null())
7138 curdiv->copy_file(filename.contents());
7139 tok.next();
7142 #ifdef COLUMN
7144 void vjustify()
7146 if (curdiv == topdiv && topdiv->before_first_page) {
7147 handle_initial_request(VJUSTIFY_REQUEST);
7148 return;
7150 symbol type = get_long_name(1);
7151 if (!type.is_null())
7152 curdiv->vjustify(type);
7153 skip_line();
7156 #endif /* COLUMN */
7158 void transparent_file()
7160 if (curdiv == topdiv && topdiv->before_first_page) {
7161 handle_initial_request(TRANSPARENT_FILE_REQUEST);
7162 return;
7164 symbol filename = get_long_name(1);
7165 while (!tok.newline() && !tok.eof())
7166 tok.next();
7167 if (break_flag)
7168 curenv->do_break();
7169 if (!filename.is_null()) {
7170 file_case *fcp = include_search_path
7171 .open_file_cautious(filename.contents());
7172 if (fcp == NULL)
7173 error("can't open `%1': %2", filename.contents(), strerror(errno));
7174 else {
7175 int bol = 1;
7176 for (;;) {
7177 int c = fcp->get_c();
7178 if (c == EOF)
7179 break;
7180 if (invalid_input_char(c))
7181 warning(WARN_INPUT, "invalid input character code %1", int(c));
7182 else {
7183 curdiv->transparent_output(c);
7184 bol = c == '\n';
7187 if (!bol)
7188 curdiv->transparent_output('\n');
7189 delete fcp;
7192 tok.next();
7195 class page_range {
7196 int first;
7197 int last;
7198 public:
7199 page_range *next;
7200 page_range(int, int, page_range *);
7201 int contains(int n);
7204 page_range::page_range(int i, int j, page_range *p)
7205 : first(i), last(j), next(p)
7209 int page_range::contains(int n)
7211 return n >= first && (last <= 0 || n <= last);
7214 page_range *output_page_list = 0;
7216 int in_output_page_list(int n)
7218 if (!output_page_list)
7219 return 1;
7220 for (page_range *p = output_page_list; p; p = p->next)
7221 if (p->contains(n))
7222 return 1;
7223 return 0;
7226 static void parse_output_page_list(char *p)
7228 for (;;) {
7229 int i;
7230 if (*p == '-')
7231 i = 1;
7232 else if (csdigit(*p)) {
7233 i = 0;
7235 i = i*10 + *p++ - '0';
7236 while (csdigit(*p));
7238 else
7239 break;
7240 int j;
7241 if (*p == '-') {
7242 p++;
7243 j = 0;
7244 if (csdigit(*p)) {
7246 j = j*10 + *p++ - '0';
7247 while (csdigit(*p));
7250 else
7251 j = i;
7252 if (j == 0)
7253 last_page_number = -1;
7254 else if (last_page_number >= 0 && j > last_page_number)
7255 last_page_number = j;
7256 output_page_list = new page_range(i, j, output_page_list);
7257 if (*p != ',')
7258 break;
7259 ++p;
7261 if (*p != '\0') {
7262 error("bad output page list");
7263 output_page_list = 0;
7267 static file_case *open_mac_file(const char *mac)
7269 // Try first FOOBAR.tmac, then tmac.FOOBAR
7270 char *s = new char[strlen(mac) + strlen(MACRO_POSTFIX) +1];
7271 strcpy(s, mac);
7272 strcat(s, MACRO_POSTFIX);
7274 file_case *fcp;
7275 if ((fcp = mac_path->open_file(s, fcp->fc_take_path)) == NULL) {
7276 s = new char[strlen(mac) + strlen(MACRO_PREFIX) +1];
7277 strcpy(s, MACRO_PREFIX);
7278 strcat(s, mac);
7279 fcp = mac_path->open_file(s, fcp->fc_take_path);
7281 return fcp;
7284 static void process_macro_file(const char *mac)
7286 file_case *fcp = open_mac_file(mac);
7287 if (fcp == NULL)
7288 fatal("can't find macro file %1", mac);
7289 const char *s = symbol(fcp->path()).contents();
7290 input_stack::push(new file_iterator(fcp, s));
7291 tok.next();
7292 process_input_stack();
7295 static void process_startup_file(const char *filename)
7297 search_path *orig_mac_path = mac_path;
7298 mac_path = &config_macro_path;
7299 file_case *fcp;
7300 if ((fcp = mac_path->open_file(filename)) != NULL) {
7301 input_stack::push(new file_iterator(fcp, symbol(fcp->path()).contents()));
7302 tok.next();
7303 process_input_stack();
7305 mac_path = orig_mac_path;
7308 void macro_source()
7310 symbol nm = get_long_name(1);
7311 if (nm.is_null())
7312 skip_line();
7313 else {
7314 while (!tok.newline() && !tok.eof())
7315 tok.next();
7316 // .mso doesn't (and cannot) go through open_mac_file, so we
7317 // need to do it here manually: If we have tmac.FOOBAR, try
7318 // FOOBAR.tmac and vice versa
7319 file_case *fcp;
7320 if ((fcp = mac_path->open_file(nm.contents())) == NULL) {
7321 const char *fn = nm.contents();
7323 if (strncasecmp(fn, MACRO_PREFIX, sizeof(MACRO_PREFIX) - 1) == 0) {
7324 char *s = new char[strlen(fn) + sizeof(MACRO_POSTFIX)];
7325 strcpy(s, fn + sizeof(MACRO_PREFIX) - 1);
7326 strcat(s, MACRO_POSTFIX);
7327 fcp = mac_path->open_file(s, fcp->fc_take_path);
7330 if (fcp == NULL) {
7331 if (strncasecmp(fn + strlen(fn) - sizeof(MACRO_POSTFIX) + 1,
7332 MACRO_POSTFIX, sizeof(MACRO_POSTFIX) - 1) == 0) {
7333 char *s = new char[strlen(fn) + sizeof(MACRO_PREFIX)];
7334 strcpy(s, MACRO_PREFIX);
7335 strncat(s, fn, strlen(fn) - sizeof(MACRO_POSTFIX) + 1);
7336 fcp = mac_path->open_file(s, fcp->fc_take_path);
7341 if (fcp != NULL)
7342 input_stack::push(new file_iterator(fcp, symbol(fcp->path()).contents()));
7343 else
7344 warning(WARN_FILE, "can't find macro file `%1'", nm.contents());
7345 tok.next();
7349 static void process_input_file(const char *name)
7351 file_case *fcp;
7352 if ((fcp = include_search_path.open_file_cautious(name)) == NULL) {
7353 assert(strcmp(name, "-"));
7354 fatal("can't open `%1': %2", name, strerror(errno));
7356 input_stack::push(new file_iterator(fcp, name));
7357 tok.next();
7358 process_input_stack();
7361 // make sure the_input is empty before calling this
7363 static int evaluate_expression(const char *expr, units *res)
7365 input_stack::push(make_temp_iterator(expr));
7366 tok.next();
7367 int success = get_number(res, 'u');
7368 while (input_stack::get(0) != EOF)
7370 return success;
7373 static void do_register_assignment(const char *s)
7375 const char *p = strchr(s, '=');
7376 if (!p) {
7377 char buf[2];
7378 buf[0] = s[0];
7379 buf[1] = 0;
7380 units n;
7381 if (evaluate_expression(s + 1, &n))
7382 set_number_reg(buf, n);
7384 else {
7385 char *buf = new char[p - s + 1];
7386 memcpy(buf, s, p - s);
7387 buf[p - s] = 0;
7388 units n;
7389 if (evaluate_expression(p + 1, &n))
7390 set_number_reg(buf, n);
7391 a_delete buf;
7395 static void set_string(const char *name, const char *value)
7397 macro *m = new macro;
7398 for (const char *p = value; *p; p++)
7399 if (!invalid_input_char((unsigned char)*p))
7400 m->append(*p);
7401 request_dictionary.define(name, m);
7404 static void do_string_assignment(const char *s)
7406 const char *p = strchr(s, '=');
7407 if (!p) {
7408 char buf[2];
7409 buf[0] = s[0];
7410 buf[1] = 0;
7411 set_string(buf, s + 1);
7413 else {
7414 char *buf = new char[p - s + 1];
7415 memcpy(buf, s, p - s);
7416 buf[p - s] = 0;
7417 set_string(buf, p + 1);
7418 a_delete buf;
7422 struct string_list {
7423 const char *s;
7424 string_list *next;
7425 string_list(const char *ss) : s(ss), next(0) {}
7428 #if 0
7429 static void prepend_string(const char *s, string_list **p)
7431 string_list *l = new string_list(s);
7432 l->next = *p;
7433 *p = l;
7435 #endif
7437 static void add_string(const char *s, string_list **p)
7439 while (*p)
7440 p = &((*p)->next);
7441 *p = new string_list(s);
7444 void usage(FILE *stream, const char *prog)
7446 fprintf(stream,
7447 "usage: %s -abcivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n"
7448 " -rcn -Tname -Fdir -Idir -Mdir [files...]\n",
7449 prog);
7452 int main(int argc, char **argv)
7454 program_name = argv[0];
7455 static char stderr_buf[BUFSIZ];
7456 setbuf(stderr, stderr_buf);
7457 int c;
7458 string_list *macros = 0;
7459 string_list *register_assignments = 0;
7460 string_list *string_assignments = 0;
7461 int iflag = 0;
7462 int tflag = 0;
7463 int fflag = 0;
7464 int nflag = 0;
7465 int no_rc = 0; // don't process troffrc and troffrc-end
7466 int next_page_number = 0; // pacify compiler
7467 opterr = 0;
7468 hresolution = vresolution = 1;
7469 // restore $PATH if called from groff
7470 char* groff_path = getenv("GROFF_PATH__");
7471 if (groff_path) {
7472 string e = "PATH";
7473 e += '=';
7474 if (*groff_path)
7475 e += groff_path;
7476 e += '\0';
7477 if (putenv(strsave(e.contents())))
7478 fatal("putenv failed");
7480 static const struct option long_options[] = {
7481 { "help", no_argument, 0, CHAR_MAX + 1 },
7482 { "version", no_argument, 0, 'v' },
7483 { 0, 0, 0, 0 }
7485 #if defined(DEBUGGING)
7486 #define DEBUG_OPTION "D"
7487 #endif
7488 while ((c = getopt_long(argc, argv,
7489 "abciI:vw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU"
7490 DEBUG_OPTION, long_options, 0))
7491 != EOF)
7492 switch(c) {
7493 case 'v':
7495 printf("GNU troff (groff) version %s\n", Version_string);
7496 exit(0);
7497 break;
7499 case 'I':
7500 // Search path for .psbb files
7501 // and most other non-system input files.
7502 include_search_path.command_line_dir(optarg);
7503 break;
7504 case 'T':
7505 device = optarg;
7506 tflag = 1;
7507 is_html = (strcmp(device, "html") == 0);
7508 break;
7509 case 'C':
7510 compatible_flag = 1;
7511 // fall through
7512 case 'c':
7513 color_flag = 0;
7514 break;
7515 case 'M':
7516 macro_path.command_line_dir(optarg);
7517 safer_macro_path.command_line_dir(optarg);
7518 config_macro_path.command_line_dir(optarg);
7519 break;
7520 case 'F':
7521 font::command_line_font_dir(optarg);
7522 break;
7523 case 'm':
7524 add_string(optarg, &macros);
7525 break;
7526 case 'E':
7527 inhibit_errors = 1;
7528 break;
7529 case 'R':
7530 no_rc = 1;
7531 break;
7532 case 'w':
7533 enable_warning(optarg);
7534 break;
7535 case 'W':
7536 disable_warning(optarg);
7537 break;
7538 case 'i':
7539 iflag = 1;
7540 break;
7541 case 'b':
7542 backtrace_flag = 1;
7543 break;
7544 case 'a':
7545 ascii_output_flag = 1;
7546 break;
7547 case 'z':
7548 suppress_output_flag = 1;
7549 break;
7550 case 'n':
7551 if (sscanf(optarg, "%d", &next_page_number) == 1)
7552 nflag++;
7553 else
7554 error("bad page number");
7555 break;
7556 case 'o':
7557 parse_output_page_list(optarg);
7558 break;
7559 case 'd':
7560 if (*optarg == '\0')
7561 error("`-d' requires non-empty argument");
7562 else
7563 add_string(optarg, &string_assignments);
7564 break;
7565 case 'r':
7566 if (*optarg == '\0')
7567 error("`-r' requires non-empty argument");
7568 else
7569 add_string(optarg, &register_assignments);
7570 break;
7571 case 'f':
7572 default_family = symbol(optarg);
7573 fflag = 1;
7574 break;
7575 case 'q':
7576 case 's':
7577 case 't':
7578 // silently ignore these
7579 break;
7580 case 'U':
7581 unsafe_flag = 1; // unsafe behaviour
7582 break;
7583 #if defined(DEBUGGING)
7584 case 'D':
7585 debug_state = 1;
7586 break;
7587 #endif
7588 case CHAR_MAX + 1: // --help
7589 usage(stdout, argv[0]);
7590 exit(0);
7591 break;
7592 case '?':
7593 usage(stderr, argv[0]);
7594 exit(1);
7595 break; // never reached
7596 default:
7597 assert(0);
7599 if (unsafe_flag)
7600 mac_path = &macro_path;
7601 set_string(".T", device);
7602 init_charset_table();
7603 init_hpf_code_table();
7604 if (!font::load_desc())
7605 fatal("sorry, I can't continue");
7606 units_per_inch = font::res;
7607 hresolution = font::hor;
7608 vresolution = font::vert;
7609 sizescale = font::sizescale;
7610 tcommand_flag = font::tcommand;
7611 warn_scale = (double)units_per_inch;
7612 warn_scaling_indicator = 'i';
7613 if (!fflag && font::family != 0 && *font::family != '\0')
7614 default_family = symbol(font::family);
7615 font_size::init_size_table(font::sizes);
7616 int i;
7617 int j = 1;
7618 if (font::style_table) {
7619 for (i = 0; font::style_table[i]; i++)
7620 mount_style(j++, symbol(font::style_table[i]));
7622 for (i = 0; font::font_name_table[i]; i++, j++)
7623 // In the DESC file a font name of 0 (zero) means leave this
7624 // position empty.
7625 if (strcmp(font::font_name_table[i], "0") != 0)
7626 mount_font(j, symbol(font::font_name_table[i]));
7627 curdiv = topdiv = new top_level_diversion;
7628 if (nflag)
7629 topdiv->set_next_page_number(next_page_number);
7630 init_input_requests();
7631 init_env_requests();
7632 init_div_requests();
7633 #ifdef COLUMN
7634 init_column_requests();
7635 #endif /* COLUMN */
7636 init_node_requests();
7637 number_reg_dictionary.define(".T", new constant_reg(tflag ? "1" : "0"));
7638 init_registers();
7639 init_reg_requests();
7640 init_hyphen_requests();
7641 init_environments();
7642 while (string_assignments) {
7643 do_string_assignment(string_assignments->s);
7644 string_list *tem = string_assignments;
7645 string_assignments = string_assignments->next;
7646 delete tem;
7648 while (register_assignments) {
7649 do_register_assignment(register_assignments->s);
7650 string_list *tem = register_assignments;
7651 register_assignments = register_assignments->next;
7652 delete tem;
7654 if (!no_rc)
7655 process_startup_file(INITIAL_STARTUP_FILE);
7656 while (macros) {
7657 process_macro_file(macros->s);
7658 string_list *tem = macros;
7659 macros = macros->next;
7660 delete tem;
7662 if (!no_rc)
7663 process_startup_file(FINAL_STARTUP_FILE);
7664 for (i = optind; i < argc; i++)
7665 process_input_file(argv[i]);
7666 if (optind >= argc || iflag)
7667 process_input_file("-");
7668 exit_troff();
7669 return 0; // not reached
7672 void warn_request()
7674 int n;
7675 if (has_arg() && get_integer(&n)) {
7676 if (n & ~WARN_TOTAL) {
7677 warning(WARN_RANGE, "warning mask must be between 0 and %1", WARN_TOTAL);
7678 n &= WARN_TOTAL;
7680 warning_mask = n;
7682 else
7683 warning_mask = WARN_TOTAL;
7684 skip_line();
7687 static void init_registers()
7689 #ifdef LONG_FOR_TIME_T
7690 long
7691 #else /* not LONG_FOR_TIME_T */
7692 time_t
7693 #endif /* not LONG_FOR_TIME_T */
7694 t = time(0);
7695 // Use struct here to work around misfeature in old versions of g++.
7696 struct tm *tt = localtime(&t);
7697 set_number_reg("seconds", int(tt->tm_sec));
7698 set_number_reg("minutes", int(tt->tm_min));
7699 set_number_reg("hours", int(tt->tm_hour));
7700 set_number_reg("dw", int(tt->tm_wday + 1));
7701 set_number_reg("dy", int(tt->tm_mday));
7702 set_number_reg("mo", int(tt->tm_mon + 1));
7703 set_number_reg("year", int(1900 + tt->tm_year));
7704 set_number_reg("yr", int(tt->tm_year));
7705 set_number_reg("$$", getpid());
7706 number_reg_dictionary.define(".A",
7707 new constant_reg(ascii_output_flag
7708 ? "1"
7709 : "0"));
7713 * registers associated with \O
7716 static int output_reg_minx_contents = -1;
7717 static int output_reg_miny_contents = -1;
7718 static int output_reg_maxx_contents = -1;
7719 static int output_reg_maxy_contents = -1;
7721 void check_output_limits(int x, int y)
7723 if ((output_reg_minx_contents == -1) || (x < output_reg_minx_contents))
7724 output_reg_minx_contents = x;
7725 if (x > output_reg_maxx_contents)
7726 output_reg_maxx_contents = x;
7727 if ((output_reg_miny_contents == -1) || (y < output_reg_miny_contents))
7728 output_reg_miny_contents = y;
7729 if (y > output_reg_maxy_contents)
7730 output_reg_maxy_contents = y;
7733 void reset_output_registers()
7735 output_reg_minx_contents = -1;
7736 output_reg_miny_contents = -1;
7737 output_reg_maxx_contents = -1;
7738 output_reg_maxy_contents = -1;
7741 void get_output_registers(int *minx, int *miny, int *maxx, int *maxy)
7743 *minx = output_reg_minx_contents;
7744 *miny = output_reg_miny_contents;
7745 *maxx = output_reg_maxx_contents;
7746 *maxy = output_reg_maxy_contents;
7749 void init_input_requests()
7751 init_request("ab", abort_request);
7752 init_request("als", alias_macro);
7753 init_request("am", append_macro);
7754 init_request("am1", append_nocomp_macro);
7755 init_request("ami", append_indirect_macro);
7756 init_request("ami1", append_indirect_nocomp_macro);
7757 init_request("as", append_string);
7758 init_request("as1", append_nocomp_string);
7759 init_request("asciify", asciify_macro);
7760 init_request("backtrace", backtrace_request);
7761 init_request("blm", blank_line_macro);
7762 init_request("break", while_break_request);
7763 init_request("cf", copy_file);
7764 init_request("cflags", char_flags);
7765 init_request("char", define_character);
7766 init_request("chop", chop_macro);
7767 init_request("close", close_request);
7768 init_request("color", activate_color);
7769 init_request("composite", composite_request);
7770 init_request("continue", while_continue_request);
7771 init_request("cp", compatible);
7772 init_request("de", define_macro);
7773 init_request("de1", define_nocomp_macro);
7774 init_request("defcolor", define_color);
7775 init_request("dei", define_indirect_macro);
7776 init_request("dei1", define_indirect_nocomp_macro);
7777 init_request("device", device_request);
7778 init_request("devicem", device_macro_request);
7779 init_request("do", do_request);
7780 init_request("ds", define_string);
7781 init_request("ds1", define_nocomp_string);
7782 init_request("ec", set_escape_char);
7783 init_request("ecr", restore_escape_char);
7784 init_request("ecs", save_escape_char);
7785 init_request("el", else_request);
7786 init_request("em", end_macro);
7787 init_request("eo", escape_off);
7788 init_request("ex", exit_request);
7789 init_request("fchar", define_fallback_character);
7790 #ifdef WIDOW_CONTROL
7791 init_request("fpl", flush_pending_lines);
7792 #endif /* WIDOW_CONTROL */
7793 init_request("hcode", hyphenation_code);
7794 init_request("hpfcode", hyphenation_patterns_file_code);
7795 init_request("ie", if_else_request);
7796 init_request("if", if_request);
7797 init_request("ig", ignore);
7798 init_request("length", length_request);
7799 init_request("lf", line_file);
7800 init_request("mso", macro_source);
7801 init_request("nop", nop_request);
7802 init_request("nroff", nroff_request);
7803 init_request("nx", next_file);
7804 init_request("open", open_request);
7805 init_request("opena", opena_request);
7806 init_request("output", output_request);
7807 init_request("pc", set_page_character);
7808 init_request("pi", pipe_output);
7809 init_request("pm", print_macros);
7810 init_request("psbb", ps_bbox_request);
7811 #ifndef POPEN_MISSING
7812 init_request("pso", pipe_source);
7813 #endif /* not POPEN_MISSING */
7814 init_request("rchar", remove_character);
7815 init_request("rd", read_request);
7816 init_request("return", return_macro_request);
7817 init_request("rm", remove_macro);
7818 init_request("rn", rename_macro);
7819 init_request("schar", define_special_character);
7820 init_request("shift", shift);
7821 init_request("so", source);
7822 init_request("spreadwarn", spreadwarn_request);
7823 init_request("substring", substring_request);
7824 init_request("sy", system_request);
7825 init_request("tag", tag);
7826 init_request("taga", taga);
7827 init_request("tm", terminal);
7828 init_request("tm1", terminal1);
7829 init_request("tmc", terminal_continue);
7830 init_request("tr", translate);
7831 init_request("trf", transparent_file);
7832 init_request("trin", translate_input);
7833 init_request("trnt", translate_no_transparent);
7834 init_request("troff", troff_request);
7835 init_request("unformat", unformat_macro);
7836 #ifdef COLUMN
7837 init_request("vj", vjustify);
7838 #endif /* COLUMN */
7839 init_request("warn", warn_request);
7840 init_request("warnscale", warnscale_request);
7841 init_request("while", while_request);
7842 init_request("write", write_request);
7843 init_request("writec", write_request_continue);
7844 init_request("writem", write_macro_request);
7845 number_reg_dictionary.define(".$", new nargs_reg);
7846 number_reg_dictionary.define(".br", new break_flag_reg);
7847 number_reg_dictionary.define(".C", new constant_int_reg(&compatible_flag));
7848 number_reg_dictionary.define(".O", new variable_reg(&begin_level));
7849 number_reg_dictionary.define(".c", new lineno_reg);
7850 number_reg_dictionary.define(".color", new constant_int_reg(&color_flag));
7851 number_reg_dictionary.define(".F", new filename_reg);
7852 number_reg_dictionary.define(".g", new constant_reg("1"));
7853 number_reg_dictionary.define(".H", new constant_int_reg(&hresolution));
7854 number_reg_dictionary.define(".R", new constant_reg("10000"));
7855 number_reg_dictionary.define(".U", new constant_int_reg(&unsafe_flag));
7856 number_reg_dictionary.define(".V", new constant_int_reg(&vresolution));
7857 number_reg_dictionary.define(".warn", new constant_int_reg(&warning_mask));
7858 extern const char *major_version;
7859 number_reg_dictionary.define(".x", new constant_reg(major_version));
7860 extern const char *revision;
7861 number_reg_dictionary.define(".Y", new constant_reg(revision));
7862 extern const char *minor_version;
7863 number_reg_dictionary.define(".y", new constant_reg(minor_version));
7864 number_reg_dictionary.define("c.", new writable_lineno_reg);
7865 number_reg_dictionary.define("llx", new variable_reg(&llx_reg_contents));
7866 number_reg_dictionary.define("lly", new variable_reg(&lly_reg_contents));
7867 number_reg_dictionary.define("opmaxx",
7868 new variable_reg(&output_reg_maxx_contents));
7869 number_reg_dictionary.define("opmaxy",
7870 new variable_reg(&output_reg_maxy_contents));
7871 number_reg_dictionary.define("opminx",
7872 new variable_reg(&output_reg_minx_contents));
7873 number_reg_dictionary.define("opminy",
7874 new variable_reg(&output_reg_miny_contents));
7875 number_reg_dictionary.define("slimit",
7876 new variable_reg(&input_stack::limit));
7877 number_reg_dictionary.define("systat", new variable_reg(&system_status));
7878 number_reg_dictionary.define("urx", new variable_reg(&urx_reg_contents));
7879 number_reg_dictionary.define("ury", new variable_reg(&ury_reg_contents));
7882 object_dictionary request_dictionary(501);
7884 void init_request(const char *s, REQUEST_FUNCP f)
7886 request_dictionary.define(s, new request(f));
7889 static request_or_macro *lookup_request(symbol nm)
7891 assert(!nm.is_null());
7892 request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm);
7893 if (p == 0) {
7894 warning(WARN_MAC, "macro `%1' not defined", nm.contents());
7895 p = new macro;
7896 request_dictionary.define(nm, p);
7898 return p;
7901 node *charinfo_to_node_list(charinfo *ci, const environment *envp)
7903 // Don't interpret character definitions in compatible mode.
7904 int old_compatible_flag = compatible_flag;
7905 compatible_flag = 0;
7906 int old_escape_char = escape_char;
7907 escape_char = '\\';
7908 macro *mac = ci->set_macro(0);
7909 assert(mac != 0);
7910 environment *oldenv = curenv;
7911 environment env(envp);
7912 curenv = &env;
7913 curenv->set_composite();
7914 token old_tok = tok;
7915 input_stack::add_boundary();
7916 string_iterator *si =
7917 new string_iterator(*mac, "composite character", ci->nm);
7918 input_stack::push(si);
7919 // we don't use process_input_stack, because we don't want to recognise
7920 // requests
7921 for (;;) {
7922 tok.next();
7923 if (tok.eof())
7924 break;
7925 if (tok.newline()) {
7926 error("composite character mustn't contain newline");
7927 while (!tok.eof())
7928 tok.next();
7929 break;
7931 else
7932 tok.process();
7934 node *n = curenv->extract_output_line();
7935 input_stack::remove_boundary();
7936 ci->set_macro(mac);
7937 tok = old_tok;
7938 curenv = oldenv;
7939 compatible_flag = old_compatible_flag;
7940 escape_char = old_escape_char;
7941 have_input = 0;
7942 return n;
7945 static node *read_draw_node()
7947 token start;
7948 start.next();
7949 if (!start.delimiter(1)){
7950 do {
7951 tok.next();
7952 } while (tok != start && !tok.newline() && !tok.eof());
7954 else {
7955 tok.next();
7956 if (tok == start)
7957 error("missing argument");
7958 else {
7959 unsigned char type = tok.ch();
7960 if (type == 'F') {
7961 read_color_draw_node(start);
7962 return 0;
7964 tok.next();
7965 int maxpoints = 10;
7966 hvpair *point = new hvpair[maxpoints];
7967 int npoints = 0;
7968 int no_last_v = 0;
7969 int err = 0;
7970 int i;
7971 for (i = 0; tok != start; i++) {
7972 if (i == maxpoints) {
7973 hvpair *oldpoint = point;
7974 point = new hvpair[maxpoints*2];
7975 for (int j = 0; j < maxpoints; j++)
7976 point[j] = oldpoint[j];
7977 maxpoints *= 2;
7978 a_delete oldpoint;
7980 if (!get_hunits(&point[i].h,
7981 type == 'f' || type == 't' ? 'u' : 'm')) {
7982 err = 1;
7983 break;
7985 ++npoints;
7986 tok.skip();
7987 point[i].v = V0;
7988 if (tok == start) {
7989 no_last_v = 1;
7990 break;
7992 if (!get_vunits(&point[i].v, 'v')) {
7993 err = 1;
7994 break;
7996 tok.skip();
7998 while (tok != start && !tok.newline() && !tok.eof())
7999 tok.next();
8000 if (!err) {
8001 switch (type) {
8002 case 'l':
8003 if (npoints != 1 || no_last_v) {
8004 error("two arguments needed for line");
8005 npoints = 1;
8007 break;
8008 case 'c':
8009 if (npoints != 1 || !no_last_v) {
8010 error("one argument needed for circle");
8011 npoints = 1;
8012 point[0].v = V0;
8014 break;
8015 case 'e':
8016 if (npoints != 1 || no_last_v) {
8017 error("two arguments needed for ellipse");
8018 npoints = 1;
8020 break;
8021 case 'a':
8022 if (npoints != 2 || no_last_v) {
8023 error("four arguments needed for arc");
8024 npoints = 2;
8026 break;
8027 case '~':
8028 if (no_last_v)
8029 error("even number of arguments needed for spline");
8030 break;
8031 case 'f':
8032 if (npoints != 1 || !no_last_v) {
8033 error("one argument needed for gray shade");
8034 npoints = 1;
8035 point[0].v = V0;
8037 default:
8038 // silently pass it through
8039 break;
8041 draw_node *dn = new draw_node(type, point, npoints,
8042 curenv->get_font_size(),
8043 curenv->get_glyph_color(),
8044 curenv->get_fill_color());
8045 a_delete point;
8046 return dn;
8048 else {
8049 a_delete point;
8053 return 0;
8056 static void read_color_draw_node(token &start)
8058 tok.next();
8059 if (tok == start) {
8060 error("missing color scheme");
8061 return;
8063 unsigned char scheme = tok.ch();
8064 tok.next();
8065 color *col = 0;
8066 char end = start.ch();
8067 switch (scheme) {
8068 case 'c':
8069 col = read_cmy(end);
8070 break;
8071 case 'd':
8072 col = &default_color;
8073 break;
8074 case 'g':
8075 col = read_gray(end);
8076 break;
8077 case 'k':
8078 col = read_cmyk(end);
8079 break;
8080 case 'r':
8081 col = read_rgb(end);
8082 break;
8084 if (col)
8085 curenv->set_fill_color(col);
8086 while (tok != start) {
8087 if (tok.newline() || tok.eof()) {
8088 warning(WARN_DELIM, "missing closing delimiter");
8089 input_stack::push(make_temp_iterator("\n"));
8090 break;
8092 tok.next();
8094 have_input = 1;
8097 static struct {
8098 const char *name;
8099 int mask;
8100 } warning_table[] = {
8101 { "char", WARN_CHAR },
8102 { "range", WARN_RANGE },
8103 { "break", WARN_BREAK },
8104 { "delim", WARN_DELIM },
8105 { "el", WARN_EL },
8106 { "scale", WARN_SCALE },
8107 { "number", WARN_NUMBER },
8108 { "syntax", WARN_SYNTAX },
8109 { "tab", WARN_TAB },
8110 { "right-brace", WARN_RIGHT_BRACE },
8111 { "missing", WARN_MISSING },
8112 { "input", WARN_INPUT },
8113 { "escape", WARN_ESCAPE },
8114 { "space", WARN_SPACE },
8115 { "font", WARN_FONT },
8116 { "di", WARN_DI },
8117 { "mac", WARN_MAC },
8118 { "reg", WARN_REG },
8119 { "ig", WARN_IG },
8120 { "color", WARN_COLOR },
8121 { "all", WARN_TOTAL & ~(WARN_DI | WARN_MAC | WARN_REG) },
8122 { "w", WARN_TOTAL },
8123 { "default", DEFAULT_WARNING_MASK },
8126 static int lookup_warning(const char *name)
8128 for (unsigned int i = 0;
8129 i < sizeof(warning_table)/sizeof(warning_table[0]);
8130 i++)
8131 if (strcmp(name, warning_table[i].name) == 0)
8132 return warning_table[i].mask;
8133 return 0;
8136 static void enable_warning(const char *name)
8138 int mask = lookup_warning(name);
8139 if (mask)
8140 warning_mask |= mask;
8141 else
8142 error("unknown warning `%1'", name);
8145 static void disable_warning(const char *name)
8147 int mask = lookup_warning(name);
8148 if (mask)
8149 warning_mask &= ~mask;
8150 else
8151 error("unknown warning `%1'", name);
8154 static void copy_mode_error(const char *format,
8155 const errarg &arg1,
8156 const errarg &arg2,
8157 const errarg &arg3)
8159 if (ignoring) {
8160 static const char prefix[] = "(in ignored input) ";
8161 char *s = new char[sizeof(prefix) + strlen(format)];
8162 strcpy(s, prefix);
8163 strcat(s, format);
8164 warning(WARN_IG, s, arg1, arg2, arg3);
8165 a_delete s;
8167 else
8168 error(format, arg1, arg2, arg3);
8171 enum error_type { WARNING, OUTPUT_WARNING, ERROR, FATAL };
8173 static void do_error(error_type type,
8174 const char *format,
8175 const errarg &arg1,
8176 const errarg &arg2,
8177 const errarg &arg3)
8179 const char *filename;
8180 int lineno;
8181 if (inhibit_errors && type < FATAL)
8182 return;
8183 if (backtrace_flag)
8184 input_stack::backtrace();
8185 if (!get_file_line(&filename, &lineno))
8186 filename = 0;
8187 if (filename)
8188 errprint("%1:%2: ", filename, lineno);
8189 else if (program_name)
8190 fprintf(stderr, "%s: ", program_name);
8191 switch (type) {
8192 case FATAL:
8193 fputs("fatal error: ", stderr);
8194 break;
8195 case ERROR:
8196 break;
8197 case WARNING:
8198 fputs("warning: ", stderr);
8199 break;
8200 case OUTPUT_WARNING:
8201 double fromtop = topdiv->get_vertical_position().to_units() / warn_scale;
8202 fprintf(stderr, "warning [p %d, %.1f%c",
8203 topdiv->get_page_number(), fromtop, warn_scaling_indicator);
8204 if (topdiv != curdiv) {
8205 double fromtop1 = curdiv->get_vertical_position().to_units()
8206 / warn_scale;
8207 fprintf(stderr, ", div `%s', %.1f%c",
8208 curdiv->get_diversion_name(), fromtop1, warn_scaling_indicator);
8210 fprintf(stderr, "]: ");
8211 break;
8213 errprint(format, arg1, arg2, arg3);
8214 fputc('\n', stderr);
8215 fflush(stderr);
8216 if (type == FATAL)
8217 cleanup_and_exit(1);
8220 int warning(warning_type t,
8221 const char *format,
8222 const errarg &arg1,
8223 const errarg &arg2,
8224 const errarg &arg3)
8226 if ((t & warning_mask) != 0) {
8227 do_error(WARNING, format, arg1, arg2, arg3);
8228 return 1;
8230 else
8231 return 0;
8234 int output_warning(warning_type t,
8235 const char *format,
8236 const errarg &arg1,
8237 const errarg &arg2,
8238 const errarg &arg3)
8240 if ((t & warning_mask) != 0) {
8241 do_error(OUTPUT_WARNING, format, arg1, arg2, arg3);
8242 return 1;
8244 else
8245 return 0;
8248 void error(const char *format,
8249 const errarg &arg1,
8250 const errarg &arg2,
8251 const errarg &arg3)
8253 do_error(ERROR, format, arg1, arg2, arg3);
8256 void fatal(const char *format,
8257 const errarg &arg1,
8258 const errarg &arg2,
8259 const errarg &arg3)
8261 do_error(FATAL, format, arg1, arg2, arg3);
8264 void fatal_with_file_and_line(const char *filename, int lineno,
8265 const char *format,
8266 const errarg &arg1,
8267 const errarg &arg2,
8268 const errarg &arg3)
8270 fprintf(stderr, "%s:%d: fatal error: ", filename, lineno);
8271 errprint(format, arg1, arg2, arg3);
8272 fputc('\n', stderr);
8273 fflush(stderr);
8274 cleanup_and_exit(1);
8277 void error_with_file_and_line(const char *filename, int lineno,
8278 const char *format,
8279 const errarg &arg1,
8280 const errarg &arg2,
8281 const errarg &arg3)
8283 fprintf(stderr, "%s:%d: error: ", filename, lineno);
8284 errprint(format, arg1, arg2, arg3);
8285 fputc('\n', stderr);
8286 fflush(stderr);
8289 dictionary charinfo_dictionary(501);
8291 charinfo *get_charinfo(symbol nm)
8293 void *p = charinfo_dictionary.lookup(nm);
8294 if (p != 0)
8295 return (charinfo *)p;
8296 charinfo *cp = new charinfo(nm);
8297 (void)charinfo_dictionary.lookup(nm, cp);
8298 return cp;
8301 int charinfo::next_index = 0;
8303 charinfo::charinfo(symbol s)
8304 : translation(0), mac(0), special_translation(TRANSLATE_NONE),
8305 hyphenation_code(0), flags(0), ascii_code(0), asciify_code(0),
8306 not_found(0), transparent_translate(1), translate_input(0),
8307 mode(CHAR_NORMAL), nm(s)
8309 index = next_index++;
8310 number = -1;
8313 void charinfo::set_hyphenation_code(unsigned char c)
8315 hyphenation_code = c;
8318 void charinfo::set_translation(charinfo *ci, int tt, int ti)
8320 translation = ci;
8321 if (ci && ti) {
8322 if (hyphenation_code != 0)
8323 ci->set_hyphenation_code(hyphenation_code);
8324 if (asciify_code != 0)
8325 ci->set_asciify_code(asciify_code);
8326 else if (ascii_code != 0)
8327 ci->set_asciify_code(ascii_code);
8328 ci->set_translation_input();
8330 special_translation = TRANSLATE_NONE;
8331 transparent_translate = tt;
8334 void charinfo::set_special_translation(int c, int tt)
8336 special_translation = c;
8337 translation = 0;
8338 transparent_translate = tt;
8341 void charinfo::set_ascii_code(unsigned char c)
8343 ascii_code = c;
8346 void charinfo::set_asciify_code(unsigned char c)
8348 asciify_code = c;
8351 macro *charinfo::set_macro(macro *m)
8353 macro *tem = mac;
8354 mac = m;
8355 return tem;
8358 macro *charinfo::setx_macro(macro *m, char_mode cm)
8360 macro *tem = mac;
8361 mac = m;
8362 mode = cm;
8363 return tem;
8366 void charinfo::set_number(int n)
8368 assert(n >= 0);
8369 number = n;
8372 int charinfo::get_number()
8374 assert(number >= 0);
8375 return number;
8378 symbol UNNAMED_SYMBOL("---");
8380 // For numbered characters not between 0 and 255, we make a symbol out
8381 // of the number and store them in this dictionary.
8383 dictionary numbered_charinfo_dictionary(11);
8385 charinfo *get_charinfo_by_number(int n)
8387 static charinfo *number_table[256];
8389 if (n >= 0 && n < 256) {
8390 charinfo *ci = number_table[n];
8391 if (!ci) {
8392 ci = new charinfo(UNNAMED_SYMBOL);
8393 ci->set_number(n);
8394 number_table[n] = ci;
8396 return ci;
8398 else {
8399 symbol ns(i_to_a(n));
8400 charinfo *ci = (charinfo *)numbered_charinfo_dictionary.lookup(ns);
8401 if (!ci) {
8402 ci = new charinfo(UNNAMED_SYMBOL);
8403 ci->set_number(n);
8404 (void)numbered_charinfo_dictionary.lookup(ns, ci);
8406 return ci;
8410 // This overrides the same function from libgroff; while reading font
8411 // definition files it puts single-letter glyph names into `charset_table'
8412 // and converts glyph names of the form `\x' (`x' a single letter) into `x'.
8413 // Consequently, symbol("x") refers to glyph name `\x', not `x'.
8415 glyph *name_to_glyph(const char *nm)
8417 charinfo *ci;
8418 if (nm[1] == 0)
8419 ci = charset_table[nm[0] & 0xff];
8420 else if (nm[0] == '\\' && nm[2] == 0)
8421 ci = get_charinfo(symbol(nm + 1));
8422 else
8423 ci = get_charinfo(symbol(nm));
8424 return ci->as_glyph();
8427 glyph *number_to_glyph(int n)
8429 return get_charinfo_by_number(n)->as_glyph();
8432 const char *glyph_to_name(glyph *g)
8434 charinfo *ci = (charinfo *)g; // Every glyph is actually a charinfo.
8435 return (ci->nm != UNNAMED_SYMBOL ? ci->nm.contents() : NULL);