1 /* $OpenBSD: lib.c,v 1.48 2022/06/03 19:42:27 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 *e = '='; 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 const size_t fss_rem = fields + fieldssize + 1 - fr; 519 if (i > nfields) 520 growfldtab(i); 521 if (freeable(fldtab[i])) 522 xfree(fldtab[i]->sval); 523 fldtab[i]->tval = FLD | STR | DONTFREE; 524 fldtab[i]->sval = fr; 525 DPRINTF("refldbld: i=%d\n", i); 526 if (nematch(pfa, rec)) { 527 const size_t reclen = patbeg - rec; 528 pfa->initstat = 2; /* horrible coupling to b.c */ 529 DPRINTF("match %s (%d chars)\n", patbeg, patlen); 530 if (reclen >= fss_rem) 531 FATAL("out of space for fields in refldbld"); 532 memcpy(fr, rec, reclen); 533 fr += reclen; 534 *fr++ = '\0'; 535 rec = patbeg + patlen; 536 } else { 537 DPRINTF("no match %s\n", rec); 538 if (strlcpy(fr, rec, fss_rem) >= fss_rem) 539 FATAL("out of space for fields in refldbld"); 540 pfa->initstat = tempstat; 541 break; 542 } 543 } 544 return i; 545 } 546 547 void recbld(void) /* create $0 from $1..$NF if necessary */ 548 { 549 int i; 550 char *r, *p; 551 char *sep = getsval(ofsloc); 552 553 if (donerec) 554 return; 555 r = record; 556 for (i = 1; i <= *NF; i++) { 557 p = getsval(fldtab[i]); 558 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1")) 559 FATAL("created $0 `%.30s...' too long", record); 560 while ((*r = *p++) != 0) 561 r++; 562 if (i < *NF) { 563 if (!adjbuf(&record, &recsize, 2+strlen(sep)+r-record, recsize, &r, "recbld 2")) 564 FATAL("created $0 `%.30s...' too long", record); 565 for (p = sep; (*r = *p++) != 0; ) 566 r++; 567 } 568 } 569 if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3")) 570 FATAL("built giant record `%.30s...'", record); 571 *r = '\0'; 572 DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]); 573 574 if (freeable(fldtab[0])) 575 xfree(fldtab[0]->sval); 576 fldtab[0]->tval = REC | STR | DONTFREE; 577 fldtab[0]->sval = record; 578 579 DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]); 580 DPRINTF("recbld = |%s|\n", record); 581 donerec = true; 582 } 583 584 int errorflag = 0; 585 586 void yyerror(const char *s) 587 { 588 SYNTAX("%s", s); 589 } 590 591 void SYNTAX(const char *fmt, ...) 592 { 593 extern char *cmdname, *curfname; 594 static int been_here = 0; 595 va_list varg; 596 597 if (been_here++ > 2) 598 return; 599 fprintf(stderr, "%s: ", cmdname); 600 va_start(varg, fmt); 601 vfprintf(stderr, fmt, varg); 602 va_end(varg); 603 fprintf(stderr, " at source line %d", lineno); 604 if (curfname != NULL) 605 fprintf(stderr, " in function %s", curfname); 606 if (compile_time == COMPILING && cursource() != NULL) 607 fprintf(stderr, " source file %s", cursource()); 608 fprintf(stderr, "\n"); 609 errorflag = 2; 610 eprint(); 611 } 612 613 extern int bracecnt, brackcnt, parencnt; 614 615 void bracecheck(void) 616 { 617 int c; 618 static int beenhere = 0; 619 620 if (beenhere++) 621 return; 622 while ((c = input()) != EOF && c != '\0') 623 bclass(c); 624 bcheck2(bracecnt, '{', '}'); 625 bcheck2(brackcnt, '[', ']'); 626 bcheck2(parencnt, '(', ')'); 627 } 628 629 void bcheck2(int n, int c1, int c2) 630 { 631 if (n == 1) 632 fprintf(stderr, "\tmissing %c\n", c2); 633 else if (n > 1) 634 fprintf(stderr, "\t%d missing %c's\n", n, c2); 635 else if (n == -1) 636 fprintf(stderr, "\textra %c\n", c2); 637 else if (n < -1) 638 fprintf(stderr, "\t%d extra %c's\n", -n, c2); 639 } 640 641 void FATAL(const char *fmt, ...) 642 { 643 extern char *cmdname; 644 va_list varg; 645 646 fflush(stdout); 647 fprintf(stderr, "%s: ", cmdname); 648 va_start(varg, fmt); 649 vfprintf(stderr, fmt, varg); 650 va_end(varg); 651 error(); 652 if (dbg > 1) /* core dump if serious debugging on */ 653 abort(); 654 exit(2); 655 } 656 657 void WARNING(const char *fmt, ...) 658 { 659 extern char *cmdname; 660 va_list varg; 661 662 fflush(stdout); 663 fprintf(stderr, "%s: ", cmdname); 664 va_start(varg, fmt); 665 vfprintf(stderr, fmt, varg); 666 va_end(varg); 667 error(); 668 } 669 670 void error() 671 { 672 extern Node *curnode; 673 674 fprintf(stderr, "\n"); 675 if (compile_time != ERROR_PRINTING) { 676 if (NR && *NR > 0) { 677 fprintf(stderr, " input record number %d", (int) (*FNR)); 678 if (strcmp(*FILENAME, "-") != 0) 679 fprintf(stderr, ", file %s", *FILENAME); 680 fprintf(stderr, "\n"); 681 } 682 if (curnode) 683 fprintf(stderr, " source line number %d", curnode->lineno); 684 else if (lineno) 685 fprintf(stderr, " source line number %d", lineno); 686 if (compile_time == COMPILING && cursource() != NULL) 687 fprintf(stderr, " source file %s", cursource()); 688 fprintf(stderr, "\n"); 689 eprint(); 690 } 691 } 692 693 void eprint(void) /* try to print context around error */ 694 { 695 char *p, *q; 696 int c; 697 static int been_here = 0; 698 extern char ebuf[], *ep; 699 700 if (compile_time != COMPILING || been_here++ > 0 || ebuf == ep) 701 return; 702 p = ep - 1; 703 if (p > ebuf && *p == '\n') 704 p--; 705 for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--) 706 ; 707 while (*p == '\n') 708 p++; 709 fprintf(stderr, " context is\n\t"); 710 for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--) 711 ; 712 for ( ; p < q; p++) 713 if (*p) 714 putc(*p, stderr); 715 fprintf(stderr, " >>> "); 716 for ( ; p < ep; p++) 717 if (*p) 718 putc(*p, stderr); 719 fprintf(stderr, " <<< "); 720 if (*ep) 721 while ((c = input()) != '\n' && c != '\0' && c != EOF) { 722 putc(c, stderr); 723 bclass(c); 724 } 725 putc('\n', stderr); 726 ep = ebuf; 727 } 728 729 void bclass(int c) 730 { 731 switch (c) { 732 case '{': bracecnt++; break; 733 case '}': bracecnt--; break; 734 case '[': brackcnt++; break; 735 case ']': brackcnt--; break; 736 case '(': parencnt++; break; 737 case ')': parencnt--; break; 738 } 739 } 740 741 double errcheck(double x, const char *s) 742 { 743 744 if (errno == EDOM) { 745 errno = 0; 746 WARNING("%s argument out of domain", s); 747 x = 1; 748 } else if (errno == ERANGE) { 749 errno = 0; 750 WARNING("%s result out of range", s); 751 x = 1; 752 } 753 return x; 754 } 755 756 int isclvar(const char *s) /* is s of form var=something ? */ 757 { 758 const char *os = s; 759 760 if (!isalpha((uschar) *s) && *s != '_') 761 return 0; 762 for ( ; *s; s++) 763 if (!(isalnum((uschar) *s) || *s == '_')) 764 break; 765 return *s == '=' && s > os; 766 } 767 768 /* strtod is supposed to be a proper test of what's a valid number */ 769 /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */ 770 /* wrong: violates 4.10.1.4 of ansi C standard */ 771 772 /* well, not quite. As of C99, hex floating point is allowed. so this is 773 * a bit of a mess. We work around the mess by checking for a hexadecimal 774 * value and disallowing it. Similarly, we now follow gawk and allow only 775 * +nan, -nan, +inf, and -inf for NaN and infinity values. 776 */ 777 778 /* 779 * This routine now has a more complicated interface, the main point 780 * being to avoid the double conversion of a string to double, and 781 * also to convey out, if requested, the information that the numeric 782 * value was a leading string or is all of the string. The latter bit 783 * is used in getfval(). 784 */ 785 786 bool is_valid_number(const char *s, bool trailing_stuff_ok, 787 bool *no_trailing, double *result) 788 { 789 double r; 790 char *ep; 791 bool retval = false; 792 bool is_nan = false; 793 bool is_inf = false; 794 795 if (no_trailing) 796 *no_trailing = false; 797 798 while (isspace((uschar)*s)) 799 s++; 800 801 // no hex floating point, sorry 802 if (s[0] == '0' && tolower((uschar)s[1]) == 'x') 803 return false; 804 805 // allow +nan, -nan, +inf, -inf, any other letter, no 806 if (s[0] == '+' || s[0] == '-') { 807 is_nan = (strncasecmp(s+1, "nan", 3) == 0); 808 is_inf = (strncasecmp(s+1, "inf", 3) == 0); 809 if ((is_nan || is_inf) 810 && (isspace((uschar)s[4]) || s[4] == '\0')) 811 goto convert; 812 else if (! isdigit((uschar)s[1]) && s[1] != '.') 813 return false; 814 } 815 else if (! isdigit((uschar)s[0]) && s[0] != '.') 816 return false; 817 818 convert: 819 errno = 0; 820 r = strtod(s, &ep); 821 if (ep == s || errno == ERANGE) 822 return false; 823 824 if (isnan(r) && s[0] == '-' && signbit(r) == 0) 825 r = -r; 826 827 if (result != NULL) 828 *result = r; 829 830 /* 831 * check for trailing stuff 832 */ 833 while (isspace((uschar)*ep)) 834 ep++; 835 836 if (no_trailing != NULL) 837 *no_trailing = (*ep == '\0'); 838 839 // return true if found the end, or trailing stuff is allowed 840 retval = *ep == '\0' || trailing_stuff_ok; 841 842 return retval; 843 } 844