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