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