aboutsummaryrefslogtreecommitdiffstats
path: root/gawkapi.c
diff options
context:
space:
mode:
Diffstat (limited to 'gawkapi.c')
-rw-r--r--gawkapi.c1123
1 files changed, 1123 insertions, 0 deletions
diff --git a/gawkapi.c b/gawkapi.c
new file mode 100644
index 00000000..9f4c4f54
--- /dev/null
+++ b/gawkapi.c
@@ -0,0 +1,1123 @@
+/*
+ * gawkapi.c -- Implement the functions defined for gawkapi.h
+ */
+
+/*
+ * 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"
+
+static awk_bool_t node_to_awk_value(NODE *node, awk_value_t *result, awk_valtype_t wanted);
+static void set_constant();
+
+/*
+ * Get the count'th paramater, zero-based.
+ * Returns false if count is out of range, or if actual paramater
+ * does not match what is specified in wanted. In the latter
+ * case, fills in result->val_type with the actual type.
+ */
+static awk_bool_t
+api_get_argument(awk_ext_id_t id, size_t count,
+ awk_valtype_t wanted, awk_value_t *result)
+{
+#ifdef DYNAMIC
+ NODE *arg;
+
+ if (result == NULL)
+ return false;
+
+ (void) id;
+
+ /* set up default result */
+ memset(result, 0, sizeof(*result));
+ result->val_type = AWK_UNDEFINED;
+
+ /*
+ * Song and dance here. get_array_argument() and get_scalar_argument()
+ * will force a change in type of a parameter that is Node_var_new.
+ *
+ * Start by looking at the unadulterated argument as it was passed.
+ */
+ arg = get_argument(count);
+ if (arg == NULL)
+ return false;
+
+ /* if type is undefined */
+ if (arg->type == Node_var_new) {
+ if (wanted == AWK_UNDEFINED)
+ return true;
+ else if (wanted == AWK_ARRAY) {
+ goto array;
+ } else {
+ goto scalar;
+ }
+ }
+
+ /* at this point, we have real type */
+ if (arg->type == Node_var_array || arg->type == Node_array_ref) {
+ if (wanted != AWK_ARRAY && wanted != AWK_UNDEFINED)
+ return false;
+ goto array;
+ } else
+ goto scalar;
+
+array:
+ /* get the array here */
+ arg = get_array_argument(count, false);
+ if (arg == NULL)
+ return false;
+
+ return node_to_awk_value(arg, result, wanted);
+
+scalar:
+ /* at this point we have a real type that is not an array */
+ arg = get_scalar_argument(count, false);
+ if (arg == NULL)
+ return false;
+
+ return node_to_awk_value(arg, result, wanted);
+#else
+ return false;
+#endif
+}
+
+static awk_bool_t
+api_set_argument(awk_ext_id_t id,
+ size_t count,
+ awk_array_t new_array)
+{
+#ifdef DYNAMIC
+ NODE *arg;
+ NODE *array = (NODE *) new_array;
+
+ (void) id;
+
+ if (array == NULL || array->type != Node_var_array)
+ return false;
+
+ if ( (arg = get_argument(count)) == NULL
+ || arg->type != Node_var_new)
+ return false;
+
+ arg = get_array_argument(count, false);
+ if (arg == NULL)
+ return false;
+
+ array->vname = arg->vname;
+ *arg = *array;
+ freenode(array);
+
+ return true;
+#else
+ return false;
+#endif
+}
+
+/* awk_value_to_node --- convert a value into a NODE */
+
+NODE *
+awk_value_to_node(const awk_value_t *retval)
+{
+ NODE *ext_ret_val;
+ NODE *v;
+
+ if (retval == NULL)
+ fatal(_("awk_value_to_node: received null retval"));
+
+ switch (retval->val_type) {
+ case AWK_ARRAY:
+ ext_ret_val = (NODE *) retval->array_cookie;
+ break;
+ case AWK_UNDEFINED:
+ ext_ret_val = dupnode(Nnull_string);
+ break;
+ case AWK_NUMBER:
+ ext_ret_val = make_number(retval->num_value);
+ break;
+ case AWK_STRING:
+ ext_ret_val = make_str_node(retval->str_value.str,
+ retval->str_value.len, ALREADY_MALLOCED);
+ break;
+ case AWK_SCALAR:
+ v = (NODE *) retval->scalar_cookie;
+ if (v->type != Node_var)
+ ext_ret_val = NULL;
+ else
+ ext_ret_val = dupnode(v->var_value);
+ break;
+ case AWK_VALUE_COOKIE:
+ ext_ret_val = dupnode((NODE *)(retval->value_cookie));
+ break;
+ default: /* any invalid type */
+ ext_ret_val = NULL;
+ break;
+ }
+
+ return ext_ret_val;
+}
+
+/* Functions to print messages */
+
+/* api_fatal --- print a fatal message and exit */
+
+static void
+api_fatal(awk_ext_id_t id, const char *format, ...)
+{
+ va_list args;
+
+ (void) id;
+
+ va_start(args, format);
+ err(true, _("fatal: "), format, args);
+ va_end(args);
+}
+
+/* api_warning --- print a warning message and exit */
+
+static void
+api_warning(awk_ext_id_t id, const char *format, ...)
+{
+ va_list args;
+
+ (void) id;
+
+ va_start(args, format);
+ err(false, _("warning: "), format, args);
+ va_end(args);
+}
+
+/* api_lintwarn --- print a lint warning message and exit if appropriate */
+
+static void
+api_lintwarn(awk_ext_id_t id, const char *format, ...)
+{
+ va_list args;
+
+ (void) id;
+
+ va_start(args, format);
+ if (lintwarn == r_fatal) {
+ err(true, _("fatal: "), format, args);
+ va_end(args);
+ } else {
+ err(false, _("warning: "), format, args);
+ va_end(args);
+ }
+}
+
+/* api_register_input_parser --- register an input_parser; for opening files read-only */
+
+static void
+api_register_input_parser(awk_ext_id_t id, awk_input_parser_t *input_parser)
+{
+ (void) id;
+
+ if (input_parser == NULL)
+ return;
+
+ register_input_parser(input_parser);
+}
+
+/* api_register_output_wrapper --- egister an output wrapper, for writing files / two-way pipes */
+
+static void api_register_output_wrapper(awk_ext_id_t id,
+ awk_output_wrapper_t *output_wrapper)
+{
+ (void) id;
+
+ if (output_wrapper == NULL)
+ return;
+
+ register_output_wrapper(output_wrapper);
+}
+
+/* api_register_two_way_processor --- register a processor for two way I/O */
+
+static void
+api_register_two_way_processor(awk_ext_id_t id,
+ awk_two_way_processor_t *two_way_processor)
+{
+ (void) id;
+
+ if (two_way_processor == NULL)
+ return;
+
+ register_two_way_processor(two_way_processor);
+}
+
+/* Functions to update ERRNO */
+
+/* api_update_ERRNO_int --- update ERRNO with an integer value */
+
+static void
+api_update_ERRNO_int(awk_ext_id_t id, int errno_val)
+{
+ (void) id;
+
+ update_ERRNO_int(errno_val);
+}
+
+/* api_update_ERRNO_string --- update ERRNO with a string value */
+
+static void
+api_update_ERRNO_string(awk_ext_id_t id,
+ const char *string)
+{
+ (void) id;
+
+ update_ERRNO_string(string);
+}
+
+/* api_unset_ERRNO --- unset ERRNO */
+
+static void
+api_unset_ERRNO(awk_ext_id_t id)
+{
+ (void) id;
+
+ unset_ERRNO();
+}
+
+
+/* api_add_ext_func --- add a function to the interpreter, returns true upon success */
+
+static awk_bool_t
+api_add_ext_func(awk_ext_id_t id,
+ const char *namespace,
+ const awk_ext_func_t *func)
+{
+ (void) id;
+ (void) namespace;
+
+#ifdef DYNAMIC
+ return make_builtin(func);
+#else
+ return false;
+#endif
+}
+
+/* Stuff for exit handler - do it as linked list */
+
+struct ext_exit_handler {
+ struct ext_exit_handler *next;
+ void (*funcp)(void *data, int exit_status);
+ void *arg0;
+};
+static struct ext_exit_handler *list_head = NULL;
+
+/* run_ext_exit_handlers --- run the extension exit handlers, LIFO order */
+
+void
+run_ext_exit_handlers(int exitval)
+{
+ struct ext_exit_handler *p, *next;
+
+ for (p = list_head; p != NULL; p = next) {
+ next = p->next;
+ p->funcp(p->arg0, exitval);
+ free(p);
+ }
+ list_head = NULL;
+}
+
+/* api_awk_atexit --- add an exit call back, returns true upon success */
+
+static void
+api_awk_atexit(awk_ext_id_t id,
+ void (*funcp)(void *data, int exit_status),
+ void *arg0)
+{
+ struct ext_exit_handler *p;
+
+ (void) id;
+
+ /* allocate memory */
+ emalloc(p, struct ext_exit_handler *, sizeof(struct ext_exit_handler), "api_awk_atexit");
+
+ /* fill it in */
+ p->funcp = funcp;
+ p->arg0 = arg0;
+
+ /* add to linked list, LIFO order */
+ p->next = list_head;
+ list_head = p;
+}
+
+/* node_to_awk_value --- convert a node into a value for an extension */
+
+static awk_bool_t
+node_to_awk_value(NODE *node, awk_value_t *val, awk_valtype_t wanted)
+{
+ awk_bool_t ret = false;
+
+ switch (node->type) {
+ case Node_var_new: /* undefined variable */
+ val->val_type = AWK_UNDEFINED;
+ if (wanted == AWK_UNDEFINED) {
+ ret = true;
+ }
+ break;
+
+ case Node_var:
+ /* a scalar value */
+ if (wanted == AWK_SCALAR) {
+ val->val_type = AWK_SCALAR;
+ val->scalar_cookie = (void *) node;
+ ret = true;
+ break;
+ }
+
+ node = node->var_value;
+ /* FALL THROUGH */
+ case Node_val:
+ /* a scalar value */
+ switch (wanted) {
+ case AWK_NUMBER:
+ val->val_type = AWK_NUMBER;
+
+ (void) force_number(node);
+ if (node->flags & NUMCUR) {
+ val->num_value = get_number_d(node);
+ ret = true;
+ }
+ break;
+
+ case AWK_STRING:
+ val->val_type = AWK_STRING;
+
+ (void) force_string(node);
+ if (node->flags & STRCUR) {
+ val->str_value.str = node->stptr;
+ val->str_value.len = node->stlen;
+ ret = true;
+ }
+ break;
+
+ case AWK_SCALAR:
+ if (node->flags & NUMBER) {
+ val->val_type = AWK_NUMBER;
+ } else if (node->flags & STRING) {
+ val->val_type = AWK_STRING;
+ } else
+ val->val_type = AWK_UNDEFINED;
+ ret = false;
+ break;
+
+ case AWK_UNDEFINED:
+ /* return true and actual type for request of undefined */
+ if (node->flags & NUMBER) {
+ val->val_type = AWK_NUMBER;
+ val->num_value = get_number_d(node);
+ ret = true;
+ } else if (node->flags & STRING) {
+ val->val_type = AWK_STRING;
+ val->str_value.str = node->stptr;
+ val->str_value.len = node->stlen;
+ ret = true;
+ } else
+ val->val_type = AWK_UNDEFINED;
+ break;
+
+ case AWK_ARRAY:
+ case AWK_VALUE_COOKIE:
+ break;
+ }
+ break;
+
+ case Node_var_array:
+ val->val_type = AWK_ARRAY;
+ if (wanted == AWK_ARRAY || wanted == AWK_UNDEFINED) {
+ val->array_cookie = node;
+ ret = true;
+ } else {
+ ret = false;
+ }
+ break;
+
+ default:
+ val->val_type = AWK_UNDEFINED;
+ ret = false;
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * Symbol table access:
+ * - No access to special variables (NF, etc.)
+ * - One special exception: PROCINFO.
+ * - Use sym_update() to change a value, including from UNDEFINED
+ * to scalar or array.
+ */
+/*
+ * Lookup a variable, fills in value. No messing with the value
+ * returned. Returns false if the variable doesn't exist
+ * or the wrong type was requested.
+ * In the latter case, fills in vaule->val_type with the real type.
+ * Built-in variables (except PROCINFO) may not be accessed by an extension.
+ */
+
+/* api_sym_lookup --- look up a symbol */
+
+static awk_bool_t
+api_sym_lookup(awk_ext_id_t id,
+ const char *name,
+ awk_valtype_t wanted,
+ awk_value_t *result)
+{
+ NODE *node;
+
+ if ( name == NULL
+ || *name == '\0'
+ || result == NULL
+ || (node = lookup(name)) == NULL)
+ return false;
+
+ if (is_off_limits_var(name)) /* a built-in variable */
+ node->flags |= NO_EXT_SET;
+
+ return node_to_awk_value(node, result, wanted);
+}
+
+/* api_sym_lookup_scalar --- retrieve the current value of a scalar */
+
+static awk_bool_t
+api_sym_lookup_scalar(awk_ext_id_t id,
+ awk_scalar_t cookie,
+ awk_valtype_t wanted,
+ awk_value_t *result)
+{
+ NODE *node = (NODE *) cookie;
+
+ if (node == NULL
+ || result == NULL
+ || node->type != Node_var)
+ return false;
+
+ return node_to_awk_value(node, result, wanted);
+}
+
+/* api_sym_update_real --- update a symbol's value, see gawkapi.h for semantics */
+
+static awk_bool_t
+sym_update_real(awk_ext_id_t id,
+ const char *name,
+ awk_value_t *value,
+ bool is_const)
+{
+ NODE *node;
+ NODE *array_node;
+
+ if ( name == NULL
+ || *name == '\0'
+ || value == NULL)
+ return false;
+
+ switch (value->val_type) {
+ case AWK_NUMBER:
+ case AWK_STRING:
+ case AWK_UNDEFINED:
+ case AWK_ARRAY:
+ case AWK_SCALAR:
+ case AWK_VALUE_COOKIE:
+ break;
+
+ default:
+ /* fatal(_("api_sym_update: invalid value for type of new value (%d)"), value->val_type); */
+ return false;
+ }
+
+ node = lookup(name);
+
+ if (node == NULL) {
+ /* new value to be installed */
+ if (value->val_type == AWK_ARRAY) {
+ array_node = awk_value_to_node(value);
+ node = install_symbol(estrdup((char *) name, strlen(name)),
+ Node_var_array);
+ array_node->vname = node->vname;
+ *node = *array_node;
+ freenode(array_node);
+ value->array_cookie = node; /* pass new cookie back to extension */
+ } else {
+ /* regular variable */
+ node = install_symbol(estrdup((char *) name, strlen(name)),
+ Node_var);
+ node->var_value = awk_value_to_node(value);
+ if (is_const)
+ node->var_assign = set_constant;
+ }
+
+ return true;
+ }
+
+ /*
+ * If we get here, then it exists already. Any valid type is
+ * OK except for AWK_ARRAY.
+ */
+ if ( (node->flags & NO_EXT_SET) != 0
+ || is_off_limits_var(name)) { /* most built-in vars not allowed */
+ node->flags |= NO_EXT_SET;
+ return false;
+ }
+
+ if ( value->val_type != AWK_ARRAY
+ && (node->type == Node_var || node->type == Node_var_new)) {
+ unref(node->var_value);
+ node->var_value = awk_value_to_node(value);
+ /* let the extension change its own variable */
+ if (is_const)
+ node->var_assign = set_constant;
+
+ return true;
+ }
+
+ return false;
+}
+
+/* api_sym_update --- update a symbol, non-constant */
+
+static awk_bool_t
+api_sym_update(awk_ext_id_t id,
+ const char *name,
+ awk_value_t *value)
+{
+ return sym_update_real(id, name, value, false);
+}
+
+/* api_sym_update --- update a symbol, constant */
+
+static awk_bool_t
+api_sym_constant(awk_ext_id_t id,
+ const char *name,
+ awk_value_t *value)
+{
+ return sym_update_real(id, name, value, true);
+}
+
+/* api_sym_update_scalar --- update a scalar cookie */
+
+static awk_bool_t
+api_sym_update_scalar(awk_ext_id_t id,
+ awk_scalar_t cookie,
+ awk_value_t *value)
+{
+ NODE *node = (NODE *) cookie;
+
+ if (value == NULL
+ || node == NULL
+ || node->type != Node_var
+ || (node->flags & NO_EXT_SET) != 0)
+ return false;
+
+ /*
+ * Optimization: if valref is 1, and the new value is a string or
+ * a number, we can avoid calling unref and then making a new node
+ * by simply installing the new value. First, we follow the same
+ * recipe used by node.c:r_unref to wipe the current values, and then
+ * we copy the logic from r_make_number or make_str_node to install
+ * the new value.
+ */
+ switch (value->val_type) {
+ case AWK_NUMBER:
+ if (node->var_value->valref == 1 && ! do_mpfr) {
+ NODE *r = node->var_value;
+
+ /* r_unref: */
+ if ((r->flags & (MALLOC|STRCUR)) == (MALLOC|STRCUR))
+ efree(r->stptr);
+ free_wstr(r);
+
+ /* r_make_number: */
+ r->numbr = value->num_value;
+ r->flags = MALLOC|NUMBER|NUMCUR;
+ r->stptr = NULL;
+ r->stlen = 0;
+ return true;
+ }
+ break;
+ case AWK_STRING:
+ if (node->var_value->valref == 1) {
+ NODE *r = node->var_value;
+
+ /* r_unref: */
+ if ((r->flags & (MALLOC|STRCUR)) == (MALLOC|STRCUR))
+ efree(r->stptr);
+#ifdef HAVE_MPFR
+ if (is_mpg_float(r))
+ mpfr_clear(r->mpg_numbr);
+ else if (is_mpg_integer(r))
+ mpz_clear(r->mpg_i);
+#endif
+ free_wstr(r);
+
+ /* make_str_node(s, l, ALREADY_MALLOCED): */
+ r->numbr = 0;
+ r->flags = (MALLOC|STRING|STRCUR);
+ r->stfmt = -1;
+ r->stptr = value->str_value.str;
+ r->stlen = value->str_value.len;
+ return true;
+ }
+ break;
+ case AWK_UNDEFINED:
+ case AWK_SCALAR:
+ case AWK_VALUE_COOKIE:
+ break;
+
+ default: /* AWK_ARRAY or invalid type */
+ return false;
+ }
+
+ /* do it the hard (slow) way */
+ unref(node->var_value);
+ node->var_value = awk_value_to_node(value);
+ return true;
+}
+
+/*
+ * Test if a type is allowed for an array subscript.
+ * Any scalar value is fine, so only AWK_ARRAY (or an invalid type) is illegal.
+ */
+static inline int
+valid_subscript_type(awk_valtype_t valtype)
+{
+ switch (valtype) {
+ case AWK_UNDEFINED:
+ case AWK_NUMBER:
+ case AWK_STRING:
+ case AWK_SCALAR:
+ case AWK_VALUE_COOKIE:
+ return true;
+ default: /* AWK_ARRAY or an invalid type */
+ return false;
+ }
+}
+
+/* Array management */
+/*
+ * Return the value of an element - read only!
+ * Use set_array_element to change it.
+ */
+static awk_bool_t
+api_get_array_element(awk_ext_id_t id,
+ awk_array_t a_cookie,
+ const awk_value_t *const index,
+ awk_valtype_t wanted,
+ awk_value_t *result)
+{
+ NODE *array = (NODE *) a_cookie;
+ NODE *subscript;
+ NODE **aptr;
+
+ /* don't check for index len zero, null str is ok as index */
+ if ( array == NULL
+ || array->type != Node_var_array
+ || result == NULL
+ || index == NULL
+ || ! valid_subscript_type(index->val_type))
+ return false;
+
+ subscript = awk_value_to_node(index);
+
+ /* if it doesn't exist, return false */
+ if (in_array(array, subscript) == NULL) {
+ unref(subscript);
+ return false;
+ }
+
+ aptr = assoc_lookup(array, subscript);
+
+ if (aptr == NULL) { /* can't happen */
+ unref(subscript);
+ return false;
+ }
+
+ unref(subscript);
+
+ return node_to_awk_value(*aptr, result, wanted);
+}
+
+/*
+ * Change (or create) element in existing array with
+ * element->index and element->value.
+ */
+static awk_bool_t
+api_set_array_element(awk_ext_id_t id, awk_array_t a_cookie,
+ const awk_value_t *const index,
+ const awk_value_t *const value)
+{
+ NODE *array = (NODE *)a_cookie;
+ NODE *tmp;
+ NODE *elem;
+ NODE **aptr;
+
+ /* don't check for index len zero, null str is ok as index */
+ if ( array == NULL
+ || array->type != Node_var_array
+ || (array->flags & NO_EXT_SET) != 0
+ || index == NULL
+ || value == NULL
+ || ! valid_subscript_type(index->val_type))
+ return false;
+
+ tmp = awk_value_to_node(index);
+ aptr = assoc_lookup(array, tmp);
+ unref(tmp);
+ unref(*aptr);
+ elem = *aptr = awk_value_to_node(value);
+ if (elem->type == Node_var_array) {
+ elem->parent_array = array;
+ elem->vname = estrdup(index->str_value.str,
+ index->str_value.len);
+ make_aname(elem);
+ }
+
+ return true;
+}
+
+/*
+ * remove_element --- remove an array element
+ * common code used by multiple functions
+ */
+
+static void
+remove_element(NODE *array, NODE *subscript)
+{
+ NODE *val;
+
+ val = in_array(array, subscript);
+
+ if (val == NULL)
+ return;
+
+ if (val->type == Node_var_array) {
+ assoc_clear(val);
+ /* cleared a sub-array, free Node_var_array */
+ efree(val->vname);
+ freenode(val);
+ } else
+ unref(val);
+
+ (void) assoc_remove(array, subscript);
+}
+
+/*
+ * Remove the element with the given index.
+ * Returns success if removed or if element did not exist.
+ */
+static awk_bool_t
+api_del_array_element(awk_ext_id_t id,
+ awk_array_t a_cookie, const awk_value_t* const index)
+{
+ NODE *array, *sub;
+
+ array = (NODE *) a_cookie;
+ if ( array == NULL
+ || array->type != Node_var_array
+ || (array->flags & NO_EXT_SET) != 0
+ || index == NULL
+ || ! valid_subscript_type(index->val_type))
+ return false;
+
+ sub = awk_value_to_node(index);
+ remove_element(array, sub);
+ unref(sub);
+
+ return true;
+}
+
+/*
+ * Retrieve total number of elements in array.
+ * Returns false if some kind of error.
+ */
+static awk_bool_t
+api_get_element_count(awk_ext_id_t id,
+ awk_array_t a_cookie, size_t *count)
+{
+ NODE *node = (NODE *) a_cookie;
+
+ if (count == NULL || node == NULL || node->type != Node_var_array)
+ return false;
+
+ *count = node->table_size;
+ return true;
+}
+
+/* Create a new array cookie to which elements may be added */
+static awk_array_t
+api_create_array(awk_ext_id_t id)
+{
+ NODE *n;
+
+ getnode(n);
+ memset(n, 0, sizeof(NODE));
+ init_array(n);
+
+ return (awk_array_t) n;
+}
+
+/* Clear out an array */
+static awk_bool_t
+api_clear_array(awk_ext_id_t id, awk_array_t a_cookie)
+{
+ NODE *node = (NODE *) a_cookie;
+
+ if (node == NULL || node->type != Node_var_array)
+ return false;
+
+ assoc_clear(node);
+ return true;
+}
+
+/* api_flatten_array --- flatten out an array so that it can be looped over easily. */
+
+static awk_bool_t
+api_flatten_array(awk_ext_id_t id,
+ awk_array_t a_cookie,
+ awk_flat_array_t **data)
+{
+ NODE **list;
+ size_t i, j;
+ NODE *array = (NODE *) a_cookie;
+ size_t alloc_size;
+
+ if ( array == NULL
+ || array->type != Node_var_array
+ || array->table_size == 0
+ || data == NULL)
+ return false;
+
+ alloc_size = sizeof(awk_flat_array_t) +
+ (array->table_size - 1) * sizeof(awk_element_t);
+
+ emalloc(*data, awk_flat_array_t *, alloc_size,
+ "api_flatten_array");
+ memset(*data, 0, alloc_size);
+
+ list = assoc_list(array, "@unsorted", ASORTI);
+
+ (*data)->opaque1 = array;
+ (*data)->opaque2 = list;
+ (*data)->count = array->table_size;
+
+ for (i = j = 0; i < 2 * array->table_size; i += 2, j++) {
+ NODE *index, *value;
+
+ index = force_string(list[i]);
+ value = list[i + 1]; /* number or string or subarray */
+
+ /* convert index and value to ext types */
+ if (! node_to_awk_value(index,
+ & (*data)->elements[j].index, AWK_UNDEFINED)) {
+ fatal(_("api_flatten_array: could not convert index %d\n"),
+ (int) i);
+ }
+ if (! node_to_awk_value(value,
+ & (*data)->elements[j].value, AWK_UNDEFINED)) {
+ fatal(_("api_flatten_array: could not convert value %d\n"),
+ (int) i);
+ }
+ }
+ return true;
+}
+
+/*
+ * When done, release the memory, delete any marked elements
+ * Count must match what gawk thinks the size is.
+ */
+static awk_bool_t
+api_release_flattened_array(awk_ext_id_t id,
+ awk_array_t a_cookie,
+ awk_flat_array_t *data)
+{
+ NODE *array = a_cookie;
+ NODE **list;
+ size_t i, j, k;
+
+ if ( array == NULL
+ || array->type != Node_var_array
+ || data == NULL
+ || array != (NODE *) data->opaque1
+ || data->count != array->table_size
+ || data->opaque2 == NULL)
+ return false;
+
+ list = (NODE **) data->opaque2;
+
+ /* free index nodes */
+ for (i = j = 0, k = 2 * array->table_size; i < k; i += 2, j++) {
+ /* Delete items flagged for delete. */
+ if ( (data->elements[j].flags & AWK_ELEMENT_DELETE) != 0
+ && (array->flags & NO_EXT_SET) == 0) {
+ remove_element(array, list[i]);
+ }
+ unref(list[i]);
+ }
+
+ efree(list);
+ efree(data);
+
+ return true;
+}
+
+static awk_bool_t
+api_create_value(awk_ext_id_t id, awk_value_t *value,
+ awk_value_cookie_t *result)
+{
+ switch (value->val_type) {
+ case AWK_NUMBER:
+ case AWK_STRING:
+ break;
+ default:
+ /* reject anything other than a simple scalar */
+ return false;
+ }
+
+ return (*result = awk_value_to_node(value)) != NULL;
+}
+
+static awk_bool_t
+api_release_value(awk_ext_id_t id, awk_value_cookie_t value)
+{
+ unref((NODE *) value);
+ return true;
+}
+
+/*
+ * Register a version string for this extension with gawk.
+ */
+
+struct version_info {
+ const char *version;
+ struct version_info *next;
+};
+
+static struct version_info *vi_head;
+
+/* api_register_ext_version --- add an extension version string to the list */
+
+static void
+api_register_ext_version(awk_ext_id_t id, const char *version)
+{
+ struct version_info *info;
+
+ (void) id;
+
+ emalloc(info, struct version_info *, sizeof(struct version_info), "register_ext_version");
+ info->version = version;
+ info->next = vi_head;
+ vi_head = info;
+}
+
+gawk_api_t api_impl = {
+ /* data */
+ GAWK_API_MAJOR_VERSION, /* major and minor versions */
+ GAWK_API_MINOR_VERSION,
+ { 0 }, /* do_flags */
+
+ /* registration functions */
+ api_add_ext_func,
+ api_register_input_parser,
+ api_register_output_wrapper,
+ api_register_two_way_processor,
+ api_awk_atexit,
+ api_register_ext_version,
+
+ /* message printing functions */
+ api_fatal,
+ api_warning,
+ api_lintwarn,
+
+ /* updating ERRNO */
+ api_update_ERRNO_int,
+ api_update_ERRNO_string,
+ api_unset_ERRNO,
+
+ /* Function arguments */
+ api_get_argument,
+ api_set_argument,
+
+ /* Accessing and installing variables and constants */
+ api_sym_lookup,
+ api_sym_update,
+ api_sym_constant,
+
+ /* Accessing and modifying variables via scalar cookies */
+ api_sym_lookup_scalar,
+ api_sym_update_scalar,
+
+ /* Cached values */
+ api_create_value,
+ api_release_value,
+
+ /* Array management */
+ api_get_element_count,
+ api_get_array_element,
+ api_set_array_element,
+ api_del_array_element,
+ api_create_array,
+ api_clear_array,
+ api_flatten_array,
+ api_release_flattened_array,
+};
+
+/* init_ext_api --- init the extension API */
+
+void
+init_ext_api()
+{
+ /* force values to 1 / 0 */
+ api_impl.do_flags[0] = (do_lint ? 1 : 0);
+ api_impl.do_flags[1] = (do_traditional ? 1 : 0);
+ api_impl.do_flags[2] = (do_profile ? 1 : 0);
+ api_impl.do_flags[3] = (do_sandbox ? 1 : 0);
+ api_impl.do_flags[4] = (do_debug ? 1 : 0);
+ api_impl.do_flags[5] = (do_mpfr ? 1 : 0);
+}
+
+/* update_ext_api --- update the variables in the API that can change */
+
+void
+update_ext_api()
+{
+ api_impl.do_flags[0] = (do_lint ? 1 : 0);
+}
+
+/* set_constant --- prevent awk code from changing a constant */
+
+static void
+set_constant()
+{
+ fatal(_("cannot assign to defined constant"));
+}
+
+/* print_ext_versions --- print the list */
+
+extern void
+print_ext_versions(void)
+{
+ struct version_info *p;
+
+ for (p = vi_head; p != NULL; p = p->next)
+ printf("%s\n", p->version);
+}