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