xref: /freebsd/contrib/one-true-awk/tran.c (revision b5253557)
12a55deb1SDavid E. O'Brien /****************************************************************
22a55deb1SDavid E. O'Brien Copyright (C) Lucent Technologies 1997
32a55deb1SDavid E. O'Brien All Rights Reserved
42a55deb1SDavid E. O'Brien 
52a55deb1SDavid E. O'Brien Permission to use, copy, modify, and distribute this software and
62a55deb1SDavid E. O'Brien its documentation for any purpose and without fee is hereby
72a55deb1SDavid E. O'Brien granted, provided that the above copyright notice appear in all
82a55deb1SDavid E. O'Brien copies and that both that the copyright notice and this
92a55deb1SDavid E. O'Brien permission notice and warranty disclaimer appear in supporting
102a55deb1SDavid E. O'Brien documentation, and that the name Lucent Technologies or any of
112a55deb1SDavid E. O'Brien its entities not be used in advertising or publicity pertaining
122a55deb1SDavid E. O'Brien to distribution of the software without specific, written prior
132a55deb1SDavid E. O'Brien permission.
142a55deb1SDavid E. O'Brien 
152a55deb1SDavid E. O'Brien LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
162a55deb1SDavid E. O'Brien INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
172a55deb1SDavid E. O'Brien IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
182a55deb1SDavid E. O'Brien SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
192a55deb1SDavid E. O'Brien WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
202a55deb1SDavid E. O'Brien IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
212a55deb1SDavid E. O'Brien ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
222a55deb1SDavid E. O'Brien THIS SOFTWARE.
232a55deb1SDavid E. O'Brien ****************************************************************/
242a55deb1SDavid E. O'Brien 
252a55deb1SDavid E. O'Brien #define	DEBUG
262a55deb1SDavid E. O'Brien #include <stdio.h>
272a55deb1SDavid E. O'Brien #include <math.h>
282a55deb1SDavid E. O'Brien #include <ctype.h>
292a55deb1SDavid E. O'Brien #include <string.h>
302a55deb1SDavid E. O'Brien #include <stdlib.h>
312a55deb1SDavid E. O'Brien #include "awk.h"
322a55deb1SDavid E. O'Brien #include "ytab.h"
332a55deb1SDavid E. O'Brien 
342a55deb1SDavid E. O'Brien #define	FULLTAB	2	/* rehash when table gets this x full */
352a55deb1SDavid E. O'Brien #define	GROWTAB 4	/* grow table by this factor */
362a55deb1SDavid E. O'Brien 
372a55deb1SDavid E. O'Brien Array	*symtab;	/* main symbol table */
382a55deb1SDavid E. O'Brien 
392a55deb1SDavid E. O'Brien char	**FS;		/* initial field sep */
402a55deb1SDavid E. O'Brien char	**RS;		/* initial record sep */
412a55deb1SDavid E. O'Brien char	**OFS;		/* output field sep */
422a55deb1SDavid E. O'Brien char	**ORS;		/* output record sep */
432a55deb1SDavid E. O'Brien char	**OFMT;		/* output format for numbers */
442a55deb1SDavid E. O'Brien char	**CONVFMT;	/* format for conversions in getsval */
452a55deb1SDavid E. O'Brien Awkfloat *NF;		/* number of fields in current record */
462a55deb1SDavid E. O'Brien Awkfloat *NR;		/* number of current record */
472a55deb1SDavid E. O'Brien Awkfloat *FNR;		/* number of current record in current file */
482a55deb1SDavid E. O'Brien char	**FILENAME;	/* current filename argument */
492a55deb1SDavid E. O'Brien Awkfloat *ARGC;		/* number of arguments from command line */
502a55deb1SDavid E. O'Brien char	**SUBSEP;	/* subscript separator for a[i,j,k]; default \034 */
512a55deb1SDavid E. O'Brien Awkfloat *RSTART;	/* start of re matched with ~; origin 1 (!) */
522a55deb1SDavid E. O'Brien Awkfloat *RLENGTH;	/* length of same */
532a55deb1SDavid E. O'Brien 
54c263f9bfSRuslan Ermilov Cell	*fsloc;		/* FS */
552a55deb1SDavid E. O'Brien Cell	*nrloc;		/* NR */
562a55deb1SDavid E. O'Brien Cell	*nfloc;		/* NF */
572a55deb1SDavid E. O'Brien Cell	*fnrloc;	/* FNR */
58b5253557SWarner Losh Cell	*ofsloc;	/* OFS */
59b5253557SWarner Losh Cell	*orsloc;	/* ORS */
60b5253557SWarner Losh Cell	*rsloc;		/* RS */
612a55deb1SDavid E. O'Brien Array	*ARGVtab;	/* symbol table containing ARGV[...] */
622a55deb1SDavid E. O'Brien Array	*ENVtab;	/* symbol table containing ENVIRON[...] */
632a55deb1SDavid E. O'Brien Cell	*rstartloc;	/* RSTART */
642a55deb1SDavid E. O'Brien Cell	*rlengthloc;	/* RLENGTH */
65b5253557SWarner Losh Cell	*subseploc;	/* SUBSEP */
662a55deb1SDavid E. O'Brien Cell	*symtabloc;	/* SYMTAB */
672a55deb1SDavid E. O'Brien 
682a55deb1SDavid E. O'Brien Cell	*nullloc;	/* a guaranteed empty cell */
692a55deb1SDavid E. O'Brien Node	*nullnode;	/* zero&null, converted into a node for comparisons */
702a55deb1SDavid E. O'Brien Cell	*literal0;
712a55deb1SDavid E. O'Brien 
722a55deb1SDavid E. O'Brien extern Cell **fldtab;
732a55deb1SDavid E. O'Brien 
74b5253557SWarner Losh static void
75b5253557SWarner Losh setfree(Cell *vp)
76b5253557SWarner Losh {
77b5253557SWarner Losh 	if (&vp->sval == FS || &vp->sval == RS ||
78b5253557SWarner Losh 	    &vp->sval == OFS || &vp->sval == ORS ||
79b5253557SWarner Losh 	    &vp->sval == OFMT || &vp->sval == CONVFMT ||
80b5253557SWarner Losh 	    &vp->sval == FILENAME || &vp->sval == SUBSEP)
81b5253557SWarner Losh 		vp->tval |= DONTFREE;
82b5253557SWarner Losh 	else
83b5253557SWarner Losh 		vp->tval &= ~DONTFREE;
84b5253557SWarner Losh }
85b5253557SWarner Losh 
862a55deb1SDavid E. O'Brien void syminit(void)	/* initialize symbol table with builtin vars */
872a55deb1SDavid E. O'Brien {
882a55deb1SDavid E. O'Brien 	literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab);
892a55deb1SDavid E. O'Brien 	/* this is used for if(x)... tests: */
902a55deb1SDavid E. O'Brien 	nullloc = setsymtab("$zero&null", "", 0.0, NUM|STR|CON|DONTFREE, symtab);
912a55deb1SDavid E. O'Brien 	nullnode = celltonode(nullloc, CCON);
922a55deb1SDavid E. O'Brien 
93c263f9bfSRuslan Ermilov 	fsloc = setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab);
94c263f9bfSRuslan Ermilov 	FS = &fsloc->sval;
95b5253557SWarner Losh 	rsloc = setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab);
96b5253557SWarner Losh 	RS = &rsloc->sval;
97b5253557SWarner Losh 	ofsloc = setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab);
98b5253557SWarner Losh 	OFS = &ofsloc->sval;
99b5253557SWarner Losh 	orsloc = setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab);
100b5253557SWarner Losh 	ORS = &orsloc->sval;
1012a55deb1SDavid E. O'Brien 	OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
1022a55deb1SDavid E. O'Brien 	CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
1032a55deb1SDavid E. O'Brien 	FILENAME = &setsymtab("FILENAME", "", 0.0, STR|DONTFREE, symtab)->sval;
1042a55deb1SDavid E. O'Brien 	nfloc = setsymtab("NF", "", 0.0, NUM, symtab);
1052a55deb1SDavid E. O'Brien 	NF = &nfloc->fval;
1062a55deb1SDavid E. O'Brien 	nrloc = setsymtab("NR", "", 0.0, NUM, symtab);
1072a55deb1SDavid E. O'Brien 	NR = &nrloc->fval;
1082a55deb1SDavid E. O'Brien 	fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab);
1092a55deb1SDavid E. O'Brien 	FNR = &fnrloc->fval;
110b5253557SWarner Losh 	subseploc = setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab);
111b5253557SWarner Losh 	SUBSEP = &subseploc->sval;
1122a55deb1SDavid E. O'Brien 	rstartloc = setsymtab("RSTART", "", 0.0, NUM, symtab);
1132a55deb1SDavid E. O'Brien 	RSTART = &rstartloc->fval;
1142a55deb1SDavid E. O'Brien 	rlengthloc = setsymtab("RLENGTH", "", 0.0, NUM, symtab);
1152a55deb1SDavid E. O'Brien 	RLENGTH = &rlengthloc->fval;
1162a55deb1SDavid E. O'Brien 	symtabloc = setsymtab("SYMTAB", "", 0.0, ARR, symtab);
1172a55deb1SDavid E. O'Brien 	symtabloc->sval = (char *) symtab;
1182a55deb1SDavid E. O'Brien }
1192a55deb1SDavid E. O'Brien 
1202a55deb1SDavid E. O'Brien void arginit(int ac, char **av)	/* set up ARGV and ARGC */
1212a55deb1SDavid E. O'Brien {
1222a55deb1SDavid E. O'Brien 	Cell *cp;
1232a55deb1SDavid E. O'Brien 	int i;
1242a55deb1SDavid E. O'Brien 	char temp[50];
1252a55deb1SDavid E. O'Brien 
1262a55deb1SDavid E. O'Brien 	ARGC = &setsymtab("ARGC", "", (Awkfloat) ac, NUM, symtab)->fval;
1272a55deb1SDavid E. O'Brien 	cp = setsymtab("ARGV", "", 0.0, ARR, symtab);
1282a55deb1SDavid E. O'Brien 	ARGVtab = makesymtab(NSYMTAB);	/* could be (int) ARGC as well */
1292a55deb1SDavid E. O'Brien 	cp->sval = (char *) ARGVtab;
1302a55deb1SDavid E. O'Brien 	for (i = 0; i < ac; i++) {
1312a55deb1SDavid E. O'Brien 		sprintf(temp, "%d", i);
1322a55deb1SDavid E. O'Brien 		if (is_number(*av))
1332a55deb1SDavid E. O'Brien 			setsymtab(temp, *av, atof(*av), STR|NUM, ARGVtab);
1342a55deb1SDavid E. O'Brien 		else
1352a55deb1SDavid E. O'Brien 			setsymtab(temp, *av, 0.0, STR, ARGVtab);
1362a55deb1SDavid E. O'Brien 		av++;
1372a55deb1SDavid E. O'Brien 	}
1382a55deb1SDavid E. O'Brien }
1392a55deb1SDavid E. O'Brien 
1402a55deb1SDavid E. O'Brien void envinit(char **envp)	/* set up ENVIRON variable */
1412a55deb1SDavid E. O'Brien {
1422a55deb1SDavid E. O'Brien 	Cell *cp;
1432a55deb1SDavid E. O'Brien 	char *p;
1442a55deb1SDavid E. O'Brien 
1452a55deb1SDavid E. O'Brien 	cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab);
1462a55deb1SDavid E. O'Brien 	ENVtab = makesymtab(NSYMTAB);
1472a55deb1SDavid E. O'Brien 	cp->sval = (char *) ENVtab;
1482a55deb1SDavid E. O'Brien 	for ( ; *envp; envp++) {
1492a55deb1SDavid E. O'Brien 		if ((p = strchr(*envp, '=')) == NULL)
1502a55deb1SDavid E. O'Brien 			continue;
1512a55deb1SDavid E. O'Brien 		if( p == *envp ) /* no left hand side name in env string */
1522a55deb1SDavid E. O'Brien 			continue;
1532a55deb1SDavid E. O'Brien 		*p++ = 0;	/* split into two strings at = */
1542a55deb1SDavid E. O'Brien 		if (is_number(p))
1552a55deb1SDavid E. O'Brien 			setsymtab(*envp, p, atof(p), STR|NUM, ENVtab);
1562a55deb1SDavid E. O'Brien 		else
1572a55deb1SDavid E. O'Brien 			setsymtab(*envp, p, 0.0, STR, ENVtab);
1582a55deb1SDavid E. O'Brien 		p[-1] = '=';	/* restore in case env is passed down to a shell */
1592a55deb1SDavid E. O'Brien 	}
1602a55deb1SDavid E. O'Brien }
1612a55deb1SDavid E. O'Brien 
1622a55deb1SDavid E. O'Brien Array *makesymtab(int n)	/* make a new symbol table */
1632a55deb1SDavid E. O'Brien {
1642a55deb1SDavid E. O'Brien 	Array *ap;
1652a55deb1SDavid E. O'Brien 	Cell **tp;
1662a55deb1SDavid E. O'Brien 
1672a55deb1SDavid E. O'Brien 	ap = (Array *) malloc(sizeof(Array));
1682a55deb1SDavid E. O'Brien 	tp = (Cell **) calloc(n, sizeof(Cell *));
1692a55deb1SDavid E. O'Brien 	if (ap == NULL || tp == NULL)
1702a55deb1SDavid E. O'Brien 		FATAL("out of space in makesymtab");
1712a55deb1SDavid E. O'Brien 	ap->nelem = 0;
1722a55deb1SDavid E. O'Brien 	ap->size = n;
1732a55deb1SDavid E. O'Brien 	ap->tab = tp;
1742a55deb1SDavid E. O'Brien 	return(ap);
1752a55deb1SDavid E. O'Brien }
1762a55deb1SDavid E. O'Brien 
1772a55deb1SDavid E. O'Brien void freesymtab(Cell *ap)	/* free a symbol table */
1782a55deb1SDavid E. O'Brien {
1792a55deb1SDavid E. O'Brien 	Cell *cp, *temp;
1802a55deb1SDavid E. O'Brien 	Array *tp;
1812a55deb1SDavid E. O'Brien 	int i;
1822a55deb1SDavid E. O'Brien 
1832a55deb1SDavid E. O'Brien 	if (!isarr(ap))
1842a55deb1SDavid E. O'Brien 		return;
1852a55deb1SDavid E. O'Brien 	tp = (Array *) ap->sval;
1862a55deb1SDavid E. O'Brien 	if (tp == NULL)
1872a55deb1SDavid E. O'Brien 		return;
1882a55deb1SDavid E. O'Brien 	for (i = 0; i < tp->size; i++) {
1892a55deb1SDavid E. O'Brien 		for (cp = tp->tab[i]; cp != NULL; cp = temp) {
1902a55deb1SDavid E. O'Brien 			xfree(cp->nval);
1912a55deb1SDavid E. O'Brien 			if (freeable(cp))
1922a55deb1SDavid E. O'Brien 				xfree(cp->sval);
1932a55deb1SDavid E. O'Brien 			temp = cp->cnext;	/* avoids freeing then using */
1942a55deb1SDavid E. O'Brien 			free(cp);
195007c6572SDag-Erling Smørgrav 			tp->nelem--;
1962a55deb1SDavid E. O'Brien 		}
197b5253557SWarner Losh 		tp->tab[i] = 0;
1982a55deb1SDavid E. O'Brien 	}
199007c6572SDag-Erling Smørgrav 	if (tp->nelem != 0)
200007c6572SDag-Erling Smørgrav 		WARNING("can't happen: inconsistent element count freeing %s", ap->nval);
2012a55deb1SDavid E. O'Brien 	free(tp->tab);
2022a55deb1SDavid E. O'Brien 	free(tp);
2032a55deb1SDavid E. O'Brien }
2042a55deb1SDavid E. O'Brien 
205813da98dSDavid E. O'Brien void freeelem(Cell *ap, const char *s)	/* free elem s from ap (i.e., ap["s"] */
2062a55deb1SDavid E. O'Brien {
2072a55deb1SDavid E. O'Brien 	Array *tp;
2082a55deb1SDavid E. O'Brien 	Cell *p, *prev = NULL;
2092a55deb1SDavid E. O'Brien 	int h;
2102a55deb1SDavid E. O'Brien 
2112a55deb1SDavid E. O'Brien 	tp = (Array *) ap->sval;
2122a55deb1SDavid E. O'Brien 	h = hash(s, tp->size);
2132a55deb1SDavid E. O'Brien 	for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext)
2142a55deb1SDavid E. O'Brien 		if (strcmp(s, p->nval) == 0) {
2152a55deb1SDavid E. O'Brien 			if (prev == NULL)	/* 1st one */
2162a55deb1SDavid E. O'Brien 				tp->tab[h] = p->cnext;
2172a55deb1SDavid E. O'Brien 			else			/* middle somewhere */
2182a55deb1SDavid E. O'Brien 				prev->cnext = p->cnext;
2192a55deb1SDavid E. O'Brien 			if (freeable(p))
2202a55deb1SDavid E. O'Brien 				xfree(p->sval);
2212a55deb1SDavid E. O'Brien 			free(p->nval);
2222a55deb1SDavid E. O'Brien 			free(p);
2232a55deb1SDavid E. O'Brien 			tp->nelem--;
2242a55deb1SDavid E. O'Brien 			return;
2252a55deb1SDavid E. O'Brien 		}
2262a55deb1SDavid E. O'Brien }
2272a55deb1SDavid E. O'Brien 
228813da98dSDavid E. O'Brien Cell *setsymtab(const char *n, const char *s, Awkfloat f, unsigned t, Array *tp)
2292a55deb1SDavid E. O'Brien {
2302a55deb1SDavid E. O'Brien 	int h;
2312a55deb1SDavid E. O'Brien 	Cell *p;
2322a55deb1SDavid E. O'Brien 
2332a55deb1SDavid E. O'Brien 	if (n != NULL && (p = lookup(n, tp)) != NULL) {
2342a55deb1SDavid E. O'Brien 		   dprintf( ("setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n",
235d86a0988SRuslan Ermilov 			(void*)p, NN(p->nval), NN(p->sval), p->fval, p->tval) );
2362a55deb1SDavid E. O'Brien 		return(p);
2372a55deb1SDavid E. O'Brien 	}
2382a55deb1SDavid E. O'Brien 	p = (Cell *) malloc(sizeof(Cell));
2392a55deb1SDavid E. O'Brien 	if (p == NULL)
2402a55deb1SDavid E. O'Brien 		FATAL("out of space for symbol table at %s", n);
2412a55deb1SDavid E. O'Brien 	p->nval = tostring(n);
2422a55deb1SDavid E. O'Brien 	p->sval = s ? tostring(s) : tostring("");
2432a55deb1SDavid E. O'Brien 	p->fval = f;
2442a55deb1SDavid E. O'Brien 	p->tval = t;
2452a55deb1SDavid E. O'Brien 	p->csub = CUNK;
2462a55deb1SDavid E. O'Brien 	p->ctype = OCELL;
2472a55deb1SDavid E. O'Brien 	tp->nelem++;
2482a55deb1SDavid E. O'Brien 	if (tp->nelem > FULLTAB * tp->size)
2492a55deb1SDavid E. O'Brien 		rehash(tp);
2502a55deb1SDavid E. O'Brien 	h = hash(n, tp->size);
2512a55deb1SDavid E. O'Brien 	p->cnext = tp->tab[h];
2522a55deb1SDavid E. O'Brien 	tp->tab[h] = p;
2532a55deb1SDavid E. O'Brien 	   dprintf( ("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n",
254d86a0988SRuslan Ermilov 		(void*)p, p->nval, p->sval, p->fval, p->tval) );
2552a55deb1SDavid E. O'Brien 	return(p);
2562a55deb1SDavid E. O'Brien }
2572a55deb1SDavid E. O'Brien 
258813da98dSDavid E. O'Brien int hash(const char *s, int n)	/* form hash value for string s */
2592a55deb1SDavid E. O'Brien {
2602a55deb1SDavid E. O'Brien 	unsigned hashval;
2612a55deb1SDavid E. O'Brien 
2622a55deb1SDavid E. O'Brien 	for (hashval = 0; *s != '\0'; s++)
2632a55deb1SDavid E. O'Brien 		hashval = (*s + 31 * hashval);
2642a55deb1SDavid E. O'Brien 	return hashval % n;
2652a55deb1SDavid E. O'Brien }
2662a55deb1SDavid E. O'Brien 
2672a55deb1SDavid E. O'Brien void rehash(Array *tp)	/* rehash items in small table into big one */
2682a55deb1SDavid E. O'Brien {
2692a55deb1SDavid E. O'Brien 	int i, nh, nsz;
2702a55deb1SDavid E. O'Brien 	Cell *cp, *op, **np;
2712a55deb1SDavid E. O'Brien 
2722a55deb1SDavid E. O'Brien 	nsz = GROWTAB * tp->size;
2732a55deb1SDavid E. O'Brien 	np = (Cell **) calloc(nsz, sizeof(Cell *));
2742a55deb1SDavid E. O'Brien 	if (np == NULL)		/* can't do it, but can keep running. */
2752a55deb1SDavid E. O'Brien 		return;		/* someone else will run out later. */
2762a55deb1SDavid E. O'Brien 	for (i = 0; i < tp->size; i++) {
2772a55deb1SDavid E. O'Brien 		for (cp = tp->tab[i]; cp; cp = op) {
2782a55deb1SDavid E. O'Brien 			op = cp->cnext;
2792a55deb1SDavid E. O'Brien 			nh = hash(cp->nval, nsz);
2802a55deb1SDavid E. O'Brien 			cp->cnext = np[nh];
2812a55deb1SDavid E. O'Brien 			np[nh] = cp;
2822a55deb1SDavid E. O'Brien 		}
2832a55deb1SDavid E. O'Brien 	}
2842a55deb1SDavid E. O'Brien 	free(tp->tab);
2852a55deb1SDavid E. O'Brien 	tp->tab = np;
2862a55deb1SDavid E. O'Brien 	tp->size = nsz;
2872a55deb1SDavid E. O'Brien }
2882a55deb1SDavid E. O'Brien 
289813da98dSDavid E. O'Brien Cell *lookup(const char *s, Array *tp)	/* look for s in tp */
2902a55deb1SDavid E. O'Brien {
2912a55deb1SDavid E. O'Brien 	Cell *p;
2922a55deb1SDavid E. O'Brien 	int h;
2932a55deb1SDavid E. O'Brien 
2942a55deb1SDavid E. O'Brien 	h = hash(s, tp->size);
2952a55deb1SDavid E. O'Brien 	for (p = tp->tab[h]; p != NULL; p = p->cnext)
2962a55deb1SDavid E. O'Brien 		if (strcmp(s, p->nval) == 0)
2972a55deb1SDavid E. O'Brien 			return(p);	/* found it */
2982a55deb1SDavid E. O'Brien 	return(NULL);			/* not found */
2992a55deb1SDavid E. O'Brien }
3002a55deb1SDavid E. O'Brien 
3012a55deb1SDavid E. O'Brien Awkfloat setfval(Cell *vp, Awkfloat f)	/* set float val of a Cell */
3022a55deb1SDavid E. O'Brien {
3032a55deb1SDavid E. O'Brien 	int fldno;
3042a55deb1SDavid E. O'Brien 
305b5253557SWarner Losh 	f += 0.0;		/* normalise negative zero to positive zero */
3062a55deb1SDavid E. O'Brien 	if ((vp->tval & (NUM | STR)) == 0)
3072a55deb1SDavid E. O'Brien 		funnyvar(vp, "assign to");
3082a55deb1SDavid E. O'Brien 	if (isfld(vp)) {
3092a55deb1SDavid E. O'Brien 		donerec = 0;	/* mark $0 invalid */
3102a55deb1SDavid E. O'Brien 		fldno = atoi(vp->nval);
3112a55deb1SDavid E. O'Brien 		if (fldno > *NF)
3122a55deb1SDavid E. O'Brien 			newfld(fldno);
3132a55deb1SDavid E. O'Brien 		   dprintf( ("setting field %d to %g\n", fldno, f) );
314b5253557SWarner Losh 	} else if (&vp->fval == NF) {
315b5253557SWarner Losh 		donerec = 0;	/* mark $0 invalid */
316b5253557SWarner Losh 		setlastfld(f);
317b5253557SWarner Losh 		dprintf( ("setting NF to %g\n", f) );
3182a55deb1SDavid E. O'Brien 	} else if (isrec(vp)) {
3192a55deb1SDavid E. O'Brien 		donefld = 0;	/* mark $1... invalid */
3202a55deb1SDavid E. O'Brien 		donerec = 1;
321b5253557SWarner Losh 	} else if (vp == ofsloc) {
322b5253557SWarner Losh 		if (donerec == 0)
323b5253557SWarner Losh 			recbld();
3242a55deb1SDavid E. O'Brien 	}
3252a55deb1SDavid E. O'Brien 	if (freeable(vp))
3262a55deb1SDavid E. O'Brien 		xfree(vp->sval); /* free any previous string */
327b5253557SWarner Losh 	vp->tval &= ~(STR|CONVC|CONVO); /* mark string invalid */
328b5253557SWarner Losh 	vp->fmt = NULL;
3292a55deb1SDavid E. O'Brien 	vp->tval |= NUM;	/* mark number ok */
3300840e960SXin LI 	if (f == -0)  /* who would have thought this possible? */
3310840e960SXin LI 		f = 0;
332d86a0988SRuslan Ermilov 	   dprintf( ("setfval %p: %s = %g, t=%o\n", (void*)vp, NN(vp->nval), f, vp->tval) );
3332a55deb1SDavid E. O'Brien 	return vp->fval = f;
3342a55deb1SDavid E. O'Brien }
3352a55deb1SDavid E. O'Brien 
336813da98dSDavid E. O'Brien void funnyvar(Cell *vp, const char *rw)
3372a55deb1SDavid E. O'Brien {
3382a55deb1SDavid E. O'Brien 	if (isarr(vp))
3392a55deb1SDavid E. O'Brien 		FATAL("can't %s %s; it's an array name.", rw, vp->nval);
3402a55deb1SDavid E. O'Brien 	if (vp->tval & FCN)
3412a55deb1SDavid E. O'Brien 		FATAL("can't %s %s; it's a function.", rw, vp->nval);
3422a55deb1SDavid E. O'Brien 	WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o",
3432a55deb1SDavid E. O'Brien 		vp, vp->nval, vp->sval, vp->fval, vp->tval);
3442a55deb1SDavid E. O'Brien }
3452a55deb1SDavid E. O'Brien 
346813da98dSDavid E. O'Brien char *setsval(Cell *vp, const char *s)	/* set string val of a Cell */
3472a55deb1SDavid E. O'Brien {
3482a55deb1SDavid E. O'Brien 	char *t;
3492a55deb1SDavid E. O'Brien 	int fldno;
350b5253557SWarner Losh 	Awkfloat f;
3512a55deb1SDavid E. O'Brien 
352c263f9bfSRuslan Ermilov 	   dprintf( ("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n",
353d86a0988SRuslan Ermilov 		(void*)vp, NN(vp->nval), s, vp->tval, donerec, donefld) );
3542a55deb1SDavid E. O'Brien 	if ((vp->tval & (NUM | STR)) == 0)
3552a55deb1SDavid E. O'Brien 		funnyvar(vp, "assign to");
3562a55deb1SDavid E. O'Brien 	if (isfld(vp)) {
3572a55deb1SDavid E. O'Brien 		donerec = 0;	/* mark $0 invalid */
3582a55deb1SDavid E. O'Brien 		fldno = atoi(vp->nval);
3592a55deb1SDavid E. O'Brien 		if (fldno > *NF)
3602a55deb1SDavid E. O'Brien 			newfld(fldno);
361b5253557SWarner Losh 		   dprintf( ("setting field %d to %s (%p)\n", fldno, s, (void *) s) );
3622a55deb1SDavid E. O'Brien 	} else if (isrec(vp)) {
3632a55deb1SDavid E. O'Brien 		donefld = 0;	/* mark $1... invalid */
3642a55deb1SDavid E. O'Brien 		donerec = 1;
365b5253557SWarner Losh 	} else if (vp == ofsloc) {
366b5253557SWarner Losh 		if (donerec == 0)
367b5253557SWarner Losh 			recbld();
3682a55deb1SDavid E. O'Brien 	}
369b5253557SWarner Losh 	t = s ? tostring(s) : tostring("");	/* in case it's self-assign */
3702a55deb1SDavid E. O'Brien 	if (freeable(vp))
3712a55deb1SDavid E. O'Brien 		xfree(vp->sval);
372b5253557SWarner Losh 	vp->tval &= ~(NUM|CONVC|CONVO);
373d2f6e492SDavid E. O'Brien 	vp->tval |= STR;
374b5253557SWarner Losh 	vp->fmt = NULL;
375b5253557SWarner Losh 	setfree(vp);
376c263f9bfSRuslan Ermilov 	   dprintf( ("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n",
377b5253557SWarner Losh 		(void*)vp, NN(vp->nval), t, (void *) t, vp->tval, donerec, donefld) );
378b5253557SWarner Losh 	vp->sval = t;
379b5253557SWarner Losh 	if (&vp->fval == NF) {
380b5253557SWarner Losh 		donerec = 0;	/* mark $0 invalid */
381b5253557SWarner Losh 		f = getfval(vp);
382b5253557SWarner Losh 		setlastfld(f);
383b5253557SWarner Losh 		dprintf( ("setting NF to %g\n", f) );
384b5253557SWarner Losh 	}
385b5253557SWarner Losh 
386b5253557SWarner Losh 	return(vp->sval);
3872a55deb1SDavid E. O'Brien }
3882a55deb1SDavid E. O'Brien 
3892a55deb1SDavid E. O'Brien Awkfloat getfval(Cell *vp)	/* get float val of a Cell */
3902a55deb1SDavid E. O'Brien {
3912a55deb1SDavid E. O'Brien 	if ((vp->tval & (NUM | STR)) == 0)
3922a55deb1SDavid E. O'Brien 		funnyvar(vp, "read value of");
3932a55deb1SDavid E. O'Brien 	if (isfld(vp) && donefld == 0)
3942a55deb1SDavid E. O'Brien 		fldbld();
3952a55deb1SDavid E. O'Brien 	else if (isrec(vp) && donerec == 0)
3962a55deb1SDavid E. O'Brien 		recbld();
3972a55deb1SDavid E. O'Brien 	if (!isnum(vp)) {	/* not a number */
3982a55deb1SDavid E. O'Brien 		vp->fval = atof(vp->sval);	/* best guess */
3992a55deb1SDavid E. O'Brien 		if (is_number(vp->sval) && !(vp->tval&CON))
4002a55deb1SDavid E. O'Brien 			vp->tval |= NUM;	/* make NUM only sparingly */
4012a55deb1SDavid E. O'Brien 	}
402d86a0988SRuslan Ermilov 	   dprintf( ("getfval %p: %s = %g, t=%o\n",
403d86a0988SRuslan Ermilov 		(void*)vp, NN(vp->nval), vp->fval, vp->tval) );
4042a55deb1SDavid E. O'Brien 	return(vp->fval);
4052a55deb1SDavid E. O'Brien }
4062a55deb1SDavid E. O'Brien 
407813da98dSDavid E. O'Brien static char *get_str_val(Cell *vp, char **fmt)        /* get string val of a Cell */
4082a55deb1SDavid E. O'Brien {
409b5253557SWarner Losh 	char s[256];
4102a55deb1SDavid E. O'Brien 	double dtemp;
4112a55deb1SDavid E. O'Brien 
4122a55deb1SDavid E. O'Brien 	if ((vp->tval & (NUM | STR)) == 0)
4132a55deb1SDavid E. O'Brien 		funnyvar(vp, "read value of");
4142a55deb1SDavid E. O'Brien 	if (isfld(vp) && donefld == 0)
4152a55deb1SDavid E. O'Brien 		fldbld();
4162a55deb1SDavid E. O'Brien 	else if (isrec(vp) && donerec == 0)
4172a55deb1SDavid E. O'Brien 		recbld();
418b5253557SWarner Losh 
419b5253557SWarner Losh 	/*
420b5253557SWarner Losh 	 * ADR: This is complicated and more fragile than is desirable.
421b5253557SWarner Losh 	 * Retrieving a string value for a number associates the string
422b5253557SWarner Losh 	 * value with the scalar.  Previously, the string value was
423b5253557SWarner Losh 	 * sticky, meaning if converted via OFMT that became the value
424b5253557SWarner Losh 	 * (even though POSIX wants it to be via CONVFMT). Or if CONVFMT
425b5253557SWarner Losh 	 * changed after a string value was retrieved, the original value
426b5253557SWarner Losh 	 * was maintained and used.  Also not per POSIX.
427b5253557SWarner Losh 	 *
428b5253557SWarner Losh 	 * We work around this design by adding two additional flags,
429b5253557SWarner Losh 	 * CONVC and CONVO, indicating how the string value was
430b5253557SWarner Losh 	 * obtained (via CONVFMT or OFMT) and _also_ maintaining a copy
431b5253557SWarner Losh 	 * of the pointer to the xFMT format string used for the
432b5253557SWarner Losh 	 * conversion.  This pointer is only read, **never** dereferenced.
433b5253557SWarner Losh 	 * The next time we do a conversion, if it's coming from the same
434b5253557SWarner Losh 	 * xFMT as last time, and the pointer value is different, we
435b5253557SWarner Losh 	 * know that the xFMT format string changed, and we need to
436b5253557SWarner Losh 	 * redo the conversion. If it's the same, we don't have to.
437b5253557SWarner Losh 	 *
438b5253557SWarner Losh 	 * There are also several cases where we don't do a conversion,
439b5253557SWarner Losh 	 * such as for a field (see the checks below).
440b5253557SWarner Losh 	 */
441b5253557SWarner Losh 
442b5253557SWarner Losh 	/* Don't duplicate the code for actually updating the value */
443b5253557SWarner Losh #define update_str_val(vp) \
444b5253557SWarner Losh 	{ \
445b5253557SWarner Losh 		if (freeable(vp)) \
446b5253557SWarner Losh 			xfree(vp->sval); \
447b5253557SWarner Losh 		if (modf(vp->fval, &dtemp) == 0)	/* it's integral */ \
448b5253557SWarner Losh 			snprintf(s, sizeof (s), "%.30g", vp->fval); \
449b5253557SWarner Losh 		else \
450b5253557SWarner Losh 			snprintf(s, sizeof (s), *fmt, vp->fval); \
451b5253557SWarner Losh 		vp->sval = tostring(s); \
452b5253557SWarner Losh 		vp->tval &= ~DONTFREE; \
453b5253557SWarner Losh 		vp->tval |= STR; \
4542a55deb1SDavid E. O'Brien 	}
455b5253557SWarner Losh 
456b5253557SWarner Losh 	if (isstr(vp) == 0) {
457b5253557SWarner Losh 		update_str_val(vp);
458b5253557SWarner Losh 		if (fmt == OFMT) {
459b5253557SWarner Losh 			vp->tval &= ~CONVC;
460b5253557SWarner Losh 			vp->tval |= CONVO;
461b5253557SWarner Losh 		} else {
462b5253557SWarner Losh 			/* CONVFMT */
463b5253557SWarner Losh 			vp->tval &= ~CONVO;
464b5253557SWarner Losh 			vp->tval |= CONVC;
465b5253557SWarner Losh 		}
466b5253557SWarner Losh 		vp->fmt = *fmt;
467b5253557SWarner Losh 	} else if ((vp->tval & DONTFREE) != 0 || ! isnum(vp) || isfld(vp)) {
468b5253557SWarner Losh 		goto done;
469b5253557SWarner Losh 	} else if (isstr(vp)) {
470b5253557SWarner Losh 		if (fmt == OFMT) {
471b5253557SWarner Losh 			if ((vp->tval & CONVC) != 0
472b5253557SWarner Losh 			    || ((vp->tval & CONVO) != 0 && vp->fmt != *fmt)) {
473b5253557SWarner Losh 				update_str_val(vp);
474b5253557SWarner Losh 				vp->tval &= ~CONVC;
475b5253557SWarner Losh 				vp->tval |= CONVO;
476b5253557SWarner Losh 				vp->fmt = *fmt;
477b5253557SWarner Losh 			}
478b5253557SWarner Losh 		} else {
479b5253557SWarner Losh 			/* CONVFMT */
480b5253557SWarner Losh 			if ((vp->tval & CONVO) != 0
481b5253557SWarner Losh 			    || ((vp->tval & CONVC) != 0 && vp->fmt != *fmt)) {
482b5253557SWarner Losh 				update_str_val(vp);
483b5253557SWarner Losh 				vp->tval &= ~CONVO;
484b5253557SWarner Losh 				vp->tval |= CONVC;
485b5253557SWarner Losh 				vp->fmt = *fmt;
486b5253557SWarner Losh 			}
487b5253557SWarner Losh 		}
488b5253557SWarner Losh 	}
489b5253557SWarner Losh done:
490d86a0988SRuslan Ermilov 	   dprintf( ("getsval %p: %s = \"%s (%p)\", t=%o\n",
491b5253557SWarner Losh 		(void*)vp, NN(vp->nval), vp->sval, (void *) vp->sval, vp->tval) );
4922a55deb1SDavid E. O'Brien 	return(vp->sval);
4932a55deb1SDavid E. O'Brien }
4942a55deb1SDavid E. O'Brien 
495813da98dSDavid E. O'Brien char *getsval(Cell *vp)       /* get string val of a Cell */
496813da98dSDavid E. O'Brien {
497813da98dSDavid E. O'Brien       return get_str_val(vp, CONVFMT);
498813da98dSDavid E. O'Brien }
499813da98dSDavid E. O'Brien 
500813da98dSDavid E. O'Brien char *getpssval(Cell *vp)     /* get string val of a Cell for print */
501813da98dSDavid E. O'Brien {
502813da98dSDavid E. O'Brien       return get_str_val(vp, OFMT);
503813da98dSDavid E. O'Brien }
504813da98dSDavid E. O'Brien 
505813da98dSDavid E. O'Brien 
506813da98dSDavid E. O'Brien char *tostring(const char *s)	/* make a copy of string s */
5072a55deb1SDavid E. O'Brien {
5082a55deb1SDavid E. O'Brien 	char *p;
5092a55deb1SDavid E. O'Brien 
5102a55deb1SDavid E. O'Brien 	p = (char *) malloc(strlen(s)+1);
5112a55deb1SDavid E. O'Brien 	if (p == NULL)
5122a55deb1SDavid E. O'Brien 		FATAL("out of space in tostring on %s", s);
5132a55deb1SDavid E. O'Brien 	strcpy(p, s);
5142a55deb1SDavid E. O'Brien 	return(p);
5152a55deb1SDavid E. O'Brien }
5162a55deb1SDavid E. O'Brien 
517813da98dSDavid E. O'Brien char *qstring(const char *is, int delim)	/* collect string up to next delim */
5182a55deb1SDavid E. O'Brien {
519813da98dSDavid E. O'Brien 	const char *os = is;
5202a55deb1SDavid E. O'Brien 	int c, n;
5212a55deb1SDavid E. O'Brien 	uschar *s = (uschar *) is;
5222a55deb1SDavid E. O'Brien 	uschar *buf, *bp;
5232a55deb1SDavid E. O'Brien 
524007c6572SDag-Erling Smørgrav 	if ((buf = (uschar *) malloc(strlen(is)+3)) == NULL)
5252a55deb1SDavid E. O'Brien 		FATAL( "out of space in qstring(%s)", s);
5262a55deb1SDavid E. O'Brien 	for (bp = buf; (c = *s) != delim; s++) {
5272a55deb1SDavid E. O'Brien 		if (c == '\n')
5282a55deb1SDavid E. O'Brien 			SYNTAX( "newline in string %.20s...", os );
5292a55deb1SDavid E. O'Brien 		else if (c != '\\')
5302a55deb1SDavid E. O'Brien 			*bp++ = c;
5312a55deb1SDavid E. O'Brien 		else {	/* \something */
5322a55deb1SDavid E. O'Brien 			c = *++s;
5332a55deb1SDavid E. O'Brien 			if (c == 0) {	/* \ at end */
5342a55deb1SDavid E. O'Brien 				*bp++ = '\\';
5352a55deb1SDavid E. O'Brien 				break;	/* for loop */
5362a55deb1SDavid E. O'Brien 			}
5372a55deb1SDavid E. O'Brien 			switch (c) {
5382a55deb1SDavid E. O'Brien 			case '\\':	*bp++ = '\\'; break;
5392a55deb1SDavid E. O'Brien 			case 'n':	*bp++ = '\n'; break;
5402a55deb1SDavid E. O'Brien 			case 't':	*bp++ = '\t'; break;
5412a55deb1SDavid E. O'Brien 			case 'b':	*bp++ = '\b'; break;
5422a55deb1SDavid E. O'Brien 			case 'f':	*bp++ = '\f'; break;
5432a55deb1SDavid E. O'Brien 			case 'r':	*bp++ = '\r'; break;
5442a55deb1SDavid E. O'Brien 			default:
5452a55deb1SDavid E. O'Brien 				if (!isdigit(c)) {
5462a55deb1SDavid E. O'Brien 					*bp++ = c;
5472a55deb1SDavid E. O'Brien 					break;
5482a55deb1SDavid E. O'Brien 				}
5492a55deb1SDavid E. O'Brien 				n = c - '0';
5502a55deb1SDavid E. O'Brien 				if (isdigit(s[1])) {
5512a55deb1SDavid E. O'Brien 					n = 8 * n + *++s - '0';
5522a55deb1SDavid E. O'Brien 					if (isdigit(s[1]))
5532a55deb1SDavid E. O'Brien 						n = 8 * n + *++s - '0';
5542a55deb1SDavid E. O'Brien 				}
5552a55deb1SDavid E. O'Brien 				*bp++ = n;
5562a55deb1SDavid E. O'Brien 				break;
5572a55deb1SDavid E. O'Brien 			}
5582a55deb1SDavid E. O'Brien 		}
5592a55deb1SDavid E. O'Brien 	}
5602a55deb1SDavid E. O'Brien 	*bp++ = 0;
5612a55deb1SDavid E. O'Brien 	return (char *) buf;
5622a55deb1SDavid E. O'Brien }
563b5253557SWarner Losh 
564b5253557SWarner Losh const char *flags2str(int flags)
565b5253557SWarner Losh {
566b5253557SWarner Losh 	static const struct ftab {
567b5253557SWarner Losh 		const char *name;
568b5253557SWarner Losh 		int value;
569b5253557SWarner Losh 	} flagtab[] = {
570b5253557SWarner Losh 		{ "NUM", NUM },
571b5253557SWarner Losh 		{ "STR", STR },
572b5253557SWarner Losh 		{ "DONTFREE", DONTFREE },
573b5253557SWarner Losh 		{ "CON", CON },
574b5253557SWarner Losh 		{ "ARR", ARR },
575b5253557SWarner Losh 		{ "FCN", FCN },
576b5253557SWarner Losh 		{ "FLD", FLD },
577b5253557SWarner Losh 		{ "REC", REC },
578b5253557SWarner Losh 		{ "CONVC", CONVC },
579b5253557SWarner Losh 		{ "CONVO", CONVO },
580b5253557SWarner Losh 		{ NULL, 0 }
581b5253557SWarner Losh 	};
582b5253557SWarner Losh 	static char buf[100];
583b5253557SWarner Losh 	int i;
584b5253557SWarner Losh 	char *cp = buf;
585b5253557SWarner Losh 
586b5253557SWarner Losh 	for (i = 0; flagtab[i].name != NULL; i++) {
587b5253557SWarner Losh 		if ((flags & flagtab[i].value) != 0) {
588b5253557SWarner Losh 			if (cp > buf)
589b5253557SWarner Losh 				*cp++ = '|';
590b5253557SWarner Losh 			strcpy(cp, flagtab[i].name);
591b5253557SWarner Losh 			cp += strlen(cp);
592b5253557SWarner Losh 		}
593b5253557SWarner Losh 	}
594b5253557SWarner Losh 
595b5253557SWarner Losh 	return buf;
596b5253557SWarner Losh }
597