458 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			458 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/****************************************************************
 | 
						|
Copyright (C) Lucent Technologies 1997
 | 
						|
All Rights Reserved
 | 
						|
 | 
						|
Permission to use, copy, modify, and distribute this software and
 | 
						|
its documentation for any purpose and without fee is hereby
 | 
						|
granted, provided that the above copyright notice appear in all
 | 
						|
copies and that both that the copyright notice and this
 | 
						|
permission notice and warranty disclaimer appear in supporting
 | 
						|
documentation, and that the name Lucent Technologies or any of
 | 
						|
its entities not be used in advertising or publicity pertaining
 | 
						|
to distribution of the software without specific, written prior
 | 
						|
permission.
 | 
						|
 | 
						|
LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 | 
						|
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
 | 
						|
IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
 | 
						|
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
						|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 | 
						|
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 | 
						|
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
 | 
						|
THIS SOFTWARE.
 | 
						|
****************************************************************/
 | 
						|
 | 
						|
#define	DEBUG
 | 
						|
#include <stdio.h>
 | 
						|
#include <math.h>
 | 
						|
#include <ctype.h>
 | 
						|
#include <string.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include "awk.h"
 | 
						|
#include "awkgram.h"
 | 
						|
 | 
						|
#define	FULLTAB	2	/* rehash when table gets this x full */
 | 
						|
#define	GROWTAB 4	/* grow table by this factor */
 | 
						|
 | 
						|
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 *ARGC;		/* 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	*fsloc;		/* FS */
 | 
						|
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", "", 0.0, NUM|STR|CON|DONTFREE, symtab);
 | 
						|
	nullnode = celltonode(nullloc, CCON);
 | 
						|
 | 
						|
	fsloc = setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab);
 | 
						|
	FS = &fsloc->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", "", 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;
 | 
						|
	SUBSEP = &setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab)->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)	/* set up ARGV and ARGC */
 | 
						|
{
 | 
						|
	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);	/* could be (int) ARGC as well */
 | 
						|
	cp->sval = (char *) ARGVtab;
 | 
						|
	for (i = 0; i < ac; i++) {
 | 
						|
		sprintf(temp, "%d", i);
 | 
						|
		if (is_number(*av))
 | 
						|
			setsymtab(temp, *av, atof(*av), STR|NUM, ARGVtab);
 | 
						|
		else
 | 
						|
			setsymtab(temp, *av, 0.0, STR, ARGVtab);
 | 
						|
		av++;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void envinit(char **envp)	/* set up ENVIRON variable */
 | 
						|
{
 | 
						|
	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 ) /* no left hand side name in env string */
 | 
						|
			continue;
 | 
						|
		*p++ = 0;	/* split into two strings at = */
 | 
						|
		if (is_number(p))
 | 
						|
			setsymtab(*envp, p, atof(p), STR|NUM, ENVtab);
 | 
						|
		else
 | 
						|
			setsymtab(*envp, p, 0.0, STR, ENVtab);
 | 
						|
		p[-1] = '=';	/* restore in case env is passed down to a shell */
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
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 == NULL || tp == NULL)
 | 
						|
		FATAL("out of space in makesymtab");
 | 
						|
	ap->nelem = 0;
 | 
						|
	ap->size = n;
 | 
						|
	ap->tab = tp;
 | 
						|
	return(ap);
 | 
						|
}
 | 
						|
 | 
						|
void freesymtab(const Cell *ap)	/* free a symbol table */
 | 
						|
{
 | 
						|
	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;	/* avoids freeing then using */
 | 
						|
			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(const Cell *ap, const char *s)
 | 
						|
/* free elem s from ap (i.e., ap["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)	/* 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->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",
 | 
						|
			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",
 | 
						|
		p, p->nval, p->sval, p->fval, p->tval) );
 | 
						|
	return(p);
 | 
						|
}
 | 
						|
 | 
						|
int hash(const 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 == NULL)		/* 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(const char *s, const Array *tp)	/* look for s in 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);	/* found it */
 | 
						|
	return(NULL);			/* 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);
 | 
						|
		   dprintf( ("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 */
 | 
						|
	   dprintf( ("setfval %p: %s = %g, t=%o\n", 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",
 | 
						|
		vp, vp->nval, vp->sval, vp->fval, vp->tval);
 | 
						|
}
 | 
						|
 | 
						|
char *setsval(Cell *vp, const char *s)	/* set string val of a Cell */
 | 
						|
{
 | 
						|
	char *t;
 | 
						|
	int fldno;
 | 
						|
 | 
						|
	   dprintf( ("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n", 
 | 
						|
		vp, NN(vp->nval), s, vp->tval, donerec, donefld) );
 | 
						|
	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);
 | 
						|
		   dprintf( ("setting field %d to %s (%p)\n", fldno, s, s) );
 | 
						|
	} else if (isrec(vp)) {
 | 
						|
		donefld = 0;	/* mark $1... invalid */
 | 
						|
		donerec = 1;
 | 
						|
	}
 | 
						|
	t = tostring(s);	/* in case it's self-assign */
 | 
						|
	if (freeable(vp))
 | 
						|
		xfree(vp->sval);
 | 
						|
	vp->tval &= ~NUM;
 | 
						|
	vp->tval |= STR;
 | 
						|
	vp->tval &= ~DONTFREE;
 | 
						|
	   dprintf( ("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n", 
 | 
						|
		vp, NN(vp->nval), t,t, vp->tval, donerec, donefld) );
 | 
						|
	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 = atof(vp->sval);	/* best guess */
 | 
						|
		if (is_number(vp->sval) && !(vp->tval&CON))
 | 
						|
			vp->tval |= NUM;	/* make NUM only sparingly */
 | 
						|
	}
 | 
						|
	   dprintf( ("getfval %p: %s = %g, t=%o\n", vp, NN(vp->nval), vp->fval, vp->tval) );
 | 
						|
	return(vp->fval);
 | 
						|
}
 | 
						|
 | 
						|
static char *get_str_val(Cell *vp, char **fmt)
 | 
						|
/* 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 */
 | 
						|
			sprintf(s, "%.30g", vp->fval);
 | 
						|
		else
 | 
						|
			sprintf(s, *fmt, vp->fval);
 | 
						|
		vp->sval = tostring(s);
 | 
						|
		vp->tval &= ~DONTFREE;
 | 
						|
		vp->tval |= STR;
 | 
						|
	}
 | 
						|
	   dprintf( ("getsval %p: %s = \"%s (%p)\", t=%o\n", vp, NN(vp->nval), vp->sval, vp->sval, vp->tval) );
 | 
						|
	return(vp->sval);
 | 
						|
}
 | 
						|
 | 
						|
char *getsval(Cell *vp)       /* get string val of a Cell */
 | 
						|
{
 | 
						|
      return get_str_val(vp, CONVFMT);
 | 
						|
}
 | 
						|
 | 
						|
char *getpssval(Cell *vp)     /* get string val of a Cell for print */
 | 
						|
{
 | 
						|
      return get_str_val(vp, OFMT);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
char *tostring(const char *s)	/* make a copy of string s */
 | 
						|
{
 | 
						|
	char *p;
 | 
						|
 | 
						|
	p = (char *) malloc(strlen(s)+1);
 | 
						|
	if (p == NULL)
 | 
						|
		FATAL("out of space in tostring on %s", s);
 | 
						|
	strcpy(p, s);
 | 
						|
	return(p);
 | 
						|
}
 | 
						|
 | 
						|
char *qstring(const char *is, int delim)	/* collect string up to next 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 {	/* \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 (char *) buf;
 | 
						|
}
 |