xref: /freebsd-src/contrib/one-true-awk/lib.c (revision 8d457988a72487b35ee3922671775d73169339e3)
12a55deb1SDavid E. O'Brien /****************************************************************
22a55deb1SDavid E. O'Brien Copyright (C) Lucent Technologies 1997
32a55deb1SDavid E. O'Brien All Rights Reserved
42a55deb1SDavid E. O'Brien 
52a55deb1SDavid E. O'Brien Permission to use, copy, modify, and distribute this software and
62a55deb1SDavid E. O'Brien its documentation for any purpose and without fee is hereby
72a55deb1SDavid E. O'Brien granted, provided that the above copyright notice appear in all
82a55deb1SDavid E. O'Brien copies and that both that the copyright notice and this
92a55deb1SDavid E. O'Brien permission notice and warranty disclaimer appear in supporting
102a55deb1SDavid E. O'Brien documentation, and that the name Lucent Technologies or any of
112a55deb1SDavid E. O'Brien its entities not be used in advertising or publicity pertaining
122a55deb1SDavid E. O'Brien to distribution of the software without specific, written prior
132a55deb1SDavid E. O'Brien permission.
142a55deb1SDavid E. O'Brien 
152a55deb1SDavid E. O'Brien LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
162a55deb1SDavid E. O'Brien INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
172a55deb1SDavid E. O'Brien IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
182a55deb1SDavid E. O'Brien SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
192a55deb1SDavid E. O'Brien WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
202a55deb1SDavid E. O'Brien IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
212a55deb1SDavid E. O'Brien ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
222a55deb1SDavid E. O'Brien THIS SOFTWARE.
232a55deb1SDavid E. O'Brien ****************************************************************/
242a55deb1SDavid E. O'Brien 
252a55deb1SDavid E. O'Brien #define DEBUG
262a55deb1SDavid E. O'Brien #include <stdio.h>
272a55deb1SDavid E. O'Brien #include <string.h>
28f39dd6a9SWarner Losh #include <strings.h>
292a55deb1SDavid E. O'Brien #include <ctype.h>
302a55deb1SDavid E. O'Brien #include <errno.h>
312a55deb1SDavid E. O'Brien #include <stdlib.h>
322a55deb1SDavid E. O'Brien #include <stdarg.h>
33f39dd6a9SWarner Losh #include <limits.h>
34f39dd6a9SWarner Losh #include <math.h>
352a55deb1SDavid E. O'Brien #include "awk.h"
362a55deb1SDavid E. O'Brien 
37f32a6403SWarner Losh extern int u8_nextlen(const char *s);
38f32a6403SWarner Losh 
39f39dd6a9SWarner Losh char	EMPTY[] = { '\0' };
402a55deb1SDavid E. O'Brien FILE	*infile	= NULL;
41f39dd6a9SWarner Losh bool	innew;		/* true = infile has not been read by readrec */
42f39dd6a9SWarner Losh char	*file	= EMPTY;
432a55deb1SDavid E. O'Brien char	*record;
442a55deb1SDavid E. O'Brien int	recsize	= RECSIZE;
452a55deb1SDavid E. O'Brien char	*fields;
462a55deb1SDavid E. O'Brien int	fieldssize = RECSIZE;
472a55deb1SDavid E. O'Brien 
482a55deb1SDavid E. O'Brien Cell	**fldtab;	/* pointers to Cells */
49f39dd6a9SWarner Losh static size_t	len_inputFS = 0;
50f39dd6a9SWarner Losh static char	*inputFS = NULL; /* FS at time of input, for field splitting */
512a55deb1SDavid E. O'Brien 
52addad6afSRong-En Fan #define	MAXFLD	2
532a55deb1SDavid E. O'Brien int	nfields	= MAXFLD;	/* last allocated slot for $i */
542a55deb1SDavid E. O'Brien 
55f39dd6a9SWarner Losh bool	donefld;	/* true = implies rec broken into fields */
56f39dd6a9SWarner Losh bool	donerec;	/* true = record is valid (no flds have changed) */
572a55deb1SDavid E. O'Brien 
582a55deb1SDavid E. O'Brien int	lastfld	= 0;	/* last used field */
592a55deb1SDavid E. O'Brien int	argno	= 1;	/* current input argument number */
602a55deb1SDavid E. O'Brien extern	Awkfloat *ARGC;
612a55deb1SDavid E. O'Brien 
62f39dd6a9SWarner Losh static Cell dollar0 = { OCELL, CFLD, NULL, EMPTY, 0.0, REC|STR|DONTFREE, NULL, NULL };
63f39dd6a9SWarner Losh static Cell dollar1 = { OCELL, CFLD, NULL, EMPTY, 0.0, FLD|STR|DONTFREE, NULL, NULL };
642a55deb1SDavid E. O'Brien 
652a55deb1SDavid E. O'Brien void recinit(unsigned int n)
662a55deb1SDavid E. O'Brien {
67c263f9bfSRuslan Ermilov 	if ( (record = (char *) malloc(n)) == NULL
68d2f6e492SDavid E. O'Brien 	  || (fields = (char *) malloc(n+1)) == NULL
69f39dd6a9SWarner Losh 	  || (fldtab = (Cell **) calloc(nfields+2, sizeof(*fldtab))) == NULL
70f39dd6a9SWarner Losh 	  || (fldtab[0] = (Cell *) malloc(sizeof(**fldtab))) == NULL)
712a55deb1SDavid E. O'Brien 		FATAL("out of space for $0 and fields");
72547f34caSWarner Losh 	*record = '\0';
732a55deb1SDavid E. O'Brien 	*fldtab[0] = dollar0;
742a55deb1SDavid E. O'Brien 	fldtab[0]->sval = record;
752a55deb1SDavid E. O'Brien 	fldtab[0]->nval = tostring("0");
762a55deb1SDavid E. O'Brien 	makefields(1, nfields);
772a55deb1SDavid E. O'Brien }
782a55deb1SDavid E. O'Brien 
792a55deb1SDavid E. O'Brien void makefields(int n1, int n2)		/* create $n1..$n2 inclusive */
802a55deb1SDavid E. O'Brien {
812a55deb1SDavid E. O'Brien 	char temp[50];
822a55deb1SDavid E. O'Brien 	int i;
832a55deb1SDavid E. O'Brien 
842a55deb1SDavid E. O'Brien 	for (i = n1; i <= n2; i++) {
85f39dd6a9SWarner Losh 		fldtab[i] = (Cell *) malloc(sizeof(**fldtab));
862a55deb1SDavid E. O'Brien 		if (fldtab[i] == NULL)
872a55deb1SDavid E. O'Brien 			FATAL("out of space in makefields %d", i);
882a55deb1SDavid E. O'Brien 		*fldtab[i] = dollar1;
89f39dd6a9SWarner Losh 		snprintf(temp, sizeof(temp), "%d", i);
902a55deb1SDavid E. O'Brien 		fldtab[i]->nval = tostring(temp);
912a55deb1SDavid E. O'Brien 	}
922a55deb1SDavid E. O'Brien }
932a55deb1SDavid E. O'Brien 
942a55deb1SDavid E. O'Brien void initgetrec(void)
952a55deb1SDavid E. O'Brien {
962a55deb1SDavid E. O'Brien 	int i;
972a55deb1SDavid E. O'Brien 	char *p;
982a55deb1SDavid E. O'Brien 
992a55deb1SDavid E. O'Brien 	for (i = 1; i < *ARGC; i++) {
100aa0da2e4SRuslan Ermilov 		p = getargv(i); /* find 1st real filename */
101aa0da2e4SRuslan Ermilov 		if (p == NULL || *p == '\0') {  /* deleted or zapped */
102aa0da2e4SRuslan Ermilov 			argno++;
103aa0da2e4SRuslan Ermilov 			continue;
104aa0da2e4SRuslan Ermilov 		}
105aa0da2e4SRuslan Ermilov 		if (!isclvar(p)) {
106aa0da2e4SRuslan Ermilov 			setsval(lookup("FILENAME", symtab), p);
1072a55deb1SDavid E. O'Brien 			return;
1082a55deb1SDavid E. O'Brien 		}
1092a55deb1SDavid E. O'Brien 		setclvar(p);	/* a commandline assignment before filename */
1102a55deb1SDavid E. O'Brien 		argno++;
1112a55deb1SDavid E. O'Brien 	}
1122a55deb1SDavid E. O'Brien 	infile = stdin;		/* no filenames, so use stdin */
113f39dd6a9SWarner Losh 	innew = true;
1142a55deb1SDavid E. O'Brien }
1152a55deb1SDavid E. O'Brien 
116f39dd6a9SWarner Losh /*
117f39dd6a9SWarner Losh  * POSIX specifies that fields are supposed to be evaluated as if they were
118f39dd6a9SWarner Losh  * split using the value of FS at the time that the record's value ($0) was
119f39dd6a9SWarner Losh  * read.
120f39dd6a9SWarner Losh  *
121f39dd6a9SWarner Losh  * Since field-splitting is done lazily, we save the current value of FS
122f39dd6a9SWarner Losh  * whenever a new record is read in (implicitly or via getline), or when
123f39dd6a9SWarner Losh  * a new value is assigned to $0.
124f39dd6a9SWarner Losh  */
125f39dd6a9SWarner Losh void savefs(void)
126f39dd6a9SWarner Losh {
127f39dd6a9SWarner Losh 	size_t len;
128f39dd6a9SWarner Losh 	if ((len = strlen(getsval(fsloc))) < len_inputFS) {
129f39dd6a9SWarner Losh 		strcpy(inputFS, *FS);	/* for subsequent field splitting */
130f39dd6a9SWarner Losh 		return;
131f39dd6a9SWarner Losh 	}
132c263f9bfSRuslan Ermilov 
133f39dd6a9SWarner Losh 	len_inputFS = len + 1;
134f39dd6a9SWarner Losh 	inputFS = (char *) realloc(inputFS, len_inputFS);
135f39dd6a9SWarner Losh 	if (inputFS == NULL)
136f39dd6a9SWarner Losh 		FATAL("field separator %.10s... is too long", *FS);
137f39dd6a9SWarner Losh 	memcpy(inputFS, *FS, len_inputFS);
138f39dd6a9SWarner Losh }
139f39dd6a9SWarner Losh 
140f39dd6a9SWarner Losh static bool firsttime = true;
141f39dd6a9SWarner Losh 
142f39dd6a9SWarner Losh int getrec(char **pbuf, int *pbufsize, bool isrecord)	/* get next input record */
1432a55deb1SDavid E. O'Brien {			/* note: cares whether buf == record */
1442a55deb1SDavid E. O'Brien 	int c;
1452a55deb1SDavid E. O'Brien 	char *buf = *pbuf;
146c263f9bfSRuslan Ermilov 	uschar saveb0;
147c263f9bfSRuslan Ermilov 	int bufsize = *pbufsize, savebufsize = bufsize;
1482a55deb1SDavid E. O'Brien 
1492a55deb1SDavid E. O'Brien 	if (firsttime) {
150f39dd6a9SWarner Losh 		firsttime = false;
1512a55deb1SDavid E. O'Brien 		initgetrec();
1522a55deb1SDavid E. O'Brien 	}
153f39dd6a9SWarner Losh 	DPRINTF("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n",
154f39dd6a9SWarner Losh 		*RS, *FS, *ARGC, *FILENAME);
155c263f9bfSRuslan Ermilov 	saveb0 = buf[0];
1562a55deb1SDavid E. O'Brien 	buf[0] = 0;
1572a55deb1SDavid E. O'Brien 	while (argno < *ARGC || infile == stdin) {
158f39dd6a9SWarner Losh 		DPRINTF("argno=%d, file=|%s|\n", argno, file);
1592a55deb1SDavid E. O'Brien 		if (infile == NULL) {	/* have to open a new file */
1602a55deb1SDavid E. O'Brien 			file = getargv(argno);
161d86a0988SRuslan Ermilov 			if (file == NULL || *file == '\0') {	/* deleted or zapped */
1622a55deb1SDavid E. O'Brien 				argno++;
1632a55deb1SDavid E. O'Brien 				continue;
1642a55deb1SDavid E. O'Brien 			}
1652a55deb1SDavid E. O'Brien 			if (isclvar(file)) {	/* a var=value arg */
1662a55deb1SDavid E. O'Brien 				setclvar(file);
1672a55deb1SDavid E. O'Brien 				argno++;
1682a55deb1SDavid E. O'Brien 				continue;
1692a55deb1SDavid E. O'Brien 			}
1702a55deb1SDavid E. O'Brien 			*FILENAME = file;
171f39dd6a9SWarner Losh 			DPRINTF("opening file %s\n", file);
1722a55deb1SDavid E. O'Brien 			if (*file == '-' && *(file+1) == '\0')
1732a55deb1SDavid E. O'Brien 				infile = stdin;
1742a55deb1SDavid E. O'Brien 			else if ((infile = fopen(file, "r")) == NULL)
1752a55deb1SDavid E. O'Brien 				FATAL("can't open file %s", file);
17623f24377SWarner Losh 			innew = true;
1772a55deb1SDavid E. O'Brien 			setfval(fnrloc, 0.0);
1782a55deb1SDavid E. O'Brien 		}
179f39dd6a9SWarner Losh 		c = readrec(&buf, &bufsize, infile, innew);
180f39dd6a9SWarner Losh 		if (innew)
181f39dd6a9SWarner Losh 			innew = false;
1822a55deb1SDavid E. O'Brien 		if (c != 0 || buf[0] != '\0') {	/* normal record */
1832a55deb1SDavid E. O'Brien 			if (isrecord) {
184f39dd6a9SWarner Losh 				double result;
185f39dd6a9SWarner Losh 
1862a55deb1SDavid E. O'Brien 				if (freeable(fldtab[0]))
1872a55deb1SDavid E. O'Brien 					xfree(fldtab[0]->sval);
1882a55deb1SDavid E. O'Brien 				fldtab[0]->sval = buf;	/* buf == record */
1892a55deb1SDavid E. O'Brien 				fldtab[0]->tval = REC | STR | DONTFREE;
190f39dd6a9SWarner Losh 				if (is_number(fldtab[0]->sval, & result)) {
191f39dd6a9SWarner Losh 					fldtab[0]->fval = result;
1922a55deb1SDavid E. O'Brien 					fldtab[0]->tval |= NUM;
1932a55deb1SDavid E. O'Brien 				}
194f32a6403SWarner Losh 				donefld = false;
195f32a6403SWarner Losh 				donerec = true;
196f32a6403SWarner Losh 				savefs();
1972a55deb1SDavid E. O'Brien 			}
1982a55deb1SDavid E. O'Brien 			setfval(nrloc, nrloc->fval+1);
1992a55deb1SDavid E. O'Brien 			setfval(fnrloc, fnrloc->fval+1);
2002a55deb1SDavid E. O'Brien 			*pbuf = buf;
2012a55deb1SDavid E. O'Brien 			*pbufsize = bufsize;
2022a55deb1SDavid E. O'Brien 			return 1;
2032a55deb1SDavid E. O'Brien 		}
2042a55deb1SDavid E. O'Brien 		/* EOF arrived on this file; set up next */
2052a55deb1SDavid E. O'Brien 		if (infile != stdin)
2062a55deb1SDavid E. O'Brien 			fclose(infile);
2072a55deb1SDavid E. O'Brien 		infile = NULL;
2082a55deb1SDavid E. O'Brien 		argno++;
2092a55deb1SDavid E. O'Brien 	}
210c263f9bfSRuslan Ermilov 	buf[0] = saveb0;
2112a55deb1SDavid E. O'Brien 	*pbuf = buf;
212c263f9bfSRuslan Ermilov 	*pbufsize = savebufsize;
2132a55deb1SDavid E. O'Brien 	return 0;	/* true end of file */
2142a55deb1SDavid E. O'Brien }
2152a55deb1SDavid E. O'Brien 
2162a55deb1SDavid E. O'Brien void nextfile(void)
2172a55deb1SDavid E. O'Brien {
218addad6afSRong-En Fan 	if (infile != NULL && infile != stdin)
2192a55deb1SDavid E. O'Brien 		fclose(infile);
2202a55deb1SDavid E. O'Brien 	infile = NULL;
2212a55deb1SDavid E. O'Brien 	argno++;
2222a55deb1SDavid E. O'Brien }
2232a55deb1SDavid E. O'Brien 
224f32a6403SWarner Losh extern int readcsvrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag);
225f32a6403SWarner Losh 
226f39dd6a9SWarner Losh int readrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag)	/* read one record into buf */
2272a55deb1SDavid E. O'Brien {
228f32a6403SWarner Losh 	int sep, c, isrec; // POTENTIAL BUG? isrec is a macro in awk.h
229f32a6403SWarner Losh 	char *rr = *pbuf, *buf = *pbuf;
2302a55deb1SDavid E. O'Brien 	int bufsize = *pbufsize;
231b5253557SWarner Losh 	char *rs = getsval(rsloc);
2322a55deb1SDavid E. O'Brien 
233f32a6403SWarner Losh 	if (CSV) {
234*8d457988SWarner Losh 		c = readcsvrec(&buf, &bufsize, inf, newflag);
235f32a6403SWarner Losh 		isrec = (c == EOF && rr == buf) ? false : true;
236f32a6403SWarner Losh 	} else if (*rs && rs[1]) {
237f39dd6a9SWarner Losh 		bool found;
238f39dd6a9SWarner Losh 
239f32a6403SWarner Losh 		memset(buf, 0, bufsize);
240f39dd6a9SWarner Losh 		fa *pfa = makedfa(rs, 1);
241f39dd6a9SWarner Losh 		if (newflag)
242f39dd6a9SWarner Losh 			found = fnematch(pfa, inf, &buf, &bufsize, recsize);
243f39dd6a9SWarner Losh 		else {
244f39dd6a9SWarner Losh 			int tempstat = pfa->initstat;
245f39dd6a9SWarner Losh 			pfa->initstat = 2;
246f39dd6a9SWarner Losh 			found = fnematch(pfa, inf, &buf, &bufsize, recsize);
247f39dd6a9SWarner Losh 			pfa->initstat = tempstat;
248f39dd6a9SWarner Losh 		}
249f39dd6a9SWarner Losh 		if (found)
250f39dd6a9SWarner Losh 			setptr(patbeg, '\0');
25123f24377SWarner Losh 		isrec = (found == 0 && *buf == '\0') ? false : true;
252f32a6403SWarner Losh 
253f39dd6a9SWarner Losh 	} else {
254b5253557SWarner Losh 		if ((sep = *rs) == 0) {
2552a55deb1SDavid E. O'Brien 			sep = '\n';
2562a55deb1SDavid E. O'Brien 			while ((c=getc(inf)) == '\n' && c != EOF)	/* skip leading \n's */
2572a55deb1SDavid E. O'Brien 				;
2582a55deb1SDavid E. O'Brien 			if (c != EOF)
2592a55deb1SDavid E. O'Brien 				ungetc(c, inf);
2602a55deb1SDavid E. O'Brien 		}
2612a55deb1SDavid E. O'Brien 		for (rr = buf; ; ) {
2622a55deb1SDavid E. O'Brien 			for (; (c=getc(inf)) != sep && c != EOF; ) {
2632a55deb1SDavid E. O'Brien 				if (rr-buf+1 > bufsize)
264f39dd6a9SWarner Losh 					if (!adjbuf(&buf, &bufsize, 1+rr-buf,
265f39dd6a9SWarner Losh 					    recsize, &rr, "readrec 1"))
2662a55deb1SDavid E. O'Brien 						FATAL("input record `%.30s...' too long", buf);
2672a55deb1SDavid E. O'Brien 				*rr++ = c;
2682a55deb1SDavid E. O'Brien 			}
269b5253557SWarner Losh 			if (*rs == sep || c == EOF)
2702a55deb1SDavid E. O'Brien 				break;
2712a55deb1SDavid E. O'Brien 			if ((c = getc(inf)) == '\n' || c == EOF)	/* 2 in a row */
2722a55deb1SDavid E. O'Brien 				break;
273f39dd6a9SWarner Losh 			if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr,
274f39dd6a9SWarner Losh 			    "readrec 2"))
2752a55deb1SDavid E. O'Brien 				FATAL("input record `%.30s...' too long", buf);
2762a55deb1SDavid E. O'Brien 			*rr++ = '\n';
2772a55deb1SDavid E. O'Brien 			*rr++ = c;
2782a55deb1SDavid E. O'Brien 		}
2792a55deb1SDavid E. O'Brien 		if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
2802a55deb1SDavid E. O'Brien 			FATAL("input record `%.30s...' too long", buf);
2812a55deb1SDavid E. O'Brien 		*rr = 0;
28223f24377SWarner Losh 		isrec = (c == EOF && rr == buf) ? false : true;
283f39dd6a9SWarner Losh 	}
2842a55deb1SDavid E. O'Brien 	*pbuf = buf;
2852a55deb1SDavid E. O'Brien 	*pbufsize = bufsize;
286f39dd6a9SWarner Losh 	DPRINTF("readrec saw <%s>, returns %d\n", buf, isrec);
287f39dd6a9SWarner Losh 	return isrec;
2882a55deb1SDavid E. O'Brien }
2892a55deb1SDavid E. O'Brien 
290f32a6403SWarner Losh 
291f32a6403SWarner Losh /*******************
292f32a6403SWarner Losh  * loose ends here:
293f32a6403SWarner Losh  *   \r\n should become \n
294f32a6403SWarner Losh  *   what about bare \r?  Excel uses that for embedded newlines
295f32a6403SWarner Losh  *   can't have "" in unquoted fields, according to RFC 4180
296f32a6403SWarner Losh */
297f32a6403SWarner Losh 
298f32a6403SWarner Losh 
299f32a6403SWarner Losh int readcsvrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag) /* csv can have \n's */
300f32a6403SWarner Losh {			/* so read a complete record that might be multiple lines */
301f32a6403SWarner Losh 	int sep, c;
302f32a6403SWarner Losh 	char *rr = *pbuf, *buf = *pbuf;
303f32a6403SWarner Losh 	int bufsize = *pbufsize;
304f32a6403SWarner Losh 	bool in_quote = false;
305f32a6403SWarner Losh 
306f32a6403SWarner Losh 	sep = '\n'; /* the only separator; have to skip over \n embedded in "..." */
307f32a6403SWarner Losh 	rr = buf;
308f32a6403SWarner Losh 	while ((c = getc(inf)) != EOF) {
309f32a6403SWarner Losh 		if (c == sep) {
310f32a6403SWarner Losh 			if (! in_quote)
311f32a6403SWarner Losh 				break;
312f32a6403SWarner Losh 			if (rr > buf && rr[-1] == '\r')	// remove \r if was \r\n
313f32a6403SWarner Losh 				rr--;
314f32a6403SWarner Losh 		}
315f32a6403SWarner Losh 
316f32a6403SWarner Losh 		if (rr-buf+1 > bufsize)
317f32a6403SWarner Losh 			if (!adjbuf(&buf, &bufsize, 1+rr-buf,
318f32a6403SWarner Losh 			    recsize, &rr, "readcsvrec 1"))
319f32a6403SWarner Losh 				FATAL("input record `%.30s...' too long", buf);
320f32a6403SWarner Losh 		*rr++ = c;
321f32a6403SWarner Losh 		if (c == '"')
322f32a6403SWarner Losh 			in_quote = ! in_quote;
323f32a6403SWarner Losh  	}
324f32a6403SWarner Losh 	if (c == '\n' && rr > buf && rr[-1] == '\r') 	// remove \r if was \r\n
325f32a6403SWarner Losh 		rr--;
326f32a6403SWarner Losh 
327f32a6403SWarner Losh 	if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readcsvrec 4"))
328f32a6403SWarner Losh 		FATAL("input record `%.30s...' too long", buf);
329f32a6403SWarner Losh 	*rr = 0;
330f32a6403SWarner Losh 	*pbuf = buf;
331f32a6403SWarner Losh 	*pbufsize = bufsize;
332f32a6403SWarner Losh 	DPRINTF("readcsvrec saw <%s>, returns %d\n", buf, c);
333f32a6403SWarner Losh 	return c;
334f32a6403SWarner Losh }
335f32a6403SWarner Losh 
3362a55deb1SDavid E. O'Brien char *getargv(int n)	/* get ARGV[n] */
3372a55deb1SDavid E. O'Brien {
33817853db4SWarner Losh 	Array *ap;
3392a55deb1SDavid E. O'Brien 	Cell *x;
3402a55deb1SDavid E. O'Brien 	char *s, temp[50];
34117853db4SWarner Losh 	extern Cell *ARGVcell;
3422a55deb1SDavid E. O'Brien 
34317853db4SWarner Losh 	ap = (Array *)ARGVcell->sval;
344f39dd6a9SWarner Losh 	snprintf(temp, sizeof(temp), "%d", n);
34517853db4SWarner Losh 	if (lookup(temp, ap) == NULL)
346d86a0988SRuslan Ermilov 		return NULL;
34717853db4SWarner Losh 	x = setsymtab(temp, "", 0.0, STR, ap);
3482a55deb1SDavid E. O'Brien 	s = getsval(x);
349f39dd6a9SWarner Losh 	DPRINTF("getargv(%d) returns |%s|\n", n, s);
3502a55deb1SDavid E. O'Brien 	return s;
3512a55deb1SDavid E. O'Brien }
3522a55deb1SDavid E. O'Brien 
3532a55deb1SDavid E. O'Brien void setclvar(char *s)	/* set var=value from s */
3542a55deb1SDavid E. O'Brien {
355f32a6403SWarner Losh 	char *e, *p;
3562a55deb1SDavid E. O'Brien 	Cell *q;
357f39dd6a9SWarner Losh 	double result;
3582a55deb1SDavid E. O'Brien 
359f32a6403SWarner Losh /* commit f3d9187d4e0f02294fb1b0e31152070506314e67 broke T.argv test */
360f32a6403SWarner Losh /* I don't understand why it was changed. */
361f32a6403SWarner Losh 
3622a55deb1SDavid E. O'Brien 	for (p=s; *p != '='; p++)
3632a55deb1SDavid E. O'Brien 		;
364f32a6403SWarner Losh 	e = p;
3652a55deb1SDavid E. O'Brien 	*p++ = 0;
3662a55deb1SDavid E. O'Brien 	p = qstring(p, '\0');
3672a55deb1SDavid E. O'Brien 	q = setsymtab(s, p, 0.0, STR, symtab);
3682a55deb1SDavid E. O'Brien 	setsval(q, p);
369f39dd6a9SWarner Losh 	if (is_number(q->sval, & result)) {
370f39dd6a9SWarner Losh 		q->fval = result;
3712a55deb1SDavid E. O'Brien 		q->tval |= NUM;
3722a55deb1SDavid E. O'Brien 	}
373f39dd6a9SWarner Losh 	DPRINTF("command line set %s to |%s|\n", s, p);
374f32a6403SWarner Losh 	free(p);
375f32a6403SWarner Losh 	*e = '=';
3762a55deb1SDavid E. O'Brien }
3772a55deb1SDavid E. O'Brien 
3782a55deb1SDavid E. O'Brien 
3792a55deb1SDavid E. O'Brien void fldbld(void)	/* create fields from current record */
3802a55deb1SDavid E. O'Brien {
3812a55deb1SDavid E. O'Brien 	/* this relies on having fields[] the same length as $0 */
3822a55deb1SDavid E. O'Brien 	/* the fields are all stored in this one array with \0's */
3831b11b783SRuslan Ermilov 	/* possibly with a final trailing \0 not associated with any field */
3842a55deb1SDavid E. O'Brien 	char *r, *fr, sep;
3852a55deb1SDavid E. O'Brien 	Cell *p;
3862a55deb1SDavid E. O'Brien 	int i, j, n;
3872a55deb1SDavid E. O'Brien 
3882a55deb1SDavid E. O'Brien 	if (donefld)
3892a55deb1SDavid E. O'Brien 		return;
3902a55deb1SDavid E. O'Brien 	if (!isstr(fldtab[0]))
3912a55deb1SDavid E. O'Brien 		getsval(fldtab[0]);
3922a55deb1SDavid E. O'Brien 	r = fldtab[0]->sval;
3932a55deb1SDavid E. O'Brien 	n = strlen(r);
3942a55deb1SDavid E. O'Brien 	if (n > fieldssize) {
3952a55deb1SDavid E. O'Brien 		xfree(fields);
3961b11b783SRuslan Ermilov 		if ((fields = (char *) malloc(n+2)) == NULL) /* possibly 2 final \0s */
3972a55deb1SDavid E. O'Brien 			FATAL("out of space for fields in fldbld %d", n);
3982a55deb1SDavid E. O'Brien 		fieldssize = n;
3992a55deb1SDavid E. O'Brien 	}
4002a55deb1SDavid E. O'Brien 	fr = fields;
4012a55deb1SDavid E. O'Brien 	i = 0;	/* number of fields accumulated here */
402f39dd6a9SWarner Losh 	if (inputFS == NULL)	/* make sure we have a copy of FS */
403f39dd6a9SWarner Losh 		savefs();
404f32a6403SWarner Losh 	if (!CSV && strlen(inputFS) > 1) {	/* it's a regular expression */
4052a55deb1SDavid E. O'Brien 		i = refldbld(r, inputFS);
406f32a6403SWarner Losh 	} else if (!CSV && (sep = *inputFS) == ' ') {	/* default whitespace */
4072a55deb1SDavid E. O'Brien 		for (i = 0; ; ) {
4082a55deb1SDavid E. O'Brien 			while (*r == ' ' || *r == '\t' || *r == '\n')
4092a55deb1SDavid E. O'Brien 				r++;
4102a55deb1SDavid E. O'Brien 			if (*r == 0)
4112a55deb1SDavid E. O'Brien 				break;
4122a55deb1SDavid E. O'Brien 			i++;
4132a55deb1SDavid E. O'Brien 			if (i > nfields)
4142a55deb1SDavid E. O'Brien 				growfldtab(i);
4152a55deb1SDavid E. O'Brien 			if (freeable(fldtab[i]))
4162a55deb1SDavid E. O'Brien 				xfree(fldtab[i]->sval);
4172a55deb1SDavid E. O'Brien 			fldtab[i]->sval = fr;
4182a55deb1SDavid E. O'Brien 			fldtab[i]->tval = FLD | STR | DONTFREE;
4192a55deb1SDavid E. O'Brien 			do
4202a55deb1SDavid E. O'Brien 				*fr++ = *r++;
4212a55deb1SDavid E. O'Brien 			while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
4222a55deb1SDavid E. O'Brien 			*fr++ = 0;
4232a55deb1SDavid E. O'Brien 		}
4242a55deb1SDavid E. O'Brien 		*fr = 0;
425f32a6403SWarner Losh 	} else if (CSV) {	/* CSV processing.  no error handling */
426f32a6403SWarner Losh 		if (*r != 0) {
427f32a6403SWarner Losh 			for (;;) {
4283fd60a6bSWarner Losh 				i++;
4293fd60a6bSWarner Losh 				if (i > nfields)
4303fd60a6bSWarner Losh 					growfldtab(i);
4313fd60a6bSWarner Losh 				if (freeable(fldtab[i]))
4323fd60a6bSWarner Losh 					xfree(fldtab[i]->sval);
433f32a6403SWarner Losh 				fldtab[i]->sval = fr;
434f32a6403SWarner Losh 				fldtab[i]->tval = FLD | STR | DONTFREE;
435f32a6403SWarner Losh 				if (*r == '"' ) { /* start of "..." */
436f32a6403SWarner Losh 					for (r++ ; *r != '\0'; ) {
437f32a6403SWarner Losh 						if (*r == '"' && r[1] != '\0' && r[1] == '"') {
438f32a6403SWarner Losh 							r += 2; /* doubled quote */
439f32a6403SWarner Losh 							*fr++ = '"';
440f32a6403SWarner Losh 						} else if (*r == '"' && (r[1] == '\0' || r[1] == ',')) {
441f32a6403SWarner Losh 							r++; /* skip over closing quote */
442f32a6403SWarner Losh 							break;
443f32a6403SWarner Losh 						} else {
444f32a6403SWarner Losh 							*fr++ = *r++;
445f32a6403SWarner Losh 						}
446f32a6403SWarner Losh 					}
447f32a6403SWarner Losh 					*fr++ = 0;
448f32a6403SWarner Losh 				} else {	/* unquoted field */
449f32a6403SWarner Losh 					while (*r != ',' && *r != '\0')
450f32a6403SWarner Losh 						*fr++ = *r++;
451f32a6403SWarner Losh 					*fr++ = 0;
452f32a6403SWarner Losh 				}
453f32a6403SWarner Losh 				if (*r++ == 0)
454f32a6403SWarner Losh 					break;
455f32a6403SWarner Losh 
456f32a6403SWarner Losh 			}
457f32a6403SWarner Losh 		}
458f32a6403SWarner Losh 		*fr = 0;
459f32a6403SWarner Losh 	} else if ((sep = *inputFS) == 0) {	/* new: FS="" => 1 char/field */
460f32a6403SWarner Losh 		for (i = 0; *r != '\0'; ) {
461f32a6403SWarner Losh 			char buf[10];
462f32a6403SWarner Losh 			i++;
463f32a6403SWarner Losh 			if (i > nfields)
464f32a6403SWarner Losh 				growfldtab(i);
465f32a6403SWarner Losh 			if (freeable(fldtab[i]))
466f32a6403SWarner Losh 				xfree(fldtab[i]->sval);
467f32a6403SWarner Losh 			n = u8_nextlen(r);
468f32a6403SWarner Losh 			for (j = 0; j < n; j++)
469f32a6403SWarner Losh 				buf[j] = *r++;
470f32a6403SWarner Losh 			buf[j] = '\0';
4712a55deb1SDavid E. O'Brien 			fldtab[i]->sval = tostring(buf);
4722a55deb1SDavid E. O'Brien 			fldtab[i]->tval = FLD | STR;
4732a55deb1SDavid E. O'Brien 		}
4742a55deb1SDavid E. O'Brien 		*fr = 0;
4752a55deb1SDavid E. O'Brien 	} else if (*r != 0) {	/* if 0, it's a null field */
476fc6b1dfeSDavid E. O'Brien 		/* subtle case: if length(FS) == 1 && length(RS > 0)
477fc6b1dfeSDavid E. O'Brien 		 * \n is NOT a field separator (cf awk book 61,84).
478fc6b1dfeSDavid E. O'Brien 		 * this variable is tested in the inner while loop.
479fc6b1dfeSDavid E. O'Brien 		 */
480fc6b1dfeSDavid E. O'Brien 		int rtest = '\n';  /* normal case */
481fc6b1dfeSDavid E. O'Brien 		if (strlen(*RS) > 0)
482fc6b1dfeSDavid E. O'Brien 			rtest = '\0';
4832a55deb1SDavid E. O'Brien 		for (;;) {
4842a55deb1SDavid E. O'Brien 			i++;
4852a55deb1SDavid E. O'Brien 			if (i > nfields)
4862a55deb1SDavid E. O'Brien 				growfldtab(i);
4872a55deb1SDavid E. O'Brien 			if (freeable(fldtab[i]))
4882a55deb1SDavid E. O'Brien 				xfree(fldtab[i]->sval);
4892a55deb1SDavid E. O'Brien 			fldtab[i]->sval = fr;
4902a55deb1SDavid E. O'Brien 			fldtab[i]->tval = FLD | STR | DONTFREE;
491fc6b1dfeSDavid E. O'Brien 			while (*r != sep && *r != rtest && *r != '\0')	/* \n is always a separator */
4922a55deb1SDavid E. O'Brien 				*fr++ = *r++;
4932a55deb1SDavid E. O'Brien 			*fr++ = 0;
4942a55deb1SDavid E. O'Brien 			if (*r++ == 0)
4952a55deb1SDavid E. O'Brien 				break;
4962a55deb1SDavid E. O'Brien 		}
4972a55deb1SDavid E. O'Brien 		*fr = 0;
4982a55deb1SDavid E. O'Brien 	}
4992a55deb1SDavid E. O'Brien 	if (i > nfields)
5002a55deb1SDavid E. O'Brien 		FATAL("record `%.30s...' has too many fields; can't happen", r);
5012a55deb1SDavid E. O'Brien 	cleanfld(i+1, lastfld);	/* clean out junk from previous record */
5022a55deb1SDavid E. O'Brien 	lastfld = i;
503f39dd6a9SWarner Losh 	donefld = true;
5042a55deb1SDavid E. O'Brien 	for (j = 1; j <= lastfld; j++) {
505f39dd6a9SWarner Losh 		double result;
506f39dd6a9SWarner Losh 
5072a55deb1SDavid E. O'Brien 		p = fldtab[j];
508f39dd6a9SWarner Losh 		if(is_number(p->sval, & result)) {
509f39dd6a9SWarner Losh 			p->fval = result;
5102a55deb1SDavid E. O'Brien 			p->tval |= NUM;
5112a55deb1SDavid E. O'Brien 		}
5122a55deb1SDavid E. O'Brien 	}
5132a55deb1SDavid E. O'Brien 	setfval(nfloc, (Awkfloat) lastfld);
514f39dd6a9SWarner Losh 	donerec = true; /* restore */
5152a55deb1SDavid E. O'Brien 	if (dbg) {
5162a55deb1SDavid E. O'Brien 		for (j = 0; j <= lastfld; j++) {
5172a55deb1SDavid E. O'Brien 			p = fldtab[j];
5182a55deb1SDavid E. O'Brien 			printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
5192a55deb1SDavid E. O'Brien 		}
5202a55deb1SDavid E. O'Brien 	}
5212a55deb1SDavid E. O'Brien }
5222a55deb1SDavid E. O'Brien 
5232a55deb1SDavid E. O'Brien void cleanfld(int n1, int n2)	/* clean out fields n1 .. n2 inclusive */
5242a55deb1SDavid E. O'Brien {				/* nvals remain intact */
5252a55deb1SDavid E. O'Brien 	Cell *p;
5262a55deb1SDavid E. O'Brien 	int i;
5272a55deb1SDavid E. O'Brien 
5282a55deb1SDavid E. O'Brien 	for (i = n1; i <= n2; i++) {
5292a55deb1SDavid E. O'Brien 		p = fldtab[i];
5302a55deb1SDavid E. O'Brien 		if (freeable(p))
5312a55deb1SDavid E. O'Brien 			xfree(p->sval);
532f39dd6a9SWarner Losh 		p->sval = EMPTY,
5332a55deb1SDavid E. O'Brien 		p->tval = FLD | STR | DONTFREE;
5342a55deb1SDavid E. O'Brien 	}
5352a55deb1SDavid E. O'Brien }
5362a55deb1SDavid E. O'Brien 
5372a55deb1SDavid E. O'Brien void newfld(int n)	/* add field n after end of existing lastfld */
5382a55deb1SDavid E. O'Brien {
5392a55deb1SDavid E. O'Brien 	if (n > nfields)
5402a55deb1SDavid E. O'Brien 		growfldtab(n);
5412a55deb1SDavid E. O'Brien 	cleanfld(lastfld+1, n);
5422a55deb1SDavid E. O'Brien 	lastfld = n;
5432a55deb1SDavid E. O'Brien 	setfval(nfloc, (Awkfloat) n);
5442a55deb1SDavid E. O'Brien }
5452a55deb1SDavid E. O'Brien 
546b5253557SWarner Losh void setlastfld(int n)	/* set lastfld cleaning fldtab cells if necessary */
547b5253557SWarner Losh {
548b5253557SWarner Losh 	if (n < 0)
549b5253557SWarner Losh 		FATAL("cannot set NF to a negative value");
550b5253557SWarner Losh 	if (n > nfields)
551b5253557SWarner Losh 		growfldtab(n);
552b5253557SWarner Losh 
553b5253557SWarner Losh 	if (lastfld < n)
554b5253557SWarner Losh 	    cleanfld(lastfld+1, n);
555b5253557SWarner Losh 	else
556b5253557SWarner Losh 	    cleanfld(n+1, lastfld);
557b5253557SWarner Losh 
558b5253557SWarner Losh 	lastfld = n;
559b5253557SWarner Losh }
560b5253557SWarner Losh 
5612a55deb1SDavid E. O'Brien Cell *fieldadr(int n)	/* get nth field */
5622a55deb1SDavid E. O'Brien {
5632a55deb1SDavid E. O'Brien 	if (n < 0)
564c263f9bfSRuslan Ermilov 		FATAL("trying to access out of range field %d", n);
5652a55deb1SDavid E. O'Brien 	if (n > nfields)	/* fields after NF are empty */
5662a55deb1SDavid E. O'Brien 		growfldtab(n);	/* but does not increase NF */
5672a55deb1SDavid E. O'Brien 	return(fldtab[n]);
5682a55deb1SDavid E. O'Brien }
5692a55deb1SDavid E. O'Brien 
5702a55deb1SDavid E. O'Brien void growfldtab(int n)	/* make new fields up to at least $n */
5712a55deb1SDavid E. O'Brien {
5722a55deb1SDavid E. O'Brien 	int nf = 2 * nfields;
573c263f9bfSRuslan Ermilov 	size_t s;
5742a55deb1SDavid E. O'Brien 
5752a55deb1SDavid E. O'Brien 	if (n > nf)
5762a55deb1SDavid E. O'Brien 		nf = n;
577c263f9bfSRuslan Ermilov 	s = (nf+1) * (sizeof (struct Cell *));  /* freebsd: how much do we need? */
578f39dd6a9SWarner Losh 	if (s / sizeof(struct Cell *) - 1 == (size_t)nf) /* didn't overflow */
579c263f9bfSRuslan Ermilov 		fldtab = (Cell **) realloc(fldtab, s);
580c263f9bfSRuslan Ermilov 	else					/* overflow sizeof int */
581c263f9bfSRuslan Ermilov 		xfree(fldtab);	/* make it null */
5822a55deb1SDavid E. O'Brien 	if (fldtab == NULL)
5832a55deb1SDavid E. O'Brien 		FATAL("out of space creating %d fields", nf);
5842a55deb1SDavid E. O'Brien 	makefields(nfields+1, nf);
5852a55deb1SDavid E. O'Brien 	nfields = nf;
5862a55deb1SDavid E. O'Brien }
5872a55deb1SDavid E. O'Brien 
588813da98dSDavid E. O'Brien int refldbld(const char *rec, const char *fs)	/* build fields from reg expr in FS */
5892a55deb1SDavid E. O'Brien {
5902a55deb1SDavid E. O'Brien 	/* this relies on having fields[] the same length as $0 */
5912a55deb1SDavid E. O'Brien 	/* the fields are all stored in this one array with \0's */
5922a55deb1SDavid E. O'Brien 	char *fr;
5932a55deb1SDavid E. O'Brien 	int i, tempstat, n;
5942a55deb1SDavid E. O'Brien 	fa *pfa;
5952a55deb1SDavid E. O'Brien 
5962a55deb1SDavid E. O'Brien 	n = strlen(rec);
5972a55deb1SDavid E. O'Brien 	if (n > fieldssize) {
5982a55deb1SDavid E. O'Brien 		xfree(fields);
5992a55deb1SDavid E. O'Brien 		if ((fields = (char *) malloc(n+1)) == NULL)
6002a55deb1SDavid E. O'Brien 			FATAL("out of space for fields in refldbld %d", n);
6012a55deb1SDavid E. O'Brien 		fieldssize = n;
6022a55deb1SDavid E. O'Brien 	}
6032a55deb1SDavid E. O'Brien 	fr = fields;
6042a55deb1SDavid E. O'Brien 	*fr = '\0';
6052a55deb1SDavid E. O'Brien 	if (*rec == '\0')
6062a55deb1SDavid E. O'Brien 		return 0;
6072a55deb1SDavid E. O'Brien 	pfa = makedfa(fs, 1);
608f39dd6a9SWarner Losh 	DPRINTF("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs);
6092a55deb1SDavid E. O'Brien 	tempstat = pfa->initstat;
6102a55deb1SDavid E. O'Brien 	for (i = 1; ; i++) {
6112a55deb1SDavid E. O'Brien 		if (i > nfields)
6122a55deb1SDavid E. O'Brien 			growfldtab(i);
6132a55deb1SDavid E. O'Brien 		if (freeable(fldtab[i]))
6142a55deb1SDavid E. O'Brien 			xfree(fldtab[i]->sval);
6152a55deb1SDavid E. O'Brien 		fldtab[i]->tval = FLD | STR | DONTFREE;
6162a55deb1SDavid E. O'Brien 		fldtab[i]->sval = fr;
617f39dd6a9SWarner Losh 		DPRINTF("refldbld: i=%d\n", i);
6182a55deb1SDavid E. O'Brien 		if (nematch(pfa, rec)) {
6192a55deb1SDavid E. O'Brien 			pfa->initstat = 2;	/* horrible coupling to b.c */
620f39dd6a9SWarner Losh 			DPRINTF("match %s (%d chars)\n", patbeg, patlen);
6212a55deb1SDavid E. O'Brien 			strncpy(fr, rec, patbeg-rec);
6222a55deb1SDavid E. O'Brien 			fr += patbeg - rec + 1;
6232a55deb1SDavid E. O'Brien 			*(fr-1) = '\0';
6242a55deb1SDavid E. O'Brien 			rec = patbeg + patlen;
6252a55deb1SDavid E. O'Brien 		} else {
626f39dd6a9SWarner Losh 			DPRINTF("no match %s\n", rec);
6272a55deb1SDavid E. O'Brien 			strcpy(fr, rec);
6282a55deb1SDavid E. O'Brien 			pfa->initstat = tempstat;
6292a55deb1SDavid E. O'Brien 			break;
6302a55deb1SDavid E. O'Brien 		}
6312a55deb1SDavid E. O'Brien 	}
6322a55deb1SDavid E. O'Brien 	return i;
6332a55deb1SDavid E. O'Brien }
6342a55deb1SDavid E. O'Brien 
6352a55deb1SDavid E. O'Brien void recbld(void)	/* create $0 from $1..$NF if necessary */
6362a55deb1SDavid E. O'Brien {
6372a55deb1SDavid E. O'Brien 	int i;
6382a55deb1SDavid E. O'Brien 	char *r, *p;
639b5253557SWarner Losh 	char *sep = getsval(ofsloc);
6402a55deb1SDavid E. O'Brien 
641f39dd6a9SWarner Losh 	if (donerec)
6422a55deb1SDavid E. O'Brien 		return;
6432a55deb1SDavid E. O'Brien 	r = record;
6442a55deb1SDavid E. O'Brien 	for (i = 1; i <= *NF; i++) {
6452a55deb1SDavid E. O'Brien 		p = getsval(fldtab[i]);
6462a55deb1SDavid E. O'Brien 		if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
6472a55deb1SDavid E. O'Brien 			FATAL("created $0 `%.30s...' too long", record);
6482a55deb1SDavid E. O'Brien 		while ((*r = *p++) != 0)
6492a55deb1SDavid E. O'Brien 			r++;
6502a55deb1SDavid E. O'Brien 		if (i < *NF) {
651b5253557SWarner Losh 			if (!adjbuf(&record, &recsize, 2+strlen(sep)+r-record, recsize, &r, "recbld 2"))
6522a55deb1SDavid E. O'Brien 				FATAL("created $0 `%.30s...' too long", record);
653b5253557SWarner Losh 			for (p = sep; (*r = *p++) != 0; )
6542a55deb1SDavid E. O'Brien 				r++;
6552a55deb1SDavid E. O'Brien 		}
6562a55deb1SDavid E. O'Brien 	}
6572a55deb1SDavid E. O'Brien 	if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
6582a55deb1SDavid E. O'Brien 		FATAL("built giant record `%.30s...'", record);
6592a55deb1SDavid E. O'Brien 	*r = '\0';
660f39dd6a9SWarner Losh 	DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]);
6612a55deb1SDavid E. O'Brien 
6622a55deb1SDavid E. O'Brien 	if (freeable(fldtab[0]))
6632a55deb1SDavid E. O'Brien 		xfree(fldtab[0]->sval);
6642a55deb1SDavid E. O'Brien 	fldtab[0]->tval = REC | STR | DONTFREE;
6652a55deb1SDavid E. O'Brien 	fldtab[0]->sval = record;
6662a55deb1SDavid E. O'Brien 
667f39dd6a9SWarner Losh 	DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]);
668f39dd6a9SWarner Losh 	DPRINTF("recbld = |%s|\n", record);
669f39dd6a9SWarner Losh 	donerec = true;
6702a55deb1SDavid E. O'Brien }
6712a55deb1SDavid E. O'Brien 
6722a55deb1SDavid E. O'Brien int	errorflag	= 0;
6732a55deb1SDavid E. O'Brien 
674813da98dSDavid E. O'Brien void yyerror(const char *s)
6752a55deb1SDavid E. O'Brien {
676c263f9bfSRuslan Ermilov 	SYNTAX("%s", s);
6772a55deb1SDavid E. O'Brien }
6782a55deb1SDavid E. O'Brien 
679813da98dSDavid E. O'Brien void SYNTAX(const char *fmt, ...)
6802a55deb1SDavid E. O'Brien {
6812a55deb1SDavid E. O'Brien 	extern char *cmdname, *curfname;
6822a55deb1SDavid E. O'Brien 	static int been_here = 0;
6832a55deb1SDavid E. O'Brien 	va_list varg;
6842a55deb1SDavid E. O'Brien 
6852a55deb1SDavid E. O'Brien 	if (been_here++ > 2)
6862a55deb1SDavid E. O'Brien 		return;
6872a55deb1SDavid E. O'Brien 	fprintf(stderr, "%s: ", cmdname);
6882a55deb1SDavid E. O'Brien 	va_start(varg, fmt);
6892a55deb1SDavid E. O'Brien 	vfprintf(stderr, fmt, varg);
6902a55deb1SDavid E. O'Brien 	va_end(varg);
6912a55deb1SDavid E. O'Brien 	fprintf(stderr, " at source line %d", lineno);
6922a55deb1SDavid E. O'Brien 	if (curfname != NULL)
6932a55deb1SDavid E. O'Brien 		fprintf(stderr, " in function %s", curfname);
694f39dd6a9SWarner Losh 	if (compile_time == COMPILING && cursource() != NULL)
6952a55deb1SDavid E. O'Brien 		fprintf(stderr, " source file %s", cursource());
6962a55deb1SDavid E. O'Brien 	fprintf(stderr, "\n");
6972a55deb1SDavid E. O'Brien 	errorflag = 2;
6982a55deb1SDavid E. O'Brien 	eprint();
6992a55deb1SDavid E. O'Brien }
7002a55deb1SDavid E. O'Brien 
7012a55deb1SDavid E. O'Brien extern int bracecnt, brackcnt, parencnt;
7022a55deb1SDavid E. O'Brien 
7032a55deb1SDavid E. O'Brien void bracecheck(void)
7042a55deb1SDavid E. O'Brien {
7052a55deb1SDavid E. O'Brien 	int c;
7062a55deb1SDavid E. O'Brien 	static int beenhere = 0;
7072a55deb1SDavid E. O'Brien 
7082a55deb1SDavid E. O'Brien 	if (beenhere++)
7092a55deb1SDavid E. O'Brien 		return;
7102a55deb1SDavid E. O'Brien 	while ((c = input()) != EOF && c != '\0')
7112a55deb1SDavid E. O'Brien 		bclass(c);
7122a55deb1SDavid E. O'Brien 	bcheck2(bracecnt, '{', '}');
7132a55deb1SDavid E. O'Brien 	bcheck2(brackcnt, '[', ']');
7142a55deb1SDavid E. O'Brien 	bcheck2(parencnt, '(', ')');
7152a55deb1SDavid E. O'Brien }
7162a55deb1SDavid E. O'Brien 
7172a55deb1SDavid E. O'Brien void bcheck2(int n, int c1, int c2)
7182a55deb1SDavid E. O'Brien {
7192a55deb1SDavid E. O'Brien 	if (n == 1)
7202a55deb1SDavid E. O'Brien 		fprintf(stderr, "\tmissing %c\n", c2);
7212a55deb1SDavid E. O'Brien 	else if (n > 1)
7222a55deb1SDavid E. O'Brien 		fprintf(stderr, "\t%d missing %c's\n", n, c2);
7232a55deb1SDavid E. O'Brien 	else if (n == -1)
7242a55deb1SDavid E. O'Brien 		fprintf(stderr, "\textra %c\n", c2);
7252a55deb1SDavid E. O'Brien 	else if (n < -1)
7262a55deb1SDavid E. O'Brien 		fprintf(stderr, "\t%d extra %c's\n", -n, c2);
7272a55deb1SDavid E. O'Brien }
7282a55deb1SDavid E. O'Brien 
729813da98dSDavid E. O'Brien void FATAL(const char *fmt, ...)
7302a55deb1SDavid E. O'Brien {
7312a55deb1SDavid E. O'Brien 	extern char *cmdname;
7322a55deb1SDavid E. O'Brien 	va_list varg;
7332a55deb1SDavid E. O'Brien 
7342a55deb1SDavid E. O'Brien 	fflush(stdout);
7352a55deb1SDavid E. O'Brien 	fprintf(stderr, "%s: ", cmdname);
7362a55deb1SDavid E. O'Brien 	va_start(varg, fmt);
7372a55deb1SDavid E. O'Brien 	vfprintf(stderr, fmt, varg);
7382a55deb1SDavid E. O'Brien 	va_end(varg);
7392a55deb1SDavid E. O'Brien 	error();
7402a55deb1SDavid E. O'Brien 	if (dbg > 1)		/* core dump if serious debugging on */
7412a55deb1SDavid E. O'Brien 		abort();
7422a55deb1SDavid E. O'Brien 	exit(2);
7432a55deb1SDavid E. O'Brien }
7442a55deb1SDavid E. O'Brien 
745813da98dSDavid E. O'Brien void WARNING(const char *fmt, ...)
7462a55deb1SDavid E. O'Brien {
7472a55deb1SDavid E. O'Brien 	extern char *cmdname;
7482a55deb1SDavid E. O'Brien 	va_list varg;
7492a55deb1SDavid E. O'Brien 
7502a55deb1SDavid E. O'Brien 	fflush(stdout);
7512a55deb1SDavid E. O'Brien 	fprintf(stderr, "%s: ", cmdname);
7522a55deb1SDavid E. O'Brien 	va_start(varg, fmt);
7532a55deb1SDavid E. O'Brien 	vfprintf(stderr, fmt, varg);
7542a55deb1SDavid E. O'Brien 	va_end(varg);
7552a55deb1SDavid E. O'Brien 	error();
7562a55deb1SDavid E. O'Brien }
7572a55deb1SDavid E. O'Brien 
7582a55deb1SDavid E. O'Brien void error()
7592a55deb1SDavid E. O'Brien {
7602a55deb1SDavid E. O'Brien 	extern Node *curnode;
7612a55deb1SDavid E. O'Brien 
7622a55deb1SDavid E. O'Brien 	fprintf(stderr, "\n");
763f39dd6a9SWarner Losh 	if (compile_time != ERROR_PRINTING) {
764f39dd6a9SWarner Losh 		if (NR && *NR > 0) {
7652a55deb1SDavid E. O'Brien 			fprintf(stderr, " input record number %d", (int) (*FNR));
7662a55deb1SDavid E. O'Brien 			if (strcmp(*FILENAME, "-") != 0)
7672a55deb1SDavid E. O'Brien 				fprintf(stderr, ", file %s", *FILENAME);
7682a55deb1SDavid E. O'Brien 			fprintf(stderr, "\n");
7692a55deb1SDavid E. O'Brien 		}
770f39dd6a9SWarner Losh 		if (curnode)
7712a55deb1SDavid E. O'Brien 			fprintf(stderr, " source line number %d", curnode->lineno);
772f39dd6a9SWarner Losh 		else if (lineno)
7732a55deb1SDavid E. O'Brien 			fprintf(stderr, " source line number %d", lineno);
774f39dd6a9SWarner Losh 		if (compile_time == COMPILING && cursource() != NULL)
7752a55deb1SDavid E. O'Brien 			fprintf(stderr, " source file %s", cursource());
7762a55deb1SDavid E. O'Brien 		fprintf(stderr, "\n");
7772a55deb1SDavid E. O'Brien 		eprint();
7782a55deb1SDavid E. O'Brien 	}
779f39dd6a9SWarner Losh }
7802a55deb1SDavid E. O'Brien 
7812a55deb1SDavid E. O'Brien void eprint(void)	/* try to print context around error */
7822a55deb1SDavid E. O'Brien {
7832a55deb1SDavid E. O'Brien 	char *p, *q;
7842a55deb1SDavid E. O'Brien 	int c;
7852a55deb1SDavid E. O'Brien 	static int been_here = 0;
7862a55deb1SDavid E. O'Brien 	extern char ebuf[], *ep;
7872a55deb1SDavid E. O'Brien 
788f39dd6a9SWarner Losh 	if (compile_time != COMPILING || been_here++ > 0 || ebuf == ep)
7892a55deb1SDavid E. O'Brien 		return;
790b5253557SWarner Losh 	if (ebuf == ep)
791b5253557SWarner Losh 		return;
7922a55deb1SDavid E. O'Brien 	p = ep - 1;
7932a55deb1SDavid E. O'Brien 	if (p > ebuf && *p == '\n')
7942a55deb1SDavid E. O'Brien 		p--;
7952a55deb1SDavid E. O'Brien 	for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
7962a55deb1SDavid E. O'Brien 		;
7972a55deb1SDavid E. O'Brien 	while (*p == '\n')
7982a55deb1SDavid E. O'Brien 		p++;
7992a55deb1SDavid E. O'Brien 	fprintf(stderr, " context is\n\t");
8002a55deb1SDavid E. O'Brien 	for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
8012a55deb1SDavid E. O'Brien 		;
8022a55deb1SDavid E. O'Brien 	for ( ; p < q; p++)
8032a55deb1SDavid E. O'Brien 		if (*p)
8042a55deb1SDavid E. O'Brien 			putc(*p, stderr);
8052a55deb1SDavid E. O'Brien 	fprintf(stderr, " >>> ");
8062a55deb1SDavid E. O'Brien 	for ( ; p < ep; p++)
8072a55deb1SDavid E. O'Brien 		if (*p)
8082a55deb1SDavid E. O'Brien 			putc(*p, stderr);
8092a55deb1SDavid E. O'Brien 	fprintf(stderr, " <<< ");
8102a55deb1SDavid E. O'Brien 	if (*ep)
8112a55deb1SDavid E. O'Brien 		while ((c = input()) != '\n' && c != '\0' && c != EOF) {
8122a55deb1SDavid E. O'Brien 			putc(c, stderr);
8132a55deb1SDavid E. O'Brien 			bclass(c);
8142a55deb1SDavid E. O'Brien 		}
8152a55deb1SDavid E. O'Brien 	putc('\n', stderr);
8162a55deb1SDavid E. O'Brien 	ep = ebuf;
8172a55deb1SDavid E. O'Brien }
8182a55deb1SDavid E. O'Brien 
8192a55deb1SDavid E. O'Brien void bclass(int c)
8202a55deb1SDavid E. O'Brien {
8212a55deb1SDavid E. O'Brien 	switch (c) {
8222a55deb1SDavid E. O'Brien 	case '{': bracecnt++; break;
8232a55deb1SDavid E. O'Brien 	case '}': bracecnt--; break;
8242a55deb1SDavid E. O'Brien 	case '[': brackcnt++; break;
8252a55deb1SDavid E. O'Brien 	case ']': brackcnt--; break;
8262a55deb1SDavid E. O'Brien 	case '(': parencnt++; break;
8272a55deb1SDavid E. O'Brien 	case ')': parencnt--; break;
8282a55deb1SDavid E. O'Brien 	}
8292a55deb1SDavid E. O'Brien }
8302a55deb1SDavid E. O'Brien 
831813da98dSDavid E. O'Brien double errcheck(double x, const char *s)
8322a55deb1SDavid E. O'Brien {
8332a55deb1SDavid E. O'Brien 
8342a55deb1SDavid E. O'Brien 	if (errno == EDOM) {
8352a55deb1SDavid E. O'Brien 		errno = 0;
8362a55deb1SDavid E. O'Brien 		WARNING("%s argument out of domain", s);
8372a55deb1SDavid E. O'Brien 		x = 1;
8382a55deb1SDavid E. O'Brien 	} else if (errno == ERANGE) {
8392a55deb1SDavid E. O'Brien 		errno = 0;
8402a55deb1SDavid E. O'Brien 		WARNING("%s result out of range", s);
8412a55deb1SDavid E. O'Brien 		x = 1;
8422a55deb1SDavid E. O'Brien 	}
8432a55deb1SDavid E. O'Brien 	return x;
8442a55deb1SDavid E. O'Brien }
8452a55deb1SDavid E. O'Brien 
846813da98dSDavid E. O'Brien int isclvar(const char *s)	/* is s of form var=something ? */
8472a55deb1SDavid E. O'Brien {
848813da98dSDavid E. O'Brien 	const char *os = s;
8492a55deb1SDavid E. O'Brien 
850f32a6403SWarner Losh 	if (!isalpha((int) *s) && *s != '_')
8512a55deb1SDavid E. O'Brien 		return 0;
8522a55deb1SDavid E. O'Brien 	for ( ; *s; s++)
853f32a6403SWarner Losh 		if (!(isalnum((int) *s) || *s == '_'))
8542a55deb1SDavid E. O'Brien 			break;
855b5253557SWarner Losh 	return *s == '=' && s > os;
8562a55deb1SDavid E. O'Brien }
8572a55deb1SDavid E. O'Brien 
8582a55deb1SDavid E. O'Brien /* strtod is supposed to be a proper test of what's a valid number */
8592a55deb1SDavid E. O'Brien /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
8602a55deb1SDavid E. O'Brien /* wrong: violates 4.10.1.4 of ansi C standard */
8612a55deb1SDavid E. O'Brien 
862f39dd6a9SWarner Losh /* well, not quite. As of C99, hex floating point is allowed. so this is
863f39dd6a9SWarner Losh  * a bit of a mess. We work around the mess by checking for a hexadecimal
864f39dd6a9SWarner Losh  * value and disallowing it. Similarly, we now follow gawk and allow only
865f39dd6a9SWarner Losh  * +nan, -nan, +inf, and -inf for NaN and infinity values.
866f39dd6a9SWarner Losh  */
867f39dd6a9SWarner Losh 
868f39dd6a9SWarner Losh /*
869f39dd6a9SWarner Losh  * This routine now has a more complicated interface, the main point
870f39dd6a9SWarner Losh  * being to avoid the double conversion of a string to double, and
871f39dd6a9SWarner Losh  * also to convey out, if requested, the information that the numeric
872f39dd6a9SWarner Losh  * value was a leading string or is all of the string. The latter bit
873f39dd6a9SWarner Losh  * is used in getfval().
874f39dd6a9SWarner Losh  */
875f39dd6a9SWarner Losh 
876f39dd6a9SWarner Losh bool is_valid_number(const char *s, bool trailing_stuff_ok,
877f39dd6a9SWarner Losh 			bool *no_trailing, double *result)
8782a55deb1SDavid E. O'Brien {
8792a55deb1SDavid E. O'Brien 	double r;
8802a55deb1SDavid E. O'Brien 	char *ep;
881f39dd6a9SWarner Losh 	bool retval = false;
882f39dd6a9SWarner Losh 	bool is_nan = false;
883f39dd6a9SWarner Losh 	bool is_inf = false;
884f39dd6a9SWarner Losh 
885f39dd6a9SWarner Losh 	if (no_trailing)
886f39dd6a9SWarner Losh 		*no_trailing = false;
887f39dd6a9SWarner Losh 
888f32a6403SWarner Losh 	while (isspace((int) *s))
889f39dd6a9SWarner Losh 		s++;
890f39dd6a9SWarner Losh 
891f32a6403SWarner Losh 	/* no hex floating point, sorry */
892f39dd6a9SWarner Losh 	if (s[0] == '0' && tolower(s[1]) == 'x')
893f39dd6a9SWarner Losh 		return false;
894f39dd6a9SWarner Losh 
895f32a6403SWarner Losh 	/* allow +nan, -nan, +inf, -inf, any other letter, no */
896f39dd6a9SWarner Losh 	if (s[0] == '+' || s[0] == '-') {
897f39dd6a9SWarner Losh 		is_nan = (strncasecmp(s+1, "nan", 3) == 0);
898f39dd6a9SWarner Losh 		is_inf = (strncasecmp(s+1, "inf", 3) == 0);
899f39dd6a9SWarner Losh 		if ((is_nan || is_inf)
900f32a6403SWarner Losh 		    && (isspace((int) s[4]) || s[4] == '\0'))
901f39dd6a9SWarner Losh 			goto convert;
902f39dd6a9SWarner Losh 		else if (! isdigit(s[1]) && s[1] != '.')
903f39dd6a9SWarner Losh 			return false;
904f39dd6a9SWarner Losh 	}
905f39dd6a9SWarner Losh 	else if (! isdigit(s[0]) && s[0] != '.')
906f39dd6a9SWarner Losh 		return false;
907f39dd6a9SWarner Losh 
908f39dd6a9SWarner Losh convert:
9092a55deb1SDavid E. O'Brien 	errno = 0;
9102a55deb1SDavid E. O'Brien 	r = strtod(s, &ep);
911f39dd6a9SWarner Losh 	if (ep == s || errno == ERANGE)
912f39dd6a9SWarner Losh 		return false;
913f39dd6a9SWarner Losh 
914f39dd6a9SWarner Losh 	if (isnan(r) && s[0] == '-' && signbit(r) == 0)
915f39dd6a9SWarner Losh 		r = -r;
916f39dd6a9SWarner Losh 
917f39dd6a9SWarner Losh 	if (result != NULL)
918f39dd6a9SWarner Losh 		*result = r;
919f39dd6a9SWarner Losh 
920f39dd6a9SWarner Losh 	/*
921f39dd6a9SWarner Losh 	 * check for trailing stuff
922f39dd6a9SWarner Losh 	 */
923f32a6403SWarner Losh 	while (isspace((int) *ep))
9242a55deb1SDavid E. O'Brien 		ep++;
925f39dd6a9SWarner Losh 
926f39dd6a9SWarner Losh 	if (no_trailing != NULL)
927f39dd6a9SWarner Losh 		*no_trailing = (*ep == '\0');
928f39dd6a9SWarner Losh 
929f32a6403SWarner Losh         /* return true if found the end, or trailing stuff is allowed */
930f39dd6a9SWarner Losh 	retval = *ep == '\0' || trailing_stuff_ok;
931f39dd6a9SWarner Losh 
932f39dd6a9SWarner Losh 	return retval;
9332a55deb1SDavid E. O'Brien }
934