Annotation of src/usr.bin/awk/tran.c, Revision 1.29
1.29 ! millert 1: /* $OpenBSD: tran.c,v 1.28 2020/06/16 16:14:22 millert Exp $ */
1.1 tholo 2: /****************************************************************
1.3 kstailey 3: Copyright (C) Lucent Technologies 1997
1.1 tholo 4: All Rights Reserved
5:
6: Permission to use, copy, modify, and distribute this software and
7: its documentation for any purpose and without fee is hereby
8: granted, provided that the above copyright notice appear in all
9: copies and that both that the copyright notice and this
10: permission notice and warranty disclaimer appear in supporting
1.3 kstailey 11: documentation, and that the name Lucent Technologies or any of
12: its entities not be used in advertising or publicity pertaining
13: to distribution of the software without specific, written prior
14: permission.
15:
16: LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17: INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
18: IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
19: SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20: WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
21: IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
22: ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
23: THIS SOFTWARE.
1.1 tholo 24: ****************************************************************/
25:
26: #define DEBUG
27: #include <stdio.h>
28: #include <math.h>
29: #include <ctype.h>
30: #include <string.h>
31: #include <stdlib.h>
32: #include "awk.h"
1.3 kstailey 33: #include "ytab.h"
1.1 tholo 34:
35: #define FULLTAB 2 /* rehash when table gets this x full */
36: #define GROWTAB 4 /* grow table by this factor */
37:
38: Array *symtab; /* main symbol table */
39:
40: char **FS; /* initial field sep */
41: char **RS; /* initial record sep */
42: char **OFS; /* output field sep */
43: char **ORS; /* output record sep */
44: char **OFMT; /* output format for numbers */
45: char **CONVFMT; /* format for conversions in getsval */
46: Awkfloat *NF; /* number of fields in current record */
47: Awkfloat *NR; /* number of current record */
48: Awkfloat *FNR; /* number of current record in current file */
49: char **FILENAME; /* current filename argument */
50: Awkfloat *ARGC; /* number of arguments from command line */
51: char **SUBSEP; /* subscript separator for a[i,j,k]; default \034 */
52: Awkfloat *RSTART; /* start of re matched with ~; origin 1 (!) */
53: Awkfloat *RLENGTH; /* length of same */
54:
1.11 millert 55: Cell *fsloc; /* FS */
1.1 tholo 56: Cell *nrloc; /* NR */
57: Cell *nfloc; /* NF */
58: Cell *fnrloc; /* FNR */
1.21 millert 59: Cell *ofsloc; /* OFS */
60: Cell *orsloc; /* ORS */
61: Cell *rsloc; /* RS */
1.1 tholo 62: Array *ARGVtab; /* symbol table containing ARGV[...] */
63: Array *ENVtab; /* symbol table containing ENVIRON[...] */
64: Cell *rstartloc; /* RSTART */
65: Cell *rlengthloc; /* RLENGTH */
1.21 millert 66: Cell *subseploc; /* SUBSEP */
1.1 tholo 67: Cell *symtabloc; /* SYMTAB */
68:
69: Cell *nullloc; /* a guaranteed empty cell */
70: Node *nullnode; /* zero&null, converted into a node for comparisons */
1.3 kstailey 71: Cell *literal0;
1.1 tholo 72:
1.3 kstailey 73: extern Cell **fldtab;
1.1 tholo 74:
1.19 millert 75: static void
76: setfree(Cell *vp)
77: {
78: if (&vp->sval == FS || &vp->sval == RS ||
79: &vp->sval == OFS || &vp->sval == ORS ||
80: &vp->sval == OFMT || &vp->sval == CONVFMT ||
81: &vp->sval == FILENAME || &vp->sval == SUBSEP)
82: vp->tval |= DONTFREE;
83: else
84: vp->tval &= ~DONTFREE;
85: }
86:
1.1 tholo 87: void syminit(void) /* initialize symbol table with builtin vars */
88: {
1.3 kstailey 89: literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab);
1.1 tholo 90: /* this is used for if(x)... tests: */
91: nullloc = setsymtab("$zero&null", "", 0.0, NUM|STR|CON|DONTFREE, symtab);
1.3 kstailey 92: nullnode = celltonode(nullloc, CCON);
1.1 tholo 93:
1.11 millert 94: fsloc = setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab);
95: FS = &fsloc->sval;
1.21 millert 96: rsloc = setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab);
97: RS = &rsloc->sval;
98: ofsloc = setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab);
99: OFS = &ofsloc->sval;
100: orsloc = setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab);
101: ORS = &orsloc->sval;
1.1 tholo 102: OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
103: CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
1.2 millert 104: FILENAME = &setsymtab("FILENAME", "", 0.0, STR|DONTFREE, symtab)->sval;
1.1 tholo 105: nfloc = setsymtab("NF", "", 0.0, NUM, symtab);
106: NF = &nfloc->fval;
107: nrloc = setsymtab("NR", "", 0.0, NUM, symtab);
108: NR = &nrloc->fval;
109: fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab);
110: FNR = &fnrloc->fval;
1.21 millert 111: subseploc = setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab);
112: SUBSEP = &subseploc->sval;
1.1 tholo 113: rstartloc = setsymtab("RSTART", "", 0.0, NUM, symtab);
114: RSTART = &rstartloc->fval;
115: rlengthloc = setsymtab("RLENGTH", "", 0.0, NUM, symtab);
116: RLENGTH = &rlengthloc->fval;
117: symtabloc = setsymtab("SYMTAB", "", 0.0, ARR, symtab);
1.25 millert 118: free(symtabloc->sval);
1.1 tholo 119: symtabloc->sval = (char *) symtab;
120: }
121:
1.3 kstailey 122: void arginit(int ac, char **av) /* set up ARGV and ARGC */
1.1 tholo 123: {
124: Cell *cp;
125: int i;
1.3 kstailey 126: char temp[50];
1.1 tholo 127:
128: ARGC = &setsymtab("ARGC", "", (Awkfloat) ac, NUM, symtab)->fval;
129: cp = setsymtab("ARGV", "", 0.0, ARR, symtab);
130: ARGVtab = makesymtab(NSYMTAB); /* could be (int) ARGC as well */
1.25 millert 131: free(cp->sval);
1.1 tholo 132: cp->sval = (char *) ARGVtab;
133: for (i = 0; i < ac; i++) {
1.9 deraadt 134: snprintf(temp, sizeof temp, "%d", i);
1.4 millert 135: if (is_number(*av))
1.1 tholo 136: setsymtab(temp, *av, atof(*av), STR|NUM, ARGVtab);
137: else
138: setsymtab(temp, *av, 0.0, STR, ARGVtab);
139: av++;
140: }
141: }
142:
143: void envinit(char **envp) /* set up ENVIRON variable */
144: {
145: Cell *cp;
146: char *p;
147:
148: cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab);
149: ENVtab = makesymtab(NSYMTAB);
1.25 millert 150: free(cp->sval);
1.1 tholo 151: cp->sval = (char *) ENVtab;
152: for ( ; *envp; envp++) {
1.3 kstailey 153: if ((p = strchr(*envp, '=')) == NULL)
1.1 tholo 154: continue;
1.7 millert 155: if( p == *envp ) /* no left hand side name in env string */
156: continue;
1.1 tholo 157: *p++ = 0; /* split into two strings at = */
1.4 millert 158: if (is_number(p))
1.1 tholo 159: setsymtab(*envp, p, atof(p), STR|NUM, ENVtab);
160: else
161: setsymtab(*envp, p, 0.0, STR, ENVtab);
162: p[-1] = '='; /* restore in case env is passed down to a shell */
163: }
164: }
165:
166: Array *makesymtab(int n) /* make a new symbol table */
167: {
168: Array *ap;
169: Cell **tp;
170:
1.23 millert 171: ap = malloc(sizeof(*ap));
172: tp = calloc(n, sizeof(*tp));
1.1 tholo 173: if (ap == NULL || tp == NULL)
1.6 millert 174: FATAL("out of space in makesymtab");
1.1 tholo 175: ap->nelem = 0;
176: ap->size = n;
177: ap->tab = tp;
178: return(ap);
179: }
180:
181: void freesymtab(Cell *ap) /* free a symbol table */
182: {
183: Cell *cp, *temp;
184: Array *tp;
185: int i;
186:
187: if (!isarr(ap))
188: return;
189: tp = (Array *) ap->sval;
190: if (tp == NULL)
191: return;
192: for (i = 0; i < tp->size; i++) {
193: for (cp = tp->tab[i]; cp != NULL; cp = temp) {
194: xfree(cp->nval);
195: if (freeable(cp))
196: xfree(cp->sval);
197: temp = cp->cnext; /* avoids freeing then using */
1.22 millert 198: free(cp);
1.8 millert 199: tp->nelem--;
1.1 tholo 200: }
1.22 millert 201: tp->tab[i] = NULL;
1.1 tholo 202: }
1.8 millert 203: if (tp->nelem != 0)
204: WARNING("can't happen: inconsistent element count freeing %s", ap->nval);
1.3 kstailey 205: free(tp->tab);
206: free(tp);
1.1 tholo 207: }
208:
1.8 millert 209: void freeelem(Cell *ap, const char *s) /* free elem s from ap (i.e., ap["s"] */
1.1 tholo 210: {
211: Array *tp;
212: Cell *p, *prev = NULL;
213: int h;
1.22 millert 214:
1.1 tholo 215: tp = (Array *) ap->sval;
216: h = hash(s, tp->size);
217: for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext)
1.3 kstailey 218: if (strcmp(s, p->nval) == 0) {
1.1 tholo 219: if (prev == NULL) /* 1st one */
220: tp->tab[h] = p->cnext;
221: else /* middle somewhere */
222: prev->cnext = p->cnext;
223: if (freeable(p))
224: xfree(p->sval);
225: free(p->nval);
1.3 kstailey 226: free(p);
1.1 tholo 227: tp->nelem--;
228: return;
229: }
230: }
231:
1.8 millert 232: Cell *setsymtab(const char *n, const char *s, Awkfloat f, unsigned t, Array *tp)
1.1 tholo 233: {
234: int h;
235: Cell *p;
236:
237: if (n != NULL && (p = lookup(n, tp)) != NULL) {
1.29 ! millert 238: DPRINTF("setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n",
! 239: (void*)p, NN(p->nval), NN(p->sval), p->fval, p->tval);
1.1 tholo 240: return(p);
241: }
1.23 millert 242: p = malloc(sizeof(*p));
1.1 tholo 243: if (p == NULL)
1.6 millert 244: FATAL("out of space for symbol table at %s", n);
1.1 tholo 245: p->nval = tostring(n);
246: p->sval = s ? tostring(s) : tostring("");
247: p->fval = f;
248: p->tval = t;
249: p->csub = CUNK;
250: p->ctype = OCELL;
251: tp->nelem++;
252: if (tp->nelem > FULLTAB * tp->size)
253: rehash(tp);
254: h = hash(n, tp->size);
255: p->cnext = tp->tab[h];
256: tp->tab[h] = p;
1.29 ! millert 257: DPRINTF("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n",
! 258: (void*)p, p->nval, p->sval, p->fval, p->tval);
1.1 tholo 259: return(p);
260: }
261:
1.8 millert 262: int hash(const char *s, int n) /* form hash value for string s */
1.1 tholo 263: {
264: unsigned hashval;
265:
266: for (hashval = 0; *s != '\0'; s++)
267: hashval = (*s + 31 * hashval);
268: return hashval % n;
269: }
270:
271: void rehash(Array *tp) /* rehash items in small table into big one */
272: {
273: int i, nh, nsz;
274: Cell *cp, *op, **np;
275:
276: nsz = GROWTAB * tp->size;
1.23 millert 277: np = calloc(nsz, sizeof(*np));
1.1 tholo 278: if (np == NULL) /* can't do it, but can keep running. */
279: return; /* someone else will run out later. */
280: for (i = 0; i < tp->size; i++) {
281: for (cp = tp->tab[i]; cp; cp = op) {
282: op = cp->cnext;
283: nh = hash(cp->nval, nsz);
284: cp->cnext = np[nh];
285: np[nh] = cp;
286: }
287: }
1.3 kstailey 288: free(tp->tab);
1.1 tholo 289: tp->tab = np;
290: tp->size = nsz;
291: }
292:
1.8 millert 293: Cell *lookup(const char *s, Array *tp) /* look for s in tp */
1.1 tholo 294: {
1.5 millert 295: Cell *p;
1.1 tholo 296: int h;
297:
298: h = hash(s, tp->size);
1.5 millert 299: for (p = tp->tab[h]; p != NULL; p = p->cnext)
1.3 kstailey 300: if (strcmp(s, p->nval) == 0)
1.1 tholo 301: return(p); /* found it */
302: return(NULL); /* not found */
303: }
304:
305: Awkfloat setfval(Cell *vp, Awkfloat f) /* set float val of a Cell */
306: {
1.3 kstailey 307: int fldno;
308:
1.19 millert 309: f += 0.0; /* normalise negative zero to positive zero */
1.22 millert 310: if ((vp->tval & (NUM | STR)) == 0)
1.1 tholo 311: funnyvar(vp, "assign to");
1.3 kstailey 312: if (isfld(vp)) {
1.24 millert 313: donerec = false; /* mark $0 invalid */
1.3 kstailey 314: fldno = atoi(vp->nval);
315: if (fldno > *NF)
316: newfld(fldno);
1.29 ! millert 317: DPRINTF("setting field %d to %g\n", fldno, f);
1.19 millert 318: } else if (&vp->fval == NF) {
1.24 millert 319: donerec = false; /* mark $0 invalid */
1.19 millert 320: setlastfld(f);
1.29 ! millert 321: DPRINTF("setting NF to %g\n", f);
1.3 kstailey 322: } else if (isrec(vp)) {
1.24 millert 323: donefld = false; /* mark $1... invalid */
324: donerec = true;
1.22 millert 325: savefs();
1.21 millert 326: } else if (vp == ofsloc) {
1.24 millert 327: if (!donerec)
1.21 millert 328: recbld();
1.1 tholo 329: }
1.4 millert 330: if (freeable(vp))
331: xfree(vp->sval); /* free any previous string */
1.19 millert 332: vp->tval &= ~(STR|CONVC|CONVO); /* mark string invalid */
333: vp->fmt = NULL;
1.1 tholo 334: vp->tval |= NUM; /* mark number ok */
1.18 millert 335: if (f == -0) /* who would have thought this possible? */
336: f = 0;
1.29 ! millert 337: DPRINTF("setfval %p: %s = %g, t=%o\n", (void*)vp, NN(vp->nval), f, vp->tval);
1.1 tholo 338: return vp->fval = f;
339: }
340:
1.8 millert 341: void funnyvar(Cell *vp, const char *rw)
1.1 tholo 342: {
1.3 kstailey 343: if (isarr(vp))
1.6 millert 344: FATAL("can't %s %s; it's an array name.", rw, vp->nval);
1.1 tholo 345: if (vp->tval & FCN)
1.6 millert 346: FATAL("can't %s %s; it's a function.", rw, vp->nval);
347: WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o",
1.27 millert 348: (void *)vp, vp->nval, vp->sval, vp->fval, vp->tval);
1.1 tholo 349: }
350:
1.8 millert 351: char *setsval(Cell *vp, const char *s) /* set string val of a Cell */
1.1 tholo 352: {
353: char *t;
1.3 kstailey 354: int fldno;
1.19 millert 355: Awkfloat f;
1.1 tholo 356:
1.29 ! millert 357: DPRINTF("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n",
! 358: (void*)vp, NN(vp->nval), s, vp->tval, donerec, donefld);
1.1 tholo 359: if ((vp->tval & (NUM | STR)) == 0)
360: funnyvar(vp, "assign to");
1.3 kstailey 361: if (isfld(vp)) {
1.24 millert 362: donerec = false; /* mark $0 invalid */
1.3 kstailey 363: fldno = atoi(vp->nval);
364: if (fldno > *NF)
365: newfld(fldno);
1.29 ! millert 366: DPRINTF("setting field %d to %s (%p)\n", fldno, s, s);
1.3 kstailey 367: } else if (isrec(vp)) {
1.24 millert 368: donefld = false; /* mark $1... invalid */
369: donerec = true;
1.22 millert 370: savefs();
1.21 millert 371: } else if (vp == ofsloc) {
1.24 millert 372: if (!donerec)
1.19 millert 373: recbld();
1.1 tholo 374: }
1.19 millert 375: t = s ? tostring(s) : tostring(""); /* in case it's self-assign */
1.14 jmc 376: if (freeable(vp))
377: xfree(vp->sval);
1.19 millert 378: vp->tval &= ~(NUM|CONVC|CONVO);
1.1 tholo 379: vp->tval |= STR;
1.19 millert 380: vp->fmt = NULL;
381: setfree(vp);
1.29 ! millert 382: DPRINTF("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n",
! 383: (void*)vp, NN(vp->nval), t, t, vp->tval, donerec, donefld);
1.19 millert 384: vp->sval = t;
385: if (&vp->fval == NF) {
1.24 millert 386: donerec = false; /* mark $0 invalid */
1.19 millert 387: f = getfval(vp);
388: setlastfld(f);
1.29 ! millert 389: DPRINTF("setting NF to %g\n", f);
1.19 millert 390: }
391:
392: return(vp->sval);
1.1 tholo 393: }
394:
395: Awkfloat getfval(Cell *vp) /* get float val of a Cell */
396: {
397: if ((vp->tval & (NUM | STR)) == 0)
398: funnyvar(vp, "read value of");
1.24 millert 399: if (isfld(vp) && !donefld)
1.1 tholo 400: fldbld();
1.24 millert 401: else if (isrec(vp) && !donerec)
1.1 tholo 402: recbld();
403: if (!isnum(vp)) { /* not a number */
404: vp->fval = atof(vp->sval); /* best guess */
1.4 millert 405: if (is_number(vp->sval) && !(vp->tval&CON))
1.1 tholo 406: vp->tval |= NUM; /* make NUM only sparingly */
407: }
1.29 ! millert 408: DPRINTF("getfval %p: %s = %g, t=%o\n",
! 409: (void*)vp, NN(vp->nval), vp->fval, vp->tval);
1.1 tholo 410: return(vp->fval);
411: }
412:
1.11 millert 413: static char *get_str_val(Cell *vp, char **fmt) /* get string val of a Cell */
1.1 tholo 414: {
1.13 millert 415: int n;
1.1 tholo 416: double dtemp;
417:
418: if ((vp->tval & (NUM | STR)) == 0)
419: funnyvar(vp, "read value of");
1.24 millert 420: if (isfld(vp) && ! donefld)
1.1 tholo 421: fldbld();
1.24 millert 422: else if (isrec(vp) && ! donerec)
1.1 tholo 423: recbld();
1.19 millert 424:
425: /*
426: * ADR: This is complicated and more fragile than is desirable.
427: * Retrieving a string value for a number associates the string
428: * value with the scalar. Previously, the string value was
429: * sticky, meaning if converted via OFMT that became the value
430: * (even though POSIX wants it to be via CONVFMT). Or if CONVFMT
431: * changed after a string value was retrieved, the original value
432: * was maintained and used. Also not per POSIX.
433: *
434: * We work around this design by adding two additional flags,
435: * CONVC and CONVO, indicating how the string value was
436: * obtained (via CONVFMT or OFMT) and _also_ maintaining a copy
437: * of the pointer to the xFMT format string used for the
438: * conversion. This pointer is only read, **never** dereferenced.
439: * The next time we do a conversion, if it's coming from the same
440: * xFMT as last time, and the pointer value is different, we
441: * know that the xFMT format string changed, and we need to
442: * redo the conversion. If it's the same, we don't have to.
443: *
444: * There are also several cases where we don't do a conversion,
445: * such as for a field (see the checks below).
446: */
447:
448: /* Don't duplicate the code for actually updating the value */
449: #define update_str_val(vp) \
450: { \
451: if (freeable(vp)) \
452: xfree(vp->sval); \
453: if (modf(vp->fval, &dtemp) == 0) /* it's integral */ \
454: n = asprintf(&vp->sval, "%.30g", vp->fval); \
455: else \
456: n = asprintf(&vp->sval, *fmt, vp->fval); \
457: if (n == -1) \
458: FATAL("out of space in get_str_val"); \
459: vp->tval &= ~DONTFREE; \
460: vp->tval |= STR; \
461: }
462:
1.3 kstailey 463: if (isstr(vp) == 0) {
1.19 millert 464: update_str_val(vp);
465: if (fmt == OFMT) {
466: vp->tval &= ~CONVC;
467: vp->tval |= CONVO;
468: } else {
469: /* CONVFMT */
470: vp->tval &= ~CONVO;
471: vp->tval |= CONVC;
472: }
473: vp->fmt = *fmt;
474: } else if ((vp->tval & DONTFREE) != 0 || ! isnum(vp) || isfld(vp)) {
475: goto done;
476: } else if (isstr(vp)) {
477: if (fmt == OFMT) {
478: if ((vp->tval & CONVC) != 0
479: || ((vp->tval & CONVO) != 0 && vp->fmt != *fmt)) {
480: update_str_val(vp);
481: vp->tval &= ~CONVC;
482: vp->tval |= CONVO;
483: vp->fmt = *fmt;
484: }
485: } else {
486: /* CONVFMT */
487: if ((vp->tval & CONVO) != 0
488: || ((vp->tval & CONVC) != 0 && vp->fmt != *fmt)) {
489: update_str_val(vp);
490: vp->tval &= ~CONVO;
491: vp->tval |= CONVC;
492: vp->fmt = *fmt;
493: }
494: }
1.1 tholo 495: }
1.19 millert 496: done:
1.29 ! millert 497: DPRINTF("getsval %p: %s = \"%s (%p)\", t=%o\n",
! 498: (void*)vp, NN(vp->nval), vp->sval, vp->sval, vp->tval);
1.1 tholo 499: return(vp->sval);
500: }
501:
1.8 millert 502: char *getsval(Cell *vp) /* get string val of a Cell */
503: {
504: return get_str_val(vp, CONVFMT);
505: }
506:
507: char *getpssval(Cell *vp) /* get string val of a Cell for print */
508: {
509: return get_str_val(vp, OFMT);
510: }
511:
512:
513: char *tostring(const char *s) /* make a copy of string s */
1.1 tholo 514: {
1.23 millert 515: char *p = strdup(s);
1.12 millert 516: if (p == NULL)
517: FATAL("out of space in tostring on %s", s);
1.27 millert 518: return(p);
519: }
520:
521: char *tostringN(const char *s, size_t n) /* make a copy of string s */
522: {
523: char *p;
524:
525: p = malloc(n);
526: if (p == NULL)
1.28 millert 527: FATAL("out of space in tostringN %zu", n);
528: if (strlcpy(p, s, n) >= n)
529: FATAL("out of space in tostringN on %s", s);
1.23 millert 530: return(p);
1.1 tholo 531: }
532:
1.22 millert 533: Cell *catstr(Cell *a, Cell *b) /* concatenate a and b */
534: {
535: Cell *c;
536: char *p;
537: char *sa = getsval(a);
538: char *sb = getsval(b);
539: size_t l = strlen(sa) + strlen(sb) + 1;
540: p = malloc(l);
541: if (p == NULL)
542: FATAL("out of space concatenating %s and %s", sa, sb);
543: snprintf(p, l, "%s%s", sa, sb);
1.26 millert 544:
545: l++; // add room for ' '
546: char *newbuf = malloc(l);
1.25 millert 547: if (newbuf == NULL)
548: FATAL("out of space concatenating %s and %s", sa, sb);
549: // See string() in lex.c; a string "xx" is stored in the symbol
550: // table as "xx ".
1.26 millert 551: snprintf(newbuf, l, "%s ", p);
1.25 millert 552: c = setsymtab(newbuf, p, 0.0, CON|STR|DONTFREE, symtab);
1.22 millert 553: free(p);
1.25 millert 554: free(newbuf);
1.22 millert 555: return c;
556: }
557:
1.8 millert 558: char *qstring(const char *is, int delim) /* collect string up to next delim */
1.1 tholo 559: {
1.8 millert 560: const char *os = is;
1.1 tholo 561: int c, n;
1.23 millert 562: const uschar *s = (const uschar *) is;
1.7 millert 563: uschar *buf, *bp;
1.1 tholo 564:
1.23 millert 565: if ((buf = malloc(strlen(is)+3)) == NULL)
1.6 millert 566: FATAL( "out of space in qstring(%s)", s);
1.3 kstailey 567: for (bp = buf; (c = *s) != delim; s++) {
1.1 tholo 568: if (c == '\n')
1.6 millert 569: SYNTAX( "newline in string %.20s...", os );
1.1 tholo 570: else if (c != '\\')
1.3 kstailey 571: *bp++ = c;
1.6 millert 572: else { /* \something */
573: c = *++s;
574: if (c == 0) { /* \ at end */
575: *bp++ = '\\';
576: break; /* for loop */
1.22 millert 577: }
1.6 millert 578: switch (c) {
1.3 kstailey 579: case '\\': *bp++ = '\\'; break;
580: case 'n': *bp++ = '\n'; break;
581: case 't': *bp++ = '\t'; break;
582: case 'b': *bp++ = '\b'; break;
583: case 'f': *bp++ = '\f'; break;
584: case 'r': *bp++ = '\r'; break;
1.22 millert 585: case 'v': *bp++ = '\v'; break;
586: case 'a': *bp++ = '\a'; break;
1.1 tholo 587: default:
588: if (!isdigit(c)) {
1.3 kstailey 589: *bp++ = c;
1.1 tholo 590: break;
591: }
592: n = c - '0';
593: if (isdigit(s[1])) {
594: n = 8 * n + *++s - '0';
595: if (isdigit(s[1]))
596: n = 8 * n + *++s - '0';
597: }
1.3 kstailey 598: *bp++ = n;
1.1 tholo 599: break;
600: }
1.3 kstailey 601: }
1.1 tholo 602: }
1.3 kstailey 603: *bp++ = 0;
1.7 millert 604: return (char *) buf;
1.19 millert 605: }
606:
607: const char *flags2str(int flags)
608: {
609: static const struct ftab {
610: const char *name;
611: int value;
612: } flagtab[] = {
613: { "NUM", NUM },
614: { "STR", STR },
615: { "DONTFREE", DONTFREE },
616: { "CON", CON },
617: { "ARR", ARR },
618: { "FCN", FCN },
619: { "FLD", FLD },
620: { "REC", REC },
621: { "CONVC", CONVC },
622: { "CONVO", CONVO },
623: { NULL, 0 }
624: };
625: static char buf[100];
626: int i, len;
627: char *cp = buf;
628:
629: for (i = 0; flagtab[i].name != NULL; i++) {
630: if ((flags & flagtab[i].value) != 0) {
631: len = snprintf(cp, sizeof(buf) - (cp - buf),
632: "%s%s", cp > buf ? "|" : "", flagtab[i].name);
633: if (len < 0 || len >= sizeof(buf) - (cp - buf))
634: FATAL("out of space in flags2str");
635: cp += len;
636: }
637: }
638:
639: return buf;
1.1 tholo 640: }