Print this page
3731 Update nawk to version 20121220

@@ -22,84 +22,155 @@
 /*
  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * 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);
 static  void    cleanfld(int, int);
 static  int     refldbld(uchar *, uchar *);
 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)
 {
         int i;
         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;
                 initgetrec();
         }
         dprintf(("RS=<%s>, FS=<%s>, ARGC=%f, FILENAME=%s\n",
             *RS, *FS, *ARGC, *FILENAME));
+        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;
                         }
                         if (isclvar(file)) {    /* a var=value arg */
                                 setclvar(file);

@@ -109,93 +180,119 @@
                         *FILENAME = file;
                         dprintf(("opening file %s\n", file));
                         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 */
                 if (infile != stdin)
                         (void) fclose(infile);
                 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 */
                 while ((c = getc(inf)) == '\n' && c != EOF)
                         ;
                 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));
         return (s);
 }

@@ -215,236 +312,215 @@
         if (is_number(q->sval)) {
                 q->fval = atof((const char *)q->sval);
                 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' &&
                             *r != '\0')
                                 ;
                         *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)
                                 break;
                 }
                 *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;
 
-        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) */
-{
-        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)
+newfld(int n)   /* add field n after end of existing lastfld */
 {
-        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;
                         *(fr-1) = '\0';
                         rec = patbeg + patlen;

@@ -457,75 +533,87 @@
         }
         return (i);
 }
 
 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;
 
 void

@@ -556,48 +644,74 @@
         else if (n < -1)
                 (void) fprintf(stderr, gettext("\t%d extra %c's\n"), -n, c2);
 }
 
 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
 eprint(void)    /* try to print context around error */
 {
         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;
         p = ep - 1;
         if (p > ebuf && *p == '\n')

@@ -638,158 +752,54 @@
         case ')': parencnt--; break;
         }
 }
 
 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 == '_')) {
+        if (!isalpha(*s) && *s != '_')
+                return (0);
+        for (; *s; s++) {
+                if (!(isalnum(*s) || *s == '_'))
                                         break;
                                 }
-                        }
-                        return (*s == '=' && *(s + 1) != '=');
-                }
-        }
 
-        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))
-                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) {
+        double r;
+        char *ep;
+        errno = 0;
+        r = strtod((const char *)s, &ep);
+        if (ep == (char *)s || r == HUGE_VAL || errno == ERANGE)
                         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;
-}