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