lex.cpp revision 114402
1// -*- C++ -*-
2/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003
3   Free Software Foundation, Inc.
4     Written by James Clark (jjc@jclark.com)
5
6This file is part of groff.
7
8groff is free software; you can redistribute it and/or modify it under
9the terms of the GNU General Public License as published by the Free
10Software Foundation; either version 2, or (at your option) any later
11version.
12
13groff is distributed in the hope that it will be useful, but WITHOUT ANY
14WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16for more details.
17
18You should have received a copy of the GNU General Public License along
19with groff; see the file COPYING.  If not, write to the Free Software
20Foundation, 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
27struct definition {
28  char is_macro;
29  char is_simple;
30  union {
31    int tok;
32    char *contents;
33  };
34  definition();
35  ~definition();
36};
37
38definition::definition() : is_macro(1), is_simple(0)
39{
40  contents = 0;
41}
42
43definition::~definition()
44{
45  if (is_macro)
46    a_delete contents;
47}
48
49declare_ptable(definition)
50implement_ptable(definition)
51
52PTABLE(definition) macro_table;
53
54static 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
117static 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
253void 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[1];
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[1];
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[1];
270  def->is_macro = 1;
271  def->contents = strsave("1");
272  macro_table.define(device, def);
273}
274
275class input {
276  input *next;
277public:
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
290class file_input : public input {
291  FILE *fp;
292  char *filename;
293  int lineno;
294  string line;
295  const char *ptr;
296  int read_line();
297public:
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
306class macro_input : public input {
307  char *s;
308  char *p;
309public:
310  macro_input(const char *, input *);
311  ~macro_input();
312  int get();
313  int peek();
314};
315
316class 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
326class argument_macro_input: public input {
327  char *s;
328  char *p;
329  char *ap;
330  int argc;
331  char *argv[9];
332public:
333  argument_macro_input(const char *, int, char **, input *);
334  ~argument_macro_input();
335  int get();
336  int peek();
337};
338
339input::input(input *x) : next(x)
340{
341}
342
343input::~input()
344{
345}
346
347int input::get_location(char **, int *)
348{
349  return 0;
350}
351
352file_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
359file_input::~file_input()
360{
361  a_delete filename;
362  fclose(fp);
363}
364
365int 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
395int file_input::get()
396{
397  if (*ptr != '\0' || read_line())
398    return *ptr++ & 0377;
399  else
400    return EOF;
401}
402
403int file_input::peek()
404{
405  if (*ptr != '\0' || read_line())
406    return *ptr;
407  else
408    return EOF;
409}
410
411int file_input::get_location(char **fnp, int *lnp)
412{
413  *fnp = filename;
414  *lnp = lineno;
415  return 1;
416}
417
418macro_input::macro_input(const char *str, input *x) : input(x)
419{
420  p = s = strsave(str);
421}
422
423macro_input::~macro_input()
424{
425  a_delete s;
426}
427
428int macro_input::get()
429{
430  if (p == 0 || *p == '\0')
431    return EOF;
432  else
433    return *p++ & 0377;
434}
435
436int macro_input::peek()
437{
438  if (p == 0 || *p == '\0')
439    return EOF;
440  else
441    return *p & 0377;
442}
443
444top_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
450top_input::~top_input()
451{
452  a_delete filename;
453}
454
455int top_input::get()
456{
457  int c = macro_input::get();
458  if (c == '\n')
459    lineno++;
460  return c;
461}
462
463int 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
473argument_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
493argument_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
500int 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
521int 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
542static input *current_input = 0;
543
544/* we insert a newline between input from different levels */
545
546int 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
563int 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
576int 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
584string token_buffer;
585const int NCONTEXT = 4;
586string context_ring[NCONTEXT];
587int context_index = 0;
588
589void flush_context()
590{
591  for (int i = 0; i < NCONTEXT; i++)
592    context_ring[i] = "";
593  context_index = 0;
594}
595
596void 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
617void add_context(const string &s)
618{
619  context_ring[context_index] = s;
620  context_index = (context_index + 1) % NCONTEXT;
621}
622
623void add_context(char c)
624{
625  context_ring[context_index] = c;
626  context_index = (context_index + 1) % NCONTEXT;
627}
628
629void 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
642void 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
654void 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
689void 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
727if it is macro. If it's 1, it will looked up to see if it's a token.
728*/
729
730int 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
874void 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
892void 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
902void 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[1];
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
926void 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
937void 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
949void 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
960void 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
971void 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
982void 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
998void 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
1015void 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
1035void 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
1053void 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
1076int 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
1143void 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
1156void 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
1167