summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew J. Schorr <aschorr@telemetry-investments.com>2015-01-06 20:17:35 -0500
committerAndrew J. Schorr <aschorr@telemetry-investments.com>2015-01-06 20:17:35 -0500
commitf38a8f801496ea91cef7a8507e2919f6586d0694 (patch)
tree65afc30f5423e623189f381a1cf2ff8a8a023620
parent9121c3059288f36e004108e02ed4d826b84604e7 (diff)
downloadgawk-f38a8f801496ea91cef7a8507e2919f6586d0694.tar.gz
Fix bug in API deferred variable creation and add a test case.
-rw-r--r--ChangeLog12
-rw-r--r--awk.h2
-rw-r--r--awkgram.c7
-rw-r--r--awkgram.y7
-rw-r--r--extension/ChangeLog7
-rw-r--r--extension/testext.c71
-rw-r--r--gawkapi.c26
-rw-r--r--test/ChangeLog9
-rw-r--r--test/Makefile.am9
-rw-r--r--test/Makefile.in9
-rw-r--r--test/defvar.awk3
-rw-r--r--test/defvar.ok5
12 files changed, 149 insertions, 18 deletions
diff --git a/ChangeLog b/ChangeLog
index 098031ab..71268d3e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,17 @@
2015-01-06 Andrew J. Schorr <aschorr@telemetry-investments.com>
+ * awk.h (variable_create): Now takes a 3rd argument to tell caller
+ whether this is a deferred variable.
+ * awkgram.y (variable_create): Return indicator of whether this is
+ a deferred variable in a newly added 3rd arg.
+ (variable): Pass 3rd arg to variable_create.
+ * gawkapi.c (api_sym_update): If we triggered the creation of a deferred
+ variable, we must merge the extension's array elements into the deffered
+ array, not the other way around. The ENVIRON array has special funcs
+ to call setenv and unsetenv.
+
+2015-01-06 Andrew J. Schorr <aschorr@telemetry-investments.com>
+
* awk.h (variable_create): Declare new function.
* awkgram.y (variable_create): New function to create a variable
taking the deferred variable list into consideration.
diff --git a/awk.h b/awk.h
index 5ecc13fa..f624a876 100644
--- a/awk.h
+++ b/awk.h
@@ -1318,7 +1318,7 @@ extern NODE *do_asorti(int nargs);
extern unsigned long (*hash)(const char *s, size_t len, unsigned long hsize, size_t *code);
extern void init_env_array(NODE *env_node);
/* awkgram.c */
-extern NODE *variable_create(char *name, NODETYPE type);
+extern NODE *variable_create(char *name, NODETYPE type, bool *is_deferred);
extern NODE *variable(int location, char *name, NODETYPE type);
extern int parse_program(INSTRUCTION **pcode);
extern void track_ext_func(const char *name);
diff --git a/awkgram.c b/awkgram.c
index 2257ee35..249fdcdf 100644
--- a/awkgram.c
+++ b/awkgram.c
@@ -7054,16 +7054,18 @@ is_deferred_variable(const char *name)
/* variable_create --- create a new variable */
NODE *
-variable_create(char *name, NODETYPE type)
+variable_create(char *name, NODETYPE type, bool *is_deferred)
{
struct deferred_variable *dv;
for (dv = deferred_variables; dv != NULL; dv = dv->next) {
if (strcmp(name, dv->name) == 0) {
efree(name);
+ *is_deferred = true;
return (*dv->load_func)();
}
}
+ *is_deferred = false;
return install_symbol(name, type);
}
@@ -7073,6 +7075,7 @@ NODE *
variable(int location, char *name, NODETYPE type)
{
NODE *r;
+ bool is_deferred;
if ((r = lookup(name)) != NULL) {
if (r->type == Node_func || r->type == Node_ext_func )
@@ -7084,7 +7087,7 @@ variable(int location, char *name, NODETYPE type)
return r;
}
/* not found */
- return variable_create(name, type);
+ return variable_create(name, type, & is_deferred);
}
/* process_deferred --- if the program uses SYMTAB, load deferred variables */
diff --git a/awkgram.y b/awkgram.y
index 6575e0f0..461bd6b0 100644
--- a/awkgram.y
+++ b/awkgram.y
@@ -4716,16 +4716,18 @@ is_deferred_variable(const char *name)
/* variable_create --- create a new variable */
NODE *
-variable_create(char *name, NODETYPE type)
+variable_create(char *name, NODETYPE type, bool *is_deferred)
{
struct deferred_variable *dv;
for (dv = deferred_variables; dv != NULL; dv = dv->next) {
if (strcmp(name, dv->name) == 0) {
efree(name);
+ *is_deferred = true;
return (*dv->load_func)();
}
}
+ *is_deferred = false;
return install_symbol(name, type);
}
@@ -4735,6 +4737,7 @@ NODE *
variable(int location, char *name, NODETYPE type)
{
NODE *r;
+ bool is_deferred;
if ((r = lookup(name)) != NULL) {
if (r->type == Node_func || r->type == Node_ext_func )
@@ -4746,7 +4749,7 @@ variable(int location, char *name, NODETYPE type)
return r;
}
/* not found */
- return variable_create(name, type);
+ return variable_create(name, type, & is_deferred);
}
/* process_deferred --- if the program uses SYMTAB, load deferred variables */
diff --git a/extension/ChangeLog b/extension/ChangeLog
index 617cc033..c8f77042 100644
--- a/extension/ChangeLog
+++ b/extension/ChangeLog
@@ -1,3 +1,10 @@
+2015-01-06 Andrew J. Schorr <aschorr@telemetry-investments.com>
+
+ * testext.c (test_deferred): New function to help with testing
+ of deferred variable instantiation.
+ (do_get_file): Remove unused variable array.
+ (func_table): Add test_deferred.
+
2015-01-05 Andrew J. Schorr <aschorr@telemetry-investments.com>
* testext.c (test_get_file): Fix error message.
diff --git a/extension/testext.c b/extension/testext.c
index c931ed39..7c61bb0d 100644
--- a/extension/testext.c
+++ b/extension/testext.c
@@ -374,6 +374,75 @@ out:
return result;
}
+static awk_value_t *
+test_deferred(int nargs, awk_value_t *result)
+{
+ awk_value_t arr;
+ awk_value_t index, value;
+ const struct nval {
+ const char *name;
+ double val;
+ } seed[] = {
+ { "fubar", 9.0, },
+ { "rumpus", -5.0, },
+ };
+ struct nval sysval[] = {
+ { "uid", getuid(), },
+ { "api_major", GAWK_API_MAJOR_VERSION, },
+ };
+ size_t i;
+
+ assert(result != NULL);
+ make_number(0.0, result);
+
+ if (nargs != 0) {
+ printf("test_deferred: nargs not right (%d should be 0)\n", nargs);
+ goto out;
+ }
+ arr.val_type = AWK_ARRAY;
+ arr.array_cookie = create_array();
+
+ for (i = 0; i < sizeof(seed)/sizeof(seed[0]); i++) {
+ make_const_string(seed[i].name, strlen(seed[i].name), & index);
+ make_number(seed[i].val, & value);
+ if (! set_array_element(arr.array_cookie, & index, & value)) {
+ printf("test_deferred: %d: set_array_element(%s) failed\n", __LINE__, seed[i].name);
+ goto out;
+ }
+ }
+
+ if (! sym_update("PROCINFO", & arr)) {
+ printf("test_deferred: %d: sym_update failed\n", __LINE__);
+ goto out;
+ }
+
+ /* test that it still contains the values we loaded */
+ for (i = 0; i < sizeof(seed)/sizeof(seed[0]); i++) {
+ make_const_string(seed[i].name, strlen(seed[i].name), & index);
+ make_null_string(& value);
+ if (! get_array_element(arr.array_cookie, &index, AWK_NUMBER, & value)) {
+ printf("test_deferred: %d: get_array_element(%s) failed\n", __LINE__, seed[i].name);
+ goto out;
+ }
+ printf("%s = %g\n", seed[i].name, value.num_value);
+ }
+
+ /* check a few automatically-supplied values */
+ for (i = 0; i < sizeof(sysval)/sizeof(sysval[0]); i++) {
+ make_const_string(sysval[i].name, strlen(sysval[i].name), & index);
+ make_null_string(& value);
+ if (! get_array_element(arr.array_cookie, &index, AWK_NUMBER, & value)) {
+ printf("test_deferred: %d: get_array_element(%s) failed\n", __LINE__, sysval[i].name);
+ goto out;
+ }
+ printf("%s matches %d\n", sysval[i].name, (value.num_value == sysval[i].val));
+ }
+
+ make_number(1.0, result);
+out:
+ return result;
+}
+
/*
BEGIN {
for (i = 1; i <= 10; i++)
@@ -809,7 +878,6 @@ do_get_file(int nargs, awk_value_t *result)
awk_value_t filename, filetype, fd, res;
const awk_input_buf_t *ibuf;
const awk_output_buf_t *obuf;
- awk_array_t array;
if (nargs != 4) {
printf("%s: nargs not right (%d should be 4)\n", "get_file", nargs);
@@ -950,6 +1018,7 @@ static awk_ext_func_t func_table[] = {
{ "dump_array_and_delete", dump_array_and_delete, 2 },
{ "try_modify_environ", try_modify_environ, 0 },
{ "var_test", var_test, 1 },
+ { "test_deferred", test_deferred, 0 },
{ "test_errno", test_errno, 0 },
{ "test_array_size", test_array_size, 1 },
{ "test_array_elem", test_array_elem, 2 },
diff --git a/gawkapi.c b/gawkapi.c
index 5630185c..e8879022 100644
--- a/gawkapi.c
+++ b/gawkapi.c
@@ -579,7 +579,7 @@ api_sym_update(awk_ext_id_t id,
if (node == NULL) {
/* new value to be installed */
if (value->val_type == AWK_ARRAY) {
- unsigned long nel;
+ bool is_deferred;
array_node = awk_value_to_node(value);
/*
@@ -587,27 +587,35 @@ api_sym_update(awk_ext_id_t id,
* case this is a deferred variable such as PROCINFO
* or ENVIRON
*/
- node = variable_create(estrdup((char *) name, strlen(name)), Node_var_array);
- array_node->vname = node->vname;
- if ((nel = assoc_length(node)) > 0) {
- /* merge the 2 arrays */
+ node = variable_create(estrdup((char *) name, strlen(name)), Node_var_array, & is_deferred);
+ if (is_deferred) {
+ /*
+ * merge the user-supplied elements into the
+ * already-existing array. Since ENVIRON
+ * has special array_funcs, we need to retain
+ * the auto-created array!
+ */
+ unsigned long nel;
NODE **list;
NODE akind;
unsigned long i;
+ nel = assoc_length(array_node);
akind.flags = (AINDEX|AVALUE);
- list = node->alist(node, & akind);
+ list = array_node->alist(array_node, & akind);
for (i = 0; i < nel; i++) {
NODE **aptr;
- aptr = assoc_lookup(array_node, list[2*i]);
+ aptr = assoc_lookup(node, list[2*i]);
unref(*aptr);
unref(list[2*i]); /* alist duped it */
*aptr = dupnode(list[2*i+1]);
}
efree(list);
- assoc_clear(node);
+ assoc_clear(array_node);
+ } else {
+ array_node->vname = node->vname;
+ *node = *array_node;
}
- *node = *array_node;
freenode(array_node);
value->array_cookie = node; /* pass new cookie back to extension */
} else {
diff --git a/test/ChangeLog b/test/ChangeLog
index 0fa59a65..7522f7aa 100644
--- a/test/ChangeLog
+++ b/test/ChangeLog
@@ -1,7 +1,14 @@
+2015-01-06 Andrew J. Schorr <aschorr@telemetry-investments.com>
+
+ * Makefile.am (EXTRA_DIST): Add defvar.awk and defvar.ok.
+ (SHLIB_TESTS): Add defvar.
+ (defvar): New test.
+ * defvar.awk, defvar.ok: New files.
+
2015-01-05 Andrew J. Schorr <aschorr@telemetry-investments.com>
* Makefile.am (EXTRA_DIST): Add getfile.awk and getfile.ok.
- (SHLIB_TESTS): Add gefile.
+ (SHLIB_TESTS): Add getfile.
(getfile): New test.
* getfile.awk, getfile.ok: New files.
diff --git a/test/Makefile.am b/test/Makefile.am
index c31e8823..3d95f4cc 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -174,6 +174,8 @@ EXTRA_DIST = \
dbugeval.ok \
defref.awk \
defref.ok \
+ defvar.awk \
+ defvar.ok \
delargv.awk \
delargv.ok \
delarpm2.awk \
@@ -1059,7 +1061,7 @@ LOCALE_CHARSET_TESTS = \
mbprintf1 mbprintf2 mbprintf3 mbprintf4 rebt8b2 rtlenmb sort1 sprintfc
SHLIB_TESTS = \
- fnmatch filefuncs fork fork2 fts functab4 getfile inplace1 inplace2 inplace3 \
+ defvar fnmatch filefuncs fork fork2 fts functab4 getfile inplace1 inplace2 inplace3 \
ordchr ordchr2 readdir readfile readfile2 revout revtwoway rwarray testext time
# List of the tests which should be run with --lint option:
@@ -1900,6 +1902,11 @@ testext::
@$(AWK) -f ./testext.awk >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@
@-$(CMP) "$(srcdir)"/$@.ok _$@ && rm -f _$@ testext.awk
+defvar:
+ @echo $@
+ @$(AWK) -v TESTEXT_QUIET=1 -ltestext -f $(srcdir)/$@.awk $(srcdir)/$@.awk >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@
+ @-$(CMP) "$(srcdir)"/$@.ok _$@ && rm -f _$@
+
getfile:
@echo $@
@$(AWK) -v TESTEXT_QUIET=1 -ltestext -f $(srcdir)/$@.awk $(srcdir)/$@.awk >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@
diff --git a/test/Makefile.in b/test/Makefile.in
index 1ab2c8a8..d2492d32 100644
--- a/test/Makefile.in
+++ b/test/Makefile.in
@@ -421,6 +421,8 @@ EXTRA_DIST = \
dbugeval.ok \
defref.awk \
defref.ok \
+ defvar.awk \
+ defvar.ok \
delargv.awk \
delargv.ok \
delarpm2.awk \
@@ -1302,7 +1304,7 @@ LOCALE_CHARSET_TESTS = \
mbprintf1 mbprintf2 mbprintf3 mbprintf4 rebt8b2 rtlenmb sort1 sprintfc
SHLIB_TESTS = \
- fnmatch filefuncs fork fork2 fts functab4 getfile inplace1 inplace2 inplace3 \
+ defvar fnmatch filefuncs fork fork2 fts functab4 getfile inplace1 inplace2 inplace3 \
ordchr ordchr2 readdir readfile readfile2 revout revtwoway rwarray testext time
@@ -2325,6 +2327,11 @@ testext::
@$(AWK) -f ./testext.awk >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@
@-$(CMP) "$(srcdir)"/$@.ok _$@ && rm -f _$@ testext.awk
+defvar:
+ @echo $@
+ @$(AWK) -v TESTEXT_QUIET=1 -ltestext -f $(srcdir)/$@.awk $(srcdir)/$@.awk >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@
+ @-$(CMP) "$(srcdir)"/$@.ok _$@ && rm -f _$@
+
getfile:
@echo $@
@$(AWK) -v TESTEXT_QUIET=1 -ltestext -f $(srcdir)/$@.awk $(srcdir)/$@.awk >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@
diff --git a/test/defvar.awk b/test/defvar.awk
new file mode 100644
index 00000000..444b81c9
--- /dev/null
+++ b/test/defvar.awk
@@ -0,0 +1,3 @@
+BEGIN {
+ print "test_deferred returns", test_deferred()
+}
diff --git a/test/defvar.ok b/test/defvar.ok
new file mode 100644
index 00000000..4c85427e
--- /dev/null
+++ b/test/defvar.ok
@@ -0,0 +1,5 @@
+fubar = 9
+rumpus = -5
+uid matches 1
+api_major matches 1
+test_deferred returns 1