summaryrefslogtreecommitdiff
path: root/bash_completion
diff options
context:
space:
mode:
authorDavid Paleino <dapal@debian.org>2011-11-03 12:32:52 +0100
committerDavid Paleino <dapal@debian.org>2011-11-03 12:32:52 +0100
commit2c8171c38d87ddef31c92a76547d3fdf773a1337 (patch)
tree5e720d5a06ead72ed55454bf6647a712a761ed91 /bash_completion
parent9920a8faedf704420571d8072ccab27e9dac40ba (diff)
downloadbash-completion-2c8171c38d87ddef31c92a76547d3fdf773a1337.tar.gz
Imported Upstream version 1.90upstream/1.90
Diffstat (limited to 'bash_completion')
-rw-r--r--bash_completion936
1 files changed, 569 insertions, 367 deletions
diff --git a/bash_completion b/bash_completion
index 66019379..62ef87ed 100644
--- a/bash_completion
+++ b/bash_completion
@@ -1,5 +1,6 @@
+# -*- shell-script -*-
#
-# bash_completion - programmable completion functions for bash 3.2+
+# bash_completion - programmable completion functions for bash 4.1+
#
# Copyright © 2006-2008, Ian Macdonald <ian@caliban.org>
# © 2009-2011, Bash Completion Maintainers
@@ -17,13 +18,13 @@
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
-# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# The latest version of this software can be obtained here:
#
# http://bash-completion.alioth.debian.org/
#
-# RELEASE: 1.3
+# RELEASE: 1.90
if [[ $- == *v* ]]; then
BASH_COMPLETION_ORIGINAL_V_VALUE="-v"
@@ -39,21 +40,12 @@ fi
# Alter the following to reflect the location of this file.
#
-[ -n "$BASH_COMPLETION" ] || BASH_COMPLETION=/etc/bash_completion
-[ -n "$BASH_COMPLETION_DIR" ] || BASH_COMPLETION_DIR=/etc/bash_completion.d
[ -n "$BASH_COMPLETION_COMPAT_DIR" ] || BASH_COMPLETION_COMPAT_DIR=/etc/bash_completion.d
-readonly BASH_COMPLETION BASH_COMPLETION_DIR BASH_COMPLETION_COMPAT_DIR
+readonly BASH_COMPLETION_COMPAT_DIR
-# Set a couple of useful vars
+# Blacklisted completions, causing problems with our code.
#
-UNAME=$( uname -s )
-# strip OS type and version under Cygwin (e.g. CYGWIN_NT-5.1 => Cygwin)
-UNAME=${UNAME/CYGWIN_*/Cygwin}
-
-case ${UNAME} in
- Linux|GNU|GNU/*) USERLAND=GNU ;;
- *) USERLAND=${UNAME} ;;
-esac
+_blacklist_glob='@(acroread.sh)'
# Turn on extended globbing and programmable completion
shopt -s extglob progcomp
@@ -64,68 +56,6 @@ shopt -s extglob progcomp
# Make directory commands see only directories
complete -d pushd
-# The following section lists completions that are redefined later
-# Do NOT break these over multiple lines.
-#
-# START exclude -- do NOT remove this line
-# bzcmp, bzdiff, bz*grep, bzless, bzmore intentionally not here, see Debian: #455510
-complete -f -X '!*.?(t)bz?(2)' bunzip2 bzcat pbunzip2 pbzcat
-complete -f -X '!*.@(zip|[ejw]ar|exe|pk3|wsz|zargo|xpi|sxw|o[tx]t|od[fgpst]|epub|apk)' unzip zipinfo
-complete -f -X '*.Z' compress znew
-# zcmp, zdiff, z*grep, zless, zmore intentionally not here, see Debian: #455510
-complete -f -X '!*.@(Z|[gGd]z|t[ag]z)' gunzip zcat unpigz
-complete -f -X '!*.Z' uncompress
-# lzcmp, lzdiff intentionally not here, see Debian: #455510
-complete -f -X '!*.@(tlz|lzma)' lzcat lzegrep lzfgrep lzgrep lzless lzmore unlzma
-complete -f -X '!*.@(?(t)xz|tlz|lzma)' unxz xzcat
-complete -f -X '!*.lrz' lrunzip
-complete -f -X '!*.@(gif|jp?(e)g|miff|tif?(f)|pn[gm]|p[bgp]m|bmp|xpm|ico|xwd|tga|pcx)' ee
-complete -f -X '!*.@(gif|jp?(e)g|tif?(f)|png|p[bgp]m|bmp|x[bp]m|rle|rgb|pcx|fits|pm)' xv qiv
-complete -f -X '!*.@(@(?(e)ps|?(E)PS|pdf|PDF)?(.gz|.GZ|.bz2|.BZ2|.Z))' gv ggv kghostview
-complete -f -X '!*.@(dvi|DVI)?(.@(gz|Z|bz2))' xdvi kdvi
-complete -f -X '!*.dvi' dvips dviselect dvitype dvipdf advi dvipdfm dvipdfmx
-complete -f -X '!*.[pf]df' acroread gpdf xpdf
-complete -f -X '!*.@(?(e)ps|pdf)' kpdf
-complete -f -X '!*.@(@(?(e)ps|?(E)PS|[pf]df|[PF]DF|dvi|DVI)?(.gz|.GZ|.bz2|.BZ2)|cb[rz]|djv?(u)|gif|jp?(e)g|miff|tif?(f)|pn[gm]|p[bgp]m|bmp|xpm|ico|xwd|tga|pcx|fdf)' evince
-complete -f -X '!*.@(okular|@(?(e|x)ps|?(E|X)PS|pdf|PDF|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|fdf|FDF)?(.?(gz|GZ|bz2|BZ2)))' okular
-complete -f -X '!*.@(?(e)ps|pdf)' ps2pdf ps2pdf12 ps2pdf13 ps2pdf14 ps2pdfwr
-complete -f -X '!*.texi*' makeinfo texi2html
-complete -f -X '!*.@(?(la)tex|texi|dtx|ins|ltx)' tex latex slitex jadetex pdfjadetex pdftex pdflatex texi2dvi
-complete -f -X '!*.mp3' mpg123 mpg321 madplay
-complete -f -X '!*@(.@(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
-complete -f -X '!*@(.@(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
-complete -f -X '!*.@(avi|asf|wmv)' aviplay
-complete -f -X '!*.@(rm?(j)|ra?(m)|smi?(l))' realplay
-complete -f -X '!*.@(mpg|mpeg|avi|mov|qt)' xanim
-complete -f -X '!*.@(ogg|m3u|flac|spx)' ogg123
-complete -f -X '!*.@(mp3|ogg|pls|m3u)' gqmpeg freeamp
-complete -f -X '!*.fig' xfig
-complete -f -X '!*.@(mid?(i)|cmf)' playmidi
-complete -f -X '!*.@(mid?(i)|rmi|rcp|[gr]36|g18|mod|xm|it|x3m|s[3t]m|kar)' timidity
-complete -f -X '!*.@(m[eo]d|s[3t]m|xm|it)' modplugplay modplug123
-complete -f -X '*.@(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
-complete -f -X '!*.@([eE][xX][eE]?(.[sS][oO])|[cC][oO][mM]|[sS][cC][rR])' wine
-complete -f -X '!*.@(zip|z|gz|tgz)' bzme
-# konqueror not here on purpose, it's more than a web/html browser
-complete -f -X '!*.@(?([xX]|[sS])[hH][tT][mM]?([lL]))' netscape mozilla lynx opera galeon dillo elinks amaya firefox mozilla-firefox iceweasel google-chrome chromium-browser epiphany
-complete -f -X '!*.@(sxw|stw|sxg|sgl|doc?([mx])|dot?([mx])|rtf|txt|htm|html|odt|ott|odm)' oowriter
-complete -f -X '!*.@(sxi|sti|pps?(x)|ppt?([mx])|pot?([mx])|odp|otp)' ooimpress
-complete -f -X '!*.@(sxc|stc|xls?([bmx])|xlw|xlt?([mx])|[ct]sv|ods|ots)' oocalc
-complete -f -X '!*.@(sxd|std|sda|sdd|odg|otg)' oodraw
-complete -f -X '!*.@(sxm|smf|mml|odf)' oomath
-complete -f -X '!*.odb' oobase
-complete -f -X '!*.[rs]pm' rpm2cpio
-complete -f -X '!*.aux' bibtex
-complete -f -X '!*.po' poedit gtranslator kbabel lokalize
-complete -f -X '!*.@([Pp][Rr][Gg]|[Cc][Ll][Pp])' harbour gharbour hbpp
-complete -f -X '!*.[Hh][Rr][Bb]' hbrun
-complete -f -X '!*.ly' lilypond ly2dvi
-complete -f -X '!*.@(dif?(f)|?(d)patch)?(.@([gx]z|bz2|lzma))' cdiff
-complete -f -X '!*.lyx' lyx
-complete -f -X '!@(*.@(ks|jks|jceks|p12|pfx|bks|ubr|gkr|cer|crt|cert|p7b|pkipath|pem|p10|csr|crl)|cacerts)' portecle
-complete -f -X '!*.@(mp[234c]|og[ag]|@(fl|a)ac|m4[abp]|spx|tta|w?(a)v|wma|aif?(f)|asf|ape)' kid3 kid3-qt
-# FINISH exclude -- do not remove this line
-
# start of section containing compspecs that can be handled within bash
# user commands see only users
@@ -163,16 +93,31 @@ complete -b builtin
# start of section containing completion functions called by other functions
+# Check if we're running on the given userland
+# @param $1 userland to check for
+_userland()
+{
+ local userland=$( uname -s )
+ [[ $userland == @(Linux|GNU/*) ]] && userland=GNU
+ [[ $userland == $1 ]]
+}
+
# This function checks whether we have a given program on the system.
-# No need for bulky functions in memory if we don't.
#
-have()
+_have()
{
- unset -v have
# Completions for system administrator commands are installed as well in
# case completion is attempted via `sudo command ...'.
- PATH=$PATH:/sbin:/usr/sbin:/usr/local/sbin type $1 &>/dev/null &&
- have="yes"
+ PATH=$PATH:/usr/sbin:/sbin:/usr/local/sbin type $1 &>/dev/null
+}
+
+# Backwards compatibility for compat completions that use have().
+# @deprecated should no longer be used; generally not needed with dynamically
+# loaded completions, and _have is suitable for runtime use.
+have()
+{
+ unset -v have
+ _have $1 && have=yes
}
# This function checks whether a given readline variable
@@ -214,7 +159,8 @@ dequote()
# use multiple '_upvar' calls, since one '_upvar' call might
# reassign a variable to be used by another '_upvar' call.
# See: http://fvue.nl/wiki/Bash:_Passing_variables_by_reference
-_upvar() {
+_upvar()
+{
if unset -v "$1"; then # Unset & validate varname
if (( $# == 2 )); then
eval $1=\"\$2\" # Return single value
@@ -233,7 +179,8 @@ _upvar() {
# -v Assign single value to varname
# Return: 1 if error occurs
# See: http://fvue.nl/wiki/Bash:_Passing_variables_by_reference
-_upvars() {
+_upvars()
+{
if ! (( $# )); then
echo "${FUNCNAME[0]}: usage: ${FUNCNAME[0]} [-v varname"\
"value] | [-aN varname [value ...]] ..." 1>&2
@@ -276,8 +223,9 @@ _upvars() {
# @param $2 words Name of variable to return words to
# @param $3 cword Name of variable to return cword to
#
-__reassemble_comp_words_by_ref() {
- local exclude i j ref
+__reassemble_comp_words_by_ref()
+{
+ local exclude i j line ref
# Exclude word separator characters?
if [[ $1 ]]; then
# Yes, exclude word separator characters;
@@ -290,26 +238,40 @@ __reassemble_comp_words_by_ref() {
# Are characters excluded which were former included?
if [[ $exclude ]]; then
# Yes, list of word completion separators has shrunk;
+ line=$COMP_LINE
# Re-assemble words to complete
for (( i=0, j=0; i < ${#COMP_WORDS[@]}; i++, j++)); do
# Is current word not word 0 (the command itself) and is word not
- # empty and is word made up of just word separator characters to be
- # excluded?
+ # empty and is word made up of just word separator characters to
+ # be excluded and is current word not preceded by whitespace in
+ # original line?
while [[ $i -gt 0 && ${COMP_WORDS[$i]} &&
- ${COMP_WORDS[$i]//[^$exclude]} == ${COMP_WORDS[$i]}
+ ${COMP_WORDS[$i]//[^$exclude]} == ${COMP_WORDS[$i]}
]]; do
- [ $j -ge 2 ] && ((j--))
- # Append word separator to current word
+ # 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:0:1} != ' ' && ${line:0:1} != $'\t' ]] &&
+ (( j >= 2 )) && ((j--))
+ # Append word separator to current or new word
ref="$2[$j]"
eval $2[$j]=\${!ref}\${COMP_WORDS[i]}
# Indicate new cword
[ $i = $COMP_CWORD ] && eval $3=$j
- # Indicate next word if available, else end *both* while and for loop
+ # 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:0:1} == ' ' || ${line:0:1} == $'\t' ]] && ((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]}
+ # Remove optional whitespace + word from line copy
+ line=${line#*"${COMP_WORDS[i]}"}
# Indicate new cword
[[ $i == $COMP_CWORD ]] && eval $3=$j
done
@@ -323,51 +285,50 @@ __reassemble_comp_words_by_ref() {
# @param $1 exclude Characters out of $COMP_WORDBREAKS which should NOT be
# considered word breaks. This is useful for things like scp where
# we want to return host:path and not only path, so we would pass the
-# colon (:) as $1 in this case. Bash-3 doesn't do word splitting, so this
-# ensures we get the same word on both bash-3 and bash-4.
+# colon (:) as $1 in this case.
# @param $2 words Name of variable to return words to
# @param $3 cword Name of variable to return cword to
# @param $4 cur Name of variable to return current word to complete to
-# @see ___get_cword_at_cursor_by_ref()
-__get_cword_at_cursor_by_ref() {
+# @see __reassemble_comp_words_by_ref()
+__get_cword_at_cursor_by_ref()
+{
local cword words=()
__reassemble_comp_words_by_ref "$1" words cword
- local i cur2
- local cur="$COMP_LINE"
- local index="$COMP_POINT"
- for (( i = 0; i <= cword; ++i )); do
- while [[
- # Current word fits in $cur?
- "${#cur}" -ge ${#words[i]} &&
- # $cur doesn't match cword?
- "${cur:0:${#words[i]}}" != "${words[i]}"
- ]]; do
- # Strip first character
- cur="${cur:1}"
- # Decrease cursor position
- ((index--))
- done
-
- # Does found word matches cword?
- if [[ "$i" -lt "$cword" ]]; then
- # No, cword lies further;
- local old_size="${#cur}"
- cur="${cur#${words[i]}}"
- local new_size="${#cur}"
- index=$(( index - old_size + new_size ))
- fi
- done
+ local i cur cur2 index=$COMP_POINT lead=${COMP_LINE:0:$COMP_POINT}
+ # Cursor not at position 0 and not leaded by just space(s)?
+ if [[ $index -gt 0 && ( $lead && ${lead//[[:space:]]} ) ]]; then
+ cur=$COMP_LINE
+ for (( i = 0; i <= cword; ++i )); do
+ while [[
+ # Current word fits in $cur?
+ "${#cur}" -ge ${#words[i]} &&
+ # $cur doesn't match cword?
+ "${cur:0:${#words[i]}}" != "${words[i]}"
+ ]]; do
+ # Strip first character
+ cur="${cur:1}"
+ # Decrease cursor position
+ ((index--))
+ done
- if [[ "${words[cword]:0:${#cur}}" != "$cur" ]]; then
- # We messed up. At least return the whole word so things keep working
- cur2=${words[cword]}
- else
- cur2=${cur:0:$index}
+ # Does found word match cword?
+ if [[ "$i" -lt "$cword" ]]; then
+ # No, cword lies further;
+ local old_size="${#cur}"
+ cur="${cur#${words[i]}}"
+ local new_size="${#cur}"
+ index=$(( index - old_size + new_size ))
+ fi
+ done
+ # Clear $cur if just space(s)
+ [[ $cur && ! ${cur//[[:space:]]} ]] && cur=
+ # Zero $index if negative
+ [[ $index -lt 0 ]] && index=0
fi
- local "$2" "$3" "$4" &&
- _upvars -a${#words[@]} $2 "${words[@]}" -v $3 "$cword" -v $4 "$cur2"
+ local "$2" "$3" "$4" && _upvars -a${#words[@]} $2 "${words[@]}" \
+ -v $3 "$cword" -v $4 "${cur:0:$index}"
}
@@ -388,9 +349,7 @@ __get_cword_at_cursor_by_ref() {
# -n EXCLUDE Characters out of $COMP_WORDBREAKS which should NOT be
# considered word breaks. This is useful for things like scp
# where we want to return host:path and not only path, so we
-# would pass the colon (:) as -n option in this case. Bash-3
-# doesn't do word splitting, so this ensures we get the same
-# word on both bash-3 and bash-4.
+# would pass the colon (:) as -n option in this case.
# -c VARNAME Return cur via $VARNAME
# -p VARNAME Return prev via $VARNAME
# -w VARNAME Return words via $VARNAME
@@ -431,7 +390,7 @@ _get_comp_words_by_ref()
[[ $vcur ]] && { upvars+=("$vcur" ); upargs+=(-v $vcur "$cur" ); }
[[ $vcword ]] && { upvars+=("$vcword"); upargs+=(-v $vcword "$cword"); }
- [[ $vprev ]] && { upvars+=("$vprev" ); upargs+=(-v $vprev
+ [[ $vprev && $cword -ge 1 ]] && { upvars+=("$vprev" ); upargs+=(-v $vprev
"${words[cword - 1]}"); }
[[ $vwords ]] && { upvars+=("$vwords"); upargs+=(-a${#words[@]} $vwords
"${words[@]}"); }
@@ -448,8 +407,7 @@ _get_comp_words_by_ref()
# @param $1 string Characters out of $COMP_WORDBREAKS which should NOT be
# considered word breaks. This is useful for things like scp where
# we want to return host:path and not only path, so we would pass the
-# colon (:) as $1 in this case. Bash-3 doesn't do word splitting, so this
-# ensures we get the same word on both bash-3 and bash-4.
+# colon (:) as $1 in this case.
# @param $2 integer Index number of word to return, negatively offset to the
# current word (default is 0, previous is 1), respecting the exclusions
# given at $1. For example, `_get_cword "=:" 1' returns the word left of
@@ -515,14 +473,14 @@ _get_cword()
_get_pword()
{
if [ $COMP_CWORD -ge 1 ]; then
- _get_cword "${@:-}" 1;
+ _get_cword "${@:-}" 1
fi
}
# If the word-to-complete contains a colon (:), left-trim COMPREPLY items with
# word-to-complete.
-# On bash-3, and bash-4 with a colon in COMP_WORDBREAKS, words containing
+# With a colon in COMP_WORDBREAKS, words containing
# colons are always completed as entire words if the word to complete contains
# a colon. This function fixes this, by removing the colon-containing-prefix
# from COMPREPLY items.
@@ -537,16 +495,9 @@ _get_pword()
# @param $1 current word to complete (cur)
# @modifies global array $COMPREPLY
#
-__ltrim_colon_completions() {
- # If word-to-complete contains a colon,
- # and bash-version < 4,
- # or bash-version >= 4 and COMP_WORDBREAKS contains a colon
- if [[
- "$1" == *:* && (
- ${BASH_VERSINFO[0]} -lt 4 ||
- (${BASH_VERSINFO[0]} -ge 4 && "$COMP_WORDBREAKS" == *:*)
- )
- ]]; then
+__ltrim_colon_completions()
+{
+ if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then
# Remove colon-word prefix from COMPREPLY items
local colon_word=${1%${1##*:}}
local i=${#COMPREPLY[*]}
@@ -564,20 +515,8 @@ __ltrim_colon_completions() {
# $ ls "a'b/"
# c
# $ compgen -f "a'b/" # Wrong, doesn't return output
-# $ compgen -f "a\'b/" # Good (bash-4)
+# $ compgen -f "a\'b/" # Good
# a\'b/c
-# $ compgen -f "a\\\\\'b/" # Good (bash-3)
-# a\'b/c
-#
-# On bash-3, special characters need to be escaped extra. This is
-# unless the first character is a single quote ('). If the single
-# quote appears further down the string, bash default completion also
-# fails, e.g.:
-#
-# $ ls 'a&b/'
-# f
-# $ foo 'a&b/<TAB> # Becomes: foo 'a&b/f'
-# $ foo a'&b/<TAB> # Nothing happens
#
# See also:
# - http://lists.gnu.org/archive/html/bug-bash/2009-03/msg00155.html
@@ -588,17 +527,8 @@ __ltrim_colon_completions() {
_quote_readline_by_ref()
{
if [[ ${1:0:1} == "'" ]]; then
- if [[ ${BASH_VERSINFO[0]} -ge 4 ]]; then
- # Leave out first character
- printf -v $2 %s "${1:1}"
- else
- # Quote word, leaving out first character
- printf -v $2 %q "${1:1}"
- # Double-quote word (bash-3)
- printf -v $2 %q ${!2}
- fi
- elif [[ ${BASH_VERSINFO[0]} -le 3 && ${1:0:1} == '"' ]]; then
- printf -v $2 %q "${1:1}"
+ # Leave out first character
+ printf -v $2 %s "${1:1}"
else
printf -v $2 %q "$1"
fi
@@ -610,19 +540,6 @@ _quote_readline_by_ref()
} # _quote_readline_by_ref()
-# This function turns on "-o filenames" behavior dynamically. It is present
-# for bash < 4 reasons. See http://bugs.debian.org/272660#64 for info about
-# the bash < 4 compgen hack.
-_compopt_o_filenames()
-{
- # We test for compopt availability first because directly invoking it on
- # bash < 4 at this point may cause terminal echo to be turned off for some
- # reason, see https://bugzilla.redhat.com/653669 for more info.
- type compopt &>/dev/null && compopt -o filenames 2>/dev/null || \
- compgen -f /non-existing-dir/ >/dev/null
-}
-
-
# This function performs file and directory completion. It's better than
# simply using 'compgen -f', because it honours spaces in filenames.
# @param $1 If `-d', complete only on directories. Otherwise filter/pick only
@@ -639,7 +556,7 @@ _filedir()
local quoted tmp
_quote_readline_by_ref "$cur" quoted
- toks=( ${toks[@]-} $(
+ toks=( $(
compgen -d -- "$quoted" | {
while read -r tmp; do
# TODO: I have removed a "[ -n $tmp ] &&" before 'printf ..',
@@ -654,19 +571,26 @@ _filedir()
if [[ "$1" != -d ]]; then
# Munge xspec to contain uppercase version too
- [[ ${BASH_VERSINFO[0]} -ge 4 ]] && \
- xspec=${1:+"!*.@($1|${1^^})"} || \
- xspec=${1:+"!*.@($1|$(printf %s $1 | tr '[:lower:]' '[:upper:]'))"}
- toks=( ${toks[@]-} $( compgen -f -X "$xspec" -- $quoted) )
+ # http://thread.gmane.org/gmane.comp.shells.bash.bugs/15294/focus=15306
+ xspec=${1:+"!*.@($1|${1^^})"}
+ toks+=( $( compgen -f -X "$xspec" -- $quoted ) )
fi
- [ ${#toks[@]} -ne 0 ] && _compopt_o_filenames
- COMPREPLY=( "${COMPREPLY[@]}" "${toks[@]}" )
+ # If the filter failed to produce anything, try without it if configured to
+ [[ -n ${COMP_FILEDIR_FALLBACK:-} && \
+ -n "$1" && "$1" != -d && ${#toks[@]} -lt 1 ]] && \
+ toks+=( $( compgen -f -- $quoted ) )
+
+ [ ${#toks[@]} -ne 0 ] && compopt -o filenames 2>/dev/null
+
+ COMPREPLY+=( "${toks[@]}" )
} # _filedir()
# This function splits $cur=--foo=bar into $prev=--foo, $cur=bar, making it
# easier to support both "--foo bar" and "--foo=bar" style completions.
+# `=' should have been removed from COMP_WORDBREAKS when setting $cur for
+# this to be useful.
# Returns 0 if current option was split, 1 otherwise.
#
_split_longopt()
@@ -682,31 +606,186 @@ _split_longopt()
return 1
}
-# This function tries to parse the help output of the given command.
+# Initialize completion and deal with various general things: do file
+# and variable completion where appropriate, and adjust prev, words,
+# and cword as if no redirections exist so that completions do not
+# need to deal with them. Before calling this function, make sure
+# cur, prev, words, and cword are local, ditto split if you use -s.
+#
+# Options:
+# -n EXCLUDE Passed to _get_comp_words_by_ref -n with redirection chars
+# -e XSPEC Passed to _filedir as first arg for stderr redirections
+# -o XSPEC Passed to _filedir as first arg for other output redirections
+# -i XSPEC Passed to _filedir as first arg for stdin redirections
+# -s Split long options with _split_longopt, implies -n =
+# @return True (0) if completion needs further processing,
+# False (> 0) no further processing is necessary.
+#
+_init_completion()
+{
+ local exclude flag outx errx inx OPTIND=1
+
+ while getopts "n:e:o:i:s" flag "$@"; do
+ case $flag in
+ n) exclude+=$OPTARG ;;
+ e) errx=$OPTARG ;;
+ o) outx=$OPTARG ;;
+ i) inx=$OPTARG ;;
+ s) split=false ; exclude+== ;;
+ esac
+ done
+
+ # For some reason completion functions are not invoked at all by
+ # bash (at least as of 4.1.7) after the command line contains an
+ # ampersand so we don't get a chance to deal with redirections
+ # containing them, but if we did, hopefully the below would also
+ # do the right thing with them...
+
+ COMPREPLY=()
+ local redir="@(?([0-9])<|?([0-9&])>?(>)|>&)"
+ _get_comp_words_by_ref -n "$exclude<>&" cur prev words cword
+
+ # Complete variable names.
+ if [[ $cur =~ ^(\$\{?)([A-Za-z0-9_]*)$ ]]; then
+ [[ $cur == *{* ]] && local suffix=} || local suffix=
+ COMPREPLY=( $( compgen -P ${BASH_REMATCH[1]} -S "$suffix" -v -- \
+ "${BASH_REMATCH[2]}" ) )
+ return 1
+ fi
+
+ # Complete on files if current is a redirect possibly followed by a
+ # filename, e.g. ">foo", or previous is a "bare" redirect, e.g. ">".
+ if [[ $cur == $redir* || $prev == $redir ]]; then
+ local xspec
+ case $cur in
+ 2'>'*) xspec=$errx ;;
+ *'>'*) xspec=$outx ;;
+ *'<'*) xspec=$inx ;;
+ *)
+ case $prev in
+ 2'>'*) xspec=$errx ;;
+ *'>'*) xspec=$outx ;;
+ *'<'*) xspec=$inx ;;
+ esac
+ ;;
+ esac
+ cur="${cur##$redir}"
+ _filedir $xspec
+ return 1
+ fi
+
+ # Remove all redirections so completions don't have to deal with them.
+ local i skip
+ for (( i=1; i < ${#words[@]}; )); do
+ if [[ ${words[i]} == $redir* ]]; then
+ # 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 ))
+ else
+ i=$(( ++i ))
+ fi
+ done
+
+ [[ $cword -eq 0 ]] && return 1
+ prev=${words[cword-1]}
+
+ [[ $split ]] && _split_longopt && split=true
+
+ return 0
+}
+
+# Helper function for _parse_help and _parse_usage.
+__parse_options()
+{
+ local option option2 i IFS=$' \t\n,/|'
+
+ # Take first found long option, or first one (short) if not found.
+ option=
+ for i in $1; do
+ case $i in
+ ---*) break ;;
+ --?*) option=$i ; break ;;
+ -?*) [[ $option ]] || option=$i ;;
+ *) break ;;
+ esac
+ done
+ [[ $option ]] || return 0
+
+ IFS=$' \t\n' # affects parsing of the regexps below...
+
+ # Expand --[no]foo to --foo and --nofoo etc
+ if [[ $option =~ (\[((no|dont)-?)\]). ]]; then
+ option2=${option/"${BASH_REMATCH[1]}"/}
+ option2=${option2%%[<{().[]*}
+ printf '%s\n' "${option2/=*/=}"
+ option=${option/"${BASH_REMATCH[1]}"/"${BASH_REMATCH[2]}"}
+ fi
+
+ option=${option%%[<{().[]*}
+ printf '%s\n' "${option/=*/=}"
+}
+
+# Parse GNU style help output of the given command.
# @param $1 command
# @param $2 command options (default: --help)
#
-_parse_help() {
- $1 ${2:---help} 2>&1 | sed -e '/^[[:space:]]*-/!d' -e 's|[,/]| |g' | \
- awk '{ print $1; if ($2 ~ /^-/) { print $2 } }' | sed -e 's|[<=].*||'
+_parse_help()
+{
+ eval local cmd=$1
+ local line
+ "$cmd" ${2:---help} 2>&1 | while read -r line; do
+
+ [[ $line == *([ $'\t'])-* ]] || continue
+ # transform "-f FOO, --foo=FOO" to "-f , --foo=FOO" etc
+ while [[ $line =~ \
+ ((^|[^-])-[A-Za-z0-9?][[:space:]]+)\[?[A-Z0-9]+\]? ]]; do
+ line=${line/"${BASH_REMATCH[0]}"/"${BASH_REMATCH[1]}"}
+ done
+ __parse_options "${line// or /, }"
+
+ done
}
-# This function completes on signal names
+# Parse BSD style usage output (options in brackets) of the given command.
+# @param $1 command
+# @param $2 command options (default: --usage)
#
-_signals()
+_parse_usage()
{
- local i
+ eval local cmd=$1
+ local line match option i char
+ "$cmd" ${2:---usage} 2>&1 | while read -r line; do
+
+ while [[ $line =~ \[[[:space:]]*(-[^]]+)[[:space:]]*\] ]]; do
+ match=${BASH_REMATCH[0]}
+ option=${BASH_REMATCH[1]}
+ case $option in
+ -?(\[)+([a-zA-Z0-9?]))
+ # Treat as bundled short options
+ for (( i=1; i < ${#option}; i++ )); do
+ char=${option:i:1}
+ [[ $char != '[' ]] && printf '%s\n' -$char
+ done
+ ;;
+ *)
+ __parse_options "$option"
+ ;;
+ esac
+ line=${line#*"$match"}
+ done
- # standard signal completion is rather braindead, so we need
- # to hack around to get what we want here, which is to
- # complete on a dash, followed by the signal name minus
- # the SIG prefix
- COMPREPLY=( $( compgen -A signal SIG${cur#-} ))
- for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do
- COMPREPLY[i]=-${COMPREPLY[i]#SIG}
done
}
+# This function completes on signal names (minus the SIG prefix)
+# @param $1 prefix
+_signals()
+{
+ local -a sigs=( $( compgen -P "$1" -A signal "SIG${cur#$1}" ) )
+ COMPREPLY+=( "${sigs[@]/#${1}SIG/${1}}" )
+}
+
# This function completes on known mac addresses
#
_mac_addresses()
@@ -715,16 +794,16 @@ _mac_addresses()
local PATH="$PATH:/sbin:/usr/sbin"
# Local interfaces (Linux only?)
- COMPREPLY=( "${COMPREPLY[@]}" $( ifconfig -a 2>/dev/null | sed -ne \
+ COMPREPLY+=( $( ifconfig -a 2>/dev/null | sed -ne \
"s/.*[[:space:]]HWaddr[[:space:]]\{1,\}\($re\)[[:space:]]*$/\1/p" ) )
# ARP cache
- COMPREPLY=( "${COMPREPLY[@]}" $( arp -an 2>/dev/null | sed -ne \
+ COMPREPLY+=( $( arp -an 2>/dev/null | sed -ne \
"s/.*[[:space:]]\($re\)[[:space:]].*/\1/p" -ne \
"s/.*[[:space:]]\($re\)[[:space:]]*$/\1/p" ) )
# /etc/ethers
- COMPREPLY=( "${COMPREPLY[@]}" $( sed -ne \
+ COMPREPLY+=( $( sed -ne \
"s/^[[:space:]]*\($re\)[[:space:]].*/\1/p" /etc/ethers 2>/dev/null ) )
COMPREPLY=( $( compgen -W '${COMPREPLY[@]}' -- "$cur" ) )
@@ -757,6 +836,17 @@ _configured_interfaces()
fi
}
+# Local IP addresses.
+#
+_ip_addresses()
+{
+ COMPREPLY+=( $( compgen -W \
+ "$( PATH="$PATH:/sbin" ifconfig -a |
+ sed -ne 's/.*addr:\([^[:space:]]*\).*/\1/p' \
+ -ne 's/.*inet[[:space:]]\{1,\}\([^[:space:]]*\).*/\1/p' )" \
+ -- "$cur" ) )
+}
+
# This function completes on available kernels
#
_kernel_versions()
@@ -790,14 +880,14 @@ _available_interfaces()
# @return True (0) if completion needs further processing,
# False (> 0) if tilde is followed by a valid username, completions
# are put in COMPREPLY and no further processing is necessary.
-_tilde() {
+_tilde()
+{
local result=0
- # Does $1 start with tilde (~) and doesn't contain slash (/)?
- if [[ ${1:0:1} == "~" && $1 == ${1//\/} ]]; then
- _compopt_o_filenames
- # Try generate username completions
+ if [[ $1 == ~* && $1 != */* ]]; then
+ # Try generate ~username completions
COMPREPLY=( $( compgen -P '~' -u "${1#\~}" ) )
result=${#COMPREPLY[@]}
+ [ $result -gt 0 ] && compopt -o filenames 2>/dev/null
fi
return $result
}
@@ -825,7 +915,8 @@ _tilde() {
# ~foo/* /home/foo/*
#
# @param $1 Name of variable (not the value of the variable) to expand
-__expand_tilde_by_ref() {
+__expand_tilde_by_ref()
+{
# Does $1 start with tilde (~)?
if [ "${!1:0:1}" = "~" ]; then
# Does $1 contain slash (/)?
@@ -850,7 +941,7 @@ __expand_tilde_by_ref() {
_expand()
{
# FIXME: Why was this here?
- #[ "$cur" != "${cur%\\}" ] && cur="$cur\\"
+ #[ "$cur" != "${cur%\\}" ] && cur+="\\"
# Expand ~username type directory specifications. We want to expand
# ~foo/... to /home/foo/... to avoid problems when $cur starting with
@@ -868,7 +959,7 @@ _expand()
# This function completes on process IDs.
# AIX and Solaris ps prefers X/Open syntax.
-[[ $UNAME == SunOS || $UNAME == AIX ]] &&
+[[ $OSTYPE == *@(solaris|aix)* ]] &&
_pids()
{
COMPREPLY=( $( compgen -W '$( command ps -efo pid | sed 1d )' -- "$cur" ))
@@ -880,7 +971,7 @@ _pids()
# This function completes on process group IDs.
# AIX and SunOS prefer X/Open, all else should be BSD.
-[[ $UNAME == SunOS || $UNAME == AIX ]] &&
+[[ $OSTYPE == *@(solaris|aix)* ]] &&
_pgids()
{
COMPREPLY=( $( compgen -W '$( command ps -efo pgid | sed 1d )' -- "$cur" ))
@@ -892,7 +983,7 @@ _pgids()
# This function completes on process names.
# AIX and SunOS prefer X/Open, all else should be BSD.
-[[ $UNAME == SunOS || $UNAME == AIX ]] &&
+[[ $OSTYPE == *@(solaris|aix)* ]] &&
_pnames()
{
COMPREPLY=( $( compgen -X '<defunct>' -W '$( command ps -efo comm | \
@@ -942,6 +1033,10 @@ _gids()
fi
}
+# Glob for matching various backup files.
+#
+_backup_glob='@(#*#|*@(~|.@(bak|orig|rej|swp|dpkg*|rpm@(orig|new|save))))'
+
# This function completes on services
#
_services()
@@ -949,14 +1044,20 @@ _services()
local sysvdir famdir
[ -d /etc/rc.d/init.d ] && sysvdir=/etc/rc.d/init.d || sysvdir=/etc/init.d
famdir=/etc/xinetd.d
- COMPREPLY=( $( printf '%s\n' \
- $sysvdir/!(*.rpm@(orig|new|save)|*~|functions) ) )
+
+ local restore_nullglob=$(shopt -p nullglob); shopt -s nullglob
+
+ COMPREPLY=( $( printf '%s\n' $sysvdir/!($_backup_glob|functions) ) )
if [ -d $famdir ]; then
- COMPREPLY=( "${COMPREPLY[@]}" $( printf '%s\n' \
- $famdir/!(*.rpm@(orig|new|save)|*~) ) )
+ COMPREPLY+=( $( printf '%s\n' $famdir/!($_backup_glob) ) )
fi
+ $restore_nullglob
+
+ COMPREPLY+=( $( systemctl list-units --full --all 2>/dev/null | \
+ awk '$1 ~ /\.service$/ { sub("\\.service$", "", $1); print $1 }' ) )
+
COMPREPLY=( $( compgen -W '${COMPREPLY[@]#@($sysvdir|$famdir)/}' -- "$cur" ) )
}
@@ -1059,9 +1160,10 @@ _allowed_groups()
#
_shells()
{
- COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W \
- '$( command grep "^[[:space:]]*/" /etc/shells 2>/dev/null )' \
- -- "$cur" ) )
+ local shell rest
+ while read -r shell rest; do
+ [[ $shell == /* && $shell == "$cur"* ]] && COMPREPLY+=( $shell )
+ done 2>/dev/null < /etc/shells
}
# This function completes on valid filesystem types
@@ -1084,7 +1186,7 @@ _fstypes()
fi
[ -n "$fss" ] && \
- COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W "$fss" -- "$cur" ) )
+ COMPREPLY+=( $( compgen -W "$fss" -- "$cur" ) )
}
# Get real command.
@@ -1140,7 +1242,7 @@ _count_args()
#
_pci_ids()
{
- COMPREPLY=( ${COMPREPLY[@]:-} $( compgen -W \
+ COMPREPLY+=( $( compgen -W \
"$( PATH="$PATH:/sbin" lspci -n | awk '{print $3}')" -- "$cur" ) )
}
@@ -1148,38 +1250,43 @@ _pci_ids()
#
_usb_ids()
{
- COMPREPLY=( ${COMPREPLY[@]:-} $( compgen -W \
+ COMPREPLY+=( $( compgen -W \
"$( PATH="$PATH:/sbin" lsusb | awk '{print $6}' )" -- "$cur" ) )
}
# CD device names
_cd_devices()
{
- COMPREPLY=( "${COMPREPLY[@]}"
- $( compgen -f -d -X "!*/?([amrs])cd*" -- "${cur:-/dev/}" ) )
+ COMPREPLY+=( $( compgen -f -d -X "!*/?([amrs])cd*" -- "${cur:-/dev/}" ) )
}
# DVD device names
_dvd_devices()
{
- COMPREPLY=( "${COMPREPLY[@]}"
- $( compgen -f -d -X "!*/?(r)dvd*" -- "${cur:-/dev/}" ) )
+ COMPREPLY+=( $( compgen -f -d -X "!*/?(r)dvd*" -- "${cur:-/dev/}" ) )
}
-# start of section containing completion functions for external programs
+# TERM environment variable values
+_terms()
+{
+ COMPREPLY+=( $( compgen -W \
+ "$( 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
-[ $UNAME = FreeBSD ] && complete -W 'index search fetch fetch-list extract \
- patch configure build install reinstall deinstall clean clean-depends \
- kernel buildworld' make
+[[ $OSTYPE == *freebsd* ]] && complete -W 'index search fetch fetch-list
+ extract patch configure build install reinstall deinstall clean
+ clean-depends kernel buildworld' make
# This function provides simple user@host completion
#
-_user_at_host() {
- local cur
-
- COMPREPLY=()
- _get_comp_words_by_ref -n : cur
+_user_at_host()
+{
+ local cur prev words cword
+ _init_completion -n : || return
if [[ $cur == *@* ]]; then
_known_hosts_real "$cur"
@@ -1195,14 +1302,15 @@ shopt -u hostcomplete && complete -F _user_at_host -o nospace talk ytalk finger
# `_known_hosts_real' instead.
_known_hosts()
{
- local options
- COMPREPLY=()
+ local cur prev words cword
+ _init_completion -n : || return
# NOTE: Using `_known_hosts' as a helper function and passing options
# to `_known_hosts' is deprecated: Use `_known_hosts_real' instead.
+ local options
[[ "$1" == -a || "$2" == -a ]] && options=-a
- [[ "$1" == -c || "$2" == -c ]] && options="$options -c"
- _known_hosts_real $options "$(_get_cword :)"
+ [[ "$1" == -c || "$2" == -c ]] && options+=" -c"
+ _known_hosts_real $options -- "$cur"
} # _known_hosts()
# Helper function for completing _known_hosts.
@@ -1243,11 +1351,11 @@ _known_hosts_real()
# ssh config files
if [ -n "$configfile" ]; then
[ -r "$configfile" ] &&
- config=( "${config[@]}" "$configfile" )
+ config+=( "$configfile" )
else
for i in /etc/ssh/ssh_config "${HOME}/.ssh/config" \
"${HOME}/.ssh2/config"; do
- [ -r $i ] && config=( "${config[@]}" "$i" )
+ [ -r $i ] && config+=( "$i" )
done
fi
@@ -1265,7 +1373,7 @@ _known_hosts_real()
i=${i//\"}
# Eval/expand possible `~' or `~user'
__expand_tilde_by_ref i
- [ -r "$i" ] && kh=( "${kh[@]}" "$i" )
+ [ -r "$i" ] && kh+=( "$i" )
done
IFS=$OIFS
fi
@@ -1275,10 +1383,10 @@ _known_hosts_real()
for i in /etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts2 \
/etc/known_hosts /etc/known_hosts2 ~/.ssh/known_hosts \
~/.ssh/known_hosts2; do
- [ -r $i ] && kh=( "${kh[@]}" $i )
+ [ -r $i ] && kh+=( $i )
done
for i in /etc/ssh2/knownhosts ~/.ssh2/hostkeys; do
- [ -d $i ] && khd=( "${khd[@]}" $i/*pub )
+ [ -d $i ] && khd+=( $i/*pub )
done
fi
@@ -1305,7 +1413,7 @@ _known_hosts_real()
if [ ${#kh[@]} -gt 0 ]; then
# FS needs to look for a comma separated list
- COMPREPLY=( "${COMPREPLY[@]}" $( awk 'BEGIN {FS=","}
+ COMPREPLY+=( $( awk 'BEGIN {FS=","}
/^\s*[^|\#]/ {for (i=1; i<=2; ++i) { \
sub(" .*$", "", $i); \
sub("^\\[", "", $i); sub("\\](:[0-9]+)?$", "", $i); \
@@ -1321,7 +1429,7 @@ _known_hosts_real()
if [[ "$i" == *key_22_$curd*.pub && -r "$i" ]]; then
host=${i/#*key_22_/}
host=${host/%.pub/}
- COMPREPLY=( "${COMPREPLY[@]}" $host )
+ COMPREPLY+=( $host )
fi
done
fi
@@ -1335,7 +1443,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[@]}" )
- COMPREPLY=( "${COMPREPLY[@]}" $( compgen -P "$prefix$user" \
+ COMPREPLY+=( $( compgen -P "$prefix$user" \
-S "$suffix" -W "$hosts" -- "$cur" ) )
fi
@@ -1347,8 +1455,7 @@ _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=( "${COMPREPLY[@]}" $( \
- compgen -P "$prefix$user" -S "$suffix" -W \
+ COMPREPLY+=( $( compgen -P "$prefix$user" -S "$suffix" -W \
"$( avahi-browse -cpr _workstation._tcp 2>/dev/null | \
awk -F';' '/^=/ { print $7 }' | sort -u )" -- "$cur" ) )
fi
@@ -1356,7 +1463,7 @@ _known_hosts_real()
# 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=( "${COMPREPLY[@]}"
+ COMPREPLY+=(
$( compgen -A hostname -P "$prefix$user" -S "$suffix" -- "$cur" ) )
fi
@@ -1364,25 +1471,25 @@ _known_hosts_real()
return 0
} # _known_hosts_real()
-complete -F _known_hosts traceroute traceroute6 tracepath tracepath6 ping \
- ping6 fping fping6 telnet host nslookup rsh rlogin ftp dig mtr \
- ssh-installkeys showmount
+complete -F _known_hosts traceroute traceroute6 tracepath tracepath6 \
+ fping fping6 telnet rsh rlogin ftp dig mtr ssh-installkeys showmount
# This meta-cd function observes the CDPATH variable, so that cd additionally
# completes on directories under those specified in CDPATH.
#
_cd()
{
- local cur IFS=$'\n' i j k
- _get_comp_words_by_ref cur
+ local cur prev words cword
+ _init_completion || return
+ local IFS=$'\n' i j k
# try to allow variable completion
if [[ "$cur" == ?(\\)\$* ]]; then
COMPREPLY=( $( compgen -v -P '$' -- "${cur#?(\\)$}" ) )
return 0
fi
- _compopt_o_filenames
+ compopt -o filenames
# Use standard dir completion if no CDPATH or parameter starts with /,
# ./ or ../
@@ -1400,7 +1507,7 @@ _cd()
k="${#COMPREPLY[@]}"
for j in $( compgen -d $i/$cur ); do
if [[ ( $mark_symdirs && -h $j || $mark_dirs && ! -h $j ) && ! -d ${j#$i/} ]]; then
- j="${j}/"
+ j+="/"
fi
COMPREPLY[k++]=${j#$i/}
done
@@ -1441,22 +1548,17 @@ _command()
# A meta-command completion function for commands like sudo(8), which need to
# first complete on a command, then complete according to that command's own
-# completion definition - currently not quite foolproof (e.g. mount and umount
-# don't work properly), but still quite useful.
+# completion definition.
#
_command_offset()
{
- local cur func cline cspec noglob cmd i char_offset word_offset \
- _COMMAND_FUNC _COMMAND_FUNC_ARGS
-
- word_offset=$1
-
# rewrite current completion context before invoking
# actual command completion
# find new first word position, then
# rewrite COMP_LINE and adjust COMP_POINT
- local first_word=${COMP_WORDS[$word_offset]}
+ local word_offset=$1
+ local first_word=${COMP_WORDS[$word_offset]} char_offset i
for (( i=0; i <= ${#COMP_LINE}; i++ )); do
if [[ "${COMP_LINE:$i:${#first_word}}" == "$first_word" ]]; then
char_offset=$i
@@ -1471,25 +1573,47 @@ _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=$(( $COMP_CWORD - $word_offset ))
COMPREPLY=()
+ local cur
_get_comp_words_by_ref cur
if [[ $COMP_CWORD -eq 0 ]]; then
- _compopt_o_filenames
- COMPREPLY=( $( compgen -c -- "$cur" ) )
+ local IFS=$'\n'
+ compopt -o filenames
+ COMPREPLY=( $( compgen -d -c -- "$cur" ) )
else
- cmd=${COMP_WORDS[0]}
- if complete -p ${cmd##*/} &>/dev/null; then
- cspec=$( complete -p ${cmd##*/} )
+ local cmd=${COMP_WORDS[0]} compcmd=${COMP_WORDS[0]}
+ local cspec=$( complete -p $cmd 2>/dev/null )
+ if [[ ! $cspec ]]; then
+ if [[ $cmd == */* ]]; then
+ # Load completion for full path
+ _completion_loader $cmd
+ if [[ $? -eq 124 ]]; then
+ # Success, but we may now have the full path completion...
+ cspec=$( complete -p $cmd 2>/dev/null )
+ if [[ ! $cspec ]]; then
+ # ...or just the basename one.
+ compcmd=${cmd##*/}
+ cspec=$( complete -p $compcmd 2>/dev/null )
+ fi
+ fi
+ else
+ # Simple, non-full path case.
+ _completion_loader $cmd
+ [[ $? -eq 124 ]] && cspec=$( complete -p $cmd 2>/dev/null )
+ fi
+ fi
+
+ if [[ -n $cspec ]]; then
if [ "${cspec#* -F }" != "$cspec" ]; then
# complete -F <function>
# get function name
- func=${cspec#*-F }
+ local func=${cspec#*-F }
func=${func%% *}
if [[ ${#COMP_WORDS[@]} -ge 2 ]]; then
@@ -1498,25 +1622,25 @@ _command_offset()
$func $cmd "${COMP_WORDS[${#COMP_WORDS[@]}-1]}"
fi
- # remove any \: generated by a command that doesn't
- # default to filenames or dirnames (e.g. sudo chown)
- # FIXME: I'm pretty sure this does not work!
- if [ "${cspec#*-o }" != "$cspec" ]; then
- cspec=${cspec#*-o }
- cspec=${cspec%% *}
- if [[ "$cspec" != @(dir|file)names ]]; then
- COMPREPLY=("${COMPREPLY[@]//\\\\:/:}")
- else
- _compopt_o_filenames
+ # restore initial compopts
+ local opt t
+ while true; do
+ # FIXME: should we take "+o opt" into account?
+ t=${cspec#*-o }
+ if [ "$t" == "$cspec" ]; then
+ break
fi
- fi
- elif [ -n "$cspec" ]; then
- cspec=${cspec#complete};
- cspec=${cspec%%${cmd##*/}};
- COMPREPLY=( $( eval compgen "$cspec" -- "$cur" ) );
+ opt=${t%% *}
+ compopt -o $opt
+ cspec=${t#$opt}
+ done
+ else
+ cspec=${cspec#complete}
+ cspec=${cspec%%$compcmd}
+ COMPREPLY=( $( eval compgen "$cspec" -- "$cur" ) )
fi
elif [ ${#COMPREPLY[@]} -eq 0 ]; then
- _filedir
+ _minimal
fi
fi
}
@@ -1539,28 +1663,44 @@ _complete_as_root()
_longopt()
{
- local cur prev split=false
- _get_comp_words_by_ref -n = cur prev
+ local cur prev words cword split
+ _init_completion -s || return
- _split_longopt && split=true
-
- case "$prev" in
- --*[Dd][Ii][Rr]*)
+ case "${prev,,}" in
+ --help|--usage|--version)
+ return 0
+ ;;
+ --*dir*)
_filedir -d
return 0
;;
- --*[Ff][Ii][Ll][Ee]*|--*[Pp][Aa][Tt][Hh]*)
+ --*file*|--*path*)
_filedir
return 0
;;
+ --+([-a-z0-9_]))
+ local argtype=$( $1 --help 2>&1 | sed -ne \
+ "s|.*$prev\[\{0,1\}=[<[]\{0,1\}\([-A-Za-z0-9_]\{1,\}\).*|\1|p" )
+ case ${argtype,,} in
+ *dir*)
+ _filedir -d
+ return 0
+ ;;
+ *file*|*path*)
+ _filedir
+ return 0
+ ;;
+ esac
+ ;;
esac
$split && return 0
if [[ "$cur" == -* ]]; then
COMPREPLY=( $( compgen -W "$( $1 --help 2>&1 | \
- sed -ne 's/.*\(--[-A-Za-z0-9]\{1,\}\).*/\1/p' | sort -u )" \
+ 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
_filedir -d
else
@@ -1568,37 +1708,25 @@ _longopt()
fi
}
# makeinfo and texi2dvi are defined elsewhere.
-for i in a2ps awk bash bc bison cat colordiff cp csplit \
- curl cut date df diff dir du enscript env expand fmt fold gperf gprof \
+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 \
mv netstat nl nm objcopy objdump od paste patch pr ptx readelf rm rmdir \
- sed seq sha{,1,224,256,384,512}sum shar sort split strip tac tail tee \
- texindex touch tr uname unexpand uniq units vdir wc wget who; do
- have $i && complete -F _longopt -o default $i
-done
-unset i
+ 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 wget who
+declare -A _xspecs
_filedir_xspec()
{
- local IFS cur xspec
-
- IFS=$'\n'
- COMPREPLY=()
- _get_comp_words_by_ref cur
+ local cur prev words cword
+ _init_completion || return
_expand || return 0
- # get first exclusion compspec that matches this command
- xspec=$( awk "/^complete[ \t]+.*[ \t]${1##*/}([ \t]|\$)/ { print \$0; exit }" \
- "$BASH_COMPLETION" )
- # prune to leave nothing but the -X spec
- xspec=${xspec#*-X }
- xspec=${xspec%% *}
-
+ local IFS=$'\n' xspec=${_xspecs[${1##*/}]} tmp
local -a toks
- local tmp
- toks=( ${toks[@]-} $(
+ toks=( $(
compgen -d -- "$(quote_readline "$cur")" | {
while read -r tmp; do
# see long TODO comment in _filedir() --David
@@ -1608,17 +1736,16 @@ _filedir_xspec()
))
# Munge xspec to contain uppercase version too
+ # http://thread.gmane.org/gmane.comp.shells.bash.bugs/15294/focus=15306
eval xspec="${xspec}"
local matchop=!
if [[ $xspec == !* ]]; then
xspec=${xspec#!}
matchop=@
fi
- [[ ${BASH_VERSINFO[0]} -ge 4 ]] && \
- xspec="$matchop($xspec|${xspec^^})" || \
- xspec="$matchop($xspec|$(printf %s $xspec | tr '[:lower:]' '[:upper:]'))"
+ xspec="$matchop($xspec|${xspec^^})"
- toks=( ${toks[@]-} $(
+ toks+=( $(
eval compgen -f -X "!$xspec" -- "\$(quote_readline "\$cur")" | {
while read -r tmp; do
[ -n $tmp ] && printf '%s\n' $tmp
@@ -1626,65 +1753,140 @@ _filedir_xspec()
}
))
- [ ${#toks[@]} -ne 0 ] && _compopt_o_filenames
+ [ ${#toks[@]} -ne 0 ] && compopt -o filenames
COMPREPLY=( "${toks[@]}" )
}
-list=( $( sed -ne '/^# START exclude/,/^# FINISH exclude/p' "$BASH_COMPLETION" | \
- # read exclusion compspecs
- (
- while read line
- do
- # ignore compspecs that are commented out
- if [ "${line#\#}" != "$line" ]; then continue; fi
- line=${line%# START exclude*}
- line=${line%# FINISH exclude*}
- line=${line##*\'}
- list=( "${list[@]}" $line )
+
+_install_xspec()
+{
+ local xspec=$1 cmd
+ shift
+ for cmd in $@; do
+ _xspecs[$cmd]=$xspec
done
- printf '%s ' "${list[@]}"
- )
- ) )
-# remove previous compspecs
-if [ ${#list[@]} -gt 0 ]; then
- eval complete -r ${list[@]}
- # install new compspecs
- eval complete -F _filedir_xspec "${list[@]}"
-fi
-unset list
+ complete -F _filedir_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|sxw|o[tx]t|od[fgpst]|epub|apk)' 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' uncompress
+# lzcmp, lzdiff intentionally not here, see Debian: #455510
+_install_xspec '!*.@(tlz|lzma)' lzcat lzegrep lzfgrep lzgrep lzless lzmore unlzma
+_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)' xv qiv
+_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 '!*.@(?(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 '!*.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 '!*.@(avi|asf|wmv)' aviplay
+_install_xspec '!*.@(rm?(j)|ra?(m)|smi?(l))' realplay
+_install_xspec '!*.@(mpg|mpeg|avi|mov|qt)' xanim
+_install_xspec '!*.@(ogg|m3u|flac|spx)' ogg123
+_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 '!*.@([eE][xX][eE]?(.[sS][oO])|[cC][oO][mM]|[sS][cC][rR])' wine
+_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 opera 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 '!*.@(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
+_install_xspec '!*.@(sxm|smf|mml|odf)' oomath
+_install_xspec '!*.odb' oobase
+_install_xspec '!*.[rs]pm' rpm2cpio
+_install_xspec '!*.aux' bibtex
+_install_xspec '!*.po' poedit gtranslator kbabel lokalize
+_install_xspec '!*.@([Pp][Rr][Gg]|[Cc][Ll][Pp])' harbour gharbour hbpp
+_install_xspec '!*.[Hh][Rr][Bb]' hbrun
+_install_xspec '!*.ly' lilypond ly2dvi
+_install_xspec '!*.@(dif?(f)|?(d)patch)?(.@([gx]z|bz2|lzma))' cdiff
+_install_xspec '!*.lyx' lyx
+_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
+unset -f _install_xspec
+
+# Minimal completion to use as fallback in _completion_loader.
+_minimal()
+{
+ local cur prev words cword split
+ _init_completion -s || return
+ $split && return
+ _filedir
+}
-# source completion directory definitions
+# set up dynamic completion loading
+_completion_loader()
+{
+ local compdir=./completions
+ [[ $BASH_SOURCE == */* ]] && compdir="${BASH_SOURCE%/*}/completions"
+
+ # Special case for init.d scripts.
+ if [[ $1 == /etc?(/rc.d)/init.d/* ]]; then
+ . "$compdir/service" &>/dev/null && return 124 || return 1
+ fi
+
+ # Try basename.
+ . "$compdir/${1##*/}" &>/dev/null && return 124
+
+ # Need to define *something*, otherwise there will be no completion at all.
+ complete -F _minimal "$1" && return 124
+} &&
+complete -D -F _completion_loader
+
+# Function for loading and calling functions from dynamically loaded
+# completion files that may not have been sourced yet.
+# @param $1 completion file to load function from in case it is missing
+# @param $2... function and its arguments
+_xfunc()
+{
+ set -- "$@"
+ local srcfile=$1
+ shift
+ declare -F $1 &>/dev/null || {
+ local compdir=./completions
+ [[ $BASH_SOURCE == */* ]] && compdir="${BASH_SOURCE%/*}/completions"
+ . "$compdir/$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
- [[ ${i##*/} != @(*~|*.bak|*.swp|\#*\#|*.dpkg*|*.rpm@(orig|new|save)|Makefile*) \
- && -f $i && -r $i ]] && . "$i"
- done
-fi
-if [[ $BASH_COMPLETION_DIR != $BASH_COMPLETION_COMPAT_DIR && \
- -d $BASH_COMPLETION_DIR && -r $BASH_COMPLETION_DIR && \
- -x $BASH_COMPLETION_DIR ]]; then
- for i in $(LC_ALL=C command ls "$BASH_COMPLETION_DIR"); do
- i=$BASH_COMPLETION_DIR/$i
- [[ ${i##*/} != @(*~|*.bak|*.swp|\#*\#|*.dpkg*|*.rpm@(orig|new|save)|Makefile*) \
+ [[ ${i##*/} != @($_backup_glob|Makefile*|$_blacklist_glob) \
&& -f $i && -r $i ]] && . "$i"
done
fi
unset i
# source user completion file
-[[ $BASH_COMPLETION != ~/.bash_completion && -r ~/.bash_completion ]] \
+[[ ${BASH_SOURCE[0]} != ~/.bash_completion && -r ~/.bash_completion ]] \
&& . ~/.bash_completion
unset -f have
-unset UNAME USERLAND have
+unset have
set $BASH_COMPLETION_ORIGINAL_V_VALUE
unset BASH_COMPLETION_ORIGINAL_V_VALUE
-# Local variables:
-# mode: shell-script
-# sh-basic-offset: 4
-# sh-indent-comment: t
-# indent-tabs-mode: nil
-# End:
# ex: ts=4 sw=4 et filetype=sh