1 /****************************************************************
2 Copyright (C) Lucent Technologies 1997
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
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
23 ****************************************************************/
35 int recsize = RECSIZE;
37 int fieldssize = RECSIZE;
39 Cell **fldtab; /* pointers to Cells */
40 char inputFS[100] = " ";
43 int nfields = MAXFLD; /* last allocated slot for $i */
45 int donefld; /* 1 = implies rec broken into fields */
46 int donerec; /* 1 = record is valid (no flds have changed) */
48 int lastfld = 0; /* last used field */
49 int argno = 1; /* current input argument number */
50 extern Awkfloat *AARGC;
52 static Cell dollar0 = { OCELL, CFLD, nil, "", 0.0, REC|STR|DONTFREE };
53 static Cell dollar1 = { OCELL, CFLD, nil, "", 0.0, FLD|STR|DONTFREE };
55 void recinit(unsigned int n)
57 record = (char *) malloc(n);
58 fields = (char *) malloc(n);
59 fldtab = (Cell **) malloc((nfields+1) * sizeof(Cell *));
60 if (record == nil || fields == nil || fldtab == nil)
61 FATAL("out of space for $0 and fields");
62 fldtab[0] = (Cell *) malloc(sizeof (Cell));
64 fldtab[0]->sval = record;
65 fldtab[0]->nval = tostring("0");
66 makefields(1, nfields);
69 void makefields(int n1, int n2) /* create $n1..$n2 inclusive */
74 for (i = n1; i <= n2; i++) {
75 fldtab[i] = (Cell *) malloc(sizeof (struct Cell));
77 FATAL("out of space in makefields %d", i);
79 sprint(temp, "%d", i);
80 fldtab[i]->nval = tostring(temp);
89 for (i = 1; i < *AARGC; i++) {
90 if (!isclvar(p = getargv(i))) { /* find 1st real filename */
91 setsval(lookup("FILENAME", symtab), getargv(i));
94 setclvar(p); /* a commandline assignment before filename */
97 infile = &stdin; /* no filenames, so use &stdin */
100 int getrec(char **pbuf, int *pbufsize, int isrecord) /* get next input record */
101 { /* note: cares whether buf == record */
103 static int firsttime = 1;
105 int bufsize = *pbufsize;
111 dprint( ("RS=<%s>, FS=<%s>, AARGC=%g, FILENAME=%s\n",
112 *RS, *FS, *AARGC, *FILENAME) );
118 while (argno < *AARGC || infile == &stdin) {
119 dprint( ("argno=%d, file=|%s|\n", argno, file) );
120 if (infile == nil) { /* have to open a new file */
121 file = getargv(argno);
122 if (*file == '\0') { /* it's been zapped */
126 if (isclvar(file)) { /* a var=value arg */
132 dprint( ("opening file %s\n", file) );
133 if (*file == '-' && *(file+1) == '\0')
135 else if ((infile = Bopen(file, OREAD)) == nil)
136 FATAL("can't open file %s", file);
137 setfval(fnrloc, 0.0);
139 c = readrec(&buf, &bufsize, infile);
140 if (c != 0 || buf[0] != '\0') { /* normal record */
142 if (freeable(fldtab[0]))
143 xfree(fldtab[0]->sval);
144 fldtab[0]->sval = buf; /* buf == record */
145 fldtab[0]->tval = REC | STR | DONTFREE;
146 if (is_number(fldtab[0]->sval)) {
147 fldtab[0]->fval = atof(fldtab[0]->sval);
148 fldtab[0]->tval |= NUM;
151 setfval(nrloc, nrloc->fval+1);
152 setfval(fnrloc, fnrloc->fval+1);
157 /* Beof arrived on this file; set up next */
158 if (infile != &stdin)
165 return 0; /* true end of file */
170 if (infile != &stdin)
176 int readrec(char **pbuf, int *pbufsize, Biobuf *inf) /* read one record into buf */
179 char *rr, *buf = *pbuf;
180 int bufsize = *pbufsize;
182 if (strlen(*FS) >= sizeof(inputFS))
183 FATAL("field separator %.10s... is too long", *FS);
184 strcpy(inputFS, *FS); /* for subsequent field splitting */
185 if ((sep = **RS) == 0) {
187 while ((c=Bgetc(inf)) == '\n' && c != Beof) /* skip leading \n's */
193 for (; (c=Bgetc(inf)) != sep && c != Beof; ) {
194 if (rr-buf+1 > bufsize)
195 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 1"))
196 FATAL("input record `%.30s...' too long", buf);
199 if (**RS == sep || c == Beof)
201 if ((c = Bgetc(inf)) == '\n' || c == Beof) /* 2 in a row */
203 if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr, "readrec 2"))
204 FATAL("input record `%.30s...' too long", buf);
208 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
209 FATAL("input record `%.30s...' too long", buf);
211 dprint( ("readrec saw <%s>, returns %d\n", buf, c == Beof && rr == buf ? 0 : 1) );
214 return c == Beof && rr == buf ? 0 : 1;
217 char *getargv(int n) /* get ARGV[n] */
221 extern Array *ARGVtab;
223 sprint(temp, "%d", n);
224 x = setsymtab(temp, "", 0.0, STR, ARGVtab);
226 dprint( ("getargv(%d) returns |%s|\n", n, s) );
230 void setclvar(char *s) /* set var=value from s */
235 for (p=s; *p != '='; p++)
238 p = qstring(p, '\0');
239 q = setsymtab(s, p, 0.0, STR, symtab);
241 if (is_number(q->sval)) {
242 q->fval = atof(q->sval);
245 dprint( ("command line set %s to |%s|\n", s, p) );
249 void fldbld(void) /* create fields from current record */
251 /* this relies on having fields[] the same length as $0 */
252 /* the fields are all stored in this one array with \0's */
259 if (!isstr(fldtab[0]))
263 if (n > fieldssize) {
265 if ((fields = (char *) malloc(n+1)) == nil)
266 FATAL("out of space for fields in fldbld %d", n);
270 i = 0; /* number of fields accumulated here */
271 if (strlen(inputFS) > 1) { /* it's a regular expression */
272 i = refldbld(r, inputFS);
273 } else if (*inputFS == ' ') { /* default whitespace */
275 while (*r == ' ' || *r == '\t' || *r == '\n')
282 if (freeable(fldtab[i]))
283 xfree(fldtab[i]->sval);
284 fldtab[i]->sval = fr;
285 fldtab[i]->tval = FLD | STR | DONTFREE;
288 while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
292 } else if ((sep = *inputFS) == 0) { /* new: FS="" => 1 char/field */
293 for (i = 0; *r != 0; r++) {
298 if (freeable(fldtab[i]))
299 xfree(fldtab[i]->sval);
302 fldtab[i]->sval = tostring(buf);
303 fldtab[i]->tval = FLD | STR;
306 } else if (*r != 0) { /* if 0, it's a null field */
311 if (freeable(fldtab[i]))
312 xfree(fldtab[i]->sval);
313 fldtab[i]->sval = fr;
314 fldtab[i]->tval = FLD | STR | DONTFREE;
315 while (*r != sep && *r != '\n' && *r != '\0') /* \n is always a separator */
324 FATAL("record `%.30s...' has too many fields; can't happen", r);
325 cleanfld(i+1, lastfld); /* clean out junk from previous record */
328 for (j = 1; j <= lastfld; j++) {
330 if(is_number(p->sval)) {
331 p->fval = atof(p->sval);
335 setfval(nfloc, (Awkfloat) lastfld);
337 for (j = 0; j <= lastfld; j++) {
339 print("field %d (%s): |%s|\n", j, p->nval, p->sval);
344 void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */
345 { /* nvals remain intact */
349 for (i = n1; i <= n2; i++) {
354 p->tval = FLD | STR | DONTFREE;
358 void newfld(int n) /* add field n after end of existing lastfld */
362 cleanfld(lastfld+1, n);
364 setfval(nfloc, (Awkfloat) n);
367 Cell *fieldadr(int n) /* get nth field */
370 FATAL("trying to access field %d", n);
371 if (n > nfields) /* fields after NF are empty */
372 growfldtab(n); /* but does not increase NF */
376 void growfldtab(int n) /* make new fields up to at least $n */
378 int nf = 2 * nfields;
382 fldtab = (Cell **) realloc(fldtab, (nf+1) * (sizeof (struct Cell *)));
384 FATAL("out of space creating %d fields", nf);
385 makefields(nfields+1, nf);
389 int refldbld(char *rec, char *fs) /* build fields from reg expr in FS */
391 /* this relies on having fields[] the same length as $0 */
392 /* the fields are all stored in this one array with \0's */
398 if (n > fieldssize) {
400 if ((fields = (char *) malloc(n+1)) == nil)
401 FATAL("out of space for fields in refldbld %d", n);
409 dprint( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
413 if (freeable(fldtab[i]))
414 xfree(fldtab[i]->sval);
415 fldtab[i]->tval = FLD | STR | DONTFREE;
416 fldtab[i]->sval = fr;
417 dprint( ("refldbld: i=%d\n", i) );
418 if (nematch(p, rec, rec)) {
419 dprint( ("match %s (%d chars)\n", patbeg, patlen) );
420 strncpy(fr, rec, patbeg-rec);
421 fr += patbeg - rec + 1;
423 rec = patbeg + patlen;
425 dprint( ("no match %s\n", rec) );
433 void recbld(void) /* create $0 from $1..$NF if necessary */
441 for (i = 1; i <= *NF; i++) {
442 p = getsval(fldtab[i]);
443 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
444 FATAL("created $0 `%.30s...' too long", record);
445 while ((*r = *p++) != 0)
448 if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2"))
449 FATAL("created $0 `%.30s...' too long", record);
450 for (p = *OFS; (*r = *p++) != 0; )
454 if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
455 FATAL("built giant record `%.30s...'", record);
457 dprint( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
459 if (freeable(fldtab[0]))
460 xfree(fldtab[0]->sval);
461 fldtab[0]->tval = REC | STR | DONTFREE;
462 fldtab[0]->sval = record;
464 dprint( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
465 dprint( ("recbld = |%s|\n", record) );
469 char *exitstatus = nil;
471 void yyerror(char *s)
476 void SYNTAX(char *fmt, ...)
478 extern char *cmdname, *curfname;
479 static int been_here = 0;
484 Bprint(&stderr, "%s: ", cmdname);
486 Bvprint(&stderr, fmt, varg);
488 if(compile_time == 1 && cursource() != nil)
489 Bprint(&stderr, " at %s:%d", cursource(), lineno);
491 Bprint(&stderr, " at line %d", lineno);
493 Bprint(&stderr, " in function %s", curfname);
494 Bprint(&stderr, "\n");
495 exitstatus = "syntax error";
499 int handler(void *, char *err)
502 fprint(2, "%s\n", err);
506 extern int bracecnt, brackcnt, parencnt;
508 void bracecheck(void)
511 static int beenhere = 0;
515 while ((c = input()) != Beof && c != '\0')
517 bcheck2(bracecnt, '{', '}');
518 bcheck2(brackcnt, '[', ']');
519 bcheck2(parencnt, '(', ')');
522 void bcheck2(int n, int, int c2)
525 Bprint(&stderr, "\tmissing %c\n", c2);
527 Bprint(&stderr, "\t%d missing %c's\n", n, c2);
529 Bprint(&stderr, "\textra %c\n", c2);
531 Bprint(&stderr, "\t%d extra %c's\n", -n, c2);
534 void FATAL(char *fmt, ...)
536 extern char *cmdname;
540 Bprint(&stderr, "%s: ", cmdname);
542 Bvprint(&stderr, fmt, varg);
545 if (dbg > 1) /* core dump if serious debugging on */
550 void WARNING(char *fmt, ...)
552 extern char *cmdname;
556 Bprint(&stderr, "%s: ", cmdname);
558 Bvprint(&stderr, fmt, varg);
565 extern Node *curnode;
568 Bprint(&stderr, "\n");
569 if (compile_time != 2 && NR && *NR > 0) {
570 if (strcmp(*FILENAME, "-") != 0)
571 Bprint(&stderr, " input record %s:%d", *FILENAME, (int) (*FNR));
573 Bprint(&stderr, " input record number %d", (int) (*FNR));
574 Bprint(&stderr, "\n");
576 if (compile_time != 2 && curnode)
577 line = curnode->lineno;
578 else if (compile_time != 2 && lineno)
582 if (compile_time == 1 && cursource() != nil){
584 Bprint(&stderr, " source %s:%d", cursource(), line);
586 Bprint(&stderr, " source file %s", cursource());
588 Bprint(&stderr, " source line %d", line);
589 Bprint(&stderr, "\n");
593 void eprint(void) /* try to print context around error */
597 static int been_here = 0;
598 extern char ebuf[], *ep;
600 if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
603 if (p > ebuf && *p == '\n')
605 for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
609 Bprint(&stderr, " context is\n\t");
610 for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
615 Bprint(&stderr, " >>> ");
619 Bprint(&stderr, " <<< ");
621 while ((c = input()) != '\n' && c != '\0' && c != Beof) {
625 Bputc(&stderr, '\n');
632 case '{': bracecnt++; break;
633 case '}': bracecnt--; break;
634 case '[': brackcnt++; break;
635 case ']': brackcnt--; break;
636 case '(': parencnt++; break;
637 case ')': parencnt--; break;
641 double errcheck(double x, char *s)
645 WARNING("%s argument out of domain", s);
647 } else if (isInf(x, 1) || isInf(x, -1)) {
648 WARNING("%s result out of range", s);
654 int isclvar(char *s) /* is s of form var=something ? */
658 if (!isalpha(*s) && *s != '_')
661 if (!(isalnum(*s) || *s == '_'))
663 return *s == '=' && s > os && *(s+1) != '=';
666 /* strtod is supposed to be a proper test of what's a valid number */
668 int is_number(char *s)
674 * fast could-it-be-a-number check before calling strtod,
675 * which takes a surprisingly long time to reject non-numbers.
678 case '0': case '1': case '2': case '3': case '4':
679 case '5': case '6': case '7': case '8': case '9':
695 return 0; /* can't be a number */
699 if (ep == s || isInf(r, 1) || isInf(r, -1) || isNaN(r))
701 while (*ep == ' ' || *ep == '\t' || *ep == '\n')