1/****************************************************************
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 "awk.h"
27#include <ctype.h>
28#include <math.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32
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 */
57Cell *ofsloc; /* OFS */
58Cell *orsloc; /* ORS */
59Cell *rsloc; /* RS */
60Cell *ARGVcell; /* cell with symbol table containing ARGV[...] */
61Cell *rstartloc; /* RSTART */
62Cell *rlengthloc; /* RLENGTH */
63Cell *subseploc; /* SUBSEP */
64Cell *symtabloc; /* SYMTAB */
65
66Cell *nullloc; /* a guaranteed empty cell */
67Node *nullnode; /* zero&null, converted into a node for comparisons */
68Cell *literal0;
69
70extern Cell **fldtab;
71
72void
73syminit(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;
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;
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;
97 subseploc = setsymtab("SUBSEP", "\034", 0.0, STR | DONTFREE, symtab);
98 SUBSEP = &subseploc->sval;
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);
104 free(symtabloc->sval);
105 symtabloc->sval = (char *)symtab;
106}
107
108void
109arginit(int ac, char **av) /* set up ARGV and ARGC */
110{
111 Array *ap;
112 Cell *cp;
113 int i;
114 char temp[50];
115
116 ARGC = &setsymtab("ARGC", "", (Awkfloat)ac, NUM, symtab)->fval;
117 cp = setsymtab("ARGV", "", 0.0, ARR, symtab);
118 ap = makesymtab(NSYMTAB); /* could be (int) ARGC as well */
119 free(cp->sval);
120 cp->sval = (char *)ap;
121 for (i = 0; i < ac; i++) {
122 double result;
123
124 sprintf(temp, "%d", i);
125 if (is_number(*av, &result))
126 setsymtab(temp, *av, result, STR | NUM, ap);
127 else
128 setsymtab(temp, *av, 0.0, STR, ap);
129 av++;
130 }
131 ARGVcell = cp;
132}
133
134void
135envinit(char **envp) /* set up ENVIRON variable */
136{
137 Array *ap;
138 Cell *cp;
139 char *p;
140
141 cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab);
142 ap = makesymtab(NSYMTAB);
143 free(cp->sval);
144 cp->sval = (char *)ap;
145 for (; *envp; envp++) {
146 double result;
147
148 if ((p = strchr(*envp, '=')) == NULL)
149 continue;
150 if (p == *envp) /* no left hand side name in env string */
151 continue;
152 *p++ = 0; /* split into two strings at = */
153 if (is_number(p, &result))
154 setsymtab(*envp, p, result, STR | NUM, ap);
155 else
156 setsymtab(*envp, p, 0.0, STR, ap);
157 p[-1] = '='; /* restore in case env is passed down to a shell */
158 }
159}
160
161Array *
162makesymtab(int n) /* make a new symbol table */
163{
164 Array *ap;
165 Cell **tp;
166
167 ap = (Array *)malloc(sizeof(*ap));
168 tp = (Cell **)calloc(n, sizeof(*tp));
169 if (ap == NULL || tp == NULL)
170 FATAL("out of space in makesymtab");
171 ap->nelem = 0;
172 ap->size = n;
173 ap->tab = tp;
174 return (ap);
175}
176
177void
178freesymtab(Cell *ap) /* free a symbol table */
179{
180 Cell *cp, *temp;
181 Array *tp;
182 int i;
183
184 if (!isarr(ap))
185 return;
186 tp = (Array *)ap->sval;
187 if (tp == NULL)
188 return;
189 for (i = 0; i < tp->size; i++) {
190 for (cp = tp->tab[i]; cp != NULL; cp = temp) {
191 xfree(cp->nval);
192 if (freeable(cp))
193 xfree(cp->sval);
194 temp = cp->cnext; /* avoids freeing then using */
195 free(cp);
196 tp->nelem--;
197 }
198 tp->tab[i] = NULL;
199 }
200 if (tp->nelem != 0)
201 WARNING("can't happen: inconsistent element count freeing %s", ap->nval);
202 free(tp->tab);
203 free(tp);
204}
205
206void
207freeelem(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;
212
213 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 *
231setsymtab(const char *n, const char *s, Awkfloat f, unsigned t, Array *tp)
232{
233 int h;
234 Cell *p;
235
236 if (n != NULL && (p = lookup(n, tp)) != NULL) {
237 DPRINTF(
238 "setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n",
239 (void *)p,
240 NN(p->nval),
241 NN(p->sval),
242 p->fval,
243 p->tval
244 );
245 return (p);
246 }
247 p = (Cell *)malloc(sizeof(*p));
248 if (p == NULL)
249 FATAL("out of space for symbol table at %s", n);
250 p->nval = tostring(n);
251 p->sval = s ? tostring(s) : tostring("");
252 p->fval = f;
253 p->tval = t;
254 p->csub = CUNK;
255 p->ctype = OCELL;
256 tp->nelem++;
257 if (tp->nelem > FULLTAB * tp->size)
258 rehash(tp);
259 h = hash(n, tp->size);
260 p->cnext = tp->tab[h];
261 tp->tab[h] = p;
262 DPRINTF(
263 "setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n", (void *)p, p->nval, p->sval, p->fval, p->tval
264 );
265 return (p);
266}
267
268int
269hash(const char *s, int n) /* form hash value for string s */
270{
271 unsigned hashval;
272
273 for (hashval = 0; *s != '\0'; s++)
274 hashval = (*s + 31 * hashval);
275 return hashval % n;
276}
277
278void
279rehash(Array *tp) /* rehash items in small table into big one */
280{
281 int i, nh, nsz;
282 Cell *cp, *op, **np;
283
284 nsz = GROWTAB * tp->size;
285 np = (Cell **)calloc(nsz, sizeof(*np));
286 if (np == NULL) /* can't do it, but can keep running. */
287 return; /* someone else will run out later. */
288 for (i = 0; i < tp->size; i++) {
289 for (cp = tp->tab[i]; cp; cp = op) {
290 op = cp->cnext;
291 nh = hash(cp->nval, nsz);
292 cp->cnext = np[nh];
293 np[nh] = cp;
294 }
295 }
296 free(tp->tab);
297 tp->tab = np;
298 tp->size = nsz;
299}
300
301Cell *
302lookup(const char *s, Array *tp) /* look for s in tp */
303{
304 Cell *p;
305 int h;
306
307 h = hash(s, tp->size);
308 for (p = tp->tab[h]; p != NULL; p = p->cnext)
309 if (strcmp(s, p->nval) == 0)
310 return (p); /* found it */
311 return (NULL); /* not found */
312}
313
314Awkfloat
315setfval(Cell *vp, Awkfloat f) /* set float val of a Cell */
316{
317 int fldno;
318
319 f += 0.0; /* normalise negative zero to positive zero */
320 if ((vp->tval & (NUM | STR)) == 0)
321 funnyvar(vp, "assign to");
322 if (isfld(vp)) {
323 donerec = false; /* mark $0 invalid */
324 fldno = atoi(vp->nval);
325 if (fldno > *NF)
326 newfld(fldno);
327 DPRINTF("setting field %d to %g\n", fldno, f);
328 } else if (&vp->fval == NF) {
329 donerec = false; /* mark $0 invalid */
330 setlastfld(f);
331 DPRINTF("setfval: setting NF to %g\n", f);
332 } else if (isrec(vp)) {
333 donefld = false; /* mark $1... invalid */
334 donerec = true;
335 savefs();
336 } else if (vp == ofsloc) {
337 if (!donerec)
338 recbld();
339 }
340 if (freeable(vp))
341 xfree(vp->sval); /* free any previous string */
342 vp->tval &= ~(STR | CONVC | CONVO); /* mark string invalid */
343 vp->fmt = NULL;
344 vp->tval |= NUM; /* mark number ok */
345 if (f == -0) /* who would have thought this possible? */
346 f = 0;
347 DPRINTF("setfval %p: %s = %g, t=%o\n", (void *)vp, NN(vp->nval), f, vp->tval);
348 return vp->fval = f;
349}
350
351void
352funnyvar(Cell *vp, const char *rw)
353{
354 if (isarr(vp))
355 FATAL("can't %s %s; it's an array name.", rw, vp->nval);
356 if (vp->tval & FCN)
357 FATAL("can't %s %s; it's a function.", rw, vp->nval);
358 WARNING(
359 "funny variable %p: n=%s s=\"%s\" f=%g t=%o",
360 (void *)vp,
361 vp->nval,
362 vp->sval,
363 vp->fval,
364 vp->tval
365 );
366}
367
368char *
369setsval(Cell *vp, const char *s) /* set string val of a Cell */
370{
371 char *t;
372 int fldno;
373 Awkfloat f;
374
375 DPRINTF(
376 "starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n",
377 (void *)vp,
378 NN(vp->nval),
379 s,
380 vp->tval,
381 donerec,
382 donefld
383 );
384 if ((vp->tval & (NUM | STR)) == 0)
385 funnyvar(vp, "assign to");
386 if (CSV && (vp == rsloc))
387 WARNING("danger: don't set RS when --csv is in effect");
388 if (CSV && (vp == fsloc))
389 WARNING("danger: don't set FS when --csv is in effect");
390 if (isfld(vp)) {
391 donerec = false; /* mark $0 invalid */
392 fldno = atoi(vp->nval);
393 if (fldno > *NF)
394 newfld(fldno);
395 DPRINTF("setting field %d to %s (%p)\n", fldno, s, (const void *)s);
396 } else if (isrec(vp)) {
397 donefld = false; /* mark $1... invalid */
398 donerec = true;
399 savefs();
400 } else if (vp == ofsloc) {
401 if (!donerec)
402 recbld();
403 }
404 t = s ? tostring(s) : tostring(""); /* in case it's self-assign */
405 if (freeable(vp))
406 xfree(vp->sval);
407 vp->tval &= ~(NUM | DONTFREE | CONVC | CONVO);
408 vp->tval |= STR;
409 vp->fmt = NULL;
410 DPRINTF(
411 "setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n",
412 (void *)vp,
413 NN(vp->nval),
414 t,
415 (void *)t,
416 vp->tval,
417 donerec,
418 donefld
419 );
420 vp->sval = t;
421 if (&vp->fval == NF) {
422 donerec = false; /* mark $0 invalid */
423 f = getfval(vp);
424 setlastfld(f);
425 DPRINTF("setsval: setting NF to %g\n", f);
426 }
427
428 return (vp->sval);
429}
430
431Awkfloat
432getfval(Cell *vp) /* get float val of a Cell */
433{
434 if ((vp->tval & (NUM | STR)) == 0)
435 funnyvar(vp, "read value of");
436 if (isfld(vp) && !donefld)
437 fldbld();
438 else if (isrec(vp) && !donerec)
439 recbld();
440 if (!isnum(vp)) { /* not a number */
441 double fval;
442 bool no_trailing;
443
444 if (is_valid_number(vp->sval, true, &no_trailing, &fval)) {
445 vp->fval = fval;
446 if (no_trailing && !(vp->tval & CON))
447 vp->tval |= NUM; /* make NUM only sparingly */
448 } else
449 vp->fval = 0.0;
450 }
451 DPRINTF("getfval %p: %s = %g, t=%o\n", (void *)vp, NN(vp->nval), vp->fval, vp->tval);
452 return (vp->fval);
453}
454
455static const char *
456get_inf_nan(double d)
457{
458 if (isinf(d)) {
459 return (d < 0 ? "-inf" : "+inf");
460 } else if (isnan(d)) {
461 return (signbit(d) != 0 ? "-nan" : "+nan");
462 } else
463 return NULL;
464}
465
466static char *
467get_str_val(Cell *vp, char **fmt) /* get string val of a Cell */
468{
469 char s[256];
470 double dtemp;
471 const char *p;
472
473 if ((vp->tval & (NUM | STR)) == 0)
474 funnyvar(vp, "read value of");
475 if (isfld(vp) && !donefld)
476 fldbld();
477 else if (isrec(vp) && !donerec)
478 recbld();
479
480 /*
481 * ADR: This is complicated and more fragile than is desirable.
482 * Retrieving a string value for a number associates the string
483 * value with the scalar. Previously, the string value was
484 * sticky, meaning if converted via OFMT that became the value
485 * (even though POSIX wants it to be via CONVFMT). Or if CONVFMT
486 * changed after a string value was retrieved, the original value
487 * was maintained and used. Also not per POSIX.
488 *
489 * We work around this design by adding two additional flags,
490 * CONVC and CONVO, indicating how the string value was
491 * obtained (via CONVFMT or OFMT) and _also_ maintaining a copy
492 * of the pointer to the xFMT format string used for the
493 * conversion. This pointer is only read, **never** dereferenced.
494 * The next time we do a conversion, if it's coming from the same
495 * xFMT as last time, and the pointer value is different, we
496 * know that the xFMT format string changed, and we need to
497 * redo the conversion. If it's the same, we don't have to.
498 *
499 * There are also several cases where we don't do a conversion,
500 * such as for a field (see the checks below).
501 */
502
503 /* Don't duplicate the code for actually updating the value */
504#define update_str_val(vp) \
505 { \
506 if (freeable(vp)) \
507 xfree(vp->sval); \
508 if ((p = get_inf_nan(vp->fval)) != NULL) \
509 strcpy(s, p); \
510 else if (modf(vp->fval, &dtemp) == 0) /* it's integral */ \
511 snprintf(s, sizeof(s), "%.30g", vp->fval); \
512 else \
513 snprintf(s, sizeof(s), *fmt, vp->fval); \
514 vp->sval = tostring(s); \
515 vp->tval &= ~DONTFREE; \
516 vp->tval |= STR; \
517 }
518
519 if (isstr(vp) == 0) {
520 update_str_val(vp);
521 if (fmt == OFMT) {
522 vp->tval &= ~CONVC;
523 vp->tval |= CONVO;
524 } else {
525 /* CONVFMT */
526 vp->tval &= ~CONVO;
527 vp->tval |= CONVC;
528 }
529 vp->fmt = *fmt;
530 } else if ((vp->tval & DONTFREE) != 0 || !isnum(vp) || isfld(vp)) {
531 goto done;
532 } else if (isstr(vp)) {
533 if (fmt == OFMT) {
534 if ((vp->tval & CONVC) != 0 || ((vp->tval & CONVO) != 0 && vp->fmt != *fmt)) {
535 update_str_val(vp);
536 vp->tval &= ~CONVC;
537 vp->tval |= CONVO;
538 vp->fmt = *fmt;
539 }
540 } else {
541 /* CONVFMT */
542 if ((vp->tval & CONVO) != 0 || ((vp->tval & CONVC) != 0 && vp->fmt != *fmt)) {
543 update_str_val(vp);
544 vp->tval &= ~CONVO;
545 vp->tval |= CONVC;
546 vp->fmt = *fmt;
547 }
548 }
549 }
550done:
551 DPRINTF(
552 "getsval %p: %s = \"%s (%p)\", t=%o\n",
553 (void *)vp,
554 NN(vp->nval),
555 vp->sval,
556 (void *)vp->sval,
557 vp->tval
558 );
559 return (vp->sval);
560}
561
562char *
563getsval(Cell *vp) /* get string val of a Cell */
564{
565 return get_str_val(vp, CONVFMT);
566}
567
568char *
569getpssval(Cell *vp) /* get string val of a Cell for print */
570{
571 return get_str_val(vp, OFMT);
572}
573
574char *
575tostring(const char *s) /* make a copy of string s */
576{
577 char *p = strdup(s);
578 if (p == NULL)
579 FATAL("out of space in tostring on %s", s);
580 return (p);
581}
582
583char *
584tostringN(const char *s, size_t n) /* make a copy of string s */
585{
586 char *p;
587
588 p = (char *)malloc(n);
589 if (p == NULL)
590 FATAL("out of space in tostring on %s", s);
591 strcpy(p, s);
592 return (p);
593}
594
595Cell *
596catstr(Cell *a, Cell *b) /* concatenate a and b */
597{
598 Cell *c;
599 char *p;
600 char *sa = getsval(a);
601 char *sb = getsval(b);
602 size_t l = strlen(sa) + strlen(sb) + 1;
603 p = (char *)malloc(l);
604 if (p == NULL)
605 FATAL("out of space concatenating %s and %s", sa, sb);
606 snprintf(p, l, "%s%s", sa, sb);
607
608 l++; // add room for ' '
609 char *newbuf = (char *)malloc(l);
610 if (newbuf == NULL)
611 FATAL("out of space concatenating %s and %s", sa, sb);
612 // See string() in lex.c; a string "xx" is stored in the symbol
613 // table as "xx ".
614 snprintf(newbuf, l, "%s ", p);
615 c = setsymtab(newbuf, p, 0.0, CON | STR | DONTFREE, symtab);
616 free(p);
617 free(newbuf);
618 return c;
619}
620
621char *
622qstring(const char *is, int delim) /* collect string up to next delim */
623{
624 int c, n;
625 const uschar *s = (const uschar *)is;
626 uschar *buf, *bp;
627
628 if ((buf = (uschar *)malloc(strlen(is) + 3)) == NULL)
629 FATAL("out of space in qstring(%s)", s);
630 for (bp = buf; (c = *s) != delim; s++) {
631 if (c == '\n')
632 SYNTAX("newline in string %.20s...", is);
633 else if (c != '\\')
634 *bp++ = c;
635 else { /* \something */
636 c = *++s;
637 if (c == 0) { /* \ at end */
638 *bp++ = '\\';
639 break; /* for loop */
640 }
641 switch (c) {
642 case '\\':
643 *bp++ = '\\';
644 break;
645 case 'n':
646 *bp++ = '\n';
647 break;
648 case 't':
649 *bp++ = '\t';
650 break;
651 case 'b':
652 *bp++ = '\b';
653 break;
654 case 'f':
655 *bp++ = '\f';
656 break;
657 case 'r':
658 *bp++ = '\r';
659 break;
660 case 'v':
661 *bp++ = '\v';
662 break;
663 case 'a':
664 *bp++ = '\a';
665 break;
666 default:
667 if (!isdigit(c)) {
668 *bp++ = c;
669 break;
670 }
671 n = c - '0';
672 if (isdigit(s[1])) {
673 n = 8 * n + *++s - '0';
674 if (isdigit(s[1]))
675 n = 8 * n + *++s - '0';
676 }
677 *bp++ = n;
678 break;
679 }
680 }
681 }
682 *bp++ = 0;
683 return (char *)buf;
684}
685
686const char *
687flags2str(int flags)
688{
689 static const struct ftab {
690 const char *name;
691 int value;
692 } flagtab[] = {
693 {"NUM", NUM},
694 {"STR", STR},
695 {"DONTFREE", DONTFREE},
696 {"CON", CON},
697 {"ARR", ARR},
698 {"FCN", FCN},
699 {"FLD", FLD},
700 {"REC", REC},
701 {"CONVC", CONVC},
702 {"CONVO", CONVO},
703 {NULL, 0}
704 };
705 static char buf[100];
706 int i;
707 char *cp = buf;
708
709 for (i = 0; flagtab[i].name != NULL; i++) {
710 if ((flags & flagtab[i].value) != 0) {
711 if (cp > buf)
712 *cp++ = '|';
713 strcpy(cp, flagtab[i].name);
714 cp += strlen(cp);
715 }
716 }
717
718 return buf;
719}