xref: /dragonfly/contrib/awk/lib.c (revision f9993810)
1 /****************************************************************
2 Copyright (C) Lucent Technologies 1997
3 All Rights Reserved
4 
5 Permission to use, copy, modify, and distribute this software and
6 its documentation for any purpose and without fee is hereby
7 granted, provided that the above copyright notice appear in all
8 copies and that both that the copyright notice and this
9 permission notice and warranty disclaimer appear in supporting
10 documentation, and that the name Lucent Technologies or any of
11 its entities not be used in advertising or publicity pertaining
12 to distribution of the software without specific, written prior
13 permission.
14 
15 LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
17 IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
18 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
20 IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
21 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
22 THIS SOFTWARE.
23 ****************************************************************/
24 
25 #define DEBUG
26 #include <stdio.h>
27 #include <string.h>
28 #include <strings.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;
126 	if ((len = strlen(getsval(fsloc))) < len_inputFS) {
127 		strcpy(inputFS, *FS);	/* for subsequent field splitting */
128 		return;
129 	}
130 
131 	len_inputFS = len + 1;
132 	inputFS = (char *) realloc(inputFS, len_inputFS);
133 	if (inputFS == NULL)
134 		FATAL("field separator %.10s... is too long", *FS);
135 	memcpy(inputFS, *FS, len_inputFS);
136 }
137 
138 static bool firsttime = true;
139 
140 int getrec(char **pbuf, int *pbufsize, bool isrecord)	/* get next input record */
141 {			/* note: cares whether buf == record */
142 	int c;
143 	char *buf = *pbuf;
144 	uschar saveb0;
145 	int bufsize = *pbufsize, savebufsize = bufsize;
146 
147 	if (firsttime) {
148 		firsttime = false;
149 		initgetrec();
150 	}
151 	DPRINTF("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n",
152 		*RS, *FS, *ARGC, *FILENAME);
153 	if (isrecord) {
154 		donefld = false;
155 		donerec = true;
156 		savefs();
157 	}
158 	saveb0 = buf[0];
159 	buf[0] = 0;
160 	while (argno < *ARGC || infile == stdin) {
161 		DPRINTF("argno=%d, file=|%s|\n", argno, file);
162 		if (infile == NULL) {	/* have to open a new file */
163 			file = getargv(argno);
164 			if (file == NULL || *file == '\0') {	/* deleted or zapped */
165 				argno++;
166 				continue;
167 			}
168 			if (isclvar(file)) {	/* a var=value arg */
169 				setclvar(file);
170 				argno++;
171 				continue;
172 			}
173 			*FILENAME = file;
174 			DPRINTF("opening file %s\n", file);
175 			if (*file == '-' && *(file+1) == '\0')
176 				infile = stdin;
177 			else if ((infile = fopen(file, "r")) == NULL)
178 				FATAL("can't open file %s", file);
179 			innew = true;
180 			setfval(fnrloc, 0.0);
181 		}
182 		c = readrec(&buf, &bufsize, infile, innew);
183 		if (innew)
184 			innew = false;
185 		if (c != 0 || buf[0] != '\0') {	/* normal record */
186 			if (isrecord) {
187 				double result;
188 
189 				if (freeable(fldtab[0]))
190 					xfree(fldtab[0]->sval);
191 				fldtab[0]->sval = buf;	/* buf == record */
192 				fldtab[0]->tval = REC | STR | DONTFREE;
193 				if (is_number(fldtab[0]->sval, & result)) {
194 					fldtab[0]->fval = result;
195 					fldtab[0]->tval |= NUM;
196 				}
197 			}
198 			setfval(nrloc, nrloc->fval+1);
199 			setfval(fnrloc, fnrloc->fval+1);
200 			*pbuf = buf;
201 			*pbufsize = bufsize;
202 			return 1;
203 		}
204 		/* EOF arrived on this file; set up next */
205 		if (infile != stdin)
206 			fclose(infile);
207 		infile = NULL;
208 		argno++;
209 	}
210 	buf[0] = saveb0;
211 	*pbuf = buf;
212 	*pbufsize = savebufsize;
213 	return 0;	/* true end of file */
214 }
215 
216 void nextfile(void)
217 {
218 	if (infile != NULL && infile != stdin)
219 		fclose(infile);
220 	infile = NULL;
221 	argno++;
222 }
223 
224 int readrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag)	/* read one record into buf */
225 {
226 	int sep, c, isrec;
227 	char *rr, *buf = *pbuf;
228 	int bufsize = *pbufsize;
229 	char *rs = getsval(rsloc);
230 
231 	if (*rs && rs[1]) {
232 		bool found;
233 
234 		fa *pfa = makedfa(rs, 1);
235 		if (newflag)
236 			found = fnematch(pfa, inf, &buf, &bufsize, recsize);
237 		else {
238 			int tempstat = pfa->initstat;
239 			pfa->initstat = 2;
240 			found = fnematch(pfa, inf, &buf, &bufsize, recsize);
241 			pfa->initstat = tempstat;
242 		}
243 		if (found)
244 			setptr(patbeg, '\0');
245 		isrec = (found == 0 && *buf == '\0') ? false : true;
246 	} else {
247 		if ((sep = *rs) == 0) {
248 			sep = '\n';
249 			while ((c=getc(inf)) == '\n' && c != EOF)	/* skip leading \n's */
250 				;
251 			if (c != EOF)
252 				ungetc(c, inf);
253 		}
254 		for (rr = buf; ; ) {
255 			for (; (c=getc(inf)) != sep && c != EOF; ) {
256 				if (rr-buf+1 > bufsize)
257 					if (!adjbuf(&buf, &bufsize, 1+rr-buf,
258 					    recsize, &rr, "readrec 1"))
259 						FATAL("input record `%.30s...' too long", buf);
260 				*rr++ = c;
261 			}
262 			if (*rs == sep || c == EOF)
263 				break;
264 			if ((c = getc(inf)) == '\n' || c == EOF)	/* 2 in a row */
265 				break;
266 			if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr,
267 			    "readrec 2"))
268 				FATAL("input record `%.30s...' too long", buf);
269 			*rr++ = '\n';
270 			*rr++ = c;
271 		}
272 		if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
273 			FATAL("input record `%.30s...' too long", buf);
274 		*rr = 0;
275 		isrec = (c == EOF && rr == buf) ? false : true;
276 	}
277 	*pbuf = buf;
278 	*pbufsize = bufsize;
279 	DPRINTF("readrec saw <%s>, returns %d\n", buf, isrec);
280 	return isrec;
281 }
282 
283 char *getargv(int n)	/* get ARGV[n] */
284 {
285 	Cell *x;
286 	char *s, temp[50];
287 	extern Array *ARGVtab;
288 
289 	snprintf(temp, sizeof(temp), "%d", n);
290 	if (lookup(temp, ARGVtab) == NULL)
291 		return NULL;
292 	x = setsymtab(temp, "", 0.0, STR, ARGVtab);
293 	s = getsval(x);
294 	DPRINTF("getargv(%d) returns |%s|\n", n, s);
295 	return s;
296 }
297 
298 void setclvar(char *s)	/* set var=value from s */
299 {
300 	char *e, *p;
301 	Cell *q;
302 	double result;
303 
304 	for (p=s; *p != '='; p++)
305 		;
306 	e = p;
307 	*p++ = 0;
308 	p = qstring(p, '\0');
309 	q = setsymtab(s, p, 0.0, STR, symtab);
310 	setsval(q, p);
311 	if (is_number(q->sval, & result)) {
312 		q->fval = result;
313 		q->tval |= NUM;
314 	}
315 	DPRINTF("command line set %s to |%s|\n", s, p);
316 	free(p);
317 	*e = '=';
318 }
319 
320 
321 void fldbld(void)	/* create fields from current record */
322 {
323 	/* this relies on having fields[] the same length as $0 */
324 	/* the fields are all stored in this one array with \0's */
325 	/* possibly with a final trailing \0 not associated with any field */
326 	char *r, *fr, sep;
327 	Cell *p;
328 	int i, j, n;
329 
330 	if (donefld)
331 		return;
332 	if (!isstr(fldtab[0]))
333 		getsval(fldtab[0]);
334 	r = fldtab[0]->sval;
335 	n = strlen(r);
336 	if (n > fieldssize) {
337 		xfree(fields);
338 		if ((fields = (char *) malloc(n+2)) == NULL) /* possibly 2 final \0s */
339 			FATAL("out of space for fields in fldbld %d", n);
340 		fieldssize = n;
341 	}
342 	fr = fields;
343 	i = 0;	/* number of fields accumulated here */
344 	if (inputFS == NULL)	/* make sure we have a copy of FS */
345 		savefs();
346 	if (strlen(inputFS) > 1) {	/* it's a regular expression */
347 		i = refldbld(r, inputFS);
348 	} else if ((sep = *inputFS) == ' ') {	/* default whitespace */
349 		for (i = 0; ; ) {
350 			while (*r == ' ' || *r == '\t' || *r == '\n')
351 				r++;
352 			if (*r == 0)
353 				break;
354 			i++;
355 			if (i > nfields)
356 				growfldtab(i);
357 			if (freeable(fldtab[i]))
358 				xfree(fldtab[i]->sval);
359 			fldtab[i]->sval = fr;
360 			fldtab[i]->tval = FLD | STR | DONTFREE;
361 			do
362 				*fr++ = *r++;
363 			while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
364 			*fr++ = 0;
365 		}
366 		*fr = 0;
367 	} else if ((sep = *inputFS) == 0) {		/* new: FS="" => 1 char/field */
368 		for (i = 0; *r != '\0'; r += n) {
369 			char buf[MB_LEN_MAX + 1];
370 
371 			i++;
372 			if (i > nfields)
373 				growfldtab(i);
374 			if (freeable(fldtab[i]))
375 				xfree(fldtab[i]->sval);
376 			n = mblen(r, MB_LEN_MAX);
377 			if (n < 0)
378 				n = 1;
379 			memcpy(buf, r, n);
380 			buf[n] = '\0';
381 			fldtab[i]->sval = tostring(buf);
382 			fldtab[i]->tval = FLD | STR;
383 		}
384 		*fr = 0;
385 	} else if (*r != 0) {	/* if 0, it's a null field */
386 		/* subtlecase : if length(FS) == 1 && length(RS > 0)
387 		 * \n is NOT a field separator (cf awk book 61,84).
388 		 * this variable is tested in the inner while loop.
389 		 */
390 		int rtest = '\n';  /* normal case */
391 		if (strlen(*RS) > 0)
392 			rtest = '\0';
393 		for (;;) {
394 			i++;
395 			if (i > nfields)
396 				growfldtab(i);
397 			if (freeable(fldtab[i]))
398 				xfree(fldtab[i]->sval);
399 			fldtab[i]->sval = fr;
400 			fldtab[i]->tval = FLD | STR | DONTFREE;
401 			while (*r != sep && *r != rtest && *r != '\0')	/* \n is always a separator */
402 				*fr++ = *r++;
403 			*fr++ = 0;
404 			if (*r++ == 0)
405 				break;
406 		}
407 		*fr = 0;
408 	}
409 	if (i > nfields)
410 		FATAL("record `%.30s...' has too many fields; can't happen", r);
411 	cleanfld(i+1, lastfld);	/* clean out junk from previous record */
412 	lastfld = i;
413 	donefld = true;
414 	for (j = 1; j <= lastfld; j++) {
415 		double result;
416 
417 		p = fldtab[j];
418 		if(is_number(p->sval, & result)) {
419 			p->fval = result;
420 			p->tval |= NUM;
421 		}
422 	}
423 	setfval(nfloc, (Awkfloat) lastfld);
424 	donerec = true; /* restore */
425 	if (dbg) {
426 		for (j = 0; j <= lastfld; j++) {
427 			p = fldtab[j];
428 			printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
429 		}
430 	}
431 }
432 
433 void cleanfld(int n1, int n2)	/* clean out fields n1 .. n2 inclusive */
434 {				/* nvals remain intact */
435 	Cell *p;
436 	int i;
437 
438 	for (i = n1; i <= n2; i++) {
439 		p = fldtab[i];
440 		if (freeable(p))
441 			xfree(p->sval);
442 		p->sval = EMPTY,
443 		p->tval = FLD | STR | DONTFREE;
444 	}
445 }
446 
447 void newfld(int n)	/* add field n after end of existing lastfld */
448 {
449 	if (n > nfields)
450 		growfldtab(n);
451 	cleanfld(lastfld+1, n);
452 	lastfld = n;
453 	setfval(nfloc, (Awkfloat) n);
454 }
455 
456 void setlastfld(int n)	/* set lastfld cleaning fldtab cells if necessary */
457 {
458 	if (n < 0)
459 		FATAL("cannot set NF to a negative value");
460 	if (n > nfields)
461 		growfldtab(n);
462 
463 	if (lastfld < n)
464 	    cleanfld(lastfld+1, n);
465 	else
466 	    cleanfld(n+1, lastfld);
467 
468 	lastfld = n;
469 }
470 
471 Cell *fieldadr(int n)	/* get nth field */
472 {
473 	if (n < 0)
474 		FATAL("trying to access out of range field %d", n);
475 	if (n > nfields)	/* fields after NF are empty */
476 		growfldtab(n);	/* but does not increase NF */
477 	return(fldtab[n]);
478 }
479 
480 void growfldtab(int n)	/* make new fields up to at least $n */
481 {
482 	int nf = 2 * nfields;
483 	size_t s;
484 
485 	if (n > nf)
486 		nf = n;
487 	s = (nf+1) * (sizeof (struct Cell *));  /* freebsd: how much do we need? */
488 	if (s / sizeof(struct Cell *) - 1 == (size_t)nf) /* didn't overflow */
489 		fldtab = (Cell **) realloc(fldtab, s);
490 	else					/* overflow sizeof int */
491 		xfree(fldtab);	/* make it null */
492 	if (fldtab == NULL)
493 		FATAL("out of space creating %d fields", nf);
494 	makefields(nfields+1, nf);
495 	nfields = nf;
496 }
497 
498 int refldbld(const char *rec, const char *fs)	/* build fields from reg expr in FS */
499 {
500 	/* this relies on having fields[] the same length as $0 */
501 	/* the fields are all stored in this one array with \0's */
502 	char *fr;
503 	int i, tempstat, n;
504 	fa *pfa;
505 
506 	n = strlen(rec);
507 	if (n > fieldssize) {
508 		xfree(fields);
509 		if ((fields = (char *) malloc(n+1)) == NULL)
510 			FATAL("out of space for fields in refldbld %d", n);
511 		fieldssize = n;
512 	}
513 	fr = fields;
514 	*fr = '\0';
515 	if (*rec == '\0')
516 		return 0;
517 	pfa = makedfa(fs, 1);
518 	DPRINTF("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs);
519 	tempstat = pfa->initstat;
520 	for (i = 1; ; i++) {
521 		if (i > nfields)
522 			growfldtab(i);
523 		if (freeable(fldtab[i]))
524 			xfree(fldtab[i]->sval);
525 		fldtab[i]->tval = FLD | STR | DONTFREE;
526 		fldtab[i]->sval = fr;
527 		DPRINTF("refldbld: i=%d\n", i);
528 		if (nematch(pfa, rec)) {
529 			pfa->initstat = 2;	/* horrible coupling to b.c */
530 			DPRINTF("match %s (%d chars)\n", patbeg, patlen);
531 			strncpy(fr, rec, patbeg-rec);
532 			fr += patbeg - rec + 1;
533 			*(fr-1) = '\0';
534 			rec = patbeg + patlen;
535 		} else {
536 			DPRINTF("no match %s\n", rec);
537 			strcpy(fr, rec);
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 	if (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(*s))
799 		s++;
800 
801 	// no hex floating point, sorry
802 	if (s[0] == '0' && tolower(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(s[4]) || s[4] == '\0'))
811 			goto convert;
812 		else if (! isdigit(s[1]) && s[1] != '.')
813 			return false;
814 	}
815 	else if (! isdigit(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(*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