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