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