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