summaryrefslogtreecommitdiff
path: root/old-extension
diff options
context:
space:
mode:
authorArnold D. Robbins <arnold@skeeve.com>2013-01-06 17:36:58 +0200
committerArnold D. Robbins <arnold@skeeve.com>2013-01-06 17:36:58 +0200
commit6b4dccf6db4dcaaff9f5a58baeee21329635ec18 (patch)
tree057f9b6d5c02ad8338c7f5cb71ca6cb295de3fbe /old-extension
parentcccfa4aac8863671e7215877f9d582d7c6c63753 (diff)
downloadgawk-6b4dccf6db4dcaaff9f5a58baeee21329635ec18.tar.gz
Move old extension stuff into a separate directory.
Diffstat (limited to 'old-extension')
-rw-r--r--old-extension/ChangeLog6
-rw-r--r--old-extension/bindarr.c347
-rw-r--r--old-extension/dbarray.awk222
-rw-r--r--old-extension/fileop.c394
-rw-r--r--old-extension/record.awk252
-rw-r--r--old-extension/sparr.c163
-rw-r--r--old-extension/spec_array.c416
-rw-r--r--old-extension/spec_array.h28
-rwxr-xr-xold-extension/steps10
-rw-r--r--old-extension/testdbarray.awk21
-rwxr-xr-xold-extension/testrecord.sh19
-rw-r--r--old-extension/testsparr.awk18
12 files changed, 1896 insertions, 0 deletions
diff --git a/old-extension/ChangeLog b/old-extension/ChangeLog
new file mode 100644
index 00000000..a25295a6
--- /dev/null
+++ b/old-extension/ChangeLog
@@ -0,0 +1,6 @@
+2013-01-06 Arnold D. Robbins <arnold@skeeve.com>
+
+ * bindarr.c, dbarray.awk, fileop.c, record.awk, sparr.c,
+ spec_array.c, spec_array.h, steps, testdbarray.awk, testrecord.sh,
+ testsparr.awk: Moved here from extension directory, since they
+ use the old mechanism.
diff --git a/old-extension/bindarr.c b/old-extension/bindarr.c
new file mode 100644
index 00000000..60959903
--- /dev/null
+++ b/old-extension/bindarr.c
@@ -0,0 +1,347 @@
+/*
+ * bindarr.c - routines for binding (attaching) user-defined functions
+ * to array and array elements.
+ */
+
+/*
+ * Copyright (C) 1986, 1988, 1989, 1991-2011 the Free Software Foundation, Inc.
+ *
+ * This file is part of GAWK, the GNU implementation of the
+ * AWK Programming Language.
+ *
+ * GAWK is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GAWK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "awk.h"
+
+/*
+ * Binding an array is basically the binding of functions to the internal
+ * triggers for reading and writing that array or an element of that array.
+ * This allows the user to define the set of behaviors for gawk arrays
+ * using gawk functions. With arrays you can assign and read values of
+ * specific elements, provide list of indices and values, and tell if a
+ * certain index exists or not. A variable can be "tied" by including
+ * code which overrides any or all of the standard behaviors of awk arrays.
+ *
+ * See dbarray.awk and testdbarray.awk to learn how to bind an array
+ * to an external database for persistent storage.
+ */
+
+int plugin_is_GPL_compatible;
+
+static NODE **bind_array_lookup(NODE *, NODE *);
+static NODE **bind_array_exists(NODE *, NODE *);
+static NODE **bind_array_clear(NODE *, NODE *);
+static NODE **bind_array_remove(NODE *, NODE *);
+static NODE **bind_array_list(NODE *, NODE *);
+static NODE **bind_array_store(NODE *, NODE *);
+static NODE **bind_array_length(NODE *, NODE *);
+
+static afunc_t bind_array_func[] = {
+ (afunc_t) 0,
+ (afunc_t) 0,
+ bind_array_length,
+ bind_array_lookup,
+ bind_array_exists,
+ bind_array_clear,
+ bind_array_remove,
+ bind_array_list,
+ null_afunc, /* copy */
+ null_afunc, /* dump */
+ bind_array_store,
+};
+
+enum { INIT, FINI, COUNT, EXISTS, LOOKUP,
+ STORE, DELETE, CLEAR, FETCHALL
+};
+
+static const char *const bfn[] = {
+ "init", "fini", "count", "exists", "lookup",
+ "store", "delete", "clear", "fetchall",
+};
+
+typedef struct {
+ NODE *func[sizeof(bfn)/sizeof(char *)];
+ NODE *arg0;
+} array_t;
+
+static NODE *call_func(NODE *func, NODE **arg, int arg_count);
+static long array_func_call(NODE *, NODE *, int);
+
+
+/* bind_array_length -- find the number of elements in the array */
+
+static NODE **
+bind_array_length(NODE *symbol, NODE *subs ATTRIBUTE_UNUSED)
+{
+ static NODE *length_node;
+
+ symbol->table_size = array_func_call(symbol, NULL, COUNT);
+ length_node = symbol;
+ return & length_node;
+}
+
+/* bind_array_lookup --- find element in the array; return a pointer to value. */
+
+static NODE **
+bind_array_lookup(NODE *symbol, NODE *subs)
+{
+ NODE *xn = symbol->xarray;
+ (void) array_func_call(symbol, subs, LOOKUP);
+ return xn->alookup(xn, subs);
+}
+
+/*
+ * bind_array_exists --- test whether the array element symbol[subs] exists or not,
+ * return pointer to value if it does.
+ */
+
+static NODE **
+bind_array_exists(NODE *symbol, NODE *subs)
+{
+ NODE *xn = symbol->xarray;
+ (void) array_func_call(symbol, subs, EXISTS);
+ return xn->aexists(xn, subs);
+}
+
+/* bind_array_clear --- flush all the values in symbol[] */
+
+static NODE **
+bind_array_clear(NODE *symbol, NODE *subs ATTRIBUTE_UNUSED)
+{
+ NODE *xn = symbol->xarray;
+ (void) xn->aclear(xn, NULL);
+ (void) array_func_call(symbol, NULL, CLEAR);
+ return NULL;
+}
+
+/* bind_array_remove --- if subs is already in the table, remove it. */
+
+static NODE **
+bind_array_remove(NODE *symbol, NODE *subs)
+{
+ NODE *xn = symbol->xarray;
+ (void) xn->aremove(xn, subs);
+ (void) array_func_call(symbol, subs, DELETE);
+ return NULL;
+}
+
+/* bind_array_store --- update the value for the SUBS */
+
+static NODE **
+bind_array_store(NODE *symbol, NODE *subs)
+{
+ (void) array_func_call(symbol, subs, STORE);
+ return NULL;
+}
+
+/* bind_array_list --- return a list of array items */
+
+static NODE**
+bind_array_list(NODE *symbol, NODE *akind)
+{
+ NODE *xn = symbol->xarray;
+ (void) array_func_call(symbol, NULL, FETCHALL);
+ return xn->alist(xn, akind);
+}
+
+
+/* array_func_call --- call user-defined array routine */
+
+static long
+array_func_call(NODE *symbol, NODE *arg1, int fi)
+{
+ NODE *argp[3];
+ NODE *retval;
+ long ret;
+ int i = 0;
+ array_t *aq;
+
+ aq = symbol->a_opaque;
+ if (! aq) /* an array routine invoked from the same or another routine */
+ fatal(_("bind_array: cannot access bound array, operation not allowed"));
+ symbol->a_opaque = NULL; /* avoid infinite recursion */
+
+ argp[i++] = symbol->xarray;
+ argp[i++] = aq->arg0;
+ if (arg1 != NULL)
+ argp[i++] = arg1;
+
+ retval = call_func(aq->func[fi], argp, i);
+ symbol->a_opaque = aq;
+ force_number(retval);
+ ret = get_number_si(retval);
+ unref(retval);
+ if (ret < 0) {
+ if (ERRNO_node->var_value->stlen > 0)
+ fatal(_("%s"), ERRNO_node->var_value->stptr);
+ else
+ fatal(_("unknown reason"));
+ }
+ return ret;
+}
+
+/* do_bind_array --- bind an array to user-defined functions */
+
+static NODE *
+do_bind_array(int nargs)
+{
+ NODE *symbol, *xn, *t, *td;
+ int i;
+ array_t *aq;
+ char *aname;
+
+ symbol = get_array_argument(0, false);
+ if (symbol->array_funcs == bind_array_func)
+ fatal(_("bind_array: array `%s' already bound"), array_vname(symbol));
+
+ assoc_clear(symbol);
+
+ emalloc(aq, array_t *, sizeof(array_t), "do_bind_array");
+ memset(aq, '\0', sizeof(array_t));
+
+ t = get_array_argument(1, false);
+
+ for (i = 0; i < sizeof(bfn)/sizeof(char *); i++) {
+ NODE *subs, *val, *f;
+
+ subs = make_string(bfn[i], strlen(bfn[i]));
+ val = in_array(t, subs);
+ unref(subs);
+ if (val == NULL) {
+ if (i != INIT && i != FINI)
+ fatal(_("bind_array: array element `%s[\"%s\"]' not defined"),
+ t->vname, bfn[i]);
+ continue;
+ }
+
+ force_string(val);
+ f = lookup(val->stptr);
+ if (f == NULL || f->type != Node_func)
+ fatal(_("bind_array: function `%s' is not defined"), val->stptr);
+ aq->func[i] = f;
+ }
+
+ /* copy the array -- this is passed as the second argument to the functions */
+ emalloc(aname, char *, strlen(t->vname) + 2, "do_bind_array");
+ aname[0] = '~'; /* any illegal character */
+ strcpy(& aname[1], symbol->vname);
+ td = make_array();
+ td->vname = aname;
+ assoc_copy(t, td);
+ aq->arg0 = td;
+
+ /* internal array for the actual storage */
+ xn = make_array();
+ xn->vname = symbol->vname; /* shallow copy */
+ xn->flags |= XARRAY;
+ symbol->a_opaque = aq;
+ symbol->array_funcs = bind_array_func;
+ symbol->xarray = xn;
+
+ if (aq->func[INIT] != NULL)
+ (void) array_func_call(symbol, NULL, INIT);
+
+ return make_number(0);
+}
+
+/* do_unbind_array --- unbind an array */
+
+static NODE *
+do_unbind_array(int nargs)
+{
+ NODE *symbol, *xn, *td;
+ array_t *aq;
+
+ symbol = get_array_argument(0, false);
+ if (symbol->array_funcs != bind_array_func)
+ fatal(_("unbind_array: `%s' is not a bound array"), array_vname(symbol));
+
+ aq = symbol->a_opaque;
+ if (aq->func[FINI] != NULL)
+ (void) array_func_call(symbol, NULL, FINI);
+
+ td = aq->arg0;
+ assoc_clear(td);
+ efree(td->vname);
+ freenode(td);
+ efree(aq);
+
+ /* promote xarray to symbol */
+ xn = symbol->xarray;
+ xn->flags &= ~XARRAY;
+ xn->parent_array = symbol->parent_array;
+ *symbol = *xn;
+ freenode(xn);
+
+ return make_number(0);
+}
+
+
+/* call_func --- call a user-defined gawk function */
+
+static NODE *
+call_func(NODE *func, NODE **arg, int arg_count)
+{
+ NODE *ret;
+ INSTRUCTION *code;
+ extern int currule;
+ int i, save_rule = 0;
+
+ if (arg_count > func->param_cnt)
+ fatal(_("function `%s' called with too many parameters"), func->vname);
+
+ /* make function call instructions */
+ code = bcalloc(Op_func_call, 2, 0);
+ code->func_body = func;
+ code->func_name = NULL; /* not needed, func_body already assigned */
+ (code + 1)->expr_count = arg_count;
+ code->nexti = bcalloc(Op_stop, 1, 0);
+
+ save_rule = currule; /* save current rule */
+ currule = 0;
+
+ /* push arguments onto stack */
+ for (i = 0; i < arg_count; i++) {
+ if (arg[i]->type == Node_val)
+ UPREF(arg[i]);
+ PUSH(arg[i]);
+ }
+
+ /* execute the function */
+ (void) interpret(code);
+
+ ret = POP_SCALAR(); /* the return value of the function */
+
+ /* restore current rule */
+ currule = save_rule;
+
+ /* free code */
+ bcfree(code->nexti);
+ bcfree(code);
+
+ return ret;
+}
+
+
+/* dlload --- load this library */
+
+NODE *
+dlload(NODE *obj, void *dl)
+{
+ make_old_builtin("bind_array", do_bind_array, 2);
+ make_old_builtin("unbind_array", do_unbind_array, 1);
+ return make_number((AWKNUM) 0);
+}
diff --git a/old-extension/dbarray.awk b/old-extension/dbarray.awk
new file mode 100644
index 00000000..e0a3c093
--- /dev/null
+++ b/old-extension/dbarray.awk
@@ -0,0 +1,222 @@
+# dbarray.awk -- persistent array with sqlite database backend
+
+# @load "bindarr"
+
+BEGIN {
+ extension("bindarr")
+}
+
+function _db_count(symbol, sq,
+ sth, ret, count)
+{
+ sth = sq["sqlc"]
+ printf "SELECT count(col1) FROM %s;\n", sq["table"] |& sth
+ close(sth, "to")
+ ret = (sth |& getline count)
+ if (close(sth) != 0 || ret <= 0)
+ return -1
+ return count
+}
+
+function _db_exists(symbol, sq, subs,
+ sth, ret, row, qsubs)
+{
+ if (! (subs in symbol)) {
+ sth = sq["sqlc"]
+
+ # double up single quotes
+ qsubs = gensub(/'/, "''", "g", subs)
+
+ printf "SELECT col2 FROM %s WHERE col1='%s';\n", sq["table"], qsubs |& sth
+ close(sth, "to")
+ ret = (sth |& getline row)
+ if (close(sth) != 0 || ret < 0)
+ return -1
+ if (ret == 0) # non-existent row
+ return 0
+ if (row == sq["null"])
+ symbol[subs] # install null string as value
+ else
+ symbol[subs] = row
+ }
+ return 0
+}
+
+function _db_lookup(symbol, sq, subs,
+ sth, ret, row, qsubs)
+{
+ if (! (subs in symbol)) {
+ sth = sq["sqlc"]
+
+ # double up single quotes
+ qsubs = gensub(/'/, "''", "g", subs)
+
+ printf "SELECT col2 FROM %s WHERE col1='%s';\n", sq["table"], qsubs |& sth
+ close(sth, "to")
+ ret = (sth |& getline row)
+ if (close(sth) != 0 || ret < 0)
+ return -1
+
+ if (ret > 0) {
+ if (row == sq["null"])
+ symbol[subs] # install null string as value
+ else
+ symbol[subs] = row
+ } else {
+ # Not there, install it with NULL as value
+ printf "INSERT INTO %s (col1) VALUES('%s');\n", sq["table"], qsubs |& sth
+ close(sth, "to")
+ ret = (sth |& getline)
+ if (close(sth) != 0 || ret < 0)
+ return -1
+ }
+ }
+ return 0
+}
+
+function _db_clear(symbol, sq,
+ sth, ret)
+{
+ sth = sq["sqlc"]
+ printf "DELETE FROM %s;\n", sq["table"] |& sth
+ close(sth, "to")
+ ret = (sth |& getline)
+ if (close(sth) != 0 || ret < 0)
+ return -1
+ return 0
+}
+
+function _db_delete(symbol, sq, subs,
+ sth, ret, qsubs)
+{
+ sth = sq["sqlc"]
+ qsubs = gensub(/'/, "''", "g", subs)
+ printf "DELETE FROM %s WHERE col1='%s';\n", sq["table"], qsubs |& sth
+ close(sth, "to")
+ ret = (sth |& getline)
+ if (close(sth) != 0 || ret < 0)
+ return -1
+ return 0
+}
+
+function _db_store(symbol, sq, subs,
+ sth, ret, qsubs, qval)
+{
+ sth = sq["sqlc"]
+
+ qval = gensub(/'/, "''", "g", symbol[subs])
+ qsubs = gensub(/'/, "''", "g", subs)
+ printf "UPDATE %s SET col2='%s' WHERE col1='%s';\n", \
+ sq["table"], qval, qsubs |& sth
+ close(sth, "to")
+ ret = (sth |& getline)
+ if (close(sth) != 0 || ret < 0)
+ return -1
+ return 0
+}
+
+function _db_fetchall(symbol, sq,
+ sth, ret, save_RS, save_FS)
+{
+ sth = sq["sqlc2"]
+
+ if (! sq["loaded"]) {
+ printf "SELECT col1, col2 FROM %s;\n", sq["table"] |& sth
+ close(sth, "to")
+ save_RS = RS
+ save_FS = FS
+ RS = "\n\n"
+ FS = "\n"
+ while ((ret = (sth |& getline)) > 0) {
+ sub(/^ *col1 = /, "", $1)
+ sub(/^ *col2 = /, "", $2)
+ if ($2 == sq["null"])
+ symbol[$1] # install null string as value
+ else
+ symbol[$1] = $2
+ }
+ RS = save_RS
+ FS = save_FS
+ if (ret < 0 || close(sth) != 0)
+ return -1
+ sq["loaded"] = 1
+ }
+}
+
+
+function _db_init(symbol, sq,
+ sth, table, ret)
+{
+ sth = sq["sqlc"]
+ table = sq["table"]
+
+ # check if table exists
+ printf ".tables %s\n", table |& sth
+ close(sth, "to")
+ ret = (sth |& getline)
+ if (close(sth) != 0 || ret < 0)
+ return -1
+ if (ret > 0 && $0 == table) {
+ # verify schema
+ printf ".schema %s\n", table |& sth
+ close(sth, "to")
+ ret = (sth |& getline)
+ if (close(sth) != 0 || ret <= 0)
+ return -1
+ if ($0 !~ /\(col1 TEXT PRIMARY KEY, col2 TEXT\)/) {
+ printf "table %s: Invalid column name or type(s)\n", table > "/dev/stderr"
+ return -1
+ }
+ } else {
+ # table does not exist, create it.
+ printf "CREATE TABLE %s (col1 TEXT PRIMARY KEY, col2 TEXT);\n", table |& sth
+ close(sth, "to")
+ ret = (sth |& getline)
+ if (close(sth) != 0 || ret < 0)
+ return -1
+ }
+ return 0
+}
+
+#function _db_fini(tie, a, subs) {}
+
+function db_bind(arr, database, table, sq)
+{
+ if (! database) {
+ print "db_bind: must specify a database name" > "/dev/stderr"
+ exit(1)
+ }
+
+ if (! table) {
+ print "db_bind: must specify a table name" > "/dev/stderr"
+ exit(1)
+ }
+
+ # string used by the sqlite3 client to represent NULL
+ sq["null"] = "(null)"
+
+ sq["sqlc"] = sprintf("sqlite3 -nullvalue '%s' %s", sq["null"], database)
+ # sqlite command used in _db_fetchall
+ sq["sqlc2"] = sprintf("sqlite3 -line -nullvalue '%s' %s", sq["null"], database)
+
+ sq["table"] = table
+
+ # register our array routines
+ sq["init"] = "_db_init"
+ sq["count"] = "_db_count"
+ sq["exists"] = "_db_exists"
+ sq["lookup"] = "_db_lookup"
+ sq["delete"] = "_db_delete"
+ sq["store"] = "_db_store"
+ sq["clear"] = "_db_clear"
+ sq["fetchall"] = "_db_fetchall"
+
+# sq["fini"] = "_db_fini";
+
+ bind_array(arr, sq)
+}
+
+function db_unbind(arr)
+{
+ unbind_array(arr)
+}
diff --git a/old-extension/fileop.c b/old-extension/fileop.c
new file mode 100644
index 00000000..86f62576
--- /dev/null
+++ b/old-extension/fileop.c
@@ -0,0 +1,394 @@
+/*
+ * fileop.c -- Builtin functions for binary I/O and other interfaces to
+ * the filesystem.
+ */
+
+/*
+ * Copyright (C) 2012 the Free Software Foundation, Inc.
+ *
+ * This file is part of GAWK, the GNU implementation of the
+ * AWK Programming Language.
+ *
+ * GAWK is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GAWK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "awk.h"
+
+int plugin_is_GPL_compatible;
+
+typedef struct file_struct {
+ struct file_struct *next;
+ FILE *fp;
+ int flags;
+ char path[1];
+} file_t;
+
+static file_t *files;
+static file_t *file_open(const char *builtin_name, int nargs, int do_open);
+static int mode2flags(const char *mode);
+
+/* do_fread --- read from file */
+
+static NODE *
+do_fread(int nargs)
+{
+ NODE *arg;
+ size_t rlen, count;
+ file_t *f;
+ char *rbuf;
+
+ f = file_open("fread", nargs, true);
+
+ arg = get_scalar_argument(2, false);
+ force_number(arg);
+ rlen = get_number_ui(arg);
+
+ emalloc(rbuf, char *, rlen + 2, "do_fread");
+ if ((count = fread(rbuf, 1, rlen, f->fp)) < rlen) {
+ if (! feof(f->fp))
+ update_ERRNO_int(errno);
+ }
+ return make_str_node(rbuf, count, ALREADY_MALLOCED);
+}
+
+/* do_fwrite --- write to file */
+
+static NODE *
+do_fwrite(int nargs)
+{
+ NODE *arg;
+ file_t *f;
+ size_t count = 0;
+
+ f = file_open("fwrite", nargs, true);
+
+ arg = get_scalar_argument(2, false);
+ force_string(arg);
+ if (arg->stlen > 0) {
+ count = fwrite(arg->stptr, 1, arg->stlen, f->fp);
+ if (count < arg->stlen)
+ update_ERRNO_int(errno);
+ }
+ return make_number(count);
+}
+
+/* do_fseek --- set the file position indicator */
+
+static NODE *
+do_fseek(int nargs)
+{
+ NODE *arg;
+ long offset;
+ file_t *f;
+ int whence = 0, ret = 0;
+
+ f = file_open("fseek", nargs, true);
+
+ arg = get_scalar_argument(2, false);
+ force_number(arg);
+ offset = get_number_si(arg);
+
+ arg = get_scalar_argument(3, false);
+ force_string(arg);
+ if (strcasecmp(arg->stptr, "SEEK_SET") == 0)
+ whence = SEEK_SET;
+ else if (strcasecmp(arg->stptr, "SEEK_CUR") == 0)
+ whence = SEEK_CUR;
+ else if (strcasecmp(arg->stptr, "SEEK_END") == 0)
+ whence = SEEK_END;
+ else
+ fatal(_("fseek: `%.*s' is not a valid 4th argument"),
+ (int) arg->stlen, arg->stptr);
+
+ if (fseek(f->fp, offset, whence) < 0) {
+ update_ERRNO_int(errno);
+ ret = -1;
+ }
+ return make_number(ret);
+}
+
+/* do_ftruncate --- truncate the file to a specified length */
+
+static NODE *
+do_ftruncate(int nargs)
+{
+ NODE *arg;
+ file_t *f;
+ off_t len;
+ int ret = 0;
+
+ f = file_open("ftruncate", nargs, true);
+ arg = get_scalar_argument(2, false);
+ force_number(arg);
+ len = (off_t) get_number_si(arg);
+ if (ftruncate(fileno(f->fp), len) < 0) {
+ update_ERRNO_int(errno);
+ ret = -1;
+ }
+ return make_number(ret);
+}
+
+/* do_unlink --- delete the name from the filesystem */
+
+static NODE *
+do_unlink(int nargs)
+{
+ NODE *file;
+ int ret = 0;
+
+ file = get_scalar_argument(0, false);
+ force_string(file);
+ if (file->stlen == 0)
+ fatal(_("unlink: filename has empty string value"));
+ if (unlink(file->stptr) < 0) {
+ update_ERRNO_int(errno);
+ ret = -1;
+ }
+ return make_number(ret);
+}
+
+/* do_flush --- flush buffered data to file */
+
+static NODE *
+do_flush(int nargs)
+{
+ file_t *f;
+ int status = -1;
+
+ f = file_open("flush", nargs, false);
+ if (f != NULL) {
+ status = fflush(f->fp);
+ if (status != 0)
+ update_ERRNO_int(errno);
+ }
+ return make_number(status);
+}
+
+/* do_fclose --- close an open file */
+
+static NODE *
+do_fclose(int nargs)
+{
+ file_t *f;
+ int status = -1;
+
+ f = file_open("fclose", nargs, false);
+ if (f != NULL) {
+ status = fclose(f->fp);
+ if (status != 0)
+ update_ERRNO_int(errno);
+ assert(files == f);
+ files = f->next;
+ efree(f);
+ }
+ return make_number(status);
+}
+
+/* do_filesize --- return the size of the file */
+
+static NODE *
+do_filesize(int nargs)
+{
+ NODE *file;
+ struct stat sbuf;
+ AWKNUM d = -1.0;
+
+ file = get_scalar_argument(0, false);
+ force_string(file);
+ if (file->stlen == 0)
+ fatal(_("filesize: filename has empty string value"));
+
+ if (stat(file->stptr, & sbuf) < 0) {
+ update_ERRNO_int(errno);
+ goto ferror;
+ }
+ if ((sbuf.st_mode & S_IFMT) != S_IFREG) {
+ errno = EINVAL;
+ update_ERRNO_int(errno);
+ goto ferror;
+ }
+ d = sbuf.st_size;
+
+ferror:
+ return make_number(d);
+}
+
+/* do_file_exists --- check if path exists in the filesystem */
+
+static NODE *
+do_file_exists(int nargs)
+{
+ NODE *file;
+ struct stat sbuf;
+ int ret = 1;
+
+ file = get_scalar_argument(0, false);
+ force_string(file);
+ if (file->stlen == 0)
+ fatal(_("file_exists: filename has empty string value"));
+
+ if (stat(file->stptr, & sbuf) < 0) {
+ if (errno != ENOENT)
+ update_ERRNO_int(errno);
+ ret = 0;
+ }
+ return make_number(ret);
+}
+
+
+/* file_open --- open a file or find an already opened file */
+
+static file_t *
+file_open(const char *builtin_name, int nargs, int do_open)
+{
+ NODE *file, *mode;
+ file_t *f, *prev;
+ FILE *fp;
+ int flags;
+ char *path;
+
+ if (nargs < 2)
+ cant_happen();
+
+ file = get_scalar_argument(0, false);
+ force_string(file);
+ mode = get_scalar_argument(1, true);
+ force_string(mode);
+
+ if (file->stlen == 0)
+ fatal(_("%s: filename has empty string value"), builtin_name);
+ if (mode->stlen == 0)
+ fatal(_("%s: mode has empty string value"), builtin_name);
+
+ flags = mode2flags(mode->stptr);
+ if (flags < 0)
+ fatal(_("%s: invalid mode `%.*s'"), builtin_name,
+ (int) mode->stlen, mode->stptr);
+
+ path = file->stptr;
+ for (prev = NULL, f = files; f != NULL; prev = f, f = f->next) {
+ if (strcmp(f->path, path) == 0 && f->flags == flags) {
+ /* Move to the head of the list */
+ if (prev != NULL) {
+ prev->next = f->next;
+ f->next = files;
+ files = f;
+ }
+ return f;
+ }
+ }
+
+ if (! do_open) {
+ if (do_lint)
+ lintwarn(_("%s: `%.*s' is not an open file"),
+ builtin_name, (int) file->stlen, file->stptr);
+ return NULL;
+ }
+
+ fp = fopen(path, mode->stptr);
+ if (fp == NULL)
+ fatal(_("%s: cannot open file `%.*s'"),
+ builtin_name, (int) file->stlen, file->stptr);
+
+ os_close_on_exec(fileno(fp), path, "", "");
+
+ emalloc(f, file_t *, sizeof(file_t) + file->stlen + 1, "file_open");
+ memcpy(f->path, path, file->stlen + 1);
+ f->fp = fp;
+ f->flags = flags;
+ f->next = files;
+ files = f;
+ return f;
+}
+
+
+/*
+ * mode2flags --- convert a string mode to an integer flag;
+ * modified from str2mode in io.c.
+ */
+
+static int
+mode2flags(const char *mode)
+{
+ int ret = -1;
+ const char *second;
+
+ if (mode == NULL || mode[0] == '\0')
+ return -1;
+
+ second = & mode[1];
+
+ if (*second == 'b')
+ second++;
+
+ switch(mode[0]) {
+ case 'r':
+ ret = O_RDONLY;
+ if (*second == '+' || *second == 'w')
+ ret = O_RDWR;
+ break;
+
+ case 'w':
+ ret = O_WRONLY|O_CREAT|O_TRUNC;
+ if (*second == '+' || *second == 'r')
+ ret = O_RDWR|O_CREAT|O_TRUNC;
+ break;
+
+ case 'a':
+ ret = O_WRONLY|O_APPEND|O_CREAT;
+ if (*second == '+')
+ ret = O_RDWR|O_APPEND|O_CREAT;
+ break;
+
+ default:
+ ret = -1;
+ }
+ if (ret != -1 && strchr(mode, 'b') != NULL)
+ ret |= O_BINARY;
+ return ret;
+}
+
+
+/* dlload --- load new builtins in this library */
+
+NODE *
+dlload(NODE *tree, void *dl)
+{
+ make_old_builtin("fseek", do_fseek, 4);
+ make_old_builtin("fread", do_fread, 3);
+ make_old_builtin("fwrite", do_fwrite, 3);
+ make_old_builtin("flush", do_flush, 2);
+ make_old_builtin("filesize", do_filesize, 1);
+ make_old_builtin("file_exists", do_file_exists, 1);
+ make_old_builtin("fclose", do_fclose, 2);
+ make_old_builtin("ftruncate", do_ftruncate, 3);
+ make_old_builtin("unlink", do_unlink, 1);
+ return make_number((AWKNUM) 0);
+}
+
+
+/* dlunload --- routine called when exiting */
+
+void
+dlunload()
+{
+ file_t *f;
+ for (f = files; f != NULL; f = f->next) {
+ if (f->fp != NULL) {
+ fclose(f->fp);
+ f->fp = NULL;
+ }
+ }
+}
diff --git a/old-extension/record.awk b/old-extension/record.awk
new file mode 100644
index 00000000..18a3ce48
--- /dev/null
+++ b/old-extension/record.awk
@@ -0,0 +1,252 @@
+# record.awk -- represent fixed-length records in a file as an array.
+# Each element in the array corresponds to a record in the file.
+# The records are numbered starting from 1, and each record read in
+# from the file is cached. If opened using mode "r+",
+# changes to the array are reflected in the file immediately i.e.
+# writing to an element writes the data into the file.
+#
+# Usage:
+# record(r, path [, reclen [, mode]])
+# r -- array to bind
+# path -- filename
+# reclen -- length of each record
+# mode -- "r" for reading (default), "r+" for reading and writing
+#
+# With reclen <= 0, entire file is treated as one record #1.
+#
+# record(r, "data.in", 80, "r+")
+# r[10] = r[1]
+# for (i = 1; i in r; i++)
+# print r[i]
+# delete r[1]
+#
+# See Also: testrecord.sh
+#
+#
+# TODO:
+# * implement deferred writing
+# * limit memory usage for read cache
+# * use fixed size buffer when deleting a record
+#
+
+BEGIN {
+ extension("fileop.so")
+ extension("bindarr.so")
+}
+
+# _record_count --- return the number of records in file
+
+function _record_count(symbol, rd)
+{
+ if (! ("rectot" in rd))
+ rd["rectot"] = ("reclen" in rd) ?
+ int(filesize(rd["path"]) / rd["reclen"]) : 1
+ return rd["rectot"]
+}
+
+# _record_exists --- check if record exists
+
+function _record_exists(symbol, rd, recnum,
+ path, mode, reclen, rectot)
+{
+ path = rd["path"]
+ reclen = ("reclen" in rd) ? rd["reclen"] : filesize(path)
+ mode = rd["mode"]
+ rectot = _record_count(symbol, rd)
+
+ recnum = int(recnum)
+ if (recnum <= 0 || recnum > rectot)
+ return 0
+
+ if (! (recnum in symbol)) {
+ fseek(path, mode, (recnum - 1) * reclen, "SEEK_SET")
+ symbol[recnum] = fread(path, mode, reclen)
+ }
+ return 0
+}
+
+# _record_lookup --- lookup a record
+
+function _record_lookup(symbol, rd, recnum,
+ path, mode, reclen, rectot)
+{
+ path = rd["path"]
+ reclen = ("reclen" in rd) ? rd["reclen"] : filesize(path)
+ mode = rd["mode"]
+ rectot = _record_count(symbol, rd)
+
+ recnum = int(recnum)
+ if (recnum <= 0 || recnum > rectot) {
+ ERRNO = sprintf("record: %s: reference to non-existent record #%d", path, recnum)
+ return -1
+ }
+
+ if (! (recnum in symbol)) {
+ fseek(path, mode, (recnum - 1) * reclen, "SEEK_SET")
+ symbol[recnum] = fread(path, mode, reclen)
+ }
+ return 0
+}
+
+# _record_clear --- remove all records
+
+function _record_clear(symbol, rd,
+ path, mode)
+{
+ path = rd["path"]
+ mode = rd["mode"]
+ if (mode == "r") {
+ ERRNO = sprintf("record: cannot delete record from file `%s' opened only for reading", path)
+ return -1
+ }
+ ftruncate(path, mode, 0)
+ delete rd["reclen"]
+ return 0
+}
+
+# _record_delete --- delete a record from the file
+
+function _record_delete(symbol, rd, recnum,
+ path, mode, reclen, rectot)
+{
+ path = rd["path"]
+ reclen = ("reclen" in rd) ? rd["reclen"] : filesize(path)
+ mode = rd["mode"]
+
+ if (mode == "r") {
+ ERRNO = sprintf("record: cannot delete record from file `%s' opened only for reading", path)
+ return -1
+ }
+
+ recnum = int(recnum)
+ if (! ("reclen" in rd)) {
+ # entire file is record #1
+ ftruncate(path, mode, 0)
+ delete rd["reclen"]
+ return 0
+ }
+
+ sz = filesize(path)
+ rectot = int(sz / reclen)
+
+ recstart = (recnum - 1) * reclen
+ off = sz - (recstart + reclen)
+
+ fseek(path, mode, -off, "SEEK_END")
+ tmp = fread(path, mode, off)
+ fseek(path, mode, recstart, "SEEK_SET")
+ if (fwrite(path, mode, tmp) != length(tmp))
+ return -1
+ flush(path, mode)
+ ftruncate(path, mode, sz - reclen)
+
+ rd["rectot"] = rectot - 1
+ for (i = recnum + 1; i <= rectot; i++) {
+ if (i in symbol) {
+ symbol[i - 1] = symbol[i]
+ delete symbol[i]
+ }
+ }
+ return 0
+}
+
+# _record_store --- write a record to file
+
+function _record_store(symbol, rd, recnum,
+ path, mode, reclen, val)
+{
+ path = rd["path"]
+ reclen = ("reclen" in rd) ? rd["reclen"] : filesize(path)
+ mode = rd["mode"]
+
+ if (mode == "r") {
+ ERRNO = sprintf("record: cannot write to file `%s' opened only for reading", path)
+ return -1
+ }
+
+ recnum = int(recnum)
+ val = symbol[recnum]
+ if (! ("reclen" in rd)) {
+ # the entire file is record #1
+ if (reclen != 0)
+ ftruncate(path, mode, 0)
+ } else if (length(val) != reclen) {
+ ERRNO = sprintf("record: %s: invalid length for record #%d", path, recnum)
+ return -1
+ }
+
+ fseek(path, mode, (recnum - 1) * reclen, "SEEK_SET")
+ if (fwrite(path, mode, val) != length(val))
+ return -1
+ flush(path, mode)
+ return 0
+}
+
+# _record_fetchall --- retrieve all the records
+
+function _record_fetchall(symbol, rd,
+ path, mode, reclen, rectot, recnum)
+{
+ path = rd["path"]
+ reclen = ("reclen" in rd) ? rd["reclen"] : filesize(path)
+ mode = rd["mode"]
+ rectot = _record_count(symbol, rd)
+
+ if (rd["loaded"])
+ return 0
+ for (recnum = 1; recnum <= rectot; recnum++) {
+ if (! (recnum in symbol)) {
+ fseek(path, mode, (recnum - 1) * reclen, "SEEK_SET")
+ symbol[recnum] = fread(path, mode, reclen)
+ }
+ }
+ rd["loaded"] = 1
+ return 0
+}
+
+# _record_init --- initialization routine
+
+function _record_init(symbol, rd)
+{
+ if (! file_exists(rd["path"])) {
+ ERRNO = sprintf("record: cannot open file `%s' for reading", rd["path"])
+ return -1
+ }
+ return 0
+}
+
+# _record_fini --- cleanup routine
+
+function _record_fini(symbol, rd)
+{
+ fclose(rd["path"], rd["mode"])
+}
+
+# record --- bind an array to a file with fixed-length records
+
+function record(array, path, reclen, mode, rd)
+{
+ if (path == "") {
+ print "fatal: record: empty string value for filename" > "/dev/stderr"
+ exit(1)
+ }
+
+ # register our array routines
+ rd["init"] = "_record_init"
+ rd["fini"] = "_record_fini"
+ rd["count"] = "_record_count"
+ rd["exists"] = "_record_exists"
+ rd["lookup"] = "_record_lookup"
+ rd["delete"] = "_record_delete"
+ rd["store"] = "_record_store"
+ rd["clear"] = "_record_clear"
+ rd["fetchall"] = "_record_fetchall"
+
+ rd["path"] = path
+ if (reclen > 0)
+ rd["reclen"] = reclen
+ rd["mode"] = mode == "r+" ? "r+" : "r"
+
+ delete array
+ bind_array(array, rd)
+}
diff --git a/old-extension/sparr.c b/old-extension/sparr.c
new file mode 100644
index 00000000..a3d06e66
--- /dev/null
+++ b/old-extension/sparr.c
@@ -0,0 +1,163 @@
+/*
+ * sparr.c - Example of changing behavior of arrays in gawk.
+ * See testsparr.awk for usage.
+ */
+
+/*
+ * Copyright (C) 2012 the Free Software Foundation, Inc.
+ *
+ * This file is part of GAWK, the GNU implementation of the
+ * AWK Programming Language.
+ *
+ * GAWK is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GAWK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+
+#include "awk.h"
+#include "spec_array.h"
+
+int plugin_is_GPL_compatible;
+
+typedef struct {
+ int load_file;
+ NODE *filename;
+} sdata_t;
+
+/* install_array --- install an array in the symbol table */
+
+static NODE *
+install_array(const char *name)
+{
+ NODE *r;
+
+ r = lookup(name);
+ if (r == NULL)
+ r = install_symbol(estrdup(name, strlen(name)), Node_var_array);
+ switch (r->type) {
+ case Node_var_new:
+ r = force_array(r, false);
+ /* fall through */
+ case Node_var_array:
+ assoc_clear(r);
+ break;
+ default:
+ fatal(_("`%s' is not an array"), name);
+ }
+ return r;
+}
+
+/* fetch_SYS --- fetch routine for the array `SYS' */
+
+static NODE *
+fetch_SYS(NODE *symbol, NODE *subs, void *data)
+{
+ force_string(subs);
+ if (strcmp(subs->stptr, "time") == 0)
+ return do_strftime(0);
+ return NULL;
+}
+
+/* store_SYS --- store routine for the array `SYS' */
+
+static void
+store_SYS(NODE *symbol, NODE *subs, NODE *val, void *data)
+{
+ sdata_t *sd = (sdata_t *) data;
+
+ if (subs != NULL && val != NULL && val->type == Node_val) {
+ force_string(subs);
+ if (strcmp(subs->stptr, "readline") == 0) {
+ sd->load_file = true;
+ unref(sd->filename);
+ sd->filename = dupnode(val);
+ }
+ }
+}
+
+/* load_READLINE --- load routine for the array `READLINE' */
+
+static void
+load_READLINE(NODE *symbol, void *data)
+{
+ sdata_t *sd = (sdata_t *) data;
+ NODE *file, *tmp;
+ FILE *fp;
+ static char linebuf[BUFSIZ];
+ int i;
+ bool long_line = false;
+
+ if (! sd->load_file) /* non-existent SYS["readline"] or already loaded */
+ return;
+
+ file = sd->filename;
+ force_string(file);
+
+ if (file->stlen == 0)
+ return;
+
+ assoc_clear(symbol);
+
+ if ((fp = fopen(file->stptr, "r" )) == NULL) {
+ warning(_("READLINE (%s): %s"), file->stptr, strerror(errno));
+ return;
+ }
+
+ for (i = 1; fgets(linebuf, sizeof(linebuf), fp ) != NULL; i++) {
+ NODE **lhs;
+ size_t sz;
+
+ sz = strlen(linebuf);
+ if (sz > 0 && linebuf[sz - 1] == '\n') {
+ linebuf[sz - 1] = '\0';
+ sz--;
+ if (long_line) {
+ long_line = false;
+ i--;
+ continue;
+ }
+ } else if (long_line) {
+ i--;
+ continue;
+ } else {
+ if (do_lint)
+ lintwarn(_("file `%s' does not end in newline or line # `%d' is too long"),
+ file->stptr, i);
+ long_line = true;
+ }
+
+ tmp = make_number(i);
+ lhs = assoc_lookup(symbol, tmp);
+ unref(tmp);
+ unref(*lhs);
+ *lhs = make_string(linebuf, sz);
+ }
+ fclose(fp);
+ sd->load_file = false; /* don't load this file again */
+}
+
+/* dlload --- load this library */
+
+NODE *
+dlload(NODE *obj, void *dl)
+{
+ NODE *a1, *a2;
+ static sdata_t data;
+
+ a1 = install_array("SYS");
+ register_dyn_array(a1, fetch_SYS, store_SYS, & data);
+ a2 = install_array("READLINE");
+ register_deferred_array(a2, load_READLINE, & data);
+ return make_number((AWKNUM) 0);
+}
diff --git a/old-extension/spec_array.c b/old-extension/spec_array.c
new file mode 100644
index 00000000..78b24018
--- /dev/null
+++ b/old-extension/spec_array.c
@@ -0,0 +1,416 @@
+/*
+ * spec_array.c - Support for specialized associative arrays.
+ */
+
+/*
+ * Copyright (C) 2012 the Free Software Foundation, Inc.
+ *
+ * This file is part of GAWK, the GNU implementation of the
+ * AWK Programming Language.
+ *
+ * GAWK is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GAWK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "awk.h"
+#include "spec_array.h"
+
+typedef struct spec_array {
+ Fetch_func_t fetch_func;
+ Store_func_t store_func;
+ Load_func_t load_func;
+ void *data;
+} array_t;
+
+/*
+ * The array_t structure is attached to the array itself without
+ * the necessity to maintain a list of symbols; this works only
+ * because there is just enough free space in the NODE strcture when
+ * the base array is str_array.
+ */
+
+#define SUPER(F) (*str_array_func[AFUNC(F)])
+
+
+/*
+ * deferred_array --- Deferred loading of array at run-time.
+ *
+ * The load routine takes two arguments, the array and
+ * a void * data:
+ *
+ * void load_func(NODE *array, void *data)
+ *
+ * Use register_deferred_array(array, load_func, void *data) to
+ * bind an array to the load routine.
+ */
+
+static NODE **deferred_array_init(NODE *, NODE *);
+static NODE **deferred_array_lookup(NODE *, NODE *);
+static NODE **deferred_array_exists(NODE *, NODE *);
+static NODE **deferred_array_remove(NODE *, NODE *);
+static NODE **deferred_array_clear(NODE *, NODE *);
+static NODE **deferred_array_list(NODE *, NODE *);
+static NODE **deferred_array_copy(NODE *, NODE *);
+static NODE **deferred_array_length(NODE *, NODE *);
+
+static afunc_t deferred_array_func[] = {
+ deferred_array_init,
+ (afunc_t) 0, /* typeof */
+ deferred_array_length,
+ deferred_array_lookup,
+ deferred_array_exists,
+ deferred_array_clear,
+ deferred_array_remove,
+ deferred_array_list,
+ deferred_array_copy,
+ null_afunc, /* dump */
+ (afunc_t) 0, /* store */
+};
+
+
+/* deferred_array_init --- called when array becomes empty, e.g: delete BOUND_ARRAY */
+
+static NODE **
+deferred_array_init(NODE *symbol, NODE *subs)
+{
+ if (symbol != NULL) {
+ array_t *av = (array_t *) symbol->xarray;
+ symbol->xarray = NULL; /* this is to avoid an assertion failure in null_array */
+ null_array(symbol); /* typeless empty array */
+ if (symbol->parent_array == NULL) {
+ /* main array */
+ symbol->array_funcs = deferred_array_func; /* restore type */
+ symbol->xarray = (NODE *) av;
+ } else if (av) /* sub-array */
+ efree(av);
+ }
+ return NULL;
+}
+
+/* deferred_array_length --- get the length of the array */
+
+static NODE **
+deferred_array_length(NODE *symbol, NODE *subs)
+{
+ static NODE *length_node;
+ array_t *av = (array_t *) symbol->xarray;
+ if (av) {
+ symbol->xarray = NULL;
+ (*av->load_func)(symbol, av->data);
+ symbol->xarray = (NODE *) av;
+ }
+ length_node = symbol;
+ return & length_node;
+}
+
+#define DEF_ARR(F) static NODE ** \
+deferred_array_##F(NODE *symbol, NODE *subs) \
+{ \
+ array_t *av = (array_t *) symbol->xarray; \
+ if (av) { \
+ symbol->xarray = NULL; \
+ (*av->load_func)(symbol, av->data); \
+ symbol->xarray = (NODE *) av; \
+ } \
+ return SUPER(a##F)(symbol, subs); \
+}
+
+/* the rest of the routines */
+
+DEF_ARR(exists)
+DEF_ARR(lookup)
+DEF_ARR(list)
+DEF_ARR(copy)
+
+#undef DEF_ARR
+
+/* deferred_array_remove --- remove the index from the array */
+
+static NODE **
+deferred_array_remove(NODE *symbol, NODE *subs)
+{
+ array_t *av = (array_t *) symbol->xarray;
+
+ (void) SUPER(aremove)(symbol, subs);
+ if (av) {
+ symbol->xarray = NULL;
+ (*av->load_func)(symbol, av->data);
+ symbol->xarray = (NODE *) av;
+ }
+ return NULL;
+}
+
+/* deferred_array_clear --- flush all the values in symbol[] */
+
+static NODE **
+deferred_array_clear(NODE *symbol, NODE *subs)
+{
+ array_t *av = (array_t *) symbol->xarray;
+
+ (void) SUPER(aclear)(symbol, subs);
+ if (av) {
+ symbol->xarray = NULL;
+ (*av->load_func)(symbol, av->data);
+ symbol->xarray = (NODE *) av;
+ }
+ return NULL;
+}
+
+
+/*
+ * dyn_array --- array with triggers for reading and writing
+ * an element.
+ *
+ * The fetch routine should expect three arguments, the array,
+ * the subscript and optional void * data. It should return the value
+ * if it exists or NULL otherwise.
+ *
+ * NODE *fetch_func(NODE *array, NODE *subs, void *data)
+ *
+ * The store routine must take an additional argument for the
+ * value. The value can be NULL if the specific element is
+ * removed from the array. The subscript (and the value) is NULL
+ * when the entire array is deleted.
+ *
+ * void store_func(NODE *array, NODE *subs, NODE *value, void *data)
+ *
+ * Use register_dyn_array(array, fetch_func, store_func, void *data) to
+ * bind an array to the fetch/store routine.
+ */
+
+
+static NODE **dyn_array_init(NODE *, NODE *);
+static NODE **dyn_array_lookup(NODE *, NODE *);
+static NODE **dyn_array_exists(NODE *, NODE *);
+static NODE **dyn_array_remove(NODE *, NODE *);
+static NODE **dyn_array_clear(NODE *, NODE *);
+static NODE **dyn_array_list(NODE *, NODE *);
+static NODE **dyn_array_copy(NODE *, NODE *);
+static NODE **dyn_array_store(NODE *, NODE *);
+
+static afunc_t dyn_array_func[] = {
+ dyn_array_init,
+ (afunc_t) 0, /* typeof */
+ null_length, /* length */
+ dyn_array_lookup,
+ dyn_array_exists,
+ dyn_array_clear,
+ dyn_array_remove,
+ dyn_array_list,
+ dyn_array_copy,
+ null_afunc, /* dump */
+ dyn_array_store,
+};
+
+/* dyn_array_init --- called when array becomes empty */
+
+static NODE **
+dyn_array_init(NODE *symbol, NODE *subs)
+{
+ if (symbol != NULL) {
+ array_t *av = (array_t *) symbol->xarray;
+ symbol->xarray = NULL;
+ null_array(symbol); /* typeless empty array */
+ if (symbol->parent_array == NULL) {
+ /* main array */
+ symbol->array_funcs = dyn_array_func; /* restore type */
+ symbol->xarray = (NODE *) av;
+ } else if (av) /* sub-array */
+ efree(av);
+ }
+ return NULL;
+}
+
+/* dyn_array_exists --- check if the SUBS exists */
+
+static NODE **
+dyn_array_exists(NODE *symbol, NODE *subs)
+{
+ NODE *r;
+ array_t *av = (array_t *) symbol->xarray;
+
+ if (av && av->fetch_func) {
+ symbol->xarray = NULL;
+ r = (*av->fetch_func)(symbol, subs, av->data);
+ symbol->xarray = (NODE *) av;
+ if (r != NULL) {
+ NODE **lhs;
+ lhs = SUPER(alookup)(symbol, subs);
+ unref(*lhs);
+ *lhs = r;
+ return lhs;
+ }
+ }
+
+ return SUPER(aexists)(symbol, subs);
+}
+
+/* dyn_array_lookup --- lookup SUBS and return a pointer to store its value */
+
+static NODE **
+dyn_array_lookup(NODE *symbol, NODE *subs)
+{
+ NODE **lhs;
+ NODE *r;
+ array_t *av = (array_t *) symbol->xarray;
+
+ lhs = SUPER(alookup)(symbol, subs);
+ if (av && av->fetch_func) {
+ symbol->xarray = NULL;
+ r = (*av->fetch_func)(symbol, subs, av->data);
+ symbol->xarray = (NODE *) av;
+ if (r != NULL) {
+ unref(*lhs);
+ *lhs = r;
+ }
+ }
+ return lhs;
+}
+
+/* dyn_array_store --- call the store routine after an assignment */
+
+static NODE **
+dyn_array_store(NODE *symbol, NODE *subs)
+{
+ array_t *av = (array_t *) symbol->xarray;
+
+ if (av && av->store_func) {
+ NODE **lhs;
+ lhs = SUPER(aexists)(symbol, subs);
+ symbol->xarray = NULL;
+ (*av->store_func)(symbol, subs, *lhs, av->data);
+ symbol->xarray = (NODE *) av;
+ }
+ return NULL;
+}
+
+/* dyn_array_remove --- remove the index from the array */
+
+static NODE **
+dyn_array_remove(NODE *symbol, NODE *subs)
+{
+ array_t *av = (array_t *) symbol->xarray;
+
+ (void) SUPER(aremove)(symbol, subs);
+ if (av && av->store_func) {
+ symbol->xarray = NULL;
+ (*av->store_func)(symbol, subs, NULL, av->data);
+ symbol->xarray = (NODE *) av;
+ }
+ return NULL;
+}
+
+/* dyn_array_clear --- flush all the values in symbol[] */
+
+static NODE **
+dyn_array_clear(NODE *symbol, NODE *subs)
+{
+ array_t *av = (array_t *) symbol->xarray;
+
+ (void) SUPER(aclear)(symbol, subs);
+ if (av && av->store_func) {
+ symbol->xarray = NULL;
+ (*av->store_func)(symbol, NULL, NULL, av->data);
+ symbol->xarray = (NODE *) av;
+ }
+ return NULL;
+}
+
+/* dyn_array_list --- return a list of items in symbol[] */
+
+static NODE **
+dyn_array_list(NODE *symbol, NODE *subs)
+{
+ return SUPER(alist)(symbol, subs);
+}
+
+/* dyn_array_copy --- duplicate the array */
+
+static NODE **
+dyn_array_copy(NODE *symbol, NODE *subs)
+{
+ return SUPER(acopy)(symbol, subs);
+}
+
+/* register_array_s --- attach the specified routine(s) to an array */
+
+static void
+register_array_s(NODE *symbol, Fetch_func_t fetch_func,
+ Store_func_t store_func, Load_func_t load_func, void *data)
+{
+ array_t *av;
+
+ if (symbol->type != Node_var_array)
+ fatal(_("register_array_s: argument is not an array"));
+
+ if (symbol->array_funcs == deferred_array_func
+ || symbol->array_funcs == dyn_array_func)
+ fatal(_("register_array_s: `%s' already is a deferred/dyn array"),
+ array_vname(symbol));
+
+ assoc_clear(symbol);
+ assert(symbol->xarray == NULL);
+ emalloc(av, array_t *, sizeof (array_t), "register_spec_array");
+ av->fetch_func = fetch_func;
+ av->store_func = store_func;
+ av->load_func = load_func;
+ av->data = data;
+ symbol->xarray = (NODE *) av;
+}
+
+/* register_deferred_array --- make the array to be loaded at run-time */
+
+void
+register_deferred_array(NODE *symbol, Load_func_t load_func, void *dq)
+{
+ if (! load_func)
+ fatal(_("register_deferred_array: null load function"));
+ register_array_s(symbol, 0, 0, load_func, dq);
+ symbol->array_funcs = deferred_array_func;
+}
+
+/* register_dyn_array --- attach read and write triggers to an array */
+
+void
+register_dyn_array(NODE *symbol, Fetch_func_t fetch_func,
+ Store_func_t store_func, void *dq)
+{
+ register_array_s(symbol, fetch_func, store_func, 0, dq);
+ symbol->array_funcs = dyn_array_func;
+}
+
+/* unregister_array_s --- un-special the array */
+
+void *
+unregister_array_s(NODE *symbol)
+{
+ void *data = NULL;
+ if (symbol->type != Node_var_array)
+ fatal(_("unregister_array_s: argument is not an array"));
+
+ if (symbol->array_funcs == dyn_array_func
+ || symbol->array_funcs == deferred_array_func
+ ) {
+ array_t *av;
+
+ av = (array_t *) symbol->xarray;
+ assert(av != NULL);
+ data = av->data;
+ efree(av);
+ symbol->array_funcs = str_array_func;
+ symbol->xarray = NULL;
+ /* FIXME: do we assoc_clear the array ? */
+ }
+ return data;
+}
diff --git a/old-extension/spec_array.h b/old-extension/spec_array.h
new file mode 100644
index 00000000..f75fc7ce
--- /dev/null
+++ b/old-extension/spec_array.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2012 the Free Software Foundation, Inc.
+ *
+ * This file is part of GAWK, the GNU implementation of the
+ * AWK Programming Language.
+ *
+ * GAWK is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GAWK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+
+typedef NODE *(*Fetch_func_t)(NODE *, NODE *, void *);
+typedef void(*Store_func_t)(NODE *, NODE *, NODE *, void *);
+typedef void(*Load_func_t)(NODE *, void *);
+extern void register_dyn_array(NODE *, Fetch_func_t, Store_func_t, void *);
+extern void register_deferred_array(NODE *, Load_func_t, void *);
+extern void *unregister_array_s(NODE *);
diff --git a/old-extension/steps b/old-extension/steps
new file mode 100755
index 00000000..3e8070d6
--- /dev/null
+++ b/old-extension/steps
@@ -0,0 +1,10 @@
+# what to do under linux to make dl.so
+# Sun Nov 25 21:40:49 IST 2012
+
+gcc -fPIC -shared -Wall -DGAWK -DHAVE_CONFIG_H -c -O -g -I.. spec_array.c
+gcc -fPIC -shared -Wall -DGAWK -DHAVE_CONFIG_H -c -O -g -I.. sparr.c
+gcc -fPIC -shared -Wall -DGAWK -DHAVE_CONFIG_H -c -O -g -I.. bindarr.c
+gcc -fPIC -shared -Wall -DGAWK -DHAVE_CONFIG_H -c -O -g -I.. fileop.c
+gcc -o sparr.so -shared sparr.o spec_array.o
+gcc -o bindarr.so -shared bindarr.o
+gcc -o fileop.so -shared fileop.o
diff --git a/old-extension/testdbarray.awk b/old-extension/testdbarray.awk
new file mode 100644
index 00000000..fd7fd595
--- /dev/null
+++ b/old-extension/testdbarray.awk
@@ -0,0 +1,21 @@
+@include "dbarray.awk"
+
+# $ ../gawk -f testdbarray.awk
+# $ ../gawk -f testdbarray.awk
+# ...
+# $ ../gawk -vINIT=1 -f testdbarray.awk
+
+
+BEGIN {
+ # bind array 'A' to the table 'table_A' in sqlite3 database 'testdb'
+ db_bind(A, "testdb", "table_A")
+
+ if (INIT) # detele table and start over
+ delete A
+
+ lenA = length(A)
+ A[++lenA] = strftime()
+ PROCINFO["sorted_in"] = "@ind_num_asc"
+ for (item in A)
+ print item, ":", A[item]
+}
diff --git a/old-extension/testrecord.sh b/old-extension/testrecord.sh
new file mode 100755
index 00000000..61d1ba76
--- /dev/null
+++ b/old-extension/testrecord.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+AWK=../gawk
+$AWK 'BEGIN { OFS = ORS = ""; for (j = 1; j <= 4; j++) for (i = 1; i <= 16; i++) print j}' > _rec.in
+for i in 1 2 3 4 5
+do
+$AWK -f record.awk -vinfile='_rec.in' -e 'BEGIN {
+reclen = 16
+record(r, infile, reclen, "r+")
+FIELDWIDTHS="8 4 4"
+for (i = 1; i in r; i++) {
+ $0 = r[i]
+ print $1
+}
+delete r[1]
+unbind_array(r)
+print "--" }'
+done
+rm -f _rec.in
diff --git a/old-extension/testsparr.awk b/old-extension/testsparr.awk
new file mode 100644
index 00000000..648a21a2
--- /dev/null
+++ b/old-extension/testsparr.awk
@@ -0,0 +1,18 @@
+# ../gawk -lsparr -f testsparr.awk
+BEGIN {
+ extension("sparr")
+ print SYS["time"]
+ SYS["readline"] = "sparr.c";
+ printf("File %s has %d lines\n", SYS["readline"], length(READLINE))
+ SYS["readline"] = "testsparr.awk";
+ printf("File %s has %d lines\n", SYS["readline"], length(READLINE))
+ for (i = 1; i in READLINE; i++)
+ print READLINE[i]
+
+ system("sleep 1")
+
+# PROCINFO["/dev/stdin", "READ_TIMEOUT"] = 1000
+# getline < "/dev/stdin"
+
+ print SYS["time"]
+}