diff --git a/bash_completion b/bash_completion index 6b53346dcf6..555619a2cf0 100644 --- a/bash_completion +++ b/bash_completion @@ -190,11 +190,11 @@ _comp_dequote__initialize() _comp_dequote__initialize # This function expands a word using `eval` in a safe way. This function can -# be typically used to get the expanded value of `${word[i]}` as -# `_comp_dequote "${word[i]}"`. When the word contains unquoted shell special -# characters, command substitutions, and other unsafe strings, the function -# call fails before applying `eval`. Otherwise, `eval` is applied to the -# string to generate the result. +# be typically used to get the expanded value of `${word[i]}` as `_comp_dequote +# "${word[i]}"`. When the word contains unquoted shell special characters, +# command substitutions, and other unsafe strings, the function call fails +# before applying `eval` and REPLY is set to be the literal string. Otherwise, +# `eval` is applied to the string to generate the result. # # @param $1 String to be expanded. A safe word consists of the following # sequence of substrings: @@ -209,7 +209,12 @@ _comp_dequote__initialize # quotations, parameter expansions are allowed. # # @var[out] REPLY Array that contains the expanded results. Multiple words or -# no words may be generated through pathname expansions. +# no words may be generated through pathname expansions. If +# $1 is not a safe word, REPLY contains the literal value of +# $1. +# +# @return 0 if $1 is a safe word and the expansion result contains one word at +# least, or 1 otherwise. # # Note: This function allows parameter expansions as safe strings, which might # cause unexpected results: @@ -236,9 +241,15 @@ _comp_dequote__initialize # @since 2.12 _comp_dequote() { - REPLY=() # fallback value for unsafe word and failglob - [[ $1 =~ $_comp_dequote__regex_safe_word ]] || return 1 - eval "REPLY=($1)" 2>/dev/null # may produce failglob + if [[ ${1-} =~ $_comp_dequote__regex_safe_word ]]; then + REPLY=() # fallback value for failglob + eval "REPLY=($1)" 2>/dev/null # may produce failglob + ((${#REPLY[@]} > 0)) + return "$?" + else + REPLY=("${1-}") + return 1 + fi } # Unset the given variables across a scope boundary. Useful for unshadowing @@ -1627,7 +1638,7 @@ _comp_compgen_help__get_help_lines() --) shift 1 ;& *) local REPLY - _comp_dequote "${comp_args[0]-}" || REPLY=${comp_args[0]-} + _comp_dequote "${comp_args[0]-}" help_cmd=("${REPLY:-false}" "$@") ;; esac @@ -2923,7 +2934,7 @@ _comp_command_offset() if ((COMP_CWORD == 0)); then _comp_compgen_commands else - _comp_dequote "${COMP_WORDS[0]}" || REPLY=${COMP_WORDS[0]} + _comp_dequote "${COMP_WORDS[0]}" local cmd=${REPLY-} compcmd=${REPLY-} local cspec=$(complete -p -- "$cmd" 2>/dev/null) diff --git a/completions/java b/completions/java index 0827f7fd8c7..4ea6b817635 100644 --- a/completions/java +++ b/completions/java @@ -114,7 +114,7 @@ _comp_cmd_java__packages() local -a sourcepaths=("${REPLY[@]}") local REPLY - _comp_dequote "$cur" || REPLY=$cur + _comp_dequote "$cur" local cur_val=${REPLY-} # convert package syntax to path syntax diff --git a/completions/make b/completions/make index f9da4c90e11..639690b3edf 100644 --- a/completions/make +++ b/completions/make @@ -121,7 +121,7 @@ _comp_cmd_make() # Expand tilde expansion local REPLY _comp_dequote "${words[i + 1]-}" && - [[ -d ${REPLY-} ]] && + [[ -d $REPLY ]] && makef_dir=(-C "$REPLY") break fi @@ -134,7 +134,7 @@ _comp_cmd_make() # Expand tilde expansion local REPLY _comp_dequote "${words[i + 1]-}" && - [[ -f ${REPLY-} ]] && + [[ -f $REPLY ]] && makef=(-f "$REPLY") break fi diff --git a/completions/mutt b/completions/mutt index 5ebf65a463f..a741c8cdf9b 100644 --- a/completions/mutt +++ b/completions/mutt @@ -32,7 +32,7 @@ _comp_cmd_mutt__get_muttrc() shift done - if [[ ! $REPLY ]]; then + if [[ ! ${REPLY-} ]]; then if [[ -f ~/.${muttcmd}rc ]]; then REPLY=\~/.${muttcmd}rc elif [[ -f ~/.${muttcmd}/${muttcmd}rc ]]; then @@ -52,7 +52,7 @@ _comp_cmd_mutt__get_conffiles() local file for file; do _comp_dequote "$file" - _comp_cmd_mutt__get_conffiles__visit "$REPLY" + _comp_cmd_mutt__get_conffiles__visit "${REPLY-}" done ((${#conffiles[@]})) || return 1 REPLY=("${conffiles[@]}") diff --git a/completions/pkgutil b/completions/pkgutil index 0b2e4ec5d4b..d80f3679e91 100644 --- a/completions/pkgutil +++ b/completions/pkgutil @@ -33,8 +33,7 @@ _comp_cmd_pkgutil() catalog_files=("$REPLY") elif [[ ${words[i]} == --config ]]; then local REPLY - _comp_dequote "${words[i + 1]}" - [[ ${REPLY-} ]] && configuration_files=("$REPLY") + _comp_dequote "${words[i + 1]}" && configuration_files=("$REPLY") elif [[ ${words[i]} == -@([iurdacUS]|-install|-upgrade|-remove|-download|-available|-compare|-catalog|-stream) ]]; then command="${words[i]}" fi diff --git a/completions/ssh b/completions/ssh index 199343fa379..301ff1545c4 100644 --- a/completions/ssh +++ b/completions/ssh @@ -587,7 +587,7 @@ _comp_xfunc_scp_compgen_local_files() fi local REPLY - _comp_dequote "$cur" || REPLY=$cur + _comp_dequote "$cur" local cur_val=${REPLY-} local files diff --git a/test/t/unit/test_unit_dequote.py b/test/t/unit/test_unit_dequote.py index 117a487758d..5082f60af06 100644 --- a/test/t/unit/test_unit_dequote.py +++ b/test/t/unit/test_unit_dequote.py @@ -25,7 +25,7 @@ def test_2_str(self, bash, functions): assert output.strip() == "" def test_3_null(self, bash, functions): - output = assert_bash_exec(bash, "__tester ''", want_output=True) + output = assert_bash_exec(bash, "! __tester ''", want_output=True) assert output.strip() == "" def test_4_empty(self, bash, functions): @@ -108,25 +108,25 @@ def test_unsafe_1(self, bash, functions): output = assert_bash_exec( bash, "! __tester '$(echo hello >&2)'", want_output=True ) - assert output.strip() == "" + assert output.strip() == "<$(echo hello >&2)>" def test_unsafe_2(self, bash, functions): output = assert_bash_exec( bash, "! __tester '|echo hello >&2'", want_output=True ) - assert output.strip() == "" + assert output.strip() == "<|echo hello >&2>" def test_unsafe_3(self, bash, functions): output = assert_bash_exec( bash, "! __tester '>| important_file.txt'", want_output=True ) - assert output.strip() == "" + assert output.strip() == "<>| important_file.txt>" def test_unsafe_4(self, bash, functions): output = assert_bash_exec( bash, "! __tester '`echo hello >&2`'", want_output=True ) - assert output.strip() == "" + assert output.strip() == "<`echo hello >&2`>" def test_glob_default(self, bash, functions): with bash_env_saved(bash) as bash_env: @@ -160,6 +160,6 @@ def test_glob_nullglob(self, bash, functions): bash_env.shopt("failglob", False) bash_env.shopt("nullglob", True) output = assert_bash_exec( - bash, "__tester 'non-existent-*.txt'", want_output=True + bash, "! __tester 'non-existent-*.txt'", want_output=True ) assert output.strip() == ""