Blob


1 /****************************************************************
2 Copyright (C) Lucent Technologies 1997
3 All Rights Reserved
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.
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 ****************************************************************/
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 <utf.h>
33 #include "awk.h"
34 #include "y.tab.h"
36 FILE *infile = NULL;
37 char *file = "";
38 char *record;
39 int recsize = RECSIZE;
40 char *fields;
41 int fieldssize = RECSIZE;
43 Cell **fldtab; /* pointers to Cells */
44 char inputFS[100] = " ";
46 #define MAXFLD 200
47 int nfields = MAXFLD; /* last allocated slot for $i */
49 int donefld; /* 1 = implies rec broken into fields */
50 int donerec; /* 1 = record is valid (no flds have changed) */
52 int lastfld = 0; /* last used field */
53 int argno = 1; /* current input argument number */
54 extern Awkfloat *ARGC;
56 static Cell dollar0 = { OCELL, CFLD, NULL, "", 0.0, REC|STR|DONTFREE };
57 static Cell dollar1 = { OCELL, CFLD, NULL, "", 0.0, FLD|STR|DONTFREE };
59 void recinit(unsigned int n)
60 {
61 record = (char *) malloc(n);
62 fields = (char *) malloc(n);
63 fldtab = (Cell **) malloc((nfields+1) * sizeof(Cell *));
64 if (record == NULL || fields == NULL || fldtab == NULL)
65 FATAL("out of space for $0 and fields");
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 }
73 void makefields(int n1, int n2) /* create $n1..$n2 inclusive */
74 {
75 char temp[50];
76 int i;
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 }
88 void initgetrec(void)
89 {
90 int i;
91 char *p;
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++;
101 infile = stdin; /* no filenames, so use stdin */
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;
111 if (firsttime) {
112 firsttime = 0;
113 initgetrec();
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;
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;
130 if (isclvar(file)) { /* a var=value arg */
131 setclvar(file);
132 argno++;
133 continue;
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);
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;
155 setfval(nrloc, nrloc->fval+1);
156 setfval(fnrloc, fnrloc->fval+1);
157 *pbuf = buf;
158 *pbufsize = bufsize;
159 return 1;
161 /* EOF arrived on this file; set up next */
162 if (infile != stdin)
163 fclose(infile);
164 infile = NULL;
165 argno++;
167 *pbuf = buf;
168 *pbufsize = bufsize;
169 return 0; /* true end of file */
172 void nextfile(void)
174 if (infile != stdin)
175 fclose(infile);
176 infile = NULL;
177 argno++;
180 int readrec(char **pbuf, int *pbufsize, FILE *inf) /* read one record into buf */
182 int sep, c;
183 char *rr, *buf = *pbuf;
184 int bufsize = *pbufsize;
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 */
193 if (c != EOF)
194 ungetc(c, inf);
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;
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;
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;
221 char *getargv(int n) /* get ARGV[n] */
223 Cell *x;
224 char *s, temp[50];
225 extern Array *ARGVtab;
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;
234 void setclvar(char *s) /* set var=value from s */
236 char *p;
237 Cell *q;
239 for (p=s; *p != '='; p++)
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;
249 dprintf( ("command line set %s to |%s|\n", s, p) );
253 void fldbld(void) /* create fields from current record */
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;
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;
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;
295 *fr = 0;
296 } else if ((sep = *inputFS) == 0) { /* new: FS="" => 1 char/field */
297 int nb;
298 for (i = 0; *r != 0; r += nb) {
299 Rune rr;
300 char buf[UTFmax+1];
302 i++;
303 if (i > nfields)
304 growfldtab(i);
305 if (freeable(fldtab[i]))
306 xfree(fldtab[i]->sval);
307 nb = chartorune(&rr, r);
308 memmove(buf, r, nb);
309 buf[nb] = '\0';
310 fldtab[i]->sval = tostring(buf);
311 fldtab[i]->tval = FLD | STR;
313 *fr = 0;
314 } else if (*r != 0) { /* if 0, it's a null field */
315 for (;;) {
316 i++;
317 if (i > nfields)
318 growfldtab(i);
319 if (freeable(fldtab[i]))
320 xfree(fldtab[i]->sval);
321 fldtab[i]->sval = fr;
322 fldtab[i]->tval = FLD | STR | DONTFREE;
323 while (*r != sep && *r != '\n' && *r != '\0') /* \n is always a separator */
324 *fr++ = *r++;
325 *fr++ = 0;
326 if (*r++ == 0)
327 break;
329 *fr = 0;
331 if (i > nfields)
332 FATAL("record `%.30s...' has too many fields; can't happen", r);
333 cleanfld(i+1, lastfld); /* clean out junk from previous record */
334 lastfld = i;
335 donefld = 1;
336 for (j = 1; j <= lastfld; j++) {
337 p = fldtab[j];
338 if(is_number(p->sval)) {
339 p->fval = atof(p->sval);
340 p->tval |= NUM;
343 setfval(nfloc, (Awkfloat) lastfld);
344 if (dbg) {
345 for (j = 0; j <= lastfld; j++) {
346 p = fldtab[j];
347 printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
352 void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */
353 { /* nvals remain intact */
354 Cell *p;
355 int i;
357 for (i = n1; i <= n2; i++) {
358 p = fldtab[i];
359 if (freeable(p))
360 xfree(p->sval);
361 p->sval = "";
362 p->tval = FLD | STR | DONTFREE;
366 void newfld(int n) /* add field n after end of existing lastfld */
368 if (n > nfields)
369 growfldtab(n);
370 cleanfld(lastfld+1, n);
371 lastfld = n;
372 setfval(nfloc, (Awkfloat) n);
375 Cell *fieldadr(int n) /* get nth field */
377 if (n < 0)
378 FATAL("trying to access field %d", n);
379 if (n > nfields) /* fields after NF are empty */
380 growfldtab(n); /* but does not increase NF */
381 return(fldtab[n]);
384 void growfldtab(int n) /* make new fields up to at least $n */
386 int nf = 2 * nfields;
388 if (n > nf)
389 nf = n;
390 fldtab = (Cell **) realloc(fldtab, (nf+1) * (sizeof (struct Cell *)));
391 if (fldtab == NULL)
392 FATAL("out of space creating %d fields", nf);
393 makefields(nfields+1, nf);
394 nfields = nf;
397 int refldbld(char *rec, char *fs) /* build fields from reg expr in FS */
399 /* this relies on having fields[] the same length as $0 */
400 /* the fields are all stored in this one array with \0's */
401 char *fr;
402 void *p;
403 int i, n;
405 n = strlen(rec);
406 if (n > fieldssize) {
407 xfree(fields);
408 if ((fields = (char *) malloc(n+1)) == NULL)
409 FATAL("out of space for fields in refldbld %d", n);
410 fieldssize = n;
412 fr = fields;
413 *fr = '\0';
414 if (*rec == '\0')
415 return 0;
416 p = compre(fs);
417 dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
418 for (i = 1; ; i++) {
419 if (i > nfields)
420 growfldtab(i);
421 if (freeable(fldtab[i]))
422 xfree(fldtab[i]->sval);
423 fldtab[i]->tval = FLD | STR | DONTFREE;
424 fldtab[i]->sval = fr;
425 dprintf( ("refldbld: i=%d\n", i) );
426 if (nematch(p, rec, rec)) {
427 dprintf( ("match %s (%d chars)\n", patbeg, patlen) );
428 strncpy(fr, rec, patbeg-rec);
429 fr += patbeg - rec + 1;
430 *(fr-1) = '\0';
431 rec = patbeg + patlen;
432 } else {
433 dprintf( ("no match %s\n", rec) );
434 strcpy(fr, rec);
435 break;
438 return i;
441 void recbld(void) /* create $0 from $1..$NF if necessary */
443 int i;
444 char *r, *p;
446 if (donerec == 1)
447 return;
448 r = record;
449 for (i = 1; i <= *NF; i++) {
450 p = getsval(fldtab[i]);
451 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
452 FATAL("created $0 `%.30s...' too long", record);
453 while ((*r = *p++) != 0)
454 r++;
455 if (i < *NF) {
456 if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2"))
457 FATAL("created $0 `%.30s...' too long", record);
458 for (p = *OFS; (*r = *p++) != 0; )
459 r++;
462 if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
463 FATAL("built giant record `%.30s...'", record);
464 *r = '\0';
465 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
467 if (freeable(fldtab[0]))
468 xfree(fldtab[0]->sval);
469 fldtab[0]->tval = REC | STR | DONTFREE;
470 fldtab[0]->sval = record;
472 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
473 dprintf( ("recbld = |%s|\n", record) );
474 donerec = 1;
477 int errorflag = 0;
479 void yyerror(char *s)
481 SYNTAX(s);
484 void SYNTAX(char *fmt, ...)
486 extern char *cmdname, *curfname;
487 static int been_here = 0;
488 va_list varg;
490 if (been_here++ > 2)
491 return;
492 fprintf(stderr, "%s: ", cmdname);
493 va_start(varg, fmt);
494 vfprintf(stderr, fmt, varg);
495 va_end(varg);
496 if(compile_time == 1 && cursource() != NULL)
497 fprintf(stderr, " at %s:%d", cursource(), lineno);
498 else
499 fprintf(stderr, " at line %d", lineno);
500 if (curfname != NULL)
501 fprintf(stderr, " in function %s", curfname);
502 fprintf(stderr, "\n");
503 errorflag = 2;
504 eprint();
507 void fpecatch(int n)
509 FATAL("floating point exception %d", n);
512 extern int bracecnt, brackcnt, parencnt;
514 void bracecheck(void)
516 int c;
517 static int beenhere = 0;
519 if (beenhere++)
520 return;
521 while ((c = input()) != EOF && c != '\0')
522 bclass(c);
523 bcheck2(bracecnt, '{', '}');
524 bcheck2(brackcnt, '[', ']');
525 bcheck2(parencnt, '(', ')');
528 void bcheck2(int n, int c1, int c2)
530 if (n == 1)
531 fprintf(stderr, "\tmissing %c\n", c2);
532 else if (n > 1)
533 fprintf(stderr, "\t%d missing %c's\n", n, c2);
534 else if (n == -1)
535 fprintf(stderr, "\textra %c\n", c2);
536 else if (n < -1)
537 fprintf(stderr, "\t%d extra %c's\n", -n, c2);
540 void FATAL(char *fmt, ...)
542 extern char *cmdname;
543 va_list varg;
545 fflush(stdout);
546 fprintf(stderr, "%s: ", cmdname);
547 va_start(varg, fmt);
548 vfprintf(stderr, fmt, varg);
549 va_end(varg);
550 error();
551 if (dbg > 1) /* core dump if serious debugging on */
552 abort();
553 exit(2);
556 void WARNING(char *fmt, ...)
558 extern char *cmdname;
559 va_list varg;
561 fflush(stdout);
562 fprintf(stderr, "%s: ", cmdname);
563 va_start(varg, fmt);
564 vfprintf(stderr, fmt, varg);
565 va_end(varg);
566 error();
569 void error()
571 extern Node *curnode;
572 int line;
574 fprintf(stderr, "\n");
575 if (compile_time != 2 && NR && *NR > 0) {
576 if (strcmp(*FILENAME, "-") != 0)
577 fprintf(stderr, " input record %s:%d", *FILENAME, (int) (*FNR));
578 else
579 fprintf(stderr, " input record number %d", (int) (*FNR));
580 fprintf(stderr, "\n");
582 if (compile_time != 2 && curnode)
583 line = curnode->lineno;
584 else if (compile_time != 2 && lineno)
585 line = lineno;
586 else
587 line = -1;
588 if (compile_time == 1 && cursource() != NULL){
589 if(line >= 0)
590 fprintf(stderr, " source %s:%d", cursource(), line);
591 else
592 fprintf(stderr, " source file %s", cursource());
593 }else if(line >= 0)
594 fprintf(stderr, " source line %d", line);
595 fprintf(stderr, "\n");
596 eprint();
599 void eprint(void) /* try to print context around error */
601 char *p, *q;
602 int c;
603 static int been_here = 0;
604 extern char ebuf[], *ep;
606 if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
607 return;
608 p = ep - 1;
609 if (p > ebuf && *p == '\n')
610 p--;
611 for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
613 while (*p == '\n')
614 p++;
615 fprintf(stderr, " context is\n\t");
616 for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
618 for ( ; p < q; p++)
619 if (*p)
620 putc(*p, stderr);
621 fprintf(stderr, " >>> ");
622 for ( ; p < ep; p++)
623 if (*p)
624 putc(*p, stderr);
625 fprintf(stderr, " <<< ");
626 if (*ep)
627 while ((c = input()) != '\n' && c != '\0' && c != EOF) {
628 putc(c, stderr);
629 bclass(c);
631 putc('\n', stderr);
632 ep = ebuf;
635 void bclass(int c)
637 switch (c) {
638 case '{': bracecnt++; break;
639 case '}': bracecnt--; break;
640 case '[': brackcnt++; break;
641 case ']': brackcnt--; break;
642 case '(': parencnt++; break;
643 case ')': parencnt--; break;
647 double errcheck(double x, char *s)
650 if (errno == EDOM) {
651 errno = 0;
652 WARNING("%s argument out of domain", s);
653 x = 1;
654 } else if (errno == ERANGE) {
655 errno = 0;
656 WARNING("%s result out of range", s);
657 x = 1;
659 return x;
662 int isclvar(char *s) /* is s of form var=something ? */
664 char *os = s;
666 if (!isalpha(*s) && *s != '_')
667 return 0;
668 for ( ; *s; s++)
669 if (!(isalnum(*s) || *s == '_'))
670 break;
671 return *s == '=' && s > os && *(s+1) != '=';
674 /* strtod is supposed to be a proper test of what's a valid number */
676 #include <math.h>
677 int is_number(char *s)
679 double r;
680 char *ep;
682 /*
683 * fast could-it-be-a-number check before calling strtod,
684 * which takes a surprisingly long time to reject non-numbers.
685 */
686 switch (*s) {
687 case '0': case '1': case '2': case '3': case '4':
688 case '5': case '6': case '7': case '8': case '9':
689 case '\t':
690 case '\n':
691 case '\v':
692 case '\f':
693 case '\r':
694 case ' ':
695 case '-':
696 case '+':
697 case '.':
698 case 'n': /* nans */
699 case 'N':
700 case 'i': /* infs */
701 case 'I':
702 break;
703 default:
704 return 0; /* can't be a number */
707 errno = 0;
708 r = strtod(s, &ep);
709 if (ep == s || r == HUGE_VAL || errno == ERANGE)
710 return 0;
711 while (*ep == ' ' || *ep == '\t' || *ep == '\n')
712 ep++;
713 if (*ep == '\0')
714 return 1;
715 else
716 return 0;