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