Line data Source code
1 : /* Copyright 1989-2024 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 <assert.h>
24 : #include <errno.h>
25 : #include <math.h> // pow()
26 : #include <stdcountof.h>
27 : #include <stdio.h> // EOF, FILE, fclose(), fopen(), getc(), ungetc()
28 : #include <string.h> // strerror()
29 :
30 : #include "pic.h" // must precede object.h
31 :
32 : #include "ptable.h" // must precede object.h
33 : #include "object.h"
34 : #include "pic.hpp"
35 :
36 : declare_ptable(char)
37 5064 : implement_ptable(char)
38 :
39 : PTABLE(char) macro_table;
40 :
41 : // First character of the range representing $1-$<MAX_ARG>.
42 : // All of them must be invalid input characters.
43 : #define ARG1 0x80
44 : #define MAX_ARG 32
45 :
46 : class macro_input : public input {
47 : char *s;
48 : char *p;
49 : public:
50 : macro_input(const char *);
51 : ~macro_input();
52 : int get();
53 : int peek();
54 : };
55 :
56 : class argument_macro_input : public input {
57 : char *s;
58 : char *p;
59 : char *ap;
60 : int argc;
61 : char *argv[MAX_ARG];
62 : public:
63 : argument_macro_input(const char *, int, char **);
64 : ~argument_macro_input();
65 : int get();
66 : int peek();
67 : };
68 :
69 364 : input::input() : next(0)
70 : {
71 364 : }
72 :
73 351 : input::~input()
74 : {
75 351 : }
76 :
77 540 : int input::get_location(const char **, int *)
78 : {
79 540 : return 0;
80 : }
81 :
82 0 : file_input::file_input(FILE *f, const char *fn)
83 0 : : fp(f), filename(fn), lineno(0), ptr("")
84 : {
85 0 : }
86 :
87 0 : file_input::~file_input()
88 : {
89 0 : fclose(fp);
90 0 : }
91 :
92 0 : int file_input::read_line()
93 : {
94 : for (;;) {
95 0 : line.clear();
96 0 : lineno++;
97 : for (;;) {
98 0 : int c = getc(fp);
99 0 : if (c == '\r') {
100 0 : c = getc(fp);
101 0 : if (c != '\n')
102 0 : lex_error("invalid input character code %1", '\r');
103 : }
104 0 : if (c == EOF)
105 0 : break;
106 0 : else if (is_invalid_input_char(c))
107 0 : lex_error("invalid input character code %1", c);
108 : else {
109 0 : line += char(c);
110 0 : if (c == '\n')
111 0 : break;
112 : }
113 0 : }
114 0 : if (line.length() == 0)
115 0 : return 0;
116 0 : if (!(line.length() >= 3 && line[0] == '.' && line[1] == 'P'
117 0 : && (line[2] == 'S' || line[2] == 'E' || line[2] == 'F')
118 0 : && (line.length() == 3 || line[3] == ' ' || line[3] == '\n'
119 0 : || compatible_flag))) {
120 0 : line += '\0';
121 0 : ptr = line.contents();
122 0 : return 1;
123 : }
124 0 : }
125 : }
126 :
127 0 : int file_input::get()
128 : {
129 0 : if (*ptr != '\0' || read_line())
130 0 : return (unsigned char)*ptr++;
131 : else
132 0 : return EOF;
133 : }
134 :
135 0 : int file_input::peek()
136 : {
137 0 : if (*ptr != '\0' || read_line())
138 0 : return (unsigned char)*ptr;
139 : else
140 0 : return EOF;
141 : }
142 :
143 0 : int file_input::get_location(const char **fnp, int *lnp)
144 : {
145 0 : *fnp = filename;
146 0 : *lnp = lineno;
147 0 : return 1;
148 : }
149 :
150 22 : macro_input::macro_input(const char *str)
151 : {
152 22 : p = s = strsave(str);
153 22 : }
154 :
155 44 : macro_input::~macro_input()
156 : {
157 22 : free(s);
158 44 : }
159 :
160 462 : int macro_input::get()
161 : {
162 462 : if (p == 0 || *p == '\0')
163 0 : return EOF;
164 : else
165 462 : return (unsigned char)*p++;
166 : }
167 :
168 374 : int macro_input::peek()
169 : {
170 374 : if (p == 0 || *p == '\0')
171 22 : return EOF;
172 : else
173 352 : return (unsigned char)*p;
174 : }
175 :
176 212 : char *process_body(const char *body)
177 : {
178 212 : char *s = strsave(body);
179 212 : int j = 0;
180 29230 : for (int i = 0; s[i] != '\0'; i++)
181 29018 : if (s[i] == '$' && csdigit(s[i + 1])) {
182 360 : int n = 0;
183 360 : int start = i;
184 360 : i++;
185 720 : while (csdigit(s[i]))
186 360 : if (n > MAX_ARG)
187 0 : i++;
188 : else
189 360 : n = 10 * n + s[i++] - '0';
190 360 : if (n > MAX_ARG) {
191 0 : string arg;
192 0 : for (int k = start; k < i; k++)
193 0 : arg += s[k];
194 0 : lex_error("invalid macro argument number %1", arg.contents());
195 : }
196 360 : else if (n > 0)
197 360 : s[j++] = ARG1 + n - 1;
198 360 : i--;
199 : }
200 : else
201 28658 : s[j++] = s[i];
202 212 : s[j] = '\0';
203 212 : return s;
204 : }
205 :
206 212 : argument_macro_input::argument_macro_input(const char *body, int ac, char **av)
207 212 : : ap(0), argc(ac)
208 : {
209 490 : for (int i = 0; i < argc; i++)
210 278 : argv[i] = av[i];
211 212 : p = s = process_body(body);
212 212 : }
213 :
214 424 : argument_macro_input::~argument_macro_input()
215 : {
216 490 : for (int i = 0; i < argc; i++)
217 278 : free(argv[i]);
218 212 : free(s);
219 424 : }
220 :
221 30262 : int argument_macro_input::get()
222 : {
223 30262 : if (ap) {
224 1268 : if (*ap != '\0')
225 1132 : return (unsigned char)*ap++;
226 136 : ap = 0;
227 : }
228 29130 : if (p == 0)
229 0 : return EOF;
230 0 : while ((unsigned char)*p >= ARG1
231 29130 : && (unsigned char)*p <= ARG1 + MAX_ARG - 1) {
232 324 : int i = (unsigned char)*p++ - ARG1;
233 324 : if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
234 324 : ap = argv[i];
235 324 : return (unsigned char)*ap++;
236 : }
237 : }
238 28806 : if (*p == '\0')
239 148 : return EOF;
240 28658 : return (unsigned char)*p++;
241 : }
242 :
243 15762 : int argument_macro_input::peek()
244 : {
245 15762 : if (ap) {
246 1172 : if (*ap != '\0')
247 948 : return (unsigned char)*ap;
248 224 : ap = 0;
249 : }
250 14814 : if (p == 0)
251 0 : return EOF;
252 0 : while ((unsigned char)*p >= ARG1
253 14814 : && (unsigned char)*p <= ARG1 + MAX_ARG - 1) {
254 36 : int i = (unsigned char)*p++ - ARG1;
255 36 : if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
256 36 : ap = argv[i];
257 36 : return (unsigned char)*ap;
258 : }
259 : }
260 14778 : if (*p == '\0')
261 64 : return EOF;
262 14714 : return (unsigned char)*p;
263 : }
264 :
265 : class input_stack {
266 : static input *current_input;
267 : static int bol_flag;
268 : public:
269 : static void push(input *);
270 : static void clear();
271 : static int get_char();
272 : static int peek_char();
273 : static int get_location(const char **fnp, int *lnp);
274 : static void push_back(unsigned char c, int was_bol = 0);
275 : static int bol();
276 : };
277 :
278 : input *input_stack::current_input = 0;
279 : int input_stack::bol_flag = 0;
280 :
281 31220 : inline int input_stack::bol()
282 : {
283 31220 : return bol_flag;
284 : }
285 :
286 215 : void input_stack::clear()
287 : {
288 215 : while (current_input != 0) {
289 101 : input *tem = current_input;
290 101 : current_input = current_input->next;
291 101 : delete tem;
292 : }
293 114 : bol_flag = 1;
294 114 : }
295 :
296 364 : void input_stack::push(input *in)
297 : {
298 364 : in->next = current_input;
299 364 : current_input = in;
300 364 : }
301 :
302 114 : void lex_init(input *top)
303 : {
304 114 : input_stack::clear();
305 114 : input_stack::push(top);
306 114 : }
307 :
308 114 : void lex_cleanup()
309 : {
310 114 : while (input_stack::get_char() != EOF)
311 : ;
312 114 : }
313 :
314 72608 : int input_stack::get_char()
315 : {
316 72608 : while (current_input != 0) {
317 72608 : int c = current_input->get();
318 72608 : if (c != EOF) {
319 72216 : bol_flag = c == '\n';
320 72216 : return c;
321 : }
322 : // don't pop the top-level input off the stack
323 392 : if (current_input->next == 0)
324 228 : return EOF;
325 164 : input *tem = current_input;
326 164 : current_input = current_input->next;
327 164 : delete tem;
328 : }
329 0 : return EOF;
330 : }
331 :
332 31208 : int input_stack::peek_char()
333 : {
334 31208 : while (current_input != 0) {
335 31208 : int c = current_input->peek();
336 31208 : if (c != EOF)
337 31122 : return c;
338 86 : if (current_input->next == 0)
339 0 : return EOF;
340 86 : input *tem = current_input;
341 86 : current_input = current_input->next;
342 86 : delete tem;
343 : }
344 0 : return EOF;
345 : }
346 :
347 : class char_input : public input {
348 : int c;
349 : public:
350 : char_input(int);
351 : int get();
352 : int peek();
353 : };
354 :
355 0 : char_input::char_input(int n) : c((unsigned char)n)
356 : {
357 0 : }
358 :
359 0 : int char_input::get()
360 : {
361 0 : int n = c;
362 0 : c = EOF;
363 0 : return n;
364 : }
365 :
366 0 : int char_input::peek()
367 : {
368 0 : return c;
369 : }
370 :
371 0 : void input_stack::push_back(unsigned char c, int was_bol)
372 : {
373 0 : push(new char_input(c));
374 0 : bol_flag = was_bol;
375 0 : }
376 :
377 937 : int input_stack::get_location(const char **fnp, int *lnp)
378 : {
379 1477 : for (input *p = current_input; p; p = p->next)
380 1477 : if (p->get_location(fnp, lnp))
381 937 : return 1;
382 0 : return 0;
383 : }
384 :
385 : string context_buffer;
386 :
387 : string token_buffer;
388 : double token_double;
389 : int token_int;
390 :
391 212 : void interpolate_macro_with_args(const char *body)
392 : {
393 : char *argv[MAX_ARG];
394 212 : int argc = 0;
395 212 : int ignore = 0;
396 : int i;
397 6996 : for (i = 0; i < MAX_ARG; i++)
398 6784 : argv[i] = 0;
399 212 : int level = 0;
400 : int c;
401 212 : enum { NORMAL, IN_STRING, IN_STRING_QUOTED } state = NORMAL;
402 89 : do {
403 301 : token_buffer.clear();
404 : for (;;) {
405 1789 : c = input_stack::get_char();
406 1789 : if (c == EOF) {
407 0 : lex_error("end of input while scanning macro arguments");
408 0 : break;
409 : }
410 1789 : if (state == NORMAL && level == 0 && (c == ',' || c == ')')) {
411 301 : if (!ignore) {
412 279 : if (argc == MAX_ARG) {
413 1 : lex_warning("pic supports at most %1 macro arguments",
414 1 : MAX_ARG);
415 1 : ignore = 1;
416 : }
417 278 : else if (token_buffer.length() > 0) {
418 272 : token_buffer += '\0';
419 272 : argv[argc] = strsave(token_buffer.contents());
420 : }
421 : }
422 : // for 'foo()', argc = 0
423 301 : if (argc > 0 || c != ')' || i > 0)
424 301 : if (!ignore)
425 278 : argc++;
426 301 : break;
427 : }
428 1488 : token_buffer += char(c);
429 1488 : switch (state) {
430 1458 : case NORMAL:
431 1458 : if (c == '"')
432 6 : state = IN_STRING;
433 1452 : else if (c == '(')
434 2 : level++;
435 1450 : else if (c == ')')
436 2 : level--;
437 1458 : break;
438 30 : case IN_STRING:
439 30 : if (c == '"')
440 6 : state = NORMAL;
441 24 : else if (c == '\\')
442 0 : state = IN_STRING_QUOTED;
443 30 : break;
444 0 : case IN_STRING_QUOTED:
445 0 : state = IN_STRING;
446 0 : break;
447 : }
448 : }
449 301 : } while (c != ')' && c != EOF);
450 212 : input_stack::push(new argument_macro_input(body, argc, argv));
451 212 : }
452 :
453 38811 : static int docmp(const char *s1, int n1, const char *s2, int n2)
454 : {
455 38811 : if (n1 < n2) {
456 16502 : int r = memcmp(s1, s2, n1);
457 16502 : return r ? r : -1;
458 : }
459 22309 : else if (n1 > n2) {
460 12624 : int r = memcmp(s1, s2, n2);
461 12624 : return r ? r : 1;
462 : }
463 : else
464 9685 : return memcmp(s1, s2, n1);
465 : }
466 :
467 6618 : int lookup_keyword(const char *str, int len)
468 : {
469 : static struct keyword {
470 : const char *name;
471 : int token;
472 : } table[] = {
473 : { "Here", HERE },
474 : { "above", ABOVE },
475 : { "aligned", ALIGNED },
476 : { "and", AND },
477 : { "arc", ARC },
478 : { "arrow", ARROW },
479 : { "at", AT },
480 : { "atan2", ATAN2 },
481 : { "below", BELOW },
482 : { "between", BETWEEN },
483 : { "bottom", BOTTOM },
484 : { "box", BOX },
485 : { "by", BY },
486 : { "ccw", CCW },
487 : { "center", CENTER },
488 : { "chop", CHOP },
489 : { "circle", CIRCLE },
490 : { "color", COLORED },
491 : { "colored", COLORED },
492 : { "colour", COLORED },
493 : { "coloured", COLORED },
494 : { "command", COMMAND },
495 : { "copy", COPY },
496 : { "cos", COS },
497 : { "cw", CW },
498 : { "dashed", DASHED },
499 : { "define", DEFINE },
500 : { "diam", DIAMETER },
501 : { "diameter", DIAMETER },
502 : { "do", DO },
503 : { "dotted", DOTTED },
504 : { "down", DOWN },
505 : { "east", EAST },
506 : { "ellipse", ELLIPSE },
507 : { "else", ELSE },
508 : { "end", END },
509 : { "exp", EXP },
510 : { "figname", FIGNAME },
511 : { "fill", FILL },
512 : { "filled", FILL },
513 : { "for", FOR },
514 : { "from", FROM },
515 : { "height", HEIGHT },
516 : { "ht", HEIGHT },
517 : { "if", IF },
518 : { "int", INT },
519 : { "invis", INVISIBLE },
520 : { "invisible", INVISIBLE },
521 : { "last", LAST },
522 : { "left", LEFT },
523 : { "line", LINE },
524 : { "ljust", LJUST },
525 : { "log", LOG },
526 : { "lower", LOWER },
527 : { "max", K_MAX },
528 : { "min", K_MIN },
529 : { "move", MOVE },
530 : { "north", NORTH },
531 : { "of", OF },
532 : { "outline", OUTLINED },
533 : { "outlined", OUTLINED },
534 : { "plot", PLOT },
535 : { "polygon", POLYGON },
536 : { "print", PRINT },
537 : { "rad", RADIUS },
538 : { "radius", RADIUS },
539 : { "rand", RAND },
540 : { "reset", RESET },
541 : { "right", RIGHT },
542 : { "rjust", RJUST },
543 : { "same", SAME },
544 : { "sh", SH },
545 : { "shaded", SHADED },
546 : { "sin", SIN },
547 : { "solid", SOLID },
548 : { "south", SOUTH },
549 : { "spline", SPLINE },
550 : { "sprintf", SPRINTF },
551 : { "sqrt", SQRT },
552 : { "srand", SRAND },
553 : { "start", START },
554 : { "the", THE },
555 : { "then", THEN },
556 : { "thick", THICKNESS },
557 : { "thickness", THICKNESS },
558 : { "thru", THRU },
559 : { "to", TO },
560 : { "top", TOP },
561 : { "undef", UNDEF },
562 : { "until", UNTIL },
563 : { "up", UP },
564 : { "upper", UPPER },
565 : { "way", WAY },
566 : { "west", WEST },
567 : { "wid", WIDTH },
568 : { "width", WIDTH },
569 : { "with", WITH },
570 : { "xslanted", XSLANTED },
571 : { "yslanted", YSLANTED },
572 : };
573 :
574 6618 : const keyword *start = table;
575 6618 : const keyword *end = table + countof(table);
576 40569 : while (start < end) {
577 : // start <= target < end
578 38811 : const keyword *mid = start + (end - start)/2;
579 :
580 38811 : int cmp = docmp(str, len, mid->name, strlen(mid->name));
581 38811 : if (cmp == 0)
582 4860 : return mid->token;
583 33951 : if (cmp < 0)
584 20170 : end = mid;
585 : else
586 13781 : start = mid + 1;
587 : }
588 1758 : return 0;
589 : }
590 :
591 783 : int get_token_after_dot(int c)
592 : {
593 : // get_token deals with the case where c is a digit
594 783 : switch (c) {
595 42 : case 'h':
596 42 : input_stack::get_char();
597 42 : c = input_stack::peek_char();
598 42 : if (c == 't') {
599 42 : input_stack::get_char();
600 42 : context_buffer = ".ht";
601 42 : return DOT_HT;
602 : }
603 0 : else if (c == 'e') {
604 0 : input_stack::get_char();
605 0 : c = input_stack::peek_char();
606 0 : if (c == 'i') {
607 0 : input_stack::get_char();
608 0 : c = input_stack::peek_char();
609 0 : if (c == 'g') {
610 0 : input_stack::get_char();
611 0 : c = input_stack::peek_char();
612 0 : if (c == 'h') {
613 0 : input_stack::get_char();
614 0 : c = input_stack::peek_char();
615 0 : if (c == 't') {
616 0 : input_stack::get_char();
617 0 : context_buffer = ".height";
618 0 : return DOT_HT;
619 : }
620 0 : input_stack::push_back('h');
621 : }
622 0 : input_stack::push_back('g');
623 : }
624 0 : input_stack::push_back('i');
625 : }
626 0 : input_stack::push_back('e');
627 : }
628 0 : input_stack::push_back('h');
629 0 : return '.';
630 0 : case 'x':
631 0 : input_stack::get_char();
632 0 : context_buffer = ".x";
633 0 : return DOT_X;
634 0 : case 'y':
635 0 : input_stack::get_char();
636 0 : context_buffer = ".y";
637 0 : return DOT_Y;
638 62 : case 'c':
639 62 : input_stack::get_char();
640 62 : c = input_stack::peek_char();
641 62 : if (c == 'e') {
642 32 : input_stack::get_char();
643 32 : c = input_stack::peek_char();
644 32 : if (c == 'n') {
645 32 : input_stack::get_char();
646 32 : c = input_stack::peek_char();
647 32 : if (c == 't') {
648 32 : input_stack::get_char();
649 32 : c = input_stack::peek_char();
650 32 : if (c == 'e') {
651 32 : input_stack::get_char();
652 32 : c = input_stack::peek_char();
653 32 : if (c == 'r') {
654 32 : input_stack::get_char();
655 32 : context_buffer = ".center";
656 32 : return DOT_C;
657 : }
658 0 : input_stack::push_back('e');
659 : }
660 0 : input_stack::push_back('t');
661 : }
662 0 : input_stack::push_back('n');
663 : }
664 0 : input_stack::push_back('e');
665 : }
666 30 : context_buffer = ".c";
667 30 : return DOT_C;
668 171 : case 'n':
669 171 : input_stack::get_char();
670 171 : c = input_stack::peek_char();
671 171 : if (c == 'e') {
672 35 : input_stack::get_char();
673 35 : context_buffer = ".ne";
674 35 : return DOT_NE;
675 : }
676 136 : else if (c == 'w') {
677 71 : input_stack::get_char();
678 71 : context_buffer = ".nw";
679 71 : return DOT_NW;
680 : }
681 : else {
682 65 : context_buffer = ".n";
683 65 : return DOT_N;
684 : }
685 : break;
686 113 : case 'e':
687 113 : input_stack::get_char();
688 113 : c = input_stack::peek_char();
689 113 : if (c == 'n') {
690 48 : input_stack::get_char();
691 48 : c = input_stack::peek_char();
692 48 : if (c == 'd') {
693 48 : input_stack::get_char();
694 48 : context_buffer = ".end";
695 48 : return DOT_END;
696 : }
697 0 : input_stack::push_back('n');
698 0 : context_buffer = ".e";
699 0 : return DOT_E;
700 : }
701 65 : context_buffer = ".e";
702 65 : return DOT_E;
703 71 : case 'w':
704 71 : input_stack::get_char();
705 71 : c = input_stack::peek_char();
706 71 : if (c == 'i') {
707 42 : input_stack::get_char();
708 42 : c = input_stack::peek_char();
709 42 : if (c == 'd') {
710 42 : input_stack::get_char();
711 42 : c = input_stack::peek_char();
712 42 : if (c == 't') {
713 0 : input_stack::get_char();
714 0 : c = input_stack::peek_char();
715 0 : if (c == 'h') {
716 0 : input_stack::get_char();
717 0 : context_buffer = ".width";
718 0 : return DOT_WID;
719 : }
720 0 : input_stack::push_back('t');
721 : }
722 42 : context_buffer = ".wid";
723 42 : return DOT_WID;
724 : }
725 0 : input_stack::push_back('i');
726 : }
727 29 : context_buffer = ".w";
728 29 : return DOT_W;
729 237 : case 's':
730 237 : input_stack::get_char();
731 237 : c = input_stack::peek_char();
732 237 : if (c == 'e') {
733 57 : input_stack::get_char();
734 57 : context_buffer = ".se";
735 57 : return DOT_SE;
736 : }
737 180 : else if (c == 'w') {
738 57 : input_stack::get_char();
739 57 : context_buffer = ".sw";
740 57 : return DOT_SW;
741 : }
742 : else {
743 123 : if (c == 't') {
744 22 : input_stack::get_char();
745 22 : c = input_stack::peek_char();
746 22 : if (c == 'a') {
747 22 : input_stack::get_char();
748 22 : c = input_stack::peek_char();
749 22 : if (c == 'r') {
750 22 : input_stack::get_char();
751 22 : c = input_stack::peek_char();
752 22 : if (c == 't') {
753 22 : input_stack::get_char();
754 22 : context_buffer = ".start";
755 22 : return DOT_START;
756 : }
757 0 : input_stack::push_back('r');
758 : }
759 0 : input_stack::push_back('a');
760 : }
761 0 : input_stack::push_back('t');
762 : }
763 101 : context_buffer = ".s";
764 101 : return DOT_S;
765 : }
766 : break;
767 6 : case 't':
768 6 : input_stack::get_char();
769 6 : c = input_stack::peek_char();
770 6 : if (c == 'o') {
771 6 : input_stack::get_char();
772 6 : c = input_stack::peek_char();
773 6 : if (c == 'p') {
774 6 : input_stack::get_char();
775 6 : context_buffer = ".top";
776 6 : return DOT_N;
777 : }
778 0 : input_stack::push_back('o');
779 : }
780 0 : context_buffer = ".t";
781 0 : return DOT_N;
782 0 : case 'l':
783 0 : input_stack::get_char();
784 0 : c = input_stack::peek_char();
785 0 : if (c == 'e') {
786 0 : input_stack::get_char();
787 0 : c = input_stack::peek_char();
788 0 : if (c == 'f') {
789 0 : input_stack::get_char();
790 0 : c = input_stack::peek_char();
791 0 : if (c == 't') {
792 0 : input_stack::get_char();
793 0 : context_buffer = ".left";
794 0 : return DOT_W;
795 : }
796 0 : input_stack::push_back('f');
797 : }
798 0 : input_stack::push_back('e');
799 : }
800 0 : context_buffer = ".l";
801 0 : return DOT_W;
802 11 : case 'r':
803 11 : input_stack::get_char();
804 11 : c = input_stack::peek_char();
805 11 : if (c == 'a') {
806 0 : input_stack::get_char();
807 0 : c = input_stack::peek_char();
808 0 : if (c == 'd') {
809 0 : input_stack::get_char();
810 0 : context_buffer = ".rad";
811 0 : return DOT_RAD;
812 : }
813 0 : input_stack::push_back('a');
814 : }
815 11 : else if (c == 'i') {
816 9 : input_stack::get_char();
817 9 : c = input_stack::peek_char();
818 9 : if (c == 'g') {
819 9 : input_stack::get_char();
820 9 : c = input_stack::peek_char();
821 9 : if (c == 'h') {
822 9 : input_stack::get_char();
823 9 : c = input_stack::peek_char();
824 9 : if (c == 't') {
825 9 : input_stack::get_char();
826 9 : context_buffer = ".right";
827 9 : return DOT_E;
828 : }
829 0 : input_stack::push_back('h');
830 : }
831 0 : input_stack::push_back('g');
832 : }
833 0 : input_stack::push_back('i');
834 : }
835 2 : context_buffer = ".r";
836 2 : return DOT_E;
837 12 : case 'b':
838 12 : input_stack::get_char();
839 12 : c = input_stack::peek_char();
840 12 : if (c == 'o') {
841 12 : input_stack::get_char();
842 12 : c = input_stack::peek_char();
843 12 : if (c == 't') {
844 12 : input_stack::get_char();
845 12 : c = input_stack::peek_char();
846 12 : if (c == 't') {
847 12 : input_stack::get_char();
848 12 : c = input_stack::peek_char();
849 12 : if (c == 'o') {
850 12 : input_stack::get_char();
851 12 : c = input_stack::peek_char();
852 12 : if (c == 'm') {
853 12 : input_stack::get_char();
854 12 : context_buffer = ".bottom";
855 12 : return DOT_S;
856 : }
857 0 : input_stack::push_back('o');
858 : }
859 0 : input_stack::push_back('t');
860 : }
861 0 : context_buffer = ".bot";
862 0 : return DOT_S;
863 : }
864 0 : input_stack::push_back('o');
865 : }
866 0 : context_buffer = ".b";
867 0 : return DOT_S;
868 18 : case 'v':
869 18 : input_stack::get_char();
870 18 : c = input_stack::peek_char();
871 18 : if (c == 'e') {
872 0 : input_stack::get_char();
873 0 : c = input_stack::peek_char();
874 0 : if (c == 'r') {
875 0 : input_stack::get_char();
876 0 : c = input_stack::peek_char();
877 0 : if (c == 't') {
878 0 : input_stack::get_char();
879 0 : c = input_stack::peek_char();
880 0 : if (c == 'e') {
881 0 : input_stack::get_char();
882 0 : c = input_stack::peek_char();
883 0 : if (c == 'x') {
884 0 : input_stack::get_char();
885 0 : context_buffer = ".vertex";
886 0 : return DOT_V;
887 : }
888 : }
889 0 : context_buffer = ".ver";
890 0 : return DOT_V;
891 : }
892 : }
893 : }
894 18 : context_buffer = ".v";
895 18 : return DOT_V;
896 18 : case 'm':
897 18 : input_stack::get_char();
898 18 : c = input_stack::peek_char();
899 18 : if (c == 'i') {
900 18 : input_stack::get_char();
901 18 : c = input_stack::peek_char();
902 18 : if (c == 'd') {
903 18 : input_stack::get_char();
904 18 : c = input_stack::peek_char();
905 18 : if (c == 'p') {
906 0 : input_stack::get_char();
907 0 : c = input_stack::peek_char();
908 0 : if (c == 'o') {
909 0 : input_stack::get_char();
910 0 : c = input_stack::peek_char();
911 0 : if (c == 'i') {
912 0 : input_stack::get_char();
913 0 : c = input_stack::peek_char();
914 0 : if (c == 'n') {
915 0 : input_stack::get_char();
916 0 : c = input_stack::peek_char();
917 0 : if (c == 't') {
918 0 : input_stack::get_char();
919 0 : context_buffer = ".midpoint";
920 0 : return DOT_MID;
921 : }
922 : }
923 : }
924 : }
925 : }
926 18 : context_buffer = ".mid";
927 18 : return DOT_MID;
928 : }
929 : }
930 : // fall through
931 : default:
932 22 : context_buffer = '.';
933 22 : return '.';
934 : }
935 : }
936 :
937 18490 : int get_token(int lookup_flag)
938 : {
939 18490 : context_buffer.clear();
940 : for (;;) {
941 31220 : int n = 0;
942 31220 : int bol = input_stack::bol();
943 31220 : int c = input_stack::get_char();
944 31220 : if (bol && c == command_char) {
945 24 : token_buffer.clear();
946 24 : token_buffer += c;
947 : // the newline is not part of the token
948 : for (;;) {
949 108 : c = input_stack::peek_char();
950 108 : if (c == EOF || c == '\n')
951 : break;
952 84 : input_stack::get_char();
953 84 : token_buffer += char(c);
954 : }
955 24 : context_buffer = token_buffer;
956 24 : return COMMAND_LINE;
957 : }
958 31196 : switch (c) {
959 114 : case EOF:
960 114 : return EOF;
961 12446 : case ' ':
962 : case '\t':
963 12446 : break;
964 50 : case '\\':
965 : {
966 50 : int d = input_stack::peek_char();
967 50 : if (d != '\n') {
968 0 : context_buffer = '\\';
969 0 : return '\\';
970 : }
971 50 : input_stack::get_char();
972 50 : break;
973 : }
974 3253 : case '#':
975 3168 : do {
976 3253 : c = input_stack::get_char();
977 3253 : } while (c != '\n' && c != EOF);
978 85 : if (c == '\n')
979 85 : context_buffer = '\n';
980 85 : return c;
981 911 : case '"':
982 911 : context_buffer = '"';
983 911 : token_buffer.clear();
984 : for (;;) {
985 6680 : c = input_stack::get_char();
986 6680 : if (c == '\\') {
987 408 : context_buffer += '\\';
988 408 : c = input_stack::peek_char();
989 408 : if (c == '"') {
990 0 : input_stack::get_char();
991 0 : token_buffer += '"';
992 0 : context_buffer += '"';
993 : }
994 : else
995 408 : token_buffer += '\\';
996 : }
997 6272 : else if (c == '\n') {
998 0 : error("newline in string");
999 0 : break;
1000 : }
1001 6272 : else if (c == EOF) {
1002 0 : error("missing '\"'");
1003 0 : break;
1004 : }
1005 6272 : else if (c == '"') {
1006 911 : context_buffer += '"';
1007 911 : break;
1008 : }
1009 : else {
1010 5361 : context_buffer += char(c);
1011 5361 : token_buffer += char(c);
1012 : }
1013 : }
1014 911 : return TEXT;
1015 2126 : case '0':
1016 : case '1':
1017 : case '2':
1018 : case '3':
1019 : case '4':
1020 : case '5':
1021 : case '6':
1022 : case '7':
1023 : case '8':
1024 : case '9':
1025 : {
1026 2126 : int overflow = 0;
1027 2126 : n = 0;
1028 : for (;;) {
1029 2126 : if (n > (INT_MAX - 9)/10) {
1030 0 : overflow = 1;
1031 0 : break;
1032 : }
1033 2126 : n *= 10;
1034 2126 : n += c - '0';
1035 2126 : context_buffer += char(c);
1036 2126 : c = input_stack::peek_char();
1037 2126 : if (c == EOF || !csdigit(c))
1038 2126 : break;
1039 0 : c = input_stack::get_char();
1040 : }
1041 2126 : token_double = n;
1042 2126 : if (overflow) {
1043 : for (;;) {
1044 0 : token_double *= 10.0;
1045 0 : token_double += c - '0';
1046 0 : context_buffer += char(c);
1047 0 : c = input_stack::peek_char();
1048 0 : if (c == EOF || !csdigit(c))
1049 0 : break;
1050 0 : c = input_stack::get_char();
1051 : }
1052 : // if somebody asks for 1000000000000th, we will silently
1053 : // give them INT_MAXth
1054 0 : double temp = token_double; // work around gas 1.34/sparc bug
1055 0 : if (token_double > INT_MAX)
1056 0 : n = INT_MAX;
1057 : else
1058 0 : n = int(temp);
1059 : }
1060 : }
1061 2126 : switch (c) {
1062 0 : case 'i':
1063 : case 'I':
1064 0 : context_buffer += char(c);
1065 0 : input_stack::get_char();
1066 0 : return NUMBER;
1067 888 : case '.':
1068 : {
1069 888 : context_buffer += '.';
1070 888 : input_stack::get_char();
1071 925 : got_dot:
1072 925 : double factor = 1.0;
1073 : for (;;) {
1074 2126 : c = input_stack::peek_char();
1075 2126 : if (c == EOF || !csdigit(c))
1076 925 : break;
1077 1201 : input_stack::get_char();
1078 1201 : context_buffer += char(c);
1079 1201 : factor /= 10.0;
1080 1201 : if (c != '0')
1081 1067 : token_double += factor*(c - '0');
1082 : }
1083 925 : if (c != 'e' && c != 'E') {
1084 925 : if (c == 'i' || c == 'I') {
1085 1 : context_buffer += char(c);
1086 1 : input_stack::get_char();
1087 : }
1088 925 : return NUMBER;
1089 : }
1090 : }
1091 : // fall through
1092 : case 'e':
1093 : case 'E':
1094 : {
1095 0 : int echar = c;
1096 0 : input_stack::get_char();
1097 0 : c = input_stack::peek_char();
1098 0 : int sign = '+';
1099 0 : if (c == '+' || c == '-') {
1100 0 : sign = c;
1101 0 : input_stack::get_char();
1102 0 : c = input_stack::peek_char();
1103 0 : if (c == EOF || !csdigit(c)) {
1104 0 : input_stack::push_back(sign);
1105 0 : input_stack::push_back(echar);
1106 0 : return NUMBER;
1107 : }
1108 0 : context_buffer += char(echar);
1109 0 : context_buffer += char(sign);
1110 : }
1111 : else {
1112 0 : if (c == EOF || !csdigit(c)) {
1113 0 : input_stack::push_back(echar);
1114 0 : return NUMBER;
1115 : }
1116 0 : context_buffer += char(echar);
1117 : }
1118 0 : input_stack::get_char();
1119 0 : context_buffer += char(c);
1120 0 : n = c - '0';
1121 : for (;;) {
1122 0 : c = input_stack::peek_char();
1123 0 : if (c == EOF || !csdigit(c))
1124 0 : break;
1125 0 : input_stack::get_char();
1126 0 : context_buffer += char(c);
1127 0 : n = n*10 + (c - '0');
1128 : }
1129 0 : if (sign == '-')
1130 0 : n = -n;
1131 0 : if (c == 'i' || c == 'I') {
1132 0 : context_buffer += char(c);
1133 0 : input_stack::get_char();
1134 : }
1135 0 : token_double *= pow(10.0, n);
1136 0 : return NUMBER;
1137 : }
1138 12 : case 'n':
1139 12 : input_stack::get_char();
1140 12 : c = input_stack::peek_char();
1141 12 : if (c == 'd') {
1142 12 : input_stack::get_char();
1143 12 : token_int = n;
1144 12 : context_buffer += "nd";
1145 12 : return ORDINAL;
1146 : }
1147 0 : input_stack::push_back('n');
1148 0 : return NUMBER;
1149 6 : case 'r':
1150 6 : input_stack::get_char();
1151 6 : c = input_stack::peek_char();
1152 6 : if (c == 'd') {
1153 6 : input_stack::get_char();
1154 6 : token_int = n;
1155 6 : context_buffer += "rd";
1156 6 : return ORDINAL;
1157 : }
1158 0 : input_stack::push_back('r');
1159 0 : return NUMBER;
1160 8 : case 't':
1161 8 : input_stack::get_char();
1162 8 : c = input_stack::peek_char();
1163 8 : if (c == 'h') {
1164 8 : input_stack::get_char();
1165 8 : token_int = n;
1166 8 : context_buffer += "th";
1167 8 : return ORDINAL;
1168 : }
1169 0 : input_stack::push_back('t');
1170 0 : return NUMBER;
1171 10 : case 's':
1172 10 : input_stack::get_char();
1173 10 : c = input_stack::peek_char();
1174 10 : if (c == 't') {
1175 10 : input_stack::get_char();
1176 10 : token_int = n;
1177 10 : context_buffer += "st";
1178 10 : return ORDINAL;
1179 : }
1180 0 : input_stack::push_back('s');
1181 0 : return NUMBER;
1182 1202 : default:
1183 1202 : return NUMBER;
1184 : }
1185 : break;
1186 18 : case '\'':
1187 : {
1188 18 : c = input_stack::peek_char();
1189 18 : if (c == 't') {
1190 0 : input_stack::get_char();
1191 0 : c = input_stack::peek_char();
1192 0 : if (c == 'h') {
1193 0 : input_stack::get_char();
1194 0 : context_buffer = "'th";
1195 0 : return TH;
1196 : }
1197 : else
1198 0 : input_stack::push_back('t');
1199 : }
1200 18 : context_buffer = "'";
1201 18 : return '\'';
1202 : }
1203 820 : case '.':
1204 : {
1205 820 : c = input_stack::peek_char();
1206 820 : if (c != EOF && csdigit(c)) {
1207 37 : n = 0;
1208 37 : token_double = 0.0;
1209 37 : context_buffer = '.';
1210 37 : goto got_dot;
1211 : }
1212 783 : return get_token_after_dot(c);
1213 : }
1214 12 : case '<':
1215 12 : c = input_stack::peek_char();
1216 12 : if (c == '-') {
1217 8 : input_stack::get_char();
1218 8 : c = input_stack::peek_char();
1219 8 : if (c == '>') {
1220 0 : input_stack::get_char();
1221 0 : context_buffer = "<->";
1222 0 : return DOUBLE_ARROW_HEAD;
1223 : }
1224 8 : context_buffer = "<-";
1225 8 : return LEFT_ARROW_HEAD;
1226 : }
1227 4 : else if (c == '=') {
1228 0 : input_stack::get_char();
1229 0 : context_buffer = "<=";
1230 0 : return LESSEQUAL;
1231 : }
1232 4 : context_buffer = "<";
1233 4 : return '<';
1234 96 : case '-':
1235 96 : c = input_stack::peek_char();
1236 96 : if (c == '>') {
1237 16 : input_stack::get_char();
1238 16 : context_buffer = "->";
1239 16 : return RIGHT_ARROW_HEAD;
1240 : }
1241 80 : context_buffer = "-";
1242 80 : return '-';
1243 36 : case '!':
1244 36 : c = input_stack::peek_char();
1245 36 : if (c == '=') {
1246 0 : input_stack::get_char();
1247 0 : context_buffer = "!=";
1248 0 : return NOTEQUAL;
1249 : }
1250 36 : context_buffer = "!";
1251 36 : return '!';
1252 4 : case '>':
1253 4 : c = input_stack::peek_char();
1254 4 : if (c == '=') {
1255 0 : input_stack::get_char();
1256 0 : context_buffer = ">=";
1257 0 : return GREATEREQUAL;
1258 : }
1259 4 : context_buffer = ">";
1260 4 : return '>';
1261 163 : case '=':
1262 163 : c = input_stack::peek_char();
1263 163 : if (c == '=') {
1264 0 : input_stack::get_char();
1265 0 : context_buffer = "==";
1266 0 : return EQUALEQUAL;
1267 : }
1268 163 : context_buffer = "=";
1269 163 : return '=';
1270 0 : case '&':
1271 0 : c = input_stack::peek_char();
1272 0 : if (c == '&') {
1273 0 : input_stack::get_char();
1274 0 : context_buffer = "&&";
1275 0 : return ANDAND;
1276 : }
1277 0 : context_buffer = "&";
1278 0 : return '&';
1279 0 : case '|':
1280 0 : c = input_stack::peek_char();
1281 0 : if (c == '|') {
1282 0 : input_stack::get_char();
1283 0 : context_buffer = "||";
1284 0 : return OROR;
1285 : }
1286 0 : context_buffer = "|";
1287 0 : return '|';
1288 14315 : default:
1289 14315 : if (c != EOF && csalpha(c)) {
1290 6618 : token_buffer.clear();
1291 6618 : token_buffer = c;
1292 : for (;;) {
1293 23907 : c = input_stack::peek_char();
1294 23907 : if (c == EOF || (!csalnum(c) && c != '_'))
1295 6618 : break;
1296 17289 : input_stack::get_char();
1297 17289 : token_buffer += char(c);
1298 : }
1299 6618 : int tok = lookup_keyword(token_buffer.contents(),
1300 : token_buffer.length());
1301 6618 : if (tok != 0) {
1302 4860 : context_buffer = token_buffer;
1303 4860 : return tok;
1304 : }
1305 1758 : char *def = 0;
1306 1758 : if (lookup_flag) {
1307 1726 : token_buffer += '\0';
1308 1726 : def = macro_table.lookup(token_buffer.contents());
1309 1726 : token_buffer.set_length(token_buffer.length() - 1);
1310 1726 : if (def) {
1311 234 : if (c == '(') {
1312 212 : input_stack::get_char();
1313 212 : interpolate_macro_with_args(def);
1314 : }
1315 : else
1316 22 : input_stack::push(new macro_input(def));
1317 : }
1318 : }
1319 1758 : if (!def) {
1320 1524 : context_buffer = token_buffer;
1321 1524 : if (csupper(token_buffer[0]))
1322 573 : return LABEL;
1323 : else
1324 951 : return VARIABLE;
1325 : }
1326 : }
1327 : else {
1328 7697 : context_buffer = char(c);
1329 7697 : return (unsigned char)c;
1330 : }
1331 234 : break;
1332 : }
1333 12730 : }
1334 : }
1335 :
1336 46 : int get_delimited()
1337 : {
1338 46 : token_buffer.clear();
1339 46 : int c = input_stack::get_char();
1340 92 : while (c == ' ' || c == '\t' || c == '\n')
1341 46 : c = input_stack::get_char();
1342 46 : if (c == EOF) {
1343 0 : lex_error("missing delimiter");
1344 0 : return 0;
1345 : }
1346 46 : context_buffer = char(c);
1347 46 : int had_newline = 0;
1348 46 : int start = c;
1349 46 : int level = 0;
1350 46 : enum { NORMAL, IN_STRING, IN_STRING_QUOTED, DELIM_END } state = NORMAL;
1351 : for (;;) {
1352 7880 : c = input_stack::get_char();
1353 7880 : if (c == EOF) {
1354 0 : lex_error("missing closing delimiter");
1355 0 : return 0;
1356 : }
1357 7880 : if (c == '\n')
1358 284 : had_newline = 1;
1359 7596 : else if (!had_newline)
1360 204 : context_buffer += char(c);
1361 7880 : switch (state) {
1362 7184 : case NORMAL:
1363 7184 : if (start == '{') {
1364 7184 : if (c == '{') {
1365 16 : level++;
1366 16 : break;
1367 : }
1368 7168 : if (c == '}') {
1369 62 : if (--level < 0)
1370 46 : state = DELIM_END;
1371 62 : break;
1372 : }
1373 : }
1374 : else {
1375 0 : if (c == start) {
1376 0 : state = DELIM_END;
1377 0 : break;
1378 : }
1379 : }
1380 7106 : if (c == '"')
1381 63 : state = IN_STRING;
1382 7106 : break;
1383 84 : case IN_STRING_QUOTED:
1384 84 : if (c == '\n')
1385 0 : state = NORMAL;
1386 : else
1387 84 : state = IN_STRING;
1388 84 : break;
1389 612 : case IN_STRING:
1390 612 : if (c == '"' || c == '\n')
1391 63 : state = NORMAL;
1392 549 : else if (c == '\\')
1393 84 : state = IN_STRING_QUOTED;
1394 612 : break;
1395 0 : default:
1396 0 : assert(0 == "unhandled case of lexical analyzer state");
1397 : }
1398 7880 : if (state == DELIM_END)
1399 46 : break;
1400 7834 : token_buffer += c;
1401 : }
1402 46 : return 1;
1403 : }
1404 :
1405 26 : void do_define()
1406 : {
1407 26 : int t = get_token(0); // do not expand what we are defining
1408 26 : if (t != VARIABLE && t != LABEL) {
1409 0 : lex_error("can only define variable or placename");
1410 0 : return;
1411 : }
1412 26 : token_buffer += '\0';
1413 26 : string nm = token_buffer;
1414 26 : const char *name = nm.contents();
1415 26 : if (!get_delimited())
1416 0 : return;
1417 26 : token_buffer += '\0';
1418 26 : macro_table.define(name, strsave(token_buffer.contents()));
1419 : }
1420 :
1421 6 : void do_undef()
1422 : {
1423 6 : int t = get_token(0); // do not expand what we are undefining
1424 6 : if (t != VARIABLE && t != LABEL) {
1425 0 : lex_error("can only define variable or placename");
1426 0 : return;
1427 : }
1428 6 : token_buffer += '\0';
1429 6 : macro_table.define(token_buffer.contents(), 0);
1430 : }
1431 :
1432 :
1433 : class for_input : public input {
1434 : char *var;
1435 : char *body;
1436 : double from;
1437 : double to;
1438 : int by_is_multiplicative;
1439 : double by;
1440 : const char *p;
1441 : int done_newline;
1442 : public:
1443 : for_input(char *, double, double, int, double, char *);
1444 : ~for_input();
1445 : int get();
1446 : int peek();
1447 : };
1448 :
1449 16 : for_input::for_input(char *vr, double f, double t,
1450 16 : int bim, double b, char *bd)
1451 : : var(vr), body(bd), from(f), to(t), by_is_multiplicative(bim), by(b),
1452 16 : p(body), done_newline(0)
1453 : {
1454 16 : }
1455 :
1456 32 : for_input::~for_input()
1457 : {
1458 16 : free(var);
1459 16 : free(body);
1460 32 : }
1461 :
1462 11614 : int for_input::get()
1463 : {
1464 11614 : if (p == 0)
1465 0 : return EOF;
1466 : for (;;) {
1467 11754 : if (*p != '\0')
1468 11614 : return (unsigned char)*p++;
1469 312 : if (!done_newline) {
1470 156 : done_newline = 1;
1471 156 : return '\n';
1472 : }
1473 : double val;
1474 156 : if (!lookup_variable(var, &val)) {
1475 0 : lex_error("body of 'for' terminated enclosing block");
1476 0 : return EOF;
1477 : }
1478 156 : if (by_is_multiplicative)
1479 0 : val *= by;
1480 : else
1481 156 : val += by;
1482 156 : define_variable(var, val);
1483 156 : if ((from <= to && val > to)
1484 140 : || (from >= to && val < to)) {
1485 16 : p = 0;
1486 16 : return EOF;
1487 : }
1488 140 : p = body;
1489 140 : done_newline = 0;
1490 140 : }
1491 : }
1492 :
1493 3834 : int for_input::peek()
1494 : {
1495 3834 : if (p == 0)
1496 0 : return EOF;
1497 3834 : if (*p != '\0')
1498 3834 : return (unsigned char)*p;
1499 0 : if (!done_newline)
1500 0 : return '\n';
1501 : double val;
1502 0 : if (!lookup_variable(var, &val))
1503 0 : return EOF;
1504 0 : if (by_is_multiplicative) {
1505 0 : if (val * by > to)
1506 0 : return EOF;
1507 : }
1508 : else {
1509 0 : if ((from <= to && val + by > to)
1510 0 : || (from >= to && val + by < to))
1511 0 : return EOF;
1512 : }
1513 0 : if (*body == '\0')
1514 0 : return EOF;
1515 0 : return (unsigned char)*body;
1516 : }
1517 :
1518 20 : void do_for(char *var, double from, double to, int by_is_multiplicative,
1519 : double by, char *body)
1520 : {
1521 20 : define_variable(var, from);
1522 20 : if ((by_is_multiplicative && by <= 0)
1523 20 : || (by > 0 && from > to)
1524 16 : || (by < 0 && from < to))
1525 4 : return;
1526 16 : input_stack::push(new for_input(var, from, to,
1527 16 : by_is_multiplicative, by, body));
1528 : }
1529 :
1530 :
1531 1 : void do_copy(const char *filename)
1532 : {
1533 1 : errno = 0;
1534 1 : FILE *fp = fopen(filename, "r");
1535 1 : if (fp == 0) {
1536 1 : lex_error("can't open '%1': %2", filename, strerror(errno));
1537 1 : return;
1538 : }
1539 0 : input_stack::push(new file_input(fp, filename));
1540 : }
1541 :
1542 : class copy_thru_input : public input {
1543 : int done;
1544 : char *body;
1545 : char *until;
1546 : const char *p;
1547 : const char *ap;
1548 : int argv[MAX_ARG];
1549 : int argc;
1550 : string line;
1551 : int get_line();
1552 : virtual int inget() = 0;
1553 : public:
1554 : copy_thru_input(const char *b, const char *u);
1555 : ~copy_thru_input();
1556 : int get();
1557 : int peek();
1558 : };
1559 :
1560 : class copy_file_thru_input : public copy_thru_input {
1561 : input *in;
1562 : public:
1563 : copy_file_thru_input(input *, const char *b, const char *u);
1564 : ~copy_file_thru_input();
1565 : int inget();
1566 : };
1567 :
1568 0 : copy_file_thru_input::copy_file_thru_input(input *i, const char *b,
1569 0 : const char *u)
1570 0 : : copy_thru_input(b, u), in(i)
1571 : {
1572 0 : }
1573 :
1574 0 : copy_file_thru_input::~copy_file_thru_input()
1575 : {
1576 0 : delete in;
1577 0 : }
1578 :
1579 0 : int copy_file_thru_input::inget()
1580 : {
1581 0 : if (!in)
1582 0 : return EOF;
1583 : else
1584 0 : return in->get();
1585 : }
1586 :
1587 : class copy_rest_thru_input : public copy_thru_input {
1588 : public:
1589 : copy_rest_thru_input(const char *, const char *u);
1590 : int inget();
1591 : };
1592 :
1593 0 : copy_rest_thru_input::copy_rest_thru_input(const char *b, const char *u)
1594 0 : : copy_thru_input(b, u)
1595 : {
1596 0 : }
1597 :
1598 0 : int copy_rest_thru_input::inget()
1599 : {
1600 0 : while (next != 0) {
1601 0 : int c = next->get();
1602 0 : if (c != EOF)
1603 0 : return c;
1604 0 : if (next->next == 0)
1605 0 : return EOF;
1606 0 : input *tem = next;
1607 0 : next = next->next;
1608 0 : delete tem;
1609 : }
1610 0 : return EOF;
1611 :
1612 : }
1613 :
1614 0 : copy_thru_input::copy_thru_input(const char *b, const char *u)
1615 0 : : done(0)
1616 : {
1617 0 : ap = 0;
1618 0 : body = process_body(b);
1619 0 : p = 0;
1620 0 : until = strsave(u);
1621 0 : }
1622 :
1623 :
1624 0 : copy_thru_input::~copy_thru_input()
1625 : {
1626 0 : delete[] body;
1627 0 : delete[] until;
1628 0 : }
1629 :
1630 0 : int copy_thru_input::get()
1631 : {
1632 0 : if (ap) {
1633 0 : if (*ap != '\0')
1634 0 : return (unsigned char)*ap++;
1635 0 : ap = 0;
1636 : }
1637 : for (;;) {
1638 0 : if (p == 0) {
1639 0 : if (!get_line())
1640 0 : break;
1641 0 : p = body;
1642 : }
1643 0 : if (*p == '\0') {
1644 0 : p = 0;
1645 0 : return '\n';
1646 : }
1647 0 : while ((unsigned char)*p >= ARG1
1648 0 : && (unsigned char)*p <= ARG1 + MAX_ARG - 1) {
1649 0 : int i = (unsigned char)*p++ - ARG1;
1650 0 : if (i < argc && line[argv[i]] != '\0') {
1651 0 : ap = line.contents() + argv[i];
1652 0 : return (unsigned char)*ap++;
1653 : }
1654 : }
1655 0 : if (*p != '\0')
1656 0 : return (unsigned char)*p++;
1657 0 : }
1658 0 : return EOF;
1659 : }
1660 :
1661 0 : int copy_thru_input::peek()
1662 : {
1663 0 : if (ap) {
1664 0 : if (*ap != '\0')
1665 0 : return (unsigned char)*ap;
1666 0 : ap = 0;
1667 : }
1668 : for (;;) {
1669 0 : if (p == 0) {
1670 0 : if (!get_line())
1671 0 : break;
1672 0 : p = body;
1673 : }
1674 0 : if (*p == '\0')
1675 0 : return '\n';
1676 0 : while ((unsigned char)*p >= ARG1
1677 0 : && (unsigned char)*p <= ARG1 + MAX_ARG - 1) {
1678 0 : int i = (unsigned char)*p++ - ARG1;
1679 0 : if (i < argc && line[argv[i]] != '\0') {
1680 0 : ap = line.contents() + argv[i];
1681 0 : return (unsigned char)*ap;
1682 : }
1683 : }
1684 0 : if (*p != '\0')
1685 0 : return (unsigned char)*p;
1686 0 : }
1687 0 : return EOF;
1688 : }
1689 :
1690 0 : int copy_thru_input::get_line()
1691 : {
1692 0 : if (done)
1693 0 : return 0;
1694 0 : line.clear();
1695 0 : argc = 0;
1696 0 : int c = inget();
1697 : for (;;) {
1698 0 : while (c == ' ')
1699 0 : c = inget();
1700 0 : if (c == EOF || c == '\n')
1701 : break;
1702 0 : if (argc == MAX_ARG) {
1703 0 : do {
1704 0 : c = inget();
1705 0 : } while (c != '\n' && c != EOF);
1706 0 : break;
1707 : }
1708 0 : argv[argc++] = line.length();
1709 0 : do {
1710 0 : line += char(c);
1711 0 : c = inget();
1712 0 : } while (c != ' ' && c != '\n');
1713 0 : line += '\0';
1714 : }
1715 0 : if (until != 0 && argc > 0 && strcmp(&line[argv[0]], until) == 0) {
1716 0 : done = 1;
1717 0 : return 0;
1718 : }
1719 0 : return argc > 0 || c == '\n';
1720 : }
1721 :
1722 : class simple_file_input : public input {
1723 : const char *filename;
1724 : int lineno;
1725 : FILE *fp;
1726 : public:
1727 : simple_file_input(FILE *, const char *);
1728 : ~simple_file_input();
1729 : int get();
1730 : int peek();
1731 : int get_location(const char **, int *);
1732 : };
1733 :
1734 0 : simple_file_input::simple_file_input(FILE *p, const char *s)
1735 0 : : filename(s), lineno(1), fp(p)
1736 : {
1737 0 : }
1738 :
1739 0 : simple_file_input::~simple_file_input()
1740 : {
1741 : // don't delete the filename
1742 0 : fclose(fp);
1743 0 : }
1744 :
1745 0 : int simple_file_input::get()
1746 : {
1747 0 : int c = getc(fp);
1748 0 : while (is_invalid_input_char(c)) {
1749 0 : error("invalid input character code %1", c);
1750 0 : c = getc(fp);
1751 : }
1752 0 : if (c == '\n')
1753 0 : lineno++;
1754 0 : return c;
1755 : }
1756 :
1757 0 : int simple_file_input::peek()
1758 : {
1759 0 : int c = getc(fp);
1760 0 : while (is_invalid_input_char(c)) {
1761 0 : error("invalid input character code %1", c);
1762 0 : c = getc(fp);
1763 : }
1764 0 : if (c != EOF)
1765 0 : ungetc(c, fp);
1766 0 : return c;
1767 : }
1768 :
1769 0 : int simple_file_input::get_location(const char **fnp, int *lnp)
1770 : {
1771 0 : *fnp = filename;
1772 0 : *lnp = lineno;
1773 0 : return 1;
1774 : }
1775 :
1776 :
1777 0 : void copy_file_thru(const char *filename, const char *body, const char *until)
1778 : {
1779 0 : errno = 0;
1780 0 : FILE *fp = fopen(filename, "r");
1781 0 : if (fp == 0) {
1782 0 : lex_error("can't open '%1': %2", filename, strerror(errno));
1783 0 : return;
1784 : }
1785 0 : input *in = new copy_file_thru_input(new simple_file_input(fp, filename),
1786 0 : body, until);
1787 0 : input_stack::push(in);
1788 : }
1789 :
1790 0 : void copy_rest_thru(const char *body, const char *until)
1791 : {
1792 0 : input_stack::push(new copy_rest_thru_input(body, until));
1793 0 : }
1794 :
1795 0 : void push_body(const char *s)
1796 : {
1797 0 : input_stack::push(new char_input('\n'));
1798 0 : input_stack::push(new macro_input(s));
1799 0 : }
1800 :
1801 : int delim_flag = 0;
1802 :
1803 0 : char *get_thru_arg()
1804 : {
1805 0 : int c = input_stack::peek_char();
1806 0 : while (c == ' ') {
1807 0 : input_stack::get_char();
1808 0 : c = input_stack::peek_char();
1809 : }
1810 0 : if (c != EOF && csalpha(c)) {
1811 : // looks like a macro
1812 0 : input_stack::get_char();
1813 0 : token_buffer = c;
1814 : for (;;) {
1815 0 : c = input_stack::peek_char();
1816 0 : if (c == EOF || (!csalnum(c) && c != '_'))
1817 0 : break;
1818 0 : input_stack::get_char();
1819 0 : token_buffer += char(c);
1820 : }
1821 0 : context_buffer = token_buffer;
1822 0 : token_buffer += '\0';
1823 0 : char *def = macro_table.lookup(token_buffer.contents());
1824 0 : if (def)
1825 0 : return strsave(def);
1826 : // I guess it wasn't a macro after all; so push the macro name back.
1827 : // -2 because we added a '\0'
1828 0 : for (int i = token_buffer.length() - 2; i >= 0; i--)
1829 0 : input_stack::push_back(token_buffer[i]);
1830 : }
1831 0 : if (get_delimited()) {
1832 0 : token_buffer += '\0';
1833 0 : return strsave(token_buffer.contents());
1834 : }
1835 : else
1836 0 : return 0;
1837 : }
1838 :
1839 : int lookahead_token = -1;
1840 : string old_context_buffer;
1841 :
1842 20 : void do_lookahead()
1843 : {
1844 20 : if (lookahead_token == -1) {
1845 20 : old_context_buffer = context_buffer;
1846 20 : lookahead_token = get_token(1);
1847 : }
1848 20 : }
1849 :
1850 18446 : int yylex()
1851 : {
1852 18446 : if (delim_flag) {
1853 20 : assert(lookahead_token == -1);
1854 20 : if (delim_flag == 2) {
1855 0 : if ((yylval.str = get_thru_arg()) != 0)
1856 0 : return DELIMITED;
1857 : else
1858 0 : return 0;
1859 : }
1860 : else {
1861 20 : if (get_delimited()) {
1862 20 : token_buffer += '\0';
1863 20 : yylval.str = strsave(token_buffer.contents());
1864 20 : return DELIMITED;
1865 : }
1866 : else
1867 0 : return 0;
1868 : }
1869 : }
1870 : for (;;) {
1871 : int t;
1872 18458 : if (lookahead_token >= 0) {
1873 286 : t = lookahead_token;
1874 286 : lookahead_token = -1;
1875 : }
1876 : else
1877 18172 : t = get_token(1);
1878 18458 : switch (t) {
1879 2480 : case '\n':
1880 2480 : return ';';
1881 114 : case EOF:
1882 114 : return 0;
1883 26 : case DEFINE:
1884 26 : do_define();
1885 26 : break;
1886 6 : case UNDEF:
1887 6 : do_undef();
1888 6 : break;
1889 36 : case ORDINAL:
1890 36 : yylval.n = token_int;
1891 36 : return t;
1892 2127 : case NUMBER:
1893 2127 : yylval.x = token_double;
1894 2127 : return t;
1895 935 : case COMMAND_LINE:
1896 : case TEXT:
1897 935 : token_buffer += '\0';
1898 935 : if (!input_stack::get_location(&yylval.lstr.filename,
1899 : &yylval.lstr.lineno)) {
1900 0 : yylval.lstr.filename = 0;
1901 0 : yylval.lstr.lineno = -1;
1902 : }
1903 935 : yylval.lstr.str = strsave(token_buffer.contents());
1904 935 : return t;
1905 1492 : case LABEL:
1906 : case VARIABLE:
1907 1492 : token_buffer += '\0';
1908 1492 : yylval.str = strsave(token_buffer.contents());
1909 1492 : return t;
1910 61 : case LEFT:
1911 : // change LEFT to LEFT_CORNER when followed by OF
1912 61 : old_context_buffer = context_buffer;
1913 61 : lookahead_token = get_token(1);
1914 61 : if (lookahead_token == OF)
1915 0 : return LEFT_CORNER;
1916 : else
1917 61 : return t;
1918 125 : case RIGHT:
1919 : // change RIGHT to RIGHT_CORNER when followed by OF
1920 125 : old_context_buffer = context_buffer;
1921 125 : lookahead_token = get_token(1);
1922 125 : if (lookahead_token == OF)
1923 0 : return RIGHT_CORNER;
1924 : else
1925 125 : return t;
1926 0 : case UPPER:
1927 : // recognise UPPER only before LEFT or RIGHT
1928 0 : old_context_buffer = context_buffer;
1929 0 : lookahead_token = get_token(1);
1930 0 : if (lookahead_token != LEFT && lookahead_token != RIGHT) {
1931 0 : yylval.str = strsave("upper");
1932 0 : return VARIABLE;
1933 : }
1934 : else
1935 0 : return t;
1936 0 : case LOWER:
1937 : // recognise LOWER only before LEFT or RIGHT
1938 0 : old_context_buffer = context_buffer;
1939 0 : lookahead_token = get_token(1);
1940 0 : if (lookahead_token != LEFT && lookahead_token != RIGHT) {
1941 0 : yylval.str = strsave("lower");
1942 0 : return VARIABLE;
1943 : }
1944 : else
1945 0 : return t;
1946 0 : case NORTH:
1947 : // recognise NORTH only before OF
1948 0 : old_context_buffer = context_buffer;
1949 0 : lookahead_token = get_token(1);
1950 0 : if (lookahead_token != OF) {
1951 0 : yylval.str = strsave("north");
1952 0 : return VARIABLE;
1953 : }
1954 : else
1955 0 : return t;
1956 0 : case SOUTH:
1957 : // recognise SOUTH only before OF
1958 0 : old_context_buffer = context_buffer;
1959 0 : lookahead_token = get_token(1);
1960 0 : if (lookahead_token != OF) {
1961 0 : yylval.str = strsave("south");
1962 0 : return VARIABLE;
1963 : }
1964 : else
1965 0 : return t;
1966 0 : case EAST:
1967 : // recognise EAST only before OF
1968 0 : old_context_buffer = context_buffer;
1969 0 : lookahead_token = get_token(1);
1970 0 : if (lookahead_token != OF) {
1971 0 : yylval.str = strsave("east");
1972 0 : return VARIABLE;
1973 : }
1974 : else
1975 0 : return t;
1976 0 : case WEST:
1977 : // recognise WEST only before OF
1978 0 : old_context_buffer = context_buffer;
1979 0 : lookahead_token = get_token(1);
1980 0 : if (lookahead_token != OF) {
1981 0 : yylval.str = strsave("west");
1982 0 : return VARIABLE;
1983 : }
1984 : else
1985 0 : return t;
1986 0 : case TOP:
1987 : // recognise TOP only before OF
1988 0 : old_context_buffer = context_buffer;
1989 0 : lookahead_token = get_token(1);
1990 0 : if (lookahead_token != OF) {
1991 0 : yylval.str = strsave("top");
1992 0 : return VARIABLE;
1993 : }
1994 : else
1995 0 : return t;
1996 0 : case BOTTOM:
1997 : // recognise BOTTOM only before OF
1998 0 : old_context_buffer = context_buffer;
1999 0 : lookahead_token = get_token(1);
2000 0 : if (lookahead_token != OF) {
2001 0 : yylval.str = strsave("bottom");
2002 0 : return VARIABLE;
2003 : }
2004 : else
2005 0 : return t;
2006 78 : case CENTER:
2007 : // recognise CENTER only before OF
2008 78 : old_context_buffer = context_buffer;
2009 78 : lookahead_token = get_token(1);
2010 78 : if (lookahead_token != OF) {
2011 0 : yylval.str = strsave("center");
2012 0 : return VARIABLE;
2013 : }
2014 : else
2015 78 : return t;
2016 2 : case START:
2017 : // recognise START only before OF
2018 2 : old_context_buffer = context_buffer;
2019 2 : lookahead_token = get_token(1);
2020 2 : if (lookahead_token != OF) {
2021 0 : yylval.str = strsave("start");
2022 0 : return VARIABLE;
2023 : }
2024 : else
2025 2 : return t;
2026 0 : case END:
2027 : // recognise END only before OF
2028 0 : old_context_buffer = context_buffer;
2029 0 : lookahead_token = get_token(1);
2030 0 : if (lookahead_token != OF) {
2031 0 : yylval.str = strsave("end");
2032 0 : return VARIABLE;
2033 : }
2034 : else
2035 0 : return t;
2036 10976 : default:
2037 10976 : return t;
2038 : }
2039 32 : }
2040 : }
2041 :
2042 1 : void lex_error(const char *message,
2043 : const errarg &arg1,
2044 : const errarg &arg2,
2045 : const errarg &arg3)
2046 : {
2047 : const char *filename;
2048 : int lineno;
2049 1 : if (!input_stack::get_location(&filename, &lineno))
2050 0 : error(message, arg1, arg2, arg3);
2051 : else
2052 1 : error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
2053 1 : }
2054 :
2055 1 : void lex_warning(const char *message,
2056 : const errarg &arg1,
2057 : const errarg &arg2,
2058 : const errarg &arg3)
2059 : {
2060 : const char *filename;
2061 : int lineno;
2062 1 : if (!input_stack::get_location(&filename, &lineno))
2063 0 : warning(message, arg1, arg2, arg3);
2064 : else
2065 1 : warning_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
2066 1 : }
2067 :
2068 0 : void yyerror(const char *s)
2069 : {
2070 : const char *filename;
2071 : int lineno;
2072 0 : const char *context = 0;
2073 0 : if (lookahead_token == -1) {
2074 0 : if (context_buffer.length() > 0) {
2075 0 : context_buffer += '\0';
2076 0 : context = context_buffer.contents();
2077 : }
2078 : }
2079 : else {
2080 0 : if (old_context_buffer.length() > 0) {
2081 0 : old_context_buffer += '\0';
2082 0 : context = old_context_buffer.contents();
2083 : }
2084 : }
2085 0 : if (!input_stack::get_location(&filename, &lineno)) {
2086 0 : if (context) {
2087 0 : if (context[0] == '\n' && context[1] == '\0')
2088 0 : error("%1 before newline", s);
2089 : else
2090 0 : error("%1 before '%2'", s, context);
2091 : }
2092 : else
2093 0 : error("%1 at end of picture", s);
2094 : }
2095 : else {
2096 0 : if (context) {
2097 0 : if (context[0] == '\n' && context[1] == '\0')
2098 0 : error_with_file_and_line(filename, lineno, "%1 before newline", s);
2099 : else
2100 0 : error_with_file_and_line(filename, lineno, "%1 before '%2'",
2101 0 : s, context);
2102 : }
2103 : else
2104 0 : error_with_file_and_line(filename, lineno, "%1 at end of picture", s);
2105 : }
2106 0 : }
2107 :
2108 : // Local Variables:
2109 : // fill-column: 72
2110 : // mode: C++
2111 : // End:
2112 : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
|