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