1/* ***************************************************************
2 * copyright (c) lucent technologies 1997
3 * all rights reserved
4 *
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
14 *
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 * ************************************************************** */
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 * cant 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 dont 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) { /* its 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, its 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) /* didnt 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}