]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/awk/lib.c
rio: fix goodrect() bug (thanks mike)
[plan9front.git] / sys / src / cmd / 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 #include <u.h>
26 #include <libc.h>
27 #include <ctype.h>
28 #include <bio.h>
29 #include "awk.h"
30 #include "y.tab.h"
31
32 Biobuf  *infile;
33 char    *file   = "";
34 char    *record;
35 int     recsize = RECSIZE;
36 char    *fields;
37 int     fieldssize = RECSIZE;
38
39 Cell    **fldtab;       /* pointers to Cells */
40 char    inputFS[100] = " ";
41
42 #define MAXFLD  200
43 int     nfields = MAXFLD;       /* last allocated slot for $i */
44
45 int     donefld;        /* 1 = implies rec broken into fields */
46 int     donerec;        /* 1 = record is valid (no flds have changed) */
47
48 int     lastfld = 0;    /* last used field */
49 int     argno   = 1;    /* current input argument number */
50 extern  Awkfloat *AARGC;
51
52 static Cell dollar0 = { OCELL, CFLD, nil, "", 0.0, REC|STR|DONTFREE };
53 static Cell dollar1 = { OCELL, CFLD, nil, "", 0.0, FLD|STR|DONTFREE };
54
55 void recinit(unsigned int n)
56 {
57         record = (char *) malloc(n);
58         fields = (char *) malloc(n);
59         fldtab = (Cell **) malloc((nfields+1) * sizeof(Cell *));
60         if (record == nil || fields == nil || fldtab == nil)
61                 FATAL("out of space for $0 and fields");
62         fldtab[0] = (Cell *) malloc(sizeof (Cell));
63         *fldtab[0] = dollar0;
64         fldtab[0]->sval = record;
65         fldtab[0]->nval = tostring("0");
66         makefields(1, nfields);
67 }
68
69 void makefields(int n1, int n2)         /* create $n1..$n2 inclusive */
70 {
71         char temp[50];
72         int i;
73
74         for (i = n1; i <= n2; i++) {
75                 fldtab[i] = (Cell *) malloc(sizeof (struct Cell));
76                 if (fldtab[i] == nil)
77                         FATAL("out of space in makefields %d", i);
78                 *fldtab[i] = dollar1;
79                 sprint(temp, "%d", i);
80                 fldtab[i]->nval = tostring(temp);
81         }
82 }
83
84 void initgetrec(void)
85 {
86         int i;
87         char *p;
88
89         for (i = 1; i < *AARGC; i++) {
90                 if (!isclvar(p = getargv(i))) { /* find 1st real filename */
91                         setsval(lookup("FILENAME", symtab), p);
92                         return;
93                 }
94                 setclvar(p);    /* a commandline assignment before filename */
95                 argno++;
96         }
97         infile = &stdin;                /* no filenames, so use &stdin */
98 }
99
100 int getrec(char **pbuf, int *pbufsize, int isrecord)    /* get next input record */
101 {                       /* note: cares whether buf == record */
102         int c;
103         static int firsttime = 1;
104         char *buf = *pbuf;
105         int bufsize = *pbufsize;
106
107         if (firsttime) {
108                 firsttime = 0;
109                 initgetrec();
110         }
111            dprint( ("RS=<%s>, FS=<%s>, AARGC=%g, FILENAME=%s\n",
112                 *RS, *FS, *AARGC, *FILENAME) );
113         if (isrecord) {
114                 donefld = 0;
115                 donerec = 1;
116         }
117         buf[0] = 0;
118         while (argno < *AARGC || infile == &stdin) {
119                    dprint( ("argno=%d, file=|%s|\n", argno, file) );
120                 if (infile == nil) {    /* have to open a new file */
121                         file = getargv(argno);
122                         if (*file == '\0') {    /* it's been zapped */
123                                 argno++;
124                                 continue;
125                         }
126                         if (isclvar(file)) {    /* a var=value arg */
127                                 setclvar(file);
128                                 argno++;
129                                 continue;
130                         }
131                         *FILENAME = file;
132                            dprint( ("opening file %s\n", file) );
133                         if (*file == '-' && *(file+1) == '\0')
134                                 infile = &stdin;
135                         else if ((infile = Bopen(file, OREAD)) == nil)
136                                 FATAL("can't open file %s", file);
137                         setfval(fnrloc, 0.0);
138                 }
139                 c = readrec(&buf, &bufsize, infile);
140                 if (c != 0 || buf[0] != '\0') { /* normal record */
141                         if (isrecord) {
142                                 if (freeable(fldtab[0]))
143                                         xfree(fldtab[0]->sval);
144                                 fldtab[0]->sval = buf;  /* buf == record */
145                                 fldtab[0]->tval = REC | STR | DONTFREE;
146                                 if (is_number(fldtab[0]->sval)) {
147                                         fldtab[0]->fval = atof(fldtab[0]->sval);
148                                         fldtab[0]->tval |= NUM;
149                                 }
150                         }
151                         setfval(nrloc, nrloc->fval+1);
152                         setfval(fnrloc, fnrloc->fval+1);
153                         *pbuf = buf;
154                         *pbufsize = bufsize;
155                         return 1;
156                 }
157                 /* Beof arrived on this file; set up next */
158                 nextfile();
159         }
160         *pbuf = buf;
161         *pbufsize = bufsize;
162         return 0;       /* true end of file */
163 }
164
165 void nextfile(void)
166 {
167         if (infile != nil && infile != &stdin)
168                 Bterm(infile);
169         infile = nil;
170         argno++;
171 }
172
173 int readrec(char **pbuf, int *pbufsize, Biobuf *inf)    /* read one record into buf */
174 {
175         int sep, c;
176         char *rr, *buf = *pbuf;
177         int bufsize = *pbufsize;
178
179         if (strlen(*FS) >= sizeof(inputFS))
180                 FATAL("field separator %.10s... is too long", *FS);
181         strcpy(inputFS, *FS);   /* for subsequent field splitting */
182         if ((sep = **RS) == 0) {
183                 sep = '\n';
184                 while ((c=Bgetc(inf)) == '\n' && c != Beof)     /* skip leading \n's */
185                         ;
186                 if (c != Beof)
187                         Bungetc(inf);
188         }
189         for (rr = buf; ; ) {
190                 for (; (c=Bgetc(inf)) != sep && c != Beof; ) {
191                         if (rr-buf+1 > bufsize)
192                                 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 1"))
193                                         FATAL("input record `%.30s...' too long", buf);
194                         *rr++ = c;
195                 }
196                 if (**RS == sep || c == Beof)
197                         break;
198                 if ((c = Bgetc(inf)) == '\n' || c == Beof) /* 2 in a row */
199                         break;
200                 if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr, "readrec 2"))
201                         FATAL("input record `%.30s...' too long", buf);
202                 *rr++ = '\n';
203                 *rr++ = c;
204         }
205         if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
206                 FATAL("input record `%.30s...' too long", buf);
207         *rr = 0;
208            dprint( ("readrec saw <%s>, returns %d\n", buf, c == Beof && rr == buf ? 0 : 1) );
209         *pbuf = buf;
210         *pbufsize = bufsize;
211         return c == Beof && rr == buf ? 0 : 1;
212 }
213
214 char *getargv(int n)    /* get ARGV[n] */
215 {
216         Cell *x;
217         char *s, temp[50];
218         extern Array *ARGVtab;
219
220         sprint(temp, "%d", n);
221         x = setsymtab(temp, "", 0.0, STR, ARGVtab);
222         s = getsval(x);
223         dprint( ("getargv(%d) returns |%s|\n", n, s) );
224         return s;
225 }
226
227 void setclvar(char *s)  /* set var=value from s */
228 {
229         char *p;
230         Cell *q;
231
232         for (p=s; *p != '='; p++)
233                 ;
234         *p++ = 0;
235         p = qstring(p, '\0');
236         q = setsymtab(s, p, 0.0, STR, symtab);
237         setsval(q, p);
238         if (is_number(q->sval)) {
239                 q->fval = atof(q->sval);
240                 q->tval |= NUM;
241         }
242            dprint( ("command line set %s to |%s|\n", s, p) );
243 }
244
245
246 void fldbld(void)       /* create fields from current record */
247 {
248         /* this relies on having fields[] the same length as $0 */
249         /* the fields are all stored in this one array with \0's */
250         char *r, *fr, sep;
251         Cell *p;
252         int i, j, n, w;
253
254         if (donefld)
255                 return;
256         if (!isstr(fldtab[0]))
257                 getsval(fldtab[0]);
258         r = fldtab[0]->sval;
259         n = strlen(r);
260         if (n > fieldssize) {
261                 xfree(fields);
262                 if ((fields = (char *) malloc(n+1)) == nil)
263                         FATAL("out of space for fields in fldbld %d", n);
264                 fieldssize = n;
265         }
266         fr = fields;
267         i = 0;  /* number of fields accumulated here */
268         if (strlen(inputFS) > 1) {      /* it's a regular expression */
269                 i = refldbld(r, inputFS);
270         } else if (*inputFS == ' ') {   /* default whitespace */
271                 for (i = 0; ; ) {
272                         while (*r == ' ' || *r == '\t' || *r == '\n')
273                                 r++;
274                         if (*r == 0)
275                                 break;
276                         i++;
277                         if (i > nfields)
278                                 growfldtab(i);
279                         if (freeable(fldtab[i]))
280                                 xfree(fldtab[i]->sval);
281                         fldtab[i]->sval = fr;
282                         fldtab[i]->tval = FLD | STR | DONTFREE;
283                         do
284                                 *fr++ = *r++;
285                         while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
286                         *fr++ = 0;
287                 }
288                 *fr = 0;
289         } else if ((sep = *inputFS) == 0) {             /* new: FS="" => 1 char/field */
290                 for (i = 0; *r != 0; r += w) {
291                         char buf[UTFmax + 1];
292                         Rune chr;
293
294                         i++;
295                         if (i > nfields)
296                                 growfldtab(i);
297                         if (freeable(fldtab[i]))
298                                 xfree(fldtab[i]->sval);
299                         w = chartorune(&chr, r);
300                         n = runetochar(buf, &chr);
301                         buf[n] = 0;
302                         fldtab[i]->sval = tostring(buf);
303                         fldtab[i]->tval = FLD | STR;
304                 }
305                 *fr = 0;
306         } else if (*r != 0) {   /* if 0, it's a null field */
307                 for (;;) {
308                         i++;
309                         if (i > nfields)
310                                 growfldtab(i);
311                         if (freeable(fldtab[i]))
312                                 xfree(fldtab[i]->sval);
313                         fldtab[i]->sval = fr;
314                         fldtab[i]->tval = FLD | STR | DONTFREE;
315                         while (*r != sep && *r != '\n' && *r != '\0')   /* \n is always a separator */
316                                 *fr++ = *r++;
317                         *fr++ = 0;
318                         if (*r++ == 0)
319                                 break;
320                 }
321                 *fr = 0;
322         }
323         if (i > nfields)
324                 FATAL("record `%.30s...' has too many fields; can't happen", r);
325         cleanfld(i+1, lastfld); /* clean out junk from previous record */
326         lastfld = i;
327         donefld = 1;
328         for (j = 1; j <= lastfld; j++) {
329                 p = fldtab[j];
330                 if(is_number(p->sval)) {
331                         p->fval = atof(p->sval);
332                         p->tval |= NUM;
333                 }
334         }
335         setfval(nfloc, (Awkfloat) lastfld);
336         if (dbg) {
337                 for (j = 0; j <= lastfld; j++) {
338                         p = fldtab[j];
339                         print("field %d (%s): |%s|\n", j, p->nval, p->sval);
340                 }
341         }
342 }
343
344 void cleanfld(int n1, int n2)   /* clean out fields n1 .. n2 inclusive */
345 {                               /* nvals remain intact */
346         Cell *p;
347         int i;
348
349         for (i = n1; i <= n2; i++) {
350                 p = fldtab[i];
351                 if (freeable(p))
352                         xfree(p->sval);
353                 p->sval = "";
354                 p->tval = FLD | STR | DONTFREE;
355         }
356 }
357
358 void newfld(int n)      /* add field n after end of existing lastfld */
359 {
360         if (n > nfields)
361                 growfldtab(n);
362         cleanfld(lastfld+1, n);
363         lastfld = n;
364         setfval(nfloc, (Awkfloat) n);
365 }
366
367 Cell *fieldadr(int n)   /* get nth field */
368 {
369         if (n < 0)
370                 FATAL("trying to access field %d", n);
371         if (n > nfields)        /* fields after NF are empty */
372                 growfldtab(n);  /* but does not increase NF */
373         return(fldtab[n]);
374 }
375
376 void growfldtab(int n)  /* make new fields up to at least $n */
377 {
378         int nf = 2 * nfields;
379
380         if (n > nf)
381                 nf = n;
382         fldtab = (Cell **) realloc(fldtab, (nf+1) * (sizeof (struct Cell *)));
383         if (fldtab == nil)
384                 FATAL("out of space creating %d fields", nf);
385         makefields(nfields+1, nf);
386         nfields = nf;
387 }
388
389 int refldbld(char *rec, char *fs)       /* build fields from reg expr in FS */
390 {
391         /* this relies on having fields[] the same length as $0 */
392         /* the fields are all stored in this one array with \0's */
393         char *fr;
394         void *p;
395         int i, n;
396
397         n = strlen(rec);
398         if (n > fieldssize) {
399                 xfree(fields);
400                 if ((fields = (char *) malloc(n+1)) == nil)
401                         FATAL("out of space for fields in refldbld %d", n);
402                 fieldssize = n;
403         }
404         fr = fields;
405         *fr = '\0';
406         if (*rec == '\0')
407                 return 0;
408         p = compre(fs);
409            dprint( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
410         for (i = 1; ; i++) {
411                 if (i > nfields)
412                         growfldtab(i);
413                 if (freeable(fldtab[i]))
414                         xfree(fldtab[i]->sval);
415                 fldtab[i]->tval = FLD | STR | DONTFREE;
416                 fldtab[i]->sval = fr;
417                    dprint( ("refldbld: i=%d\n", i) );
418                 if (nematch(p, rec, rec)) {
419                            dprint( ("match %s (%d chars)\n", patbeg, patlen) );
420                         strncpy(fr, rec, patbeg-rec);
421                         fr += patbeg - rec + 1;
422                         *(fr-1) = '\0';
423                         rec = patbeg + patlen;
424                 } else {
425                            dprint( ("no match %s\n", rec) );
426                         strcpy(fr, rec);
427                         break;
428                 }
429         }
430         return i;               
431 }
432
433 void recbld(void)       /* create $0 from $1..$NF if necessary */
434 {
435         int i;
436         char *r, *p;
437
438         if (donerec == 1)
439                 return;
440         r = record;
441         for (i = 1; i <= *NF; i++) {
442                 p = getsval(fldtab[i]);
443                 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
444                         FATAL("created $0 `%.30s...' too long", record);
445                 while ((*r = *p++) != 0)
446                         r++;
447                 if (i < *NF) {
448                         if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2"))
449                                 FATAL("created $0 `%.30s...' too long", record);
450                         for (p = *OFS; (*r = *p++) != 0; )
451                                 r++;
452                 }
453         }
454         if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
455                 FATAL("built giant record `%.30s...'", record);
456         *r = '\0';
457            dprint( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
458
459         if (freeable(fldtab[0]))
460                 xfree(fldtab[0]->sval);
461         fldtab[0]->tval = REC | STR | DONTFREE;
462         fldtab[0]->sval = record;
463
464            dprint( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
465            dprint( ("recbld = |%s|\n", record) );
466         donerec = 1;
467 }
468
469 char    *exitstatus     = nil;
470
471 void yyerror(char *s)
472 {
473         SYNTAX(s);
474 }
475
476 void SYNTAX(char *fmt, ...)
477 {
478         extern char *cmdname, *curfname;
479         static int been_here = 0;
480         va_list varg;
481
482         if (been_here++ > 2)
483                 return;
484         Bprint(&stderr, "%s: ", cmdname);
485         va_start(varg, fmt);
486         Bvprint(&stderr, fmt, varg);
487         va_end(varg);
488         if(compile_time == 1 && cursource() != nil)
489                 Bprint(&stderr, " at %s:%d", cursource(), lineno);
490         else
491                 Bprint(&stderr, " at line %d", lineno);
492         if (curfname != nil)
493                 Bprint(&stderr, " in function %s", curfname);
494         Bprint(&stderr, "\n");
495         exitstatus = "syntax error";
496         eprint();
497 }
498
499 int handler(void *, char *err)
500 {
501         Bflush(&stdout);
502         fprint(2, "%s\n", err);
503         return 0;
504 }
505
506 extern int bracecnt, brackcnt, parencnt;
507
508 void bracecheck(void)
509 {
510         int c;
511         static int beenhere = 0;
512
513         if (beenhere++)
514                 return;
515         while ((c = input()) != Beof && c != '\0')
516                 bclass(c);
517         bcheck2(bracecnt, '{', '}');
518         bcheck2(brackcnt, '[', ']');
519         bcheck2(parencnt, '(', ')');
520 }
521
522 void bcheck2(int n, int, int c2)
523 {
524         if (n == 1)
525                 Bprint(&stderr, "\tmissing %c\n", c2);
526         else if (n > 1)
527                 Bprint(&stderr, "\t%d missing %c's\n", n, c2);
528         else if (n == -1)
529                 Bprint(&stderr, "\textra %c\n", c2);
530         else if (n < -1)
531                 Bprint(&stderr, "\t%d extra %c's\n", -n, c2);
532 }
533
534 void FATAL(char *fmt, ...)
535 {
536         extern char *cmdname;
537         va_list varg;
538
539         Bflush(&stdout);
540         Bprint(&stderr, "%s: ", cmdname);
541         va_start(varg, fmt);
542         Bvprint(&stderr, fmt, varg);
543         va_end(varg);
544         error();
545         if (dbg > 1)            /* core dump if serious debugging on */
546                 abort();
547         exits("FATAL");
548 }
549
550 void WARNING(char *fmt, ...)
551 {
552         extern char *cmdname;
553         va_list varg;
554
555         Bflush(&stdout);
556         Bprint(&stderr, "%s: ", cmdname);
557         va_start(varg, fmt);
558         Bvprint(&stderr, fmt, varg);
559         va_end(varg);
560         error();
561 }
562
563 void error()
564 {
565         extern Node *curnode;
566         int line;
567
568         Bprint(&stderr, "\n");
569         if (compile_time != 2 && NR && *NR > 0) {
570                 if (strcmp(*FILENAME, "-") != 0)
571                         Bprint(&stderr, " input record %s:%d", *FILENAME, (int) (*FNR));
572                 else
573                         Bprint(&stderr, " input record number %d", (int) (*FNR));
574                 Bprint(&stderr, "\n");
575         }
576         if (compile_time != 2 && curnode)
577                 line = curnode->lineno;
578         else if (compile_time != 2 && lineno)
579                 line = lineno;
580         else
581                 line = -1;
582         if (compile_time == 1 && cursource() != nil){
583                 if(line >= 0)
584                         Bprint(&stderr, " source %s:%d", cursource(), line);
585                 else
586                         Bprint(&stderr, " source file %s", cursource());
587         }else if(line >= 0)
588                 Bprint(&stderr, " source line %d", line);
589         Bprint(&stderr, "\n");
590         eprint();
591 }
592
593 void eprint(void)       /* try to print context around error */
594 {
595         char *p, *q;
596         int c;
597         static int been_here = 0;
598         extern char ebuf[], *ep;
599
600         if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
601                 return;
602         p = ep - 1;
603         if (p > ebuf && *p == '\n')
604                 p--;
605         for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
606                 ;
607         while (*p == '\n')
608                 p++;
609         Bprint(&stderr, " context is\n\t");
610         for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
611                 ;
612         for ( ; p < q; p++)
613                 if (*p)
614                         Bputc(&stderr, *p);
615         Bprint(&stderr, " >>> ");
616         for ( ; p < ep; p++)
617                 if (*p)
618                         Bputc(&stderr, *p);
619         Bprint(&stderr, " <<< ");
620         if (*ep)
621                 while ((c = input()) != '\n' && c != '\0' && c != Beof) {
622                         Bputc(&stderr, c);
623                         bclass(c);
624                 }
625         Bputc(&stderr, '\n');
626         ep = ebuf;
627 }
628
629 void bclass(int c)
630 {
631         switch (c) {
632         case '{': bracecnt++; break;
633         case '}': bracecnt--; break;
634         case '[': brackcnt++; break;
635         case ']': brackcnt--; break;
636         case '(': parencnt++; break;
637         case ')': parencnt--; break;
638         }
639 }
640
641 double errcheck(double x, char *s)
642 {
643
644         if (isNaN(x)) {
645                 WARNING("%s argument out of domain", s);
646                 x = 1;
647         } else if (isInf(x, 1) || isInf(x, -1)) {
648                 WARNING("%s result out of range", s);
649                 x = 1;
650         }
651         return x;
652 }
653
654 int isclvar(char *s)    /* is s of form var=something ? */
655 {
656         char *os = s;
657
658         if (!isalpha(*s) && *s != '_')
659                 return 0;
660         for ( ; *s; s++)
661                 if (!(isalnum(*s) || *s == '_'))
662                         break;
663         return *s == '=' && s > os && *(s+1) != '=';
664 }
665
666 /* strtod is supposed to be a proper test of what's a valid number */
667
668 int is_number(char *s)
669 {
670         double r;
671         char *ep;
672
673         /*
674          * fast could-it-be-a-number check before calling strtod,
675          * which takes a surprisingly long time to reject non-numbers.
676          */
677         switch (*s) {
678         case '0': case '1': case '2': case '3': case '4':
679         case '5': case '6': case '7': case '8': case '9':
680         case '\t':
681         case '\n':
682         case '\v':
683         case '\f':
684         case '\r':
685         case ' ':
686         case '-':
687         case '+':
688         case '.':
689         case 'n':               /* nans */
690         case 'N':
691         case 'i':               /* infs */
692         case 'I':
693                 break;
694         default:
695                 return 0;       /* can't be a number */
696         }
697
698         r = strtod(s, &ep);
699         if (ep == s || isInf(r, 1) || isInf(r, -1) || isNaN(r))
700                 return 0;
701         while (*ep == ' ' || *ep == '\t' || *ep == '\n')
702                 ep++;
703         if (*ep == '\0')
704                 return 1;
705         else
706                 return 0;
707 }