Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: Zsh autocompletion with no access to a parent directory

More from the world of "The Hairiest Shell Function in History, No,
Really, I Mean, Like, Ever".

On Fri, 23 Apr 2010 22:44:28 +0100
Peter Stephenson <p.w.stephenson@xxxxxxxxxxxx> wrote:
> On Fri, 23 Apr 2010 08:01:53 -0700
> Matt Wright <matt@xxxxxxxxxxxx> wrote:
> > If you have a path with a "special" character in it, where by special
> > so far I've found space and tilde, then completion stops offering
> > results after that letter. It's particularly frustrating on a Mac, where
> > many app bundles have spaces in their names but the actual executables
> > are another two layers of directories deeper after the bundle name.
> It appears to be this, though I haven't looked too closely since I've
> heard it makes you blind.  Where is the input supposed to be unquoted?
> Er... 

Well, the input is supposed to be unquoted in two stages.  Or something.
I ran across this trying to complete the Calibre user guide,

/home/pws/ebooks/John Schember/Calibre Quick Start Guide (1)/Calibre Quick Start Guide - John Schember.epub

with accept-exact-dirs set.  That has the added interest over setting
path-completion to false that you then need a completely unquoted string
to test as a directory, and the added gotcha that if that doesn't work
it just blithely carries on doing normal completion so it doesn't
exercise the logic to put the directory onto the path immediately.

I tried this with files in directories called

1 1

just to be sure.

Matt, could you make sure this doesn't break anything?  Of course, with
code this trivial it's hard to make a mistake.  Sob.

Index: Completion/Unix/Type/_path_files
RCS file: /cvsroot/zsh/zsh/Completion/Unix/Type/_path_files,v
retrieving revision 1.51
diff -p -u -r1.51 _path_files
--- Completion/Unix/Type/_path_files	23 Apr 2010 21:52:00 -0000	1.51
+++ Completion/Unix/Type/_path_files	29 Apr 2010 21:27:45 -0000
@@ -357,16 +357,26 @@ for prepath in "$prepaths[@]"; do
   if [[ ( -n $accept_exact_dirs || -z $path_completion ) && \
-        ${(Q)pre} = (#b)(*)/([^/]#) ]]; then
+        ${pre} = (#b)(*)/([^/]#) ]]; then
     # We've been told either that we can accept an exact directory prefix
     # immediately, or that path expansion is inhibited.  Try the longest
     # path prefix first: in the first case, this saves stats in the simple
     # case and may get around automount behaviour if early components don't
     # yet exist, and in the second case this is the prefix we want to keep.
-    tmp1=$match[1]
-    tpre=$match[2]
+    #
+    # Explanation of substitution: For tmp1 and tpre, which are used further
+    # on, we need to remove quotes from everything that's not a pattern
+    # character, because the code that does the file generation only
+    # strips qutoes from pattern characters (you know better than
+    # to ask why).  Because we need to test for a real directory,
+    # however, for tmp2 we unquote everything.
+    tmp1=${match[1]}
+    tpre=${match[2]}
+    tmp2=${(Q)tmp1}
+    tmp1=${tmp1//(#b)\\([^\\\]\[\^\~\(\)\#\*\?])/$match[1]}
+    tpre=${tpre//(#b)\\([^\\\]\[\^\~\(\)\#\*\?])/$match[1]}
     while true; do
-      if [[ -z $path_completion || -d $prepath$realpath$donepath$tmp1 ]]; then
+      if [[ -z $path_completion || -d $prepath$realpath$donepath$tmp2 ]]; then
@@ -381,7 +391,14 @@ for prepath in "$prepaths[@]"; do
-  testpath="$donepath"
+  # Now we strip quoting from pattern characters, too, because
+  # testpath is used as a literal string.  I suppose we could
+  # alternatively use ${~testpath} later.
+  #
+  # I'm not sure if donepath itself should be entirely unquoted at
+  # some point but probably not here, since we need the quoted pattern
+  # characters in tmp1 below (I think).
+  testpath="${donepath//(#b)\\([\\\]\[\^\~\(\)\#\*\?])/$match[1]}"

Peter Stephenson <p.w.stephenson@xxxxxxxxxxxx>
Web page now at http://homepage.ntlworld.com/p.w.stephenson/

Messages sorted by: Reverse Date, Date, Thread, Author