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