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 */ 60*17853db4SWarner Losh Cell *ARGVcell; /* cell with symbol table containing ARGV[...] */ 612a55deb1SDavid E. O'Brien Cell *rstartloc; /* RSTART */ 622a55deb1SDavid E. O'Brien Cell *rlengthloc; /* RLENGTH */ 63b5253557SWarner Losh Cell *subseploc; /* SUBSEP */ 642a55deb1SDavid E. O'Brien Cell *symtabloc; /* SYMTAB */ 652a55deb1SDavid E. O'Brien 662a55deb1SDavid E. O'Brien Cell *nullloc; /* a guaranteed empty cell */ 672a55deb1SDavid E. O'Brien Node *nullnode; /* zero&null, converted into a node for comparisons */ 682a55deb1SDavid E. O'Brien Cell *literal0; 692a55deb1SDavid E. O'Brien 702a55deb1SDavid E. O'Brien extern Cell **fldtab; 712a55deb1SDavid E. O'Brien 722a55deb1SDavid E. O'Brien void syminit(void) /* initialize symbol table with builtin vars */ 732a55deb1SDavid E. O'Brien { 742a55deb1SDavid E. O'Brien literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab); 752a55deb1SDavid E. O'Brien /* this is used for if(x)... tests: */ 762a55deb1SDavid E. O'Brien nullloc = setsymtab("$zero&null", "", 0.0, NUM|STR|CON|DONTFREE, symtab); 772a55deb1SDavid E. O'Brien nullnode = celltonode(nullloc, CCON); 782a55deb1SDavid E. O'Brien 79c263f9bfSRuslan Ermilov fsloc = setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab); 80c263f9bfSRuslan Ermilov FS = &fsloc->sval; 81b5253557SWarner Losh rsloc = setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab); 82b5253557SWarner Losh RS = &rsloc->sval; 83b5253557SWarner Losh ofsloc = setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab); 84b5253557SWarner Losh OFS = &ofsloc->sval; 85b5253557SWarner Losh orsloc = setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab); 86b5253557SWarner Losh ORS = &orsloc->sval; 872a55deb1SDavid E. O'Brien OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval; 882a55deb1SDavid E. O'Brien CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval; 892a55deb1SDavid E. O'Brien FILENAME = &setsymtab("FILENAME", "", 0.0, STR|DONTFREE, symtab)->sval; 902a55deb1SDavid E. O'Brien nfloc = setsymtab("NF", "", 0.0, NUM, symtab); 912a55deb1SDavid E. O'Brien NF = &nfloc->fval; 922a55deb1SDavid E. O'Brien nrloc = setsymtab("NR", "", 0.0, NUM, symtab); 932a55deb1SDavid E. O'Brien NR = &nrloc->fval; 942a55deb1SDavid E. O'Brien fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab); 952a55deb1SDavid E. O'Brien FNR = &fnrloc->fval; 96b5253557SWarner Losh subseploc = setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab); 97b5253557SWarner Losh SUBSEP = &subseploc->sval; 982a55deb1SDavid E. O'Brien rstartloc = setsymtab("RSTART", "", 0.0, NUM, symtab); 992a55deb1SDavid E. O'Brien RSTART = &rstartloc->fval; 1002a55deb1SDavid E. O'Brien rlengthloc = setsymtab("RLENGTH", "", 0.0, NUM, symtab); 1012a55deb1SDavid E. O'Brien RLENGTH = &rlengthloc->fval; 1022a55deb1SDavid E. O'Brien symtabloc = setsymtab("SYMTAB", "", 0.0, ARR, symtab); 103f39dd6a9SWarner Losh free(symtabloc->sval); 1042a55deb1SDavid E. O'Brien symtabloc->sval = (char *) symtab; 1052a55deb1SDavid E. O'Brien } 1062a55deb1SDavid E. O'Brien 1072a55deb1SDavid E. O'Brien void arginit(int ac, char **av) /* set up ARGV and ARGC */ 1082a55deb1SDavid E. O'Brien { 109*17853db4SWarner Losh Array *ap; 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); 116*17853db4SWarner Losh ap = makesymtab(NSYMTAB); /* could be (int) ARGC as well */ 117f39dd6a9SWarner Losh free(cp->sval); 118*17853db4SWarner Losh cp->sval = (char *) ap; 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)) 124*17853db4SWarner Losh setsymtab(temp, *av, result, STR|NUM, ap); 1252a55deb1SDavid E. O'Brien else 126*17853db4SWarner Losh setsymtab(temp, *av, 0.0, STR, ap); 1272a55deb1SDavid E. O'Brien av++; 1282a55deb1SDavid E. O'Brien } 129*17853db4SWarner Losh ARGVcell = cp; 1302a55deb1SDavid E. O'Brien } 1312a55deb1SDavid E. O'Brien 1322a55deb1SDavid E. O'Brien void envinit(char **envp) /* set up ENVIRON variable */ 1332a55deb1SDavid E. O'Brien { 134*17853db4SWarner Losh Array *ap; 1352a55deb1SDavid E. O'Brien Cell *cp; 1362a55deb1SDavid E. O'Brien char *p; 1372a55deb1SDavid E. O'Brien 1382a55deb1SDavid E. O'Brien cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab); 139*17853db4SWarner Losh ap = makesymtab(NSYMTAB); 140f39dd6a9SWarner Losh free(cp->sval); 141*17853db4SWarner Losh cp->sval = (char *) ap; 1422a55deb1SDavid E. O'Brien for ( ; *envp; envp++) { 143f39dd6a9SWarner Losh double result; 144f39dd6a9SWarner Losh 1452a55deb1SDavid E. O'Brien if ((p = strchr(*envp, '=')) == NULL) 1462a55deb1SDavid E. O'Brien continue; 1472a55deb1SDavid E. O'Brien if( p == *envp ) /* no left hand side name in env string */ 1482a55deb1SDavid E. O'Brien continue; 1492a55deb1SDavid E. O'Brien *p++ = 0; /* split into two strings at = */ 150f39dd6a9SWarner Losh if (is_number(p, & result)) 151*17853db4SWarner Losh setsymtab(*envp, p, result, STR|NUM, ap); 1522a55deb1SDavid E. O'Brien else 153*17853db4SWarner Losh setsymtab(*envp, p, 0.0, STR, ap); 1542a55deb1SDavid E. O'Brien p[-1] = '='; /* restore in case env is passed down to a shell */ 1552a55deb1SDavid E. O'Brien } 1562a55deb1SDavid E. O'Brien } 1572a55deb1SDavid E. O'Brien 1582a55deb1SDavid E. O'Brien Array *makesymtab(int n) /* make a new symbol table */ 1592a55deb1SDavid E. O'Brien { 1602a55deb1SDavid E. O'Brien Array *ap; 1612a55deb1SDavid E. O'Brien Cell **tp; 1622a55deb1SDavid E. O'Brien 163f39dd6a9SWarner Losh ap = (Array *) malloc(sizeof(*ap)); 164f39dd6a9SWarner Losh tp = (Cell **) calloc(n, sizeof(*tp)); 1652a55deb1SDavid E. O'Brien if (ap == NULL || tp == NULL) 1662a55deb1SDavid E. O'Brien FATAL("out of space in makesymtab"); 1672a55deb1SDavid E. O'Brien ap->nelem = 0; 1682a55deb1SDavid E. O'Brien ap->size = n; 1692a55deb1SDavid E. O'Brien ap->tab = tp; 1702a55deb1SDavid E. O'Brien return(ap); 1712a55deb1SDavid E. O'Brien } 1722a55deb1SDavid E. O'Brien 1732a55deb1SDavid E. O'Brien void freesymtab(Cell *ap) /* free a symbol table */ 1742a55deb1SDavid E. O'Brien { 1752a55deb1SDavid E. O'Brien Cell *cp, *temp; 1762a55deb1SDavid E. O'Brien Array *tp; 1772a55deb1SDavid E. O'Brien int i; 1782a55deb1SDavid E. O'Brien 1792a55deb1SDavid E. O'Brien if (!isarr(ap)) 1802a55deb1SDavid E. O'Brien return; 1812a55deb1SDavid E. O'Brien tp = (Array *) ap->sval; 1822a55deb1SDavid E. O'Brien if (tp == NULL) 1832a55deb1SDavid E. O'Brien return; 1842a55deb1SDavid E. O'Brien for (i = 0; i < tp->size; i++) { 1852a55deb1SDavid E. O'Brien for (cp = tp->tab[i]; cp != NULL; cp = temp) { 1862a55deb1SDavid E. O'Brien xfree(cp->nval); 1872a55deb1SDavid E. O'Brien if (freeable(cp)) 1882a55deb1SDavid E. O'Brien xfree(cp->sval); 1892a55deb1SDavid E. O'Brien temp = cp->cnext; /* avoids freeing then using */ 1902a55deb1SDavid E. O'Brien free(cp); 191007c6572SDag-Erling Smørgrav tp->nelem--; 1922a55deb1SDavid E. O'Brien } 19310ce5b99SWarner Losh tp->tab[i] = NULL; 1942a55deb1SDavid E. O'Brien } 195007c6572SDag-Erling Smørgrav if (tp->nelem != 0) 196007c6572SDag-Erling Smørgrav WARNING("can't happen: inconsistent element count freeing %s", ap->nval); 1972a55deb1SDavid E. O'Brien free(tp->tab); 1982a55deb1SDavid E. O'Brien free(tp); 1992a55deb1SDavid E. O'Brien } 2002a55deb1SDavid E. O'Brien 201813da98dSDavid E. O'Brien void freeelem(Cell *ap, const char *s) /* free elem s from ap (i.e., ap["s"] */ 2022a55deb1SDavid E. O'Brien { 2032a55deb1SDavid E. O'Brien Array *tp; 2042a55deb1SDavid E. O'Brien Cell *p, *prev = NULL; 2052a55deb1SDavid E. O'Brien int h; 2062a55deb1SDavid E. O'Brien 2072a55deb1SDavid E. O'Brien tp = (Array *) ap->sval; 2082a55deb1SDavid E. O'Brien h = hash(s, tp->size); 2092a55deb1SDavid E. O'Brien for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext) 2102a55deb1SDavid E. O'Brien if (strcmp(s, p->nval) == 0) { 2112a55deb1SDavid E. O'Brien if (prev == NULL) /* 1st one */ 2122a55deb1SDavid E. O'Brien tp->tab[h] = p->cnext; 2132a55deb1SDavid E. O'Brien else /* middle somewhere */ 2142a55deb1SDavid E. O'Brien prev->cnext = p->cnext; 2152a55deb1SDavid E. O'Brien if (freeable(p)) 2162a55deb1SDavid E. O'Brien xfree(p->sval); 2172a55deb1SDavid E. O'Brien free(p->nval); 2182a55deb1SDavid E. O'Brien free(p); 2192a55deb1SDavid E. O'Brien tp->nelem--; 2202a55deb1SDavid E. O'Brien return; 2212a55deb1SDavid E. O'Brien } 2222a55deb1SDavid E. O'Brien } 2232a55deb1SDavid E. O'Brien 224813da98dSDavid E. O'Brien Cell *setsymtab(const char *n, const char *s, Awkfloat f, unsigned t, Array *tp) 2252a55deb1SDavid E. O'Brien { 2262a55deb1SDavid E. O'Brien int h; 2272a55deb1SDavid E. O'Brien Cell *p; 2282a55deb1SDavid E. O'Brien 2292a55deb1SDavid E. O'Brien if (n != NULL && (p = lookup(n, tp)) != NULL) { 230f39dd6a9SWarner Losh DPRINTF("setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n", 231f39dd6a9SWarner Losh (void*)p, NN(p->nval), NN(p->sval), p->fval, p->tval); 2322a55deb1SDavid E. O'Brien return(p); 2332a55deb1SDavid E. O'Brien } 234f39dd6a9SWarner Losh p = (Cell *) malloc(sizeof(*p)); 2352a55deb1SDavid E. O'Brien if (p == NULL) 2362a55deb1SDavid E. O'Brien FATAL("out of space for symbol table at %s", n); 2372a55deb1SDavid E. O'Brien p->nval = tostring(n); 2382a55deb1SDavid E. O'Brien p->sval = s ? tostring(s) : tostring(""); 2392a55deb1SDavid E. O'Brien p->fval = f; 2402a55deb1SDavid E. O'Brien p->tval = t; 2412a55deb1SDavid E. O'Brien p->csub = CUNK; 2422a55deb1SDavid E. O'Brien p->ctype = OCELL; 2432a55deb1SDavid E. O'Brien tp->nelem++; 2442a55deb1SDavid E. O'Brien if (tp->nelem > FULLTAB * tp->size) 2452a55deb1SDavid E. O'Brien rehash(tp); 2462a55deb1SDavid E. O'Brien h = hash(n, tp->size); 2472a55deb1SDavid E. O'Brien p->cnext = tp->tab[h]; 2482a55deb1SDavid E. O'Brien tp->tab[h] = p; 249f39dd6a9SWarner Losh DPRINTF("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n", 250f39dd6a9SWarner Losh (void*)p, p->nval, p->sval, p->fval, p->tval); 2512a55deb1SDavid E. O'Brien return(p); 2522a55deb1SDavid E. O'Brien } 2532a55deb1SDavid E. O'Brien 254813da98dSDavid E. O'Brien int hash(const char *s, int n) /* form hash value for string s */ 2552a55deb1SDavid E. O'Brien { 2562a55deb1SDavid E. O'Brien unsigned hashval; 2572a55deb1SDavid E. O'Brien 2582a55deb1SDavid E. O'Brien for (hashval = 0; *s != '\0'; s++) 2592a55deb1SDavid E. O'Brien hashval = (*s + 31 * hashval); 2602a55deb1SDavid E. O'Brien return hashval % n; 2612a55deb1SDavid E. O'Brien } 2622a55deb1SDavid E. O'Brien 2632a55deb1SDavid E. O'Brien void rehash(Array *tp) /* rehash items in small table into big one */ 2642a55deb1SDavid E. O'Brien { 2652a55deb1SDavid E. O'Brien int i, nh, nsz; 2662a55deb1SDavid E. O'Brien Cell *cp, *op, **np; 2672a55deb1SDavid E. O'Brien 2682a55deb1SDavid E. O'Brien nsz = GROWTAB * tp->size; 269f39dd6a9SWarner Losh np = (Cell **) calloc(nsz, sizeof(*np)); 2702a55deb1SDavid E. O'Brien if (np == NULL) /* can't do it, but can keep running. */ 2712a55deb1SDavid E. O'Brien return; /* someone else will run out later. */ 2722a55deb1SDavid E. O'Brien for (i = 0; i < tp->size; i++) { 2732a55deb1SDavid E. O'Brien for (cp = tp->tab[i]; cp; cp = op) { 2742a55deb1SDavid E. O'Brien op = cp->cnext; 2752a55deb1SDavid E. O'Brien nh = hash(cp->nval, nsz); 2762a55deb1SDavid E. O'Brien cp->cnext = np[nh]; 2772a55deb1SDavid E. O'Brien np[nh] = cp; 2782a55deb1SDavid E. O'Brien } 2792a55deb1SDavid E. O'Brien } 2802a55deb1SDavid E. O'Brien free(tp->tab); 2812a55deb1SDavid E. O'Brien tp->tab = np; 2822a55deb1SDavid E. O'Brien tp->size = nsz; 2832a55deb1SDavid E. O'Brien } 2842a55deb1SDavid E. O'Brien 285813da98dSDavid E. O'Brien Cell *lookup(const char *s, Array *tp) /* look for s in tp */ 2862a55deb1SDavid E. O'Brien { 2872a55deb1SDavid E. O'Brien Cell *p; 2882a55deb1SDavid E. O'Brien int h; 2892a55deb1SDavid E. O'Brien 2902a55deb1SDavid E. O'Brien h = hash(s, tp->size); 2912a55deb1SDavid E. O'Brien for (p = tp->tab[h]; p != NULL; p = p->cnext) 2922a55deb1SDavid E. O'Brien if (strcmp(s, p->nval) == 0) 2932a55deb1SDavid E. O'Brien return(p); /* found it */ 2942a55deb1SDavid E. O'Brien return(NULL); /* not found */ 2952a55deb1SDavid E. O'Brien } 2962a55deb1SDavid E. O'Brien 2972a55deb1SDavid E. O'Brien Awkfloat setfval(Cell *vp, Awkfloat f) /* set float val of a Cell */ 2982a55deb1SDavid E. O'Brien { 2992a55deb1SDavid E. O'Brien int fldno; 3002a55deb1SDavid E. O'Brien 301b5253557SWarner Losh f += 0.0; /* normalise negative zero to positive zero */ 3022a55deb1SDavid E. O'Brien if ((vp->tval & (NUM | STR)) == 0) 3032a55deb1SDavid E. O'Brien funnyvar(vp, "assign to"); 3042a55deb1SDavid E. O'Brien if (isfld(vp)) { 305f39dd6a9SWarner Losh donerec = false; /* mark $0 invalid */ 3062a55deb1SDavid E. O'Brien fldno = atoi(vp->nval); 3072a55deb1SDavid E. O'Brien if (fldno > *NF) 3082a55deb1SDavid E. O'Brien newfld(fldno); 309f39dd6a9SWarner Losh DPRINTF("setting field %d to %g\n", fldno, f); 310b5253557SWarner Losh } else if (&vp->fval == NF) { 311f39dd6a9SWarner Losh donerec = false; /* mark $0 invalid */ 312b5253557SWarner Losh setlastfld(f); 313f32a6403SWarner Losh DPRINTF("setfval: setting NF to %g\n", f); 3142a55deb1SDavid E. O'Brien } else if (isrec(vp)) { 315f39dd6a9SWarner Losh donefld = false; /* mark $1... invalid */ 316f39dd6a9SWarner Losh donerec = true; 317f39dd6a9SWarner Losh savefs(); 318b5253557SWarner Losh } else if (vp == ofsloc) { 319f39dd6a9SWarner Losh if (!donerec) 320b5253557SWarner Losh recbld(); 3212a55deb1SDavid E. O'Brien } 3222a55deb1SDavid E. O'Brien if (freeable(vp)) 3232a55deb1SDavid E. O'Brien xfree(vp->sval); /* free any previous string */ 324b5253557SWarner Losh vp->tval &= ~(STR|CONVC|CONVO); /* mark string invalid */ 325b5253557SWarner Losh vp->fmt = NULL; 3262a55deb1SDavid E. O'Brien vp->tval |= NUM; /* mark number ok */ 3270840e960SXin LI if (f == -0) /* who would have thought this possible? */ 3280840e960SXin LI f = 0; 329f39dd6a9SWarner Losh DPRINTF("setfval %p: %s = %g, t=%o\n", (void*)vp, NN(vp->nval), f, vp->tval); 3302a55deb1SDavid E. O'Brien return vp->fval = f; 3312a55deb1SDavid E. O'Brien } 3322a55deb1SDavid E. O'Brien 333813da98dSDavid E. O'Brien void funnyvar(Cell *vp, const char *rw) 3342a55deb1SDavid E. O'Brien { 3352a55deb1SDavid E. O'Brien if (isarr(vp)) 3362a55deb1SDavid E. O'Brien FATAL("can't %s %s; it's an array name.", rw, vp->nval); 3372a55deb1SDavid E. O'Brien if (vp->tval & FCN) 3382a55deb1SDavid E. O'Brien FATAL("can't %s %s; it's a function.", rw, vp->nval); 3392a55deb1SDavid E. O'Brien WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o", 340f39dd6a9SWarner Losh (void *)vp, vp->nval, vp->sval, vp->fval, vp->tval); 3412a55deb1SDavid E. O'Brien } 3422a55deb1SDavid E. O'Brien 343813da98dSDavid E. O'Brien char *setsval(Cell *vp, const char *s) /* set string val of a Cell */ 3442a55deb1SDavid E. O'Brien { 3452a55deb1SDavid E. O'Brien char *t; 3462a55deb1SDavid E. O'Brien int fldno; 347b5253557SWarner Losh Awkfloat f; 3482a55deb1SDavid E. O'Brien 349f39dd6a9SWarner Losh DPRINTF("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n", 350f39dd6a9SWarner Losh (void*)vp, NN(vp->nval), s, vp->tval, donerec, donefld); 3512a55deb1SDavid E. O'Brien if ((vp->tval & (NUM | STR)) == 0) 3522a55deb1SDavid E. O'Brien funnyvar(vp, "assign to"); 353f32a6403SWarner Losh if (CSV && (vp == rsloc)) 354f32a6403SWarner Losh WARNING("danger: don't set RS when --csv is in effect"); 355f32a6403SWarner Losh if (CSV && (vp == fsloc)) 356f32a6403SWarner Losh WARNING("danger: don't set FS when --csv is in effect"); 3572a55deb1SDavid E. O'Brien if (isfld(vp)) { 358f39dd6a9SWarner Losh donerec = false; /* mark $0 invalid */ 3592a55deb1SDavid E. O'Brien fldno = atoi(vp->nval); 3602a55deb1SDavid E. O'Brien if (fldno > *NF) 3612a55deb1SDavid E. O'Brien newfld(fldno); 362f39dd6a9SWarner Losh DPRINTF("setting field %d to %s (%p)\n", fldno, s, (const void*)s); 3632a55deb1SDavid E. O'Brien } else if (isrec(vp)) { 364f39dd6a9SWarner Losh donefld = false; /* mark $1... invalid */ 365f39dd6a9SWarner Losh donerec = true; 366f39dd6a9SWarner Losh savefs(); 367b5253557SWarner Losh } else if (vp == ofsloc) { 368f39dd6a9SWarner Losh if (!donerec) 369b5253557SWarner Losh recbld(); 3702a55deb1SDavid E. O'Brien } 371adb46ac4SWarner Losh t = s ? tostring(s) : tostring(""); /* in case it's self-assign */ 3722a55deb1SDavid E. O'Brien if (freeable(vp)) 3732a55deb1SDavid E. O'Brien xfree(vp->sval); 374f32a6403SWarner Losh vp->tval &= ~(NUM|DONTFREE|CONVC|CONVO); 375d2f6e492SDavid E. O'Brien vp->tval |= STR; 376b5253557SWarner Losh vp->fmt = NULL; 377f39dd6a9SWarner Losh DPRINTF("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n", 378f39dd6a9SWarner Losh (void*)vp, NN(vp->nval), t, (void*)t, vp->tval, donerec, donefld); 379b5253557SWarner Losh vp->sval = t; 380b5253557SWarner Losh if (&vp->fval == NF) { 381f39dd6a9SWarner Losh donerec = false; /* mark $0 invalid */ 382b5253557SWarner Losh f = getfval(vp); 383b5253557SWarner Losh setlastfld(f); 384f32a6403SWarner Losh DPRINTF("setsval: setting NF to %g\n", f); 385b5253557SWarner Losh } 386b5253557SWarner Losh 387b5253557SWarner Losh return(vp->sval); 3882a55deb1SDavid E. O'Brien } 3892a55deb1SDavid E. O'Brien 3902a55deb1SDavid E. O'Brien Awkfloat getfval(Cell *vp) /* get float val of a Cell */ 3912a55deb1SDavid E. O'Brien { 3922a55deb1SDavid E. O'Brien if ((vp->tval & (NUM | STR)) == 0) 3932a55deb1SDavid E. O'Brien funnyvar(vp, "read value of"); 394f39dd6a9SWarner Losh if (isfld(vp) && !donefld) 3952a55deb1SDavid E. O'Brien fldbld(); 396f39dd6a9SWarner Losh else if (isrec(vp) && !donerec) 3972a55deb1SDavid E. O'Brien recbld(); 3982a55deb1SDavid E. O'Brien if (!isnum(vp)) { /* not a number */ 399f39dd6a9SWarner Losh double fval; 400f39dd6a9SWarner Losh bool no_trailing; 401f39dd6a9SWarner Losh 402f39dd6a9SWarner Losh if (is_valid_number(vp->sval, true, & no_trailing, & fval)) { 403f39dd6a9SWarner Losh vp->fval = fval; 404f39dd6a9SWarner Losh if (no_trailing && !(vp->tval&CON)) 4052a55deb1SDavid E. O'Brien vp->tval |= NUM; /* make NUM only sparingly */ 406f39dd6a9SWarner Losh } else 407f39dd6a9SWarner Losh vp->fval = 0.0; 4082a55deb1SDavid E. O'Brien } 409f39dd6a9SWarner Losh DPRINTF("getfval %p: %s = %g, t=%o\n", 410f39dd6a9SWarner Losh (void*)vp, NN(vp->nval), vp->fval, vp->tval); 4112a55deb1SDavid E. O'Brien return(vp->fval); 4122a55deb1SDavid E. O'Brien } 4132a55deb1SDavid E. O'Brien 414f39dd6a9SWarner Losh static const char *get_inf_nan(double d) 415f39dd6a9SWarner Losh { 416f39dd6a9SWarner Losh if (isinf(d)) { 417f39dd6a9SWarner Losh return (d < 0 ? "-inf" : "+inf"); 418f39dd6a9SWarner Losh } else if (isnan(d)) { 419f39dd6a9SWarner Losh return (signbit(d) != 0 ? "-nan" : "+nan"); 420f39dd6a9SWarner Losh } else 421f39dd6a9SWarner Losh return NULL; 422f39dd6a9SWarner Losh } 423f39dd6a9SWarner Losh 424813da98dSDavid E. O'Brien static char *get_str_val(Cell *vp, char **fmt) /* get string val of a Cell */ 4252a55deb1SDavid E. O'Brien { 426b5253557SWarner Losh char s[256]; 4272a55deb1SDavid E. O'Brien double dtemp; 428f39dd6a9SWarner Losh const char *p; 4292a55deb1SDavid E. O'Brien 4302a55deb1SDavid E. O'Brien if ((vp->tval & (NUM | STR)) == 0) 4312a55deb1SDavid E. O'Brien funnyvar(vp, "read value of"); 432f39dd6a9SWarner Losh if (isfld(vp) && ! donefld) 4332a55deb1SDavid E. O'Brien fldbld(); 434f39dd6a9SWarner Losh else if (isrec(vp) && ! donerec) 4352a55deb1SDavid E. O'Brien recbld(); 436b5253557SWarner Losh 437b5253557SWarner Losh /* 438b5253557SWarner Losh * ADR: This is complicated and more fragile than is desirable. 439b5253557SWarner Losh * Retrieving a string value for a number associates the string 440b5253557SWarner Losh * value with the scalar. Previously, the string value was 441b5253557SWarner Losh * sticky, meaning if converted via OFMT that became the value 442b5253557SWarner Losh * (even though POSIX wants it to be via CONVFMT). Or if CONVFMT 443b5253557SWarner Losh * changed after a string value was retrieved, the original value 444b5253557SWarner Losh * was maintained and used. Also not per POSIX. 445b5253557SWarner Losh * 446b5253557SWarner Losh * We work around this design by adding two additional flags, 447b5253557SWarner Losh * CONVC and CONVO, indicating how the string value was 448b5253557SWarner Losh * obtained (via CONVFMT or OFMT) and _also_ maintaining a copy 449b5253557SWarner Losh * of the pointer to the xFMT format string used for the 450b5253557SWarner Losh * conversion. This pointer is only read, **never** dereferenced. 451b5253557SWarner Losh * The next time we do a conversion, if it's coming from the same 452b5253557SWarner Losh * xFMT as last time, and the pointer value is different, we 453b5253557SWarner Losh * know that the xFMT format string changed, and we need to 454b5253557SWarner Losh * redo the conversion. If it's the same, we don't have to. 455b5253557SWarner Losh * 456b5253557SWarner Losh * There are also several cases where we don't do a conversion, 457b5253557SWarner Losh * such as for a field (see the checks below). 458b5253557SWarner Losh */ 459b5253557SWarner Losh 460b5253557SWarner Losh /* Don't duplicate the code for actually updating the value */ 461b5253557SWarner Losh #define update_str_val(vp) \ 462b5253557SWarner Losh { \ 463b5253557SWarner Losh if (freeable(vp)) \ 464b5253557SWarner Losh xfree(vp->sval); \ 465f39dd6a9SWarner Losh if ((p = get_inf_nan(vp->fval)) != NULL) \ 466f39dd6a9SWarner Losh strcpy(s, p); \ 467f39dd6a9SWarner Losh else if (modf(vp->fval, &dtemp) == 0) /* it's integral */ \ 468b5253557SWarner Losh snprintf(s, sizeof (s), "%.30g", vp->fval); \ 469b5253557SWarner Losh else \ 470b5253557SWarner Losh snprintf(s, sizeof (s), *fmt, vp->fval); \ 471b5253557SWarner Losh vp->sval = tostring(s); \ 472b5253557SWarner Losh vp->tval &= ~DONTFREE; \ 473b5253557SWarner Losh vp->tval |= STR; \ 4742a55deb1SDavid E. O'Brien } 475b5253557SWarner Losh 476b5253557SWarner Losh if (isstr(vp) == 0) { 477b5253557SWarner Losh update_str_val(vp); 478b5253557SWarner Losh if (fmt == OFMT) { 479b5253557SWarner Losh vp->tval &= ~CONVC; 480b5253557SWarner Losh vp->tval |= CONVO; 481b5253557SWarner Losh } else { 482b5253557SWarner Losh /* CONVFMT */ 483b5253557SWarner Losh vp->tval &= ~CONVO; 484b5253557SWarner Losh vp->tval |= CONVC; 485b5253557SWarner Losh } 486b5253557SWarner Losh vp->fmt = *fmt; 487b5253557SWarner Losh } else if ((vp->tval & DONTFREE) != 0 || ! isnum(vp) || isfld(vp)) { 488b5253557SWarner Losh goto done; 489b5253557SWarner Losh } else if (isstr(vp)) { 490b5253557SWarner Losh if (fmt == OFMT) { 491b5253557SWarner Losh if ((vp->tval & CONVC) != 0 492b5253557SWarner Losh || ((vp->tval & CONVO) != 0 && vp->fmt != *fmt)) { 493b5253557SWarner Losh update_str_val(vp); 494b5253557SWarner Losh vp->tval &= ~CONVC; 495b5253557SWarner Losh vp->tval |= CONVO; 496b5253557SWarner Losh vp->fmt = *fmt; 497b5253557SWarner Losh } 498b5253557SWarner Losh } else { 499b5253557SWarner Losh /* CONVFMT */ 500b5253557SWarner Losh if ((vp->tval & CONVO) != 0 501b5253557SWarner Losh || ((vp->tval & CONVC) != 0 && vp->fmt != *fmt)) { 502b5253557SWarner Losh update_str_val(vp); 503b5253557SWarner Losh vp->tval &= ~CONVO; 504b5253557SWarner Losh vp->tval |= CONVC; 505b5253557SWarner Losh vp->fmt = *fmt; 506b5253557SWarner Losh } 507b5253557SWarner Losh } 508b5253557SWarner Losh } 509b5253557SWarner Losh done: 510f39dd6a9SWarner Losh DPRINTF("getsval %p: %s = \"%s (%p)\", t=%o\n", 511f39dd6a9SWarner Losh (void*)vp, NN(vp->nval), vp->sval, (void*)vp->sval, vp->tval); 5122a55deb1SDavid E. O'Brien return(vp->sval); 5132a55deb1SDavid E. O'Brien } 5142a55deb1SDavid E. O'Brien 515813da98dSDavid E. O'Brien char *getsval(Cell *vp) /* get string val of a Cell */ 516813da98dSDavid E. O'Brien { 517813da98dSDavid E. O'Brien return get_str_val(vp, CONVFMT); 518813da98dSDavid E. O'Brien } 519813da98dSDavid E. O'Brien 520813da98dSDavid E. O'Brien char *getpssval(Cell *vp) /* get string val of a Cell for print */ 521813da98dSDavid E. O'Brien { 522813da98dSDavid E. O'Brien return get_str_val(vp, OFMT); 523813da98dSDavid E. O'Brien } 524813da98dSDavid E. O'Brien 525813da98dSDavid E. O'Brien 526813da98dSDavid E. O'Brien char *tostring(const char *s) /* make a copy of string s */ 5272a55deb1SDavid E. O'Brien { 528f39dd6a9SWarner Losh char *p = strdup(s); 529f39dd6a9SWarner Losh if (p == NULL) 530f39dd6a9SWarner Losh FATAL("out of space in tostring on %s", s); 531f39dd6a9SWarner Losh return(p); 532f39dd6a9SWarner Losh } 533f39dd6a9SWarner Losh 534f39dd6a9SWarner Losh char *tostringN(const char *s, size_t n) /* make a copy of string s */ 535f39dd6a9SWarner Losh { 5362a55deb1SDavid E. O'Brien char *p; 5372a55deb1SDavid E. O'Brien 538f39dd6a9SWarner Losh p = (char *) malloc(n); 5392a55deb1SDavid E. O'Brien if (p == NULL) 5402a55deb1SDavid E. O'Brien FATAL("out of space in tostring on %s", s); 5412a55deb1SDavid E. O'Brien strcpy(p, s); 5422a55deb1SDavid E. O'Brien return(p); 5432a55deb1SDavid E. O'Brien } 5442a55deb1SDavid E. O'Brien 545f39dd6a9SWarner Losh Cell *catstr(Cell *a, Cell *b) /* concatenate a and b */ 546f39dd6a9SWarner Losh { 547f39dd6a9SWarner Losh Cell *c; 548f39dd6a9SWarner Losh char *p; 549f39dd6a9SWarner Losh char *sa = getsval(a); 550f39dd6a9SWarner Losh char *sb = getsval(b); 551f39dd6a9SWarner Losh size_t l = strlen(sa) + strlen(sb) + 1; 552f39dd6a9SWarner Losh p = (char *) malloc(l); 553f39dd6a9SWarner Losh if (p == NULL) 554f39dd6a9SWarner Losh FATAL("out of space concatenating %s and %s", sa, sb); 555f39dd6a9SWarner Losh snprintf(p, l, "%s%s", sa, sb); 556f39dd6a9SWarner Losh 557f39dd6a9SWarner Losh l++; // add room for ' ' 558f39dd6a9SWarner Losh char *newbuf = (char *) malloc(l); 559f39dd6a9SWarner Losh if (newbuf == NULL) 560f39dd6a9SWarner Losh FATAL("out of space concatenating %s and %s", sa, sb); 561f39dd6a9SWarner Losh // See string() in lex.c; a string "xx" is stored in the symbol 562f39dd6a9SWarner Losh // table as "xx ". 563f39dd6a9SWarner Losh snprintf(newbuf, l, "%s ", p); 564f39dd6a9SWarner Losh c = setsymtab(newbuf, p, 0.0, CON|STR|DONTFREE, symtab); 565f39dd6a9SWarner Losh free(p); 566f39dd6a9SWarner Losh free(newbuf); 567f39dd6a9SWarner Losh return c; 568f39dd6a9SWarner Losh } 569f39dd6a9SWarner Losh 570813da98dSDavid E. O'Brien char *qstring(const char *is, int delim) /* collect string up to next delim */ 5712a55deb1SDavid E. O'Brien { 5722a55deb1SDavid E. O'Brien int c, n; 573f39dd6a9SWarner Losh const uschar *s = (const uschar *) is; 5742a55deb1SDavid E. O'Brien uschar *buf, *bp; 5752a55deb1SDavid E. O'Brien 576007c6572SDag-Erling Smørgrav if ((buf = (uschar *) malloc(strlen(is)+3)) == NULL) 5772a55deb1SDavid E. O'Brien FATAL( "out of space in qstring(%s)", s); 5782a55deb1SDavid E. O'Brien for (bp = buf; (c = *s) != delim; s++) { 5792a55deb1SDavid E. O'Brien if (c == '\n') 580f32a6403SWarner Losh SYNTAX( "newline in string %.20s...", is ); 5812a55deb1SDavid E. O'Brien else if (c != '\\') 5822a55deb1SDavid E. O'Brien *bp++ = c; 5832a55deb1SDavid E. O'Brien else { /* \something */ 5842a55deb1SDavid E. O'Brien c = *++s; 5852a55deb1SDavid E. O'Brien if (c == 0) { /* \ at end */ 5862a55deb1SDavid E. O'Brien *bp++ = '\\'; 5872a55deb1SDavid E. O'Brien break; /* for loop */ 5882a55deb1SDavid E. O'Brien } 5892a55deb1SDavid E. O'Brien switch (c) { 5902a55deb1SDavid E. O'Brien case '\\': *bp++ = '\\'; break; 5912a55deb1SDavid E. O'Brien case 'n': *bp++ = '\n'; break; 5922a55deb1SDavid E. O'Brien case 't': *bp++ = '\t'; break; 5932a55deb1SDavid E. O'Brien case 'b': *bp++ = '\b'; break; 5942a55deb1SDavid E. O'Brien case 'f': *bp++ = '\f'; break; 5952a55deb1SDavid E. O'Brien case 'r': *bp++ = '\r'; break; 596f39dd6a9SWarner Losh case 'v': *bp++ = '\v'; break; 597f39dd6a9SWarner Losh case 'a': *bp++ = '\a'; break; 5982a55deb1SDavid E. O'Brien default: 5992a55deb1SDavid E. O'Brien if (!isdigit(c)) { 6002a55deb1SDavid E. O'Brien *bp++ = c; 6012a55deb1SDavid E. O'Brien break; 6022a55deb1SDavid E. O'Brien } 6032a55deb1SDavid E. O'Brien n = c - '0'; 6042a55deb1SDavid E. O'Brien if (isdigit(s[1])) { 6052a55deb1SDavid E. O'Brien n = 8 * n + *++s - '0'; 6062a55deb1SDavid E. O'Brien if (isdigit(s[1])) 6072a55deb1SDavid E. O'Brien n = 8 * n + *++s - '0'; 6082a55deb1SDavid E. O'Brien } 6092a55deb1SDavid E. O'Brien *bp++ = n; 6102a55deb1SDavid E. O'Brien break; 6112a55deb1SDavid E. O'Brien } 6122a55deb1SDavid E. O'Brien } 6132a55deb1SDavid E. O'Brien } 6142a55deb1SDavid E. O'Brien *bp++ = 0; 6152a55deb1SDavid E. O'Brien return (char *) buf; 6162a55deb1SDavid E. O'Brien } 617b5253557SWarner Losh 618b5253557SWarner Losh const char *flags2str(int flags) 619b5253557SWarner Losh { 620b5253557SWarner Losh static const struct ftab { 621b5253557SWarner Losh const char *name; 622b5253557SWarner Losh int value; 623b5253557SWarner Losh } flagtab[] = { 624b5253557SWarner Losh { "NUM", NUM }, 625b5253557SWarner Losh { "STR", STR }, 626b5253557SWarner Losh { "DONTFREE", DONTFREE }, 627b5253557SWarner Losh { "CON", CON }, 628b5253557SWarner Losh { "ARR", ARR }, 629b5253557SWarner Losh { "FCN", FCN }, 630b5253557SWarner Losh { "FLD", FLD }, 631b5253557SWarner Losh { "REC", REC }, 632b5253557SWarner Losh { "CONVC", CONVC }, 633b5253557SWarner Losh { "CONVO", CONVO }, 634b5253557SWarner Losh { NULL, 0 } 635b5253557SWarner Losh }; 636b5253557SWarner Losh static char buf[100]; 637b5253557SWarner Losh int i; 638b5253557SWarner Losh char *cp = buf; 639b5253557SWarner Losh 640b5253557SWarner Losh for (i = 0; flagtab[i].name != NULL; i++) { 641b5253557SWarner Losh if ((flags & flagtab[i].value) != 0) { 642b5253557SWarner Losh if (cp > buf) 643b5253557SWarner Losh *cp++ = '|'; 644b5253557SWarner Losh strcpy(cp, flagtab[i].name); 645b5253557SWarner Losh cp += strlen(cp); 646b5253557SWarner Losh } 647b5253557SWarner Losh } 648b5253557SWarner Losh 649b5253557SWarner Losh return buf; 650b5253557SWarner Losh } 651