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