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