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