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