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