]> git.donarmstrong.com Git - zsh.git/blob - .zsh/func/_path_files
add temp hack for #486785
[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] ]]