diff options
author | Arnold D. Robbins <arnold@skeeve.com> | 2011-05-04 23:05:28 +0300 |
---|---|---|
committer | Arnold D. Robbins <arnold@skeeve.com> | 2011-05-04 23:05:28 +0300 |
commit | e7dced088c226280bcb1edb252011d6c186504e4 (patch) | |
tree | 868eabc52db1e4a08e1385868e1a56062e74ea40 | |
parent | 19093d5a231421594788d633e811859276d8f92f (diff) | |
download | egawk-e7dced088c226280bcb1edb252011d6c186504e4.tar.gz egawk-e7dced088c226280bcb1edb252011d6c186504e4.tar.bz2 egawk-e7dced088c226280bcb1edb252011d6c186504e4.zip |
Fix problem with subarray of deleted array.
-rw-r--r-- | ChangeLog | 36 | ||||
-rw-r--r-- | array.c | 235 | ||||
-rw-r--r-- | awk.h | 13 | ||||
-rw-r--r-- | awkgram.c | 7 | ||||
-rw-r--r-- | awkgram.y | 7 | ||||
-rw-r--r-- | command.c | 2 | ||||
-rw-r--r-- | command.y | 2 | ||||
-rw-r--r-- | eval.c | 2 | ||||
-rw-r--r-- | field.c | 65 | ||||
-rw-r--r-- | io.c | 2 | ||||
-rw-r--r-- | test/ChangeLog | 5 | ||||
-rw-r--r-- | test/Makefile.am | 4 | ||||
-rw-r--r-- | test/delsub.awk | 2 | ||||
-rw-r--r-- | test/delsub.ok | 1 |
14 files changed, 269 insertions, 114 deletions
@@ -1,3 +1,39 @@ +Wed May 4 23:04:06 2011 John Haque <j.eh@mchsi.com> + + Fix the problem (crash) with disappearing array argument when + it is a subarray of another deleted array argument. + + * awk.h (struct exp_node): Nuke unused field sub.nodep.number. + New field sub.nodep.rn. + (parent_array): New definition for sub.nodep.rn to keep track + of the parent of a subarray. + * awkgram.y (mk_symbol): Initialize parent_array to NULL. + * eval.c (r_interpret): In the case Op_sub_array, assign + parent_array. + * array.c (get_array): Initialize parent_array to NULL when + a Node_var_new becomes a Node_var_array. + (assoc_find): Add a fourth argument for the previous node + of the returned bucket. + (in_array, assoc_lookup): Adjust calls to assoc_find(). + (adjust_fcall_stack): New routine to change a soon-to-be deleted + subarray parameter in the function call stack to a local array. + (do_delete): Simplify code, remove recursive usage. Call + adjust_fcall_stack() where appropriate. + (do_delete_loop): Call adjust_fcall_stack() before clearing the + array. + (asort_actual): Don't accept an array and its subarray as + arguments for asort() or asorti(). + (asort_actual, dup_table): For asort(), appropriately assign + parent_array when creating the result array. + * field.c (do_split, do_patsplit): An array and its subarray not + accepted for the second and the fourth arguments. Remove + unnecessary dupnode of the field seperator node. + + Unrelated: + * awkgram.y (LEX_DELETE, simple_variable): Change type argument + from Node_var_array to Node_var_new for calls to variable(). + * io.c (devopen): Fix parsing GAWK_MSEC_SLEEP env variable. + Mon May 2 23:44:34 2011 Arnold D. Robbins <arnold@skeeve.com> * dfa.c (parse_bracket_exp): Sync with GNU grep, since we @@ -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); } } @@ -319,7 +319,7 @@ typedef struct exp_node { char **param_list; } x; char *name; - short number; + struct exp_node *rn; unsigned long reflags; # define CASE 1 # define CONSTANT 2 @@ -375,8 +375,8 @@ typedef struct exp_node { * function name; see awkgram.y */ # define FIELD 512 /* this is a field */ # define INTLSTR 1024 /* use localized version */ -# define WSTRCUR 2048 /* wide str value is current */ -# define NUMIND 4096 /* numeric val of index is current */ +# define NUMIND 2048 /* numeric val of index is current */ +# define WSTRCUR 4096 /* wide str value is current */ } NODE; @@ -419,9 +419,10 @@ typedef struct exp_node { #define var_assign sub.nodep.x.aptr /* Node_var_array: */ -#define var_array sub.nodep.r.av -#define array_size sub.nodep.l.ll -#define table_size sub.nodep.x.xl +#define var_array sub.nodep.r.av +#define array_size sub.nodep.l.ll +#define table_size sub.nodep.x.xl +#define parent_array sub.nodep.rn /* Node_array_ref: */ #define orig_array lnode @@ -3005,7 +3005,7 @@ regular_loop: char *arr = (yyvsp[(2) - (4)])->lextok; (yyvsp[(2) - (4)])->opcode = Op_push_array; - (yyvsp[(2) - (4)])->memory = variable(arr, Node_var_array); + (yyvsp[(2) - (4)])->memory = variable(arr, Node_var_new); if ((yyvsp[(4) - (4)]) == NULL) { static short warned = FALSE; @@ -3044,7 +3044,7 @@ regular_loop: error_ln((yyvsp[(1) - (4)])->source_line, _("`delete array' is a gawk extension")); } - (yyvsp[(3) - (4)])->memory = variable(arr, Node_var_array); + (yyvsp[(3) - (4)])->memory = variable(arr, Node_var_new); (yyvsp[(3) - (4)])->opcode = Op_push_array; (yyvsp[(1) - (4)])->expr_count = 0; (yyval) = list_append(list_create((yyvsp[(3) - (4)])), (yyvsp[(1) - (4)])); @@ -4115,7 +4115,7 @@ regular_loop: char *arr = (yyvsp[(1) - (2)])->lextok; if ((n = lookup(arr)) != NULL && ! isarray(n)) yyerror(_("use of non-array as array")); - (yyvsp[(1) - (2)])->memory = variable(arr, Node_var_array); + (yyvsp[(1) - (2)])->memory = variable(arr, Node_var_new); (yyvsp[(1) - (2)])->opcode = Op_push_array; (yyval) = list_prepend((yyvsp[(2) - (2)]), (yyvsp[(1) - (2)])); } @@ -6219,6 +6219,7 @@ mk_symbol(NODETYPE type, NODE *value) r->flags = MALLOC; r->lnode = value; r->rnode = NULL; + r->parent_array = NULL; r->var_assign = (Func_ptr) 0; return r; } @@ -986,7 +986,7 @@ simple_stmt char *arr = $2->lextok; $2->opcode = Op_push_array; - $2->memory = variable(arr, Node_var_array); + $2->memory = variable(arr, Node_var_new); if ($4 == NULL) { static short warned = FALSE; @@ -1024,7 +1024,7 @@ simple_stmt error_ln($1->source_line, _("`delete array' is a gawk extension")); } - $3->memory = variable(arr, Node_var_array); + $3->memory = variable(arr, Node_var_new); $3->opcode = Op_push_array; $1->expr_count = 0; $$ = list_append(list_create($3), $1); @@ -1711,7 +1711,7 @@ simple_variable char *arr = $1->lextok; if ((n = lookup(arr)) != NULL && ! isarray(n)) yyerror(_("use of non-array as array")); - $1->memory = variable(arr, Node_var_array); + $1->memory = variable(arr, Node_var_new); $1->opcode = Op_push_array; $$ = list_prepend($2, $1); } @@ -3572,6 +3572,7 @@ mk_symbol(NODETYPE type, NODE *value) r->flags = MALLOC; r->lnode = value; r->rnode = NULL; + r->parent_array = NULL; r->var_assign = (Func_ptr) 0; return r; } @@ -3020,7 +3020,7 @@ again: /* force a quit, and let do_quit (in debug.c) exit */ if (! seen_eof) { if (errno != 0) { - fprintf(stderr, _("can't read command (%s)"), strerror(errno)); + fprintf(stderr, _("can't read command (%s)\n"), strerror(errno)); exit_val = EXIT_FAILURE; } /* else exit_val = EXIT_SUCCESS; */ @@ -1047,7 +1047,7 @@ again: /* force a quit, and let do_quit (in debug.c) exit */ if (! seen_eof) { if (errno != 0) { - fprintf(stderr, _("can't read command (%s)"), strerror(errno)); + fprintf(stderr, _("can't read command (%s)\n"), strerror(errno)); exit_val = EXIT_FAILURE; } /* else exit_val = EXIT_SUCCESS; */ @@ -455,6 +455,7 @@ flags2str(int flagval) { FUNC, "FUNC" }, { FIELD, "FIELD" }, { INTLSTR, "INTLSTR" }, + { NUMIND, "NUMIND" }, #ifdef WSTRCUR { WSTRCUR, "WSTRCUR" }, #endif @@ -1758,6 +1759,7 @@ top: r->type = Node_var_array; r->var_array = NULL; r->vname = estrdup(arr_name, strlen(arr_name)); + r->parent_array = t1; *assoc_lookup(t1, t2, FALSE) = r; } else if (r->type != Node_var_array) { const char *arr_name = make_aname(t1, t2); @@ -956,12 +956,20 @@ do_split(int nargs) if (arr->type != Node_var_array) fatal(_("split: second argument is not an array")); - assoc_clear(arr); if (sep_arr != NULL) { if (sep_arr == arr) - fatal(_("split: can not use the same array for second and fourth args")); + fatal(_("split: cannot use the same array for second and fourth args")); + + /* This checks need to be done before clearing any of the arrays */ + for (tmp = sep_arr->parent_array; tmp != NULL; tmp = tmp->parent_array) + if (tmp == arr) + fatal(_("split: cannot use a subarray of second arg for fourth arg")); + for (tmp = arr->parent_array; tmp != NULL; tmp = tmp->parent_array) + if (tmp == sep_arr) + fatal(_("split: cannot use a subarray of fourth arg for second arg")); assoc_clear(sep_arr); } + assoc_clear(arr); src = TOP_STRING(); if (src->stlen == 0) { @@ -975,10 +983,10 @@ do_split(int nargs) if ((sep->re_flags & FS_DFLT) != 0 && current_field_sep() != Using_FIELDWIDTHS && ! RS_is_null) { parseit = parse_field; - fs = dupnode(force_string(FS_node->var_value)); + fs = force_string(FS_node->var_value); rp = FS_regexp; } else { - fs = dupnode(sep->re_exp); + fs = sep->re_exp; if (fs->stlen == 0) { static short warned = FALSE; @@ -1009,7 +1017,6 @@ do_split(int nargs) decr_sp(); DEREF(src); - unref(fs); return tmp; } @@ -1036,39 +1043,41 @@ do_patsplit(int nargs) fatal(_("patsplit: second argument is not an array")); src = TOP_STRING(); - if (src->stlen == 0) { - /* - * Skip the work if first arg is the null string. - */ - assoc_clear(arr); - if (sep_arr != NULL) - assoc_clear(sep_arr); - decr_sp(); - DEREF(src); - return make_number((AWKNUM) 0); - } - fpat = dupnode(sep->re_exp); - if (fpat->stlen == 0) { - unref(fpat); + fpat = sep->re_exp; + if (fpat->stlen == 0) fatal(_("patsplit: third argument must be non-null")); - } - assoc_clear(arr); + if (sep_arr != NULL) { if (sep_arr == arr) - fatal(_("patsplit: can not use the same array for second and fourth args")); + fatal(_("patsplit: cannot use the same array for second and fourth args")); + + /* This checks need to be done before clearing any of the arrays */ + for (tmp = sep_arr->parent_array; tmp != NULL; tmp = tmp->parent_array) + if (tmp == arr) + fatal(_("patsplit: cannot use a subarray of second arg for fourth arg")); + for (tmp = arr->parent_array; tmp != NULL; tmp = tmp->parent_array) + if (tmp == sep_arr) + fatal(_("patsplit: cannot use a subarray of fourth arg for second arg")); assoc_clear(sep_arr); } + assoc_clear(arr); - rp = re_update(sep); - - s = src->stptr; - tmp = make_number((AWKNUM) fpat_parse_field(UNLIMITED, &s, + if (src->stlen == 0) { + /* + * Skip the work if first arg is the null string. + */ + tmp = make_number((AWKNUM) 0); + } else { + rp = re_update(sep); + s = src->stptr; + tmp = make_number((AWKNUM) fpat_parse_field(UNLIMITED, &s, (int) src->stlen, fpat, rp, set_element, arr, sep_arr, FALSE)); - decr_sp(); + } + + decr_sp(); /* 1st argument not POP-ed */ DEREF(src); - unref(fpat); return tmp; } @@ -1504,7 +1504,7 @@ devopen(const char *name, const char *mode) */ if ((ms2 = getenv("GAWK_MSEC_SLEEP")) != NULL) { msleep = strtol(ms2, &end, 10); - if (end != cp && msleep < 0) + if (end == ms2 || msleep < 0) msleep = 1000; else msleep *= 1000; diff --git a/test/ChangeLog b/test/ChangeLog index 3754c554..ffaf4629 100644 --- a/test/ChangeLog +++ b/test/ChangeLog @@ -1,3 +1,8 @@ +Wed May 4 23:03:06 2011 Arnold D. Robbins <arnold@skeeve.com> + + * delsub.awk, delsub.ok: New files. + * Makefile.am (delsub): New test. + Fri Apr 22 16:07:01 2011 John Haque <j.eh@mchsi.com> * sortu.awk, sortu.ok: New files. diff --git a/test/Makefile.am b/test/Makefile.am index 8eeb80da..c4cfcebc 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -155,6 +155,8 @@ EXTRA_DIST = \ delarpm2.ok \ delfunc.awk \ delfunc.ok \ + delsub.awk \ + delsub.ok \ devfd.in1 \ devfd.in2 \ devfd.in4 \ @@ -775,7 +777,7 @@ GAWK_EXT_TESTS = \ aadelete1 aadelete2 aarray1 aasort aasorti \ arraysort \ argtest backw badargs beginfile1 binmode1 clos1way \ - devfd devfd1 devfd2 dumpvars \ + delsub devfd devfd1 devfd2 dumpvars \ fieldwdth fpat1 funlen fsfwfs fwtest fwtest2 gensub gensub2 getlndir \ gnuops2 gnuops3 gnureops \ icasefs icasers igncdym igncfs ignrcas2 ignrcase indirectcall lint \ diff --git a/test/delsub.awk b/test/delsub.awk new file mode 100644 index 00000000..0c3ffb0e --- /dev/null +++ b/test/delsub.awk @@ -0,0 +1,2 @@ +function f(c, d, x) { delete c; x = d[0] } +BEGIN { a[0][0] = 1; f(a, a[0]); print "still here" } diff --git a/test/delsub.ok b/test/delsub.ok new file mode 100644 index 00000000..ae3eb5bb --- /dev/null +++ b/test/delsub.ok @@ -0,0 +1 @@ +still here |