Skip to content

refactor(scp): factorize common codes #1412

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 10, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 56 additions & 28 deletions completions/ssh
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,54 @@ _comp_cmd_sftp()
# things we want to backslash escape in scp paths
_comp_cmd_scp__path_esc='[][(){}<>"'"'"',:;^&!$=?`\\|[:space:]]'

# Escape shell special characters in filenames by backslash. This also
# suffixes a space or a slash based on the file type.
#
# Note: With a non-empty prefix ($1 of _comp_xfunc_scp_compgen_local_files),
# Bash will not recognize any filenames, so we need to perform the proper
# quoting manually. We also need to manually suffix a space or a slash based
# on the file type because "-o nospace" is specified. One might think of using
# "compopt +o nospace" instead, but it would suffix a space to directory names
# unexpectedly.
#
# Options:
# -d Only directory names are selected.
# @param $1 escape_replacement - If a non-empty value is specified, special
# characters are replaced with the specified value (instead of the default
# '\\&').
# @stdin List of filenames in the "ls -1F" format, where filenames are
# separated by newlines, and characters /*@|=> are suffixed based on the
# types of the files.
_comp_cmd_scp__escape_path()
{
local OPTIND=1 OPTARG="" OPTERR=0 opt dirs_only=""
while getopts ':d' _flag "$@"; do
case $_flag in
d) dirs_only=set ;;
*)
echo "bash_completion: $FUNCNAME: usage error: $*" >&2
return 1
;;
esac
done
shift "$((OPTIND - 1))"
local escape_replacement=${1:-'\\&'}

if [[ $dirs_only ]]; then
# escape problematic characters; remove non-dirs
command sed \
-e '/[^/]$/d' \
-e 's/'"$_comp_cmd_scp__path_esc"'/'"$escape_replacement"'/g'
else
# escape problematic characters; remove executable, symlink, pipe,
# socket and door indicators; add space at end of file names
command sed \
-e 's/[*@|=>]$//g' \
-e 's/'"$_comp_cmd_scp__path_esc"'/'"$escape_replacement"'/g' \
-e 's/[^/]$/& /g'
fi
}

# Complete remote files with ssh. Returns paths escaped with three backslashes
# (unless -l option is provided).
# Options:
Expand Down Expand Up @@ -501,20 +549,10 @@ _comp_xfunc_scp_compgen_remote_files()
fi

local _files
if [[ $_dirs_only ]]; then
# escape problematic characters; remove non-dirs
_files=$(ssh -o 'Batchmode yes' "$_userhost" \
command ls -aF1dL "$_path*" 2>/dev/null |
command sed -e 's/'"$_comp_cmd_scp__path_esc"'/'"$_escape_replacement"'/g' -e '/[^/]$/d')
else
# escape problematic characters; remove executables, aliases, pipes
# and sockets; add space at end of file names
_files=$(ssh -o 'Batchmode yes' "$_userhost" \
command ls -aF1dL "$_path*" 2>/dev/null |
command sed -e 's/[*@|=]$//g' \
-e 's/'"$_comp_cmd_scp__path_esc"'/'"$_escape_replacement"'/g' \
-e 's/[^/]$/& /g')
fi
_files=$(ssh -o 'Batchmode yes' "$_userhost" \
command ls -aF1dL "$_path*" 2>/dev/null |
_comp_cmd_scp__escape_path ${_dirs_only:+'-d'} -- \
"$_escape_replacement")
_comp_compgen -R split -l -- "$_files"
}

Expand All @@ -539,20 +577,10 @@ _comp_xfunc_scp_compgen_local_files()

local files
_comp_expand_glob files '"$cur"*' || return 0
if [[ $_dirs_only ]]; then
_comp_compgen -RU files split -l ${1:+-P "$1"} -- "$(
command ls -aF1dL "${files[@]}" 2>/dev/null |
command sed -e "s/$_comp_cmd_scp__path_esc/\\\\&/g" \
-e '/[^/]$/d'
)"
else
_comp_compgen -RU files split -l ${1:+-P "$1"} -- "$(
command ls -aF1dL "${files[@]}" 2>/dev/null |
command sed -e 's/[*@|=]$//g' \
-e "s/$_comp_cmd_scp__path_esc/\\\\&/g" \
-e 's/[^/]$/& /g'
)"
fi
_comp_compgen -RU files split -l ${1:+-P "$1"} -- "$(
command ls -aF1dL "${files[@]}" 2>/dev/null |
_comp_cmd_scp__escape_path ${_dirs_only:+'-d'}
)"
}

# @deprecated 2.12
Expand Down