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