[Bash-completion-commits] [SCM] bash-completion branch, master, updated. eb860b7b9f88f0a42bccfc66ab72133cca7bac9f

Freddy Vulto fvulto at gmail.com
Wed Dec 2 21:40:07 UTC 2009


The following commit has been merged in the master branch:
commit eb860b7b9f88f0a42bccfc66ab72133cca7bac9f
Author: Freddy Vulto <fvulto at gmail.com>
Date:   Wed Dec 2 22:38:30 2009 +0100

    Added helper function __expand_tilde_by_ref()
    Expands only tilde (~), if first char, in variable.
    This function displays bash's capabilities of passing a variable by
    reference (variable indirection) which allows us to avoid using a
    subshell.  As far as I can see it works surprisingly well?
    
    To run the automated test:
    
        ./runUnit __expand_tilde_by_ref.exp
    
    Also fixed some testsuite issues regarding list splitting.

diff --git a/bash_completion b/bash_completion
index 12a4f3f..9732010 100644
--- a/bash_completion
+++ b/bash_completion
@@ -557,6 +557,46 @@ _available_interfaces()
     COMPREPLY=( $( compgen -W '${COMPREPLY[@]/%[[:punct:]]/}' -- "$cur" ) )
 }
 
+
+# Expand variable starting with tilde (~)
+# Only the first portion of the variable from the tilde up to the first slash
+# (~../) is expanded.  The remainder of the variable, containing for example
+# a dollar sign variable ($) or asterisk (*) is not expanded.
+# Example usage:
+#
+#    $ v="~"; __expand_tilde v; echo "$v"
+#
+# Example output:
+#
+#       v                  output
+#    --------         ----------------
+#    ~                /home/user
+#    ~foo/bar         /home/foo/bar
+#    ~foo/$HOME       /home/foo/$HOME
+#    ~foo/a  b        /home/foo/a  b
+#    ~foo/*           /home/foo/*
+#  
+# @param $1  Name of variable (not the value of the variable) to expand
+__expand_tilde_by_ref() {
+    # Does $1 start with tilde (~)?
+    if [ "${!1:0:1}" = "~" ]; then
+        # Does $1 contain slash (/)?
+        if [ "${!1}" != "${!1//\/}" ]; then
+            # Yes, $1 contains slash;
+            # 1: Remove * including and after first slash (/), i.e. "~a/b"
+            #    becomes "~a".  Double quotes allow eval.
+            # 2: Remove * before the first slash (/), i.e. "~a/b"
+            #    becomes "b".  Single quotes prevent eval.
+            #       +-----1----+ +---2----+
+            eval $1="${!1/%\/*}"/'${!1#*/}'
+        else 
+            # No, $1 doesn't contain slash
+            eval $1="${!1}"
+        fi
+    fi
+} # __expand_tilde_by_ref()
+
+
 # This function expands tildes in pathnames
 #
 _expand()
@@ -1270,8 +1310,8 @@ _known_hosts_real()
         for i in "${tmpkh[@]}"; do
             # Remove possible quotes
             i=${i//\"}
-            # Eval/expand `~' or `~user', only if first char is tilde (~)
-            [ "${i:0:1}" = "~" ] && i=$( eval echo "$i" )
+            # Eval/expand possible `~' or `~user'
+            __expand_tilde_by_ref i
             [ -r "$i" ] && kh=( "${kh[@]}" "$i" )
         done
         IFS=$OIFS
diff --git a/test/lib/library.exp b/test/lib/library.exp
index 7a13b9f..3fa1f8e 100644
--- a/test/lib/library.exp
+++ b/test/lib/library.exp
@@ -458,6 +458,16 @@ proc get_signals {} {
 }; # get_signals()
 
 
+# Sort list.
+# `exec sort' is used instead of `lsort' to achieve exactly the
+#  same sort order as in bash.
+# @param list $items
+# @return list  Sort list
+proc bash_sort {items} {
+    return [split [exec sort << [join $items "\n"]] "\n"]
+}; # bash_sort()
+
+
 # Expect items.
 # Break items into chunks because `expect' seems to have a limited buffer size
 # @param list $items
@@ -466,8 +476,8 @@ proc get_signals {} {
 proc match_items {items test {size 20}} {
     # NOTE: `exec sort' is used instead of `lsort' to achieve exactly the
     #       same sort order as in bash -- FVu, Wed Nov 25 22:25:28 CET 2009
-    #set items [list [exec sort << [join $items "\n"]]]
-    set items [exec sort << [join $items "\n"]]
+    set items [bash_sort $items]
+    #set items [exec sort << [join $items "\n"]]
     set result false
     for {set i 0} {$i < [llength $items]} {set i [expr {$i + $size}]} {
         set expected ""
diff --git a/test/unit/__expand_tilde_by_ref.exp b/test/unit/__expand_tilde_by_ref.exp
new file mode 100644
index 0000000..d4a75d1
--- /dev/null
+++ b/test/unit/__expand_tilde_by_ref.exp
@@ -0,0 +1,87 @@
+# @param string $out  Reference to variable to hold value of bash environment
+#                     variable $HOME.
+proc setup {home user} {
+    upvar $home _home
+    upvar $user _user
+    save_env
+    assert_bash_exec {echo "$HOME"} {} /@ _home
+    set _home [string trim $_home]
+    assert_bash_exec {echo "$USER"} {} /@ _user
+    set _user [string trim $_user]
+}; # setup()
+
+
+proc teardown {} {
+    assert_env_unmodified {
+        /var=/d
+    }
+}; # teardown()
+
+
+setup home user
+
+
+set test "function should run without errors"
+assert_bash_exec {__expand_tilde_by_ref > /dev/null} $test
+
+
+sync_after_int
+
+
+set test "function should not pollute environment"
+# NOTE: A possible environment pollution is detected by assert_env_modified() in teardown()
+assert_bash_exec {foo() { local aa="~"; __expand_tilde_by_ref aa; }; foo; unset foo} $test
+
+
+sync_after_int
+
+
+set test "~user should return /home/user"
+set cmd [format {var="~%s"; __expand_tilde_by_ref var; printf "%%s" "$var"} $user]
+assert_bash_list "$home" $cmd $test
+
+
+sync_after_int
+
+
+set test "~/foo should return /home/user/foo"
+set cmd {var='~/foo'; __expand_tilde_by_ref var; printf "%s" "$var"}
+assert_bash_list "$home/foo" $cmd $test
+
+
+sync_after_int
+
+
+set test "~user/bar should return /home/user/bar"
+set cmd [format {var="~%s/bar"; __expand_tilde_by_ref var; printf "%%s" "$var"} $user]
+assert_bash_list "$home/bar" $cmd $test
+
+
+sync_after_int
+
+
+set test "~user/\$HOME should return /home/user/\$HOME"
+set cmd [format {var="~%s/\$HOME"; __expand_tilde_by_ref var; printf "%%s" "$var"} $user]
+assert_bash_list "$home/\$HOME" $cmd $test
+
+
+sync_after_int
+
+
+set test "'~user/a  b' should return '/home/user/a  b'"
+set cmd [format {var="~%s/a  b"; __expand_tilde_by_ref var; printf "%%s" "$var"} $user]
+assert_bash_list [list [format {%s/a  b} $home]] $cmd $test
+
+
+sync_after_int
+
+
+set test "~user/* should return /home/user/*"
+set cmd [format {var="~%s/*"; __expand_tilde_by_ref var; printf "%%s" "$var"} $user]
+assert_bash_list "$home/\*" $cmd $test
+
+
+sync_after_int
+
+
+teardown
diff --git a/test/unit/_get_cword.exp b/test/unit/_get_cword.exp
index ec29bf1..5d032eb 100644
--- a/test/unit/_get_cword.exp
+++ b/test/unit/_get_cword.exp
@@ -57,7 +57,7 @@ sync_after_int
 
 set test {a b\ c| should return b\ c};  # | = cursor position
 set cmd {COMP_WORDS=(a 'b\ c'); COMP_CWORD=1; COMP_LINE='a b\ c'; COMP_POINT=6; _get_cword}
-assert_bash_list {{"b\\ c"}} $cmd $test
+assert_bash_list {"b\\ c"} $cmd $test
 
 
 sync_after_int
@@ -65,7 +65,7 @@ sync_after_int
 
 set test {a b\| c should return b\ };  # | = cursor position
 set cmd {COMP_WORDS=(a 'b\ c'); COMP_CWORD=1; COMP_LINE='a b\ c'; COMP_POINT=4; _get_cword}
-assert_bash_list {{"b\\"}} $cmd $test
+assert_bash_list {"b\\"} $cmd $test
 
 
 sync_after_int
@@ -73,7 +73,7 @@ sync_after_int
 
 set test {a "b\| should return "b\ };  # | = cursor position
 set cmd {COMP_WORDS=(a '"b\'); COMP_CWORD=1; COMP_LINE='a "b\'; COMP_POINT=5; _get_cword}
-assert_bash_list {{"\"b\\"}} $cmd $test
+assert_bash_list {"\"b\\"} $cmd $test
 
 
 sync_after_int
diff --git a/test/unit/_known_hosts_real.exp b/test/unit/_known_hosts_real.exp
index 0cdf63c..152cf88 100644
--- a/test/unit/_known_hosts_real.exp
+++ b/test/unit/_known_hosts_real.exp
@@ -53,9 +53,9 @@ lappend hosts gee hus two
 set hosts_config $hosts
     # Hosts `doo' and `ike' are defined in `./fixtures/_known_hosts_/spaced  known_hosts'
 lappend hosts doo ike
-set hosts        [join [lsort -ascii $hosts       ] "\\s+"]
-set hosts_orig   [join [lsort -ascii $hosts_orig  ] "\\s+"]
-set hosts_config [join [lsort -ascii $hosts_config] "\\s+"]
+set hosts        [join [bash_sort $hosts       ] "\\s+"]
+set hosts_orig   [join [bash_sort $hosts_orig  ] "\\s+"]
+set hosts_config [join [bash_sort $hosts_config] "\\s+"]
     # Call _known_hosts
 set cmd {unset COMPREPLY; _known_hosts_real -aF 'fixtures/_known_hosts_real/spaced  conf' ''; echo_array COMPREPLY}
 send "$cmd\r"
@@ -77,7 +77,7 @@ set hosts [get_hosts]
     # Host `two' is defined in ./fixtures/_known_hosts_real/known_hosts2
     # Host `three' is defined in ./fixtures/_known_hosts_real/known_hosts3
 lappend hosts two three
-set hosts [join [lsort -ascii $hosts] "\\s+"]
+set hosts [join [bash_sort $hosts] "\\s+"]
     # Setup environment
 set cmd {OLDHOME=$HOME; HOME=$TESTDIR}
 send "$cmd\r"

-- 
bash-completion



More information about the Bash-completion-commits mailing list