No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / dist / groff / src / roff / troff / input.cpp
blobd97c66d94bdd7607714441a967a0873a23040388
1 /* $NetBSD$ */
3 // -*- C++ -*-
4 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005
5 Free Software Foundation, Inc.
6 Written by James Clark (jjc@jclark.com)
8 This file is part of groff.
10 groff is free software; you can redistribute it and/or modify it under
11 the terms of the GNU General Public License as published by the Free
12 Software Foundation; either version 2, or (at your option) any later
13 version.
15 groff is distributed in the hope that it will be useful, but WITHOUT ANY
16 WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 for more details.
20 You should have received a copy of the GNU General Public License along
21 with groff; see the file COPYING. If not, write to the Free Software
22 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
24 #define DEBUGGING
26 #include "troff.h"
27 #include "dictionary.h"
28 #include "hvunits.h"
29 #include "stringclass.h"
30 #include "mtsm.h"
31 #include "env.h"
32 #include "request.h"
33 #include "node.h"
34 #include "token.h"
35 #include "div.h"
36 #include "reg.h"
37 #include "charinfo.h"
38 #include "macropath.h"
39 #include "input.h"
40 #include "defs.h"
41 #include "font.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 // \R, \s, or \S has been processed in
114 // token::next()
115 int old_have_input = 0; // value of have_input right before \n
116 int tcommand_flag = 0;
117 int safer_flag = 1; // 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);
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 mode = NO_ARGS);
140 static symbol read_long_escape_name(read_mode mode = NO_ARGS);
141 static void interpolate_string(symbol);
142 static void interpolate_string_with_args(symbol);
143 static void interpolate_macro(symbol);
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 class input_iterator {
203 public:
204 input_iterator();
205 input_iterator(int is_div);
206 virtual ~input_iterator() {}
207 int get(node **);
208 friend class input_stack;
209 int is_diversion;
210 statem *diversion_state;
211 protected:
212 const unsigned char *ptr;
213 const unsigned char *eptr;
214 input_iterator *next;
215 private:
216 virtual int fill(node **);
217 virtual int peek();
218 virtual int has_args() { return 0; }
219 virtual int nargs() { return 0; }
220 virtual input_iterator *get_arg(int) { return 0; }
221 virtual int get_location(int, const char **, int *) { return 0; }
222 virtual void backtrace() {}
223 virtual int set_location(const char *, int) { return 0; }
224 virtual int next_file(FILE *, const char *) { return 0; }
225 virtual void shift(int) {}
226 virtual int is_boundary() {return 0; }
227 virtual int is_file() { return 0; }
228 virtual int is_macro() { return 0; }
229 virtual void save_compatible_flag(int) {}
230 virtual int get_compatible_flag() { return 0; }
233 input_iterator::input_iterator()
234 : is_diversion(0), ptr(0), eptr(0)
238 input_iterator::input_iterator(int is_div)
239 : is_diversion(is_div), ptr(0), eptr(0)
243 int input_iterator::fill(node **)
245 return EOF;
248 int input_iterator::peek()
250 return EOF;
253 inline int input_iterator::get(node **p)
255 return ptr < eptr ? *ptr++ : fill(p);
258 class input_boundary : public input_iterator {
259 public:
260 int is_boundary() { return 1; }
263 class input_return_boundary : public input_iterator {
264 public:
265 int is_boundary() { return 2; }
268 class file_iterator : public input_iterator {
269 FILE *fp;
270 int lineno;
271 const char *filename;
272 int popened;
273 int newline_flag;
274 int seen_escape;
275 enum { BUF_SIZE = 512 };
276 unsigned char buf[BUF_SIZE];
277 void close();
278 public:
279 file_iterator(FILE *, const char *, int = 0);
280 ~file_iterator();
281 int fill(node **);
282 int peek();
283 int get_location(int, const char **, int *);
284 void backtrace();
285 int set_location(const char *, int);
286 int next_file(FILE *, const char *);
287 int is_file();
290 file_iterator::file_iterator(FILE *f, const char *fn, int po)
291 : fp(f), lineno(1), filename(fn), popened(po),
292 newline_flag(0), seen_escape(0)
294 if ((font::use_charnames_in_special) && (fn != 0)) {
295 if (!the_output)
296 init_output();
297 the_output->put_filename(fn);
301 file_iterator::~file_iterator()
303 close();
306 void file_iterator::close()
308 if (fp == stdin)
309 clearerr(stdin);
310 #ifndef POPEN_MISSING
311 else if (popened)
312 pclose(fp);
313 #endif /* not POPEN_MISSING */
314 else
315 fclose(fp);
318 int file_iterator::is_file()
320 return 1;
323 int file_iterator::next_file(FILE *f, const char *s)
325 close();
326 filename = s;
327 fp = f;
328 lineno = 1;
329 newline_flag = 0;
330 seen_escape = 0;
331 popened = 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 = getc(fp);
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 = getc(fp);
374 while (invalid_input_char(c)) {
375 warning(WARN_INPUT, "invalid input character code %1", int(c));
376 c = getc(fp);
378 if (c != EOF)
379 ungetc(c, fp);
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 popened ? "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);
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 int nargs();
421 static int get_location(int, const char **, int *);
422 static int set_location(const char *, int);
423 static void backtrace();
424 static void backtrace_all();
425 static void next_file(FILE *, const char *);
426 static void end_file();
427 static void shift(int n);
428 static void add_boundary();
429 static void add_return_boundary();
430 static int is_return_boundary();
431 static void remove_boundary();
432 static int get_level();
433 static int get_div_level();
434 static void increase_level();
435 static void decrease_level();
436 static void clear();
437 static void pop_macro();
438 static void save_compatible_flag(int);
439 static int get_compatible_flag();
440 static statem *get_diversion_state();
441 static void check_end_diversion(input_iterator *t);
442 static int limit;
443 static int div_level;
444 static statem *diversion_state;
445 private:
446 static input_iterator *top;
447 static int level;
448 static int finish_get(node **);
449 static int finish_peek();
452 input_iterator *input_stack::top = &nil_iterator;
453 int input_stack::level = 0;
454 int input_stack::limit = DEFAULT_INPUT_STACK_LIMIT;
455 int input_stack::div_level = 0;
456 statem *input_stack::diversion_state = NULL;
457 int suppress_push=0;
460 inline int input_stack::get_level()
462 return level;
465 inline void input_stack::increase_level()
467 level++;
470 inline void input_stack::decrease_level()
472 level--;
475 inline int input_stack::get_div_level()
477 return div_level;
480 inline int input_stack::get(node **np)
482 int res = (top->ptr < top->eptr) ? *top->ptr++ : finish_get(np);
483 if (res == '\n') {
484 old_have_input = have_input;
485 have_input = 0;
487 return res;
490 int input_stack::finish_get(node **np)
492 for (;;) {
493 int c = top->fill(np);
494 if (c != EOF || top->is_boundary())
495 return c;
496 if (top == &nil_iterator)
497 break;
498 input_iterator *tem = top;
499 check_end_diversion(tem);
500 #if defined(DEBUGGING)
501 if (debug_state)
502 if (tem->is_diversion)
503 fprintf(stderr,
504 "in diversion level = %d\n", input_stack::get_div_level());
505 #endif
506 top = top->next;
507 level--;
508 delete tem;
509 if (top->ptr < top->eptr)
510 return *top->ptr++;
512 assert(level == 0);
513 return EOF;
516 inline int input_stack::peek()
518 return (top->ptr < top->eptr) ? *top->ptr : finish_peek();
521 void input_stack::check_end_diversion(input_iterator *t)
523 if (t->is_diversion) {
524 div_level--;
525 diversion_state = t->diversion_state;
529 int input_stack::finish_peek()
531 for (;;) {
532 int c = top->peek();
533 if (c != EOF || top->is_boundary())
534 return c;
535 if (top == &nil_iterator)
536 break;
537 input_iterator *tem = top;
538 check_end_diversion(tem);
539 top = top->next;
540 level--;
541 delete tem;
542 if (top->ptr < top->eptr)
543 return *top->ptr;
545 assert(level == 0);
546 return EOF;
549 void input_stack::add_boundary()
551 push(new input_boundary);
554 void input_stack::add_return_boundary()
556 push(new input_return_boundary);
559 int input_stack::is_return_boundary()
561 return top->is_boundary() == 2;
564 void input_stack::remove_boundary()
566 assert(top->is_boundary());
567 input_iterator *temp = top->next;
568 check_end_diversion(top);
570 delete top;
571 top = temp;
572 level--;
575 void input_stack::push(input_iterator *in)
577 if (in == 0)
578 return;
579 if (++level > limit && limit > 0)
580 fatal("input stack limit exceeded (probable infinite loop)");
581 in->next = top;
582 top = in;
583 if (top->is_diversion) {
584 div_level++;
585 in->diversion_state = diversion_state;
586 diversion_state = curenv->construct_state(0);
587 #if defined(DEBUGGING)
588 if (debug_state) {
589 curenv->dump_troff_state();
590 fflush(stderr);
592 #endif
594 #if defined(DEBUGGING)
595 if (debug_state)
596 if (top->is_diversion) {
597 fprintf(stderr,
598 "in diversion level = %d\n", input_stack::get_div_level());
599 fflush(stderr);
601 #endif
604 statem *get_diversion_state()
606 return input_stack::get_diversion_state();
609 statem *input_stack::get_diversion_state()
611 if (diversion_state == NULL)
612 return NULL;
613 else
614 return new statem(diversion_state);
617 input_iterator *input_stack::get_arg(int i)
619 input_iterator *p;
620 for (p = top; p != 0; p = p->next)
621 if (p->has_args())
622 return p->get_arg(i);
623 return 0;
626 void input_stack::shift(int n)
628 for (input_iterator *p = top; p; p = p->next)
629 if (p->has_args()) {
630 p->shift(n);
631 return;
635 int input_stack::nargs()
637 for (input_iterator *p =top; p != 0; p = p->next)
638 if (p->has_args())
639 return p->nargs();
640 return 0;
643 int input_stack::get_location(int allow_macro, const char **filenamep, int *linenop)
645 for (input_iterator *p = top; p; p = p->next)
646 if (p->get_location(allow_macro, filenamep, linenop))
647 return 1;
648 return 0;
651 void input_stack::backtrace()
653 const char *f;
654 int n;
655 // only backtrace down to (not including) the topmost file
656 for (input_iterator *p = top;
657 p && !p->get_location(0, &f, &n);
658 p = p->next)
659 p->backtrace();
662 void input_stack::backtrace_all()
664 for (input_iterator *p = top; p; p = p->next)
665 p->backtrace();
668 int input_stack::set_location(const char *filename, int lineno)
670 for (input_iterator *p = top; p; p = p->next)
671 if (p->set_location(filename, lineno))
672 return 1;
673 return 0;
676 void input_stack::next_file(FILE *fp, const char *s)
678 input_iterator **pp;
679 for (pp = &top; *pp != &nil_iterator; pp = &(*pp)->next)
680 if ((*pp)->next_file(fp, s))
681 return;
682 if (++level > limit && limit > 0)
683 fatal("input stack limit exceeded");
684 *pp = new file_iterator(fp, s);
685 (*pp)->next = &nil_iterator;
688 void input_stack::end_file()
690 for (input_iterator **pp = &top; *pp != &nil_iterator; pp = &(*pp)->next)
691 if ((*pp)->is_file()) {
692 input_iterator *tem = *pp;
693 check_end_diversion(tem);
694 *pp = (*pp)->next;
695 delete tem;
696 level--;
697 return;
701 void input_stack::clear()
703 int nboundaries = 0;
704 while (top != &nil_iterator) {
705 if (top->is_boundary())
706 nboundaries++;
707 input_iterator *tem = top;
708 check_end_diversion(tem);
709 top = top->next;
710 level--;
711 delete tem;
713 // Keep while_request happy.
714 for (; nboundaries > 0; --nboundaries)
715 add_return_boundary();
718 void input_stack::pop_macro()
720 int nboundaries = 0;
721 int is_macro = 0;
722 do {
723 if (top->next == &nil_iterator)
724 break;
725 if (top->is_boundary())
726 nboundaries++;
727 is_macro = top->is_macro();
728 input_iterator *tem = top;
729 check_end_diversion(tem);
730 top = top->next;
731 level--;
732 delete tem;
733 } while (!is_macro);
734 // Keep while_request happy.
735 for (; nboundaries > 0; --nboundaries)
736 add_return_boundary();
739 inline void input_stack::save_compatible_flag(int f)
741 top->save_compatible_flag(f);
744 inline int input_stack::get_compatible_flag()
746 return top->get_compatible_flag();
749 void backtrace_request()
751 input_stack::backtrace_all();
752 fflush(stderr);
753 skip_line();
756 void next_file()
758 symbol nm = get_long_name();
759 while (!tok.newline() && !tok.eof())
760 tok.next();
761 if (nm.is_null())
762 input_stack::end_file();
763 else {
764 errno = 0;
765 FILE *fp = include_search_path.open_file_cautious(nm.contents());
766 if (!fp)
767 error("can't open `%1': %2", nm.contents(), strerror(errno));
768 else
769 input_stack::next_file(fp, nm.contents());
771 tok.next();
774 void shift()
776 int n;
777 if (!has_arg() || !get_integer(&n))
778 n = 1;
779 input_stack::shift(n);
780 skip_line();
783 static char get_char_for_escape_name(int allow_space = 0)
785 int c = get_copy(0);
786 switch (c) {
787 case EOF:
788 copy_mode_error("end of input in escape name");
789 return '\0';
790 default:
791 if (!invalid_input_char(c))
792 break;
793 // fall through
794 case '\n':
795 if (c == '\n')
796 input_stack::push(make_temp_iterator("\n"));
797 // fall through
798 case ' ':
799 if (c == ' ' && allow_space)
800 break;
801 // fall through
802 case '\t':
803 case '\001':
804 case '\b':
805 copy_mode_error("%1 is not allowed in an escape name",
806 input_char_description(c));
807 return '\0';
809 return c;
812 static symbol read_two_char_escape_name()
814 char buf[3];
815 buf[0] = get_char_for_escape_name();
816 if (buf[0] != '\0') {
817 buf[1] = get_char_for_escape_name();
818 if (buf[1] == '\0')
819 buf[0] = 0;
820 else
821 buf[2] = 0;
823 return symbol(buf);
826 static symbol read_long_escape_name(read_mode mode)
828 int start_level = input_stack::get_level();
829 char abuf[ABUF_SIZE];
830 char *buf = abuf;
831 int buf_size = ABUF_SIZE;
832 int i = 0;
833 char c;
834 int have_char = 0;
835 for (;;) {
836 c = get_char_for_escape_name(have_char && mode == WITH_ARGS);
837 if (c == 0) {
838 if (buf != abuf)
839 a_delete buf;
840 return NULL_SYMBOL;
842 have_char = 1;
843 if (mode == WITH_ARGS && c == ' ')
844 break;
845 if (i + 2 > buf_size) {
846 if (buf == abuf) {
847 buf = new char[ABUF_SIZE*2];
848 memcpy(buf, abuf, buf_size);
849 buf_size = ABUF_SIZE*2;
851 else {
852 char *old_buf = buf;
853 buf = new char[buf_size*2];
854 memcpy(buf, old_buf, buf_size);
855 buf_size *= 2;
856 a_delete old_buf;
859 if (c == ']' && input_stack::get_level() == start_level)
860 break;
861 buf[i++] = c;
863 buf[i] = 0;
864 if (c == ' ')
865 have_string_arg = 1;
866 if (buf == abuf) {
867 if (i == 0) {
868 if (mode != ALLOW_EMPTY)
869 copy_mode_error("empty escape name");
870 return EMPTY_SYMBOL;
872 return symbol(abuf);
874 else {
875 symbol s(buf);
876 a_delete buf;
877 return s;
881 static symbol read_escape_name(read_mode mode)
883 char c = get_char_for_escape_name();
884 if (c == 0)
885 return NULL_SYMBOL;
886 if (c == '(')
887 return read_two_char_escape_name();
888 if (c == '[' && !compatible_flag)
889 return read_long_escape_name(mode);
890 char buf[2];
891 buf[0] = c;
892 buf[1] = '\0';
893 return symbol(buf);
896 static symbol read_increment_and_escape_name(int *incp)
898 char c = get_char_for_escape_name();
899 switch (c) {
900 case 0:
901 *incp = 0;
902 return NULL_SYMBOL;
903 case '(':
904 *incp = 0;
905 return read_two_char_escape_name();
906 case '+':
907 *incp = 1;
908 return read_escape_name();
909 case '-':
910 *incp = -1;
911 return read_escape_name();
912 case '[':
913 if (!compatible_flag) {
914 *incp = 0;
915 return read_long_escape_name();
917 break;
919 *incp = 0;
920 char buf[2];
921 buf[0] = c;
922 buf[1] = '\0';
923 return symbol(buf);
926 static int get_copy(node **nd, int defining)
928 for (;;) {
929 int c = input_stack::get(nd);
930 if (c == PUSH_GROFF_MODE) {
931 input_stack::save_compatible_flag(compatible_flag);
932 compatible_flag = 0;
933 continue;
935 if (c == PUSH_COMP_MODE) {
936 input_stack::save_compatible_flag(compatible_flag);
937 compatible_flag = 1;
938 continue;
940 if (c == POP_GROFFCOMP_MODE) {
941 compatible_flag = input_stack::get_compatible_flag();
942 continue;
944 if (c == BEGIN_QUOTE) {
945 input_stack::increase_level();
946 continue;
948 if (c == END_QUOTE) {
949 input_stack::decrease_level();
950 continue;
952 if (c == ESCAPE_NEWLINE) {
953 if (defining)
954 return c;
955 do {
956 c = input_stack::get(nd);
957 } while (c == ESCAPE_NEWLINE);
959 if (c != escape_char || escape_char <= 0)
960 return c;
961 c = input_stack::peek();
962 switch(c) {
963 case 0:
964 return escape_char;
965 case '"':
966 (void)input_stack::get(0);
967 while ((c = input_stack::get(0)) != '\n' && c != EOF)
969 return c;
970 case '#': // Like \" but newline is ignored.
971 (void)input_stack::get(0);
972 while ((c = input_stack::get(0)) != '\n')
973 if (c == EOF)
974 return EOF;
975 break;
976 case '$':
978 (void)input_stack::get(0);
979 symbol s = read_escape_name();
980 if (!(s.is_null() || s.is_empty()))
981 interpolate_arg(s);
982 break;
984 case '*':
986 (void)input_stack::get(0);
987 symbol s = read_escape_name(WITH_ARGS);
988 if (!(s.is_null() || s.is_empty())) {
989 if (have_string_arg) {
990 have_string_arg = 0;
991 interpolate_string_with_args(s);
993 else
994 interpolate_string(s);
996 break;
998 case 'a':
999 (void)input_stack::get(0);
1000 return '\001';
1001 case 'e':
1002 (void)input_stack::get(0);
1003 return ESCAPE_e;
1004 case 'E':
1005 (void)input_stack::get(0);
1006 return ESCAPE_E;
1007 case 'n':
1009 (void)input_stack::get(0);
1010 int inc;
1011 symbol s = read_increment_and_escape_name(&inc);
1012 if (!(s.is_null() || s.is_empty()))
1013 interpolate_number_reg(s, inc);
1014 break;
1016 case 'g':
1018 (void)input_stack::get(0);
1019 symbol s = read_escape_name();
1020 if (!(s.is_null() || s.is_empty()))
1021 interpolate_number_format(s);
1022 break;
1024 case 't':
1025 (void)input_stack::get(0);
1026 return '\t';
1027 case 'V':
1029 (void)input_stack::get(0);
1030 symbol s = read_escape_name();
1031 if (!(s.is_null() || s.is_empty()))
1032 interpolate_environment_variable(s);
1033 break;
1035 case '\n':
1036 (void)input_stack::get(0);
1037 if (defining)
1038 return ESCAPE_NEWLINE;
1039 break;
1040 case ' ':
1041 (void)input_stack::get(0);
1042 return ESCAPE_SPACE;
1043 case '~':
1044 (void)input_stack::get(0);
1045 return ESCAPE_TILDE;
1046 case ':':
1047 (void)input_stack::get(0);
1048 return ESCAPE_COLON;
1049 case '|':
1050 (void)input_stack::get(0);
1051 return ESCAPE_BAR;
1052 case '^':
1053 (void)input_stack::get(0);
1054 return ESCAPE_CIRCUMFLEX;
1055 case '{':
1056 (void)input_stack::get(0);
1057 return ESCAPE_LEFT_BRACE;
1058 case '}':
1059 (void)input_stack::get(0);
1060 return ESCAPE_RIGHT_BRACE;
1061 case '`':
1062 (void)input_stack::get(0);
1063 return ESCAPE_LEFT_QUOTE;
1064 case '\'':
1065 (void)input_stack::get(0);
1066 return ESCAPE_RIGHT_QUOTE;
1067 case '-':
1068 (void)input_stack::get(0);
1069 return ESCAPE_HYPHEN;
1070 case '_':
1071 (void)input_stack::get(0);
1072 return ESCAPE_UNDERSCORE;
1073 case 'c':
1074 (void)input_stack::get(0);
1075 return ESCAPE_c;
1076 case '!':
1077 (void)input_stack::get(0);
1078 return ESCAPE_BANG;
1079 case '?':
1080 (void)input_stack::get(0);
1081 return ESCAPE_QUESTION;
1082 case '&':
1083 (void)input_stack::get(0);
1084 return ESCAPE_AMPERSAND;
1085 case ')':
1086 (void)input_stack::get(0);
1087 return ESCAPE_RIGHT_PARENTHESIS;
1088 case '.':
1089 (void)input_stack::get(0);
1090 return c;
1091 case '%':
1092 (void)input_stack::get(0);
1093 return ESCAPE_PERCENT;
1094 default:
1095 if (c == escape_char) {
1096 (void)input_stack::get(0);
1097 return c;
1099 else
1100 return escape_char;
1105 class non_interpreted_char_node : public node {
1106 unsigned char c;
1107 public:
1108 non_interpreted_char_node(unsigned char);
1109 node *copy();
1110 int interpret(macro *);
1111 int same(node *);
1112 const char *type();
1113 int force_tprint();
1114 int is_tag();
1117 int non_interpreted_char_node::same(node *nd)
1119 return c == ((non_interpreted_char_node *)nd)->c;
1122 const char *non_interpreted_char_node::type()
1124 return "non_interpreted_char_node";
1127 int non_interpreted_char_node::force_tprint()
1129 return 0;
1132 int non_interpreted_char_node::is_tag()
1134 return 0;
1137 non_interpreted_char_node::non_interpreted_char_node(unsigned char n) : c(n)
1139 assert(n != 0);
1142 node *non_interpreted_char_node::copy()
1144 return new non_interpreted_char_node(c);
1147 int non_interpreted_char_node::interpret(macro *mac)
1149 mac->append(c);
1150 return 1;
1153 static void do_width();
1154 static node *do_non_interpreted();
1155 static node *do_special();
1156 static node *do_suppress(symbol nm);
1157 static void do_register();
1159 dictionary color_dictionary(501);
1161 static color *lookup_color(symbol nm)
1163 assert(!nm.is_null());
1164 if (nm == default_symbol)
1165 return &default_color;
1166 color *c = (color *)color_dictionary.lookup(nm);
1167 if (c == 0)
1168 warning(WARN_COLOR, "color `%1' not defined", nm.contents());
1169 return c;
1172 void do_glyph_color(symbol nm)
1174 if (nm.is_null())
1175 return;
1176 if (nm.is_empty())
1177 curenv->set_glyph_color(curenv->get_prev_glyph_color());
1178 else {
1179 color *tem = lookup_color(nm);
1180 if (tem)
1181 curenv->set_glyph_color(tem);
1182 else
1183 (void)color_dictionary.lookup(nm, new color(nm));
1187 void do_fill_color(symbol nm)
1189 if (nm.is_null())
1190 return;
1191 if (nm.is_empty())
1192 curenv->set_fill_color(curenv->get_prev_fill_color());
1193 else {
1194 color *tem = lookup_color(nm);
1195 if (tem)
1196 curenv->set_fill_color(tem);
1197 else
1198 (void)color_dictionary.lookup(nm, new color(nm));
1202 static unsigned int get_color_element(const char *scheme, const char *col)
1204 units val;
1205 if (!get_number(&val, 'f')) {
1206 warning(WARN_COLOR, "%1 in %2 definition set to 0", col, scheme);
1207 tok.next();
1208 return 0;
1210 if (val < 0) {
1211 warning(WARN_RANGE, "%1 cannot be negative: set to 0", col);
1212 return 0;
1214 if (val > color::MAX_COLOR_VAL+1) {
1215 warning(WARN_RANGE, "%1 cannot be greater than 1", col);
1216 // we change 0x10000 to 0xffff
1217 return color::MAX_COLOR_VAL;
1219 return (unsigned int)val;
1222 static color *read_rgb(char end = 0)
1224 symbol component = do_get_long_name(0, end);
1225 if (component.is_null()) {
1226 warning(WARN_COLOR, "missing rgb color values");
1227 return 0;
1229 const char *s = component.contents();
1230 color *col = new color;
1231 if (*s == '#') {
1232 if (!col->read_rgb(s)) {
1233 warning(WARN_COLOR, "expecting rgb color definition not `%1'", s);
1234 delete col;
1235 return 0;
1238 else {
1239 if (!end)
1240 input_stack::push(make_temp_iterator(" "));
1241 input_stack::push(make_temp_iterator(s));
1242 tok.next();
1243 unsigned int r = get_color_element("rgb color", "red component");
1244 unsigned int g = get_color_element("rgb color", "green component");
1245 unsigned int b = get_color_element("rgb color", "blue component");
1246 col->set_rgb(r, g, b);
1248 return col;
1251 static color *read_cmy(char end = 0)
1253 symbol component = do_get_long_name(0, end);
1254 if (component.is_null()) {
1255 warning(WARN_COLOR, "missing cmy color values");
1256 return 0;
1258 const char *s = component.contents();
1259 color *col = new color;
1260 if (*s == '#') {
1261 if (!col->read_cmy(s)) {
1262 warning(WARN_COLOR, "expecting cmy color definition not `%1'", s);
1263 delete col;
1264 return 0;
1267 else {
1268 if (!end)
1269 input_stack::push(make_temp_iterator(" "));
1270 input_stack::push(make_temp_iterator(s));
1271 tok.next();
1272 unsigned int c = get_color_element("cmy color", "cyan component");
1273 unsigned int m = get_color_element("cmy color", "magenta component");
1274 unsigned int y = get_color_element("cmy color", "yellow component");
1275 col->set_cmy(c, m, y);
1277 return col;
1280 static color *read_cmyk(char end = 0)
1282 symbol component = do_get_long_name(0, end);
1283 if (component.is_null()) {
1284 warning(WARN_COLOR, "missing cmyk color values");
1285 return 0;
1287 const char *s = component.contents();
1288 color *col = new color;
1289 if (*s == '#') {
1290 if (!col->read_cmyk(s)) {
1291 warning(WARN_COLOR, "`expecting a cmyk color definition not `%1'", s);
1292 delete col;
1293 return 0;
1296 else {
1297 if (!end)
1298 input_stack::push(make_temp_iterator(" "));
1299 input_stack::push(make_temp_iterator(s));
1300 tok.next();
1301 unsigned int c = get_color_element("cmyk color", "cyan component");
1302 unsigned int m = get_color_element("cmyk color", "magenta component");
1303 unsigned int y = get_color_element("cmyk color", "yellow component");
1304 unsigned int k = get_color_element("cmyk color", "black component");
1305 col->set_cmyk(c, m, y, k);
1307 return col;
1310 static color *read_gray(char end = 0)
1312 symbol component = do_get_long_name(0, end);
1313 if (component.is_null()) {
1314 warning(WARN_COLOR, "missing gray values");
1315 return 0;
1317 const char *s = component.contents();
1318 color *col = new color;
1319 if (*s == '#') {
1320 if (!col->read_gray(s)) {
1321 warning(WARN_COLOR, "`expecting a gray definition not `%1'", s);
1322 delete col;
1323 return 0;
1326 else {
1327 if (!end)
1328 input_stack::push(make_temp_iterator("\n"));
1329 input_stack::push(make_temp_iterator(s));
1330 tok.next();
1331 unsigned int g = get_color_element("gray", "gray value");
1332 col->set_gray(g);
1334 return col;
1337 static void activate_color()
1339 int n;
1340 if (has_arg() && get_integer(&n))
1341 color_flag = n != 0;
1342 else
1343 color_flag = 1;
1344 skip_line();
1347 static void define_color()
1349 symbol color_name = get_long_name(1);
1350 if (color_name.is_null()) {
1351 skip_line();
1352 return;
1354 if (color_name == default_symbol) {
1355 warning(WARN_COLOR, "default color can't be redefined");
1356 skip_line();
1357 return;
1359 symbol style = get_long_name(1);
1360 if (style.is_null()) {
1361 skip_line();
1362 return;
1364 color *col;
1365 if (strcmp(style.contents(), "rgb") == 0)
1366 col = read_rgb();
1367 else if (strcmp(style.contents(), "cmyk") == 0)
1368 col = read_cmyk();
1369 else if (strcmp(style.contents(), "gray") == 0)
1370 col = read_gray();
1371 else if (strcmp(style.contents(), "grey") == 0)
1372 col = read_gray();
1373 else if (strcmp(style.contents(), "cmy") == 0)
1374 col = read_cmy();
1375 else {
1376 warning(WARN_COLOR,
1377 "unknown color space `%1'; use rgb, cmyk, gray or cmy",
1378 style.contents());
1379 skip_line();
1380 return;
1382 if (col) {
1383 col->nm = color_name;
1384 (void)color_dictionary.lookup(color_name, col);
1386 skip_line();
1389 static node *do_overstrike()
1391 token start;
1392 overstrike_node *on = new overstrike_node;
1393 int start_level = input_stack::get_level();
1394 start.next();
1395 for (;;) {
1396 tok.next();
1397 if (tok.newline() || tok.eof()) {
1398 warning(WARN_DELIM, "missing closing delimiter");
1399 input_stack::push(make_temp_iterator("\n"));
1400 break;
1402 if (tok == start
1403 && (compatible_flag || input_stack::get_level() == start_level))
1404 break;
1405 charinfo *ci = tok.get_char(1);
1406 if (ci) {
1407 node *n = curenv->make_char_node(ci);
1408 if (n)
1409 on->overstrike(n);
1412 return on;
1415 static node *do_bracket()
1417 token start;
1418 bracket_node *bn = new bracket_node;
1419 start.next();
1420 int start_level = input_stack::get_level();
1421 for (;;) {
1422 tok.next();
1423 if (tok.eof()) {
1424 warning(WARN_DELIM, "missing closing delimiter");
1425 break;
1427 if (tok.newline()) {
1428 warning(WARN_DELIM, "missing closing delimiter");
1429 input_stack::push(make_temp_iterator("\n"));
1430 break;
1432 if (tok == start
1433 && (compatible_flag || input_stack::get_level() == start_level))
1434 break;
1435 charinfo *ci = tok.get_char(1);
1436 if (ci) {
1437 node *n = curenv->make_char_node(ci);
1438 if (n)
1439 bn->bracket(n);
1442 return bn;
1445 static int do_name_test()
1447 token start;
1448 start.next();
1449 int start_level = input_stack::get_level();
1450 int bad_char = 0;
1451 int some_char = 0;
1452 for (;;) {
1453 tok.next();
1454 if (tok.newline() || tok.eof()) {
1455 warning(WARN_DELIM, "missing closing delimiter");
1456 input_stack::push(make_temp_iterator("\n"));
1457 break;
1459 if (tok == start
1460 && (compatible_flag || input_stack::get_level() == start_level))
1461 break;
1462 if (!tok.ch())
1463 bad_char = 1;
1464 some_char = 1;
1466 return some_char && !bad_char;
1469 static int do_expr_test()
1471 token start;
1472 start.next();
1473 int start_level = input_stack::get_level();
1474 if (!start.delimiter(1))
1475 return 0;
1476 tok.next();
1477 // disable all warning and error messages temporarily
1478 int saved_warning_mask = warning_mask;
1479 int saved_inhibit_errors = inhibit_errors;
1480 warning_mask = 0;
1481 inhibit_errors = 1;
1482 int dummy;
1483 int result = get_number_rigidly(&dummy, 'u');
1484 warning_mask = saved_warning_mask;
1485 inhibit_errors = saved_inhibit_errors;
1486 if (tok == start && input_stack::get_level() == start_level)
1487 return result;
1488 // ignore everything up to the delimiter in case we aren't right there
1489 for (;;) {
1490 tok.next();
1491 if (tok.newline() || tok.eof()) {
1492 warning(WARN_DELIM, "missing closing delimiter");
1493 input_stack::push(make_temp_iterator("\n"));
1494 break;
1496 if (tok == start && input_stack::get_level() == start_level)
1497 break;
1499 return 0;
1502 #if 0
1503 static node *do_zero_width()
1505 token start;
1506 start.next();
1507 int start_level = input_stack::get_level();
1508 environment env(curenv);
1509 environment *oldenv = curenv;
1510 curenv = &env;
1511 for (;;) {
1512 tok.next();
1513 if (tok.newline() || tok.eof()) {
1514 error("missing closing delimiter");
1515 break;
1517 if (tok == start
1518 && (compatible_flag || input_stack::get_level() == start_level))
1519 break;
1520 tok.process();
1522 curenv = oldenv;
1523 node *rev = env.extract_output_line();
1524 node *n = 0;
1525 while (rev) {
1526 node *tem = rev;
1527 rev = rev->next;
1528 tem->next = n;
1529 n = tem;
1531 return new zero_width_node(n);
1534 #else
1536 // It's undesirable for \Z to change environments, because then
1537 // \n(.w won't work as expected.
1539 static node *do_zero_width()
1541 node *rev = new dummy_node;
1542 token start;
1543 start.next();
1544 int start_level = input_stack::get_level();
1545 for (;;) {
1546 tok.next();
1547 if (tok.newline() || tok.eof()) {
1548 warning(WARN_DELIM, "missing closing delimiter");
1549 input_stack::push(make_temp_iterator("\n"));
1550 break;
1552 if (tok == start
1553 && (compatible_flag || input_stack::get_level() == start_level))
1554 break;
1555 if (!tok.add_to_node_list(&rev))
1556 error("invalid token in argument to \\Z");
1558 node *n = 0;
1559 while (rev) {
1560 node *tem = rev;
1561 rev = rev->next;
1562 tem->next = n;
1563 n = tem;
1565 return new zero_width_node(n);
1568 #endif
1570 token_node *node::get_token_node()
1572 return 0;
1575 class token_node : public node {
1576 public:
1577 token tk;
1578 token_node(const token &t);
1579 node *copy();
1580 token_node *get_token_node();
1581 int same(node *);
1582 const char *type();
1583 int force_tprint();
1584 int is_tag();
1587 token_node::token_node(const token &t) : tk(t)
1591 node *token_node::copy()
1593 return new token_node(tk);
1596 token_node *token_node::get_token_node()
1598 return this;
1601 int token_node::same(node *nd)
1603 return tk == ((token_node *)nd)->tk;
1606 const char *token_node::type()
1608 return "token_node";
1611 int token_node::force_tprint()
1613 return 0;
1616 int token_node::is_tag()
1618 return 0;
1621 token::token() : nd(0), type(TOKEN_EMPTY)
1625 token::~token()
1627 delete nd;
1630 token::token(const token &t)
1631 : nm(t.nm), c(t.c), val(t.val), dim(t.dim), type(t.type)
1633 // Use two statements to work around bug in SGI C++.
1634 node *tem = t.nd;
1635 nd = tem ? tem->copy() : 0;
1638 void token::operator=(const token &t)
1640 delete nd;
1641 nm = t.nm;
1642 // Use two statements to work around bug in SGI C++.
1643 node *tem = t.nd;
1644 nd = tem ? tem->copy() : 0;
1645 c = t.c;
1646 val = t.val;
1647 dim = t.dim;
1648 type = t.type;
1651 void token::skip()
1653 while (space())
1654 next();
1657 int has_arg()
1659 while (tok.space())
1660 tok.next();
1661 return !tok.newline();
1664 void token::make_space()
1666 type = TOKEN_SPACE;
1669 void token::make_newline()
1671 type = TOKEN_NEWLINE;
1674 void token::next()
1676 if (nd) {
1677 delete nd;
1678 nd = 0;
1680 units x;
1681 for (;;) {
1682 node *n = 0;
1683 int cc = input_stack::get(&n);
1684 if (cc != escape_char || escape_char == 0) {
1685 handle_normal_char:
1686 switch(cc) {
1687 case PUSH_GROFF_MODE:
1688 input_stack::save_compatible_flag(compatible_flag);
1689 compatible_flag = 0;
1690 continue;
1691 case PUSH_COMP_MODE:
1692 input_stack::save_compatible_flag(compatible_flag);
1693 compatible_flag = 1;
1694 continue;
1695 case POP_GROFFCOMP_MODE:
1696 compatible_flag = input_stack::get_compatible_flag();
1697 continue;
1698 case BEGIN_QUOTE:
1699 input_stack::increase_level();
1700 continue;
1701 case END_QUOTE:
1702 input_stack::decrease_level();
1703 continue;
1704 case EOF:
1705 type = TOKEN_EOF;
1706 return;
1707 case TRANSPARENT_FILE_REQUEST:
1708 case TITLE_REQUEST:
1709 case COPY_FILE_REQUEST:
1710 #ifdef COLUMN
1711 case VJUSTIFY_REQUEST:
1712 #endif /* COLUMN */
1713 type = TOKEN_REQUEST;
1714 c = cc;
1715 return;
1716 case BEGIN_TRAP:
1717 type = TOKEN_BEGIN_TRAP;
1718 return;
1719 case END_TRAP:
1720 type = TOKEN_END_TRAP;
1721 return;
1722 case LAST_PAGE_EJECTOR:
1723 seen_last_page_ejector = 1;
1724 // fall through
1725 case PAGE_EJECTOR:
1726 type = TOKEN_PAGE_EJECTOR;
1727 return;
1728 case ESCAPE_PERCENT:
1729 ESCAPE_PERCENT:
1730 type = TOKEN_HYPHEN_INDICATOR;
1731 return;
1732 case ESCAPE_SPACE:
1733 ESCAPE_SPACE:
1734 type = TOKEN_UNSTRETCHABLE_SPACE;
1735 return;
1736 case ESCAPE_TILDE:
1737 ESCAPE_TILDE:
1738 type = TOKEN_STRETCHABLE_SPACE;
1739 return;
1740 case ESCAPE_COLON:
1741 ESCAPE_COLON:
1742 type = TOKEN_ZERO_WIDTH_BREAK;
1743 return;
1744 case ESCAPE_e:
1745 ESCAPE_e:
1746 type = TOKEN_ESCAPE;
1747 return;
1748 case ESCAPE_E:
1749 goto handle_escape_char;
1750 case ESCAPE_BAR:
1751 ESCAPE_BAR:
1752 type = TOKEN_NODE;
1753 nd = new hmotion_node(curenv->get_narrow_space_width(),
1754 curenv->get_fill_color());
1755 return;
1756 case ESCAPE_CIRCUMFLEX:
1757 ESCAPE_CIRCUMFLEX:
1758 type = TOKEN_NODE;
1759 nd = new hmotion_node(curenv->get_half_narrow_space_width(),
1760 curenv->get_fill_color());
1761 return;
1762 case ESCAPE_NEWLINE:
1763 have_input = 0;
1764 break;
1765 case ESCAPE_LEFT_BRACE:
1766 ESCAPE_LEFT_BRACE:
1767 type = TOKEN_LEFT_BRACE;
1768 return;
1769 case ESCAPE_RIGHT_BRACE:
1770 ESCAPE_RIGHT_BRACE:
1771 type = TOKEN_RIGHT_BRACE;
1772 return;
1773 case ESCAPE_LEFT_QUOTE:
1774 ESCAPE_LEFT_QUOTE:
1775 type = TOKEN_SPECIAL;
1776 nm = symbol("ga");
1777 return;
1778 case ESCAPE_RIGHT_QUOTE:
1779 ESCAPE_RIGHT_QUOTE:
1780 type = TOKEN_SPECIAL;
1781 nm = symbol("aa");
1782 return;
1783 case ESCAPE_HYPHEN:
1784 ESCAPE_HYPHEN:
1785 type = TOKEN_SPECIAL;
1786 nm = symbol("-");
1787 return;
1788 case ESCAPE_UNDERSCORE:
1789 ESCAPE_UNDERSCORE:
1790 type = TOKEN_SPECIAL;
1791 nm = symbol("ul");
1792 return;
1793 case ESCAPE_c:
1794 ESCAPE_c:
1795 type = TOKEN_INTERRUPT;
1796 return;
1797 case ESCAPE_BANG:
1798 ESCAPE_BANG:
1799 type = TOKEN_TRANSPARENT;
1800 return;
1801 case ESCAPE_QUESTION:
1802 ESCAPE_QUESTION:
1803 nd = do_non_interpreted();
1804 if (nd) {
1805 type = TOKEN_NODE;
1806 return;
1808 break;
1809 case ESCAPE_AMPERSAND:
1810 ESCAPE_AMPERSAND:
1811 type = TOKEN_DUMMY;
1812 return;
1813 case ESCAPE_RIGHT_PARENTHESIS:
1814 ESCAPE_RIGHT_PARENTHESIS:
1815 type = TOKEN_TRANSPARENT_DUMMY;
1816 return;
1817 case '\b':
1818 type = TOKEN_BACKSPACE;
1819 return;
1820 case ' ':
1821 type = TOKEN_SPACE;
1822 return;
1823 case '\t':
1824 type = TOKEN_TAB;
1825 return;
1826 case '\n':
1827 type = TOKEN_NEWLINE;
1828 return;
1829 case '\001':
1830 type = TOKEN_LEADER;
1831 return;
1832 case 0:
1834 assert(n != 0);
1835 token_node *tn = n->get_token_node();
1836 if (tn) {
1837 *this = tn->tk;
1838 delete tn;
1840 else {
1841 nd = n;
1842 type = TOKEN_NODE;
1845 return;
1846 default:
1847 type = TOKEN_CHAR;
1848 c = cc;
1849 return;
1852 else {
1853 handle_escape_char:
1854 cc = input_stack::get(&n);
1855 switch(cc) {
1856 case '(':
1857 nm = read_two_char_escape_name();
1858 type = TOKEN_SPECIAL;
1859 return;
1860 case EOF:
1861 type = TOKEN_EOF;
1862 error("end of input after escape character");
1863 return;
1864 case '`':
1865 goto ESCAPE_LEFT_QUOTE;
1866 case '\'':
1867 goto ESCAPE_RIGHT_QUOTE;
1868 case '-':
1869 goto ESCAPE_HYPHEN;
1870 case '_':
1871 goto ESCAPE_UNDERSCORE;
1872 case '%':
1873 goto ESCAPE_PERCENT;
1874 case ' ':
1875 goto ESCAPE_SPACE;
1876 case '0':
1877 nd = new hmotion_node(curenv->get_digit_width(),
1878 curenv->get_fill_color());
1879 type = TOKEN_NODE;
1880 return;
1881 case '|':
1882 goto ESCAPE_BAR;
1883 case '^':
1884 goto ESCAPE_CIRCUMFLEX;
1885 case '/':
1886 type = TOKEN_ITALIC_CORRECTION;
1887 return;
1888 case ',':
1889 type = TOKEN_NODE;
1890 nd = new left_italic_corrected_node;
1891 return;
1892 case '&':
1893 goto ESCAPE_AMPERSAND;
1894 case ')':
1895 goto ESCAPE_RIGHT_PARENTHESIS;
1896 case '!':
1897 goto ESCAPE_BANG;
1898 case '?':
1899 goto ESCAPE_QUESTION;
1900 case '~':
1901 goto ESCAPE_TILDE;
1902 case ':':
1903 goto ESCAPE_COLON;
1904 case '"':
1905 while ((cc = input_stack::get(0)) != '\n' && cc != EOF)
1907 if (cc == '\n')
1908 type = TOKEN_NEWLINE;
1909 else
1910 type = TOKEN_EOF;
1911 return;
1912 case '#': // Like \" but newline is ignored.
1913 while ((cc = input_stack::get(0)) != '\n')
1914 if (cc == EOF) {
1915 type = TOKEN_EOF;
1916 return;
1918 break;
1919 case '$':
1921 symbol s = read_escape_name();
1922 if (!(s.is_null() || s.is_empty()))
1923 interpolate_arg(s);
1924 break;
1926 case '*':
1928 symbol s = read_escape_name(WITH_ARGS);
1929 if (!(s.is_null() || s.is_empty())) {
1930 if (have_string_arg) {
1931 have_string_arg = 0;
1932 interpolate_string_with_args(s);
1934 else
1935 interpolate_string(s);
1937 break;
1939 case 'a':
1940 nd = new non_interpreted_char_node('\001');
1941 type = TOKEN_NODE;
1942 return;
1943 case 'A':
1944 c = '0' + do_name_test();
1945 type = TOKEN_CHAR;
1946 return;
1947 case 'b':
1948 nd = do_bracket();
1949 type = TOKEN_NODE;
1950 return;
1951 case 'B':
1952 c = '0' + do_expr_test();
1953 type = TOKEN_CHAR;
1954 return;
1955 case 'c':
1956 goto ESCAPE_c;
1957 case 'C':
1958 nm = get_delim_name();
1959 if (nm.is_null())
1960 break;
1961 type = TOKEN_SPECIAL;
1962 return;
1963 case 'd':
1964 type = TOKEN_NODE;
1965 nd = new vmotion_node(curenv->get_size() / 2,
1966 curenv->get_fill_color());
1967 return;
1968 case 'D':
1969 nd = read_draw_node();
1970 if (!nd)
1971 break;
1972 type = TOKEN_NODE;
1973 return;
1974 case 'e':
1975 goto ESCAPE_e;
1976 case 'E':
1977 goto handle_escape_char;
1978 case 'f':
1980 symbol s = read_escape_name(ALLOW_EMPTY);
1981 if (s.is_null())
1982 break;
1983 const char *p;
1984 for (p = s.contents(); *p != '\0'; p++)
1985 if (!csdigit(*p))
1986 break;
1987 if (*p || s.is_empty())
1988 curenv->set_font(s);
1989 else
1990 curenv->set_font(atoi(s.contents()));
1991 if (!compatible_flag)
1992 have_input = 1;
1993 break;
1995 case 'F':
1997 symbol s = read_escape_name(ALLOW_EMPTY);
1998 if (s.is_null())
1999 break;
2000 curenv->set_family(s);
2001 have_input = 1;
2002 break;
2004 case 'g':
2006 symbol s = read_escape_name();
2007 if (!(s.is_null() || s.is_empty()))
2008 interpolate_number_format(s);
2009 break;
2011 case 'h':
2012 if (!get_delim_number(&x, 'm'))
2013 break;
2014 type = TOKEN_NODE;
2015 nd = new hmotion_node(x, curenv->get_fill_color());
2016 return;
2017 case 'H':
2018 // don't take height increments relative to previous height if
2019 // in compatibility mode
2020 if (!compatible_flag && curenv->get_char_height())
2022 if (get_delim_number(&x, 'z', curenv->get_char_height()))
2023 curenv->set_char_height(x);
2025 else
2027 if (get_delim_number(&x, 'z', curenv->get_requested_point_size()))
2028 curenv->set_char_height(x);
2030 if (!compatible_flag)
2031 have_input = 1;
2032 break;
2033 case 'k':
2034 nm = read_escape_name();
2035 if (nm.is_null() || nm.is_empty())
2036 break;
2037 type = TOKEN_MARK_INPUT;
2038 return;
2039 case 'l':
2040 case 'L':
2042 charinfo *s = 0;
2043 if (!get_line_arg(&x, (cc == 'l' ? 'm': 'v'), &s))
2044 break;
2045 if (s == 0)
2046 s = get_charinfo(cc == 'l' ? "ru" : "br");
2047 type = TOKEN_NODE;
2048 node *char_node = curenv->make_char_node(s);
2049 if (cc == 'l')
2050 nd = new hline_node(x, char_node);
2051 else
2052 nd = new vline_node(x, char_node);
2053 return;
2055 case 'm':
2056 do_glyph_color(read_escape_name(ALLOW_EMPTY));
2057 if (!compatible_flag)
2058 have_input = 1;
2059 break;
2060 case 'M':
2061 do_fill_color(read_escape_name(ALLOW_EMPTY));
2062 if (!compatible_flag)
2063 have_input = 1;
2064 break;
2065 case 'n':
2067 int inc;
2068 symbol s = read_increment_and_escape_name(&inc);
2069 if (!(s.is_null() || s.is_empty()))
2070 interpolate_number_reg(s, inc);
2071 break;
2073 case 'N':
2074 if (!get_delim_number(&val, 0))
2075 break;
2076 type = TOKEN_NUMBERED_CHAR;
2077 return;
2078 case 'o':
2079 nd = do_overstrike();
2080 type = TOKEN_NODE;
2081 return;
2082 case 'O':
2083 nd = do_suppress(read_escape_name());
2084 if (!nd)
2085 break;
2086 type = TOKEN_NODE;
2087 return;
2088 case 'p':
2089 type = TOKEN_SPREAD;
2090 return;
2091 case 'r':
2092 type = TOKEN_NODE;
2093 nd = new vmotion_node(-curenv->get_size(), curenv->get_fill_color());
2094 return;
2095 case 'R':
2096 do_register();
2097 if (!compatible_flag)
2098 have_input = 1;
2099 break;
2100 case 's':
2101 if (read_size(&x))
2102 curenv->set_size(x);
2103 if (!compatible_flag)
2104 have_input = 1;
2105 break;
2106 case 'S':
2107 if (get_delim_number(&x, 0))
2108 curenv->set_char_slant(x);
2109 if (!compatible_flag)
2110 have_input = 1;
2111 break;
2112 case 't':
2113 type = TOKEN_NODE;
2114 nd = new non_interpreted_char_node('\t');
2115 return;
2116 case 'u':
2117 type = TOKEN_NODE;
2118 nd = new vmotion_node(-curenv->get_size() / 2,
2119 curenv->get_fill_color());
2120 return;
2121 case 'v':
2122 if (!get_delim_number(&x, 'v'))
2123 break;
2124 type = TOKEN_NODE;
2125 nd = new vmotion_node(x, curenv->get_fill_color());
2126 return;
2127 case 'V':
2129 symbol s = read_escape_name();
2130 if (!(s.is_null() || s.is_empty()))
2131 interpolate_environment_variable(s);
2132 break;
2134 case 'w':
2135 do_width();
2136 break;
2137 case 'x':
2138 if (!get_delim_number(&x, 'v'))
2139 break;
2140 type = TOKEN_NODE;
2141 nd = new extra_size_node(x);
2142 return;
2143 case 'X':
2144 nd = do_special();
2145 if (!nd)
2146 break;
2147 type = TOKEN_NODE;
2148 return;
2149 case 'Y':
2151 symbol s = read_escape_name();
2152 if (s.is_null() || s.is_empty())
2153 break;
2154 request_or_macro *p = lookup_request(s);
2155 macro *m = p->to_macro();
2156 if (!m) {
2157 error("can't transparently throughput a request");
2158 break;
2160 nd = new special_node(*m);
2161 type = TOKEN_NODE;
2162 return;
2164 case 'z':
2166 next();
2167 if (type == TOKEN_NODE)
2168 nd = new zero_width_node(nd);
2169 else {
2170 charinfo *ci = get_char(1);
2171 if (ci == 0)
2172 break;
2173 node *gn = curenv->make_char_node(ci);
2174 if (gn == 0)
2175 break;
2176 nd = new zero_width_node(gn);
2177 type = TOKEN_NODE;
2179 return;
2181 case 'Z':
2182 nd = do_zero_width();
2183 if (nd == 0)
2184 break;
2185 type = TOKEN_NODE;
2186 return;
2187 case '{':
2188 goto ESCAPE_LEFT_BRACE;
2189 case '}':
2190 goto ESCAPE_RIGHT_BRACE;
2191 case '\n':
2192 break;
2193 case '[':
2194 if (!compatible_flag) {
2195 symbol s = read_long_escape_name(WITH_ARGS);
2196 if (s.is_null() || s.is_empty())
2197 break;
2198 if (have_string_arg) {
2199 have_string_arg = 0;
2200 nm = composite_glyph_name(s);
2202 else {
2203 const char *gn = check_unicode_name(s.contents());
2204 if (gn) {
2205 const char *gn_decomposed = decompose_unicode(gn);
2206 if (gn_decomposed)
2207 gn = &gn_decomposed[1];
2208 const char *groff_gn = unicode_to_glyph_name(gn);
2209 if (groff_gn)
2210 nm = symbol(groff_gn);
2211 else {
2212 char *buf = new char[strlen(gn) + 1 + 1];
2213 strcpy(buf, "u");
2214 strcat(buf, gn);
2215 nm = symbol(buf);
2216 a_delete buf;
2219 else
2220 nm = symbol(s.contents());
2222 type = TOKEN_SPECIAL;
2223 return;
2225 goto handle_normal_char;
2226 default:
2227 if (cc != escape_char && cc != '.')
2228 warning(WARN_ESCAPE, "escape character ignored before %1",
2229 input_char_description(cc));
2230 goto handle_normal_char;
2236 int token::operator==(const token &t)
2238 if (type != t.type)
2239 return 0;
2240 switch(type) {
2241 case TOKEN_CHAR:
2242 return c == t.c;
2243 case TOKEN_SPECIAL:
2244 return nm == t.nm;
2245 case TOKEN_NUMBERED_CHAR:
2246 return val == t.val;
2247 default:
2248 return 1;
2252 int token::operator!=(const token &t)
2254 return !(*this == t);
2257 // is token a suitable delimiter (like ')?
2259 int token::delimiter(int err)
2261 switch(type) {
2262 case TOKEN_CHAR:
2263 switch(c) {
2264 case '0':
2265 case '1':
2266 case '2':
2267 case '3':
2268 case '4':
2269 case '5':
2270 case '6':
2271 case '7':
2272 case '8':
2273 case '9':
2274 case '+':
2275 case '-':
2276 case '/':
2277 case '*':
2278 case '%':
2279 case '<':
2280 case '>':
2281 case '=':
2282 case '&':
2283 case ':':
2284 case '(':
2285 case ')':
2286 case '.':
2287 if (err)
2288 error("cannot use character `%1' as a starting delimiter", char(c));
2289 return 0;
2290 default:
2291 return 1;
2293 case TOKEN_NODE:
2294 case TOKEN_SPACE:
2295 case TOKEN_STRETCHABLE_SPACE:
2296 case TOKEN_UNSTRETCHABLE_SPACE:
2297 case TOKEN_TAB:
2298 case TOKEN_NEWLINE:
2299 if (err)
2300 error("cannot use %1 as a starting delimiter", description());
2301 return 0;
2302 default:
2303 return 1;
2307 const char *token::description()
2309 static char buf[4];
2310 switch (type) {
2311 case TOKEN_BACKSPACE:
2312 return "a backspace character";
2313 case TOKEN_CHAR:
2314 buf[0] = '`';
2315 buf[1] = c;
2316 buf[2] = '\'';
2317 buf[3] = '\0';
2318 return buf;
2319 case TOKEN_DUMMY:
2320 return "`\\&'";
2321 case TOKEN_ESCAPE:
2322 return "`\\e'";
2323 case TOKEN_HYPHEN_INDICATOR:
2324 return "`\\%'";
2325 case TOKEN_INTERRUPT:
2326 return "`\\c'";
2327 case TOKEN_ITALIC_CORRECTION:
2328 return "`\\/'";
2329 case TOKEN_LEADER:
2330 return "a leader character";
2331 case TOKEN_LEFT_BRACE:
2332 return "`\\{'";
2333 case TOKEN_MARK_INPUT:
2334 return "`\\k'";
2335 case TOKEN_NEWLINE:
2336 return "newline";
2337 case TOKEN_NODE:
2338 return "a node";
2339 case TOKEN_NUMBERED_CHAR:
2340 return "`\\N'";
2341 case TOKEN_RIGHT_BRACE:
2342 return "`\\}'";
2343 case TOKEN_SPACE:
2344 return "a space";
2345 case TOKEN_SPECIAL:
2346 return "a special character";
2347 case TOKEN_SPREAD:
2348 return "`\\p'";
2349 case TOKEN_STRETCHABLE_SPACE:
2350 return "`\\~'";
2351 case TOKEN_UNSTRETCHABLE_SPACE:
2352 return "`\\ '";
2353 case TOKEN_TAB:
2354 return "a tab character";
2355 case TOKEN_TRANSPARENT:
2356 return "`\\!'";
2357 case TOKEN_TRANSPARENT_DUMMY:
2358 return "`\\)'";
2359 case TOKEN_ZERO_WIDTH_BREAK:
2360 return "`\\:'";
2361 case TOKEN_EOF:
2362 return "end of input";
2363 default:
2364 break;
2366 return "a magic token";
2369 void skip_line()
2371 while (!tok.newline())
2372 if (tok.eof())
2373 return;
2374 else
2375 tok.next();
2376 tok.next();
2379 void compatible()
2381 int n;
2382 if (has_arg() && get_integer(&n))
2383 compatible_flag = n != 0;
2384 else
2385 compatible_flag = 1;
2386 skip_line();
2389 static void empty_name_warning(int required)
2391 if (tok.newline() || tok.eof()) {
2392 if (required)
2393 warning(WARN_MISSING, "missing name");
2395 else if (tok.right_brace() || tok.tab()) {
2396 const char *start = tok.description();
2397 do {
2398 tok.next();
2399 } while (tok.space() || tok.right_brace() || tok.tab());
2400 if (!tok.newline() && !tok.eof())
2401 error("%1 is not allowed before an argument", start);
2402 else if (required)
2403 warning(WARN_MISSING, "missing name");
2405 else if (required)
2406 error("name expected (got %1)", tok.description());
2407 else
2408 error("name expected (got %1): treated as missing", tok.description());
2411 static void non_empty_name_warning()
2413 if (!tok.newline() && !tok.eof() && !tok.space() && !tok.tab()
2414 && !tok.right_brace()
2415 // We don't want to give a warning for .el\{
2416 && !tok.left_brace())
2417 error("%1 is not allowed in a name", tok.description());
2420 symbol get_name(int required)
2422 if (compatible_flag) {
2423 char buf[3];
2424 tok.skip();
2425 if ((buf[0] = tok.ch()) != 0) {
2426 tok.next();
2427 if ((buf[1] = tok.ch()) != 0) {
2428 buf[2] = 0;
2429 tok.make_space();
2431 else
2432 non_empty_name_warning();
2433 return symbol(buf);
2435 else {
2436 empty_name_warning(required);
2437 return NULL_SYMBOL;
2440 else
2441 return get_long_name(required);
2444 symbol get_long_name(int required)
2446 return do_get_long_name(required, 0);
2449 static symbol do_get_long_name(int required, char end)
2451 while (tok.space())
2452 tok.next();
2453 char abuf[ABUF_SIZE];
2454 char *buf = abuf;
2455 int buf_size = ABUF_SIZE;
2456 int i = 0;
2457 for (;;) {
2458 // If end != 0 we normally have to append a null byte
2459 if (i + 2 > buf_size) {
2460 if (buf == abuf) {
2461 buf = new char[ABUF_SIZE*2];
2462 memcpy(buf, abuf, buf_size);
2463 buf_size = ABUF_SIZE*2;
2465 else {
2466 char *old_buf = buf;
2467 buf = new char[buf_size*2];
2468 memcpy(buf, old_buf, buf_size);
2469 buf_size *= 2;
2470 a_delete old_buf;
2473 if ((buf[i] = tok.ch()) == 0 || buf[i] == end)
2474 break;
2475 i++;
2476 tok.next();
2478 if (i == 0) {
2479 empty_name_warning(required);
2480 return NULL_SYMBOL;
2482 if (end && buf[i] == end)
2483 buf[i+1] = '\0';
2484 else
2485 non_empty_name_warning();
2486 if (buf == abuf)
2487 return symbol(buf);
2488 else {
2489 symbol s(buf);
2490 a_delete buf;
2491 return s;
2495 void exit_troff()
2497 exit_started = 1;
2498 topdiv->set_last_page();
2499 if (!end_macro_name.is_null()) {
2500 spring_trap(end_macro_name);
2501 tok.next();
2502 process_input_stack();
2504 curenv->final_break();
2505 tok.next();
2506 process_input_stack();
2507 end_diversions();
2508 if (topdiv->get_page_length() > 0) {
2509 done_end_macro = 1;
2510 topdiv->set_ejecting();
2511 static unsigned char buf[2] = { LAST_PAGE_EJECTOR, '\0' };
2512 input_stack::push(make_temp_iterator((char *)buf));
2513 topdiv->space(topdiv->get_page_length(), 1);
2514 tok.next();
2515 process_input_stack();
2516 seen_last_page_ejector = 1; // should be set already
2517 topdiv->set_ejecting();
2518 push_page_ejector();
2519 topdiv->space(topdiv->get_page_length(), 1);
2520 tok.next();
2521 process_input_stack();
2523 // This will only happen if a trap-invoked macro starts a diversion,
2524 // or if vertical position traps have been disabled.
2525 cleanup_and_exit(0);
2528 // This implements .ex. The input stack must be cleared before calling
2529 // exit_troff().
2531 void exit_request()
2533 input_stack::clear();
2534 if (exit_started)
2535 tok.next();
2536 else
2537 exit_troff();
2540 void return_macro_request()
2542 if (has_arg() && tok.ch())
2543 input_stack::pop_macro();
2544 input_stack::pop_macro();
2545 tok.next();
2548 void end_macro()
2550 end_macro_name = get_name();
2551 skip_line();
2554 void blank_line_macro()
2556 blank_line_macro_name = get_name();
2557 skip_line();
2560 static void trapping_blank_line()
2562 if (!blank_line_macro_name.is_null())
2563 spring_trap(blank_line_macro_name);
2564 else
2565 blank_line();
2568 void do_request()
2570 int old_compatible_flag = compatible_flag;
2571 compatible_flag = 0;
2572 symbol nm = get_name();
2573 if (nm.is_null())
2574 skip_line();
2575 else
2576 interpolate_macro(nm);
2577 compatible_flag = old_compatible_flag;
2580 inline int possibly_handle_first_page_transition()
2582 if (topdiv->before_first_page && curdiv == topdiv && !curenv->is_dummy()) {
2583 handle_first_page_transition();
2584 return 1;
2586 else
2587 return 0;
2590 static int transparent_translate(int cc)
2592 if (!invalid_input_char(cc)) {
2593 charinfo *ci = charset_table[cc];
2594 switch (ci->get_special_translation(1)) {
2595 case charinfo::TRANSLATE_SPACE:
2596 return ' ';
2597 case charinfo::TRANSLATE_STRETCHABLE_SPACE:
2598 return ESCAPE_TILDE;
2599 case charinfo::TRANSLATE_DUMMY:
2600 return ESCAPE_AMPERSAND;
2601 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
2602 return ESCAPE_PERCENT;
2604 // This is really ugly.
2605 ci = ci->get_translation(1);
2606 if (ci) {
2607 int c = ci->get_ascii_code();
2608 if (c != '\0')
2609 return c;
2610 error("can't translate %1 to special character `%2'"
2611 " in transparent throughput",
2612 input_char_description(cc),
2613 ci->nm.contents());
2616 return cc;
2619 class int_stack {
2620 struct int_stack_element {
2621 int n;
2622 int_stack_element *next;
2623 } *top;
2624 public:
2625 int_stack();
2626 ~int_stack();
2627 void push(int);
2628 int is_empty();
2629 int pop();
2632 int_stack::int_stack()
2634 top = 0;
2637 int_stack::~int_stack()
2639 while (top != 0) {
2640 int_stack_element *temp = top;
2641 top = top->next;
2642 delete temp;
2646 int int_stack::is_empty()
2648 return top == 0;
2651 void int_stack::push(int n)
2653 int_stack_element *p = new int_stack_element;
2654 p->next = top;
2655 p->n = n;
2656 top = p;
2659 int int_stack::pop()
2661 assert(top != 0);
2662 int_stack_element *p = top;
2663 top = top->next;
2664 int n = p->n;
2665 delete p;
2666 return n;
2669 int node::reread(int *)
2671 return 0;
2674 int global_diverted_space = 0;
2676 int diverted_space_node::reread(int *bolp)
2678 global_diverted_space = 1;
2679 if (curenv->get_fill())
2680 trapping_blank_line();
2681 else
2682 curdiv->space(n);
2683 global_diverted_space = 0;
2684 *bolp = 1;
2685 return 1;
2688 int diverted_copy_file_node::reread(int *bolp)
2690 curdiv->copy_file(filename.contents());
2691 *bolp = 1;
2692 return 1;
2695 int word_space_node::reread(int *)
2697 if (unformat) {
2698 for (width_list *w = orig_width; w; w = w->next)
2699 curenv->space(w->width, w->sentence_width);
2700 unformat = 0;
2701 return 1;
2703 return 0;
2706 int unbreakable_space_node::reread(int *)
2708 return 0;
2711 int hmotion_node::reread(int *)
2713 if (unformat && was_tab) {
2714 curenv->handle_tab(0);
2715 unformat = 0;
2716 return 1;
2718 return 0;
2721 void process_input_stack()
2723 int_stack trap_bol_stack;
2724 int bol = 1;
2725 for (;;) {
2726 int suppress_next = 0;
2727 switch (tok.type) {
2728 case token::TOKEN_CHAR:
2730 unsigned char ch = tok.c;
2731 if (bol && !have_input
2732 && (ch == curenv->control_char
2733 || ch == curenv->no_break_control_char)) {
2734 break_flag = ch == curenv->control_char;
2735 // skip tabs as well as spaces here
2736 do {
2737 tok.next();
2738 } while (tok.white_space());
2739 symbol nm = get_name();
2740 #if defined(DEBUGGING)
2741 if (debug_state) {
2742 if (! nm.is_null()) {
2743 if (strcmp(nm.contents(), "test") == 0) {
2744 fprintf(stderr, "found it!\n");
2745 fflush(stderr);
2747 fprintf(stderr, "interpreting [%s]", nm.contents());
2748 if (strcmp(nm.contents(), "di") == 0 && topdiv != curdiv)
2749 fprintf(stderr, " currently in diversion: %s",
2750 curdiv->get_diversion_name());
2751 fprintf(stderr, "\n");
2752 fflush(stderr);
2755 #endif
2756 if (nm.is_null())
2757 skip_line();
2758 else {
2759 interpolate_macro(nm);
2760 #if defined(DEBUGGING)
2761 if (debug_state) {
2762 fprintf(stderr, "finished interpreting [%s] and environment state is\n", nm.contents());
2763 curenv->dump_troff_state();
2765 #endif
2767 suppress_next = 1;
2769 else {
2770 if (possibly_handle_first_page_transition())
2772 else {
2773 for (;;) {
2774 #if defined(DEBUGGING)
2775 if (debug_state) {
2776 fprintf(stderr, "found [%c]\n", ch); fflush(stderr);
2778 #endif
2779 curenv->add_char(charset_table[ch]);
2780 tok.next();
2781 if (tok.type != token::TOKEN_CHAR)
2782 break;
2783 ch = tok.c;
2785 suppress_next = 1;
2786 bol = 0;
2789 break;
2791 case token::TOKEN_TRANSPARENT:
2793 if (bol) {
2794 if (possibly_handle_first_page_transition())
2796 else {
2797 int cc;
2798 do {
2799 node *n;
2800 cc = get_copy(&n);
2801 if (cc != EOF)
2802 if (cc != '\0')
2803 curdiv->transparent_output(transparent_translate(cc));
2804 else
2805 curdiv->transparent_output(n);
2806 } while (cc != '\n' && cc != EOF);
2807 if (cc == EOF)
2808 curdiv->transparent_output('\n');
2811 break;
2813 case token::TOKEN_NEWLINE:
2815 if (bol && !old_have_input
2816 && !curenv->get_prev_line_interrupted())
2817 trapping_blank_line();
2818 else {
2819 curenv->newline();
2820 bol = 1;
2822 break;
2824 case token::TOKEN_REQUEST:
2826 int request_code = tok.c;
2827 tok.next();
2828 switch (request_code) {
2829 case TITLE_REQUEST:
2830 title();
2831 break;
2832 case COPY_FILE_REQUEST:
2833 copy_file();
2834 break;
2835 case TRANSPARENT_FILE_REQUEST:
2836 transparent_file();
2837 break;
2838 #ifdef COLUMN
2839 case VJUSTIFY_REQUEST:
2840 vjustify();
2841 break;
2842 #endif /* COLUMN */
2843 default:
2844 assert(0);
2845 break;
2847 suppress_next = 1;
2848 break;
2850 case token::TOKEN_SPACE:
2852 if (possibly_handle_first_page_transition())
2854 else if (bol && !curenv->get_prev_line_interrupted()) {
2855 int nspaces = 0;
2856 // save space_width now so that it isn't changed by \f or \s
2857 // which we wouldn't notice here
2858 hunits space_width = curenv->get_space_width();
2859 do {
2860 nspaces += tok.nspaces();
2861 tok.next();
2862 } while (tok.space());
2863 if (tok.newline())
2864 trapping_blank_line();
2865 else {
2866 push_token(tok);
2867 curenv->do_break();
2868 curenv->add_node(new hmotion_node(space_width * nspaces,
2869 curenv->get_fill_color()));
2870 bol = 0;
2873 else {
2874 curenv->space();
2875 bol = 0;
2877 break;
2879 case token::TOKEN_EOF:
2880 return;
2881 case token::TOKEN_NODE:
2883 if (possibly_handle_first_page_transition())
2885 else if (tok.nd->reread(&bol)) {
2886 delete tok.nd;
2887 tok.nd = 0;
2889 else {
2890 curenv->add_node(tok.nd);
2891 tok.nd = 0;
2892 bol = 0;
2893 curenv->possibly_break_line(1);
2895 break;
2897 case token::TOKEN_PAGE_EJECTOR:
2899 continue_page_eject();
2900 // I think we just want to preserve bol.
2901 // bol = 1;
2902 break;
2904 case token::TOKEN_BEGIN_TRAP:
2906 trap_bol_stack.push(bol);
2907 bol = 1;
2908 have_input = 0;
2909 break;
2911 case token::TOKEN_END_TRAP:
2913 if (trap_bol_stack.is_empty())
2914 error("spurious end trap token detected!");
2915 else
2916 bol = trap_bol_stack.pop();
2917 have_input = 0;
2919 /* I'm not totally happy about this. But I can't think of any other
2920 way to do it. Doing an output_pending_lines() whenever a
2921 TOKEN_END_TRAP is detected doesn't work: for example,
2923 .wh -1i x
2924 .de x
2927 .wh -.5i y
2928 .de y
2929 .tl ''-%-''
2932 .ll .5i
2933 .sp |\n(.pu-1i-.5v
2934 a\%very\%very\%long\%word
2936 will print all but the first lines from the word immediately
2937 after the footer, rather than on the next page. */
2939 if (trap_bol_stack.is_empty())
2940 curenv->output_pending_lines();
2941 break;
2943 default:
2945 bol = 0;
2946 tok.process();
2947 break;
2950 if (!suppress_next)
2951 tok.next();
2952 trap_sprung_flag = 0;
2956 #ifdef WIDOW_CONTROL
2958 void flush_pending_lines()
2960 while (!tok.newline() && !tok.eof())
2961 tok.next();
2962 curenv->output_pending_lines();
2963 tok.next();
2966 #endif /* WIDOW_CONTROL */
2968 request_or_macro::request_or_macro()
2972 macro *request_or_macro::to_macro()
2974 return 0;
2977 request::request(REQUEST_FUNCP pp) : p(pp)
2981 void request::invoke(symbol)
2983 (*p)();
2986 struct char_block {
2987 enum { SIZE = 128 };
2988 unsigned char s[SIZE];
2989 char_block *next;
2990 char_block();
2993 char_block::char_block()
2994 : next(0)
2998 class char_list {
2999 public:
3000 char_list();
3001 ~char_list();
3002 void append(unsigned char);
3003 void set(unsigned char, int);
3004 unsigned char get(int);
3005 int length();
3006 private:
3007 unsigned char *ptr;
3008 int len;
3009 char_block *head;
3010 char_block *tail;
3011 friend class macro_header;
3012 friend class string_iterator;
3015 char_list::char_list()
3016 : ptr(0), len(0), head(0), tail(0)
3020 char_list::~char_list()
3022 while (head != 0) {
3023 char_block *tem = head;
3024 head = head->next;
3025 delete tem;
3029 int char_list::length()
3031 return len;
3034 void char_list::append(unsigned char c)
3036 if (tail == 0) {
3037 head = tail = new char_block;
3038 ptr = tail->s;
3040 else {
3041 if (ptr >= tail->s + char_block::SIZE) {
3042 tail->next = new char_block;
3043 tail = tail->next;
3044 ptr = tail->s;
3047 *ptr++ = c;
3048 len++;
3051 void char_list::set(unsigned char c, int offset)
3053 assert(len > offset);
3054 // optimization for access at the end
3055 int boundary = len - len % char_block::SIZE;
3056 if (offset >= boundary) {
3057 *(tail->s + offset - boundary) = c;
3058 return;
3060 char_block *tem = head;
3061 int l = 0;
3062 for (;;) {
3063 l += char_block::SIZE;
3064 if (l > offset) {
3065 *(tem->s + offset % char_block::SIZE) = c;
3066 return;
3068 tem = tem->next;
3072 unsigned char char_list::get(int offset)
3074 assert(len > offset);
3075 // optimization for access at the end
3076 int boundary = len - len % char_block::SIZE;
3077 if (offset >= boundary)
3078 return *(tail->s + offset - boundary);
3079 char_block *tem = head;
3080 int l = 0;
3081 for (;;) {
3082 l += char_block::SIZE;
3083 if (l > offset)
3084 return *(tem->s + offset % char_block::SIZE);
3085 tem = tem->next;
3089 class node_list {
3090 node *head;
3091 node *tail;
3092 public:
3093 node_list();
3094 ~node_list();
3095 void append(node *);
3096 int length();
3097 node *extract();
3099 friend class macro_header;
3100 friend class string_iterator;
3103 void node_list::append(node *n)
3105 if (head == 0) {
3106 n->next = 0;
3107 head = tail = n;
3109 else {
3110 n->next = 0;
3111 tail = tail->next = n;
3115 int node_list::length()
3117 int total = 0;
3118 for (node *n = head; n != 0; n = n->next)
3119 ++total;
3120 return total;
3123 node_list::node_list()
3125 head = tail = 0;
3128 node *node_list::extract()
3130 node *temp = head;
3131 head = tail = 0;
3132 return temp;
3135 node_list::~node_list()
3137 delete_node_list(head);
3140 class macro_header {
3141 public:
3142 int count;
3143 char_list cl;
3144 node_list nl;
3145 macro_header() { count = 1; }
3146 macro_header *copy(int);
3149 macro::~macro()
3151 if (p != 0 && --(p->count) <= 0)
3152 delete p;
3155 macro::macro()
3156 : is_a_diversion(0)
3158 if (!input_stack::get_location(1, &filename, &lineno)) {
3159 filename = 0;
3160 lineno = 0;
3162 len = 0;
3163 empty_macro = 1;
3164 p = 0;
3167 macro::macro(const macro &m)
3168 : filename(m.filename), lineno(m.lineno), len(m.len),
3169 empty_macro(m.empty_macro), is_a_diversion(m.is_a_diversion), p(m.p)
3171 if (p != 0)
3172 p->count++;
3175 macro::macro(int is_div)
3176 : is_a_diversion(is_div)
3178 if (!input_stack::get_location(1, &filename, &lineno)) {
3179 filename = 0;
3180 lineno = 0;
3182 len = 0;
3183 empty_macro = 1;
3184 p = 0;
3187 int macro::is_diversion()
3189 return is_a_diversion;
3192 macro &macro::operator=(const macro &m)
3194 // don't assign object
3195 if (m.p != 0)
3196 m.p->count++;
3197 if (p != 0 && --(p->count) <= 0)
3198 delete p;
3199 p = m.p;
3200 filename = m.filename;
3201 lineno = m.lineno;
3202 len = m.len;
3203 empty_macro = m.empty_macro;
3204 is_a_diversion = m.is_a_diversion;
3205 return *this;
3208 void macro::append(unsigned char c)
3210 assert(c != 0);
3211 if (p == 0)
3212 p = new macro_header;
3213 if (p->cl.length() != len) {
3214 macro_header *tem = p->copy(len);
3215 if (--(p->count) <= 0)
3216 delete p;
3217 p = tem;
3219 p->cl.append(c);
3220 ++len;
3221 if (c != PUSH_GROFF_MODE && c != PUSH_COMP_MODE && c != POP_GROFFCOMP_MODE)
3222 empty_macro = 0;
3225 void macro::set(unsigned char c, int offset)
3227 assert(p != 0);
3228 assert(c != 0);
3229 p->cl.set(c, offset);
3232 unsigned char macro::get(int offset)
3234 assert(p != 0);
3235 return p->cl.get(offset);
3238 int macro::length()
3240 return len;
3243 void macro::append_str(const char *s)
3245 int i = 0;
3247 if (s) {
3248 while (s[i] != (char)0) {
3249 append(s[i]);
3250 i++;
3255 void macro::append(node *n)
3257 assert(n != 0);
3258 if (p == 0)
3259 p = new macro_header;
3260 if (p->cl.length() != len) {
3261 macro_header *tem = p->copy(len);
3262 if (--(p->count) <= 0)
3263 delete p;
3264 p = tem;
3266 p->cl.append(0);
3267 p->nl.append(n);
3268 ++len;
3269 empty_macro = 0;
3272 void macro::append_unsigned(unsigned int i)
3274 unsigned int j = i / 10;
3275 if (j != 0)
3276 append_unsigned(j);
3277 append(((unsigned char)(((int)'0') + i % 10)));
3280 void macro::append_int(int i)
3282 if (i < 0) {
3283 append('-');
3284 i = -i;
3286 append_unsigned((unsigned int)i);
3289 void macro::print_size()
3291 errprint("%1", len);
3294 // make a copy of the first n bytes
3296 macro_header *macro_header::copy(int n)
3298 macro_header *p = new macro_header;
3299 char_block *bp = cl.head;
3300 unsigned char *ptr = bp->s;
3301 node *nd = nl.head;
3302 while (--n >= 0) {
3303 if (ptr >= bp->s + char_block::SIZE) {
3304 bp = bp->next;
3305 ptr = bp->s;
3307 unsigned char c = *ptr++;
3308 p->cl.append(c);
3309 if (c == 0) {
3310 p->nl.append(nd->copy());
3311 nd = nd->next;
3314 return p;
3317 void print_macros()
3319 object_dictionary_iterator iter(request_dictionary);
3320 request_or_macro *rm;
3321 symbol s;
3322 while (iter.get(&s, (object **)&rm)) {
3323 assert(!s.is_null());
3324 macro *m = rm->to_macro();
3325 if (m) {
3326 errprint("%1\t", s.contents());
3327 m->print_size();
3328 errprint("\n");
3331 fflush(stderr);
3332 skip_line();
3335 class string_iterator : public input_iterator {
3336 macro mac;
3337 const char *how_invoked;
3338 int newline_flag;
3339 int lineno;
3340 char_block *bp;
3341 int count; // of characters remaining
3342 node *nd;
3343 int saved_compatible_flag;
3344 protected:
3345 symbol nm;
3346 string_iterator();
3347 public:
3348 string_iterator(const macro &m, const char *p = 0, symbol s = NULL_SYMBOL);
3349 int fill(node **);
3350 int peek();
3351 int get_location(int, const char **, int *);
3352 void backtrace();
3353 void save_compatible_flag(int f) { saved_compatible_flag = f; }
3354 int get_compatible_flag() { return saved_compatible_flag; }
3355 int is_diversion();
3358 string_iterator::string_iterator(const macro &m, const char *p, symbol s)
3359 : input_iterator(m.is_a_diversion), mac(m), how_invoked(p), newline_flag(0),
3360 lineno(1), nm(s)
3362 count = mac.len;
3363 if (count != 0) {
3364 bp = mac.p->cl.head;
3365 nd = mac.p->nl.head;
3366 ptr = eptr = bp->s;
3368 else {
3369 bp = 0;
3370 nd = 0;
3371 ptr = eptr = 0;
3375 string_iterator::string_iterator()
3377 bp = 0;
3378 nd = 0;
3379 ptr = eptr = 0;
3380 newline_flag = 0;
3381 how_invoked = 0;
3382 lineno = 1;
3383 count = 0;
3386 int string_iterator::is_diversion()
3388 return mac.is_diversion();
3391 int string_iterator::fill(node **np)
3393 if (newline_flag)
3394 lineno++;
3395 newline_flag = 0;
3396 if (count <= 0)
3397 return EOF;
3398 const unsigned char *p = eptr;
3399 if (p >= bp->s + char_block::SIZE) {
3400 bp = bp->next;
3401 p = bp->s;
3403 if (*p == '\0') {
3404 if (np) {
3405 *np = nd->copy();
3406 if (is_diversion())
3407 (*np)->div_nest_level = input_stack::get_div_level();
3408 else
3409 (*np)->div_nest_level = 0;
3411 nd = nd->next;
3412 eptr = ptr = p + 1;
3413 count--;
3414 return 0;
3416 const unsigned char *e = bp->s + char_block::SIZE;
3417 if (e - p > count)
3418 e = p + count;
3419 ptr = p;
3420 while (p < e) {
3421 unsigned char c = *p;
3422 if (c == '\n' || c == ESCAPE_NEWLINE) {
3423 newline_flag = 1;
3424 p++;
3425 break;
3427 if (c == '\0')
3428 break;
3429 p++;
3431 eptr = p;
3432 count -= p - ptr;
3433 return *ptr++;
3436 int string_iterator::peek()
3438 if (count <= 0)
3439 return EOF;
3440 const unsigned char *p = eptr;
3441 if (p >= bp->s + char_block::SIZE) {
3442 p = bp->next->s;
3444 return *p;
3447 int string_iterator::get_location(int allow_macro,
3448 const char **filep, int *linep)
3450 if (!allow_macro)
3451 return 0;
3452 if (mac.filename == 0)
3453 return 0;
3454 *filep = mac.filename;
3455 *linep = mac.lineno + lineno - 1;
3456 return 1;
3459 void string_iterator::backtrace()
3461 if (mac.filename) {
3462 errprint("%1:%2: backtrace", mac.filename, mac.lineno + lineno - 1);
3463 if (how_invoked) {
3464 if (!nm.is_null())
3465 errprint(": %1 `%2'\n", how_invoked, nm.contents());
3466 else
3467 errprint(": %1\n", how_invoked);
3469 else
3470 errprint("\n");
3474 class temp_iterator : public input_iterator {
3475 unsigned char *base;
3476 temp_iterator(const char *, int len);
3477 public:
3478 ~temp_iterator();
3479 friend input_iterator *make_temp_iterator(const char *);
3482 #ifdef __GNUG__
3483 inline
3484 #endif
3485 temp_iterator::temp_iterator(const char *s, int len)
3487 base = new unsigned char[len];
3488 memcpy(base, s, len);
3489 ptr = base;
3490 eptr = base + len;
3493 temp_iterator::~temp_iterator()
3495 a_delete base;
3498 class small_temp_iterator : public input_iterator {
3499 private:
3500 small_temp_iterator(const char *, int);
3501 ~small_temp_iterator();
3502 enum { BLOCK = 16 };
3503 static small_temp_iterator *free_list;
3504 void *operator new(size_t);
3505 void operator delete(void *);
3506 enum { SIZE = 12 };
3507 unsigned char buf[SIZE];
3508 friend input_iterator *make_temp_iterator(const char *);
3511 small_temp_iterator *small_temp_iterator::free_list = 0;
3513 void *small_temp_iterator::operator new(size_t n)
3515 assert(n == sizeof(small_temp_iterator));
3516 if (!free_list) {
3517 free_list =
3518 (small_temp_iterator *)new char[sizeof(small_temp_iterator)*BLOCK];
3519 for (int i = 0; i < BLOCK - 1; i++)
3520 free_list[i].next = free_list + i + 1;
3521 free_list[BLOCK-1].next = 0;
3523 small_temp_iterator *p = free_list;
3524 free_list = (small_temp_iterator *)(free_list->next);
3525 p->next = 0;
3526 return p;
3529 #ifdef __GNUG__
3530 inline
3531 #endif
3532 void small_temp_iterator::operator delete(void *p)
3534 if (p) {
3535 ((small_temp_iterator *)p)->next = free_list;
3536 free_list = (small_temp_iterator *)p;
3540 small_temp_iterator::~small_temp_iterator()
3544 #ifdef __GNUG__
3545 inline
3546 #endif
3547 small_temp_iterator::small_temp_iterator(const char *s, int len)
3549 for (int i = 0; i < len; i++)
3550 buf[i] = s[i];
3551 ptr = buf;
3552 eptr = buf + len;
3555 input_iterator *make_temp_iterator(const char *s)
3557 if (s == 0)
3558 return new small_temp_iterator(s, 0);
3559 else {
3560 int n = strlen(s);
3561 if (n <= small_temp_iterator::SIZE)
3562 return new small_temp_iterator(s, n);
3563 else
3564 return new temp_iterator(s, n);
3568 // this is used when macros with arguments are interpolated
3570 struct arg_list {
3571 macro mac;
3572 arg_list *next;
3573 arg_list(const macro &);
3574 ~arg_list();
3577 arg_list::arg_list(const macro &m) : mac(m), next(0)
3581 arg_list::~arg_list()
3585 class macro_iterator : public string_iterator {
3586 arg_list *args;
3587 int argc;
3588 public:
3589 macro_iterator(symbol, macro &, const char *how_invoked = "macro");
3590 macro_iterator();
3591 ~macro_iterator();
3592 int has_args() { return 1; }
3593 input_iterator *get_arg(int i);
3594 int nargs() { return argc; }
3595 void add_arg(const macro &m);
3596 void shift(int n);
3597 int is_macro() { return 1; }
3598 int is_diversion();
3601 input_iterator *macro_iterator::get_arg(int i)
3603 if (i == 0)
3604 return make_temp_iterator(nm.contents());
3605 if (i > 0 && i <= argc) {
3606 arg_list *p = args;
3607 for (int j = 1; j < i; j++) {
3608 assert(p != 0);
3609 p = p->next;
3611 return new string_iterator(p->mac);
3613 else
3614 return 0;
3617 void macro_iterator::add_arg(const macro &m)
3619 arg_list **p;
3620 for (p = &args; *p; p = &((*p)->next))
3622 *p = new arg_list(m);
3623 ++argc;
3626 void macro_iterator::shift(int n)
3628 while (n > 0 && argc > 0) {
3629 arg_list *tem = args;
3630 args = args->next;
3631 delete tem;
3632 --argc;
3633 --n;
3637 // This gets used by eg .if '\?xxx\?''.
3639 int operator==(const macro &m1, const macro &m2)
3641 if (m1.len != m2.len)
3642 return 0;
3643 string_iterator iter1(m1);
3644 string_iterator iter2(m2);
3645 int n = m1.len;
3646 while (--n >= 0) {
3647 node *nd1 = 0;
3648 int c1 = iter1.get(&nd1);
3649 assert(c1 != EOF);
3650 node *nd2 = 0;
3651 int c2 = iter2.get(&nd2);
3652 assert(c2 != EOF);
3653 if (c1 != c2) {
3654 if (c1 == 0)
3655 delete nd1;
3656 else if (c2 == 0)
3657 delete nd2;
3658 return 0;
3660 if (c1 == 0) {
3661 assert(nd1 != 0);
3662 assert(nd2 != 0);
3663 int are_same = nd1->type() == nd2->type() && nd1->same(nd2);
3664 delete nd1;
3665 delete nd2;
3666 if (!are_same)
3667 return 0;
3670 return 1;
3673 static void interpolate_macro(symbol nm)
3675 request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm);
3676 if (p == 0) {
3677 int warned = 0;
3678 const char *s = nm.contents();
3679 if (strlen(s) > 2) {
3680 request_or_macro *r;
3681 char buf[3];
3682 buf[0] = s[0];
3683 buf[1] = s[1];
3684 buf[2] = '\0';
3685 r = (request_or_macro *)request_dictionary.lookup(symbol(buf));
3686 if (r) {
3687 macro *m = r->to_macro();
3688 if (!m || !m->empty())
3689 warned = warning(WARN_SPACE,
3690 "macro `%1' not defined "
3691 "(probably missing space after `%2')",
3692 nm.contents(), buf);
3695 if (!warned) {
3696 warning(WARN_MAC, "macro `%1' not defined", nm.contents());
3697 p = new macro;
3698 request_dictionary.define(nm, p);
3701 if (p)
3702 p->invoke(nm);
3703 else {
3704 skip_line();
3705 return;
3709 static void decode_args(macro_iterator *mi)
3711 if (!tok.newline() && !tok.eof()) {
3712 node *n;
3713 int c = get_copy(&n);
3714 for (;;) {
3715 while (c == ' ')
3716 c = get_copy(&n);
3717 if (c == '\n' || c == EOF)
3718 break;
3719 macro arg;
3720 int quote_input_level = 0;
3721 int done_tab_warning = 0;
3722 if (c == '"') {
3723 quote_input_level = input_stack::get_level();
3724 c = get_copy(&n);
3726 arg.append(compatible_flag ? PUSH_COMP_MODE : PUSH_GROFF_MODE);
3727 while (c != EOF && c != '\n' && !(c == ' ' && quote_input_level == 0)) {
3728 if (quote_input_level > 0 && c == '"'
3729 && (compatible_flag
3730 || input_stack::get_level() == quote_input_level)) {
3731 c = get_copy(&n);
3732 if (c == '"') {
3733 arg.append(c);
3734 c = get_copy(&n);
3736 else
3737 break;
3739 else {
3740 if (c == 0)
3741 arg.append(n);
3742 else {
3743 if (c == '\t' && quote_input_level == 0 && !done_tab_warning) {
3744 warning(WARN_TAB, "tab character in unquoted macro argument");
3745 done_tab_warning = 1;
3747 arg.append(c);
3749 c = get_copy(&n);
3752 arg.append(POP_GROFFCOMP_MODE);
3753 mi->add_arg(arg);
3758 static void decode_string_args(macro_iterator *mi)
3760 node *n;
3761 int c = get_copy(&n);
3762 for (;;) {
3763 while (c == ' ')
3764 c = get_copy(&n);
3765 if (c == '\n' || c == EOF) {
3766 error("missing `]'");
3767 break;
3769 if (c == ']')
3770 break;
3771 macro arg;
3772 int quote_input_level = 0;
3773 int done_tab_warning = 0;
3774 if (c == '"') {
3775 quote_input_level = input_stack::get_level();
3776 c = get_copy(&n);
3778 while (c != EOF && c != '\n'
3779 && !(c == ']' && quote_input_level == 0)
3780 && !(c == ' ' && quote_input_level == 0)) {
3781 if (quote_input_level > 0 && c == '"'
3782 && input_stack::get_level() == quote_input_level) {
3783 c = get_copy(&n);
3784 if (c == '"') {
3785 arg.append(c);
3786 c = get_copy(&n);
3788 else
3789 break;
3791 else {
3792 if (c == 0)
3793 arg.append(n);
3794 else {
3795 if (c == '\t' && quote_input_level == 0 && !done_tab_warning) {
3796 warning(WARN_TAB, "tab character in unquoted string argument");
3797 done_tab_warning = 1;
3799 arg.append(c);
3801 c = get_copy(&n);
3804 mi->add_arg(arg);
3808 void macro::invoke(symbol nm)
3810 macro_iterator *mi = new macro_iterator(nm, *this);
3811 decode_args(mi);
3812 input_stack::push(mi);
3813 tok.next();
3816 macro *macro::to_macro()
3818 return this;
3821 int macro::empty()
3823 return empty_macro == 1;
3826 macro_iterator::macro_iterator(symbol s, macro &m, const char *how_called)
3827 : string_iterator(m, how_called, s), args(0), argc(0)
3831 macro_iterator::macro_iterator() : args(0), argc(0)
3835 macro_iterator::~macro_iterator()
3837 while (args != 0) {
3838 arg_list *tem = args;
3839 args = args->next;
3840 delete tem;
3844 dictionary composite_dictionary(17);
3846 void composite_request()
3848 symbol from = get_name(1);
3849 if (!from.is_null()) {
3850 const char *from_gn = glyph_name_to_unicode(from.contents());
3851 if (!from_gn) {
3852 from_gn = check_unicode_name(from.contents());
3853 if (!from_gn) {
3854 error("invalid composite glyph name `%1'", from.contents());
3855 skip_line();
3856 return;
3859 const char *from_decomposed = decompose_unicode(from_gn);
3860 if (from_decomposed)
3861 from_gn = &from_decomposed[1];
3862 symbol to = get_name(1);
3863 if (to.is_null())
3864 composite_dictionary.remove(symbol(from_gn));
3865 else {
3866 const char *to_gn = glyph_name_to_unicode(to.contents());
3867 if (!to_gn) {
3868 to_gn = check_unicode_name(to.contents());
3869 if (!to_gn) {
3870 error("invalid composite glyph name `%1'", to.contents());
3871 skip_line();
3872 return;
3875 const char *to_decomposed = decompose_unicode(to_gn);
3876 if (to_decomposed)
3877 to_gn = &to_decomposed[1];
3878 if (strcmp(from_gn, to_gn) == 0)
3879 composite_dictionary.remove(symbol(from_gn));
3880 else
3881 (void)composite_dictionary.lookup(symbol(from_gn), (void *)to_gn);
3884 skip_line();
3887 static symbol composite_glyph_name(symbol nm)
3889 macro_iterator *mi = new macro_iterator();
3890 decode_string_args(mi);
3891 input_stack::push(mi);
3892 const char *gn = glyph_name_to_unicode(nm.contents());
3893 if (!gn) {
3894 gn = check_unicode_name(nm.contents());
3895 if (!gn) {
3896 error("invalid base glyph `%1' in composite glyph name", nm.contents());
3897 return EMPTY_SYMBOL;
3900 const char *gn_decomposed = decompose_unicode(gn);
3901 string glyph_name(gn_decomposed ? &gn_decomposed[1] : gn);
3902 string gl;
3903 int n = input_stack::nargs();
3904 for (int i = 1; i <= n; i++) {
3905 glyph_name += '_';
3906 input_iterator *p = input_stack::get_arg(i);
3907 gl.clear();
3908 int c;
3909 while ((c = p->get(0)) != EOF)
3910 gl += c;
3911 gl += '\0';
3912 const char *u = glyph_name_to_unicode(gl.contents());
3913 if (!u) {
3914 u = check_unicode_name(gl.contents());
3915 if (!u) {
3916 error("invalid component `%1' in composite glyph name",
3917 gl.contents());
3918 return EMPTY_SYMBOL;
3921 const char *decomposed = decompose_unicode(u);
3922 if (decomposed)
3923 u = &decomposed[1];
3924 void *mapped_composite = composite_dictionary.lookup(symbol(u));
3925 if (mapped_composite)
3926 u = (const char *)mapped_composite;
3927 glyph_name += u;
3929 glyph_name += '\0';
3930 const char *groff_gn = unicode_to_glyph_name(glyph_name.contents());
3931 if (groff_gn)
3932 return symbol(groff_gn);
3933 gl.clear();
3934 gl += 'u';
3935 gl += glyph_name;
3936 return symbol(gl.contents());
3939 int trap_sprung_flag = 0;
3940 int postpone_traps_flag = 0;
3941 symbol postponed_trap;
3943 void spring_trap(symbol nm)
3945 assert(!nm.is_null());
3946 trap_sprung_flag = 1;
3947 if (postpone_traps_flag) {
3948 postponed_trap = nm;
3949 return;
3951 static char buf[2] = { BEGIN_TRAP, 0 };
3952 static char buf2[2] = { END_TRAP, '\0' };
3953 input_stack::push(make_temp_iterator(buf2));
3954 request_or_macro *p = lookup_request(nm);
3955 macro *m = p->to_macro();
3956 if (m)
3957 input_stack::push(new macro_iterator(nm, *m, "trap-invoked macro"));
3958 else
3959 error("you can't invoke a request with a trap");
3960 input_stack::push(make_temp_iterator(buf));
3963 void postpone_traps()
3965 postpone_traps_flag = 1;
3968 int unpostpone_traps()
3970 postpone_traps_flag = 0;
3971 if (!postponed_trap.is_null()) {
3972 spring_trap(postponed_trap);
3973 postponed_trap = NULL_SYMBOL;
3974 return 1;
3976 else
3977 return 0;
3980 void read_request()
3982 macro_iterator *mi = new macro_iterator;
3983 int reading_from_terminal = isatty(fileno(stdin));
3984 int had_prompt = 0;
3985 if (!tok.newline() && !tok.eof()) {
3986 int c = get_copy(0);
3987 while (c == ' ')
3988 c = get_copy(0);
3989 while (c != EOF && c != '\n' && c != ' ') {
3990 if (!invalid_input_char(c)) {
3991 if (reading_from_terminal)
3992 fputc(c, stderr);
3993 had_prompt = 1;
3995 c = get_copy(0);
3997 if (c == ' ') {
3998 tok.make_space();
3999 decode_args(mi);
4002 if (reading_from_terminal) {
4003 fputc(had_prompt ? ':' : '\a', stderr);
4004 fflush(stderr);
4006 input_stack::push(mi);
4007 macro mac;
4008 int nl = 0;
4009 int c;
4010 while ((c = getchar()) != EOF) {
4011 if (invalid_input_char(c))
4012 warning(WARN_INPUT, "invalid input character code %1", int(c));
4013 else {
4014 if (c == '\n') {
4015 if (nl)
4016 break;
4017 else
4018 nl = 1;
4020 else
4021 nl = 0;
4022 mac.append(c);
4025 if (reading_from_terminal)
4026 clearerr(stdin);
4027 input_stack::push(new string_iterator(mac));
4028 tok.next();
4031 enum define_mode { DEFINE_NORMAL, DEFINE_APPEND, DEFINE_IGNORE };
4032 enum calling_mode { CALLING_NORMAL, CALLING_INDIRECT };
4033 enum comp_mode { COMP_IGNORE, COMP_DISABLE, COMP_ENABLE };
4035 void do_define_string(define_mode mode, comp_mode comp)
4037 symbol nm;
4038 node *n = 0; // pacify compiler
4039 int c;
4040 nm = get_name(1);
4041 if (nm.is_null()) {
4042 skip_line();
4043 return;
4045 if (tok.newline())
4046 c = '\n';
4047 else if (tok.tab())
4048 c = '\t';
4049 else if (!tok.space()) {
4050 error("bad string definition");
4051 skip_line();
4052 return;
4054 else
4055 c = get_copy(&n);
4056 while (c == ' ')
4057 c = get_copy(&n);
4058 if (c == '"')
4059 c = get_copy(&n);
4060 macro mac;
4061 request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm);
4062 macro *mm = rm ? rm->to_macro() : 0;
4063 if (mode == DEFINE_APPEND && mm)
4064 mac = *mm;
4065 if (comp == COMP_DISABLE)
4066 mac.append(PUSH_GROFF_MODE);
4067 else if (comp == COMP_ENABLE)
4068 mac.append(PUSH_COMP_MODE);
4069 while (c != '\n' && c != EOF) {
4070 if (c == 0)
4071 mac.append(n);
4072 else
4073 mac.append((unsigned char)c);
4074 c = get_copy(&n);
4076 if (!mm) {
4077 mm = new macro;
4078 request_dictionary.define(nm, mm);
4080 if (comp == COMP_DISABLE || comp == COMP_ENABLE)
4081 mac.append(POP_GROFFCOMP_MODE);
4082 *mm = mac;
4083 tok.next();
4086 void define_string()
4088 do_define_string(DEFINE_NORMAL,
4089 compatible_flag ? COMP_ENABLE: COMP_IGNORE);
4092 void define_nocomp_string()
4094 do_define_string(DEFINE_NORMAL, COMP_DISABLE);
4097 void append_string()
4099 do_define_string(DEFINE_APPEND,
4100 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4103 void append_nocomp_string()
4105 do_define_string(DEFINE_APPEND, COMP_DISABLE);
4108 void do_define_character(char_mode mode, const char *font_name)
4110 node *n = 0; // pacify compiler
4111 int c;
4112 tok.skip();
4113 charinfo *ci = tok.get_char(1);
4114 if (ci == 0) {
4115 skip_line();
4116 return;
4118 if (font_name) {
4119 string s(font_name);
4120 s += ' ';
4121 s += ci->nm.contents();
4122 s += '\0';
4123 ci = get_charinfo(symbol(s.contents()));
4125 tok.next();
4126 if (tok.newline())
4127 c = '\n';
4128 else if (tok.tab())
4129 c = '\t';
4130 else if (!tok.space()) {
4131 error("bad character definition");
4132 skip_line();
4133 return;
4135 else
4136 c = get_copy(&n);
4137 while (c == ' ' || c == '\t')
4138 c = get_copy(&n);
4139 if (c == '"')
4140 c = get_copy(&n);
4141 macro *m = new macro;
4142 while (c != '\n' && c != EOF) {
4143 if (c == 0)
4144 m->append(n);
4145 else
4146 m->append((unsigned char)c);
4147 c = get_copy(&n);
4149 m = ci->setx_macro(m, mode);
4150 if (m)
4151 delete m;
4152 tok.next();
4155 void define_character()
4157 do_define_character(CHAR_NORMAL);
4160 void define_fallback_character()
4162 do_define_character(CHAR_FALLBACK);
4165 void define_special_character()
4167 do_define_character(CHAR_SPECIAL);
4170 static void remove_character()
4172 tok.skip();
4173 while (!tok.newline() && !tok.eof()) {
4174 if (!tok.space() && !tok.tab()) {
4175 charinfo *ci = tok.get_char(1);
4176 if (!ci)
4177 break;
4178 macro *m = ci->set_macro(0);
4179 if (m)
4180 delete m;
4182 tok.next();
4184 skip_line();
4187 static void interpolate_string(symbol nm)
4189 request_or_macro *p = lookup_request(nm);
4190 macro *m = p->to_macro();
4191 if (!m)
4192 error("you can only invoke a string or macro using \\*");
4193 else {
4194 string_iterator *si = new string_iterator(*m, "string", nm);
4195 input_stack::push(si);
4199 static void interpolate_string_with_args(symbol s)
4201 request_or_macro *p = lookup_request(s);
4202 macro *m = p->to_macro();
4203 if (!m)
4204 error("you can only invoke a string or macro using \\*");
4205 else {
4206 macro_iterator *mi = new macro_iterator(s, *m);
4207 decode_string_args(mi);
4208 input_stack::push(mi);
4212 static void interpolate_arg(symbol nm)
4214 const char *s = nm.contents();
4215 if (!s || *s == '\0')
4216 copy_mode_error("missing argument name");
4217 else if (s[1] == 0 && csdigit(s[0]))
4218 input_stack::push(input_stack::get_arg(s[0] - '0'));
4219 else if (s[0] == '*' && s[1] == '\0') {
4220 int limit = input_stack::nargs();
4221 string args;
4222 for (int i = 1; i <= limit; i++) {
4223 input_iterator *p = input_stack::get_arg(i);
4224 int c;
4225 while ((c = p->get(0)) != EOF)
4226 args += c;
4227 if (i != limit)
4228 args += ' ';
4230 if (limit > 0) {
4231 args += '\0';
4232 input_stack::push(make_temp_iterator(args.contents()));
4235 else if (s[0] == '@' && s[1] == '\0') {
4236 int limit = input_stack::nargs();
4237 string args;
4238 for (int i = 1; i <= limit; i++) {
4239 args += '"';
4240 args += BEGIN_QUOTE;
4241 input_iterator *p = input_stack::get_arg(i);
4242 int c;
4243 while ((c = p->get(0)) != EOF)
4244 args += c;
4245 args += END_QUOTE;
4246 args += '"';
4247 if (i != limit)
4248 args += ' ';
4250 if (limit > 0) {
4251 args += '\0';
4252 input_stack::push(make_temp_iterator(args.contents()));
4255 else {
4256 const char *p;
4257 for (p = s; *p && csdigit(*p); p++)
4259 if (*p)
4260 copy_mode_error("bad argument name `%1'", s);
4261 else
4262 input_stack::push(input_stack::get_arg(atoi(s)));
4266 void handle_first_page_transition()
4268 push_token(tok);
4269 topdiv->begin_page();
4272 // We push back a token by wrapping it up in a token_node, and
4273 // wrapping that up in a string_iterator.
4275 static void push_token(const token &t)
4277 macro m;
4278 m.append(new token_node(t));
4279 input_stack::push(new string_iterator(m));
4282 void push_page_ejector()
4284 static char buf[2] = { PAGE_EJECTOR, '\0' };
4285 input_stack::push(make_temp_iterator(buf));
4288 void handle_initial_request(unsigned char code)
4290 char buf[2];
4291 buf[0] = code;
4292 buf[1] = '\0';
4293 macro mac;
4294 mac.append(new token_node(tok));
4295 input_stack::push(new string_iterator(mac));
4296 input_stack::push(make_temp_iterator(buf));
4297 topdiv->begin_page();
4298 tok.next();
4301 void handle_initial_title()
4303 handle_initial_request(TITLE_REQUEST);
4306 // this should be local to define_macro, but cfront 1.2 doesn't support that
4307 static symbol dot_symbol(".");
4309 void do_define_macro(define_mode mode, calling_mode calling, comp_mode comp)
4311 symbol nm, term;
4312 if (calling == CALLING_INDIRECT) {
4313 symbol temp1 = get_name(1);
4314 if (temp1.is_null()) {
4315 skip_line();
4316 return;
4318 symbol temp2 = get_name();
4319 input_stack::push(make_temp_iterator("\n"));
4320 if (!temp2.is_null()) {
4321 interpolate_string(temp2);
4322 input_stack::push(make_temp_iterator(" "));
4324 interpolate_string(temp1);
4325 input_stack::push(make_temp_iterator(" "));
4326 tok.next();
4328 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4329 nm = get_name(1);
4330 if (nm.is_null()) {
4331 skip_line();
4332 return;
4335 term = get_name(); // the request that terminates the definition
4336 if (term.is_null())
4337 term = dot_symbol;
4338 while (!tok.newline() && !tok.eof())
4339 tok.next();
4340 const char *start_filename;
4341 int start_lineno;
4342 int have_start_location = input_stack::get_location(0, &start_filename,
4343 &start_lineno);
4344 node *n;
4345 // doing this here makes the line numbers come out right
4346 int c = get_copy(&n, 1);
4347 macro mac;
4348 macro *mm = 0;
4349 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4350 request_or_macro *rm =
4351 (request_or_macro *)request_dictionary.lookup(nm);
4352 if (rm)
4353 mm = rm->to_macro();
4354 if (mm && mode == DEFINE_APPEND)
4355 mac = *mm;
4357 int bol = 1;
4358 if (comp == COMP_DISABLE)
4359 mac.append(PUSH_GROFF_MODE);
4360 else if (comp == COMP_ENABLE)
4361 mac.append(PUSH_COMP_MODE);
4362 for (;;) {
4363 while (c == ESCAPE_NEWLINE) {
4364 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND)
4365 mac.append(c);
4366 c = get_copy(&n, 1);
4368 if (bol && c == '.') {
4369 const char *s = term.contents();
4370 int d = 0;
4371 // see if it matches term
4372 int i = 0;
4373 if (s[0] != 0) {
4374 while ((d = get_copy(&n)) == ' ' || d == '\t')
4376 if ((unsigned char)s[0] == d) {
4377 for (i = 1; s[i] != 0; i++) {
4378 d = get_copy(&n);
4379 if ((unsigned char)s[i] != d)
4380 break;
4384 if (s[i] == 0
4385 && ((i == 2 && compatible_flag)
4386 || (d = get_copy(&n)) == ' '
4387 || d == '\n')) { // we found it
4388 if (d == '\n')
4389 tok.make_newline();
4390 else
4391 tok.make_space();
4392 if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) {
4393 if (!mm) {
4394 mm = new macro;
4395 request_dictionary.define(nm, mm);
4397 if (comp == COMP_DISABLE || comp == COMP_ENABLE)
4398 mac.append(POP_GROFFCOMP_MODE);
4399 *mm = mac;
4401 if (term != dot_symbol) {
4402 ignoring = 0;
4403 interpolate_macro(term);
4405 else
4406 skip_line();
4407 return;
4409 if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) {
4410 mac.append(c);
4411 for (int j = 0; j < i; j++)
4412 mac.append(s[j]);
4414 c = d;
4416 if (c == EOF) {
4417 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4418 if (have_start_location)
4419 error_with_file_and_line(start_filename, start_lineno,
4420 "end of file while defining macro `%1'",
4421 nm.contents());
4422 else
4423 error("end of file while defining macro `%1'", nm.contents());
4425 else {
4426 if (have_start_location)
4427 error_with_file_and_line(start_filename, start_lineno,
4428 "end of file while ignoring input lines");
4429 else
4430 error("end of file while ignoring input lines");
4432 tok.next();
4433 return;
4435 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4436 if (c == 0)
4437 mac.append(n);
4438 else
4439 mac.append(c);
4441 bol = (c == '\n');
4442 c = get_copy(&n, 1);
4446 void define_macro()
4448 do_define_macro(DEFINE_NORMAL, CALLING_NORMAL,
4449 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4452 void define_nocomp_macro()
4454 do_define_macro(DEFINE_NORMAL, CALLING_NORMAL, COMP_DISABLE);
4457 void define_indirect_macro()
4459 do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT,
4460 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4463 void define_indirect_nocomp_macro()
4465 do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT, COMP_DISABLE);
4468 void append_macro()
4470 do_define_macro(DEFINE_APPEND, CALLING_NORMAL,
4471 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4474 void append_nocomp_macro()
4476 do_define_macro(DEFINE_APPEND, CALLING_NORMAL, COMP_DISABLE);
4479 void append_indirect_macro()
4481 do_define_macro(DEFINE_APPEND, CALLING_INDIRECT,
4482 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4485 void append_indirect_nocomp_macro()
4487 do_define_macro(DEFINE_APPEND, CALLING_INDIRECT, COMP_DISABLE);
4490 void ignore()
4492 ignoring = 1;
4493 do_define_macro(DEFINE_IGNORE, CALLING_NORMAL, COMP_IGNORE);
4494 ignoring = 0;
4497 void remove_macro()
4499 for (;;) {
4500 symbol s = get_name();
4501 if (s.is_null())
4502 break;
4503 request_dictionary.remove(s);
4505 skip_line();
4508 void rename_macro()
4510 symbol s1 = get_name(1);
4511 if (!s1.is_null()) {
4512 symbol s2 = get_name(1);
4513 if (!s2.is_null())
4514 request_dictionary.rename(s1, s2);
4516 skip_line();
4519 void alias_macro()
4521 symbol s1 = get_name(1);
4522 if (!s1.is_null()) {
4523 symbol s2 = get_name(1);
4524 if (!s2.is_null()) {
4525 if (!request_dictionary.alias(s1, s2))
4526 warning(WARN_MAC, "macro `%1' not defined", s2.contents());
4529 skip_line();
4532 void chop_macro()
4534 symbol s = get_name(1);
4535 if (!s.is_null()) {
4536 request_or_macro *p = lookup_request(s);
4537 macro *m = p->to_macro();
4538 if (!m)
4539 error("cannot chop request");
4540 else if (m->empty())
4541 error("cannot chop empty macro");
4542 else {
4543 int have_restore = 0;
4544 // we have to check for additional save/restore pairs which could be
4545 // there due to empty am1 requests.
4546 for (;;) {
4547 if (m->get(m->len - 1) != POP_GROFFCOMP_MODE)
4548 break;
4549 have_restore = 1;
4550 m->len -= 1;
4551 if (m->get(m->len - 1) != PUSH_GROFF_MODE
4552 && m->get(m->len - 1) != PUSH_COMP_MODE)
4553 break;
4554 have_restore = 0;
4555 m->len -= 1;
4556 if (m->len == 0)
4557 break;
4559 if (m->len == 0)
4560 error("cannot chop empty macro");
4561 else {
4562 if (have_restore)
4563 m->set(POP_GROFFCOMP_MODE, m->len - 1);
4564 else
4565 m->len -= 1;
4569 skip_line();
4572 void substring_request()
4574 int start; // 0, 1, ..., n-1 or -1, -2, ...
4575 symbol s = get_name(1);
4576 if (!s.is_null() && get_integer(&start)) {
4577 request_or_macro *p = lookup_request(s);
4578 macro *m = p->to_macro();
4579 if (!m)
4580 error("cannot apply `substring' on a request");
4581 else {
4582 int end = -1;
4583 if (!has_arg() || get_integer(&end)) {
4584 int real_length = 0; // 1, 2, ..., n
4585 string_iterator iter1(*m);
4586 for (int l = 0; l < m->len; l++) {
4587 int c = iter1.get(0);
4588 if (c == PUSH_GROFF_MODE
4589 || c == PUSH_COMP_MODE
4590 || c == POP_GROFFCOMP_MODE)
4591 continue;
4592 if (c == EOF)
4593 break;
4594 real_length++;
4596 if (start < 0)
4597 start += real_length;
4598 if (end < 0)
4599 end += real_length;
4600 if (start > end) {
4601 int tem = start;
4602 start = end;
4603 end = tem;
4605 if (start >= real_length || end < 0) {
4606 warning(WARN_RANGE,
4607 "start and end index of substring out of range");
4608 m->len = 0;
4609 if (m->p) {
4610 if (--(m->p->count) <= 0)
4611 delete m->p;
4612 m->p = 0;
4614 skip_line();
4615 return;
4617 if (start < 0) {
4618 warning(WARN_RANGE,
4619 "start index of substring out of range, set to 0");
4620 start = 0;
4622 if (end >= real_length) {
4623 warning(WARN_RANGE,
4624 "end index of substring out of range, set to string length");
4625 end = real_length - 1;
4627 // now extract the substring
4628 string_iterator iter(*m);
4629 int i;
4630 for (i = 0; i < start; i++) {
4631 int c = iter.get(0);
4632 while (c == PUSH_GROFF_MODE
4633 || c == PUSH_COMP_MODE
4634 || c == POP_GROFFCOMP_MODE)
4635 c = iter.get(0);
4636 if (c == EOF)
4637 break;
4639 macro mac;
4640 for (; i <= end; i++) {
4641 node *nd = 0; // pacify compiler
4642 int c = iter.get(&nd);
4643 while (c == PUSH_GROFF_MODE
4644 || c == PUSH_COMP_MODE
4645 || c == POP_GROFFCOMP_MODE)
4646 c = iter.get(0);
4647 if (c == EOF)
4648 break;
4649 if (c == 0)
4650 mac.append(nd);
4651 else
4652 mac.append((unsigned char)c);
4654 *m = mac;
4658 skip_line();
4661 void length_request()
4663 symbol ret;
4664 ret = get_name(1);
4665 if (ret.is_null()) {
4666 skip_line();
4667 return;
4669 int c;
4670 node *n;
4671 if (tok.newline())
4672 c = '\n';
4673 else if (tok.tab())
4674 c = '\t';
4675 else if (!tok.space()) {
4676 error("bad string definition");
4677 skip_line();
4678 return;
4680 else
4681 c = get_copy(&n);
4682 while (c == ' ')
4683 c = get_copy(&n);
4684 if (c == '"')
4685 c = get_copy(&n);
4686 int len = 0;
4687 while (c != '\n' && c != EOF) {
4688 ++len;
4689 c = get_copy(&n);
4691 reg *r = (reg*)number_reg_dictionary.lookup(ret);
4692 if (r)
4693 r->set_value(len);
4694 else
4695 set_number_reg(ret, len);
4696 tok.next();
4699 void asciify_macro()
4701 symbol s = get_name(1);
4702 if (!s.is_null()) {
4703 request_or_macro *p = lookup_request(s);
4704 macro *m = p->to_macro();
4705 if (!m)
4706 error("cannot asciify request");
4707 else {
4708 macro am;
4709 string_iterator iter(*m);
4710 for (;;) {
4711 node *nd = 0; // pacify compiler
4712 int c = iter.get(&nd);
4713 if (c == EOF)
4714 break;
4715 if (c != 0)
4716 am.append(c);
4717 else
4718 nd->asciify(&am);
4720 *m = am;
4723 skip_line();
4726 void unformat_macro()
4728 symbol s = get_name(1);
4729 if (!s.is_null()) {
4730 request_or_macro *p = lookup_request(s);
4731 macro *m = p->to_macro();
4732 if (!m)
4733 error("cannot unformat request");
4734 else {
4735 macro am;
4736 string_iterator iter(*m);
4737 for (;;) {
4738 node *nd = 0; // pacify compiler
4739 int c = iter.get(&nd);
4740 if (c == EOF)
4741 break;
4742 if (c != 0)
4743 am.append(c);
4744 else {
4745 if (nd->set_unformat_flag())
4746 am.append(nd);
4749 *m = am;
4752 skip_line();
4755 static void interpolate_environment_variable(symbol nm)
4757 const char *s = getenv(nm.contents());
4758 if (s && *s)
4759 input_stack::push(make_temp_iterator(s));
4762 void interpolate_number_reg(symbol nm, int inc)
4764 reg *r = lookup_number_reg(nm);
4765 if (inc < 0)
4766 r->decrement();
4767 else if (inc > 0)
4768 r->increment();
4769 input_stack::push(make_temp_iterator(r->get_string()));
4772 static void interpolate_number_format(symbol nm)
4774 reg *r = (reg *)number_reg_dictionary.lookup(nm);
4775 if (r)
4776 input_stack::push(make_temp_iterator(r->get_format()));
4779 static int get_delim_number(units *n, unsigned char si, int prev_value)
4781 token start;
4782 start.next();
4783 if (start.delimiter(1)) {
4784 tok.next();
4785 if (get_number(n, si, prev_value)) {
4786 if (start != tok)
4787 warning(WARN_DELIM, "closing delimiter does not match");
4788 return 1;
4791 return 0;
4794 static int get_delim_number(units *n, unsigned char si)
4796 token start;
4797 start.next();
4798 if (start.delimiter(1)) {
4799 tok.next();
4800 if (get_number(n, si)) {
4801 if (start != tok)
4802 warning(WARN_DELIM, "closing delimiter does not match");
4803 return 1;
4806 return 0;
4809 static int get_line_arg(units *n, unsigned char si, charinfo **cp)
4811 token start;
4812 start.next();
4813 int start_level = input_stack::get_level();
4814 if (!start.delimiter(1))
4815 return 0;
4816 tok.next();
4817 if (get_number(n, si)) {
4818 if (tok.dummy() || tok.transparent_dummy())
4819 tok.next();
4820 if (!(start == tok && input_stack::get_level() == start_level)) {
4821 *cp = tok.get_char(1);
4822 tok.next();
4824 if (!(start == tok && input_stack::get_level() == start_level))
4825 warning(WARN_DELIM, "closing delimiter does not match");
4826 return 1;
4828 return 0;
4831 static int read_size(int *x)
4833 tok.next();
4834 int c = tok.ch();
4835 int inc = 0;
4836 if (c == '-') {
4837 inc = -1;
4838 tok.next();
4839 c = tok.ch();
4841 else if (c == '+') {
4842 inc = 1;
4843 tok.next();
4844 c = tok.ch();
4846 int val = 0; // pacify compiler
4847 int bad = 0;
4848 if (c == '(') {
4849 tok.next();
4850 c = tok.ch();
4851 if (!inc) {
4852 // allow an increment either before or after the left parenthesis
4853 if (c == '-') {
4854 inc = -1;
4855 tok.next();
4856 c = tok.ch();
4858 else if (c == '+') {
4859 inc = 1;
4860 tok.next();
4861 c = tok.ch();
4864 if (!csdigit(c))
4865 bad = 1;
4866 else {
4867 val = c - '0';
4868 tok.next();
4869 c = tok.ch();
4870 if (!csdigit(c))
4871 bad = 1;
4872 else {
4873 val = val*10 + (c - '0');
4874 val *= sizescale;
4878 else if (csdigit(c)) {
4879 val = c - '0';
4880 if (!inc && c != '0' && c < '4') {
4881 tok.next();
4882 c = tok.ch();
4883 if (!csdigit(c))
4884 bad = 1;
4885 else
4886 val = val*10 + (c - '0');
4888 val *= sizescale;
4890 else if (!tok.delimiter(1))
4891 return 0;
4892 else {
4893 token start(tok);
4894 tok.next();
4895 if (!(inc
4896 ? get_number(&val, 'z')
4897 : get_number(&val, 'z', curenv->get_requested_point_size())))
4898 return 0;
4899 if (!(start.ch() == '[' && tok.ch() == ']') && start != tok) {
4900 if (start.ch() == '[')
4901 error("missing `]'");
4902 else
4903 error("missing closing delimiter");
4904 return 0;
4907 if (!bad) {
4908 switch (inc) {
4909 case 0:
4910 if (val == 0) {
4911 // special case -- \s[0] and \s0 means to revert to previous size
4912 *x = 0;
4913 return 1;
4915 *x = val;
4916 break;
4917 case 1:
4918 *x = curenv->get_requested_point_size() + val;
4919 break;
4920 case -1:
4921 *x = curenv->get_requested_point_size() - val;
4922 break;
4923 default:
4924 assert(0);
4926 if (*x <= 0) {
4927 warning(WARN_RANGE,
4928 "\\s request results in non-positive point size; set to 1");
4929 *x = 1;
4931 return 1;
4933 else {
4934 error("bad digit in point size");
4935 return 0;
4939 static symbol get_delim_name()
4941 token start;
4942 start.next();
4943 if (start.eof()) {
4944 error("end of input at start of delimited name");
4945 return NULL_SYMBOL;
4947 if (start.newline()) {
4948 error("can't delimit name with a newline");
4949 return NULL_SYMBOL;
4951 int start_level = input_stack::get_level();
4952 char abuf[ABUF_SIZE];
4953 char *buf = abuf;
4954 int buf_size = ABUF_SIZE;
4955 int i = 0;
4956 for (;;) {
4957 if (i + 1 > buf_size) {
4958 if (buf == abuf) {
4959 buf = new char[ABUF_SIZE*2];
4960 memcpy(buf, abuf, buf_size);
4961 buf_size = ABUF_SIZE*2;
4963 else {
4964 char *old_buf = buf;
4965 buf = new char[buf_size*2];
4966 memcpy(buf, old_buf, buf_size);
4967 buf_size *= 2;
4968 a_delete old_buf;
4971 tok.next();
4972 if (tok == start
4973 && (compatible_flag || input_stack::get_level() == start_level))
4974 break;
4975 if ((buf[i] = tok.ch()) == 0) {
4976 error("missing delimiter (got %1)", tok.description());
4977 if (buf != abuf)
4978 a_delete buf;
4979 return NULL_SYMBOL;
4981 i++;
4983 buf[i] = '\0';
4984 if (buf == abuf) {
4985 if (i == 0) {
4986 error("empty delimited name");
4987 return NULL_SYMBOL;
4989 else
4990 return symbol(buf);
4992 else {
4993 symbol s(buf);
4994 a_delete buf;
4995 return s;
4999 // Implement \R
5001 static void do_register()
5003 token start;
5004 start.next();
5005 if (!start.delimiter(1))
5006 return;
5007 tok.next();
5008 symbol nm = get_long_name(1);
5009 if (nm.is_null())
5010 return;
5011 while (tok.space())
5012 tok.next();
5013 reg *r = (reg *)number_reg_dictionary.lookup(nm);
5014 int prev_value;
5015 if (!r || !r->get_value(&prev_value))
5016 prev_value = 0;
5017 int val;
5018 if (!get_number(&val, 'u', prev_value))
5019 return;
5020 if (start != tok)
5021 warning(WARN_DELIM, "closing delimiter does not match");
5022 if (r)
5023 r->set_value(val);
5024 else
5025 set_number_reg(nm, val);
5028 // this implements the \w escape sequence
5030 static void do_width()
5032 token start;
5033 start.next();
5034 int start_level = input_stack::get_level();
5035 environment env(curenv);
5036 environment *oldenv = curenv;
5037 curenv = &env;
5038 for (;;) {
5039 tok.next();
5040 if (tok.eof()) {
5041 warning(WARN_DELIM, "missing closing delimiter");
5042 break;
5044 if (tok.newline()) {
5045 warning(WARN_DELIM, "missing closing delimiter");
5046 input_stack::push(make_temp_iterator("\n"));
5047 break;
5049 if (tok == start
5050 && (compatible_flag || input_stack::get_level() == start_level))
5051 break;
5052 tok.process();
5054 env.wrap_up_tab();
5055 units x = env.get_input_line_position().to_units();
5056 input_stack::push(make_temp_iterator(i_to_a(x)));
5057 env.width_registers();
5058 curenv = oldenv;
5059 have_input = 0;
5062 charinfo *page_character;
5064 void set_page_character()
5066 page_character = get_optional_char();
5067 skip_line();
5070 static const symbol percent_symbol("%");
5072 void read_title_parts(node **part, hunits *part_width)
5074 tok.skip();
5075 if (tok.newline() || tok.eof())
5076 return;
5077 token start(tok);
5078 int start_level = input_stack::get_level();
5079 tok.next();
5080 for (int i = 0; i < 3; i++) {
5081 while (!tok.newline() && !tok.eof()) {
5082 if (tok == start
5083 && (compatible_flag || input_stack::get_level() == start_level)) {
5084 tok.next();
5085 break;
5087 if (page_character != 0 && tok.get_char() == page_character)
5088 interpolate_number_reg(percent_symbol, 0);
5089 else
5090 tok.process();
5091 tok.next();
5093 curenv->wrap_up_tab();
5094 part_width[i] = curenv->get_input_line_position();
5095 part[i] = curenv->extract_output_line();
5097 while (!tok.newline() && !tok.eof())
5098 tok.next();
5101 class non_interpreted_node : public node {
5102 macro mac;
5103 public:
5104 non_interpreted_node(const macro &);
5105 int interpret(macro *);
5106 node *copy();
5107 int ends_sentence();
5108 int same(node *);
5109 const char *type();
5110 int force_tprint();
5111 int is_tag();
5114 non_interpreted_node::non_interpreted_node(const macro &m) : mac(m)
5118 int non_interpreted_node::ends_sentence()
5120 return 2;
5123 int non_interpreted_node::same(node *nd)
5125 return mac == ((non_interpreted_node *)nd)->mac;
5128 const char *non_interpreted_node::type()
5130 return "non_interpreted_node";
5133 int non_interpreted_node::force_tprint()
5135 return 0;
5138 int non_interpreted_node::is_tag()
5140 return 0;
5143 node *non_interpreted_node::copy()
5145 return new non_interpreted_node(mac);
5148 int non_interpreted_node::interpret(macro *m)
5150 string_iterator si(mac);
5151 node *n = 0; // pacify compiler
5152 for (;;) {
5153 int c = si.get(&n);
5154 if (c == EOF)
5155 break;
5156 if (c == 0)
5157 m->append(n);
5158 else
5159 m->append(c);
5161 return 1;
5164 static node *do_non_interpreted()
5166 node *n;
5167 int c;
5168 macro mac;
5169 while ((c = get_copy(&n)) != ESCAPE_QUESTION && c != EOF && c != '\n')
5170 if (c == 0)
5171 mac.append(n);
5172 else
5173 mac.append(c);
5174 if (c == EOF || c == '\n') {
5175 error("missing \\?");
5176 return 0;
5178 return new non_interpreted_node(mac);
5181 static void encode_char(macro *mac, char c)
5183 if (c == '\0') {
5184 if ((font::use_charnames_in_special) && tok.special()) {
5185 charinfo *ci = tok.get_char(1);
5186 const char *s = ci->get_symbol()->contents();
5187 if (s[0] != (char)0) {
5188 mac->append('\\');
5189 mac->append('(');
5190 int i = 0;
5191 while (s[i] != (char)0) {
5192 mac->append(s[i]);
5193 i++;
5195 mac->append('\\');
5196 mac->append(')');
5199 else if (tok.stretchable_space()
5200 || tok.unstretchable_space())
5201 mac->append(' ');
5202 else if (!(tok.hyphen_indicator()
5203 || tok.dummy()
5204 || tok.transparent_dummy()
5205 || tok.zero_width_break()))
5206 error("%1 is invalid within \\X", tok.description());
5208 else {
5209 if ((font::use_charnames_in_special) && (c == '\\')) {
5211 * add escape escape sequence
5213 mac->append(c);
5215 mac->append(c);
5219 node *do_special()
5221 token start;
5222 start.next();
5223 int start_level = input_stack::get_level();
5224 macro mac;
5225 for (tok.next();
5226 tok != start || input_stack::get_level() != start_level;
5227 tok.next()) {
5228 if (tok.eof()) {
5229 warning(WARN_DELIM, "missing closing delimiter");
5230 return 0;
5232 if (tok.newline()) {
5233 input_stack::push(make_temp_iterator("\n"));
5234 warning(WARN_DELIM, "missing closing delimiter");
5235 break;
5237 unsigned char c;
5238 if (tok.space())
5239 c = ' ';
5240 else if (tok.tab())
5241 c = '\t';
5242 else if (tok.leader())
5243 c = '\001';
5244 else if (tok.backspace())
5245 c = '\b';
5246 else
5247 c = tok.ch();
5248 encode_char(&mac, c);
5250 return new special_node(mac);
5253 void output_request()
5255 if (!tok.newline() && !tok.eof()) {
5256 int c;
5257 for (;;) {
5258 c = get_copy(0);
5259 if (c == '"') {
5260 c = get_copy(0);
5261 break;
5263 if (c != ' ' && c != '\t')
5264 break;
5266 for (; c != '\n' && c != EOF; c = get_copy(0))
5267 topdiv->transparent_output(c);
5268 topdiv->transparent_output('\n');
5270 tok.next();
5273 extern int image_no; // from node.cpp
5275 static node *do_suppress(symbol nm)
5277 if (nm.is_null() || nm.is_empty()) {
5278 error("expecting an argument to escape \\O");
5279 return 0;
5281 const char *s = nm.contents();
5282 switch (*s) {
5283 case '0':
5284 if (begin_level == 0)
5285 // suppress generation of glyphs
5286 return new suppress_node(0, 0);
5287 break;
5288 case '1':
5289 if (begin_level == 0)
5290 // enable generation of glyphs
5291 return new suppress_node(1, 0);
5292 break;
5293 case '2':
5294 if (begin_level == 0)
5295 return new suppress_node(1, 1);
5296 break;
5297 case '3':
5298 begin_level++;
5299 break;
5300 case '4':
5301 begin_level--;
5302 break;
5303 case '5':
5305 s++; // move over '5'
5306 char position = *s;
5307 if (*s == (char)0) {
5308 error("missing position and filename in \\O");
5309 return 0;
5311 if (!(position == 'l'
5312 || position == 'r'
5313 || position == 'c'
5314 || position == 'i')) {
5315 error("l, r, c, or i position expected (got %1 in \\O)", position);
5316 return 0;
5318 s++; // onto image name
5319 if (s == (char *)0) {
5320 error("missing image name for \\O");
5321 return 0;
5323 image_no++;
5324 if (begin_level == 0)
5325 return new suppress_node(symbol(s), position, image_no);
5327 break;
5328 default:
5329 error("`%1' is an invalid argument to \\O", *s);
5331 return 0;
5334 void special_node::tprint(troff_output_file *out)
5336 tprint_start(out);
5337 string_iterator iter(mac);
5338 for (;;) {
5339 int c = iter.get(0);
5340 if (c == EOF)
5341 break;
5342 for (const char *s = ::asciify(c); *s; s++)
5343 tprint_char(out, *s);
5345 tprint_end(out);
5348 int get_file_line(const char **filename, int *lineno)
5350 return input_stack::get_location(0, filename, lineno);
5353 void line_file()
5355 int n;
5356 if (get_integer(&n)) {
5357 const char *filename = 0;
5358 if (has_arg()) {
5359 symbol s = get_long_name();
5360 filename = s.contents();
5362 (void)input_stack::set_location(filename, n-1);
5364 skip_line();
5367 static int nroff_mode = 0;
5369 static void nroff_request()
5371 nroff_mode = 1;
5372 skip_line();
5375 static void troff_request()
5377 nroff_mode = 0;
5378 skip_line();
5381 static void skip_alternative()
5383 int level = 0;
5384 // ensure that ``.if 0\{'' works as expected
5385 if (tok.left_brace())
5386 level++;
5387 int c;
5388 for (;;) {
5389 c = input_stack::get(0);
5390 if (c == EOF)
5391 break;
5392 if (c == ESCAPE_LEFT_BRACE)
5393 ++level;
5394 else if (c == ESCAPE_RIGHT_BRACE)
5395 --level;
5396 else if (c == escape_char && escape_char > 0)
5397 switch(input_stack::get(0)) {
5398 case '{':
5399 ++level;
5400 break;
5401 case '}':
5402 --level;
5403 break;
5404 case '"':
5405 while ((c = input_stack::get(0)) != '\n' && c != EOF)
5409 Note that the level can properly be < 0, eg
5411 .if 1 \{\
5412 .if 0 \{\
5413 .\}\}
5415 So don't give an error message in this case.
5417 if (level <= 0 && c == '\n')
5418 break;
5420 tok.next();
5423 static void begin_alternative()
5425 while (tok.space() || tok.left_brace())
5426 tok.next();
5429 void nop_request()
5431 while (tok.space())
5432 tok.next();
5435 static int_stack if_else_stack;
5437 int do_if_request()
5439 int invert = 0;
5440 while (tok.space())
5441 tok.next();
5442 while (tok.ch() == '!') {
5443 tok.next();
5444 invert = !invert;
5446 int result;
5447 unsigned char c = tok.ch();
5448 if (c == 't') {
5449 tok.next();
5450 result = !nroff_mode;
5452 else if (c == 'n') {
5453 tok.next();
5454 result = nroff_mode;
5456 else if (c == 'v') {
5457 tok.next();
5458 result = 0;
5460 else if (c == 'o') {
5461 result = (topdiv->get_page_number() & 1);
5462 tok.next();
5464 else if (c == 'e') {
5465 result = !(topdiv->get_page_number() & 1);
5466 tok.next();
5468 else if (c == 'd' || c == 'r') {
5469 tok.next();
5470 symbol nm = get_name(1);
5471 if (nm.is_null()) {
5472 skip_alternative();
5473 return 0;
5475 result = (c == 'd'
5476 ? request_dictionary.lookup(nm) != 0
5477 : number_reg_dictionary.lookup(nm) != 0);
5479 else if (c == 'm') {
5480 tok.next();
5481 symbol nm = get_long_name(1);
5482 if (nm.is_null()) {
5483 skip_alternative();
5484 return 0;
5486 result = (nm == default_symbol
5487 || color_dictionary.lookup(nm) != 0);
5489 else if (c == 'c') {
5490 tok.next();
5491 tok.skip();
5492 charinfo *ci = tok.get_char(1);
5493 if (ci == 0) {
5494 skip_alternative();
5495 return 0;
5497 result = character_exists(ci, curenv);
5498 tok.next();
5500 else if (c == 'F') {
5501 tok.next();
5502 symbol nm = get_long_name(1);
5503 if (nm.is_null()) {
5504 skip_alternative();
5505 return 0;
5507 result = check_font(curenv->get_family()->nm, nm);
5509 else if (c == 'S') {
5510 tok.next();
5511 symbol nm = get_long_name(1);
5512 if (nm.is_null()) {
5513 skip_alternative();
5514 return 0;
5516 result = check_style(nm);
5518 else if (tok.space())
5519 result = 0;
5520 else if (tok.delimiter()) {
5521 token delim = tok;
5522 int delim_level = input_stack::get_level();
5523 environment env1(curenv);
5524 environment env2(curenv);
5525 environment *oldenv = curenv;
5526 curenv = &env1;
5527 suppress_push = 1;
5528 for (int i = 0; i < 2; i++) {
5529 for (;;) {
5530 tok.next();
5531 if (tok.newline() || tok.eof()) {
5532 warning(WARN_DELIM, "missing closing delimiter");
5533 tok.next();
5534 curenv = oldenv;
5535 return 0;
5537 if (tok == delim
5538 && (compatible_flag || input_stack::get_level() == delim_level))
5539 break;
5540 tok.process();
5542 curenv = &env2;
5544 node *n1 = env1.extract_output_line();
5545 node *n2 = env2.extract_output_line();
5546 result = same_node_list(n1, n2);
5547 delete_node_list(n1);
5548 delete_node_list(n2);
5549 curenv = oldenv;
5550 have_input = 0;
5551 suppress_push = 0;
5552 tok.next();
5554 else {
5555 units n;
5556 if (!get_number(&n, 'u')) {
5557 skip_alternative();
5558 return 0;
5560 else
5561 result = n > 0;
5563 if (invert)
5564 result = !result;
5565 if (result)
5566 begin_alternative();
5567 else
5568 skip_alternative();
5569 return result;
5572 void if_else_request()
5574 if_else_stack.push(do_if_request());
5577 void if_request()
5579 do_if_request();
5582 void else_request()
5584 if (if_else_stack.is_empty()) {
5585 warning(WARN_EL, "unbalanced .el request");
5586 skip_alternative();
5588 else {
5589 if (if_else_stack.pop())
5590 skip_alternative();
5591 else
5592 begin_alternative();
5596 static int while_depth = 0;
5597 static int while_break_flag = 0;
5599 void while_request()
5601 macro mac;
5602 int escaped = 0;
5603 int level = 0;
5604 mac.append(new token_node(tok));
5605 for (;;) {
5606 node *n = 0; // pacify compiler
5607 int c = input_stack::get(&n);
5608 if (c == EOF)
5609 break;
5610 if (c == 0) {
5611 escaped = 0;
5612 mac.append(n);
5614 else if (escaped) {
5615 if (c == '{')
5616 level += 1;
5617 else if (c == '}')
5618 level -= 1;
5619 escaped = 0;
5620 mac.append(c);
5622 else {
5623 if (c == ESCAPE_LEFT_BRACE)
5624 level += 1;
5625 else if (c == ESCAPE_RIGHT_BRACE)
5626 level -= 1;
5627 else if (c == escape_char)
5628 escaped = 1;
5629 mac.append(c);
5630 if (c == '\n' && level <= 0)
5631 break;
5634 if (level != 0)
5635 error("unbalanced \\{ \\}");
5636 else {
5637 while_depth++;
5638 input_stack::add_boundary();
5639 for (;;) {
5640 input_stack::push(new string_iterator(mac, "while loop"));
5641 tok.next();
5642 if (!do_if_request()) {
5643 while (input_stack::get(0) != EOF)
5645 break;
5647 process_input_stack();
5648 if (while_break_flag || input_stack::is_return_boundary()) {
5649 while_break_flag = 0;
5650 break;
5653 input_stack::remove_boundary();
5654 while_depth--;
5656 tok.next();
5659 void while_break_request()
5661 if (!while_depth) {
5662 error("no while loop");
5663 skip_line();
5665 else {
5666 while_break_flag = 1;
5667 while (input_stack::get(0) != EOF)
5669 tok.next();
5673 void while_continue_request()
5675 if (!while_depth) {
5676 error("no while loop");
5677 skip_line();
5679 else {
5680 while (input_stack::get(0) != EOF)
5682 tok.next();
5686 // .so
5688 void source()
5690 symbol nm = get_long_name(1);
5691 if (nm.is_null())
5692 skip_line();
5693 else {
5694 while (!tok.newline() && !tok.eof())
5695 tok.next();
5696 errno = 0;
5697 FILE *fp = include_search_path.open_file_cautious(nm.contents());
5698 if (fp)
5699 input_stack::push(new file_iterator(fp, nm.contents()));
5700 else
5701 error("can't open `%1': %2", nm.contents(), strerror(errno));
5702 tok.next();
5706 // like .so but use popen()
5708 void pipe_source()
5710 if (safer_flag) {
5711 error(".pso request not allowed in safer mode");
5712 skip_line();
5714 else {
5715 #ifdef POPEN_MISSING
5716 error("pipes not available on this system");
5717 skip_line();
5718 #else /* not POPEN_MISSING */
5719 if (tok.newline() || tok.eof())
5720 error("missing command");
5721 else {
5722 int c;
5723 while ((c = get_copy(0)) == ' ' || c == '\t')
5725 int buf_size = 24;
5726 char *buf = new char[buf_size];
5727 int buf_used = 0;
5728 for (; c != '\n' && c != EOF; c = get_copy(0)) {
5729 const char *s = asciify(c);
5730 int slen = strlen(s);
5731 if (buf_used + slen + 1> buf_size) {
5732 char *old_buf = buf;
5733 int old_buf_size = buf_size;
5734 buf_size *= 2;
5735 buf = new char[buf_size];
5736 memcpy(buf, old_buf, old_buf_size);
5737 a_delete old_buf;
5739 strcpy(buf + buf_used, s);
5740 buf_used += slen;
5742 buf[buf_used] = '\0';
5743 errno = 0;
5744 FILE *fp = popen(buf, POPEN_RT);
5745 if (fp)
5746 input_stack::push(new file_iterator(fp, symbol(buf).contents(), 1));
5747 else
5748 error("can't open pipe to process `%1': %2", buf, strerror(errno));
5749 a_delete buf;
5751 tok.next();
5752 #endif /* not POPEN_MISSING */
5756 // .psbb
5758 static int llx_reg_contents = 0;
5759 static int lly_reg_contents = 0;
5760 static int urx_reg_contents = 0;
5761 static int ury_reg_contents = 0;
5763 struct bounding_box {
5764 int llx, lly, urx, ury;
5767 /* Parse the argument to a %%BoundingBox comment. Return 1 if it
5768 contains 4 numbers, 2 if it contains (atend), 0 otherwise. */
5770 int parse_bounding_box(char *p, bounding_box *bb)
5772 if (sscanf(p, "%d %d %d %d",
5773 &bb->llx, &bb->lly, &bb->urx, &bb->ury) == 4)
5774 return 1;
5775 else {
5776 /* The Document Structuring Conventions say that the numbers
5777 should be integers. Unfortunately some broken applications
5778 get this wrong. */
5779 double x1, x2, x3, x4;
5780 if (sscanf(p, "%lf %lf %lf %lf", &x1, &x2, &x3, &x4) == 4) {
5781 bb->llx = (int)x1;
5782 bb->lly = (int)x2;
5783 bb->urx = (int)x3;
5784 bb->ury = (int)x4;
5785 return 1;
5787 else {
5788 for (; *p == ' ' || *p == '\t'; p++)
5790 if (strncmp(p, "(atend)", 7) == 0) {
5791 return 2;
5795 bb->llx = bb->lly = bb->urx = bb->ury = 0;
5796 return 0;
5799 // This version is taken from psrm.cpp
5801 #define PS_LINE_MAX 255
5802 cset white_space("\n\r \t");
5804 int ps_get_line(char *buf, FILE *fp, const char* filename)
5806 int c = getc(fp);
5807 if (c == EOF) {
5808 buf[0] = '\0';
5809 return 0;
5811 int i = 0;
5812 int err = 0;
5813 while (c != '\r' && c != '\n' && c != EOF) {
5814 if ((c < 0x1b && !white_space(c)) || c == 0x7f)
5815 error("invalid input character code %1 in `%2'", int(c), filename);
5816 else if (i < PS_LINE_MAX)
5817 buf[i++] = c;
5818 else if (!err) {
5819 err = 1;
5820 error("PostScript file `%1' is non-conforming "
5821 "because length of line exceeds 255", filename);
5823 c = getc(fp);
5825 buf[i++] = '\n';
5826 buf[i] = '\0';
5827 if (c == '\r') {
5828 c = getc(fp);
5829 if (c != EOF && c != '\n')
5830 ungetc(c, fp);
5832 return 1;
5835 inline void assign_registers(int llx, int lly, int urx, int ury)
5837 llx_reg_contents = llx;
5838 lly_reg_contents = lly;
5839 urx_reg_contents = urx;
5840 ury_reg_contents = ury;
5843 void do_ps_file(FILE *fp, const char* filename)
5845 bounding_box bb;
5846 int bb_at_end = 0;
5847 char buf[PS_LINE_MAX];
5848 llx_reg_contents = lly_reg_contents =
5849 urx_reg_contents = ury_reg_contents = 0;
5850 if (!ps_get_line(buf, fp, filename)) {
5851 error("`%1' is empty", filename);
5852 return;
5854 if (strncmp("%!PS-Adobe-", buf, 11) != 0) {
5855 error("`%1' is not conforming to the Document Structuring Conventions",
5856 filename);
5857 return;
5859 while (ps_get_line(buf, fp, filename) != 0) {
5860 if (buf[0] != '%' || buf[1] != '%'
5861 || strncmp(buf + 2, "EndComments", 11) == 0)
5862 break;
5863 if (strncmp(buf + 2, "BoundingBox:", 12) == 0) {
5864 int res = parse_bounding_box(buf + 14, &bb);
5865 if (res == 1) {
5866 assign_registers(bb.llx, bb.lly, bb.urx, bb.ury);
5867 return;
5869 else if (res == 2) {
5870 bb_at_end = 1;
5871 break;
5873 else {
5874 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
5875 filename);
5876 return;
5880 if (bb_at_end) {
5881 long offset;
5882 int last_try = 0;
5883 /* in the trailer, the last BoundingBox comment is significant */
5884 for (offset = 512; !last_try; offset *= 2) {
5885 int had_trailer = 0;
5886 int got_bb = 0;
5887 if (offset > 32768 || fseek(fp, -offset, 2) == -1) {
5888 last_try = 1;
5889 if (fseek(fp, 0L, 0) == -1)
5890 break;
5892 while (ps_get_line(buf, fp, filename) != 0) {
5893 if (buf[0] == '%' && buf[1] == '%') {
5894 if (!had_trailer) {
5895 if (strncmp(buf + 2, "Trailer", 7) == 0)
5896 had_trailer = 1;
5898 else {
5899 if (strncmp(buf + 2, "BoundingBox:", 12) == 0) {
5900 int res = parse_bounding_box(buf + 14, &bb);
5901 if (res == 1)
5902 got_bb = 1;
5903 else if (res == 2) {
5904 error("`(atend)' not allowed in trailer of `%1'", filename);
5905 return;
5907 else {
5908 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
5909 filename);
5910 return;
5916 if (got_bb) {
5917 assign_registers(bb.llx, bb.lly, bb.urx, bb.ury);
5918 return;
5922 error("%%%%BoundingBox comment not found in `%1'", filename);
5925 void ps_bbox_request()
5927 symbol nm = get_long_name(1);
5928 if (nm.is_null())
5929 skip_line();
5930 else {
5931 while (!tok.newline() && !tok.eof())
5932 tok.next();
5933 errno = 0;
5934 // PS files might contain non-printable characters, such as ^Z
5935 // and CRs not followed by an LF, so open them in binary mode.
5936 FILE *fp = include_search_path.open_file_cautious(nm.contents(),
5937 0, FOPEN_RB);
5938 if (fp) {
5939 do_ps_file(fp, nm.contents());
5940 fclose(fp);
5942 else
5943 error("can't open `%1': %2", nm.contents(), strerror(errno));
5944 tok.next();
5948 const char *asciify(int c)
5950 static char buf[3];
5951 buf[0] = escape_char == '\0' ? '\\' : escape_char;
5952 buf[1] = buf[2] = '\0';
5953 switch (c) {
5954 case ESCAPE_QUESTION:
5955 buf[1] = '?';
5956 break;
5957 case ESCAPE_AMPERSAND:
5958 buf[1] = '&';
5959 break;
5960 case ESCAPE_RIGHT_PARENTHESIS:
5961 buf[1] = ')';
5962 break;
5963 case ESCAPE_UNDERSCORE:
5964 buf[1] = '_';
5965 break;
5966 case ESCAPE_BAR:
5967 buf[1] = '|';
5968 break;
5969 case ESCAPE_CIRCUMFLEX:
5970 buf[1] = '^';
5971 break;
5972 case ESCAPE_LEFT_BRACE:
5973 buf[1] = '{';
5974 break;
5975 case ESCAPE_RIGHT_BRACE:
5976 buf[1] = '}';
5977 break;
5978 case ESCAPE_LEFT_QUOTE:
5979 buf[1] = '`';
5980 break;
5981 case ESCAPE_RIGHT_QUOTE:
5982 buf[1] = '\'';
5983 break;
5984 case ESCAPE_HYPHEN:
5985 buf[1] = '-';
5986 break;
5987 case ESCAPE_BANG:
5988 buf[1] = '!';
5989 break;
5990 case ESCAPE_c:
5991 buf[1] = 'c';
5992 break;
5993 case ESCAPE_e:
5994 buf[1] = 'e';
5995 break;
5996 case ESCAPE_E:
5997 buf[1] = 'E';
5998 break;
5999 case ESCAPE_PERCENT:
6000 buf[1] = '%';
6001 break;
6002 case ESCAPE_SPACE:
6003 buf[1] = ' ';
6004 break;
6005 case ESCAPE_TILDE:
6006 buf[1] = '~';
6007 break;
6008 case ESCAPE_COLON:
6009 buf[1] = ':';
6010 break;
6011 case PUSH_GROFF_MODE:
6012 case PUSH_COMP_MODE:
6013 case POP_GROFFCOMP_MODE:
6014 buf[0] = '\0';
6015 break;
6016 default:
6017 if (invalid_input_char(c))
6018 buf[0] = '\0';
6019 else
6020 buf[0] = c;
6021 break;
6023 return buf;
6026 const char *input_char_description(int c)
6028 switch (c) {
6029 case '\n':
6030 return "a newline character";
6031 case '\b':
6032 return "a backspace character";
6033 case '\001':
6034 return "a leader character";
6035 case '\t':
6036 return "a tab character";
6037 case ' ':
6038 return "a space character";
6039 case '\0':
6040 return "a node";
6042 static char buf[sizeof("magic character code ") + 1 + INT_DIGITS];
6043 if (invalid_input_char(c)) {
6044 const char *s = asciify(c);
6045 if (*s) {
6046 buf[0] = '`';
6047 strcpy(buf + 1, s);
6048 strcat(buf, "'");
6049 return buf;
6051 sprintf(buf, "magic character code %d", c);
6052 return buf;
6054 if (csprint(c)) {
6055 buf[0] = '`';
6056 buf[1] = c;
6057 buf[2] = '\'';
6058 return buf;
6060 sprintf(buf, "character code %d", c);
6061 return buf;
6064 void tag()
6066 if (!tok.newline() && !tok.eof()) {
6067 string s;
6068 int c;
6069 for (;;) {
6070 c = get_copy(0);
6071 if (c == '"') {
6072 c = get_copy(0);
6073 break;
6075 if (c != ' ' && c != '\t')
6076 break;
6078 s = "x X ";
6079 for (; c != '\n' && c != EOF; c = get_copy(0))
6080 s += (char)c;
6081 s += '\n';
6082 curenv->add_node(new tag_node(s, 0));
6084 tok.next();
6087 void taga()
6089 if (!tok.newline() && !tok.eof()) {
6090 string s;
6091 int c;
6092 for (;;) {
6093 c = get_copy(0);
6094 if (c == '"') {
6095 c = get_copy(0);
6096 break;
6098 if (c != ' ' && c != '\t')
6099 break;
6101 s = "x X ";
6102 for (; c != '\n' && c != EOF; c = get_copy(0))
6103 s += (char)c;
6104 s += '\n';
6105 curenv->add_node(new tag_node(s, 1));
6107 tok.next();
6110 // .tm, .tm1, and .tmc
6112 void do_terminal(int newline, int string_like)
6114 if (!tok.newline() && !tok.eof()) {
6115 int c;
6116 for (;;) {
6117 c = get_copy(0);
6118 if (string_like && c == '"') {
6119 c = get_copy(0);
6120 break;
6122 if (c != ' ' && c != '\t')
6123 break;
6125 for (; c != '\n' && c != EOF; c = get_copy(0))
6126 fputs(asciify(c), stderr);
6128 if (newline)
6129 fputc('\n', stderr);
6130 fflush(stderr);
6131 tok.next();
6134 void terminal()
6136 do_terminal(1, 0);
6139 void terminal1()
6141 do_terminal(1, 1);
6144 void terminal_continue()
6146 do_terminal(0, 1);
6149 dictionary stream_dictionary(20);
6151 void do_open(int append)
6153 symbol stream = get_name(1);
6154 if (!stream.is_null()) {
6155 symbol filename = get_long_name(1);
6156 if (!filename.is_null()) {
6157 errno = 0;
6158 FILE *fp = fopen(filename.contents(), append ? "a" : "w");
6159 if (!fp) {
6160 error("can't open `%1' for %2: %3",
6161 filename.contents(),
6162 append ? "appending" : "writing",
6163 strerror(errno));
6164 fp = (FILE *)stream_dictionary.remove(stream);
6166 else
6167 fp = (FILE *)stream_dictionary.lookup(stream, fp);
6168 if (fp)
6169 fclose(fp);
6172 skip_line();
6175 void open_request()
6177 if (safer_flag) {
6178 error(".open request not allowed in safer mode");
6179 skip_line();
6181 else
6182 do_open(0);
6185 void opena_request()
6187 if (safer_flag) {
6188 error(".opena request not allowed in safer mode");
6189 skip_line();
6191 else
6192 do_open(1);
6195 void close_request()
6197 symbol stream = get_name(1);
6198 if (!stream.is_null()) {
6199 FILE *fp = (FILE *)stream_dictionary.remove(stream);
6200 if (!fp)
6201 error("no stream named `%1'", stream.contents());
6202 else
6203 fclose(fp);
6205 skip_line();
6208 // .write and .writec
6210 void do_write_request(int newline)
6212 symbol stream = get_name(1);
6213 if (stream.is_null()) {
6214 skip_line();
6215 return;
6217 FILE *fp = (FILE *)stream_dictionary.lookup(stream);
6218 if (!fp) {
6219 error("no stream named `%1'", stream.contents());
6220 skip_line();
6221 return;
6223 int c;
6224 while ((c = get_copy(0)) == ' ')
6226 if (c == '"')
6227 c = get_copy(0);
6228 for (; c != '\n' && c != EOF; c = get_copy(0))
6229 fputs(asciify(c), fp);
6230 if (newline)
6231 fputc('\n', fp);
6232 fflush(fp);
6233 tok.next();
6236 void write_request()
6238 do_write_request(1);
6241 void write_request_continue()
6243 do_write_request(0);
6246 void write_macro_request()
6248 symbol stream = get_name(1);
6249 if (stream.is_null()) {
6250 skip_line();
6251 return;
6253 FILE *fp = (FILE *)stream_dictionary.lookup(stream);
6254 if (!fp) {
6255 error("no stream named `%1'", stream.contents());
6256 skip_line();
6257 return;
6259 symbol s = get_name(1);
6260 if (s.is_null()) {
6261 skip_line();
6262 return;
6264 request_or_macro *p = lookup_request(s);
6265 macro *m = p->to_macro();
6266 if (!m)
6267 error("cannot write request");
6268 else {
6269 string_iterator iter(*m);
6270 for (;;) {
6271 int c = iter.get(0);
6272 if (c == EOF)
6273 break;
6274 fputs(asciify(c), fp);
6276 fflush(fp);
6278 skip_line();
6281 void warnscale_request()
6283 if (has_arg()) {
6284 char c = tok.ch();
6285 if (c == 'u')
6286 warn_scale = 1.0;
6287 else if (c == 'i')
6288 warn_scale = (double)units_per_inch;
6289 else if (c == 'c')
6290 warn_scale = (double)units_per_inch / 2.54;
6291 else if (c == 'p')
6292 warn_scale = (double)units_per_inch / 72.0;
6293 else if (c == 'P')
6294 warn_scale = (double)units_per_inch / 6.0;
6295 else {
6296 warning(WARN_SCALE,
6297 "invalid scaling indicator `%1', using `i' instead", c);
6298 c = 'i';
6300 warn_scaling_indicator = c;
6302 skip_line();
6305 void spreadwarn_request()
6307 hunits n;
6308 if (has_arg() && get_hunits(&n, 'm')) {
6309 if (n < 0)
6310 n = 0;
6311 hunits em = curenv->get_size();
6312 spread_limit = (double)n.to_units()
6313 / (em.is_zero() ? hresolution : em.to_units());
6315 else
6316 spread_limit = -spread_limit - 1; // no arg toggles on/off without
6317 // changing value; we mirror at
6318 // -0.5 to make zero a valid value
6319 skip_line();
6322 static void init_charset_table()
6324 char buf[16];
6325 strcpy(buf, "char");
6326 for (int i = 0; i < 256; i++) {
6327 strcpy(buf + 4, i_to_a(i));
6328 charset_table[i] = get_charinfo(symbol(buf));
6329 charset_table[i]->set_ascii_code(i);
6330 if (csalpha(i))
6331 charset_table[i]->set_hyphenation_code(cmlower(i));
6333 charset_table['.']->set_flags(charinfo::ENDS_SENTENCE);
6334 charset_table['?']->set_flags(charinfo::ENDS_SENTENCE);
6335 charset_table['!']->set_flags(charinfo::ENDS_SENTENCE);
6336 charset_table['-']->set_flags(charinfo::BREAK_AFTER);
6337 charset_table['"']->set_flags(charinfo::TRANSPARENT);
6338 charset_table['\'']->set_flags(charinfo::TRANSPARENT);
6339 charset_table[')']->set_flags(charinfo::TRANSPARENT);
6340 charset_table[']']->set_flags(charinfo::TRANSPARENT);
6341 charset_table['*']->set_flags(charinfo::TRANSPARENT);
6342 get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT);
6343 get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT);
6344 get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER);
6345 get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6346 get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6347 get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6348 get_charinfo(symbol("sqrtex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6349 get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6350 get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY);
6351 page_character = charset_table['%'];
6354 static void init_hpf_code_table()
6356 for (int i = 0; i < 256; i++)
6357 hpf_code_table[i] = i;
6360 static void do_translate(int translate_transparent, int translate_input)
6362 tok.skip();
6363 while (!tok.newline() && !tok.eof()) {
6364 if (tok.space()) {
6365 // This is a really bizarre troff feature.
6366 tok.next();
6367 translate_space_to_dummy = tok.dummy();
6368 if (tok.newline() || tok.eof())
6369 break;
6370 tok.next();
6371 continue;
6373 charinfo *ci1 = tok.get_char(1);
6374 if (ci1 == 0)
6375 break;
6376 tok.next();
6377 if (tok.newline() || tok.eof()) {
6378 ci1->set_special_translation(charinfo::TRANSLATE_SPACE,
6379 translate_transparent);
6380 break;
6382 if (tok.space())
6383 ci1->set_special_translation(charinfo::TRANSLATE_SPACE,
6384 translate_transparent);
6385 else if (tok.stretchable_space())
6386 ci1->set_special_translation(charinfo::TRANSLATE_STRETCHABLE_SPACE,
6387 translate_transparent);
6388 else if (tok.dummy())
6389 ci1->set_special_translation(charinfo::TRANSLATE_DUMMY,
6390 translate_transparent);
6391 else if (tok.hyphen_indicator())
6392 ci1->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR,
6393 translate_transparent);
6394 else {
6395 charinfo *ci2 = tok.get_char(1);
6396 if (ci2 == 0)
6397 break;
6398 if (ci1 == ci2)
6399 ci1->set_translation(0, translate_transparent, translate_input);
6400 else
6401 ci1->set_translation(ci2, translate_transparent, translate_input);
6403 tok.next();
6405 skip_line();
6408 void translate()
6410 do_translate(1, 0);
6413 void translate_no_transparent()
6415 do_translate(0, 0);
6418 void translate_input()
6420 do_translate(1, 1);
6423 void char_flags()
6425 int flags;
6426 if (get_integer(&flags))
6427 while (has_arg()) {
6428 charinfo *ci = tok.get_char(1);
6429 if (ci) {
6430 charinfo *tem = ci->get_translation();
6431 if (tem)
6432 ci = tem;
6433 ci->set_flags(flags);
6435 tok.next();
6437 skip_line();
6440 void hyphenation_code()
6442 tok.skip();
6443 while (!tok.newline() && !tok.eof()) {
6444 charinfo *ci = tok.get_char(1);
6445 if (ci == 0)
6446 break;
6447 tok.next();
6448 tok.skip();
6449 unsigned char c = tok.ch();
6450 if (c == 0) {
6451 error("hyphenation code must be ordinary character");
6452 break;
6454 if (csdigit(c)) {
6455 error("hyphenation code cannot be digit");
6456 break;
6458 ci->set_hyphenation_code(c);
6459 if (ci->get_translation()
6460 && ci->get_translation()->get_translation_input())
6461 ci->get_translation()->set_hyphenation_code(c);
6462 tok.next();
6463 tok.skip();
6465 skip_line();
6468 void hyphenation_patterns_file_code()
6470 tok.skip();
6471 while (!tok.newline() && !tok.eof()) {
6472 int n1, n2;
6473 if (get_integer(&n1) && (0 <= n1 && n1 <= 255)) {
6474 if (!has_arg()) {
6475 error("missing output hyphenation code");
6476 break;
6478 if (get_integer(&n2) && (0 <= n2 && n2 <= 255)) {
6479 hpf_code_table[n1] = n2;
6480 tok.skip();
6482 else {
6483 error("output hyphenation code must be integer in the range 0..255");
6484 break;
6487 else {
6488 error("input hyphenation code must be integer in the range 0..255");
6489 break;
6492 skip_line();
6495 charinfo *token::get_char(int required)
6497 if (type == TOKEN_CHAR)
6498 return charset_table[c];
6499 if (type == TOKEN_SPECIAL)
6500 return get_charinfo(nm);
6501 if (type == TOKEN_NUMBERED_CHAR)
6502 return get_charinfo_by_number(val);
6503 if (type == TOKEN_ESCAPE) {
6504 if (escape_char != 0)
6505 return charset_table[escape_char];
6506 else {
6507 error("`\\e' used while no current escape character");
6508 return 0;
6511 if (required) {
6512 if (type == TOKEN_EOF || type == TOKEN_NEWLINE)
6513 warning(WARN_MISSING, "missing normal or special character");
6514 else
6515 error("normal or special character expected (got %1)", description());
6517 return 0;
6520 charinfo *get_optional_char()
6522 while (tok.space())
6523 tok.next();
6524 charinfo *ci = tok.get_char();
6525 if (!ci)
6526 check_missing_character();
6527 else
6528 tok.next();
6529 return ci;
6532 void check_missing_character()
6534 if (!tok.newline() && !tok.eof() && !tok.right_brace() && !tok.tab())
6535 error("normal or special character expected (got %1): "
6536 "treated as missing",
6537 tok.description());
6540 // this is for \Z
6542 int token::add_to_node_list(node **pp)
6544 hunits w;
6545 int s;
6546 node *n = 0;
6547 switch (type) {
6548 case TOKEN_CHAR:
6549 *pp = (*pp)->add_char(charset_table[c], curenv, &w, &s);
6550 break;
6551 case TOKEN_DUMMY:
6552 n = new dummy_node;
6553 break;
6554 case TOKEN_ESCAPE:
6555 if (escape_char != 0)
6556 *pp = (*pp)->add_char(charset_table[escape_char], curenv, &w, &s);
6557 break;
6558 case TOKEN_HYPHEN_INDICATOR:
6559 *pp = (*pp)->add_discretionary_hyphen();
6560 break;
6561 case TOKEN_ITALIC_CORRECTION:
6562 *pp = (*pp)->add_italic_correction(&w);
6563 break;
6564 case TOKEN_LEFT_BRACE:
6565 break;
6566 case TOKEN_MARK_INPUT:
6567 set_number_reg(nm, curenv->get_input_line_position().to_units());
6568 break;
6569 case TOKEN_NODE:
6570 n = nd;
6571 nd = 0;
6572 break;
6573 case TOKEN_NUMBERED_CHAR:
6574 *pp = (*pp)->add_char(get_charinfo_by_number(val), curenv, &w, &s);
6575 break;
6576 case TOKEN_RIGHT_BRACE:
6577 break;
6578 case TOKEN_SPACE:
6579 n = new hmotion_node(curenv->get_space_width(),
6580 curenv->get_fill_color());
6581 break;
6582 case TOKEN_SPECIAL:
6583 *pp = (*pp)->add_char(get_charinfo(nm), curenv, &w, &s);
6584 break;
6585 case TOKEN_STRETCHABLE_SPACE:
6586 n = new unbreakable_space_node(curenv->get_space_width(),
6587 curenv->get_fill_color());
6588 break;
6589 case TOKEN_UNSTRETCHABLE_SPACE:
6590 n = new space_char_hmotion_node(curenv->get_space_width(),
6591 curenv->get_fill_color());
6592 break;
6593 case TOKEN_TRANSPARENT_DUMMY:
6594 n = new transparent_dummy_node;
6595 break;
6596 case TOKEN_ZERO_WIDTH_BREAK:
6597 n = new space_node(H0, curenv->get_fill_color());
6598 n->freeze_space();
6599 n->is_escape_colon();
6600 break;
6601 default:
6602 return 0;
6604 if (n) {
6605 n->next = *pp;
6606 *pp = n;
6608 return 1;
6611 void token::process()
6613 if (possibly_handle_first_page_transition())
6614 return;
6615 switch (type) {
6616 case TOKEN_BACKSPACE:
6617 curenv->add_node(new hmotion_node(-curenv->get_space_width(),
6618 curenv->get_fill_color()));
6619 break;
6620 case TOKEN_CHAR:
6621 curenv->add_char(charset_table[c]);
6622 break;
6623 case TOKEN_DUMMY:
6624 curenv->add_node(new dummy_node);
6625 break;
6626 case TOKEN_EMPTY:
6627 assert(0);
6628 break;
6629 case TOKEN_EOF:
6630 assert(0);
6631 break;
6632 case TOKEN_ESCAPE:
6633 if (escape_char != 0)
6634 curenv->add_char(charset_table[escape_char]);
6635 break;
6636 case TOKEN_BEGIN_TRAP:
6637 case TOKEN_END_TRAP:
6638 case TOKEN_PAGE_EJECTOR:
6639 // these are all handled in process_input_stack()
6640 break;
6641 case TOKEN_HYPHEN_INDICATOR:
6642 curenv->add_hyphen_indicator();
6643 break;
6644 case TOKEN_INTERRUPT:
6645 curenv->interrupt();
6646 break;
6647 case TOKEN_ITALIC_CORRECTION:
6648 curenv->add_italic_correction();
6649 break;
6650 case TOKEN_LEADER:
6651 curenv->handle_tab(1);
6652 break;
6653 case TOKEN_LEFT_BRACE:
6654 break;
6655 case TOKEN_MARK_INPUT:
6656 set_number_reg(nm, curenv->get_input_line_position().to_units());
6657 break;
6658 case TOKEN_NEWLINE:
6659 curenv->newline();
6660 break;
6661 case TOKEN_NODE:
6662 curenv->add_node(nd);
6663 nd = 0;
6664 break;
6665 case TOKEN_NUMBERED_CHAR:
6666 curenv->add_char(get_charinfo_by_number(val));
6667 break;
6668 case TOKEN_REQUEST:
6669 // handled in process_input_stack()
6670 break;
6671 case TOKEN_RIGHT_BRACE:
6672 break;
6673 case TOKEN_SPACE:
6674 curenv->space();
6675 break;
6676 case TOKEN_SPECIAL:
6677 curenv->add_char(get_charinfo(nm));
6678 break;
6679 case TOKEN_SPREAD:
6680 curenv->spread();
6681 break;
6682 case TOKEN_STRETCHABLE_SPACE:
6683 curenv->add_node(new unbreakable_space_node(curenv->get_space_width(),
6684 curenv->get_fill_color()));
6685 break;
6686 case TOKEN_UNSTRETCHABLE_SPACE:
6687 curenv->add_node(new space_char_hmotion_node(curenv->get_space_width(),
6688 curenv->get_fill_color()));
6689 break;
6690 case TOKEN_TAB:
6691 curenv->handle_tab(0);
6692 break;
6693 case TOKEN_TRANSPARENT:
6694 break;
6695 case TOKEN_TRANSPARENT_DUMMY:
6696 curenv->add_node(new transparent_dummy_node);
6697 break;
6698 case TOKEN_ZERO_WIDTH_BREAK:
6700 node *tmp = new space_node(H0, curenv->get_fill_color());
6701 tmp->freeze_space();
6702 tmp->is_escape_colon();
6703 curenv->add_node(tmp);
6704 break;
6706 default:
6707 assert(0);
6711 class nargs_reg : public reg {
6712 public:
6713 const char *get_string();
6716 const char *nargs_reg::get_string()
6718 return i_to_a(input_stack::nargs());
6721 class lineno_reg : public reg {
6722 public:
6723 const char *get_string();
6726 const char *lineno_reg::get_string()
6728 int line;
6729 const char *file;
6730 if (!input_stack::get_location(0, &file, &line))
6731 line = 0;
6732 return i_to_a(line);
6735 class writable_lineno_reg : public general_reg {
6736 public:
6737 writable_lineno_reg();
6738 void set_value(units);
6739 int get_value(units *);
6742 writable_lineno_reg::writable_lineno_reg()
6746 int writable_lineno_reg::get_value(units *res)
6748 int line;
6749 const char *file;
6750 if (!input_stack::get_location(0, &file, &line))
6751 return 0;
6752 *res = line;
6753 return 1;
6756 void writable_lineno_reg::set_value(units n)
6758 input_stack::set_location(0, n);
6761 class filename_reg : public reg {
6762 public:
6763 const char *get_string();
6766 const char *filename_reg::get_string()
6768 int line;
6769 const char *file;
6770 if (input_stack::get_location(0, &file, &line))
6771 return file;
6772 else
6773 return 0;
6776 class constant_reg : public reg {
6777 const char *s;
6778 public:
6779 constant_reg(const char *);
6780 const char *get_string();
6783 constant_reg::constant_reg(const char *p) : s(p)
6787 const char *constant_reg::get_string()
6789 return s;
6792 constant_int_reg::constant_int_reg(int *q) : p(q)
6796 const char *constant_int_reg::get_string()
6798 return i_to_a(*p);
6801 void abort_request()
6803 int c;
6804 if (tok.eof())
6805 c = EOF;
6806 else if (tok.newline())
6807 c = '\n';
6808 else {
6809 while ((c = get_copy(0)) == ' ')
6812 if (c == EOF || c == '\n')
6813 fputs("User Abort.", stderr);
6814 else {
6815 for (; c != '\n' && c != EOF; c = get_copy(0))
6816 fputs(asciify(c), stderr);
6818 fputc('\n', stderr);
6819 cleanup_and_exit(1);
6822 char *read_string()
6824 int len = 256;
6825 char *s = new char[len];
6826 int c;
6827 while ((c = get_copy(0)) == ' ')
6829 int i = 0;
6830 while (c != '\n' && c != EOF) {
6831 if (!invalid_input_char(c)) {
6832 if (i + 2 > len) {
6833 char *tem = s;
6834 s = new char[len*2];
6835 memcpy(s, tem, len);
6836 len *= 2;
6837 a_delete tem;
6839 s[i++] = c;
6841 c = get_copy(0);
6843 s[i] = '\0';
6844 tok.next();
6845 if (i == 0) {
6846 a_delete s;
6847 return 0;
6849 return s;
6852 void pipe_output()
6854 if (safer_flag) {
6855 error(".pi request not allowed in safer mode");
6856 skip_line();
6858 else {
6859 #ifdef POPEN_MISSING
6860 error("pipes not available on this system");
6861 skip_line();
6862 #else /* not POPEN_MISSING */
6863 if (the_output) {
6864 error("can't pipe: output already started");
6865 skip_line();
6867 else {
6868 char *pc;
6869 if ((pc = read_string()) == 0)
6870 error("can't pipe to empty command");
6871 if (pipe_command) {
6872 char *s = new char[strlen(pipe_command) + strlen(pc) + 1 + 1];
6873 strcpy(s, pipe_command);
6874 strcat(s, "|");
6875 strcat(s, pc);
6876 a_delete pipe_command;
6877 a_delete pc;
6878 pipe_command = s;
6880 else
6881 pipe_command = pc;
6883 #endif /* not POPEN_MISSING */
6887 static int system_status;
6889 void system_request()
6891 if (safer_flag) {
6892 error(".sy request not allowed in safer mode");
6893 skip_line();
6895 else {
6896 char *command = read_string();
6897 if (!command)
6898 error("empty command");
6899 else {
6900 system_status = system(command);
6901 a_delete command;
6906 void copy_file()
6908 if (curdiv == topdiv && topdiv->before_first_page) {
6909 handle_initial_request(COPY_FILE_REQUEST);
6910 return;
6912 symbol filename = get_long_name(1);
6913 while (!tok.newline() && !tok.eof())
6914 tok.next();
6915 if (break_flag)
6916 curenv->do_break();
6917 if (!filename.is_null())
6918 curdiv->copy_file(filename.contents());
6919 tok.next();
6922 #ifdef COLUMN
6924 void vjustify()
6926 if (curdiv == topdiv && topdiv->before_first_page) {
6927 handle_initial_request(VJUSTIFY_REQUEST);
6928 return;
6930 symbol type = get_long_name(1);
6931 if (!type.is_null())
6932 curdiv->vjustify(type);
6933 skip_line();
6936 #endif /* COLUMN */
6938 void transparent_file()
6940 if (curdiv == topdiv && topdiv->before_first_page) {
6941 handle_initial_request(TRANSPARENT_FILE_REQUEST);
6942 return;
6944 symbol filename = get_long_name(1);
6945 while (!tok.newline() && !tok.eof())
6946 tok.next();
6947 if (break_flag)
6948 curenv->do_break();
6949 if (!filename.is_null()) {
6950 errno = 0;
6951 FILE *fp = include_search_path.open_file_cautious(filename.contents());
6952 if (!fp)
6953 error("can't open `%1': %2", filename.contents(), strerror(errno));
6954 else {
6955 int bol = 1;
6956 for (;;) {
6957 int c = getc(fp);
6958 if (c == EOF)
6959 break;
6960 if (invalid_input_char(c))
6961 warning(WARN_INPUT, "invalid input character code %1", int(c));
6962 else {
6963 curdiv->transparent_output(c);
6964 bol = c == '\n';
6967 if (!bol)
6968 curdiv->transparent_output('\n');
6969 fclose(fp);
6972 tok.next();
6975 class page_range {
6976 int first;
6977 int last;
6978 public:
6979 page_range *next;
6980 page_range(int, int, page_range *);
6981 int contains(int n);
6984 page_range::page_range(int i, int j, page_range *p)
6985 : first(i), last(j), next(p)
6989 int page_range::contains(int n)
6991 return n >= first && (last <= 0 || n <= last);
6994 page_range *output_page_list = 0;
6996 int in_output_page_list(int n)
6998 if (!output_page_list)
6999 return 1;
7000 for (page_range *p = output_page_list; p; p = p->next)
7001 if (p->contains(n))
7002 return 1;
7003 return 0;
7006 static void parse_output_page_list(char *p)
7008 for (;;) {
7009 int i;
7010 if (*p == '-')
7011 i = 1;
7012 else if (csdigit(*p)) {
7013 i = 0;
7015 i = i*10 + *p++ - '0';
7016 while (csdigit(*p));
7018 else
7019 break;
7020 int j;
7021 if (*p == '-') {
7022 p++;
7023 j = 0;
7024 if (csdigit(*p)) {
7026 j = j*10 + *p++ - '0';
7027 while (csdigit(*p));
7030 else
7031 j = i;
7032 if (j == 0)
7033 last_page_number = -1;
7034 else if (last_page_number >= 0 && j > last_page_number)
7035 last_page_number = j;
7036 output_page_list = new page_range(i, j, output_page_list);
7037 if (*p != ',')
7038 break;
7039 ++p;
7041 if (*p != '\0') {
7042 error("bad output page list");
7043 output_page_list = 0;
7047 static FILE *open_mac_file(const char *mac, char **path)
7049 // Try first FOOBAR.tmac, then tmac.FOOBAR
7050 char *s1 = new char[strlen(mac)+strlen(MACRO_POSTFIX)+1];
7051 strcpy(s1, mac);
7052 strcat(s1, MACRO_POSTFIX);
7053 FILE *fp = mac_path->open_file(s1, path);
7054 a_delete s1;
7055 if (!fp) {
7056 char *s2 = new char[strlen(mac)+strlen(MACRO_PREFIX)+1];
7057 strcpy(s2, MACRO_PREFIX);
7058 strcat(s2, mac);
7059 fp = mac_path->open_file(s2, path);
7060 a_delete s2;
7062 return fp;
7065 static void process_macro_file(const char *mac)
7067 char *path;
7068 FILE *fp = open_mac_file(mac, &path);
7069 if (!fp)
7070 fatal("can't find macro file %1", mac);
7071 const char *s = symbol(path).contents();
7072 a_delete path;
7073 input_stack::push(new file_iterator(fp, s));
7074 tok.next();
7075 process_input_stack();
7078 static void process_startup_file(const char *filename)
7080 char *path;
7081 search_path *orig_mac_path = mac_path;
7082 mac_path = &config_macro_path;
7083 FILE *fp = mac_path->open_file(filename, &path);
7084 if (fp) {
7085 input_stack::push(new file_iterator(fp, symbol(path).contents()));
7086 a_delete path;
7087 tok.next();
7088 process_input_stack();
7090 mac_path = orig_mac_path;
7093 void macro_source()
7095 symbol nm = get_long_name(1);
7096 if (nm.is_null())
7097 skip_line();
7098 else {
7099 while (!tok.newline() && !tok.eof())
7100 tok.next();
7101 char *path;
7102 FILE *fp = mac_path->open_file(nm.contents(), &path);
7103 // .mso doesn't (and cannot) go through open_mac_file, so we
7104 // need to do it here manually: If we have tmac.FOOBAR, try
7105 // FOOBAR.tmac and vice versa
7106 if (!fp) {
7107 const char *fn = nm.contents();
7108 if (strncasecmp(fn, MACRO_PREFIX, sizeof(MACRO_PREFIX) - 1) == 0) {
7109 char *s = new char[strlen(fn) + sizeof(MACRO_POSTFIX)];
7110 strcpy(s, fn + sizeof(MACRO_PREFIX) - 1);
7111 strcat(s, MACRO_POSTFIX);
7112 fp = mac_path->open_file(s, &path);
7113 a_delete s;
7115 if (!fp) {
7116 if (strncasecmp(fn + strlen(fn) - sizeof(MACRO_POSTFIX) + 1,
7117 MACRO_POSTFIX, sizeof(MACRO_POSTFIX) - 1) == 0) {
7118 char *s = new char[strlen(fn) + sizeof(MACRO_PREFIX)];
7119 strcpy(s, MACRO_PREFIX);
7120 strncat(s, fn, strlen(fn) - sizeof(MACRO_POSTFIX) + 1);
7121 fp = mac_path->open_file(s, &path);
7122 a_delete s;
7126 if (fp) {
7127 input_stack::push(new file_iterator(fp, symbol(path).contents()));
7128 a_delete path;
7130 else
7131 error("can't find macro file `%1'", nm.contents());
7132 tok.next();
7136 static void process_input_file(const char *name)
7138 FILE *fp;
7139 if (strcmp(name, "-") == 0) {
7140 clearerr(stdin);
7141 fp = stdin;
7143 else {
7144 errno = 0;
7145 fp = include_search_path.open_file_cautious(name);
7146 if (!fp)
7147 fatal("can't open `%1': %2", name, strerror(errno));
7149 input_stack::push(new file_iterator(fp, name));
7150 tok.next();
7151 process_input_stack();
7154 // make sure the_input is empty before calling this
7156 static int evaluate_expression(const char *expr, units *res)
7158 input_stack::push(make_temp_iterator(expr));
7159 tok.next();
7160 int success = get_number(res, 'u');
7161 while (input_stack::get(0) != EOF)
7163 return success;
7166 static void do_register_assignment(const char *s)
7168 const char *p = strchr(s, '=');
7169 if (!p) {
7170 char buf[2];
7171 buf[0] = s[0];
7172 buf[1] = 0;
7173 units n;
7174 if (evaluate_expression(s + 1, &n))
7175 set_number_reg(buf, n);
7177 else {
7178 char *buf = new char[p - s + 1];
7179 memcpy(buf, s, p - s);
7180 buf[p - s] = 0;
7181 units n;
7182 if (evaluate_expression(p + 1, &n))
7183 set_number_reg(buf, n);
7184 a_delete buf;
7188 static void set_string(const char *name, const char *value)
7190 macro *m = new macro;
7191 for (const char *p = value; *p; p++)
7192 if (!invalid_input_char((unsigned char)*p))
7193 m->append(*p);
7194 request_dictionary.define(name, m);
7197 static void do_string_assignment(const char *s)
7199 const char *p = strchr(s, '=');
7200 if (!p) {
7201 char buf[2];
7202 buf[0] = s[0];
7203 buf[1] = 0;
7204 set_string(buf, s + 1);
7206 else {
7207 char *buf = new char[p - s + 1];
7208 memcpy(buf, s, p - s);
7209 buf[p - s] = 0;
7210 set_string(buf, p + 1);
7211 a_delete buf;
7215 struct string_list {
7216 const char *s;
7217 string_list *next;
7218 string_list(const char *ss) : s(ss), next(0) {}
7221 #if 0
7222 static void prepend_string(const char *s, string_list **p)
7224 string_list *l = new string_list(s);
7225 l->next = *p;
7226 *p = l;
7228 #endif
7230 static void add_string(const char *s, string_list **p)
7232 while (*p)
7233 p = &((*p)->next);
7234 *p = new string_list(s);
7237 void usage(FILE *stream, const char *prog)
7239 fprintf(stream,
7240 "usage: %s -abcivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n"
7241 " -rcn -Tname -Fdir -Idir -Mdir [files...]\n",
7242 prog);
7245 int main(int argc, char **argv)
7247 program_name = argv[0];
7248 static char stderr_buf[BUFSIZ];
7249 setbuf(stderr, stderr_buf);
7250 int c;
7251 string_list *macros = 0;
7252 string_list *register_assignments = 0;
7253 string_list *string_assignments = 0;
7254 int iflag = 0;
7255 int tflag = 0;
7256 int fflag = 0;
7257 int nflag = 0;
7258 int no_rc = 0; // don't process troffrc and troffrc-end
7259 int next_page_number = 0; // pacify compiler
7260 opterr = 0;
7261 hresolution = vresolution = 1;
7262 // restore $PATH if called from groff
7263 char* groff_path = getenv("GROFF_PATH__");
7264 if (groff_path) {
7265 string e = "PATH";
7266 e += '=';
7267 if (*groff_path)
7268 e += groff_path;
7269 e += '\0';
7270 if (putenv(strsave(e.contents())))
7271 fatal("putenv failed");
7273 static const struct option long_options[] = {
7274 { "help", no_argument, 0, CHAR_MAX + 1 },
7275 { "version", no_argument, 0, 'v' },
7276 { 0, 0, 0, 0 }
7278 #if defined(DEBUGGING)
7279 #define DEBUG_OPTION "D"
7280 #endif
7281 while ((c = getopt_long(argc, argv,
7282 "abciI:vw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU"
7283 DEBUG_OPTION, long_options, 0))
7284 != EOF)
7285 switch(c) {
7286 case 'v':
7288 printf("GNU troff (groff) version %s\n", Version_string);
7289 exit(0);
7290 break;
7292 case 'I':
7293 // Search path for .psbb files
7294 // and most other non-system input files.
7295 include_search_path.command_line_dir(optarg);
7296 break;
7297 case 'T':
7298 device = optarg;
7299 tflag = 1;
7300 is_html = (strcmp(device, "html") == 0);
7301 break;
7302 case 'C':
7303 compatible_flag = 1;
7304 // fall through
7305 case 'c':
7306 color_flag = 0;
7307 break;
7308 case 'M':
7309 macro_path.command_line_dir(optarg);
7310 safer_macro_path.command_line_dir(optarg);
7311 config_macro_path.command_line_dir(optarg);
7312 break;
7313 case 'F':
7314 font::command_line_font_dir(optarg);
7315 break;
7316 case 'm':
7317 add_string(optarg, &macros);
7318 break;
7319 case 'E':
7320 inhibit_errors = 1;
7321 break;
7322 case 'R':
7323 no_rc = 1;
7324 break;
7325 case 'w':
7326 enable_warning(optarg);
7327 break;
7328 case 'W':
7329 disable_warning(optarg);
7330 break;
7331 case 'i':
7332 iflag = 1;
7333 break;
7334 case 'b':
7335 backtrace_flag = 1;
7336 break;
7337 case 'a':
7338 ascii_output_flag = 1;
7339 break;
7340 case 'z':
7341 suppress_output_flag = 1;
7342 break;
7343 case 'n':
7344 if (sscanf(optarg, "%d", &next_page_number) == 1)
7345 nflag++;
7346 else
7347 error("bad page number");
7348 break;
7349 case 'o':
7350 parse_output_page_list(optarg);
7351 break;
7352 case 'd':
7353 if (*optarg == '\0')
7354 error("`-d' requires non-empty argument");
7355 else
7356 add_string(optarg, &string_assignments);
7357 break;
7358 case 'r':
7359 if (*optarg == '\0')
7360 error("`-r' requires non-empty argument");
7361 else
7362 add_string(optarg, &register_assignments);
7363 break;
7364 case 'f':
7365 default_family = symbol(optarg);
7366 fflag = 1;
7367 break;
7368 case 'q':
7369 case 's':
7370 case 't':
7371 // silently ignore these
7372 break;
7373 case 'U':
7374 safer_flag = 0; // unsafe behaviour
7375 break;
7376 #if defined(DEBUGGING)
7377 case 'D':
7378 debug_state = 1;
7379 break;
7380 #endif
7381 case CHAR_MAX + 1: // --help
7382 usage(stdout, argv[0]);
7383 exit(0);
7384 break;
7385 case '?':
7386 usage(stderr, argv[0]);
7387 exit(1);
7388 break; // never reached
7389 default:
7390 assert(0);
7392 if (!safer_flag)
7393 mac_path = &macro_path;
7394 set_string(".T", device);
7395 init_charset_table();
7396 init_hpf_code_table();
7397 if (!font::load_desc())
7398 fatal("sorry, I can't continue");
7399 units_per_inch = font::res;
7400 hresolution = font::hor;
7401 vresolution = font::vert;
7402 sizescale = font::sizescale;
7403 tcommand_flag = font::tcommand;
7404 warn_scale = (double)units_per_inch;
7405 warn_scaling_indicator = 'i';
7406 if (!fflag && font::family != 0 && *font::family != '\0')
7407 default_family = symbol(font::family);
7408 font_size::init_size_table(font::sizes);
7409 int i;
7410 int j = 1;
7411 if (font::style_table) {
7412 for (i = 0; font::style_table[i]; i++)
7413 mount_style(j++, symbol(font::style_table[i]));
7415 for (i = 0; font::font_name_table[i]; i++, j++)
7416 // In the DESC file a font name of 0 (zero) means leave this
7417 // position empty.
7418 if (strcmp(font::font_name_table[i], "0") != 0)
7419 mount_font(j, symbol(font::font_name_table[i]));
7420 curdiv = topdiv = new top_level_diversion;
7421 if (nflag)
7422 topdiv->set_next_page_number(next_page_number);
7423 init_input_requests();
7424 init_env_requests();
7425 init_div_requests();
7426 #ifdef COLUMN
7427 init_column_requests();
7428 #endif /* COLUMN */
7429 init_node_requests();
7430 number_reg_dictionary.define(".T", new constant_reg(tflag ? "1" : "0"));
7431 init_registers();
7432 init_reg_requests();
7433 init_hyphen_requests();
7434 init_environments();
7435 while (string_assignments) {
7436 do_string_assignment(string_assignments->s);
7437 string_list *tem = string_assignments;
7438 string_assignments = string_assignments->next;
7439 delete tem;
7441 while (register_assignments) {
7442 do_register_assignment(register_assignments->s);
7443 string_list *tem = register_assignments;
7444 register_assignments = register_assignments->next;
7445 delete tem;
7447 if (!no_rc)
7448 process_startup_file(INITIAL_STARTUP_FILE);
7449 while (macros) {
7450 process_macro_file(macros->s);
7451 string_list *tem = macros;
7452 macros = macros->next;
7453 delete tem;
7455 if (!no_rc)
7456 process_startup_file(FINAL_STARTUP_FILE);
7457 for (i = optind; i < argc; i++)
7458 process_input_file(argv[i]);
7459 if (optind >= argc || iflag)
7460 process_input_file("-");
7461 exit_troff();
7462 return 0; // not reached
7465 void warn_request()
7467 int n;
7468 if (has_arg() && get_integer(&n)) {
7469 if (n & ~WARN_TOTAL) {
7470 warning(WARN_RANGE, "warning mask must be between 0 and %1", WARN_TOTAL);
7471 n &= WARN_TOTAL;
7473 warning_mask = n;
7475 else
7476 warning_mask = WARN_TOTAL;
7477 skip_line();
7480 static void init_registers()
7482 #ifdef LONG_FOR_TIME_T
7483 long
7484 #else /* not LONG_FOR_TIME_T */
7485 time_t
7486 #endif /* not LONG_FOR_TIME_T */
7487 t = time(0);
7488 // Use struct here to work around misfeature in old versions of g++.
7489 struct tm *tt = localtime(&t);
7490 set_number_reg("seconds", int(tt->tm_sec));
7491 set_number_reg("minutes", int(tt->tm_min));
7492 set_number_reg("hours", int(tt->tm_hour));
7493 set_number_reg("dw", int(tt->tm_wday + 1));
7494 set_number_reg("dy", int(tt->tm_mday));
7495 set_number_reg("mo", int(tt->tm_mon + 1));
7496 set_number_reg("year", int(1900 + tt->tm_year));
7497 set_number_reg("yr", int(tt->tm_year));
7498 set_number_reg("$$", getpid());
7499 number_reg_dictionary.define(".A",
7500 new constant_reg(ascii_output_flag
7501 ? "1"
7502 : "0"));
7506 * registers associated with \O
7509 static int output_reg_minx_contents = -1;
7510 static int output_reg_miny_contents = -1;
7511 static int output_reg_maxx_contents = -1;
7512 static int output_reg_maxy_contents = -1;
7514 void check_output_limits(int x, int y)
7516 if ((output_reg_minx_contents == -1) || (x < output_reg_minx_contents))
7517 output_reg_minx_contents = x;
7518 if (x > output_reg_maxx_contents)
7519 output_reg_maxx_contents = x;
7520 if ((output_reg_miny_contents == -1) || (y < output_reg_miny_contents))
7521 output_reg_miny_contents = y;
7522 if (y > output_reg_maxy_contents)
7523 output_reg_maxy_contents = y;
7526 void reset_output_registers()
7528 output_reg_minx_contents = -1;
7529 output_reg_miny_contents = -1;
7530 output_reg_maxx_contents = -1;
7531 output_reg_maxy_contents = -1;
7534 void get_output_registers(int *minx, int *miny, int *maxx, int *maxy)
7536 *minx = output_reg_minx_contents;
7537 *miny = output_reg_miny_contents;
7538 *maxx = output_reg_maxx_contents;
7539 *maxy = output_reg_maxy_contents;
7542 void init_input_requests()
7544 init_request("ab", abort_request);
7545 init_request("als", alias_macro);
7546 init_request("am", append_macro);
7547 init_request("am1", append_nocomp_macro);
7548 init_request("ami", append_indirect_macro);
7549 init_request("ami1", append_indirect_nocomp_macro);
7550 init_request("as", append_string);
7551 init_request("as1", append_nocomp_string);
7552 init_request("asciify", asciify_macro);
7553 init_request("backtrace", backtrace_request);
7554 init_request("blm", blank_line_macro);
7555 init_request("break", while_break_request);
7556 init_request("cf", copy_file);
7557 init_request("cflags", char_flags);
7558 init_request("char", define_character);
7559 init_request("chop", chop_macro);
7560 init_request("close", close_request);
7561 init_request("color", activate_color);
7562 init_request("composite", composite_request);
7563 init_request("continue", while_continue_request);
7564 init_request("cp", compatible);
7565 init_request("de", define_macro);
7566 init_request("de1", define_nocomp_macro);
7567 init_request("defcolor", define_color);
7568 init_request("dei", define_indirect_macro);
7569 init_request("dei1", define_indirect_nocomp_macro);
7570 init_request("do", do_request);
7571 init_request("ds", define_string);
7572 init_request("ds1", define_nocomp_string);
7573 init_request("ec", set_escape_char);
7574 init_request("ecr", restore_escape_char);
7575 init_request("ecs", save_escape_char);
7576 init_request("el", else_request);
7577 init_request("em", end_macro);
7578 init_request("eo", escape_off);
7579 init_request("ex", exit_request);
7580 init_request("fchar", define_fallback_character);
7581 #ifdef WIDOW_CONTROL
7582 init_request("fpl", flush_pending_lines);
7583 #endif /* WIDOW_CONTROL */
7584 init_request("hcode", hyphenation_code);
7585 init_request("hpfcode", hyphenation_patterns_file_code);
7586 init_request("ie", if_else_request);
7587 init_request("if", if_request);
7588 init_request("ig", ignore);
7589 init_request("length", length_request);
7590 init_request("lf", line_file);
7591 init_request("mso", macro_source);
7592 init_request("nop", nop_request);
7593 init_request("nroff", nroff_request);
7594 init_request("nx", next_file);
7595 init_request("open", open_request);
7596 init_request("opena", opena_request);
7597 init_request("output", output_request);
7598 init_request("pc", set_page_character);
7599 init_request("pi", pipe_output);
7600 init_request("pm", print_macros);
7601 init_request("psbb", ps_bbox_request);
7602 #ifndef POPEN_MISSING
7603 init_request("pso", pipe_source);
7604 #endif /* not POPEN_MISSING */
7605 init_request("rchar", remove_character);
7606 init_request("rd", read_request);
7607 init_request("return", return_macro_request);
7608 init_request("rm", remove_macro);
7609 init_request("rn", rename_macro);
7610 init_request("schar", define_special_character);
7611 init_request("shift", shift);
7612 init_request("so", source);
7613 init_request("spreadwarn", spreadwarn_request);
7614 init_request("substring", substring_request);
7615 init_request("sy", system_request);
7616 init_request("tag", tag);
7617 init_request("taga", taga);
7618 init_request("tm", terminal);
7619 init_request("tm1", terminal1);
7620 init_request("tmc", terminal_continue);
7621 init_request("tr", translate);
7622 init_request("trf", transparent_file);
7623 init_request("trin", translate_input);
7624 init_request("trnt", translate_no_transparent);
7625 init_request("troff", troff_request);
7626 init_request("unformat", unformat_macro);
7627 #ifdef COLUMN
7628 init_request("vj", vjustify);
7629 #endif /* COLUMN */
7630 init_request("warn", warn_request);
7631 init_request("warnscale", warnscale_request);
7632 init_request("while", while_request);
7633 init_request("write", write_request);
7634 init_request("writec", write_request_continue);
7635 init_request("writem", write_macro_request);
7636 number_reg_dictionary.define(".$", new nargs_reg);
7637 number_reg_dictionary.define(".C", new constant_int_reg(&compatible_flag));
7638 number_reg_dictionary.define(".c", new lineno_reg);
7639 number_reg_dictionary.define(".color", new constant_int_reg(&color_flag));
7640 number_reg_dictionary.define(".F", new filename_reg);
7641 number_reg_dictionary.define(".g", new constant_reg("1"));
7642 number_reg_dictionary.define(".H", new constant_int_reg(&hresolution));
7643 number_reg_dictionary.define(".R", new constant_reg("10000"));
7644 number_reg_dictionary.define(".U", new constant_int_reg(&safer_flag));
7645 number_reg_dictionary.define(".V", new constant_int_reg(&vresolution));
7646 number_reg_dictionary.define(".warn", new constant_int_reg(&warning_mask));
7647 extern const char *major_version;
7648 number_reg_dictionary.define(".x", new constant_reg(major_version));
7649 extern const char *revision;
7650 number_reg_dictionary.define(".Y", new constant_reg(revision));
7651 extern const char *minor_version;
7652 number_reg_dictionary.define(".y", new constant_reg(minor_version));
7653 number_reg_dictionary.define("c.", new writable_lineno_reg);
7654 number_reg_dictionary.define("llx", new variable_reg(&llx_reg_contents));
7655 number_reg_dictionary.define("lly", new variable_reg(&lly_reg_contents));
7656 number_reg_dictionary.define("opmaxx",
7657 new variable_reg(&output_reg_maxx_contents));
7658 number_reg_dictionary.define("opmaxy",
7659 new variable_reg(&output_reg_maxy_contents));
7660 number_reg_dictionary.define("opminx",
7661 new variable_reg(&output_reg_minx_contents));
7662 number_reg_dictionary.define("opminy",
7663 new variable_reg(&output_reg_miny_contents));
7664 number_reg_dictionary.define("slimit",
7665 new variable_reg(&input_stack::limit));
7666 number_reg_dictionary.define("systat", new variable_reg(&system_status));
7667 number_reg_dictionary.define("urx", new variable_reg(&urx_reg_contents));
7668 number_reg_dictionary.define("ury", new variable_reg(&ury_reg_contents));
7671 object_dictionary request_dictionary(501);
7673 void init_request(const char *s, REQUEST_FUNCP f)
7675 request_dictionary.define(s, new request(f));
7678 static request_or_macro *lookup_request(symbol nm)
7680 assert(!nm.is_null());
7681 request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm);
7682 if (p == 0) {
7683 warning(WARN_MAC, "macro `%1' not defined", nm.contents());
7684 p = new macro;
7685 request_dictionary.define(nm, p);
7687 return p;
7690 node *charinfo_to_node_list(charinfo *ci, const environment *envp)
7692 // Don't interpret character definitions in compatible mode.
7693 int old_compatible_flag = compatible_flag;
7694 compatible_flag = 0;
7695 int old_escape_char = escape_char;
7696 escape_char = '\\';
7697 macro *mac = ci->set_macro(0);
7698 assert(mac != 0);
7699 environment *oldenv = curenv;
7700 environment env(envp);
7701 curenv = &env;
7702 curenv->set_composite();
7703 token old_tok = tok;
7704 input_stack::add_boundary();
7705 string_iterator *si =
7706 new string_iterator(*mac, "composite character", ci->nm);
7707 input_stack::push(si);
7708 // we don't use process_input_stack, because we don't want to recognise
7709 // requests
7710 for (;;) {
7711 tok.next();
7712 if (tok.eof())
7713 break;
7714 if (tok.newline()) {
7715 error("composite character mustn't contain newline");
7716 while (!tok.eof())
7717 tok.next();
7718 break;
7720 else
7721 tok.process();
7723 node *n = curenv->extract_output_line();
7724 input_stack::remove_boundary();
7725 ci->set_macro(mac);
7726 tok = old_tok;
7727 curenv = oldenv;
7728 compatible_flag = old_compatible_flag;
7729 escape_char = old_escape_char;
7730 have_input = 0;
7731 return n;
7734 static node *read_draw_node()
7736 token start;
7737 start.next();
7738 if (!start.delimiter(1)){
7739 do {
7740 tok.next();
7741 } while (tok != start && !tok.newline() && !tok.eof());
7743 else {
7744 tok.next();
7745 if (tok == start)
7746 error("missing argument");
7747 else {
7748 unsigned char type = tok.ch();
7749 if (type == 'F') {
7750 read_color_draw_node(start);
7751 return 0;
7753 tok.next();
7754 int maxpoints = 10;
7755 hvpair *point = new hvpair[maxpoints];
7756 int npoints = 0;
7757 int no_last_v = 0;
7758 int err = 0;
7759 int i;
7760 for (i = 0; tok != start; i++) {
7761 if (i == maxpoints) {
7762 hvpair *oldpoint = point;
7763 point = new hvpair[maxpoints*2];
7764 for (int j = 0; j < maxpoints; j++)
7765 point[j] = oldpoint[j];
7766 maxpoints *= 2;
7767 a_delete oldpoint;
7769 if (!get_hunits(&point[i].h,
7770 type == 'f' || type == 't' ? 'u' : 'm')) {
7771 err = 1;
7772 break;
7774 ++npoints;
7775 tok.skip();
7776 point[i].v = V0;
7777 if (tok == start) {
7778 no_last_v = 1;
7779 break;
7781 if (!get_vunits(&point[i].v, 'v')) {
7782 err = 1;
7783 break;
7785 tok.skip();
7787 while (tok != start && !tok.newline() && !tok.eof())
7788 tok.next();
7789 if (!err) {
7790 switch (type) {
7791 case 'l':
7792 if (npoints != 1 || no_last_v) {
7793 error("two arguments needed for line");
7794 npoints = 1;
7796 break;
7797 case 'c':
7798 if (npoints != 1 || !no_last_v) {
7799 error("one argument needed for circle");
7800 npoints = 1;
7801 point[0].v = V0;
7803 break;
7804 case 'e':
7805 if (npoints != 1 || no_last_v) {
7806 error("two arguments needed for ellipse");
7807 npoints = 1;
7809 break;
7810 case 'a':
7811 if (npoints != 2 || no_last_v) {
7812 error("four arguments needed for arc");
7813 npoints = 2;
7815 break;
7816 case '~':
7817 if (no_last_v)
7818 error("even number of arguments needed for spline");
7819 break;
7820 case 'f':
7821 if (npoints != 1 || !no_last_v) {
7822 error("one argument needed for gray shade");
7823 npoints = 1;
7824 point[0].v = V0;
7826 default:
7827 // silently pass it through
7828 break;
7830 draw_node *dn = new draw_node(type, point, npoints,
7831 curenv->get_font_size(),
7832 curenv->get_glyph_color(),
7833 curenv->get_fill_color());
7834 a_delete point;
7835 return dn;
7837 else {
7838 a_delete point;
7842 return 0;
7845 static void read_color_draw_node(token &start)
7847 tok.next();
7848 if (tok == start) {
7849 error("missing color scheme");
7850 return;
7852 unsigned char scheme = tok.ch();
7853 tok.next();
7854 color *col = 0;
7855 char end = start.ch();
7856 switch (scheme) {
7857 case 'c':
7858 col = read_cmy(end);
7859 break;
7860 case 'd':
7861 col = &default_color;
7862 break;
7863 case 'g':
7864 col = read_gray(end);
7865 break;
7866 case 'k':
7867 col = read_cmyk(end);
7868 break;
7869 case 'r':
7870 col = read_rgb(end);
7871 break;
7873 if (col)
7874 curenv->set_fill_color(col);
7875 while (tok != start) {
7876 if (tok.newline() || tok.eof()) {
7877 warning(WARN_DELIM, "missing closing delimiter");
7878 input_stack::push(make_temp_iterator("\n"));
7879 break;
7881 tok.next();
7883 have_input = 1;
7886 static struct {
7887 const char *name;
7888 int mask;
7889 } warning_table[] = {
7890 { "char", WARN_CHAR },
7891 { "range", WARN_RANGE },
7892 { "break", WARN_BREAK },
7893 { "delim", WARN_DELIM },
7894 { "el", WARN_EL },
7895 { "scale", WARN_SCALE },
7896 { "number", WARN_NUMBER },
7897 { "syntax", WARN_SYNTAX },
7898 { "tab", WARN_TAB },
7899 { "right-brace", WARN_RIGHT_BRACE },
7900 { "missing", WARN_MISSING },
7901 { "input", WARN_INPUT },
7902 { "escape", WARN_ESCAPE },
7903 { "space", WARN_SPACE },
7904 { "font", WARN_FONT },
7905 { "di", WARN_DI },
7906 { "mac", WARN_MAC },
7907 { "reg", WARN_REG },
7908 { "ig", WARN_IG },
7909 { "color", WARN_COLOR },
7910 { "all", WARN_TOTAL & ~(WARN_DI | WARN_MAC | WARN_REG) },
7911 { "w", WARN_TOTAL },
7912 { "default", DEFAULT_WARNING_MASK },
7915 static int lookup_warning(const char *name)
7917 for (unsigned int i = 0;
7918 i < sizeof(warning_table)/sizeof(warning_table[0]);
7919 i++)
7920 if (strcmp(name, warning_table[i].name) == 0)
7921 return warning_table[i].mask;
7922 return 0;
7925 static void enable_warning(const char *name)
7927 int mask = lookup_warning(name);
7928 if (mask)
7929 warning_mask |= mask;
7930 else
7931 error("unknown warning `%1'", name);
7934 static void disable_warning(const char *name)
7936 int mask = lookup_warning(name);
7937 if (mask)
7938 warning_mask &= ~mask;
7939 else
7940 error("unknown warning `%1'", name);
7943 static void copy_mode_error(const char *format,
7944 const errarg &arg1,
7945 const errarg &arg2,
7946 const errarg &arg3)
7948 if (ignoring) {
7949 static const char prefix[] = "(in ignored input) ";
7950 char *s = new char[sizeof(prefix) + strlen(format)];
7951 strcpy(s, prefix);
7952 strcat(s, format);
7953 warning(WARN_IG, s, arg1, arg2, arg3);
7954 a_delete s;
7956 else
7957 error(format, arg1, arg2, arg3);
7960 enum error_type { WARNING, OUTPUT_WARNING, ERROR, FATAL };
7962 static void do_error(error_type type,
7963 const char *format,
7964 const errarg &arg1,
7965 const errarg &arg2,
7966 const errarg &arg3)
7968 const char *filename;
7969 int lineno;
7970 if (inhibit_errors && type < FATAL)
7971 return;
7972 if (backtrace_flag)
7973 input_stack::backtrace();
7974 if (!get_file_line(&filename, &lineno))
7975 filename = 0;
7976 if (filename)
7977 errprint("%1:%2: ", filename, lineno);
7978 else if (program_name)
7979 fprintf(stderr, "%s: ", program_name);
7980 switch (type) {
7981 case FATAL:
7982 fputs("fatal error: ", stderr);
7983 break;
7984 case ERROR:
7985 break;
7986 case WARNING:
7987 fputs("warning: ", stderr);
7988 break;
7989 case OUTPUT_WARNING:
7990 double fromtop = topdiv->get_vertical_position().to_units() / warn_scale;
7991 fprintf(stderr, "warning [p %d, %.1f%c",
7992 topdiv->get_page_number(), fromtop, warn_scaling_indicator);
7993 if (topdiv != curdiv) {
7994 double fromtop1 = curdiv->get_vertical_position().to_units()
7995 / warn_scale;
7996 fprintf(stderr, ", div `%s', %.1f%c",
7997 curdiv->get_diversion_name(), fromtop1, warn_scaling_indicator);
7999 fprintf(stderr, "]: ");
8000 break;
8002 errprint(format, arg1, arg2, arg3);
8003 fputc('\n', stderr);
8004 fflush(stderr);
8005 if (type == FATAL)
8006 cleanup_and_exit(1);
8009 int warning(warning_type t,
8010 const char *format,
8011 const errarg &arg1,
8012 const errarg &arg2,
8013 const errarg &arg3)
8015 if ((t & warning_mask) != 0) {
8016 do_error(WARNING, format, arg1, arg2, arg3);
8017 return 1;
8019 else
8020 return 0;
8023 int output_warning(warning_type t,
8024 const char *format,
8025 const errarg &arg1,
8026 const errarg &arg2,
8027 const errarg &arg3)
8029 if ((t & warning_mask) != 0) {
8030 do_error(OUTPUT_WARNING, format, arg1, arg2, arg3);
8031 return 1;
8033 else
8034 return 0;
8037 void error(const char *format,
8038 const errarg &arg1,
8039 const errarg &arg2,
8040 const errarg &arg3)
8042 do_error(ERROR, format, arg1, arg2, arg3);
8045 void fatal(const char *format,
8046 const errarg &arg1,
8047 const errarg &arg2,
8048 const errarg &arg3)
8050 do_error(FATAL, format, arg1, arg2, arg3);
8053 void fatal_with_file_and_line(const char *filename, int lineno,
8054 const char *format,
8055 const errarg &arg1,
8056 const errarg &arg2,
8057 const errarg &arg3)
8059 fprintf(stderr, "%s:%d: fatal error: ", filename, lineno);
8060 errprint(format, arg1, arg2, arg3);
8061 fputc('\n', stderr);
8062 fflush(stderr);
8063 cleanup_and_exit(1);
8066 void error_with_file_and_line(const char *filename, int lineno,
8067 const char *format,
8068 const errarg &arg1,
8069 const errarg &arg2,
8070 const errarg &arg3)
8072 fprintf(stderr, "%s:%d: error: ", filename, lineno);
8073 errprint(format, arg1, arg2, arg3);
8074 fputc('\n', stderr);
8075 fflush(stderr);
8078 dictionary charinfo_dictionary(501);
8080 charinfo *get_charinfo(symbol nm)
8082 void *p = charinfo_dictionary.lookup(nm);
8083 if (p != 0)
8084 return (charinfo *)p;
8085 charinfo *cp = new charinfo(nm);
8086 (void)charinfo_dictionary.lookup(nm, cp);
8087 return cp;
8090 int charinfo::next_index = 0;
8092 charinfo::charinfo(symbol s)
8093 : translation(0), mac(0), special_translation(TRANSLATE_NONE),
8094 hyphenation_code(0), flags(0), ascii_code(0), asciify_code(0),
8095 not_found(0), transparent_translate(1), translate_input(0),
8096 mode(CHAR_NORMAL), nm(s)
8098 index = next_index++;
8101 void charinfo::set_hyphenation_code(unsigned char c)
8103 hyphenation_code = c;
8106 void charinfo::set_translation(charinfo *ci, int tt, int ti)
8108 translation = ci;
8109 if (ci && ti) {
8110 if (hyphenation_code != 0)
8111 ci->set_hyphenation_code(hyphenation_code);
8112 if (asciify_code != 0)
8113 ci->set_asciify_code(asciify_code);
8114 else if (ascii_code != 0)
8115 ci->set_asciify_code(ascii_code);
8116 ci->set_translation_input();
8118 special_translation = TRANSLATE_NONE;
8119 transparent_translate = tt;
8122 void charinfo::set_special_translation(int c, int tt)
8124 special_translation = c;
8125 translation = 0;
8126 transparent_translate = tt;
8129 void charinfo::set_ascii_code(unsigned char c)
8131 ascii_code = c;
8134 void charinfo::set_asciify_code(unsigned char c)
8136 asciify_code = c;
8139 macro *charinfo::set_macro(macro *m)
8141 macro *tem = mac;
8142 mac = m;
8143 return tem;
8146 macro *charinfo::setx_macro(macro *m, char_mode cm)
8148 macro *tem = mac;
8149 mac = m;
8150 mode = cm;
8151 return tem;
8154 void charinfo::set_number(int n)
8156 number = n;
8157 flags |= NUMBERED;
8160 int charinfo::get_number()
8162 assert(flags & NUMBERED);
8163 return number;
8166 symbol UNNAMED_SYMBOL("---");
8168 // For numbered characters not between 0 and 255, we make a symbol out
8169 // of the number and store them in this dictionary.
8171 dictionary numbered_charinfo_dictionary(11);
8173 charinfo *get_charinfo_by_number(int n)
8175 static charinfo *number_table[256];
8177 if (n >= 0 && n < 256) {
8178 charinfo *ci = number_table[n];
8179 if (!ci) {
8180 ci = new charinfo(UNNAMED_SYMBOL);
8181 ci->set_number(n);
8182 number_table[n] = ci;
8184 return ci;
8186 else {
8187 symbol ns(i_to_a(n));
8188 charinfo *ci = (charinfo *)numbered_charinfo_dictionary.lookup(ns);
8189 if (!ci) {
8190 ci = new charinfo(UNNAMED_SYMBOL);
8191 ci->set_number(n);
8192 (void)numbered_charinfo_dictionary.lookup(ns, ci);
8194 return ci;
8198 int font::name_to_index(const char *nm)
8200 charinfo *ci;
8201 if (nm[1] == 0)
8202 ci = charset_table[nm[0] & 0xff];
8203 else if (nm[0] == '\\' && nm[2] == 0)
8204 ci = get_charinfo(symbol(nm + 1));
8205 else
8206 ci = get_charinfo(symbol(nm));
8207 if (ci == 0)
8208 return -1;
8209 else
8210 return ci->get_index();
8213 int font::number_to_index(int n)
8215 return get_charinfo_by_number(n)->get_index();