Remove no longer needed catman periodic via 'make upgrade'.
[dragonfly.git] / contrib / groff / src / roff / troff / input.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004, 2005,
3                  2006, 2007, 2008, 2009
4    Free Software Foundation, Inc.
5      Written by James Clark (jjc@jclark.com)
6
7 This file is part of groff.
8
9 groff is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 groff is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
17 for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>. */
21
22 #define DEBUGGING
23
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 "font.h"
36 #include "charinfo.h"
37 #include "macropath.h"
38 #include "input.h"
39 #include "defs.h"
40 #include "unicode.h"
41
42 // Needed for getpid() and isatty()
43 #include "posix.h"
44
45 #include "nonposix.h"
46
47 #ifdef NEED_DECLARATION_PUTENV
48 extern "C" {
49   int putenv(const char *);
50 }
51 #endif /* NEED_DECLARATION_PUTENV */
52
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
58
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
64
65 // initial size of buffer for reading names; expanded as necessary
66 #define ABUF_SIZE 16
67
68 extern "C" const char *program_name;
69 extern "C" const char *Version_string;
70
71 #ifdef COLUMN
72 void init_column_requests();
73 #endif /* COLUMN */
74
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();
83
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];
93
94 static int warning_mask = DEFAULT_WARNING_MASK;
95 static int inhibit_errors = 0;
96 static int ignoring = 0;
97
98 static void enable_warning(const char *);
99 static void disable_warning(const char *);
100
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
109
110 int have_input = 0;             // whether \f, \F, \D'F...', \H, \m, \M,
111                                 // \O[345], \R, \s, or \S has been processed
112                                 // in token::next()
113 int old_have_input = 0;         // value of have_input right before \n
114 int tcommand_flag = 0;
115 int unsafe_flag = 0;            // safer by default
116
117 int have_string_arg = 0;        // whether we have \*[foo bar...]
118
119 double spread_limit = -3.0 - 1.0;       // negative means deactivated
120
121 double warn_scale;
122 char warn_scaling_indicator;
123 int debug_state = 0;            // turns on debugging of the html troff state
124
125 search_path *mac_path = &safer_macro_path;
126
127 // Defaults to the current directory.
128 search_path include_search_path(0, 0, 0, 1);
129
130 static int get_copy(node**, int = 0, 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);
135
136 enum read_mode { ALLOW_EMPTY, WITH_ARGS, NO_ARGS };
137 static symbol read_escape_name(read_mode = NO_ARGS);
138 static symbol read_long_escape_name(read_mode = NO_ARGS);
139 static void interpolate_string(symbol);
140 static void interpolate_string_with_args(symbol);
141 static void interpolate_macro(symbol, int = 0);
142 static void interpolate_number_format(symbol);
143 static void interpolate_environment_variable(symbol);
144
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();
156
157 class input_iterator;
158 input_iterator *make_temp_iterator(const char *);
159 const char *input_char_description(int);
160
161 void process_input_stack();
162 void chop_macro();              // declare to avoid friend name injection
163
164
165 void set_escape_char()
166 {
167   if (has_arg()) {
168     if (tok.ch() == 0) {
169       error("bad escape character");
170       escape_char = '\\';
171     }
172     else
173       escape_char = tok.ch();
174   }
175   else
176     escape_char = '\\';
177   skip_line();
178 }
179
180 void escape_off()
181 {
182   escape_char = 0;
183   skip_line();
184 }
185
186 static int saved_escape_char = '\\';
187
188 void save_escape_char()
189 {
190   saved_escape_char = escape_char;
191   skip_line();
192 }
193
194 void restore_escape_char()
195 {
196   escape_char = saved_escape_char;
197   skip_line();
198 }
199
200 struct arg_list;
201
202 class input_iterator {
203 public:
204   input_iterator();
205   input_iterator(int is_div);
206   virtual ~input_iterator() {}
207   int get(node **);
208   friend class input_stack;
209   int is_diversion;
210   statem *diversion_state;
211 protected:
212   const unsigned char *ptr;
213   const unsigned char *eptr;
214   input_iterator *next;
215 private:
216   virtual int fill(node **);
217   virtual int peek();
218   virtual int has_args() { return 0; }
219   virtual int nargs() { return 0; }
220   virtual input_iterator *get_arg(int) { return 0; }
221   virtual arg_list *get_arg_list() { return 0; }
222   virtual symbol get_macro_name() { return NULL_SYMBOL; }
223   virtual int space_follows_arg(int) { return 0; }
224   virtual int get_break_flag() { return 0; }
225   virtual int get_location(int, const char **, int *) { return 0; }
226   virtual void backtrace() {}
227   virtual int set_location(const char *, int) { return 0; }
228   virtual int next_file(FILE *, const char *) { return 0; }
229   virtual void shift(int) {}
230   virtual int is_boundary() {return 0; }
231   virtual int is_file() { return 0; }
232   virtual int is_macro() { return 0; }
233   virtual void save_compatible_flag(int) {}
234   virtual int get_compatible_flag() { return 0; }
235 };
236
237 input_iterator::input_iterator()
238 : is_diversion(0), ptr(0), eptr(0)
239 {
240 }
241
242 input_iterator::input_iterator(int is_div)
243 : is_diversion(is_div), ptr(0), eptr(0)
244 {
245 }
246
247 int input_iterator::fill(node **)
248 {
249   return EOF;
250 }
251
252 int input_iterator::peek()
253 {
254   return EOF;
255 }
256
257 inline int input_iterator::get(node **p)
258 {
259   return ptr < eptr ? *ptr++ : fill(p);
260 }
261
262 class input_boundary : public input_iterator {
263 public:
264   int is_boundary() { return 1; }
265 };
266
267 class input_return_boundary : public input_iterator {
268 public:
269   int is_boundary() { return 2; }
270 };
271
272 class file_iterator : public input_iterator {
273   FILE *fp;
274   int lineno;
275   const char *filename;
276   int popened;
277   int newline_flag;
278   int seen_escape;
279   enum { BUF_SIZE = 512 };
280   unsigned char buf[BUF_SIZE];
281   void close();
282 public:
283   file_iterator(FILE *, const char *, int = 0);
284   ~file_iterator();
285   int fill(node **);
286   int peek();
287   int get_location(int, const char **, int *);
288   void backtrace();
289   int set_location(const char *, int);
290   int next_file(FILE *, const char *);
291   int is_file();
292 };
293
294 file_iterator::file_iterator(FILE *f, const char *fn, int po)
295 : fp(f), lineno(1), filename(fn), popened(po),
296   newline_flag(0), seen_escape(0)
297 {
298   if ((font::use_charnames_in_special) && (fn != 0)) {
299     if (!the_output)
300       init_output();
301     the_output->put_filename(fn, po);
302   }
303 }
304
305 file_iterator::~file_iterator()
306 {
307   close();
308 }
309
310 void file_iterator::close()
311 {
312   if (fp == stdin)
313     clearerr(stdin);
314 #ifndef POPEN_MISSING
315   else if (popened)
316     pclose(fp);
317 #endif /* not POPEN_MISSING */
318   else
319     fclose(fp);
320 }
321
322 int file_iterator::is_file()
323 {
324   return 1;
325 }
326
327 int file_iterator::next_file(FILE *f, const char *s)
328 {
329   close();
330   filename = s;
331   fp = f;
332   lineno = 1;
333   newline_flag = 0;
334   seen_escape = 0;
335   popened = 0;
336   ptr = 0;
337   eptr = 0;
338   return 1;
339 }
340
341 int file_iterator::fill(node **)
342 {
343   if (newline_flag)
344     lineno++;
345   newline_flag = 0;
346   unsigned char *p = buf;
347   ptr = p;
348   unsigned char *e = p + BUF_SIZE;
349   while (p < e) {
350     int c = getc(fp);
351     if (c == EOF)
352       break;
353     if (invalid_input_char(c))
354       warning(WARN_INPUT, "invalid input character code %1", int(c));
355     else {
356       *p++ = c;
357       if (c == '\n') {
358         seen_escape = 0;
359         newline_flag = 1;
360         break;
361       }
362       seen_escape = (c == '\\');
363     }
364   }
365   if (p > buf) {
366     eptr = p;
367     return *ptr++;
368   }
369   else {
370     eptr = p;
371     return EOF;
372   }
373 }
374
375 int file_iterator::peek()
376 {
377   int c = getc(fp);
378   while (invalid_input_char(c)) {
379     warning(WARN_INPUT, "invalid input character code %1", int(c));
380     c = getc(fp);
381   }
382   if (c != EOF)
383     ungetc(c, fp);
384   return c;
385 }
386
387 int file_iterator::get_location(int /*allow_macro*/,
388                                 const char **filenamep, int *linenop)
389 {
390   *linenop = lineno;
391   if (filename != 0 && strcmp(filename, "-") == 0)
392     *filenamep = "<standard input>";
393   else
394     *filenamep = filename;
395   return 1;
396 }
397
398 void file_iterator::backtrace()
399 {
400   errprint("%1:%2: backtrace: %3 `%1'\n", filename, lineno,
401            popened ? "process" : "file");
402 }
403
404 int file_iterator::set_location(const char *f, int ln)
405 {
406   if (f) {
407     filename = f;
408     if (!the_output)
409       init_output();
410     the_output->put_filename(f, 0);
411   }
412   lineno = ln;
413   return 1;
414 }
415
416 input_iterator nil_iterator;
417
418 class input_stack {
419 public:
420   static int get(node **);
421   static int peek();
422   static void push(input_iterator *);
423   static input_iterator *get_arg(int);
424   static arg_list *get_arg_list();
425   static symbol get_macro_name();
426   static int space_follows_arg(int);
427   static int get_break_flag();
428   static int nargs();
429   static int get_location(int, const char **, int *);
430   static int set_location(const char *, int);
431   static void backtrace();
432   static void backtrace_all();
433   static void next_file(FILE *, const char *);
434   static void end_file();
435   static void shift(int n);
436   static void add_boundary();
437   static void add_return_boundary();
438   static int is_return_boundary();
439   static void remove_boundary();
440   static int get_level();
441   static int get_div_level();
442   static void increase_level();
443   static void decrease_level();
444   static void clear();
445   static void pop_macro();
446   static void save_compatible_flag(int);
447   static int get_compatible_flag();
448   static statem *get_diversion_state();
449   static void check_end_diversion(input_iterator *t);
450   static int limit;
451   static int div_level;
452   static statem *diversion_state;
453 private:
454   static input_iterator *top;
455   static int level;
456   static int finish_get(node **);
457   static int finish_peek();
458 };
459
460 input_iterator *input_stack::top = &nil_iterator;
461 int input_stack::level = 0;
462 int input_stack::limit = DEFAULT_INPUT_STACK_LIMIT;
463 int input_stack::div_level = 0;
464 statem *input_stack::diversion_state = NULL;
465 int suppress_push=0;
466
467
468 inline int input_stack::get_level()
469 {
470   return level;
471 }
472
473 inline void input_stack::increase_level()
474 {
475   level++;
476 }
477
478 inline void input_stack::decrease_level()
479 {
480   level--;
481 }
482
483 inline int input_stack::get_div_level()
484 {
485   return div_level;
486 }
487
488 inline int input_stack::get(node **np)
489 {
490   int res = (top->ptr < top->eptr) ? *top->ptr++ : finish_get(np);
491   if (res == '\n') {
492     old_have_input = have_input;
493     have_input = 0;
494   }
495   return res;
496 }
497
498 int input_stack::finish_get(node **np)
499 {
500   for (;;) {
501     int c = top->fill(np);
502     if (c != EOF || top->is_boundary())
503       return c;
504     if (top == &nil_iterator)
505       break;
506     input_iterator *tem = top;
507     check_end_diversion(tem);
508 #if defined(DEBUGGING)
509     if (debug_state)
510       if (tem->is_diversion)
511         fprintf(stderr,
512                 "in diversion level = %d\n", input_stack::get_div_level());
513 #endif
514     top = top->next;
515     level--;
516     delete tem;
517     if (top->ptr < top->eptr)
518       return *top->ptr++;
519   }
520   assert(level == 0);
521   return EOF;
522 }
523
524 inline int input_stack::peek()
525 {
526   return (top->ptr < top->eptr) ? *top->ptr : finish_peek();
527 }
528
529 void input_stack::check_end_diversion(input_iterator *t)
530 {
531   if (t->is_diversion) {
532     div_level--;
533     diversion_state = t->diversion_state;
534   }
535 }
536
537 int input_stack::finish_peek()
538 {
539   for (;;) {
540     int c = top->peek();
541     if (c != EOF || top->is_boundary())
542       return c;
543     if (top == &nil_iterator)
544       break;
545     input_iterator *tem = top;
546     check_end_diversion(tem);
547     top = top->next;
548     level--;
549     delete tem;
550     if (top->ptr < top->eptr)
551       return *top->ptr;
552   }
553   assert(level == 0);
554   return EOF;
555 }
556
557 void input_stack::add_boundary()
558 {
559   push(new input_boundary);
560 }
561
562 void input_stack::add_return_boundary()
563 {
564   push(new input_return_boundary);
565 }
566
567 int input_stack::is_return_boundary()
568 {
569   return top->is_boundary() == 2;
570 }
571
572 void input_stack::remove_boundary()
573 {
574   assert(top->is_boundary());
575   input_iterator *temp = top->next;
576   check_end_diversion(top);
577
578   delete top;
579   top = temp;
580   level--;
581 }
582
583 void input_stack::push(input_iterator *in)
584 {
585   if (in == 0)
586     return;
587   if (++level > limit && limit > 0)
588     fatal("input stack limit exceeded (probable infinite loop)");
589   in->next = top;
590   top = in;
591   if (top->is_diversion) {
592     div_level++;
593     in->diversion_state = diversion_state;
594     diversion_state = curenv->construct_state(0);
595 #if defined(DEBUGGING)
596     if (debug_state) {
597       curenv->dump_troff_state();
598       fflush(stderr);
599     }
600 #endif
601   }
602 #if defined(DEBUGGING)
603   if (debug_state)
604     if (top->is_diversion) {
605       fprintf(stderr,
606               "in diversion level = %d\n", input_stack::get_div_level());
607       fflush(stderr);
608     }
609 #endif
610 }
611
612 statem *get_diversion_state()
613 {
614   return input_stack::get_diversion_state();
615 }
616
617 statem *input_stack::get_diversion_state()
618 {
619   if (diversion_state == NULL)
620     return NULL;
621   else
622     return new statem(diversion_state);
623 }
624
625 input_iterator *input_stack::get_arg(int i)
626 {
627   input_iterator *p;
628   for (p = top; p != 0; p = p->next)
629     if (p->has_args())
630       return p->get_arg(i);
631   return 0;
632 }
633
634 arg_list *input_stack::get_arg_list()
635 {
636   input_iterator *p;
637   for (p = top; p != 0; p = p->next)
638     if (p->has_args())
639       return p->get_arg_list();
640   return 0;
641 }
642
643 symbol input_stack::get_macro_name()
644 {
645   input_iterator *p;
646   for (p = top; p != 0; p = p->next)
647     if (p->has_args())
648       return p->get_macro_name();
649   return NULL_SYMBOL;
650 }
651
652 int input_stack::space_follows_arg(int i)
653 {
654   input_iterator *p;
655   for (p = top; p != 0; p = p->next)
656     if (p->has_args())
657       return p->space_follows_arg(i);
658   return 0;
659 }
660
661 int input_stack::get_break_flag()
662 {
663   return top->get_break_flag();
664 }
665
666 void input_stack::shift(int n)
667 {
668   for (input_iterator *p = top; p; p = p->next)
669     if (p->has_args()) {
670       p->shift(n);
671       return;
672     }
673 }
674
675 int input_stack::nargs()
676 {
677   for (input_iterator *p =top; p != 0; p = p->next)
678     if (p->has_args())
679       return p->nargs();
680   return 0;
681 }
682
683 int input_stack::get_location(int allow_macro, const char **filenamep, int *linenop)
684 {
685   for (input_iterator *p = top; p; p = p->next)
686     if (p->get_location(allow_macro, filenamep, linenop))
687       return 1;
688   return 0;
689 }
690
691 void input_stack::backtrace()
692 {
693   const char *f;
694   int n;
695   // only backtrace down to (not including) the topmost file
696   for (input_iterator *p = top;
697        p && !p->get_location(0, &f, &n);
698        p = p->next)
699     p->backtrace();
700 }
701
702 void input_stack::backtrace_all()
703 {
704   for (input_iterator *p = top; p; p = p->next)
705     p->backtrace();
706 }
707
708 int input_stack::set_location(const char *filename, int lineno)
709 {
710   for (input_iterator *p = top; p; p = p->next)
711     if (p->set_location(filename, lineno))
712       return 1;
713   return 0;
714 }
715
716 void input_stack::next_file(FILE *fp, const char *s)
717 {
718   input_iterator **pp;
719   for (pp = &top; *pp != &nil_iterator; pp = &(*pp)->next)
720     if ((*pp)->next_file(fp, s))
721       return;
722   if (++level > limit && limit > 0)
723     fatal("input stack limit exceeded");
724   *pp = new file_iterator(fp, s);
725   (*pp)->next = &nil_iterator;
726 }
727
728 void input_stack::end_file()
729 {
730   for (input_iterator **pp = &top; *pp != &nil_iterator; pp = &(*pp)->next)
731     if ((*pp)->is_file()) {
732       input_iterator *tem = *pp;
733       check_end_diversion(tem);
734       *pp = (*pp)->next;
735       delete tem;
736       level--;
737       return;
738     }
739 }
740
741 void input_stack::clear()
742 {
743   int nboundaries = 0;
744   while (top != &nil_iterator) {
745     if (top->is_boundary())
746       nboundaries++;
747     input_iterator *tem = top;
748     check_end_diversion(tem);
749     top = top->next;
750     level--;
751     delete tem;
752   }
753   // Keep while_request happy.
754   for (; nboundaries > 0; --nboundaries)
755     add_return_boundary();
756 }
757
758 void input_stack::pop_macro()
759 {
760   int nboundaries = 0;
761   int is_macro = 0;
762   do {
763     if (top->next == &nil_iterator)
764       break;
765     if (top->is_boundary())
766       nboundaries++;
767     is_macro = top->is_macro();
768     input_iterator *tem = top;
769     check_end_diversion(tem);
770     top = top->next;
771     level--;
772     delete tem;
773   } while (!is_macro);
774   // Keep while_request happy.
775   for (; nboundaries > 0; --nboundaries)
776     add_return_boundary();
777 }
778
779 inline void input_stack::save_compatible_flag(int f)
780 {
781   top->save_compatible_flag(f);
782 }
783
784 inline int input_stack::get_compatible_flag()
785 {
786   return top->get_compatible_flag();
787 }
788
789 void backtrace_request()
790 {
791   input_stack::backtrace_all();
792   fflush(stderr);
793   skip_line();
794 }
795
796 void next_file()
797 {
798   symbol nm = get_long_name();
799   while (!tok.newline() && !tok.eof())
800     tok.next();
801   if (nm.is_null())
802     input_stack::end_file();
803   else {
804     errno = 0;
805     FILE *fp = include_search_path.open_file_cautious(nm.contents());
806     if (!fp)
807       error("can't open `%1': %2", nm.contents(), strerror(errno));
808     else
809       input_stack::next_file(fp, nm.contents());
810   }
811   tok.next();
812 }
813
814 void shift()
815 {
816   int n;
817   if (!has_arg() || !get_integer(&n))
818     n = 1;
819   input_stack::shift(n);
820   skip_line();
821 }
822
823 static char get_char_for_escape_name(int allow_space = 0)
824 {
825   int c = get_copy(0, 0, 1);
826   switch (c) {
827   case EOF:
828     copy_mode_error("end of input in escape name");
829     return '\0';
830   default:
831     if (!invalid_input_char(c))
832       break;
833     // fall through
834   case '\n':
835     if (c == '\n')
836       input_stack::push(make_temp_iterator("\n"));
837     // fall through
838   case ' ':
839     if (c == ' ' && allow_space)
840       break;
841     // fall through
842   case '\t':
843   case '\001':
844   case '\b':
845     copy_mode_error("%1 is not allowed in an escape name",
846                     input_char_description(c));
847     return '\0';
848   }
849   return c;
850 }
851
852 static symbol read_two_char_escape_name()
853 {
854   char buf[3];
855   buf[0] = get_char_for_escape_name();
856   if (buf[0] != '\0') {
857     buf[1] = get_char_for_escape_name();
858     if (buf[1] == '\0')
859       buf[0] = 0;
860     else
861       buf[2] = 0;
862   }
863   return symbol(buf);
864 }
865
866 static symbol read_long_escape_name(read_mode mode)
867 {
868   int start_level = input_stack::get_level();
869   char abuf[ABUF_SIZE];
870   char *buf = abuf;
871   int buf_size = ABUF_SIZE;
872   int i = 0;
873   char c;
874   int have_char = 0;
875   for (;;) {
876     c = get_char_for_escape_name(have_char && mode == WITH_ARGS);
877     if (c == 0) {
878       if (buf != abuf)
879         a_delete buf;
880       return NULL_SYMBOL;
881     }
882     have_char = 1;
883     if (mode == WITH_ARGS && c == ' ')
884       break;
885     if (i + 2 > buf_size) {
886       if (buf == abuf) {
887         buf = new char[ABUF_SIZE*2];
888         memcpy(buf, abuf, buf_size);
889         buf_size = ABUF_SIZE*2;
890       }
891       else {
892         char *old_buf = buf;
893         buf = new char[buf_size*2];
894         memcpy(buf, old_buf, buf_size);
895         buf_size *= 2;
896         a_delete old_buf;
897       }
898     }
899     if (c == ']' && input_stack::get_level() == start_level)
900       break;
901     buf[i++] = c;
902   }
903   buf[i] = 0;
904   if (c == ' ')
905     have_string_arg = 1;
906   if (buf == abuf) {
907     if (i == 0) {
908       if (mode != ALLOW_EMPTY)
909         copy_mode_error("empty escape name");
910       return EMPTY_SYMBOL;
911     }
912     return symbol(abuf);
913   }
914   else {
915     symbol s(buf);
916     a_delete buf;
917     return s;
918   }
919 }
920
921 static symbol read_escape_name(read_mode mode)
922 {
923   char c = get_char_for_escape_name();
924   if (c == 0)
925     return NULL_SYMBOL;
926   if (c == '(')
927     return read_two_char_escape_name();
928   if (c == '[' && !compatible_flag)
929     return read_long_escape_name(mode);
930   char buf[2];
931   buf[0] = c;
932   buf[1] = '\0';
933   return symbol(buf);
934 }
935
936 static symbol read_increment_and_escape_name(int *incp)
937 {
938   char c = get_char_for_escape_name();
939   switch (c) {
940   case 0:
941     *incp = 0;
942     return NULL_SYMBOL;
943   case '(':
944     *incp = 0;
945     return read_two_char_escape_name();
946   case '+':
947     *incp = 1;
948     return read_escape_name();
949   case '-':
950     *incp = -1;
951     return read_escape_name();
952   case '[':
953     if (!compatible_flag) {
954       *incp = 0;
955       return read_long_escape_name();
956     }
957     break;
958   }
959   *incp = 0;
960   char buf[2];
961   buf[0] = c;
962   buf[1] = '\0';
963   return symbol(buf);
964 }
965
966 static int get_copy(node **nd, int defining, int handle_escape_E)
967 {
968   for (;;) {
969     int c = input_stack::get(nd);
970     if (c == PUSH_GROFF_MODE) {
971       input_stack::save_compatible_flag(compatible_flag);
972       compatible_flag = 0;
973       continue;
974     }
975     if (c == PUSH_COMP_MODE) {
976       input_stack::save_compatible_flag(compatible_flag);
977       compatible_flag = 1;
978       continue;
979     }
980     if (c == POP_GROFFCOMP_MODE) {
981       compatible_flag = input_stack::get_compatible_flag();
982       continue;
983     }
984     if (c == BEGIN_QUOTE) {
985       input_stack::increase_level();
986       continue;
987     }
988     if (c == END_QUOTE) {
989       input_stack::decrease_level();
990       continue;
991     }
992     if (c == DOUBLE_QUOTE)
993       continue;
994     if (c == ESCAPE_E && handle_escape_E)
995       c = escape_char;
996     if (c == ESCAPE_NEWLINE) {
997       if (defining)
998         return c;
999       do {
1000         c = input_stack::get(nd);
1001       } while (c == ESCAPE_NEWLINE);
1002     }
1003     if (c != escape_char || escape_char <= 0)
1004       return c;
1005   again:
1006     c = input_stack::peek();
1007     switch(c) {
1008     case 0:
1009       return escape_char;
1010     case '"':
1011       (void)input_stack::get(0);
1012       while ((c = input_stack::get(0)) != '\n' && c != EOF)
1013         ;
1014       return c;
1015     case '#':                   // Like \" but newline is ignored.
1016       (void)input_stack::get(0);
1017       while ((c = input_stack::get(0)) != '\n')
1018         if (c == EOF)
1019           return EOF;
1020       break;
1021     case '$':
1022       {
1023         (void)input_stack::get(0);
1024         symbol s = read_escape_name();
1025         if (!(s.is_null() || s.is_empty()))
1026           interpolate_arg(s);
1027         break;
1028       }
1029     case '*':
1030       {
1031         (void)input_stack::get(0);
1032         symbol s = read_escape_name(WITH_ARGS);
1033         if (!(s.is_null() || s.is_empty())) {
1034           if (have_string_arg) {
1035             have_string_arg = 0;
1036             interpolate_string_with_args(s);
1037           }
1038           else
1039             interpolate_string(s);
1040         }
1041         break;
1042       }
1043     case 'a':
1044       (void)input_stack::get(0);
1045       return '\001';
1046     case 'e':
1047       (void)input_stack::get(0);
1048       return ESCAPE_e;
1049     case 'E':
1050       (void)input_stack::get(0);
1051       if (handle_escape_E)
1052         goto again;
1053       return ESCAPE_E;
1054     case 'n':
1055       {
1056         (void)input_stack::get(0);
1057         int inc;
1058         symbol s = read_increment_and_escape_name(&inc);
1059         if (!(s.is_null() || s.is_empty()))
1060           interpolate_number_reg(s, inc);
1061         break;
1062       }
1063     case 'g':
1064       {
1065         (void)input_stack::get(0);
1066         symbol s = read_escape_name();
1067         if (!(s.is_null() || s.is_empty()))
1068           interpolate_number_format(s);
1069         break;
1070       }
1071     case 't':
1072       (void)input_stack::get(0);
1073       return '\t';
1074     case 'V':
1075       {
1076         (void)input_stack::get(0);
1077         symbol s = read_escape_name();
1078         if (!(s.is_null() || s.is_empty()))
1079           interpolate_environment_variable(s);
1080         break;
1081       }
1082     case '\n':
1083       (void)input_stack::get(0);
1084       if (defining)
1085         return ESCAPE_NEWLINE;
1086       break;
1087     case ' ':
1088       (void)input_stack::get(0);
1089       return ESCAPE_SPACE;
1090     case '~':
1091       (void)input_stack::get(0);
1092       return ESCAPE_TILDE;
1093     case ':':
1094       (void)input_stack::get(0);
1095       return ESCAPE_COLON;
1096     case '|':
1097       (void)input_stack::get(0);
1098       return ESCAPE_BAR;
1099     case '^':
1100       (void)input_stack::get(0);
1101       return ESCAPE_CIRCUMFLEX;
1102     case '{':
1103       (void)input_stack::get(0);
1104       return ESCAPE_LEFT_BRACE;
1105     case '}':
1106       (void)input_stack::get(0);
1107       return ESCAPE_RIGHT_BRACE;
1108     case '`':
1109       (void)input_stack::get(0);
1110       return ESCAPE_LEFT_QUOTE;
1111     case '\'':
1112       (void)input_stack::get(0);
1113       return ESCAPE_RIGHT_QUOTE;
1114     case '-':
1115       (void)input_stack::get(0);
1116       return ESCAPE_HYPHEN;
1117     case '_':
1118       (void)input_stack::get(0);
1119       return ESCAPE_UNDERSCORE;
1120     case 'c':
1121       (void)input_stack::get(0);
1122       return ESCAPE_c;
1123     case '!':
1124       (void)input_stack::get(0);
1125       return ESCAPE_BANG;
1126     case '?':
1127       (void)input_stack::get(0);
1128       return ESCAPE_QUESTION;
1129     case '&':
1130       (void)input_stack::get(0);
1131       return ESCAPE_AMPERSAND;
1132     case ')':
1133       (void)input_stack::get(0);
1134       return ESCAPE_RIGHT_PARENTHESIS;
1135     case '.':
1136       (void)input_stack::get(0);
1137       return c;                 
1138     case '%':
1139       (void)input_stack::get(0);
1140       return ESCAPE_PERCENT;
1141     default:
1142       if (c == escape_char) {
1143         (void)input_stack::get(0);
1144         return c;
1145       }
1146       else
1147         return escape_char;
1148     }
1149   }
1150 }
1151
1152 class non_interpreted_char_node : public node {
1153   unsigned char c;
1154 public:
1155   non_interpreted_char_node(unsigned char);
1156   node *copy();
1157   int interpret(macro *);
1158   int same(node *);
1159   const char *type();
1160   int force_tprint();
1161   int is_tag();
1162 };
1163
1164 int non_interpreted_char_node::same(node *nd)
1165 {
1166   return c == ((non_interpreted_char_node *)nd)->c;
1167 }
1168
1169 const char *non_interpreted_char_node::type()
1170 {
1171   return "non_interpreted_char_node";
1172 }
1173
1174 int non_interpreted_char_node::force_tprint()
1175 {
1176   return 0;
1177 }
1178
1179 int non_interpreted_char_node::is_tag()
1180 {
1181   return 0;
1182 }
1183
1184 non_interpreted_char_node::non_interpreted_char_node(unsigned char n) : c(n)
1185 {
1186   assert(n != 0);
1187 }
1188
1189 node *non_interpreted_char_node::copy()
1190 {
1191   return new non_interpreted_char_node(c);
1192 }
1193
1194 int non_interpreted_char_node::interpret(macro *mac)
1195 {
1196   mac->append(c);
1197   return 1;
1198 }
1199
1200 static void do_width();
1201 static node *do_non_interpreted();
1202 static node *do_special();
1203 static node *do_suppress(symbol nm);
1204 static void do_register();
1205
1206 dictionary color_dictionary(501);
1207
1208 static color *lookup_color(symbol nm)
1209 {
1210   assert(!nm.is_null());
1211   if (nm == default_symbol)
1212     return &default_color;
1213   color *c = (color *)color_dictionary.lookup(nm);
1214   if (c == 0)
1215     warning(WARN_COLOR, "color `%1' not defined", nm.contents());
1216   return c;
1217 }
1218
1219 void do_glyph_color(symbol nm)
1220 {
1221   if (nm.is_null())
1222     return;
1223   if (nm.is_empty())
1224     curenv->set_glyph_color(curenv->get_prev_glyph_color());
1225   else {
1226     color *tem = lookup_color(nm);
1227     if (tem)
1228       curenv->set_glyph_color(tem);
1229     else
1230       (void)color_dictionary.lookup(nm, new color(nm));
1231   }
1232 }
1233
1234 void do_fill_color(symbol nm)
1235 {
1236   if (nm.is_null())
1237     return;
1238   if (nm.is_empty())
1239     curenv->set_fill_color(curenv->get_prev_fill_color());
1240   else {
1241     color *tem = lookup_color(nm);
1242     if (tem)
1243       curenv->set_fill_color(tem);
1244     else
1245       (void)color_dictionary.lookup(nm, new color(nm));
1246   }
1247 }
1248
1249 static unsigned int get_color_element(const char *scheme, const char *col)
1250 {
1251   units val;
1252   if (!get_number(&val, 'f')) {
1253     warning(WARN_COLOR, "%1 in %2 definition set to 0", col, scheme);
1254     tok.next();
1255     return 0;
1256   }
1257   if (val < 0) {
1258     warning(WARN_RANGE, "%1 cannot be negative: set to 0", col);
1259     return 0;
1260   }
1261   if (val > color::MAX_COLOR_VAL+1) {
1262     warning(WARN_RANGE, "%1 cannot be greater than 1", col);
1263     // we change 0x10000 to 0xffff
1264     return color::MAX_COLOR_VAL;
1265   }
1266   return (unsigned int)val;
1267 }
1268
1269 static color *read_rgb(char end = 0)
1270 {
1271   symbol component = do_get_long_name(0, end);
1272   if (component.is_null()) {
1273     warning(WARN_COLOR, "missing rgb color values");
1274     return 0;
1275   }
1276   const char *s = component.contents();
1277   color *col = new color;
1278   if (*s == '#') {
1279     if (!col->read_rgb(s)) {
1280       warning(WARN_COLOR, "expecting rgb color definition not `%1'", s);
1281       delete col;
1282       return 0;
1283     }
1284   }
1285   else {
1286     if (!end)
1287       input_stack::push(make_temp_iterator(" "));
1288     input_stack::push(make_temp_iterator(s));
1289     tok.next();
1290     unsigned int r = get_color_element("rgb color", "red component");
1291     unsigned int g = get_color_element("rgb color", "green component");
1292     unsigned int b = get_color_element("rgb color", "blue component");
1293     col->set_rgb(r, g, b);
1294   }
1295   return col;
1296 }
1297
1298 static color *read_cmy(char end = 0)
1299 {
1300   symbol component = do_get_long_name(0, end);
1301   if (component.is_null()) {
1302     warning(WARN_COLOR, "missing cmy color values");
1303     return 0;
1304   }
1305   const char *s = component.contents();
1306   color *col = new color;
1307   if (*s == '#') {
1308     if (!col->read_cmy(s)) {
1309       warning(WARN_COLOR, "expecting cmy color definition not `%1'", s);
1310       delete col;
1311       return 0;
1312     }
1313   }
1314   else {
1315     if (!end)
1316       input_stack::push(make_temp_iterator(" "));
1317     input_stack::push(make_temp_iterator(s));
1318     tok.next();
1319     unsigned int c = get_color_element("cmy color", "cyan component");
1320     unsigned int m = get_color_element("cmy color", "magenta component");
1321     unsigned int y = get_color_element("cmy color", "yellow component");
1322     col->set_cmy(c, m, y);
1323   }
1324   return col;
1325 }
1326
1327 static color *read_cmyk(char end = 0)
1328 {
1329   symbol component = do_get_long_name(0, end);
1330   if (component.is_null()) {
1331     warning(WARN_COLOR, "missing cmyk color values");
1332     return 0;
1333   }
1334   const char *s = component.contents();
1335   color *col = new color;
1336   if (*s == '#') {
1337     if (!col->read_cmyk(s)) {
1338       warning(WARN_COLOR, "`expecting a cmyk color definition not `%1'", s);
1339       delete col;
1340       return 0;
1341     }
1342   }
1343   else {
1344     if (!end)
1345       input_stack::push(make_temp_iterator(" "));
1346     input_stack::push(make_temp_iterator(s));
1347     tok.next();
1348     unsigned int c = get_color_element("cmyk color", "cyan component");
1349     unsigned int m = get_color_element("cmyk color", "magenta component");
1350     unsigned int y = get_color_element("cmyk color", "yellow component");
1351     unsigned int k = get_color_element("cmyk color", "black component");
1352     col->set_cmyk(c, m, y, k);
1353   }
1354   return col;
1355 }
1356
1357 static color *read_gray(char end = 0)
1358 {
1359   symbol component = do_get_long_name(0, end);
1360   if (component.is_null()) {
1361     warning(WARN_COLOR, "missing gray values");
1362     return 0;
1363   }
1364   const char *s = component.contents();
1365   color *col = new color;
1366   if (*s == '#') {
1367     if (!col->read_gray(s)) {
1368       warning(WARN_COLOR, "`expecting a gray definition not `%1'", s);
1369       delete col;
1370       return 0;
1371     }
1372   }
1373   else {
1374     if (!end)
1375       input_stack::push(make_temp_iterator("\n"));
1376     input_stack::push(make_temp_iterator(s));
1377     tok.next();
1378     unsigned int g = get_color_element("gray", "gray value");
1379     col->set_gray(g);
1380   }
1381   return col;
1382 }
1383
1384 static void activate_color()
1385 {
1386   int n;
1387   if (has_arg() && get_integer(&n))
1388     color_flag = n != 0;
1389   else
1390     color_flag = 1;
1391   skip_line();
1392 }
1393
1394 static void define_color()
1395 {
1396   symbol color_name = get_long_name(1);
1397   if (color_name.is_null()) {
1398     skip_line();
1399     return;
1400   }
1401   if (color_name == default_symbol) {
1402     warning(WARN_COLOR, "default color can't be redefined");
1403     skip_line();
1404     return;
1405   }
1406   symbol style = get_long_name(1);
1407   if (style.is_null()) {
1408     skip_line();
1409     return;
1410   }
1411   color *col;
1412   if (strcmp(style.contents(), "rgb") == 0)
1413     col = read_rgb();
1414   else if (strcmp(style.contents(), "cmyk") == 0)
1415     col = read_cmyk();
1416   else if (strcmp(style.contents(), "gray") == 0)
1417     col = read_gray();
1418   else if (strcmp(style.contents(), "grey") == 0)
1419     col = read_gray();
1420   else if (strcmp(style.contents(), "cmy") == 0)
1421     col = read_cmy();
1422   else {
1423     warning(WARN_COLOR,
1424             "unknown color space `%1'; use rgb, cmyk, gray or cmy",
1425             style.contents());
1426     skip_line();
1427     return;
1428   }
1429   if (col) {
1430     col->nm = color_name;
1431     (void)color_dictionary.lookup(color_name, col);
1432   }
1433   skip_line();
1434 }
1435
1436 static node *do_overstrike()
1437 {
1438   token start;
1439   overstrike_node *on = new overstrike_node;
1440   int start_level = input_stack::get_level();
1441   start.next();
1442   for (;;) {
1443     tok.next();
1444     if (tok.newline() || tok.eof()) {
1445       warning(WARN_DELIM, "missing closing delimiter");
1446       input_stack::push(make_temp_iterator("\n"));
1447       break;
1448     }
1449     if (tok == start
1450         && (compatible_flag || input_stack::get_level() == start_level))
1451       break;
1452     charinfo *ci = tok.get_char(1);
1453     if (ci) {
1454       node *n = curenv->make_char_node(ci);
1455       if (n)
1456         on->overstrike(n);
1457     }
1458   }
1459   return on;
1460 }
1461
1462 static node *do_bracket()
1463 {
1464   token start;
1465   bracket_node *bn = new bracket_node;
1466   start.next();
1467   int start_level = input_stack::get_level();
1468   for (;;) {
1469     tok.next();
1470     if (tok.eof()) {
1471       warning(WARN_DELIM, "missing closing delimiter");
1472       break;
1473     }
1474     if (tok.newline()) {
1475       warning(WARN_DELIM, "missing closing delimiter");
1476       input_stack::push(make_temp_iterator("\n"));
1477       break;
1478     }
1479     if (tok == start
1480         && (compatible_flag || input_stack::get_level() == start_level))
1481       break;
1482     charinfo *ci = tok.get_char(1);
1483     if (ci) {
1484       node *n = curenv->make_char_node(ci);
1485       if (n)
1486         bn->bracket(n);
1487     }
1488   }
1489   return bn;
1490 }
1491
1492 static int do_name_test()
1493 {
1494   token start;
1495   start.next();
1496   int start_level = input_stack::get_level();
1497   int bad_char = 0;
1498   int some_char = 0;
1499   for (;;) {
1500     tok.next();
1501     if (tok.newline() || tok.eof()) {
1502       warning(WARN_DELIM, "missing closing delimiter");
1503       input_stack::push(make_temp_iterator("\n"));
1504       break;
1505     }
1506     if (tok == start
1507         && (compatible_flag || input_stack::get_level() == start_level))
1508       break;
1509     if (!tok.ch())
1510       bad_char = 1;
1511     some_char = 1;
1512   }
1513   return some_char && !bad_char;
1514 }
1515
1516 static int do_expr_test()
1517 {
1518   token start;
1519   start.next();
1520   int start_level = input_stack::get_level();
1521   if (!start.delimiter(1))
1522     return 0;
1523   tok.next();
1524   // disable all warning and error messages temporarily
1525   int saved_warning_mask = warning_mask;
1526   int saved_inhibit_errors = inhibit_errors;
1527   warning_mask = 0;
1528   inhibit_errors = 1;
1529   int dummy;
1530   int result = get_number_rigidly(&dummy, 'u');
1531   warning_mask = saved_warning_mask;
1532   inhibit_errors = saved_inhibit_errors;
1533   if (tok == start && input_stack::get_level() == start_level)
1534     return result;
1535   // ignore everything up to the delimiter in case we aren't right there
1536   for (;;) {
1537     tok.next();
1538     if (tok.newline() || tok.eof()) {
1539       warning(WARN_DELIM, "missing closing delimiter");
1540       input_stack::push(make_temp_iterator("\n"));
1541       break;
1542     }
1543     if (tok == start && input_stack::get_level() == start_level)
1544       break;
1545   }
1546   return 0;
1547 }
1548
1549 #if 0
1550 static node *do_zero_width()
1551 {
1552   token start;
1553   start.next();
1554   int start_level = input_stack::get_level();
1555   environment env(curenv);
1556   environment *oldenv = curenv;
1557   curenv = &env;
1558   for (;;) {
1559     tok.next();
1560     if (tok.newline() || tok.eof()) {
1561       error("missing closing delimiter");
1562       break;
1563     }
1564     if (tok == start
1565         && (compatible_flag || input_stack::get_level() == start_level))
1566       break;
1567     tok.process();
1568   }
1569   curenv = oldenv;
1570   node *rev = env.extract_output_line();
1571   node *n = 0;
1572   while (rev) {
1573     node *tem = rev;
1574     rev = rev->next;
1575     tem->next = n;
1576     n = tem;
1577   }
1578   return new zero_width_node(n);
1579 }
1580
1581 #else
1582
1583 // It's undesirable for \Z to change environments, because then
1584 // \n(.w won't work as expected.
1585
1586 static node *do_zero_width()
1587 {
1588   node *rev = new dummy_node;
1589   token start;
1590   start.next();
1591   int start_level = input_stack::get_level();
1592   for (;;) {
1593     tok.next();
1594     if (tok.newline() || tok.eof()) {
1595       warning(WARN_DELIM, "missing closing delimiter");
1596       input_stack::push(make_temp_iterator("\n"));
1597       break;
1598     }
1599     if (tok == start
1600         && (compatible_flag || input_stack::get_level() == start_level))
1601       break;
1602     if (!tok.add_to_node_list(&rev))
1603       error("invalid token in argument to \\Z");
1604   }
1605   node *n = 0;
1606   while (rev) {
1607     node *tem = rev;
1608     rev = rev->next;
1609     tem->next = n;
1610     n = tem;
1611   }
1612   return new zero_width_node(n);
1613 }
1614
1615 #endif
1616
1617 token_node *node::get_token_node()
1618 {
1619   return 0;
1620 }
1621
1622 class token_node : public node {
1623 public:
1624   token tk;
1625   token_node(const token &t);
1626   node *copy();
1627   token_node *get_token_node();
1628   int same(node *);
1629   const char *type();
1630   int force_tprint();
1631   int is_tag();
1632 };
1633
1634 token_node::token_node(const token &t) : tk(t)
1635 {
1636 }
1637
1638 node *token_node::copy()
1639 {
1640   return new token_node(tk);
1641 }
1642
1643 token_node *token_node::get_token_node()
1644 {
1645   return this;
1646 }
1647
1648 int token_node::same(node *nd)
1649 {
1650   return tk == ((token_node *)nd)->tk;
1651 }
1652
1653 const char *token_node::type()
1654 {
1655   return "token_node";
1656 }
1657
1658 int token_node::force_tprint()
1659 {
1660   return 0;
1661 }
1662
1663 int token_node::is_tag()
1664 {
1665   return 0;
1666 }
1667
1668 token::token() : nd(0), type(TOKEN_EMPTY)
1669 {
1670 }
1671
1672 token::~token()
1673 {
1674   delete nd;
1675 }
1676
1677 token::token(const token &t)
1678 : nm(t.nm), c(t.c), val(t.val), dim(t.dim), type(t.type)
1679 {
1680   // Use two statements to work around bug in SGI C++.
1681   node *tem = t.nd;
1682   nd = tem ? tem->copy() : 0;
1683 }
1684
1685 void token::operator=(const token &t)
1686 {
1687   delete nd;
1688   nm = t.nm;
1689   // Use two statements to work around bug in SGI C++.
1690   node *tem = t.nd;
1691   nd = tem ? tem->copy() : 0;
1692   c = t.c;
1693   val = t.val;
1694   dim = t.dim;
1695   type = t.type;
1696 }
1697
1698 void token::skip()
1699 {
1700   while (space())
1701     next();
1702 }
1703
1704 int has_arg()
1705 {
1706   while (tok.space())
1707     tok.next();
1708   return !tok.newline();
1709 }
1710
1711 void token::make_space()
1712 {
1713   type = TOKEN_SPACE;
1714 }
1715
1716 void token::make_newline()
1717 {
1718   type = TOKEN_NEWLINE;
1719 }
1720
1721 void token::next()
1722 {
1723   if (nd) {
1724     delete nd;
1725     nd = 0;
1726   }
1727   units x;
1728   for (;;) {
1729     node *n = 0;
1730     int cc = input_stack::get(&n);
1731     if (cc != escape_char || escape_char == 0) {
1732     handle_normal_char:
1733       switch(cc) {
1734       case PUSH_GROFF_MODE:
1735         input_stack::save_compatible_flag(compatible_flag);
1736         compatible_flag = 0;
1737         continue;
1738       case PUSH_COMP_MODE:
1739         input_stack::save_compatible_flag(compatible_flag);
1740         compatible_flag = 1;
1741         continue;
1742       case POP_GROFFCOMP_MODE:
1743         compatible_flag = input_stack::get_compatible_flag();
1744         continue;
1745       case BEGIN_QUOTE:
1746         input_stack::increase_level();
1747         continue;
1748       case END_QUOTE:
1749         input_stack::decrease_level();
1750         continue;
1751       case DOUBLE_QUOTE:
1752         continue;
1753       case EOF:
1754         type = TOKEN_EOF;
1755         return;
1756       case TRANSPARENT_FILE_REQUEST:
1757       case TITLE_REQUEST:
1758       case COPY_FILE_REQUEST:
1759 #ifdef COLUMN
1760       case VJUSTIFY_REQUEST:
1761 #endif /* COLUMN */
1762         type = TOKEN_REQUEST;
1763         c = cc;
1764         return;
1765       case BEGIN_TRAP:
1766         type = TOKEN_BEGIN_TRAP;
1767         return;
1768       case END_TRAP:
1769         type = TOKEN_END_TRAP;
1770         return;
1771       case LAST_PAGE_EJECTOR:
1772         seen_last_page_ejector = 1;
1773         // fall through
1774       case PAGE_EJECTOR:
1775         type = TOKEN_PAGE_EJECTOR;
1776         return;
1777       case ESCAPE_PERCENT:
1778       ESCAPE_PERCENT:
1779         type = TOKEN_HYPHEN_INDICATOR;
1780         return;
1781       case ESCAPE_SPACE:
1782       ESCAPE_SPACE:
1783         type = TOKEN_UNSTRETCHABLE_SPACE;
1784         return;
1785       case ESCAPE_TILDE:
1786       ESCAPE_TILDE:
1787         type = TOKEN_STRETCHABLE_SPACE;
1788         return;
1789       case ESCAPE_COLON:
1790       ESCAPE_COLON:
1791         type = TOKEN_ZERO_WIDTH_BREAK;
1792         return;
1793       case ESCAPE_e:
1794       ESCAPE_e:
1795         type = TOKEN_ESCAPE;
1796         return;
1797       case ESCAPE_E:
1798         goto handle_escape_char;
1799       case ESCAPE_BAR:
1800       ESCAPE_BAR:
1801         type = TOKEN_NODE;
1802         nd = new hmotion_node(curenv->get_narrow_space_width(),
1803                               curenv->get_fill_color());
1804         return;
1805       case ESCAPE_CIRCUMFLEX:
1806       ESCAPE_CIRCUMFLEX:
1807         type = TOKEN_NODE;
1808         nd = new hmotion_node(curenv->get_half_narrow_space_width(),
1809                               curenv->get_fill_color());
1810         return;
1811       case ESCAPE_NEWLINE:
1812         have_input = 0;
1813         break;
1814       case ESCAPE_LEFT_BRACE:
1815       ESCAPE_LEFT_BRACE:
1816         type = TOKEN_LEFT_BRACE;
1817         return;
1818       case ESCAPE_RIGHT_BRACE:
1819       ESCAPE_RIGHT_BRACE:
1820         type = TOKEN_RIGHT_BRACE;
1821         return;
1822       case ESCAPE_LEFT_QUOTE:
1823       ESCAPE_LEFT_QUOTE:
1824         type = TOKEN_SPECIAL;
1825         nm = symbol("ga");
1826         return;
1827       case ESCAPE_RIGHT_QUOTE:
1828       ESCAPE_RIGHT_QUOTE:
1829         type = TOKEN_SPECIAL;
1830         nm = symbol("aa");
1831         return;
1832       case ESCAPE_HYPHEN:
1833       ESCAPE_HYPHEN:
1834         type = TOKEN_SPECIAL;
1835         nm = symbol("-");
1836         return;
1837       case ESCAPE_UNDERSCORE:
1838       ESCAPE_UNDERSCORE:
1839         type = TOKEN_SPECIAL;
1840         nm = symbol("ul");
1841         return;
1842       case ESCAPE_c:
1843       ESCAPE_c:
1844         type = TOKEN_INTERRUPT;
1845         return;
1846       case ESCAPE_BANG:
1847       ESCAPE_BANG:
1848         type = TOKEN_TRANSPARENT;
1849         return;
1850       case ESCAPE_QUESTION:
1851       ESCAPE_QUESTION:
1852         nd = do_non_interpreted();
1853         if (nd) {
1854           type = TOKEN_NODE;
1855           return;
1856         }
1857         break;
1858       case ESCAPE_AMPERSAND:
1859       ESCAPE_AMPERSAND:
1860         type = TOKEN_DUMMY;
1861         return;
1862       case ESCAPE_RIGHT_PARENTHESIS:
1863       ESCAPE_RIGHT_PARENTHESIS:
1864         type = TOKEN_TRANSPARENT_DUMMY;
1865         return;
1866       case '\b':
1867         type = TOKEN_BACKSPACE;
1868         return;
1869       case ' ':
1870         type = TOKEN_SPACE;
1871         return;
1872       case '\t':
1873         type = TOKEN_TAB;
1874         return;
1875       case '\n':
1876         type = TOKEN_NEWLINE;
1877         return;
1878       case '\001':
1879         type = TOKEN_LEADER;
1880         return;
1881       case 0:
1882         {
1883           assert(n != 0);
1884           token_node *tn = n->get_token_node();
1885           if (tn) {
1886             *this = tn->tk;
1887             delete tn;
1888           }
1889           else {
1890             nd = n;
1891             type = TOKEN_NODE;
1892           }
1893         }
1894         return;
1895       default:
1896         type = TOKEN_CHAR;
1897         c = cc;
1898         return;
1899       }
1900     }
1901     else {
1902     handle_escape_char:
1903       cc = input_stack::get(&n);
1904       switch(cc) {
1905       case '(':
1906         nm = read_two_char_escape_name();
1907         type = TOKEN_SPECIAL;
1908         return;
1909       case EOF:
1910         type = TOKEN_EOF;
1911         error("end of input after escape character");
1912         return;
1913       case '`':
1914         goto ESCAPE_LEFT_QUOTE;
1915       case '\'':
1916         goto ESCAPE_RIGHT_QUOTE;
1917       case '-':
1918         goto ESCAPE_HYPHEN;
1919       case '_':
1920         goto ESCAPE_UNDERSCORE;
1921       case '%':
1922         goto ESCAPE_PERCENT;
1923       case ' ':
1924         goto ESCAPE_SPACE;
1925       case '0':
1926         nd = new hmotion_node(curenv->get_digit_width(),
1927                               curenv->get_fill_color());
1928         type = TOKEN_NODE;
1929         return;
1930       case '|':
1931         goto ESCAPE_BAR;
1932       case '^':
1933         goto ESCAPE_CIRCUMFLEX;
1934       case '/':
1935         type = TOKEN_ITALIC_CORRECTION;
1936         return;
1937       case ',':
1938         type = TOKEN_NODE;
1939         nd = new left_italic_corrected_node;
1940         return;
1941       case '&':
1942         goto ESCAPE_AMPERSAND;
1943       case ')':
1944         goto ESCAPE_RIGHT_PARENTHESIS;
1945       case '!':
1946         goto ESCAPE_BANG;
1947       case '?':
1948         goto ESCAPE_QUESTION;
1949       case '~':
1950         goto ESCAPE_TILDE;
1951       case ':':
1952         goto ESCAPE_COLON;
1953       case '"':
1954         while ((cc = input_stack::get(0)) != '\n' && cc != EOF)
1955           ;
1956         if (cc == '\n')
1957           type = TOKEN_NEWLINE;
1958         else
1959           type = TOKEN_EOF;
1960         return;
1961       case '#':                 // Like \" but newline is ignored.
1962         while ((cc = input_stack::get(0)) != '\n')
1963           if (cc == EOF) {
1964             type = TOKEN_EOF;
1965             return;
1966           }
1967         break;
1968       case '$':
1969         {
1970           symbol s = read_escape_name();
1971           if (!(s.is_null() || s.is_empty()))
1972             interpolate_arg(s);
1973           break;
1974         }
1975       case '*':
1976         {
1977           symbol s = read_escape_name(WITH_ARGS);
1978           if (!(s.is_null() || s.is_empty())) {
1979             if (have_string_arg) {
1980               have_string_arg = 0;
1981               interpolate_string_with_args(s);
1982             }
1983             else
1984               interpolate_string(s);
1985           }
1986           break;
1987         }
1988       case 'a':
1989         nd = new non_interpreted_char_node('\001');
1990         type = TOKEN_NODE;
1991         return;
1992       case 'A':
1993         c = '0' + do_name_test();
1994         type = TOKEN_CHAR;
1995         return;
1996       case 'b':
1997         nd = do_bracket();
1998         type = TOKEN_NODE;
1999         return;
2000       case 'B':
2001         c = '0' + do_expr_test();
2002         type = TOKEN_CHAR;
2003         return;
2004       case 'c':
2005         goto ESCAPE_c;
2006       case 'C':
2007         nm = get_delim_name();
2008         if (nm.is_null())
2009           break;
2010         type = TOKEN_SPECIAL;
2011         return;
2012       case 'd':
2013         type = TOKEN_NODE;
2014         nd = new vmotion_node(curenv->get_size() / 2,
2015                               curenv->get_fill_color());
2016         return;
2017       case 'D':
2018         nd = read_draw_node();
2019         if (!nd)
2020           break;
2021         type = TOKEN_NODE;
2022         return;
2023       case 'e':
2024         goto ESCAPE_e;
2025       case 'E':
2026         goto handle_escape_char;
2027       case 'f':
2028         {
2029           symbol s = read_escape_name(ALLOW_EMPTY);
2030           if (s.is_null())
2031             break;
2032           const char *p;
2033           for (p = s.contents(); *p != '\0'; p++)
2034             if (!csdigit(*p))
2035               break;
2036           if (*p || s.is_empty())
2037             curenv->set_font(s);
2038           else
2039             curenv->set_font(atoi(s.contents()));
2040           if (!compatible_flag)
2041             have_input = 1;
2042           break;
2043         }
2044       case 'F':
2045         {
2046           symbol s = read_escape_name(ALLOW_EMPTY);
2047           if (s.is_null())
2048             break;
2049           curenv->set_family(s);
2050           have_input = 1;
2051           break;
2052         }
2053       case 'g':
2054         {
2055           symbol s = read_escape_name();
2056           if (!(s.is_null() || s.is_empty()))
2057             interpolate_number_format(s);
2058           break;
2059         }
2060       case 'h':
2061         if (!get_delim_number(&x, 'm'))
2062           break;
2063         type = TOKEN_NODE;
2064         nd = new hmotion_node(x, curenv->get_fill_color());
2065         return;
2066       case 'H':
2067         // don't take height increments relative to previous height if
2068         // in compatibility mode
2069         if (!compatible_flag && curenv->get_char_height()) {
2070           if (get_delim_number(&x, 'z', curenv->get_char_height()))
2071             curenv->set_char_height(x);
2072         }
2073         else {
2074           if (get_delim_number(&x, 'z', curenv->get_requested_point_size()))
2075             curenv->set_char_height(x);
2076         }
2077         if (!compatible_flag)
2078           have_input = 1;
2079         break;
2080       case 'k':
2081         nm = read_escape_name();
2082         if (nm.is_null() || nm.is_empty())
2083           break;
2084         type = TOKEN_MARK_INPUT;
2085         return;
2086       case 'l':
2087       case 'L':
2088         {
2089           charinfo *s = 0;
2090           if (!get_line_arg(&x, (cc == 'l' ? 'm': 'v'), &s))
2091             break;
2092           if (s == 0)
2093             s = get_charinfo(cc == 'l' ? "ru" : "br");
2094           type = TOKEN_NODE;
2095           node *char_node = curenv->make_char_node(s);
2096           if (cc == 'l')
2097             nd = new hline_node(x, char_node);
2098           else
2099             nd = new vline_node(x, char_node);
2100           return;
2101         }
2102       case 'm':
2103         do_glyph_color(read_escape_name(ALLOW_EMPTY));
2104         if (!compatible_flag)
2105           have_input = 1;
2106         break;
2107       case 'M':
2108         do_fill_color(read_escape_name(ALLOW_EMPTY));
2109         if (!compatible_flag)
2110           have_input = 1;
2111         break;
2112       case 'n':
2113         {
2114           int inc;
2115           symbol s = read_increment_and_escape_name(&inc);
2116           if (!(s.is_null() || s.is_empty()))
2117             interpolate_number_reg(s, inc);
2118           break;
2119         }
2120       case 'N':
2121         if (!get_delim_number(&val, 0))
2122           break;
2123         if (val < 0) {
2124           warning(WARN_CHAR, "invalid numbered character %1", val);
2125           break;
2126         }
2127         type = TOKEN_NUMBERED_CHAR;
2128         return;
2129       case 'o':
2130         nd = do_overstrike();
2131         type = TOKEN_NODE;
2132         return;
2133       case 'O':
2134         nd = do_suppress(read_escape_name());
2135         if (!nd)
2136           break;
2137         type = TOKEN_NODE;
2138         return;
2139       case 'p':
2140         type = TOKEN_SPREAD;
2141         return;
2142       case 'r':
2143         type = TOKEN_NODE;
2144         nd = new vmotion_node(-curenv->get_size(), curenv->get_fill_color());
2145         return;
2146       case 'R':
2147         do_register();
2148         if (!compatible_flag)
2149           have_input = 1;
2150         break;
2151       case 's':
2152         if (read_size(&x))
2153           curenv->set_size(x);
2154         if (!compatible_flag)
2155           have_input = 1;
2156         break;
2157       case 'S':
2158         if (get_delim_number(&x, 0))
2159           curenv->set_char_slant(x);
2160         if (!compatible_flag)
2161           have_input = 1;
2162         break;
2163       case 't':
2164         type = TOKEN_NODE;
2165         nd = new non_interpreted_char_node('\t');
2166         return;
2167       case 'u':
2168         type = TOKEN_NODE;
2169         nd = new vmotion_node(-curenv->get_size() / 2,
2170                               curenv->get_fill_color());
2171         return;
2172       case 'v':
2173         if (!get_delim_number(&x, 'v'))
2174           break;
2175         type = TOKEN_NODE;
2176         nd = new vmotion_node(x, curenv->get_fill_color());
2177         return;
2178       case 'V':
2179         {
2180           symbol s = read_escape_name();
2181           if (!(s.is_null() || s.is_empty()))
2182             interpolate_environment_variable(s);
2183           break;
2184         }
2185       case 'w':
2186         do_width();
2187         break;
2188       case 'x':
2189         if (!get_delim_number(&x, 'v'))
2190           break;
2191         type = TOKEN_NODE;
2192         nd = new extra_size_node(x);
2193         return;
2194       case 'X':
2195         nd = do_special();
2196         if (!nd)
2197           break;
2198         type = TOKEN_NODE;
2199         return;
2200       case 'Y':
2201         {
2202           symbol s = read_escape_name();
2203           if (s.is_null() || s.is_empty())
2204             break;
2205           request_or_macro *p = lookup_request(s);
2206           macro *m = p->to_macro();
2207           if (!m) {
2208             error("can't transparently throughput a request");
2209             break;
2210           }
2211           nd = new special_node(*m);
2212           type = TOKEN_NODE;
2213           return;
2214         }
2215       case 'z':
2216         {
2217           next();
2218           if (type == TOKEN_NODE)
2219             nd = new zero_width_node(nd);
2220           else {
2221             charinfo *ci = get_char(1);
2222             if (ci == 0)
2223               break;
2224             node *gn = curenv->make_char_node(ci);
2225             if (gn == 0)
2226               break;
2227             nd = new zero_width_node(gn);
2228             type = TOKEN_NODE;
2229           }
2230           return;
2231         }
2232       case 'Z':
2233         nd = do_zero_width();
2234         if (nd == 0)
2235           break;
2236         type = TOKEN_NODE;
2237         return;
2238       case '{':
2239         goto ESCAPE_LEFT_BRACE;
2240       case '}':
2241         goto ESCAPE_RIGHT_BRACE;
2242       case '\n':
2243         break;
2244       case '[':
2245         if (!compatible_flag) {
2246           symbol s = read_long_escape_name(WITH_ARGS);
2247           if (s.is_null() || s.is_empty())
2248             break;
2249           if (have_string_arg) {
2250             have_string_arg = 0;
2251             nm = composite_glyph_name(s);
2252           }
2253           else {
2254             const char *gn = check_unicode_name(s.contents());
2255             if (gn) {
2256               const char *gn_decomposed = decompose_unicode(gn);
2257               if (gn_decomposed)
2258                 gn = &gn_decomposed[1];
2259               const char *groff_gn = unicode_to_glyph_name(gn);
2260               if (groff_gn)
2261                 nm = symbol(groff_gn);
2262               else {
2263                 char *buf = new char[strlen(gn) + 1 + 1];
2264                 strcpy(buf, "u");
2265                 strcat(buf, gn);
2266                 nm = symbol(buf);
2267                 a_delete buf;
2268               }
2269             }
2270             else
2271               nm = symbol(s.contents());
2272           }
2273           type = TOKEN_SPECIAL;
2274           return;
2275         }
2276         goto handle_normal_char;
2277       default:
2278         if (cc != escape_char && cc != '.')
2279           warning(WARN_ESCAPE, "escape character ignored before %1",
2280                   input_char_description(cc));
2281         goto handle_normal_char;
2282       }
2283     }
2284   }
2285 }
2286
2287 int token::operator==(const token &t)
2288 {
2289   if (type != t.type)
2290     return 0;
2291   switch(type) {
2292   case TOKEN_CHAR:
2293     return c == t.c;
2294   case TOKEN_SPECIAL:
2295     return nm == t.nm;
2296   case TOKEN_NUMBERED_CHAR:
2297     return val == t.val;
2298   default:
2299     return 1;
2300   }
2301 }
2302
2303 int token::operator!=(const token &t)
2304 {
2305   return !(*this == t);
2306 }
2307
2308 // is token a suitable delimiter (like ')?
2309
2310 int token::delimiter(int err)
2311 {
2312   switch(type) {
2313   case TOKEN_CHAR:
2314     switch(c) {
2315     case '0':
2316     case '1':
2317     case '2':
2318     case '3':
2319     case '4':
2320     case '5':
2321     case '6':
2322     case '7':
2323     case '8':
2324     case '9':
2325     case '+':
2326     case '-':
2327     case '/':
2328     case '*':
2329     case '%':
2330     case '<':
2331     case '>':
2332     case '=':
2333     case '&':
2334     case ':':
2335     case '(':
2336     case ')':
2337     case '.':
2338       if (err)
2339         error("cannot use character `%1' as a starting delimiter", char(c));
2340       return 0;
2341     default:
2342       return 1;
2343     }
2344   case TOKEN_NODE:
2345   case TOKEN_SPACE:
2346   case TOKEN_STRETCHABLE_SPACE:
2347   case TOKEN_UNSTRETCHABLE_SPACE:
2348   case TOKEN_TAB:
2349   case TOKEN_NEWLINE:
2350     if (err)
2351       error("cannot use %1 as a starting delimiter", description());
2352     return 0;
2353   default:
2354     return 1;
2355   }
2356 }
2357
2358 const char *token::description()
2359 {
2360   static char buf[4];
2361   switch (type) {
2362   case TOKEN_BACKSPACE:
2363     return "a backspace character";
2364   case TOKEN_CHAR:
2365     buf[0] = '`';
2366     buf[1] = c;
2367     buf[2] = '\'';
2368     buf[3] = '\0';
2369     return buf;
2370   case TOKEN_DUMMY:
2371     return "`\\&'";
2372   case TOKEN_ESCAPE:
2373     return "`\\e'";
2374   case TOKEN_HYPHEN_INDICATOR:
2375     return "`\\%'";
2376   case TOKEN_INTERRUPT:
2377     return "`\\c'";
2378   case TOKEN_ITALIC_CORRECTION:
2379     return "`\\/'";
2380   case TOKEN_LEADER:
2381     return "a leader character";
2382   case TOKEN_LEFT_BRACE:
2383     return "`\\{'";
2384   case TOKEN_MARK_INPUT:
2385     return "`\\k'";
2386   case TOKEN_NEWLINE:
2387     return "newline";
2388   case TOKEN_NODE:
2389     return "a node";
2390   case TOKEN_NUMBERED_CHAR:
2391     return "`\\N'";
2392   case TOKEN_RIGHT_BRACE:
2393     return "`\\}'";
2394   case TOKEN_SPACE:
2395     return "a space";
2396   case TOKEN_SPECIAL:
2397     return "a special character";
2398   case TOKEN_SPREAD:
2399     return "`\\p'";
2400   case TOKEN_STRETCHABLE_SPACE:
2401     return "`\\~'";
2402   case TOKEN_UNSTRETCHABLE_SPACE:
2403     return "`\\ '";
2404   case TOKEN_TAB:
2405     return "a tab character";
2406   case TOKEN_TRANSPARENT:
2407     return "`\\!'";
2408   case TOKEN_TRANSPARENT_DUMMY:
2409     return "`\\)'";
2410   case TOKEN_ZERO_WIDTH_BREAK:
2411     return "`\\:'";
2412   case TOKEN_EOF:
2413     return "end of input";
2414   default:
2415     break;
2416   }
2417   return "a magic token";
2418 }
2419
2420 void skip_line()
2421 {
2422   while (!tok.newline())
2423     if (tok.eof())
2424       return;
2425     else
2426       tok.next();
2427   tok.next();
2428 }
2429
2430 void compatible()
2431 {
2432   int n;
2433   if (has_arg() && get_integer(&n))
2434     compatible_flag = n != 0;
2435   else
2436     compatible_flag = 1;
2437   skip_line();
2438 }
2439
2440 static void empty_name_warning(int required)
2441 {
2442   if (tok.newline() || tok.eof()) {
2443     if (required)
2444       warning(WARN_MISSING, "missing name");
2445   }
2446   else if (tok.right_brace() || tok.tab()) {
2447     const char *start = tok.description();
2448     do {
2449       tok.next();
2450     } while (tok.space() || tok.right_brace() || tok.tab());
2451     if (!tok.newline() && !tok.eof())
2452       error("%1 is not allowed before an argument", start);
2453     else if (required)
2454       warning(WARN_MISSING, "missing name");
2455   }
2456   else if (required)
2457     error("name expected (got %1)", tok.description());
2458   else
2459     error("name expected (got %1): treated as missing", tok.description());
2460 }
2461
2462 static void non_empty_name_warning()
2463 {
2464   if (!tok.newline() && !tok.eof() && !tok.space() && !tok.tab()
2465       && !tok.right_brace()
2466       // We don't want to give a warning for .el\{
2467       && !tok.left_brace())
2468     error("%1 is not allowed in a name", tok.description());
2469 }
2470
2471 symbol get_name(int required)
2472 {
2473   if (compatible_flag) {
2474     char buf[3];
2475     tok.skip();
2476     if ((buf[0] = tok.ch()) != 0) {
2477       tok.next();
2478       if ((buf[1] = tok.ch()) != 0) {
2479         buf[2] = 0;
2480         tok.make_space();
2481       }
2482       else
2483         non_empty_name_warning();
2484       return symbol(buf);
2485     }
2486     else {
2487       empty_name_warning(required);
2488       return NULL_SYMBOL;
2489     }
2490   }
2491   else
2492     return get_long_name(required);
2493 }
2494
2495 symbol get_long_name(int required)
2496 {
2497   return do_get_long_name(required, 0);
2498 }
2499
2500 static symbol do_get_long_name(int required, char end)
2501 {
2502   while (tok.space())
2503     tok.next();
2504   char abuf[ABUF_SIZE];
2505   char *buf = abuf;
2506   int buf_size = ABUF_SIZE;
2507   int i = 0;
2508   for (;;) {
2509     // If end != 0 we normally have to append a null byte
2510     if (i + 2 > buf_size) {
2511       if (buf == abuf) {
2512         buf = new char[ABUF_SIZE*2];
2513         memcpy(buf, abuf, buf_size);
2514         buf_size = ABUF_SIZE*2;
2515       }
2516       else {
2517         char *old_buf = buf;
2518         buf = new char[buf_size*2];
2519         memcpy(buf, old_buf, buf_size);
2520         buf_size *= 2;
2521         a_delete old_buf;
2522       }
2523     }
2524     if ((buf[i] = tok.ch()) == 0 || buf[i] == end)
2525       break;
2526     i++;
2527     tok.next();
2528   }
2529   if (i == 0) {
2530     empty_name_warning(required);
2531     return NULL_SYMBOL;
2532   }
2533   if (end && buf[i] == end)
2534     buf[i+1] = '\0';
2535   else
2536     non_empty_name_warning();
2537   if (buf == abuf)
2538     return symbol(buf);
2539   else {
2540     symbol s(buf);
2541     a_delete buf;
2542     return s;
2543   }
2544 }
2545
2546 void exit_troff()
2547 {
2548   exit_started = 1;
2549   topdiv->set_last_page();
2550   if (!end_macro_name.is_null()) {
2551     spring_trap(end_macro_name);
2552     tok.next();
2553     process_input_stack();
2554   }
2555   curenv->final_break();
2556   tok.next();
2557   process_input_stack();
2558   end_diversions();
2559   if (topdiv->get_page_length() > 0) {
2560     done_end_macro = 1;
2561     topdiv->set_ejecting();
2562     static unsigned char buf[2] = { LAST_PAGE_EJECTOR, '\0' };
2563     input_stack::push(make_temp_iterator((char *)buf));
2564     topdiv->space(topdiv->get_page_length(), 1);
2565     tok.next();
2566     process_input_stack();
2567     seen_last_page_ejector = 1; // should be set already
2568     topdiv->set_ejecting();
2569     push_page_ejector();
2570     topdiv->space(topdiv->get_page_length(), 1);
2571     tok.next();
2572     process_input_stack();
2573   }
2574   // This will only happen if a trap-invoked macro starts a diversion,
2575   // or if vertical position traps have been disabled.
2576   cleanup_and_exit(0);
2577 }
2578
2579 // This implements .ex.  The input stack must be cleared before calling
2580 // exit_troff().
2581
2582 void exit_request()
2583 {
2584   input_stack::clear();
2585   if (exit_started)
2586     tok.next();
2587   else
2588     exit_troff();
2589 }
2590
2591 void return_macro_request()
2592 {
2593   if (has_arg() && tok.ch())
2594     input_stack::pop_macro();
2595   input_stack::pop_macro();
2596   tok.next();
2597 }
2598
2599 void end_macro()
2600 {
2601   end_macro_name = get_name();
2602   skip_line();
2603 }
2604
2605 void blank_line_macro()
2606 {
2607   blank_line_macro_name = get_name();
2608   skip_line();
2609 }
2610
2611 static void trapping_blank_line()
2612 {
2613   if (!blank_line_macro_name.is_null())
2614     spring_trap(blank_line_macro_name);
2615   else
2616     blank_line();
2617 }
2618
2619 void do_request()
2620 {
2621   int old_compatible_flag = compatible_flag;
2622   compatible_flag = 0;
2623   symbol nm = get_name();
2624   if (nm.is_null())
2625     skip_line();
2626   else
2627     interpolate_macro(nm, 1);
2628   compatible_flag = old_compatible_flag;
2629   request_or_macro *p = lookup_request(nm);
2630   macro *m = p->to_macro();
2631   if (m)
2632     tok.next();
2633 }
2634
2635 inline int possibly_handle_first_page_transition()
2636 {
2637   if (topdiv->before_first_page && curdiv == topdiv && !curenv->is_dummy()) {
2638     handle_first_page_transition();
2639     return 1;
2640   }
2641   else
2642     return 0;
2643 }
2644
2645 static int transparent_translate(int cc)
2646 {
2647   if (!invalid_input_char(cc)) {
2648     charinfo *ci = charset_table[cc];
2649     switch (ci->get_special_translation(1)) {
2650     case charinfo::TRANSLATE_SPACE:
2651       return ' ';
2652     case charinfo::TRANSLATE_STRETCHABLE_SPACE:
2653       return ESCAPE_TILDE;
2654     case charinfo::TRANSLATE_DUMMY:
2655       return ESCAPE_AMPERSAND;
2656     case charinfo::TRANSLATE_HYPHEN_INDICATOR:
2657       return ESCAPE_PERCENT;
2658     }
2659     // This is really ugly.
2660     ci = ci->get_translation(1);
2661     if (ci) {
2662       int c = ci->get_ascii_code();
2663       if (c != '\0')
2664         return c;
2665       error("can't translate %1 to special character `%2'"
2666             " in transparent throughput",
2667             input_char_description(cc),
2668             ci->nm.contents());
2669     }
2670   }
2671   return cc;
2672 }
2673
2674 class int_stack {
2675   struct int_stack_element {
2676     int n;
2677     int_stack_element *next;
2678   } *top;
2679 public:
2680   int_stack();
2681   ~int_stack();
2682   void push(int);
2683   int is_empty();
2684   int pop();
2685 };
2686
2687 int_stack::int_stack()
2688 {
2689   top = 0;
2690 }
2691
2692 int_stack::~int_stack()
2693 {
2694   while (top != 0) {
2695     int_stack_element *temp = top;
2696     top = top->next;
2697     delete temp;
2698   }
2699 }
2700
2701 int int_stack::is_empty()
2702 {
2703   return top == 0;
2704 }
2705
2706 void int_stack::push(int n)
2707 {
2708   int_stack_element *p = new int_stack_element;
2709   p->next = top;
2710   p->n = n;
2711   top = p;
2712 }
2713
2714 int int_stack::pop()
2715 {
2716   assert(top != 0);
2717   int_stack_element *p = top;
2718   top = top->next;
2719   int n = p->n;
2720   delete p;
2721   return n;
2722 }
2723
2724 int node::reread(int *)
2725 {
2726   return 0;
2727 }
2728
2729 int global_diverted_space = 0;
2730
2731 int diverted_space_node::reread(int *bolp)
2732 {
2733   global_diverted_space = 1;
2734   if (curenv->get_fill())
2735     trapping_blank_line();
2736   else
2737     curdiv->space(n);
2738   global_diverted_space = 0;
2739   *bolp = 1;
2740   return 1;
2741 }
2742
2743 int diverted_copy_file_node::reread(int *bolp)
2744 {
2745   curdiv->copy_file(filename.contents());
2746   *bolp = 1;
2747   return 1;
2748 }
2749
2750 int word_space_node::reread(int *)
2751 {
2752   if (unformat) {
2753     for (width_list *w = orig_width; w; w = w->next)
2754       curenv->space(w->width, w->sentence_width);
2755     unformat = 0;
2756     return 1;
2757   }
2758   return 0;
2759 }
2760
2761 int unbreakable_space_node::reread(int *)
2762 {
2763   return 0;
2764 }
2765
2766 int hmotion_node::reread(int *)
2767 {
2768   if (unformat && was_tab) {
2769     curenv->handle_tab(0);
2770     unformat = 0;
2771     return 1;
2772   }
2773   return 0;
2774 }
2775
2776 void process_input_stack()
2777 {
2778   int_stack trap_bol_stack;
2779   int bol = 1;
2780   for (;;) {
2781     int suppress_next = 0;
2782     switch (tok.type) {
2783     case token::TOKEN_CHAR:
2784       {
2785         unsigned char ch = tok.c;
2786         if (bol && !have_input
2787             && (ch == curenv->control_char
2788                 || ch == curenv->no_break_control_char)) {
2789           break_flag = ch == curenv->control_char;
2790           // skip tabs as well as spaces here
2791           do {
2792             tok.next();
2793           } while (tok.white_space());
2794           symbol nm = get_name();
2795 #if defined(DEBUGGING)
2796           if (debug_state) {
2797             if (! nm.is_null()) {
2798               if (strcmp(nm.contents(), "test") == 0) {
2799                 fprintf(stderr, "found it!\n");
2800                 fflush(stderr);
2801               }
2802               fprintf(stderr, "interpreting [%s]", nm.contents());
2803               if (strcmp(nm.contents(), "di") == 0 && topdiv != curdiv)
2804                 fprintf(stderr, " currently in diversion: %s",
2805                         curdiv->get_diversion_name());
2806               fprintf(stderr, "\n");
2807               fflush(stderr);
2808             }
2809           }
2810 #endif
2811           if (nm.is_null())
2812             skip_line();
2813           else {
2814             interpolate_macro(nm);
2815 #if defined(DEBUGGING)
2816             if (debug_state) {
2817               fprintf(stderr, "finished interpreting [%s] and environment state is\n", nm.contents());
2818               curenv->dump_troff_state();
2819             }
2820 #endif
2821           }
2822           suppress_next = 1;
2823         }
2824         else {
2825           if (possibly_handle_first_page_transition())
2826             ;
2827           else {
2828             for (;;) {
2829 #if defined(DEBUGGING)
2830               if (debug_state) {
2831                 fprintf(stderr, "found [%c]\n", ch); fflush(stderr);
2832               }
2833 #endif
2834               curenv->add_char(charset_table[ch]);
2835               tok.next();
2836               if (tok.type != token::TOKEN_CHAR)
2837                 break;
2838               ch = tok.c;
2839             }
2840             suppress_next = 1;
2841             bol = 0;
2842           }
2843         }
2844         break;
2845       }
2846     case token::TOKEN_TRANSPARENT:
2847       {
2848         if (bol) {
2849           if (possibly_handle_first_page_transition())
2850             ;
2851           else {
2852             int cc;
2853             do {
2854               node *n;
2855               cc = get_copy(&n);
2856               if (cc != EOF)
2857                 if (cc != '\0')
2858                   curdiv->transparent_output(transparent_translate(cc));
2859                 else
2860                   curdiv->transparent_output(n);
2861             } while (cc != '\n' && cc != EOF);
2862             if (cc == EOF)
2863               curdiv->transparent_output('\n');
2864           }
2865         }
2866         break;
2867       }
2868     case token::TOKEN_NEWLINE:
2869       {
2870         if (bol && !old_have_input
2871             && !curenv->get_prev_line_interrupted())
2872           trapping_blank_line();
2873         else {
2874           curenv->newline();
2875           bol = 1;
2876         }
2877         break;
2878       }
2879     case token::TOKEN_REQUEST:
2880       {
2881         int request_code = tok.c;
2882         tok.next();
2883         switch (request_code) {
2884         case TITLE_REQUEST:
2885           title();
2886           break;
2887         case COPY_FILE_REQUEST:
2888           copy_file();
2889           break;
2890         case TRANSPARENT_FILE_REQUEST:
2891           transparent_file();
2892           break;
2893 #ifdef COLUMN
2894         case VJUSTIFY_REQUEST:
2895           vjustify();
2896           break;
2897 #endif /* COLUMN */
2898         default:
2899           assert(0);
2900           break;
2901         }
2902         suppress_next = 1;
2903         break;
2904       }
2905     case token::TOKEN_SPACE:
2906       {
2907         if (possibly_handle_first_page_transition())
2908           ;
2909         else if (bol && !curenv->get_prev_line_interrupted()) {
2910           int nspaces = 0;
2911           // save space_width now so that it isn't changed by \f or \s
2912           // which we wouldn't notice here
2913           hunits space_width = curenv->get_space_width();
2914           do {
2915             nspaces += tok.nspaces();
2916             tok.next();
2917           } while (tok.space());
2918           if (tok.newline())
2919             trapping_blank_line();
2920           else {
2921             push_token(tok);
2922             curenv->do_break();
2923             curenv->add_node(new hmotion_node(space_width * nspaces,
2924                                               curenv->get_fill_color()));
2925             bol = 0;
2926           }
2927         }
2928         else {
2929           curenv->space();
2930           bol = 0;
2931         }
2932         break;
2933       }
2934     case token::TOKEN_EOF:
2935       return;
2936     case token::TOKEN_NODE:
2937       {
2938         if (possibly_handle_first_page_transition())
2939           ;
2940         else if (tok.nd->reread(&bol)) {
2941           delete tok.nd;
2942           tok.nd = 0;
2943         }
2944         else {
2945           curenv->add_node(tok.nd);
2946           tok.nd = 0;
2947           bol = 0;
2948           curenv->possibly_break_line(1);
2949         }
2950         break;
2951       }
2952     case token::TOKEN_PAGE_EJECTOR:
2953       {
2954         continue_page_eject();
2955         // I think we just want to preserve bol.
2956         // bol = 1;
2957         break;
2958       }
2959     case token::TOKEN_BEGIN_TRAP:
2960       {
2961         trap_bol_stack.push(bol);
2962         bol = 1;
2963         have_input = 0;
2964         break;
2965       }
2966     case token::TOKEN_END_TRAP:
2967       {
2968         if (trap_bol_stack.is_empty())
2969           error("spurious end trap token detected!");
2970         else
2971           bol = trap_bol_stack.pop();
2972         have_input = 0;
2973
2974         /* I'm not totally happy about this.  But I can't think of any other
2975           way to do it.  Doing an output_pending_lines() whenever a
2976           TOKEN_END_TRAP is detected doesn't work: for example,
2977
2978           .wh -1i x
2979           .de x
2980           'bp
2981           ..
2982           .wh -.5i y
2983           .de y
2984           .tl ''-%-''
2985           ..
2986           .br
2987           .ll .5i
2988           .sp |\n(.pu-1i-.5v
2989           a\%very\%very\%long\%word
2990
2991           will print all but the first lines from the word immediately
2992           after the footer, rather than on the next page. */
2993
2994         if (trap_bol_stack.is_empty())
2995           curenv->output_pending_lines();
2996         break;
2997       }
2998     default:
2999       {
3000         bol = 0;
3001         tok.process();
3002         break;
3003       }
3004     }
3005     if (!suppress_next)
3006       tok.next();
3007     trap_sprung_flag = 0;
3008   }
3009 }
3010
3011 #ifdef WIDOW_CONTROL
3012
3013 void flush_pending_lines()
3014 {
3015   while (!tok.newline() && !tok.eof())
3016     tok.next();
3017   curenv->output_pending_lines();
3018   tok.next();
3019 }
3020
3021 #endif /* WIDOW_CONTROL */
3022
3023 request_or_macro::request_or_macro()
3024 {
3025 }
3026
3027 macro *request_or_macro::to_macro()
3028 {
3029   return 0;
3030 }
3031
3032 request::request(REQUEST_FUNCP pp) : p(pp)
3033 {
3034 }
3035
3036 void request::invoke(symbol, int)
3037 {
3038   (*p)();
3039 }
3040
3041 struct char_block {
3042   enum { SIZE = 128 };
3043   unsigned char s[SIZE];
3044   char_block *next;
3045   char_block();
3046 };
3047
3048 char_block::char_block()
3049 : next(0)
3050 {
3051 }
3052
3053 class char_list {
3054 public:
3055   char_list();
3056   ~char_list();
3057   void append(unsigned char);
3058   void set(unsigned char, int);
3059   unsigned char get(int);
3060   int length();
3061 private:
3062   unsigned char *ptr;
3063   int len;
3064   char_block *head;
3065   char_block *tail;
3066   friend class macro_header;
3067   friend class string_iterator;
3068 };
3069
3070 char_list::char_list()
3071 : ptr(0), len(0), head(0), tail(0)
3072 {
3073 }
3074
3075 char_list::~char_list()
3076 {
3077   while (head != 0) {
3078     char_block *tem = head;
3079     head = head->next;
3080     delete tem;
3081   }
3082 }
3083
3084 int char_list::length()
3085 {
3086   return len;
3087 }
3088
3089 void char_list::append(unsigned char c)
3090 {
3091   if (tail == 0) {
3092     head = tail = new char_block;
3093     ptr = tail->s;
3094   }
3095   else {
3096     if (ptr >= tail->s + char_block::SIZE) {
3097       tail->next = new char_block;
3098       tail = tail->next;
3099       ptr = tail->s;
3100     }
3101   }
3102   *ptr++ = c;
3103   len++;
3104 }
3105
3106 void char_list::set(unsigned char c, int offset)
3107 {
3108   assert(len > offset);
3109   // optimization for access at the end
3110   int boundary = len - len % char_block::SIZE;
3111   if (offset >= boundary) {
3112     *(tail->s + offset - boundary) = c;
3113     return;
3114   }
3115   char_block *tem = head;
3116   int l = 0;
3117   for (;;) {
3118     l += char_block::SIZE;
3119     if (l > offset) {
3120       *(tem->s + offset % char_block::SIZE) = c;
3121       return;
3122     }
3123     tem = tem->next;
3124   }
3125 }
3126
3127 unsigned char char_list::get(int offset)
3128 {
3129   assert(len > offset);
3130   // optimization for access at the end
3131   int boundary = len - len % char_block::SIZE;
3132   if (offset >= boundary)
3133     return *(tail->s + offset - boundary);
3134   char_block *tem = head;
3135   int l = 0;
3136   for (;;) {
3137     l += char_block::SIZE;
3138     if (l > offset)
3139       return *(tem->s + offset % char_block::SIZE);
3140     tem = tem->next;
3141   }
3142 }
3143
3144 class node_list {
3145   node *head;
3146   node *tail;
3147 public:
3148   node_list();
3149   ~node_list();
3150   void append(node *);
3151   int length();
3152   node *extract();
3153
3154   friend class macro_header;
3155   friend class string_iterator;
3156 };
3157
3158 void node_list::append(node *n)
3159 {
3160   if (head == 0) {
3161     n->next = 0;
3162     head = tail = n;
3163   }
3164   else {
3165     n->next = 0;
3166     tail = tail->next = n;
3167   }
3168 }
3169
3170 int node_list::length()
3171 {
3172   int total = 0;
3173   for (node *n = head; n != 0; n = n->next)
3174     ++total;
3175   return total;
3176 }
3177
3178 node_list::node_list()
3179 {
3180   head = tail = 0;
3181 }
3182
3183 node *node_list::extract()
3184 {
3185   node *temp = head;
3186   head = tail = 0;
3187   return temp;
3188 }
3189
3190 node_list::~node_list()
3191 {
3192   delete_node_list(head);
3193 }
3194
3195 class macro_header {
3196 public:
3197   int count;
3198   char_list cl;
3199   node_list nl;
3200   macro_header() { count = 1; }
3201   macro_header *copy(int);
3202 };
3203
3204 macro::~macro()
3205 {
3206   if (p != 0 && --(p->count) <= 0)
3207     delete p;
3208 }
3209
3210 macro::macro()
3211 : is_a_diversion(0), is_a_string(1)
3212 {
3213   if (!input_stack::get_location(1, &filename, &lineno)) {
3214     filename = 0;
3215     lineno = 0;
3216   }
3217   len = 0;
3218   empty_macro = 1;
3219   p = 0;
3220 }
3221
3222 macro::macro(const macro &m)
3223 : filename(m.filename), lineno(m.lineno), len(m.len),
3224   empty_macro(m.empty_macro), is_a_diversion(m.is_a_diversion),
3225   is_a_string(m.is_a_string), p(m.p)
3226 {
3227   if (p != 0)
3228     p->count++;
3229 }
3230
3231 macro::macro(int is_div)
3232 : is_a_diversion(is_div)
3233 {
3234   if (!input_stack::get_location(1, &filename, &lineno)) {
3235     filename = 0;
3236     lineno = 0;
3237   }
3238   len = 0;
3239   empty_macro = 1;
3240   is_a_string = 1;
3241   p = 0;
3242 }
3243
3244 int macro::is_diversion()
3245 {
3246   return is_a_diversion;
3247 }
3248
3249 int macro::is_string()
3250 {
3251   return is_a_string;
3252 }
3253
3254 void macro::clear_string_flag()
3255 {
3256   is_a_string = 0;
3257 }
3258
3259 macro &macro::operator=(const macro &m)
3260 {
3261   // don't assign object
3262   if (m.p != 0)
3263     m.p->count++;
3264   if (p != 0 && --(p->count) <= 0)
3265     delete p;
3266   p = m.p;
3267   filename = m.filename;
3268   lineno = m.lineno;
3269   len = m.len;
3270   empty_macro = m.empty_macro;
3271   is_a_diversion = m.is_a_diversion;
3272   is_a_string = m.is_a_string;
3273   return *this;
3274 }
3275
3276 void macro::append(unsigned char c)
3277 {
3278   assert(c != 0);
3279   if (p == 0)
3280     p = new macro_header;
3281   if (p->cl.length() != len) {
3282     macro_header *tem = p->copy(len);
3283     if (--(p->count) <= 0)
3284       delete p;
3285     p = tem;
3286   }
3287   p->cl.append(c);
3288   ++len;
3289   if (c != PUSH_GROFF_MODE && c != PUSH_COMP_MODE && c != POP_GROFFCOMP_MODE)
3290     empty_macro = 0;
3291 }
3292
3293 void macro::set(unsigned char c, int offset)
3294 {
3295   assert(p != 0);
3296   assert(c != 0);
3297   p->cl.set(c, offset);
3298 }
3299
3300 unsigned char macro::get(int offset)
3301 {
3302   assert(p != 0);
3303   return p->cl.get(offset);
3304 }
3305
3306 int macro::length()
3307 {
3308   return len;
3309 }
3310
3311 void macro::append_str(const char *s)
3312 {
3313   int i = 0;
3314
3315   if (s) {
3316     while (s[i] != (char)0) {
3317       append(s[i]);
3318       i++;
3319     }
3320   }
3321 }
3322
3323 void macro::append(node *n)
3324 {
3325   assert(n != 0);
3326   if (p == 0)
3327     p = new macro_header;
3328   if (p->cl.length() != len) {
3329     macro_header *tem = p->copy(len);
3330     if (--(p->count) <= 0)
3331       delete p;
3332     p = tem;
3333   }
3334   p->cl.append(0);
3335   p->nl.append(n);
3336   ++len;
3337   empty_macro = 0;
3338 }
3339
3340 void macro::append_unsigned(unsigned int i)
3341 {
3342   unsigned int j = i / 10;
3343   if (j != 0)
3344     append_unsigned(j);
3345   append(((unsigned char)(((int)'0') + i % 10)));
3346 }
3347
3348 void macro::append_int(int i)
3349 {
3350   if (i < 0) {
3351     append('-');
3352     i = -i;
3353   }
3354   append_unsigned((unsigned int)i);
3355 }
3356
3357 void macro::print_size()
3358 {
3359   errprint("%1", len);
3360 }
3361
3362 // make a copy of the first n bytes
3363
3364 macro_header *macro_header::copy(int n)
3365 {
3366   macro_header *p = new macro_header;
3367   char_block *bp = cl.head;
3368   unsigned char *ptr = bp->s;
3369   node *nd = nl.head;
3370   while (--n >= 0) {
3371     if (ptr >= bp->s + char_block::SIZE) {
3372       bp = bp->next;
3373       ptr = bp->s;
3374     }
3375     unsigned char c = *ptr++;
3376     p->cl.append(c);
3377     if (c == 0) {
3378       p->nl.append(nd->copy());
3379       nd = nd->next;
3380     }
3381   }
3382   return p;
3383 }
3384
3385 void print_macros()
3386 {
3387   object_dictionary_iterator iter(request_dictionary);
3388   request_or_macro *rm;
3389   symbol s;
3390   while (iter.get(&s, (object **)&rm)) {
3391     assert(!s.is_null());
3392     macro *m = rm->to_macro();
3393     if (m) {
3394       errprint("%1\t", s.contents());
3395       m->print_size();
3396       errprint("\n");
3397     }
3398   }
3399   fflush(stderr);
3400   skip_line();
3401 }
3402
3403 class string_iterator : public input_iterator {
3404   macro mac;
3405   const char *how_invoked;
3406   int newline_flag;
3407   int lineno;
3408   char_block *bp;
3409   int count;                    // of characters remaining
3410   node *nd;
3411   int saved_compatible_flag;
3412   int with_break;               // inherited from the caller
3413 protected:
3414   symbol nm;
3415   string_iterator();
3416 public:
3417   string_iterator(const macro &, const char * = 0, symbol = NULL_SYMBOL);
3418   int fill(node **);
3419   int peek();
3420   int get_location(int, const char **, int *);
3421   void backtrace();
3422   int get_break_flag() { return with_break; }
3423   void save_compatible_flag(int f) { saved_compatible_flag = f; }
3424   int get_compatible_flag() { return saved_compatible_flag; }
3425   int is_diversion();
3426 };
3427
3428 string_iterator::string_iterator(const macro &m, const char *p, symbol s)
3429 : input_iterator(m.is_a_diversion), mac(m), how_invoked(p), newline_flag(0),
3430   lineno(1), nm(s)
3431 {
3432   count = mac.len;
3433   if (count != 0) {
3434     bp = mac.p->cl.head;
3435     nd = mac.p->nl.head;
3436     ptr = eptr = bp->s;
3437   }
3438   else {
3439     bp = 0;
3440     nd = 0;
3441     ptr = eptr = 0;
3442   }
3443   with_break = input_stack::get_break_flag();
3444 }
3445
3446 string_iterator::string_iterator()
3447 {
3448   bp = 0;
3449   nd = 0;
3450   ptr = eptr = 0;
3451   newline_flag = 0;
3452   how_invoked = 0;
3453   lineno = 1;
3454   count = 0;
3455   with_break = input_stack::get_break_flag();
3456 }
3457
3458 int string_iterator::is_diversion()
3459 {
3460   return mac.is_diversion();
3461 }
3462
3463 int string_iterator::fill(node **np)
3464 {
3465   if (newline_flag)
3466     lineno++;
3467   newline_flag = 0;
3468   if (count <= 0)
3469     return EOF;
3470   const unsigned char *p = eptr;
3471   if (p >= bp->s + char_block::SIZE) {
3472     bp = bp->next;
3473     p = bp->s;
3474   }
3475   if (*p == '\0') {
3476     if (np) {
3477       *np = nd->copy();
3478       if (is_diversion())
3479         (*np)->div_nest_level = input_stack::get_div_level();
3480       else
3481         (*np)->div_nest_level = 0;
3482     }
3483     nd = nd->next;
3484     eptr = ptr = p + 1;
3485     count--;
3486     return 0;
3487   }
3488   const unsigned char *e = bp->s + char_block::SIZE;
3489   if (e - p > count)
3490     e = p + count;
3491   ptr = p;
3492   while (p < e) {
3493     unsigned char c = *p;
3494     if (c == '\n' || c == ESCAPE_NEWLINE) {
3495       newline_flag = 1;
3496       p++;
3497       break;
3498     }
3499     if (c == '\0')
3500       break;
3501     p++;
3502   }
3503   eptr = p;
3504   count -= p - ptr;
3505   return *ptr++;
3506 }
3507
3508 int string_iterator::peek()
3509 {
3510   if (count <= 0)
3511     return EOF;
3512   const unsigned char *p = eptr;
3513   if (p >= bp->s + char_block::SIZE) {
3514     p = bp->next->s;
3515   }
3516   return *p;
3517 }
3518
3519 int string_iterator::get_location(int allow_macro,
3520                                   const char **filep, int *linep)
3521 {
3522   if (!allow_macro)
3523     return 0;
3524   if (mac.filename == 0)
3525     return 0;
3526   *filep = mac.filename;
3527   *linep = mac.lineno + lineno - 1;
3528   return 1;
3529 }
3530
3531 void string_iterator::backtrace()
3532 {
3533   if (mac.filename) {
3534     errprint("%1:%2: backtrace", mac.filename, mac.lineno + lineno - 1);
3535     if (how_invoked) {
3536       if (!nm.is_null())
3537         errprint(": %1 `%2'\n", how_invoked, nm.contents());
3538       else
3539         errprint(": %1\n", how_invoked);
3540     }
3541     else
3542       errprint("\n");
3543   }
3544 }
3545
3546 class temp_iterator : public input_iterator {
3547   unsigned char *base;
3548   temp_iterator(const char *, int len);
3549 public:
3550   ~temp_iterator();
3551   friend input_iterator *make_temp_iterator(const char *);
3552 };
3553
3554 #ifdef __GNUG__
3555 inline
3556 #endif
3557 temp_iterator::temp_iterator(const char *s, int len)
3558 {
3559   base = new unsigned char[len];
3560   memcpy(base, s, len);
3561   ptr = base;
3562   eptr = base + len;
3563 }
3564
3565 temp_iterator::~temp_iterator()
3566 {
3567   a_delete base;
3568 }
3569
3570 class small_temp_iterator : public input_iterator {
3571 private:
3572   small_temp_iterator(const char *, int);
3573   ~small_temp_iterator();
3574   enum { BLOCK = 16 };
3575   static small_temp_iterator *free_list;
3576   void *operator new(size_t);
3577   void operator delete(void *);
3578   enum { SIZE = 12 };
3579   unsigned char buf[SIZE];
3580   friend input_iterator *make_temp_iterator(const char *);
3581 };
3582
3583 small_temp_iterator *small_temp_iterator::free_list = 0;
3584
3585 void *small_temp_iterator::operator new(size_t n)
3586 {
3587   assert(n == sizeof(small_temp_iterator));
3588   if (!free_list) {
3589     free_list =
3590       (small_temp_iterator *)new char[sizeof(small_temp_iterator)*BLOCK];
3591     for (int i = 0; i < BLOCK - 1; i++)
3592       free_list[i].next = free_list + i + 1;
3593     free_list[BLOCK-1].next = 0;
3594   }
3595   small_temp_iterator *p = free_list;
3596   free_list = (small_temp_iterator *)(free_list->next);
3597   p->next = 0;
3598   return p;
3599 }
3600
3601 #ifdef __GNUG__
3602 inline
3603 #endif
3604 void small_temp_iterator::operator delete(void *p)
3605 {
3606   if (p) {
3607     ((small_temp_iterator *)p)->next = free_list;
3608     free_list = (small_temp_iterator *)p;
3609   }
3610 }
3611
3612 small_temp_iterator::~small_temp_iterator()
3613 {
3614 }
3615
3616 #ifdef __GNUG__
3617 inline
3618 #endif
3619 small_temp_iterator::small_temp_iterator(const char *s, int len)
3620 {
3621   for (int i = 0; i < len; i++)
3622     buf[i] = s[i];
3623   ptr = buf;
3624   eptr = buf + len;
3625 }
3626
3627 input_iterator *make_temp_iterator(const char *s)
3628 {
3629   if (s == 0)
3630     return new small_temp_iterator(s, 0);
3631   else {
3632     int n = strlen(s);
3633     if (n <= small_temp_iterator::SIZE)
3634       return new small_temp_iterator(s, n);
3635     else
3636       return new temp_iterator(s, n);
3637   }
3638 }
3639
3640 // this is used when macros with arguments are interpolated
3641
3642 struct arg_list {
3643   macro mac;
3644   int space_follows;
3645   arg_list *next;
3646   arg_list(const macro &, int);
3647   arg_list(const arg_list *);
3648   ~arg_list();
3649 };
3650
3651 arg_list::arg_list(const macro &m, int s) : mac(m), space_follows(s), next(0)
3652 {
3653 }
3654
3655 arg_list::arg_list(const arg_list *al)
3656 : next(0)
3657 {
3658   mac = al->mac;
3659   space_follows = al->space_follows;
3660   arg_list **a = &next;
3661   arg_list *p = al->next;
3662   while (p) {
3663     *a = new arg_list(p->mac, p->space_follows);
3664     p = p->next;
3665     a = &(*a)->next;
3666   }
3667 }
3668
3669 arg_list::~arg_list()
3670 {
3671 }
3672
3673 class macro_iterator : public string_iterator {
3674   arg_list *args;
3675   int argc;
3676   int with_break;               // whether called as .foo or 'foo
3677 public:
3678   macro_iterator(symbol, macro &, const char * = "macro", int = 0);
3679   macro_iterator();
3680   ~macro_iterator();
3681   int has_args() { return 1; }
3682   input_iterator *get_arg(int);
3683   arg_list *get_arg_list();
3684   symbol get_macro_name();
3685   int space_follows_arg(int);
3686   int get_break_flag() { return with_break; }
3687   int nargs() { return argc; }
3688   void add_arg(const macro &, int);
3689   void shift(int);
3690   int is_macro() { return 1; }
3691   int is_diversion();
3692 };
3693
3694 input_iterator *macro_iterator::get_arg(int i)
3695 {
3696   if (i == 0)
3697     return make_temp_iterator(nm.contents());
3698   if (i > 0 && i <= argc) {
3699     arg_list *p = args;
3700     for (int j = 1; j < i; j++) {
3701       assert(p != 0);
3702       p = p->next;
3703     }
3704     return new string_iterator(p->mac);
3705   }
3706   else
3707     return 0;
3708 }
3709
3710 arg_list *macro_iterator::get_arg_list()
3711 {
3712   return args;
3713 }
3714
3715 symbol macro_iterator::get_macro_name()
3716 {
3717   return nm;
3718 }
3719
3720 int macro_iterator::space_follows_arg(int i)
3721 {
3722   if (i > 0 && i <= argc) {
3723     arg_list *p = args;
3724     for (int j = 1; j < i; j++) {
3725       assert(p != 0);
3726       p = p->next;
3727     }
3728     return p->space_follows;
3729   }
3730   else
3731     return 0;
3732 }
3733
3734 void macro_iterator::add_arg(const macro &m, int s)
3735 {
3736   arg_list **p;
3737   for (p = &args; *p; p = &((*p)->next))
3738     ;
3739   *p = new arg_list(m, s);
3740   ++argc;
3741 }
3742
3743 void macro_iterator::shift(int n)
3744 {
3745   while (n > 0 && argc > 0) {
3746     arg_list *tem = args;
3747     args = args->next;
3748     delete tem;
3749     --argc;
3750     --n;
3751   }
3752 }
3753
3754 // This gets used by eg .if '\?xxx\?''.
3755
3756 int operator==(const macro &m1, const macro &m2)
3757 {
3758   if (m1.len != m2.len)
3759     return 0;
3760   string_iterator iter1(m1);
3761   string_iterator iter2(m2);
3762   int n = m1.len;
3763   while (--n >= 0) {
3764     node *nd1 = 0;
3765     int c1 = iter1.get(&nd1);
3766     assert(c1 != EOF);
3767     node *nd2 = 0;
3768     int c2 = iter2.get(&nd2);
3769     assert(c2 != EOF);
3770     if (c1 != c2) {
3771       if (c1 == 0)
3772         delete nd1;
3773       else if (c2 == 0)
3774         delete nd2;
3775       return 0;
3776     }
3777     if (c1 == 0) {
3778       assert(nd1 != 0);
3779       assert(nd2 != 0);
3780       int are_same = nd1->type() == nd2->type() && nd1->same(nd2);
3781       delete nd1;
3782       delete nd2;
3783       if (!are_same)
3784         return 0;
3785     }
3786   }
3787   return 1;
3788 }
3789
3790 static void interpolate_macro(symbol nm, int no_next)
3791 {
3792   request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm);
3793   if (p == 0) {
3794     int warned = 0;
3795     const char *s = nm.contents();
3796     if (strlen(s) > 2) {
3797       request_or_macro *r;
3798       char buf[3];
3799       buf[0] = s[0];
3800       buf[1] = s[1];
3801       buf[2] = '\0';
3802       r = (request_or_macro *)request_dictionary.lookup(symbol(buf));
3803       if (r) {
3804         macro *m = r->to_macro();
3805         if (!m || !m->empty())
3806           warned = warning(WARN_SPACE,
3807                            "macro `%1' not defined "
3808                            "(possibly missing space after `%2')",
3809                            nm.contents(), buf);
3810       }
3811     }
3812     if (!warned) {
3813       warning(WARN_MAC, "macro `%1' not defined", nm.contents());
3814       p = new macro;
3815       request_dictionary.define(nm, p);
3816     }
3817   }
3818   if (p)
3819     p->invoke(nm, no_next);
3820   else {
3821     skip_line();
3822     return;
3823   }
3824 }
3825
3826 static void decode_args(macro_iterator *mi)
3827 {
3828   if (!tok.newline() && !tok.eof()) {
3829     node *n;
3830     int c = get_copy(&n);
3831     for (;;) {
3832       while (c == ' ')
3833         c = get_copy(&n);
3834       if (c == '\n' || c == EOF)
3835         break;
3836       macro arg;
3837       int quote_input_level = 0;
3838       int done_tab_warning = 0;
3839       arg.append(compatible_flag ? PUSH_COMP_MODE : PUSH_GROFF_MODE);
3840       // we store discarded double quotes for \$^
3841       if (c == '"') {
3842         arg.append(DOUBLE_QUOTE);
3843         quote_input_level = input_stack::get_level();
3844         c = get_copy(&n);
3845       }
3846       while (c != EOF && c != '\n' && !(c == ' ' && quote_input_level == 0)) {
3847         if (quote_input_level > 0 && c == '"'
3848             && (compatible_flag
3849                 || input_stack::get_level() == quote_input_level)) {
3850           arg.append(DOUBLE_QUOTE);
3851           c = get_copy(&n);
3852           if (c == '"') {
3853             arg.append(c);
3854             c = get_copy(&n);
3855           }
3856           else
3857             break;
3858         }
3859         else {
3860           if (c == 0)
3861             arg.append(n);
3862           else {
3863             if (c == '\t' && quote_input_level == 0 && !done_tab_warning) {
3864               warning(WARN_TAB, "tab character in unquoted macro argument");
3865               done_tab_warning = 1;
3866             }
3867             arg.append(c);
3868           }
3869           c = get_copy(&n);
3870         }
3871       }
3872       arg.append(POP_GROFFCOMP_MODE);
3873       mi->add_arg(arg, (c == ' '));
3874     }
3875   }
3876 }
3877
3878 static void decode_string_args(macro_iterator *mi)
3879 {
3880   node *n;
3881   int c = get_copy(&n);
3882   for (;;) {
3883     while (c == ' ')
3884       c = get_copy(&n);
3885     if (c == '\n' || c == EOF) {
3886       error("missing `]'");
3887       break;
3888     }
3889     if (c == ']')
3890       break;
3891     macro arg;
3892     int quote_input_level = 0;
3893     int done_tab_warning = 0;
3894     if (c == '"') {
3895       quote_input_level = input_stack::get_level();
3896       c = get_copy(&n);
3897     }
3898     while (c != EOF && c != '\n'
3899            && !(c == ']' && quote_input_level == 0)
3900            && !(c == ' ' && quote_input_level == 0)) {
3901       if (quote_input_level > 0 && c == '"'
3902           && input_stack::get_level() == quote_input_level) {
3903         c = get_copy(&n);
3904         if (c == '"') {
3905           arg.append(c);
3906           c = get_copy(&n);
3907         }
3908         else
3909           break;
3910       }
3911       else {
3912         if (c == 0)
3913           arg.append(n);
3914         else {
3915           if (c == '\t' && quote_input_level == 0 && !done_tab_warning) {
3916             warning(WARN_TAB, "tab character in unquoted string argument");
3917             done_tab_warning = 1;
3918           }
3919           arg.append(c);
3920         }
3921         c = get_copy(&n);
3922       }
3923     }
3924     mi->add_arg(arg, (c == ' '));
3925   }
3926 }
3927
3928 void macro::invoke(symbol nm, int no_next)
3929 {
3930   macro_iterator *mi = new macro_iterator(nm, *this);
3931   decode_args(mi);
3932   input_stack::push(mi);
3933   // we must delay tok.next() in case the function has been called by
3934   // do_request to assure proper handling of compatible_flag
3935   if (!no_next)
3936     tok.next();
3937 }
3938
3939 macro *macro::to_macro()
3940 {
3941   return this;
3942 }
3943
3944 int macro::empty()
3945 {
3946   return empty_macro == 1;
3947 }
3948
3949 macro_iterator::macro_iterator(symbol s, macro &m, const char *how_called,
3950                                int init_args)
3951 : string_iterator(m, how_called, s), args(0), argc(0), with_break(break_flag)
3952 {
3953   if (init_args) {
3954     arg_list *al = input_stack::get_arg_list();
3955     if (al) {
3956       args = new arg_list(al);
3957       argc = input_stack::nargs();
3958     }
3959   }
3960 }
3961
3962 macro_iterator::macro_iterator() : args(0), argc(0), with_break(break_flag)
3963 {
3964 }
3965
3966 macro_iterator::~macro_iterator()
3967 {
3968   while (args != 0) {
3969     arg_list *tem = args;
3970     args = args->next;
3971     delete tem;
3972   }
3973 }
3974
3975 dictionary composite_dictionary(17);
3976
3977 void composite_request()
3978 {
3979   symbol from = get_name(1);
3980   if (!from.is_null()) {
3981     const char *from_gn = glyph_name_to_unicode(from.contents());
3982     if (!from_gn) {
3983       from_gn = check_unicode_name(from.contents());
3984       if (!from_gn) {
3985         error("invalid composite glyph name `%1'", from.contents());
3986         skip_line();
3987         return;
3988       }
3989     }
3990     const char *from_decomposed = decompose_unicode(from_gn);
3991     if (from_decomposed)
3992       from_gn = &from_decomposed[1];
3993     symbol to = get_name(1);
3994     if (to.is_null())
3995       composite_dictionary.remove(symbol(from_gn));
3996     else {
3997       const char *to_gn = glyph_name_to_unicode(to.contents());
3998       if (!to_gn) {
3999         to_gn = check_unicode_name(to.contents());
4000         if (!to_gn) {
4001           error("invalid composite glyph name `%1'", to.contents());
4002           skip_line();
4003           return;
4004         }
4005       }
4006       const char *to_decomposed = decompose_unicode(to_gn);
4007       if (to_decomposed)
4008         to_gn = &to_decomposed[1];
4009       if (strcmp(from_gn, to_gn) == 0)
4010         composite_dictionary.remove(symbol(from_gn));
4011       else
4012         (void)composite_dictionary.lookup(symbol(from_gn), (void *)to_gn);
4013     }
4014   }
4015   skip_line();
4016 }
4017
4018 static symbol composite_glyph_name(symbol nm)
4019 {
4020   macro_iterator *mi = new macro_iterator();
4021   decode_string_args(mi);
4022   input_stack::push(mi);
4023   const char *gn = glyph_name_to_unicode(nm.contents());
4024   if (!gn) {
4025     gn = check_unicode_name(nm.contents());
4026     if (!gn) {
4027       error("invalid base glyph `%1' in composite glyph name", nm.contents());
4028       return EMPTY_SYMBOL;
4029     }
4030   }
4031   const char *gn_decomposed = decompose_unicode(gn);
4032   string glyph_name(gn_decomposed ? &gn_decomposed[1] : gn);
4033   string gl;
4034   int n = input_stack::nargs();
4035   for (int i = 1; i <= n; i++) {
4036     glyph_name += '_';
4037     input_iterator *p = input_stack::get_arg(i);
4038     gl.clear();
4039     int c;
4040     while ((c = p->get(0)) != EOF)
4041       if (c != DOUBLE_QUOTE)
4042         gl += c;
4043     gl += '\0';
4044     const char *u = glyph_name_to_unicode(gl.contents());
4045     if (!u) {
4046       u = check_unicode_name(gl.contents());
4047       if (!u) {
4048         error("invalid component `%1' in composite glyph name",
4049               gl.contents());
4050         return EMPTY_SYMBOL;
4051       }
4052     }
4053     const char *decomposed = decompose_unicode(u);
4054     if (decomposed)
4055       u = &decomposed[1];
4056     void *mapped_composite = composite_dictionary.lookup(symbol(u));
4057     if (mapped_composite)
4058       u = (const char *)mapped_composite;
4059     glyph_name += u;
4060   }
4061   glyph_name += '\0';
4062   const char *groff_gn = unicode_to_glyph_name(glyph_name.contents());
4063   if (groff_gn)
4064     return symbol(groff_gn);
4065   gl.clear();
4066   gl += 'u';
4067   gl += glyph_name;
4068   return symbol(gl.contents());
4069 }
4070
4071 int trap_sprung_flag = 0;
4072 int postpone_traps_flag = 0;
4073 symbol postponed_trap;
4074
4075 void spring_trap(symbol nm)
4076 {
4077   assert(!nm.is_null());
4078   trap_sprung_flag = 1;
4079   if (postpone_traps_flag) {
4080     postponed_trap = nm;
4081     return;
4082   }
4083   static char buf[2] = { BEGIN_TRAP, 0 };
4084   static char buf2[2] = { END_TRAP, '\0' };
4085   input_stack::push(make_temp_iterator(buf2));
4086   request_or_macro *p = lookup_request(nm);
4087   macro *m = p->to_macro();
4088   if (m)
4089     input_stack::push(new macro_iterator(nm, *m, "trap-invoked macro"));
4090   else
4091     error("you can't invoke a request with a trap");
4092   input_stack::push(make_temp_iterator(buf));
4093 }
4094
4095 void postpone_traps()
4096 {
4097   postpone_traps_flag = 1;
4098 }
4099
4100 int unpostpone_traps()
4101 {
4102   postpone_traps_flag = 0;
4103   if (!postponed_trap.is_null()) {
4104     spring_trap(postponed_trap);
4105     postponed_trap = NULL_SYMBOL;
4106     return 1;
4107   }
4108   else
4109     return 0;
4110 }
4111
4112 void read_request()
4113 {
4114   macro_iterator *mi = new macro_iterator;
4115   int reading_from_terminal = isatty(fileno(stdin));
4116   int had_prompt = 0;
4117   if (!tok.newline() && !tok.eof()) {
4118     int c = get_copy(0);
4119     while (c == ' ')
4120       c = get_copy(0);
4121     while (c != EOF && c != '\n' && c != ' ') {
4122       if (!invalid_input_char(c)) {
4123         if (reading_from_terminal)
4124           fputc(c, stderr);
4125         had_prompt = 1;
4126       }
4127       c = get_copy(0);
4128     }
4129     if (c == ' ') {
4130       tok.make_space();
4131       decode_args(mi);
4132     }
4133   }
4134   if (reading_from_terminal) {
4135     fputc(had_prompt ? ':' : '\a', stderr);
4136     fflush(stderr);
4137   }
4138   input_stack::push(mi);
4139   macro mac;
4140   int nl = 0;
4141   int c;
4142   while ((c = getchar()) != EOF) {
4143     if (invalid_input_char(c))
4144       warning(WARN_INPUT, "invalid input character code %1", int(c));
4145     else {
4146       if (c == '\n') {
4147         if (nl)
4148           break;
4149         else
4150           nl = 1;
4151       }
4152       else
4153         nl = 0;
4154       mac.append(c);
4155     }
4156   }
4157   if (reading_from_terminal)
4158     clearerr(stdin);
4159   input_stack::push(new string_iterator(mac));
4160   tok.next();
4161 }
4162
4163 enum define_mode { DEFINE_NORMAL, DEFINE_APPEND, DEFINE_IGNORE };
4164 enum calling_mode { CALLING_NORMAL, CALLING_INDIRECT };
4165 enum comp_mode { COMP_IGNORE, COMP_DISABLE, COMP_ENABLE };
4166
4167 void do_define_string(define_mode mode, comp_mode comp)
4168 {
4169   symbol nm;
4170   node *n = 0;          // pacify compiler
4171   int c;
4172   nm = get_name(1);
4173   if (nm.is_null()) {
4174     skip_line();
4175     return;
4176   }
4177   if (tok.newline())
4178     c = '\n';
4179   else if (tok.tab())
4180     c = '\t';
4181   else if (!tok.space()) {
4182     error("bad string definition");
4183     skip_line();
4184     return;
4185   }
4186   else
4187     c = get_copy(&n);
4188   while (c == ' ')
4189     c = get_copy(&n);
4190   if (c == '"')
4191     c = get_copy(&n);
4192   macro mac;
4193   request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm);
4194   macro *mm = rm ? rm->to_macro() : 0;
4195   if (mode == DEFINE_APPEND && mm)
4196     mac = *mm;
4197   if (comp == COMP_DISABLE)
4198     mac.append(PUSH_GROFF_MODE);
4199   else if (comp == COMP_ENABLE)
4200     mac.append(PUSH_COMP_MODE);
4201   while (c != '\n' && c != EOF) {
4202     if (c == 0)
4203       mac.append(n);
4204     else
4205       mac.append((unsigned char)c);
4206     c = get_copy(&n);
4207   }
4208   if (comp == COMP_DISABLE || comp == COMP_ENABLE)
4209     mac.append(POP_GROFFCOMP_MODE);
4210   if (!mm) {
4211     mm = new macro;
4212     request_dictionary.define(nm, mm);
4213   }
4214   *mm = mac;
4215   tok.next();
4216 }
4217
4218 void define_string()
4219 {
4220   do_define_string(DEFINE_NORMAL,
4221                    compatible_flag ? COMP_ENABLE: COMP_IGNORE);
4222 }
4223
4224 void define_nocomp_string()
4225 {
4226   do_define_string(DEFINE_NORMAL, COMP_DISABLE);
4227 }
4228
4229 void append_string()
4230 {
4231   do_define_string(DEFINE_APPEND,
4232                    compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4233 }
4234
4235 void append_nocomp_string()
4236 {
4237   do_define_string(DEFINE_APPEND, COMP_DISABLE);
4238 }
4239
4240 void do_define_character(char_mode mode, const char *font_name)
4241 {
4242   node *n = 0;          // pacify compiler
4243   int c;
4244   tok.skip();
4245   charinfo *ci = tok.get_char(1);
4246   if (ci == 0) {
4247     skip_line();
4248     return;
4249   }
4250   if (font_name) {
4251     string s(font_name);
4252     s += ' ';
4253     s += ci->nm.contents();
4254     s += '\0';
4255     ci = get_charinfo(symbol(s.contents()));
4256   }
4257   tok.next();
4258   if (tok.newline())
4259     c = '\n';
4260   else if (tok.tab())
4261     c = '\t';
4262   else if (!tok.space()) {
4263     error("bad character definition");
4264     skip_line();
4265     return;
4266   }
4267   else
4268     c = get_copy(&n);
4269   while (c == ' ' || c == '\t')
4270     c = get_copy(&n);
4271   if (c == '"')
4272     c = get_copy(&n);
4273   macro *m = new macro;
4274   while (c != '\n' && c != EOF) {
4275     if (c == 0)
4276       m->append(n);
4277     else
4278       m->append((unsigned char)c);
4279     c = get_copy(&n);
4280   }
4281   m = ci->setx_macro(m, mode);
4282   if (m)
4283     delete m;
4284   tok.next();
4285 }
4286
4287 void define_character()
4288 {
4289   do_define_character(CHAR_NORMAL);
4290 }
4291
4292 void define_fallback_character()
4293 {
4294   do_define_character(CHAR_FALLBACK);
4295 }
4296
4297 void define_special_character()
4298 {
4299   do_define_character(CHAR_SPECIAL);
4300 }
4301
4302 static void remove_character()
4303 {
4304   tok.skip();
4305   while (!tok.newline() && !tok.eof()) {
4306     if (!tok.space() && !tok.tab()) {
4307       charinfo *ci = tok.get_char(1);
4308       if (!ci)
4309         break;
4310       macro *m = ci->set_macro(0);
4311       if (m)
4312         delete m;
4313     }
4314     tok.next();
4315   }
4316   skip_line();
4317 }
4318
4319 static void interpolate_string(symbol nm)
4320 {
4321   request_or_macro *p = lookup_request(nm);
4322   macro *m = p->to_macro();
4323   if (!m)
4324     error("you can only invoke a string or macro using \\*");
4325   else {
4326     if (m->is_string()) {
4327       string_iterator *si = new string_iterator(*m, "string", nm);
4328       input_stack::push(si);
4329      }
4330     else {
4331       // if a macro is called as a string, \$0 doesn't get changed
4332       macro_iterator *mi = new macro_iterator(input_stack::get_macro_name(),
4333                                               *m, "string", 1);
4334       input_stack::push(mi);
4335     }
4336   }
4337 }
4338
4339 static void interpolate_string_with_args(symbol s)
4340 {
4341   request_or_macro *p = lookup_request(s);
4342   macro *m = p->to_macro();
4343   if (!m)
4344     error("you can only invoke a string or macro using \\*");
4345   else {
4346     macro_iterator *mi = new macro_iterator(s, *m);
4347     decode_string_args(mi);
4348     input_stack::push(mi);
4349   }
4350 }
4351
4352 static void interpolate_arg(symbol nm)
4353 {
4354   const char *s = nm.contents();
4355   if (!s || *s == '\0')
4356     copy_mode_error("missing argument name");
4357   else if (s[1] == 0 && csdigit(s[0]))
4358     input_stack::push(input_stack::get_arg(s[0] - '0'));
4359   else if (s[0] == '*' && s[1] == '\0') {
4360     int limit = input_stack::nargs();
4361     string args;
4362     for (int i = 1; i <= limit; i++) {
4363       input_iterator *p = input_stack::get_arg(i);
4364       int c;
4365       while ((c = p->get(0)) != EOF)
4366         if (c != DOUBLE_QUOTE)
4367           args += c;
4368       if (i != limit)
4369         args += ' ';
4370     }
4371     if (limit > 0) {
4372       args += '\0';
4373       input_stack::push(make_temp_iterator(args.contents()));
4374     }
4375   }
4376   else if (s[0] == '@' && s[1] == '\0') {
4377     int limit = input_stack::nargs();
4378     string args;
4379     for (int i = 1; i <= limit; i++) {
4380       args += '"';
4381       args += char(BEGIN_QUOTE);
4382       input_iterator *p = input_stack::get_arg(i);
4383       int c;
4384       while ((c = p->get(0)) != EOF)
4385         if (c != DOUBLE_QUOTE)
4386           args += c;
4387       args += char(END_QUOTE);
4388       args += '"';
4389       if (i != limit)
4390         args += ' ';
4391     }
4392     if (limit > 0) {
4393       args += '\0';
4394       input_stack::push(make_temp_iterator(args.contents()));
4395     }
4396   }
4397   else if (s[0] == '^' && s[1] == '\0') {
4398     int limit = input_stack::nargs();
4399     string args;
4400     int c = input_stack::peek();
4401     for (int i = 1; i <= limit; i++) {
4402       input_iterator *p = input_stack::get_arg(i);
4403       while ((c = p->get(0)) != EOF) {
4404         if (c == DOUBLE_QUOTE)
4405           c = '"';
4406         args += c;
4407       }
4408       if (input_stack::space_follows_arg(i))
4409         args += ' ';
4410     }
4411     if (limit > 0) {
4412       args += '\0';
4413       input_stack::push(make_temp_iterator(args.contents()));
4414     }
4415   }
4416   else {
4417     const char *p;
4418     for (p = s; *p && csdigit(*p); p++)
4419       ;
4420     if (*p)
4421       copy_mode_error("bad argument name `%1'", s);
4422     else
4423       input_stack::push(input_stack::get_arg(atoi(s)));
4424   }
4425 }
4426
4427 void handle_first_page_transition()
4428 {
4429   push_token(tok);
4430   topdiv->begin_page();
4431 }
4432
4433 // We push back a token by wrapping it up in a token_node, and
4434 // wrapping that up in a string_iterator.
4435
4436 static void push_token(const token &t)
4437 {
4438   macro m;
4439   m.append(new token_node(t));
4440   input_stack::push(new string_iterator(m));
4441 }
4442
4443 void push_page_ejector()
4444 {
4445   static char buf[2] = { PAGE_EJECTOR, '\0' };
4446   input_stack::push(make_temp_iterator(buf));
4447 }
4448
4449 void handle_initial_request(unsigned char code)
4450 {
4451   char buf[2];
4452   buf[0] = code;
4453   buf[1] = '\0';
4454   macro mac;
4455   mac.append(new token_node(tok));
4456   input_stack::push(new string_iterator(mac));
4457   input_stack::push(make_temp_iterator(buf));
4458   topdiv->begin_page();
4459   tok.next();
4460 }
4461
4462 void handle_initial_title()
4463 {
4464   handle_initial_request(TITLE_REQUEST);
4465 }
4466
4467 // this should be local to define_macro, but cfront 1.2 doesn't support that
4468 static symbol dot_symbol(".");
4469
4470 void do_define_macro(define_mode mode, calling_mode calling, comp_mode comp)
4471 {
4472   symbol nm, term;
4473   if (calling == CALLING_INDIRECT) {
4474     symbol temp1 = get_name(1);
4475     if (temp1.is_null()) {
4476       skip_line();
4477       return;
4478     }
4479     symbol temp2 = get_name();
4480     input_stack::push(make_temp_iterator("\n"));
4481     if (!temp2.is_null()) {
4482       interpolate_string(temp2);
4483       input_stack::push(make_temp_iterator(" "));
4484     }
4485     interpolate_string(temp1);
4486     input_stack::push(make_temp_iterator(" "));
4487     tok.next();
4488   }
4489   if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4490     nm = get_name(1);
4491     if (nm.is_null()) {
4492       skip_line();
4493       return;
4494     }
4495   }
4496   term = get_name();    // the request that terminates the definition
4497   if (term.is_null())
4498     term = dot_symbol;
4499   while (!tok.newline() && !tok.eof())
4500     tok.next();
4501   const char *start_filename;
4502   int start_lineno;
4503   int have_start_location = input_stack::get_location(0, &start_filename,
4504                                                       &start_lineno);
4505   node *n;
4506   // doing this here makes the line numbers come out right
4507   int c = get_copy(&n, 1);
4508   macro mac;
4509   macro *mm = 0;
4510   if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4511     request_or_macro *rm =
4512       (request_or_macro *)request_dictionary.lookup(nm);
4513     if (rm)
4514       mm = rm->to_macro();
4515     if (mm && mode == DEFINE_APPEND)
4516       mac = *mm;
4517   }
4518   int bol = 1;
4519   if (comp == COMP_DISABLE)
4520     mac.append(PUSH_GROFF_MODE);
4521   else if (comp == COMP_ENABLE)
4522     mac.append(PUSH_COMP_MODE);
4523   for (;;) {
4524     if (c == '\n')
4525       mac.clear_string_flag();
4526     while (c == ESCAPE_NEWLINE) {
4527       if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND)
4528         mac.append(c);
4529       c = get_copy(&n, 1);
4530     }
4531     if (bol && c == '.') {
4532       const char *s = term.contents();
4533       int d = 0;
4534       // see if it matches term
4535       int i = 0;
4536       if (s[0] != 0) {
4537         while ((d = get_copy(&n)) == ' ' || d == '\t')
4538           ;
4539         if ((unsigned char)s[0] == d) {
4540           for (i = 1; s[i] != 0; i++) {
4541             d = get_copy(&n);
4542             if ((unsigned char)s[i] != d)
4543               break;
4544           }
4545         }
4546       }
4547       if (s[i] == 0
4548           && ((i == 2 && compatible_flag)
4549               || (d = get_copy(&n)) == ' '
4550               || d == '\n')) {  // we found it
4551         if (d == '\n')
4552           tok.make_newline();
4553         else
4554           tok.make_space();
4555         if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) {
4556           if (!mm) {
4557             mm = new macro;
4558             request_dictionary.define(nm, mm);
4559           }
4560           if (comp == COMP_DISABLE || comp == COMP_ENABLE)
4561             mac.append(POP_GROFFCOMP_MODE);
4562           *mm = mac;
4563         }
4564         if (term != dot_symbol) {
4565           ignoring = 0;
4566           interpolate_macro(term);
4567         }
4568         else
4569           skip_line();
4570         return;
4571       }
4572       if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) {
4573         mac.append(c);
4574         for (int j = 0; j < i; j++)
4575           mac.append(s[j]);
4576       }
4577       c = d;
4578     }
4579     if (c == EOF) {
4580       if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4581         if (have_start_location)
4582           error_with_file_and_line(start_filename, start_lineno,
4583                                    "end of file while defining macro `%1'",
4584                                    nm.contents());
4585         else
4586           error("end of file while defining macro `%1'", nm.contents());
4587       }
4588       else {
4589         if (have_start_location)
4590           error_with_file_and_line(start_filename, start_lineno,
4591                                    "end of file while ignoring input lines");
4592         else
4593           error("end of file while ignoring input lines");
4594       }
4595       tok.next();
4596       return;
4597     }
4598     if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
4599       if (c == 0)
4600         mac.append(n);
4601       else
4602         mac.append(c);
4603     }
4604     bol = (c == '\n');
4605     c = get_copy(&n, 1);
4606   }
4607 }
4608
4609 void define_macro()
4610 {
4611   do_define_macro(DEFINE_NORMAL, CALLING_NORMAL,
4612                   compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4613 }
4614
4615 void define_nocomp_macro()
4616 {
4617   do_define_macro(DEFINE_NORMAL, CALLING_NORMAL, COMP_DISABLE);
4618 }
4619
4620 void define_indirect_macro()
4621 {
4622   do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT,
4623                   compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4624 }
4625
4626 void define_indirect_nocomp_macro()
4627 {
4628   do_define_macro(DEFINE_NORMAL, CALLING_INDIRECT, COMP_DISABLE);
4629 }
4630
4631 void append_macro()
4632 {
4633   do_define_macro(DEFINE_APPEND, CALLING_NORMAL,
4634                   compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4635 }
4636
4637 void append_nocomp_macro()
4638 {
4639   do_define_macro(DEFINE_APPEND, CALLING_NORMAL, COMP_DISABLE);
4640 }
4641
4642 void append_indirect_macro()
4643 {
4644   do_define_macro(DEFINE_APPEND, CALLING_INDIRECT,
4645                   compatible_flag ? COMP_ENABLE : COMP_IGNORE);
4646 }
4647
4648 void append_indirect_nocomp_macro()
4649 {
4650   do_define_macro(DEFINE_APPEND, CALLING_INDIRECT, COMP_DISABLE);
4651 }
4652
4653 void ignore()
4654 {
4655   ignoring = 1;
4656   do_define_macro(DEFINE_IGNORE, CALLING_NORMAL, COMP_IGNORE);
4657   ignoring = 0;
4658 }
4659
4660 void remove_macro()
4661 {
4662   for (;;) {
4663     symbol s = get_name();
4664     if (s.is_null())
4665       break;
4666     request_dictionary.remove(s);
4667   }
4668   skip_line();
4669 }
4670
4671 void rename_macro()
4672 {
4673   symbol s1 = get_name(1);
4674   if (!s1.is_null()) {
4675     symbol s2 = get_name(1);
4676     if (!s2.is_null())
4677       request_dictionary.rename(s1, s2);
4678   }
4679   skip_line();
4680 }
4681
4682 void alias_macro()
4683 {
4684   symbol s1 = get_name(1);
4685   if (!s1.is_null()) {
4686     symbol s2 = get_name(1);
4687     if (!s2.is_null()) {
4688       if (!request_dictionary.alias(s1, s2))
4689         warning(WARN_MAC, "macro `%1' not defined", s2.contents());
4690     }
4691   }
4692   skip_line();
4693 }
4694
4695 void chop_macro()
4696 {
4697   symbol s = get_name(1);
4698   if (!s.is_null()) {
4699     request_or_macro *p = lookup_request(s);
4700     macro *m = p->to_macro();
4701     if (!m)
4702       error("cannot chop request");
4703     else if (m->empty())
4704       error("cannot chop empty macro");
4705     else {
4706       int have_restore = 0;
4707       // we have to check for additional save/restore pairs which could be
4708       // there due to empty am1 requests.
4709       for (;;) {
4710         if (m->get(m->len - 1) != POP_GROFFCOMP_MODE)
4711           break;
4712         have_restore = 1;
4713         m->len -= 1;
4714         if (m->get(m->len - 1) != PUSH_GROFF_MODE
4715             && m->get(m->len - 1) != PUSH_COMP_MODE)
4716           break;
4717         have_restore = 0;
4718         m->len -= 1;
4719         if (m->len == 0)
4720           break;
4721       }
4722       if (m->len == 0)
4723         error("cannot chop empty macro");
4724       else {
4725         if (have_restore)
4726           m->set(POP_GROFFCOMP_MODE, m->len - 1);
4727         else
4728           m->len -= 1;
4729       }
4730     }
4731   }
4732   skip_line();
4733 }
4734
4735 void substring_request()
4736 {
4737   int start;                            // 0, 1, ..., n-1  or  -1, -2, ...
4738   symbol s = get_name(1);
4739   if (!s.is_null() && get_integer(&start)) {
4740     request_or_macro *p = lookup_request(s);
4741     macro *m = p->to_macro();
4742     if (!m)
4743       error("cannot apply `substring' on a request");
4744     else {
4745       int end = -1;
4746       if (!has_arg() || get_integer(&end)) {
4747         int real_length = 0;                    // 1, 2, ..., n
4748         string_iterator iter1(*m);
4749         for (int l = 0; l < m->len; l++) {
4750           int c = iter1.get(0);
4751           if (c == PUSH_GROFF_MODE
4752               || c == PUSH_COMP_MODE
4753               || c == POP_GROFFCOMP_MODE)
4754             continue;
4755           if (c == EOF)
4756             break;
4757           real_length++;
4758         }
4759         if (start < 0)
4760           start += real_length;
4761         if (end < 0)
4762           end += real_length;
4763         if (start > end) {
4764           int tem = start;
4765           start = end;
4766           end = tem;
4767         }
4768         if (start >= real_length || end < 0) {
4769           warning(WARN_RANGE,
4770                   "start and end index of substring out of range");
4771           m->len = 0;
4772           if (m->p) {
4773             if (--(m->p->count) <= 0)
4774               delete m->p;
4775             m->p = 0;
4776           }
4777           skip_line();
4778           return;
4779         }
4780         if (start < 0) {
4781           warning(WARN_RANGE,
4782                   "start index of substring out of range, set to 0");
4783           start = 0;
4784         }
4785         if (end >= real_length) {
4786           warning(WARN_RANGE,
4787                   "end index of substring out of range, set to string length");
4788           end = real_length - 1;
4789         }
4790         // now extract the substring
4791         string_iterator iter(*m);
4792         int i;
4793         for (i = 0; i < start; i++) {
4794           int c = iter.get(0);
4795           while (c == PUSH_GROFF_MODE
4796                  || c == PUSH_COMP_MODE
4797                  || c == POP_GROFFCOMP_MODE)
4798             c = iter.get(0);
4799           if (c == EOF)
4800             break;
4801         }
4802         macro mac;
4803         for (; i <= end; i++) {
4804           node *nd = 0;         // pacify compiler
4805           int c = iter.get(&nd);
4806           while (c == PUSH_GROFF_MODE
4807                  || c == PUSH_COMP_MODE
4808                  || c == POP_GROFFCOMP_MODE)
4809             c = iter.get(0);
4810           if (c == EOF)
4811             break;
4812           if (c == 0)
4813             mac.append(nd);
4814           else
4815             mac.append((unsigned char)c);
4816         }
4817         *m = mac;
4818       }
4819     }
4820   }
4821   skip_line();
4822 }
4823
4824 void length_request()
4825 {
4826   symbol ret;
4827   ret = get_name(1);
4828   if (ret.is_null()) {
4829     skip_line();
4830     return;
4831   }
4832   int c;
4833   node *n;
4834   if (tok.newline())
4835     c = '\n';
4836   else if (tok.tab())
4837     c = '\t';
4838   else if (!tok.space()) {
4839     error("bad string definition");
4840     skip_line();
4841     return;
4842   }
4843   else
4844     c = get_copy(&n);
4845   while (c == ' ')
4846     c = get_copy(&n);
4847   if (c == '"')
4848     c = get_copy(&n);
4849   int len = 0;
4850   while (c != '\n' && c != EOF) {
4851     ++len;
4852     c = get_copy(&n);
4853   }
4854   reg *r = (reg*)number_reg_dictionary.lookup(ret);
4855   if (r)
4856     r->set_value(len);
4857   else
4858     set_number_reg(ret, len);
4859   tok.next();
4860 }
4861
4862 void asciify_macro()
4863 {
4864   symbol s = get_name(1);
4865   if (!s.is_null()) {
4866     request_or_macro *p = lookup_request(s);
4867     macro *m = p->to_macro();
4868     if (!m)
4869       error("cannot asciify request");
4870     else {
4871       macro am;
4872       string_iterator iter(*m);
4873       for (;;) {
4874         node *nd = 0;           // pacify compiler
4875         int c = iter.get(&nd);
4876         if (c == EOF)
4877           break;
4878         if (c != 0)
4879           am.append(c);
4880         else
4881           nd->asciify(&am);
4882       }
4883       *m = am;
4884     }
4885   }
4886   skip_line();
4887 }
4888
4889 void unformat_macro()
4890 {
4891   symbol s = get_name(1);
4892   if (!s.is_null()) {
4893     request_or_macro *p = lookup_request(s);
4894     macro *m = p->to_macro();
4895     if (!m)
4896       error("cannot unformat request");
4897     else {
4898       macro am;
4899       string_iterator iter(*m);
4900       for (;;) {
4901         node *nd = 0;           // pacify compiler
4902         int c = iter.get(&nd);
4903         if (c == EOF)
4904           break;
4905         if (c != 0)
4906           am.append(c);
4907         else {
4908           if (nd->set_unformat_flag())
4909             am.append(nd);
4910         }
4911       }
4912       *m = am;
4913     }
4914   }
4915   skip_line();
4916 }
4917
4918 static void interpolate_environment_variable(symbol nm)
4919 {
4920   const char *s = getenv(nm.contents());
4921   if (s && *s)
4922     input_stack::push(make_temp_iterator(s));
4923 }
4924
4925 void interpolate_number_reg(symbol nm, int inc)
4926 {
4927   reg *r = lookup_number_reg(nm);
4928   if (inc < 0)
4929     r->decrement();
4930   else if (inc > 0)
4931     r->increment();
4932   input_stack::push(make_temp_iterator(r->get_string()));
4933 }
4934
4935 static void interpolate_number_format(symbol nm)
4936 {
4937   reg *r = (reg *)number_reg_dictionary.lookup(nm);
4938   if (r)
4939     input_stack::push(make_temp_iterator(r->get_format()));
4940 }
4941
4942 static int get_delim_number(units *n, unsigned char si, int prev_value)
4943 {
4944   token start;
4945   start.next();
4946   if (start.delimiter(1)) {
4947     tok.next();
4948     if (get_number(n, si, prev_value)) {
4949       if (start != tok)
4950         warning(WARN_DELIM, "closing delimiter does not match");
4951       return 1;
4952     }
4953   }
4954   return 0;
4955 }
4956
4957 static int get_delim_number(units *n, unsigned char si)
4958 {
4959   token start;
4960   start.next();
4961   if (start.delimiter(1)) {
4962     tok.next();
4963     if (get_number(n, si)) {
4964       if (start != tok)
4965         warning(WARN_DELIM, "closing delimiter does not match");
4966       return 1;
4967     }
4968   }
4969   return 0;
4970 }
4971
4972 static int get_line_arg(units *n, unsigned char si, charinfo **cp)
4973 {
4974   token start;
4975   start.next();
4976   int start_level = input_stack::get_level();
4977   if (!start.delimiter(1))
4978     return 0;
4979   tok.next();
4980   if (get_number(n, si)) {
4981     if (tok.dummy() || tok.transparent_dummy())
4982       tok.next();
4983     if (!(start == tok && input_stack::get_level() == start_level)) {
4984       *cp = tok.get_char(1);
4985       tok.next();
4986     }
4987     if (!(start == tok && input_stack::get_level() == start_level))
4988       warning(WARN_DELIM, "closing delimiter does not match");
4989     return 1;
4990   }
4991   return 0;
4992 }
4993
4994 static int read_size(int *x)
4995 {
4996   tok.next();
4997   int c = tok.ch();
4998   int inc = 0;
4999   if (c == '-') {
5000     inc = -1;
5001     tok.next();
5002     c = tok.ch();
5003   }
5004   else if (c == '+') {
5005     inc = 1;
5006     tok.next();
5007     c = tok.ch();
5008   }
5009   int val = 0;          // pacify compiler
5010   int bad = 0;
5011   if (c == '(') {
5012     tok.next();
5013     c = tok.ch();
5014     if (!inc) {
5015       // allow an increment either before or after the left parenthesis
5016       if (c == '-') {
5017         inc = -1;
5018         tok.next();
5019         c = tok.ch();
5020       }
5021       else if (c == '+') {
5022         inc = 1;
5023         tok.next();
5024         c = tok.ch();
5025       }
5026     }
5027     if (!csdigit(c))
5028       bad = 1;
5029     else {
5030       val = c - '0';
5031       tok.next();
5032       c = tok.ch();
5033       if (!csdigit(c))
5034         bad = 1;
5035       else {
5036         val = val*10 + (c - '0');
5037         val *= sizescale;
5038       }
5039     }
5040   }
5041   else if (csdigit(c)) {
5042     val = c - '0';
5043     if (!inc && c != '0' && c < '4') {
5044       tok.next();
5045       c = tok.ch();
5046       if (!csdigit(c))
5047         bad = 1;
5048       else
5049         val = val*10 + (c - '0');
5050     }
5051     val *= sizescale;
5052   }
5053   else if (!tok.delimiter(1))
5054     return 0;
5055   else {
5056     token start(tok);
5057     tok.next();
5058     c = tok.ch();
5059     if (!inc && (c == '-' || c == '+')) {
5060       inc = c == '+' ? 1 : -1;
5061       tok.next();
5062     }
5063     if (!get_number(&val, 'z'))
5064       return 0;
5065     if (!(start.ch() == '[' && tok.ch() == ']') && start != tok) {
5066       if (start.ch() == '[')
5067         error("missing `]'");
5068       else
5069         error("missing closing delimiter");
5070       return 0;
5071     }
5072   }
5073   if (!bad) {
5074     switch (inc) {
5075     case 0:
5076       if (val == 0) {
5077         // special case -- \s[0] and \s0 means to revert to previous size
5078         *x = 0;
5079         return 1;
5080       }
5081       *x = val;
5082       break;
5083     case 1:
5084       *x = curenv->get_requested_point_size() + val;
5085       break;
5086     case -1:
5087       *x = curenv->get_requested_point_size() - val;
5088       break;
5089     default:
5090       assert(0);
5091     }
5092     if (*x <= 0) {
5093       warning(WARN_RANGE,
5094               "\\s escape results in non-positive point size; set to 1");
5095       *x = 1;
5096     }
5097     return 1;
5098   }
5099   else {
5100     error("bad digit in point size");
5101     return 0;
5102   }
5103 }
5104
5105 static symbol get_delim_name()
5106 {
5107   token start;
5108   start.next();
5109   if (start.eof()) {
5110     error("end of input at start of delimited name");
5111     return NULL_SYMBOL;
5112   }
5113   if (start.newline()) {
5114     error("can't delimit name with a newline");
5115     return NULL_SYMBOL;
5116   }
5117   int start_level = input_stack::get_level();
5118   char abuf[ABUF_SIZE];
5119   char *buf = abuf;
5120   int buf_size = ABUF_SIZE;
5121   int i = 0;
5122   for (;;) {
5123     if (i + 1 > buf_size) {
5124       if (buf == abuf) {
5125         buf = new char[ABUF_SIZE*2];
5126         memcpy(buf, abuf, buf_size);
5127         buf_size = ABUF_SIZE*2;
5128       }
5129       else {
5130         char *old_buf = buf;
5131         buf = new char[buf_size*2];
5132         memcpy(buf, old_buf, buf_size);
5133         buf_size *= 2;
5134         a_delete old_buf;
5135       }
5136     }
5137     tok.next();
5138     if (tok == start
5139         && (compatible_flag || input_stack::get_level() == start_level))
5140       break;
5141     if ((buf[i] = tok.ch()) == 0) {
5142       error("missing delimiter (got %1)", tok.description());
5143       if (buf != abuf)
5144         a_delete buf;
5145       return NULL_SYMBOL;
5146     }
5147     i++;
5148   }
5149   buf[i] = '\0';
5150   if (buf == abuf) {
5151     if (i == 0) {
5152       error("empty delimited name");
5153       return NULL_SYMBOL;
5154     }
5155     else
5156       return symbol(buf);
5157   }
5158   else {
5159     symbol s(buf);
5160     a_delete buf;
5161     return s;
5162   }
5163 }
5164
5165 // Implement \R
5166
5167 static void do_register()
5168 {
5169   token start;
5170   start.next();
5171   if (!start.delimiter(1))
5172     return;
5173   tok.next();
5174   symbol nm = get_long_name(1);
5175   if (nm.is_null())
5176     return;
5177   while (tok.space())
5178     tok.next();
5179   reg *r = (reg *)number_reg_dictionary.lookup(nm);
5180   int prev_value;
5181   if (!r || !r->get_value(&prev_value))
5182     prev_value = 0;
5183   int val;
5184   if (!get_number(&val, 'u', prev_value))
5185     return;
5186   if (start != tok)
5187     warning(WARN_DELIM, "closing delimiter does not match");
5188   if (r)
5189     r->set_value(val);
5190   else
5191     set_number_reg(nm, val);
5192 }
5193
5194 // this implements the \w escape sequence
5195
5196 static void do_width()
5197 {
5198   token start;
5199   start.next();
5200   int start_level = input_stack::get_level();
5201   environment env(curenv);
5202   environment *oldenv = curenv;
5203   curenv = &env;
5204   for (;;) {
5205     tok.next();
5206     if (tok.eof()) {
5207       warning(WARN_DELIM, "missing closing delimiter");
5208       break;
5209     }
5210     if (tok.newline()) {
5211       warning(WARN_DELIM, "missing closing delimiter");
5212       input_stack::push(make_temp_iterator("\n"));
5213       break;
5214     }
5215     if (tok == start
5216         && (compatible_flag || input_stack::get_level() == start_level))
5217       break;
5218     tok.process();
5219   }
5220   env.wrap_up_tab();
5221   units x = env.get_input_line_position().to_units();
5222   input_stack::push(make_temp_iterator(i_to_a(x)));
5223   env.width_registers();
5224   curenv = oldenv;
5225   have_input = 0;
5226 }
5227
5228 charinfo *page_character;
5229
5230 void set_page_character()
5231 {
5232   page_character = get_optional_char();
5233   skip_line();
5234 }
5235
5236 static const symbol percent_symbol("%");
5237
5238 void read_title_parts(node **part, hunits *part_width)
5239 {
5240   tok.skip();
5241   if (tok.newline() || tok.eof())
5242     return;
5243   token start(tok);
5244   int start_level = input_stack::get_level();
5245   tok.next();
5246   for (int i = 0; i < 3; i++) {
5247     while (!tok.newline() && !tok.eof()) {
5248       if (tok == start
5249           && (compatible_flag || input_stack::get_level() == start_level)) {
5250         tok.next();
5251         break;
5252       }
5253       if (page_character != 0 && tok.get_char() == page_character)
5254         interpolate_number_reg(percent_symbol, 0);
5255       else
5256         tok.process();
5257       tok.next();
5258     }
5259     curenv->wrap_up_tab();
5260     part_width[i] = curenv->get_input_line_position();
5261     part[i] = curenv->extract_output_line();
5262   }
5263   while (!tok.newline() && !tok.eof())
5264     tok.next();
5265 }
5266
5267 class non_interpreted_node : public node {
5268   macro mac;
5269 public:
5270   non_interpreted_node(const macro &);
5271   int interpret(macro *);
5272   node *copy();
5273   int ends_sentence();
5274   int same(node *);
5275   const char *type();
5276   int force_tprint();
5277   int is_tag();
5278 };
5279
5280 non_interpreted_node::non_interpreted_node(const macro &m) : mac(m)
5281 {
5282 }
5283
5284 int non_interpreted_node::ends_sentence()
5285 {
5286   return 2;
5287 }
5288
5289 int non_interpreted_node::same(node *nd)
5290 {
5291   return mac == ((non_interpreted_node *)nd)->mac;
5292 }
5293
5294 const char *non_interpreted_node::type()
5295 {
5296   return "non_interpreted_node";
5297 }
5298
5299 int non_interpreted_node::force_tprint()
5300 {
5301   return 0;
5302 }
5303
5304 int non_interpreted_node::is_tag()
5305 {
5306   return 0;
5307 }
5308
5309 node *non_interpreted_node::copy()
5310 {
5311   return new non_interpreted_node(mac);
5312 }
5313
5314 int non_interpreted_node::interpret(macro *m)
5315 {
5316   string_iterator si(mac);
5317   node *n = 0;          // pacify compiler
5318   for (;;) {
5319     int c = si.get(&n);
5320     if (c == EOF)
5321       break;
5322     if (c == 0)
5323       m->append(n);
5324     else
5325       m->append(c);
5326   }
5327   return 1;
5328 }
5329
5330 static node *do_non_interpreted()
5331 {
5332   node *n;
5333   int c;
5334   macro mac;
5335   while ((c = get_copy(&n)) != ESCAPE_QUESTION && c != EOF && c != '\n')
5336     if (c == 0)
5337       mac.append(n);
5338     else
5339       mac.append(c);
5340   if (c == EOF || c == '\n') {
5341     error("missing \\?");
5342     return 0;
5343   }
5344   return new non_interpreted_node(mac);
5345 }
5346
5347 static void encode_char(macro *mac, char c)
5348 {
5349   if (c == '\0') {
5350     if ((font::use_charnames_in_special) && tok.special()) {
5351       charinfo *ci = tok.get_char(1);
5352       const char *s = ci->get_symbol()->contents();
5353       if (s[0] != (char)0) {
5354         mac->append('\\');
5355         mac->append('[');
5356         int i = 0;
5357         while (s[i] != (char)0) {
5358           mac->append(s[i]);
5359           i++;
5360         }
5361         mac->append(']');
5362       }
5363     }
5364     else if (tok.stretchable_space()
5365              || tok.unstretchable_space())
5366       mac->append(' ');
5367     else if (!(tok.hyphen_indicator()
5368                || tok.dummy()
5369                || tok.transparent_dummy()
5370                || tok.zero_width_break()))
5371       error("%1 is invalid within \\X", tok.description());
5372   }
5373   else {
5374     if ((font::use_charnames_in_special) && (c == '\\')) {
5375       /*
5376        * add escape escape sequence
5377        */
5378       mac->append(c);
5379     }
5380     mac->append(c);
5381   }
5382 }
5383
5384 node *do_special()
5385 {
5386   token start;
5387   start.next();
5388   int start_level = input_stack::get_level();
5389   macro mac;
5390   for (tok.next();
5391        tok != start || input_stack::get_level() != start_level;
5392        tok.next()) {
5393     if (tok.eof()) {
5394       warning(WARN_DELIM, "missing closing delimiter");
5395       return 0;
5396     }
5397     if (tok.newline()) {
5398       input_stack::push(make_temp_iterator("\n"));
5399       warning(WARN_DELIM, "missing closing delimiter");
5400       break;
5401     }
5402     unsigned char c;
5403     if (tok.space())
5404       c = ' ';
5405     else if (tok.tab())
5406       c = '\t';
5407     else if (tok.leader())
5408       c = '\001';
5409     else if (tok.backspace())
5410       c = '\b';
5411     else
5412       c = tok.ch();
5413     encode_char(&mac, c);
5414   }
5415   return new special_node(mac);
5416 }
5417
5418 void device_request()
5419 {
5420   if (!tok.newline() && !tok.eof()) {
5421     int c;
5422     macro mac;
5423     for (;;) {
5424       c = get_copy(0);
5425       if (c == '"') {
5426         c = get_copy(0);
5427         break;
5428       }
5429       if (c != ' ' && c != '\t')
5430         break;
5431     }
5432     for (; c != '\n' && c != EOF; c = get_copy(0))
5433       mac.append(c);
5434     curenv->add_node(new special_node(mac));
5435   }
5436   tok.next();
5437 }
5438
5439 void device_macro_request()
5440 {
5441   symbol s = get_name(1);
5442   if (!(s.is_null() || s.is_empty())) {
5443     request_or_macro *p = lookup_request(s);
5444     macro *m = p->to_macro();
5445     if (m)
5446       curenv->add_node(new special_node(*m));
5447     else 
5448       error("can't transparently throughput a request");
5449   }
5450   skip_line();
5451 }
5452
5453 void output_request()
5454 {
5455   if (!tok.newline() && !tok.eof()) {
5456     int c;
5457     for (;;) {
5458       c = get_copy(0);
5459       if (c == '"') {
5460         c = get_copy(0);
5461         break;
5462       }
5463       if (c != ' ' && c != '\t')
5464         break;
5465     }
5466     for (; c != '\n' && c != EOF; c = get_copy(0))
5467       topdiv->transparent_output(c);
5468     topdiv->transparent_output('\n');
5469   }
5470   tok.next();
5471 }
5472
5473 extern int image_no;            // from node.cpp
5474
5475 static node *do_suppress(symbol nm)
5476 {
5477   if (nm.is_null() || nm.is_empty()) {
5478     error("expecting an argument to escape \\O");
5479     return 0;
5480   }
5481   const char *s = nm.contents();
5482   switch (*s) {
5483   case '0':
5484     if (begin_level == 0)
5485       // suppress generation of glyphs
5486       return new suppress_node(0, 0);
5487     break;
5488   case '1':
5489     if (begin_level == 0)
5490       // enable generation of glyphs
5491       return new suppress_node(1, 0);
5492     break;
5493   case '2':
5494     if (begin_level == 0)
5495       return new suppress_node(1, 1);
5496     break;
5497   case '3':
5498     have_input = 1;
5499     begin_level++;
5500     break;
5501   case '4':
5502     have_input = 1;
5503     begin_level--;
5504     break;
5505   case '5':
5506     {
5507       s++;                      // move over '5'
5508       char position = *s;
5509       if (*s == (char)0) {
5510         error("missing position and filename in \\O");
5511         return 0;
5512       }
5513       if (!(position == 'l'
5514             || position == 'r'
5515             || position == 'c'
5516             || position == 'i')) {
5517         error("l, r, c, or i position expected (got %1 in \\O)", position);
5518         return 0;
5519       }
5520       s++;                      // onto image name
5521       if (s == (char *)0) {
5522         error("missing image name for \\O");
5523         return 0;
5524       }
5525       image_no++;
5526       if (begin_level == 0)
5527         return new suppress_node(symbol(s), position, image_no);
5528       else
5529         have_input = 1;
5530     }
5531     break;
5532   default:
5533     error("`%1' is an invalid argument to \\O", *s);
5534   }
5535   return 0;
5536 }
5537
5538 void special_node::tprint(troff_output_file *out)
5539 {
5540   tprint_start(out);
5541   string_iterator iter(mac);
5542   for (;;) {
5543     int c = iter.get(0);
5544     if (c == EOF)
5545       break;
5546     for (const char *s = ::asciify(c); *s; s++)
5547       tprint_char(out, *s);
5548   }
5549   tprint_end(out);
5550 }
5551
5552 int get_file_line(const char **filename, int *lineno)
5553 {
5554   return input_stack::get_location(0, filename, lineno);
5555 }
5556
5557 void line_file()
5558 {
5559   int n;
5560   if (get_integer(&n)) {
5561     const char *filename = 0;
5562     if (has_arg()) {
5563       symbol s = get_long_name();
5564       filename = s.contents();
5565     }
5566     (void)input_stack::set_location(filename, n-1);
5567   }
5568   skip_line();
5569 }
5570
5571 static int nroff_mode = 0;
5572
5573 static void nroff_request()
5574 {
5575   nroff_mode = 1;
5576   skip_line();
5577 }
5578
5579 static void troff_request()
5580 {
5581   nroff_mode = 0;
5582   skip_line();
5583 }
5584
5585 static void skip_alternative()
5586 {
5587   int level = 0;
5588   // ensure that ``.if 0\{'' works as expected
5589   if (tok.left_brace())
5590     level++;
5591   int c;
5592   for (;;) {
5593     c = input_stack::get(0);
5594     if (c == EOF)
5595       break;
5596     if (c == ESCAPE_LEFT_BRACE)
5597       ++level;
5598     else if (c == ESCAPE_RIGHT_BRACE)
5599       --level;
5600     else if (c == escape_char && escape_char > 0)
5601       switch(input_stack::get(0)) {
5602       case '{':
5603         ++level;
5604         break;
5605       case '}':
5606         --level;
5607         break;
5608       case '"':
5609         while ((c = input_stack::get(0)) != '\n' && c != EOF)
5610           ;
5611       }
5612     /*
5613       Note that the level can properly be < 0, eg
5614         
5615         .if 1 \{\
5616         .if 0 \{\
5617         .\}\}
5618
5619       So don't give an error message in this case.
5620     */
5621     if (level <= 0 && c == '\n')
5622       break;
5623   }
5624   tok.next();
5625 }
5626
5627 static void begin_alternative()
5628 {
5629   while (tok.space() || tok.left_brace())
5630     tok.next();
5631 }
5632
5633 void nop_request()
5634 {
5635   while (tok.space())
5636     tok.next();
5637 }
5638
5639 static int_stack if_else_stack;
5640
5641 int do_if_request()
5642 {
5643   int invert = 0;
5644   while (tok.space())
5645     tok.next();
5646   while (tok.ch() == '!') {
5647     tok.next();
5648     invert = !invert;
5649   }
5650   int result;
5651   unsigned char c = tok.ch();
5652   if (c == 't') {
5653     tok.next();
5654     result = !nroff_mode;
5655   }
5656   else if (c == 'n') {
5657     tok.next();
5658     result = nroff_mode;
5659   }
5660   else if (c == 'v') {
5661     tok.next();
5662     result = 0;
5663   }
5664   else if (c == 'o') {
5665     result = (topdiv->get_page_number() & 1);
5666     tok.next();
5667   }
5668   else if (c == 'e') {
5669     result = !(topdiv->get_page_number() & 1);
5670     tok.next();
5671   }
5672   else if (c == 'd' || c == 'r') {
5673     tok.next();
5674     symbol nm = get_name(1);
5675     if (nm.is_null()) {
5676       skip_alternative();
5677       return 0;
5678     }
5679     result = (c == 'd'
5680               ? request_dictionary.lookup(nm) != 0
5681               : number_reg_dictionary.lookup(nm) != 0);
5682   }
5683   else if (c == 'm') {
5684     tok.next();
5685     symbol nm = get_long_name(1);
5686     if (nm.is_null()) {
5687       skip_alternative();
5688       return 0;
5689     }
5690     result = (nm == default_symbol
5691               || color_dictionary.lookup(nm) != 0);
5692   }
5693   else if (c == 'c') {
5694     tok.next();
5695     tok.skip();
5696     charinfo *ci = tok.get_char(1);
5697     if (ci == 0) {
5698       skip_alternative();
5699       return 0;
5700     }
5701     result = character_exists(ci, curenv);
5702     tok.next();
5703   }
5704   else if (c == 'F') {
5705     tok.next();
5706     symbol nm = get_long_name(1);
5707     if (nm.is_null()) {
5708       skip_alternative();
5709       return 0;
5710     }
5711     result = check_font(curenv->get_family()->nm, nm);
5712   }
5713   else if (c == 'S') {
5714     tok.next();
5715     symbol nm = get_long_name(1);
5716     if (nm.is_null()) {
5717       skip_alternative();
5718       return 0;
5719     }
5720     result = check_style(nm);
5721   }
5722   else if (tok.space())
5723     result = 0;
5724   else if (tok.delimiter()) {
5725     token delim = tok;
5726     int delim_level = input_stack::get_level();
5727     environment env1(curenv);
5728     environment env2(curenv);
5729     environment *oldenv = curenv;
5730     curenv = &env1;
5731     suppress_push = 1;
5732     for (int i = 0; i < 2; i++) {
5733       for (;;) {
5734         tok.next();
5735         if (tok.newline() || tok.eof()) {
5736           warning(WARN_DELIM, "missing closing delimiter");
5737           tok.next();
5738           curenv = oldenv;
5739           return 0;
5740         }
5741         if (tok == delim
5742             && (compatible_flag || input_stack::get_level() == delim_level))
5743           break;
5744         tok.process();
5745       }
5746       curenv = &env2;
5747     }
5748     node *n1 = env1.extract_output_line();
5749     node *n2 = env2.extract_output_line();
5750     result = same_node_list(n1, n2);
5751     delete_node_list(n1);
5752     delete_node_list(n2);
5753     curenv = oldenv;
5754     have_input = 0;
5755     suppress_push = 0;
5756     tok.next();
5757   }
5758   else {
5759     units n;
5760     if (!get_number(&n, 'u')) {
5761       skip_alternative();
5762       return 0;
5763     }
5764     else
5765       result = n > 0;
5766   }
5767   if (invert)
5768     result = !result;
5769   if (result)
5770     begin_alternative();
5771   else
5772     skip_alternative();
5773   return result;
5774 }
5775
5776 void if_else_request()
5777 {
5778   if_else_stack.push(do_if_request());
5779 }
5780
5781 void if_request()
5782 {
5783   do_if_request();
5784 }
5785
5786 void else_request()
5787 {
5788   if (if_else_stack.is_empty()) {
5789     warning(WARN_EL, "unbalanced .el request");
5790     skip_alternative();
5791   }
5792   else {
5793     if (if_else_stack.pop())
5794       skip_alternative();
5795     else
5796       begin_alternative();
5797   }
5798 }
5799
5800 static int while_depth = 0;
5801 static int while_break_flag = 0;
5802
5803 void while_request()
5804 {
5805   macro mac;
5806   int escaped = 0;
5807   int level = 0;
5808   mac.append(new token_node(tok));
5809   for (;;) {
5810     node *n = 0;                // pacify compiler
5811     int c = input_stack::get(&n);
5812     if (c == EOF)
5813       break;
5814     if (c == 0) {
5815       escaped = 0;
5816       mac.append(n);
5817     }
5818     else if (escaped) {
5819       if (c == '{')
5820         level += 1;
5821       else if (c == '}')
5822         level -= 1;
5823       escaped = 0;
5824       mac.append(c);
5825     }
5826     else {
5827       if (c == ESCAPE_LEFT_BRACE)
5828         level += 1;
5829       else if (c == ESCAPE_RIGHT_BRACE)
5830         level -= 1;
5831       else if (c == escape_char)
5832         escaped = 1;
5833       mac.append(c);
5834       if (c == '\n' && level <= 0)
5835         break;
5836     }
5837   }
5838   if (level != 0)
5839     error("unbalanced \\{ \\}");
5840   else {
5841     while_depth++;
5842     input_stack::add_boundary();
5843     for (;;) {
5844       input_stack::push(new string_iterator(mac, "while loop"));
5845       tok.next();
5846       if (!do_if_request()) {
5847         while (input_stack::get(0) != EOF)
5848           ;
5849         break;
5850       }
5851       process_input_stack();
5852       if (while_break_flag || input_stack::is_return_boundary()) {
5853         while_break_flag = 0;
5854         break;
5855       }
5856     }
5857     input_stack::remove_boundary();
5858     while_depth--;
5859   }
5860   tok.next();
5861 }
5862
5863 void while_break_request()
5864 {
5865   if (!while_depth) {
5866     error("no while loop");
5867     skip_line();
5868   }
5869   else {
5870     while_break_flag = 1;
5871     while (input_stack::get(0) != EOF)
5872       ;
5873     tok.next();
5874   }
5875 }
5876
5877 void while_continue_request()
5878 {
5879   if (!while_depth) {
5880     error("no while loop");
5881     skip_line();
5882   }
5883   else {
5884     while (input_stack::get(0) != EOF)
5885       ;
5886     tok.next();
5887   }
5888 }
5889
5890 // .so
5891
5892 void source()
5893 {
5894   symbol nm = get_long_name(1);
5895   if (nm.is_null())
5896     skip_line();
5897   else {
5898     while (!tok.newline() && !tok.eof())
5899       tok.next();
5900     errno = 0;
5901     FILE *fp = include_search_path.open_file_cautious(nm.contents());
5902     if (fp)
5903       input_stack::push(new file_iterator(fp, nm.contents()));
5904     else
5905       error("can't open `%1': %2", nm.contents(), strerror(errno));
5906     tok.next();
5907   }
5908 }
5909
5910 // like .so but use popen()
5911
5912 void pipe_source()
5913 {
5914   if (!unsafe_flag) {
5915     error(".pso request not allowed in safer mode");
5916     skip_line();
5917   }
5918   else {
5919 #ifdef POPEN_MISSING
5920     error("pipes not available on this system");
5921     skip_line();
5922 #else /* not POPEN_MISSING */
5923     if (tok.newline() || tok.eof())
5924       error("missing command");
5925     else {
5926       int c;
5927       while ((c = get_copy(0)) == ' ' || c == '\t')
5928         ;
5929       int buf_size = 24;
5930       char *buf = new char[buf_size];
5931       int buf_used = 0;
5932       for (; c != '\n' && c != EOF; c = get_copy(0)) {
5933         const char *s = asciify(c);
5934         int slen = strlen(s);
5935         if (buf_used + slen + 1> buf_size) {
5936           char *old_buf = buf;
5937           int old_buf_size = buf_size;
5938           buf_size *= 2;
5939           buf = new char[buf_size];
5940           memcpy(buf, old_buf, old_buf_size);
5941           a_delete old_buf;
5942         }
5943         strcpy(buf + buf_used, s);
5944         buf_used += slen;
5945       }
5946       buf[buf_used] = '\0';
5947       errno = 0;
5948       FILE *fp = popen(buf, POPEN_RT);
5949       if (fp)
5950         input_stack::push(new file_iterator(fp, symbol(buf).contents(), 1));
5951       else
5952         error("can't open pipe to process `%1': %2", buf, strerror(errno));
5953       a_delete buf;
5954     }
5955     tok.next();
5956 #endif /* not POPEN_MISSING */
5957   }
5958 }
5959
5960 // .psbb
5961
5962 static int llx_reg_contents = 0;
5963 static int lly_reg_contents = 0;
5964 static int urx_reg_contents = 0;
5965 static int ury_reg_contents = 0;
5966
5967 struct bounding_box {
5968   int llx, lly, urx, ury;
5969 };
5970
5971 /* Parse the argument to a %%BoundingBox comment.  Return 1 if it
5972 contains 4 numbers, 2 if it contains (atend), 0 otherwise. */
5973
5974 int parse_bounding_box(char *p, bounding_box *bb)
5975 {
5976   if (sscanf(p, "%d %d %d %d",
5977              &bb->llx, &bb->lly, &bb->urx, &bb->ury) == 4)
5978     return 1;
5979   else {
5980     /* The Document Structuring Conventions say that the numbers
5981        should be integers.  Unfortunately some broken applications
5982        get this wrong. */
5983     double x1, x2, x3, x4;
5984     if (sscanf(p, "%lf %lf %lf %lf", &x1, &x2, &x3, &x4) == 4) {
5985       bb->llx = (int)x1;
5986       bb->lly = (int)x2;
5987       bb->urx = (int)x3;
5988       bb->ury = (int)x4;
5989       return 1;
5990     }
5991     else {
5992       for (; *p == ' ' || *p == '\t'; p++)
5993         ;
5994       if (strncmp(p, "(atend)", 7) == 0) {
5995         return 2;
5996       }
5997     }
5998   }
5999   bb->llx = bb->lly = bb->urx = bb->ury = 0;
6000   return 0;
6001 }
6002
6003 // This version is taken from psrm.cpp
6004
6005 #define PS_LINE_MAX 255
6006 cset white_space("\n\r \t");
6007
6008 int ps_get_line(char *buf, FILE *fp, const char* filename)
6009 {
6010   int c = getc(fp);
6011   if (c == EOF) {
6012     buf[0] = '\0';
6013     return 0;
6014   }
6015   int i = 0;
6016   int err = 0;
6017   while (c != '\r' && c != '\n' && c != EOF) {
6018     if ((c < 0x1b && !white_space(c)) || c == 0x7f)
6019       error("invalid input character code %1 in `%2'", int(c), filename);
6020     else if (i < PS_LINE_MAX)
6021       buf[i++] = c;
6022     else if (!err) {
6023       err = 1;
6024       error("PostScript file `%1' is non-conforming "
6025             "because length of line exceeds 255", filename);
6026     }
6027     c = getc(fp);
6028   }
6029   buf[i++] = '\n';
6030   buf[i] = '\0';
6031   if (c == '\r') {
6032     c = getc(fp);
6033     if (c != EOF && c != '\n')
6034       ungetc(c, fp);
6035   }
6036   return 1;
6037 }
6038
6039 inline void assign_registers(int llx, int lly, int urx, int ury)
6040 {
6041   llx_reg_contents = llx;
6042   lly_reg_contents = lly;
6043   urx_reg_contents = urx;
6044   ury_reg_contents = ury;
6045 }
6046
6047 void do_ps_file(FILE *fp, const char* filename)
6048 {
6049   bounding_box bb;
6050   int bb_at_end = 0;
6051   char buf[PS_LINE_MAX];
6052   llx_reg_contents = lly_reg_contents =
6053     urx_reg_contents = ury_reg_contents = 0;
6054   if (!ps_get_line(buf, fp, filename)) {
6055     error("`%1' is empty", filename);
6056     return;
6057   }
6058   if (strncmp("%!PS-Adobe-", buf, 11) != 0) {
6059     error("`%1' is not conforming to the Document Structuring Conventions",
6060           filename);
6061     return;
6062   }
6063   while (ps_get_line(buf, fp, filename) != 0) {
6064     // in header comments, `%X' (`X' any printable character except
6065     // whitespace) is possible too
6066     if (buf[0] == '%') {
6067       if (strncmp(buf + 1, "%EndComments", 12) == 0)
6068         break;
6069       if (white_space(buf[1]))
6070         break;
6071     }
6072     else
6073       break;
6074     if (strncmp(buf + 1, "%BoundingBox:", 13) == 0) {
6075       int res = parse_bounding_box(buf + 14, &bb);
6076       if (res == 1) {
6077         assign_registers(bb.llx, bb.lly, bb.urx, bb.ury);
6078         return;
6079       }
6080       else if (res == 2) {
6081         bb_at_end = 1;
6082         break;
6083       }
6084       else {
6085         error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
6086               filename);
6087         return;
6088       }
6089     }
6090   }
6091   if (bb_at_end) {
6092     long offset;
6093     int last_try = 0;
6094     // in the trailer, the last BoundingBox comment is significant
6095     for (offset = 512; !last_try; offset *= 2) {
6096       int had_trailer = 0;
6097       int got_bb = 0;
6098       if (offset > 32768 || fseek(fp, -offset, 2) == -1) {
6099         last_try = 1;
6100         if (fseek(fp, 0L, 0) == -1)
6101           break;
6102       }
6103       while (ps_get_line(buf, fp, filename) != 0) {
6104         if (buf[0] == '%' && buf[1] == '%') {
6105           if (!had_trailer) {
6106             if (strncmp(buf + 2, "Trailer", 7) == 0)
6107               had_trailer = 1;
6108           }
6109           else {
6110             if (strncmp(buf + 2, "BoundingBox:", 12) == 0) {
6111               int res = parse_bounding_box(buf + 14, &bb);
6112               if (res == 1)
6113                 got_bb = 1;
6114               else if (res == 2) {
6115                 error("`(atend)' not allowed in trailer of `%1'", filename);
6116                 return;
6117               }
6118               else {
6119                 error("the arguments to the %%%%BoundingBox comment in `%1' are bad",
6120                       filename);
6121                 return;
6122               }
6123             }
6124           }
6125         }
6126       }
6127       if (got_bb) {
6128         assign_registers(bb.llx, bb.lly, bb.urx, bb.ury);
6129         return;
6130       }
6131     }
6132   }
6133   error("%%%%BoundingBox comment not found in `%1'", filename);
6134 }
6135
6136 void ps_bbox_request()
6137 {
6138   symbol nm = get_long_name(1);
6139   if (nm.is_null())
6140     skip_line();
6141   else {
6142     while (!tok.newline() && !tok.eof())
6143       tok.next();
6144     errno = 0;
6145     // PS files might contain non-printable characters, such as ^Z
6146     // and CRs not followed by an LF, so open them in binary mode.
6147     FILE *fp = include_search_path.open_file_cautious(nm.contents(),
6148                                                       0, FOPEN_RB);
6149     if (fp) {
6150       do_ps_file(fp, nm.contents());
6151       fclose(fp);
6152     }
6153     else
6154       error("can't open `%1': %2", nm.contents(), strerror(errno));
6155     tok.next();
6156   }
6157 }
6158
6159 const char *asciify(int c)
6160 {
6161   static char buf[3];
6162   buf[0] = escape_char == '\0' ? '\\' : escape_char;
6163   buf[1] = buf[2] = '\0';
6164   switch (c) {
6165   case ESCAPE_QUESTION:
6166     buf[1] = '?';
6167     break;
6168   case ESCAPE_AMPERSAND:
6169     buf[1] = '&';
6170     break;
6171   case ESCAPE_RIGHT_PARENTHESIS:
6172     buf[1] = ')';
6173     break;
6174   case ESCAPE_UNDERSCORE:
6175     buf[1] = '_';
6176     break;
6177   case ESCAPE_BAR:
6178     buf[1] = '|';
6179     break;
6180   case ESCAPE_CIRCUMFLEX:
6181     buf[1] = '^';
6182     break;
6183   case ESCAPE_LEFT_BRACE:
6184     buf[1] = '{';
6185     break;
6186   case ESCAPE_RIGHT_BRACE:
6187     buf[1] = '}';
6188     break;
6189   case ESCAPE_LEFT_QUOTE:
6190     buf[1] = '`';
6191     break;
6192   case ESCAPE_RIGHT_QUOTE:
6193     buf[1] = '\'';
6194     break;
6195   case ESCAPE_HYPHEN:
6196     buf[1] = '-';
6197     break;
6198   case ESCAPE_BANG:
6199     buf[1] = '!';
6200     break;
6201   case ESCAPE_c:
6202     buf[1] = 'c';
6203     break;
6204   case ESCAPE_e:
6205     buf[1] = 'e';
6206     break;
6207   case ESCAPE_E:
6208     buf[1] = 'E';
6209     break;
6210   case ESCAPE_PERCENT:
6211     buf[1] = '%';
6212     break;
6213   case ESCAPE_SPACE:
6214     buf[1] = ' ';
6215     break;
6216   case ESCAPE_TILDE:
6217     buf[1] = '~';
6218     break;
6219   case ESCAPE_COLON:
6220     buf[1] = ':';
6221     break;
6222   case PUSH_GROFF_MODE:
6223   case PUSH_COMP_MODE:
6224   case POP_GROFFCOMP_MODE:
6225     buf[0] = '\0';
6226     break;
6227   default:
6228     if (invalid_input_char(c))
6229       buf[0] = '\0';
6230     else
6231       buf[0] = c;
6232     break;
6233   }
6234   return buf;
6235 }
6236
6237 const char *input_char_description(int c)
6238 {
6239   switch (c) {
6240   case '\n':
6241     return "a newline character";
6242   case '\b':
6243     return "a backspace character";
6244   case '\001':
6245     return "a leader character";
6246   case '\t':
6247     return "a tab character";
6248   case ' ':
6249     return "a space character";
6250   case '\0':
6251     return "a node";
6252   }
6253   static char buf[sizeof("magic character code ") + 1 + INT_DIGITS];
6254   if (invalid_input_char(c)) {
6255     const char *s = asciify(c);
6256     if (*s) {
6257       buf[0] = '`';
6258       strcpy(buf + 1, s);
6259       strcat(buf, "'");
6260       return buf;
6261     }
6262     sprintf(buf, "magic character code %d", c);
6263     return buf;
6264   }
6265   if (csprint(c)) {
6266     buf[0] = '`';
6267     buf[1] = c;
6268     buf[2] = '\'';
6269     return buf;
6270   }
6271   sprintf(buf, "character code %d", c);
6272   return buf;
6273 }
6274
6275 void tag()
6276 {
6277   if (!tok.newline() && !tok.eof()) {
6278     string s;
6279     int c;
6280     for (;;) {
6281       c = get_copy(0);
6282       if (c == '"') {
6283         c = get_copy(0);
6284         break;
6285       }
6286       if (c != ' ' && c != '\t')
6287         break;
6288     }
6289     s = "x X ";
6290     for (; c != '\n' && c != EOF; c = get_copy(0))
6291       s += (char)c;
6292     s += '\n';
6293     curenv->add_node(new tag_node(s, 0));
6294   }
6295   tok.next();
6296 }
6297
6298 void taga()
6299 {
6300   if (!tok.newline() && !tok.eof()) {
6301     string s;
6302     int c;
6303     for (;;) {
6304       c = get_copy(0);
6305       if (c == '"') {
6306         c = get_copy(0);
6307         break;
6308       }
6309       if (c != ' ' && c != '\t')
6310         break;
6311     }
6312     s = "x X ";
6313     for (; c != '\n' && c != EOF; c = get_copy(0))
6314       s += (char)c;
6315     s += '\n';
6316     curenv->add_node(new tag_node(s, 1));
6317   }
6318   tok.next();
6319 }
6320
6321 // .tm, .tm1, and .tmc
6322
6323 void do_terminal(int newline, int string_like)
6324 {
6325   if (!tok.newline() && !tok.eof()) {
6326     int c;
6327     for (;;) {
6328       c = get_copy(0);
6329       if (string_like && c == '"') {
6330         c = get_copy(0);
6331         break;
6332       }
6333       if (c != ' ' && c != '\t')
6334         break;
6335     }
6336     for (; c != '\n' && c != EOF; c = get_copy(0))
6337       fputs(asciify(c), stderr);
6338   }
6339   if (newline)
6340     fputc('\n', stderr);
6341   fflush(stderr);
6342   tok.next();
6343 }
6344
6345 void terminal()
6346 {
6347   do_terminal(1, 0);
6348 }
6349
6350 void terminal1()
6351 {
6352   do_terminal(1, 1);
6353 }
6354
6355 void terminal_continue()
6356 {
6357   do_terminal(0, 1);
6358 }
6359
6360 dictionary stream_dictionary(20);
6361
6362 void do_open(int append)
6363 {
6364   symbol stream = get_name(1);
6365   if (!stream.is_null()) {
6366     symbol filename = get_long_name(1);
6367     if (!filename.is_null()) {
6368       errno = 0;
6369       FILE *fp = fopen(filename.contents(), append ? "a" : "w");
6370       if (!fp) {
6371         error("can't open `%1' for %2: %3",
6372               filename.contents(),
6373               append ? "appending" : "writing",
6374               strerror(errno));
6375         fp = (FILE *)stream_dictionary.remove(stream);
6376       }
6377       else
6378         fp = (FILE *)stream_dictionary.lookup(stream, fp);
6379       if (fp)
6380         fclose(fp);
6381     }
6382   }
6383   skip_line();
6384 }
6385
6386 void open_request()
6387 {
6388   if (!unsafe_flag) {
6389     error(".open request not allowed in safer mode");
6390     skip_line();
6391   }
6392   else
6393     do_open(0);
6394 }
6395
6396 void opena_request()
6397 {
6398   if (!unsafe_flag) {
6399     error(".opena request not allowed in safer mode");
6400     skip_line();
6401   }
6402   else
6403     do_open(1);
6404 }
6405
6406 void close_request()
6407 {
6408   symbol stream = get_name(1);
6409   if (!stream.is_null()) {
6410     FILE *fp = (FILE *)stream_dictionary.remove(stream);
6411     if (!fp)
6412       error("no stream named `%1'", stream.contents());
6413     else
6414       fclose(fp);
6415   }
6416   skip_line();
6417 }
6418
6419 // .write and .writec
6420
6421 void do_write_request(int newline)
6422 {
6423   symbol stream = get_name(1);
6424   if (stream.is_null()) {
6425     skip_line();
6426     return;
6427   }
6428   FILE *fp = (FILE *)stream_dictionary.lookup(stream);
6429   if (!fp) {
6430     error("no stream named `%1'", stream.contents());
6431     skip_line();
6432     return;
6433   }
6434   int c;
6435   while ((c = get_copy(0)) == ' ')
6436     ;
6437   if (c == '"')
6438     c = get_copy(0);
6439   for (; c != '\n' && c != EOF; c = get_copy(0))
6440     fputs(asciify(c), fp);
6441   if (newline)
6442     fputc('\n', fp);
6443   fflush(fp);
6444   tok.next();
6445 }
6446
6447 void write_request()
6448 {
6449   do_write_request(1);
6450 }
6451
6452 void write_request_continue()
6453 {
6454   do_write_request(0);
6455 }
6456
6457 void write_macro_request()
6458 {
6459   symbol stream = get_name(1);
6460   if (stream.is_null()) {
6461     skip_line();
6462     return;
6463   }
6464   FILE *fp = (FILE *)stream_dictionary.lookup(stream);
6465   if (!fp) {
6466     error("no stream named `%1'", stream.contents());
6467     skip_line();
6468     return;
6469   }
6470   symbol s = get_name(1);
6471   if (s.is_null()) {
6472     skip_line();
6473     return;
6474   }
6475   request_or_macro *p = lookup_request(s);
6476   macro *m = p->to_macro();
6477   if (!m)
6478     error("cannot write request");
6479   else {
6480     string_iterator iter(*m);
6481     for (;;) {
6482       int c = iter.get(0);
6483       if (c == EOF)
6484         break;
6485       fputs(asciify(c), fp);
6486     }
6487     fflush(fp);
6488   }
6489   skip_line();
6490 }
6491
6492 void warnscale_request()
6493 {
6494   if (has_arg()) {
6495     char c = tok.ch();
6496     if (c == 'u')
6497       warn_scale = 1.0;
6498     else if (c == 'i')
6499       warn_scale = (double)units_per_inch;
6500     else if (c == 'c')
6501       warn_scale = (double)units_per_inch / 2.54;
6502     else if (c == 'p')
6503       warn_scale = (double)units_per_inch / 72.0;
6504     else if (c == 'P')
6505       warn_scale = (double)units_per_inch / 6.0;
6506     else {
6507       warning(WARN_SCALE,
6508               "invalid scaling indicator `%1', using `i' instead", c);
6509       c = 'i';
6510     }
6511     warn_scaling_indicator = c;
6512   }
6513   skip_line();
6514 }
6515
6516 void spreadwarn_request()
6517 {
6518   hunits n;
6519   if (has_arg() && get_hunits(&n, 'm')) {
6520     if (n < 0)
6521       n = 0;
6522     hunits em = curenv->get_size();
6523     spread_limit = (double)n.to_units()
6524                    / (em.is_zero() ? hresolution : em.to_units());
6525   }
6526   else
6527     spread_limit = -spread_limit - 1;   // no arg toggles on/off without
6528                                         // changing value; we mirror at
6529                                         // -0.5 to make zero a valid value
6530   skip_line();
6531 }
6532
6533 static void init_charset_table()
6534 {
6535   char buf[16];
6536   strcpy(buf, "char");
6537   for (int i = 0; i < 256; i++) {
6538     strcpy(buf + 4, i_to_a(i));
6539     charset_table[i] = get_charinfo(symbol(buf));
6540     charset_table[i]->set_ascii_code(i);
6541     if (csalpha(i))
6542       charset_table[i]->set_hyphenation_code(cmlower(i));
6543   }
6544   charset_table['.']->set_flags(charinfo::ENDS_SENTENCE);
6545   charset_table['?']->set_flags(charinfo::ENDS_SENTENCE);
6546   charset_table['!']->set_flags(charinfo::ENDS_SENTENCE);
6547   charset_table['-']->set_flags(charinfo::BREAK_AFTER);
6548   charset_table['"']->set_flags(charinfo::TRANSPARENT);
6549   charset_table['\'']->set_flags(charinfo::TRANSPARENT);
6550   charset_table[')']->set_flags(charinfo::TRANSPARENT);
6551   charset_table[']']->set_flags(charinfo::TRANSPARENT);
6552   charset_table['*']->set_flags(charinfo::TRANSPARENT);
6553   get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT);
6554   get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT);
6555   get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER);
6556   get_charinfo(symbol("hy"))->set_flags(charinfo::BREAK_AFTER);
6557   get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6558   get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6559   get_charinfo(symbol("radicalex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6560   get_charinfo(symbol("sqrtex"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6561   get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
6562   get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY);
6563   page_character = charset_table['%'];
6564 }
6565
6566 static void init_hpf_code_table()
6567 {
6568   for (int i = 0; i < 256; i++)
6569     hpf_code_table[i] = i;
6570 }
6571
6572 static void do_translate(int translate_transparent, int translate_input)
6573 {
6574   tok.skip();
6575   while (!tok.newline() && !tok.eof()) {
6576     if (tok.space()) {
6577       // This is a really bizarre troff feature.
6578       tok.next();
6579       translate_space_to_dummy = tok.dummy();
6580       if (tok.newline() || tok.eof())
6581         break;
6582       tok.next();
6583       continue;
6584     }
6585     charinfo *ci1 = tok.get_char(1);
6586     if (ci1 == 0)
6587       break;
6588     tok.next();
6589     if (tok.newline() || tok.eof()) {
6590       ci1->set_special_translation(charinfo::TRANSLATE_SPACE,
6591                                    translate_transparent);
6592       break;
6593     }
6594     if (tok.space())
6595       ci1->set_special_translation(charinfo::TRANSLATE_SPACE,
6596                                    translate_transparent);
6597     else if (tok.stretchable_space())
6598       ci1->set_special_translation(charinfo::TRANSLATE_STRETCHABLE_SPACE,
6599                                    translate_transparent);
6600     else if (tok.dummy())
6601       ci1->set_special_translation(charinfo::TRANSLATE_DUMMY,
6602                                    translate_transparent);
6603     else if (tok.hyphen_indicator())
6604       ci1->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR,
6605                                    translate_transparent);
6606     else {
6607       charinfo *ci2 = tok.get_char(1);
6608       if (ci2 == 0)
6609         break;
6610       if (ci1 == ci2)
6611         ci1->set_translation(0, translate_transparent, translate_input);
6612       else
6613         ci1->set_translation(ci2, translate_transparent, translate_input);
6614     }
6615     tok.next();
6616   }
6617   skip_line();
6618 }
6619
6620 void translate()
6621 {
6622   do_translate(1, 0);
6623 }
6624
6625 void translate_no_transparent()
6626 {
6627   do_translate(0, 0);
6628 }
6629
6630 void translate_input()
6631 {
6632   do_translate(1, 1);
6633 }
6634
6635 void char_flags()
6636 {
6637   int flags;
6638   if (get_integer(&flags))
6639     while (has_arg()) {
6640       charinfo *ci = tok.get_char(1);
6641       if (ci) {
6642         charinfo *tem = ci->get_translation();
6643         if (tem)
6644           ci = tem;
6645         ci->set_flags(flags);
6646       }
6647       tok.next();
6648     }
6649   skip_line();
6650 }
6651
6652 void hyphenation_code()
6653 {
6654   tok.skip();
6655   while (!tok.newline() && !tok.eof()) {
6656     charinfo *ci = tok.get_char(1);
6657     if (ci == 0)
6658       break;
6659     tok.next();
6660     tok.skip();
6661     unsigned char c = tok.ch();
6662     if (c == 0) {
6663       error("hyphenation code must be ordinary character");
6664       break;
6665     }
6666     if (csdigit(c)) {
6667       error("hyphenation code cannot be digit");
6668       break;
6669     }
6670     ci->set_hyphenation_code(c);
6671     if (ci->get_translation()
6672         && ci->get_translation()->get_translation_input())
6673       ci->get_translation()->set_hyphenation_code(c);
6674     tok.next();
6675     tok.skip();
6676   }
6677   skip_line();
6678 }
6679
6680 void hyphenation_patterns_file_code()
6681 {
6682   tok.skip();
6683   while (!tok.newline() && !tok.eof()) {
6684     int n1, n2;
6685     if (get_integer(&n1) && (0 <= n1 && n1 <= 255)) {
6686       if (!has_arg()) {
6687         error("missing output hyphenation code");
6688         break;
6689       }
6690       if (get_integer(&n2) && (0 <= n2 && n2 <= 255)) {
6691         hpf_code_table[n1] = n2;
6692         tok.skip();
6693       }
6694       else {
6695         error("output hyphenation code must be integer in the range 0..255");
6696         break;
6697       }
6698     }
6699     else {
6700       error("input hyphenation code must be integer in the range 0..255");
6701       break;
6702     }
6703   }
6704   skip_line();
6705 }
6706
6707 charinfo *token::get_char(int required)
6708 {
6709   if (type == TOKEN_CHAR)
6710     return charset_table[c];
6711   if (type == TOKEN_SPECIAL)
6712     return get_charinfo(nm);
6713   if (type == TOKEN_NUMBERED_CHAR)
6714     return get_charinfo_by_number(val);
6715   if (type == TOKEN_ESCAPE) {
6716     if (escape_char != 0)
6717       return charset_table[escape_char];
6718     else {
6719       error("`\\e' used while no current escape character");
6720       return 0;
6721     }
6722   }
6723   if (required) {
6724     if (type == TOKEN_EOF || type == TOKEN_NEWLINE)
6725       warning(WARN_MISSING, "missing normal or special character");
6726     else
6727       error("normal or special character expected (got %1)", description());
6728   }
6729   return 0;
6730 }
6731
6732 charinfo *get_optional_char()
6733 {
6734   while (tok.space())
6735     tok.next();
6736   charinfo *ci = tok.get_char();
6737   if (!ci)
6738     check_missing_character();
6739   else
6740     tok.next();
6741   return ci;
6742 }
6743
6744 void check_missing_character()
6745 {
6746   if (!tok.newline() && !tok.eof() && !tok.right_brace() && !tok.tab())
6747     error("normal or special character expected (got %1): "
6748           "treated as missing",
6749           tok.description());
6750 }
6751
6752 // this is for \Z
6753
6754 int token::add_to_node_list(node **pp)
6755 {
6756   hunits w;
6757   int s;
6758   node *n = 0;
6759   switch (type) {
6760   case TOKEN_CHAR:
6761     *pp = (*pp)->add_char(charset_table[c], curenv, &w, &s);
6762     break;
6763   case TOKEN_DUMMY:
6764     n = new dummy_node;
6765     break;
6766   case TOKEN_ESCAPE:
6767     if (escape_char != 0)
6768       *pp = (*pp)->add_char(charset_table[escape_char], curenv, &w, &s);
6769     break;
6770   case TOKEN_HYPHEN_INDICATOR:
6771     *pp = (*pp)->add_discretionary_hyphen();
6772     break;
6773   case TOKEN_ITALIC_CORRECTION:
6774     *pp = (*pp)->add_italic_correction(&w);
6775     break;
6776   case TOKEN_LEFT_BRACE:
6777     break;
6778   case TOKEN_MARK_INPUT:
6779     set_number_reg(nm, curenv->get_input_line_position().to_units());
6780     break;
6781   case TOKEN_NODE:
6782     n = nd;
6783     nd = 0;
6784     break;
6785   case TOKEN_NUMBERED_CHAR:
6786     *pp = (*pp)->add_char(get_charinfo_by_number(val), curenv, &w, &s);
6787     break;
6788   case TOKEN_RIGHT_BRACE:
6789     break;
6790   case TOKEN_SPACE:
6791     n = new hmotion_node(curenv->get_space_width(),
6792                          curenv->get_fill_color());
6793     break;
6794   case TOKEN_SPECIAL:
6795     *pp = (*pp)->add_char(get_charinfo(nm), curenv, &w, &s);
6796     break;
6797   case TOKEN_STRETCHABLE_SPACE:
6798     n = new unbreakable_space_node(curenv->get_space_width(),
6799                                    curenv->get_fill_color());
6800     break;
6801   case TOKEN_UNSTRETCHABLE_SPACE:
6802     n = new space_char_hmotion_node(curenv->get_space_width(),
6803                                     curenv->get_fill_color());
6804     break;
6805   case TOKEN_TRANSPARENT_DUMMY:
6806     n = new transparent_dummy_node;
6807     break;
6808   case TOKEN_ZERO_WIDTH_BREAK:
6809     n = new space_node(H0, curenv->get_fill_color());
6810     n->freeze_space();
6811     n->is_escape_colon();
6812     break;
6813   default:
6814     return 0;
6815   }
6816   if (n) {
6817     n->next = *pp;
6818     *pp = n;
6819   }
6820   return 1;
6821 }
6822
6823 void token::process()
6824 {
6825   if (possibly_handle_first_page_transition())
6826     return;
6827   switch (type) {
6828   case TOKEN_BACKSPACE:
6829     curenv->add_node(new hmotion_node(-curenv->get_space_width(),
6830                                       curenv->get_fill_color()));
6831     break;
6832   case TOKEN_CHAR:
6833     curenv->add_char(charset_table[c]);
6834     break;
6835   case TOKEN_DUMMY:
6836     curenv->add_node(new dummy_node);
6837     break;
6838   case TOKEN_EMPTY:
6839     assert(0);
6840     break;
6841   case TOKEN_EOF:
6842     assert(0);
6843     break;
6844   case TOKEN_ESCAPE:
6845     if (escape_char != 0)
6846       curenv->add_char(charset_table[escape_char]);
6847     break;
6848   case TOKEN_BEGIN_TRAP:
6849   case TOKEN_END_TRAP:
6850   case TOKEN_PAGE_EJECTOR:
6851     // these are all handled in process_input_stack()
6852     break;
6853   case TOKEN_HYPHEN_INDICATOR:
6854     curenv->add_hyphen_indicator();
6855     break;
6856   case TOKEN_INTERRUPT:
6857     curenv->interrupt();
6858     break;
6859   case TOKEN_ITALIC_CORRECTION:
6860     curenv->add_italic_correction();
6861     break;
6862   case TOKEN_LEADER:
6863     curenv->handle_tab(1);
6864     break;
6865   case TOKEN_LEFT_BRACE:
6866     break;
6867   case TOKEN_MARK_INPUT:
6868     set_number_reg(nm, curenv->get_input_line_position().to_units());
6869     break;
6870   case TOKEN_NEWLINE:
6871     curenv->newline();
6872     break;
6873   case TOKEN_NODE:
6874     curenv->add_node(nd);
6875     nd = 0;
6876     break;
6877   case TOKEN_NUMBERED_CHAR:
6878     curenv->add_char(get_charinfo_by_number(val));
6879     break;
6880   case TOKEN_REQUEST:
6881     // handled in process_input_stack()
6882     break;
6883   case TOKEN_RIGHT_BRACE:
6884     break;
6885   case TOKEN_SPACE:
6886     curenv->space();
6887     break;
6888   case TOKEN_SPECIAL:
6889     curenv->add_char(get_charinfo(nm));
6890     break;
6891   case TOKEN_SPREAD:
6892     curenv->spread();
6893     break;
6894   case TOKEN_STRETCHABLE_SPACE:
6895     curenv->add_node(new unbreakable_space_node(curenv->get_space_width(),
6896                                                 curenv->get_fill_color()));
6897     break;
6898   case TOKEN_UNSTRETCHABLE_SPACE:
6899     curenv->add_node(new space_char_hmotion_node(curenv->get_space_width(),
6900                                                  curenv->get_fill_color()));
6901     break;
6902   case TOKEN_TAB:
6903     curenv->handle_tab(0);
6904     break;
6905   case TOKEN_TRANSPARENT:
6906     break;
6907   case TOKEN_TRANSPARENT_DUMMY:
6908     curenv->add_node(new transparent_dummy_node);
6909     break;
6910   case TOKEN_ZERO_WIDTH_BREAK:
6911     {
6912       node *tmp = new space_node(H0, curenv->get_fill_color());
6913       tmp->freeze_space();
6914       tmp->is_escape_colon();
6915       curenv->add_node(tmp);
6916       break;
6917     }
6918   default:
6919     assert(0);
6920   }
6921 }
6922
6923 class nargs_reg : public reg {
6924 public:
6925   const char *get_string();
6926 };
6927
6928 const char *nargs_reg::get_string()
6929 {
6930   return i_to_a(input_stack::nargs());
6931 }
6932
6933 class lineno_reg : public reg {
6934 public:
6935   const char *get_string();
6936 };
6937
6938 const char *lineno_reg::get_string()
6939 {
6940   int line;
6941   const char *file;
6942   if (!input_stack::get_location(0, &file, &line))
6943     line = 0;
6944   return i_to_a(line);
6945 }
6946
6947 class writable_lineno_reg : public general_reg {
6948 public:
6949   writable_lineno_reg();
6950   void set_value(units);
6951   int get_value(units *);
6952 };
6953
6954 writable_lineno_reg::writable_lineno_reg()
6955 {
6956 }
6957
6958 int writable_lineno_reg::get_value(units *res)
6959 {
6960   int line;
6961   const char *file;
6962   if (!input_stack::get_location(0, &file, &line))
6963     return 0;
6964   *res = line;
6965   return 1;
6966 }
6967
6968 void writable_lineno_reg::set_value(units n)
6969 {
6970   input_stack::set_location(0, n);
6971 }
6972
6973 class filename_reg : public reg {
6974 public:
6975   const char *get_string();
6976 };
6977
6978 const char *filename_reg::get_string()
6979 {
6980   int line;
6981   const char *file;
6982   if (input_stack::get_location(0, &file, &line))
6983     return file;
6984   else
6985     return 0;
6986 }
6987
6988 class break_flag_reg : public reg {
6989 public:
6990   const char *get_string();
6991 };
6992
6993 const char *break_flag_reg::get_string()
6994 {
6995   return i_to_a(input_stack::get_break_flag());
6996 }
6997
6998 class constant_reg : public reg {
6999   const char *s;
7000 public:
7001   constant_reg(const char *);
7002   const char *get_string();
7003 };
7004
7005 constant_reg::constant_reg(const char *p) : s(p)
7006 {
7007 }
7008
7009 const char *constant_reg::get_string()
7010 {
7011   return s;
7012 }
7013
7014 constant_int_reg::constant_int_reg(int *q) : p(q)
7015 {
7016 }
7017
7018 const char *constant_int_reg::get_string()
7019 {
7020   return i_to_a(*p);
7021 }
7022
7023 void abort_request()
7024 {
7025   int c;
7026   if (tok.eof())
7027     c = EOF;
7028   else if (tok.newline())
7029     c = '\n';
7030   else {
7031     while ((c = get_copy(0)) == ' ')
7032       ;
7033   }
7034   if (c == EOF || c == '\n')
7035     fputs("User Abort.", stderr);
7036   else {
7037     for (; c != '\n' && c != EOF; c = get_copy(0))
7038       fputs(asciify(c), stderr);
7039   }
7040   fputc('\n', stderr);
7041   cleanup_and_exit(1);
7042 }
7043
7044 char *read_string()
7045 {
7046   int len = 256;
7047   char *s = new char[len];
7048   int c;
7049   while ((c = get_copy(0)) == ' ')
7050     ;
7051   int i = 0;
7052   while (c != '\n' && c != EOF) {
7053     if (!invalid_input_char(c)) {
7054       if (i + 2 > len) {
7055         char *tem = s;
7056         s = new char[len*2];
7057         memcpy(s, tem, len);
7058         len *= 2;
7059         a_delete tem;
7060       }
7061       s[i++] = c;
7062     }
7063     c = get_copy(0);
7064   }
7065   s[i] = '\0';
7066   tok.next();
7067   if (i == 0) {
7068     a_delete s;
7069     return 0;
7070   }
7071   return s;
7072 }
7073
7074 void pipe_output()
7075 {
7076   if (!unsafe_flag) {
7077     error(".pi request not allowed in safer mode");
7078     skip_line();
7079   }
7080   else {
7081 #ifdef POPEN_MISSING
7082     error("pipes not available on this system");
7083     skip_line();
7084 #else /* not POPEN_MISSING */
7085     if (the_output) {
7086       error("can't pipe: output already started");
7087       skip_line();
7088     }
7089     else {
7090       char *pc;
7091       if ((pc = read_string()) == 0)
7092         error("can't pipe to empty command");
7093       if (pipe_command) {
7094         char *s = new char[strlen(pipe_command) + strlen(pc) + 1 + 1];
7095         strcpy(s, pipe_command);
7096         strcat(s, "|");
7097         strcat(s, pc);
7098         a_delete pipe_command;
7099         a_delete pc;
7100         pipe_command = s;
7101       }
7102       else
7103         pipe_command = pc;
7104     }
7105 #endif /* not POPEN_MISSING */
7106   }
7107 }
7108
7109 static int system_status;
7110
7111 void system_request()
7112 {
7113   if (!unsafe_flag) {
7114     error(".sy request not allowed in safer mode");
7115     skip_line();
7116   }
7117   else {
7118     char *command = read_string();
7119     if (!command)
7120       error("empty command");
7121     else {
7122       system_status = system(command);
7123       a_delete command;
7124     }
7125   }
7126 }
7127
7128 void copy_file()
7129 {
7130   if (curdiv == topdiv && topdiv->before_first_page) {
7131     handle_initial_request(COPY_FILE_REQUEST);
7132     return;
7133   }
7134   symbol filename = get_long_name(1);
7135   while (!tok.newline() && !tok.eof())
7136     tok.next();
7137   if (break_flag)
7138     curenv->do_break();
7139   if (!filename.is_null())
7140     curdiv->copy_file(filename.contents());
7141   tok.next();
7142 }
7143
7144 #ifdef COLUMN
7145
7146 void vjustify()
7147 {
7148   if (curdiv == topdiv && topdiv->before_first_page) {
7149     handle_initial_request(VJUSTIFY_REQUEST);
7150     return;
7151   }
7152   symbol type = get_long_name(1);
7153   if (!type.is_null())
7154     curdiv->vjustify(type);
7155   skip_line();
7156 }
7157
7158 #endif /* COLUMN */
7159
7160 void transparent_file()
7161 {
7162   if (curdiv == topdiv && topdiv->before_first_page) {
7163     handle_initial_request(TRANSPARENT_FILE_REQUEST);
7164     return;
7165   }
7166   symbol filename = get_long_name(1);
7167   while (!tok.newline() && !tok.eof())
7168     tok.next();
7169   if (break_flag)
7170     curenv->do_break();
7171   if (!filename.is_null()) {
7172     errno = 0;
7173     FILE *fp = include_search_path.open_file_cautious(filename.contents());
7174     if (!fp)
7175       error("can't open `%1': %2", filename.contents(), strerror(errno));
7176     else {
7177       int bol = 1;
7178       for (;;) {
7179         int c = getc(fp);
7180         if (c == EOF)
7181           break;
7182         if (invalid_input_char(c))
7183           warning(WARN_INPUT, "invalid input character code %1", int(c));
7184         else {
7185           curdiv->transparent_output(c);
7186           bol = c == '\n';
7187         }
7188       }
7189       if (!bol)
7190         curdiv->transparent_output('\n');
7191       fclose(fp);
7192     }
7193   }
7194   tok.next();
7195 }
7196
7197 class page_range {
7198   int first;
7199   int last;
7200 public:
7201   page_range *next;
7202   page_range(int, int, page_range *);
7203   int contains(int n);
7204 };
7205
7206 page_range::page_range(int i, int j, page_range *p)
7207 : first(i), last(j), next(p)
7208 {
7209 }
7210
7211 int page_range::contains(int n)
7212 {
7213   return n >= first && (last <= 0 || n <= last);
7214 }
7215
7216 page_range *output_page_list = 0;
7217
7218 int in_output_page_list(int n)
7219 {
7220   if (!output_page_list)
7221     return 1;
7222   for (page_range *p = output_page_list; p; p = p->next)
7223     if (p->contains(n))
7224       return 1;
7225   return 0;
7226 }
7227
7228 static void parse_output_page_list(char *p)
7229 {
7230   for (;;) {
7231     int i;
7232     if (*p == '-')
7233       i = 1;
7234     else if (csdigit(*p)) {
7235       i = 0;
7236       do
7237         i = i*10 + *p++ - '0';
7238       while (csdigit(*p));
7239     }
7240     else
7241       break;
7242     int j;
7243     if (*p == '-') {
7244       p++;
7245       j = 0;
7246       if (csdigit(*p)) {
7247         do
7248           j = j*10 + *p++ - '0';
7249         while (csdigit(*p));
7250       }
7251     }
7252     else
7253       j = i;
7254     if (j == 0)
7255       last_page_number = -1;
7256     else if (last_page_number >= 0 && j > last_page_number)
7257       last_page_number = j;
7258     output_page_list = new page_range(i, j, output_page_list);
7259     if (*p != ',')
7260       break;
7261     ++p;
7262   }
7263   if (*p != '\0') {
7264     error("bad output page list");
7265     output_page_list = 0;
7266   }
7267 }
7268
7269 static FILE *open_mac_file(const char *mac, char **path)
7270 {
7271   // Try first FOOBAR.tmac, then tmac.FOOBAR
7272   char *s1 = new char[strlen(mac)+strlen(MACRO_POSTFIX)+1];
7273   strcpy(s1, mac);
7274   strcat(s1, MACRO_POSTFIX);
7275   FILE *fp = mac_path->open_file(s1, path);
7276   a_delete s1;
7277   if (!fp) {
7278     char *s2 = new char[strlen(mac)+strlen(MACRO_PREFIX)+1];
7279     strcpy(s2, MACRO_PREFIX);
7280     strcat(s2, mac);
7281     fp = mac_path->open_file(s2, path);
7282     a_delete s2;
7283   }
7284   return fp;
7285 }
7286
7287 static void process_macro_file(const char *mac)
7288 {
7289   char *path;
7290   FILE *fp = open_mac_file(mac, &path);
7291   if (!fp)
7292     fatal("can't find macro file %1", mac);
7293   const char *s = symbol(path).contents();
7294   a_delete path;
7295   input_stack::push(new file_iterator(fp, s));
7296   tok.next();
7297   process_input_stack();
7298 }
7299
7300 static void process_startup_file(const char *filename)
7301 {
7302   char *path;
7303   search_path *orig_mac_path = mac_path;
7304   mac_path = &config_macro_path;
7305   FILE *fp = mac_path->open_file(filename, &path);
7306   if (fp) {
7307     input_stack::push(new file_iterator(fp, symbol(path).contents()));
7308     a_delete path;
7309     tok.next();
7310     process_input_stack();
7311   }
7312   mac_path = orig_mac_path;
7313 }
7314
7315 void macro_source()
7316 {
7317   symbol nm = get_long_name(1);
7318   if (nm.is_null())
7319     skip_line();
7320   else {
7321     while (!tok.newline() && !tok.eof())
7322       tok.next();
7323     char *path;
7324     FILE *fp = mac_path->open_file(nm.contents(), &path);
7325     // .mso doesn't (and cannot) go through open_mac_file, so we
7326     // need to do it here manually: If we have tmac.FOOBAR, try
7327     // FOOBAR.tmac and vice versa
7328     if (!fp) {
7329       const char *fn = nm.contents();
7330       if (strncasecmp(fn, MACRO_PREFIX, sizeof(MACRO_PREFIX) - 1) == 0) {
7331         char *s = new char[strlen(fn) + sizeof(MACRO_POSTFIX)];
7332         strcpy(s, fn + sizeof(MACRO_PREFIX) - 1);
7333         strcat(s, MACRO_POSTFIX);
7334         fp = mac_path->open_file(s, &path);
7335         a_delete s;
7336       }
7337       if (!fp) {
7338         if (strncasecmp(fn + strlen(fn) - sizeof(MACRO_POSTFIX) + 1,
7339                         MACRO_POSTFIX, sizeof(MACRO_POSTFIX) - 1) == 0) {
7340           char *s = new char[strlen(fn) + sizeof(MACRO_PREFIX)];
7341           strcpy(s, MACRO_PREFIX);
7342           strncat(s, fn, strlen(fn) - sizeof(MACRO_POSTFIX) + 1);
7343           fp = mac_path->open_file(s, &path);
7344           a_delete s;
7345         }
7346       }
7347     }
7348     if (fp) {
7349       input_stack::push(new file_iterator(fp, symbol(path).contents()));
7350       a_delete path;
7351     }
7352     else
7353       error("can't find macro file `%1'", nm.contents());
7354     tok.next();
7355   }
7356 }
7357
7358 static void process_input_file(const char *name)
7359 {
7360   FILE *fp;
7361   if (strcmp(name, "-") == 0) {
7362     clearerr(stdin);
7363     fp = stdin;
7364   }
7365   else {
7366     errno = 0;
7367     fp = include_search_path.open_file_cautious(name);
7368     if (!fp)
7369       fatal("can't open `%1': %2", name, strerror(errno));
7370   }
7371   input_stack::push(new file_iterator(fp, name));
7372   tok.next();
7373   process_input_stack();
7374 }
7375
7376 // make sure the_input is empty before calling this
7377
7378 static int evaluate_expression(const char *expr, units *res)
7379 {
7380   input_stack::push(make_temp_iterator(expr));
7381   tok.next();
7382   int success = get_number(res, 'u');
7383   while (input_stack::get(0) != EOF)
7384     ;
7385   return success;
7386 }
7387
7388 static void do_register_assignment(const char *s)
7389 {
7390   const char *p = strchr(s, '=');
7391   if (!p) {
7392     char buf[2];
7393     buf[0] = s[0];
7394     buf[1] = 0;
7395     units n;
7396     if (evaluate_expression(s + 1, &n))
7397       set_number_reg(buf, n);
7398   }
7399   else {
7400     char *buf = new char[p - s + 1];
7401     memcpy(buf, s, p - s);
7402     buf[p - s] = 0;
7403     units n;
7404     if (evaluate_expression(p + 1, &n))
7405       set_number_reg(buf, n);
7406     a_delete buf;
7407   }
7408 }
7409
7410 static void set_string(const char *name, const char *value)
7411 {
7412   macro *m = new macro;
7413   for (const char *p = value; *p; p++)
7414     if (!invalid_input_char((unsigned char)*p))
7415       m->append(*p);
7416   request_dictionary.define(name, m);
7417 }
7418
7419 static void do_string_assignment(const char *s)
7420 {
7421   const char *p = strchr(s, '=');
7422   if (!p) {
7423     char buf[2];
7424     buf[0] = s[0];
7425     buf[1] = 0;
7426     set_string(buf, s + 1);
7427   }
7428   else {
7429     char *buf = new char[p - s + 1];
7430     memcpy(buf, s, p - s);
7431     buf[p - s] = 0;
7432     set_string(buf, p + 1);
7433     a_delete buf;
7434   }
7435 }
7436
7437 struct string_list {
7438   const char *s;
7439   string_list *next;
7440   string_list(const char *ss) : s(ss), next(0) {}
7441 };
7442
7443 #if 0
7444 static void prepend_string(const char *s, string_list **p)
7445 {
7446   string_list *l = new string_list(s);
7447   l->next = *p;
7448   *p = l;
7449 }
7450 #endif
7451
7452 static void add_string(const char *s, string_list **p)
7453 {
7454   while (*p)
7455     p = &((*p)->next);
7456   *p = new string_list(s);
7457 }
7458
7459 void usage(FILE *stream, const char *prog)
7460 {
7461   fprintf(stream,
7462 "usage: %s -abcivzCERU -wname -Wname -dcs -ffam -mname -nnum -olist\n"
7463 "       -rcn -Tname -Fdir -Idir -Mdir [files...]\n",
7464           prog);
7465 }
7466
7467 int main(int argc, char **argv)
7468 {
7469   program_name = argv[0];
7470   static char stderr_buf[BUFSIZ];
7471   setbuf(stderr, stderr_buf);
7472   int c;
7473   string_list *macros = 0;
7474   string_list *register_assignments = 0;
7475   string_list *string_assignments = 0;
7476   int iflag = 0;
7477   int tflag = 0;
7478   int fflag = 0;
7479   int nflag = 0;
7480   int no_rc = 0;                // don't process troffrc and troffrc-end
7481   int next_page_number = 0;     // pacify compiler
7482   opterr = 0;
7483   hresolution = vresolution = 1;
7484   // restore $PATH if called from groff
7485   char* groff_path = getenv("GROFF_PATH__");
7486   if (groff_path) {
7487     string e = "PATH";
7488     e += '=';
7489     if (*groff_path)
7490       e += groff_path;
7491     e += '\0';
7492     if (putenv(strsave(e.contents())))
7493       fatal("putenv failed");
7494   }
7495   static const struct option long_options[] = {
7496     { "help", no_argument, 0, CHAR_MAX + 1 },
7497     { "version", no_argument, 0, 'v' },
7498     { 0, 0, 0, 0 }
7499   };
7500 #if defined(DEBUGGING)
7501 #define DEBUG_OPTION "D"
7502 #endif
7503   while ((c = getopt_long(argc, argv,
7504                           "abciI:vw:W:zCEf:m:n:o:r:d:F:M:T:tqs:RU"
7505                           DEBUG_OPTION, long_options, 0))
7506          != EOF)
7507     switch(c) {
7508     case 'v':
7509       {
7510         printf("GNU troff (groff) version %s\n", Version_string);
7511         exit(0);
7512         break;
7513       }
7514     case 'I':
7515       // Search path for .psbb files
7516       // and most other non-system input files.
7517       include_search_path.command_line_dir(optarg);
7518       break;
7519     case 'T':
7520       device = optarg;
7521       tflag = 1;
7522       is_html = (strcmp(device, "html") == 0);
7523       break;
7524     case 'C':
7525       compatible_flag = 1;
7526       // fall through
7527     case 'c':
7528       color_flag = 0;
7529       break;
7530     case 'M':
7531       macro_path.command_line_dir(optarg);
7532       safer_macro_path.command_line_dir(optarg);
7533       config_macro_path.command_line_dir(optarg);
7534       break;
7535     case 'F':
7536       font::command_line_font_dir(optarg);
7537       break;
7538     case 'm':
7539       add_string(optarg, &macros);
7540       break;
7541     case 'E':
7542       inhibit_errors = 1;
7543       break;
7544     case 'R':
7545       no_rc = 1;
7546       break;
7547     case 'w':
7548       enable_warning(optarg);
7549       break;
7550     case 'W':
7551       disable_warning(optarg);
7552       break;
7553     case 'i':
7554       iflag = 1;
7555       break;
7556     case 'b':
7557       backtrace_flag = 1;
7558       break;
7559     case 'a':
7560       ascii_output_flag = 1;
7561       break;
7562     case 'z':
7563       suppress_output_flag = 1;
7564       break;
7565     case 'n':
7566       if (sscanf(optarg, "%d", &next_page_number) == 1)
7567         nflag++;
7568       else
7569         error("bad page number");
7570       break;
7571     case 'o':
7572       parse_output_page_list(optarg);
7573       break;
7574     case 'd':
7575       if (*optarg == '\0')
7576         error("`-d' requires non-empty argument");
7577       else
7578         add_string(optarg, &string_assignments);
7579       break;
7580     case 'r':
7581       if (*optarg == '\0')
7582         error("`-r' requires non-empty argument");
7583       else
7584         add_string(optarg, &register_assignments);
7585       break;
7586     case 'f':
7587       default_family = symbol(optarg);
7588       fflag = 1;
7589       break;
7590     case 'q':
7591     case 's':
7592     case 't':
7593       // silently ignore these
7594       break;
7595     case 'U':
7596       unsafe_flag = 1;  // unsafe behaviour
7597       break;
7598 #if defined(DEBUGGING)
7599     case 'D':
7600       debug_state = 1;
7601       break;
7602 #endif
7603     case CHAR_MAX + 1: // --help
7604       usage(stdout, argv[0]);
7605       exit(0);
7606       break;
7607     case '?':
7608       usage(stderr, argv[0]);
7609       exit(1);
7610       break;            // never reached
7611     default:
7612       assert(0);
7613     }
7614   if (unsafe_flag)
7615     mac_path = &macro_path;
7616   set_string(".T", device);
7617   init_charset_table();
7618   init_hpf_code_table();
7619   if (!font::load_desc())
7620     fatal("sorry, I can't continue");
7621   units_per_inch = font::res;
7622   hresolution = font::hor;
7623   vresolution = font::vert;
7624   sizescale = font::sizescale;
7625   tcommand_flag = font::tcommand;
7626   warn_scale = (double)units_per_inch;
7627   warn_scaling_indicator = 'i';
7628   if (!fflag && font::family != 0 && *font::family != '\0')
7629     default_family = symbol(font::family);
7630   font_size::init_size_table(font::sizes);
7631   int i;
7632   int j = 1;
7633   if (font::style_table) {
7634     for (i = 0; font::style_table[i]; i++)
7635       mount_style(j++, symbol(font::style_table[i]));
7636   }
7637   for (i = 0; font::font_name_table[i]; i++, j++)
7638     // In the DESC file a font name of 0 (zero) means leave this
7639     // position empty.
7640     if (strcmp(font::font_name_table[i], "0") != 0)
7641       mount_font(j, symbol(font::font_name_table[i]));
7642   curdiv = topdiv = new top_level_diversion;
7643   if (nflag)
7644     topdiv->set_next_page_number(next_page_number);
7645   init_input_requests();
7646   init_env_requests();
7647   init_div_requests();
7648 #ifdef COLUMN
7649   init_column_requests();
7650 #endif /* COLUMN */
7651   init_node_requests();
7652   number_reg_dictionary.define(".T", new constant_reg(tflag ? "1" : "0"));
7653   init_registers();
7654   init_reg_requests();
7655   init_hyphen_requests();
7656   init_environments();
7657   while (string_assignments) {
7658     do_string_assignment(string_assignments->s);
7659     string_list *tem = string_assignments;
7660     string_assignments = string_assignments->next;
7661     delete tem;
7662   }
7663   while (register_assignments) {
7664     do_register_assignment(register_assignments->s);
7665     string_list *tem = register_assignments;
7666     register_assignments = register_assignments->next;
7667     delete tem;
7668   }
7669   if (!no_rc)
7670     process_startup_file(INITIAL_STARTUP_FILE);
7671   while (macros) {
7672     process_macro_file(macros->s);
7673     string_list *tem = macros;
7674     macros = macros->next;
7675     delete tem;
7676   }
7677   if (!no_rc)
7678     process_startup_file(FINAL_STARTUP_FILE);
7679   for (i = optind; i < argc; i++)
7680     process_input_file(argv[i]);
7681   if (optind >= argc || iflag)
7682     process_input_file("-");
7683   exit_troff();
7684   return 0;                     // not reached
7685 }
7686
7687 void warn_request()
7688 {
7689   int n;
7690   if (has_arg() && get_integer(&n)) {
7691     if (n & ~WARN_TOTAL) {
7692       warning(WARN_RANGE, "warning mask must be between 0 and %1", WARN_TOTAL);
7693       n &= WARN_TOTAL;
7694     }
7695     warning_mask = n;
7696   }
7697   else
7698     warning_mask = WARN_TOTAL;
7699   skip_line();
7700 }
7701
7702 static void init_registers()
7703 {
7704 #ifdef LONG_FOR_TIME_T
7705   long
7706 #else /* not LONG_FOR_TIME_T */
7707   time_t
7708 #endif /* not LONG_FOR_TIME_T */
7709     t = time(0);
7710   // Use struct here to work around misfeature in old versions of g++.
7711   struct tm *tt = localtime(&t);
7712   set_number_reg("seconds", int(tt->tm_sec));
7713   set_number_reg("minutes", int(tt->tm_min));
7714   set_number_reg("hours", int(tt->tm_hour));
7715   set_number_reg("dw", int(tt->tm_wday + 1));
7716   set_number_reg("dy", int(tt->tm_mday));
7717   set_number_reg("mo", int(tt->tm_mon + 1));
7718   set_number_reg("year", int(1900 + tt->tm_year));
7719   set_number_reg("yr", int(tt->tm_year));
7720   set_number_reg("$$", getpid());
7721   number_reg_dictionary.define(".A",
7722                                new constant_reg(ascii_output_flag
7723                                                 ? "1"
7724                                                 : "0"));
7725 }
7726
7727 /*
7728  *  registers associated with \O
7729  */
7730
7731 static int output_reg_minx_contents = -1;
7732 static int output_reg_miny_contents = -1;
7733 static int output_reg_maxx_contents = -1;
7734 static int output_reg_maxy_contents = -1;
7735
7736 void check_output_limits(int x, int y)
7737 {
7738   if ((output_reg_minx_contents == -1) || (x < output_reg_minx_contents))
7739     output_reg_minx_contents = x;
7740   if (x > output_reg_maxx_contents)
7741     output_reg_maxx_contents = x;
7742   if ((output_reg_miny_contents == -1) || (y < output_reg_miny_contents))
7743     output_reg_miny_contents = y;
7744   if (y > output_reg_maxy_contents)
7745     output_reg_maxy_contents = y;
7746 }
7747
7748 void reset_output_registers()
7749 {
7750   output_reg_minx_contents = -1;
7751   output_reg_miny_contents = -1;
7752   output_reg_maxx_contents = -1;
7753   output_reg_maxy_contents = -1;
7754 }
7755
7756 void get_output_registers(int *minx, int *miny, int *maxx, int *maxy)
7757 {
7758   *minx = output_reg_minx_contents;
7759   *miny = output_reg_miny_contents;
7760   *maxx = output_reg_maxx_contents;
7761   *maxy = output_reg_maxy_contents;
7762 }
7763
7764 void init_input_requests()
7765 {
7766   init_request("ab", abort_request);
7767   init_request("als", alias_macro);
7768   init_request("am", append_macro);
7769   init_request("am1", append_nocomp_macro);
7770   init_request("ami", append_indirect_macro);
7771   init_request("ami1", append_indirect_nocomp_macro);
7772   init_request("as", append_string);
7773   init_request("as1", append_nocomp_string);
7774   init_request("asciify", asciify_macro);
7775   init_request("backtrace", backtrace_request);
7776   init_request("blm", blank_line_macro);
7777   init_request("break", while_break_request);
7778   init_request("cf", copy_file);
7779   init_request("cflags", char_flags);
7780   init_request("char", define_character);
7781   init_request("chop", chop_macro);
7782   init_request("close", close_request);
7783   init_request("color", activate_color);
7784   init_request("composite", composite_request);
7785   init_request("continue", while_continue_request);
7786   init_request("cp", compatible);
7787   init_request("de", define_macro);
7788   init_request("de1", define_nocomp_macro);
7789   init_request("defcolor", define_color);
7790   init_request("dei", define_indirect_macro);
7791   init_request("dei1", define_indirect_nocomp_macro);
7792   init_request("device", device_request);
7793   init_request("devicem", device_macro_request);
7794   init_request("do", do_request);
7795   init_request("ds", define_string);
7796   init_request("ds1", define_nocomp_string);
7797   init_request("ec", set_escape_char);
7798   init_request("ecr", restore_escape_char);
7799   init_request("ecs", save_escape_char);
7800   init_request("el", else_request);
7801   init_request("em", end_macro);
7802   init_request("eo", escape_off);
7803   init_request("ex", exit_request);
7804   init_request("fchar", define_fallback_character);
7805 #ifdef WIDOW_CONTROL
7806   init_request("fpl", flush_pending_lines);
7807 #endif /* WIDOW_CONTROL */
7808   init_request("hcode", hyphenation_code);
7809   init_request("hpfcode", hyphenation_patterns_file_code);
7810   init_request("ie", if_else_request);
7811   init_request("if", if_request);
7812   init_request("ig", ignore);
7813   init_request("length", length_request);
7814   init_request("lf", line_file);
7815   init_request("mso", macro_source);
7816   init_request("nop", nop_request);
7817   init_request("nroff", nroff_request);
7818   init_request("nx", next_file);
7819   init_request("open", open_request);
7820   init_request("opena", opena_request);
7821   init_request("output", output_request);
7822   init_request("pc", set_page_character);
7823   init_request("pi", pipe_output);
7824   init_request("pm", print_macros);
7825   init_request("psbb", ps_bbox_request);
7826 #ifndef POPEN_MISSING
7827   init_request("pso", pipe_source);
7828 #endif /* not POPEN_MISSING */
7829   init_request("rchar", remove_character);
7830   init_request("rd", read_request);
7831   init_request("return", return_macro_request);
7832   init_request("rm", remove_macro);
7833   init_request("rn", rename_macro);
7834   init_request("schar", define_special_character);
7835   init_request("shift", shift);
7836   init_request("so", source);
7837   init_request("spreadwarn", spreadwarn_request);
7838   init_request("substring", substring_request);
7839   init_request("sy", system_request);
7840   init_request("tag", tag);
7841   init_request("taga", taga);
7842   init_request("tm", terminal);
7843   init_request("tm1", terminal1);
7844   init_request("tmc", terminal_continue);
7845   init_request("tr", translate);
7846   init_request("trf", transparent_file);
7847   init_request("trin", translate_input);
7848   init_request("trnt", translate_no_transparent);
7849   init_request("troff", troff_request);
7850   init_request("unformat", unformat_macro);
7851 #ifdef COLUMN
7852   init_request("vj", vjustify);
7853 #endif /* COLUMN */
7854   init_request("warn", warn_request);
7855   init_request("warnscale", warnscale_request);
7856   init_request("while", while_request);
7857   init_request("write", write_request);
7858   init_request("writec", write_request_continue);
7859   init_request("writem", write_macro_request);
7860   number_reg_dictionary.define(".$", new nargs_reg);
7861   number_reg_dictionary.define(".br", new break_flag_reg);
7862   number_reg_dictionary.define(".C", new constant_int_reg(&compatible_flag));
7863   number_reg_dictionary.define(".O", new variable_reg(&begin_level));
7864   number_reg_dictionary.define(".c", new lineno_reg);
7865   number_reg_dictionary.define(".color", new constant_int_reg(&color_flag));
7866   number_reg_dictionary.define(".F", new filename_reg);
7867   number_reg_dictionary.define(".g", new constant_reg("1"));
7868   number_reg_dictionary.define(".H", new constant_int_reg(&hresolution));
7869   number_reg_dictionary.define(".R", new constant_reg("10000"));
7870   number_reg_dictionary.define(".U", new constant_int_reg(&unsafe_flag));
7871   number_reg_dictionary.define(".V", new constant_int_reg(&vresolution));
7872   number_reg_dictionary.define(".warn", new constant_int_reg(&warning_mask));
7873   extern const char *major_version;
7874   number_reg_dictionary.define(".x", new constant_reg(major_version));
7875   extern const char *revision;
7876   number_reg_dictionary.define(".Y", new constant_reg(revision));
7877   extern const char *minor_version;
7878   number_reg_dictionary.define(".y", new constant_reg(minor_version));
7879   number_reg_dictionary.define("c.", new writable_lineno_reg);
7880   number_reg_dictionary.define("llx", new variable_reg(&llx_reg_contents));
7881   number_reg_dictionary.define("lly", new variable_reg(&lly_reg_contents));
7882   number_reg_dictionary.define("opmaxx",
7883                                new variable_reg(&output_reg_maxx_contents));
7884   number_reg_dictionary.define("opmaxy",
7885                                new variable_reg(&output_reg_maxy_contents));
7886   number_reg_dictionary.define("opminx",
7887                                new variable_reg(&output_reg_minx_contents));
7888   number_reg_dictionary.define("opminy",
7889                                new variable_reg(&output_reg_miny_contents));
7890   number_reg_dictionary.define("slimit",
7891                                new variable_reg(&input_stack::limit));
7892   number_reg_dictionary.define("systat", new variable_reg(&system_status));
7893   number_reg_dictionary.define("urx", new variable_reg(&urx_reg_contents));
7894   number_reg_dictionary.define("ury", new variable_reg(&ury_reg_contents));
7895 }
7896
7897 object_dictionary request_dictionary(501);
7898
7899 void init_request(const char *s, REQUEST_FUNCP f)
7900 {
7901   request_dictionary.define(s, new request(f));
7902 }
7903
7904 static request_or_macro *lookup_request(symbol nm)
7905 {
7906   assert(!nm.is_null());
7907   request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm);
7908   if (p == 0) {
7909     warning(WARN_MAC, "macro `%1' not defined", nm.contents());
7910     p = new macro;
7911     request_dictionary.define(nm, p);
7912   }
7913   return p;
7914 }
7915
7916 node *charinfo_to_node_list(charinfo *ci, const environment *envp)
7917 {
7918   // Don't interpret character definitions in compatible mode.
7919   int old_compatible_flag = compatible_flag;
7920   compatible_flag = 0;
7921   int old_escape_char = escape_char;
7922   escape_char = '\\';
7923   macro *mac = ci->set_macro(0);
7924   assert(mac != 0);
7925   environment *oldenv = curenv;
7926   environment env(envp);
7927   curenv = &env;
7928   curenv->set_composite();
7929   token old_tok = tok;
7930   input_stack::add_boundary();
7931   string_iterator *si =
7932     new string_iterator(*mac, "composite character", ci->nm);
7933   input_stack::push(si);
7934   // we don't use process_input_stack, because we don't want to recognise
7935   // requests
7936   for (;;) {
7937     tok.next();
7938     if (tok.eof())
7939       break;
7940     if (tok.newline()) {
7941       error("composite character mustn't contain newline");
7942       while (!tok.eof())
7943         tok.next();
7944       break;
7945     }
7946     else
7947       tok.process();
7948   }
7949   node *n = curenv->extract_output_line();
7950   input_stack::remove_boundary();
7951   ci->set_macro(mac);
7952   tok = old_tok;
7953   curenv = oldenv;
7954   compatible_flag = old_compatible_flag;
7955   escape_char = old_escape_char;
7956   have_input = 0;
7957   return n;
7958 }
7959
7960 static node *read_draw_node()
7961 {
7962   token start;
7963   start.next();
7964   if (!start.delimiter(1)){
7965     do {
7966       tok.next();
7967     } while (tok != start && !tok.newline() && !tok.eof());
7968   }
7969   else {
7970     tok.next();
7971     if (tok == start)
7972       error("missing argument");
7973     else {
7974       unsigned char type = tok.ch();
7975       if (type == 'F') {
7976         read_color_draw_node(start);
7977         return 0;
7978       }
7979       tok.next();
7980       int maxpoints = 10;
7981       hvpair *point = new hvpair[maxpoints];
7982       int npoints = 0;
7983       int no_last_v = 0;
7984       int err = 0;
7985       int i;
7986       for (i = 0; tok != start; i++) {
7987         if (i == maxpoints) {
7988           hvpair *oldpoint = point;
7989           point = new hvpair[maxpoints*2];
7990           for (int j = 0; j < maxpoints; j++)
7991             point[j] = oldpoint[j];
7992           maxpoints *= 2;
7993           a_delete oldpoint;
7994         }
7995         if (!get_hunits(&point[i].h,
7996                         type == 'f' || type == 't' ? 'u' : 'm')) {
7997           err = 1;
7998           break;
7999         }
8000         ++npoints;
8001         tok.skip();
8002         point[i].v = V0;
8003         if (tok == start) {
8004           no_last_v = 1;
8005           break;
8006         }
8007         if (!get_vunits(&point[i].v, 'v')) {
8008           err = 1;
8009           break;
8010         }
8011         tok.skip();
8012       }
8013       while (tok != start && !tok.newline() && !tok.eof())
8014         tok.next();
8015       if (!err) {
8016         switch (type) {
8017         case 'l':
8018           if (npoints != 1 || no_last_v) {
8019             error("two arguments needed for line");
8020             npoints = 1;
8021           }
8022           break;
8023         case 'c':
8024           if (npoints != 1 || !no_last_v) {
8025             error("one argument needed for circle");
8026             npoints = 1;
8027             point[0].v = V0;
8028           }
8029           break;
8030         case 'e':
8031           if (npoints != 1 || no_last_v) {
8032             error("two arguments needed for ellipse");
8033             npoints = 1;
8034           }
8035           break;
8036         case 'a':
8037           if (npoints != 2 || no_last_v) {
8038             error("four arguments needed for arc");
8039             npoints = 2;
8040           }
8041           break;
8042         case '~':
8043           if (no_last_v)
8044             error("even number of arguments needed for spline");
8045           break;
8046         case 'f':
8047           if (npoints != 1 || !no_last_v) {
8048             error("one argument needed for gray shade");
8049             npoints = 1;
8050             point[0].v = V0;
8051           }
8052         default:
8053           // silently pass it through
8054           break;
8055         }
8056         draw_node *dn = new draw_node(type, point, npoints,
8057                                       curenv->get_font_size(),
8058                                       curenv->get_glyph_color(),
8059                                       curenv->get_fill_color());
8060         a_delete point;
8061         return dn;
8062       }
8063       else {
8064         a_delete point;
8065       }
8066     }
8067   }
8068   return 0;
8069 }
8070
8071 static void read_color_draw_node(token &start)
8072 {
8073   tok.next();
8074   if (tok == start) {
8075     error("missing color scheme");
8076     return;
8077   }
8078   unsigned char scheme = tok.ch();
8079   tok.next();
8080   color *col = 0;
8081   char end = start.ch();
8082   switch (scheme) {
8083   case 'c':
8084     col = read_cmy(end);
8085     break;
8086   case 'd':
8087     col = &default_color;
8088     break;
8089   case 'g':
8090     col = read_gray(end);
8091     break;
8092   case 'k':
8093     col = read_cmyk(end);
8094     break;
8095   case 'r':
8096     col = read_rgb(end);
8097     break;
8098   }
8099   if (col)
8100     curenv->set_fill_color(col);
8101   while (tok != start) {
8102     if (tok.newline() || tok.eof()) {
8103       warning(WARN_DELIM, "missing closing delimiter");
8104       input_stack::push(make_temp_iterator("\n"));
8105       break;
8106     }
8107     tok.next();
8108   }
8109   have_input = 1;
8110 }
8111
8112 static struct {
8113   const char *name;
8114   int mask;
8115 } warning_table[] = {
8116   { "char", WARN_CHAR },
8117   { "range", WARN_RANGE },
8118   { "break", WARN_BREAK },
8119   { "delim", WARN_DELIM },
8120   { "el", WARN_EL },
8121   { "scale", WARN_SCALE },
8122   { "number", WARN_NUMBER },
8123   { "syntax", WARN_SYNTAX },
8124   { "tab", WARN_TAB },
8125   { "right-brace", WARN_RIGHT_BRACE },
8126   { "missing", WARN_MISSING },
8127   { "input", WARN_INPUT },
8128   { "escape", WARN_ESCAPE },
8129   { "space", WARN_SPACE },
8130   { "font", WARN_FONT },
8131   { "di", WARN_DI },
8132   { "mac", WARN_MAC },
8133   { "reg", WARN_REG },
8134   { "ig", WARN_IG },
8135   { "color", WARN_COLOR },
8136   { "all", WARN_TOTAL & ~(WARN_DI | WARN_MAC | WARN_REG) },
8137   { "w", WARN_TOTAL },
8138   { "default", DEFAULT_WARNING_MASK },
8139 };
8140
8141 static int lookup_warning(const char *name)
8142 {
8143   for (unsigned int i = 0;
8144        i < sizeof(warning_table)/sizeof(warning_table[0]);
8145        i++)
8146     if (strcmp(name, warning_table[i].name) == 0)
8147       return warning_table[i].mask;
8148   return 0;
8149 }
8150
8151 static void enable_warning(const char *name)
8152 {
8153   int mask = lookup_warning(name);
8154   if (mask)
8155     warning_mask |= mask;
8156   else
8157     error("unknown warning `%1'", name);
8158 }
8159
8160 static void disable_warning(const char *name)
8161 {
8162   int mask = lookup_warning(name);
8163   if (mask)
8164     warning_mask &= ~mask;
8165   else
8166     error("unknown warning `%1'", name);
8167 }
8168
8169 static void copy_mode_error(const char *format,
8170                             const errarg &arg1,
8171                             const errarg &arg2,
8172                             const errarg &arg3)
8173 {
8174   if (ignoring) {
8175     static const char prefix[] = "(in ignored input) ";
8176     char *s = new char[sizeof(prefix) + strlen(format)];
8177     strcpy(s, prefix);
8178     strcat(s, format);
8179     warning(WARN_IG, s, arg1, arg2, arg3);
8180     a_delete s;
8181   }
8182   else
8183     error(format, arg1, arg2, arg3);
8184 }
8185
8186 enum error_type { WARNING, OUTPUT_WARNING, ERROR, FATAL };
8187
8188 static void do_error(error_type type,
8189                      const char *format,
8190                      const errarg &arg1,
8191                      const errarg &arg2,
8192                      const errarg &arg3)
8193 {
8194   const char *filename;
8195   int lineno;
8196   if (inhibit_errors && type < FATAL)
8197     return;
8198   if (backtrace_flag)
8199     input_stack::backtrace();
8200   if (!get_file_line(&filename, &lineno))
8201     filename = 0;
8202   if (filename)
8203     errprint("%1:%2: ", filename, lineno);
8204   else if (program_name)
8205     fprintf(stderr, "%s: ", program_name);
8206   switch (type) {
8207   case FATAL:
8208     fputs("fatal error: ", stderr);
8209     break;
8210   case ERROR:
8211     break;
8212   case WARNING:
8213     fputs("warning: ", stderr);
8214     break;
8215   case OUTPUT_WARNING:
8216     double fromtop = topdiv->get_vertical_position().to_units() / warn_scale;
8217     fprintf(stderr, "warning [p %d, %.1f%c",
8218             topdiv->get_page_number(), fromtop, warn_scaling_indicator);
8219     if (topdiv != curdiv) {
8220       double fromtop1 = curdiv->get_vertical_position().to_units()
8221                         / warn_scale;
8222       fprintf(stderr, ", div `%s', %.1f%c",
8223               curdiv->get_diversion_name(), fromtop1, warn_scaling_indicator);
8224     }
8225     fprintf(stderr, "]: ");
8226     break;
8227   }
8228   errprint(format, arg1, arg2, arg3);
8229   fputc('\n', stderr);
8230   fflush(stderr);
8231   if (type == FATAL)
8232     cleanup_and_exit(1);
8233 }
8234
8235 int warning(warning_type t,
8236             const char *format,
8237             const errarg &arg1,
8238             const errarg &arg2,
8239             const errarg &arg3)
8240 {
8241   if ((t & warning_mask) != 0) {
8242     do_error(WARNING, format, arg1, arg2, arg3);
8243     return 1;
8244   }
8245   else
8246     return 0;
8247 }
8248
8249 int output_warning(warning_type t,
8250                    const char *format,
8251                    const errarg &arg1,
8252                    const errarg &arg2,
8253                    const errarg &arg3)
8254 {
8255   if ((t & warning_mask) != 0) {
8256     do_error(OUTPUT_WARNING, format, arg1, arg2, arg3);
8257     return 1;
8258   }
8259   else
8260     return 0;
8261 }
8262
8263 void error(const char *format,
8264            const errarg &arg1,
8265            const errarg &arg2,
8266            const errarg &arg3)
8267 {
8268   do_error(ERROR, format, arg1, arg2, arg3);
8269 }
8270
8271 void fatal(const char *format,
8272            const errarg &arg1,
8273            const errarg &arg2,
8274            const errarg &arg3)
8275 {
8276   do_error(FATAL, format, arg1, arg2, arg3);
8277 }
8278
8279 void fatal_with_file_and_line(const char *filename, int lineno,
8280                               const char *format,
8281                               const errarg &arg1,
8282                               const errarg &arg2,
8283                               const errarg &arg3)
8284 {
8285   fprintf(stderr, "%s:%d: fatal error: ", filename, lineno);
8286   errprint(format, arg1, arg2, arg3);
8287   fputc('\n', stderr);
8288   fflush(stderr);
8289   cleanup_and_exit(1);
8290 }
8291
8292 void error_with_file_and_line(const char *filename, int lineno,
8293                               const char *format,
8294                               const errarg &arg1,
8295                               const errarg &arg2,
8296                               const errarg &arg3)
8297 {
8298   fprintf(stderr, "%s:%d: error: ", filename, lineno);
8299   errprint(format, arg1, arg2, arg3);
8300   fputc('\n', stderr);
8301   fflush(stderr);
8302 }
8303
8304 dictionary charinfo_dictionary(501);
8305
8306 charinfo *get_charinfo(symbol nm)
8307 {
8308   void *p = charinfo_dictionary.lookup(nm);
8309   if (p != 0)
8310     return (charinfo *)p;
8311   charinfo *cp = new charinfo(nm);
8312   (void)charinfo_dictionary.lookup(nm, cp);
8313   return cp;
8314 }
8315
8316 int charinfo::next_index = 0;
8317
8318 charinfo::charinfo(symbol s)
8319 : translation(0), mac(0), special_translation(TRANSLATE_NONE),
8320   hyphenation_code(0), flags(0), ascii_code(0), asciify_code(0),
8321   not_found(0), transparent_translate(1), translate_input(0),
8322   mode(CHAR_NORMAL), nm(s)
8323 {
8324   index = next_index++;
8325   number = -1;
8326 }
8327
8328 void charinfo::set_hyphenation_code(unsigned char c)
8329 {
8330   hyphenation_code = c;
8331 }
8332
8333 void charinfo::set_translation(charinfo *ci, int tt, int ti)
8334 {
8335   translation = ci;
8336   if (ci && ti) {
8337     if (hyphenation_code != 0)
8338       ci->set_hyphenation_code(hyphenation_code);
8339     if (asciify_code != 0)
8340       ci->set_asciify_code(asciify_code);
8341     else if (ascii_code != 0)
8342       ci->set_asciify_code(ascii_code);
8343     ci->set_translation_input();
8344   }
8345   special_translation = TRANSLATE_NONE;
8346   transparent_translate = tt;
8347 }
8348
8349 void charinfo::set_special_translation(int c, int tt)
8350 {
8351   special_translation = c;
8352   translation = 0;
8353   transparent_translate = tt;
8354 }
8355
8356 void charinfo::set_ascii_code(unsigned char c)
8357 {
8358   ascii_code = c;
8359 }
8360
8361 void charinfo::set_asciify_code(unsigned char c)
8362 {
8363   asciify_code = c;
8364 }
8365
8366 macro *charinfo::set_macro(macro *m)
8367 {
8368   macro *tem = mac;
8369   mac = m;
8370   return tem;
8371 }
8372
8373 macro *charinfo::setx_macro(macro *m, char_mode cm)
8374 {
8375   macro *tem = mac;
8376   mac = m;
8377   mode = cm;
8378   return tem;
8379 }
8380
8381 void charinfo::set_number(int n)
8382 {
8383   assert(n >= 0);
8384   number = n;
8385 }
8386
8387 int charinfo::get_number()
8388 {
8389   assert(number >= 0);
8390   return number;
8391 }
8392
8393 symbol UNNAMED_SYMBOL("---");
8394
8395 // For numbered characters not between 0 and 255, we make a symbol out
8396 // of the number and store them in this dictionary.
8397
8398 dictionary numbered_charinfo_dictionary(11);
8399
8400 charinfo *get_charinfo_by_number(int n)
8401 {
8402   static charinfo *number_table[256];
8403
8404   if (n >= 0 && n < 256) {
8405     charinfo *ci = number_table[n];
8406     if (!ci) {
8407       ci = new charinfo(UNNAMED_SYMBOL);
8408       ci->set_number(n);
8409       number_table[n] = ci;
8410     }
8411     return ci;
8412   }
8413   else {
8414     symbol ns(i_to_a(n));
8415     charinfo *ci = (charinfo *)numbered_charinfo_dictionary.lookup(ns);
8416     if (!ci) {
8417       ci = new charinfo(UNNAMED_SYMBOL);
8418       ci->set_number(n);
8419       (void)numbered_charinfo_dictionary.lookup(ns, ci);
8420     }
8421     return ci;
8422   }
8423 }
8424
8425 // This overrides the same function from libgroff; while reading font
8426 // definition files it puts single-letter glyph names into `charset_table'
8427 // and converts glyph names of the form `\x' (`x' a single letter) into `x'. 
8428 // Consequently, symbol("x") refers to glyph name `\x', not `x'.
8429
8430 glyph *name_to_glyph(const char *nm)
8431 {
8432   charinfo *ci;
8433   if (nm[1] == 0)
8434     ci = charset_table[nm[0] & 0xff];
8435   else if (nm[0] == '\\' && nm[2] == 0)
8436     ci = get_charinfo(symbol(nm + 1));
8437   else
8438     ci = get_charinfo(symbol(nm));
8439   return ci->as_glyph();
8440 }
8441
8442 glyph *number_to_glyph(int n)
8443 {
8444   return get_charinfo_by_number(n)->as_glyph();
8445 }
8446
8447 const char *glyph_to_name(glyph *g)
8448 {
8449   charinfo *ci = (charinfo *)g; // Every glyph is actually a charinfo.
8450   return (ci->nm != UNNAMED_SYMBOL ? ci->nm.contents() : NULL);
8451 }