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