]> git.madduck.net Git - etc/zsh.git/blob - .zsh/func/_path_files

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

a4aac8aa6393d9156c55e93d78cfcea5dfc8ac32
[etc/zsh.git] / .zsh / func / _path_files
1 #autoload
2
3 # TEMPORARY HACK for #486785
4
5 # Utility function for in-path completion. This allows `/u/l/b<TAB>'
6 # to complete to `/usr/local/bin'.
7
8 local linepath realpath donepath prepath testpath exppath skips skipped
9 local tmp1 tmp2 tmp3 tmp4 i orig eorig pre suf tpre tsuf opre osuf cpre
10 local pats haspats ignore pfx pfxsfx sopt gopt opt sdirs ignpar cfopt listsfx
11 local nm=$compstate[nmatches] menu matcher mopts sort mid accex fake
12 local listfiles listopts tmpdisp
13 local -a match mbegin mend
14
15 typeset -U prepaths exppaths
16
17 exppaths=()
18
19 # Get the options.
20
21 zparseopts -a mopts \
22     'P:=pfx' 'S:=pfxsfx' 'q=pfxsfx' 'r:=pfxsfx' 'R:=pfxsfx' \
23     'W:=prepaths' 'F:=ignore' 'M+:=matcher' \
24     J+: V+: X+: 1 2 n 'f=tmp1' '/=tmp1' 'g+:-=tmp1'
25
26 sopt="-${(@j::M)${(@)tmp1#-}#?}"
27 (( $tmp1[(I)-[/g]*] )) && haspats=yes
28 (( $tmp1[(I)-g*] )) && gopt=yes
29 if (( $tmp1[(I)-/] )); then
30   pats="${(@)${(@M)tmp1:#-g*}#-g}"
31   pats=( '*(-/)' ${${(z):-x $pats}[2,-1]} )
32 else
33   pats="${(@)${(@M)tmp1:#-g*}#-g}"
34   pats=( ${${(z):-x $pats}[2,-1]} )
35 fi
36 pats=( "${(@)pats:# #}" )
37
38 if (( $#pfx )); then
39   compset -P "$pfx[2]" || pfxsfx=( "$pfx[@]" "$pfxsfx[@]" )
40 fi
41
42 if (( $#prepaths )); then
43   tmp1="${prepaths[2]}"
44   if [[ "$tmp1[1]" = '(' ]]; then
45     prepaths=( ${^=tmp1[2,-2]%/}/ )
46   elif [[ "$tmp1[1]" = '/' ]]; then
47     prepaths=( "${tmp1%/}/" )
48   else
49     prepaths=( ${(P)^tmp1%/}/ )
50     (( ! $#prepaths )) && prepaths=( ${tmp1%/}/ )
51   fi
52   (( ! $#prepaths )) && prepaths=( '' )
53 else
54   prepaths=( '' )
55 fi
56
57 if (( $#ignore )); then
58   if [[ "${ignore[2]}" = \(* ]]; then
59     ignore=( ${=ignore[2][2,-2]} )
60   else
61     ignore=( ${(P)ignore[2]} )
62   fi
63 fi  
64
65 # If we were given no file selection option, we behave as if we were given
66 # a `-f'.
67
68 if [[ "$sopt" = -(f|) ]]; then
69   if [[ -z "$gopt" ]]; then
70     sopt='-f'
71     pats=('*')
72   else
73     unset sopt
74   fi
75 fi
76
77 if (( ! $mopts[(I)-[JVX]] )); then
78   local expl
79
80   if [[ -z "$gopt" && "$sopt" = -/ ]]; then
81     _description directories expl directory
82   else
83     _description files expl file
84   fi
85   tmp1=$expl[(I)-M*]
86   if (( tmp1 )); then
87     if (( $#matcher )); then
88       matcher[2]="$matcher[2] $expl[1+tmp1]"
89     else
90       matcher=(-M "$expl[1+tmp1]")
91     fi
92   fi
93   mopts=( "$mopts[@]" "$expl[@]" )
94 fi
95
96 # If given no `-F' option, we may want to use $fignore, turned into patterns.
97
98 [[ -z "$_comp_no_ignore" && $#ignore -eq 0 &&
99    ( -z $gopt || "$pats" = \ #\*\ # ) && -n $FIGNORE ]] && 
100     ignore=( "?*${^fignore[@]}" )
101
102 if (( $#ignore )); then
103   _comp_ignore=( "$_comp_ignore[@]" "$ignore[@]" )
104   (( $mopts[(I)-F] )) || mopts=( "$mopts[@]" -F _comp_ignore )
105 fi
106
107 if [[ $#matcher -eq 0 && -o nocaseglob ]]; then
108   # If globbing is case insensitive and there's no matcher,
109   # do case-insensitive matching.
110   matcher=( -M 'm:{a-zA-Z}={A-Za-z}' )
111 fi
112
113 if (( $#matcher )); then
114   # Add the current matcher to the options to compadd.
115   mopts=( "$mopts[@]" "$matcher[@]" )
116 fi
117
118 if zstyle -s ":completion:${curcontext}:" file-sort tmp1; then
119   case "$tmp1" in
120   *size*)             sort=oL;;
121   *links*)            sort=ol;;
122   *(time|date|modi)*) sort=om;;
123   *access*)           sort=oa;;
124   *(inode|change)*)   sort=oc;;
125   *)                  sort=on;;
126   esac
127   [[ "$tmp1" = *rev* ]] && sort[1]=O
128
129   if [[ "$sort" = on ]]; then
130     sort=
131   else
132     mopts=( "${(@)mopts/#-J/-V}" )
133
134     tmp2=()
135     for tmp1 in "$pats[@]"; do
136       if [[ "$tmp1" = (#b)(*[^\$])"(#q"(*)")" ]]; then
137         tmp2=( "$tmp2[@]" "${match[1]}(#q${sort}${match[2]})" )
138       elif [[ "$tmp1" = (#b)(*[^\$])(\(\([^\|~]##\)\)) ]]; then
139         tmp2=( "$tmp2[@]" "${match[1]}((${sort}${match[2][3,-1]}" )
140       elif [[ "$tmp1" = (#b)(*[^\$])(\([^\|~]##\)) ]]; then
141         tmp2=( "$tmp2[@]" "${match[1]}(${sort}${match[2][2,-1]}" )
142       else
143         tmp2=( "$tmp2[@]" "${tmp1}(${sort})" )
144       fi
145     done
146     pats=( "$tmp2[@]" )
147   fi
148 fi
149
150 # Check if we have to skip over sequences of slashes. The value of $skips
151 # is used below to match the pathname components we always have to accept
152 # immediately.
153
154 if zstyle -t ":completion:${curcontext}:paths" squeeze-slashes; then
155   skips='((.|..|)/)##'
156 else
157   skips='((.|..)/)##'
158 fi
159
160 zstyle -s ":completion:${curcontext}:paths" special-dirs sdirs
161 zstyle -t ":completion:${curcontext}:paths" list-suffixes &&
162     listsfx=yes
163
164 [[ "$pats" = ((|*[[:blank:]])\*(|[[:blank:]]*|\([^[:blank:]]##\))|*\([^[:blank:]]#/[^[:blank:]]#\)*) ]] &&
165     sopt=$sopt/
166
167 zstyle -a ":completion:${curcontext}:paths" accept-exact accex
168 zstyle -a ":completion:${curcontext}:" fake-files fake
169
170 zstyle -s ":completion:${curcontext}:" ignore-parents ignpar
171
172 if [[ -n "$compstate[pattern_match]" &&
173       ( ( -z "$SUFFIX" && "$PREFIX" = (|*[^\$])\([^\|\~]##\) ) ||
174         "$SUFFIX" =  (|*[^\$])\([^\|\~]##\) ) ]]; then
175   # Copy all glob qualifiers from the line to
176   # the patterns used when generating matches
177   if [[ "$SUFFIX" = *\([^\|\~]##\) ]]; then
178     tmp3="${${(M)SUFFIX%\([^\|\~]##\)}[2,-2]}"
179     SUFFIX="${SUFFIX%\($tmp3\)}"
180   else
181     tmp3="${${(M)PREFIX%\([^\|\~]##\)}[2,-2]}"
182     PREFIX="${PREFIX%\($tmp3\)}"
183   fi
184   tmp2=()
185   for tmp1 in "$pats[@]"; do
186     if [[ "$tmp1" = (#b)(*[^\$])"(#q"(*)")" ]]; then
187       tmp2=( "$tmp2[@]" "${match[1]}(#q${tmp3}${match[2]})" )
188     elif [[ "$tmp1" = (#b)(*[^\$])(\(\([^\|~]##\)\)) ]]; then
189       tmp2=( "$tmp2[@]" "${match[1]}((${tmp3}${match[2][3,-1]}" )
190     elif [[ "$tmp1" = (#b)(*[^\$])(\([^\|~]##\)) ]]; then
191       tmp2=( "$tmp2[@]" "${match[1]}(${tmp3}${match[2][2,-1]}" )
192     else
193       tmp2=( "$tmp2[@]" "${tmp1}(${tmp3})" )
194     fi
195   done
196   pats=( "$tmp2[@]" )
197 fi
198
199 # We get the prefix and the suffix from the line and save the whole
200 # original string. Then we see if we will do menu completion.
201
202 pre="$PREFIX"
203 suf="$SUFFIX"
204 opre="$PREFIX"
205 osuf="$SUFFIX"
206 orig="${PREFIX}${SUFFIX}"
207 eorig="$orig"
208
209 [[ $compstate[insert] = (*menu|[0-9]*) || -n "$_comp_correct" ||
210    ( -n "$compstate[pattern_match]" &&
211      "${orig#\~}" != (|*[^\\])[][*?#~^\|\<\>]* ) ]] && menu=yes
212 [[ -n "$_comp_correct" ]] && cfopt=-
213
214 # Now let's have a closer look at the string to complete.
215
216 if [[ "$pre" = [^][*?#^\|\<\>\\]#(\`[^\`]#\`|\$)*/* && "$compstate[quote]" != \' ]]; then
217
218   # If there is a parameter expansion in the word from the line, we try
219   # to complete the beast by expanding the prefix and completing anything
220   # after the first slash after the parameter expansion.
221   # This fails for things like `f/$foo/b/<TAB>' where the first `f' is
222   # meant as a partial path.
223
224   linepath="${(M)pre##*\$[^/]##/}"
225   eval 'realpath=${(e)~linepath}' 2>/dev/null
226   [[ -z "$realpath" || "$realpath" = "$linepath" ]] && return 1
227   pre="${pre#${linepath}}"
228   i='[^/]'
229   i="${#linepath//$i}"
230   orig="${orig[1,(in:i:)/][1,-2]}"
231   donepath=
232   prepaths=( '' )
233 elif [[ "$pre[1]" = \~ && -z "$compstate[quote]" ]]; then
234
235   # It begins with `~', so remember anything before the first slash to be able
236   # to report it to the completion code. Also get an expanded version of it
237   # (in `realpath'), so that we can generate the matches. Then remove that
238   # prefix from the string to complete, set `donepath' to build the correct
239   # paths and make sure that the loop below is run only once with an empty
240   # prefix path by setting `prepaths'.
241
242   linepath="${pre[2,-1]%%/*}"
243   if [[ -z "$linepath" ]]; then
244     realpath="${HOME%/}/"
245   elif [[ "$linepath" = ([-+]|)[0-9]## ]]; then
246     if [[ "$linepath" != [-+]* ]]; then
247       tmp1="$linepath"
248     else
249       if [[ "$linepath" = -* ]]; then
250         tmp1=$(( $#dirstack $linepath ))
251       else
252         tmp1=$linepath[2,-1]
253       fi
254       [[ -o pushdminus ]] && tmp1=$(( $#dirstack - $tmp1 ))
255     fi
256     if (( ! tmp1 )); then
257       realpath=$PWD/
258     elif [[ tmp1 -le $#dirstack ]]; then
259       realpath=$dirstack[tmp1]/
260     else
261       _message 'not enough directory stack entries'
262       return 1
263     fi
264   elif [[ "$linepath" = [-+] ]]; then
265     realpath=${~:-\~$linepath}/
266   else
267     eval "realpath=~${linepath}/" 2>/dev/null
268     if [[ -z "$realpath" ]]; then
269       _message "unknown user \`$linepath'"
270       return 1
271     fi
272   fi
273   linepath="~${linepath}/"
274   [[ "$realpath" = "$linepath" ]] && return 1
275   pre="${pre#*/}"
276   orig="${orig#*/}"
277   donepath=
278   prepaths=( '' )
279 else
280   # If the string does not start with a `~' we don't remove a prefix from the
281   # string.
282
283   linepath=
284   realpath=
285
286   if zstyle -s ":completion:${curcontext}:" preserve-prefix tmp1 &&
287      [[ -n "$tmp1" && "$pre" = (#b)(${~tmp1})* ]]; then
288
289     pre="$pre[${#match[1]}+1,-1]"
290     orig="$orig[${#match[1]}+1,-1]"
291     donepath="$match[1]"
292     prepaths=( '' )
293
294   elif [[ "$pre[1]" = / ]]; then
295     # If it is a absolute path name, we remove the first slash and put it in
296     # `donepath' meaning that we treat it as the path that was already handled.
297     # Also, we don't use the paths from `-W'.
298
299     pre="$pre[2,-1]"
300     orig="$orig[2,-1]"
301     donepath='/'
302     prepaths=( '' )
303   else
304     # The common case, we just use the string as it is, unless it begins with
305     # `./' or `../' in which case we don't use the paths from `-W'.
306     
307     [[ "$pre" = (.|..)/* ]] && prepaths=( '' )
308     donepath=
309   fi
310 fi
311
312 # Now we generate the matches. First we loop over all prefix paths given
313 # with the `-W' option.
314
315 for prepath in "$prepaths[@]"; do
316
317   # Get local copies of the prefix, suffix, and the prefix path to use
318   # in the following loop, which walks through the pathname components
319   # in the string from the line.
320
321   skipped=
322   cpre=
323   tpre="$pre"
324   tsuf="$suf"
325   testpath="$donepath"
326
327   tmp2="${(M)tpre##${~skips}}"
328   tpre="${tpre#$tmp2}"
329
330   tmp1=( "$prepath$realpath$donepath$tmp2" )
331
332   while true; do
333
334     # Get the prefix and suffix for matching.
335
336     if [[ "$tpre" = */* ]]; then
337       PREFIX="${tpre%%/*}"
338       SUFFIX=
339     else
340       PREFIX="${tpre}"
341       SUFFIX="${tsuf%%/*}"
342     fi
343
344     # Force auto-mounting. There might be a better way...
345     # Commented out in the hope that `pws non-canonical hack'
346     # down below does this for us.  Can be uncommented if it
347     # doesn't.
348
349     # : ${^tmp1}/${PREFIX}${SUFFIX}/.(/)
350
351     # Get the matching files by globbing.
352
353     tmp2=( "$tmp1[@]" )
354
355     # Look for glob qualifiers.
356     # Extra nastiness to be careful about a quoted parenthesis.
357     # The initial tests look for parentheses with zero or an
358     # even number of backslashes in front.
359     # The later test looks for an outstanding quote.
360     if [[ ( -o bareglobqual && \
361               "$tpre/$tsuf" = (#b)((*[^\\]|)(\\\\)#\()([^\)]#) || \
362             -o extendedglob && \
363                 "$tpre/$tsuf" = (#b)((*[^\\]|)(\\\\)#"(#q")([^\)]#) \
364           ) && -z $compstate[quote] ]]; then
365        compset -p ${#match[1]}
366        _globquals
367     elif [[ "$tpre$tsuf" = */* ]]; then
368       compfiles -P$cfopt tmp1 accex "$skipped" "$_matcher $matcher[2]" "$sdirs" fake
369     elif [[ "$sopt" = *[/f]* ]]; then
370       compfiles -p$cfopt tmp1 accex "$skipped" "$_matcher $matcher[2]" "$sdirs" fake "$pats[@]"
371     else
372       compfiles -p$cfopt tmp1 accex "$skipped" "$_matcher $matcher[2]" '' fake "$pats[@]"
373     fi
374     tmp1=( $~tmp1 ) 2> /dev/null
375
376     if [[ -n "$PREFIX$SUFFIX" ]]; then
377       # See which of them match what's on the line.
378
379       # pws non-canonical hack which seems to work so far...
380       # if we didn't match by globbing, check that there is
381       # something to match by explicit name.  This is for
382       # `clever' filing systems where names pop into existence
383       # when referenced.
384       if (( ! $#tmp1 )); then
385           for tmp3 in "$tmp2[@]"; do
386               if [[ -n $tmp3 && $tmp3 != */ ]]; then
387                   tmp3+=/
388               fi
389               if [[ -e "$tmp3${(Q)PREFIX}${(Q)SUFFIX}" ]] then
390                   tmp1+=("$tmp3${(Q)PREFIX}${(Q)SUFFIX}")
391               fi
392           done
393       fi
394
395       if (( ! $#tmp1 )); then
396         tmp2=( ${^tmp2}/$PREFIX$SUFFIX )
397       elif [[ "$tmp1[1]" = */* ]]; then
398         if [[ -n "$_comp_correct" ]]; then
399           tmp2=( "$tmp1[@]" )
400           builtin compadd -D tmp1 "$matcher[@]" - "${(@)tmp1:t}"
401
402           if [[ $#tmp1 -eq 0 ]]; then
403             tmp1=( "$tmp2[@]" )
404             compadd -D tmp1 "$matcher[@]" - "${(@)tmp2:t}"
405           fi
406         else
407           tmp2=( "$tmp1[@]" )
408           compadd -D tmp1 "$matcher[@]" - "${(@)tmp1:t}"
409         fi
410       else
411         tmp2=( '' )
412         compadd -D tmp1 "$matcher[@]" -a tmp1
413       fi
414
415       # If no file matches, save the expanded path and continue with
416       # the outer loop.
417
418       if (( ! $#tmp1 )); then
419         if [[ "$tmp2[1]" = */* ]]; then
420           tmp2=( "${(@)tmp2#${prepath}${realpath}}" )
421           if [[ "$tmp2[1]" = */* ]]; then
422             tmp2=( "${(@)tmp2:h}" )
423             compquote tmp2
424             if [[ "$tmp2" = */ ]]; then
425               exppaths=( "$exppaths[@]" ${^tmp2}${tpre}${tsuf} )
426             else
427               exppaths=( "$exppaths[@]" ${^tmp2}/${tpre}${tsuf} )
428             fi
429           elif [[ ${tpre}${tsuf} = */* ]]; then
430             exppaths=( "$exppaths[@]" ${tpre}${tsuf} )
431
432             ### this once was in an `else' (not `elif')
433           fi
434         fi
435         continue 2
436       fi
437     elif (( ! $#tmp1 )); then
438       # A little extra hack: if we were completing `foo/<TAB>' and `foo'
439       # contains no files, this will normally produce no matches and other
440       # completers might think that's it's their time now. But if the next
441       # completer is _correct or something like that, this will result in
442       # an attempt to correct a valid directory name. So we just add the
443       # original string in such a case so that the command line doesn't 
444       # change but other completers still think there are matches.
445       # We do this only if we weren't given a `-g' or `-/' option because
446       # otherwise this would keep `_files' from completing all filenames
447       # if none of the patterns match.
448
449       if [[ -z "$tpre$tsuf" && -n "$pre$suf" ]]; then
450         pfxsfx=(-S '' "$pfxsfx[@]")
451         ### Don't remember what the break was good for. We explicitly
452         ### execute this only when there are no matches in the directory,
453         ### so why continue?
454         ###
455         ### tmp1=( "$tmp2[@]" )
456         ### break
457       elif [[ -n "$haspats" && -z "$tpre$tsuf$suf" && "$pre" = */ ]]; then
458         PREFIX="${opre}"
459         SUFFIX="${osuf}"
460         compadd -nQS '' - "$linepath$donepath$orig"
461         tmp4=-
462       fi
463       continue 2
464     fi
465
466     if [[ -n "$ignpar" && -z "$_comp_no_ignore" &&
467           "$tpre$tsuf" != */* && $#tmp1 -ne 0 &&
468           ( "$ignpar" != *dir* || "$pats" = '*(-/)' ) &&
469           ( "$ignpar" != *..* || "$tmp1[1]" = *../* ) ]]; then
470
471       compfiles -i tmp1 _comp_ignore "$ignpar" "$prepath$realpath$donepath"
472
473       (( $#_comp_ignore && $mopts[(I)-F] )) ||
474           mopts=( "$mopts[@]" -F _comp_ignore )
475     fi
476
477     # Step over to the next component, if any.
478
479     if [[ "$tpre" = */* ]]; then
480       tpre="${tpre#*/}"
481     elif [[ "$tsuf" = */* ]]; then
482       tpre="${tsuf#*/}"
483       tsuf=
484     else
485       break
486     fi
487
488     # There are more components, so skip over the next components and make a
489     # slash be added.
490
491     tmp1=( ${tmp1//(#b)([][()|*?^#~<>\\=])/\\${match[1]}} )
492     tmp2="${(M)tpre##((.|..|)/)##}"
493     if [[ -n "$tmp2" ]]; then
494       skipped="/$tmp2"
495       tpre="${tpre#$tmp2}"
496     else
497       skipped=/
498     fi
499   done
500
501   # The next loop searches the first ambiguous component.
502
503   tmp3="$pre$suf"
504   tpre="$pre"
505   tsuf="$suf"
506   [[ -n "${prepath}${realpath}${testpath}" ]] &&
507       tmp1=( "${(@)tmp1#${prepath}${realpath}${testpath}}" )
508
509   while true; do
510
511     # First we check if some of the files match the original string
512     # for this component. If there are some we remove all other
513     # names. This avoids having `foo' complete to `foo' and `foobar'.
514     # The return value is non-zero if the component is ambiguous.
515
516     compfiles -r tmp1 "${(Q)tmp3}"
517     tmp4=$?
518
519     if [[ "$tpre" = */* ]]; then
520       tmp2="${cpre}${tpre%%/*}"
521       PREFIX="${donepath}${linepath}${tmp2}"
522       SUFFIX="/${tpre#*/}${tsuf#*/}"
523     else
524       tmp2="${cpre}${tpre}"
525       PREFIX="${donepath}${linepath}${tmp2}"
526       SUFFIX="${tsuf}"
527     fi
528
529     # This once tested `|| [[ -n "$compstate[pattern_match]" &&
530     # "$tmp2" = (|*[^\\])[][*?#~^\|\<\>]* ]]' but it should now be smart
531     # enough to handle multiple components with patterns.
532
533     if (( tmp4 )); then
534       # It is. For menu completion we now add the possible completions
535       # for this component with the unambiguous prefix we have built
536       # and the rest of the string from the line as the suffix.
537       # For normal completion we add the rests of the filenames
538       # collected as the suffixes to make the completion code expand
539       # it as far as possible.
540
541       tmp2="$testpath"
542       if [[ -n "$linepath" ]]; then
543         compquote -p tmp2 tmp1
544       elif [[ -n "$tmp2" ]]; then
545         compquote -p tmp1
546         compquote tmp2
547       else
548         compquote tmp1 tmp2
549       fi
550
551       if [[ -z "$_comp_correct" &&
552             "$compstate[pattern_match]" = \*  && -n "$listsfx" &&
553             "$tmp2" = (|*[^\\])[][*?#~^\|\<\>]* ]]; then
554         PREFIX="$opre"
555         SUFFIX="$osuf"
556       fi
557
558       # This once tested `-n $menu ||' but our menu-completion expert says
559       # that's not what we want.
560
561       if [[ -z "$compstate[insert]" ]] ||
562          { ! zstyle -t ":completion:${curcontext}:paths" expand suffix &&
563            [[ -z "$listsfx" &&
564               ( -n "$_comp_correct" ||
565                 -z "$compstate[pattern_match]" || "$SUFFIX" != */* ||
566                 "${SUFFIX#*/}" = (|*[^\\])[][*?#~^\|\<\>]* ) ]] }; then
567         # We have not been told to insert the match, so we are
568         # listing, or something.
569         (( tmp4 )) && zstyle -t ":completion:${curcontext}:paths" ambiguous &&
570             compstate[to_end]=
571         if [[ "$tmp3" = */* ]]; then
572           if [[ -z "$listsfx" || "$tmp3" != */?* ]]; then
573             # I think this means we are expanding some directory
574             # back up the path.
575             tmp1=("${(@)tmp1%%/*}")
576             _list_files tmp1 "$prepath$realpath$testpath"
577             compadd -Qf "$mopts[@]" -p "$linepath$tmp2" -s "/${tmp3#*/}" \
578                     -W "$prepath$realpath$testpath" \
579                     "$pfxsfx[@]" -M "r:|/=* r:|=*" \
580                     $listopts \
581                     -a tmp1
582           else
583             # Same with a non-empty suffix
584             tmp1=("${(@)^tmp1%%/*}/${tmp3#*/}")
585             _list_files tmp1 "$prepath$realpath$testpath"
586             compadd -Qf "$mopts[@]" -p "$linepath$tmp2" \
587                     -W "$prepath$realpath$testpath" \
588                     "$pfxsfx[@]" -M "r:|/=* r:|=*" \
589                     $listopts \
590                     -a tmp1
591           fi
592         else
593           _list_files tmp1 "$prepath$realpath$testpath"
594           compadd -Qf "$mopts[@]" -p "$linepath$tmp2" \
595                   -W "$prepath$realpath$testpath" \
596                    "$pfxsfx[@]" -M "r:|/=* r:|=*" \
597                    $listopts \
598                    -a tmp1
599         fi
600       else
601         # We are inserting the match into the command line.
602         if [[ "$tmp3" = */* ]]; then
603           tmp4=( -Qf "$mopts[@]" -p "$linepath$tmp2"
604                  -W "$prepath$realpath$testpath"
605                  "$pfxsfx[@]" -M "r:|/=* r:|=*" )
606           if [[ -z "$listsfx" ]]; then
607             for i in "$tmp1[@]"; do
608               tmpdisp=("${i%%/*}")
609               _list_files tmpdisp "$prepath$realpath$testpath"
610               compadd "$tmp4[@]" -s "/${i#*/}" $listopts - "$tmpdisp"
611             done
612           else
613             [[ -n "$compstate[pattern_match]" ]] && SUFFIX="${SUFFIX:s./.*/}*"
614
615             for i in "$tmp1[@]"; do
616               _list_files i "$prepath$realpath$testpath"
617               compadd "$tmp4[@]" $listopts - "$i"
618             done
619           fi
620         else
621           _list_files tmp1 "$prepath$realpath$testpath"
622           compadd -Qf "$mopts[@]" -p "$linepath$tmp2" \
623                   -W "$prepath$realpath$testpath" \
624                   "$pfxsfx[@]" -M "r:|/=* r:|=*" \
625                   $listopts \
626                   -a tmp1
627         fi
628       fi
629       tmp4=-
630       break
631     fi
632
633     # If we have checked all components, we stop now and add the 
634     # strings collected after the loop.
635
636     if [[ "$tmp3" != */* ]]; then
637       tmp4=
638       break
639     fi
640
641     # Otherwise we add the unambiguous component to `testpath' and
642     # take it from the filenames.
643
644     testpath="${testpath}${tmp1[1]%%/*}/"
645
646     tmp3="${tmp3#*/}"
647
648     if [[ "$tpre" = */* ]]; then
649       if [[ -z "$_comp_correct" && -n "$compstate[pattern_match]" &&
650             "$tmp2" = (|*[^\\])[][*?#~^\|\<\>]* ]]; then
651         cpre="${cpre}${tmp1[1]%%/*}/"
652       else
653         cpre="${cpre}${tpre%%/*}/"
654       fi
655       tpre="${tpre#*/}"
656     elif [[ "$tsuf" = */* ]]; then
657       [[ "$tsuf" != /* ]] && mid="$testpath"
658       if [[ -z "$_comp_correct" && -n "$compstate[pattern_match]" &&
659             "$tmp2" = (|*[^\\])[][*?#~^\|\<\>]* ]]; then
660         cpre="${cpre}${tmp1[1]%%/*}/"
661       else
662         cpre="${cpre}${tpre}/"
663       fi
664       tpre="${tsuf#*/}"
665       tsuf=
666     else
667       tpre=
668       tsuf=
669     fi
670
671     tmp1=( "${(@)tmp1#*/}" )
672   done
673
674   if [[ -z "$tmp4" ]]; then
675     if [[ "$mid" = */ ]]; then
676       PREFIX="${opre}"
677       SUFFIX="${osuf}"
678
679       tmp4="${testpath#${mid}}"
680       tmp3="${mid%/*/}"
681       tmp2="${${mid%/}##*/}"
682       if [[ -n "$linepath" ]]; then
683         compquote -p tmp3
684       else
685         compquote tmp3
686       fi
687       compquote tmp4 tmp2 tmp1
688       for i in "$tmp1[@]"; do
689         _list_files tmp2 "$prepath$realpath${mid%/*/}"
690         compadd -Qf "$mopts[@]" -p "$linepath$tmp3/" -s "/$tmp4$i" \
691                 -W "$prepath$realpath${mid%/*/}/" \
692                 "$pfxsfx[@]" -M "r:|/=* r:|=*" $listopts - "$tmp2"
693       done
694     else
695       if [[ "$osuf" = */* ]]; then
696         PREFIX="${opre}${osuf}"
697         SUFFIX=
698       else
699         PREFIX="${opre}"
700         SUFFIX="${osuf}"
701       fi
702       tmp4="$testpath"
703       if [[ -n "$linepath" ]]; then
704         compquote -p tmp4 tmp1
705       elif [[ -n "$tmp4" ]]; then
706         compquote -p tmp1
707         compquote tmp4
708       else
709         compquote tmp4 tmp1
710       fi
711       if [[ -z "$_comp_correct" && -n "$compstate[pattern_match]" &&
712             "${PREFIX#\~}$SUFFIX" = (|*[^\\])[][*?#~^\|\<\>]* ]]; then
713         tmp1=("$linepath$tmp4${(@)^tmp1}")
714         _list_files tmp1 "$prepath$realpath"
715         compadd -Qf -W "$prepath$realpath" "$pfxsfx[@]" "$mopts[@]" \
716                 -M "r:|/=* r:|=*" $listopts -a tmp1
717       else
718         # Not a pattern match
719         _list_files tmp1 "$prepath$realpath$testpath"
720         compadd -Qf -p "$linepath$tmp4" -W "$prepath$realpath$testpath" \
721                 "$pfxsfx[@]" "$mopts[@]" -M "r:|/=* r:|=*" $listopts -a tmp1
722       fi
723     fi
724   fi
725 done
726
727 # If we are configured to expand paths as far as possible and we collected
728 # expanded paths that are different from the string on the line, we add
729 # them as possible matches. Do that only if we are currently trying the
730 # last entry in the matcher-list style, otherwise other match specs might
731 # make the suffix that didn't match this time match in one of the following
732 # attempts.
733
734 if [[ _matcher_num -eq ${#_matchers} ]] &&
735    zstyle -t ":completion:${curcontext}:paths" expand prefix &&
736    [[ nm -eq compstate[nmatches] && $#exppaths -ne 0 &&
737       "$linepath$exppaths" != "$eorig" ]]; then
738   PREFIX="${opre}"
739   SUFFIX="${osuf}"
740   compadd -Q "$mopts[@]" -S '' -M "r:|/=* r:|=*" -p "$linepath" -a exppaths
741 fi
742
743 [[ nm -ne compstate[nmatches] ]]