1*f190fa91Smillert /* $OpenBSD: tran.c,v 1.36 2022/09/21 01:42:59 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 746ab05f83Stholo void syminit(void) /* initialize symbol table with builtin vars */ 756ab05f83Stholo { 7607edfa4aSkstailey literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab); 776ab05f83Stholo /* this is used for if(x)... tests: */ 786ab05f83Stholo nullloc = setsymtab("$zero&null", "", 0.0, NUM|STR|CON|DONTFREE, symtab); 7907edfa4aSkstailey nullnode = celltonode(nullloc, CCON); 806ab05f83Stholo 8123cb51abSmillert fsloc = setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab); 8223cb51abSmillert FS = &fsloc->sval; 8302265e66Smillert rsloc = setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab); 8402265e66Smillert RS = &rsloc->sval; 8502265e66Smillert ofsloc = setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab); 8602265e66Smillert OFS = &ofsloc->sval; 8702265e66Smillert orsloc = setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab); 8802265e66Smillert ORS = &orsloc->sval; 896ab05f83Stholo OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval; 906ab05f83Stholo CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval; 91b2698ba9Smillert FILENAME = &setsymtab("FILENAME", "", 0.0, STR|DONTFREE, symtab)->sval; 926ab05f83Stholo nfloc = setsymtab("NF", "", 0.0, NUM, symtab); 936ab05f83Stholo NF = &nfloc->fval; 946ab05f83Stholo nrloc = setsymtab("NR", "", 0.0, NUM, symtab); 956ab05f83Stholo NR = &nrloc->fval; 966ab05f83Stholo fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab); 976ab05f83Stholo FNR = &fnrloc->fval; 9802265e66Smillert subseploc = setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab); 9902265e66Smillert SUBSEP = &subseploc->sval; 1006ab05f83Stholo rstartloc = setsymtab("RSTART", "", 0.0, NUM, symtab); 1016ab05f83Stholo RSTART = &rstartloc->fval; 1026ab05f83Stholo rlengthloc = setsymtab("RLENGTH", "", 0.0, NUM, symtab); 1036ab05f83Stholo RLENGTH = &rlengthloc->fval; 1046ab05f83Stholo symtabloc = setsymtab("SYMTAB", "", 0.0, ARR, symtab); 10592497129Smillert free(symtabloc->sval); 1066ab05f83Stholo symtabloc->sval = (char *) symtab; 1076ab05f83Stholo } 1086ab05f83Stholo 10907edfa4aSkstailey void arginit(int ac, char **av) /* set up ARGV and ARGC */ 1106ab05f83Stholo { 1116ab05f83Stholo Cell *cp; 1126ab05f83Stholo int i; 11307edfa4aSkstailey char temp[50]; 1146ab05f83Stholo 1156ab05f83Stholo ARGC = &setsymtab("ARGC", "", (Awkfloat) ac, NUM, symtab)->fval; 1166ab05f83Stholo cp = setsymtab("ARGV", "", 0.0, ARR, symtab); 1176ab05f83Stholo ARGVtab = makesymtab(NSYMTAB); /* could be (int) ARGC as well */ 11892497129Smillert free(cp->sval); 1196ab05f83Stholo cp->sval = (char *) ARGVtab; 1206ab05f83Stholo for (i = 0; i < ac; i++) { 121483fa115Smillert double result; 122483fa115Smillert 1239e405e78Sderaadt snprintf(temp, sizeof temp, "%d", i); 124483fa115Smillert if (is_number(*av, & result)) 125483fa115Smillert setsymtab(temp, *av, result, STR|NUM, ARGVtab); 1266ab05f83Stholo else 1276ab05f83Stholo setsymtab(temp, *av, 0.0, STR, ARGVtab); 1286ab05f83Stholo av++; 1296ab05f83Stholo } 1306ab05f83Stholo } 1316ab05f83Stholo 1326ab05f83Stholo void envinit(char **envp) /* set up ENVIRON variable */ 1336ab05f83Stholo { 1346ab05f83Stholo Cell *cp; 1356ab05f83Stholo char *p; 1366ab05f83Stholo 1376ab05f83Stholo cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab); 1386ab05f83Stholo ENVtab = makesymtab(NSYMTAB); 13992497129Smillert free(cp->sval); 1406ab05f83Stholo cp->sval = (char *) ENVtab; 1416ab05f83Stholo for ( ; *envp; envp++) { 142483fa115Smillert double result; 143483fa115Smillert 14407edfa4aSkstailey if ((p = strchr(*envp, '=')) == NULL) 1456ab05f83Stholo continue; 146a27f5228Smillert if( p == *envp ) /* no left hand side name in env string */ 147a27f5228Smillert continue; 1486ab05f83Stholo *p++ = 0; /* split into two strings at = */ 149483fa115Smillert if (is_number(p, & result)) 150483fa115Smillert setsymtab(*envp, p, result, STR|NUM, ENVtab); 1516ab05f83Stholo else 1526ab05f83Stholo setsymtab(*envp, p, 0.0, STR, ENVtab); 1536ab05f83Stholo p[-1] = '='; /* restore in case env is passed down to a shell */ 1546ab05f83Stholo } 1556ab05f83Stholo } 1566ab05f83Stholo 1576ab05f83Stholo Array *makesymtab(int n) /* make a new symbol table */ 1586ab05f83Stholo { 1596ab05f83Stholo Array *ap; 1606ab05f83Stholo Cell **tp; 1616ab05f83Stholo 162483fa115Smillert ap = (Array *) malloc(sizeof(*ap)); 163483fa115Smillert tp = (Cell **) calloc(n, sizeof(*tp)); 1646ab05f83Stholo if (ap == NULL || tp == NULL) 1657b11b857Smillert FATAL("out of space in makesymtab"); 1666ab05f83Stholo ap->nelem = 0; 1676ab05f83Stholo ap->size = n; 1686ab05f83Stholo ap->tab = tp; 1696ab05f83Stholo return(ap); 1706ab05f83Stholo } 1716ab05f83Stholo 1726ab05f83Stholo void freesymtab(Cell *ap) /* free a symbol table */ 1736ab05f83Stholo { 1746ab05f83Stholo Cell *cp, *temp; 1756ab05f83Stholo Array *tp; 1766ab05f83Stholo int i; 1776ab05f83Stholo 1786ab05f83Stholo if (!isarr(ap)) 1796ab05f83Stholo return; 1806ab05f83Stholo tp = (Array *) ap->sval; 1816ab05f83Stholo if (tp == NULL) 1826ab05f83Stholo return; 1836ab05f83Stholo for (i = 0; i < tp->size; i++) { 1846ab05f83Stholo for (cp = tp->tab[i]; cp != NULL; cp = temp) { 1856ab05f83Stholo xfree(cp->nval); 1866ab05f83Stholo if (freeable(cp)) 1876ab05f83Stholo xfree(cp->sval); 1886ab05f83Stholo temp = cp->cnext; /* avoids freeing then using */ 18907edfa4aSkstailey free(cp); 1909a69093aSmillert tp->nelem--; 1916ab05f83Stholo } 192c0fa3611Smillert tp->tab[i] = NULL; 1936ab05f83Stholo } 1949a69093aSmillert if (tp->nelem != 0) 1959a69093aSmillert WARNING("can't happen: inconsistent element count freeing %s", ap->nval); 19607edfa4aSkstailey free(tp->tab); 19707edfa4aSkstailey free(tp); 1986ab05f83Stholo } 1996ab05f83Stholo 2009a69093aSmillert void freeelem(Cell *ap, const char *s) /* free elem s from ap (i.e., ap["s"] */ 2016ab05f83Stholo { 2026ab05f83Stholo Array *tp; 2036ab05f83Stholo Cell *p, *prev = NULL; 2046ab05f83Stholo int h; 2056ab05f83Stholo 2066ab05f83Stholo tp = (Array *) ap->sval; 2076ab05f83Stholo h = hash(s, tp->size); 2086ab05f83Stholo for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext) 20907edfa4aSkstailey if (strcmp(s, p->nval) == 0) { 2106ab05f83Stholo if (prev == NULL) /* 1st one */ 2116ab05f83Stholo tp->tab[h] = p->cnext; 2126ab05f83Stholo else /* middle somewhere */ 2136ab05f83Stholo prev->cnext = p->cnext; 2146ab05f83Stholo if (freeable(p)) 2156ab05f83Stholo xfree(p->sval); 2166ab05f83Stholo free(p->nval); 21707edfa4aSkstailey free(p); 2186ab05f83Stholo tp->nelem--; 2196ab05f83Stholo return; 2206ab05f83Stholo } 2216ab05f83Stholo } 2226ab05f83Stholo 2239a69093aSmillert Cell *setsymtab(const char *n, const char *s, Awkfloat f, unsigned t, Array *tp) 2246ab05f83Stholo { 2256ab05f83Stholo int h; 2266ab05f83Stholo Cell *p; 2276ab05f83Stholo 2286ab05f83Stholo if (n != NULL && (p = lookup(n, tp)) != NULL) { 229115bd590Smillert DPRINTF("setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n", 230115bd590Smillert (void*)p, NN(p->nval), NN(p->sval), p->fval, p->tval); 2316ab05f83Stholo return(p); 2326ab05f83Stholo } 233483fa115Smillert p = (Cell *) malloc(sizeof(*p)); 2346ab05f83Stholo if (p == NULL) 2357b11b857Smillert FATAL("out of space for symbol table at %s", n); 2366ab05f83Stholo p->nval = tostring(n); 2376ab05f83Stholo p->sval = s ? tostring(s) : tostring(""); 2386ab05f83Stholo p->fval = f; 2396ab05f83Stholo p->tval = t; 2406ab05f83Stholo p->csub = CUNK; 2416ab05f83Stholo p->ctype = OCELL; 2426ab05f83Stholo tp->nelem++; 2436ab05f83Stholo if (tp->nelem > FULLTAB * tp->size) 2446ab05f83Stholo rehash(tp); 2456ab05f83Stholo h = hash(n, tp->size); 2466ab05f83Stholo p->cnext = tp->tab[h]; 2476ab05f83Stholo tp->tab[h] = p; 248115bd590Smillert DPRINTF("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n", 249115bd590Smillert (void*)p, p->nval, p->sval, p->fval, p->tval); 2506ab05f83Stholo return(p); 2516ab05f83Stholo } 2526ab05f83Stholo 2539a69093aSmillert int hash(const char *s, int n) /* form hash value for string s */ 2546ab05f83Stholo { 2556ab05f83Stholo unsigned hashval; 2566ab05f83Stholo 2576ab05f83Stholo for (hashval = 0; *s != '\0'; s++) 2586ab05f83Stholo hashval = (*s + 31 * hashval); 2596ab05f83Stholo return hashval % n; 2606ab05f83Stholo } 2616ab05f83Stholo 2626ab05f83Stholo void rehash(Array *tp) /* rehash items in small table into big one */ 2636ab05f83Stholo { 2646ab05f83Stholo int i, nh, nsz; 2656ab05f83Stholo Cell *cp, *op, **np; 2666ab05f83Stholo 2676ab05f83Stholo nsz = GROWTAB * tp->size; 268483fa115Smillert np = (Cell **) calloc(nsz, sizeof(*np)); 2696ab05f83Stholo if (np == NULL) /* can't do it, but can keep running. */ 2706ab05f83Stholo return; /* someone else will run out later. */ 2716ab05f83Stholo for (i = 0; i < tp->size; i++) { 2726ab05f83Stholo for (cp = tp->tab[i]; cp; cp = op) { 2736ab05f83Stholo op = cp->cnext; 2746ab05f83Stholo nh = hash(cp->nval, nsz); 2756ab05f83Stholo cp->cnext = np[nh]; 2766ab05f83Stholo np[nh] = cp; 2776ab05f83Stholo } 2786ab05f83Stholo } 27907edfa4aSkstailey free(tp->tab); 2806ab05f83Stholo tp->tab = np; 2816ab05f83Stholo tp->size = nsz; 2826ab05f83Stholo } 2836ab05f83Stholo 2849a69093aSmillert Cell *lookup(const char *s, Array *tp) /* look for s in tp */ 2856ab05f83Stholo { 286271018d0Smillert Cell *p; 2876ab05f83Stholo int h; 2886ab05f83Stholo 2896ab05f83Stholo h = hash(s, tp->size); 290271018d0Smillert for (p = tp->tab[h]; p != NULL; p = p->cnext) 29107edfa4aSkstailey if (strcmp(s, p->nval) == 0) 2926ab05f83Stholo return(p); /* found it */ 2936ab05f83Stholo return(NULL); /* not found */ 2946ab05f83Stholo } 2956ab05f83Stholo 2966ab05f83Stholo Awkfloat setfval(Cell *vp, Awkfloat f) /* set float val of a Cell */ 2976ab05f83Stholo { 29807edfa4aSkstailey int fldno; 29907edfa4aSkstailey 300c062391aSmillert f += 0.0; /* normalise negative zero to positive zero */ 3016ab05f83Stholo if ((vp->tval & (NUM | STR)) == 0) 3026ab05f83Stholo funnyvar(vp, "assign to"); 30307edfa4aSkstailey if (isfld(vp)) { 304f81b289fSmillert donerec = false; /* mark $0 invalid */ 30507edfa4aSkstailey fldno = atoi(vp->nval); 30607edfa4aSkstailey if (fldno > *NF) 30707edfa4aSkstailey newfld(fldno); 308115bd590Smillert DPRINTF("setting field %d to %g\n", fldno, f); 309c062391aSmillert } else if (&vp->fval == NF) { 310f81b289fSmillert donerec = false; /* mark $0 invalid */ 311c062391aSmillert setlastfld(f); 312115bd590Smillert DPRINTF("setting NF to %g\n", f); 31307edfa4aSkstailey } else if (isrec(vp)) { 314f81b289fSmillert donefld = false; /* mark $1... invalid */ 315f81b289fSmillert donerec = true; 316c0fa3611Smillert savefs(); 31702265e66Smillert } else if (vp == ofsloc) { 318f81b289fSmillert if (!donerec) 31902265e66Smillert recbld(); 3206ab05f83Stholo } 321a4fa8700Smillert if (freeable(vp)) 322a4fa8700Smillert xfree(vp->sval); /* free any previous string */ 323c062391aSmillert vp->tval &= ~(STR|CONVC|CONVO); /* mark string invalid */ 324c062391aSmillert vp->fmt = NULL; 3256ab05f83Stholo vp->tval |= NUM; /* mark number ok */ 326a8d6f668Smillert if (f == -0) /* who would have thought this possible? */ 327a8d6f668Smillert f = 0; 328115bd590Smillert DPRINTF("setfval %p: %s = %g, t=%o\n", (void*)vp, NN(vp->nval), f, vp->tval); 3296ab05f83Stholo return vp->fval = f; 3306ab05f83Stholo } 3316ab05f83Stholo 3329a69093aSmillert void funnyvar(Cell *vp, const char *rw) 3336ab05f83Stholo { 33407edfa4aSkstailey if (isarr(vp)) 3357b11b857Smillert FATAL("can't %s %s; it's an array name.", rw, vp->nval); 3366ab05f83Stholo if (vp->tval & FCN) 3377b11b857Smillert FATAL("can't %s %s; it's a function.", rw, vp->nval); 3387b11b857Smillert WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o", 3396685ce51Smillert (void *)vp, vp->nval, vp->sval, vp->fval, vp->tval); 3406ab05f83Stholo } 3416ab05f83Stholo 3429a69093aSmillert char *setsval(Cell *vp, const char *s) /* set string val of a Cell */ 3436ab05f83Stholo { 3446ab05f83Stholo char *t; 34507edfa4aSkstailey int fldno; 346c062391aSmillert Awkfloat f; 3476ab05f83Stholo 348115bd590Smillert DPRINTF("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n", 349115bd590Smillert (void*)vp, NN(vp->nval), s, vp->tval, donerec, donefld); 3506ab05f83Stholo if ((vp->tval & (NUM | STR)) == 0) 3516ab05f83Stholo funnyvar(vp, "assign to"); 35207edfa4aSkstailey if (isfld(vp)) { 353f81b289fSmillert donerec = false; /* mark $0 invalid */ 35407edfa4aSkstailey fldno = atoi(vp->nval); 35507edfa4aSkstailey if (fldno > *NF) 35607edfa4aSkstailey newfld(fldno); 35760613915Smillert DPRINTF("setting field %d to %s (%p)\n", fldno, s, (const void*)s); 35807edfa4aSkstailey } else if (isrec(vp)) { 359f81b289fSmillert donefld = false; /* mark $1... invalid */ 360f81b289fSmillert donerec = true; 361c0fa3611Smillert savefs(); 36202265e66Smillert } else if (vp == ofsloc) { 363f81b289fSmillert if (!donerec) 364c062391aSmillert recbld(); 3656ab05f83Stholo } 366c062391aSmillert t = s ? tostring(s) : tostring(""); /* in case it's self-assign */ 3676ab05f83Stholo if (freeable(vp)) 3686ab05f83Stholo xfree(vp->sval); 369a06fd656Smillert vp->tval &= ~(NUM|DONTFREE|CONVC|CONVO); 3704eb91dacSjmc vp->tval |= STR; 371c062391aSmillert vp->fmt = NULL; 372115bd590Smillert DPRINTF("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n", 37360613915Smillert (void*)vp, NN(vp->nval), t, (void*)t, vp->tval, donerec, donefld); 374c062391aSmillert vp->sval = t; 375c062391aSmillert if (&vp->fval == NF) { 376f81b289fSmillert donerec = false; /* mark $0 invalid */ 377c062391aSmillert f = getfval(vp); 378c062391aSmillert setlastfld(f); 379115bd590Smillert DPRINTF("setting NF to %g\n", f); 380c062391aSmillert } 381c062391aSmillert 382c062391aSmillert return(vp->sval); 3836ab05f83Stholo } 3846ab05f83Stholo 3856ab05f83Stholo Awkfloat getfval(Cell *vp) /* get float val of a Cell */ 3866ab05f83Stholo { 3876ab05f83Stholo if ((vp->tval & (NUM | STR)) == 0) 3886ab05f83Stholo funnyvar(vp, "read value of"); 389f81b289fSmillert if (isfld(vp) && !donefld) 3906ab05f83Stholo fldbld(); 391f81b289fSmillert else if (isrec(vp) && !donerec) 3926ab05f83Stholo recbld(); 3936ab05f83Stholo if (!isnum(vp)) { /* not a number */ 394483fa115Smillert double fval; 395483fa115Smillert bool no_trailing; 396483fa115Smillert 397483fa115Smillert if (is_valid_number(vp->sval, true, & no_trailing, & fval)) { 398483fa115Smillert vp->fval = fval; 399483fa115Smillert if (no_trailing && !(vp->tval&CON)) 4006ab05f83Stholo vp->tval |= NUM; /* make NUM only sparingly */ 401483fa115Smillert } else 402483fa115Smillert vp->fval = 0.0; 4036ab05f83Stholo } 404115bd590Smillert DPRINTF("getfval %p: %s = %g, t=%o\n", 405115bd590Smillert (void*)vp, NN(vp->nval), vp->fval, vp->tval); 4066ab05f83Stholo return(vp->fval); 4076ab05f83Stholo } 4086ab05f83Stholo 409f9297e05Smillert static const char *get_inf_nan(double d) 4106de80fb8Smillert { 4116de80fb8Smillert if (isinf(d)) { 4126de80fb8Smillert return (d < 0 ? "-inf" : "+inf"); 4136de80fb8Smillert } else if (isnan(d)) { 4146de80fb8Smillert return (signbit(d) != 0 ? "-nan" : "+nan"); 4156de80fb8Smillert } else 4166de80fb8Smillert return NULL; 4176de80fb8Smillert } 4186de80fb8Smillert 4199a69093aSmillert static char *get_str_val(Cell *vp, char **fmt) /* get string val of a Cell */ 4206ab05f83Stholo { 421a4a48c73Smillert int n; 4226ab05f83Stholo double dtemp; 423f9297e05Smillert const char *p; 4246ab05f83Stholo 4256ab05f83Stholo if ((vp->tval & (NUM | STR)) == 0) 4266ab05f83Stholo funnyvar(vp, "read value of"); 427f81b289fSmillert if (isfld(vp) && ! donefld) 4286ab05f83Stholo fldbld(); 429f81b289fSmillert else if (isrec(vp) && ! donerec) 4306ab05f83Stholo recbld(); 431c062391aSmillert 432c062391aSmillert /* 433c062391aSmillert * ADR: This is complicated and more fragile than is desirable. 434c062391aSmillert * Retrieving a string value for a number associates the string 435c062391aSmillert * value with the scalar. Previously, the string value was 436c062391aSmillert * sticky, meaning if converted via OFMT that became the value 437c062391aSmillert * (even though POSIX wants it to be via CONVFMT). Or if CONVFMT 438c062391aSmillert * changed after a string value was retrieved, the original value 439c062391aSmillert * was maintained and used. Also not per POSIX. 440c062391aSmillert * 441c062391aSmillert * We work around this design by adding two additional flags, 442c062391aSmillert * CONVC and CONVO, indicating how the string value was 443c062391aSmillert * obtained (via CONVFMT or OFMT) and _also_ maintaining a copy 444c062391aSmillert * of the pointer to the xFMT format string used for the 445c062391aSmillert * conversion. This pointer is only read, **never** dereferenced. 446c062391aSmillert * The next time we do a conversion, if it's coming from the same 447c062391aSmillert * xFMT as last time, and the pointer value is different, we 448c062391aSmillert * know that the xFMT format string changed, and we need to 449c062391aSmillert * redo the conversion. If it's the same, we don't have to. 450c062391aSmillert * 451c062391aSmillert * There are also several cases where we don't do a conversion, 452c062391aSmillert * such as for a field (see the checks below). 453c062391aSmillert */ 454c062391aSmillert 455c062391aSmillert /* Don't duplicate the code for actually updating the value */ 456c062391aSmillert #define update_str_val(vp) \ 457c062391aSmillert { \ 458c062391aSmillert if (freeable(vp)) \ 459c062391aSmillert xfree(vp->sval); \ 4606de80fb8Smillert if ((p = get_inf_nan(vp->fval)) != NULL) \ 4616de80fb8Smillert n = (vp->sval = strdup(p)) ? 0 : -1; \ 4626de80fb8Smillert else if (modf(vp->fval, &dtemp) == 0) /* it's integral */ \ 463c062391aSmillert n = asprintf(&vp->sval, "%.30g", vp->fval); \ 464c062391aSmillert else \ 465c062391aSmillert n = asprintf(&vp->sval, *fmt, vp->fval); \ 466c062391aSmillert if (n == -1) \ 467c062391aSmillert FATAL("out of space in get_str_val"); \ 468c062391aSmillert vp->tval &= ~DONTFREE; \ 469c062391aSmillert vp->tval |= STR; \ 4706ab05f83Stholo } 471c062391aSmillert 472c062391aSmillert if (isstr(vp) == 0) { 473c062391aSmillert update_str_val(vp); 474c062391aSmillert if (fmt == OFMT) { 475c062391aSmillert vp->tval &= ~CONVC; 476c062391aSmillert vp->tval |= CONVO; 477c062391aSmillert } else { 478c062391aSmillert /* CONVFMT */ 479c062391aSmillert vp->tval &= ~CONVO; 480c062391aSmillert vp->tval |= CONVC; 481c062391aSmillert } 482c062391aSmillert vp->fmt = *fmt; 483c062391aSmillert } else if ((vp->tval & DONTFREE) != 0 || ! isnum(vp) || isfld(vp)) { 484c062391aSmillert goto done; 485c062391aSmillert } else if (isstr(vp)) { 486c062391aSmillert if (fmt == OFMT) { 487c062391aSmillert if ((vp->tval & CONVC) != 0 488c062391aSmillert || ((vp->tval & CONVO) != 0 && vp->fmt != *fmt)) { 489c062391aSmillert update_str_val(vp); 490c062391aSmillert vp->tval &= ~CONVC; 491c062391aSmillert vp->tval |= CONVO; 492c062391aSmillert vp->fmt = *fmt; 493c062391aSmillert } 494c062391aSmillert } else { 495c062391aSmillert /* CONVFMT */ 496c062391aSmillert if ((vp->tval & CONVO) != 0 497c062391aSmillert || ((vp->tval & CONVC) != 0 && vp->fmt != *fmt)) { 498c062391aSmillert update_str_val(vp); 499c062391aSmillert vp->tval &= ~CONVO; 500c062391aSmillert vp->tval |= CONVC; 501c062391aSmillert vp->fmt = *fmt; 502c062391aSmillert } 503c062391aSmillert } 504c062391aSmillert } 505c062391aSmillert done: 506115bd590Smillert DPRINTF("getsval %p: %s = \"%s (%p)\", t=%o\n", 50760613915Smillert (void*)vp, NN(vp->nval), vp->sval, (void*)vp->sval, vp->tval); 5086ab05f83Stholo return(vp->sval); 5096ab05f83Stholo } 5106ab05f83Stholo 5119a69093aSmillert char *getsval(Cell *vp) /* get string val of a Cell */ 5129a69093aSmillert { 5139a69093aSmillert return get_str_val(vp, CONVFMT); 5149a69093aSmillert } 5159a69093aSmillert 5169a69093aSmillert char *getpssval(Cell *vp) /* get string val of a Cell for print */ 5179a69093aSmillert { 5189a69093aSmillert return get_str_val(vp, OFMT); 5199a69093aSmillert } 5209a69093aSmillert 5219a69093aSmillert 5229a69093aSmillert char *tostring(const char *s) /* make a copy of string s */ 5236ab05f83Stholo { 524d7cce239Smillert char *p = strdup(s); 5258055ea94Smillert if (p == NULL) 5268055ea94Smillert FATAL("out of space in tostring on %s", s); 527d7cce239Smillert return(p); 5286ab05f83Stholo } 5296ab05f83Stholo 5306685ce51Smillert char *tostringN(const char *s, size_t n) /* make a copy of string s */ 5316685ce51Smillert { 5326685ce51Smillert char *p; 5336685ce51Smillert 534483fa115Smillert p = (char *) malloc(n); 5356685ce51Smillert if (p == NULL) 536144915fcSmillert FATAL("out of space in tostringN %zu", n); 537144915fcSmillert if (strlcpy(p, s, n) >= n) 538144915fcSmillert FATAL("out of space in tostringN on %s", s); 5396685ce51Smillert return(p); 5406685ce51Smillert } 5416685ce51Smillert 542c0fa3611Smillert Cell *catstr(Cell *a, Cell *b) /* concatenate a and b */ 543c0fa3611Smillert { 544c0fa3611Smillert Cell *c; 545c0fa3611Smillert char *p; 546c0fa3611Smillert char *sa = getsval(a); 547c0fa3611Smillert char *sb = getsval(b); 548c0fa3611Smillert size_t l = strlen(sa) + strlen(sb) + 1; 549483fa115Smillert p = (char *) malloc(l); 550c0fa3611Smillert if (p == NULL) 551c0fa3611Smillert FATAL("out of space concatenating %s and %s", sa, sb); 552c0fa3611Smillert snprintf(p, l, "%s%s", sa, sb); 553fabd211eSmillert 554fabd211eSmillert l++; // add room for ' ' 555483fa115Smillert char *newbuf = (char *) malloc(l); 55692497129Smillert if (newbuf == NULL) 55792497129Smillert FATAL("out of space concatenating %s and %s", sa, sb); 55892497129Smillert // See string() in lex.c; a string "xx" is stored in the symbol 55992497129Smillert // table as "xx ". 560fabd211eSmillert snprintf(newbuf, l, "%s ", p); 56192497129Smillert c = setsymtab(newbuf, p, 0.0, CON|STR|DONTFREE, symtab); 562c0fa3611Smillert free(p); 56392497129Smillert free(newbuf); 564c0fa3611Smillert return c; 565c0fa3611Smillert } 566c0fa3611Smillert 5679a69093aSmillert char *qstring(const char *is, int delim) /* collect string up to next delim */ 5686ab05f83Stholo { 5696ab05f83Stholo int c, n; 570d7cce239Smillert const uschar *s = (const uschar *) is; 571a27f5228Smillert uschar *buf, *bp; 5726ab05f83Stholo 573483fa115Smillert if ((buf = (uschar *) malloc(strlen(is)+3)) == NULL) 5747b11b857Smillert FATAL( "out of space in qstring(%s)", s); 57507edfa4aSkstailey for (bp = buf; (c = *s) != delim; s++) { 5766ab05f83Stholo if (c == '\n') 577*f190fa91Smillert SYNTAX( "newline in string %.20s...", is ); 5786ab05f83Stholo else if (c != '\\') 57907edfa4aSkstailey *bp++ = c; 58007edfa4aSkstailey else { /* \something */ 5817b11b857Smillert c = *++s; 5827b11b857Smillert if (c == 0) { /* \ at end */ 5837b11b857Smillert *bp++ = '\\'; 5847b11b857Smillert break; /* for loop */ 5857b11b857Smillert } 5867b11b857Smillert switch (c) { 58707edfa4aSkstailey case '\\': *bp++ = '\\'; break; 58807edfa4aSkstailey case 'n': *bp++ = '\n'; break; 58907edfa4aSkstailey case 't': *bp++ = '\t'; break; 59007edfa4aSkstailey case 'b': *bp++ = '\b'; break; 59107edfa4aSkstailey case 'f': *bp++ = '\f'; break; 59207edfa4aSkstailey case 'r': *bp++ = '\r'; break; 593c0fa3611Smillert case 'v': *bp++ = '\v'; break; 594c0fa3611Smillert case 'a': *bp++ = '\a'; break; 5956ab05f83Stholo default: 5966ab05f83Stholo if (!isdigit(c)) { 59707edfa4aSkstailey *bp++ = c; 5986ab05f83Stholo break; 5996ab05f83Stholo } 6006ab05f83Stholo n = c - '0'; 6016ab05f83Stholo if (isdigit(s[1])) { 6026ab05f83Stholo n = 8 * n + *++s - '0'; 6036ab05f83Stholo if (isdigit(s[1])) 6046ab05f83Stholo n = 8 * n + *++s - '0'; 6056ab05f83Stholo } 60607edfa4aSkstailey *bp++ = n; 6076ab05f83Stholo break; 6086ab05f83Stholo } 6096ab05f83Stholo } 61007edfa4aSkstailey } 61107edfa4aSkstailey *bp++ = 0; 612a27f5228Smillert return (char *) buf; 6136ab05f83Stholo } 614c062391aSmillert 615c062391aSmillert const char *flags2str(int flags) 616c062391aSmillert { 617c062391aSmillert static const struct ftab { 618c062391aSmillert const char *name; 619c062391aSmillert int value; 620c062391aSmillert } flagtab[] = { 621c062391aSmillert { "NUM", NUM }, 622c062391aSmillert { "STR", STR }, 623c062391aSmillert { "DONTFREE", DONTFREE }, 624c062391aSmillert { "CON", CON }, 625c062391aSmillert { "ARR", ARR }, 626c062391aSmillert { "FCN", FCN }, 627c062391aSmillert { "FLD", FLD }, 628c062391aSmillert { "REC", REC }, 629c062391aSmillert { "CONVC", CONVC }, 630c062391aSmillert { "CONVO", CONVO }, 631c062391aSmillert { NULL, 0 } 632c062391aSmillert }; 633c062391aSmillert static char buf[100]; 634c062391aSmillert int i, len; 635c062391aSmillert char *cp = buf; 636c062391aSmillert 637c062391aSmillert for (i = 0; flagtab[i].name != NULL; i++) { 638c062391aSmillert if ((flags & flagtab[i].value) != 0) { 639c062391aSmillert len = snprintf(cp, sizeof(buf) - (cp - buf), 640c062391aSmillert "%s%s", cp > buf ? "|" : "", flagtab[i].name); 641c062391aSmillert if (len < 0 || len >= sizeof(buf) - (cp - buf)) 642c062391aSmillert FATAL("out of space in flags2str"); 643c062391aSmillert cp += len; 644c062391aSmillert } 645c062391aSmillert } 646c062391aSmillert 647c062391aSmillert return buf; 648c062391aSmillert } 649