PageRenderTime 65ms CodeModel.GetById 22ms app.highlight 36ms RepoModel.GetById 1ms app.codeStats 1ms

/contrib/one-true-awk/lib.c

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