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