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
definition()44 definition::definition() : is_macro(1), is_simple(0)
45 {
46 contents = 0;
47 }
48
~definition()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
init_table(const char * device)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
input(input * x)345 input::input(input *x) : next(x)
346 {
347 }
348
~input()349 input::~input()
350 {
351 }
352
get_location(char **,int *)353 int input::get_location(char **, int *)
354 {
355 return 0;
356 }
357
file_input(FILE * f,const char * fn,input * p)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
~file_input()365 file_input::~file_input()
366 {
367 a_delete filename;
368 fclose(fp);
369 }
370
read_line()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
get()401 int file_input::get()
402 {
403 if (*ptr != '\0' || read_line())
404 return *ptr++ & 0377;
405 else
406 return EOF;
407 }
408
peek()409 int file_input::peek()
410 {
411 if (*ptr != '\0' || read_line())
412 return *ptr;
413 else
414 return EOF;
415 }
416
get_location(char ** fnp,int * lnp)417 int file_input::get_location(char **fnp, int *lnp)
418 {
419 *fnp = filename;
420 *lnp = lineno;
421 return 1;
422 }
423
macro_input(const char * str,input * x)424 macro_input::macro_input(const char *str, input *x) : input(x)
425 {
426 p = s = strsave(str);
427 }
428
~macro_input()429 macro_input::~macro_input()
430 {
431 a_delete s;
432 }
433
get()434 int macro_input::get()
435 {
436 if (p == 0 || *p == '\0')
437 return EOF;
438 else
439 return *p++ & 0377;
440 }
441
peek()442 int macro_input::peek()
443 {
444 if (p == 0 || *p == '\0')
445 return EOF;
446 else
447 return *p & 0377;
448 }
449
top_input(const char * str,const char * fn,int ln,input * x)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
~top_input()456 top_input::~top_input()
457 {
458 a_delete filename;
459 }
460
get()461 int top_input::get()
462 {
463 int c = macro_input::get();
464 if (c == '\n')
465 lineno++;
466 return c;
467 }
468
get_location(char ** fnp,int * lnp)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
argument_macro_input(const char * body,int ac,char ** av,input * x)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
~argument_macro_input()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
get()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
peek()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
get_char()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
peek_char()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
get_location(char ** fnp,int * lnp)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
flush_context()595 void flush_context()
596 {
597 for (int i = 0; i < NCONTEXT; i++)
598 context_ring[i] = "";
599 context_index = 0;
600 }
601
show_context()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
add_context(const string & s)623 void add_context(const string &s)
624 {
625 context_ring[context_index] = s;
626 context_index = (context_index + 1) % NCONTEXT;
627 }
628
add_context(char c)629 void add_context(char c)
630 {
631 context_ring[context_index] = c;
632 context_index = (context_index + 1) % NCONTEXT;
633 }
634
add_quoted_context(const string & s)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
init_lex(const char * str,const char * filename,int lineno)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
get_delimited_text()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
interpolate_macro_with_args(const char * body)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
get_token(int lookup_flag=0)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
do_include()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
ignore_definition()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
do_definition(int is_simple)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
do_undef()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
do_gsize()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
do_gfont()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
do_grfont()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
do_gbfont()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
do_space()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
do_ifdef()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
do_delim()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
do_chartype()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
do_set()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
yylex()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
lex_error(const char * message,const errarg & arg1,const errarg & arg2,const errarg & arg3)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
yyerror(const char * s)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
1173