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