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 <strings.h> 29 #include <ctype.h> 30 #include <errno.h> 31 #include <stdlib.h> 32 #include <stdarg.h> 33 #include <limits.h> 34 #include <math.h> 35 #include "awk.h" 36 37 char EMPTY[] = { '\0' }; 38 FILE *infile = NULL; 39 bool innew; /* true = infile has not been read by readrec */ 40 char *file = EMPTY; 41 char *record; 42 int recsize = RECSIZE; 43 char *fields; 44 int fieldssize = RECSIZE; 45 46 Cell **fldtab; /* pointers to Cells */ 47 static size_t len_inputFS = 0; 48 static char *inputFS = NULL; /* FS at time of input, for field splitting */ 49 50 #define MAXFLD 2 51 int nfields = MAXFLD; /* last allocated slot for $i */ 52 53 bool donefld; /* true = implies rec broken into fields */ 54 bool donerec; /* true = record is valid (no flds have changed) */ 55 56 int lastfld = 0; /* last used field */ 57 int argno = 1; /* current input argument number */ 58 extern Awkfloat *ARGC; 59 60 static Cell dollar0 = { OCELL, CFLD, NULL, EMPTY, 0.0, REC|STR|DONTFREE, NULL, NULL }; 61 static Cell dollar1 = { OCELL, CFLD, NULL, EMPTY, 0.0, FLD|STR|DONTFREE, NULL, NULL }; 62 63 void recinit(unsigned int n) 64 { 65 if ( (record = (char *) malloc(n)) == NULL 66 || (fields = (char *) malloc(n+1)) == NULL 67 || (fldtab = (Cell **) calloc(nfields+2, sizeof(*fldtab))) == NULL 68 || (fldtab[0] = (Cell *) malloc(sizeof(**fldtab))) == NULL) 69 FATAL("out of space for $0 and fields"); 70 *record = '\0'; 71 *fldtab[0] = dollar0; 72 fldtab[0]->sval = record; 73 fldtab[0]->nval = tostring("0"); 74 makefields(1, nfields); 75 } 76 77 void makefields(int n1, int n2) /* create $n1..$n2 inclusive */ 78 { 79 char temp[50]; 80 int i; 81 82 for (i = n1; i <= n2; i++) { 83 fldtab[i] = (Cell *) malloc(sizeof(**fldtab)); 84 if (fldtab[i] == NULL) 85 FATAL("out of space in makefields %d", i); 86 *fldtab[i] = dollar1; 87 snprintf(temp, sizeof(temp), "%d", i); 88 fldtab[i]->nval = tostring(temp); 89 } 90 } 91 92 void initgetrec(void) 93 { 94 int i; 95 char *p; 96 97 for (i = 1; i < *ARGC; i++) { 98 p = getargv(i); /* find 1st real filename */ 99 if (p == NULL || *p == '\0') { /* deleted or zapped */ 100 argno++; 101 continue; 102 } 103 if (!isclvar(p)) { 104 setsval(lookup("FILENAME", symtab), p); 105 return; 106 } 107 setclvar(p); /* a commandline assignment before filename */ 108 argno++; 109 } 110 infile = stdin; /* no filenames, so use stdin */ 111 innew = true; 112 } 113 114 /* 115 * POSIX specifies that fields are supposed to be evaluated as if they were 116 * split using the value of FS at the time that the record's value ($0) was 117 * read. 118 * 119 * Since field-splitting is done lazily, we save the current value of FS 120 * whenever a new record is read in (implicitly or via getline), or when 121 * a new value is assigned to $0. 122 */ 123 void savefs(void) 124 { 125 size_t len; 126 if ((len = strlen(getsval(fsloc))) < len_inputFS) { 127 strcpy(inputFS, *FS); /* for subsequent field splitting */ 128 return; 129 } 130 131 len_inputFS = len + 1; 132 inputFS = (char *) realloc(inputFS, len_inputFS); 133 if (inputFS == NULL) 134 FATAL("field separator %.10s... is too long", *FS); 135 memcpy(inputFS, *FS, len_inputFS); 136 } 137 138 static bool firsttime = true; 139 140 int getrec(char **pbuf, int *pbufsize, bool isrecord) /* get next input record */ 141 { /* note: cares whether buf == record */ 142 int c; 143 char *buf = *pbuf; 144 uschar saveb0; 145 int bufsize = *pbufsize, savebufsize = bufsize; 146 147 if (firsttime) { 148 firsttime = false; 149 initgetrec(); 150 } 151 DPRINTF("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n", 152 *RS, *FS, *ARGC, *FILENAME); 153 if (isrecord) { 154 donefld = false; 155 donerec = true; 156 savefs(); 157 } 158 saveb0 = buf[0]; 159 buf[0] = 0; 160 while (argno < *ARGC || infile == stdin) { 161 DPRINTF("argno=%d, file=|%s|\n", argno, file); 162 if (infile == NULL) { /* have to open a new file */ 163 file = getargv(argno); 164 if (file == NULL || *file == '\0') { /* deleted or zapped */ 165 argno++; 166 continue; 167 } 168 if (isclvar(file)) { /* a var=value arg */ 169 setclvar(file); 170 argno++; 171 continue; 172 } 173 *FILENAME = file; 174 DPRINTF("opening file %s\n", file); 175 if (*file == '-' && *(file+1) == '\0') 176 infile = stdin; 177 else if ((infile = fopen(file, "r")) == NULL) 178 FATAL("can't open file %s", file); 179 innew = true; 180 setfval(fnrloc, 0.0); 181 } 182 c = readrec(&buf, &bufsize, infile, innew); 183 if (innew) 184 innew = false; 185 if (c != 0 || buf[0] != '\0') { /* normal record */ 186 if (isrecord) { 187 double result; 188 189 if (freeable(fldtab[0])) 190 xfree(fldtab[0]->sval); 191 fldtab[0]->sval = buf; /* buf == record */ 192 fldtab[0]->tval = REC | STR | DONTFREE; 193 if (is_number(fldtab[0]->sval, & result)) { 194 fldtab[0]->fval = result; 195 fldtab[0]->tval |= NUM; 196 } 197 } 198 setfval(nrloc, nrloc->fval+1); 199 setfval(fnrloc, fnrloc->fval+1); 200 *pbuf = buf; 201 *pbufsize = bufsize; 202 return 1; 203 } 204 /* EOF arrived on this file; set up next */ 205 if (infile != stdin) 206 fclose(infile); 207 infile = NULL; 208 argno++; 209 } 210 buf[0] = saveb0; 211 *pbuf = buf; 212 *pbufsize = savebufsize; 213 return 0; /* true end of file */ 214 } 215 216 void nextfile(void) 217 { 218 if (infile != NULL && infile != stdin) 219 fclose(infile); 220 infile = NULL; 221 argno++; 222 } 223 224 int readrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag) /* read one record into buf */ 225 { 226 int sep, c, isrec; 227 char *rr, *buf = *pbuf; 228 int bufsize = *pbufsize; 229 char *rs = getsval(rsloc); 230 231 if (*rs && rs[1]) { 232 bool found; 233 234 fa *pfa = makedfa(rs, 1); 235 if (newflag) 236 found = fnematch(pfa, inf, &buf, &bufsize, recsize); 237 else { 238 int tempstat = pfa->initstat; 239 pfa->initstat = 2; 240 found = fnematch(pfa, inf, &buf, &bufsize, recsize); 241 pfa->initstat = tempstat; 242 } 243 if (found) 244 setptr(patbeg, '\0'); 245 isrec = (found == 0 && *buf == '\0') ? false : true; 246 } else { 247 if ((sep = *rs) == 0) { 248 sep = '\n'; 249 while ((c=getc(inf)) == '\n' && c != EOF) /* skip leading \n's */ 250 ; 251 if (c != EOF) 252 ungetc(c, inf); 253 } 254 for (rr = buf; ; ) { 255 for (; (c=getc(inf)) != sep && c != EOF; ) { 256 if (rr-buf+1 > bufsize) 257 if (!adjbuf(&buf, &bufsize, 1+rr-buf, 258 recsize, &rr, "readrec 1")) 259 FATAL("input record `%.30s...' too long", buf); 260 *rr++ = c; 261 } 262 if (*rs == sep || c == EOF) 263 break; 264 if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */ 265 break; 266 if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr, 267 "readrec 2")) 268 FATAL("input record `%.30s...' too long", buf); 269 *rr++ = '\n'; 270 *rr++ = c; 271 } 272 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3")) 273 FATAL("input record `%.30s...' too long", buf); 274 *rr = 0; 275 isrec = (c == EOF && rr == buf) ? false : true; 276 } 277 *pbuf = buf; 278 *pbufsize = bufsize; 279 DPRINTF("readrec saw <%s>, returns %d\n", buf, isrec); 280 return isrec; 281 } 282 283 char *getargv(int n) /* get ARGV[n] */ 284 { 285 Cell *x; 286 char *s, temp[50]; 287 extern Array *ARGVtab; 288 289 snprintf(temp, sizeof(temp), "%d", n); 290 if (lookup(temp, ARGVtab) == NULL) 291 return NULL; 292 x = setsymtab(temp, "", 0.0, STR, ARGVtab); 293 s = getsval(x); 294 DPRINTF("getargv(%d) returns |%s|\n", n, s); 295 return s; 296 } 297 298 void setclvar(char *s) /* set var=value from s */ 299 { 300 char *p; 301 Cell *q; 302 double result; 303 304 for (p=s; *p != '='; p++) 305 ; 306 *p++ = 0; 307 p = qstring(p, '\0'); 308 q = setsymtab(s, p, 0.0, STR, symtab); 309 setsval(q, p); 310 if (is_number(q->sval, & result)) { 311 q->fval = result; 312 q->tval |= NUM; 313 } 314 DPRINTF("command line set %s to |%s|\n", s, p); 315 } 316 317 318 void fldbld(void) /* create fields from current record */ 319 { 320 /* this relies on having fields[] the same length as $0 */ 321 /* the fields are all stored in this one array with \0's */ 322 /* possibly with a final trailing \0 not associated with any field */ 323 char *r, *fr, sep; 324 Cell *p; 325 int i, j, n; 326 327 if (donefld) 328 return; 329 if (!isstr(fldtab[0])) 330 getsval(fldtab[0]); 331 r = fldtab[0]->sval; 332 n = strlen(r); 333 if (n > fieldssize) { 334 xfree(fields); 335 if ((fields = (char *) malloc(n+2)) == NULL) /* possibly 2 final \0s */ 336 FATAL("out of space for fields in fldbld %d", n); 337 fieldssize = n; 338 } 339 fr = fields; 340 i = 0; /* number of fields accumulated here */ 341 if (inputFS == NULL) /* make sure we have a copy of FS */ 342 savefs(); 343 if (strlen(inputFS) > 1) { /* it's a regular expression */ 344 i = refldbld(r, inputFS); 345 } else if ((sep = *inputFS) == ' ') { /* default whitespace */ 346 for (i = 0; ; ) { 347 while (*r == ' ' || *r == '\t' || *r == '\n') 348 r++; 349 if (*r == 0) 350 break; 351 i++; 352 if (i > nfields) 353 growfldtab(i); 354 if (freeable(fldtab[i])) 355 xfree(fldtab[i]->sval); 356 fldtab[i]->sval = fr; 357 fldtab[i]->tval = FLD | STR | DONTFREE; 358 do 359 *fr++ = *r++; 360 while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0'); 361 *fr++ = 0; 362 } 363 *fr = 0; 364 } else if ((sep = *inputFS) == 0) { /* new: FS="" => 1 char/field */ 365 for (i = 0; *r != '\0'; r += n) { 366 char buf[MB_LEN_MAX + 1]; 367 368 i++; 369 if (i > nfields) 370 growfldtab(i); 371 if (freeable(fldtab[i])) 372 xfree(fldtab[i]->sval); 373 n = mblen(r, MB_LEN_MAX); 374 if (n < 0) 375 n = 1; 376 memcpy(buf, r, n); 377 buf[n] = '\0'; 378 fldtab[i]->sval = tostring(buf); 379 fldtab[i]->tval = FLD | STR; 380 } 381 *fr = 0; 382 } else if (*r != 0) { /* if 0, it's a null field */ 383 /* subtlecase : if length(FS) == 1 && length(RS > 0) 384 * \n is NOT a field separator (cf awk book 61,84). 385 * this variable is tested in the inner while loop. 386 */ 387 int rtest = '\n'; /* normal case */ 388 if (strlen(*RS) > 0) 389 rtest = '\0'; 390 for (;;) { 391 i++; 392 if (i > nfields) 393 growfldtab(i); 394 if (freeable(fldtab[i])) 395 xfree(fldtab[i]->sval); 396 fldtab[i]->sval = fr; 397 fldtab[i]->tval = FLD | STR | DONTFREE; 398 while (*r != sep && *r != rtest && *r != '\0') /* \n is always a separator */ 399 *fr++ = *r++; 400 *fr++ = 0; 401 if (*r++ == 0) 402 break; 403 } 404 *fr = 0; 405 } 406 if (i > nfields) 407 FATAL("record `%.30s...' has too many fields; can't happen", r); 408 cleanfld(i+1, lastfld); /* clean out junk from previous record */ 409 lastfld = i; 410 donefld = true; 411 for (j = 1; j <= lastfld; j++) { 412 double result; 413 414 p = fldtab[j]; 415 if(is_number(p->sval, & result)) { 416 p->fval = result; 417 p->tval |= NUM; 418 } 419 } 420 setfval(nfloc, (Awkfloat) lastfld); 421 donerec = true; /* restore */ 422 if (dbg) { 423 for (j = 0; j <= lastfld; j++) { 424 p = fldtab[j]; 425 printf("field %d (%s): |%s|\n", j, p->nval, p->sval); 426 } 427 } 428 } 429 430 void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */ 431 { /* nvals remain intact */ 432 Cell *p; 433 int i; 434 435 for (i = n1; i <= n2; i++) { 436 p = fldtab[i]; 437 if (freeable(p)) 438 xfree(p->sval); 439 p->sval = EMPTY, 440 p->tval = FLD | STR | DONTFREE; 441 } 442 } 443 444 void newfld(int n) /* add field n after end of existing lastfld */ 445 { 446 if (n > nfields) 447 growfldtab(n); 448 cleanfld(lastfld+1, n); 449 lastfld = n; 450 setfval(nfloc, (Awkfloat) n); 451 } 452 453 void setlastfld(int n) /* set lastfld cleaning fldtab cells if necessary */ 454 { 455 if (n < 0) 456 FATAL("cannot set NF to a negative value"); 457 if (n > nfields) 458 growfldtab(n); 459 460 if (lastfld < n) 461 cleanfld(lastfld+1, n); 462 else 463 cleanfld(n+1, lastfld); 464 465 lastfld = n; 466 } 467 468 Cell *fieldadr(int n) /* get nth field */ 469 { 470 if (n < 0) 471 FATAL("trying to access out of range field %d", n); 472 if (n > nfields) /* fields after NF are empty */ 473 growfldtab(n); /* but does not increase NF */ 474 return(fldtab[n]); 475 } 476 477 void growfldtab(int n) /* make new fields up to at least $n */ 478 { 479 int nf = 2 * nfields; 480 size_t s; 481 482 if (n > nf) 483 nf = n; 484 s = (nf+1) * (sizeof (struct Cell *)); /* freebsd: how much do we need? */ 485 if (s / sizeof(struct Cell *) - 1 == (size_t)nf) /* didn't overflow */ 486 fldtab = (Cell **) realloc(fldtab, s); 487 else /* overflow sizeof int */ 488 xfree(fldtab); /* make it null */ 489 if (fldtab == NULL) 490 FATAL("out of space creating %d fields", nf); 491 makefields(nfields+1, nf); 492 nfields = nf; 493 } 494 495 int refldbld(const char *rec, const char *fs) /* build fields from reg expr in FS */ 496 { 497 /* this relies on having fields[] the same length as $0 */ 498 /* the fields are all stored in this one array with \0's */ 499 char *fr; 500 int i, tempstat, n; 501 fa *pfa; 502 503 n = strlen(rec); 504 if (n > fieldssize) { 505 xfree(fields); 506 if ((fields = (char *) malloc(n+1)) == NULL) 507 FATAL("out of space for fields in refldbld %d", n); 508 fieldssize = n; 509 } 510 fr = fields; 511 *fr = '\0'; 512 if (*rec == '\0') 513 return 0; 514 pfa = makedfa(fs, 1); 515 DPRINTF("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs); 516 tempstat = pfa->initstat; 517 for (i = 1; ; i++) { 518 if (i > nfields) 519 growfldtab(i); 520 if (freeable(fldtab[i])) 521 xfree(fldtab[i]->sval); 522 fldtab[i]->tval = FLD | STR | DONTFREE; 523 fldtab[i]->sval = fr; 524 DPRINTF("refldbld: i=%d\n", i); 525 if (nematch(pfa, rec)) { 526 pfa->initstat = 2; /* horrible coupling to b.c */ 527 DPRINTF("match %s (%d chars)\n", patbeg, patlen); 528 strncpy(fr, rec, patbeg-rec); 529 fr += patbeg - rec + 1; 530 *(fr-1) = '\0'; 531 rec = patbeg + patlen; 532 } else { 533 DPRINTF("no match %s\n", rec); 534 strcpy(fr, rec); 535 pfa->initstat = tempstat; 536 break; 537 } 538 } 539 return i; 540 } 541 542 void recbld(void) /* create $0 from $1..$NF if necessary */ 543 { 544 int i; 545 char *r, *p; 546 char *sep = getsval(ofsloc); 547 548 if (donerec) 549 return; 550 r = record; 551 for (i = 1; i <= *NF; i++) { 552 p = getsval(fldtab[i]); 553 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1")) 554 FATAL("created $0 `%.30s...' too long", record); 555 while ((*r = *p++) != 0) 556 r++; 557 if (i < *NF) { 558 if (!adjbuf(&record, &recsize, 2+strlen(sep)+r-record, recsize, &r, "recbld 2")) 559 FATAL("created $0 `%.30s...' too long", record); 560 for (p = sep; (*r = *p++) != 0; ) 561 r++; 562 } 563 } 564 if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3")) 565 FATAL("built giant record `%.30s...'", record); 566 *r = '\0'; 567 DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]); 568 569 if (freeable(fldtab[0])) 570 xfree(fldtab[0]->sval); 571 fldtab[0]->tval = REC | STR | DONTFREE; 572 fldtab[0]->sval = record; 573 574 DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]); 575 DPRINTF("recbld = |%s|\n", record); 576 donerec = true; 577 } 578 579 int errorflag = 0; 580 581 void yyerror(const char *s) 582 { 583 SYNTAX("%s", s); 584 } 585 586 void SYNTAX(const char *fmt, ...) 587 { 588 extern char *cmdname, *curfname; 589 static int been_here = 0; 590 va_list varg; 591 592 if (been_here++ > 2) 593 return; 594 fprintf(stderr, "%s: ", cmdname); 595 va_start(varg, fmt); 596 vfprintf(stderr, fmt, varg); 597 va_end(varg); 598 fprintf(stderr, " at source line %d", lineno); 599 if (curfname != NULL) 600 fprintf(stderr, " in function %s", curfname); 601 if (compile_time == COMPILING && cursource() != NULL) 602 fprintf(stderr, " source file %s", cursource()); 603 fprintf(stderr, "\n"); 604 errorflag = 2; 605 eprint(); 606 } 607 608 extern int bracecnt, brackcnt, parencnt; 609 610 void bracecheck(void) 611 { 612 int c; 613 static int beenhere = 0; 614 615 if (beenhere++) 616 return; 617 while ((c = input()) != EOF && c != '\0') 618 bclass(c); 619 bcheck2(bracecnt, '{', '}'); 620 bcheck2(brackcnt, '[', ']'); 621 bcheck2(parencnt, '(', ')'); 622 } 623 624 void bcheck2(int n, int c1, int c2) 625 { 626 if (n == 1) 627 fprintf(stderr, "\tmissing %c\n", c2); 628 else if (n > 1) 629 fprintf(stderr, "\t%d missing %c's\n", n, c2); 630 else if (n == -1) 631 fprintf(stderr, "\textra %c\n", c2); 632 else if (n < -1) 633 fprintf(stderr, "\t%d extra %c's\n", -n, c2); 634 } 635 636 void FATAL(const char *fmt, ...) 637 { 638 extern char *cmdname; 639 va_list varg; 640 641 fflush(stdout); 642 fprintf(stderr, "%s: ", cmdname); 643 va_start(varg, fmt); 644 vfprintf(stderr, fmt, varg); 645 va_end(varg); 646 error(); 647 if (dbg > 1) /* core dump if serious debugging on */ 648 abort(); 649 exit(2); 650 } 651 652 void WARNING(const char *fmt, ...) 653 { 654 extern char *cmdname; 655 va_list varg; 656 657 fflush(stdout); 658 fprintf(stderr, "%s: ", cmdname); 659 va_start(varg, fmt); 660 vfprintf(stderr, fmt, varg); 661 va_end(varg); 662 error(); 663 } 664 665 void error() 666 { 667 extern Node *curnode; 668 669 fprintf(stderr, "\n"); 670 if (compile_time != ERROR_PRINTING) { 671 if (NR && *NR > 0) { 672 fprintf(stderr, " input record number %d", (int) (*FNR)); 673 if (strcmp(*FILENAME, "-") != 0) 674 fprintf(stderr, ", file %s", *FILENAME); 675 fprintf(stderr, "\n"); 676 } 677 if (curnode) 678 fprintf(stderr, " source line number %d", curnode->lineno); 679 else if (lineno) 680 fprintf(stderr, " source line number %d", lineno); 681 if (compile_time == COMPILING && cursource() != NULL) 682 fprintf(stderr, " source file %s", cursource()); 683 fprintf(stderr, "\n"); 684 eprint(); 685 } 686 } 687 688 void eprint(void) /* try to print context around error */ 689 { 690 char *p, *q; 691 int c; 692 static int been_here = 0; 693 extern char ebuf[], *ep; 694 695 if (compile_time != COMPILING || been_here++ > 0 || ebuf == ep) 696 return; 697 if (ebuf == ep) 698 return; 699 p = ep - 1; 700 if (p > ebuf && *p == '\n') 701 p--; 702 for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--) 703 ; 704 while (*p == '\n') 705 p++; 706 fprintf(stderr, " context is\n\t"); 707 for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--) 708 ; 709 for ( ; p < q; p++) 710 if (*p) 711 putc(*p, stderr); 712 fprintf(stderr, " >>> "); 713 for ( ; p < ep; p++) 714 if (*p) 715 putc(*p, stderr); 716 fprintf(stderr, " <<< "); 717 if (*ep) 718 while ((c = input()) != '\n' && c != '\0' && c != EOF) { 719 putc(c, stderr); 720 bclass(c); 721 } 722 putc('\n', stderr); 723 ep = ebuf; 724 } 725 726 void bclass(int c) 727 { 728 switch (c) { 729 case '{': bracecnt++; break; 730 case '}': bracecnt--; break; 731 case '[': brackcnt++; break; 732 case ']': brackcnt--; break; 733 case '(': parencnt++; break; 734 case ')': parencnt--; break; 735 } 736 } 737 738 double errcheck(double x, const char *s) 739 { 740 741 if (errno == EDOM) { 742 errno = 0; 743 WARNING("%s argument out of domain", s); 744 x = 1; 745 } else if (errno == ERANGE) { 746 errno = 0; 747 WARNING("%s result out of range", s); 748 x = 1; 749 } 750 return x; 751 } 752 753 int isclvar(const char *s) /* is s of form var=something ? */ 754 { 755 const char *os = s; 756 757 if (!isalpha((uschar) *s) && *s != '_') 758 return 0; 759 for ( ; *s; s++) 760 if (!(isalnum((uschar) *s) || *s == '_')) 761 break; 762 return *s == '=' && s > os; 763 } 764 765 /* strtod is supposed to be a proper test of what's a valid number */ 766 /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */ 767 /* wrong: violates 4.10.1.4 of ansi C standard */ 768 769 /* well, not quite. As of C99, hex floating point is allowed. so this is 770 * a bit of a mess. We work around the mess by checking for a hexadecimal 771 * value and disallowing it. Similarly, we now follow gawk and allow only 772 * +nan, -nan, +inf, and -inf for NaN and infinity values. 773 */ 774 775 /* 776 * This routine now has a more complicated interface, the main point 777 * being to avoid the double conversion of a string to double, and 778 * also to convey out, if requested, the information that the numeric 779 * value was a leading string or is all of the string. The latter bit 780 * is used in getfval(). 781 */ 782 783 bool is_valid_number(const char *s, bool trailing_stuff_ok, 784 bool *no_trailing, double *result) 785 { 786 double r; 787 char *ep; 788 bool retval = false; 789 bool is_nan = false; 790 bool is_inf = false; 791 792 if (no_trailing) 793 *no_trailing = false; 794 795 while (isspace(*s)) 796 s++; 797 798 /* 799 * This test, while allowed by newer POSIX standards, represents a regression 800 * where hex strings were treated as numbers in nawk the whole time it has been 801 * in FreeBSD (since 2001). The POSIX 2001 through 2004 standards mandated this 802 * behavior and the current standard allows it. Deviate from upstream by restoring 803 * the prior FreeBSD behavior. 804 */ 805 #if 0 806 // no hex floating point, sorry 807 if (s[0] == '0' && tolower(s[1]) == 'x') 808 return false; 809 #endif 810 811 // allow +nan, -nan, +inf, -inf, any other letter, no 812 if (s[0] == '+' || s[0] == '-') { 813 is_nan = (strncasecmp(s+1, "nan", 3) == 0); 814 is_inf = (strncasecmp(s+1, "inf", 3) == 0); 815 if ((is_nan || is_inf) 816 && (isspace(s[4]) || s[4] == '\0')) 817 goto convert; 818 else if (! isdigit(s[1]) && s[1] != '.') 819 return false; 820 } 821 else if (! isdigit(s[0]) && s[0] != '.') 822 return false; 823 824 convert: 825 errno = 0; 826 r = strtod(s, &ep); 827 if (ep == s || errno == ERANGE) 828 return false; 829 830 if (isnan(r) && s[0] == '-' && signbit(r) == 0) 831 r = -r; 832 833 if (result != NULL) 834 *result = r; 835 836 /* 837 * check for trailing stuff 838 */ 839 while (isspace(*ep)) 840 ep++; 841 842 if (no_trailing != NULL) 843 *no_trailing = (*ep == '\0'); 844 845 // return true if found the end, or trailing stuff is allowed 846 retval = *ep == '\0' || trailing_stuff_ok; 847 848 return retval; 849 } 850