summaryrefslogtreecommitdiff
path: root/bash_completion
diff options
context:
space:
mode:
authorFreddy Vulto <fvulto@gmail.com>2009-12-24 10:00:29 +0100
committerFreddy Vulto <fvulto@gmail.com>2009-12-24 10:00:29 +0100
commitc9c98da36edeb9305e24e7ea2d8ccf8b0934ce70 (patch)
tree08dbfe60c72122d4298c7e214faa918a02a8b63d /bash_completion
parentf9db6abdc1743d81c77e0b3eb051e3b69017f315 (diff)
downloadbash-completion-c9c98da36edeb9305e24e7ea2d8ccf8b0934ce70.tar.gz
Fixed `quote_readline'.
This fixes completing filenames containing single quote (') on bash-4. Also added emulation of `-o filenames' to _filedir. Added tests for _filedir. Fixed array assignment within __reassemble_comp_words_by_ref().
Diffstat (limited to 'bash_completion')
-rw-r--r--bash_completion133
1 files changed, 106 insertions, 27 deletions
diff --git a/bash_completion b/bash_completion
index 1b103d71..d6374f7c 100644
--- a/bash_completion
+++ b/bash_completion
@@ -188,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 [ ${BASH_VERSINFO[0]} -ge 4 ]; 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()
@@ -224,8 +219,6 @@ __reassemble_comp_words_by_ref() {
if [[ $1 ]]; then
# Yes, exclude word separator characters;
# Exclude only those characters, which were really included
- # NOTE: On bash-3, `COMP_WORDBREAKS' is empty which is ok; no
- # additional word breaking is done on bash-3.
exclude="${1//[^$COMP_WORDBREAKS]}"
fi
@@ -240,7 +233,7 @@ __reassemble_comp_words_by_ref() {
[ $j -ge 2 ] && ((j--))
# Append word separator to current word
ref="$2[$j]"
- eval $2[$j]=\""${!ref}${COMP_WORDS[$i]}"\"
+ 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
@@ -248,7 +241,7 @@ __reassemble_comp_words_by_ref() {
done
# Append word to current word
ref="$2[$j]"
- eval $2[$j]=\""${!ref}${COMP_WORDS[$i]}"\"
+ eval $2[$j]=\${!ref}\${COMP_WORDS[i]}
# Indicate new cword
[ $i = $COMP_CWORD ] && eval $3=$j
done
@@ -295,7 +288,7 @@ _get_cword()
"${#cur}" -ge ${#words[i]} &&
# $cur doesn't match cword?
"${cur:0:${#words[i]}}" != "${words[i]}"
- ]]; do
+ ]]; do
# Strip first character
cur="${cur:1}"
# Decrease cursor position
@@ -369,6 +362,46 @@ __ltrim_colon_completions() {
} # __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 bash <= 3 and argument starts with single quote ('), double-escape
+# if [[ ${BASH_VERSINFO[0]} -le 3 && ${1:0:1} == "'" ]]; then
+# local t
+# printf -v t %q "${1:1}"
+# printf -v $2 %q "$t"
+# else
+# printf -v $2 %q "$1"
+# fi
+ 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
+ elif [[ ${BASH_VERSINFO[0]} -le 3 && ${1:0:1} == '"' ]]; then
+ printf -v $2 %q "${1:1}"
+ else
+ printf -v $2 %q "$1"
+ fi
+} # _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,
@@ -376,12 +409,12 @@ __ltrim_colon_completions() {
#
_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 `printf '%s\n' $tmp',
# and everything works again. If this bug
@@ -393,28 +426,74 @@ _filedir()
# 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")" | {
+ compgen -d -- "$quoted" | {
while read -r tmp; do
printf '%s\n' $tmp
done
}
))
+ # On bash-3, special characters need to be escaped extra. This is
+ # unless the first character is a single quote ('). If the single
+ # quote appears further down the string, bash default completion also
+ # fails, e.g.:
+ #
+ # $ ls 'a&b/'
+ # f
+ # $ foo 'a&b/<TAB> # Becomes: foo 'a&b/f'
+ # $ foo a'&b/<TAB> # Nothing happens
+ #
if [[ "$1" != -d ]]; then
xspec=${1:+"!*.$1"}
- toks=( ${toks[@]-} $(
- compgen -f -X "$xspec" -- "$(quote_readline "$cur")" | {
- while read -r tmp; do
- [ -n $tmp ] && printf '%s\n' $tmp
+ 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?
+ [[ "$(complete -p ${COMP_WORDS[0]})" == *"-o filenames"* ]] || {
+ # No, `-o filenames' isn't set;
+ # Emulate `-o filenames'
+ # NOTE: A side-effect of emulating `-o filenames' is that backslash escape
+ # characters are visible within the list of presented completions, e.g.
+ # the completions look like:
+ #
+ # $ foo a<TAB>
+ # a\ b/ a\$b/
+ #
+ # whereas with `-o filenames' active the completions look like:
+ #
+ # $ ls a<TAB>
+ # a b/ a$b/
+ #
+ for ((i=0; i < ${#toks[@]}; i++)); do
+ # If directory exists, append slash (/)
+ if [[ ${cur:0:1} != "'" ]]; then
+ [[ -d ${toks[i]} ]] && toks[i]="${toks[i]}"/
+ if [[ ${cur:0:1} == '"' ]]; then
+ toks[i]=${toks[i]//\\/\\\\}
+ toks[i]=${toks[i]//\"/\\\"}
+ toks[i]=${toks[i]//\$/\\\$}
+ else
+ toks[i]=$(printf %q ${toks[i]})
+ fi
+ fi
done
}
- ))
+ fi
fi
COMPREPLY=( "${COMPREPLY[@]}" "${toks[@]}" )
- [ ${#COMPREPLY[@]} -ne 0 ] && type compopt &>/dev/null && \
- compopt -o filenames
} # _filedir()