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