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 <strings.h>
29#include <ctype.h>
30#include <errno.h>
31#include <stdlib.h>
32#include <stdarg.h>
33#include <limits.h>
34#include <math.h>
35#include "awk.h"
36
37extern int u8_nextlen(const char *s);
38
39char EMPTY[] = { '\0' };
40FILE *infile = NULL;
41bool innew; /* true = infile has not been read by readrec */
42char *file = EMPTY;
43char *record;
44int recsize = RECSIZE;
45char *fields;
46int fieldssize = RECSIZE;
47
48Cell **fldtab; /* pointers to Cells */
49static size_t len_inputFS = 0;
50static char *inputFS = NULL; /* FS at time of input, for field splitting */
51
52#define MAXFLD 2
53int nfields = MAXFLD; /* last allocated slot for $i */
54
55bool donefld; /* true = implies rec broken into fields */
56bool donerec; /* true = record is valid (no flds have changed) */
57
58int lastfld = 0; /* last used field */
59int argno = 1; /* current input argument number */
60extern Awkfloat *ARGC;
61
62static Cell dollar0 = { OCELL, CFLD, NULL, EMPTY, 0.0, REC|STR|DONTFREE, NULL, NULL };
63static Cell dollar1 = { OCELL, CFLD, NULL, EMPTY, 0.0, FLD|STR|DONTFREE, NULL, NULL };
64
65void recinit(unsigned int n)
66{
67 if ( (record = (char *) malloc(n)) == NULL
68 || (fields = (char *) malloc(n+1)) == NULL
69 || (fldtab = (Cell **) calloc(nfields+2, sizeof(*fldtab))) == NULL
70 || (fldtab[0] = (Cell *) malloc(sizeof(**fldtab))) == NULL)
71 FATAL("out of space for $0 and fields");
72 *record = '\0';
73 *fldtab[0] = dollar0;
74 fldtab[0]->sval = record;
75 fldtab[0]->nval = tostring("0");
76 makefields(1, nfields);
77}
78
79void makefields(int n1, int n2) /* create $n1..$n2 inclusive */
80{
81 char temp[50];
82 int i;
83
84 for (i = n1; i <= n2; i++) {
85 fldtab[i] = (Cell *) malloc(sizeof(**fldtab));
86 if (fldtab[i] == NULL)
87 FATAL("out of space in makefields %d", i);
88 *fldtab[i] = dollar1;
89 snprintf(temp, sizeof(temp), "%d", i);
90 fldtab[i]->nval = tostring(temp);
91 }
92}
93
94void initgetrec(void)
95{
96 int i;
97 char *p;
98
99 for (i = 1; i < *ARGC; i++) {
100 p = getargv(i); /* find 1st real filename */
101 if (p == NULL || *p == '\0') { /* deleted or zapped */
102 argno++;
103 continue;
104 }
105 if (!isclvar(p)) {
106 setsval(lookup("FILENAME", symtab), p);
107 return;
108 }
109 setclvar(p); /* a commandline assignment before filename */
110 argno++;
111 }
112 infile = stdin; /* no filenames, so use stdin */
113 innew = true;
114}
115
116/*
117 * POSIX specifies that fields are supposed to be evaluated as if they were
118 * split using the value of FS at the time that the record's value ($0) was
119 * read.
120 *
121 * Since field-splitting is done lazily, we save the current value of FS
122 * whenever a new record is read in (implicitly or via getline), or when
123 * a new value is assigned to $0.
124 */
125void savefs(void)
126{
127 size_t len;
128 if ((len = strlen(getsval(fsloc))) < len_inputFS) {
129 strcpy(inputFS, *FS); /* for subsequent field splitting */
130 return;
131 }
132
133 len_inputFS = len + 1;
134 inputFS = (char *) realloc(inputFS, len_inputFS);
135 if (inputFS == NULL)
136 FATAL("field separator %.10s... is too long", *FS);
137 memcpy(inputFS, *FS, len_inputFS);
138}
139
140static bool firsttime = true;
141
142int getrec(char **pbuf, int *pbufsize, bool isrecord) /* get next input record */
143{ /* note: cares whether buf == record */
144 int c;
145 char *buf = *pbuf;
146 uschar saveb0;
147 int bufsize = *pbufsize, savebufsize = bufsize;
148
149 if (firsttime) {
150 firsttime = false;
151 initgetrec();
152 }
153 DPRINTF("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n",
154 *RS, *FS, *ARGC, *FILENAME);
155 saveb0 = buf[0];
156 buf[0] = 0;
157 while (argno < *ARGC || infile == stdin) {
158 DPRINTF("argno=%d, file=|%s|\n", argno, file);
159 if (infile == NULL) { /* have to open a new file */
160 file = getargv(argno);
161 if (file == NULL || *file == '\0') { /* deleted or zapped */
162 argno++;
163 continue;
164 }
165 if (isclvar(file)) { /* a var=value arg */
166 setclvar(file);
167 argno++;
168 continue;
169 }
170 *FILENAME = file;
171 DPRINTF("opening file %s\n", file);
172 if (*file == '-' && *(file+1) == '\0')
173 infile = stdin;
174 else if ((infile = fopen(file, "r")) == NULL)
175 FATAL("can't open file %s", file);
176 innew = true;
177 setfval(fnrloc, 0.0);
178 }
179 c = readrec(&buf, &bufsize, infile, innew);
180 if (innew)
181 innew = false;
182 if (c != 0 || buf[0] != '\0') { /* normal record */
183 if (isrecord) {
184 double result;
185
186 if (freeable(fldtab[0]))
187 xfree(fldtab[0]->sval);
188 fldtab[0]->sval = buf; /* buf == record */
189 fldtab[0]->tval = REC | STR | DONTFREE;
190 if (is_number(fldtab[0]->sval, & result)) {
191 fldtab[0]->fval = result;
192 fldtab[0]->tval |= NUM;
193 }
194 donefld = false;
195 donerec = true;
196 savefs();
197 }
198 setfval(nrloc, nrloc->fval+1);
199 setfval(fnrloc, fnrloc->fval+1);
200 *pbuf = buf;
201 *pbufsize = bufsize;
202 return 1;
203 }
204 /* EOF arrived on this file; set up next */
205 if (infile != stdin)
206 fclose(infile);
207 infile = NULL;
208 argno++;
209 }
210 buf[0] = saveb0;
211 *pbuf = buf;
212 *pbufsize = savebufsize;
213 return 0; /* true end of file */
214}
215
216void nextfile(void)
217{
218 if (infile != NULL && infile != stdin)
219 fclose(infile);
220 infile = NULL;
221 argno++;
222}
223
224extern int readcsvrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag);
225
226int readrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag) /* read one record into buf */
227{
228 int sep, c, isrec; // POTENTIAL BUG? isrec is a macro in awk.h
229 char *rr = *pbuf, *buf = *pbuf;
230 int bufsize = *pbufsize;
231 char *rs = getsval(rsloc);
232
233 if (CSV) {
234 c = readcsvrec(&buf, &bufsize, inf, newflag);
235 isrec = (c == EOF && rr == buf) ? false : true;
236 } else if (*rs && rs[1]) {
237 bool found;
238
239 memset(buf, 0, bufsize);
240 fa *pfa = makedfa(rs, 1);
241 if (newflag)
242 found = fnematch(pfa, inf, &buf, &bufsize, recsize);
243 else {
244 int tempstat = pfa->initstat;
245 pfa->initstat = 2;
246 found = fnematch(pfa, inf, &buf, &bufsize, recsize);
247 pfa->initstat = tempstat;
248 }
249 if (found)
250 setptr(patbeg, '\0');
251 isrec = (found == 0 && *buf == '\0') ? false : true;
252
253 } else {
254 if ((sep = *rs) == 0) {
255 sep = '\n';
256 while ((c=getc(inf)) == '\n' && c != EOF) /* skip leading \n's */
257 ;
258 if (c != EOF)
259 ungetc(c, inf);
260 }
261 for (rr = buf; ; ) {
262 for (; (c=getc(inf)) != sep && c != EOF; ) {
263 if (rr-buf+1 > bufsize)
264 if (!adjbuf(&buf, &bufsize, 1+rr-buf,
265 recsize, &rr, "readrec 1"))
266 FATAL("input record `%.30s...' too long", buf);
267 *rr++ = c;
268 }
269 if (*rs == sep || c == EOF)
270 break;
271 if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */
272 break;
273 if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr,
274 "readrec 2"))
275 FATAL("input record `%.30s...' too long", buf);
276 *rr++ = '\n';
277 *rr++ = c;
278 }
279 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
280 FATAL("input record `%.30s...' too long", buf);
281 *rr = 0;
282 isrec = (c == EOF && rr == buf) ? false : true;
283 }
284 *pbuf = buf;
285 *pbufsize = bufsize;
286 DPRINTF("readrec saw <%s>, returns %d\n", buf, isrec);
287 return isrec;
288}
289
290
291/*******************
292 * loose ends here:
293 * \r\n should become \n
294 * what about bare \r? Excel uses that for embedded newlines
295 * can't have "" in unquoted fields, according to RFC 4180
296*/
297
298
299int readcsvrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag) /* csv can have \n's */
300{ /* so read a complete record that might be multiple lines */
301 int sep, c;
302
303 (void)newflag;
304 char *rr = *pbuf, *buf = *pbuf;
305 int bufsize = *pbufsize;
306 bool in_quote = false;
307
308 sep = '\n'; /* the only separator; have to skip over \n embedded in "..." */
309 rr = buf;
310 while ((c = getc(inf)) != EOF) {
311 if (c == sep) {
312 if (! in_quote)
313 break;
314 if (rr > buf && rr[-1] == '\r') // remove \r if was \r\n
315 rr--;
316 }
317
318 if (rr-buf+1 > bufsize)
319 if (!adjbuf(&buf, &bufsize, 1+rr-buf,
320 recsize, &rr, "readcsvrec 1"))
321 FATAL("input record `%.30s...' too long", buf);
322 *rr++ = c;
323 if (c == '"')
324 in_quote = ! in_quote;
325 }
326 if (c == '\n' && rr > buf && rr[-1] == '\r') // remove \r if was \r\n
327 rr--;
328
329 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readcsvrec 4"))
330 FATAL("input record `%.30s...' too long", buf);
331 *rr = 0;
332 *pbuf = buf;
333 *pbufsize = bufsize;
334 DPRINTF("readcsvrec saw <%s>, returns %d\n", buf, c);
335 return c;
336}
337
338char *getargv(int n) /* get ARGV[n] */
339{
340 Array *ap;
341 Cell *x;
342 char *s, temp[50];
343 extern Cell *ARGVcell;
344
345 ap = (Array *)ARGVcell->sval;
346 snprintf(temp, sizeof(temp), "%d", n);
347 if (lookup(temp, ap) == NULL)
348 return NULL;
349 x = setsymtab(temp, "", 0.0, STR, ap);
350 s = getsval(x);
351 DPRINTF("getargv(%d) returns |%s|\n", n, s);
352 return s;
353}
354
355void setclvar(char *s) /* set var=value from s */
356{
357 char *e, *p;
358 Cell *q;
359 double result;
360
361/* commit f3d9187d4e0f02294fb1b0e31152070506314e67 broke T.argv test */
362/* I don't understand why it was changed. */
363
364 for (p=s; *p != '='; p++)
365 ;
366 e = p;
367 *p++ = 0;
368 p = qstring(p, '\0');
369 q = setsymtab(s, p, 0.0, STR, symtab);
370 setsval(q, p);
371 if (is_number(q->sval, & result)) {
372 q->fval = result;
373 q->tval |= NUM;
374 }
375 DPRINTF("command line set %s to |%s|\n", s, p);
376 free(p);
377 *e = '=';
378}
379
380
381void fldbld(void) /* create fields from current record */
382{
383 /* this relies on having fields[] the same length as $0 */
384 /* the fields are all stored in this one array with \0's */
385 /* possibly with a final trailing \0 not associated with any field */
386 char *r, *fr, sep;
387 Cell *p;
388 int i, j, n;
389
390 if (donefld)
391 return;
392 if (!isstr(fldtab[0]))
393 getsval(fldtab[0]);
394 r = fldtab[0]->sval;
395 n = strlen(r);
396 if (n > fieldssize) {
397 xfree(fields);
398 if ((fields = (char *) malloc(n+2)) == NULL) /* possibly 2 final \0s */
399 FATAL("out of space for fields in fldbld %d", n);
400 fieldssize = n;
401 }
402 fr = fields;
403 i = 0; /* number of fields accumulated here */
404 if (inputFS == NULL) /* make sure we have a copy of FS */
405 savefs();
406 if (!CSV && strlen(inputFS) > 1) { /* it's a regular expression */
407 i = refldbld(r, inputFS);
408 } else if (!CSV && (sep = *inputFS) == ' ') { /* default whitespace */
409 for (i = 0; ; ) {
410 while (*r == ' ' || *r == '\t' || *r == '\n')
411 r++;
412 if (*r == 0)
413 break;
414 i++;
415 if (i > nfields)
416 growfldtab(i);
417 if (freeable(fldtab[i]))
418 xfree(fldtab[i]->sval);
419 fldtab[i]->sval = fr;
420 fldtab[i]->tval = FLD | STR | DONTFREE;
421 do
422 *fr++ = *r++;
423 while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
424 *fr++ = 0;
425 }
426 *fr = 0;
427 } else if (CSV) { /* CSV processing. no error handling */
428 if (*r != 0) {
429 for (;;) {
430 i++;
431 if (i > nfields)
432 growfldtab(i);
433 if (freeable(fldtab[i]))
434 xfree(fldtab[i]->sval);
435 fldtab[i]->sval = fr;
436 fldtab[i]->tval = FLD | STR | DONTFREE;
437 if (*r == '"' ) { /* start of "..." */
438 for (r++ ; *r != '\0'; ) {
439 if (*r == '"' && r[1] != '\0' && r[1] == '"') {
440 r += 2; /* doubled quote */
441 *fr++ = '"';
442 } else if (*r == '"' && (r[1] == '\0' || r[1] == ',')) {
443 r++; /* skip over closing quote */
444 break;
445 } else {
446 *fr++ = *r++;
447 }
448 }
449 *fr++ = 0;
450 } else { /* unquoted field */
451 while (*r != ',' && *r != '\0')
452 *fr++ = *r++;
453 *fr++ = 0;
454 }
455 if (*r++ == 0)
456 break;
457
458 }
459 }
460 *fr = 0;
461 } else if ((sep = *inputFS) == 0) { /* new: FS="" => 1 char/field */
462 for (i = 0; *r != '\0'; ) {
463 char buf[10];
464 i++;
465 if (i > nfields)
466 growfldtab(i);
467 if (freeable(fldtab[i]))
468 xfree(fldtab[i]->sval);
469 n = u8_nextlen(r);
470 for (j = 0; j < n; j++)
471 buf[j] = *r++;
472 buf[j] = '\0';
473 fldtab[i]->sval = tostring(buf);
474 fldtab[i]->tval = FLD | STR;
475 }
476 *fr = 0;
477 } else if (*r != 0) { /* if 0, it's a null field */
478 /* subtle case: if length(FS) == 1 && length(RS > 0)
479 * \n is NOT a field separator (cf awk book 61,84).
480 * this variable is tested in the inner while loop.
481 */
482 int rtest = '\n'; /* normal case */
483 if (strlen(*RS) > 0)
484 rtest = '\0';
485 for (;;) {
486 i++;
487 if (i > nfields)
488 growfldtab(i);
489 if (freeable(fldtab[i]))
490 xfree(fldtab[i]->sval);
491 fldtab[i]->sval = fr;
492 fldtab[i]->tval = FLD | STR | DONTFREE;
493 while (*r != sep && *r != rtest && *r != '\0') /* \n is always a separator */
494 *fr++ = *r++;
495 *fr++ = 0;
496 if (*r++ == 0)
497 break;
498 }
499 *fr = 0;
500 }
501 if (i > nfields)
502 FATAL("record `%.30s...' has too many fields; can't happen", r);
503 cleanfld(i+1, lastfld); /* clean out junk from previous record */
504 lastfld = i;
505 donefld = true;
506 for (j = 1; j <= lastfld; j++) {
507 double result;
508
509 p = fldtab[j];
510 if(is_number(p->sval, & result)) {
511 p->fval = result;
512 p->tval |= NUM;
513 }
514 }
515 setfval(nfloc, (Awkfloat) lastfld);
516 donerec = true; /* restore */
517 if (dbg) {
518 for (j = 0; j <= lastfld; j++) {
519 p = fldtab[j];
520 printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
521 }
522 }
523}
524
525void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */
526{ /* nvals remain intact */
527 Cell *p;
528 int i;
529
530 for (i = n1; i <= n2; i++) {
531 p = fldtab[i];
532 if (freeable(p))
533 xfree(p->sval);
534 p->sval = EMPTY,
535 p->tval = FLD | STR | DONTFREE;
536 }
537}
538
539void newfld(int n) /* add field n after end of existing lastfld */
540{
541 if (n > nfields)
542 growfldtab(n);
543 cleanfld(lastfld+1, n);
544 lastfld = n;
545 setfval(nfloc, (Awkfloat) n);
546}
547
548void setlastfld(int n) /* set lastfld cleaning fldtab cells if necessary */
549{
550 if (n < 0)
551 FATAL("cannot set NF to a negative value");
552 if (n > nfields)
553 growfldtab(n);
554
555 if (lastfld < n)
556 cleanfld(lastfld+1, n);
557 else
558 cleanfld(n+1, lastfld);
559
560 lastfld = n;
561}
562
563Cell *fieldadr(int n) /* get nth field */
564{
565 if (n < 0)
566 FATAL("trying to access out of range field %d", n);
567 if (n > nfields) /* fields after NF are empty */
568 growfldtab(n); /* but does not increase NF */
569 return(fldtab[n]);
570}
571
572void growfldtab(int n) /* make new fields up to at least $n */
573{
574 int nf = 2 * nfields;
575 size_t s;
576
577 if (n > nf)
578 nf = n;
579 s = (nf+1) * (sizeof (struct Cell *)); /* freebsd: how much do we need? */
580 if (s / sizeof(struct Cell *) - 1 == (size_t)nf) /* didn't overflow */
581 fldtab = (Cell **) realloc(fldtab, s);
582 else /* overflow sizeof int */
583 xfree(fldtab); /* make it null */
584 if (fldtab == NULL)
585 FATAL("out of space creating %d fields", nf);
586 makefields(nfields+1, nf);
587 nfields = nf;
588}
589
590int refldbld(const char *rec, const char *fs) /* build fields from reg expr in FS */
591{
592 /* this relies on having fields[] the same length as $0 */
593 /* the fields are all stored in this one array with \0's */
594 char *fr;
595 int i, tempstat, n;
596 fa *pfa;
597
598 n = strlen(rec);
599 if (n > fieldssize) {
600 xfree(fields);
601 if ((fields = (char *) malloc(n+1)) == NULL)
602 FATAL("out of space for fields in refldbld %d", n);
603 fieldssize = n;
604 }
605 fr = fields;
606 *fr = '\0';
607 if (*rec == '\0')
608 return 0;
609 pfa = makedfa(fs, 1);
610 DPRINTF("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs);
611 tempstat = pfa->initstat;
612 for (i = 1; ; i++) {
613 if (i > nfields)
614 growfldtab(i);
615 if (freeable(fldtab[i]))
616 xfree(fldtab[i]->sval);
617 fldtab[i]->tval = FLD | STR | DONTFREE;
618 fldtab[i]->sval = fr;
619 DPRINTF("refldbld: i=%d\n", i);
620 if (nematch(pfa, rec)) {
621 pfa->initstat = 2; /* horrible coupling to b.c */
622 DPRINTF("match %s (%d chars)\n", patbeg, patlen);
623 strncpy(fr, rec, patbeg-rec);
624 fr += patbeg - rec + 1;
625 *(fr-1) = '\0';
626 rec = patbeg + patlen;
627 } else {
628 DPRINTF("no match %s\n", rec);
629 strcpy(fr, rec);
630 pfa->initstat = tempstat;
631 break;
632 }
633 }
634 return i;
635}
636
637void recbld(void) /* create $0 from $1..$NF if necessary */
638{
639 int i;
640 char *r, *p;
641 char *sep = getsval(ofsloc);
642
643 if (donerec)
644 return;
645 r = record;
646 for (i = 1; i <= *NF; i++) {
647 p = getsval(fldtab[i]);
648 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
649 FATAL("created $0 `%.30s...' too long", record);
650 while ((*r = *p++) != 0)
651 r++;
652 if (i < *NF) {
653 if (!adjbuf(&record, &recsize, 2+strlen(sep)+r-record, recsize, &r, "recbld 2"))
654 FATAL("created $0 `%.30s...' too long", record);
655 for (p = sep; (*r = *p++) != 0; )
656 r++;
657 }
658 }
659 if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
660 FATAL("built giant record `%.30s...'", record);
661 *r = '\0';
662 DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]);
663
664 if (freeable(fldtab[0]))
665 xfree(fldtab[0]->sval);
666 fldtab[0]->tval = REC | STR | DONTFREE;
667 fldtab[0]->sval = record;
668
669 DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]);
670 DPRINTF("recbld = |%s|\n", record);
671 donerec = true;
672}
673
674int errorflag = 0;
675
676void yyerror(const char *s)
677{
678 SYNTAX("%s", s);
679}
680
681void SYNTAX(const char *fmt, ...)
682{
683 extern char *cmdname, *curfname;
684 static int been_here = 0;
685 va_list varg;
686
687 if (been_here++ > 2)
688 return;
689 fprintf(stderr, "%s: ", cmdname);
690 va_start(varg, fmt);
691 vfprintf(stderr, fmt, varg);
692 va_end(varg);
693 fprintf(stderr, " at source line %d", lineno);
694 if (curfname != NULL)
695 fprintf(stderr, " in function %s", curfname);
696 if (compile_time == COMPILING && cursource() != NULL)
697 fprintf(stderr, " source file %s", cursource());
698 fprintf(stderr, "\n");
699 errorflag = 2;
700 eprint();
701}
702
703extern int bracecnt, brackcnt, parencnt;
704
705void bracecheck(void)
706{
707 int c;
708 static int beenhere = 0;
709
710 if (beenhere++)
711 return;
712 while ((c = input()) != EOF && c != '\0')
713 bclass(c);
714 bcheck2(bracecnt, '{', '}');
715 bcheck2(brackcnt, '[', ']');
716 bcheck2(parencnt, '(', ')');
717}
718
719void bcheck2(int n, int c1, int c2)
720{
721 (void)c1;
722 if (n == 1)
723 fprintf(stderr, "\tmissing %c\n", c2);
724 else if (n > 1)
725 fprintf(stderr, "\t%d missing %c's\n", n, c2);
726 else if (n == -1)
727 fprintf(stderr, "\textra %c\n", c2);
728 else if (n < -1)
729 fprintf(stderr, "\t%d extra %c's\n", -n, c2);
730}
731
732void FATAL(const char *fmt, ...)
733{
734 extern char *cmdname;
735 va_list varg;
736
737 fflush(stdout);
738 fprintf(stderr, "%s: ", cmdname);
739 va_start(varg, fmt);
740 vfprintf(stderr, fmt, varg);
741 va_end(varg);
742 error();
743 if (dbg > 1) /* core dump if serious debugging on */
744 abort();
745 exit(2);
746}
747
748void WARNING(const char *fmt, ...)
749{
750 extern char *cmdname;
751 va_list varg;
752
753 fflush(stdout);
754 fprintf(stderr, "%s: ", cmdname);
755 va_start(varg, fmt);
756 vfprintf(stderr, fmt, varg);
757 va_end(varg);
758 error();
759}
760
761void error()
762{
763 extern Node *curnode;
764
765 fprintf(stderr, "\n");
766 if (compile_time != ERROR_PRINTING) {
767 if (NR && *NR > 0) {
768 fprintf(stderr, " input record number %d", (int) (*FNR));
769 if (strcmp(*FILENAME, "-") != 0)
770 fprintf(stderr, ", file %s", *FILENAME);
771 fprintf(stderr, "\n");
772 }
773 if (curnode)
774 fprintf(stderr, " source line number %d", curnode->lineno);
775 else if (lineno)
776 fprintf(stderr, " source line number %d", lineno);
777 if (compile_time == COMPILING && cursource() != NULL)
778 fprintf(stderr, " source file %s", cursource());
779 fprintf(stderr, "\n");
780 eprint();
781 }
782}
783
784void eprint(void) /* try to print context around error */
785{
786 char *p, *q;
787 int c;
788 static int been_here = 0;
789 extern char ebuf[], *ep;
790
791 if (compile_time != COMPILING || been_here++ > 0 || ebuf == ep)
792 return;
793 if (ebuf == ep)
794 return;
795 p = ep - 1;
796 if (p > ebuf && *p == '\n')
797 p--;
798 for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
799 ;
800 while (*p == '\n')
801 p++;
802 fprintf(stderr, " context is\n\t");
803 for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
804 ;
805 for ( ; p < q; p++)
806 if (*p)
807 putc(*p, stderr);
808 fprintf(stderr, " >>> ");
809 for ( ; p < ep; p++)
810 if (*p)
811 putc(*p, stderr);
812 fprintf(stderr, " <<< ");
813 if (*ep)
814 while ((c = input()) != '\n' && c != '\0' && c != EOF) {
815 putc(c, stderr);
816 bclass(c);
817 }
818 putc('\n', stderr);
819 ep = ebuf;
820}
821
822void bclass(int c)
823{
824 switch (c) {
825 case '{': bracecnt++; break;
826 case '}': bracecnt--; break;
827 case '[': brackcnt++; break;
828 case ']': brackcnt--; break;
829 case '(': parencnt++; break;
830 case ')': parencnt--; break;
831 }
832}
833
834int isclvar(const char *s) /* is s of form var=something ? */
835{
836 const char *os = s;
837
838 if (!isalpha((int) *s) && *s != '_')
839 return 0;
840 for ( ; *s; s++)
841 if (!(isalnum((int) *s) || *s == '_'))
842 break;
843 return *s == '=' && s > os;
844}
845
846/* strtod is supposed to be a proper test of what's a valid number */
847/* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
848/* wrong: violates 4.10.1.4 of ansi C standard */
849
850/* well, not quite. As of C99, hex floating point is allowed. so this is
851 * a bit of a mess. We work around the mess by checking for a hexadecimal
852 * value and disallowing it. Similarly, we now follow gawk and allow only
853 * +nan, -nan, +inf, and -inf for NaN and infinity values.
854 */
855
856/*
857 * This routine now has a more complicated interface, the main point
858 * being to avoid the double conversion of a string to double, and
859 * also to convey out, if requested, the information that the numeric
860 * value was a leading string or is all of the string. The latter bit
861 * is used in getfval().
862 */
863
864bool is_valid_number(const char *s, bool trailing_stuff_ok,
865 bool *no_trailing, double *result)
866{
867 double r;
868 char *ep;
869 bool retval = false;
870 bool is_nan = false;
871 bool is_inf = false;
872
873 if (no_trailing)
874 *no_trailing = false;
875
876 while (isspace((int) *s))
877 s++;
878
879 /* no hex floating point, sorry */
880 if (s[0] == '0' && tolower(s[1]) == 'x' && isxdigit(s[2]))
881 return false;
882
883 /* allow +nan, -nan, +inf, -inf, any other letter, no */
884 if (s[0] == '+' || s[0] == '-') {
885 is_nan = (strncasecmp(s+1, "nan", 3) == 0);
886 is_inf = (strncasecmp(s+1, "inf", 3) == 0);
887 if ((is_nan || is_inf)
888 && (isspace((int) s[4]) || s[4] == '\0'))
889 goto convert;
890 else if (! isdigit(s[1]) && s[1] != '.')
891 return false;
892 }
893 else if (! isdigit(s[0]) && s[0] != '.')
894 return false;
895
896convert:
897 errno = 0;
898 r = strtod(s, &ep);
899 if (ep == s || errno == ERANGE)
900 return false;
901
902 if (isnan(r) && s[0] == '-' && signbit(r) == 0)
903 r = -r;
904
905 if (result != NULL)
906 *result = r;
907
908 /*
909 * check for trailing stuff
910 */
911 while (isspace((int) *ep))
912 ep++;
913
914 if (no_trailing != NULL)
915 *no_trailing = (*ep == '\0');
916
917 /* return true if found the end, or trailing stuff is allowed */
918 retval = *ep == '\0' || trailing_stuff_ok;
919
920 return retval;
921}