aboutsummaryrefslogtreecommitdiffstats
path: root/extension
diff options
context:
space:
mode:
Diffstat (limited to 'extension')
-rw-r--r--extension/CMakeLists.txt84
-rw-r--r--extension/ChangeLog100
-rw-r--r--extension/intdiv.c3
-rw-r--r--extension/rwarray.3am39
-rw-r--r--extension/rwarray.c343
-rw-r--r--extension/testext.c113
6 files changed, 506 insertions, 176 deletions
diff --git a/extension/CMakeLists.txt b/extension/CMakeLists.txt
deleted file mode 100644
index 1bb4ceb1..00000000
--- a/extension/CMakeLists.txt
+++ /dev/null
@@ -1,84 +0,0 @@
-#
-# extension/CMakeLists.txt --- CMake input file for gawk
-#
-# Copyright (C) 2013
-# 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
-#
-
-## process this file with CMake to produce Makefile
-
-# Remove the definition of GAWK because of gawkapi.h.
-remove_definitions(-DGAWK)
-
-MACRO(BuildExtension name sources)
- add_library (${name} MODULE ${sources} ${ARGN})
- target_link_libraries(${name} ${EXTRA_LIBS})
- set_target_properties(${name} PROPERTIES PREFIX "")
- install(PROGRAMS ${CMAKE_BINARY_DIR}/extension/${name}${CMAKE_SHARED_LIBRARY_SUFFIX} DESTINATION lib)
-ENDMACRO(BuildExtension)
-
-if (${HAVE_STRUCT_STAT_ST_BLKSIZE})
- BuildExtension(filefuncs filefuncs.c stack.c gawkfts.c)
-else()
- message(STATUS "extension filefuncs cannot be built because HAVE_STRUCT_STAT_ST_BLKSIZE is missing")
-endif()
-
-if (HAVE_FNMATCH AND HAVE_FNMATCH_H)
- BuildExtension(fnmatch fnmatch.c)
-else()
- message(STATUS "extension fnmatch cannot be built because function fnmatch or fnmatch.h is missing")
-endif()
-
-if (${HAVE_SYS_WAIT_H})
- BuildExtension(fork fork.c)
-else()
- message(STATUS "extension fork cannot be built because HAVE_SYS_WAIT_H is missing")
-endif()
-
-if (${HAVE_MKSTEMP})
- BuildExtension(inplace inplace.c)
-else()
- message(STATUS "extension inplace cannot be built because HAVE_MKSTEMP is missing")
-endif()
-
-BuildExtension(ordchr ordchr.c)
-
-if (HAVE_DIRENT_H AND HAVE_DIRFD)
- BuildExtension(readdir readdir.c)
-else()
- message(STATUS "extension readdir cannot be built because function readdir is missing")
-endif()
-
-BuildExtension(readfile readfile.c)
-
-BuildExtension(revoutput revoutput.c)
-
-if (${HAVE_GETDTABLESIZE})
- BuildExtension(revtwoway revtwoway.c)
-else()
- message(STATUS "extension revtwoway cannot be built because function getdtablesize is missing")
-endif()
-
-BuildExtension(rwarray rwarray.c)
-
-BuildExtension(time time.c)
-
-BuildExtension(testext testext.c)
-
diff --git a/extension/ChangeLog b/extension/ChangeLog
index 927d6b15..d160c031 100644
--- a/extension/ChangeLog
+++ b/extension/ChangeLog
@@ -4,6 +4,17 @@
* rwarray.c (read_number): Fix typo in case MPFR isn't available.
+2022-03-11 Arnold D. Robbins <arnold@skeeve.com>
+
+ * rwarray.3m: Typo fixes, update copyright and modification dates.
+
+2022-02-25 Andrew J. Schorr <aschorr@telemetry-investments.com>
+
+ Memory issues with MPFR, fix part 1.
+
+ * intdiv.c (do_intdiv): Don't clear quotient and remainder.
+ * testext.c (test_scalar): Add GMP/MPFR support.
+
2022-02-22 Arnold D. Robbins <arnold@skeeve.com>
Fix resource links found by Coverity. Thanks to
@@ -11,10 +22,86 @@
* readfile.c (do_readfile): Close fd if text == NULL.
+2021-12-10 Andrew J. Schorr <aschorr@telemetry-investments.com>
+
+ * rwarray.c (write_number): Update comment to reflect that we are
+ now using mpfr_get_default_rounding_mode() instead of MPFR_RNDN.
+
2021-12-10 Arnold D. Robbins <arnold@skeeve.com>
* rwarray.c (write_number, read_number): Reformat comments a bit.
+2021-12-09 Andrew J. Schorr <aschorr@telemetry-investments.com>
+
+ * rwarray.c (write_number, read_number): Use
+ mpfr_get_default_rounding_mode() instead of arbitrarily choosing
+ MPFR_RNDN, taking advantage of the fact that core gawk maintains
+ this using the ROUNDMODE global variable.
+
+2021-12-08 Andrew J. Schorr <aschorr@telemetry-investments.com>
+
+ * rwarray.c: Fix valgrind complaints related to creating mpz and mpfr
+ values on the stack in read_number by passing down storage from
+ the calling function that loads the data into gawk.
+ (value_storage): New union type to contain mpz_t or mpfr_t data.
+ (read_global): Allocate value_storage on the stack and pass a pointer
+ to read_elem.
+ (read_array): Ditto.
+ (read_elem): Receive new arg pointing to value_storage, and pass it
+ down to read_value.
+ (read_value): Receive new arg pointing to value_storage, and pass it
+ down to read_number.
+ (read_number): Receive new arg pointing to value_storage, and create
+ mpz and mpfr variables using that storage instead of in the local
+ scope.
+
+2021-12-08 Andrew J. Schorr <aschorr@telemetry-investments.com>
+
+ * rwarray.c: Add new functions writeall and readall to implement
+ persistent state.
+ (write_backend): New helper function containing most of the logic
+ from do_writea. Note that we do not need to check nargs < 2 because
+ gawk will issue a fatal error if a function is called with fewer
+ than min_required_args. Clean up some minor issues with error
+ handling.
+ (do_writea): Grab the array argument and use write_backend to
+ do the rest of the work.
+ (do_writeall): Lookup SYMTAB and invoke write_backend.
+ (free_value): New function to free memory for data we end up ignoring
+ because the variables exist already.
+ (do_poke): Attempt to create variables that don't exist already or
+ are undefined.
+ (regular_array_handle): Wrapper around create_array.
+ (global_array_handle): Call create_array unless the variable exists
+ already and is an array with zero elements.
+ (read_global): New function used by readall to load global variables
+ from a file.
+ (read_one): New function to read a single array from a file.
+ (read_backend): New helper function containing most of the logic
+ from do_reada. Remove the superfluous nargs check. Read the file
+ prologue and then call read_global or read_one as appropriate to load
+ the data.
+ (do_reada): Grab the array argument and call read_backend with
+ read_one to load the data.
+ (do_readall): Call read_backend with read_global to load the data.
+ (read_array): Call read_elem with additional arg regular_array_handle.
+ (read_elem): Add a function argument controlling array creation to
+ pass down to read_value.
+ (read_value): Add a function argument to call for array creation
+ instead of calling create_array directly, since we may need to use
+ an existing array when populating global arrays in readall.
+ (func_table): Add writeall and readall.
+ * rwarray.3am: Document new functions writeall and readall.
+
+2021-12-08 Andrew J. Schorr <aschorr@telemetry-investments.com>
+
+ * testext.c (test_array_create): New function to create an array
+ by name that enables testing whether an undefined variable can
+ be converted by the API into an array.
+ (populate_array): New helper function.
+ (fill_in_array): Use populate_array to fill in the elements.
+ (func_table): Add test_array_create.
+
2021-12-08 Andrew J. Schorr <aschorr@telemetry-investments.com>
* rwarray.c (write_number): Since mpfr_fpif_export is experimental
@@ -72,15 +159,28 @@
2021-05-05 Arnold D. Robbins <arnold@skeeve.com>
+ * CMakeLists.txt: Removed.
+
+2021-05-05 Arnold D. Robbins <arnold@skeeve.com>
+
Get `make distcheck' working again:
* Makefile.am (EXTRA_DIST): Remove files that are now in build-aux.
* aclocal.m4: Regenerated.
+2021-03-30 Arnold D. Robbins <arnold@skeeve.com>
+
+ * rwarray.c (write_value): Add support for writing boolean values.
+ (read_value): Ditto.
+
2021-03-29 Arnold D. Robbins <arnold@skeeve.com>
* testext.c (var_test): Fix a comment. Update copyright year.
+2021-03-22 Arnold D. Robbins <arnold@skeeve.com>
+
+ * testext.c (valrep2str): Add support for AWK_BOOL.
+
2020-07-26 Arnold D. Robbins <arnold@skeeve.com>
* intdiv.c (do_intdiv): Change quotient and remainder to
diff --git a/extension/intdiv.c b/extension/intdiv.c
index 8d52c879..7eaa4841 100644
--- a/extension/intdiv.c
+++ b/extension/intdiv.c
@@ -222,9 +222,6 @@ do_intdiv(int nargs, awk_value_t *result, struct awk_ext_func *unused)
array_set_mpz(array, "quotient", 8, quotient);
array_set_mpz(array, "remainder", 9, remainder);
- mpz_clear(quotient);
- mpz_clear(remainder);
-
/* release temporary variables */
if (numer == numer_tmp)
mpz_clear(numer);
diff --git a/extension/rwarray.3am b/extension/rwarray.3am
index f17ffaa9..dbebf519 100644
--- a/extension/rwarray.3am
+++ b/extension/rwarray.3am
@@ -1,6 +1,6 @@
-.TH RWARRAY 3am "Feb 02 2018" "Free Software Foundation" "GNU Awk Extension Modules"
+.TH RWARRAY 3am "Mar 11 2022" "Free Software Foundation" "GNU Awk Extension Modules"
.SH NAME
-writea, reada \- write and read gawk arrays to/from files
+writea, reada, writeall, readall \- write and read gawk arrays to/from files
.SH SYNOPSIS
.ft CW
@load "rwarray"
@@ -8,14 +8,20 @@ writea, reada \- write and read gawk arrays to/from files
ret = writea(file, array)
.br
ret = reada(file, array)
+.br
+ret = writeall(file)
+.br
+ret = readall(file)
.ft R
.SH DESCRIPTION
The
.I rwarray
-extension adds two functions named
-.BR writea() .
-and
+extension adds functions named
+.BR writea() ,
.BR reada() ,
+.BR writeall() ,
+and
+.BR readall() ,
as follows.
.TP
.B writea()
@@ -33,6 +39,23 @@ it reads the file named as its first argument, filling in
the array named as the second argument. It clears the array
first.
Here too, the return value is one on success and zero upon failure.
+.TP
+.B writeall()
+This function takes a string argument, which is the name of the
+file to which dump the state of all variables. Calling this function
+is completely equivalent to calling
+.B writea()
+with the second argument equal to
+.BR SYMTAB .
+It returns one on success, or zero upon failure.
+.TP
+.B readall()
+This function takes a string argument, which is the name of the
+file from which to read the contents of various global variables.
+For each variable in the file, the data is loaded unless the variable
+already exists. If the variable already exists, the data for that variable
+in the file is ignored.
+It returns one on success, or zero upon failure.
.SH NOTES
The array created by
.B reada()
@@ -62,6 +85,10 @@ restored on systems with a different one, but this has not been tried.
ret = writea("arraydump.bin", array)
\&...
ret = reada("arraydump.bin", array)
+\&...
+ret = writeall("globalstate.bin")
+\&...
+ret = readall("globalstate.bin")
.fi
.ft R
.SH "SEE ALSO"
@@ -79,7 +106,7 @@ ret = reada("arraydump.bin", array)
Arnold Robbins,
.BR arnold@skeeve.com .
.SH COPYING PERMISSIONS
-Copyright \(co 2012, 2013, 2018,
+Copyright \(co 2012, 2013, 2018, 2022
Free Software Foundation, Inc.
.PP
Permission is granted to make and distribute verbatim copies of
diff --git a/extension/rwarray.c b/extension/rwarray.c
index e8bc2276..7422be9f 100644
--- a/extension/rwarray.c
+++ b/extension/rwarray.c
@@ -37,6 +37,7 @@
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
+#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -63,11 +64,11 @@
#define MAGIC "awkrulz\n"
#define MAJOR 4
-#define MINOR 0
+#define MINOR 1
static const gawk_api_t *api; /* for convenience macros to work */
static awk_ext_id_t ext_id;
-static const char *ext_version = "rwarray extension: version 2.0";
+static const char *ext_version = "rwarray extension: version 2.1";
static awk_bool_t (*init_func)(void) = NULL;
int plugin_is_GPL_compatible;
@@ -77,10 +78,16 @@ static awk_bool_t write_elem(FILE *fp, awk_element_t *element);
static awk_bool_t write_value(FILE *fp, awk_value_t *val);
static awk_bool_t write_number(FILE *fp, awk_value_t *val);
+typedef union {
+ mpz_t mpz_val;
+ mpfr_t mpfr_val;
+} value_storage;
+
+typedef awk_array_t (*array_handle_t)(awk_value_t *);
static awk_bool_t read_array(FILE *fp, awk_array_t array);
-static awk_bool_t read_elem(FILE *fp, awk_element_t *element);
-static awk_bool_t read_value(FILE *fp, awk_value_t *value);
-static awk_bool_t read_number(FILE *fp, awk_value_t *value, uint32_t code);
+static awk_bool_t read_elem(FILE *fp, awk_element_t *element, array_handle_t, value_storage *);
+static awk_bool_t read_value(FILE *fp, awk_value_t *value, array_handle_t, awk_value_t *idx, value_storage *vs);
+static awk_bool_t read_number(FILE *fp, awk_value_t *value, uint32_t code, value_storage *);
/*
* Format of array info:
@@ -113,14 +120,15 @@ static awk_bool_t read_number(FILE *fp, awk_value_t *value, uint32_t code);
#define VT_ARRAY 5
#define VT_REGEX 6
#define VT_STRNUM 7
+#define VT_BOOL 8
#define VT_UNDEFINED 20
-/* do_writea --- write an array */
+/* write_backend --- write an array */
static awk_value_t *
-do_writea(int nargs, awk_value_t *result, struct awk_ext_func *unused)
+write_backend(awk_value_t *result, awk_array_t array, const char *name)
{
- awk_value_t filename, array;
+ awk_value_t filename;
FILE *fp = NULL;
uint32_t major = MAJOR;
uint32_t minor = MINOR;
@@ -128,18 +136,9 @@ do_writea(int nargs, awk_value_t *result, struct awk_ext_func *unused)
assert(result != NULL);
make_number(0.0, result);
- if (nargs < 2)
- goto out;
-
- /* filename is first arg, array to dump is second */
+ /* filename is first arg */
if (! get_argument(0, AWK_STRING, & filename)) {
- warning(ext_id, _("do_writea: first argument is not a string"));
- errno = EINVAL;
- goto done1;
- }
-
- if (! get_argument(1, AWK_ARRAY, & array)) {
- warning(ext_id, _("do_writea: second argument is not an array"));
+ warning(ext_id, _("%s: first argument is not a string"), name);
errno = EINVAL;
goto done1;
}
@@ -160,21 +159,55 @@ do_writea(int nargs, awk_value_t *result, struct awk_ext_func *unused)
if (fwrite(& minor, 1, sizeof(minor), fp) != sizeof(minor))
goto done1;
- if (write_array(fp, array.array_cookie)) {
+ if (write_array(fp, array)) {
make_number(1.0, result);
- goto done0;
+ fclose(fp);
+ return result;
}
done1:
update_ERRNO_int(errno);
- unlink(filename.str_value.str);
-
-done0:
- fclose(fp);
-out:
+ if (fp != NULL) {
+ fclose(fp);
+ unlink(filename.str_value.str);
+ }
return result;
}
+/* do_writea --- write an array */
+
+static awk_value_t *
+do_writea(int nargs, awk_value_t *result, struct awk_ext_func *unused)
+{
+ awk_value_t array;
+
+ if (! get_argument(1, AWK_ARRAY, & array)) {
+ warning(ext_id, _("writea: second argument is not an array"));
+ errno = EINVAL;
+ update_ERRNO_int(errno);
+ make_number(0.0, result);
+ return result;
+ }
+ return write_backend(result, array.array_cookie, "writea");
+}
+
+/* do_writeall --- write out SYMTAB */
+
+static awk_value_t *
+do_writeall(int nargs, awk_value_t *result, struct awk_ext_func *unused)
+{
+ awk_value_t array;
+
+ if (! sym_lookup("SYMTAB", AWK_ARRAY, & array)) {
+ warning(ext_id, _("writeall: unable to find SYMTAB array"));
+ errno = EINVAL;
+ update_ERRNO_int(errno);
+ make_number(0.0, result);
+ return result;
+ }
+ return write_backend(result, array.array_cookie, "writeall");
+}
+
/* write_array --- write out an array or a sub-array */
@@ -258,6 +291,9 @@ write_value(FILE *fp, awk_value_t *val)
case AWK_REGEX:
code = htonl(VT_REGEX);
break;
+ case AWK_BOOL:
+ code = htonl(VT_BOOL);
+ break;
case AWK_UNDEFINED:
code = htonl(VT_UNDEFINED);
break;
@@ -267,17 +303,29 @@ write_value(FILE *fp, awk_value_t *val)
warning(ext_id, _("array value has unknown type %d"), val->val_type);
break;
}
+
if (fwrite(& code, 1, sizeof(code), fp) != sizeof(code))
return awk_false;
- len = htonl(val->str_value.len);
- if (fwrite(& len, 1, sizeof(len), fp) != sizeof(len))
- return awk_false;
+ if (code == ntohl(VT_BOOL)) {
+ len = (val->bool_value == awk_true ? 4 : 5);
+ len = htonl(len);
+ const char *s = (val->bool_value == awk_true ? "TRUE" : "FALSE");
- if (fwrite(val->str_value.str, 1, val->str_value.len, fp)
- != (ssize_t) val->str_value.len)
- return awk_false;
+ if (fwrite(& len, 1, sizeof(len), fp) != sizeof(len))
+ return awk_false;
+
+ if (fwrite(s, 1, strlen(s), fp) != (ssize_t) strlen(s))
+ return awk_false;
+ } else {
+ len = htonl(val->str_value.len);
+ if (fwrite(& len, 1, sizeof(len), fp) != sizeof(len))
+ return awk_false;
+ if (fwrite(val->str_value.str, 1, val->str_value.len, fp)
+ != (ssize_t) val->str_value.len)
+ return awk_false;
+ }
return awk_true;
}
@@ -323,9 +371,9 @@ write_number(FILE *fp, awk_value_t *val)
if (mpfr_fpif_export(fp, val->num_ptr) != 0)
#else
#define MPFR_STR_BASE 62 /* maximize base to minimize string len */
-#define MPFR_STR_ROUND MPFR_RNDN
+#define MPFR_STR_ROUND mpfr_get_default_rounding_mode()
/*
- * XXX does the choice of MPFR_RNDN matter, given
+ * Does the choice of rounding mode matter, given
* that the precision is 0, so we should be rendering
* in full precision?
*/
@@ -350,12 +398,139 @@ write_number(FILE *fp, awk_value_t *val)
return awk_true;
}
-/* do_reada --- read an array */
+/* free_value --- release memory for ignored global variables */
+
+static void
+free_value(awk_value_t *v)
+{
+ switch (v->val_type) {
+ case AWK_ARRAY:
+ clear_array(v->array_cookie);
+ break;
+ case AWK_STRING:
+ case AWK_REGEX:
+ case AWK_STRNUM:
+ case AWK_UNDEFINED:
+ gawk_free(v->str_value.str);
+ break;
+ case AWK_BOOL:
+ /* no memory allocated */
+ break;
+ case AWK_NUMBER:
+ switch (v->num_type) {
+ case AWK_NUMBER_TYPE_DOUBLE:
+ /* no memory allocated */
+ break;
+ case AWK_NUMBER_TYPE_MPZ:
+ mpz_clear(v->num_ptr);
+ break;
+ case AWK_NUMBER_TYPE_MPFR:
+ mpfr_clear(v->num_ptr);
+ break;
+ default:
+ warning(ext_id, _("cannot free number with unknown type %d"), v->num_type);
+ break;
+ }
+ break;
+ default:
+ warning(ext_id, _("cannot free value with unhandled type %d"), v->val_type);
+ break;
+ }
+}
+
+/* do_poke --- create a global variable */
+
+static awk_bool_t
+do_poke(awk_element_t *e)
+{
+ awk_value_t t;
+
+ if (e->index.val_type != AWK_STRING)
+ return awk_false;
+ /* So this is a bit tricky. If the program refers to the variable,
+ * then it will already exist in an undefined state after parsing.
+ * If the program never refers to it, then the lookup fails.
+ * We still need to create it in case the program accesses it via
+ * indirection through the SYMTAB table. */
+ if (sym_lookup(e->index.str_value.str, AWK_UNDEFINED, &t) && (t.val_type != AWK_UNDEFINED))
+ return awk_false;
+ if (! sym_update(e->index.str_value.str, & e->value)) {
+ warning(ext_id, _("readall: unable to set %s"), e->index.str_value.str);
+ return awk_false;
+ }
+ return awk_true;
+}
+
+/* regular_array_handle --- array creation hook for normal reada */
+
+static awk_array_t
+regular_array_handle(awk_value_t *unused)
+{
+ return create_array();
+}
+
+/* global_array_handle --- array creation hook for readall */
+
+static awk_array_t
+global_array_handle(awk_value_t *n)
+{
+ awk_value_t t;
+ size_t count;
+
+ /* The array may exist already because it was instantiated during
+ * program parsing, so we use the existing array if it is empty. */
+ return ((n->val_type == AWK_STRING) && sym_lookup(n->str_value.str, AWK_UNDEFINED, &t) && (t.val_type == AWK_ARRAY) && get_element_count(t.array_cookie, & count) && ! count) ? t.array_cookie : create_array();
+}
+
+/* read_global --- read top-level variables dumped from SYMTAB */
+
+static awk_bool_t
+read_global(FILE *fp, awk_array_t unused)
+{
+ uint32_t i;
+ uint32_t count;
+ awk_element_t new_elem;
+ value_storage vs;
+
+ if (fread(& count, 1, sizeof(count), fp) != sizeof(count))
+ return awk_false;
+
+ count = ntohl(count);
+
+ for (i = 0; i < count; i++) {
+ if (read_elem(fp, & new_elem, global_array_handle, &vs)) {
+ if (! do_poke(& new_elem))
+ free_value(& new_elem.value);
+ if (new_elem.index.str_value.len)
+ /* free string allocated by make_const_string */
+ gawk_free(new_elem.index.str_value.str);
+ } else
+ return awk_false;
+ }
+
+ return awk_true;
+}
+
+/* read_one --- read one array */
+
+static awk_bool_t
+read_one(FILE *fp, awk_array_t array)
+{
+ if (! clear_array(array)) {
+ errno = ENOMEM;
+ warning(ext_id, _("reada: clear_array failed"));
+ return awk_false;
+ }
+
+ return read_array(fp, array);
+}
+
+/* read_backend --- common code for reada and readall */
static awk_value_t *
-do_reada(int nargs, awk_value_t *result, struct awk_ext_func *unused)
+read_backend(awk_value_t *result, awk_array_t array, const char *name, awk_bool_t (*func)(FILE *, awk_array_t))
{
- awk_value_t filename, array;
+ awk_value_t filename;
FILE *fp = NULL;
uint32_t major;
uint32_t minor;
@@ -364,18 +539,9 @@ do_reada(int nargs, awk_value_t *result, struct awk_ext_func *unused)
assert(result != NULL);
make_number(0.0, result);
- if (nargs < 2)
- goto out;
-
- /* directory is first arg, array to read is second */
+ /* filename is first arg */
if (! get_argument(0, AWK_STRING, & filename)) {
- warning(ext_id, _("do_reada: first argument is not a string"));
- errno = EINVAL;
- goto done1;
- }
-
- if (! get_argument(1, AWK_ARRAY, & array)) {
- warning(ext_id, _("do_reada: second argument is not an array"));
+ warning(ext_id, _("%s: first argument is not a string"), name);
errno = EINVAL;
goto done1;
}
@@ -417,13 +583,7 @@ do_reada(int nargs, awk_value_t *result, struct awk_ext_func *unused)
goto done1;
}
- if (! clear_array(array.array_cookie)) {
- errno = ENOMEM;
- warning(ext_id, _("do_reada: clear_array failed"));
- goto done1;
- }
-
- if (read_array(fp, array.array_cookie)) {
+ if ((*func)(fp, array)) {
make_number(1.0, result);
goto done0;
}
@@ -433,10 +593,34 @@ done1:
done0:
if (fp != NULL)
fclose(fp);
-out:
return result;
}
+/* do_reada --- read an array */
+
+static awk_value_t *
+do_reada(int nargs, awk_value_t *result, struct awk_ext_func *unused)
+{
+ awk_value_t array;
+
+ if (! get_argument(1, AWK_ARRAY, & array)) {
+ warning(ext_id, _("reada: second argument is not an array"));
+ errno = EINVAL;
+ update_ERRNO_int(errno);
+ make_number(0.0, result);
+ return result;
+ }
+ return read_backend(result, array.array_cookie, "read", read_one);
+}
+
+/* do_readall --- read top-level variables */
+
+static awk_value_t *
+do_readall(int nargs, awk_value_t *result, struct awk_ext_func *unused)
+{
+ return read_backend(result, NULL, "readall", read_global);
+}
+
/* read_array --- read in an array or sub-array */
@@ -446,6 +630,7 @@ read_array(FILE *fp, awk_array_t array)
uint32_t i;
uint32_t count;
awk_element_t new_elem;
+ value_storage vs;
if (fread(& count, 1, sizeof(count), fp) != sizeof(count))
return awk_false;
@@ -453,7 +638,7 @@ read_array(FILE *fp, awk_array_t array)
count = ntohl(count);
for (i = 0; i < count; i++) {
- if (read_elem(fp, & new_elem)) {
+ if (read_elem(fp, & new_elem, regular_array_handle, &vs)) {
/* add to array */
if (! set_array_element_by_elem(array, & new_elem)) {
warning(ext_id, _("read_array: set_array_element failed"));
@@ -472,7 +657,7 @@ read_array(FILE *fp, awk_array_t array)
/* read_elem --- read in a single element */
static awk_bool_t
-read_elem(FILE *fp, awk_element_t *element)
+read_elem(FILE *fp, awk_element_t *element, array_handle_t array_handle, value_storage *vs)
{
uint32_t index_len;
static char *buffer;
@@ -510,7 +695,7 @@ read_elem(FILE *fp, awk_element_t *element)
make_null_string(& element->index);
}
- if (! read_value(fp, & element->value))
+ if (! read_value(fp, & element->value, array_handle, & element->index, vs))
return awk_false;
return awk_true;
@@ -519,7 +704,7 @@ read_elem(FILE *fp, awk_element_t *element)
/* read_value --- read a number or a string */
static awk_bool_t
-read_value(FILE *fp, awk_value_t *value)
+read_value(FILE *fp, awk_value_t *value, array_handle_t array_handle, awk_value_t *idx, value_storage *vs)
{
uint32_t code, len;
@@ -529,7 +714,7 @@ read_value(FILE *fp, awk_value_t *value)
code = ntohl(code);
if (code == VT_ARRAY) {
- awk_array_t array = create_array();
+ awk_array_t array = (*array_handle)(idx);
if (! read_array(fp, array))
return awk_false;
@@ -540,7 +725,7 @@ read_value(FILE *fp, awk_value_t *value)
} else if (code == VT_NUMBER
|| code == VT_GMP
|| code == VT_MPFR) {
- return read_number(fp, value, code);
+ return read_number(fp, value, code, vs);
} else {
if (fread(& len, 1, sizeof(len), fp) != sizeof(len)) {
return awk_false;
@@ -559,6 +744,9 @@ read_value(FILE *fp, awk_value_t *value)
case VT_UNDEFINED:
value->val_type = AWK_UNDEFINED;
break;
+ case VT_BOOL:
+ value->val_type = AWK_BOOL;
+ break;
default:
/* this cannot happen! */
warning(ext_id, _("treating recovered value with unknown type code %d as a string"), code);
@@ -573,6 +761,15 @@ read_value(FILE *fp, awk_value_t *value)
return awk_false;
}
value->str_value.str[len] = '\0';
+ value->str_value.len = len;
+
+ if (code == VT_BOOL) {
+ bool val = (strcmp(value->str_value.str, "TRUE") == 0);
+
+ gawk_free(value->str_value.str);
+ value->str_value.str = NULL;
+ value->bool_value = val ? awk_true : awk_false;
+ }
}
return awk_true;
@@ -581,7 +778,7 @@ read_value(FILE *fp, awk_value_t *value)
/* read_number --- read a double, GMP, or MPFR number */
static awk_bool_t
-read_number(FILE *fp, awk_value_t *value, uint32_t code)
+read_number(FILE *fp, awk_value_t *value, uint32_t code, value_storage *vs)
{
uint32_t len;
@@ -603,28 +800,24 @@ read_number(FILE *fp, awk_value_t *value, uint32_t code)
} else {
#ifdef HAVE_MPFR
if (code == VT_GMP) {
- mpz_t mp_ptr;
-
- mpz_init(mp_ptr);
- if (mpz_inp_raw(mp_ptr, fp) == 0)
+ mpz_init(vs->mpz_val);
+ if (mpz_inp_raw(vs->mpz_val, fp) == 0)
return awk_false;
- value = make_number_mpz(mp_ptr, value);
+ value = make_number_mpz(vs->mpz_val, value);
} else {
- mpfr_t mpfr_val;
- mpfr_init(mpfr_val);
-
+ mpfr_init(vs->mpfr_val);
#ifdef USE_MPFR_FPIF
/* preferable if widely available and stable */
- if (mpfr_fpif_import(mpfr_val, fp) != 0)
+ if (mpfr_fpif_import(vs->mpfr_val, fp) != 0)
#else
// N.B. need to consume the terminating space we wrote
// after mpfr_out_str
- if ((mpfr_inp_str(mpfr_val, fp, MPFR_STR_BASE, MPFR_STR_ROUND) == 0) || (getc(fp) != ' '))
+ if ((mpfr_inp_str(vs->mpfr_val, fp, MPFR_STR_BASE, MPFR_STR_ROUND) == 0) || (getc(fp) != ' '))
#endif
return awk_false;
- value = make_number_mpfr(& mpfr_val, value);
+ value = make_number_mpfr(vs->mpfr_val, value);
}
#else
fatal(ext_id, _("rwarray extension: GMP/MPFR value in file but compiled without GMP/MPFR support."));
@@ -637,6 +830,8 @@ read_number(FILE *fp, awk_value_t *value, uint32_t code)
static awk_ext_func_t func_table[] = {
{ "writea", do_writea, 2, 2, awk_false, NULL },
{ "reada", do_reada, 2, 2, awk_false, NULL },
+ { "writeall", do_writeall, 1, 1, awk_false, NULL },
+ { "readall", do_readall, 1, 1, awk_false, NULL },
};
diff --git a/extension/testext.c b/extension/testext.c
index a5bef7ae..30dcdeb3 100644
--- a/extension/testext.c
+++ b/extension/testext.c
@@ -39,6 +39,11 @@
#include <sys/stat.h>
#include <fcntl.h>
+#ifdef HAVE_MPFR
+#include <gmp.h>
+#include <mpfr.h>
+#endif
+
#include "gawkapi.h"
static const gawk_api_t *api; /* for convenience macros to work */
@@ -48,6 +53,7 @@ static const char *ext_version = "testext extension: version 1.0";
int plugin_is_GPL_compatible;
static void fill_in_array(awk_value_t *value);
+static int populate_array(awk_array_t);
#ifdef __MINGW32__
unsigned int
@@ -88,6 +94,13 @@ valrep2str(const awk_value_t *value)
size,
value->str_value.str);
break;
+ case AWK_BOOL:
+ if (value->str_value.len + 8 < size)
+ size = value->str_value.len;
+ sprintf(buf, "<bool>: %.*s",
+ size,
+ value->str_value.str);
+ break;
case AWK_NUMBER:
sprintf(buf, "%g", value->num_value);
break;
@@ -659,6 +672,54 @@ out:
}
/*
+function tfunc(f) {
+ if (isarray(f))
+ print "good: we have an array"
+}
+
+BEGIN {
+ printf "test_array_create returned %d\n", test_array_create("testarr")
+ tfunc(testarr)
+}
+*/
+
+static awk_value_t *
+test_array_create(int nargs, awk_value_t *result, struct awk_ext_func *unused)
+{
+ awk_value_t new_array;
+ awk_value_t arg0;
+
+ (void) nargs; /* silence warnings */
+ make_number(0.0, result);
+
+ if (! get_argument(0, AWK_STRING, & arg0)) {
+ printf("test_array_create: could not get argument\n");
+ goto out;
+ }
+
+ if (arg0.val_type != AWK_STRING) {
+ printf("test_array_create: argument is not string (%d)\n",
+ arg0.val_type);
+ goto out;
+ }
+
+ new_array.val_type = AWK_ARRAY;
+ new_array.array_cookie = create_array();
+ if (! sym_update(arg0.str_value.str, & new_array)) {
+ printf("test_array_create: sym_update(\"%s\") failed!\n", arg0.str_value.str);
+ goto out;
+ }
+ if (populate_array(new_array.array_cookie) < 0) {
+ printf("test_array_create: populate(\"%s\") failed!\n", arg0.str_value.str);
+ goto out;
+ }
+
+ make_number(1.0, result);
+out:
+ return result;
+}
+
+/*
BEGIN {
printf("Initial value of LINT is %d\n", LINT)
ret = print_do_lint();
@@ -711,6 +772,10 @@ test_scalar(int nargs, awk_value_t *result, struct awk_ext_func *unused)
{
awk_value_t new_value, new_value2;
awk_value_t the_scalar;
+#ifdef HAVE_MPFR
+ mpz_t mpz_val;
+ mpfr_t mpfr_val;
+#endif
(void) nargs; /* silence warnings */
make_number(0.0, result);
@@ -730,8 +795,26 @@ test_scalar(int nargs, awk_value_t *result, struct awk_ext_func *unused)
if (new_value.val_type == AWK_STRING) {
make_const_string(new_value.str_value.str, new_value.str_value.len, & new_value2);
- } else {
+ } else { /* AWK_NUMBER */
+#ifdef HAVE_MPFR
+ switch (new_value.num_type) {
+ case AWK_NUMBER_TYPE_MPZ:
+ mpz_init(mpz_val);
+ mpz_set(mpz_val, new_value.num_ptr);
+ make_number_mpz(mpz_val, & new_value2);
+ break;
+ case AWK_NUMBER_TYPE_MPFR:
+ mpfr_init(mpfr_val);
+ mpfr_set(mpfr_val, (mpfr_ptr) new_value.num_ptr, mpfr_get_default_rounding_mode());
+ make_number_mpfr(mpfr_val, & new_value2);
+ break;
+ default:
+ new_value2 = new_value;
+ break;
+ }
+#else
new_value2 = new_value;
+#endif
}
if (! sym_update_scalar(the_scalar.scalar_cookie, & new_value2)) {
@@ -951,29 +1034,40 @@ do_get_file(int nargs, awk_value_t *result, struct awk_ext_func *unused)
return make_number(1.0, result);
}
-/* fill_in_array --- fill in a new array */
+/* populate_array --- fill in some array values */
-static void
-fill_in_array(awk_value_t *new_array)
+static int
+populate_array(awk_array_t a_cookie)
{
- awk_array_t a_cookie;
awk_value_t index, value;
- a_cookie = create_array();
-
(void) make_const_string("hello", 5, & index);
(void) make_const_string("world", 5, & value);
if (! set_array_element(a_cookie, & index, & value)) {
printf("fill_in_array:%d: set_array_element failed\n", __LINE__);
- return;
+ return -1;
}
(void) make_const_string("answer", 6, & index);
(void) make_number(42.0, & value);
if (! set_array_element(a_cookie, & index, & value)) {
printf("fill_in_array:%d: set_array_element failed\n", __LINE__);
- return;
+ return -1;
}
+ return 0;
+}
+
+/* fill_in_array --- fill in a new array */
+
+static void
+fill_in_array(awk_value_t *new_array)
+{
+ awk_array_t a_cookie;
+
+ a_cookie = create_array();
+
+ if (populate_array(a_cookie) < 0)
+ return;
new_array->val_type = AWK_ARRAY;
new_array->array_cookie = a_cookie;
@@ -1054,6 +1148,7 @@ static awk_ext_func_t func_table[] = {
{ "test_array_size", test_array_size, 1, 1, awk_false, NULL },
{ "test_array_elem", test_array_elem, 2, 2, awk_false, NULL },
{ "test_array_param", test_array_param, 1, 1, awk_false, NULL },
+ { "test_array_create", test_array_create, 1, 1, awk_false, NULL },
{ "print_do_lint", print_do_lint, 0, 0, awk_false, NULL },
{ "test_scalar", test_scalar, 1, 1, awk_false, NULL },
{ "test_scalar_reserved", test_scalar_reserved, 0, 0, awk_false, NULL },