xref: /inferno-os/utils/awk/lib.c (revision 6e425a9de8c003b5a733621a6b6730ec3cc902b8)
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