xref: /openbsd-src/usr.bin/awk/lib.c (revision fb60ec6abe2666cd64ae233c0a37d18aa070e02d)
1*fb60ec6aSmillert /*	$OpenBSD: lib.c,v 1.59 2024/08/03 21:12:16 millert Exp $	*/
26ab05f83Stholo /****************************************************************
307edfa4aSkstailey Copyright (C) Lucent Technologies 1997
46ab05f83Stholo All Rights Reserved
56ab05f83Stholo 
66ab05f83Stholo Permission to use, copy, modify, and distribute this software and
76ab05f83Stholo its documentation for any purpose and without fee is hereby
86ab05f83Stholo granted, provided that the above copyright notice appear in all
96ab05f83Stholo copies and that both that the copyright notice and this
106ab05f83Stholo permission notice and warranty disclaimer appear in supporting
1107edfa4aSkstailey documentation, and that the name Lucent Technologies or any of
1207edfa4aSkstailey its entities not be used in advertising or publicity pertaining
1307edfa4aSkstailey to distribution of the software without specific, written prior
1407edfa4aSkstailey permission.
156ab05f83Stholo 
1607edfa4aSkstailey LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
1707edfa4aSkstailey INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
1807edfa4aSkstailey IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
1907edfa4aSkstailey SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2007edfa4aSkstailey WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
2107edfa4aSkstailey IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
2207edfa4aSkstailey ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
2307edfa4aSkstailey THIS SOFTWARE.
246ab05f83Stholo ****************************************************************/
256ab05f83Stholo 
266ab05f83Stholo #define DEBUG
276ab05f83Stholo #include <stdio.h>
286ab05f83Stholo #include <string.h>
29a11aea23Smillert #include <strings.h>
306ab05f83Stholo #include <ctype.h>
316ab05f83Stholo #include <errno.h>
326ab05f83Stholo #include <stdlib.h>
337b11b857Smillert #include <stdarg.h>
34fabd211eSmillert #include <limits.h>
356de80fb8Smillert #include <math.h>
366ab05f83Stholo #include "awk.h"
376ab05f83Stholo 
38fabd211eSmillert char	EMPTY[] = { '\0' };
396ab05f83Stholo FILE	*infile	= NULL;
406685ce51Smillert bool	innew;		/* true = infile has not been read by readrec */
41fabd211eSmillert char	*file	= EMPTY;
426ab05f83Stholo char	*record;
4307edfa4aSkstailey int	recsize	= RECSIZE;
446ab05f83Stholo char	*fields;
4507edfa4aSkstailey int	fieldssize = RECSIZE;
4607edfa4aSkstailey 
4707edfa4aSkstailey Cell	**fldtab;	/* pointers to Cells */
48fabd211eSmillert static size_t	len_inputFS = 0;
49fabd211eSmillert static char	*inputFS = NULL; /* FS at time of input, for field splitting */
506ab05f83Stholo 
5180b86fb0Smillert #define	MAXFLD	2
5207edfa4aSkstailey int	nfields	= MAXFLD;	/* last allocated slot for $i */
536ab05f83Stholo 
54f81b289fSmillert bool	donefld;	/* true = implies rec broken into fields */
55f81b289fSmillert bool	donerec;	/* true = record is valid (no flds have changed) */
566ab05f83Stholo 
5707edfa4aSkstailey int	lastfld	= 0;	/* last used field */
586ab05f83Stholo int	argno	= 1;	/* current input argument number */
596ab05f83Stholo extern	Awkfloat *ARGC;
606ab05f83Stholo 
616685ce51Smillert static Cell dollar0 = { OCELL, CFLD, NULL, EMPTY, 0.0, REC|STR|DONTFREE, NULL, NULL };
626685ce51Smillert static Cell dollar1 = { OCELL, CFLD, NULL, EMPTY, 0.0, FLD|STR|DONTFREE, NULL, NULL };
6307edfa4aSkstailey 
646ab05f83Stholo void recinit(unsigned int n)
656ab05f83Stholo {
66483fa115Smillert 	if ( (record = (char *) malloc(n)) == NULL
67483fa115Smillert 	  || (fields = (char *) malloc(n+1)) == NULL
68483fa115Smillert 	  || (fldtab = (Cell **) calloc(nfields+2, sizeof(*fldtab))) == NULL
69483fa115Smillert 	  || (fldtab[0] = (Cell *) malloc(sizeof(**fldtab))) == NULL)
707b11b857Smillert 		FATAL("out of space for $0 and fields");
716b41c728Smillert 	*record = '\0';
7207edfa4aSkstailey 	*fldtab[0] = dollar0;
7307edfa4aSkstailey 	fldtab[0]->sval = record;
7407edfa4aSkstailey 	fldtab[0]->nval = tostring("0");
7507edfa4aSkstailey 	makefields(1, nfields);
7607edfa4aSkstailey }
7707edfa4aSkstailey 
7807edfa4aSkstailey void makefields(int n1, int n2)		/* create $n1..$n2 inclusive */
7907edfa4aSkstailey {
8007edfa4aSkstailey 	char temp[50];
816ab05f83Stholo 	int i;
826ab05f83Stholo 
8307edfa4aSkstailey 	for (i = n1; i <= n2; i++) {
84483fa115Smillert 		fldtab[i] = (Cell *) malloc(sizeof(**fldtab));
8507edfa4aSkstailey 		if (fldtab[i] == NULL)
867b11b857Smillert 			FATAL("out of space in makefields %d", i);
8707edfa4aSkstailey 		*fldtab[i] = dollar1;
88d7cce239Smillert 		snprintf(temp, sizeof(temp), "%d", i);
8907edfa4aSkstailey 		fldtab[i]->nval = tostring(temp);
9007edfa4aSkstailey 	}
916ab05f83Stholo }
926ab05f83Stholo 
936ab05f83Stholo void initgetrec(void)
946ab05f83Stholo {
956ab05f83Stholo 	int i;
966ab05f83Stholo 	char *p;
976ab05f83Stholo 
986ab05f83Stholo 	for (i = 1; i < *ARGC; i++) {
99000399a4Smillert 		p = getargv(i); /* find 1st real filename */
100000399a4Smillert 		if (p == NULL || *p == '\0') {  /* deleted or zapped */
101000399a4Smillert 			argno++;
102000399a4Smillert 			continue;
103000399a4Smillert 		}
104000399a4Smillert 		if (!isclvar(p)) {
105000399a4Smillert 			setsval(lookup("FILENAME", symtab), p);
1066ab05f83Stholo 			return;
1076ab05f83Stholo 		}
1086ab05f83Stholo 		setclvar(p);	/* a commandline assignment before filename */
1096ab05f83Stholo 		argno++;
1106ab05f83Stholo 	}
1116ab05f83Stholo 	infile = stdin;		/* no filenames, so use stdin */
1126685ce51Smillert 	innew = true;
1136ab05f83Stholo }
1146ab05f83Stholo 
115c0fa3611Smillert /*
116c0fa3611Smillert  * POSIX specifies that fields are supposed to be evaluated as if they were
117c0fa3611Smillert  * split using the value of FS at the time that the record's value ($0) was
118c0fa3611Smillert  * read.
119c0fa3611Smillert  *
120c0fa3611Smillert  * Since field-splitting is done lazily, we save the current value of FS
121c0fa3611Smillert  * whenever a new record is read in (implicitly or via getline), or when
122c0fa3611Smillert  * a new value is assigned to $0.
123c0fa3611Smillert  */
124c0fa3611Smillert void savefs(void)
125c0fa3611Smillert {
126144915fcSmillert 	size_t len = strlen(getsval(fsloc));
127144915fcSmillert 	if (len >= len_inputFS) {
128fabd211eSmillert 		len_inputFS = len + 1;
129483fa115Smillert 		inputFS = (char *) realloc(inputFS, len_inputFS);
130fabd211eSmillert 		if (inputFS == NULL)
131c0fa3611Smillert 			FATAL("field separator %.10s... is too long", *FS);
132144915fcSmillert 	}
133144915fcSmillert 	if (strlcpy(inputFS, *FS, len_inputFS) >= len_inputFS)
134144915fcSmillert 		FATAL("field separator %.10s... is too long", *FS);
135c0fa3611Smillert }
136c0fa3611Smillert 
137f81b289fSmillert static bool firsttime = true;
13823cb51abSmillert 
139f81b289fSmillert int getrec(char **pbuf, int *pbufsize, bool isrecord)	/* get next input record */
14007edfa4aSkstailey {			/* note: cares whether buf == record */
1416ab05f83Stholo 	int c;
14207edfa4aSkstailey 	char *buf = *pbuf;
14380b86fb0Smillert 	uschar saveb0;
14480b86fb0Smillert 	int bufsize = *pbufsize, savebufsize = bufsize;
1456ab05f83Stholo 
1466ab05f83Stholo 	if (firsttime) {
147f81b289fSmillert 		firsttime = false;
1486ab05f83Stholo 		initgetrec();
1496ab05f83Stholo 	}
150115bd590Smillert 	DPRINTF("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n",
151115bd590Smillert 		*RS, *FS, *ARGC, *FILENAME);
15280b86fb0Smillert 	saveb0 = buf[0];
1536ab05f83Stholo 	buf[0] = 0;
1546ab05f83Stholo 	while (argno < *ARGC || infile == stdin) {
155115bd590Smillert 		DPRINTF("argno=%d, file=|%s|\n", argno, file);
1566ab05f83Stholo 		if (infile == NULL) {	/* have to open a new file */
1576ab05f83Stholo 			file = getargv(argno);
158000399a4Smillert 			if (file == NULL || *file == '\0') {	/* deleted or zapped */
1596ab05f83Stholo 				argno++;
1606ab05f83Stholo 				continue;
1616ab05f83Stholo 			}
1626ab05f83Stholo 			if (isclvar(file)) {	/* a var=value arg */
1636ab05f83Stholo 				setclvar(file);
1646ab05f83Stholo 				argno++;
1656ab05f83Stholo 				continue;
1666ab05f83Stholo 			}
1676ab05f83Stholo 			*FILENAME = file;
168115bd590Smillert 			DPRINTF("opening file %s\n", file);
1696ab05f83Stholo 			if (*file == '-' && *(file+1) == '\0')
1706ab05f83Stholo 				infile = stdin;
17107edfa4aSkstailey 			else if ((infile = fopen(file, "r")) == NULL)
1727b11b857Smillert 				FATAL("can't open file %s", file);
1730a102a45Smillert 			innew = true;
1746ab05f83Stholo 			setfval(fnrloc, 0.0);
1756ab05f83Stholo 		}
1766685ce51Smillert 		c = readrec(&buf, &bufsize, infile, innew);
1776685ce51Smillert 		if (innew)
1786685ce51Smillert 			innew = false;
1796ab05f83Stholo 		if (c != 0 || buf[0] != '\0') {	/* normal record */
18007edfa4aSkstailey 			if (isrecord) {
181483fa115Smillert 				double result;
182483fa115Smillert 
18307edfa4aSkstailey 				if (freeable(fldtab[0]))
18407edfa4aSkstailey 					xfree(fldtab[0]->sval);
18507edfa4aSkstailey 				fldtab[0]->sval = buf;	/* buf == record */
18607edfa4aSkstailey 				fldtab[0]->tval = REC | STR | DONTFREE;
187483fa115Smillert 				if (is_number(fldtab[0]->sval, & result)) {
188483fa115Smillert 					fldtab[0]->fval = result;
18907edfa4aSkstailey 					fldtab[0]->tval |= NUM;
1906ab05f83Stholo 				}
191c4ead62fSmillert 				donefld = false;
192c4ead62fSmillert 				donerec = true;
193c4ead62fSmillert 				savefs();
1946ab05f83Stholo 			}
1956ab05f83Stholo 			setfval(nrloc, nrloc->fval+1);
1966ab05f83Stholo 			setfval(fnrloc, fnrloc->fval+1);
19707edfa4aSkstailey 			*pbuf = buf;
19807edfa4aSkstailey 			*pbufsize = bufsize;
1996ab05f83Stholo 			return 1;
2006ab05f83Stholo 		}
2016ab05f83Stholo 		/* EOF arrived on this file; set up next */
2026ab05f83Stholo 		if (infile != stdin)
2036ab05f83Stholo 			fclose(infile);
2046ab05f83Stholo 		infile = NULL;
2056ab05f83Stholo 		argno++;
2066ab05f83Stholo 	}
20780b86fb0Smillert 	buf[0] = saveb0;
20807edfa4aSkstailey 	*pbuf = buf;
20980b86fb0Smillert 	*pbufsize = savebufsize;
2106ab05f83Stholo 	return 0;	/* true end of file */
2116ab05f83Stholo }
2126ab05f83Stholo 
2136ab05f83Stholo void nextfile(void)
2146ab05f83Stholo {
21580b86fb0Smillert 	if (infile != NULL && infile != stdin)
2166ab05f83Stholo 		fclose(infile);
2176ab05f83Stholo 	infile = NULL;
2186ab05f83Stholo 	argno++;
2196ab05f83Stholo }
2206ab05f83Stholo 
221a886e62eSmillert extern int readcsvrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag);
222a886e62eSmillert 
2236685ce51Smillert int readrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag)	/* read one record into buf */
2246ab05f83Stholo {
225a886e62eSmillert 	int sep, c, isrec; // POTENTIAL BUG? isrec is a macro in awk.h
226a886e62eSmillert 	char *rr = *pbuf, *buf = *pbuf;
22707edfa4aSkstailey 	int bufsize = *pbufsize;
22802265e66Smillert 	char *rs = getsval(rsloc);
2296ab05f83Stholo 
230a886e62eSmillert 	if (CSV) {
231*fb60ec6aSmillert 		c = readcsvrec(&buf, &bufsize, inf, newflag);
232a886e62eSmillert 		isrec = (c == EOF && rr == buf) ? false : true;
233a886e62eSmillert 	} else if (*rs && rs[1]) {
234f81b289fSmillert 		bool found;
235ec104564Smillert 
23635a004d4Smillert 		memset(buf, 0, bufsize);
237ec104564Smillert 		fa *pfa = makedfa(rs, 1);
2386685ce51Smillert 		if (newflag)
239ec104564Smillert 			found = fnematch(pfa, inf, &buf, &bufsize, recsize);
2406685ce51Smillert 		else {
2416685ce51Smillert 			int tempstat = pfa->initstat;
2426685ce51Smillert 			pfa->initstat = 2;
2436685ce51Smillert 			found = fnematch(pfa, inf, &buf, &bufsize, recsize);
2446685ce51Smillert 			pfa->initstat = tempstat;
2456685ce51Smillert 		}
246ec104564Smillert 		if (found)
247d7cce239Smillert 			setptr(patbeg, '\0');
2482a0b1829Smillert 		isrec = (found == 0 && *buf == '\0') ? false : true;
249ec104564Smillert 	} else {
25002265e66Smillert 		if ((sep = *rs) == 0) {
2516ab05f83Stholo 			sep = '\n';
2526ab05f83Stholo 			while ((c=getc(inf)) == '\n' && c != EOF)	/* skip leading \n's */
2536ab05f83Stholo 				;
2546ab05f83Stholo 			if (c != EOF)
2556ab05f83Stholo 				ungetc(c, inf);
2566ab05f83Stholo 		}
25707edfa4aSkstailey 		for (rr = buf; ; ) {
25807edfa4aSkstailey 			for (; (c=getc(inf)) != sep && c != EOF; ) {
25907edfa4aSkstailey 				if (rr-buf+1 > bufsize)
260ec104564Smillert 					if (!adjbuf(&buf, &bufsize, 1+rr-buf,
261ec104564Smillert 					    recsize, &rr, "readrec 1"))
2627b11b857Smillert 						FATAL("input record `%.30s...' too long", buf);
26307edfa4aSkstailey 				*rr++ = c;
26407edfa4aSkstailey 			}
26502265e66Smillert 			if (*rs == sep || c == EOF)
2666ab05f83Stholo 				break;
2676ab05f83Stholo 			if ((c = getc(inf)) == '\n' || c == EOF)	/* 2 in a row */
2686ab05f83Stholo 				break;
269ec104564Smillert 			if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr,
270ec104564Smillert 			    "readrec 2"))
2717b11b857Smillert 				FATAL("input record `%.30s...' too long", buf);
2726ab05f83Stholo 			*rr++ = '\n';
2736ab05f83Stholo 			*rr++ = c;
2746ab05f83Stholo 		}
27507edfa4aSkstailey 		if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
2767b11b857Smillert 			FATAL("input record `%.30s...' too long", buf);
2776ab05f83Stholo 		*rr = 0;
2782a0b1829Smillert 		isrec = (c == EOF && rr == buf) ? false : true;
279ec104564Smillert 	}
28007edfa4aSkstailey 	*pbuf = buf;
28107edfa4aSkstailey 	*pbufsize = bufsize;
282115bd590Smillert 	DPRINTF("readrec saw <%s>, returns %d\n", buf, isrec);
283ec104564Smillert 	return isrec;
2846ab05f83Stholo }
2856ab05f83Stholo 
286a886e62eSmillert 
287a886e62eSmillert /*******************
288a886e62eSmillert  * loose ends here:
289a886e62eSmillert  *   \r\n should become \n
290a886e62eSmillert  *   what about bare \r?  Excel uses that for embedded newlines
291a886e62eSmillert  *   can't have "" in unquoted fields, according to RFC 4180
292a886e62eSmillert */
293a886e62eSmillert 
294a886e62eSmillert int readcsvrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag) /* csv can have \n's */
295a886e62eSmillert {			/* so read a complete record that might be multiple lines */
296a886e62eSmillert 	int sep, c;
297a886e62eSmillert 	char *rr = *pbuf, *buf = *pbuf;
298a886e62eSmillert 	int bufsize = *pbufsize;
299a886e62eSmillert 	bool in_quote = false;
300a886e62eSmillert 
301a886e62eSmillert 	sep = '\n'; /* the only separator; have to skip over \n embedded in "..." */
302a886e62eSmillert 	rr = buf;
303a886e62eSmillert 	while ((c = getc(inf)) != EOF) {
304a886e62eSmillert 		if (c == sep) {
305a886e62eSmillert 			if (! in_quote)
306a886e62eSmillert 				break;
307a886e62eSmillert 			if (rr > buf && rr[-1] == '\r')	// remove \r if was \r\n
308a886e62eSmillert 				rr--;
309a886e62eSmillert 		}
310a886e62eSmillert 
311a886e62eSmillert 		if (rr-buf+1 > bufsize)
312a886e62eSmillert 			if (!adjbuf(&buf, &bufsize, 1+rr-buf,
313a886e62eSmillert 			    recsize, &rr, "readcsvrec 1"))
314a886e62eSmillert 				FATAL("input record `%.30s...' too long", buf);
315a886e62eSmillert 		*rr++ = c;
316a886e62eSmillert 		if (c == '"')
317a886e62eSmillert 			in_quote = ! in_quote;
318a886e62eSmillert  	}
319a886e62eSmillert 	if (c == '\n' && rr > buf && rr[-1] == '\r') 	// remove \r if was \r\n
320a886e62eSmillert 		rr--;
321a886e62eSmillert 
322a886e62eSmillert 	if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readcsvrec 4"))
323a886e62eSmillert 		FATAL("input record `%.30s...' too long", buf);
324a886e62eSmillert 	*rr = 0;
325a886e62eSmillert 	*pbuf = buf;
326a886e62eSmillert 	*pbufsize = bufsize;
327a886e62eSmillert 	DPRINTF("readcsvrec saw <%s>, returns %d\n", buf, c);
328a886e62eSmillert 	return c;
329a886e62eSmillert }
330a886e62eSmillert 
3316ab05f83Stholo char *getargv(int n)	/* get ARGV[n] */
3326ab05f83Stholo {
333925804ecSjsg 	Array *ap;
3346ab05f83Stholo 	Cell *x;
33507edfa4aSkstailey 	char *s, temp[50];
336925804ecSjsg 	extern Cell *ARGVcell;
3376ab05f83Stholo 
338925804ecSjsg 	ap = (Array *)ARGVcell->sval;
339d7cce239Smillert 	snprintf(temp, sizeof(temp), "%d", n);
340925804ecSjsg 	if (lookup(temp, ap) == NULL)
341000399a4Smillert 		return NULL;
342925804ecSjsg 	x = setsymtab(temp, "", 0.0, STR, ap);
3436ab05f83Stholo 	s = getsval(x);
344115bd590Smillert 	DPRINTF("getargv(%d) returns |%s|\n", n, s);
3456ab05f83Stholo 	return s;
3466ab05f83Stholo }
3476ab05f83Stholo 
3486ab05f83Stholo void setclvar(char *s)	/* set var=value from s */
3496ab05f83Stholo {
3507ef192a2Smillert 	char *e, *p;
3516ab05f83Stholo 	Cell *q;
352483fa115Smillert 	double result;
3536ab05f83Stholo 
354a886e62eSmillert /* commit f3d9187d4e0f02294fb1b0e31152070506314e67 broke T.argv test */
355a886e62eSmillert /* I don't understand why it was changed. */
356a886e62eSmillert 
3576ab05f83Stholo 	for (p=s; *p != '='; p++)
3586ab05f83Stholo 		;
3597ef192a2Smillert 	e = p;
3606ab05f83Stholo 	*p++ = 0;
3616ab05f83Stholo 	p = qstring(p, '\0');
3626ab05f83Stholo 	q = setsymtab(s, p, 0.0, STR, symtab);
3636ab05f83Stholo 	setsval(q, p);
364483fa115Smillert 	if (is_number(q->sval, & result)) {
365483fa115Smillert 		q->fval = result;
3666ab05f83Stholo 		q->tval |= NUM;
3676ab05f83Stholo 	}
368115bd590Smillert 	DPRINTF("command line set %s to |%s|\n", s, p);
369dd20eed5Smillert 	free(p);
3707ef192a2Smillert 	*e = '=';
3716ab05f83Stholo }
3726ab05f83Stholo 
3736ab05f83Stholo 
3746ab05f83Stholo void fldbld(void)	/* create fields from current record */
3756ab05f83Stholo {
37607edfa4aSkstailey 	/* this relies on having fields[] the same length as $0 */
37707edfa4aSkstailey 	/* the fields are all stored in this one array with \0's */
378000399a4Smillert 	/* possibly with a final trailing \0 not associated with any field */
3796ab05f83Stholo 	char *r, *fr, sep;
3806ab05f83Stholo 	Cell *p;
38107edfa4aSkstailey 	int i, j, n;
3826ab05f83Stholo 
3836ab05f83Stholo 	if (donefld)
3846ab05f83Stholo 		return;
38507edfa4aSkstailey 	if (!isstr(fldtab[0]))
38607edfa4aSkstailey 		getsval(fldtab[0]);
38707edfa4aSkstailey 	r = fldtab[0]->sval;
38807edfa4aSkstailey 	n = strlen(r);
38907edfa4aSkstailey 	if (n > fieldssize) {
39007edfa4aSkstailey 		xfree(fields);
391483fa115Smillert 		if ((fields = (char *) malloc(n+2)) == NULL) /* possibly 2 final \0s */
3927b11b857Smillert 			FATAL("out of space for fields in fldbld %d", n);
39307edfa4aSkstailey 		fieldssize = n;
39407edfa4aSkstailey 	}
3956ab05f83Stholo 	fr = fields;
3966ab05f83Stholo 	i = 0;	/* number of fields accumulated here */
3974edce374Smillert 	if (inputFS == NULL)	/* make sure we have a copy of FS */
3984edce374Smillert 		savefs();
399d9ecc40dSmillert 	if (!CSV && strlen(inputFS) > 1) {	/* it's a regular expression */
400b2698ba9Smillert 		i = refldbld(r, inputFS);
401a886e62eSmillert 	} else if (!CSV && (sep = *inputFS) == ' ') {	/* default whitespace */
4026ab05f83Stholo 		for (i = 0; ; ) {
4036ab05f83Stholo 			while (*r == ' ' || *r == '\t' || *r == '\n')
4046ab05f83Stholo 				r++;
4056ab05f83Stholo 			if (*r == 0)
4066ab05f83Stholo 				break;
4076ab05f83Stholo 			i++;
40807edfa4aSkstailey 			if (i > nfields)
40907edfa4aSkstailey 				growfldtab(i);
41007edfa4aSkstailey 			if (freeable(fldtab[i]))
41107edfa4aSkstailey 				xfree(fldtab[i]->sval);
41207edfa4aSkstailey 			fldtab[i]->sval = fr;
41307edfa4aSkstailey 			fldtab[i]->tval = FLD | STR | DONTFREE;
4146ab05f83Stholo 			do
4156ab05f83Stholo 				*fr++ = *r++;
4166ab05f83Stholo 			while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
4176ab05f83Stholo 			*fr++ = 0;
4186ab05f83Stholo 		}
4196ab05f83Stholo 		*fr = 0;
420a886e62eSmillert 	} else if (CSV) {	/* CSV processing.  no error handling */
421a886e62eSmillert 		if (*r != 0) {
422a886e62eSmillert 			for (;;) {
4236ab05f83Stholo 				i++;
42407edfa4aSkstailey 				if (i > nfields)
42507edfa4aSkstailey 					growfldtab(i);
42607edfa4aSkstailey 				if (freeable(fldtab[i]))
42707edfa4aSkstailey 					xfree(fldtab[i]->sval);
428a886e62eSmillert 				fldtab[i]->sval = fr;
429a886e62eSmillert 				fldtab[i]->tval = FLD | STR | DONTFREE;
430a886e62eSmillert 				if (*r == '"' ) { /* start of "..." */
431a886e62eSmillert 					for (r++ ; *r != '\0'; ) {
432a886e62eSmillert 						if (*r == '"' && r[1] != '\0' && r[1] == '"') {
433a886e62eSmillert 							r += 2; /* doubled quote */
434a886e62eSmillert 							*fr++ = '"';
435a886e62eSmillert 						} else if (*r == '"' && (r[1] == '\0' || r[1] == ',')) {
436a886e62eSmillert 							r++; /* skip over closing quote */
437a886e62eSmillert 							break;
438a886e62eSmillert 						} else {
439a886e62eSmillert 							*fr++ = *r++;
440a886e62eSmillert 						}
441a886e62eSmillert 					}
442a886e62eSmillert 					*fr++ = 0;
443a886e62eSmillert 				} else {	/* unquoted field */
444a886e62eSmillert 					while (*r != ',' && *r != '\0')
445a886e62eSmillert 						*fr++ = *r++;
446a886e62eSmillert 					*fr++ = 0;
447a886e62eSmillert 				}
448a886e62eSmillert 				if (*r++ == 0)
449a886e62eSmillert 					break;
450a886e62eSmillert 
451a886e62eSmillert 			}
452a886e62eSmillert 		}
453a886e62eSmillert 		*fr = 0;
454a886e62eSmillert 	} else if ((sep = *inputFS) == 0) {	/* new: FS="" => 1 char/field */
455a886e62eSmillert 		for (i = 0; *r != '\0'; ) {
456a886e62eSmillert 			char buf[10];
457a886e62eSmillert 			i++;
458a886e62eSmillert 			if (i > nfields)
459a886e62eSmillert 				growfldtab(i);
460a886e62eSmillert 			if (freeable(fldtab[i]))
461a886e62eSmillert 				xfree(fldtab[i]->sval);
462a886e62eSmillert 			n = u8_nextlen(r);
463a886e62eSmillert 			for (j = 0; j < n; j++)
464a886e62eSmillert 				buf[j] = *r++;
465a886e62eSmillert 			buf[j] = '\0';
46607edfa4aSkstailey 			fldtab[i]->sval = tostring(buf);
46707edfa4aSkstailey 			fldtab[i]->tval = FLD | STR;
4686ab05f83Stholo 		}
4696ab05f83Stholo 		*fr = 0;
4706ab05f83Stholo 	} else if (*r != 0) {	/* if 0, it's a null field */
47123cb51abSmillert 		/* subtle case: if length(FS) == 1 && length(RS > 0)
47223cb51abSmillert 		 * \n is NOT a field separator (cf awk book 61,84).
47323cb51abSmillert 		 * this variable is tested in the inner while loop.
47423cb51abSmillert 		 */
47523cb51abSmillert 		int rtest = '\n';  /* normal case */
47623cb51abSmillert 		if (strlen(*RS) > 0)
47723cb51abSmillert 			rtest = '\0';
4786ab05f83Stholo 		for (;;) {
4796ab05f83Stholo 			i++;
48007edfa4aSkstailey 			if (i > nfields)
48107edfa4aSkstailey 				growfldtab(i);
48207edfa4aSkstailey 			if (freeable(fldtab[i]))
48307edfa4aSkstailey 				xfree(fldtab[i]->sval);
48407edfa4aSkstailey 			fldtab[i]->sval = fr;
48507edfa4aSkstailey 			fldtab[i]->tval = FLD | STR | DONTFREE;
48623cb51abSmillert 			while (*r != sep && *r != rtest && *r != '\0')	/* \n is always a separator */
4876ab05f83Stholo 				*fr++ = *r++;
4886ab05f83Stholo 			*fr++ = 0;
4896ab05f83Stholo 			if (*r++ == 0)
4906ab05f83Stholo 				break;
4916ab05f83Stholo 		}
4926ab05f83Stholo 		*fr = 0;
4936ab05f83Stholo 	}
49407edfa4aSkstailey 	if (i > nfields)
4957b11b857Smillert 		FATAL("record `%.30s...' has too many fields; can't happen", r);
49607edfa4aSkstailey 	cleanfld(i+1, lastfld);	/* clean out junk from previous record */
49707edfa4aSkstailey 	lastfld = i;
498f81b289fSmillert 	donefld = true;
49907edfa4aSkstailey 	for (j = 1; j <= lastfld; j++) {
500483fa115Smillert 		double result;
501483fa115Smillert 
50207edfa4aSkstailey 		p = fldtab[j];
503483fa115Smillert 		if(is_number(p->sval, & result)) {
504483fa115Smillert 			p->fval = result;
5056ab05f83Stholo 			p->tval |= NUM;
5066ab05f83Stholo 		}
5076ab05f83Stholo 	}
50807edfa4aSkstailey 	setfval(nfloc, (Awkfloat) lastfld);
509f81b289fSmillert 	donerec = true; /* restore */
51007edfa4aSkstailey 	if (dbg) {
51107edfa4aSkstailey 		for (j = 0; j <= lastfld; j++) {
51207edfa4aSkstailey 			p = fldtab[j];
51307edfa4aSkstailey 			printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
51407edfa4aSkstailey 		}
51507edfa4aSkstailey 	}
5166ab05f83Stholo }
5176ab05f83Stholo 
5186ab05f83Stholo void cleanfld(int n1, int n2)	/* clean out fields n1 .. n2 inclusive */
51907edfa4aSkstailey {				/* nvals remain intact */
52007edfa4aSkstailey 	Cell *p;
52107edfa4aSkstailey 	int i;
5226ab05f83Stholo 
52307edfa4aSkstailey 	for (i = n1; i <= n2; i++) {
52407edfa4aSkstailey 		p = fldtab[i];
52507edfa4aSkstailey 		if (freeable(p))
5266ab05f83Stholo 			xfree(p->sval);
527fabd211eSmillert 		p->sval = EMPTY,
5286ab05f83Stholo 		p->tval = FLD | STR | DONTFREE;
5296ab05f83Stholo 	}
5306ab05f83Stholo }
5316ab05f83Stholo 
53207edfa4aSkstailey void newfld(int n)	/* add field n after end of existing lastfld */
5336ab05f83Stholo {
53407edfa4aSkstailey 	if (n > nfields)
53507edfa4aSkstailey 		growfldtab(n);
53607edfa4aSkstailey 	cleanfld(lastfld+1, n);
53707edfa4aSkstailey 	lastfld = n;
5386ab05f83Stholo 	setfval(nfloc, (Awkfloat) n);
5396ab05f83Stholo }
5406ab05f83Stholo 
541c062391aSmillert void setlastfld(int n)	/* set lastfld cleaning fldtab cells if necessary */
542c062391aSmillert {
54302265e66Smillert 	if (n < 0)
54402265e66Smillert 		FATAL("cannot set NF to a negative value");
545c062391aSmillert 	if (n > nfields)
546c062391aSmillert 		growfldtab(n);
547c062391aSmillert 
548c062391aSmillert 	if (lastfld < n)
549c062391aSmillert 	    cleanfld(lastfld+1, n);
550c062391aSmillert 	else
551c062391aSmillert 	    cleanfld(n+1, lastfld);
552c062391aSmillert 
553c062391aSmillert 	lastfld = n;
554c062391aSmillert }
555c062391aSmillert 
55607edfa4aSkstailey Cell *fieldadr(int n)	/* get nth field */
55707edfa4aSkstailey {
55807edfa4aSkstailey 	if (n < 0)
55923cb51abSmillert 		FATAL("trying to access out of range field %d", n);
56007edfa4aSkstailey 	if (n > nfields)	/* fields after NF are empty */
56107edfa4aSkstailey 		growfldtab(n);	/* but does not increase NF */
56207edfa4aSkstailey 	return(fldtab[n]);
56307edfa4aSkstailey }
56407edfa4aSkstailey 
56507edfa4aSkstailey void growfldtab(int n)	/* make new fields up to at least $n */
56607edfa4aSkstailey {
56707edfa4aSkstailey 	int nf = 2 * nfields;
56823cb51abSmillert 	size_t s;
56907edfa4aSkstailey 
57007edfa4aSkstailey 	if (n > nf)
57107edfa4aSkstailey 		nf = n;
57223cb51abSmillert 	s = (nf+1) * (sizeof (struct Cell *));  /* freebsd: how much do we need? */
5736685ce51Smillert 	if (s / sizeof(struct Cell *) - 1 == (size_t)nf) /* didn't overflow */
574483fa115Smillert 		fldtab = (Cell **) realloc(fldtab, s);
57523cb51abSmillert 	else					/* overflow sizeof int */
57623cb51abSmillert 		xfree(fldtab);	/* make it null */
57707edfa4aSkstailey 	if (fldtab == NULL)
5787b11b857Smillert 		FATAL("out of space creating %d fields", nf);
57907edfa4aSkstailey 	makefields(nfields+1, nf);
58007edfa4aSkstailey 	nfields = nf;
58107edfa4aSkstailey }
58207edfa4aSkstailey 
5839a69093aSmillert int refldbld(const char *rec, const char *fs)	/* build fields from reg expr in FS */
5846ab05f83Stholo {
58507edfa4aSkstailey 	/* this relies on having fields[] the same length as $0 */
58607edfa4aSkstailey 	/* the fields are all stored in this one array with \0's */
5876ab05f83Stholo 	char *fr;
58807edfa4aSkstailey 	int i, tempstat, n;
5896ab05f83Stholo 	fa *pfa;
5906ab05f83Stholo 
59107edfa4aSkstailey 	n = strlen(rec);
59207edfa4aSkstailey 	if (n > fieldssize) {
59307edfa4aSkstailey 		xfree(fields);
594483fa115Smillert 		if ((fields = (char *) malloc(n+1)) == NULL)
5957b11b857Smillert 			FATAL("out of space for fields in refldbld %d", n);
59607edfa4aSkstailey 		fieldssize = n;
59707edfa4aSkstailey 	}
5986ab05f83Stholo 	fr = fields;
5996ab05f83Stholo 	*fr = '\0';
6006ab05f83Stholo 	if (*rec == '\0')
6016ab05f83Stholo 		return 0;
6026ab05f83Stholo 	pfa = makedfa(fs, 1);
603115bd590Smillert 	DPRINTF("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs);
6046ab05f83Stholo 	tempstat = pfa->initstat;
60507edfa4aSkstailey 	for (i = 1; ; i++) {
606144915fcSmillert 		const size_t fss_rem = fields + fieldssize + 1 - fr;
60707edfa4aSkstailey 		if (i > nfields)
60807edfa4aSkstailey 			growfldtab(i);
60907edfa4aSkstailey 		if (freeable(fldtab[i]))
61007edfa4aSkstailey 			xfree(fldtab[i]->sval);
61107edfa4aSkstailey 		fldtab[i]->tval = FLD | STR | DONTFREE;
61207edfa4aSkstailey 		fldtab[i]->sval = fr;
613115bd590Smillert 		DPRINTF("refldbld: i=%d\n", i);
6146ab05f83Stholo 		if (nematch(pfa, rec)) {
615144915fcSmillert 			const size_t reclen = patbeg - rec;
61607edfa4aSkstailey 			pfa->initstat = 2;	/* horrible coupling to b.c */
617115bd590Smillert 			DPRINTF("match %s (%d chars)\n", patbeg, patlen);
618144915fcSmillert 			if (reclen >= fss_rem)
619144915fcSmillert 				FATAL("out of space for fields in refldbld");
620144915fcSmillert 			memcpy(fr, rec, reclen);
621144915fcSmillert 			fr += reclen;
622144915fcSmillert 			*fr++ = '\0';
6236ab05f83Stholo 			rec = patbeg + patlen;
6246ab05f83Stholo 		} else {
625115bd590Smillert 			DPRINTF("no match %s\n", rec);
626144915fcSmillert 			if (strlcpy(fr, rec, fss_rem) >= fss_rem)
627144915fcSmillert 				FATAL("out of space for fields in refldbld");
6286ab05f83Stholo 			pfa->initstat = tempstat;
6296ab05f83Stholo 			break;
6306ab05f83Stholo 		}
6316ab05f83Stholo 	}
6326ab05f83Stholo 	return i;
6336ab05f83Stholo }
6346ab05f83Stholo 
6356ab05f83Stholo void recbld(void)	/* create $0 from $1..$NF if necessary */
6366ab05f83Stholo {
6376ab05f83Stholo 	int i;
6386ab05f83Stholo 	char *r, *p;
63902265e66Smillert 	char *sep = getsval(ofsloc);
6406ab05f83Stholo 
641f81b289fSmillert 	if (donerec)
6426ab05f83Stholo 		return;
643a4fa8700Smillert 	r = record;
6446ab05f83Stholo 	for (i = 1; i <= *NF; i++) {
64507edfa4aSkstailey 		p = getsval(fldtab[i]);
646a4fa8700Smillert 		if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
6477b11b857Smillert 			FATAL("created $0 `%.30s...' too long", record);
64807edfa4aSkstailey 		while ((*r = *p++) != 0)
6496ab05f83Stholo 			r++;
65007edfa4aSkstailey 		if (i < *NF) {
65102265e66Smillert 			if (!adjbuf(&record, &recsize, 2+strlen(sep)+r-record, recsize, &r, "recbld 2"))
6527b11b857Smillert 				FATAL("created $0 `%.30s...' too long", record);
65302265e66Smillert 			for (p = sep; (*r = *p++) != 0; )
6546ab05f83Stholo 				r++;
6556ab05f83Stholo 		}
65607edfa4aSkstailey 	}
657a4fa8700Smillert 	if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
6587b11b857Smillert 		FATAL("built giant record `%.30s...'", record);
6596ab05f83Stholo 	*r = '\0';
660115bd590Smillert 	DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]);
66107edfa4aSkstailey 
66207edfa4aSkstailey 	if (freeable(fldtab[0]))
66307edfa4aSkstailey 		xfree(fldtab[0]->sval);
66407edfa4aSkstailey 	fldtab[0]->tval = REC | STR | DONTFREE;
66507edfa4aSkstailey 	fldtab[0]->sval = record;
66607edfa4aSkstailey 
667115bd590Smillert 	DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]);
668115bd590Smillert 	DPRINTF("recbld = |%s|\n", record);
669f81b289fSmillert 	donerec = true;
6706ab05f83Stholo }
6716ab05f83Stholo 
6726ab05f83Stholo int	errorflag	= 0;
6736ab05f83Stholo 
6749a69093aSmillert void yyerror(const char *s)
6756ab05f83Stholo {
6767843b775Sgrange 	SYNTAX("%s", s);
6777b11b857Smillert }
6787b11b857Smillert 
6799a69093aSmillert void SYNTAX(const char *fmt, ...)
6807b11b857Smillert {
6816ab05f83Stholo 	extern char *cmdname, *curfname;
6826ab05f83Stholo 	static int been_here = 0;
6837b11b857Smillert 	va_list varg;
6846ab05f83Stholo 
6856ab05f83Stholo 	if (been_here++ > 2)
6866ab05f83Stholo 		return;
6877b11b857Smillert 	fprintf(stderr, "%s: ", cmdname);
6887b11b857Smillert 	va_start(varg, fmt);
6897b11b857Smillert 	vfprintf(stderr, fmt, varg);
6907b11b857Smillert 	va_end(varg);
6916ab05f83Stholo 	fprintf(stderr, " at source line %d", lineno);
6926ab05f83Stholo 	if (curfname != NULL)
6936ab05f83Stholo 		fprintf(stderr, " in function %s", curfname);
694f81b289fSmillert 	if (compile_time == COMPILING && cursource() != NULL)
695271018d0Smillert 		fprintf(stderr, " source file %s", cursource());
6966ab05f83Stholo 	fprintf(stderr, "\n");
6976ab05f83Stholo 	errorflag = 2;
6986ab05f83Stholo 	eprint();
6996ab05f83Stholo }
7006ab05f83Stholo 
7016ab05f83Stholo extern int bracecnt, brackcnt, parencnt;
7026ab05f83Stholo 
7036ab05f83Stholo void bracecheck(void)
7046ab05f83Stholo {
7056ab05f83Stholo 	int c;
7066ab05f83Stholo 	static int beenhere = 0;
7076ab05f83Stholo 
7086ab05f83Stholo 	if (beenhere++)
7096ab05f83Stholo 		return;
710d3dcc631Smillert 	while ((c = input()) != EOF && c != '\0')
7116ab05f83Stholo 		bclass(c);
7126ab05f83Stholo 	bcheck2(bracecnt, '{', '}');
7136ab05f83Stholo 	bcheck2(brackcnt, '[', ']');
7146ab05f83Stholo 	bcheck2(parencnt, '(', ')');
7156ab05f83Stholo }
7166ab05f83Stholo 
7176ab05f83Stholo void bcheck2(int n, int c1, int c2)
7186ab05f83Stholo {
7196ab05f83Stholo 	if (n == 1)
7206ab05f83Stholo 		fprintf(stderr, "\tmissing %c\n", c2);
7216ab05f83Stholo 	else if (n > 1)
7226ab05f83Stholo 		fprintf(stderr, "\t%d missing %c's\n", n, c2);
7236ab05f83Stholo 	else if (n == -1)
7246ab05f83Stholo 		fprintf(stderr, "\textra %c\n", c2);
7256ab05f83Stholo 	else if (n < -1)
7266ab05f83Stholo 		fprintf(stderr, "\t%d extra %c's\n", -n, c2);
7276ab05f83Stholo }
7286ab05f83Stholo 
7294edce374Smillert void FATAL(const char *fmt, ...)
7306ab05f83Stholo {
7316ab05f83Stholo 	extern char *cmdname;
7327b11b857Smillert 	va_list varg;
7336ab05f83Stholo 
7346ab05f83Stholo 	fflush(stdout);
7356ab05f83Stholo 	fprintf(stderr, "%s: ", cmdname);
7367b11b857Smillert 	va_start(varg, fmt);
7377b11b857Smillert 	vfprintf(stderr, fmt, varg);
7387b11b857Smillert 	va_end(varg);
7397b11b857Smillert 	error();
7407b11b857Smillert 	if (dbg > 1)		/* core dump if serious debugging on */
7417b11b857Smillert 		abort();
7427b11b857Smillert 	exit(2);
7437b11b857Smillert }
7447b11b857Smillert 
7459a69093aSmillert void WARNING(const char *fmt, ...)
7467b11b857Smillert {
7477b11b857Smillert 	extern char *cmdname;
7487b11b857Smillert 	va_list varg;
7497b11b857Smillert 
7507b11b857Smillert 	fflush(stdout);
7517b11b857Smillert 	fprintf(stderr, "%s: ", cmdname);
7527b11b857Smillert 	va_start(varg, fmt);
7537b11b857Smillert 	vfprintf(stderr, fmt, varg);
7547b11b857Smillert 	va_end(varg);
7557b11b857Smillert 	error();
7567b11b857Smillert }
7577b11b857Smillert 
75888689b4cSmillert void error(void)
7597b11b857Smillert {
7607b11b857Smillert 	extern Node *curnode;
7617b11b857Smillert 
7626ab05f83Stholo 	fprintf(stderr, "\n");
763f81b289fSmillert 	if (compile_time != ERROR_PRINTING) {
764f81b289fSmillert 		if (NR && *NR > 0) {
7656ab05f83Stholo 			fprintf(stderr, " input record number %d", (int) (*FNR));
7666ab05f83Stholo 			if (strcmp(*FILENAME, "-") != 0)
7676ab05f83Stholo 				fprintf(stderr, ", file %s", *FILENAME);
7686ab05f83Stholo 			fprintf(stderr, "\n");
7696ab05f83Stholo 		}
770f81b289fSmillert 		if (curnode)
771271018d0Smillert 			fprintf(stderr, " source line number %d", curnode->lineno);
772f81b289fSmillert 		else if (lineno)
773271018d0Smillert 			fprintf(stderr, " source line number %d", lineno);
774f81b289fSmillert 		if (compile_time == COMPILING && cursource() != NULL)
775271018d0Smillert 			fprintf(stderr, " source file %s", cursource());
776271018d0Smillert 		fprintf(stderr, "\n");
7776ab05f83Stholo 		eprint();
7786ab05f83Stholo 	}
779149ad3e3Smillert }
7806ab05f83Stholo 
7816ab05f83Stholo void eprint(void)	/* try to print context around error */
7826ab05f83Stholo {
7836ab05f83Stholo 	char *p, *q;
7846ab05f83Stholo 	int c;
7856ab05f83Stholo 	static int been_here = 0;
7866ab05f83Stholo 	extern char ebuf[], *ep;
7876ab05f83Stholo 
788f81b289fSmillert 	if (compile_time != COMPILING || been_here++ > 0 || ebuf == ep)
7896ab05f83Stholo 		return;
7906ab05f83Stholo 	p = ep - 1;
7916ab05f83Stholo 	if (p > ebuf && *p == '\n')
7926ab05f83Stholo 		p--;
7936ab05f83Stholo 	for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
7946ab05f83Stholo 		;
7956ab05f83Stholo 	while (*p == '\n')
7966ab05f83Stholo 		p++;
7976ab05f83Stholo 	fprintf(stderr, " context is\n\t");
7986ab05f83Stholo 	for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
7996ab05f83Stholo 		;
8006ab05f83Stholo 	for ( ; p < q; p++)
8016ab05f83Stholo 		if (*p)
8026ab05f83Stholo 			putc(*p, stderr);
8036ab05f83Stholo 	fprintf(stderr, " >>> ");
8046ab05f83Stholo 	for ( ; p < ep; p++)
8056ab05f83Stholo 		if (*p)
8066ab05f83Stholo 			putc(*p, stderr);
8076ab05f83Stholo 	fprintf(stderr, " <<< ");
8086ab05f83Stholo 	if (*ep)
8096ab05f83Stholo 		while ((c = input()) != '\n' && c != '\0' && c != EOF) {
8106ab05f83Stholo 			putc(c, stderr);
8116ab05f83Stholo 			bclass(c);
8126ab05f83Stholo 		}
8136ab05f83Stholo 	putc('\n', stderr);
8146ab05f83Stholo 	ep = ebuf;
8156ab05f83Stholo }
8166ab05f83Stholo 
8176ab05f83Stholo void bclass(int c)
8186ab05f83Stholo {
8196ab05f83Stholo 	switch (c) {
8206ab05f83Stholo 	case '{': bracecnt++; break;
8216ab05f83Stholo 	case '}': bracecnt--; break;
8226ab05f83Stholo 	case '[': brackcnt++; break;
8236ab05f83Stholo 	case ']': brackcnt--; break;
8246ab05f83Stholo 	case '(': parencnt++; break;
8256ab05f83Stholo 	case ')': parencnt--; break;
8266ab05f83Stholo 	}
8276ab05f83Stholo }
8286ab05f83Stholo 
8299a69093aSmillert double errcheck(double x, const char *s)
8306ab05f83Stholo {
8316ab05f83Stholo 
8326ab05f83Stholo 	if (errno == EDOM) {
8336ab05f83Stholo 		errno = 0;
8347b11b857Smillert 		WARNING("%s argument out of domain", s);
8356ab05f83Stholo 		x = 1;
8366ab05f83Stholo 	} else if (errno == ERANGE) {
8376ab05f83Stholo 		errno = 0;
8387b11b857Smillert 		WARNING("%s result out of range", s);
8396ab05f83Stholo 		x = 1;
8406ab05f83Stholo 	}
8416ab05f83Stholo 	return x;
8426ab05f83Stholo }
8436ab05f83Stholo 
8449a69093aSmillert int isclvar(const char *s)	/* is s of form var=something ? */
8456ab05f83Stholo {
8469a69093aSmillert 	const char *os = s;
8476ab05f83Stholo 
848a27f5228Smillert 	if (!isalpha((uschar)*s) && *s != '_')
8496ab05f83Stholo 		return 0;
8506ab05f83Stholo 	for ( ; *s; s++)
851a27f5228Smillert 		if (!(isalnum((uschar)*s) || *s == '_'))
8526ab05f83Stholo 			break;
853203f9af3Smillert 	return *s == '=' && s > os;
8546ab05f83Stholo }
8556ab05f83Stholo 
85607edfa4aSkstailey /* strtod is supposed to be a proper test of what's a valid number */
857a27f5228Smillert /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
858a27f5228Smillert /* wrong: violates 4.10.1.4 of ansi C standard */
859483fa115Smillert 
8600b359418Smillert /* well, not quite. As of C99, hex floating point is allowed. so this is
861483fa115Smillert  * a bit of a mess. We work around the mess by checking for a hexadecimal
862483fa115Smillert  * value and disallowing it. Similarly, we now follow gawk and allow only
863483fa115Smillert  * +nan, -nan, +inf, and -inf for NaN and infinity values.
8640b359418Smillert  */
8656ab05f83Stholo 
866483fa115Smillert /*
867483fa115Smillert  * This routine now has a more complicated interface, the main point
868483fa115Smillert  * being to avoid the double conversion of a string to double, and
869483fa115Smillert  * also to convey out, if requested, the information that the numeric
870483fa115Smillert  * value was a leading string or is all of the string. The latter bit
871483fa115Smillert  * is used in getfval().
872483fa115Smillert  */
873483fa115Smillert 
874483fa115Smillert bool is_valid_number(const char *s, bool trailing_stuff_ok,
875483fa115Smillert 			bool *no_trailing, double *result)
8766ab05f83Stholo {
8776ab05f83Stholo 	double r;
8786ab05f83Stholo 	char *ep;
879483fa115Smillert 	bool retval = false;
8806de80fb8Smillert 	bool is_nan = false;
8816de80fb8Smillert 	bool is_inf = false;
882483fa115Smillert 
883483fa115Smillert 	if (no_trailing)
884483fa115Smillert 		*no_trailing = false;
885483fa115Smillert 
88664161c95Smillert 	while (isspace((uschar)*s))
887483fa115Smillert 		s++;
888483fa115Smillert 
889a886e62eSmillert 	/* no hex floating point, sorry */
8906de80fb8Smillert 	if (s[0] == '0' && tolower((uschar)s[1]) == 'x')
891483fa115Smillert 		return false;
892483fa115Smillert 
893a886e62eSmillert 	/* allow +nan, -nan, +inf, -inf, any other letter, no */
894483fa115Smillert 	if (s[0] == '+' || s[0] == '-') {
8956de80fb8Smillert 		is_nan = (strncasecmp(s+1, "nan", 3) == 0);
8966de80fb8Smillert 		is_inf = (strncasecmp(s+1, "inf", 3) == 0);
8976de80fb8Smillert 		if ((is_nan || is_inf)
8986de80fb8Smillert 		    && (isspace((uschar)s[4]) || s[4] == '\0'))
8996de80fb8Smillert 			goto convert;
9006de80fb8Smillert 		else if (! isdigit((uschar)s[1]) && s[1] != '.')
9016de80fb8Smillert 			return false;
902483fa115Smillert 	}
9036de80fb8Smillert 	else if (! isdigit((uschar)s[0]) && s[0] != '.')
904483fa115Smillert 		return false;
905483fa115Smillert 
9066de80fb8Smillert convert:
9076ab05f83Stholo 	errno = 0;
9086ab05f83Stholo 	r = strtod(s, &ep);
90964161c95Smillert 	if (ep == s || errno == ERANGE)
910483fa115Smillert 		return false;
911483fa115Smillert 
9126de80fb8Smillert 	if (isnan(r) && s[0] == '-' && signbit(r) == 0)
9136de80fb8Smillert 		r = -r;
9146de80fb8Smillert 
915483fa115Smillert 	if (result != NULL)
916483fa115Smillert 		*result = r;
917483fa115Smillert 
918f9297e05Smillert 	/*
919f9297e05Smillert 	 * check for trailing stuff
920f9297e05Smillert 	 */
921f9297e05Smillert 	while (isspace((uschar)*ep))
922f9297e05Smillert 		ep++;
923483fa115Smillert 
9246de80fb8Smillert 	if (no_trailing != NULL)
925483fa115Smillert 		*no_trailing = (*ep == '\0');
926483fa115Smillert 
927a886e62eSmillert 	/* return true if found the end, or trailing stuff is allowed */
928f9297e05Smillert 	retval = *ep == '\0' || trailing_stuff_ok;
929f9297e05Smillert 
930483fa115Smillert 	return retval;
9316ab05f83Stholo }
932