12a55deb1SDavid E. O'Brien /****************************************************************
22a55deb1SDavid E. O'Brien Copyright (C) Lucent Technologies 1997
32a55deb1SDavid E. O'Brien All Rights Reserved
42a55deb1SDavid E. O'Brien
52a55deb1SDavid E. O'Brien Permission to use, copy, modify, and distribute this software and
62a55deb1SDavid E. O'Brien its documentation for any purpose and without fee is hereby
72a55deb1SDavid E. O'Brien granted, provided that the above copyright notice appear in all
82a55deb1SDavid E. O'Brien copies and that both that the copyright notice and this
92a55deb1SDavid E. O'Brien permission notice and warranty disclaimer appear in supporting
102a55deb1SDavid E. O'Brien documentation, and that the name Lucent Technologies or any of
112a55deb1SDavid E. O'Brien its entities not be used in advertising or publicity pertaining
122a55deb1SDavid E. O'Brien to distribution of the software without specific, written prior
132a55deb1SDavid E. O'Brien permission.
142a55deb1SDavid E. O'Brien
152a55deb1SDavid E. O'Brien LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
162a55deb1SDavid E. O'Brien INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
172a55deb1SDavid E. O'Brien IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
182a55deb1SDavid E. O'Brien SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
192a55deb1SDavid E. O'Brien WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
202a55deb1SDavid E. O'Brien IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
212a55deb1SDavid E. O'Brien ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
222a55deb1SDavid E. O'Brien THIS SOFTWARE.
232a55deb1SDavid E. O'Brien ****************************************************************/
242a55deb1SDavid E. O'Brien
252a55deb1SDavid E. O'Brien #define DEBUG
262a55deb1SDavid E. O'Brien #include <stdio.h>
272a55deb1SDavid E. O'Brien #include <math.h>
282a55deb1SDavid E. O'Brien #include <ctype.h>
292a55deb1SDavid E. O'Brien #include <string.h>
302a55deb1SDavid E. O'Brien #include <stdlib.h>
312a55deb1SDavid E. O'Brien #include "awk.h"
322a55deb1SDavid E. O'Brien
332a55deb1SDavid E. O'Brien #define FULLTAB 2 /* rehash when table gets this x full */
342a55deb1SDavid E. O'Brien #define GROWTAB 4 /* grow table by this factor */
352a55deb1SDavid E. O'Brien
362a55deb1SDavid E. O'Brien Array *symtab; /* main symbol table */
372a55deb1SDavid E. O'Brien
382a55deb1SDavid E. O'Brien char **FS; /* initial field sep */
392a55deb1SDavid E. O'Brien char **RS; /* initial record sep */
402a55deb1SDavid E. O'Brien char **OFS; /* output field sep */
412a55deb1SDavid E. O'Brien char **ORS; /* output record sep */
422a55deb1SDavid E. O'Brien char **OFMT; /* output format for numbers */
432a55deb1SDavid E. O'Brien char **CONVFMT; /* format for conversions in getsval */
442a55deb1SDavid E. O'Brien Awkfloat *NF; /* number of fields in current record */
452a55deb1SDavid E. O'Brien Awkfloat *NR; /* number of current record */
462a55deb1SDavid E. O'Brien Awkfloat *FNR; /* number of current record in current file */
472a55deb1SDavid E. O'Brien char **FILENAME; /* current filename argument */
482a55deb1SDavid E. O'Brien Awkfloat *ARGC; /* number of arguments from command line */
492a55deb1SDavid E. O'Brien char **SUBSEP; /* subscript separator for a[i,j,k]; default \034 */
502a55deb1SDavid E. O'Brien Awkfloat *RSTART; /* start of re matched with ~; origin 1 (!) */
512a55deb1SDavid E. O'Brien Awkfloat *RLENGTH; /* length of same */
522a55deb1SDavid E. O'Brien
53c263f9bfSRuslan Ermilov Cell *fsloc; /* FS */
542a55deb1SDavid E. O'Brien Cell *nrloc; /* NR */
552a55deb1SDavid E. O'Brien Cell *nfloc; /* NF */
562a55deb1SDavid E. O'Brien Cell *fnrloc; /* FNR */
57b5253557SWarner Losh Cell *ofsloc; /* OFS */
58b5253557SWarner Losh Cell *orsloc; /* ORS */
59b5253557SWarner Losh Cell *rsloc; /* RS */
602a55deb1SDavid E. O'Brien Array *ARGVtab; /* symbol table containing ARGV[...] */
612a55deb1SDavid E. O'Brien Array *ENVtab; /* symbol table containing ENVIRON[...] */
622a55deb1SDavid E. O'Brien Cell *rstartloc; /* RSTART */
632a55deb1SDavid E. O'Brien Cell *rlengthloc; /* RLENGTH */
64b5253557SWarner Losh Cell *subseploc; /* SUBSEP */
652a55deb1SDavid E. O'Brien Cell *symtabloc; /* SYMTAB */
662a55deb1SDavid E. O'Brien
672a55deb1SDavid E. O'Brien Cell *nullloc; /* a guaranteed empty cell */
682a55deb1SDavid E. O'Brien Node *nullnode; /* zero&null, converted into a node for comparisons */
692a55deb1SDavid E. O'Brien Cell *literal0;
702a55deb1SDavid E. O'Brien
712a55deb1SDavid E. O'Brien extern Cell **fldtab;
722a55deb1SDavid E. O'Brien
syminit(void)732a55deb1SDavid E. O'Brien void syminit(void) /* initialize symbol table with builtin vars */
742a55deb1SDavid E. O'Brien {
752a55deb1SDavid E. O'Brien literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab);
762a55deb1SDavid E. O'Brien /* this is used for if(x)... tests: */
772a55deb1SDavid E. O'Brien nullloc = setsymtab("$zero&null", "", 0.0, NUM|STR|CON|DONTFREE, symtab);
782a55deb1SDavid E. O'Brien nullnode = celltonode(nullloc, CCON);
792a55deb1SDavid E. O'Brien
80c263f9bfSRuslan Ermilov fsloc = setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab);
81c263f9bfSRuslan Ermilov FS = &fsloc->sval;
82b5253557SWarner Losh rsloc = setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab);
83b5253557SWarner Losh RS = &rsloc->sval;
84b5253557SWarner Losh ofsloc = setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab);
85b5253557SWarner Losh OFS = &ofsloc->sval;
86b5253557SWarner Losh orsloc = setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab);
87b5253557SWarner Losh ORS = &orsloc->sval;
882a55deb1SDavid E. O'Brien OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
892a55deb1SDavid E. O'Brien CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
902a55deb1SDavid E. O'Brien FILENAME = &setsymtab("FILENAME", "", 0.0, STR|DONTFREE, symtab)->sval;
912a55deb1SDavid E. O'Brien nfloc = setsymtab("NF", "", 0.0, NUM, symtab);
922a55deb1SDavid E. O'Brien NF = &nfloc->fval;
932a55deb1SDavid E. O'Brien nrloc = setsymtab("NR", "", 0.0, NUM, symtab);
942a55deb1SDavid E. O'Brien NR = &nrloc->fval;
952a55deb1SDavid E. O'Brien fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab);
962a55deb1SDavid E. O'Brien FNR = &fnrloc->fval;
97b5253557SWarner Losh subseploc = setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab);
98b5253557SWarner Losh SUBSEP = &subseploc->sval;
992a55deb1SDavid E. O'Brien rstartloc = setsymtab("RSTART", "", 0.0, NUM, symtab);
1002a55deb1SDavid E. O'Brien RSTART = &rstartloc->fval;
1012a55deb1SDavid E. O'Brien rlengthloc = setsymtab("RLENGTH", "", 0.0, NUM, symtab);
1022a55deb1SDavid E. O'Brien RLENGTH = &rlengthloc->fval;
1032a55deb1SDavid E. O'Brien symtabloc = setsymtab("SYMTAB", "", 0.0, ARR, symtab);
104f39dd6a9SWarner Losh free(symtabloc->sval);
1052a55deb1SDavid E. O'Brien symtabloc->sval = (char *) symtab;
1062a55deb1SDavid E. O'Brien }
1072a55deb1SDavid E. O'Brien
arginit(int ac,char ** av)1082a55deb1SDavid E. O'Brien void arginit(int ac, char **av) /* set up ARGV and ARGC */
1092a55deb1SDavid E. O'Brien {
1102a55deb1SDavid E. O'Brien Cell *cp;
1112a55deb1SDavid E. O'Brien int i;
1122a55deb1SDavid E. O'Brien char temp[50];
1132a55deb1SDavid E. O'Brien
1142a55deb1SDavid E. O'Brien ARGC = &setsymtab("ARGC", "", (Awkfloat) ac, NUM, symtab)->fval;
1152a55deb1SDavid E. O'Brien cp = setsymtab("ARGV", "", 0.0, ARR, symtab);
1162a55deb1SDavid E. O'Brien ARGVtab = makesymtab(NSYMTAB); /* could be (int) ARGC as well */
117f39dd6a9SWarner Losh free(cp->sval);
1182a55deb1SDavid E. O'Brien cp->sval = (char *) ARGVtab;
1192a55deb1SDavid E. O'Brien for (i = 0; i < ac; i++) {
120f39dd6a9SWarner Losh double result;
121f39dd6a9SWarner Losh
1222a55deb1SDavid E. O'Brien sprintf(temp, "%d", i);
123f39dd6a9SWarner Losh if (is_number(*av, & result))
124f39dd6a9SWarner Losh setsymtab(temp, *av, result, STR|NUM, ARGVtab);
1252a55deb1SDavid E. O'Brien else
1262a55deb1SDavid E. O'Brien setsymtab(temp, *av, 0.0, STR, ARGVtab);
1272a55deb1SDavid E. O'Brien av++;
1282a55deb1SDavid E. O'Brien }
1292a55deb1SDavid E. O'Brien }
1302a55deb1SDavid E. O'Brien
envinit(char ** envp)1312a55deb1SDavid E. O'Brien void envinit(char **envp) /* set up ENVIRON variable */
1322a55deb1SDavid E. O'Brien {
1332a55deb1SDavid E. O'Brien Cell *cp;
1342a55deb1SDavid E. O'Brien char *p;
1352a55deb1SDavid E. O'Brien
1362a55deb1SDavid E. O'Brien cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab);
1372a55deb1SDavid E. O'Brien ENVtab = makesymtab(NSYMTAB);
138f39dd6a9SWarner Losh free(cp->sval);
1392a55deb1SDavid E. O'Brien cp->sval = (char *) ENVtab;
1402a55deb1SDavid E. O'Brien for ( ; *envp; envp++) {
141f39dd6a9SWarner Losh double result;
142f39dd6a9SWarner Losh
1432a55deb1SDavid E. O'Brien if ((p = strchr(*envp, '=')) == NULL)
1442a55deb1SDavid E. O'Brien continue;
1452a55deb1SDavid E. O'Brien if( p == *envp ) /* no left hand side name in env string */
1462a55deb1SDavid E. O'Brien continue;
1472a55deb1SDavid E. O'Brien *p++ = 0; /* split into two strings at = */
148f39dd6a9SWarner Losh if (is_number(p, & result))
149f39dd6a9SWarner Losh setsymtab(*envp, p, result, STR|NUM, ENVtab);
1502a55deb1SDavid E. O'Brien else
1512a55deb1SDavid E. O'Brien setsymtab(*envp, p, 0.0, STR, ENVtab);
1522a55deb1SDavid E. O'Brien p[-1] = '='; /* restore in case env is passed down to a shell */
1532a55deb1SDavid E. O'Brien }
1542a55deb1SDavid E. O'Brien }
1552a55deb1SDavid E. O'Brien
makesymtab(int n)1562a55deb1SDavid E. O'Brien Array *makesymtab(int n) /* make a new symbol table */
1572a55deb1SDavid E. O'Brien {
1582a55deb1SDavid E. O'Brien Array *ap;
1592a55deb1SDavid E. O'Brien Cell **tp;
1602a55deb1SDavid E. O'Brien
161f39dd6a9SWarner Losh ap = (Array *) malloc(sizeof(*ap));
162f39dd6a9SWarner Losh tp = (Cell **) calloc(n, sizeof(*tp));
1632a55deb1SDavid E. O'Brien if (ap == NULL || tp == NULL)
1642a55deb1SDavid E. O'Brien FATAL("out of space in makesymtab");
1652a55deb1SDavid E. O'Brien ap->nelem = 0;
1662a55deb1SDavid E. O'Brien ap->size = n;
1672a55deb1SDavid E. O'Brien ap->tab = tp;
1682a55deb1SDavid E. O'Brien return(ap);
1692a55deb1SDavid E. O'Brien }
1702a55deb1SDavid E. O'Brien
freesymtab(Cell * ap)1712a55deb1SDavid E. O'Brien void freesymtab(Cell *ap) /* free a symbol table */
1722a55deb1SDavid E. O'Brien {
1732a55deb1SDavid E. O'Brien Cell *cp, *temp;
1742a55deb1SDavid E. O'Brien Array *tp;
1752a55deb1SDavid E. O'Brien int i;
1762a55deb1SDavid E. O'Brien
1772a55deb1SDavid E. O'Brien if (!isarr(ap))
1782a55deb1SDavid E. O'Brien return;
1792a55deb1SDavid E. O'Brien tp = (Array *) ap->sval;
1802a55deb1SDavid E. O'Brien if (tp == NULL)
1812a55deb1SDavid E. O'Brien return;
1822a55deb1SDavid E. O'Brien for (i = 0; i < tp->size; i++) {
1832a55deb1SDavid E. O'Brien for (cp = tp->tab[i]; cp != NULL; cp = temp) {
1842a55deb1SDavid E. O'Brien xfree(cp->nval);
1852a55deb1SDavid E. O'Brien if (freeable(cp))
1862a55deb1SDavid E. O'Brien xfree(cp->sval);
1872a55deb1SDavid E. O'Brien temp = cp->cnext; /* avoids freeing then using */
1882a55deb1SDavid E. O'Brien free(cp);
189007c6572SDag-Erling Smørgrav tp->nelem--;
1902a55deb1SDavid E. O'Brien }
19110ce5b99SWarner Losh tp->tab[i] = NULL;
1922a55deb1SDavid E. O'Brien }
193007c6572SDag-Erling Smørgrav if (tp->nelem != 0)
194007c6572SDag-Erling Smørgrav WARNING("can't happen: inconsistent element count freeing %s", ap->nval);
1952a55deb1SDavid E. O'Brien free(tp->tab);
1962a55deb1SDavid E. O'Brien free(tp);
1972a55deb1SDavid E. O'Brien }
1982a55deb1SDavid E. O'Brien
freeelem(Cell * ap,const char * s)199813da98dSDavid E. O'Brien void freeelem(Cell *ap, const char *s) /* free elem s from ap (i.e., ap["s"] */
2002a55deb1SDavid E. O'Brien {
2012a55deb1SDavid E. O'Brien Array *tp;
2022a55deb1SDavid E. O'Brien Cell *p, *prev = NULL;
2032a55deb1SDavid E. O'Brien int h;
2042a55deb1SDavid E. O'Brien
2052a55deb1SDavid E. O'Brien tp = (Array *) ap->sval;
2062a55deb1SDavid E. O'Brien h = hash(s, tp->size);
2072a55deb1SDavid E. O'Brien for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext)
2082a55deb1SDavid E. O'Brien if (strcmp(s, p->nval) == 0) {
2092a55deb1SDavid E. O'Brien if (prev == NULL) /* 1st one */
2102a55deb1SDavid E. O'Brien tp->tab[h] = p->cnext;
2112a55deb1SDavid E. O'Brien else /* middle somewhere */
2122a55deb1SDavid E. O'Brien prev->cnext = p->cnext;
2132a55deb1SDavid E. O'Brien if (freeable(p))
2142a55deb1SDavid E. O'Brien xfree(p->sval);
2152a55deb1SDavid E. O'Brien free(p->nval);
2162a55deb1SDavid E. O'Brien free(p);
2172a55deb1SDavid E. O'Brien tp->nelem--;
2182a55deb1SDavid E. O'Brien return;
2192a55deb1SDavid E. O'Brien }
2202a55deb1SDavid E. O'Brien }
2212a55deb1SDavid E. O'Brien
setsymtab(const char * n,const char * s,Awkfloat f,unsigned t,Array * tp)222813da98dSDavid E. O'Brien Cell *setsymtab(const char *n, const char *s, Awkfloat f, unsigned t, Array *tp)
2232a55deb1SDavid E. O'Brien {
2242a55deb1SDavid E. O'Brien int h;
2252a55deb1SDavid E. O'Brien Cell *p;
2262a55deb1SDavid E. O'Brien
2272a55deb1SDavid E. O'Brien if (n != NULL && (p = lookup(n, tp)) != NULL) {
228f39dd6a9SWarner Losh DPRINTF("setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n",
229f39dd6a9SWarner Losh (void*)p, NN(p->nval), NN(p->sval), p->fval, p->tval);
2302a55deb1SDavid E. O'Brien return(p);
2312a55deb1SDavid E. O'Brien }
232f39dd6a9SWarner Losh p = (Cell *) malloc(sizeof(*p));
2332a55deb1SDavid E. O'Brien if (p == NULL)
2342a55deb1SDavid E. O'Brien FATAL("out of space for symbol table at %s", n);
2352a55deb1SDavid E. O'Brien p->nval = tostring(n);
2362a55deb1SDavid E. O'Brien p->sval = s ? tostring(s) : tostring("");
2372a55deb1SDavid E. O'Brien p->fval = f;
2382a55deb1SDavid E. O'Brien p->tval = t;
2392a55deb1SDavid E. O'Brien p->csub = CUNK;
2402a55deb1SDavid E. O'Brien p->ctype = OCELL;
2412a55deb1SDavid E. O'Brien tp->nelem++;
2422a55deb1SDavid E. O'Brien if (tp->nelem > FULLTAB * tp->size)
2432a55deb1SDavid E. O'Brien rehash(tp);
2442a55deb1SDavid E. O'Brien h = hash(n, tp->size);
2452a55deb1SDavid E. O'Brien p->cnext = tp->tab[h];
2462a55deb1SDavid E. O'Brien tp->tab[h] = p;
247f39dd6a9SWarner Losh DPRINTF("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n",
248f39dd6a9SWarner Losh (void*)p, p->nval, p->sval, p->fval, p->tval);
2492a55deb1SDavid E. O'Brien return(p);
2502a55deb1SDavid E. O'Brien }
2512a55deb1SDavid E. O'Brien
hash(const char * s,int n)252813da98dSDavid E. O'Brien int hash(const char *s, int n) /* form hash value for string s */
2532a55deb1SDavid E. O'Brien {
2542a55deb1SDavid E. O'Brien unsigned hashval;
2552a55deb1SDavid E. O'Brien
2562a55deb1SDavid E. O'Brien for (hashval = 0; *s != '\0'; s++)
2572a55deb1SDavid E. O'Brien hashval = (*s + 31 * hashval);
2582a55deb1SDavid E. O'Brien return hashval % n;
2592a55deb1SDavid E. O'Brien }
2602a55deb1SDavid E. O'Brien
rehash(Array * tp)2612a55deb1SDavid E. O'Brien void rehash(Array *tp) /* rehash items in small table into big one */
2622a55deb1SDavid E. O'Brien {
2632a55deb1SDavid E. O'Brien int i, nh, nsz;
2642a55deb1SDavid E. O'Brien Cell *cp, *op, **np;
2652a55deb1SDavid E. O'Brien
2662a55deb1SDavid E. O'Brien nsz = GROWTAB * tp->size;
267f39dd6a9SWarner Losh np = (Cell **) calloc(nsz, sizeof(*np));
2682a55deb1SDavid E. O'Brien if (np == NULL) /* can't do it, but can keep running. */
2692a55deb1SDavid E. O'Brien return; /* someone else will run out later. */
2702a55deb1SDavid E. O'Brien for (i = 0; i < tp->size; i++) {
2712a55deb1SDavid E. O'Brien for (cp = tp->tab[i]; cp; cp = op) {
2722a55deb1SDavid E. O'Brien op = cp->cnext;
2732a55deb1SDavid E. O'Brien nh = hash(cp->nval, nsz);
2742a55deb1SDavid E. O'Brien cp->cnext = np[nh];
2752a55deb1SDavid E. O'Brien np[nh] = cp;
2762a55deb1SDavid E. O'Brien }
2772a55deb1SDavid E. O'Brien }
2782a55deb1SDavid E. O'Brien free(tp->tab);
2792a55deb1SDavid E. O'Brien tp->tab = np;
2802a55deb1SDavid E. O'Brien tp->size = nsz;
2812a55deb1SDavid E. O'Brien }
2822a55deb1SDavid E. O'Brien
lookup(const char * s,Array * tp)283813da98dSDavid E. O'Brien Cell *lookup(const char *s, Array *tp) /* look for s in tp */
2842a55deb1SDavid E. O'Brien {
2852a55deb1SDavid E. O'Brien Cell *p;
2862a55deb1SDavid E. O'Brien int h;
2872a55deb1SDavid E. O'Brien
2882a55deb1SDavid E. O'Brien h = hash(s, tp->size);
2892a55deb1SDavid E. O'Brien for (p = tp->tab[h]; p != NULL; p = p->cnext)
2902a55deb1SDavid E. O'Brien if (strcmp(s, p->nval) == 0)
2912a55deb1SDavid E. O'Brien return(p); /* found it */
2922a55deb1SDavid E. O'Brien return(NULL); /* not found */
2932a55deb1SDavid E. O'Brien }
2942a55deb1SDavid E. O'Brien
setfval(Cell * vp,Awkfloat f)2952a55deb1SDavid E. O'Brien Awkfloat setfval(Cell *vp, Awkfloat f) /* set float val of a Cell */
2962a55deb1SDavid E. O'Brien {
2972a55deb1SDavid E. O'Brien int fldno;
2982a55deb1SDavid E. O'Brien
299b5253557SWarner Losh f += 0.0; /* normalise negative zero to positive zero */
3002a55deb1SDavid E. O'Brien if ((vp->tval & (NUM | STR)) == 0)
3012a55deb1SDavid E. O'Brien funnyvar(vp, "assign to");
3022a55deb1SDavid E. O'Brien if (isfld(vp)) {
303f39dd6a9SWarner Losh donerec = false; /* mark $0 invalid */
3042a55deb1SDavid E. O'Brien fldno = atoi(vp->nval);
3052a55deb1SDavid E. O'Brien if (fldno > *NF)
3062a55deb1SDavid E. O'Brien newfld(fldno);
307f39dd6a9SWarner Losh DPRINTF("setting field %d to %g\n", fldno, f);
308b5253557SWarner Losh } else if (&vp->fval == NF) {
309f39dd6a9SWarner Losh donerec = false; /* mark $0 invalid */
310b5253557SWarner Losh setlastfld(f);
311f32a6403SWarner Losh DPRINTF("setfval: setting NF to %g\n", f);
3122a55deb1SDavid E. O'Brien } else if (isrec(vp)) {
313f39dd6a9SWarner Losh donefld = false; /* mark $1... invalid */
314f39dd6a9SWarner Losh donerec = true;
315f39dd6a9SWarner Losh savefs();
316b5253557SWarner Losh } else if (vp == ofsloc) {
317f39dd6a9SWarner Losh if (!donerec)
318b5253557SWarner Losh recbld();
3192a55deb1SDavid E. O'Brien }
3202a55deb1SDavid E. O'Brien if (freeable(vp))
3212a55deb1SDavid E. O'Brien xfree(vp->sval); /* free any previous string */
322b5253557SWarner Losh vp->tval &= ~(STR|CONVC|CONVO); /* mark string invalid */
323b5253557SWarner Losh vp->fmt = NULL;
3242a55deb1SDavid E. O'Brien vp->tval |= NUM; /* mark number ok */
3250840e960SXin LI if (f == -0) /* who would have thought this possible? */
3260840e960SXin LI f = 0;
327f39dd6a9SWarner Losh DPRINTF("setfval %p: %s = %g, t=%o\n", (void*)vp, NN(vp->nval), f, vp->tval);
3282a55deb1SDavid E. O'Brien return vp->fval = f;
3292a55deb1SDavid E. O'Brien }
3302a55deb1SDavid E. O'Brien
funnyvar(Cell * vp,const char * rw)331813da98dSDavid E. O'Brien void funnyvar(Cell *vp, const char *rw)
3322a55deb1SDavid E. O'Brien {
3332a55deb1SDavid E. O'Brien if (isarr(vp))
3342a55deb1SDavid E. O'Brien FATAL("can't %s %s; it's an array name.", rw, vp->nval);
3352a55deb1SDavid E. O'Brien if (vp->tval & FCN)
3362a55deb1SDavid E. O'Brien FATAL("can't %s %s; it's a function.", rw, vp->nval);
3372a55deb1SDavid E. O'Brien WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o",
338f39dd6a9SWarner Losh (void *)vp, vp->nval, vp->sval, vp->fval, vp->tval);
3392a55deb1SDavid E. O'Brien }
3402a55deb1SDavid E. O'Brien
setsval(Cell * vp,const char * s)341813da98dSDavid E. O'Brien char *setsval(Cell *vp, const char *s) /* set string val of a Cell */
3422a55deb1SDavid E. O'Brien {
3432a55deb1SDavid E. O'Brien char *t;
3442a55deb1SDavid E. O'Brien int fldno;
345b5253557SWarner Losh Awkfloat f;
3462a55deb1SDavid E. O'Brien
347f39dd6a9SWarner Losh DPRINTF("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n",
348f39dd6a9SWarner Losh (void*)vp, NN(vp->nval), s, vp->tval, donerec, donefld);
3492a55deb1SDavid E. O'Brien if ((vp->tval & (NUM | STR)) == 0)
3502a55deb1SDavid E. O'Brien funnyvar(vp, "assign to");
351f32a6403SWarner Losh if (CSV && (vp == rsloc))
352f32a6403SWarner Losh WARNING("danger: don't set RS when --csv is in effect");
353f32a6403SWarner Losh if (CSV && (vp == fsloc))
354f32a6403SWarner Losh WARNING("danger: don't set FS when --csv is in effect");
3552a55deb1SDavid E. O'Brien if (isfld(vp)) {
356f39dd6a9SWarner Losh donerec = false; /* mark $0 invalid */
3572a55deb1SDavid E. O'Brien fldno = atoi(vp->nval);
3582a55deb1SDavid E. O'Brien if (fldno > *NF)
3592a55deb1SDavid E. O'Brien newfld(fldno);
360f39dd6a9SWarner Losh DPRINTF("setting field %d to %s (%p)\n", fldno, s, (const void*)s);
3612a55deb1SDavid E. O'Brien } else if (isrec(vp)) {
362f39dd6a9SWarner Losh donefld = false; /* mark $1... invalid */
363f39dd6a9SWarner Losh donerec = true;
364f39dd6a9SWarner Losh savefs();
365b5253557SWarner Losh } else if (vp == ofsloc) {
366f39dd6a9SWarner Losh if (!donerec)
367b5253557SWarner Losh recbld();
3682a55deb1SDavid E. O'Brien }
369adb46ac4SWarner Losh t = s ? tostring(s) : tostring(""); /* in case it's self-assign */
3702a55deb1SDavid E. O'Brien if (freeable(vp))
3712a55deb1SDavid E. O'Brien xfree(vp->sval);
372f32a6403SWarner Losh vp->tval &= ~(NUM|DONTFREE|CONVC|CONVO);
373d2f6e492SDavid E. O'Brien vp->tval |= STR;
374b5253557SWarner Losh vp->fmt = NULL;
375f39dd6a9SWarner Losh DPRINTF("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n",
376f39dd6a9SWarner Losh (void*)vp, NN(vp->nval), t, (void*)t, vp->tval, donerec, donefld);
377b5253557SWarner Losh vp->sval = t;
378b5253557SWarner Losh if (&vp->fval == NF) {
379f39dd6a9SWarner Losh donerec = false; /* mark $0 invalid */
380b5253557SWarner Losh f = getfval(vp);
381b5253557SWarner Losh setlastfld(f);
382f32a6403SWarner Losh DPRINTF("setsval: setting NF to %g\n", f);
383b5253557SWarner Losh }
384b5253557SWarner Losh
385b5253557SWarner Losh return(vp->sval);
3862a55deb1SDavid E. O'Brien }
3872a55deb1SDavid E. O'Brien
getfval(Cell * vp)3882a55deb1SDavid E. O'Brien Awkfloat getfval(Cell *vp) /* get float val of a Cell */
3892a55deb1SDavid E. O'Brien {
3902a55deb1SDavid E. O'Brien if ((vp->tval & (NUM | STR)) == 0)
3912a55deb1SDavid E. O'Brien funnyvar(vp, "read value of");
392f39dd6a9SWarner Losh if (isfld(vp) && !donefld)
3932a55deb1SDavid E. O'Brien fldbld();
394f39dd6a9SWarner Losh else if (isrec(vp) && !donerec)
3952a55deb1SDavid E. O'Brien recbld();
3962a55deb1SDavid E. O'Brien if (!isnum(vp)) { /* not a number */
397f39dd6a9SWarner Losh double fval;
398f39dd6a9SWarner Losh bool no_trailing;
399f39dd6a9SWarner Losh
400f39dd6a9SWarner Losh if (is_valid_number(vp->sval, true, & no_trailing, & fval)) {
401f39dd6a9SWarner Losh vp->fval = fval;
402f39dd6a9SWarner Losh if (no_trailing && !(vp->tval&CON))
4032a55deb1SDavid E. O'Brien vp->tval |= NUM; /* make NUM only sparingly */
404f39dd6a9SWarner Losh } else
405f39dd6a9SWarner Losh vp->fval = 0.0;
4062a55deb1SDavid E. O'Brien }
407f39dd6a9SWarner Losh DPRINTF("getfval %p: %s = %g, t=%o\n",
408f39dd6a9SWarner Losh (void*)vp, NN(vp->nval), vp->fval, vp->tval);
4092a55deb1SDavid E. O'Brien return(vp->fval);
4102a55deb1SDavid E. O'Brien }
4112a55deb1SDavid E. O'Brien
get_inf_nan(double d)412f39dd6a9SWarner Losh static const char *get_inf_nan(double d)
413f39dd6a9SWarner Losh {
414f39dd6a9SWarner Losh if (isinf(d)) {
415f39dd6a9SWarner Losh return (d < 0 ? "-inf" : "+inf");
416f39dd6a9SWarner Losh } else if (isnan(d)) {
417f39dd6a9SWarner Losh return (signbit(d) != 0 ? "-nan" : "+nan");
418f39dd6a9SWarner Losh } else
419f39dd6a9SWarner Losh return NULL;
420f39dd6a9SWarner Losh }
421f39dd6a9SWarner Losh
get_str_val(Cell * vp,char ** fmt)422813da98dSDavid E. O'Brien static char *get_str_val(Cell *vp, char **fmt) /* get string val of a Cell */
4232a55deb1SDavid E. O'Brien {
424b5253557SWarner Losh char s[256];
4252a55deb1SDavid E. O'Brien double dtemp;
426f39dd6a9SWarner Losh const char *p;
4272a55deb1SDavid E. O'Brien
4282a55deb1SDavid E. O'Brien if ((vp->tval & (NUM | STR)) == 0)
4292a55deb1SDavid E. O'Brien funnyvar(vp, "read value of");
430f39dd6a9SWarner Losh if (isfld(vp) && ! donefld)
4312a55deb1SDavid E. O'Brien fldbld();
432f39dd6a9SWarner Losh else if (isrec(vp) && ! donerec)
4332a55deb1SDavid E. O'Brien recbld();
434b5253557SWarner Losh
435b5253557SWarner Losh /*
436b5253557SWarner Losh * ADR: This is complicated and more fragile than is desirable.
437b5253557SWarner Losh * Retrieving a string value for a number associates the string
438b5253557SWarner Losh * value with the scalar. Previously, the string value was
439b5253557SWarner Losh * sticky, meaning if converted via OFMT that became the value
440b5253557SWarner Losh * (even though POSIX wants it to be via CONVFMT). Or if CONVFMT
441b5253557SWarner Losh * changed after a string value was retrieved, the original value
442b5253557SWarner Losh * was maintained and used. Also not per POSIX.
443b5253557SWarner Losh *
444b5253557SWarner Losh * We work around this design by adding two additional flags,
445b5253557SWarner Losh * CONVC and CONVO, indicating how the string value was
446b5253557SWarner Losh * obtained (via CONVFMT or OFMT) and _also_ maintaining a copy
447b5253557SWarner Losh * of the pointer to the xFMT format string used for the
448b5253557SWarner Losh * conversion. This pointer is only read, **never** dereferenced.
449b5253557SWarner Losh * The next time we do a conversion, if it's coming from the same
450b5253557SWarner Losh * xFMT as last time, and the pointer value is different, we
451b5253557SWarner Losh * know that the xFMT format string changed, and we need to
452b5253557SWarner Losh * redo the conversion. If it's the same, we don't have to.
453b5253557SWarner Losh *
454b5253557SWarner Losh * There are also several cases where we don't do a conversion,
455b5253557SWarner Losh * such as for a field (see the checks below).
456b5253557SWarner Losh */
457b5253557SWarner Losh
458b5253557SWarner Losh /* Don't duplicate the code for actually updating the value */
459b5253557SWarner Losh #define update_str_val(vp) \
460b5253557SWarner Losh { \
461b5253557SWarner Losh if (freeable(vp)) \
462b5253557SWarner Losh xfree(vp->sval); \
463f39dd6a9SWarner Losh if ((p = get_inf_nan(vp->fval)) != NULL) \
464f39dd6a9SWarner Losh strcpy(s, p); \
465f39dd6a9SWarner Losh else if (modf(vp->fval, &dtemp) == 0) /* it's integral */ \
466b5253557SWarner Losh snprintf(s, sizeof (s), "%.30g", vp->fval); \
467b5253557SWarner Losh else \
468b5253557SWarner Losh snprintf(s, sizeof (s), *fmt, vp->fval); \
469b5253557SWarner Losh vp->sval = tostring(s); \
470b5253557SWarner Losh vp->tval &= ~DONTFREE; \
471b5253557SWarner Losh vp->tval |= STR; \
4722a55deb1SDavid E. O'Brien }
473b5253557SWarner Losh
474b5253557SWarner Losh if (isstr(vp) == 0) {
475b5253557SWarner Losh update_str_val(vp);
476b5253557SWarner Losh if (fmt == OFMT) {
477b5253557SWarner Losh vp->tval &= ~CONVC;
478b5253557SWarner Losh vp->tval |= CONVO;
479b5253557SWarner Losh } else {
480b5253557SWarner Losh /* CONVFMT */
481b5253557SWarner Losh vp->tval &= ~CONVO;
482b5253557SWarner Losh vp->tval |= CONVC;
483b5253557SWarner Losh }
484b5253557SWarner Losh vp->fmt = *fmt;
485b5253557SWarner Losh } else if ((vp->tval & DONTFREE) != 0 || ! isnum(vp) || isfld(vp)) {
486b5253557SWarner Losh goto done;
487b5253557SWarner Losh } else if (isstr(vp)) {
488b5253557SWarner Losh if (fmt == OFMT) {
489b5253557SWarner Losh if ((vp->tval & CONVC) != 0
490b5253557SWarner Losh || ((vp->tval & CONVO) != 0 && vp->fmt != *fmt)) {
491b5253557SWarner Losh update_str_val(vp);
492b5253557SWarner Losh vp->tval &= ~CONVC;
493b5253557SWarner Losh vp->tval |= CONVO;
494b5253557SWarner Losh vp->fmt = *fmt;
495b5253557SWarner Losh }
496b5253557SWarner Losh } else {
497b5253557SWarner Losh /* CONVFMT */
498b5253557SWarner Losh if ((vp->tval & CONVO) != 0
499b5253557SWarner Losh || ((vp->tval & CONVC) != 0 && vp->fmt != *fmt)) {
500b5253557SWarner Losh update_str_val(vp);
501b5253557SWarner Losh vp->tval &= ~CONVO;
502b5253557SWarner Losh vp->tval |= CONVC;
503b5253557SWarner Losh vp->fmt = *fmt;
504b5253557SWarner Losh }
505b5253557SWarner Losh }
506b5253557SWarner Losh }
507b5253557SWarner Losh done:
508f39dd6a9SWarner Losh DPRINTF("getsval %p: %s = \"%s (%p)\", t=%o\n",
509f39dd6a9SWarner Losh (void*)vp, NN(vp->nval), vp->sval, (void*)vp->sval, vp->tval);
5102a55deb1SDavid E. O'Brien return(vp->sval);
5112a55deb1SDavid E. O'Brien }
5122a55deb1SDavid E. O'Brien
getsval(Cell * vp)513813da98dSDavid E. O'Brien char *getsval(Cell *vp) /* get string val of a Cell */
514813da98dSDavid E. O'Brien {
515813da98dSDavid E. O'Brien return get_str_val(vp, CONVFMT);
516813da98dSDavid E. O'Brien }
517813da98dSDavid E. O'Brien
getpssval(Cell * vp)518813da98dSDavid E. O'Brien char *getpssval(Cell *vp) /* get string val of a Cell for print */
519813da98dSDavid E. O'Brien {
520813da98dSDavid E. O'Brien return get_str_val(vp, OFMT);
521813da98dSDavid E. O'Brien }
522813da98dSDavid E. O'Brien
523813da98dSDavid E. O'Brien
tostring(const char * s)524813da98dSDavid E. O'Brien char *tostring(const char *s) /* make a copy of string s */
5252a55deb1SDavid E. O'Brien {
526f39dd6a9SWarner Losh char *p = strdup(s);
527f39dd6a9SWarner Losh if (p == NULL)
528f39dd6a9SWarner Losh FATAL("out of space in tostring on %s", s);
529f39dd6a9SWarner Losh return(p);
530f39dd6a9SWarner Losh }
531f39dd6a9SWarner Losh
tostringN(const char * s,size_t n)532f39dd6a9SWarner Losh char *tostringN(const char *s, size_t n) /* make a copy of string s */
533f39dd6a9SWarner Losh {
5342a55deb1SDavid E. O'Brien char *p;
5352a55deb1SDavid E. O'Brien
536f39dd6a9SWarner Losh p = (char *) malloc(n);
5372a55deb1SDavid E. O'Brien if (p == NULL)
5382a55deb1SDavid E. O'Brien FATAL("out of space in tostring on %s", s);
5392a55deb1SDavid E. O'Brien strcpy(p, s);
5402a55deb1SDavid E. O'Brien return(p);
5412a55deb1SDavid E. O'Brien }
5422a55deb1SDavid E. O'Brien
catstr(Cell * a,Cell * b)543f39dd6a9SWarner Losh Cell *catstr(Cell *a, Cell *b) /* concatenate a and b */
544f39dd6a9SWarner Losh {
545f39dd6a9SWarner Losh Cell *c;
546f39dd6a9SWarner Losh char *p;
547f39dd6a9SWarner Losh char *sa = getsval(a);
548f39dd6a9SWarner Losh char *sb = getsval(b);
549f39dd6a9SWarner Losh size_t l = strlen(sa) + strlen(sb) + 1;
550f39dd6a9SWarner Losh p = (char *) malloc(l);
551f39dd6a9SWarner Losh if (p == NULL)
552f39dd6a9SWarner Losh FATAL("out of space concatenating %s and %s", sa, sb);
553f39dd6a9SWarner Losh snprintf(p, l, "%s%s", sa, sb);
554f39dd6a9SWarner Losh
555f39dd6a9SWarner Losh l++; // add room for ' '
556f39dd6a9SWarner Losh char *newbuf = (char *) malloc(l);
557f39dd6a9SWarner Losh if (newbuf == NULL)
558f39dd6a9SWarner Losh FATAL("out of space concatenating %s and %s", sa, sb);
559f39dd6a9SWarner Losh // See string() in lex.c; a string "xx" is stored in the symbol
560f39dd6a9SWarner Losh // table as "xx ".
561f39dd6a9SWarner Losh snprintf(newbuf, l, "%s ", p);
562f39dd6a9SWarner Losh c = setsymtab(newbuf, p, 0.0, CON|STR|DONTFREE, symtab);
563f39dd6a9SWarner Losh free(p);
564f39dd6a9SWarner Losh free(newbuf);
565f39dd6a9SWarner Losh return c;
566f39dd6a9SWarner Losh }
567f39dd6a9SWarner Losh
qstring(const char * is,int delim)568813da98dSDavid E. O'Brien char *qstring(const char *is, int delim) /* collect string up to next delim */
5692a55deb1SDavid E. O'Brien {
5702a55deb1SDavid E. O'Brien int c, n;
571f39dd6a9SWarner Losh const uschar *s = (const uschar *) is;
5722a55deb1SDavid E. O'Brien uschar *buf, *bp;
5732a55deb1SDavid E. O'Brien
574007c6572SDag-Erling Smørgrav if ((buf = (uschar *) malloc(strlen(is)+3)) == NULL)
5752a55deb1SDavid E. O'Brien FATAL( "out of space in qstring(%s)", s);
5762a55deb1SDavid E. O'Brien for (bp = buf; (c = *s) != delim; s++) {
5772a55deb1SDavid E. O'Brien if (c == '\n')
578f32a6403SWarner Losh SYNTAX( "newline in string %.20s...", is );
5792a55deb1SDavid E. O'Brien else if (c != '\\')
5802a55deb1SDavid E. O'Brien *bp++ = c;
5812a55deb1SDavid E. O'Brien else { /* \something */
5822a55deb1SDavid E. O'Brien c = *++s;
5832a55deb1SDavid E. O'Brien if (c == 0) { /* \ at end */
5842a55deb1SDavid E. O'Brien *bp++ = '\\';
5852a55deb1SDavid E. O'Brien break; /* for loop */
5862a55deb1SDavid E. O'Brien }
5872a55deb1SDavid E. O'Brien switch (c) {
5882a55deb1SDavid E. O'Brien case '\\': *bp++ = '\\'; break;
5892a55deb1SDavid E. O'Brien case 'n': *bp++ = '\n'; break;
5902a55deb1SDavid E. O'Brien case 't': *bp++ = '\t'; break;
5912a55deb1SDavid E. O'Brien case 'b': *bp++ = '\b'; break;
5922a55deb1SDavid E. O'Brien case 'f': *bp++ = '\f'; break;
5932a55deb1SDavid E. O'Brien case 'r': *bp++ = '\r'; break;
594f39dd6a9SWarner Losh case 'v': *bp++ = '\v'; break;
595f39dd6a9SWarner Losh case 'a': *bp++ = '\a'; break;
5962a55deb1SDavid E. O'Brien default:
5972a55deb1SDavid E. O'Brien if (!isdigit(c)) {
5982a55deb1SDavid E. O'Brien *bp++ = c;
5992a55deb1SDavid E. O'Brien break;
6002a55deb1SDavid E. O'Brien }
6012a55deb1SDavid E. O'Brien n = c - '0';
6022a55deb1SDavid E. O'Brien if (isdigit(s[1])) {
6032a55deb1SDavid E. O'Brien n = 8 * n + *++s - '0';
6042a55deb1SDavid E. O'Brien if (isdigit(s[1]))
6052a55deb1SDavid E. O'Brien n = 8 * n + *++s - '0';
6062a55deb1SDavid E. O'Brien }
6072a55deb1SDavid E. O'Brien *bp++ = n;
6082a55deb1SDavid E. O'Brien break;
6092a55deb1SDavid E. O'Brien }
6102a55deb1SDavid E. O'Brien }
6112a55deb1SDavid E. O'Brien }
6122a55deb1SDavid E. O'Brien *bp++ = 0;
6132a55deb1SDavid E. O'Brien return (char *) buf;
6142a55deb1SDavid E. O'Brien }
615b5253557SWarner Losh
flags2str(int flags)616b5253557SWarner Losh const char *flags2str(int flags)
617b5253557SWarner Losh {
618b5253557SWarner Losh static const struct ftab {
619b5253557SWarner Losh const char *name;
620b5253557SWarner Losh int value;
621b5253557SWarner Losh } flagtab[] = {
622b5253557SWarner Losh { "NUM", NUM },
623b5253557SWarner Losh { "STR", STR },
624b5253557SWarner Losh { "DONTFREE", DONTFREE },
625b5253557SWarner Losh { "CON", CON },
626b5253557SWarner Losh { "ARR", ARR },
627b5253557SWarner Losh { "FCN", FCN },
628b5253557SWarner Losh { "FLD", FLD },
629b5253557SWarner Losh { "REC", REC },
630b5253557SWarner Losh { "CONVC", CONVC },
631b5253557SWarner Losh { "CONVO", CONVO },
632b5253557SWarner Losh { NULL, 0 }
633b5253557SWarner Losh };
634b5253557SWarner Losh static char buf[100];
635b5253557SWarner Losh int i;
636b5253557SWarner Losh char *cp = buf;
637b5253557SWarner Losh
638b5253557SWarner Losh for (i = 0; flagtab[i].name != NULL; i++) {
639b5253557SWarner Losh if ((flags & flagtab[i].value) != 0) {
640b5253557SWarner Losh if (cp > buf)
641b5253557SWarner Losh *cp++ = '|';
642b5253557SWarner Losh strcpy(cp, flagtab[i].name);
643b5253557SWarner Losh cp += strlen(cp);
644b5253557SWarner Losh }
645b5253557SWarner Losh }
646b5253557SWarner Losh
647b5253557SWarner Losh return buf;
648b5253557SWarner Losh }
649