diff --git a/bash_completion b/bash_completion index 2f00f7bd8d7..f1dc0f31449 100644 --- a/bash_completion +++ b/bash_completion @@ -390,6 +390,14 @@ _comp_expand_glob() return 0 } +# Check if the argument looks like a path. +# @param $1 thing to check +# @return True (0) if it does, False (> 0) otherwise +_comp_looks_like_path() +{ + [[ ${1-} == @(*/|[.~])* ]] +} + # Reassemble command line words, excluding specified characters from the # list of word completion separators (COMP_WORDBREAKS). # @param $1 chars Characters out of $COMP_WORDBREAKS which should diff --git a/completions/apt-get b/completions/apt-get index dd60e561cd9..7ceefb57d67 100644 --- a/completions/apt-get +++ b/completions/apt-get @@ -39,7 +39,7 @@ _apt_get() awk '$1 == "Source:" { print $2 }' | sort -u)" -- "$cur")) ;; install | reinstall) - if [[ $cur == */* ]]; then + if _comp_looks_like_path "$cur"; then _filedir deb return elif [[ $cur == *=* ]]; then @@ -58,7 +58,7 @@ _apt_get() ;;& build-dep) _filedir -d - [[ $cur != */* ]] || return + _comp_looks_like_path "$cur" && return ;;& *) COMPREPLY+=($(_comp_xfunc apt-cache packages)) diff --git a/completions/gdb b/completions/gdb index 333766aff23..e6197ffb884 100644 --- a/completions/gdb +++ b/completions/gdb @@ -17,7 +17,7 @@ _gdb() if ((cword == 1)); then local IFS compopt -o filenames - if [[ $cur == */* ]]; then + if _comp_looks_like_path "$cur"; then # compgen -c works as expected if $cur contains any slashes. IFS=$'\n' COMPREPLY=($(PATH="$PATH:." compgen -d -c -- "$cur")) diff --git a/completions/kldload b/completions/kldload index 3cce9423fc7..9079ec58434 100644 --- a/completions/kldload +++ b/completions/kldload @@ -7,7 +7,7 @@ _kldload() local cur prev words cword _init_completion || return - if [[ "$cur" == */* ]]; then + if _comp_looks_like_path "$cur"; then _filedir ko return fi diff --git a/completions/man b/completions/man index fb5ef717e6d..84f43fd51d6 100644 --- a/completions/man +++ b/completions/man @@ -52,7 +52,7 @@ _man() fi # file based completion if parameter looks like a path - if [[ $cur == @(*/|[.~])* ]]; then + if _comp_looks_like_path "$cur"; then _filedir "$manext" return fi diff --git a/completions/pydoc b/completions/pydoc index 877f369d488..5295cde9bb5 100644 --- a/completions/pydoc +++ b/completions/pydoc @@ -24,7 +24,7 @@ _pydoc() COMPREPLY=($(compgen -W 'keywords topics modules' -- "$cur")) - if [[ $cur != @(.|*/)* ]]; then + if ! _comp_looks_like_path "$cur"; then local python=python [[ ${1##*/} == *3* ]] && python=python3 _comp_xfunc python modules $python diff --git a/completions/pylint b/completions/pylint index a40f850e800..6b3b39e1a66 100644 --- a/completions/pylint +++ b/completions/pylint @@ -107,7 +107,7 @@ _pylint() return fi - [[ $cur == @(.|*/)* ]] || _comp_xfunc python modules $python + _comp_looks_like_path "$cur" || _comp_xfunc python modules $python _filedir py } && complete -F _pylint pylint pylint-2 pylint-3 diff --git a/completions/removepkg b/completions/removepkg index ef3bd7384ab..392ff899e29 100644 --- a/completions/removepkg +++ b/completions/removepkg @@ -9,7 +9,7 @@ _removepkg() return fi - if [[ $cur == */* ]]; then + if _comp_looks_like_path "$cur"; then _filedir return fi diff --git a/completions/rpm b/completions/rpm index d4e3e08dcac..0142d82a98f 100644 --- a/completions/rpm +++ b/completions/rpm @@ -92,7 +92,7 @@ _rpm() ;; --whatenhances | --whatprovides | --whatrecommends | --whatrequires | \ --whatsuggests | --whatsupplements) - if [[ $cur == */* ]]; then + if _comp_looks_like_path "$cur"; then _filedir else # complete on capabilities diff --git a/completions/ssh b/completions/ssh index 49a61d9b10d..ccf7241ef79 100644 --- a/completions/ssh +++ b/completions/ssh @@ -580,12 +580,11 @@ _scp() COMPREPLY=("${COMPREPLY[@]/%/ }") return ;; - */* | [.~]*) - # not a known host, pass through - ;; *) - _known_hosts_real ${ipvx-} -c -a \ - ${configfile:+-F "$configfile"} -- "$cur" + if ! _comp_looks_like_path "$cur"; then + _known_hosts_real ${ipvx-} -c -a \ + ${configfile:+-F "$configfile"} -- "$cur" + fi ;; esac fi diff --git a/completions/vpnc b/completions/vpnc index d9abfd3cbd6..f5f740b558a 100644 --- a/completions/vpnc +++ b/completions/vpnc @@ -61,7 +61,7 @@ _vpnc() if [[ $cur == -* ]]; then COMPREPLY=($(compgen -W '$(_parse_help "$1" --long-help)' -- "$cur")) - elif [[ $cur == */* ]]; then + elif _comp_looks_like_path "$cur"; then # explicit filename _filedir conf else diff --git a/test/t/unit/test_unit_looks_like_path.py b/test/t/unit/test_unit_looks_like_path.py new file mode 100644 index 00000000000..196199231df --- /dev/null +++ b/test/t/unit/test_unit_looks_like_path.py @@ -0,0 +1,31 @@ +import pytest + +from conftest import TestUnitBase, assert_bash_exec + + +@pytest.mark.bashcomp(cmd=None) +class TestUnitQuote(TestUnitBase): + @pytest.mark.parametrize( + "thing_looks_like", + ( + ("", False), + ("foo", False), + ("/foo", True), + ("foo/", True), + ("foo/bar", True), + (".", True), + ("../", True), + ("~", True), + ("~foo", True), + ), + ) + def test_1(self, bash, thing_looks_like): + thing, looks_like = thing_looks_like + output = assert_bash_exec( + bash, + f"_comp_looks_like_path '{thing}'; printf %s $?", + want_output=True, + want_newline=False, + ) + is_zero = output.strip() == "0" + assert (looks_like and is_zero) or (not looks_like and not is_zero)