xref: /freebsd/contrib/one-true-awk/lib.c (revision 17d6c636720d00f77e5d098daf4c278f89d84f7b)
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 		for (;;) {
312 			i++;
313 			if (i > nfields)
314 				growfldtab(i);
315 			if (freeable(fldtab[i]))
316 				xfree(fldtab[i]->sval);
317 			fldtab[i]->sval = fr;
318 			fldtab[i]->tval = FLD | STR | DONTFREE;
319 			while (*r != sep && *r != '\n' && *r != '\0')	/* \n is always a separator */
320 				*fr++ = *r++;
321 			*fr++ = 0;
322 			if (*r++ == 0)
323 				break;
324 		}
325 		*fr = 0;
326 	}
327 	if (i > nfields)
328 		FATAL("record `%.30s...' has too many fields; can't happen", r);
329 	cleanfld(i+1, lastfld);	/* clean out junk from previous record */
330 	lastfld = i;
331 	donefld = 1;
332 	for (j = 1; j <= lastfld; j++) {
333 		p = fldtab[j];
334 		if(is_number(p->sval)) {
335 			p->fval = atof(p->sval);
336 			p->tval |= NUM;
337 		}
338 	}
339 	setfval(nfloc, (Awkfloat) lastfld);
340 	if (dbg) {
341 		for (j = 0; j <= lastfld; j++) {
342 			p = fldtab[j];
343 			printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
344 		}
345 	}
346 }
347 
348 void cleanfld(int n1, int n2)	/* clean out fields n1 .. n2 inclusive */
349 {				/* nvals remain intact */
350 	Cell *p;
351 	int i;
352 
353 	for (i = n1; i <= n2; i++) {
354 		p = fldtab[i];
355 		if (freeable(p))
356 			xfree(p->sval);
357 		p->sval = "";
358 		p->tval = FLD | STR | DONTFREE;
359 	}
360 }
361 
362 void newfld(int n)	/* add field n after end of existing lastfld */
363 {
364 	if (n > nfields)
365 		growfldtab(n);
366 	cleanfld(lastfld+1, n);
367 	lastfld = n;
368 	setfval(nfloc, (Awkfloat) n);
369 }
370 
371 Cell *fieldadr(int n)	/* get nth field */
372 {
373 	if (n < 0)
374 		FATAL("trying to access field %d", n);
375 	if (n > nfields)	/* fields after NF are empty */
376 		growfldtab(n);	/* but does not increase NF */
377 	return(fldtab[n]);
378 }
379 
380 void growfldtab(int n)	/* make new fields up to at least $n */
381 {
382 	int nf = 2 * nfields;
383 
384 	if (n > nf)
385 		nf = n;
386 	fldtab = (Cell **) realloc(fldtab, (nf+1) * (sizeof (struct Cell *)));
387 	if (fldtab == NULL)
388 		FATAL("out of space creating %d fields", nf);
389 	makefields(nfields+1, nf);
390 	nfields = nf;
391 }
392 
393 int refldbld(char *rec, char *fs)	/* build fields from reg expr in FS */
394 {
395 	/* this relies on having fields[] the same length as $0 */
396 	/* the fields are all stored in this one array with \0's */
397 	char *fr;
398 	int i, tempstat, n;
399 	fa *pfa;
400 
401 	n = strlen(rec);
402 	if (n > fieldssize) {
403 		xfree(fields);
404 		if ((fields = (char *) malloc(n+1)) == NULL)
405 			FATAL("out of space for fields in refldbld %d", n);
406 		fieldssize = n;
407 	}
408 	fr = fields;
409 	*fr = '\0';
410 	if (*rec == '\0')
411 		return 0;
412 	pfa = makedfa(fs, 1);
413 	   dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
414 	tempstat = pfa->initstat;
415 	for (i = 1; ; i++) {
416 		if (i > nfields)
417 			growfldtab(i);
418 		if (freeable(fldtab[i]))
419 			xfree(fldtab[i]->sval);
420 		fldtab[i]->tval = FLD | STR | DONTFREE;
421 		fldtab[i]->sval = fr;
422 		   dprintf( ("refldbld: i=%d\n", i) );
423 		if (nematch(pfa, rec)) {
424 			pfa->initstat = 2;	/* horrible coupling to b.c */
425 			   dprintf( ("match %s (%d chars)\n", patbeg, patlen) );
426 			strncpy(fr, rec, patbeg-rec);
427 			fr += patbeg - rec + 1;
428 			*(fr-1) = '\0';
429 			rec = patbeg + patlen;
430 		} else {
431 			   dprintf( ("no match %s\n", rec) );
432 			strcpy(fr, rec);
433 			pfa->initstat = tempstat;
434 			break;
435 		}
436 	}
437 	return i;
438 }
439 
440 void recbld(void)	/* create $0 from $1..$NF if necessary */
441 {
442 	int i;
443 	char *r, *p;
444 
445 	if (donerec == 1)
446 		return;
447 	r = record;
448 	for (i = 1; i <= *NF; i++) {
449 		p = getsval(fldtab[i]);
450 		if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
451 			FATAL("created $0 `%.30s...' too long", record);
452 		while ((*r = *p++) != 0)
453 			r++;
454 		if (i < *NF) {
455 			if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2"))
456 				FATAL("created $0 `%.30s...' too long", record);
457 			for (p = *OFS; (*r = *p++) != 0; )
458 				r++;
459 		}
460 	}
461 	if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
462 		FATAL("built giant record `%.30s...'", record);
463 	*r = '\0';
464 	   dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
465 
466 	if (freeable(fldtab[0]))
467 		xfree(fldtab[0]->sval);
468 	fldtab[0]->tval = REC | STR | DONTFREE;
469 	fldtab[0]->sval = record;
470 
471 	   dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
472 	   dprintf( ("recbld = |%s|\n", record) );
473 	donerec = 1;
474 }
475 
476 int	errorflag	= 0;
477 
478 void yyerror(char *s)
479 {
480 	SYNTAX(s);
481 }
482 
483 void SYNTAX(char *fmt, ...)
484 {
485 	extern char *cmdname, *curfname;
486 	static int been_here = 0;
487 	va_list varg;
488 
489 	if (been_here++ > 2)
490 		return;
491 	fprintf(stderr, "%s: ", cmdname);
492 	va_start(varg, fmt);
493 	vfprintf(stderr, fmt, varg);
494 	va_end(varg);
495 	fprintf(stderr, " at source line %d", lineno);
496 	if (curfname != NULL)
497 		fprintf(stderr, " in function %s", curfname);
498 	if (compile_time == 1 && cursource() != NULL)
499 		fprintf(stderr, " source file %s", cursource());
500 	fprintf(stderr, "\n");
501 	errorflag = 2;
502 	eprint();
503 }
504 
505 void fpecatch(int n)
506 {
507 	FATAL("floating point exception %d", n);
508 }
509 
510 extern int bracecnt, brackcnt, parencnt;
511 
512 void bracecheck(void)
513 {
514 	int c;
515 	static int beenhere = 0;
516 
517 	if (beenhere++)
518 		return;
519 	while ((c = input()) != EOF && c != '\0')
520 		bclass(c);
521 	bcheck2(bracecnt, '{', '}');
522 	bcheck2(brackcnt, '[', ']');
523 	bcheck2(parencnt, '(', ')');
524 }
525 
526 void bcheck2(int n, int c1, int c2)
527 {
528 	if (n == 1)
529 		fprintf(stderr, "\tmissing %c\n", c2);
530 	else if (n > 1)
531 		fprintf(stderr, "\t%d missing %c's\n", n, c2);
532 	else if (n == -1)
533 		fprintf(stderr, "\textra %c\n", c2);
534 	else if (n < -1)
535 		fprintf(stderr, "\t%d extra %c's\n", -n, c2);
536 }
537 
538 void FATAL(char *fmt, ...)
539 {
540 	extern char *cmdname;
541 	va_list varg;
542 
543 	fflush(stdout);
544 	fprintf(stderr, "%s: ", cmdname);
545 	va_start(varg, fmt);
546 	vfprintf(stderr, fmt, varg);
547 	va_end(varg);
548 	error();
549 	if (dbg > 1)		/* core dump if serious debugging on */
550 		abort();
551 	exit(2);
552 }
553 
554 void WARNING(char *fmt, ...)
555 {
556 	extern char *cmdname;
557 	va_list varg;
558 
559 	fflush(stdout);
560 	fprintf(stderr, "%s: ", cmdname);
561 	va_start(varg, fmt);
562 	vfprintf(stderr, fmt, varg);
563 	va_end(varg);
564 	error();
565 }
566 
567 void error()
568 {
569 	extern Node *curnode;
570 
571 	fprintf(stderr, "\n");
572 	if (compile_time != 2 && NR && *NR > 0) {
573 		fprintf(stderr, " input record number %d", (int) (*FNR));
574 		if (strcmp(*FILENAME, "-") != 0)
575 			fprintf(stderr, ", file %s", *FILENAME);
576 		fprintf(stderr, "\n");
577 	}
578 	if (compile_time != 2 && curnode)
579 		fprintf(stderr, " source line number %d", curnode->lineno);
580 	else if (compile_time != 2 && lineno)
581 		fprintf(stderr, " source line number %d", lineno);
582 	if (compile_time == 1 && cursource() != NULL)
583 		fprintf(stderr, " source file %s", cursource());
584 	fprintf(stderr, "\n");
585 	eprint();
586 }
587 
588 void eprint(void)	/* try to print context around error */
589 {
590 	char *p, *q;
591 	int c;
592 	static int been_here = 0;
593 	extern char ebuf[], *ep;
594 
595 	if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
596 		return;
597 	p = ep - 1;
598 	if (p > ebuf && *p == '\n')
599 		p--;
600 	for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
601 		;
602 	while (*p == '\n')
603 		p++;
604 	fprintf(stderr, " context is\n\t");
605 	for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
606 		;
607 	for ( ; p < q; p++)
608 		if (*p)
609 			putc(*p, stderr);
610 	fprintf(stderr, " >>> ");
611 	for ( ; p < ep; p++)
612 		if (*p)
613 			putc(*p, stderr);
614 	fprintf(stderr, " <<< ");
615 	if (*ep)
616 		while ((c = input()) != '\n' && c != '\0' && c != EOF) {
617 			putc(c, stderr);
618 			bclass(c);
619 		}
620 	putc('\n', stderr);
621 	ep = ebuf;
622 }
623 
624 void bclass(int c)
625 {
626 	switch (c) {
627 	case '{': bracecnt++; break;
628 	case '}': bracecnt--; break;
629 	case '[': brackcnt++; break;
630 	case ']': brackcnt--; break;
631 	case '(': parencnt++; break;
632 	case ')': parencnt--; break;
633 	}
634 }
635 
636 double errcheck(double x, char *s)
637 {
638 
639 	if (errno == EDOM) {
640 		errno = 0;
641 		WARNING("%s argument out of domain", s);
642 		x = 1;
643 	} else if (errno == ERANGE) {
644 		errno = 0;
645 		WARNING("%s result out of range", s);
646 		x = 1;
647 	}
648 	return x;
649 }
650 
651 int isclvar(char *s)	/* is s of form var=something ? */
652 {
653 	char *os = s;
654 
655 	if (!isalpha((uschar) *s) && *s != '_')
656 		return 0;
657 	for ( ; *s; s++)
658 		if (!(isalnum((uschar) *s) || *s == '_'))
659 			break;
660 	return *s == '=' && s > os && *(s+1) != '=';
661 }
662 
663 /* strtod is supposed to be a proper test of what's a valid number */
664 /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
665 /* wrong: violates 4.10.1.4 of ansi C standard */
666 
667 #include <math.h>
668 int is_number(char *s)
669 {
670 	double r;
671 	char *ep;
672 	errno = 0;
673 	r = strtod(s, &ep);
674 	if (ep == s || r == HUGE_VAL || errno == ERANGE)
675 		return 0;
676 	while (*ep == ' ' || *ep == '\t' || *ep == '\n')
677 		ep++;
678 	if (*ep == '\0')
679 		return 1;
680 	else
681 		return 0;
682 }
683