[Bash-completion-commits] [SCM] bash-completion branch, master, updated. 1.3

David Paleino dapal at debian.org
Sun Feb 6 20:13:05 UTC 2011


The following commit has been merged in the master branch:
commit a170d886a293dc24bb03c1ff8872ee313469094a
Merge: 88096a3a1183eef5cf6ee2cc5e8b7d7d4ae18ca3 c6697f91e32fa5c28f20d1ed5faa9451b8ebb6ce
Author: David Paleino <dapal at debian.org>
Date:   Wed Jun 9 07:55:33 2010 +0200

    Merge branch 'master' into 1.x
    
    Conflicts:
    	bash_completion
    	configure.ac

diff --combined bash_completion
index e1c4054,c232989..f02a62d
--- a/bash_completion
+++ b/bash_completion
@@@ -1,9 -1,8 +1,8 @@@
  #
- #   bash_completion - programmable completion functions for bash 3.x
- #             (backwards compatible with bash 2.05b)
+ #   bash_completion - programmable completion functions for bash 3.2+
  #
  #   Copyright © 2006-2008, Ian Macdonald <ian at caliban.org>
- #             © 2009, Bash Completion Maintainers
+ #             © 2009-2010, Bash Completion Maintainers
  #                     <bash-completion-devel at lists.alioth.debian.org>
  #
  #   This program is free software; you can redistribute it and/or modify
@@@ -24,7 -23,7 +23,7 @@@
  #
  #   http://bash-completion.alioth.debian.org/
  #
 -#   RELEASE: 2.x
 +#   RELEASE: 1.1
  
  if [[ $- == *v* ]]; then
      BASH_COMPLETION_ORIGINAL_V_VALUE="-v"
@@@ -56,33 -55,6 +55,6 @@@ case ${UNAME} i
      *) USERLAND=${UNAME} ;;
  esac
  
- # features supported by bash 2.05 and higher
- if [ ${BASH_VERSINFO[0]} -eq 2 ] && [[ ${BASH_VERSINFO[1]} > 04 ]] ||
-     [ ${BASH_VERSINFO[0]} -gt 2 ]; then
-     declare -r bash205=$BASH_VERSION 2>/dev/null || :
-     default="-o default"
-     dirnames="-o dirnames"
-     filenames="-o filenames"
-     compopt=:
- fi
- # features supported by bash 2.05b and higher
- if [ ${BASH_VERSINFO[0]} -eq 2 ] && [[ ${BASH_VERSINFO[1]} = "05b" ]] ||
-     [ ${BASH_VERSINFO[0]} -gt 2 ]; then
-     declare -r bash205b=$BASH_VERSION 2>/dev/null || :
-     nospace="-o nospace"
- fi
- # features supported by bash 3.0 and higher
- if [ ${BASH_VERSINFO[0]} -gt 2 ]; then
-     declare -r bash3=$BASH_VERSION 2>/dev/null || :
-     bashdefault="-o bashdefault"
-     plusdirs="-o plusdirs"
- fi
- # features supported by bash 4.0 and higher
- if [ ${BASH_VERSINFO[0]} -gt 3 ]; then
-     declare -r bash4=$BASH_VERSION 2>/dev/null || :
-     compopt=compopt
- fi
- 
  # Turn on extended globbing and programmable completion
  shopt -s extglob progcomp
  
@@@ -97,11 -69,11 +69,11 @@@ complete -d push
  #
  # 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
- complete -f -X '!*.@(zip|ZIP|jar|JAR|exe|EXE|pk3|war|wsz|ear|zargo|xpi|sxw|ott|od[fgpst]|epub)' unzip zipinfo
+ complete -f -X '!*.?(t)bz?(2)' bunzip2 bzcat pbunzip2 pbzcat
+ complete -f -X '!*.@(zip|ZIP|[ejw]ar|[EJW]AR|exe|EXE|pk3|wsz|zargo|xpi|sxw|o[tx]t|od[fgpst]|epub)' unzip zipinfo
  complete -f -X '*.Z' compress znew
  # zcmp, zdiff, z*grep, zless, zmore intentionally not here, see Debian: #455510
- complete -f -X '!*.@(Z|gz|tgz|Gz|dz)' gunzip zcat
+ 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 '!*.lzma' lzcat lzegrep lzfgrep lzgrep lzless lzmore unlzma
@@@ -114,13 -86,13 +86,13 @@@ complete -f -X '!*.@(dvi|DVI)?(.@(gz|Z|
  complete -f -X '!*.@(dvi|DVI)' dvips dviselect dvitype dvipdf advi dvipdfm dvipdfmx
  complete -f -X '!*.@(pdf|PDF)' acroread gpdf xpdf
  complete -f -X '!*.@(?(e)ps|?(E)PS|pdf|PDF)' kpdf
- complete -f -X '!*.@(@(?(e)ps|?(E)PS|pdf|PDF|dvi|DVI)?(.gz|.GZ|.bz2|.BZ2)|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)' evince
- complete -f -X '!*.@(?(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|FB|mobi|MOBI|g3|G3|chm|CHM)?(.?(gz|GZ|bz2|BZ2))' okular
+ complete -f -X '!*.@(@(?(e)ps|?(E)PS|pdf|PDF|dvi|DVI)?(.gz|.GZ|.bz2|.BZ2)|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|fdf|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|FB|mobi|MOBI|g3|G3|chm|CHM|fdf|FDF)?(.?(gz|GZ|bz2|BZ2)))' okular
  complete -f -X '!*.@(?(e)ps|?(E)PS|pdf|PDF)' ps2pdf ps2pdf12 ps2pdf13 ps2pdf14 ps2pdfwr
  complete -f -X '!*.texi*' makeinfo texi2html
- complete -f -X '!*.@(?(la)tex|?(LA)TEX|texi|TEXI|dtx|DTX|ins|INS)' tex latex slitex jadetex pdfjadetex pdftex pdflatex texi2dvi
+ complete -f -X '!*.@(?(la)tex|?(LA)TEX|texi|TEXI|dtx|DTX|ins|INS|ltx|LTX)' tex latex slitex jadetex pdfjadetex pdftex pdflatex texi2dvi
  complete -f -X '!*.@(mp3|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|viv|rm|ram|yuv|mov|MOV|qt|QT|wmv|mp[234]|MP[234]|m4[pv]|M4[PV]|mkv|MKV|og[gmv]|OG[GMV]|wav|WAV|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))' xine aaxine fbxine kaffeine
+ 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]|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))' xine aaxine fbxine 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
@@@ -129,12 -101,12 +101,12 @@@ complete -f -X '!*.@(mp3|MP3|ogg|OGG|pl
  complete -f -X '!*.fig' xfig
  complete -f -X '!*.@(mid?(i)|MID?(I)|cmf|CMF)' playmidi
  complete -f -X '!*.@(mid?(i)|MID?(I)|rmi|RMI|rcp|RCP|[gr]36|[GR]36|g18|G18|mod|MOD|xm|XM|it|IT|x3m|X3M|s[3t]m|S[3T]M|kar|KAR)' timidity
- complete -f -X '!*.@(m[eo]d|M[EO]D|s[3t]m|S[3T]M|xm|XM|it|IT)' modplugplay
- complete -f -X '*.@(o|so|so.!(conf)|a|rpm|gif|GIF|jp?(e)g|JP?(E)G|mp3|MP3|mp?(e)g|MPG|avi|AVI|asf|ASF|ogg|OGG|class|CLASS)' vi vim gvim rvim view rview rgvim rgview gview
- complete -f -X '*.@(o|so|so.!(conf)|a|rpm|gif|GIF|jp?(e)g|JP?(E)G|mp3|MP3|mp?(e)g|MPG|avi|AVI|asf|ASF|ogg|OGG|class|CLASS)' emacs
- complete -f -X '!*.@(exe|EXE|com|COM|scr|SCR|exe.so)' wine
+ complete -f -X '!*.@(m[eo]d|M[EO]D|s[3t]m|S[3T]M|xm|XM|it|IT)' modplugplay modplug123
+ complete -f -X '*.@(o|so|so.!(conf)|a|rpm|gif|GIF|jp?(e)g|JP?(E)G|mp3|MP3|mp?(e)g|MPG|avi|AVI|asf|ASF|ogg|OGG|class|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|ZIP|z|Z|gz|GZ|tgz|TGZ)' bzme
- complete -f -X '!*.@(?([xX]|[sS])[hH][tT][mM]?([lL]))' netscape mozilla lynx opera galeon curl dillo elinks amaya
+ # 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
@@@ -142,12 -114,15 +114,15 @@@ complete -f -X '!*.@(sxd|std|sda|sdd|od
  complete -f -X '!*.@(sxm|smf|mml|odf)' oomath
  complete -f -X '!*.odb' oobase
  complete -f -X '!*.rpm' rpm2cpio
- complete -f -X '!*.sqlite' sqlite3
+ complete -f -X '!*.s@(qlite?(3)|?(3)db)' sqlite3
  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 '!*.@(dif?(f)|?(d)patch)' kompare
+ complete -f -X '!*.lyx' lyx
  # FINISH exclude -- do not remove this line
  
  # start of section containing compspecs that can be handled within bash
@@@ -193,14 -168,12 +168,12 @@@ complete -b builti
  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"
  }
  
- # use GNU sed if we have it, since its extensions are still used in our code
- #
- [ $USERLAND != GNU ] && have gsed && alias sed=gsed
- 
  # This function checks whether a given readline variable
  # is `on'.
  #
@@@ -215,19 -188,14 +188,14 @@@ quote(
      echo \'${1//\'/\'\\\'\'}\' #'# Help vim syntax highlighting
  }
  
- # This function quotes the argument in a way so that readline dequoting
- # results in the original argument
+ # @see _quote_readline_by_ref()
  quote_readline()
  {
-     if [ -n "$bash4" ] ; then
-         # This function isn't really necessary on bash 4
-         # See: http://lists.gnu.org/archive/html/bug-bash/2009-03/msg00155.html
-         echo "${1}"
-         return
-     fi
-     local t="${1//\\/\\\\}"
-     echo \'${t//\'/\'\\\'\'}\' #'# Help vim syntax highlighting
- }
+     local quoted
+     _quote_readline_by_ref "$1" ret
+     printf %s "$ret"
+ } # quote_readline()
+ 
  
  # This function shell-dequotes the argument
  dequote()
@@@ -235,189 -203,442 +203,442 @@@
      eval echo "$1" 2> /dev/null
  }
  
- # Get the word to complete.
- # This is nicer than ${COMP_WORDS[$COMP_CWORD]}, since it handles cases
- # where the user is completing in the middle of a word.
- # (For example, if the line is "ls foobar",
- # and the cursor is here -------->   ^
- # it will complete just "foo", not "foobar", which is what the user wants.)
- # @param $1 string  (optional) Characters out of $COMP_WORDBREAKS which should
+ 
+ # Reassemble command line words, excluding specified characters from the
+ # list of word completion separators (COMP_WORDBREAKS).
+ # @param $1 chars  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.
- #     NOTE: This parameter only applies to bash-4.
+ #     we want to return host:path and not only path, so we would pass the
+ #     colon (:) as $1 here.
+ # @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
+     # Exclude word separator characters?
+     if [[ $1 ]]; then
+         # Yes, exclude word separator characters;
+         # Exclude only those characters, which were really included
+         exclude="${1//[^$COMP_WORDBREAKS]}"
+     fi
+         
+     # Default to cword unchanged
+     eval $3=$COMP_CWORD
+     # Are characters excluded which were former included?
+     if [[ $exclude ]]; then
+         # Yes, list of word completion separators has shrunk;
+         # 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?
+             while [[ $i -gt 0 && ${COMP_WORDS[$i]} && 
+                 ${COMP_WORDS[$i]//[^$exclude]} == ${COMP_WORDS[$i]} 
+             ]]; do
+                 [ $j -ge 2 ] && ((j--))
+                 # Append word separator to current 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
+                 (( $i < ${#COMP_WORDS[@]} - 1)) && ((i++)) || break 2
+             done
+             # Append word to current word
+             ref="$2[$j]"
+             eval $2[$j]=\${!ref}\${COMP_WORDS[i]}
+             # Indicate new cword
+             [ $i = $COMP_CWORD ] && [[ ${COMP_WORDS[i]} ]] && eval $3=$j
+         done
+     else
+         # No, list of word completions separators hasn't changed;
+         eval $2=\( \"\${COMP_WORDS[@]}\" \)
+     fi
+ } # __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.
+ # @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() {
+     # NOTE: The call to the main function ___get_cword_at_cursor_by_ref() is
+     #       wrapped to make collisions with local variable names less likely.
+     local __words __cword __cur
+     ___get_cword_at_cursor_by_ref "$1" __words __cword __cur
+ 
+     eval $2=\( \"\${__words[@]}\" \)
+     eval $3=\$__cword
+     eval $4=\$__cur
+ }
  
- _get_cword()
- {
-     if [ -n "$bash4" ] ; then
-         __get_cword4 "$@"
+ 
+ # @param $1 exclude
+ # @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
+ # @note  Do not call this function directly but call 
+ #     `__get_cword_at_cursor_by_ref()' instead to make variable name collisions
+ #     less likely
+ # @see __get_cword_at_cursor_by_ref()
+ ___get_cword_at_cursor_by_ref() {
+     local cword words
+     __reassemble_comp_words_by_ref "$1" words cword
+ 
+     local i
+     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
+ 
+     if [[ "${words[cword]:0:${#cur}}" != "$cur" ]]; then
+         # We messed up. At least return the whole word so things keep working
+         eval $4=\"\${words[cword]}\"
      else
-         __get_cword3
+         eval $4=\"\${cur:0:\$index}\"
      fi
- } # _get_cword()
+ 
+     eval $2=\( \"\${words[@]}\" \)
+     eval $3=\$cword
+ }
  
  
- # Get the word to complete on bash-3, where words are not broken by
- # COMP_WORDBREAKS characters and the COMP_CWORD variables look like this, for
- # example:
+ # Get the word to complete and optional previous words.
+ # This is nicer than ${COMP_WORDS[$COMP_CWORD]}, since it handles cases
+ # where the user is completing in the middle of a word.
+ # (For example, if the line is "ls foobar",
+ # and the cursor is here -------->   ^
+ # Also one is able to cross over possible wordbreak characters.
+ # Usage: _get_comp_words_by_ref [OPTIONS] VAR1 [VAR2 [VAR3]]
+ # Example usage:
  #
- #     $ a b:c<TAB>
- #     COMP_CWORD: 1
- #     COMP_CWORDS:
- #     0: a
- #     1: b:c
+ #    $ _get_comp_words_by_ref -n : cur prev
  #
- # See also:
- # _get_cword, main routine
- # __get_cword4, bash-4 variant
+ # Options:  -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.
+ # @see __get_comp_words_by_ref
+ _get_comp_words_by_ref() {
+     # NOTE: The call to the main function __get_comp_words_by_ref() is wrapped
+     #       to make collisions with local variable name less likely.
+     local __words __cword __cur __var __vars 
+     __get_comp_words_by_ref __words __cword __cur __vars "$@"
+     set -- "${__vars[@]}"
+     eval $1=\$__cur
+     shift
+     for __var; do
+         ((__cword--))
+         [[ ${__words[__cword]} ]] && eval $__var=\${__words[__cword]}
+     done
+ }
+ 
+ 
+ # @param $1 words  Name of variable to return words to
+ # @param $2 cword  Name of variable to return cword to
+ # @param $3 cur  Name of variable to return current word to complete to
+ # @param $4 varnames  Name of variable to return array of variable names to
+ # @param $@  Arguments to _get_comp_words_by_ref()
+ # @note  Do not call this function directly but call `_get_comp_words_by_ref()'
+ #     instead to make variable name collisions less likely
+ # @see _get_comp_words_by_ref()
+ __get_comp_words_by_ref()
+ {
+     local exclude flag i OPTIND=5  # Skip first four arguments
+     local cword words cur varnames=()
+     while getopts "n:" flag "$@"; do
+         case $flag in
+             n) exclude=$OPTARG ;;
+         esac
+     done
+     varnames=( ${!OPTIND} )
+     let "OPTIND += 1"
+     while [[ $# -ge $OPTIND ]]; do 
+         varnames+=( ${!OPTIND} )
+         let "OPTIND += 1"
+     done
+ 
+     __get_cword_at_cursor_by_ref "$exclude" words cword cur
+ 
+     eval $1=\( \"\${words[@]}\" \)
+     eval $2=\$cword
+     eval $3=\$cur
+     eval $4=\( \"\${varnames[@]}\" \)
+ }
+ 
+ 
+ # Get the word to complete.
+ # This is nicer than ${COMP_WORDS[$COMP_CWORD]}, since it handles cases
+ # where the user is completing in the middle of a word.
+ # (For example, if the line is "ls foobar",
+ # and the cursor is here -------->   ^
+ # @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.
+ # @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
+ #     the current word, respecting the exclusions "=:".
  #
- __get_cword3()
+ _get_cword()
  {
-     if [[ "${#COMP_WORDS[COMP_CWORD]}" -eq 0 ]] || [[ "$COMP_POINT" == "${#COMP_LINE}" ]]; then
-         printf "%s" "${COMP_WORDS[COMP_CWORD]}"
+     local LC_CTYPE=C
+     local cword words
+     __reassemble_comp_words_by_ref "$1" words cword
+ 
+     # return previous word offset by $2
+     if [[ ${2//[^0-9]/} ]]; then
+         printf "%s" "${words[cword-$2]}"
+     elif [[ "${#words[cword]}" -eq 0 ]] || [[ "$COMP_POINT" == "${#COMP_LINE}" ]]; then
+         printf "%s" "${words[cword]}"
      else
          local i
          local cur="$COMP_LINE"
          local index="$COMP_POINT"
-         for (( i = 0; i <= COMP_CWORD; ++i )); do
+         for (( i = 0; i <= cword; ++i )); do
              while [[
-                 # Current COMP_WORD fits in $cur?
-                 "${#cur}" -ge ${#COMP_WORDS[i]} &&
-                 # $cur doesn't match COMP_WORD?
-                 "${cur:0:${#COMP_WORDS[i]}}" != "${COMP_WORDS[i]}"
-                 ]]; do
+                 # 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="$(( index - 1 ))"
+                 ((index--))
              done
  
-             # Does found COMP_WORD matches COMP_CWORD?
-             if [[ "$i" -lt "$COMP_CWORD" ]]; then
-                 # No, COMP_CWORD lies further;
+             # Does found word matches cword?
+             if [[ "$i" -lt "$cword" ]]; then
+                 # No, cword lies further;
                  local old_size="${#cur}"
-                 cur="${cur#${COMP_WORDS[i]}}"
+                 cur="${cur#${words[i]}}"
                  local new_size="${#cur}"
-                 index="$(( index - old_size + new_size ))"
+                 index=$(( index - old_size + new_size ))
              fi
          done
  
-         if [[ "${COMP_WORDS[COMP_CWORD]:0:${#cur}}" != "$cur" ]]; then
+         if [[ "${words[cword]:0:${#cur}}" != "$cur" ]]; then
              # We messed up! At least return the whole word so things
              # keep working
-             printf "%s" "${COMP_WORDS[COMP_CWORD]}"
+             printf "%s" "${words[cword]}"
          else
              printf "%s" "${cur:0:$index}"
          fi
      fi
- } # __get_cword3()
+ } # _get_cword()
  
  
- # Get the word to complete on bash-4, where words are splitted by
- # COMP_WORDBREAKS characters (default is " \t\n\"'><=;|&(:") and the COMP_CWORD
- # variables look like this, for example:
+ # Get word previous to the current word.
+ # This is a good alternative to `prev=${COMP_WORDS[COMP_CWORD-1]}' because bash4
+ # will properly return the previous word with respect to any given exclusions to
+ # COMP_WORDBREAKS.
+ # @see _get_cword()
  #
- #     $ a b:c<TAB>
- #     COMP_CWORD: 3
- #     COMP_CWORDS:
- #     0: a
- #     1: b
- #     2: :
- #     3: c
+ _get_pword() 
+ {
+     if [ $COMP_CWORD -ge 1 ]; then
+         _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
+ # 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.
+ # The preferred solution is to remove the colon (:) from COMP_WORDBREAKS in
+ # your .bashrc:
  #
- # @oaram $1 string
- # $1 string  (optional) 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.
- # See also:
- # _get_cword, main routine
- # __get_cword3, bash-3 variant
+ #    # Remove colon (:) from list of word completion separators
+ #    COMP_WORDBREAKS=${COMP_WORDBREAKS//:}
  #
- __get_cword4()
- {
-     local i
-     local LC_CTYPE=C
-     local WORDBREAKS=$COMP_WORDBREAKS
-     # Strip single quote (') and double quote (") from WORDBREAKS to
-     # workaround a bug in bash-4.0, where quoted words are split
-     # unintended, see:
-     # http://www.mail-archive.com/bug-bash@gnu.org/msg06095.html
-     # This fixes simple quoting (e.g. $ a "b<TAB> returns "b instead of b)
-     # but still fails quoted spaces (e.g. $ a "b c<TAB> returns c instead
-     # of "b c).
-     WORDBREAKS=${WORDBREAKS//\"/}
-     WORDBREAKS=${WORDBREAKS//\'/}
-     if [ -n "$1" ]; then
-         for (( i=0; i<${#1}; ++i )); do
-             local char=${1:$i:1}
-             WORDBREAKS=${WORDBREAKS//$char/}
+ # See also: Bash FAQ - E13) Why does filename completion misbehave if a colon
+ # appears in the filename? - http://tiswww.case.edu/php/chet/bash/FAQ
+ # @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
+         # Remove colon-word prefix from COMPREPLY items
+         local colon_word=${1%${1##*:}}
+         local i=${#COMPREPLY[*]}
+         while [ $((--i)) -ge 0 ]; do
+             COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"}
          done
      fi
-     local cur=${COMP_LINE:0:$COMP_POINT}
-     local tmp=$cur
-     local word_start=`expr "$tmp" : '.*['"$WORDBREAKS"']'`
-     while [ "$word_start" -ge 2 ]; do
-         # Get character before $word_start
-         local char=${cur:$(( $word_start - 2 )):1}
-         # If the WORDBREAK character isn't escaped, exit loop
-         if [ "$char" != "\\" ]; then
-             break
+ } # __ltrim_colon_completions()
+ 
+ 
+ # This function quotes the argument in a way so that readline dequoting
+ # results in the original argument.  This is necessary for at least
+ # `compgen' which requires its arguments quoted/escaped:
+ #
+ #     $ ls "a'b/"
+ #     c
+ #     $ compgen -f "a'b/"       # Wrong, doesn't return output
+ #     $ compgen -f "a\'b/"      # Good (bash-4)
+ #     a\'b/c
+ #     $ compgen -f "a\\\\\'b/"  # Good (bash-3)
+ #     a\'b/c
+ #
+ # See also: http://lists.gnu.org/archive/html/bug-bash/2009-03/msg00155.html
+ # @param $1  Argument to quote
+ # @param $2  Name of variable to return result to
+ _quote_readline_by_ref()
+ {
+     if [[ ${1:0:1} == "'" ]]; then
+         # Quote word, leaving out first character
+         printf -v $2 %q "${1:1}"
+         if [[ ${BASH_VERSINFO[0]} -le 3 ]]; then
+             # Double-quote word on bash-3
+             printf -v $2 %q ${!2}
          fi
-         # The WORDBREAK character is escaped;
-         # Recalculate $word_start
-         tmp=${COMP_LINE:0:$(( $word_start - 2 ))}
-         word_start=`expr "$tmp" : '.*['"$WORDBREAKS"']'`
-     done
+     elif [[ ${BASH_VERSINFO[0]} -le 3 && ${1:0:1} == '"' ]]; then
+         printf -v $2 %q "${1:1}"
+     else
+         printf -v $2 %q "$1"
+     fi
  
-     cur=${cur:$word_start}
-     printf "%s" "$cur"
- } # __get_cword4()
+     # If result becomes quoted like this: $'string', re-evaluate in order to
+     # drop the additional quoting.  See also: http://www.mail-archive.com/
+     # bash-completion-devel at lists.alioth.debian.org/msg01942.html
+     [[ ${!2:0:1} == '$' ]] && eval $2=${!2}
+ } # _quote_readline_by_ref()
  
  
  # This function performs file and directory completion. It's better than
  # simply using 'compgen -f', because it honours spaces in filenames.
- # If passed -d, it completes only on directories. If passed anything else,
- # it's assumed to be a file glob to complete on.
+ # @param $1  If `-d', complete only on directories.  Otherwise filter/pick only
+ #            completions with `.$1' as file extension.
  #
  _filedir()
  {
-     local IFS=$'\t\n' xspec
+     local i IFS=$'\t\n' xspec
  
-     _expand || return 0
+     __expand_tilde_by_ref cur
  
      local -a toks
-     local tmp
+     local quoted tmp
  
-     # TODO: I've removed a "[ -n $tmp ] &&" before `echo $tmp',
-     #       and everything works again. If this bug
-     #       suddenly appears again (i.e. "cd /b<TAB>"
-     #       becomes "cd /"), remember to check for
-     #       other similar conditionals (here and
-     #       _filedir_xspec()). --David
-     # NOTE: The comment above has been moved outside of the subshell below,
-     #       because quotes-in-comments-in-a-subshell cause errors on
-     #       bash-3.1.  See also:
-     #       http://www.mail-archive.com/bug-bash@gnu.org/msg01667.html
+     _quote_readline_by_ref "$cur" quoted
      toks=( ${toks[@]-} $(
-     compgen -d -- "$(quote_readline "$cur")" | {
-     while read -r tmp; do
-         echo $tmp
-     done
- }
- ))
+         compgen -d -- "$quoted" | {
+             while read -r tmp; do
+                 # TODO: I have removed a "[ -n $tmp ] &&" before 'printf ..',
+                 #       and everything works again. If this bug suddenly
+                 #       appears again (i.e. "cd /b<TAB>" becomes "cd /"),
+                 #       remember to check for other similar conditionals (here
+                 #       and _filedir_xspec()). --David
+                 printf '%s\n' $tmp
+             done
+         }
+     ))
  
- if [[ "$1" != -d ]]; then
-     xspec=${1:+"!*.$1"}
-     toks=( ${toks[@]-} $(
-     compgen -f -X "$xspec" -- "$(quote_readline "$cur")" | {
-     while read -r tmp; do
-         [ -n $tmp ] && echo $tmp
-     done
- }
- ))
+     # 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
+     #
+     if [[ "$1" != -d ]]; then
+         xspec=${1:+"!*.$1"}
+         if [[ ${cur:0:1} == "'" && ${BASH_VERSINFO[0]} -ge 4 ]]; then
+             toks=( ${toks[@]-} $(
+                 eval compgen -f -X \"\$xspec\" -- $quoted
+             ) )
+         else
+             toks=( ${toks[@]-} $(
+                 compgen -f -X "$xspec" -- $quoted
+             ) )
+         fi
+         if [ ${#toks[@]} -ne 0 ]; then
+             # If `compopt' is available, set `-o filenames'
+             compopt &>/dev/null && compopt -o filenames ||
+             # No, `compopt' isn't available;
+             # Is `-o filenames' set?
+             [[ (
+                 ${COMP_WORDS[0]} && 
+                 "$(complete -p ${COMP_WORDS[0]})" == *"-o filenames"*
+             ) ]] || {
+                 # No, `-o filenames' isn't set;
+                 # Emulate `-o filenames'
+                 # NOTE: A side-effect of emulating `-o filenames' is that
+                 #       backslash escape characters are visible within the list
+                 #       of presented completions, e.g.  the completions look
+                 #       like:
+                 #
+                 #           $ foo a<TAB>
+                 #           a\ b/  a\$b/
+                 #
+                 #       whereas with `-o filenames' active the completions look
+                 #       like:
+                 #
+                 #           $ ls a<TAB>
+                 #           a b/  a$b/
+                 #
+                 for ((i=0; i < ${#toks[@]}; i++)); do
+                     # If directory exists, append slash (/)
+                     if [[ ${cur:0:1} != "'" ]]; then
+                         [[ -d ${toks[i]} ]] && toks[i]="${toks[i]}"/
+                         if [[ ${cur:0:1} == '"' ]]; then
+                             toks[i]=${toks[i]//\\/\\\\}
+                             toks[i]=${toks[i]//\"/\\\"}
+                             toks[i]=${toks[i]//\$/\\\$}
+                         else
+                             toks[i]=$(printf %q ${toks[i]})
+                         fi
+                     fi
+                 done
+             }
+         fi
      fi
  
      COMPREPLY=( "${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.
@@@ -441,11 -662,8 +662,8 @@@ _split_longopt(
  _parse_help() {
      local cmd
      cmd=$1
-     $cmd --help | \
-     grep -- "^[[:space:]]*-" | \
-     tr "," " " | \
-     awk '{print $1; if ($2 ~ /-.*/) { print $2 } }' | \
-     sed -e "s:=.*::g"
+     $cmd --help 2>&1 | command grep -- "^[[:space:]]*-" | tr "," " " | \
+         awk '{print $1; if ($2 ~ /-.*/) { print $2 } }' | sed -e "s:=.*::g"
  }
  
  # This function completes on signal names
@@@ -464,29 -682,49 +682,49 @@@ _signals(
      done
  }
  
+ # This function completes on known mac addresses
+ #
+ _mac_addresses()
+ {
+     local re='\([A-Fa-f0-9]\{2\}:\)\{5\}[A-Fa-f0-9]\{2\}'
+     local PATH="$PATH:/sbin:/usr/sbin"
+ 
+     # Local interfaces (Linux only?)
+     COMPREPLY=( "${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 \
+         "s/.*[[:space:]]\($re\)[[:space:]].*/\1/p" -ne \
+         "s/.*[[:space:]]\($re\)[[:space:]]*$/\1/p" ) )
+ 
+     COMPREPLY=( $( compgen -W '${COMPREPLY[@]}' -- "$cur" ) )
+     __ltrim_colon_completions "$cur"
+ }
+ 
  # This function completes on configured network interfaces
  #
  _configured_interfaces()
  {
      if [ -f /etc/debian_version ]; then
          # Debian system
-         COMPREPLY=( $( sed -ne 's|^iface \([^ ]\+\).*$|\1|p' \
-         /etc/network/interfaces ) )
+         COMPREPLY=( $( compgen -W "$( sed -ne 's|^iface \([^ ]\{1,\}\).*$|\1|p'\
+             /etc/network/interfaces )" -- "$cur" ) )
      elif [ -f /etc/SuSE-release ]; then
          # SuSE system
-         COMPREPLY=( $( command ls \
-         /etc/sysconfig/network/ifcfg-* | \
-         sed -ne 's|.*ifcfg-\('"$cur"'.*\)|\1|p' ) )
+         COMPREPLY=( $( compgen -W "$( printf '%s\n' \
+             /etc/sysconfig/network/ifcfg-* | \
+             sed -ne 's|.*ifcfg-\(.*\)|\1|p' )" -- "$cur" ) )
      elif [ -f /etc/pld-release ]; then
          # PLD Linux
-         COMPREPLY=( $( command ls -B \
-         /etc/sysconfig/interfaces | \
-         sed -ne 's|.*ifcfg-\('"$cur"'.*\)|\1|p' ) )
+         COMPREPLY=( $( compgen -W "$( command ls -B \
+             /etc/sysconfig/interfaces | \
+             sed -ne 's|.*ifcfg-\(.*\)|\1|p' )" -- "$cur" ) )
      else
          # Assume Red Hat
-         COMPREPLY=( $( command ls \
-         /etc/sysconfig/network-scripts/ifcfg-* | \
-         sed -ne 's|.*ifcfg-\('"$cur"'.*\)|\1|p' ) )
+         COMPREPLY=( $( compgen -W "$( printf '%s\n' \
+             /etc/sysconfig/network-scripts/ifcfg-* | \
+             sed -ne 's|.*ifcfg-\(.*\)|\1|p' )" -- "$cur" ) )
      fi
  }
  
@@@ -513,10 -751,51 +751,51 @@@ _available_interfaces(
          cmd="ifconfig -a"
      fi
  
-     COMPREPLY=( $( eval $cmd 2>/dev/null | \
-     sed -ne 's|^\('"$cur"'[^[:space:][:punct:]]\{1,\}\).*$|\1|p') )
+     COMPREPLY=( $( eval PATH="$PATH:/sbin" $cmd 2>/dev/null | \
+         awk '/^[^ \t]/ { print $1 }' ) )
+     COMPREPLY=( $( compgen -W '${COMPREPLY[@]/%[[:punct:]]/}' -- "$cur" ) )
  }
  
+ 
+ # Expand variable starting with tilde (~)
+ # Only the first portion of the variable from the tilde up to the first slash
+ # (~../) is expanded.  The remainder of the variable, containing for example
+ # a dollar sign variable ($) or asterisk (*) is not expanded.
+ # Example usage:
+ #
+ #    $ v="~"; __expand_tilde_by_ref v; echo "$v"
+ #
+ # Example output:
+ #
+ #       v                  output
+ #    --------         ----------------
+ #    ~                /home/user
+ #    ~foo/bar         /home/foo/bar
+ #    ~foo/$HOME       /home/foo/$HOME
+ #    ~foo/a  b        /home/foo/a  b
+ #    ~foo/*           /home/foo/*
+ #  
+ # @param $1  Name of variable (not the value of the variable) to expand
+ __expand_tilde_by_ref() {
+     # Does $1 start with tilde (~)?
+     if [ "${!1:0:1}" = "~" ]; then
+         # Does $1 contain slash (/)?
+         if [ "${!1}" != "${!1//\/}" ]; then
+             # Yes, $1 contains slash;
+             # 1: Remove * including and after first slash (/), i.e. "~a/b"
+             #    becomes "~a".  Double quotes allow eval.
+             # 2: Remove * before the first slash (/), i.e. "~a/b"
+             #    becomes "b".  Single quotes prevent eval.
+             #       +-----1----+ +---2----+
+             eval $1="${!1/%\/*}"/'${!1#*/}'
+         else 
+             # No, $1 doesn't contain slash
+             eval $1="${!1}"
+         fi
+     fi
+ } # __expand_tilde_by_ref()
+ 
+ 
  # This function expands tildes in pathnames
  #
  _expand()
@@@ -540,7 -819,7 +819,7 @@@
  
  # This function completes on process IDs.
  # AIX and Solaris ps prefers X/Open syntax.
- [ $UNAME = SunOS -o $UNAME = AIX ] &&
+ [[ $UNAME == SunOS || $UNAME == AIX ]] &&
  _pids()
  {
      COMPREPLY=( $( compgen -W '$( command ps -efo pid | sed 1d )' -- "$cur" ))
@@@ -552,7 -831,7 +831,7 @@@ _pids(
  
  # This function completes on process group IDs.
  # AIX and SunOS prefer X/Open, all else should be BSD.
- [ $UNAME = SunOS -o $UNAME = AIX ] &&
+ [[ $UNAME == SunOS || $UNAME == AIX ]] &&
  _pgids()
  {
      COMPREPLY=( $( compgen -W '$( command ps -efo pgid | sed 1d )' -- "$cur" ))
@@@ -564,13 -843,11 +843,11 @@@ _pgids(
  
  # This function completes on process names.
  # AIX and SunOS prefer X/Open, all else should be BSD.
- [ $UNAME = SunOS -o $UNAME = AIX ] &&
+ [[ $UNAME == SunOS || $UNAME == AIX ]] &&
  _pnames()
  {
-     COMPREPLY=( $( compgen -W '$( command ps -efo comm | \
-     sed -e 1d -e "s:.*/::" -e "s/^-//" \
-     -e "s/^<defunct>$//")' \
-     -- "$cur" ) )
+     COMPREPLY=( $( compgen -X '<defunct>' -W '$( command ps -efo comm | \
+         sed -e 1d -e "s:.*/::" -e "s/^-//" | sort -u )' -- "$cur" ) )
  } ||
  _pnames()
  {
@@@ -582,11 -859,9 +859,9 @@@
      # 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 -W '$( command ps axo command= | \
-     sed -e "s/ .*//; s:.*/::; s/:$//;" \
-     -e "s/^[[(-]//; s/[])]$//" \
-     -e "s/^<defunct>$//")' \
-     -- "$cur" ) )
+     COMPREPLY=( $( compgen -X '<defunct>' -W '$( command ps axo command= | \
+         sed -e "s/ .*//" -e "s:.*/::" -e "s/:$//" -e "s/^[[(-]//" \
+             -e "s/[])]$//" | sort -u )' -- "$cur" ) )
  }
  
  # This function completes on user IDs
@@@ -608,14 -883,13 +883,13 @@@ _uids(
  _gids()
  {
      if type getent &>/dev/null; then
-         COMPREPLY=( $( getent group | \
-         awk -F: '{if ($3 ~ /^'"$cur"'/) print $3}' ) )
+         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" ) )
      else
          # make do with /etc/group
-         COMPREPLY=( $( awk 'BEGIN {FS=":"} {if ($3 ~ /^'"$cur"'/) print $3}'\
-         /etc/group ) )
+         COMPREPLY=( $( compgen -W '$( cut -d: -f3 /etc/group )' -- "$cur" ) )
      fi
  }
  
@@@ -626,10 -900,12 +900,12 @@@ _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=( $( builtin echo $sysvdir/!(*.rpm@(orig|new|save)|*~|functions)) )
+     COMPREPLY=( $( printf '%s\n' \
+         $sysvdir/!(*.rpm@(orig|new|save)|*~|functions) ) )
  
      if [ -d $famdir ]; then
-         COMPREPLY=( "${COMPREPLY[@]}" $( builtin echo $famdir/!(*.rpm@(orig|new|save)|*~)) )
+         COMPREPLY=( "${COMPREPLY[@]}" $( printf '%s\n' \
+             $famdir/!(*.rpm@(orig|new|save)|*~) ) )
      fi
  
      COMPREPLY=( $( compgen -W '${COMPREPLY[@]#@($sysvdir|$famdir)/}' -- "$cur" ) )
@@@ -641,8 -917,8 +917,8 @@@ _modules(
  {
      local modpath
      modpath=/lib/modules/$1
-     COMPREPLY=( $( command ls -R $modpath | \
-     sed -ne 's/^\('"$cur"'.*\)\.k\?o\(\|.gz\)$/\1/p') )
+     COMPREPLY=( $( compgen -W "$( command ls -R $modpath | \
+         sed -ne 's/^\(.*\)\.k\{0,1\}o\(\.gz\)\{0,1\}$/\1/p' )" -- "$cur" ) )
  }
  
  # This function completes on installed modules
@@@ -650,22 -926,41 +926,41 @@@
  _installed_modules()
  {
      COMPREPLY=( $( compgen -W "$( PATH="$PATH:/sbin" lsmod | \
-     awk '{if (NR != 1) print $1}' )" -- $1 ) )
+         awk '{if (NR != 1) print $1}' )" -- "$1" ) )
  }
  
- # This function completes on user:group format
+ # This function completes on user or user:group format; as for chown and cpio.
+ #
+ # The : must be added manually; it will only complete usernames initially.
+ # The legacy user.group format is not supported.
  #
+ # It assumes compopt -o filenames; but doesn't touch it.
  _usergroup()
  {
      local IFS=$'\n'
-     cur=${cur//\\\\ / }
-     if [[ $cur = *@(\\:|.)* ]] && [ -n "$bash205" ]; then
-         user=${cur%%*([^:.])}
-         COMPREPLY=( $(compgen -P ${user/\\\\} -g -- ${cur##*[.:]}) )
-     elif [[ $cur = *:* ]] && [ -n "$bash205" ]; then
-         COMPREPLY=( $( compgen -g -- ${cur##*[.:]} ) )
+     if [[ $cur = *\\\\* || $cur = *:*:* ]]; then
+         # Give up early on if something seems horribly wrong.
+         return
+     elif [[ $cur = *\\:* ]]; then
+         # Completing group after 'user\:gr<TAB>'.
+         # Reply with a list of groups prefixed with 'user:', readline will
+         # escape to the colon.
+         local prefix
+         prefix=${cur%%*([^:])}
+         prefix=${prefix//\\}
+         COMPREPLY=( $( compgen -P "$prefix" -g -- "${cur#*[:]}" ) )
+     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
+         COMPREPLY=( $( compgen -g -- "${cur#*:}" ) )
      else
-         COMPREPLY=( $( compgen -S : -u -- "$cur" ) )
+         # Completing a partial 'usernam<TAB>'.
+         #
+         # Don't suffix with a : because readline will escape it and add a
+         # slash. It's better to complete into 'chown username ' than 'chown
+         # username\:'.
+         COMPREPLY=( $( compgen -u -- "$cur" ) )
      fi
  }
  
@@@ -673,8 -968,32 +968,32 @@@
  #
  _shells()
  {
-     COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W '$( grep "^[[:space:]]*/" \
-     /etc/shells 2>/dev/null )' -- "$cur" ) )
+     COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W \
+         '$( command grep "^[[:space:]]*/" /etc/shells 2>/dev/null )' \
+         -- "$cur" ) )
+ }
+ 
+ # This function completes on valid filesystem types
+ #
+ _fstypes()
+ {
+     local fss
+ 
+     if [ -e /proc/filesystems ] ; then
+         # Linux
+         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 )"
+     fi
+ 
+     [ -n "$fss" ] && \
+         COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W "$fss" -- "$cur" ) )
  }
  
  # Get real command.
@@@ -682,28 -1001,47 +1001,47 @@@
  # - stdout:  Filename of command in PATH with possible symbolic links resolved.
  #            Empty string if command not found.
  # - return:  True (0) if command found, False (> 0) if not.
- _realcommand() {
+ _realcommand()
+ {
      type -P "$1" > /dev/null && {
-     if type -p realpath > /dev/null; then
-         realpath "$(type -P "$1")"
-     elif type -p readlink > /dev/null; then
-         readlink -f "$(type -P "$1")"
-     else
-         type -P "$1"
-     fi
+         if type -p realpath > /dev/null; then
+             realpath "$(type -P "$1")"
+         elif type -p readlink > /dev/null; then
+             readlink -f "$(type -P "$1")"
+         else
+             type -P "$1"
+         fi
+     }
  }
+ 
+ # This function returns the first arugment, excluding options
+ # @param $1 chars  Characters out of $COMP_WORDBREAKS which should
+ #     NOT be considered word breaks. See __reassemble_comp_words_by_ref.
+ _get_first_arg()
+ {
+     local i
+ 
+     arg=
+     for (( i=1; i < COMP_CWORD; i++ )); do
+         if [[ "${COMP_WORDS[i]}" != -* ]]; then
+             arg=${COMP_WORDS[i]}
+             break
+         fi
+     done
  }
  
  
- # this function count the number of mandatory args
- #
+ # 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.
  _count_args()
  {
+     local i cword words
+     __reassemble_comp_words_by_ref "$1" words cword
+ 
      args=1
-     for (( i=1; i < COMP_CWORD; i++ )); do
-         if [[ "${COMP_WORDS[i]}" != -* ]]; then
-             args=$(($args+1))
-         fi
+     for i in "${words[@]:1:cword-1}"; do
+         [[ "$i" != -* ]] && args=$(($args+1))
      done
  }
  
@@@ -712,7 -1050,7 +1050,7 @@@
  _pci_ids()
  {
      COMPREPLY=( ${COMPREPLY[@]:-} $( compgen -W \
-     "$( PATH="$PATH:/sbin" lspci -n | awk '{print $3}')" -- "$cur" ) )
+         "$( PATH="$PATH:/sbin" lspci -n | awk '{print $3}')" -- "$cur" ) )
  }
  
  # This function completes on USB IDs
@@@ -720,257 -1058,161 +1058,161 @@@
  _usb_ids()
  {
      COMPREPLY=( ${COMPREPLY[@]:-} $( compgen -W \
-     "$( PATH="$PATH:/sbin" lsusb | awk '{print $6}' )" -- "$cur" ) )
+         "$( PATH="$PATH:/sbin" lsusb | awk '{print $6}' )" -- "$cur" ) )
+ }
+ 
+ # CD device names
+ _cd_devices()
+ {
+     COMPREPLY=( "${COMPREPLY[@]}"
+         $( compgen -f -d -X "!*/?([amrs])cd*" -- "${cur:-/dev/}" ) )
+ }
+ 
+ # DVD device names
+ _dvd_devices()
+ {
+     COMPREPLY=( "${COMPREPLY[@]}"
+         $( compgen -f -d -X "!*/?(r)dvd*" -- "${cur:-/dev/}" ) )
  }
  
  # start of section containing completion functions for external programs
  
  # 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
+ [ $UNAME = FreeBSD ] && complete -W 'index search fetch fetch-list extract \
+     patch configure build install reinstall deinstall clean clean-depends \
+     kernel buildworld' make
  
  # This completes on a list of all available service scripts for the
  # 'service' command and/or the SysV init.d directory, followed by
  # that script's available commands
  #
  { have service || [ -d /etc/init.d/ ]; } &&
-     _service()
-     {
-         local cur prev sysvdir
+ _service()
+ {
+     local cur prev sysvdir
  
-         COMPREPLY=()
-         prev=${COMP_WORDS[COMP_CWORD-1]}
-         cur=`_get_cword`
+     COMPREPLY=()
+     prev=${COMP_WORDS[COMP_CWORD-1]}
+     cur=`_get_cword`
  
-         # don't complete for things like killall, ssh and mysql if it's
-         # the standalone command, rather than the init script
-         [[ ${COMP_WORDS[0]} != @(*init.d/!(functions|~)|service) ]] && return 0
+     # don't complete for things like killall, ssh and mysql if it's
+     # the standalone command, rather than the init script
+     [[ ${COMP_WORDS[0]} != @(*init.d/!(functions|~)|service) ]] && return 0
  
-         # don't complete past 2nd token
-         [ $COMP_CWORD -gt 2 ] && return 0
+     # don't complete past 2nd token
+     [ $COMP_CWORD -gt 2 ] && return 0
  
-         [ -d /etc/rc.d/init.d ] && sysvdir=/etc/rc.d/init.d \
-         || sysvdir=/etc/init.d
+     [ -d /etc/rc.d/init.d ] && sysvdir=/etc/rc.d/init.d || sysvdir=/etc/init.d
  
-         if [[ $COMP_CWORD -eq 1 ]] && [[ $prev == "service" ]]; then
-             _services
-         else
-             COMPREPLY=( $( compgen -W '`sed -ne "y/|/ /; \
-             s/^.*\(U\|msg_u\)sage.*{\(.*\)}.*$/\1/p" \
+     if [[ $COMP_CWORD -eq 1 ]] && [[ $prev == "service" ]]; then
+         _services
+     else
+         COMPREPLY=( $( compgen -W '`sed -e "y/|/ /" \
+             -ne "s/^.*\(U\|msg_u\)sage.*{\(.*\)}.*$/\2/p" \
              $sysvdir/${prev##*/} 2>/dev/null`' -- "$cur" ) )
-         fi
- 
-         return 0
-     } &&
-     complete -F _service service
-     [ -d /etc/init.d/ ] && complete -F _service $default \
-     $(for i in /etc/init.d/*; do echo ${i##*/}; done)
- 
-     # chown(1) completion
-     #
-     _chown()
-     {
-         local cur prev split=false
-         cur=`_get_cword`
-         prev=${COMP_WORDS[COMP_CWORD-1]}
- 
-         _split_longopt && split=true
- 
-         case "$prev" in
-             --from)
-                 _usergroup
-                 return 0
-                 ;;
-             --reference)
-                 _filedir
-                 return 0
-                 ;;
-         esac
+     fi
  
-         $split && return 0
+     return 0
+ } &&
+ complete -F _service service
+ [ -d /etc/init.d/ ] && complete -F _service -o default \
+     $(for i in /etc/init.d/*; do printf '%s\n' ${i##*/}; done)
  
-         # options completion
-         if [[ "$cur" == -* ]]; then
-             COMPREPLY=( $( compgen -W '-c -h -f -R -v --changes \
-             --dereference --no-dereference --from --silent --quiet \
-             --reference --recursive --verbose --help --version' -- "$cur" ) )
-         else
-             _count_args
- 
-             case $args in
-                 1)
-                     _usergroup
-                     ;;
-                 *)
-                     _filedir
-                     ;;
-             esac
-         fi
-     }
-     complete -F _chown $filenames chown
  
-     # chgrp(1) completion
-     #
-     _chgrp()
-     {
-         local cur prev split=false
+ # chown(1) completion
+ #
+ _chown()
+ {
+     local cur prev split=false
  
-         COMPREPLY=()
-         cur=`_get_cword`
-         cur=${cur//\\\\/}
-         prev=${COMP_WORDS[COMP_CWORD-1]}
+     # Get cur and prev words; but don't treat user:group as separate words.
+     cur=`_get_cword :`
+     prev=`_get_pword :`
  
-         _split_longopt && split=true
+     _split_longopt && split=true
  
-         if [[ "$prev" == --reference ]]; then
+     case "$prev" in
+         --from)
+             _usergroup
+             return 0
+             ;;
+         --reference)
              _filedir
              return 0
-         fi
+             ;;
+     esac
  
-         $split && return 0
+     $split && return 0
  
-         # options completion
-         if [[ "$cur" == -* ]]; then
-             COMPREPLY=( $( compgen -W '-c -h -f -R -v --changes \
-             --dereference --no-dereference --silent --quiet \
-             --reference --recursive --verbose --help --version' -- "$cur" ) )
-             return 0
-         fi
+     if [[ "$cur" == -* ]]; then
+         # Complete -options
+         local w opts
+         for w in "${COMP_WORDS[@]}" ; do
+             [[ "$w" == -@(R|-recursive) ]] && opts="-H -L -P" && break
+         done
+         COMPREPLY=( $( compgen -W '-c -h -f -R -v --changes --dereference \
+             --no-dereference --from --silent --quiet --reference --recursive \
+             --verbose --help --version $opts' -- "$cur" ) )
+     else
+         local args
+ 
+         # The first argument is an usergroup; the rest are filedir.
+         _count_args :
  
-         # first parameter on line or first since an option?
-         if [ $COMP_CWORD -eq 1 ] && [[ "$cur" != -* ]] || \
-             [[ "$prev" == -* ]] && [ -n "$bash205" ]; then
-             local IFS=$'\n'
-             COMPREPLY=( $( compgen -g "$cur" 2>/dev/null ) )
+         if [[ $args == 1 ]]; then
+             _usergroup
          else
-             _filedir || return 0
+             _filedir
          fi
+     fi
+ } # _chown()
+ complete -F _chown -o filenames chown
  
-         return 0
-     }
-     complete -F _chgrp $filenames chgrp
- 
-     # umount(8) completion. This relies on the mount point being the third
-     # space-delimited field in the output of mount(8)
-     #
-     _umount()
-     {
-         local cur IFS=$'\n'
- 
-         COMPREPLY=()
-         cur=`_get_cword`
  
-         COMPREPLY=( $( compgen -W '$( mount | cut -d" " -f 3 )' -- "$cur" ) )
+ # chgrp(1) completion
+ #
+ _chgrp()
+ {
+     local cur prev split=false
  
-         return 0
-     }
-     complete -F _umount $dirnames umount
+     COMPREPLY=()
+     cur=`_get_cword`
+     cur=${cur//\\\\/}
+     prev=${COMP_WORDS[COMP_CWORD-1]}
  
-     # mount(8) completion. This will pull a list of possible mounts out of
-     # /etc/{,v}fstab, unless the word being completed contains a ':', which
-     # would indicate the specification of an NFS server. In that case, we
-     # query the server for a list of all available exports and complete on
-     # that instead.
-     #
-     _mount()
-     {
-         local cur i sm host prev
- 
-         COMPREPLY=()
-         cur=`_get_cword`
-         [[ "$cur" == \\ ]] && cur="/"
-         prev=${COMP_WORDS[COMP_CWORD-1]}
- 
-         for i in {,/usr}/{,s}bin/showmount; do [ -x $i ] && sm=$i && break; done
- 
-         if [ -n "$sm" ] && [[ "$cur" == *:* ]]; then
-             COMPREPLY=( $( $sm -e ${cur%%:*} | sed 1d | \
-             grep ^${cur#*:} | awk '{print $1}' ) )
-         elif [[ "$cur" == //* ]]; then
-             host=${cur#//}
-             host=${host%%/*}
-             if [ -n "$host" ]; then
-                 COMPREPLY=( $( compgen -W "$( echo $( smbclient -d 0 -NL $host 2>/dev/null|
-                 sed -ne '/^['"$'\t '"']*Sharename/,/^$/p' |
-                 sed -ne '3,$s|^[^A-Za-z]*\([^'"$'\t '"']*\).*$|//'$host'/\1|p' ) )" -- "$cur" ) )
-             fi
-         elif [ -r /etc/vfstab ]; then
-             # Solaris
-             COMPREPLY=( $( compgen -W "$( awk '! /^[ \t]*#/ {if ($3 ~ /\//) print $3}' /etc/vfstab )" -- "$cur" ) )
-         elif [ ! -e /etc/fstab ]; then
-             # probably Cygwin
-             COMPREPLY=( $( compgen -W "$( mount | awk '! /^[ \t]*#/ {if ($3 ~ /\//) print $3}' )" -- "$cur" ) )
-         else
-             # probably Linux
-             if [ $prev = -L ]; then
-                 COMPREPLY=( $( compgen -W '$(sed -ne "s/^[[:space:]]*LABEL=\([^[:space:]]*\).*/\1/p" /etc/fstab )' -- "$cur" ) )
-             elif [ $prev = -U ]; then
-                 COMPREPLY=( $( compgen -W '$(sed -ne "s/^[[:space:]]*UUID=\([^[:space:]]*\).*/\1/p" /etc/fstab )' -- "$cur" ) )
-             else
-                 COMPREPLY=( $( compgen -W "$( awk '! /^[ \t]*#/ {if ($2 ~ /\//) print $2}' /etc/fstab )" -- "$cur" ) )
-             fi
-         fi
+     _split_longopt && split=true
  
+     if [[ "$prev" == --reference ]]; then
+         _filedir
          return 0
-     }
-     complete -F _mount $default $dirnames mount
- 
-     # Linux rmmod(8) completion. This completes on a list of all currently
-     # installed kernel modules.
-     #
-     have rmmod && {
-     _rmmod()
-     {
-         local cur
+     fi
  
-         COMPREPLY=()
-         cur=`_get_cword`
+     $split && return 0
  
-         _installed_modules "$cur"
+     # options completion
+     if [[ "$cur" == -* ]]; then
+         local w opts
+         for w in "${COMP_WORDS[@]}" ; do
+             [[ "$w" == -@(R|-recursive) ]] && opts="-H -L -P" && break
+         done
+         COMPREPLY=( $( compgen -W '-c -h -f -R -v --changes --dereference \
+             --no-dereference --silent --quiet --reference --recursive \
+             --verbose --help --version $opts' -- "$cur" ) )
          return 0
-     }
-     complete -F _rmmod rmmod
+     fi
  
-     # Linux insmod(8), modprobe(8) and modinfo(8) completion. This completes on a
-     # list of all available modules for the version of the kernel currently
-     # running.
-     #
-     _insmod()
-     {
-         local cur prev modpath
- 
-         COMPREPLY=()
-         cur=`_get_cword`
-         prev=${COMP_WORDS[COMP_CWORD-1]}
- 
-         # behave like lsmod for modprobe -r
-         if [ $1 = "modprobe" ] &&
-             [ "${COMP_WORDS[1]}" = "-r" ]; then
-             _installed_modules "$cur"
-             return 0
-         fi
+     # first parameter on line or first since an option?
+     if [[ $COMP_CWORD -eq 1 && "$cur" != -* || "$prev" == -* ]]; then
+         local IFS=$'\n'
+         COMPREPLY=( $( compgen -g "$cur" 2>/dev/null ) )
+     else
+         _filedir || return 0
+     fi
  
-         # do filename completion if we're giving a path to a module
-         if [[ "$cur" == */* ]]; then
-             _filedir '@(?(k)o?(.gz))'
-             return 0
-         fi
+     return 0
+ } # _chgrp()
+ complete -F _chgrp -o filenames chgrp
  
-         if [ $COMP_CWORD -gt 1 ] &&
-             [[ "${COMP_WORDS[COMP_CWORD-1]}" != -* ]]; then
-             # do module parameter completion
-             COMPREPLY=( $( /sbin/modinfo -p ${COMP_WORDS[1]} 2>/dev/null | \
-             awk '{if ($1 ~ /^parm:/ && $2 ~ /^'"$cur"'/) { print $2 } \
-         else if ($1 !~ /:/ && $1 ~ /^'"$cur"'/) { print $1 }}' ) )
-         else
-             _modules $(uname -r)
-         fi
- 
-         return 0
-     }
-     complete -F _insmod $filenames insmod modprobe modinfo
- }
  
  # renice(8) completion
  #
@@@ -984,7 -1226,7 +1226,7 @@@ _renice(
  
      i=0
      # walk back through command line and find last option
-     while [ $i -le $COMP_CWORD -a ${#COMPREPLY[@]} -eq 0 ]; do
+     while [[ $i -le $COMP_CWORD && ${#COMPREPLY[@]} -eq 0 ]]; do
          curopt=${COMP_WORDS[COMP_CWORD-$i]}
          case "$curopt" in
              -u)
@@@ -1002,6 -1244,7 +1244,7 @@@
  }
  complete -F _renice renice
  
+ 
  # kill(1) completion
  #
  _kill()
@@@ -1011,7 -1254,7 +1254,7 @@@
      COMPREPLY=()
      cur=`_get_cword`
  
-     if [ $COMP_CWORD -eq 1 ] && [[ "$cur" == -* ]]; then
+     if [[ $COMP_CWORD -eq 1 && "$cur" == -* ]]; then
          # return list of available signals
          _signals
      else
@@@ -1023,7 -1266,7 +1266,7 @@@ complete -F _kill kil
  
  # killall(1) (Linux and FreeBSD) and pkill(1) completion.
  #
- [ $UNAME = Linux -o $UNAME = FreeBSD ] || have pkill &&
+ [[ $UNAME == Linux || $UNAME == FreeBSD ]] || have pkill &&
  _killall()
  {
      local cur
@@@ -1031,7 -1274,7 +1274,7 @@@
      COMPREPLY=()
      cur=`_get_cword`
  
-     if [ $COMP_CWORD -eq 1 ] && [[ "$cur" == -* ]]; then
+     if [[ $COMP_CWORD -eq 1 && "$cur" == -* ]]; then
          _signals
      else
          _pnames
@@@ -1039,7 -1282,7 +1282,7 @@@
  
      return 0
  }
- [ $UNAME = Linux -o $UNAME = FreeBSD ] && complete -F _killall killall
+ [[ $UNAME == Linux || $UNAME == FreeBSD ]] && complete -F _killall killall
  have pkill && complete -F _killall pkill
  
  # pgrep(1) completion.
@@@ -1093,10 -1336,9 +1336,9 @@@ _ipsec(
  
  
      if [ $COMP_CWORD -eq 1 ]; then
-         COMPREPLY=( $( compgen -W 'auto barf eroute klipsdebug look \
-         manual pluto ranbits rsasigkey \
-         setup showdefaults showhostkey spi \
-         spigrp tncfg whack' -- "$cur" ) )
+         COMPREPLY=( $( compgen -W 'auto barf eroute klipsdebug look manual \
+             pluto ranbits rsasigkey setup showdefaults showhostkey spi spigrp \
+             tncfg whack' -- "$cur" ) )
          return 0
      fi
  
@@@ -1132,7 -1374,7 +1374,7 @@@ _user_at_host() 
      local cur
  
      COMPREPLY=()
-     cur=`_get_cword`
+     cur=`_get_cword :`
  
      if [[ $cur == *@* ]]; then
          _known_hosts_real "$cur"
@@@ -1142,7 -1384,7 +1384,7 @@@
  
      return 0
  }
- shopt -u hostcomplete && complete -F _user_at_host $nospace talk ytalk finger
+ shopt -u hostcomplete && complete -F _user_at_host -o nospace talk ytalk finger
  
  # NOTE: Using this function as a helper function is deprecated.  Use
  #       `_known_hosts_real' instead.
@@@ -1153,14 -1395,15 +1395,15 @@@ _known_hosts(
  
      # NOTE: Using `_known_hosts' as a helper function and passing options
      #       to `_known_hosts' is deprecated: Use `_known_hosts_real' instead.
-     [ "$1" = -a ] || [ "$2" = -a ] && options=-a
-     [ "$1" = -c ] || [ "$2" = -c ] && options="$options -c"
-     _known_hosts_real $options "$(_get_cword)"
- }
+     [[ "$1" == -a || "$2" == -a ]] && options=-a
+     [[ "$1" == -c || "$2" == -c ]] && options="$options -c"
+     _known_hosts_real $options "$(_get_cword :)"
+ } # _known_hosts()
  
  # Helper function for completing _known_hosts.
- # This function performs host completion based on ssh's known_hosts files.
- # Also hosts from HOSTFILE (compgen -A hostname) are added, unless
+ # This function performs host completion based on ssh's config and known_hosts
+ # files, as well as hostnames reported by avahi-browse.  Also hosts from
+ # HOSTFILE (compgen -A hostname) are added, unless
  # COMP_KNOWN_HOSTS_WITH_HOSTFILE is set to an empty value.
  # Usage: _known_hosts_real [OPTIONS] CWORD
  # Options:  -a             Use aliases
@@@ -1171,7 -1414,7 +1414,7 @@@
  _known_hosts_real()
  {
      local configfile flag prefix
-     local cur curd awkcur user suffix aliases global_kh user_kh hosts i host
+     local cur curd awkcur user suffix aliases i host
      local -a kh khd config
  
      local OPTIND=1
@@@ -1186,7 -1429,7 +1429,7 @@@
      [ $# -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 echo ${!OPTIND}; shift; done)
+     $(while [ $# -ge $OPTIND ]; do printf '%s\n' ${!OPTIND}; shift; done)
  
      [[ $cur == *@* ]] && user=${cur%@*}@ && cur=${cur#*@}
      kh=()
@@@ -1204,24 -1447,26 +1447,26 @@@
          config=( "${config[@]}" "${HOME}/.ssh2/config" )
      fi
  
+     # Known hosts files from configs
      if [ ${#config[@]} -gt 0 ]; then
          local OIFS=$IFS IFS=$'\n'
-         # expand path (if present) to global known hosts file
-         global_kh=($( sed -ne 's/^[ \t]*[Gg][Ll][Oo][Bb][Aa][Ll][Kk][Nn][Oo][Ww][Nn][Hh][Oo][Ss][Tt][Ss][Ff][Ii][Ll][Ee]['"$'\t '"']*\(.*\)$/"\1"/p' "${config[@]}" ))
-         for (( i=0; i < ${#global_kh[@]}; i++ )); do
-             global_kh[i]=$(echo "${global_kh[i]//\"/}")
-         done
-         # expand path (if present) to user known hosts file
-         user_kh=($( sed -ne 's/^[ \t]*[Uu][Ss][Ee][Rr][Kk][Nn][Oo][Ww][Nn][Hh][Oo][Ss][Tt][Ss][Ff][Ii][Ll][Ee]['"$'\t '"']*\(.*\)$/"\1"/p' "${config[@]}" ))
-         for (( i=0; i < ${#user_kh[@]}; i++ )); do
-             user_kh[i]=$(echo "${user_kh[i]//\"/}")
+         local -a tmpkh
+         # 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 ) )
+         for i in "${tmpkh[@]}"; do
+             # Remove possible quotes
+             i=${i//\"}
+             # Eval/expand possible `~' or `~user'
+             __expand_tilde_by_ref i
+             [ -r "$i" ] && kh=( "${kh[@]}" "$i" )
          done
          IFS=$OIFS
      fi
  
      # Global known_hosts files
-     [ -r "$global_kh" ] &&
-     kh=( "${kh[@]}" "${global_kh[@]}" )
      if [ -z "$configfile" ]; then
          [ -r /etc/ssh/ssh_known_hosts ] &&
          kh=( "${kh[@]}" /etc/ssh/ssh_known_hosts )
@@@ -1236,8 -1481,6 +1481,6 @@@
      fi
  
      # User known_hosts files
-     [ -r "$user_kh" ] &&
-     kh=( "${kh[@]}" "${user_kh[@]}" )
      if [ -z "$configfile" ]; then
          [ -r ~/.ssh/known_hosts ] &&
          kh=( "${kh[@]}" ~/.ssh/known_hosts )
@@@ -1248,7 -1491,7 +1491,7 @@@
      fi
  
      # If we have known_hosts files to use
-     if [ ${#kh[@]} -gt 0 -o ${#khd[@]} -gt 0 -o -n "$configfile" ]; then
+     if [[ ${#kh[@]} -gt 0 || ${#khd[@]} -gt 0 ]]; then
          # Escape slashes and dots in paths for awk
          awkcur=${cur//\//\\\/}
          awkcur=${awkcur//\./\\\.}
@@@ -1270,7 -1513,7 +1513,7 @@@
  
          if [ ${#kh[@]} -gt 0 ]; then
              # FS needs to look for a comma separated list
-             COMPREPLY=( $( awk 'BEGIN {FS=","}
+             COMPREPLY=( "${COMPREPLY[@]}" $( awk 'BEGIN {FS=","}
              /^\s*[^|\#]/ {for (i=1; i<=2; ++i) { \
              gsub(" .*$", "", $i); \
              gsub("[\\[\\]]", "", $i); \
@@@ -1284,34 -1527,13 +1527,13 @@@
              # dont fork any processes, because in a cluster environment,
              # there can be hundreds of hostkeys
              for i in "${khd[@]}" ; do
-                 if [[ "$i" == *key_22_$awkcurd*.pub ]] && [ -r "$i" ] ; then
+                 if [[ "$i" == *key_22_$curd*.pub && -r "$i" ]]; then
                      host=${i/#*key_22_/}
                      host=${host/%.pub/}
                      COMPREPLY=( "${COMPREPLY[@]}" $host )
                  fi
              done
          fi
-         # append any available aliases from config files
-         if [ ${#config[@]} -gt 0 ] && [ -n "$aliases" ]; then
-             local host_aliases=$( sed -ne 's/^[ \t]*[Hh][Oo][Ss][Tt]\([Nn][Aa][Mm][Ee]\)\?['"$'\t '"']\+\([^#*?]*\)\(#.*\)\?$/\2/p' "${config[@]}" )
-             hosts=$( compgen -W "$host_aliases" -- "$cur" )
-             COMPREPLY=( "${COMPREPLY[@]}" $hosts )
-         fi
- 
-         # Add hosts reported by avahi, if it's available
-         # and if the daemon is started.
-         # The original call to avahi-browse also had "-k", to avoid
-         #  lookups into avahi's services DB. We don't need the name
-         #  of the service, and if it contains ";", it may mistify
-         #  the result. But on Gentoo (at least), -k isn't available
-         #  (even if mentioned in the manpage), so...
-         if type avahi-browse >&/dev/null; then
-             if [ -n "$(pidof avahi-daemon)" ]; then
-                 COMPREPLY=( "${COMPREPLY[@]}" $(
-                 compgen -W "$( avahi-browse -cpr _workstation._tcp | \
-                 grep ^= | cut -d\; -f7 | sort -u )" -- "$cur" ) )
-             fi
-         fi
  
          # apply suffix and prefix
          for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do
@@@ -1319,16 -1541,39 +1541,39 @@@
          done
      fi
  
-     # Add results of normal hostname completion, unless `COMP_KNOWN_HOSTS_WITH_HOSTFILE'
-     # is set to an empty value.
+     # 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" \
+             -S "$suffix" -W "$hosts" -- "$cur" ) )
+     fi
+ 
+     # Add hosts reported by avahi-browse, if it's available.
+     # The original call to avahi-browse also had "-k", to avoid lookups into
+     # avahi's services DB. We don't need the name of the service, and if it
+     # contains ";", it may mistify the result. But on Gentoo (at least),
+     # -k isn't available (even if mentioned in the manpage), so...
+     if type avahi-browse >&/dev/null; then
+         COMPREPLY=( "${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 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[@]}" $( compgen -A hostname -P "$prefix$user" -S "$suffix" -- "$cur" ) )
+         COMPREPLY=( "${COMPREPLY[@]}"
+             $( compgen -A hostname -P "$prefix$user" -S "$suffix" -- "$cur" ) )
      fi
  
+     __ltrim_colon_completions "$prefix$user$cur"
+ 
      return 0
- }
- complete -F _known_hosts traceroute traceroute6 tracepath tracepath6 \
- ping ping6 fping fping6 telnet host nslookup rsh rlogin ftp dig ssh-installkeys mtr
+ } # _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
  
  # This meta-cd function observes the CDPATH variable, so that cd additionally
  # completes on directories under those specified in CDPATH.
@@@ -1348,7 -1593,7 +1593,7 @@@ _cd(
  
      # Use standard dir completion if no CDPATH or parameter starts with /,
      # ./ or ../
-     if [ -z "${CDPATH:-}" ] || [[ "$cur" == ?(.)?(.)/* ]]; then
+     if [[ -z "${CDPATH:-}" || "$cur" == ?(.)?(.)/* ]]; then
          _filedir -d
          return 0
      fi
@@@ -1372,7 -1617,7 +1617,7 @@@
  
      if [[ ${#COMPREPLY[@]} -eq 1 ]]; then
          i=${COMPREPLY[0]}
-         if [ "$i" == "$cur" ] && [[ $i != "*/" ]]; then
+         if [[ "$i" == "$cur" && $i != "*/" ]]; then
              COMPREPLY[0]="${i}/"
          fi
      fi
@@@ -1380,9 -1625,9 +1625,9 @@@
      return 0
  }
  if shopt -q cdable_vars; then
-     complete -v -F _cd $nospace cd
+     complete -v -F _cd -o nospace cd
  else
-     complete -F _cd $nospace cd
+     complete -F _cd -o nospace cd
  fi
  
  # a wrapper method for the next one, when the offset is unknown
@@@ -1409,7 -1654,7 +1654,7 @@@ _command(
  _command_offset()
  {
      local cur func cline cspec noglob cmd i char_offset word_offset \
-     _COMMAND_FUNC _COMMAND_FUNC_ARGS
+         _COMMAND_FUNC _COMMAND_FUNC_ARGS
  
      word_offset=$1
  
@@@ -1479,14 -1724,15 +1724,15 @@@
  
      [ ${#COMPREPLY[@]} -eq 0 ] && _filedir
  }
- complete -F _command $filenames nohup exec nice eval time ltrace then \
-     else do vsound command xargs tsocks
+ complete -F _command -o filenames nohup exec nice eval time ltrace then \
+     else do vsound command xargs tsocks aoss padsp
  
  _root_command()
  {
-     PATH=$PATH:/sbin:/usr/sbin:/usr/local/sbin _command $1 $2 $3
+     local PATH=$PATH:/sbin:/usr/sbin:/usr/local/sbin
+     _command $1 $2 $3
  }
- complete -F _root_command $filenames sudo fakeroot really gksudo gksu kdesudo
+ complete -F _root_command -o filenames sudo fakeroot really gksudo gksu kdesudo
  
  _longopt()
  {
@@@ -1508,8 -1754,8 +1754,8 @@@
      fi
  
      if [[ "$cur" == -* ]]; then
-         COMPREPLY=( $( compgen -W "$( $1 --help 2>&1 | sed -e '/--/!d' \
-             -e 's/.*\(--[-A-Za-z0-9]\+\).*/\1/' |sort -u )"\
+         COMPREPLY=( $( compgen -W "$( $1 --help 2>&1 | \
+             sed -ne 's/.*\(--[-A-Za-z0-9]\{1,\}\).*/\1/p' | sort -u )" \
              -- "$cur" ) )
      elif [[ "$1" == rmdir ]]; then
          _filedir -d
@@@ -1519,17 -1765,17 +1765,17 @@@
  }
  # makeinfo and texi2dvi are defined elsewhere.
  for i in a2ps autoconf automake bc gprof ld nm objcopy objdump readelf strip \
-     bison diff patch enscript cp df dir du ln ls mkfifo mknod mv rm \
+     bison colordiff diff patch enscript cp df dir du ln ls mkfifo mknod mv rm \
      touch vdir awk gperf grep grub indent less m4 sed shar date \
      tee who texindex cat csplit cut expand fmt fold head \
      md5sum nl od paste pr ptx sha1sum sort split tac tail tr unexpand \
-     uniq wc ldd bash id irb mkdir rmdir; do
-     have $i && complete -F _longopt $filenames $i
+     uniq wc ldd bash id irb mkdir rmdir wget curl; do
+     have $i && complete -F _longopt -o filenames $i
  done
  
  # These commands do not use filenames, so '-o filenames' is not needed.
- for i in env netstat seq uname units wget; do
-     have $i && complete -F _longopt $default $i
+ for i in env netstat seq uname units; do
+     have $i && complete -F _longopt -o default $i
  done
  unset i
  
@@@ -1544,10 -1790,10 +1790,10 @@@ _look(
      cur=`_get_cword`
  
      if [ $COMP_CWORD = 1 ]; then
-         COMPREPLY=( $( compgen -W '$(look "$cur" 2>/dev/null)' ) )
+         COMPREPLY=( $( compgen -W '$(look "$cur" 2>/dev/null)' -- "$cur" ) )
      fi
  } &&
- complete -F _look $default look
+ complete -F _look -o default look
  
  # id(1) completion
  #
@@@ -1579,8 -1825,8 +1825,8 @@@ _filedir_xspec(
      _expand || return 0
  
      # get first exclusion compspec that matches this command
-     xspec=$( sed -ne $'/^complete .*[ \t]'${1##*/}$'\([ \t]\|$\)/{p;q;}' \
-         $BASH_COMPLETION )
+     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%% *}
@@@ -1592,7 -1838,7 +1838,7 @@@
          compgen -d -- "$(quote_readline "$cur")" | {
          while read -r tmp; do
              # see long TODO comment in _filedir() --David
-             echo $tmp
+             printf '%s\n' $tmp
          done
          }
          ))
@@@ -1600,14 -1846,14 +1846,14 @@@
      toks=( ${toks[@]-} $(
          eval compgen -f -X "$xspec" -- "\$(quote_readline "\$cur")" | {
          while read -r tmp; do
-             [ -n $tmp ] && echo $tmp
+             [ -n $tmp ] && printf '%s\n' $tmp
          done
          }
          ))
  
      COMPREPLY=( "${toks[@]}" )
  }
- list=( $( sed -ne '/^# START exclude/,/^# FINISH exclude/p' $BASH_COMPLETION | \
+ list=( $( sed -ne '/^# START exclude/,/^# FINISH exclude/p' "$BASH_COMPLETION" | \
      # read exclusion compspecs
      (
      while read line
@@@ -1619,41 -1865,42 +1865,42 @@@
          line=${line##*\'}
          list=( "${list[@]}" $line )
      done
-     echo "${list[@]}"
+     printf '%s ' "${list[@]}"
      )
      ) )
  # remove previous compspecs
  if [ ${#list[@]} -gt 0 ]; then
      eval complete -r ${list[@]}
      # install new compspecs
-     eval complete -F _filedir_xspec $filenames "${list[@]}"
+     eval complete -F _filedir_xspec -o filenames "${list[@]}"
  fi
  unset list
  
  # source completion directory definitions
- if [ -d $BASH_COMPLETION_COMPAT_DIR -a -r $BASH_COMPLETION_COMPAT_DIR -a \
-     -x $BASH_COMPLETION_COMPAT_DIR ]; then
-     for i in $BASH_COMPLETION_COMPAT_DIR/*; do
-         [[ ${i##*/} != @(*~|*.bak|*.swp|\#*\#|*.dpkg*|*.rpm@(orig|new|save)) ]] &&
-         [ \( -f $i -o -h $i \) -a -r $i ] && . $i
+ 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)) \
+             && ( -f $i || -h $i ) && -r $i ]] && . "$i"
      done
  fi
- if [ -d $BASH_COMPLETION_DIR -a -r $BASH_COMPLETION_DIR -a \
-     $BASH_COMPLETION_DIR != $BASH_COMPLETION_COMPAT_DIR -a \
-     -x $BASH_COMPLETION_DIR ]; then
-     for i in $BASH_COMPLETION_DIR/*; do
-         [[ ${i##*/} != @(*~|*.bak|*.swp|\#*\#|*.dpkg*|*.rpm@(orig|new|save)) ]] &&
-         [ \( -f $i -o -h $i \) -a -r $i ] && . $i
+ 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)) \
+             && ( -f $i || -h $i ) && -r $i ]] && . "$i"
      done
  fi
  unset i
  
  # source user completion file
- [ $BASH_COMPLETION != ~/.bash_completion -a -r ~/.bash_completion ] \
+ [[ $BASH_COMPLETION != ~/.bash_completion && -r ~/.bash_completion ]] \
      && . ~/.bash_completion
  unset -f have
- unset UNAME USERLAND default dirnames filenames have nospace bashdefault \
-     plusdirs compopt
+ unset UNAME USERLAND have
  
  set $BASH_COMPLETION_ORIGINAL_V_VALUE
  unset BASH_COMPLETION_ORIGINAL_V_VALUE
diff --combined configure.ac
index c8d66cd,63f0898..8dbf8eb
--- a/configure.ac
+++ b/configure.ac
@@@ -1,5 -1,5 +1,5 @@@
  AC_PREREQ([2.59])
 -AC_INIT([bash-completion], [1.99])
 +AC_INIT([bash-completion], [1.1])
- AM_INIT_AUTOMAKE([foreign -Wall -Werror])
- AC_CONFIG_FILES([Makefile])
+ AM_INIT_AUTOMAKE([foreign dejagnu dist-bzip2 -Wall -Werror])
+ AC_CONFIG_FILES([Makefile test/Makefile])
  AC_OUTPUT

-- 
bash-completion



More information about the Bash-completion-commits mailing list