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