Import of awk 20040207
[dragonfly.git] / contrib / awk20040207 / lib.c
1 /****************************************************************
2 Copyright (C) Lucent Technologies 1997
3 All Rights Reserved
4
5 Permission to use, copy, modify, and distribute this software and
6 its documentation for any purpose and without fee is hereby
7 granted, provided that the above copyright notice appear in all
8 copies and that both that the copyright notice and this
9 permission notice and warranty disclaimer appear in supporting
10 documentation, and that the name Lucent Technologies or any of
11 its entities not be used in advertising or publicity pertaining
12 to distribution of the software without specific, written prior
13 permission.
14
15 LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
17 IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
18 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
20 IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
21 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
22 THIS SOFTWARE.
23 ****************************************************************/
24
25 #define DEBUG
26 #include <stdio.h>
27 #include <string.h>
28 #include <ctype.h>
29 #include <errno.h>
30 #include <stdlib.h>
31 #include <stdarg.h>
32 #include "awk.h"
33 #include "ytab.h"
34
35 FILE    *infile = NULL;
36 char    *file   = "";
37 char    *record;
38 int     recsize = RECSIZE;
39 char    *fields;
40 int     fieldssize = RECSIZE;
41
42 Cell    **fldtab;       /* pointers to Cells */
43 char    inputFS[100] = " ";
44
45 #define MAXFLD  200
46 int     nfields = MAXFLD;       /* last allocated slot for $i */
47
48 int     donefld;        /* 1 = implies rec broken into fields */
49 int     donerec;        /* 1 = record is valid (no flds have changed) */
50
51 int     lastfld = 0;    /* last used field */
52 int     argno   = 1;    /* current input argument number */
53 extern  Awkfloat *ARGC;
54
55 static Cell dollar0 = { OCELL, CFLD, NULL, "", 0.0, REC|STR|DONTFREE };
56 static Cell dollar1 = { OCELL, CFLD, NULL, "", 0.0, FLD|STR|DONTFREE };
57
58 void recinit(unsigned int n)
59 {
60         record = (char *) malloc(n);
61         fields = (char *) malloc(n);
62         fldtab = (Cell **) malloc((nfields+1) * sizeof(Cell *));
63         if (record == NULL || fields == NULL || fldtab == NULL)
64                 FATAL("out of space for $0 and fields");
65
66         fldtab[0] = (Cell *) malloc(sizeof (Cell));
67         *fldtab[0] = dollar0;
68         fldtab[0]->sval = record;
69         fldtab[0]->nval = tostring("0");
70         makefields(1, nfields);
71 }
72
73 void makefields(int n1, int n2)         /* create $n1..$n2 inclusive */
74 {
75         char temp[50];
76         int i;
77
78         for (i = n1; i <= n2; i++) {
79                 fldtab[i] = (Cell *) malloc(sizeof (struct Cell));
80                 if (fldtab[i] == NULL)
81                         FATAL("out of space in makefields %d", i);
82                 *fldtab[i] = dollar1;
83                 sprintf(temp, "%d", i);
84                 fldtab[i]->nval = tostring(temp);
85         }
86 }
87
88 void initgetrec(void)
89 {
90         int i;
91         char *p;
92
93         for (i = 1; i < *ARGC; i++) {
94                 if (!isclvar(p = getargv(i))) { /* find 1st real filename */
95                         setsval(lookup("FILENAME", symtab), getargv(i));
96                         return;
97                 }
98                 setclvar(p);    /* a commandline assignment before filename */
99                 argno++;
100         }
101         infile = stdin;         /* no filenames, so use stdin */
102 }
103
104 int getrec(char **pbuf, int *pbufsize, int isrecord)    /* get next input record */
105 {                       /* note: cares whether buf == record */
106         int c;
107         static int firsttime = 1;
108         char *buf = *pbuf;
109         int bufsize = *pbufsize;
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         buf[0] = 0;
122         while (argno < *ARGC || infile == stdin) {
123                    dprintf( ("argno=%d, file=|%s|\n", argno, file) );
124                 if (infile == NULL) {   /* have to open a new file */
125                         file = getargv(argno);
126                         if (*file == '\0') {    /* it's been zapped */
127                                 argno++;
128                                 continue;
129                         }
130                         if (isclvar(file)) {    /* a var=value arg */
131                                 setclvar(file);
132                                 argno++;
133                                 continue;
134                         }
135                         *FILENAME = file;
136                            dprintf( ("opening file %s\n", file) );
137                         if (*file == '-' && *(file+1) == '\0')
138                                 infile = stdin;
139                         else if ((infile = fopen(file, "r")) == NULL)
140                                 FATAL("can't open file %s", file);
141                         setfval(fnrloc, 0.0);
142                 }
143                 c = readrec(&buf, &bufsize, infile);
144                 if (c != 0 || buf[0] != '\0') { /* normal record */
145                         if (isrecord) {
146                                 if (freeable(fldtab[0]))
147                                         xfree(fldtab[0]->sval);
148                                 fldtab[0]->sval = buf;  /* buf == record */
149                                 fldtab[0]->tval = REC | STR | DONTFREE;
150                                 if (is_number(fldtab[0]->sval)) {
151                                         fldtab[0]->fval = atof(fldtab[0]->sval);
152                                         fldtab[0]->tval |= NUM;
153                                 }
154                         }
155                         setfval(nrloc, nrloc->fval+1);
156                         setfval(fnrloc, fnrloc->fval+1);
157                         *pbuf = buf;
158                         *pbufsize = bufsize;
159                         return 1;
160                 }
161                 /* EOF arrived on this file; set up next */
162                 if (infile != stdin)
163                         fclose(infile);
164                 infile = NULL;
165                 argno++;
166         }
167         *pbuf = buf;
168         *pbufsize = bufsize;
169         return 0;       /* true end of file */
170 }
171
172 void nextfile(void)
173 {
174         if (infile != stdin)
175                 fclose(infile);
176         infile = NULL;
177         argno++;
178 }
179
180 int readrec(char **pbuf, int *pbufsize, FILE *inf)      /* read one record into buf */
181 {
182         int sep, c;
183         char *rr, *buf = *pbuf;
184         int bufsize = *pbufsize;
185
186         if (strlen(*FS) >= sizeof(inputFS))
187                 FATAL("field separator %.10s... is too long", *FS);
188         strcpy(inputFS, *FS);   /* for subsequent field splitting */
189         if ((sep = **RS) == 0) {
190                 sep = '\n';
191                 while ((c=getc(inf)) == '\n' && c != EOF)       /* skip leading \n's */
192                         ;
193                 if (c != EOF)
194                         ungetc(c, inf);
195         }
196         for (rr = buf; ; ) {
197                 for (; (c=getc(inf)) != sep && c != EOF; ) {
198                         if (rr-buf+1 > bufsize)
199                                 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 1"))
200                                         FATAL("input record `%.30s...' too long", buf);
201                         *rr++ = c;
202                 }
203                 if (**RS == sep || c == EOF)
204                         break;
205                 if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */
206                         break;
207                 if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr, "readrec 2"))
208                         FATAL("input record `%.30s...' too long", buf);
209                 *rr++ = '\n';
210                 *rr++ = c;
211         }
212         if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
213                 FATAL("input record `%.30s...' too long", buf);
214         *rr = 0;
215            dprintf( ("readrec saw <%s>, returns %d\n", buf, c == EOF && rr == buf ? 0 : 1) );
216         *pbuf = buf;
217         *pbufsize = bufsize;
218         return c == EOF && rr == buf ? 0 : 1;
219 }
220
221 char *getargv(int n)    /* get ARGV[n] */
222 {
223         Cell *x;
224         char *s, temp[50];
225         extern Array *ARGVtab;
226
227         sprintf(temp, "%d", n);
228         x = setsymtab(temp, "", 0.0, STR, ARGVtab);
229         s = getsval(x);
230            dprintf( ("getargv(%d) returns |%s|\n", n, s) );
231         return s;
232 }
233
234 void setclvar(char *s)  /* set var=value from s */
235 {
236         char *p;
237         Cell *q;
238
239         for (p=s; *p != '='; p++)
240                 ;
241         *p++ = 0;
242         p = qstring(p, '\0');
243         q = setsymtab(s, p, 0.0, STR, symtab);
244         setsval(q, p);
245         if (is_number(q->sval)) {
246                 q->fval = atof(q->sval);
247                 q->tval |= NUM;
248         }
249            dprintf( ("command line set %s to |%s|\n", s, p) );
250 }
251
252
253 void fldbld(void)       /* create fields from current record */
254 {
255         /* this relies on having fields[] the same length as $0 */
256         /* the fields are all stored in this one array with \0's */
257         char *r, *fr, sep;
258         Cell *p;
259         int i, j, n;
260
261         if (donefld)
262                 return;
263         if (!isstr(fldtab[0]))
264                 getsval(fldtab[0]);
265         r = fldtab[0]->sval;
266         n = strlen(r);
267         if (n > fieldssize) {
268                 xfree(fields);
269                 if ((fields = (char *) malloc(n+1)) == NULL)
270                         FATAL("out of space for fields in fldbld %d", n);
271                 fieldssize = n;
272         }
273         fr = fields;
274         i = 0;  /* number of fields accumulated here */
275         if (strlen(inputFS) > 1) {      /* it's a regular expression */
276                 i = refldbld(r, inputFS);
277         } else if ((sep = *inputFS) == ' ') {   /* default whitespace */
278                 for (i = 0; ; ) {
279                         while (*r == ' ' || *r == '\t' || *r == '\n')
280                                 r++;
281                         if (*r == 0)
282                                 break;
283                         i++;
284                         if (i > nfields)
285                                 growfldtab(i);
286                         if (freeable(fldtab[i]))
287                                 xfree(fldtab[i]->sval);
288                         fldtab[i]->sval = fr;
289                         fldtab[i]->tval = FLD | STR | DONTFREE;
290                         do
291                                 *fr++ = *r++;
292                         while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
293                         *fr++ = 0;
294                 }
295                 *fr = 0;
296         } else if ((sep = *inputFS) == 0) {             /* new: FS="" => 1 char/field */
297                 for (i = 0; *r != 0; r++) {
298                         char buf[2];
299                         i++;
300                         if (i > nfields)
301                                 growfldtab(i);
302                         if (freeable(fldtab[i]))
303                                 xfree(fldtab[i]->sval);
304                         buf[0] = *r;
305                         buf[1] = 0;
306                         fldtab[i]->sval = tostring(buf);
307                         fldtab[i]->tval = FLD | STR;
308                 }
309                 *fr = 0;
310         } else if (*r != 0) {   /* if 0, it's a null field */
311                 /* subtlecase : if length(FS) == 1 && length(RS > 0)
312                  * \n is NOT a field separator (cf awk book 61,84).
313                  * this variable is tested in the inner while loop.
314                  */
315                 int rtest = '\n';  /* normal case */
316                 if (strlen(*RS) > 0)
317                         rtest = '\0';
318                 for (;;) {
319                         i++;
320                         if (i > nfields)
321                                 growfldtab(i);
322                         if (freeable(fldtab[i]))
323                                 xfree(fldtab[i]->sval);
324                         fldtab[i]->sval = fr;
325                         fldtab[i]->tval = FLD | STR | DONTFREE;
326                         while (*r != sep && *r != rtest && *r != '\0')  /* \n is always a separator */
327                                 *fr++ = *r++;
328                         *fr++ = 0;
329                         if (*r++ == 0)
330                                 break;
331                 }
332                 *fr = 0;
333         }
334         if (i > nfields)
335                 FATAL("record `%.30s...' has too many fields; can't happen", r);
336         cleanfld(i+1, lastfld); /* clean out junk from previous record */
337         lastfld = i;
338         donefld = 1;
339         for (j = 1; j <= lastfld; j++) {
340                 p = fldtab[j];
341                 if(is_number(p->sval)) {
342                         p->fval = atof(p->sval);
343                         p->tval |= NUM;
344                 }
345         }
346         setfval(nfloc, (Awkfloat) lastfld);
347         if (dbg) {
348                 for (j = 0; j <= lastfld; j++) {
349                         p = fldtab[j];
350                         printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
351                 }
352         }
353 }
354
355 void cleanfld(int n1, int n2)   /* clean out fields n1 .. n2 inclusive */
356 {                               /* nvals remain intact */
357         Cell *p;
358         int i;
359
360         for (i = n1; i <= n2; i++) {
361                 p = fldtab[i];
362                 if (freeable(p))
363                         xfree(p->sval);
364                 p->sval = "";
365                 p->tval = FLD | STR | DONTFREE;
366         }
367 }
368
369 void newfld(int n)      /* add field n after end of existing lastfld */
370 {
371         if (n > nfields)
372                 growfldtab(n);
373         cleanfld(lastfld+1, n);
374         lastfld = n;
375         setfval(nfloc, (Awkfloat) n);
376 }
377
378 Cell *fieldadr(int n)   /* get nth field */
379 {
380         if (n < 0)
381                 FATAL("trying to access field %d", n);
382         if (n > nfields)        /* fields after NF are empty */
383                 growfldtab(n);  /* but does not increase NF */
384         return(fldtab[n]);
385 }
386
387 void growfldtab(int n)  /* make new fields up to at least $n */
388 {
389         int nf = 2 * nfields;
390
391         if (n > nf)
392                 nf = n;
393         fldtab = (Cell **) realloc(fldtab, (nf+1) * (sizeof (struct Cell *)));
394         if (fldtab == NULL)
395                 FATAL("out of space creating %d fields", nf);
396         makefields(nfields+1, nf);
397         nfields = nf;
398 }
399
400 int refldbld(const char *rec, const char *fs)   /* build fields from reg expr in FS */
401 {
402         /* this relies on having fields[] the same length as $0 */
403         /* the fields are all stored in this one array with \0's */
404         char *fr;
405         int i, tempstat, n;
406         fa *pfa;
407
408         n = strlen(rec);
409         if (n > fieldssize) {
410                 xfree(fields);
411                 if ((fields = (char *) malloc(n+1)) == NULL)
412                         FATAL("out of space for fields in refldbld %d", n);
413                 fieldssize = n;
414         }
415         fr = fields;
416         *fr = '\0';
417         if (*rec == '\0')
418                 return 0;
419         pfa = makedfa(fs, 1);
420            dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
421         tempstat = pfa->initstat;
422         for (i = 1; ; i++) {
423                 if (i > nfields)
424                         growfldtab(i);
425                 if (freeable(fldtab[i]))
426                         xfree(fldtab[i]->sval);
427                 fldtab[i]->tval = FLD | STR | DONTFREE;
428                 fldtab[i]->sval = fr;
429                    dprintf( ("refldbld: i=%d\n", i) );
430                 if (nematch(pfa, rec)) {
431                         pfa->initstat = 2;      /* horrible coupling to b.c */
432                            dprintf( ("match %s (%d chars)\n", patbeg, patlen) );
433                         strncpy(fr, rec, patbeg-rec);
434                         fr += patbeg - rec + 1;
435                         *(fr-1) = '\0';
436                         rec = patbeg + patlen;
437                 } else {
438                            dprintf( ("no match %s\n", rec) );
439                         strcpy(fr, rec);
440                         pfa->initstat = tempstat;
441                         break;
442                 }
443         }
444         return i;               
445 }
446
447 void recbld(void)       /* create $0 from $1..$NF if necessary */
448 {
449         int i;
450         char *r, *p;
451
452         if (donerec == 1)
453                 return;
454         r = record;
455         for (i = 1; i <= *NF; i++) {
456                 p = getsval(fldtab[i]);
457                 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
458                         FATAL("created $0 `%.30s...' too long", record);
459                 while ((*r = *p++) != 0)
460                         r++;
461                 if (i < *NF) {
462                         if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2"))
463                                 FATAL("created $0 `%.30s...' too long", record);
464                         for (p = *OFS; (*r = *p++) != 0; )
465                                 r++;
466                 }
467         }
468         if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
469                 FATAL("built giant record `%.30s...'", record);
470         *r = '\0';
471            dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
472
473         if (freeable(fldtab[0]))
474                 xfree(fldtab[0]->sval);
475         fldtab[0]->tval = REC | STR | DONTFREE;
476         fldtab[0]->sval = record;
477
478            dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
479            dprintf( ("recbld = |%s|\n", record) );
480         donerec = 1;
481 }
482
483 int     errorflag       = 0;
484
485 void yyerror(const char *s)
486 {
487         SYNTAX(s);
488 }
489
490 void SYNTAX(const char *fmt, ...)
491 {
492         extern char *cmdname, *curfname;
493         static int been_here = 0;
494         va_list varg;
495
496         if (been_here++ > 2)
497                 return;
498         fprintf(stderr, "%s: ", cmdname);
499         va_start(varg, fmt);
500         vfprintf(stderr, fmt, varg);
501         va_end(varg);
502         fprintf(stderr, " at source line %d", lineno);
503         if (curfname != NULL)
504                 fprintf(stderr, " in function %s", curfname);
505         if (compile_time == 1 && cursource() != NULL)
506                 fprintf(stderr, " source file %s", cursource());
507         fprintf(stderr, "\n");
508         errorflag = 2;
509         eprint();
510 }
511
512 void fpecatch(int n)
513 {
514         FATAL("floating point exception %d", n);
515 }
516
517 extern int bracecnt, brackcnt, parencnt;
518
519 void bracecheck(void)
520 {
521         int c;
522         static int beenhere = 0;
523
524         if (beenhere++)
525                 return;
526         while ((c = input()) != EOF && c != '\0')
527                 bclass(c);
528         bcheck2(bracecnt, '{', '}');
529         bcheck2(brackcnt, '[', ']');
530         bcheck2(parencnt, '(', ')');
531 }
532
533 void bcheck2(int n, int c1, int c2)
534 {
535         if (n == 1)
536                 fprintf(stderr, "\tmissing %c\n", c2);
537         else if (n > 1)
538                 fprintf(stderr, "\t%d missing %c's\n", n, c2);
539         else if (n == -1)
540                 fprintf(stderr, "\textra %c\n", c2);
541         else if (n < -1)
542                 fprintf(stderr, "\t%d extra %c's\n", -n, c2);
543 }
544
545 void FATAL(const char *fmt, ...)
546 {
547         extern char *cmdname;
548         va_list varg;
549
550         fflush(stdout);
551         fprintf(stderr, "%s: ", cmdname);
552         va_start(varg, fmt);
553         vfprintf(stderr, fmt, varg);
554         va_end(varg);
555         error();
556         if (dbg > 1)            /* core dump if serious debugging on */
557                 abort();
558         exit(2);
559 }
560
561 void WARNING(const char *fmt, ...)
562 {
563         extern char *cmdname;
564         va_list varg;
565
566         fflush(stdout);
567         fprintf(stderr, "%s: ", cmdname);
568         va_start(varg, fmt);
569         vfprintf(stderr, fmt, varg);
570         va_end(varg);
571         error();
572 }
573
574 void error()
575 {
576         extern Node *curnode;
577
578         fprintf(stderr, "\n");
579         if (compile_time != 2 && NR && *NR > 0) {
580                 fprintf(stderr, " input record number %d", (int) (*FNR));
581                 if (strcmp(*FILENAME, "-") != 0)
582                         fprintf(stderr, ", file %s", *FILENAME);
583                 fprintf(stderr, "\n");
584         }
585         if (compile_time != 2 && curnode)
586                 fprintf(stderr, " source line number %d", curnode->lineno);
587         else if (compile_time != 2 && lineno)
588                 fprintf(stderr, " source line number %d", lineno);
589         if (compile_time == 1 && cursource() != NULL)
590                 fprintf(stderr, " source file %s", cursource());
591         fprintf(stderr, "\n");
592         eprint();
593 }
594
595 void eprint(void)       /* try to print context around error */
596 {
597         char *p, *q;
598         int c;
599         static int been_here = 0;
600         extern char ebuf[], *ep;
601
602         if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
603                 return;
604         p = ep - 1;
605         if (p > ebuf && *p == '\n')
606                 p--;
607         for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
608                 ;
609         while (*p == '\n')
610                 p++;
611         fprintf(stderr, " context is\n\t");
612         for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
613                 ;
614         for ( ; p < q; p++)
615                 if (*p)
616                         putc(*p, stderr);
617         fprintf(stderr, " >>> ");
618         for ( ; p < ep; p++)
619                 if (*p)
620                         putc(*p, stderr);
621         fprintf(stderr, " <<< ");
622         if (*ep)
623                 while ((c = input()) != '\n' && c != '\0' && c != EOF) {
624                         putc(c, stderr);
625                         bclass(c);
626                 }
627         putc('\n', stderr);
628         ep = ebuf;
629 }
630
631 void bclass(int c)
632 {
633         switch (c) {
634         case '{': bracecnt++; break;
635         case '}': bracecnt--; break;
636         case '[': brackcnt++; break;
637         case ']': brackcnt--; break;
638         case '(': parencnt++; break;
639         case ')': parencnt--; break;
640         }
641 }
642
643 double errcheck(double x, const char *s)
644 {
645
646         if (errno == EDOM) {
647                 errno = 0;
648                 WARNING("%s argument out of domain", s);
649                 x = 1;
650         } else if (errno == ERANGE) {
651                 errno = 0;
652                 WARNING("%s result out of range", s);
653                 x = 1;
654         }
655         return x;
656 }
657
658 int isclvar(const char *s)      /* is s of form var=something ? */
659 {
660         const char *os = s;
661
662         if (!isalpha((uschar) *s) && *s != '_')
663                 return 0;
664         for ( ; *s; s++)
665                 if (!(isalnum((uschar) *s) || *s == '_'))
666                         break;
667         return *s == '=' && s > os && *(s+1) != '=';
668 }
669
670 /* strtod is supposed to be a proper test of what's a valid number */
671 /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
672 /* wrong: violates 4.10.1.4 of ansi C standard */
673
674 #include <math.h>
675 int is_number(const char *s)
676 {
677         double r;
678         char *ep;
679         errno = 0;
680         r = strtod(s, &ep);
681         if (ep == s || r == HUGE_VAL || errno == ERANGE)
682                 return 0;
683         while (*ep == ' ' || *ep == '\t' || *ep == '\n')
684                 ep++;
685         if (*ep == '\0')
686                 return 1;
687         else
688                 return 0;
689 }