etc/services - sync with NetBSD-8
[minix.git] / external / historical / nawk / dist / lib.c
blob4cdded453517fe1c83ad9cbfc65e77b7516c7bd5
1 /****************************************************************
2 Copyright (C) Lucent Technologies 1997
3 All Rights Reserved
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.
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 ****************************************************************/
25 #if HAVE_NBTOOL_CONFIG_H
26 #include "nbtool_config.h"
27 #endif
29 #define DEBUG
30 #include <stdio.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <errno.h>
34 #include <stdlib.h>
35 #include <stdarg.h>
36 #include "awk.h"
37 #include "awkgram.h"
39 char EMPTY[] = { '\0' };
40 FILE *infile = NULL;
41 int innew; /* 1 = infile has not been read by readrec */
42 char *file = EMPTY;
43 uschar *record;
44 int recsize = RECSIZE;
45 char *fields;
46 int fieldssize = RECSIZE;
48 Cell **fldtab; /* pointers to Cells */
50 static size_t len_inputFS = 0;
51 static char *inputFS = NULL;
53 #define MAXFLD 2
54 int nfields = MAXFLD; /* last allocated slot for $i */
56 int donefld; /* 1 = implies rec broken into fields */
57 int donerec; /* 1 = record is valid (no flds have changed) */
59 int lastfld = 0; /* last used field */
60 int argno = 1; /* current input argument number */
61 extern Awkfloat *ARGC;
63 static Cell dollar0 = { OCELL, CFLD, NULL, EMPTY, 0.0, REC|STR|DONTFREE, NULL };
64 static Cell dollar1 = { OCELL, CFLD, NULL, EMPTY, 0.0, FLD|STR|DONTFREE, NULL };
66 void recinit(unsigned int n)
68 if ( (record = malloc(n)) == NULL
69 || (fields = malloc(n+1)) == NULL
70 || (fldtab = malloc((nfields+1) * sizeof(*fldtab))) == NULL
71 || (fldtab[0] = malloc(sizeof(**fldtab))) == NULL )
72 FATAL("out of space for $0 and fields");
73 *fldtab[0] = dollar0;
74 fldtab[0]->sval = record;
75 fldtab[0]->nval = tostring("0");
76 makefields(1, nfields);
79 void makefields(int n1, int n2) /* create $n1..$n2 inclusive */
81 char temp[50];
82 int i;
84 for (i = n1; i <= n2; i++) {
85 fldtab[i] = malloc(sizeof(**fldtab));
86 if (fldtab[i] == NULL)
87 FATAL("out of space in makefields %d", i);
88 *fldtab[i] = dollar1;
89 snprintf(temp, sizeof(temp), "%d", i);
90 fldtab[i]->nval = tostring(temp);
94 void initgetrec(void)
96 int i;
97 char *p;
99 for (i = 1; i < *ARGC; i++) {
100 p = getargv(i); /* find 1st real filename */
101 if (p == NULL || *p == '\0') { /* deleted or zapped */
102 argno++;
103 continue;
105 if (!isclvar(p)) {
106 setsval(lookup("FILENAME", symtab), p);
107 return;
109 setclvar(p); /* a commandline assignment before filename */
110 argno++;
112 infile = stdin; /* no filenames, so use stdin */
113 innew = 1;
116 static int firsttime = 1;
118 int getrec(uschar **pbuf, int *pbufsize, int isrecord) /* get next input record */
119 { /* note: cares whether buf == record */
120 int c;
121 uschar *buf = *pbuf;
122 uschar saveb0;
123 int bufsize = *pbufsize, savebufsize = bufsize;
125 if (firsttime) {
126 firsttime = 0;
127 initgetrec();
129 dprintf( ("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n",
130 *RS, *FS, *ARGC, *FILENAME) );
131 if (isrecord) {
132 donefld = 0;
133 donerec = 1;
135 saveb0 = buf[0];
136 buf[0] = 0;
137 while (argno < *ARGC || infile == stdin) {
138 dprintf( ("argno=%d, file=|%s|\n", argno, file) );
139 if (infile == NULL) { /* have to open a new file */
140 file = getargv(argno);
141 if (file == NULL || *file == '\0') { /* deleted or zapped */
142 argno++;
143 continue;
145 if (isclvar(file)) { /* a var=value arg */
146 setclvar(file);
147 argno++;
148 continue;
150 *FILENAME = file;
151 dprintf( ("opening file %s\n", file) );
152 if (*file == '-' && *(file+1) == '\0')
153 infile = stdin;
154 else if ((infile = fopen(file, "r")) == NULL)
155 FATAL("can't open file %s", file);
156 innew = 1;
157 setfval(fnrloc, 0.0);
159 c = readrec(&buf, &bufsize, infile, innew);
160 if (innew)
161 innew = 0;
162 if (c != 0 || buf[0] != '\0') { /* normal record */
163 if (isrecord) {
164 if (freeable(fldtab[0]))
165 xfree(fldtab[0]->sval);
166 fldtab[0]->sval = buf; /* buf == record */
167 fldtab[0]->tval = REC | STR | DONTFREE;
168 if (is_number(fldtab[0]->sval)) {
169 fldtab[0]->fval = atof(fldtab[0]->sval);
170 fldtab[0]->tval |= NUM;
173 setfval(nrloc, nrloc->fval+1);
174 setfval(fnrloc, fnrloc->fval+1);
175 *pbuf = buf;
176 *pbufsize = bufsize;
177 return 1;
179 /* EOF arrived on this file; set up next */
180 if (infile != stdin)
181 fclose(infile);
182 infile = NULL;
183 argno++;
185 buf[0] = saveb0;
186 *pbuf = buf;
187 *pbufsize = savebufsize;
188 return 0; /* true end of file */
191 void nextfile(void)
193 if (infile != NULL && infile != stdin)
194 fclose(infile);
195 infile = NULL;
196 argno++;
199 int readrec(uschar **pbuf, int *pbufsize, FILE *inf, int newflag) /* read one record into buf */
201 int sep, c, isrec, found, tempstat;
202 uschar *rr, *buf = *pbuf;
203 int bufsize = *pbufsize;
204 size_t len;
206 if ((len = strlen(*FS)) < len_inputFS) {
207 strcpy(inputFS, *FS); /* for subsequent field splitting */
208 } else {
209 len_inputFS = len + 1;
210 inputFS = realloc(inputFS, len_inputFS);
211 if (inputFS == NULL)
212 FATAL("field separator %.10s... is too long", *FS);
213 memcpy(inputFS, *FS, len_inputFS);
215 /*fflush(stdout); avoids some buffering problem but makes it 25% slower*/
216 if (**RS && (*RS)[1]) {
217 fa *pfa = makedfa(*RS, 1);
218 if (newflag)
219 found = fnematch(pfa, inf, &buf, &bufsize, recsize);
220 else {
221 tempstat = pfa->initstat;
222 pfa->initstat = 2;
223 found = fnematch(pfa, inf, &buf, &bufsize, recsize);
224 pfa->initstat = tempstat;
226 if (found)
227 *patbeg = 0;
228 } else {
229 if ((sep = **RS) == 0) {
230 sep = '\n';
231 while ((c=getc(inf)) == '\n' && c != EOF) /* skip leading \n's */
233 if (c != EOF)
234 ungetc(c, inf);
236 for (rr = buf; ; ) {
237 for (; (c=getc(inf)) != sep && c != EOF; ) {
238 if (rr-buf+1 > bufsize)
239 if (!adjbuf(&buf, &bufsize, 1+rr-buf,
240 recsize, &rr, "readrec 1"))
241 FATAL("input record `%.30s...'"
242 " too long", buf);
243 *rr++ = c;
245 if (**RS == sep || c == EOF)
246 break;
247 if ((c = getc(inf)) == '\n' || c == EOF)
248 /* 2 in a row */
249 break;
250 if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr,
251 "readrec 2"))
252 FATAL("input record `%.30s...' too long", buf);
253 *rr++ = '\n';
254 *rr++ = c;
256 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
257 FATAL("input record `%.30s...' too long", buf);
258 *rr = 0;
260 *pbuf = buf;
261 *pbufsize = bufsize;
262 isrec = *buf || !feof(inf);
263 dprintf( ("readrec saw <%s>, returns %d\n", buf, isrec) );
264 return isrec;
267 char *getargv(int n) /* get ARGV[n] */
269 Cell *x;
270 char *s, temp[50];
271 extern Array *ARGVtab;
273 snprintf(temp, sizeof(temp), "%d", n);
274 if (lookup(temp, ARGVtab) == NULL)
275 return NULL;
276 x = setsymtab(temp, "", 0.0, STR, ARGVtab);
277 s = getsval(x);
278 dprintf( ("getargv(%d) returns |%s|\n", n, s) );
279 return s;
282 void setclvar(char *s) /* set var=value from s */
284 char *p;
285 Cell *q;
287 for (p=s; *p != '='; p++)
289 *p++ = 0;
290 p = qstring(p, '\0');
291 q = setsymtab(s, p, 0.0, STR, symtab);
292 setsval(q, p);
293 if (is_number(q->sval)) {
294 q->fval = atof(q->sval);
295 q->tval |= NUM;
297 dprintf( ("command line set %s to |%s|\n", s, p) );
301 void fldbld(void) /* create fields from current record */
303 /* this relies on having fields[] the same length as $0 */
304 /* the fields are all stored in this one array with \0's */
305 /* possibly with a final trailing \0 not associated with any field */
306 char *r, *fr, sep;
307 Cell *p;
308 int i, j, n;
310 if (donefld)
311 return;
312 if (!isstr(fldtab[0]))
313 getsval(fldtab[0]);
314 r = fldtab[0]->sval;
315 n = strlen(r);
316 if (n > fieldssize) {
317 xfree(fields);
318 if ((fields = malloc(n+2)) == NULL) /* possibly 2 final \0s */
319 FATAL("out of space for fields in fldbld %d", n);
320 fieldssize = n;
322 fr = fields;
323 i = 0; /* number of fields accumulated here */
324 if (!inputFS) {
325 /* do nothing */
326 } else if (inputFS[0] && inputFS[1]) { /* it's a regular expression */
327 i = refldbld(r, inputFS);
328 } else if ((sep = *inputFS) == ' ') { /* default whitespace */
329 for (i = 0; ; ) {
330 while (*r == ' ' || *r == '\t' || *r == '\n')
331 r++;
332 if (*r == 0)
333 break;
334 i++;
335 if (i > nfields)
336 growfldtab(i);
337 if (freeable(fldtab[i]))
338 xfree(fldtab[i]->sval);
339 fldtab[i]->sval = fr;
340 fldtab[i]->tval = FLD | STR | DONTFREE;
342 *fr++ = *r++;
343 while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
344 *fr++ = 0;
346 *fr = 0;
347 } else if ((sep = *inputFS) == 0) { /* new: FS="" => 1 char/field */
348 for (i = 0; *r != 0; r++) {
349 char buf[2];
350 i++;
351 if (i > nfields)
352 growfldtab(i);
353 if (freeable(fldtab[i]))
354 xfree(fldtab[i]->sval);
355 buf[0] = *r;
356 buf[1] = 0;
357 fldtab[i]->sval = tostring(buf);
358 fldtab[i]->tval = FLD | STR;
360 *fr = 0;
361 } else if (*r != 0) { /* if 0, it's a null field */
362 /* subtlecase : if length(FS) == 1 && length(RS > 0)
363 * \n is NOT a field separator (cf awk book 61,84).
364 * this variable is tested in the inner while loop.
366 int rtest = '\n'; /* normal case */
367 if (strlen(*RS) > 0)
368 rtest = '\0';
369 for (;;) {
370 i++;
371 if (i > nfields)
372 growfldtab(i);
373 if (freeable(fldtab[i]))
374 xfree(fldtab[i]->sval);
375 fldtab[i]->sval = fr;
376 fldtab[i]->tval = FLD | STR | DONTFREE;
377 while (*r != sep && *r != rtest && *r != '\0') /* \n is always a separator */
378 *fr++ = *r++;
379 *fr++ = 0;
380 if (*r++ == 0)
381 break;
383 *fr = 0;
385 if (i > nfields)
386 FATAL("record `%.30s...' has too many fields; can't happen", r);
387 cleanfld(i+1, lastfld); /* clean out junk from previous record */
388 lastfld = i;
389 donefld = 1;
390 for (j = 1; j <= lastfld; j++) {
391 p = fldtab[j];
392 if(is_number(p->sval)) {
393 p->fval = atof(p->sval);
394 p->tval |= NUM;
397 setfval(nfloc, (Awkfloat) lastfld);
398 donerec = 1; /* restore */
399 if (dbg) {
400 for (j = 0; j <= lastfld; j++) {
401 p = fldtab[j];
402 printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
407 void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */
408 { /* nvals remain intact */
409 Cell *p;
410 int i;
412 for (i = n1; i <= n2; i++) {
413 p = fldtab[i];
414 if (freeable(p))
415 xfree(p->sval);
416 p->sval = EMPTY;
417 p->tval = FLD | STR | DONTFREE;
421 void newfld(int n) /* add field n after end of existing lastfld */
423 if (n > nfields)
424 growfldtab(n);
425 cleanfld(lastfld+1, n);
426 lastfld = n;
427 setfval(nfloc, (Awkfloat) n);
430 void setlastfld(int n) /* set lastfld cleaning fldtab cells if necessary */
432 if (n > nfields)
433 growfldtab(n);
435 if (lastfld < n)
436 cleanfld(lastfld+1, n);
437 else
438 cleanfld(n+1, lastfld);
440 lastfld = n;
443 Cell *fieldadr(int n) /* get nth field */
445 if (n < 0)
446 FATAL("trying to access out of range field %d", n);
447 if (n > nfields) /* fields after NF are empty */
448 growfldtab(n); /* but does not increase NF */
449 return(fldtab[n]);
452 void growfldtab(int n) /* make new fields up to at least $n */
454 int nf = 2 * nfields;
455 size_t s;
457 if (n > nf)
458 nf = n;
459 s = (nf+1) * (sizeof (struct Cell *)); /* freebsd: how much do we need? */
460 if (s / sizeof(struct Cell *) - 1 == (size_t)nf) /* didn't overflow */
461 fldtab = realloc(fldtab, s);
462 else /* overflow sizeof int */
463 xfree(fldtab); /* make it null */
464 if (fldtab == NULL)
465 FATAL("out of space creating %d fields", nf);
466 makefields(nfields+1, nf);
467 nfields = nf;
470 int refldbld(const char *rec, const char *fs) /* build fields from reg expr in FS */
472 /* this relies on having fields[] the same length as $0 */
473 /* the fields are all stored in this one array with \0's */
474 char *fr;
475 int i, tempstat, n;
476 fa *pfa;
478 n = strlen(rec);
479 if (n > fieldssize) {
480 xfree(fields);
481 if ((fields = malloc(n+1)) == NULL)
482 FATAL("out of space for fields in refldbld %d", n);
483 fieldssize = n;
485 fr = fields;
486 *fr = '\0';
487 if (*rec == '\0')
488 return 0;
489 pfa = makedfa(fs, 1);
490 dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
491 tempstat = pfa->initstat;
492 for (i = 1; ; i++) {
493 if (i > nfields)
494 growfldtab(i);
495 if (freeable(fldtab[i]))
496 xfree(fldtab[i]->sval);
497 fldtab[i]->tval = FLD | STR | DONTFREE;
498 fldtab[i]->sval = fr;
499 dprintf( ("refldbld: i=%d\n", i) );
500 if (nematch(pfa, rec)) {
501 pfa->initstat = 2; /* horrible coupling to b.c */
502 dprintf( ("match %s (%d chars)\n", patbeg, patlen) );
503 strncpy(fr, rec, ((const char*)patbeg)-rec);
504 fr += ((const char*)patbeg) - rec + 1;
505 *(fr-1) = '\0';
506 rec = patbeg + patlen;
507 } else {
508 dprintf( ("no match %s\n", rec) );
509 strcpy(fr, rec);
510 pfa->initstat = tempstat;
511 break;
514 return i;
517 void recbld(void) /* create $0 from $1..$NF if necessary */
519 int i;
520 uschar *r;
521 char *p;
523 if (donerec == 1)
524 return;
525 r = record;
526 for (i = 1; i <= *NF; i++) {
527 p = getsval(fldtab[i]);
528 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
529 FATAL("created $0 `%.30s...' too long", record);
530 while ((*r = *p++) != 0)
531 r++;
532 if (i < *NF) {
533 if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2"))
534 FATAL("created $0 `%.30s...' too long", record);
535 for (p = *OFS; (*r = *p++) != 0; )
536 r++;
539 if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
540 FATAL("built giant record `%.30s...'", record);
541 *r = '\0';
542 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
544 if (freeable(fldtab[0]))
545 xfree(fldtab[0]->sval);
546 fldtab[0]->tval = REC | STR | DONTFREE;
547 fldtab[0]->sval = record;
549 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
550 dprintf( ("recbld = |%s|\n", record) );
551 donerec = 1;
554 int errorflag = 0;
556 void yyerror(const char *s)
558 SYNTAX("%s", s);
561 void SYNTAX(const char *fmt, ...)
563 extern char *cmdname, *curfname;
564 static int been_here = 0;
565 va_list varg;
567 if (been_here++ > 2)
568 return;
569 fprintf(stderr, "%s: ", cmdname);
570 va_start(varg, fmt);
571 vfprintf(stderr, fmt, varg);
572 va_end(varg);
573 fprintf(stderr, " at source line %d", lineno);
574 if (curfname != NULL)
575 fprintf(stderr, " in function %s", curfname);
576 if (compile_time == 1 && cursource() != NULL)
577 fprintf(stderr, " source file %s", cursource());
578 fprintf(stderr, "\n");
579 errorflag = 2;
580 eprint();
583 extern int bracecnt, brackcnt, parencnt;
585 void bracecheck(void)
587 int c;
588 static int beenhere = 0;
590 if (beenhere++)
591 return;
592 while ((c = input()) != EOF && c != '\0')
593 bclass(c);
594 bcheck2(bracecnt, '{', '}');
595 bcheck2(brackcnt, '[', ']');
596 bcheck2(parencnt, '(', ')');
599 void bcheck2(int n, int c1, int c2)
601 if (n == 1)
602 fprintf(stderr, "\tmissing %c\n", c2);
603 else if (n > 1)
604 fprintf(stderr, "\t%d missing %c's\n", n, c2);
605 else if (n == -1)
606 fprintf(stderr, "\textra %c\n", c2);
607 else if (n < -1)
608 fprintf(stderr, "\t%d extra %c's\n", -n, c2);
611 void FATAL(const char *fmt, ...)
613 extern char *cmdname;
614 va_list varg;
616 fflush(stdout);
617 fprintf(stderr, "%s: ", cmdname);
618 va_start(varg, fmt);
619 vfprintf(stderr, fmt, varg);
620 va_end(varg);
621 error();
622 if (dbg > 1) /* core dump if serious debugging on */
623 abort();
624 exit(2);
627 void WARNING(const char *fmt, ...)
629 extern char *cmdname;
630 va_list varg;
632 fflush(stdout);
633 fprintf(stderr, "%s: ", cmdname);
634 va_start(varg, fmt);
635 vfprintf(stderr, fmt, varg);
636 va_end(varg);
637 error();
640 void error()
642 extern Node *curnode;
644 fprintf(stderr, "\n");
645 if (compile_time != 2 && NR && *NR > 0) {
646 fprintf(stderr, " input record number %d", (int) (*FNR));
647 if (strcmp(*FILENAME, "-") != 0)
648 fprintf(stderr, ", file %s", *FILENAME);
649 fprintf(stderr, "\n");
651 if (compile_time != 2 && curnode)
652 fprintf(stderr, " source line number %d", curnode->lineno);
653 else if (compile_time != 2 && lineno)
654 fprintf(stderr, " source line number %d", lineno);
655 if (compile_time == 1 && cursource() != NULL)
656 fprintf(stderr, " source file %s", cursource());
657 fprintf(stderr, "\n");
658 eprint();
661 void eprint(void) /* try to print context around error */
663 char *p, *q;
664 static int been_here = 0;
665 extern char ebuf[], *ep;
667 if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
668 return;
669 p = ep - 1;
670 if (p > ebuf && *p == '\n')
671 p--;
672 for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
674 while (*p == '\n')
675 p++;
676 fprintf(stderr, " context is\n\t");
677 for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
679 for ( ; p < q; p++)
680 if (*p)
681 putc(*p, stderr);
682 fprintf(stderr, " >>> ");
683 for ( ; p < ep; p++)
684 if (*p)
685 putc(*p, stderr);
686 fprintf(stderr, " <<< ");
687 #if 0
689 * The following code was used to print the rest of the line of
690 * error context. It naively counts brackets, parens and braces in
691 * order to minimize the parsing effect of dropping the rest of the
692 * line but it does not work in all the cases. It is too much work
693 * to save the current program input point and restore it in all the
694 * cases just for the benefit of error printing so for now this
695 * code is disabled. In particular this code is confused if the
696 * [ { ( ) } ] is inside a quoted string or a pattern.
698 if (*ep) {
699 int c;
700 while ((c = input()) != '\n' && c != '\0' && c != EOF) {
701 putc(c, stderr);
702 bclass(c);
705 #endif
706 putc('\n', stderr);
707 ep = ebuf;
710 void bclass(int c)
712 switch (c) {
713 case '{': bracecnt++; break;
714 case '}': bracecnt--; break;
715 case '[': brackcnt++; break;
716 case ']': brackcnt--; break;
717 case '(': parencnt++; break;
718 case ')': parencnt--; break;
722 double errcheck(double x, const char *s)
725 if (errno == EDOM) {
726 errno = 0;
727 WARNING("%s argument out of domain", s);
728 x = 1;
729 } else if (errno == ERANGE) {
730 errno = 0;
731 WARNING("%s result out of range", s);
732 x = 1;
734 return x;
737 int isclvar(const char *s) /* is s of form var=something ? */
739 const char *os = s;
741 if (!isalpha((uschar) *s) && *s != '_')
742 return 0;
743 for ( ; *s; s++)
744 if (!(isalnum((uschar) *s) || *s == '_'))
745 break;
746 return *s == '=' && s > os && *(s+1) != '=';
749 /* strtod is supposed to be a proper test of what's a valid number */
750 /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
751 /* wrong: violates 4.10.1.4 of ansi C standard */
753 #include <math.h>
754 int is_number(const char *s)
756 char *ep;
757 errno = 0;
758 (void)strtod(s, &ep);
759 if (ep == s || errno == ERANGE)
760 return 0;
761 if (ep - s >= 3 && strncasecmp(ep - 3, "nan", 3) == 0)
762 return 0;
763 while (*ep == ' ' || *ep == '\t' || *ep == '\n')
764 ep++;
765 if (*ep == '\0')
766 return 1;
767 else
768 return 0;