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().
This commit is contained in:
Freddy Vulto 2009-12-24 10:00:29 +01:00
parent f9db6abdc1
commit c9c98da36e
16 changed files with 788 additions and 27 deletions

View File

@ -188,19 +188,14 @@ quote()
echo \'${1//\'/\'\\\'\'}\' #'# Help vim syntax highlighting echo \'${1//\'/\'\\\'\'}\' #'# Help vim syntax highlighting
} }
# This function quotes the argument in a way so that readline dequoting # @see _quote_readline_by_ref()
# results in the original argument
quote_readline() quote_readline()
{ {
if [ ${BASH_VERSINFO[0]} -ge 4 ]; then local quoted
# This function isn't really necessary on bash 4 _quote_readline_by_ref "$1" ret
# See: http://lists.gnu.org/archive/html/bug-bash/2009-03/msg00155.html printf %s "$ret"
echo "${1}" } # quote_readline()
return
fi
local t="${1//\\/\\\\}"
echo \'${t//\'/\'\\\'\'}\' #'# Help vim syntax highlighting
}
# This function shell-dequotes the argument # This function shell-dequotes the argument
dequote() dequote()
@ -224,8 +219,6 @@ __reassemble_comp_words_by_ref() {
if [[ $1 ]]; then if [[ $1 ]]; then
# Yes, exclude word separator characters; # Yes, exclude word separator characters;
# Exclude only those characters, which were really included # 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]}" exclude="${1//[^$COMP_WORDBREAKS]}"
fi fi
@ -240,7 +233,7 @@ __reassemble_comp_words_by_ref() {
[ $j -ge 2 ] && ((j--)) [ $j -ge 2 ] && ((j--))
# Append word separator to current word # Append word separator to current word
ref="$2[$j]" ref="$2[$j]"
eval $2[$j]=\""${!ref}${COMP_WORDS[$i]}"\" eval $2[$j]=\${!ref}\${COMP_WORDS[i]}
# Indicate new cword # Indicate new cword
[ $i = $COMP_CWORD ] && eval $3=$j [ $i = $COMP_CWORD ] && eval $3=$j
# Indicate next word if available, else end *both* while and for loop # Indicate next word if available, else end *both* while and for loop
@ -248,7 +241,7 @@ __reassemble_comp_words_by_ref() {
done done
# Append word to current word # Append word to current word
ref="$2[$j]" ref="$2[$j]"
eval $2[$j]=\""${!ref}${COMP_WORDS[$i]}"\" eval $2[$j]=\${!ref}\${COMP_WORDS[i]}
# Indicate new cword # Indicate new cword
[ $i = $COMP_CWORD ] && eval $3=$j [ $i = $COMP_CWORD ] && eval $3=$j
done done
@ -295,7 +288,7 @@ _get_cword()
"${#cur}" -ge ${#words[i]} && "${#cur}" -ge ${#words[i]} &&
# $cur doesn't match cword? # $cur doesn't match cword?
"${cur:0:${#words[i]}}" != "${words[i]}" "${cur:0:${#words[i]}}" != "${words[i]}"
]]; do ]]; do
# Strip first character # Strip first character
cur="${cur:1}" cur="${cur:1}"
# Decrease cursor position # Decrease cursor position
@ -369,6 +362,46 @@ __ltrim_colon_completions() {
} # __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 # This function performs file and directory completion. It's better than
# simply using 'compgen -f', because it honours spaces in filenames. # simply using 'compgen -f', because it honours spaces in filenames.
# If passed -d, it completes only on directories. If passed anything else, # If passed -d, it completes only on directories. If passed anything else,
@ -376,12 +409,12 @@ __ltrim_colon_completions() {
# #
_filedir() _filedir()
{ {
local IFS=$'\t\n' xspec local i IFS=$'\t\n' xspec
_expand || return 0 __expand_tilde_by_ref cur
local -a toks local -a toks
local tmp local quoted tmp
# TODO: I've removed a "[ -n $tmp ] &&" before `printf '%s\n' $tmp', # TODO: I've removed a "[ -n $tmp ] &&" before `printf '%s\n' $tmp',
# and everything works again. If this bug # and everything works again. If this bug
@ -393,28 +426,74 @@ _filedir()
# because quotes-in-comments-in-a-subshell cause errors on # because quotes-in-comments-in-a-subshell cause errors on
# bash-3.1. See also: # bash-3.1. See also:
# http://www.mail-archive.com/bug-bash@gnu.org/msg01667.html # http://www.mail-archive.com/bug-bash@gnu.org/msg01667.html
_quote_readline_by_ref "$cur" quoted
toks=( ${toks[@]-} $( toks=( ${toks[@]-} $(
compgen -d -- "$(quote_readline "$cur")" | { compgen -d -- "$quoted" | {
while read -r tmp; do while read -r tmp; do
printf '%s\n' $tmp printf '%s\n' $tmp
done 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 if [[ "$1" != -d ]]; then
xspec=${1:+"!*.$1"} xspec=${1:+"!*.$1"}
toks=( ${toks[@]-} $( if [[ ${cur:0:1} == "'" && ${BASH_VERSINFO[0]} -ge 4 ]]; then
compgen -f -X "$xspec" -- "$(quote_readline "$cur")" | { toks=( ${toks[@]-} $(
while read -r tmp; do eval compgen -f -X \"\$xspec\" -- $quoted
[ -n $tmp ] && printf '%s\n' $tmp ) )
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 done
} }
)) fi
fi fi
COMPREPLY=( "${COMPREPLY[@]}" "${toks[@]}" ) COMPREPLY=( "${COMPREPLY[@]}" "${toks[@]}" )
[ ${#COMPREPLY[@]} -ne 0 ] && type compopt &>/dev/null && \
compopt -o filenames
} # _filedir() } # _filedir()

0
test/fixtures/_filedir/a b/i vendored Normal file
View File

0
test/fixtures/_filedir/a"b/d vendored Normal file
View File

0
test/fixtures/_filedir/a$b/h vendored Normal file
View File

0
test/fixtures/_filedir/a&b/f vendored Normal file
View File

0
test/fixtures/_filedir/a'b/c vendored Normal file
View File

0
test/fixtures/_filedir/a\b/g vendored Normal file
View File

0
test/fixtures/_filedir/ab/e vendored Normal file
View File

0
test/fixtures/compgen/a'b/c vendored Normal file
View File

121
test/fixtures/compgen/t1.txt vendored Normal file
View File

@ -0,0 +1,121 @@
BASH=/bin/bash
BASH_ARGC=()
BASH_ARGV=()
BASH_LINENO=()
BASH_SOURCE=()
BASH_VERSINFO=([0]="3" [1]="2" [2]="39" [3]="1" [4]="release" [5]="i486-pc-linux-gnu")
BASH_VERSION='3.2.39(1)-release'
CDPL_DIRS=([0]="/home/freddy/proj")
CDPM_DIRS=
CDP_DIRS=([0]="/home/freddy/proj" [1]="")
COLUMNS=130
COMP_CACHE=/home/freddy/.bash_completion_lib.d/cache~
COMP_DIR=/etc/bash_completion_lib
COMP_PATH=/home/freddy/.bash_completion_lib.d:/etc/bash_completion_lib
COMP_RESTRICT_BY_EXTENSION=0
COMP_VERSION=bash_completion_lib-1.3.1
DIRSTACK=()
EDITOR=/usr/bin/vim
EUID=1000
GPGKEY=10A575C3
GPG_AGENT_INFO=/tmp/gpg-Pg6JXR/S.gpg-agent:4129:1
GPG_TTY=/dev/pts/0
GREP_OPTIONS='--exclude '\''distrib/*'\'' --exclude tags'
GROUPS=()
HISTCONTROL=ignoreboth
HISTFILE=/home/freddy/.bash_history
HISTFILESIZE=500
HISTIGNORE=exit
HISTSIZE=500
HOME=/home/freddy
HOSTNAME=blondy
HOSTTYPE=i486
IFS=$' \t\n'
LANG=en_US
LANGUAGE=en_NL:en_US:en_GB:en
LINES=49
LOGNAME=freddy
MACHTYPE=i486-pc-linux-gnu
MAIL=/var/mail/freddy
MAILCHECK=60
OLDPWD=/home/freddy/.bash_completion_lib.d
OPTERR=1
OPTIND=1
OSTYPE=linux-gnu
PATH=/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games:/home/freddy/proj/rc/bin
PIPESTATUS=([0]="0")
PPID=29352
PS1=$'\\[\E[0;34m\\]\\!\\[\E[0m\\]\\[\E[1;32m\\]$(stoppedjobs)\\[\E[0m\\]:\\u@\\h:\\w> \\[\E[m\\]'
PS2='> '
PS4='+ '
PWD=/home/freddy/proj/bashCompletion/bash-completion.git/test/fixtures/compgen
SHELL=/bin/bash
SHELLOPTS=braceexpand:hashall:histexpand:interactive-comments:monitor:vi
SHLVL=1
SSH_AUTH_SOCK=/tmp/ssh-xhQbo29352/agent.29352
SSH_CLIENT='192.168.123.143 37670 4822'
SSH_CONNECTION='192.168.123.143 37670 192.168.123.8 4822'
SSH_TTY=/dev/pts/0
TERM=xterm
UID=1000
USER=freddy
VIM=/home/freddy/.vim
VIMRUNTIME=/usr/share/vim/vimcurrent
_=GPG_AGENT_INFO
bash205='3.2.39(1)-release'
bash205b='3.2.39(1)-release'
bash3='3.2.39(1)-release'
cdots ()
{
[ -d "$1$2" ] && cd "$1$2" || eval cd "$1$2"
}
comp_load ()
{
local cmd=${COMP_WORDS[0]} dir globs OLDIFS=$IFS;
IFS=:;
local -a aPaths=($COMP_PATH);
IFS='
';
globs=($(
for dir in "${aPaths[@]}"; do
echo \"$dir\"/complete\*/\*.$cmd
echo \"$dir\"/complete\*/$cmd\!
echo \"$dir\"/complete\*/$cmd
done
));
IFS=$OLDIFS;
if ! declare -F comp_include >&/dev/null; then
for dir in "${aPaths[@]}";
do
[ -r "$dir/include/comp_include" ] && . "$dir/include/comp_include" && break;
done;
fi;
comp_include comp_load_init;
comp_load_init;
local script="$(eval find "${globs[@]}" 2> /dev/null | head -1)";
local link comp=${script##*/};
[[ ${comp: -1:1} == ! ]] || {
link=${comp#*.};
comp=${comp%.$link}
};
local path=${script%/*};
[ "$script" -a -r "$path/$comp" ] && . "$path/$comp" && declare -F _$comp >&/dev/null && {
[ ${COMP_INSTALL:-1} -eq 0 ] || _comp_install $comp "$path"
} && _$comp $link;
comp_load_deinit
}
nameTerminal ()
{
[ "${TERM:0:5}" = "xterm" ] && local ansiNrTab=0;
[ "$TERM" = "rxvt" ] && local ansiNrTab=61;
[ "$TERM" = "konsole" ] && local ansiNrTab=30 ansiNrWindow=0;
[ $ansiNrTab ] && echo -n ''"]$ansiNrTab;$1"'';
[ $ansiNrWindow -a "$2" ] && echo -n ''"]$ansiNrWindow;$2"''
}
stoppedjobs ()
{
if [ "$(jobs -s)" ]; then
echo -n "%";
jobs -s | wc -l;
fi
}

121
test/fixtures/compgen/t2.txt vendored Normal file
View File

@ -0,0 +1,121 @@
BASH=/bin/bash
BASH_ARGC=()
BASH_ARGV=()
BASH_LINENO=()
BASH_SOURCE=()
BASH_VERSINFO=([0]="3" [1]="2" [2]="39" [3]="1" [4]="release" [5]="i486-pc-linux-gnu")
BASH_VERSION='3.2.39(1)-release'
CDPL_DIRS=([0]="/home/freddy/proj")
CDPM_DIRS=
CDP_DIRS=([0]="/home/freddy/proj" [1]="")
COLUMNS=130
COMP_CACHE=/home/freddy/.bash_completion_lib.d/cache~
COMP_DIR=/etc/bash_completion_lib
COMP_PATH=/home/freddy/.bash_completion_lib.d:/etc/bash_completion_lib
COMP_RESTRICT_BY_EXTENSION=0
COMP_VERSION=bash_completion_lib-1.3.1
DIRSTACK=()
EDITOR=/usr/bin/vim
EUID=1000
GPGKEY=10A575C3
GPG_AGENT_INFO=/tmp/gpg-Pg6JXR/S.gpg-agent:4129:1
GPG_TTY=/dev/pts/0
GREP_OPTIONS='--exclude '\''distrib/*'\'' --exclude tags'
GROUPS=()
HISTCONTROL=ignoreboth
HISTFILE=/home/freddy/.bash_history
HISTFILESIZE=500
HISTIGNORE=exit
HISTSIZE=500
HOME=/home/freddy
HOSTNAME=blondy
HOSTTYPE=i486
IFS=$' \t\n'
LANG=en_US
LANGUAGE=en_NL:en_US:en_GB:en
LINES=49
LOGNAME=freddy
MACHTYPE=i486-pc-linux-gnu
MAIL=/var/mail/freddy
MAILCHECK=60
OLDPWD=/home/freddy/.bash_completion_lib.d
OPTERR=1
OPTIND=1
OSTYPE=linux-gnu
PATH=/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games:/home/freddy/proj/rc/bin
PIPESTATUS=([0]="0")
PPID=29352
PS1=$'\\[\E[0;34m\\]\\!\\[\E[0m\\]\\[\E[1;32m\\]$(stoppedjobs)\\[\E[0m\\]:\\u@\\h:\\w> \\[\E[m\\]'
PS2='> '
PS4='+ '
PWD=/home/freddy/proj/bashCompletion/bash-completion.git/test/fixtures/compgen
SHELL=/bin/bash
SHELLOPTS=braceexpand:hashall:histexpand:interactive-comments:monitor:vi
SHLVL=1
SSH_AUTH_SOCK=/tmp/ssh-xhQbo29352/agent.29352
SSH_CLIENT='192.168.123.143 37670 4822'
SSH_CONNECTION='192.168.123.143 37670 192.168.123.8 4822'
SSH_TTY=/dev/pts/0
TERM=xterm
UID=1000
USER=freddy
VIM=/home/freddy/.vim
VIMRUNTIME=/usr/share/vim/vimcurrent
_='a\\\'\''b/'
bash205='3.2.39(1)-release'
bash205b='3.2.39(1)-release'
bash3='3.2.39(1)-release'
cdots ()
{
[ -d "$1$2" ] && cd "$1$2" || eval cd "$1$2"
}
comp_load ()
{
local cmd=${COMP_WORDS[0]} dir globs OLDIFS=$IFS;
IFS=:;
local -a aPaths=($COMP_PATH);
IFS='
';
globs=($(
for dir in "${aPaths[@]}"; do
echo \"$dir\"/complete\*/\*.$cmd
echo \"$dir\"/complete\*/$cmd\!
echo \"$dir\"/complete\*/$cmd
done
));
IFS=$OLDIFS;
if ! declare -F comp_include >&/dev/null; then
for dir in "${aPaths[@]}";
do
[ -r "$dir/include/comp_include" ] && . "$dir/include/comp_include" && break;
done;
fi;
comp_include comp_load_init;
comp_load_init;
local script="$(eval find "${globs[@]}" 2> /dev/null | head -1)";
local link comp=${script##*/};
[[ ${comp: -1:1} == ! ]] || {
link=${comp#*.};
comp=${comp%.$link}
};
local path=${script%/*};
[ "$script" -a -r "$path/$comp" ] && . "$path/$comp" && declare -F _$comp >&/dev/null && {
[ ${COMP_INSTALL:-1} -eq 0 ] || _comp_install $comp "$path"
} && _$comp $link;
comp_load_deinit
}
nameTerminal ()
{
[ "${TERM:0:5}" = "xterm" ] && local ansiNrTab=0;
[ "$TERM" = "rxvt" ] && local ansiNrTab=61;
[ "$TERM" = "konsole" ] && local ansiNrTab=30 ansiNrWindow=0;
[ $ansiNrTab ] && echo -n ''"]$ansiNrTab;$1"'';
[ $ansiNrWindow -a "$2" ] && echo -n ''"]$ansiNrWindow;$2"''
}
stoppedjobs ()
{
if [ "$(jobs -s)" ]; then
echo -n "%";
jobs -s | wc -l;
fi
}

121
test/fixtures/compgen/t3.txt vendored Normal file
View File

@ -0,0 +1,121 @@
BASH=/bin/bash
BASH_ARGC=()
BASH_ARGV=()
BASH_LINENO=()
BASH_SOURCE=()
BASH_VERSINFO=([0]="3" [1]="2" [2]="39" [3]="1" [4]="release" [5]="i486-pc-linux-gnu")
BASH_VERSION='3.2.39(1)-release'
CDPL_DIRS=([0]="/home/freddy/proj")
CDPM_DIRS=
CDP_DIRS=([0]="/home/freddy/proj" [1]="")
COLUMNS=130
COMP_CACHE=/home/freddy/.bash_completion_lib.d/cache~
COMP_DIR=/etc/bash_completion_lib
COMP_PATH=/home/freddy/.bash_completion_lib.d:/etc/bash_completion_lib
COMP_RESTRICT_BY_EXTENSION=0
COMP_VERSION=bash_completion_lib-1.3.1
DIRSTACK=()
EDITOR=/usr/bin/vim
EUID=1000
GPGKEY=10A575C3
GPG_AGENT_INFO=/tmp/gpg-Pg6JXR/S.gpg-agent:4129:1
GPG_TTY=/dev/pts/0
GREP_OPTIONS='--exclude '\''distrib/*'\'' --exclude tags'
GROUPS=()
HISTCONTROL=ignoreboth
HISTFILE=/home/freddy/.bash_history
HISTFILESIZE=500
HISTIGNORE=exit
HISTSIZE=500
HOME=/home/freddy
HOSTNAME=blondy
HOSTTYPE=i486
IFS=$' \t\n'
LANG=en_US
LANGUAGE=en_NL:en_US:en_GB:en
LINES=49
LOGNAME=freddy
MACHTYPE=i486-pc-linux-gnu
MAIL=/var/mail/freddy
MAILCHECK=60
OLDPWD=/home/freddy/.bash_completion_lib.d
OPTERR=1
OPTIND=1
OSTYPE=linux-gnu
PATH=/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games:/home/freddy/proj/rc/bin
PIPESTATUS=([0]="0")
PPID=29352
PS1=$'\\[\E[0;34m\\]\\!\\[\E[0m\\]\\[\E[1;32m\\]$(stoppedjobs)\\[\E[0m\\]:\\u@\\h:\\w> \\[\E[m\\]'
PS2='> '
PS4='+ '
PWD=/home/freddy/proj/bashCompletion/bash-completion.git/test/fixtures/compgen
SHELL=/bin/bash
SHELLOPTS=braceexpand:hashall:histexpand:interactive-comments:monitor:vi
SHLVL=1
SSH_AUTH_SOCK=/tmp/ssh-xhQbo29352/agent.29352
SSH_CLIENT='192.168.123.143 37670 4822'
SSH_CONNECTION='192.168.123.143 37670 192.168.123.8 4822'
SSH_TTY=/dev/pts/0
TERM=xterm
UID=1000
USER=freddy
VIM=/home/freddy/.vim
VIMRUNTIME=/usr/share/vim/vimcurrent
_='a\\\'\''b/'
bash205='3.2.39(1)-release'
bash205b='3.2.39(1)-release'
bash3='3.2.39(1)-release'
cdots ()
{
[ -d "$1$2" ] && cd "$1$2" || eval cd "$1$2"
}
comp_load ()
{
local cmd=${COMP_WORDS[0]} dir globs OLDIFS=$IFS;
IFS=:;
local -a aPaths=($COMP_PATH);
IFS='
';
globs=($(
for dir in "${aPaths[@]}"; do
echo \"$dir\"/complete\*/\*.$cmd
echo \"$dir\"/complete\*/$cmd\!
echo \"$dir\"/complete\*/$cmd
done
));
IFS=$OLDIFS;
if ! declare -F comp_include >&/dev/null; then
for dir in "${aPaths[@]}";
do
[ -r "$dir/include/comp_include" ] && . "$dir/include/comp_include" && break;
done;
fi;
comp_include comp_load_init;
comp_load_init;
local script="$(eval find "${globs[@]}" 2> /dev/null | head -1)";
local link comp=${script##*/};
[[ ${comp: -1:1} == ! ]] || {
link=${comp#*.};
comp=${comp%.$link}
};
local path=${script%/*};
[ "$script" -a -r "$path/$comp" ] && . "$path/$comp" && declare -F _$comp >&/dev/null && {
[ ${COMP_INSTALL:-1} -eq 0 ] || _comp_install $comp "$path"
} && _$comp $link;
comp_load_deinit
}
nameTerminal ()
{
[ "${TERM:0:5}" = "xterm" ] && local ansiNrTab=0;
[ "$TERM" = "rxvt" ] && local ansiNrTab=61;
[ "$TERM" = "konsole" ] && local ansiNrTab=30 ansiNrWindow=0;
[ $ansiNrTab ] && echo -n ''"]$ansiNrTab;$1"'';
[ $ansiNrWindow -a "$2" ] && echo -n ''"]$ansiNrWindow;$2"''
}
stoppedjobs ()
{
if [ "$(jobs -s)" ]; then
echo -n "%";
jobs -s | wc -l;
fi
}

View File

@ -33,6 +33,11 @@ expect {
-re "\r\nbar\\s+bar bar.d\\s+foo\\s+foo.d" { -re "\r\nbar\\s+bar bar.d\\s+foo\\s+foo.d" {
if {[lindex $BASH_VERSINFO 0] < 4} {xfail "$test"} {fail "$test"} if {[lindex $BASH_VERSINFO 0] < 4} {xfail "$test"} {fail "$test"}
} }
-re "\r\nbar\\s+bar\\\\ bar.d/\\s+foo\\s+foo.d/" {
# On bash-3, the space in `bar bar.d' is escaped with a backslash
# as a side-effect of emulating `-o filenames'.
if {[lindex $BASH_VERSINFO 0] <= 3} {pass "$test"} {fail "$test"}
}
-re $prompt { unresolved "$test at prompt" } -re $prompt { unresolved "$test at prompt" }
default { unresolved "$test" } default { unresolved "$test" }
}; # expect }; # expect

234
test/unit/_filedir.exp Normal file
View File

@ -0,0 +1,234 @@
proc setup {} {
assert_bash_exec {unset COMPREPLY cur}
assert_bash_exec {unset -f _f}
save_env
# Declare bash completion function `_f'
assert_bash_exec { \
_f() { local cur=$(_get_cword); unset COMPREPLY; _filedir; }; \
complete -F _f f \
}
# Declare bash completion function `_f2' with `-o filenames' active.
assert_bash_exec { \
complete -F _f -o filenames f2 \
}
# Create directory `a*b'
# NOTE: directory `a*b' isn't included in Git, because a directory
# containing an asterisk (*) causes troubles on Cygwin/Windows
assert_bash_exec {(cd fixtures/_filedir && [ ! -d a\*b ] && mkdir a\*b && touch a\*b/j || true)}
}; # setup()
proc teardown {} {
assert_bash_exec {(cd fixtures/_filedir && rm -- a\*b/j && rmdir a\*b/ || true)}
assert_bash_exec {unset COMPREPLY cur}
assert_bash_exec {unset -f _f}
assert_bash_exec {complete -r f}
assert_env_unmodified { /OLDPWD/d }
}; # teardown()
setup
set test "_filedir should run without errors"
assert_bash_exec {_filedir > /dev/null} $test
sync_after_int
foreach name {f f2} {
set test "completing $name ab/ should return e"
set cmd "$name ab/"
assert_complete_dir e $cmd "fixtures/_filedir"
sync_after_int
set test "completing $name a\\ b/ should return i"
set cmd "$name a\\ b/"
assert_complete_dir i $cmd "fixtures/_filedir"
sync_after_int
set test "completing $name a\\\'b/ should return i"
set cmd "$name a\\\'b/"
assert_complete_dir c $cmd "fixtures/_filedir"
sync_after_int
set test "completing $name a\\\"b/ should return i"; #"
set cmd "$name a\\\"b/"; #"
assert_complete_dir d $cmd "fixtures/_filedir"
sync_after_int
set test "completing $name a\\\$b/ should return h"
set cmd "$name a\\\$b/"
assert_complete_dir "\b\b\b\b\b$::TESTDIR/fixtures/_filedir/a\\\\\$b/h" $cmd "fixtures/_filedir"
sync_after_int
set test "completing $name a\\\\b/ should return g"
set cmd "$name a\\\\b/"
assert_complete_dir g $cmd "fixtures/_filedir"
sync_after_int
set test "completing $name a\\&b/ should return f"
set cmd "$name a\\&b/"
assert_complete_dir f $cmd "fixtures/_filedir"
sync_after_int
set test "completing $name a\$ should return a\\\$b/"
set cmd "$name a\$"
assert_complete_dir "\b\\\\\$b/" $cmd "fixtures/_filedir"
sync_after_int
# NOTE: Bash versions 4.0.0 up to 4.0.34 contain a bug when completing quoted
# words, so tests within this if aren't executed for these bash versions.
if {! (
[lindex $::BASH_VERSINFO 0] == 4 &&
[lindex $::BASH_VERSINFO 1] == 0 &&
[lindex $::BASH_VERSINFO 2] < 35
)} {
set test "completing $name 'ab/ should return e"
set cmd "$name 'ab/"
assert_complete_dir {e'} $cmd "fixtures/_filedir"
sync_after_int
set test "completing $name 'a b/ should return i"
set cmd "$name 'a b/"
assert_complete_dir {i'} $cmd "fixtures/_filedir"
sync_after_int
set test "completing $name 'a\"b/ should return d"; #"
set cmd "$name 'a\"b/"; #"
assert_complete_dir {d'} $cmd "fixtures/_filedir"
sync_after_int
set test "completing $name 'a\$b/ should return h"
set cmd "$name 'a\$b/"
if {[lindex $::BASH_VERSINFO 0] == 4} {
assert_complete_dir {h'} $cmd "fixtures/_filedir"
} else {
assert_complete_dir "\b\b\b\b$::TESTDIR/fixtures/_filedir/a\$b/h'" $cmd "fixtures/_filedir"
}; # if
sync_after_int
set test "completing $name 'a\\b/ should return g"
set cmd "$name 'a\\b/"
assert_complete_dir {g'} $cmd "fixtures/_filedir"
sync_after_int
set test "completing $name 'a&b/ should return f"
set cmd "$name 'a&b/"
assert_complete_dir {f'} $cmd "fixtures/_filedir"
sync_after_int
set test "completing $name \"ab/ should return e"; #"
set cmd "$name \"ab/"; #"
assert_complete_dir {e"} $cmd "fixtures/_filedir"; #"
sync_after_int
set test "completing $name \"a b/ should return i"; #"
set cmd "$name \"a b/"; #"
assert_complete_dir {i"} $cmd "fixtures/_filedir"; #"
sync_after_int
set test "completing $name \"a'b/ should return c"; #"
set cmd "$name \"a'b/"; #"
assert_complete_dir {c"} $cmd "fixtures/_filedir"; #"
sync_after_int
set test "completing $name \"a\\\"b/ should return d"; #"
set cmd "$name \"a\\\"b/"; #"
assert_complete_dir {d"} $cmd "fixtures/_filedir"; #"
sync_after_int
set test "completing $name \"a\\\$b/ should return h"; #"
set cmd "$name \"a\\\$b/"; #"
assert_complete_dir "\b\b\b\b\b$::TESTDIR/fixtures/_filedir/a\\\\\$b/h\\\"" $cmd "fixtures/_filedir"
sync_after_int
set test "completing $name \"a\\b/ should return e"; #"
set cmd "$name \"a\\b/"; #"
assert_complete_dir "\b\b\bb/e\\\"" $cmd "fixtures/_filedir"
sync_after_int
set test "completing $name \"a\\\\b/ should return g"; #"
set cmd "$name \"a\\\\b/"; #"
assert_complete_dir {g"} $cmd "fixtures/_filedir"; #"
sync_after_int
set test "completing $name \"a&b/ should return f"; #"
set cmd "$name \"a&b/"; #"
assert_complete_dir {f"} $cmd "fixtures/_filedir"; #"
sync_after_int
}; # if
}; # for
teardown

View File

@ -253,4 +253,32 @@ expect {
sync_after_int sync_after_int
set test {a 'b&c| should return 'b&c}; # | = cursor position
if {
[lindex $::BASH_VERSINFO 0] == 4 &&
[lindex $::BASH_VERSINFO 1] == 0 &&
[lindex $::BASH_VERSINFO 2] < 35
} {
set cmd {COMP_WORDS=(a "'" b "&" c); COMP_CWORD=4}
} else {
set cmd {COMP_WORDS=(a "'b&c"); COMP_CWORD=1}
}; # if
append cmd {; COMP_LINE="a 'b&c"; COMP_POINT=6; _get_cword}
send "$cmd\r"
expect -ex "$cmd\r\n"
expect {
-ex "'b&c/@" { pass "$test" }
-ex "c/@" {
if {
[lindex $::BASH_VERSINFO 0] == 4 &&
[lindex $::BASH_VERSINFO 1] == 0 &&
[lindex $::BASH_VERSINFO 2] < 35
} {xfail "$test"} {fail "$test"}
}
}; # expect
sync_after_int
teardown teardown

52
test/unit/compgen.exp Normal file
View File

@ -0,0 +1,52 @@
proc setup {} {
save_env
}; # setup()
proc teardown {} {
assert_env_unmodified {/OLDPWD/d}
}; # teardown()
setup
if {[lindex $::BASH_VERSINFO 0] <= 3} {
set test {compgen -f a\\\\\\\'b/ on bash-3 should return a\'b/c};
set cmd {compgen -f a\\\\\\\'b/}
} else {
set test {compgen -f a\\\'b/ on bash-4 should return a\'b/c};
set cmd {compgen -f a\\\'b/}
}; # if
set dir fixtures/compgen
set prompt "/$dir/@"
assert_bash_exec "cd $dir" "" $prompt
send "$cmd\r"
expect -ex "$cmd\r\n"
expect {
-re {a\\\'b/c} {
# On bash-3.2, compgen returns inconsequent output
if {
[lindex $::BASH_VERSINFO 0] >= 4 || (
[lindex $::BASH_VERSINFO 0] == 3 &&
[lindex $::BASH_VERSINFO 1] == 2
)
} {pass $test} else {fail $test}
}
-re {a'b/c} {
if {[lindex $::BASH_VERSINFO 0] <= 3 } \
{pass $test} else {fail $test}
}
-re $prompt { pass "$test" }
-re eof { unresolved "eof" }
}; # expect
sync_after_int $prompt
assert_bash_exec "cd \$TESTDIR"
#assert_bash_list_dir {a\\\'b/c} $cmd fixtures/compgen
sync_after_int
teardown