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