aboutsummaryrefslogtreecommitdiffstats
path: root/extension/rwarray0.c
diff options
context:
space:
mode:
Diffstat (limited to 'extension/rwarray0.c')
-rw-r--r--extension/rwarray0.c475
1 files changed, 475 insertions, 0 deletions
diff --git a/extension/rwarray0.c b/extension/rwarray0.c
new file mode 100644
index 00000000..c50c6a38
--- /dev/null
+++ b/extension/rwarray0.c
@@ -0,0 +1,475 @@
+/*
+ * rwarray.c - Builtin functions to binary read / write arrays to a file.
+ *
+ * Arnold Robbins
+ * May 2009
+ * Redone June 2012
+ */
+
+/*
+ * Copyright (C) 2009, 2010, 2011, 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "gawkapi.h"
+
+#include "gettext.h"
+#define _(msgid) gettext(msgid)
+#define N_(msgid) msgid
+
+#define MAGIC "awkrulz\n"
+#define MAJOR 3
+#define MINOR 0
+
+static const gawk_api_t *api; /* for convenience macros to work */
+static awk_ext_id_t *ext_id;
+static const char *ext_version = "rwarray0 extension: version 1.0";
+static awk_bool_t (*init_func)(void) = NULL;
+
+int plugin_is_GPL_compatible;
+
+static awk_bool_t write_array(int fd, awk_array_t array);
+static awk_bool_t write_elem(int fd, awk_element_t *element);
+static awk_bool_t write_value(int fd, awk_value_t *val);
+
+static awk_bool_t read_array(int fd, awk_array_t array);
+static awk_bool_t read_elem(int fd, awk_element_t *element);
+static awk_bool_t read_value(int fd, awk_value_t *value);
+
+/*
+ * Format of array info:
+ *
+ * MAGIC 8 bytes
+ * Major version 4 bytes - network order
+ * Minor version 4 bytes - network order
+ * Element count 4 bytes - network order
+ * Elements
+ *
+ * For each element:
+ * Length of index val: 4 bytes - network order
+ * Index val as characters (N bytes)
+ * Value type 4 bytes (0 = string, 1 = number, 2 = array)
+ * IF string:
+ * Length of value 4 bytes
+ * Value as characters (N bytes)
+ * ELSE IF number:
+ * 8 bytes as native double
+ * ELSE
+ * Element count
+ * Elements
+ * END IF
+ */
+
+/* do_writea --- write an array */
+
+static awk_value_t *
+do_writea(int nargs, awk_value_t *result)
+{
+ awk_value_t filename, array;
+ int fd = -1;
+ uint32_t major = MAJOR;
+ uint32_t minor = MINOR;
+
+ assert(result != NULL);
+ make_number(0.0, result);
+
+ if (do_lint && nargs > 2)
+ lintwarn(ext_id, _("writea: called with too many arguments"));
+
+ if (nargs < 2)
+ goto out;
+
+ /* directory is first arg, array to dump is second */
+ if (! get_argument(0, AWK_STRING, & filename)) {
+ fprintf(stderr, _("do_writea: argument 0 is not a string\n"));
+ errno = EINVAL;
+ goto done1;
+ }
+
+ if (! get_argument(1, AWK_ARRAY, & array)) {
+ fprintf(stderr, _("do_writea: argument 1 is not an array\n"));
+ errno = EINVAL;
+ goto done1;
+ }
+
+ /* open the file, if error, set ERRNO and return */
+ fd = creat(filename.str_value.str, 0600);
+ if (fd < 0)
+ goto done1;
+
+ if (write(fd, MAGIC, strlen(MAGIC)) != strlen(MAGIC))
+ goto done1;
+
+ major = htonl(major);
+ if (write(fd, & major, sizeof(major)) != sizeof(major))
+ goto done1;
+
+ minor = htonl(minor);
+ if (write(fd, & minor, sizeof(minor)) != sizeof(minor))
+ goto done1;
+
+ if (write_array(fd, array.array_cookie)) {
+ make_number(1.0, result);
+ goto done0;
+ }
+
+done1:
+ update_ERRNO_int(errno);
+ unlink(filename.str_value.str);
+
+done0:
+ close(fd);
+out:
+ return result;
+}
+
+
+/* write_array --- write out an array or a sub-array */
+
+static awk_bool_t
+write_array(int fd, awk_array_t array)
+{
+ uint32_t i;
+ uint32_t count;
+ awk_flat_array_t *flat_array;
+
+ if (! flatten_array(array, & flat_array)) {
+ fprintf(stderr, _("write_array: could not flatten array\n"));
+ return 0;
+ }
+
+ count = htonl(flat_array->count);
+ if (write(fd, & count, sizeof(count)) != sizeof(count))
+ return 0;
+
+ for (i = 0; i < flat_array->count; i++) {
+ if (! write_elem(fd, & flat_array->elements[i]))
+ return 0;
+ }
+
+ if (! release_flattened_array(array, flat_array)) {
+ fprintf(stderr, _("write_array: could not release flattened array\n"));
+ return 0;
+ }
+
+ return 1;
+}
+
+/* write_elem --- write out a single element */
+
+static awk_bool_t
+write_elem(int fd, awk_element_t *element)
+{
+ uint32_t indexval_len;
+ ssize_t write_count;
+
+ indexval_len = htonl(element->index.str_value.len);
+ if (write(fd, & indexval_len, sizeof(indexval_len)) != sizeof(indexval_len))
+ return 0;
+
+ if (element->index.str_value.len > 0) {
+ write_count = write(fd, element->index.str_value.str,
+ element->index.str_value.len);
+ if (write_count != (ssize_t) element->index.str_value.len)
+ return 0;
+ }
+
+ return write_value(fd, & element->value);
+}
+
+/* write_value --- write a number or a string or a array */
+
+static int
+write_value(int fd, awk_value_t *val)
+{
+ uint32_t code, len;
+
+ if (val->val_type == AWK_ARRAY) {
+ code = htonl(2);
+ if (write(fd, & code, sizeof(code)) != sizeof(code))
+ return 0;
+ return write_array(fd, val->array_cookie);
+ }
+
+ if (val->val_type == AWK_NUMBER) {
+ code = htonl(1);
+ if (write(fd, & code, sizeof(code)) != sizeof(code))
+ return 0;
+
+ if (write(fd, & val->num_value, sizeof(val->num_value)) != sizeof(val->num_value))
+ return 0;
+ } else {
+ code = 0;
+ if (write(fd, & code, sizeof(code)) != sizeof(code))
+ return 0;
+
+ len = htonl(val->str_value.len);
+ if (write(fd, & len, sizeof(len)) != sizeof(len))
+ return 0;
+
+ if (write(fd, val->str_value.str, val->str_value.len)
+ != (ssize_t) val->str_value.len)
+ return 0;
+ }
+
+ return 1;
+}
+
+/* do_reada --- read an array */
+
+static awk_value_t *
+do_reada(int nargs, awk_value_t *result)
+{
+ awk_value_t filename, array;
+ int fd = -1;
+ uint32_t major;
+ uint32_t minor;
+ char magic_buf[30];
+
+ assert(result != NULL);
+ make_number(0.0, result);
+
+ if (do_lint && nargs > 2)
+ lintwarn(ext_id, _("reada: called with too many arguments"));
+
+ if (nargs < 2)
+ goto out;
+
+ /* directory is first arg, array to read is second */
+ if (! get_argument(0, AWK_STRING, & filename)) {
+ fprintf(stderr, _("do_reada: argument 0 is not a string\n"));
+ errno = EINVAL;
+ goto done1;
+ }
+
+ if (! get_argument(1, AWK_ARRAY, & array)) {
+ fprintf(stderr, _("do_reada: argument 1 is not an array\n"));
+ errno = EINVAL;
+ goto done1;
+ }
+
+ fd = open(filename.str_value.str, O_RDONLY);
+ if (fd < 0)
+ goto done1;
+
+ memset(magic_buf, '\0', sizeof(magic_buf));
+ if (read(fd, magic_buf, strlen(MAGIC)) != strlen(MAGIC)) {
+ errno = EBADF;
+ goto done1;
+ }
+
+ if (strcmp(magic_buf, MAGIC) != 0) {
+ errno = EBADF;
+ goto done1;
+ }
+
+ if (read(fd, & major, sizeof(major)) != sizeof(major)) {
+ errno = EBADF;
+ goto done1;
+ }
+ major = ntohl(major);
+
+ if (major != MAJOR) {
+ errno = EBADF;
+ goto done1;
+ }
+
+ if (read(fd, & minor, sizeof(minor)) != sizeof(minor)) {
+ /* read() sets errno */
+ goto done1;
+ }
+
+ minor = ntohl(minor);
+ if (minor != MINOR) {
+ errno = EBADF;
+ goto done1;
+ }
+
+ if (! clear_array(array.array_cookie)) {
+ errno = ENOMEM;
+ fprintf(stderr, _("do_reada: clear_array failed\n"));
+ goto done1;
+ }
+
+ if (read_array(fd, array.array_cookie)) {
+ make_number(1.0, result);
+ goto done0;
+ }
+
+done1:
+ update_ERRNO_int(errno);
+done0:
+ close(fd);
+out:
+ return result;
+}
+
+
+/* read_array --- read in an array or sub-array */
+
+static awk_bool_t
+read_array(int fd, awk_array_t array)
+{
+ uint32_t i;
+ uint32_t count;
+ awk_element_t new_elem;
+
+ if (read(fd, & count, sizeof(count)) != sizeof(count)) {
+ return 0;
+ }
+ count = ntohl(count);
+
+ for (i = 0; i < count; i++) {
+ if (read_elem(fd, & new_elem)) {
+ /* add to array */
+ if (! set_array_element_by_elem(array, & new_elem)) {
+ fprintf(stderr, _("read_array: set_array_element failed\n"));
+ return 0;
+ }
+ } else
+ break;
+ }
+
+ if (i != count)
+ return 0;
+
+ return 1;
+}
+
+/* read_elem --- read in a single element */
+
+static awk_bool_t
+read_elem(int fd, awk_element_t *element)
+{
+ uint32_t index_len;
+ static char *buffer;
+ static uint32_t buflen;
+ ssize_t ret;
+
+ if ((ret = read(fd, & index_len, sizeof(index_len))) != sizeof(index_len)) {
+ return 0;
+ }
+ index_len = ntohl(index_len);
+
+ memset(element, 0, sizeof(*element));
+
+ if (index_len > 0) {
+ if (buffer == NULL) {
+ // allocate buffer
+ emalloc(buffer, char *, index_len, "read_elem");
+ buflen = index_len;
+ } else if (buflen < index_len) {
+ // reallocate buffer
+ char *cp = realloc(buffer, index_len);
+
+ if (cp == NULL)
+ return 0;
+
+ buffer = cp;
+ buflen = index_len;
+ }
+
+ if (read(fd, buffer, index_len) != (ssize_t) index_len) {
+ return 0;
+ }
+ make_const_string(buffer, index_len, & element->index);
+ } else {
+ make_null_string(& element->index);
+ }
+
+ if (! read_value(fd, & element->value))
+ return 0;
+
+ return 1;
+}
+
+/* read_value --- read a number or a string */
+
+static awk_bool_t
+read_value(int fd, awk_value_t *value)
+{
+ uint32_t code, len;
+
+ if (read(fd, & code, sizeof(code)) != sizeof(code))
+ return 0;
+
+ code = ntohl(code);
+
+ if (code == 2) {
+ awk_array_t array = create_array();
+
+ if (read_array(fd, array) != 0)
+ return 0;
+
+ /* hook into value */
+ value->val_type = AWK_ARRAY;
+ value->array_cookie = array;
+ } else if (code == 1) {
+ double d;
+
+ if (read(fd, & d, sizeof(d)) != sizeof(d))
+ return 0;
+
+ /* hook into value */
+ value->val_type = AWK_NUMBER;
+ value->num_value = d;
+ } else {
+ if (read(fd, & len, sizeof(len)) != sizeof(len)) {
+ return 0;
+ }
+ len = ntohl(len);
+ value->val_type = AWK_STRING;
+ value->str_value.len = len;
+ value->str_value.str = malloc(len + 2);
+ memset(value->str_value.str, '\0', len + 2);
+
+ if (read(fd, value->str_value.str, len) != (ssize_t) len) {
+ free(value->str_value.str);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static awk_ext_func_t func_table[] = {
+ { "writea", do_writea, 2 },
+ { "reada", do_reada, 2 },
+};
+
+
+/* define the dl_load function using the boilerplate macro */
+
+dl_load_func(func_table, rwarray, "")