xref: /386bsd/usr/src/usr.bin/groff/troff/input.cc (revision a2142627)
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3      Written by James Clark (jjc@jclark.com)
4 
5 This file is part of groff.
6 
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11 
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16 
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING.  If not, write to the Free Software
19 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
20 
21 #include "troff.h"
22 #include "symbol.h"
23 #include "dictionary.h"
24 #include "hvunits.h"
25 #include "env.h"
26 #include "request.h"
27 #include "node.h"
28 #include "reg.h"
29 #include "token.h"
30 #include "div.h"
31 #include "charinfo.h"
32 #include "font.h"
33 #include "searchpath.h"
34 #include "macropath.h"
35 
36 // Needed for getpid().
37 #include "posix.h"
38 
39 #define USAGE_EXIT_CODE 1
40 #define MACRO_PREFIX "tmac."
41 #define STARTUP_FILE "troffrc"
42 #define DEFAULT_INPUT_STACK_LIMIT 1000
43 
44 #ifndef DEFAULT_WARNING_MASK
45 // warnings that are enabled by default
46 #define DEFAULT_WARNING_MASK \
47      (WARN_CHAR|WARN_NUMBER|WARN_BREAK|WARN_SPACE|WARN_FONT)
48 #endif
49 
50 // initial size of buffer for reading names; expanded as necessary
51 #define ABUF_SIZE 16
52 
53 #ifdef COLUMN
54 void init_column_requests();
55 #endif /* COLUMN */
56 
57 static node *read_draw_node();
58 void handle_first_page_transition();
59 static void push_token(const token &);
60 void copy_file();
61 #ifdef COLUMN
62 void vjustify();
63 #endif /* COLUMN */
64 void transparent();
65 void transparent_file();
66 
67 const char *program_name = 0;
68 token tok;
69 int break_flag = 0;
70 static int backtrace_flag = 0;
71 char *pipe_command = 0;
72 charinfo *charset_table[256];
73 
74 static int warning_mask = DEFAULT_WARNING_MASK;
75 static int inhibit_errors = 0;
76 
77 static void enable_warning(const char *);
78 static void disable_warning(const char *);
79 
80 static int escape_char = '\\';
81 static symbol end_macro_name;
82 static int compatible_flag = 0;
83 static void process_input_stack();
84 int ascii_output_flag = 0;
85 int suppress_output_flag = 0;
86 
87 int tcommand_flag = 0;
88 
89 static int get_copy(node**, int = 0);
90 static symbol read_escape_name();
91 static void interpolate_string(symbol);
92 static void interpolate_macro(symbol);
93 static void interpolate_number_format(symbol);
94 static void interpolate_environment_variable(symbol);
95 
96 static void interpolate_arg(symbol);
97 static request_or_macro *lookup_request(symbol);
98 static int get_delim_number(units *, int);
99 static int get_delim_number(units *, int, units);
100 static int get_line_arg(units *res, int si, charinfo **cp);
101 static int read_size(int *);
102 static symbol get_delim_name();
103 static void init_registers();
104 
105 struct input_iterator;
106 input_iterator *make_temp_iterator(const char *);
107 const char *input_char_description(int);
108 
109 const int ESCAPE_QUESTION = 015;
110 const int BEGIN_TRAP = 016;
111 const int END_TRAP = 017;
112 const int PAGE_EJECTOR = 020;
113 const int ESCAPE_NEWLINE = 021;
114 const int ESCAPE_AMPERSAND = 022;
115 const int ESCAPE_UNDERSCORE = 023;
116 const int ESCAPE_BAR = 024;
117 const int ESCAPE_CIRCUMFLEX = 025;
118 const int ESCAPE_LEFT_BRACE = 026;
119 const int ESCAPE_RIGHT_BRACE = 027;
120 const int ESCAPE_LEFT_QUOTE = 030;
121 const int ESCAPE_RIGHT_QUOTE = 031;
122 const int ESCAPE_HYPHEN = 032;
123 const int ESCAPE_BANG = 033;
124 const int ESCAPE_c = 034;
125 const int ESCAPE_e = 035;
126 const int ESCAPE_PERCENT = 036;
127 const int ESCAPE_SPACE = 037;
128 
129 const int TITLE_REQUEST = 0200;
130 const int COPY_FILE_REQUEST = 0201;
131 const int TRANSPARENT_FILE_REQUEST = 0202;
132 #ifdef COLUMN
133 const int VJUSTIFY_REQUEST = 0203;
134 #endif /* COLUMN */
135 const int ESCAPE_E = 0204;
136 const int LAST_PAGE_EJECTOR = 0205;
137 const int ESCAPE_RIGHT_PARENTHESIS = 0206;
138 
set_escape_char()139 void set_escape_char()
140 {
141   if (has_arg()) {
142     if (tok.ch() == 0) {
143       error("bad escape character");
144       escape_char = '\\';
145     }
146     else
147       escape_char = tok.ch();
148   }
149   else
150     escape_char = '\\';
151   skip_line();
152 }
153 
escape_off()154 void escape_off()
155 {
156   escape_char = 0;
157   skip_line();
158 }
159 
160 class input_iterator {
161 public:
162   input_iterator();
163   virtual ~input_iterator();
164   int get(node **);
165   friend class input_stack;
166 protected:
167   const unsigned char *ptr;
168   const unsigned char *eptr;
169   input_iterator *next;
170 private:
171   virtual int fill(node **);
172   virtual int peek();
has_args()173   virtual int has_args() { return 0; }
nargs()174   virtual int nargs() { return 0; }
get_arg(int)175   virtual input_iterator *get_arg(int) { return NULL; }
get_location(int,const char **,int *)176   virtual int get_location(int, const char **,  int *)
177     { return 0; }
backtrace()178   virtual void backtrace() {}
set_location(const char *,int)179   virtual int set_location(const char *, int)
180     { return 0; }
next_file(FILE *,const char *)181   virtual int next_file(FILE *, const char *) { return 0; }
shift(int)182   virtual void shift(int) {}
183   virtual int is_boundary();
internal_level()184   virtual int internal_level() { return 0; }
is_file()185   virtual int is_file() { return 0; }
186 };
187 
input_iterator()188 input_iterator::input_iterator()
189 : ptr(0), eptr(0)
190 {
191 }
192 
~input_iterator()193 input_iterator::~input_iterator()
194 {
195 }
196 
fill(node **)197 int input_iterator::fill(node **)
198 {
199   return EOF;
200 }
201 
peek()202 int input_iterator::peek()
203 {
204   return EOF;
205 }
206 
is_boundary()207 int input_iterator::is_boundary()
208 {
209   return 0;
210 }
211 
get(node ** p)212 inline int input_iterator::get(node **p)
213 {
214   return ptr < eptr ? *ptr++ : fill(p);
215 }
216 
217 
218 class input_boundary : public input_iterator {
219 public:
is_boundary()220   int is_boundary() { return 1; }
221 };
222 
223 class file_iterator : public input_iterator {
224   FILE *fp;
225   int lineno;
226   const char *filename;
227   int newline_flag;
228   enum { BUF_SIZE = 512 };
229   unsigned char buf[BUF_SIZE];
230 public:
231   file_iterator(FILE *, const char *);
232   ~file_iterator();
233   int fill(node **);
234   int peek();
235   int get_location(int, const char **, int *);
236   void backtrace();
237   int set_location(const char *, int);
238   int next_file(FILE *, const char *);
239   int is_file();
240 };
241 
file_iterator(FILE * f,const char * fn)242 file_iterator::file_iterator(FILE *f, const char *fn)
243 : fp(f), filename(fn), lineno(1), newline_flag(0)
244 {
245 }
246 
~file_iterator()247 file_iterator::~file_iterator()
248 {
249   if (fp != stdin)
250     fclose(fp);
251   else
252     clearerr(stdin);
253 }
254 
is_file()255 int file_iterator::is_file()
256 {
257   return 1;
258 }
259 
next_file(FILE * f,const char * s)260 int file_iterator::next_file(FILE *f, const char *s)
261 {
262   if (fp != stdin)
263     fclose(fp);
264   else
265     clearerr(stdin);
266   filename = s;
267   fp = f;
268   lineno = 1;
269   newline_flag = 0;
270   ptr = 0;
271   eptr = 0;
272   return 1;
273 }
274 
fill(node **)275 int file_iterator::fill(node **)
276 {
277   if (newline_flag)
278     lineno++;
279   newline_flag = 0;
280   unsigned char *p = buf;
281   ptr = p;
282   unsigned char *e = p + BUF_SIZE;
283   while (p < e) {
284     int c = getc(fp);
285     if (c == EOF)
286       break;
287     if (illegal_input_char(c))
288       warning(WARN_INPUT, "illegal input character code %1", int(c));
289     else {
290       *p++ = c;
291       if (c == '\n') {
292 	newline_flag = 1;
293 	break;
294       }
295     }
296   }
297   if (p > buf) {
298     eptr = p;
299     return *ptr++;
300   }
301   else {
302     eptr = p;
303     return EOF;
304   }
305 }
306 
peek()307 int file_iterator::peek()
308 {
309   int c = getc(fp);
310   while (illegal_input_char(c)) {
311     warning(WARN_INPUT, "illegal input character code %1", int(c));
312     c = getc(fp);
313   }
314   if (c != EOF)
315     ungetc(c, fp);
316   return c;
317 }
318 
get_location(int,const char ** filenamep,int * linenop)319 int file_iterator::get_location(int /*allow_macro*/,
320 				const char **filenamep, int *linenop)
321 {
322   *linenop = lineno;
323   if (filename != 0 && strcmp(filename, "-") == 0)
324     *filenamep = "<standard input>";
325   else
326     *filenamep = filename;
327   return 1;
328 }
329 
backtrace()330 void file_iterator::backtrace()
331 {
332   errprint("%1:%2: backtrace: file `%1'\n", filename, lineno);
333 }
334 
set_location(const char * f,int ln)335 int file_iterator::set_location(const char *f, int ln)
336 {
337   if (f)
338     filename = f;
339   lineno = ln;
340   return 1;
341 }
342 
343 input_iterator nil_iterator;
344 
345 class input_stack {
346 public:
347   static int get(node **);
348   static int peek();
349   static void push(input_iterator *);
350   static input_iterator *get_arg(int);
351   static int nargs();
352   static int get_location(int, const char **, int *);
353   static int set_location(const char *, int);
354   static void backtrace();
355   static void backtrace_all();
356   static void next_file(FILE *, const char *);
357   static void end_file();
358   static void shift(int n);
359   static void add_boundary();
360   static void remove_boundary();
361   static int get_level();
362   static void clear();
363 
364   static int limit;
365 private:
366   static input_iterator *top;
367   static int level;
368 
369   static int finish_get(node **);
370   static int finish_peek();
371 };
372 
373 input_iterator *input_stack::top = &nil_iterator;
374 int input_stack::level = 0;
375 int input_stack::limit = DEFAULT_INPUT_STACK_LIMIT;
376 
get_level()377 inline int input_stack::get_level()
378 {
379   return level + top->internal_level();
380 }
381 
get(node ** np)382 inline int input_stack::get(node **np)
383 {
384   return (top->ptr < top->eptr) ? *top->ptr++ : finish_get(np);
385 }
386 
finish_get(node ** np)387 int input_stack::finish_get(node **np)
388 {
389   for (;;) {
390     int c = top->fill(np);
391     if (c != EOF || top->is_boundary())
392       return c;
393     if (top == &nil_iterator)
394       break;
395     input_iterator *tem = top;
396     top = top->next;
397     level--;
398     delete tem;
399     if (top->ptr < top->eptr)
400       return *top->ptr++;
401   }
402   assert(level == 0);
403   return EOF;
404 }
405 
peek()406 inline int input_stack::peek()
407 {
408   return (top->ptr < top->eptr) ? *top->ptr : finish_peek();
409 }
410 
finish_peek()411 int input_stack::finish_peek()
412 {
413   for (;;) {
414     int c = top->peek();
415     if (c != EOF || top->is_boundary())
416       return c;
417     if (top == &nil_iterator)
418       break;
419     input_iterator *tem = top;
420     top = top->next;
421     level--;
422     delete tem;
423     if (top->ptr < top->eptr)
424       return *top->ptr;
425   }
426   assert(level == 0);
427   return EOF;
428 }
429 
add_boundary()430 void input_stack::add_boundary()
431 {
432   push(new input_boundary);
433 }
434 
remove_boundary()435 void input_stack::remove_boundary()
436 {
437   assert(top->is_boundary());
438   input_iterator *temp = top->next;
439   delete top;
440   top = temp;
441   level--;
442 }
443 
push(input_iterator * in)444 void input_stack::push(input_iterator *in)
445 {
446   if (in == 0)
447     return;
448   if (++level > limit && limit > 0)
449     fatal("input stack limit exceeded (probable infinite loop)");
450   in->next = top;
451   top = in;
452 }
453 
get_arg(int i)454 input_iterator *input_stack::get_arg(int i)
455 {
456   input_iterator *p;
457   for (p = top; p != NULL; p = p->next)
458     if (p->has_args())
459       return p->get_arg(i);
460   return 0;
461 }
462 
shift(int n)463 void input_stack::shift(int n)
464 {
465   for (input_iterator *p = top; p; p = p->next)
466     if (p->has_args()) {
467       p->shift(n);
468       return;
469     }
470 }
471 
nargs()472 int input_stack::nargs()
473 {
474   for (input_iterator *p =top; p != 0; p = p->next)
475     if (p->has_args())
476       return p->nargs();
477   return 0;
478 }
479 
get_location(int allow_macro,const char ** filenamep,int * linenop)480 int input_stack::get_location(int allow_macro, const char **filenamep, int *linenop)
481 {
482   for (input_iterator *p = top; p; p = p->next)
483     if (p->get_location(allow_macro, filenamep, linenop))
484       return 1;
485   return 0;
486 }
487 
backtrace()488 void input_stack::backtrace()
489 {
490   const char *f;
491   int n;
492   // only backtrace down to (not including) the topmost file
493   for (input_iterator *p = top;
494        p && !p->get_location(0, &f, &n);
495        p = p->next)
496     p->backtrace();
497 }
498 
backtrace_all()499 void input_stack::backtrace_all()
500 {
501   for (input_iterator *p = top; p; p = p->next)
502     p->backtrace();
503 }
504 
set_location(const char * filename,int lineno)505 int input_stack::set_location(const char *filename, int lineno)
506 {
507   for (input_iterator *p = top; p; p = p->next)
508     if (p->set_location(filename, lineno))
509       return 1;
510   return 0;
511 }
512 
next_file(FILE * fp,const char * s)513 void input_stack::next_file(FILE *fp, const char *s)
514 {
515   for (input_iterator **pp = &top; *pp != &nil_iterator; pp = &(*pp)->next)
516     if ((*pp)->next_file(fp, s))
517       return;
518   if (++level > limit && limit > 0)
519     fatal("input stack limit exceeded");
520   *pp = new file_iterator(fp, s);
521   (*pp)->next = &nil_iterator;
522 }
523 
end_file()524 void input_stack::end_file()
525 {
526   for (input_iterator **pp = &top; *pp != &nil_iterator; pp = &(*pp)->next)
527     if ((*pp)->is_file()) {
528       input_iterator *tem = *pp;
529       *pp = (*pp)->next;
530       delete tem;
531       level--;
532       return;
533     }
534 }
535 
clear()536 void input_stack::clear()
537 {
538   int nboundaries = 0;
539   while (top != &nil_iterator) {
540     if (top->is_boundary())
541       nboundaries++;
542     input_iterator *tem = top;
543     top = top->next;
544     level--;
545     delete tem;
546   }
547   // Keep while_request happy.
548   for (; nboundaries > 0; --nboundaries)
549     add_boundary();
550 }
551 
backtrace_request()552 void backtrace_request()
553 {
554   input_stack::backtrace_all();
555   fflush(stderr);
556   skip_line();
557 }
558 
next_file()559 void next_file()
560 {
561   symbol nm = get_long_name(0);
562   while (!tok.newline() && !tok.eof())
563     tok.next();
564   if (nm.is_null())
565     input_stack::end_file();
566   else {
567     errno = 0;
568     FILE *fp = fopen(nm.contents(), "r");
569     if (!fp)
570       error("can't open `%1': %2", nm.contents(), strerror(errno));
571     else
572       input_stack::next_file(fp, nm.contents());
573   }
574   tok.next();
575 }
576 
shift()577 void shift()
578 {
579   int n;
580   if (!has_arg() || !get_integer(&n))
581     n = 1;
582   input_stack::shift(n);
583   skip_line();
584 }
585 
get_char_for_escape_name()586 static int get_char_for_escape_name()
587 {
588   int c = get_copy(NULL);
589   switch (c) {
590   case EOF:
591     error("end of input in escape name");
592     return '\0';
593   default:
594     if (!illegal_input_char(c))
595       break;
596     // fall through
597   case ' ':
598   case '\n':
599   case '\t':
600   case '\001':
601   case '\b':
602     error("%1 is not allowed in an escape name", input_char_description(c));
603     return '\0';
604   }
605   return c;
606 }
607 
read_two_char_escape_name()608 static symbol read_two_char_escape_name()
609 {
610   char buf[3];
611   buf[0] = get_char_for_escape_name();
612   if (buf[0] != '\0') {
613     buf[1] = get_char_for_escape_name();
614     if (buf[1] == '\0')
615       buf[0] = 0;
616     else
617       buf[2] = 0;
618   }
619   return symbol(buf);
620 }
621 
read_long_escape_name()622 static symbol read_long_escape_name()
623 {
624   int start_level = input_stack::get_level();
625   char abuf[ABUF_SIZE];
626   char *buf = abuf;
627   int buf_size = ABUF_SIZE;
628   int i = 0;
629   for (;;) {
630     int c = get_char_for_escape_name();
631     if (c == 0) {
632       if (buf != abuf)
633 	a_delete buf;
634       return NULL_SYMBOL;
635     }
636     if (i + 2 > buf_size) {
637       if (buf == abuf) {
638 	buf = new char [ABUF_SIZE*2];
639 	memcpy(buf, abuf, buf_size);
640 	buf_size = ABUF_SIZE*2;
641       }
642       else {
643 	char *old_buf = buf;
644 	buf = new char[buf_size*2];
645 	memcpy(buf, old_buf, buf_size);
646 	buf_size *= 2;
647 	a_delete old_buf;
648       }
649     }
650     if (c == ']' && input_stack::get_level() == start_level)
651       break;
652     buf[i++] = c;
653   }
654   buf[i] = 0;
655   if (buf == abuf) {
656     if (i == 0) {
657       error("empty escape name");
658       return NULL_SYMBOL;
659     }
660     return symbol(abuf);
661   }
662   else {
663     symbol s(buf);
664     a_delete buf;
665     return s;
666   }
667 }
668 
read_escape_name()669 static symbol read_escape_name()
670 {
671   int c = get_char_for_escape_name();
672   if (c == 0)
673     return NULL_SYMBOL;
674   if (c == '(')
675     return read_two_char_escape_name();
676   if (c == '[' && !compatible_flag)
677     return read_long_escape_name();
678   char buf[2];
679   buf[0] = c;
680   buf[1] = '\0';
681   return symbol(buf);
682 }
683 
read_increment_and_escape_name(int * incp)684 static symbol read_increment_and_escape_name(int *incp)
685 {
686   int c = get_char_for_escape_name();
687   switch (c) {
688   case 0:
689     *incp = 0;
690     return NULL_SYMBOL;
691   case '(':
692     *incp = 0;
693     return read_two_char_escape_name();
694   case '+':
695     *incp = 1;
696     return read_escape_name();
697   case '-':
698     *incp = -1;
699     return read_escape_name();
700   case '[':
701     if (!compatible_flag) {
702       *incp = 0;
703       return read_long_escape_name();
704     }
705     break;
706   }
707   *incp = 0;
708   char buf[2];
709   buf[0] = c;
710   buf[1] = '\0';
711   return symbol(buf);
712 }
713 
get_copy(node ** nd,int defining)714 static int get_copy(node **nd, int defining)
715 {
716   for (;;) {
717     int c = input_stack::get(nd);
718     if (c == ESCAPE_NEWLINE) {
719       if (defining)
720 	return c;
721       do {
722 	c = input_stack::get(nd);
723       } while (c == ESCAPE_NEWLINE);
724     }
725     if (c != escape_char || escape_char <= 0)
726       return c;
727     c = input_stack::peek();
728     switch(c) {
729     case 0:
730       return escape_char;
731     case '"':
732       (void)input_stack::get(NULL);
733       while ((c = input_stack::get(NULL)) != '\n' && c != EOF)
734 	;
735       return c;
736     case '#':			// Like \" but newline is ignored.
737       (void)input_stack::get(NULL);
738       while ((c = input_stack::get(NULL)) != '\n')
739 	if (c == EOF)
740 	  return EOF;
741       break;
742     case '$':
743       {
744 	(void)input_stack::get(NULL);
745 	symbol s = read_escape_name();
746 	if (!s.is_null())
747 	  interpolate_arg(s);
748 	break;
749       }
750     case '*':
751       {
752 	(void)input_stack::get(NULL);
753 	symbol s = read_escape_name();
754 	if (!s.is_null())
755 	  interpolate_string(s);
756 	break;
757       }
758     case 'a':
759       (void)input_stack::get(NULL);
760       return '\001';
761     case 'e':
762       (void)input_stack::get(NULL);
763       return ESCAPE_e;
764     case 'E':
765       (void)input_stack::get(NULL);
766       return ESCAPE_E;
767     case 'n':
768       {
769 	(void)input_stack::get(NULL);
770 	int inc;
771 	symbol s = read_increment_and_escape_name(&inc);
772 	if (!s.is_null())
773 	  interpolate_number_reg(s, inc);
774 	break;
775       }
776     case 'g':
777       {
778         (void)input_stack::get(NULL);
779         symbol s = read_escape_name();
780 	if (!s.is_null())
781 	  interpolate_number_format(s);
782         break;
783       }
784     case 't':
785       (void)input_stack::get(NULL);
786       return '\t';
787     case 'V':
788       {
789         (void)input_stack::get(NULL);
790 	symbol s = read_escape_name();
791 	if (!s.is_null())
792 	  interpolate_environment_variable(s);
793 	break;
794       }
795     case '\n':
796       (void)input_stack::get(NULL);
797       if (defining)
798 	return ESCAPE_NEWLINE;
799       break;
800     case ' ':
801       (void)input_stack::get(NULL);
802       return ESCAPE_SPACE;
803     case '|':
804       (void)input_stack::get(NULL);
805       return ESCAPE_BAR;
806     case '^':
807       (void)input_stack::get(NULL);
808       return ESCAPE_CIRCUMFLEX;
809     case '{':
810       (void)input_stack::get(NULL);
811       return ESCAPE_LEFT_BRACE;
812     case '}':
813       (void)input_stack::get(NULL);
814       return ESCAPE_RIGHT_BRACE;
815     case '`':
816       (void)input_stack::get(NULL);
817       return ESCAPE_LEFT_QUOTE;
818     case '\'':
819       (void)input_stack::get(NULL);
820       return ESCAPE_RIGHT_QUOTE;
821     case '-':
822       (void)input_stack::get(NULL);
823       return ESCAPE_HYPHEN;
824     case '_':
825       (void)input_stack::get(NULL);
826       return ESCAPE_UNDERSCORE;
827     case 'c':
828       (void)input_stack::get(NULL);
829       return ESCAPE_c;
830     case '!':
831       (void)input_stack::get(NULL);
832       return ESCAPE_BANG;
833     case '?':
834       (void)input_stack::get(NULL);
835       return ESCAPE_QUESTION;
836     case '&':
837       (void)input_stack::get(NULL);
838       return ESCAPE_AMPERSAND;
839     case ')':
840       (void)input_stack::get(NULL);
841       return ESCAPE_RIGHT_PARENTHESIS;
842     case '.':
843       (void)input_stack::get(NULL);
844       return c;
845     case '%':
846       (void)input_stack::get(NULL);
847       return ESCAPE_PERCENT;
848     default:
849       if (c == escape_char) {
850 	(void)input_stack::get(NULL);
851 	return c;
852       }
853       else
854 	return escape_char;
855     }
856   }
857 }
858 
859 class non_interpreted_char_node : public node {
860   unsigned char c;
861 public:
862   non_interpreted_char_node(unsigned char);
863   node *copy();
864   int interpret(macro *);
865   int same(node *);
866   const char *type();
867 };
868 
same(node * nd)869 int non_interpreted_char_node::same(node *nd)
870 {
871   return c == ((non_interpreted_char_node *)nd)->c;
872 }
873 
type()874 const char *non_interpreted_char_node::type()
875 {
876   return "non_interpreted_char_node";
877 }
878 
non_interpreted_char_node(unsigned char n)879 non_interpreted_char_node::non_interpreted_char_node(unsigned char n) : c(n)
880 {
881   assert(n != 0);
882 }
883 
copy()884 node *non_interpreted_char_node::copy()
885 {
886   return new non_interpreted_char_node(c);
887 }
888 
interpret(macro * mac)889 int non_interpreted_char_node::interpret(macro *mac)
890 {
891   mac->append(c);
892   return 1;
893 }
894 
895 static void do_width();
896 static node *do_non_interpreted();
897 static node *do_special();
898 static void do_register();
899 
do_overstrike()900 static node *do_overstrike()
901 {
902   token start;
903   overstrike_node *on = new overstrike_node;
904   start.next();
905   tok.next();
906   while (tok != start) {
907     if (tok.newline() || tok.eof()) {
908       warning(WARN_DELIM, "missing closing delimiter");
909       break;
910     }
911     charinfo *ci = tok.get_char(1);
912     if (ci) {
913       node *n = curenv->make_char_node(ci);
914       if (n)
915 	on->overstrike(n);
916     }
917     tok.next();
918   }
919   return on;
920 }
921 
do_bracket()922 static node *do_bracket()
923 {
924   token start;
925   bracket_node *bn = new bracket_node;
926   start.next();
927   tok.next();
928   while (tok != start) {
929     if (tok.eof()) {
930       warning(WARN_DELIM, "missing closing delimiter");
931       break;
932     }
933     if (tok.newline()) {
934       warning(WARN_DELIM, "missing closing delimiter");
935       input_stack::push(make_temp_iterator("\n"));
936       break;
937     }
938     charinfo *ci = tok.get_char(1);
939     if (ci) {
940       node *n = curenv->make_char_node(ci);
941       if (n)
942 	bn->bracket(n);
943     }
944     tok.next();
945   }
946   return bn;
947 }
948 
do_name_test()949 static int do_name_test()
950 {
951   token start;
952   start.next();
953   int start_level = input_stack::get_level();
954   int bad_char = 0;
955   int some_char = 0;
956   for (;;) {
957     tok.next();
958     if (tok.newline() || tok.eof()) {
959       warning(WARN_DELIM, "missing closing delimiter");
960       break;
961     }
962     if (tok == start
963 	&& (compatible_flag || input_stack::get_level() == start_level))
964       break;
965     if (!tok.ch())
966       bad_char = 1;
967     some_char = 1;
968   }
969   return some_char && !bad_char;
970 }
971 
972 #if 0
973 static node *do_zero_width()
974 {
975   token start;
976   start.next();
977   int start_level = input_stack::get_level();
978   environment env(curenv);
979   environment *oldenv = curenv;
980   curenv = &env;
981   for (;;) {
982     tok.next();
983     if (tok.newline() || tok.eof()) {
984       error("missing closing delimiter");
985       break;
986     }
987     if (tok == start
988 	&& (compatible_flag || input_stack::get_level() == start_level))
989       break;
990     tok.process();
991   }
992   curenv = oldenv;
993   node *rev = env.extract_output_line();
994   node *n = 0;
995   while (rev) {
996     node *tem = rev;
997     rev = rev->next;
998     tem->next = n;
999     n = tem;
1000   }
1001   return new zero_width_node(n);
1002 }
1003 
1004 #else
1005 
1006 // It's undesirable for \Z to change environments, because then
1007 // \n(.w won't work as expected.
1008 
do_zero_width()1009 static node *do_zero_width()
1010 {
1011   node *rev = new dummy_node;
1012   token start;
1013   start.next();
1014   int start_level = input_stack::get_level();
1015   for (;;) {
1016     tok.next();
1017     if (tok.newline() || tok.eof()) {
1018       warning(WARN_DELIM, "missing closing delimiter");
1019       break;
1020     }
1021     if (tok == start
1022 	&& (compatible_flag || input_stack::get_level() == start_level))
1023       break;
1024     if (!tok.add_to_node_list(&rev))
1025       error("illegal token in argument to \\Z");
1026   }
1027   node *n = 0;
1028   while (rev) {
1029     node *tem = rev;
1030     rev = rev->next;
1031     tem->next = n;
1032     n = tem;
1033   }
1034   return new zero_width_node(n);
1035 }
1036 
1037 #endif
1038 
get_token_node()1039 token_node *node::get_token_node()
1040 {
1041   return 0;
1042 }
1043 
1044 class token_node : public node {
1045 public:
1046   token tk;
1047   token_node(const token &t);
1048   node *copy();
1049   token_node *get_token_node();
1050   int same(node *);
1051   const char *type();
1052 };
1053 
token_node(const token & t)1054 token_node::token_node(const token &t) : tk(t)
1055 {
1056 }
1057 
copy()1058 node *token_node::copy()
1059 {
1060   return new token_node(tk);
1061 }
1062 
get_token_node()1063 token_node *token_node::get_token_node()
1064 {
1065   return this;
1066 }
1067 
same(node * nd)1068 int token_node::same(node *nd)
1069 {
1070   return tk == ((token_node *)nd)->tk;
1071 }
1072 
type()1073 const char *token_node::type()
1074 {
1075   return "token_node";
1076 }
1077 
token()1078 token::token() : nd(0), type(TOKEN_EMPTY)
1079 {
1080 }
1081 
~token()1082 token::~token()
1083 {
1084   delete nd;
1085 }
1086 
token(const token & t)1087 token::token(const token &t)
1088 : nm(t.nm), c(t.c), val(t.val), dim(t.dim), type(t.type)
1089 {
1090   // Use two statements to work around bug in SGI C++.
1091   node *tem = t.nd;
1092   nd = tem ? tem->copy() : 0;
1093 }
1094 
operator =(const token & t)1095 void token::operator=(const token &t)
1096 {
1097   delete nd;
1098   nm = t.nm;
1099   // Use two statements to work around bug in SGI C++.
1100   node *tem = t.nd;
1101   nd = tem ? tem->copy() : 0;
1102   c = t.c;
1103   val = t.val;
1104   dim = t.dim;
1105   type = t.type;
1106 }
1107 
skip()1108 void token::skip()
1109 {
1110   while (space())
1111     next();
1112 }
1113 
has_arg()1114 int has_arg()
1115 {
1116   while (tok.space())
1117     tok.next();
1118   return !tok.newline();
1119 }
1120 
make_space()1121 void token::make_space()
1122 {
1123   type = TOKEN_SPACE;
1124 }
1125 
make_newline()1126 void token::make_newline()
1127 {
1128   type = TOKEN_NEWLINE;
1129 }
1130 
next()1131 void token::next()
1132 {
1133   if (nd) {
1134     delete nd;
1135     nd = 0;
1136   }
1137   units x;
1138   for (;;) {
1139     node *n;
1140     int cc = input_stack::get(&n);
1141     if (cc != escape_char || escape_char == 0) {
1142     handle_normal_char:
1143       switch(cc) {
1144       case EOF:
1145 	type = TOKEN_EOF;
1146 	return;
1147       case TRANSPARENT_FILE_REQUEST:
1148       case TITLE_REQUEST:
1149       case COPY_FILE_REQUEST:
1150 #ifdef COLUMN
1151       case VJUSTIFY_REQUEST:
1152 #endif /* COLUMN */
1153 	type = TOKEN_REQUEST;
1154 	c = cc;
1155 	return;
1156       case BEGIN_TRAP:
1157 	type = TOKEN_BEGIN_TRAP;
1158 	return;
1159       case END_TRAP:
1160 	type = TOKEN_END_TRAP;
1161 	return;
1162       case LAST_PAGE_EJECTOR:
1163 	seen_last_page_ejector = 1;
1164 	// fall through
1165       case PAGE_EJECTOR:
1166 	type = TOKEN_PAGE_EJECTOR;
1167 	return;
1168       case ESCAPE_PERCENT:
1169       ESCAPE_PERCENT:
1170 	type = TOKEN_HYPHEN_INDICATOR;
1171 	return;
1172       case ESCAPE_SPACE:
1173       ESCAPE_SPACE:
1174 	type = TOKEN_NODE;
1175 	nd = new space_char_hmotion_node(curenv->get_space_width());
1176 	return;
1177       case ESCAPE_e:
1178       ESCAPE_e:
1179 	type = TOKEN_ESCAPE;
1180 	return;
1181       case ESCAPE_E:
1182 	goto handle_escape_char;
1183       case ESCAPE_BAR:
1184       ESCAPE_BAR:
1185 	type = TOKEN_NODE;
1186 	nd = new hmotion_node(curenv->get_narrow_space_width());
1187 	return;
1188       case ESCAPE_CIRCUMFLEX:
1189       ESCAPE_CIRCUMFLEX:
1190 	type = TOKEN_NODE;
1191 	nd = new hmotion_node(curenv->get_half_narrow_space_width());
1192 	return;
1193       case ESCAPE_NEWLINE:
1194 	break;
1195       case ESCAPE_LEFT_BRACE:
1196       ESCAPE_LEFT_BRACE:
1197 	type = TOKEN_LEFT_BRACE;
1198 	return;
1199       case ESCAPE_RIGHT_BRACE:
1200       ESCAPE_RIGHT_BRACE:
1201 	type = TOKEN_RIGHT_BRACE;
1202 	return;
1203       case ESCAPE_LEFT_QUOTE:
1204       ESCAPE_LEFT_QUOTE:
1205 	type = TOKEN_SPECIAL;
1206 	nm = symbol("ga");
1207 	return;
1208       case ESCAPE_RIGHT_QUOTE:
1209       ESCAPE_RIGHT_QUOTE:
1210 	type = TOKEN_SPECIAL;
1211 	nm = symbol("aa");
1212 	return;
1213       case ESCAPE_HYPHEN:
1214       ESCAPE_HYPHEN:
1215 	type = TOKEN_SPECIAL;
1216 	nm = symbol("-");
1217 	return;
1218       case ESCAPE_UNDERSCORE:
1219       ESCAPE_UNDERSCORE:
1220 	type = TOKEN_SPECIAL;
1221 	nm = symbol("ul");
1222 	return;
1223       case ESCAPE_c:
1224       ESCAPE_c:
1225 	type = TOKEN_INTERRUPT;
1226 	return;
1227       case ESCAPE_BANG:
1228       ESCAPE_BANG:
1229 	type = TOKEN_TRANSPARENT;
1230 	return;
1231       case ESCAPE_QUESTION:
1232       ESCAPE_QUESTION:
1233 	nd = do_non_interpreted();
1234 	if (nd) {
1235 	  type = TOKEN_NODE;
1236 	  return;
1237 	}
1238 	break;
1239       case ESCAPE_AMPERSAND:
1240       ESCAPE_AMPERSAND:
1241 	type = TOKEN_DUMMY;
1242 	return;
1243       case ESCAPE_RIGHT_PARENTHESIS:
1244       ESCAPE_RIGHT_PARENTHESIS:
1245 	type = TOKEN_NODE;
1246 	nd = new transparent_dummy_node;
1247 	return;
1248       case '\b':
1249 	type = TOKEN_BACKSPACE;
1250 	return;
1251       case ' ':
1252 	type = TOKEN_SPACE;
1253 	return;
1254       case '\t':
1255 	type = TOKEN_TAB;
1256 	return;
1257       case '\n':
1258 	type = TOKEN_NEWLINE;
1259 	return;
1260       case '\001':
1261 	type = TOKEN_LEADER;
1262 	return;
1263       case 0:
1264 	{
1265 	  assert(n != 0);
1266 	  token_node *tn = n->get_token_node();
1267 	  if (tn) {
1268 	    *this = tn->tk;
1269 	    delete tn;
1270 	  }
1271 	  else {
1272 	    nd = n;
1273 	    type = TOKEN_NODE;
1274 	  }
1275 	}
1276 	return;
1277       default:
1278 	type = TOKEN_CHAR;
1279 	c = cc;
1280 	return;
1281       }
1282     }
1283     else {
1284     handle_escape_char:
1285       cc = input_stack::get(NULL);
1286       switch(cc) {
1287       case '(':
1288 	nm = read_two_char_escape_name();
1289 	type = TOKEN_SPECIAL;
1290 	return;
1291       case EOF:
1292 	type = TOKEN_EOF;
1293 	error("end of input after escape character");
1294 	return;
1295       case '`':
1296 	goto ESCAPE_LEFT_QUOTE;
1297       case '\'':
1298 	goto ESCAPE_RIGHT_QUOTE;
1299       case '-':
1300 	goto ESCAPE_HYPHEN;
1301       case '_':
1302 	goto ESCAPE_UNDERSCORE;
1303       case '%':
1304 	goto ESCAPE_PERCENT;
1305       case ' ':
1306 	goto ESCAPE_SPACE;
1307       case '0':
1308 	nd = new hmotion_node(curenv->get_digit_width());
1309 	type = TOKEN_NODE;
1310 	return;
1311       case '|':
1312 	goto ESCAPE_BAR;
1313       case '^':
1314 	goto ESCAPE_CIRCUMFLEX;
1315       case '/':
1316 	type = TOKEN_ITALIC_CORRECTION;
1317 	return;
1318       case ',':
1319 	type = TOKEN_NODE;
1320 	nd = new left_italic_corrected_node;
1321 	return;
1322       case '&':
1323 	goto ESCAPE_AMPERSAND;
1324       case ')':
1325 	goto ESCAPE_RIGHT_PARENTHESIS;
1326       case '!':
1327 	goto ESCAPE_BANG;
1328       case '?':
1329 	goto ESCAPE_QUESTION;
1330       case '~':
1331 	nd = new unbreakable_space_node(curenv->get_space_width());
1332 	type = TOKEN_NODE;
1333 	return;
1334       case '"':
1335 	while ((cc = input_stack::get(NULL)) != '\n' && cc != EOF)
1336 	  ;
1337 	if (cc == '\n')
1338 	  type = TOKEN_NEWLINE;
1339 	else
1340 	  type = TOKEN_EOF;
1341 	return;
1342       case '#':			// Like \" but newline is ignored.
1343 	while ((cc = input_stack::get(NULL)) != '\n')
1344 	  if (cc == EOF) {
1345 	    type = TOKEN_EOF;
1346 	    return;
1347 	  }
1348 	break;
1349       case '$':
1350 	{
1351 	  symbol nm = read_escape_name();
1352 	  if (!nm.is_null())
1353 	    interpolate_arg(nm);
1354 	  break;
1355 	}
1356       case '*':
1357 	{
1358 	  symbol nm = read_escape_name();
1359 	  if (!nm.is_null())
1360 	    interpolate_string(nm);
1361 	  break;
1362 	}
1363       case 'a':
1364 	nd = new non_interpreted_char_node('\001');
1365 	type = TOKEN_NODE;
1366 	return;
1367       case 'A':
1368 	c = '0' + do_name_test();
1369 	type = TOKEN_CHAR;
1370 	return;
1371       case 'b':
1372 	nd = do_bracket();
1373 	type = TOKEN_NODE;
1374 	return;
1375       case 'c':
1376 	goto ESCAPE_c;
1377       case 'C':
1378 	nm = get_delim_name();
1379 	if (nm.is_null())
1380 	  break;
1381 	type = TOKEN_SPECIAL;
1382 	return;
1383       case 'd':
1384 	type = TOKEN_NODE;
1385 	nd = new vmotion_node(curenv->get_size()/2);
1386 	return;
1387       case 'D':
1388 	nd = read_draw_node();
1389 	if (!nd)
1390 	  break;
1391 	type = TOKEN_NODE;
1392 	return;
1393       case 'e':
1394 	goto ESCAPE_e;
1395       case 'E':
1396 	goto handle_escape_char;
1397       case 'f':
1398 	{
1399 	  symbol s = read_escape_name();
1400 	  if (s.is_null())
1401 	    break;
1402 	  for (const char *p = s.contents(); *p != '\0'; p++)
1403 	    if (!csdigit(*p))
1404 	      break;
1405 	  if (*p)
1406 	    curenv->set_font(s);
1407 	  else
1408 	    curenv->set_font(atoi(s.contents()));
1409 	  break;
1410 	}
1411       case 'g':
1412 	{
1413 	  symbol s = read_escape_name();
1414 	  if (!s.is_null())
1415 	    interpolate_number_format(s);
1416 	  break;
1417 	}
1418       case 'h':
1419 	if (!get_delim_number(&x, 'm'))
1420 	  break;
1421 	type = TOKEN_NODE;
1422 	nd = new hmotion_node(x);
1423 	return;
1424       case 'H':
1425 	if (get_delim_number(&x, 'z', curenv->get_requested_point_size()))
1426 	  curenv->set_char_height(x);
1427 	break;
1428       case 'k':
1429 	nm = read_escape_name();
1430 	if (nm.is_null())
1431 	  break;
1432 	type = TOKEN_MARK_INPUT;
1433 	return;
1434       case 'l':
1435       case 'L':
1436 	{
1437 	  charinfo *s = 0;
1438 	  if (!get_line_arg(&x, (cc == 'l' ? 'm': 'v'), &s))
1439 	    break;
1440 	  if (s == 0)
1441 	    s = get_charinfo(cc == 'l' ? "ru" : "br");
1442 	  type = TOKEN_NODE;
1443 	  node *n = curenv->make_char_node(s);
1444 	  if (cc == 'l')
1445 	    nd = new hline_node(x, n);
1446 	  else
1447 	    nd = new vline_node(x, n);
1448 	  return;
1449 	}
1450       case 'n':
1451 	{
1452 	  int inc;
1453 	  symbol nm = read_increment_and_escape_name(&inc);
1454 	  if (!nm.is_null())
1455 	    interpolate_number_reg(nm, inc);
1456 	  break;
1457 	}
1458       case 'N':
1459 	if (!get_delim_number(&val, 0))
1460 	  break;
1461 	type = TOKEN_NUMBERED_CHAR;
1462 	return;
1463       case 'o':
1464 	nd = do_overstrike();
1465 	type = TOKEN_NODE;
1466 	return;
1467       case 'p':
1468 	type = TOKEN_SPREAD;
1469 	return;
1470       case 'r':
1471 	type = TOKEN_NODE;
1472 	nd = new vmotion_node(-curenv->get_size());
1473 	return;
1474       case 'R':
1475 	do_register();
1476 	break;
1477       case 's':
1478 	if (read_size(&x))
1479 	  curenv->set_size(x);
1480 	break;
1481       case 'S':
1482 	if (get_delim_number(&x, 0))
1483 	  curenv->set_char_slant(x);
1484 	break;
1485       case 't':
1486 	type = TOKEN_NODE;
1487 	nd = new non_interpreted_char_node('\t');
1488 	return;
1489       case 'u':
1490 	type = TOKEN_NODE;
1491 	nd = new vmotion_node(-curenv->get_size()/2);
1492 	return;
1493       case 'v':
1494 	if (!get_delim_number(&x, 'v'))
1495 	  break;
1496 	type = TOKEN_NODE;
1497 	nd = new vmotion_node(x);
1498 	return;
1499       case 'V':
1500         {
1501 	  symbol nm = read_escape_name();
1502 	  if (!nm.is_null())
1503 	    interpolate_environment_variable(nm);
1504 	  break;
1505 	}
1506       case 'w':
1507 	do_width();
1508 	break;
1509       case 'x':
1510 	if (!get_delim_number(&x, 'v'))
1511 	  break;
1512 	type = TOKEN_NODE;
1513 	nd = new extra_size_node(x);
1514 	return;
1515       case 'X':
1516 	nd = do_special();
1517 	if (!nd)
1518 	  break;
1519 	type = TOKEN_NODE;
1520 	return;
1521       case 'Y':
1522 	{
1523 	  symbol s = read_escape_name();
1524 	  if (s.is_null())
1525 	    break;
1526 	  request_or_macro *p = lookup_request(s);
1527 	  macro *m = p->to_macro();
1528 	  if (!m) {
1529 	    error("can't transparently throughput a request");
1530 	    break;
1531 	  }
1532 	  nd = new special_node(*m);
1533 	  type = TOKEN_NODE;
1534 	  return;
1535 	}
1536       case 'z':
1537 	{
1538 	  next();
1539           if (type == TOKEN_NODE)
1540             nd = new zero_width_node(nd);
1541           else {
1542   	    charinfo *ci = get_char(1);
1543 	    if (ci == 0)
1544 	      break;
1545 	    node *gn = curenv->make_char_node(ci);
1546 	    if (gn == 0)
1547 	      break;
1548 	    nd = new zero_width_node(gn);
1549 	    type = TOKEN_NODE;
1550           }
1551 	  return;
1552 	}
1553       case 'Z':
1554 	nd = do_zero_width();
1555 	if (nd == 0)
1556 	  break;
1557 	type = TOKEN_NODE;
1558 	return;
1559       case '{':
1560 	goto ESCAPE_LEFT_BRACE;
1561       case '}':
1562 	goto ESCAPE_RIGHT_BRACE;
1563       case '\n':
1564 	break;
1565       case '[':
1566 	if (!compatible_flag) {
1567 	  nm = read_long_escape_name();
1568 	  if (nm.is_null())
1569 	    break;
1570 	  type = TOKEN_SPECIAL;
1571 	  return;
1572 	}
1573 	goto handle_normal_char;
1574       default:
1575 	if (cc != escape_char && cc != '.')
1576 	  warning(WARN_ESCAPE, "escape character ignored before %1",
1577 		  input_char_description(cc));
1578 	goto handle_normal_char;
1579       }
1580     }
1581   }
1582 }
1583 
operator ==(const token & t)1584 int token::operator==(const token &t)
1585 {
1586   if (type != t.type)
1587     return 0;
1588   switch(type) {
1589   case TOKEN_CHAR:
1590     return c == t.c;
1591   case TOKEN_SPECIAL:
1592     return nm == t.nm;
1593   case TOKEN_NUMBERED_CHAR:
1594     return val == t.val;
1595   default:
1596     return 1;
1597   }
1598 }
1599 
operator !=(const token & t)1600 int token::operator!=(const token &t)
1601 {
1602   return !(*this == t);
1603 }
1604 
1605 // is token a suitable delimiter (like ')?
1606 
delimiter(int err)1607 int token::delimiter(int err)
1608 {
1609   switch(type) {
1610   case TOKEN_CHAR:
1611     switch(c) {
1612     case '0':
1613     case '1':
1614     case '2':
1615     case '3':
1616     case '4':
1617     case '5':
1618     case '6':
1619     case '7':
1620     case '8':
1621     case '9':
1622     case '+':
1623     case '-':
1624     case '/':
1625     case '*':
1626     case '%':
1627     case '<':
1628     case '>':
1629     case '=':
1630     case '&':
1631     case ':':
1632     case '(':
1633     case ')':
1634     case '.':
1635       if (err)
1636 	error("cannot use character `%1' as a starting delimiter", char(c));
1637       return 0;
1638     default:
1639       return 1;
1640     }
1641   case TOKEN_NODE:
1642   case TOKEN_SPACE:
1643   case TOKEN_TAB:
1644   case TOKEN_NEWLINE:
1645     if (err)
1646       error("cannot use %1 as a starting delimiter", description());
1647     return 0;
1648   default:
1649     return 1;
1650   }
1651 }
1652 
description()1653 const char *token::description()
1654 {
1655   static char buf[4];
1656   switch (type) {
1657   case TOKEN_BACKSPACE:
1658     return "a backspace character";
1659   case TOKEN_CHAR:
1660     buf[0] = '`';
1661     buf[1] = c;
1662     buf[2] = '\'';
1663     buf[3] = '\0';
1664     return buf;
1665   case TOKEN_DUMMY:
1666     return "`\\&'";
1667   case TOKEN_ESCAPE:
1668     return "`\\e'";
1669   case TOKEN_HYPHEN_INDICATOR:
1670     return "`\\%'";
1671   case TOKEN_INTERRUPT:
1672     return "`\\c'";
1673   case TOKEN_ITALIC_CORRECTION:
1674     return "`\\/'";
1675   case TOKEN_LEADER:
1676     return "a leader character";
1677   case TOKEN_LEFT_BRACE:
1678     return "`\\{'";
1679   case TOKEN_MARK_INPUT:
1680     return "`\\k'";
1681   case TOKEN_NEWLINE:
1682     return "newline";
1683   case TOKEN_NODE:
1684     return "a node";
1685   case TOKEN_NUMBERED_CHAR:
1686     return "`\\N'";
1687   case TOKEN_RIGHT_BRACE:
1688     return "`\\}'";
1689   case TOKEN_SPACE:
1690     return "a space";
1691   case TOKEN_SPECIAL:
1692     return "a special character";
1693   case TOKEN_SPREAD:
1694     return "`\\p'";
1695   case TOKEN_TAB:
1696     return "a tab character";
1697   case TOKEN_TRANSPARENT:
1698     return "`\\!'";
1699   case TOKEN_EOF:
1700     return "end of input";
1701   default:
1702     break;
1703   }
1704   return "a magic token";
1705 }
1706 
skip_line()1707 void skip_line()
1708 {
1709   while (!tok.newline())
1710     if (tok.eof())
1711       return;
1712     else
1713       tok.next();
1714   tok.next();
1715 }
1716 
compatible()1717 void compatible()
1718 {
1719   int n;
1720   if (has_arg() && get_integer(&n))
1721     compatible_flag = n != 0;
1722   else
1723     compatible_flag = 1;
1724   skip_line();
1725 }
1726 
empty_name_warning(int required)1727 static void empty_name_warning(int required)
1728 {
1729   if (tok.newline() || tok.eof()) {
1730     if (required)
1731       warning(WARN_MISSING, "missing name");
1732   }
1733   else if (tok.right_brace() || tok.tab()) {
1734     const char *start = tok.description();
1735     do {
1736       tok.next();
1737     } while (tok.space() || tok.right_brace() || tok.tab());
1738     if (!tok.newline() && !tok.eof())
1739       error("%1 is not allowed before an argument", start);
1740     else if (required)
1741       warning(WARN_MISSING, "missing name");
1742   }
1743   else if (required)
1744     error("name expected (got %1)", tok.description());
1745   else
1746     error("name expected (got %1): treated as missing", tok.description());
1747 }
1748 
non_empty_name_warning()1749 static void non_empty_name_warning()
1750 {
1751   if (!tok.newline() && !tok.eof() && !tok.space() && !tok.tab()
1752       && !tok.right_brace()
1753       // We don't want to give a warning for .el\{
1754       && !tok.left_brace())
1755     error("%1 is not allowed in a name", tok.description());
1756 }
1757 
get_name(int required)1758 symbol get_name(int required)
1759 {
1760   if (compatible_flag) {
1761     char buf[3];
1762     tok.skip();
1763     if ((buf[0] = tok.ch()) != 0) {
1764       tok.next();
1765       if ((buf[1] = tok.ch()) != 0) {
1766 	buf[2] = 0;
1767 	tok.make_space();
1768       }
1769       else
1770 	non_empty_name_warning();
1771       return symbol(buf);
1772     }
1773     else {
1774       empty_name_warning(required);
1775       return NULL_SYMBOL;
1776     }
1777   }
1778   else
1779     return get_long_name(required);
1780 }
1781 
get_long_name(int required)1782 symbol get_long_name(int required)
1783 {
1784   while (tok.space())
1785     tok.next();
1786   char abuf[ABUF_SIZE];
1787   char *buf = abuf;
1788   int buf_size = ABUF_SIZE;
1789   int i = 0;
1790   for (;;) {
1791     if (i + 1 > buf_size) {
1792       if (buf == abuf) {
1793 	buf = new char [ABUF_SIZE*2];
1794 	memcpy(buf, abuf, buf_size);
1795 	buf_size = ABUF_SIZE*2;
1796       }
1797       else {
1798 	char *old_buf = buf;
1799 	buf = new char[buf_size*2];
1800 	memcpy(buf, old_buf, buf_size);
1801 	buf_size *= 2;
1802 	a_delete old_buf;
1803       }
1804     }
1805     if ((buf[i] = tok.ch()) == 0)
1806       break;
1807     i++;
1808     tok.next();
1809   }
1810   if (i == 0) {
1811     empty_name_warning(required);
1812     return NULL_SYMBOL;
1813   }
1814   non_empty_name_warning();
1815   if (buf == abuf)
1816     return symbol(buf);
1817   else {
1818     symbol s(buf);
1819     a_delete buf;
1820     return s;
1821   }
1822 }
1823 
exit_troff()1824 NO_RETURN void exit_troff()
1825 {
1826   exit_started = 1;
1827   topdiv->set_last_page();
1828   if (!end_macro_name.is_null()) {
1829     spring_trap(end_macro_name);
1830     tok.next();
1831     process_input_stack();
1832   }
1833   curenv->final_break();
1834   tok.next();
1835   process_input_stack();
1836   end_diversions();
1837   done_end_macro = 1;
1838   topdiv->set_ejecting();
1839   static unsigned char buf[2] = { LAST_PAGE_EJECTOR, '\0' };
1840   input_stack::push(make_temp_iterator((char *)buf));
1841   topdiv->space(topdiv->get_page_length(), 1);
1842   tok.next();
1843   process_input_stack();
1844   seen_last_page_ejector = 1;	// should be set already
1845   topdiv->set_ejecting();
1846   push_page_ejector();
1847   topdiv->space(topdiv->get_page_length(), 1);
1848   tok.next();
1849   process_input_stack();
1850   // This will only happen if a trap-invoked macro starts a diversion,
1851   // or if vertical position traps have been disabled.
1852   cleanup_and_exit(0);
1853 }
1854 
1855 // This implements .ex.  The input stack must be cleared before calling
1856 // exit_troff().
1857 
exit_request()1858 void exit_request()
1859 {
1860   input_stack::clear();
1861   if (exit_started)
1862     tok.next();
1863   else
1864     exit_troff();
1865 }
1866 
end_macro()1867 void end_macro()
1868 {
1869   end_macro_name = get_name();
1870   skip_line();
1871 }
1872 
do_request()1873 void do_request()
1874 {
1875   int saved_compatible_flag = compatible_flag;
1876   compatible_flag = 0;
1877   symbol nm = get_name();
1878   if (nm.is_null())
1879     skip_line();
1880   else
1881     interpolate_macro(nm);
1882   compatible_flag = saved_compatible_flag;
1883 }
1884 
possibly_handle_first_page_transition()1885 inline int possibly_handle_first_page_transition()
1886 {
1887   if (topdiv->before_first_page && curdiv == topdiv && !curenv->is_dummy()) {
1888     handle_first_page_transition();
1889     return 1;
1890   }
1891   else
1892     return 0;
1893 }
1894 
transparent_translate(int cc)1895 static int transparent_translate(int cc)
1896 {
1897   if (!illegal_input_char(cc)) {
1898     charinfo *ci = charset_table[cc];
1899     switch (ci->get_special_translation(1)) {
1900     case charinfo::TRANSLATE_SPACE:
1901       return ' ';
1902     case charinfo::TRANSLATE_DUMMY:
1903       return ESCAPE_AMPERSAND;
1904     case charinfo::TRANSLATE_HYPHEN_INDICATOR:
1905       return ESCAPE_PERCENT;
1906     }
1907     // This is realy ugly.
1908     ci = ci->get_translation(1);
1909     if (ci) {
1910       int c = ci->get_ascii_code();
1911       if (c != '\0')
1912 	return c;
1913       error("can't translate %1 to special character `%2'"
1914 	    " in transparent throughput",
1915 	    input_char_description(cc),
1916 	    ci->nm.contents());
1917     }
1918   }
1919   return cc;
1920 }
1921 
1922 class int_stack {
1923   struct int_stack_element {
1924     int n;
1925     int_stack_element *next;
1926   } *top;
1927 public:
1928   int_stack();
1929   ~int_stack();
1930   void push(int);
1931   int is_empty();
1932   int pop();
1933 };
1934 
int_stack()1935 int_stack::int_stack()
1936 {
1937   top = 0;
1938 }
1939 
~int_stack()1940 int_stack::~int_stack()
1941 {
1942   while (top != 0) {
1943     int_stack_element *temp = top;
1944     top = top->next;
1945     delete temp;
1946   }
1947 
1948 }
1949 
is_empty()1950 int int_stack::is_empty()
1951 {
1952   return top == 0;
1953 }
1954 
push(int n)1955 void int_stack::push(int n)
1956 {
1957   int_stack_element *p = new int_stack_element;
1958   p->next = top;
1959   p->n = n;
1960   top = p;
1961 }
1962 
1963 
pop()1964 int int_stack::pop()
1965 {
1966   assert(top != 0);
1967   int_stack_element *p = top;
1968   top = top->next;
1969   int n = p->n;
1970   delete p;
1971   return n;
1972 }
1973 
reread(int *)1974 int node::reread(int *)
1975 {
1976   return 0;
1977 }
1978 
reread(int * bolp)1979 int diverted_space_node::reread(int *bolp)
1980 {
1981   if (curenv->get_fill())
1982     blank_line();
1983   else
1984     curdiv->space(n);
1985   *bolp = 1;
1986   return 1;
1987 }
1988 
reread(int * bolp)1989 int diverted_copy_file_node::reread(int *bolp)
1990 {
1991   curdiv->copy_file(filename.contents());
1992   *bolp = 1;
1993   return 1;
1994 }
1995 
process_input_stack()1996 static void process_input_stack()
1997 {
1998   int_stack trap_bol_stack;
1999   int bol = 1;
2000   for (;;) {
2001     int suppress_next = 0;
2002     switch (tok.type) {
2003     case token::TOKEN_CHAR:
2004       {
2005 	unsigned char ch = tok.c;
2006 	if (bol &&
2007 	    (ch == curenv->control_char
2008 	     || ch == curenv->no_break_control_char)) {
2009 	  break_flag = ch == curenv->control_char;
2010 	  // skip tabs as well as spaces here
2011 	  do {
2012 	    tok.next();
2013 	  } while (tok.white_space());
2014 	  symbol nm = get_name();
2015 	  if (nm.is_null())
2016 	    skip_line();
2017 	  else
2018 	    interpolate_macro(nm);
2019 	  suppress_next = 1;
2020 	}
2021 	else {
2022 	  if (possibly_handle_first_page_transition())
2023 	    ;
2024 	  else {
2025 	    for (;;) {
2026 	      curenv->add_char(charset_table[ch]);
2027 	      tok.next();
2028 	      if (tok.type != token::TOKEN_CHAR)
2029 		break;
2030 	      ch = tok.c;
2031 	    }
2032 	    suppress_next = 1;
2033 	    bol = 0;
2034 	  }
2035 	}
2036 	break;
2037       }
2038     case token::TOKEN_TRANSPARENT:
2039       {
2040 	if (bol) {
2041 	  if (possibly_handle_first_page_transition())
2042 	    ;
2043 	  else {
2044 	    int cc;
2045 	    do {
2046 	      node *n;
2047 	      cc = get_copy(&n);
2048 	      if (cc != EOF)
2049 		if (cc != '\0')
2050 		  curdiv->transparent_output(transparent_translate(cc));
2051 		else
2052 		  curdiv->transparent_output(n);
2053 	    } while (cc != '\n' && cc != EOF);
2054 	    if (cc == EOF)
2055 	      curdiv->transparent_output('\n');
2056 	  }
2057 	}
2058 	break;
2059       }
2060     case token::TOKEN_NEWLINE:
2061       {
2062 	if (bol && !curenv->get_prev_line_interrupted())
2063 	  blank_line();
2064 	else {
2065 	  curenv->newline();
2066 	  bol = 1;
2067 	}
2068 	break;
2069       }
2070     case token::TOKEN_REQUEST:
2071       {
2072 	int request_code = tok.c;
2073 	tok.next();
2074 	switch (request_code) {
2075 	case TITLE_REQUEST:
2076 	  title();
2077 	  break;
2078 	case COPY_FILE_REQUEST:
2079 	  copy_file();
2080 	  break;
2081 	case TRANSPARENT_FILE_REQUEST:
2082 	  transparent_file();
2083 	  break;
2084 #ifdef COLUMN
2085 	case VJUSTIFY_REQUEST:
2086 	  vjustify();
2087 	  break;
2088 #endif /* COLUMN */
2089 	default:
2090 	  assert(0);
2091 	  break;
2092 	}
2093 	suppress_next = 1;
2094 	break;
2095       }
2096     case token::TOKEN_SPACE:
2097       {
2098 	if (possibly_handle_first_page_transition())
2099 	  ;
2100 	else if (bol && !curenv->get_prev_line_interrupted()) {
2101 	  int nspaces = 0;
2102 	  do {
2103 	    nspaces += tok.nspaces();
2104 	    tok.next();
2105 	  } while (tok.space());
2106 	  if (tok.newline())
2107 	    blank_line();
2108 	  else {
2109 	    push_token(tok);
2110 	    curenv->do_break();
2111 	    curenv->add_node(new hmotion_node(curenv->get_space_width()*nspaces));
2112 	    bol = 0;
2113 	  }
2114 	}
2115 	else {
2116 	  curenv->space();
2117 	  bol = 0;
2118 	}
2119 	break;
2120       }
2121     case token::TOKEN_EOF:
2122       return;
2123     case token::TOKEN_NODE:
2124       {
2125 	if (possibly_handle_first_page_transition())
2126 	  ;
2127 	else if (tok.nd->reread(&bol)) {
2128 	  delete tok.nd;
2129 	  tok.nd = 0;
2130 	}
2131 	else {
2132 	  curenv->add_node(tok.nd);
2133 	  tok.nd = 0;
2134 	  bol = 0;
2135 	}
2136 	break;
2137       }
2138     case token::TOKEN_PAGE_EJECTOR:
2139       {
2140 	continue_page_eject();
2141 	// I think we just want to preserve bol.
2142 	// bol = 1;
2143 	break;
2144       }
2145     case token::TOKEN_BEGIN_TRAP:
2146       {
2147 	trap_bol_stack.push(bol);
2148 	bol = 1;
2149 	break;
2150       }
2151     case token::TOKEN_END_TRAP:
2152       {
2153 	if (trap_bol_stack.is_empty())
2154 	  error("spurious end trap token detected!");
2155 	else
2156 	  bol = trap_bol_stack.pop();
2157 
2158 	/* I'm not totally happy about this.  But I can't think of any other
2159 	  way to do it.  Doing an output_pending_lines() whenever a
2160 	  TOKEN_END_TRAP is detected doesn't work: for example,
2161 
2162           .wh -1i x
2163           .de x
2164           'bp
2165 	  ..
2166 	  .wh -.5i y
2167 	  .de y
2168 	  .tl ''-%-''
2169 	  ..
2170 	  .br
2171 	  .ll .5i
2172 	  .sp |\n(.pu-1i-.5v
2173 	  a\%very\%very\%long\%word
2174 
2175           will print all but the first lines from the word immediately
2176           after the footer, rather than on the next page. */
2177 
2178 	if (trap_bol_stack.is_empty())
2179 	  curenv->output_pending_lines();
2180 	break;
2181       }
2182     default:
2183       {
2184 	bol = 0;
2185 	tok.process();
2186 	break;
2187       }
2188     }
2189     if (!suppress_next)
2190       tok.next();
2191     trap_sprung_flag = 0;
2192   }
2193 }
2194 
2195 #ifdef WIDOW_CONTROL
2196 
flush_pending_lines()2197 void flush_pending_lines()
2198 {
2199   while (!tok.newline() && !tok.eof())
2200     tok.next();
2201   curenv->output_pending_lines();
2202   tok.next();
2203 }
2204 
2205 #endif /* WIDOW_CONTROL */
2206 
request_or_macro()2207 request_or_macro::request_or_macro()
2208 {
2209 }
2210 
to_macro()2211 macro *request_or_macro::to_macro()
2212 {
2213   return 0;
2214 }
2215 
request(REQUEST_FUNCP pp)2216 request::request(REQUEST_FUNCP pp) : p(pp)
2217 {
2218 }
2219 
invoke(symbol)2220 void request::invoke(symbol)
2221 {
2222   (*p)();
2223 }
2224 
2225 struct char_block {
2226   enum { SIZE = 128 };
2227   unsigned char s[SIZE];
2228   char_block *next;
2229   char_block();
2230 };
2231 
char_block()2232 char_block::char_block()
2233 : next(0)
2234 {
2235 }
2236 
2237 class char_list {
2238 public:
2239   char_list();
2240   ~char_list();
2241   void append(unsigned char);
2242   int length();
2243 private:
2244   unsigned char *ptr;
2245   int len;
2246   char_block *head;
2247   char_block *tail;
2248   friend class macro_header;
2249   friend class string_iterator;
2250 };
2251 
char_list()2252 char_list::char_list()
2253 : head(0), tail(0), ptr(0), len(0)
2254 {
2255 }
2256 
~char_list()2257 char_list::~char_list()
2258 {
2259   while (head != 0) {
2260     char_block *tem = head;
2261     head = head->next;
2262     delete tem;
2263   }
2264 }
2265 
length()2266 int char_list::length()
2267 {
2268   return len;
2269 }
2270 
append(unsigned char c)2271 void char_list::append(unsigned char c)
2272 {
2273   if (tail == 0) {
2274     head = tail = new char_block;
2275     ptr = tail->s;
2276   }
2277   else {
2278     if (ptr >= tail->s + char_block::SIZE) {
2279       tail->next = new char_block;
2280       tail = tail->next;
2281       ptr = tail->s;
2282     }
2283   }
2284   *ptr++ = c;
2285   len++;
2286 }
2287 
2288 class node_list {
2289   node *head;
2290   node *tail;
2291 public:
2292   node_list();
2293   ~node_list();
2294   void append(node *);
2295   int length();
2296   node *extract();
2297 
2298   friend class macro_header;
2299   friend class string_iterator;
2300 };
2301 
append(node * n)2302 void node_list::append(node *n)
2303 {
2304   if (head == 0) {
2305     n->next = 0;
2306     head = tail = n;
2307   }
2308   else {
2309     n->next = 0;
2310     tail = tail->next = n;
2311   }
2312 }
2313 
length()2314 int node_list::length()
2315 {
2316   int total = 0;
2317   for (node *n = head; n != 0; n = n->next)
2318     ++total;
2319   return total;
2320 }
2321 
node_list()2322 node_list::node_list()
2323 {
2324   head = tail = 0;
2325 }
2326 
extract()2327 node *node_list::extract()
2328 {
2329   node *temp = head;
2330   head = tail = 0;
2331   return temp;
2332 }
2333 
2334 
~node_list()2335 node_list::~node_list()
2336 {
2337   delete_node_list(head);
2338 }
2339 
2340 struct macro_header {
2341   int count;
2342   char_list cl;
2343   node_list nl;
macro_headermacro_header2344   macro_header() { count = 1; }
2345   macro_header *copy(int);
2346 };
2347 
2348 
~macro()2349 macro::~macro()
2350 {
2351   if (p != 0 && --(p->count) <= 0)
2352     delete p;
2353 }
2354 
macro()2355 macro::macro()
2356 {
2357   if (!input_stack::get_location(1, &filename, &lineno)) {
2358     filename = 0;
2359     lineno = 0;
2360   }
2361   length = 0;
2362   p = 0;
2363 }
2364 
macro(const macro & m)2365 macro::macro(const macro &m)
2366 : filename(m.filename), lineno(m.lineno), p(m.p), length(m.length)
2367 {
2368   if (p != 0)
2369     p->count++;
2370 }
2371 
operator =(const macro & m)2372 macro &macro::operator=(const macro &m)
2373 {
2374   // don't assign object
2375   if (m.p != 0)
2376     m.p->count++;
2377   if (p != 0 && --(p->count) <= 0)
2378     delete p;
2379   p = m.p;
2380   filename = m.filename;
2381   lineno = m.lineno;
2382   length = m.length;
2383   return *this;
2384 }
2385 
append(unsigned char c)2386 void macro::append(unsigned char c)
2387 {
2388   assert(c != 0);
2389   if (p == 0)
2390     p = new macro_header;
2391   if (p->cl.length() != length) {
2392     macro_header *tem = p->copy(length);
2393     if (--(p->count) <= 0)
2394       delete p;
2395     p = tem;
2396   }
2397   p->cl.append(c);
2398   ++length;
2399 }
2400 
append(node * n)2401 void macro::append(node *n)
2402 {
2403   assert(n != 0);
2404   if (p == 0)
2405     p = new macro_header;
2406   if (p->cl.length() != length) {
2407     macro_header *tem = p->copy(length);
2408     if (--(p->count) <= 0)
2409       delete p;
2410     p = tem;
2411   }
2412   p->cl.append(0);
2413   p->nl.append(n);
2414   ++length;
2415 }
2416 
print_size()2417 void macro::print_size()
2418 {
2419   errprint("%1", length);
2420 }
2421 
2422 // make a copy of the first n bytes
2423 
copy(int n)2424 macro_header *macro_header::copy(int n)
2425 {
2426   macro_header *p = new macro_header;
2427   char_block *bp = cl.head;
2428   unsigned char *ptr = bp->s;
2429   node *nd = nl.head;
2430   while (--n >= 0) {
2431     if (ptr >= bp->s + char_block::SIZE) {
2432       bp = bp->next;
2433       ptr = bp->s;
2434     }
2435     int c = *ptr++;
2436     p->cl.append(c);
2437     if (c == 0) {
2438       p->nl.append(nd->copy());
2439       nd = nd->next;
2440     }
2441   }
2442   return p;
2443 }
2444 
print_macros()2445 void print_macros()
2446 {
2447   object_dictionary_iterator iter(request_dictionary);
2448   request_or_macro *rm;
2449   symbol s;
2450   while (iter.get(&s, (object **)&rm)) {
2451     assert(!s.is_null());
2452     macro *m = rm->to_macro();
2453     if (m) {
2454       errprint("%1\t", s.contents());
2455       m->print_size();
2456       errprint("\n");
2457     }
2458   }
2459   fflush(stderr);
2460   skip_line();
2461 }
2462 
2463 class string_iterator : public input_iterator {
2464   macro mac;
2465   const char *how_invoked;
2466   int newline_flag;
2467   int lineno;
2468   char_block *bp;
2469   int count;			// of characters remaining
2470   node *nd;
2471 protected:
2472   symbol nm;
2473   string_iterator();
2474 public:
2475   string_iterator(const macro &m, const char *p = 0, symbol s = NULL_SYMBOL);
2476   int fill(node **);
2477   int peek();
2478   int get_location(int, const char **, int *);
2479   void backtrace();
2480 };
2481 
string_iterator(const macro & m,const char * p,symbol s)2482 string_iterator::string_iterator(const macro &m, const char *p, symbol s)
2483 : lineno(1), mac(m), newline_flag(0), how_invoked(p), nm(s)
2484 {
2485   count = mac.length;
2486   if (count != 0) {
2487     bp = mac.p->cl.head;
2488     nd = mac.p->nl.head;
2489     ptr = eptr = bp->s;
2490   }
2491   else {
2492     bp = 0;
2493     nd = 0;
2494     ptr = eptr = 0;
2495   }
2496 }
2497 
string_iterator()2498 string_iterator::string_iterator()
2499 {
2500   bp = 0;
2501   nd = 0;
2502   ptr = eptr = 0;
2503   newline_flag = 0;
2504   how_invoked = 0;
2505   lineno = 1;
2506   count = 0;
2507 }
2508 
fill(node ** np)2509 int string_iterator::fill(node **np)
2510 {
2511   if (newline_flag)
2512     lineno++;
2513   newline_flag = 0;
2514   if (count <= 0)
2515     return EOF;
2516   const unsigned char *p = eptr;
2517   if (p >= bp->s + char_block::SIZE) {
2518     bp = bp->next;
2519     p = bp->s;
2520   }
2521   if (*p == '\0') {
2522     if (np)
2523       *np = nd->copy();
2524     nd = nd->next;
2525     eptr = ptr = p + 1;
2526     count--;
2527     return 0;
2528   }
2529   const unsigned char *e = bp->s + char_block::SIZE;
2530   if (e - p > count)
2531     e = p + count;
2532   ptr = p;
2533   while (p < e) {
2534     unsigned char c = *p;
2535     if (c == '\n' || c == ESCAPE_NEWLINE) {
2536       newline_flag = 1;
2537       p++;
2538       break;
2539     }
2540     if (c == '\0')
2541       break;
2542     p++;
2543   }
2544   eptr = p;
2545   count -= p - ptr;
2546   return *ptr++;
2547 }
2548 
peek()2549 int string_iterator::peek()
2550 {
2551   if (count <= 0)
2552     return EOF;
2553   const unsigned char *p = eptr;
2554   if (count <= 0)
2555     return EOF;
2556   if (p >= bp->s + char_block::SIZE) {
2557     p = bp->next->s;
2558   }
2559   return *p;
2560 }
2561 
get_location(int allow_macro,const char ** filep,int * linep)2562 int string_iterator::get_location(int allow_macro,
2563 				  const char **filep, int *linep)
2564 {
2565   if (!allow_macro)
2566     return 0;
2567   if (mac.filename == 0)
2568     return 0;
2569   *filep = mac.filename;
2570   *linep = mac.lineno + lineno - 1;
2571   return 1;
2572 }
2573 
backtrace()2574 void string_iterator::backtrace()
2575 {
2576   if (mac.filename) {
2577     errprint("%1:%2: backtrace", mac.filename, mac.lineno + lineno - 1);
2578     if (how_invoked) {
2579       if (!nm.is_null())
2580 	errprint(": %1 `%2'\n", how_invoked, nm.contents());
2581       else
2582 	errprint(": %1\n", how_invoked);
2583     }
2584     else
2585       errprint("\n");
2586   }
2587 }
2588 
2589 class temp_iterator : public input_iterator {
2590   unsigned char *base;
2591   temp_iterator(const char *, int len);
2592 public:
2593   ~temp_iterator();
2594   friend input_iterator *make_temp_iterator(const char *);
2595 };
2596 
2597 #ifdef __GNUG__
2598 inline
2599 #endif
temp_iterator(const char * s,int len)2600 temp_iterator::temp_iterator(const char *s, int len)
2601 {
2602   base = new unsigned char[len];
2603   memcpy(base, s, len);
2604   ptr = base;
2605   eptr = base + len;
2606 }
2607 
~temp_iterator()2608 temp_iterator::~temp_iterator()
2609 {
2610   a_delete base;
2611 }
2612 
2613 class small_temp_iterator : public input_iterator {
2614 private:
2615   small_temp_iterator(const char *, int);
2616   ~small_temp_iterator();
2617   enum { BLOCK = 16 };
2618   static small_temp_iterator *free_list;
2619   void *operator new(size_t);
2620   void operator delete(void *);
2621   enum { SIZE = 12 };
2622   unsigned char buf[SIZE];
2623   friend input_iterator *make_temp_iterator(const char *);
2624 };
2625 
2626 small_temp_iterator *small_temp_iterator::free_list = 0;
2627 
operator new(size_t n)2628 void *small_temp_iterator::operator new(size_t n)
2629 {
2630   assert(n == sizeof(small_temp_iterator));
2631   if (!free_list) {
2632     free_list = (small_temp_iterator *)new char[sizeof(small_temp_iterator)*BLOCK];
2633     for (int i = 0; i < BLOCK - 1; i++)
2634       free_list[i].next = free_list + i + 1;
2635     free_list[BLOCK-1].next = 0;
2636   }
2637   small_temp_iterator *p = free_list;
2638   free_list = (small_temp_iterator *)(free_list->next);
2639   p->next = 0;
2640   return p;
2641 }
2642 
2643 #ifdef __GNUG__
2644 inline
2645 #endif
operator delete(void * p)2646 void small_temp_iterator::operator delete(void *p)
2647 {
2648   if (p) {
2649     ((small_temp_iterator *)p)->next = free_list;
2650     free_list = (small_temp_iterator *)p;
2651   }
2652 }
2653 
~small_temp_iterator()2654 small_temp_iterator::~small_temp_iterator()
2655 {
2656 }
2657 
2658 
2659 #ifdef __GNUG__
2660 inline
2661 #endif
small_temp_iterator(const char * s,int len)2662 small_temp_iterator::small_temp_iterator(const char *s, int len)
2663 {
2664   for (int i = 0; i < len; i++)
2665     buf[i] = s[i];
2666   ptr = buf;
2667   eptr = buf + len;
2668 }
2669 
make_temp_iterator(const char * s)2670 input_iterator *make_temp_iterator(const char *s)
2671 {
2672   if (s == 0)
2673     return new small_temp_iterator(s, 0);
2674   else {
2675     int n = strlen(s);
2676     if (n <= small_temp_iterator::SIZE)
2677       return new small_temp_iterator(s, n);
2678     else
2679       return new temp_iterator(s, n);
2680   }
2681 }
2682 
2683 // this is used when macros are interpolated using the .macro_name notation
2684 
2685 struct arg_list {
2686   macro mac;
2687   arg_list *next;
2688   arg_list(const macro &);
2689   ~arg_list();
2690 };
2691 
arg_list(const macro & m)2692 arg_list::arg_list(const macro &m) : mac(m), next(0)
2693 {
2694 }
2695 
~arg_list()2696 arg_list::~arg_list()
2697 {
2698 }
2699 
2700 class macro_iterator : public string_iterator {
2701   arg_list *args;
2702   int argc;
2703 public:
2704   macro_iterator(symbol, macro &);
2705   macro_iterator();
2706   ~macro_iterator();
has_args()2707   int has_args() { return 1; }
2708   input_iterator *get_arg(int i);
nargs()2709   int nargs() { return argc; }
2710   void add_arg(const macro &m);
2711   void shift(int n);
2712 };
2713 
get_arg(int i)2714 input_iterator *macro_iterator::get_arg(int i)
2715 {
2716   if (i == 0)
2717     return make_temp_iterator(nm.contents());
2718   if (i > 0 && i <= argc) {
2719     arg_list *p = args;
2720     for (int j = 1; j < i; j++) {
2721       assert(p != 0);
2722       p = p->next;
2723     }
2724     return new string_iterator(p->mac);
2725   }
2726   else
2727     return 0;
2728 }
2729 
add_arg(const macro & m)2730 void macro_iterator::add_arg(const macro &m)
2731 {
2732   for (arg_list **p = &args; *p; p = &((*p)->next))
2733     ;
2734   *p = new arg_list(m);
2735   ++argc;
2736 }
2737 
shift(int n)2738 void macro_iterator::shift(int n)
2739 {
2740   while (n > 0 && argc > 0) {
2741     arg_list *tem = args;
2742     args = args->next;
2743     delete tem;
2744     --argc;
2745     --n;
2746   }
2747 }
2748 
2749 // This gets used by eg .if '\?xxx\?''.
2750 
operator ==(const macro & m1,const macro & m2)2751 int operator==(const macro &m1, const macro &m2)
2752 {
2753   if (m1.length != m2.length)
2754     return 0;
2755   string_iterator iter1(m1);
2756   string_iterator iter2(m2);
2757   int n = m1.length;
2758   while (--n >= 0) {
2759     node *nd1 = 0;
2760     int c1 = iter1.get(&nd1);
2761     assert(c1 != EOF);
2762     node *nd2 = 0;
2763     int c2 = iter2.get(&nd2);
2764     assert(c2 != EOF);
2765     if (c1 != c2) {
2766       if (c1 == 0)
2767 	delete nd1;
2768       else if (c2 == 0)
2769 	delete nd2;
2770       return 0;
2771     }
2772     if (c1 == 0) {
2773       assert(nd1 != 0);
2774       assert(nd2 != 0);
2775       int are_same = nd1->type() == nd2->type() && nd1->same(nd2);
2776       delete nd1;
2777       delete nd2;
2778       if (!are_same)
2779 	return 0;
2780     }
2781   }
2782   return 1;
2783 }
2784 
interpolate_macro(symbol nm)2785 static void interpolate_macro(symbol nm)
2786 {
2787   request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm);
2788   if (p == 0) {
2789     int warned = 0;
2790     const char *s = nm.contents();
2791     if (strlen(s) > 2) {
2792       request_or_macro *r;
2793       char buf[3];
2794       buf[0] = s[0];
2795       buf[1] = s[1];
2796       buf[2] = '\0';
2797       r = (request_or_macro *)request_dictionary.lookup(symbol(buf));
2798       if (r) {
2799 	macro *m = r->to_macro();
2800 	if (!m || !m->empty())
2801 	  warned = warning(WARN_SPACE,
2802 			   "space required between `%1' and argument", buf);
2803       }
2804     }
2805     if (!warned) {
2806       warning(WARN_MAC, "`%1' not defined", nm.contents());
2807       p = new macro;
2808       request_dictionary.define(nm, p);
2809     }
2810   }
2811   if (p)
2812     p->invoke(nm);
2813   else {
2814     skip_line();
2815     return;
2816   }
2817 }
2818 
decode_args(macro_iterator * mi)2819 static void decode_args(macro_iterator *mi)
2820 {
2821   if (!tok.newline() && !tok.eof()) {
2822     node *n;
2823     int c = get_copy(&n);
2824     for (;;)  {
2825       while (c == ' ')
2826 	c = get_copy(&n);
2827       if (c == '\n' || c == EOF)
2828 	break;
2829       macro arg;
2830       int quote_input_level = 0;
2831       if (c == '\"') {
2832 	quote_input_level = input_stack::get_level();
2833 	c = get_copy(&n);
2834       }
2835       while (c != EOF && c != '\n' && !(c == ' ' && quote_input_level == 0)) {
2836 	if (quote_input_level > 0 && c == '\"'
2837 	    && (compatible_flag
2838 		|| input_stack::get_level() == quote_input_level)) {
2839 	  c = get_copy(&n);
2840 	  if (c == '"') {
2841 	    arg.append(c);
2842 	    c = get_copy(&n);
2843 	  }
2844 	  else
2845 	    break;
2846 	}
2847 	else {
2848 	  if (c == 0)
2849 	    arg.append(n);
2850 	  else
2851 	    arg.append(c);
2852 	  c = get_copy(&n);
2853 	}
2854       }
2855       mi->add_arg(arg);
2856     }
2857   }
2858 }
2859 
invoke(symbol nm)2860 void macro::invoke(symbol nm)
2861 {
2862   macro_iterator *mi = new macro_iterator(nm, *this);
2863   decode_args(mi);
2864   input_stack::push(mi);
2865   tok.next();
2866 }
2867 
to_macro()2868 macro *macro::to_macro()
2869 {
2870   return this;
2871 }
2872 
empty()2873 int macro::empty()
2874 {
2875   return length == 0;
2876 }
2877 
macro_iterator(symbol s,macro & m)2878 macro_iterator::macro_iterator(symbol s, macro &m)
2879 : string_iterator(m, "macro", s), args(0), argc(0)
2880 {
2881 }
2882 
macro_iterator()2883 macro_iterator::macro_iterator() : args(0), argc(0)
2884 {
2885 }
2886 
~macro_iterator()2887 macro_iterator::~macro_iterator()
2888 {
2889   while (args != 0) {
2890     arg_list *tem = args;
2891     args = args->next;
2892     delete tem;
2893   }
2894 }
2895 
read_request()2896 void read_request()
2897 {
2898   macro_iterator *mi = new macro_iterator;
2899   int had_prompt = 0;
2900   if (!tok.newline() && !tok.eof()) {
2901     int c = get_copy(NULL);
2902     while (c == ' ')
2903       c = get_copy(NULL);
2904     while (c != EOF && c != '\n' && c != ' ') {
2905       if (!illegal_input_char(c)) {
2906 	fputc(c, stderr);
2907 	had_prompt = 1;
2908       }
2909       c = get_copy(NULL);
2910     }
2911     if (c == ' ') {
2912       tok.make_space();
2913       decode_args(mi);
2914     }
2915   }
2916   fputc(had_prompt ? ':' : '\007', stderr);
2917   fflush(stderr);
2918   input_stack::push(mi);
2919   macro mac;
2920   int nl = 0;
2921   int c;
2922   while ((c = getchar()) != EOF) {
2923     if (illegal_input_char(c))
2924       warning(WARN_INPUT, "illegal input character code %1", int(c));
2925     else {
2926       if (c == '\n') {
2927 	if (nl)
2928 	  break;
2929 	else
2930 	  nl = 1;
2931       }
2932       else
2933 	nl = 0;
2934       mac.append(c);
2935     }
2936   }
2937   input_stack::push(new string_iterator(mac));
2938   tok.next();
2939 }
2940 
2941 
do_define_string(int append)2942 void do_define_string(int append)
2943 {
2944   symbol nm;
2945   node *n;
2946   int c;
2947   nm = get_name(1);
2948   if (nm.is_null()) {
2949     skip_line();
2950     return;
2951   }
2952   if (tok.newline())
2953     c = '\n';
2954   else if (tok.tab())
2955     c = '\t';
2956   else if (!tok.space()) {
2957     error("bad string definition");
2958     skip_line();
2959     return;
2960   }
2961   else
2962     c = get_copy(&n);
2963   while (c == ' ')
2964     c = get_copy(&n);
2965   if (c == '"')
2966     c = get_copy(&n);
2967   macro mac;
2968   request_or_macro *rm = (request_or_macro *)request_dictionary.lookup(nm);
2969   macro *mm = rm ? rm->to_macro() : 0;
2970   if (append && mm)
2971     mac = *mm;
2972   while (c != '\n' && c != EOF) {
2973     if (c == 0)
2974       mac.append(n);
2975     else
2976       mac.append((unsigned char)c);
2977     c = get_copy(&n);
2978   }
2979   if (!mm) {
2980     mm = new macro;
2981     request_dictionary.define(nm, mm);
2982   }
2983   *mm = mac;
2984   tok.next();
2985 }
2986 
define_string()2987 void define_string()
2988 {
2989   do_define_string(0);
2990 }
2991 
append_string()2992 void append_string()
2993 {
2994   do_define_string(1);
2995 }
2996 
define_character()2997 void define_character()
2998 {
2999   node *n;
3000   int c;
3001   tok.skip();
3002   charinfo *ci = tok.get_char(1);
3003   if (ci == 0) {
3004     skip_line();
3005     return;
3006   }
3007   tok.next();
3008   if (tok.newline())
3009     c = '\n';
3010   else if (tok.tab())
3011     c = '\t';
3012   else if (!tok.space()) {
3013     error("bad character definition");
3014     skip_line();
3015     return;
3016   }
3017   else
3018     c = get_copy(&n);
3019   while (c == ' ' || c == '\t')
3020     c = get_copy(&n);
3021   if (c == '"')
3022     c = get_copy(&n);
3023   macro *m = new macro;
3024   while (c != '\n' && c != EOF) {
3025     if (c == 0)
3026       m->append(n);
3027     else
3028       m->append((unsigned char)c);
3029     c = get_copy(&n);
3030   }
3031   m = ci->set_macro(m);
3032   if (m)
3033     delete m;
3034   tok.next();
3035 }
3036 
3037 
remove_character()3038 static void remove_character()
3039 {
3040   tok.skip();
3041   while (!tok.newline() && !tok.eof()) {
3042     if (!tok.space() && !tok.tab()) {
3043       charinfo *ci = tok.get_char(1);
3044       if (!ci)
3045 	break;
3046       macro *m = ci->set_macro(0);
3047       if (m)
3048 	delete m;
3049     }
3050     tok.next();
3051   }
3052   skip_line();
3053 }
3054 
interpolate_string(symbol nm)3055 static void interpolate_string(symbol nm)
3056 {
3057   request_or_macro *p = lookup_request(nm);
3058   macro *m = p->to_macro();
3059   if (!m)
3060     error("you can only invoke a string using \\*");
3061   else {
3062     string_iterator *si = new string_iterator(*m, "string", nm);
3063     input_stack::push(si);
3064   }
3065 }
3066 
3067 /* This class is used for the implementation of \$@.  It is used for
3068 each of the closing double quotes.  It artificially increases the
3069 input level by 2, so that the closing double quote will appear to have
3070 the same input level as the opening quote. */
3071 
3072 class end_quote_iterator : public input_iterator {
3073   unsigned char buf[1];
3074 public:
3075   end_quote_iterator();
~end_quote_iterator()3076   ~end_quote_iterator() { }
internal_level()3077   int internal_level() { return 2; }
3078 };
3079 
end_quote_iterator()3080 end_quote_iterator::end_quote_iterator()
3081 {
3082   buf[0] = '"';
3083   ptr = buf;
3084   eptr = buf + 1;
3085 }
3086 
interpolate_arg(symbol nm)3087 static void interpolate_arg(symbol nm)
3088 {
3089   const char *s = nm.contents();
3090   if (!s || *s == '\0')
3091     error("missing argument name");
3092   else if (s[1] == 0 && csdigit(s[0]))
3093     input_stack::push(input_stack::get_arg(s[0] - '0'));
3094   else if (s[0] == '*' && s[1] == '\0') {
3095     for (int i = input_stack::nargs(); i > 0; i--) {
3096       input_stack::push(input_stack::get_arg(i));
3097       if (i != 1)
3098 	input_stack::push(make_temp_iterator(" "));
3099     }
3100   }
3101   else if (s[0] == '@' && s[1] == '\0') {
3102     for (int i = input_stack::nargs(); i > 0; i--) {
3103       input_stack::push(new end_quote_iterator);
3104       input_stack::push(input_stack::get_arg(i));
3105       input_stack::push(make_temp_iterator(i == 1 ? "\"" : " \""));
3106     }
3107   }
3108   else {
3109     for (const char *p = s; *p && csdigit(*p); p++)
3110       ;
3111     if (*p)
3112       error("bad argument name `%1'", s);
3113     else
3114       input_stack::push(input_stack::get_arg(atoi(s)));
3115   }
3116 }
3117 
handle_first_page_transition()3118 void handle_first_page_transition()
3119 {
3120   push_token(tok);
3121   topdiv->begin_page();
3122 }
3123 
3124 // We push back a token by wrapping it up in a token_node, and
3125 // wrapping that up in a string_iterator.
3126 
push_token(const token & t)3127 static void push_token(const token &t)
3128 {
3129   macro m;
3130   m.append(new token_node(t));
3131   input_stack::push(new string_iterator(m));
3132 }
3133 
push_page_ejector()3134 void push_page_ejector()
3135 {
3136   static char buf[2] = { PAGE_EJECTOR, '\0' };
3137   input_stack::push(make_temp_iterator(buf));
3138 }
3139 
handle_initial_request(unsigned char code)3140 void handle_initial_request(unsigned char code)
3141 {
3142   char buf[2];
3143   buf[0] = code;
3144   buf[1] = '\0';
3145   macro mac;
3146   mac.append(new token_node(tok));
3147   input_stack::push(new string_iterator(mac));
3148   input_stack::push(make_temp_iterator(buf));
3149   topdiv->begin_page();
3150   tok.next();
3151 }
3152 
handle_initial_title()3153 void handle_initial_title()
3154 {
3155   handle_initial_request(TITLE_REQUEST);
3156 }
3157 
3158 int trap_sprung_flag = 0;
3159 int postpone_traps_flag = 0;
3160 symbol postponed_trap;
3161 
spring_trap(symbol nm)3162 void spring_trap(symbol nm)
3163 {
3164   assert(!nm.is_null());
3165   trap_sprung_flag = 1;
3166   if (postpone_traps_flag) {
3167     postponed_trap = nm;
3168     return;
3169   }
3170   static char buf[2] = { BEGIN_TRAP, 0 };
3171   static char buf2[2] = { END_TRAP, '\0' };
3172   input_stack::push(make_temp_iterator(buf2));
3173   request_or_macro *p = lookup_request(nm);
3174   macro *m = p->to_macro();
3175   if (m)
3176     input_stack::push(new string_iterator(*m, "trap-invoked macro", nm));
3177   else
3178     error("you can't invoke a request with a trap");
3179   input_stack::push(make_temp_iterator(buf));
3180 }
3181 
postpone_traps()3182 void postpone_traps()
3183 {
3184   postpone_traps_flag = 1;
3185 }
3186 
unpostpone_traps()3187 int unpostpone_traps()
3188 {
3189   postpone_traps_flag = 0;
3190   if (!postponed_trap.is_null()) {
3191     spring_trap(postponed_trap);
3192     postponed_trap = NULL_SYMBOL;
3193     return 1;
3194   }
3195   else
3196     return 0;
3197 }
3198 
3199 // this should be local to define_macro, but cfront 1.2 doesn't support that
3200 static symbol dot_symbol(".");
3201 
3202 enum define_mode { DEFINE_NORMAL, DEFINE_APPEND, DEFINE_IGNORE };
3203 
do_define_macro(define_mode mode)3204 void do_define_macro(define_mode mode)
3205 {
3206   symbol nm;
3207   if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
3208     nm = get_name(1);
3209     if (nm.is_null()) {
3210       skip_line();
3211       return;
3212     }
3213   }
3214   symbol term = get_name();	// the request that terminates the definition
3215   if (term.is_null())
3216     term = dot_symbol;
3217   while (!tok.newline() && !tok.eof())
3218     tok.next();
3219   const char *start_filename;
3220   int start_lineno;
3221   int have_start_location = input_stack::get_location(0, &start_filename,
3222 						      &start_lineno);
3223   node *n;
3224   // doing this here makes the line numbers come out right
3225   int c = get_copy(&n, 1);
3226   macro mac;
3227   macro *mm = 0;
3228   if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
3229     request_or_macro *rm =
3230       (request_or_macro *)request_dictionary.lookup(nm);
3231     if (rm)
3232       mm = rm->to_macro();
3233     if (mm && mode == DEFINE_APPEND)
3234       mac = *mm;
3235   }
3236   int bol = 1;
3237   for (;;) {
3238     while (c == ESCAPE_NEWLINE) {
3239       if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND)
3240 	mac.append(c);
3241       c = get_copy(&n, 1);
3242     }
3243     if (bol && c == '.') {
3244       const char *s = term.contents();
3245       int d;
3246       // see if it matches term
3247       for (int i = 0; s[i] != 0; i++) {
3248 	d = get_copy(&n);
3249 	if ((unsigned char)s[i] != d)
3250 	  break;
3251       }
3252       if (s[i] == 0
3253 	  && ((i == 2 && compatible_flag)
3254 	      || (d = get_copy(&n)) == ' '
3255 	      || d == '\n')) {	// we found it
3256 		if (d == '\n')
3257 		  tok.make_newline();
3258 		else
3259 		  tok.make_space();
3260 		if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) {
3261 		  if (!mm) {
3262 		    mm = new macro;
3263 		    request_dictionary.define(nm, mm);
3264 		  }
3265 		  *mm = mac;
3266 		}
3267 		if (term != dot_symbol)
3268 		  interpolate_macro(term);
3269 		else
3270 		  skip_line();
3271 		return;
3272 	      }
3273       if (mode == DEFINE_APPEND || mode == DEFINE_NORMAL) {
3274 	mac.append(c);
3275 	for (int j = 0; j < i; j++)
3276 	  mac.append(s[j]);
3277       }
3278       c = d;
3279     }
3280     if (c == EOF) {
3281       if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
3282 	if (have_start_location)
3283 	  error_with_file_and_line(start_filename, start_lineno,
3284 				   "end of file while defining macro `%1'",
3285 				   nm.contents());
3286 	else
3287 	  error("end of file while defining macro `%1'", nm.contents());
3288       }
3289       else {
3290 	if (have_start_location)
3291 	  error_with_file_and_line(start_filename, start_lineno,
3292 				   "end of file while ignoring input lines");
3293 	else
3294 	  error("end of file while ignoring input lines");
3295       }
3296       tok.next();
3297       return;
3298     }
3299     if (mode == DEFINE_NORMAL || mode == DEFINE_APPEND) {
3300       if (c == 0)
3301 	mac.append(n);
3302       else
3303 	mac.append(c);
3304     }
3305     bol = (c == '\n');
3306     c = get_copy(&n, 1);
3307   }
3308 }
3309 
define_macro()3310 void define_macro()
3311 {
3312   do_define_macro(DEFINE_NORMAL);
3313 }
3314 
append_macro()3315 void append_macro()
3316 {
3317   do_define_macro(DEFINE_APPEND);
3318 }
3319 
ignore()3320 void ignore()
3321 {
3322   do_define_macro(DEFINE_IGNORE);
3323 }
3324 
remove_macro()3325 void remove_macro()
3326 {
3327   for (;;) {
3328     symbol s = get_name();
3329     if (s.is_null())
3330       break;
3331     request_dictionary.remove(s);
3332   }
3333   skip_line();
3334 }
3335 
rename_macro()3336 void rename_macro()
3337 {
3338   symbol s1 = get_name(1);
3339   if (!s1.is_null()) {
3340     symbol s2 = get_name(1);
3341     if (!s2.is_null())
3342       request_dictionary.rename(s1, s2);
3343   }
3344   skip_line();
3345 }
3346 
alias_macro()3347 void alias_macro()
3348 {
3349   symbol s1 = get_name(1);
3350   if (!s1.is_null()) {
3351     symbol s2 = get_name(1);
3352     if (!s2.is_null()) {
3353       if (!request_dictionary.alias(s1, s2))
3354 	warning(WARN_MAC, "`%1' not defined", s2.contents());
3355     }
3356   }
3357   skip_line();
3358 }
3359 
chop_macro()3360 void chop_macro()
3361 {
3362   symbol s = get_name(1);
3363   if (!s.is_null()) {
3364     request_or_macro *p = lookup_request(s);
3365     macro *m = p->to_macro();
3366     if (!m)
3367       error("cannot chop request");
3368     else if (m->length == 0)
3369       error("cannot chop empty macro");
3370     else
3371       m->length -= 1;
3372   }
3373   skip_line();
3374 }
3375 
asciify_macro()3376 void asciify_macro()
3377 {
3378   symbol s = get_name(1);
3379   if (!s.is_null()) {
3380     request_or_macro *p = lookup_request(s);
3381     macro *m = p->to_macro();
3382     if (!m)
3383       error("cannot asciify request");
3384     else {
3385       macro am;
3386       string_iterator iter(*m);
3387       for (;;) {
3388 	node *nd;
3389 	int c = iter.get(&nd);
3390 	if (c == EOF)
3391 	  break;
3392 	if (c != 0)
3393 	  am.append(c);
3394 	else
3395 	  nd->asciify(&am);
3396       }
3397       *m = am;
3398     }
3399   }
3400   skip_line();
3401 }
3402 
interpolate_environment_variable(symbol nm)3403 static void interpolate_environment_variable(symbol nm)
3404 {
3405   const char *s = getenv(nm.contents());
3406   if (s && *s)
3407     input_stack::push(make_temp_iterator(s));
3408 }
3409 
interpolate_number_reg(symbol nm,int inc)3410 void interpolate_number_reg(symbol nm, int inc)
3411 {
3412   reg *r = lookup_number_reg(nm);
3413   if (inc < 0)
3414     r->decrement();
3415   else if (inc > 0)
3416     r->increment();
3417   input_stack::push(make_temp_iterator(r->get_string()));
3418 }
3419 
interpolate_number_format(symbol nm)3420 static void interpolate_number_format(symbol nm)
3421 {
3422   reg *r = (reg *)number_reg_dictionary.lookup(nm);
3423   if (r)
3424     input_stack::push(make_temp_iterator(r->get_format()));
3425 }
3426 
get_delim_number(units * n,int si,int prev_value)3427 static int get_delim_number(units *n, int si, int prev_value)
3428 {
3429   token start;
3430   start.next();
3431   if (start.delimiter(1)) {
3432     tok.next();
3433     if (get_number(n, si, prev_value)) {
3434       if (start != tok)
3435 	warning(WARN_DELIM, "closing delimiter does not match");
3436       return 1;
3437     }
3438   }
3439   return 0;
3440 }
3441 
get_delim_number(units * n,int si)3442 static int get_delim_number(units *n, int si)
3443 {
3444   token start;
3445   start.next();
3446   if (start.delimiter(1)) {
3447     tok.next();
3448     if (get_number(n, si)) {
3449       if (start != tok)
3450 	warning(WARN_DELIM, "closing delimiter does not match");
3451       return 1;
3452     }
3453   }
3454   return 0;
3455 }
3456 
get_line_arg(units * n,int si,charinfo ** cp)3457 static int get_line_arg(units *n, int si, charinfo **cp)
3458 {
3459   token start;
3460   start.next();
3461   if (start.delimiter(1)) {
3462     tok.next();
3463     if (get_number(n, si)) {
3464       if (tok.dummy())
3465 	tok.next();
3466       if (start != tok) {
3467 	*cp = tok.get_char(1);
3468 	tok.next();
3469       }
3470       if (start != tok)
3471 	warning(WARN_DELIM, "closing delimiter does not match");
3472       return 1;
3473     }
3474   }
3475   return 0;
3476 }
3477 
read_size(int * x)3478 static int read_size(int *x)
3479 {
3480   tok.next();
3481   int c = tok.ch();
3482   int inc = 0;
3483   if (c == '-') {
3484     inc = -1;
3485     tok.next();
3486     c = tok.ch();
3487   }
3488   else if (c == '+') {
3489     inc = 1;
3490     tok.next();
3491     c = tok.ch();
3492   }
3493   int val;
3494   int bad = 0;
3495   if (c == '(') {
3496     tok.next();
3497     c = tok.ch();
3498     if (!inc) {
3499       // allow an increment either before or after the left parenthesis
3500       if (c == '-') {
3501 	inc = -1;
3502 	tok.next();
3503 	c = tok.ch();
3504       }
3505       else if (c == '+') {
3506 	inc = 1;
3507 	tok.next();
3508 	c = tok.ch();
3509       }
3510     }
3511     if (!csdigit(c))
3512       bad = 1;
3513     else {
3514       val = c - '0';
3515       tok.next();
3516       c = tok.ch();
3517       if (!csdigit(c))
3518 	bad = 1;
3519       else {
3520 	val = val*10 + (c - '0');
3521 	val *= sizescale;
3522       }
3523     }
3524   }
3525   else if (csdigit(c)) {
3526     val = c - '0';
3527     if (!inc && c != '0' && c < '4') {
3528       tok.next();
3529       c = tok.ch();
3530       if (!csdigit(c))
3531 	bad = 1;
3532       else
3533 	val = val*10 + (c - '0');
3534     }
3535     val *= sizescale;
3536   }
3537   else if (!tok.delimiter(1))
3538     return 0;
3539   else {
3540     token start(tok);
3541     tok.next();
3542     if (!(inc
3543 	  ? get_number(&val, 'z')
3544 	  : get_number(&val, 'z', curenv->get_requested_point_size())))
3545       return 0;
3546     if (!(start.ch() == '[' && tok.ch() == ']') && start != tok) {
3547       if (start.ch() == '[')
3548 	error("missing `]'");
3549       else
3550 	error("missing closing delimiter");
3551       return 0;
3552     }
3553   }
3554   if (!bad) {
3555     switch (inc) {
3556     case 0:
3557       *x = val;
3558       break;
3559     case 1:
3560       *x = curenv->get_requested_point_size() + val;
3561       break;
3562     case -1:
3563       *x = curenv->get_requested_point_size() - val;
3564       break;
3565     default:
3566       assert(0);
3567     }
3568     return 1;
3569   }
3570   else {
3571     error("bad digit in point size");
3572     return 0;
3573   }
3574 }
3575 
get_delim_name()3576 static symbol get_delim_name()
3577 {
3578   token start;
3579   start.next();
3580   if (start.eof()) {
3581     error("end of input at start of delimited name");
3582     return NULL_SYMBOL;
3583   }
3584   if (start.newline()) {
3585     error("can't delimit name with a newline");
3586     return NULL_SYMBOL;
3587   }
3588   int start_level = input_stack::get_level();
3589   char abuf[ABUF_SIZE];
3590   char *buf = abuf;
3591   int buf_size = ABUF_SIZE;
3592   int i = 0;
3593   for (;;) {
3594     if (i + 1 > buf_size) {
3595       if (buf == abuf) {
3596 	buf = new char [ABUF_SIZE*2];
3597 	memcpy(buf, abuf, buf_size);
3598 	buf_size = ABUF_SIZE*2;
3599       }
3600       else {
3601 	char *old_buf = buf;
3602 	buf = new char[buf_size*2];
3603 	memcpy(buf, old_buf, buf_size);
3604 	buf_size *= 2;
3605 	a_delete old_buf;
3606       }
3607     }
3608     tok.next();
3609     if (tok == start
3610 	&& (compatible_flag || input_stack::get_level() == start_level))
3611       break;
3612     if ((buf[i] = tok.ch()) == 0) {
3613       error("missing delimiter (got %1)", tok.description());
3614       if (buf != abuf)
3615 	a_delete buf;
3616       return NULL_SYMBOL;
3617     }
3618     i++;
3619   }
3620   buf[i] = '\0';
3621   if (buf == abuf) {
3622     if (i == 0) {
3623       error("empty delimited name");
3624       return NULL_SYMBOL;
3625     }
3626     else
3627       return symbol(buf);
3628   }
3629   else {
3630     symbol s(buf);
3631     a_delete buf;
3632     return s;
3633   }
3634 }
3635 
3636 
3637 // Implement \R
3638 
do_register()3639 static void do_register()
3640 {
3641   token start;
3642   start.next();
3643   if (!start.delimiter(1))
3644     return;
3645   tok.next();
3646   symbol nm = get_long_name(1);
3647   if (nm.is_null())
3648     return;
3649   while (tok.space())
3650     tok.next();
3651   reg *r = (reg *)number_reg_dictionary.lookup(nm);
3652   int prev_value;
3653   if (!r || !r->get_value(&prev_value))
3654     prev_value = 0;
3655   int val;
3656   if (!get_number(&val, 'u', prev_value))
3657     return;
3658   if (start != tok)
3659     warning(WARN_DELIM, "closing delimiter does not match");
3660   if (r)
3661     r->set_value(val);
3662   else
3663     set_number_reg(nm, val);
3664 }
3665 
3666 // this implements the \w escape sequence
3667 
do_width()3668 static void do_width()
3669 {
3670   token start;
3671   start.next();
3672   int start_level = input_stack::get_level();
3673   environment env(curenv);
3674   environment *oldenv = curenv;
3675   curenv = &env;
3676   for (;;) {
3677     tok.next();
3678     if (tok.eof()) {
3679       warning(WARN_DELIM, "missing closing delimiter");
3680       break;
3681     }
3682     if (tok.newline()) {
3683       warning(WARN_DELIM, "missing closing delimiter");
3684       input_stack::push(make_temp_iterator("\n"));
3685       break;
3686     }
3687     if (tok == start
3688 	&& (compatible_flag || input_stack::get_level() == start_level))
3689       break;
3690     tok.process();
3691   }
3692   env.wrap_up_tab();
3693   units x = env.get_input_line_position().to_units();
3694   input_stack::push(make_temp_iterator(itoa(x)));
3695   env.width_registers();
3696   curenv = oldenv;
3697 }
3698 
3699 charinfo *page_character;
3700 
set_page_character()3701 void set_page_character()
3702 {
3703   page_character = get_optional_char();
3704   skip_line();
3705 }
3706 
3707 static const symbol percent_symbol("%");
3708 
read_title_parts(node ** part,hunits * part_width)3709 void read_title_parts(node **part, hunits *part_width)
3710 {
3711   tok.skip();
3712   if (tok.newline() || tok.eof())
3713     return;
3714   token start(tok);
3715   int start_level = input_stack::get_level();
3716   tok.next();
3717   for (int i = 0; i < 3; i++) {
3718     while (!tok.newline() && !tok.eof()) {
3719       if (tok == start
3720 	  && (compatible_flag || input_stack::get_level() == start_level)) {
3721 	tok.next();
3722 	break;
3723       }
3724       if (page_character != 0 && tok.get_char() == page_character)
3725 	interpolate_number_reg(percent_symbol, 0);
3726       else
3727 	tok.process();
3728       tok.next();
3729     }
3730     curenv->wrap_up_tab();
3731     part_width[i] = curenv->get_input_line_position();
3732     part[i] = curenv->extract_output_line();
3733   }
3734   while (!tok.newline() && !tok.eof())
3735     tok.next();
3736 }
3737 
3738 class non_interpreted_node : public node {
3739   macro mac;
3740 public:
3741   non_interpreted_node(const macro &);
3742   int interpret(macro *);
3743   node *copy();
3744   int same(node *);
3745   const char *type();
3746 };
3747 
non_interpreted_node(const macro & m)3748 non_interpreted_node::non_interpreted_node(const macro &m) : mac(m)
3749 {
3750 }
3751 
same(node * nd)3752 int non_interpreted_node::same(node *nd)
3753 {
3754   return mac == ((non_interpreted_node *)nd)->mac;
3755 }
3756 
type()3757 const char *non_interpreted_node::type()
3758 {
3759   return "non_interpreted_node";
3760 }
3761 
copy()3762 node *non_interpreted_node::copy()
3763 {
3764   return new non_interpreted_node(mac);
3765 }
3766 
interpret(macro * m)3767 int non_interpreted_node::interpret(macro *m)
3768 {
3769   string_iterator si(mac);
3770   node *n;
3771   for (;;) {
3772     int c = si.get(&n);
3773     if (c == EOF)
3774       break;
3775     if (c == 0)
3776       m->append(n);
3777     else
3778       m->append(c);
3779   }
3780   return 1;
3781 }
3782 
do_non_interpreted()3783 static node *do_non_interpreted()
3784 {
3785   node *n;
3786   int c;
3787   macro mac;
3788   while ((c = get_copy(&n)) != ESCAPE_QUESTION && c != EOF && c != '\n')
3789     if (c == 0)
3790       mac.append(n);
3791     else
3792       mac.append(c);
3793   if (c == EOF || c == '\n') {
3794     error("missing \\?");
3795     return 0;
3796   }
3797   return new non_interpreted_node(mac);
3798 }
3799 
do_special()3800 node *do_special()
3801 {
3802   token start;
3803   start.next();
3804   int start_level = input_stack::get_level();
3805   macro mac;
3806   for (tok.next();
3807        tok != start || input_stack::get_level() != start_level;
3808        tok.next()) {
3809     if (tok.eof()) {
3810       warning(WARN_DELIM, "missing closing delimiter");
3811       return 0;
3812     }
3813     if (tok.newline()) {
3814       input_stack::push(make_temp_iterator("\n"));
3815       warning(WARN_DELIM, "missing closing delimiter");
3816       break;
3817     }
3818     unsigned char c;
3819     if (tok.space())
3820       c = ' ';
3821     else if (tok.tab())
3822       c = '\t';
3823     else if (tok.leader())
3824       c = '\001';
3825     else if (tok.backspace())
3826       c = '\b';
3827     else
3828       c = tok.ch();
3829     if (c == '\0')
3830       error("%1 is illegal within \\X", tok.description());
3831     else
3832       mac.append(c);
3833   }
3834   return new special_node(mac);
3835 }
3836 
tprint(troff_output_file * out)3837 void special_node::tprint(troff_output_file *out)
3838 {
3839   tprint_start(out);
3840   string_iterator iter(mac);
3841   for (;;) {
3842     int c = iter.get(NULL);
3843     if (c == EOF)
3844       break;
3845     for (const char *s = ::asciify(c); *s; s++)
3846       tprint_char(out, *s);
3847   }
3848   tprint_end(out);
3849 }
3850 
get_file_line(const char ** filename,int * lineno)3851 int get_file_line(const char **filename, int *lineno)
3852 {
3853   return input_stack::get_location(0, filename, lineno);
3854 }
3855 
line_file()3856 void line_file()
3857 {
3858   int n;
3859   if (get_integer(&n)) {
3860     const char *filename = 0;
3861     if (has_arg()) {
3862       symbol s = get_long_name();
3863       filename = s.contents();
3864     }
3865     (void)input_stack::set_location(filename, n-1);
3866   }
3867   skip_line();
3868 }
3869 
3870 static int nroff_mode = 0;
3871 
nroff_request()3872 static void nroff_request()
3873 {
3874   nroff_mode = 1;
3875   skip_line();
3876 }
3877 
troff_request()3878 static void troff_request()
3879 {
3880   nroff_mode = 0;
3881   skip_line();
3882 }
3883 
skip_alternative()3884 static void skip_alternative()
3885 {
3886   int level = 0;
3887   // ensure that ``.if 0\{'' works as expected
3888   if (tok.left_brace())
3889     level++;
3890   int c;
3891   for (;;) {
3892     c = input_stack::get(NULL);
3893     if (c == EOF)
3894       break;
3895     if (c == ESCAPE_LEFT_BRACE)
3896       ++level;
3897     else if (c == ESCAPE_RIGHT_BRACE)
3898       --level;
3899     else if (c == escape_char && escape_char > 0)
3900       switch(input_stack::get(NULL)) {
3901       case '{':
3902 	++level;
3903 	break;
3904       case '}':
3905 	--level;
3906 	break;
3907       case '"':
3908 	while ((c = input_stack::get(NULL)) != '\n' && c != EOF)
3909 	  ;
3910       }
3911     /*
3912       Note that the level can properly be < 0, eg
3913 
3914 	.if 1 \{\
3915 	.if 0 \{\
3916 	.\}\}
3917 
3918       So don't give an error message in this case.
3919     */
3920     if (level <= 0 && c == '\n')
3921       break;
3922   }
3923   tok.next();
3924 }
3925 
begin_alternative()3926 static void begin_alternative()
3927 {
3928   while (tok.space() || tok.left_brace())
3929     tok.next();
3930 }
3931 
3932 
3933 static int_stack if_else_stack;
3934 
do_if_request()3935 int do_if_request()
3936 {
3937   int invert = 0;
3938   while (tok.space())
3939     tok.next();
3940   while (tok.ch() == '!') {
3941     tok.next();
3942     invert = !invert;
3943   }
3944   int result;
3945   unsigned char c = tok.ch();
3946   if (c == 't') {
3947     tok.next();
3948     result = !nroff_mode;
3949   }
3950   else if (c == 'n') {
3951     tok.next();
3952     result = nroff_mode;
3953   }
3954   else if (c == 'v') {
3955     tok.next();
3956     result = 0;
3957   }
3958   else if (c == 'o') {
3959     result = (topdiv->get_page_number() & 1);
3960     tok.next();
3961   }
3962   else if (c == 'e') {
3963     result = !(topdiv->get_page_number() & 1);
3964     tok.next();
3965   }
3966   else if (c == 'd' || c == 'r') {
3967     tok.next();
3968     symbol nm = get_name(1);
3969     if (nm.is_null()) {
3970       skip_alternative();
3971       return 0;
3972     }
3973     result = (c == 'd'
3974 	      ? request_dictionary.lookup(nm) != 0
3975 	      : number_reg_dictionary.lookup(nm) != 0);
3976   }
3977   else if (c == 'c') {
3978     tok.next();
3979     tok.skip();
3980     charinfo *ci = tok.get_char(1);
3981     if (ci == 0) {
3982       skip_alternative();
3983       return 0;
3984     }
3985     result = character_exists(ci, curenv);
3986     tok.next();
3987   }
3988   else if (tok.space())
3989     result = 0;
3990   else if (tok.delimiter()) {
3991     token delim = tok;
3992     int delim_level = input_stack::get_level();
3993     environment env1(curenv);
3994     environment env2(curenv);
3995     environment *oldenv = curenv;
3996     curenv = &env1;
3997     for (int i = 0; i < 2; i++) {
3998       for (;;) {
3999 	tok.next();
4000 	if (tok.newline() || tok.eof()) {
4001 	  warning(WARN_DELIM, "missing closing delimiter");
4002 	  tok.next();
4003 	  curenv = oldenv;
4004 	  return 0;
4005 	}
4006 	if (tok == delim
4007 	    && (compatible_flag || input_stack::get_level() == delim_level))
4008 	  break;
4009 	tok.process();
4010       }
4011       curenv = &env2;
4012     }
4013     node *n1 = env1.extract_output_line();
4014     node *n2 = env2.extract_output_line();
4015     result = same_node_list(n1, n2);
4016     delete_node_list(n1);
4017     delete_node_list(n2);
4018     tok.next();
4019     curenv = oldenv;
4020   }
4021   else {
4022     units n;
4023     if (!get_number(&n, 'u')) {
4024       skip_alternative();
4025       return 0;
4026     }
4027     else
4028       result = n > 0;
4029   }
4030   if (invert)
4031     result = !result;
4032   if (result)
4033     begin_alternative();
4034   else
4035     skip_alternative();
4036   return result;
4037 }
4038 
if_else_request()4039 void if_else_request()
4040 {
4041   if_else_stack.push(do_if_request());
4042 }
4043 
if_request()4044 void if_request()
4045 {
4046   do_if_request();
4047 }
4048 
else_request()4049 void else_request()
4050 {
4051   if (if_else_stack.is_empty()) {
4052     warning(WARN_EL, "unbalanced .el request");
4053     skip_alternative();
4054   }
4055   else {
4056     if (if_else_stack.pop())
4057       skip_alternative();
4058     else
4059       begin_alternative();
4060   }
4061 }
4062 
4063 static int while_depth = 0;
4064 static int while_break_flag = 0;
4065 
while_request()4066 void while_request()
4067 {
4068   macro mac;
4069   int escaped = 0;
4070   int level = 0;
4071   mac.append(new token_node(tok));
4072   for (;;) {
4073     node *n;
4074     int c = input_stack::get(&n);
4075     if (c == EOF)
4076       break;
4077     if (c == 0) {
4078       escaped = 0;
4079       mac.append(n);
4080     }
4081     else if (escaped) {
4082       if (c == '{')
4083 	level += 1;
4084       else if (c == '}')
4085 	level -= 1;
4086       escaped = 0;
4087       mac.append(c);
4088     }
4089     else {
4090       if (c == ESCAPE_LEFT_BRACE)
4091 	level += 1;
4092       else if (c == ESCAPE_RIGHT_BRACE)
4093 	level -= 1;
4094       else if (c == escape_char)
4095 	escaped = 1;
4096       mac.append(c);
4097       if (c == '\n' && level <= 0)
4098 	break;
4099     }
4100   }
4101   if (level != 0)
4102     error("unbalanced \\{ \\}");
4103   else {
4104     while_depth++;
4105     input_stack::add_boundary();
4106     for (;;) {
4107       input_stack::push(new string_iterator(mac, "while loop"));
4108       tok.next();
4109       if (!do_if_request()) {
4110 	while (input_stack::get(NULL) != EOF)
4111 	  ;
4112 	break;
4113       }
4114       process_input_stack();
4115       if (while_break_flag) {
4116 	while_break_flag = 0;
4117 	break;
4118       }
4119     }
4120     input_stack::remove_boundary();
4121     while_depth--;
4122   }
4123   tok.next();
4124 }
4125 
while_break_request()4126 void while_break_request()
4127 {
4128   if (!while_depth) {
4129     error("no while loop");
4130     skip_line();
4131   }
4132   else {
4133     while_break_flag = 1;
4134     while (input_stack::get(NULL) != EOF)
4135       ;
4136     tok.next();
4137   }
4138 }
4139 
while_continue_request()4140 void while_continue_request()
4141 {
4142   if (!while_depth) {
4143     error("no while loop");
4144     skip_line();
4145   }
4146   else {
4147     while (input_stack::get(NULL) != EOF)
4148       ;
4149     tok.next();
4150   }
4151 }
4152 
4153 // .so
4154 
source()4155 void source()
4156 {
4157   symbol nm = get_long_name(1);
4158   if (nm.is_null())
4159     skip_line();
4160   else {
4161     while (!tok.newline() && !tok.eof())
4162       tok.next();
4163     errno = 0;
4164     FILE *fp = fopen(nm.contents(), "r");
4165     if (fp)
4166       input_stack::push(new file_iterator(fp, nm.contents()));
4167     else
4168       error("can't open `%1': %2", nm.contents(), strerror(errno));
4169     tok.next();
4170   }
4171 }
4172 
asciify(int c)4173 const char *asciify(int c)
4174 {
4175   static char buf[3];
4176   buf[0] = escape_char == '\0' ? '\\' : escape_char;
4177   buf[1] = buf[2] = '\0';
4178   switch (c) {
4179   case ESCAPE_QUESTION:
4180     buf[1] = '?';
4181     break;
4182   case ESCAPE_AMPERSAND:
4183     buf[1] = '&';
4184     break;
4185   case ESCAPE_UNDERSCORE:
4186     buf[1] = '_';
4187     break;
4188   case ESCAPE_BAR:
4189     buf[1] = '|';
4190     break;
4191   case ESCAPE_CIRCUMFLEX:
4192     buf[1] = '^';
4193     break;
4194   case ESCAPE_LEFT_BRACE:
4195     buf[1] = '{';
4196     break;
4197   case ESCAPE_RIGHT_BRACE:
4198     buf[1] = '}';
4199     break;
4200   case ESCAPE_LEFT_QUOTE:
4201     buf[1] = '`';
4202     break;
4203   case ESCAPE_RIGHT_QUOTE:
4204     buf[1] = '\'';
4205     break;
4206   case ESCAPE_HYPHEN:
4207     buf[1] = '-';
4208     break;
4209   case ESCAPE_BANG:
4210     buf[1] = '!';
4211     break;
4212   case ESCAPE_c:
4213     buf[1] = 'c';
4214     break;
4215   case ESCAPE_e:
4216     buf[1] = 'e';
4217     break;
4218   case ESCAPE_E:
4219     buf[1] = 'E';
4220     break;
4221   case ESCAPE_PERCENT:
4222     buf[1] = '%';
4223     break;
4224   case ESCAPE_SPACE:
4225     buf[1] = ' ';
4226     break;
4227   default:
4228     if (illegal_input_char(c))
4229       buf[0] = '\0';
4230     else
4231       buf[0] = c;
4232     break;
4233   }
4234   return buf;
4235 }
4236 
4237 
input_char_description(int c)4238 const char *input_char_description(int c)
4239 {
4240   switch (c) {
4241   case '\n':
4242     return "a newline character";
4243   case '\b':
4244     return "a backspace character";
4245   case '\001':
4246     return "a leader character";
4247   case '\t':
4248     return "a tab character ";
4249   case ' ':
4250     return "a space character";
4251   case '\0':
4252     return "a node";
4253   }
4254   static char buf[sizeof("magic character code ") + 1 + INT_DIGITS];
4255   if (illegal_input_char(c)) {
4256     const char *s = asciify(c);
4257     if (*s) {
4258       buf[0] = '`';
4259       strcpy(buf + 1, s);
4260       strcat(buf, "'");
4261       return buf;
4262     }
4263     sprintf(buf, "magic character code %d", c);
4264     return buf;
4265   }
4266   if (csprint(c)) {
4267     buf[0] = '`';
4268     buf[1] = c;
4269     buf[2] = '\'';
4270     return buf;
4271   }
4272   sprintf(buf, "character code %d", c);
4273   return buf;
4274 }
4275 
4276 // .tm
4277 
terminal()4278 void terminal()
4279 {
4280   if (!tok.newline() && !tok.eof()) {
4281     int c;
4282     while ((c = get_copy(NULL)) == ' ' || c == '\t')
4283       ;
4284     for (; c != '\n' && c != EOF; c = get_copy(NULL))
4285       fputs(asciify(c), stderr);
4286   }
4287   fputc('\n', stderr);
4288   fflush(stderr);
4289   tok.next();
4290 }
4291 
4292 dictionary stream_dictionary(20);
4293 
do_open(int append)4294 void do_open(int append)
4295 {
4296   symbol stream = get_name(1);
4297   if (!stream.is_null()) {
4298     symbol filename = get_long_name(1);
4299     if (!filename.is_null()) {
4300       errno = 0;
4301       FILE *fp = fopen(filename.contents(), append ? "a" : "w");
4302       if (!fp) {
4303 	error("can't open `%1' for %2: %3",
4304 	      filename.contents(),
4305 	      append ? "appending" : "writing",
4306 	      strerror(errno));
4307 	fp = (FILE *)stream_dictionary.remove(stream);
4308       }
4309       else
4310 	fp = (FILE *)stream_dictionary.lookup(stream, fp);
4311       if (fp)
4312 	fclose(fp);
4313     }
4314   }
4315   skip_line();
4316 }
4317 
open_request()4318 void open_request()
4319 {
4320   do_open(0);
4321 }
4322 
opena_request()4323 void opena_request()
4324 {
4325   do_open(1);
4326 }
4327 
close_request()4328 void close_request()
4329 {
4330   symbol stream = get_name(1);
4331   if (!stream.is_null()) {
4332     FILE *fp = (FILE *)stream_dictionary.remove(stream);
4333     if (!fp)
4334       error("no stream named `%1'", stream.contents());
4335     else
4336       fclose(fp);
4337   }
4338   skip_line();
4339 }
4340 
write_request()4341 void write_request()
4342 {
4343   symbol stream = get_name(1);
4344   if (stream.is_null()) {
4345     skip_line();
4346     return;
4347   }
4348   FILE *fp = (FILE *)stream_dictionary.lookup(stream);
4349   if (!fp) {
4350     error("no stream named `%1'", stream.contents());
4351     skip_line();
4352     return;
4353   }
4354   int c;
4355   while ((c = get_copy(NULL)) == ' ')
4356     ;
4357   if (c == '"')
4358     c = get_copy(NULL);
4359   for (; c != '\n' && c != EOF; c = get_copy(NULL))
4360     fputs(asciify(c), fp);
4361   fputc('\n', fp);
4362   fflush(fp);
4363   tok.next();
4364 }
4365 
init_charset_table()4366 static void init_charset_table()
4367 {
4368   char buf[16];
4369   strcpy(buf, "char");
4370   for (int i = 0; i < 256; i++) {
4371     strcpy(buf + 4, itoa(i));
4372     charset_table[i] = get_charinfo(symbol(buf));
4373     charset_table[i]->set_ascii_code(i);
4374     if (csalpha(i))
4375       charset_table[i]->set_hyphenation_code(cmlower(i));
4376   }
4377   charset_table['.']->set_flags(charinfo::ENDS_SENTENCE);
4378   charset_table['?']->set_flags(charinfo::ENDS_SENTENCE);
4379   charset_table['!']->set_flags(charinfo::ENDS_SENTENCE);
4380   charset_table['-']->set_flags(charinfo::BREAK_AFTER);
4381   charset_table['"']->set_flags(charinfo::TRANSPARENT);
4382   charset_table['\'']->set_flags(charinfo::TRANSPARENT);
4383   charset_table[')']->set_flags(charinfo::TRANSPARENT);
4384   charset_table[']']->set_flags(charinfo::TRANSPARENT);
4385   charset_table['*']->set_flags(charinfo::TRANSPARENT);
4386   get_charinfo(symbol("dg"))->set_flags(charinfo::TRANSPARENT);
4387   get_charinfo(symbol("rq"))->set_flags(charinfo::TRANSPARENT);
4388   get_charinfo(symbol("em"))->set_flags(charinfo::BREAK_AFTER);
4389   get_charinfo(symbol("ul"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
4390   get_charinfo(symbol("rn"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
4391   get_charinfo(symbol("ru"))->set_flags(charinfo::OVERLAPS_HORIZONTALLY);
4392   get_charinfo(symbol("br"))->set_flags(charinfo::OVERLAPS_VERTICALLY);
4393   page_character = charset_table['%'];
4394 }
4395 
4396 static
do_translate(int translate_transparent)4397 void do_translate(int translate_transparent)
4398 {
4399   tok.skip();
4400   while (!tok.newline() && !tok.eof()) {
4401     if (tok.space()) {
4402       // This is a really bizarre troff feature.
4403       tok.next();
4404       translate_space_to_dummy = tok.dummy();
4405       if (tok.newline() || tok.eof())
4406 	break;
4407       tok.next();
4408       continue;
4409     }
4410     charinfo *ci1 = tok.get_char(1);
4411     if (ci1 == 0)
4412       break;
4413     tok.next();
4414     if (tok.newline() || tok.eof()) {
4415       ci1->set_special_translation(charinfo::TRANSLATE_SPACE,
4416 				   translate_transparent);
4417       break;
4418     }
4419     if (tok.space())
4420       ci1->set_special_translation(charinfo::TRANSLATE_SPACE,
4421 				   translate_transparent);
4422     else if (tok.dummy())
4423       ci1->set_special_translation(charinfo::TRANSLATE_DUMMY,
4424 				   translate_transparent);
4425     else if (tok.hyphen_indicator())
4426       ci1->set_special_translation(charinfo::TRANSLATE_HYPHEN_INDICATOR,
4427 				   translate_transparent);
4428     else {
4429       charinfo *ci2 = tok.get_char(1);
4430       if (ci2 == 0)
4431 	break;
4432       if (ci1 == ci2)
4433 	ci1->set_translation(0, translate_transparent);
4434       else
4435 	ci1->set_translation(ci2, translate_transparent);
4436     }
4437     tok.next();
4438   }
4439   skip_line();
4440 }
4441 
translate()4442 void translate()
4443 {
4444   do_translate(1);
4445 }
4446 
translate_no_transparent()4447 void translate_no_transparent()
4448 {
4449   do_translate(0);
4450 }
4451 
char_flags()4452 void char_flags()
4453 {
4454   int flags;
4455   if (get_integer(&flags))
4456     while (has_arg()) {
4457       charinfo *ci = tok.get_char(1);
4458       if (ci) {
4459 	charinfo *tem = ci->get_translation();
4460 	if (tem)
4461 	  ci = tem;
4462 	ci->set_flags(flags);
4463       }
4464       tok.next();
4465     }
4466   skip_line();
4467 }
4468 
hyphenation_code()4469 void hyphenation_code()
4470 {
4471   tok.skip();
4472   while (!tok.newline() && !tok.eof()) {
4473     charinfo *ci = tok.get_char(1);
4474     if (ci == 0)
4475       break;
4476     tok.next();
4477     tok.skip();
4478     unsigned char c = tok.ch();
4479     if (c == 0) {
4480       error("hyphenation code must be ordinary character");
4481       break;
4482     }
4483     if (csdigit(c)) {
4484       error("hyphenation code cannot be digit");
4485       break;
4486     }
4487     ci->set_hyphenation_code(c);
4488     tok.next();
4489   }
4490   skip_line();
4491 }
4492 
get_char(int required)4493 charinfo *token::get_char(int required)
4494 {
4495   if (type == TOKEN_CHAR)
4496     return charset_table[c];
4497   if (type == TOKEN_SPECIAL)
4498     return get_charinfo(nm);
4499   if (type == TOKEN_NUMBERED_CHAR)
4500     return get_charinfo_by_number(val);
4501   if (type == TOKEN_ESCAPE) {
4502     if (escape_char != 0)
4503       return charset_table[escape_char];
4504     else {
4505       error("`\\e' used while no current escape character");
4506       return 0;
4507     }
4508   }
4509   if (required) {
4510     if (type == TOKEN_EOF || type == TOKEN_NEWLINE)
4511       warning(WARN_MISSING, "missing normal or special character");
4512     else
4513       error("normal or special character expected (got %1)", description());
4514   }
4515   return 0;
4516 }
4517 
get_optional_char()4518 charinfo *get_optional_char()
4519 {
4520   while (tok.space())
4521     tok.next();
4522   charinfo *ci = tok.get_char();
4523   if (!ci) {
4524     if (!tok.newline() && !tok.eof() && !tok.right_brace() && !tok.tab())
4525       error("normal or special character expected (got %1): "
4526 	    "treated as missing",
4527 	    tok.description());
4528   }
4529   else
4530     tok.next();
4531   return ci;
4532 }
4533 
add_to_node_list(node ** pp)4534 int token::add_to_node_list(node **pp)
4535 {
4536   hunits w;
4537   node *n = 0;
4538   switch (type) {
4539   case TOKEN_CHAR:
4540     *pp = (*pp)->add_char(charset_table[c], curenv, &w);
4541     break;
4542   case TOKEN_DUMMY:
4543     n = new dummy_node;
4544     break;
4545   case TOKEN_ESCAPE:
4546     if (escape_char != 0)
4547       *pp = (*pp)->add_char(charset_table[escape_char], curenv, &w);
4548     break;
4549   case TOKEN_HYPHEN_INDICATOR:
4550     *pp = (*pp)->add_discretionary_hyphen();
4551     break;
4552   case TOKEN_ITALIC_CORRECTION:
4553     *pp = (*pp)->add_italic_correction(&w);
4554     break;
4555   case TOKEN_LEFT_BRACE:
4556     break;
4557   case TOKEN_MARK_INPUT:
4558     set_number_reg(nm, curenv->get_input_line_position().to_units());
4559     break;
4560   case TOKEN_NODE:
4561     n = nd;
4562     nd = 0;
4563     break;
4564   case TOKEN_NUMBERED_CHAR:
4565     *pp = (*pp)->add_char(get_charinfo_by_number(val), curenv, &w);
4566     break;
4567   case TOKEN_RIGHT_BRACE:
4568     break;
4569   case TOKEN_SPACE:
4570     n = new hmotion_node(curenv->get_space_width());
4571     break;
4572   case TOKEN_SPECIAL:
4573     *pp = (*pp)->add_char(get_charinfo(nm), curenv, &w);
4574     break;
4575   default:
4576     return 0;
4577   }
4578   if (n) {
4579     n->next = *pp;
4580     *pp = n;
4581   }
4582   return 1;
4583 }
4584 
process()4585 void token::process()
4586 {
4587   if (possibly_handle_first_page_transition())
4588     return;
4589   switch (type) {
4590   case TOKEN_BACKSPACE:
4591     curenv->add_node(new hmotion_node(-curenv->get_space_width()));
4592     break;
4593   case TOKEN_CHAR:
4594     curenv->add_char(charset_table[c]);
4595     break;
4596   case TOKEN_DUMMY:
4597     curenv->add_node(new dummy_node);
4598     break;
4599   case TOKEN_EOF:
4600     assert(0);
4601     break;
4602   case TOKEN_EMPTY:
4603     assert(0);
4604     break;
4605   case TOKEN_ESCAPE:
4606     if (escape_char != 0)
4607       curenv->add_char(charset_table[escape_char]);
4608     break;
4609   case TOKEN_BEGIN_TRAP:
4610   case TOKEN_END_TRAP:
4611   case TOKEN_PAGE_EJECTOR:
4612     // these are all handled in process_input_stack()
4613     break;
4614   case TOKEN_HYPHEN_INDICATOR:
4615     curenv->add_hyphen_indicator();
4616     break;
4617   case TOKEN_INTERRUPT:
4618     curenv->interrupt();
4619     break;
4620   case TOKEN_ITALIC_CORRECTION:
4621     curenv->add_italic_correction();
4622     break;
4623   case TOKEN_LEADER:
4624     curenv->handle_tab(1);
4625     break;
4626   case TOKEN_LEFT_BRACE:
4627     break;
4628   case TOKEN_MARK_INPUT:
4629     set_number_reg(nm, curenv->get_input_line_position().to_units());
4630     break;
4631   case TOKEN_NEWLINE:
4632     curenv->newline();
4633     break;
4634   case TOKEN_NODE:
4635     curenv->add_node(nd);
4636     nd = 0;
4637     break;
4638   case TOKEN_NUMBERED_CHAR:
4639     curenv->add_char(get_charinfo_by_number(val));
4640     break;
4641   case TOKEN_REQUEST:
4642     // handled in process_input_stack
4643     break;
4644   case TOKEN_RIGHT_BRACE:
4645     break;
4646   case TOKEN_SPACE:
4647     curenv->space();
4648     break;
4649   case TOKEN_SPECIAL:
4650     curenv->add_char(get_charinfo(nm));
4651     break;
4652   case TOKEN_SPREAD:
4653     curenv->spread();
4654     break;
4655   case TOKEN_TAB:
4656     curenv->handle_tab(0);
4657     break;
4658   case TOKEN_TRANSPARENT:
4659     break;
4660   default:
4661     assert(0);
4662   }
4663 }
4664 
4665 class nargs_reg : public reg {
4666 public:
4667   const char *get_string();
4668 };
4669 
get_string()4670 const char *nargs_reg::get_string()
4671 {
4672   return itoa(input_stack::nargs());
4673 }
4674 
4675 class lineno_reg : public reg {
4676 public:
4677   const char *get_string();
4678 };
4679 
get_string()4680 const char *lineno_reg::get_string()
4681 {
4682   int line;
4683   const char *file;
4684   if (!input_stack::get_location(0, &file, &line))
4685     line = 0;
4686   return itoa(line);
4687 }
4688 
4689 
4690 class writable_lineno_reg : public general_reg {
4691 public:
4692   writable_lineno_reg();
4693   void set_value(units);
4694   int get_value(units *);
4695 };
4696 
writable_lineno_reg()4697 writable_lineno_reg::writable_lineno_reg()
4698 {
4699 }
4700 
get_value(units * res)4701 int writable_lineno_reg::get_value(units *res)
4702 {
4703   int line;
4704   const char *file;
4705   if (!input_stack::get_location(0, &file, &line))
4706     return 0;
4707   *res = line;
4708   return 1;
4709 }
4710 
set_value(units n)4711 void writable_lineno_reg::set_value(units n)
4712 {
4713   input_stack::set_location(0, n);
4714 }
4715 
4716 class filename_reg : public reg {
4717 public:
4718   const char *get_string();
4719 };
4720 
get_string()4721 const char *filename_reg::get_string()
4722 {
4723   int line;
4724   const char *file;
4725   if (input_stack::get_location(0, &file, &line))
4726     return file;
4727   else
4728     return 0;
4729 }
4730 
4731 
4732 class constant_reg : public reg {
4733   const char *s;
4734 public:
4735   constant_reg(const char *);
4736   const char *get_string();
4737 };
4738 
constant_reg(const char * p)4739 constant_reg::constant_reg(const char *p) : s(p)
4740 {
4741 }
4742 
get_string()4743 const char *constant_reg::get_string()
4744 {
4745   return s;
4746 }
4747 
constant_int_reg(int * q)4748 constant_int_reg::constant_int_reg(int *q) : p(q)
4749 {
4750 }
4751 
get_string()4752 const char *constant_int_reg::get_string()
4753 {
4754   return itoa(*p);
4755 }
4756 
abort_request()4757 void abort_request()
4758 {
4759   int c;
4760   while ((c = get_copy(0)) == ' ')
4761     ;
4762   if (c == EOF || c == '\n')
4763     fputs("User Abort.", stderr);
4764   else {
4765     for (; c != '\n' && c != EOF; c = get_copy(NULL))
4766       fputs(asciify(c), stderr);
4767   }
4768   fputc('\n', stderr);
4769   cleanup_and_exit(1);
4770 }
4771 
read_string()4772 char *read_string()
4773 {
4774   int len = 256;
4775   char *s = new char[len];
4776   int c;
4777   while ((c = get_copy(0)) == ' ')
4778     ;
4779   int i = 0;
4780   while (c != '\n' && c != EOF) {
4781     if (!illegal_input_char(c)) {
4782       if (i + 2 > len) {
4783 	char *tem = s;
4784 	s = new char[len*2];
4785 	memcpy(s, tem, len);
4786 	len *= 2;
4787 	a_delete tem;
4788       }
4789       s[i++] = c;
4790     }
4791     c = get_copy(0);
4792   }
4793   s[i] = '\0';
4794   tok.next();
4795   if (i == 0) {
4796     a_delete s;
4797     return 0;
4798   }
4799   return s;
4800 }
4801 
pipe_output()4802 void pipe_output()
4803 {
4804   if (the_output) {
4805     error("can't pipe: output already started");
4806     skip_line();
4807   }
4808   else {
4809     if ((pipe_command = read_string()) == 0)
4810       error("can't pipe to empty command");
4811   }
4812 }
4813 
4814 static int system_status;
4815 
system_request()4816 void system_request()
4817 {
4818   char *command = read_string();
4819   if (!command)
4820     error("empty command");
4821   else {
4822     system_status = system(command);
4823     a_delete command;
4824   }
4825 }
4826 
copy_file()4827 void copy_file()
4828 {
4829   if (curdiv == topdiv && topdiv->before_first_page) {
4830     handle_initial_request(COPY_FILE_REQUEST);
4831     return;
4832   }
4833   symbol filename = get_long_name(1);
4834   while (!tok.newline() && !tok.eof())
4835     tok.next();
4836   if (break_flag)
4837     curenv->do_break();
4838   if (!filename.is_null())
4839     curdiv->copy_file(filename.contents());
4840   tok.next();
4841 }
4842 
4843 #ifdef COLUMN
4844 
vjustify()4845 void vjustify()
4846 {
4847   if (curdiv == topdiv && topdiv->before_first_page) {
4848     handle_initial_request(VJUSTIFY_REQUEST);
4849     return;
4850   }
4851   symbol type = get_long_name(1);
4852   if (!type.is_null())
4853     curdiv->vjustify(type);
4854   skip_line();
4855 }
4856 
4857 #endif /* COLUMN */
4858 
transparent_file()4859 void transparent_file()
4860 {
4861   if (curdiv == topdiv && topdiv->before_first_page) {
4862     handle_initial_request(TRANSPARENT_FILE_REQUEST);
4863     return;
4864   }
4865   symbol filename = get_long_name(1);
4866   while (!tok.newline() && !tok.eof())
4867     tok.next();
4868   if (break_flag)
4869     curenv->do_break();
4870   if (!filename.is_null()) {
4871     errno = 0;
4872     FILE *fp = fopen(filename.contents(), "r");
4873     if (!fp)
4874       error("can't open `%1': %2", filename.contents(), strerror(errno));
4875     else {
4876       int bol = 1;
4877       for (;;) {
4878 	int c = getc(fp);
4879 	if (c == EOF)
4880 	  break;
4881 	if (illegal_input_char(c))
4882 	  warning(WARN_INPUT, "illegal input character code %1", int(c));
4883 	else {
4884 	  curdiv->transparent_output(c);
4885 	  bol = c == '\n';
4886 	}
4887       }
4888       if (!bol)
4889 	curdiv->transparent_output('\n');
4890       fclose(fp);
4891     }
4892   }
4893   tok.next();
4894 }
4895 
4896 class page_range {
4897   int first;
4898   int last;
4899 public:
4900   page_range *next;
4901   page_range(int, int, page_range *);
4902   int contains(int n);
4903 };
4904 
page_range(int i,int j,page_range * p)4905 page_range::page_range(int i, int j, page_range *p)
4906 : first(i), last(j), next(p)
4907 {
4908 }
4909 
contains(int n)4910 int page_range::contains(int n)
4911 {
4912   return n >= first && (last <= 0 || n <= last);
4913 }
4914 
4915 page_range *output_page_list = 0;
4916 
in_output_page_list(int n)4917 int in_output_page_list(int n)
4918 {
4919   if (!output_page_list)
4920     return 1;
4921   for (page_range *p = output_page_list; p; p = p->next)
4922     if (p->contains(n))
4923       return 1;
4924   return 0;
4925 }
4926 
parse_output_page_list(char * p)4927 static void parse_output_page_list(char *p)
4928 {
4929   for (;;) {
4930     int i;
4931     if (*p == '-')
4932       i = 1;
4933     else if (csdigit(*p)) {
4934       i = 0;
4935       do
4936 	i = i*10 + *p++ - '0';
4937       while (csdigit(*p));
4938     }
4939     else
4940       break;
4941     int j;
4942     if (*p == '-') {
4943       p++;
4944       j = 0;
4945       if (csdigit(*p)) {
4946 	do
4947 	  j = j*10 + *p++ - '0';
4948 	while (csdigit(*p));
4949       }
4950     }
4951     else
4952       j = i;
4953     output_page_list = new page_range(i, j, output_page_list);
4954     if (*p != ',')
4955       break;
4956     ++p;
4957   }
4958   if (*p != '\0') {
4959     error("bad output page list");
4960     output_page_list = 0;
4961   }
4962 }
4963 
open_mac_file(const char * mac,char ** path)4964 static FILE *open_mac_file(const char *mac, char **path)
4965 {
4966   char *s = new char[strlen(mac)+strlen(MACRO_PREFIX)+1];
4967   strcpy(s, MACRO_PREFIX);
4968   strcat(s, mac);
4969   FILE *fp = macro_path.open_file(s, path);
4970   a_delete s;
4971   return fp;
4972 }
4973 
process_macro_file(const char * mac)4974 static void process_macro_file(const char *mac)
4975 {
4976   char *path;
4977   FILE *fp = open_mac_file(mac, &path);
4978   if (!fp)
4979     fatal("can't find macro file %1", mac);
4980   const char *s = symbol(path).contents();
4981   a_delete path;
4982   input_stack::push(new file_iterator(fp, s));
4983   tok.next();
4984   process_input_stack();
4985 }
4986 
process_startup_file()4987 static void process_startup_file()
4988 {
4989   char *path;
4990   FILE *fp = macro_path.open_file(STARTUP_FILE, &path);
4991   if (fp) {
4992     input_stack::push(new file_iterator(fp, symbol(path).contents()));
4993     a_delete path;
4994     tok.next();
4995     process_input_stack();
4996   }
4997 }
4998 
macro_source()4999 void macro_source()
5000 {
5001   symbol nm = get_long_name(1);
5002   if (nm.is_null())
5003     skip_line();
5004   else {
5005     while (!tok.newline() && !tok.eof())
5006       tok.next();
5007     char *path;
5008     FILE *fp = macro_path.open_file(nm.contents(), &path);
5009     if (fp) {
5010       input_stack::push(new file_iterator(fp, symbol(path).contents()));
5011       a_delete path;
5012     }
5013     else
5014       error("can't find macro file `%1'", nm.contents());
5015     tok.next();
5016   }
5017 }
5018 
process_input_file(const char * name)5019 static void process_input_file(const char *name)
5020 {
5021   FILE *fp;
5022   if (strcmp(name, "-") == 0) {
5023     clearerr(stdin);
5024     fp = stdin;
5025   }
5026   else {
5027     errno = 0;
5028     fp = fopen(name, "r");
5029     if (!fp)
5030       fatal("can't open `%1': %2", name, strerror(errno));
5031   }
5032   input_stack::push(new file_iterator(fp, name));
5033   tok.next();
5034   process_input_stack();
5035 }
5036 
5037 // make sure the_input is empty before calling this
5038 
evaluate_expression(const char * expr,units * res)5039 static int evaluate_expression(const char *expr, units *res)
5040 {
5041   input_stack::push(make_temp_iterator(expr));
5042   tok.next();
5043   int success = get_number(res, 'u');
5044   while (input_stack::get(NULL) != EOF)
5045     ;
5046   return success;
5047 }
5048 
do_register_assignment(const char * s)5049 static void do_register_assignment(const char *s)
5050 {
5051   const char *p = strchr(s, '=');
5052   if (!p) {
5053     char buf[2];
5054     buf[0] = s[0];
5055     buf[1] = 0;
5056     units n;
5057     if (evaluate_expression(s + 1, &n))
5058       set_number_reg(buf, n);
5059   }
5060   else {
5061     char *buf = new char[p - s + 1];
5062     memcpy(buf, s, p - s);
5063     buf[p - s] = 0;
5064     units n;
5065     if (evaluate_expression(p + 1, &n))
5066       set_number_reg(buf, n);
5067     a_delete buf;
5068   }
5069 }
5070 
set_string(const char * name,const char * value)5071 static void set_string(const char *name, const char *value)
5072 {
5073   macro *m = new macro;
5074   for (const char *p = value; *p; p++)
5075     if (!illegal_input_char((unsigned char)*p))
5076       m->append(*p);
5077   request_dictionary.define(name, m);
5078 }
5079 
5080 
do_string_assignment(const char * s)5081 static void do_string_assignment(const char *s)
5082 {
5083   const char *p = strchr(s, '=');
5084   if (!p) {
5085     char buf[2];
5086     buf[0] = s[0];
5087     buf[1] = 0;
5088     set_string(buf, s + 1);
5089   }
5090   else {
5091     char *buf = new char[p - s + 1];
5092     memcpy(buf, s, p - s);
5093     buf[p - s] = 0;
5094     set_string(buf, p + 1);
5095     a_delete buf;
5096   }
5097 }
5098 
5099 struct string_list {
5100   const char *s;
5101   string_list *next;
string_liststring_list5102   string_list(const char *ss) : s(ss), next(0) {}
5103 };
5104 
add_string(const char * s,string_list ** p)5105 static void add_string(const char *s, string_list **p)
5106 {
5107   while (*p)
5108     p = &((*p)->next);
5109   *p = new string_list(s);
5110 }
5111 
usage(const char * prog)5112 void usage(const char *prog)
5113 {
5114   errprint(
5115 "usage: %1 -abivzCER -wname -Wname -dcstring -mname -nN -olist -rcN\n"
5116 "       -Tname -Fdir -Mdir [ files ]\n",
5117 	  prog);
5118   exit(USAGE_EXIT_CODE);
5119 }
5120 
main(int argc,char ** argv)5121 int main(int argc, char **argv)
5122 {
5123   program_name = argv[0];
5124   static char stderr_buf[BUFSIZ];
5125   setbuf(stderr, stderr_buf);
5126   int c;
5127   string_list *macros = 0;
5128   string_list *register_assignments = 0;
5129   string_list *string_assignments = 0;
5130   int iflag = 0;
5131   int tflag = 0;
5132   int fflag = 0;
5133   int nflag = 0;
5134   int no_rc = 0;		// don't process troffrc
5135   int next_page_number;
5136   opterr = 0;
5137   hresolution = vresolution = 1;
5138   while ((c = getopt(argc, argv, "abivw:W:zCEf:m:n:o:r:d:F:M:T:tqs:R"))
5139 	 != EOF)
5140     switch(c) {
5141     case 'v':
5142       {
5143 	extern const char *version_string;
5144 	fprintf(stderr, "GNU troff version %s\n", version_string);
5145 	fflush(stderr);
5146 	break;
5147       }
5148     case 'T':
5149       device = optarg;
5150       tflag = 1;
5151       break;
5152     case 'C':
5153       compatible_flag = 1;
5154       break;
5155     case 'M':
5156       macro_path.command_line_dir(optarg);
5157       break;
5158     case 'F':
5159       font::command_line_font_dir(optarg);
5160       break;
5161     case 'm':
5162       add_string(optarg, &macros);
5163       break;
5164     case 'E':
5165       inhibit_errors = 1;
5166       break;
5167     case 'R':
5168       no_rc = 1;
5169       break;
5170     case 'w':
5171       enable_warning(optarg);
5172       break;
5173     case 'W':
5174       disable_warning(optarg);
5175       break;
5176     case 'i':
5177       iflag = 1;
5178       break;
5179     case 'b':
5180       backtrace_flag = 1;
5181       break;
5182     case 'a':
5183       ascii_output_flag = 1;
5184       break;
5185     case 'z':
5186       suppress_output_flag = 1;
5187       break;
5188     case 'n':
5189       if (sscanf(optarg, "%d", &next_page_number) == 1)
5190 	nflag++;
5191       else
5192 	error("bad page number");
5193       break;
5194     case 'o':
5195       parse_output_page_list(optarg);
5196       break;
5197     case 'd':
5198       if (*optarg == '\0')
5199 	error("`-d' requires non-empty argument");
5200       else
5201 	add_string(optarg, &string_assignments);
5202       break;
5203     case 'r':
5204       if (*optarg == '\0')
5205 	error("`-r' requires non-empty argument");
5206       else
5207 	add_string(optarg, &register_assignments);
5208       break;
5209     case 'f':
5210       default_family = symbol(optarg);
5211       fflag = 1;
5212       break;
5213     case 'q':
5214     case 's':
5215     case 't':
5216       // silently ignore these
5217       break;
5218     case '?':
5219       usage(argv[0]);
5220     default:
5221       assert(0);
5222     }
5223   set_string(".T", device);
5224   init_charset_table();
5225   if (!font::load_desc())
5226     fatal("sorry, I can't continue");
5227   units_per_inch = font::res;
5228   hresolution = font::hor;
5229   vresolution = font::vert;
5230   sizescale = font::sizescale;
5231   tcommand_flag = font::tcommand;
5232   if (!fflag && font::family != 0 && *font::family != '\0')
5233     default_family = symbol(font::family);
5234   font_size::init_size_table(font::sizes);
5235   int i;
5236   int j = 1;
5237   if (font::style_table) {
5238     for (i = 0; font::style_table[i]; i++)
5239       mount_style(j++, symbol(font::style_table[i]));
5240   }
5241   for (i = 0; font::font_name_table[i]; i++, j++)
5242     // In the DESC file a font name of 0 (zero) means leave this
5243     // position empty.
5244     if (strcmp(font::font_name_table[i], "0") != 0)
5245       mount_font(j, symbol(font::font_name_table[i]));
5246   curdiv = topdiv = new top_level_diversion;
5247   if (nflag)
5248     topdiv->set_next_page_number(next_page_number);
5249   init_input_requests();
5250   init_env_requests();
5251   init_div_requests();
5252 #ifdef COLUMN
5253   init_column_requests();
5254 #endif /* COLUMN */
5255   init_node_requests();
5256   number_reg_dictionary.define(".T", new constant_reg(tflag ? "1" : "0"));
5257   init_registers();
5258   init_reg_requests();
5259   init_hyphen_requests();
5260   init_environments();
5261   while (string_assignments) {
5262     do_string_assignment(string_assignments->s);
5263     string_list *tem = string_assignments;
5264     string_assignments = string_assignments->next;
5265     delete tem;
5266   }
5267   while (register_assignments) {
5268     do_register_assignment(register_assignments->s);
5269     string_list *tem = register_assignments;
5270     register_assignments = register_assignments->next;
5271     delete tem;
5272   }
5273   if (!no_rc)
5274     process_startup_file();
5275   while (macros) {
5276     process_macro_file(macros->s);
5277     string_list *tem = macros;
5278     macros = macros->next;
5279     delete tem;
5280   }
5281   for (i = optind; i < argc; i++)
5282     process_input_file(argv[i]);
5283   if (optind >= argc || iflag)
5284     process_input_file("-");
5285   exit_troff();
5286 }
5287 
warn_request()5288 void warn_request()
5289 {
5290   int n;
5291   if (has_arg() && get_integer(&n)) {
5292     if (n & ~WARN_TOTAL) {
5293       warning(WARN_RANGE, "warning mask must be between 0 and %1", WARN_TOTAL);
5294       n &= WARN_TOTAL;
5295     }
5296     warning_mask = n;
5297   }
5298   else
5299     warning_mask = WARN_TOTAL;
5300   skip_line();
5301 }
5302 
init_registers()5303 static void init_registers()
5304 {
5305 #ifdef LONG_FOR_TIME_T
5306   long
5307 #else /* not LONG_FOR_TIME_T */
5308   time_t
5309 #endif /* not LONG_FOR_TIME_T */
5310     t = time(0);
5311   // Use struct here to work around misfeature in old versions of g++.
5312   struct tm *tt = localtime(&t);
5313   set_number_reg("dw", int(tt->tm_wday + 1));
5314   set_number_reg("dy", int(tt->tm_mday));
5315   set_number_reg("mo", int(tt->tm_mon + 1));
5316   set_number_reg("yr", int(tt->tm_year));
5317   set_number_reg("$$", getpid());
5318   number_reg_dictionary.define(".A",
5319 			       new constant_reg(ascii_output_flag
5320 						? "1"
5321 						: "0"));
5322 }
5323 
init_input_requests()5324 void init_input_requests()
5325 {
5326   init_request("ds", define_string);
5327   init_request("as", append_string);
5328   init_request("de", define_macro);
5329   init_request("am", append_macro);
5330   init_request("ig", ignore);
5331   init_request("rm", remove_macro);
5332   init_request("rn", rename_macro);
5333   init_request("if", if_request);
5334   init_request("ie", if_else_request);
5335   init_request("el", else_request);
5336   init_request("so", source);
5337   init_request("nx", next_file);
5338   init_request("pm", print_macros);
5339   init_request("eo", escape_off);
5340   init_request("ec", set_escape_char);
5341   init_request("pc", set_page_character);
5342   init_request("tm", terminal);
5343   init_request("ex", exit_request);
5344   init_request("em", end_macro);
5345   init_request("tr", translate);
5346   init_request("trnt", translate_no_transparent);
5347   init_request("ab", abort_request);
5348   init_request("pi", pipe_output);
5349   init_request("cf", copy_file);
5350   init_request("sy", system_request);
5351   init_request("lf", line_file);
5352   init_request("cflags", char_flags);
5353   init_request("shift", shift);
5354   init_request("rd", read_request);
5355   init_request("cp", compatible);
5356   init_request("char", define_character);
5357   init_request("rchar", remove_character);
5358   init_request("hcode", hyphenation_code);
5359   init_request("while", while_request);
5360   init_request("break", while_break_request);
5361   init_request("continue", while_continue_request);
5362   init_request("als", alias_macro);
5363   init_request("backtrace", backtrace_request);
5364   init_request("chop", chop_macro);
5365   init_request("asciify", asciify_macro);
5366   init_request("warn", warn_request);
5367   init_request("open", open_request);
5368   init_request("opena", opena_request);
5369   init_request("close", close_request);
5370   init_request("write", write_request);
5371   init_request("trf", transparent_file);
5372 #ifdef WIDOW_CONTROL
5373   init_request("fpl", flush_pending_lines);
5374 #endif /* WIDOW_CONTROL */
5375   init_request("nroff", nroff_request);
5376   init_request("troff", troff_request);
5377 #ifdef COLUMN
5378   init_request("vj", vjustify);
5379 #endif /* COLUMN */
5380   init_request("mso", macro_source);
5381   init_request("do", do_request);
5382   number_reg_dictionary.define("systat", new variable_reg(&system_status));
5383   number_reg_dictionary.define("slimit",
5384 			       new variable_reg(&input_stack::limit));
5385   number_reg_dictionary.define(".$", new nargs_reg);
5386   number_reg_dictionary.define(".c", new lineno_reg);
5387   number_reg_dictionary.define("c.", new writable_lineno_reg);
5388   number_reg_dictionary.define(".F", new filename_reg);
5389   number_reg_dictionary.define(".C", new constant_int_reg(&compatible_flag));
5390   number_reg_dictionary.define(".H", new constant_int_reg(&hresolution));
5391   number_reg_dictionary.define(".V", new constant_int_reg(&vresolution));
5392   number_reg_dictionary.define(".R", new constant_reg("10000"));
5393   extern const char *major_version;
5394   number_reg_dictionary.define(".x", new constant_reg(major_version));
5395   extern const char *minor_version;
5396   number_reg_dictionary.define(".y", new constant_reg(minor_version));
5397   number_reg_dictionary.define(".g", new constant_reg("1"));
5398   number_reg_dictionary.define(".warn", new constant_int_reg(&warning_mask));
5399 }
5400 
5401 object_dictionary request_dictionary(501);
5402 
init_request(const char * s,REQUEST_FUNCP f)5403 void init_request(const char *s, REQUEST_FUNCP f)
5404 {
5405   request_dictionary.define(s, new request(f));
5406 }
5407 
lookup_request(symbol nm)5408 static request_or_macro *lookup_request(symbol nm)
5409 {
5410   assert(!nm.is_null());
5411   request_or_macro *p = (request_or_macro *)request_dictionary.lookup(nm);
5412   if (p == 0) {
5413     warning(WARN_MAC, "`%1' not defined", nm.contents());
5414     p = new macro;
5415     request_dictionary.define(nm, p);
5416   }
5417   return p;
5418 }
5419 
5420 
charinfo_to_node_list(charinfo * ci,const environment * envp)5421 node *charinfo_to_node_list(charinfo *ci, const environment *envp)
5422 {
5423   // Don't interpret character definitions in compatible mode.
5424   int old_compatible_flag = compatible_flag;
5425   compatible_flag = 0;
5426   int old_escape_char = escape_char;
5427   escape_char = '\\';
5428   macro *mac = ci->set_macro(0);
5429   assert(mac != 0);
5430   environment *oldenv = curenv;
5431   environment env(envp);
5432   curenv = &env;
5433   curenv->set_composite();
5434   token old_tok = tok;
5435   input_stack::add_boundary();
5436   string_iterator *si = new string_iterator(*mac, "composite character", ci->nm);
5437   input_stack::push(si);
5438   // we don't use process_input_stack, because we don't want to recognise
5439   // requests
5440   for (;;) {
5441     tok.next();
5442     if (tok.eof())
5443       break;
5444     if (tok.newline()) {
5445       error("composite character mustn't contain newline");
5446       while (!tok.eof())
5447 	tok.next();
5448       break;
5449     }
5450     else
5451       tok.process();
5452   }
5453   node *n = curenv->extract_output_line();
5454   input_stack::remove_boundary();
5455   ci->set_macro(mac);
5456   tok = old_tok;
5457   curenv = oldenv;
5458   compatible_flag = old_compatible_flag;
5459   escape_char = old_escape_char;
5460   return n;
5461 }
5462 
read_draw_node()5463 static node *read_draw_node()
5464 {
5465   token start;
5466   start.next();
5467   if (!start.delimiter(1)){
5468     do {
5469       tok.next();
5470     } while (tok != start && !tok.newline() && !tok.eof());
5471   }
5472   else {
5473     tok.next();
5474     if (tok == start)
5475       error("missing argument");
5476     else {
5477       unsigned char type = tok.ch();
5478       tok.next();
5479       int maxpoints = 10;
5480       hvpair *point = new hvpair[maxpoints];
5481       int npoints = 0;
5482       int no_last_v = 0;
5483       int err = 0;
5484       int i;
5485       for (i = 0; tok != start; i++) {
5486 	if (i == maxpoints) {
5487 	  hvpair *oldpoint = point;
5488 	  point = new hvpair[maxpoints*2];
5489 	  for (int j = 0; j < maxpoints; j++)
5490 	    point[j] = oldpoint[j];
5491 	  maxpoints *= 2;
5492 	  a_delete oldpoint;
5493 	}
5494 	if (!get_hunits(&point[i].h, 'm')) {
5495 	  err = 1;
5496 	  break;
5497 	}
5498 	++npoints;
5499 	tok.skip();
5500 	point[i].v = V0;
5501 	if (tok == start) {
5502 	  no_last_v = 1;
5503 	  break;
5504 	}
5505 	if (!get_vunits(&point[i].v, 'v')) {
5506 	  err = 1;
5507 	  break;
5508 	}
5509 	tok.skip();
5510       }
5511       while (tok != start && !tok.newline() && !tok.eof())
5512 	tok.next();
5513       if (!err) {
5514 	switch (type) {
5515 	case 'l':
5516 	  if (npoints != 1 || no_last_v) {
5517 	    error("two arguments needed for line");
5518 	    npoints = 1;
5519 	  }
5520 	  break;
5521 	case 'c':
5522 	  if (npoints != 1 || !no_last_v) {
5523 	    error("one argument needed for circle");
5524 	    npoints = 1;
5525 	    point[0].v = V0;
5526 	  }
5527 	  break;
5528 	case 'e':
5529 	  if (npoints != 1 || no_last_v) {
5530 	    error("two arguments needed for ellipse");
5531 	    npoints = 1;
5532 	  }
5533 	  break;
5534 	case 'a':
5535 	  if (npoints != 2 || no_last_v) {
5536 	    error("four arguments needed for arc");
5537 	    npoints = 2;
5538 	  }
5539 	  break;
5540 	case '~':
5541 	  if (no_last_v)
5542 	    error("even number of arguments needed for spline");
5543 	  break;
5544 	default:
5545 	  // silently pass it through
5546 	  break;
5547 	}
5548 	draw_node *dn = new draw_node(type, point, npoints,
5549 				      curenv->get_font_size());
5550 	a_delete point;
5551 	return dn;
5552       }
5553       else {
5554 	a_delete point;
5555       }
5556     }
5557   }
5558   return 0;
5559 }
5560 
5561 static struct {
5562   const char *name;
5563   int mask;
5564 } warning_table[] = {
5565   "char", WARN_CHAR,
5566   "range", WARN_RANGE,
5567   "break", WARN_BREAK,
5568   "delim", WARN_DELIM,
5569   "el", WARN_EL,
5570   "scale", WARN_SCALE,
5571   "number", WARN_NUMBER,
5572   "syntax", WARN_SYNTAX,
5573   "tab", WARN_TAB,
5574   "right-brace", WARN_RIGHT_BRACE,
5575   "missing", WARN_MISSING,
5576   "input", WARN_INPUT,
5577   "escape", WARN_ESCAPE,
5578   "space", WARN_SPACE,
5579   "font", WARN_FONT,
5580   "di", WARN_DI,
5581   "mac", WARN_MAC,
5582   "reg", WARN_REG,
5583   "all", WARN_TOTAL & ~(WARN_DI | WARN_MAC | WARN_REG),
5584   "w", WARN_TOTAL,
5585   "default", DEFAULT_WARNING_MASK,
5586 };
5587 
lookup_warning(const char * name)5588 static int lookup_warning(const char *name)
5589 {
5590   for (int i = 0;
5591        i < sizeof(warning_table)/sizeof(warning_table[0]);
5592        i++)
5593     if (strcmp(name, warning_table[i].name) == 0)
5594       return warning_table[i].mask;
5595   return 0;
5596 }
5597 
enable_warning(const char * name)5598 static void enable_warning(const char *name)
5599 {
5600   int mask = lookup_warning(name);
5601   if (mask)
5602     warning_mask |= mask;
5603   else
5604     error("unknown warning `%1'", name);
5605 }
5606 
disable_warning(const char * name)5607 static void disable_warning(const char *name)
5608 {
5609   int mask = lookup_warning(name);
5610   if (mask)
5611     warning_mask &= ~mask;
5612   else
5613     error("unknown warning `%1'", name);
5614 }
5615 
5616 enum error_type { WARNING, ERROR, FATAL };
5617 
do_error(error_type type,const char * format,const errarg & arg1,const errarg & arg2,const errarg & arg3)5618 static void do_error(error_type type,
5619 		     const char *format,
5620 		     const errarg &arg1,
5621 		     const errarg &arg2,
5622 		     const errarg &arg3)
5623 {
5624   const char *filename;
5625   int lineno;
5626   if (inhibit_errors && type < FATAL)
5627     return;
5628   if (backtrace_flag)
5629     input_stack::backtrace();
5630   if (!get_file_line(&filename, &lineno))
5631     filename = 0;
5632   if (filename)
5633     errprint("%1:%2: ", filename, lineno);
5634   else if (program_name)
5635     fprintf(stderr, "%s: ", program_name);
5636   switch (type) {
5637   case FATAL:
5638     fputs("fatal error: ", stderr);
5639     break;
5640   case ERROR:
5641     break;
5642   case WARNING:
5643     fputs("warning: ", stderr);
5644     break;
5645   }
5646   errprint(format, arg1, arg2, arg3);
5647   fputc('\n', stderr);
5648   fflush(stderr);
5649   if (type == FATAL)
5650     cleanup_and_exit(1);
5651 }
5652 
warning(warning_type t,const char * format,const errarg & arg1,const errarg & arg2,const errarg & arg3)5653 int warning(warning_type t,
5654 	    const char *format,
5655 	    const errarg &arg1,
5656 	    const errarg &arg2,
5657 	    const errarg &arg3)
5658 {
5659   if ((t & warning_mask) != 0) {
5660     do_error(WARNING, format, arg1, arg2, arg3);
5661     return 1;
5662   }
5663   else
5664     return 0;
5665 }
5666 
error(const char * format,const errarg & arg1,const errarg & arg2,const errarg & arg3)5667 void error(const char *format,
5668 	   const errarg &arg1,
5669 	   const errarg &arg2,
5670 	   const errarg &arg3)
5671 {
5672   do_error(ERROR, format, arg1, arg2, arg3);
5673 }
5674 
fatal(const char * format,const errarg & arg1,const errarg & arg2,const errarg & arg3)5675 void fatal(const char *format,
5676 	   const errarg &arg1,
5677 	   const errarg &arg2,
5678 	   const errarg &arg3)
5679 {
5680   do_error(FATAL, format, arg1, arg2, arg3);
5681 }
5682 
fatal_with_file_and_line(const char * filename,int lineno,const char * format,const errarg & arg1,const errarg & arg2,const errarg & arg3)5683 void fatal_with_file_and_line(const char *filename, int lineno,
5684 			      const char *format,
5685 			      const errarg &arg1,
5686 			      const errarg &arg2,
5687 			      const errarg &arg3)
5688 {
5689   fprintf(stderr, "%s:%d: fatal error: ", filename, lineno);
5690   errprint(format, arg1, arg2, arg3);
5691   fputc('\n', stderr);
5692   fflush(stderr);
5693   cleanup_and_exit(1);
5694 }
5695 
error_with_file_and_line(const char * filename,int lineno,const char * format,const errarg & arg1,const errarg & arg2,const errarg & arg3)5696 void error_with_file_and_line(const char *filename, int lineno,
5697 			      const char *format,
5698 			      const errarg &arg1,
5699 			      const errarg &arg2,
5700 			      const errarg &arg3)
5701 {
5702   fprintf(stderr, "%s:%d: error: ", filename, lineno);
5703   errprint(format, arg1, arg2, arg3);
5704   fputc('\n', stderr);
5705   fflush(stderr);
5706 }
5707 
5708 dictionary charinfo_dictionary(501);
5709 
get_charinfo(symbol nm)5710 charinfo *get_charinfo(symbol nm)
5711 {
5712   void *p = charinfo_dictionary.lookup(nm);
5713   if (p != 0)
5714     return (charinfo *)p;
5715   charinfo *cp = new charinfo(nm);
5716   (void)charinfo_dictionary.lookup(nm, cp);
5717   return cp;
5718 }
5719 
5720 int charinfo::next_index = 0;
5721 
charinfo(symbol s)5722 charinfo::charinfo(symbol s)
5723 : nm(s), hyphenation_code(0), translation(0), flags(0), ascii_code(0),
5724   special_translation(TRANSLATE_NONE), mac(0), not_found(0),
5725   transparent_translate(1)
5726 {
5727   index = next_index++;
5728 }
5729 
set_hyphenation_code(unsigned char c)5730 void charinfo::set_hyphenation_code(unsigned char c)
5731 {
5732   hyphenation_code = c;
5733 }
5734 
set_translation(charinfo * ci,int tt)5735 void charinfo::set_translation(charinfo *ci, int tt)
5736 {
5737   translation = ci;
5738   special_translation = TRANSLATE_NONE;
5739   transparent_translate = tt;
5740 }
5741 
set_special_translation(int c,int tt)5742 void charinfo::set_special_translation(int c, int tt)
5743 {
5744   special_translation = c;
5745   translation = 0;
5746   transparent_translate = tt;
5747 }
5748 
set_ascii_code(unsigned char c)5749 void charinfo::set_ascii_code(unsigned char c)
5750 {
5751   ascii_code = c;
5752 }
5753 
set_macro(macro * m)5754 macro *charinfo::set_macro(macro *m)
5755 {
5756   macro *tem = mac;
5757   mac = m;
5758   return tem;
5759 }
5760 
set_number(int n)5761 void charinfo::set_number(int n)
5762 {
5763   number = n;
5764   flags |= NUMBERED;
5765 }
5766 
get_number()5767 int charinfo::get_number()
5768 {
5769   assert(flags & NUMBERED);
5770   return number;
5771 }
5772 
5773 symbol UNNAMED_SYMBOL("---");
5774 
5775 // For numbered characters not between 0 and 255, we make a symbol out
5776 // of the number and store them in this dictionary.
5777 
5778 dictionary numbered_charinfo_dictionary(11);
5779 
get_charinfo_by_number(int n)5780 charinfo *get_charinfo_by_number(int n)
5781 {
5782   static charinfo *number_table[256];
5783 
5784   if (n >= 0 && n < 256) {
5785     charinfo *ci = number_table[n];
5786     if (!ci) {
5787       ci = new charinfo(UNNAMED_SYMBOL);
5788       ci->set_number(n);
5789       number_table[n] = ci;
5790     }
5791     return ci;
5792   }
5793   else {
5794     symbol ns(itoa(n));
5795     charinfo *ci = (charinfo *)numbered_charinfo_dictionary.lookup(ns);
5796     if (!ci) {
5797       ci = new charinfo(UNNAMED_SYMBOL);
5798       ci->set_number(n);
5799       numbered_charinfo_dictionary.lookup(ns, ci);
5800     }
5801     return ci;
5802   }
5803 }
5804 
name_to_index(const char * nm)5805 int font::name_to_index(const char *nm)
5806 {
5807   charinfo *ci;
5808   if (nm[1] == 0)
5809     ci = charset_table[nm[0] & 0xff];
5810   else if (nm[0] == '\\' && nm[2] == 0)
5811     ci = get_charinfo(symbol(nm + 1));
5812   else
5813     ci = get_charinfo(symbol(nm));
5814   if (ci == 0)
5815     return -1;
5816   else
5817     return ci->get_index();
5818 }
5819 
number_to_index(int n)5820 int font::number_to_index(int n)
5821 {
5822   return get_charinfo_by_number(n)->get_index();
5823 }
5824