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