aboutsummaryrefslogtreecommitdiffstats
path: root/array.c
diff options
context:
space:
mode:
authorArnold D. Robbins <arnold@skeeve.com>2011-05-04 23:05:28 +0300
committerArnold D. Robbins <arnold@skeeve.com>2011-05-04 23:05:28 +0300
commite7dced088c226280bcb1edb252011d6c186504e4 (patch)
tree868eabc52db1e4a08e1385868e1a56062e74ea40 /array.c
parent19093d5a231421594788d633e811859276d8f92f (diff)
downloadegawk-e7dced088c226280bcb1edb252011d6c186504e4.tar.gz
egawk-e7dced088c226280bcb1edb252011d6c186504e4.tar.bz2
egawk-e7dced088c226280bcb1edb252011d6c186504e4.zip
Fix problem with subarray of deleted array.
Diffstat (limited to 'array.c')
-rw-r--r--array.c235
1 files changed, 165 insertions, 70 deletions
diff --git a/array.c b/array.c
index ee8ac9ee..380d4f32 100644
--- a/array.c
+++ b/array.c
@@ -45,7 +45,7 @@ static size_t AVG_CHAIN_MAX = 2; /* Modern machines are bigger, reduce this from
static size_t SUBSEPlen;
static char *SUBSEP;
-static NODE *assoc_find(NODE *symbol, NODE *subs, unsigned long hash1);
+static NODE *assoc_find(NODE *symbol, NODE *subs, unsigned long hash1, NODE **last);
static void grow_table(NODE *symbol);
static unsigned long gst_hash_string(const char *str, size_t len, unsigned long hsize, size_t *code);
@@ -198,6 +198,7 @@ get_array(NODE *symbol, int canfatal)
case Node_var_new:
symbol->type = Node_var_array;
symbol->var_array = NULL;
+ symbol->parent_array = NULL; /* main array has no parent */
/* fall through */
case Node_var_array:
break;
@@ -296,6 +297,7 @@ concat_exp(int nargs, int do_subsep)
return make_str_node(str, len, ALREADY_MALLOCED);
}
+
/* assoc_clear --- flush all the values in symbol[] */
void
@@ -317,6 +319,7 @@ assoc_clear(NODE *symbol)
freenode(r);
} else
unref(bucket->ahvalue);
+
unref(bucket); /* unref() will free the ahname_str */
}
symbol->var_array[i] = NULL;
@@ -393,15 +396,15 @@ awk_hash(const char *s, size_t len, unsigned long hsize, size_t *code)
/* assoc_find --- locate symbol[subs] */
static NODE * /* NULL if not found */
-assoc_find(NODE *symbol, NODE *subs, unsigned long hash1)
+assoc_find(NODE *symbol, NODE *subs, unsigned long hash1, NODE **last)
{
- NODE *bucket;
+ NODE *bucket, *prev;
const char *s1_str;
size_t s1_len;
NODE *s2;
- for (bucket = symbol->var_array[hash1]; bucket != NULL;
- bucket = bucket->ahnext) {
+ for (prev = NULL, bucket = symbol->var_array[hash1]; bucket != NULL;
+ prev = bucket, bucket = bucket->ahnext) {
/*
* This used to use cmp_nodes() here. That's wrong.
* Array indices are strings; compare as such, always!
@@ -413,10 +416,12 @@ assoc_find(NODE *symbol, NODE *subs, unsigned long hash1)
if (s1_len == s2->stlen) {
if (s1_len == 0 /* "" is a valid index */
|| memcmp(s1_str, s2->stptr, s1_len) == 0)
- return bucket;
+ break;
}
}
- return NULL;
+ if (last != NULL)
+ *last = prev;
+ return bucket;
}
/* in_array --- test whether the array element symbol[subs] exists or not,
@@ -435,7 +440,7 @@ in_array(NODE *symbol, NODE *subs)
return NULL;
hash1 = hash(subs->stptr, subs->stlen, (unsigned long) symbol->array_size, NULL);
- ret = assoc_find(symbol, subs, hash1);
+ ret = assoc_find(symbol, subs, hash1, NULL);
return (ret ? ret->ahvalue : NULL);
}
@@ -468,7 +473,7 @@ assoc_lookup(NODE *symbol, NODE *subs, int reference)
} else {
hash1 = hash(subs->stptr, subs->stlen,
(unsigned long) symbol->array_size, & code);
- bucket = assoc_find(symbol, subs, hash1);
+ bucket = assoc_find(symbol, subs, hash1, NULL);
if (bucket != NULL)
return &(bucket->ahvalue);
}
@@ -532,6 +537,94 @@ assoc_lookup(NODE *symbol, NODE *subs, int reference)
return &(bucket->ahvalue);
}
+
+/* adjust_fcall_stack: remove subarray(s) of symbol[] from
+ * function call stack.
+ */
+
+static void
+adjust_fcall_stack(NODE *symbol, int nsubs)
+{
+ NODE *func, *r, *n;
+ NODE **sp;
+ int pcount;
+
+ /*
+ * Solve the nasty problem of disappearing subarray arguments:
+ *
+ * function f(c, d) { delete c; .. use non-existent array d .. }
+ * BEGIN { a[0][0] = 1; f(a, a[0]); .. }
+ *
+ * The fix is to convert 'd' to a local empty array; This has
+ * to be done before clearing the parent array to avoid referring to
+ * already free-ed memory.
+ *
+ * Similar situations exist for builtins accepting more than
+ * one array argument: split, patsplit, asort and asorti. For example:
+ *
+ * BEGIN { a[0][0] = 1; split("abc", a, "", a[0]) }
+ *
+ * These cases do not involve the function call stack, and are
+ * handled individually in their respective routines.
+ */
+
+ func = frame_ptr->func_node;
+ if (func == NULL) /* in main */
+ return;
+ pcount = func->lnode->param_cnt;
+ sp = frame_ptr->stack;
+
+ for (; pcount > 0; pcount--) {
+ r = *sp++;
+ if (r->type != Node_array_ref
+ || r->orig_array->type != Node_var_array)
+ continue;
+ n = r->orig_array;
+
+ /* Case 1 */
+ if (n == symbol
+ && symbol->parent_array != NULL
+ && nsubs > 0
+ ) {
+ /* 'symbol' is a subarray, and 'r' is the same subarray:
+ *
+ * function f(c, d) { delete c[0]; .. }
+ * BEGIN { a[0][0] = 1; f(a, a[0]); .. }
+ *
+ * But excludes cases like (nsubs = 0):
+ *
+ * function f(c, d) { delete c; ..}
+ * BEGIN { a[0][0] = 1; f(a[0], a[0]); ...}
+ */
+ char *save;
+local_array:
+ save = r->vname;
+ memset(r, '\0', sizeof(NODE));
+ r->vname = save;
+ r->type = Node_var_array;
+ continue;
+ }
+
+ /* Case 2 */
+ for (n = n->parent_array; n != NULL; n = n->parent_array) {
+ assert(n->type == Node_var_array);
+ if (n == symbol) {
+ /* 'r' is a subarray of 'symbol':
+ *
+ * function f(c, d) { delete c; .. use d as array .. }
+ * BEGIN { a[0][0] = 1; f(a, a[0]); .. }
+ * OR
+ * BEGIN { a[0][0][0][0] = 1; f(a[0], a[0][0][0]); .. }
+ *
+ */
+
+ goto local_array;
+ }
+ }
+ }
+}
+
+
/* do_delete --- perform `delete array[s]' */
/*
@@ -543,8 +636,8 @@ void
do_delete(NODE *symbol, int nsubs)
{
unsigned long hash1;
- NODE *bucket, *last;
- NODE *subs;
+ NODE *subs, *bucket, *last, *r;
+ int i;
assert(symbol->type == Node_var_array);
@@ -568,81 +661,63 @@ do { \
} while (--n > 0)
if (nsubs == 0) { /* delete array */
+ adjust_fcall_stack(symbol, 0); /* fix function call stack; See above. */
assoc_clear(symbol);
return;
}
/* NB: subscripts are in reverse order on stack */
- subs = PEEK(nsubs - 1);
- if (subs->type != Node_val) {
- if (--nsubs > 0)
- free_subs(nsubs);
- fatal(_("attempt to use array `%s' in a scalar context"), array_vname(subs));
- }
- (void) force_string(subs);
- last = NULL; /* shut up gcc -Wall */
- hash1 = 0; /* ditto */
+ for (i = nsubs; i > 0; i--) {
+ subs = PEEK(i - 1);
+ if (subs->type != Node_val) {
+ free_subs(i);
+ fatal(_("attempt to use array `%s' in a scalar context"), array_vname(subs));
+ }
+ (void) force_string(subs);
- if (symbol->var_array != NULL) {
- hash1 = hash(subs->stptr, subs->stlen,
- (unsigned long) symbol->array_size, NULL);
- last = NULL;
- for (bucket = symbol->var_array[hash1]; bucket != NULL;
- last = bucket, bucket = bucket->ahnext) {
- /*
- * This used to use cmp_nodes() here. That's wrong.
- * Array indices are strings; compare as such, always!
- */
- const char *s1_str;
- size_t s1_len;
- NODE *s2;
+ last = NULL; /* shut up gcc -Wall */
+ hash1 = 0; /* ditto */
+ bucket = NULL; /* array may be empty */
- s1_str = bucket->ahname_str;
- s1_len = bucket->ahname_len;
- s2 = subs;
-
- if (s1_len == s2->stlen) {
- if (s1_len == 0 /* "" is a valid index */
- || memcmp(s1_str, s2->stptr, s1_len) == 0)
- break;
- }
+ if (symbol->var_array != NULL) {
+ hash1 = hash(subs->stptr, subs->stlen,
+ (unsigned long) symbol->array_size, NULL);
+ bucket = assoc_find(symbol, subs, hash1, &last);
}
- } else
- bucket = NULL; /* The array is empty. */
- if (bucket == NULL) {
- if (do_lint)
- lintwarn(_("delete: index `%s' not in array `%s'"),
- subs->stptr, array_vname(symbol));
- DEREF(subs);
+ if (bucket == NULL) {
+ if (do_lint)
+ lintwarn(_("delete: index `%s' not in array `%s'"),
+ subs->stptr, array_vname(symbol));
+ /* avoid memory leak, free all subs */
+ free_subs(i);
+ return;
+ }
- /* avoid memory leak, free rest of the subs */
- if (--nsubs > 0)
- free_subs(nsubs);
- return;
+ if (i > 1) {
+ if (bucket->ahvalue->type != Node_var_array) {
+ /* e.g.: a[1] = 1; delete a[1][1] */
+ free_subs(i);
+ fatal(_("attempt to use scalar `%s[\"%.*s\"]' as an array"),
+ symbol->vname,
+ (int) bucket->ahname_len,
+ bucket->ahname_str);
+ }
+ symbol = bucket->ahvalue;
+ }
+ DEREF(subs);
}
- DEREF(subs);
- if (bucket->ahvalue->type == Node_var_array) {
- NODE *r = bucket->ahvalue;
- do_delete(r, nsubs - 1);
- if (r->var_array != NULL || nsubs > 1)
- return;
- /* else
- cleared a sub-array, free the array node
- and the bucket in parent array */
+ r = bucket->ahvalue;
+ if (r->type == Node_var_array) {
+ adjust_fcall_stack(r, nsubs); /* fix function call stack; See above. */
+ assoc_clear(r);
+ /* cleared a sub-array, free Node_var_array */
efree(r->vname);
freenode(r);
- } else if (--nsubs > 0) {
- /* e.g.: a[1] = 1; delete a[1][1] */
- free_subs(nsubs);
- fatal(_("attempt to use scalar `%s[\"%.*s\"]' as an array"),
- symbol->vname,
- (int) bucket->ahname_len,
- bucket->ahname_str);
} else
- unref(bucket->ahvalue);
+ unref(r);
if (last != NULL)
last->ahnext = bucket->ahnext;
@@ -663,6 +738,7 @@ do { \
#undef free_subs
}
+
/* do_delete_loop --- simulate ``for (iggy in foo) delete foo[iggy]'' */
/*
@@ -692,6 +768,7 @@ do_delete_loop(NODE *symbol, NODE **lhs)
}
/* blast the array in one shot */
+ adjust_fcall_stack(symbol, 0);
assoc_clear(symbol);
}
@@ -923,6 +1000,7 @@ dup_table(NODE *symbol, NODE *newsymb)
emalloc(aname, char *, aname_len + 2, "dup_table");
sprintf(aname, "%s[\"%.*s\"]", newsymb->vname, (int) chain->ahname_len, chain->ahname_str);
r->vname = aname;
+ r->parent_array = newsymb;
bucket->ahvalue = dup_table(chain->ahvalue, r);
} else
bucket->ahvalue = dupnode(chain->ahvalue);
@@ -979,6 +1057,21 @@ asort_actual(int nargs, SORT_CTXT ctxt)
_("asorti: first argument not an array"));
}
+ if (dest != NULL) {
+ for (r = dest->parent_array; r != NULL; r = r->parent_array) {
+ if (r == array)
+ fatal(ctxt == ASORT ?
+ _("asort: cannot use a subarray of first arg for second arg") :
+ _("asorti: cannot use a subarray of first arg for second arg"));
+ }
+ for (r = array->parent_array; r != NULL; r = r->parent_array) {
+ if (r == dest)
+ fatal(ctxt == ASORT ?
+ _("asort: cannot use a subarray of second arg for first arg") :
+ _("asorti: cannot use a subarray of second arg for first arg"));
+ }
+ }
+
num_elems = array->table_size;
if (num_elems == 0 || array->var_array == NULL) { /* source array is empty */
if (dest != NULL && dest != array)
@@ -1005,6 +1098,7 @@ asort_actual(int nargs, SORT_CTXT ctxt)
memset(result, '\0', sizeof(NODE));
result->type = Node_var_array;
result->vname = array->vname;
+ result->parent_array = array->parent_array;
}
subs = make_str_node(buf, TSIZE, ALREADY_MALLOCED); /* fake it */
@@ -1041,6 +1135,7 @@ asort_actual(int nargs, SORT_CTXT ctxt)
arr->type = Node_var_array;
arr->var_array = NULL;
arr->vname = estrdup(arr_name, strlen(arr_name));
+ arr->parent_array = array; /* actual parent, not the temporary one. */
*assoc_lookup(result, subs, FALSE) = dup_table(val, arr);
}
}