xref: /dragonfly/contrib/awk/lib.c (revision 9317c2d0)
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 }
782