xref: /openbsd/usr.bin/awk/tran.c (revision 88689b4c)
1*88689b4cSmillert /*	$OpenBSD: tran.c,v 1.40 2024/06/03 00:55:05 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 */
61e109dc98Smillert Cell	*ARGVcell;	/* cell with symbol table containing ARGV[...] */
626ab05f83Stholo Cell	*rstartloc;	/* RSTART */
636ab05f83Stholo Cell	*rlengthloc;	/* RLENGTH */
6402265e66Smillert Cell	*subseploc;	/* SUBSEP */
656ab05f83Stholo Cell	*symtabloc;	/* SYMTAB */
666ab05f83Stholo 
676ab05f83Stholo Cell	*nullloc;	/* a guaranteed empty cell */
686ab05f83Stholo Node	*nullnode;	/* zero&null, converted into a node for comparisons */
6907edfa4aSkstailey Cell	*literal0;
706ab05f83Stholo 
7107edfa4aSkstailey extern Cell **fldtab;
726ab05f83Stholo 
syminit(void)736ab05f83Stholo void syminit(void)	/* initialize symbol table with builtin vars */
746ab05f83Stholo {
7507edfa4aSkstailey 	literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab);
766ab05f83Stholo 	/* this is used for if(x)... tests: */
776ab05f83Stholo 	nullloc = setsymtab("$zero&null", "", 0.0, NUM|STR|CON|DONTFREE, symtab);
7807edfa4aSkstailey 	nullnode = celltonode(nullloc, CCON);
796ab05f83Stholo 
8023cb51abSmillert 	fsloc = setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab);
8123cb51abSmillert 	FS = &fsloc->sval;
8202265e66Smillert 	rsloc = setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab);
8302265e66Smillert 	RS = &rsloc->sval;
8402265e66Smillert 	ofsloc = setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab);
8502265e66Smillert 	OFS = &ofsloc->sval;
8602265e66Smillert 	orsloc = setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab);
8702265e66Smillert 	ORS = &orsloc->sval;
886ab05f83Stholo 	OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
896ab05f83Stholo 	CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
90b2698ba9Smillert 	FILENAME = &setsymtab("FILENAME", "", 0.0, STR|DONTFREE, symtab)->sval;
916ab05f83Stholo 	nfloc = setsymtab("NF", "", 0.0, NUM, symtab);
926ab05f83Stholo 	NF = &nfloc->fval;
936ab05f83Stholo 	nrloc = setsymtab("NR", "", 0.0, NUM, symtab);
946ab05f83Stholo 	NR = &nrloc->fval;
956ab05f83Stholo 	fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab);
966ab05f83Stholo 	FNR = &fnrloc->fval;
9702265e66Smillert 	subseploc = setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab);
9802265e66Smillert 	SUBSEP = &subseploc->sval;
996ab05f83Stholo 	rstartloc = setsymtab("RSTART", "", 0.0, NUM, symtab);
1006ab05f83Stholo 	RSTART = &rstartloc->fval;
1016ab05f83Stholo 	rlengthloc = setsymtab("RLENGTH", "", 0.0, NUM, symtab);
1026ab05f83Stholo 	RLENGTH = &rlengthloc->fval;
1036ab05f83Stholo 	symtabloc = setsymtab("SYMTAB", "", 0.0, ARR, symtab);
10492497129Smillert 	free(symtabloc->sval);
1056ab05f83Stholo 	symtabloc->sval = (char *) symtab;
1066ab05f83Stholo }
1076ab05f83Stholo 
arginit(int ac,char ** av)10807edfa4aSkstailey void arginit(int ac, char **av)	/* set up ARGV and ARGC */
1096ab05f83Stholo {
110e109dc98Smillert 	Array *ap;
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);
117e109dc98Smillert 	ap = makesymtab(NSYMTAB);	/* could be (int) ARGC as well */
11892497129Smillert 	free(cp->sval);
119e109dc98Smillert 	cp->sval = (char *) ap;
1206ab05f83Stholo 	for (i = 0; i < ac; i++) {
121483fa115Smillert 		double result;
122483fa115Smillert 
1232a0b1829Smillert 		snprintf(temp, sizeof(temp), "%d", i);
124483fa115Smillert 		if (is_number(*av, & result))
125e109dc98Smillert 			setsymtab(temp, *av, result, STR|NUM, ap);
1266ab05f83Stholo 		else
127e109dc98Smillert 			setsymtab(temp, *av, 0.0, STR, ap);
1286ab05f83Stholo 		av++;
1296ab05f83Stholo 	}
130e109dc98Smillert 	ARGVcell = cp;
1316ab05f83Stholo }
1326ab05f83Stholo 
envinit(char ** envp)1336ab05f83Stholo void envinit(char **envp)	/* set up ENVIRON variable */
1346ab05f83Stholo {
135e109dc98Smillert 	Array *ap;
1366ab05f83Stholo 	Cell *cp;
1376ab05f83Stholo 	char *p;
1386ab05f83Stholo 
1396ab05f83Stholo 	cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab);
140e109dc98Smillert 	ap = makesymtab(NSYMTAB);
14192497129Smillert 	free(cp->sval);
142e109dc98Smillert 	cp->sval = (char *) ap;
1436ab05f83Stholo 	for ( ; *envp; envp++) {
144483fa115Smillert 		double result;
145483fa115Smillert 
14607edfa4aSkstailey 		if ((p = strchr(*envp, '=')) == NULL)
1476ab05f83Stholo 			continue;
148a27f5228Smillert 		if( p == *envp ) /* no left hand side name in env string */
149a27f5228Smillert 			continue;
1506ab05f83Stholo 		*p++ = 0;	/* split into two strings at = */
151483fa115Smillert 		if (is_number(p, & result))
152e109dc98Smillert 			setsymtab(*envp, p, result, STR|NUM, ap);
1536ab05f83Stholo 		else
154e109dc98Smillert 			setsymtab(*envp, p, 0.0, STR, ap);
1556ab05f83Stholo 		p[-1] = '=';	/* restore in case env is passed down to a shell */
1566ab05f83Stholo 	}
1576ab05f83Stholo }
1586ab05f83Stholo 
makesymtab(int n)1596ab05f83Stholo Array *makesymtab(int n)	/* make a new symbol table */
1606ab05f83Stholo {
1616ab05f83Stholo 	Array *ap;
1626ab05f83Stholo 	Cell **tp;
1636ab05f83Stholo 
164483fa115Smillert 	ap = (Array *) malloc(sizeof(*ap));
165483fa115Smillert 	tp = (Cell **) calloc(n, sizeof(*tp));
1666ab05f83Stholo 	if (ap == NULL || tp == NULL)
1677b11b857Smillert 		FATAL("out of space in makesymtab");
1686ab05f83Stholo 	ap->nelem = 0;
1696ab05f83Stholo 	ap->size = n;
1706ab05f83Stholo 	ap->tab = tp;
1716ab05f83Stholo 	return(ap);
1726ab05f83Stholo }
1736ab05f83Stholo 
freesymtab(Cell * ap)1746ab05f83Stholo void freesymtab(Cell *ap)	/* free a symbol table */
1756ab05f83Stholo {
1766ab05f83Stholo 	Cell *cp, *temp;
1776ab05f83Stholo 	Array *tp;
1786ab05f83Stholo 	int i;
1796ab05f83Stholo 
1806ab05f83Stholo 	if (!isarr(ap))
1816ab05f83Stholo 		return;
1826ab05f83Stholo 	tp = (Array *) ap->sval;
1836ab05f83Stholo 	if (tp == NULL)
1846ab05f83Stholo 		return;
1856ab05f83Stholo 	for (i = 0; i < tp->size; i++) {
1866ab05f83Stholo 		for (cp = tp->tab[i]; cp != NULL; cp = temp) {
1876ab05f83Stholo 			xfree(cp->nval);
1886ab05f83Stholo 			if (freeable(cp))
1896ab05f83Stholo 				xfree(cp->sval);
1906ab05f83Stholo 			temp = cp->cnext;	/* avoids freeing then using */
19107edfa4aSkstailey 			free(cp);
1929a69093aSmillert 			tp->nelem--;
1936ab05f83Stholo 		}
194c0fa3611Smillert 		tp->tab[i] = NULL;
1956ab05f83Stholo 	}
1969a69093aSmillert 	if (tp->nelem != 0)
1979a69093aSmillert 		WARNING("can't happen: inconsistent element count freeing %s", ap->nval);
19807edfa4aSkstailey 	free(tp->tab);
19907edfa4aSkstailey 	free(tp);
2006ab05f83Stholo }
2016ab05f83Stholo 
freeelem(Cell * ap,const char * s)2029a69093aSmillert void freeelem(Cell *ap, const char *s)	/* free elem s from ap (i.e., ap["s"] */
2036ab05f83Stholo {
2046ab05f83Stholo 	Array *tp;
2056ab05f83Stholo 	Cell *p, *prev = NULL;
2066ab05f83Stholo 	int h;
2076ab05f83Stholo 
2086ab05f83Stholo 	tp = (Array *) ap->sval;
2096ab05f83Stholo 	h = hash(s, tp->size);
2106ab05f83Stholo 	for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext)
21107edfa4aSkstailey 		if (strcmp(s, p->nval) == 0) {
2126ab05f83Stholo 			if (prev == NULL)	/* 1st one */
2136ab05f83Stholo 				tp->tab[h] = p->cnext;
2146ab05f83Stholo 			else			/* middle somewhere */
2156ab05f83Stholo 				prev->cnext = p->cnext;
2166ab05f83Stholo 			if (freeable(p))
2176ab05f83Stholo 				xfree(p->sval);
2186ab05f83Stholo 			free(p->nval);
21907edfa4aSkstailey 			free(p);
2206ab05f83Stholo 			tp->nelem--;
2216ab05f83Stholo 			return;
2226ab05f83Stholo 		}
2236ab05f83Stholo }
2246ab05f83Stholo 
setsymtab(const char * n,const char * s,Awkfloat f,unsigned t,Array * tp)2259a69093aSmillert Cell *setsymtab(const char *n, const char *s, Awkfloat f, unsigned t, Array *tp)
2266ab05f83Stholo {
2276ab05f83Stholo 	int h;
2286ab05f83Stholo 	Cell *p;
2296ab05f83Stholo 
2306ab05f83Stholo 	if (n != NULL && (p = lookup(n, tp)) != NULL) {
231115bd590Smillert 		DPRINTF("setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n",
232115bd590Smillert 			(void*)p, NN(p->nval), NN(p->sval), p->fval, p->tval);
2336ab05f83Stholo 		return(p);
2346ab05f83Stholo 	}
235483fa115Smillert 	p = (Cell *) malloc(sizeof(*p));
2366ab05f83Stholo 	if (p == NULL)
2377b11b857Smillert 		FATAL("out of space for symbol table at %s", n);
2386ab05f83Stholo 	p->nval = tostring(n);
2396ab05f83Stholo 	p->sval = s ? tostring(s) : tostring("");
2406ab05f83Stholo 	p->fval = f;
2416ab05f83Stholo 	p->tval = t;
2426ab05f83Stholo 	p->csub = CUNK;
2436ab05f83Stholo 	p->ctype = OCELL;
2446ab05f83Stholo 	tp->nelem++;
2456ab05f83Stholo 	if (tp->nelem > FULLTAB * tp->size)
2466ab05f83Stholo 		rehash(tp);
2476ab05f83Stholo 	h = hash(n, tp->size);
2486ab05f83Stholo 	p->cnext = tp->tab[h];
2496ab05f83Stholo 	tp->tab[h] = p;
250115bd590Smillert 	DPRINTF("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n",
251115bd590Smillert 		(void*)p, p->nval, p->sval, p->fval, p->tval);
2526ab05f83Stholo 	return(p);
2536ab05f83Stholo }
2546ab05f83Stholo 
hash(const char * s,int n)2559a69093aSmillert int hash(const char *s, int n)	/* form hash value for string s */
2566ab05f83Stholo {
2576ab05f83Stholo 	unsigned hashval;
2586ab05f83Stholo 
2596ab05f83Stholo 	for (hashval = 0; *s != '\0'; s++)
2606ab05f83Stholo 		hashval = (*s + 31 * hashval);
2616ab05f83Stholo 	return hashval % n;
2626ab05f83Stholo }
2636ab05f83Stholo 
rehash(Array * tp)2646ab05f83Stholo void rehash(Array *tp)	/* rehash items in small table into big one */
2656ab05f83Stholo {
2666ab05f83Stholo 	int i, nh, nsz;
2676ab05f83Stholo 	Cell *cp, *op, **np;
2686ab05f83Stholo 
2696ab05f83Stholo 	nsz = GROWTAB * tp->size;
270483fa115Smillert 	np = (Cell **) calloc(nsz, sizeof(*np));
2716ab05f83Stholo 	if (np == NULL)		/* can't do it, but can keep running. */
2726ab05f83Stholo 		return;		/* someone else will run out later. */
2736ab05f83Stholo 	for (i = 0; i < tp->size; i++) {
2746ab05f83Stholo 		for (cp = tp->tab[i]; cp; cp = op) {
2756ab05f83Stholo 			op = cp->cnext;
2766ab05f83Stholo 			nh = hash(cp->nval, nsz);
2776ab05f83Stholo 			cp->cnext = np[nh];
2786ab05f83Stholo 			np[nh] = cp;
2796ab05f83Stholo 		}
2806ab05f83Stholo 	}
28107edfa4aSkstailey 	free(tp->tab);
2826ab05f83Stholo 	tp->tab = np;
2836ab05f83Stholo 	tp->size = nsz;
2846ab05f83Stholo }
2856ab05f83Stholo 
lookup(const char * s,Array * tp)2869a69093aSmillert Cell *lookup(const char *s, Array *tp)	/* look for s in tp */
2876ab05f83Stholo {
288271018d0Smillert 	Cell *p;
2896ab05f83Stholo 	int h;
2906ab05f83Stholo 
2916ab05f83Stholo 	h = hash(s, tp->size);
292271018d0Smillert 	for (p = tp->tab[h]; p != NULL; p = p->cnext)
29307edfa4aSkstailey 		if (strcmp(s, p->nval) == 0)
2946ab05f83Stholo 			return(p);	/* found it */
2956ab05f83Stholo 	return(NULL);			/* not found */
2966ab05f83Stholo }
2976ab05f83Stholo 
setfval(Cell * vp,Awkfloat f)2986ab05f83Stholo Awkfloat setfval(Cell *vp, Awkfloat f)	/* set float val of a Cell */
2996ab05f83Stholo {
30007edfa4aSkstailey 	int fldno;
30107edfa4aSkstailey 
302c062391aSmillert 	f += 0.0;		/* normalise negative zero to positive zero */
3036ab05f83Stholo 	if ((vp->tval & (NUM | STR)) == 0)
3046ab05f83Stholo 		funnyvar(vp, "assign to");
30507edfa4aSkstailey 	if (isfld(vp)) {
306f81b289fSmillert 		donerec = false;	/* mark $0 invalid */
30707edfa4aSkstailey 		fldno = atoi(vp->nval);
30807edfa4aSkstailey 		if (fldno > *NF)
30907edfa4aSkstailey 			newfld(fldno);
310115bd590Smillert 		DPRINTF("setting field %d to %g\n", fldno, f);
311c062391aSmillert 	} else if (&vp->fval == NF) {
312f81b289fSmillert 		donerec = false;	/* mark $0 invalid */
313c062391aSmillert 		setlastfld(f);
314a886e62eSmillert 		DPRINTF("setfval: setting NF to %g\n", f);
31507edfa4aSkstailey 	} else if (isrec(vp)) {
316f81b289fSmillert 		donefld = false;	/* mark $1... invalid */
317f81b289fSmillert 		donerec = true;
318c0fa3611Smillert 		savefs();
31902265e66Smillert 	} else if (vp == ofsloc) {
320f81b289fSmillert 		if (!donerec)
32102265e66Smillert 			recbld();
3226ab05f83Stholo 	}
323a4fa8700Smillert 	if (freeable(vp))
324a4fa8700Smillert 		xfree(vp->sval); /* free any previous string */
325c062391aSmillert 	vp->tval &= ~(STR|CONVC|CONVO); /* mark string invalid */
326c062391aSmillert 	vp->fmt = NULL;
3276ab05f83Stholo 	vp->tval |= NUM;	/* mark number ok */
328a8d6f668Smillert 	if (f == -0)  /* who would have thought this possible? */
329a8d6f668Smillert 		f = 0;
330115bd590Smillert 	DPRINTF("setfval %p: %s = %g, t=%o\n", (void*)vp, NN(vp->nval), f, vp->tval);
3316ab05f83Stholo 	return vp->fval = f;
3326ab05f83Stholo }
3336ab05f83Stholo 
funnyvar(Cell * vp,const char * rw)3349a69093aSmillert void funnyvar(Cell *vp, const char *rw)
3356ab05f83Stholo {
33607edfa4aSkstailey 	if (isarr(vp))
3377b11b857Smillert 		FATAL("can't %s %s; it's an array name.", rw, vp->nval);
3386ab05f83Stholo 	if (vp->tval & FCN)
3397b11b857Smillert 		FATAL("can't %s %s; it's a function.", rw, vp->nval);
3407b11b857Smillert 	WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o",
3416685ce51Smillert 		(void *)vp, vp->nval, vp->sval, vp->fval, vp->tval);
3426ab05f83Stholo }
3436ab05f83Stholo 
setsval(Cell * vp,const char * s)3449a69093aSmillert char *setsval(Cell *vp, const char *s)	/* set string val of a Cell */
3456ab05f83Stholo {
3466ab05f83Stholo 	char *t;
34707edfa4aSkstailey 	int fldno;
348c062391aSmillert 	Awkfloat f;
3496ab05f83Stholo 
350115bd590Smillert 	DPRINTF("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n",
351115bd590Smillert 		(void*)vp, NN(vp->nval), s, vp->tval, donerec, donefld);
3526ab05f83Stholo 	if ((vp->tval & (NUM | STR)) == 0)
3536ab05f83Stholo 		funnyvar(vp, "assign to");
354a886e62eSmillert 	if (CSV && (vp == rsloc))
355a886e62eSmillert 		WARNING("danger: don't set RS when --csv is in effect");
356a886e62eSmillert 	if (CSV && (vp == fsloc))
357a886e62eSmillert 		WARNING("danger: don't set FS when --csv is in effect");
35807edfa4aSkstailey 	if (isfld(vp)) {
359f81b289fSmillert 		donerec = false;	/* mark $0 invalid */
36007edfa4aSkstailey 		fldno = atoi(vp->nval);
36107edfa4aSkstailey 		if (fldno > *NF)
36207edfa4aSkstailey 			newfld(fldno);
36360613915Smillert 		DPRINTF("setting field %d to %s (%p)\n", fldno, s, (const void*)s);
36407edfa4aSkstailey 	} else if (isrec(vp)) {
365f81b289fSmillert 		donefld = false;	/* mark $1... invalid */
366f81b289fSmillert 		donerec = true;
367c0fa3611Smillert 		savefs();
36802265e66Smillert 	} else if (vp == ofsloc) {
369f81b289fSmillert 		if (!donerec)
370c062391aSmillert 			recbld();
3716ab05f83Stholo 	}
372c062391aSmillert 	t = s ? tostring(s) : tostring("");	/* in case it's self-assign */
3736ab05f83Stholo 	if (freeable(vp))
3746ab05f83Stholo 		xfree(vp->sval);
375a06fd656Smillert 	vp->tval &= ~(NUM|DONTFREE|CONVC|CONVO);
3764eb91dacSjmc 	vp->tval |= STR;
377c062391aSmillert 	vp->fmt = NULL;
378115bd590Smillert 	DPRINTF("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n",
37960613915Smillert 		(void*)vp, NN(vp->nval), t, (void*)t, vp->tval, donerec, donefld);
380c062391aSmillert 	vp->sval = t;
381c062391aSmillert 	if (&vp->fval == NF) {
382f81b289fSmillert 		donerec = false;	/* mark $0 invalid */
383c062391aSmillert 		f = getfval(vp);
384c062391aSmillert 		setlastfld(f);
385a886e62eSmillert 		DPRINTF("setsval: setting NF to %g\n", f);
386c062391aSmillert 	}
387c062391aSmillert 
388c062391aSmillert 	return(vp->sval);
3896ab05f83Stholo }
3906ab05f83Stholo 
getfval(Cell * vp)3916ab05f83Stholo Awkfloat getfval(Cell *vp)	/* get float val of a Cell */
3926ab05f83Stholo {
3936ab05f83Stholo 	if ((vp->tval & (NUM | STR)) == 0)
3946ab05f83Stholo 		funnyvar(vp, "read value of");
395f81b289fSmillert 	if (isfld(vp) && !donefld)
3966ab05f83Stholo 		fldbld();
397f81b289fSmillert 	else if (isrec(vp) && !donerec)
3986ab05f83Stholo 		recbld();
3996ab05f83Stholo 	if (!isnum(vp)) {	/* not a number */
400483fa115Smillert 		double fval;
401483fa115Smillert 		bool no_trailing;
402483fa115Smillert 
403483fa115Smillert 		if (is_valid_number(vp->sval, true, & no_trailing, & fval)) {
404483fa115Smillert 			vp->fval = fval;
405483fa115Smillert 			if (no_trailing && !(vp->tval&CON))
4066ab05f83Stholo 				vp->tval |= NUM;	/* make NUM only sparingly */
407483fa115Smillert 		} else
408483fa115Smillert 			vp->fval = 0.0;
4096ab05f83Stholo 	}
410115bd590Smillert 	DPRINTF("getfval %p: %s = %g, t=%o\n",
411115bd590Smillert 		(void*)vp, NN(vp->nval), vp->fval, vp->tval);
4126ab05f83Stholo 	return(vp->fval);
4136ab05f83Stholo }
4146ab05f83Stholo 
get_inf_nan(double d)415f9297e05Smillert static const char *get_inf_nan(double d)
4166de80fb8Smillert {
4176de80fb8Smillert 	if (isinf(d)) {
4186de80fb8Smillert 		return (d < 0 ? "-inf" : "+inf");
4196de80fb8Smillert 	} else if (isnan(d)) {
4206de80fb8Smillert 		return (signbit(d) != 0 ? "-nan" : "+nan");
4216de80fb8Smillert 	} else
4226de80fb8Smillert 		return NULL;
4236de80fb8Smillert }
4246de80fb8Smillert 
get_str_val(Cell * vp,char ** fmt)4259a69093aSmillert static char *get_str_val(Cell *vp, char **fmt)        /* get string val of a Cell */
4266ab05f83Stholo {
427a4a48c73Smillert 	int n;
4286ab05f83Stholo 	double dtemp;
429f9297e05Smillert 	const char *p;
4306ab05f83Stholo 
4316ab05f83Stholo 	if ((vp->tval & (NUM | STR)) == 0)
4326ab05f83Stholo 		funnyvar(vp, "read value of");
433f81b289fSmillert 	if (isfld(vp) && ! donefld)
4346ab05f83Stholo 		fldbld();
435f81b289fSmillert 	else if (isrec(vp) && ! donerec)
4366ab05f83Stholo 		recbld();
437c062391aSmillert 
438c062391aSmillert 	/*
439c062391aSmillert 	 * ADR: This is complicated and more fragile than is desirable.
440c062391aSmillert 	 * Retrieving a string value for a number associates the string
441c062391aSmillert 	 * value with the scalar.  Previously, the string value was
442c062391aSmillert 	 * sticky, meaning if converted via OFMT that became the value
443c062391aSmillert 	 * (even though POSIX wants it to be via CONVFMT). Or if CONVFMT
444c062391aSmillert 	 * changed after a string value was retrieved, the original value
445c062391aSmillert 	 * was maintained and used.  Also not per POSIX.
446c062391aSmillert 	 *
447c062391aSmillert 	 * We work around this design by adding two additional flags,
448c062391aSmillert 	 * CONVC and CONVO, indicating how the string value was
449c062391aSmillert 	 * obtained (via CONVFMT or OFMT) and _also_ maintaining a copy
450c062391aSmillert 	 * of the pointer to the xFMT format string used for the
451c062391aSmillert 	 * conversion.  This pointer is only read, **never** dereferenced.
452c062391aSmillert 	 * The next time we do a conversion, if it's coming from the same
453c062391aSmillert 	 * xFMT as last time, and the pointer value is different, we
454c062391aSmillert 	 * know that the xFMT format string changed, and we need to
455c062391aSmillert 	 * redo the conversion. If it's the same, we don't have to.
456c062391aSmillert 	 *
457c062391aSmillert 	 * There are also several cases where we don't do a conversion,
458c062391aSmillert 	 * such as for a field (see the checks below).
459c062391aSmillert 	 */
460c062391aSmillert 
461c062391aSmillert 	/* Don't duplicate the code for actually updating the value */
462c062391aSmillert #define update_str_val(vp) \
463c062391aSmillert 	{ \
464c062391aSmillert 		if (freeable(vp)) \
465c062391aSmillert 			xfree(vp->sval); \
4666de80fb8Smillert 		if ((p = get_inf_nan(vp->fval)) != NULL) \
4676de80fb8Smillert 			n = (vp->sval = strdup(p)) ? 0 : -1; \
4686de80fb8Smillert 		else if (modf(vp->fval, &dtemp) == 0)	/* it's integral */ \
469c062391aSmillert 			n = asprintf(&vp->sval, "%.30g", vp->fval); \
470c062391aSmillert 		else \
471c062391aSmillert 			n = asprintf(&vp->sval, *fmt, vp->fval); \
472c062391aSmillert 		if (n == -1) \
473c062391aSmillert 			FATAL("out of space in get_str_val"); \
474c062391aSmillert 		vp->tval &= ~DONTFREE; \
475c062391aSmillert 		vp->tval |= STR; \
4766ab05f83Stholo 	}
477c062391aSmillert 
478c062391aSmillert 	if (isstr(vp) == 0) {
479c062391aSmillert 		update_str_val(vp);
480c062391aSmillert 		if (fmt == OFMT) {
481c062391aSmillert 			vp->tval &= ~CONVC;
482c062391aSmillert 			vp->tval |= CONVO;
483c062391aSmillert 		} else {
484c062391aSmillert 			/* CONVFMT */
485c062391aSmillert 			vp->tval &= ~CONVO;
486c062391aSmillert 			vp->tval |= CONVC;
487c062391aSmillert 		}
488c062391aSmillert 		vp->fmt = *fmt;
489c062391aSmillert 	} else if ((vp->tval & DONTFREE) != 0 || ! isnum(vp) || isfld(vp)) {
490c062391aSmillert 		goto done;
491c062391aSmillert 	} else if (isstr(vp)) {
492c062391aSmillert 		if (fmt == OFMT) {
493c062391aSmillert 			if ((vp->tval & CONVC) != 0
494c062391aSmillert 			    || ((vp->tval & CONVO) != 0 && vp->fmt != *fmt)) {
495c062391aSmillert 				update_str_val(vp);
496c062391aSmillert 				vp->tval &= ~CONVC;
497c062391aSmillert 				vp->tval |= CONVO;
498c062391aSmillert 				vp->fmt = *fmt;
499c062391aSmillert 			}
500c062391aSmillert 		} else {
501c062391aSmillert 			/* CONVFMT */
502c062391aSmillert 			if ((vp->tval & CONVO) != 0
503c062391aSmillert 			    || ((vp->tval & CONVC) != 0 && vp->fmt != *fmt)) {
504c062391aSmillert 				update_str_val(vp);
505c062391aSmillert 				vp->tval &= ~CONVO;
506c062391aSmillert 				vp->tval |= CONVC;
507c062391aSmillert 				vp->fmt = *fmt;
508c062391aSmillert 			}
509c062391aSmillert 		}
510c062391aSmillert 	}
511c062391aSmillert done:
512115bd590Smillert 	DPRINTF("getsval %p: %s = \"%s (%p)\", t=%o\n",
51360613915Smillert 		(void*)vp, NN(vp->nval), vp->sval, (void*)vp->sval, vp->tval);
5146ab05f83Stholo 	return(vp->sval);
5156ab05f83Stholo }
5166ab05f83Stholo 
getsval(Cell * vp)5179a69093aSmillert char *getsval(Cell *vp)       /* get string val of a Cell */
5189a69093aSmillert {
5199a69093aSmillert       return get_str_val(vp, CONVFMT);
5209a69093aSmillert }
5219a69093aSmillert 
getpssval(Cell * vp)5229a69093aSmillert char *getpssval(Cell *vp)     /* get string val of a Cell for print */
5239a69093aSmillert {
5249a69093aSmillert       return get_str_val(vp, OFMT);
5259a69093aSmillert }
5269a69093aSmillert 
5279a69093aSmillert 
tostring(const char * s)5289a69093aSmillert char *tostring(const char *s)	/* make a copy of string s */
5296ab05f83Stholo {
530d7cce239Smillert 	char *p = strdup(s);
5318055ea94Smillert 	if (p == NULL)
5328055ea94Smillert 		FATAL("out of space in tostring on %s", s);
533d7cce239Smillert 	return(p);
5346ab05f83Stholo }
5356ab05f83Stholo 
tostringN(const char * s,size_t n)5366685ce51Smillert char *tostringN(const char *s, size_t n)	/* make a copy of string s */
5376685ce51Smillert {
5386685ce51Smillert 	char *p;
5396685ce51Smillert 
540483fa115Smillert 	p = (char *) malloc(n);
5416685ce51Smillert 	if (p == NULL)
542144915fcSmillert 		FATAL("out of space in tostringN %zu", n);
543144915fcSmillert 	if (strlcpy(p, s, n) >= n)
544144915fcSmillert 		FATAL("out of space in tostringN on %s", s);
5456685ce51Smillert 	return(p);
5466685ce51Smillert }
5476685ce51Smillert 
catstr(Cell * a,Cell * b)548c0fa3611Smillert Cell *catstr(Cell *a, Cell *b) /* concatenate a and b */
549c0fa3611Smillert {
550c0fa3611Smillert 	Cell *c;
551c0fa3611Smillert 	char *p;
552c0fa3611Smillert 	char *sa = getsval(a);
553c0fa3611Smillert 	char *sb = getsval(b);
554c0fa3611Smillert 	size_t l = strlen(sa) + strlen(sb) + 1;
555483fa115Smillert 	p = (char *) malloc(l);
556c0fa3611Smillert 	if (p == NULL)
557c0fa3611Smillert 		FATAL("out of space concatenating %s and %s", sa, sb);
558c0fa3611Smillert 	snprintf(p, l, "%s%s", sa, sb);
559fabd211eSmillert 
560fabd211eSmillert 	l++;	// add room for ' '
561483fa115Smillert 	char *newbuf = (char *) malloc(l);
56292497129Smillert 	if (newbuf == NULL)
56392497129Smillert 		FATAL("out of space concatenating %s and %s", sa, sb);
56492497129Smillert 	// See string() in lex.c; a string "xx" is stored in the symbol
56592497129Smillert 	// table as "xx ".
566fabd211eSmillert 	snprintf(newbuf, l, "%s ", p);
56792497129Smillert 	c = setsymtab(newbuf, p, 0.0, CON|STR|DONTFREE, symtab);
568c0fa3611Smillert 	free(p);
56992497129Smillert 	free(newbuf);
570c0fa3611Smillert 	return c;
571c0fa3611Smillert }
572c0fa3611Smillert 
qstring(const char * is,int delim)5739a69093aSmillert char *qstring(const char *is, int delim)	/* collect string up to next delim */
5746ab05f83Stholo {
5756ab05f83Stholo 	int c, n;
576d7cce239Smillert 	const uschar *s = (const uschar *) is;
577a27f5228Smillert 	uschar *buf, *bp;
5786ab05f83Stholo 
579483fa115Smillert 	if ((buf = (uschar *) malloc(strlen(is)+3)) == NULL)
5807b11b857Smillert 		FATAL( "out of space in qstring(%s)", s);
58107edfa4aSkstailey 	for (bp = buf; (c = *s) != delim; s++) {
5826ab05f83Stholo 		if (c == '\n')
583f190fa91Smillert 			SYNTAX( "newline in string %.20s...", is );
5846ab05f83Stholo 		else if (c != '\\')
58507edfa4aSkstailey 			*bp++ = c;
58607edfa4aSkstailey 		else {	/* \something */
5877b11b857Smillert 			c = *++s;
5887b11b857Smillert 			if (c == 0) {	/* \ at end */
5897b11b857Smillert 				*bp++ = '\\';
5907b11b857Smillert 				break;	/* for loop */
5917b11b857Smillert 			}
5927b11b857Smillert 			switch (c) {
59307edfa4aSkstailey 			case '\\':	*bp++ = '\\'; break;
59407edfa4aSkstailey 			case 'n':	*bp++ = '\n'; break;
59507edfa4aSkstailey 			case 't':	*bp++ = '\t'; break;
59607edfa4aSkstailey 			case 'b':	*bp++ = '\b'; break;
59707edfa4aSkstailey 			case 'f':	*bp++ = '\f'; break;
59807edfa4aSkstailey 			case 'r':	*bp++ = '\r'; break;
599c0fa3611Smillert 			case 'v':	*bp++ = '\v'; break;
600c0fa3611Smillert 			case 'a':	*bp++ = '\a'; break;
6016ab05f83Stholo 			default:
6026ab05f83Stholo 				if (!isdigit(c)) {
60307edfa4aSkstailey 					*bp++ = c;
6046ab05f83Stholo 					break;
6056ab05f83Stholo 				}
6066ab05f83Stholo 				n = c - '0';
6076ab05f83Stholo 				if (isdigit(s[1])) {
6086ab05f83Stholo 					n = 8 * n + *++s - '0';
6096ab05f83Stholo 					if (isdigit(s[1]))
6106ab05f83Stholo 						n = 8 * n + *++s - '0';
6116ab05f83Stholo 				}
61207edfa4aSkstailey 				*bp++ = n;
6136ab05f83Stholo 				break;
6146ab05f83Stholo 			}
6156ab05f83Stholo 		}
61607edfa4aSkstailey 	}
61707edfa4aSkstailey 	*bp++ = 0;
618a27f5228Smillert 	return (char *) buf;
6196ab05f83Stholo }
620c062391aSmillert 
flags2str(int flags)621c062391aSmillert const char *flags2str(int flags)
622c062391aSmillert {
623c062391aSmillert 	static const struct ftab {
624c062391aSmillert 		const char *name;
625c062391aSmillert 		int value;
626c062391aSmillert 	} flagtab[] = {
627c062391aSmillert 		{ "NUM", NUM },
628c062391aSmillert 		{ "STR", STR },
629c062391aSmillert 		{ "DONTFREE", DONTFREE },
630c062391aSmillert 		{ "CON", CON },
631c062391aSmillert 		{ "ARR", ARR },
632c062391aSmillert 		{ "FCN", FCN },
633c062391aSmillert 		{ "FLD", FLD },
634c062391aSmillert 		{ "REC", REC },
635c062391aSmillert 		{ "CONVC", CONVC },
636c062391aSmillert 		{ "CONVO", CONVO },
637c062391aSmillert 		{ NULL, 0 }
638c062391aSmillert 	};
639c062391aSmillert 	static char buf[100];
640c062391aSmillert 	int i, len;
641c062391aSmillert 	char *cp = buf;
642c062391aSmillert 
643c062391aSmillert 	for (i = 0; flagtab[i].name != NULL; i++) {
644c062391aSmillert 		if ((flags & flagtab[i].value) != 0) {
645c062391aSmillert 			len = snprintf(cp, sizeof(buf) - (cp - buf),
646c062391aSmillert 			    "%s%s", cp > buf ? "|" : "", flagtab[i].name);
647*88689b4cSmillert 			if (len < 0 || (size_t)len >= sizeof(buf) - (cp - buf))
648c062391aSmillert 				FATAL("out of space in flags2str");
649c062391aSmillert 			cp += len;
650c062391aSmillert 		}
651c062391aSmillert 	}
652c062391aSmillert 
653c062391aSmillert 	return buf;
654c062391aSmillert }
655