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
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 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
recbld(void)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
yyerror(const char * s)502 void yyerror(const char *s)
503 {
504 SYNTAX("%s", s);
505 }
506
SYNTAX(const char * fmt,...)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
fpecatch(int n)529 void fpecatch(int n)
530 {
531 FATAL("floating point exception %d", n);
532 }
533
534 extern int bracecnt, brackcnt, parencnt;
535
bracecheck(void)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
bcheck2(int n,int c1,int c2)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
FATAL(const char * fmt,...)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
WARNING(const char * fmt,...)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
error()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
eprint(void)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
bclass(int c)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
errcheck(double x,const char * s)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
isclvar(const char * s)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>
is_number(const char * s)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 }
707