diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 01:03:18 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 01:03:18 +0000 |
commit | f2621414ee5f2f601424c22f00e207903e3b6104 (patch) | |
tree | 56a856dafd1ca684bb23263cacaa723ee4f404fc /completions/ssh | |
parent | Adding debian version 1:2.11-8. (diff) | |
download | bash-completion-f2621414ee5f2f601424c22f00e207903e3b6104.tar.xz bash-completion-f2621414ee5f2f601424c22f00e207903e3b6104.zip |
Merging upstream version 1:2.12.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'completions/ssh')
-rw-r--r-- | completions/ssh | 514 |
1 files changed, 297 insertions, 217 deletions
diff --git a/completions/ssh b/completions/ssh index 907c039..386fd25 100644 --- a/completions/ssh +++ b/completions/ssh @@ -1,195 +1,229 @@ # ssh(1) completion -*- shell-script -*- -_ssh_queries() +_comp_cmd_ssh__compgen_queries() { - COMPREPLY+=($(compgen -W \ - "cipher cipher-auth help mac kex key key-cert key-plain key-sig - protocol-version compression sig - ciphers macs kexalgorithms pubkeyacceptedkeytypes - hostkeyalgorithms hostbasedkeytypes hostbasedacceptedkeytypes" \ - -- "${cur,,}")) + local -a queries + _comp_compgen -v queries -i ssh query "$1" help || + queries=(cipher cipher-auth mac kex key key-cert key-plain key-sig + protocol-version compression sig ciphers macs kexalgorithms + pubkeyacceptedkeytypes hostkeyalgorithms hostbasedkeytypes + hostbasedacceptedkeytypes) + _comp_compgen -c "${cur,,}" -U queries -- -W '"${queries[@]}" help' } +# @since 2.12 +_comp_xfunc_ssh_compgen_query() +{ + _comp_cmd_ssh__compgen_query ssh "$1" +} + +# @deprecated 2.12 use _comp_xfunc_ssh_compgen_query _ssh_query() { - ${1:-ssh} -Q $2 2>/dev/null + local -a queries=() + _comp_compgen -v queries -i ssh query "${1:-ssh}" "$2" && + printf "%s\n" "${queries[@]}" } -_ssh_ciphers() +_comp_cmd_ssh__compgen_query() { - local ciphers='$(_ssh_query "$1" cipher)' - [[ $ciphers ]] || ciphers="3des-cbc aes128-cbc aes192-cbc aes256-cbc - aes128-ctr aes192-ctr aes256-ctr arcfour128 arcfour256 arcfour - blowfish-cbc cast128-cbc" - COMPREPLY+=($(compgen -W "$ciphers" -- "$cur")) + _comp_compgen_split -- "$("$1" -Q "$2" 2>/dev/null)" } -_ssh_macs() +_comp_cmd_ssh__compgen_ciphers() { - local macs='$(_ssh_query "$1" mac)' - [[ $macs ]] || macs="hmac-md5 hmac-sha1 umac-64@openssh.com hmac-ripemd160 - hmac-sha1-96 hmac-md5-96" - COMPREPLY+=($(compgen -W "$macs" -- "$cur")) + local -a queries + _comp_compgen -v queries -i ssh query "$1" cipher || + queries=(3des-cbc aes128-cbc aes192-cbc aes256-cbc aes128-ctr + aes192-ctr aes256-ctr arcfour128 arcfour256 arcfour blowfish-cbc + cast128-cbc) + _comp_compgen -U queries -- -W '"${queries[@]}"' } -_ssh_options() +_comp_cmd_ssh__compgen_macs() { - local opts=( - AddKeysToAgent AddressFamily BatchMode BindAddress CanonicalDomains - CanonicalizeFallbackLocal CanonicalizeHostname CanonicalizeMaxDots - CanonicalizePermittedCNAMEs CASignatureAlgorithms CertificateFile - ChallengeResponseAuthentication CheckHostIP Ciphers ClearAllForwardings - Compression ConnectionAttempts ConnectTimeout ControlMaster ControlPath - ControlPersist DynamicForward EnableSSHKeysign EscapeChar - ExitOnForwardFailure FingerprintHash ForwardAgent ForwardX11 - ForwardX11Timeout ForwardX11Trusted GatewayPorts GlobalKnownHostsFile - GSSAPIAuthentication GSSAPIClientIdentity GSSAPIDelegateCredentials - GSSAPIKeyExchange GSSAPIRenewalForcesRekey GSSAPIServerIdentity - GSSAPITrustDns HashKnownHosts Host HostbasedAuthentication - HostbasedKeyTypes HostKeyAlgorithms HostKeyAlias HostName + local -a queries + _comp_compgen -v queries -i ssh query "$1" mac || + queries=(hmac-md5 hmac-sha1 umac-64@openssh.com hmac-ripemd160 + hmac-sha1-96 hmac-md5-96) + _comp_compgen -U queries -- -W '"${queries[@]}"' +} + +# @since 2.12 +_comp_xfunc_ssh_compgen_options() +{ + # curl --silent https://raw.githubusercontent.com/openssh/openssh-portable/master/ssh_config.5 | _comp_awk '$1==".It" && $2=="Cm" && $3!="Host" && $3!="Match" {print " "$3}' | sort + local _opts=( + AddKeysToAgent AddressFamily BatchMode BindAddress BindInterface + CanonicalDomains CanonicalizeFallbackLocal CanonicalizeHostname + CanonicalizeMaxDots CanonicalizePermittedCNAMEs CASignatureAlgorithms + CertificateFile ChallengeResponseAuthentication CheckHostIP Ciphers + ClearAllForwardings Compression ConnectionAttempts ConnectTimeout + ControlMaster ControlPath ControlPersist DynamicForward + EnableSSHKeysign EscapeChar ExitOnForwardFailure FingerprintHash + ForwardAgent ForwardX11 ForwardX11Timeout ForwardX11Trusted + GatewayPorts GlobalKnownHostsFile GSSAPIAuthentication + GSSAPIDelegateCredentials HashKnownHosts HostbasedAuthentication + HostbasedKeyTypes HostKeyAlgorithms HostKeyAlias Hostname IdentitiesOnly IdentityAgent IdentityFile IgnoreUnknown Include IPQoS KbdInteractiveAuthentication KbdInteractiveDevices KexAlgorithms LocalCommand LocalForward LogLevel MACs NoHostAuthenticationForLocalhost NumberOfPasswordPrompts PasswordAuthentication PermitLocalCommand PKCS11Provider Port PreferredAuthentications ProxyCommand ProxyJump ProxyUseFdpass - PubkeyAcceptedKeyTypes PubkeyAuthentication RekeyLimit RemoteCommand - RemoteForward RequestTTY RevokedHostKeys SendEnv ServerAliveCountMax - ServerAliveInterval SmartcardDevice StreamLocalBindMask - StreamLocalBindUnlink StrictHostKeyChecking SyslogFacility TCPKeepAlive - Tunnel TunnelDevice UpdateHostKeys UsePrivilegedPort User - UserKnownHostsFile VerifyHostKeyDNS VisualHostKey XAuthLocation) - local protocols=$(_ssh_query "$1" protocol-version) - if [[ -z $protocols || $protocols == *1* ]]; then - opts+=(Cipher CompressionLevel Protocol RhostsRSAAuthentication + PubkeyAcceptedAlgorithms PubkeyAuthentication RekeyLimit RemoteCommand + RemoteForward RequestTTY RequiredRSASize RevokedHostKeys + SecurityKeyProvider SendEnv ServerAliveCountMax ServerAliveInterval + SetEnv StreamLocalBindMask StreamLocalBindUnlink StrictHostKeyChecking + SyslogFacility TCPKeepAlive Tunnel TunnelDevice UpdateHostKeys User + UserKnownHostsFile VerifyHostKeyDNS VisualHostKey XAuthLocation + ) + # Selected old ones + _opts+=( + GSSAPIKeyExchange GSSAPIRenewalForcesRekey GSSAPIServerIdentity + GSSAPITrustDns PubkeyAcceptedKeyTypes SmartcardDevice UsePrivilegedPort + ) + local -a protocols + _comp_compgen -v protocols -i ssh query ssh protocol-version + if [[ ${protocols[*]-} == *1* ]]; then + _opts+=(Cipher CompressionLevel Protocol RhostsRSAAuthentication RSAAuthentication) fi + _comp_unlocal protocols compopt -o nospace - local IFS=$' \t\n' reset=$(shopt -p nocasematch) - shopt -s nocasematch - local option - COMPREPLY=($(for option in "${opts[@]}"; do - [[ $option == "$cur"* ]] && printf '%s=\n' "$option" - done)) - $reset + _comp_compgen_split -l -- "$( + shopt -s nocasematch + local option + for option in "${_opts[@]}"; do + [[ $option == "$cur"* ]] && printf '%s=\n' "$option" + done + )" } +_comp_deprecate_func 2.12 _ssh_options _comp_xfunc_ssh_compgen_options + # Complete a ssh suboption (like ForwardAgent=y<tab>) -# Two parameters: the string to complete including the equal sign, and -# the ssh executable to invoke (optional). +# @param $1 the ssh executable to invoke +# @param $2 the string to complete including the equal sign # Not all suboptions are completed. # Doesn't handle comma-separated lists. -_ssh_suboption() +_comp_cmd_ssh__compgen_suboption() { # Split into subopt and subval - local prev=${1%%=*} cur=${1#*=} + local _prev=${2%%=*} cur=${2#*=} - case ${prev,,} in + case ${_prev,,} in batchmode | canonicaldomains | canonicalizefallbacklocal | \ challengeresponseauthentication | checkhostip | \ - clearallforwardings | controlpersist | compression | enablesshkeysign | \ - exitonforwardfailure | forwardagent | forwardx11 | forwardx11trusted | \ - gatewayports | gssapiauthentication | gssapikeyexchange | \ - gssapidelegatecredentials | gssapirenewalforcesrekey | gssapitrustdns | \ - hashknownhosts | hostbasedauthentication | identitiesonly | \ - kbdinteractiveauthentication | kbdinteractivedevices | \ - nohostauthenticationforlocalhost | passwordauthentication | permitlocalcommand | \ - proxyusefdpass | pubkeyauthentication | rhostsrsaauthentication | \ + clearallforwardings | controlpersist | compression | \ + enablesshkeysign | exitonforwardfailure | forwardagent | \ + forwardx11 | forwardx11trusted | gatewayports | \ + gssapiauthentication | gssapikeyexchange | \ + gssapidelegatecredentials | gssapirenewalforcesrekey | \ + gssapitrustdns | hashknownhosts | hostbasedauthentication | \ + identitiesonly | kbdinteractiveauthentication | \ + kbdinteractivedevices | nohostauthenticationforlocalhost | \ + passwordauthentication | permitlocalcommand | proxyusefdpass | \ + pubkeyauthentication | rhostsrsaauthentication | \ rsaauthentication | streamlocalbindunlink | \ tcpkeepalive | useprivilegedport | visualhostkey) - COMPREPLY=($(compgen -W 'yes no' -- "$cur")) + _comp_compgen -- -W 'yes no' ;; addkeystoagent) - COMPREPLY=($(compgen -W 'yes ask confirm no' -- "$cur")) + _comp_compgen -- -W 'yes ask confirm no' ;; addressfamily) - COMPREPLY=($(compgen -W 'any inet inet6' -- "$cur")) + _comp_compgen -- -W 'any inet inet6' ;; bindaddress) - _ip_addresses + _comp_compgen_ip_addresses ;; canonicalizehostname) - COMPREPLY=($(compgen -W 'yes no always' -- "$cur")) + _comp_compgen -- -W 'yes no always' ;; identityfile) - _ssh_identityfile + _comp_xfunc_ssh_compgen_identityfile ;; - *file | identityagent | include | controlpath | revokedhostkeys | xauthlocation) - _filedir + *file | identityagent | include | controlpath | revokedhostkeys | \ + xauthlocation) + _comp_compgen_filedir ;; casignaturealgorithms) - COMPREPLY=($(compgen -W '$(_ssh_query "$2" sig)' -- "$cur")) + _comp_cmd_ssh__compgen_query "$1" sig ;; cipher) - COMPREPLY=($(compgen -W 'blowfish des 3des' -- "$cur")) + _comp_compgen -- -W 'blowfish des 3des' ;; ciphers) - _ssh_ciphers "$2" + _comp_cmd_ssh__compgen_ciphers "$1" ;; controlmaster) - COMPREPLY=($(compgen -W 'yes ask auto autoask no' -- "$cur")) + _comp_compgen -- -W 'yes ask auto autoask no' ;; compressionlevel) - COMPREPLY=($(compgen -W '{1..9}' -- "$cur")) + _comp_compgen -- -W '{1..9}' ;; fingerprinthash) - COMPREPLY=($(compgen -W 'md5 sha256' -- "$cur")) + _comp_compgen -- -W 'md5 sha256' ;; ipqos) - COMPREPLY=($(compgen -W 'af1{1..4} af2{2..3} af3{1..3} af4{1..3} - cs{0..7} ef lowdelay throughput reliability' -- "$cur")) + _comp_compgen -- -W 'af1{1..4} af2{2..3} af3{1..3} af4{1..3} + cs{0..7} ef lowdelay throughput reliability' ;; hostbasedkeytypes | hostkeyalgorithms) - COMPREPLY=($(compgen -W '$(_ssh_query "$2" key)' -- "$cur")) + _comp_cmd_ssh__compgen_query "$1" key ;; kexalgorithms) - COMPREPLY=($(compgen -W '$(_ssh_query "$2" kex)' -- "$cur")) + _comp_cmd_ssh__compgen_query "$1" kex ;; loglevel) - COMPREPLY=($(compgen -W 'QUIET FATAL ERROR INFO VERBOSE DEBUG{,1,2,3}' -- "$cur")) + _comp_compgen -- -W 'QUIET FATAL ERROR INFO VERBOSE DEBUG{,1,2,3}' ;; macs) - _ssh_macs "$2" + _comp_cmd_ssh__compgen_macs "$1" ;; pkcs11provider) - _filedir so + _comp_compgen_filedir so ;; preferredauthentications) - COMPREPLY=($(compgen -W 'gssapi-with-mic host-based publickey - keyboard-interactive password' -- "$cur")) + _comp_compgen -- -W 'gssapi-with-mic host-based publickey + keyboard-interactive password' ;; protocol) - local protocols=($(_ssh_query "$2" protocol-version)) - [[ $protocols ]] || protocols=(1 2) + local -a protocols + _comp_compgen -v protocols -i ssh query "$1" protocol-version + [[ ${protocols-} ]] || protocols=(1 2) if ((${#protocols[@]} > 1)); then - COMPREPLY=($(compgen -W '${protocols[@]}' -- "$cur")) + _comp_compgen -- -W '"${protocols[@]}"' fi ;; proxyjump) - _known_hosts_real -a ${configfile:+-F "$configfile"} -- "$cur" + _comp_compgen_known_hosts -a ${configfile:+-F "$configfile"} -- "$cur" ;; proxycommand | remotecommand | localcommand) - COMPREPLY=($(compgen -c -- "$cur")) + _comp_compgen_commands ;; - pubkeyacceptedkeytypes) - COMPREPLY=($(compgen -W '$(_ssh_query "$2" key)' -- "$cur")) + pubkeyacceptedalgorithms | pubkeyacceptedkeytypes) + _comp_cmd_ssh__compgen_query "$1" key ;; requesttty) - COMPREPLY=($(compgen -W 'no yes force auto' -- "$cur")) + _comp_compgen -- -W 'no yes force auto' + ;; + requiredrsasize) + _comp_compgen -- -W '1024 2048 3072 4096 7680 15360' ;; stricthostkeychecking) - COMPREPLY=($(compgen -W 'accept-new ask no off' -- "$cur")) + _comp_compgen -- -W 'accept-new ask no off' ;; syslogfacility) - COMPREPLY=($(compgen -W 'DAEMON USER AUTH LOCAL{0..7}' -- "$cur")) + _comp_compgen -- -W 'DAEMON USER AUTH LOCAL{0..7}' ;; tunnel) - COMPREPLY=($(compgen -W 'yes no point-to-point ethernet' \ - -- "$cur")) + _comp_compgen -- -W 'yes no point-to-point ethernet' ;; updatehostkeys | verifyhostkeydns) - COMPREPLY=($(compgen -W 'yes no ask' -- "$cur")) + _comp_compgen -- -W 'yes no ask' ;; esac return 0 @@ -198,28 +232,46 @@ _ssh_suboption() # Try to complete -o SubOptions= # # Returns 0 if the completion was handled or non-zero otherwise. -_ssh_suboption_check() +# @since 2.12 +_comp_xfunc_ssh_compgen_suboption_check() +{ + _comp_cmd_ssh__compgen_suboption_check ssh +} + +# @param $1 the ssh executable to invoke +_comp_cmd_ssh__compgen_suboption_check() { # Get prev and cur words without splitting on = - local cureq=$(_get_cword :=) preveq=$(_get_pword :=) - if [[ $cureq == *=* && $preveq == -*o ]]; then - _ssh_suboption $cureq "$1" + local cur prev + _comp_get_words -n := cur prev + if [[ $cur == *=* && $prev == -*o ]]; then + _comp_unlocal prev + _comp_cmd_ssh__compgen_suboption "$1" "$cur" return $? fi return 1 } +# @deprecated 2.12 use `_comp_xfunc_ssh_compgen_suboption_check` instead +_ssh_suboption_check() +{ + _comp_cmd_ssh__compgen_suboption_check "${1:-ssh}" +} + # Search COMP_WORDS for '-F configfile' or '-Fconfigfile' argument -_ssh_configfile() +# @var[out] configfile Found configfile, if any +_comp_cmd_ssh__configfile() { + configfile="" set -- "${words[@]}" while (($# > 0)); do if [[ $1 == -F* ]]; then + local REPLY if ((${#1} > 2)); then - configfile="$(dequote "${1:2}")" + _comp_dequote "${1:2}" && configfile=$REPLY else shift - [[ ${1-} ]] && configfile="$(dequote "$1")" + [[ ${1-} ]] && _comp_dequote "$1" && configfile=$REPLY fi break fi @@ -228,79 +280,86 @@ _ssh_configfile() } # With $1 set, look for public key files, else private +# @since 2.12 # shellcheck disable=SC2120 -_ssh_identityfile() +_comp_xfunc_ssh_compgen_identityfile() { - [[ -z $cur && -d ~/.ssh ]] && cur=~/.ssh/id - _filedir - if ((${#COMPREPLY[@]} > 0)); then - COMPREPLY=($(compgen -W '${COMPREPLY[@]}' \ - -X "${1:+!}*.pub" -- "$cur")) - fi + local cur=$cur tmp + [[ ! $cur && -d ~/.ssh ]] && cur=~/.ssh/id + _comp_compgen -v tmp -c "$cur" filedir && + _comp_compgen -U tmp -- -W '"${tmp[@]}"' -X "${1:+!}*.pub" } -_ssh() +_comp_deprecate_func 2.12 _ssh_identityfile _comp_xfunc_ssh_compgen_identityfile + +_comp_cmd_ssh() { - local cur prev words cword - _init_completion -n : || return + local cur prev words cword comp_args + _comp_initialize -n : -- "$@" || return local configfile - _ssh_configfile + _comp_cmd_ssh__configfile - _ssh_suboption_check "$1" && return + _comp_cmd_ssh__compgen_suboption_check "$1" && return local ipvx + # Keep cases sorted the same they're in ssh's usage message + # (but do group ones with same arg completion) case $prev in + -*B) + _comp_compgen_available_interfaces -a + return + ;; -*b) - _ip_addresses + _comp_compgen_ip_addresses return ;; -*c) - _ssh_ciphers "$1" + _comp_cmd_ssh__compgen_ciphers "$1" return ;; -*[DeLpRW]) return ;; -*[EFS]) - _filedir + _comp_compgen_filedir return ;; - -*i) - _ssh_identityfile + -*I) + _comp_compgen_filedir so return ;; - -*I) - _filedir so + -*i) + _comp_xfunc_ssh_compgen_identityfile return ;; -*J) - _known_hosts_real -a ${configfile:+-F "$configfile"} -- "$cur" + _comp_compgen_known_hosts -a ${configfile:+-F "$configfile"} -- "$cur" return ;; -*l) - COMPREPLY=($(compgen -u -- "$cur")) + _comp_compgen -- -u return ;; -*m) - _ssh_macs "$1" + _comp_cmd_ssh__compgen_macs "$1" return ;; -*O) - COMPREPLY=($(compgen -W 'check forward cancel exit stop' -- "$cur")) + _comp_compgen -- -W 'check forward cancel exit stop' return ;; -*o) - _ssh_options "$1" + _comp_xfunc_ssh_compgen_options "$1" return ;; -*Q) - _ssh_queries "$1" + _comp_cmd_ssh__compgen_queries "$1" return ;; -*w) - _available_interfaces + _comp_compgen_available_interfaces return ;; -*4*) @@ -312,67 +371,71 @@ _ssh() esac if [[ $cur == -F* ]]; then - cur=${cur#-F} - _filedir + _comp_compgen -c "${cur#-F}" filedir # Prefix completions with '-F' COMPREPLY=("${COMPREPLY[@]/#/-F}") - cur=-F$cur # Restore cur elif [[ $cur == -* ]]; then - COMPREPLY=($(compgen -W '$(_parse_usage "$1")' -- "$cur")) + _comp_compgen_usage else - _known_hosts_real ${ipvx-} -a ${configfile:+-F "$configfile"} -- "$cur" - - local args - _count_args - if ((args > 1)); then + local REPLY + # Keep glob sort in sync with cases above + _comp_count_args -n "=" -a "-*[BbcDeLpRWEFSIiJlmOoQw]" + if ((REPLY > 1)); then compopt -o filenames - COMPREPLY+=($(compgen -c -- "$cur")) + _comp_compgen_commands + else + _comp_compgen_known_hosts ${ipvx-} -a ${configfile:+-F "$configfile"} \ + -- "$cur" fi fi } && - shopt -u hostcomplete && complete -F _ssh ssh slogin autossh sidedoor + shopt -u hostcomplete && + complete -F _comp_cmd_ssh ssh slogin autossh sidedoor # sftp(1) completion # -_sftp() +_comp_cmd_sftp() { - local cur prev words cword - _init_completion || return + local cur prev words cword comp_args + _comp_initialize -- "$@" || return local configfile - _ssh_configfile + _comp_cmd_ssh__configfile - _ssh_suboption_check && return + # Prefer `ssh` from same dir for resolving options, etc + local pathcmd + pathcmd=$(type -P "$1") && local PATH=${pathcmd%/*}:$PATH - local ipvx + _comp_xfunc_ssh_compgen_suboption_check && return + + local ipvx= case $prev in -*[BDlPRs]) return ;; -*[bF]) - _filedir + _comp_compgen_filedir return ;; -*i) - _ssh_identityfile + _comp_xfunc_ssh_compgen_identityfile return ;; -*c) - _ssh_ciphers + _comp_cmd_ssh__compgen_ciphers return ;; -*J) - _known_hosts_real -a ${configfile:+-F "$configfile"} -- "$cur" + _comp_compgen_known_hosts -a ${configfile:+-F "$configfile"} -- "$cur" return ;; -*o) - _ssh_options + _comp_xfunc_ssh_compgen_options return ;; -*S) - compopt -o filenames - COMPREPLY=($(compgen -c -- "$cur")) + _comp_compgen_commands return ;; -*4*) @@ -384,100 +447,119 @@ _sftp() esac if [[ $cur == -F* ]]; then - cur=${cur#-F} - _filedir + _comp_compgen -c "${cur#-F}" filedir # Prefix completions with '-F' COMPREPLY=("${COMPREPLY[@]/#/-F}") - cur=-F$cur # Restore cur elif [[ $cur == -* ]]; then - COMPREPLY=($(compgen -W '$(_parse_usage "$1")' -- "$cur")) + _comp_compgen_usage else - _known_hosts_real ${ipvx-} -a ${configfile:+-F "$configfile"} -- "$cur" + _comp_compgen_known_hosts ${ipvx:+"$ipvx"} -a ${configfile:+-F "$configfile"} -- "$cur" fi } && - shopt -u hostcomplete && complete -F _sftp sftp + shopt -u hostcomplete && complete -F _comp_cmd_sftp sftp # things we want to backslash escape in scp paths # shellcheck disable=SC2089 -_scp_path_esc='[][(){}<>"'"'"',:;^&!$=?`\\|[:space:]]' +_comp_cmd_scp__path_esc='[][(){}<>"'"'"',:;^&!$=?`\\|[:space:]]' # Complete remote files with ssh. If the first arg is -d, complete on dirs # only. Returns paths escaped with three backslashes. +# @since 2.12 # shellcheck disable=SC2120 -_scp_remote_files() +_comp_xfunc_scp_compgen_remote_files() { - local IFS=$'\n' - # remove backslash escape from the first colon cur=${cur/\\:/:} - local userhost=${cur%%?(\\):*} - local path=${cur#*:} + local _userhost=${cur%%?(\\):*} + local _path=${cur#*:} # unescape (3 backslashes to 1 for chars we escaped) # shellcheck disable=SC2090 - path=$(command sed -e 's/\\\\\\\('$_scp_path_esc'\)/\\\1/g' <<<"$path") + _path=$(command sed -e 's/\\\\\\\('"$_comp_cmd_scp__path_esc"'\)/\\\1/g' <<<"$_path") # default to home dir of specified user on remote host - if [[ -z $path ]]; then - path=$(ssh -o 'Batchmode yes' $userhost pwd 2>/dev/null) + if [[ ! $_path ]]; then + _path=$(ssh -o 'Batchmode yes' "$_userhost" pwd 2>/dev/null) fi - local files - if [[ $1 == -d ]]; then + local _files + if [[ ${1-} == -d ]]; then # escape problematic characters; remove non-dirs # shellcheck disable=SC2090 - files=$(ssh -o 'Batchmode yes' $userhost \ - command ls -aF1dL "$path*" 2>/dev/null | - command sed -e 's/'$_scp_path_esc'/\\\\\\&/g' -e '/[^\/]$/d') + _files=$(ssh -o 'Batchmode yes' "$_userhost" \ + command ls -aF1dL "$_path*" 2>/dev/null | + command sed -e 's/'"$_comp_cmd_scp__path_esc"'/\\\\\\&/g' -e '/[^\/]$/d') else # escape problematic characters; remove executables, aliases, pipes # and sockets; add space at end of file names # shellcheck disable=SC2090 - files=$(ssh -o 'Batchmode yes' $userhost \ - command ls -aF1dL "$path*" 2>/dev/null | - command sed -e 's/'$_scp_path_esc'/\\\\\\&/g' -e 's/[*@|=]$//g' \ + _files=$(ssh -o 'Batchmode yes' "$_userhost" \ + command ls -aF1dL "$_path*" 2>/dev/null | + command sed -e 's/'"$_comp_cmd_scp__path_esc"'/\\\\\\&/g' -e 's/[*@|=]$//g' \ -e 's/[^\/]$/& /g') fi - COMPREPLY+=($files) + _comp_compgen_split -l -- "$_files" } -# This approach is used instead of _filedir to get a space appended -# after local file/dir completions, and -o nospace retained for others. -# If first arg is -d, complete on directory names only. The next arg is -# an optional prefix to add to returned completions. -_scp_local_files() +# @deprecated 2.12 use `_comp_compgen -ax ssh remote_files` instead +_scp_remote_files() { - local IFS=$'\n' + _comp_compgen -ax scp remote_files +} - local dirsonly=false +# This approach is used instead of _comp_compgen_filedir to get a space +# appended after local file/dir completions, and -o nospace retained for +# others. If first arg is -d, complete on directory names only. The next arg +# is an optional prefix to add to returned completions. +# @since 2.12 +_comp_xfunc_scp_compgen_local_files() +{ + local _dirsonly="" if [[ ${1-} == -d ]]; then - dirsonly=true + _dirsonly=set shift fi - if $dirsonly; then - COMPREPLY+=($(command ls -aF1dL $cur* 2>/dev/null | - command sed -e "s/$_scp_path_esc/\\\\&/g" -e '/[^\/]$/d' -e "s/^/${1-}/")) + local files + _comp_expand_glob files '"$cur"*' || return 0 + if [[ $_dirsonly ]]; then + _comp_compgen -U files split -l -- "$( + command ls -aF1dL "${files[@]}" 2>/dev/null | + command sed -e "s/$_comp_cmd_scp__path_esc/\\\\&/g" \ + -e '/[^\/]$/d' -e "s/^/${1-}/" + )" else - COMPREPLY+=($(command ls -aF1dL $cur* 2>/dev/null | - command sed -e "s/$_scp_path_esc/\\\\&/g" -e 's/[*@|=]$//g' \ - -e 's/[^\/]$/& /g' -e "s/^/${1-}/")) + _comp_compgen -U files split -l -- "$( + command ls -aF1dL "${files[@]}" 2>/dev/null | + command sed -e "s/$_comp_cmd_scp__path_esc/\\\\&/g" \ + -e 's/[*@|=]$//g' -e 's/[^\/]$/& /g' -e "s/^/${1-}/" + )" fi } +# @deprecated 2.12 +_scp_local_files() +{ + _comp_compgen -ax scp local_files "$@" +} + # scp(1) completion # -_scp() +_comp_cmd_scp() { - local cur prev words cword - _init_completion -n : || return + local cur prev words cword comp_args + _comp_initialize -n : -- "$@" || return local configfile - _ssh_configfile + _comp_cmd_ssh__configfile - _ssh_suboption_check && { - COMPREPLY=("${COMPREPLY[@]/%/ }") + # Prefer `ssh` from same dir for resolving options, remote files, etc + local pathcmd + pathcmd=$(type -P "$1") && local PATH=${pathcmd%/*}:$PATH + + _comp_xfunc_ssh_compgen_suboption_check && { + ((${#COMPREPLY[@]})) && COMPREPLY=("${COMPREPLY[@]/%/ }") return } @@ -485,34 +567,34 @@ _scp() case $prev in -*c) - _ssh_ciphers + _comp_cmd_ssh__compgen_ciphers COMPREPLY=("${COMPREPLY[@]/%/ }") return ;; -*F) - _filedir + _comp_compgen_filedir compopt +o nospace return ;; -*i) - _ssh_identityfile + _comp_xfunc_ssh_compgen_identityfile compopt +o nospace return ;; -*J) - _known_hosts_real -a ${configfile:+-F "$configfile"} -- "$cur" + _comp_compgen_known_hosts -a ${configfile:+-F "$configfile"} -- "$cur" return ;; -*[lP]) return ;; -*o) - _ssh_options + _comp_xfunc_ssh_compgen_options return ;; -*S) - compopt +o nospace -o filenames - COMPREPLY=($(compgen -c -- "$cur")) + compopt +o nospace + _comp_compgen_commands return ;; -*4*) @@ -523,12 +605,12 @@ _scp() ;; esac - _expand || return + _comp_expand || return case $cur in !(*:*)/* | [.~]*) ;; # looks like a path *:*) - _scp_remote_files + _comp_xfunc_scp_compgen_remote_files return ;; esac @@ -541,23 +623,21 @@ _scp() else case $cur in -*) - COMPREPLY=($(compgen -W '$(_parse_usage "${words[0]}")' \ - -- "$cur")) + _comp_compgen_usage 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 + _comp_compgen_known_hosts ${ipvx-} -c -a \ + ${configfile:+-F "$configfile"} -- "$cur" + fi ;; esac fi - _scp_local_files "${prefix-}" + _comp_compgen -ax scp local_files "${prefix-}" } && - complete -F _scp -o nospace scp + complete -F _comp_cmd_scp -o nospace scp # ex: filetype=sh |