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