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