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