blob: 8078db0fa1576bfae16eb587063228606d95bd80 [file] [log] [blame]
Brian Kernighan87b94932012-12-22 10:35:39 -05001/****************************************************************
2Copyright (C) Lucent Technologies 1997
3All Rights Reserved
4
5Permission to use, copy, modify, and distribute this software and
6its documentation for any purpose and without fee is hereby
7granted, provided that the above copyright notice appear in all
8copies and that both that the copyright notice and this
9permission notice and warranty disclaimer appear in supporting
10documentation, and that the name Lucent Technologies or any of
11its entities not be used in advertising or publicity pertaining
12to distribution of the software without specific, written prior
13permission.
14
15LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
17IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
18SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
20IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
21ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
22THIS 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
35FILE *infile = NULL;
36char *file = "";
37char *record;
38int recsize = RECSIZE;
39char *fields;
40int fieldssize = RECSIZE;
41
42Cell **fldtab; /* pointers to Cells */
43char inputFS[100] = " ";
44
45#define MAXFLD 2
46int nfields = MAXFLD; /* last allocated slot for $i */
47
48int donefld; /* 1 = implies rec broken into fields */
49int donerec; /* 1 = record is valid (no flds have changed) */
50
51int lastfld = 0; /* last used field */
52int argno = 1; /* current input argument number */
53extern Awkfloat *ARGC;
54
55static Cell dollar0 = { OCELL, CFLD, NULL, "", 0.0, REC|STR|DONTFREE };
56static Cell dollar1 = { OCELL, CFLD, NULL, "", 0.0, FLD|STR|DONTFREE };
57
58void 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
71void 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
86void 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
107static int firsttime = 1;
108
109int 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
179void nextfile(void)
180{
181 if (infile != NULL && infile != stdin)
182 fclose(infile);
183 infile = NULL;
184 argno++;
185}
186
187int 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
229char *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
244void 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
263void 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);
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300359 donerec = 1; /* restore */
Brian Kernighan87b94932012-12-22 10:35:39 -0500360 if (dbg) {
361 for (j = 0; j <= lastfld; j++) {
362 p = fldtab[j];
363 printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
364 }
365 }
366}
367
368void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */
369{ /* nvals remain intact */
370 Cell *p;
371 int i;
372
373 for (i = n1; i <= n2; i++) {
374 p = fldtab[i];
375 if (freeable(p))
376 xfree(p->sval);
377 p->sval = "";
378 p->tval = FLD | STR | DONTFREE;
379 }
380}
381
382void newfld(int n) /* add field n after end of existing lastfld */
383{
384 if (n > nfields)
385 growfldtab(n);
386 cleanfld(lastfld+1, n);
387 lastfld = n;
388 setfval(nfloc, (Awkfloat) n);
389}
390
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300391void setlastfld(int n) /* set lastfld cleaning fldtab cells if necessary */
392{
Cody Peter Mello179536a2018-09-25 21:19:49 -0700393 if (n < 0)
394 FATAL("cannot set NF to a negative value");
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300395 if (n > nfields)
396 growfldtab(n);
397
398 if (lastfld < n)
399 cleanfld(lastfld+1, n);
400 else
401 cleanfld(n+1, lastfld);
402
403 lastfld = n;
404}
405
Brian Kernighan87b94932012-12-22 10:35:39 -0500406Cell *fieldadr(int n) /* get nth field */
407{
408 if (n < 0)
409 FATAL("trying to access out of range field %d", n);
410 if (n > nfields) /* fields after NF are empty */
411 growfldtab(n); /* but does not increase NF */
412 return(fldtab[n]);
413}
414
415void growfldtab(int n) /* make new fields up to at least $n */
416{
417 int nf = 2 * nfields;
418 size_t s;
419
420 if (n > nf)
421 nf = n;
422 s = (nf+1) * (sizeof (struct Cell *)); /* freebsd: how much do we need? */
423 if (s / sizeof(struct Cell *) - 1 == nf) /* didn't overflow */
424 fldtab = (Cell **) realloc(fldtab, s);
425 else /* overflow sizeof int */
426 xfree(fldtab); /* make it null */
427 if (fldtab == NULL)
428 FATAL("out of space creating %d fields", nf);
429 makefields(nfields+1, nf);
430 nfields = nf;
431}
432
433int refldbld(const char *rec, const char *fs) /* build fields from reg expr in FS */
434{
435 /* this relies on having fields[] the same length as $0 */
436 /* the fields are all stored in this one array with \0's */
437 char *fr;
438 int i, tempstat, n;
439 fa *pfa;
440
441 n = strlen(rec);
442 if (n > fieldssize) {
443 xfree(fields);
444 if ((fields = (char *) malloc(n+1)) == NULL)
445 FATAL("out of space for fields in refldbld %d", n);
446 fieldssize = n;
447 }
448 fr = fields;
449 *fr = '\0';
450 if (*rec == '\0')
451 return 0;
452 pfa = makedfa(fs, 1);
453 dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
454 tempstat = pfa->initstat;
455 for (i = 1; ; i++) {
456 if (i > nfields)
457 growfldtab(i);
458 if (freeable(fldtab[i]))
459 xfree(fldtab[i]->sval);
460 fldtab[i]->tval = FLD | STR | DONTFREE;
461 fldtab[i]->sval = fr;
462 dprintf( ("refldbld: i=%d\n", i) );
463 if (nematch(pfa, rec)) {
464 pfa->initstat = 2; /* horrible coupling to b.c */
465 dprintf( ("match %s (%d chars)\n", patbeg, patlen) );
466 strncpy(fr, rec, patbeg-rec);
467 fr += patbeg - rec + 1;
468 *(fr-1) = '\0';
469 rec = patbeg + patlen;
470 } else {
471 dprintf( ("no match %s\n", rec) );
472 strcpy(fr, rec);
473 pfa->initstat = tempstat;
474 break;
475 }
476 }
477 return i;
478}
479
480void recbld(void) /* create $0 from $1..$NF if necessary */
481{
482 int i;
483 char *r, *p;
484
485 if (donerec == 1)
486 return;
487 r = record;
488 for (i = 1; i <= *NF; i++) {
489 p = getsval(fldtab[i]);
490 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
491 FATAL("created $0 `%.30s...' too long", record);
492 while ((*r = *p++) != 0)
493 r++;
494 if (i < *NF) {
495 if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2"))
496 FATAL("created $0 `%.30s...' too long", record);
497 for (p = *OFS; (*r = *p++) != 0; )
498 r++;
499 }
500 }
501 if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
502 FATAL("built giant record `%.30s...'", record);
503 *r = '\0';
504 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
505
506 if (freeable(fldtab[0]))
507 xfree(fldtab[0]->sval);
508 fldtab[0]->tval = REC | STR | DONTFREE;
509 fldtab[0]->sval = record;
510
511 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
512 dprintf( ("recbld = |%s|\n", record) );
513 donerec = 1;
514}
515
516int errorflag = 0;
517
518void yyerror(const char *s)
519{
520 SYNTAX("%s", s);
521}
522
523void SYNTAX(const char *fmt, ...)
524{
525 extern char *cmdname, *curfname;
526 static int been_here = 0;
527 va_list varg;
528
529 if (been_here++ > 2)
530 return;
531 fprintf(stderr, "%s: ", cmdname);
532 va_start(varg, fmt);
533 vfprintf(stderr, fmt, varg);
534 va_end(varg);
535 fprintf(stderr, " at source line %d", lineno);
536 if (curfname != NULL)
537 fprintf(stderr, " in function %s", curfname);
538 if (compile_time == 1 && cursource() != NULL)
539 fprintf(stderr, " source file %s", cursource());
540 fprintf(stderr, "\n");
541 errorflag = 2;
542 eprint();
543}
544
545void fpecatch(int n)
546{
547 FATAL("floating point exception %d", n);
548}
549
550extern int bracecnt, brackcnt, parencnt;
551
552void bracecheck(void)
553{
554 int c;
555 static int beenhere = 0;
556
557 if (beenhere++)
558 return;
559 while ((c = input()) != EOF && c != '\0')
560 bclass(c);
561 bcheck2(bracecnt, '{', '}');
562 bcheck2(brackcnt, '[', ']');
563 bcheck2(parencnt, '(', ')');
564}
565
566void bcheck2(int n, int c1, int c2)
567{
568 if (n == 1)
569 fprintf(stderr, "\tmissing %c\n", c2);
570 else if (n > 1)
571 fprintf(stderr, "\t%d missing %c's\n", n, c2);
572 else if (n == -1)
573 fprintf(stderr, "\textra %c\n", c2);
574 else if (n < -1)
575 fprintf(stderr, "\t%d extra %c's\n", -n, c2);
576}
577
578void FATAL(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 if (dbg > 1) /* core dump if serious debugging on */
590 abort();
591 exit(2);
592}
593
594void WARNING(const char *fmt, ...)
595{
596 extern char *cmdname;
597 va_list varg;
598
599 fflush(stdout);
600 fprintf(stderr, "%s: ", cmdname);
601 va_start(varg, fmt);
602 vfprintf(stderr, fmt, varg);
603 va_end(varg);
604 error();
605}
606
607void error()
608{
609 extern Node *curnode;
610
611 fprintf(stderr, "\n");
612 if (compile_time != 2 && NR && *NR > 0) {
613 fprintf(stderr, " input record number %d", (int) (*FNR));
614 if (strcmp(*FILENAME, "-") != 0)
615 fprintf(stderr, ", file %s", *FILENAME);
616 fprintf(stderr, "\n");
617 }
618 if (compile_time != 2 && curnode)
619 fprintf(stderr, " source line number %d", curnode->lineno);
620 else if (compile_time != 2 && lineno)
621 fprintf(stderr, " source line number %d", lineno);
622 if (compile_time == 1 && cursource() != NULL)
623 fprintf(stderr, " source file %s", cursource());
624 fprintf(stderr, "\n");
625 eprint();
626}
627
628void eprint(void) /* try to print context around error */
629{
630 char *p, *q;
631 int c;
632 static int been_here = 0;
633 extern char ebuf[], *ep;
634
635 if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
636 return;
Brian Kernighan3ed9e242018-08-15 10:45:03 -0400637 if (ebuf == ep)
638 return;
Brian Kernighan87b94932012-12-22 10:35:39 -0500639 p = ep - 1;
640 if (p > ebuf && *p == '\n')
641 p--;
642 for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
643 ;
644 while (*p == '\n')
645 p++;
646 fprintf(stderr, " context is\n\t");
647 for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
648 ;
649 for ( ; p < q; p++)
650 if (*p)
651 putc(*p, stderr);
652 fprintf(stderr, " >>> ");
653 for ( ; p < ep; p++)
654 if (*p)
655 putc(*p, stderr);
656 fprintf(stderr, " <<< ");
657 if (*ep)
658 while ((c = input()) != '\n' && c != '\0' && c != EOF) {
659 putc(c, stderr);
660 bclass(c);
661 }
662 putc('\n', stderr);
663 ep = ebuf;
664}
665
666void bclass(int c)
667{
668 switch (c) {
669 case '{': bracecnt++; break;
670 case '}': bracecnt--; break;
671 case '[': brackcnt++; break;
672 case ']': brackcnt--; break;
673 case '(': parencnt++; break;
674 case ')': parencnt--; break;
675 }
676}
677
678double errcheck(double x, const char *s)
679{
680
681 if (errno == EDOM) {
682 errno = 0;
683 WARNING("%s argument out of domain", s);
684 x = 1;
685 } else if (errno == ERANGE) {
686 errno = 0;
687 WARNING("%s result out of range", s);
688 x = 1;
689 }
690 return x;
691}
692
693int isclvar(const char *s) /* is s of form var=something ? */
694{
695 const char *os = s;
696
697 if (!isalpha((uschar) *s) && *s != '_')
698 return 0;
699 for ( ; *s; s++)
700 if (!(isalnum((uschar) *s) || *s == '_'))
701 break;
702 return *s == '=' && s > os && *(s+1) != '=';
703}
704
705/* strtod is supposed to be a proper test of what's a valid number */
706/* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
707/* wrong: violates 4.10.1.4 of ansi C standard */
708
709#include <math.h>
710int is_number(const char *s)
711{
712 double r;
713 char *ep;
714 errno = 0;
715 r = strtod(s, &ep);
716 if (ep == s || r == HUGE_VAL || errno == ERANGE)
717 return 0;
718 while (*ep == ' ' || *ep == '\t' || *ep == '\n')
719 ep++;
720 if (*ep == '\0')
721 return 1;
722 else
723 return 0;
724}