diff options
Diffstat (limited to 'extension/rwarray0.c')
-rw-r--r-- | extension/rwarray0.c | 475 |
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, "") |