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