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