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