xref: /openbsd-src/usr.bin/awk/lib.c (revision 097a140d792de8b2bbe59ad827d39eabf9b4280a)
1 /*	$OpenBSD: lib.c,v 1.45 2021/04/19 14:08:55 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 <math.h>
35 #include "awk.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 = (char *) malloc(n)) == NULL
66 	  || (fields = (char *) malloc(n+1)) == NULL
67 	  || (fldtab = (Cell **) calloc(nfields+2, sizeof(*fldtab))) == NULL
68 	  || (fldtab[0] = (Cell *) 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] = (Cell *) 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 = (char *) 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 			innew = true;
178 			setfval(fnrloc, 0.0);
179 		}
180 		c = readrec(&buf, &bufsize, infile, innew);
181 		if (innew)
182 			innew = false;
183 		if (c != 0 || buf[0] != '\0') {	/* normal record */
184 			if (isrecord) {
185 				double result;
186 
187 				if (freeable(fldtab[0]))
188 					xfree(fldtab[0]->sval);
189 				fldtab[0]->sval = buf;	/* buf == record */
190 				fldtab[0]->tval = REC | STR | DONTFREE;
191 				if (is_number(fldtab[0]->sval, & result)) {
192 					fldtab[0]->fval = result;
193 					fldtab[0]->tval |= NUM;
194 				}
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 	double result;
300 
301 	for (p=s; *p != '='; p++)
302 		;
303 	*p++ = 0;
304 	p = qstring(p, '\0');
305 	q = setsymtab(s, p, 0.0, STR, symtab);
306 	setsval(q, p);
307 	if (is_number(q->sval, & result)) {
308 		q->fval = result;
309 		q->tval |= NUM;
310 	}
311 	DPRINTF("command line set %s to |%s|\n", s, p);
312 }
313 
314 
315 void fldbld(void)	/* create fields from current record */
316 {
317 	/* this relies on having fields[] the same length as $0 */
318 	/* the fields are all stored in this one array with \0's */
319 	/* possibly with a final trailing \0 not associated with any field */
320 	char *r, *fr, sep;
321 	Cell *p;
322 	int i, j, n;
323 
324 	if (donefld)
325 		return;
326 	if (!isstr(fldtab[0]))
327 		getsval(fldtab[0]);
328 	r = fldtab[0]->sval;
329 	n = strlen(r);
330 	if (n > fieldssize) {
331 		xfree(fields);
332 		if ((fields = (char *) malloc(n+2)) == NULL) /* possibly 2 final \0s */
333 			FATAL("out of space for fields in fldbld %d", n);
334 		fieldssize = n;
335 	}
336 	fr = fields;
337 	i = 0;	/* number of fields accumulated here */
338 	if (inputFS == NULL)	/* make sure we have a copy of FS */
339 		savefs();
340 	if (strlen(inputFS) > 1) {	/* it's a regular expression */
341 		i = refldbld(r, inputFS);
342 	} else if ((sep = *inputFS) == ' ') {	/* default whitespace */
343 		for (i = 0; ; ) {
344 			while (*r == ' ' || *r == '\t' || *r == '\n')
345 				r++;
346 			if (*r == 0)
347 				break;
348 			i++;
349 			if (i > nfields)
350 				growfldtab(i);
351 			if (freeable(fldtab[i]))
352 				xfree(fldtab[i]->sval);
353 			fldtab[i]->sval = fr;
354 			fldtab[i]->tval = FLD | STR | DONTFREE;
355 			do
356 				*fr++ = *r++;
357 			while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
358 			*fr++ = 0;
359 		}
360 		*fr = 0;
361 	} else if ((sep = *inputFS) == 0) {		/* new: FS="" => 1 char/field */
362 		for (i = 0; *r != '\0'; r += n) {
363 			char buf[MB_LEN_MAX + 1];
364 
365 			i++;
366 			if (i > nfields)
367 				growfldtab(i);
368 			if (freeable(fldtab[i]))
369 				xfree(fldtab[i]->sval);
370 			n = mblen(r, MB_LEN_MAX);
371 			if (n < 0)
372 				n = 1;
373 			memcpy(buf, r, n);
374 			buf[n] = '\0';
375 			fldtab[i]->sval = tostring(buf);
376 			fldtab[i]->tval = FLD | STR;
377 		}
378 		*fr = 0;
379 	} else if (*r != 0) {	/* if 0, it's a null field */
380 		/* subtlecase : if length(FS) == 1 && length(RS > 0)
381 		 * \n is NOT a field separator (cf awk book 61,84).
382 		 * this variable is tested in the inner while loop.
383 		 */
384 		int rtest = '\n';  /* normal case */
385 		if (strlen(*RS) > 0)
386 			rtest = '\0';
387 		for (;;) {
388 			i++;
389 			if (i > nfields)
390 				growfldtab(i);
391 			if (freeable(fldtab[i]))
392 				xfree(fldtab[i]->sval);
393 			fldtab[i]->sval = fr;
394 			fldtab[i]->tval = FLD | STR | DONTFREE;
395 			while (*r != sep && *r != rtest && *r != '\0')	/* \n is always a separator */
396 				*fr++ = *r++;
397 			*fr++ = 0;
398 			if (*r++ == 0)
399 				break;
400 		}
401 		*fr = 0;
402 	}
403 	if (i > nfields)
404 		FATAL("record `%.30s...' has too many fields; can't happen", r);
405 	cleanfld(i+1, lastfld);	/* clean out junk from previous record */
406 	lastfld = i;
407 	donefld = true;
408 	for (j = 1; j <= lastfld; j++) {
409 		double result;
410 
411 		p = fldtab[j];
412 		if(is_number(p->sval, & result)) {
413 			p->fval = result;
414 			p->tval |= NUM;
415 		}
416 	}
417 	setfval(nfloc, (Awkfloat) lastfld);
418 	donerec = true; /* restore */
419 	if (dbg) {
420 		for (j = 0; j <= lastfld; j++) {
421 			p = fldtab[j];
422 			printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
423 		}
424 	}
425 }
426 
427 void cleanfld(int n1, int n2)	/* clean out fields n1 .. n2 inclusive */
428 {				/* nvals remain intact */
429 	Cell *p;
430 	int i;
431 
432 	for (i = n1; i <= n2; i++) {
433 		p = fldtab[i];
434 		if (freeable(p))
435 			xfree(p->sval);
436 		p->sval = EMPTY,
437 		p->tval = FLD | STR | DONTFREE;
438 	}
439 }
440 
441 void newfld(int n)	/* add field n after end of existing lastfld */
442 {
443 	if (n > nfields)
444 		growfldtab(n);
445 	cleanfld(lastfld+1, n);
446 	lastfld = n;
447 	setfval(nfloc, (Awkfloat) n);
448 }
449 
450 void setlastfld(int n)	/* set lastfld cleaning fldtab cells if necessary */
451 {
452 	if (n < 0)
453 		FATAL("cannot set NF to a negative value");
454 	if (n > nfields)
455 		growfldtab(n);
456 
457 	if (lastfld < n)
458 	    cleanfld(lastfld+1, n);
459 	else
460 	    cleanfld(n+1, lastfld);
461 
462 	lastfld = n;
463 }
464 
465 Cell *fieldadr(int n)	/* get nth field */
466 {
467 	if (n < 0)
468 		FATAL("trying to access out of range field %d", n);
469 	if (n > nfields)	/* fields after NF are empty */
470 		growfldtab(n);	/* but does not increase NF */
471 	return(fldtab[n]);
472 }
473 
474 void growfldtab(int n)	/* make new fields up to at least $n */
475 {
476 	int nf = 2 * nfields;
477 	size_t s;
478 
479 	if (n > nf)
480 		nf = n;
481 	s = (nf+1) * (sizeof (struct Cell *));  /* freebsd: how much do we need? */
482 	if (s / sizeof(struct Cell *) - 1 == (size_t)nf) /* didn't overflow */
483 		fldtab = (Cell **) realloc(fldtab, s);
484 	else					/* overflow sizeof int */
485 		xfree(fldtab);	/* make it null */
486 	if (fldtab == NULL)
487 		FATAL("out of space creating %d fields", nf);
488 	makefields(nfields+1, nf);
489 	nfields = nf;
490 }
491 
492 int refldbld(const char *rec, const char *fs)	/* build fields from reg expr in FS */
493 {
494 	/* this relies on having fields[] the same length as $0 */
495 	/* the fields are all stored in this one array with \0's */
496 	char *fr;
497 	int i, tempstat, n;
498 	fa *pfa;
499 
500 	n = strlen(rec);
501 	if (n > fieldssize) {
502 		xfree(fields);
503 		if ((fields = (char *) malloc(n+1)) == NULL)
504 			FATAL("out of space for fields in refldbld %d", n);
505 		fieldssize = n;
506 	}
507 	fr = fields;
508 	*fr = '\0';
509 	if (*rec == '\0')
510 		return 0;
511 	pfa = makedfa(fs, 1);
512 	DPRINTF("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs);
513 	tempstat = pfa->initstat;
514 	for (i = 1; ; i++) {
515 		const size_t fss_rem = fields + fieldssize + 1 - fr;
516 		if (i > nfields)
517 			growfldtab(i);
518 		if (freeable(fldtab[i]))
519 			xfree(fldtab[i]->sval);
520 		fldtab[i]->tval = FLD | STR | DONTFREE;
521 		fldtab[i]->sval = fr;
522 		DPRINTF("refldbld: i=%d\n", i);
523 		if (nematch(pfa, rec)) {
524 			const size_t reclen = patbeg - rec;
525 			pfa->initstat = 2;	/* horrible coupling to b.c */
526 			DPRINTF("match %s (%d chars)\n", patbeg, patlen);
527 			if (reclen >= fss_rem)
528 				FATAL("out of space for fields in refldbld");
529 			memcpy(fr, rec, reclen);
530 			fr += reclen;
531 			*fr++ = '\0';
532 			rec = patbeg + patlen;
533 		} else {
534 			DPRINTF("no match %s\n", rec);
535 			if (strlcpy(fr, rec, fss_rem) >= fss_rem)
536 				FATAL("out of space for fields in refldbld");
537 			pfa->initstat = tempstat;
538 			break;
539 		}
540 	}
541 	return i;
542 }
543 
544 void recbld(void)	/* create $0 from $1..$NF if necessary */
545 {
546 	int i;
547 	char *r, *p;
548 	char *sep = getsval(ofsloc);
549 
550 	if (donerec)
551 		return;
552 	r = record;
553 	for (i = 1; i <= *NF; i++) {
554 		p = getsval(fldtab[i]);
555 		if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
556 			FATAL("created $0 `%.30s...' too long", record);
557 		while ((*r = *p++) != 0)
558 			r++;
559 		if (i < *NF) {
560 			if (!adjbuf(&record, &recsize, 2+strlen(sep)+r-record, recsize, &r, "recbld 2"))
561 				FATAL("created $0 `%.30s...' too long", record);
562 			for (p = sep; (*r = *p++) != 0; )
563 				r++;
564 		}
565 	}
566 	if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
567 		FATAL("built giant record `%.30s...'", record);
568 	*r = '\0';
569 	DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]);
570 
571 	if (freeable(fldtab[0]))
572 		xfree(fldtab[0]->sval);
573 	fldtab[0]->tval = REC | STR | DONTFREE;
574 	fldtab[0]->sval = record;
575 
576 	DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]);
577 	DPRINTF("recbld = |%s|\n", record);
578 	donerec = true;
579 }
580 
581 int	errorflag	= 0;
582 
583 void yyerror(const char *s)
584 {
585 	SYNTAX("%s", s);
586 }
587 
588 void SYNTAX(const char *fmt, ...)
589 {
590 	extern char *cmdname, *curfname;
591 	static int been_here = 0;
592 	va_list varg;
593 
594 	if (been_here++ > 2)
595 		return;
596 	fprintf(stderr, "%s: ", cmdname);
597 	va_start(varg, fmt);
598 	vfprintf(stderr, fmt, varg);
599 	va_end(varg);
600 	fprintf(stderr, " at source line %d", lineno);
601 	if (curfname != NULL)
602 		fprintf(stderr, " in function %s", curfname);
603 	if (compile_time == COMPILING && cursource() != NULL)
604 		fprintf(stderr, " source file %s", cursource());
605 	fprintf(stderr, "\n");
606 	errorflag = 2;
607 	eprint();
608 }
609 
610 extern int bracecnt, brackcnt, parencnt;
611 
612 void bracecheck(void)
613 {
614 	int c;
615 	static int beenhere = 0;
616 
617 	if (beenhere++)
618 		return;
619 	while ((c = input()) != EOF && c != '\0')
620 		bclass(c);
621 	bcheck2(bracecnt, '{', '}');
622 	bcheck2(brackcnt, '[', ']');
623 	bcheck2(parencnt, '(', ')');
624 }
625 
626 void bcheck2(int n, int c1, int c2)
627 {
628 	if (n == 1)
629 		fprintf(stderr, "\tmissing %c\n", c2);
630 	else if (n > 1)
631 		fprintf(stderr, "\t%d missing %c's\n", n, c2);
632 	else if (n == -1)
633 		fprintf(stderr, "\textra %c\n", c2);
634 	else if (n < -1)
635 		fprintf(stderr, "\t%d extra %c's\n", -n, c2);
636 }
637 
638 void FATAL(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 	if (dbg > 1)		/* core dump if serious debugging on */
650 		abort();
651 	exit(2);
652 }
653 
654 void WARNING(const char *fmt, ...)
655 {
656 	extern char *cmdname;
657 	va_list varg;
658 
659 	fflush(stdout);
660 	fprintf(stderr, "%s: ", cmdname);
661 	va_start(varg, fmt);
662 	vfprintf(stderr, fmt, varg);
663 	va_end(varg);
664 	error();
665 }
666 
667 void error()
668 {
669 	extern Node *curnode;
670 
671 	fprintf(stderr, "\n");
672 	if (compile_time != ERROR_PRINTING) {
673 		if (NR && *NR > 0) {
674 			fprintf(stderr, " input record number %d", (int) (*FNR));
675 			if (strcmp(*FILENAME, "-") != 0)
676 				fprintf(stderr, ", file %s", *FILENAME);
677 			fprintf(stderr, "\n");
678 		}
679 		if (curnode)
680 			fprintf(stderr, " source line number %d", curnode->lineno);
681 		else if (lineno)
682 			fprintf(stderr, " source line number %d", lineno);
683 		if (compile_time == COMPILING && cursource() != NULL)
684 			fprintf(stderr, " source file %s", cursource());
685 		fprintf(stderr, "\n");
686 		eprint();
687 	}
688 }
689 
690 void eprint(void)	/* try to print context around error */
691 {
692 	char *p, *q;
693 	int c;
694 	static int been_here = 0;
695 	extern char ebuf[], *ep;
696 
697 	if (compile_time != COMPILING || been_here++ > 0 || ebuf == ep)
698 		return;
699 	p = ep - 1;
700 	if (p > ebuf && *p == '\n')
701 		p--;
702 	for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
703 		;
704 	while (*p == '\n')
705 		p++;
706 	fprintf(stderr, " context is\n\t");
707 	for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
708 		;
709 	for ( ; p < q; p++)
710 		if (*p)
711 			putc(*p, stderr);
712 	fprintf(stderr, " >>> ");
713 	for ( ; p < ep; p++)
714 		if (*p)
715 			putc(*p, stderr);
716 	fprintf(stderr, " <<< ");
717 	if (*ep)
718 		while ((c = input()) != '\n' && c != '\0' && c != EOF) {
719 			putc(c, stderr);
720 			bclass(c);
721 		}
722 	putc('\n', stderr);
723 	ep = ebuf;
724 }
725 
726 void bclass(int c)
727 {
728 	switch (c) {
729 	case '{': bracecnt++; break;
730 	case '}': bracecnt--; break;
731 	case '[': brackcnt++; break;
732 	case ']': brackcnt--; break;
733 	case '(': parencnt++; break;
734 	case ')': parencnt--; break;
735 	}
736 }
737 
738 double errcheck(double x, const char *s)
739 {
740 
741 	if (errno == EDOM) {
742 		errno = 0;
743 		WARNING("%s argument out of domain", s);
744 		x = 1;
745 	} else if (errno == ERANGE) {
746 		errno = 0;
747 		WARNING("%s result out of range", s);
748 		x = 1;
749 	}
750 	return x;
751 }
752 
753 int isclvar(const char *s)	/* is s of form var=something ? */
754 {
755 	const char *os = s;
756 
757 	if (!isalpha((uschar) *s) && *s != '_')
758 		return 0;
759 	for ( ; *s; s++)
760 		if (!(isalnum((uschar) *s) || *s == '_'))
761 			break;
762 	return *s == '=' && s > os;
763 }
764 
765 /* strtod is supposed to be a proper test of what's a valid number */
766 /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
767 /* wrong: violates 4.10.1.4 of ansi C standard */
768 
769 /* well, not quite. As of C99, hex floating point is allowed. so this is
770  * a bit of a mess. We work around the mess by checking for a hexadecimal
771  * value and disallowing it. Similarly, we now follow gawk and allow only
772  * +nan, -nan, +inf, and -inf for NaN and infinity values.
773  */
774 
775 /*
776  * This routine now has a more complicated interface, the main point
777  * being to avoid the double conversion of a string to double, and
778  * also to convey out, if requested, the information that the numeric
779  * value was a leading string or is all of the string. The latter bit
780  * is used in getfval().
781  */
782 
783 bool is_valid_number(const char *s, bool trailing_stuff_ok,
784 			bool *no_trailing, double *result)
785 {
786 	double r;
787 	char *ep;
788 	bool retval = false;
789 	bool is_nan = false;
790 	bool is_inf = false;
791 
792 	if (no_trailing)
793 		*no_trailing = false;
794 
795 	while (isspace((uschar)*s))
796 		s++;
797 
798 	// no hex floating point, sorry
799 	if (s[0] == '0' && tolower((uschar)s[1]) == 'x')
800 		return false;
801 
802 	// allow +nan, -nan, +inf, -inf, any other letter, no
803 	if (s[0] == '+' || s[0] == '-') {
804 		is_nan = (strncasecmp(s+1, "nan", 3) == 0);
805 		is_inf = (strncasecmp(s+1, "inf", 3) == 0);
806 		if ((is_nan || is_inf)
807 		    && (isspace((uschar)s[4]) || s[4] == '\0'))
808 			goto convert;
809 		else if (! isdigit((uschar)s[1]) && s[1] != '.')
810 			return false;
811 	}
812 	else if (! isdigit((uschar)s[0]) && s[0] != '.')
813 		return false;
814 
815 convert:
816 	errno = 0;
817 	r = strtod(s, &ep);
818 	if (ep == s || errno == ERANGE)
819 		return false;
820 
821 	if (isnan(r) && s[0] == '-' && signbit(r) == 0)
822 		r = -r;
823 
824 	if (result != NULL)
825 		*result = r;
826 
827 	retval = (isspace((uschar)*ep) || *ep == '\0' || trailing_stuff_ok);
828 
829 	if (no_trailing != NULL)
830 		*no_trailing = (*ep == '\0');
831 
832 	return retval;
833 }
834