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}