From 874d24f544783512aa13b6ae624fa090895eff75 Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Tue, 17 Dec 2013 17:00:44 +0100 Subject: [PATCH 1/3] add minial set of bash functions required to make tab completion work for 'older' versions of bash --- minimal_bash_completion.bash | 658 +++++++++++++++++++++++++++++++++++ 1 file changed, 658 insertions(+) create mode 100644 minimal_bash_completion.bash diff --git a/minimal_bash_completion.bash b/minimal_bash_completion.bash new file mode 100644 index 0000000000..b79dba5c0e --- /dev/null +++ b/minimal_bash_completion.bash @@ -0,0 +1,658 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +# +# Modifications by stdweird: +# These are minimal set of functions to make autocompletion and optcomplete work on pre-bash 4.2 +# They are taken from https://github.com/brianzimmer/dotfiles/blob/master/bashcompletion/bash_completion +# which is version 1.3 of the bash_completion project from http://bash-completion.alioth.debian.org/ +# So these too are GPLv2 or later +# The functions are wrapped in conditionals to check if the functions already exist or not (eg set via OS environment) +# + +type _quote_readline_by_ref >& /dev/null +if [ $? -ne 0 ] +then # begin _quote_readline_by_ref conditional + +# 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 +# +# 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 +# +# See also: +# - http://lists.gnu.org/archive/html/bug-bash/2009-03/msg00155.html +# - http://www.mail-archive.com/bash-completion-devel@lists.alioth.\ +# debian.org/msg01944.html +# @param $1 Argument to quote +# @param $2 Name of variable to return result to + +_quote_readline_by_ref() +{ + if [[ ${1:0:1} == "'" ]]; then + if [[ ${BASH_VERSINFO[0]} -ge 4 ]]; then + # Leave out first character + printf -v $2 %s "${1:1}" + else + # Quote word, leaving out first character + printf -v $2 %q "${1:1}" + # Double-quote word (bash-3) + printf -v $2 %q ${!2} + fi + elif [[ ${BASH_VERSINFO[0]} -le 3 && ${1:0:1} == '"' ]]; then + printf -v $2 %q "${1:1}" + else + printf -v $2 %q "$1" + fi + + # 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() + +fi # end _quote_readline_by_ref conditional + +type _expand >& /dev/null +if [ $? -ne 0 ] +then # begin _expand conditional + +# This function expands tildes in pathnames +# +_expand() +{ + # FIXME: Why was this here? + #[ "$cur" != "${cur%\\}" ] && cur="$cur\\" + + # Expand ~username type directory specifications. We want to expand + # ~foo/... to /home/foo/... to avoid problems when $cur starting with + # a tilde is fed to commands and ending up quoted instead of expanded. + + if [[ "$cur" == \~*/* ]]; then + eval cur=$cur + elif [[ "$cur" == \~* ]]; then + cur=${cur#\~} + COMPREPLY=( $( compgen -P '~' -u "$cur" ) ) + [ ${#COMPREPLY[@]} -eq 1 ] && eval COMPREPLY[0]=${COMPREPLY[0]} + return ${#COMPREPLY[@]} + fi +} + +fi # end _expand conditional + +type _get_comp_words_by_ref >& /dev/null +if [ $? -ne 0 ] +then # begin _get_comp_words_by_ref conditional + +# 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, 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 ]] && eval $3=$j + done + else + # No, list of word completions separators hasn't changed; + eval $2=\( \"\${COMP_WORDS[@]}\" \) + fi +} # __reassemble_comp_words_by_ref() + +# 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 +} + +# @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 + + # 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 + cur2=${cur:0:$index} + fi + + local "$2" "$3" "$4" && + _upvars -a${#words[@]} $2 "${words[@]}" -v $3 "$cword" -v $4 "$cur2" +} + + +# 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 +# +# Example usage: +# +# $ _get_comp_words_by_ref -n : cur prev +# +_get_comp_words_by_ref() +{ + 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[@]}" +} + +fi # end _get_comp_words_by_ref conditional + +type _filedir >& /dev/null +if [ $? -ne 0 ] +then # begin _filedir conditional + +# This function turns on "-o filenames" behavior dynamically. It is present +# for bash < 4 reasons. See http://bugs.debian.org/272660#64 for info about +# the bash < 4 compgen hack. +_compopt_o_filenames() +{ + # We test for compopt availability first because directly invoking it on + # bash < 4 at this point may cause terminal echo to be turned off for some + # reason, see https://bugzilla.redhat.com/653669 for more info. + type compopt &>/dev/null && compopt -o filenames 2>/dev/null || \ + compgen -f /non-existing-dir/ >/dev/null +} + +# Perform tilde (~) completion +# @return True (0) if completion needs further processing, +# False (> 0) if tilde is followed by a valid username, completions +# are put in COMPREPLY and no further processing is necessary. +_tilde() { + local result=0 + # Does $1 start with tilde (~) and doesn't contain slash (/)? + if [[ ${1:0:1} == "~" && $1 == ${1//\/} ]]; then + _compopt_o_filenames + # Try generate username completions + COMPREPLY=( $( compgen -P '~' -u "${1#\~}" ) ) + result=${#COMPREPLY[@]} + fi + return $result +} + +# This function performs file and directory completion. It's better than +# simply using 'compgen -f', because it honours spaces in filenames. +# @param $1 If `-d', complete only on directories. Otherwise filter/pick only +# completions with `.$1' and the uppercase version of it as file +# extension. +# +_filedir() +{ + local i IFS=$'\n' xspec + + _tilde "$cur" || return 0 + + local -a toks + local quoted tmp + + _quote_readline_by_ref "$cur" quoted + toks=( ${toks[@]-} $( + 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 + # Munge xspec to contain uppercase version too + [[ ${BASH_VERSINFO[0]} -ge 4 ]] && \ + xspec=${1:+"!*.@($1|${1^^})"} || \ + xspec=${1:+"!*.@($1|$(printf %s $1 | tr '[:lower:]' '[:upper:]'))"} + toks=( ${toks[@]-} $( compgen -f -X "$xspec" -- $quoted) ) + fi + [ ${#toks[@]} -ne 0 ] && _compopt_o_filenames + + COMPREPLY=( "${COMPREPLY[@]}" "${toks[@]}" ) +} # _filedir() + +fi # end _filedir conditional + +type _known_hosts >& /dev/null +if [ $? -ne 0 ] +then # begin _known_hosts conditional + +# NOTE: Using this function as a helper function is deprecated. Use +# `_known_hosts_real' instead. +_known_hosts() +{ + local options + COMPREPLY=() + + # 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 :)" +} # _known_hosts() + +# Expand variable starting with tilde (~) +# We want to expand ~foo/... to /home/foo/... to avoid problems when +# word-to-complete starting with a tilde is fed to commands and ending up +# quoted instead of expanded. +# 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() + +# 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: +# +# # Remove colon (:) from list of word completion separators +# COMP_WORDBREAKS=${COMP_WORDBREAKS//:} +# +# 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 +} # __ltrim_colon_completions() + +# Helper function for completing _known_hosts. +# This function performs host completion based on ssh's config and known_hosts +# files, as well as hostnames reported by avahi-browse if +# COMP_KNOWN_HOSTS_WITH_AVAHI is set to a non-empty value. 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 +# -c Use `:' suffix +# -F configfile Use `configfile' for configuration settings +# -p PREFIX Use PREFIX +# Return: Completions, starting with CWORD, are added to COMPREPLY[] +_known_hosts_real() +{ + local configfile flag prefix + local cur curd awkcur user suffix aliases i host + local -a kh khd config + + local OPTIND=1 + while getopts "acF:p:" flag "$@"; do + case $flag in + a) aliases='yes' ;; + c) suffix=':' ;; + F) configfile=$OPTARG ;; + p) prefix=$OPTARG ;; + esac + done + [ $# -lt $OPTIND ] && echo "error: $FUNCNAME: missing mandatory argument CWORD" + cur=${!OPTIND}; let "OPTIND += 1" + [ $# -ge $OPTIND ] && echo "error: $FUNCNAME("$@"): unprocessed arguments:"\ + $(while [ $# -ge $OPTIND ]; do printf '%s\n' ${!OPTIND}; shift; done) + + [[ $cur == *@* ]] && user=${cur%@*}@ && cur=${cur#*@} + kh=() + + # ssh config files + if [ -n "$configfile" ]; then + [ -r "$configfile" ] && + config=( "${config[@]}" "$configfile" ) + else + for i in /etc/ssh/ssh_config "${HOME}/.ssh/config" \ + "${HOME}/.ssh2/config"; do + [ -r $i ] && config=( "${config[@]}" "$i" ) + done + fi + + # Known hosts files from configs + if [ ${#config[@]} -gt 0 ]; then + local OIFS=$IFS IFS=$'\n' + 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 + + if [ -z "$configfile" ]; then + # Global and user known_hosts files + for i in /etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts2 \ + /etc/known_hosts /etc/known_hosts2 ~/.ssh/known_hosts \ + ~/.ssh/known_hosts2; do + [ -r $i ] && kh=( "${kh[@]}" $i ) + done + for i in /etc/ssh2/knownhosts ~/.ssh2/hostkeys; do + [ -d $i ] && khd=( "${khd[@]}" $i/*pub ) + done + fi + + # If we have known_hosts files to use + if [[ ${#kh[@]} -gt 0 || ${#khd[@]} -gt 0 ]]; then + # Escape slashes and dots in paths for awk + awkcur=${cur//\//\\\/} + awkcur=${awkcur//\./\\\.} + curd=$awkcur + + if [[ "$awkcur" == [0-9]*[.:]* ]]; then + # Digits followed by a dot or a colon - just search for that + awkcur="^$awkcur[.:]*" + elif [[ "$awkcur" == [0-9]* ]]; then + # Digits followed by no dot or colon - search for digits followed + # by a dot or a colon + awkcur="^$awkcur.*[.:]" + elif [ -z "$awkcur" ]; then + # A blank - search for a dot, a colon, or an alpha character + awkcur="[a-z.:]" + else + awkcur="^$awkcur" + fi + + if [ ${#kh[@]} -gt 0 ]; then + # FS needs to look for a comma separated list + COMPREPLY=( "${COMPREPLY[@]}" $( awk 'BEGIN {FS=","} + /^\s*[^|\#]/ {for (i=1; i<=2; ++i) { \ + sub(" .*$", "", $i); \ + sub("^\\[", "", $i); sub("\\](:[0-9]+)?$", "", $i); \ + if ($i ~ /'"$awkcur"'/) {print $i} \ + }}' "${kh[@]}" 2>/dev/null ) ) + fi + if [ ${#khd[@]} -gt 0 ]; then + # Needs to look for files called + # .../.ssh2/key_22_.pub + # dont fork any processes, because in a cluster environment, + # there can be hundreds of hostkeys + for i in "${khd[@]}" ; do + if [[ "$i" == *key_22_$curd*.pub && -r "$i" ]]; then + host=${i/#*key_22_/} + host=${host/%.pub/} + COMPREPLY=( "${COMPREPLY[@]}" $host ) + fi + done + fi + + # apply suffix and prefix + for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do + COMPREPLY[i]=$prefix$user${COMPREPLY[i]}$suffix + done + fi + + # 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 desired and it's available. + if [[ ${COMP_KNOWN_HOSTS_WITH_AVAHI:-} ]] && \ + type avahi-browse &>/dev/null; then + # 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 wasn't available (even if mentioned in the manpage) some + # time ago, so... + 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" ) ) + fi + + __ltrim_colon_completions "$prefix$user$cur" + + return 0 +} # _known_hosts_real() + +fi # end _known_hosts conditional From e9f3578958afec773d1c3babcb82f5d883f2b8c5 Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Tue, 17 Dec 2013 17:05:44 +0100 Subject: [PATCH 2/3] make sure minimal_bash_completion.bash is installed alongside eb command --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b461cbbdeb..b3a44f0f4f 100644 --- a/setup.py +++ b/setup.py @@ -92,7 +92,7 @@ def find_rel_test(): packages = easybuild_packages, package_dir = {'test.framework': "test/framework"}, package_data = {"test.framework": find_rel_test()}, - scripts = ["eb", "optcomplete.bash"], + scripts = ["eb", "optcomplete.bash", "minimal_bash_completion.bash"], data_files = [ ('easybuild', ["easybuild/easybuild_config.py"]), ], From 3e656fbcb6ce00becb248de0561df02b4502d30b Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Tue, 17 Dec 2013 17:19:13 +0100 Subject: [PATCH 3/3] make sure optcomplete.bash and minimal_bash_completion.bash are installed alongside 'eb' command --- MANIFEST.in | 2 ++ optcomplete.bash | 0 setup.py | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) mode change 100755 => 100644 optcomplete.bash diff --git a/MANIFEST.in b/MANIFEST.in index 28383096b0..901ae1a0f7 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,6 @@ include eb +include minimal_bash_completion.bash +include optcomplete.bash recursive-include easybuild *py recursive-include test *py *eb recursive-include test/framework/modules * diff --git a/optcomplete.bash b/optcomplete.bash old mode 100755 new mode 100644 diff --git a/setup.py b/setup.py index b3a44f0f4f..01dfb7d25d 100644 --- a/setup.py +++ b/setup.py @@ -92,7 +92,7 @@ def find_rel_test(): packages = easybuild_packages, package_dir = {'test.framework': "test/framework"}, package_data = {"test.framework": find_rel_test()}, - scripts = ["eb", "optcomplete.bash", "minimal_bash_completion.bash"], + scripts = ["eb", "optcomplete.bash", "minimal_bash_completion.bash"]), data_files = [ ('easybuild', ["easybuild/easybuild_config.py"]), ],