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