* All affected files: Update postal address of FSF.
[s-roff.git] / src / roff / troff / input.cpp
blobc708ee8a45fc263a7bac97774a06eb75041aeecd
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
11 version.
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
22 #define DEBUGGING
24 #include "troff.h"
25 #include "dictionary.h"
26 #include "hvunits.h"
27 #include "stringclass.h"
28 #include "mtsm.h"
29 #include "env.h"
30 #include "request.h"
31 #include "node.h"
32 #include "token.h"
33 #include "div.h"
34 #include "reg.h"
35 #include "charinfo.h"
36 #include "macropath.h"
37 #include "input.h"
38 #include "defs.h"
39 #include "font.h"
40 #include "unicode.h"
42 // Needed for getpid() and isatty()
43 #include "posix.h"
45 #include "nonposix.h"
47 #ifdef NEED_DECLARATION_PUTENV
48 extern "C" {
49 int putenv(const char *);
51 #endif /* NEED_DECLARATION_PUTENV */
53 #define MACRO_PREFIX "tmac."
54 #define MACRO_POSTFIX ".tmac"
55 #define INITIAL_STARTUP_FILE "troffrc"
56 #define FINAL_STARTUP_FILE "troffrc-end"
57 #define DEFAULT_INPUT_STACK_LIMIT 1000
59 #ifndef DEFAULT_WARNING_MASK
60 // warnings that are enabled by default
61 #define DEFAULT_WARNING_MASK \
62 (WARN_CHAR|WARN_NUMBER|WARN_BREAK|WARN_SPACE|WARN_FONT)
63 #endif
65 // initial size of buffer for reading names; expanded as necessary
66 #define ABUF_SIZE 16
68 extern "C" const char *program_name;
69 extern "C" const char *Version_string;
71 #ifdef COLUMN
72 void init_column_requests();
73 #endif /* COLUMN */
75 static node *read_draw_node();
76 static void read_color_draw_node(token &);
77 static void push_token(const token &);
78 void copy_file();
79 #ifdef COLUMN
80 void vjustify();
81 #endif /* COLUMN */
82 void transparent_file();
84 token tok;
85 int break_flag = 0;
86 int color_flag = 1; // colors are on by default
87 static int backtrace_flag = 0;
88 #ifndef POPEN_MISSING
89 char *pipe_command = 0;
90 #endif
91 charinfo *charset_table[256];
92 unsigned char hpf_code_table[256];
94 static int warning_mask = DEFAULT_WARNING_MASK;
95 static int inhibit_errors = 0;
96 static int ignoring = 0;
98 static void enable_warning(const char *);
99 static void disable_warning(const char *);
101 static int escape_char = '\\';
102 static symbol end_macro_name;
103 static symbol blank_line_macro_name;
104 static int compatible_flag = 0;
105 int ascii_output_flag = 0;
106 int suppress_output_flag = 0;
107 int is_html = 0;
108 int begin_level = 0; // number of nested \O escapes
110 int have_input = 0; // whether \f, \F, \D'F...', \H, \m, \M,
111 // \R, \s, or \S has been processed in
112 // token::next()
113 int old_have_input = 0; // value of have_input right before \n
114 int tcommand_flag = 0;
115 int safer_flag = 1; // safer by default
117 int have_string_arg = 0; // whether we have \*[foo bar...]
119 double spread_limit = -3.0 - 1.0; // negative means deactivated
121 double warn_scale;
122 char warn_scaling_indicator;
123 int debug_state = 0; // turns on debugging of the html troff state
125 search_path *mac_path = &safer_macro_path;
127 // Defaults to the current directory.
128 search_path include_search_path(0, 0, 0, 1);
130 static int get_copy(node**, int = 0);
131 static void copy_mode_error(const char *,
132 const errarg & = empty_errarg,
133 const errarg & = empty_errarg,
134 const errarg & = empty_errarg);
136 enum read_mode { ALLOW_EMPTY, WITH_ARGS, NO_ARGS };
137 static symbol read_escape_name(read_mode mode = NO_ARGS);
138 static symbol read_long_escape_name(read_mode mode = NO_ARGS);
139 static void interpolate_string(symbol);
140 static void interpolate_string_with_args(symbol);
141 static void interpolate_macro(symbol);
142 static void interpolate_number_format(symbol);
143 static void interpolate_environment_variable(symbol);
145 static symbol composite_glyph_name(symbol);
146 static void interpolate_arg(symbol);
147 static request_or_macro *lookup_request(symbol);
148 static int get_delim_number(units *, unsigned char);
149 static int get_delim_number(units *, unsigned char, units);
150 static symbol do_get_long_name(int, char);
151 static int get_line_arg(units *res, unsigned char si, charinfo **cp);
152 static int read_size(int *);
153 static symbol get_delim_name();
154 static void init_registers();
155 static void trapping_blank_line();
157 class input_iterator;
158 input_iterator *make_temp_iterator(const char *);
159 const char *input_char_description(int);
161 void process_input_stack();
164 void set_escape_char()
166 if (has_arg()) {
167 if (tok.ch() == 0) {
168 error("bad escape character");
169 escape_char = '\\';
171 else
172 escape_char = tok.ch();
174 else
175 escape_char = '\\';
176 skip_line();
179 void escape_off()
181 escape_char = 0;
182 skip_line();
185 static int saved_escape_char = '\\';
187 void save_escape_char()
189 saved_escape_char = escape_char;
190 skip_line();
193 void restore_escape_char()
195 escape_char = saved_escape_char;
196 skip_line();
199 class input_iterator {
200 public:
201 input_iterator();
202 input_iterator(int is_div);
203 virtual ~input_iterator() {}
204 int get(node **);
205 friend class input_stack;
206 int is_diversion;
207 statem *diversion_state;
208 protected:
209 const unsigned char *ptr;
210 const unsigned char *eptr;
211 input_iterator *next;
212 private:
213 virtual int fill(node **);
214 virtual int peek();
215 virtual int has_args() { return 0; }
216 virtual int nargs() { return 0; }
217 virtual input_iterator *get_arg(int) { return 0; }
218 virtual int get_location(int, const char **, int *) { return 0; }
219 virtual void backtrace() {}
220 virtual int set_location(const char *, int) { return 0; }
221 virtual int next_file(FILE *, const char *) { return 0; }
222 virtual void shift(int) {}
223 virtual int is_boundary() {return 0; }
224 virtual int is_file() { return 0; }
225 virtual int is_macro() { return 0; }
226 virtual void save_compatible_flag(int) {}
227 virtual int get_compatible_flag() { return 0; }
230 input_iterator::input_iterator()
231 : is_diversion(0), ptr(0), eptr(0)
235 input_iterator::input_iterator(int is_div)
236 : is_diversion(is_div), ptr(0), eptr(0)
240 int input_iterator::fill(node **)
242 return EOF;
245 int input_iterator::peek()
247 return EOF;
250 inline int input_iterator::get(node **p)
252 return ptr < eptr ? *ptr++ : fill(p);
255 class input_boundary : public input_iterator {
256 public:
257 int is_boundary() { return 1; }
260 class input_return_boundary : public input_iterator {
261 public:
262 int is_boundary() { return 2; }
265 class file_iterator : public input_iterator {
266 FILE *fp;
267 int lineno;
268 const char *filename;
269 int popened;
270 int newline_flag;
271 int seen_escape;
272 enum { BUF_SIZE = 512 };
273 unsigned char buf[BUF_SIZE];
274 void close();
275 public:
276 file_iterator(FILE *, const char *, int = 0);
277 ~file_iterator();
278 int fill(node **);
279 int peek();
280 int get_location(int, const char **, int *);
281 void backtrace();
282 int set_location(const char *, int);
283 int next_file(FILE *, const char *);
284 int is_file();
287 file_iterator::file_iterator(FILE *f, const char *fn, int po)
288 : fp(f), lineno(1), filename(fn), popened(po),
289 newline_flag(0), seen_escape(0)
291 if ((font::use_charnames_in_special) && (fn != 0)) {
292 if (!the_output)
293 init_output();
294 the_output->put_filename(fn);
298 file_iterator::~file_iterator()
300 close();
303 void file_iterator::close()
305 if (fp == stdin)
306 clearerr(stdin);
307 #ifndef POPEN_MISSING
308 else if (popened)
309 pclose(fp);
310 #endif /* not POPEN_MISSING */
311 else
312 fclose(fp);
315 int file_iterator::is_file()
317 return 1;
320 int file_iterator::next_file(FILE *f, const char *s)
322 close();
323 filename = s;
324 fp = f;
325 lineno = 1;
326 newline_flag = 0;
327 seen_escape = 0;
328 popened = 0;
329 ptr = 0;
330 eptr = 0;
331 return 1;
334 int file_iterator::fill(node **)
336 if (newline_flag)
337 lineno++;
338 newline_flag = 0;
339 unsigned char *p = buf;
340 ptr = p;
341 unsigned char *e = p + BUF_SIZE;
342 while (p < e) {
343 int c = getc(fp);
344 if (c == EOF)
345 break;
346 if (invalid_input_char(c))
347 warning(WARN_INPUT, "invalid input character code %1", int(c));
348 else {
349 *p++ = c;
350 if (c == '\n') {
351 seen_escape = 0;
352 newline_flag = 1;
353 break;
355 seen_escape = (c == '\\');
358 if (p > buf) {
359 eptr = p;
360 return *ptr++;
362 else {
363 eptr = p;
364 return EOF;
368 int file_iterator::peek()
370 int c = getc(fp);
371 while (invalid_input_char(c)) {
372 warning(WARN_INPUT, "invalid input character code %1", int(c));
373 c = getc(fp);
375 if (c != EOF)
376 ungetc(c, fp);
377 return c;
380 int file_iterator::get_location(int /*allow_macro*/,
381 const char **filenamep, int *linenop)
383 *linenop = lineno;
384 if (filename != 0 && strcmp(filename, "-") == 0)
385 *filenamep = "<standard input>";
386 else
387 *filenamep = filename;
388 return 1;
391 void file_iterator::backtrace()
393 errprint("%1:%2: backtrace: %3 `%1'\n", filename, lineno,
394 popened ? "process" : "file");
397 int file_iterator::set_location(const char *f, int ln)
399 if (f) {
400 filename = f;
401 if (!the_output)
402 init_output();
403 the_output->put_filename(f);
405 lineno = ln;
406 return 1;
409 input_iterator nil_iterator;
411 class input_stack {
412 public:
413 static int get(node **);
414 static int peek();
415 static void push(input_iterator *);
416 static input_iterator *get_arg(int);
417 static int nargs();
418 static int get_location(int, const char **, int *);
419 static int set_location(const char *, int);
420 static void backtrace();
421 static void backtrace_all();
422 static void next_file(FILE *, const char *);
423 static void end_file();
424 static void shift(int n);
425 static void add_boundary();
426 static void add_return_boundary();
427 static int is_return_boundary();
428 static void remove_boundary();
429 static int get_level();
430 static int get_div_level();
431 static void increase_level();
432 static void decrease_level();
433 static void clear();
434 static void pop_macro();
435 static void save_compatible_flag(int);
436 static int get_compatible_flag();
437 static statem *get_diversion_state();
438 static void check_end_diversion(input_iterator *t);
439 static int limit;
440 static int div_level;
441 static statem *diversion_state;
442 private:
443 static input_iterator *top;
444 static int level;
445 static int finish_get(node **);
446 static int finish_peek();
449 input_iterator *input_stack::top = &nil_iterator;
450 int input_stack::level = 0;
451 int input_stack::limit = DEFAULT_INPUT_STACK_LIMIT;
452 int input_stack::div_level = 0;
453 statem *input_stack::diversion_state = NULL;
454 int suppress_push=0;
457 inline int input_stack::get_level()
459 return level;
462 inline void input_stack::increase_level()
464 level++;
467 inline void input_stack::decrease_level()
469 level--;
472 inline int input_stack::get_div_level()
474 return div_level;
477 inline int input_stack::get(node **np)
479 int res = (top->ptr < top->eptr) ? *top->ptr++ : finish_get(np);
480 if (res == '\n') {
481 old_have_input = have_input;
482 have_input = 0;
484 return res;
487 int input_stack::finish_get(node **np)
489 for (;;) {
490 int c = top->fill(np);
491 if (c != EOF || top->is_boundary())
492 return c;
493 if (top == &nil_iterator)
494 break;
495 input_iterator *tem = top;
496 check_end_diversion(tem);
497 #if defined(DEBUGGING)
498 if (debug_state)
499 if (tem->is_diversion)
500 fprintf(stderr,
501 "in diversion level = %d\n", input_stack::get_div_level());
502 #endif
503 top = top->next;
504 level--;
505 delete tem;
506 if (top->ptr < top->eptr)
507 return *top->ptr++;
509 assert(level == 0);
510 return EOF;
513 inline int input_stack::peek()
515 return (top->ptr < top->eptr) ? *top->ptr : finish_peek();
518 void input_stack::check_end_diversion(input_iterator *t)
520 if (t->is_diversion) {
521 div_level--;
522 diversion_state = t->diversion_state;
526 int input_stack::finish_peek()
528 for (;;) {
529 int c = top->peek();
530 if (c != EOF || top->is_boundary())
531 return c;
532 if (top == &nil_iterator)
533 break;
534 input_iterator *tem = top;
535 check_end_diversion(tem);
536 top = top->next;
537 level--;
538 delete tem;
539 if (top->ptr < top->eptr)
540 return *top->ptr;
542 assert(level == 0);
543 return EOF;
546 void input_stack::add_boundary()
548 push(new input_boundary);
551 void input_stack::add_return_boundary()
553 push(new input_return_boundary);
556 int input_stack::is_return_boundary()
558 return top->is_boundary() == 2;
561 void input_stack::remove_boundary()
563 assert(top->is_boundary());
564 input_iterator *temp = top->next;
565 check_end_diversion(top);
567 delete top;
568 top = temp;
569 level--;
572 void input_stack::push(input_iterator *in)
574 if (in == 0)
575 return;
576 if (++level > limit && limit > 0)
577 fatal("input stack limit exceeded (probable infinite loop)");
578 in->next = top;
579 top = in;
580 if (top->is_diversion) {
581 div_level++;
582 in->diversion_state = diversion_state;
583 diversion_state = curenv->construct_state(0);
584 #if defined(DEBUGGING)
585 if (debug_state) {
586 curenv->dump_troff_state();
587 fflush(stderr);
589 #endif
591 #if defined(DEBUGGING)
592 if (debug_state)
593 if (top->is_diversion) {
594 fprintf(stderr,
595 "in diversion level = %d\n", input_stack::get_div_level());
596 fflush(stderr);
598 #endif
601 statem *get_diversion_state()
603 return input_stack::get_diversion_state();
606 statem *input_stack::get_diversion_state()
608 if (diversion_state == NULL)
609 return NULL;
610 else
611 return new statem(diversion_state);
614 input_iterator *input_stack::get_arg(int i)
616 input_iterator *p;
617 for (p = top; p != 0; p = p->next)
618 if (p->has_args())
619 return p->get_arg(i);
620 return 0;
623 void input_stack::shift(int n)
625 for (input_iterator *p = top; p; p = p->next)
626 if (p->has_args()) {
627 p->shift(n);
628 return;
632 int input_stack::nargs()
634 for (input_iterator *p =top; p != 0; p = p->next)
635 if (p->has_args())
636 return p->nargs();
637 return 0;
640 int input_stack::get_location(int allow_macro, const char **filenamep, int *linenop)
642 for (input_iterator *p = top; p; p = p->next)
643 if (p->get_location(allow_macro, filenamep, linenop))
644 return 1;
645 return 0;
648 void input_stack::backtrace()
650 const char *f;
651 int n;
652 // only backtrace down to (not including) the topmost file
653 for (input_iterator *p = top;
654 p && !p->get_location(0, &f, &n);
655 p = p->next)
656 p->backtrace();
659 void input_stack::backtrace_all()
661 for (input_iterator *p = top; p; p = p->next)
662 p->backtrace();
665 int input_stack::set_location(const char *filename, int lineno)
667 for (input_iterator *p = top; p; p = p->next)
668 if (p->set_location(filename, lineno))
669 return 1;
670 return 0;
673 void input_stack::next_file(FILE *fp, const char *s)
675 input_iterator **pp;
676 for (pp = &top; *pp != &nil_iterator; pp = &(*pp)->next)
677 if ((*pp)->next_file(fp, s))
678 return;
679 if (++level > limit && limit > 0)
680 fatal("input stack limit exceeded");
681 *pp = new file_iterator(fp, s);
682 (*pp)->next = &nil_iterator;
685 void input_stack::end_file()
687 for (input_iterator **pp = &top; *pp != &nil_iterator; pp = &(*pp)->next)
688 if ((*pp)->is_file()) {
689 input_iterator *tem = *pp;
690 check_end_diversion(tem);
691 *pp = (*pp)->next;
692 delete tem;
693 level--;
694 return;
698 void input_stack::clear()
700 int nboundaries = 0;
701 while (top != &nil_iterator) {
702 if (top->is_boundary())
703 nboundaries++;
704 input_iterator *tem = top;
705 check_end_diversion(tem);
706 top = top->next;
707 level--;
708 delete tem;
710 // Keep while_request happy.
711 for (; nboundaries > 0; --nboundaries)
712 add_return_boundary();
715 void input_stack::pop_macro()
717 int nboundaries = 0;
718 int is_macro = 0;
719 do {
720 if (top->next == &nil_iterator)
721 break;
722 if (top->is_boundary())
723 nboundaries++;
724 is_macro = top->is_macro();
725 input_iterator *tem = top;
726 check_end_diversion(tem);
727 top = top->next;
728 level--;
729 delete tem;
730 } while (!is_macro);
731 // Keep while_request happy.
732 for (; nboundaries > 0; --nboundaries)
733 add_return_boundary();
736 inline void input_stack::save_compatible_flag(int f)
738 top->save_compatible_flag(f);
741 inline int input_stack::get_compatible_flag()
743 return top->get_compatible_flag();
746 void backtrace_request()
748 input_stack::backtrace_all();
749 fflush(stderr);
750 skip_line();
753 void next_file()
755 symbol nm = get_long_name();
756 while (!tok.newline() && !tok.eof())
757 tok.next();
758 if (nm.is_null())
759 input_stack::end_file();
760 else {
761 errno = 0;
762 FILE *fp = include_search_path.open_file_cautious(nm.contents());
763 if (!fp)
764 error("can't open `%1': %2", nm.contents(), strerror(errno));
765 else
766 input_stack::next_file(fp, nm.contents());
768 tok.next();
771 void shift()
773 int n;
774 if (!has_arg() || !get_integer(&n))
775 n = 1;
776 input_stack::shift(n);
777 skip_line();
780 static char get_char_for_escape_name(int allow_space = 0)
782 int c = get_copy(0);
783 switch (c) {
784 case EOF:
785 copy_mode_error("end of input in escape name");
786 return '\0';
787 default:
788 if (!invalid_input_char(c))
789 break;
790 // fall through
791 case '\n':
792 if (c == '\n')
793 input_stack::push(make_temp_iterator("\n"));
794 // fall through
795 case ' ':
796 if (c == ' ' && allow_space)
797 break;
798 // fall through
799 case '\t':
800 case '\001':
801 case '\b':
802 copy_mode_error("%1 is not allowed in an escape name",
803 input_char_description(c));
804 return '\0';
806 return c;
809 static symbol read_two_char_escape_name()
811 char buf[3];
812 buf[0] = get_char_for_escape_name();
813 if (buf[0] != '\0') {
814 buf[1] = get_char_for_escape_name();
815 if (buf[1] == '\0')
816 buf[0] = 0;
817 else
818 buf[2] = 0;
820 return symbol(buf);
823 static symbol read_long_escape_name(read_mode mode)
825 int start_level = input_stack::get_level();
826 char abuf[ABUF_SIZE];
827 char *buf = abuf;
828 int buf_size = ABUF_SIZE;
829 int i = 0;
830 char c;
831 int have_char = 0;
832 for (;;) {
833 c = get_char_for_escape_name(have_char && mode == WITH_ARGS);
834 if (c == 0) {
835 if (buf != abuf)
836 a_delete buf;
837 return NULL_SYMBOL;
839 have_char = 1;
840 if (mode == WITH_ARGS && c == ' ')
841 break;
842 if (i + 2 > buf_size) {
843 if (buf == abuf) {
844 buf = new char[ABUF_SIZE*2];
845 memcpy(buf, abuf, buf_size);
846 buf_size = ABUF_SIZE*2;
848 else {
849 char *old_buf = buf;
850 buf = new char[buf_size*2];
851 memcpy(buf, old_buf, buf_size);
852 buf_size *= 2;
853 a_delete old_buf;
856 if (c == ']' && input_stack::get_level() == start_level)
857 break;
858 buf[i++] = c;
860 buf[i] = 0;
861 if (c == ' ')
862 have_string_arg = 1;
863 if (buf == abuf) {
864 if (i == 0) {
865 if (mode != ALLOW_EMPTY)
866 copy_mode_error("empty escape name");
867 return EMPTY_SYMBOL;
869 return symbol(abuf);
871 else {
872 symbol s(buf);
873 a_delete buf;
874 return s;
878 static symbol read_escape_name(read_mode mode)
880 char c = get_char_for_escape_name();
881 if (c == 0)
882 return NULL_SYMBOL;
883 if (c == '(')
884 return read_two_char_escape_name();
885 if (c == '[' && !compatible_flag)
886 return read_long_escape_name(mode);
887 char buf[2];
888 buf[0] = c;
889 buf[1] = '\0';
890 return symbol(buf);
893 static symbol read_increment_and_escape_name(int *incp)
895 char c = get_char_for_escape_name();
896 switch (c) {
897 case 0:
898 *incp = 0;
899 return NULL_SYMBOL;
900 case '(':
901 *incp = 0;
902 return read_two_char_escape_name();
903 case '+':
904 *incp = 1;
905 return read_escape_name();
906 case '-':
907 *incp = -1;
908 return read_escape_name();
909 case '[':
910 if (!compatible_flag) {
911 *incp = 0;
912 return read_long_escape_name();
914 break;
916 *incp = 0;
917 char buf[2];
918 buf[0] = c;
919 buf[1] = '\0';
920 return symbol(buf);
923 static int get_copy(node **nd, int defining)
925 for (;;) {
926 int c = input_stack::get(nd);
927 if (c == PUSH_GROFF_MODE) {
928 input_stack::save_compatible_flag(compatible_flag);
929 compatible_flag = 0;
930 continue;
932 if (c == PUSH_COMP_MODE) {
933 input_stack::save_compatible_flag(compatible_flag);
934 compatible_flag = 1;
935 continue;
937 if (c == POP_GROFFCOMP_MODE) {
938 compatible_flag = input_stack::get_compatible_flag();
939 continue;
941 if (c == BEGIN_QUOTE) {
942 input_stack::increase_level();
943 continue;
945 if (c == END_QUOTE) {
946 input_stack::decrease_level();
947 continue;
949 if (c == ESCAPE_NEWLINE) {
950 if (defining)
951 return c;
952 do {
953 c = input_stack::get(nd);
954 } while (c == ESCAPE_NEWLINE);
956 if (c != escape_char || escape_char <= 0)
957 return c;
958 c = input_stack::peek();
959 switch(c) {
960 case 0:
961 return escape_char;
962 case '"':
963 (void)input_stack::get(0);
964 while ((c = input_stack::get(0)) != '\n' && c != EOF)
966 return c;
967 case '#': // Like \" but newline is ignored.
968 (void)input_stack::get(0);
969 while ((c = input_stack::get(0)) != '\n')
970 if (c == EOF)
971 return EOF;
972 break;
973 case '$':
975 (void)input_stack::get(0);
976 symbol s = read_escape_name();
977 if (!(s.is_null() || s.is_empty()))
978 interpolate_arg(s);
979 break;
981 case '*':
983 (void)input_stack::get(0);
984 symbol s = read_escape_name(WITH_ARGS);
985 if (!(s.is_null() || s.is_empty())) {
986 if (have_string_arg) {
987 have_string_arg = 0;
988 interpolate_string_with_args(s);
990 else
991 interpolate_string(s);
993 break;
995 case 'a':
996 (void)input_stack::get(0);
997 return '\001';
998 case 'e':
999 (void)input_stack::get(0);
1000 return ESCAPE_e;
1001 case 'E':
1002 (void)input_stack::get(0);
1003 return ESCAPE_E;
1004 case 'n':
1006 (void)input_stack::get(0);
1007 int inc;
1008 symbol s = read_increment_and_escape_name(&inc);
1009 if (!(s.is_null() || s.is_empty()))
1010 interpolate_number_reg(s, inc);
1011 break;
1013 case 'g':
1015 (void)input_stack::get(0);
1016 symbol s = read_escape_name();
1017 if (!(s.is_null() || s.is_empty()))
1018 interpolate_number_format(s);
1019 break;
1021 case 't':
1022 (void)input_stack::get(0);
1023 return '\t';
1024 case 'V':
1026 (void)input_stack::get(0);
1027 symbol s = read_escape_name();
1028 if (!(s.is_null() || s.is_empty()))
1029 interpolate_environment_variable(s);
1030 break;
1032 case '\n':
1033 (void)input_stack::get(0);
1034 if (defining)
1035 return ESCAPE_NEWLINE;
1036 break;
1037 case ' ':
1038 (void)input_stack::get(0);
1039 return ESCAPE_SPACE;
1040 case '~':
1041 (void)input_stack::get(0);
1042 return ESCAPE_TILDE;
1043 case ':':
1044 (void)input_stack::get(0);
1045 return ESCAPE_COLON;
1046 case '|':
1047 (void)input_stack::get(0);
1048 return ESCAPE_BAR;
1049 case '^':
1050 (void)input_stack::get(0);
1051 return ESCAPE_CIRCUMFLEX;
1052 case '{':
1053 (void)input_stack::get(0);
1054 return ESCAPE_LEFT_BRACE;
1055 case '}':
1056 (void)input_stack::get(0);
1057 return ESCAPE_RIGHT_BRACE;
1058 case '`':
1059 (void)input_stack::get(0);
1060 return ESCAPE_LEFT_QUOTE;
1061 case '\'':
1062 (void)input_stack::get(0);
1063 return ESCAPE_RIGHT_QUOTE;
1064 case '-':
1065 (void)input_stack::get(0);
1066 return ESCAPE_HYPHEN;
1067 case '_':
1068 (void)input_stack::get(0);
1069 return ESCAPE_UNDERSCORE;
1070 case 'c':
1071 (void)input_stack::get(0);
1072 return ESCAPE_c;
1073 case '!':
1074 (void)input_stack::get(0);
1075 return ESCAPE_BANG;
1076 case '?':
1077 (void)input_stack::get(0);
1078 return ESCAPE_QUESTION;
1079 case '&':
1080 (void)input_stack::get(0);
1081 return ESCAPE_AMPERSAND;
1082 case ')':
1083 (void)input_stack::get(0);
1084 return ESCAPE_RIGHT_PARENTHESIS;
1085 case '.':
1086 (void)input_stack::get(0);
1087 return c;
1088 case '%':
1089 (void)input_stack::get(0);
1090 return ESCAPE_PERCENT;
1091 default:
1092 if (c == escape_char) {
1093 (void)input_stack::get(0);
1094 return c;
1096 else
1097 return escape_char;
1102 class non_interpreted_char_node : public node {
1103 unsigned char c;
1104 public:
1105 non_interpreted_char_node(unsigned char);
1106 node *copy();
1107 int interpret(macro *);
1108 int same(node *);
1109 const char *type();
1110 int force_tprint();
1111 int is_tag();
1114 int non_interpreted_char_node::same(node *nd)
1116 return c == ((non_interpreted_char_node *)nd)->c;
1119 const char *non_interpreted_char_node::type()
1121 return "non_interpreted_char_node";
1124 int non_interpreted_char_node::force_tprint()
1126 return 0;
1129 int non_interpreted_char_node::is_tag()
1131 return 0;
1134 non_interpreted_char_node::non_interpreted_char_node(unsigned char n) : c(n)
1136 assert(n != 0);
1139 node *non_interpreted_char_node::copy()
1141 return new non_interpreted_char_node(c);
1144 int non_interpreted_char_node::interpret(macro *mac)
1146 mac->append(c);
1147 return 1;
1150 static void do_width();
1151 static node *do_non_interpreted();
1152 static node *do_special();
1153 static node *do_suppress(symbol nm);
1154 static void do_register();
1156 dictionary color_dictionary(501);
1158 static color *lookup_color(symbol nm)
1160 assert(!nm.is_null());
1161 if (nm == default_symbol)
1162 return &default_color;
1163 color *c = (color *)color_dictionary.lookup(nm);
1164 if (c == 0)
1165 warning(WARN_COLOR, "color `%1' not defined", nm.contents());
1166 return c;
1169 void do_glyph_color(symbol nm)
1171 if (nm.is_null())
1172 return;
1173 if (nm.is_empty())
1174 curenv->set_glyph_color(curenv->get_prev_glyph_color());
1175 else {
1176 color *tem = lookup_color(nm);
1177 if (tem)
1178 curenv->set_glyph_color(tem);
1179 else
1180 (void)color_dictionary.lookup(nm, new color(nm));
1184 void do_fill_color(symbol nm)
1186 if (nm.is_null())
1187 return;
1188 if (nm.is_empty())
1189 curenv->set_fill_color(curenv->get_prev_fill_color());
1190 else {
1191 color *tem = lookup_color(nm);
1192 if (tem)
1193 curenv->set_fill_color(tem);
1194 else
1195 (void)color_dictionary.lookup(nm, new color(nm));
1199 static unsigned int get_color_element(const char *scheme, const char *col)
1201 units val;
1202 if (!get_number(&val, 'f')) {
1203 warning(WARN_COLOR, "%1 in %2 definition set to 0", col, scheme);
1204 tok.next();
1205 return 0;
1207 if (val < 0) {
1208 warning(WARN_RANGE, "%1 cannot be negative: set to 0", col);
1209 return 0;
1211 if (val > color::MAX_COLOR_VAL+1) {
1212 warning(WARN_RANGE, "%1 cannot be greater than 1", col);
1213 // we change 0x10000 to 0xffff
1214 return color::MAX_COLOR_VAL;
1216 return (unsigned int)val;
1219 static color *read_rgb(char end = 0)
1221 symbol component = do_get_long_name(0, end);
1222 if (component.is_null()) {
1223 warning(WARN_COLOR, "missing rgb color values");
1224 return 0;
1226 const char *s = component.contents();
1227 color *col = new color;
1228 if (*s == '#') {
1229 if (!col->read_rgb(s)) {
1230 warning(WARN_COLOR, "expecting rgb color definition not `%1'", s);
1231 delete col;
1232 return 0;
1235 else {
1236 if (!end)
1237 input_stack::push(make_temp_iterator(" "));
1238 input_stack::push(make_temp_iterator(s));
1239 tok.next();
1240 unsigned int r = get_color_element("rgb color", "red component");
1241 unsigned int g = get_color_element("rgb color", "green component");
1242 unsigned int b = get_color_element("rgb color", "blue component");
1243 col->set_rgb(r, g, b);
1245 return col;
1248 static color *read_cmy(char end = 0)
1250 symbol component = do_get_long_name(0, end);
1251 if (component.is_null()) {
1252 warning(WARN_COLOR, "missing cmy color values");
1253 return 0;
1255 const char *s = component.contents();
1256 color *col = new color;
1257 if (*s == '#') {
1258 if (!col->read_cmy(s)) {
1259 warning(WARN_COLOR, "expecting cmy color definition not `%1'", s);
1260 delete col;
1261 return 0;
1264 else {
1265 if (!end)
1266 input_stack::push(make_temp_iterator(" "));
1267 input_stack::push(make_temp_iterator(s));
1268 tok.next();
1269 unsigned int c = get_color_element("cmy color", "cyan component");
1270 unsigned int m = get_color_element("cmy color", "magenta component");
1271 unsigned int y = get_color_element("cmy color", "yellow component");
1272 col->set_cmy(c, m, y);
1274 return col;
1277 static color *read_cmyk(char end = 0)
1279 symbol component = do_get_long_name(0, end);
1280 if (component.is_null()) {
1281 warning(WARN_COLOR, "missing cmyk color values");
1282 return 0;
1284 const char *s = component.contents();
1285 color *col = new color;
1286 if (*s == '#') {
1287 if (!col->read_cmyk(s)) {
1288 warning(WARN_COLOR, "`expecting a cmyk color definition not `%1'", s);
1289 delete col;
1290 return 0;
1293 else {
1294 if (!end)
1295 input_stack::push(make_temp_iterator(" "));
1296 input_stack::push(make_temp_iterator(s));
1297 tok.next();
1298 unsigned int c = get_color_element("cmyk color", "cyan component");
1299 unsigned int m = get_color_element("cmyk color", "magenta component");
1300 unsigned int y = get_color_element("cmyk color", "yellow component");
1301 unsigned int k = get_color_element("cmyk color", "black component");
1302 col->set_cmyk(c, m, y, k);
1304 return col;
1307 static color *read_gray(char end = 0)
1309 symbol component = do_get_long_name(0, end);
1310 if (component.is_null()) {
1311 warning(WARN_COLOR, "missing gray values");
1312 return 0;
1314 const char *s = component.contents();
1315 color *col = new color;
1316 if (*s == '#') {
1317 if (!col->read_gray(s)) {
1318 warning(WARN_COLOR, "`expecting a gray definition not `%1'", s);
1319 delete col;
1320 return 0;
1323 else {
1324 if (!end)
1325 input_stack::push(make_temp_iterator("\n"));
1326 input_stack::push(make_temp_iterator(s));
1327 tok.next();
1328 unsigned int g = get_color_element("gray", "gray value");
1329 col->set_gray(g);
1331 return col;
1334 static void activate_color()
1336 int n;
1337 if (has_arg() && get_integer(&n))
1338 color_flag = n != 0;
1339 else
1340 color_flag = 1;
1341 skip_line();
1344 static void define_color()
1346 symbol color_name = get_long_name(1);
1347 if (color_name.is_null()) {
1348 skip_line();
1349 return;
1351 if (color_name == default_symbol) {
1352 warning(WARN_COLOR, "default color can't be redefined");
1353 skip_line();
1354 return;
1356 symbol style = get_long_name(1);
1357 if (style.is_null()) {
1358 skip_line();
1359 return;
1361 color *col;
1362 if (strcmp(style.contents(), "rgb") == 0)
1363 col = read_rgb();
1364 else if (strcmp(style.contents(), "cmyk") == 0)
1365 col = read_cmyk();
1366 else if (strcmp(style.contents(), "gray") == 0)
1367 col = read_gray();
1368 else if (strcmp(style.contents(), "grey") == 0)
1369 col = read_gray();
1370 else if (strcmp(style.contents(), "cmy") == 0)
1371 col = read_cmy();
1372 else {
1373 warning(WARN_COLOR,
1374 "unknown color space `%1'; use rgb, cmyk, gray or cmy",
1375 style.contents());
1376 skip_line();
1377 return;
1379 if (col) {
1380 col->nm = color_name;
1381 (void)color_dictionary.lookup(color_name, col);
1383 skip_line();
1386 static node *do_overstrike()
1388 token start;
1389 overstrike_node *on = new overstrike_node;
1390 int start_level = input_stack::get_level();
1391 start.next();
1392 for (;;) {
1393 tok.next();
1394 if (tok.newline() || tok.eof()) {
1395 warning(WARN_DELIM, "missing closing delimiter");
1396 input_stack::push(make_temp_iterator("\n"));
1397 break;
1399 if (tok == start
1400 && (compatible_flag || input_stack::get_level() == start_level))
1401 break;
1402 charinfo *ci = tok.get_char(1);
1403 if (ci) {
1404 node *n = curenv->make_char_node(ci);
1405 if (n)
1406 on->overstrike(n);
1409 return on;
1412 static node *do_bracket()
1414 token start;
1415 bracket_node *bn = new bracket_node;
1416 start.next();
1417 int start_level = input_stack::get_level();
1418 for (;;) {
1419 tok.next();
1420 if (tok.eof()) {
1421 warning(WARN_DELIM, "missing closing delimiter");
1422 break;
1424 if (tok.newline()) {
1425 warning(WARN_DELIM, "missing closing delimiter");
1426 input_stack::push(make_temp_iterator("\n"));
1427 break;
1429 if (tok == start
1430 && (compatible_flag || input_stack::get_level() == start_level))
1431 break;
1432 charinfo *ci = tok.get_char(1);
1433 if (ci) {
1434 node *n = curenv->make_char_node(ci);
1435 if (n)
1436 bn->bracket(n);
1439 return bn;
1442 static int do_name_test()
1444 token start;
1445 start.next();
1446 int start_level = input_stack::get_level();
1447 int bad_char = 0;
1448 int some_char = 0;
1449 for (;;) {
1450 tok.next();
1451 if (tok.newline() || tok.eof()) {
1452 warning(WARN_DELIM, "missing closing delimiter");
1453 input_stack::push(make_temp_iterator("\n"));
1454 break;
1456 if (tok == start
1457 && (compatible_flag || input_stack::get_level() == start_level))
1458 break;
1459 if (!tok.ch())
1460 bad_char = 1;
1461 some_char = 1;
1463 return some_char && !bad_char;
1466 static int do_expr_test()
1468 token start;
1469 start.next();
1470 int start_level = input_stack::get_level();
1471 if (!start.delimiter(1))
1472 return 0;
1473 tok.next();
1474 // disable all warning and error messages temporarily
1475 int saved_warning_mask = warning_mask;
1476 int saved_inhibit_errors = inhibit_errors;
1477 warning_mask = 0;
1478 inhibit_errors = 1;
1479 int dummy;
1480 int result = get_number_rigidly(&dummy, 'u');
1481 warning_mask = saved_warning_mask;
1482 inhibit_errors = saved_inhibit_errors;
1483 if (tok == start && input_stack::get_level() == start_level)
1484 return result;
1485 // ignore everything up to the delimiter in case we aren't right there
1486 for (;;) {
1487 tok.next();
1488 if (tok.newline() || tok.eof()) {
1489 warning(WARN_DELIM, "missing closing delimiter");
1490 input_stack::push(make_temp_iterator("\n"));
1491 break;
1493 if (tok == start && input_stack::get_level() == start_level)
1494 break;
1496 return 0;
1499 #if 0
1500 static node *do_zero_width()
1502 token start;
1503 start.next();
1504 int start_level = input_stack::get_level();
1505 environment env(curenv);
1506 environment *oldenv = curenv;
1507 curenv = &env;
1508 for (;;) {
1509 tok.next();
1510 if (tok.newline() || tok.eof()) {
1511 error("missing closing delimiter");
1512 break;
1514 if (tok == start
1515 && (compatible_flag || input_stack::get_level() == start_level))
1516 break;
1517 tok.process();
1519 curenv = oldenv;
1520 node *rev = env.extract_output_line();
1521 node *n = 0;
1522 while (rev) {
1523 node *tem = rev;
1524 rev = rev->next;
1525 tem->next = n;
1526 n = tem;
1528 return new zero_width_node(n);
1531 #else
1533 // It's undesirable for \Z to change environments, because then
1534 // \n(.w won't work as expected.
1536 static node *do_zero_width()
1538 node *rev = new dummy_node;
1539 token start;
1540 start.next();
1541 int start_level = input_stack::get_level();
1542 for (;;) {
1543 tok.next();
1544 if (tok.newline() || tok.eof()) {
1545 warning(WARN_DELIM, "missing closing delimiter");
1546 input_stack::push(make_temp_iterator("\n"));
1547 break;
1549 if (tok == start
1550 && (compatible_flag || input_stack::get_level() == start_level))
1551 break;
1552 if (!tok.add_to_node_list(&rev))
1553 error("invalid token in argument to \\Z");
1555 node *n = 0;
1556 while (rev) {
1557 node *tem = rev;
1558 rev = rev->next;
1559 tem->next = n;
1560 n = tem;
1562 return new zero_width_node(n);
1565 #endif
1567 token_node *node::get_token_node()
1569 return 0;
1572 class token_node : public node {
1573 public:
1574 token tk;
1575 token_node(const token &t);
1576 node *copy();
1577 token_node *get_token_node();
1578 int same(node *);
1579 const char *type();
1580 int force_tprint();
1581 int is_tag();
1584 token_node::token_node(const token &t) : tk(t)
1588 node *token_node::copy()
1590 return new token_node(tk);
1593 token_node *token_node::get_token_node()
1595 return this;
1598 int token_node::same(node *nd)
1600 return tk == ((token_node *)nd)->tk;
1603 const char *token_node::type()
1605 return "token_node";
1608 int token_node::force_tprint()
1610 return 0;
1613 int token_node::is_tag()
1615 return 0;
1618 token::token() : nd(0), type(TOKEN_EMPTY)
1622 token::~token()
1624 delete nd;
1627 token::token(const token &t)
1628 : nm(t.nm), c(t.c), val(t.val), dim(t.dim), type(t.type)
1630 // Use two statements to work around bug in SGI C++.
1631 node *tem = t.nd;
1632 nd = tem ? tem->copy() : 0;
1635 void token::operator=(const token &t)
1637 delete nd;
1638 nm = t.nm;
1639 // Use two statements to work around bug in SGI C++.
1640 node *tem = t.nd;
1641 nd = tem ? tem->copy() : 0;
1642 c = t.c;
1643 val = t.val;
1644 dim = t.dim;
1645 type = t.type;
1648 void token::skip()
1650 while (space())
1651 next();
1654 int has_arg()
1656 while (tok.space())
1657 tok.next();
1658 return !tok.newline();
1661 void token::make_space()
1663 type = TOKEN_SPACE;
1666 void token::make_newline()
1668 type = TOKEN_NEWLINE;
1671 void token::next()
1673 if (nd) {
1674 delete nd;
1675 nd = 0;
1677 units x;
1678 for (;;) {
1679 node *n = 0;
1680 int cc = input_stack::get(&n);
1681 if (cc != escape_char || escape_char == 0) {
1682 handle_normal_char:
1683 switch(cc) {
1684 case PUSH_GROFF_MODE:
1685 input_stack::save_compatible_flag(compatible_flag);
1686 compatible_flag = 0;
1687 continue;
1688 case PUSH_COMP_MODE:
1689 input_stack::save_compatible_flag(compatible_flag);
1690 compatible_flag = 1;
1691 continue;
1692 case POP_GROFFCOMP_MODE:
1693 compatible_flag = input_stack::get_compatible_flag();
1694 continue;
1695 case BEGIN_QUOTE:
1696 input_stack::increase_level();
1697 continue;
1698 case END_QUOTE:
1699 input_stack::decrease_level();
1700 continue;
1701 case EOF:
1702 type = TOKEN_EOF;
1703 return;
1704 case TRANSPARENT_FILE_REQUEST:
1705 case TITLE_REQUEST:
1706 case COPY_FILE_REQUEST:
1707 #ifdef COLUMN
1708 case VJUSTIFY_REQUEST:
1709 #endif /* COLUMN */
1710 type = TOKEN_REQUEST;
1711 c = cc;
1712 return;
1713 case BEGIN_TRAP:
1714 type = TOKEN_BEGIN_TRAP;
1715 return;
1716 case END_TRAP:
1717 type = TOKEN_END_TRAP;
1718 return;
1719 case LAST_PAGE_EJECTOR:
1720 seen_last_page_ejector = 1;
1721 // fall through
1722 case PAGE_EJECTOR:
1723 type = TOKEN_PAGE_EJECTOR;
1724 return;
1725 case ESCAPE_PERCENT:
1726 ESCAPE_PERCENT:
1727 type = TOKEN_HYPHEN_INDICATOR;
1728 return;
1729 case ESCAPE_SPACE:
1730 ESCAPE_SPACE:
1731 type = TOKEN_UNSTRETCHABLE_SPACE;
1732 return;
1733 case ESCAPE_TILDE:
1734 ESCAPE_TILDE:
1735 type = TOKEN_STRETCHABLE_SPACE;
1736 return;
1737 case ESCAPE_COLON:
1738 ESCAPE_COLON:
1739 type = TOKEN_ZERO_WIDTH_BREAK;
1740 return;
1741 case ESCAPE_e:
1742 ESCAPE_e:
1743 type = TOKEN_ESCAPE;
1744 return;
1745 case ESCAPE_E:
1746 goto handle_escape_char;
1747 case ESCAPE_BAR:
1748 ESCAPE_BAR:
1749 type = TOKEN_NODE;
1750 nd = new hmotion_node(curenv->get_narrow_space_width(),
1751 curenv->get_fill_color());
1752 return;
1753 case ESCAPE_CIRCUMFLEX:
1754 ESCAPE_CIRCUMFLEX:
1755 type = TOKEN_NODE;
1756 nd = new hmotion_node(curenv->get_half_narrow_space_width(),
1757 curenv->get_fill_color());
1758 return;
1759 case ESCAPE_NEWLINE:
1760 have_input = 0;
1761 break;
1762 case ESCAPE_LEFT_BRACE:
1763 ESCAPE_LEFT_BRACE:
1764 type = TOKEN_LEFT_BRACE;
1765 return;
1766 case ESCAPE_RIGHT_BRACE:
1767 ESCAPE_RIGHT_BRACE:
1768 type = TOKEN_RIGHT_BRACE;
1769 return;
1770 case ESCAPE_LEFT_QUOTE:
1771 ESCAPE_LEFT_QUOTE:
1772 type = TOKEN_SPECIAL;
1773 nm = symbol("ga");
1774 return;
1775 case ESCAPE_RIGHT_QUOTE:
1776 ESCAPE_RIGHT_QUOTE:
1777 type = TOKEN_SPECIAL;
1778 nm = symbol("aa");
1779 return;
1780 case ESCAPE_HYPHEN:
1781 ESCAPE_HYPHEN:
1782 type = TOKEN_SPECIAL;
1783 nm = symbol("-");
1784 return;
1785 case ESCAPE_UNDERSCORE:
1786 ESCAPE_UNDERSCORE:
1787 type = TOKEN_SPECIAL;
1788 nm = symbol("ul");
1789 return;
1790 case ESCAPE_c:
1791 ESCAPE_c:
1792 type = TOKEN_INTERRUPT;
1793 return;
1794 case ESCAPE_BANG:
1795 ESCAPE_BANG:
1796 type = TOKEN_TRANSPARENT;
1797 return;
1798 case ESCAPE_QUESTION:
1799 ESCAPE_QUESTION:
1800 nd = do_non_interpreted();
1801 if (nd) {
1802 type = TOKEN_NODE;
1803 return;
1805 break;
1806 case ESCAPE_AMPERSAND:
1807 ESCAPE_AMPERSAND:
1808 type = TOKEN_DUMMY;
1809 return;
1810 case ESCAPE_RIGHT_PARENTHESIS:
1811 ESCAPE_RIGHT_PARENTHESIS:
1812 type = TOKEN_TRANSPARENT_DUMMY;
1813 return;
1814 case '\b':
1815 type = TOKEN_BACKSPACE;
1816 return;
1817 case ' ':
1818 type = TOKEN_SPACE;
1819 return;
1820 case '\t':
1821 type = TOKEN_TAB;
1822 return;
1823 case '\n':
1824 type = TOKEN_NEWLINE;
1825 return;
1826 case '\001':
1827 type = TOKEN_LEADER;
1828 return;
1829 case 0:
1831 assert(n != 0);
1832 token_node *tn = n->get_token_node();
1833 if (tn) {
1834 *this = tn->tk;
1835 delete tn;
1837 else {
1838 nd = n;
1839 type = TOKEN_NODE;
1842 return;
1843 default:
1844 type = TOKEN_CHAR;
1845 c = cc;
1846 return;
1849 else {
1850 handle_escape_char:
1851 cc = input_stack::get(&n);
1852 switch(cc) {
1853 case '(':
1854 nm = read_two_char_escape_name();
1855 type = TOKEN_SPECIAL;
1856 return;
1857 case EOF:
1858 type = TOKEN_EOF;
1859 error("end of input after escape character");
1860 return;
1861 case '`':
1862 goto ESCAPE_LEFT_QUOTE;
1863 case '\'':
1864 goto ESCAPE_RIGHT_QUOTE;
1865 case '-':
1866 goto ESCAPE_HYPHEN;
1867 case '_':
1868 goto ESCAPE_UNDERSCORE;
1869 case '%':
1870 goto ESCAPE_PERCENT;
1871 case ' ':
1872 goto ESCAPE_SPACE;
1873 case '0':
1874 nd = new hmotion_node(curenv->get_digit_width(),
1875 curenv->get_fill_color());
1876 type = TOKEN_NODE;
1877 return;
1878 case '|':
1879 goto ESCAPE_BAR;
1880 case '^':
1881 goto ESCAPE_CIRCUMFLEX;
1882 case '/':
1883 type = TOKEN_ITALIC_CORRECTION;
1884 return;
1885 case ',':
1886 type = TOKEN_NODE;
1887 nd = new left_italic_corrected_node;
1888 return;
1889 case '&':
1890 goto ESCAPE_AMPERSAND;
1891 case ')':
1892 goto ESCAPE_RIGHT_PARENTHESIS;
1893 case '!':
1894 goto ESCAPE_BANG;
1895 case '?':
1896 goto ESCAPE_QUESTION;
1897 case '~':
1898 goto ESCAPE_TILDE;
1899 case ':':
1900 goto ESCAPE_COLON;
1901 case '"':
1902 while ((cc = input_stack::get(0)) != '\n' && cc != EOF)
1904 if (cc == '\n')
1905 type = TOKEN_NEWLINE;
1906 else
1907 type = TOKEN_EOF;
1908 return;
1909 case '#': // Like \" but newline is ignored.
1910 while ((cc = input_stack::get(0)) != '\n')
1911 if (cc == EOF) {
1912 type = TOKEN_EOF;
1913 return;
1915 break;
1916 case '$':
1918 symbol s = read_escape_name();
1919 if (!(s.is_null() || s.is_empty()))
1920 interpolate_arg(s);
1921 break;
1923 case '*':
1925 symbol s = read_escape_name(WITH_ARGS);
1926 if (!(s.is_null() || s.is_empty())) {
1927 if (have_string_arg) {
1928 have_string_arg = 0;
1929 interpolate_string_with_args(s);
1931 else
1932 interpolate_string(s);
1934 break;
1936 case 'a':
1937 nd = new non_interpreted_char_node('\001');
1938 type = TOKEN_NODE;
1939 return;
1940 case 'A':
1941 c = '0' + do_name_test();
1942 type = TOKEN_CHAR;
1943 return;
1944 case 'b':
1945 nd = do_bracket();
1946 type = TOKEN_NODE;
1947 return;
1948 case 'B':
1949 c = '0' + do_expr_test();
1950 type = TOKEN_CHAR;
1951 return;
1952 case 'c':
1953 goto ESCAPE_c;
1954 case 'C':
1955 nm = get_delim_name();
1956 if (nm.is_null())
1957 break;
1958 type = TOKEN_SPECIAL;
1959 return;
1960 case 'd':
1961 type = TOKEN_NODE;
1962 nd = new vmotion_node(curenv->get_size() / 2,
1963 curenv->get_fill_color());
1964 return;
1965 case 'D':
1966 nd = read_draw_node();
1967 if (!nd)
1968 break;
1969 type = TOKEN_NODE;
1970 return;
1971 case 'e':
1972 goto ESCAPE_e;
1973 case 'E':
1974 goto handle_escape_char;
1975 case 'f':
1977 symbol s = read_escape_name(ALLOW_EMPTY);
1978 if (s.is_null())
1979 break;
1980 const char *p;
1981 for (p = s.contents(); *p != '\0'; p++)
1982 if (!csdigit(*p))
1983 break;
1984 if (*p || s.is_empty())
1985 curenv->set_font(s);
1986 else
1987 curenv->set_font(atoi(s.contents()));
1988 if (!compatible_flag)
1989 have_input = 1;
1990 break;
1992 case 'F':
1994 symbol s = read_escape_name(ALLOW_EMPTY);
1995 if (s.is_null())
1996 break;
1997 curenv->set_family(s);
1998 have_input = 1;
1999 break;
2001 case 'g':
2003 symbol s = read_escape_name();
2004 if (!(s.is_null() || s.is_empty()))
2005 interpolate_number_format(s);
2006 break;
2008 case 'h':
2009 if (!get_delim_number(&x, 'm'))
2010 break;
2011 type = TOKEN_NODE;
2012 nd = new hmotion_node(x, curenv->get_fill_color());
2013 return;
2014 case 'H':
2015 // don't take height increments relative to previous height if
2016 // in compatibility mode
2017 if (!compatible_flag && curenv->get_char_height())
2019 if (get_delim_number(&x, 'z', curenv->get_char_height()))
2020 curenv->set_char_height(x);
2022 else
2024 if (get_delim_number(&x, 'z', curenv->get_requested_point_size()))
2025 curenv->set_char_height(x);
2027 if (!compatible_flag)
2028 have_input = 1;
2029 break;
2030 case 'k':
2031 nm = read_escape_name();
2032 if (nm.is_null() || nm.is_empty())
2033 break;
2034 type = TOKEN_MARK_INPUT;
2035 return;
2036 case 'l':
2037 case 'L':
2039 charinfo *s = 0;
2040 if (!get_line_arg(&x, (cc == 'l' ? 'm': 'v'), &s))
2041 break;
2042 if (s == 0)
2043 s = get_charinfo(cc == 'l' ? "ru" : "br");
2044 type = TOKEN_NODE;
2045 node *char_node = curenv->make_char_node(s);
2046 if (cc == 'l')
2047 nd = new hline_node(x, char_node);
2048 else
2049 nd = new vline_node(x, char_node);
2050 return;
2052 case 'm':
2053 do_glyph_color(read_escape_name(ALLOW_EMPTY));
2054 if (!compatible_flag)
2055 have_input = 1;
2056 break;
2057 case 'M':
2058 do_fill_color(read_escape_name(ALLOW_EMPTY));
2059 if (!compatible_flag)
2060 have_input = 1;
2061 break;
2062 case 'n':
2064 int inc;
2065 symbol s = read_increment_and_escape_name(&inc);
2066 if (!(s.is_null() || s.is_empty()))
2067 interpolate_number_reg(s, inc);
2068 break;
2070 case 'N':
2071 if (!get_delim_number(&val, 0))
2072 break;
2073 type = TOKEN_NUMBERED_CHAR;
2074 return;
2075 case 'o':
2076 nd = do_overstrike();
2077 type = TOKEN_NODE;
2078 return;
2079 case 'O':
2080 nd = do_suppress(read_escape_name());
2081 if (!nd)
2082 break;
2083 type = TOKEN_NODE;
2084 return;
2085 case 'p':
2086 type = TOKEN_SPREAD;
2087 return;
2088 case 'r':
2089 type = TOKEN_NODE;
2090 nd = new vmotion_node(-curenv->get_size(), curenv->get_fill_color());
2091 return;
2092 case 'R':
2093 do_register();
2094 if (!compatible_flag)
2095 have_input = 1;
2096 break;
2097 case 's':
2098 if (read_size(&x))
2099 curenv->set_size(x);
2100 if (!compatible_flag)
2101 have_input = 1;
2102 break;
2103 case 'S':
2104 if (get_delim_number(&x, 0))
2105 curenv->set_char_slant(x);
2106 if (!compatible_flag)
2107 have_input = 1;
2108 break;
2109 case 't':
2110 type = TOKEN_NODE;
2111 nd = new non_interpreted_char_node('\t');
2112 return;
2113 case 'u':
2114 type = TOKEN_NODE;
2115 nd = new vmotion_node(-curenv->get_size() / 2,
2116 curenv->get_fill_color());
2117 return;
2118 case 'v':
2119 if (!get_delim_number(&x, 'v'))
2120 break;
2121 type = TOKEN_NODE;
2122 nd = new vmotion_node(x, curenv->get_fill_color());
2123 return;
2124 case 'V':
2126 symbol s = read_escape_name();
2127 if (!(s.is_null() || s.is_empty()))
2128 interpolate_environment_variable(s);
2129 break;
2131 case 'w':
2132 do_width();
2133 break;
2134 case 'x':
2135 if (!get_delim_number(&x, 'v'))
2136 break;
2137 type = TOKEN_NODE;
2138 nd = new extra_size_node(x);
2139 return;
2140 case 'X':
2141 nd = do_special();
2142 if (!nd)
2143 break;
2144 type = TOKEN_NODE;
2145 return;
2146 case 'Y':
2148 symbol s = read_escape_name();
2149 if (s.is_null() || s.is_empty())
2150 break;
2151 request_or_macro *p = lookup_request(s);
2152 macro *m = p->to_macro();
2153 if (!m) {
2154 error("can't transparently throughput a request");
2155 break;
2157 nd = new special_node(*m);
2158 type = TOKEN_NODE;
2159 return;
2161 case 'z':
2163 next();
2164 if (type == TOKEN_NODE)
2165 nd = new zero_width_node(nd);
2166 else {
2167 charinfo *ci = get_char(1);
2168 if (ci == 0)
2169 break;
2170 node *gn = curenv->make_char_node(ci);
2171 if (gn == 0)
2172 break;
2173 nd = new zero_width_node(gn);
2174 type = TOKEN_NODE;
2176 return;
2178 case 'Z':
2179 nd = do_zero_width();
2180 if (nd == 0)
2181 break;
2182 type = TOKEN_NODE;
2183 return;
2184 case '{':
2185 goto ESCAPE_LEFT_BRACE;
2186 case '}':
2187 goto ESCAPE_RIGHT_BRACE;
2188 case '\n':
2189 break;
2190 case '[':
2191 if (!compatible_flag) {
2192 symbol s = read_long_escape_name(WITH_ARGS);
2193 if (s.is_null() || s.is_empty())
2194 break;
2195 if (have_string_arg) {
2196 have_string_arg = 0;
2197 nm = composite_glyph_name(s);
2199 else {
2200 const char *gn = check_unicode_name(s.contents());
2201 if (gn) {
2202 const char *gn_decomposed = decompose_unicode(gn);
2203 if (gn_decomposed)
2204 gn = &gn_decomposed[1];
2205 const char *groff_gn = unicode_to_glyph_name(gn);
2206 if (groff_gn)
2207 nm = symbol(groff_gn);
2208 else {
2209 char *buf = new char[strlen(gn) + 1 + 1];
2210 strcpy(buf, "u");
2211 strcat(buf, gn);
2212 nm = symbol(buf);
2213 a_delete buf;
2216 else
2217 nm = symbol(s.contents());
2219 type = TOKEN_SPECIAL;
2220 return;
2222 goto handle_normal_char;
2223 default:
2224 if (cc != escape_char && cc != '.')
2225 warning(WARN_ESCAPE, "escape character ignored before %1",
2226 input_char_description(cc));
2227 goto handle_normal_char;
2233 int token::operator==(const token &t)
2235 if (type != t.type)
2236 return 0;
2237 switch(type) {
2238 case TOKEN_CHAR:
2239 return c == t.c;
2240 case TOKEN_SPECIAL:
2241 return nm == t.nm;
2242 case TOKEN_NUMBERED_CHAR:
2243 return val == t.val;
2244 default:
2245 return 1;
2249 int token::operator!=(const token &t)
2251 return !(*this == t);
2254 // is token a suitable delimiter (like ')?
2256 int token::delimiter(int err)
2258 switch(type) {
2259 case TOKEN_CHAR:
2260 switch(c) {
2261 case '0':
2262 case '1':
2263 case '2':
2264 case '3':
2265 case '4':
2266 case '5':
2267 case '6':
2268 case '7':
2269 case '8':
2270 case '9':
2271 case '+':
2272 case '-':
2273 case '/':
2274 case '*':
2275 case '%':
2276 case '<':
2277 case '>':
2278 case '=':
2279 case '&':
2280 case ':':
2281 case '(':
2282 case ')':
2283 case '.':
2284 if (err)
2285 error("cannot use character `%1' as a starting delimiter", char(c));
2286 return 0;
2287 default:
2288 return 1;
2290 case TOKEN_NODE:
2291 case TOKEN_SPACE:
2292 case TOKEN_STRETCHABLE_SPACE:
2293 case TOKEN_UNSTRETCHABLE_SPACE:
2294 case TOKEN_TAB:
2295 case TOKEN_NEWLINE:
2296 if (err)
2297 error("cannot use %1 as a starting delimiter", description());
2298 return 0;
2299 default:
2300 return 1;
2304 const char *token::description()
2306 static char buf[4];
2307 switch (type) {
2308 case TOKEN_BACKSPACE:
2309 return "a backspace character";
2310 case TOKEN_CHAR:
2311 buf[0] = '`';
2312 buf[1] = c;
2313 buf[2] = '\'';
2314 buf[3] = '\0';
2315 return buf;
2316 case TOKEN_DUMMY:
2317 return "`\\&'";
2318 case TOKEN_ESCAPE:
2319 return "`\\e'";
2320 case TOKEN_HYPHEN_INDICATOR:
2321 return "`\\%'";
2322 case TOKEN_INTERRUPT:
2323 return "`\\c'";
2324 case TOKEN_ITALIC_CORRECTION:
2325 return "`\\/'";
2326 case TOKEN_LEADER:
2327 return "a leader character";
2328 case TOKEN_LEFT_BRACE:
2329 return "`\\{'";
2330 case TOKEN_MARK_INPUT:
2331 return "`\\k'";
2332 case TOKEN_NEWLINE:
2333 return "newline";
2334 case TOKEN_NODE:
2335 return "a node";
2336 case TOKEN_NUMBERED_CHAR:
2337 return "`\\N'";
2338 case TOKEN_RIGHT_BRACE:
2339 return "`\\}'";
2340 case TOKEN_SPACE:
2341 return "a space";
2342 case TOKEN_SPECIAL:
2343 return "a special character";
2344 case TOKEN_SPREAD:
2345 return "`\\p'";
2346 case TOKEN_STRETCHABLE_SPACE:
2347 return "`\\~'";
2348 case TOKEN_UNSTRETCHABLE_SPACE:
2349 return "`\\ '";
2350 case TOKEN_TAB:
2351 return "a tab character";
2352 case TOKEN_TRANSPARENT:
2353 return "`\\!'";
2354 case TOKEN_TRANSPARENT_DUMMY:
2355 return "`\\)'";
2356 case TOKEN_ZERO_WIDTH_BREAK:
2357 return "`\\:'";
2358 case TOKEN_EOF:
2359 return "end of input";
2360 default:
2361 break;
2363 return "a magic token";
2366 void skip_line()
2368 while (!tok.newline())
2369 if (tok.eof())
2370 return;
2371 else
2372 tok.next();
2373 tok.next();
2376 void compatible()
2378 int n;
2379 if (has_arg() && get_integer(&n))
2380 compatible_flag = n != 0;
2381 else
2382 compatible_flag = 1;
2383 skip_line();
2386 static void empty_name_warning(int required)
2388 if (tok.newline() || tok.eof()) {
2389 if (required)
2390 warning(WARN_MISSING, "missing name");
2392 else if (tok.right_brace() || tok.tab()) {
2393 const char *start = tok.description();
2394 do {
2395 tok.next();
2396 } while (tok.space() || tok.right_brace() || tok.tab());
2397 if (!tok.newline() && !tok.eof())
2398 error("%1 is not allowed before an argument", start);
2399 else if (required)
2400 warning(WARN_MISSING, "missing name");
2402 else if (required)
2403 error("name expected (got %1)", tok.description());
2404 else
2405 error("name expected (got %1): treated as missing", tok.description());
2408 static void non_empty_name_warning()
2410 if (!tok.newline() && !tok.eof() && !tok.space() && !tok.tab()
2411 && !tok.right_brace()
2412 // We don't want to give a warning for .el\{
2413 && !tok.left_brace())
2414 error("%1 is not allowed in a name", tok.description());
2417 symbol get_name(int required)
2419 if (compatible_flag) {
2420 char buf[3];
2421 tok.skip();
2422 if ((buf[0] = tok.ch()) != 0) {
2423 tok.next();
2424 if ((buf[1] = tok.ch()) != 0) {
2425 buf[2] = 0;
2426 tok.make_space();
2428 else
2429 non_empty_name_warning();
2430 return symbol(buf);
2432 else {
2433 empty_name_warning(required);
2434 return NULL_SYMBOL;
2437 else
2438 return get_long_name(required);
2441 symbol get_long_name(int required)
2443 return do_get_long_name(required, 0);
2446 static symbol do_get_long_name(int required, char end)
2448 while (tok.space())
2449 tok.next();
2450 char abuf[ABUF_SIZE];
2451 char *buf = abuf;
2452 int buf_size = ABUF_SIZE;
2453 int i = 0;
2454 for (;;) {
2455 // If end != 0 we normally have to append a null byte
2456 if (i + 2 > buf_size) {
2457 if (buf == abuf) {
2458 buf = new char[ABUF_SIZE*2];
2459 memcpy(buf, abuf, buf_size);
2460 buf_size = ABUF_SIZE*2;
2462 else {
2463 char *old_buf = buf;
2464 buf = new char[buf_size*2];
2465 memcpy(buf, old_buf, buf_size);
2466 buf_size *= 2;
2467 a_delete old_buf;
2470 if ((buf[i] = tok.ch()) == 0 || buf[i] == end)
2471 break;
2472 i++;
2473 tok.next();
2475 if (i == 0) {
2476 empty_name_warning(required);
2477 return NULL_SYMBOL;
2479 if (end && buf[i] == end)
2480 buf[i+1] = '\0';
2481 else
2482 non_empty_name_warning();
2483 if (buf == abuf)
2484 return symbol(buf);
2485 else {
2486 symbol s(buf);
2487 a_delete buf;
2488 return s;
2492 void exit_troff()
2494 exit_started = 1;
2495 topdiv->set_last_page();
2496 if (!end_macro_name.is_null()) {
2497 spring_trap(end_macro_name);
2498 tok.next();
2499 process_input_stack();
2501 curenv->final_break();
2502 tok.next();
2503 process_input_stack();
2504 end_diversions();
2505 if (topdiv->get_page_length() > 0) {
2506 done_end_macro = 1;
2507 topdiv->set_ejecting();
2508 static unsigned char buf[2] = { LAST_PAGE_EJECTOR, '\0' };
2509 input_stack::push(make_temp_iterator((char *)buf));
2510 topdiv->space(topdiv->get_page_length(), 1);
2511 tok.next();
2512 process_input_stack();
2513 seen_last_page_ejector = 1; // should be set already
2514 topdiv->set_ejecting();
2515 push_page_ejector();
2516 topdiv->space(topdiv->get_page_length(), 1);
2517 tok.next();
2518 process_input_stack();
2520 // This will only happen if a trap-invoked macro starts a diversion,
2521 // or if vertical position traps have been disabled.
2522 cleanup_and_exit(0);
2525 // This implements .ex. The input stack must be cleared before calling
2526 // exit_troff().
2528 void exit_request()
2530 input_stack::clear();
2531 if (exit_started)
2532 tok.next();
2533 else
2534 exit_troff();
2537 void return_macro_request()
2539 if (has_arg() && tok.ch())
2540 input_stack::pop_macro();
2541 input_stack::pop_macro();
2542 tok.next();
2545 void end_macro()
2547 end_macro_name = get_name();
2548 skip_line();
2551 void blank_line_macro()
2553 blank_line_macro_name = get_name();
2554 skip_line();
2557 static void trapping_blank_line()
2559 if (!blank_line_macro_name.is_null())
2560 spring_trap(blank_line_macro_name);
2561 else
2562 blank_line();
2565 void do_request()
2567 int old_compatible_flag = compatible_flag;
2568 compatible_flag = 0;
2569 symbol nm = get_name();
2570 if (nm.is_null())
2571 skip_line();
2572 else
2573 interpolate_macro(nm);
2574 compatible_flag = old_compatible_flag;
2577 inline int possibly_handle_first_page_transition()
2579 if (topdiv->before_first_page && curdiv == topdiv && !curenv->is_dummy()) {
2580 handle_first_page_transition();
2581 return 1;
2583 else
2584 return 0;
2587 static int transparent_translate(int cc)
2589 if (!invalid_input_char(cc)) {
2590 charinfo *ci = charset_table[cc];
2591 switch (ci->get_special_translation(1)) {
2592 case charinfo::TRANSLATE_SPACE:
2593 return ' ';
2594 case charinfo::TRANSLATE_STRETCHABLE_SPACE:
2595 return ESCAPE_TILDE;
2596 case charinfo::TRANSLATE_DUMMY:
2597 return ESCAPE_AMPERSAND;
2598 case charinfo::TRANSLATE_HYPHEN_INDICATOR:
2599 return ESCAPE_PERCENT;
2601 // This is really ugly.
2602 ci = ci->get_translation(1);
2603 if (ci) {
2604 int c = ci->get_ascii_code();
2605 if (c != '\0')
2606 return c;
2607 error("can't translate %1 to special character `%2'"
2608 " in transparent throughput",
2609 input_char_description(cc),
2610 ci->nm.contents());
2613 return cc;
2616 class int_stack {
2617 struct int_stack_element {
2618 int n;
2619 int_stack_element *next;
2620 } *top;
2621 public:
2622 int_stack();
2623 ~int_stack();
2624 void push(int);
2625 int is_empty();
2626 int pop();
2629 int_stack::int_stack()
2631 top = 0;
2634 int_stack::~int_stack()
2636 while (top != 0) {
2637 int_stack_element *temp = top;
2638 top = top->next;
2639 delete temp;
2643 int int_stack::is_empty()
2645 return top == 0;
2648 void int_stack::push(int n)
2650 int_stack_element *p = new int_stack_element;
2651 p->next = top;
2652 p->n = n;
2653 top = p;
2656 int int_stack::pop()
2658 assert(top != 0);
2659 int_stack_element *p = top;
2660 top = top->next;
2661 int n = p->n;
2662 delete p;
2663 return n;
2666 int node::reread(int *)
2668 return 0;
2671 int global_diverted_space = 0;
2673 int diverted_space_node::reread(int *bolp)
2675 global_diverted_space = 1;
2676 if (curenv->get_fill())
2677 trapping_blank_line();
2678 else
2679 curdiv->space(n);
2680 global_diverted_space = 0;
2681 *bolp = 1;
2682 return 1;
2685 int diverted_copy_file_node::reread(int *bolp)
2687 curdiv->copy_file(filename.contents());
2688 *bolp = 1;
2689 return 1;
2692 int word_space_node::reread(int *)
2694 if (unformat) {
2695 for (width_list *w = orig_width; w; w = w->next)
2696 curenv->space(w->width, w->sentence_width);
2697 unformat = 0;
2698 return 1;
2700 return 0;
2703 int unbreakable_space_node::reread(int *)
2705 return 0;
2708 int hmotion_node::reread(int *)
2710 if (unformat && was_tab) {
2711 curenv->handle_tab(0);
2712 unformat = 0;
2713 return 1;
2715 return 0;
2718 void process_input_stack()
2720 int_stack trap_bol_stack;
2721 int bol = 1;
2722 for (;;) {
2723 int suppress_next = 0;
2724 switch (tok.type) {
2725 case token::TOKEN_CHAR:
2727 unsigned char ch = tok.c;
2728 if (bol && !have_input
2729 && (ch == curenv->control_char
2730 || ch == curenv->no_break_control_char)) {
2731 break_flag = ch == curenv->control_char;
2732 // skip tabs as well as spaces here
2733 do {
2734 tok.next();
2735 } while (tok.white_space());
2736 symbol nm = get_name();
2737 #if defined(DEBUGGING)
2738 if (debug_state) {
2739 if (! nm.is_null()) {
2740 if (strcmp(nm.contents(), "test") == 0) {
2741 fprintf(stderr, "found it!\n");
2742 fflush(stderr);
2744 fprintf(stderr, "interpreting [%s]", nm.contents());
2745 if (strcmp(nm.contents(), "di") == 0 && topdiv != curdiv)
2746 fprintf(stderr, " currently in diversion: %s",
2747 curdiv->get_diversion_name());
2748 fprintf(stderr, "\n");
2749 fflush(stderr);
2752 #endif
2753 if (nm.is_null())
2754 skip_line();
2755 else {
2756 interpolate_macro(nm);
2757 #if defined(DEBUGGING)
2758 if (debug_state) {
2759 fprintf(stderr, "finished interpreting [%s] and environment state is\n", nm.contents());
2760 curenv->dump_troff_state();
2762 #endif
2764 suppress_next = 1;
2766 else {
2767 if (possibly_handle_first_page_transition())
2769 else {
2770 for (;;) {
2771 #if defined(DEBUGGING)
2772 if (debug_state) {
2773 fprintf(stderr, "found [%c]\n", ch); fflush(stderr);
2775 #endif
2776 curenv->add_char(charset_table[ch]);
2777 tok.next();
2778 if (tok.type != token::TOKEN_CHAR)
2779 break;
2780 ch = tok.c;
2782 suppress_next = 1;
2783 bol = 0;
2786 break;
2788 case token::TOKEN_TRANSPARENT:
2790 if (bol) {
2791 if (possibly_handle_first_page_transition())
2793 else {
2794 int cc;
2795 do {
2796 node *n;
2797 cc = get_copy(&n);
2798 if (cc != EOF)
2799 if (cc != '\0')
2800 curdiv->transparent_output(transparent_translate(cc));
2801 else
2802 curdiv->transparent_output(n);
2803 } while (cc != '\n' && cc != EOF);
2804 if (cc == EOF)
2805 curdiv->transparent_output('\n');
2808 break;
2810 case token::TOKEN_NEWLINE:
2812 if (bol && !old_have_input
2813 && !curenv->get_prev_line_interrupted())
2814 trapping_blank_line();
2815 else {
2816 curenv->newline();
2817 bol = 1;
2819 break;
2821 case token::TOKEN_REQUEST:
2823 int request_code = tok.c;
2824 tok.next();
2825 switch (request_code) {
2826 case TITLE_REQUEST:
2827 title();
2828 break;
2829 case COPY_FILE_REQUEST:
2830 copy_file();
2831 break;
2832 case TRANSPARENT_FILE_REQUEST:
2833 transparent_file();
2834 break;
2835 #ifdef COLUMN
2836 case VJUSTIFY_REQUEST:
2837 vjustify();
2838 break;
2839 #endif /* COLUMN */
2840 default:
2841 assert(0);
2842 break;
2844 suppress_next = 1;
2845 break;
2847 case token::TOKEN_SPACE:
2849 if (possibly_handle_first_page_transition())
2851 else if (bol && !curenv->get_prev_line_interrupted()) {
2852 int nspaces = 0;
2853 // save space_width now so that it isn't changed by \f or \s
2854 // which we wouldn't notice here
2855 hunits space_width = curenv->get_space_width();
2856 do {
2857 nspaces += tok.nspaces();
2858 tok.next();
2859 } while (tok.space());
2860 if (tok.newline())
2861 trapping_blank_line();
2862 else {
2863 push_token(tok);
2864 curenv->do_break();
2865 curenv->add_node(new hmotion_node(space_width * nspaces,
2866 curenv->get_fill_color()));
2867 bol = 0;
2870 else {
2871 curenv->space();
2872 bol = 0;
2874 break;
2876 case token::TOKEN_EOF:
2877 return;
2878 case token::TOKEN_NODE:
2880 if (possibly_handle_first_page_transition())
2882 else if (tok.nd->reread(&bol)) {
2883 delete tok.nd;
2884 tok.nd = 0;
2886 else {
2887 curenv->add_node(tok.nd);
2888 tok.nd = 0;
2889 bol = 0;
2890 curenv->possibly_break_line(1);
2892 break;
2894 case token::TOKEN_PAGE_EJECTOR:
2896 continue_page_eject();
2897 // I think we just want to preserve bol.
2898 // bol = 1;
2899 break;
2901 case token::TOKEN_BEGIN_TRAP:
2903 trap_bol_stack.push(bol);
2904 bol = 1;
2905 have_input = 0;
2906 break;
2908 case token::TOKEN_END_TRAP:
2910 if (trap_bol_stack.is_empty())
2911 error("spurious end trap token detected!");
2912 else
2913 bol = trap_bol_stack.pop();
2914 have_input = 0;
2916 /* I'm not totally happy about this. But I can't think of any other
2917 way to do it. Doing an output_pending_lines() whenever a
2918 TOKEN_END_TRAP is detected doesn't work: for example,
2920 .wh -1i x
2921 .de x
2924 .wh -.5i y
2925 .de y
2926 .tl ''-%-''
2929 .ll .5i
2930 .sp |\n(.pu-1i-.5v
2931 a\%very\%very\%long\%word
2933 will print all but the first lines from the word immediately
2934 after the footer, rather than on the next page. */
2936 if (trap_bol_stack.is_empty())
2937 curenv->output_pending_lines();
2938 break;
2940 default:
2942 bol = 0;
2943 tok.process();
2944 break;
2947 if (!suppress_next)
2948 tok.next();
2949 trap_sprung_flag = 0;
2953 #ifdef WIDOW_CONTROL
2955 void flush_pending_lines()
2957 while (!tok.newline() && !tok.eof())
2958 tok.next();
2959 curenv->output_pending_lines();
2960 tok.next();
2963 #endif /* WIDOW_CONTROL */
2965 request_or_macro::request_or_macro()
2969 macro *request_or_macro::to_macro()
2971 return 0;
2974 request::request(REQUEST_FUNCP pp) : p(pp)
2978 void request::invoke(symbol)
2980 (*p)();
2983 struct char_block {
2984 enum { SIZE = 128 };
2985 unsigned char s[SIZE];
2986 char_block *next;
2987 char_block();
2990 char_block::char_block()
2991 : next(0)
2995 class char_list {
2996 public:
2997 char_list();
2998 ~char_list();
2999 void append(unsigned char);
3000 void set(unsigned char, int);
3001 unsigned char get(int);
3002 int length();
3003 private:
3004 unsigned char *ptr;
3005 int len;
3006 char_block *head;
3007 char_block *tail;
3008 friend class macro_header;
3009 friend class string_iterator;
3012 char_list::char_list()
3013 : ptr(0), len(0), head(0), tail(0)
3017 char_list::~char_list()
3019 while (head != 0) {
3020 char_block *tem = head;
3021 head = head->next;
3022 delete tem;
3026 int char_list::length()
3028 return len;
3031 void char_list::append(unsigned char c)
3033 if (tail == 0) {
3034 head = tail = new char_block;
3035 ptr = tail->s;
3037 else {
3038 if (ptr >= tail->s + char_block::SIZE) {
3039 tail->next = new char_block;
3040 tail = tail->next;
3041 ptr = tail->s;
3044 *ptr++ = c;
3045 len++;
3048 void char_list::set(unsigned char c, int offset)
3050 assert(len > offset);
3051 // optimization for access at the end
3052 int boundary = len - len % char_block::SIZE;
3053 if (offset >= boundary) {
3054 *(tail->s + offset - boundary) = c;
3055 return;
3057 char_block *tem = head;
3058 int l = 0;
3059 for (;;) {
3060 l += char_block::SIZE;
3061 if (l > offset) {
3062 *(tem->s + offset % char_block::SIZE) = c;
3063 return;
3065 tem = tem->next;
3069 unsigned char char_list::get(int offset)
3071 assert(len > offset);
3072 // optimization for access at the end
3073 int boundary = len - len % char_block::SIZE;
3074 if (offset >= boundary)
3075 return *(tail->s + offset - boundary);
3076 char_block *tem = head;
3077 int l = 0;
3078 for (;;) {
3079 l += char_block::SIZE;
3080 if (l > offset)
3081 return *(tem->s + offset % char_block::SIZE);
3082 tem = tem->next;
3086 class node_list {
3087 node *head;
3088 node *tail;
3089 public:
3090 node_list();
3091 ~node_list();
3092 void append(node *);
3093 int length();
3094 node *extract();
3096 friend class macro_header;
3097 friend class string_iterator;
3100 void node_list::append(node *n)
3102 if (head == 0) {
3103 n->next = 0;
3104 head = tail = n;
3106 else {
3107 n->next = 0;
3108 tail = tail->next = n;
3112 int node_list::length()
3114 int total = 0;
3115 for (node *n = head; n != 0; n = n->next)
3116 ++total;
3117 return total;
3120 node_list::node_list()
3122 head = tail = 0;
3125 node *node_list::extract()
3127 node *temp = head;
3128 head = tail = 0;
3129 return temp;
3132 node_list::~node_list()
3134 delete_node_list(head);
3137 class macro_header {
3138 public:
3139 int count;
3140 char_list cl;
3141 node_list nl;
3142 macro_header() { count = 1; }
3143 macro_header *copy(int);
3146 macro::~macro()
3148 if (p != 0 && --(p->count) <= 0)
3149 delete p;
3152 macro::macro()
3153 : is_a_diversion(0)
3155 if (!input_stack::get_location(1, &filename, &lineno)) {
3156 filename = 0;
3157 lineno = 0;
3159 len = 0;
3160 empty_macro = 1;
3161 p = 0;
3164 macro::macro(const macro &m)
3165 : filename(m.filename), lineno(m.lineno), len(m.len),
3166 empty_macro(m.empty_macro), is_a_diversion(m.is_a_diversion), p(m.p)
3168 if (p != 0)
3169 p->count++;
3172 macro::macro(int is_div)
3173 : is_a_diversion(is_div)
3175 if (!input_stack::get_location(1, &filename, &lineno)) {
3176 filename = 0;
3177 lineno = 0;
3179 len = 0;
3180 empty_macro = 1;
3181 p = 0;
3184 int macro::is_diversion()
3186 return is_a_diversion;
3189 macro &macro::operator=(const macro &m)
3191 // don't assign object
3192 if (m.p != 0)
3193 m.p->count++;
3194 if (p != 0 && --(p->count) <= 0)
3195 delete p;
3196 p = m.p;
3197 filename = m.filename;
3198 lineno = m.lineno;
3199 len = m.len;
3200 empty_macro = m.empty_macro;
3201 is_a_diversion = m.is_a_diversion;
3202 return *this;
3205 void macro::append(unsigned char c)
3207 assert(c != 0);
3208 if (p == 0)
3209 p = new macro_header;
3210 if (p->cl.length() != len) {
3211 macro_header *tem = p->copy(len);
3212 if (--(p->count) <= 0)
3213 delete p;
3214 p = tem;
3216 p->cl.append(c);
3217 ++len;
3218 if (c != PUSH_GROFF_MODE && c != PUSH_COMP_MODE && c != POP_GROFFCOMP_MODE)
3219 empty_macro = 0;
3222 void macro::set(unsigned char c, int offset)
3224 assert(p != 0);
3225 assert(c != 0);
3226 p->cl.set(c, offset);
3229 unsigned char macro::get(int offset)
3231 assert(p != 0);
3232 return p->cl.get(offset);
3235 int macro::length()
3237 return len;
3240 void macro::append_str(const char *s)
3242 int i = 0;
3244 if (s) {
3245 while (s[i] != (char)0) {
3246 append(s[i]);
3247 i++;
3252 void macro::append(node *n)
3254 assert(n != 0);
3255 if (p == 0)
3256 p = new macro_header;
3257 if (p->cl.length() != len) {
3258 macro_header *tem = p->copy(len);
3259 if (--(p->count) <= 0)
3260 delete p;
3261 p = tem;
3263 p->cl.append(0);
3264 p->nl.append(n);
3265 ++len;
3266 empty_macro = 0;
3269 void macro::append_unsigned(unsigned int i)
3271 unsigned int j = i / 10;
3272 if (j != 0)
3273 append_unsigned(j);
3274 append(((unsigned char)(((int)'0') + i % 10)));
3277 void macro::append_int(int i)
3279 if (i < 0) {
3280 append('-');
3281 i = -i;
3283 append_unsigned((unsigned int)i);
3286 void macro::print_size()
3288 errprint("%1", len);
3291 // make a copy of the first n bytes
3293 macro_header *macro_header::copy(int n)
3295 macro_header *p = new macro_header;
3296 char_block *bp = cl.head;
3297 unsigned char *ptr = bp->s;
3298 node *nd = nl.head;
3299 while (--n >= 0) {
3300 if (ptr >= bp->s + char_block::SIZE) {
3301 bp = bp->next;
3302 ptr = bp->s;
3304 unsigned char c = *ptr++;
3305 p->cl.append(c);
3306 if (c == 0) {
3307 p->nl.append(nd->copy());
3308 nd = nd->next;
3311 return p;
3314 void print_macros()
3316 object_dictionary_iterator iter(request_dictionary);
3317 request_or_macro *rm;
3318 symbol s;
3319 while (iter.get(&s, (object **)&rm)) {
3320 assert(!s.is_null());
3321 macro *m = rm->to_macro();
3322 if (m) {
3323 errprint("%1\t", s.contents());
3324 m->print_size();
3325 errprint("\n");
3328 fflush(stderr);
3329 skip_line();
3332 class string_iterator : public input_iterator {
3333 macro mac;
3334 const char *how_invoked;
3335 int newline_flag;
3336 int lineno;
3337 char_block *bp;
3338 int count; // of characters remaining
3339 node *nd;
3340 int saved_compatible_flag;
3341 protected:
3342 symbol nm;
3343 string_iterator();
3344 public:
3345 string_iterator(const macro &m, const char *p = 0, symbol s = NULL_SYMBOL);
3346 int fill(node **);
3347 int peek();
3348 int get_location(int, const char **, int *);
3349 void backtrace();
3350 void save_compatible_flag(int f) { saved_compatible_flag = f; }
3351 int get_compatible_flag() { return saved_compatible_flag; }
3352 int is_diversion();
3355 string_iterator::string_iterator(const macro &m, const char *p, symbol s)
3356 : input_iterator(m.is_a_diversion), mac(m), how_invoked(p), newline_flag(0),
3357 lineno(1), nm(s)
3359 count = mac.len;
3360 if (count != 0) {
3361 bp = mac.p->cl.head;
3362 nd = mac.p->nl.head;
3363 ptr = eptr = bp->s;
3365 else {
3366 bp = 0;
3367 nd = 0;
3368 ptr = eptr = 0;
3372 string_iterator::string_iterator()
3374 bp = 0;
3375 nd = 0;
3376 ptr = eptr = 0;
3377 newline_flag = 0;
3378 how_invoked = 0;
3379 lineno = 1;
3380 count = 0;
3383 int string_iterator::is_diversion()
3385 return mac.is_diversion();
3388 int string_iterator::fill(node **np)
3390 if (newline_flag)
3391 lineno++;
3392 newline_flag = 0;
3393 if (count <= 0)
3394 return EOF;
3395 const unsigned char *p = eptr;
3396 if (p >= bp->s + char_block::SIZE) {
3397 bp = bp->next;
3398 p = bp->s;
3400 if (*p == '\0') {
3401 if (np) {
3402 *np = nd->copy();
3403 if (is_diversion())
3404 (*np)->div_nest_level = input_stack::get_div_level();
3405 else
3406 (*np)->div_nest_level = 0;
3408 nd = nd->next;
3409 eptr = ptr = p + 1;
3410 count--;
3411 return 0;
3413 const unsigned char *e = bp->s + char_block::SIZE;
3414 if (e - p > count)
3415 e = p + count;
3416 ptr = p;
3417 while (p < e) {
3418 unsigned char c = *p;
3419 if (c == '\n' || c == ESCAPE_NEWLINE) {
3420 newline_flag = 1;
3421 p++;
3422 break;
3424 if (c == '\0')
3425 break;
3426 p++;
3428 eptr = p;
3429 count -= p - ptr;
3430 return *ptr++;
3433 int string_iterator::peek()
3435 if (count <= 0)
3436 return EOF;
3437 const unsigned char *p = eptr;
3438 if (p >= bp->s + char_block::SIZE) {
3439 p = bp->next->s;
3441 return *p;
3444 int string_iterator::get_location(int allow_macro,
3445 const char **filep, int *linep)
3447 if (!allow_macro)
3448 return 0;
3449 if (mac.filename == 0)
3450 return 0;
3451 *filep = mac.filename;
3452 *linep = mac.lineno + lineno - 1;
3453 return 1;
3456 void string_iterator::backtrace()
3458 if (mac.filename) {
3459 errprint("%1:%2: backtrace", mac.filename, mac.lineno + lineno - 1);
3460 if (how_invoked) {
3461 if (!nm.is_null())
3462 errprint(": %1 `%2'\n", how_invoked, nm.contents());
3463 else
3464 errprint(": %1\n", how_invoked);
3466 else
3467 errprint("\n");
3471 class temp_iterator : public input_iterator {
3472 unsigned char *base;
3473 temp_iterator(const char *, int len);
3474 public:
3475 ~temp_iterator();
3476 friend input_iterator *make_temp_iterator(const char *);
3479 #ifdef __GNUG__
3480 inline
3481 #endif
3482 temp_iterator::temp_iterator(const char *s, int len)
3484 base = new unsigned char[len];
3485 memcpy(base, s, len);
3486 ptr = base;
3487 eptr = base + len;
3490 temp_iterator::~temp_iterator()
3492 a_delete base;
3495 class small_temp_iterator : public input_iterator {
3496 private:
3497 small_temp_iterator(const char *, int);
3498 ~small_temp_iterator();
3499 enum { BLOCK = 16 };
3500 static small_temp_iterator *free_list;
3501 void *operator new(size_t);
3502 void operator delete(void *);
3503 enum { SIZE = 12 };
3504 unsigned char buf[SIZE];
3505 friend input_iterator *make_temp_iterator(const char *);
3508 small_temp_iterator *small_temp_iterator::free_list = 0;
3510 void *small_temp_iterator::operator new(size_t n)
3512 assert(n == sizeof(small_temp_iterator));
3513 if (!free_list) {
3514 free_list =
3515 (small_temp_iterator *)new char[sizeof(small_temp_iterator)*BLOCK];
3516 for (int i = 0; i < BLOCK - 1; i++)
3517 free_list[i].next = free_list + i + 1;
3518 free_list[BLOCK-1].next = 0;
3520 small_temp_iterator *p = free_list;
3521 free_list = (small_temp_iterator *)(free_list->next);
3522 p->next = 0;
3523 return p;
3526 #ifdef __GNUG__
3527 inline
3528 #endif
3529 void small_temp_iterator::operator delete(void *p)
3531 if (p) {
3532 ((small_temp_iterator *)p)->next = free_list;
3533 free_list = (small_temp_iterator *)p;
3537 small_temp_iterator::~small_temp_iterator()
3541 #ifdef __GNUG__
3542 inline
3543 #endif
3544 small_temp_iterator::small_temp_iterator(const char *s, int len)
3546 for (int i = 0; i < len; i++)
3547 buf[i] = s[i];
3548 ptr = buf;
3549 eptr = buf + len;
3552 input_iterator *make_temp_iterator(const char *s)
3554 if (s == 0)
3555 return new small_temp_iterator(s, 0);
3556 else {
3557 int n = strlen(s);
3558 if (n <= small_temp_iterator::SIZE)
3559 return new small_temp_iterator(s, n);
3560 else
3561 return new temp_iterator(s, n);
3565 // this is used when macros with arguments are interpolated
3567 struct arg_list {
3568 macro mac;
3569 arg_list *next;
3570 arg_list(const macro &);
3571 ~arg_list();
3574 arg_list::arg_list(const macro &m) : mac(m), next(0)
3578 arg_list::~arg_list()
3582 class macro_iterator : public string_iterator {
3583 arg_list *args;
3584 int argc;
3585 public:
3586 macro_iterator(symbol, macro &, const char *how_invoked = "macro");
3587 macro_iterator();
3588 ~macro_iterator();
3589 int has_args() { return 1; }
3590 input_iterator *get_arg(int i);
3591 int nargs() { return argc; }
3592 void add_arg(const macro &m);
3593 void shift(int n);
3594 int is_macro() { return 1; }
3595 int is_diversion();
3598 input_iterator *macro_iterator::get_arg(int i)
3600 if (i == 0)
3601 return make_temp_iterator(nm.contents());
3602 if (i > 0 && i <= argc) {
3603 arg_list *p = args;
3604 for (int j = 1; j < i; j++) {
3605 assert(p != 0);
3606 p = p->next;
3608 return new string_iterator(p->mac);
3610 else
3611 return 0;
3614 void macro_iterator::add_arg(const macro &m)
3616 arg_list **p;
3617 for (p = &args; *p; p = &((*p)->next))
3619 *p = new arg_list(m);
3620 ++argc;
3623 void macro_iterator::shift(int n)
3625 while (n > 0 && argc > 0) {
3626 arg_list *tem = args;
3627 args = args->next;
3628 delete tem;
3629 --argc;
3630 --n;
3634 // This gets used by eg .if '\?xxx\?''.
3636 int operator==(const macro &m1, const macro &m2)
3638 if (m1.len != m2.len)
3639 return 0;
3640 string_iterator iter1(m1);
3641 string_iterator iter2(m2);
3642 int n = m1.len;
3643 while (--n >= 0) {
3644 node *nd1 = 0;
3645 int c1 = iter1.get(&nd1);
3646 assert(c1 != EOF);
3647 node *nd2 = 0;
3648 int c2 = iter2.get(&nd2);
3649 assert(c2 != EOF);
3650 if (c1 != c2) {
3651 if (c1 == 0)
3652 delete nd1;
3653 else if (c2 == 0)
3654 delete nd2;
3655 return 0;
3657 if (c1 == 0) {
3658 assert(nd1 != 0);
3659 assert(nd2 != 0);
3660 int are_same = nd1->type() == nd2->type() && nd1->same(nd2);
3661 delete nd1;
3662 delete nd2;
3663 if (!are_same)
3664 return 0;
3667 return 1;
3670 static void interpolate_macro(symbol nm)
3672 request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm);
3673 if (p == 0) {
3674 int warned = 0;
3675 const char *s = nm.contents();
3676 if (strlen(s) > 2) {
3677 request_or_macro *r;
3678 char buf[3];
3679 buf[0] = s[0];
3680 buf[1] = s[1];
3681 buf[2] = '\0';
3682 r = (request_or_macro *)request_dictionary.lookup(symbol(buf));
3683 if (r) {
3684 macro *m = r->to_macro();
3685 if (!m || !m->empty())
3686 warned = warning(WARN_SPACE,
3687 "macro `%1' not defined "
3688 "(probably missing space after `%2')",
3689 nm.contents(), buf);
3692 if (!warned) {
3693 warning(WARN_MAC, "macro `%1' not defined", nm.contents());
3694 p = new macro;
3695 request_dictionary.define(nm, p);
3698 if (p)
3699 p->invoke(nm);
3700 else {
3701 skip_line();
3702 return;
3706 static void decode_args(macro_iterator *mi)
3708 if (!tok.newline() && !tok.eof()) {
3709 node *n;
3710 int c = get_copy(&n);
3711 for (;;) {
3712 while (c == ' ')
3713 c = get_copy(&n);
3714 if (c == '\n' || c == EOF)
3715 break;
3716 macro arg;
3717 int quote_input_level = 0;
3718 int done_tab_warning = 0;
3719 if (c == '"') {
3720 quote_input_level = input_stack::get_level();
3721 c = get_copy(&n);
3723 arg.append(compatible_flag ? PUSH_COMP_MODE : PUSH_GROFF_MODE);
3724 while (c != EOF && c != '\n' && !(c == ' ' && quote_input_level == 0)) {
3725 if (quote_input_level > 0 && c == '"'
3726 && (compatible_flag
3727 || input_stack::get_level() == quote_input_level)) {
3728 c = get_copy(&n);
3729 if (c == '"') {
3730 arg.append(c);
3731 c = get_copy(&n);
3733 else
3734 break;
3736 else {
3737 if (c == 0)
3738 arg.append(n);
3739 else {
3740 if (c == '\t' && quote_input_level == 0 && !done_tab_warning) {
3741 warning(WARN_TAB, "tab character in unquoted macro argument");
3742 done_tab_warning = 1;
3744 arg.append(c);
3746 c = get_copy(&n);
3749 arg.append(POP_GROFFCOMP_MODE);
3750 mi->add_arg(arg);
3755 static void decode_string_args(macro_iterator *mi)
3757 node *n;
3758 int c = get_copy(&n);
3759 for (;;) {
3760 while (c == ' ')
3761 c = get_copy(&n);
3762 if (c == '\n' || c == EOF) {
3763 error("missing `]'");
3764 break;
3766 if (c == ']')
3767 break;
3768 macro arg;
3769 int quote_input_level = 0;
3770 int done_tab_warning = 0;
3771 if (c == '"') {
3772 quote_input_level = input_stack::get_level();
3773 c = get_copy(&n);
3775 while (c != EOF && c != '\n'
3776 && !(c == ']' && quote_input_level == 0)
3777 && !(c == ' ' && quote_input_level == 0)) {
3778 if (quote_input_level > 0 && c == '"'
3779 && input_stack::get_level() == quote_input_level) {
3780 c = get_copy(&n);
3781 if (c == '"') {
3782 arg.append(c);
3783 c = get_copy(&n);
3785 else
3786 break;
3788 else {
3789 if (c == 0)
3790 arg.append(n);
3791 else {
3792 if (c == '\t' && quote_input_level == 0 && !done_tab_warning) {
3793 warning(WARN_TAB, "tab character in unquoted string argument");
3794 done_tab_warning = 1;
3796 arg.append(c);
3798 c = get_copy(&n);
3801 mi->add_arg(arg);
3805 void macro::invoke(symbol nm)
3807 macro_iterator *mi = new macro_iterator(nm, *this);
3808 decode_args(mi);
3809 input_stack::push(mi);
3810 tok.next();
3813 macro *macro::to_macro()
3815 return this;
3818 int macro::empty()
3820 return empty_macro == 1;
3823 macro_iterator::macro_iterator(symbol s, macro &m, const char *how_called)
3824 : string_iterator(m, how_called, s), args(0), argc(0)
3828 macro_iterator::macro_iterator() : args(0), argc(0)
3832 macro_iterator::~macro_iterator()
3834 while (args != 0) {
3835 arg_list *tem = args;
3836 args = args->next;
3837 delete tem;
3841 dictionary composite_dictionary(17);
3843 void composite_request()
3845 symbol from = get_name(1);
3846 if (!from.is_null()) {
3847 const char *from_gn = glyph_name_to_unicode(from.contents());
3848 if (!from_gn) {
3849 from_gn = check_unicode_name(from.contents());
3850 if (!from_gn) {
3851 error("invalid composite glyph name `%1'", from.contents());
3852 skip_line();
3853 return;
3856 const char *from_decomposed = decompose_unicode(from_gn);
3857 if (from_decomposed)
3858 from_gn = &from_decomposed[1];
3859 symbol to = get_name(1);
3860 if (to.is_null())
3861 composite_dictionary.remove(symbol(from_gn));
3862 else {
3863 const char *to_gn = glyph_name_to_unicode(to.contents());
3864 if (!to_gn) {
3865 to_gn = check_unicode_name(to.contents());
3866 if (!to_gn) {
3867 error("invalid composite glyph name `%1'", to.contents());
3868 skip_line();
3869 return;
3872 const char *to_decomposed = decompose_unicode(to_gn);
3873 if (to_decomposed)
3874 to_gn = &to_decomposed[1];
3875 if (strcmp(from_gn, to_gn) == 0)
3876 composite_dictionary.remove(symbol(from_gn));
3877 else
3878 (void)composite_dictionary.lookup(symbol(from_gn), (void *)to_gn);
3881 skip_line();
3884 static symbol composite_glyph_name(symbol nm)
3886 macro_iterator *mi = new macro_iterator();
3887 decode_string_args(mi);
3888 input_stack::push(mi);
3889 const char *gn = glyph_name_to_unicode(nm.contents());
3890 if (!gn) {
3891 gn = check_unicode_name(nm.contents());
3892 if (!gn) {
3893 error("invalid base glyph `%1' in composite glyph name", nm.contents());
3894 return EMPTY_SYMBOL;
3897 const char *gn_decomposed = decompose_unicode(gn);
3898 string glyph_name(gn_decomposed ? &gn_decomposed[1] : gn);
3899 string gl;
3900 int n = input_stack::nargs();
3901 for (int i = 1; i <= n; i++) {
3902 glyph_name += '_';
3903 input_iterator *p = input_stack::get_arg(i);
3904 gl.clear();
3905 int c;
3906 while ((c = p->get(0)) != EOF)
3907 gl += c;
3908 gl += '\0';
3909 const char *u = glyph_name_to_unicode(gl.contents());
3910 if (!u) {
3911 u = check_unicode_name(gl.contents());
3912 if (!u) {
3913 error("invalid component `%1' in composite glyph name",
3914 gl.contents());
3915 return EMPTY_SYMBOL;
3918 const char *decomposed = decompose_unicode(u);
3919 if (decomposed)
3920 u = &decomposed[1];
3921 void *mapped_composite = composite_dictionary.lookup(symbol(u));
3922 if (mapped_composite)
3923 u = (const char *)mapped_composite;
3924 glyph_name += u;
3926 glyph_name += '\0';
3927 const char *groff_gn = unicode_to_glyph_name(glyph_name.contents());
3928 if (groff_gn)
3929 return symbol(groff_gn);
3930 gl.clear();
3931 gl += 'u';
3932 gl += glyph_name;
3933 return symbol(gl.contents());
3936 int trap_sprung_flag = 0;
3937 int postpone_traps_flag = 0;
3938 symbol postponed_trap;
3940 void spring_trap(symbol nm)
3942 assert(!nm.is_null());
3943 trap_sprung_flag = 1;
3944 if (postpone_traps_flag) {
3945 postponed_trap = nm;
3946 return;
3948 static char buf[2] = { BEGIN_TRAP, 0 };
3949 static char buf2[2] = { END_TRAP, '\0' };
3950 input_stack::push(make_temp_iterator(buf2));
3951 request_or_macro *p = lookup_request(nm);
3952 macro *m = p->to_macro();
3953 if (m)
3954 input_stack::push(new macro_iterator(nm, *m, "trap-invoked macro"));
3955 else
3956 error("you can't invoke a request with a trap");
3957 input_stack::push(make_temp_iterator(buf));
3960 void postpone_traps()
3962 postpone_traps_flag = 1;
3965 int unpostpone_traps()
3967 postpone_traps_flag = 0;
3968 if (!postponed_trap.is_null()) {
3969 spring_trap(postponed_trap);
3970 postponed_trap = NULL_SYMBOL;
3971 return 1;
3973 else
3974 return 0;
3977 void read_request()
3979 macro_iterator *mi = new macro_iterator;
3980 int reading_from_terminal = isatty(fileno(stdin));
3981 int had_prompt = 0;
3982 if (!tok.newline() && !tok.eof()) {
3983 int c = get_copy(0);
3984 while (c == ' ')
3985 c = get_copy(0);
3986 while (c != EOF && c != '\n' && c != ' ') {
3987 if (!invalid_input_char(c)) {
3988 if (reading_from_terminal)
3989 fputc(c, stderr);
3990 had_prompt = 1;
3992 c = get_copy(0);
3994 if (c == ' ') {
3995 tok.make_space();
3996 decode_args(mi);
3999 if (reading_from_terminal) {
4000 fputc(had_prompt ? ':' : '\a', stderr);
4001 fflush(stderr);
4003 input_stack::push(mi);
4004 macro mac;
4005 int nl = 0;
4006 int c;
4007 while ((c = getchar()) != EOF) {
4008 if (invalid_input_char(c))
4009 warning(WARN_INPUT, "invalid input character code %1", int(c));
4010 else {
4011 if (c == '\n') {
4012 if (nl)
4013 break;
4014 else
4015 nl = 1;
4017 else
4018 nl = 0;
4019 mac.append(c);
4022 if (reading_from_terminal)
4023 clearerr(stdin);
4024 input_stack::push(new string_iterator(mac));
4025 tok.next();
4028 enum define_mode { DEFINE_NORMAL, DEFINE_APPEND, DEFINE_IGNORE };
4029 enum calling_mode { CALLING_NORMAL, CALLING_INDIRECT };
4030 enum comp_mode { COMP_IGNORE, COMP_DISABLE, COMP_ENABLE };
4032 void do_define_string(define_mode mode, comp_mode comp)
4034 symbol nm;
4035 node *n = 0; // pacify compiler
4036 int c;
4037 nm = get_name(1);
4038 if (nm.is_null()) {
4039 skip_line();
4040 return;
4042 if (tok.newline())
4043 c = '\n';
4044 else if (tok.tab())
4045 c = '\t';
4046 else if (!tok.space()) {
4047 error("bad string definition");
4048 skip_line();
4049 return;
4051 else
4052 c = get_copy(&n);
4053 while (c == ' ')
4054 c = get_copy(&n);
4055 if (c == '"')
4056 c = get_copy(&n);
4057 macro mac;
4058 request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm);
4059 macro *mm = rm ? rm->to_macro() : 0;
4060 if (mode == DEFINE_APPEND && mm)
4061 mac = *mm;
4062 if (comp == COMP_DISABLE)
4063 mac.append(PUSH_GROFF_MODE);
4064 else if (comp == COMP_ENABLE)
4065 mac.append(PUSH_COMP_MODE);
4066 while (c != '\n' && c != EOF) {
4067 if (c == 0)
4068 mac.append(n);
4069 else
4070 mac.append((unsigned char)c);
4071 c = get_copy(&n);
4073 if (!mm) {
4074 mm = new macro;
4075 request_dictionary.define(nm, mm);
4077 if (comp == COMP_DISABLE || comp == COMP_ENABLE)
4078 mac.append(POP_GROFFCOMP_MODE);
4079 *mm = mac;
4080 tok.next();
4083 void define_string()
4085 do_define_string(DEFINE_NORMAL,
4086 compatible_flag ? COMP_ENABLE: COMP_IGNORE);
4089 void define_nocomp_string()
4091 do_define_string(DEFINE_NORMAL, COMP_DISABLE);
4094 void append_string()
4096 do_define_string(DEFINE_APPEND,
4097 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4100 void append_nocomp_string()
4102 do_define_string(DEFINE_APPEND, COMP_DISABLE);
4105 void do_define_character(char_mode mode, const char *font_name)
4107 node *n = 0; // pacify compiler
4108 int c;
4109 tok.skip();
4110 charinfo *ci = tok.get_char(1);
4111 if (ci == 0) {
4112 skip_line();
4113 return;
4115 if (font_name) {
4116 string s(font_name);
4117 s += ' ';
4118 s += ci->nm.contents();
4119 s += '\0';
4120 ci = get_charinfo(symbol(s.contents()));
4122 tok.next();
4123 if (tok.newline())
4124 c = '\n';
4125 else if (tok.tab())
4126 c = '\t';
4127 else if (!tok.space()) {
4128 error("bad character definition");
4129 skip_line();
4130 return;
4132 else
4133 c = get_copy(&n);
4134 while (c == ' ' || c == '\t')
4135 c = get_copy(&n);
4136 if (c == '"')
4137 c = get_copy(&n);
4138 macro *m = new macro;
4139 while (c != '\n' && c != EOF) {
4140 if (c == 0)
4141 m->append(n);
4142 else
4143 m->append((unsigned char)c);
4144 c = get_copy(&n);
4146 m = ci->setx_macro(m, mode);
4147 if (m)
4148 delete m;
4149 tok.next();
4152 void define_character()
4154 do_define_character(CHAR_NORMAL);
4157 void define_fallback_character()
4159 do_define_character(CHAR_FALLBACK);
4162 void define_special_character()
4164 do_define_character(CHAR_SPECIAL);
4167 static void remove_character()
4169 tok.skip();
4170 while (!tok.newline() && !tok.eof()) {
4171 if (!tok.space() && !tok.tab()) {
4172 charinfo *ci = tok.get_char(1);
4173 if (!ci)
4174 break;
4175 macro *m = ci->set_macro(0);
4176 if (m)
4177 delete m;
4179 tok.next();
4181 skip_line();
4184 static void interpolate_string(symbol nm)
4186 request_or_macro *p = lookup_request(nm);
4187 macro *m = p->to_macro();
4188 if (!m)
4189 error("you can only invoke a string or macro using \\*");
4190 else {
4191 string_iterator *si = new string_iterator(*m, "string", nm);
4192 input_stack::push(si);
4196 static void interpolate_string_with_args(symbol s)
4198 request_or_macro *p = lookup_request(s);
4199 macro *m = p->to_macro();
4200 if (!m)
4201 error("you can only invoke a string or macro using \\*");
4202 else {
4203 macro_iterator *mi = new macro_iterator(s, *m);
4204 decode_string_args(mi);
4205 input_stack::push(mi);
4209 static void interpolate_arg(symbol nm)
4211 const char *s = nm.contents();
4212 if (!s || *s == '\0')
4213 copy_mode_error("missing argument name");
4214 else if (s[1] == 0 && csdigit(s[0]))
4215 input_stack::push(input_stack::get_arg(s[0] - '0'));
4216 else if (s[0] == '*' && s[1] == '\0') {
4217 int limit = input_stack::nargs();
4218 string args;
4219 for (int i = 1; i <= limit; i++) {
4220 input_iterator *p = input_stack::get_arg(i);
4221 int c;
4222 while ((c = p->get(0)) != EOF)
4223 args += c;
4224 if (i != limit)
4225 args += ' ';
4227 if (limit > 0) {
4228 args += '\0';
4229 input_stack::push(make_temp_iterator(args.contents()));
4232 else if (s[0] == '@' && s[1] == '\0') {
4233 int limit = input_stack::nargs();
4234 string args;
4235 for (int i = 1; i <= limit; i++) {
4236 args += '"';
4237 args += BEGIN_QUOTE;
4238 input_iterator *p = input_stack::get_arg(i);
4239 int c;
4240 while ((c = p->get(0)) != EOF)
4241 args += c;
4242 args += END_QUOTE;
4243 args += '"';
4244 if (i != limit)
4245 args += ' ';
4247 if (limit > 0) {
4248 args += '\0';
4249 input_stack::push(make_temp_iterator(args.contents()));
4252 else {
4253 const char *p;
4254 for (p = s; *p && csdigit(*p); p++)
4256 if (*p)
4257 copy_mode_error("bad argument name `%1'", s);
4258 else
4259 input_stack::push(input_stack::get_arg(atoi(s)));
4263 void handle_first_page_transition()
4265 push_token(tok);
4266 topdiv->begin_page();
4269 // We push back a token by wrapping it up in a token_node, and
4270 // wrapping that up in a string_iterator.
4272 static void push_token(const token &t)
4274 macro m;
4275 m.append(new token_node(t));
4276 input_stack::push(new string_iterator(m));
4279 void push_page_ejector()
4281 static char buf[2] = { PAGE_EJECTOR, '\0' };
4282 input_stack::push(make_temp_iterator(buf));
4285 void handle_initial_request(unsigned char code)
4287 char buf[2];
4288 buf[0] = code;
4289 buf[1] = '\0';
4290 macro mac;
4291 mac.append(new token_node(tok));
4292 input_stack::push(new string_iterator(mac));
4293 input_stack::push(make_temp_iterator(buf));
4294 topdiv->begin_page();
4295 tok.next();
4298 void handle_initial_title()
4300 handle_initial_request(TITLE_REQUEST);
4303 // this should be local to define_macro, but cfront 1.2 doesn't support that
4304 static symbol dot_symbol(".");
4306 void do_define_macro(define_mode mode, calling_mode calling, comp_mode comp)
4308 symbol nm, term;
4309 if (calling == CALLING_INDIRECT) {
4310 symbol temp1 = get_name(1);
4311 if (temp1.is_null()) {
4312 skip_line();
4313 return;
4315 symbol temp2 = get_name();
4316 input_stack::push(make_temp_iterator("\n"));
4317 if (!temp2.is_null()) {
4318 interpolate_string(temp2);
4319 input_stack::push(make_temp_iterator(" "));
4321 interpolate_string(temp1);
4322 input_stack::push(make_temp_iterator(" "));
4323 tok.next();
4325 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4326 nm = get_name(1);
4327 if (nm.is_null()) {
4328 skip_line();
4329 return;
4332 term = get_name(); // the request that terminates the definition
4333 if (term.is_null())
4334 term = dot_symbol;
4335 while (!tok.newline() && !tok.eof())
4336 tok.next();
4337 const char *start_filename;
4338 int start_lineno;
4339 int have_start_location = input_stack::get_location(0, &start_filename,
4340 &start_lineno);
4341 node *n;
4342 // doing this here makes the line numbers come out right
4343 int c = get_copy(&n, 1);
4344 macro mac;
4345 macro *mm = 0;
4346 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4347 request_or_macro *rm =
4348 (request_or_macro *)request_dictionary.lookup(nm);
4349 if (rm)
4350 mm = rm->to_macro();
4351 if (mm && mode == DEFINE_APPEND)
4352 mac = *mm;
4354 int bol = 1;
4355 if (comp == COMP_DISABLE)
4356 mac.append(PUSH_GROFF_MODE);
4357 else if (comp == COMP_ENABLE)
4358 mac.append(PUSH_COMP_MODE);
4359 for (;;) {
4360 while (c == ESCAPE_NEWLINE) {
4361 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND)
4362 mac.append(c);
4363 c = get_copy(&n, 1);
4365 if (bol && c == '.') {
4366 const char *s = term.contents();
4367 int d = 0;
4368 // see if it matches term
4369 int i = 0;
4370 if (s[0] != 0) {
4371 while ((d = get_copy(&n)) == ' ' || d == '\t')
4373 if ((unsigned char)s[0] == d) {
4374 for (i = 1; s[i] != 0; i++) {
4375 d = get_copy(&n);
4376 if ((unsigned char)s[i] != d)
4377 break;
4381 if (s[i] == 0
4382 && ((i == 2 && compatible_flag)
4383 || (d = get_copy(&n)) == ' '
4384 || d == '\n')) { // we found it
4385 if (d == '\n')
4386 tok.make_newline();
4387 else
4388 tok.make_space();
4389 if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) {
4390 if (!mm) {
4391 mm = new macro;
4392 request_dictionary.define(nm, mm);
4394 if (comp == COMP_DISABLE || comp == COMP_ENABLE)
4395 mac.append(POP_GROFFCOMP_MODE);
4396 *mm = mac;
4398 if (term != dot_symbol) {
4399 ignoring = 0;
4400 interpolate_macro(term);
4402 else
4403 skip_line();
4404 return;
4406 if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) {
4407 mac.append(c);
4408 for (int j = 0; j < i; j++)
4409 mac.append(s[j]);
4411 c = d;
4413 if (c == EOF) {
4414 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4415 if (have_start_location)
4416 error_with_file_and_line(start_filename, start_lineno,
4417 "end of file while defining macro `%1'",
4418 nm.contents());
4419 else
4420 error("end of file while defining macro `%1'", nm.contents());
4422 else {
4423 if (have_start_location)
4424 error_with_file_and_line(start_filename, start_lineno,
4425 "end of file while ignoring input lines");
4426 else
4427 error("end of file while ignoring input lines");
4429 tok.next();
4430 return;
4432 if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4433 if (c == 0)
4434 mac.append(n);
4435 else
4436 mac.append(c);
4438 bol = (c == '\n');
4439 c = get_copy(&n, 1);
4443 void define_macro()
4445 do_define_macro(DEFINE_NORMAL, CALLING_NORMAL,
4446 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4449 void define_nocomp_macro()
4451 do_define_macro(DEFINE_NORMAL, CALLING_NORMAL, COMP_DISABLE);
4454 void define_indirect_macro()
4456 do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT,
4457 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4460 void define_indirect_nocomp_macro()
4462 do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT, COMP_DISABLE);
4465 void append_macro()
4467 do_define_macro(DEFINE_APPEND, CALLING_NORMAL,
4468 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4471 void append_nocomp_macro()
4473 do_define_macro(DEFINE_APPEND, CALLING_NORMAL, COMP_DISABLE);
4476 void append_indirect_macro()
4478 do_define_macro(DEFINE_APPEND, CALLING_INDIRECT,
4479 compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4482 void append_indirect_nocomp_macro()
4484 do_define_macro(DEFINE_APPEND, CALLING_INDIRECT, COMP_DISABLE);
4487 void ignore()
4489 ignoring = 1;
4490 do_define_macro(DEFINE_IGNORE, CALLING_NORMAL, COMP_IGNORE);
4491 ignoring = 0;
4494 void remove_macro()
4496 for (;;) {
4497 symbol s = get_name();
4498 if (s.is_null())
4499 break;
4500 request_dictionary.remove(s);
4502 skip_line();
4505 void rename_macro()
4507 symbol s1 = get_name(1);
4508 if (!s1.is_null()) {
4509 symbol s2 = get_name(1);
4510 if (!s2.is_null())
4511 request_dictionary.rename(s1, s2);
4513 skip_line();
4516 void alias_macro()
4518 symbol s1 = get_name(1);
4519 if (!s1.is_null()) {
4520 symbol s2 = get_name(1);
4521 if (!s2.is_null()) {
4522 if (!request_dictionary.alias(s1, s2))
4523 warning(WARN_MAC, "macro `%1' not defined", s2.contents());
4526 skip_line();
4529 void chop_macro()
4531 symbol s = get_name(1);
4532 if (!s.is_null()) {
4533 request_or_macro *p = lookup_request(s);
4534 macro *m = p->to_macro();
4535 if (!m)
4536 error("cannot chop request");
4537 else if (m->empty())
4538 error("cannot chop empty macro");
4539 else {
4540 int have_restore = 0;
4541 // we have to check for additional save/restore pairs which could be
4542 // there due to empty am1 requests.
4543 for (;;) {
4544 if (m->get(m->len - 1) != POP_GROFFCOMP_MODE)
4545 break;
4546 have_restore = 1;
4547 m->len -= 1;
4548 if (m->get(m->len - 1) != PUSH_GROFF_MODE
4549 && m->get(m->len - 1) != PUSH_COMP_MODE)
4550 break;
4551 have_restore = 0;
4552 m->len -= 1;
4553 if (m->len == 0)
4554 break;
4556 if (m->len == 0)
4557 error("cannot chop empty macro");
4558 else {
4559 if (have_restore)
4560 m->set(POP_GROFFCOMP_MODE, m->len - 1);
4561 else
4562 m->len -= 1;
4566 skip_line();
4569 void substring_request()
4571 int start; // 0, 1, ..., n-1 or -1, -2, ...
4572 symbol s = get_name(1);
4573 if (!s.is_null() && get_integer(&start)) {
4574 request_or_macro *p = lookup_request(s);
4575 macro *m = p->to_macro();
4576 if (!m)
4577 error("cannot apply `substring' on a request");
4578 else {
4579 int end = -1;
4580 if (!has_arg() || get_integer(&end)) {
4581 int real_length = 0; // 1, 2, ..., n
4582 string_iterator iter1(*m);
4583 for (int l = 0; l < m->len; l++) {
4584 int c = iter1.get(0);
4585 if (c == PUSH_GROFF_MODE
4586 || c == PUSH_COMP_MODE
4587 || c == POP_GROFFCOMP_MODE)
4588 continue;
4589 if (c == EOF)
4590 break;
4591 real_length++;
4593 if (start < 0)
4594 start += real_length;
4595 if (end < 0)
4596 end += real_length;
4597 if (start > end) {
4598 int tem = start;
4599 start = end;
4600 end = tem;
4602 if (start >= real_length || end < 0) {
4603 warning(WARN_RANGE,
4604 "start and end index of substring out of range");
4605 m->len = 0;
4606 if (m->p) {
4607 if (--(m->p->count) <= 0)
4608 delete m->p;
4609 m->p = 0;
4611 skip_line();
4612 return;
4614 if (start < 0) {
4615 warning(WARN_RANGE,
4616 "start index of substring out of range, set to 0");
4617 start = 0;
4619 if (end >= real_length) {
4620 warning(WARN_RANGE,
4621 "end index of substring out of range, set to string length");
4622 end = real_length - 1;
4624 // now extract the substring
4625 string_iterator iter(*m);
4626 int i;
4627 for (i = 0; i < start; i++) {
4628 int c = iter.get(0);
4629 while (c == PUSH_GROFF_MODE
4630 || c == PUSH_COMP_MODE
4631 || c == POP_GROFFCOMP_MODE)
4632 c = iter.get(0);
4633 if (c == EOF)
4634 break;
4636 macro mac;
4637 for (; i <= end; i++) {
4638 node *nd = 0; // pacify compiler
4639 int c = iter.get(&nd);
4640 while (c == PUSH_GROFF_MODE
4641 || c == PUSH_COMP_MODE
4642 || c == POP_GROFFCOMP_MODE)
4643 c = iter.get(0);
4644 if (c == EOF)
4645 break;
4646 if (c == 0)
4647 mac.append(nd);
4648 else
4649 mac.append((unsigned char)c);
4651 *m = mac;
4655 skip_line();
4658 void length_request()
4660 symbol ret;
4661 ret = get_name(1);
4662 if (ret.is_null()) {
4663 skip_line();
4664 return;
4666 int c;
4667 node *n;
4668 if (tok.newline())
4669 c = '\n';
4670 else if (tok.tab())
4671 c = '\t';
4672 else if (!tok.space()) {
4673 error("bad string definition");
4674 skip_line();
4675 return;
4677 else
4678 c = get_copy(&n);
4679 while (c == ' ')
4680 c = get_copy(&n);
4681 if (c == '"')
4682 c = get_copy(&n);
4683 int len = 0;
4684 while (c != '\n' && c != EOF) {
4685 ++len;
4686 c = get_copy(&n);
4688 reg *r = (reg*)number_reg_dictionary.lookup(ret);
4689 if (r)
4690 r->set_value(len);
4691 else
4692 set_number_reg(ret, len);
4693 tok.next();
4696 void asciify_macro()
4698 symbol s = get_name(1);
4699 if (!s.is_null()) {
4700 request_or_macro *p = lookup_request(s);
4701 macro *m = p->to_macro();
4702 if (!m)
4703 error("cannot asciify request");
4704 else {
4705 macro am;
4706 string_iterator iter(*m);
4707 for (;;) {
4708 node *nd = 0; // pacify compiler
4709 int c = iter.get(&nd);
4710 if (c == EOF)
4711 break;
4712 if (c != 0)
4713 am.append(c);
4714 else
4715 nd->asciify(&am);
4717 *m = am;
4720 skip_line();
4723 void unformat_macro()
4725 symbol s = get_name(1);
4726 if (!s.is_null()) {
4727 request_or_macro *p = lookup_request(s);
4728 macro *m = p->to_macro();
4729 if (!m)
4730 error("cannot unformat request");
4731 else {
4732 macro am;
4733 string_iterator iter(*m);
4734 for (;;) {
4735 node *nd = 0; // pacify compiler
4736 int c = iter.get(&nd);
4737 if (c == EOF)
4738 break;
4739 if (c != 0)
4740 am.append(c);
4741 else {
4742 if (nd->set_unformat_flag())
4743 am.append(nd);
4746 *m = am;
4749 skip_line();
4752 static void interpolate_environment_variable(symbol nm)
4754 const char *s = getenv(nm.contents());
4755 if (s && *s)
4756 input_stack::push(make_temp_iterator(s));
4759 void interpolate_number_reg(symbol nm, int inc)
4761 reg *r = lookup_number_reg(nm);
4762 if (inc < 0)
4763 r->decrement();
4764 else if (inc > 0)
4765 r->increment();
4766 input_stack::push(make_temp_iterator(r->get_string()));
4769 static void interpolate_number_format(symbol nm)
4771 reg *r = (reg *)number_reg_dictionary.lookup(nm);
4772 if (r)
4773 input_stack::push(make_temp_iterator(r->get_format()));
4776 static int get_delim_number(units *n, unsigned char si, int prev_value)
4778 token start;
4779 start.next();
4780 if (start.delimiter(1)) {
4781 tok.next();
4782 if (get_number(n, si, prev_value)) {
4783 if (start != tok)
4784 warning(WARN_DELIM, "closing delimiter does not match");
4785 return 1;
4788 return 0;
4791 static int get_delim_number(units *n, unsigned char si)
4793 token start;
4794 start.next();
4795 if (start.delimiter(1)) {
4796 tok.next();
4797 if (get_number(n, si)) {
4798 if (start != tok)
4799 warning(WARN_DELIM, "closing delimiter does not match");
4800 return 1;
4803 return 0;
4806 static int get_line_arg(units *n, unsigned char si, charinfo **cp)
4808 token start;
4809 start.next();
4810 int start_level = input_stack::get_level();
4811 if (!start.delimiter(1))
4812 return 0;
4813 tok.next();
4814 if (get_number(n, si)) {
4815 if (tok.dummy() || tok.transparent_dummy())
4816 tok.next();
4817 if (!(start == tok && input_stack::get_level() == start_level)) {
4818 *cp = tok.get_char(1);
4819 tok.next();
4821 if (!(start == tok && input_stack::get_level() == start_level))
4822 warning(WARN_DELIM, "closing delimiter does not match");
4823 return 1;
4825 return 0;
4828 static int read_size(int *x)
4830 tok.next();
4831 int c = tok.ch();
4832 int inc = 0;
4833 if (c == '-') {
4834 inc = -1;
4835 tok.next();
4836 c = tok.ch();
4838 else if (c == '+') {
4839 inc = 1;
4840 tok.next();
4841 c = tok.ch();
4843 int val = 0; // pacify compiler
4844 int bad = 0;
4845 if (c == '(') {
4846 tok.next();
4847 c = tok.ch();
4848 if (!inc) {
4849 // allow an increment either before or after the left parenthesis
4850 if (c == '-') {
4851 inc = -1;
4852 tok.next();
4853 c = tok.ch();
4855 else if (c == '+') {
4856 inc = 1;
4857 tok.next();
4858 c = tok.ch();
4861 if (!csdigit(c))
4862 bad = 1;
4863 else {
4864 val = c - '0';
4865 tok.next();
4866 c = tok.ch();
4867 if (!csdigit(c))
4868 bad = 1;
4869 else {
4870 val = val*10 + (c - '0');
4871 val *= sizescale;
4875 else if (csdigit(c)) {
4876 val = c - '0';
4877 if (!inc && c != '0' && c < '4') {
4878 tok.next();
4879 c = tok.ch();
4880 if (!csdigit(c))
4881 bad = 1;
4882 else
4883 val = val*10 + (c - '0');
4885 val *= sizescale;
4887 else if (!tok.delimiter(1))
4888 return 0;
4889 else {
4890 token start(tok);
4891 tok.next();
4892 if (!(inc
4893 ? get_number(&val, 'z')
4894 : get_number(&val, 'z', curenv->get_requested_point_size())))
4895 return 0;
4896 if (!(start.ch() == '[' && tok.ch() == ']') && start != tok) {
4897 if (start.ch() == '[')
4898 error("missing `]'");
4899 else
4900 error("missing closing delimiter");
4901 return 0;
4904 if (!bad) {
4905 switch (inc) {
4906 case 0:
4907 if (val == 0) {
4908 // special case -- \s[0] and \s0 means to revert to previous size
4909 *x = 0;
4910 return 1;
4912 *x = val;
4913 break;
4914 case 1:
4915 *x = curenv->get_requested_point_size() + val;
4916 break;
4917 case -1:
4918 *x = curenv->get_requested_point_size() - val;
4919 break;
4920 default:
4921 assert(0);
4923 if (*x <= 0) {
4924 warning(WARN_RANGE,
4925 "\\s request results in non-positive point size; set to 1");
4926 *x = 1;
4928 return 1;
4930 else {
4931 error("bad digit in point size");
4932 return 0;
4936 static symbol get_delim_name()
4938 token start;
4939 start.next();
4940 if (start.eof()) {
4941 error("end of input at start of delimited name");
4942 return NULL_SYMBOL;
4944 if (start.newline()) {
4945 error("can't delimit name with a newline");
4946 return NULL_SYMBOL;
4948 int start_level = input_stack::get_level();
4949 char abuf[ABUF_SIZE];
4950 char *buf = abuf;
4951 int buf_size = ABUF_SIZE;
4952 int i = 0;
4953 for (;;) {
4954 if (i + 1 > buf_size) {
4955 if (buf == abuf) {
4956 buf = new char[ABUF_SIZE*2];
4957 memcpy(buf, abuf, buf_size);
4958 buf_size = ABUF_SIZE*2;
4960 else {
4961 char *old_buf = buf;
4962 buf = new char[buf_size*2];
4963 memcpy(buf, old_buf, buf_size);
4964 buf_size *= 2;
4965 a_delete old_buf;
4968 tok.next();
4969 if (tok == start
4970 && (compatible_flag || input_stack::get_level() == start_level))
4971 break;
4972 if ((buf[i] = tok.ch()) == 0) {
4973 error("missing delimiter (got %1)", tok.description());
4974 if (buf != abuf)
4975 a_delete buf;
4976 return NULL_SYMBOL;
4978 i++;
4980 buf[i] = '\0';
4981 if (buf == abuf) {
4982 if (i == 0) {
4983 error("empty delimited name");
4984 return NULL_SYMBOL;
4986 else
4987 return symbol(buf);
4989 else {
4990 symbol s(buf);
4991 a_delete buf;
4992 return s;
4996 // Implement \R
4998 static void do_register()
5000 token start;
5001 start.next();
5002 if (!start.delimiter(1))
5003 return;
5004 tok.next();
5005 symbol nm = get_long_name(1);
5006 if (nm.is_null())
5007 return;
5008 while (tok.space())
5009 tok.next();
5010 reg *r = (reg *)number_reg_dictionary.lookup(nm);
5011 int prev_value;
5012 if (!r || !r->get_value(&prev_value))
5013 prev_value = 0;
5014 int val;
5015 if (!get_number(&val, 'u', prev_value))
5016 return;
5017 if (start != tok)
5018 warning(WARN_DELIM, "closing delimiter does not match");
5019 if (r)
5020 r->set_value(val);
5021 else
5022 set_number_reg(nm, val);
5025 // this implements the \w escape sequence
5027 static void do_width()
5029 token start;
5030 start.next();
5031 int start_level = input_stack::get_level();
5032 environment env(curenv);
5033 environment *oldenv = curenv;
5034 curenv = &env;
5035 for (;;) {
5036 tok.next();
5037 if (tok.eof()) {
5038 warning(WARN_DELIM, "missing closing delimiter");
5039 break;
5041 if (tok.newline()) {
5042 warning(WARN_DELIM, "missing closing delimiter");
5043 input_stack::push(make_temp_iterator("\n"));
5044 break;
5046 if (tok == start
5047 && (compatible_flag || input_stack::get_level() == start_level))
5048 break;
5049 tok.process();
5051 env.wrap_up_tab();
5052 units x = env.get_input_line_position().to_units();
5053 input_stack::push(make_temp_iterator(i_to_a(x)));
5054 env.width_registers();
5055 curenv = oldenv;
5056 have_input = 0;
5059 charinfo *page_character;
5061 void set_page_character()
5063 page_character = get_optional_char();
5064 skip_line();
5067 static const symbol percent_symbol("%");
5069 void read_title_parts(node **part, hunits *part_width)
5071 tok.skip();
5072 if (tok.newline() || tok.eof())
5073 return;
5074 token start(tok);
5075 int start_level = input_stack::get_level();
5076 tok.next();
5077 for (int i = 0; i < 3; i++) {
5078 while (!tok.newline() && !tok.eof()) {
5079 if (tok == start
5080 && (compatible_flag || input_stack::get_level() == start_level)) {
5081 tok.next();
5082 break;
5084 if (page_character != 0 && tok.get_char() == page_character)
5085 interpolate_number_reg(percent_symbol, 0);
5086 else
5087 tok.process();
5088 tok.next();
5090 curenv->wrap_up_tab();
5091 part_width[i] = curenv->get_input_line_position();
5092 part[i] = curenv->extract_output_line();
5094 while (!tok.newline() && !tok.eof())
5095 tok.next();
5098 class non_interpreted_node : public node {
5099 macro mac;
5100 public:
5101 non_interpreted_node(const macro &);
5102 int interpret(macro *);
5103 node *copy();
5104 int ends_sentence();
5105 int same(node *);
5106 const char *type();
5107 int force_tprint();
5108 int is_tag();
5111 non_interpreted_node::non_interpreted_node(const macro &m) : mac(m)
5115 int non_interpreted_node::ends_sentence()
5117 return 2;
5120 int non_interpreted_node::same(node *nd)
5122 return mac == ((non_interpreted_node *)nd)->mac;
5125 const char *non_interpreted_node::type()
5127 return "non_interpreted_node";
5130 int non_interpreted_node::force_tprint()
5132 return 0;
5135 int non_interpreted_node::is_tag()
5137 return 0;
5140 node *non_interpreted_node::copy()
5142 return new non_interpreted_node(mac);
5145 int non_interpreted_node::interpret(macro *m)
5147 string_iterator si(mac);
5148 node *n = 0; // pacify compiler
5149 for (;;) {
5150 int c = si.get(&n);
5151 if (c == EOF)
5152 break;
5153 if (c == 0)
5154 m->append(n);
5155 else
5156 m->append(c);
5158 return 1;
5161 static node *do_non_interpreted()
5163 node *n;
5164 int c;
5165 macro mac;
5166 while ((c = get_copy(&n)) != ESCAPE_QUESTION && c != EOF && c != '\n')
5167 if (c == 0)
5168 mac.append(n);
5169 else
5170 mac.append(c);
5171 if (c == EOF || c == '\n') {
5172 error("missing \\?");
5173 return 0;
5175 return new non_interpreted_node(mac);
5178 static void encode_char(macro *mac, char c)
5180 if (c == '\0') {
5181 if ((font::use_charnames_in_special) && tok.special()) {
5182 charinfo *ci = tok.get_char(1);
5183 const char *s = ci->get_symbol()->contents();
5184 if (s[0] != (char)0) {
5185 mac->append('\\');
5186 mac->append('(');
5187 int i = 0;
5188 while (s[i] != (char)0) {
5189 mac->append(s[i]);
5190 i++;
5192 mac->append('\\');
5193 mac->append(')');
5196 else if (tok.stretchable_space()
5197 || tok.unstretchable_space())
5198 mac->append(' ');
5199 else if (!(tok.hyphen_indicator()
5200 || tok.dummy()
5201 || tok.transparent_dummy()
5202 || tok.zero_width_break()))
5203 error("%1 is invalid within \\X", tok.description());
5205 else {
5206 if ((font::use_charnames_in_special) && (c == '\\')) {
5208 * add escape escape sequence
5210 mac->append(c);
5212 mac->append(c);
5216 node *do_special()
5218 token start;
5219 start.next();
5220 int start_level = input_stack::get_level();
5221 macro mac;
5222 for (tok.next();
5223 tok != start || input_stack::get_level() != start_level;
5224 tok.next()) {
5225 if (tok.eof()) {
5226 warning(WARN_DELIM, "missing closing delimiter");
5227 return 0;
5229 if (tok.newline()) {
5230 input_stack::push(make_temp_iterator("\n"));
5231 warning(WARN_DELIM, "missing closing delimiter");
5232 break;
5234 unsigned char c;
5235 if (tok.space())
5236 c = ' ';
5237 else if (tok.tab())
5238 c = '\t';
5239 else if (tok.leader())
5240 c = '\001';
5241 else if (tok.backspace())
5242 c = '\b';
5243 else
5244 c = tok.ch();
5245 encode_char(&mac, c);
5247 return new special_node(mac);
5250 void output_request()
5252 if (!tok.newline() && !tok.eof()) {
5253 int c;
5254 for (;;) {
5255 c = get_copy(0);
5256 if (c == '"') {
5257 c = get_copy(0);
5258 break;
5260 if (c != ' ' && c != '\t')
5261 break;
5263 for (; c != '\n' && c != EOF; c = get_copy(0))
5264 topdiv->transparent_output(c);
5265 topdiv->transparent_output('\n');
5267 tok.next();
5270 extern int image_no; // from node.cpp
5272 static node *do_suppress(symbol nm)
5274 if (nm.is_null() || nm.is_empty()) {
5275 error("expecting an argument to escape \\O");
5276 return 0;
5278 const char *s = nm.contents();
5279 switch (*s) {
5280 case '0':
5281 if (begin_level == 0)
5282 // suppress generation of glyphs
5283 return new suppress_node(0, 0);
5284 break;
5285 case '1':
5286 if (begin_level == 0)
5287 // enable generation of glyphs
5288 return new suppress_node(1, 0);
5289 break;
5290 case '2':
5291 if (begin_level == 0)
5292 return new suppress_node(1, 1);
5293 break;
5294 case '3':
5295 begin_level++;
5296 break;
5297 case '4':
5298 begin_level--;
5299 break;
5300 case '5':
5302 s++; // move over '5'
5303 char position = *s;
5304 if (*s == (char)0) {
5305 error("missing position and filename in \\O");
5306 return 0;
5308 if (!(position == 'l'
5309 || position == 'r'
5310 || position == 'c'
5311 || position == 'i')) {
5312 error("l, r, c, or i position expected (got %1 in \\O)", position);
5313 return 0;
5315 s++; // onto image name
5316 if (s == (char *)0) {
5317 error("missing image name for \\O");
5318 return 0;
5320 image_no++;
5321 if (begin_level == 0)
5322 return new suppress_node(symbol(s), position, image_no);
5324 break;
5325 default:
5326 error("`%1' is an invalid argument to \\O", *s);
5328 return 0;
5331 void special_node::tprint(troff_output_file *out)
5333 tprint_start(out);
5334 string_iterator iter(mac);
5335 for (;;) {
5336 int c = iter.get(0);
5337 if (c == EOF)
5338 break;
5339 for (const char *s = ::asciify(c); *s; s++)
5340 tprint_char(out, *s);
5342 tprint_end(out);
5345 int get_file_line(const char **filename, int *lineno)
5347 return input_stack::get_location(0, filename, lineno);
5350 void line_file()
5352 int n;
5353 if (get_integer(&n)) {
5354 const char *filename = 0;
5355 if (has_arg()) {
5356 symbol s = get_long_name();
5357 filename = s.contents();
5359 (void)input_stack::set_location(filename, n-1);
5361 skip_line();
5364 static int nroff_mode = 0;
5366 static void nroff_request()
5368 nroff_mode = 1;
5369 skip_line();
5372 static void troff_request()
5374 nroff_mode = 0;
5375 skip_line();
5378 static void skip_alternative()
5380 int level = 0;
5381 // ensure that ``.if 0\{'' works as expected
5382 if (tok.left_brace())
5383 level++;
5384 int c;
5385 for (;;) {
5386 c = input_stack::get(0);
5387 if (c == EOF)
5388 break;
5389 if (c == ESCAPE_LEFT_BRACE)
5390 ++level;
5391 else if (c == ESCAPE_RIGHT_BRACE)
5392 --level;
5393 else if (c == escape_char && escape_char > 0)
5394 switch(input_stack::get(0)) {
5395 case '{':
5396 ++level;
5397 break;
5398 case '}':
5399 --level;
5400 break;
5401 case '"':
5402 while ((c = input_stack::get(0)) != '\n' && c != EOF)
5406 Note that the level can properly be < 0, eg
5408 .if 1 \{\
5409 .if 0 \{\
5410 .\}\}
5412 So don't give an error message in this case.
5414 if (level <= 0 && c == '\n')
5415 break;
5417 tok.next();
5420 static void begin_alternative()
5422 while (tok.space() || tok.left_brace())
5423 tok.next();
5426 void nop_request()
5428 while (tok.space())
5429 tok.next();
5432 static int_stack if_else_stack;
5434 int do_if_request()
5436 int invert = 0;
5437 while (tok.space())
5438 tok.next();
5439 while (tok.ch() == '!') {
5440 tok.next();
5441 invert = !invert;
5443 int result;
5444 unsigned char c = tok.ch();
5445 if (c == 't') {
5446 tok.next();
5447 result = !nroff_mode;
5449 else if (c == 'n') {
5450 tok.next();
5451 result = nroff_mode;
5453 else if (c == 'v') {
5454 tok.next();
5455 result = 0;
5457 else if (c == 'o') {
5458 result = (topdiv->get_page_number() & 1);
5459 tok.next();
5461 else if (c == 'e') {
5462 result = !(topdiv->get_page_number() & 1);
5463 tok.next();
5465 else if (c == 'd' || c == 'r') {
5466 tok.next();
5467 symbol nm = get_name(1);
5468 if (nm.is_null()) {
5469 skip_alternative();
5470 return 0;
5472 result = (c == 'd'
5473 ? request_dictionary.lookup(nm) != 0
5474 : number_reg_dictionary.lookup(nm) != 0);
5476 else if (c == 'm') {
5477 tok.next();
5478 symbol nm = get_long_name(1);
5479 if (nm.is_null()) {
5480 skip_alternative();
5481 return 0;
5483 result = (nm == default_symbol
5484 || color_dictionary.lookup(nm) != 0);
5486 else if (c == 'c') {
5487 tok.next();
5488 tok.skip();
5489 charinfo *ci = tok.get_char(1);
5490 if (ci == 0) {
5491 skip_alternative();
5492 return 0;
5494 result = character_exists(ci, curenv);
5495 tok.next();
5497 else if (c == 'F') {
5498 tok.next();
5499 symbol nm = get_long_name(1);
5500 if (nm.is_null()) {
5501 skip_alternative();
5502 return 0;
5504 result = check_font(curenv->get_family()->nm, nm);
5506 else if (c == 'S') {
5507 tok.next();
5508 symbol nm = get_long_name(1);
5509 if (nm.is_null()) {
5510 skip_alternative();
5511 return 0;
5513 result = check_style(nm);
5515 else if (tok.space())
5516 result = 0;
5517 else if (tok.delimiter()) {
5518 token delim = tok;
5519 int delim_level = input_stack::get_level();
5520 environment env1(curenv);
5521 environment env2(curenv);
5522 environment *oldenv = curenv;
5523 curenv = &env1;
5524 suppress_push = 1;
5525 for (int i = 0; i < 2; i++) {
5526 for (;;) {
5527 tok.next();
5528 if (tok.newline() || tok.eof()) {
5529 warning(WARN_DELIM, "missing closing delimiter");
5530 tok.next();
5531 curenv = oldenv;
5532 return 0;
5534 if (tok == delim
5535 && (compatible_flag || input_stack::get_level() == delim_level))
5536 break;
5537 tok.process();
5539 curenv = &env2;
5541 node *n1 = env1.extract_output_line();
5542 node *n2 = env2.extract_output_line();
5543 result = same_node_list(n1, n2);
5544 delete_node_list(n1);
5545 delete_node_list(n2);
5546 curenv = oldenv;
5547 have_input = 0;
5548 suppress_push = 0;
5549 tok.next();
5551 else {
5552 units n;
5553 if (!get_number(&n, 'u')) {
5554 skip_alternative();
5555 return 0;
5557 else
5558 result = n > 0;
5560 if (invert)
5561 result = !result;
5562 if (result)
5563 begin_alternative();
5564 else
5565 skip_alternative();
5566 return result;
5569 void if_else_request()
5571 if_else_stack.push(do_if_request());
5574 void if_request()
5576 do_if_request();
5579 void else_request()
5581 if (if_else_stack.is_empty()) {
5582 warning(WARN_EL, "unbalanced .el request");
5583 skip_alternative();
5585 else {
5586 if (if_else_stack.pop())
5587 skip_alternative();
5588 else
5589 begin_alternative();
5593 static int while_depth = 0;
5594 static int while_break_flag = 0;
5596 void while_request()
5598 macro mac;
5599 int escaped = 0;
5600 int level = 0;
5601 mac.append(new token_node(tok));
5602 for (;;) {
5603 node *n = 0; // pacify compiler
5604 int c = input_stack::get(&n);
5605 if (c == EOF)
5606 break;
5607 if (c == 0) {
5608 escaped = 0;
5609 mac.append(n);
5611 else if (escaped) {
5612 if (c == '{')
5613 level += 1;
5614 else if (c == '}')
5615 level -= 1;
5616 escaped = 0;
5617 mac.append(c);
5619 else {
5620 if (c == ESCAPE_LEFT_BRACE)
5621 level += 1;
5622 else if (c == ESCAPE_RIGHT_BRACE)
5623 level -= 1;
5624 else if (c == escape_char)
5625 escaped = 1;
5626 mac.append(c);
5627 if (c == '\n' && level <= 0)
5628 break;
5631 if (level != 0)
5632 error("unbalanced \\{ \\}");
5633 else {
5634 while_depth++;
5635 input_stack::add_boundary();
5636 for (;;) {
5637 input_stack::push(new string_iterator(mac, "while loop"));
5638 tok.next();
5639 if (!do_if_request()) {
5640 while (input_stack::get(0) != EOF)
5642 break;
5644 process_input_stack();
5645 if (while_break_flag || input_stack::is_return_boundary()) {
5646 while_break_flag = 0;
5647 break;
5650 input_stack::remove_boundary();
5651 while_depth--;
5653 tok.next();
5656 void while_break_request()
5658 if (!while_depth) {
5659 error("no while loop");
5660 skip_line();
5662 else {
5663 while_break_flag = 1;
5664 while (input_stack::get(0) != EOF)
5666 tok.next();
5670 void while_continue_request()
5672 if (!while_depth) {
5673 error("no while loop");
5674 skip_line();
5676 else {
5677 while (input_stack::get(0) != EOF)
5679 tok.next();
5683 // .so
5685 void source()
5687 symbol nm = get_long_name(1);
5688 if (nm.is_null())
5689 skip_line();
5690 else {
5691 while (!tok.newline() && !tok.eof())
5692 tok.next();
5693 errno = 0;
5694 FILE *fp = include_search_path.open_file_cautious(nm.contents());
5695 if (fp)
5696 input_stack::push(new file_iterator(fp, nm.contents()));
5697 else
5698 error("can't open `%1': %2", nm.contents(), strerror(errno));
5699 tok.next();
5703 // like .so but use popen()
5705 void pipe_source()
5707 if (safer_flag) {
5708 error(".pso request not allowed in safer mode");
5709 skip_line();
5711 else {
5712 #ifdef POPEN_MISSING
5713 error("pipes not available on this system");
5714 skip_line();
5715 #else /* not POPEN_MISSING */
5716 if (tok.newline() || tok.eof())
5717 error("missing command");
5718 else {
5719 int c;
5720 while ((c = get_copy(0)) == ' ' || c == '\t')
5722 int buf_size = 24;
5723 char *buf = new char[buf_size];
5724 int buf_used = 0;
5725 for (; c != '\n' && c != EOF; c = get_copy(0)) {
5726 const char *s = asciify(c);
5727 int slen = strlen(s);
5728 if (buf_used + slen + 1> buf_size) {
5729 char *old_buf = buf;
5730 int old_buf_size = buf_size;
5731 buf_size *= 2;
5732 buf = new char[buf_size];
5733 memcpy(buf, old_buf, old_buf_size);
5734 a_delete old_buf;
5736 strcpy(buf + buf_used, s);
5737 buf_used += slen;
5739 buf[buf_used] = '\0';
5740 errno = 0;
5741 FILE *fp = popen(buf, POPEN_RT);
5742 if (fp)
5743 input_stack::push(new file_iterator(fp, symbol(buf).contents(), 1));
5744 else
5745 error("can't open pipe to process `%1': %2", buf, strerror(errno));
5746 a_delete buf;
5748 tok.next();
5749 #endif /* not POPEN_MISSING */
5753 // .psbb
5755 static int llx_reg_contents = 0;
5756 static int lly_reg_contents = 0;
5757 static int urx_reg_contents = 0;
5758 static int ury_reg_contents = 0;
5760 struct bounding_box {
5761 int llx, lly, urx, ury;
5764 /* Parse the argument to a %%BoundingBox comment. Return 1 if it
5765 contains 4 numbers, 2 if it contains (atend), 0 otherwise. */
5767 int parse_bounding_box(char *p, bounding_box *bb)
5769 if (sscanf(p, "%d %d %d %d",
5770 &bb->llx, &bb->lly, &bb->urx, &bb->ury) == 4)
5771 return 1;
5772 else {
5773 /* The Document Structuring Conventions say that the numbers
5774 should be integers. Unfortunately some broken applications
5775 get this wrong. */
5776 double x1, x2, x3, x4;
5777 if (sscanf(p, "%lf %lf %lf %lf", &x1, &x2, &x3, &x4) == 4) {
5778 bb->llx = (int)x1;
5779 bb->lly = (int)x2;
5780 bb->urx = (int)x3;
5781 bb->ury = (int)x4;
5782 return 1;
5784 else {
5785 for (; *p == ' ' || *p == '\t'; p++)
5787 if (strncmp(p, "(atend)", 7) == 0) {
5788 return 2;
5792 bb->llx = bb->lly = bb->urx = bb->ury = 0;
5793 return 0;
5796 // This version is taken from psrm.cpp
5798 #define PS_LINE_MAX 255
5799 cset white_space("\n\r \t");
5801 int ps_get_line(char *buf, FILE *fp, const char* filename)
5803 int c = getc(fp);
5804 if (c == EOF) {
5805 buf[0] = '\0';
5806 return 0;
5808 int i = 0;
5809 int err = 0;
5810 while (c != '\r' && c != '\n' && c != EOF) {
5811 if ((c < 0x1b && !white_space(c)) || c == 0x7f)
5812 error("invalid input character code %1 in `%2'", int(c), filename);
5813 else if (i < PS_LINE_MAX)
5814 buf[i++] = c;
5815 else if (!err) {
5816 err = 1;
5817 error("PostScript file `%1' is non-conforming "
5818 "because length of line exceeds 255", filename);
5820 c = getc(fp);
5822 buf[i++] = '\n';
5823 buf[i] = '\0';
5824 if (c == '\r') {
5825 c = getc(fp);
5826 if (c != EOF && c != '\n')
5827 ungetc(c, fp);
5829 return 1;
5832 inline void assign_registers(int llx, int lly, int urx, int ury)
5834 llx_reg_contents = llx;
5835 lly_reg_contents = lly;
5836 urx_reg_contents = urx;
5837 ury_reg_contents = ury;
5840 void do_ps_file(FILE *fp, const char* filename)
5842 bounding_box bb;
5843 int bb_at_end = 0;
5844 char buf[PS_LINE_MAX];
5845 llx_reg_contents = lly_reg_contents =
5846 urx_reg_contents = ury_reg_contents = 0;
5847 if (!ps_get_line(buf, fp, filename)) {
5848 error("`%1' is empty", filename);
5849 return;
5851 if (strncmp("%!PS-Adobe-", buf, 11) != 0) {
5852 error("`%1' is not conforming to the Document Structuring Conventions",
5853 filename);
5854 return;
5856 while (ps_get_line(buf, fp, filename) != 0) {
5857 if (buf[0] != '%' || buf[1] != '%'
5858 || strncmp(buf + 2, "EndComments", 11) == 0)
5859 break;
5860 if (strncmp(buf + 2, "BoundingBox:", 12) == 0) {
5861 int res = parse_bounding_box(buf + 14, &bb);
5862 if (res == 1) {
5863 assign_registers(bb.llx, bb.lly, bb.urx, bb.ury);
5864 return;
5866 else if (res == 2) {
5867 bb_at_end = 1;
5868 break;
5870 else {
5871 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
5872 filename);
5873 return;
5877 if (bb_at_end) {
5878 long offset;
5879 int last_try = 0;
5880 /* in the trailer, the last BoundingBox comment is significant */
5881 for (offset = 512; !last_try; offset *= 2) {
5882 int had_trailer = 0;
5883 int got_bb = 0;
5884 if (offset > 32768 || fseek(fp, -offset, 2) == -1) {
5885 last_try = 1;
5886 if (fseek(fp, 0L, 0) == -1)
5887 break;
5889 while (ps_get_line(buf, fp, filename) != 0) {
5890 if (buf[0] == '%' && buf[1] == '%') {
5891 if (!had_trailer) {
5892 if (strncmp(buf + 2, "Trailer", 7) == 0)
5893 had_trailer = 1;
5895 else {
5896 if (strncmp(buf + 2, "BoundingBox:", 12) == 0) {
5897 int res = parse_bounding_box(buf + 14, &bb);
5898 if (res == 1)
5899 got_bb = 1;
5900 else if (res == 2) {
5901 error("`(atend)' not allowed in trailer of `%1'", filename);
5902 return;
5904 else {
5905 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
5906 filename);
5907 return;
5913 if (got_bb) {
5914 assign_registers(bb.llx, bb.lly, bb.urx, bb.ury);
5915 return;
5919 error("%%%%BoundingBox comment not found in `%1'", filename);
5922 void ps_bbox_request()
5924 symbol nm = get_long_name(1);
5925 if (nm.is_null())
5926 skip_line();
5927 else {
5928 while (!tok.newline() && !tok.eof())
5929 tok.next();
5930 errno = 0;
5931 // PS files might contain non-printable characters, such as ^Z
5932 // and CRs not followed by an LF, so open them in binary mode.
5933 FILE *fp = include_search_path.open_file_cautious(nm.contents(),
5934 0, FOPEN_RB);
5935 if (fp) {
5936 do_ps_file(fp, nm.contents());
5937 fclose(fp);
5939 else
5940 error("can't open `%1': %2", nm.contents(), strerror(errno));
5941 tok.next();
5945 const char *asciify(int c)
5947 static char buf[3];
5948 buf[0] = escape_char == '\0' ? '\\' : escape_char;
5949 buf[1] = buf[2] = '\0';
5950 switch (c) {
5951 case ESCAPE_QUESTION:
5952 buf[1] = '?';
5953 break;
5954 case ESCAPE_AMPERSAND:
5955 buf[1] = '&';
5956 break;
5957 case ESCAPE_RIGHT_PARENTHESIS:
5958 buf[1] = ')';
5959 break;
5960 case ESCAPE_UNDERSCORE:
5961 buf[1] = '_';
5962 break;
5963 case ESCAPE_BAR:
5964 buf[1] = '|';
5965 break;
5966 case ESCAPE_CIRCUMFLEX:
5967 buf[1] = '^';
5968 break;
5969 case ESCAPE_LEFT_BRACE:
5970 buf[1] = '{';
5971 break;
5972 case ESCAPE_RIGHT_BRACE:
5973 buf[1] = '}';
5974 break;
5975 case ESCAPE_LEFT_QUOTE:
5976 buf[1] = '`';
5977 break;
5978 case ESCAPE_RIGHT_QUOTE:
5979 buf[1] = '\'';
5980 break;
5981 case ESCAPE_HYPHEN:
5982 buf[1] = '-';
5983 break;
5984 case ESCAPE_BANG:
5985 buf[1] = '!';
5986 break;
5987 case ESCAPE_c:
5988 buf[1] = 'c';
5989 break;
5990 case ESCAPE_e:
5991 buf[1] = 'e';
5992 break;
5993 case ESCAPE_E:
5994 buf[1] = 'E';
5995 break;
5996 case ESCAPE_PERCENT:
5997 buf[1] = '%';
5998 break;
5999 case ESCAPE_SPACE:
6000 buf[1] = ' ';
6001 break;
6002 case ESCAPE_TILDE:
6003 buf[1] = '~';
6004 break;
6005 case ESCAPE_COLON:
6006 buf[1] = ':';
6007 break;
6008 case PUSH_GROFF_MODE:
6009 case PUSH_COMP_MODE:
6010 case POP_GROFFCOMP_MODE:
6011 buf[0] = '\0';
6012 break;
6013 default:
6014 if (invalid_input_char(c))
6015 buf[0] = '\0';
6016 else
6017 buf[0] = c;
6018 break;
6020 return buf;
6023 const char *input_char_description(int c)
6025 switch (c) {
6026 case '\n':
6027 return "a newline character";
6028 case '\b':
6029 return "a backspace character";
6030 case '\001':
6031 return "a leader character";
6032 case '\t':
6033 return "a tab character";
6034 case ' ':
6035 return "a space character";
6036 case '\0':
6037 return "a node";
6039 static char buf[sizeof("magic character code ") + 1 + INT_DIGITS];
6040 if (invalid_input_char(c)) {
6041 const char *s = asciify(c);
6042 if (*s) {
6043 buf[0] = '`';
6044 strcpy(buf + 1, s);
6045 strcat(buf, "'");
6046 return buf;
6048 sprintf(buf, "magic character code %d", c);
6049 return buf;
6051 if (csprint(c)) {
6052 buf[0] = '`';
6053 buf[1] = c;
6054 buf[2] = '\'';
6055 return buf;
6057 sprintf(buf, "character code %d", c);
6058 return buf;
6061 void tag()
6063 if (!tok.newline() && !tok.eof()) {
6064 string s;
6065 int c;
6066 for (;;) {
6067 c = get_copy(0);
6068 if (c == '"') {
6069 c = get_copy(0);
6070 break;
6072 if (c != ' ' && c != '\t')
6073 break;
6075 s = "x X ";
6076 for (; c != '\n' && c != EOF; c = get_copy(0))
6077 s += (char)c;
6078 s += '\n';
6079 curenv->add_node(new tag_node(s, 0));
6081 tok.next();
6084 void taga()
6086 if (!tok.newline() && !tok.eof()) {
6087 string s;
6088 int c;
6089 for (;;) {
6090 c = get_copy(0);
6091 if (c == '"') {
6092 c = get_copy(0);
6093 break;
6095 if (c != ' ' && c != '\t')
6096 break;
6098 s = "x X ";
6099 for (; c != '\n' && c != EOF; c = get_copy(0))
6100 s += (char)c;
6101 s += '\n';
6102 curenv->add_node(new tag_node(s, 1));
6104 tok.next();
6107 // .tm, .tm1, and .tmc
6109 void do_terminal(int newline, int string_like)
6111 if (!tok.newline() && !tok.eof()) {
6112 int c;
6113 for (;;) {
6114 c = get_copy(0);
6115 if (string_like && c == '"') {
6116 c = get_copy(0);
6117 break;
6119 if (c != ' ' && c != '\t')
6120 break;
6122 for (; c != '\n' && c != EOF; c = get_copy(0))
6123 fputs(asciify(c), stderr);
6125 if (newline)
6126 fputc('\n', stderr);
6127 fflush(stderr);
6128 tok.next();
6131 void terminal()
6133 do_terminal(1, 0);
6136 void terminal1()
6138 do_terminal(1, 1);
6141 void terminal_continue()
6143 do_terminal(0, 1);
6146 dictionary stream_dictionary(20);
6148 void do_open(int append)
6150 symbol stream = get_name(1);
6151 if (!stream.is_null()) {
6152 symbol filename = get_long_name(1);
6153 if (!filename.is_null()) {
6154 errno = 0;
6155 FILE *fp = fopen(filename.contents(), append ? "a" : "w");
6156 if (!fp) {
6157 error("can't open `%1' for %2: %3",
6158 filename.contents(),
6159 append ? "appending" : "writing",
6160 strerror(errno));
6161 fp = (FILE *)stream_dictionary.remove(stream);
6163 else
6164 fp = (FILE *)stream_dictionary.lookup(stream, fp);
6165 if (fp)
6166 fclose(fp);
6169 skip_line();
6172 void open_request()
6174 if (safer_flag) {
6175 error(".open request not allowed in safer mode");
6176 skip_line();
6178 else
6179 do_open(0);
6182 void opena_request()
6184 if (safer_flag) {
6185 error(".opena request not allowed in safer mode");
6186 skip_line();
6188 else
6189 do_open(1);
6192 void close_request()
6194 symbol stream = get_name(1);
6195 if (!stream.is_null()) {
6196 FILE *fp = (FILE *)stream_dictionary.remove(stream);
6197 if (!fp)
6198 error("no stream named `%1'", stream.contents());
6199 else
6200 fclose(fp);
6202 skip_line();
6205 // .write and .writec
6207 void do_write_request(int newline)
6209 symbol stream = get_name(1);
6210 if (stream.is_null()) {
6211 skip_line();
6212 return;
6214 FILE *fp = (FILE *)stream_dictionary.lookup(stream);
6215 if (!fp) {
6216 error("no stream named `%1'", stream.contents());
6217 skip_line();
6218 return;
6220 int c;
6221 while ((c = get_copy(0)) == ' ')
6223 if (c == '"')
6224 c = get_copy(0);
6225 for (; c != '\n' && c != EOF; c = get_copy(0))
6226 fputs(asciify(c), fp);
6227 if (newline)
6228 fputc('\n', fp);
6229 fflush(fp);
6230 tok.next();
6233 void write_request()
6235 do_write_request(1);
6238 void write_request_continue()
6240 do_write_request(0);
6243 void write_macro_request()
6245 symbol stream = get_name(1);
6246 if (stream.is_null()) {
6247 skip_line();
6248 return;
6250 FILE *fp = (FILE *)stream_dictionary.lookup(stream);
6251 if (!fp) {
6252 error("no stream named `%1'", stream.contents());
6253 skip_line();
6254 return;
6256 symbol s = get_name(1);
6257 if (s.is_null()) {
6258 skip_line();
6259 return;
6261 request_or_macro *p = lookup_request(s);
6262 macro *m = p->to_macro();
6263 if (!m)
6264 error("cannot write request");
6265 else {
6266 string_iterator iter(*m);
6267 for (;;) {
6268 int c = iter.get(0);
6269 if (c == EOF)
6270 break;
6271 fputs(asciify(c), fp);
6273 fflush(fp);
6275 skip_line();
6278 void warnscale_request()
6280 if (has_arg()) {
6281 char c = tok.ch();
6282 if (c == 'u')
6283 warn_scale = 1.0;
6284 else if (c == 'i')
6285 warn_scale = (double)units_per_inch;
6286 else if (c == 'c')
6287 warn_scale = (double)units_per_inch / 2.54;
6288 else if (c == 'p')
6289 warn_scale = (double)units_per_inch / 72.0;
6290 else if (c == 'P')
6291 warn_scale = (double)units_per_inch / 6.0;
6292 else {
6293 warning(WARN_SCALE,
6294 "invalid scaling indicator `%1', using `i' instead", c);
6295 c = 'i';
6297 warn_scaling_indicator = c;
6299 skip_line();
6302 void spreadwarn_request()
6304 hunits n;
6305 if (has_arg() && get_hunits(&n, 'm')) {
6306 if (n < 0)
6307 n = 0;
6308 hunits em = curenv->get_size();
6309 spread_limit = (double)n.to_units()
6310 / (em.is_zero() ? hresolution : em.to_units());
6312 else
6313 spread_limit = -spread_limit - 1; // no arg toggles on/off without
6314 // changing value; we mirror at
6315 // -0.5 to make zero a valid value
6316 skip_line();
6319 static void init_charset_table()
6321 char buf[16];
6322 strcpy(buf, "char");
6323 for (int i = 0; i < 256; i++) {
6324 strcpy(buf + 4, i_to_a(i));
6325 charset_table[i] = get_charinfo(symbol(buf));
6326 charset_table[i]->set_ascii_code(i);
6327 if (csalpha(i))
6328 charset_table[i]->set_hyphenation_code(cmlower(i));
6330 charset_table['.']->set_flags(charinfo::ENDS_SENTENCE);
6331 charset_table['?']->set_flags(charinfo::ENDS_SENTENCE);
6332 charset_table['!']->set_flags(charinfo::ENDS_SENTENCE);
6333 charset_table['-']->set_flags(charinfo::BREAK_AFTER);
6334 charset_table['"']->set_flags(charinfo::TRANSPARENT);
6335 charset_table['\'']->set_flags(charinfo::TRANSPARENT);
6336 charset_table[')']->set_flags(charinfo::TRANSPARENT);
6337 charset_table[']']->set_flags(charinfo::TRANSPARENT);
6338 charset_table['*']->set_flags(charinfo::TRANSPARENT);
6339 get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT);
6340 get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT);
6341 get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER);
6342 get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6343 get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6344 get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6345 get_charinfo(symbol("sqrtex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6346 get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6347 get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY);
6348 page_character = charset_table['%'];
6351 static void init_hpf_code_table()
6353 for (int i = 0; i < 256; i++)
6354 hpf_code_table[i] = i;
6357 static void do_translate(int translate_transparent, int translate_input)
6359 tok.skip();
6360 while (!tok.newline() && !tok.eof()) {
6361 if (tok.space()) {
6362 // This is a really bizarre troff feature.
6363 tok.next();
6364 translate_space_to_dummy = tok.dummy();
6365 if (tok.newline() || tok.eof())
6366 break;
6367 tok.next();
6368 continue;
6370 charinfo *ci1 = tok.get_char(1);
6371 if (ci1 == 0)
6372 break;
6373 tok.next();
6374 if (tok.newline() || tok.eof()) {
6375 ci1->set_special_translation(charinfo::TRANSLATE_SPACE,
6376 translate_transparent);
6377 break;
6379 if (tok.space())
6380 ci1->set_special_translation(charinfo::TRANSLATE_SPACE,
6381 translate_transparent);
6382 else if (tok.stretchable_space())
6383 ci1->set_special_translation(charinfo::TRANSLATE_STRETCHABLE_SPACE,
6384 translate_transparent);
6385 else if (tok.dummy())
6386 ci1->set_special_translation(charinfo::TRANSLATE_DUMMY,
6387 translate_transparent);
6388 else if (tok.hyphen_indicator())
6389 ci1->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR,
6390 translate_transparent);
6391 else {
6392 charinfo *ci2 = tok.get_char(1);
6393 if (ci2 == 0)
6394 break;
6395 if (ci1 == ci2)
6396 ci1->set_translation(0, translate_transparent, translate_input);
6397 else
6398 ci1->set_translation(ci2, translate_transparent, translate_input);
6400 tok.next();
6402 skip_line();
6405 void translate()
6407 do_translate(1, 0);
6410 void translate_no_transparent()
6412 do_translate(0, 0);
6415 void translate_input()
6417 do_translate(1, 1);
6420 void char_flags()
6422 int flags;
6423 if (get_integer(&flags))
6424 while (has_arg()) {
6425 charinfo *ci = tok.get_char(1);
6426 if (ci) {
6427 charinfo *tem = ci->get_translation();
6428 if (tem)
6429 ci = tem;
6430 ci->set_flags(flags);
6432 tok.next();
6434 skip_line();
6437 void hyphenation_code()
6439 tok.skip();
6440 while (!tok.newline() && !tok.eof()) {
6441 charinfo *ci = tok.get_char(1);
6442 if (ci == 0)
6443 break;
6444 tok.next();
6445 tok.skip();
6446 unsigned char c = tok.ch();
6447 if (c == 0) {
6448 error("hyphenation code must be ordinary character");
6449 break;
6451 if (csdigit(c)) {
6452 error("hyphenation code cannot be digit");
6453 break;
6455 ci->set_hyphenation_code(c);
6456 if (ci->get_translation()
6457 && ci->get_translation()->get_translation_input())
6458 ci->get_translation()->set_hyphenation_code(c);
6459 tok.next();
6460 tok.skip();
6462 skip_line();
6465 void hyphenation_patterns_file_code()
6467 tok.skip();
6468 while (!tok.newline() && !tok.eof()) {
6469 int n1, n2;
6470 if (get_integer(&n1) && (0 <= n1 && n1 <= 255)) {
6471 if (!has_arg()) {
6472 error("missing output hyphenation code");
6473 break;
6475 if (get_integer(&n2) && (0 <= n2 && n2 <= 255)) {
6476 hpf_code_table[n1] = n2;
6477 tok.skip();
6479 else {
6480 error("output hyphenation code must be integer in the range 0..255");
6481 break;
6484 else {
6485 error("input hyphenation code must be integer in the range 0..255");
6486 break;
6489 skip_line();
6492 charinfo *token::get_char(int required)
6494 if (type == TOKEN_CHAR)
6495 return charset_table[c];
6496 if (type == TOKEN_SPECIAL)
6497 return get_charinfo(nm);
6498 if (type == TOKEN_NUMBERED_CHAR)
6499 return get_charinfo_by_number(val);
6500 if (type == TOKEN_ESCAPE) {
6501 if (escape_char != 0)
6502 return charset_table[escape_char];
6503 else {
6504 error("`\\e' used while no current escape character");
6505 return 0;
6508 if (required) {
6509 if (type == TOKEN_EOF || type == TOKEN_NEWLINE)
6510 warning(WARN_MISSING, "missing normal or special character");
6511 else
6512 error("normal or special character expected (got %1)", description());
6514 return 0;
6517 charinfo *get_optional_char()
6519 while (tok.space())
6520 tok.next();
6521 charinfo *ci = tok.get_char();
6522 if (!ci)
6523 check_missing_character();
6524 else
6525 tok.next();
6526 return ci;
6529 void check_missing_character()
6531 if (!tok.newline() && !tok.eof() && !tok.right_brace() && !tok.tab())
6532 error("normal or special character expected (got %1): "
6533 "treated as missing",
6534 tok.description());
6537 // this is for \Z
6539 int token::add_to_node_list(node **pp)
6541 hunits w;
6542 int s;
6543 node *n = 0;
6544 switch (type) {
6545 case TOKEN_CHAR:
6546 *pp = (*pp)->add_char(charset_table[c], curenv, &w, &s);
6547 break;
6548 case TOKEN_DUMMY:
6549 n = new dummy_node;
6550 break;
6551 case TOKEN_ESCAPE:
6552 if (escape_char != 0)
6553 *pp = (*pp)->add_char(charset_table[escape_char], curenv, &w, &s);
6554 break;
6555 case TOKEN_HYPHEN_INDICATOR:
6556 *pp = (*pp)->add_discretionary_hyphen();
6557 break;
6558 case TOKEN_ITALIC_CORRECTION:
6559 *pp = (*pp)->add_italic_correction(&w);
6560 break;
6561 case TOKEN_LEFT_BRACE:
6562 break;
6563 case TOKEN_MARK_INPUT:
6564 set_number_reg(nm, curenv->get_input_line_position().to_units());
6565 break;
6566 case TOKEN_NODE:
6567 n = nd;
6568 nd = 0;
6569 break;
6570 case TOKEN_NUMBERED_CHAR:
6571 *pp = (*pp)->add_char(get_charinfo_by_number(val), curenv, &w, &s);
6572 break;
6573 case TOKEN_RIGHT_BRACE:
6574 break;
6575 case TOKEN_SPACE:
6576 n = new hmotion_node(curenv->get_space_width(),
6577 curenv->get_fill_color());
6578 break;
6579 case TOKEN_SPECIAL:
6580 *pp = (*pp)->add_char(get_charinfo(nm), curenv, &w, &s);
6581 break;
6582 case TOKEN_STRETCHABLE_SPACE:
6583 n = new unbreakable_space_node(curenv->get_space_width(),
6584 curenv->get_fill_color());
6585 break;
6586 case TOKEN_UNSTRETCHABLE_SPACE:
6587 n = new space_char_hmotion_node(curenv->get_space_width(),
6588 curenv->get_fill_color());
6589 break;
6590 case TOKEN_TRANSPARENT_DUMMY:
6591 n = new transparent_dummy_node;
6592 break;
6593 case TOKEN_ZERO_WIDTH_BREAK:
6594 n = new space_node(H0, curenv->get_fill_color());
6595 n->freeze_space();
6596 n->is_escape_colon();
6597 break;
6598 default:
6599 return 0;
6601 if (n) {
6602 n->next = *pp;
6603 *pp = n;
6605 return 1;
6608 void token::process()
6610 if (possibly_handle_first_page_transition())
6611 return;
6612 switch (type) {
6613 case TOKEN_BACKSPACE:
6614 curenv->add_node(new hmotion_node(-curenv->get_space_width(),
6615 curenv->get_fill_color()));
6616 break;
6617 case TOKEN_CHAR:
6618 curenv->add_char(charset_table[c]);
6619 break;
6620 case TOKEN_DUMMY:
6621 curenv->add_node(new dummy_node);
6622 break;
6623 case TOKEN_EMPTY:
6624 assert(0);
6625 break;
6626 case TOKEN_EOF:
6627 assert(0);
6628 break;
6629 case TOKEN_ESCAPE:
6630 if (escape_char != 0)
6631 curenv->add_char(charset_table[escape_char]);
6632 break;
6633 case TOKEN_BEGIN_TRAP:
6634 case TOKEN_END_TRAP:
6635 case TOKEN_PAGE_EJECTOR:
6636 // these are all handled in process_input_stack()
6637 break;
6638 case TOKEN_HYPHEN_INDICATOR:
6639 curenv->add_hyphen_indicator();
6640 break;
6641 case TOKEN_INTERRUPT:
6642 curenv->interrupt();
6643 break;
6644 case TOKEN_ITALIC_CORRECTION:
6645 curenv->add_italic_correction();
6646 break;
6647 case TOKEN_LEADER:
6648 curenv->handle_tab(1);
6649 break;
6650 case TOKEN_LEFT_BRACE:
6651 break;
6652 case TOKEN_MARK_INPUT:
6653 set_number_reg(nm, curenv->get_input_line_position().to_units());
6654 break;
6655 case TOKEN_NEWLINE:
6656 curenv->newline();
6657 break;
6658 case TOKEN_NODE:
6659 curenv->add_node(nd);
6660 nd = 0;
6661 break;
6662 case TOKEN_NUMBERED_CHAR:
6663 curenv->add_char(get_charinfo_by_number(val));
6664 break;
6665 case TOKEN_REQUEST:
6666 // handled in process_input_stack()
6667 break;
6668 case TOKEN_RIGHT_BRACE:
6669 break;
6670 case TOKEN_SPACE:
6671 curenv->space();
6672 break;
6673 case TOKEN_SPECIAL:
6674 curenv->add_char(get_charinfo(nm));
6675 break;
6676 case TOKEN_SPREAD:
6677 curenv->spread();
6678 break;
6679 case TOKEN_STRETCHABLE_SPACE:
6680 curenv->add_node(new unbreakable_space_node(curenv->get_space_width(),
6681 curenv->get_fill_color()));
6682 break;
6683 case TOKEN_UNSTRETCHABLE_SPACE:
6684 curenv->add_node(new space_char_hmotion_node(curenv->get_space_width(),
6685 curenv->get_fill_color()));
6686 break;
6687 case TOKEN_TAB:
6688 curenv->handle_tab(0);
6689 break;
6690 case TOKEN_TRANSPARENT:
6691 break;
6692 case TOKEN_TRANSPARENT_DUMMY:
6693 curenv->add_node(new transparent_dummy_node);
6694 break;
6695 case TOKEN_ZERO_WIDTH_BREAK:
6697 node *tmp = new space_node(H0, curenv->get_fill_color());
6698 tmp->freeze_space();
6699 tmp->is_escape_colon();
6700 curenv->add_node(tmp);
6701 break;
6703 default:
6704 assert(0);
6708 class nargs_reg : public reg {
6709 public:
6710 const char *get_string();
6713 const char *nargs_reg::get_string()
6715 return i_to_a(input_stack::nargs());
6718 class lineno_reg : public reg {
6719 public:
6720 const char *get_string();
6723 const char *lineno_reg::get_string()
6725 int line;
6726 const char *file;
6727 if (!input_stack::get_location(0, &file, &line))
6728 line = 0;
6729 return i_to_a(line);
6732 class writable_lineno_reg : public general_reg {
6733 public:
6734 writable_lineno_reg();
6735 void set_value(units);
6736 int get_value(units *);
6739 writable_lineno_reg::writable_lineno_reg()
6743 int writable_lineno_reg::get_value(units *res)
6745 int line;
6746 const char *file;
6747 if (!input_stack::get_location(0, &file, &line))
6748 return 0;
6749 *res = line;
6750 return 1;
6753 void writable_lineno_reg::set_value(units n)
6755 input_stack::set_location(0, n);
6758 class filename_reg : public reg {
6759 public:
6760 const char *get_string();
6763 const char *filename_reg::get_string()
6765 int line;
6766 const char *file;
6767 if (input_stack::get_location(0, &file, &line))
6768 return file;
6769 else
6770 return 0;
6773 class constant_reg : public reg {
6774 const char *s;
6775 public:
6776 constant_reg(const char *);
6777 const char *get_string();
6780 constant_reg::constant_reg(const char *p) : s(p)
6784 const char *constant_reg::get_string()
6786 return s;
6789 constant_int_reg::constant_int_reg(int *q) : p(q)
6793 const char *constant_int_reg::get_string()
6795 return i_to_a(*p);
6798 void abort_request()
6800 int c;
6801 if (tok.eof())
6802 c = EOF;
6803 else if (tok.newline())
6804 c = '\n';
6805 else {
6806 while ((c = get_copy(0)) == ' ')
6809 if (c == EOF || c == '\n')
6810 fputs("User Abort.", stderr);
6811 else {
6812 for (; c != '\n' && c != EOF; c = get_copy(0))
6813 fputs(asciify(c), stderr);
6815 fputc('\n', stderr);
6816 cleanup_and_exit(1);
6819 char *read_string()
6821 int len = 256;
6822 char *s = new char[len];
6823 int c;
6824 while ((c = get_copy(0)) == ' ')
6826 int i = 0;
6827 while (c != '\n' && c != EOF) {
6828 if (!invalid_input_char(c)) {
6829 if (i + 2 > len) {
6830 char *tem = s;
6831 s = new char[len*2];
6832 memcpy(s, tem, len);
6833 len *= 2;
6834 a_delete tem;
6836 s[i++] = c;
6838 c = get_copy(0);
6840 s[i] = '\0';
6841 tok.next();
6842 if (i == 0) {
6843 a_delete s;
6844 return 0;
6846 return s;
6849 void pipe_output()
6851 if (safer_flag) {
6852 error(".pi request not allowed in safer mode");
6853 skip_line();
6855 else {
6856 #ifdef POPEN_MISSING
6857 error("pipes not available on this system");
6858 skip_line();
6859 #else /* not POPEN_MISSING */
6860 if (the_output) {
6861 error("can't pipe: output already started");
6862 skip_line();
6864 else {
6865 char *pc;
6866 if ((pc = read_string()) == 0)
6867 error("can't pipe to empty command");
6868 if (pipe_command) {
6869 char *s = new char[strlen(pipe_command) + strlen(pc) + 1 + 1];
6870 strcpy(s, pipe_command);
6871 strcat(s, "|");
6872 strcat(s, pc);
6873 a_delete pipe_command;
6874 a_delete pc;
6875 pipe_command = s;
6877 else
6878 pipe_command = pc;
6880 #endif /* not POPEN_MISSING */
6884 static int system_status;
6886 void system_request()
6888 if (safer_flag) {
6889 error(".sy request not allowed in safer mode");
6890 skip_line();
6892 else {
6893 char *command = read_string();
6894 if (!command)
6895 error("empty command");
6896 else {
6897 system_status = system(command);
6898 a_delete command;
6903 void copy_file()
6905 if (curdiv == topdiv && topdiv->before_first_page) {
6906 handle_initial_request(COPY_FILE_REQUEST);
6907 return;
6909 symbol filename = get_long_name(1);
6910 while (!tok.newline() && !tok.eof())
6911 tok.next();
6912 if (break_flag)
6913 curenv->do_break();
6914 if (!filename.is_null())
6915 curdiv->copy_file(filename.contents());
6916 tok.next();
6919 #ifdef COLUMN
6921 void vjustify()
6923 if (curdiv == topdiv && topdiv->before_first_page) {
6924 handle_initial_request(VJUSTIFY_REQUEST);
6925 return;
6927 symbol type = get_long_name(1);
6928 if (!type.is_null())
6929 curdiv->vjustify(type);
6930 skip_line();
6933 #endif /* COLUMN */
6935 void transparent_file()
6937 if (curdiv == topdiv && topdiv->before_first_page) {
6938 handle_initial_request(TRANSPARENT_FILE_REQUEST);
6939 return;
6941 symbol filename = get_long_name(1);
6942 while (!tok.newline() && !tok.eof())
6943 tok.next();
6944 if (break_flag)
6945 curenv->do_break();
6946 if (!filename.is_null()) {
6947 errno = 0;
6948 FILE *fp = include_search_path.open_file_cautious(filename.contents());
6949 if (!fp)
6950 error("can't open `%1': %2", filename.contents(), strerror(errno));
6951 else {
6952 int bol = 1;
6953 for (;;) {
6954 int c = getc(fp);
6955 if (c == EOF)
6956 break;
6957 if (invalid_input_char(c))
6958 warning(WARN_INPUT, "invalid input character code %1", int(c));
6959 else {
6960 curdiv->transparent_output(c);
6961 bol = c == '\n';
6964 if (!bol)
6965 curdiv->transparent_output('\n');
6966 fclose(fp);
6969 tok.next();
6972 class page_range {
6973 int first;
6974 int last;
6975 public:
6976 page_range *next;
6977 page_range(int, int, page_range *);
6978 int contains(int n);
6981 page_range::page_range(int i, int j, page_range *p)
6982 : first(i), last(j), next(p)
6986 int page_range::contains(int n)
6988 return n >= first && (last <= 0 || n <= last);
6991 page_range *output_page_list = 0;
6993 int in_output_page_list(int n)
6995 if (!output_page_list)
6996 return 1;
6997 for (page_range *p = output_page_list; p; p = p->next)
6998 if (p->contains(n))
6999 return 1;
7000 return 0;
7003 static void parse_output_page_list(char *p)
7005 for (;;) {
7006 int i;
7007 if (*p == '-')
7008 i = 1;
7009 else if (csdigit(*p)) {
7010 i = 0;
7012 i = i*10 + *p++ - '0';
7013 while (csdigit(*p));
7015 else
7016 break;
7017 int j;
7018 if (*p == '-') {
7019 p++;
7020 j = 0;
7021 if (csdigit(*p)) {
7023 j = j*10 + *p++ - '0';
7024 while (csdigit(*p));
7027 else
7028 j = i;
7029 if (j == 0)
7030 last_page_number = -1;
7031 else if (last_page_number >= 0 && j > last_page_number)
7032 last_page_number = j;
7033 output_page_list = new page_range(i, j, output_page_list);
7034 if (*p != ',')
7035 break;
7036 ++p;
7038 if (*p != '\0') {
7039 error("bad output page list");
7040 output_page_list = 0;
7044 static FILE *open_mac_file(const char *mac, char **path)
7046 // Try first FOOBAR.tmac, then tmac.FOOBAR
7047 char *s1 = new char[strlen(mac)+strlen(MACRO_POSTFIX)+1];
7048 strcpy(s1, mac);
7049 strcat(s1, MACRO_POSTFIX);
7050 FILE *fp = mac_path->open_file(s1, path);
7051 a_delete s1;
7052 if (!fp) {
7053 char *s2 = new char[strlen(mac)+strlen(MACRO_PREFIX)+1];
7054 strcpy(s2, MACRO_PREFIX);
7055 strcat(s2, mac);
7056 fp = mac_path->open_file(s2, path);
7057 a_delete s2;
7059 return fp;
7062 static void process_macro_file(const char *mac)
7064 char *path;
7065 FILE *fp = open_mac_file(mac, &path);
7066 if (!fp)
7067 fatal("can't find macro file %1", mac);
7068 const char *s = symbol(path).contents();
7069 a_delete path;
7070 input_stack::push(new file_iterator(fp, s));
7071 tok.next();
7072 process_input_stack();
7075 static void process_startup_file(const char *filename)
7077 char *path;
7078 search_path *orig_mac_path = mac_path;
7079 mac_path = &config_macro_path;
7080 FILE *fp = mac_path->open_file(filename, &path);
7081 if (fp) {
7082 input_stack::push(new file_iterator(fp, symbol(path).contents()));
7083 a_delete path;
7084 tok.next();
7085 process_input_stack();
7087 mac_path = orig_mac_path;
7090 void macro_source()
7092 symbol nm = get_long_name(1);
7093 if (nm.is_null())
7094 skip_line();
7095 else {
7096 while (!tok.newline() && !tok.eof())
7097 tok.next();
7098 char *path;
7099 FILE *fp = mac_path->open_file(nm.contents(), &path);
7100 // .mso doesn't (and cannot) go through open_mac_file, so we
7101 // need to do it here manually: If we have tmac.FOOBAR, try
7102 // FOOBAR.tmac and vice versa
7103 if (!fp) {
7104 const char *fn = nm.contents();
7105 if (strncasecmp(fn, MACRO_PREFIX, sizeof(MACRO_PREFIX) - 1) == 0) {
7106 char *s = new char[strlen(fn) + sizeof(MACRO_POSTFIX)];
7107 strcpy(s, fn + sizeof(MACRO_PREFIX) - 1);
7108 strcat(s, MACRO_POSTFIX);
7109 fp = mac_path->open_file(s, &path);
7110 a_delete s;
7112 if (!fp) {
7113 if (strncasecmp(fn + strlen(fn) - sizeof(MACRO_POSTFIX) + 1,
7114 MACRO_POSTFIX, sizeof(MACRO_POSTFIX) - 1) == 0) {
7115 char *s = new char[strlen(fn) + sizeof(MACRO_PREFIX)];
7116 strcpy(s, MACRO_PREFIX);
7117 strncat(s, fn, strlen(fn) - sizeof(MACRO_POSTFIX) + 1);
7118 fp = mac_path->open_file(s, &path);
7119 a_delete s;
7123 if (fp) {
7124 input_stack::push(new file_iterator(fp, symbol(path).contents()));
7125 a_delete path;
7127 else
7128 error("can't find macro file `%1'", nm.contents());
7129 tok.next();
7133 static void process_input_file(const char *name)
7135 FILE *fp;
7136 if (strcmp(name, "-") == 0) {
7137 clearerr(stdin);
7138 fp = stdin;
7140 else {
7141 errno = 0;
7142 fp = include_search_path.open_file_cautious(name);
7143 if (!fp)
7144 fatal("can't open `%1': %2", name, strerror(errno));
7146 input_stack::push(new file_iterator(fp, name));
7147 tok.next();
7148 process_input_stack();
7151 // make sure the_input is empty before calling this
7153 static int evaluate_expression(const char *expr, units *res)
7155 input_stack::push(make_temp_iterator(expr));
7156 tok.next();
7157 int success = get_number(res, 'u');
7158 while (input_stack::get(0) != EOF)
7160 return success;
7163 static void do_register_assignment(const char *s)
7165 const char *p = strchr(s, '=');
7166 if (!p) {
7167 char buf[2];
7168 buf[0] = s[0];
7169 buf[1] = 0;
7170 units n;
7171 if (evaluate_expression(s + 1, &n))
7172 set_number_reg(buf, n);
7174 else {
7175 char *buf = new char[p - s + 1];
7176 memcpy(buf, s, p - s);
7177 buf[p - s] = 0;
7178 units n;
7179 if (evaluate_expression(p + 1, &n))
7180 set_number_reg(buf, n);
7181 a_delete buf;
7185 static void set_string(const char *name, const char *value)
7187 macro *m = new macro;
7188 for (const char *p = value; *p; p++)
7189 if (!invalid_input_char((unsigned char)*p))
7190 m->append(*p);
7191 request_dictionary.define(name, m);
7194 static void do_string_assignment(const char *s)
7196 const char *p = strchr(s, '=');
7197 if (!p) {
7198 char buf[2];
7199 buf[0] = s[0];
7200 buf[1] = 0;
7201 set_string(buf, s + 1);
7203 else {
7204 char *buf = new char[p - s + 1];
7205 memcpy(buf, s, p - s);
7206 buf[p - s] = 0;
7207 set_string(buf, p + 1);
7208 a_delete buf;
7212 struct string_list {
7213 const char *s;
7214 string_list *next;
7215 string_list(const char *ss) : s(ss), next(0) {}
7218 #if 0
7219 static void prepend_string(const char *s, string_list **p)
7221 string_list *l = new string_list(s);
7222 l->next = *p;
7223 *p = l;
7225 #endif
7227 static void add_string(const char *s, string_list **p)
7229 while (*p)
7230 p = &((*p)->next);
7231 *p = new string_list(s);
7234 void usage(FILE *stream, const char *prog)
7236 fprintf(stream,
7237 "usage: %s -abcivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n"
7238 " -rcn -Tname -Fdir -Idir -Mdir [files...]\n",
7239 prog);
7242 int main(int argc, char **argv)
7244 program_name = argv[0];
7245 static char stderr_buf[BUFSIZ];
7246 setbuf(stderr, stderr_buf);
7247 int c;
7248 string_list *macros = 0;
7249 string_list *register_assignments = 0;
7250 string_list *string_assignments = 0;
7251 int iflag = 0;
7252 int tflag = 0;
7253 int fflag = 0;
7254 int nflag = 0;
7255 int no_rc = 0; // don't process troffrc and troffrc-end
7256 int next_page_number = 0; // pacify compiler
7257 opterr = 0;
7258 hresolution = vresolution = 1;
7259 // restore $PATH if called from groff
7260 char* groff_path = getenv("GROFF_PATH__");
7261 if (groff_path) {
7262 string e = "PATH";
7263 e += '=';
7264 if (*groff_path)
7265 e += groff_path;
7266 e += '\0';
7267 if (putenv(strsave(e.contents())))
7268 fatal("putenv failed");
7270 static const struct option long_options[] = {
7271 { "help", no_argument, 0, CHAR_MAX + 1 },
7272 { "version", no_argument, 0, 'v' },
7273 { 0, 0, 0, 0 }
7275 #if defined(DEBUGGING)
7276 #define DEBUG_OPTION "D"
7277 #endif
7278 while ((c = getopt_long(argc, argv,
7279 "abciI:vw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU"
7280 DEBUG_OPTION, long_options, 0))
7281 != EOF)
7282 switch(c) {
7283 case 'v':
7285 printf("GNU troff (groff) version %s\n", Version_string);
7286 exit(0);
7287 break;
7289 case 'I':
7290 // Search path for .psbb files
7291 // and most other non-system input files.
7292 include_search_path.command_line_dir(optarg);
7293 break;
7294 case 'T':
7295 device = optarg;
7296 tflag = 1;
7297 is_html = (strcmp(device, "html") == 0);
7298 break;
7299 case 'C':
7300 compatible_flag = 1;
7301 // fall through
7302 case 'c':
7303 color_flag = 0;
7304 break;
7305 case 'M':
7306 macro_path.command_line_dir(optarg);
7307 safer_macro_path.command_line_dir(optarg);
7308 config_macro_path.command_line_dir(optarg);
7309 break;
7310 case 'F':
7311 font::command_line_font_dir(optarg);
7312 break;
7313 case 'm':
7314 add_string(optarg, &macros);
7315 break;
7316 case 'E':
7317 inhibit_errors = 1;
7318 break;
7319 case 'R':
7320 no_rc = 1;
7321 break;
7322 case 'w':
7323 enable_warning(optarg);
7324 break;
7325 case 'W':
7326 disable_warning(optarg);
7327 break;
7328 case 'i':
7329 iflag = 1;
7330 break;
7331 case 'b':
7332 backtrace_flag = 1;
7333 break;
7334 case 'a':
7335 ascii_output_flag = 1;
7336 break;
7337 case 'z':
7338 suppress_output_flag = 1;
7339 break;
7340 case 'n':
7341 if (sscanf(optarg, "%d", &next_page_number) == 1)
7342 nflag++;
7343 else
7344 error("bad page number");
7345 break;
7346 case 'o':
7347 parse_output_page_list(optarg);
7348 break;
7349 case 'd':
7350 if (*optarg == '\0')
7351 error("`-d' requires non-empty argument");
7352 else
7353 add_string(optarg, &string_assignments);
7354 break;
7355 case 'r':
7356 if (*optarg == '\0')
7357 error("`-r' requires non-empty argument");
7358 else
7359 add_string(optarg, &register_assignments);
7360 break;
7361 case 'f':
7362 default_family = symbol(optarg);
7363 fflag = 1;
7364 break;
7365 case 'q':
7366 case 's':
7367 case 't':
7368 // silently ignore these
7369 break;
7370 case 'U':
7371 safer_flag = 0; // unsafe behaviour
7372 break;
7373 #if defined(DEBUGGING)
7374 case 'D':
7375 debug_state = 1;
7376 break;
7377 #endif
7378 case CHAR_MAX + 1: // --help
7379 usage(stdout, argv[0]);
7380 exit(0);
7381 break;
7382 case '?':
7383 usage(stderr, argv[0]);
7384 exit(1);
7385 break; // never reached
7386 default:
7387 assert(0);
7389 if (!safer_flag)
7390 mac_path = &macro_path;
7391 set_string(".T", device);
7392 init_charset_table();
7393 init_hpf_code_table();
7394 if (!font::load_desc())
7395 fatal("sorry, I can't continue");
7396 units_per_inch = font::res;
7397 hresolution = font::hor;
7398 vresolution = font::vert;
7399 sizescale = font::sizescale;
7400 tcommand_flag = font::tcommand;
7401 warn_scale = (double)units_per_inch;
7402 warn_scaling_indicator = 'i';
7403 if (!fflag && font::family != 0 && *font::family != '\0')
7404 default_family = symbol(font::family);
7405 font_size::init_size_table(font::sizes);
7406 int i;
7407 int j = 1;
7408 if (font::style_table) {
7409 for (i = 0; font::style_table[i]; i++)
7410 mount_style(j++, symbol(font::style_table[i]));
7412 for (i = 0; font::font_name_table[i]; i++, j++)
7413 // In the DESC file a font name of 0 (zero) means leave this
7414 // position empty.
7415 if (strcmp(font::font_name_table[i], "0") != 0)
7416 mount_font(j, symbol(font::font_name_table[i]));
7417 curdiv = topdiv = new top_level_diversion;
7418 if (nflag)
7419 topdiv->set_next_page_number(next_page_number);
7420 init_input_requests();
7421 init_env_requests();
7422 init_div_requests();
7423 #ifdef COLUMN
7424 init_column_requests();
7425 #endif /* COLUMN */
7426 init_node_requests();
7427 number_reg_dictionary.define(".T", new constant_reg(tflag ? "1" : "0"));
7428 init_registers();
7429 init_reg_requests();
7430 init_hyphen_requests();
7431 init_environments();
7432 while (string_assignments) {
7433 do_string_assignment(string_assignments->s);
7434 string_list *tem = string_assignments;
7435 string_assignments = string_assignments->next;
7436 delete tem;
7438 while (register_assignments) {
7439 do_register_assignment(register_assignments->s);
7440 string_list *tem = register_assignments;
7441 register_assignments = register_assignments->next;
7442 delete tem;
7444 if (!no_rc)
7445 process_startup_file(INITIAL_STARTUP_FILE);
7446 while (macros) {
7447 process_macro_file(macros->s);
7448 string_list *tem = macros;
7449 macros = macros->next;
7450 delete tem;
7452 if (!no_rc)
7453 process_startup_file(FINAL_STARTUP_FILE);
7454 for (i = optind; i < argc; i++)
7455 process_input_file(argv[i]);
7456 if (optind >= argc || iflag)
7457 process_input_file("-");
7458 exit_troff();
7459 return 0; // not reached
7462 void warn_request()
7464 int n;
7465 if (has_arg() && get_integer(&n)) {
7466 if (n & ~WARN_TOTAL) {
7467 warning(WARN_RANGE, "warning mask must be between 0 and %1", WARN_TOTAL);
7468 n &= WARN_TOTAL;
7470 warning_mask = n;
7472 else
7473 warning_mask = WARN_TOTAL;
7474 skip_line();
7477 static void init_registers()
7479 #ifdef LONG_FOR_TIME_T
7480 long
7481 #else /* not LONG_FOR_TIME_T */
7482 time_t
7483 #endif /* not LONG_FOR_TIME_T */
7484 t = time(0);
7485 // Use struct here to work around misfeature in old versions of g++.
7486 struct tm *tt = localtime(&t);
7487 set_number_reg("seconds", int(tt->tm_sec));
7488 set_number_reg("minutes", int(tt->tm_min));
7489 set_number_reg("hours", int(tt->tm_hour));
7490 set_number_reg("dw", int(tt->tm_wday + 1));
7491 set_number_reg("dy", int(tt->tm_mday));
7492 set_number_reg("mo", int(tt->tm_mon + 1));
7493 set_number_reg("year", int(1900 + tt->tm_year));
7494 set_number_reg("yr", int(tt->tm_year));
7495 set_number_reg("$$", getpid());
7496 number_reg_dictionary.define(".A",
7497 new constant_reg(ascii_output_flag
7498 ? "1"
7499 : "0"));
7503 * registers associated with \O
7506 static int output_reg_minx_contents = -1;
7507 static int output_reg_miny_contents = -1;
7508 static int output_reg_maxx_contents = -1;
7509 static int output_reg_maxy_contents = -1;
7511 void check_output_limits(int x, int y)
7513 if ((output_reg_minx_contents == -1) || (x < output_reg_minx_contents))
7514 output_reg_minx_contents = x;
7515 if (x > output_reg_maxx_contents)
7516 output_reg_maxx_contents = x;
7517 if ((output_reg_miny_contents == -1) || (y < output_reg_miny_contents))
7518 output_reg_miny_contents = y;
7519 if (y > output_reg_maxy_contents)
7520 output_reg_maxy_contents = y;
7523 void reset_output_registers()
7525 output_reg_minx_contents = -1;
7526 output_reg_miny_contents = -1;
7527 output_reg_maxx_contents = -1;
7528 output_reg_maxy_contents = -1;
7531 void get_output_registers(int *minx, int *miny, int *maxx, int *maxy)
7533 *minx = output_reg_minx_contents;
7534 *miny = output_reg_miny_contents;
7535 *maxx = output_reg_maxx_contents;
7536 *maxy = output_reg_maxy_contents;
7539 void init_input_requests()
7541 init_request("ab", abort_request);
7542 init_request("als", alias_macro);
7543 init_request("am", append_macro);
7544 init_request("am1", append_nocomp_macro);
7545 init_request("ami", append_indirect_macro);
7546 init_request("ami1", append_indirect_nocomp_macro);
7547 init_request("as", append_string);
7548 init_request("as1", append_nocomp_string);
7549 init_request("asciify", asciify_macro);
7550 init_request("backtrace", backtrace_request);
7551 init_request("blm", blank_line_macro);
7552 init_request("break", while_break_request);
7553 init_request("cf", copy_file);
7554 init_request("cflags", char_flags);
7555 init_request("char", define_character);
7556 init_request("chop", chop_macro);
7557 init_request("close", close_request);
7558 init_request("color", activate_color);
7559 init_request("composite", composite_request);
7560 init_request("continue", while_continue_request);
7561 init_request("cp", compatible);
7562 init_request("de", define_macro);
7563 init_request("de1", define_nocomp_macro);
7564 init_request("defcolor", define_color);
7565 init_request("dei", define_indirect_macro);
7566 init_request("dei1", define_indirect_nocomp_macro);
7567 init_request("do", do_request);
7568 init_request("ds", define_string);
7569 init_request("ds1", define_nocomp_string);
7570 init_request("ec", set_escape_char);
7571 init_request("ecr", restore_escape_char);
7572 init_request("ecs", save_escape_char);
7573 init_request("el", else_request);
7574 init_request("em", end_macro);
7575 init_request("eo", escape_off);
7576 init_request("ex", exit_request);
7577 init_request("fchar", define_fallback_character);
7578 #ifdef WIDOW_CONTROL
7579 init_request("fpl", flush_pending_lines);
7580 #endif /* WIDOW_CONTROL */
7581 init_request("hcode", hyphenation_code);
7582 init_request("hpfcode", hyphenation_patterns_file_code);
7583 init_request("ie", if_else_request);
7584 init_request("if", if_request);
7585 init_request("ig", ignore);
7586 init_request("length", length_request);
7587 init_request("lf", line_file);
7588 init_request("mso", macro_source);
7589 init_request("nop", nop_request);
7590 init_request("nroff", nroff_request);
7591 init_request("nx", next_file);
7592 init_request("open", open_request);
7593 init_request("opena", opena_request);
7594 init_request("output", output_request);
7595 init_request("pc", set_page_character);
7596 init_request("pi", pipe_output);
7597 init_request("pm", print_macros);
7598 init_request("psbb", ps_bbox_request);
7599 #ifndef POPEN_MISSING
7600 init_request("pso", pipe_source);
7601 #endif /* not POPEN_MISSING */
7602 init_request("rchar", remove_character);
7603 init_request("rd", read_request);
7604 init_request("return", return_macro_request);
7605 init_request("rm", remove_macro);
7606 init_request("rn", rename_macro);
7607 init_request("schar", define_special_character);
7608 init_request("shift", shift);
7609 init_request("so", source);
7610 init_request("spreadwarn", spreadwarn_request);
7611 init_request("substring", substring_request);
7612 init_request("sy", system_request);
7613 init_request("tag", tag);
7614 init_request("taga", taga);
7615 init_request("tm", terminal);
7616 init_request("tm1", terminal1);
7617 init_request("tmc", terminal_continue);
7618 init_request("tr", translate);
7619 init_request("trf", transparent_file);
7620 init_request("trin", translate_input);
7621 init_request("trnt", translate_no_transparent);
7622 init_request("troff", troff_request);
7623 init_request("unformat", unformat_macro);
7624 #ifdef COLUMN
7625 init_request("vj", vjustify);
7626 #endif /* COLUMN */
7627 init_request("warn", warn_request);
7628 init_request("warnscale", warnscale_request);
7629 init_request("while", while_request);
7630 init_request("write", write_request);
7631 init_request("writec", write_request_continue);
7632 init_request("writem", write_macro_request);
7633 number_reg_dictionary.define(".$", new nargs_reg);
7634 number_reg_dictionary.define(".C", new constant_int_reg(&compatible_flag));
7635 number_reg_dictionary.define(".c", new lineno_reg);
7636 number_reg_dictionary.define(".color", new constant_int_reg(&color_flag));
7637 number_reg_dictionary.define(".F", new filename_reg);
7638 number_reg_dictionary.define(".g", new constant_reg("1"));
7639 number_reg_dictionary.define(".H", new constant_int_reg(&hresolution));
7640 number_reg_dictionary.define(".R", new constant_reg("10000"));
7641 number_reg_dictionary.define(".U", new constant_int_reg(&safer_flag));
7642 number_reg_dictionary.define(".V", new constant_int_reg(&vresolution));
7643 number_reg_dictionary.define(".warn", new constant_int_reg(&warning_mask));
7644 extern const char *major_version;
7645 number_reg_dictionary.define(".x", new constant_reg(major_version));
7646 extern const char *revision;
7647 number_reg_dictionary.define(".Y", new constant_reg(revision));
7648 extern const char *minor_version;
7649 number_reg_dictionary.define(".y", new constant_reg(minor_version));
7650 number_reg_dictionary.define("c.", new writable_lineno_reg);
7651 number_reg_dictionary.define("llx", new variable_reg(&llx_reg_contents));
7652 number_reg_dictionary.define("lly", new variable_reg(&lly_reg_contents));
7653 number_reg_dictionary.define("opmaxx",
7654 new variable_reg(&output_reg_maxx_contents));
7655 number_reg_dictionary.define("opmaxy",
7656 new variable_reg(&output_reg_maxy_contents));
7657 number_reg_dictionary.define("opminx",
7658 new variable_reg(&output_reg_minx_contents));
7659 number_reg_dictionary.define("opminy",
7660 new variable_reg(&output_reg_miny_contents));
7661 number_reg_dictionary.define("slimit",
7662 new variable_reg(&input_stack::limit));
7663 number_reg_dictionary.define("systat", new variable_reg(&system_status));
7664 number_reg_dictionary.define("urx", new variable_reg(&urx_reg_contents));
7665 number_reg_dictionary.define("ury", new variable_reg(&ury_reg_contents));
7668 object_dictionary request_dictionary(501);
7670 void init_request(const char *s, REQUEST_FUNCP f)
7672 request_dictionary.define(s, new request(f));
7675 static request_or_macro *lookup_request(symbol nm)
7677 assert(!nm.is_null());
7678 request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm);
7679 if (p == 0) {
7680 warning(WARN_MAC, "macro `%1' not defined", nm.contents());
7681 p = new macro;
7682 request_dictionary.define(nm, p);
7684 return p;
7687 node *charinfo_to_node_list(charinfo *ci, const environment *envp)
7689 // Don't interpret character definitions in compatible mode.
7690 int old_compatible_flag = compatible_flag;
7691 compatible_flag = 0;
7692 int old_escape_char = escape_char;
7693 escape_char = '\\';
7694 macro *mac = ci->set_macro(0);
7695 assert(mac != 0);
7696 environment *oldenv = curenv;
7697 environment env(envp);
7698 curenv = &env;
7699 curenv->set_composite();
7700 token old_tok = tok;
7701 input_stack::add_boundary();
7702 string_iterator *si =
7703 new string_iterator(*mac, "composite character", ci->nm);
7704 input_stack::push(si);
7705 // we don't use process_input_stack, because we don't want to recognise
7706 // requests
7707 for (;;) {
7708 tok.next();
7709 if (tok.eof())
7710 break;
7711 if (tok.newline()) {
7712 error("composite character mustn't contain newline");
7713 while (!tok.eof())
7714 tok.next();
7715 break;
7717 else
7718 tok.process();
7720 node *n = curenv->extract_output_line();
7721 input_stack::remove_boundary();
7722 ci->set_macro(mac);
7723 tok = old_tok;
7724 curenv = oldenv;
7725 compatible_flag = old_compatible_flag;
7726 escape_char = old_escape_char;
7727 have_input = 0;
7728 return n;
7731 static node *read_draw_node()
7733 token start;
7734 start.next();
7735 if (!start.delimiter(1)){
7736 do {
7737 tok.next();
7738 } while (tok != start && !tok.newline() && !tok.eof());
7740 else {
7741 tok.next();
7742 if (tok == start)
7743 error("missing argument");
7744 else {
7745 unsigned char type = tok.ch();
7746 if (type == 'F') {
7747 read_color_draw_node(start);
7748 return 0;
7750 tok.next();
7751 int maxpoints = 10;
7752 hvpair *point = new hvpair[maxpoints];
7753 int npoints = 0;
7754 int no_last_v = 0;
7755 int err = 0;
7756 int i;
7757 for (i = 0; tok != start; i++) {
7758 if (i == maxpoints) {
7759 hvpair *oldpoint = point;
7760 point = new hvpair[maxpoints*2];
7761 for (int j = 0; j < maxpoints; j++)
7762 point[j] = oldpoint[j];
7763 maxpoints *= 2;
7764 a_delete oldpoint;
7766 if (!get_hunits(&point[i].h,
7767 type == 'f' || type == 't' ? 'u' : 'm')) {
7768 err = 1;
7769 break;
7771 ++npoints;
7772 tok.skip();
7773 point[i].v = V0;
7774 if (tok == start) {
7775 no_last_v = 1;
7776 break;
7778 if (!get_vunits(&point[i].v, 'v')) {
7779 err = 1;
7780 break;
7782 tok.skip();
7784 while (tok != start && !tok.newline() && !tok.eof())
7785 tok.next();
7786 if (!err) {
7787 switch (type) {
7788 case 'l':
7789 if (npoints != 1 || no_last_v) {
7790 error("two arguments needed for line");
7791 npoints = 1;
7793 break;
7794 case 'c':
7795 if (npoints != 1 || !no_last_v) {
7796 error("one argument needed for circle");
7797 npoints = 1;
7798 point[0].v = V0;
7800 break;
7801 case 'e':
7802 if (npoints != 1 || no_last_v) {
7803 error("two arguments needed for ellipse");
7804 npoints = 1;
7806 break;
7807 case 'a':
7808 if (npoints != 2 || no_last_v) {
7809 error("four arguments needed for arc");
7810 npoints = 2;
7812 break;
7813 case '~':
7814 if (no_last_v)
7815 error("even number of arguments needed for spline");
7816 break;
7817 case 'f':
7818 if (npoints != 1 || !no_last_v) {
7819 error("one argument needed for gray shade");
7820 npoints = 1;
7821 point[0].v = V0;
7823 default:
7824 // silently pass it through
7825 break;
7827 draw_node *dn = new draw_node(type, point, npoints,
7828 curenv->get_font_size(),
7829 curenv->get_glyph_color(),
7830 curenv->get_fill_color());
7831 a_delete point;
7832 return dn;
7834 else {
7835 a_delete point;
7839 return 0;
7842 static void read_color_draw_node(token &start)
7844 tok.next();
7845 if (tok == start) {
7846 error("missing color scheme");
7847 return;
7849 unsigned char scheme = tok.ch();
7850 tok.next();
7851 color *col = 0;
7852 char end = start.ch();
7853 switch (scheme) {
7854 case 'c':
7855 col = read_cmy(end);
7856 break;
7857 case 'd':
7858 col = &default_color;
7859 break;
7860 case 'g':
7861 col = read_gray(end);
7862 break;
7863 case 'k':
7864 col = read_cmyk(end);
7865 break;
7866 case 'r':
7867 col = read_rgb(end);
7868 break;
7870 if (col)
7871 curenv->set_fill_color(col);
7872 while (tok != start) {
7873 if (tok.newline() || tok.eof()) {
7874 warning(WARN_DELIM, "missing closing delimiter");
7875 input_stack::push(make_temp_iterator("\n"));
7876 break;
7878 tok.next();
7880 have_input = 1;
7883 static struct {
7884 const char *name;
7885 int mask;
7886 } warning_table[] = {
7887 { "char", WARN_CHAR },
7888 { "range", WARN_RANGE },
7889 { "break", WARN_BREAK },
7890 { "delim", WARN_DELIM },
7891 { "el", WARN_EL },
7892 { "scale", WARN_SCALE },
7893 { "number", WARN_NUMBER },
7894 { "syntax", WARN_SYNTAX },
7895 { "tab", WARN_TAB },
7896 { "right-brace", WARN_RIGHT_BRACE },
7897 { "missing", WARN_MISSING },
7898 { "input", WARN_INPUT },
7899 { "escape", WARN_ESCAPE },
7900 { "space", WARN_SPACE },
7901 { "font", WARN_FONT },
7902 { "di", WARN_DI },
7903 { "mac", WARN_MAC },
7904 { "reg", WARN_REG },
7905 { "ig", WARN_IG },
7906 { "color", WARN_COLOR },
7907 { "all", WARN_TOTAL & ~(WARN_DI | WARN_MAC | WARN_REG) },
7908 { "w", WARN_TOTAL },
7909 { "default", DEFAULT_WARNING_MASK },
7912 static int lookup_warning(const char *name)
7914 for (unsigned int i = 0;
7915 i < sizeof(warning_table)/sizeof(warning_table[0]);
7916 i++)
7917 if (strcmp(name, warning_table[i].name) == 0)
7918 return warning_table[i].mask;
7919 return 0;
7922 static void enable_warning(const char *name)
7924 int mask = lookup_warning(name);
7925 if (mask)
7926 warning_mask |= mask;
7927 else
7928 error("unknown warning `%1'", name);
7931 static void disable_warning(const char *name)
7933 int mask = lookup_warning(name);
7934 if (mask)
7935 warning_mask &= ~mask;
7936 else
7937 error("unknown warning `%1'", name);
7940 static void copy_mode_error(const char *format,
7941 const errarg &arg1,
7942 const errarg &arg2,
7943 const errarg &arg3)
7945 if (ignoring) {
7946 static const char prefix[] = "(in ignored input) ";
7947 char *s = new char[sizeof(prefix) + strlen(format)];
7948 strcpy(s, prefix);
7949 strcat(s, format);
7950 warning(WARN_IG, s, arg1, arg2, arg3);
7951 a_delete s;
7953 else
7954 error(format, arg1, arg2, arg3);
7957 enum error_type { WARNING, OUTPUT_WARNING, ERROR, FATAL };
7959 static void do_error(error_type type,
7960 const char *format,
7961 const errarg &arg1,
7962 const errarg &arg2,
7963 const errarg &arg3)
7965 const char *filename;
7966 int lineno;
7967 if (inhibit_errors && type < FATAL)
7968 return;
7969 if (backtrace_flag)
7970 input_stack::backtrace();
7971 if (!get_file_line(&filename, &lineno))
7972 filename = 0;
7973 if (filename)
7974 errprint("%1:%2: ", filename, lineno);
7975 else if (program_name)
7976 fprintf(stderr, "%s: ", program_name);
7977 switch (type) {
7978 case FATAL:
7979 fputs("fatal error: ", stderr);
7980 break;
7981 case ERROR:
7982 break;
7983 case WARNING:
7984 fputs("warning: ", stderr);
7985 break;
7986 case OUTPUT_WARNING:
7987 double fromtop = topdiv->get_vertical_position().to_units() / warn_scale;
7988 fprintf(stderr, "warning [p %d, %.1f%c",
7989 topdiv->get_page_number(), fromtop, warn_scaling_indicator);
7990 if (topdiv != curdiv) {
7991 double fromtop1 = curdiv->get_vertical_position().to_units()
7992 / warn_scale;
7993 fprintf(stderr, ", div `%s', %.1f%c",
7994 curdiv->get_diversion_name(), fromtop1, warn_scaling_indicator);
7996 fprintf(stderr, "]: ");
7997 break;
7999 errprint(format, arg1, arg2, arg3);
8000 fputc('\n', stderr);
8001 fflush(stderr);
8002 if (type == FATAL)
8003 cleanup_and_exit(1);
8006 int warning(warning_type t,
8007 const char *format,
8008 const errarg &arg1,
8009 const errarg &arg2,
8010 const errarg &arg3)
8012 if ((t & warning_mask) != 0) {
8013 do_error(WARNING, format, arg1, arg2, arg3);
8014 return 1;
8016 else
8017 return 0;
8020 int output_warning(warning_type t,
8021 const char *format,
8022 const errarg &arg1,
8023 const errarg &arg2,
8024 const errarg &arg3)
8026 if ((t & warning_mask) != 0) {
8027 do_error(OUTPUT_WARNING, format, arg1, arg2, arg3);
8028 return 1;
8030 else
8031 return 0;
8034 void error(const char *format,
8035 const errarg &arg1,
8036 const errarg &arg2,
8037 const errarg &arg3)
8039 do_error(ERROR, format, arg1, arg2, arg3);
8042 void fatal(const char *format,
8043 const errarg &arg1,
8044 const errarg &arg2,
8045 const errarg &arg3)
8047 do_error(FATAL, format, arg1, arg2, arg3);
8050 void fatal_with_file_and_line(const char *filename, int lineno,
8051 const char *format,
8052 const errarg &arg1,
8053 const errarg &arg2,
8054 const errarg &arg3)
8056 fprintf(stderr, "%s:%d: fatal error: ", filename, lineno);
8057 errprint(format, arg1, arg2, arg3);
8058 fputc('\n', stderr);
8059 fflush(stderr);
8060 cleanup_and_exit(1);
8063 void error_with_file_and_line(const char *filename, int lineno,
8064 const char *format,
8065 const errarg &arg1,
8066 const errarg &arg2,
8067 const errarg &arg3)
8069 fprintf(stderr, "%s:%d: error: ", filename, lineno);
8070 errprint(format, arg1, arg2, arg3);
8071 fputc('\n', stderr);
8072 fflush(stderr);
8075 dictionary charinfo_dictionary(501);
8077 charinfo *get_charinfo(symbol nm)
8079 void *p = charinfo_dictionary.lookup(nm);
8080 if (p != 0)
8081 return (charinfo *)p;
8082 charinfo *cp = new charinfo(nm);
8083 (void)charinfo_dictionary.lookup(nm, cp);
8084 return cp;
8087 int charinfo::next_index = 0;
8089 charinfo::charinfo(symbol s)
8090 : translation(0), mac(0), special_translation(TRANSLATE_NONE),
8091 hyphenation_code(0), flags(0), ascii_code(0), asciify_code(0),
8092 not_found(0), transparent_translate(1), translate_input(0),
8093 mode(CHAR_NORMAL), nm(s)
8095 index = next_index++;
8098 void charinfo::set_hyphenation_code(unsigned char c)
8100 hyphenation_code = c;
8103 void charinfo::set_translation(charinfo *ci, int tt, int ti)
8105 translation = ci;
8106 if (ci && ti) {
8107 if (hyphenation_code != 0)
8108 ci->set_hyphenation_code(hyphenation_code);
8109 if (asciify_code != 0)
8110 ci->set_asciify_code(asciify_code);
8111 else if (ascii_code != 0)
8112 ci->set_asciify_code(ascii_code);
8113 ci->set_translation_input();
8115 special_translation = TRANSLATE_NONE;
8116 transparent_translate = tt;
8119 void charinfo::set_special_translation(int c, int tt)
8121 special_translation = c;
8122 translation = 0;
8123 transparent_translate = tt;
8126 void charinfo::set_ascii_code(unsigned char c)
8128 ascii_code = c;
8131 void charinfo::set_asciify_code(unsigned char c)
8133 asciify_code = c;
8136 macro *charinfo::set_macro(macro *m)
8138 macro *tem = mac;
8139 mac = m;
8140 return tem;
8143 macro *charinfo::setx_macro(macro *m, char_mode cm)
8145 macro *tem = mac;
8146 mac = m;
8147 mode = cm;
8148 return tem;
8151 void charinfo::set_number(int n)
8153 number = n;
8154 flags |= NUMBERED;
8157 int charinfo::get_number()
8159 assert(flags & NUMBERED);
8160 return number;
8163 symbol UNNAMED_SYMBOL("---");
8165 // For numbered characters not between 0 and 255, we make a symbol out
8166 // of the number and store them in this dictionary.
8168 dictionary numbered_charinfo_dictionary(11);
8170 charinfo *get_charinfo_by_number(int n)
8172 static charinfo *number_table[256];
8174 if (n >= 0 && n < 256) {
8175 charinfo *ci = number_table[n];
8176 if (!ci) {
8177 ci = new charinfo(UNNAMED_SYMBOL);
8178 ci->set_number(n);
8179 number_table[n] = ci;
8181 return ci;
8183 else {
8184 symbol ns(i_to_a(n));
8185 charinfo *ci = (charinfo *)numbered_charinfo_dictionary.lookup(ns);
8186 if (!ci) {
8187 ci = new charinfo(UNNAMED_SYMBOL);
8188 ci->set_number(n);
8189 (void)numbered_charinfo_dictionary.lookup(ns, ci);
8191 return ci;
8195 int font::name_to_index(const char *nm)
8197 charinfo *ci;
8198 if (nm[1] == 0)
8199 ci = charset_table[nm[0] & 0xff];
8200 else if (nm[0] == '\\' && nm[2] == 0)
8201 ci = get_charinfo(symbol(nm + 1));
8202 else
8203 ci = get_charinfo(symbol(nm));
8204 if (ci == 0)
8205 return -1;
8206 else
8207 return ci->get_index();
8210 int font::number_to_index(int n)
8212 return get_charinfo_by_number(n)->get_index();