summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFreddy Vulto <fvulto@gmail.com>2010-06-09 22:37:02 +0200
committerFreddy Vulto <fvulto@gmail.com>2010-06-09 22:37:02 +0200
commita8dd58cfa9a9e409053628c62f36880928b3e1c6 (patch)
tree4567496c8bdd7866d378597af6a2e048648842e6
parentf894f07500af8734a3c855f381c8382751463a45 (diff)
downloadbash-completion-a8dd58cfa9a9e409053628c62f36880928b3e1c6.tar.gz
Added `_upvars' and `_upvar'.
These helper methods aid in passing variables by reference.
-rw-r--r--bash_completion208
-rw-r--r--contrib/screen6
-rw-r--r--test/unit/_get_comp_words_by_ref.exp2
3 files changed, 123 insertions, 93 deletions
diff --git a/bash_completion b/bash_completion
index 0d329f8a..e427bd2f 100644
--- a/bash_completion
+++ b/bash_completion
@@ -200,6 +200,83 @@ dequote()
}
+# 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
+_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; }
+ ;;
+ --help) echo "\
+Usage: local varname [varname ...] &&
+ ${FUNCNAME[0]} [-v varname value] | [-aN varname [value ...]] ...
+Available OPTIONS:
+-aN VARNAME [value ...] assign next N values to varname as array
+-v VARNAME value assign single value to varname
+--help display this help and exit
+--version output version information and exit"
+ return 0 ;;
+ --version) echo "\
+${FUNCNAME[0]}-0.9.dev
+Copyright (C) 2010 Freddy Vulto
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law."
+ return 0 ;;
+ *)
+ 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
@@ -263,30 +340,10 @@ __reassemble_comp_words_by_ref() {
# @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
-}
-
-
-# @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
+ local cword words=()
__reassemble_comp_words_by_ref "$1" words cword
- local i
+ local i cur2
local cur="$COMP_LINE"
local index="$COMP_POINT"
for (( i = 0; i <= cword; ++i )); do
@@ -314,13 +371,13 @@ ___get_cword_at_cursor_by_ref() {
if [[ "${words[cword]:0:${#cur}}" != "$cur" ]]; then
# We messed up. At least return the whole word so things keep working
- eval $4=\"\${words[cword]}\"
+ cur2=${words[cword]}
else
- eval $4=\"\${cur:0:\$index}\"
+ cur2=${cur:0:$index}
fi
- eval $2=\( \"\${words[@]}\" \)
- eval $3=\$cword
+ local "$2" "$3" "$4" &&
+ _upvars -a${#words[@]} $2 "${words[@]}" -v $3 "$cword" -v $4 "$cur2"
}
@@ -332,10 +389,10 @@ ___get_cword_at_cursor_by_ref() {
# Also one is able to cross over possible wordbreak characters.
# Usage: _get_comp_words_by_ref [OPTIONS] [VARNAMES]
# Available VARNAMES:
-# cur Return cur within varname "cur"
-# prev Return prev within varname "prev"
-# words Return words within varname "words"
-# cword Return cword within varname "cword"
+# 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
@@ -344,81 +401,52 @@ ___get_cword_at_cursor_by_ref() {
# 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 within specified VARNAME
-# -p VARNAME Return prev within specified VARNAME
-# -w VARNAME Return words within specified VARNAME
-# -i VARNAME Return cword within specified VARNAME
+# -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
#
-# @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 names less likely.
- local __words __cword __cur
- local __var_cur __var_prev __var_words __var_cword
-
- __get_comp_words_by_ref \
- __words __cword __cur \
- __var_cur __var_prev __var_words __var_cword "$@"
-
- [[ $__var_cur ]] && eval $__var_cur=\$__cur
- [[ $__var_prev ]] && ((__cword)) && eval $__var_prev=\${__words[__cword - 1]}
- [[ $__var_words ]] && eval $__var_words=\${__words[@]}
- [[ $__var_cword ]] && eval $__var_cword=\$__cword
- return 0
-}
-
-
-# @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 var_cur Name of variable to return current word to complete to
-# @param $5 var_prev Name of variable to return previous word to complete to
-# @param $6 var_words Name of variable to return words to complete to
-# @param $7 var_cword Name of variable to return index of words to complete 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()
+_get_comp_words_by_ref()
{
- local exclude flag i OPTIND=8 # Skip first seven arguments
- local cword words cur
- local var_cur var_cword var_prev var_words
+ 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) var_cur=$OPTARG ;;
- i) var_cword=$OPTARG ;;
+ c) vcur=$OPTARG ;;
+ i) vcword=$OPTARG ;;
n) exclude=$OPTARG ;;
- p) var_prev=$OPTARG ;;
- w) var_words=$OPTARG ;;
+ p) vprev=$OPTARG ;;
+ w) vwords=$OPTARG ;;
esac
done
while [[ $# -ge $OPTIND ]]; do
case ${!OPTIND} in
- cur) var_cur=cur ;;
- prev) var_prev=prev ;;
- cword) var_cword=cword ;;
- words) var_words=words ;;
- *) echo "error: $FUNCNAME(): unknown argument: ${!OPTIND}"
+ 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
- eval $1=\( \"\${words[@]}\" \)
- eval $2=\$cword
- eval $3=\$cur
- eval $4=\$var_cur
- eval $5=\$var_prev
- eval $6=\${var_words[@]}
- eval $7=\$var_cword
+ [[ $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[@]}"
}
@@ -436,7 +464,8 @@ __get_comp_words_by_ref()
# 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 cword words
@@ -489,7 +518,8 @@ _get_cword()
# 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()
+# @deprecated Use `_get_comp_words_by_ref cur prev' instead
+# @see _get_comp_words_by_ref()
#
_get_pword()
{
@@ -797,7 +827,7 @@ __expand_tilde_by_ref() {
# becomes "~a". Double quotes allow eval.
# 2: Remove * before the first slash (/), i.e. "~a/b"
# becomes "b". Single quotes prevent eval.
- # +-----1----+ +---2----+
+ # +-----1----+ +---2----+
eval $1="${!1/%\/*}"/'${!1#*/}'
else
# No, $1 doesn't contain slash
@@ -1593,7 +1623,7 @@ complete -F _known_hosts traceroute traceroute6 tracepath tracepath6 ping \
#
_cd()
{
- local IFS=$'\t\n' i j k
+ local cur IFS=$'\t\n' i j k
_get_comp_words_by_ref cur
# try to allow variable completion
diff --git a/contrib/screen b/contrib/screen
index a0df1227..721c076c 100644
--- a/contrib/screen
+++ b/contrib/screen
@@ -16,12 +16,12 @@ _screen_sessions()
} &&
_screen()
{
- local cur prev preprev
+ local cur prev words cword
COMPREPLY=()
- _get_comp_words_by_ref cur prev preprev
+ _get_comp_words_by_ref cur prev words cword
- case $preprev in
+ case ${words[cwords-2]} in
-[dD])
_screen_sessions
return 0
diff --git a/test/unit/_get_comp_words_by_ref.exp b/test/unit/_get_comp_words_by_ref.exp
index 1016d997..22561e7f 100644
--- a/test/unit/_get_comp_words_by_ref.exp
+++ b/test/unit/_get_comp_words_by_ref.exp
@@ -341,7 +341,7 @@ sync_after_int
set test {unknown argument should raise error}
set cmd {_get_comp_words_by_ref dummy}
-assert_bash_list {"error: __get_comp_words_by_ref(): unknown argument: dummy"} $cmd $test
+assert_bash_list {"bash: _get_comp_words_by_ref(): `dummy': unknown argument"} $cmd $test
sync_after_int