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