xref: /openbsd/usr.bin/awk/tran.c (revision c062391a)
1*c062391aSmillert /*	$OpenBSD: tran.c,v 1.19 2020/06/10 21:00:01 millert Exp $	*/
26ab05f83Stholo /****************************************************************
307edfa4aSkstailey Copyright (C) Lucent Technologies 1997
46ab05f83Stholo All Rights Reserved
56ab05f83Stholo 
66ab05f83Stholo Permission to use, copy, modify, and distribute this software and
76ab05f83Stholo its documentation for any purpose and without fee is hereby
86ab05f83Stholo granted, provided that the above copyright notice appear in all
96ab05f83Stholo copies and that both that the copyright notice and this
106ab05f83Stholo permission notice and warranty disclaimer appear in supporting
1107edfa4aSkstailey documentation, and that the name Lucent Technologies or any of
1207edfa4aSkstailey its entities not be used in advertising or publicity pertaining
1307edfa4aSkstailey to distribution of the software without specific, written prior
1407edfa4aSkstailey permission.
156ab05f83Stholo 
1607edfa4aSkstailey LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
1707edfa4aSkstailey INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
1807edfa4aSkstailey IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
1907edfa4aSkstailey SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2007edfa4aSkstailey WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
2107edfa4aSkstailey IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
2207edfa4aSkstailey ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
2307edfa4aSkstailey THIS SOFTWARE.
246ab05f83Stholo ****************************************************************/
256ab05f83Stholo 
266ab05f83Stholo #define	DEBUG
276ab05f83Stholo #include <stdio.h>
286ab05f83Stholo #include <math.h>
296ab05f83Stholo #include <ctype.h>
306ab05f83Stholo #include <string.h>
316ab05f83Stholo #include <stdlib.h>
326ab05f83Stholo #include "awk.h"
3307edfa4aSkstailey #include "ytab.h"
346ab05f83Stholo 
356ab05f83Stholo #define	FULLTAB	2	/* rehash when table gets this x full */
366ab05f83Stholo #define	GROWTAB 4	/* grow table by this factor */
376ab05f83Stholo 
386ab05f83Stholo Array	*symtab;	/* main symbol table */
396ab05f83Stholo 
406ab05f83Stholo char	**FS;		/* initial field sep */
416ab05f83Stholo char	**RS;		/* initial record sep */
426ab05f83Stholo char	**OFS;		/* output field sep */
436ab05f83Stholo char	**ORS;		/* output record sep */
446ab05f83Stholo char	**OFMT;		/* output format for numbers */
456ab05f83Stholo char	**CONVFMT;	/* format for conversions in getsval */
466ab05f83Stholo Awkfloat *NF;		/* number of fields in current record */
476ab05f83Stholo Awkfloat *NR;		/* number of current record */
486ab05f83Stholo Awkfloat *FNR;		/* number of current record in current file */
496ab05f83Stholo char	**FILENAME;	/* current filename argument */
506ab05f83Stholo Awkfloat *ARGC;		/* number of arguments from command line */
516ab05f83Stholo char	**SUBSEP;	/* subscript separator for a[i,j,k]; default \034 */
526ab05f83Stholo Awkfloat *RSTART;	/* start of re matched with ~; origin 1 (!) */
536ab05f83Stholo Awkfloat *RLENGTH;	/* length of same */
546ab05f83Stholo 
5523cb51abSmillert Cell	*fsloc;		/* FS */
566ab05f83Stholo Cell	*nrloc;		/* NR */
576ab05f83Stholo Cell	*nfloc;		/* NF */
586ab05f83Stholo Cell	*fnrloc;	/* FNR */
596ab05f83Stholo Array	*ARGVtab;	/* symbol table containing ARGV[...] */
606ab05f83Stholo Array	*ENVtab;	/* symbol table containing ENVIRON[...] */
616ab05f83Stholo Cell	*rstartloc;	/* RSTART */
626ab05f83Stholo Cell	*rlengthloc;	/* RLENGTH */
636ab05f83Stholo Cell	*symtabloc;	/* SYMTAB */
646ab05f83Stholo 
656ab05f83Stholo Cell	*nullloc;	/* a guaranteed empty cell */
666ab05f83Stholo Node	*nullnode;	/* zero&null, converted into a node for comparisons */
6707edfa4aSkstailey Cell	*literal0;
686ab05f83Stholo 
6907edfa4aSkstailey extern Cell **fldtab;
706ab05f83Stholo 
71*c062391aSmillert static void
72*c062391aSmillert setfree(Cell *vp)
73*c062391aSmillert {
74*c062391aSmillert 	if (&vp->sval == FS || &vp->sval == RS ||
75*c062391aSmillert 	    &vp->sval == OFS || &vp->sval == ORS ||
76*c062391aSmillert 	    &vp->sval == OFMT || &vp->sval == CONVFMT ||
77*c062391aSmillert 	    &vp->sval == FILENAME || &vp->sval == SUBSEP)
78*c062391aSmillert 		vp->tval |= DONTFREE;
79*c062391aSmillert 	else
80*c062391aSmillert 		vp->tval &= ~DONTFREE;
81*c062391aSmillert }
82*c062391aSmillert 
836ab05f83Stholo void syminit(void)	/* initialize symbol table with builtin vars */
846ab05f83Stholo {
8507edfa4aSkstailey 	literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab);
866ab05f83Stholo 	/* this is used for if(x)... tests: */
876ab05f83Stholo 	nullloc = setsymtab("$zero&null", "", 0.0, NUM|STR|CON|DONTFREE, symtab);
8807edfa4aSkstailey 	nullnode = celltonode(nullloc, CCON);
896ab05f83Stholo 
9023cb51abSmillert 	fsloc = setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab);
9123cb51abSmillert 	FS = &fsloc->sval;
926ab05f83Stholo 	RS = &setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab)->sval;
936ab05f83Stholo 	OFS = &setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab)->sval;
946ab05f83Stholo 	ORS = &setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab)->sval;
956ab05f83Stholo 	OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
966ab05f83Stholo 	CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
97b2698ba9Smillert 	FILENAME = &setsymtab("FILENAME", "", 0.0, STR|DONTFREE, symtab)->sval;
986ab05f83Stholo 	nfloc = setsymtab("NF", "", 0.0, NUM, symtab);
996ab05f83Stholo 	NF = &nfloc->fval;
1006ab05f83Stholo 	nrloc = setsymtab("NR", "", 0.0, NUM, symtab);
1016ab05f83Stholo 	NR = &nrloc->fval;
1026ab05f83Stholo 	fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab);
1036ab05f83Stholo 	FNR = &fnrloc->fval;
1046ab05f83Stholo 	SUBSEP = &setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab)->sval;
1056ab05f83Stholo 	rstartloc = setsymtab("RSTART", "", 0.0, NUM, symtab);
1066ab05f83Stholo 	RSTART = &rstartloc->fval;
1076ab05f83Stholo 	rlengthloc = setsymtab("RLENGTH", "", 0.0, NUM, symtab);
1086ab05f83Stholo 	RLENGTH = &rlengthloc->fval;
1096ab05f83Stholo 	symtabloc = setsymtab("SYMTAB", "", 0.0, ARR, symtab);
1106ab05f83Stholo 	symtabloc->sval = (char *) symtab;
1116ab05f83Stholo }
1126ab05f83Stholo 
11307edfa4aSkstailey void arginit(int ac, char **av)	/* set up ARGV and ARGC */
1146ab05f83Stholo {
1156ab05f83Stholo 	Cell *cp;
1166ab05f83Stholo 	int i;
11707edfa4aSkstailey 	char temp[50];
1186ab05f83Stholo 
1196ab05f83Stholo 	ARGC = &setsymtab("ARGC", "", (Awkfloat) ac, NUM, symtab)->fval;
1206ab05f83Stholo 	cp = setsymtab("ARGV", "", 0.0, ARR, symtab);
1216ab05f83Stholo 	ARGVtab = makesymtab(NSYMTAB);	/* could be (int) ARGC as well */
1226ab05f83Stholo 	cp->sval = (char *) ARGVtab;
1236ab05f83Stholo 	for (i = 0; i < ac; i++) {
1249e405e78Sderaadt 		snprintf(temp, sizeof temp, "%d", i);
125a4fa8700Smillert 		if (is_number(*av))
1266ab05f83Stholo 			setsymtab(temp, *av, atof(*av), STR|NUM, ARGVtab);
1276ab05f83Stholo 		else
1286ab05f83Stholo 			setsymtab(temp, *av, 0.0, STR, ARGVtab);
1296ab05f83Stholo 		av++;
1306ab05f83Stholo 	}
1316ab05f83Stholo }
1326ab05f83Stholo 
1336ab05f83Stholo void envinit(char **envp)	/* set up ENVIRON variable */
1346ab05f83Stholo {
1356ab05f83Stholo 	Cell *cp;
1366ab05f83Stholo 	char *p;
1376ab05f83Stholo 
1386ab05f83Stholo 	cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab);
1396ab05f83Stholo 	ENVtab = makesymtab(NSYMTAB);
1406ab05f83Stholo 	cp->sval = (char *) ENVtab;
1416ab05f83Stholo 	for ( ; *envp; envp++) {
14207edfa4aSkstailey 		if ((p = strchr(*envp, '=')) == NULL)
1436ab05f83Stholo 			continue;
144a27f5228Smillert 		if( p == *envp ) /* no left hand side name in env string */
145a27f5228Smillert 			continue;
1466ab05f83Stholo 		*p++ = 0;	/* split into two strings at = */
147a4fa8700Smillert 		if (is_number(p))
1486ab05f83Stholo 			setsymtab(*envp, p, atof(p), STR|NUM, ENVtab);
1496ab05f83Stholo 		else
1506ab05f83Stholo 			setsymtab(*envp, p, 0.0, STR, ENVtab);
1516ab05f83Stholo 		p[-1] = '=';	/* restore in case env is passed down to a shell */
1526ab05f83Stholo 	}
1536ab05f83Stholo }
1546ab05f83Stholo 
1556ab05f83Stholo Array *makesymtab(int n)	/* make a new symbol table */
1566ab05f83Stholo {
1576ab05f83Stholo 	Array *ap;
1586ab05f83Stholo 	Cell **tp;
1596ab05f83Stholo 
1606ab05f83Stholo 	ap = (Array *) malloc(sizeof(Array));
1616ab05f83Stholo 	tp = (Cell **) calloc(n, sizeof(Cell *));
1626ab05f83Stholo 	if (ap == NULL || tp == NULL)
1637b11b857Smillert 		FATAL("out of space in makesymtab");
1646ab05f83Stholo 	ap->nelem = 0;
1656ab05f83Stholo 	ap->size = n;
1666ab05f83Stholo 	ap->tab = tp;
1676ab05f83Stholo 	return(ap);
1686ab05f83Stholo }
1696ab05f83Stholo 
1706ab05f83Stholo void freesymtab(Cell *ap)	/* free a symbol table */
1716ab05f83Stholo {
1726ab05f83Stholo 	Cell *cp, *temp;
1736ab05f83Stholo 	Array *tp;
1746ab05f83Stholo 	int i;
1756ab05f83Stholo 
1766ab05f83Stholo 	if (!isarr(ap))
1776ab05f83Stholo 		return;
1786ab05f83Stholo 	tp = (Array *) ap->sval;
1796ab05f83Stholo 	if (tp == NULL)
1806ab05f83Stholo 		return;
1816ab05f83Stholo 	for (i = 0; i < tp->size; i++) {
1826ab05f83Stholo 		for (cp = tp->tab[i]; cp != NULL; cp = temp) {
1836ab05f83Stholo 			xfree(cp->nval);
1846ab05f83Stholo 			if (freeable(cp))
1856ab05f83Stholo 				xfree(cp->sval);
1866ab05f83Stholo 			temp = cp->cnext;	/* avoids freeing then using */
18707edfa4aSkstailey 			free(cp);
1889a69093aSmillert 			tp->nelem--;
1896ab05f83Stholo 		}
1906ab05f83Stholo 		tp->tab[i] = 0;
1916ab05f83Stholo 	}
1929a69093aSmillert 	if (tp->nelem != 0)
1939a69093aSmillert 		WARNING("can't happen: inconsistent element count freeing %s", ap->nval);
19407edfa4aSkstailey 	free(tp->tab);
19507edfa4aSkstailey 	free(tp);
1966ab05f83Stholo }
1976ab05f83Stholo 
1989a69093aSmillert void freeelem(Cell *ap, const char *s)	/* free elem s from ap (i.e., ap["s"] */
1996ab05f83Stholo {
2006ab05f83Stholo 	Array *tp;
2016ab05f83Stholo 	Cell *p, *prev = NULL;
2026ab05f83Stholo 	int h;
2036ab05f83Stholo 
2046ab05f83Stholo 	tp = (Array *) ap->sval;
2056ab05f83Stholo 	h = hash(s, tp->size);
2066ab05f83Stholo 	for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext)
20707edfa4aSkstailey 		if (strcmp(s, p->nval) == 0) {
2086ab05f83Stholo 			if (prev == NULL)	/* 1st one */
2096ab05f83Stholo 				tp->tab[h] = p->cnext;
2106ab05f83Stholo 			else			/* middle somewhere */
2116ab05f83Stholo 				prev->cnext = p->cnext;
2126ab05f83Stholo 			if (freeable(p))
2136ab05f83Stholo 				xfree(p->sval);
2146ab05f83Stholo 			free(p->nval);
21507edfa4aSkstailey 			free(p);
2166ab05f83Stholo 			tp->nelem--;
2176ab05f83Stholo 			return;
2186ab05f83Stholo 		}
2196ab05f83Stholo }
2206ab05f83Stholo 
2219a69093aSmillert Cell *setsymtab(const char *n, const char *s, Awkfloat f, unsigned t, Array *tp)
2226ab05f83Stholo {
2236ab05f83Stholo 	int h;
2246ab05f83Stholo 	Cell *p;
2256ab05f83Stholo 
2266ab05f83Stholo 	if (n != NULL && (p = lookup(n, tp)) != NULL) {
2275dd7c43cSderaadt 		   DPRINTF( ("setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n",
228000399a4Smillert 			(void*)p, NN(p->nval), NN(p->sval), p->fval, p->tval) );
2296ab05f83Stholo 		return(p);
2306ab05f83Stholo 	}
2316ab05f83Stholo 	p = (Cell *) malloc(sizeof(Cell));
2326ab05f83Stholo 	if (p == NULL)
2337b11b857Smillert 		FATAL("out of space for symbol table at %s", n);
2346ab05f83Stholo 	p->nval = tostring(n);
2356ab05f83Stholo 	p->sval = s ? tostring(s) : tostring("");
2366ab05f83Stholo 	p->fval = f;
2376ab05f83Stholo 	p->tval = t;
2386ab05f83Stholo 	p->csub = CUNK;
2396ab05f83Stholo 	p->ctype = OCELL;
2406ab05f83Stholo 	tp->nelem++;
2416ab05f83Stholo 	if (tp->nelem > FULLTAB * tp->size)
2426ab05f83Stholo 		rehash(tp);
2436ab05f83Stholo 	h = hash(n, tp->size);
2446ab05f83Stholo 	p->cnext = tp->tab[h];
2456ab05f83Stholo 	tp->tab[h] = p;
2465dd7c43cSderaadt 	   DPRINTF( ("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n",
247000399a4Smillert 		(void*)p, p->nval, p->sval, p->fval, p->tval) );
2486ab05f83Stholo 	return(p);
2496ab05f83Stholo }
2506ab05f83Stholo 
2519a69093aSmillert int hash(const char *s, int n)	/* form hash value for string s */
2526ab05f83Stholo {
2536ab05f83Stholo 	unsigned hashval;
2546ab05f83Stholo 
2556ab05f83Stholo 	for (hashval = 0; *s != '\0'; s++)
2566ab05f83Stholo 		hashval = (*s + 31 * hashval);
2576ab05f83Stholo 	return hashval % n;
2586ab05f83Stholo }
2596ab05f83Stholo 
2606ab05f83Stholo void rehash(Array *tp)	/* rehash items in small table into big one */
2616ab05f83Stholo {
2626ab05f83Stholo 	int i, nh, nsz;
2636ab05f83Stholo 	Cell *cp, *op, **np;
2646ab05f83Stholo 
2656ab05f83Stholo 	nsz = GROWTAB * tp->size;
2666ab05f83Stholo 	np = (Cell **) calloc(nsz, sizeof(Cell *));
2676ab05f83Stholo 	if (np == NULL)		/* can't do it, but can keep running. */
2686ab05f83Stholo 		return;		/* someone else will run out later. */
2696ab05f83Stholo 	for (i = 0; i < tp->size; i++) {
2706ab05f83Stholo 		for (cp = tp->tab[i]; cp; cp = op) {
2716ab05f83Stholo 			op = cp->cnext;
2726ab05f83Stholo 			nh = hash(cp->nval, nsz);
2736ab05f83Stholo 			cp->cnext = np[nh];
2746ab05f83Stholo 			np[nh] = cp;
2756ab05f83Stholo 		}
2766ab05f83Stholo 	}
27707edfa4aSkstailey 	free(tp->tab);
2786ab05f83Stholo 	tp->tab = np;
2796ab05f83Stholo 	tp->size = nsz;
2806ab05f83Stholo }
2816ab05f83Stholo 
2829a69093aSmillert Cell *lookup(const char *s, Array *tp)	/* look for s in tp */
2836ab05f83Stholo {
284271018d0Smillert 	Cell *p;
2856ab05f83Stholo 	int h;
2866ab05f83Stholo 
2876ab05f83Stholo 	h = hash(s, tp->size);
288271018d0Smillert 	for (p = tp->tab[h]; p != NULL; p = p->cnext)
28907edfa4aSkstailey 		if (strcmp(s, p->nval) == 0)
2906ab05f83Stholo 			return(p);	/* found it */
2916ab05f83Stholo 	return(NULL);			/* not found */
2926ab05f83Stholo }
2936ab05f83Stholo 
2946ab05f83Stholo Awkfloat setfval(Cell *vp, Awkfloat f)	/* set float val of a Cell */
2956ab05f83Stholo {
29607edfa4aSkstailey 	int fldno;
29707edfa4aSkstailey 
298*c062391aSmillert 	f += 0.0;		/* normalise negative zero to positive zero */
2996ab05f83Stholo 	if ((vp->tval & (NUM | STR)) == 0)
3006ab05f83Stholo 		funnyvar(vp, "assign to");
30107edfa4aSkstailey 	if (isfld(vp)) {
3026ab05f83Stholo 		donerec = 0;	/* mark $0 invalid */
30307edfa4aSkstailey 		fldno = atoi(vp->nval);
30407edfa4aSkstailey 		if (fldno > *NF)
30507edfa4aSkstailey 			newfld(fldno);
3065dd7c43cSderaadt 		   DPRINTF( ("setting field %d to %g\n", fldno, f) );
307*c062391aSmillert 	} else if (&vp->fval == NF) {
308*c062391aSmillert 		donerec = 0;	/* mark $0 invalid */
309*c062391aSmillert 		setlastfld(f);
310*c062391aSmillert 		DPRINTF( ("setting NF to %g\n", f) );
31107edfa4aSkstailey 	} else if (isrec(vp)) {
3126ab05f83Stholo 		donefld = 0;	/* mark $1... invalid */
3136ab05f83Stholo 		donerec = 1;
3146ab05f83Stholo 	}
315a4fa8700Smillert 	if (freeable(vp))
316a4fa8700Smillert 		xfree(vp->sval); /* free any previous string */
317*c062391aSmillert 	vp->tval &= ~(STR|CONVC|CONVO); /* mark string invalid */
318*c062391aSmillert 	vp->fmt = NULL;
3196ab05f83Stholo 	vp->tval |= NUM;	/* mark number ok */
320a8d6f668Smillert 	if (f == -0)  /* who would have thought this possible? */
321a8d6f668Smillert 		f = 0;
3225dd7c43cSderaadt 	   DPRINTF( ("setfval %p: %s = %g, t=%o\n", (void*)vp, NN(vp->nval), f, vp->tval) );
3236ab05f83Stholo 	return vp->fval = f;
3246ab05f83Stholo }
3256ab05f83Stholo 
3269a69093aSmillert void funnyvar(Cell *vp, const char *rw)
3276ab05f83Stholo {
32807edfa4aSkstailey 	if (isarr(vp))
3297b11b857Smillert 		FATAL("can't %s %s; it's an array name.", rw, vp->nval);
3306ab05f83Stholo 	if (vp->tval & FCN)
3317b11b857Smillert 		FATAL("can't %s %s; it's a function.", rw, vp->nval);
3327b11b857Smillert 	WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o",
3337b11b857Smillert 		vp, vp->nval, vp->sval, vp->fval, vp->tval);
3346ab05f83Stholo }
3356ab05f83Stholo 
3369a69093aSmillert char *setsval(Cell *vp, const char *s)	/* set string val of a Cell */
3376ab05f83Stholo {
3386ab05f83Stholo 	char *t;
33907edfa4aSkstailey 	int fldno;
340*c062391aSmillert 	Awkfloat f;
3416ab05f83Stholo 
3425dd7c43cSderaadt 	   DPRINTF( ("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n",
343000399a4Smillert 		(void*)vp, NN(vp->nval), s, vp->tval, donerec, donefld) );
3446ab05f83Stholo 	if ((vp->tval & (NUM | STR)) == 0)
3456ab05f83Stholo 		funnyvar(vp, "assign to");
34607edfa4aSkstailey 	if (isfld(vp)) {
3476ab05f83Stholo 		donerec = 0;	/* mark $0 invalid */
34807edfa4aSkstailey 		fldno = atoi(vp->nval);
34907edfa4aSkstailey 		if (fldno > *NF)
35007edfa4aSkstailey 			newfld(fldno);
3515dd7c43cSderaadt 		   DPRINTF( ("setting field %d to %s (%p)\n", fldno, s, s) );
35207edfa4aSkstailey 	} else if (isrec(vp)) {
3536ab05f83Stholo 		donefld = 0;	/* mark $1... invalid */
3546ab05f83Stholo 		donerec = 1;
355*c062391aSmillert 	} else if (&vp->sval == OFS) {
356*c062391aSmillert 		if (donerec == 0)
357*c062391aSmillert 			recbld();
3586ab05f83Stholo 	}
359*c062391aSmillert 	t = s ? tostring(s) : tostring("");	/* in case it's self-assign */
3606ab05f83Stholo 	if (freeable(vp))
3616ab05f83Stholo 		xfree(vp->sval);
362*c062391aSmillert 	vp->tval &= ~(NUM|CONVC|CONVO);
3634eb91dacSjmc 	vp->tval |= STR;
364*c062391aSmillert 	vp->fmt = NULL;
365*c062391aSmillert 	setfree(vp);
3665dd7c43cSderaadt 	   DPRINTF( ("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n",
367000399a4Smillert 		(void*)vp, NN(vp->nval), t,t, vp->tval, donerec, donefld) );
368*c062391aSmillert 	vp->sval = t;
369*c062391aSmillert 	if (&vp->fval == NF) {
370*c062391aSmillert 		donerec = 0;	/* mark $0 invalid */
371*c062391aSmillert 		f = getfval(vp);
372*c062391aSmillert 		setlastfld(f);
373*c062391aSmillert 		DPRINTF( ("setting NF to %g\n", f) );
374*c062391aSmillert 	}
375*c062391aSmillert 
376*c062391aSmillert 	return(vp->sval);
3776ab05f83Stholo }
3786ab05f83Stholo 
3796ab05f83Stholo Awkfloat getfval(Cell *vp)	/* get float val of a Cell */
3806ab05f83Stholo {
3816ab05f83Stholo 	if ((vp->tval & (NUM | STR)) == 0)
3826ab05f83Stholo 		funnyvar(vp, "read value of");
38307edfa4aSkstailey 	if (isfld(vp) && donefld == 0)
3846ab05f83Stholo 		fldbld();
38507edfa4aSkstailey 	else if (isrec(vp) && donerec == 0)
3866ab05f83Stholo 		recbld();
3876ab05f83Stholo 	if (!isnum(vp)) {	/* not a number */
3886ab05f83Stholo 		vp->fval = atof(vp->sval);	/* best guess */
389a4fa8700Smillert 		if (is_number(vp->sval) && !(vp->tval&CON))
3906ab05f83Stholo 			vp->tval |= NUM;	/* make NUM only sparingly */
3916ab05f83Stholo 	}
3925dd7c43cSderaadt 	   DPRINTF( ("getfval %p: %s = %g, t=%o\n",
393000399a4Smillert 		(void*)vp, NN(vp->nval), vp->fval, vp->tval) );
3946ab05f83Stholo 	return(vp->fval);
3956ab05f83Stholo }
3966ab05f83Stholo 
3979a69093aSmillert static char *get_str_val(Cell *vp, char **fmt)        /* get string val of a Cell */
3986ab05f83Stholo {
399a4a48c73Smillert 	int n;
4006ab05f83Stholo 	double dtemp;
4016ab05f83Stholo 
4026ab05f83Stholo 	if ((vp->tval & (NUM | STR)) == 0)
4036ab05f83Stholo 		funnyvar(vp, "read value of");
40407edfa4aSkstailey 	if (isfld(vp) && donefld == 0)
4056ab05f83Stholo 		fldbld();
40607edfa4aSkstailey 	else if (isrec(vp) && donerec == 0)
4076ab05f83Stholo 		recbld();
408*c062391aSmillert 
409*c062391aSmillert 	/*
410*c062391aSmillert 	 * ADR: This is complicated and more fragile than is desirable.
411*c062391aSmillert 	 * Retrieving a string value for a number associates the string
412*c062391aSmillert 	 * value with the scalar.  Previously, the string value was
413*c062391aSmillert 	 * sticky, meaning if converted via OFMT that became the value
414*c062391aSmillert 	 * (even though POSIX wants it to be via CONVFMT). Or if CONVFMT
415*c062391aSmillert 	 * changed after a string value was retrieved, the original value
416*c062391aSmillert 	 * was maintained and used.  Also not per POSIX.
417*c062391aSmillert 	 *
418*c062391aSmillert 	 * We work around this design by adding two additional flags,
419*c062391aSmillert 	 * CONVC and CONVO, indicating how the string value was
420*c062391aSmillert 	 * obtained (via CONVFMT or OFMT) and _also_ maintaining a copy
421*c062391aSmillert 	 * of the pointer to the xFMT format string used for the
422*c062391aSmillert 	 * conversion.  This pointer is only read, **never** dereferenced.
423*c062391aSmillert 	 * The next time we do a conversion, if it's coming from the same
424*c062391aSmillert 	 * xFMT as last time, and the pointer value is different, we
425*c062391aSmillert 	 * know that the xFMT format string changed, and we need to
426*c062391aSmillert 	 * redo the conversion. If it's the same, we don't have to.
427*c062391aSmillert 	 *
428*c062391aSmillert 	 * There are also several cases where we don't do a conversion,
429*c062391aSmillert 	 * such as for a field (see the checks below).
430*c062391aSmillert 	 */
431*c062391aSmillert 
432*c062391aSmillert 	/* Don't duplicate the code for actually updating the value */
433*c062391aSmillert #define update_str_val(vp) \
434*c062391aSmillert 	{ \
435*c062391aSmillert 		if (freeable(vp)) \
436*c062391aSmillert 			xfree(vp->sval); \
437*c062391aSmillert 		if (modf(vp->fval, &dtemp) == 0)	/* it's integral */ \
438*c062391aSmillert 			n = asprintf(&vp->sval, "%.30g", vp->fval); \
439*c062391aSmillert 		else \
440*c062391aSmillert 			n = asprintf(&vp->sval, *fmt, vp->fval); \
441*c062391aSmillert 		if (n == -1) \
442*c062391aSmillert 			FATAL("out of space in get_str_val"); \
443*c062391aSmillert 		vp->tval &= ~DONTFREE; \
444*c062391aSmillert 		vp->tval |= STR; \
4456ab05f83Stholo 	}
446*c062391aSmillert 
447*c062391aSmillert 	if (isstr(vp) == 0) {
448*c062391aSmillert 		update_str_val(vp);
449*c062391aSmillert 		if (fmt == OFMT) {
450*c062391aSmillert 			vp->tval &= ~CONVC;
451*c062391aSmillert 			vp->tval |= CONVO;
452*c062391aSmillert 		} else {
453*c062391aSmillert 			/* CONVFMT */
454*c062391aSmillert 			vp->tval &= ~CONVO;
455*c062391aSmillert 			vp->tval |= CONVC;
456*c062391aSmillert 		}
457*c062391aSmillert 		vp->fmt = *fmt;
458*c062391aSmillert 	} else if ((vp->tval & DONTFREE) != 0 || ! isnum(vp) || isfld(vp)) {
459*c062391aSmillert 		goto done;
460*c062391aSmillert 	} else if (isstr(vp)) {
461*c062391aSmillert 		if (fmt == OFMT) {
462*c062391aSmillert 			if ((vp->tval & CONVC) != 0
463*c062391aSmillert 			    || ((vp->tval & CONVO) != 0 && vp->fmt != *fmt)) {
464*c062391aSmillert 				update_str_val(vp);
465*c062391aSmillert 				vp->tval &= ~CONVC;
466*c062391aSmillert 				vp->tval |= CONVO;
467*c062391aSmillert 				vp->fmt = *fmt;
468*c062391aSmillert 			}
469*c062391aSmillert 		} else {
470*c062391aSmillert 			/* CONVFMT */
471*c062391aSmillert 			if ((vp->tval & CONVO) != 0
472*c062391aSmillert 			    || ((vp->tval & CONVC) != 0 && vp->fmt != *fmt)) {
473*c062391aSmillert 				update_str_val(vp);
474*c062391aSmillert 				vp->tval &= ~CONVO;
475*c062391aSmillert 				vp->tval |= CONVC;
476*c062391aSmillert 				vp->fmt = *fmt;
477*c062391aSmillert 			}
478*c062391aSmillert 		}
479*c062391aSmillert 	}
480*c062391aSmillert done:
4815dd7c43cSderaadt 	   DPRINTF( ("getsval %p: %s = \"%s (%p)\", t=%o\n",
482000399a4Smillert 		(void*)vp, NN(vp->nval), vp->sval, vp->sval, vp->tval) );
4836ab05f83Stholo 	return(vp->sval);
4846ab05f83Stholo }
4856ab05f83Stholo 
4869a69093aSmillert char *getsval(Cell *vp)       /* get string val of a Cell */
4879a69093aSmillert {
4889a69093aSmillert       return get_str_val(vp, CONVFMT);
4899a69093aSmillert }
4909a69093aSmillert 
4919a69093aSmillert char *getpssval(Cell *vp)     /* get string val of a Cell for print */
4929a69093aSmillert {
4939a69093aSmillert       return get_str_val(vp, OFMT);
4949a69093aSmillert }
4959a69093aSmillert 
4969a69093aSmillert 
4979a69093aSmillert char *tostring(const char *s)	/* make a copy of string s */
4986ab05f83Stholo {
4998055ea94Smillert 	char *p;
5008055ea94Smillert 
5018055ea94Smillert 	p = strdup(s);
5028055ea94Smillert 	if (p == NULL)
5038055ea94Smillert 		FATAL("out of space in tostring on %s", s);
5048055ea94Smillert 	return p;
5056ab05f83Stholo }
5066ab05f83Stholo 
5079a69093aSmillert char *qstring(const char *is, int delim)	/* collect string up to next delim */
5086ab05f83Stholo {
5099a69093aSmillert 	const char *os = is;
5106ab05f83Stholo 	int c, n;
511a27f5228Smillert 	uschar *s = (uschar *) is;
512a27f5228Smillert 	uschar *buf, *bp;
5136ab05f83Stholo 
5149a69093aSmillert 	if ((buf = (uschar *) malloc(strlen(is)+3)) == NULL)
5157b11b857Smillert 		FATAL( "out of space in qstring(%s)", s);
51607edfa4aSkstailey 	for (bp = buf; (c = *s) != delim; s++) {
5176ab05f83Stholo 		if (c == '\n')
5187b11b857Smillert 			SYNTAX( "newline in string %.20s...", os );
5196ab05f83Stholo 		else if (c != '\\')
52007edfa4aSkstailey 			*bp++ = c;
52107edfa4aSkstailey 		else {	/* \something */
5227b11b857Smillert 			c = *++s;
5237b11b857Smillert 			if (c == 0) {	/* \ at end */
5247b11b857Smillert 				*bp++ = '\\';
5257b11b857Smillert 				break;	/* for loop */
5267b11b857Smillert 			}
5277b11b857Smillert 			switch (c) {
52807edfa4aSkstailey 			case '\\':	*bp++ = '\\'; break;
52907edfa4aSkstailey 			case 'n':	*bp++ = '\n'; break;
53007edfa4aSkstailey 			case 't':	*bp++ = '\t'; break;
5317bcc45acSmillert 			case 'v':	*bp++ = '\v'; break;
53207edfa4aSkstailey 			case 'b':	*bp++ = '\b'; break;
53307edfa4aSkstailey 			case 'f':	*bp++ = '\f'; break;
53407edfa4aSkstailey 			case 'r':	*bp++ = '\r'; break;
5357bcc45acSmillert 			case 'a':	*bp++ = '\007'; break;
5366ab05f83Stholo 			default:
5376ab05f83Stholo 				if (!isdigit(c)) {
53807edfa4aSkstailey 					*bp++ = c;
5396ab05f83Stholo 					break;
5406ab05f83Stholo 				}
5416ab05f83Stholo 				n = c - '0';
5426ab05f83Stholo 				if (isdigit(s[1])) {
5436ab05f83Stholo 					n = 8 * n + *++s - '0';
5446ab05f83Stholo 					if (isdigit(s[1]))
5456ab05f83Stholo 						n = 8 * n + *++s - '0';
5466ab05f83Stholo 				}
54707edfa4aSkstailey 				*bp++ = n;
5486ab05f83Stholo 				break;
5496ab05f83Stholo 			}
5506ab05f83Stholo 		}
55107edfa4aSkstailey 	}
55207edfa4aSkstailey 	*bp++ = 0;
553a27f5228Smillert 	return (char *) buf;
5546ab05f83Stholo }
555*c062391aSmillert 
556*c062391aSmillert const char *flags2str(int flags)
557*c062391aSmillert {
558*c062391aSmillert 	static const struct ftab {
559*c062391aSmillert 		const char *name;
560*c062391aSmillert 		int value;
561*c062391aSmillert 	} flagtab[] = {
562*c062391aSmillert 		{ "NUM", NUM },
563*c062391aSmillert 		{ "STR", STR },
564*c062391aSmillert 		{ "DONTFREE", DONTFREE },
565*c062391aSmillert 		{ "CON", CON },
566*c062391aSmillert 		{ "ARR", ARR },
567*c062391aSmillert 		{ "FCN", FCN },
568*c062391aSmillert 		{ "FLD", FLD },
569*c062391aSmillert 		{ "REC", REC },
570*c062391aSmillert 		{ "CONVC", CONVC },
571*c062391aSmillert 		{ "CONVO", CONVO },
572*c062391aSmillert 		{ NULL, 0 }
573*c062391aSmillert 	};
574*c062391aSmillert 	static char buf[100];
575*c062391aSmillert 	int i, len;
576*c062391aSmillert 	char *cp = buf;
577*c062391aSmillert 
578*c062391aSmillert 	for (i = 0; flagtab[i].name != NULL; i++) {
579*c062391aSmillert 		if ((flags & flagtab[i].value) != 0) {
580*c062391aSmillert 			len = snprintf(cp, sizeof(buf) - (cp - buf),
581*c062391aSmillert 			    "%s%s", cp > buf ? "|" : "", flagtab[i].name);
582*c062391aSmillert 			if (len < 0 || len >= sizeof(buf) - (cp - buf))
583*c062391aSmillert 				FATAL("out of space in flags2str");
584*c062391aSmillert 			cp += len;
585*c062391aSmillert 		}
586*c062391aSmillert 	}
587*c062391aSmillert 
588*c062391aSmillert 	return buf;
589*c062391aSmillert }
590