Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / groff / src / preproc / eqn / lex.cc
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002
3    Free Software Foundation, Inc.
4      Written by James Clark (jjc@jclark.com)
5
6 This file is part of groff.
7
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
11 version.
12
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16 for more details.
17
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING.  If not, write to the Free Software
20 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
21
22 #include "eqn.h"
23 #include "eqn_tab.h"
24 #include "stringclass.h"
25 #include "ptable.h"
26
27 struct definition {
28   char is_macro;
29   char is_simple;
30   union {
31     int tok;
32     char *contents;
33   };
34   definition();
35   ~definition();
36 };
37
38 definition::definition() : is_macro(1), is_simple(0)
39 {
40   contents = 0;
41 }
42
43 definition::~definition()
44 {
45   if (is_macro)
46     a_delete contents;
47 }
48
49 declare_ptable(definition)
50 implement_ptable(definition)
51
52 PTABLE(definition) macro_table;
53
54 static struct {
55   const char *name;
56   int token;
57 } token_table[] = {
58   { "over", OVER },
59   { "smallover", SMALLOVER },
60   { "sqrt", SQRT },
61   { "sub", SUB },
62   { "sup", SUP },
63   { "lpile", LPILE },
64   { "rpile", RPILE },
65   { "cpile", CPILE },
66   { "pile", PILE },
67   { "left", LEFT },
68   { "right", RIGHT },
69   { "to", TO },
70   { "from", FROM },
71   { "size", SIZE },
72   { "font", FONT },
73   { "roman", ROMAN },
74   { "bold", BOLD },
75   { "italic", ITALIC },
76   { "fat", FAT },
77   { "bar", BAR },
78   { "under", UNDER },
79   { "accent", ACCENT },
80   { "uaccent", UACCENT },
81   { "above", ABOVE },
82   { "fwd", FWD },
83   { "back", BACK },
84   { "down", DOWN },
85   { "up", UP },
86   { "matrix", MATRIX },
87   { "col", COL },
88   { "lcol", LCOL },
89   { "rcol", RCOL },
90   { "ccol", CCOL },
91   { "mark", MARK },
92   { "lineup", LINEUP },
93   { "space", SPACE },
94   { "gfont", GFONT },
95   { "gsize", GSIZE },
96   { "define", DEFINE },
97   { "sdefine", SDEFINE },
98   { "ndefine", NDEFINE },
99   { "tdefine", TDEFINE },
100   { "undef", UNDEF },
101   { "ifdef", IFDEF },
102   { "include", INCLUDE },
103   { "copy", INCLUDE },
104   { "delim", DELIM },
105   { "chartype", CHARTYPE },
106   { "type", TYPE },
107   { "vcenter", VCENTER },
108   { "set", SET },
109   { "opprime", PRIME },
110   { "grfont", GRFONT },
111   { "gbfont", GBFONT },
112   { "split", SPLIT },
113   { "nosplit", NOSPLIT },
114   { "special", SPECIAL },
115 };
116
117 static struct {
118   const char *name;
119   const char *def;
120 } def_table[] = {
121   { "ALPHA", "\\(*A" },
122   { "BETA", "\\(*B" },
123   { "CHI", "\\(*X" },
124   { "DELTA", "\\(*D" },
125   { "EPSILON", "\\(*E" },
126   { "ETA", "\\(*Y" },
127   { "GAMMA", "\\(*G" },
128   { "IOTA", "\\(*I" },
129   { "KAPPA", "\\(*K" },
130   { "LAMBDA", "\\(*L" },
131   { "MU", "\\(*M" },
132   { "NU", "\\(*N" },
133   { "OMEGA", "\\(*W" },
134   { "OMICRON", "\\(*O" },
135   { "PHI", "\\(*F" },
136   { "PI", "\\(*P" },
137   { "PSI", "\\(*Q" },
138   { "RHO", "\\(*R" },
139   { "SIGMA", "\\(*S" },
140   { "TAU", "\\(*T" },
141   { "THETA", "\\(*H" },
142   { "UPSILON", "\\(*U" },
143   { "XI", "\\(*C" },
144   { "ZETA", "\\(*Z" },
145   { "Alpha", "\\(*A" },
146   { "Beta", "\\(*B" },
147   { "Chi", "\\(*X" },
148   { "Delta", "\\(*D" },
149   { "Epsilon", "\\(*E" },
150   { "Eta", "\\(*Y" },
151   { "Gamma", "\\(*G" },
152   { "Iota", "\\(*I" },
153   { "Kappa", "\\(*K" },
154   { "Lambda", "\\(*L" },
155   { "Mu", "\\(*M" },
156   { "Nu", "\\(*N" },
157   { "Omega", "\\(*W" },
158   { "Omicron", "\\(*O" },
159   { "Phi", "\\(*F" },
160   { "Pi", "\\(*P" },
161   { "Psi", "\\(*Q" },
162   { "Rho", "\\(*R" },
163   { "Sigma", "\\(*S" },
164   { "Tau", "\\(*T" },
165   { "Theta", "\\(*H" },
166   { "Upsilon", "\\(*U" },
167   { "Xi", "\\(*C" },
168   { "Zeta", "\\(*Z" },
169   { "alpha", "\\(*a" },
170   { "beta", "\\(*b" },
171   { "chi", "\\(*x" },
172   { "delta", "\\(*d" },
173   { "epsilon", "\\(*e" },
174   { "eta", "\\(*y" },
175   { "gamma", "\\(*g" },
176   { "iota", "\\(*i" },
177   { "kappa", "\\(*k" },
178   { "lambda", "\\(*l" },
179   { "mu", "\\(*m" },
180   { "nu", "\\(*n" },
181   { "omega", "\\(*w" },
182   { "omicron", "\\(*o" },
183   { "phi", "\\(*f" },
184   { "pi", "\\(*p" },
185   { "psi", "\\(*q" },
186   { "rho", "\\(*r" },
187   { "sigma", "\\(*s" },
188   { "tau", "\\(*t" },
189   { "theta", "\\(*h" },
190   { "upsilon", "\\(*u" },
191   { "xi", "\\(*c" },
192   { "zeta", "\\(*z" },
193   { "max", "{type \"operator\" roman \"max\"}" },
194   { "min", "{type \"operator\" roman \"min\"}" },
195   { "lim", "{type \"operator\" roman \"lim\"}" },
196   { "sin", "{type \"operator\" roman \"sin\"}" },
197   { "cos", "{type \"operator\" roman \"cos\"}" },
198   { "tan", "{type \"operator\" roman \"tan\"}" },
199   { "sinh", "{type \"operator\" roman \"sinh\"}" },
200   { "cosh", "{type \"operator\" roman \"cosh\"}" },
201   { "tanh", "{type \"operator\" roman \"tanh\"}" },
202   { "arc", "{type \"operator\" roman \"arc\"}" },
203   { "log", "{type \"operator\" roman \"log\"}" },
204   { "ln", "{type \"operator\" roman \"ln\"}" },
205   { "exp", "{type \"operator\" roman \"exp\"}" },
206   { "Re", "{type \"operator\" roman \"Re\"}" },
207   { "Im", "{type \"operator\" roman \"Im\"}" },
208   { "det", "{type \"operator\" roman \"det\"}" },
209   { "and", "{roman \"and\"}" },
210   { "if", "{roman \"if\"}" },
211   { "for", "{roman \"for\"}" },
212   { "sum", "{type \"operator\" vcenter size +5 \\(*S}" },
213   { "prod", "{type \"operator\" vcenter size +5 \\(*P}" },
214   { "int", "{type \"operator\" vcenter size +8 \\(is}" },
215   { "union", "{type \"operator\" vcenter size +5 \\(cu}" },
216   { "inter", "{type \"operator\" vcenter size +5 \\(ca}" },
217   { "times", "type \"binary\" \\(mu" },
218   { "ldots", "type \"inner\" { . . . }" },
219   { "inf", "\\(if" },
220   { "partial", "\\(pd" },
221   { "nothing", "\"\"" },
222   { "half", "{1 smallover 2}" },
223   { "hat_def", "roman \"^\"" },
224   { "hat", "accent { hat_def }" },
225   { "dot_def", "back 15 \"\\v'-52M'.\\v'52M'\"" },
226   { "dot", "accent { dot_def }" },
227   { "dotdot_def", "back 25 \"\\v'-52M'..\\v'52M'\"" },
228   { "dotdot", "accent { dotdot_def }" },
229   { "tilde_def", "\"~\"" },
230   { "tilde", "accent { tilde_def }" },
231   { "utilde_def", "\"\\v'75M'~\\v'-75M'\"" },
232   { "utilde", "uaccent { utilde_def }" },
233   { "vec_def", "up 52 size -5 \\(->" },
234   { "vec", "accent { vec_def }" },
235   { "dyad_def", "up 52 size -5 {\\(<- back 60 \\(->}" },
236   { "dyad", "accent { dyad_def }" },
237   { "==", "type \"relation\" \\(==" },
238   { "!=", "type \"relation\" \\(!=" },
239   { "+-", "type \"binary\" \\(+-" },
240   { "->", "type \"relation\" \\(->" },
241   { "<-", "type \"relation\" \\(<-" },
242   { "<<", "{ < back 20 < }" },
243   { ">>", "{ > back 20 > }" },
244   { "...", "type \"inner\" vcenter { . . . }" },
245   { "prime", "'" },
246   { "approx", "type \"relation\" \"\\(~=\"" },
247   { "grad", "\\(gr" },
248   { "del", "\\(gr" },
249   { "cdot", "type \"binary\" vcenter ." },
250   { "dollar", "$" },
251 };  
252
253 void init_table(const char *device)
254 {
255   unsigned int i;
256   for (i = 0; i < sizeof(token_table)/sizeof(token_table[0]); i++) {
257     definition *def = new definition;
258     def->is_macro = 0;
259     def->tok = token_table[i].token;
260     macro_table.define(token_table[i].name, def);
261   }
262   for (i = 0; i < sizeof(def_table)/sizeof(def_table[0]); i++) {
263     definition *def = new definition;
264     def->is_macro = 1;
265     def->contents = strsave(def_table[i].def);
266     def->is_simple = 1;
267     macro_table.define(def_table[i].name, def);
268   }
269   definition *def = new definition;
270   def->is_macro = 1;
271   def->contents = strsave("1");
272   macro_table.define(device, def);
273 }
274
275 class input {
276   input *next;
277 public:
278   input(input *p);
279   virtual ~input();
280   virtual int get() = 0;
281   virtual int peek() = 0;
282   virtual int get_location(char **, int *);
283
284   friend int get_char();
285   friend int peek_char();
286   friend int get_location(char **, int *);
287   friend void init_lex(const char *str, const char *filename, int lineno);
288 };
289
290 class file_input : public input {
291   FILE *fp;
292   char *filename;
293   int lineno;
294   string line;
295   const char *ptr;
296   int read_line();
297 public:
298   file_input(FILE *, const char *, input *);
299   ~file_input();
300   int get();
301   int peek();
302   int get_location(char **, int *);
303 };
304
305
306 class macro_input : public input {
307   char *s;
308   char *p;
309 public:
310   macro_input(const char *, input *);
311   ~macro_input();
312   int get();
313   int peek();
314 };
315
316 class top_input : public macro_input {
317   char *filename;
318   int lineno;
319  public:
320   top_input(const char *, const char *, int, input *);
321   ~top_input();
322   int get();
323   int get_location(char **, int *);
324 };
325
326 class argument_macro_input: public input {
327   char *s;
328   char *p;
329   char *ap;
330   int argc;
331   char *argv[9];
332 public:
333   argument_macro_input(const char *, int, char **, input *);
334   ~argument_macro_input();
335   int get();
336   int peek();
337 };
338
339 input::input(input *x) : next(x)
340 {
341 }
342
343 input::~input()
344 {
345 }
346
347 int input::get_location(char **, int *)
348 {
349   return 0;
350 }
351
352 file_input::file_input(FILE *f, const char *fn, input *p)
353 : input(p), lineno(0), ptr("")
354 {
355   fp = f;
356   filename = strsave(fn);
357 }
358
359 file_input::~file_input()
360 {
361   a_delete filename;
362   fclose(fp);
363 }
364
365 int file_input::read_line()
366 {
367   for (;;) {
368     line.clear();
369     lineno++;
370     for (;;) {
371       int c = getc(fp);
372       if (c == EOF)
373         break;
374       else if (invalid_input_char(c))
375         lex_error("invalid input character code %1", c);
376       else {
377         line += char(c);
378         if (c == '\n') 
379           break;
380       }
381     }
382     if (line.length() == 0)
383       return 0;
384     if (!(line.length() >= 3 && line[0] == '.' && line[1] == 'E'
385           && (line[2] == 'Q' || line[2] == 'N')
386           && (line.length() == 3 || line[3] == ' ' || line[3] == '\n'
387               || compatible_flag))) {
388       line += '\0';
389       ptr = line.contents();
390       return 1;
391     }
392   }
393 }
394
395 int file_input::get()
396 {
397   if (*ptr != '\0' || read_line())
398     return *ptr++ & 0377;
399   else
400     return EOF;
401 }
402
403 int file_input::peek()
404 {
405   if (*ptr != '\0' || read_line())
406     return *ptr;
407   else
408     return EOF;
409 }
410
411 int file_input::get_location(char **fnp, int *lnp)
412 {
413   *fnp = filename;
414   *lnp = lineno;
415   return 1;
416 }
417
418 macro_input::macro_input(const char *str, input *x) : input(x)
419 {
420   p = s = strsave(str);
421 }
422
423 macro_input::~macro_input()
424 {
425   a_delete s;
426 }
427
428 int macro_input::get()
429 {
430   if (p == 0 || *p == '\0')
431     return EOF;
432   else
433     return *p++ & 0377;
434 }
435
436 int macro_input::peek()
437 {
438   if (p == 0 || *p == '\0')
439     return EOF;
440   else
441     return *p & 0377;
442 }
443
444 top_input::top_input(const char *str, const char *fn, int ln, input *x)
445 : macro_input(str, x), lineno(ln)
446 {
447   filename = strsave(fn);
448 }
449
450 top_input::~top_input()
451 {
452   a_delete filename;
453 }
454
455 int top_input::get()
456 {
457   int c = macro_input::get();
458   if (c == '\n')
459     lineno++;
460   return c;
461 }
462
463 int top_input::get_location(char **fnp, int *lnp)
464 {
465   *fnp = filename;
466   *lnp = lineno;
467   return 1;
468 }
469
470 // Character representing $1.  Must be invalid input character.
471 #define ARG1 14
472
473 argument_macro_input::argument_macro_input(const char *body, int ac, 
474                                            char **av, input *x)
475 : input(x), ap(0), argc(ac)
476 {
477   int i;
478   for (i = 0; i < argc; i++)
479     argv[i] = av[i];
480   p = s = strsave(body);
481   int j = 0;
482   for (i = 0; s[i] != '\0'; i++)
483     if (s[i] == '$' && s[i+1] >= '0' && s[i+1] <= '9') {
484       if (s[i+1] != '0')
485         s[j++] = ARG1 + s[++i] - '1';
486     }
487     else
488       s[j++] = s[i];
489   s[j] = '\0';
490 }
491
492
493 argument_macro_input::~argument_macro_input()
494 {
495   for (int i = 0; i < argc; i++)
496     a_delete argv[i];
497   a_delete s;
498 }
499
500 int argument_macro_input::get()
501 {
502   if (ap) {
503     if (*ap != '\0')
504       return *ap++ & 0377;
505     ap = 0;
506   }
507   if (p == 0)
508     return EOF;
509   while (*p >= ARG1 && *p <= ARG1 + 8) {
510     int i = *p++ - ARG1;
511     if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
512       ap = argv[i];
513       return *ap++ & 0377;
514     }
515   }
516   if (*p == '\0')
517     return EOF;
518   return *p++ & 0377;
519 }
520
521 int argument_macro_input::peek()
522 {
523   if (ap) {
524     if (*ap != '\0')
525       return *ap & 0377;
526     ap = 0;
527   }
528   if (p == 0)
529     return EOF;
530   while (*p >= ARG1 && *p <= ARG1 + 8) {
531     int i = *p++ - ARG1;
532     if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
533       ap = argv[i];
534       return *ap & 0377;
535     }
536   }
537   if (*p == '\0')
538     return EOF;
539   return *p & 0377;
540 }
541
542 static input *current_input = 0;
543
544 /* we insert a newline between input from different levels */
545
546 int get_char()
547 {
548   if (current_input == 0)
549     return EOF;
550   else {
551     int c = current_input->get();
552     if (c != EOF)
553       return c;
554     else {
555       input *tem = current_input;
556       current_input = current_input->next;
557       delete tem;
558       return '\n';
559     }
560   }
561 }
562
563 int peek_char()
564 {
565   if (current_input == 0)
566     return EOF;
567   else {
568     int c = current_input->peek();
569     if (c != EOF)
570       return c;
571     else
572       return '\n';
573   }
574 }
575
576 int get_location(char **fnp, int *lnp)
577 {
578   for (input *p = current_input; p; p = p->next)
579     if (p->get_location(fnp, lnp))
580       return 1;
581   return 0;
582 }
583
584 string token_buffer;
585 const int NCONTEXT = 4;
586 string context_ring[NCONTEXT];
587 int context_index = 0;
588
589 void flush_context()
590 {
591   for (int i = 0; i < NCONTEXT; i++)
592     context_ring[i] = "";
593   context_index = 0;
594 }
595
596 void show_context()
597 {
598   int i = context_index;
599   fputs(" context is\n\t", stderr);
600   for (;;) {
601     int j = (i + 1) % NCONTEXT;
602     if (j == context_index) {
603       fputs(">>> ", stderr);
604       put_string(context_ring[i], stderr);
605       fputs(" <<<", stderr);
606       break;
607     }
608     else if (context_ring[i].length() > 0) {
609       put_string(context_ring[i], stderr);
610       putc(' ', stderr);
611     }
612     i = j;
613   }
614   putc('\n', stderr);
615 }
616
617 void add_context(const string &s)
618 {
619   context_ring[context_index] = s;
620   context_index = (context_index + 1) % NCONTEXT;
621 }
622
623 void add_context(char c)
624 {
625   context_ring[context_index] = c;
626   context_index = (context_index + 1) % NCONTEXT;
627 }
628
629 void add_quoted_context(const string &s)
630 {
631   string &r = context_ring[context_index];
632   r = '"';
633   for (int i = 0; i < s.length(); i++)
634     if (s[i] == '"')
635       r += "\\\"";
636     else
637       r += s[i];
638   r += '"';
639   context_index = (context_index + 1) % NCONTEXT;
640 }
641
642 void init_lex(const char *str, const char *filename, int lineno)
643 {
644  while (current_input != 0) {
645     input *tem = current_input;
646     current_input = current_input->next;
647     delete tem;
648   }
649   current_input = new top_input(str, filename, lineno, 0);
650   flush_context();
651 }
652
653
654 void get_delimited_text()
655 {
656   char *filename;
657   int lineno;
658   int got_location = get_location(&filename, &lineno);
659   int start = get_char();
660   while (start == ' ' || start == '\t' || start == '\n')
661     start = get_char();
662   token_buffer.clear();
663   if (start == EOF) {
664     if (got_location)
665       error_with_file_and_line(filename, lineno,
666                                "end of input while defining macro");
667     else
668       error("end of input while defining macro");
669     return;
670   }
671   for (;;) {
672     int c = get_char();
673     if (c == EOF) {
674       if (got_location)
675         error_with_file_and_line(filename, lineno,
676                                  "end of input while defining macro");
677       else
678         error("end of input while defining macro");
679       add_context(start + token_buffer);
680       return;
681     }
682     if (c == start)
683       break;
684     token_buffer += char(c);
685   }
686   add_context(start + token_buffer + start);
687 }
688
689 void interpolate_macro_with_args(const char *body)
690 {
691   char *argv[9];
692   int argc = 0;
693   int i;
694   for (i = 0; i < 9; i++)
695     argv[i] = 0;
696   int level = 0;
697   int c;
698   do {
699     token_buffer.clear();
700     for (;;) {
701       c = get_char();
702       if (c == EOF) {
703         lex_error("end of input while scanning macro arguments");
704         break;
705       }
706       if (level == 0 && (c == ',' || c == ')')) {
707         if (token_buffer.length() > 0) {
708           token_buffer +=  '\0';
709           argv[argc] = strsave(token_buffer.contents());
710         }
711         // for `foo()', argc = 0
712         if (argc > 0 || c != ')' || i > 0)
713           argc++;
714         break;
715       }
716       token_buffer += char(c);
717       if (c == '(')
718         level++;
719       else if (c == ')')
720         level--;
721     }
722   } while (c != ')' && c != EOF);
723   current_input = new argument_macro_input(body, argc, argv, current_input);
724 }
725
726 /* If lookup flag is non-zero the token will be looked up to see
727 if it is macro. If it's 1, it will looked up to see if it's a token.
728 */
729
730 int get_token(int lookup_flag = 0)
731 {
732   for (;;) {
733     int c = get_char();
734     while (c == ' ' || c == '\n')
735       c = get_char();
736     switch (c) {
737     case EOF:
738       {
739         add_context("end of input");
740       }
741       return 0;
742     case '"':
743       {
744         int quoted = 0;
745         token_buffer.clear();
746         for (;;) {
747           c = get_char();
748           if (c == EOF) {
749             lex_error("missing \"");
750             break;
751           }
752           else if (c == '\n') {
753             lex_error("newline before end of quoted text");
754             break;
755           }
756           else if (c == '"') {
757             if (!quoted)
758               break;
759             token_buffer[token_buffer.length() - 1] = '"';
760             quoted = 0;
761           }
762           else {
763             token_buffer += c;
764             quoted = quoted ? 0 : c == '\\';
765           }
766         }
767       }
768       add_quoted_context(token_buffer);
769       return QUOTED_TEXT;
770     case '{':
771     case '}':
772     case '^':
773     case '~':
774     case '\t':
775       add_context(c);
776       return c;
777     default:
778       {
779         int break_flag = 0;
780         int quoted = 0;
781         token_buffer.clear();
782         if (c == '\\')
783           quoted = 1;
784         else
785           token_buffer += c;
786         int done = 0;
787         while (!done) {
788           c = peek_char();
789           if (!quoted && lookup_flag != 0 && c == '(') {
790             token_buffer += '\0';
791             definition *def = macro_table.lookup(token_buffer.contents());
792             if (def && def->is_macro && !def->is_simple) {
793               (void)get_char(); // skip initial '('
794               interpolate_macro_with_args(def->contents);
795               break_flag = 1;
796               break;
797             }
798             token_buffer.set_length(token_buffer.length() - 1);
799           }
800           if (quoted) {
801             quoted = 0;
802             switch (c) {
803             case EOF:
804               lex_error("`\\' ignored at end of equation");
805               done = 1;
806               break;
807             case '\n':
808               lex_error("`\\' ignored because followed by newline");
809               done = 1;
810               break;
811             case '\t':
812               lex_error("`\\' ignored because followed by tab");
813               done = 1;
814               break;
815             case '"':
816               (void)get_char();
817               token_buffer += '"';
818               break;
819             default:
820               (void)get_char();
821               token_buffer += '\\';
822               token_buffer += c;
823               break;
824             }
825           }
826           else {
827             switch (c) {
828             case EOF:
829             case '{':
830             case '}':
831             case '^':
832             case '~':
833             case '"':
834             case ' ':
835             case '\t':
836             case '\n':
837               done = 1;
838               break;
839             case '\\':
840               (void)get_char();
841               quoted = 1;
842               break;
843             default:
844               (void)get_char();
845               token_buffer += char(c);
846               break;
847             }
848           }
849         }
850         if (break_flag || token_buffer.length() == 0)
851           break;
852         if (lookup_flag != 0) {
853           token_buffer += '\0';
854           definition *def = macro_table.lookup(token_buffer.contents());
855           token_buffer.set_length(token_buffer.length() - 1);
856           if (def) {
857             if (def->is_macro) {
858               current_input = new macro_input(def->contents, current_input);
859               break;
860             }
861             else if (lookup_flag == 1) {
862               add_context(token_buffer);
863               return def->tok;
864             }
865           }
866         }
867         add_context(token_buffer);
868         return TEXT;
869       }
870     }
871   }
872 }
873
874 void do_include()
875 {
876   int t = get_token(2);
877   if (t != TEXT && t != QUOTED_TEXT) {
878     lex_error("bad filename for include");
879     return;
880   }
881   token_buffer += '\0';
882   const char *filename = token_buffer.contents();
883   errno = 0;
884   FILE *fp = fopen(filename, "r");
885   if (fp == 0) {
886     lex_error("can't open included file `%1'", filename);
887     return;
888   }
889   current_input = new file_input(fp, filename, current_input);
890 }
891
892 void ignore_definition()
893 {
894   int t = get_token();
895   if (t != TEXT) {
896     lex_error("bad definition");
897     return;
898   }
899   get_delimited_text();
900 }
901
902 void do_definition(int is_simple)
903 {
904   int t = get_token();
905   if (t != TEXT) {
906     lex_error("bad definition");
907     return;
908   }
909   token_buffer += '\0';
910   const char *name = token_buffer.contents();
911   definition *def = macro_table.lookup(name);
912   if (def == 0) {
913     def = new definition;
914     macro_table.define(name, def);
915   }
916   else if (def->is_macro) {
917     a_delete def->contents;
918   }
919   get_delimited_text();
920   token_buffer += '\0';
921   def->is_macro = 1;
922   def->contents = strsave(token_buffer.contents());
923   def->is_simple = is_simple;
924 }
925
926 void do_undef()
927 {
928   int t = get_token();
929   if (t != TEXT) {
930     lex_error("bad undef command");
931     return;
932   }
933   token_buffer += '\0';
934   macro_table.define(token_buffer.contents(), 0);
935 }
936
937 void do_gsize()
938 {
939   int t = get_token(2);
940   if (t != TEXT && t != QUOTED_TEXT) {
941     lex_error("bad argument to gsize command");
942     return;
943   }
944   token_buffer += '\0';
945   if (!set_gsize(token_buffer.contents()))
946     lex_error("invalid size `%1'", token_buffer.contents());
947 }
948
949 void do_gfont()
950 {
951   int t = get_token(2);
952   if (t != TEXT && t != QUOTED_TEXT) {
953     lex_error("bad argument to gfont command");
954     return;
955   }
956   token_buffer += '\0';
957   set_gfont(token_buffer.contents());
958 }
959
960 void do_grfont()
961 {
962   int t = get_token(2);
963   if (t != TEXT && t != QUOTED_TEXT) {
964     lex_error("bad argument to grfont command");
965     return;
966   }
967   token_buffer += '\0';
968   set_grfont(token_buffer.contents());
969 }
970
971 void do_gbfont()
972 {
973   int t = get_token(2);
974   if (t != TEXT && t != QUOTED_TEXT) {
975     lex_error("bad argument to gbfont command");
976     return;
977   }
978   token_buffer += '\0';
979   set_gbfont(token_buffer.contents());
980 }
981
982 void do_space()
983 {
984   int t = get_token(2);
985   if (t != TEXT && t != QUOTED_TEXT) {
986     lex_error("bad argument to space command");
987     return;
988   }
989   token_buffer += '\0';
990   char *ptr;
991   long n = strtol(token_buffer.contents(), &ptr, 10);
992   if (n == 0 && ptr == token_buffer.contents())
993     lex_error("bad argument `%1' to space command", token_buffer.contents());
994   else
995     set_space(int(n));
996 }
997
998 void do_ifdef()
999 {
1000   int t = get_token();
1001   if (t != TEXT) {
1002     lex_error("bad ifdef");
1003     return;
1004   }
1005   token_buffer += '\0';
1006   definition *def = macro_table.lookup(token_buffer.contents());
1007   int result = def && def->is_macro && !def->is_simple;
1008   get_delimited_text();
1009   if (result) {
1010     token_buffer += '\0';
1011     current_input = new macro_input(token_buffer.contents(), current_input);
1012   }
1013 }
1014
1015 void do_delim()
1016 {
1017   int c = get_char();
1018   while (c == ' ' || c == '\n')
1019     c = get_char();
1020   int d;
1021   if (c == EOF || (d = get_char()) == EOF)
1022     lex_error("end of file while reading argument to `delim'");
1023   else {
1024     if (c == 'o' && d == 'f' && peek_char() == 'f') {
1025       (void)get_char();
1026       start_delim = end_delim = '\0';
1027     }
1028     else {
1029       start_delim = c;
1030       end_delim = d;
1031     }
1032   }
1033 }
1034
1035 void do_chartype()
1036 {
1037   int t = get_token(2);
1038   if (t != TEXT && t != QUOTED_TEXT) {
1039     lex_error("bad chartype");
1040     return;
1041   }
1042   token_buffer += '\0';
1043   string type = token_buffer;
1044   t = get_token();
1045   if (t != TEXT && t != QUOTED_TEXT) {
1046     lex_error("bad chartype");
1047     return;
1048   }
1049   token_buffer += '\0';
1050   set_char_type(type.contents(), strsave(token_buffer.contents()));
1051 }
1052
1053 void do_set()
1054 {
1055   int t = get_token(2);
1056   if (t != TEXT && t != QUOTED_TEXT) {
1057     lex_error("bad set");
1058     return;
1059   }
1060   token_buffer += '\0';
1061   string param = token_buffer;
1062   t = get_token();
1063   if (t != TEXT && t != QUOTED_TEXT) {
1064     lex_error("bad set");
1065     return;
1066   }
1067   token_buffer += '\0';
1068   int n;
1069   if (sscanf(&token_buffer[0], "%d", &n) != 1) {
1070     lex_error("bad number `%1'", token_buffer.contents());
1071     return;
1072   }
1073   set_param(param.contents(), n);
1074 }
1075
1076 int yylex()
1077 {
1078   for (;;) {
1079     int tk = get_token(1);
1080     switch(tk) {
1081     case UNDEF:
1082       do_undef();
1083       break;
1084     case SDEFINE:
1085       do_definition(1);
1086       break;
1087     case DEFINE:
1088       do_definition(0);
1089       break;
1090     case TDEFINE:
1091       if (!nroff)
1092         do_definition(0);
1093       else
1094         ignore_definition();
1095       break;
1096     case NDEFINE:
1097       if (nroff)
1098         do_definition(0);
1099       else
1100         ignore_definition();
1101       break;
1102     case GSIZE:
1103       do_gsize();
1104       break;
1105     case GFONT:
1106       do_gfont();
1107       break;
1108     case GRFONT:
1109       do_grfont();
1110       break;
1111     case GBFONT:
1112       do_gbfont();
1113       break;
1114     case SPACE:
1115       do_space();
1116       break;
1117     case INCLUDE:
1118       do_include();
1119       break;
1120     case IFDEF:
1121       do_ifdef();
1122       break;
1123     case DELIM:
1124       do_delim();
1125       break;
1126     case CHARTYPE:
1127       do_chartype();
1128       break;
1129     case SET:
1130       do_set();
1131       break;
1132     case QUOTED_TEXT:
1133     case TEXT:
1134       token_buffer += '\0';
1135       yylval.str = strsave(token_buffer.contents());
1136       // fall through
1137     default:
1138       return tk;
1139     }
1140   }
1141 }
1142
1143 void lex_error(const char *message,
1144                const errarg &arg1,
1145                const errarg &arg2,
1146                const errarg &arg3)
1147 {
1148   char *filename;
1149   int lineno;
1150   if (!get_location(&filename, &lineno))
1151     error(message, arg1, arg2, arg3);
1152   else
1153     error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
1154 }
1155
1156 void yyerror(const char *s)
1157 {
1158   char *filename;
1159   int lineno;
1160   if (!get_location(&filename, &lineno))
1161     error(s);
1162   else
1163     error_with_file_and_line(filename, lineno, s);
1164   show_context();
1165 }
1166