1 /* $OpenBSD: lib.c,v 1.45 2021/04/19 14:08:55 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 } else { 244 if ((sep = *rs) == 0) { 245 sep = '\n'; 246 while ((c=getc(inf)) == '\n' && c != EOF) /* skip leading \n's */ 247 ; 248 if (c != EOF) 249 ungetc(c, inf); 250 } 251 for (rr = buf; ; ) { 252 for (; (c=getc(inf)) != sep && c != EOF; ) { 253 if (rr-buf+1 > bufsize) 254 if (!adjbuf(&buf, &bufsize, 1+rr-buf, 255 recsize, &rr, "readrec 1")) 256 FATAL("input record `%.30s...' too long", buf); 257 *rr++ = c; 258 } 259 if (*rs == sep || c == EOF) 260 break; 261 if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */ 262 break; 263 if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr, 264 "readrec 2")) 265 FATAL("input record `%.30s...' too long", buf); 266 *rr++ = '\n'; 267 *rr++ = c; 268 } 269 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3")) 270 FATAL("input record `%.30s...' too long", buf); 271 *rr = 0; 272 } 273 *pbuf = buf; 274 *pbufsize = bufsize; 275 isrec = *buf || !feof(inf); 276 DPRINTF("readrec saw <%s>, returns %d\n", buf, isrec); 277 return isrec; 278 } 279 280 char *getargv(int n) /* get ARGV[n] */ 281 { 282 Cell *x; 283 char *s, temp[50]; 284 extern Array *ARGVtab; 285 286 snprintf(temp, sizeof(temp), "%d", n); 287 if (lookup(temp, ARGVtab) == NULL) 288 return NULL; 289 x = setsymtab(temp, "", 0.0, STR, ARGVtab); 290 s = getsval(x); 291 DPRINTF("getargv(%d) returns |%s|\n", n, s); 292 return s; 293 } 294 295 void setclvar(char *s) /* set var=value from s */ 296 { 297 char *p; 298 Cell *q; 299 double result; 300 301 for (p=s; *p != '='; p++) 302 ; 303 *p++ = 0; 304 p = qstring(p, '\0'); 305 q = setsymtab(s, p, 0.0, STR, symtab); 306 setsval(q, p); 307 if (is_number(q->sval, & result)) { 308 q->fval = result; 309 q->tval |= NUM; 310 } 311 DPRINTF("command line set %s to |%s|\n", s, p); 312 } 313 314 315 void fldbld(void) /* create fields from current record */ 316 { 317 /* this relies on having fields[] the same length as $0 */ 318 /* the fields are all stored in this one array with \0's */ 319 /* possibly with a final trailing \0 not associated with any field */ 320 char *r, *fr, sep; 321 Cell *p; 322 int i, j, n; 323 324 if (donefld) 325 return; 326 if (!isstr(fldtab[0])) 327 getsval(fldtab[0]); 328 r = fldtab[0]->sval; 329 n = strlen(r); 330 if (n > fieldssize) { 331 xfree(fields); 332 if ((fields = (char *) malloc(n+2)) == NULL) /* possibly 2 final \0s */ 333 FATAL("out of space for fields in fldbld %d", n); 334 fieldssize = n; 335 } 336 fr = fields; 337 i = 0; /* number of fields accumulated here */ 338 if (inputFS == NULL) /* make sure we have a copy of FS */ 339 savefs(); 340 if (strlen(inputFS) > 1) { /* it's a regular expression */ 341 i = refldbld(r, inputFS); 342 } else if ((sep = *inputFS) == ' ') { /* default whitespace */ 343 for (i = 0; ; ) { 344 while (*r == ' ' || *r == '\t' || *r == '\n') 345 r++; 346 if (*r == 0) 347 break; 348 i++; 349 if (i > nfields) 350 growfldtab(i); 351 if (freeable(fldtab[i])) 352 xfree(fldtab[i]->sval); 353 fldtab[i]->sval = fr; 354 fldtab[i]->tval = FLD | STR | DONTFREE; 355 do 356 *fr++ = *r++; 357 while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0'); 358 *fr++ = 0; 359 } 360 *fr = 0; 361 } else if ((sep = *inputFS) == 0) { /* new: FS="" => 1 char/field */ 362 for (i = 0; *r != '\0'; r += n) { 363 char buf[MB_LEN_MAX + 1]; 364 365 i++; 366 if (i > nfields) 367 growfldtab(i); 368 if (freeable(fldtab[i])) 369 xfree(fldtab[i]->sval); 370 n = mblen(r, MB_LEN_MAX); 371 if (n < 0) 372 n = 1; 373 memcpy(buf, r, n); 374 buf[n] = '\0'; 375 fldtab[i]->sval = tostring(buf); 376 fldtab[i]->tval = FLD | STR; 377 } 378 *fr = 0; 379 } else if (*r != 0) { /* if 0, it's a null field */ 380 /* subtlecase : if length(FS) == 1 && length(RS > 0) 381 * \n is NOT a field separator (cf awk book 61,84). 382 * this variable is tested in the inner while loop. 383 */ 384 int rtest = '\n'; /* normal case */ 385 if (strlen(*RS) > 0) 386 rtest = '\0'; 387 for (;;) { 388 i++; 389 if (i > nfields) 390 growfldtab(i); 391 if (freeable(fldtab[i])) 392 xfree(fldtab[i]->sval); 393 fldtab[i]->sval = fr; 394 fldtab[i]->tval = FLD | STR | DONTFREE; 395 while (*r != sep && *r != rtest && *r != '\0') /* \n is always a separator */ 396 *fr++ = *r++; 397 *fr++ = 0; 398 if (*r++ == 0) 399 break; 400 } 401 *fr = 0; 402 } 403 if (i > nfields) 404 FATAL("record `%.30s...' has too many fields; can't happen", r); 405 cleanfld(i+1, lastfld); /* clean out junk from previous record */ 406 lastfld = i; 407 donefld = true; 408 for (j = 1; j <= lastfld; j++) { 409 double result; 410 411 p = fldtab[j]; 412 if(is_number(p->sval, & result)) { 413 p->fval = result; 414 p->tval |= NUM; 415 } 416 } 417 setfval(nfloc, (Awkfloat) lastfld); 418 donerec = true; /* restore */ 419 if (dbg) { 420 for (j = 0; j <= lastfld; j++) { 421 p = fldtab[j]; 422 printf("field %d (%s): |%s|\n", j, p->nval, p->sval); 423 } 424 } 425 } 426 427 void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */ 428 { /* nvals remain intact */ 429 Cell *p; 430 int i; 431 432 for (i = n1; i <= n2; i++) { 433 p = fldtab[i]; 434 if (freeable(p)) 435 xfree(p->sval); 436 p->sval = EMPTY, 437 p->tval = FLD | STR | DONTFREE; 438 } 439 } 440 441 void newfld(int n) /* add field n after end of existing lastfld */ 442 { 443 if (n > nfields) 444 growfldtab(n); 445 cleanfld(lastfld+1, n); 446 lastfld = n; 447 setfval(nfloc, (Awkfloat) n); 448 } 449 450 void setlastfld(int n) /* set lastfld cleaning fldtab cells if necessary */ 451 { 452 if (n < 0) 453 FATAL("cannot set NF to a negative value"); 454 if (n > nfields) 455 growfldtab(n); 456 457 if (lastfld < n) 458 cleanfld(lastfld+1, n); 459 else 460 cleanfld(n+1, lastfld); 461 462 lastfld = n; 463 } 464 465 Cell *fieldadr(int n) /* get nth field */ 466 { 467 if (n < 0) 468 FATAL("trying to access out of range field %d", n); 469 if (n > nfields) /* fields after NF are empty */ 470 growfldtab(n); /* but does not increase NF */ 471 return(fldtab[n]); 472 } 473 474 void growfldtab(int n) /* make new fields up to at least $n */ 475 { 476 int nf = 2 * nfields; 477 size_t s; 478 479 if (n > nf) 480 nf = n; 481 s = (nf+1) * (sizeof (struct Cell *)); /* freebsd: how much do we need? */ 482 if (s / sizeof(struct Cell *) - 1 == (size_t)nf) /* didn't overflow */ 483 fldtab = (Cell **) realloc(fldtab, s); 484 else /* overflow sizeof int */ 485 xfree(fldtab); /* make it null */ 486 if (fldtab == NULL) 487 FATAL("out of space creating %d fields", nf); 488 makefields(nfields+1, nf); 489 nfields = nf; 490 } 491 492 int refldbld(const char *rec, const char *fs) /* build fields from reg expr in FS */ 493 { 494 /* this relies on having fields[] the same length as $0 */ 495 /* the fields are all stored in this one array with \0's */ 496 char *fr; 497 int i, tempstat, n; 498 fa *pfa; 499 500 n = strlen(rec); 501 if (n > fieldssize) { 502 xfree(fields); 503 if ((fields = (char *) malloc(n+1)) == NULL) 504 FATAL("out of space for fields in refldbld %d", n); 505 fieldssize = n; 506 } 507 fr = fields; 508 *fr = '\0'; 509 if (*rec == '\0') 510 return 0; 511 pfa = makedfa(fs, 1); 512 DPRINTF("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs); 513 tempstat = pfa->initstat; 514 for (i = 1; ; i++) { 515 const size_t fss_rem = fields + fieldssize + 1 - fr; 516 if (i > nfields) 517 growfldtab(i); 518 if (freeable(fldtab[i])) 519 xfree(fldtab[i]->sval); 520 fldtab[i]->tval = FLD | STR | DONTFREE; 521 fldtab[i]->sval = fr; 522 DPRINTF("refldbld: i=%d\n", i); 523 if (nematch(pfa, rec)) { 524 const size_t reclen = patbeg - rec; 525 pfa->initstat = 2; /* horrible coupling to b.c */ 526 DPRINTF("match %s (%d chars)\n", patbeg, patlen); 527 if (reclen >= fss_rem) 528 FATAL("out of space for fields in refldbld"); 529 memcpy(fr, rec, reclen); 530 fr += reclen; 531 *fr++ = '\0'; 532 rec = patbeg + patlen; 533 } else { 534 DPRINTF("no match %s\n", rec); 535 if (strlcpy(fr, rec, fss_rem) >= fss_rem) 536 FATAL("out of space for fields in refldbld"); 537 pfa->initstat = tempstat; 538 break; 539 } 540 } 541 return i; 542 } 543 544 void recbld(void) /* create $0 from $1..$NF if necessary */ 545 { 546 int i; 547 char *r, *p; 548 char *sep = getsval(ofsloc); 549 550 if (donerec) 551 return; 552 r = record; 553 for (i = 1; i <= *NF; i++) { 554 p = getsval(fldtab[i]); 555 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1")) 556 FATAL("created $0 `%.30s...' too long", record); 557 while ((*r = *p++) != 0) 558 r++; 559 if (i < *NF) { 560 if (!adjbuf(&record, &recsize, 2+strlen(sep)+r-record, recsize, &r, "recbld 2")) 561 FATAL("created $0 `%.30s...' too long", record); 562 for (p = sep; (*r = *p++) != 0; ) 563 r++; 564 } 565 } 566 if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3")) 567 FATAL("built giant record `%.30s...'", record); 568 *r = '\0'; 569 DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]); 570 571 if (freeable(fldtab[0])) 572 xfree(fldtab[0]->sval); 573 fldtab[0]->tval = REC | STR | DONTFREE; 574 fldtab[0]->sval = record; 575 576 DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]); 577 DPRINTF("recbld = |%s|\n", record); 578 donerec = true; 579 } 580 581 int errorflag = 0; 582 583 void yyerror(const char *s) 584 { 585 SYNTAX("%s", s); 586 } 587 588 void SYNTAX(const char *fmt, ...) 589 { 590 extern char *cmdname, *curfname; 591 static int been_here = 0; 592 va_list varg; 593 594 if (been_here++ > 2) 595 return; 596 fprintf(stderr, "%s: ", cmdname); 597 va_start(varg, fmt); 598 vfprintf(stderr, fmt, varg); 599 va_end(varg); 600 fprintf(stderr, " at source line %d", lineno); 601 if (curfname != NULL) 602 fprintf(stderr, " in function %s", curfname); 603 if (compile_time == COMPILING && cursource() != NULL) 604 fprintf(stderr, " source file %s", cursource()); 605 fprintf(stderr, "\n"); 606 errorflag = 2; 607 eprint(); 608 } 609 610 extern int bracecnt, brackcnt, parencnt; 611 612 void bracecheck(void) 613 { 614 int c; 615 static int beenhere = 0; 616 617 if (beenhere++) 618 return; 619 while ((c = input()) != EOF && c != '\0') 620 bclass(c); 621 bcheck2(bracecnt, '{', '}'); 622 bcheck2(brackcnt, '[', ']'); 623 bcheck2(parencnt, '(', ')'); 624 } 625 626 void bcheck2(int n, int c1, int c2) 627 { 628 if (n == 1) 629 fprintf(stderr, "\tmissing %c\n", c2); 630 else if (n > 1) 631 fprintf(stderr, "\t%d missing %c's\n", n, c2); 632 else if (n == -1) 633 fprintf(stderr, "\textra %c\n", c2); 634 else if (n < -1) 635 fprintf(stderr, "\t%d extra %c's\n", -n, c2); 636 } 637 638 void FATAL(const char *fmt, ...) 639 { 640 extern char *cmdname; 641 va_list varg; 642 643 fflush(stdout); 644 fprintf(stderr, "%s: ", cmdname); 645 va_start(varg, fmt); 646 vfprintf(stderr, fmt, varg); 647 va_end(varg); 648 error(); 649 if (dbg > 1) /* core dump if serious debugging on */ 650 abort(); 651 exit(2); 652 } 653 654 void WARNING(const char *fmt, ...) 655 { 656 extern char *cmdname; 657 va_list varg; 658 659 fflush(stdout); 660 fprintf(stderr, "%s: ", cmdname); 661 va_start(varg, fmt); 662 vfprintf(stderr, fmt, varg); 663 va_end(varg); 664 error(); 665 } 666 667 void error() 668 { 669 extern Node *curnode; 670 671 fprintf(stderr, "\n"); 672 if (compile_time != ERROR_PRINTING) { 673 if (NR && *NR > 0) { 674 fprintf(stderr, " input record number %d", (int) (*FNR)); 675 if (strcmp(*FILENAME, "-") != 0) 676 fprintf(stderr, ", file %s", *FILENAME); 677 fprintf(stderr, "\n"); 678 } 679 if (curnode) 680 fprintf(stderr, " source line number %d", curnode->lineno); 681 else if (lineno) 682 fprintf(stderr, " source line number %d", lineno); 683 if (compile_time == COMPILING && cursource() != NULL) 684 fprintf(stderr, " source file %s", cursource()); 685 fprintf(stderr, "\n"); 686 eprint(); 687 } 688 } 689 690 void eprint(void) /* try to print context around error */ 691 { 692 char *p, *q; 693 int c; 694 static int been_here = 0; 695 extern char ebuf[], *ep; 696 697 if (compile_time != COMPILING || been_here++ > 0 || 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((uschar)*s)) 796 s++; 797 798 // no hex floating point, sorry 799 if (s[0] == '0' && tolower((uschar)s[1]) == 'x') 800 return false; 801 802 // allow +nan, -nan, +inf, -inf, any other letter, no 803 if (s[0] == '+' || s[0] == '-') { 804 is_nan = (strncasecmp(s+1, "nan", 3) == 0); 805 is_inf = (strncasecmp(s+1, "inf", 3) == 0); 806 if ((is_nan || is_inf) 807 && (isspace((uschar)s[4]) || s[4] == '\0')) 808 goto convert; 809 else if (! isdigit((uschar)s[1]) && s[1] != '.') 810 return false; 811 } 812 else if (! isdigit((uschar)s[0]) && s[0] != '.') 813 return false; 814 815 convert: 816 errno = 0; 817 r = strtod(s, &ep); 818 if (ep == s || errno == ERANGE) 819 return false; 820 821 if (isnan(r) && s[0] == '-' && signbit(r) == 0) 822 r = -r; 823 824 if (result != NULL) 825 *result = r; 826 827 retval = (isspace((uschar)*ep) || *ep == '\0' || trailing_stuff_ok); 828 829 if (no_trailing != NULL) 830 *no_trailing = (*ep == '\0'); 831 832 return retval; 833 } 834