1 /****************************************************************
2 Copyright (C) Lucent Technologies 1997
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
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
23 ****************************************************************/
33 #define FULLTAB 2 /* rehash when table gets this x full */
34 #define GROWTAB 4 /* grow table by this factor */
36 Array *symtab; /* main symbol table */
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 */
56 Cell *fnrloc; /* FNR */
57 Cell *ofsloc; /* OFS */
58 Cell *orsloc; /* ORS */
60 Array *ARGVtab; /* symbol table containing ARGV[...] */
61 Array *ENVtab; /* symbol table containing ENVIRON[...] */
62 Cell *rstartloc; /* RSTART */
63 Cell *rlengthloc; /* RLENGTH */
64 Cell *subseploc; /* SUBSEP */
65 Cell *symtabloc; /* SYMTAB */
67 Cell *nullloc; /* a guaranteed empty cell */
68 Node *nullnode; /* zero&null, converted into a node for comparisons */
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)
82 vp->tval &= ~DONTFREE;
85 void syminit(void) /* initialize symbol table with builtin vars */
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);
92 fsloc = setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab);
94 rsloc = setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab);
96 ofsloc = setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab);
98 orsloc = setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab);
100 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);
105 nrloc = setsymtab("NR", "", 0.0, NUM, symtab);
107 fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab);
109 subseploc = setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab);
110 SUBSEP = &subseploc->sval;
111 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);
116 free(symtabloc->sval);
117 symtabloc->sval = (char *) symtab;
120 void arginit(int ac, char **av) /* set up ARGV and ARGC */
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 */
130 cp->sval = (char *) ARGVtab;
131 for (i = 0; i < ac; i++) {
134 sprintf(temp, "%d", i);
135 if (is_number(*av, & result))
136 setsymtab(temp, *av, result, STR|NUM, ARGVtab);
138 setsymtab(temp, *av, 0.0, STR, ARGVtab);
143 void envinit(char **envp) /* set up ENVIRON variable */
148 cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab);
149 ENVtab = makesymtab(NSYMTAB);
151 cp->sval = (char *) ENVtab;
152 for ( ; *envp; envp++) {
155 if ((p = strchr(*envp, '=')) == NULL)
157 if( p == *envp ) /* no left hand side name in env string */
159 *p++ = 0; /* split into two strings at = */
160 if (is_number(p, & result))
161 setsymtab(*envp, p, result, STR|NUM, ENVtab);
163 setsymtab(*envp, p, 0.0, STR, ENVtab);
164 p[-1] = '='; /* restore in case env is passed down to a shell */
168 Array *makesymtab(int n) /* make a new symbol table */
173 ap = (Array *) malloc(sizeof(*ap));
174 tp = (Cell **) calloc(n, sizeof(*tp));
175 if (ap == NULL || tp == NULL)
176 FATAL("out of space in makesymtab");
183 void freesymtab(Cell *ap) /* free a symbol table */
191 tp = (Array *) ap->sval;
194 for (i = 0; i < tp->size; i++) {
195 for (cp = tp->tab[i]; cp != NULL; cp = temp) {
199 temp = cp->cnext; /* avoids freeing then using */
206 WARNING("can't happen: inconsistent element count freeing %s", ap->nval);
211 void freeelem(Cell *ap, const char *s) /* free elem s from ap (i.e., ap["s"] */
214 Cell *p, *prev = NULL;
217 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;
234 Cell *setsymtab(const char *n, const char *s, Awkfloat f, unsigned t, Array *tp)
239 if (n != NULL && (p = lookup(n, tp)) != NULL) {
240 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);
244 p = (Cell *) malloc(sizeof(*p));
246 FATAL("out of space for symbol table at %s", n);
247 p->nval = tostring(n);
248 p->sval = s ? tostring(s) : tostring("");
254 if (tp->nelem > FULLTAB * tp->size)
256 h = hash(n, tp->size);
257 p->cnext = tp->tab[h];
259 DPRINTF("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n",
260 (void*)p, p->nval, p->sval, p->fval, p->tval);
264 int hash(const char *s, int n) /* form hash value for string s */
268 for (hashval = 0; *s != '\0'; s++)
269 hashval = (*s + 31 * hashval);
273 void rehash(Array *tp) /* rehash items in small table into big one */
278 nsz = GROWTAB * tp->size;
279 np = (Cell **) calloc(nsz, sizeof(*np));
280 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) {
285 nh = hash(cp->nval, nsz);
295 Cell *lookup(const char *s, Array *tp) /* look for s in tp */
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 */
307 Awkfloat setfval(Cell *vp, Awkfloat f) /* set float val of a Cell */
311 f += 0.0; /* normalise negative zero to positive zero */
312 if ((vp->tval & (NUM | STR)) == 0)
313 funnyvar(vp, "assign to");
315 donerec = false; /* mark $0 invalid */
316 fldno = atoi(vp->nval);
319 DPRINTF("setting field %d to %g\n", fldno, f);
320 } else if (&vp->fval == NF) {
321 donerec = false; /* mark $0 invalid */
323 DPRINTF("setting NF to %g\n", f);
324 } else if (isrec(vp)) {
325 donefld = false; /* mark $1... invalid */
328 } else if (vp == ofsloc) {
333 xfree(vp->sval); /* free any previous string */
334 vp->tval &= ~(STR|CONVC|CONVO); /* mark string invalid */
336 vp->tval |= NUM; /* mark number ok */
337 if (f == -0) /* who would have thought this possible? */
339 DPRINTF("setfval %p: %s = %g, t=%o\n", (void*)vp, NN(vp->nval), f, vp->tval);
343 void funnyvar(Cell *vp, const char *rw)
346 FATAL("can't %s %s; it's an array name.", rw, vp->nval);
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",
350 (void *)vp, vp->nval, vp->sval, vp->fval, vp->tval);
353 char *setsval(Cell *vp, const char *s) /* set string val of a Cell */
359 DPRINTF("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n",
360 (void*)vp, NN(vp->nval), s, vp->tval, donerec, donefld);
361 if ((vp->tval & (NUM | STR)) == 0)
362 funnyvar(vp, "assign to");
364 donerec = false; /* mark $0 invalid */
365 fldno = atoi(vp->nval);
368 DPRINTF("setting field %d to %s (%p)\n", fldno, s, (const void*)s);
369 } else if (isrec(vp)) {
370 donefld = false; /* mark $1... invalid */
373 } else if (vp == ofsloc) {
377 t = s ? tostring(s) : tostring(""); /* in case it's self-assign */
380 vp->tval &= ~(NUM|CONVC|CONVO);
384 DPRINTF("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n",
385 (void*)vp, NN(vp->nval), t, (void*)t, vp->tval, donerec, donefld);
387 if (&vp->fval == NF) {
388 donerec = false; /* mark $0 invalid */
391 DPRINTF("setting NF to %g\n", f);
397 Awkfloat getfval(Cell *vp) /* get float val of a Cell */
399 if ((vp->tval & (NUM | STR)) == 0)
400 funnyvar(vp, "read value of");
401 if (isfld(vp) && !donefld)
403 else if (isrec(vp) && !donerec)
405 if (!isnum(vp)) { /* not a number */
409 if (is_valid_number(vp->sval, true, & no_trailing, & fval)) {
411 if (no_trailing && !(vp->tval&CON))
412 vp->tval |= NUM; /* make NUM only sparingly */
416 DPRINTF("getfval %p: %s = %g, t=%o\n",
417 (void*)vp, NN(vp->nval), vp->fval, vp->tval);
421 static const char *get_inf_nan(double d)
424 return (d < 0 ? "-inf" : "+inf");
425 } else if (isnan(d)) {
426 return (signbit(d) != 0 ? "-nan" : "+nan");
431 static char *get_str_val(Cell *vp, char **fmt) /* get string val of a Cell */
437 if ((vp->tval & (NUM | STR)) == 0)
438 funnyvar(vp, "read value of");
439 if (isfld(vp) && ! donefld)
441 else if (isrec(vp) && ! donerec)
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.
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.
463 * There are also several cases where we don't do a conversion,
464 * such as for a field (see the checks below).
467 /* Don't duplicate the code for actually updating the value */
468 #define update_str_val(vp) \
472 if ((p = get_inf_nan(vp->fval)) != NULL) \
474 else if (modf(vp->fval, &dtemp) == 0) /* it's integral */ \
475 snprintf(s, sizeof (s), "%.30g", vp->fval); \
477 snprintf(s, sizeof (s), *fmt, vp->fval); \
478 vp->sval = tostring(s); \
479 vp->tval &= ~DONTFREE; \
483 if (isstr(vp) == 0) {
494 } else if ((vp->tval & DONTFREE) != 0 || ! isnum(vp) || isfld(vp)) {
496 } else if (isstr(vp)) {
498 if ((vp->tval & CONVC) != 0
499 || ((vp->tval & CONVO) != 0 && vp->fmt != *fmt)) {
507 if ((vp->tval & CONVO) != 0
508 || ((vp->tval & CONVC) != 0 && vp->fmt != *fmt)) {
517 DPRINTF("getsval %p: %s = \"%s (%p)\", t=%o\n",
518 (void*)vp, NN(vp->nval), vp->sval, (void*)vp->sval, vp->tval);
522 char *getsval(Cell *vp) /* get string val of a Cell */
524 return get_str_val(vp, CONVFMT);
527 char *getpssval(Cell *vp) /* get string val of a Cell for print */
529 return get_str_val(vp, OFMT);
533 char *tostring(const char *s) /* make a copy of string s */
537 FATAL("out of space in tostring on %s", s);
541 char *tostringN(const char *s, size_t n) /* make a copy of string s */
545 p = (char *) malloc(n);
547 FATAL("out of space in tostring on %s", s);
552 Cell *catstr(Cell *a, Cell *b) /* concatenate a and b */
556 char *sa = getsval(a);
557 char *sb = getsval(b);
558 size_t l = strlen(sa) + strlen(sb) + 1;
559 p = (char *) malloc(l);
561 FATAL("out of space concatenating %s and %s", sa, sb);
562 snprintf(p, l, "%s%s", sa, sb);
564 l++; // add room for ' '
565 char *newbuf = (char *) malloc(l);
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
570 snprintf(newbuf, l, "%s ", p);
571 c = setsymtab(newbuf, p, 0.0, CON|STR|DONTFREE, symtab);
577 char *qstring(const char *is, int delim) /* collect string up to next delim */
581 const uschar *s = (const uschar *) is;
584 if ((buf = (uschar *) malloc(strlen(is)+3)) == NULL)
585 FATAL( "out of space in qstring(%s)", s);
586 for (bp = buf; (c = *s) != delim; s++) {
588 SYNTAX( "newline in string %.20s...", os );
591 else { /* \something */
593 if (c == 0) { /* \ at end */
595 break; /* for loop */
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;
604 case 'v': *bp++ = '\v'; break;
605 case 'a': *bp++ = '\a'; break;
613 n = 8 * n + *++s - '0';
615 n = 8 * n + *++s - '0';
626 const char *flags2str(int flags)
628 static const struct ftab {
634 { "DONTFREE", DONTFREE },
644 static char buf[100];
648 for (i = 0; flagtab[i].name != NULL; i++) {
649 if ((flags & flagtab[i].value) != 0) {
652 strcpy(cp, flagtab[i].name);