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 <string.h> 28 #include <ctype.h> 29 #include <errno.h> 30 #include <stdlib.h> 31 #include <stdarg.h> 32 #include "awk.h" 33 #include "ytab.h" 34 35 FILE *infile = NULL; 36 char *file = ""; 37 char *record; 38 int recsize = RECSIZE; 39 char *fields; 40 int fieldssize = RECSIZE; 41 42 Cell **fldtab; /* pointers to Cells */ 43 char inputFS[100] = " "; 44 45 #define MAXFLD 200 46 int nfields = MAXFLD; /* last allocated slot for $i */ 47 48 int donefld; /* 1 = implies rec broken into fields */ 49 int donerec; /* 1 = record is valid (no flds have changed) */ 50 51 int lastfld = 0; /* last used field */ 52 int argno = 1; /* current input argument number */ 53 extern Awkfloat *ARGC; 54 55 static Cell dollar0 = { OCELL, CFLD, NULL, "", 0.0, REC|STR|DONTFREE }; 56 static Cell dollar1 = { OCELL, CFLD, NULL, "", 0.0, FLD|STR|DONTFREE }; 57 58 void recinit(unsigned int n) 59 { 60 record = (char *) malloc(n); 61 fields = (char *) malloc(n); 62 fldtab = (Cell **) malloc((nfields+1) * sizeof(Cell *)); 63 if (record == NULL || fields == NULL || fldtab == NULL) 64 FATAL("out of space for $0 and fields"); 65 66 fldtab[0] = (Cell *) malloc(sizeof (Cell)); 67 *fldtab[0] = dollar0; 68 fldtab[0]->sval = record; 69 fldtab[0]->nval = tostring("0"); 70 makefields(1, nfields); 71 } 72 73 void makefields(int n1, int n2) /* create $n1..$n2 inclusive */ 74 { 75 char temp[50]; 76 int i; 77 78 for (i = n1; i <= n2; i++) { 79 fldtab[i] = (Cell *) malloc(sizeof (struct Cell)); 80 if (fldtab[i] == NULL) 81 FATAL("out of space in makefields %d", i); 82 *fldtab[i] = dollar1; 83 sprintf(temp, "%d", i); 84 fldtab[i]->nval = tostring(temp); 85 } 86 } 87 88 void initgetrec(void) 89 { 90 int i; 91 char *p; 92 93 for (i = 1; i < *ARGC; i++) { 94 if (!isclvar(p = getargv(i))) { /* find 1st real filename */ 95 setsval(lookup("FILENAME", symtab), getargv(i)); 96 return; 97 } 98 setclvar(p); /* a commandline assignment before filename */ 99 argno++; 100 } 101 infile = stdin; /* no filenames, so use stdin */ 102 } 103 104 int getrec(char **pbuf, int *pbufsize, int isrecord) /* get next input record */ 105 { /* note: cares whether buf == record */ 106 int c; 107 static int firsttime = 1; 108 char *buf = *pbuf; 109 int bufsize = *pbufsize; 110 111 if (firsttime) { 112 firsttime = 0; 113 initgetrec(); 114 } 115 dprintf( ("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n", 116 *RS, *FS, *ARGC, *FILENAME) ); 117 if (isrecord) { 118 donefld = 0; 119 donerec = 1; 120 } 121 buf[0] = 0; 122 while (argno < *ARGC || infile == stdin) { 123 dprintf( ("argno=%d, file=|%s|\n", argno, file) ); 124 if (infile == NULL) { /* have to open a new file */ 125 file = getargv(argno); 126 if (*file == '\0') { /* it's been zapped */ 127 argno++; 128 continue; 129 } 130 if (isclvar(file)) { /* a var=value arg */ 131 setclvar(file); 132 argno++; 133 continue; 134 } 135 *FILENAME = file; 136 dprintf( ("opening file %s\n", file) ); 137 if (*file == '-' && *(file+1) == '\0') 138 infile = stdin; 139 else if ((infile = fopen(file, "r")) == NULL) 140 FATAL("can't open file %s", file); 141 setfval(fnrloc, 0.0); 142 } 143 c = readrec(&buf, &bufsize, infile); 144 if (c != 0 || buf[0] != '\0') { /* normal record */ 145 if (isrecord) { 146 if (freeable(fldtab[0])) 147 xfree(fldtab[0]->sval); 148 fldtab[0]->sval = buf; /* buf == record */ 149 fldtab[0]->tval = REC | STR | DONTFREE; 150 if (is_number(fldtab[0]->sval)) { 151 fldtab[0]->fval = atof(fldtab[0]->sval); 152 fldtab[0]->tval |= NUM; 153 } 154 } 155 setfval(nrloc, nrloc->fval+1); 156 setfval(fnrloc, fnrloc->fval+1); 157 *pbuf = buf; 158 *pbufsize = bufsize; 159 return 1; 160 } 161 /* EOF arrived on this file; set up next */ 162 if (infile != stdin) 163 fclose(infile); 164 infile = NULL; 165 argno++; 166 } 167 *pbuf = buf; 168 *pbufsize = bufsize; 169 return 0; /* true end of file */ 170 } 171 172 void nextfile(void) 173 { 174 if (infile != stdin) 175 fclose(infile); 176 infile = NULL; 177 argno++; 178 } 179 180 int readrec(char **pbuf, int *pbufsize, FILE *inf) /* read one record into buf */ 181 { 182 int sep, c; 183 char *rr, *buf = *pbuf; 184 int bufsize = *pbufsize; 185 186 if (strlen(*FS) >= sizeof(inputFS)) 187 FATAL("field separator %.10s... is too long", *FS); 188 strcpy(inputFS, *FS); /* for subsequent field splitting */ 189 if ((sep = **RS) == 0) { 190 sep = '\n'; 191 while ((c=getc(inf)) == '\n' && c != EOF) /* skip leading \n's */ 192 ; 193 if (c != EOF) 194 ungetc(c, inf); 195 } 196 for (rr = buf; ; ) { 197 for (; (c=getc(inf)) != sep && c != EOF; ) { 198 if (rr-buf+1 > bufsize) 199 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 1")) 200 FATAL("input record `%.30s...' too long", buf); 201 *rr++ = c; 202 } 203 if (**RS == sep || c == EOF) 204 break; 205 if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */ 206 break; 207 if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr, "readrec 2")) 208 FATAL("input record `%.30s...' too long", buf); 209 *rr++ = '\n'; 210 *rr++ = c; 211 } 212 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3")) 213 FATAL("input record `%.30s...' too long", buf); 214 *rr = 0; 215 dprintf( ("readrec saw <%s>, returns %d\n", buf, c == EOF && rr == buf ? 0 : 1) ); 216 *pbuf = buf; 217 *pbufsize = bufsize; 218 return c == EOF && rr == buf ? 0 : 1; 219 } 220 221 char *getargv(int n) /* get ARGV[n] */ 222 { 223 Cell *x; 224 char *s, temp[50]; 225 extern Array *ARGVtab; 226 227 sprintf(temp, "%d", n); 228 x = setsymtab(temp, "", 0.0, STR, ARGVtab); 229 s = getsval(x); 230 dprintf( ("getargv(%d) returns |%s|\n", n, s) ); 231 return s; 232 } 233 234 void setclvar(char *s) /* set var=value from s */ 235 { 236 char *p; 237 Cell *q; 238 239 for (p=s; *p != '='; p++) 240 ; 241 *p++ = 0; 242 p = qstring(p, '\0'); 243 q = setsymtab(s, p, 0.0, STR, symtab); 244 setsval(q, p); 245 if (is_number(q->sval)) { 246 q->fval = atof(q->sval); 247 q->tval |= NUM; 248 } 249 dprintf( ("command line set %s to |%s|\n", s, p) ); 250 } 251 252 253 void fldbld(void) /* create fields from current record */ 254 { 255 /* this relies on having fields[] the same length as $0 */ 256 /* the fields are all stored in this one array with \0's */ 257 char *r, *fr, sep; 258 Cell *p; 259 int i, j, n; 260 261 if (donefld) 262 return; 263 if (!isstr(fldtab[0])) 264 getsval(fldtab[0]); 265 r = fldtab[0]->sval; 266 n = strlen(r); 267 if (n > fieldssize) { 268 xfree(fields); 269 if ((fields = (char *) malloc(n+1)) == NULL) 270 FATAL("out of space for fields in fldbld %d", n); 271 fieldssize = n; 272 } 273 fr = fields; 274 i = 0; /* number of fields accumulated here */ 275 if (strlen(inputFS) > 1) { /* it's a regular expression */ 276 i = refldbld(r, inputFS); 277 } else if ((sep = *inputFS) == ' ') { /* default whitespace */ 278 for (i = 0; ; ) { 279 while (*r == ' ' || *r == '\t' || *r == '\n') 280 r++; 281 if (*r == 0) 282 break; 283 i++; 284 if (i > nfields) 285 growfldtab(i); 286 if (freeable(fldtab[i])) 287 xfree(fldtab[i]->sval); 288 fldtab[i]->sval = fr; 289 fldtab[i]->tval = FLD | STR | DONTFREE; 290 do 291 *fr++ = *r++; 292 while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0'); 293 *fr++ = 0; 294 } 295 *fr = 0; 296 } else if ((sep = *inputFS) == 0) { /* new: FS="" => 1 char/field */ 297 for (i = 0; *r != 0; r++) { 298 char buf[2]; 299 i++; 300 if (i > nfields) 301 growfldtab(i); 302 if (freeable(fldtab[i])) 303 xfree(fldtab[i]->sval); 304 buf[0] = *r; 305 buf[1] = 0; 306 fldtab[i]->sval = tostring(buf); 307 fldtab[i]->tval = FLD | STR; 308 } 309 *fr = 0; 310 } else if (*r != 0) { /* if 0, it's a null field */ 311 /* subtlecase : if length(FS) == 1 && length(RS > 0) 312 * \n is NOT a field separator (cf awk book 61,84). 313 * this variable is tested in the inner while loop. 314 */ 315 int rtest = '\n'; /* normal case */ 316 if (strlen(*RS) > 0) 317 rtest = '\0'; 318 for (;;) { 319 i++; 320 if (i > nfields) 321 growfldtab(i); 322 if (freeable(fldtab[i])) 323 xfree(fldtab[i]->sval); 324 fldtab[i]->sval = fr; 325 fldtab[i]->tval = FLD | STR | DONTFREE; 326 while (*r != sep && *r != rtest && *r != '\0') /* \n is always a separator */ 327 *fr++ = *r++; 328 *fr++ = 0; 329 if (*r++ == 0) 330 break; 331 } 332 *fr = 0; 333 } 334 if (i > nfields) 335 FATAL("record `%.30s...' has too many fields; can't happen", r); 336 cleanfld(i+1, lastfld); /* clean out junk from previous record */ 337 lastfld = i; 338 donefld = 1; 339 for (j = 1; j <= lastfld; j++) { 340 p = fldtab[j]; 341 if(is_number(p->sval)) { 342 p->fval = atof(p->sval); 343 p->tval |= NUM; 344 } 345 } 346 setfval(nfloc, (Awkfloat) lastfld); 347 if (dbg) { 348 for (j = 0; j <= lastfld; j++) { 349 p = fldtab[j]; 350 printf("field %d (%s): |%s|\n", j, p->nval, p->sval); 351 } 352 } 353 } 354 355 void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */ 356 { /* nvals remain intact */ 357 Cell *p; 358 int i; 359 360 for (i = n1; i <= n2; i++) { 361 p = fldtab[i]; 362 if (freeable(p)) 363 xfree(p->sval); 364 p->sval = ""; 365 p->tval = FLD | STR | DONTFREE; 366 } 367 } 368 369 void newfld(int n) /* add field n after end of existing lastfld */ 370 { 371 if (n > nfields) 372 growfldtab(n); 373 cleanfld(lastfld+1, n); 374 lastfld = n; 375 setfval(nfloc, (Awkfloat) n); 376 } 377 378 Cell *fieldadr(int n) /* get nth field */ 379 { 380 if (n < 0) 381 FATAL("trying to access field %d", n); 382 if (n > nfields) /* fields after NF are empty */ 383 growfldtab(n); /* but does not increase NF */ 384 return(fldtab[n]); 385 } 386 387 void growfldtab(int n) /* make new fields up to at least $n */ 388 { 389 int nf = 2 * nfields; 390 391 if (n > nf) 392 nf = n; 393 fldtab = (Cell **) realloc(fldtab, (nf+1) * (sizeof (struct Cell *))); 394 if (fldtab == NULL) 395 FATAL("out of space creating %d fields", nf); 396 makefields(nfields+1, nf); 397 nfields = nf; 398 } 399 400 int refldbld(const char *rec, const char *fs) /* build fields from reg expr in FS */ 401 { 402 /* this relies on having fields[] the same length as $0 */ 403 /* the fields are all stored in this one array with \0's */ 404 char *fr; 405 int i, tempstat, n; 406 fa *pfa; 407 408 n = strlen(rec); 409 if (n > fieldssize) { 410 xfree(fields); 411 if ((fields = (char *) malloc(n+1)) == NULL) 412 FATAL("out of space for fields in refldbld %d", n); 413 fieldssize = n; 414 } 415 fr = fields; 416 *fr = '\0'; 417 if (*rec == '\0') 418 return 0; 419 pfa = makedfa(fs, 1); 420 dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) ); 421 tempstat = pfa->initstat; 422 for (i = 1; ; i++) { 423 if (i > nfields) 424 growfldtab(i); 425 if (freeable(fldtab[i])) 426 xfree(fldtab[i]->sval); 427 fldtab[i]->tval = FLD | STR | DONTFREE; 428 fldtab[i]->sval = fr; 429 dprintf( ("refldbld: i=%d\n", i) ); 430 if (nematch(pfa, rec)) { 431 pfa->initstat = 2; /* horrible coupling to b.c */ 432 dprintf( ("match %s (%d chars)\n", patbeg, patlen) ); 433 strncpy(fr, rec, patbeg-rec); 434 fr += patbeg - rec + 1; 435 *(fr-1) = '\0'; 436 rec = patbeg + patlen; 437 } else { 438 dprintf( ("no match %s\n", rec) ); 439 strcpy(fr, rec); 440 pfa->initstat = tempstat; 441 break; 442 } 443 } 444 return i; 445 } 446 447 void recbld(void) /* create $0 from $1..$NF if necessary */ 448 { 449 int i; 450 char *r, *p; 451 452 if (donerec == 1) 453 return; 454 r = record; 455 for (i = 1; i <= *NF; i++) { 456 p = getsval(fldtab[i]); 457 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1")) 458 FATAL("created $0 `%.30s...' too long", record); 459 while ((*r = *p++) != 0) 460 r++; 461 if (i < *NF) { 462 if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2")) 463 FATAL("created $0 `%.30s...' too long", record); 464 for (p = *OFS; (*r = *p++) != 0; ) 465 r++; 466 } 467 } 468 if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3")) 469 FATAL("built giant record `%.30s...'", record); 470 *r = '\0'; 471 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) ); 472 473 if (freeable(fldtab[0])) 474 xfree(fldtab[0]->sval); 475 fldtab[0]->tval = REC | STR | DONTFREE; 476 fldtab[0]->sval = record; 477 478 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) ); 479 dprintf( ("recbld = |%s|\n", record) ); 480 donerec = 1; 481 } 482 483 int errorflag = 0; 484 485 void yyerror(const char *s) 486 { 487 SYNTAX(s); 488 } 489 490 void SYNTAX(const char *fmt, ...) 491 { 492 extern char *cmdname, *curfname; 493 static int been_here = 0; 494 va_list varg; 495 496 if (been_here++ > 2) 497 return; 498 fprintf(stderr, "%s: ", cmdname); 499 va_start(varg, fmt); 500 vfprintf(stderr, fmt, varg); 501 va_end(varg); 502 fprintf(stderr, " at source line %d", lineno); 503 if (curfname != NULL) 504 fprintf(stderr, " in function %s", curfname); 505 if (compile_time == 1 && cursource() != NULL) 506 fprintf(stderr, " source file %s", cursource()); 507 fprintf(stderr, "\n"); 508 errorflag = 2; 509 eprint(); 510 } 511 512 void fpecatch(int n) 513 { 514 FATAL("floating point exception %d", n); 515 } 516 517 extern int bracecnt, brackcnt, parencnt; 518 519 void bracecheck(void) 520 { 521 int c; 522 static int beenhere = 0; 523 524 if (beenhere++) 525 return; 526 while ((c = input()) != EOF && c != '\0') 527 bclass(c); 528 bcheck2(bracecnt, '{', '}'); 529 bcheck2(brackcnt, '[', ']'); 530 bcheck2(parencnt, '(', ')'); 531 } 532 533 void bcheck2(int n, int c1, int c2) 534 { 535 if (n == 1) 536 fprintf(stderr, "\tmissing %c\n", c2); 537 else if (n > 1) 538 fprintf(stderr, "\t%d missing %c's\n", n, c2); 539 else if (n == -1) 540 fprintf(stderr, "\textra %c\n", c2); 541 else if (n < -1) 542 fprintf(stderr, "\t%d extra %c's\n", -n, c2); 543 } 544 545 void FATAL(const char *fmt, ...) 546 { 547 extern char *cmdname; 548 va_list varg; 549 550 fflush(stdout); 551 fprintf(stderr, "%s: ", cmdname); 552 va_start(varg, fmt); 553 vfprintf(stderr, fmt, varg); 554 va_end(varg); 555 error(); 556 if (dbg > 1) /* core dump if serious debugging on */ 557 abort(); 558 exit(2); 559 } 560 561 void WARNING(const char *fmt, ...) 562 { 563 extern char *cmdname; 564 va_list varg; 565 566 fflush(stdout); 567 fprintf(stderr, "%s: ", cmdname); 568 va_start(varg, fmt); 569 vfprintf(stderr, fmt, varg); 570 va_end(varg); 571 error(); 572 } 573 574 void error() 575 { 576 extern Node *curnode; 577 578 fprintf(stderr, "\n"); 579 if (compile_time != 2 && NR && *NR > 0) { 580 fprintf(stderr, " input record number %d", (int) (*FNR)); 581 if (strcmp(*FILENAME, "-") != 0) 582 fprintf(stderr, ", file %s", *FILENAME); 583 fprintf(stderr, "\n"); 584 } 585 if (compile_time != 2 && curnode) 586 fprintf(stderr, " source line number %d", curnode->lineno); 587 else if (compile_time != 2 && lineno) 588 fprintf(stderr, " source line number %d", lineno); 589 if (compile_time == 1 && cursource() != NULL) 590 fprintf(stderr, " source file %s", cursource()); 591 fprintf(stderr, "\n"); 592 eprint(); 593 } 594 595 void eprint(void) /* try to print context around error */ 596 { 597 char *p, *q; 598 int c; 599 static int been_here = 0; 600 extern char ebuf[], *ep; 601 602 if (compile_time == 2 || compile_time == 0 || been_here++ > 0) 603 return; 604 p = ep - 1; 605 if (p > ebuf && *p == '\n') 606 p--; 607 for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--) 608 ; 609 while (*p == '\n') 610 p++; 611 fprintf(stderr, " context is\n\t"); 612 for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--) 613 ; 614 for ( ; p < q; p++) 615 if (*p) 616 putc(*p, stderr); 617 fprintf(stderr, " >>> "); 618 for ( ; p < ep; p++) 619 if (*p) 620 putc(*p, stderr); 621 fprintf(stderr, " <<< "); 622 if (*ep) 623 while ((c = input()) != '\n' && c != '\0' && c != EOF) { 624 putc(c, stderr); 625 bclass(c); 626 } 627 putc('\n', stderr); 628 ep = ebuf; 629 } 630 631 void bclass(int c) 632 { 633 switch (c) { 634 case '{': bracecnt++; break; 635 case '}': bracecnt--; break; 636 case '[': brackcnt++; break; 637 case ']': brackcnt--; break; 638 case '(': parencnt++; break; 639 case ')': parencnt--; break; 640 } 641 } 642 643 double errcheck(double x, const char *s) 644 { 645 646 if (errno == EDOM) { 647 errno = 0; 648 WARNING("%s argument out of domain", s); 649 x = 1; 650 } else if (errno == ERANGE) { 651 errno = 0; 652 WARNING("%s result out of range", s); 653 x = 1; 654 } 655 return x; 656 } 657 658 int isclvar(const char *s) /* is s of form var=something ? */ 659 { 660 const char *os = s; 661 662 if (!isalpha((uschar) *s) && *s != '_') 663 return 0; 664 for ( ; *s; s++) 665 if (!(isalnum((uschar) *s) || *s == '_')) 666 break; 667 return *s == '=' && s > os && *(s+1) != '='; 668 } 669 670 /* strtod is supposed to be a proper test of what's a valid number */ 671 /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */ 672 /* wrong: violates 4.10.1.4 of ansi C standard */ 673 674 #include <math.h> 675 int is_number(const char *s) 676 { 677 double r; 678 char *ep; 679 errno = 0; 680 r = strtod(s, &ep); 681 if (ep == s || r == HUGE_VAL || errno == ERANGE) 682 return 0; 683 while (*ep == ' ' || *ep == '\t' || *ep == '\n') 684 ep++; 685 if (*ep == '\0') 686 return 1; 687 else 688 return 0; 689 } 690