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