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