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