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