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