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 for (;;) { 312 i++; 313 if (i > nfields) 314 growfldtab(i); 315 if (freeable(fldtab[i])) 316 xfree(fldtab[i]->sval); 317 fldtab[i]->sval = fr; 318 fldtab[i]->tval = FLD | STR | DONTFREE; 319 while (*r != sep && *r != '\n' && *r != '\0') /* \n is always a separator */ 320 *fr++ = *r++; 321 *fr++ = 0; 322 if (*r++ == 0) 323 break; 324 } 325 *fr = 0; 326 } 327 if (i > nfields) 328 FATAL("record `%.30s...' has too many fields; can't happen", r); 329 cleanfld(i+1, lastfld); /* clean out junk from previous record */ 330 lastfld = i; 331 donefld = 1; 332 for (j = 1; j <= lastfld; j++) { 333 p = fldtab[j]; 334 if(is_number(p->sval)) { 335 p->fval = atof(p->sval); 336 p->tval |= NUM; 337 } 338 } 339 setfval(nfloc, (Awkfloat) lastfld); 340 if (dbg) { 341 for (j = 0; j <= lastfld; j++) { 342 p = fldtab[j]; 343 printf("field %d (%s): |%s|\n", j, p->nval, p->sval); 344 } 345 } 346 } 347 348 void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */ 349 { /* nvals remain intact */ 350 Cell *p; 351 int i; 352 353 for (i = n1; i <= n2; i++) { 354 p = fldtab[i]; 355 if (freeable(p)) 356 xfree(p->sval); 357 p->sval = ""; 358 p->tval = FLD | STR | DONTFREE; 359 } 360 } 361 362 void newfld(int n) /* add field n after end of existing lastfld */ 363 { 364 if (n > nfields) 365 growfldtab(n); 366 cleanfld(lastfld+1, n); 367 lastfld = n; 368 setfval(nfloc, (Awkfloat) n); 369 } 370 371 Cell *fieldadr(int n) /* get nth field */ 372 { 373 if (n < 0) 374 FATAL("trying to access field %d", n); 375 if (n > nfields) /* fields after NF are empty */ 376 growfldtab(n); /* but does not increase NF */ 377 return(fldtab[n]); 378 } 379 380 void growfldtab(int n) /* make new fields up to at least $n */ 381 { 382 int nf = 2 * nfields; 383 384 if (n > nf) 385 nf = n; 386 fldtab = (Cell **) realloc(fldtab, (nf+1) * (sizeof (struct Cell *))); 387 if (fldtab == NULL) 388 FATAL("out of space creating %d fields", nf); 389 makefields(nfields+1, nf); 390 nfields = nf; 391 } 392 393 int refldbld(char *rec, char *fs) /* build fields from reg expr in FS */ 394 { 395 /* this relies on having fields[] the same length as $0 */ 396 /* the fields are all stored in this one array with \0's */ 397 char *fr; 398 int i, tempstat, n; 399 fa *pfa; 400 401 n = strlen(rec); 402 if (n > fieldssize) { 403 xfree(fields); 404 if ((fields = (char *) malloc(n+1)) == NULL) 405 FATAL("out of space for fields in refldbld %d", n); 406 fieldssize = n; 407 } 408 fr = fields; 409 *fr = '\0'; 410 if (*rec == '\0') 411 return 0; 412 pfa = makedfa(fs, 1); 413 dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) ); 414 tempstat = pfa->initstat; 415 for (i = 1; ; i++) { 416 if (i > nfields) 417 growfldtab(i); 418 if (freeable(fldtab[i])) 419 xfree(fldtab[i]->sval); 420 fldtab[i]->tval = FLD | STR | DONTFREE; 421 fldtab[i]->sval = fr; 422 dprintf( ("refldbld: i=%d\n", i) ); 423 if (nematch(pfa, rec)) { 424 pfa->initstat = 2; /* horrible coupling to b.c */ 425 dprintf( ("match %s (%d chars)\n", patbeg, patlen) ); 426 strncpy(fr, rec, patbeg-rec); 427 fr += patbeg - rec + 1; 428 *(fr-1) = '\0'; 429 rec = patbeg + patlen; 430 } else { 431 dprintf( ("no match %s\n", rec) ); 432 strcpy(fr, rec); 433 pfa->initstat = tempstat; 434 break; 435 } 436 } 437 return i; 438 } 439 440 void recbld(void) /* create $0 from $1..$NF if necessary */ 441 { 442 int i; 443 char *r, *p; 444 445 if (donerec == 1) 446 return; 447 r = record; 448 for (i = 1; i <= *NF; i++) { 449 p = getsval(fldtab[i]); 450 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1")) 451 FATAL("created $0 `%.30s...' too long", record); 452 while ((*r = *p++) != 0) 453 r++; 454 if (i < *NF) { 455 if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2")) 456 FATAL("created $0 `%.30s...' too long", record); 457 for (p = *OFS; (*r = *p++) != 0; ) 458 r++; 459 } 460 } 461 if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3")) 462 FATAL("built giant record `%.30s...'", record); 463 *r = '\0'; 464 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) ); 465 466 if (freeable(fldtab[0])) 467 xfree(fldtab[0]->sval); 468 fldtab[0]->tval = REC | STR | DONTFREE; 469 fldtab[0]->sval = record; 470 471 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) ); 472 dprintf( ("recbld = |%s|\n", record) ); 473 donerec = 1; 474 } 475 476 int errorflag = 0; 477 478 void yyerror(char *s) 479 { 480 SYNTAX(s); 481 } 482 483 void SYNTAX(char *fmt, ...) 484 { 485 extern char *cmdname, *curfname; 486 static int been_here = 0; 487 va_list varg; 488 489 if (been_here++ > 2) 490 return; 491 fprintf(stderr, "%s: ", cmdname); 492 va_start(varg, fmt); 493 vfprintf(stderr, fmt, varg); 494 va_end(varg); 495 fprintf(stderr, " at source line %d", lineno); 496 if (curfname != NULL) 497 fprintf(stderr, " in function %s", curfname); 498 if (compile_time == 1 && cursource() != NULL) 499 fprintf(stderr, " source file %s", cursource()); 500 fprintf(stderr, "\n"); 501 errorflag = 2; 502 eprint(); 503 } 504 505 void fpecatch(int n) 506 { 507 FATAL("floating point exception %d", n); 508 } 509 510 extern int bracecnt, brackcnt, parencnt; 511 512 void bracecheck(void) 513 { 514 int c; 515 static int beenhere = 0; 516 517 if (beenhere++) 518 return; 519 while ((c = input()) != EOF && c != '\0') 520 bclass(c); 521 bcheck2(bracecnt, '{', '}'); 522 bcheck2(brackcnt, '[', ']'); 523 bcheck2(parencnt, '(', ')'); 524 } 525 526 void bcheck2(int n, int c1, int c2) 527 { 528 if (n == 1) 529 fprintf(stderr, "\tmissing %c\n", c2); 530 else if (n > 1) 531 fprintf(stderr, "\t%d missing %c's\n", n, c2); 532 else if (n == -1) 533 fprintf(stderr, "\textra %c\n", c2); 534 else if (n < -1) 535 fprintf(stderr, "\t%d extra %c's\n", -n, c2); 536 } 537 538 void FATAL(char *fmt, ...) 539 { 540 extern char *cmdname; 541 va_list varg; 542 543 fflush(stdout); 544 fprintf(stderr, "%s: ", cmdname); 545 va_start(varg, fmt); 546 vfprintf(stderr, fmt, varg); 547 va_end(varg); 548 error(); 549 if (dbg > 1) /* core dump if serious debugging on */ 550 abort(); 551 exit(2); 552 } 553 554 void WARNING(char *fmt, ...) 555 { 556 extern char *cmdname; 557 va_list varg; 558 559 fflush(stdout); 560 fprintf(stderr, "%s: ", cmdname); 561 va_start(varg, fmt); 562 vfprintf(stderr, fmt, varg); 563 va_end(varg); 564 error(); 565 } 566 567 void error() 568 { 569 extern Node *curnode; 570 571 fprintf(stderr, "\n"); 572 if (compile_time != 2 && NR && *NR > 0) { 573 fprintf(stderr, " input record number %d", (int) (*FNR)); 574 if (strcmp(*FILENAME, "-") != 0) 575 fprintf(stderr, ", file %s", *FILENAME); 576 fprintf(stderr, "\n"); 577 } 578 if (compile_time != 2 && curnode) 579 fprintf(stderr, " source line number %d", curnode->lineno); 580 else if (compile_time != 2 && lineno) 581 fprintf(stderr, " source line number %d", lineno); 582 if (compile_time == 1 && cursource() != NULL) 583 fprintf(stderr, " source file %s", cursource()); 584 fprintf(stderr, "\n"); 585 eprint(); 586 } 587 588 void eprint(void) /* try to print context around error */ 589 { 590 char *p, *q; 591 int c; 592 static int been_here = 0; 593 extern char ebuf[], *ep; 594 595 if (compile_time == 2 || compile_time == 0 || been_here++ > 0) 596 return; 597 p = ep - 1; 598 if (p > ebuf && *p == '\n') 599 p--; 600 for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--) 601 ; 602 while (*p == '\n') 603 p++; 604 fprintf(stderr, " context is\n\t"); 605 for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--) 606 ; 607 for ( ; p < q; p++) 608 if (*p) 609 putc(*p, stderr); 610 fprintf(stderr, " >>> "); 611 for ( ; p < ep; p++) 612 if (*p) 613 putc(*p, stderr); 614 fprintf(stderr, " <<< "); 615 if (*ep) 616 while ((c = input()) != '\n' && c != '\0' && c != EOF) { 617 putc(c, stderr); 618 bclass(c); 619 } 620 putc('\n', stderr); 621 ep = ebuf; 622 } 623 624 void bclass(int c) 625 { 626 switch (c) { 627 case '{': bracecnt++; break; 628 case '}': bracecnt--; break; 629 case '[': brackcnt++; break; 630 case ']': brackcnt--; break; 631 case '(': parencnt++; break; 632 case ')': parencnt--; break; 633 } 634 } 635 636 double errcheck(double x, char *s) 637 { 638 639 if (errno == EDOM) { 640 errno = 0; 641 WARNING("%s argument out of domain", s); 642 x = 1; 643 } else if (errno == ERANGE) { 644 errno = 0; 645 WARNING("%s result out of range", s); 646 x = 1; 647 } 648 return x; 649 } 650 651 int isclvar(char *s) /* is s of form var=something ? */ 652 { 653 char *os = s; 654 655 if (!isalpha((uschar) *s) && *s != '_') 656 return 0; 657 for ( ; *s; s++) 658 if (!(isalnum((uschar) *s) || *s == '_')) 659 break; 660 return *s == '=' && s > os && *(s+1) != '='; 661 } 662 663 /* strtod is supposed to be a proper test of what's a valid number */ 664 /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */ 665 /* wrong: violates 4.10.1.4 of ansi C standard */ 666 667 #include <math.h> 668 int is_number(char *s) 669 { 670 double r; 671 char *ep; 672 errno = 0; 673 r = strtod(s, &ep); 674 if (ep == s || r == HUGE_VAL || errno == ERANGE) 675 return 0; 676 while (*ep == ' ' || *ep == '\t' || *ep == '\n') 677 ep++; 678 if (*ep == '\0') 679 return 1; 680 else 681 return 0; 682 } 683