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 */
73 void syminit(void) /* initialize symbol table with builtin vars */
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
);
80 fsloc
= setsymtab("FS", " ", 0.0, STR
|DONTFREE
, symtab
);
82 rsloc
= setsymtab("RS", "\n", 0.0, STR
|DONTFREE
, symtab
);
84 ofsloc
= setsymtab("OFS", " ", 0.0, STR
|DONTFREE
, symtab
);
86 orsloc
= setsymtab("ORS", "\n", 0.0, STR
|DONTFREE
, symtab
);
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
);
93 nrloc
= setsymtab("NR", "", 0.0, NUM
, symtab
);
95 fnrloc
= setsymtab("FNR", "", 0.0, NUM
, symtab
);
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
;
108 void arginit(int ac
, char **av
) /* set up ARGV and ARGC */
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 */
118 cp
->sval
= (char *) ARGVtab
;
119 for (i
= 0; i
< ac
; i
++) {
122 sprintf(temp
, "%d", i
);
123 if (is_number(*av
, & result
))
124 setsymtab(temp
, *av
, result
, STR
|NUM
, ARGVtab
);
126 setsymtab(temp
, *av
, 0.0, STR
, ARGVtab
);
131 void envinit(char **envp
) /* set up ENVIRON variable */
136 cp
= setsymtab("ENVIRON", "", 0.0, ARR
, symtab
);
137 ENVtab
= makesymtab(NSYMTAB
);
139 cp
->sval
= (char *) ENVtab
;
140 for ( ; *envp
; envp
++) {
143 if ((p
= strchr(*envp
, '=')) == NULL
)
145 if( p
== *envp
) /* no left hand side name in env string */
147 *p
++ = 0; /* split into two strings at = */
148 if (is_number(p
, & result
))
149 setsymtab(*envp
, p
, result
, STR
|NUM
, ENVtab
);
151 setsymtab(*envp
, p
, 0.0, STR
, ENVtab
);
152 p
[-1] = '='; /* restore in case env is passed down to a shell */
156 Array
*makesymtab(int n
) /* make a new symbol table */
161 ap
= (Array
*) malloc(sizeof(*ap
));
162 tp
= (Cell
**) calloc(n
, sizeof(*tp
));
163 if (ap
== NULL
|| tp
== NULL
)
164 FATAL("out of space in makesymtab");
171 void freesymtab(Cell
*ap
) /* free a symbol table */
179 tp
= (Array
*) ap
->sval
;
182 for (i
= 0; i
< tp
->size
; i
++) {
183 for (cp
= tp
->tab
[i
]; cp
!= NULL
; cp
= temp
) {
187 temp
= cp
->cnext
; /* avoids freeing then using */
194 WARNING("can't happen: inconsistent element count freeing %s", ap
->nval
);
199 void freeelem(Cell
*ap
, const char *s
) /* free elem s from ap (i.e., ap["s"] */
202 Cell
*p
, *prev
= NULL
;
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
;
222 Cell
*setsymtab(const char *n
, const char *s
, Awkfloat f
, unsigned t
, Array
*tp
)
227 if (n
!= NULL
&& (p
= lookup(n
, tp
)) != NULL
) {
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
);
232 p
= (Cell
*) malloc(sizeof(*p
));
234 FATAL("out of space for symbol table at %s", n
);
235 p
->nval
= tostring(n
);
236 p
->sval
= s
? tostring(s
) : tostring("");
242 if (tp
->nelem
> FULLTAB
* tp
->size
)
244 h
= hash(n
, tp
->size
);
245 p
->cnext
= tp
->tab
[h
];
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
);
252 int hash(const char *s
, int n
) /* form hash value for string s */
256 for (hashval
= 0; *s
!= '\0'; s
++)
257 hashval
= (*s
+ 31 * hashval
);
261 void rehash(Array
*tp
) /* rehash items in small table into big one */
266 nsz
= GROWTAB
* tp
->size
;
267 np
= (Cell
**) calloc(nsz
, sizeof(*np
));
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
) {
273 nh
= hash(cp
->nval
, nsz
);
283 Cell
*lookup(const char *s
, Array
*tp
) /* look for s in tp */
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 */
295 Awkfloat
setfval(Cell
*vp
, Awkfloat f
) /* set float val of a Cell */
299 f
+= 0.0; /* normalise negative zero to positive zero */
300 if ((vp
->tval
& (NUM
| STR
)) == 0)
301 funnyvar(vp
, "assign to");
303 donerec
= false; /* mark $0 invalid */
304 fldno
= atoi(vp
->nval
);
307 DPRINTF("setting field %d to %g\n", fldno
, f
);
308 } else if (&vp
->fval
== NF
) {
309 donerec
= false; /* mark $0 invalid */
311 DPRINTF("setting NF to %g\n", f
);
312 } else if (isrec(vp
)) {
313 donefld
= false; /* mark $1... invalid */
316 } else if (vp
== ofsloc
) {
321 xfree(vp
->sval
); /* free any previous string */
322 vp
->tval
&= ~(STR
|CONVC
|CONVO
); /* mark string invalid */
324 vp
->tval
|= NUM
; /* mark number ok */
325 if (f
== -0) /* who would have thought this possible? */
327 DPRINTF("setfval %p: %s = %g, t=%o\n", (void*)vp
, NN(vp
->nval
), f
, vp
->tval
);
331 void funnyvar(Cell
*vp
, const char *rw
)
334 FATAL("can't %s %s; it's an array name.", rw
, vp
->nval
);
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",
338 (void *)vp
, vp
->nval
, vp
->sval
, vp
->fval
, vp
->tval
);
341 char *setsval(Cell
*vp
, const char *s
) /* set string val of a Cell */
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
);
349 if ((vp
->tval
& (NUM
| STR
)) == 0)
350 funnyvar(vp
, "assign to");
352 donerec
= false; /* mark $0 invalid */
353 fldno
= atoi(vp
->nval
);
356 DPRINTF("setting field %d to %s (%p)\n", fldno
, s
, (const void*)s
);
357 } else if (isrec(vp
)) {
358 donefld
= false; /* mark $1... invalid */
361 } else if (vp
== ofsloc
) {
365 t
= s
? tostring(s
) : tostring(""); /* in case it's self-assign */
368 vp
->tval
&= ~(NUM
|DONTFREE
|CONVC
|CONVO
);
371 DPRINTF("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n",
372 (void*)vp
, NN(vp
->nval
), t
, (void*)t
, vp
->tval
, donerec
, donefld
);
374 if (&vp
->fval
== NF
) {
375 donerec
= false; /* mark $0 invalid */
378 DPRINTF("setting NF to %g\n", f
);
384 Awkfloat
getfval(Cell
*vp
) /* get float val of a Cell */
386 if ((vp
->tval
& (NUM
| STR
)) == 0)
387 funnyvar(vp
, "read value of");
388 if (isfld(vp
) && !donefld
)
390 else if (isrec(vp
) && !donerec
)
392 if (!isnum(vp
)) { /* not a number */
396 if (is_valid_number(vp
->sval
, true, & no_trailing
, & fval
)) {
398 if (no_trailing
&& !(vp
->tval
&CON
))
399 vp
->tval
|= NUM
; /* make NUM only sparingly */
403 DPRINTF("getfval %p: %s = %g, t=%o\n",
404 (void*)vp
, NN(vp
->nval
), vp
->fval
, vp
->tval
);
408 static const char *get_inf_nan(double d
)
411 return (d
< 0 ? "-inf" : "+inf");
412 } else if (isnan(d
)) {
413 return (signbit(d
) != 0 ? "-nan" : "+nan");
418 static char *get_str_val(Cell
*vp
, char **fmt
) /* get string val of a Cell */
424 if ((vp
->tval
& (NUM
| STR
)) == 0)
425 funnyvar(vp
, "read value of");
426 if (isfld(vp
) && ! donefld
)
428 else if (isrec(vp
) && ! donerec
)
432 * ADR: This is complicated and more fragile than is desirable.
433 * Retrieving a string value for a number associates the string
434 * value with the scalar. Previously, the string value was
435 * sticky, meaning if converted via OFMT that became the value
436 * (even though POSIX wants it to be via CONVFMT). Or if CONVFMT
437 * changed after a string value was retrieved, the original value
438 * was maintained and used. Also not per POSIX.
440 * We work around this design by adding two additional flags,
441 * CONVC and CONVO, indicating how the string value was
442 * obtained (via CONVFMT or OFMT) and _also_ maintaining a copy
443 * of the pointer to the xFMT format string used for the
444 * conversion. This pointer is only read, **never** dereferenced.
445 * The next time we do a conversion, if it's coming from the same
446 * xFMT as last time, and the pointer value is different, we
447 * know that the xFMT format string changed, and we need to
448 * redo the conversion. If it's the same, we don't have to.
450 * There are also several cases where we don't do a conversion,
451 * such as for a field (see the checks below).
454 /* Don't duplicate the code for actually updating the value */
455 #define update_str_val(vp) \
459 if ((p = get_inf_nan(vp->fval)) != NULL) \
461 else if (modf(vp->fval, &dtemp) == 0) /* it's integral */ \
462 snprintf(s, sizeof (s), "%.30g", vp->fval); \
464 snprintf(s, sizeof (s), *fmt, vp->fval); \
465 vp->sval = tostring(s); \
466 vp->tval &= ~DONTFREE; \
470 if (isstr(vp
) == 0) {
481 } else if ((vp
->tval
& DONTFREE
) != 0 || ! isnum(vp
) || isfld(vp
)) {
483 } else if (isstr(vp
)) {
485 if ((vp
->tval
& CONVC
) != 0
486 || ((vp
->tval
& CONVO
) != 0 && vp
->fmt
!= *fmt
)) {
494 if ((vp
->tval
& CONVO
) != 0
495 || ((vp
->tval
& CONVC
) != 0 && vp
->fmt
!= *fmt
)) {
504 DPRINTF("getsval %p: %s = \"%s (%p)\", t=%o\n",
505 (void*)vp
, NN(vp
->nval
), vp
->sval
, (void*)vp
->sval
, vp
->tval
);
509 char *getsval(Cell
*vp
) /* get string val of a Cell */
511 return get_str_val(vp
, CONVFMT
);
514 char *getpssval(Cell
*vp
) /* get string val of a Cell for print */
516 return get_str_val(vp
, OFMT
);
520 char *tostring(const char *s
) /* make a copy of string s */
524 FATAL("out of space in tostring on %s", s
);
528 char *tostringN(const char *s
, size_t n
) /* make a copy of string s */
532 p
= (char *) malloc(n
);
534 FATAL("out of space in tostring on %s", s
);
539 Cell
*catstr(Cell
*a
, Cell
*b
) /* concatenate a and b */
543 char *sa
= getsval(a
);
544 char *sb
= getsval(b
);
545 size_t l
= strlen(sa
) + strlen(sb
) + 1;
546 p
= (char *) malloc(l
);
548 FATAL("out of space concatenating %s and %s", sa
, sb
);
549 snprintf(p
, l
, "%s%s", sa
, sb
);
551 l
++; // add room for ' '
552 char *newbuf
= (char *) malloc(l
);
554 FATAL("out of space concatenating %s and %s", sa
, sb
);
555 // See string() in lex.c; a string "xx" is stored in the symbol
557 snprintf(newbuf
, l
, "%s ", p
);
558 c
= setsymtab(newbuf
, p
, 0.0, CON
|STR
|DONTFREE
, symtab
);
564 char *qstring(const char *is
, int delim
) /* collect string up to next delim */
567 const uschar
*s
= (const uschar
*) is
;
570 if ((buf
= (uschar
*) malloc(strlen(is
)+3)) == NULL
)
571 FATAL( "out of space in qstring(%s)", s
);
572 for (bp
= buf
; (c
= *s
) != delim
; s
++) {
574 SYNTAX( "newline in string %.20s...", is
);
577 else { /* \something */
579 if (c
== 0) { /* \ at end */
581 break; /* for loop */
584 case '\\': *bp
++ = '\\'; break;
585 case 'n': *bp
++ = '\n'; break;
586 case 't': *bp
++ = '\t'; break;
587 case 'b': *bp
++ = '\b'; break;
588 case 'f': *bp
++ = '\f'; break;
589 case 'r': *bp
++ = '\r'; break;
590 case 'v': *bp
++ = '\v'; break;
591 case 'a': *bp
++ = '\a'; break;
599 n
= 8 * n
+ *++s
- '0';
601 n
= 8 * n
+ *++s
- '0';
612 const char *flags2str(int flags
)
614 static const struct ftab
{
620 { "DONTFREE", DONTFREE
},
630 static char buf
[100];
634 for (i
= 0; flagtab
[i
].name
!= NULL
; i
++) {
635 if ((flags
& flagtab
[i
].value
) != 0) {
638 strcpy(cp
, flagtab
[i
].name
);