From: Chet Ramey Date: Mon, 26 Apr 2021 20:31:46 +0000 (-0400) Subject: allow assignment to array keys @ and *; minor completion fix X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=3fd77612fca0fa1956e54aac2e61bdcfecfe3b52;p=thirdparty%2Fbash.git allow assignment to array keys @ and *; minor completion fix --- diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index e0610f629..b719bef6b 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -10021,3 +10021,56 @@ builtins/bind.def lib/readline/doc/rltech.texi - RL_PROMPT_{START,END}_IGNORE: document current values of \001 and \002. Report from Mingye Wang + + 4/19 + ---- +arrayfunc.c + - assign_assoc_from_kvlist: fix memory leak reported by konsolebox + + + 4/20 + ---- +command.h,subst.c + - W_ITILDE: remove, replace with a variable since it's only used inside + a single call to expand_word_internal + + 4/21 + ---- +{subst.c,make_cmd.c,parse.y} + - W_DQUOTE: no longer used, use W_NOPROCSUB and W_NOTILDE directly + (for arithmetic commands and words in arithmetic for commands) + + 4/24 + ---- +bashline.c + - executable_completion: since this function gets an unquoted filename + from rl_filename_completion_function, we need to quote special + characters before passing it to bash_directory_completion_hook. + Report from Alex fxmbsw7 Ratchev + + 4/26 + ---- +lib/readline/search.c + - _rl_nsearch_abort: move function calls around so _rl_restore_prompt + happens before rl_clear_message, like when aborting an incremental + search. Suggested by sparrowhawk996@gmail.com + +subst.h + - ASS_ALLOWALLSUB: new assignment flag value, means to allow @ and * as + array subscripts when assigning to existing associative arrays + +arrayfunc.c + - assign_array_element: allow assignment of key `@' to an existing + associative array if the caller passes ASS_ALLOWALLSUB + - assign_compound_array_list: allow ( [@]=value ) to an existing + associative array + +builtins/declare.def + - declare_internal: allow assignment of key `@' to an existing + associative array by passing ASS_ALLOWALLSUB to assign_array_element + as part of local_aflags + +subst.c + - do_assignment_internal: allow a[@]=value to an existing associative + array by passing ASS_ALLOWALLSUB to assign_array_element + diff --git a/arrayfunc.c b/arrayfunc.c index 226f4e598..f57d7837d 100644 --- a/arrayfunc.c +++ b/arrayfunc.c @@ -338,7 +338,13 @@ assign_array_element (name, value, flags) entry = find_variable (vname); isassoc = entry && assoc_p (entry); - if (((isassoc == 0 || (flags & ASS_NOEXPAND) == 0) && (ALL_ELEMENT_SUB (sub[0]) && sub[1] == ']')) || (sublen <= 1)) + /* We don't allow assignment to `*' or `@' associative array keys if the + caller hasn't told us the subscript has already been expanded + (ASS_NOEXPAND). If the caller has explicitly told us it's ok + (ASS_ALLOWALLSUB) we allow it. */ + if (((isassoc == 0 || (flags & (ASS_NOEXPAND|ASS_ALLOWALLSUB)) == 0) && + (ALL_ELEMENT_SUB (sub[0]) && sub[1] == ']')) || + (sublen <= 1)) { free (vname); err_badarraysub (name); @@ -564,12 +570,9 @@ assign_assoc_from_kvlist (var, nlist, h, flags) { WORD_LIST *list; char *akey, *aval, *k, *v; - int free_aval; for (list = nlist; list; list = list->next) { - free_aval = 0; - k = list->word->word; v = list->next ? list->next->word->word : 0; @@ -577,24 +580,22 @@ assign_assoc_from_kvlist (var, nlist, h, flags) list = list->next; akey = expand_assignment_string_to_string (k, 0); - aval = expand_assignment_string_to_string (v, 0); - if (akey == 0 || *akey == 0) { err_badarraysub (k); FREE (akey); continue; } + + aval = expand_assignment_string_to_string (v, 0); if (aval == 0) { aval = (char *)xmalloc (1); aval[0] = '\0'; /* like do_assignment_internal */ - free_aval = 1; } bind_assoc_var_internal (var, h, akey, aval, flags); - if (free_aval) - free (aval); + free (aval); } } @@ -714,13 +715,10 @@ assign_compound_array_list (var, nlist, flags) continue; } - if (ALL_ELEMENT_SUB (w[1]) && len == 2) + if (ALL_ELEMENT_SUB (w[1]) && len == 2 && array_p (var)) { set_exit_status (EXECUTION_FAILURE); - if (assoc_p (var)) - report_error (_("%s: invalid associative array key"), w); - else - report_error (_("%s: cannot assign to non-numeric index"), w); + report_error (_("%s: cannot assign to non-numeric index"), w); continue; } diff --git a/bashline.c b/bashline.c index 362f0176a..f0fbe5386 100644 --- a/bashline.c +++ b/bashline.c @@ -1930,10 +1930,17 @@ executable_completion (filename, searching_path) const char *filename; int searching_path; { - char *f; + char *f, c; int r; + /* This gets an unquoted filename, so we need to quote special characters + in the filename before the completion hook gets it. */ +#if 0 f = savestring (filename); +#else + c = 0; + f = bash_quote_filename (filename, SINGLE_MATCH, &c); +#endif bash_directory_completion_hook (&f); r = searching_path ? executable_file (f) : executable_or_directory (f); diff --git a/builtins/declare.def b/builtins/declare.def index cb4bdc8ca..d1d3c04ea 100644 --- a/builtins/declare.def +++ b/builtins/declare.def @@ -942,6 +942,7 @@ restart_new_var_name: /* XXX - problem here with appending */ local_aflags = aflags&ASS_APPEND; local_aflags |= assoc_noexpand ? ASS_NOEXPAND : 0; + local_aflags |= ASS_ALLOWALLSUB; /* allow declare a[@]=at */ var = assign_array_element (name, value, local_aflags); /* XXX - not aflags */ *subscript_start = '\0'; if (var == 0) /* some kind of assignment error */ diff --git a/command.h b/command.h index b84775280..137608de7 100644 --- a/command.h +++ b/command.h @@ -1,7 +1,7 @@ /* command.h -- The structures used internally to represent commands, and the extern declarations of the functions used to create them. */ -/* Copyright (C) 1993-2020 Free Software Foundation, Inc. +/* Copyright (C) 1993-2021 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -81,18 +81,18 @@ enum command_type { cm_for, cm_case, cm_while, cm_if, cm_simple, cm_select, #define W_NOGLOB (1 << 5) /* Do not perform globbing on this word. */ #define W_NOSPLIT2 (1 << 6) /* Don't split word except for $@ expansion (using spaces) because context does not allow it. */ #define W_TILDEEXP (1 << 7) /* Tilde expand this assignment word */ -#define W_DOLLARAT (1 << 8) /* $@ and its special handling -- UNUSED */ -#define W_DOLLARSTAR (1 << 9) /* $* and its special handling -- UNUSED */ +#define W_DOLLARAT (1 << 8) /* UNUSED - $@ and its special handling */ +#define W_DOLLARSTAR (1 << 9) /* UNUSED - $* and its special handling */ #define W_NOCOMSUB (1 << 10) /* Don't perform command substitution on this word */ #define W_ASSIGNRHS (1 << 11) /* Word is rhs of an assignment statement */ #define W_NOTILDE (1 << 12) /* Don't perform tilde expansion on this word */ -#define W_ITILDE (1 << 13) /* Internal flag for word expansion */ +#define W_NOASSNTILDE (1 << 13) /* don't do tilde expansion like an assignment statement */ #define W_EXPANDRHS (1 << 14) /* Expanding word in ${paramOPword} */ #define W_COMPASSIGN (1 << 15) /* Compound assignment */ #define W_ASSNBLTIN (1 << 16) /* word is a builtin command that takes assignments */ #define W_ASSIGNARG (1 << 17) /* word is assignment argument to command */ #define W_HASQUOTEDNULL (1 << 18) /* word contains a quoted null character */ -#define W_DQUOTE (1 << 19) /* word should be treated as if double-quoted */ +#define W_DQUOTE (1 << 19) /* UNUSED - word should be treated as if double-quoted */ #define W_NOPROCSUB (1 << 20) /* don't perform process substitution */ #define W_SAWQUOTEDNULL (1 << 21) /* word contained a quoted null that was removed */ #define W_ASSIGNASSOC (1 << 22) /* word looks like associative array assignment */ @@ -102,8 +102,8 @@ enum command_type { cm_for, cm_case, cm_while, cm_if, cm_simple, cm_select, #define W_NOBRACE (1 << 26) /* Don't perform brace expansion */ #define W_COMPLETE (1 << 27) /* word is being expanded for completion */ #define W_CHKLOCAL (1 << 28) /* check for local vars on assignment */ -#define W_NOASSNTILDE (1 << 29) /* don't do tilde expansion like an assignment statement */ -#define W_FORCELOCAL (1 << 30) /* force assignments to be to local variables, non-fatal on assignment errors */ +#define W_FORCELOCAL (1 << 29) /* force assignments to be to local variables, non-fatal on assignment errors */ +/* UNUSED (1 << 30) */ /* Flags for the `pflags' argument to param_expand() and various parameter_brace_expand_xxx functions; also used for string_list_dollar_at */ diff --git a/doc/bash.1 b/doc/bash.1 index 0c57056f1..4dd91545b 100644 --- a/doc/bash.1 +++ b/doc/bash.1 @@ -9160,9 +9160,11 @@ is empty, or a non-existent directory stack entry is specified. .PP If the .B popd -command is successful, a +command is successful, +bash runs .B dirs -is performed as well, and the return status is 0. +to show the final contents of the directory stack, +and the return status is 0. .RE .TP \fBprintf\fP [\fB\-v\fP \fIvar\fP] \fIformat\fP [\fIarguments\fP] @@ -9271,9 +9273,10 @@ a non-existent directory stack element is specified. .PP If the .B pushd -command is successful, a +command is successful, +bash runs .B dirs -is performed as well. +to show the final contents of the directory stack. .RE .TP \fBpwd\fP [\fB\-LP\fP] diff --git a/doc/bashref.texi b/doc/bashref.texi index 7dbdb29aa..3e408c0aa 100644 --- a/doc/bashref.texi +++ b/doc/bashref.texi @@ -7720,8 +7720,9 @@ Otherwise, @code{popd} returns an unsuccessful status if an invalid option is encountered, the directory stack is empty, or a non-existent directory stack entry is specified. -If the @code{popd} command is successful, a @code{dirs} -is performed as well, and the return status is 0. +If the @code{popd} command is successful, +Bash runs @code{dirs} to show the final contents of the directory stack, +and the return status is 0. @btindex pushd @item pushd @@ -7764,7 +7765,8 @@ When rotating the directory stack, @code{pushd} returns 0 unless the directory stack is empty or a non-existent directory stack element is specified. -If the @code{pushd} command is successful, a @code{dirs} is performed as well. +If the @code{pushd} command is successful, +Bash runs @code{dirs} to show the final contents of the directory stack. @end table diff --git a/lib/readline/bind.c b/lib/readline/bind.c index e47fe2d76..fe73e3cb1 100644 --- a/lib/readline/bind.c +++ b/lib/readline/bind.c @@ -1154,6 +1154,12 @@ const char * const _rl_possible_meta_prefixes[] = { "Meta", "M-", (const char *)NULL }; +/* Forward declarations */ +static int parser_if (char *); +static int parser_else (char *); +static int parser_endif (char *); +static int parser_include (char *); + /* Conditionals. */ /* Calling programs set this to have their argv[0]. */ diff --git a/lib/readline/search.c b/lib/readline/search.c index 0d503e830..6cfe97d50 100644 --- a/lib/readline/search.c +++ b/lib/readline/search.c @@ -259,11 +259,11 @@ static void _rl_nsearch_abort (_rl_search_cxt *cxt) { rl_maybe_unsave_line (); - rl_clear_message (); rl_point = cxt->save_point; rl_mark = cxt->save_mark; - _rl_fix_point (1); rl_restore_prompt (); + rl_clear_message (); + _rl_fix_point (1); RL_UNSETSTATE (RL_STATE_NSEARCH); } diff --git a/make_cmd.c b/make_cmd.c index f9a8d1dfd..38cc0892b 100644 --- a/make_cmd.c +++ b/make_cmd.c @@ -251,7 +251,7 @@ make_arith_for_expr (s) if (s == 0 || *s == '\0') return ((WORD_LIST *)NULL); wd = make_word (s); - wd->flags |= W_NOGLOB|W_NOSPLIT|W_QUOTED|W_DQUOTE; /* no word splitting or globbing */ + wd->flags |= W_NOGLOB|W_NOSPLIT|W_QUOTED|W_NOTILDE; /* no word splitting or globbing */ #if defined (PROCESS_SUBSTITUTION) wd->flags |= W_NOPROCSUB; /* no process substitution */ #endif diff --git a/parse.y b/parse.y index 54957442f..7cc00b312 100644 --- a/parse.y +++ b/parse.y @@ -4717,7 +4717,7 @@ parse_dparen (c) { wd = alloc_word_desc (); wd->word = wval; - wd->flags = W_QUOTED|W_NOSPLIT|W_NOGLOB|W_DQUOTE; + wd->flags = W_QUOTED|W_NOSPLIT|W_NOGLOB|W_NOTILDE|W_NOPROCSUB; yylval.word_list = make_word_list (wd, (WORD_LIST *)NULL); return (ARITH_CMD); } diff --git a/subst.c b/subst.c index 4f12f22dd..8fd36724e 100644 --- a/subst.c +++ b/subst.c @@ -438,11 +438,6 @@ dump_word_flags (flags) f &= ~W_EXPANDRHS; fprintf (stderr, "W_EXPANDRHS%s", f ? "|" : ""); } - if (f & W_ITILDE) - { - f &= ~W_ITILDE; - fprintf (stderr, "W_ITILDE%s", f ? "|" : ""); - } if (f & W_NOTILDE) { f &= ~W_NOTILDE; @@ -3283,6 +3278,7 @@ do_assignment_internal (word, expand) report_error (_("%s: cannot assign list to array member"), name); ASSIGN_RETURN (0); } + aflags |= ASS_ALLOWALLSUB; entry = assign_array_element (name, value, aflags); if (entry == 0) ASSIGN_RETURN (0); @@ -10197,6 +10193,7 @@ expand_word_internal (word, quoted, isexp, contains_dollar_at, expanded_somethin int had_quoted_null; int has_quoted_ifs; /* did we add a quoted $IFS character here? */ int has_dollar_at, temp_has_dollar_at; + int internal_tilde; int split_on_spaces; int local_expanded; int tflag; @@ -10234,6 +10231,7 @@ expand_word_internal (word, quoted, isexp, contains_dollar_at, expanded_somethin quoted_dollar_at = had_quoted_null = has_dollar_at = 0; has_quoted_ifs = 0; split_on_spaces = 0; + internal_tilde = 0; /* expanding =~ or :~ */ quoted_state = UNQUOTED; string = word->word; @@ -10298,7 +10296,7 @@ add_string: { /* XXX - technically this should only be expanded at the start of a word */ - if (string[++sindex] != LPAREN || (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || (word->flags & (W_DQUOTE|W_NOPROCSUB))) + if (string[++sindex] != LPAREN || (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || (word->flags & W_NOPROCSUB)) { sindex--; /* add_character: label increments sindex */ goto add_character; @@ -10349,7 +10347,7 @@ add_string: assignoff == -1 && sindex > 0) assignoff = sindex; if (sindex == assignoff && string[sindex+1] == '~') /* XXX */ - word->flags |= W_ITILDE; + internal_tilde = 1; if (word->flags & W_ASSIGNARG) word->flags |= W_ASSIGNRHS; /* affects $@ */ @@ -10374,7 +10372,7 @@ add_string: if ((word->flags & (W_ASSIGNMENT|W_ASSIGNRHS)) && (posixly_correct == 0 || (word->flags & W_TILDEEXP)) && string[sindex+1] == '~') - word->flags |= W_ITILDE; + internal_tilde = 1; if (isexp == 0 && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0 && isifs (c)) goto add_ifs_character; @@ -10387,11 +10385,11 @@ add_string: assignment statement, we don't do tilde expansion. We don't do tilde expansion if quoted or in an arithmetic context. */ - if ((word->flags & (W_NOTILDE|W_DQUOTE)) || - (sindex > 0 && ((word->flags & W_ITILDE) == 0)) || + if ((word->flags & W_NOTILDE) || + (sindex > 0 && (internal_tilde == 0)) || (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) { - word->flags &= ~W_ITILDE; + internal_tilde = 0; if (isexp == 0 && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0 && isifs (c) && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) == 0) goto add_ifs_character; else @@ -10407,7 +10405,7 @@ add_string: temp = bash_tilde_find_word (string + sindex, tflag, &t_index); - word->flags &= ~W_ITILDE; + internal_tilde = 0; if (temp && *temp && t_index > 0) { diff --git a/subst.h b/subst.h index 6a69c3500..f8d715d40 100644 --- a/subst.h +++ b/subst.h @@ -55,6 +55,7 @@ #define ASS_NOEVAL 0x0100 /* don't evaluate value as expression */ #define ASS_NOLONGJMP 0x0200 /* don't longjmp on fatal assignment error */ #define ASS_NOINVIS 0x0400 /* don't resolve local invisible variables */ +#define ASS_ALLOWALLSUB 0x0800 /* allow * and @ as associative array keys */ /* Flags for the string extraction functions. */ #define SX_NOALLOC 0x0001 /* just skip; don't return substring */ diff --git a/tests/RUN-ONE-TEST b/tests/RUN-ONE-TEST index 0b0638107..c8bef8dd1 100755 --- a/tests/RUN-ONE-TEST +++ b/tests/RUN-ONE-TEST @@ -1,4 +1,4 @@ -BUILD_DIR=/usr/local/build/chet/bash/bash-current +BUILD_DIR=/usr/local/build/bash/bash-current THIS_SH=$BUILD_DIR/bash PATH=$PATH:$BUILD_DIR diff --git a/tests/array.right b/tests/array.right index 2d1c51db5..3a13f5abb 100644 --- a/tests/array.right +++ b/tests/array.right @@ -747,16 +747,15 @@ argv[1] = declare -A A=([$'\t']="2" [" "]="2" ) ./array27.sub: line 36: ((: A[]]=2 : syntax error: invalid arithmetic operator (error token is "]=2 ") declare -A A=([$'\t']="2" ["*"]="2" [" "]="2" ["@"]="2" ) -./array27.sub: line 45: A[]]: bad array subscript +declare -A A=([$'\t']="2" ["*"]="2" [" "]="2" ["]"]="2" ["@"]="2" ) +./array27.sub: line 52: A[]]: bad array subscript declare -A A=([$'\t']="X" ["*"]="X" [" "]="X" ["@"]="X" ) -./array27.sub: line 53: A[]]: bad array subscript +./array27.sub: line 60: A[]]: bad array subscript declare -A A=([$'\t']="X" ["*"]="X" [" "]="X" ["@"]="X" ) -./array27.sub: line 61: declare: `A[]]=X': not a valid identifier +./array27.sub: line 68: declare: `A[]]=X': not a valid identifier +declare -A A=(["*"]="X" ["@"]="X" ) +./array27.sub: line 76: declare: `A[]]=X': not a valid identifier declare -A A=(["*"]="X" ["@"]="X" ) -./array27.sub: line 69: declare: `A[]]=X': not a valid identifier -./array27.sub: line 69: A[*]: bad array subscript -./array27.sub: line 69: A[@]: bad array subscript -declare -A A declare -a bug4=([0]="" [1]="5" [2]="" [3]="1" [4]="") declare -a bug=([0]="" [1]="5" [2]="" [3]="1" [4]="") declare -a bug2=([0]="") diff --git a/tests/array27.sub b/tests/array27.sub index 44ed444dd..e2a1e7083 100644 --- a/tests/array27.sub +++ b/tests/array27.sub @@ -38,6 +38,13 @@ done declare -p A +unset A +declare -A A +for k in ']' '*' '@' $'\t' ' '; do + A[$k]=2 +done +declare -p A + unset A declare -A A @@ -69,4 +76,3 @@ for k in ']' '*' '@'; do declare "A[$k]=X" done declare -p A - diff --git a/tests/assoc.right b/tests/assoc.right index 20c260ae1..2c3f0933d 100644 --- a/tests/assoc.right +++ b/tests/assoc.right @@ -16,25 +16,24 @@ declare -A wheat=([two]="b" [three]="c" [one]="a" [zero]="0" ) declare -A chaff=(["hello world"]="flip" [one]="10" [zero]="5" ) ./assoc.tests: line 51: waste: readonly variable ./assoc.tests: line 52: unset: waste: cannot unset: readonly variable -./assoc.tests: line 53: chaff[*]: bad array subscript -./assoc.tests: line 54: [*]=12: invalid associative array key -declare -A chaff=(["hello world"]="flip" [one]="a" ) +declare -A chaff=(["*"]="12" ["hello world"]="flip" [one]="a" ) flip argv[1] = argv[2] = +argv[3] = <12> +argv[4] = +argv[5] = +argv[1] = +argv[2] = <12> argv[3] = argv[4] = -argv[1] = -argv[2] = -argv[3] = argv[1] = argv[2] = -argv[3] = -argv[4] = -argv[1] = +argv[3] = <12> +argv[4] = +argv[5] = +argv[1] = ./assoc.tests: line 71: declare: chaff: cannot destroy array variables in this way -./assoc.tests: line 73: chaff[*]: bad array subscript -./assoc.tests: line 74: [*]=12: invalid associative array key declare -A wheat=([six]="6" ["foo bar"]="qux qix" ) argv[1] = argv[2] = diff --git a/tests/assoc.tests b/tests/assoc.tests index 92bd96413..be7d5f6d7 100644 --- a/tests/assoc.tests +++ b/tests/assoc.tests @@ -47,7 +47,7 @@ declare +i chaff chaff[hello world]=flip declare -p chaff -# TEST - errors +# TEST - no longer errors waste[stuff]=other unset waste chaff[*]=12 diff --git a/tests/varenv.right b/tests/varenv.right index 358f5dc74..028a120f2 100644 --- a/tests/varenv.right +++ b/tests/varenv.right @@ -168,10 +168,9 @@ after bar: var=global ./varenv13.sub: line 16: `var[0]': not a valid identifier ./varenv13.sub: line 16: `var[@]': not a valid identifier ./varenv13.sub: line 14: declare: var: not found -./varenv13.sub: line 25: var[@]: bad array subscript -declare -A var=([0]="X" ) +declare -A var=([0]="X" ["@"]="Y" ) help -./varenv13.sub: line 34: `var[0]': not a valid identifier +./varenv13.sub: line 35: `var[0]': not a valid identifier 1 declare -A var=([0]="X" ) declare -A var=([Y]="Y" ) diff --git a/tests/varenv13.sub b/tests/varenv13.sub index 1f837ec7f..1fa7d5b37 100644 --- a/tests/varenv13.sub +++ b/tests/varenv13.sub @@ -22,6 +22,7 @@ typeset -A var f() { declare -p ${!var*}; } +# this is no longer an error var[0]=X var[@]=Y f