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