From f9748115fb4b2950fb4df7535fb723c4affde078 Mon Sep 17 00:00:00 2001 From: David Paleino Date: Wed, 16 Jun 2010 18:20:29 +0200 Subject: Imported Upstream version 1.2 --- bash_completion | 1524 +++++++++++++++++++++++++++---------------------------- 1 file changed, 749 insertions(+), 775 deletions(-) (limited to 'bash_completion') diff --git a/bash_completion b/bash_completion index e1c40544..e7a4b6ec 100644 --- a/bash_completion +++ b/bash_completion @@ -1,9 +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 -# © 2009, Bash Completion Maintainers +# © 2009-2010, Bash Completion Maintainers # # # This program is free software; you can redistribute it and/or modify @@ -56,33 +55,6 @@ case ${UNAME} in *) 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 @@ complete -d pushd # # 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 @@ complete -f -X '!*.@(dvi|DVI)?(.@(gz|Z|bz2))' kdvi 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 @@ complete -f -X '!*.@(mp3|MP3|ogg|OGG|pls|m3u)' gqmpeg freeamp 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 @@ 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 '!*.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 @@ complete -b builtin 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 @@ 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,490 @@ dequote() 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 + +# Assign variable one scope above the caller +# Usage: local "$1" && _upvar $1 "value(s)" +# Param: $1 Variable name to assign value to +# Param: $* Value(s) to assign. If multiple values, an array is +# assigned, otherwise a single value is assigned. +# NOTE: For assigning multiple variables, use '_upvars'. Do NOT +# 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() { + if unset -v "$1"; then # Unset & validate varname + if (( $# == 2 )); then + eval $1=\"\$2\" # Return single value + else + eval $1=\(\"\${@:2}\"\) # Return array + fi + fi +} + + +# Assign variables one scope above the caller +# Usage: local varname [varname ...] && +# _upvars [-v varname value] | [-aN varname [value ...]] ... +# Available OPTIONS: +# -aN Assign next N values to varname as array +# -v Assign single value to varname +# Return: 1 if error occurs +# See: http://fvue.nl/wiki/Bash:_Passing_variables_by_reference +_upvars() { + if ! (( $# )); then + echo "${FUNCNAME[0]}: usage: ${FUNCNAME[0]} [-v varname"\ + "value] | [-aN varname [value ...]] ..." 1>&2 + return 2 + fi + while (( $# )); do + case $1 in + -a*) + # Error checking + [[ ${1#-a} ]] || { echo "bash: ${FUNCNAME[0]}: \`$1': missing"\ + "number specifier" 1>&2; return 1; } + printf %d "${1#-a}" &> /dev/null || { echo "bash:"\ + "${FUNCNAME[0]}: \`$1': invalid number specifier" 1>&2 + return 1; } + # Assign array of -aN elements + [[ "$2" ]] && unset -v "$2" && eval $2=\(\"\${@:3:${1#-a}}\"\) && + shift $((${1#-a} + 2)) || { echo "bash: ${FUNCNAME[0]}:"\ + "\`$1${2+ }$2': missing argument(s)" 1>&2; return 1; } + ;; + -v) + # Assign single value + [[ "$2" ]] && unset -v "$2" && eval $2=\"\$3\" && + shift 3 || { echo "bash: ${FUNCNAME[0]}: $1: missing"\ + "argument(s)" 1>&2; return 1; } + ;; + *) + echo "bash: ${FUNCNAME[0]}: $1: invalid option" 1>&2 + return 1 ;; + esac + done +} + + +# 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() { + 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 -_get_cword() -{ - if [ -n "$bash4" ] ; then - __get_cword4 "$@" + # 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 + cur2=${words[cword]} else - __get_cword3 + cur2=${cur:0:$index} fi -} # _get_cword() + + local "$2" "$3" "$4" && + _upvars -a${#words[@]} $2 "${words[@]}" -v $3 "$cword" -v $4 "$cur2" +} -# 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] [VARNAMES] +# Available VARNAMES: +# cur Return cur via $cur +# prev Return prev via $prev +# words Return words via $words +# cword Return cword via $cword +# +# Available 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. +# -c VARNAME Return cur via $VARNAME +# -p VARNAME Return prev via $VARNAME +# -w VARNAME Return words via $VARNAME +# -i VARNAME Return cword via $VARNAME # -# $ a b:c -# COMP_CWORD: 1 -# COMP_CWORDS: -# 0: a -# 1: b:c +# Example usage: # -# See also: -# _get_cword, main routine -# __get_cword4, bash-4 variant +# $ _get_comp_words_by_ref -n : cur prev # -__get_cword3() +_get_comp_words_by_ref() { - if [[ "${#COMP_WORDS[COMP_CWORD]}" -eq 0 ]] || [[ "$COMP_POINT" == "${#COMP_LINE}" ]]; then - printf "%s" "${COMP_WORDS[COMP_CWORD]}" + local exclude flag i OPTIND=1 + local cur cword words=() + local upargs=() upvars=() vcur vcword vprev vwords + + while getopts "c:i:n:p:w:" flag "$@"; do + case $flag in + c) vcur=$OPTARG ;; + i) vcword=$OPTARG ;; + n) exclude=$OPTARG ;; + p) vprev=$OPTARG ;; + w) vwords=$OPTARG ;; + esac + done + while [[ $# -ge $OPTIND ]]; do + case ${!OPTIND} in + cur) vcur=cur ;; + prev) vprev=prev ;; + cword) vcword=cword ;; + words) vwords=words ;; + *) echo "bash: $FUNCNAME(): \`${!OPTIND}': unknown argument" \ + 1>&2; return 1 + esac + let "OPTIND += 1" + done + + __get_cword_at_cursor_by_ref "$exclude" words cword cur + + [[ $vcur ]] && { upvars+=("$vcur" ); upargs+=(-v $vcur "$cur" ); } + [[ $vcword ]] && { upvars+=("$vcword"); upargs+=(-v $vcword "$cword"); } + [[ $vprev ]] && { upvars+=("$vprev" ); upargs+=(-v $vprev + "${words[cword - 1]}"); } + [[ $vwords ]] && { upvars+=("$vwords"); upargs+=(-a${#words[@]} $vwords + "${words[@]}"); } + + (( ${#upvars[@]} )) && local "${upvars[@]}" && _upvars "${upargs[@]}" +} + + +# 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 "=:". +# @deprecated Use `_get_comp_words_by_ref cur' instead +# @see _get_comp_words_by_ref() +_get_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. +# @deprecated Use `_get_comp_words_by_ref cur prev' instead +# @see _get_comp_words_by_ref() # -# $ a b:c -# 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 returns "b instead of b) - # but still fails quoted spaces (e.g. $ a "b c 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@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" - # 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" 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/ # Becomes: foo 'a&b/f' + # $ foo a'&b/ # 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 + # a\ b/ a\$b/ + # + # whereas with `-o filenames' active the completions look + # like: + # + # $ ls a + # 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 +710,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 +730,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 +799,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 +867,7 @@ _expand() # 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 +879,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 +891,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/^$//")' \ - -- "$cur" ) ) + COMPREPLY=( $( compgen -X '' -W '$( command ps -efo comm | \ + sed -e 1d -e "s:.*/::" -e "s/^-//" | sort -u )' -- "$cur" ) ) } || _pnames() { @@ -582,11 +907,9 @@ _pnames() # 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/^$//")' \ - -- "$cur" ) ) + COMPREPLY=( $( compgen -X '' -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 +931,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 +948,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 +965,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 +974,41 @@ _modules() _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'. + # 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'. + # 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'. + # + # 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 +1016,32 @@ _usergroup() # _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 +1049,47 @@ _shells() # - 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 +1098,7 @@ _count_args() _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,411 +1106,29 @@ _pci_ids() _usb_ids() { COMPREPLY=( ${COMPREPLY[@]:-} $( compgen -W \ - "$( PATH="$PATH:/sbin" lsusb | awk '{print $6}' )" -- "$cur" ) ) -} - -# 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 - -# 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 - - 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 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 - - if [[ $COMP_CWORD -eq 1 ]] && [[ $prev == "service" ]]; then - _services - else - COMPREPLY=( $( compgen -W '`sed -ne "y/|/ /; \ - s/^.*\(U\|msg_u\)sage.*{\(.*\)}.*$/\1/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 - - $split && return 0 - - # 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 - - COMPREPLY=() - cur=`_get_cword` - cur=${cur//\\\\/} - prev=${COMP_WORDS[COMP_CWORD-1]} - - _split_longopt && split=true - - if [[ "$prev" == --reference ]]; then - _filedir - return 0 - fi - - $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 - - # 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 ) ) - else - _filedir || return 0 - fi - - 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" ) ) - - return 0 - } - complete -F _umount $dirnames umount - - # 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 - - 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 - - COMPREPLY=() - cur=`_get_cword` - - _installed_modules "$cur" - return 0 - } - complete -F _rmmod rmmod - - # 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 - - # do filename completion if we're giving a path to a module - if [[ "$cur" == */* ]]; then - _filedir '@(?(k)o?(.gz))' - return 0 - fi - - 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 + "$( PATH="$PATH:/sbin" lsusb | awk '{print $6}' )" -- "$cur" ) ) } -# renice(8) completion -# -_renice() +# CD device names +_cd_devices() { - local command cur curopt i - - COMPREPLY=() - cur=`_get_cword` - command=$1 - - i=0 - # walk back through command line and find last option - while [ $i -le $COMP_CWORD -a ${#COMPREPLY[@]} -eq 0 ]; do - curopt=${COMP_WORDS[COMP_CWORD-$i]} - case "$curopt" in - -u) - COMPREPLY=( $( compgen -u -- "$cur" ) ) - ;; - -g) - _pgids - ;; - -p|$command) - _pids - ;; - esac - i=$(( ++i )) - done + COMPREPLY=( "${COMPREPLY[@]}" + $( compgen -f -d -X "!*/?([amrs])cd*" -- "${cur:-/dev/}" ) ) } -complete -F _renice renice -# kill(1) completion -# -_kill() +# DVD device names +_dvd_devices() { - local cur - - COMPREPLY=() - cur=`_get_cword` - - if [ $COMP_CWORD -eq 1 ] && [[ "$cur" == -* ]]; then - # return list of available signals - _signals - else - # return list of available PIDs - _pids - fi + COMPREPLY=( "${COMPREPLY[@]}" + $( compgen -f -d -X "!*/?(r)dvd*" -- "${cur:-/dev/}" ) ) } -complete -F _kill kill - -# killall(1) (Linux and FreeBSD) and pkill(1) completion. -# -[ $UNAME = Linux -o $UNAME = FreeBSD ] || have pkill && -_killall() -{ - local cur - - COMPREPLY=() - cur=`_get_cword` - - if [ $COMP_CWORD -eq 1 ] && [[ "$cur" == -* ]]; then - _signals - else - _pnames - fi - - return 0 -} -[ $UNAME = Linux -o $UNAME = FreeBSD ] && complete -F _killall killall -have pkill && complete -F _killall pkill - -# pgrep(1) completion. -# -[ $UNAME = Linux ] || have pgrep && -_pgrep() -{ - local cur - COMPREPLY=() - cur=`_get_cword` - - _pnames - - return 0 -} -have pgrep && complete -F _pgrep pgrep - -# Linux pidof(8) completion. -[ $UNAME = Linux ] && complete -F _pgrep pidof - -# Red Hat & Debian GNU/Linux if{up,down} completion -# -[ $USERLAND = GNU ] && { have ifup || have ifdown; } && -_ifupdown() -{ - local cur - - COMPREPLY=() - cur=`_get_cword` - - if [ $COMP_CWORD -eq 1 ]; then - _configured_interfaces - COMPREPLY=( $(compgen -W '${COMPREPLY[@]}' -- "$cur") ) - fi - - return 0 -} && -complete -F _ifupdown ifup ifdown -[ $USERLAND = GNU ] && have ifstatus && complete -F _ifupdown ifstatus - -# Linux ipsec(8) completion (for FreeS/WAN) -# -[ $UNAME = Linux ] && have ipsec && -_ipsec() -{ - local cur - - COMPREPLY=() - cur=`_get_cword` - - - 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" ) ) - return 0 - fi - - case ${COMP_WORDS[1]} in - auto) - COMPREPLY=( $( compgen -W '--asynchronous --up --add --delete \ - --replace --down --route --unroute \ - --ready --status --rereadsecrets' \ - -- "$cur" ) ) - ;; - manual) - COMPREPLY=( $( compgen -W '--up --down --route --unroute \ - --union' -- "$cur" ) ) - ;; - ranbits) - COMPREPLY=( $( compgen -W '--quick --continuous --bytes' \ - -- "$cur" ) ) - ;; - setup) - COMPREPLY=( $( compgen -W '--start --stop --restart' -- "$cur" ) ) - ;; - *) - ;; - esac +# start of section containing completion functions for external programs - return 0 -} && -complete -F _ipsec ipsec +# 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 # This function provides simple user@host completion # @@ -1132,7 +1136,7 @@ _user_at_host() { local cur COMPREPLY=() - cur=`_get_cword` + _get_comp_words_by_ref -n : cur if [[ $cur == *@* ]]; then _known_hosts_real "$cur" @@ -1142,7 +1146,7 @@ _user_at_host() { 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 +1157,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 +1176,7 @@ _known_hosts() _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 +1191,7 @@ _known_hosts_real() [ $# -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 +1209,26 @@ _known_hosts_real() 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 +1243,6 @@ _known_hosts_real() 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 +1253,7 @@ _known_hosts_real() 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 +1275,7 @@ _known_hosts_real() 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 +1289,13 @@ _known_hosts_real() # dont fork any processes, because in a cluster environment, # there can be hundreds of hostkeys for i in "${khd[@]}" ; do - if [[ "$i" == *key_22_$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,23 +1303,47 @@ _known_hosts_real() 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. # _cd() { - local IFS=$'\t\n' cur=`_get_cword` i j k + local cur IFS=$'\t\n' i j k + _get_comp_words_by_ref cur # try to allow variable completion if [[ "$cur" == ?(\\)\$* ]]; then @@ -1348,7 +1356,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 +1380,7 @@ _cd() 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 +1388,9 @@ _cd() 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 +1417,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 @@ -1438,7 +1446,7 @@ _command_offset() COMP_CWORD=$(( $COMP_CWORD - $word_offset )) COMPREPLY=() - cur=`_get_cword` + _get_comp_words_by_ref cur if [[ $COMP_CWORD -eq 0 ]]; then COMPREPLY=( $( compgen -c -- "$cur" ) ) @@ -1479,21 +1487,21 @@ _command_offset() [ ${#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() { local cur prev - cur=`_get_cword` - prev=${COMP_WORDS[COMP_CWORD-1]} + _get_comp_words_by_ref cur prev if _split_longopt; then case "$prev" in @@ -1508,8 +1516,8 @@ _longopt() 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,68 +1527,33 @@ _longopt() } # 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 -# look(1) completion -# -have look && -_look() -{ - local cur - - COMPREPLY=() - cur=`_get_cword` - - if [ $COMP_CWORD = 1 ]; then - COMPREPLY=( $( compgen -W '$(look "$cur" 2>/dev/null)' ) ) - fi -} && -complete -F _look $default look - -# id(1) completion -# -have id && -_id() -{ - local cur - - COMPREPLY=() - cur=`_get_cword` - - if [[ "$cur" == -* ]]; then - COMPREPLY=( $( compgen -W '-a -g --group -G --groups -n --name\ - -r --real -u --user --help --version' -- "$cur" ) ) - else - COMPREPLY=( $( compgen -u "$cur" ) ) - fi -} && -complete -F _id id - _filedir_xspec() { local IFS cur xspec IFS=$'\t\n' COMPREPLY=() - cur=`_get_cword` + _get_comp_words_by_ref cur _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 +1565,7 @@ _filedir_xspec() 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 +1573,14 @@ _filedir_xspec() 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 +1592,42 @@ list=( $( sed -ne '/^# START exclude/,/^# FINISH exclude/p' $BASH_COMPLETION | \ 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 -- cgit v1.2.1