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