about summary refs log tree commit diff
path: root/Completion/Core
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-04-15 18:20:19 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-04-15 18:20:19 +0000
commit04a89199d02a3ee6c4b3d89a6c782bdb0a4f1bc8 (patch)
tree2215f99f95d55660fc939a029bf965c454d080b5 /Completion/Core
parent7a0415cfd70a02b2280d27556c6c54cef1c86e1a (diff)
downloadzsh-04a89199d02a3ee6c4b3d89a6c782bdb0a4f1bc8.tar.gz
zsh-04a89199d02a3ee6c4b3d89a6c782bdb0a4f1bc8.tar.xz
zsh-04a89199d02a3ee6c4b3d89a6c782bdb0a4f1bc8.zip
zsh-3.1.5-pws-12 zsh-3.1.5-pws-12
Diffstat (limited to 'Completion/Core')
-rw-r--r--Completion/Core/.distfiles5
-rw-r--r--Completion/Core/_compalso6
-rw-r--r--Completion/Core/_files11
-rw-r--r--Completion/Core/_main_complete118
-rw-r--r--Completion/Core/_normal16
-rw-r--r--Completion/Core/_path_files141
-rw-r--r--Completion/Core/_sep_parts171
-rw-r--r--Completion/Core/compdump2
-rw-r--r--Completion/Core/compinit48
9 files changed, 419 insertions, 99 deletions
diff --git a/Completion/Core/.distfiles b/Completion/Core/.distfiles
new file mode 100644
index 000000000..ddf2a707e
--- /dev/null
+++ b/Completion/Core/.distfiles
@@ -0,0 +1,5 @@
+DISTFILES_SRC='
+    .distfiles
+    _compalso _files _main_complete _multi_parts _normal _path_files
+    _sep_parts compdump compinit
+'
diff --git a/Completion/Core/_compalso b/Completion/Core/_compalso
index 6ff6cf0bf..52fb08f05 100644
--- a/Completion/Core/_compalso
+++ b/Completion/Core/_compalso
@@ -7,7 +7,9 @@
 # `_compalso -math-' to get the completions that would be generated for a
 # mathematical context.
 
-local tmp
+local tmp ret=1
 
 tmp="$_comps[$1]"
-[[ -z "$tmp" ]] || "$tmp"
+[[ -z "$tmp" ]] || "$tmp" && ret=0
+
+return ret
diff --git a/Completion/Core/_files b/Completion/Core/_files
index 471824bfe..506ddbc8e 100644
--- a/Completion/Core/_files
+++ b/Completion/Core/_files
@@ -3,10 +3,9 @@
 # Utility function for completing files of a given type or any file.
 # In many cases you will want to call this one instead of _path_files().
 
-local nm=$compstate[nmatches] ret
+local nm=$compstate[nmatches] ret=1
 
-_path_files "$@"
-ret=$?
+_path_files "$@" && ret=0
 
 if [[ $# -ne 0 && compstate[nmatches] -eq nm ]]; then
   local opt opts
@@ -23,7 +22,7 @@ if [[ $# -ne 0 && compstate[nmatches] -eq nm ]]; then
     [[ "$opt" = [PSWFJVX] ]] && opts=("$opts[@]" "-$opt" "$OPTARG")
   done
 
-  _path_files "$opts[@]"
-else
-  return $ret
+  _path_files "$opts[@]" && ret=0
 fi
+
+return ret
diff --git a/Completion/Core/_main_complete b/Completion/Core/_main_complete
index 34c5a3d3c..3571b712c 100644
--- a/Completion/Core/_main_complete
+++ b/Completion/Core/_main_complete
@@ -3,36 +3,47 @@
 # The main loop of the completion code. This is what is called when 
 # completion is attempted from the command line.
 #
-# This code will automatically try to correct the string on the
-# line based on the strings generated for the context if the
-# parameter `COMPCORRECT' is set and normal completion didn't yield
-# any matches. These corrected strings will be shown in a list and
-# one can cycle through them as in a menucompletion. To use this 
-# feature, `COMPCORRECT' should be set to a number, specifying the
+# This code will automatically try to correct the string on the line
+# based on the strings generated for the context if
+# `compconfig[correct]' is set and normal completion didn't yield any
+# matches. These corrected strings will be shown in a list and one can
+#cycle through them as in a menucompletion. To use this feature,
+#`compconfig[correct]' should be set to a number, specifying the 
 # maximum number of errors that should be accepted. If the string also
 # contains a `n' or `N', the code will use the numeric argument as the
 # maximum number of errors if a numeric argument was given. If no
 # numeric argument was given, the number from the value of
-# `COMPCORRECT' will be used. E.g. with `COMPCORRECT=2n' two errors
-# will be accepted, but if the user gives another number with the
-# numeric argument, this will be prefered. Also, with `COMPCORRECT=0n',
-# normally no automatic correction will be tried, but if a numeric
-# argument is given, automatic correction will be used. Once the
-# number of errors to accept is determined, the code will repeatedly
-# try to generate matches by allowing one error, two errors, and so
-# on.
-# If the parameter `CCORIG' is set (independent of the value), the
-# line will first be left unchanged and consecutive TABs cycle through 
-# the list.
-# When using automatic correction, one can also set the parameter
-# `CCPROMPT' to a string that will be shown when multiple
-# correction results are displayed and the code starts cycling
-# through them (this string is used with the `-X' option and thus may
-# contain the control sequences `%n', `%B',...).
-
-local comp name _comp_correct comax
-
-setopt localoptions nullglob rcexpandparam globdots
+# `compconfig[correct]' will be used. E.g. with `compconfig[correct]=2n'
+# two errors will be accepted, but if the user gives another number
+# with the numeric argument, this will be prefered. Also, with
+# `compconfig[correct]=0n',normally no automatic correction will be
+# tried, but if a numeric argument is given, automatic correction will
+# be used. Once the number of errors to accept is determined, the code
+# will repeatedly try to generate matches by allowing one error, two
+# errors, and so on. Independent of the number of errors the user
+# wants to accept, the code will allow only fewer errors than there
+# are characters in the string from the line.
+# The value of `compconfig[correct_orig]' is used to determine if the
+# original string should be included in the list (and thus be
+# presented to the user when cycling through the corrections). If it
+# is set to any non-empty value, the original string will be
+# offered. If it contains the sub-string `last', the original string
+# will apear as the last string when cycling through the corrections,
+# otherwise it will appear as the first one (so that the command line
+# does not change immediatly). Also, if the value of
+# `compconfig[correct_orig]' contains the sub-string `always', the
+# original string will always be included, whereas normally it is
+# included only if more than one possible correction was generated.
+# Finally, `compconfig[correct_prompt]' may be set to a string that
+# should be printed before the list of corrected strings when cycling
+# through them. This string may contain the control sequences `%n',
+# `%B', etc. known from the `-X' option of `compctl'. Also, the
+# sequence `%e' will be replaced by the number of errors accepted to
+# generate the corrected strings.
+
+local comp name _comp_correct _correct_prompt comax
+
+setopt localoptions nullglob rcexpandparam
 unsetopt markdirs globsubst shwordsplit nounset ksharrays
 
 # Special completion contexts after `~' and `='.
@@ -91,25 +102,30 @@ while true; do
 
   # Use automatic correction?
 
-  if (( $+COMPCORRECT )); then
+  if (( $+compconfig[correct] )); then
 
     # Do we have matches?
     if (( compstate[nmatches] )); then
 
       # Yes, were they added using correction? (More than one match?)
 
-      if [[ -n "$_comp_correct" && compstate[nmatches] -gt 1 ]]; then
+      if [[ -n "$_comp_correct" &&
+            ( "$compconfig[correct_orig]" = *always* ||
+	      compstate[nmatches] -gt 1 ) ]]; then
 
-        # If we got more than one string from correction, we add the 
-	# original string as a possible match, let it not be shown in
-	# the list, and probably display the `CCPROMPT'.
-
-        (( $+CCORIG )) && builtin compadd -nQ - "$PREFIX$SUFFIX"
+        if [[ "$compconfig[correct_orig]" = *last* ]]; then
+	  builtin compadd -V _correct_orig -nQ - "$PREFIX$SUFFIX"
+        elif [[ -n "$compconfig[correct_orig]" ]]; then
+	  builtin compadd -nQ - "$PREFIX$SUFFIX"
+	fi
 
 	# If you always want to see the list of possible corrections,
 	# set `compstate[list]=list' here.
+
+	compstate[force_list]=list
       fi
       # Since we have matches, we don't want to try again.
+
       break
     fi
 
@@ -117,25 +133,33 @@ while true; do
 
     if [[ -n "$_comp_correct" ]]; then
 
-      # Yes, give up if we reached the maximum number of tries,
-      # otherwise increment our counter.
+      # Yes, give up if we reached the maximum number of tries or the
+      # string from the line is too short, otherwise increment our 
+      # counter.
 
-      [[ _comp_correct -eq comax ]] && break
+      [[ _comp_correct -eq comax ||
+         "${#${:-$PREFIX$SUFFIX}}" -le _comp_correct+1 ]] && break
       (( _comp_correct++ ))
 
+      _correct_prompt="${compconfig[correct_prompt]//\%e/$_comp_correct}"
+
     elif [[ compstate[matcher] -eq compstate[total_matchers] ]]; then
 
+      # We don't try correction if the string is too short.
+
+      [[ "${#${:-$PREFIX$SUFFIX}}" -le 1 ]] && return
+
       # No matches and no correction tried yet, but we just tried the
       # last global match specification, so let's see if we should use
       # correction now. First, get the maximum number of errors.
 
-      if [[ "$COMPCORRECT" = *[nN]* && NUMERIC -ne 1 ]]; then
+      if [[ "$compconfig[correct]" = *[nN]* && NUMERIC -ne 1 ]]; then
         # Prefer the numeric argument if that has a sensible value.
         comax="$NUMERIC"
       else
-        comax="${COMPCORRECT//[^0-9]}"
+        comax="${compconfig[correct]//[^0-9]}"
       fi
-      # If the number of errors to accept is to small, give up.
+      # If the number of errors to accept is too small, give up.
 
       [[ "$comax" -lt 1 ]] && break
 
@@ -145,25 +169,31 @@ while true; do
       # ignored prefix).
 
       compadd() {
+        [[ "$*" != *-([a-zA-Z/]#|)U* &&
+           "${#${:-$PREFIX$SUFFIX}}" -le _comp_correct ]] && return
+
         if [[ "$PREFIX" = \~*/* ]]; then
 	  PREFIX="${PREFIX%%/*}/(#a${_comp_correct})${PREFIX#*/}"
 	else
           PREFIX="(#a${_comp_correct})$PREFIX"
 	fi
-	if (( $+CCPROMPT )); then
-	  builtin compadd -X "$CCPROMPT" -J _correct "$@"
+	if [[ -n "$_correct_prompt" ]]; then
+	  builtin compadd -X "$_correct_prompt" -J _correct "$@"
 	else
 	  builtin compadd -J _correct "$@"
 	fi
       }
       compgen() {
+        [[ "$*" != *-([a-zA-Z/]#|)U* &&
+           "${#${:-$PREFIX$SUFFIX}}" -le _comp_correct ]] && return
+
         if [[ "$PREFIX" = \~*/* ]]; then
 	  PREFIX="${PREFIX%%/*}/(#a${_comp_correct})${PREFIX#*/}"
 	else
           PREFIX="(#a${_comp_correct})$PREFIX"
 	fi
-	if (( $+CCPROMPT )); then
-	  builtin compgen "$@" -X "$CCPROMPT" -J _correct
+	if [[ -n "$_correct_prompt" ]]; then
+	  builtin compgen "$@" -X "$_correct_prompt" -J _correct
 	else
 	  builtin compgen "$@" -J _correct
 	fi
@@ -179,6 +209,8 @@ while true; do
       _comp_correct=1
       compstate[matcher]=-1
 
+      _correct_prompt="${compconfig[correct_prompt]//\%e/$_comp_correct}"
+
       # We also need to set `extendedglob' and to make the completion
       # code behave as if globcomplete were set.
 
diff --git a/Completion/Core/_normal b/Completion/Core/_normal
index f56849194..98337eae5 100644
--- a/Completion/Core/_normal
+++ b/Completion/Core/_normal
@@ -1,6 +1,6 @@
 #autoload
 
-local comp command cmd1 cmd2 pat val name i ret
+local comp command cmd1 cmd2 pat val name i ret=1
 
 # Completing in command position? If not we set up `cmd1' and `cmd2' as
 # two strings we have search in the completion definition arrays (e.g.
@@ -9,8 +9,9 @@ local comp command cmd1 cmd2 pat val name i ret
 command="$words[1]"
 if [[ CURRENT -eq 1 ]]; then
   comp="$_comps[-command-]"
-  [[ -z "$comp" ]] || "$comp"
-  return
+  [[ -z "$comp" ]] || "$comp" && ret=0
+
+  return ret
 elif [[ "$command[1]" == '=' ]]; then
   eval cmd1\=$command
   cmd2="$command[2,-1]"
@@ -28,11 +29,10 @@ for i in "$_patcomps[@]"; do
   pat="${i% *}"
   val="${i#* }"
   if [[ "$cmd1" == $~pat || "$cmd2" == $~pat ]]; then
-    "$val"
-    ret=$?
+    "$val" && ret=0
     if (( $+_compskip )); then
       unset _compskip
-      return $ret
+      return ret
     fi
   fi
 done
@@ -53,4 +53,6 @@ if [[ -z "$comp" ]]; then
   name=-default-
   comp="$_comps[-default-]"
 fi
-[[ -z "$comp" ]] || "$comp"
+[[ -z "$comp" ]] || "$comp" && ret=0
+
+return ret
diff --git a/Completion/Core/_path_files b/Completion/Core/_path_files
index 3c03c0c61..535ba537c 100644
--- a/Completion/Core/_path_files
+++ b/Completion/Core/_path_files
@@ -2,13 +2,15 @@
 
 # Utility function for in-path completion.
 # Supported arguments are: `-f', `-/', `-g <patterns>', `-J <group>',
-# `-V <group>', `-W paths', `-X explanation', and `-F <ignore>'. All but 
-# the last have the same syntax and meaning as for `compgen'. The
-# `-F <ignore>' option may be used to give a list of suffixes either by
-# giving the name of an array or literally by giving them in a string
-# surrounded by parentheses. Files with one of the suffixes thus given
-# are treated like files with one of the suffixes in the `fignore' array
-# in normal completion.
+# `-V <group>', `-W paths', `-X explanation', `-P prefix', `-S suffix',
+# `-q', `-r remove-chars', `-R remove-func', and `-F <ignore>'. All but 
+# the last have the same syntax and meaning as for `compgen' or
+# `compadd', respectively. The `-F <ignore>' option may be used to give
+# a list of suffixes either by giving the name of an array or
+# literally by giving them in a string surrounded by
+# parentheses. Files with one of the suffixes thus given are treated
+# like files with one of the suffixes in the `fignore' array in normal
+# completion.
 #
 # This function uses the helper functions `_match_test' and `_match_pattern'.
 
@@ -20,9 +22,10 @@ _match_test _path_files || return 1
 
 local nm prepaths str linepath realpath donepath patstr prepath testpath rest
 local tmp1 collect tmp2 suffixes i ignore matchflags opt group sopt pats gopt
-local addpfx addsfx expl orig ostr nm=$compstate[nmatches]
+local addpfx addsfx expl orig ostr nm=$compstate[nmatches] menu remsfx patlast
+local origflags mflags
 
-setopt localoptions nullglob rcexpandparam globdots extendedglob
+setopt localoptions nullglob rcexpandparam extendedglob
 unsetopt markdirs globsubst shwordsplit nounset
 
 prepaths=('')
@@ -33,16 +36,21 @@ gopt=''
 pats=()
 addpfx=()
 addsfx=()
+remsfx=()
 expl=()
 
 # Get the options.
 
-while getopts "P:S:W:F:J:V:X:f/g:" opt; do
+while getopts "P:S:qr:R:W:F:J:V:X:f/g:" opt; do
   case "$opt" in
   P)     addpfx=(-P "$OPTARG")
          ;;
   S)     addsfx=(-S "$OPTARG")
          ;;
+  q)     tmp1=yes
+         ;;
+  [rR])  remsfx=("-$opt" "$OPTARG")
+         ;;
   W)     tmp1="$OPTARG"
          if [[ "$tmp1[1]" = '(' ]]; then
            prepaths=( ${^=tmp1[2,-2]}/ )
@@ -76,6 +84,8 @@ while getopts "P:S:W:F:J:V:X:f/g:" opt; do
   esac
 done
 
+[[ -n "$tmp1" && $#addsfx -ne 0 ]] && addsfx[1]=-qS
+
 # If we were given no file selection option, we behave as if we were given
 # a `-f'.
 
@@ -89,19 +99,25 @@ if [[ "$sopt" = - ]]; then
 fi
 
 # str holds the whole string from the command line with a `*' between
-# the prefix and the suffix.
+# the prefix and the suffix. Then we see if we will do menucompletion.
 
-if [[ -o globcomplete ]]; then
+if [[ $#compstate[pattern_match] -ne 0 ]]; then
   str="${PREFIX}*${SUFFIX}"
 else
   str="${PREFIX:q}*${SUFFIX:q}"
+  [[ "$str" = \\\~* ]] && str="$str[2,-1]"
 fi
 orig="${PREFIX}${SUFFIX}"
 
+[[ $compstate[insert] = *menu || -n "$_comp_correct" ||
+   ( $#compstate[pattern_match] -ne 0 &&
+     "$orig" != "${orig:q}" ) ]] && menu=yes
+
+
 # We will first try normal completion called with `compgen', but only if we
-# weren't given a `-F' option.
+# weren't given a `-F', `-r', or `-R' option.
 
-if (( ! $#ignore )); then
+if [[ $#ignore -eq 0 && $#remsfx -eq 0 && -z "$_comp_correct" ]]; then
   # First build an array containing the `-W' option, if there is any and we
   # want to use it. We don't want to use it if the string from the command line
   # is a absolute path or relative to the current directory.
@@ -124,11 +140,11 @@ if (( ! $#ignore )); then
   # If this generated any matches, we don't want to do in-path completion.
 
   [[ compstate[nmatches] -eq nm ]] || return 0
+fi
 
-  # No `-F' option, so we want to use `fignore'.
+# No `-F' option, so we want to use `fignore'.
 
-  ignore=(-F fignore)
-fi
+(( $#ignore )) || ignore=(-F fignore)
 
 # Now let's have a closer look at the string to complete.
 
@@ -176,6 +192,7 @@ fi
 patstr="$str"
 matchflags=""
 _match_pattern _path_files patstr matchflags
+origflags="$matchflags"
 [[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)"
 
 # We almost expect the pattern to have changed `..' into `*.*.', `/.' into
@@ -185,13 +202,40 @@ _match_pattern _path_files patstr matchflags
 
 patstr="$patstr:gs-/-*/-:gs/*.*./../:gs-/*.-/.-:gs/**/*/:gs-.*/-./-"
 
+# We take the last pathname component from the pattern and store it in
+# `patlast', replacing `*'s in it with patterns that match any character
+# but not slashes. Later we will generate matches using `patstr' with the
+# patterns we were given (like `*.c') appended to it, producing all matching
+# files. These filenames are then compared to `patlast' and all names not
+# matching that will be removed. All this is needed to be able to correctly
+# support `completeinword' as otherwise we would have something like `a*x'
+# from the line (the `*' was inserted above) and appending the `-g' pattern
+# `*.tex' would yield `a*x*.tex' which is not what we want.
+
+if [[ "$patstr" = */* ]]; then
+  if [[ -n "$_comp_correct" && "${#orig##*/}" -le _comp_correct ]]; then
+    patlast="*/${origflags}${${patstr##*/}//\*/[^/]#}"
+  else
+    patlast="*/${matchflags}${${patstr##*/}//\*/[^/]#}"
+  fi
+  patstr="${patstr%/*}/"
+else
+  if [[ -n "$_comp_correct" && "$#orig" -le _comp_correct ]]; then
+    patlast="${origflags}${patstr//\*/[^/]#}"
+  else
+    patlast="${matchflags}${patstr//\*/[^/]#}"
+  fi
+  patstr=""
+fi
+
 # First we skip over all pathname components in `str' which really exist in
 # the file-system, so that `/usr/lib/l<TAB>' doesn't offer you `lib' and
 # `lib5'. Pathname components skipped this way are taken from `orig' and added
 # to `donepath'.
 
 while [[ "$orig" = */* ]] do
-  tmp1=( ${~matchflags}$realpath$donepath${orig%%/*}/${~patstr#*/}$^pats )
+  tmp1=( $realpath$donepath${orig%%/*}/${~matchflags}${~patstr#*/}$^pats )
+  tmp1=("${(@M)tmp1:#$~patlast}")
   [[ $#tmp1 -gt 0 && -e "$realpath$donepath${orig%%/*}" ]] || break
   donepath="$donepath${orig%%/*}/"
   orig="${orig#*/}"
@@ -217,8 +261,13 @@ for prepath in "$prepaths[@]"; do
     # we get the globbing matches for the pathname component currently
     # handled.
 
+    if [[ -n "$_comp_correct" && "${#ostr%%/*}" -le _comp_correct ]]; then
+      mflags="$origflags"
+    else
+      mflags="$matchflags"
+    fi
     rest="${str#*/}"
-    tmp1="${prepath}${realpath}${testpath}${~matchflags}${str%%/*}(-/)"
+    tmp1="${prepath}${realpath}${testpath}${~mflags}${str%%/*}(-/)"
     tmp1=( $~tmp1 )
 
     if [[ $#tmp1 -eq 0 ]]; then
@@ -240,12 +289,19 @@ for prepath in "$prepaths[@]"; do
       suffixes=( $rest$^pats )
       suffixes=( "${(@)suffixes:gs.**.*.}" )
 
+      if [[ -n "$_comp_correct" && "${#ostr#*/}" -le _comp_correct ]]; then
+        mflags="$origflags"
+      else
+        mflags="$matchflags"
+      fi
+
       # In the loop the prefixes from the `tmp1' array produced above and
       # the suffixes we just built are used to produce possible matches
       # via globbing.
 
       for i in "$tmp1[@]" ; do
-        tmp2=( ${~i}/${~matchflags}${~suffixes} )
+        tmp2=( ${~i}/${~mflags}${~suffixes} )
+        tmp2=("${(@M)tmp2:#$~patlast}")
         [[ $#tmp2 -ne 0 ]] && collect=( $collect $i )
       done
 
@@ -255,9 +311,6 @@ for prepath in "$prepaths[@]"; do
       # next `-W' path.
 
       if [[ $#collect -eq 0 ]]; then
-        compadd -QU "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \
-                -i "$IPREFIX" -p "${linepath:q}${testpath:q}" -S "/${ostr#*/}" \
-		-W "$tmp1" -f "$ignore[@]" - "${(@)tmp1:q}"
         continue 2
       elif [[ $#collect -ne 1 ]]; then
         # If we have more than one possible match, this means that the
@@ -270,6 +323,7 @@ for prepath in "$prepaths[@]"; do
 	# Now produce all matching pathnames in `collect'.
 
         collect=( ${~collect}/${~matchflags}${~suffixes} )
+	collect=("${(@M)collect:#$~patlast}")
 
 	# And then remove the common path prefix from all these matches.
 
@@ -282,15 +336,16 @@ for prepath in "$prepaths[@]"; do
 	# these are file names and that `fignore' should be used as usual
 	# (the `-f' and `-F' options).
 
-	if [[ $compstate[insert] = *menu ]]; then
+	if [[ -n "$menu" ]]; then
           compadd -QU "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \
-                  -i "$IPREFIX" -p "${linepath:q}${testpath:q}" -S "/${ostr#*/}" \
+                  -i "$IPREFIX" -p "$linepath${testpath:q}" \
+		  -s "/${ostr#*/}" \
 		  -W "$tmp1" -f "$ignore[@]" - "${(@)${(@)collect%%/*}:q}"
 	else
           for i in $collect; do
-            compadd -U "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \
-	            -i "$IPREFIX" -p "$linepath$testpath" -s "/${i#*/}" \
-		    -M 'r:|/=*' -W "$tmp1" -f "$ignore[@]" - "${i%%/*}"
+            compadd -QU "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \
+	            -i "$IPREFIX" -p "$linepath${testpath:q}" -s "/${${i#*/}:q}" \
+		    -M 'r:|/=*' -W "$tmp1" -f "$ignore[@]" - "${${i%%/*}:q}"
           done
 	fi
 
@@ -299,12 +354,27 @@ for prepath in "$prepaths[@]"; do
 
 	continue 2
       fi
+
       # We reach this point if only one of the path prefixes in `tmp1'
       # has a existing path-suffix matching the string from the line.
       # In this case we accept this match and continue with the next
       # path-name component.
 
       tmp1=( "$collect[1]" )
+    elif [[ -n "$_comp_correct" && "$mflags" = "$matchflags" ]]; then
+
+      # If we got only one match with auto-correction and if we get none
+      # without correction, stop now.
+
+      tmp2="${prepath}${realpath}${testpath}${~origflags}${str%%/*}(-/)"
+      tmp2=( $~tmp2 )
+
+      if [[ $#tmp1 -ne $#tmp2 ]]; then
+        compadd -QU "$addpfx[@]" -S '' "$group[@]" "$expl[@]" \
+                -i "$IPREFIX" -p "$linepath${testpath:q}" -s "/${ostr#*/}" \
+		- "${${tmp1#${prepath}${realpath}${testpath}}:q}"
+        continue 2
+      fi
     fi
     # This is also reached if the first globbing produced only one match
     # in this case we just continue with the next pathname component, too.
@@ -320,10 +390,17 @@ for prepath in "$prepaths[@]"; do
   # no path suffix, the `-W' we are currently handling, all the matches we
   # can produce in this directory, if any.
 
+  if [[ -n "$_comp_correct" && "${#ostr#*/}" -le _comp_correct ]]; then
+    mflags="$origflags"
+  else
+    mflags="$matchflags"
+  fi
   tmp1="$prepath$realpath$testpath"
   suffixes=( $str$^pats )
   suffixes=( "${(@)suffixes:gs.**.*.}" )
   tmp2=( ${~tmp1}${~matchflags}${~suffixes} )
+  tmp2=("${(@M)tmp2:#$~patlast}")
+
   if [[ $#tmp2 -eq 0 ]]; then
     # No match, insert the expanded path and add the original tail.
 
@@ -334,11 +411,11 @@ for prepath in "$prepaths[@]"; do
     [[ "$linepath$testpath$ostr" = "$PREFIX$SUFFIX" ]] && return 1
 
     compadd -QU -S '' "$group[@]" "$expl[@]" \
-            -i "$IPREFIX" -f - "${linepath:q}${testpath:q}$ostr"
+            -i "$IPREFIX" -f - "$linepath${testpath:q}$ostr"
   else
-    compadd -U "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \
-            -i "$IPREFIX" -p "$linepath$testpath" -f "$ignore[@]" \
-	    -W "$prepath$realpath$testpath" - "${(@)tmp2#$tmp1}"
+    compadd -QU "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" "$group[@]" "$expl[@]" \
+            -i "$IPREFIX" -p "$linepath${testpath:q}" -f "$ignore[@]" \
+	    -W "$prepath$realpath$testpath" - "${(@)${(@)tmp2#$tmp1}:q}"
   fi
 done
 
diff --git a/Completion/Core/_sep_parts b/Completion/Core/_sep_parts
new file mode 100644
index 000000000..c1cda2b9a
--- /dev/null
+++ b/Completion/Core/_sep_parts
@@ -0,0 +1,171 @@
+#autoload
+
+# This function can be used to separately complete parts of strings
+# where each part may be one of a set of matches and different parts
+# have different sets.
+# Arguments are alternatingly arrays and separator strings. Arrays may
+# be given by name or literally as words separated by white space in
+# parentheses, e.g.:
+#
+#  _sep_parts '(foo bar)' @ hosts
+#
+# This will make this function complete the strings in the array
+# `friends'. If the string on the line contains a `@', the substring
+# after it will be completed from the array `hosts'. Of course more
+# arrays may be given, each preceded by another separator string.
+#
+# This function understands the `-J group', `-V group', and
+# `-X explanation' options.
+#
+# This function does part of the matching itself and calls the functions
+# `_match_test' and `_match_pattern' for this.
+
+local str arr sep test testarr tmparr prefix suffixes matchers autosuffix
+local matchflags opt group expl nm=$compstate[nmatches]
+
+# Test if we should use this function for the global matcher in use.
+
+_match_test _sep_parts || return 1
+
+# Get the options.
+
+group=()
+expl=()
+while getopts "J:V:X:" opt; do
+  case "$opt" in
+  [JV]) group=("-$opt" "$OPTARG");;
+  X)    expl=(-X "$OPTARG");;
+  esac
+done
+shift OPTIND-1
+
+# Get the string from the line.
+
+str="$PREFIX$SUFFIX"
+[[ $#compstate[pattern_match] -ne 0 ]] || str="$str:q"
+prefix=""
+
+# Walk through the arguments to find the longest unambiguous prefix.
+
+while [[ $# -gt 1 ]]; do
+  # Get the next array and separator.
+  arr="$1"
+  sep="$2"
+
+  if [[ "$arr[1]" == '(' ]]; then
+    tmparr=( ${=arr[2,-2]} )
+    arr=tmparr
+  fi
+  # Is the separator on the line?
+  [[ "$str" != *${sep}* ]] && break
+
+  # Build a pattern matching the possible matches and get all these
+  # matches in an array.
+
+  test="${str%%${sep}*}"
+  [[ -n "$_comp_correct" && $#test -le _comp_correct ]] && return 1
+
+  matchflags=""
+  _match_pattern _sep_parts test matchflags
+  [[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)"
+
+  test="${matchflags}${test}"
+  testarr=( "${(@M)${(@P)arr}:#${~test}*}" )
+  testarr=( "${(@)testarr:#}" )
+
+  # If there are no matches we give up. If there is more than one
+  # match, this is the part we will complete.
+  (( $#testarr )) || return 1
+  [[ $#testarr -gt 1 ]] && break
+
+  # Only one match, add it to the prefix and skip over it in `str',
+  # continuing with the next array and separator.
+  prefix="${prefix}${testarr[1]}${sep}"
+  str="${str#*${sep}}"
+  shift 2
+done
+
+# Get the array to work upon.
+arr="$1"
+if [[ "$arr[1]" == '(' ]]; then
+  tmparr=( ${=arr[2,-2]} )
+  arr=tmparr
+fi
+if [[ $# -le 1 || "$str" != *${2}* ]]; then
+  # No more separators, build the matches.
+
+  test="$str"
+  [[ -n "$_comp_correct" && $#test -le _comp_correct ]] && return 1
+
+  matchflags=""
+  _match_pattern _sep_parts test matchflags
+  [[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)"
+
+  test="${matchflags}${test}"
+  testarr=( "${(@M)${(@P)arr}:#${~test}*}" )
+  testarr=( "${(@)testarr:#}" )
+fi
+
+[[ $#testarr -eq 0 || ${#testarr[1]} -eq 0 ]] && return 1
+
+# Now we build the suffixes to give to the completion code.
+shift
+matchers=()
+suffixes=("")
+autosuffix=()
+
+while [[ $# -gt 0 && "$str" == *${1}* ]]; do
+  # Remove anything up to the the suffix.
+  str="${str#*${1}}"
+
+  # Again, we get the string from the line up to the next separator
+  # and build a pattern from it.
+  if [[ $# -gt 2 ]]; then
+    test="${str%%${3}*}"
+  else
+    test="$str"
+  fi
+
+  [[ -n "$_comp_correct" && $#test -le _comp_correct ]] && return 1
+
+  matchflags=""
+  _match_pattern _sep_parts test matchflags
+  [[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)"
+  test="${matchflags}${test}"
+
+  # We incrementally add suffixes by appending to them the seperators
+  # and the strings from the next array that match the pattern we built.
+
+  arr="$2"
+  if [[ "$arr[1]" == '(' ]]; then
+    tmparr=( ${=arr[2,-2]} )
+    arr=tmparr
+  fi
+  tmparr=( "${(@M)${(@P)arr}:#${~test}*}" )
+  tmparr=( "${(@)tmparr:#}" )
+  suffixes=("${(@)^suffixes[@]}${1}${(@)^tmparr}")
+
+  # We want the completion code to generate the most specific suffix
+  # for us, so we collect matching specifications that allow partial
+  # word matching before the separators on the fly.
+  matchers=("$matchers[@]" "r:|${1:q}=*")
+  shift 2
+done
+
+# If we were given at least one more separator we make the completion
+# code offer it by appending it as a autoremovable suffix.
+(( $# )) && autosuffix=(-qS "$1")
+
+# If we have collected matching specifications, we build an array
+# from it that can be used as arguments to `compadd'.
+[[ $#matchers -gt 0 ]] && matchers=(-M "$matchers")
+
+# Add the matches for each of the suffixes.
+for i in "$suffixes[@]"; do
+  compadd -U "$group[@]" "$expl[@]" "$matchers[@]" "$autosuffix[@]" \
+          -i "$IPREFIX" -p "$prefix" -s "$i" - "$testarr[@]"
+done
+
+# This sets the return value to indicate that we added matches (or not).
+
+[[ nm -ne compstate[nmatches] ]]
diff --git a/Completion/Core/compdump b/Completion/Core/compdump
index 8be096f50..5fdee8c7a 100644
--- a/Completion/Core/compdump
+++ b/Completion/Core/compdump
@@ -15,7 +15,7 @@
 # Print the number of files used for completion. This is used in compinit
 # to see if auto-dump should re-dump the dump-file.
 
-_d_file=${COMPDUMP-${0:h}/compinit.dump}
+_d_file=${compconfig[dump_file]-${0:h}/compinit.dump}
 
 typeset -U _d_files
 _d_files=( ${^~fpath}/_*~*~(N:t) )
diff --git a/Completion/Core/compinit b/Completion/Core/compinit
index ec5867838..31d011565 100644
--- a/Completion/Core/compinit
+++ b/Completion/Core/compinit
@@ -37,18 +37,24 @@
 # Note that no white space is allowed between the `#' and the rest of
 # the string.
 #
-# See the file `compdump' for how to speed up initialiation.
+# Functions that are used to generate matches should return zero if they
+# were able to add matches and non-zero otherwise.
+#
+# See the file `compdump' for how to speed up initialisation.
 #
 # If you are using global matching specifications with `compctl -M ...'
 # have a look at the files `_match_test' and `_match_pattern'. To make
 # all the example functions use matching as specified with `-M' these
 # need some editing.
-#
+
 # If we got the `-d'-flag, we will automatically dump the new state (at
 # the end).
+# If we were given an argument, this will be taken as the name of the
+# file in which to store the dump.
 
 if [[ "$1" = -d ]]; then
   _i_autodump=1
+  shift
 else
   _i_autodump=0
 fi
@@ -59,6 +65,18 @@ fi
 typeset -A _comps
 _patcomps=()
 
+# This is the associative array used for configuration.
+
+typeset -A compconfig
+
+# Standard initialisation for `compconfig'.
+
+(( $# )) && compconfig[dump_file]="$1"
+[[ -z "$compconfig[dump_file]" ]] && compconfig[dump_file]="$0.dump"
+
+compconfig[correct_prompt]='correct to:'
+
+
 # This function is used to register or delete completion functions. For
 # registering completion functions, it is invoked with the name of the
 # function as it's first argument (after the options). The other
@@ -197,11 +215,25 @@ compdef() {
   fi
 }
 
-# Now we automatically make the definition files autoloaded.
+# Functional interface to configuration. This takes its arguments
+# and sets the according values in `compconfig'.
+# Arguments may be `foo=bar' to set key `foo' to `bar' or `baz' to
+# set key `baz' to the empty string.
 
-# First we get the name of a dump file if this will be used.
+compconf() {
+  local i name
 
-: ${COMPDUMP:=$0.dump}
+  for i; do
+    if [[ "$i" = *\=* ]]; then
+      name="${i%%\=*}"
+      compconfig[$name]="${i#*\=}"
+    else
+      compconfig[$i]=''
+    fi
+  done
+}
+
+# Now we automatically make the definition files autoloaded.
 
 if [[ ! -o extendedglob ]]; then
   _i_noextglob=yes
@@ -215,10 +247,10 @@ _i_done=''
 
 # If we have a dump file, load it.
 
-if [[ -f "$COMPDUMP" ]]; then
-  read -rA _i_line < "$COMPDUMP"
+if [[ -f "$compconfig[dump_file]" ]]; then
+  read -rA _i_line < "$compconfig[dump_file]"
   if [[ _i_autodump -eq 1 && $_i_line[2] -eq $#_i_files ]]; then
-    builtin . "$COMPDUMP"
+    builtin . "$compconfig[dump_file]"
     _i_done=yes
   fi
   unset _i_line