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