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