master xplshn/aruu / cmd / posix / awk / lex.c
  1/****************************************************************
  2Copyright (C) Lucent Technologies 1997
  3All Rights Reserved
  4
  5Permission to use, copy, modify, and distribute this software and
  6its documentation for any purpose and without fee is hereby
  7granted, provided that the above copyright notice appear in all
  8copies and that both that the copyright notice and this
  9permission notice and warranty disclaimer appear in supporting
 10documentation, and that the name Lucent Technologies or any of
 11its entities not be used in advertising or publicity pertaining
 12to distribution of the software without specific, written prior
 13permission.
 14
 15LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 16INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
 17IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
 18SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 19WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 20IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 21ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
 22THIS SOFTWARE.
 23****************************************************************/
 24
 25#include "awk.h"
 26#include "awkgram.tab.h"
 27#include <ctype.h>
 28#include <stdio.h>
 29#include <stdlib.h>
 30#include <string.h>
 31
 32extern YYSTYPE yylval;
 33extern bool    infunc;
 34
 35int lineno   = 1;
 36int bracecnt = 0;
 37int brackcnt = 0;
 38int parencnt = 0;
 39
 40typedef struct Keyword {
 41  const char *word;
 42  int         sub;
 43  int         type;
 44} Keyword;
 45
 46const Keyword keywords[] = {
 47    /* keep sorted: binary searched */
 48    {"BEGIN", XBEGIN, XBEGIN},
 49    {"END", XEND, XEND},
 50    {"NF", VARNF, VARNF},
 51    {"atan2", FATAN, BLTIN},
 52    {"break", BREAK, BREAK},
 53    {"close", CLOSE, CLOSE},
 54    {"continue", CONTINUE, CONTINUE},
 55    {"cos", FCOS, BLTIN},
 56    {"delete", DELETE, DELETE},
 57    {"do", DO, DO},
 58    {"else", ELSE, ELSE},
 59    {"exit", EXIT, EXIT},
 60    {"exp", FEXP, BLTIN},
 61    {"fflush", FFLUSH, BLTIN},
 62    {"for", FOR, FOR},
 63    {"func", FUNC, FUNC},
 64    {"function", FUNC, FUNC},
 65    {"getline", GETLINE, GETLINE},
 66    {"gsub", GSUB, GSUB},
 67    {"if", IF, IF},
 68    {"in", IN, IN},
 69    {"index", INDEX, INDEX},
 70    {"int", FINT, BLTIN},
 71    {"length", FLENGTH, BLTIN},
 72    {"log", FLOG, BLTIN},
 73    {"match", MATCHFCN, MATCHFCN},
 74    {"next", NEXT, NEXT},
 75    {"nextfile", NEXTFILE, NEXTFILE},
 76    {"print", PRINT, PRINT},
 77    {"printf", PRINTF, PRINTF},
 78    {"rand", FRAND, BLTIN},
 79    {"return", RETURN, RETURN},
 80    {"sin", FSIN, BLTIN},
 81    {"split", SPLIT, SPLIT},
 82    {"sprintf", SPRINTF, SPRINTF},
 83    {"sqrt", FSQRT, BLTIN},
 84    {"srand", FSRAND, BLTIN},
 85    {"sub", SUB, SUB},
 86    {"substr", SUBSTR, SUBSTR},
 87    {"system", FSYSTEM, BLTIN},
 88    {"tolower", FTOLOWER, BLTIN},
 89    {"toupper", FTOUPPER, BLTIN},
 90    {"while", WHILE, WHILE},
 91};
 92
 93#define RET(x)                                                                                     \
 94  {                                                                                                \
 95    if (dbg)                                                                                       \
 96      printf("lex %s\n", tokname(x));                                                              \
 97    return (x);                                                                                    \
 98  }
 99
100static int
101peek(void)
102{
103  int c = input();
104  unput(c);
105  return c;
106}
107
108static int
109gettok(char **pbuf, int *psz) /* get next input token */
110{
111  int   c, retc;
112  char *buf = *pbuf;
113  int   sz  = *psz;
114  char *bp  = buf;
115
116  c = input();
117  if (c == 0)
118    return 0;
119  buf[0] = c;
120  buf[1] = 0;
121  if (!isalnum(c) && c != '.' && c != '_')
122    return c;
123
124  *bp++ = c;
125  if (isalpha(c) || c == '_') { /* it's a varname */
126    for (; (c = input()) != 0;) {
127      if (bp - buf >= sz)
128        if (!adjbuf(&buf, &sz, bp - buf + 2, 100, &bp, "gettok"))
129          FATAL("out of space for name %.10s...", buf);
130      if (isalnum(c) || c == '_')
131        *bp++ = c;
132      else {
133        *bp = 0;
134        unput(c);
135        break;
136      }
137    }
138    *bp  = 0;
139    retc = 'a'; /* alphanumeric */
140  } else {      /* maybe it's a number, but could be . */
141    char *rem;
142    /* read input until can't be a number */
143    for (; (c = input()) != 0;) {
144      if (bp - buf >= sz)
145        if (!adjbuf(&buf, &sz, bp - buf + 2, 100, &bp, "gettok"))
146          FATAL(
147              "out of space for number "
148              "%.10s...",
149              buf
150          );
151      if (isdigit(c) || c == 'e' || c == 'E' || c == '.' || c == '+' || c == '-')
152        *bp++ = c;
153      else {
154        unput(c);
155        break;
156      }
157    }
158    *bp = 0;
159    strtod(buf, &rem);         /* parse the number */
160    if (rem == buf) {          /* it wasn't a valid number at all */
161      buf[1] = 0;              /* return one character as token */
162      retc   = (uschar)buf[0]; /* character is its own type */
163      unputstr(rem + 1);       /* put rest back for later */
164    } else {                   /* some prefix was a number */
165      unputstr(rem);           /* put rest back for later */
166      rem[0] = 0;              /* truncate buf after number part */
167      retc   = '0';            /* type is number */
168    }
169  }
170  *pbuf = buf;
171  *psz  = sz;
172  return retc;
173}
174
175int  word(char *);
176int  string(void);
177int  regexpr(void);
178bool sc  = false; /* true => return a } right now */
179bool reg = false; /* true => return a REGEXPR now */
180
181int
182yylex(void)
183{
184  int          c;
185  static char *buf     = NULL;
186  static int   bufsize = 5; /* BUG: setting this small causes core dump! */
187
188  if (buf == NULL && (buf = (char *)malloc(bufsize)) == NULL)
189    FATAL("out of space in yylex");
190  if (sc) {
191    sc = false;
192    RET('}');
193  }
194  if (reg) {
195    reg = false;
196    return regexpr();
197  }
198  for (;;) {
199    c = gettok(&buf, &bufsize);
200    if (c == 0)
201      return 0;
202    if (isalpha(c) || c == '_')
203      return word(buf);
204    if (isdigit(c)) {
205      char  *cp = tostring(buf);
206      double result;
207
208      if (is_number(cp, &result))
209        yylval.cp = setsymtab(buf, cp, result, CON | NUM, symtab);
210      else
211        yylval.cp = setsymtab(buf, cp, 0.0, STR, symtab);
212      free(cp);
213      /* should this also have STR set? */
214      RET(NUMBER);
215    }
216
217    yylval.i = c;
218    switch (c) {
219      case '\n': /* {EOL} */
220        lineno++;
221        RET(NL);
222      case '\r': /* assume \n is coming */
223      case ' ':  /* {WS}+ */
224      case '\t':
225        break;
226      case '#': /* #.* strip comments */
227        while ((c = input()) != '\n' && c != 0)
228          ;
229        unput(c);
230        break;
231      case ';':
232        RET(';');
233      case '\\':
234        if (peek() == '\n') {
235          input();
236          lineno++;
237        } else if (peek() == '\r') {
238          input();
239          input(); /* \n */
240          lineno++;
241        } else {
242          RET(c);
243        }
244        break;
245      case '&':
246        if (peek() == '&') {
247          input();
248          RET(AND);
249        } else
250          RET('&');
251      case '|':
252        if (peek() == '|') {
253          input();
254          RET(BOR);
255        } else
256          RET('|');
257      case '!':
258        if (peek() == '=') {
259          input();
260          yylval.i = NE;
261          RET(NE);
262        } else if (peek() == '~') {
263          input();
264          yylval.i = NOTMATCH;
265          RET(MATCHOP);
266        } else
267          RET(NOT);
268      case '~':
269        yylval.i = MATCH;
270        RET(MATCHOP);
271      case '<':
272        if (peek() == '=') {
273          input();
274          yylval.i = LE;
275          RET(LE);
276        } else {
277          yylval.i = LT;
278          RET(LT);
279        }
280      case '=':
281        if (peek() == '=') {
282          input();
283          yylval.i = EQ;
284          RET(EQ);
285        } else {
286          yylval.i = ASSIGN;
287          RET(ASGNOP);
288        }
289      case '>':
290        if (peek() == '=') {
291          input();
292          yylval.i = GE;
293          RET(GE);
294        } else if (peek() == '>') {
295          input();
296          yylval.i = APPEND;
297          RET(APPEND);
298        } else {
299          yylval.i = GT;
300          RET(GT);
301        }
302      case '+':
303        if (peek() == '+') {
304          input();
305          yylval.i = INCR;
306          RET(INCR);
307        } else if (peek() == '=') {
308          input();
309          yylval.i = ADDEQ;
310          RET(ASGNOP);
311        } else
312          RET('+');
313      case '-':
314        if (peek() == '-') {
315          input();
316          yylval.i = DECR;
317          RET(DECR);
318        } else if (peek() == '=') {
319          input();
320          yylval.i = SUBEQ;
321          RET(ASGNOP);
322        } else
323          RET('-');
324      case '*':
325        if (peek() == '=') { /* *= */
326          input();
327          yylval.i = MULTEQ;
328          RET(ASGNOP);
329        } else if (peek() == '*') { /* ** or **= */
330          input();                  /* eat 2nd * */
331          if (peek() == '=') {
332            input();
333            yylval.i = POWEQ;
334            RET(ASGNOP);
335          } else {
336            RET(POWER);
337          }
338        } else
339          RET('*');
340      case '/':
341        RET('/');
342      case '%':
343        if (peek() == '=') {
344          input();
345          yylval.i = MODEQ;
346          RET(ASGNOP);
347        } else
348          RET('%');
349      case '^':
350        if (peek() == '=') {
351          input();
352          yylval.i = POWEQ;
353          RET(ASGNOP);
354        } else
355          RET(POWER);
356
357      case '$':
358        /* BUG: awkward, if not wrong */
359        c = gettok(&buf, &bufsize);
360        if (isalpha(c)) {
361          if (strcmp(buf, "NF") == 0) { /* very special */
362            unputstr("(NF)");
363            RET(INDIRECT);
364          }
365          c = peek();
366          if (c == '(' || c == '[' || (infunc && isarg(buf) >= 0)) {
367            unputstr(buf);
368            RET(INDIRECT);
369          }
370          yylval.cp = setsymtab(buf, "", 0.0, STR | NUM, symtab);
371          RET(IVAR);
372        } else if (c == 0) { /*  */
373          SYNTAX("unexpected end of input after $");
374          RET(';');
375        } else {
376          unputstr(buf);
377          RET(INDIRECT);
378        }
379
380      case '}':
381        if (--bracecnt < 0)
382          SYNTAX("extra }");
383        sc = true;
384        RET(';');
385      case ']':
386        if (--brackcnt < 0)
387          SYNTAX("extra ]");
388        RET(']');
389      case ')':
390        if (--parencnt < 0)
391          SYNTAX("extra )");
392        RET(')');
393      case '{':
394        bracecnt++;
395        RET('{');
396      case '[':
397        brackcnt++;
398        RET('[');
399      case '(':
400        parencnt++;
401        RET('(');
402
403      case '"':
404        return string(); /* BUG: should be like tran.c ? */
405
406      default:
407        RET(c);
408    }
409  }
410}
411
412extern int runetochar(char *str, int c);
413
414int
415string(void)
416{
417  int          c, n;
418  char        *s, *bp;
419  static char *buf   = NULL;
420  static int   bufsz = 500;
421
422  if (buf == NULL && (buf = (char *)malloc(bufsz)) == NULL)
423    FATAL("out of space for strings");
424  for (bp = buf; (c = input()) != '"';) {
425    if (!adjbuf(&buf, &bufsz, bp - buf + 2, 500, &bp, "string"))
426      FATAL("out of space for string %.10s...", buf);
427    switch (c) {
428      case '\n':
429      case '\r':
430      case 0:
431        *bp = '\0';
432        SYNTAX("non-terminated string %.10s...", buf);
433        if (c == 0) /* hopeless */
434          FATAL("giving up");
435        lineno++;
436        break;
437      case '\\':
438        c = input();
439        switch (c) {
440          case '\n':
441            break;
442          case '"':
443            *bp++ = '"';
444            break;
445          case 'n':
446            *bp++ = '\n';
447            break;
448          case 't':
449            *bp++ = '\t';
450            break;
451          case 'f':
452            *bp++ = '\f';
453            break;
454          case 'r':
455            *bp++ = '\r';
456            break;
457          case 'b':
458            *bp++ = '\b';
459            break;
460          case 'v':
461            *bp++ = '\v';
462            break;
463          case 'a':
464            *bp++ = '\a';
465            break;
466          case '\\':
467            *bp++ = '\\';
468            break;
469
470          case '0':
471          case '1':
472          case '2': /* octal: \d \dd \ddd */
473          case '3':
474          case '4':
475          case '5':
476          case '6':
477          case '7':
478            n = c - '0';
479            if ((c = peek()) >= '0' && c < '8') {
480              n = 8 * n + input() - '0';
481              if ((c = peek()) >= '0' && c < '8')
482                n = 8 * n + input() - '0';
483            }
484            *bp++ = n;
485            break;
486
487          case 'x': /* hex  \x0-9a-fA-F (exactly two) */
488          {
489            int i;
490
491            if (!isxdigit(peek())) {
492              unput(c);
493              break;
494            }
495            n = 0;
496            for (i = 0; i < 2; i++) {
497              c = input();
498              if (c == 0)
499                break;
500              if (isxdigit(c)) {
501                c = tolower(c);
502                n *= 16;
503                if (isdigit(c))
504                  n += (c - '0');
505                else
506                  n += 10 + (c - 'a');
507              } else {
508                unput(c);
509                break;
510              }
511            }
512            if (i)
513              *bp++ = n;
514            break;
515          }
516
517          case 'u': /* utf  \u0-9a-fA-F (1..8) */
518          {
519            int i;
520
521            n = 0;
522            for (i = 0; i < 8; i++) {
523              c = input();
524              if (!isxdigit(c) || c == 0)
525                break;
526              c = tolower(c);
527              n *= 16;
528              if (isdigit(c))
529                n += (c - '0');
530              else
531                n += 10 + (c - 'a');
532            }
533            unput(c);
534            bp += runetochar(bp, n);
535            break;
536          }
537
538          default:
539            *bp++ = c;
540            break;
541        }
542        break;
543      default:
544        *bp++ = c;
545        break;
546    }
547  }
548  *bp       = 0;
549  s         = tostring(buf);
550  *bp++     = ' ';
551  *bp++     = '\0';
552  yylval.cp = setsymtab(buf, s, 0.0, CON | STR | DONTFREE, symtab);
553  free(s);
554  RET(STRING);
555}
556
557static int
558binsearch(char *w, const Keyword *kp, int n)
559{
560  int cond, low, mid, high;
561
562  low  = 0;
563  high = n - 1;
564  while (low <= high) {
565    mid = (low + high) / 2;
566    if ((cond = strcmp(w, kp[mid].word)) < 0)
567      high = mid - 1;
568    else if (cond > 0)
569      low = mid + 1;
570    else
571      return mid;
572  }
573  return -1;
574}
575
576int
577word(char *w)
578{
579  const Keyword *kp;
580  int            c, n;
581
582  n = binsearch(w, keywords, sizeof(keywords) / sizeof(keywords[0]));
583  if (n != -1) { /* found in table */
584    kp       = keywords + n;
585    yylval.i = kp->sub;
586    switch (kp->type) { /* special handling */
587      case BLTIN:
588        if (kp->sub == FSYSTEM && safe)
589          SYNTAX("system is unsafe");
590        RET(kp->type);
591      case FUNC:
592        if (infunc)
593          SYNTAX("illegal nested function");
594        RET(kp->type);
595      case RETURN:
596        if (!infunc)
597          SYNTAX("return not in function");
598        RET(kp->type);
599      case VARNF:
600        yylval.cp = setsymtab("NF", "", 0.0, NUM, symtab);
601        RET(VARNF);
602      default:
603        RET(kp->type);
604    }
605  }
606  c = peek(); /* look for '(' */
607  if (c != '(' && infunc && (n = isarg(w)) >= 0) {
608    yylval.i = n;
609    RET(ARG);
610  } else {
611    yylval.cp = setsymtab(w, "", 0.0, STR | NUM | DONTFREE, symtab);
612    if (c == '(') {
613      RET(CALL);
614    } else {
615      RET(VAR);
616    }
617  }
618}
619
620void
621startreg(void) /* next call to yylex will return a regular expression */
622{
623  reg = true;
624}
625
626int
627regexpr(void)
628{
629  int          c;
630  static char *buf   = NULL;
631  static int   bufsz = 500;
632  char        *bp;
633
634  if (buf == NULL && (buf = (char *)malloc(bufsz)) == NULL)
635    FATAL("out of space for reg expr");
636  bp = buf;
637  for (; (c = input()) != '/' && c != 0;) {
638    if (!adjbuf(&buf, &bufsz, bp - buf + 3, 500, &bp, "regexpr"))
639      FATAL("out of space for reg expr %.10s...", buf);
640    if (c == '\n') {
641      *bp = '\0';
642      SYNTAX("newline in regular expression %.10s...", buf);
643      unput('\n');
644      break;
645    } else if (c == '\\') {
646      *bp++ = '\\';
647      *bp++ = input();
648    } else {
649      *bp++ = c;
650    }
651  }
652  *bp = 0;
653  if (c == 0)
654    SYNTAX("non-terminated regular expression %.10s...", buf);
655  yylval.s = tostring(buf);
656  unput('/');
657  RET(REGEXPR);
658}
659
660/* low-level lexical stuff, sort of inherited from lex */
661
662char  ebuf[300];
663char *ep = ebuf;
664char  yysbuf[100]; /* pushback buffer */
665char *yysptr = yysbuf;
666FILE *yyin   = NULL;
667
668int
669input(void) /* get next lexical input character */
670{
671  int          c;
672  extern char *lexprog;
673
674  if (yysptr > yysbuf)
675    c = (uschar) * --yysptr;
676  else if (lexprog != NULL) { /* awk '...' */
677    if ((c = (uschar)*lexprog) != 0)
678      lexprog++;
679  } else /* awk -f ... */
680    c = pgetc();
681  if (c == EOF)
682    c = 0;
683  if (ep >= ebuf + sizeof ebuf)
684    ep = ebuf;
685  *ep = c;
686  if (c != 0) {
687    ep++;
688  }
689  return (c);
690}
691
692void
693unput(int c) /* put lexical character back on input */
694{
695  if (yysptr >= yysbuf + sizeof(yysbuf))
696    FATAL("pushed back too much: %.20s...", yysbuf);
697  *yysptr++ = c;
698  if (--ep < ebuf)
699    ep = ebuf + sizeof(ebuf) - 1;
700}
701
702void
703unputstr(const char *s) /* put a string back on input */
704{
705  int i;
706
707  for (i = strlen(s) - 1; i >= 0; i--)
708    unput(s[i]);
709}