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 "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) /* cant 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 its 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 its 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 its the same, we dont have to
498 *
499 * there are also several cases where we dont do a conversion,
500 * such as for a field (see the checks below)
501 */
502
503 /* dont 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) /* its 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}