diff options
author | Gabriel F. T. Gomes <gabriel@inconstante.eti.br> | 2017-09-25 23:46:54 -0300 |
---|---|---|
committer | Gabriel F. T. Gomes <gabriel@inconstante.eti.br> | 2017-09-25 23:46:54 -0300 |
commit | 6d88f1055806932d9291f96847d2b691cccda2cd (patch) | |
tree | 0ff79eedaa8a239331256048981deedbd0721965 /bash_completion | |
parent | 059a87a5936cfebfd2d71ab8057002cafb2ea051 (diff) | |
download | bash-completion-6d88f1055806932d9291f96847d2b691cccda2cd.tar.gz |
New upstream version 2.7upstream/2.7
Diffstat (limited to 'bash_completion')
-rw-r--r-- | bash_completion | 421 |
1 files changed, 268 insertions, 153 deletions
diff --git a/bash_completion b/bash_completion index 6d3ba762..d16b10a5 100644 --- a/bash_completion +++ b/bash_completion @@ -3,8 +3,7 @@ # bash_completion - programmable completion functions for bash 4.1+ # # Copyright © 2006-2008, Ian Macdonald <ian@caliban.org> -# © 2009-2013, Bash Completion Maintainers -# <bash-completion-devel@lists.alioth.debian.org> +# © 2009-2017, 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 @@ -22,9 +21,9 @@ # # The latest version of this software can be obtained here: # -# http://bash-completion.alioth.debian.org/ -# -# RELEASE: 2.1 +# https://github.com/scop/bash-completion + +BASH_COMPLETION_VERSINFO=(2 7) if [[ $- == *v* ]]; then BASH_COMPLETION_ORIGINAL_V_VALUE="-v" @@ -38,11 +37,6 @@ else set +v fi -# Set the following to the location of the backwards compat completion dir. -# -: ${BASH_COMPLETION_COMPAT_DIR:=/etc/bash_completion.d} -readonly BASH_COMPLETION_COMPAT_DIR - # Blacklisted completions, causing problems with our code. # _blacklist_glob='@(acroread.sh)' @@ -53,13 +47,10 @@ shopt -s extglob progcomp # A lot of the following one-liners were taken directly from the # completion examples provided with the bash 2.04 source distribution -# Make directory commands see only directories -complete -d pushd - # start of section containing compspecs that can be handled within bash # user commands see only users -complete -u write chfn groups slay w sux runuser +complete -u groups slay w sux # bg completes with stopped jobs complete -A stopped -P '"%' -S '"' bg @@ -82,9 +73,6 @@ complete -A helptopic help # unalias completes with aliases complete -a unalias -# bind completes with readline bindings (make this more intelligent) -complete -A binding bind - # type and which complete on commands complete -c command type which @@ -136,7 +124,7 @@ have() # _rl_enabled() { - [[ "$( bind -v )" = *$1+([[:space:]])on* ]] + [[ "$( bind -v )" == *$1+([[:space:]])on* ]] } # This function shell-quotes the argument @@ -246,7 +234,7 @@ __reassemble_comp_words_by_ref() fi # Default to cword unchanged - eval $3=$COMP_CWORD + printf -v "$3" %s "$COMP_CWORD" # Are characters excluded which were former included? if [[ $exclude ]]; then # Yes, list of word completion separators has shrunk; @@ -261,33 +249,35 @@ __reassemble_comp_words_by_ref() # Is word separator not preceded by whitespace in original line # and are we not going to append to word 0 (the command # itself), then append to current word. - [[ $line != [$' \t']* ]] && (( j >= 2 )) && ((j--)) + [[ $line != [[:blank:]]* ]] && (( j >= 2 )) && ((j--)) # Append word separator to current or new word ref="$2[$j]" - eval $2[$j]=\${!ref}\${COMP_WORDS[i]} + printf -v "$ref" %s "${!ref}${COMP_WORDS[i]}" # Indicate new cword - [[ $i == $COMP_CWORD ]] && eval $3=$j + [[ $i == $COMP_CWORD ]] && printf -v "$3" %s "$j" # Remove optional whitespace + word separator from line copy line=${line#*"${COMP_WORDS[$i]}"} # Start new word if word separator in original line is # followed by whitespace. - [[ $line == [$' \t']* ]] && ((j++)) + [[ $line == [[:blank:]]* ]] && ((j++)) # Indicate next word if available, else end *both* while and # for loop (( $i < ${#COMP_WORDS[@]} - 1)) && ((i++)) || break 2 done # Append word to current word ref="$2[$j]" - eval $2[$j]=\${!ref}\${COMP_WORDS[i]} + printf -v "$ref" %s "${!ref}${COMP_WORDS[i]}" # Remove optional whitespace + word from line copy line=${line#*"${COMP_WORDS[i]}"} # Indicate new cword - [[ $i == $COMP_CWORD ]] && eval $3=$j + [[ $i == $COMP_CWORD ]] && printf -v "$3" %s "$j" done - [[ $i == $COMP_CWORD ]] && eval $3=$j + [[ $i == $COMP_CWORD ]] && printf -v "$3" %s "$j" else # No, list of word completions separators hasn't changed; - eval $2=\( \"\${COMP_WORDS[@]}\" \) + for i in ${!COMP_WORDS[@]}; do + printf -v "$2[i]" %s "${COMP_WORDS[i]}" + done fi } # __reassemble_comp_words_by_ref() @@ -318,8 +308,8 @@ __get_cword_at_cursor_by_ref() ]]; do # Strip first character cur="${cur:1}" - # Decrease cursor position - ((index--)) + # Decrease cursor position, staying >= 0 + [[ $index -gt 0 ]] && ((index--)) done # Does found word match cword? @@ -448,8 +438,8 @@ _get_cword() ]]; do # Strip first character cur="${cur:1}" - # Decrease cursor position - ((index--)) + # Decrease cursor position, staying >= 0 + [[ $index -gt 0 ]] && ((index--)) done # Does found word matches cword? @@ -558,37 +548,37 @@ _quote_readline_by_ref() # _filedir() { - local i IFS=$'\n' xspec + local IFS=$'\n' - _tilde "$cur" || return 0 + _tilde "$cur" || return local -a toks - local quoted x tmp + local x tmp - _quote_readline_by_ref "$cur" quoted - x=$( compgen -d -- "$quoted" ) && + x=$( compgen -d -- "$cur" ) && while read -r tmp; do toks+=( "$tmp" ) done <<< "$x" if [[ "$1" != -d ]]; then + local quoted + _quote_readline_by_ref "$cur" quoted + # Munge xspec to contain uppercase version too # http://thread.gmane.org/gmane.comp.shells.bash.bugs/15294/focus=15306 - xspec=${1:+"!*.@($1|${1^^})"} + local xspec=${1:+"!*.@($1|${1^^})"} x=$( compgen -f -X "$xspec" -- $quoted ) && while read -r tmp; do toks+=( "$tmp" ) done <<< "$x" - fi - - # If the filter failed to produce anything, try without it if configured to - [[ -n ${COMP_FILEDIR_FALLBACK:-} && \ - -n "$1" && "$1" != -d && ${#toks[@]} -lt 1 ]] && \ - x=$( compgen -f -- $quoted ) && - while read -r tmp; do - toks+=( "$tmp" ) - done <<< "$x" + # 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" + fi if [[ ${#toks[@]} -ne 0 ]]; then # 2>/dev/null for direct invocation, e.g. in the _filedir unit test @@ -622,11 +612,59 @@ _split_longopt() # False (> 0) if not. _variables() { - if [[ $cur =~ ^(\$\{?)([A-Za-z0-9_]*)$ ]]; then - [[ $cur == *{* ]] && local suffix=} || local suffix= - COMPREPLY+=( $( compgen -P ${BASH_REMATCH[1]} -S "$suffix" -v -- \ - "${BASH_REMATCH[2]}" ) ) + if [[ $cur =~ ^(\$(\{[!#]?)?)([A-Za-z0-9_]*)$ ]]; then + # 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]} ) ) + 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 + COMPREPLY+=( ${arrs[*]} ) + else + # Complete ${var with ${variable} + COMPREPLY+=( ${vars[*]} ) + fi + else + # Complete $var with $variable + 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]}" ) ) + # Complete ${arr[@ and ${arr[* + if [[ ${BASH_REMATCH[3]} == [@*] ]]; then + COMPREPLY+=( "${BASH_REMATCH[1]}${BASH_REMATCH[2]}[${BASH_REMATCH[3]}]}" ) + fi + __ltrim_colon_completions "$cur" # array indexes may have colons return 0 + elif [[ $cur =~ ^\$\{[#!]?[A-Za-z0-9_]*\[.*\]$ ]]; then + # Complete ${array[idx] with ${array[idx]} + COMPREPLY+=( "$cur}" ) + __ltrim_colon_completions "$cur" + return 0 + else + case $prev in + TZ) + cur=/usr/share/zoneinfo/$cur + _filedir + for i in ${!COMPREPLY[@]}; do + if [[ ${COMPREPLY[i]} == *.tab ]]; then + unset 'COMPREPLY[i]' + continue + elif [[ -d ${COMPREPLY[i]} ]]; then + COMPREPLY[i]+=/ + compopt -o nospace + fi + COMPREPLY[i]=${COMPREPLY[i]#/usr/share/zoneinfo/} + done + return 0 + ;; + esac fi return 1 } @@ -707,7 +745,7 @@ _init_completion() fi done - [[ $cword -eq 0 ]] && return 1 + [[ $cword -le 0 ]] && return 1 prev=${words[cword-1]} [[ ${split-} ]] && _split_longopt && split=true @@ -722,15 +760,17 @@ __parse_options() # Take first found long option, or first one (short) if not found. option= - for i in $1; do - case $i in + local -a array + read -a array <<<"$1" + for i in "${array[@]}"; do + case "$i" in ---*) break ;; --?*) option=$i ; break ;; -?*) [[ $option ]] || option=$i ;; *) break ;; esac done - [[ $option ]] || return 0 + [[ $option ]] || return IFS=$' \t\n' # affects parsing of the regexps below... @@ -760,7 +800,7 @@ _parse_help() esac } \ | while read -r line; do - [[ $line == *([ $'\t'])-* ]] || continue + [[ $line == *([[:blank:]])-* ]] || continue # transform "-f FOO, --foo=FOO" to "-f , --foo=FOO" etc while [[ $line =~ \ ((^|[^-])-[A-Za-z0-9?][[:space:]]+)\[?[A-Z0-9]+\]? ]]; do @@ -825,7 +865,8 @@ _mac_addresses() # - ifconfig on Linux: HWaddr or ether # - ifconfig on FreeBSD: ether # - ip link: link/ether - COMPREPLY+=( $( { ifconfig -a || ip link show; } 2>/dev/null | sed -ne \ + 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 \ "s|.*[[:space:]]\(link/\)\{0,1\}ether[[:space:]]\{1,\}\($re\)[[:space:]].*|\2|p" -ne \ @@ -833,12 +874,12 @@ _mac_addresses() ) ) # ARP cache - COMPREPLY+=( $( { arp -an || ip neigh show; } 2>/dev/null | 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" ) ) # /etc/ethers - COMPREPLY+=( $( sed -ne \ + COMPREPLY+=( $( command sed -ne \ "s/^[[:space:]]*\($re\)[[:space:]].*/\1/p" /etc/ethers 2>/dev/null ) ) COMPREPLY=( $( compgen -W '${COMPREPLY[@]}' -- "$cur" ) ) @@ -851,23 +892,24 @@ _configured_interfaces() { if [[ -f /etc/debian_version ]]; then # Debian system - COMPREPLY=( $( compgen -W "$( sed -ne 's|^iface \([^ ]\{1,\}\).*$|\1|p'\ - /etc/network/interfaces )" -- "$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' \ /etc/sysconfig/network/ifcfg-* | \ - 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 \ /etc/sysconfig/interfaces | \ - 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' \ /etc/sysconfig/network-scripts/ifcfg-* | \ - sed -ne 's|.*ifcfg-\(.*\)|\1|p' )" -- "$cur" ) ) + command sed -ne 's|.*ifcfg-\([^*].*\)$|\1|p' )" -- "$cur" ) ) fi } @@ -877,9 +919,9 @@ _ip_addresses() { local PATH=$PATH:/sbin COMPREPLY+=( $( compgen -W \ - "$( { LC_ALL=C ifconfig -a || ip addr show; } 2>/dev/null | - sed -ne 's/.*addr:\([^[:space:]]*\).*/\1/p' \ - -ne 's|.*inet[[:space:]]\{1,\}\([^[:space:]/]*\).*|\1|p' )" \ + "$( { 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" ) ) } @@ -929,7 +971,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 @@ -996,7 +1038,7 @@ _expand() eval cur=$cur 2>/dev/null elif [[ "$cur" == \~* ]]; then cur=${cur#\~} - COMPREPLY=( $( compgen -P '~' -u "$cur" ) ) + COMPREPLY=( $( compgen -P '~' -u -- "$cur" ) ) [[ ${#COMPREPLY[@]} -eq 1 ]] && eval COMPREPLY[0]=${COMPREPLY[0]} return ${#COMPREPLY[@]} fi @@ -1007,7 +1049,7 @@ _expand() [[ $OSTYPE == *@(solaris|aix)* ]] && _pids() { - COMPREPLY=( $( compgen -W '$( command ps -efo pid | sed 1d )' -- "$cur" )) + COMPREPLY=( $( compgen -W '$( command ps -efo pid | command sed 1d )' -- "$cur" )) } || _pids() { @@ -1019,7 +1061,7 @@ _pids() [[ $OSTYPE == *@(solaris|aix)* ]] && _pgids() { - COMPREPLY=( $( compgen -W '$( command ps -efo pgid | sed 1d )' -- "$cur" )) + COMPREPLY=( $( compgen -W '$( command ps -efo pgid | command sed 1d )' -- "$cur" )) } || _pgids() { @@ -1028,25 +1070,32 @@ _pgids() # This function completes on process names. # AIX and SunOS prefer X/Open, all else should be BSD. +# @param $1 if -s, don't try to avoid truncated command names [[ $OSTYPE == *@(solaris|aix)* ]] && _pnames() { COMPREPLY=( $( compgen -X '<defunct>' -W '$( command ps -efo comm | \ - sed -e 1d -e "s:.*/::" -e "s/^-//" | sort -u )' -- "$cur" ) ) + command sed -e 1d -e "s:.*/::" -e "s/^-//" | sort -u )' -- "$cur" ) ) } || _pnames() { - # FIXME: completes "[kblockd/0]" to "0". Previously it was completed - # to "kblockd" which isn't correct either. "kblockd/0" would be - # arguably most correct, but killall from psmisc 22 treats arguments - # 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. - # Not using "ps axo comm" because under some Linux kernels, it - # truncates command names (see e.g. http://bugs.debian.org/497540#19) - COMPREPLY=( $( compgen -X '<defunct>' -W '$( command ps axo command= | \ - sed -e "s/ .*//" -e "s:.*/::" -e "s/:$//" -e "s/^[[(-]//" \ - -e "s/[])]$//" | sort -u )' -- "$cur" ) ) + if [[ "$1" == -s ]]; then + 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 + # arguably most correct, but killall from psmisc 22 treats arguments + # 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 \ + "s/ .*//" -e \ + "s:.*/::" -e \ + "s/:$//" -e \ + "s/^[[(-]//" -e \ + "s/[])]$//" | sort -u )' -- "$cur" ) ) + fi } # This function completes on user IDs @@ -1103,12 +1152,17 @@ _services() _sysvdirs local restore_nullglob=$(shopt -p nullglob); shopt -s nullglob - COMPREPLY=( $( printf '%s\n' ${sysvdirs[0]}/!($_backup_glob|functions) ) ) + COMPREPLY=( \ + $( printf '%s\n' ${sysvdirs[0]}/!($_backup_glob|functions|README) ) ) $restore_nullglob COMPREPLY+=( $( systemctl list-units --full --all 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 ) ) + fi + COMPREPLY=( $( compgen -W '${COMPREPLY[@]#${sysvdirs[0]}/}' -- "$cur" ) ) } @@ -1122,7 +1176,7 @@ _service() _init_completion || return # don't complete past 2nd token - [[ $cword -gt 2 ]] && return 0 + [[ $cword -gt 2 ]] && return if [[ $cword -eq 1 && $prev == ?(*/)service ]]; then _services @@ -1130,7 +1184,7 @@ _service() else local sysvdirs _sysvdirs - COMPREPLY=( $( compgen -W '`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" ) ) fi @@ -1151,7 +1205,7 @@ _modules() local modpath modpath=/lib/modules/$1 COMPREPLY=( $( compgen -W "$( command ls -RL $modpath 2>/dev/null | \ - sed -ne 's/^\(.*\)\.k\{0,1\}o\(\.[gx]z\)\{0,1\}$/\1/p' )" -- "$cur" ) ) + command sed -ne 's/^\(.*\)\.k\{0,1\}o\(\.[gx]z\)\{0,1\}$/\1/p' )" -- "$cur" ) ) } # This function completes on installed modules @@ -1171,10 +1225,10 @@ _installed_modules() # context of current completion. _usergroup() { - if [[ $cur = *\\\\* || $cur = *:*:* ]]; then + if [[ $cur == *\\\\* || $cur == *:*:* ]]; then # Give up early on if something seems horribly wrong. return - elif [[ $cur = *\\:* ]]; then + elif [[ $cur == *\\:* ]]; then # Completing group after 'user\:gr<TAB>'. # Reply with a list of groups prefixed with 'user:', readline will # escape to the colon. @@ -1189,7 +1243,7 @@ _usergroup() COMPREPLY=( $( compgen -g -- "$mycur" ) ) fi COMPREPLY=( $( compgen -P "$prefix" -W "${COMPREPLY[@]}" ) ) - elif [[ $cur = *:* ]]; then + elif [[ $cur == *:* ]]; then # Completing group after 'user:gr<TAB>'. # Reply with a list of unprefixed groups since readline with split on : # and only replace the 'gr' part @@ -1258,14 +1312,14 @@ _fstypes() if [[ -e /proc/filesystems ]]; then # Linux fss="$( cut -d$'\t' -f2 /proc/filesystems ) - $( awk '! /\*/ { print $NF }' /etc/filesystems 2>/dev/null )" + $( 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 )" + $( 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" ) ) @@ -1354,7 +1408,7 @@ _dvd_devices() _terms() { COMPREPLY+=( $( compgen -W \ - "$( sed -ne 's/^\([^[:space:]#|]\{2,\}\)|.*/\1/p' /etc/termcap \ + "$( 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" ) ) @@ -1375,12 +1429,11 @@ _user_at_host() if [[ $cur == *@* ]]; then _known_hosts_real "$cur" else - COMPREPLY=( $( compgen -u -- "$cur" ) ) + COMPREPLY=( $( compgen -u -S @ -- "$cur" ) ) + compopt -o nospace fi - - return 0 } -shopt -u hostcomplete && complete -F _user_at_host -o nospace talk ytalk finger +shopt -u hostcomplete && complete -F _user_at_host talk ytalk finger # NOTE: Using this function as a helper function is deprecated. Use # `_known_hosts_real' instead. @@ -1397,6 +1450,39 @@ _known_hosts() _known_hosts_real $options -- "$cur" } # _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 +_included_ssh_config_files() +{ + [[ $# -lt 1 ]] && echo "error: $FUNCNAME: missing mandatory argument CONFIG" + 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 + # 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 + # configuration file or /etc/ssh if included from the system configuration file.[...]" + if ! [[ "$i" =~ ^\~.*|^\/.* ]]; then + if [[ "$configfile" =~ ^\/etc\/ssh.* ]]; then + i="/etc/ssh/$i" + else + i="$HOME/.ssh/$i" + fi + fi + __expand_tilde_by_ref i + # In case the expanded variable contains multiple paths + for f in ${i}; do + if [ -r $f ]; then + config+=( "$f" ) + # The Included file is processed to look for Included files in itself + _included_ssh_config_files $f + fi + done + done +} # _included_ssh_config_files() + # Helper function for completing _known_hosts. # This function performs host completion based on ssh's config and known_hosts # files, as well as hostnames reported by avahi-browse if @@ -1441,6 +1527,11 @@ _known_hosts_real() done fi + # "Include" keyword in ssh config files + for i in "${config[@]}"; do + _included_ssh_config_files "$i" + done + # Known hosts files from configs if [[ ${#config[@]} -gt 0 ]]; then local OIFS=$IFS IFS=$'\n' j @@ -1533,7 +1624,7 @@ _known_hosts_real() # append any available aliases from config files if [[ ${#config[@]} -gt 0 && -n "$aliases" ]]; then - local hosts=$( sed -ne 's/^[ \t]*[Hh][Oo][Ss][Tt]\([Nn][Aa][Mm][Ee]\)\{0,1\}['"$'\t '"']\{1,\}\([^#*?]*\)\(#.*\)\{0,1\}$/\2/p' "${config[@]}" ) + 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 @@ -1548,7 +1639,7 @@ _known_hosts_real() # 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" ) ) + awk -F';' '/^=/ { print $7 }' | sort -u )" -- "$cur" ) ) fi # Add hosts reported by ruptime. @@ -1565,7 +1656,6 @@ _known_hosts_real() __ltrim_colon_completions "$prefix$user$cur" - return 0 } # _known_hosts_real() complete -F _known_hosts traceroute traceroute6 tracepath tracepath6 \ fping fping6 telnet rsh rlogin ftp dig mtr ssh-installkeys showmount @@ -1586,7 +1676,7 @@ _cd() # ./ or ../ if [[ -z "${CDPATH:-}" || "$cur" == ?(.)?(.)/* ]]; then _filedir -d - return 0 + return fi local -r mark_dirs=$(_rl_enabled mark-directories && echo y) @@ -1596,7 +1686,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 @@ -1613,12 +1703,12 @@ _cd() fi fi - return 0 + return } if shopt -q cdable_vars; then - complete -v -F _cd -o nospace cd + complete -v -F _cd -o nospace cd pushd else - complete -F _cd -o nospace cd + complete -F _cd -o nospace cd pushd fi # a wrapper method for the next one, when the offset is unknown @@ -1664,7 +1754,7 @@ _command_offset() COMP_WORDS[i]=${COMP_WORDS[i+$word_offset]} done for (( i; i <= COMP_CWORD; i++ )); do - unset COMP_WORDS[i] + unset 'COMP_WORDS[i]' done ((COMP_CWORD -= $word_offset)) @@ -1751,49 +1841,49 @@ _longopt() case "${prev,,}" in --help|--usage|--version) - return 0 + return ;; --*dir*) _filedir -d - return 0 + return ;; --*file*|--*path*) _filedir - return 0 + return ;; --+([-a-z0-9_])) - local argtype=$( $1 --help 2>&1 | sed -ne \ + 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 - return 0 + return ;; *file*|*path*) _filedir - return 0 + return ;; esac ;; esac - $split && return 0 + $split && return if [[ "$cur" == -* ]]; then - COMPREPLY=( $( compgen -W "$( $1 --help 2>&1 | \ - sed -ne 's/.*\(--[-A-Za-z0-9]\{1,\}=\{0,1\}\).*/\1/p' | sort -u )" \ + 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 == *= ]] && compopt -o nospace - elif [[ "$1" == @(mk|rm)dir ]]; then + elif [[ "$1" == @(@(mk|rm)dir|chroot) ]]; then _filedir -d else _filedir fi } # makeinfo and texi2dvi are defined elsewhere. -complete -F _longopt a2ps awk base64 bash bc bison cat colordiff cp csplit \ - cut date df diff dir du enscript env expand fmt fold gperf \ - grep grub head indent irb ld ldd less ln ls m4 md5sum mkdir mkfifo mknod \ +complete -F _longopt a2ps awk base64 bash bc bison cat chroot colordiff cp \ + csplit cut date df diff dir du enscript env expand fmt fold gperf \ + grep grub head irb ld ldd less ln ls m4 md5sum mkdir mkfifo mknod \ mv netstat nl nm objcopy objdump od paste pr ptx readelf rm rmdir \ 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 @@ -1804,7 +1894,7 @@ _filedir_xspec() local cur prev words cword _init_completion || return - _tilde "$cur" || return 0 + _tilde "$cur" || return local IFS=$'\n' xspec=${_xspecs[${1##*/}]} tmp local -a toks @@ -1828,7 +1918,7 @@ _filedir_xspec() xspec="$matchop($xspec|${xspec^^})" toks+=( $( - eval compgen -f -X "!$xspec" -- "\$(quote_readline "\$cur")" | { + eval compgen -f -X "'!$xspec'" -- "\$(quote_readline "\$cur")" | { while read -r tmp; do [[ -n $tmp ]] && printf '%s\n' $tmp done @@ -1852,10 +1942,11 @@ _install_xspec() } # 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|[ejsw]ar|exe|pk3|wsz|zargo|xpi|s[tx][cdiw]|sx[gm]|o[dt][tspgfc]|od[bm]|oxt|epub|apk|do[ct][xm]|p[op]t[mx]|xl[st][xm])' unzip zipinfo +_install_xspec '!*.@(zip|[ejsw]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 '*.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 unpigz +_install_xspec '!*.@(Z|[gGd]z|t[ag]z)' gunzip zcat +_install_xspec '!*.@(Z|[gGdz]z|t[ag]z)' unpigz _install_xspec '!*.Z' uncompress # lzcmp, lzdiff intentionally not here, see Debian: #455510 _install_xspec '!*.@(tlz|lzma)' lzcat lzegrep lzfgrep lzgrep lzless lzmore unlzma @@ -1863,21 +1954,21 @@ _install_xspec '!*.@(?(t)xz|tlz|lzma)' unxz xzcat _install_xspec '!*.lrz' lrunzip _install_xspec '!*.@(gif|jp?(e)g|miff|tif?(f)|pn[gm]|p[bgp]m|bmp|xpm|ico|xwd|tga|pcx)' ee _install_xspec '!*.@(gif|jp?(e)g|tif?(f)|png|p[bgp]m|bmp|x[bp]m|rle|rgb|pcx|fits|pm|svg)' qiv -_install_xspec '!*.@(gif|jp?(e)g|tif?(f)|png|p[bgp]m|bmp|x[bp]m|rle|rgb|pcx|fits|pm|?(e)ps)' xv +_install_xspec '!*.@(gif|jp?(e)g?(2)|j2[ck]|jp[2f]|tif?(f)|png|p[bgp]m|bmp|x[bp]m|rle|rgb|pcx|fits|pm|?(e)ps)' xv _install_xspec '!*.@(@(?(e)ps|?(E)PS|pdf|PDF)?(.gz|.GZ|.bz2|.BZ2|.Z))' gv ggv kghostview _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 '!*.pdf' epdfview +_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 +_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|wma|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|wmv|mp[234]|MP[234]|m4[pv]|M4[PV]|mkv|MKV|og[gmv]|OG[GMV]|t[ps]|T[PS]|m2t?(s)|M2T?(S)|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|wma|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|wmv|mp[234]|MP[234]|m4[pv]|M4[PV]|mkv|MKV|og[gmv]|OG[GMV]|t[ps]|T[PS]|m2t?(s)|M2T?(S)|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[gmv]|OG[GMV]|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[gmv]|OG[GMV]|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 '!*.@(avi|asf|wmv)' aviplay _install_xspec '!*.@(rm?(j)|ra?(m)|smi?(l))' realplay _install_xspec '!*.@(mpg|mpeg|avi|mov|qt)' xanim @@ -1886,12 +1977,13 @@ _install_xspec '!*.@(mp3|ogg|pls|m3u)' gqmpeg freeamp _install_xspec '!*.fig' xfig _install_xspec '!*.@(mid?(i)|cmf)' playmidi _install_xspec '!*.@(mid?(i)|rmi|rcp|[gr]36|g18|mod|xm|it|x3m|s[3t]m|kar)' timidity -_install_xspec '!*.@(669|abc|am[fs]|d[bs]m|dmf|far|it|mdl|m[eo]d|mid?(i)|mt[2m]|okta|p[st]m|s[3t]m|ult|umx|wav|xm)' modplugplay modplug123 -_install_xspec '*.@(o|so|so.!(conf|*/*)|a|[rs]pm|gif|jp?(e)g|mp3|mp?(e)g|avi|asf|ogg|class)' vi vim gvim rvim view rview rgvim rgview gview emacs xemacs sxemacs kate kwrite +_install_xspec '!*.@(669|abc|am[fs]|d[bs]m|dmf|far|it|mdl|m[eo]d|mid?(i)|mt[2m]|oct|okt?(a)|p[st]m|s[3t]m|ult|umx|wav|xm)' modplugplay modplug123 +_install_xspec '*.@([ao]|so|so.!(conf|*/*)|[rs]pm|gif|jp?(e)g|mp3|mp?(e)g|avi|asf|ogg|class)' vi vim gvim rvim view rview rgvim rgview gview emacs xemacs sxemacs kate kwrite _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 firefox mozilla-firefox iceweasel google-chrome chromium-browser epiphany -_install_xspec '!*.@(sxw|stw|sxg|sgl|doc?([mx])|dot?([mx])|rtf|txt|htm|html|?(f)odt|ott|odm)' oowriter +_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 _install_xspec '!*.@(sxi|sti|pps?(x)|ppt?([mx])|pot?([mx])|?(f)odp|otp)' ooimpress _install_xspec '!*.@(sxc|stc|xls?([bmx])|xlw|xlt?([mx])|[ct]sv|?(f)ods|ots)' oocalc _install_xspec '!*.@(sxd|std|sda|sdd|?(f)odg|otg)' oodraw @@ -1906,7 +1998,6 @@ _install_xspec '!*.ly' lilypond ly2dvi _install_xspec '!*.@(dif?(f)|?(d)patch)?(.@([gx]z|bz2|lzma))' cdiff _install_xspec '!@(*.@(ks|jks|jceks|p12|pfx|bks|ubr|gkr|cer|crt|cert|p7b|pkipath|pem|p10|csr|crl)|cacerts)' portecle _install_xspec '!*.@(mp[234c]|og[ag]|@(fl|a)ac|m4[abp]|spx|tta|w?(a)v|wma|aif?(f)|asf|ape)' kid3 kid3-qt -_install_xspec '!*.py' pyflakes unset -f _install_xspec # Minimal completion to use as fallback in _completion_loader. @@ -1917,23 +2008,47 @@ _minimal() $split && return _filedir } -# Complete the empty string to allow completion of '>', '>>', and '<' +# Complete the empty string to allow completion of '>', '>>', and '<' on < 4.3 # http://lists.gnu.org/archive/html/bug-bash/2012-01/msg00045.html complete -F _minimal '' +__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 + for dir in ${XDG_DATA_DIRS:-/usr/local/share:/usr/share}; do + dirs+=( $dir/bash-completion/completions ) + done + IFS=$OIFS + + if [[ $BASH_SOURCE == */* ]]; then + dirs+=( "${BASH_SOURCE%/*}/completions" ) + else + dirs+=( ./completions ) + fi + + for dir in "${dirs[@]}"; do + for compfile in "${cmd##*/}" "${cmd##*/}".bash _"${cmd##*/}"; do + compfile="$dir/$compfile" + # Avoid trying to source dirs; https://bugzilla.redhat.com/903540 + [[ -f "$compfile" ]] && . "$compfile" &>/dev/null && return 0 + done + done + + return 1 +} + # set up dynamic completion loading _completion_loader() { - local compfile=./completions - [[ $BASH_SOURCE == */* ]] && compfile="${BASH_SOURCE%/*}/completions" - compfile+="/${1##*/}" + # $1=_EmptycmD_ already for empty cmds in bash 4.3, set to it for earlier + local cmd="${1:-_EmptycmD_}" - # Avoid trying to source dirs; https://bugzilla.redhat.com/903540 - [[ -f "$compfile" ]] && . "$compfile" &>/dev/null && return 124 + __load_completion "$cmd" && return 124 # Need to define *something*, otherwise there will be no completion at all. - complete -F _minimal "$1" && return 124 + complete -F _minimal -- "$cmd" && return 124 } && complete -D -F _completion_loader @@ -1947,31 +2062,31 @@ _xfunc() local srcfile=$1 shift declare -F $1 &>/dev/null || { - local compdir=./completions - [[ $BASH_SOURCE == */* ]] && compdir="${BASH_SOURCE%/*}/completions" - . "$compdir/$srcfile" + __load_completion "$srcfile" } "$@" } # source compat completion directory definitions -if [[ -d $BASH_COMPLETION_COMPAT_DIR && -r $BASH_COMPLETION_COMPAT_DIR && \ - -x $BASH_COMPLETION_COMPAT_DIR ]]; then - for i in $(LC_ALL=C command ls "$BASH_COMPLETION_COMPAT_DIR"); do - i=$BASH_COMPLETION_COMPAT_DIR/$i +compat_dir=${BASH_COMPLETION_COMPAT_DIR:-/etc/bash_completion.d} +if [[ -d $compat_dir && -r $compat_dir && -x $compat_dir ]]; then + for i in "$compat_dir"/*; do [[ ${i##*/} != @($_backup_glob|Makefile*|$_blacklist_glob) \ && -f $i && -r $i ]] && . "$i" done fi -unset i _blacklist_glob +unset compat_dir i _blacklist_glob # source user completion file -[[ ${BASH_SOURCE[0]} != ~/.bash_completion && -r ~/.bash_completion ]] \ - && . ~/.bash_completion +user_completion=${BASH_COMPLETION_USER_FILE:-~/.bash_completion} +[[ ${BASH_SOURCE[0]} != $user_completion && -r $user_completion ]] \ + && . $user_completion +unset user_completion + unset -f have unset have set $BASH_COMPLETION_ORIGINAL_V_VALUE unset BASH_COMPLETION_ORIGINAL_V_VALUE -# ex: ts=4 sw=4 et filetype=sh +# ex: filetype=sh |