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 "y.tab.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
recinit(unsigned int n)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
makefields(int n1,int n2)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
initgetrec(void)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
getrec(char ** pbuf,int * pbufsize,int isrecord)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
nextfile(void)179 void nextfile(void)
180 {
181 if (infile != NULL && infile != stdin)
182 fclose(infile);
183 infile = NULL;
184 argno++;
185 }
186
readrec(char ** pbuf,int * pbufsize,FILE * inf)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
getargv(int n)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
setclvar(char * s)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
fldbld(void)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
cleanfld(int n1,int n2)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
newfld(int n)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
fieldadr(int n)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
growfldtab(int n)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
refldbld(const char * rec,const char * fs)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 void *p;
423 int i, tempstat, n;
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 p = compre(fs);
437 dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
438 for (i = 1; ; i++) {
439 if (i > nfields)
440 growfldtab(i);
441 if (freeable(fldtab[i]))
442 xfree(fldtab[i]->sval);
443 fldtab[i]->tval = FLD | STR | DONTFREE;
444 fldtab[i]->sval = fr;
445 dprintf( ("refldbld: i=%d\n", i) );
446 if (nematch(p, rec, rec)) {
447 dprintf( ("match %s (%d chars)\n", patbeg, patlen) );
448 strncpy(fr, rec, patbeg-rec);
449 fr += patbeg - rec + 1;
450 *(fr-1) = '\0';
451 rec = patbeg + patlen;
452 } else {
453 dprintf( ("no match %s\n", rec) );
454 strcpy(fr, rec);
455 break;
456 }
457 }
458 return i;
459 }
460
recbld(void)461 void recbld(void) /* create $0 from $1..$NF if necessary */
462 {
463 int i;
464 char *r, *p;
465
466 if (donerec == 1)
467 return;
468 r = record;
469 for (i = 1; i <= *NF; i++) {
470 p = getsval(fldtab[i]);
471 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
472 FATAL("created $0 `%.30s...' too long", record);
473 while ((*r = *p++) != 0)
474 r++;
475 if (i < *NF) {
476 if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2"))
477 FATAL("created $0 `%.30s...' too long", record);
478 for (p = *OFS; (*r = *p++) != 0; )
479 r++;
480 }
481 }
482 if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
483 FATAL("built giant record `%.30s...'", record);
484 *r = '\0';
485 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
486
487 if (freeable(fldtab[0]))
488 xfree(fldtab[0]->sval);
489 fldtab[0]->tval = REC | STR | DONTFREE;
490 fldtab[0]->sval = record;
491
492 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
493 dprintf( ("recbld = |%s|\n", record) );
494 donerec = 1;
495 }
496
497 int errorflag = 0;
498
yyerror(const char * s)499 void yyerror(const char *s)
500 {
501 SYNTAX("%s", s);
502 }
503
SYNTAX(const char * fmt,...)504 void SYNTAX(const char *fmt, ...)
505 {
506 extern char *cmdname, *curfname;
507 static int been_here = 0;
508 va_list varg;
509
510 if (been_here++ > 2)
511 return;
512 fprintf(stderr, "%s: ", cmdname);
513 va_start(varg, fmt);
514 vfprintf(stderr, fmt, varg);
515 va_end(varg);
516 fprintf(stderr, " at source line %d", lineno);
517 if (curfname != NULL)
518 fprintf(stderr, " in function %s", curfname);
519 if (compile_time == 1 && cursource() != NULL)
520 fprintf(stderr, " source file %s", cursource());
521 fprintf(stderr, "\n");
522 errorflag = 2;
523 eprint();
524 }
525
fpecatch(int n)526 void fpecatch(int n)
527 {
528 FATAL("floating point exception %d", n);
529 }
530
531 extern int bracecnt, brackcnt, parencnt;
532
bracecheck(void)533 void bracecheck(void)
534 {
535 int c;
536 static int beenhere = 0;
537
538 if (beenhere++)
539 return;
540 while ((c = input()) != EOF && c != '\0')
541 bclass(c);
542 bcheck2(bracecnt, '{', '}');
543 bcheck2(brackcnt, '[', ']');
544 bcheck2(parencnt, '(', ')');
545 }
546
bcheck2(int n,int c1,int c2)547 void bcheck2(int n, int c1, int c2)
548 {
549 if (n == 1)
550 fprintf(stderr, "\tmissing %c\n", c2);
551 else if (n > 1)
552 fprintf(stderr, "\t%d missing %c's\n", n, c2);
553 else if (n == -1)
554 fprintf(stderr, "\textra %c\n", c2);
555 else if (n < -1)
556 fprintf(stderr, "\t%d extra %c's\n", -n, c2);
557 }
558
FATAL(const char * fmt,...)559 void FATAL(const char *fmt, ...)
560 {
561 extern char *cmdname;
562 va_list varg;
563
564 fflush(stdout);
565 fprintf(stderr, "%s: ", cmdname);
566 va_start(varg, fmt);
567 vfprintf(stderr, fmt, varg);
568 va_end(varg);
569 error();
570 if (dbg > 1) /* core dump if serious debugging on */
571 abort();
572 exit(2);
573 }
574
WARNING(const char * fmt,...)575 void WARNING(const char *fmt, ...)
576 {
577 extern char *cmdname;
578 va_list varg;
579
580 fflush(stdout);
581 fprintf(stderr, "%s: ", cmdname);
582 va_start(varg, fmt);
583 vfprintf(stderr, fmt, varg);
584 va_end(varg);
585 error();
586 }
587
error()588 void error()
589 {
590 extern Node *curnode;
591
592 fprintf(stderr, "\n");
593 if (compile_time != 2 && NR && *NR > 0) {
594 fprintf(stderr, " input record number %d", (int) (*FNR));
595 if (strcmp(*FILENAME, "-") != 0)
596 fprintf(stderr, ", file %s", *FILENAME);
597 fprintf(stderr, "\n");
598 }
599 if (compile_time != 2 && curnode)
600 fprintf(stderr, " source line number %d", curnode->lineno);
601 else if (compile_time != 2 && lineno)
602 fprintf(stderr, " source line number %d", lineno);
603 if (compile_time == 1 && cursource() != NULL)
604 fprintf(stderr, " source file %s", cursource());
605 fprintf(stderr, "\n");
606 eprint();
607 }
608
eprint(void)609 void eprint(void) /* try to print context around error */
610 {
611 char *p, *q;
612 int c;
613 static int been_here = 0;
614 extern char ebuf[], *ep;
615
616 if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
617 return;
618 p = ep - 1;
619 if (p > ebuf && *p == '\n')
620 p--;
621 for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
622 ;
623 while (*p == '\n')
624 p++;
625 fprintf(stderr, " context is\n\t");
626 for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
627 ;
628 for ( ; p < q; p++)
629 if (*p)
630 putc(*p, stderr);
631 fprintf(stderr, " >>> ");
632 for ( ; p < ep; p++)
633 if (*p)
634 putc(*p, stderr);
635 fprintf(stderr, " <<< ");
636 if (*ep)
637 while ((c = input()) != '\n' && c != '\0' && c != EOF) {
638 putc(c, stderr);
639 bclass(c);
640 }
641 putc('\n', stderr);
642 ep = ebuf;
643 }
644
bclass(int c)645 void bclass(int c)
646 {
647 switch (c) {
648 case '{': bracecnt++; break;
649 case '}': bracecnt--; break;
650 case '[': brackcnt++; break;
651 case ']': brackcnt--; break;
652 case '(': parencnt++; break;
653 case ')': parencnt--; break;
654 }
655 }
656
errcheck(double x,const char * s)657 double errcheck(double x, const char *s)
658 {
659
660 if (errno == EDOM) {
661 errno = 0;
662 WARNING("%s argument out of domain", s);
663 x = 1;
664 } else if (errno == ERANGE) {
665 errno = 0;
666 WARNING("%s result out of range", s);
667 x = 1;
668 }
669 return x;
670 }
671
isclvar(const char * s)672 int isclvar(const char *s) /* is s of form var=something ? */
673 {
674 const char *os = s;
675
676 if (!isalpha((uschar) *s) && *s != '_')
677 return 0;
678 for ( ; *s; s++)
679 if (!(isalnum((uschar) *s) || *s == '_'))
680 break;
681 return *s == '=' && s > os && *(s+1) != '=';
682 }
683
684 /* strtod is supposed to be a proper test of what's a valid number */
685 /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
686 /* wrong: violates 4.10.1.4 of ansi C standard */
687
688 #include <math.h>
is_number(const char * s)689 int is_number(const char *s)
690 {
691 double r;
692 char *ep;
693
694 /*
695 * fast could-it-be-a-number check before calling strtod,
696 * which takes a surprisingly long time to reject non-numbers.
697 */
698 switch (*s) {
699 case '0': case '1': case '2': case '3': case '4':
700 case '5': case '6': case '7': case '8': case '9':
701 case '\t':
702 case '\n':
703 case '\v':
704 case '\f':
705 case '\r':
706 case ' ':
707 case '-':
708 case '+':
709 case '.':
710 case 'n': /* nans */
711 case 'N':
712 case 'i': /* infs */
713 case 'I':
714 break;
715 default:
716 return 0; /* can't be a number */
717 }
718
719 errno = 0;
720 r = strtod(s, &ep);
721 if (ep == s || r == HUGE_VAL || errno == ERANGE)
722 return 0;
723 while (*ep == ' ' || *ep == '\t' || *ep == '\n')
724 ep++;
725 if (*ep == '\0')
726 return 1;
727 else
728 return 0;
729 }
730