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