--- old/usr/src/cmd/awk/lib.c	2013-05-11 15:20:44.184563151 +0000
+++ new/usr/src/cmd/awk/lib.c	2013-05-11 15:20:44.192954240 +0000
@@ -24,34 +24,55 @@
  * Use is subject to license terms.
  */
 
-/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
-/*	  All Rights Reserved  	*/
-
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
+/*
+ * Copyright (C) Lucent Technologies 1997
+ * All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and this
+ * permission notice and warranty disclaimer appear in supporting
+ * documentation, and that the name Lucent Technologies or any of
+ * its entities not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.
+ *
+ * LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+ * IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ * THIS SOFTWARE.
+ */
 
 #include <errno.h>
+#include <stdarg.h>
+#include <math.h>
 #include "awk.h"
 #include "y.tab.h"
 
 uchar	*record;
-size_t	record_size;
+size_t	record_size = RECSIZE;
+
+Cell	**fldtab;	/* pointers to Cells */
+char	inputFS[100] = " ";
+
+#define	MAXFLD	2
+int	nfields = MAXFLD;	/* last allocated slot for $i */
 
 int	donefld;	/* 1 = implies rec broken into fields */
 int	donerec;	/* 1 = record is valid (no flds have changed) */
 
-static struct fldtab_chunk {
-	struct fldtab_chunk	*next;
-	Cell			fields[FLD_INCR];
-} *fldtab_head, *fldtab_tail;
-
-static	size_t	fldtab_maxidx;
+int	lastfld = 0;	/* last used field */
 
 static FILE	*infile	= NULL;
 static uchar	*file	= (uchar*) "";
 static uchar	*fields;
-static size_t	fields_size = LINE_INCR;
+static size_t	fields_size = RECSIZE;
 
-static int	maxfld	= 0;	/* last used field */
 static int	argno	= 1;	/* current input argument number */
 
 static	uchar	*getargv(int);
@@ -60,6 +81,42 @@
 static	void	bcheck2(int, int, int);
 static	void	eprint(void);
 static	void	bclass(int);
+static	void	makefields(int, int);
+
+
+static Cell dollar0 = { OCELL, CFLD, NULL, (uchar *)"", 0.0, REC|STR|DONTFREE };
+static Cell dollar1 = { OCELL, CFLD, NULL, (uchar *)"", 0.0, FLD|STR|DONTFREE };
+
+void
+recinit(unsigned int n)
+{
+	if ((record = (uchar *)malloc(n)) == NULL ||
+	    (fields = (uchar *)malloc(n+1)) == NULL ||
+	    (fldtab = (Cell **)malloc((nfields + 1) *
+	    sizeof (Cell *))) == NULL ||
+	    (fldtab[0] = (Cell *)malloc(sizeof (Cell))) == NULL)
+		FATAL("out of space for $0 and fields");
+	*fldtab[0] = dollar0;
+	fldtab[0]->sval = record;
+	fldtab[0]->nval = tostring((uchar *)"0");
+	makefields(1, nfields);
+}
+
+static void
+makefields(int n1, int n2)	/* create $n1..$n2 inclusive */
+{
+	char temp[50];
+	int i;
+
+	for (i = n1; i <= n2; i++) {
+		fldtab[i] = (Cell *)malloc(sizeof (struct Cell));
+		if (fldtab[i] == NULL)
+			FATAL("out of space in makefields %d", i);
+		*fldtab[i] = dollar1;
+		(void) sprintf(temp, "%d", i);
+		fldtab[i]->nval = tostring((uchar *)temp);
+	}
+}
 
 static void
 initgetrec(void)
@@ -68,22 +125,31 @@
 	uchar *p;
 
 	for (i = 1; i < *ARGC; i++) {
-		if (!isclvar(p = getargv(i)))	/* find 1st real filename */
+		p = getargv(i);	/* find 1st real filename */
+		if (p == NULL || *p == '\0') {	/* deleted or zapped */
+			argno++;
+			continue;
+		}
+		if (!isclvar(p)) {
+			(void) setsval(lookup((uchar *)"FILENAME", symtab), p);
 			return;
+		}
 		setclvar(p);	/* a commandline assignment before filename */
 		argno++;
 	}
 	infile = stdin;		/* no filenames, so use stdin */
-	/* *FILENAME = file = (uchar*) "-"; */
 }
 
+static int firsttime = 1;
+
 int
-getrec(uchar **bufp, size_t *bufsizep)
-{
+getrec(uchar **pbuf, size_t *pbufsize, int isrecord)
+{				/* get next input record */
+				/* note: cares whether buf == record */
 	int c;
-	static int firsttime = 1;
-	uchar_t	*buf, *nbuf;
-	size_t	len;
+	uchar_t	*buf = *pbuf;
+	uchar saveb0;
+	size_t bufsize = *pbufsize, savebufsize = bufsize;
 
 	if (firsttime) {
 		firsttime = 0;
@@ -91,13 +157,18 @@
 	}
 	dprintf(("RS=<%s>, FS=<%s>, ARGC=%f, FILENAME=%s\n",
 	    *RS, *FS, *ARGC, *FILENAME));
-	donefld = 0;
-	donerec = 1;
+	if (isrecord) {
+		donefld = 0;
+		donerec = 1;
+	}
+	saveb0 = buf[0];
+	buf[0] = 0;
 	while (argno < *ARGC || infile == stdin) {
 		dprintf(("argno=%d, file=|%s|\n", argno, file));
 		if (infile == NULL) {	/* have to open a new file */
 			file = getargv(argno);
-			if (*file == '\0') {	/* it's been zapped */
+			/* deleted or zapped */
+			if (file == NULL || *file == '\0') {
 				argno++;
 				continue;
 			}
@@ -111,30 +182,26 @@
 			if (*file == '-' && *(file+1) == '\0')
 				infile = stdin;
 			else if ((infile = fopen((char *)file, "r")) == NULL)
-				ERROR "can't open file %s", file FATAL;
+				FATAL("can't open file %s", file);
 			(void) setfval(fnrloc, 0.0);
 		}
-		c = readrec(&nbuf, &len, infile);
-		expand_buf(bufp, bufsizep, len);
-		buf = *bufp;
-		(void) memcpy(buf, nbuf, len);
-		buf[len] = '\0';
-		free(nbuf);
-
+		c = readrec(&buf, &bufsize, infile);
 		if (c != 0 || buf[0] != '\0') {	/* normal record */
-			if (bufp == &record) {
-				if (!(recloc->tval & DONTFREE))
-					xfree(recloc->sval);
-				recloc->sval = record;
-				recloc->tval = REC | STR | DONTFREE;
-				if (is_number(recloc->sval)) {
-					recloc->fval =
-					    atof((const char *)recloc->sval);
-					recloc->tval |= NUM;
+			if (isrecord) {
+				if (freeable(fldtab[0]))
+					xfree(fldtab[0]->sval);
+				fldtab[0]->sval = buf;	/* buf == record */
+				fldtab[0]->tval = REC | STR | DONTFREE;
+				if (is_number(fldtab[0]->sval)) {
+					fldtab[0]->fval =
+					    atof((const char *)fldtab[0]->sval);
+					fldtab[0]->tval |= NUM;
 				}
 			}
 			(void) setfval(nrloc, nrloc->fval+1);
 			(void) setfval(fnrloc, fnrloc->fval+1);
+			*pbuf = buf;
+			*pbufsize = bufsize;
 			return (1);
 		}
 		/* EOF arrived on this file; set up next */
@@ -143,18 +210,40 @@
 		infile = NULL;
 		argno++;
 	}
+	buf[0] = saveb0;
+	*pbuf = buf;
+	*pbufsize = savebufsize;
 	return (0);	/* true end of file */
 }
 
+void
+nextfile(void)
+{
+	if (infile != NULL && infile != stdin)
+		(void) fclose(infile);
+	infile = NULL;
+	argno++;
+}
+
+/*
+ * read one record into buf
+ */
 int
-readrec(uchar **bufp, size_t *sizep, FILE *inf)	/* read one record into buf */
+readrec(uchar **pbuf, size_t *pbufsize, FILE *inf)
 {
 	int sep, c;
-	uchar	*buf;
-	int	count;
-	size_t	bufsize;
+	uchar *rr, *buf = *pbuf;
+	size_t bufsize = *pbufsize;
+
+	if (strlen((char *)*FS) >= sizeof (inputFS))
+		FATAL("field separator %.10s... is too long", *FS);
+	/*
+	 * fflush(stdout); avoids some buffering problem
+	 * but makes it 25% slower
+	 */
 
-	init_buf(&buf, &bufsize, LINE_INCR);
+	/* for subsequent field splitting */
+	(void) strcpy(inputFS, (char *)*FS);
 	if ((sep = **RS) == 0) {
 		sep = '\n';
 		/* skip leading \n's */
@@ -163,37 +252,45 @@
 		if (c != EOF)
 			(void) ungetc(c, inf);
 	}
-	count = 0;
-	for (;;) {
-		while ((c = getc(inf)) != sep && c != EOF) {
-			expand_buf(&buf, &bufsize, count);
-			buf[count++] = c;
+	for (rr = buf; ; ) {
+		for (; (c = getc(inf)) != sep && c != EOF; ) {
+			if (rr-buf+1 > bufsize)
+				if (!adjbuf(&buf, &bufsize, 1+rr-buf,
+				    record_size, &rr, "readrec 1"))
+					FATAL(
+				"input record `%.30s...' too long", buf);
+			*rr++ = c;
 		}
 		if (**RS == sep || c == EOF)
 			break;
 		if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */
 			break;
-		expand_buf(&buf, &bufsize, count + 1);
-		buf[count++] = '\n';
-		buf[count++] = c;
-	}
-	buf[count] = '\0';
+		if (!adjbuf(&buf, &bufsize, 2+rr-buf, record_size, &rr,
+		    "readrec 2"))
+			FATAL("input record `%.30s...' too long", buf);
+		*rr++ = '\n';
+		*rr++ = c;
+	}
+	if (!adjbuf(&buf, &bufsize, 1+rr-buf, record_size, &rr, "readrec 3"))
+		FATAL("input record `%.30s...' too long", buf);
+	*rr = 0;
 	dprintf(("readrec saw <%s>, returns %d\n",
-	    buf, c == EOF && count == 0 ? 0 : 1));
-	*bufp = buf;
-	*sizep = count;
-	return (c == EOF && count == 0 ? 0 : 1);
+	    buf, c == EOF && rr == buf ? 0 : 1));
+	*pbuf = buf;
+	*pbufsize = bufsize;
+	return (c == EOF && rr == buf ? 0 : 1);
 }
 
-/* get ARGV[n] */
 static uchar *
-getargv(int n)
+getargv(int n)		/* get ARGV[n] */
 {
 	Cell *x;
-	uchar *s, temp[11];
+	uchar *s, temp[50];
 	extern Array *ARGVtab;
 
 	(void) sprintf((char *)temp, "%d", n);
+	if (lookup(temp, ARGVtab) == NULL)
+		return (NULL);
 	x = setsymtab(temp, (uchar *)"", 0.0, STR, ARGVtab);
 	s = getsval(x);
 	dprintf(("getargv(%d) returns |%s|\n", n, s));
@@ -217,50 +314,50 @@
 		q->tval |= NUM;
 	}
 	dprintf(("command line set %s to |%s|\n", s, p));
-	free(p);
 }
 
 void
-fldbld(void)
+fldbld(void)	/* create fields from current record */
 {
+	/* this relies on having fields[] the same length as $0 */
+	/* the fields are all stored in this one array with \0's */
+	/* possibly with a final trailing \0 not associated with any field */
 	uchar *r, *fr, sep;
 	Cell *p;
-	int i;
-	size_t	len;
+	int i, j;
+	size_t n;
 
 	if (donefld)
 		return;
-	if (!(recloc->tval & STR))
-		(void) getsval(recloc);
-	r = recloc->sval;	/* was record! */
-
-	/* make sure fields is always allocated */
-	adjust_buf(&fields, fields_size);
-
-	/*
-	 * make sure fields has enough size. We don't expand the buffer
-	 * in the middle of the loop, since p->sval has already pointed
-	 * the address in the fields.
-	 */
-	len = strlen((char *)r) + 1;
-	expand_buf(&fields, &fields_size, len);
+	if (!isstr(fldtab[0]))
+		(void) getsval(fldtab[0]);
+	r = fldtab[0]->sval;
+	n = strlen((char *)r);
+	if (n > fields_size) {
+		xfree(fields);
+		/* possibly 2 final \0s */
+		if ((fields = (uchar *)malloc(n + 2)) == NULL)
+			FATAL("out of space for fields in fldbld %d", n);
+		fields_size = n;
+	}
 	fr = fields;
-
 	i = 0;	/* number of fields accumulated here */
-	if (strlen((char *)*FS) > 1) {	/* it's a regular expression */
-		i = refldbld(r, *FS);
-	} else if ((sep = **FS) == ' ') {
+	(void) strcpy(inputFS, (char *)*FS);
+	if (strlen(inputFS) > 1) {	/* it's a regular expression */
+		i = refldbld(r, (uchar *)inputFS);
+	} else if ((sep = *inputFS) == ' ') {	/* default whitespace */
 		for (i = 0; ; ) {
 			while (*r == ' ' || *r == '\t' || *r == '\n')
 				r++;
 			if (*r == 0)
 				break;
 			i++;
-			p = getfld(i);
-			if (!(p->tval & DONTFREE))
-				xfree(p->sval);
-			p->sval = fr;
-			p->tval = FLD | STR | DONTFREE;
+			if (i > nfields)
+				growfldtab(i);
+			if (freeable(fldtab[i]))
+				xfree(fldtab[i]->sval);
+			fldtab[i]->sval = fr;
+			fldtab[i]->tval = FLD | STR | DONTFREE;
 			do
 				*fr++ = *r++;
 			while (*r != ' ' && *r != '\t' && *r != '\n' &&
@@ -269,16 +366,39 @@
 			*fr++ = 0;
 		}
 		*fr = 0;
+	} else if ((sep = *inputFS) == 0) {	/* new: FS="" => 1 char/field */
+		for (i = 0; *r != 0; r++) {
+			uchar buf[2];
+			i++;
+			if (i > nfields)
+				growfldtab(i);
+			if (freeable(fldtab[i]))
+				xfree(fldtab[i]->sval);
+			buf[0] = *r;
+			buf[1] = 0;
+			fldtab[i]->sval = tostring(buf);
+			fldtab[i]->tval = FLD | STR;
+		}
+		*fr = 0;
 	} else if (*r != 0) {	/* if 0, it's a null field */
+		/*
+		 * subtlecase : if length(FS) == 1 && length(RS > 0)
+		 * \n is NOT a field separator (cf awk book 61,84).
+		 * this variable is tested in the inner while loop.
+		 */
+		int rtest = '\n';  /* normal case */
+		if (strlen((char *)*RS) > 0)
+			rtest = '\0';
 		for (;;) {
 			i++;
-			p = getfld(i);
-			if (!(p->tval & DONTFREE))
-				xfree(p->sval);
-			p->sval = fr;
-			p->tval = FLD | STR | DONTFREE;
-			/* \n always a separator */
-			while (*r != sep && *r != '\n' && *r != '\0')
+			if (i > nfields)
+				growfldtab(i);
+			if (freeable(fldtab[i]))
+				xfree(fldtab[i]->sval);
+			fldtab[i]->sval = fr;
+			fldtab[i]->tval = FLD | STR | DONTFREE;
+			/* \n is always a separator */
+			while (*r != sep && *r != rtest && *r != '\0')
 				*fr++ = *r++;
 			*fr++ = 0;
 			if (*r++ == 0)
@@ -286,163 +406,119 @@
 		}
 		*fr = 0;
 	}
+	if (i > nfields)
+		FATAL("record `%.30s...' has too many fields; can't happen", r);
 	/* clean out junk from previous record */
-	cleanfld(i, maxfld);
-	maxfld = i;
+	cleanfld(i + 1, lastfld);
+	lastfld = i;
 	donefld = 1;
-	for (i = 1; i <= maxfld; i++) {
-		p = getfld(i);
+	for (j = 1; j <= lastfld; j++) {
+		p = fldtab[j];
 		if (is_number(p->sval)) {
 			p->fval = atof((const char *)p->sval);
 			p->tval |= NUM;
 		}
 	}
-
-	(void) setfval(nfloc, (Awkfloat) maxfld);
+	(void) setfval(nfloc, (Awkfloat)lastfld);
 	if (dbg) {
-		for (i = 0; i <= maxfld; i++) {
-			p = getfld(i);
-			(void) printf("field %d: |%s|\n", i, p->sval);
+		for (j = 0; j <= lastfld; j++) {
+			p = fldtab[j];
+			(void) printf("field %d (%s): |%s|\n", j, p->nval,
+			    p->sval);
 		}
 	}
 }
 
 static void
 cleanfld(int n1, int n2)	/* clean out fields n1..n2 inclusive */
-{
-	static uchar *nullstat = (uchar *) "";
+{				/* nvals remain intact */
+	static uchar *nullstat = (uchar *)"";
 	Cell *p;
-	int	i;
+	int i;
 
-	for (i = n2; i > n1; i--) {
-		p = getfld(i);
-		if (!(p->tval & DONTFREE))
+	for (i = n1; i <= n2; i++) {
+		p = fldtab[i];
+		if (freeable(p))
 			xfree(p->sval);
-		p->tval = FLD | STR | DONTFREE;
 		p->sval = nullstat;
+		p->tval = FLD | STR | DONTFREE;
 	}
 }
 
 void
-newfld(int n)	/* add field n (after end) */
+newfld(int n)	/* add field n after end of existing lastfld */
 {
-	if (n < 0)
-		ERROR "accessing invalid field", record FATAL;
-	(void) getfld(n);
-	cleanfld(maxfld, n);
-	maxfld = n;
-	(void) setfval(nfloc, (Awkfloat) n);
-}
-
-/*
- * allocate field table. We don't reallocate the table since there
- * might be somewhere recording the address of the table.
- */
-static void
-morefld(void)
-{
-	int	i;
-	struct fldtab_chunk *fldcp;
-	Cell	*newfld;
-
-	if ((fldcp = calloc(sizeof (struct fldtab_chunk), 1)) == NULL)
-		ERROR "out of space in morefld" FATAL;
-
-	newfld = &fldcp->fields[0];
-	for (i = 0; i < FLD_INCR; i++) {
-		newfld[i].ctype = OCELL;
-		newfld[i].csub = CFLD;
-		newfld[i].nval = NULL;
-		newfld[i].sval = (uchar *)"";
-		newfld[i].fval = 0.0;
-		newfld[i].tval = FLD|STR|DONTFREE;
-		newfld[i].cnext = NULL;
-	}
-	/*
-	 * link this field chunk
-	 */
-	if (fldtab_head == NULL)
-		fldtab_head = fldcp;
-	else
-		fldtab_tail->next = fldcp;
-	fldtab_tail = fldcp;
-	fldcp->next = NULL;
-
-	fldtab_maxidx += FLD_INCR;
+	if (n > nfields)
+		growfldtab(n);
+	cleanfld(lastfld + 1, n);
+	lastfld = n;
+	(void) setfval(nfloc, (Awkfloat)n);
 }
 
 Cell *
-getfld(int idx)
+fieldadr(int n)		/* get nth field */
 {
-	struct fldtab_chunk *fldcp;
-	int	cbase;
-
-	if (idx < 0)
-		ERROR "trying to access field %d", idx FATAL;
-	while (idx >= fldtab_maxidx)
-		morefld();
-	cbase = 0;
-	for (fldcp = fldtab_head; fldcp != NULL; fldcp = fldcp->next) {
-		if (idx < (cbase + FLD_INCR))
-			return (&fldcp->fields[idx - cbase]);
-		cbase += FLD_INCR;
-	}
-	/* should never happen */
-	ERROR "trying to access invalid field %d", idx FATAL;
-	return (NULL);
+	if (n < 0)
+		FATAL("trying to access out of range field %d", n);
+	if (n > nfields)	/* fields after NF are empty */
+		growfldtab(n);	/* but does not increase NF */
+	return (fldtab[n]);
 }
 
-int
-fldidx(Cell *vp)
-{
-	struct fldtab_chunk *fldcp;
-	Cell	*tbl;
-	int	cbase;
-
-	cbase = 0;
-	for (fldcp = fldtab_head; fldcp != NULL; fldcp = fldcp->next) {
-		tbl = &fldcp->fields[0];
-		if (vp >= tbl && vp < (tbl + FLD_INCR))
-			return (cbase + (vp - tbl));
-		cbase += FLD_INCR;
-	}
-	/* should never happen */
-	ERROR "trying to access unknown field" FATAL;
-	return (0);
+void
+growfldtab(int n)	/* make new fields up to at least $n */
+{
+	int nf = 2 * nfields;
+	size_t s;
+
+	if (n > nf)
+		nf = n;
+	/* freebsd: how much do we need? */
+	s = (nf + 1) * (sizeof (struct Cell *));
+	if (s / sizeof (struct Cell *) - 1 == nf)	/* didn't overflow */
+		fldtab = (Cell **) realloc(fldtab, s);
+	else	/* overflow sizeof int */
+		xfree(fldtab);  /* make it null */
+	if (fldtab == NULL)
+		FATAL("out of space creating %d fields", nf);
+	makefields(nfields + 1, nf);
+	nfields = nf;
 }
 
 static int
 refldbld(uchar *rec, uchar *fs)	/* build fields from reg expr in FS */
 {
+	/* this relies on having fields[] the same length as $0 */
+	/* the fields are all stored in this one array with \0's */
 	uchar *fr;
 	int i, tempstat;
 	fa *pfa;
-	Cell	*p;
-	size_t	len;
+	size_t n;
 
-	/* make sure fields is allocated */
-	adjust_buf(&fields, fields_size);
+	n = strlen((char *)rec);
+	if (n > fields_size) {
+		xfree(fields);
+		if ((fields = (uchar *)malloc(n + 1)) == NULL)
+			FATAL("out of space for fields in refldbld %d", n);
+		fields_size = n;
+	}
 	fr = fields;
 	*fr = '\0';
 	if (*rec == '\0')
 		return (0);
-
-	len = strlen((char *)rec) + 1;
-	expand_buf(&fields, &fields_size, len);
-	fr = fields;
-
 	pfa = makedfa(fs, 1);
 	dprintf(("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs));
 	tempstat = pfa->initstat;
 	for (i = 1; ; i++) {
-		p = getfld(i);
-		if (!(p->tval & DONTFREE))
-			xfree(p->sval);
-		p->tval = FLD | STR | DONTFREE;
-		p->sval = fr;
+		if (i > nfields)
+			growfldtab(i);
+		if (freeable(fldtab[i]))
+			xfree(fldtab[i]->sval);
+		fldtab[i]->tval = FLD | STR | DONTFREE;
+		fldtab[i]->sval = fr;
 		dprintf(("refldbld: i=%d\n", i));
 		if (nematch(pfa, rec)) {
-			pfa->initstat = 2;
+			pfa->initstat = 2;	/* horrible coupling to b.c */
 			dprintf(("match %s (%d chars)\n", patbeg, patlen));
 			(void) strncpy((char *)fr, (char *)rec, patbeg-rec);
 			fr += patbeg - rec + 1;
@@ -459,71 +535,83 @@
 }
 
 void
-recbld(void)
+recbld(void)		/* create $0 from $1..$NF if necessary */
 {
 	int i;
-	uchar *p;
-	size_t cnt, len, olen;
+	uchar *r, *p;
 
 	if (donerec == 1)
 		return;
-	cnt = 0;
-	olen = strlen((char *)*OFS);
+	r = record;
 	for (i = 1; i <= *NF; i++) {
-		p = getsval(getfld(i));
-		len = strlen((char *)p);
-		expand_buf(&record, &record_size, cnt + len + olen);
-		(void) memcpy(&record[cnt], p, len);
-		cnt += len;
+		p = getsval(fldtab[i]);
+		if (!adjbuf(&record, &record_size, 1 + strlen((char *)p) + r -
+		    record, record_size, &r, "recbld 1"))
+			FATAL("created $0 `%.30s...' too long", record);
+		while ((*r = *p++) != 0)
+			r++;
 		if (i < *NF) {
-			(void) memcpy(&record[cnt], *OFS, olen);
-			cnt += olen;
+			if (!adjbuf(&record, &record_size, 2 +
+			    strlen((char *)*OFS) + r - record, record_size,
+			    &r, "recbld 2"))
+				FATAL("created $0 `%.30s...' too long", record);
+			for (p = *OFS; (*r = *p++) != 0; )
+				r++;
 		}
 	}
-	record[cnt] = '\0';
-	dprintf(("in recbld FS=%o, recloc=%p\n", **FS, (void *)recloc));
-	if (!(recloc->tval & DONTFREE))
-		xfree(recloc->sval);
-	recloc->tval = REC | STR | DONTFREE;
-	recloc->sval = record;
-	dprintf(("in recbld FS=%o, recloc=%p\n", **FS, (void *)recloc));
+	if (!adjbuf(&record, &record_size, 2 + r - record, record_size, &r,
+	    "recbld 3"))
+		FATAL("built giant record `%.30s...'", record);
+	*r = '\0';
+	dprintf(("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS,
+	    (void *)fldtab[0]));
+
+	if (freeable(fldtab[0]))
+		xfree(fldtab[0]->sval);
+	fldtab[0]->tval = REC | STR | DONTFREE;
+	fldtab[0]->sval = record;
+
+	dprintf(("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS,
+	    (void *)fldtab[0]));
 	dprintf(("recbld = |%s|\n", record));
 	donerec = 1;
 }
 
-Cell *
-fieldadr(int n)
-{
-	if (n < 0)
-		ERROR "trying to access field %d", n FATAL;
-	return (getfld(n));
-}
-
 int	errorflag	= 0;
-char	errbuf[200];
 
 void
 yyerror(char *s)
 {
+	SYNTAX("%s", s);
+}
+
+void
+SYNTAX(const char *fmt, ...)
+{
 	extern uchar *cmdname, *curfname;
 	static int been_here = 0;
+	va_list varg;
 
 	if (been_here++ > 2)
 		return;
-	(void) fprintf(stderr, "%s: %s", cmdname, s);
+	(void) fprintf(stderr, "%s: ", cmdname);
+	va_start(varg, fmt);
+	(void) vfprintf(stderr, fmt, varg);
+	va_end(varg);
 	(void) fprintf(stderr, gettext(" at source line %lld"), lineno);
 	if (curfname != NULL)
 		(void) fprintf(stderr, gettext(" in function %s"), curfname);
+	if (compile_time == 1 && cursource() != NULL)
+		(void) fprintf(stderr, gettext(" source file %s"), cursource());
 	(void) fprintf(stderr, "\n");
 	errorflag = 2;
 	eprint();
 }
 
-/*ARGSUSED*/
 void
-fpecatch(int sig)
+fpecatch(int n)
 {
-	ERROR "floating point exception" FATAL;
+	FATAL("floating point exception %d", n);
 }
 
 extern int bracecnt, brackcnt, parencnt;
@@ -558,35 +646,61 @@
 }
 
 void
-error(int f, char *s)
+FATAL(const char *fmt, ...)
 {
-	extern Node *curnode;
 	extern uchar *cmdname;
+	va_list varg;
 
 	(void) fflush(stdout);
 	(void) fprintf(stderr, "%s: ", cmdname);
-	(void) fprintf(stderr, "%s", s);
+	va_start(varg, fmt);
+	(void) vfprintf(stderr, fmt, varg);
+	va_end(varg);
+	error();
+	if (dbg > 1)	/* core dump if serious debugging on */
+		abort();
+	exit(2);
+}
+
+void
+WARNING(const char *fmt, ...)
+{
+	extern uchar *cmdname;
+	va_list varg;
+
+	(void) fflush(stdout);
+	(void) fprintf(stderr, "%s: ", cmdname);
+	va_start(varg, fmt);
+	(void) vfprintf(stderr, fmt, varg);
+	va_end(varg);
+	error();
+}
+
+void
+error(void)
+{
+	extern Node *curnode;
+
 	(void) fprintf(stderr, "\n");
 	if (compile_time != 2 && NR && *NR > 0) {
 		(void) fprintf(stderr,
-		    gettext(" input record number %g"), *FNR);
+		    gettext(" input record number %g"), (int) (*FNR));
 		if (strcmp((char *)*FILENAME, "-") != 0)
 			(void) fprintf(stderr, gettext(", file %s"), *FILENAME);
 		(void) fprintf(stderr, "\n");
 	}
 	if (compile_time != 2 && curnode)
-		(void) fprintf(stderr, gettext(" source line number %lld\n"),
+		(void) fprintf(stderr, gettext(" source line number %d"),
 		    curnode->lineno);
 	else if (compile_time != 2 && lineno) {
 		(void) fprintf(stderr,
-		    gettext(" source line number %lld\n"), lineno);
+		    gettext(" source line number %d"), lineno);
 	}
+	if (compile_time == 1 && cursource() != NULL)
+		(void) fprintf(stderr,
+		    gettext(" source file %s"), cursource());
+	(void) fprintf(stderr, "\n");
 	eprint();
-	if (f) {
-		if (dbg)
-			abort();
-		exit(2);
-	}
 }
 
 static void
@@ -595,7 +709,7 @@
 	uchar *p, *q;
 	int c;
 	static int been_here = 0;
-	extern uchar ebuf[300], *ep;
+	extern uchar ebuf[], *ep;
 
 	if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
 		return;
@@ -640,156 +754,52 @@
 }
 
 double
-errcheck(double x, char *s)
+errcheck(double x, const char *s)
 {
-	extern int errno;
-
 	if (errno == EDOM) {
 		errno = 0;
-		ERROR "%s argument out of domain", s WARNING;
+		WARNING("%s argument out of domain", s);
 		x = 1;
 	} else if (errno == ERANGE) {
 		errno = 0;
-		ERROR "%s result out of range", s WARNING;
+		WARNING("%s result out of range", s);
 		x = 1;
 	}
 	return (x);
 }
 
-void
-PUTS(uchar *s)
-{
-	dprintf(("%s\n", s));
-}
-
 int
-isclvar(uchar *s)	/* is s of form var=something? */
+isclvar(const uchar *s)	/* is s of form var=something? */
 {
-	if (s != NULL) {
+	const uchar *os = s;
 
-		/* Must begin with an underscore or alphabetic character */
-		if (isalpha(*s) || (*s == '_')) {
-
-			for (s++; *s; s++) {
-				/*
-				 * followed by a sequence of underscores,
-				 * digits, and alphabetics
-				 */
-				if (!(isalnum(*s) || *s == '_')) {
-					break;
-				}
-			}
-			return (*s == '=' && *(s + 1) != '=');
-		}
+	if (!isalpha(*s) && *s != '_')
+		return (0);
+	for (; *s; s++) {
+		if (!(isalnum(*s) || *s == '_'))
+			break;
 	}
 
-	return (0);
+	return (*s == '=' && s > os && *(s+1) != '=');
 }
 
-#define	MAXEXPON	38	/* maximum exponent for fp number */
+/* strtod is supposed to be a proper test of what's a valid number */
+/* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
+/* wrong: violates 4.10.1.4 of ansi C standard */
 
 int
-is_number(uchar *s)
+is_number(const uchar *s)
 {
-	int d1, d2;
-	int point;
-	uchar *es;
-	extern char	radixpoint;
-
-	d1 = d2 = point = 0;
-	while (*s == ' ' || *s == '\t' || *s == '\n')
-		s++;
-	if (*s == '\0')
-		return (0);	/* empty stuff isn't number */
-	if (*s == '+' || *s == '-')
-		s++;
-	if (!isdigit(*s) && *s != radixpoint)
-		return (0);
-	if (isdigit(*s)) {
-		do {
-			d1++;
-			s++;
-		} while (isdigit(*s));
-	}
-	if (d1 >= MAXEXPON)
-		return (0);	/* too many digits to convert */
-	if (*s == radixpoint) {
-		point++;
-		s++;
-	}
-	if (isdigit(*s)) {
-		d2++;
-		do {
-			s++;
-		} while (isdigit(*s));
-	}
-	if (!(d1 || point && d2))
+	double r;
+	char *ep;
+	errno = 0;
+	r = strtod((const char *)s, &ep);
+	if (ep == (char *)s || r == HUGE_VAL || errno == ERANGE)
 		return (0);
-	if (*s == 'e' || *s == 'E') {
-		s++;
-		if (*s == '+' || *s == '-')
-			s++;
-		if (!isdigit(*s))
-			return (0);
-		es = s;
-		do {
-			s++;
-		} while (isdigit(*s));
-		if (s - es > 2) {
-			return (0);
-		} else if (s - es == 2 &&
-		    (int)(10 * (*es-'0') + *(es+1)-'0') >= MAXEXPON) {
-			return (0);
-		}
-	}
-	while (*s == ' ' || *s == '\t' || *s == '\n')
-		s++;
-	if (*s == '\0')
+	while (*ep == ' ' || *ep == '\t' || *ep == '\n')
+		ep++;
+	if (*ep == '\0')
 		return (1);
 	else
 		return (0);
 }
-
-void
-init_buf(uchar **optr, size_t *sizep, size_t amt)
-{
-	uchar	*nptr = NULL;
-
-	if ((nptr = malloc(amt)) == NULL)
-		ERROR "out of space in init_buf" FATAL;
-	/* initial buffer should have NULL terminated */
-	*nptr = '\0';
-	if (sizep != NULL)
-		*sizep = amt;
-	*optr = nptr;
-}
-
-void
-r_expand_buf(uchar **optr, size_t *sizep, size_t req)
-{
-	uchar	*nptr;
-	size_t	amt, size = *sizep;
-
-	if (size != 0 && req < (size - 1))
-		return;
-	amt = req + 1 - size;
-	amt = (amt / LINE_INCR + 1) * LINE_INCR;
-
-	if ((nptr = realloc(*optr, size + amt)) == NULL)
-		ERROR "out of space in expand_buf" FATAL;
-	/* initial buffer should have NULL terminated */
-	if (size == 0)
-		*nptr = '\0';
-	*sizep += amt;
-	*optr = nptr;
-}
-
-void
-adjust_buf(uchar **optr, size_t size)
-{
-	uchar	*nptr;
-
-	if ((nptr = realloc(*optr, size)) == NULL)
-		ERROR "out of space in adjust_buf" FATAL;
-	*optr = nptr;
-}
