diff options
Diffstat (limited to 'bash_completion')
-rw-r--r-- | bash_completion | 481 |
1 files changed, 253 insertions, 228 deletions
diff --git a/bash_completion b/bash_completion index ca84b01d..58987a7f 100644 --- a/bash_completion +++ b/bash_completion @@ -3,7 +3,7 @@ # bash_completion - programmable completion functions for bash 4.1+ # # Copyright © 2006-2008, Ian Macdonald <ian@caliban.org> -# © 2009-2018, Bash Completion Maintainers +# © 2009-2019, Bash Completion Maintainers # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -23,7 +23,7 @@ # # https://github.com/scop/bash-completion -BASH_COMPLETION_VERSINFO=(2 8) +BASH_COMPLETION_VERSINFO=(2 9) if [[ $- == *v* ]]; then BASH_COMPLETION_ORIGINAL_V_VALUE="-v" @@ -85,7 +85,7 @@ complete -b builtin # @param $1 userland to check for _userland() { - local userland=$( uname -s ) + local userland=$(uname -s) [[ $userland == @(Linux|GNU/*) ]] && userland=GNU [[ $userland == $1 ]] } @@ -124,7 +124,7 @@ have() # _rl_enabled() { - [[ "$( bind -v )" == *$1+([[:space:]])on* ]] + [[ "$(bind -v)" == *$1+([[:space:]])on* ]] } # This function shell-quotes the argument @@ -146,7 +146,7 @@ quote_readline() # This function shell-dequotes the argument dequote() { - eval printf %s "$1" 2> /dev/null + eval printf %s "$1" 2>/dev/null } @@ -161,6 +161,8 @@ dequote() # See: http://fvue.nl/wiki/Bash:_Passing_variables_by_reference _upvar() { + echo "bash_completion: $FUNCNAME: deprecated function," \ + "use _upvars instead" >&2 if unset -v "$1"; then # Unset & validate varname if (( $# == 2 )); then eval $1=\"\$2\" # Return single value @@ -182,32 +184,33 @@ _upvar() _upvars() { if ! (( $# )); then - echo "${FUNCNAME[0]}: usage: ${FUNCNAME[0]} [-v varname"\ - "value] | [-aN varname [value ...]] ..." 1>&2 + echo "bash_completion: $FUNCNAME: usage: $FUNCNAME" \ + "[-v varname value] | [-aN varname [value ...]] ..." >&2 return 2 fi while (( $# )); do case $1 in -a*) # Error checking - [[ ${1#-a} ]] || { echo "bash: ${FUNCNAME[0]}: \`$1': missing"\ - "number specifier" 1>&2; return 1; } - printf %d "${1#-a}" &> /dev/null || { echo "bash:"\ - "${FUNCNAME[0]}: \`$1': invalid number specifier" 1>&2 + [[ ${1#-a} ]] || { echo "bash_completion: $FUNCNAME:" \ + "\`$1': missing number specifier" >&2; return 1; } + printf %d "${1#-a}" &>/dev/null || { echo bash_completion: \ + "$FUNCNAME: \`$1': invalid number specifier" >&2 return 1; } # Assign array of -aN elements [[ "$2" ]] && unset -v "$2" && eval $2=\(\"\${@:3:${1#-a}}\"\) && - shift $((${1#-a} + 2)) || { echo "bash: ${FUNCNAME[0]}:"\ - "\`$1${2+ }$2': missing argument(s)" 1>&2; return 1; } + shift $((${1#-a} + 2)) || { echo bash_completion: \ + "$FUNCNAME: \`$1${2+ }$2': missing argument(s)" \ + >&2; return 1; } ;; -v) # Assign single value [[ "$2" ]] && unset -v "$2" && eval $2=\"\$3\" && - shift 3 || { echo "bash: ${FUNCNAME[0]}: $1: missing"\ - "argument(s)" 1>&2; return 1; } + shift 3 || { echo "bash_completion: $FUNCNAME: $1:" \ + "missing argument(s)" >&2; return 1; } ;; *) - echo "bash: ${FUNCNAME[0]}: $1: invalid option" 1>&2 + echo "bash_completion: $FUNCNAME: $1: invalid option" >&2 return 1 ;; esac done @@ -275,7 +278,7 @@ __reassemble_comp_words_by_ref() [[ $i == $COMP_CWORD ]] && printf -v "$3" %s "$j" else # No, list of word completions separators hasn't changed; - for i in ${!COMP_WORDS[@]}; do + for i in "${!COMP_WORDS[@]}"; do printf -v "$2[i]" %s "${COMP_WORDS[i]}" done fi @@ -318,7 +321,7 @@ __get_cword_at_cursor_by_ref() local old_size=${#cur} cur="${cur#"${words[i]}"}" local new_size=${#cur} - index=$(( index - old_size + new_size )) + (( index -= old_size - new_size )) fi done # Clear $cur if just space(s) @@ -380,10 +383,10 @@ _get_comp_words_by_ref() prev) vprev=prev ;; cword) vcword=cword ;; words) vwords=words ;; - *) echo "bash: $FUNCNAME(): \`${!OPTIND}': unknown argument" \ - 1>&2; return 1 + *) echo "bash_completion: $FUNCNAME: \`${!OPTIND}':" \ + "unknown argument" >&2; return 1 ;; esac - let "OPTIND += 1" + (( OPTIND += 1 )) done __get_cword_at_cursor_by_ref "$exclude" words cword cur @@ -448,7 +451,7 @@ _get_cword() local old_size="${#cur}" cur="${cur#${words[i]}}" local new_size="${#cur}" - index=$(( index - old_size + new_size )) + (( index -= old_size - new_size )) fi done @@ -553,12 +556,11 @@ _filedir() _tilde "$cur" || return local -a toks - local x tmp + local x reset - x=$( compgen -d -- "$cur" ) && - while read -r tmp; do - toks+=( "$tmp" ) - done <<< "$x" + reset=$(shopt -po noglob); set -o noglob + toks=( $(compgen -d -- "$cur") ) + IFS=' '; $reset; IFS=$'\n' if [[ "$1" != -d ]]; then local quoted @@ -567,17 +569,16 @@ _filedir() # Munge xspec to contain uppercase version too # http://thread.gmane.org/gmane.comp.shells.bash.bugs/15294/focus=15306 local xspec=${1:+"!*.@($1|${1^^})"} - x=$( compgen -f -X "$xspec" -- $quoted ) && - while read -r tmp; do - toks+=( "$tmp" ) - done <<< "$x" + reset=$(shopt -po noglob); set -o noglob + toks+=( $(compgen -f -X "$xspec" -- $quoted) ) + IFS=' '; $reset; IFS=$'\n' # Try without filter if it failed to produce anything and configured to - [[ -n ${COMP_FILEDIR_FALLBACK:-} && -n "$1" && ${#toks[@]} -lt 1 ]] && \ - x=$( compgen -f -- $quoted ) && - while read -r tmp; do - toks+=( "$tmp" ) - done <<< "$x" + [[ -n ${COMP_FILEDIR_FALLBACK:-} && -n "$1" && ${#toks[@]} -lt 1 ]] && { + reset=$(shopt -po noglob); set -o noglob + toks+=( $(compgen -f -- $quoted) ) + IFS=' '; $reset; IFS=$'\n' + } fi if [[ ${#toks[@]} -ne 0 ]]; then @@ -616,8 +617,8 @@ _variables() # Completing $var / ${var / ${!var / ${#var if [[ $cur == \${* ]]; then local arrs vars - vars=( $( compgen -A variable -P ${BASH_REMATCH[1]} -S '}' -- ${BASH_REMATCH[3]} ) ) && \ - arrs=( $( compgen -A arrayvar -P ${BASH_REMATCH[1]} -S '[' -- ${BASH_REMATCH[3]} ) ) + vars=( $(compgen -A variable -P ${BASH_REMATCH[1]} -S '}' -- ${BASH_REMATCH[3]}) ) && \ + arrs=( $(compgen -A arrayvar -P ${BASH_REMATCH[1]} -S '[' -- ${BASH_REMATCH[3]}) ) if [[ ${#vars[@]} -eq 1 && $arrs ]]; then # Complete ${arr with ${array[ if there is only one match, and that match is an array variable compopt -o nospace @@ -628,14 +629,14 @@ _variables() fi else # Complete $var with $variable - COMPREPLY+=( $( compgen -A variable -P '$' -- "${BASH_REMATCH[3]}" ) ) + COMPREPLY+=( $(compgen -A variable -P '$' -- "${BASH_REMATCH[3]}") ) fi return 0 elif [[ $cur =~ ^(\$\{[#!]?)([A-Za-z0-9_]*)\[([^]]*)$ ]]; then # Complete ${array[i with ${array[idx]} local IFS=$'\n' - COMPREPLY+=( $( compgen -W '$(printf %s\\n "${!'${BASH_REMATCH[2]}'[@]}")' \ - -P "${BASH_REMATCH[1]}${BASH_REMATCH[2]}[" -S ']}' -- "${BASH_REMATCH[3]}" ) ) + COMPREPLY+=( $(compgen -W '$(printf %s\\n "${!'${BASH_REMATCH[2]}'[@]}")' \ + -P "${BASH_REMATCH[1]}${BASH_REMATCH[2]}[" -S ']}' -- "${BASH_REMATCH[3]}") ) # Complete ${arr[@ and ${arr[* if [[ ${BASH_REMATCH[3]} == [@*] ]]; then COMPREPLY+=( "${BASH_REMATCH[1]}${BASH_REMATCH[2]}[${BASH_REMATCH[3]}]}" ) @@ -652,7 +653,7 @@ _variables() TZ) cur=/usr/share/zoneinfo/$cur _filedir - for i in ${!COMPREPLY[@]}; do + for i in "${!COMPREPLY[@]}"; do if [[ ${COMPREPLY[i]} == *.tab ]]; then unset 'COMPREPLY[i]' continue @@ -686,7 +687,7 @@ _variables() # _init_completion() { - local exclude= flag outx errx inx OPTIND=1 + local exclude="" flag outx errx inx OPTIND=1 while getopts "n:e:o:i:s" flag "$@"; do case $flag in @@ -739,9 +740,9 @@ _init_completion() # If "bare" redirect, remove also the next word (skip=2). [[ ${words[i]} == $redir ]] && skip=2 || skip=1 words=( "${words[@]:0:i}" "${words[@]:i+skip}" ) - [[ $i -le $cword ]] && cword=$(( cword - skip )) + [[ $i -le $cword ]] && (( cword -= skip )) else - i=$(( ++i )) + (( i++ )) fi done @@ -760,8 +761,7 @@ __parse_options() # Take first found long option, or first one (short) if not found. option= - local -a array - read -a array <<<"$1" + local -a array=( $1 ) for i in "${array[@]}"; do case "$i" in ---*) break ;; @@ -770,7 +770,7 @@ __parse_options() *) break ;; esac done - [[ $option ]] || return + [[ $option ]] || return 0 IFS=$' \t\n' # affects parsing of the regexps below... @@ -792,18 +792,18 @@ __parse_options() # _parse_help() { - eval local cmd=$( quote "$1" ) + eval local cmd=$(quote "$1") local line { case $cmd in -) cat ;; - *) LC_ALL=C "$( dequote "$cmd" )" ${2:---help} 2>&1 ;; + *) LC_ALL=C "$(dequote "$cmd")" ${2:---help} 2>&1 ;; esac } \ | while read -r line; do [[ $line == *([[:blank:]])-* ]] || continue # transform "-f FOO, --foo=FOO" to "-f , --foo=FOO" etc while [[ $line =~ \ - ((^|[^-])-[A-Za-z0-9?][[:space:]]+)\[?[A-Z0-9]+\]? ]]; do + ((^|[^-])-[A-Za-z0-9?][[:space:]]+)\[?[A-Z0-9]+([,_-]+[A-Z0-9]+)?(\.\.+)?\]? ]]; do line=${line/"${BASH_REMATCH[0]}"/"${BASH_REMATCH[1]}"} done __parse_options "${line// or /, }" @@ -817,11 +817,11 @@ _parse_help() # _parse_usage() { - eval local cmd=$( quote "$1" ) + eval local cmd=$(quote "$1") local line match option i char { case $cmd in -) cat ;; - *) LC_ALL=C "$( dequote "$cmd" )" ${2:---usage} 2>&1 ;; + *) LC_ALL=C "$(dequote "$cmd")" ${2:---usage} 2>&1 ;; esac } \ | while read -r line; do @@ -850,7 +850,7 @@ _parse_usage() # @param $1 prefix _signals() { - local -a sigs=( $( compgen -P "$1" -A signal "SIG${cur#$1}" ) ) + local -a sigs=( $(compgen -P "$1" -A signal "SIG${cur#$1}") ) COMPREPLY+=( "${sigs[@]/#${1}SIG/${1}}" ) } @@ -865,7 +865,7 @@ _mac_addresses() # - ifconfig on Linux: HWaddr or ether # - ifconfig on FreeBSD: ether # - ip link: link/ether - COMPREPLY+=( $( \ + COMPREPLY+=( $(\ { LC_ALL=C ifconfig -a || ip link show; } 2>/dev/null | command sed -ne \ "s/.*[[:space:]]HWaddr[[:space:]]\{1,\}\($re\)[[:space:]].*/\1/p" -ne \ "s/.*[[:space:]]HWaddr[[:space:]]\{1,\}\($re\)[[:space:]]*$/\1/p" -ne \ @@ -874,15 +874,15 @@ _mac_addresses() ) ) # ARP cache - COMPREPLY+=( $( { arp -an || ip neigh show; } 2>/dev/null | command sed -ne \ + COMPREPLY+=( $({ arp -an || ip neigh show; } 2>/dev/null | command sed -ne \ "s/.*[[:space:]]\($re\)[[:space:]].*/\1/p" -ne \ - "s/.*[[:space:]]\($re\)[[:space:]]*$/\1/p" ) ) + "s/.*[[:space:]]\($re\)[[:space:]]*$/\1/p") ) # /etc/ethers - COMPREPLY+=( $( command sed -ne \ - "s/^[[:space:]]*\($re\)[[:space:]].*/\1/p" /etc/ethers 2>/dev/null ) ) + COMPREPLY+=( $(command sed -ne \ + "s/^[[:space:]]*\($re\)[[:space:]].*/\1/p" /etc/ethers 2>/dev/null) ) - COMPREPLY=( $( compgen -W '${COMPREPLY[@]}' -- "$cur" ) ) + COMPREPLY=( $(compgen -W '${COMPREPLY[@]}' -- "$cur") ) __ltrim_colon_completions "$cur" } @@ -892,44 +892,51 @@ _configured_interfaces() { if [[ -f /etc/debian_version ]]; then # Debian system - COMPREPLY=( $( compgen -W "$( command sed -ne 's|^iface \([^ ]\{1,\}\).*$|\1|p'\ - /etc/network/interfaces /etc/network/interfaces.d/* 2>/dev/null )" \ - -- "$cur" ) ) + COMPREPLY=( $(compgen -W "$(command sed -ne 's|^iface \([^ ]\{1,\}\).*$|\1|p'\ + /etc/network/interfaces /etc/network/interfaces.d/* 2>/dev/null)" \ + -- "$cur") ) elif [[ -f /etc/SuSE-release ]]; then # SuSE system - COMPREPLY=( $( compgen -W "$( printf '%s\n' \ + COMPREPLY=( $(compgen -W "$(printf '%s\n' \ /etc/sysconfig/network/ifcfg-* | \ - command sed -ne 's|.*ifcfg-\([^*].*\)$|\1|p' )" -- "$cur" ) ) + command sed -ne 's|.*ifcfg-\([^*].*\)$|\1|p')" -- "$cur") ) elif [[ -f /etc/pld-release ]]; then # PLD Linux - COMPREPLY=( $( compgen -W "$( command ls -B \ + COMPREPLY=( $(compgen -W "$(command ls -B \ /etc/sysconfig/interfaces | \ - command sed -ne 's|.*ifcfg-\([^*].*\)$|\1|p' )" -- "$cur" ) ) + command sed -ne 's|.*ifcfg-\([^*].*\)$|\1|p')" -- "$cur") ) else # Assume Red Hat - COMPREPLY=( $( compgen -W "$( printf '%s\n' \ + COMPREPLY=( $(compgen -W "$(printf '%s\n' \ /etc/sysconfig/network-scripts/ifcfg-* | \ - command sed -ne 's|.*ifcfg-\([^*].*\)$|\1|p' )" -- "$cur" ) ) + command sed -ne 's|.*ifcfg-\([^*].*\)$|\1|p')" -- "$cur") ) fi } # Local IP addresses. +# -4: IPv4 addresses only (default) +# -6: IPv6 addresses only +# -a: All addresses # _ip_addresses() { + local n + case $1 in + -a) n='6\?' ;; + -6) n='6' ;; + esac local PATH=$PATH:/sbin - COMPREPLY+=( $( compgen -W \ - "$( { LC_ALL=C ifconfig -a || ip addr show; } 2>/dev/null | command sed -ne \ - 's/.*addr:\([^[:space:]]*\).*/\1/p' -ne \ - 's|.*inet[[:space:]]\{1,\}\([^[:space:]/]*\).*|\1|p' )" \ - -- "$cur" ) ) + local addrs=$({ LC_ALL=C ifconfig -a || ip addr show; } 2>/dev/null | + command sed -e 's/[[:space:]]addr:/ /' -ne \ + "s|.*inet${n}[[:space:]]\{1,\}\([^[:space:]/]*\).*|\1|p") + COMPREPLY+=( $(compgen -W "$addrs" -- "$cur") ) } # This function completes on available kernels # _kernel_versions() { - COMPREPLY=( $( compgen -W '$( command ls /lib/modules )' -- "$cur" ) ) + COMPREPLY=( $(compgen -W '$(command ls /lib/modules)' -- "$cur") ) } # This function completes on all available network interfaces @@ -940,7 +947,7 @@ _available_interfaces() { local PATH=$PATH:/sbin - COMPREPLY=( $( { + COMPREPLY=( $({ if [[ ${1:-} == -w ]]; then iwconfig elif [[ ${1:-} == -a ]]; then @@ -949,9 +956,9 @@ _available_interfaces() ifconfig -a || ip link show fi } 2>/dev/null | awk \ - '/^[^ \t]/ { if ($1 ~ /^[0-9]+:/) { print $2 } else { print $1 } }' ) ) + '/^[^ \t]/ { if ($1 ~ /^[0-9]+:/) { print $2 } else { print $1 } }') ) - COMPREPLY=( $( compgen -W '${COMPREPLY[@]/%[[:punct:]]/}' -- "$cur" ) ) + COMPREPLY=( $(compgen -W '${COMPREPLY[@]/%[[:punct:]]/}' -- "$cur") ) } # Echo number of CPUs, falling back to 1 on failure. @@ -959,7 +966,7 @@ _ncpus() { local var=NPROCESSORS_ONLN [[ $OSTYPE == *linux* ]] && var=_$var - local n=$( getconf $var 2>/dev/null ) + local n=$(getconf $var 2>/dev/null) printf %s ${n:-1} } @@ -972,7 +979,7 @@ _tilde() local result=0 if [[ $1 == \~* && $1 != */* ]]; then # Try generate ~username completions - COMPREPLY=( $( compgen -P '~' -u -- "${1#\~}" ) ) + COMPREPLY=( $(compgen -P '~' -u -- "${1#\~}") ) result=${#COMPREPLY[@]} # 2>/dev/null for direct invocation, e.g. in the _tilde unit test [[ $result -gt 0 ]] && compopt -o filenames 2>/dev/null @@ -1032,11 +1039,11 @@ _expand() [[ $OSTYPE == *@(solaris|aix)* ]] && _pids() { - COMPREPLY=( $( compgen -W '$( command ps -efo pid | command sed 1d )' -- "$cur" )) + COMPREPLY=( $(compgen -W '$(command ps -efo pid | command sed 1d)' -- "$cur") ) } || _pids() { - COMPREPLY=( $( compgen -W '$( command ps axo pid= )' -- "$cur" ) ) + COMPREPLY=( $(compgen -W '$(command ps axo pid=)' -- "$cur") ) } # This function completes on process group IDs. @@ -1044,11 +1051,11 @@ _pids() [[ $OSTYPE == *@(solaris|aix)* ]] && _pgids() { - COMPREPLY=( $( compgen -W '$( command ps -efo pgid | command sed 1d )' -- "$cur" )) + COMPREPLY=( $(compgen -W '$(command ps -efo pgid | command sed 1d)' -- "$cur") ) } || _pgids() { - COMPREPLY=( $( compgen -W '$( command ps axo pgid= )' -- "$cur" )) + COMPREPLY=( $(compgen -W '$(command ps axo pgid=)' -- "$cur") ) } # This function completes on process names. @@ -1057,14 +1064,14 @@ _pgids() [[ $OSTYPE == *@(solaris|aix)* ]] && _pnames() { - COMPREPLY=( $( compgen -X '<defunct>' -W '$( command ps -efo comm | \ - command sed -e 1d -e "s:.*/::" -e "s/^-//" | sort -u )' -- "$cur" ) ) + COMPREPLY=( $(compgen -X '<defunct>' -W '$(command ps -efo comm | \ + command sed -e 1d -e "s:.*/::" -e "s/^-//" | sort -u)' -- "$cur") ) } || _pnames() { if [[ "$1" == -s ]]; then - COMPREPLY=( $( compgen -X '<defunct>' \ - -W '$( command ps axo comm | command sed -e 1d )' -- "$cur" ) ) + COMPREPLY=( $(compgen -X '<defunct>' \ + -W '$(command ps axo comm | command sed -e 1d)' -- "$cur") ) else # FIXME: completes "[kblockd/0]" to "0". Previously it was completed # to "kblockd" which isn't correct either. "kblockd/0" would be @@ -1072,12 +1079,12 @@ _pnames() # containing "/" specially unless -r is given so that wouldn't quite # work either. Perhaps it'd be best to not complete these to anything # for now. - COMPREPLY=( $( compgen -X '<defunct>' -W '$( command ps axo command= | command sed -e \ + COMPREPLY=( $(compgen -X '<defunct>' -W '$(command ps axo command= | command sed -e \ "s/ .*//" -e \ "s:.*/::" -e \ "s/:$//" -e \ "s/^[[(-]//" -e \ - "s/[])]$//" | sort -u )' -- "$cur" ) ) + "s/[])]$//" | sort -u)' -- "$cur") ) fi } @@ -1086,12 +1093,12 @@ _pnames() _uids() { if type getent &>/dev/null; then - COMPREPLY=( $( compgen -W '$( getent passwd | cut -d: -f3 )' -- "$cur" ) ) + COMPREPLY=( $(compgen -W '$(getent passwd | cut -d: -f3)' -- "$cur") ) elif type perl &>/dev/null; then - COMPREPLY=( $( compgen -W '$( perl -e '"'"'while (($uid) = (getpwent)[2]) { print $uid . "\n" }'"'"' )' -- "$cur" ) ) + COMPREPLY=( $(compgen -W '$(perl -e '"'"'while (($uid) = (getpwent)[2]) { print $uid . "\n" }'"'"')' -- "$cur") ) else # make do with /etc/passwd - COMPREPLY=( $( compgen -W '$( cut -d: -f3 /etc/passwd )' -- "$cur" ) ) + COMPREPLY=( $(compgen -W '$(cut -d: -f3 /etc/passwd)' -- "$cur") ) fi } @@ -1100,13 +1107,12 @@ _uids() _gids() { if type getent &>/dev/null; then - COMPREPLY=( $( compgen -W '$( getent group | cut -d: -f3 )' \ - -- "$cur" ) ) + COMPREPLY=( $(compgen -W '$(getent group | cut -d: -f3)' -- "$cur") ) elif type perl &>/dev/null; then - COMPREPLY=( $( compgen -W '$( perl -e '"'"'while (($gid) = (getgrent)[2]) { print $gid . "\n" }'"'"' )' -- "$cur" ) ) + COMPREPLY=( $(compgen -W '$(perl -e '"'"'while (($gid) = (getgrent)[2]) { print $gid . "\n" }'"'"')' -- "$cur") ) else # make do with /etc/group - COMPREPLY=( $( compgen -W '$( cut -d: -f3 /etc/group )' -- "$cur" ) ) + COMPREPLY=( $(compgen -W '$(cut -d: -f3 /etc/group)' -- "$cur") ) fi } @@ -1121,9 +1127,9 @@ _xinetd_services() local xinetddir=/etc/xinetd.d if [[ -d $xinetddir ]]; then local IFS=$' \t\n' reset=$(shopt -p nullglob); shopt -s nullglob - local -a svcs=( $( printf '%s\n' $xinetddir/!($_backup_glob) ) ) + local -a svcs=( $(printf '%s\n' $xinetddir/!($_backup_glob)) ) $reset - COMPREPLY+=( $( compgen -W '${svcs[@]#$xinetddir/}' -- "$cur" ) ) + COMPREPLY+=( $(compgen -W '${svcs[@]#$xinetddir/}' -- "$cur") ) fi } @@ -1136,17 +1142,18 @@ _services() local IFS=$' \t\n' reset=$(shopt -p nullglob); shopt -s nullglob COMPREPLY=( \ - $( printf '%s\n' ${sysvdirs[0]}/!($_backup_glob|functions|README) ) ) + $(printf '%s\n' ${sysvdirs[0]}/!($_backup_glob|functions|README)) ) $reset - COMPREPLY+=( $( systemctl list-units --full --all 2>/dev/null | \ - awk '$1 ~ /\.service$/ { sub("\\.service$", "", $1); print $1 }' ) ) + COMPREPLY+=( $({ systemctl list-units --full --all || \ + systemctl list-unit-files; } 2>/dev/null | \ + awk '$1 ~ /\.service$/ { sub("\\.service$", "", $1); print $1 }') ) if [[ -x /sbin/upstart-udev-bridge ]]; then - COMPREPLY+=( $( initctl list 2>/dev/null | cut -d' ' -f1 ) ) + COMPREPLY+=( $(initctl list 2>/dev/null | cut -d' ' -f1) ) fi - COMPREPLY=( $( compgen -W '${COMPREPLY[@]#${sysvdirs[0]}/}' -- "$cur" ) ) + COMPREPLY=( $(compgen -W '${COMPREPLY[@]#${sysvdirs[0]}/}' -- "$cur") ) } # This completes on a list of all available service scripts for the @@ -1167,14 +1174,14 @@ _service() else local sysvdirs _sysvdirs - COMPREPLY=( $( compgen -W '`command sed -e "y/|/ /" \ + COMPREPLY=( $(compgen -W '`command sed -e "y/|/ /" \ -ne "s/^.*\(U\|msg_u\)sage.*{\(.*\)}.*$/\2/p" \ - ${sysvdirs[0]}/${prev##*/} 2>/dev/null` start stop' -- "$cur" ) ) + ${sysvdirs[0]}/${prev##*/} 2>/dev/null` start stop' -- "$cur") ) fi } && complete -F _service service _sysvdirs -for svcdir in ${sysvdirs[@]}; do +for svcdir in "${sysvdirs[@]}"; do for svc in $svcdir/!($_backup_glob); do [[ -x $svc ]] && complete -F _service $svc done @@ -1187,16 +1194,16 @@ _modules() { local modpath modpath=/lib/modules/$1 - COMPREPLY=( $( compgen -W "$( command ls -RL $modpath 2>/dev/null | \ - command sed -ne 's/^\(.*\)\.k\{0,1\}o\(\.[gx]z\)\{0,1\}$/\1/p' )" -- "$cur" ) ) + COMPREPLY=( $(compgen -W "$(command ls -RL $modpath 2>/dev/null | \ + command sed -ne 's/^\(.*\)\.k\{0,1\}o\(\.[gx]z\)\{0,1\}$/\1/p')" -- "$cur") ) } # This function completes on installed modules # _installed_modules() { - COMPREPLY=( $( compgen -W "$( PATH="$PATH:/sbin" lsmod | \ - awk '{if (NR != 1) print $1}' )" -- "$1" ) ) + COMPREPLY=( $(compgen -W "$(PATH="$PATH:/sbin" lsmod | \ + awk '{if (NR != 1) print $1}')" -- "$1") ) } # This function completes on user or user:group format; as for chown and cpio. @@ -1223,9 +1230,9 @@ _usergroup() _allowed_groups "$mycur" else local IFS=$'\n' - COMPREPLY=( $( compgen -g -- "$mycur" ) ) + COMPREPLY=( $(compgen -g -- "$mycur") ) fi - COMPREPLY=( $( compgen -P "$prefix" -W "${COMPREPLY[@]}" ) ) + COMPREPLY=( $(compgen -P "$prefix" -W "${COMPREPLY[@]}") ) elif [[ $cur == *:* ]]; then # Completing group after 'user:gr<TAB>'. # Reply with a list of unprefixed groups since readline with split on : @@ -1235,7 +1242,7 @@ _usergroup() _allowed_groups "$mycur" else local IFS=$'\n' - COMPREPLY=( $( compgen -g -- "$mycur" ) ) + COMPREPLY=( $(compgen -g -- "$mycur") ) fi else # Completing a partial 'usernam<TAB>'. @@ -1247,7 +1254,7 @@ _usergroup() _allowed_users "$cur" else local IFS=$'\n' - COMPREPLY=( $( compgen -u -- "$cur" ) ) + COMPREPLY=( $(compgen -u -- "$cur") ) fi fi } @@ -1256,11 +1263,11 @@ _allowed_users() { if _complete_as_root; then local IFS=$'\n' - COMPREPLY=( $( compgen -u -- "${1:-$cur}" ) ) + COMPREPLY=( $(compgen -u -- "${1:-$cur}") ) else local IFS=$'\n ' - COMPREPLY=( $( compgen -W \ - "$( id -un 2>/dev/null || whoami 2>/dev/null )" -- "${1:-$cur}" ) ) + COMPREPLY=( $(compgen -W \ + "$(id -un 2>/dev/null || whoami 2>/dev/null)" -- "${1:-$cur}") ) fi } @@ -1268,11 +1275,11 @@ _allowed_groups() { if _complete_as_root; then local IFS=$'\n' - COMPREPLY=( $( compgen -g -- "$1" ) ) + COMPREPLY=( $(compgen -g -- "$1") ) else local IFS=$'\n ' - COMPREPLY=( $( compgen -W \ - "$( id -Gn 2>/dev/null || groups 2>/dev/null )" -- "$1" ) ) + COMPREPLY=( $(compgen -W \ + "$(id -Gn 2>/dev/null || groups 2>/dev/null)" -- "$1") ) fi } @@ -1283,7 +1290,7 @@ _shells() local shell rest while read -r shell rest; do [[ $shell == /* && $shell == "$cur"* ]] && COMPREPLY+=( $shell ) - done 2>/dev/null < /etc/shells + done 2>/dev/null </etc/shells } # This function completes on valid filesystem types @@ -1294,18 +1301,18 @@ _fstypes() if [[ -e /proc/filesystems ]]; then # Linux - fss="$( cut -d$'\t' -f2 /proc/filesystems ) - $( awk '! /\*/ { print $NF }' /etc/filesystems 2>/dev/null )" + fss="$(cut -d$'\t' -f2 /proc/filesystems) + $(awk '! /\*/ { print $NF }' /etc/filesystems 2>/dev/null)" else # Generic - fss="$( awk '/^[ \t]*[^#]/ { print $3 }' /etc/fstab 2>/dev/null ) - $( awk '/^[ \t]*[^#]/ { print $3 }' /etc/mnttab 2>/dev/null ) - $( awk '/^[ \t]*[^#]/ { print $4 }' /etc/vfstab 2>/dev/null ) - $( awk '{ print $1 }' /etc/dfs/fstypes 2>/dev/null ) - $( [[ -d /etc/fs ]] && command ls /etc/fs )" + fss="$(awk '/^[ \t]*[^#]/ { print $3 }' /etc/fstab 2>/dev/null) + $(awk '/^[ \t]*[^#]/ { print $3 }' /etc/mnttab 2>/dev/null) + $(awk '/^[ \t]*[^#]/ { print $4 }' /etc/vfstab 2>/dev/null) + $(awk '{ print $1 }' /etc/dfs/fstypes 2>/dev/null) + $([[ -d /etc/fs ]] && command ls /etc/fs)" fi - [[ -n $fss ]] && COMPREPLY+=( $( compgen -W "$fss" -- "$cur" ) ) + [[ -n $fss ]] && COMPREPLY+=( $(compgen -W "$fss" -- "$cur") ) } # Get real command. @@ -1315,12 +1322,12 @@ _fstypes() # - return: True (0) if command found, False (> 0) if not. _realcommand() { - type -P "$1" > /dev/null && { - if type -p realpath > /dev/null; then + type -P "$1" >/dev/null && { + if type -p realpath >/dev/null; then realpath "$(type -P "$1")" - elif type -p greadlink > /dev/null; then + elif type -p greadlink >/dev/null; then greadlink -f "$(type -P "$1")" - elif type -p readlink > /dev/null; then + elif type -p readlink >/dev/null; then readlink -f "$(type -P "$1")" else type -P "$1" @@ -1348,14 +1355,19 @@ _get_first_arg() # This function counts the number of args, excluding options # @param $1 chars Characters out of $COMP_WORDBREAKS which should # NOT be considered word breaks. See __reassemble_comp_words_by_ref. +# @param $2 glob Options whose following argument should not be counted +# @param $3 glob Options that should be counted as args _count_args() { local i cword words __reassemble_comp_words_by_ref "$1" words cword args=1 - for i in "${words[@]:1:cword-1}"; do - [[ "$i" != -* ]] && args=$(($args+1)) + for (( i=1; i < cword; i++ )); do + if [[ ${words[i]} != -* && ${words[i-1]} != $2 || + ${words[i]} == $3 ]]; then + (( args++ )) + fi done } @@ -1363,38 +1375,38 @@ _count_args() # _pci_ids() { - COMPREPLY+=( $( compgen -W \ - "$( PATH="$PATH:/sbin" lspci -n | awk '{print $3}')" -- "$cur" ) ) + COMPREPLY+=( $(compgen -W \ + "$(PATH="$PATH:/sbin" lspci -n | awk '{print $3}')" -- "$cur") ) } # This function completes on USB IDs # _usb_ids() { - COMPREPLY+=( $( compgen -W \ - "$( PATH="$PATH:/sbin" lsusb | awk '{print $6}' )" -- "$cur" ) ) + COMPREPLY+=( $(compgen -W \ + "$(PATH="$PATH:/sbin" lsusb | awk '{print $6}')" -- "$cur") ) } # CD device names _cd_devices() { - COMPREPLY+=( $( compgen -f -d -X "!*/?([amrs])cd*" -- "${cur:-/dev/}" ) ) + COMPREPLY+=( $(compgen -f -d -X "!*/?([amrs])cd*" -- "${cur:-/dev/}") ) } # DVD device names _dvd_devices() { - COMPREPLY+=( $( compgen -f -d -X "!*/?(r)dvd*" -- "${cur:-/dev/}" ) ) + COMPREPLY+=( $(compgen -f -d -X "!*/?(r)dvd*" -- "${cur:-/dev/}") ) } # TERM environment variable values _terms() { - COMPREPLY+=( $( compgen -W \ - "$( command sed -ne 's/^\([^[:space:]#|]\{2,\}\)|.*/\1/p' /etc/termcap \ - 2>/dev/null )" -- "$cur" ) ) - COMPREPLY+=( $( compgen -W "$( { toe -a 2>/dev/null || toe 2>/dev/null; } \ - | awk '{ print $1 }' | sort -u )" -- "$cur" ) ) + COMPREPLY+=( $(compgen -W \ + "$(command sed -ne 's/^\([^[:space:]#|]\{2,\}\)|.*/\1/p' /etc/termcap \ + 2>/dev/null)" -- "$cur") ) + COMPREPLY+=( $(compgen -W "$({ toe -a 2>/dev/null || toe 2>/dev/null; } \ + | awk '{ print $1 }' | sort -u)" -- "$cur") ) } # a little help for FreeBSD ports users @@ -1412,7 +1424,7 @@ _user_at_host() if [[ $cur == *@* ]]; then _known_hosts_real "$cur" else - COMPREPLY=( $( compgen -u -S @ -- "$cur" ) ) + COMPREPLY=( $(compgen -u -S @ -- "$cur") ) compopt -o nospace fi } @@ -1434,15 +1446,16 @@ _known_hosts() } # _known_hosts() # Helper function to locate ssh included files in configs -# This function look for the "Include" keyword in ssh config files and include -# them recursively adding each result to the config variable +# This function looks for the "Include" keyword in ssh config files and +# includes them recursively, adding each result to the config variable. _included_ssh_config_files() { - [[ $# -lt 1 ]] && echo "error: $FUNCNAME: missing mandatory argument CONFIG" + [[ $# -lt 1 ]] && \ + echo "bash_completion: $FUNCNAME: missing mandatory argument CONFIG" >&2 local configfile i f configfile=$1 - local included=$( command sed -ne 's/^[[:blank:]]*[Ii][Nn][Cc][Ll][Uu][Dd][Ee][[:blank:]]\{1,\}\([^#%]*\)\(#.*\)\{0,1\}$/\1/p' "${configfile}" ) - for i in ${included[@]}; do + local included=( $(command sed -ne 's/^[[:blank:]]*[Ii][Nn][Cc][Ll][Uu][Dd][Ee][[:blank:]]\{1,\}\([^#%]*\)\(#.*\)\{0,1\}$/\1/p' "${configfile}") ) + for i in "${included[@]}"; do # Check the origin of $configfile to complete relative included paths on included # files according to ssh_config(5): # "[...] Files without absolute paths are assumed to be in ~/.ssh if included in a user @@ -1482,9 +1495,9 @@ _included_ssh_config_files() # Return: Completions, starting with CWORD, are added to COMPREPLY[] _known_hosts_real() { - local configfile flag prefix - local cur curd awkcur user suffix aliases i host ipv4 ipv6 - local -a kh khd config + local configfile flag prefix OIFS=$IFS + local cur user suffix aliases i host ipv4 ipv6 + local -a kh tmpkh khd config # TODO remove trailing %foo from entries @@ -1499,10 +1512,12 @@ _known_hosts_real() 6) ipv6=1 ;; esac done - [[ $# -lt $OPTIND ]] && echo "error: $FUNCNAME: missing mandatory argument CWORD" - cur=${!OPTIND}; let "OPTIND += 1" - [[ $# -ge $OPTIND ]] && echo "error: $FUNCNAME("$@"): unprocessed arguments:"\ - $(while [[ $# -ge $OPTIND ]]; do printf '%s\n' ${!OPTIND}; shift; done) + [[ $# -lt $OPTIND ]] && \ + echo "bash_completion: $FUNCNAME: missing mandatory argument CWORD" >&2 + cur=${!OPTIND}; (( OPTIND += 1 )) + [[ $# -ge $OPTIND ]] && \ + echo "bash_completion: $FUNCNAME($*): unprocessed arguments:" \ + $(while [[ $# -ge $OPTIND ]]; do printf '%s\n' ${!OPTIND}; shift; done) >&2 [[ $cur == *@* ]] && user=${cur%@*}@ && cur=${cur#*@} kh=() @@ -1523,13 +1538,12 @@ _known_hosts_real() # Known hosts files from configs if [[ ${#config[@]} -gt 0 ]]; then - local OIFS=$IFS IFS=$'\n' j - local -a tmpkh + local IFS=$'\n' j # expand paths (if present) to global and user known hosts files # TODO(?): try to make known hosts files with more than one consecutive # spaces in their name work (watch out for ~ expansion # breakage! Alioth#311595) - tmpkh=( $( awk 'sub("^[ \t]*([Gg][Ll][Oo][Bb][Aa][Ll]|[Uu][Ss][Ee][Rr])[Kk][Nn][Oo][Ww][Nn][Hh][Oo][Ss][Tt][Ss][Ff][Ii][Ll][Ee][ \t]+", "") { print $0 }' "${config[@]}" | sort -u ) ) + tmpkh=( $(awk 'sub("^[ \t]*([Gg][Ll][Oo][Bb][Aa][Ll]|[Uu][Ss][Ee][Rr])[Kk][Nn][Oo][Ww][Nn][Hh][Oo][Ss][Tt][Ss][Ff][Ii][Ll][Ee][ \t]+", "") { print $0 }' "${config[@]}" | sort -u) ) IFS=$OIFS for i in "${tmpkh[@]}"; do # First deal with quoted entries... @@ -1561,35 +1575,31 @@ _known_hosts_real() # If we have known_hosts files to use if [[ ${#kh[@]} -gt 0 || ${#khd[@]} -gt 0 ]]; then - # Escape slashes and dots in paths for awk - awkcur=${cur//\//\\\/} - awkcur=${awkcur//\./\\\.} - curd=$awkcur - - if [[ "$awkcur" == [0-9]*[.:]* ]]; then - # Digits followed by a dot or a colon - just search for that - awkcur="^$awkcur[.:]*" - elif [[ "$awkcur" == [0-9]* ]]; then - # Digits followed by no dot or colon - search for digits followed - # by a dot or a colon - awkcur="^$awkcur.*[.:]" - elif [[ -z $awkcur ]]; then - # A blank - search for a dot, a colon, or an alpha character - awkcur="[a-z.:]" - else - awkcur="^$awkcur" - fi - if [[ ${#kh[@]} -gt 0 ]]; then - # FS needs to look for a comma separated list - COMPREPLY+=( $( awk 'BEGIN {FS=","} - /^\s*[^|\#]/ { - sub("^@[^ ]+ +", ""); \ - sub(" .*$", ""); \ - for (i=1; i<=NF; ++i) { \ - sub("^\\[", "", $i); sub("\\](:[0-9]+)?$", "", $i); \ - if ($i !~ /[*?]/ && $i ~ /'"$awkcur"'/) {print $i} \ - }}' "${kh[@]}" 2>/dev/null ) ) + # https://man.openbsd.org/sshd.8#SSH_KNOWN_HOSTS_FILE_FORMAT + for i in "${kh[@]}"; do + while read -ra tmpkh; do + set -- "${tmpkh[@]}" + # Skip entries starting with | (hashed) and # (comment) + [[ $1 == [\|\#]* ]] && continue + # Ignore leading @foo (markers) + [[ $1 == @* ]] && shift + # Split entry on commas + local IFS=, + for host in $1; do + # Skip hosts containing wildcards + [[ $host == *[*?]* ]] && continue + # Remove leading [ + host="${host#[}" + # Remove trailing ] + optional :port + host="${host%]?(:+([0-9]))}" + # Add host to candidates + COMPREPLY+=( $host ) + done + IFS=$OIFS + done <"$i" + done + COMPREPLY=( $(compgen -W '${COMPREPLY[@]}' -- "$cur") ) fi if [[ ${#khd[@]} -gt 0 ]]; then # Needs to look for files called @@ -1597,7 +1607,7 @@ _known_hosts_real() # dont fork any processes, because in a cluster environment, # there can be hundreds of hostkeys for i in "${khd[@]}" ; do - if [[ "$i" == *key_22_$curd*.pub && -r "$i" ]]; then + if [[ "$i" == *key_22_$cur*.pub && -r "$i" ]]; then host=${i/#*key_22_/} host=${host/%.pub/} COMPREPLY+=( $host ) @@ -1613,9 +1623,9 @@ _known_hosts_real() # append any available aliases from ssh config files if [[ ${#config[@]} -gt 0 && -n "$aliases" ]]; then - local hosts=$( command sed -ne 's/^[[:blank:]]*[Hh][Oo][Ss][Tt][[:blank:]]\{1,\}\([^#*?%]*\)\(#.*\)\{0,1\}$/\1/p' "${config[@]}" ) - COMPREPLY+=( $( compgen -P "$prefix$user" \ - -S "$suffix" -W "$hosts" -- "$cur" ) ) + local hosts=$(command sed -ne 's/^[[:blank:]]*[Hh][Oo][Ss][Tt][[:blank:]]\{1,\}\([^#*?%]*\)\(#.*\)\{0,1\}$/\1/p' "${config[@]}") + COMPREPLY+=( $(compgen -P "$prefix$user" \ + -S "$suffix" -W "$hosts" -- "$cur") ) fi # Add hosts reported by avahi-browse, if desired and it's available. @@ -1626,21 +1636,21 @@ _known_hosts_real() # if it contains ";", it may mistify the result. But on Gentoo (at # least), -k wasn't available (even if mentioned in the manpage) some # time ago, so... - COMPREPLY+=( $( compgen -P "$prefix$user" -S "$suffix" -W \ - "$( avahi-browse -cpr _workstation._tcp 2>/dev/null | \ - awk -F';' '/^=/ { print $7 }' | sort -u )" -- "$cur" ) ) + COMPREPLY+=( $(compgen -P "$prefix$user" -S "$suffix" -W \ + "$(avahi-browse -cpr _workstation._tcp 2>/dev/null | \ + awk -F';' '/^=/ { print $7 }' | sort -u)" -- "$cur") ) fi # Add hosts reported by ruptime. - COMPREPLY+=( $( compgen -W \ - "$( ruptime 2>/dev/null | awk '!/^ruptime:/ { print $1 }' )" \ - -- "$cur" ) ) + COMPREPLY+=( $(compgen -W \ + "$(ruptime 2>/dev/null | awk '!/^ruptime:/ { print $1 }')" \ + -- "$cur") ) # Add results of normal hostname completion, unless # `COMP_KNOWN_HOSTS_WITH_HOSTFILE' is set to an empty value. if [[ -n ${COMP_KNOWN_HOSTS_WITH_HOSTFILE-1} ]]; then COMPREPLY+=( - $( compgen -A hostname -P "$prefix$user" -S "$suffix" -- "$cur" ) ) + $(compgen -A hostname -P "$prefix$user" -S "$suffix" -- "$cur") ) fi if [[ $ipv4 ]]; then @@ -1650,7 +1660,7 @@ _known_hosts_real() COMPREPLY=( "${COMPREPLY[@]/+([0-9]).+([0-9]).+([0-9]).+([0-9])$suffix/}" ) fi if [[ $ipv4 || $ipv6 ]]; then - for i in ${!COMPREPLY[@]}; do + for i in "${!COMPREPLY[@]}"; do [[ ${COMPREPLY[i]} ]] || unset -v COMPREPLY[i] done fi @@ -1658,7 +1668,7 @@ _known_hosts_real() __ltrim_colon_completions "$prefix$user$cur" } # _known_hosts_real() -complete -F _known_hosts traceroute traceroute6 tracepath tracepath6 \ +complete -F _known_hosts traceroute traceroute6 \ fping fping6 telnet rsh rlogin ftp dig mtr ssh-installkeys showmount # This meta-cd function observes the CDPATH variable, so that cd additionally @@ -1687,7 +1697,7 @@ _cd() for i in ${CDPATH//:/$'\n'}; do # create an array of matched subdirs k="${#COMPREPLY[@]}" - for j in $( compgen -d -- $i/$cur ); do + for j in $(compgen -d -- $i/$cur); do if [[ ( $mark_symdirs && -h $j || $mark_dirs && ! -h $j ) && ! -d ${j#$i/} ]]; then j+="/" fi @@ -1766,21 +1776,21 @@ _command_offset() if [[ $COMP_CWORD -eq 0 ]]; then local IFS=$'\n' compopt -o filenames - COMPREPLY=( $( compgen -d -c -- "$cur" ) ) + COMPREPLY=( $(compgen -d -c -- "$cur") ) else local cmd=${COMP_WORDS[0]} compcmd=${COMP_WORDS[0]} - local cspec=$( complete -p $cmd 2>/dev/null ) + local cspec=$(complete -p $cmd 2>/dev/null) # If we have no completion for $cmd yet, see if we have for basename if [[ ! $cspec && $cmd == */* ]]; then - cspec=$( complete -p ${cmd##*/} 2>/dev/null ) + cspec=$(complete -p ${cmd##*/} 2>/dev/null) [[ $cspec ]] && compcmd=${cmd##*/} fi # If still nothing, just load it for the basename if [[ ! $cspec ]]; then compcmd=${cmd##*/} _completion_loader $compcmd - cspec=$( complete -p $compcmd 2>/dev/null ) + cspec=$(complete -p $compcmd 2>/dev/null) fi if [[ -n $cspec ]]; then @@ -1809,7 +1819,7 @@ _command_offset() else cspec=${cspec#complete} cspec=${cspec%%$compcmd} - COMPREPLY=( $( eval compgen "$cspec" -- '$cur' ) ) + COMPREPLY=( $(eval compgen "$cspec" -- '$cur') ) fi elif [[ ${#COMPREPLY[@]} -eq 0 ]]; then # XXX will probably never happen as long as completion loader loads @@ -1818,8 +1828,8 @@ _command_offset() fi fi } -complete -F _command aoss command do else eval exec ltrace nice nohup padsp \ - then time tsocks vsound xargs +complete -F _command aoss command "do" else eval exec ltrace nice nohup padsp \ + "then" time tsocks vsound xargs _root_command() { @@ -1844,17 +1854,17 @@ _longopt() --help|--usage|--version) return ;; - --*dir*) + --!(no-*)dir*) _filedir -d return ;; - --*file*|--*path*) + --!(no-*)@(file|path)*) _filedir return ;; --+([-a-z0-9_])) - local argtype=$( LC_ALL=C $1 --help 2>&1 | command sed -ne \ - "s|.*$prev\[\{0,1\}=[<[]\{0,1\}\([-A-Za-z0-9_]\{1,\}\).*|\1|p" ) + local argtype=$(LC_ALL=C $1 --help 2>&1 | command sed -ne \ + "s|.*$prev\[\{0,1\}=[<[]\{0,1\}\([-A-Za-z0-9_]\{1,\}\).*|\1|p") case ${argtype,,} in *dir*) _filedir -d @@ -1871,14 +1881,16 @@ _longopt() $split && return if [[ "$cur" == -* ]]; then - COMPREPLY=( $( compgen -W "$( LC_ALL=C $1 --help 2>&1 | \ - command sed -ne 's/.*\(--[-A-Za-z0-9]\{1,\}=\{0,1\}\).*/\1/p' | sort -u )" \ - -- "$cur" ) ) + COMPREPLY=( $(compgen -W "$(LC_ALL=C $1 --help 2>&1 | \ + while read -r line; do \ + [[ $line =~ --[-A-Za-z0-9]+=? ]] && \ + printf '%s\n' ${BASH_REMATCH[0]} + done)" -- "$cur") ) [[ $COMPREPLY == *= ]] && compopt -o nospace - elif [[ "$1" == @(rmdir|chroot) ]]; then + elif [[ "$1" == *@(rmdir|chroot) ]]; then _filedir -d else - [[ "$1" == mkdir ]] && compopt -o nospace + [[ "$1" == *mkdir ]] && compopt -o nospace _filedir fi } @@ -1890,7 +1902,12 @@ complete -F _longopt a2ps awk base64 bash bc bison cat chroot colordiff cp \ sed seq sha{,1,224,256,384,512}sum shar sort split strip sum tac tail tee \ texindex touch tr uname unexpand uniq units vdir wc who -declare -A _xspecs +# declare only knows -g in bash >= 4.2. +if [[ ${BASH_VERSINFO[0]} -gt 4 || ${BASH_VERSINFO[1]} -ge 2 ]]; then + declare -Ag _xspecs +else + declare -A _xspecs +fi _filedir_xspec() { local cur prev words cword @@ -1927,6 +1944,13 @@ _filedir_xspec() } )) + # Try without filter if it failed to produce anything and configured to + [[ -n ${COMP_FILEDIR_FALLBACK:-} && ${#toks[@]} -lt 1 ]] && { + local reset=$(shopt -po noglob); set -o noglob + toks+=( $(compgen -f -- "$(quote_readline "$cur")") ) + IFS=' '; $reset; IFS=$'\n' + } + if [[ ${#toks[@]} -ne 0 ]]; then compopt -o filenames COMPREPLY=( "${toks[@]}" ) @@ -1937,13 +1961,13 @@ _install_xspec() { local xspec=$1 cmd shift - for cmd in $@; do + for cmd in "$@"; do _xspecs[$cmd]=$xspec done } # bzcmp, bzdiff, bz*grep, bzless, bzmore intentionally not here, see Debian: #455510 _install_xspec '!*.?(t)bz?(2)' bunzip2 bzcat pbunzip2 pbzcat lbunzip2 lbzcat -_install_xspec '!*.@(zip|[egjsw]ar|exe|pk3|wsz|zargo|xpi|s[tx][cdiw]|sx[gm]|o[dt][tspgfc]|od[bm]|oxt|epub|apk|ipa|do[ct][xm]|p[op]t[mx]|xl[st][xm]|pyz)' unzip zipinfo +_install_xspec '!*.@(zip|[egjswx]ar|exe|pk3|wsz|zargo|xpi|s[tx][cdiw]|sx[gm]|o[dt][tspgfc]|od[bm]|oxt|epub|apk|ipa|do[ct][xm]|p[op]t[mx]|xl[st][xm]|pyz|whl)' unzip zipinfo _install_xspec '*.Z' compress znew # zcmp, zdiff, z*grep, zless, zmore intentionally not here, see Debian: #455510 _install_xspec '!*.@(Z|[gGd]z|t[ag]z)' gunzip zcat @@ -1961,15 +1985,15 @@ _install_xspec '!*.@(dvi|DVI)?(.@(gz|Z|bz2))' xdvi kdvi _install_xspec '!*.dvi' dvips dviselect dvitype dvipdf advi dvipdfm dvipdfmx _install_xspec '!*.[pf]df' acroread gpdf xpdf _install_xspec '!*.@(?(e)ps|pdf)' kpdf -_install_xspec '!*.@(okular|@(?(e|x)ps|?(E|X)PS|[pf]df|[PF]DF|dvi|DVI|cb[rz]|CB[RZ]|djv?(u)|DJV?(U)|dvi|DVI|gif|jp?(e)g|miff|tif?(f)|pn[gm]|p[bgp]m|bmp|xpm|ico|xwd|tga|pcx|GIF|JP?(E)G|MIFF|TIF?(F)|PN[GM]|P[BGP]M|BMP|XPM|ICO|XWD|TGA|PCX|epub|EPUB|odt|ODT|fb?(2)|FB?(2)|mobi|MOBI|g3|G3|chm|CHM)?(.?(gz|GZ|bz2|BZ2)))' okular +_install_xspec '!*.@(okular|@(?(e|x)ps|?(E|X)PS|[pf]df|[PF]DF|dvi|DVI|cb[rz]|CB[RZ]|djv?(u)|DJV?(U)|dvi|DVI|gif|jp?(e)g|miff|tif?(f)|pn[gm]|p[bgp]m|bmp|xpm|ico|xwd|tga|pcx|GIF|JP?(E)G|MIFF|TIF?(F)|PN[GM]|P[BGP]M|BMP|XPM|ICO|XWD|TGA|PCX|epub|EPUB|odt|ODT|fb?(2)|FB?(2)|mobi|MOBI|g3|G3|chm|CHM)?(.?(gz|GZ|bz2|BZ2|xz|XZ)))' okular _install_xspec '!*.pdf' epdfview pdfunite _install_xspec '!*.@(cb[rz7t]|djv?(u)|?(e)ps|pdf)' zathura _install_xspec '!*.@(?(e)ps|pdf)' ps2pdf ps2pdf12 ps2pdf13 ps2pdf14 ps2pdfwr _install_xspec '!*.texi*' makeinfo texi2html _install_xspec '!*.@(?(la)tex|texi|dtx|ins|ltx|dbj)' tex latex slitex jadetex pdfjadetex pdftex pdflatex texi2dvi xetex xelatex luatex lualatex _install_xspec '!*.mp3' mpg123 mpg321 madplay -_install_xspec '!*@(.@(mp?(e)g|MP?(E)G|wm[av]|WM[AV]|avi|AVI|asf|vob|VOB|bin|dat|divx|DIVX|vcd|ps|pes|fli|flv|FLV|fxm|FXM|viv|rm|ram|yuv|mov|MOV|qt|QT|web[am]|WEB[AM]|mp[234]|MP[234]|m?(p)4[av]|M?(P)4[AV]|mkv|MKV|og[agmv]|OG[AGMV]|t[ps]|T[PS]|m2t?(s)|M2T?(S)|mts|MTS|wav|WAV|flac|FLAC|asx|ASX|mng|MNG|srt|m[eo]d|M[EO]D|s[3t]m|S[3T]M|it|IT|xm|XM)|+([0-9]).@(vdr|VDR))?(.part)' xine aaxine fbxine -_install_xspec '!*@(.@(mp?(e)g|MP?(E)G|wm[av]|WM[AV]|avi|AVI|asf|vob|VOB|bin|dat|divx|DIVX|vcd|ps|pes|fli|flv|FLV|fxm|FXM|viv|rm|ram|yuv|mov|MOV|qt|QT|web[am]|WEB[AM]|mp[234]|MP[234]|m?(p)4[av]|M?(P)4[AV]|mkv|MKV|og[agmv]|OG[AGMV]|t[ps]|T[PS]|m2t?(s)|M2T?(S)|mts|MTS|wav|WAV|flac|FLAC|asx|ASX|mng|MNG|srt|m[eo]d|M[EO]D|s[3t]m|S[3T]M|it|IT|xm|XM|iso|ISO)|+([0-9]).@(vdr|VDR))?(.part)' kaffeine dragon +_install_xspec '!*@(.@(mp?(e)g|MP?(E)G|wm[av]|WM[AV]|avi|AVI|asf|vob|VOB|bin|dat|divx|DIVX|vcd|ps|pes|fli|flv|FLV|fxm|FXM|viv|rm|ram|yuv|mov|MOV|qt|QT|web[am]|WEB[AM]|mp[234]|MP[234]|m?(p)4[av]|M?(P)4[AV]|mkv|MKV|og[agmv]|OG[AGMV]|t[ps]|T[PS]|m2t?(s)|M2T?(S)|mts|MTS|wav|WAV|flac|FLAC|asx|ASX|mng|MNG|srt|m[eo]d|M[EO]D|s[3t]m|S[3T]M|it|IT|xm|XM)|+([0-9]).@(vdr|VDR))?(.@(crdownload|part))' xine aaxine fbxine +_install_xspec '!*@(.@(mp?(e)g|MP?(E)G|wm[av]|WM[AV]|avi|AVI|asf|vob|VOB|bin|dat|divx|DIVX|vcd|ps|pes|fli|flv|FLV|fxm|FXM|viv|rm|ram|yuv|mov|MOV|qt|QT|web[am]|WEB[AM]|mp[234]|MP[234]|m?(p)4[av]|M?(P)4[AV]|mkv|MKV|og[agmv]|OG[AGMV]|t[ps]|T[PS]|m2t?(s)|M2T?(S)|mts|MTS|wav|WAV|flac|FLAC|asx|ASX|mng|MNG|srt|m[eo]d|M[EO]D|s[3t]m|S[3T]M|it|IT|xm|XM|iso|ISO)|+([0-9]).@(vdr|VDR))?(.@(crdownload|part))' kaffeine dragon _install_xspec '!*.@(avi|asf|wmv)' aviplay _install_xspec '!*.@(rm?(j)|ra?(m)|smi?(l))' realplay _install_xspec '!*.@(mpg|mpeg|avi|mov|qt)' xanim @@ -1983,7 +2007,6 @@ _install_xspec '*.@([ao]|so|so.!(conf|*/*)|[rs]pm|gif|jp?(e)g|mp3|mp?(e)g|avi|as _install_xspec '!*.@(zip|z|gz|tgz)' bzme # konqueror not here on purpose, it's more than a web/html browser _install_xspec '!*.@(?([xX]|[sS])[hH][tT][mM]?([lL]))' netscape mozilla lynx galeon dillo elinks amaya epiphany -_install_xspec '!*.@(?([xX]|[sS])[hH][tT][mM]?([lL])|[pP][dD][fF])' firefox mozilla-firefox iceweasel google-chrome chromium-browser _install_xspec '!*.@(sxw|stw|sxg|sgl|doc?([mx])|dot?([mx])|rtf|txt|htm|html|?(f)odt|ott|odm|pdf)' oowriter lowriter _install_xspec '!*.@(sxi|sti|pps?(x)|ppt?([mx])|pot?([mx])|?(f)odp|otp)' ooimpress loimpress _install_xspec '!*.@(sxc|stc|xls?([bmx])|xlw|xlt?([mx])|[ct]sv|?(f)ods|ots)' oocalc localc @@ -2018,6 +2041,7 @@ __load_completion() { local -a dirs=( ${BASH_COMPLETION_USER_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion}/completions ) local OIFS=$IFS IFS=: dir cmd="${1##*/}" compfile + [[ -n $cmd ]] || return 1 for dir in ${XDG_DATA_DIRS:-/usr/local/share:/usr/share}; do dirs+=( $dir/bash-completion/completions ) done @@ -2030,6 +2054,7 @@ __load_completion() fi for dir in "${dirs[@]}"; do + [[ -d "$dir" ]] || continue for compfile in "$cmd" "$cmd.bash" "_$cmd"; do compfile="$dir/$compfile" # Avoid trying to source dirs; https://bugzilla.redhat.com/903540 |