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