]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/awk/lib.c
ndb/dnsquery, ndb/csquery: handle long lines
[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), getargv(i));
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                 if (infile != &stdin)
159                         Bterm(infile);
160                 infile = nil;
161                 argno++;
162         }
163         *pbuf = buf;
164         *pbufsize = bufsize;
165         return 0;       /* true end of file */
166 }
167
168 void nextfile(void)
169 {
170         if (infile != &stdin)
171                 Bterm(infile);
172         infile = nil;
173         argno++;
174 }
175
176 int readrec(char **pbuf, int *pbufsize, Biobuf *inf)    /* read one record into buf */
177 {
178         int sep, c;
179         char *rr, *buf = *pbuf;
180         int bufsize = *pbufsize;
181
182         if (strlen(*FS) >= sizeof(inputFS))
183                 FATAL("field separator %.10s... is too long", *FS);
184         strcpy(inputFS, *FS);   /* for subsequent field splitting */
185         if ((sep = **RS) == 0) {
186                 sep = '\n';
187                 while ((c=Bgetc(inf)) == '\n' && c != Beof)     /* skip leading \n's */
188                         ;
189                 if (c != Beof)
190                         Bungetc(inf);
191         }
192         for (rr = buf; ; ) {
193                 for (; (c=Bgetc(inf)) != sep && c != Beof; ) {
194                         if (rr-buf+1 > bufsize)
195                                 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 1"))
196                                         FATAL("input record `%.30s...' too long", buf);
197                         *rr++ = c;
198                 }
199                 if (**RS == sep || c == Beof)
200                         break;
201                 if ((c = Bgetc(inf)) == '\n' || c == Beof) /* 2 in a row */
202                         break;
203                 if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr, "readrec 2"))
204                         FATAL("input record `%.30s...' too long", buf);
205                 *rr++ = '\n';
206                 *rr++ = c;
207         }
208         if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
209                 FATAL("input record `%.30s...' too long", buf);
210         *rr = 0;
211            dprint( ("readrec saw <%s>, returns %d\n", buf, c == Beof && rr == buf ? 0 : 1) );
212         *pbuf = buf;
213         *pbufsize = bufsize;
214         return c == Beof && rr == buf ? 0 : 1;
215 }
216
217 char *getargv(int n)    /* get ARGV[n] */
218 {
219         Cell *x;
220         char *s, temp[50];
221         extern Array *ARGVtab;
222
223         sprint(temp, "%d", n);
224         x = setsymtab(temp, "", 0.0, STR, ARGVtab);
225         s = getsval(x);
226         dprint( ("getargv(%d) returns |%s|\n", n, s) );
227         return s;
228 }
229
230 void setclvar(char *s)  /* set var=value from s */
231 {
232         char *p;
233         Cell *q;
234
235         for (p=s; *p != '='; p++)
236                 ;
237         *p++ = 0;
238         p = qstring(p, '\0');
239         q = setsymtab(s, p, 0.0, STR, symtab);
240         setsval(q, p);
241         if (is_number(q->sval)) {
242                 q->fval = atof(q->sval);
243                 q->tval |= NUM;
244         }
245            dprint( ("command line set %s to |%s|\n", s, p) );
246 }
247
248
249 void fldbld(void)       /* create fields from current record */
250 {
251         /* this relies on having fields[] the same length as $0 */
252         /* the fields are all stored in this one array with \0's */
253         char *r, *fr, sep;
254         Cell *p;
255         int i, j, n;
256
257         if (donefld)
258                 return;
259         if (!isstr(fldtab[0]))
260                 getsval(fldtab[0]);
261         r = fldtab[0]->sval;
262         n = strlen(r);
263         if (n > fieldssize) {
264                 xfree(fields);
265                 if ((fields = (char *) malloc(n+1)) == nil)
266                         FATAL("out of space for fields in fldbld %d", n);
267                 fieldssize = n;
268         }
269         fr = fields;
270         i = 0;  /* number of fields accumulated here */
271         if (strlen(inputFS) > 1) {      /* it's a regular expression */
272                 i = refldbld(r, inputFS);
273         } else if (*inputFS == ' ') {   /* default whitespace */
274                 for (i = 0; ; ) {
275                         while (*r == ' ' || *r == '\t' || *r == '\n')
276                                 r++;
277                         if (*r == 0)
278                                 break;
279                         i++;
280                         if (i > nfields)
281                                 growfldtab(i);
282                         if (freeable(fldtab[i]))
283                                 xfree(fldtab[i]->sval);
284                         fldtab[i]->sval = fr;
285                         fldtab[i]->tval = FLD | STR | DONTFREE;
286                         do
287                                 *fr++ = *r++;
288                         while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
289                         *fr++ = 0;
290                 }
291                 *fr = 0;
292         } else if ((sep = *inputFS) == 0) {             /* new: FS="" => 1 char/field */
293                 for (i = 0; *r != 0; r++) {
294                         char buf[2];
295                         i++;
296                         if (i > nfields)
297                                 growfldtab(i);
298                         if (freeable(fldtab[i]))
299                                 xfree(fldtab[i]->sval);
300                         buf[0] = *r;
301                         buf[1] = 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 }