xref: /openbsd/usr.bin/awk/tran.c (revision 60613915)
1*60613915Smillert /*	$OpenBSD: tran.c,v 1.31 2020/08/11 16:57: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 */
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 
74c062391aSmillert static void
75c062391aSmillert setfree(Cell *vp)
76c062391aSmillert {
77c062391aSmillert 	if (&vp->sval == FS || &vp->sval == RS ||
78c062391aSmillert 	    &vp->sval == OFS || &vp->sval == ORS ||
79c062391aSmillert 	    &vp->sval == OFMT || &vp->sval == CONVFMT ||
80c062391aSmillert 	    &vp->sval == FILENAME || &vp->sval == SUBSEP)
81c062391aSmillert 		vp->tval |= DONTFREE;
82c062391aSmillert 	else
83c062391aSmillert 		vp->tval &= ~DONTFREE;
84c062391aSmillert }
85c062391aSmillert 
866ab05f83Stholo void syminit(void)	/* initialize symbol table with builtin vars */
876ab05f83Stholo {
8807edfa4aSkstailey 	literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab);
896ab05f83Stholo 	/* this is used for if(x)... tests: */
906ab05f83Stholo 	nullloc = setsymtab("$zero&null", "", 0.0, NUM|STR|CON|DONTFREE, symtab);
9107edfa4aSkstailey 	nullnode = celltonode(nullloc, CCON);
926ab05f83Stholo 
9323cb51abSmillert 	fsloc = setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab);
9423cb51abSmillert 	FS = &fsloc->sval;
9502265e66Smillert 	rsloc = setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab);
9602265e66Smillert 	RS = &rsloc->sval;
9702265e66Smillert 	ofsloc = setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab);
9802265e66Smillert 	OFS = &ofsloc->sval;
9902265e66Smillert 	orsloc = setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab);
10002265e66Smillert 	ORS = &orsloc->sval;
1016ab05f83Stholo 	OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
1026ab05f83Stholo 	CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
103b2698ba9Smillert 	FILENAME = &setsymtab("FILENAME", "", 0.0, STR|DONTFREE, symtab)->sval;
1046ab05f83Stholo 	nfloc = setsymtab("NF", "", 0.0, NUM, symtab);
1056ab05f83Stholo 	NF = &nfloc->fval;
1066ab05f83Stholo 	nrloc = setsymtab("NR", "", 0.0, NUM, symtab);
1076ab05f83Stholo 	NR = &nrloc->fval;
1086ab05f83Stholo 	fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab);
1096ab05f83Stholo 	FNR = &fnrloc->fval;
11002265e66Smillert 	subseploc = setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab);
11102265e66Smillert 	SUBSEP = &subseploc->sval;
1126ab05f83Stholo 	rstartloc = setsymtab("RSTART", "", 0.0, NUM, symtab);
1136ab05f83Stholo 	RSTART = &rstartloc->fval;
1146ab05f83Stholo 	rlengthloc = setsymtab("RLENGTH", "", 0.0, NUM, symtab);
1156ab05f83Stholo 	RLENGTH = &rlengthloc->fval;
1166ab05f83Stholo 	symtabloc = setsymtab("SYMTAB", "", 0.0, ARR, symtab);
11792497129Smillert 	free(symtabloc->sval);
1186ab05f83Stholo 	symtabloc->sval = (char *) symtab;
1196ab05f83Stholo }
1206ab05f83Stholo 
12107edfa4aSkstailey void arginit(int ac, char **av)	/* set up ARGV and ARGC */
1226ab05f83Stholo {
1236ab05f83Stholo 	Cell *cp;
1246ab05f83Stholo 	int i;
12507edfa4aSkstailey 	char temp[50];
1266ab05f83Stholo 
1276ab05f83Stholo 	ARGC = &setsymtab("ARGC", "", (Awkfloat) ac, NUM, symtab)->fval;
1286ab05f83Stholo 	cp = setsymtab("ARGV", "", 0.0, ARR, symtab);
1296ab05f83Stholo 	ARGVtab = makesymtab(NSYMTAB);	/* could be (int) ARGC as well */
13092497129Smillert 	free(cp->sval);
1316ab05f83Stholo 	cp->sval = (char *) ARGVtab;
1326ab05f83Stholo 	for (i = 0; i < ac; i++) {
1339e405e78Sderaadt 		snprintf(temp, sizeof temp, "%d", i);
134a4fa8700Smillert 		if (is_number(*av))
1356ab05f83Stholo 			setsymtab(temp, *av, atof(*av), STR|NUM, ARGVtab);
1366ab05f83Stholo 		else
1376ab05f83Stholo 			setsymtab(temp, *av, 0.0, STR, ARGVtab);
1386ab05f83Stholo 		av++;
1396ab05f83Stholo 	}
1406ab05f83Stholo }
1416ab05f83Stholo 
1426ab05f83Stholo void envinit(char **envp)	/* set up ENVIRON variable */
1436ab05f83Stholo {
1446ab05f83Stholo 	Cell *cp;
1456ab05f83Stholo 	char *p;
1466ab05f83Stholo 
1476ab05f83Stholo 	cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab);
1486ab05f83Stholo 	ENVtab = makesymtab(NSYMTAB);
14992497129Smillert 	free(cp->sval);
1506ab05f83Stholo 	cp->sval = (char *) ENVtab;
1516ab05f83Stholo 	for ( ; *envp; envp++) {
15207edfa4aSkstailey 		if ((p = strchr(*envp, '=')) == NULL)
1536ab05f83Stholo 			continue;
154a27f5228Smillert 		if( p == *envp ) /* no left hand side name in env string */
155a27f5228Smillert 			continue;
1566ab05f83Stholo 		*p++ = 0;	/* split into two strings at = */
157a4fa8700Smillert 		if (is_number(p))
1586ab05f83Stholo 			setsymtab(*envp, p, atof(p), STR|NUM, ENVtab);
1596ab05f83Stholo 		else
1606ab05f83Stholo 			setsymtab(*envp, p, 0.0, STR, ENVtab);
1616ab05f83Stholo 		p[-1] = '=';	/* restore in case env is passed down to a shell */
1626ab05f83Stholo 	}
1636ab05f83Stholo }
1646ab05f83Stholo 
1656ab05f83Stholo Array *makesymtab(int n)	/* make a new symbol table */
1666ab05f83Stholo {
1676ab05f83Stholo 	Array *ap;
1686ab05f83Stholo 	Cell **tp;
1696ab05f83Stholo 
170d7cce239Smillert 	ap = malloc(sizeof(*ap));
171d7cce239Smillert 	tp = calloc(n, sizeof(*tp));
1726ab05f83Stholo 	if (ap == NULL || tp == NULL)
1737b11b857Smillert 		FATAL("out of space in makesymtab");
1746ab05f83Stholo 	ap->nelem = 0;
1756ab05f83Stholo 	ap->size = n;
1766ab05f83Stholo 	ap->tab = tp;
1776ab05f83Stholo 	return(ap);
1786ab05f83Stholo }
1796ab05f83Stholo 
1806ab05f83Stholo void freesymtab(Cell *ap)	/* free a symbol table */
1816ab05f83Stholo {
1826ab05f83Stholo 	Cell *cp, *temp;
1836ab05f83Stholo 	Array *tp;
1846ab05f83Stholo 	int i;
1856ab05f83Stholo 
1866ab05f83Stholo 	if (!isarr(ap))
1876ab05f83Stholo 		return;
1886ab05f83Stholo 	tp = (Array *) ap->sval;
1896ab05f83Stholo 	if (tp == NULL)
1906ab05f83Stholo 		return;
1916ab05f83Stholo 	for (i = 0; i < tp->size; i++) {
1926ab05f83Stholo 		for (cp = tp->tab[i]; cp != NULL; cp = temp) {
1936ab05f83Stholo 			xfree(cp->nval);
1946ab05f83Stholo 			if (freeable(cp))
1956ab05f83Stholo 				xfree(cp->sval);
1966ab05f83Stholo 			temp = cp->cnext;	/* avoids freeing then using */
19707edfa4aSkstailey 			free(cp);
1989a69093aSmillert 			tp->nelem--;
1996ab05f83Stholo 		}
200c0fa3611Smillert 		tp->tab[i] = NULL;
2016ab05f83Stholo 	}
2029a69093aSmillert 	if (tp->nelem != 0)
2039a69093aSmillert 		WARNING("can't happen: inconsistent element count freeing %s", ap->nval);
20407edfa4aSkstailey 	free(tp->tab);
20507edfa4aSkstailey 	free(tp);
2066ab05f83Stholo }
2076ab05f83Stholo 
2089a69093aSmillert void freeelem(Cell *ap, const char *s)	/* free elem s from ap (i.e., ap["s"] */
2096ab05f83Stholo {
2106ab05f83Stholo 	Array *tp;
2116ab05f83Stholo 	Cell *p, *prev = NULL;
2126ab05f83Stholo 	int h;
2136ab05f83Stholo 
2146ab05f83Stholo 	tp = (Array *) ap->sval;
2156ab05f83Stholo 	h = hash(s, tp->size);
2166ab05f83Stholo 	for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext)
21707edfa4aSkstailey 		if (strcmp(s, p->nval) == 0) {
2186ab05f83Stholo 			if (prev == NULL)	/* 1st one */
2196ab05f83Stholo 				tp->tab[h] = p->cnext;
2206ab05f83Stholo 			else			/* middle somewhere */
2216ab05f83Stholo 				prev->cnext = p->cnext;
2226ab05f83Stholo 			if (freeable(p))
2236ab05f83Stholo 				xfree(p->sval);
2246ab05f83Stholo 			free(p->nval);
22507edfa4aSkstailey 			free(p);
2266ab05f83Stholo 			tp->nelem--;
2276ab05f83Stholo 			return;
2286ab05f83Stholo 		}
2296ab05f83Stholo }
2306ab05f83Stholo 
2319a69093aSmillert Cell *setsymtab(const char *n, const char *s, Awkfloat f, unsigned t, Array *tp)
2326ab05f83Stholo {
2336ab05f83Stholo 	int h;
2346ab05f83Stholo 	Cell *p;
2356ab05f83Stholo 
2366ab05f83Stholo 	if (n != NULL && (p = lookup(n, tp)) != NULL) {
237115bd590Smillert 		DPRINTF("setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n",
238115bd590Smillert 			(void*)p, NN(p->nval), NN(p->sval), p->fval, p->tval);
2396ab05f83Stholo 		return(p);
2406ab05f83Stholo 	}
241d7cce239Smillert 	p = malloc(sizeof(*p));
2426ab05f83Stholo 	if (p == NULL)
2437b11b857Smillert 		FATAL("out of space for symbol table at %s", n);
2446ab05f83Stholo 	p->nval = tostring(n);
2456ab05f83Stholo 	p->sval = s ? tostring(s) : tostring("");
2466ab05f83Stholo 	p->fval = f;
2476ab05f83Stholo 	p->tval = t;
2486ab05f83Stholo 	p->csub = CUNK;
2496ab05f83Stholo 	p->ctype = OCELL;
2506ab05f83Stholo 	tp->nelem++;
2516ab05f83Stholo 	if (tp->nelem > FULLTAB * tp->size)
2526ab05f83Stholo 		rehash(tp);
2536ab05f83Stholo 	h = hash(n, tp->size);
2546ab05f83Stholo 	p->cnext = tp->tab[h];
2556ab05f83Stholo 	tp->tab[h] = p;
256115bd590Smillert 	DPRINTF("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n",
257115bd590Smillert 		(void*)p, p->nval, p->sval, p->fval, p->tval);
2586ab05f83Stholo 	return(p);
2596ab05f83Stholo }
2606ab05f83Stholo 
2619a69093aSmillert int hash(const char *s, int n)	/* form hash value for string s */
2626ab05f83Stholo {
2636ab05f83Stholo 	unsigned hashval;
2646ab05f83Stholo 
2656ab05f83Stholo 	for (hashval = 0; *s != '\0'; s++)
2666ab05f83Stholo 		hashval = (*s + 31 * hashval);
2676ab05f83Stholo 	return hashval % n;
2686ab05f83Stholo }
2696ab05f83Stholo 
2706ab05f83Stholo void rehash(Array *tp)	/* rehash items in small table into big one */
2716ab05f83Stholo {
2726ab05f83Stholo 	int i, nh, nsz;
2736ab05f83Stholo 	Cell *cp, *op, **np;
2746ab05f83Stholo 
2756ab05f83Stholo 	nsz = GROWTAB * tp->size;
276d7cce239Smillert 	np = calloc(nsz, sizeof(*np));
2776ab05f83Stholo 	if (np == NULL)		/* can't do it, but can keep running. */
2786ab05f83Stholo 		return;		/* someone else will run out later. */
2796ab05f83Stholo 	for (i = 0; i < tp->size; i++) {
2806ab05f83Stholo 		for (cp = tp->tab[i]; cp; cp = op) {
2816ab05f83Stholo 			op = cp->cnext;
2826ab05f83Stholo 			nh = hash(cp->nval, nsz);
2836ab05f83Stholo 			cp->cnext = np[nh];
2846ab05f83Stholo 			np[nh] = cp;
2856ab05f83Stholo 		}
2866ab05f83Stholo 	}
28707edfa4aSkstailey 	free(tp->tab);
2886ab05f83Stholo 	tp->tab = np;
2896ab05f83Stholo 	tp->size = nsz;
2906ab05f83Stholo }
2916ab05f83Stholo 
2929a69093aSmillert Cell *lookup(const char *s, Array *tp)	/* look for s in tp */
2936ab05f83Stholo {
294271018d0Smillert 	Cell *p;
2956ab05f83Stholo 	int h;
2966ab05f83Stholo 
2976ab05f83Stholo 	h = hash(s, tp->size);
298271018d0Smillert 	for (p = tp->tab[h]; p != NULL; p = p->cnext)
29907edfa4aSkstailey 		if (strcmp(s, p->nval) == 0)
3006ab05f83Stholo 			return(p);	/* found it */
3016ab05f83Stholo 	return(NULL);			/* not found */
3026ab05f83Stholo }
3036ab05f83Stholo 
3046ab05f83Stholo Awkfloat setfval(Cell *vp, Awkfloat f)	/* set float val of a Cell */
3056ab05f83Stholo {
30607edfa4aSkstailey 	int fldno;
30707edfa4aSkstailey 
308c062391aSmillert 	f += 0.0;		/* normalise negative zero to positive zero */
3096ab05f83Stholo 	if ((vp->tval & (NUM | STR)) == 0)
3106ab05f83Stholo 		funnyvar(vp, "assign to");
31107edfa4aSkstailey 	if (isfld(vp)) {
312f81b289fSmillert 		donerec = false;	/* mark $0 invalid */
31307edfa4aSkstailey 		fldno = atoi(vp->nval);
31407edfa4aSkstailey 		if (fldno > *NF)
31507edfa4aSkstailey 			newfld(fldno);
316115bd590Smillert 		DPRINTF("setting field %d to %g\n", fldno, f);
317c062391aSmillert 	} else if (&vp->fval == NF) {
318f81b289fSmillert 		donerec = false;	/* mark $0 invalid */
319c062391aSmillert 		setlastfld(f);
320115bd590Smillert 		DPRINTF("setting NF to %g\n", f);
32107edfa4aSkstailey 	} else if (isrec(vp)) {
322f81b289fSmillert 		donefld = false;	/* mark $1... invalid */
323f81b289fSmillert 		donerec = true;
324c0fa3611Smillert 		savefs();
32502265e66Smillert 	} else if (vp == ofsloc) {
326f81b289fSmillert 		if (!donerec)
32702265e66Smillert 			recbld();
3286ab05f83Stholo 	}
329a4fa8700Smillert 	if (freeable(vp))
330a4fa8700Smillert 		xfree(vp->sval); /* free any previous string */
331c062391aSmillert 	vp->tval &= ~(STR|CONVC|CONVO); /* mark string invalid */
332c062391aSmillert 	vp->fmt = NULL;
3336ab05f83Stholo 	vp->tval |= NUM;	/* mark number ok */
334a8d6f668Smillert 	if (f == -0)  /* who would have thought this possible? */
335a8d6f668Smillert 		f = 0;
336115bd590Smillert 	DPRINTF("setfval %p: %s = %g, t=%o\n", (void*)vp, NN(vp->nval), f, vp->tval);
3376ab05f83Stholo 	return vp->fval = f;
3386ab05f83Stholo }
3396ab05f83Stholo 
3409a69093aSmillert void funnyvar(Cell *vp, const char *rw)
3416ab05f83Stholo {
34207edfa4aSkstailey 	if (isarr(vp))
3437b11b857Smillert 		FATAL("can't %s %s; it's an array name.", rw, vp->nval);
3446ab05f83Stholo 	if (vp->tval & FCN)
3457b11b857Smillert 		FATAL("can't %s %s; it's a function.", rw, vp->nval);
3467b11b857Smillert 	WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o",
3476685ce51Smillert 		(void *)vp, vp->nval, vp->sval, vp->fval, vp->tval);
3486ab05f83Stholo }
3496ab05f83Stholo 
3509a69093aSmillert char *setsval(Cell *vp, const char *s)	/* set string val of a Cell */
3516ab05f83Stholo {
3526ab05f83Stholo 	char *t;
35307edfa4aSkstailey 	int fldno;
354c062391aSmillert 	Awkfloat f;
3556ab05f83Stholo 
356115bd590Smillert 	DPRINTF("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n",
357115bd590Smillert 		(void*)vp, NN(vp->nval), s, vp->tval, donerec, donefld);
3586ab05f83Stholo 	if ((vp->tval & (NUM | STR)) == 0)
3596ab05f83Stholo 		funnyvar(vp, "assign to");
36007edfa4aSkstailey 	if (isfld(vp)) {
361f81b289fSmillert 		donerec = false;	/* mark $0 invalid */
36207edfa4aSkstailey 		fldno = atoi(vp->nval);
36307edfa4aSkstailey 		if (fldno > *NF)
36407edfa4aSkstailey 			newfld(fldno);
365*60613915Smillert 		DPRINTF("setting field %d to %s (%p)\n", fldno, s, (const void*)s);
36607edfa4aSkstailey 	} else if (isrec(vp)) {
367f81b289fSmillert 		donefld = false;	/* mark $1... invalid */
368f81b289fSmillert 		donerec = true;
369c0fa3611Smillert 		savefs();
37002265e66Smillert 	} else if (vp == ofsloc) {
371f81b289fSmillert 		if (!donerec)
372c062391aSmillert 			recbld();
3736ab05f83Stholo 	}
374c062391aSmillert 	t = s ? tostring(s) : tostring("");	/* in case it's self-assign */
3756ab05f83Stholo 	if (freeable(vp))
3766ab05f83Stholo 		xfree(vp->sval);
377c062391aSmillert 	vp->tval &= ~(NUM|CONVC|CONVO);
3784eb91dacSjmc 	vp->tval |= STR;
379c062391aSmillert 	vp->fmt = NULL;
380c062391aSmillert 	setfree(vp);
381115bd590Smillert 	DPRINTF("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n",
382*60613915Smillert 		(void*)vp, NN(vp->nval), t, (void*)t, vp->tval, donerec, donefld);
383c062391aSmillert 	vp->sval = t;
384c062391aSmillert 	if (&vp->fval == NF) {
385f81b289fSmillert 		donerec = false;	/* mark $0 invalid */
386c062391aSmillert 		f = getfval(vp);
387c062391aSmillert 		setlastfld(f);
388115bd590Smillert 		DPRINTF("setting NF to %g\n", f);
389c062391aSmillert 	}
390c062391aSmillert 
391c062391aSmillert 	return(vp->sval);
3926ab05f83Stholo }
3936ab05f83Stholo 
3946ab05f83Stholo Awkfloat getfval(Cell *vp)	/* get float val of a Cell */
3956ab05f83Stholo {
3966ab05f83Stholo 	if ((vp->tval & (NUM | STR)) == 0)
3976ab05f83Stholo 		funnyvar(vp, "read value of");
398f81b289fSmillert 	if (isfld(vp) && !donefld)
3996ab05f83Stholo 		fldbld();
400f81b289fSmillert 	else if (isrec(vp) && !donerec)
4016ab05f83Stholo 		recbld();
4026ab05f83Stholo 	if (!isnum(vp)) {	/* not a number */
4036ab05f83Stholo 		vp->fval = atof(vp->sval);	/* best guess */
404a4fa8700Smillert 		if (is_number(vp->sval) && !(vp->tval&CON))
4056ab05f83Stholo 			vp->tval |= NUM;	/* make NUM only sparingly */
4066ab05f83Stholo 	}
407115bd590Smillert 	DPRINTF("getfval %p: %s = %g, t=%o\n",
408115bd590Smillert 		(void*)vp, NN(vp->nval), vp->fval, vp->tval);
4096ab05f83Stholo 	return(vp->fval);
4106ab05f83Stholo }
4116ab05f83Stholo 
4129a69093aSmillert static char *get_str_val(Cell *vp, char **fmt)        /* get string val of a Cell */
4136ab05f83Stholo {
414a4a48c73Smillert 	int n;
4156ab05f83Stholo 	double dtemp;
4166ab05f83Stholo 
4176ab05f83Stholo 	if ((vp->tval & (NUM | STR)) == 0)
4186ab05f83Stholo 		funnyvar(vp, "read value of");
419f81b289fSmillert 	if (isfld(vp) && ! donefld)
4206ab05f83Stholo 		fldbld();
421f81b289fSmillert 	else if (isrec(vp) && ! donerec)
4226ab05f83Stholo 		recbld();
423c062391aSmillert 
424c062391aSmillert 	/*
425c062391aSmillert 	 * ADR: This is complicated and more fragile than is desirable.
426c062391aSmillert 	 * Retrieving a string value for a number associates the string
427c062391aSmillert 	 * value with the scalar.  Previously, the string value was
428c062391aSmillert 	 * sticky, meaning if converted via OFMT that became the value
429c062391aSmillert 	 * (even though POSIX wants it to be via CONVFMT). Or if CONVFMT
430c062391aSmillert 	 * changed after a string value was retrieved, the original value
431c062391aSmillert 	 * was maintained and used.  Also not per POSIX.
432c062391aSmillert 	 *
433c062391aSmillert 	 * We work around this design by adding two additional flags,
434c062391aSmillert 	 * CONVC and CONVO, indicating how the string value was
435c062391aSmillert 	 * obtained (via CONVFMT or OFMT) and _also_ maintaining a copy
436c062391aSmillert 	 * of the pointer to the xFMT format string used for the
437c062391aSmillert 	 * conversion.  This pointer is only read, **never** dereferenced.
438c062391aSmillert 	 * The next time we do a conversion, if it's coming from the same
439c062391aSmillert 	 * xFMT as last time, and the pointer value is different, we
440c062391aSmillert 	 * know that the xFMT format string changed, and we need to
441c062391aSmillert 	 * redo the conversion. If it's the same, we don't have to.
442c062391aSmillert 	 *
443c062391aSmillert 	 * There are also several cases where we don't do a conversion,
444c062391aSmillert 	 * such as for a field (see the checks below).
445c062391aSmillert 	 */
446c062391aSmillert 
447c062391aSmillert 	/* Don't duplicate the code for actually updating the value */
448c062391aSmillert #define update_str_val(vp) \
449c062391aSmillert 	{ \
450c062391aSmillert 		if (freeable(vp)) \
451c062391aSmillert 			xfree(vp->sval); \
452c062391aSmillert 		if (modf(vp->fval, &dtemp) == 0)	/* it's integral */ \
453c062391aSmillert 			n = asprintf(&vp->sval, "%.30g", vp->fval); \
454c062391aSmillert 		else \
455c062391aSmillert 			n = asprintf(&vp->sval, *fmt, vp->fval); \
456c062391aSmillert 		if (n == -1) \
457c062391aSmillert 			FATAL("out of space in get_str_val"); \
458c062391aSmillert 		vp->tval &= ~DONTFREE; \
459c062391aSmillert 		vp->tval |= STR; \
4606ab05f83Stholo 	}
461c062391aSmillert 
462c062391aSmillert 	if (isstr(vp) == 0) {
463c062391aSmillert 		update_str_val(vp);
464c062391aSmillert 		if (fmt == OFMT) {
465c062391aSmillert 			vp->tval &= ~CONVC;
466c062391aSmillert 			vp->tval |= CONVO;
467c062391aSmillert 		} else {
468c062391aSmillert 			/* CONVFMT */
469c062391aSmillert 			vp->tval &= ~CONVO;
470c062391aSmillert 			vp->tval |= CONVC;
471c062391aSmillert 		}
472c062391aSmillert 		vp->fmt = *fmt;
473c062391aSmillert 	} else if ((vp->tval & DONTFREE) != 0 || ! isnum(vp) || isfld(vp)) {
474c062391aSmillert 		goto done;
475c062391aSmillert 	} else if (isstr(vp)) {
476c062391aSmillert 		if (fmt == OFMT) {
477c062391aSmillert 			if ((vp->tval & CONVC) != 0
478c062391aSmillert 			    || ((vp->tval & CONVO) != 0 && vp->fmt != *fmt)) {
479c062391aSmillert 				update_str_val(vp);
480c062391aSmillert 				vp->tval &= ~CONVC;
481c062391aSmillert 				vp->tval |= CONVO;
482c062391aSmillert 				vp->fmt = *fmt;
483c062391aSmillert 			}
484c062391aSmillert 		} else {
485c062391aSmillert 			/* CONVFMT */
486c062391aSmillert 			if ((vp->tval & CONVO) != 0
487c062391aSmillert 			    || ((vp->tval & CONVC) != 0 && vp->fmt != *fmt)) {
488c062391aSmillert 				update_str_val(vp);
489c062391aSmillert 				vp->tval &= ~CONVO;
490c062391aSmillert 				vp->tval |= CONVC;
491c062391aSmillert 				vp->fmt = *fmt;
492c062391aSmillert 			}
493c062391aSmillert 		}
494c062391aSmillert 	}
495c062391aSmillert done:
496115bd590Smillert 	DPRINTF("getsval %p: %s = \"%s (%p)\", t=%o\n",
497*60613915Smillert 		(void*)vp, NN(vp->nval), vp->sval, (void*)vp->sval, vp->tval);
4986ab05f83Stholo 	return(vp->sval);
4996ab05f83Stholo }
5006ab05f83Stholo 
5019a69093aSmillert char *getsval(Cell *vp)       /* get string val of a Cell */
5029a69093aSmillert {
5039a69093aSmillert       return get_str_val(vp, CONVFMT);
5049a69093aSmillert }
5059a69093aSmillert 
5069a69093aSmillert char *getpssval(Cell *vp)     /* get string val of a Cell for print */
5079a69093aSmillert {
5089a69093aSmillert       return get_str_val(vp, OFMT);
5099a69093aSmillert }
5109a69093aSmillert 
5119a69093aSmillert 
5129a69093aSmillert char *tostring(const char *s)	/* make a copy of string s */
5136ab05f83Stholo {
514d7cce239Smillert 	char *p = strdup(s);
5158055ea94Smillert 	if (p == NULL)
5168055ea94Smillert 		FATAL("out of space in tostring on %s", s);
517d7cce239Smillert 	return(p);
5186ab05f83Stholo }
5196ab05f83Stholo 
5206685ce51Smillert char *tostringN(const char *s, size_t n)	/* make a copy of string s */
5216685ce51Smillert {
5226685ce51Smillert 	char *p;
5236685ce51Smillert 
5246685ce51Smillert 	p = malloc(n);
5256685ce51Smillert 	if (p == NULL)
526144915fcSmillert 		FATAL("out of space in tostringN %zu", n);
527144915fcSmillert 	if (strlcpy(p, s, n) >= n)
528144915fcSmillert 		FATAL("out of space in tostringN on %s", s);
5296685ce51Smillert 	return(p);
5306685ce51Smillert }
5316685ce51Smillert 
532c0fa3611Smillert Cell *catstr(Cell *a, Cell *b) /* concatenate a and b */
533c0fa3611Smillert {
534c0fa3611Smillert 	Cell *c;
535c0fa3611Smillert 	char *p;
536c0fa3611Smillert 	char *sa = getsval(a);
537c0fa3611Smillert 	char *sb = getsval(b);
538c0fa3611Smillert 	size_t l = strlen(sa) + strlen(sb) + 1;
539c0fa3611Smillert 	p = malloc(l);
540c0fa3611Smillert 	if (p == NULL)
541c0fa3611Smillert 		FATAL("out of space concatenating %s and %s", sa, sb);
542c0fa3611Smillert 	snprintf(p, l, "%s%s", sa, sb);
543fabd211eSmillert 
544fabd211eSmillert 	l++;	// add room for ' '
545fabd211eSmillert 	char *newbuf = malloc(l);
54692497129Smillert 	if (newbuf == NULL)
54792497129Smillert 		FATAL("out of space concatenating %s and %s", sa, sb);
54892497129Smillert 	// See string() in lex.c; a string "xx" is stored in the symbol
54992497129Smillert 	// table as "xx ".
550fabd211eSmillert 	snprintf(newbuf, l, "%s ", p);
55192497129Smillert 	c = setsymtab(newbuf, p, 0.0, CON|STR|DONTFREE, symtab);
552c0fa3611Smillert 	free(p);
55392497129Smillert 	free(newbuf);
554c0fa3611Smillert 	return c;
555c0fa3611Smillert }
556c0fa3611Smillert 
5579a69093aSmillert char *qstring(const char *is, int delim)	/* collect string up to next delim */
5586ab05f83Stholo {
5599a69093aSmillert 	const char *os = is;
5606ab05f83Stholo 	int c, n;
561d7cce239Smillert 	const uschar *s = (const uschar *) is;
562a27f5228Smillert 	uschar *buf, *bp;
5636ab05f83Stholo 
564d7cce239Smillert 	if ((buf = malloc(strlen(is)+3)) == NULL)
5657b11b857Smillert 		FATAL( "out of space in qstring(%s)", s);
56607edfa4aSkstailey 	for (bp = buf; (c = *s) != delim; s++) {
5676ab05f83Stholo 		if (c == '\n')
5687b11b857Smillert 			SYNTAX( "newline in string %.20s...", os );
5696ab05f83Stholo 		else if (c != '\\')
57007edfa4aSkstailey 			*bp++ = c;
57107edfa4aSkstailey 		else {	/* \something */
5727b11b857Smillert 			c = *++s;
5737b11b857Smillert 			if (c == 0) {	/* \ at end */
5747b11b857Smillert 				*bp++ = '\\';
5757b11b857Smillert 				break;	/* for loop */
5767b11b857Smillert 			}
5777b11b857Smillert 			switch (c) {
57807edfa4aSkstailey 			case '\\':	*bp++ = '\\'; break;
57907edfa4aSkstailey 			case 'n':	*bp++ = '\n'; break;
58007edfa4aSkstailey 			case 't':	*bp++ = '\t'; break;
58107edfa4aSkstailey 			case 'b':	*bp++ = '\b'; break;
58207edfa4aSkstailey 			case 'f':	*bp++ = '\f'; break;
58307edfa4aSkstailey 			case 'r':	*bp++ = '\r'; break;
584c0fa3611Smillert 			case 'v':	*bp++ = '\v'; break;
585c0fa3611Smillert 			case 'a':	*bp++ = '\a'; break;
5866ab05f83Stholo 			default:
5876ab05f83Stholo 				if (!isdigit(c)) {
58807edfa4aSkstailey 					*bp++ = c;
5896ab05f83Stholo 					break;
5906ab05f83Stholo 				}
5916ab05f83Stholo 				n = c - '0';
5926ab05f83Stholo 				if (isdigit(s[1])) {
5936ab05f83Stholo 					n = 8 * n + *++s - '0';
5946ab05f83Stholo 					if (isdigit(s[1]))
5956ab05f83Stholo 						n = 8 * n + *++s - '0';
5966ab05f83Stholo 				}
59707edfa4aSkstailey 				*bp++ = n;
5986ab05f83Stholo 				break;
5996ab05f83Stholo 			}
6006ab05f83Stholo 		}
60107edfa4aSkstailey 	}
60207edfa4aSkstailey 	*bp++ = 0;
603a27f5228Smillert 	return (char *) buf;
6046ab05f83Stholo }
605c062391aSmillert 
606c062391aSmillert const char *flags2str(int flags)
607c062391aSmillert {
608c062391aSmillert 	static const struct ftab {
609c062391aSmillert 		const char *name;
610c062391aSmillert 		int value;
611c062391aSmillert 	} flagtab[] = {
612c062391aSmillert 		{ "NUM", NUM },
613c062391aSmillert 		{ "STR", STR },
614c062391aSmillert 		{ "DONTFREE", DONTFREE },
615c062391aSmillert 		{ "CON", CON },
616c062391aSmillert 		{ "ARR", ARR },
617c062391aSmillert 		{ "FCN", FCN },
618c062391aSmillert 		{ "FLD", FLD },
619c062391aSmillert 		{ "REC", REC },
620c062391aSmillert 		{ "CONVC", CONVC },
621c062391aSmillert 		{ "CONVO", CONVO },
622c062391aSmillert 		{ NULL, 0 }
623c062391aSmillert 	};
624c062391aSmillert 	static char buf[100];
625c062391aSmillert 	int i, len;
626c062391aSmillert 	char *cp = buf;
627c062391aSmillert 
628c062391aSmillert 	for (i = 0; flagtab[i].name != NULL; i++) {
629c062391aSmillert 		if ((flags & flagtab[i].value) != 0) {
630c062391aSmillert 			len = snprintf(cp, sizeof(buf) - (cp - buf),
631c062391aSmillert 			    "%s%s", cp > buf ? "|" : "", flagtab[i].name);
632c062391aSmillert 			if (len < 0 || len >= sizeof(buf) - (cp - buf))
633c062391aSmillert 				FATAL("out of space in flags2str");
634c062391aSmillert 			cp += len;
635c062391aSmillert 		}
636c062391aSmillert 	}
637c062391aSmillert 
638c062391aSmillert 	return buf;
639c062391aSmillert }
640