#define DEBUG
#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include "awk.h"
#include "y.tab.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 *recloc;
Cell *fsloc;
Cell *nrloc;
Cell *nfloc;
Cell *fnrloc;
Cell *ofsloc;
Cell *orsloc;
Cell *rsloc;
Cell *rtloc;
Array *ARGVtab;
Array *ENVtab;
Cell *rstartloc;
Cell *rlengthloc;
Cell *subseploc;
Cell *symtabloc;
Cell *nullloc;
Node *nullnode;
Cell *literal0;
static void rehash(Array *);
static void
setfree(Cell *vp)
{
if (&vp->sval == FS || &vp->sval == RS ||
&vp->sval == OFS || &vp->sval == ORS ||
&vp->sval == OFMT || &vp->sval == CONVFMT ||
&vp->sval == FILENAME || &vp->sval == SUBSEP)
vp->tval |= DONTFREE;
else
vp->tval &= ~DONTFREE;
}
void
syminit(void)
{
recloc = fieldadr(0);
recloc->nval = "$0";
recloc->sval = record;
recloc->tval = REC|STR|DONTFREE;
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;
rtloc = setsymtab("RT", "", 0.0, STR|DONTFREE, symtab);
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);
symtabloc->sval = (char *)symtab;
}
void
arginit(int ac, char **av)
{
Cell *cp;
int i;
char temp[50];
ARGC = &setsymtab("ARGC", "", (Awkfloat)ac, NUM, symtab)->fval;
cp = setsymtab("ARGV", "", 0.0, ARR, symtab);
ARGVtab = makesymtab(NSYMTAB);
cp->sval = (char *)ARGVtab;
for (i = 0; i < ac; i++) {
(void) sprintf(temp, "%d", i);
if (is_number(*av)) {
(void) setsymtab(temp, *av, atof(*av),
STR|NUM, ARGVtab);
} else {
(void) setsymtab(temp, *av, 0.0, STR, ARGVtab);
}
av++;
}
}
void
envinit(char **envp)
{
Cell *cp;
char *p;
cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab);
ENVtab = makesymtab(NSYMTAB);
cp->sval = (char *)ENVtab;
for (; *envp; envp++) {
if ((p = strchr(*envp, '=')) == NULL)
continue;
if (p == *envp)
continue;
*p++ = 0;
if (is_number(p)) {
(void) setsymtab(*envp, p, atof(p),
STR|NUM, ENVtab);
} else {
(void) setsymtab(*envp, p, 0.0, STR, ENVtab);
}
p[-1] = '=';
}
}
Array *
makesymtab(int n)
{
Array *ap;
Cell **tp;
ap = (Array *)malloc(sizeof (Array));
tp = (Cell **)calloc(n, sizeof (Cell *));
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] = 0;
}
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 int 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 (Cell));
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 int hashval;
for (hashval = 0; *s != '\0'; s++)
hashval = (*s + 31 * hashval);
return (hashval % n);
}
static void
rehash(Array *tp)
{
int i, nh, nsz;
Cell *cp, *op, **np;
nsz = GROWTAB * tp->size;
np = (Cell **)calloc(nsz, sizeof (Cell *));
if (np == NULL)
return;
for (i = 0; i < tp->size; i++) {
for (cp = tp->tab[i]; cp != NULL; 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 = 0;
fldno = atoi(vp->nval);
if (fldno > *NF)
newfld(fldno);
dprintf(("setting field %d to %g\n", fldno, f));
} else if (&vp->fval == NF) {
donerec = 0;
setlastfld((int)f);
dprintf(("setting NF to %g\n", f));
} else if (isrec(vp)) {
donefld = 0;
donerec = 1;
savefs();
} else if (vp == ofsloc) {
if (donerec == 0)
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 (isfcn(vp))
FATAL("can't %s %s; it's a function.", rw, vp->nval);
WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o",
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 (isfld(vp)) {
donerec = 0;
fldno = atoi(vp->nval);
if (fldno > *NF)
newfld(fldno);
dprintf(("setting field %d to %s (%p)\n", fldno, s, (void *)s));
} else if (isrec(vp)) {
donefld = 0;
donerec = 1;
savefs();
} else if (vp == ofsloc) {
if (donerec == 0)
recbld();
}
t = s ? tostring(s) : tostring("");
if (freeable(vp))
xfree(vp->sval);
vp->tval &= ~(NUM|CONVC|CONVO);
vp->tval |= STR;
vp->fmt = NULL;
setfree(vp);
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 = 0;
f = getfval(vp);
setlastfld((int)f);
dprintf(("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 == 0)
fldbld();
else if (isrec(vp) && donerec == 0)
recbld();
if (!isnum(vp)) {
vp->fval = atof(vp->sval);
if (is_number(vp->sval) && !(vp->tval&CON))
vp->tval |= NUM;
}
dprintf(("getfval %p: %s = %g, t=%o\n",
(void *)vp, NN(vp->nval), vp->fval, vp->tval));
return (vp->fval);
}
static char *
get_str_val(Cell *vp, char **fmt)
{
char s[256];
double dtemp;
if ((vp->tval & (NUM | STR)) == 0)
funnyvar(vp, "read value of");
if (isfld(vp) && donefld == 0)
fldbld();
else if (isrec(vp) && donerec == 0)
recbld();
#define update_str_val(vp) \
{ \
if (freeable(vp)) \
xfree(vp->sval); \
if (modf(vp->fval, &dtemp) == 0) \
(void) snprintf(s, sizeof (s), "%.30g", vp->fval); \
else \
(void) snprintf(s, sizeof (s), *fmt, vp->fval); \
vp->sval = tostring(s); \
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 *
qstring(const char *is, int delim)
{
const char *os = is;
int c, n;
uschar *s = (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...", os);
} 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;
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);
}