blob: 79964cd76027815023f59c0993f89f9dd16f5df3 [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++) {
132 sprintf(temp, "%d", i);
133 if (is_number(*av))
134 setsymtab(temp, *av, atof(*av), STR|NUM, ARGVtab);
135 else
136 setsymtab(temp, *av, 0.0, STR, ARGVtab);
137 av++;
138 }
139}
140
141void envinit(char **envp) /* set up ENVIRON variable */
142{
143 Cell *cp;
144 char *p;
145
146 cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab);
147 ENVtab = makesymtab(NSYMTAB);
Arnold D. Robbins3358f3f2020-01-01 22:42:20 +0200148 free(cp->sval);
Brian Kernighan87b94932012-12-22 10:35:39 -0500149 cp->sval = (char *) ENVtab;
150 for ( ; *envp; envp++) {
151 if ((p = strchr(*envp, '=')) == NULL)
152 continue;
153 if( p == *envp ) /* no left hand side name in env string */
154 continue;
155 *p++ = 0; /* split into two strings at = */
156 if (is_number(p))
157 setsymtab(*envp, p, atof(p), STR|NUM, ENVtab);
158 else
159 setsymtab(*envp, p, 0.0, STR, ENVtab);
160 p[-1] = '='; /* restore in case env is passed down to a shell */
161 }
162}
163
164Array *makesymtab(int n) /* make a new symbol table */
165{
166 Array *ap;
167 Cell **tp;
168
Arnold D. Robbins3b42cfa2020-10-13 20:52:43 +0300169 ap = (Array *) malloc(sizeof(*ap));
170 tp = (Cell **) calloc(n, sizeof(*tp));
Brian Kernighan87b94932012-12-22 10:35:39 -0500171 if (ap == NULL || tp == NULL)
172 FATAL("out of space in makesymtab");
173 ap->nelem = 0;
174 ap->size = n;
175 ap->tab = tp;
176 return(ap);
177}
178
179void freesymtab(Cell *ap) /* free a symbol table */
180{
181 Cell *cp, *temp;
182 Array *tp;
183 int i;
184
185 if (!isarr(ap))
186 return;
187 tp = (Array *) ap->sval;
188 if (tp == NULL)
189 return;
190 for (i = 0; i < tp->size; i++) {
191 for (cp = tp->tab[i]; cp != NULL; cp = temp) {
192 xfree(cp->nval);
193 if (freeable(cp))
194 xfree(cp->sval);
195 temp = cp->cnext; /* avoids freeing then using */
Arnold D. Robbins795a06b2019-07-28 05:51:52 -0600196 free(cp);
Brian Kernighan87b94932012-12-22 10:35:39 -0500197 tp->nelem--;
198 }
pfg52421942016-06-03 21:23:11 +0000199 tp->tab[i] = NULL;
Brian Kernighan87b94932012-12-22 10:35:39 -0500200 }
201 if (tp->nelem != 0)
202 WARNING("can't happen: inconsistent element count freeing %s", ap->nval);
203 free(tp->tab);
204 free(tp);
205}
206
207void freeelem(Cell *ap, const char *s) /* free elem s from ap (i.e., ap["s"] */
208{
209 Array *tp;
210 Cell *p, *prev = NULL;
211 int h;
Arnold D. Robbins795a06b2019-07-28 05:51:52 -0600212
Brian Kernighan87b94932012-12-22 10:35:39 -0500213 tp = (Array *) ap->sval;
214 h = hash(s, tp->size);
215 for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext)
216 if (strcmp(s, p->nval) == 0) {
217 if (prev == NULL) /* 1st one */
218 tp->tab[h] = p->cnext;
219 else /* middle somewhere */
220 prev->cnext = p->cnext;
221 if (freeable(p))
222 xfree(p->sval);
223 free(p->nval);
224 free(p);
225 tp->nelem--;
226 return;
227 }
228}
229
230Cell *setsymtab(const char *n, const char *s, Awkfloat f, unsigned t, Array *tp)
231{
232 int h;
233 Cell *p;
234
235 if (n != NULL && (p = lookup(n, tp)) != NULL) {
Todd C. Miller292d39f2020-06-25 12:32:34 -0600236 DPRINTF("setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n",
237 (void*)p, NN(p->nval), NN(p->sval), p->fval, p->tval);
Brian Kernighan87b94932012-12-22 10:35:39 -0500238 return(p);
239 }
Arnold D. Robbins3b42cfa2020-10-13 20:52:43 +0300240 p = (Cell *) malloc(sizeof(*p));
Brian Kernighan87b94932012-12-22 10:35:39 -0500241 if (p == NULL)
242 FATAL("out of space for symbol table at %s", n);
243 p->nval = tostring(n);
244 p->sval = s ? tostring(s) : tostring("");
245 p->fval = f;
246 p->tval = t;
247 p->csub = CUNK;
248 p->ctype = OCELL;
249 tp->nelem++;
250 if (tp->nelem > FULLTAB * tp->size)
251 rehash(tp);
252 h = hash(n, tp->size);
253 p->cnext = tp->tab[h];
254 tp->tab[h] = p;
Todd C. Miller292d39f2020-06-25 12:32:34 -0600255 DPRINTF("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n",
256 (void*)p, p->nval, p->sval, p->fval, p->tval);
Brian Kernighan87b94932012-12-22 10:35:39 -0500257 return(p);
258}
259
260int hash(const char *s, int n) /* form hash value for string s */
261{
262 unsigned hashval;
263
264 for (hashval = 0; *s != '\0'; s++)
265 hashval = (*s + 31 * hashval);
266 return hashval % n;
267}
268
269void rehash(Array *tp) /* rehash items in small table into big one */
270{
271 int i, nh, nsz;
272 Cell *cp, *op, **np;
273
274 nsz = GROWTAB * tp->size;
Arnold D. Robbins3b42cfa2020-10-13 20:52:43 +0300275 np = (Cell **) calloc(nsz, sizeof(*np));
Brian Kernighan87b94932012-12-22 10:35:39 -0500276 if (np == NULL) /* can't do it, but can keep running. */
277 return; /* someone else will run out later. */
278 for (i = 0; i < tp->size; i++) {
279 for (cp = tp->tab[i]; cp; cp = op) {
280 op = cp->cnext;
281 nh = hash(cp->nval, nsz);
282 cp->cnext = np[nh];
283 np[nh] = cp;
284 }
285 }
286 free(tp->tab);
287 tp->tab = np;
288 tp->size = nsz;
289}
290
291Cell *lookup(const char *s, Array *tp) /* look for s in tp */
292{
293 Cell *p;
294 int h;
295
296 h = hash(s, tp->size);
297 for (p = tp->tab[h]; p != NULL; p = p->cnext)
298 if (strcmp(s, p->nval) == 0)
299 return(p); /* found it */
300 return(NULL); /* not found */
301}
302
303Awkfloat setfval(Cell *vp, Awkfloat f) /* set float val of a Cell */
304{
305 int fldno;
306
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300307 f += 0.0; /* normalise negative zero to positive zero */
Arnold D. Robbins795a06b2019-07-28 05:51:52 -0600308 if ((vp->tval & (NUM | STR)) == 0)
Brian Kernighan87b94932012-12-22 10:35:39 -0500309 funnyvar(vp, "assign to");
310 if (isfld(vp)) {
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200311 donerec = false; /* mark $0 invalid */
Brian Kernighan87b94932012-12-22 10:35:39 -0500312 fldno = atoi(vp->nval);
313 if (fldno > *NF)
314 newfld(fldno);
Todd C. Miller292d39f2020-06-25 12:32:34 -0600315 DPRINTF("setting field %d to %g\n", fldno, f);
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300316 } else if (&vp->fval == NF) {
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200317 donerec = false; /* mark $0 invalid */
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300318 setlastfld(f);
Todd C. Miller292d39f2020-06-25 12:32:34 -0600319 DPRINTF("setting NF to %g\n", f);
Brian Kernighan87b94932012-12-22 10:35:39 -0500320 } else if (isrec(vp)) {
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200321 donefld = false; /* mark $1... invalid */
322 donerec = true;
Cody Peter Mellob4636802018-10-19 15:07:53 -0700323 savefs();
Cody Peter Mello52566c02018-09-18 15:45:55 -0700324 } else if (vp == ofsloc) {
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200325 if (!donerec)
Cody Peter Mello52566c02018-09-18 15:45:55 -0700326 recbld();
Brian Kernighan87b94932012-12-22 10:35:39 -0500327 }
328 if (freeable(vp))
329 xfree(vp->sval); /* free any previous string */
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300330 vp->tval &= ~(STR|CONVC|CONVO); /* mark string invalid */
331 vp->fmt = NULL;
Brian Kernighan87b94932012-12-22 10:35:39 -0500332 vp->tval |= NUM; /* mark number ok */
333 if (f == -0) /* who would have thought this possible? */
334 f = 0;
Todd C. Miller292d39f2020-06-25 12:32:34 -0600335 DPRINTF("setfval %p: %s = %g, t=%o\n", (void*)vp, NN(vp->nval), f, vp->tval);
Brian Kernighan87b94932012-12-22 10:35:39 -0500336 return vp->fval = f;
337}
338
339void funnyvar(Cell *vp, const char *rw)
340{
341 if (isarr(vp))
342 FATAL("can't %s %s; it's an array name.", rw, vp->nval);
343 if (vp->tval & FCN)
344 FATAL("can't %s %s; it's a function.", rw, vp->nval);
345 WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o",
Arnold D. Robbins5068d202020-02-06 22:27:31 +0200346 (void *)vp, vp->nval, vp->sval, vp->fval, vp->tval);
Brian Kernighan87b94932012-12-22 10:35:39 -0500347}
348
349char *setsval(Cell *vp, const char *s) /* set string val of a Cell */
350{
351 char *t;
352 int fldno;
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300353 Awkfloat f;
Brian Kernighan87b94932012-12-22 10:35:39 -0500354
Todd C. Miller292d39f2020-06-25 12:32:34 -0600355 DPRINTF("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n",
356 (void*)vp, NN(vp->nval), s, vp->tval, donerec, donefld);
Brian Kernighan87b94932012-12-22 10:35:39 -0500357 if ((vp->tval & (NUM | STR)) == 0)
358 funnyvar(vp, "assign to");
359 if (isfld(vp)) {
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200360 donerec = false; /* mark $0 invalid */
Brian Kernighan87b94932012-12-22 10:35:39 -0500361 fldno = atoi(vp->nval);
362 if (fldno > *NF)
363 newfld(fldno);
Chrisb7851412020-08-07 18:10:20 +0800364 DPRINTF("setting field %d to %s (%p)\n", fldno, s, (const void*)s);
Brian Kernighan87b94932012-12-22 10:35:39 -0500365 } else if (isrec(vp)) {
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200366 donefld = false; /* mark $1... invalid */
367 donerec = true;
Cody Peter Mellob4636802018-10-19 15:07:53 -0700368 savefs();
Cody Peter Mello52566c02018-09-18 15:45:55 -0700369 } else if (vp == ofsloc) {
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200370 if (!donerec)
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300371 recbld();
Brian Kernighan87b94932012-12-22 10:35:39 -0500372 }
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300373 t = s ? tostring(s) : tostring(""); /* in case it's self-assign */
Brian Kernighan87b94932012-12-22 10:35:39 -0500374 if (freeable(vp))
375 xfree(vp->sval);
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300376 vp->tval &= ~(NUM|CONVC|CONVO);
Brian Kernighan87b94932012-12-22 10:35:39 -0500377 vp->tval |= STR;
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300378 vp->fmt = NULL;
379 setfree(vp);
Todd C. Miller292d39f2020-06-25 12:32:34 -0600380 DPRINTF("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n",
Chrisb7851412020-08-07 18:10:20 +0800381 (void*)vp, NN(vp->nval), t, (void*)t, vp->tval, donerec, donefld);
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300382 vp->sval = t;
383 if (&vp->fval == NF) {
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200384 donerec = false; /* mark $0 invalid */
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300385 f = getfval(vp);
386 setlastfld(f);
Todd C. Miller292d39f2020-06-25 12:32:34 -0600387 DPRINTF("setting NF to %g\n", f);
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300388 }
389
390 return(vp->sval);
Brian Kernighan87b94932012-12-22 10:35:39 -0500391}
392
393Awkfloat getfval(Cell *vp) /* get float val of a Cell */
394{
395 if ((vp->tval & (NUM | STR)) == 0)
396 funnyvar(vp, "read value of");
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200397 if (isfld(vp) && !donefld)
Brian Kernighan87b94932012-12-22 10:35:39 -0500398 fldbld();
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200399 else if (isrec(vp) && !donerec)
Brian Kernighan87b94932012-12-22 10:35:39 -0500400 recbld();
401 if (!isnum(vp)) { /* not a number */
402 vp->fval = atof(vp->sval); /* best guess */
403 if (is_number(vp->sval) && !(vp->tval&CON))
404 vp->tval |= NUM; /* make NUM only sparingly */
405 }
Todd C. Miller292d39f2020-06-25 12:32:34 -0600406 DPRINTF("getfval %p: %s = %g, t=%o\n",
407 (void*)vp, NN(vp->nval), vp->fval, vp->tval);
Brian Kernighan87b94932012-12-22 10:35:39 -0500408 return(vp->fval);
409}
410
411static char *get_str_val(Cell *vp, char **fmt) /* get string val of a Cell */
412{
Cody Peter Melloe059b3b2018-09-14 19:56:34 -0700413 char s[256];
Brian Kernighan87b94932012-12-22 10:35:39 -0500414 double dtemp;
415
416 if ((vp->tval & (NUM | STR)) == 0)
417 funnyvar(vp, "read value of");
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200418 if (isfld(vp) && ! donefld)
Brian Kernighan87b94932012-12-22 10:35:39 -0500419 fldbld();
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200420 else if (isrec(vp) && ! donerec)
Brian Kernighan87b94932012-12-22 10:35:39 -0500421 recbld();
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300422
423 /*
424 * ADR: This is complicated and more fragile than is desirable.
425 * Retrieving a string value for a number associates the string
426 * value with the scalar. Previously, the string value was
427 * sticky, meaning if converted via OFMT that became the value
428 * (even though POSIX wants it to be via CONVFMT). Or if CONVFMT
429 * changed after a string value was retrieved, the original value
430 * was maintained and used. Also not per POSIX.
431 *
432 * We work around this design by adding two additional flags,
433 * CONVC and CONVO, indicating how the string value was
434 * obtained (via CONVFMT or OFMT) and _also_ maintaining a copy
435 * of the pointer to the xFMT format string used for the
436 * conversion. This pointer is only read, **never** dereferenced.
437 * The next time we do a conversion, if it's coming from the same
438 * xFMT as last time, and the pointer value is different, we
439 * know that the xFMT format string changed, and we need to
440 * redo the conversion. If it's the same, we don't have to.
441 *
442 * There are also several cases where we don't do a conversion,
443 * such as for a field (see the checks below).
444 */
445
446 /* Don't duplicate the code for actually updating the value */
447#define update_str_val(vp) \
448 { \
449 if (freeable(vp)) \
450 xfree(vp->sval); \
451 if (modf(vp->fval, &dtemp) == 0) /* it's integral */ \
Cody Peter Melloe059b3b2018-09-14 19:56:34 -0700452 snprintf(s, sizeof (s), "%.30g", vp->fval); \
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300453 else \
Cody Peter Melloe059b3b2018-09-14 19:56:34 -0700454 snprintf(s, sizeof (s), *fmt, vp->fval); \
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300455 vp->sval = tostring(s); \
456 vp->tval &= ~DONTFREE; \
457 vp->tval |= STR; \
Brian Kernighan87b94932012-12-22 10:35:39 -0500458 }
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300459
460 if (isstr(vp) == 0) {
461 update_str_val(vp);
462 if (fmt == OFMT) {
463 vp->tval &= ~CONVC;
464 vp->tval |= CONVO;
465 } else {
466 /* CONVFMT */
467 vp->tval &= ~CONVO;
468 vp->tval |= CONVC;
469 }
470 vp->fmt = *fmt;
471 } else if ((vp->tval & DONTFREE) != 0 || ! isnum(vp) || isfld(vp)) {
472 goto done;
473 } else if (isstr(vp)) {
474 if (fmt == OFMT) {
475 if ((vp->tval & CONVC) != 0
476 || ((vp->tval & CONVO) != 0 && vp->fmt != *fmt)) {
477 update_str_val(vp);
478 vp->tval &= ~CONVC;
479 vp->tval |= CONVO;
480 vp->fmt = *fmt;
481 }
482 } else {
483 /* CONVFMT */
484 if ((vp->tval & CONVO) != 0
485 || ((vp->tval & CONVC) != 0 && vp->fmt != *fmt)) {
486 update_str_val(vp);
487 vp->tval &= ~CONVO;
488 vp->tval |= CONVC;
489 vp->fmt = *fmt;
490 }
491 }
492 }
493done:
Todd C. Miller292d39f2020-06-25 12:32:34 -0600494 DPRINTF("getsval %p: %s = \"%s (%p)\", t=%o\n",
Chrisb7851412020-08-07 18:10:20 +0800495 (void*)vp, NN(vp->nval), vp->sval, (void*)vp->sval, vp->tval);
Brian Kernighan87b94932012-12-22 10:35:39 -0500496 return(vp->sval);
497}
498
499char *getsval(Cell *vp) /* get string val of a Cell */
500{
501 return get_str_val(vp, CONVFMT);
502}
503
504char *getpssval(Cell *vp) /* get string val of a Cell for print */
505{
506 return get_str_val(vp, OFMT);
507}
508
509
510char *tostring(const char *s) /* make a copy of string s */
511{
zoulasc65892082019-10-24 09:40:15 -0400512 char *p = strdup(s);
Brian Kernighan87b94932012-12-22 10:35:39 -0500513 if (p == NULL)
514 FATAL("out of space in tostring on %s", s);
Brian Kernighan87b94932012-12-22 10:35:39 -0500515 return(p);
516}
517
Arnold D. Robbins5068d202020-02-06 22:27:31 +0200518char *tostringN(const char *s, size_t n) /* make a copy of string s */
519{
520 char *p;
521
Arnold D. Robbins3b42cfa2020-10-13 20:52:43 +0300522 p = (char *) malloc(n);
Arnold D. Robbins5068d202020-02-06 22:27:31 +0200523 if (p == NULL)
524 FATAL("out of space in tostring on %s", s);
525 strcpy(p, s);
526 return(p);
527}
528
Arnold D. Robbinsc95b9602019-07-28 20:09:24 +0300529Cell *catstr(Cell *a, Cell *b) /* concatenate a and b */
530{
531 Cell *c;
532 char *p;
533 char *sa = getsval(a);
534 char *sb = getsval(b);
535 size_t l = strlen(sa) + strlen(sb) + 1;
Arnold D. Robbins3b42cfa2020-10-13 20:52:43 +0300536 p = (char *) malloc(l);
Arnold D. Robbinsc95b9602019-07-28 20:09:24 +0300537 if (p == NULL)
538 FATAL("out of space concatenating %s and %s", sa, sb);
539 snprintf(p, l, "%s%s", sa, sb);
Arnold D. Robbins944989b2020-01-06 00:01:46 -0700540
541 l++; // add room for ' '
Arnold D. Robbins3b42cfa2020-10-13 20:52:43 +0300542 char *newbuf = (char *) malloc(l);
Arnold D. Robbinsc7eeb572020-01-05 21:18:36 +0200543 if (newbuf == NULL)
544 FATAL("out of space concatenating %s and %s", sa, sb);
545 // See string() in lex.c; a string "xx" is stored in the symbol
546 // table as "xx ".
Arnold D. Robbins944989b2020-01-06 00:01:46 -0700547 snprintf(newbuf, l, "%s ", p);
Arnold D. Robbinsc7eeb572020-01-05 21:18:36 +0200548 c = setsymtab(newbuf, p, 0.0, CON|STR|DONTFREE, symtab);
Arnold D. Robbinsc95b9602019-07-28 20:09:24 +0300549 free(p);
Arnold D. Robbinsc7eeb572020-01-05 21:18:36 +0200550 free(newbuf);
Arnold D. Robbinsc95b9602019-07-28 20:09:24 +0300551 return c;
552}
553
Brian Kernighan87b94932012-12-22 10:35:39 -0500554char *qstring(const char *is, int delim) /* collect string up to next delim */
555{
556 const char *os = is;
557 int c, n;
zoulasc65892082019-10-24 09:40:15 -0400558 const uschar *s = (const uschar *) is;
Brian Kernighan87b94932012-12-22 10:35:39 -0500559 uschar *buf, *bp;
560
Arnold D. Robbins3b42cfa2020-10-13 20:52:43 +0300561 if ((buf = (uschar *) malloc(strlen(is)+3)) == NULL)
Brian Kernighan87b94932012-12-22 10:35:39 -0500562 FATAL( "out of space in qstring(%s)", s);
563 for (bp = buf; (c = *s) != delim; s++) {
564 if (c == '\n')
565 SYNTAX( "newline in string %.20s...", os );
566 else if (c != '\\')
567 *bp++ = c;
568 else { /* \something */
569 c = *++s;
570 if (c == 0) { /* \ at end */
571 *bp++ = '\\';
572 break; /* for loop */
Arnold D. Robbins795a06b2019-07-28 05:51:52 -0600573 }
Brian Kernighan87b94932012-12-22 10:35:39 -0500574 switch (c) {
575 case '\\': *bp++ = '\\'; break;
576 case 'n': *bp++ = '\n'; break;
577 case 't': *bp++ = '\t'; break;
578 case 'b': *bp++ = '\b'; break;
579 case 'f': *bp++ = '\f'; break;
580 case 'r': *bp++ = '\r'; break;
Martijn Dekker5b602ca2019-07-26 10:46:58 +0200581 case 'v': *bp++ = '\v'; break;
582 case 'a': *bp++ = '\a'; break;
Brian Kernighan87b94932012-12-22 10:35:39 -0500583 default:
584 if (!isdigit(c)) {
585 *bp++ = c;
586 break;
587 }
588 n = c - '0';
589 if (isdigit(s[1])) {
590 n = 8 * n + *++s - '0';
591 if (isdigit(s[1]))
592 n = 8 * n + *++s - '0';
593 }
594 *bp++ = n;
595 break;
596 }
597 }
598 }
599 *bp++ = 0;
600 return (char *) buf;
601}
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300602
603const char *flags2str(int flags)
604{
605 static const struct ftab {
606 const char *name;
607 int value;
608 } flagtab[] = {
609 { "NUM", NUM },
610 { "STR", STR },
611 { "DONTFREE", DONTFREE },
612 { "CON", CON },
613 { "ARR", ARR },
614 { "FCN", FCN },
615 { "FLD", FLD },
616 { "REC", REC },
617 { "CONVC", CONVC },
618 { "CONVO", CONVO },
619 { NULL, 0 }
620 };
621 static char buf[100];
622 int i;
623 char *cp = buf;
624
625 for (i = 0; flagtab[i].name != NULL; i++) {
626 if ((flags & flagtab[i].value) != 0) {
627 if (cp > buf)
628 *cp++ = '|';
629 strcpy(cp, flagtab[i].name);
630 cp += strlen(cp);
631 }
632 }
633
634 return buf;
635}