xref: /openbsd/usr.bin/awk/tran.c (revision f190fa91)
1*f190fa91Smillert /*	$OpenBSD: tran.c,v 1.36 2022/09/21 01:42:59 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"
336ab05f83Stholo 
346ab05f83Stholo #define	FULLTAB	2	/* rehash when table gets this x full */
356ab05f83Stholo #define	GROWTAB 4	/* grow table by this factor */
366ab05f83Stholo 
376ab05f83Stholo Array	*symtab;	/* main symbol table */
386ab05f83Stholo 
396ab05f83Stholo char	**FS;		/* initial field sep */
406ab05f83Stholo char	**RS;		/* initial record sep */
416ab05f83Stholo char	**OFS;		/* output field sep */
426ab05f83Stholo char	**ORS;		/* output record sep */
436ab05f83Stholo char	**OFMT;		/* output format for numbers */
446ab05f83Stholo char	**CONVFMT;	/* format for conversions in getsval */
456ab05f83Stholo Awkfloat *NF;		/* number of fields in current record */
466ab05f83Stholo Awkfloat *NR;		/* number of current record */
476ab05f83Stholo Awkfloat *FNR;		/* number of current record in current file */
486ab05f83Stholo char	**FILENAME;	/* current filename argument */
496ab05f83Stholo Awkfloat *ARGC;		/* number of arguments from command line */
506ab05f83Stholo char	**SUBSEP;	/* subscript separator for a[i,j,k]; default \034 */
516ab05f83Stholo Awkfloat *RSTART;	/* start of re matched with ~; origin 1 (!) */
526ab05f83Stholo Awkfloat *RLENGTH;	/* length of same */
536ab05f83Stholo 
5423cb51abSmillert Cell	*fsloc;		/* FS */
556ab05f83Stholo Cell	*nrloc;		/* NR */
566ab05f83Stholo Cell	*nfloc;		/* NF */
576ab05f83Stholo Cell	*fnrloc;	/* FNR */
5802265e66Smillert Cell	*ofsloc;	/* OFS */
5902265e66Smillert Cell	*orsloc;	/* ORS */
6002265e66Smillert Cell	*rsloc;		/* RS */
616ab05f83Stholo Array	*ARGVtab;	/* symbol table containing ARGV[...] */
626ab05f83Stholo Array	*ENVtab;	/* symbol table containing ENVIRON[...] */
636ab05f83Stholo Cell	*rstartloc;	/* RSTART */
646ab05f83Stholo Cell	*rlengthloc;	/* RLENGTH */
6502265e66Smillert Cell	*subseploc;	/* SUBSEP */
666ab05f83Stholo Cell	*symtabloc;	/* SYMTAB */
676ab05f83Stholo 
686ab05f83Stholo Cell	*nullloc;	/* a guaranteed empty cell */
696ab05f83Stholo Node	*nullnode;	/* zero&null, converted into a node for comparisons */
7007edfa4aSkstailey Cell	*literal0;
716ab05f83Stholo 
7207edfa4aSkstailey extern Cell **fldtab;
736ab05f83Stholo 
746ab05f83Stholo void syminit(void)	/* initialize symbol table with builtin vars */
756ab05f83Stholo {
7607edfa4aSkstailey 	literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab);
776ab05f83Stholo 	/* this is used for if(x)... tests: */
786ab05f83Stholo 	nullloc = setsymtab("$zero&null", "", 0.0, NUM|STR|CON|DONTFREE, symtab);
7907edfa4aSkstailey 	nullnode = celltonode(nullloc, CCON);
806ab05f83Stholo 
8123cb51abSmillert 	fsloc = setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab);
8223cb51abSmillert 	FS = &fsloc->sval;
8302265e66Smillert 	rsloc = setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab);
8402265e66Smillert 	RS = &rsloc->sval;
8502265e66Smillert 	ofsloc = setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab);
8602265e66Smillert 	OFS = &ofsloc->sval;
8702265e66Smillert 	orsloc = setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab);
8802265e66Smillert 	ORS = &orsloc->sval;
896ab05f83Stholo 	OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
906ab05f83Stholo 	CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
91b2698ba9Smillert 	FILENAME = &setsymtab("FILENAME", "", 0.0, STR|DONTFREE, symtab)->sval;
926ab05f83Stholo 	nfloc = setsymtab("NF", "", 0.0, NUM, symtab);
936ab05f83Stholo 	NF = &nfloc->fval;
946ab05f83Stholo 	nrloc = setsymtab("NR", "", 0.0, NUM, symtab);
956ab05f83Stholo 	NR = &nrloc->fval;
966ab05f83Stholo 	fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab);
976ab05f83Stholo 	FNR = &fnrloc->fval;
9802265e66Smillert 	subseploc = setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab);
9902265e66Smillert 	SUBSEP = &subseploc->sval;
1006ab05f83Stholo 	rstartloc = setsymtab("RSTART", "", 0.0, NUM, symtab);
1016ab05f83Stholo 	RSTART = &rstartloc->fval;
1026ab05f83Stholo 	rlengthloc = setsymtab("RLENGTH", "", 0.0, NUM, symtab);
1036ab05f83Stholo 	RLENGTH = &rlengthloc->fval;
1046ab05f83Stholo 	symtabloc = setsymtab("SYMTAB", "", 0.0, ARR, symtab);
10592497129Smillert 	free(symtabloc->sval);
1066ab05f83Stholo 	symtabloc->sval = (char *) symtab;
1076ab05f83Stholo }
1086ab05f83Stholo 
10907edfa4aSkstailey void arginit(int ac, char **av)	/* set up ARGV and ARGC */
1106ab05f83Stholo {
1116ab05f83Stholo 	Cell *cp;
1126ab05f83Stholo 	int i;
11307edfa4aSkstailey 	char temp[50];
1146ab05f83Stholo 
1156ab05f83Stholo 	ARGC = &setsymtab("ARGC", "", (Awkfloat) ac, NUM, symtab)->fval;
1166ab05f83Stholo 	cp = setsymtab("ARGV", "", 0.0, ARR, symtab);
1176ab05f83Stholo 	ARGVtab = makesymtab(NSYMTAB);	/* could be (int) ARGC as well */
11892497129Smillert 	free(cp->sval);
1196ab05f83Stholo 	cp->sval = (char *) ARGVtab;
1206ab05f83Stholo 	for (i = 0; i < ac; i++) {
121483fa115Smillert 		double result;
122483fa115Smillert 
1239e405e78Sderaadt 		snprintf(temp, sizeof temp, "%d", i);
124483fa115Smillert 		if (is_number(*av, & result))
125483fa115Smillert 			setsymtab(temp, *av, result, STR|NUM, ARGVtab);
1266ab05f83Stholo 		else
1276ab05f83Stholo 			setsymtab(temp, *av, 0.0, STR, ARGVtab);
1286ab05f83Stholo 		av++;
1296ab05f83Stholo 	}
1306ab05f83Stholo }
1316ab05f83Stholo 
1326ab05f83Stholo void envinit(char **envp)	/* set up ENVIRON variable */
1336ab05f83Stholo {
1346ab05f83Stholo 	Cell *cp;
1356ab05f83Stholo 	char *p;
1366ab05f83Stholo 
1376ab05f83Stholo 	cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab);
1386ab05f83Stholo 	ENVtab = makesymtab(NSYMTAB);
13992497129Smillert 	free(cp->sval);
1406ab05f83Stholo 	cp->sval = (char *) ENVtab;
1416ab05f83Stholo 	for ( ; *envp; envp++) {
142483fa115Smillert 		double result;
143483fa115Smillert 
14407edfa4aSkstailey 		if ((p = strchr(*envp, '=')) == NULL)
1456ab05f83Stholo 			continue;
146a27f5228Smillert 		if( p == *envp ) /* no left hand side name in env string */
147a27f5228Smillert 			continue;
1486ab05f83Stholo 		*p++ = 0;	/* split into two strings at = */
149483fa115Smillert 		if (is_number(p, & result))
150483fa115Smillert 			setsymtab(*envp, p, result, STR|NUM, ENVtab);
1516ab05f83Stholo 		else
1526ab05f83Stholo 			setsymtab(*envp, p, 0.0, STR, ENVtab);
1536ab05f83Stholo 		p[-1] = '=';	/* restore in case env is passed down to a shell */
1546ab05f83Stholo 	}
1556ab05f83Stholo }
1566ab05f83Stholo 
1576ab05f83Stholo Array *makesymtab(int n)	/* make a new symbol table */
1586ab05f83Stholo {
1596ab05f83Stholo 	Array *ap;
1606ab05f83Stholo 	Cell **tp;
1616ab05f83Stholo 
162483fa115Smillert 	ap = (Array *) malloc(sizeof(*ap));
163483fa115Smillert 	tp = (Cell **) calloc(n, sizeof(*tp));
1646ab05f83Stholo 	if (ap == NULL || tp == NULL)
1657b11b857Smillert 		FATAL("out of space in makesymtab");
1666ab05f83Stholo 	ap->nelem = 0;
1676ab05f83Stholo 	ap->size = n;
1686ab05f83Stholo 	ap->tab = tp;
1696ab05f83Stholo 	return(ap);
1706ab05f83Stholo }
1716ab05f83Stholo 
1726ab05f83Stholo void freesymtab(Cell *ap)	/* free a symbol table */
1736ab05f83Stholo {
1746ab05f83Stholo 	Cell *cp, *temp;
1756ab05f83Stholo 	Array *tp;
1766ab05f83Stholo 	int i;
1776ab05f83Stholo 
1786ab05f83Stholo 	if (!isarr(ap))
1796ab05f83Stholo 		return;
1806ab05f83Stholo 	tp = (Array *) ap->sval;
1816ab05f83Stholo 	if (tp == NULL)
1826ab05f83Stholo 		return;
1836ab05f83Stholo 	for (i = 0; i < tp->size; i++) {
1846ab05f83Stholo 		for (cp = tp->tab[i]; cp != NULL; cp = temp) {
1856ab05f83Stholo 			xfree(cp->nval);
1866ab05f83Stholo 			if (freeable(cp))
1876ab05f83Stholo 				xfree(cp->sval);
1886ab05f83Stholo 			temp = cp->cnext;	/* avoids freeing then using */
18907edfa4aSkstailey 			free(cp);
1909a69093aSmillert 			tp->nelem--;
1916ab05f83Stholo 		}
192c0fa3611Smillert 		tp->tab[i] = NULL;
1936ab05f83Stholo 	}
1949a69093aSmillert 	if (tp->nelem != 0)
1959a69093aSmillert 		WARNING("can't happen: inconsistent element count freeing %s", ap->nval);
19607edfa4aSkstailey 	free(tp->tab);
19707edfa4aSkstailey 	free(tp);
1986ab05f83Stholo }
1996ab05f83Stholo 
2009a69093aSmillert void freeelem(Cell *ap, const char *s)	/* free elem s from ap (i.e., ap["s"] */
2016ab05f83Stholo {
2026ab05f83Stholo 	Array *tp;
2036ab05f83Stholo 	Cell *p, *prev = NULL;
2046ab05f83Stholo 	int h;
2056ab05f83Stholo 
2066ab05f83Stholo 	tp = (Array *) ap->sval;
2076ab05f83Stholo 	h = hash(s, tp->size);
2086ab05f83Stholo 	for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext)
20907edfa4aSkstailey 		if (strcmp(s, p->nval) == 0) {
2106ab05f83Stholo 			if (prev == NULL)	/* 1st one */
2116ab05f83Stholo 				tp->tab[h] = p->cnext;
2126ab05f83Stholo 			else			/* middle somewhere */
2136ab05f83Stholo 				prev->cnext = p->cnext;
2146ab05f83Stholo 			if (freeable(p))
2156ab05f83Stholo 				xfree(p->sval);
2166ab05f83Stholo 			free(p->nval);
21707edfa4aSkstailey 			free(p);
2186ab05f83Stholo 			tp->nelem--;
2196ab05f83Stholo 			return;
2206ab05f83Stholo 		}
2216ab05f83Stholo }
2226ab05f83Stholo 
2239a69093aSmillert Cell *setsymtab(const char *n, const char *s, Awkfloat f, unsigned t, Array *tp)
2246ab05f83Stholo {
2256ab05f83Stholo 	int h;
2266ab05f83Stholo 	Cell *p;
2276ab05f83Stholo 
2286ab05f83Stholo 	if (n != NULL && (p = lookup(n, tp)) != NULL) {
229115bd590Smillert 		DPRINTF("setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n",
230115bd590Smillert 			(void*)p, NN(p->nval), NN(p->sval), p->fval, p->tval);
2316ab05f83Stholo 		return(p);
2326ab05f83Stholo 	}
233483fa115Smillert 	p = (Cell *) malloc(sizeof(*p));
2346ab05f83Stholo 	if (p == NULL)
2357b11b857Smillert 		FATAL("out of space for symbol table at %s", n);
2366ab05f83Stholo 	p->nval = tostring(n);
2376ab05f83Stholo 	p->sval = s ? tostring(s) : tostring("");
2386ab05f83Stholo 	p->fval = f;
2396ab05f83Stholo 	p->tval = t;
2406ab05f83Stholo 	p->csub = CUNK;
2416ab05f83Stholo 	p->ctype = OCELL;
2426ab05f83Stholo 	tp->nelem++;
2436ab05f83Stholo 	if (tp->nelem > FULLTAB * tp->size)
2446ab05f83Stholo 		rehash(tp);
2456ab05f83Stholo 	h = hash(n, tp->size);
2466ab05f83Stholo 	p->cnext = tp->tab[h];
2476ab05f83Stholo 	tp->tab[h] = p;
248115bd590Smillert 	DPRINTF("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n",
249115bd590Smillert 		(void*)p, p->nval, p->sval, p->fval, p->tval);
2506ab05f83Stholo 	return(p);
2516ab05f83Stholo }
2526ab05f83Stholo 
2539a69093aSmillert int hash(const char *s, int n)	/* form hash value for string s */
2546ab05f83Stholo {
2556ab05f83Stholo 	unsigned hashval;
2566ab05f83Stholo 
2576ab05f83Stholo 	for (hashval = 0; *s != '\0'; s++)
2586ab05f83Stholo 		hashval = (*s + 31 * hashval);
2596ab05f83Stholo 	return hashval % n;
2606ab05f83Stholo }
2616ab05f83Stholo 
2626ab05f83Stholo void rehash(Array *tp)	/* rehash items in small table into big one */
2636ab05f83Stholo {
2646ab05f83Stholo 	int i, nh, nsz;
2656ab05f83Stholo 	Cell *cp, *op, **np;
2666ab05f83Stholo 
2676ab05f83Stholo 	nsz = GROWTAB * tp->size;
268483fa115Smillert 	np = (Cell **) calloc(nsz, sizeof(*np));
2696ab05f83Stholo 	if (np == NULL)		/* can't do it, but can keep running. */
2706ab05f83Stholo 		return;		/* someone else will run out later. */
2716ab05f83Stholo 	for (i = 0; i < tp->size; i++) {
2726ab05f83Stholo 		for (cp = tp->tab[i]; cp; cp = op) {
2736ab05f83Stholo 			op = cp->cnext;
2746ab05f83Stholo 			nh = hash(cp->nval, nsz);
2756ab05f83Stholo 			cp->cnext = np[nh];
2766ab05f83Stholo 			np[nh] = cp;
2776ab05f83Stholo 		}
2786ab05f83Stholo 	}
27907edfa4aSkstailey 	free(tp->tab);
2806ab05f83Stholo 	tp->tab = np;
2816ab05f83Stholo 	tp->size = nsz;
2826ab05f83Stholo }
2836ab05f83Stholo 
2849a69093aSmillert Cell *lookup(const char *s, Array *tp)	/* look for s in tp */
2856ab05f83Stholo {
286271018d0Smillert 	Cell *p;
2876ab05f83Stholo 	int h;
2886ab05f83Stholo 
2896ab05f83Stholo 	h = hash(s, tp->size);
290271018d0Smillert 	for (p = tp->tab[h]; p != NULL; p = p->cnext)
29107edfa4aSkstailey 		if (strcmp(s, p->nval) == 0)
2926ab05f83Stholo 			return(p);	/* found it */
2936ab05f83Stholo 	return(NULL);			/* not found */
2946ab05f83Stholo }
2956ab05f83Stholo 
2966ab05f83Stholo Awkfloat setfval(Cell *vp, Awkfloat f)	/* set float val of a Cell */
2976ab05f83Stholo {
29807edfa4aSkstailey 	int fldno;
29907edfa4aSkstailey 
300c062391aSmillert 	f += 0.0;		/* normalise negative zero to positive zero */
3016ab05f83Stholo 	if ((vp->tval & (NUM | STR)) == 0)
3026ab05f83Stholo 		funnyvar(vp, "assign to");
30307edfa4aSkstailey 	if (isfld(vp)) {
304f81b289fSmillert 		donerec = false;	/* mark $0 invalid */
30507edfa4aSkstailey 		fldno = atoi(vp->nval);
30607edfa4aSkstailey 		if (fldno > *NF)
30707edfa4aSkstailey 			newfld(fldno);
308115bd590Smillert 		DPRINTF("setting field %d to %g\n", fldno, f);
309c062391aSmillert 	} else if (&vp->fval == NF) {
310f81b289fSmillert 		donerec = false;	/* mark $0 invalid */
311c062391aSmillert 		setlastfld(f);
312115bd590Smillert 		DPRINTF("setting NF to %g\n", f);
31307edfa4aSkstailey 	} else if (isrec(vp)) {
314f81b289fSmillert 		donefld = false;	/* mark $1... invalid */
315f81b289fSmillert 		donerec = true;
316c0fa3611Smillert 		savefs();
31702265e66Smillert 	} else if (vp == ofsloc) {
318f81b289fSmillert 		if (!donerec)
31902265e66Smillert 			recbld();
3206ab05f83Stholo 	}
321a4fa8700Smillert 	if (freeable(vp))
322a4fa8700Smillert 		xfree(vp->sval); /* free any previous string */
323c062391aSmillert 	vp->tval &= ~(STR|CONVC|CONVO); /* mark string invalid */
324c062391aSmillert 	vp->fmt = NULL;
3256ab05f83Stholo 	vp->tval |= NUM;	/* mark number ok */
326a8d6f668Smillert 	if (f == -0)  /* who would have thought this possible? */
327a8d6f668Smillert 		f = 0;
328115bd590Smillert 	DPRINTF("setfval %p: %s = %g, t=%o\n", (void*)vp, NN(vp->nval), f, vp->tval);
3296ab05f83Stholo 	return vp->fval = f;
3306ab05f83Stholo }
3316ab05f83Stholo 
3329a69093aSmillert void funnyvar(Cell *vp, const char *rw)
3336ab05f83Stholo {
33407edfa4aSkstailey 	if (isarr(vp))
3357b11b857Smillert 		FATAL("can't %s %s; it's an array name.", rw, vp->nval);
3366ab05f83Stholo 	if (vp->tval & FCN)
3377b11b857Smillert 		FATAL("can't %s %s; it's a function.", rw, vp->nval);
3387b11b857Smillert 	WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o",
3396685ce51Smillert 		(void *)vp, vp->nval, vp->sval, vp->fval, vp->tval);
3406ab05f83Stholo }
3416ab05f83Stholo 
3429a69093aSmillert char *setsval(Cell *vp, const char *s)	/* set string val of a Cell */
3436ab05f83Stholo {
3446ab05f83Stholo 	char *t;
34507edfa4aSkstailey 	int fldno;
346c062391aSmillert 	Awkfloat f;
3476ab05f83Stholo 
348115bd590Smillert 	DPRINTF("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n",
349115bd590Smillert 		(void*)vp, NN(vp->nval), s, vp->tval, donerec, donefld);
3506ab05f83Stholo 	if ((vp->tval & (NUM | STR)) == 0)
3516ab05f83Stholo 		funnyvar(vp, "assign to");
35207edfa4aSkstailey 	if (isfld(vp)) {
353f81b289fSmillert 		donerec = false;	/* mark $0 invalid */
35407edfa4aSkstailey 		fldno = atoi(vp->nval);
35507edfa4aSkstailey 		if (fldno > *NF)
35607edfa4aSkstailey 			newfld(fldno);
35760613915Smillert 		DPRINTF("setting field %d to %s (%p)\n", fldno, s, (const void*)s);
35807edfa4aSkstailey 	} else if (isrec(vp)) {
359f81b289fSmillert 		donefld = false;	/* mark $1... invalid */
360f81b289fSmillert 		donerec = true;
361c0fa3611Smillert 		savefs();
36202265e66Smillert 	} else if (vp == ofsloc) {
363f81b289fSmillert 		if (!donerec)
364c062391aSmillert 			recbld();
3656ab05f83Stholo 	}
366c062391aSmillert 	t = s ? tostring(s) : tostring("");	/* in case it's self-assign */
3676ab05f83Stholo 	if (freeable(vp))
3686ab05f83Stholo 		xfree(vp->sval);
369a06fd656Smillert 	vp->tval &= ~(NUM|DONTFREE|CONVC|CONVO);
3704eb91dacSjmc 	vp->tval |= STR;
371c062391aSmillert 	vp->fmt = NULL;
372115bd590Smillert 	DPRINTF("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n",
37360613915Smillert 		(void*)vp, NN(vp->nval), t, (void*)t, vp->tval, donerec, donefld);
374c062391aSmillert 	vp->sval = t;
375c062391aSmillert 	if (&vp->fval == NF) {
376f81b289fSmillert 		donerec = false;	/* mark $0 invalid */
377c062391aSmillert 		f = getfval(vp);
378c062391aSmillert 		setlastfld(f);
379115bd590Smillert 		DPRINTF("setting NF to %g\n", f);
380c062391aSmillert 	}
381c062391aSmillert 
382c062391aSmillert 	return(vp->sval);
3836ab05f83Stholo }
3846ab05f83Stholo 
3856ab05f83Stholo Awkfloat getfval(Cell *vp)	/* get float val of a Cell */
3866ab05f83Stholo {
3876ab05f83Stholo 	if ((vp->tval & (NUM | STR)) == 0)
3886ab05f83Stholo 		funnyvar(vp, "read value of");
389f81b289fSmillert 	if (isfld(vp) && !donefld)
3906ab05f83Stholo 		fldbld();
391f81b289fSmillert 	else if (isrec(vp) && !donerec)
3926ab05f83Stholo 		recbld();
3936ab05f83Stholo 	if (!isnum(vp)) {	/* not a number */
394483fa115Smillert 		double fval;
395483fa115Smillert 		bool no_trailing;
396483fa115Smillert 
397483fa115Smillert 		if (is_valid_number(vp->sval, true, & no_trailing, & fval)) {
398483fa115Smillert 			vp->fval = fval;
399483fa115Smillert 			if (no_trailing && !(vp->tval&CON))
4006ab05f83Stholo 				vp->tval |= NUM;	/* make NUM only sparingly */
401483fa115Smillert 		} else
402483fa115Smillert 			vp->fval = 0.0;
4036ab05f83Stholo 	}
404115bd590Smillert 	DPRINTF("getfval %p: %s = %g, t=%o\n",
405115bd590Smillert 		(void*)vp, NN(vp->nval), vp->fval, vp->tval);
4066ab05f83Stholo 	return(vp->fval);
4076ab05f83Stholo }
4086ab05f83Stholo 
409f9297e05Smillert static const char *get_inf_nan(double d)
4106de80fb8Smillert {
4116de80fb8Smillert 	if (isinf(d)) {
4126de80fb8Smillert 		return (d < 0 ? "-inf" : "+inf");
4136de80fb8Smillert 	} else if (isnan(d)) {
4146de80fb8Smillert 		return (signbit(d) != 0 ? "-nan" : "+nan");
4156de80fb8Smillert 	} else
4166de80fb8Smillert 		return NULL;
4176de80fb8Smillert }
4186de80fb8Smillert 
4199a69093aSmillert static char *get_str_val(Cell *vp, char **fmt)        /* get string val of a Cell */
4206ab05f83Stholo {
421a4a48c73Smillert 	int n;
4226ab05f83Stholo 	double dtemp;
423f9297e05Smillert 	const char *p;
4246ab05f83Stholo 
4256ab05f83Stholo 	if ((vp->tval & (NUM | STR)) == 0)
4266ab05f83Stholo 		funnyvar(vp, "read value of");
427f81b289fSmillert 	if (isfld(vp) && ! donefld)
4286ab05f83Stholo 		fldbld();
429f81b289fSmillert 	else if (isrec(vp) && ! donerec)
4306ab05f83Stholo 		recbld();
431c062391aSmillert 
432c062391aSmillert 	/*
433c062391aSmillert 	 * ADR: This is complicated and more fragile than is desirable.
434c062391aSmillert 	 * Retrieving a string value for a number associates the string
435c062391aSmillert 	 * value with the scalar.  Previously, the string value was
436c062391aSmillert 	 * sticky, meaning if converted via OFMT that became the value
437c062391aSmillert 	 * (even though POSIX wants it to be via CONVFMT). Or if CONVFMT
438c062391aSmillert 	 * changed after a string value was retrieved, the original value
439c062391aSmillert 	 * was maintained and used.  Also not per POSIX.
440c062391aSmillert 	 *
441c062391aSmillert 	 * We work around this design by adding two additional flags,
442c062391aSmillert 	 * CONVC and CONVO, indicating how the string value was
443c062391aSmillert 	 * obtained (via CONVFMT or OFMT) and _also_ maintaining a copy
444c062391aSmillert 	 * of the pointer to the xFMT format string used for the
445c062391aSmillert 	 * conversion.  This pointer is only read, **never** dereferenced.
446c062391aSmillert 	 * The next time we do a conversion, if it's coming from the same
447c062391aSmillert 	 * xFMT as last time, and the pointer value is different, we
448c062391aSmillert 	 * know that the xFMT format string changed, and we need to
449c062391aSmillert 	 * redo the conversion. If it's the same, we don't have to.
450c062391aSmillert 	 *
451c062391aSmillert 	 * There are also several cases where we don't do a conversion,
452c062391aSmillert 	 * such as for a field (see the checks below).
453c062391aSmillert 	 */
454c062391aSmillert 
455c062391aSmillert 	/* Don't duplicate the code for actually updating the value */
456c062391aSmillert #define update_str_val(vp) \
457c062391aSmillert 	{ \
458c062391aSmillert 		if (freeable(vp)) \
459c062391aSmillert 			xfree(vp->sval); \
4606de80fb8Smillert 		if ((p = get_inf_nan(vp->fval)) != NULL) \
4616de80fb8Smillert 			n = (vp->sval = strdup(p)) ? 0 : -1; \
4626de80fb8Smillert 		else if (modf(vp->fval, &dtemp) == 0)	/* it's integral */ \
463c062391aSmillert 			n = asprintf(&vp->sval, "%.30g", vp->fval); \
464c062391aSmillert 		else \
465c062391aSmillert 			n = asprintf(&vp->sval, *fmt, vp->fval); \
466c062391aSmillert 		if (n == -1) \
467c062391aSmillert 			FATAL("out of space in get_str_val"); \
468c062391aSmillert 		vp->tval &= ~DONTFREE; \
469c062391aSmillert 		vp->tval |= STR; \
4706ab05f83Stholo 	}
471c062391aSmillert 
472c062391aSmillert 	if (isstr(vp) == 0) {
473c062391aSmillert 		update_str_val(vp);
474c062391aSmillert 		if (fmt == OFMT) {
475c062391aSmillert 			vp->tval &= ~CONVC;
476c062391aSmillert 			vp->tval |= CONVO;
477c062391aSmillert 		} else {
478c062391aSmillert 			/* CONVFMT */
479c062391aSmillert 			vp->tval &= ~CONVO;
480c062391aSmillert 			vp->tval |= CONVC;
481c062391aSmillert 		}
482c062391aSmillert 		vp->fmt = *fmt;
483c062391aSmillert 	} else if ((vp->tval & DONTFREE) != 0 || ! isnum(vp) || isfld(vp)) {
484c062391aSmillert 		goto done;
485c062391aSmillert 	} else if (isstr(vp)) {
486c062391aSmillert 		if (fmt == OFMT) {
487c062391aSmillert 			if ((vp->tval & CONVC) != 0
488c062391aSmillert 			    || ((vp->tval & CONVO) != 0 && vp->fmt != *fmt)) {
489c062391aSmillert 				update_str_val(vp);
490c062391aSmillert 				vp->tval &= ~CONVC;
491c062391aSmillert 				vp->tval |= CONVO;
492c062391aSmillert 				vp->fmt = *fmt;
493c062391aSmillert 			}
494c062391aSmillert 		} else {
495c062391aSmillert 			/* CONVFMT */
496c062391aSmillert 			if ((vp->tval & CONVO) != 0
497c062391aSmillert 			    || ((vp->tval & CONVC) != 0 && vp->fmt != *fmt)) {
498c062391aSmillert 				update_str_val(vp);
499c062391aSmillert 				vp->tval &= ~CONVO;
500c062391aSmillert 				vp->tval |= CONVC;
501c062391aSmillert 				vp->fmt = *fmt;
502c062391aSmillert 			}
503c062391aSmillert 		}
504c062391aSmillert 	}
505c062391aSmillert done:
506115bd590Smillert 	DPRINTF("getsval %p: %s = \"%s (%p)\", t=%o\n",
50760613915Smillert 		(void*)vp, NN(vp->nval), vp->sval, (void*)vp->sval, vp->tval);
5086ab05f83Stholo 	return(vp->sval);
5096ab05f83Stholo }
5106ab05f83Stholo 
5119a69093aSmillert char *getsval(Cell *vp)       /* get string val of a Cell */
5129a69093aSmillert {
5139a69093aSmillert       return get_str_val(vp, CONVFMT);
5149a69093aSmillert }
5159a69093aSmillert 
5169a69093aSmillert char *getpssval(Cell *vp)     /* get string val of a Cell for print */
5179a69093aSmillert {
5189a69093aSmillert       return get_str_val(vp, OFMT);
5199a69093aSmillert }
5209a69093aSmillert 
5219a69093aSmillert 
5229a69093aSmillert char *tostring(const char *s)	/* make a copy of string s */
5236ab05f83Stholo {
524d7cce239Smillert 	char *p = strdup(s);
5258055ea94Smillert 	if (p == NULL)
5268055ea94Smillert 		FATAL("out of space in tostring on %s", s);
527d7cce239Smillert 	return(p);
5286ab05f83Stholo }
5296ab05f83Stholo 
5306685ce51Smillert char *tostringN(const char *s, size_t n)	/* make a copy of string s */
5316685ce51Smillert {
5326685ce51Smillert 	char *p;
5336685ce51Smillert 
534483fa115Smillert 	p = (char *) malloc(n);
5356685ce51Smillert 	if (p == NULL)
536144915fcSmillert 		FATAL("out of space in tostringN %zu", n);
537144915fcSmillert 	if (strlcpy(p, s, n) >= n)
538144915fcSmillert 		FATAL("out of space in tostringN on %s", s);
5396685ce51Smillert 	return(p);
5406685ce51Smillert }
5416685ce51Smillert 
542c0fa3611Smillert Cell *catstr(Cell *a, Cell *b) /* concatenate a and b */
543c0fa3611Smillert {
544c0fa3611Smillert 	Cell *c;
545c0fa3611Smillert 	char *p;
546c0fa3611Smillert 	char *sa = getsval(a);
547c0fa3611Smillert 	char *sb = getsval(b);
548c0fa3611Smillert 	size_t l = strlen(sa) + strlen(sb) + 1;
549483fa115Smillert 	p = (char *) malloc(l);
550c0fa3611Smillert 	if (p == NULL)
551c0fa3611Smillert 		FATAL("out of space concatenating %s and %s", sa, sb);
552c0fa3611Smillert 	snprintf(p, l, "%s%s", sa, sb);
553fabd211eSmillert 
554fabd211eSmillert 	l++;	// add room for ' '
555483fa115Smillert 	char *newbuf = (char *) malloc(l);
55692497129Smillert 	if (newbuf == NULL)
55792497129Smillert 		FATAL("out of space concatenating %s and %s", sa, sb);
55892497129Smillert 	// See string() in lex.c; a string "xx" is stored in the symbol
55992497129Smillert 	// table as "xx ".
560fabd211eSmillert 	snprintf(newbuf, l, "%s ", p);
56192497129Smillert 	c = setsymtab(newbuf, p, 0.0, CON|STR|DONTFREE, symtab);
562c0fa3611Smillert 	free(p);
56392497129Smillert 	free(newbuf);
564c0fa3611Smillert 	return c;
565c0fa3611Smillert }
566c0fa3611Smillert 
5679a69093aSmillert char *qstring(const char *is, int delim)	/* collect string up to next delim */
5686ab05f83Stholo {
5696ab05f83Stholo 	int c, n;
570d7cce239Smillert 	const uschar *s = (const uschar *) is;
571a27f5228Smillert 	uschar *buf, *bp;
5726ab05f83Stholo 
573483fa115Smillert 	if ((buf = (uschar *) malloc(strlen(is)+3)) == NULL)
5747b11b857Smillert 		FATAL( "out of space in qstring(%s)", s);
57507edfa4aSkstailey 	for (bp = buf; (c = *s) != delim; s++) {
5766ab05f83Stholo 		if (c == '\n')
577*f190fa91Smillert 			SYNTAX( "newline in string %.20s...", is );
5786ab05f83Stholo 		else if (c != '\\')
57907edfa4aSkstailey 			*bp++ = c;
58007edfa4aSkstailey 		else {	/* \something */
5817b11b857Smillert 			c = *++s;
5827b11b857Smillert 			if (c == 0) {	/* \ at end */
5837b11b857Smillert 				*bp++ = '\\';
5847b11b857Smillert 				break;	/* for loop */
5857b11b857Smillert 			}
5867b11b857Smillert 			switch (c) {
58707edfa4aSkstailey 			case '\\':	*bp++ = '\\'; break;
58807edfa4aSkstailey 			case 'n':	*bp++ = '\n'; break;
58907edfa4aSkstailey 			case 't':	*bp++ = '\t'; break;
59007edfa4aSkstailey 			case 'b':	*bp++ = '\b'; break;
59107edfa4aSkstailey 			case 'f':	*bp++ = '\f'; break;
59207edfa4aSkstailey 			case 'r':	*bp++ = '\r'; break;
593c0fa3611Smillert 			case 'v':	*bp++ = '\v'; break;
594c0fa3611Smillert 			case 'a':	*bp++ = '\a'; break;
5956ab05f83Stholo 			default:
5966ab05f83Stholo 				if (!isdigit(c)) {
59707edfa4aSkstailey 					*bp++ = c;
5986ab05f83Stholo 					break;
5996ab05f83Stholo 				}
6006ab05f83Stholo 				n = c - '0';
6016ab05f83Stholo 				if (isdigit(s[1])) {
6026ab05f83Stholo 					n = 8 * n + *++s - '0';
6036ab05f83Stholo 					if (isdigit(s[1]))
6046ab05f83Stholo 						n = 8 * n + *++s - '0';
6056ab05f83Stholo 				}
60607edfa4aSkstailey 				*bp++ = n;
6076ab05f83Stholo 				break;
6086ab05f83Stholo 			}
6096ab05f83Stholo 		}
61007edfa4aSkstailey 	}
61107edfa4aSkstailey 	*bp++ = 0;
612a27f5228Smillert 	return (char *) buf;
6136ab05f83Stholo }
614c062391aSmillert 
615c062391aSmillert const char *flags2str(int flags)
616c062391aSmillert {
617c062391aSmillert 	static const struct ftab {
618c062391aSmillert 		const char *name;
619c062391aSmillert 		int value;
620c062391aSmillert 	} flagtab[] = {
621c062391aSmillert 		{ "NUM", NUM },
622c062391aSmillert 		{ "STR", STR },
623c062391aSmillert 		{ "DONTFREE", DONTFREE },
624c062391aSmillert 		{ "CON", CON },
625c062391aSmillert 		{ "ARR", ARR },
626c062391aSmillert 		{ "FCN", FCN },
627c062391aSmillert 		{ "FLD", FLD },
628c062391aSmillert 		{ "REC", REC },
629c062391aSmillert 		{ "CONVC", CONVC },
630c062391aSmillert 		{ "CONVO", CONVO },
631c062391aSmillert 		{ NULL, 0 }
632c062391aSmillert 	};
633c062391aSmillert 	static char buf[100];
634c062391aSmillert 	int i, len;
635c062391aSmillert 	char *cp = buf;
636c062391aSmillert 
637c062391aSmillert 	for (i = 0; flagtab[i].name != NULL; i++) {
638c062391aSmillert 		if ((flags & flagtab[i].value) != 0) {
639c062391aSmillert 			len = snprintf(cp, sizeof(buf) - (cp - buf),
640c062391aSmillert 			    "%s%s", cp > buf ? "|" : "", flagtab[i].name);
641c062391aSmillert 			if (len < 0 || len >= sizeof(buf) - (cp - buf))
642c062391aSmillert 				FATAL("out of space in flags2str");
643c062391aSmillert 			cp += len;
644c062391aSmillert 		}
645c062391aSmillert 	}
646c062391aSmillert 
647c062391aSmillert 	return buf;
648c062391aSmillert }
649