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