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