xref: /freebsd/contrib/one-true-awk/lib.c (revision c697fb7f)
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 "awk.h"
33 #include "ytab.h"
34 
35 FILE	*infile	= NULL;
36 char	*file	= "";
37 char	*record;
38 int	recsize	= RECSIZE;
39 char	*fields;
40 int	fieldssize = RECSIZE;
41 
42 Cell	**fldtab;	/* pointers to Cells */
43 char	inputFS[100] = " ";
44 
45 #define	MAXFLD	2
46 int	nfields	= MAXFLD;	/* last allocated slot for $i */
47 
48 int	donefld;	/* 1 = implies rec broken into fields */
49 int	donerec;	/* 1 = record is valid (no flds have changed) */
50 
51 int	lastfld	= 0;	/* last used field */
52 int	argno	= 1;	/* current input argument number */
53 extern	Awkfloat *ARGC;
54 
55 static Cell dollar0 = { OCELL, CFLD, NULL, "", 0.0, REC|STR|DONTFREE };
56 static Cell dollar1 = { OCELL, CFLD, NULL, "", 0.0, FLD|STR|DONTFREE };
57 
58 void recinit(unsigned int n)
59 {
60 	if ( (record = (char *) malloc(n)) == NULL
61 	  || (fields = (char *) malloc(n+1)) == NULL
62 	  || (fldtab = (Cell **) malloc((nfields+2) * sizeof(Cell *))) == NULL
63 	  || (fldtab[0] = (Cell *) malloc(sizeof(Cell))) == NULL )
64 		FATAL("out of space for $0 and fields");
65 	*record = '\0';
66 	*fldtab[0] = dollar0;
67 	fldtab[0]->sval = record;
68 	fldtab[0]->nval = tostring("0");
69 	makefields(1, nfields);
70 }
71 
72 void makefields(int n1, int n2)		/* create $n1..$n2 inclusive */
73 {
74 	char temp[50];
75 	int i;
76 
77 	for (i = n1; i <= n2; i++) {
78 		fldtab[i] = (Cell *) malloc(sizeof (struct Cell));
79 		if (fldtab[i] == NULL)
80 			FATAL("out of space in makefields %d", i);
81 		*fldtab[i] = dollar1;
82 		sprintf(temp, "%d", i);
83 		fldtab[i]->nval = tostring(temp);
84 	}
85 }
86 
87 void initgetrec(void)
88 {
89 	int i;
90 	char *p;
91 
92 	for (i = 1; i < *ARGC; i++) {
93 		p = getargv(i); /* find 1st real filename */
94 		if (p == NULL || *p == '\0') {  /* deleted or zapped */
95 			argno++;
96 			continue;
97 		}
98 		if (!isclvar(p)) {
99 			setsval(lookup("FILENAME", symtab), p);
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 	uschar saveb0;
115 	int bufsize = *pbufsize, savebufsize = bufsize;
116 
117 	if (firsttime) {
118 		firsttime = 0;
119 		initgetrec();
120 	}
121 	   dprintf( ("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n",
122 		*RS, *FS, *ARGC, *FILENAME) );
123 	if (isrecord) {
124 		donefld = 0;
125 		donerec = 1;
126 	}
127 	saveb0 = buf[0];
128 	buf[0] = 0;
129 	while (argno < *ARGC || infile == stdin) {
130 		   dprintf( ("argno=%d, file=|%s|\n", argno, file) );
131 		if (infile == NULL) {	/* have to open a new file */
132 			file = getargv(argno);
133 			if (file == NULL || *file == '\0') {	/* deleted or zapped */
134 				argno++;
135 				continue;
136 			}
137 			if (isclvar(file)) {	/* a var=value arg */
138 				setclvar(file);
139 				argno++;
140 				continue;
141 			}
142 			*FILENAME = file;
143 			   dprintf( ("opening file %s\n", file) );
144 			if (*file == '-' && *(file+1) == '\0')
145 				infile = stdin;
146 			else if ((infile = fopen(file, "r")) == NULL)
147 				FATAL("can't open file %s", file);
148 			setfval(fnrloc, 0.0);
149 		}
150 		c = readrec(&buf, &bufsize, infile);
151 		if (c != 0 || buf[0] != '\0') {	/* normal record */
152 			if (isrecord) {
153 				if (freeable(fldtab[0]))
154 					xfree(fldtab[0]->sval);
155 				fldtab[0]->sval = buf;	/* buf == record */
156 				fldtab[0]->tval = REC | STR | DONTFREE;
157 				if (is_number(fldtab[0]->sval)) {
158 					fldtab[0]->fval = atof(fldtab[0]->sval);
159 					fldtab[0]->tval |= NUM;
160 				}
161 			}
162 			setfval(nrloc, nrloc->fval+1);
163 			setfval(fnrloc, fnrloc->fval+1);
164 			*pbuf = buf;
165 			*pbufsize = bufsize;
166 			return 1;
167 		}
168 		/* EOF arrived on this file; set up next */
169 		if (infile != stdin)
170 			fclose(infile);
171 		infile = NULL;
172 		argno++;
173 	}
174 	buf[0] = saveb0;
175 	*pbuf = buf;
176 	*pbufsize = savebufsize;
177 	return 0;	/* true end of file */
178 }
179 
180 void nextfile(void)
181 {
182 	if (infile != NULL && infile != stdin)
183 		fclose(infile);
184 	infile = NULL;
185 	argno++;
186 }
187 
188 int readrec(char **pbuf, int *pbufsize, FILE *inf)	/* read one record into buf */
189 {
190 	int sep, c;
191 	char *rr, *buf = *pbuf;
192 	int bufsize = *pbufsize;
193 	char *rs = getsval(rsloc);
194 
195 	if (strlen(getsval(fsloc)) >= sizeof (inputFS))
196 		FATAL("field separator %.10s... is too long", *FS);
197 	/*fflush(stdout); avoids some buffering problem but makes it 25% slower*/
198 	strcpy(inputFS, *FS);	/* for subsequent field splitting */
199 	if ((sep = *rs) == 0) {
200 		sep = '\n';
201 		while ((c=getc(inf)) == '\n' && c != EOF)	/* skip leading \n's */
202 			;
203 		if (c != EOF)
204 			ungetc(c, inf);
205 	}
206 	for (rr = buf; ; ) {
207 		for (; (c=getc(inf)) != sep && c != EOF; ) {
208 			if (rr-buf+1 > bufsize)
209 				if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 1"))
210 					FATAL("input record `%.30s...' too long", buf);
211 			*rr++ = c;
212 		}
213 		if (*rs == sep || c == EOF)
214 			break;
215 		if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */
216 			break;
217 		if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr, "readrec 2"))
218 			FATAL("input record `%.30s...' too long", buf);
219 		*rr++ = '\n';
220 		*rr++ = c;
221 	}
222 	if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
223 		FATAL("input record `%.30s...' too long", buf);
224 	*rr = 0;
225 	   dprintf( ("readrec saw <%s>, returns %d\n", buf, c == EOF && rr == buf ? 0 : 1) );
226 	*pbuf = buf;
227 	*pbufsize = bufsize;
228 	return c == EOF && rr == buf ? 0 : 1;
229 }
230 
231 char *getargv(int n)	/* get ARGV[n] */
232 {
233 	Cell *x;
234 	char *s, temp[50];
235 	extern Array *ARGVtab;
236 
237 	sprintf(temp, "%d", n);
238 	if (lookup(temp, ARGVtab) == NULL)
239 		return NULL;
240 	x = setsymtab(temp, "", 0.0, STR, ARGVtab);
241 	s = getsval(x);
242 	   dprintf( ("getargv(%d) returns |%s|\n", n, s) );
243 	return s;
244 }
245 
246 void setclvar(char *s)	/* set var=value from s */
247 {
248 	char *p;
249 	Cell *q;
250 
251 	for (p=s; *p != '='; p++)
252 		;
253 	*p++ = 0;
254 	p = qstring(p, '\0');
255 	q = setsymtab(s, p, 0.0, STR, symtab);
256 	setsval(q, p);
257 	if (is_number(q->sval)) {
258 		q->fval = atof(q->sval);
259 		q->tval |= NUM;
260 	}
261 	   dprintf( ("command line set %s to |%s|\n", s, p) );
262 }
263 
264 
265 void fldbld(void)	/* create fields from current record */
266 {
267 	/* this relies on having fields[] the same length as $0 */
268 	/* the fields are all stored in this one array with \0's */
269 	/* possibly with a final trailing \0 not associated with any field */
270 	char *r, *fr, sep;
271 	Cell *p;
272 	int i, j, n;
273 
274 	if (donefld)
275 		return;
276 	if (!isstr(fldtab[0]))
277 		getsval(fldtab[0]);
278 	r = fldtab[0]->sval;
279 	n = strlen(r);
280 	if (n > fieldssize) {
281 		xfree(fields);
282 		if ((fields = (char *) malloc(n+2)) == NULL) /* possibly 2 final \0s */
283 			FATAL("out of space for fields in fldbld %d", n);
284 		fieldssize = n;
285 	}
286 	fr = fields;
287 	i = 0;	/* number of fields accumulated here */
288 	if (strlen(getsval(fsloc)) >= sizeof (inputFS))
289 		FATAL("field separator %.10s... is too long", *FS);
290 	strcpy(inputFS, *FS);
291 	if (strlen(inputFS) > 1) {	/* it's a regular expression */
292 		i = refldbld(r, inputFS);
293 	} else if ((sep = *inputFS) == ' ') {	/* default whitespace */
294 		for (i = 0; ; ) {
295 			while (*r == ' ' || *r == '\t' || *r == '\n')
296 				r++;
297 			if (*r == 0)
298 				break;
299 			i++;
300 			if (i > nfields)
301 				growfldtab(i);
302 			if (freeable(fldtab[i]))
303 				xfree(fldtab[i]->sval);
304 			fldtab[i]->sval = fr;
305 			fldtab[i]->tval = FLD | STR | DONTFREE;
306 			do
307 				*fr++ = *r++;
308 			while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
309 			*fr++ = 0;
310 		}
311 		*fr = 0;
312 	} else if ((sep = *inputFS) == 0) {		/* new: FS="" => 1 char/field */
313 		for (i = 0; *r != 0; r++) {
314 			char buf[2];
315 			i++;
316 			if (i > nfields)
317 				growfldtab(i);
318 			if (freeable(fldtab[i]))
319 				xfree(fldtab[i]->sval);
320 			buf[0] = *r;
321 			buf[1] = 0;
322 			fldtab[i]->sval = tostring(buf);
323 			fldtab[i]->tval = FLD | STR;
324 		}
325 		*fr = 0;
326 	} else if (*r != 0) {	/* if 0, it's a null field */
327 		/* subtlecase : if length(FS) == 1 && length(RS > 0)
328 		 * \n is NOT a field separator (cf awk book 61,84).
329 		 * this variable is tested in the inner while loop.
330 		 */
331 		int rtest = '\n';  /* normal case */
332 		if (strlen(*RS) > 0)
333 			rtest = '\0';
334 		for (;;) {
335 			i++;
336 			if (i > nfields)
337 				growfldtab(i);
338 			if (freeable(fldtab[i]))
339 				xfree(fldtab[i]->sval);
340 			fldtab[i]->sval = fr;
341 			fldtab[i]->tval = FLD | STR | DONTFREE;
342 			while (*r != sep && *r != rtest && *r != '\0')	/* \n is always a separator */
343 				*fr++ = *r++;
344 			*fr++ = 0;
345 			if (*r++ == 0)
346 				break;
347 		}
348 		*fr = 0;
349 	}
350 	if (i > nfields)
351 		FATAL("record `%.30s...' has too many fields; can't happen", r);
352 	cleanfld(i+1, lastfld);	/* clean out junk from previous record */
353 	lastfld = i;
354 	donefld = 1;
355 	for (j = 1; j <= lastfld; j++) {
356 		p = fldtab[j];
357 		if(is_number(p->sval)) {
358 			p->fval = atof(p->sval);
359 			p->tval |= NUM;
360 		}
361 	}
362 	setfval(nfloc, (Awkfloat) lastfld);
363 	donerec = 1; /* restore */
364 	if (dbg) {
365 		for (j = 0; j <= lastfld; j++) {
366 			p = fldtab[j];
367 			printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
368 		}
369 	}
370 }
371 
372 void cleanfld(int n1, int n2)	/* clean out fields n1 .. n2 inclusive */
373 {				/* nvals remain intact */
374 	Cell *p;
375 	int i;
376 
377 	for (i = n1; i <= n2; i++) {
378 		p = fldtab[i];
379 		if (freeable(p))
380 			xfree(p->sval);
381 		p->sval = "";
382 		p->tval = FLD | STR | DONTFREE;
383 	}
384 }
385 
386 void newfld(int n)	/* add field n after end of existing lastfld */
387 {
388 	if (n > nfields)
389 		growfldtab(n);
390 	cleanfld(lastfld+1, n);
391 	lastfld = n;
392 	setfval(nfloc, (Awkfloat) n);
393 }
394 
395 void setlastfld(int n)	/* set lastfld cleaning fldtab cells if necessary */
396 {
397 	if (n < 0)
398 		FATAL("cannot set NF to a negative value");
399 	if (n > nfields)
400 		growfldtab(n);
401 
402 	if (lastfld < n)
403 	    cleanfld(lastfld+1, n);
404 	else
405 	    cleanfld(n+1, lastfld);
406 
407 	lastfld = n;
408 }
409 
410 Cell *fieldadr(int n)	/* get nth field */
411 {
412 	if (n < 0)
413 		FATAL("trying to access out of range field %d", n);
414 	if (n > nfields)	/* fields after NF are empty */
415 		growfldtab(n);	/* but does not increase NF */
416 	return(fldtab[n]);
417 }
418 
419 void growfldtab(int n)	/* make new fields up to at least $n */
420 {
421 	int nf = 2 * nfields;
422 	size_t s;
423 
424 	if (n > nf)
425 		nf = n;
426 	s = (nf+1) * (sizeof (struct Cell *));  /* freebsd: how much do we need? */
427 	if (s / sizeof(struct Cell *) - 1 == nf) /* didn't overflow */
428 		fldtab = (Cell **) realloc(fldtab, s);
429 	else					/* overflow sizeof int */
430 		xfree(fldtab);	/* make it null */
431 	if (fldtab == NULL)
432 		FATAL("out of space creating %d fields", nf);
433 	makefields(nfields+1, nf);
434 	nfields = nf;
435 }
436 
437 int refldbld(const char *rec, const char *fs)	/* build fields from reg expr in FS */
438 {
439 	/* this relies on having fields[] the same length as $0 */
440 	/* the fields are all stored in this one array with \0's */
441 	char *fr;
442 	int i, tempstat, n;
443 	fa *pfa;
444 
445 	n = strlen(rec);
446 	if (n > fieldssize) {
447 		xfree(fields);
448 		if ((fields = (char *) malloc(n+1)) == NULL)
449 			FATAL("out of space for fields in refldbld %d", n);
450 		fieldssize = n;
451 	}
452 	fr = fields;
453 	*fr = '\0';
454 	if (*rec == '\0')
455 		return 0;
456 	pfa = makedfa(fs, 1);
457 	   dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
458 	tempstat = pfa->initstat;
459 	for (i = 1; ; i++) {
460 		if (i > nfields)
461 			growfldtab(i);
462 		if (freeable(fldtab[i]))
463 			xfree(fldtab[i]->sval);
464 		fldtab[i]->tval = FLD | STR | DONTFREE;
465 		fldtab[i]->sval = fr;
466 		   dprintf( ("refldbld: i=%d\n", i) );
467 		if (nematch(pfa, rec)) {
468 			pfa->initstat = 2;	/* horrible coupling to b.c */
469 			   dprintf( ("match %s (%d chars)\n", patbeg, patlen) );
470 			strncpy(fr, rec, patbeg-rec);
471 			fr += patbeg - rec + 1;
472 			*(fr-1) = '\0';
473 			rec = patbeg + patlen;
474 		} else {
475 			   dprintf( ("no match %s\n", rec) );
476 			strcpy(fr, rec);
477 			pfa->initstat = tempstat;
478 			break;
479 		}
480 	}
481 	return i;
482 }
483 
484 void recbld(void)	/* create $0 from $1..$NF if necessary */
485 {
486 	int i;
487 	char *r, *p;
488 	char *sep = getsval(ofsloc);
489 
490 	if (donerec == 1)
491 		return;
492 	r = record;
493 	for (i = 1; i <= *NF; i++) {
494 		p = getsval(fldtab[i]);
495 		if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
496 			FATAL("created $0 `%.30s...' too long", record);
497 		while ((*r = *p++) != 0)
498 			r++;
499 		if (i < *NF) {
500 			if (!adjbuf(&record, &recsize, 2+strlen(sep)+r-record, recsize, &r, "recbld 2"))
501 				FATAL("created $0 `%.30s...' too long", record);
502 			for (p = sep; (*r = *p++) != 0; )
503 				r++;
504 		}
505 	}
506 	if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
507 		FATAL("built giant record `%.30s...'", record);
508 	*r = '\0';
509 	   dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
510 
511 	if (freeable(fldtab[0]))
512 		xfree(fldtab[0]->sval);
513 	fldtab[0]->tval = REC | STR | DONTFREE;
514 	fldtab[0]->sval = record;
515 
516 	   dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
517 	   dprintf( ("recbld = |%s|\n", record) );
518 	donerec = 1;
519 }
520 
521 int	errorflag	= 0;
522 
523 void yyerror(const char *s)
524 {
525 	SYNTAX("%s", s);
526 }
527 
528 void SYNTAX(const char *fmt, ...)
529 {
530 	extern char *cmdname, *curfname;
531 	static int been_here = 0;
532 	va_list varg;
533 
534 	if (been_here++ > 2)
535 		return;
536 	fprintf(stderr, "%s: ", cmdname);
537 	va_start(varg, fmt);
538 	vfprintf(stderr, fmt, varg);
539 	va_end(varg);
540 	fprintf(stderr, " at source line %d", lineno);
541 	if (curfname != NULL)
542 		fprintf(stderr, " in function %s", curfname);
543 	if (compile_time == 1 && cursource() != NULL)
544 		fprintf(stderr, " source file %s", cursource());
545 	fprintf(stderr, "\n");
546 	errorflag = 2;
547 	eprint();
548 }
549 
550 void fpecatch(int n)
551 {
552 	FATAL("floating point exception %d", n);
553 }
554 
555 extern int bracecnt, brackcnt, parencnt;
556 
557 void bracecheck(void)
558 {
559 	int c;
560 	static int beenhere = 0;
561 
562 	if (beenhere++)
563 		return;
564 	while ((c = input()) != EOF && c != '\0')
565 		bclass(c);
566 	bcheck2(bracecnt, '{', '}');
567 	bcheck2(brackcnt, '[', ']');
568 	bcheck2(parencnt, '(', ')');
569 }
570 
571 void bcheck2(int n, int c1, int c2)
572 {
573 	if (n == 1)
574 		fprintf(stderr, "\tmissing %c\n", c2);
575 	else if (n > 1)
576 		fprintf(stderr, "\t%d missing %c's\n", n, c2);
577 	else if (n == -1)
578 		fprintf(stderr, "\textra %c\n", c2);
579 	else if (n < -1)
580 		fprintf(stderr, "\t%d extra %c's\n", -n, c2);
581 }
582 
583 void FATAL(const char *fmt, ...)
584 {
585 	extern char *cmdname;
586 	va_list varg;
587 
588 	fflush(stdout);
589 	fprintf(stderr, "%s: ", cmdname);
590 	va_start(varg, fmt);
591 	vfprintf(stderr, fmt, varg);
592 	va_end(varg);
593 	error();
594 	if (dbg > 1)		/* core dump if serious debugging on */
595 		abort();
596 	exit(2);
597 }
598 
599 void WARNING(const char *fmt, ...)
600 {
601 	extern char *cmdname;
602 	va_list varg;
603 
604 	fflush(stdout);
605 	fprintf(stderr, "%s: ", cmdname);
606 	va_start(varg, fmt);
607 	vfprintf(stderr, fmt, varg);
608 	va_end(varg);
609 	error();
610 }
611 
612 void error()
613 {
614 	extern Node *curnode;
615 
616 	fprintf(stderr, "\n");
617 	if (compile_time != 2 && NR && *NR > 0) {
618 		fprintf(stderr, " input record number %d", (int) (*FNR));
619 		if (strcmp(*FILENAME, "-") != 0)
620 			fprintf(stderr, ", file %s", *FILENAME);
621 		fprintf(stderr, "\n");
622 	}
623 	if (compile_time != 2 && curnode)
624 		fprintf(stderr, " source line number %d", curnode->lineno);
625 	else if (compile_time != 2 && lineno)
626 		fprintf(stderr, " source line number %d", lineno);
627 	if (compile_time == 1 && cursource() != NULL)
628 		fprintf(stderr, " source file %s", cursource());
629 	fprintf(stderr, "\n");
630 	eprint();
631 }
632 
633 void eprint(void)	/* try to print context around error */
634 {
635 	char *p, *q;
636 	int c;
637 	static int been_here = 0;
638 	extern char ebuf[], *ep;
639 
640 	if (compile_time == 2 || compile_time == 0 || been_here++ > 0 || ebuf == ep)
641 		return;
642 	if (ebuf == ep)
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;
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