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
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, 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
29int get_char();
30int peek_char();
31int get_location(char **, int *);
32
33struct definition {
34  char is_macro;
35  char is_simple;
36  union {
37    int tok;
38    char *contents;
39  };
40  definition();
41  ~definition();
42};
43
44definition::definition() : is_macro(1), is_simple(0)
45{
46  contents = 0;
47}
48
49definition::~definition()
50{
51  if (is_macro)
52    a_delete contents;
53}
54
55declare_ptable(definition)
56implement_ptable(definition)
57
58PTABLE(definition) macro_table;
59
60static 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
123static 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
259void 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
281class input {
282  input *next;
283public:
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
296class file_input : public input {
297  FILE *fp;
298  char *filename;
299  int lineno;
300  string line;
301  const char *ptr;
302  int read_line();
303public:
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
312class macro_input : public input {
313  char *s;
314  char *p;
315public:
316  macro_input(const char *, input *);
317  ~macro_input();
318  int get();
319  int peek();
320};
321
322class 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
332class argument_macro_input: public input {
333  char *s;
334  char *p;
335  char *ap;
336  int argc;
337  char *argv[9];
338public:
339  argument_macro_input(const char *, int, char **, input *);
340  ~argument_macro_input();
341  int get();
342  int peek();
343};
344
345input::input(input *x) : next(x)
346{
347}
348
349input::~input()
350{
351}
352
353int input::get_location(char **, int *)
354{
355  return 0;
356}
357
358file_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
365file_input::~file_input()
366{
367  a_delete filename;
368  fclose(fp);
369}
370
371int 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
401int file_input::get()
402{
403  if (*ptr != '\0' || read_line())
404    return *ptr++ & 0377;
405  else
406    return EOF;
407}
408
409int file_input::peek()
410{
411  if (*ptr != '\0' || read_line())
412    return *ptr;
413  else
414    return EOF;
415}
416
417int file_input::get_location(char **fnp, int *lnp)
418{
419  *fnp = filename;
420  *lnp = lineno;
421  return 1;
422}
423
424macro_input::macro_input(const char *str, input *x) : input(x)
425{
426  p = s = strsave(str);
427}
428
429macro_input::~macro_input()
430{
431  a_delete s;
432}
433
434int macro_input::get()
435{
436  if (p == 0 || *p == '\0')
437    return EOF;
438  else
439    return *p++ & 0377;
440}
441
442int macro_input::peek()
443{
444  if (p == 0 || *p == '\0')
445    return EOF;
446  else
447    return *p & 0377;
448}
449
450top_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
456top_input::~top_input()
457{
458  a_delete filename;
459}
460
461int top_input::get()
462{
463  int c = macro_input::get();
464  if (c == '\n')
465    lineno++;
466  return c;
467}
468
469int 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
479argument_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
499argument_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
506int 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
527int 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
548static input *current_input = 0;
549
550/* we insert a newline between input from different levels */
551
552int 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
569int 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
582int 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
590string token_buffer;
591const int NCONTEXT = 4;
592string context_ring[NCONTEXT];
593int context_index = 0;
594
595void flush_context()
596{
597  for (int i = 0; i < NCONTEXT; i++)
598    context_ring[i] = "";
599  context_index = 0;
600}
601
602void 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
623void add_context(const string &s)
624{
625  context_ring[context_index] = s;
626  context_index = (context_index + 1) % NCONTEXT;
627}
628
629void add_context(char c)
630{
631  context_ring[context_index] = c;
632  context_index = (context_index + 1) % NCONTEXT;
633}
634
635void 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
648void 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
660void 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
695void 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
733if it is macro. If it's 1, it will looked up to see if it's a token.
734*/
735
736int 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
880void 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
898void 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
908void 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
932void 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
943void 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
955void 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
966void 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
977void 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
988void 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
1004void 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
1021void 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
1041void 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
1059void 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
1082int 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
1149void 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
1162void 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
1173