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}