blob: c6ae890c8a913b38fcf2a0d2e5f6b8c7be93d26b [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
Arnold D. Robbinsc0f4e972021-02-15 20:33:15 +0200421static const char *get_inf_nan(double d)
Arnold D. Robbins8909e002020-12-18 11:57:48 +0200422{
423 if (isinf(d)) {
424 return (d < 0 ? "-inf" : "+inf");
425 } else if (isnan(d)) {
426 return (signbit(d) != 0 ? "-nan" : "+nan");
427 } else
428 return NULL;
429}
430
Brian Kernighan87b94932012-12-22 10:35:39 -0500431static char *get_str_val(Cell *vp, char **fmt) /* get string val of a Cell */
432{
Cody Peter Melloe059b3b2018-09-14 19:56:34 -0700433 char s[256];
Brian Kernighan87b94932012-12-22 10:35:39 -0500434 double dtemp;
Arnold D. Robbinsc0f4e972021-02-15 20:33:15 +0200435 const char *p;
Brian Kernighan87b94932012-12-22 10:35:39 -0500436
437 if ((vp->tval & (NUM | STR)) == 0)
438 funnyvar(vp, "read value of");
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200439 if (isfld(vp) && ! donefld)
Brian Kernighan87b94932012-12-22 10:35:39 -0500440 fldbld();
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200441 else if (isrec(vp) && ! donerec)
Brian Kernighan87b94932012-12-22 10:35:39 -0500442 recbld();
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300443
444 /*
445 * ADR: This is complicated and more fragile than is desirable.
446 * Retrieving a string value for a number associates the string
447 * value with the scalar. Previously, the string value was
448 * sticky, meaning if converted via OFMT that became the value
449 * (even though POSIX wants it to be via CONVFMT). Or if CONVFMT
450 * changed after a string value was retrieved, the original value
451 * was maintained and used. Also not per POSIX.
452 *
453 * We work around this design by adding two additional flags,
454 * CONVC and CONVO, indicating how the string value was
455 * obtained (via CONVFMT or OFMT) and _also_ maintaining a copy
456 * of the pointer to the xFMT format string used for the
457 * conversion. This pointer is only read, **never** dereferenced.
458 * The next time we do a conversion, if it's coming from the same
459 * xFMT as last time, and the pointer value is different, we
460 * know that the xFMT format string changed, and we need to
461 * redo the conversion. If it's the same, we don't have to.
462 *
463 * There are also several cases where we don't do a conversion,
464 * such as for a field (see the checks below).
465 */
466
467 /* Don't duplicate the code for actually updating the value */
468#define update_str_val(vp) \
469 { \
470 if (freeable(vp)) \
471 xfree(vp->sval); \
Arnold D. Robbins8909e002020-12-18 11:57:48 +0200472 if ((p = get_inf_nan(vp->fval)) != NULL) \
473 strcpy(s, p); \
474 else if (modf(vp->fval, &dtemp) == 0) /* it's integral */ \
Cody Peter Melloe059b3b2018-09-14 19:56:34 -0700475 snprintf(s, sizeof (s), "%.30g", vp->fval); \
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300476 else \
Cody Peter Melloe059b3b2018-09-14 19:56:34 -0700477 snprintf(s, sizeof (s), *fmt, vp->fval); \
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300478 vp->sval = tostring(s); \
479 vp->tval &= ~DONTFREE; \
480 vp->tval |= STR; \
Brian Kernighan87b94932012-12-22 10:35:39 -0500481 }
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300482
483 if (isstr(vp) == 0) {
484 update_str_val(vp);
485 if (fmt == OFMT) {
486 vp->tval &= ~CONVC;
487 vp->tval |= CONVO;
488 } else {
489 /* CONVFMT */
490 vp->tval &= ~CONVO;
491 vp->tval |= CONVC;
492 }
493 vp->fmt = *fmt;
494 } else if ((vp->tval & DONTFREE) != 0 || ! isnum(vp) || isfld(vp)) {
495 goto done;
496 } else if (isstr(vp)) {
497 if (fmt == OFMT) {
498 if ((vp->tval & CONVC) != 0
499 || ((vp->tval & CONVO) != 0 && vp->fmt != *fmt)) {
500 update_str_val(vp);
501 vp->tval &= ~CONVC;
502 vp->tval |= CONVO;
503 vp->fmt = *fmt;
504 }
505 } else {
506 /* CONVFMT */
507 if ((vp->tval & CONVO) != 0
508 || ((vp->tval & CONVC) != 0 && vp->fmt != *fmt)) {
509 update_str_val(vp);
510 vp->tval &= ~CONVO;
511 vp->tval |= CONVC;
512 vp->fmt = *fmt;
513 }
514 }
515 }
516done:
Todd C. Miller292d39f2020-06-25 12:32:34 -0600517 DPRINTF("getsval %p: %s = \"%s (%p)\", t=%o\n",
Chrisb7851412020-08-07 18:10:20 +0800518 (void*)vp, NN(vp->nval), vp->sval, (void*)vp->sval, vp->tval);
Brian Kernighan87b94932012-12-22 10:35:39 -0500519 return(vp->sval);
520}
521
522char *getsval(Cell *vp) /* get string val of a Cell */
523{
524 return get_str_val(vp, CONVFMT);
525}
526
527char *getpssval(Cell *vp) /* get string val of a Cell for print */
528{
529 return get_str_val(vp, OFMT);
530}
531
532
533char *tostring(const char *s) /* make a copy of string s */
534{
zoulasc65892082019-10-24 09:40:15 -0400535 char *p = strdup(s);
Brian Kernighan87b94932012-12-22 10:35:39 -0500536 if (p == NULL)
537 FATAL("out of space in tostring on %s", s);
Brian Kernighan87b94932012-12-22 10:35:39 -0500538 return(p);
539}
540
Arnold D. Robbins5068d202020-02-06 22:27:31 +0200541char *tostringN(const char *s, size_t n) /* make a copy of string s */
542{
543 char *p;
544
Arnold D. Robbins3b42cfa2020-10-13 20:52:43 +0300545 p = (char *) malloc(n);
Arnold D. Robbins5068d202020-02-06 22:27:31 +0200546 if (p == NULL)
547 FATAL("out of space in tostring on %s", s);
548 strcpy(p, s);
549 return(p);
550}
551
Arnold D. Robbinsc95b9602019-07-28 20:09:24 +0300552Cell *catstr(Cell *a, Cell *b) /* concatenate a and b */
553{
554 Cell *c;
555 char *p;
556 char *sa = getsval(a);
557 char *sb = getsval(b);
558 size_t l = strlen(sa) + strlen(sb) + 1;
Arnold D. Robbins3b42cfa2020-10-13 20:52:43 +0300559 p = (char *) malloc(l);
Arnold D. Robbinsc95b9602019-07-28 20:09:24 +0300560 if (p == NULL)
561 FATAL("out of space concatenating %s and %s", sa, sb);
562 snprintf(p, l, "%s%s", sa, sb);
Arnold D. Robbins944989b2020-01-06 00:01:46 -0700563
564 l++; // add room for ' '
Arnold D. Robbins3b42cfa2020-10-13 20:52:43 +0300565 char *newbuf = (char *) malloc(l);
Arnold D. Robbinsc7eeb572020-01-05 21:18:36 +0200566 if (newbuf == NULL)
567 FATAL("out of space concatenating %s and %s", sa, sb);
568 // See string() in lex.c; a string "xx" is stored in the symbol
569 // table as "xx ".
Arnold D. Robbins944989b2020-01-06 00:01:46 -0700570 snprintf(newbuf, l, "%s ", p);
Arnold D. Robbinsc7eeb572020-01-05 21:18:36 +0200571 c = setsymtab(newbuf, p, 0.0, CON|STR|DONTFREE, symtab);
Arnold D. Robbinsc95b9602019-07-28 20:09:24 +0300572 free(p);
Arnold D. Robbinsc7eeb572020-01-05 21:18:36 +0200573 free(newbuf);
Arnold D. Robbinsc95b9602019-07-28 20:09:24 +0300574 return c;
575}
576
Brian Kernighan87b94932012-12-22 10:35:39 -0500577char *qstring(const char *is, int delim) /* collect string up to next delim */
578{
579 const char *os = is;
580 int c, n;
zoulasc65892082019-10-24 09:40:15 -0400581 const uschar *s = (const uschar *) is;
Brian Kernighan87b94932012-12-22 10:35:39 -0500582 uschar *buf, *bp;
583
Arnold D. Robbins3b42cfa2020-10-13 20:52:43 +0300584 if ((buf = (uschar *) malloc(strlen(is)+3)) == NULL)
Brian Kernighan87b94932012-12-22 10:35:39 -0500585 FATAL( "out of space in qstring(%s)", s);
586 for (bp = buf; (c = *s) != delim; s++) {
587 if (c == '\n')
588 SYNTAX( "newline in string %.20s...", os );
589 else if (c != '\\')
590 *bp++ = c;
591 else { /* \something */
592 c = *++s;
593 if (c == 0) { /* \ at end */
594 *bp++ = '\\';
595 break; /* for loop */
Arnold D. Robbins795a06b2019-07-28 05:51:52 -0600596 }
Brian Kernighan87b94932012-12-22 10:35:39 -0500597 switch (c) {
598 case '\\': *bp++ = '\\'; break;
599 case 'n': *bp++ = '\n'; break;
600 case 't': *bp++ = '\t'; break;
601 case 'b': *bp++ = '\b'; break;
602 case 'f': *bp++ = '\f'; break;
603 case 'r': *bp++ = '\r'; break;
Martijn Dekker5b602ca2019-07-26 10:46:58 +0200604 case 'v': *bp++ = '\v'; break;
605 case 'a': *bp++ = '\a'; break;
Brian Kernighan87b94932012-12-22 10:35:39 -0500606 default:
607 if (!isdigit(c)) {
608 *bp++ = c;
609 break;
610 }
611 n = c - '0';
612 if (isdigit(s[1])) {
613 n = 8 * n + *++s - '0';
614 if (isdigit(s[1]))
615 n = 8 * n + *++s - '0';
616 }
617 *bp++ = n;
618 break;
619 }
620 }
621 }
622 *bp++ = 0;
623 return (char *) buf;
624}
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300625
626const char *flags2str(int flags)
627{
628 static const struct ftab {
629 const char *name;
630 int value;
631 } flagtab[] = {
632 { "NUM", NUM },
633 { "STR", STR },
634 { "DONTFREE", DONTFREE },
635 { "CON", CON },
636 { "ARR", ARR },
637 { "FCN", FCN },
638 { "FLD", FLD },
639 { "REC", REC },
640 { "CONVC", CONVC },
641 { "CONVO", CONVO },
642 { NULL, 0 }
643 };
644 static char buf[100];
645 int i;
646 char *cp = buf;
647
648 for (i = 0; flagtab[i].name != NULL; i++) {
649 if ((flags & flagtab[i].value) != 0) {
650 if (cp > buf)
651 *cp++ = '|';
652 strcpy(cp, flagtab[i].name);
653 cp += strlen(cp);
654 }
655 }
656
657 return buf;
658}