Home | History | Annotate | Line # | Download | only in dist
      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