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