[Bash-completion-devel] [bash-completion-Bugs][311410] ULP 219971: Doesn't handle escaped spaces in /etc/fstab well

bash-completion-bugs at alioth.debian.org bash-completion-bugs at alioth.debian.org
Thu Jan 21 10:46:30 UTC 2010


Bugs item #311410, was changed at 2009-01-30 11:25 by Leonard Crestez
You can respond by visiting: 
https://alioth.debian.org/tracker/?func=detail&atid=413095&aid=311410&group_id=100114

Status: Open
Priority: 2
Submitted By: David Paleino (hanska-guest)
Assigned to: Nobody (None)
Summary: ULP 219971: Doesn't handle escaped spaces in /etc/fstab well 
Distribution: None
Originally reported in: Ubuntu Launchpad
Milestone: None
Status: None
Original bug number: 219971


Initial Comment:
Binary package hint: bash-completion

Because of I can't use "/some path" or /some\ path in fstab, I write some\040path as mount point.
I write "mount /some" in bash and press Tab, I see "mount /some\\040path" instead of "mount /some\ path"

----------------------------------------------------------------------

Comment By: Leonard Crestez (cdleonard-guest)
Date: 2010-01-21 12:46

Message:
Linux fstab can contain octal (and only octal) escapes. Interpreting them is
easy; the same style of escaping is supported by bash $'escaped strings'. Bash
supports more that just octal but those shouldn't be in fstab anyway.

Actually completing on mount points and devices with special characters is
hard. The -o filenames option can't be used because "filename specific
processing" does too much here. It will split on / and only print the last
component of the pathname as a complete option. This means that 'mount /<TAB>'
will show devices like sda1 and sda2 as variants. This is bad, even though the
actual completion works correctly.

The only way I could eventually get this to work is by making my own function
to complete over an array with strings containing strange characters. I tested
and it works with spaces, dolar-signs and newlines. It's some very nasty ugly
fragile code. Somehow I feel I must have missed something obvious; this should
have been easier.

Anyway; this function doesn't belong in mount completion. It would make sense
to move it to bash_completion as a generic utility. If you want I can
submit a revised version which does that and adds unit tests as well.

I tested by editting my /etc/fstab. Automatic testing for mount would require
either adding a special $FSTAB_LOCATION (ugly) or a chroot (hard).

There are various unrelated things wrong with this completion. For example:
parsing mount(8) output is fragile. On linux you can read /proc/mounts
instead; that file is in the fstab(5) format with octal escapes. I tried to
avoid unrelated changes.

Comments welcome. I wanted to send a small patch but it seems I couldn't solve
it with a small patch.

diff --git a/contrib/mount b/contrib/mount
index 0474df0..622ff88 100644
--- a/contrib/mount
+++ b/contrib/mount
@@ -1,12 +1,38 @@
-# mount(8) completion. This will pull a list of possible mounts out of
-# /etc/{,v}fstab, unless the word being completed contains a ':', which
-# would indicate the specification of an NFS server. In that case, we
-# query the server for a list of all available exports and complete on
-# that instead.
-#
+# mount(8) completion.
 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 wlist
+    for i in ${!COMPREPLY[*]}; do
+        local q=`printf %q "${COMPREPLY[$i]}"`
+        wlist+=$(quote "$q")$'\n'
+    done
+
+    # We also have to add another round of escaping to $cur.
+    local ecur="$cur"
+    ecur="${ecur//\\/\\\\}"
+    ecur="${ecur/#$\'/\$\'}"
+
+    # Actually generate completions.
+    IFS=$'\n' eval 'COMPREPLY=(`compgen -W "$wlist" -- "${ecur}"`)'
+
+    # Strip starting $' in reply if present in cur.
+    # This is necesarry because readline interprets everything after ' as a
+    # separate word for completion.
+    if [[ $cur == $\'* ]]; then
+        COMPREPLY=( "${COMPREPLY[@]/#$\'}" )
+    fi
+}
+
 _mount()
 {
     local cur i sm host prev
@@ -16,12 +42,15 @@ _mount()
     [[ "$cur" == \\ ]] && cur="/"
     prev=${COMP_WORDS[COMP_CWORD-1]}
 
+    # Look for the NFS showmount program.
     for i in {,/usr}/{,s}bin/showmount; do [ -x $i ] && sm=$i && break; done
 
     if [[ -n "$sm" && "$cur" == *:* ]]; then
+        # Complete NFS exports
         COMPREPLY=( $( compgen -W "$( $sm -e ${cur%%:*} | \
             awk 'NR>1 {print $1}' )" -- "$cur" ) )
     elif [[ "$cur" == //* ]]; then
+        # Complete samba shares when starting with multiple slashes.
         host=${cur#//}
         host=${host%%/*}
         if [ -n "$host" ]; then
@@ -39,18 +68,25 @@ _mount()
         COMPREPLY=( $( compgen -W "$( mount | awk '! /^[ \t]*#/ {if ($3 ~ /\//) print $3}' )" -- "$cur" ) )
     else
         # probably Linux
-        if [ $prev = -L ]; then
+        if [[ $prev = -L ]]; then
             COMPREPLY=( $( compgen -W '$(sed -ne "s/^[[:space:]]*LABEL=\([^[:space:]]*\).*/\1/p" /etc/fstab )' -- "$cur" ) )
-        elif [ $prev = -U ]; then
+        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" ) )
+            COMPREPLY=()
+            while read -r fs_spec fs_file fs_other; do
+                if [[ $fs_spec = [#]* ]]; then continue; fi
+                [[ $fs_spec = */* ]] && { IFS=$'\0' eval "COMPRELPY+=( $'$fs_spec' )"; }
+                [[ $fs_file = */* ]] && { IFS=$'\0' eval "COMPREPLY+=( $'$fs_file' )"; }
+            done < /etc/fstab
+
+            _reply_compgen_array
         fi
     fi
 
     return 0
 } &&
-complete -F _mount -o default -o dirnames mount
+complete -F _mount -o dirnames mount
 
 # umount(8) completion. This relies on the mount point being the third
 # space-delimited field in the output of mount(8)


----------------------------------------------------------------------

Comment By: David Paleino (hanska-guest)
Date: 2009-01-30 11:25

Message:
DBTS 511149:

From: James Westby <james.westby at canonical.com>
To: Debian Bug Tracking System <submit at bugs.debian.org>
Subject: Doesn't handle escaped spaces in /etc/fstab well
Date: Wed, 07 Jan 2009 19:35:16 +0000

Package: bash-completion
Version: 20080705
Severity: minor

Hi,

In https://bugs.launchpad.net/ubuntu/+source/bash-completion/+bug/219971
Danil Ilinykh reported that the handling of escaped spaces in /etc/fstab
is not ideal.

"Because of I can't use "/some path" or /some\ path in fstab, I write
some\040path as mount point. I write "mount /some" in bash and press
Tab, I see "mount /some\\040path" instead of "mount /some\ path""

I can verify this behaviour on sid.

Thanks,

James

----------------------------------------------------------------------

You can respond by visiting: 
https://alioth.debian.org/tracker/?func=detail&atid=413095&aid=311410&group_id=100114



More information about the Bash-completion-devel mailing list