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