ref: d36beef46ad1ba675c819334289edc9308cf3fc7
dir: /tran.c/
#include <u.h>
#include <libc.h>
#include <ctype.h>
#include <bio.h>
#include "awk.h"
#include "y.tab.h"
#define FULLTAB 2 /* rehash when table gets this x full */
#define GROWTAB 4 /* grow table by this factor */
char EMPTY[] = {'\0'};
Array *symtab; /* main symbol table */
char **FS; /* initial field sep */
char **RS; /* initial record sep */
char **OFS; /* output field sep */
char **ORS; /* output record sep */
char **OFMT; /* output format for numbers */
char **CONVFMT; /* format for conversions in getsval */
Awkfloat *NF; /* number of fields in current record */
Awkfloat *NR; /* number of current record */
Awkfloat *FNR; /* number of current record in current file */
char **FILENAME; /* current filename argument */
Awkfloat *AARGC; /* number of arguments from command line */
char **SUBSEP; /* subscript separator for a[i,j,k]; default \034 */
Awkfloat *RSTART; /* start of re matched with ~; origin 1 (!) */
Awkfloat *RLENGTH; /* length of same */
Cell *nrloc; /* NR */
Cell *nfloc; /* NF */
Cell *fnrloc; /* FNR */
Array *ARGVtab; /* symbol table containing ARGV[...] */
Array *ENVtab; /* symbol table containing ENVIRON[...] */
Cell *rstartloc; /* RSTART */
Cell *rlengthloc; /* RLENGTH */
Cell *symtabloc; /* SYMTAB */
Cell *nullloc; /* a guaranteed empty cell */
Node *nullnode; /* zero&null, converted into a node for comparisons */
Cell *literal0;
extern Cell **fldtab;
void syminit(void) /* initialize symbol table with builtin vars */
{
literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab);
/* this is used for if(x)... tests: */
nullloc = setsymtab("$zero&null", EMPTY, 0.0, NUM|STR|CON|DONTFREE, symtab);
nullnode = celltonode(nullloc, CCON);
FS = &setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab)->sval;
RS = &setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab)->sval;
OFS = &setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab)->sval;
ORS = &setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab)->sval;
OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
FILENAME = &setsymtab("FILENAME", EMPTY, 0.0, STR|DONTFREE, symtab)->sval;
nfloc = setsymtab("NF", EMPTY, 0.0, NUM, symtab);
NF = &nfloc->fval;
nrloc = setsymtab("NR", EMPTY, 0.0, NUM, symtab);
NR = &nrloc->fval;
fnrloc = setsymtab("FNR", EMPTY, 0.0, NUM, symtab);
FNR = &fnrloc->fval;
SUBSEP = &setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab)->sval;
rstartloc = setsymtab("RSTART", EMPTY, 0.0, NUM, symtab);
RSTART = &rstartloc->fval;
rlengthloc = setsymtab("RLENGTH", EMPTY, 0.0, NUM, symtab);
RLENGTH = &rlengthloc->fval;
symtabloc = setsymtab("SYMTAB", EMPTY, 0.0, ARR, symtab);
symtabloc->sval = (char *) symtab;
}
void arginit(int ac, char **av) /* set up ARGV and ARGC */
{
Cell *cp;
int i;
char temp[50];
Awkfloat f;
AARGC = &setsymtab("ARGC", EMPTY, (Awkfloat) ac, NUM, symtab)->fval;
cp = setsymtab("ARGV", EMPTY, 0.0, ARR, symtab);
ARGVtab = makesymtab(NSYMTAB); /* could be (int) ARGC as well */
cp->sval = (char *) ARGVtab;
for (i = 0; i < ac; i++) {
sprint(temp, "%d", i);
if (to_number(*av, &f, nil))
setsymtab(temp, *av, f, STR|NUM, ARGVtab);
else
setsymtab(temp, *av, 0.0, STR, ARGVtab);
av++;
}
}
void envinit(void) /* set up ENVIRON variable */
{
int fd, i, n;
char *k, *v;
Dir *buf;
Awkfloat f;
ENVtab = makesymtab(NSYMTAB);
if ((fd = open("/env", OREAD)) < 0)
return;
buf = nil;
while((n = dirread(fd, &buf)) > 0) {
for (i = 0; i < n; i++) {
k = buf[i].name;
if(strncmp(k, "fn#", 3) == 0)
continue;
if ((v = getenv(k)) == nil)
continue;
if (to_number(v, &f, nil))
setsymtab(k, v, f, STR|NUM, ENVtab);
else
setsymtab(k, v, 0.0, STR, ENVtab);
free(v);
}
free(buf);
buf = nil;
}
close(fd);
}
Array *makesymtab(int n) /* make a new symbol table */
{
Array *ap;
Cell **tp;
ap = (Array *) malloc(sizeof(Array));
tp = (Cell **) calloc(n, sizeof(Cell *));
if (ap == nil || tp == nil)
FATAL("out of space in makesymtab");
ap->nelemt = 0;
ap->size = n;
ap->tab = tp;
return(ap);
}
void freesymtab(Cell *ap) /* free a symbol table */
{
Cell *cp, *temp;
Array *tp;
int i;
if (!isarr(ap))
return;
tp = (Array *) ap->sval;
if (tp == nil)
return;
for (i = 0; i < tp->size; i++) {
for (cp = tp->tab[i]; cp != nil; cp = temp) {
xfree(cp->nval);
if (freeable(cp))
xfree(cp->sval);
temp = cp->cnext; /* avoids freeing then using */
free(cp);
}
tp->tab[i] = 0;
}
free(tp->tab);
free(tp);
}
void freeelem(Cell *ap, char *s) /* free elem s from ap (i.e., ap["s"] */
{
Array *tp;
Cell *p, *prev = nil;
int h;
tp = (Array *) ap->sval;
h = hash(s, tp->size);
for (p = tp->tab[h]; p != nil; prev = p, p = p->cnext)
if (strcmp(s, p->nval) == 0) {
if (prev == nil) /* 1st one */
tp->tab[h] = p->cnext;
else /* middle somewhere */
prev->cnext = p->cnext;
if (freeable(p))
xfree(p->sval);
free(p->nval);
free(p);
tp->nelemt--;
return;
}
}
Cell *setsymtab(char *n, char *s, Awkfloat f, unsigned t, Array *tp)
{
int h;
Cell *p;
if (n != nil && (p = lookup(n, tp)) != nil) {
dprint( ("setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n",
p, p->nval, p->sval, p->fval, p->tval) );
return(p);
}
p = (Cell *) malloc(sizeof(Cell));
if (p == nil)
FATAL("out of space for symbol table at %s", n);
p->nval = tostring(n);
p->fval = f;
if(tp == symtab && strcmp(n, "ENVIRON") == 0 && !safe) {
envinit();
p->sval = (char *) ENVtab;
p->tval = ARR;
} else {
p->sval = s != nil && s != EMPTY ? tostring(s) : EMPTY;
p->tval = t;
if (p->sval == EMPTY)
p->tval |= DONTFREE;
}
p->csub = CUNK;
p->ctype = OCELL;
tp->nelemt++;
if (tp->nelemt > FULLTAB * tp->size)
rehash(tp);
h = hash(n, tp->size);
p->cnext = tp->tab[h];
tp->tab[h] = p;
dprint( ("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n",
p, p->nval, p->sval, p->fval, p->tval) );
return(p);
}
int hash(char *s, int n) /* form hash value for string s */
{
unsigned hashval;
for (hashval = 0; *s != '\0'; s++)
hashval = (*s + 31 * hashval);
return hashval % n;
}
void rehash(Array *tp) /* rehash items in small table into big one */
{
int i, nh, nsz;
Cell *cp, *op, **np;
nsz = GROWTAB * tp->size;
np = (Cell **) calloc(nsz, sizeof(Cell *));
if (np == nil) /* can't do it, but can keep running. */
return; /* someone else will run out later. */
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(char *s, Array *tp) /* look for s in tp */
{
Cell *p;
int h;
h = hash(s, tp->size);
for (p = tp->tab[h]; p != nil; p = p->cnext)
if (strcmp(s, p->nval) == 0)
return(p); /* found it */
return(nil); /* not found */
}
Awkfloat setfval(Cell *vp, Awkfloat f) /* set float val of a Cell */
{
int fldno;
if ((vp->tval & (NUM | STR)) == 0)
funnyvar(vp, "assign to");
if (isfld(vp)) {
donerec = 0; /* mark $0 invalid */
fldno = atoi(vp->nval);
if (fldno > *NF)
newfld(fldno);
dprint( ("setting field %d to %g\n", fldno, f) );
} else if (isrec(vp)) {
donefld = 0; /* mark $1... invalid */
donerec = 1;
}
if (freeable(vp))
xfree(vp->sval); /* free any previous string */
vp->tval &= ~STR; /* mark string invalid */
vp->tval |= NUM; /* mark number ok */
dprint( ("setfval %p: %s = %g, t=%o\n", vp, vp->nval, f, vp->tval) );
return vp->fval = f;
}
void funnyvar(Cell *vp, 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",
vp, vp->nval, vp->sval, vp->fval, vp->tval);
}
char *setsval(Cell *vp, char *s) /* set string val of a Cell */
{
char *t;
int fldno;
dprint( ("starting setsval %p: %s = \"%s\", t=%o\n", vp, vp->nval, s, vp->tval) );
if ((vp->tval & (NUM | STR)) == 0)
funnyvar(vp, "assign to");
if (isfld(vp)) {
donerec = 0; /* mark $0 invalid */
fldno = atoi(vp->nval);
if (fldno > *NF)
newfld(fldno);
dprint( ("setting field %d to %s (%p)\n", fldno, s, s) );
} else if (isrec(vp)) {
donefld = 0; /* mark $1... invalid */
donerec = 1;
}
t = s != nil && s != EMPTY ? tostring(s) : EMPTY; /* in case it's self-assign */
vp->tval &= ~NUM;
vp->tval |= STR;
if (freeable(vp))
xfree(vp->sval);
if (t != EMPTY)
vp->tval &= ~DONTFREE;
else
vp->tval |= DONTFREE;
dprint( ("setsval %p: %s = \"%s (%p)\", t=%o\n", vp, vp->nval, t,t, vp->tval) );
return(vp->sval = t);
}
Awkfloat getfval(Cell *vp) /* get float val of a Cell */
{
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)) { /* not a number */
vp->fval = 0;
if (to_number(vp->sval, &vp->fval, nil)) {
if (!(vp->tval&CON))
vp->tval |= NUM; /* make NUM only sparingly */
}
}
dprint( ("getfval %p: %s = %g, t=%o\n", vp, vp->nval, vp->fval, vp->tval) );
return(vp->fval);
}
char *getsval(Cell *vp) /* get string val of a Cell */
{
char s[100]; /* BUG: unchecked */
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();
if (isstr(vp) == 0) {
if (freeable(vp))
xfree(vp->sval);
if (modf(vp->fval, &dtemp) == 0) /* it's integral */
sprint(s, "%.30g", vp->fval);
else
sprint(s, *CONVFMT, vp->fval);
vp->sval = tostring(s);
vp->tval &= ~DONTFREE;
vp->tval |= STR;
}
dprint( ("getsval %p: %s = \"%s (%p)\", t=%o\n", vp, vp->nval, vp->sval, vp->sval, vp->tval) );
return(vp->sval);
}
char *tostring(char *s) /* make a copy of string s */
{
char *p;
p = (char *) malloc(strlen(s)+1);
if (p == nil)
FATAL("out of space in tostring on %s", s);
strcpy(p, s);
return(p);
}
char *qstring(char *s, int delim) /* collect string up to next delim */
{
char *os = s;
int c, n;
char *buf, *bp;
if ((buf = (char *) malloc(strlen(s)+3)) == nil)
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 { /* \something */
c = *++s;
if (c == 0) { /* \ at end */
*bp++ = '\\';
break; /* for loop */
}
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 buf;
}