#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "awk.h"
#include "awkgram.tab.h"
extern YYSTYPE yylval;
extern bool infunc;
int lineno = 1;
int bracecnt = 0;
int brackcnt = 0;
int parencnt = 0;
typedef struct Keyword {
const char *word;
int sub;
int type;
} Keyword;
const Keyword keywords[] = {
{ "BEGIN", XBEGIN, XBEGIN },
{ "END", XEND, XEND },
{ "NF", VARNF, VARNF },
{ "and", FAND, BLTIN },
{ "atan2", FATAN, BLTIN },
{ "break", BREAK, BREAK },
{ "close", CLOSE, CLOSE },
{ "compl", FCOMPL, BLTIN },
{ "continue", CONTINUE, CONTINUE },
{ "cos", FCOS, BLTIN },
{ "delete", DELETE, DELETE },
{ "do", DO, DO },
{ "else", ELSE, ELSE },
{ "exit", EXIT, EXIT },
{ "exp", FEXP, BLTIN },
{ "fflush", FFLUSH, BLTIN },
{ "for", FOR, FOR },
{ "func", FUNC, FUNC },
{ "function", FUNC, FUNC },
{ "gensub", GENSUB, GENSUB },
{ "getline", GETLINE, GETLINE },
{ "gsub", GSUB, GSUB },
{ "if", IF, IF },
{ "in", IN, IN },
{ "index", INDEX, INDEX },
{ "int", FINT, BLTIN },
{ "length", FLENGTH, BLTIN },
{ "log", FLOG, BLTIN },
{ "lshift", FLSHIFT, BLTIN },
{ "match", MATCHFCN, MATCHFCN },
{ "mktime", FMKTIME, BLTIN },
{ "next", NEXT, NEXT },
{ "nextfile", NEXTFILE, NEXTFILE },
{ "or", FFOR, BLTIN },
{ "print", PRINT, PRINT },
{ "printf", PRINTF, PRINTF },
{ "rand", FRAND, BLTIN },
{ "return", RETURN, RETURN },
{ "rshift", FRSHIFT, BLTIN },
{ "sin", FSIN, BLTIN },
{ "split", SPLIT, SPLIT },
{ "sprintf", SPRINTF, SPRINTF },
{ "sqrt", FSQRT, BLTIN },
{ "srand", FSRAND, BLTIN },
{ "strftime", FSTRFTIME, BLTIN },
{ "sub", SUB, SUB },
{ "substr", SUBSTR, SUBSTR },
{ "system", FSYSTEM, BLTIN },
{ "systime", FSYSTIME, BLTIN },
{ "tolower", FTOLOWER, BLTIN },
{ "toupper", FTOUPPER, BLTIN },
{ "while", WHILE, WHILE },
{ "xor", FXOR, BLTIN },
};
#define RET(x) { if(dbg)printf("lex %s\n", tokname(x)); return(x); }
static int peek(void)
{
int c = input();
unput(c);
return c;
}
static int gettok(char **pbuf, int *psz)
{
int c, retc;
char *buf = *pbuf;
int sz = *psz;
char *bp = buf;
c = input();
if (c == 0)
return 0;
buf[0] = c;
buf[1] = 0;
if (!isalnum(c) && c != '.' && c != '_')
return c;
*bp++ = c;
if (isalpha(c) || c == '_') {
for ( ; (c = input()) != 0; ) {
if (bp-buf >= sz)
if (!adjbuf(&buf, &sz, bp-buf+2, 100, &bp, "gettok"))
FATAL( "out of space for name %.10s...", buf );
if (isalnum(c) || c == '_')
*bp++ = c;
else {
*bp = 0;
unput(c);
break;
}
}
*bp = 0;
retc = 'a';
} else {
char *rem;
for ( ; (c = input()) != 0; ) {
if (bp-buf >= sz)
if (!adjbuf(&buf, &sz, bp-buf+2, 100, &bp, "gettok"))
FATAL( "out of space for number %.10s...", buf );
if (isdigit(c) || c == 'e' || c == 'E'
|| c == '.' || c == '+' || c == '-')
*bp++ = c;
else {
unput(c);
break;
}
}
*bp = 0;
strtod(buf, &rem);
if (rem == buf) {
buf[1] = 0;
retc = (uschar)buf[0];
unputstr(rem+1);
} else {
unputstr(rem);
rem[0] = 0;
retc = '0';
}
}
*pbuf = buf;
*psz = sz;
return retc;
}
int word(char *);
int string(void);
int regexpr(void);
bool sc = false;
bool reg = false;
int yylex(void)
{
int c;
static char *buf = NULL;
static int bufsize = 5;
if (buf == NULL && (buf = (char *) malloc(bufsize)) == NULL)
FATAL( "out of space in yylex" );
if (sc) {
sc = false;
RET('}');
}
if (reg) {
reg = false;
return regexpr();
}
for (;;) {
c = gettok(&buf, &bufsize);
if (c == 0)
return 0;
if (isalpha(c) || c == '_')
return word(buf);
if (isdigit(c)) {
char *cp = tostring(buf);
double result;
if (is_number(cp, & result))
yylval.cp = setsymtab(buf, cp, result, CON|NUM, symtab);
else
yylval.cp = setsymtab(buf, cp, 0.0, STR, symtab);
free(cp);
RET(NUMBER);
}
yylval.i = c;
switch (c) {
case '\n':
lineno++;
RET(NL);
case '\r':
case ' ':
case '\t':
break;
case '#':
while ((c = input()) != '\n' && c != 0)
;
unput(c);
break;
case ';':
RET(';');
case '\\':
if (peek() == '\n') {
input();
lineno++;
} else if (peek() == '\r') {
input(); input();
lineno++;
} else {
RET(c);
}
break;
case '&':
if (peek() == '&') {
input(); RET(AND);
} else
RET('&');
case '|':
if (peek() == '|') {
input(); RET(BOR);
} else
RET('|');
case '!':
if (peek() == '=') {
input(); yylval.i = NE; RET(NE);
} else if (peek() == '~') {
input(); yylval.i = NOTMATCH; RET(MATCHOP);
} else
RET(NOT);
case '~':
yylval.i = MATCH;
RET(MATCHOP);
case '<':
if (peek() == '=') {
input(); yylval.i = LE; RET(LE);
} else {
yylval.i = LT; RET(LT);
}
case '=':
if (peek() == '=') {
input(); yylval.i = EQ; RET(EQ);
} else {
yylval.i = ASSIGN; RET(ASGNOP);
}
case '>':
if (peek() == '=') {
input(); yylval.i = GE; RET(GE);
} else if (peek() == '>') {
input(); yylval.i = APPEND; RET(APPEND);
} else {
yylval.i = GT; RET(GT);
}
case '+':
if (peek() == '+') {
input(); yylval.i = INCR; RET(INCR);
} else if (peek() == '=') {
input(); yylval.i = ADDEQ; RET(ASGNOP);
} else
RET('+');
case '-':
if (peek() == '-') {
input(); yylval.i = DECR; RET(DECR);
} else if (peek() == '=') {
input(); yylval.i = SUBEQ; RET(ASGNOP);
} else
RET('-');
case '*':
if (peek() == '=') {
input(); yylval.i = MULTEQ; RET(ASGNOP);
} else if (peek() == '*') {
input();
if (peek() == '=') {
input(); yylval.i = POWEQ; RET(ASGNOP);
} else {
RET(POWER);
}
} else
RET('*');
case '/':
RET('/');
case '%':
if (peek() == '=') {
input(); yylval.i = MODEQ; RET(ASGNOP);
} else
RET('%');
case '^':
if (peek() == '=') {
input(); yylval.i = POWEQ; RET(ASGNOP);
} else
RET(POWER);
case '$':
c = gettok(&buf, &bufsize);
if (isalpha(c)) {
if (strcmp(buf, "NF") == 0) {
unputstr("(NF)");
RET(INDIRECT);
}
c = peek();
if (c == '(' || c == '[' || (infunc && isarg(buf) >= 0)) {
unputstr(buf);
RET(INDIRECT);
}
yylval.cp = setsymtab(buf, "", 0.0, STR|NUM, symtab);
RET(IVAR);
} else if (c == 0) {
SYNTAX( "unexpected end of input after $" );
RET(';');
} else {
unputstr(buf);
RET(INDIRECT);
}
case '}':
if (--bracecnt < 0)
SYNTAX( "extra }" );
sc = true;
RET(';');
case ']':
if (--brackcnt < 0)
SYNTAX( "extra ]" );
RET(']');
case ')':
if (--parencnt < 0)
SYNTAX( "extra )" );
RET(')');
case '{':
bracecnt++;
RET('{');
case '[':
brackcnt++;
RET('[');
case '(':
parencnt++;
RET('(');
case '"':
return string();
default:
RET(c);
}
}
}
int string(void)
{
int c, n;
char *s, *bp;
static char *buf = NULL;
static int bufsz = 500;
if (buf == NULL && (buf = (char *) malloc(bufsz)) == NULL)
FATAL("out of space for strings");
for (bp = buf; (c = input()) != '"'; ) {
if (!adjbuf(&buf, &bufsz, bp-buf+2, 500, &bp, "string"))
FATAL("out of space for string %.10s...", buf);
switch (c) {
case '\n':
case '\r':
case 0:
*bp = '\0';
SYNTAX( "non-terminated string %.10s...", buf );
if (c == 0)
FATAL( "giving up" );
lineno++;
break;
case '\\':
c = input();
switch (c) {
case '\n': break;
case '"': *bp++ = '"'; break;
case 'n': *bp++ = '\n'; break;
case 't': *bp++ = '\t'; break;
case 'f': *bp++ = '\f'; break;
case 'r': *bp++ = '\r'; break;
case 'b': *bp++ = '\b'; break;
case 'v': *bp++ = '\v'; break;
case 'a': *bp++ = '\a'; break;
case '\\': *bp++ = '\\'; break;
case '0': case '1': case '2':
case '3': case '4': case '5': case '6': case '7':
n = c - '0';
if ((c = peek()) >= '0' && c < '8') {
n = 8 * n + input() - '0';
if ((c = peek()) >= '0' && c < '8')
n = 8 * n + input() - '0';
}
*bp++ = n;
break;
case 'x':
{
int i;
if (!isxdigit(peek())) {
unput(c);
break;
}
n = 0;
for (i = 0; i < 2; i++) {
c = input();
if (c == 0)
break;
if (isxdigit(c)) {
c = tolower(c);
n *= 16;
if (isdigit(c))
n += (c - '0');
else
n += 10 + (c - 'a');
} else {
unput(c);
break;
}
}
if (i)
*bp++ = n;
break;
}
case 'u':
{
int i;
n = 0;
for (i = 0; i < 8; i++) {
c = input();
if (!isxdigit(c) || c == 0)
break;
c = tolower(c);
n *= 16;
if (isdigit(c))
n += (c - '0');
else
n += 10 + (c - 'a');
}
unput(c);
bp += runetochar(bp, n);
break;
}
default:
*bp++ = c;
break;
}
break;
default:
*bp++ = c;
break;
}
}
*bp = 0;
s = tostring(buf);
*bp++ = ' '; *bp++ = '\0';
yylval.cp = setsymtab(buf, s, 0.0, CON|STR|DONTFREE, symtab);
free(s);
RET(STRING);
}
static int binsearch(char *w, const Keyword *kp, int n)
{
int cond, low, mid, high;
low = 0;
high = n - 1;
while (low <= high) {
mid = (low + high) / 2;
if ((cond = strcmp(w, kp[mid].word)) < 0)
high = mid - 1;
else if (cond > 0)
low = mid + 1;
else
return mid;
}
return -1;
}
int word(char *w)
{
const Keyword *kp;
int c, n;
n = binsearch(w, keywords, sizeof(keywords)/sizeof(keywords[0]));
if (n != -1) {
kp = keywords + n;
yylval.i = kp->sub;
switch (kp->type) {
case BLTIN:
if (kp->sub == FSYSTEM && safe)
SYNTAX( "system is unsafe" );
RET(kp->type);
case FUNC:
if (infunc)
SYNTAX( "illegal nested function" );
RET(kp->type);
case RETURN:
if (!infunc)
SYNTAX( "return not in function" );
RET(kp->type);
case VARNF:
yylval.cp = setsymtab("NF", "", 0.0, NUM, symtab);
RET(VARNF);
default:
RET(kp->type);
}
}
c = peek();
if (c != '(' && infunc && (n=isarg(w)) >= 0) {
yylval.i = n;
RET(ARG);
} else {
yylval.cp = setsymtab(w, "", 0.0, STR|NUM|DONTFREE, symtab);
if (c == '(') {
RET(CALL);
} else {
RET(VAR);
}
}
}
void startreg(void)
{
reg = true;
}
int regexpr(void)
{
int c, openclass = 0;
static char *buf = NULL;
static int bufsz = 500;
char *bp, *cstart;
if (buf == NULL && (buf = (char *) malloc(bufsz)) == NULL)
FATAL("out of space for reg expr");
bp = buf;
for ( ; ((c = input()) != '/' || openclass > 0) && c != 0; ) {
if (!adjbuf(&buf, &bufsz, bp-buf+3, 500, &bp, "regexpr"))
FATAL("out of space for reg expr %.10s...", buf);
if (c == '\n') {
*bp = '\0';
SYNTAX( "newline in regular expression %.10s...", buf );
unput('\n');
break;
} else if (c == '\\') {
*bp++ = '\\';
*bp++ = input();
} else {
if (!do_posix) {
if (c == '[') {
int nextc = peek();
if (openclass == 0 || nextc == ':' ||
nextc == '.' || nextc == '=') {
if (++openclass == 1)
cstart = bp;
}
} else if (c == ']' && openclass > 0) {
if (cstart != bp - 1 &&
(cstart != bp - 2 || bp[-1] != '^'))
openclass--;
}
}
*bp++ = c;
}
}
*bp = 0;
if (c == 0)
SYNTAX("non-terminated regular expression %.10s...", buf);
yylval.s = tostring(buf);
unput('/');
RET(REGEXPR);
}
char ebuf[300];
char *ep = ebuf;
char yysbuf[100];
char *yysptr = yysbuf;
FILE *yyin = NULL;
int input(void)
{
int c;
extern char *lexprog;
if (yysptr > yysbuf)
c = (uschar)*--yysptr;
else if (lexprog != NULL) {
if ((c = (uschar)*lexprog) != 0)
lexprog++;
} else
c = pgetc();
if (c == EOF)
c = 0;
if (ep >= ebuf + sizeof ebuf)
ep = ebuf;
*ep = c;
if (c != 0) {
ep++;
}
return (c);
}
void unput(int c)
{
if (yysptr >= yysbuf + sizeof(yysbuf))
FATAL("pushed back too much: %.20s...", yysbuf);
*yysptr++ = c;
if (--ep < ebuf)
ep = ebuf + sizeof(ebuf) - 1;
}
void unputstr(const char *s)
{
int i;
for (i = strlen(s)-1; i >= 0; i--)
unput(s[i]);
}