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

Re: Non-escaped special chars from var expansion



On Fri, 2 Nov 2018 13:39:26 -0700
Wayne Davison <wayne@xxxxxxxxxxxxx> wrote:
> I have a particular compinit setup where special-chars from a var (other
> than spaces) are not backslash-escaped.  To duplicate:
> 
> zsh -f
> autoload -Uz compinit
> compinit
> zstyle ':completion:*' completer _oldlist _expand _complete _match _ignored
> _files _prefix
> zstyle ':completion:*:expand:*' tag-order all-expansions
> bindkey -e
> bindkey '\t' complete-word
> mkdir 'foo; bar (baz)'
> cd !:1
> cd $PWD<tab>
> 
> The result is "cd foo;\ bar\ (baz)" instead of "cd foo\;\ bar\ \(baz\)".

Don't breathe too loudly but I *think* I might have worked out what's
going on.  (Which the author of the code obviously wasn't banking on.)

Your file name is making the attempted glob fail; it's been
deliberately silenced, so you don't notice.  But that also has the
effect of abandoning the attempt to quote the expression.

So I think we need to apply the quoting without the "~" that turns on
active glob characters any time we didn't get globbing to work.

(Whether the globbing should actually have worked is a question I
haven't gone into.  But I suspect it's not a problem --- the _expand
function is trying lots of things not all of which will work.)

Maybe?

diff --git a/Completion/Base/Completer/_expand b/Completion/Base/Completer/_expand
index ee3681bad..f4909826a 100644
--- a/Completion/Base/Completer/_expand
+++ b/Completion/Base/Completer/_expand
@@ -103,9 +103,19 @@ subd=("$exp[@]")
 
 # Now try globbing.
 
-[[ "$force" = *g* ]] || zstyle -T ":completion:${curcontext}:" glob &&
-    eval 'exp=( ${~exp//(#b)\\([ 	\"'"\'"'
+# We need to come out of this with consistent quoting, by hook or by crook.
+integer done_quote
+local orig_exp=$exp
+if [[ "$force" = *g* ]] || zstyle -T ":completion:${curcontext}:" glob; then
+  eval 'exp=( ${~exp//(#b)\\([ 	\"'"\'"'
+])/$match[1]} ); exp=( ${(q)exp} )' 2>/dev/null && done_quote=1
+fi
+# If the globbing failed, or we didn't try globbing, we'll do
+# it again without the "~" so globbing is simply omitted.
+if (( ! done_quote )); then
+  eval 'exp=( ${orig_exp//(#b)\\([ 	\"'"\'"'
 ])/$match[1]} ); exp=( ${(q)exp} )' 2>/dev/null
+fi
 
 ### Don't remember why we once used this instead of the (q) above.
 #    eval 'exp=( ${~exp} ); exp=( ${exp//(#b)([][()|*?^#~<>\\=])/\\${match[1]}} )' 2>/dev/null



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