1 /**************************************************************** 2 Copyright (C) Lucent Technologies 1997 3 All Rights Reserved 4 5 Permission to use, copy, modify, and distribute this software and 6 its documentation for any purpose and without fee is hereby 7 granted, provided that the above copyright notice appear in all 8 copies and that both that the copyright notice and this 9 permission notice and warranty disclaimer appear in supporting 10 documentation, and that the name Lucent Technologies or any of 11 its entities not be used in advertising or publicity pertaining 12 to distribution of the software without specific, written prior 13 permission. 14 15 LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 16 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. 17 IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY 18 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 20 IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 21 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 22 THIS SOFTWARE. 23 ****************************************************************/ 24 25 #define DEBUG 26 #include <stdio.h> 27 #include <string.h> 28 #include <ctype.h> 29 #include <errno.h> 30 #include <stdlib.h> 31 #include <stdarg.h> 32 #include <limits.h> 33 #include "awk.h" 34 #include "ytab.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; 125 if ((len = strlen(getsval(fsloc))) < len_inputFS) { 126 strcpy(inputFS, *FS); /* for subsequent field splitting */ 127 return; 128 } 129 130 len_inputFS = len + 1; 131 inputFS = realloc(inputFS, len_inputFS); 132 if (inputFS == NULL) 133 FATAL("field separator %.10s... is too long", *FS); 134 memcpy(inputFS, *FS, len_inputFS); 135 } 136 137 static bool firsttime = true; 138 139 int getrec(char **pbuf, int *pbufsize, bool isrecord) /* get next input record */ 140 { /* note: cares whether buf == record */ 141 int c; 142 char *buf = *pbuf; 143 uschar saveb0; 144 int bufsize = *pbufsize, savebufsize = bufsize; 145 146 if (firsttime) { 147 firsttime = false; 148 initgetrec(); 149 } 150 dprintf( ("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n", 151 *RS, *FS, *ARGC, *FILENAME) ); 152 if (isrecord) { 153 donefld = false; 154 donerec = true; 155 savefs(); 156 } 157 saveb0 = buf[0]; 158 buf[0] = 0; 159 while (argno < *ARGC || infile == stdin) { 160 dprintf( ("argno=%d, file=|%s|\n", argno, file) ); 161 if (infile == NULL) { /* have to open a new file */ 162 file = getargv(argno); 163 if (file == NULL || *file == '\0') { /* deleted or zapped */ 164 argno++; 165 continue; 166 } 167 if (isclvar(file)) { /* a var=value arg */ 168 setclvar(file); 169 argno++; 170 continue; 171 } 172 *FILENAME = file; 173 dprintf( ("opening file %s\n", file) ); 174 if (*file == '-' && *(file+1) == '\0') 175 infile = stdin; 176 else if ((infile = fopen(file, "r")) == NULL) 177 FATAL("can't open file %s", file); 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 if (freeable(fldtab[0])) 186 xfree(fldtab[0]->sval); 187 fldtab[0]->sval = buf; /* buf == record */ 188 fldtab[0]->tval = REC | STR | DONTFREE; 189 if (is_number(fldtab[0]->sval)) { 190 fldtab[0]->fval = atof(fldtab[0]->sval); 191 fldtab[0]->tval |= NUM; 192 } 193 } 194 setfval(nrloc, nrloc->fval+1); 195 setfval(fnrloc, fnrloc->fval+1); 196 *pbuf = buf; 197 *pbufsize = bufsize; 198 return 1; 199 } 200 /* EOF arrived on this file; set up next */ 201 if (infile != stdin) 202 fclose(infile); 203 infile = NULL; 204 argno++; 205 } 206 buf[0] = saveb0; 207 *pbuf = buf; 208 *pbufsize = savebufsize; 209 return 0; /* true end of file */ 210 } 211 212 void nextfile(void) 213 { 214 if (infile != NULL && infile != stdin) 215 fclose(infile); 216 infile = NULL; 217 argno++; 218 } 219 220 int readrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag) /* read one record into buf */ 221 { 222 int sep, c, isrec; 223 char *rr, *buf = *pbuf; 224 int bufsize = *pbufsize; 225 char *rs = getsval(rsloc); 226 227 if (*rs && rs[1]) { 228 bool found; 229 230 fa *pfa = makedfa(rs, 1); 231 if (newflag) 232 found = fnematch(pfa, inf, &buf, &bufsize, recsize); 233 else { 234 int tempstat = pfa->initstat; 235 pfa->initstat = 2; 236 found = fnematch(pfa, inf, &buf, &bufsize, recsize); 237 pfa->initstat = tempstat; 238 } 239 if (found) 240 setptr(patbeg, '\0'); 241 } else { 242 if ((sep = *rs) == 0) { 243 sep = '\n'; 244 while ((c=getc(inf)) == '\n' && c != EOF) /* skip leading \n's */ 245 ; 246 if (c != EOF) 247 ungetc(c, inf); 248 } 249 for (rr = buf; ; ) { 250 for (; (c=getc(inf)) != sep && c != EOF; ) { 251 if (rr-buf+1 > bufsize) 252 if (!adjbuf(&buf, &bufsize, 1+rr-buf, 253 recsize, &rr, "readrec 1")) 254 FATAL("input record `%.30s...' too long", buf); 255 *rr++ = c; 256 } 257 if (*rs == sep || c == EOF) 258 break; 259 if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */ 260 break; 261 if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr, 262 "readrec 2")) 263 FATAL("input record `%.30s...' too long", buf); 264 *rr++ = '\n'; 265 *rr++ = c; 266 } 267 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3")) 268 FATAL("input record `%.30s...' too long", buf); 269 *rr = 0; 270 } 271 *pbuf = buf; 272 *pbufsize = bufsize; 273 isrec = *buf || !feof(inf); 274 dprintf( ("readrec saw <%s>, returns %d\n", buf, isrec) ); 275 return isrec; 276 } 277 278 char *getargv(int n) /* get ARGV[n] */ 279 { 280 Cell *x; 281 char *s, temp[50]; 282 extern Array *ARGVtab; 283 284 snprintf(temp, sizeof(temp), "%d", n); 285 if (lookup(temp, ARGVtab) == NULL) 286 return NULL; 287 x = setsymtab(temp, "", 0.0, STR, ARGVtab); 288 s = getsval(x); 289 dprintf( ("getargv(%d) returns |%s|\n", n, s) ); 290 return s; 291 } 292 293 void setclvar(char *s) /* set var=value from s */ 294 { 295 char *p; 296 Cell *q; 297 298 for (p=s; *p != '='; p++) 299 ; 300 *p++ = 0; 301 p = qstring(p, '\0'); 302 q = setsymtab(s, p, 0.0, STR, symtab); 303 setsval(q, p); 304 if (is_number(q->sval)) { 305 q->fval = atof(q->sval); 306 q->tval |= NUM; 307 } 308 dprintf( ("command line set %s to |%s|\n", s, p) ); 309 } 310 311 312 void fldbld(void) /* create fields from current record */ 313 { 314 /* this relies on having fields[] the same length as $0 */ 315 /* the fields are all stored in this one array with \0's */ 316 /* possibly with a final trailing \0 not associated with any field */ 317 char *r, *fr, sep; 318 Cell *p; 319 int i, j, n; 320 321 if (donefld) 322 return; 323 if (!isstr(fldtab[0])) 324 getsval(fldtab[0]); 325 r = fldtab[0]->sval; 326 n = strlen(r); 327 if (n > fieldssize) { 328 xfree(fields); 329 if ((fields = malloc(n+2)) == NULL) /* possibly 2 final \0s */ 330 FATAL("out of space for fields in fldbld %d", n); 331 fieldssize = n; 332 } 333 fr = fields; 334 i = 0; /* number of fields accumulated here */ 335 if (inputFS == NULL) /* make sure we have a copy of FS */ 336 savefs(); 337 if (strlen(inputFS) > 1) { /* it's a regular expression */ 338 i = refldbld(r, inputFS); 339 } else if ((sep = *inputFS) == ' ') { /* default whitespace */ 340 for (i = 0; ; ) { 341 while (*r == ' ' || *r == '\t' || *r == '\n') 342 r++; 343 if (*r == 0) 344 break; 345 i++; 346 if (i > nfields) 347 growfldtab(i); 348 if (freeable(fldtab[i])) 349 xfree(fldtab[i]->sval); 350 fldtab[i]->sval = fr; 351 fldtab[i]->tval = FLD | STR | DONTFREE; 352 do 353 *fr++ = *r++; 354 while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0'); 355 *fr++ = 0; 356 } 357 *fr = 0; 358 } else if ((sep = *inputFS) == 0) { /* new: FS="" => 1 char/field */ 359 for (i = 0; *r != '\0'; r += n) { 360 char buf[MB_LEN_MAX + 1]; 361 362 i++; 363 if (i > nfields) 364 growfldtab(i); 365 if (freeable(fldtab[i])) 366 xfree(fldtab[i]->sval); 367 n = mblen(r, MB_LEN_MAX); 368 if (n < 0) 369 n = 1; 370 memcpy(buf, r, n); 371 buf[n] = '\0'; 372 fldtab[i]->sval = tostring(buf); 373 fldtab[i]->tval = FLD | STR; 374 } 375 *fr = 0; 376 } else if (*r != 0) { /* if 0, it's a null field */ 377 /* subtlecase : if length(FS) == 1 && length(RS > 0) 378 * \n is NOT a field separator (cf awk book 61,84). 379 * this variable is tested in the inner while loop. 380 */ 381 int rtest = '\n'; /* normal case */ 382 if (strlen(*RS) > 0) 383 rtest = '\0'; 384 for (;;) { 385 i++; 386 if (i > nfields) 387 growfldtab(i); 388 if (freeable(fldtab[i])) 389 xfree(fldtab[i]->sval); 390 fldtab[i]->sval = fr; 391 fldtab[i]->tval = FLD | STR | DONTFREE; 392 while (*r != sep && *r != rtest && *r != '\0') /* \n is always a separator */ 393 *fr++ = *r++; 394 *fr++ = 0; 395 if (*r++ == 0) 396 break; 397 } 398 *fr = 0; 399 } 400 if (i > nfields) 401 FATAL("record `%.30s...' has too many fields; can't happen", r); 402 cleanfld(i+1, lastfld); /* clean out junk from previous record */ 403 lastfld = i; 404 donefld = true; 405 for (j = 1; j <= lastfld; j++) { 406 p = fldtab[j]; 407 if(is_number(p->sval)) { 408 p->fval = atof(p->sval); 409 p->tval |= NUM; 410 } 411 } 412 setfval(nfloc, (Awkfloat) lastfld); 413 donerec = true; /* restore */ 414 if (dbg) { 415 for (j = 0; j <= lastfld; j++) { 416 p = fldtab[j]; 417 printf("field %d (%s): |%s|\n", j, p->nval, p->sval); 418 } 419 } 420 } 421 422 void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */ 423 { /* nvals remain intact */ 424 Cell *p; 425 int i; 426 427 for (i = n1; i <= n2; i++) { 428 p = fldtab[i]; 429 if (freeable(p)) 430 xfree(p->sval); 431 p->sval = EMPTY, 432 p->tval = FLD | STR | DONTFREE; 433 } 434 } 435 436 void newfld(int n) /* add field n after end of existing lastfld */ 437 { 438 if (n > nfields) 439 growfldtab(n); 440 cleanfld(lastfld+1, n); 441 lastfld = n; 442 setfval(nfloc, (Awkfloat) n); 443 } 444 445 void setlastfld(int n) /* set lastfld cleaning fldtab cells if necessary */ 446 { 447 if (n < 0) 448 FATAL("cannot set NF to a negative value"); 449 if (n > nfields) 450 growfldtab(n); 451 452 if (lastfld < n) 453 cleanfld(lastfld+1, n); 454 else 455 cleanfld(n+1, lastfld); 456 457 lastfld = n; 458 } 459 460 Cell *fieldadr(int n) /* get nth field */ 461 { 462 if (n < 0) 463 FATAL("trying to access out of range field %d", n); 464 if (n > nfields) /* fields after NF are empty */ 465 growfldtab(n); /* but does not increase NF */ 466 return(fldtab[n]); 467 } 468 469 void growfldtab(int n) /* make new fields up to at least $n */ 470 { 471 int nf = 2 * nfields; 472 size_t s; 473 474 if (n > nf) 475 nf = n; 476 s = (nf+1) * (sizeof (struct Cell *)); /* freebsd: how much do we need? */ 477 if (s / sizeof(struct Cell *) - 1 == (size_t)nf) /* didn't overflow */ 478 fldtab = realloc(fldtab, s); 479 else /* overflow sizeof int */ 480 xfree(fldtab); /* make it null */ 481 if (fldtab == NULL) 482 FATAL("out of space creating %d fields", nf); 483 makefields(nfields+1, nf); 484 nfields = nf; 485 } 486 487 int refldbld(const char *rec, const char *fs) /* build fields from reg expr in FS */ 488 { 489 /* this relies on having fields[] the same length as $0 */ 490 /* the fields are all stored in this one array with \0's */ 491 char *fr; 492 int i, tempstat, n; 493 fa *pfa; 494 495 n = strlen(rec); 496 if (n > fieldssize) { 497 xfree(fields); 498 if ((fields = malloc(n+1)) == NULL) 499 FATAL("out of space for fields in refldbld %d", n); 500 fieldssize = n; 501 } 502 fr = fields; 503 *fr = '\0'; 504 if (*rec == '\0') 505 return 0; 506 pfa = makedfa(fs, 1); 507 dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) ); 508 tempstat = pfa->initstat; 509 for (i = 1; ; i++) { 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 pfa->initstat = 2; /* horrible coupling to b.c */ 519 dprintf( ("match %s (%d chars)\n", patbeg, patlen) ); 520 strncpy(fr, rec, patbeg-rec); 521 fr += patbeg - rec + 1; 522 *(fr-1) = '\0'; 523 rec = patbeg + patlen; 524 } else { 525 dprintf( ("no match %s\n", rec) ); 526 strcpy(fr, rec); 527 pfa->initstat = tempstat; 528 break; 529 } 530 } 531 return i; 532 } 533 534 void recbld(void) /* create $0 from $1..$NF if necessary */ 535 { 536 int i; 537 char *r, *p; 538 char *sep = getsval(ofsloc); 539 540 if (donerec) 541 return; 542 r = record; 543 for (i = 1; i <= *NF; i++) { 544 p = getsval(fldtab[i]); 545 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1")) 546 FATAL("created $0 `%.30s...' too long", record); 547 while ((*r = *p++) != 0) 548 r++; 549 if (i < *NF) { 550 if (!adjbuf(&record, &recsize, 2+strlen(sep)+r-record, recsize, &r, "recbld 2")) 551 FATAL("created $0 `%.30s...' too long", record); 552 for (p = sep; (*r = *p++) != 0; ) 553 r++; 554 } 555 } 556 if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3")) 557 FATAL("built giant record `%.30s...'", record); 558 *r = '\0'; 559 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) ); 560 561 if (freeable(fldtab[0])) 562 xfree(fldtab[0]->sval); 563 fldtab[0]->tval = REC | STR | DONTFREE; 564 fldtab[0]->sval = record; 565 566 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) ); 567 dprintf( ("recbld = |%s|\n", record) ); 568 donerec = true; 569 } 570 571 int errorflag = 0; 572 573 void yyerror(const char *s) 574 { 575 SYNTAX("%s", s); 576 } 577 578 void SYNTAX(const char *fmt, ...) 579 { 580 extern char *cmdname, *curfname; 581 static int been_here = 0; 582 va_list varg; 583 584 if (been_here++ > 2) 585 return; 586 fprintf(stderr, "%s: ", cmdname); 587 va_start(varg, fmt); 588 vfprintf(stderr, fmt, varg); 589 va_end(varg); 590 fprintf(stderr, " at source line %d", lineno); 591 if (curfname != NULL) 592 fprintf(stderr, " in function %s", curfname); 593 if (compile_time == COMPILING && cursource() != NULL) 594 fprintf(stderr, " source file %s", cursource()); 595 fprintf(stderr, "\n"); 596 errorflag = 2; 597 eprint(); 598 } 599 600 extern int bracecnt, brackcnt, parencnt; 601 602 void bracecheck(void) 603 { 604 int c; 605 static int beenhere = 0; 606 607 if (beenhere++) 608 return; 609 while ((c = input()) != EOF && c != '\0') 610 bclass(c); 611 bcheck2(bracecnt, '{', '}'); 612 bcheck2(brackcnt, '[', ']'); 613 bcheck2(parencnt, '(', ')'); 614 } 615 616 void bcheck2(int n, int c1, int c2) 617 { 618 if (n == 1) 619 fprintf(stderr, "\tmissing %c\n", c2); 620 else if (n > 1) 621 fprintf(stderr, "\t%d missing %c's\n", n, c2); 622 else if (n == -1) 623 fprintf(stderr, "\textra %c\n", c2); 624 else if (n < -1) 625 fprintf(stderr, "\t%d extra %c's\n", -n, c2); 626 } 627 628 void FATAL(const char *fmt, ...) 629 { 630 extern char *cmdname; 631 va_list varg; 632 633 fflush(stdout); 634 fprintf(stderr, "%s: ", cmdname); 635 va_start(varg, fmt); 636 vfprintf(stderr, fmt, varg); 637 va_end(varg); 638 error(); 639 if (dbg > 1) /* core dump if serious debugging on */ 640 abort(); 641 exit(2); 642 } 643 644 void WARNING(const char *fmt, ...) 645 { 646 extern char *cmdname; 647 va_list varg; 648 649 fflush(stdout); 650 fprintf(stderr, "%s: ", cmdname); 651 va_start(varg, fmt); 652 vfprintf(stderr, fmt, varg); 653 va_end(varg); 654 error(); 655 } 656 657 void error() 658 { 659 extern Node *curnode; 660 661 fprintf(stderr, "\n"); 662 if (compile_time != ERROR_PRINTING) { 663 if (NR && *NR > 0) { 664 fprintf(stderr, " input record number %d", (int) (*FNR)); 665 if (strcmp(*FILENAME, "-") != 0) 666 fprintf(stderr, ", file %s", *FILENAME); 667 fprintf(stderr, "\n"); 668 } 669 if (curnode) 670 fprintf(stderr, " source line number %d", curnode->lineno); 671 else if (lineno) 672 fprintf(stderr, " source line number %d", lineno); 673 } 674 675 if (compile_time == COMPILING && cursource() != NULL) 676 fprintf(stderr, " source file %s", cursource()); 677 fprintf(stderr, "\n"); 678 eprint(); 679 } 680 681 void eprint(void) /* try to print context around error */ 682 { 683 char *p, *q; 684 int c; 685 static int been_here = 0; 686 extern char ebuf[], *ep; 687 688 if (compile_time != COMPILING || been_here++ > 0 || ebuf == ep) 689 return; 690 if (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