xref: /aosp_15_r20/external/one-true-awk/tran.c (revision 9a7741de182b2776d7b30d6355f2585c0780a51b)
1*9a7741deSElliott Hughes /****************************************************************
2*9a7741deSElliott Hughes Copyright (C) Lucent Technologies 1997
3*9a7741deSElliott Hughes All Rights Reserved
4*9a7741deSElliott Hughes 
5*9a7741deSElliott Hughes Permission to use, copy, modify, and distribute this software and
6*9a7741deSElliott Hughes its documentation for any purpose and without fee is hereby
7*9a7741deSElliott Hughes granted, provided that the above copyright notice appear in all
8*9a7741deSElliott Hughes copies and that both that the copyright notice and this
9*9a7741deSElliott Hughes permission notice and warranty disclaimer appear in supporting
10*9a7741deSElliott Hughes documentation, and that the name Lucent Technologies or any of
11*9a7741deSElliott Hughes its entities not be used in advertising or publicity pertaining
12*9a7741deSElliott Hughes to distribution of the software without specific, written prior
13*9a7741deSElliott Hughes permission.
14*9a7741deSElliott Hughes 
15*9a7741deSElliott Hughes LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16*9a7741deSElliott Hughes INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
17*9a7741deSElliott Hughes IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
18*9a7741deSElliott Hughes SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19*9a7741deSElliott Hughes WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
20*9a7741deSElliott Hughes IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
21*9a7741deSElliott Hughes ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
22*9a7741deSElliott Hughes THIS SOFTWARE.
23*9a7741deSElliott Hughes ****************************************************************/
24*9a7741deSElliott Hughes 
25*9a7741deSElliott Hughes #define	DEBUG
26*9a7741deSElliott Hughes #include <stdio.h>
27*9a7741deSElliott Hughes #include <math.h>
28*9a7741deSElliott Hughes #include <ctype.h>
29*9a7741deSElliott Hughes #include <string.h>
30*9a7741deSElliott Hughes #include <stdlib.h>
31*9a7741deSElliott Hughes #include "awk.h"
32*9a7741deSElliott Hughes 
33*9a7741deSElliott Hughes #define	FULLTAB	2	/* rehash when table gets this x full */
34*9a7741deSElliott Hughes #define	GROWTAB 4	/* grow table by this factor */
35*9a7741deSElliott Hughes 
36*9a7741deSElliott Hughes Array	*symtab;	/* main symbol table */
37*9a7741deSElliott Hughes 
38*9a7741deSElliott Hughes char	**FS;		/* initial field sep */
39*9a7741deSElliott Hughes char	**RS;		/* initial record sep */
40*9a7741deSElliott Hughes char	**OFS;		/* output field sep */
41*9a7741deSElliott Hughes char	**ORS;		/* output record sep */
42*9a7741deSElliott Hughes char	**OFMT;		/* output format for numbers */
43*9a7741deSElliott Hughes char	**CONVFMT;	/* format for conversions in getsval */
44*9a7741deSElliott Hughes Awkfloat *NF;		/* number of fields in current record */
45*9a7741deSElliott Hughes Awkfloat *NR;		/* number of current record */
46*9a7741deSElliott Hughes Awkfloat *FNR;		/* number of current record in current file */
47*9a7741deSElliott Hughes char	**FILENAME;	/* current filename argument */
48*9a7741deSElliott Hughes Awkfloat *ARGC;		/* number of arguments from command line */
49*9a7741deSElliott Hughes char	**SUBSEP;	/* subscript separator for a[i,j,k]; default \034 */
50*9a7741deSElliott Hughes Awkfloat *RSTART;	/* start of re matched with ~; origin 1 (!) */
51*9a7741deSElliott Hughes Awkfloat *RLENGTH;	/* length of same */
52*9a7741deSElliott Hughes 
53*9a7741deSElliott Hughes Cell	*fsloc;		/* FS */
54*9a7741deSElliott Hughes Cell	*nrloc;		/* NR */
55*9a7741deSElliott Hughes Cell	*nfloc;		/* NF */
56*9a7741deSElliott Hughes Cell	*fnrloc;	/* FNR */
57*9a7741deSElliott Hughes Cell	*ofsloc;	/* OFS */
58*9a7741deSElliott Hughes Cell	*orsloc;	/* ORS */
59*9a7741deSElliott Hughes Cell	*rsloc;		/* RS */
60*9a7741deSElliott Hughes Cell	*ARGVcell;	/* cell with symbol table containing ARGV[...] */
61*9a7741deSElliott Hughes Cell	*rstartloc;	/* RSTART */
62*9a7741deSElliott Hughes Cell	*rlengthloc;	/* RLENGTH */
63*9a7741deSElliott Hughes Cell	*subseploc;	/* SUBSEP */
64*9a7741deSElliott Hughes Cell	*symtabloc;	/* SYMTAB */
65*9a7741deSElliott Hughes 
66*9a7741deSElliott Hughes Cell	*nullloc;	/* a guaranteed empty cell */
67*9a7741deSElliott Hughes Node	*nullnode;	/* zero&null, converted into a node for comparisons */
68*9a7741deSElliott Hughes Cell	*literal0;
69*9a7741deSElliott Hughes 
70*9a7741deSElliott Hughes extern Cell **fldtab;
71*9a7741deSElliott Hughes 
syminit(void)72*9a7741deSElliott Hughes void syminit(void)	/* initialize symbol table with builtin vars */
73*9a7741deSElliott Hughes {
74*9a7741deSElliott Hughes 	literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab);
75*9a7741deSElliott Hughes 	/* this is used for if(x)... tests: */
76*9a7741deSElliott Hughes 	nullloc = setsymtab("$zero&null", "", 0.0, NUM|STR|CON|DONTFREE, symtab);
77*9a7741deSElliott Hughes 	nullnode = celltonode(nullloc, CCON);
78*9a7741deSElliott Hughes 
79*9a7741deSElliott Hughes 	fsloc = setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab);
80*9a7741deSElliott Hughes 	FS = &fsloc->sval;
81*9a7741deSElliott Hughes 	rsloc = setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab);
82*9a7741deSElliott Hughes 	RS = &rsloc->sval;
83*9a7741deSElliott Hughes 	ofsloc = setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab);
84*9a7741deSElliott Hughes 	OFS = &ofsloc->sval;
85*9a7741deSElliott Hughes 	orsloc = setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab);
86*9a7741deSElliott Hughes 	ORS = &orsloc->sval;
87*9a7741deSElliott Hughes 	OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
88*9a7741deSElliott Hughes 	CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
89*9a7741deSElliott Hughes 	FILENAME = &setsymtab("FILENAME", "", 0.0, STR|DONTFREE, symtab)->sval;
90*9a7741deSElliott Hughes 	nfloc = setsymtab("NF", "", 0.0, NUM, symtab);
91*9a7741deSElliott Hughes 	NF = &nfloc->fval;
92*9a7741deSElliott Hughes 	nrloc = setsymtab("NR", "", 0.0, NUM, symtab);
93*9a7741deSElliott Hughes 	NR = &nrloc->fval;
94*9a7741deSElliott Hughes 	fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab);
95*9a7741deSElliott Hughes 	FNR = &fnrloc->fval;
96*9a7741deSElliott Hughes 	subseploc = setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab);
97*9a7741deSElliott Hughes 	SUBSEP = &subseploc->sval;
98*9a7741deSElliott Hughes 	rstartloc = setsymtab("RSTART", "", 0.0, NUM, symtab);
99*9a7741deSElliott Hughes 	RSTART = &rstartloc->fval;
100*9a7741deSElliott Hughes 	rlengthloc = setsymtab("RLENGTH", "", 0.0, NUM, symtab);
101*9a7741deSElliott Hughes 	RLENGTH = &rlengthloc->fval;
102*9a7741deSElliott Hughes 	symtabloc = setsymtab("SYMTAB", "", 0.0, ARR, symtab);
103*9a7741deSElliott Hughes 	free(symtabloc->sval);
104*9a7741deSElliott Hughes 	symtabloc->sval = (char *) symtab;
105*9a7741deSElliott Hughes }
106*9a7741deSElliott Hughes 
arginit(int ac,char ** av)107*9a7741deSElliott Hughes void arginit(int ac, char **av)	/* set up ARGV and ARGC */
108*9a7741deSElliott Hughes {
109*9a7741deSElliott Hughes 	Array *ap;
110*9a7741deSElliott Hughes 	Cell *cp;
111*9a7741deSElliott Hughes 	int i;
112*9a7741deSElliott Hughes 	char temp[50];
113*9a7741deSElliott Hughes 
114*9a7741deSElliott Hughes 	ARGC = &setsymtab("ARGC", "", (Awkfloat) ac, NUM, symtab)->fval;
115*9a7741deSElliott Hughes 	cp = setsymtab("ARGV", "", 0.0, ARR, symtab);
116*9a7741deSElliott Hughes 	ap = makesymtab(NSYMTAB);	/* could be (int) ARGC as well */
117*9a7741deSElliott Hughes 	free(cp->sval);
118*9a7741deSElliott Hughes 	cp->sval = (char *) ap;
119*9a7741deSElliott Hughes 	for (i = 0; i < ac; i++) {
120*9a7741deSElliott Hughes 		double result;
121*9a7741deSElliott Hughes 
122*9a7741deSElliott Hughes 		sprintf(temp, "%d", i);
123*9a7741deSElliott Hughes 		if (is_number(*av, & result))
124*9a7741deSElliott Hughes 			setsymtab(temp, *av, result, STR|NUM, ap);
125*9a7741deSElliott Hughes 		else
126*9a7741deSElliott Hughes 			setsymtab(temp, *av, 0.0, STR, ap);
127*9a7741deSElliott Hughes 		av++;
128*9a7741deSElliott Hughes 	}
129*9a7741deSElliott Hughes 	ARGVcell = cp;
130*9a7741deSElliott Hughes }
131*9a7741deSElliott Hughes 
envinit(char ** envp)132*9a7741deSElliott Hughes void envinit(char **envp)	/* set up ENVIRON variable */
133*9a7741deSElliott Hughes {
134*9a7741deSElliott Hughes 	Array *ap;
135*9a7741deSElliott Hughes 	Cell *cp;
136*9a7741deSElliott Hughes 	char *p;
137*9a7741deSElliott Hughes 
138*9a7741deSElliott Hughes 	cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab);
139*9a7741deSElliott Hughes 	ap = makesymtab(NSYMTAB);
140*9a7741deSElliott Hughes 	free(cp->sval);
141*9a7741deSElliott Hughes 	cp->sval = (char *) ap;
142*9a7741deSElliott Hughes 	for ( ; *envp; envp++) {
143*9a7741deSElliott Hughes 		double result;
144*9a7741deSElliott Hughes 
145*9a7741deSElliott Hughes 		if ((p = strchr(*envp, '=')) == NULL)
146*9a7741deSElliott Hughes 			continue;
147*9a7741deSElliott Hughes 		if( p == *envp ) /* no left hand side name in env string */
148*9a7741deSElliott Hughes 			continue;
149*9a7741deSElliott Hughes 		*p++ = 0;	/* split into two strings at = */
150*9a7741deSElliott Hughes 		if (is_number(p, & result))
151*9a7741deSElliott Hughes 			setsymtab(*envp, p, result, STR|NUM, ap);
152*9a7741deSElliott Hughes 		else
153*9a7741deSElliott Hughes 			setsymtab(*envp, p, 0.0, STR, ap);
154*9a7741deSElliott Hughes 		p[-1] = '=';	/* restore in case env is passed down to a shell */
155*9a7741deSElliott Hughes 	}
156*9a7741deSElliott Hughes }
157*9a7741deSElliott Hughes 
makesymtab(int n)158*9a7741deSElliott Hughes Array *makesymtab(int n)	/* make a new symbol table */
159*9a7741deSElliott Hughes {
160*9a7741deSElliott Hughes 	Array *ap;
161*9a7741deSElliott Hughes 	Cell **tp;
162*9a7741deSElliott Hughes 
163*9a7741deSElliott Hughes 	ap = (Array *) malloc(sizeof(*ap));
164*9a7741deSElliott Hughes 	tp = (Cell **) calloc(n, sizeof(*tp));
165*9a7741deSElliott Hughes 	if (ap == NULL || tp == NULL)
166*9a7741deSElliott Hughes 		FATAL("out of space in makesymtab");
167*9a7741deSElliott Hughes 	ap->nelem = 0;
168*9a7741deSElliott Hughes 	ap->size = n;
169*9a7741deSElliott Hughes 	ap->tab = tp;
170*9a7741deSElliott Hughes 	return(ap);
171*9a7741deSElliott Hughes }
172*9a7741deSElliott Hughes 
freesymtab(Cell * ap)173*9a7741deSElliott Hughes void freesymtab(Cell *ap)	/* free a symbol table */
174*9a7741deSElliott Hughes {
175*9a7741deSElliott Hughes 	Cell *cp, *temp;
176*9a7741deSElliott Hughes 	Array *tp;
177*9a7741deSElliott Hughes 	int i;
178*9a7741deSElliott Hughes 
179*9a7741deSElliott Hughes 	if (!isarr(ap))
180*9a7741deSElliott Hughes 		return;
181*9a7741deSElliott Hughes 	tp = (Array *) ap->sval;
182*9a7741deSElliott Hughes 	if (tp == NULL)
183*9a7741deSElliott Hughes 		return;
184*9a7741deSElliott Hughes 	for (i = 0; i < tp->size; i++) {
185*9a7741deSElliott Hughes 		for (cp = tp->tab[i]; cp != NULL; cp = temp) {
186*9a7741deSElliott Hughes 			xfree(cp->nval);
187*9a7741deSElliott Hughes 			if (freeable(cp))
188*9a7741deSElliott Hughes 				xfree(cp->sval);
189*9a7741deSElliott Hughes 			temp = cp->cnext;	/* avoids freeing then using */
190*9a7741deSElliott Hughes 			free(cp);
191*9a7741deSElliott Hughes 			tp->nelem--;
192*9a7741deSElliott Hughes 		}
193*9a7741deSElliott Hughes 		tp->tab[i] = NULL;
194*9a7741deSElliott Hughes 	}
195*9a7741deSElliott Hughes 	if (tp->nelem != 0)
196*9a7741deSElliott Hughes 		WARNING("can't happen: inconsistent element count freeing %s", ap->nval);
197*9a7741deSElliott Hughes 	free(tp->tab);
198*9a7741deSElliott Hughes 	free(tp);
199*9a7741deSElliott Hughes }
200*9a7741deSElliott Hughes 
freeelem(Cell * ap,const char * s)201*9a7741deSElliott Hughes void freeelem(Cell *ap, const char *s)	/* free elem s from ap (i.e., ap["s"] */
202*9a7741deSElliott Hughes {
203*9a7741deSElliott Hughes 	Array *tp;
204*9a7741deSElliott Hughes 	Cell *p, *prev = NULL;
205*9a7741deSElliott Hughes 	int h;
206*9a7741deSElliott Hughes 
207*9a7741deSElliott Hughes 	tp = (Array *) ap->sval;
208*9a7741deSElliott Hughes 	h = hash(s, tp->size);
209*9a7741deSElliott Hughes 	for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext)
210*9a7741deSElliott Hughes 		if (strcmp(s, p->nval) == 0) {
211*9a7741deSElliott Hughes 			if (prev == NULL)	/* 1st one */
212*9a7741deSElliott Hughes 				tp->tab[h] = p->cnext;
213*9a7741deSElliott Hughes 			else			/* middle somewhere */
214*9a7741deSElliott Hughes 				prev->cnext = p->cnext;
215*9a7741deSElliott Hughes 			if (freeable(p))
216*9a7741deSElliott Hughes 				xfree(p->sval);
217*9a7741deSElliott Hughes 			free(p->nval);
218*9a7741deSElliott Hughes 			free(p);
219*9a7741deSElliott Hughes 			tp->nelem--;
220*9a7741deSElliott Hughes 			return;
221*9a7741deSElliott Hughes 		}
222*9a7741deSElliott Hughes }
223*9a7741deSElliott Hughes 
setsymtab(const char * n,const char * s,Awkfloat f,unsigned t,Array * tp)224*9a7741deSElliott Hughes Cell *setsymtab(const char *n, const char *s, Awkfloat f, unsigned t, Array *tp)
225*9a7741deSElliott Hughes {
226*9a7741deSElliott Hughes 	int h;
227*9a7741deSElliott Hughes 	Cell *p;
228*9a7741deSElliott Hughes 
229*9a7741deSElliott Hughes 	if (n != NULL && (p = lookup(n, tp)) != NULL) {
230*9a7741deSElliott Hughes 		DPRINTF("setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n",
231*9a7741deSElliott Hughes 			(void*)p, NN(p->nval), NN(p->sval), p->fval, p->tval);
232*9a7741deSElliott Hughes 		return(p);
233*9a7741deSElliott Hughes 	}
234*9a7741deSElliott Hughes 	p = (Cell *) malloc(sizeof(*p));
235*9a7741deSElliott Hughes 	if (p == NULL)
236*9a7741deSElliott Hughes 		FATAL("out of space for symbol table at %s", n);
237*9a7741deSElliott Hughes 	p->nval = tostring(n);
238*9a7741deSElliott Hughes 	p->sval = s ? tostring(s) : tostring("");
239*9a7741deSElliott Hughes 	p->fval = f;
240*9a7741deSElliott Hughes 	p->tval = t;
241*9a7741deSElliott Hughes 	p->csub = CUNK;
242*9a7741deSElliott Hughes 	p->ctype = OCELL;
243*9a7741deSElliott Hughes 	tp->nelem++;
244*9a7741deSElliott Hughes 	if (tp->nelem > FULLTAB * tp->size)
245*9a7741deSElliott Hughes 		rehash(tp);
246*9a7741deSElliott Hughes 	h = hash(n, tp->size);
247*9a7741deSElliott Hughes 	p->cnext = tp->tab[h];
248*9a7741deSElliott Hughes 	tp->tab[h] = p;
249*9a7741deSElliott Hughes 	DPRINTF("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n",
250*9a7741deSElliott Hughes 		(void*)p, p->nval, p->sval, p->fval, p->tval);
251*9a7741deSElliott Hughes 	return(p);
252*9a7741deSElliott Hughes }
253*9a7741deSElliott Hughes 
hash(const char * s,int n)254*9a7741deSElliott Hughes int hash(const char *s, int n)	/* form hash value for string s */
255*9a7741deSElliott Hughes {
256*9a7741deSElliott Hughes 	unsigned hashval;
257*9a7741deSElliott Hughes 
258*9a7741deSElliott Hughes 	for (hashval = 0; *s != '\0'; s++)
259*9a7741deSElliott Hughes 		hashval = (*s + 31 * hashval);
260*9a7741deSElliott Hughes 	return hashval % n;
261*9a7741deSElliott Hughes }
262*9a7741deSElliott Hughes 
rehash(Array * tp)263*9a7741deSElliott Hughes void rehash(Array *tp)	/* rehash items in small table into big one */
264*9a7741deSElliott Hughes {
265*9a7741deSElliott Hughes 	int i, nh, nsz;
266*9a7741deSElliott Hughes 	Cell *cp, *op, **np;
267*9a7741deSElliott Hughes 
268*9a7741deSElliott Hughes 	nsz = GROWTAB * tp->size;
269*9a7741deSElliott Hughes 	np = (Cell **) calloc(nsz, sizeof(*np));
270*9a7741deSElliott Hughes 	if (np == NULL)		/* can't do it, but can keep running. */
271*9a7741deSElliott Hughes 		return;		/* someone else will run out later. */
272*9a7741deSElliott Hughes 	for (i = 0; i < tp->size; i++) {
273*9a7741deSElliott Hughes 		for (cp = tp->tab[i]; cp; cp = op) {
274*9a7741deSElliott Hughes 			op = cp->cnext;
275*9a7741deSElliott Hughes 			nh = hash(cp->nval, nsz);
276*9a7741deSElliott Hughes 			cp->cnext = np[nh];
277*9a7741deSElliott Hughes 			np[nh] = cp;
278*9a7741deSElliott Hughes 		}
279*9a7741deSElliott Hughes 	}
280*9a7741deSElliott Hughes 	free(tp->tab);
281*9a7741deSElliott Hughes 	tp->tab = np;
282*9a7741deSElliott Hughes 	tp->size = nsz;
283*9a7741deSElliott Hughes }
284*9a7741deSElliott Hughes 
lookup(const char * s,Array * tp)285*9a7741deSElliott Hughes Cell *lookup(const char *s, Array *tp)	/* look for s in tp */
286*9a7741deSElliott Hughes {
287*9a7741deSElliott Hughes 	Cell *p;
288*9a7741deSElliott Hughes 	int h;
289*9a7741deSElliott Hughes 
290*9a7741deSElliott Hughes 	h = hash(s, tp->size);
291*9a7741deSElliott Hughes 	for (p = tp->tab[h]; p != NULL; p = p->cnext)
292*9a7741deSElliott Hughes 		if (strcmp(s, p->nval) == 0)
293*9a7741deSElliott Hughes 			return(p);	/* found it */
294*9a7741deSElliott Hughes 	return(NULL);			/* not found */
295*9a7741deSElliott Hughes }
296*9a7741deSElliott Hughes 
setfval(Cell * vp,Awkfloat f)297*9a7741deSElliott Hughes Awkfloat setfval(Cell *vp, Awkfloat f)	/* set float val of a Cell */
298*9a7741deSElliott Hughes {
299*9a7741deSElliott Hughes 	int fldno;
300*9a7741deSElliott Hughes 
301*9a7741deSElliott Hughes 	f += 0.0;		/* normalise negative zero to positive zero */
302*9a7741deSElliott Hughes 	if ((vp->tval & (NUM | STR)) == 0)
303*9a7741deSElliott Hughes 		funnyvar(vp, "assign to");
304*9a7741deSElliott Hughes 	if (isfld(vp)) {
305*9a7741deSElliott Hughes 		donerec = false;	/* mark $0 invalid */
306*9a7741deSElliott Hughes 		fldno = atoi(vp->nval);
307*9a7741deSElliott Hughes 		if (fldno > *NF)
308*9a7741deSElliott Hughes 			newfld(fldno);
309*9a7741deSElliott Hughes 		DPRINTF("setting field %d to %g\n", fldno, f);
310*9a7741deSElliott Hughes 	} else if (&vp->fval == NF) {
311*9a7741deSElliott Hughes 		donerec = false;	/* mark $0 invalid */
312*9a7741deSElliott Hughes 		setlastfld(f);
313*9a7741deSElliott Hughes 		DPRINTF("setfval: setting NF to %g\n", f);
314*9a7741deSElliott Hughes 	} else if (isrec(vp)) {
315*9a7741deSElliott Hughes 		donefld = false;	/* mark $1... invalid */
316*9a7741deSElliott Hughes 		donerec = true;
317*9a7741deSElliott Hughes 		savefs();
318*9a7741deSElliott Hughes 	} else if (vp == ofsloc) {
319*9a7741deSElliott Hughes 		if (!donerec)
320*9a7741deSElliott Hughes 			recbld();
321*9a7741deSElliott Hughes 	}
322*9a7741deSElliott Hughes 	if (freeable(vp))
323*9a7741deSElliott Hughes 		xfree(vp->sval); /* free any previous string */
324*9a7741deSElliott Hughes 	vp->tval &= ~(STR|CONVC|CONVO); /* mark string invalid */
325*9a7741deSElliott Hughes 	vp->fmt = NULL;
326*9a7741deSElliott Hughes 	vp->tval |= NUM;	/* mark number ok */
327*9a7741deSElliott Hughes 	if (f == -0)  /* who would have thought this possible? */
328*9a7741deSElliott Hughes 		f = 0;
329*9a7741deSElliott Hughes 	DPRINTF("setfval %p: %s = %g, t=%o\n", (void*)vp, NN(vp->nval), f, vp->tval);
330*9a7741deSElliott Hughes 	return vp->fval = f;
331*9a7741deSElliott Hughes }
332*9a7741deSElliott Hughes 
funnyvar(Cell * vp,const char * rw)333*9a7741deSElliott Hughes void funnyvar(Cell *vp, const char *rw)
334*9a7741deSElliott Hughes {
335*9a7741deSElliott Hughes 	if (isarr(vp))
336*9a7741deSElliott Hughes 		FATAL("can't %s %s; it's an array name.", rw, vp->nval);
337*9a7741deSElliott Hughes 	if (vp->tval & FCN)
338*9a7741deSElliott Hughes 		FATAL("can't %s %s; it's a function.", rw, vp->nval);
339*9a7741deSElliott Hughes 	WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o",
340*9a7741deSElliott Hughes 		(void *)vp, vp->nval, vp->sval, vp->fval, vp->tval);
341*9a7741deSElliott Hughes }
342*9a7741deSElliott Hughes 
setsval(Cell * vp,const char * s)343*9a7741deSElliott Hughes char *setsval(Cell *vp, const char *s)	/* set string val of a Cell */
344*9a7741deSElliott Hughes {
345*9a7741deSElliott Hughes 	char *t;
346*9a7741deSElliott Hughes 	int fldno;
347*9a7741deSElliott Hughes 	Awkfloat f;
348*9a7741deSElliott Hughes 
349*9a7741deSElliott Hughes 	DPRINTF("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n",
350*9a7741deSElliott Hughes 		(void*)vp, NN(vp->nval), s, vp->tval, donerec, donefld);
351*9a7741deSElliott Hughes 	if ((vp->tval & (NUM | STR)) == 0)
352*9a7741deSElliott Hughes 		funnyvar(vp, "assign to");
353*9a7741deSElliott Hughes 	if (CSV && (vp == rsloc))
354*9a7741deSElliott Hughes 		WARNING("danger: don't set RS when --csv is in effect");
355*9a7741deSElliott Hughes 	if (CSV && (vp == fsloc))
356*9a7741deSElliott Hughes 		WARNING("danger: don't set FS when --csv is in effect");
357*9a7741deSElliott Hughes 	if (isfld(vp)) {
358*9a7741deSElliott Hughes 		donerec = false;	/* mark $0 invalid */
359*9a7741deSElliott Hughes 		fldno = atoi(vp->nval);
360*9a7741deSElliott Hughes 		if (fldno > *NF)
361*9a7741deSElliott Hughes 			newfld(fldno);
362*9a7741deSElliott Hughes 		DPRINTF("setting field %d to %s (%p)\n", fldno, s, (const void*)s);
363*9a7741deSElliott Hughes 	} else if (isrec(vp)) {
364*9a7741deSElliott Hughes 		donefld = false;	/* mark $1... invalid */
365*9a7741deSElliott Hughes 		donerec = true;
366*9a7741deSElliott Hughes 		savefs();
367*9a7741deSElliott Hughes 	} else if (vp == ofsloc) {
368*9a7741deSElliott Hughes 		if (!donerec)
369*9a7741deSElliott Hughes 			recbld();
370*9a7741deSElliott Hughes 	}
371*9a7741deSElliott Hughes 	t = s ? tostring(s) : tostring("");	/* in case it's self-assign */
372*9a7741deSElliott Hughes 	if (freeable(vp))
373*9a7741deSElliott Hughes 		xfree(vp->sval);
374*9a7741deSElliott Hughes 	vp->tval &= ~(NUM|DONTFREE|CONVC|CONVO);
375*9a7741deSElliott Hughes 	vp->tval |= STR;
376*9a7741deSElliott Hughes 	vp->fmt = NULL;
377*9a7741deSElliott Hughes 	DPRINTF("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n",
378*9a7741deSElliott Hughes 		(void*)vp, NN(vp->nval), t, (void*)t, vp->tval, donerec, donefld);
379*9a7741deSElliott Hughes 	vp->sval = t;
380*9a7741deSElliott Hughes 	if (&vp->fval == NF) {
381*9a7741deSElliott Hughes 		donerec = false;	/* mark $0 invalid */
382*9a7741deSElliott Hughes 		f = getfval(vp);
383*9a7741deSElliott Hughes 		setlastfld(f);
384*9a7741deSElliott Hughes 		DPRINTF("setsval: setting NF to %g\n", f);
385*9a7741deSElliott Hughes 	}
386*9a7741deSElliott Hughes 
387*9a7741deSElliott Hughes 	return(vp->sval);
388*9a7741deSElliott Hughes }
389*9a7741deSElliott Hughes 
getfval(Cell * vp)390*9a7741deSElliott Hughes Awkfloat getfval(Cell *vp)	/* get float val of a Cell */
391*9a7741deSElliott Hughes {
392*9a7741deSElliott Hughes 	if ((vp->tval & (NUM | STR)) == 0)
393*9a7741deSElliott Hughes 		funnyvar(vp, "read value of");
394*9a7741deSElliott Hughes 	if (isfld(vp) && !donefld)
395*9a7741deSElliott Hughes 		fldbld();
396*9a7741deSElliott Hughes 	else if (isrec(vp) && !donerec)
397*9a7741deSElliott Hughes 		recbld();
398*9a7741deSElliott Hughes 	if (!isnum(vp)) {	/* not a number */
399*9a7741deSElliott Hughes 		double fval;
400*9a7741deSElliott Hughes 		bool no_trailing;
401*9a7741deSElliott Hughes 
402*9a7741deSElliott Hughes 		if (is_valid_number(vp->sval, true, & no_trailing, & fval)) {
403*9a7741deSElliott Hughes 			vp->fval = fval;
404*9a7741deSElliott Hughes 			if (no_trailing && !(vp->tval&CON))
405*9a7741deSElliott Hughes 				vp->tval |= NUM;	/* make NUM only sparingly */
406*9a7741deSElliott Hughes 		} else
407*9a7741deSElliott Hughes 			vp->fval = 0.0;
408*9a7741deSElliott Hughes 	}
409*9a7741deSElliott Hughes 	DPRINTF("getfval %p: %s = %g, t=%o\n",
410*9a7741deSElliott Hughes 		(void*)vp, NN(vp->nval), vp->fval, vp->tval);
411*9a7741deSElliott Hughes 	return(vp->fval);
412*9a7741deSElliott Hughes }
413*9a7741deSElliott Hughes 
get_inf_nan(double d)414*9a7741deSElliott Hughes static const char *get_inf_nan(double d)
415*9a7741deSElliott Hughes {
416*9a7741deSElliott Hughes 	if (isinf(d)) {
417*9a7741deSElliott Hughes 		return (d < 0 ? "-inf" : "+inf");
418*9a7741deSElliott Hughes 	} else if (isnan(d)) {
419*9a7741deSElliott Hughes 		return (signbit(d) != 0 ? "-nan" : "+nan");
420*9a7741deSElliott Hughes 	} else
421*9a7741deSElliott Hughes 		return NULL;
422*9a7741deSElliott Hughes }
423*9a7741deSElliott Hughes 
get_str_val(Cell * vp,char ** fmt)424*9a7741deSElliott Hughes static char *get_str_val(Cell *vp, char **fmt)        /* get string val of a Cell */
425*9a7741deSElliott Hughes {
426*9a7741deSElliott Hughes 	char s[256];
427*9a7741deSElliott Hughes 	double dtemp;
428*9a7741deSElliott Hughes 	const char *p;
429*9a7741deSElliott Hughes 
430*9a7741deSElliott Hughes 	if ((vp->tval & (NUM | STR)) == 0)
431*9a7741deSElliott Hughes 		funnyvar(vp, "read value of");
432*9a7741deSElliott Hughes 	if (isfld(vp) && ! donefld)
433*9a7741deSElliott Hughes 		fldbld();
434*9a7741deSElliott Hughes 	else if (isrec(vp) && ! donerec)
435*9a7741deSElliott Hughes 		recbld();
436*9a7741deSElliott Hughes 
437*9a7741deSElliott Hughes 	/*
438*9a7741deSElliott Hughes 	 * ADR: This is complicated and more fragile than is desirable.
439*9a7741deSElliott Hughes 	 * Retrieving a string value for a number associates the string
440*9a7741deSElliott Hughes 	 * value with the scalar.  Previously, the string value was
441*9a7741deSElliott Hughes 	 * sticky, meaning if converted via OFMT that became the value
442*9a7741deSElliott Hughes 	 * (even though POSIX wants it to be via CONVFMT). Or if CONVFMT
443*9a7741deSElliott Hughes 	 * changed after a string value was retrieved, the original value
444*9a7741deSElliott Hughes 	 * was maintained and used.  Also not per POSIX.
445*9a7741deSElliott Hughes 	 *
446*9a7741deSElliott Hughes 	 * We work around this design by adding two additional flags,
447*9a7741deSElliott Hughes 	 * CONVC and CONVO, indicating how the string value was
448*9a7741deSElliott Hughes 	 * obtained (via CONVFMT or OFMT) and _also_ maintaining a copy
449*9a7741deSElliott Hughes 	 * of the pointer to the xFMT format string used for the
450*9a7741deSElliott Hughes 	 * conversion.  This pointer is only read, **never** dereferenced.
451*9a7741deSElliott Hughes 	 * The next time we do a conversion, if it's coming from the same
452*9a7741deSElliott Hughes 	 * xFMT as last time, and the pointer value is different, we
453*9a7741deSElliott Hughes 	 * know that the xFMT format string changed, and we need to
454*9a7741deSElliott Hughes 	 * redo the conversion. If it's the same, we don't have to.
455*9a7741deSElliott Hughes 	 *
456*9a7741deSElliott Hughes 	 * There are also several cases where we don't do a conversion,
457*9a7741deSElliott Hughes 	 * such as for a field (see the checks below).
458*9a7741deSElliott Hughes 	 */
459*9a7741deSElliott Hughes 
460*9a7741deSElliott Hughes 	/* Don't duplicate the code for actually updating the value */
461*9a7741deSElliott Hughes #define update_str_val(vp) \
462*9a7741deSElliott Hughes 	{ \
463*9a7741deSElliott Hughes 		if (freeable(vp)) \
464*9a7741deSElliott Hughes 			xfree(vp->sval); \
465*9a7741deSElliott Hughes 		if ((p = get_inf_nan(vp->fval)) != NULL) \
466*9a7741deSElliott Hughes 			strcpy(s, p); \
467*9a7741deSElliott Hughes 		else if (modf(vp->fval, &dtemp) == 0)	/* it's integral */ \
468*9a7741deSElliott Hughes 			snprintf(s, sizeof (s), "%.30g", vp->fval); \
469*9a7741deSElliott Hughes 		else \
470*9a7741deSElliott Hughes 			snprintf(s, sizeof (s), *fmt, vp->fval); \
471*9a7741deSElliott Hughes 		vp->sval = tostring(s); \
472*9a7741deSElliott Hughes 		vp->tval &= ~DONTFREE; \
473*9a7741deSElliott Hughes 		vp->tval |= STR; \
474*9a7741deSElliott Hughes 	}
475*9a7741deSElliott Hughes 
476*9a7741deSElliott Hughes 	if (isstr(vp) == 0) {
477*9a7741deSElliott Hughes 		update_str_val(vp);
478*9a7741deSElliott Hughes 		if (fmt == OFMT) {
479*9a7741deSElliott Hughes 			vp->tval &= ~CONVC;
480*9a7741deSElliott Hughes 			vp->tval |= CONVO;
481*9a7741deSElliott Hughes 		} else {
482*9a7741deSElliott Hughes 			/* CONVFMT */
483*9a7741deSElliott Hughes 			vp->tval &= ~CONVO;
484*9a7741deSElliott Hughes 			vp->tval |= CONVC;
485*9a7741deSElliott Hughes 		}
486*9a7741deSElliott Hughes 		vp->fmt = *fmt;
487*9a7741deSElliott Hughes 	} else if ((vp->tval & DONTFREE) != 0 || ! isnum(vp) || isfld(vp)) {
488*9a7741deSElliott Hughes 		goto done;
489*9a7741deSElliott Hughes 	} else if (isstr(vp)) {
490*9a7741deSElliott Hughes 		if (fmt == OFMT) {
491*9a7741deSElliott Hughes 			if ((vp->tval & CONVC) != 0
492*9a7741deSElliott Hughes 			    || ((vp->tval & CONVO) != 0 && vp->fmt != *fmt)) {
493*9a7741deSElliott Hughes 				update_str_val(vp);
494*9a7741deSElliott Hughes 				vp->tval &= ~CONVC;
495*9a7741deSElliott Hughes 				vp->tval |= CONVO;
496*9a7741deSElliott Hughes 				vp->fmt = *fmt;
497*9a7741deSElliott Hughes 			}
498*9a7741deSElliott Hughes 		} else {
499*9a7741deSElliott Hughes 			/* CONVFMT */
500*9a7741deSElliott Hughes 			if ((vp->tval & CONVO) != 0
501*9a7741deSElliott Hughes 			    || ((vp->tval & CONVC) != 0 && vp->fmt != *fmt)) {
502*9a7741deSElliott Hughes 				update_str_val(vp);
503*9a7741deSElliott Hughes 				vp->tval &= ~CONVO;
504*9a7741deSElliott Hughes 				vp->tval |= CONVC;
505*9a7741deSElliott Hughes 				vp->fmt = *fmt;
506*9a7741deSElliott Hughes 			}
507*9a7741deSElliott Hughes 		}
508*9a7741deSElliott Hughes 	}
509*9a7741deSElliott Hughes done:
510*9a7741deSElliott Hughes 	DPRINTF("getsval %p: %s = \"%s (%p)\", t=%o\n",
511*9a7741deSElliott Hughes 		(void*)vp, NN(vp->nval), vp->sval, (void*)vp->sval, vp->tval);
512*9a7741deSElliott Hughes 	return(vp->sval);
513*9a7741deSElliott Hughes }
514*9a7741deSElliott Hughes 
getsval(Cell * vp)515*9a7741deSElliott Hughes char *getsval(Cell *vp)       /* get string val of a Cell */
516*9a7741deSElliott Hughes {
517*9a7741deSElliott Hughes       return get_str_val(vp, CONVFMT);
518*9a7741deSElliott Hughes }
519*9a7741deSElliott Hughes 
getpssval(Cell * vp)520*9a7741deSElliott Hughes char *getpssval(Cell *vp)     /* get string val of a Cell for print */
521*9a7741deSElliott Hughes {
522*9a7741deSElliott Hughes       return get_str_val(vp, OFMT);
523*9a7741deSElliott Hughes }
524*9a7741deSElliott Hughes 
525*9a7741deSElliott Hughes 
tostring(const char * s)526*9a7741deSElliott Hughes char *tostring(const char *s)	/* make a copy of string s */
527*9a7741deSElliott Hughes {
528*9a7741deSElliott Hughes 	char *p = strdup(s);
529*9a7741deSElliott Hughes 	if (p == NULL)
530*9a7741deSElliott Hughes 		FATAL("out of space in tostring on %s", s);
531*9a7741deSElliott Hughes 	return(p);
532*9a7741deSElliott Hughes }
533*9a7741deSElliott Hughes 
tostringN(const char * s,size_t n)534*9a7741deSElliott Hughes char *tostringN(const char *s, size_t n)	/* make a copy of string s */
535*9a7741deSElliott Hughes {
536*9a7741deSElliott Hughes 	char *p;
537*9a7741deSElliott Hughes 
538*9a7741deSElliott Hughes 	p = (char *) malloc(n);
539*9a7741deSElliott Hughes 	if (p == NULL)
540*9a7741deSElliott Hughes 		FATAL("out of space in tostring on %s", s);
541*9a7741deSElliott Hughes 	strcpy(p, s);
542*9a7741deSElliott Hughes 	return(p);
543*9a7741deSElliott Hughes }
544*9a7741deSElliott Hughes 
catstr(Cell * a,Cell * b)545*9a7741deSElliott Hughes Cell *catstr(Cell *a, Cell *b) /* concatenate a and b */
546*9a7741deSElliott Hughes {
547*9a7741deSElliott Hughes 	Cell *c;
548*9a7741deSElliott Hughes 	char *p;
549*9a7741deSElliott Hughes 	char *sa = getsval(a);
550*9a7741deSElliott Hughes 	char *sb = getsval(b);
551*9a7741deSElliott Hughes 	size_t l = strlen(sa) + strlen(sb) + 1;
552*9a7741deSElliott Hughes 	p = (char *) malloc(l);
553*9a7741deSElliott Hughes 	if (p == NULL)
554*9a7741deSElliott Hughes 		FATAL("out of space concatenating %s and %s", sa, sb);
555*9a7741deSElliott Hughes 	snprintf(p, l, "%s%s", sa, sb);
556*9a7741deSElliott Hughes 
557*9a7741deSElliott Hughes 	l++;	// add room for ' '
558*9a7741deSElliott Hughes 	char *newbuf = (char *) malloc(l);
559*9a7741deSElliott Hughes 	if (newbuf == NULL)
560*9a7741deSElliott Hughes 		FATAL("out of space concatenating %s and %s", sa, sb);
561*9a7741deSElliott Hughes 	// See string() in lex.c; a string "xx" is stored in the symbol
562*9a7741deSElliott Hughes 	// table as "xx ".
563*9a7741deSElliott Hughes 	snprintf(newbuf, l, "%s ", p);
564*9a7741deSElliott Hughes 	c = setsymtab(newbuf, p, 0.0, CON|STR|DONTFREE, symtab);
565*9a7741deSElliott Hughes 	free(p);
566*9a7741deSElliott Hughes 	free(newbuf);
567*9a7741deSElliott Hughes 	return c;
568*9a7741deSElliott Hughes }
569*9a7741deSElliott Hughes 
qstring(const char * is,int delim)570*9a7741deSElliott Hughes char *qstring(const char *is, int delim)	/* collect string up to next delim */
571*9a7741deSElliott Hughes {
572*9a7741deSElliott Hughes 	int c, n;
573*9a7741deSElliott Hughes 	const uschar *s = (const uschar *) is;
574*9a7741deSElliott Hughes 	uschar *buf, *bp;
575*9a7741deSElliott Hughes 
576*9a7741deSElliott Hughes 	if ((buf = (uschar *) malloc(strlen(is)+3)) == NULL)
577*9a7741deSElliott Hughes 		FATAL( "out of space in qstring(%s)", s);
578*9a7741deSElliott Hughes 	for (bp = buf; (c = *s) != delim; s++) {
579*9a7741deSElliott Hughes 		if (c == '\n')
580*9a7741deSElliott Hughes 			SYNTAX( "newline in string %.20s...", is );
581*9a7741deSElliott Hughes 		else if (c != '\\')
582*9a7741deSElliott Hughes 			*bp++ = c;
583*9a7741deSElliott Hughes 		else {	/* \something */
584*9a7741deSElliott Hughes 			c = *++s;
585*9a7741deSElliott Hughes 			if (c == 0) {	/* \ at end */
586*9a7741deSElliott Hughes 				*bp++ = '\\';
587*9a7741deSElliott Hughes 				break;	/* for loop */
588*9a7741deSElliott Hughes 			}
589*9a7741deSElliott Hughes 			switch (c) {
590*9a7741deSElliott Hughes 			case '\\':	*bp++ = '\\'; break;
591*9a7741deSElliott Hughes 			case 'n':	*bp++ = '\n'; break;
592*9a7741deSElliott Hughes 			case 't':	*bp++ = '\t'; break;
593*9a7741deSElliott Hughes 			case 'b':	*bp++ = '\b'; break;
594*9a7741deSElliott Hughes 			case 'f':	*bp++ = '\f'; break;
595*9a7741deSElliott Hughes 			case 'r':	*bp++ = '\r'; break;
596*9a7741deSElliott Hughes 			case 'v':	*bp++ = '\v'; break;
597*9a7741deSElliott Hughes 			case 'a':	*bp++ = '\a'; break;
598*9a7741deSElliott Hughes 			default:
599*9a7741deSElliott Hughes 				if (!isdigit(c)) {
600*9a7741deSElliott Hughes 					*bp++ = c;
601*9a7741deSElliott Hughes 					break;
602*9a7741deSElliott Hughes 				}
603*9a7741deSElliott Hughes 				n = c - '0';
604*9a7741deSElliott Hughes 				if (isdigit(s[1])) {
605*9a7741deSElliott Hughes 					n = 8 * n + *++s - '0';
606*9a7741deSElliott Hughes 					if (isdigit(s[1]))
607*9a7741deSElliott Hughes 						n = 8 * n + *++s - '0';
608*9a7741deSElliott Hughes 				}
609*9a7741deSElliott Hughes 				*bp++ = n;
610*9a7741deSElliott Hughes 				break;
611*9a7741deSElliott Hughes 			}
612*9a7741deSElliott Hughes 		}
613*9a7741deSElliott Hughes 	}
614*9a7741deSElliott Hughes 	*bp++ = 0;
615*9a7741deSElliott Hughes 	return (char *) buf;
616*9a7741deSElliott Hughes }
617*9a7741deSElliott Hughes 
flags2str(int flags)618*9a7741deSElliott Hughes const char *flags2str(int flags)
619*9a7741deSElliott Hughes {
620*9a7741deSElliott Hughes 	static const struct ftab {
621*9a7741deSElliott Hughes 		const char *name;
622*9a7741deSElliott Hughes 		int value;
623*9a7741deSElliott Hughes 	} flagtab[] = {
624*9a7741deSElliott Hughes 		{ "NUM", NUM },
625*9a7741deSElliott Hughes 		{ "STR", STR },
626*9a7741deSElliott Hughes 		{ "DONTFREE", DONTFREE },
627*9a7741deSElliott Hughes 		{ "CON", CON },
628*9a7741deSElliott Hughes 		{ "ARR", ARR },
629*9a7741deSElliott Hughes 		{ "FCN", FCN },
630*9a7741deSElliott Hughes 		{ "FLD", FLD },
631*9a7741deSElliott Hughes 		{ "REC", REC },
632*9a7741deSElliott Hughes 		{ "CONVC", CONVC },
633*9a7741deSElliott Hughes 		{ "CONVO", CONVO },
634*9a7741deSElliott Hughes 		{ NULL, 0 }
635*9a7741deSElliott Hughes 	};
636*9a7741deSElliott Hughes 	static char buf[100];
637*9a7741deSElliott Hughes 	int i;
638*9a7741deSElliott Hughes 	char *cp = buf;
639*9a7741deSElliott Hughes 
640*9a7741deSElliott Hughes 	for (i = 0; flagtab[i].name != NULL; i++) {
641*9a7741deSElliott Hughes 		if ((flags & flagtab[i].value) != 0) {
642*9a7741deSElliott Hughes 			if (cp > buf)
643*9a7741deSElliott Hughes 				*cp++ = '|';
644*9a7741deSElliott Hughes 			strcpy(cp, flagtab[i].name);
645*9a7741deSElliott Hughes 			cp += strlen(cp);
646*9a7741deSElliott Hughes 		}
647*9a7741deSElliott Hughes 	}
648*9a7741deSElliott Hughes 
649*9a7741deSElliott Hughes 	return buf;
650*9a7741deSElliott Hughes }
651