xref: /openbsd-src/usr.bin/awk/lib.c (revision 1a8dbaac879b9f3335ad7fb25429ce63ac1d6bac)
1 /*	$OpenBSD: lib.c,v 1.41 2020/07/30 17:46:54 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 "awk.h"
35 
36 char	EMPTY[] = { '\0' };
37 FILE	*infile	= NULL;
38 bool	innew;		/* true = infile has not been read by readrec */
39 char	*file	= EMPTY;
40 char	*record;
41 int	recsize	= RECSIZE;
42 char	*fields;
43 int	fieldssize = RECSIZE;
44 
45 Cell	**fldtab;	/* pointers to Cells */
46 static size_t	len_inputFS = 0;
47 static char	*inputFS = NULL; /* FS at time of input, for field splitting */
48 
49 #define	MAXFLD	2
50 int	nfields	= MAXFLD;	/* last allocated slot for $i */
51 
52 bool	donefld;	/* true = implies rec broken into fields */
53 bool	donerec;	/* true = record is valid (no flds have changed) */
54 
55 int	lastfld	= 0;	/* last used field */
56 int	argno	= 1;	/* current input argument number */
57 extern	Awkfloat *ARGC;
58 
59 static Cell dollar0 = { OCELL, CFLD, NULL, EMPTY, 0.0, REC|STR|DONTFREE, NULL, NULL };
60 static Cell dollar1 = { OCELL, CFLD, NULL, EMPTY, 0.0, FLD|STR|DONTFREE, NULL, NULL };
61 
62 void recinit(unsigned int n)
63 {
64 	if ( (record = malloc(n)) == NULL
65 	  || (fields = malloc(n+1)) == NULL
66 	  || (fldtab = calloc(nfields+2, sizeof(*fldtab))) == NULL
67 	  || (fldtab[0] = malloc(sizeof(**fldtab))) == NULL)
68 		FATAL("out of space for $0 and fields");
69 	*record = '\0';
70 	*fldtab[0] = dollar0;
71 	fldtab[0]->sval = record;
72 	fldtab[0]->nval = tostring("0");
73 	makefields(1, nfields);
74 }
75 
76 void makefields(int n1, int n2)		/* create $n1..$n2 inclusive */
77 {
78 	char temp[50];
79 	int i;
80 
81 	for (i = n1; i <= n2; i++) {
82 		fldtab[i] = malloc(sizeof(**fldtab));
83 		if (fldtab[i] == NULL)
84 			FATAL("out of space in makefields %d", i);
85 		*fldtab[i] = dollar1;
86 		snprintf(temp, sizeof(temp), "%d", i);
87 		fldtab[i]->nval = tostring(temp);
88 	}
89 }
90 
91 void initgetrec(void)
92 {
93 	int i;
94 	char *p;
95 
96 	for (i = 1; i < *ARGC; i++) {
97 		p = getargv(i); /* find 1st real filename */
98 		if (p == NULL || *p == '\0') {  /* deleted or zapped */
99 			argno++;
100 			continue;
101 		}
102 		if (!isclvar(p)) {
103 			setsval(lookup("FILENAME", symtab), p);
104 			return;
105 		}
106 		setclvar(p);	/* a commandline assignment before filename */
107 		argno++;
108 	}
109 	infile = stdin;		/* no filenames, so use stdin */
110 	innew = true;
111 }
112 
113 /*
114  * POSIX specifies that fields are supposed to be evaluated as if they were
115  * split using the value of FS at the time that the record's value ($0) was
116  * read.
117  *
118  * Since field-splitting is done lazily, we save the current value of FS
119  * whenever a new record is read in (implicitly or via getline), or when
120  * a new value is assigned to $0.
121  */
122 void savefs(void)
123 {
124 	size_t len = strlen(getsval(fsloc));
125 	if (len >= len_inputFS) {
126 		len_inputFS = len + 1;
127 		inputFS = realloc(inputFS, len_inputFS);
128 		if (inputFS == NULL)
129 			FATAL("field separator %.10s... is too long", *FS);
130 	}
131 	if (strlcpy(inputFS, *FS, len_inputFS) >= len_inputFS)
132 		FATAL("field separator %.10s... is too long", *FS);
133 }
134 
135 static bool firsttime = true;
136 
137 int getrec(char **pbuf, int *pbufsize, bool isrecord)	/* get next input record */
138 {			/* note: cares whether buf == record */
139 	int c;
140 	char *buf = *pbuf;
141 	uschar saveb0;
142 	int bufsize = *pbufsize, savebufsize = bufsize;
143 
144 	if (firsttime) {
145 		firsttime = false;
146 		initgetrec();
147 	}
148 	DPRINTF("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n",
149 		*RS, *FS, *ARGC, *FILENAME);
150 	if (isrecord) {
151 		donefld = false;
152 		donerec = true;
153 		savefs();
154 	}
155 	saveb0 = buf[0];
156 	buf[0] = 0;
157 	while (argno < *ARGC || infile == stdin) {
158 		DPRINTF("argno=%d, file=|%s|\n", argno, file);
159 		if (infile == NULL) {	/* have to open a new file */
160 			file = getargv(argno);
161 			if (file == NULL || *file == '\0') {	/* deleted or zapped */
162 				argno++;
163 				continue;
164 			}
165 			if (isclvar(file)) {	/* a var=value arg */
166 				setclvar(file);
167 				argno++;
168 				continue;
169 			}
170 			*FILENAME = file;
171 			DPRINTF("opening file %s\n", file);
172 			if (*file == '-' && *(file+1) == '\0')
173 				infile = stdin;
174 			else if ((infile = fopen(file, "r")) == NULL)
175 				FATAL("can't open file %s", file);
176 			setfval(fnrloc, 0.0);
177 		}
178 		c = readrec(&buf, &bufsize, infile, innew);
179 		if (innew)
180 			innew = false;
181 		if (c != 0 || buf[0] != '\0') {	/* normal record */
182 			if (isrecord) {
183 				if (freeable(fldtab[0]))
184 					xfree(fldtab[0]->sval);
185 				fldtab[0]->sval = buf;	/* buf == record */
186 				fldtab[0]->tval = REC | STR | DONTFREE;
187 				if (is_number(fldtab[0]->sval)) {
188 					fldtab[0]->fval = atof(fldtab[0]->sval);
189 					fldtab[0]->tval |= NUM;
190 				}
191 			}
192 			setfval(nrloc, nrloc->fval+1);
193 			setfval(fnrloc, fnrloc->fval+1);
194 			*pbuf = buf;
195 			*pbufsize = bufsize;
196 			return 1;
197 		}
198 		/* EOF arrived on this file; set up next */
199 		if (infile != stdin)
200 			fclose(infile);
201 		infile = NULL;
202 		argno++;
203 	}
204 	buf[0] = saveb0;
205 	*pbuf = buf;
206 	*pbufsize = savebufsize;
207 	return 0;	/* true end of file */
208 }
209 
210 void nextfile(void)
211 {
212 	if (infile != NULL && infile != stdin)
213 		fclose(infile);
214 	infile = NULL;
215 	argno++;
216 }
217 
218 int readrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag)	/* read one record into buf */
219 {
220 	int sep, c, isrec;
221 	char *rr, *buf = *pbuf;
222 	int bufsize = *pbufsize;
223 	char *rs = getsval(rsloc);
224 
225 	if (*rs && rs[1]) {
226 		bool found;
227 
228 		fa *pfa = makedfa(rs, 1);
229 		if (newflag)
230 			found = fnematch(pfa, inf, &buf, &bufsize, recsize);
231 		else {
232 			int tempstat = pfa->initstat;
233 			pfa->initstat = 2;
234 			found = fnematch(pfa, inf, &buf, &bufsize, recsize);
235 			pfa->initstat = tempstat;
236 		}
237 		if (found)
238 			setptr(patbeg, '\0');
239 	} else {
240 		if ((sep = *rs) == 0) {
241 			sep = '\n';
242 			while ((c=getc(inf)) == '\n' && c != EOF)	/* skip leading \n's */
243 				;
244 			if (c != EOF)
245 				ungetc(c, inf);
246 		}
247 		for (rr = buf; ; ) {
248 			for (; (c=getc(inf)) != sep && c != EOF; ) {
249 				if (rr-buf+1 > bufsize)
250 					if (!adjbuf(&buf, &bufsize, 1+rr-buf,
251 					    recsize, &rr, "readrec 1"))
252 						FATAL("input record `%.30s...' too long", buf);
253 				*rr++ = c;
254 			}
255 			if (*rs == sep || c == EOF)
256 				break;
257 			if ((c = getc(inf)) == '\n' || c == EOF)	/* 2 in a row */
258 				break;
259 			if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr,
260 			    "readrec 2"))
261 				FATAL("input record `%.30s...' too long", buf);
262 			*rr++ = '\n';
263 			*rr++ = c;
264 		}
265 		if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
266 			FATAL("input record `%.30s...' too long", buf);
267 		*rr = 0;
268 	}
269 	*pbuf = buf;
270 	*pbufsize = bufsize;
271 	isrec = *buf || !feof(inf);
272 	DPRINTF("readrec saw <%s>, returns %d\n", buf, isrec);
273 	return isrec;
274 }
275 
276 char *getargv(int n)	/* get ARGV[n] */
277 {
278 	Cell *x;
279 	char *s, temp[50];
280 	extern Array *ARGVtab;
281 
282 	snprintf(temp, sizeof(temp), "%d", n);
283 	if (lookup(temp, ARGVtab) == NULL)
284 		return NULL;
285 	x = setsymtab(temp, "", 0.0, STR, ARGVtab);
286 	s = getsval(x);
287 	DPRINTF("getargv(%d) returns |%s|\n", n, s);
288 	return s;
289 }
290 
291 void setclvar(char *s)	/* set var=value from s */
292 {
293 	char *p;
294 	Cell *q;
295 
296 	for (p=s; *p != '='; p++)
297 		;
298 	*p++ = 0;
299 	p = qstring(p, '\0');
300 	q = setsymtab(s, p, 0.0, STR, symtab);
301 	setsval(q, p);
302 	if (is_number(q->sval)) {
303 		q->fval = atof(q->sval);
304 		q->tval |= NUM;
305 	}
306 	DPRINTF("command line set %s to |%s|\n", s, p);
307 }
308 
309 
310 void fldbld(void)	/* create fields from current record */
311 {
312 	/* this relies on having fields[] the same length as $0 */
313 	/* the fields are all stored in this one array with \0's */
314 	/* possibly with a final trailing \0 not associated with any field */
315 	char *r, *fr, sep;
316 	Cell *p;
317 	int i, j, n;
318 
319 	if (donefld)
320 		return;
321 	if (!isstr(fldtab[0]))
322 		getsval(fldtab[0]);
323 	r = fldtab[0]->sval;
324 	n = strlen(r);
325 	if (n > fieldssize) {
326 		xfree(fields);
327 		if ((fields = malloc(n+2)) == NULL) /* possibly 2 final \0s */
328 			FATAL("out of space for fields in fldbld %d", n);
329 		fieldssize = n;
330 	}
331 	fr = fields;
332 	i = 0;	/* number of fields accumulated here */
333 	if (inputFS == NULL)	/* make sure we have a copy of FS */
334 		savefs();
335 	if (strlen(inputFS) > 1) {	/* it's a regular expression */
336 		i = refldbld(r, inputFS);
337 	} else if ((sep = *inputFS) == ' ') {	/* default whitespace */
338 		for (i = 0; ; ) {
339 			while (*r == ' ' || *r == '\t' || *r == '\n')
340 				r++;
341 			if (*r == 0)
342 				break;
343 			i++;
344 			if (i > nfields)
345 				growfldtab(i);
346 			if (freeable(fldtab[i]))
347 				xfree(fldtab[i]->sval);
348 			fldtab[i]->sval = fr;
349 			fldtab[i]->tval = FLD | STR | DONTFREE;
350 			do
351 				*fr++ = *r++;
352 			while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
353 			*fr++ = 0;
354 		}
355 		*fr = 0;
356 	} else if ((sep = *inputFS) == 0) {		/* new: FS="" => 1 char/field */
357 		for (i = 0; *r != '\0'; r += n) {
358 			char buf[MB_LEN_MAX + 1];
359 
360 			i++;
361 			if (i > nfields)
362 				growfldtab(i);
363 			if (freeable(fldtab[i]))
364 				xfree(fldtab[i]->sval);
365 			n = mblen(r, MB_LEN_MAX);
366 			if (n < 0)
367 				n = 1;
368 			memcpy(buf, r, n);
369 			buf[n] = '\0';
370 			fldtab[i]->sval = tostring(buf);
371 			fldtab[i]->tval = FLD | STR;
372 		}
373 		*fr = 0;
374 	} else if (*r != 0) {	/* if 0, it's a null field */
375 		/* subtlecase : if length(FS) == 1 && length(RS > 0)
376 		 * \n is NOT a field separator (cf awk book 61,84).
377 		 * this variable is tested in the inner while loop.
378 		 */
379 		int rtest = '\n';  /* normal case */
380 		if (strlen(*RS) > 0)
381 			rtest = '\0';
382 		for (;;) {
383 			i++;
384 			if (i > nfields)
385 				growfldtab(i);
386 			if (freeable(fldtab[i]))
387 				xfree(fldtab[i]->sval);
388 			fldtab[i]->sval = fr;
389 			fldtab[i]->tval = FLD | STR | DONTFREE;
390 			while (*r != sep && *r != rtest && *r != '\0')	/* \n is always a separator */
391 				*fr++ = *r++;
392 			*fr++ = 0;
393 			if (*r++ == 0)
394 				break;
395 		}
396 		*fr = 0;
397 	}
398 	if (i > nfields)
399 		FATAL("record `%.30s...' has too many fields; can't happen", r);
400 	cleanfld(i+1, lastfld);	/* clean out junk from previous record */
401 	lastfld = i;
402 	donefld = true;
403 	for (j = 1; j <= lastfld; j++) {
404 		p = fldtab[j];
405 		if(is_number(p->sval)) {
406 			p->fval = atof(p->sval);
407 			p->tval |= NUM;
408 		}
409 	}
410 	setfval(nfloc, (Awkfloat) lastfld);
411 	donerec = true; /* restore */
412 	if (dbg) {
413 		for (j = 0; j <= lastfld; j++) {
414 			p = fldtab[j];
415 			printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
416 		}
417 	}
418 }
419 
420 void cleanfld(int n1, int n2)	/* clean out fields n1 .. n2 inclusive */
421 {				/* nvals remain intact */
422 	Cell *p;
423 	int i;
424 
425 	for (i = n1; i <= n2; i++) {
426 		p = fldtab[i];
427 		if (freeable(p))
428 			xfree(p->sval);
429 		p->sval = EMPTY,
430 		p->tval = FLD | STR | DONTFREE;
431 	}
432 }
433 
434 void newfld(int n)	/* add field n after end of existing lastfld */
435 {
436 	if (n > nfields)
437 		growfldtab(n);
438 	cleanfld(lastfld+1, n);
439 	lastfld = n;
440 	setfval(nfloc, (Awkfloat) n);
441 }
442 
443 void setlastfld(int n)	/* set lastfld cleaning fldtab cells if necessary */
444 {
445 	if (n < 0)
446 		FATAL("cannot set NF to a negative value");
447 	if (n > nfields)
448 		growfldtab(n);
449 
450 	if (lastfld < n)
451 	    cleanfld(lastfld+1, n);
452 	else
453 	    cleanfld(n+1, lastfld);
454 
455 	lastfld = n;
456 }
457 
458 Cell *fieldadr(int n)	/* get nth field */
459 {
460 	if (n < 0)
461 		FATAL("trying to access out of range field %d", n);
462 	if (n > nfields)	/* fields after NF are empty */
463 		growfldtab(n);	/* but does not increase NF */
464 	return(fldtab[n]);
465 }
466 
467 void growfldtab(int n)	/* make new fields up to at least $n */
468 {
469 	int nf = 2 * nfields;
470 	size_t s;
471 
472 	if (n > nf)
473 		nf = n;
474 	s = (nf+1) * (sizeof (struct Cell *));  /* freebsd: how much do we need? */
475 	if (s / sizeof(struct Cell *) - 1 == (size_t)nf) /* didn't overflow */
476 		fldtab = realloc(fldtab, s);
477 	else					/* overflow sizeof int */
478 		xfree(fldtab);	/* make it null */
479 	if (fldtab == NULL)
480 		FATAL("out of space creating %d fields", nf);
481 	makefields(nfields+1, nf);
482 	nfields = nf;
483 }
484 
485 int refldbld(const char *rec, const char *fs)	/* build fields from reg expr in FS */
486 {
487 	/* this relies on having fields[] the same length as $0 */
488 	/* the fields are all stored in this one array with \0's */
489 	char *fr;
490 	int i, tempstat, n;
491 	fa *pfa;
492 
493 	n = strlen(rec);
494 	if (n > fieldssize) {
495 		xfree(fields);
496 		if ((fields = malloc(n+1)) == NULL)
497 			FATAL("out of space for fields in refldbld %d", n);
498 		fieldssize = n;
499 	}
500 	fr = fields;
501 	*fr = '\0';
502 	if (*rec == '\0')
503 		return 0;
504 	pfa = makedfa(fs, 1);
505 	DPRINTF("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs);
506 	tempstat = pfa->initstat;
507 	for (i = 1; ; i++) {
508 		const size_t fss_rem = fields + fieldssize + 1 - fr;
509 		if (i > nfields)
510 			growfldtab(i);
511 		if (freeable(fldtab[i]))
512 			xfree(fldtab[i]->sval);
513 		fldtab[i]->tval = FLD | STR | DONTFREE;
514 		fldtab[i]->sval = fr;
515 		DPRINTF("refldbld: i=%d\n", i);
516 		if (nematch(pfa, rec)) {
517 			const size_t reclen = patbeg - rec;
518 			pfa->initstat = 2;	/* horrible coupling to b.c */
519 			DPRINTF("match %s (%d chars)\n", patbeg, patlen);
520 			if (reclen >= fss_rem)
521 				FATAL("out of space for fields in refldbld");
522 			memcpy(fr, rec, reclen);
523 			fr += reclen;
524 			*fr++ = '\0';
525 			rec = patbeg + patlen;
526 		} else {
527 			DPRINTF("no match %s\n", rec);
528 			if (strlcpy(fr, rec, fss_rem) >= fss_rem)
529 				FATAL("out of space for fields in refldbld");
530 			pfa->initstat = tempstat;
531 			break;
532 		}
533 	}
534 	return i;
535 }
536 
537 void recbld(void)	/* create $0 from $1..$NF if necessary */
538 {
539 	int i;
540 	char *r, *p;
541 	char *sep = getsval(ofsloc);
542 
543 	if (donerec)
544 		return;
545 	r = record;
546 	for (i = 1; i <= *NF; i++) {
547 		p = getsval(fldtab[i]);
548 		if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
549 			FATAL("created $0 `%.30s...' too long", record);
550 		while ((*r = *p++) != 0)
551 			r++;
552 		if (i < *NF) {
553 			if (!adjbuf(&record, &recsize, 2+strlen(sep)+r-record, recsize, &r, "recbld 2"))
554 				FATAL("created $0 `%.30s...' too long", record);
555 			for (p = sep; (*r = *p++) != 0; )
556 				r++;
557 		}
558 	}
559 	if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
560 		FATAL("built giant record `%.30s...'", record);
561 	*r = '\0';
562 	DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]);
563 
564 	if (freeable(fldtab[0]))
565 		xfree(fldtab[0]->sval);
566 	fldtab[0]->tval = REC | STR | DONTFREE;
567 	fldtab[0]->sval = record;
568 
569 	DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]);
570 	DPRINTF("recbld = |%s|\n", record);
571 	donerec = true;
572 }
573 
574 int	errorflag	= 0;
575 
576 void yyerror(const char *s)
577 {
578 	SYNTAX("%s", s);
579 }
580 
581 void SYNTAX(const char *fmt, ...)
582 {
583 	extern char *cmdname, *curfname;
584 	static int been_here = 0;
585 	va_list varg;
586 
587 	if (been_here++ > 2)
588 		return;
589 	fprintf(stderr, "%s: ", cmdname);
590 	va_start(varg, fmt);
591 	vfprintf(stderr, fmt, varg);
592 	va_end(varg);
593 	fprintf(stderr, " at source line %d", lineno);
594 	if (curfname != NULL)
595 		fprintf(stderr, " in function %s", curfname);
596 	if (compile_time == COMPILING && cursource() != NULL)
597 		fprintf(stderr, " source file %s", cursource());
598 	fprintf(stderr, "\n");
599 	errorflag = 2;
600 	eprint();
601 }
602 
603 extern int bracecnt, brackcnt, parencnt;
604 
605 void bracecheck(void)
606 {
607 	int c;
608 	static int beenhere = 0;
609 
610 	if (beenhere++)
611 		return;
612 	while ((c = input()) != EOF && c != '\0')
613 		bclass(c);
614 	bcheck2(bracecnt, '{', '}');
615 	bcheck2(brackcnt, '[', ']');
616 	bcheck2(parencnt, '(', ')');
617 }
618 
619 void bcheck2(int n, int c1, int c2)
620 {
621 	if (n == 1)
622 		fprintf(stderr, "\tmissing %c\n", c2);
623 	else if (n > 1)
624 		fprintf(stderr, "\t%d missing %c's\n", n, c2);
625 	else if (n == -1)
626 		fprintf(stderr, "\textra %c\n", c2);
627 	else if (n < -1)
628 		fprintf(stderr, "\t%d extra %c's\n", -n, c2);
629 }
630 
631 void FATAL(const char *fmt, ...)
632 {
633 	extern char *cmdname;
634 	va_list varg;
635 
636 	fflush(stdout);
637 	fprintf(stderr, "%s: ", cmdname);
638 	va_start(varg, fmt);
639 	vfprintf(stderr, fmt, varg);
640 	va_end(varg);
641 	error();
642 	if (dbg > 1)		/* core dump if serious debugging on */
643 		abort();
644 	exit(2);
645 }
646 
647 void WARNING(const char *fmt, ...)
648 {
649 	extern char *cmdname;
650 	va_list varg;
651 
652 	fflush(stdout);
653 	fprintf(stderr, "%s: ", cmdname);
654 	va_start(varg, fmt);
655 	vfprintf(stderr, fmt, varg);
656 	va_end(varg);
657 	error();
658 }
659 
660 void error()
661 {
662 	extern Node *curnode;
663 
664 	fprintf(stderr, "\n");
665 	if (compile_time != ERROR_PRINTING) {
666 		if (NR && *NR > 0) {
667 			fprintf(stderr, " input record number %d", (int) (*FNR));
668 			if (strcmp(*FILENAME, "-") != 0)
669 				fprintf(stderr, ", file %s", *FILENAME);
670 			fprintf(stderr, "\n");
671 		}
672 		if (curnode)
673 			fprintf(stderr, " source line number %d", curnode->lineno);
674 		else if (lineno)
675 			fprintf(stderr, " source line number %d", lineno);
676 		if (compile_time == COMPILING && cursource() != NULL)
677 			fprintf(stderr, " source file %s", cursource());
678 		fprintf(stderr, "\n");
679 		eprint();
680 	}
681 }
682 
683 void eprint(void)	/* try to print context around error */
684 {
685 	char *p, *q;
686 	int c;
687 	static int been_here = 0;
688 	extern char ebuf[], *ep;
689 
690 	if (compile_time != COMPILING || been_here++ > 0 || ebuf == ep)
691 		return;
692 	p = ep - 1;
693 	if (p > ebuf && *p == '\n')
694 		p--;
695 	for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
696 		;
697 	while (*p == '\n')
698 		p++;
699 	fprintf(stderr, " context is\n\t");
700 	for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
701 		;
702 	for ( ; p < q; p++)
703 		if (*p)
704 			putc(*p, stderr);
705 	fprintf(stderr, " >>> ");
706 	for ( ; p < ep; p++)
707 		if (*p)
708 			putc(*p, stderr);
709 	fprintf(stderr, " <<< ");
710 	if (*ep)
711 		while ((c = input()) != '\n' && c != '\0' && c != EOF) {
712 			putc(c, stderr);
713 			bclass(c);
714 		}
715 	putc('\n', stderr);
716 	ep = ebuf;
717 }
718 
719 void bclass(int c)
720 {
721 	switch (c) {
722 	case '{': bracecnt++; break;
723 	case '}': bracecnt--; break;
724 	case '[': brackcnt++; break;
725 	case ']': brackcnt--; break;
726 	case '(': parencnt++; break;
727 	case ')': parencnt--; break;
728 	}
729 }
730 
731 double errcheck(double x, const char *s)
732 {
733 
734 	if (errno == EDOM) {
735 		errno = 0;
736 		WARNING("%s argument out of domain", s);
737 		x = 1;
738 	} else if (errno == ERANGE) {
739 		errno = 0;
740 		WARNING("%s result out of range", s);
741 		x = 1;
742 	}
743 	return x;
744 }
745 
746 int isclvar(const char *s)	/* is s of form var=something ? */
747 {
748 	const char *os = s;
749 
750 	if (!isalpha((uschar) *s) && *s != '_')
751 		return 0;
752 	for ( ; *s; s++)
753 		if (!(isalnum((uschar) *s) || *s == '_'))
754 			break;
755 	return *s == '=' && s > os;
756 }
757 
758 /* strtod is supposed to be a proper test of what's a valid number */
759 /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
760 /* wrong: violates 4.10.1.4 of ansi C standard */
761 /* well, not quite. As of C99, hex floating point is allowed. so this is
762  * a bit of a mess.
763  */
764 
765 #include <math.h>
766 int is_number(const char *s)
767 {
768 	double r;
769 	char *ep;
770 	errno = 0;
771 	r = strtod(s, &ep);
772 	if (ep == s || r == HUGE_VAL || errno == ERANGE)
773 		return 0;
774 	/* allow \r as well. windows files aren't going to go away. */
775 	while (*ep == ' ' || *ep == '\t' || *ep == '\n' || *ep == '\r')
776 		ep++;
777 	if (*ep == '\0')
778 		return 1;
779 	else
780 		return 0;
781 }
782