about summary refs log tree commit diff
path: root/Completion
diff options
context:
space:
mode:
authorPaul Ackersviller <packersv@users.sourceforge.net>2007-11-26 01:50:18 +0000
committerPaul Ackersviller <packersv@users.sourceforge.net>2007-11-26 01:50:18 +0000
commitbe0e1188ac3e6e4832cb56bb08b30c74d23af3dc (patch)
treea6b0a7add9f0b8e4a91f89446c923f93dfe6d031 /Completion
parentc84618416b0b25d8e6f628c3fac60cb0f654e997 (diff)
downloadzsh-be0e1188ac3e6e4832cb56bb08b30c74d23af3dc.tar.gz
zsh-be0e1188ac3e6e4832cb56bb08b30c74d23af3dc.tar.xz
zsh-be0e1188ac3e6e4832cb56bb08b30c74d23af3dc.zip
Merge of users/12100: default to a case-insensitive matcher with NO_CASE_GLOB.
Diffstat (limited to 'Completion')
-rw-r--r--Completion/Unix/Type/_path_files703
1 files changed, 703 insertions, 0 deletions
diff --git a/Completion/Unix/Type/_path_files b/Completion/Unix/Type/_path_files
new file mode 100644
index 000000000..49c35c477
--- /dev/null
+++ b/Completion/Unix/Type/_path_files
@@ -0,0 +1,703 @@
+#autoload
+
+# Utility function for in-path completion. This allows `/u/l/b<TAB>'
+# to complete to `/usr/local/bin'.
+
+local linepath realpath donepath prepath testpath exppath skips skipped
+local tmp1 tmp2 tmp3 tmp4 i orig eorig pre suf tpre tsuf opre osuf cpre
+local pats haspats ignore pfx pfxsfx sopt gopt opt sdirs ignpar cfopt listsfx
+local nm=$compstate[nmatches] menu matcher mopts sort match mid accex fake
+
+typeset -U prepaths exppaths
+
+exppaths=()
+
+# Get the options.
+
+zparseopts -a mopts \
+    'P:=pfx' 'S:=pfxsfx' 'q=pfxsfx' 'r:=pfxsfx' 'R:=pfxsfx' \
+    'W:=prepaths' 'F:=ignore' 'M+:=matcher' \
+    J+: V+: X+: 1 2 n 'f=tmp1' '/=tmp1' 'g+:-=tmp1'
+
+sopt="-${(@j::M)${(@)tmp1#-}#?}"
+(( $tmp1[(I)-[/g]*] )) && haspats=yes
+(( $tmp1[(I)-g*] )) && gopt=yes
+if (( $tmp1[(I)-/] )); then
+  pats="${(@)${(@M)tmp1:#-g*}#-g}"
+  pats=( '*(-/)' ${${(z):-x $pats}[2,-1]} )
+else
+  pats="${(@)${(@M)tmp1:#-g*}#-g}"
+  pats=( ${${(z):-x $pats}[2,-1]} )
+fi
+pats=( "${(@)pats:# #}" )
+
+if (( $#pfx )); then
+  compset -P "$pfx[2]" || pfxsfx=( "$pfx[@]" "$pfxsfx[@]" )
+fi
+
+if (( $#prepaths )); then
+  tmp1="${prepaths[2]}"
+  if [[ "$tmp1[1]" = '(' ]]; then
+    prepaths=( ${^=tmp1[2,-2]%/}/ )
+  elif [[ "$tmp1[1]" = '/' ]]; then
+    prepaths=( "${tmp1%/}/" )
+  else
+    prepaths=( ${(P)^tmp1%/}/ )
+    (( ! $#prepaths )) && prepaths=( ${tmp1%/}/ )
+  fi
+  (( ! $#prepaths )) && prepaths=( '' )
+else
+  prepaths=( '' )
+fi
+
+if (( $#ignore )); then
+  if [[ "${ignore[2]}" = \(* ]]; then
+    ignore=( ${=ignore[2][2,-2]} )
+  else
+    ignore=( ${(P)ignore[2]} )
+  fi
+fi  
+
+# If we were given no file selection option, we behave as if we were given
+# a `-f'.
+
+if [[ "$sopt" = -(f|) ]]; then
+  if [[ -z "$gopt" ]]; then
+    sopt='-f'
+    pats=('*')
+  else
+    unset sopt
+  fi
+fi
+
+if (( ! $mopts[(I)-[JVX]] )); then
+  local expl
+
+  if [[ -z "$gopt" && "$sopt" = -/ ]]; then
+    _description directories expl directory
+  else
+    _description files expl file
+  fi
+  tmp1=$expl[(I)-M*]
+  if (( tmp1 )); then
+    if (( $#matcher )); then
+      matcher[2]="$matcher[2] $expl[1+tmp1]"
+    else
+      matcher=(-M "$expl[1+tmp1]")
+    fi
+  fi
+  mopts=( "$mopts[@]" "$expl[@]" )
+fi
+
+# If given no `-F' option, we may want to use $fignore, turned into patterns.
+
+[[ -z "$_comp_no_ignore" && $#ignore -eq 0 &&
+   ( -z $gopt || "$pats" = \ #\*\ # ) && -n $FIGNORE ]] && 
+    ignore=( "?*${^fignore[@]}" )
+
+if (( $#ignore )); then
+  _comp_ignore=( "$_comp_ignore[@]" "$ignore[@]" )
+  (( $mopts[(I)-F] )) || mopts=( "$mopts[@]" -F _comp_ignore )
+fi
+
+if [[ $#matcher -eq 0 && -o nocaseglob ]]; then
+  # If globbing is case insensitive and there's no matcher,
+  # do case-insensitive matching.
+  matcher=( -M 'm:{a-zA-Z}={A-Za-z}' )
+fi
+
+if (( $#matcher )); then
+  # Add the current matcher to the options to compadd.
+  mopts=( "$mopts[@]" "$matcher[@]" )
+fi
+
+if zstyle -s ":completion:${curcontext}:" file-sort tmp1; then
+  case "$tmp1" in
+  *size*)             sort=oL;;
+  *links*)            sort=ol;;
+  *(time|date|modi)*) sort=om;;
+  *access*)           sort=oa;;
+  *(inode|change)*)   sort=oc;;
+  *)                  sort=on;;
+  esac
+  [[ "$tmp1" = *rev* ]] && sort[1]=O
+
+  if [[ "$sort" = on ]]; then
+    sort=
+  else
+    mopts=( "${(@)mopts/#-J/-V}" )
+
+    tmp2=()
+    for tmp1 in "$pats[@]"; do
+      if [[ "$tmp1" = (#b)(*[^\$])"(#q"(*)")" ]]; then
+	tmp2=( "$tmp2[@]" "${match[1]}(#q${sort}${match[2]})" )
+      elif [[ "$tmp1" = (#b)(*[^\$])(\(\([^\|~]##\)\)) ]]; then
+        tmp2=( "$tmp2[@]" "${match[1]}((${sort}${match[2][3,-1]}" )
+      elif [[ "$tmp1" = (#b)(*[^\$])(\([^\|~]##\)) ]]; then
+        tmp2=( "$tmp2[@]" "${match[1]}(${sort}${match[2][2,-1]}" )
+      else
+        tmp2=( "$tmp2[@]" "${tmp1}(${sort})" )
+      fi
+    done
+    pats=( "$tmp2[@]" )
+  fi
+fi
+
+# Check if we have to skip over sequences of slashes. The value of $skips
+# is used below to match the pathname components we always have to accept
+# immediately.
+
+if zstyle -t ":completion:${curcontext}:paths" squeeze-slashes; then
+  skips='((.|..|)/)##'
+else
+  skips='((.|..)/)##'
+fi
+
+zstyle -s ":completion:${curcontext}:paths" special-dirs sdirs
+zstyle -t ":completion:${curcontext}:paths" list-suffixes &&
+    listsfx=yes
+
+[[ "$pats" = ((|*[[:blank:]])\*(|[[:blank:]]*|\([^[:blank:]]##\))|*\([^[:blank:]]#/[^[:blank:]]#\)*) ]] &&
+    sopt=$sopt/
+
+zstyle -a ":completion:${curcontext}:paths" accept-exact accex
+zstyle -a ":completion:${curcontext}:" fake-files fake
+
+zstyle -s ":completion:${curcontext}:" ignore-parents ignpar
+
+if [[ -n "$compstate[pattern_match]" &&
+      ( ( -z "$SUFFIX" && "$PREFIX" = (|*[^\$])\([^\|\~]##\) ) ||
+        "$SUFFIX" =  (|*[^\$])\([^\|\~]##\) ) ]]; then
+  # Copy all glob qualifiers from the line to
+  # the patterns used when generating matches
+  if [[ "$SUFFIX" = *\([^\|\~]##\) ]]; then
+    tmp3="${${(M)SUFFIX%\([^\|\~]##\)}[2,-2]}"
+    SUFFIX="${SUFFIX%\($tmp3\)}"
+  else
+    tmp3="${${(M)PREFIX%\([^\|\~]##\)}[2,-2]}"
+    PREFIX="${PREFIX%\($tmp3\)}"
+  fi
+  tmp2=()
+  for tmp1 in "$pats[@]"; do
+    if [[ "$tmp1" = (#b)(*[^\$])"(#q"(*)")" ]]; then
+      tmp2=( "$tmp2[@]" "${match[1]}(#q${tmp3}${match[2]})" )
+    elif [[ "$tmp1" = (#b)(*[^\$])(\(\([^\|~]##\)\)) ]]; then
+      tmp2=( "$tmp2[@]" "${match[1]}((${tmp3}${match[2][3,-1]}" )
+    elif [[ "$tmp1" = (#b)(*[^\$])(\([^\|~]##\)) ]]; then
+      tmp2=( "$tmp2[@]" "${match[1]}(${tmp3}${match[2][2,-1]}" )
+    else
+      tmp2=( "$tmp2[@]" "${tmp1}(${tmp3})" )
+    fi
+  done
+  pats=( "$tmp2[@]" )
+fi
+
+# We get the prefix and the suffix from the line and save the whole
+# original string. Then we see if we will do menu completion.
+
+pre="$PREFIX"
+suf="$SUFFIX"
+opre="$PREFIX"
+osuf="$SUFFIX"
+orig="${PREFIX}${SUFFIX}"
+eorig="$orig"
+
+[[ $compstate[insert] = (*menu|[0-9]*) || -n "$_comp_correct" ||
+   ( -n "$compstate[pattern_match]" &&
+     "${orig#\~}" != (|*[^\\])[][*?#~^\|\<\>]* ) ]] && menu=yes
+[[ -n "$_comp_correct" ]] && cfopt=-
+
+# Now let's have a closer look at the string to complete.
+
+if [[ "$pre" = [^][*?#^\|\<\>\\]#(\`[^\`]#\`|\$)*/* && "$compstate[quote]" != \' ]]; then
+
+  # If there is a parameter expansion in the word from the line, we try
+  # to complete the beast by expanding the prefix and completing anything
+  # after the first slash after the parameter expansion.
+  # This fails for things like `f/$foo/b/<TAB>' where the first `f' is
+  # meant as a partial path.
+
+  linepath="${(M)pre##*\$[^/]##/}"
+  eval 'realpath=${(e)~linepath}' 2>/dev/null
+  [[ -z "$realpath" || "$realpath" = "$linepath" ]] && return 1
+  pre="${pre#${linepath}}"
+  i='[^/]'
+  i="${#linepath//$i}"
+  orig="${orig[1,(in:i:)/][1,-2]}"
+  donepath=
+  prepaths=( '' )
+elif [[ "$pre[1]" = \~ && -z "$compstate[quote]" ]]; then
+
+  # It begins with `~', so remember anything before the first slash to be able
+  # to report it to the completion code. Also get an expanded version of it
+  # (in `realpath'), so that we can generate the matches. Then remove that
+  # prefix from the string to complete, set `donepath' to build the correct
+  # paths and make sure that the loop below is run only once with an empty
+  # prefix path by setting `prepaths'.
+
+  linepath="${pre[2,-1]%%/*}"
+  if [[ -z "$linepath" ]]; then
+    realpath="${HOME%/}/"
+  elif [[ "$linepath" = ([-+]|)[0-9]## ]]; then
+    if [[ "$linepath" != [-+]* ]]; then
+      tmp1="$linepath"
+    else
+      if [[ "$linepath" = -* ]]; then
+        tmp1=$(( $#dirstack $linepath ))
+      else
+        tmp1=$linepath[2,-1]
+      fi
+      [[ -o pushdminus ]] && tmp1=$(( $#dirstack - $tmp1 ))
+    fi
+    if (( ! tmp1 )); then
+      realpath=$PWD/
+    elif [[ tmp1 -le $#dirstack ]]; then
+      realpath=$dirstack[tmp1]/
+    else
+      _message 'not enough directory stack entries'
+      return 1
+    fi
+  elif [[ "$linepath" = [-+] ]]; then
+    realpath=${~:-\~$linepath}/
+  else
+    eval "realpath=~${linepath}/" 2>/dev/null
+    if [[ -z "$realpath" ]]; then
+      _message "unknown user \`$linepath'"
+      return 1
+    fi
+  fi
+  linepath="~${linepath}/"
+  [[ "$realpath" = "$linepath" ]] && return 1
+  pre="${pre#*/}"
+  orig="${orig#*/}"
+  donepath=
+  prepaths=( '' )
+else
+  # If the string does not start with a `~' we don't remove a prefix from the
+  # string.
+
+  linepath=
+  realpath=
+
+  if zstyle -s ":completion:${curcontext}:" preserve-prefix tmp1 &&
+     [[ -n "$tmp1" && "$pre" = (#b)(${~tmp1})* ]]; then
+
+    pre="$pre[${#match[1]}+1,-1]"
+    orig="$orig[${#match[1]}+1,-1]"
+    donepath="$match[1]"
+    prepaths=( '' )
+
+  elif [[ "$pre[1]" = / ]]; then
+    # If it is a absolute path name, we remove the first slash and put it in
+    # `donepath' meaning that we treat it as the path that was already handled.
+    # Also, we don't use the paths from `-W'.
+
+    pre="$pre[2,-1]"
+    orig="$orig[2,-1]"
+    donepath='/'
+    prepaths=( '' )
+  else
+    # The common case, we just use the string as it is, unless it begins with
+    # `./' or `../' in which case we don't use the paths from `-W'.
+    
+    [[ "$pre" = (.|..)/* ]] && prepaths=( '' )
+    donepath=
+  fi
+fi
+
+# Now we generate the matches. First we loop over all prefix paths given
+# with the `-W' option.
+
+for prepath in "$prepaths[@]"; do
+
+  # Get local copies of the prefix, suffix, and the prefix path to use
+  # in the following loop, which walks through the pathname components
+  # in the string from the line.
+
+  skipped=
+  cpre=
+  tpre="$pre"
+  tsuf="$suf"
+  testpath="$donepath"
+
+  tmp2="${(M)tpre##${~skips}}"
+  tpre="${tpre#$tmp2}"
+
+  tmp1=( "$prepath$realpath$donepath$tmp2" )
+
+  while true; do
+
+    # Get the prefix and suffix for matching.
+
+    if [[ "$tpre" = */* ]]; then
+      PREFIX="${tpre%%/*}"
+      SUFFIX=
+    else
+      PREFIX="${tpre}"
+      SUFFIX="${tsuf%%/*}"
+    fi
+
+    # Force auto-mounting. There might be a better way...
+    # Commented out in the hope that `pws non-canonical hack'
+    # down below does this for us.  Can be uncommented if it
+    # doesn't.
+
+    # : ${^tmp1}/${PREFIX}${SUFFIX}/.(/)
+
+    # Get the matching files by globbing.
+
+    tmp2=( "$tmp1[@]" )
+
+    if [[ "$tpre$tsuf" = */* ]]; then
+      compfiles -P$cfopt tmp1 accex "$skipped" "$_matcher $matcher[2]" "$sdirs" fake
+    elif [[ "$sopt" = *[/f]* ]]; then
+      compfiles -p$cfopt tmp1 accex "$skipped" "$_matcher $matcher[2]" "$sdirs" fake "$pats[@]"
+    else
+      compfiles -p$cfopt tmp1 accex "$skipped" "$_matcher $matcher[2]" '' fake "$pats[@]"
+    fi
+    tmp1=( $~tmp1 ) 2> /dev/null
+
+    if [[ -n "$PREFIX$SUFFIX" ]]; then
+      # See which of them match what's on the line.
+
+      # pws non-canonical hack which seems to work so far...
+      # if we didn't match by globbing, check that there is
+      # something to match by explicit name.  This is for
+      # `clever' filing systems where names pop into existence
+      # when referenced.
+      if (( ! $#tmp1 )); then
+	  for tmp3 in "$tmp2[@]"; do
+	      if [[ -n $tmp3 && $tmp3 != */ ]]; then
+		  tmp3+=/
+	      fi
+	      if [[ -e "$tmp3${(Q)PREFIX}${(Q)SUFFIX}" ]] then
+		  tmp1+=("$tmp3${(Q)PREFIX}${(Q)SUFFIX}")
+	      fi
+	  done
+      fi
+
+      if (( ! $#tmp1 )); then
+        tmp2=( ${^tmp2}/$PREFIX$SUFFIX )
+      elif [[ "$tmp1[1]" = */* ]]; then
+        if [[ -n "$_comp_correct" ]]; then
+          tmp2=( "$tmp1[@]" )
+          builtin compadd -D tmp1 "$matcher[@]" - "${(@)tmp1:t}"
+
+          if [[ $#tmp1 -eq 0 ]]; then
+            tmp1=( "$tmp2[@]" )
+	    compadd -D tmp1 "$matcher[@]" - "${(@)tmp2:t}"
+          fi
+        else
+          tmp2=( "$tmp1[@]" )
+          compadd -D tmp1 "$matcher[@]" - "${(@)tmp1:t}"
+        fi
+      else
+        tmp2=( '' )
+        compadd -D tmp1 "$matcher[@]" -a tmp1
+      fi
+
+      # If no file matches, save the expanded path and continue with
+      # the outer loop.
+
+      if (( ! $#tmp1 )); then
+ 	if [[ "$tmp2[1]" = */* ]]; then
+	  tmp2=( "${(@)tmp2#${prepath}${realpath}}" )
+	  if [[ "$tmp2[1]" = */* ]]; then
+	    tmp2=( "${(@)tmp2:h}" )
+	    compquote tmp2
+	    if [[ "$tmp2" = */ ]]; then
+	      exppaths=( "$exppaths[@]" ${^tmp2}${tpre}${tsuf} )
+	    else
+	      exppaths=( "$exppaths[@]" ${^tmp2}/${tpre}${tsuf} )
+	    fi
+          elif [[ ${tpre}${tsuf} = */* ]]; then
+	    exppaths=( "$exppaths[@]" ${tpre}${tsuf} )
+
+	    ### this once was in an `else' (not `elif')
+	  fi
+        fi
+        continue 2
+      fi
+    elif (( ! $#tmp1 )); then
+      # A little extra hack: if we were completing `foo/<TAB>' and `foo'
+      # contains no files, this will normally produce no matches and other
+      # completers might think that's it's their time now. But if the next
+      # completer is _correct or something like that, this will result in
+      # an attempt to correct a valid directory name. So we just add the
+      # original string in such a case so that the command line doesn't 
+      # change but other completers still think there are matches.
+      # We do this only if we weren't given a `-g' or `-/' option because
+      # otherwise this would keep `_files' from completing all filenames
+      # if none of the patterns match.
+
+      if [[ -z "$tpre$tsuf" && -n "$pre$suf" ]]; then
+	pfxsfx=(-S '' "$pfxsfx[@]")
+	### Don't remember what the break was good for. We explicitly
+	### execute this only when there are no matches in the directory,
+	### so why continue?
+	###
+        ### tmp1=( "$tmp2[@]" )
+	### break
+      elif [[ -n "$haspats" && -z "$tpre$tsuf$suf" && "$pre" = */ ]]; then
+	PREFIX="${opre}"
+	SUFFIX="${osuf}"
+        compadd -nQS '' - "$linepath$donepath$orig"
+        tmp4=-
+      fi
+      continue 2
+    fi
+
+    if [[ -n "$ignpar" && -z "$_comp_no_ignore" &&
+          "$tpre$tsuf" != */* && $#tmp1 -ne 0 &&
+          ( "$ignpar" != *dir* || "$pats" = '*(-/)' ) &&
+          ( "$ignpar" != *..* || "$tmp1[1]" = *../* ) ]]; then
+
+      compfiles -i tmp1 _comp_ignore "$ignpar" "$prepath$realpath$donepath"
+
+      (( $#_comp_ignore && $mopts[(I)-F] )) ||
+          mopts=( "$mopts[@]" -F _comp_ignore )
+    fi
+
+    # Step over to the next component, if any.
+
+    if [[ "$tpre" = */* ]]; then
+      tpre="${tpre#*/}"
+    elif [[ "$tsuf" = */* ]]; then
+      tpre="${tsuf#*/}"
+      tsuf=
+    else
+      break
+    fi
+
+    # There are more components, so skip over the next components and make a
+    # slash be added.
+
+    tmp1=( ${tmp1//(#b)([][()|*?^#~<>\\=])/\\${match[1]}} )
+    tmp2="${(M)tpre##((.|..|)/)##}"
+    if [[ -n "$tmp2" ]]; then
+      skipped="/$tmp2"
+      tpre="${tpre#$tmp2}"
+    else
+      skipped=/
+    fi
+  done
+
+  # The next loop searches the first ambiguous component.
+
+  tmp3="$pre$suf"
+  tpre="$pre"
+  tsuf="$suf"
+  [[ -n "${prepath}${realpath}${testpath}" ]] &&
+      tmp1=( "${(@)tmp1#${prepath}${realpath}${testpath}}" )
+
+  while true; do
+
+    # First we check if some of the files match the original string
+    # for this component. If there are some we remove all other
+    # names. This avoids having `foo' complete to `foo' and `foobar'.
+    # The return value is non-zero if the component is ambiguous.
+
+    compfiles -r tmp1 "${(Q)tmp3}"
+    tmp4=$?
+
+    if [[ "$tpre" = */* ]]; then
+      tmp2="${cpre}${tpre%%/*}"
+      PREFIX="${donepath}${linepath}${tmp2}"
+      SUFFIX="/${tpre#*/}${tsuf#*/}"
+    else
+      tmp2="${cpre}${tpre}"
+      PREFIX="${donepath}${linepath}${tmp2}"
+      SUFFIX="${tsuf}"
+    fi
+
+    # This once tested `|| [[ -n "$compstate[pattern_match]" &&
+    # "$tmp2" = (|*[^\\])[][*?#~^\|\<\>]* ]]' but it should now be smart
+    # enough to handle multiple components with patterns.
+
+    if (( tmp4 )); then
+      # It is. For menu completion we now add the possible completions
+      # for this component with the unambiguous prefix we have built
+      # and the rest of the string from the line as the suffix.
+      # For normal completion we add the rests of the filenames
+      # collected as the suffixes to make the completion code expand
+      # it as far as possible.
+
+      tmp2="$testpath"
+      if [[ -n "$linepath" ]]; then
+        compquote -p tmp2 tmp1
+      elif [[ -n "$tmp2" ]]; then
+        compquote -p tmp1
+        compquote tmp2
+      else
+        compquote tmp1 tmp2
+      fi
+
+      if [[ -z "$_comp_correct" &&
+            "$compstate[pattern_match]" = \*  && -n "$listsfx" &&
+            "$tmp2" = (|*[^\\])[][*?#~^\|\<\>]* ]]; then
+        PREFIX="$opre"
+        SUFFIX="$osuf"
+      fi
+
+      # This once tested `-n $menu ||' but our menu-completion expert says
+      # that's not what we want.
+
+      if [[ -z "$compstate[insert]" ]] ||
+         { ! zstyle -t ":completion:${curcontext}:paths" expand suffix &&
+           [[ -z "$listsfx" &&
+              ( -n "$_comp_correct" ||
+                -z "$compstate[pattern_match]" || "$SUFFIX" != */* ||
+                "${SUFFIX#*/}" = (|*[^\\])[][*?#~^\|\<\>]* ) ]] }; then
+        (( tmp4 )) && zstyle -t ":completion:${curcontext}:paths" ambiguous &&
+            compstate[to_end]=
+        if [[ "$tmp3" = */* ]]; then
+	  if [[ -z "$listsfx" || "$tmp3" != */?* ]]; then
+	    compadd -Qf "$mopts[@]" -p "$linepath$tmp2" -s "/${tmp3#*/}" \
+	            -W "$prepath$realpath$testpath" \
+		    "$pfxsfx[@]" -M "r:|/=* r:|=*" \
+		    - "${(@)tmp1%%/*}"
+          else
+	    compadd -Qf "$mopts[@]" -p "$linepath$tmp2" \
+	            -W "$prepath$realpath$testpath" \
+		    "$pfxsfx[@]" -M "r:|/=* r:|=*" \
+		    - "${(@)^tmp1%%/*}/${tmp3#*/}"
+          fi
+	else
+	  compadd -Qf "$mopts[@]" -p "$linepath$tmp2" \
+	          -W "$prepath$realpath$testpath" \
+		   "$pfxsfx[@]" -M "r:|/=* r:|=*" \
+		   -a tmp1
+	fi
+      else
+        if [[ "$tmp3" = */* ]]; then
+	  tmp4=( -Qf "$mopts[@]" -p "$linepath$tmp2"
+	         -W "$prepath$realpath$testpath"
+	         "$pfxsfx[@]" -M "r:|/=* r:|=*" )
+	  if [[ -z "$listsfx" ]]; then
+            for i in "$tmp1[@]"; do
+	      compadd "$tmp4[@]" -s "/${i#*/}" - "${i%%/*}"
+	    done
+          else
+            [[ -n "$compstate[pattern_match]" ]] && SUFFIX="${SUFFIX:s./.*/}*"
+
+            for i in "$tmp1[@]"; do
+	      compadd "$tmp4[@]" - "$i"
+	    done
+          fi
+        else
+	  compadd -Qf "$mopts[@]" -p "$linepath$tmp2" \
+                  -W "$prepath$realpath$testpath" \
+		  "$pfxsfx[@]" -M "r:|/=* r:|=*" \
+		  -a tmp1
+        fi
+      fi
+      tmp4=-
+      break
+    fi
+
+    # If we have checked all components, we stop now and add the 
+    # strings collected after the loop.
+
+    if [[ "$tmp3" != */* ]]; then
+      tmp4=
+      break
+    fi
+
+    # Otherwise we add the unambiguous component to `testpath' and
+    # take it from the filenames.
+
+    testpath="${testpath}${tmp1[1]%%/*}/"
+
+    tmp3="${tmp3#*/}"
+
+    if [[ "$tpre" = */* ]]; then
+      if [[ -z "$_comp_correct" && -n "$compstate[pattern_match]" &&
+            "$tmp2" = (|*[^\\])[][*?#~^\|\<\>]* ]]; then
+        cpre="${cpre}${tmp1[1]%%/*}/"
+      else
+        cpre="${cpre}${tpre%%/*}/"
+      fi
+      tpre="${tpre#*/}"
+    elif [[ "$tsuf" = */* ]]; then
+      [[ "$tsuf" != /* ]] && mid="$testpath"
+      if [[ -z "$_comp_correct" && -n "$compstate[pattern_match]" &&
+            "$tmp2" = (|*[^\\])[][*?#~^\|\<\>]* ]]; then
+        cpre="${cpre}${tmp1[1]%%/*}/"
+      else
+        cpre="${cpre}${tpre}/"
+      fi
+      tpre="${tsuf#*/}"
+      tsuf=
+    else
+      tpre=
+      tsuf=
+    fi
+
+    tmp1=( "${(@)tmp1#*/}" )
+  done
+
+  if [[ -z "$tmp4" ]]; then
+    if [[ "$mid" = */ ]]; then
+      PREFIX="${opre}"
+      SUFFIX="${osuf}"
+
+      tmp4="${testpath#${mid}}"
+      tmp3="${mid%/*/}"
+      tmp2="${${mid%/}##*/}"
+      if [[ -n "$linepath" ]]; then
+        compquote -p tmp3
+      else
+        compquote tmp3
+      fi
+      compquote tmp4 tmp2 tmp1
+      for i in "$tmp1[@]"; do
+        compadd -Qf "$mopts[@]" -p "$linepath$tmp3/" -s "/$tmp4$i" \
+                -W "$prepath$realpath${mid%/*/}/" \
+	        "$pfxsfx[@]" -M "r:|/=* r:|=*" - "$tmp2"
+      done
+    else
+      if [[ "$osuf" = */* ]]; then
+        PREFIX="${opre}${osuf}"
+        SUFFIX=
+      else
+        PREFIX="${opre}"
+        SUFFIX="${osuf}"
+      fi
+      tmp4="$testpath"
+      if [[ -n "$linepath" ]]; then
+        compquote -p tmp4 tmp1
+      elif [[ -n "$tmp4" ]]; then
+        compquote -p tmp1
+        compquote tmp4
+      else
+        compquote tmp4 tmp1
+      fi
+      if [[ -z "$_comp_correct" && -n "$compstate[pattern_match]" &&
+            "${PREFIX#\~}$SUFFIX" = (|*[^\\])[][*?#~^\|\<\>]* ]]; then
+        compadd -Qf -W "$prepath$realpath" "$pfxsfx[@]" "$mopts[@]" \
+                -M "r:|/=* r:|=*" - "$linepath$tmp4${(@)^tmp1}"
+      else
+        compadd -Qf -p "$linepath$tmp4" -W "$prepath$realpath$testpath" \
+	        "$pfxsfx[@]" "$mopts[@]" -M "r:|/=* r:|=*" -a tmp1
+      fi
+    fi
+  fi
+done
+
+# If we are configured to expand paths as far as possible and we collected
+# expanded paths that are different from the string on the line, we add
+# them as possible matches. Do that only if we are currently trying the
+# last entry in the matcher-list style, otherwise other match specs might
+# make the suffix that didn't match this time match in one of the following
+# attempts.
+
+if [[ _matcher_num -eq ${#_matchers} ]] &&
+   zstyle -t ":completion:${curcontext}:paths" expand prefix &&
+   [[ nm -eq compstate[nmatches] && $#exppaths -ne 0 &&
+      "$linepath$exppaths" != "$eorig" ]]; then
+  PREFIX="${opre}"
+  SUFFIX="${osuf}"
+  compadd -Q "$mopts[@]" -S '' -M "r:|/=* r:|=*" -p "$linepath" -a exppaths
+fi
+
+[[ nm -ne compstate[nmatches] ]]