master xplshn/aruu / cmd / posix / awk / lex.c
  1/* ***************************************************************
  2 * copyright (c) lucent technologies 1997
  3 * all rights reserved
  4 *
  5 * permission to use, copy, modify, and distribute this software and
  6 * its documentation for any purpose and without fee is hereby
  7 * granted, provided that the above copyright notice appear in all
  8 * copies and that both that the copyright notice and this
  9 * permission notice and warranty disclaimer appear in supporting
 10 * documentation, and that the name lucent technologies or any of
 11 * its entities not be used in advertising or publicity pertaining
 12 * to distribution of the software without specific, written prior
 13 * permission
 14 *
 15 * LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 17 * IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
 18 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 20 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 21 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
 22 * THIS 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 == '_') { /* its 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 its a number, but could be */
141    char *rem;
142    /* read input until cant 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}