summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCrestez Dan Leonard <cdleonard@gmail.com>2010-02-15 15:36:58 +0200
committerCrestez Dan Leonard <cdleonard@gmail.com>2010-02-15 15:36:58 +0200
commitf294219990569082b20e08643b6e46256ca0862f (patch)
tree9426e8a90184135ac6303a5e1dde957815962052
parent72a8cb5b49ad222e74f1d97ad10f85e3a002cf38 (diff)
parent91f7e8274e90632c95527bf0fc1407e9abd0b539 (diff)
downloadbash-completion-f294219990569082b20e08643b6e46256ca0862f.tar.gz
Merge branch 'mount-fix'
Fix mount handling of escapes (Alioth: #311410, Launchpad: #219971) Conflicts: CHANGES
-rw-r--r--CHANGES1
-rw-r--r--contrib/mount86
-rw-r--r--test/fixtures/mount/test-fstab24
-rw-r--r--test/lib/completions/mount.exp85
4 files changed, 188 insertions, 8 deletions
diff --git a/CHANGES b/CHANGES
index bfa254f5..a6cdaafa 100644
--- a/CHANGES
+++ b/CHANGES
@@ -71,6 +71,7 @@ bash-completion (2.x)
* Fix completion of usernames (Alioth: #311396, Debian: #511788).
* Fix chown test crashing on systems with no root group (Alioth: #312306).
* Fixed tests when BASH_COMPLETION or TESTDIR contain spaces.
+ * Fix mount handling of escapes (Alioth: #311410, Launchpad: #219971).
[ Raphaƫl Droz ]
* Add xsltproc completion (Alioth: #311843).
diff --git a/contrib/mount b/contrib/mount
index 1f0d8a4a..0c7fad83 100644
--- a/contrib/mount
+++ b/contrib/mount
@@ -7,6 +7,74 @@
have mount &&
{
+# Just like COMPREPLY=(`compgen -W "${COMPREPLY[*]}" -- "$cur"`), only better!
+#
+# This will correctly escape special characters in COMPREPLY.
+_reply_compgen_array()
+{
+ # Create the argument for compgen -W by escaping twice.
+ #
+ # One round of escape is because we want to reply with escaped arguments. A
+ # second round is required because compgen -W will helpfully expand it's
+ # argument.
+ local i wlist
+ for i in ${!COMPREPLY[*]}; do
+ local q=$(quote "$(printf %q "${COMPREPLY[$i]}")")
+ wlist+=$q$'\n'
+ done
+
+ # We also have to add another round of escaping to $cur.
+ local ecur="$cur"
+ ecur="${ecur//\\/\\\\}"
+ ecur="${ecur//\'/\'}"
+
+ # Actually generate completions.
+ local oldifs=$IFS
+ IFS=$'\n' eval 'COMPREPLY=(`compgen -W "$wlist" -- "${ecur}"`)'
+ IFS=$oldifs
+}
+
+# Unescape strings in the linux fstab(5) format (with octal escapes).
+__linux_fstab_unescape() {
+ eval $1="'${!1//\'/\047}'"
+ eval $1="'${!1/%\\/\\\\}'"
+ eval "$1=$'${!1}'"
+}
+
+# Complete linux fstab entries.
+#
+# Reads a file from stdin in the linux fstab(5) format; as used by /etc/fstab
+# and /proc/mounts.
+_linux_fstab()
+{
+ COMPREPLY=()
+
+ # Read and unescape values into COMPREPLY
+ local fs_spec fs_file fs_other
+ local oldifs="$IFS"
+ while read -r fs_spec fs_file fs_other; do
+ if [[ $fs_spec = [#]* ]]; then continue; fi
+ if [[ $1 == -L ]]; then
+ local fs_label=${fs_spec/#LABEL=}
+ if [[ $fs_label != "$fs_spec" ]]; then
+ __linux_fstab_unescape fs_label
+ IFS=$'\0'
+ COMPREPLY+=("$fs_label")
+ IFS=$oldifs
+ fi
+ else
+ __linux_fstab_unescape fs_spec
+ __linux_fstab_unescape fs_file
+ IFS=$'\0'
+ [[ $fs_spec = */* ]] && COMPREPLY+=("$fs_spec")
+ [[ $fs_file = */* ]] && COMPREPLY+=("$fs_file")
+ IFS=$oldifs
+ fi
+ done
+
+ _reply_compgen_array
+}
+
_mount()
{
local cur sm host prev
@@ -44,11 +112,11 @@ _mount()
else
# probably Linux
if [ $prev = -L ]; then
- COMPREPLY=( $( compgen -W '$(sed -ne "s/^[[:space:]]*LABEL=\([^[:space:]]*\).*/\1/p" /etc/fstab )' -- "$cur" ) )
+ _linux_fstab -L < /etc/fstab
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" ) )
+ _linux_fstab < /etc/fstab
fi
fi
@@ -62,12 +130,18 @@ complete -F _mount -o default -o dirnames mount
have umount &&
_umount()
{
- local cur IFS=$'\n'
-
COMPREPLY=()
- cur=`_get_cword`
- COMPREPLY=( $( compgen -W '$( mount | cut -d" " -f 3 )' -- "$cur" ) )
+ local cur=`_get_cword`
+
+ if [[ $(uname -s) = Linux && -r /proc/mounts ]]; then
+ # Linux /proc/mounts is properly quoted. This is important when
+ # unmounting usb devices with pretty names.
+ _linux_fstab < /proc/mounts
+ else
+ local IFS=$'\n'
+ COMPREPLY=( $( compgen -W '$( mount | cut -d" " -f 3 )' -- "$cur" ) )
+ fi
return 0
} &&
diff --git a/test/fixtures/mount/test-fstab b/test/fixtures/mount/test-fstab
new file mode 100644
index 00000000..b2434173
--- /dev/null
+++ b/test/fixtures/mount/test-fstab
@@ -0,0 +1,24 @@
+proc /proc proc defaults 0 0
+none /debug debugfs defaults,noauto 0 0
+
+# Simple obvious test.
+/mnt/nice-test-path /dev/null auto ro,noauto 0 0
+
+# Test octal escapes
+# Contains ' ' and '-'
+/mnt/nice\040test\055path /dev/null auto ro,noauto 0 0
+# Contains '$' and '-'
+/mnt/nice\044test\055path /dev/null auto ro,noauto 0 0
+# Contains ' ' and '\\'
+/mnt/nice\040test\134path /dev/null auto ro,noauto 0 0
+# Contains '\n' and '\ '
+/mnt/nice\012test\040path /dev/null auto ro,noauto 0 0
+
+# Test apostrophe
+/mnt/nice'test-path /dev/null auto ro,noauto 0 0
+/mnt/other'test\040path /dev/null auto ro,noauto 0 0
+
+# Test some labels
+LABEL=Ubuntu\040Karmic /mnt/ubuntu auto no,noauto 0 0
+LABEL=Fedora /mnt/fedora auto ro,noauto 0 0
+LABEL=Debian-it's\040awesome /mnt/debian auto ro,noauto 0 0
diff --git a/test/lib/completions/mount.exp b/test/lib/completions/mount.exp
index 9e40803e..9e98cc6d 100644
--- a/test/lib/completions/mount.exp
+++ b/test/lib/completions/mount.exp
@@ -1,11 +1,38 @@
+# mount completion from fstab can't be tested directly because it
+# (correctly) uses absolute paths. So we create a custom completion which
+# reads from a file in our text fixture instead.
+proc setup_dummy_mnt {} {
+ assert_bash_exec {unset COMPREPLY cur}
+ assert_bash_exec {unset -f _mnt}
+
+ global TESTDIR
+ assert_bash_exec { \
+ _mnt() { \
+ local cur=$(_get_cword); \
+ _linux_fstab $(_get_pword) < "$TESTDIR/fixtures/mount/test-fstab"; \
+ }; \
+ complete -F _mnt mnt \
+ }
+}
+
+
+proc teardown_dummy_mnt {} {
+ assert_bash_exec {unset COMPREPLY cur}
+ assert_bash_exec {unset -f _mnt}
+ assert_bash_exec {complete -r mnt}
+}
+
+
proc setup {} {
save_env
-};
+ setup_dummy_mnt
+}
proc teardown {} {
+ teardown_dummy_mnt
assert_env_unmodified
-};
+}
setup
@@ -30,4 +57,58 @@ assert_bash_exec {PATH="$OLDPATH"; unset -v OLDPATH}
sync_after_int
+set test "Testing internal __linux_fstab_unescape function for mount"
+# One round of slashes is for bash.
+assert_bash_exec {var=one\'two\\040three\\}
+assert_bash_exec {__linux_fstab_unescape var}
+set cmd {echo $var}
+send "$cmd\r"
+expect {
+ -ex "$cmd\r\none'two three\\" { pass $test }
+# default { fail $test }
+}
+assert_bash_exec {unset var}
+
+
+sync_after_int
+
+
+# Begin testing through mnt (see setup_dummy_mnt).
+assert_complete {/mnt/nice-test-path} {mnt /mnt/nice-test-p}
+sync_after_int
+
+assert_complete {/mnt/nice\ test-path} {mnt /mnt/nice\ test-p}
+sync_after_int
+
+assert_complete {/mnt/nice\$test-path} {mnt /mnt/nice\$test-p}
+sync_after_int
+
+assert_complete {/mnt/nice\ test\\path} {mnt /mnt/nice\ test\\p}
+sync_after_int
+
+assert_complete {{/mnt/nice\ test\\path} {/mnt/nice\ test-path}} \
+ {mnt /mnt/nice\ } "" /@ 20 {/mnt/nice\ }
+sync_after_int
+
+assert_complete {/mnt/nice\$test-path} {mnt /mnt/nice\$}
+sync_after_int
+
+assert_complete {/mnt/nice\'test-path} {mnt /mnt/nice\'}
+sync_after_int
+
+assert_complete {/mnt/other\'test\ path} {mnt /mnt/other}
+sync_after_int
+
+assert_complete {Ubuntu\ Karmic} {mnt -L Ubu}
+sync_after_int
+
+assert_complete {Debian-it\'s\ awesome} {mnt -L Deb}
+sync_after_int
+
+# This does not work. Proper support for this requires smarter parsing of
+# $COMP_LINE and it's not worth doing just for mount.
+#assert_complete {$'/mnt/nice\ntest-path'} {mnt $'/mnt/nice\n}
+#sync_after_int
+
+
teardown