Make setthetime() static per the prototype.
[dragonfly.git] / contrib / one-true-awk / lib.c
1 /****************************************************************
2 Copyright (C) Lucent Technologies 1997
3 All Rights Reserved
4
5 Permission to use, copy, modify, and distribute this software and
6 its documentation for any purpose and without fee is hereby
7 granted, provided that the above copyright notice appear in all
8 copies and that both that the copyright notice and this
9 permission notice and warranty disclaimer appear in supporting
10 documentation, and that the name Lucent Technologies or any of
11 its entities not be used in advertising or publicity pertaining
12 to distribution of the software without specific, written prior
13 permission.
14
15 LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
17 IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
18 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
20 IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
21 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
22 THIS SOFTWARE.
23 ****************************************************************/
24
25 #define DEBUG
26 #include <stdio.h>
27 #include <string.h>
28 #include <ctype.h>
29 #include <errno.h>
30 #include <stdlib.h>
31 #include <stdarg.h>
32 #include "awk.h"
33 #include "ytab.h"
34
35 FILE    *infile = NULL;
36 char    *file   = "";
37 char    *record;
38 int     recsize = RECSIZE;
39 char    *fields;
40 int     fieldssize = RECSIZE;
41
42 Cell    **fldtab;       /* pointers to Cells */
43 char    inputFS[100] = " ";
44
45 #define MAXFLD  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                 for (;;) {
312                         i++;
313                         if (i > nfields)
314                                 growfldtab(i);
315                         if (freeable(fldtab[i]))
316                                 xfree(fldtab[i]->sval);
317                         fldtab[i]->sval = fr;
318                         fldtab[i]->tval = FLD | STR | DONTFREE;
319                         while (*r != sep && *r != '\n' && *r != '\0')   /* \n is always a separator */
320                                 *fr++ = *r++;
321                         *fr++ = 0;
322                         if (*r++ == 0)
323                                 break;
324                 }
325                 *fr = 0;
326         }
327         if (i > nfields)
328                 FATAL("record `%.30s...' has too many fields; can't happen", r);
329         cleanfld(i+1, lastfld); /* clean out junk from previous record */
330         lastfld = i;
331         donefld = 1;
332         for (j = 1; j <= lastfld; j++) {
333                 p = fldtab[j];
334                 if(is_number(p->sval)) {
335                         p->fval = atof(p->sval);
336                         p->tval |= NUM;
337                 }
338         }
339         setfval(nfloc, (Awkfloat) lastfld);
340         if (dbg) {
341                 for (j = 0; j <= lastfld; j++) {
342                         p = fldtab[j];
343                         printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
344                 }
345         }
346 }
347
348 void cleanfld(int n1, int n2)   /* clean out fields n1 .. n2 inclusive */
349 {                               /* nvals remain intact */
350         Cell *p;
351         int i;
352
353         for (i = n1; i <= n2; i++) {
354                 p = fldtab[i];
355                 if (freeable(p))
356                         xfree(p->sval);
357                 p->sval = "";
358                 p->tval = FLD | STR | DONTFREE;
359         }
360 }
361
362 void newfld(int n)      /* add field n after end of existing lastfld */
363 {
364         if (n > nfields)
365                 growfldtab(n);
366         cleanfld(lastfld+1, n);
367         lastfld = n;
368         setfval(nfloc, (Awkfloat) n);
369 }
370
371 Cell *fieldadr(int n)   /* get nth field */
372 {
373         if (n < 0)
374                 FATAL("trying to access field %d", n);
375         if (n > nfields)        /* fields after NF are empty */
376                 growfldtab(n);  /* but does not increase NF */
377         return(fldtab[n]);
378 }
379
380 void growfldtab(int n)  /* make new fields up to at least $n */
381 {
382         int nf = 2 * nfields;
383
384         if (n > nf)
385                 nf = n;
386         fldtab = (Cell **) realloc(fldtab, (nf+1) * (sizeof (struct Cell *)));
387         if (fldtab == NULL)
388                 FATAL("out of space creating %d fields", nf);
389         makefields(nfields+1, nf);
390         nfields = nf;
391 }
392
393 int refldbld(char *rec, char *fs)       /* build fields from reg expr in FS */
394 {
395         /* this relies on having fields[] the same length as $0 */
396         /* the fields are all stored in this one array with \0's */
397         char *fr;
398         int i, tempstat, n;
399         fa *pfa;
400
401         n = strlen(rec);
402         if (n > fieldssize) {
403                 xfree(fields);
404                 if ((fields = (char *) malloc(n+1)) == NULL)
405                         FATAL("out of space for fields in refldbld %d", n);
406                 fieldssize = n;
407         }
408         fr = fields;
409         *fr = '\0';
410         if (*rec == '\0')
411                 return 0;
412         pfa = makedfa(fs, 1);
413            dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
414         tempstat = pfa->initstat;
415         for (i = 1; ; i++) {
416                 if (i > nfields)
417                         growfldtab(i);
418                 if (freeable(fldtab[i]))
419                         xfree(fldtab[i]->sval);
420                 fldtab[i]->tval = FLD | STR | DONTFREE;
421                 fldtab[i]->sval = fr;
422                    dprintf( ("refldbld: i=%d\n", i) );
423                 if (nematch(pfa, rec)) {
424                         pfa->initstat = 2;      /* horrible coupling to b.c */
425                            dprintf( ("match %s (%d chars)\n", patbeg, patlen) );
426                         strncpy(fr, rec, patbeg-rec);
427                         fr += patbeg - rec + 1;
428                         *(fr-1) = '\0';
429                         rec = patbeg + patlen;
430                 } else {
431                            dprintf( ("no match %s\n", rec) );
432                         strcpy(fr, rec);
433                         pfa->initstat = tempstat;
434                         break;
435                 }
436         }
437         return i;               
438 }
439
440 void recbld(void)       /* create $0 from $1..$NF if necessary */
441 {
442         int i;
443         char *r, *p;
444
445         if (donerec == 1)
446                 return;
447         r = record;
448         for (i = 1; i <= *NF; i++) {
449                 p = getsval(fldtab[i]);
450                 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
451                         FATAL("created $0 `%.30s...' too long", record);
452                 while ((*r = *p++) != 0)
453                         r++;
454                 if (i < *NF) {
455                         if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2"))
456                                 FATAL("created $0 `%.30s...' too long", record);
457                         for (p = *OFS; (*r = *p++) != 0; )
458                                 r++;
459                 }
460         }
461         if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
462                 FATAL("built giant record `%.30s...'", record);
463         *r = '\0';
464            dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
465
466         if (freeable(fldtab[0]))
467                 xfree(fldtab[0]->sval);
468         fldtab[0]->tval = REC | STR | DONTFREE;
469         fldtab[0]->sval = record;
470
471            dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
472            dprintf( ("recbld = |%s|\n", record) );
473         donerec = 1;
474 }
475
476 int     errorflag       = 0;
477
478 void yyerror(char *s)
479 {
480         SYNTAX(s);
481 }
482
483 void SYNTAX(char *fmt, ...)
484 {
485         extern char *cmdname, *curfname;
486         static int been_here = 0;
487         va_list varg;
488
489         if (been_here++ > 2)
490                 return;
491         fprintf(stderr, "%s: ", cmdname);
492         va_start(varg, fmt);
493         vfprintf(stderr, fmt, varg);
494         va_end(varg);
495         fprintf(stderr, " at source line %d", lineno);
496         if (curfname != NULL)
497                 fprintf(stderr, " in function %s", curfname);
498         if (compile_time == 1 && cursource() != NULL)
499                 fprintf(stderr, " source file %s", cursource());
500         fprintf(stderr, "\n");
501         errorflag = 2;
502         eprint();
503 }
504
505 void fpecatch(int n)
506 {
507         FATAL("floating point exception %d", n);
508 }
509
510 extern int bracecnt, brackcnt, parencnt;
511
512 void bracecheck(void)
513 {
514         int c;
515         static int beenhere = 0;
516
517         if (beenhere++)
518                 return;
519         while ((c = input()) != EOF && c != '\0')
520                 bclass(c);
521         bcheck2(bracecnt, '{', '}');
522         bcheck2(brackcnt, '[', ']');
523         bcheck2(parencnt, '(', ')');
524 }
525
526 void bcheck2(int n, int c1, int c2)
527 {
528         if (n == 1)
529                 fprintf(stderr, "\tmissing %c\n", c2);
530         else if (n > 1)
531                 fprintf(stderr, "\t%d missing %c's\n", n, c2);
532         else if (n == -1)
533                 fprintf(stderr, "\textra %c\n", c2);
534         else if (n < -1)
535                 fprintf(stderr, "\t%d extra %c's\n", -n, c2);
536 }
537
538 void FATAL(char *fmt, ...)
539 {
540         extern char *cmdname;
541         va_list varg;
542
543         fflush(stdout);
544         fprintf(stderr, "%s: ", cmdname);
545         va_start(varg, fmt);
546         vfprintf(stderr, fmt, varg);
547         va_end(varg);
548         error();
549         if (dbg > 1)            /* core dump if serious debugging on */
550                 abort();
551         exit(2);
552 }
553
554 void WARNING(char *fmt, ...)
555 {
556         extern char *cmdname;
557         va_list varg;
558
559         fflush(stdout);
560         fprintf(stderr, "%s: ", cmdname);
561         va_start(varg, fmt);
562         vfprintf(stderr, fmt, varg);
563         va_end(varg);
564         error();
565 }
566
567 void error()
568 {
569         extern Node *curnode;
570
571         fprintf(stderr, "\n");
572         if (compile_time != 2 && NR && *NR > 0) {
573                 fprintf(stderr, " input record number %d", (int) (*FNR));
574                 if (strcmp(*FILENAME, "-") != 0)
575                         fprintf(stderr, ", file %s", *FILENAME);
576                 fprintf(stderr, "\n");
577         }
578         if (compile_time != 2 && curnode)
579                 fprintf(stderr, " source line number %d", curnode->lineno);
580         else if (compile_time != 2 && lineno)
581                 fprintf(stderr, " source line number %d", lineno);
582         if (compile_time == 1 && cursource() != NULL)
583                 fprintf(stderr, " source file %s", cursource());
584         fprintf(stderr, "\n");
585         eprint();
586 }
587
588 void eprint(void)       /* try to print context around error */
589 {
590         char *p, *q;
591         int c;
592         static int been_here = 0;
593         extern char ebuf[], *ep;
594
595         if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
596                 return;
597         p = ep - 1;
598         if (p > ebuf && *p == '\n')
599                 p--;
600         for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
601                 ;
602         while (*p == '\n')
603                 p++;
604         fprintf(stderr, " context is\n\t");
605         for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
606                 ;
607         for ( ; p < q; p++)
608                 if (*p)
609                         putc(*p, stderr);
610         fprintf(stderr, " >>> ");
611         for ( ; p < ep; p++)
612                 if (*p)
613                         putc(*p, stderr);
614         fprintf(stderr, " <<< ");
615         if (*ep)
616                 while ((c = input()) != '\n' && c != '\0' && c != EOF) {
617                         putc(c, stderr);
618                         bclass(c);
619                 }
620         putc('\n', stderr);
621         ep = ebuf;
622 }
623
624 void bclass(int c)
625 {
626         switch (c) {
627         case '{': bracecnt++; break;
628         case '}': bracecnt--; break;
629         case '[': brackcnt++; break;
630         case ']': brackcnt--; break;
631         case '(': parencnt++; break;
632         case ')': parencnt--; break;
633         }
634 }
635
636 double errcheck(double x, char *s)
637 {
638
639         if (errno == EDOM) {
640                 errno = 0;
641                 WARNING("%s argument out of domain", s);
642                 x = 1;
643         } else if (errno == ERANGE) {
644                 errno = 0;
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((uschar) *s) && *s != '_')
656                 return 0;
657         for ( ; *s; s++)
658                 if (!(isalnum((uschar) *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 /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
665 /* wrong: violates 4.10.1.4 of ansi C standard */
666
667 #include <math.h>
668 int is_number(char *s)
669 {
670         double r;
671         char *ep;
672         errno = 0;
673         r = strtod(s, &ep);
674         if (ep == s || r == HUGE_VAL || errno == ERANGE)
675                 return 0;
676         while (*ep == ' ' || *ep == '\t' || *ep == '\n')
677                 ep++;
678         if (*ep == '\0')
679                 return 1;
680         else
681                 return 0;
682 }