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