blob: 0ce45db1cf8ad6df57b8f8f2bb9b049200909bdf [file] [log] [blame]
Brian Kernighan87b94932012-12-22 10:35:39 -05001/****************************************************************
2Copyright (C) Lucent Technologies 1997
3All Rights Reserved
4
5Permission to use, copy, modify, and distribute this software and
6its documentation for any purpose and without fee is hereby
7granted, provided that the above copyright notice appear in all
8copies and that both that the copyright notice and this
9permission notice and warranty disclaimer appear in supporting
10documentation, and that the name Lucent Technologies or any of
11its entities not be used in advertising or publicity pertaining
12to distribution of the software without specific, written prior
13permission.
14
15LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
17IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
18SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
20IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
21ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
22THIS SOFTWARE.
23****************************************************************/
24
25#define DEBUG
26#include <stdio.h>
27#include <math.h>
28#include <ctype.h>
29#include <string.h>
30#include <stdlib.h>
31#include "awk.h"
Brian Kernighan87b94932012-12-22 10:35:39 -050032
33#define FULLTAB 2 /* rehash when table gets this x full */
34#define GROWTAB 4 /* grow table by this factor */
35
36Array *symtab; /* main symbol table */
37
38char **FS; /* initial field sep */
39char **RS; /* initial record sep */
40char **OFS; /* output field sep */
41char **ORS; /* output record sep */
42char **OFMT; /* output format for numbers */
43char **CONVFMT; /* format for conversions in getsval */
44Awkfloat *NF; /* number of fields in current record */
45Awkfloat *NR; /* number of current record */
46Awkfloat *FNR; /* number of current record in current file */
47char **FILENAME; /* current filename argument */
48Awkfloat *ARGC; /* number of arguments from command line */
49char **SUBSEP; /* subscript separator for a[i,j,k]; default \034 */
50Awkfloat *RSTART; /* start of re matched with ~; origin 1 (!) */
51Awkfloat *RLENGTH; /* length of same */
52
53Cell *fsloc; /* FS */
54Cell *nrloc; /* NR */
55Cell *nfloc; /* NF */
56Cell *fnrloc; /* FNR */
Cody Peter Mello52566c02018-09-18 15:45:55 -070057Cell *ofsloc; /* OFS */
58Cell *orsloc; /* ORS */
59Cell *rsloc; /* RS */
Brian Kernighan87b94932012-12-22 10:35:39 -050060Array *ARGVtab; /* symbol table containing ARGV[...] */
61Array *ENVtab; /* symbol table containing ENVIRON[...] */
62Cell *rstartloc; /* RSTART */
63Cell *rlengthloc; /* RLENGTH */
Cody Peter Mello97a4b7e2018-09-17 11:59:04 -070064Cell *subseploc; /* SUBSEP */
Brian Kernighan87b94932012-12-22 10:35:39 -050065Cell *symtabloc; /* SYMTAB */
66
67Cell *nullloc; /* a guaranteed empty cell */
68Node *nullnode; /* zero&null, converted into a node for comparisons */
69Cell *literal0;
70
71extern Cell **fldtab;
72
Arnold D. Robbins32093f52018-08-22 20:40:26 +030073static void
74setfree(Cell *vp)
75{
76 if (&vp->sval == FS || &vp->sval == RS ||
77 &vp->sval == OFS || &vp->sval == ORS ||
78 &vp->sval == OFMT || &vp->sval == CONVFMT ||
79 &vp->sval == FILENAME || &vp->sval == SUBSEP)
80 vp->tval |= DONTFREE;
81 else
82 vp->tval &= ~DONTFREE;
83}
84
Brian Kernighan87b94932012-12-22 10:35:39 -050085void syminit(void) /* initialize symbol table with builtin vars */
86{
87 literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab);
88 /* this is used for if(x)... tests: */
89 nullloc = setsymtab("$zero&null", "", 0.0, NUM|STR|CON|DONTFREE, symtab);
90 nullnode = celltonode(nullloc, CCON);
91
92 fsloc = setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab);
93 FS = &fsloc->sval;
Cody Peter Mello52566c02018-09-18 15:45:55 -070094 rsloc = setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab);
95 RS = &rsloc->sval;
96 ofsloc = setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab);
97 OFS = &ofsloc->sval;
98 orsloc = setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab);
99 ORS = &orsloc->sval;
Brian Kernighan87b94932012-12-22 10:35:39 -0500100 OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
101 CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
102 FILENAME = &setsymtab("FILENAME", "", 0.0, STR|DONTFREE, symtab)->sval;
103 nfloc = setsymtab("NF", "", 0.0, NUM, symtab);
104 NF = &nfloc->fval;
105 nrloc = setsymtab("NR", "", 0.0, NUM, symtab);
106 NR = &nrloc->fval;
107 fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab);
108 FNR = &fnrloc->fval;
Cody Peter Mello97a4b7e2018-09-17 11:59:04 -0700109 subseploc = setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab);
110 SUBSEP = &subseploc->sval;
Brian Kernighan87b94932012-12-22 10:35:39 -0500111 rstartloc = setsymtab("RSTART", "", 0.0, NUM, symtab);
112 RSTART = &rstartloc->fval;
113 rlengthloc = setsymtab("RLENGTH", "", 0.0, NUM, symtab);
114 RLENGTH = &rlengthloc->fval;
115 symtabloc = setsymtab("SYMTAB", "", 0.0, ARR, symtab);
Arnold D. Robbins3358f3f2020-01-01 22:42:20 +0200116 free(symtabloc->sval);
Brian Kernighan87b94932012-12-22 10:35:39 -0500117 symtabloc->sval = (char *) symtab;
118}
119
120void arginit(int ac, char **av) /* set up ARGV and ARGC */
121{
122 Cell *cp;
123 int i;
124 char temp[50];
125
126 ARGC = &setsymtab("ARGC", "", (Awkfloat) ac, NUM, symtab)->fval;
127 cp = setsymtab("ARGV", "", 0.0, ARR, symtab);
128 ARGVtab = makesymtab(NSYMTAB); /* could be (int) ARGC as well */
Arnold D. Robbins3358f3f2020-01-01 22:42:20 +0200129 free(cp->sval);
Brian Kernighan87b94932012-12-22 10:35:39 -0500130 cp->sval = (char *) ARGVtab;
131 for (i = 0; i < ac; i++) {
Arnold Robbinscc9e9b62020-12-08 08:05:22 +0200132 double result;
133
Brian Kernighan87b94932012-12-22 10:35:39 -0500134 sprintf(temp, "%d", i);
Arnold Robbinscc9e9b62020-12-08 08:05:22 +0200135 if (is_number(*av, & result))
136 setsymtab(temp, *av, result, STR|NUM, ARGVtab);
Brian Kernighan87b94932012-12-22 10:35:39 -0500137 else
138 setsymtab(temp, *av, 0.0, STR, ARGVtab);
139 av++;
140 }
141}
142
143void 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);
Arnold D. Robbins3358f3f2020-01-01 22:42:20 +0200150 free(cp->sval);
Brian Kernighan87b94932012-12-22 10:35:39 -0500151 cp->sval = (char *) ENVtab;
152 for ( ; *envp; envp++) {
Arnold Robbinscc9e9b62020-12-08 08:05:22 +0200153 double result;
154
Brian Kernighan87b94932012-12-22 10:35:39 -0500155 if ((p = strchr(*envp, '=')) == NULL)
156 continue;
157 if( p == *envp ) /* no left hand side name in env string */
158 continue;
159 *p++ = 0; /* split into two strings at = */
Arnold Robbinscc9e9b62020-12-08 08:05:22 +0200160 if (is_number(p, & result))
161 setsymtab(*envp, p, result, STR|NUM, ENVtab);
Brian Kernighan87b94932012-12-22 10:35:39 -0500162 else
163 setsymtab(*envp, p, 0.0, STR, ENVtab);
164 p[-1] = '='; /* restore in case env is passed down to a shell */
165 }
166}
167
168Array *makesymtab(int n) /* make a new symbol table */
169{
170 Array *ap;
171 Cell **tp;
172
Arnold D. Robbins3b42cfa2020-10-13 20:52:43 +0300173 ap = (Array *) malloc(sizeof(*ap));
174 tp = (Cell **) calloc(n, sizeof(*tp));
Brian Kernighan87b94932012-12-22 10:35:39 -0500175 if (ap == NULL || tp == NULL)
176 FATAL("out of space in makesymtab");
177 ap->nelem = 0;
178 ap->size = n;
179 ap->tab = tp;
180 return(ap);
181}
182
183void freesymtab(Cell *ap) /* free a symbol table */
184{
185 Cell *cp, *temp;
186 Array *tp;
187 int i;
188
189 if (!isarr(ap))
190 return;
191 tp = (Array *) ap->sval;
192 if (tp == NULL)
193 return;
194 for (i = 0; i < tp->size; i++) {
195 for (cp = tp->tab[i]; cp != NULL; cp = temp) {
196 xfree(cp->nval);
197 if (freeable(cp))
198 xfree(cp->sval);
199 temp = cp->cnext; /* avoids freeing then using */
Arnold D. Robbins795a06b2019-07-28 05:51:52 -0600200 free(cp);
Brian Kernighan87b94932012-12-22 10:35:39 -0500201 tp->nelem--;
202 }
pfg52421942016-06-03 21:23:11 +0000203 tp->tab[i] = NULL;
Brian Kernighan87b94932012-12-22 10:35:39 -0500204 }
205 if (tp->nelem != 0)
206 WARNING("can't happen: inconsistent element count freeing %s", ap->nval);
207 free(tp->tab);
208 free(tp);
209}
210
211void freeelem(Cell *ap, const char *s) /* free elem s from ap (i.e., ap["s"] */
212{
213 Array *tp;
214 Cell *p, *prev = NULL;
215 int h;
Arnold D. Robbins795a06b2019-07-28 05:51:52 -0600216
Brian Kernighan87b94932012-12-22 10:35:39 -0500217 tp = (Array *) ap->sval;
218 h = hash(s, tp->size);
219 for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext)
220 if (strcmp(s, p->nval) == 0) {
221 if (prev == NULL) /* 1st one */
222 tp->tab[h] = p->cnext;
223 else /* middle somewhere */
224 prev->cnext = p->cnext;
225 if (freeable(p))
226 xfree(p->sval);
227 free(p->nval);
228 free(p);
229 tp->nelem--;
230 return;
231 }
232}
233
234Cell *setsymtab(const char *n, const char *s, Awkfloat f, unsigned t, Array *tp)
235{
236 int h;
237 Cell *p;
238
239 if (n != NULL && (p = lookup(n, tp)) != NULL) {
Todd C. Miller292d39f2020-06-25 12:32:34 -0600240 DPRINTF("setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n",
241 (void*)p, NN(p->nval), NN(p->sval), p->fval, p->tval);
Brian Kernighan87b94932012-12-22 10:35:39 -0500242 return(p);
243 }
Arnold D. Robbins3b42cfa2020-10-13 20:52:43 +0300244 p = (Cell *) malloc(sizeof(*p));
Brian Kernighan87b94932012-12-22 10:35:39 -0500245 if (p == NULL)
246 FATAL("out of space for symbol table at %s", n);
247 p->nval = tostring(n);
248 p->sval = s ? tostring(s) : tostring("");
249 p->fval = f;
250 p->tval = t;
251 p->csub = CUNK;
252 p->ctype = OCELL;
253 tp->nelem++;
254 if (tp->nelem > FULLTAB * tp->size)
255 rehash(tp);
256 h = hash(n, tp->size);
257 p->cnext = tp->tab[h];
258 tp->tab[h] = p;
Todd C. Miller292d39f2020-06-25 12:32:34 -0600259 DPRINTF("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n",
260 (void*)p, p->nval, p->sval, p->fval, p->tval);
Brian Kernighan87b94932012-12-22 10:35:39 -0500261 return(p);
262}
263
264int hash(const char *s, int n) /* form hash value for string s */
265{
266 unsigned hashval;
267
268 for (hashval = 0; *s != '\0'; s++)
269 hashval = (*s + 31 * hashval);
270 return hashval % n;
271}
272
273void rehash(Array *tp) /* rehash items in small table into big one */
274{
275 int i, nh, nsz;
276 Cell *cp, *op, **np;
277
278 nsz = GROWTAB * tp->size;
Arnold D. Robbins3b42cfa2020-10-13 20:52:43 +0300279 np = (Cell **) calloc(nsz, sizeof(*np));
Brian Kernighan87b94932012-12-22 10:35:39 -0500280 if (np == NULL) /* can't do it, but can keep running. */
281 return; /* someone else will run out later. */
282 for (i = 0; i < tp->size; i++) {
283 for (cp = tp->tab[i]; cp; cp = op) {
284 op = cp->cnext;
285 nh = hash(cp->nval, nsz);
286 cp->cnext = np[nh];
287 np[nh] = cp;
288 }
289 }
290 free(tp->tab);
291 tp->tab = np;
292 tp->size = nsz;
293}
294
295Cell *lookup(const char *s, Array *tp) /* look for s in tp */
296{
297 Cell *p;
298 int h;
299
300 h = hash(s, tp->size);
301 for (p = tp->tab[h]; p != NULL; p = p->cnext)
302 if (strcmp(s, p->nval) == 0)
303 return(p); /* found it */
304 return(NULL); /* not found */
305}
306
307Awkfloat setfval(Cell *vp, Awkfloat f) /* set float val of a Cell */
308{
309 int fldno;
310
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300311 f += 0.0; /* normalise negative zero to positive zero */
Arnold D. Robbins795a06b2019-07-28 05:51:52 -0600312 if ((vp->tval & (NUM | STR)) == 0)
Brian Kernighan87b94932012-12-22 10:35:39 -0500313 funnyvar(vp, "assign to");
314 if (isfld(vp)) {
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200315 donerec = false; /* mark $0 invalid */
Brian Kernighan87b94932012-12-22 10:35:39 -0500316 fldno = atoi(vp->nval);
317 if (fldno > *NF)
318 newfld(fldno);
Todd C. Miller292d39f2020-06-25 12:32:34 -0600319 DPRINTF("setting field %d to %g\n", fldno, f);
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300320 } else if (&vp->fval == NF) {
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200321 donerec = false; /* mark $0 invalid */
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300322 setlastfld(f);
Todd C. Miller292d39f2020-06-25 12:32:34 -0600323 DPRINTF("setting NF to %g\n", f);
Brian Kernighan87b94932012-12-22 10:35:39 -0500324 } else if (isrec(vp)) {
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200325 donefld = false; /* mark $1... invalid */
326 donerec = true;
Cody Peter Mellob4636802018-10-19 15:07:53 -0700327 savefs();
Cody Peter Mello52566c02018-09-18 15:45:55 -0700328 } else if (vp == ofsloc) {
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200329 if (!donerec)
Cody Peter Mello52566c02018-09-18 15:45:55 -0700330 recbld();
Brian Kernighan87b94932012-12-22 10:35:39 -0500331 }
332 if (freeable(vp))
333 xfree(vp->sval); /* free any previous string */
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300334 vp->tval &= ~(STR|CONVC|CONVO); /* mark string invalid */
335 vp->fmt = NULL;
Brian Kernighan87b94932012-12-22 10:35:39 -0500336 vp->tval |= NUM; /* mark number ok */
337 if (f == -0) /* who would have thought this possible? */
338 f = 0;
Todd C. Miller292d39f2020-06-25 12:32:34 -0600339 DPRINTF("setfval %p: %s = %g, t=%o\n", (void*)vp, NN(vp->nval), f, vp->tval);
Brian Kernighan87b94932012-12-22 10:35:39 -0500340 return vp->fval = f;
341}
342
343void funnyvar(Cell *vp, const char *rw)
344{
345 if (isarr(vp))
346 FATAL("can't %s %s; it's an array name.", rw, vp->nval);
347 if (vp->tval & FCN)
348 FATAL("can't %s %s; it's a function.", rw, vp->nval);
349 WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o",
Arnold D. Robbins5068d202020-02-06 22:27:31 +0200350 (void *)vp, vp->nval, vp->sval, vp->fval, vp->tval);
Brian Kernighan87b94932012-12-22 10:35:39 -0500351}
352
353char *setsval(Cell *vp, const char *s) /* set string val of a Cell */
354{
355 char *t;
356 int fldno;
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300357 Awkfloat f;
Brian Kernighan87b94932012-12-22 10:35:39 -0500358
Todd C. Miller292d39f2020-06-25 12:32:34 -0600359 DPRINTF("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n",
360 (void*)vp, NN(vp->nval), s, vp->tval, donerec, donefld);
Brian Kernighan87b94932012-12-22 10:35:39 -0500361 if ((vp->tval & (NUM | STR)) == 0)
362 funnyvar(vp, "assign to");
363 if (isfld(vp)) {
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200364 donerec = false; /* mark $0 invalid */
Brian Kernighan87b94932012-12-22 10:35:39 -0500365 fldno = atoi(vp->nval);
366 if (fldno > *NF)
367 newfld(fldno);
Chrisb7851412020-08-07 18:10:20 +0800368 DPRINTF("setting field %d to %s (%p)\n", fldno, s, (const void*)s);
Brian Kernighan87b94932012-12-22 10:35:39 -0500369 } else if (isrec(vp)) {
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200370 donefld = false; /* mark $1... invalid */
371 donerec = true;
Cody Peter Mellob4636802018-10-19 15:07:53 -0700372 savefs();
Cody Peter Mello52566c02018-09-18 15:45:55 -0700373 } else if (vp == ofsloc) {
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200374 if (!donerec)
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300375 recbld();
Brian Kernighan87b94932012-12-22 10:35:39 -0500376 }
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300377 t = s ? tostring(s) : tostring(""); /* in case it's self-assign */
Brian Kernighan87b94932012-12-22 10:35:39 -0500378 if (freeable(vp))
379 xfree(vp->sval);
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300380 vp->tval &= ~(NUM|CONVC|CONVO);
Brian Kernighan87b94932012-12-22 10:35:39 -0500381 vp->tval |= STR;
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300382 vp->fmt = NULL;
383 setfree(vp);
Todd C. Miller292d39f2020-06-25 12:32:34 -0600384 DPRINTF("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n",
Chrisb7851412020-08-07 18:10:20 +0800385 (void*)vp, NN(vp->nval), t, (void*)t, vp->tval, donerec, donefld);
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300386 vp->sval = t;
387 if (&vp->fval == NF) {
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200388 donerec = false; /* mark $0 invalid */
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300389 f = getfval(vp);
390 setlastfld(f);
Todd C. Miller292d39f2020-06-25 12:32:34 -0600391 DPRINTF("setting NF to %g\n", f);
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300392 }
393
394 return(vp->sval);
Brian Kernighan87b94932012-12-22 10:35:39 -0500395}
396
397Awkfloat getfval(Cell *vp) /* get float val of a Cell */
398{
399 if ((vp->tval & (NUM | STR)) == 0)
400 funnyvar(vp, "read value of");
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200401 if (isfld(vp) && !donefld)
Brian Kernighan87b94932012-12-22 10:35:39 -0500402 fldbld();
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200403 else if (isrec(vp) && !donerec)
Brian Kernighan87b94932012-12-22 10:35:39 -0500404 recbld();
405 if (!isnum(vp)) { /* not a number */
Arnold Robbinscc9e9b62020-12-08 08:05:22 +0200406 double fval;
407 bool no_trailing;
408
409 if (is_valid_number(vp->sval, true, & no_trailing, & fval)) {
410 vp->fval = fval;
411 if (no_trailing && !(vp->tval&CON))
412 vp->tval |= NUM; /* make NUM only sparingly */
413 } else
414 vp->fval = 0.0;
Brian Kernighan87b94932012-12-22 10:35:39 -0500415 }
Todd C. Miller292d39f2020-06-25 12:32:34 -0600416 DPRINTF("getfval %p: %s = %g, t=%o\n",
417 (void*)vp, NN(vp->nval), vp->fval, vp->tval);
Brian Kernighan87b94932012-12-22 10:35:39 -0500418 return(vp->fval);
419}
420
421static char *get_str_val(Cell *vp, char **fmt) /* get string val of a Cell */
422{
Cody Peter Melloe059b3b2018-09-14 19:56:34 -0700423 char s[256];
Brian Kernighan87b94932012-12-22 10:35:39 -0500424 double dtemp;
425
426 if ((vp->tval & (NUM | STR)) == 0)
427 funnyvar(vp, "read value of");
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200428 if (isfld(vp) && ! donefld)
Brian Kernighan87b94932012-12-22 10:35:39 -0500429 fldbld();
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200430 else if (isrec(vp) && ! donerec)
Brian Kernighan87b94932012-12-22 10:35:39 -0500431 recbld();
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300432
433 /*
434 * ADR: This is complicated and more fragile than is desirable.
435 * Retrieving a string value for a number associates the string
436 * value with the scalar. Previously, the string value was
437 * sticky, meaning if converted via OFMT that became the value
438 * (even though POSIX wants it to be via CONVFMT). Or if CONVFMT
439 * changed after a string value was retrieved, the original value
440 * was maintained and used. Also not per POSIX.
441 *
442 * We work around this design by adding two additional flags,
443 * CONVC and CONVO, indicating how the string value was
444 * obtained (via CONVFMT or OFMT) and _also_ maintaining a copy
445 * of the pointer to the xFMT format string used for the
446 * conversion. This pointer is only read, **never** dereferenced.
447 * The next time we do a conversion, if it's coming from the same
448 * xFMT as last time, and the pointer value is different, we
449 * know that the xFMT format string changed, and we need to
450 * redo the conversion. If it's the same, we don't have to.
451 *
452 * There are also several cases where we don't do a conversion,
453 * such as for a field (see the checks below).
454 */
455
456 /* Don't duplicate the code for actually updating the value */
457#define update_str_val(vp) \
458 { \
459 if (freeable(vp)) \
460 xfree(vp->sval); \
461 if (modf(vp->fval, &dtemp) == 0) /* it's integral */ \
Cody Peter Melloe059b3b2018-09-14 19:56:34 -0700462 snprintf(s, sizeof (s), "%.30g", vp->fval); \
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300463 else \
Cody Peter Melloe059b3b2018-09-14 19:56:34 -0700464 snprintf(s, sizeof (s), *fmt, vp->fval); \
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300465 vp->sval = tostring(s); \
466 vp->tval &= ~DONTFREE; \
467 vp->tval |= STR; \
Brian Kernighan87b94932012-12-22 10:35:39 -0500468 }
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300469
470 if (isstr(vp) == 0) {
471 update_str_val(vp);
472 if (fmt == OFMT) {
473 vp->tval &= ~CONVC;
474 vp->tval |= CONVO;
475 } else {
476 /* CONVFMT */
477 vp->tval &= ~CONVO;
478 vp->tval |= CONVC;
479 }
480 vp->fmt = *fmt;
481 } else if ((vp->tval & DONTFREE) != 0 || ! isnum(vp) || isfld(vp)) {
482 goto done;
483 } else if (isstr(vp)) {
484 if (fmt == OFMT) {
485 if ((vp->tval & CONVC) != 0
486 || ((vp->tval & CONVO) != 0 && vp->fmt != *fmt)) {
487 update_str_val(vp);
488 vp->tval &= ~CONVC;
489 vp->tval |= CONVO;
490 vp->fmt = *fmt;
491 }
492 } else {
493 /* CONVFMT */
494 if ((vp->tval & CONVO) != 0
495 || ((vp->tval & CONVC) != 0 && vp->fmt != *fmt)) {
496 update_str_val(vp);
497 vp->tval &= ~CONVO;
498 vp->tval |= CONVC;
499 vp->fmt = *fmt;
500 }
501 }
502 }
503done:
Todd C. Miller292d39f2020-06-25 12:32:34 -0600504 DPRINTF("getsval %p: %s = \"%s (%p)\", t=%o\n",
Chrisb7851412020-08-07 18:10:20 +0800505 (void*)vp, NN(vp->nval), vp->sval, (void*)vp->sval, vp->tval);
Brian Kernighan87b94932012-12-22 10:35:39 -0500506 return(vp->sval);
507}
508
509char *getsval(Cell *vp) /* get string val of a Cell */
510{
511 return get_str_val(vp, CONVFMT);
512}
513
514char *getpssval(Cell *vp) /* get string val of a Cell for print */
515{
516 return get_str_val(vp, OFMT);
517}
518
519
520char *tostring(const char *s) /* make a copy of string s */
521{
zoulasc65892082019-10-24 09:40:15 -0400522 char *p = strdup(s);
Brian Kernighan87b94932012-12-22 10:35:39 -0500523 if (p == NULL)
524 FATAL("out of space in tostring on %s", s);
Brian Kernighan87b94932012-12-22 10:35:39 -0500525 return(p);
526}
527
Arnold D. Robbins5068d202020-02-06 22:27:31 +0200528char *tostringN(const char *s, size_t n) /* make a copy of string s */
529{
530 char *p;
531
Arnold D. Robbins3b42cfa2020-10-13 20:52:43 +0300532 p = (char *) malloc(n);
Arnold D. Robbins5068d202020-02-06 22:27:31 +0200533 if (p == NULL)
534 FATAL("out of space in tostring on %s", s);
535 strcpy(p, s);
536 return(p);
537}
538
Arnold D. Robbinsc95b9602019-07-28 20:09:24 +0300539Cell *catstr(Cell *a, Cell *b) /* concatenate a and b */
540{
541 Cell *c;
542 char *p;
543 char *sa = getsval(a);
544 char *sb = getsval(b);
545 size_t l = strlen(sa) + strlen(sb) + 1;
Arnold D. Robbins3b42cfa2020-10-13 20:52:43 +0300546 p = (char *) malloc(l);
Arnold D. Robbinsc95b9602019-07-28 20:09:24 +0300547 if (p == NULL)
548 FATAL("out of space concatenating %s and %s", sa, sb);
549 snprintf(p, l, "%s%s", sa, sb);
Arnold D. Robbins944989b2020-01-06 00:01:46 -0700550
551 l++; // add room for ' '
Arnold D. Robbins3b42cfa2020-10-13 20:52:43 +0300552 char *newbuf = (char *) malloc(l);
Arnold D. Robbinsc7eeb572020-01-05 21:18:36 +0200553 if (newbuf == NULL)
554 FATAL("out of space concatenating %s and %s", sa, sb);
555 // See string() in lex.c; a string "xx" is stored in the symbol
556 // table as "xx ".
Arnold D. Robbins944989b2020-01-06 00:01:46 -0700557 snprintf(newbuf, l, "%s ", p);
Arnold D. Robbinsc7eeb572020-01-05 21:18:36 +0200558 c = setsymtab(newbuf, p, 0.0, CON|STR|DONTFREE, symtab);
Arnold D. Robbinsc95b9602019-07-28 20:09:24 +0300559 free(p);
Arnold D. Robbinsc7eeb572020-01-05 21:18:36 +0200560 free(newbuf);
Arnold D. Robbinsc95b9602019-07-28 20:09:24 +0300561 return c;
562}
563
Brian Kernighan87b94932012-12-22 10:35:39 -0500564char *qstring(const char *is, int delim) /* collect string up to next delim */
565{
566 const char *os = is;
567 int c, n;
zoulasc65892082019-10-24 09:40:15 -0400568 const uschar *s = (const uschar *) is;
Brian Kernighan87b94932012-12-22 10:35:39 -0500569 uschar *buf, *bp;
570
Arnold D. Robbins3b42cfa2020-10-13 20:52:43 +0300571 if ((buf = (uschar *) malloc(strlen(is)+3)) == NULL)
Brian Kernighan87b94932012-12-22 10:35:39 -0500572 FATAL( "out of space in qstring(%s)", s);
573 for (bp = buf; (c = *s) != delim; s++) {
574 if (c == '\n')
575 SYNTAX( "newline in string %.20s...", os );
576 else if (c != '\\')
577 *bp++ = c;
578 else { /* \something */
579 c = *++s;
580 if (c == 0) { /* \ at end */
581 *bp++ = '\\';
582 break; /* for loop */
Arnold D. Robbins795a06b2019-07-28 05:51:52 -0600583 }
Brian Kernighan87b94932012-12-22 10:35:39 -0500584 switch (c) {
585 case '\\': *bp++ = '\\'; break;
586 case 'n': *bp++ = '\n'; break;
587 case 't': *bp++ = '\t'; break;
588 case 'b': *bp++ = '\b'; break;
589 case 'f': *bp++ = '\f'; break;
590 case 'r': *bp++ = '\r'; break;
Martijn Dekker5b602ca2019-07-26 10:46:58 +0200591 case 'v': *bp++ = '\v'; break;
592 case 'a': *bp++ = '\a'; break;
Brian Kernighan87b94932012-12-22 10:35:39 -0500593 default:
594 if (!isdigit(c)) {
595 *bp++ = c;
596 break;
597 }
598 n = c - '0';
599 if (isdigit(s[1])) {
600 n = 8 * n + *++s - '0';
601 if (isdigit(s[1]))
602 n = 8 * n + *++s - '0';
603 }
604 *bp++ = n;
605 break;
606 }
607 }
608 }
609 *bp++ = 0;
610 return (char *) buf;
611}
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300612
613const char *flags2str(int flags)
614{
615 static const struct ftab {
616 const char *name;
617 int value;
618 } flagtab[] = {
619 { "NUM", NUM },
620 { "STR", STR },
621 { "DONTFREE", DONTFREE },
622 { "CON", CON },
623 { "ARR", ARR },
624 { "FCN", FCN },
625 { "FLD", FLD },
626 { "REC", REC },
627 { "CONVC", CONVC },
628 { "CONVO", CONVO },
629 { NULL, 0 }
630 };
631 static char buf[100];
632 int i;
633 char *cp = buf;
634
635 for (i = 0; flagtab[i].name != NULL; i++) {
636 if ((flags & flagtab[i].value) != 0) {
637 if (cp > buf)
638 *cp++ = '|';
639 strcpy(cp, flagtab[i].name);
640 cp += strlen(cp);
641 }
642 }
643
644 return buf;
645}