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