Import awk-20110506.
[dragonfly.git] / contrib / awk / lib.c
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+1) * sizeof(Cell *))) == NULL
63           || (fldtab[0] = (Cell *) malloc(sizeof(Cell))) == NULL )
64                 FATAL("out of space for $0 and fields");
65         *fldtab[0] = dollar0;
66         fldtab[0]->sval = record;
67         fldtab[0]->nval = tostring("0");
68         makefields(1, nfields);
69 }
70
71 void makefields(int n1, int n2)         /* create $n1..$n2 inclusive */
72 {
73         char temp[50];
74         int i;
75
76         for (i = n1; i <= n2; i++) {
77                 fldtab[i] = (Cell *) malloc(sizeof (struct Cell));
78                 if (fldtab[i] == NULL)
79                         FATAL("out of space in makefields %d", i);
80                 *fldtab[i] = dollar1;
81                 sprintf(temp, "%d", i);
82                 fldtab[i]->nval = tostring(temp);
83         }
84 }
85
86 void initgetrec(void)
87 {
88         int i;
89         char *p;
90
91         for (i = 1; i < *ARGC; i++) {
92                 if (!isclvar(p = getargv(i))) { /* find 1st real filename */
93                         setsval(lookup("FILENAME", symtab), getargv(i));
94                         return;
95                 }
96                 setclvar(p);    /* a commandline assignment before filename */
97                 argno++;
98         }
99         infile = stdin;         /* no filenames, so use stdin */
100 }
101
102 static int firsttime = 1;
103
104 int getrec(char **pbuf, int *pbufsize, int isrecord)    /* get next input record */
105 {                       /* note: cares whether buf == record */
106         int c;
107         char *buf = *pbuf;
108         uschar saveb0;
109         int bufsize = *pbufsize, savebufsize = bufsize;
110
111         if (firsttime) {
112                 firsttime = 0;
113                 initgetrec();
114         }
115            dprintf( ("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n",
116                 *RS, *FS, *ARGC, *FILENAME) );
117         if (isrecord) {
118                 donefld = 0;
119                 donerec = 1;
120         }
121         saveb0 = buf[0];
122         buf[0] = 0;
123         while (argno < *ARGC || infile == stdin) {
124                    dprintf( ("argno=%d, file=|%s|\n", argno, file) );
125                 if (infile == NULL) {   /* have to open a new file */
126                         file = getargv(argno);
127                         if (*file == '\0') {    /* it's been zapped */
128                                 argno++;
129                                 continue;
130                         }
131                         if (isclvar(file)) {    /* a var=value arg */
132                                 setclvar(file);
133                                 argno++;
134                                 continue;
135                         }
136                         *FILENAME = file;
137                            dprintf( ("opening file %s\n", file) );
138                         if (*file == '-' && *(file+1) == '\0')
139                                 infile = stdin;
140                         else if ((infile = fopen(file, "r")) == NULL)
141                                 FATAL("can't open file %s", file);
142                         setfval(fnrloc, 0.0);
143                 }
144                 c = readrec(&buf, &bufsize, infile);
145                 if (c != 0 || buf[0] != '\0') { /* normal record */
146                         if (isrecord) {
147                                 if (freeable(fldtab[0]))
148                                         xfree(fldtab[0]->sval);
149                                 fldtab[0]->sval = buf;  /* buf == record */
150                                 fldtab[0]->tval = REC | STR | DONTFREE;
151                                 if (is_number(fldtab[0]->sval)) {
152                                         fldtab[0]->fval = atof(fldtab[0]->sval);
153                                         fldtab[0]->tval |= NUM;
154                                 }
155                         }
156                         setfval(nrloc, nrloc->fval+1);
157                         setfval(fnrloc, fnrloc->fval+1);
158                         *pbuf = buf;
159                         *pbufsize = bufsize;
160                         return 1;
161                 }
162                 /* EOF arrived on this file; set up next */
163                 if (infile != stdin)
164                         fclose(infile);
165                 infile = NULL;
166                 argno++;
167         }
168         buf[0] = saveb0;
169         *pbuf = buf;
170         *pbufsize = savebufsize;
171         return 0;       /* true end of file */
172 }
173
174 void nextfile(void)
175 {
176         if (infile != NULL && 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         /* possibly with a final trailing \0 not associated with any field */
260         char *r, *fr, sep;
261         Cell *p;
262         int i, j, n;
263
264         if (donefld)
265                 return;
266         if (!isstr(fldtab[0]))
267                 getsval(fldtab[0]);
268         r = fldtab[0]->sval;
269         n = strlen(r);
270         if (n > fieldssize) {
271                 xfree(fields);
272                 if ((fields = (char *) malloc(n+2)) == NULL) /* possibly 2 final \0s */
273                         FATAL("out of space for fields in fldbld %d", n);
274                 fieldssize = n;
275         }
276         fr = fields;
277         i = 0;  /* number of fields accumulated here */
278         strcpy(inputFS, *FS);
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                 /* subtlecase : if length(FS) == 1 && length(RS > 0)
316                  * \n is NOT a field separator (cf awk book 61,84).
317                  * this variable is tested in the inner while loop.
318                  */
319                 int rtest = '\n';  /* normal case */
320                 if (strlen(*RS) > 0)
321                         rtest = '\0';
322                 for (;;) {
323                         i++;
324                         if (i > nfields)
325                                 growfldtab(i);
326                         if (freeable(fldtab[i]))
327                                 xfree(fldtab[i]->sval);
328                         fldtab[i]->sval = fr;
329                         fldtab[i]->tval = FLD | STR | DONTFREE;
330                         while (*r != sep && *r != rtest && *r != '\0')  /* \n is always a separator */
331                                 *fr++ = *r++;
332                         *fr++ = 0;
333                         if (*r++ == 0)
334                                 break;
335                 }
336                 *fr = 0;
337         }
338         if (i > nfields)
339                 FATAL("record `%.30s...' has too many fields; can't happen", r);
340         cleanfld(i+1, lastfld); /* clean out junk from previous record */
341         lastfld = i;
342         donefld = 1;
343         for (j = 1; j <= lastfld; j++) {
344                 p = fldtab[j];
345                 if(is_number(p->sval)) {
346                         p->fval = atof(p->sval);
347                         p->tval |= NUM;
348                 }
349         }
350         setfval(nfloc, (Awkfloat) lastfld);
351         if (dbg) {
352                 for (j = 0; j <= lastfld; j++) {
353                         p = fldtab[j];
354                         printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
355                 }
356         }
357 }
358
359 void cleanfld(int n1, int n2)   /* clean out fields n1 .. n2 inclusive */
360 {                               /* nvals remain intact */
361         Cell *p;
362         int i;
363
364         for (i = n1; i <= n2; i++) {
365                 p = fldtab[i];
366                 if (freeable(p))
367                         xfree(p->sval);
368                 p->sval = "";
369                 p->tval = FLD | STR | DONTFREE;
370         }
371 }
372
373 void newfld(int n)      /* add field n after end of existing lastfld */
374 {
375         if (n > nfields)
376                 growfldtab(n);
377         cleanfld(lastfld+1, n);
378         lastfld = n;
379         setfval(nfloc, (Awkfloat) n);
380 }
381
382 Cell *fieldadr(int n)   /* get nth field */
383 {
384         if (n < 0)
385                 FATAL("trying to access out of range field %d", n);
386         if (n > nfields)        /* fields after NF are empty */
387                 growfldtab(n);  /* but does not increase NF */
388         return(fldtab[n]);
389 }
390
391 void growfldtab(int n)  /* make new fields up to at least $n */
392 {
393         int nf = 2 * nfields;
394         size_t s;
395
396         if (n > nf)
397                 nf = n;
398         s = (nf+1) * (sizeof (struct Cell *));  /* freebsd: how much do we need? */
399         if (s / sizeof(struct Cell *) - 1 == nf) /* didn't overflow */
400                 fldtab = (Cell **) realloc(fldtab, s);
401         else                                    /* overflow sizeof int */
402                 xfree(fldtab);  /* make it null */
403         if (fldtab == NULL)
404                 FATAL("out of space creating %d fields", nf);
405         makefields(nfields+1, nf);
406         nfields = nf;
407 }
408
409 int refldbld(const char *rec, const char *fs)   /* build fields from reg expr in FS */
410 {
411         /* this relies on having fields[] the same length as $0 */
412         /* the fields are all stored in this one array with \0's */
413         char *fr;
414         int i, tempstat, n;
415         fa *pfa;
416
417         n = strlen(rec);
418         if (n > fieldssize) {
419                 xfree(fields);
420                 if ((fields = (char *) malloc(n+1)) == NULL)
421                         FATAL("out of space for fields in refldbld %d", n);
422                 fieldssize = n;
423         }
424         fr = fields;
425         *fr = '\0';
426         if (*rec == '\0')
427                 return 0;
428         pfa = makedfa(fs, 1);
429            dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
430         tempstat = pfa->initstat;
431         for (i = 1; ; i++) {
432                 if (i > nfields)
433                         growfldtab(i);
434                 if (freeable(fldtab[i]))
435                         xfree(fldtab[i]->sval);
436                 fldtab[i]->tval = FLD | STR | DONTFREE;
437                 fldtab[i]->sval = fr;
438                    dprintf( ("refldbld: i=%d\n", i) );
439                 if (nematch(pfa, rec)) {
440                         pfa->initstat = 2;      /* horrible coupling to b.c */
441                            dprintf( ("match %s (%d chars)\n", patbeg, patlen) );
442                         strncpy(fr, rec, patbeg-rec);
443                         fr += patbeg - rec + 1;
444                         *(fr-1) = '\0';
445                         rec = patbeg + patlen;
446                 } else {
447                            dprintf( ("no match %s\n", rec) );
448                         strcpy(fr, rec);
449                         pfa->initstat = tempstat;
450                         break;
451                 }
452         }
453         return i;               
454 }
455
456 void recbld(void)       /* create $0 from $1..$NF if necessary */
457 {
458         int i;
459         char *r, *p;
460
461         if (donerec == 1)
462                 return;
463         r = record;
464         for (i = 1; i <= *NF; i++) {
465                 p = getsval(fldtab[i]);
466                 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
467                         FATAL("created $0 `%.30s...' too long", record);
468                 while ((*r = *p++) != 0)
469                         r++;
470                 if (i < *NF) {
471                         if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2"))
472                                 FATAL("created $0 `%.30s...' too long", record);
473                         for (p = *OFS; (*r = *p++) != 0; )
474                                 r++;
475                 }
476         }
477         if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
478                 FATAL("built giant record `%.30s...'", record);
479         *r = '\0';
480            dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
481
482         if (freeable(fldtab[0]))
483                 xfree(fldtab[0]->sval);
484         fldtab[0]->tval = REC | STR | DONTFREE;
485         fldtab[0]->sval = record;
486
487            dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
488            dprintf( ("recbld = |%s|\n", record) );
489         donerec = 1;
490 }
491
492 int     errorflag       = 0;
493
494 void yyerror(const char *s)
495 {
496         SYNTAX("%s", s);
497 }
498
499 void SYNTAX(const char *fmt, ...)
500 {
501         extern char *cmdname, *curfname;
502         static int been_here = 0;
503         va_list varg;
504
505         if (been_here++ > 2)
506                 return;
507         fprintf(stderr, "%s: ", cmdname);
508         va_start(varg, fmt);
509         vfprintf(stderr, fmt, varg);
510         va_end(varg);
511         fprintf(stderr, " at source line %d", lineno);
512         if (curfname != NULL)
513                 fprintf(stderr, " in function %s", curfname);
514         if (compile_time == 1 && cursource() != NULL)
515                 fprintf(stderr, " source file %s", cursource());
516         fprintf(stderr, "\n");
517         errorflag = 2;
518         eprint();
519 }
520
521 void fpecatch(int n)
522 {
523         FATAL("floating point exception %d", n);
524 }
525
526 extern int bracecnt, brackcnt, parencnt;
527
528 void bracecheck(void)
529 {
530         int c;
531         static int beenhere = 0;
532
533         if (beenhere++)
534                 return;
535         while ((c = input()) != EOF && c != '\0')
536                 bclass(c);
537         bcheck2(bracecnt, '{', '}');
538         bcheck2(brackcnt, '[', ']');
539         bcheck2(parencnt, '(', ')');
540 }
541
542 void bcheck2(int n, int c1, int c2)
543 {
544         if (n == 1)
545                 fprintf(stderr, "\tmissing %c\n", c2);
546         else if (n > 1)
547                 fprintf(stderr, "\t%d missing %c's\n", n, c2);
548         else if (n == -1)
549                 fprintf(stderr, "\textra %c\n", c2);
550         else if (n < -1)
551                 fprintf(stderr, "\t%d extra %c's\n", -n, c2);
552 }
553
554 void FATAL(const char *fmt, ...)
555 {
556         extern char *cmdname;
557         va_list varg;
558
559         fflush(stdout);
560         fprintf(stderr, "%s: ", cmdname);
561         va_start(varg, fmt);
562         vfprintf(stderr, fmt, varg);
563         va_end(varg);
564         error();
565         if (dbg > 1)            /* core dump if serious debugging on */
566                 abort();
567         exit(2);
568 }
569
570 void WARNING(const 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 }
582
583 void error()
584 {
585         extern Node *curnode;
586
587         fprintf(stderr, "\n");
588         if (compile_time != 2 && NR && *NR > 0) {
589                 fprintf(stderr, " input record number %d", (int) (*FNR));
590                 if (strcmp(*FILENAME, "-") != 0)
591                         fprintf(stderr, ", file %s", *FILENAME);
592                 fprintf(stderr, "\n");
593         }
594         if (compile_time != 2 && curnode)
595                 fprintf(stderr, " source line number %d", curnode->lineno);
596         else if (compile_time != 2 && lineno)
597                 fprintf(stderr, " source line number %d", lineno);
598         if (compile_time == 1 && cursource() != NULL)
599                 fprintf(stderr, " source file %s", cursource());
600         fprintf(stderr, "\n");
601         eprint();
602 }
603
604 void eprint(void)       /* try to print context around error */
605 {
606         char *p, *q;
607         int c;
608         static int been_here = 0;
609         extern char ebuf[], *ep;
610
611         if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
612                 return;
613         p = ep - 1;
614         if (p > ebuf && *p == '\n')
615                 p--;
616         for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
617                 ;
618         while (*p == '\n')
619                 p++;
620         fprintf(stderr, " context is\n\t");
621         for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
622                 ;
623         for ( ; p < q; p++)
624                 if (*p)
625                         putc(*p, stderr);
626         fprintf(stderr, " >>> ");
627         for ( ; p < ep; p++)
628                 if (*p)
629                         putc(*p, stderr);
630         fprintf(stderr, " <<< ");
631         if (*ep)
632                 while ((c = input()) != '\n' && c != '\0' && c != EOF) {
633                         putc(c, stderr);
634                         bclass(c);
635                 }
636         putc('\n', stderr);
637         ep = ebuf;
638 }
639
640 void bclass(int c)
641 {
642         switch (c) {
643         case '{': bracecnt++; break;
644         case '}': bracecnt--; break;
645         case '[': brackcnt++; break;
646         case ']': brackcnt--; break;
647         case '(': parencnt++; break;
648         case ')': parencnt--; break;
649         }
650 }
651
652 double errcheck(double x, const char *s)
653 {
654
655         if (errno == EDOM) {
656                 errno = 0;
657                 WARNING("%s argument out of domain", s);
658                 x = 1;
659         } else if (errno == ERANGE) {
660                 errno = 0;
661                 WARNING("%s result out of range", s);
662                 x = 1;
663         }
664         return x;
665 }
666
667 int isclvar(const char *s)      /* is s of form var=something ? */
668 {
669         const char *os = s;
670
671         if (!isalpha((uschar) *s) && *s != '_')
672                 return 0;
673         for ( ; *s; s++)
674                 if (!(isalnum((uschar) *s) || *s == '_'))
675                         break;
676         return *s == '=' && s > os && *(s+1) != '=';
677 }
678
679 /* strtod is supposed to be a proper test of what's a valid number */
680 /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
681 /* wrong: violates 4.10.1.4 of ansi C standard */
682
683 #include <math.h>
684 int is_number(const char *s)
685 {
686         double r;
687         char *ep;
688         errno = 0;
689         r = strtod(s, &ep);
690         if (ep == s || r == HUGE_VAL || errno == ERANGE)
691                 return 0;
692         while (*ep == ' ' || *ep == '\t' || *ep == '\n')
693                 ep++;
694         if (*ep == '\0')
695                 return 1;
696         else
697                 return 0;
698 }