#define DEBUG
#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include "awk.h"
#define FULLTAB 2
#define GROWTAB 4
Array *symtab;
char **FS;
char **RS;
char **OFS;
char **ORS;
char **OFMT;
char **CONVFMT;
Awkfloat *NF;
Awkfloat *NR;
Awkfloat *FNR;
char **FILENAME;
Awkfloat *ARGC;
char **SUBSEP;
Awkfloat *RSTART;
Awkfloat *RLENGTH;
Cell *fsloc;
Cell *nrloc;
Cell *nfloc;
Cell *fnrloc;
Cell *ofsloc;
Cell *orsloc;
Cell *rsloc;
Cell *ARGVcell;
Cell *rstartloc;
Cell *rlengthloc;
Cell *subseploc;
Cell *symtabloc;
Cell *nullloc;
Node *nullnode;
Cell *literal0;
extern Cell **fldtab;
void syminit(void)
{
literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab);
nullloc = setsymtab("$zero&null", "", 0.0, NUM|STR|CON|DONTFREE, symtab);
nullnode = celltonode(nullloc, CCON);
fsloc = setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab);
FS = &fsloc->sval;
rsloc = setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab);
RS = &rsloc->sval;
ofsloc = setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab);
OFS = &ofsloc->sval;
orsloc = setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab);
ORS = &orsloc->sval;
OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
FILENAME = &setsymtab("FILENAME", "", 0.0, STR|DONTFREE, symtab)->sval;
nfloc = setsymtab("NF", "", 0.0, NUM, symtab);
NF = &nfloc->fval;
nrloc = setsymtab("NR", "", 0.0, NUM, symtab);
NR = &nrloc->fval;
fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab);
FNR = &fnrloc->fval;
subseploc = setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab);
SUBSEP = &subseploc->sval;
rstartloc = setsymtab("RSTART", "", 0.0, NUM, symtab);
RSTART = &rstartloc->fval;
rlengthloc = setsymtab("RLENGTH", "", 0.0, NUM, symtab);
RLENGTH = &rlengthloc->fval;
symtabloc = setsymtab("SYMTAB", "", 0.0, ARR, symtab);
free(symtabloc->sval);
symtabloc->sval = (char *) symtab;
}
void arginit(int ac, char **av)
{
Array *ap;
Cell *cp;
int i;
char temp[50];
ARGC = &setsymtab("ARGC", "", (Awkfloat) ac, NUM, symtab)->fval;
cp = setsymtab("ARGV", "", 0.0, ARR, symtab);
ap = makesymtab(NSYMTAB);
free(cp->sval);
cp->sval = (char *) ap;
for (i = 0; i < ac; i++) {
double result;
snprintf(temp, sizeof(temp), "%d", i);
if (is_number(*av, & result))
setsymtab(temp, *av, result, STR|NUM, ap);
else
setsymtab(temp, *av, 0.0, STR, ap);
av++;
}
ARGVcell = cp;
}
void envinit(char **envp)
{
Array *ap;
Cell *cp;
char *p;
cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab);
ap = makesymtab(NSYMTAB);
free(cp->sval);
cp->sval = (char *) ap;
for ( ; *envp; envp++) {
double result;
if ((p = strchr(*envp, '=')) == NULL)
continue;
if( p == *envp )
continue;
*p++ = 0;
if (is_number(p, & result))
setsymtab(*envp, p, result, STR|NUM, ap);
else
setsymtab(*envp, p, 0.0, STR, ap);
p[-1] = '=';
}
}
Array *makesymtab(int n)
{
Array *ap;
Cell **tp;
ap = (Array *) malloc(sizeof(*ap));
tp = (Cell **) calloc(n, sizeof(*tp));
if (ap == NULL || tp == NULL)
FATAL("out of space in makesymtab");
ap->nelem = 0;
ap->size = n;
ap->tab = tp;
return(ap);
}
void freesymtab(Cell *ap)
{
Cell *cp, *temp;
Array *tp;
int i;
if (!isarr(ap))
return;
tp = (Array *) ap->sval;
if (tp == NULL)
return;
for (i = 0; i < tp->size; i++) {
for (cp = tp->tab[i]; cp != NULL; cp = temp) {
xfree(cp->nval);
if (freeable(cp))
xfree(cp->sval);
temp = cp->cnext;
free(cp);
tp->nelem--;
}
tp->tab[i] = NULL;
}
if (tp->nelem != 0)
WARNING("can't happen: inconsistent element count freeing %s", ap->nval);
free(tp->tab);
free(tp);
}
void freeelem(Cell *ap, const char *s)
{
Array *tp;
Cell *p, *prev = NULL;
int h;
tp = (Array *) ap->sval;
h = hash(s, tp->size);
for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext)
if (strcmp(s, p->nval) == 0) {
if (prev == NULL)
tp->tab[h] = p->cnext;
else
prev->cnext = p->cnext;
if (freeable(p))
xfree(p->sval);
free(p->nval);
free(p);
tp->nelem--;
return;
}
}
Cell *setsymtab(const char *n, const char *s, Awkfloat f, unsigned t, Array *tp)
{
int h;
Cell *p;
if (n != NULL && (p = lookup(n, tp)) != NULL) {
DPRINTF("setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n",
(void*)p, NN(p->nval), NN(p->sval), p->fval, p->tval);
return(p);
}
p = (Cell *) malloc(sizeof(*p));
if (p == NULL)
FATAL("out of space for symbol table at %s", n);
p->nval = tostring(n);
p->sval = s ? tostring(s) : tostring("");
p->fval = f;
p->tval = t;
p->csub = CUNK;
p->ctype = OCELL;
tp->nelem++;
if (tp->nelem > FULLTAB * tp->size)
rehash(tp);
h = hash(n, tp->size);
p->cnext = tp->tab[h];
tp->tab[h] = p;
DPRINTF("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n",
(void*)p, p->nval, p->sval, p->fval, p->tval);
return(p);
}
int hash(const char *s, int n)
{
unsigned hashval;
for (hashval = 0; *s != '\0'; s++)
hashval = (*s + 31 * hashval);
return hashval % n;
}
void rehash(Array *tp)
{
int i, nh, nsz;
Cell *cp, *op, **np;
nsz = GROWTAB * tp->size;
np = (Cell **) calloc(nsz, sizeof(*np));
if (np == NULL)
return;
for (i = 0; i < tp->size; i++) {
for (cp = tp->tab[i]; cp; cp = op) {
op = cp->cnext;
nh = hash(cp->nval, nsz);
cp->cnext = np[nh];
np[nh] = cp;
}
}
free(tp->tab);
tp->tab = np;
tp->size = nsz;
}
Cell *lookup(const char *s, Array *tp)
{
Cell *p;
int h;
h = hash(s, tp->size);
for (p = tp->tab[h]; p != NULL; p = p->cnext)
if (strcmp(s, p->nval) == 0)
return(p);
return(NULL);
}
Awkfloat setfval(Cell *vp, Awkfloat f)
{
int fldno;
f += 0.0;
if ((vp->tval & (NUM | STR)) == 0)
funnyvar(vp, "assign to");
if (isfld(vp)) {
donerec = false;
fldno = atoi(vp->nval);
if (fldno > *NF)
newfld(fldno);
DPRINTF("setting field %d to %g\n", fldno, f);
} else if (&vp->fval == NF) {
donerec = false;
setlastfld(f);
DPRINTF("setfval: setting NF to %g\n", f);
} else if (isrec(vp)) {
donefld = false;
donerec = true;
savefs();
} else if (vp == ofsloc) {
if (!donerec)
recbld();
}
if (freeable(vp))
xfree(vp->sval);
vp->tval &= ~(STR|CONVC|CONVO);
vp->fmt = NULL;
vp->tval |= NUM;
if (f == -0)
f = 0;
DPRINTF("setfval %p: %s = %g, t=%o\n", (void*)vp, NN(vp->nval), f, vp->tval);
return vp->fval = f;
}
void funnyvar(Cell *vp, const char *rw)
{
if (isarr(vp))
FATAL("can't %s %s; it's an array name.", rw, vp->nval);
if (vp->tval & FCN)
FATAL("can't %s %s; it's a function.", rw, vp->nval);
WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o",
(void *)vp, vp->nval, vp->sval, vp->fval, vp->tval);
}
char *setsval(Cell *vp, const char *s)
{
char *t;
int fldno;
Awkfloat f;
DPRINTF("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n",
(void*)vp, NN(vp->nval), s, vp->tval, donerec, donefld);
if ((vp->tval & (NUM | STR)) == 0)
funnyvar(vp, "assign to");
if (CSV && (vp == rsloc))
WARNING("danger: don't set RS when --csv is in effect");
if (CSV && (vp == fsloc))
WARNING("danger: don't set FS when --csv is in effect");
if (isfld(vp)) {
donerec = false;
fldno = atoi(vp->nval);
if (fldno > *NF)
newfld(fldno);
DPRINTF("setting field %d to %s (%p)\n", fldno, s, (const void*)s);
} else if (isrec(vp)) {
donefld = false;
donerec = true;
savefs();
} else if (vp == ofsloc) {
if (!donerec)
recbld();
}
t = s ? tostring(s) : tostring("");
if (freeable(vp))
xfree(vp->sval);
vp->tval &= ~(NUM|DONTFREE|CONVC|CONVO);
vp->tval |= STR;
vp->fmt = NULL;
DPRINTF("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n",
(void*)vp, NN(vp->nval), t, (void*)t, vp->tval, donerec, donefld);
vp->sval = t;
if (&vp->fval == NF) {
donerec = false;
f = getfval(vp);
setlastfld(f);
DPRINTF("setsval: setting NF to %g\n", f);
}
return(vp->sval);
}
Awkfloat getfval(Cell *vp)
{
if ((vp->tval & (NUM | STR)) == 0)
funnyvar(vp, "read value of");
if (isfld(vp) && !donefld)
fldbld();
else if (isrec(vp) && !donerec)
recbld();
if (!isnum(vp)) {
double fval;
bool no_trailing;
if (is_valid_number(vp->sval, true, & no_trailing, & fval)) {
vp->fval = fval;
if (no_trailing && !(vp->tval&CON))
vp->tval |= NUM;
} else
vp->fval = 0.0;
}
DPRINTF("getfval %p: %s = %g, t=%o\n",
(void*)vp, NN(vp->nval), vp->fval, vp->tval);
return(vp->fval);
}
static const char *get_inf_nan(double d)
{
if (isinf(d)) {
return (d < 0 ? "-inf" : "+inf");
} else if (isnan(d)) {
return (signbit(d) != 0 ? "-nan" : "+nan");
} else
return NULL;
}
static char *get_str_val(Cell *vp, char **fmt)
{
int n;
double dtemp;
const char *p;
if ((vp->tval & (NUM | STR)) == 0)
funnyvar(vp, "read value of");
if (isfld(vp) && ! donefld)
fldbld();
else if (isrec(vp) && ! donerec)
recbld();
#define update_str_val(vp) \
{ \
if (freeable(vp)) \
xfree(vp->sval); \
if ((p = get_inf_nan(vp->fval)) != NULL) \
n = (vp->sval = strdup(p)) ? 0 : -1; \
else if (modf(vp->fval, &dtemp) == 0) \
n = asprintf(&vp->sval, "%.30g", vp->fval); \
else \
n = asprintf(&vp->sval, *fmt, vp->fval); \
if (n == -1) \
FATAL("out of space in get_str_val"); \
vp->tval &= ~DONTFREE; \
vp->tval |= STR; \
}
if (isstr(vp) == 0) {
update_str_val(vp);
if (fmt == OFMT) {
vp->tval &= ~CONVC;
vp->tval |= CONVO;
} else {
vp->tval &= ~CONVO;
vp->tval |= CONVC;
}
vp->fmt = *fmt;
} else if ((vp->tval & DONTFREE) != 0 || ! isnum(vp) || isfld(vp)) {
goto done;
} else if (isstr(vp)) {
if (fmt == OFMT) {
if ((vp->tval & CONVC) != 0
|| ((vp->tval & CONVO) != 0 && vp->fmt != *fmt)) {
update_str_val(vp);
vp->tval &= ~CONVC;
vp->tval |= CONVO;
vp->fmt = *fmt;
}
} else {
if ((vp->tval & CONVO) != 0
|| ((vp->tval & CONVC) != 0 && vp->fmt != *fmt)) {
update_str_val(vp);
vp->tval &= ~CONVO;
vp->tval |= CONVC;
vp->fmt = *fmt;
}
}
}
done:
DPRINTF("getsval %p: %s = \"%s (%p)\", t=%o\n",
(void*)vp, NN(vp->nval), vp->sval, (void*)vp->sval, vp->tval);
return(vp->sval);
}
char *getsval(Cell *vp)
{
return get_str_val(vp, CONVFMT);
}
char *getpssval(Cell *vp)
{
return get_str_val(vp, OFMT);
}
char *tostring(const char *s)
{
char *p = strdup(s);
if (p == NULL)
FATAL("out of space in tostring on %s", s);
return(p);
}
char *tostringN(const char *s, size_t n)
{
char *p;
p = (char *) malloc(n);
if (p == NULL)
FATAL("out of space in tostringN %zu", n);
if (strlcpy(p, s, n) >= n)
FATAL("out of space in tostringN on %s", s);
return(p);
}
Cell *catstr(Cell *a, Cell *b)
{
Cell *c;
char *p;
char *sa = getsval(a);
char *sb = getsval(b);
size_t l = strlen(sa) + strlen(sb) + 1;
p = (char *) malloc(l);
if (p == NULL)
FATAL("out of space concatenating %s and %s", sa, sb);
snprintf(p, l, "%s%s", sa, sb);
l++;
char *newbuf = (char *) malloc(l);
if (newbuf == NULL)
FATAL("out of space concatenating %s and %s", sa, sb);
snprintf(newbuf, l, "%s ", p);
c = setsymtab(newbuf, p, 0.0, CON|STR|DONTFREE, symtab);
free(p);
free(newbuf);
return c;
}
char *qstring(const char *is, int delim)
{
int c, n;
const uschar *s = (const uschar *) is;
uschar *buf, *bp;
if ((buf = (uschar *) malloc(strlen(is)+3)) == NULL)
FATAL( "out of space in qstring(%s)", s);
for (bp = buf; (c = *s) != delim; s++) {
if (c == '\n')
SYNTAX( "newline in string %.20s...", is );
else if (c != '\\')
*bp++ = c;
else {
c = *++s;
if (c == 0) {
*bp++ = '\\';
break;
}
switch (c) {
case '\\': *bp++ = '\\'; break;
case 'n': *bp++ = '\n'; break;
case 't': *bp++ = '\t'; break;
case 'b': *bp++ = '\b'; break;
case 'f': *bp++ = '\f'; break;
case 'r': *bp++ = '\r'; break;
case 'v': *bp++ = '\v'; break;
case 'a': *bp++ = '\a'; break;
default:
if (!isdigit(c)) {
*bp++ = c;
break;
}
n = c - '0';
if (isdigit(s[1])) {
n = 8 * n + *++s - '0';
if (isdigit(s[1]))
n = 8 * n + *++s - '0';
}
*bp++ = n;
break;
}
}
}
*bp++ = 0;
return (char *) buf;
}
const char *flags2str(int flags)
{
static const struct ftab {
const char *name;
int value;
} flagtab[] = {
{ "NUM", NUM },
{ "STR", STR },
{ "DONTFREE", DONTFREE },
{ "CON", CON },
{ "ARR", ARR },
{ "FCN", FCN },
{ "FLD", FLD },
{ "REC", REC },
{ "CONVC", CONVC },
{ "CONVO", CONVO },
{ NULL, 0 }
};
static char buf[100];
int i, len;
char *cp = buf;
for (i = 0; flagtab[i].name != NULL; i++) {
if ((flags & flagtab[i].value) != 0) {
len = snprintf(cp, sizeof(buf) - (cp - buf),
"%s%s", cp > buf ? "|" : "", flagtab[i].name);
if (len < 0 || (size_t)len >= sizeof(buf) - (cp - buf))
FATAL("out of space in flags2str");
cp += len;
}
}
return buf;
}