1114402Sru// -*- C++ -*-
2151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2002, 2003, 2004
3114402Sru     Free Software Foundation, Inc.
4114402Sru     Written by James Clark (jjc@jclark.com)
5114402Sru
6114402SruThis file is part of groff.
7114402Sru
8114402Srugroff is free software; you can redistribute it and/or modify it under
9114402Sruthe terms of the GNU General Public License as published by the Free
10114402SruSoftware Foundation; either version 2, or (at your option) any later
11114402Sruversion.
12114402Sru
13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
15114402SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16114402Srufor more details.
17114402Sru
18114402SruYou should have received a copy of the GNU General Public License along
19114402Sruwith groff; see the file COPYING.  If not, write to the Free Software
20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21114402Sru
22114402Sru#include "pic.h"
23114402Sru#include "ptable.h"
24114402Sru#include "object.h"
25114402Sru#include "pic_tab.h"
26114402Sru
27114402Srudeclare_ptable(char)
28114402Sruimplement_ptable(char)
29114402Sru
30114402SruPTABLE(char) macro_table;
31114402Sru
32114402Sruclass macro_input : public input {
33114402Sru  char *s;
34114402Sru  char *p;
35114402Srupublic:
36114402Sru  macro_input(const char *);
37114402Sru  ~macro_input();
38114402Sru  int get();
39114402Sru  int peek();
40114402Sru};
41114402Sru
42114402Sruclass argument_macro_input : public input {
43114402Sru  char *s;
44114402Sru  char *p;
45114402Sru  char *ap;
46114402Sru  int argc;
47114402Sru  char *argv[9];
48114402Srupublic:
49114402Sru  argument_macro_input(const char *, int, char **);
50114402Sru  ~argument_macro_input();
51114402Sru  int get();
52114402Sru  int peek();
53114402Sru};
54114402Sru
55114402Sruinput::input() : next(0)
56114402Sru{
57114402Sru}
58114402Sru
59114402Sruinput::~input()
60114402Sru{
61114402Sru}
62114402Sru
63114402Sruint input::get_location(const char **, int *)
64114402Sru{
65114402Sru  return 0;
66114402Sru}
67114402Sru
68114402Srufile_input::file_input(FILE *f, const char *fn)
69114402Sru: fp(f), filename(fn), lineno(0), ptr("")
70114402Sru{
71114402Sru}
72114402Sru
73114402Srufile_input::~file_input()
74114402Sru{
75114402Sru  fclose(fp);
76114402Sru}
77114402Sru
78114402Sruint file_input::read_line()
79114402Sru{
80114402Sru  for (;;) {
81114402Sru    line.clear();
82114402Sru    lineno++;
83114402Sru    for (;;) {
84114402Sru      int c = getc(fp);
85114402Sru      if (c == EOF)
86114402Sru	break;
87114402Sru      else if (invalid_input_char(c))
88114402Sru	lex_error("invalid input character code %1", c);
89114402Sru      else {
90114402Sru	line += char(c);
91114402Sru	if (c == '\n')
92114402Sru	  break;
93114402Sru      }
94114402Sru    }
95114402Sru    if (line.length() == 0)
96114402Sru      return 0;
97114402Sru    if (!(line.length() >= 3 && line[0] == '.' && line[1] == 'P'
98114402Sru	  && (line[2] == 'S' || line[2] == 'E' || line[2] == 'F')
99114402Sru	  && (line.length() == 3 || line[3] == ' ' || line[3] == '\n'
100114402Sru	      || compatible_flag))) {
101114402Sru      line += '\0';
102114402Sru      ptr = line.contents();
103114402Sru      return 1;
104114402Sru    }
105114402Sru  }
106114402Sru}
107114402Sru
108114402Sruint file_input::get()
109114402Sru{
110114402Sru  if (*ptr != '\0' || read_line())
111114402Sru    return (unsigned char)*ptr++;
112114402Sru  else
113114402Sru    return EOF;
114114402Sru}
115114402Sru
116114402Sruint file_input::peek()
117114402Sru{
118114402Sru  if (*ptr != '\0' || read_line())
119114402Sru    return (unsigned char)*ptr;
120114402Sru  else
121114402Sru    return EOF;
122114402Sru}
123114402Sru
124114402Sruint file_input::get_location(const char **fnp, int *lnp)
125114402Sru{
126114402Sru  *fnp = filename;
127114402Sru  *lnp = lineno;
128114402Sru  return 1;
129114402Sru}
130114402Sru
131114402Srumacro_input::macro_input(const char *str)
132114402Sru{
133114402Sru  p = s = strsave(str);
134114402Sru}
135114402Sru
136114402Srumacro_input::~macro_input()
137114402Sru{
138114402Sru  a_delete s;
139114402Sru}
140114402Sru
141114402Sruint macro_input::get()
142114402Sru{
143114402Sru  if (p == 0 || *p == '\0')
144114402Sru    return EOF;
145114402Sru  else
146114402Sru    return (unsigned char)*p++;
147114402Sru}
148114402Sru
149114402Sruint macro_input::peek()
150114402Sru{
151114402Sru  if (p == 0 || *p == '\0')
152114402Sru    return EOF;
153114402Sru  else
154114402Sru    return (unsigned char)*p;
155114402Sru}
156114402Sru
157114402Sru// Character representing $1.  Must be invalid input character.
158114402Sru#define ARG1 14
159114402Sru
160114402Sruchar *process_body(const char *body)
161114402Sru{
162114402Sru  char *s = strsave(body);
163114402Sru  int j = 0;
164114402Sru  for (int i = 0; s[i] != '\0'; i++)
165114402Sru    if (s[i] == '$' && s[i+1] >= '0' && s[i+1] <= '9') {
166114402Sru      if (s[i+1] != '0')
167114402Sru	s[j++] = ARG1 + s[++i] - '1';
168114402Sru    }
169114402Sru    else
170114402Sru      s[j++] = s[i];
171114402Sru  s[j] = '\0';
172114402Sru  return s;
173114402Sru}
174114402Sru
175114402Sru
176114402Sruargument_macro_input::argument_macro_input(const char *body, int ac, char **av)
177114402Sru: ap(0), argc(ac)
178114402Sru{
179114402Sru  for (int i = 0; i < argc; i++)
180114402Sru    argv[i] = av[i];
181114402Sru  p = s = process_body(body);
182114402Sru}
183114402Sru
184114402Sru
185114402Sruargument_macro_input::~argument_macro_input()
186114402Sru{
187114402Sru  for (int i = 0; i < argc; i++)
188114402Sru    a_delete argv[i];
189114402Sru  a_delete s;
190114402Sru}
191114402Sru
192114402Sruint argument_macro_input::get()
193114402Sru{
194114402Sru  if (ap) {
195114402Sru    if (*ap != '\0')
196114402Sru      return (unsigned char)*ap++;
197114402Sru    ap = 0;
198114402Sru  }
199114402Sru  if (p == 0)
200114402Sru    return EOF;
201114402Sru  while (*p >= ARG1 && *p <= ARG1 + 8) {
202114402Sru    int i = *p++ - ARG1;
203114402Sru    if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
204114402Sru      ap = argv[i];
205114402Sru      return (unsigned char)*ap++;
206114402Sru    }
207114402Sru  }
208114402Sru  if (*p == '\0')
209114402Sru    return EOF;
210114402Sru  return (unsigned char)*p++;
211114402Sru}
212114402Sru
213114402Sruint argument_macro_input::peek()
214114402Sru{
215114402Sru  if (ap) {
216114402Sru    if (*ap != '\0')
217114402Sru      return (unsigned char)*ap;
218114402Sru    ap = 0;
219114402Sru  }
220114402Sru  if (p == 0)
221114402Sru    return EOF;
222114402Sru  while (*p >= ARG1 && *p <= ARG1 + 8) {
223114402Sru    int i = *p++ - ARG1;
224114402Sru    if (i < argc && argv[i] != 0 && argv[i][0] != '\0') {
225114402Sru      ap = argv[i];
226114402Sru      return (unsigned char)*ap;
227114402Sru    }
228114402Sru  }
229114402Sru  if (*p == '\0')
230114402Sru    return EOF;
231114402Sru  return (unsigned char)*p;
232114402Sru}
233114402Sru
234114402Sruclass input_stack {
235114402Sru  static input *current_input;
236114402Sru  static int bol_flag;
237114402Srupublic:
238114402Sru  static void push(input *);
239114402Sru  static void clear();
240114402Sru  static int get_char();
241114402Sru  static int peek_char();
242114402Sru  static int get_location(const char **fnp, int *lnp);
243114402Sru  static void push_back(unsigned char c, int was_bol = 0);
244114402Sru  static int bol();
245114402Sru};
246114402Sru
247114402Sruinput *input_stack::current_input = 0;
248114402Sruint input_stack::bol_flag = 0;
249114402Sru
250114402Sruinline int input_stack::bol()
251114402Sru{
252114402Sru  return bol_flag;
253114402Sru}
254114402Sru
255114402Sruvoid input_stack::clear()
256114402Sru{
257114402Sru  while (current_input != 0) {
258114402Sru    input *tem = current_input;
259114402Sru    current_input = current_input->next;
260114402Sru    delete tem;
261114402Sru  }
262114402Sru  bol_flag = 1;
263114402Sru}
264114402Sru
265114402Sruvoid input_stack::push(input *in)
266114402Sru{
267114402Sru  in->next = current_input;
268114402Sru  current_input = in;
269114402Sru}
270114402Sru
271114402Sruvoid lex_init(input *top)
272114402Sru{
273114402Sru  input_stack::clear();
274114402Sru  input_stack::push(top);
275114402Sru}
276114402Sru
277114402Sruvoid lex_cleanup()
278114402Sru{
279114402Sru  while (input_stack::get_char() != EOF)
280114402Sru    ;
281114402Sru}
282114402Sru
283114402Sruint input_stack::get_char()
284114402Sru{
285114402Sru  while (current_input != 0) {
286114402Sru    int c = current_input->get();
287114402Sru    if (c != EOF) {
288114402Sru      bol_flag = c == '\n';
289114402Sru      return c;
290114402Sru    }
291114402Sru    // don't pop the top-level input off the stack
292114402Sru    if (current_input->next == 0)
293114402Sru      return EOF;
294114402Sru    input *tem = current_input;
295114402Sru    current_input = current_input->next;
296114402Sru    delete tem;
297114402Sru  }
298114402Sru  return EOF;
299114402Sru}
300114402Sru
301114402Sruint input_stack::peek_char()
302114402Sru{
303114402Sru  while (current_input != 0) {
304114402Sru    int c = current_input->peek();
305114402Sru    if (c != EOF)
306114402Sru      return c;
307114402Sru    if (current_input->next == 0)
308114402Sru      return EOF;
309114402Sru    input *tem = current_input;
310114402Sru    current_input = current_input->next;
311114402Sru    delete tem;
312114402Sru  }
313114402Sru  return EOF;
314114402Sru}
315114402Sru
316114402Sruclass char_input : public input {
317114402Sru  int c;
318114402Srupublic:
319114402Sru  char_input(int);
320114402Sru  int get();
321114402Sru  int peek();
322114402Sru};
323114402Sru
324114402Sruchar_input::char_input(int n) : c((unsigned char)n)
325114402Sru{
326114402Sru}
327114402Sru
328114402Sruint char_input::get()
329114402Sru{
330114402Sru  int n = c;
331114402Sru  c = EOF;
332114402Sru  return n;
333114402Sru}
334114402Sru
335114402Sruint char_input::peek()
336114402Sru{
337114402Sru  return c;
338114402Sru}
339114402Sru
340114402Sruvoid input_stack::push_back(unsigned char c, int was_bol)
341114402Sru{
342114402Sru  push(new char_input(c));
343114402Sru  bol_flag = was_bol;
344114402Sru}
345114402Sru
346114402Sruint input_stack::get_location(const char **fnp, int *lnp)
347114402Sru{
348114402Sru  for (input *p = current_input; p; p = p->next)
349114402Sru    if (p->get_location(fnp, lnp))
350114402Sru      return 1;
351114402Sru  return 0;
352114402Sru}
353114402Sru
354114402Srustring context_buffer;
355114402Sru
356114402Srustring token_buffer;
357114402Srudouble token_double;
358114402Sruint token_int;
359114402Sru
360114402Sruvoid interpolate_macro_with_args(const char *body)
361114402Sru{
362114402Sru  char *argv[9];
363114402Sru  int argc = 0;
364114402Sru  int i;
365114402Sru  for (i = 0; i < 9; i++)
366114402Sru    argv[i] = 0;
367114402Sru  int level = 0;
368114402Sru  int c;
369114402Sru  enum { NORMAL, IN_STRING, IN_STRING_QUOTED } state = NORMAL;
370114402Sru  do {
371114402Sru    token_buffer.clear();
372114402Sru    for (;;) {
373114402Sru      c = input_stack::get_char();
374114402Sru      if (c == EOF) {
375114402Sru	lex_error("end of input while scanning macro arguments");
376114402Sru	break;
377114402Sru      }
378114402Sru      if (state == NORMAL && level == 0 && (c == ',' || c == ')')) {
379114402Sru	if (token_buffer.length() > 0) {
380114402Sru	  token_buffer +=  '\0';
381114402Sru	  argv[argc] = strsave(token_buffer.contents());
382114402Sru	}
383114402Sru	// for `foo()', argc = 0
384114402Sru	if (argc > 0 || c != ')' || i > 0)
385114402Sru	  argc++;
386114402Sru	break;
387114402Sru      }
388114402Sru      token_buffer += char(c);
389114402Sru      switch (state) {
390114402Sru      case NORMAL:
391114402Sru	if (c == '"')
392114402Sru	  state = IN_STRING;
393114402Sru	else if (c == '(')
394114402Sru	  level++;
395114402Sru	else if (c == ')')
396114402Sru	  level--;
397114402Sru	break;
398114402Sru      case IN_STRING:
399114402Sru	if (c == '"')
400114402Sru	  state = NORMAL;
401114402Sru	else if (c == '\\')
402114402Sru	  state = IN_STRING_QUOTED;
403114402Sru	break;
404114402Sru      case IN_STRING_QUOTED:
405114402Sru	state = IN_STRING;
406114402Sru	break;
407114402Sru      }
408114402Sru    }
409114402Sru  } while (c != ')' && c != EOF);
410114402Sru  input_stack::push(new argument_macro_input(body, argc, argv));
411114402Sru}
412114402Sru
413114402Srustatic int docmp(const char *s1, int n1, const char *s2, int n2)
414114402Sru{
415114402Sru  if (n1 < n2) {
416114402Sru    int r = memcmp(s1, s2, n1);
417114402Sru    return r ? r : -1;
418114402Sru  }
419114402Sru  else if (n1 > n2) {
420114402Sru    int r = memcmp(s1, s2, n2);
421114402Sru    return r ? r : 1;
422114402Sru  }
423114402Sru  else
424114402Sru    return memcmp(s1, s2, n1);
425114402Sru}
426114402Sru
427114402Sruint lookup_keyword(const char *str, int len)
428114402Sru{
429114402Sru  static struct keyword {
430114402Sru    const char *name;
431114402Sru    int token;
432114402Sru  } table[] = {
433114402Sru    { "Here", HERE },
434114402Sru    { "above", ABOVE },
435114402Sru    { "aligned", ALIGNED },
436114402Sru    { "and", AND },
437114402Sru    { "arc", ARC },
438114402Sru    { "arrow", ARROW },
439114402Sru    { "at", AT },
440114402Sru    { "atan2", ATAN2 },
441114402Sru    { "below", BELOW },
442114402Sru    { "between", BETWEEN },
443114402Sru    { "bottom", BOTTOM },
444114402Sru    { "box", BOX },
445114402Sru    { "by", BY },
446114402Sru    { "ccw", CCW },
447114402Sru    { "center", CENTER },
448114402Sru    { "chop", CHOP },
449114402Sru    { "circle", CIRCLE },
450114402Sru    { "color", COLORED },
451114402Sru    { "colored", COLORED },
452114402Sru    { "colour", COLORED },
453114402Sru    { "coloured", COLORED },
454114402Sru    { "command", COMMAND },
455114402Sru    { "copy", COPY },
456114402Sru    { "cos", COS },
457114402Sru    { "cw", CW },
458114402Sru    { "dashed", DASHED },
459114402Sru    { "define", DEFINE },
460114402Sru    { "diam", DIAMETER },
461114402Sru    { "diameter", DIAMETER },
462114402Sru    { "do", DO },
463114402Sru    { "dotted", DOTTED },
464114402Sru    { "down", DOWN },
465114402Sru    { "east", EAST },
466114402Sru    { "ellipse", ELLIPSE },
467114402Sru    { "else", ELSE },
468114402Sru    { "end", END },
469114402Sru    { "exp", EXP },
470114402Sru    { "figname", FIGNAME },
471114402Sru    { "fill", FILL },
472114402Sru    { "filled", FILL },
473114402Sru    { "for", FOR },
474114402Sru    { "from", FROM },
475114402Sru    { "height", HEIGHT },
476114402Sru    { "ht", HEIGHT },
477114402Sru    { "if", IF },
478114402Sru    { "int", INT },
479114402Sru    { "invis", INVISIBLE },
480114402Sru    { "invisible", INVISIBLE },
481114402Sru    { "last", LAST },
482114402Sru    { "left", LEFT },
483114402Sru    { "line", LINE },
484114402Sru    { "ljust", LJUST },
485114402Sru    { "log", LOG },
486114402Sru    { "lower", LOWER },
487114402Sru    { "max", K_MAX },
488114402Sru    { "min", K_MIN },
489114402Sru    { "move", MOVE },
490114402Sru    { "north", NORTH },
491114402Sru    { "of", OF },
492114402Sru    { "outline", OUTLINED },
493114402Sru    { "outlined", OUTLINED },
494114402Sru    { "plot", PLOT },
495114402Sru    { "print", PRINT },
496114402Sru    { "rad", RADIUS },
497114402Sru    { "radius", RADIUS },
498114402Sru    { "rand", RAND },
499114402Sru    { "reset", RESET },
500114402Sru    { "right", RIGHT },
501114402Sru    { "rjust", RJUST },
502114402Sru    { "same", SAME },
503114402Sru    { "sh", SH },
504114402Sru    { "shaded", SHADED },
505114402Sru    { "sin", SIN },
506114402Sru    { "solid", SOLID },
507114402Sru    { "south", SOUTH },
508114402Sru    { "spline", SPLINE },
509114402Sru    { "sprintf", SPRINTF },
510114402Sru    { "sqrt", SQRT },
511114402Sru    { "srand", SRAND },
512114402Sru    { "start", START },
513114402Sru    { "the", THE },
514114402Sru    { "then", THEN },
515114402Sru    { "thick", THICKNESS },
516114402Sru    { "thickness", THICKNESS },
517114402Sru    { "thru", THRU },
518114402Sru    { "to", TO },
519114402Sru    { "top", TOP },
520114402Sru    { "undef", UNDEF },
521114402Sru    { "until", UNTIL },
522114402Sru    { "up", UP },
523114402Sru    { "upper", UPPER },
524114402Sru    { "way", WAY },
525114402Sru    { "west", WEST },
526114402Sru    { "wid", WIDTH },
527114402Sru    { "width", WIDTH },
528114402Sru    { "with", WITH },
529114402Sru  };
530114402Sru
531114402Sru  const keyword *start = table;
532114402Sru  const keyword *end = table + sizeof(table)/sizeof(table[0]);
533114402Sru  while (start < end) {
534114402Sru    // start <= target < end
535114402Sru    const keyword *mid = start + (end - start)/2;
536114402Sru
537114402Sru    int cmp = docmp(str, len, mid->name, strlen(mid->name));
538114402Sru    if (cmp == 0)
539114402Sru      return mid->token;
540114402Sru    if (cmp < 0)
541114402Sru      end = mid;
542114402Sru    else
543114402Sru      start = mid + 1;
544114402Sru  }
545114402Sru  return 0;
546114402Sru}
547114402Sru
548114402Sruint get_token_after_dot(int c)
549114402Sru{
550114402Sru  // get_token deals with the case where c is a digit
551114402Sru  switch (c) {
552114402Sru  case 'h':
553114402Sru    input_stack::get_char();
554114402Sru    c = input_stack::peek_char();
555114402Sru    if (c == 't') {
556114402Sru      input_stack::get_char();
557114402Sru      context_buffer = ".ht";
558114402Sru      return DOT_HT;
559114402Sru    }
560114402Sru    else if (c == 'e') {
561114402Sru      input_stack::get_char();
562114402Sru      c = input_stack::peek_char();
563114402Sru      if (c == 'i') {
564114402Sru	input_stack::get_char();
565114402Sru	c = input_stack::peek_char();
566114402Sru	if (c == 'g') {
567114402Sru	  input_stack::get_char();
568114402Sru	  c = input_stack::peek_char();
569114402Sru	  if (c == 'h') {
570114402Sru	    input_stack::get_char();
571114402Sru	    c = input_stack::peek_char();
572114402Sru	    if (c == 't') {
573114402Sru	      input_stack::get_char();
574114402Sru	      context_buffer = ".height";
575114402Sru	      return DOT_HT;
576114402Sru	    }
577114402Sru	    input_stack::push_back('h');
578114402Sru	  }
579114402Sru	  input_stack::push_back('g');
580114402Sru	}
581114402Sru	input_stack::push_back('i');
582114402Sru      }
583114402Sru      input_stack::push_back('e');
584114402Sru    }
585114402Sru    input_stack::push_back('h');
586114402Sru    return '.';
587114402Sru  case 'x':
588114402Sru    input_stack::get_char();
589114402Sru    context_buffer = ".x";
590114402Sru    return DOT_X;
591114402Sru  case 'y':
592114402Sru    input_stack::get_char();
593114402Sru    context_buffer = ".y";
594114402Sru    return DOT_Y;
595114402Sru  case 'c':
596114402Sru    input_stack::get_char();
597114402Sru    c = input_stack::peek_char();
598114402Sru    if (c == 'e') {
599114402Sru      input_stack::get_char();
600114402Sru      c = input_stack::peek_char();
601114402Sru      if (c == 'n') {
602114402Sru	input_stack::get_char();
603114402Sru	c = input_stack::peek_char();
604114402Sru	if (c == 't') {
605114402Sru	  input_stack::get_char();
606114402Sru	  c = input_stack::peek_char();
607114402Sru	  if (c == 'e') {
608114402Sru	    input_stack::get_char();
609114402Sru	    c = input_stack::peek_char();
610114402Sru	    if (c == 'r') {
611114402Sru	      input_stack::get_char();
612114402Sru	      context_buffer = ".center";
613114402Sru	      return DOT_C;
614114402Sru	    }
615114402Sru	    input_stack::push_back('e');
616114402Sru	  }
617114402Sru	  input_stack::push_back('t');
618114402Sru	}
619114402Sru	input_stack::push_back('n');
620114402Sru      }
621114402Sru      input_stack::push_back('e');
622114402Sru    }
623114402Sru    context_buffer = ".c";
624114402Sru    return DOT_C;
625114402Sru  case 'n':
626114402Sru    input_stack::get_char();
627114402Sru    c = input_stack::peek_char();
628114402Sru    if (c == 'e') {
629114402Sru      input_stack::get_char();
630114402Sru      context_buffer = ".ne";
631114402Sru      return DOT_NE;
632114402Sru    }
633114402Sru    else if (c == 'w') {
634114402Sru      input_stack::get_char();
635114402Sru      context_buffer = ".nw";
636114402Sru      return DOT_NW;
637114402Sru    }
638114402Sru    else {
639114402Sru      context_buffer = ".n";
640114402Sru      return DOT_N;
641114402Sru    }
642114402Sru    break;
643114402Sru  case 'e':
644114402Sru    input_stack::get_char();
645114402Sru    c = input_stack::peek_char();
646114402Sru    if (c == 'n') {
647114402Sru      input_stack::get_char();
648114402Sru      c = input_stack::peek_char();
649114402Sru      if (c == 'd') {
650114402Sru	input_stack::get_char();
651114402Sru	context_buffer = ".end";
652114402Sru	return DOT_END;
653114402Sru      }
654114402Sru      input_stack::push_back('n');
655114402Sru      context_buffer = ".e";
656114402Sru      return DOT_E;
657114402Sru    }
658114402Sru    context_buffer = ".e";
659114402Sru    return DOT_E;
660114402Sru  case 'w':
661114402Sru    input_stack::get_char();
662114402Sru    c = input_stack::peek_char();
663114402Sru    if (c == 'i') {
664114402Sru      input_stack::get_char();
665114402Sru      c = input_stack::peek_char();
666114402Sru      if (c == 'd') {
667114402Sru	input_stack::get_char();
668114402Sru	c = input_stack::peek_char();
669114402Sru	if (c == 't') {
670114402Sru	  input_stack::get_char();
671114402Sru	  c = input_stack::peek_char();
672114402Sru	  if (c == 'h') {
673114402Sru	    input_stack::get_char();
674114402Sru	    context_buffer = ".width";
675114402Sru	    return DOT_WID;
676114402Sru	  }
677114402Sru	  input_stack::push_back('t');
678114402Sru	}
679114402Sru	context_buffer = ".wid";
680114402Sru	return DOT_WID;
681114402Sru      }
682114402Sru      input_stack::push_back('i');
683114402Sru    }
684114402Sru    context_buffer = ".w";
685114402Sru    return DOT_W;
686114402Sru  case 's':
687114402Sru    input_stack::get_char();
688114402Sru    c = input_stack::peek_char();
689114402Sru    if (c == 'e') {
690114402Sru      input_stack::get_char();
691114402Sru      context_buffer = ".se";
692114402Sru      return DOT_SE;
693114402Sru    }
694114402Sru    else if (c == 'w') {
695114402Sru      input_stack::get_char();
696114402Sru      context_buffer = ".sw";
697114402Sru      return DOT_SW;
698114402Sru    }
699114402Sru    else {
700114402Sru      if (c == 't') {
701114402Sru	input_stack::get_char();
702114402Sru	c = input_stack::peek_char();
703114402Sru	if (c == 'a') {
704114402Sru	  input_stack::get_char();
705114402Sru	  c = input_stack::peek_char();
706114402Sru	  if (c == 'r') {
707114402Sru	    input_stack::get_char();
708114402Sru	    c = input_stack::peek_char();
709114402Sru	    if (c == 't') {
710114402Sru	      input_stack::get_char();
711114402Sru	      context_buffer = ".start";
712114402Sru	      return DOT_START;
713114402Sru	    }
714114402Sru	    input_stack::push_back('r');
715114402Sru	  }
716114402Sru	  input_stack::push_back('a');
717114402Sru	}
718114402Sru	input_stack::push_back('t');
719114402Sru      }
720114402Sru      context_buffer = ".s";
721114402Sru      return DOT_S;
722114402Sru    }
723114402Sru    break;
724114402Sru  case 't':
725114402Sru    input_stack::get_char();
726114402Sru    c = input_stack::peek_char();
727114402Sru    if (c == 'o') {
728114402Sru      input_stack::get_char();
729114402Sru      c = input_stack::peek_char();
730114402Sru      if (c == 'p') {
731114402Sru	input_stack::get_char();
732114402Sru	context_buffer = ".top";
733114402Sru	return DOT_N;
734114402Sru      }
735114402Sru      input_stack::push_back('o');
736114402Sru    }
737114402Sru    context_buffer = ".t";
738114402Sru    return DOT_N;
739114402Sru  case 'l':
740114402Sru    input_stack::get_char();
741114402Sru    c = input_stack::peek_char();
742114402Sru    if (c == 'e') {
743114402Sru      input_stack::get_char();
744114402Sru      c = input_stack::peek_char();
745114402Sru      if (c == 'f') {
746114402Sru	input_stack::get_char();
747114402Sru	c = input_stack::peek_char();
748114402Sru	if (c == 't') {
749114402Sru	  input_stack::get_char();
750114402Sru	  context_buffer = ".left";
751114402Sru	  return DOT_W;
752114402Sru	}
753114402Sru	input_stack::push_back('f');
754114402Sru      }
755114402Sru      input_stack::push_back('e');
756114402Sru    }
757114402Sru    context_buffer = ".l";
758114402Sru    return DOT_W;
759114402Sru  case 'r':
760114402Sru    input_stack::get_char();
761114402Sru    c = input_stack::peek_char();
762114402Sru    if (c == 'a') {
763114402Sru      input_stack::get_char();
764114402Sru      c = input_stack::peek_char();
765114402Sru      if (c == 'd') {
766114402Sru	input_stack::get_char();
767114402Sru	context_buffer = ".rad";
768114402Sru	return DOT_RAD;
769114402Sru      }
770114402Sru      input_stack::push_back('a');
771114402Sru    }
772114402Sru    else if (c == 'i') {
773114402Sru      input_stack::get_char();
774114402Sru      c = input_stack::peek_char();
775114402Sru      if (c == 'g') {
776114402Sru	input_stack::get_char();
777114402Sru	c = input_stack::peek_char();
778114402Sru	if (c == 'h') {
779114402Sru	  input_stack::get_char();
780114402Sru	  c = input_stack::peek_char();
781114402Sru	  if (c == 't') {
782114402Sru	    input_stack::get_char();
783114402Sru	    context_buffer = ".right";
784114402Sru	    return DOT_E;
785114402Sru	  }
786114402Sru	  input_stack::push_back('h');
787114402Sru	}
788114402Sru	input_stack::push_back('g');
789114402Sru      }
790114402Sru      input_stack::push_back('i');
791114402Sru    }
792114402Sru    context_buffer = ".r";
793114402Sru    return DOT_E;
794114402Sru  case 'b':
795114402Sru    input_stack::get_char();
796114402Sru    c = input_stack::peek_char();
797114402Sru    if (c == 'o') {
798114402Sru      input_stack::get_char();
799114402Sru      c = input_stack::peek_char();
800114402Sru      if (c == 't') {
801114402Sru	input_stack::get_char();
802114402Sru	c = input_stack::peek_char();
803114402Sru	if (c == 't') {
804114402Sru	  input_stack::get_char();
805114402Sru	  c = input_stack::peek_char();
806114402Sru	  if (c == 'o') {
807114402Sru	    input_stack::get_char();
808114402Sru	    c = input_stack::peek_char();
809114402Sru	    if (c == 'm') {
810114402Sru	      input_stack::get_char();
811114402Sru	      context_buffer = ".bottom";
812114402Sru	      return DOT_S;
813114402Sru	    }
814114402Sru	    input_stack::push_back('o');
815114402Sru	  }
816114402Sru	  input_stack::push_back('t');
817114402Sru	}
818114402Sru	context_buffer = ".bot";
819114402Sru	return DOT_S;
820114402Sru      }
821114402Sru      input_stack::push_back('o');
822114402Sru    }
823114402Sru    context_buffer = ".b";
824114402Sru    return DOT_S;
825114402Sru  default:
826114402Sru    context_buffer = '.';
827114402Sru    return '.';
828114402Sru  }
829114402Sru}
830114402Sru
831114402Sruint get_token(int lookup_flag)
832114402Sru{
833114402Sru  context_buffer.clear();
834114402Sru  for (;;) {
835114402Sru    int n = 0;
836114402Sru    int bol = input_stack::bol();
837114402Sru    int c = input_stack::get_char();
838114402Sru    if (bol && c == command_char) {
839114402Sru      token_buffer.clear();
840114402Sru      token_buffer += c;
841114402Sru      // the newline is not part of the token
842114402Sru      for (;;) {
843114402Sru	c = input_stack::peek_char();
844114402Sru	if (c == EOF || c == '\n')
845114402Sru	  break;
846114402Sru	input_stack::get_char();
847114402Sru	token_buffer += char(c);
848114402Sru      }
849114402Sru      context_buffer = token_buffer;
850114402Sru      return COMMAND_LINE;
851114402Sru    }
852114402Sru    switch (c) {
853114402Sru    case EOF:
854114402Sru      return EOF;
855114402Sru    case ' ':
856114402Sru    case '\t':
857114402Sru      break;
858114402Sru    case '\\':
859114402Sru      {
860114402Sru	int d = input_stack::peek_char();
861114402Sru	if (d != '\n') {
862114402Sru	  context_buffer = '\\';
863114402Sru	  return '\\';
864114402Sru	}
865114402Sru	input_stack::get_char();
866114402Sru	break;
867114402Sru      }
868114402Sru    case '#':
869114402Sru      do {
870114402Sru	c = input_stack::get_char();
871114402Sru      } while (c != '\n' && c != EOF);
872114402Sru      if (c == '\n')
873114402Sru	context_buffer = '\n';
874114402Sru      return c;
875114402Sru    case '"':
876114402Sru      context_buffer = '"';
877114402Sru      token_buffer.clear();
878114402Sru      for (;;) {
879114402Sru	c = input_stack::get_char();
880114402Sru	if (c == '\\') {
881114402Sru	  context_buffer += '\\';
882114402Sru	  c = input_stack::peek_char();
883114402Sru	  if (c == '"') {
884114402Sru	    input_stack::get_char();
885114402Sru	    token_buffer += '"';
886114402Sru	    context_buffer += '"';
887114402Sru	  }
888114402Sru	  else
889114402Sru	    token_buffer += '\\';
890114402Sru	}
891114402Sru	else if (c == '\n') {
892114402Sru	  error("newline in string");
893114402Sru	  break;
894114402Sru	}
895114402Sru	else if (c == EOF) {
896114402Sru	  error("missing `\"'");
897114402Sru	  break;
898114402Sru	}
899114402Sru	else if (c == '"') {
900114402Sru	  context_buffer += '"';
901114402Sru	  break;
902114402Sru	}
903114402Sru	else {
904114402Sru	  context_buffer += char(c);
905114402Sru	  token_buffer += char(c);
906114402Sru	}
907114402Sru      }
908114402Sru      return TEXT;
909114402Sru    case '0':
910114402Sru    case '1':
911114402Sru    case '2':
912114402Sru    case '3':
913114402Sru    case '4':
914114402Sru    case '5':
915114402Sru    case '6':
916114402Sru    case '7':
917114402Sru    case '8':
918114402Sru    case '9':
919114402Sru      {
920114402Sru	int overflow = 0;
921114402Sru	n = 0;
922114402Sru	for (;;) {
923114402Sru	  if (n > (INT_MAX - 9)/10) {
924114402Sru	    overflow = 1;
925114402Sru	    break;
926114402Sru	  }
927114402Sru	  n *= 10;
928114402Sru	  n += c - '0';
929114402Sru	  context_buffer += char(c);
930114402Sru	  c = input_stack::peek_char();
931114402Sru	  if (c == EOF || !csdigit(c))
932114402Sru	    break;
933114402Sru	  c = input_stack::get_char();
934114402Sru	}
935114402Sru	token_double = n;
936114402Sru	if (overflow) {
937114402Sru	  for (;;) {
938114402Sru	    token_double *= 10.0;
939114402Sru	    token_double += c - '0';
940114402Sru	    context_buffer += char(c);
941114402Sru	    c = input_stack::peek_char();
942114402Sru	    if (c == EOF || !csdigit(c))
943114402Sru	      break;
944114402Sru	    c = input_stack::get_char();
945114402Sru	  }
946114402Sru	  // if somebody asks for 1000000000000th, we will silently
947114402Sru	  // give them INT_MAXth
948114402Sru	  double temp = token_double; // work around gas 1.34/sparc bug
949114402Sru	  if (token_double > INT_MAX)
950114402Sru	    n = INT_MAX;
951114402Sru	  else
952114402Sru	    n = int(temp);
953114402Sru	}
954114402Sru      }
955114402Sru      switch (c) {
956114402Sru      case 'i':
957114402Sru      case 'I':
958114402Sru	context_buffer += char(c);
959114402Sru	input_stack::get_char();
960114402Sru	return NUMBER;
961114402Sru      case '.':
962114402Sru	{
963114402Sru	  context_buffer += '.';
964114402Sru	  input_stack::get_char();
965114402Sru	got_dot:
966114402Sru	  double factor = 1.0;
967114402Sru	  for (;;) {
968114402Sru	    c = input_stack::peek_char();
969114402Sru	    if (c == EOF || !csdigit(c))
970114402Sru	      break;
971114402Sru	    input_stack::get_char();
972114402Sru	    context_buffer += char(c);
973114402Sru	    factor /= 10.0;
974114402Sru	    if (c != '0')
975114402Sru	      token_double += factor*(c - '0');
976114402Sru	  }
977114402Sru	  if (c != 'e' && c != 'E') {
978114402Sru	    if (c == 'i' || c == 'I') {
979114402Sru	      context_buffer += char(c);
980114402Sru	      input_stack::get_char();
981114402Sru	    }
982114402Sru	    return NUMBER;
983114402Sru	  }
984114402Sru	}
985114402Sru	// fall through
986114402Sru      case 'e':
987114402Sru      case 'E':
988114402Sru	{
989114402Sru	  int echar = c;
990114402Sru	  input_stack::get_char();
991114402Sru	  c = input_stack::peek_char();
992114402Sru	  int sign = '+';
993114402Sru	  if (c == '+' || c == '-') {
994114402Sru	    sign = c;
995114402Sru	    input_stack::get_char();
996114402Sru	    c = input_stack::peek_char();
997114402Sru	    if (c == EOF || !csdigit(c)) {
998114402Sru	      input_stack::push_back(sign);
999114402Sru	      input_stack::push_back(echar);
1000114402Sru	      return NUMBER;
1001114402Sru	    }
1002114402Sru	    context_buffer += char(echar);
1003114402Sru	    context_buffer += char(sign);
1004114402Sru	  }
1005114402Sru	  else {
1006114402Sru	    if (c == EOF || !csdigit(c)) {
1007114402Sru	      input_stack::push_back(echar);
1008114402Sru	      return NUMBER;
1009114402Sru	    }
1010114402Sru	    context_buffer += char(echar);
1011114402Sru	  }
1012114402Sru	  input_stack::get_char();
1013114402Sru	  context_buffer += char(c);
1014114402Sru	  n = c - '0';
1015114402Sru	  for (;;) {
1016114402Sru	    c = input_stack::peek_char();
1017114402Sru	    if (c == EOF || !csdigit(c))
1018114402Sru	      break;
1019114402Sru	    input_stack::get_char();
1020114402Sru	    context_buffer += char(c);
1021114402Sru	    n = n*10 + (c - '0');
1022114402Sru	  }
1023114402Sru	  if (sign == '-')
1024114402Sru	    n = -n;
1025114402Sru	  if (c == 'i' || c == 'I') {
1026114402Sru	    context_buffer += char(c);
1027114402Sru	    input_stack::get_char();
1028114402Sru	  }
1029114402Sru	  token_double *= pow(10.0, n);
1030114402Sru	  return NUMBER;
1031114402Sru	}
1032114402Sru      case 'n':
1033114402Sru	input_stack::get_char();
1034114402Sru	c = input_stack::peek_char();
1035114402Sru	if (c == 'd') {
1036114402Sru	  input_stack::get_char();
1037114402Sru	  token_int = n;
1038114402Sru	  context_buffer += "nd";
1039114402Sru	  return ORDINAL;
1040114402Sru	}
1041114402Sru	input_stack::push_back('n');
1042114402Sru	return NUMBER;
1043114402Sru      case 'r':
1044114402Sru	input_stack::get_char();
1045114402Sru	c = input_stack::peek_char();
1046114402Sru	if (c == 'd') {
1047114402Sru	  input_stack::get_char();
1048114402Sru	  token_int = n;
1049114402Sru	  context_buffer += "rd";
1050114402Sru	  return ORDINAL;
1051114402Sru	}
1052114402Sru	input_stack::push_back('r');
1053114402Sru	return NUMBER;
1054114402Sru      case 't':
1055114402Sru	input_stack::get_char();
1056114402Sru	c = input_stack::peek_char();
1057114402Sru	if (c == 'h') {
1058114402Sru	  input_stack::get_char();
1059114402Sru	  token_int = n;
1060114402Sru	  context_buffer += "th";
1061114402Sru	  return ORDINAL;
1062114402Sru	}
1063114402Sru	input_stack::push_back('t');
1064114402Sru	return NUMBER;
1065114402Sru      case 's':
1066114402Sru	input_stack::get_char();
1067114402Sru	c = input_stack::peek_char();
1068114402Sru	if (c == 't') {
1069114402Sru	  input_stack::get_char();
1070114402Sru	  token_int = n;
1071114402Sru	  context_buffer += "st";
1072114402Sru	  return ORDINAL;
1073114402Sru	}
1074114402Sru	input_stack::push_back('s');
1075114402Sru	return NUMBER;
1076114402Sru      default:
1077114402Sru	return NUMBER;
1078114402Sru      }
1079114402Sru      break;
1080114402Sru    case '\'':
1081114402Sru      {
1082114402Sru	c = input_stack::peek_char();
1083114402Sru	if (c == 't') {
1084114402Sru	  input_stack::get_char();
1085114402Sru	  c = input_stack::peek_char();
1086114402Sru	  if (c == 'h') {
1087114402Sru	    input_stack::get_char();
1088114402Sru	    context_buffer = "'th";
1089114402Sru	    return TH;
1090114402Sru	  }
1091114402Sru	  else
1092114402Sru	    input_stack::push_back('t');
1093114402Sru	}
1094114402Sru	context_buffer = "'";
1095114402Sru	return '\'';
1096114402Sru      }
1097114402Sru    case '.':
1098114402Sru      {
1099114402Sru	c = input_stack::peek_char();
1100114402Sru	if (c != EOF && csdigit(c)) {
1101114402Sru	  n = 0;
1102114402Sru	  token_double = 0.0;
1103114402Sru	  context_buffer = '.';
1104114402Sru	  goto got_dot;
1105114402Sru	}
1106114402Sru	return get_token_after_dot(c);
1107114402Sru      }
1108114402Sru    case '<':
1109114402Sru      c = input_stack::peek_char();
1110114402Sru      if (c == '-') {
1111114402Sru	input_stack::get_char();
1112114402Sru	c = input_stack::peek_char();
1113114402Sru	if (c == '>') {
1114114402Sru	  input_stack::get_char();
1115114402Sru	  context_buffer = "<->";
1116114402Sru	  return DOUBLE_ARROW_HEAD;
1117114402Sru	}
1118114402Sru	context_buffer = "<-";
1119114402Sru	return LEFT_ARROW_HEAD;
1120114402Sru      }
1121114402Sru      else if (c == '=') {
1122114402Sru	input_stack::get_char();
1123114402Sru	context_buffer = "<=";
1124114402Sru	return LESSEQUAL;
1125114402Sru      }
1126114402Sru      context_buffer = "<";
1127114402Sru      return '<';
1128114402Sru    case '-':
1129114402Sru      c = input_stack::peek_char();
1130114402Sru      if (c == '>') {
1131114402Sru	input_stack::get_char();
1132114402Sru	context_buffer = "->";
1133114402Sru	return RIGHT_ARROW_HEAD;
1134114402Sru      }
1135114402Sru      context_buffer = "-";
1136114402Sru      return '-';
1137114402Sru    case '!':
1138114402Sru      c = input_stack::peek_char();
1139114402Sru      if (c == '=') {
1140114402Sru	input_stack::get_char();
1141114402Sru	context_buffer = "!=";
1142114402Sru	return NOTEQUAL;
1143114402Sru      }
1144114402Sru      context_buffer = "!";
1145114402Sru      return '!';
1146114402Sru    case '>':
1147114402Sru      c = input_stack::peek_char();
1148114402Sru      if (c == '=') {
1149114402Sru	input_stack::get_char();
1150114402Sru	context_buffer = ">=";
1151114402Sru	return GREATEREQUAL;
1152114402Sru      }
1153114402Sru      context_buffer = ">";
1154114402Sru      return '>';
1155114402Sru    case '=':
1156114402Sru      c = input_stack::peek_char();
1157114402Sru      if (c == '=') {
1158114402Sru	input_stack::get_char();
1159114402Sru	context_buffer = "==";
1160114402Sru	return EQUALEQUAL;
1161114402Sru      }
1162114402Sru      context_buffer = "=";
1163114402Sru      return '=';
1164114402Sru    case '&':
1165114402Sru      c = input_stack::peek_char();
1166114402Sru      if (c == '&') {
1167114402Sru	input_stack::get_char();
1168114402Sru	context_buffer = "&&";
1169114402Sru	return ANDAND;
1170114402Sru      }
1171114402Sru      context_buffer = "&";
1172114402Sru      return '&';
1173114402Sru    case '|':
1174114402Sru      c = input_stack::peek_char();
1175114402Sru      if (c == '|') {
1176114402Sru	input_stack::get_char();
1177114402Sru	context_buffer = "||";
1178114402Sru	return OROR;
1179114402Sru      }
1180114402Sru      context_buffer = "|";
1181114402Sru      return '|';
1182114402Sru    default:
1183114402Sru      if (c != EOF && csalpha(c)) {
1184114402Sru	token_buffer.clear();
1185114402Sru	token_buffer = c;
1186114402Sru	for (;;) {
1187114402Sru	  c = input_stack::peek_char();
1188114402Sru	  if (c == EOF || (!csalnum(c) && c != '_'))
1189114402Sru	    break;
1190114402Sru	  input_stack::get_char();
1191114402Sru	  token_buffer += char(c);
1192114402Sru	}
1193114402Sru	int tok = lookup_keyword(token_buffer.contents(),
1194114402Sru				 token_buffer.length());
1195114402Sru	if (tok != 0) {
1196114402Sru	  context_buffer = token_buffer;
1197114402Sru	  return tok;
1198114402Sru	}
1199114402Sru	char *def = 0;
1200114402Sru	if (lookup_flag) {
1201114402Sru	  token_buffer += '\0';
1202114402Sru	  def = macro_table.lookup(token_buffer.contents());
1203114402Sru	  token_buffer.set_length(token_buffer.length() - 1);
1204114402Sru	  if (def) {
1205114402Sru	    if (c == '(') {
1206114402Sru	      input_stack::get_char();
1207114402Sru	      interpolate_macro_with_args(def);
1208114402Sru	    }
1209114402Sru	    else
1210114402Sru	      input_stack::push(new macro_input(def));
1211114402Sru	  }
1212114402Sru	}
1213114402Sru	if (!def) {
1214114402Sru	  context_buffer = token_buffer;
1215114402Sru	  if (csupper(token_buffer[0]))
1216114402Sru	    return LABEL;
1217114402Sru	  else
1218114402Sru	    return VARIABLE;
1219114402Sru	}
1220114402Sru      }
1221114402Sru      else {
1222114402Sru	context_buffer = char(c);
1223114402Sru	return (unsigned char)c;
1224114402Sru      }
1225114402Sru      break;
1226114402Sru    }
1227114402Sru  }
1228114402Sru}
1229114402Sru
1230114402Sruint get_delimited()
1231114402Sru{
1232114402Sru  token_buffer.clear();
1233114402Sru  int c = input_stack::get_char();
1234114402Sru  while (c == ' ' || c == '\t' || c == '\n')
1235114402Sru    c = input_stack::get_char();
1236114402Sru  if (c == EOF) {
1237114402Sru    lex_error("missing delimiter");
1238114402Sru    return 0;
1239114402Sru  }
1240114402Sru  context_buffer = char(c);
1241114402Sru  int had_newline = 0;
1242114402Sru  int start = c;
1243114402Sru  int level = 0;
1244114402Sru  enum { NORMAL, IN_STRING, IN_STRING_QUOTED, DELIM_END } state = NORMAL;
1245114402Sru  for (;;) {
1246114402Sru    c = input_stack::get_char();
1247114402Sru    if (c == EOF) {
1248114402Sru      lex_error("missing closing delimiter");
1249114402Sru      return 0;
1250114402Sru    }
1251114402Sru    if (c == '\n')
1252114402Sru      had_newline = 1;
1253114402Sru    else if (!had_newline)
1254114402Sru      context_buffer += char(c);
1255114402Sru    switch (state) {
1256114402Sru    case NORMAL:
1257114402Sru      if (start == '{') {
1258114402Sru	if (c == '{') {
1259114402Sru	  level++;
1260114402Sru	  break;
1261114402Sru	}
1262114402Sru	if (c == '}') {
1263114402Sru	  if (--level < 0)
1264114402Sru	    state = DELIM_END;
1265114402Sru	  break;
1266114402Sru	}
1267114402Sru      }
1268114402Sru      else {
1269114402Sru	if (c == start) {
1270114402Sru	  state = DELIM_END;
1271114402Sru	  break;
1272114402Sru	}
1273114402Sru      }
1274114402Sru      if (c == '"')
1275114402Sru	state = IN_STRING;
1276114402Sru      break;
1277114402Sru    case IN_STRING_QUOTED:
1278114402Sru      if (c == '\n')
1279114402Sru	state = NORMAL;
1280114402Sru      else
1281114402Sru	state = IN_STRING;
1282114402Sru      break;
1283114402Sru    case IN_STRING:
1284114402Sru      if (c == '"' || c == '\n')
1285114402Sru	state = NORMAL;
1286114402Sru      else if (c == '\\')
1287114402Sru	state = IN_STRING_QUOTED;
1288114402Sru      break;
1289114402Sru    case DELIM_END:
1290114402Sru      // This case it just to shut cfront 2.0 up.
1291114402Sru    default:
1292114402Sru      assert(0);
1293114402Sru    }
1294114402Sru    if (state == DELIM_END)
1295114402Sru      break;
1296114402Sru    token_buffer += c;
1297114402Sru  }
1298114402Sru  return 1;
1299114402Sru}
1300114402Sru
1301114402Sruvoid do_define()
1302114402Sru{
1303114402Sru  int t = get_token(0);		// do not expand what we are defining
1304114402Sru  if (t != VARIABLE && t != LABEL) {
1305114402Sru    lex_error("can only define variable or placename");
1306114402Sru    return;
1307114402Sru  }
1308114402Sru  token_buffer += '\0';
1309114402Sru  string nm = token_buffer;
1310114402Sru  const char *name = nm.contents();
1311114402Sru  if (!get_delimited())
1312114402Sru    return;
1313114402Sru  token_buffer += '\0';
1314114402Sru  macro_table.define(name, strsave(token_buffer.contents()));
1315114402Sru}
1316114402Sru
1317114402Sruvoid do_undef()
1318114402Sru{
1319114402Sru  int t = get_token(0);		// do not expand what we are undefining
1320114402Sru  if (t != VARIABLE && t != LABEL) {
1321114402Sru    lex_error("can only define variable or placename");
1322114402Sru    return;
1323114402Sru  }
1324114402Sru  token_buffer += '\0';
1325114402Sru  macro_table.define(token_buffer.contents(), 0);
1326114402Sru}
1327114402Sru
1328114402Sru
1329114402Sruclass for_input : public input {
1330114402Sru  char *var;
1331114402Sru  char *body;
1332151497Sru  double from;
1333114402Sru  double to;
1334114402Sru  int by_is_multiplicative;
1335114402Sru  double by;
1336114402Sru  const char *p;
1337114402Sru  int done_newline;
1338114402Srupublic:
1339151497Sru  for_input(char *, double, double, int, double, char *);
1340114402Sru  ~for_input();
1341114402Sru  int get();
1342114402Sru  int peek();
1343114402Sru};
1344114402Sru
1345151497Srufor_input::for_input(char *vr, double f, double t,
1346151497Sru		     int bim, double b, char *bd)
1347151497Sru: var(vr), body(bd), from(f), to(t), by_is_multiplicative(bim), by(b),
1348151497Sru  p(body), done_newline(0)
1349114402Sru{
1350114402Sru}
1351114402Sru
1352114402Srufor_input::~for_input()
1353114402Sru{
1354114402Sru  a_delete var;
1355114402Sru  a_delete body;
1356114402Sru}
1357114402Sru
1358114402Sruint for_input::get()
1359114402Sru{
1360114402Sru  if (p == 0)
1361114402Sru    return EOF;
1362114402Sru  for (;;) {
1363114402Sru    if (*p != '\0')
1364114402Sru      return (unsigned char)*p++;
1365114402Sru    if (!done_newline) {
1366114402Sru      done_newline = 1;
1367114402Sru      return '\n';
1368114402Sru    }
1369114402Sru    double val;
1370114402Sru    if (!lookup_variable(var, &val)) {
1371114402Sru      lex_error("body of `for' terminated enclosing block");
1372114402Sru      return EOF;
1373114402Sru    }
1374114402Sru    if (by_is_multiplicative)
1375114402Sru      val *= by;
1376114402Sru    else
1377114402Sru      val += by;
1378114402Sru    define_variable(var, val);
1379151497Sru    if ((from <= to && val > to)
1380151497Sru	|| (from >= to && val < to)) {
1381114402Sru      p = 0;
1382114402Sru      return EOF;
1383114402Sru    }
1384114402Sru    p = body;
1385114402Sru    done_newline = 0;
1386114402Sru  }
1387114402Sru}
1388114402Sru
1389114402Sruint for_input::peek()
1390114402Sru{
1391114402Sru  if (p == 0)
1392114402Sru    return EOF;
1393114402Sru  if (*p != '\0')
1394114402Sru    return (unsigned char)*p;
1395114402Sru  if (!done_newline)
1396114402Sru    return '\n';
1397114402Sru  double val;
1398114402Sru  if (!lookup_variable(var, &val))
1399114402Sru    return EOF;
1400114402Sru  if (by_is_multiplicative) {
1401114402Sru    if (val * by > to)
1402114402Sru      return EOF;
1403114402Sru  }
1404114402Sru  else {
1405151497Sru    if ((from <= to && val + by > to)
1406151497Sru	|| (from >= to && val + by < to))
1407114402Sru      return EOF;
1408114402Sru  }
1409114402Sru  if (*body == '\0')
1410114402Sru    return EOF;
1411114402Sru  return (unsigned char)*body;
1412114402Sru}
1413114402Sru
1414114402Sruvoid do_for(char *var, double from, double to, int by_is_multiplicative,
1415114402Sru	    double by, char *body)
1416114402Sru{
1417114402Sru  define_variable(var, from);
1418151497Sru  if ((by_is_multiplicative && by <= 0)
1419151497Sru      || (by > 0 && from > to)
1420151497Sru      || (by < 0 && from < to))
1421151497Sru    return;
1422151497Sru  input_stack::push(new for_input(var, from, to,
1423151497Sru				  by_is_multiplicative, by, body));
1424114402Sru}
1425114402Sru
1426114402Sru
1427114402Sruvoid do_copy(const char *filename)
1428114402Sru{
1429114402Sru  errno = 0;
1430114402Sru  FILE *fp = fopen(filename, "r");
1431114402Sru  if (fp == 0) {
1432114402Sru    lex_error("can't open `%1': %2", filename, strerror(errno));
1433114402Sru    return;
1434114402Sru  }
1435114402Sru  input_stack::push(new file_input(fp, filename));
1436114402Sru}
1437114402Sru
1438114402Sruclass copy_thru_input : public input {
1439114402Sru  int done;
1440114402Sru  char *body;
1441114402Sru  char *until;
1442114402Sru  const char *p;
1443114402Sru  const char *ap;
1444114402Sru  int argv[9];
1445114402Sru  int argc;
1446114402Sru  string line;
1447114402Sru  int get_line();
1448114402Sru  virtual int inget() = 0;
1449114402Srupublic:
1450114402Sru  copy_thru_input(const char *b, const char *u);
1451114402Sru  ~copy_thru_input();
1452114402Sru  int get();
1453114402Sru  int peek();
1454114402Sru};
1455114402Sru
1456114402Sruclass copy_file_thru_input : public copy_thru_input {
1457114402Sru  input *in;
1458114402Srupublic:
1459114402Sru  copy_file_thru_input(input *, const char *b, const char *u);
1460114402Sru  ~copy_file_thru_input();
1461114402Sru  int inget();
1462114402Sru};
1463114402Sru
1464114402Srucopy_file_thru_input::copy_file_thru_input(input *i, const char *b,
1465114402Sru					   const char *u)
1466114402Sru: copy_thru_input(b, u), in(i)
1467114402Sru{
1468114402Sru}
1469114402Sru
1470114402Srucopy_file_thru_input::~copy_file_thru_input()
1471114402Sru{
1472114402Sru  delete in;
1473114402Sru}
1474114402Sru
1475114402Sruint copy_file_thru_input::inget()
1476114402Sru{
1477114402Sru  if (!in)
1478114402Sru    return EOF;
1479114402Sru  else
1480114402Sru    return in->get();
1481114402Sru}
1482114402Sru
1483114402Sruclass copy_rest_thru_input : public copy_thru_input {
1484114402Srupublic:
1485114402Sru  copy_rest_thru_input(const char *, const char *u);
1486114402Sru  int inget();
1487114402Sru};
1488114402Sru
1489114402Srucopy_rest_thru_input::copy_rest_thru_input(const char *b, const char *u)
1490114402Sru: copy_thru_input(b, u)
1491114402Sru{
1492114402Sru}
1493114402Sru
1494114402Sruint copy_rest_thru_input::inget()
1495114402Sru{
1496114402Sru  while (next != 0) {
1497114402Sru    int c = next->get();
1498114402Sru    if (c != EOF)
1499114402Sru      return c;
1500114402Sru    if (next->next == 0)
1501114402Sru      return EOF;
1502114402Sru    input *tem = next;
1503114402Sru    next = next->next;
1504114402Sru    delete tem;
1505114402Sru  }
1506114402Sru  return EOF;
1507114402Sru
1508114402Sru}
1509114402Sru
1510114402Srucopy_thru_input::copy_thru_input(const char *b, const char *u)
1511114402Sru: done(0)
1512114402Sru{
1513114402Sru  ap = 0;
1514114402Sru  body = process_body(b);
1515114402Sru  p = 0;
1516114402Sru  until = strsave(u);
1517114402Sru}
1518114402Sru
1519114402Sru
1520114402Srucopy_thru_input::~copy_thru_input()
1521114402Sru{
1522114402Sru  a_delete body;
1523114402Sru  a_delete until;
1524114402Sru}
1525114402Sru
1526114402Sruint copy_thru_input::get()
1527114402Sru{
1528114402Sru  if (ap) {
1529114402Sru    if (*ap != '\0')
1530114402Sru      return (unsigned char)*ap++;
1531114402Sru    ap = 0;
1532114402Sru  }
1533114402Sru  for (;;) {
1534114402Sru    if (p == 0) {
1535114402Sru      if (!get_line())
1536114402Sru	break;
1537114402Sru      p = body;
1538114402Sru    }
1539114402Sru    if (*p == '\0') {
1540114402Sru      p = 0;
1541114402Sru      return '\n';
1542114402Sru    }
1543114402Sru    while (*p >= ARG1 && *p <= ARG1 + 8) {
1544114402Sru      int i = *p++ - ARG1;
1545114402Sru      if (i < argc && line[argv[i]] != '\0') {
1546114402Sru	ap = line.contents() + argv[i];
1547114402Sru	return (unsigned char)*ap++;
1548114402Sru      }
1549114402Sru    }
1550114402Sru    if (*p != '\0')
1551114402Sru      return (unsigned char)*p++;
1552114402Sru  }
1553114402Sru  return EOF;
1554114402Sru}
1555114402Sru
1556114402Sruint copy_thru_input::peek()
1557114402Sru{
1558114402Sru  if (ap) {
1559114402Sru    if (*ap != '\0')
1560114402Sru      return (unsigned char)*ap;
1561114402Sru    ap = 0;
1562114402Sru  }
1563114402Sru  for (;;) {
1564114402Sru    if (p == 0) {
1565114402Sru      if (!get_line())
1566114402Sru	break;
1567114402Sru      p = body;
1568114402Sru    }
1569114402Sru    if (*p == '\0')
1570114402Sru      return '\n';
1571114402Sru    while (*p >= ARG1 && *p <= ARG1 + 8) {
1572114402Sru      int i = *p++ - ARG1;
1573114402Sru      if (i < argc && line[argv[i]] != '\0') {
1574114402Sru	ap = line.contents() + argv[i];
1575114402Sru	return (unsigned char)*ap;
1576114402Sru      }
1577114402Sru    }
1578114402Sru    if (*p != '\0')
1579114402Sru      return (unsigned char)*p;
1580114402Sru  }
1581114402Sru  return EOF;
1582114402Sru}
1583114402Sru
1584114402Sruint copy_thru_input::get_line()
1585114402Sru{
1586114402Sru  if (done)
1587114402Sru    return 0;
1588114402Sru  line.clear();
1589114402Sru  argc = 0;
1590114402Sru  int c = inget();
1591114402Sru  for (;;) {
1592114402Sru    while (c == ' ')
1593114402Sru      c = inget();
1594114402Sru    if (c == EOF || c == '\n')
1595114402Sru      break;
1596114402Sru    if (argc == 9) {
1597114402Sru      do {
1598114402Sru	c = inget();
1599114402Sru      } while (c != '\n' && c != EOF);
1600114402Sru      break;
1601114402Sru    }
1602114402Sru    argv[argc++] = line.length();
1603114402Sru    do {
1604114402Sru      line += char(c);
1605114402Sru      c = inget();
1606114402Sru    } while (c != ' ' && c != '\n');
1607114402Sru    line += '\0';
1608114402Sru  }
1609114402Sru  if (until != 0 && argc > 0 && strcmp(&line[argv[0]], until) == 0) {
1610114402Sru    done = 1;
1611114402Sru    return 0;
1612114402Sru  }
1613114402Sru  return argc > 0 || c == '\n';
1614114402Sru}
1615114402Sru
1616114402Sruclass simple_file_input : public input {
1617114402Sru  const char *filename;
1618114402Sru  int lineno;
1619114402Sru  FILE *fp;
1620114402Srupublic:
1621114402Sru  simple_file_input(FILE *, const char *);
1622114402Sru  ~simple_file_input();
1623114402Sru  int get();
1624114402Sru  int peek();
1625114402Sru  int get_location(const char **, int *);
1626114402Sru};
1627114402Sru
1628114402Srusimple_file_input::simple_file_input(FILE *p, const char *s)
1629114402Sru: filename(s), lineno(1), fp(p)
1630114402Sru{
1631114402Sru}
1632114402Sru
1633114402Srusimple_file_input::~simple_file_input()
1634114402Sru{
1635114402Sru  // don't delete the filename
1636114402Sru  fclose(fp);
1637114402Sru}
1638114402Sru
1639114402Sruint simple_file_input::get()
1640114402Sru{
1641114402Sru  int c = getc(fp);
1642114402Sru  while (invalid_input_char(c)) {
1643114402Sru    error("invalid input character code %1", c);
1644114402Sru    c = getc(fp);
1645114402Sru  }
1646114402Sru  if (c == '\n')
1647114402Sru    lineno++;
1648114402Sru  return c;
1649114402Sru}
1650114402Sru
1651114402Sruint simple_file_input::peek()
1652114402Sru{
1653114402Sru  int c = getc(fp);
1654114402Sru  while (invalid_input_char(c)) {
1655114402Sru    error("invalid input character code %1", c);
1656114402Sru    c = getc(fp);
1657114402Sru  }
1658114402Sru  if (c != EOF)
1659114402Sru    ungetc(c, fp);
1660114402Sru  return c;
1661114402Sru}
1662114402Sru
1663114402Sruint simple_file_input::get_location(const char **fnp, int *lnp)
1664114402Sru{
1665114402Sru  *fnp = filename;
1666114402Sru  *lnp = lineno;
1667114402Sru  return 1;
1668114402Sru}
1669114402Sru
1670114402Sru
1671114402Sruvoid copy_file_thru(const char *filename, const char *body, const char *until)
1672114402Sru{
1673114402Sru  errno = 0;
1674114402Sru  FILE *fp = fopen(filename, "r");
1675114402Sru  if (fp == 0) {
1676114402Sru    lex_error("can't open `%1': %2", filename, strerror(errno));
1677114402Sru    return;
1678114402Sru  }
1679114402Sru  input *in = new copy_file_thru_input(new simple_file_input(fp, filename),
1680114402Sru				       body, until);
1681114402Sru  input_stack::push(in);
1682114402Sru}
1683114402Sru
1684114402Sruvoid copy_rest_thru(const char *body, const char *until)
1685114402Sru{
1686114402Sru  input_stack::push(new copy_rest_thru_input(body, until));
1687114402Sru}
1688114402Sru
1689114402Sruvoid push_body(const char *s)
1690114402Sru{
1691114402Sru  input_stack::push(new char_input('\n'));
1692114402Sru  input_stack::push(new macro_input(s));
1693114402Sru}
1694114402Sru
1695114402Sruint delim_flag = 0;
1696114402Sru
1697114402Sruchar *get_thru_arg()
1698114402Sru{
1699114402Sru  int c = input_stack::peek_char();
1700114402Sru  while (c == ' ') {
1701114402Sru    input_stack::get_char();
1702114402Sru    c = input_stack::peek_char();
1703114402Sru  }
1704114402Sru  if (c != EOF && csalpha(c)) {
1705114402Sru    // looks like a macro
1706114402Sru    input_stack::get_char();
1707114402Sru    token_buffer = c;
1708114402Sru    for (;;) {
1709114402Sru      c = input_stack::peek_char();
1710114402Sru      if (c == EOF || (!csalnum(c) && c != '_'))
1711114402Sru	break;
1712114402Sru      input_stack::get_char();
1713114402Sru      token_buffer += char(c);
1714114402Sru    }
1715114402Sru    context_buffer = token_buffer;
1716114402Sru    token_buffer += '\0';
1717114402Sru    char *def = macro_table.lookup(token_buffer.contents());
1718114402Sru    if (def)
1719114402Sru      return strsave(def);
1720114402Sru    // I guess it wasn't a macro after all; so push the macro name back.
1721114402Sru    // -2 because we added a '\0'
1722114402Sru    for (int i = token_buffer.length() - 2; i >= 0; i--)
1723114402Sru      input_stack::push_back(token_buffer[i]);
1724114402Sru  }
1725114402Sru  if (get_delimited()) {
1726114402Sru    token_buffer += '\0';
1727114402Sru    return strsave(token_buffer.contents());
1728114402Sru  }
1729114402Sru  else
1730114402Sru    return 0;
1731114402Sru}
1732114402Sru
1733114402Sruint lookahead_token = -1;
1734114402Srustring old_context_buffer;
1735114402Sru
1736114402Sruvoid do_lookahead()
1737114402Sru{
1738114402Sru  if (lookahead_token == -1) {
1739114402Sru    old_context_buffer = context_buffer;
1740114402Sru    lookahead_token = get_token(1);
1741114402Sru  }
1742114402Sru}
1743114402Sru
1744114402Sruint yylex()
1745114402Sru{
1746114402Sru  if (delim_flag) {
1747114402Sru    assert(lookahead_token == -1);
1748114402Sru    if (delim_flag == 2) {
1749114402Sru      if ((yylval.str = get_thru_arg()) != 0)
1750114402Sru	return DELIMITED;
1751114402Sru      else
1752114402Sru	return 0;
1753114402Sru    }
1754114402Sru    else {
1755114402Sru      if (get_delimited()) {
1756114402Sru	token_buffer += '\0';
1757114402Sru	yylval.str = strsave(token_buffer.contents());
1758114402Sru	return DELIMITED;
1759114402Sru      }
1760114402Sru      else
1761114402Sru	return 0;
1762114402Sru    }
1763114402Sru  }
1764114402Sru  for (;;) {
1765114402Sru    int t;
1766114402Sru    if (lookahead_token >= 0) {
1767114402Sru      t = lookahead_token;
1768114402Sru      lookahead_token = -1;
1769114402Sru    }
1770114402Sru    else
1771114402Sru      t = get_token(1);
1772114402Sru    switch (t) {
1773114402Sru    case '\n':
1774114402Sru      return ';';
1775114402Sru    case EOF:
1776114402Sru      return 0;
1777114402Sru    case DEFINE:
1778114402Sru      do_define();
1779114402Sru      break;
1780114402Sru    case UNDEF:
1781114402Sru      do_undef();
1782114402Sru      break;
1783114402Sru    case ORDINAL:
1784114402Sru      yylval.n = token_int;
1785114402Sru      return t;
1786114402Sru    case NUMBER:
1787114402Sru      yylval.x = token_double;
1788114402Sru      return t;
1789114402Sru    case COMMAND_LINE:
1790114402Sru    case TEXT:
1791114402Sru      token_buffer += '\0';
1792114402Sru      if (!input_stack::get_location(&yylval.lstr.filename,
1793114402Sru				     &yylval.lstr.lineno)) {
1794114402Sru	yylval.lstr.filename = 0;
1795114402Sru	yylval.lstr.lineno = -1;
1796114402Sru      }
1797114402Sru      yylval.lstr.str = strsave(token_buffer.contents());
1798114402Sru      return t;
1799114402Sru    case LABEL:
1800114402Sru    case VARIABLE:
1801114402Sru      token_buffer += '\0';
1802114402Sru      yylval.str = strsave(token_buffer.contents());
1803114402Sru      return t;
1804114402Sru    case LEFT:
1805114402Sru      // change LEFT to LEFT_CORNER when followed by OF
1806114402Sru      old_context_buffer = context_buffer;
1807114402Sru      lookahead_token = get_token(1);
1808114402Sru      if (lookahead_token == OF)
1809114402Sru	return LEFT_CORNER;
1810114402Sru      else
1811114402Sru	return t;
1812114402Sru    case RIGHT:
1813114402Sru      // change RIGHT to RIGHT_CORNER when followed by OF
1814114402Sru      old_context_buffer = context_buffer;
1815114402Sru      lookahead_token = get_token(1);
1816114402Sru      if (lookahead_token == OF)
1817114402Sru	return RIGHT_CORNER;
1818114402Sru      else
1819114402Sru	return t;
1820114402Sru    case UPPER:
1821114402Sru      // recognise UPPER only before LEFT or RIGHT
1822114402Sru      old_context_buffer = context_buffer;
1823114402Sru      lookahead_token = get_token(1);
1824114402Sru      if (lookahead_token != LEFT && lookahead_token != RIGHT) {
1825114402Sru	yylval.str = strsave("upper");
1826114402Sru	return VARIABLE;
1827114402Sru      }
1828114402Sru      else
1829114402Sru	return t;
1830114402Sru    case LOWER:
1831114402Sru      // recognise LOWER only before LEFT or RIGHT
1832114402Sru      old_context_buffer = context_buffer;
1833114402Sru      lookahead_token = get_token(1);
1834114402Sru      if (lookahead_token != LEFT && lookahead_token != RIGHT) {
1835114402Sru	yylval.str = strsave("lower");
1836114402Sru	return VARIABLE;
1837114402Sru      }
1838114402Sru      else
1839114402Sru	return t;
1840114402Sru    case NORTH:
1841114402Sru      // recognise NORTH only before OF
1842114402Sru      old_context_buffer = context_buffer;
1843114402Sru      lookahead_token = get_token(1);
1844114402Sru      if (lookahead_token != OF) {
1845114402Sru	yylval.str = strsave("north");
1846114402Sru	return VARIABLE;
1847114402Sru      }
1848114402Sru      else
1849114402Sru	return t;
1850114402Sru    case SOUTH:
1851114402Sru      // recognise SOUTH only before OF
1852114402Sru      old_context_buffer = context_buffer;
1853114402Sru      lookahead_token = get_token(1);
1854114402Sru      if (lookahead_token != OF) {
1855114402Sru	yylval.str = strsave("south");
1856114402Sru	return VARIABLE;
1857114402Sru      }
1858114402Sru      else
1859114402Sru	return t;
1860114402Sru    case EAST:
1861114402Sru      // recognise EAST only before OF
1862114402Sru      old_context_buffer = context_buffer;
1863114402Sru      lookahead_token = get_token(1);
1864114402Sru      if (lookahead_token != OF) {
1865114402Sru	yylval.str = strsave("east");
1866114402Sru	return VARIABLE;
1867114402Sru      }
1868114402Sru      else
1869114402Sru	return t;
1870114402Sru    case WEST:
1871114402Sru      // recognise WEST only before OF
1872114402Sru      old_context_buffer = context_buffer;
1873114402Sru      lookahead_token = get_token(1);
1874114402Sru      if (lookahead_token != OF) {
1875114402Sru	yylval.str = strsave("west");
1876114402Sru	return VARIABLE;
1877114402Sru      }
1878114402Sru      else
1879114402Sru	return t;
1880114402Sru    case TOP:
1881114402Sru      // recognise TOP only before OF
1882114402Sru      old_context_buffer = context_buffer;
1883114402Sru      lookahead_token = get_token(1);
1884114402Sru      if (lookahead_token != OF) {
1885114402Sru	yylval.str = strsave("top");
1886114402Sru	return VARIABLE;
1887114402Sru      }
1888114402Sru      else
1889114402Sru	return t;
1890114402Sru    case BOTTOM:
1891114402Sru      // recognise BOTTOM only before OF
1892114402Sru      old_context_buffer = context_buffer;
1893114402Sru      lookahead_token = get_token(1);
1894114402Sru      if (lookahead_token != OF) {
1895114402Sru	yylval.str = strsave("bottom");
1896114402Sru	return VARIABLE;
1897114402Sru      }
1898114402Sru      else
1899114402Sru	return t;
1900114402Sru    case CENTER:
1901114402Sru      // recognise CENTER only before OF
1902114402Sru      old_context_buffer = context_buffer;
1903114402Sru      lookahead_token = get_token(1);
1904114402Sru      if (lookahead_token != OF) {
1905114402Sru	yylval.str = strsave("center");
1906114402Sru	return VARIABLE;
1907114402Sru      }
1908114402Sru      else
1909114402Sru	return t;
1910114402Sru    case START:
1911114402Sru      // recognise START only before OF
1912114402Sru      old_context_buffer = context_buffer;
1913114402Sru      lookahead_token = get_token(1);
1914114402Sru      if (lookahead_token != OF) {
1915114402Sru	yylval.str = strsave("start");
1916114402Sru	return VARIABLE;
1917114402Sru      }
1918114402Sru      else
1919114402Sru	return t;
1920114402Sru    case END:
1921114402Sru      // recognise END only before OF
1922114402Sru      old_context_buffer = context_buffer;
1923114402Sru      lookahead_token = get_token(1);
1924114402Sru      if (lookahead_token != OF) {
1925114402Sru	yylval.str = strsave("end");
1926114402Sru	return VARIABLE;
1927114402Sru      }
1928114402Sru      else
1929114402Sru	return t;
1930114402Sru    default:
1931114402Sru      return t;
1932114402Sru    }
1933114402Sru  }
1934114402Sru}
1935114402Sru
1936114402Sruvoid lex_error(const char *message,
1937114402Sru	       const errarg &arg1,
1938114402Sru	       const errarg &arg2,
1939114402Sru	       const errarg &arg3)
1940114402Sru{
1941114402Sru  const char *filename;
1942114402Sru  int lineno;
1943114402Sru  if (!input_stack::get_location(&filename, &lineno))
1944114402Sru    error(message, arg1, arg2, arg3);
1945114402Sru  else
1946114402Sru    error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
1947114402Sru}
1948114402Sru
1949114402Sruvoid lex_warning(const char *message,
1950114402Sru		 const errarg &arg1,
1951114402Sru		 const errarg &arg2,
1952114402Sru		 const errarg &arg3)
1953114402Sru{
1954114402Sru  const char *filename;
1955114402Sru  int lineno;
1956114402Sru  if (!input_stack::get_location(&filename, &lineno))
1957114402Sru    warning(message, arg1, arg2, arg3);
1958114402Sru  else
1959114402Sru    warning_with_file_and_line(filename, lineno, message, arg1, arg2, arg3);
1960114402Sru}
1961114402Sru
1962114402Sruvoid yyerror(const char *s)
1963114402Sru{
1964114402Sru  const char *filename;
1965114402Sru  int lineno;
1966114402Sru  const char *context = 0;
1967114402Sru  if (lookahead_token == -1) {
1968114402Sru    if (context_buffer.length() > 0) {
1969114402Sru      context_buffer += '\0';
1970114402Sru      context = context_buffer.contents();
1971114402Sru    }
1972114402Sru  }
1973114402Sru  else {
1974114402Sru    if (old_context_buffer.length() > 0) {
1975114402Sru      old_context_buffer += '\0';
1976114402Sru      context = old_context_buffer.contents();
1977114402Sru    }
1978114402Sru  }
1979114402Sru  if (!input_stack::get_location(&filename, &lineno)) {
1980114402Sru    if (context) {
1981114402Sru      if (context[0] == '\n' && context[1] == '\0')
1982114402Sru	error("%1 before newline", s);
1983114402Sru      else
1984114402Sru	error("%1 before `%2'", s, context);
1985114402Sru    }
1986114402Sru    else
1987114402Sru      error("%1 at end of picture", s);
1988114402Sru  }
1989114402Sru  else {
1990114402Sru    if (context) {
1991114402Sru      if (context[0] == '\n' && context[1] == '\0')
1992114402Sru	error_with_file_and_line(filename, lineno, "%1 before newline", s);
1993114402Sru      else
1994114402Sru	error_with_file_and_line(filename, lineno, "%1 before `%2'",
1995114402Sru				 s, context);
1996114402Sru    }
1997114402Sru    else
1998114402Sru      error_with_file_and_line(filename, lineno, "%1 at end of picture", s);
1999114402Sru  }
2000114402Sru}
2001114402Sru
2002