about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Completion/Base/.distfiles2
-rw-r--r--Completion/Base/_brace_parameter10
-rw-r--r--Completion/Base/_condition2
-rw-r--r--Completion/Base/_long_options1
-rw-r--r--Completion/Base/_math18
-rw-r--r--Completion/Base/_tilde8
-rw-r--r--Completion/Builtins/_cd2
-rw-r--r--Completion/Builtins/_command3
-rw-r--r--Completion/Builtins/_hash4
-rw-r--r--Completion/Builtins/_kill2
-rw-r--r--Completion/Builtins/_sched2
-rw-r--r--Completion/Builtins/_setopt17
-rw-r--r--Completion/Builtins/_source3
-rw-r--r--Completion/Builtins/_unsetopt17
-rw-r--r--Completion/Builtins/_zmodload2
-rw-r--r--Completion/Commands/_correct_filename4
-rw-r--r--Completion/Commands/_most_recent_file2
-rw-r--r--Completion/Core/.distfiles5
-rw-r--r--Completion/Core/_approximate197
-rw-r--r--Completion/Core/_complete52
-rw-r--r--Completion/Core/_correct19
-rw-r--r--Completion/Core/_expand149
-rw-r--r--Completion/Core/_list61
-rw-r--r--Completion/Core/_main_complete249
-rw-r--r--Completion/Core/_match53
-rw-r--r--Completion/Core/_multi_parts25
-rw-r--r--Completion/Core/_options5
-rw-r--r--Completion/Core/_parameters8
-rw-r--r--Completion/Core/_path_files84
-rw-r--r--Completion/Core/_sep_parts7
-rw-r--r--Completion/Core/_set_options7
-rw-r--r--Completion/Core/_unset_options7
-rw-r--r--Completion/Core/compdump2
-rw-r--r--Completion/Core/compinit13
-rw-r--r--Completion/README243
-rw-r--r--Completion/User/_dd6
-rw-r--r--Completion/User/_find6
-rw-r--r--Completion/User/_mh4
-rw-r--r--Completion/User/_stty2
-rw-r--r--Config/version.mk4
-rw-r--r--Doc/Zsh/compwid.yo213
-rw-r--r--Doc/Zsh/expn.yo97
-rw-r--r--Doc/Zsh/files.yo7
-rw-r--r--Doc/Zsh/options.yo21
-rw-r--r--Doc/Zsh/zle.yo4
-rw-r--r--Src/Modules/zftp.c4
-rw-r--r--Src/Zle/comp.h72
-rw-r--r--Src/Zle/comp1.c18
-rw-r--r--Src/Zle/comp1.export3
-rw-r--r--Src/Zle/compctl.c634
-rw-r--r--Src/Zle/compctl.mdd4
-rw-r--r--Src/Zle/zle_main.c2
-rw-r--r--Src/Zle/zle_move.c2
-rw-r--r--Src/Zle/zle_params.c11
-rw-r--r--Src/Zle/zle_thingy.c2
-rw-r--r--Src/Zle/zle_tricky.c4663
-rw-r--r--Src/glob.c2
-rw-r--r--Src/init.c24
-rw-r--r--Src/options.c11
-rw-r--r--Src/params.c2
-rw-r--r--Src/subst.c6
-rw-r--r--Src/utils.c6
-rw-r--r--Src/zsh.h1
-rw-r--r--patchlist.txt58
64 files changed, 3665 insertions, 3509 deletions
diff --git a/Completion/Base/.distfiles b/Completion/Base/.distfiles
index 7e7635fa6..6f634c0b8 100644
--- a/Completion/Base/.distfiles
+++ b/Completion/Base/.distfiles
@@ -1,6 +1,6 @@
 DISTFILES_SRC='
     .distfiles 
     _brace_parameter _command_names _condition _default _equal
-    _long_options _match_pattern _match_pattern.orig _match_test _parameter
+    _long_options _match_pattern _match_test _math _parameter
     _precommand _redirect _subscript _tilde _vars 
 '
diff --git a/Completion/Base/_brace_parameter b/Completion/Base/_brace_parameter
index 3fab8510a..3d663a777 100644
--- a/Completion/Base/_brace_parameter
+++ b/Completion/Base/_brace_parameter
@@ -1,5 +1,9 @@
 #defcomp -brace-parameter-
 
-# Simple but without spiffy suffix handling: compgen -v -S '} '
-
-compadd -S '} ' -r '-:?#%+=[/'  - "${(@)${(@)${(@f)$(set)}%%\=*}:gs/'//}"
+if [[ "$SUFFIX" = *\}* ]]; then
+  ISUFFIX="${SUFFIX#*\}}$ISUFFIX"
+  SUFFIX="${SUFFIX%%\}*}"
+  _parameters -S '} ' -r '-:?#%+=[/'
+else
+  _parameters -S '} ' -r '-:?#%+=[/'
+fi
diff --git a/Completion/Base/_condition b/Completion/Base/_condition
index db1adfd9a..df1bf913e 100644
--- a/Completion/Base/_condition
+++ b/Completion/Base/_condition
@@ -3,7 +3,7 @@
 local prev="$words[CURRENT-1]"
 
 if [[ "$prev" = -o ]]; then
-  compgen -o -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}'
+  _options
 elif [[ "$prev" = -([no]t|ef) ]]; then
   _files
 else
diff --git a/Completion/Base/_long_options b/Completion/Base/_long_options
index cc3175592..97b11f2dc 100644
--- a/Completion/Base/_long_options
+++ b/Completion/Base/_long_options
@@ -212,6 +212,7 @@ if [[ "$str" = *\=* ]]; then
   pat="${pre}*"
   patflags=''
   _match_pattern _long_options pat patflags
+
   [[ -n "$_comp_correct" ]] && patflags="$patflags(#a$_comp_correct)"
 
   # Then we walk through the array names. For each array we test if it 
diff --git a/Completion/Base/_math b/Completion/Base/_math
new file mode 100644
index 000000000..f7f4c360f
--- /dev/null
+++ b/Completion/Base/_math
@@ -0,0 +1,18 @@
+#defcomp -math-
+
+if [[ "$PREFIX" = *[^a-zA-Z0-9_]* ]]; then
+  IPREFIX="$IPREFIX${PREFIX%%[a-zA-Z0-9_]#}"
+  PREFIX="${PREFIX##*[^a-zA-Z0-9_]}"
+fi
+if [[ "$SUFFIX" = *[^a-zA-Z0-9_]* ]]; then
+  ISUFFIX="${SUFFIX##[a-zA-Z0-9_]#}$ISUFFIX"
+  SUFFIX="${SUFFIX%%[^a-zA-Z0-9_]*}"
+fi
+
+compgen -v
+#defcomp -math-
+
+IPREFIX="$IPREFIX${PREFIX%[a-zA-Z0-9_]*}"
+PREFIX="${PREFIX##*[^a-zA-Z0-9_]}"
+
+compgen -v
diff --git a/Completion/Base/_tilde b/Completion/Base/_tilde
index aef575e19..c5ebb66d5 100644
--- a/Completion/Base/_tilde
+++ b/Completion/Base/_tilde
@@ -7,4 +7,10 @@
 #   `(( compstate[nmatches] )) || compgen -nu -qS/'
 # below that.
 
-compgen -nu -qS/
+if [[ "$SUFFIX" = */* ]]; then
+  ISUFFIX="/${SUFFIX#*/}$ISUFFIX"
+  SUFFIX="${SUFFIX%%/*}"
+  compgen -nu -S ''
+else
+  compgen -nu -qS/
+fi
diff --git a/Completion/Builtins/_cd b/Completion/Builtins/_cd
index 44443c3d5..a4fd56327 100644
--- a/Completion/Builtins/_cd
+++ b/Completion/Builtins/_cd
@@ -17,7 +17,7 @@ local pushdminus
 emulate -LR zsh
 setopt extendedglob
 
-if [[ -position 3 ]]; then
+if [[ CURRENT -eq 3 ]]; then
   # cd old new: look for old in $PWD and see what can replace it
   local rep
   # Get possible completions using word in position 2
diff --git a/Completion/Builtins/_command b/Completion/Builtins/_command
index 47eb0d667..4facceb12 100644
--- a/Completion/Builtins/_command
+++ b/Completion/Builtins/_command
@@ -1,6 +1,7 @@
 #defcomp command
 
-if [[ -position 3 -1 ]]; then
+if [[ CURRENT -ge 3 ]]; then
+  compset -n 2
   _normal
 else
   compgen -em
diff --git a/Completion/Builtins/_hash b/Completion/Builtins/_hash
index 8c100b801..4d4ba4244 100644
--- a/Completion/Builtins/_hash
+++ b/Completion/Builtins/_hash
@@ -1,12 +1,12 @@
 #defcomp hash
 
 if [[ "$words[2]" = -*d* ]]; then
-  if [[ -string 1 '=' ]]; then
+  if compset -P 1 '*\='; then
     _path_files -g '*(-/)'
   else
     compgen -n -q -S '='
   fi
-elif [[ -string 1 '=' ]]; then
+elif compset -P 1 '*\='; then
   _files -/g '*(*)'
 else
   compgen -m -q -S '='
diff --git a/Completion/Builtins/_kill b/Completion/Builtins/_kill
index 36a23ccb2..0b0f5c188 100644
--- a/Completion/Builtins/_kill
+++ b/Completion/Builtins/_kill
@@ -2,7 +2,7 @@
 
 local list
 
-if [[ -iprefix '-' ]]; then
+if compset -P 1 -; then
   compgen -k "($signals[1,-3])"
 else
   local ret=1
diff --git a/Completion/Builtins/_sched b/Completion/Builtins/_sched
index 62cdbb070..5fba71eac 100644
--- a/Completion/Builtins/_sched
+++ b/Completion/Builtins/_sched
@@ -1,3 +1,3 @@
 #defcomp sched
 
-[[ -position 3 -1 ]] && _normal
+compset -n 3 && _normal
diff --git a/Completion/Builtins/_setopt b/Completion/Builtins/_setopt
index b458cb2b0..e5a6c054f 100644
--- a/Completion/Builtins/_setopt
+++ b/Completion/Builtins/_setopt
@@ -1,11 +1,10 @@
 #defcomp setopt
 
-local nm=$compstate[nmatches] ret=1
-
-compgen -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' \
-         -s '$({ unsetopt kshoptionprint; unsetopt } 2>/dev/null)' && ret=0
-
-[[ compstate[nmatches] -eq nm ]] &&
-    compgen -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o && ret=0
-
-return ret
+# If you first want to complete only unset options, un-comment the lines
+# setting the _unset_options  array and then use:
+#
+#   _unset_options || _options
+#
+# here.
+
+_options
diff --git a/Completion/Builtins/_source b/Completion/Builtins/_source
index 1bbbf15a4..efac4f32b 100644
--- a/Completion/Builtins/_source
+++ b/Completion/Builtins/_source
@@ -1,6 +1,7 @@
 #defcomp source
 
-if [[ -position 3 -1 ]]; then
+if [[ CURRENT -ge 3 ]]; then
+  compset -n 2
   _normal
 else
   _files
diff --git a/Completion/Builtins/_unsetopt b/Completion/Builtins/_unsetopt
index 1194e28a7..cdc2ab9f3 100644
--- a/Completion/Builtins/_unsetopt
+++ b/Completion/Builtins/_unsetopt
@@ -1,11 +1,10 @@
 #defcomp unsetopt
 
-local nm=$compstate[nmatches] ret=1
-
-compgen -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' \
-         -s '$({ unsetopt kshoptionprint; setopt } 2>/dev/null)' && ret=0
-
-[[ compstate[nmatches] -eq nm ]] &&
-    compgen -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o && ret=0
-
-return ret
+# If you first want to complete only unset options, uncomment the lines
+# setting the _set_options  array and then use:
+#
+#   _set_options || _options
+#
+# here.
+
+_options
diff --git a/Completion/Builtins/_zmodload b/Completion/Builtins/_zmodload
index 4259adf06..9247ba0ce 100644
--- a/Completion/Builtins/_zmodload
+++ b/Completion/Builtins/_zmodload
@@ -2,7 +2,7 @@
 
 local fl="$words[2]"
 
-if [[ "$fl" = -*(a*u|u*a)* || "$fl" = -*a* && -position 4 -1 ]]; then
+if [[ "$fl" = -*(a*u|u*a)* || "$fl" = -*a* && CURRENT -ge 4 ]]; then
   compgen -B
 elif [[ "$fl" = -*u* ]]; then
   compgen -s '$(zmodload)'
diff --git a/Completion/Commands/_correct_filename b/Completion/Commands/_correct_filename
index 27295738c..72bac5f1b 100644
--- a/Completion/Commands/_correct_filename
+++ b/Completion/Commands/_correct_filename
@@ -45,7 +45,7 @@ fi
 if [[ -z $testcmd && -e "$file" ]] ||
   { [[ -n $testcmd ]] && whence "$file" >&/dev/null }; then
   if [[ -n $WIDGET ]]; then
-    compadd -QUf -i "$IPREFIX" "${file/#$etilde/$tilde}"
+    compadd -QUf -i "$IPREFIX" -I "$ISUFFIX" "${file/#$etilde/$tilde}"
     [[ -n "$compstate[insert]" ]] && compstate[insert]=menu
   else
     print "$file"
@@ -65,7 +65,7 @@ done
 (( $#trylist )) || return 1
 
 if [[ -n $WIDGET ]]; then
-  compadd -QUf -i "$IPREFIX" "${trylist[@]/#$etilde/$tilde}"
+  compadd -QUf -i "$IPREFIX" -I "$ISUFFIX" "${trylist[@]/#$etilde/$tilde}"
   [[ -n "$compstate[insert]" ]] && compstate[insert]=menu
 else
   print "$IPREFIX${^trylist[@]}"
diff --git a/Completion/Commands/_most_recent_file b/Completion/Commands/_most_recent_file
index df35ecba7..5bd737fd2 100644
--- a/Completion/Commands/_most_recent_file
+++ b/Completion/Commands/_most_recent_file
@@ -19,4 +19,4 @@ if [[ $PREFIX = \~*/* ]]; then
 else
   file=($~PREFIX*$~SUFFIX(om[$NUMERIC]N))
 fi
-(( $#file )) && compadd -U -f -Q $file
+(( $#file )) && compadd -U -i "$IPREFIX" -I "$ISUFFIX" -f -Q $file
diff --git a/Completion/Core/.distfiles b/Completion/Core/.distfiles
index ddf2a707e..6babe9701 100644
--- a/Completion/Core/.distfiles
+++ b/Completion/Core/.distfiles
@@ -1,5 +1,6 @@
 DISTFILES_SRC='
     .distfiles
-    _compalso _files _main_complete _multi_parts _normal _path_files
-    _sep_parts compdump compinit
+    _approximate _compalso _complete _correct _expand _files _list
+    _main_complete _match _multi_parts _normal _options _parameters
+    _path_files _sep_parts _set_options _unset_options compdump compinit
 '
diff --git a/Completion/Core/_approximate b/Completion/Core/_approximate
new file mode 100644
index 000000000..1b40f7cbf
--- /dev/null
+++ b/Completion/Core/_approximate
@@ -0,0 +1,197 @@
+#autoload
+
+# This code will try to correct the string on the line based on the
+# strings generated for the context if `compconfig[correct]' is set.
+# These corrected strings will be shown in a list and one can
+# cycle through them as in a menucompletion or get the corrected prefix.
+#
+# Supported configuration keys:
+#
+#  approximate_accept
+#    This 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 this
+#    key will be used. E.g. with `compconf approximate_accept=2n' two
+#    errors will be accepted, but if the user gives another number
+#    with the numeric argument, this will be prefered. Also, with
+#    `compconf approximate_accept=0n', normally no correction will be
+#    tried, but if a numeric argument is given, automatic correction
+#    will be used. On the other hand, if the string contains an `!'
+#    and a `n' or `N', correction is not attempted if a numeric
+#    argument is given. 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.
+#
+#  approximate_original
+#    This value 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 appear 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 immediately). Also, if the value 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.
+#
+#  approximate_prompt
+#    This can 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.
+#
+#  approximate_insert
+#    If this is set to a string starting with `unambig', the code
+#    will try to insert a usable unambiguous string in the command
+#    line instead of always cycling through the corrected strings.
+#    If such a unambiguous string could be found, the original
+#    string is not used, independent of the setting of
+#    `approximate_original'. If no sensible string could be found,
+#    one can cycle through the corrected strings as usual.
+#
+# If any of these keys is not set, but the the same key with the
+# prefix `correct' instead of `approximate' is set, that value will
+# be used.
+
+local _comp_correct _correct_prompt comax
+local cfgacc cfgorig cfgps cfgins
+
+# Only if all global matchers hav been tried.
+
+[[ compstate[matcher] -ne compstate[total_matchers] ]] && return 1
+
+# We don't try correction if the string is too short.
+
+[[ "${#:-$PREFIX$SUFFIX}" -le 1 ]] && return 1
+
+# Get the configuration values, using either the prefix `correct' or
+# `approximate'.
+
+if [[ "$compstate[pattern_match]" = (|\**) ]]; then
+  cfgacc="${compconfig[approximate_accept]:-$compconfig[correct_accept]}"
+  cfgorig="${compconfig[approximate_original]:-$compconfig[correct_original]}"
+  cfgps="${compconfig[approximate_prompt]:-$compconfig[correct_prompt]}"
+  cfgins="${compconfig[approximate_insert]:-$compconfig[correct_insert]}"
+else
+  cfgacc="$compconfig[correct_accept]"
+  cfgorig="$compconfig[correct_original]"
+  cfgps="$compconfig[correct_prompt]"
+  cfgins="$compconfig[correct_insert]"
+fi
+
+# Get the number of errors to accept.
+
+if [[ "$cfgacc" = *[nN]* && NUMERIC -ne 1 ]]; then
+  # Stop if we also have a `!'.
+
+  [[ "$cfgacc" = *\!* ]] && return 1
+
+  # Prefer the numeric argument if that has a sensible value.
+
+  comax="$NUMERIC"
+else
+  comax="${cfgacc//[^0-9]}"
+fi
+
+# If the number of errors to accept is too small, give up.
+
+[[ "$comax" -lt 1 ]] && return 1
+
+# Otherwise temporarily define functions to use instead of
+# the builtins that add matches. This is used to be able
+# to stick the `(#a...)' into the right place (after an
+# 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 [[ -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 [[ -n "$_correct_prompt" ]]; then
+    builtin compgen "$@" -X "$_correct_prompt" -J _correct
+  else
+    builtin compgen "$@" -J _correct
+  fi
+}
+
+# Now initialise our counter. We also set `compstate[matcher]'
+# to `-1'. This allows completion functions to use the simple
+# `[[ compstate[matcher] -gt 1 ]] && return' to avoid being
+# called for multiple global match specs and still be called 
+# again when correction is done. Also, this makes it easy to
+# test if correction is attempted since `compstate[matcher]'
+# will never be set to a negative value by the completion code.
+
+_comp_correct=1
+compstate[matcher]=-1
+
+_correct_prompt="${cfgps//\%e/1}"
+
+# We also need to set `extendedglob' and make the completion
+# code behave as if globcomplete were set.
+
+setopt extendedglob
+
+[[ -z "$compstate[pattern_match]" ]] && compstate[pattern_match]='*'
+
+while [[ _comp_correct -le comax ]]; do
+  if _complete; then
+    if [[ "$cfgins" = unambig* &&
+          "${#compstate[unambiguous]}" -ge "${#:-$PREFIX$SUFFIX}" ]]; then
+      compstate[pattern_insert]=unambiguous
+    elif [[ compstate[nmatches] -gt 1 || "$cfgorig" = *always* ]]; then
+      if [[ "$cfgorig" = *last* ]]; then
+        builtin compadd -U -V _correct_original -nQ - "$PREFIX$SUFFIX"
+      elif [[ -n "$cfgorig" ]]; then
+	builtin compadd -U -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
+    compstate[matcher]="$compstate[total_matchers]"
+    unfunction compadd compgen
+
+    return 0
+  fi
+
+  [[ "${#:-$PREFIX$SUFFIX}" -le _comp_correct+1 ]] && break
+  (( _comp_correct++ ))
+
+  _correct_prompt="${cfgps//\%e/$_comp_correct}"
+done
+
+compstate[matcher]="$compstate[total_matchers]"
+unfunction compadd compgen
+
+return 1
diff --git a/Completion/Core/_complete b/Completion/Core/_complete
new file mode 100644
index 000000000..0f4d5ff4b
--- /dev/null
+++ b/Completion/Core/_complete
@@ -0,0 +1,52 @@
+#autoload
+
+# Generate all possible completions. Note that this is not intended as
+# a normal completion function, but as one possible value for the
+# compconfig[completer] parameter.
+
+local comp name
+
+# An entry for `-first-' is the replacement for `compctl -T'
+# Completion functions may set `_compskip' to any value to make the 
+# main loops stop calling other completion functions.
+
+comp="$_comps[-first-]"
+if [[ ! -z "$comp" ]]; then
+  "$comp"
+  if (( $+_compskip )); then
+    unset _compskip
+    (( compstate[nmatches] ))
+    return
+  fi
+fi
+
+# For arguments and command names we use the `_normal' function.
+
+if [[ "$compstate[context]" = command ]]; then
+  _normal
+else
+  # Let's see if we have a special completion definition for the other
+  # possible contexts.
+
+  comp=''
+
+  case $compstate[context] in
+  equal)           comp="$_comps[-equal-]";;
+  tilde)           comp="$_comps[-tilde-]";;
+  redirect)        comp="$_comps[-redirect-]";;
+  math)            comp="$_comps[-math-]";;
+  subscript)       comp="$_comps[-subscript-]";;
+  value)           comp="$_comps[-value-]";;
+  array_value)     comp="$_comps[-array-value-]";;
+  condition)       comp="$_comps[-condition-]";;
+  parameter)       comp="$_comps[-parameter-]";;
+  brace_parameter) comp="$_comps[-brace-parameter-]";;
+  esac
+
+  # If not, we use default completion, if any.
+
+  [[ -z "$comp" ]] && comp="$_comps[-default-]"
+  [[ -z "$comp" ]] || "$comp"
+fi
+
+(( compstate[nmatches] ))
diff --git a/Completion/Core/_correct b/Completion/Core/_correct
new file mode 100644
index 000000000..35ab01cf1
--- /dev/null
+++ b/Completion/Core/_correct
@@ -0,0 +1,19 @@
+#autoload
+
+# This is mainly a wrapper around the more general `_approximate.
+# By setting `compstate[pattern_match]' to something unequal to `*' and
+# then calling `_approximate, we get only corrections, not all strings
+# with the corrected prefix and something after it.
+#
+# Supported configuration keys are the same as for `_approximate', only
+# starting with `correct'.
+
+local ret=1 opm="$compstate[pattern_match]"
+
+compstate[pattern_match]='-'
+
+_approximate && ret=0
+
+compstate[pattern_match]="$opm"
+
+return ret
diff --git a/Completion/Core/_expand b/Completion/Core/_expand
new file mode 100644
index 000000000..9172b6cbf
--- /dev/null
+++ b/Completion/Core/_expand
@@ -0,0 +1,149 @@
+#autoload
+
+# This completer function is intended to be used as the first completer
+# function and allows one to say more explicitly when and how the word
+# from the line should be expanded than expand-or-complete.
+# This function will allow other completer functions to be called if
+# the expansions done produce no result or do not change the original
+# word from the line.
+#
+# Configuration keys:
+#
+#  expand_substitute
+#    If this is unset or set to the empty string, the code will first
+#    try to expand all substitutions in the string (such as $(...) and
+#    ${...}). If this is set to an non-empty string it should be 
+#    an expression usable inside a $[...] arithmetical expression.
+#    In this case, expansion of substitutions will be done if the
+#    expression evaluates to `1'. For example, with
+#
+#      compconf expand_substitute='NUMERIC != 1'
+#
+#    substitution will be performed only if given an explicit numeric
+#    argument other than `1', as by typing ESC 2 TAB.
+#
+#  expand_glob
+#    If this is unset or set to an empty string, globbing will be
+#    attempted on the word resulting from substitution or the
+#    original string. The values accepted for this key are the same
+#    as for expand_substitute.
+#
+#  expand_menu
+#    If this is unset or set to the empty string, the words resulting
+#    from expansion (if any) will simply be inserted in the ommand line,
+#    replacing the original string. However, if this key is set to an
+#    non-empty string, the user can cycle through the expansion as in
+#    a menucompletion. Unless the value contains the sub-string `only',
+#    the user will still be offered all expansions at once as one of
+#    the strings to insert in the command line. Also, if the value
+#    contains the sub-string `last', the string with all expansion will
+#    be offered first, whereas normally it is offered as the last string
+#    to insert. Finally, if the value contains the sub-string `sort',
+#    the expansions will be sorted alphabetically, normally they are
+#    kept in the order the expansion produced them in.
+#    
+#  expand_original
+#    If this is set to an non-empty string, the original string from the
+#    line will be included in the list of strings the user can cycle
+#    through as in a menucompletion. If the value contains the sub-string
+#    `last', the original string will appear as the last string, with
+#    other values it is inserted as the first one (so that the command
+#    line does not change immediatly).
+#
+#  expand_prompt
+#    This may be set to a string that should be displayed before the
+#    possible expansions. This is given to the -X option and thus may
+#    contain the control sequences `%n', `%B', etc. Also, the sequence
+#    `%o' in this string will be replaced by the original string.
+
+local exp word="$PREFIX$SUFFIX" group=-V
+
+# Do this only for the first global matcher.
+
+[[ "$compstate[matcher]" -le 1 ]] || return 1
+
+# In exp we will collect the expansion.
+
+exp=("$word")
+
+# First try substitution. That weird thing spanning multiple lines
+# changes quoted spaces, tabs, and newlines into spaces.
+
+[[ -z "$compconfig[expand_substitute]" ||
+   "${(e):-\$[$compconfig[expand_substitute]]}" -eq 1 ]] &&
+    exp=( "${(e)exp//\\[ 	
+]/ }" )
+
+# If the array is empty, store the original string again.
+
+[[ -z "$exp" ]] && exp=("$word")
+
+# Now try globbing.
+
+[[ -z "$compconfig[expand_glob]" ||
+   "${(e):-\$[$compconfig[expand_glob]]}" -eq 1 ]] &&
+    exp=( ${~exp}(N) )
+
+# If we don't have any expansions or only one and that is the same
+# as the original string, we let other completers run.
+
+[[ $#exp -eq 0 ||
+   ( $#exp -eq 1 && "$exp[1]" = "$word" ) ]] && return 1
+
+# We have expansions, should we menucomplete them?
+
+if [[ -z "$compconfig[expand_menu]" ]]; then
+
+  # No, so if the user only wants a list, we add the strings
+  # separately. Otherwise we add the whole array as one string,
+  # probably also adding the original string.
+
+  if [[ -z "$compstate[insert]" ]]; then
+    compadd -U -V _expand -Q - "$exp[@]"
+  else
+    [[ -n "$compconfig[expand_original]" && 
+       "$compconfig[expand_original]" != *last* ]] &&
+        compadd -UnQ -V _expand_original - "$word"
+
+    compadd -UQ -V _expand - "$exp"
+
+    [[ -n "$compconfig[expand_original]" && 
+       "$compconfig[expand_original]" = *last* ]] &&
+        compadd -UnQ -V _expand_original - "$word"
+
+    compstate[insert]=menu
+  fi
+else
+  # Sorting? We just use a different group type then.
+
+  [[ "$compconfig[expand_menu]" = *sort* ]] && group=-J
+
+  # Now add the expansion string, probably also adding the original
+  # and/or the string containing all expanded string.
+
+  [[ -n "$compconfig[expand_original]" && 
+     "$compconfig[expand_original]" != *last* ]] &&
+      compadd -UnQ -V _expand_original - "$word"
+
+  [[ "$compconfig[expand_menu]" = *last* &&
+     "$compconfig[expand_menu]" != *only* ]] &&
+      compadd -UnQ -V _expand_all - "$exp"
+
+  if [[ -z "$compconfig[expand_prompt]" ]]; then
+    compadd -UQ $group _expand - "$exp[@]"
+  else
+    compadd -UQ -X "${compconfig[expand_prompt]//\%o/$word}" \
+            $group _expand - "$exp[@]"
+  fi
+  [[ "$compconfig[expand_menu]" != *last* &&
+     "$compconfig[expand_menu]" != *only* ]] &&
+      compadd -UnQ -V _expand_all - "$exp"
+
+  [[ -n "$compconfig[expand_original]" && 
+     "$compconfig[expand_original]" = *last* ]] &&
+      compadd -UnQ -V _expand_original - "$word"
+
+  compstate[insert]=menu
+fi
+
+return 0
diff --git a/Completion/Core/_list b/Completion/Core/_list
new file mode 100644
index 000000000..099c6bc7b
--- /dev/null
+++ b/Completion/Core/_list
@@ -0,0 +1,61 @@
+#autoload
+
+# This completer function makes the other completer functions used
+# insert possible completions only after once the list has been
+# shown.
+#
+# Configuration keys:
+#
+#  list_condition
+#    If this key is unset or set to the empty string, this completer
+#    will delay the insertion of matches unconditionally. However,
+#    if this value is set, it should be set to an expression usable
+#    inside a $[...] arithmetical expression. In this case, delaying
+#    will be done if the expression evaluates to `1'.
+#    For example, with
+#
+#      compconf list_condition='NUMERIC != 1'
+#
+#    delaying will be done only if given an explicit numeric argument
+#    other than `1'.
+#
+#  list_word
+#    To find out if only listing should be done, the code normally
+#    compares the contents of the line with the contents the line
+#    had at the time of the last invocation. If this key is set to
+#    an non-empty string comparison is done using only the current
+#    word. So if it is set, attempting completion on a word equal
+#    to the one completion was called on the last time will not
+#    delay the generation of matches.
+
+local pre suf
+
+# Get the strings to compare.
+
+if [[ -z "$compconfig[list_word]" ]]; then
+  pre="$HISTNO$LBUFFER"
+  suf="$RBUFFER"
+else
+  pre="$PREFIX"
+  suf="$SUFFIX"
+fi
+
+# Should we only show a list now?
+
+if [[ ( -z "$compconfig[list_condition]" ||
+        "${(e):-\$[$compconfig[expand_glob]]}" -eq 1 ) &&
+      ( "$pre" != "$_list_prefix" || "$suf" != "$_list_suffix" ) ]]; then
+
+  # Yes. Tell the completion code about it and save the new values
+  # to compare the next time.
+
+  compstate[insert]=''
+  compstate[list]=list
+  compstate[force_list]=yes
+  _list_prefix="$pre"
+  _list_suffix="$suf"
+fi
+
+# We always return one, because we don't really do any work here.
+
+return 1
diff --git a/Completion/Core/_main_complete b/Completion/Core/_main_complete
index 3571b712c..62e60a1cc 100644
--- a/Completion/Core/_main_complete
+++ b/Completion/Core/_main_complete
@@ -3,229 +3,56 @@
 # 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
-# `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
-# `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.
+# Configuration keys used:
+#
+#  completer
+#    This should be set to the names of the functions to generate the
+#    matches separated by colons. E.g. with
+#
+#      compconf completer=_complete:_correct:_approximate
+#
+#    the code will first try normal completion. If that doesn't yield
+#    any matches, correction is tried and if that doesn't yield
+#    anything either, correcting completion is attempted.
+#
+# These completer functions are only used when this function is called
+# without arguments. If arguments are given, they should be names of
+# completer functions which will then be called.
+
+
+# If you want to complete only set or unset options for the unsetopt
+# and setopt builtin, un-comment these lines:
+#
+#   local _set_options _unset_options
+#
+#   _set_options=("${(@f)$({ unsetopt kshoptionprint; setopt } 2>/dev/null)}")
+#   _unset_options=("${(@f)$({ unsetopt kshoptionprint; unsetopt } 2>/dev/null)}")
+#
+# This is needed because completion function may set options locally
+# which makes the output of setopt and unsetopt reflect a different
+# state than the global one for which you are completing.
 
-local comp name _comp_correct _correct_prompt comax
+
+local comp
 
 setopt localoptions nullglob rcexpandparam
 unsetopt markdirs globsubst shwordsplit nounset ksharrays
 
 # Special completion contexts after `~' and `='.
 
-if [[ -iprefix '=' ]]; then
+if compset -P 1 '\='; then
   compstate[context]=equal
-elif [[ "$PREFIX$SUFFIX" != */* && -iprefix '~' ]]; then
+elif [[ "$PREFIX" != */* && "$PREFIX[1]" = '~' ]]; then
+  compset -p 1
   compstate[context]=tilde
 fi
 
-# This is not an endless loop.
-
-while true; do
-
-  # An entry for `-first-' is the replacement for `compctl -T'
-  # Completion functions may set `_compskip' to any value to make the 
-  # main loops stop calling other completion functions.
-
-  comp="$_comps[-first-]"
-  if [[ ! -z "$comp" ]]; then
-    "$comp"
-    if (( $+_compskip )); then
-      unset _compskip
-      return
-    fi
-  fi
-
-  # For arguments and command names we use the `_normal' function.
-
-  if [[ "$compstate[context]" = command ]]; then
-    _normal
-  else
-    # Let's see if we have a special completion definition for the other
-    # possible contexts.
-
-    comp=''
-
-    case $compstate[context] in
-    equal)           comp="$_comps[-equal-]";;
-    tilde)           comp="$_comps[-tilde-]";;
-    redirect)        comp="$_comps[-redirect-]";;
-    math)            comp="$_comps[-math-]";;
-    subscript)       comp="$_comps[-subscript-]";;
-    value)           comp="$_comps[-value-]";;
-    array_value)     comp="$_comps[-array-value-]";;
-    condition)       comp="$_comps[-condition-]";;
-    parameter)       comp="$_comps[-parameter-]";;
-    brace_parameter) comp="$_comps[-brace-parameter-]";;
-    esac
-
-    # If not, we use default completion, if any.
-
-    [[ -z "$comp" ]] && comp="$_comps[-default-]"
-    [[ -z "$comp" ]] || "$comp"
-  fi
-
-  # Use automatic correction?
-
-  if (( $+compconfig[correct] )); then
-
-    # Do we have matches?
-    if (( compstate[nmatches] )); then
-
-      # Yes, were they added using correction? (More than one match?)
+# Get the names of the completers to use in the positional parameters.
 
-      if [[ -n "$_comp_correct" &&
-            ( "$compconfig[correct_orig]" = *always* ||
-	      compstate[nmatches] -gt 1 ) ]]; then
+(( $# )) || set ${(s.:.)compconfig[completer]}
 
-        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
+# And now just call the completer functions defined.
 
-	# 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
-
-    # No matches, so let's see if we already tried correction.
-
-    if [[ -n "$_comp_correct" ]]; then
-
-      # 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 ||
-         "${#${:-$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 [[ "$compconfig[correct]" = *[nN]* && NUMERIC -ne 1 ]]; then
-        # Prefer the numeric argument if that has a sensible value.
-        comax="$NUMERIC"
-      else
-        comax="${compconfig[correct]//[^0-9]}"
-      fi
-      # If the number of errors to accept is too small, give up.
-
-      [[ "$comax" -lt 1 ]] && break
-
-      # Otherwise temporarily define functions to use instead of
-      # the builtins that add matches. This is used to be able
-      # to stick the `(#a...)' into the right place (after an
-      # 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 [[ -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 [[ -n "$_correct_prompt" ]]; then
-	  builtin compgen "$@" -X "$_correct_prompt" -J _correct
-	else
-	  builtin compgen "$@" -J _correct
-	fi
-      }
-      # Now initialise our counter. We also set `compstate[matcher]'
-      # to `-1'. This allows completion functions to use the simple
-      # `[[ compstate[matcher] -gt 1 ]] && return' to avoid being
-      # called for multiple global match specs and still be called 
-      # again when correction is done. Also, this makes it easy to
-      # test if correction is attempted since `compstate[matcher]'
-      # will never be set to a negative value by the completion code.
-
-      _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.
-
-      setopt extendedglob
-      compstate[pattern_match]=yes
-    else
-      # We are still trying global match specifications...
-      break
-    fi
-  else
-    # No automatic correction to try, just give up.
-    break
-  fi
+for comp; do
+  "$comp" && return
 done
-
-# If we added wrapper functions, remove them.
-
-[[ -n "$_comp_correct" ]] && unfunction compadd compgen
diff --git a/Completion/Core/_match b/Completion/Core/_match
new file mode 100644
index 000000000..3c639935c
--- /dev/null
+++ b/Completion/Core/_match
@@ -0,0 +1,53 @@
+#autoload
+
+# This is intended to be used as a completer function after the normal
+# completer as in: `compconf completer=_complete:_match'.
+# It temporarily switches on pattern matching, allowing you to try 
+# completion on patterns without having to setopt glob_complete.
+#
+# Note, however, that this is only really useful if you don't use the
+# expand-or-complete function because otherwise the pattern will
+# be expanded using globbing.
+#
+# Configuration key used:
+#
+#  match_original
+#    If this is set to a `only', pattern matching will only be tried
+#    with the string from the line. If it is set to any other non-empty
+#    string, the original pattern will be tried first and if that yields
+#    no completions, matching will be tried again with a `*' inserted
+#    at the cursor position. If this key is not set or set to an empty
+#    string, matching will only be attempted with the `*' inserted.
+
+local tmp opm="$compstate[pattern_match]" ret=0
+
+# Do nothing if we don't have a pattern or there are still global
+# match specifications to try.
+
+tmp="${${:-$PREFIX$SUFFIX}#[~=]}"
+[[ "$tmp:q" = "$tmp" ||
+   compstate[matcher] -ne compstate[total_matchers] ]] && return 1
+
+# Try completion without inserting a `*'?
+
+if [[ -n "$compconfig[match_original]" ]]; then
+  compstate[matcher]=-1
+  compstate[pattern_match]='-'
+  _complete && ret=1
+  compstate[pattern_match]="$opm"
+  compstate[matcher]="$compstate[total_matchers]"
+
+  (( ret )) && return 0
+fi
+
+# No completion with inserting `*'?
+
+[[ "$compconfig[match_original]" = only ]] && return 1
+
+compstate[matcher]=-1
+compstate[pattern_match]='*'
+_complete && ret=1
+compstate[pattern_match]="$opm"
+compstate[matcher]="$compstate[total_matchers]"
+
+return 1-ret
diff --git a/Completion/Core/_multi_parts b/Completion/Core/_multi_parts
index 0c677aab7..ab9438494 100644
--- a/Completion/Core/_multi_parts
+++ b/Completion/Core/_multi_parts
@@ -45,7 +45,11 @@ fi
 # the original string in `orig'.
 
 if [[ $#compstate[pattern_match] -ne 0 ]]; then
-  patstr="${PREFIX}*${SUFFIX}*"
+  if [[ "${compstate[pattern_match]-*}" = \** ]]; then
+    str="${PREFIX}*${SUFFIX}*"
+  else
+    str="${PREFIX}${SUFFIX}"
+  fi
 else
   patstr="${PREFIX:q}*${SUFFIX:q}*"
 fi
@@ -145,7 +149,8 @@ if (( $#tmp1 )); then
   # found. Strings that have a separator will be added with a suffix.
 
   if [[ -z "$orig" && "$PREFIX$SUFFIX" != "$pref$orig" ]]; then
-    compadd -QU  "$group[@]" "$expl[@]" -i "$IPREFIX" -S '' - "${pref}${orig}"
+    compadd -QU  "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" -S '' - \
+            "${pref}${orig}"
   elif [[ -n "$menu" ]]; then
     if [[ "$orig" = *${sep}* ]]; then
       orig="${sep}${orig#*${sep}}"
@@ -154,20 +159,21 @@ if (( $#tmp1 )); then
     fi
     for i in "$matches[@]" ; do
       if [[ "$i" = *${sep}* ]]; then
-        compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" \
+        compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
 	        -p "$pref" -s "$orig" - "${i%%${sep}*}${sep}"
       else
-        compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" \
+        compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
 	        -p "$pref" -s "$orig" - "${i%%${sep}*}"
       fi
     done
   else
     for i in "$matches[@]" ; do
       if [[ "$i" = *${sep}* ]]; then
-        compadd -U -i "$IPREFIX" -p "$pref" -s "${i#*${sep}}" \
+        compadd -U -i "$IPREFIX" -I "$ISUFFIX" -p "$pref" -s "${i#*${sep}}" \
 	        "$group[@]" "$expl[@]" -M "r:|${sep:q}=*" - "${i%%${sep}*}${sep}"
       else
-        compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -p "$pref" - "$i"
+        compadd -U "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
+                -p "$pref" - "$i"
       fi
     done
   fi
@@ -201,8 +207,8 @@ elif [[ "$patstr" = *${sep}* ]]; then
       # the completion code together with our prefix and the rest of
       # the string from the line as the suffix.
 
-      compadd -U "$group[@]" "$expl[@]" -S '' -i "$IPREFIX" -p "$pref" \
-              -s "${sep}${orig#*${sep}}" - "${(@)matches%%${sep}*}"
+      compadd -U "$group[@]" "$expl[@]" -S '' -i "$IPREFIX" -I "$ISUFFIX" \
+              -p "$pref" -s "${sep}${orig#*${sep}}" - "${(@)matches%%${sep}*}"
       return 0
     fi
 
@@ -218,7 +224,8 @@ elif [[ "$patstr" = *${sep}* ]]; then
   # Finally, add the unambiguous prefix and the rest of the string
   # from the line.
 
-  compadd -U "$group[@]" "$expl[@]" -S '' -i "$IPREFIX" -p "$pref" - "$orig"
+  compadd -U "$group[@]" "$expl[@]" -S '' -i "$IPREFIX" -I "$ISUFFIX" \
+          -p "$pref" - "$orig"
 fi
 
 # This sets the return value to indicate that we added matches (or not).
diff --git a/Completion/Core/_options b/Completion/Core/_options
new file mode 100644
index 000000000..0a852e6ce
--- /dev/null
+++ b/Completion/Core/_options
@@ -0,0 +1,5 @@
+#autoload
+
+# This should be used to complete all option names.
+
+compgen "$@" -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o
diff --git a/Completion/Core/_parameters b/Completion/Core/_parameters
new file mode 100644
index 000000000..0e8c548f7
--- /dev/null
+++ b/Completion/Core/_parameters
@@ -0,0 +1,8 @@
+#autoload
+
+# This should be used to complete parameter names if you need some of the
+# extra options of compadd. It first tries to complete only non-local
+# parameters. All arguments are given to compadd.
+
+compadd "$@" - "${(@)${(@)${(@)${(@f)$(typeset)}:#*local *\=*}%%\=*}##* }" ||
+    compadd "$@" - "${(@)${(@)${(@f)$(typeset)}%%\=*}##* }"
diff --git a/Completion/Core/_path_files b/Completion/Core/_path_files
index 4c61ac7ef..54b04a368 100644
--- a/Completion/Core/_path_files
+++ b/Completion/Core/_path_files
@@ -12,6 +12,14 @@
 # like files with one of the suffixes in the `fignore' array in normal
 # completion.
 #
+# This function supports one configuration key:
+#
+#  path_expand
+#    If this is set to a non-empty string, the partially typed path
+#    from the line will be expanded as far as possible even if trailing
+#    pathname components can not be completed.
+#
+#
 # This function uses the helper functions `_match_test' and `_match_pattern'.
 
 # First see if we should generate matches for the global matcher in use.
@@ -20,14 +28,17 @@ _match_test _path_files || return 1
 
 # Yes, so...
 
-local nm prepaths str linepath realpath donepath patstr prepath testpath rest
+local nm 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] menu remsfx patlast
-local origflags mflags
+local origflags mflags tmp3 tmp4 exppaths
+
+typeset -U prepaths
 
 setopt localoptions nullglob rcexpandparam extendedglob
 unsetopt markdirs globsubst shwordsplit nounset
 
+exppaths=()
 prepaths=('')
 ignore=()
 group=()
@@ -102,7 +113,11 @@ fi
 # the prefix and the suffix. Then we see if we will do menucompletion.
 
 if [[ $#compstate[pattern_match] -ne 0 ]]; then
-  str="${PREFIX}*${SUFFIX}"
+  if [[ "${compstate[pattern_match]-*}" = \** ]]; then
+    str="${PREFIX}*${SUFFIX}"
+  else
+    str="${PREFIX}${SUFFIX}"
+  fi
 else
   str="${PREFIX:q}*${SUFFIX:q}"
   [[ "$str" = \\\~* ]] && str="$str[2,-1]"
@@ -200,7 +215,7 @@ origflags="$matchflags"
 # have special meaning for globbing, we remove them. But before that, we
 # add the pattern for matching any characters before a slash.
 
-patstr="$patstr:gs-/-*/-:gs/*.*./../:gs-/*.-/.-:gs/**/*/:gs-.*/-./-"
+patstr="$patstr: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
@@ -214,16 +229,16 @@ patstr="$patstr:gs-/-*/-:gs/*.*./../:gs-/*.-/.-:gs/**/*/:gs-.*/-./-"
 
 if [[ "$patstr" = */* ]]; then
   if [[ -n "$_comp_correct" && "${#orig##*/}" -le _comp_correct ]]; then
-    patlast="*/${origflags}${${patstr##*/}//\*/[^/]#}"
+    patlast="*/${origflags}${${${patstr##*/}//\*/[^/]#}:gs.\[^/]#.\\\*.}"
   else
-    patlast="*/${matchflags}${${patstr##*/}//\*/[^/]#}"
+    patlast="*/${matchflags}${${${patstr##*/}//\*/[^/]#}:gs.\[^/]#.\\\*.}"
   fi
   patstr="${patstr%/*}/"
 else
   if [[ -n "$_comp_correct" && "$#orig" -le _comp_correct ]]; then
-    patlast="${origflags}${patstr//\*/[^/]#}"
+    patlast="${origflags}${${patstr//\*/[^/]#}:gs.\[^/]#.\\\*.}"
   else
-    patlast="${matchflags}${patstr//\*/[^/]#}"
+    patlast="${matchflags}${${patstr//\*/[^/]#}:gs.\[^/]#.\\\*.}"
   fi
   patstr=""
 fi
@@ -234,7 +249,7 @@ fi
 # to `donepath'.
 
 while [[ "$orig" = */* ]] do
-  tmp1=( $realpath$donepath${orig%%/*}/${~matchflags}${~patstr#*/}$^pats )
+  tmp1=( $realpath$donepath${orig%%/*}/${~matchflags}${~patstr#*/}${^~pats} )
   tmp1=("${(@M)tmp1:#$~patlast}")
   [[ $#tmp1 -gt 0 && -e "$realpath$donepath${orig%%/*}" ]] || break
   donepath="$donepath${orig%%/*}/"
@@ -311,6 +326,24 @@ for prepath in "$prepaths[@]"; do
       # next `-W' path.
 
       if [[ $#collect -eq 0 ]]; then
+        # Before giving, we first try to get the longest expandable path-
+	# prefix, though. The result is stored in `exppaths'
+
+        tmp2=()
+	tmp3="$rest"
+	tmp4="${ostr##*/}"
+	ostr="${ostr%/*}"
+	while [[ "$tmp3" = */* ]]; do
+	  tmp2=( ${^tmp1}/${~mflags}${~tmp3} )
+	  if [[ $#tmp2 -eq 1 ]]; then
+	    exppaths=( "$exppaths[@]" "${tmp2[1]}${tmp4}" )
+	    exppaths=( "${(@)exppaths#${prepath}${realpath}}" )
+	    break;
+          fi
+	  tmp3="${tmp3%/*}"
+	  tmp4="${ostr##*/}/${tmp4}"
+	  ostr="${ostr%/*}"
+        done
         continue 2
       elif [[ $#collect -ne 1 ]]; then
         # If we have more than one possible match, this means that the
@@ -338,13 +371,14 @@ for prepath in "$prepaths[@]"; do
 
 	if [[ -n "$menu" ]]; then
           compadd -QU "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \
-                  -i "$IPREFIX" -p "$linepath${testpath:q}" \
+                  -i "$IPREFIX" -I "$ISUFFIX" -p "$linepath${testpath:q}" \
 		  -s "/${ostr#*/}" \
 		  -W "$tmp1" -f "$ignore[@]" - "${(@)${(@)collect%%/*}:q}"
 	else
           for i in $collect; do
             compadd -QU "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \
-	            -i "$IPREFIX" -p "$linepath${testpath:q}" -s "/${${i#*/}:q}" \
+	            -i "$IPREFIX" -I "$ISUFFIX" \
+                    -p "$linepath${testpath:q}" -s "/${${i#*/}:q}" \
 		    -M 'r:|/=*' -W "$tmp1" -f "$ignore[@]" - "${${i%%/*}:q}"
           done
 	fi
@@ -371,7 +405,8 @@ for prepath in "$prepaths[@]"; do
 
       if [[ $#tmp1 -ne $#tmp2 ]]; then
         compadd -QU "$addpfx[@]" -S '' "$group[@]" "$expl[@]" \
-                -i "$IPREFIX" -p "$linepath${testpath:q}" -s "/${ostr#*/}" \
+                -i "$IPREFIX" -I "$ISUFFIX" \
+                -p "$linepath${testpath:q}" -s "/${ostr#*/}" \
 		- "${${tmp1#${prepath}${realpath}${testpath}}:q}"
         continue 2
       fi
@@ -399,8 +434,11 @@ for prepath in "$prepaths[@]"; do
   suffixes=( $str$^pats )
   suffixes=( "${(@)suffixes:gs.**.*.}" )
   tmp2=( ${~tmp1}${~matchflags}${~suffixes} )
-  tmp2=("${(@M)tmp2:#$~patlast}")
-
+  if [[ "$tmp1" = */* && "$patlast" != \*/* ]]; then
+    tmp2=("${(@M)tmp2:#*${~patlast}}")
+  else
+    tmp2=("${(@M)tmp2:#$~patlast}")
+  fi
   if [[ $#tmp2 -eq 0 ]]; then
     # No match, insert the expanded path and add the original tail.
 
@@ -409,17 +447,29 @@ for prepath in "$prepaths[@]"; do
     [[ -n "$ostr" && -n "$linepath$testpath" ]] && ostr="/$ostr"
 
     # But only if something changed.
-    [[ "$linepath$testpath$ostr" = "$PREFIX$SUFFIX" ]] && return 1
+    [[ "${PREFIX}${SUFFIX}" = $linepath$testpath$ostr(|/) ]] && return 1
 
     compadd -QU -S '' "$group[@]" "$expl[@]" \
-            -i "$IPREFIX" -f - "$linepath${testpath:q}$ostr"
+            -i "$IPREFIX" -I "$ISUFFIX" -f - "$linepath${testpath:q}$ostr"
   else
     compadd -QU "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" "$group[@]" "$expl[@]" \
-            -i "$IPREFIX" -p "$linepath${testpath:q}" -f "$ignore[@]" \
+            -i "$IPREFIX" -I "$ISUFFIX" \
+            -p "$linepath${testpath:q}" -f "$ignore[@]" \
 	    -W "$prepath$realpath$testpath" - "${(@)${(@)tmp2#$tmp1}:q}"
   fi
 done
 
+# If no matches were found but we have expanded paths which are different
+# from the original string, use them.
+
+exppaths=( "${(@)exppaths:#$orig}" )
+
+if [[ -n "$compconfig[path_expand]" &&
+      nm -eq compstate[nmatches] && $#exppaths -ne 0 ]]; then
+  compadd -UQ -S '' "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" - \
+          "${linepath}${(@)^exppaths}"
+fi
+
 # This sets the return value to indicate that we added matches (or not).
 
 [[ nm -ne compstate[nmatches] ]]
diff --git a/Completion/Core/_sep_parts b/Completion/Core/_sep_parts
index c1cda2b9a..fc927163b 100644
--- a/Completion/Core/_sep_parts
+++ b/Completion/Core/_sep_parts
@@ -101,6 +101,8 @@ if [[ $# -le 1 || "$str" != *${2}* ]]; then
   _match_pattern _sep_parts test matchflags
   [[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)"
 
+  [[ "${compstate[pattern_match]-*}" != \** ]] && test="$test:gs/*//"
+
   test="${matchflags}${test}"
   testarr=( "${(@M)${(@P)arr}:#${~test}*}" )
   testarr=( "${(@)testarr:#}" )
@@ -131,6 +133,9 @@ while [[ $# -gt 0 && "$str" == *${1}* ]]; do
   matchflags=""
   _match_pattern _sep_parts test matchflags
   [[ -n "$_comp_correct" ]] && matchflags="$matchflags(#a$_comp_correct)"
+
+  [[ "${compstate[pattern_match]-*}" != \** ]] && test="$test:gs/*//"
+
   test="${matchflags}${test}"
 
   # We incrementally add suffixes by appending to them the seperators
@@ -163,7 +168,7 @@ done
 # 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[@]"
+          -i "$IPREFIX" -I "$ISUFFIX" -p "$prefix" -s "$i" - "$testarr[@]"
 done
 
 # This sets the return value to indicate that we added matches (or not).
diff --git a/Completion/Core/_set_options b/Completion/Core/_set_options
new file mode 100644
index 000000000..5f634a2cd
--- /dev/null
+++ b/Completion/Core/_set_options
@@ -0,0 +1,7 @@
+#autoload
+
+# Complete all set options. This relies on `_main_complete' to store the
+# names of the options that were set when it was called in the array
+# `_set_options'.
+
+compadd "$@" -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' - $=_set_options
diff --git a/Completion/Core/_unset_options b/Completion/Core/_unset_options
new file mode 100644
index 000000000..c5150c2e5
--- /dev/null
+++ b/Completion/Core/_unset_options
@@ -0,0 +1,7 @@
+#autoload
+
+# Complete all unset options. This relies on `_main_complete' to store the
+# names of the options that were set when it was called in the array
+# `_set_options'.
+
+compadd "$@" -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' - $=_unset_options
diff --git a/Completion/Core/compdump b/Completion/Core/compdump
index 5fdee8c7a..6a46f3265 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=${compconfig[dump_file]-${0:h}/compinit.dump}
+_d_file=${compconfig[dumpfile]-${0:h}/compinit.dump}
 
 typeset -U _d_files
 _d_files=( ${^~fpath}/_*~*~(N:t) )
diff --git a/Completion/Core/compinit b/Completion/Core/compinit
index 466c80d36..19a0fd1ad 100644
--- a/Completion/Core/compinit
+++ b/Completion/Core/compinit
@@ -71,11 +71,12 @@ typeset -A compconfig
 
 # Standard initialisation for `compconfig'.
 
-(( $# )) && compconfig[dump_file]="$1"
-[[ -z "$compconfig[dump_file]" ]] && compconfig[dump_file]="$0.dump"
+(( $# )) && compconfig[dumpfile]="$1"
+[[ -z "$compconfig[dumpfile]" ]] && compconfig[dumpfile]="$0.dump"
 
+compconfig[correct_accept]=2n
 compconfig[correct_prompt]='correct to:'
-
+compconfig[completer]=_complete
 
 # This function is used to register or delete completion functions. For
 # registering completion functions, it is invoked with the name of the
@@ -246,10 +247,10 @@ _i_done=''
 
 # If we have a dump file, load it.
 
-if [[ -f "$compconfig[dump_file]" ]]; then
-  read -rA _i_line < "$compconfig[dump_file]"
+if [[ -f "$compconfig[dumpfile]" ]]; then
+  read -rA _i_line < "$compconfig[dumpfile]"
   if [[ _i_autodump -eq 1 && $_i_line[2] -eq $#_i_files ]]; then
-    builtin . "$compconfig[dump_file]"
+    builtin . "$compconfig[dumpfile]"
     _i_done=yes
   fi
   unset _i_line
diff --git a/Completion/README b/Completion/README
index 931d14355..bf677066a 100644
--- a/Completion/README
+++ b/Completion/README
@@ -14,7 +14,7 @@ The name of the file to use may be given as an extra argument.
 This will rebind any keys which do completion to use the new system.
 For more detailed instructions, including how to add new completions, see
 the top of Core/compinit. For information about how to configure the code,
-see the comment at the top of Core/_main_complete.
+see the section below.
 
 The subdirectories contain:
 
@@ -30,32 +30,61 @@ Core:
     This dumps the completions status for faster initialisation.  The
     easiest way of doing this is to use the -d option to compinit rather
     than calling compdump directly.
-  _sep_parts
-    Utility used for completing words with multiple separate parts, such as
-    `<user>@<host>'
-  _multi_parts
-    Utility for completion parts of words given a separator character and 
-    a list of words.
+  _approximate
+    A completer function that does correcting completion.
   _compalso
     Utility for calling a function to add additional completions to an
     already existing set.
+  _complete
+    The main completer function that generates the completions by calling
+    the context and command specific functions.
+  _correct
+    A completer function that attempts correction on the word from the
+    line. Unlike _approximate this does only correction, not completion.
+  _expand
+    A completer function for expanding the word on the line.
   _files
     A frontend to _path_files which will default to any old file if the
     specified file was not found.
+  _list
+    A completer function that allows showing only a list on the first
+    TAB and insert completions on the second one.
+  _match
+    A completer function that temporarily swicthes on pattern matching
+    when comparing the string from the line with possible completions.
   _main_complete
     The main entry point called by the key bindings which compinit sets
-    up (the main `completion widget' in zsh jargon).
+    up (the main `completion widget' in zsh jargon). This mainly calls
+    completer functions like _complete, either those given as arguments
+    or (if it is called without arguments) those from the completer
+    configuration key (see below).
+  _multi_parts
+    Utility for completion parts of words given a separator character and 
+    a list of words.
   _normal
     The function called by _main_complete to handle the most common
     cases, such as completing a command name or its arguments.  This
     function dispatches to the various other functions for individual
     commands.  (Actually, the system is fairly context-sensitive, so
     it is wider than just command+argument.)
+  _options
+    Utility to complete option names, allowing the optional `no' prefix
+    and correctly handling upper case letters and underscores.
+  _parameters
+    This can be used to complete parameter names if you need some of the
+    options of compadd not supported by compgen.
   _path_files
     The function usually called to complete filenames and directories.  It
     replaces the standard -f, -g and -/ options for the basic completion
     commands:  it can do various extra tricks, such as expanding a whole
     path at once, e.g. F/C/C/_p<TAB> -> Functions/Completion/Core/_path_files
+  _sep_parts
+    Utility used for completing words with multiple separate parts, such as
+    `<user>@<host>'
+  _set_options
+  _unset_options
+    These can be used to complete only set or unset options. For this to
+    work, you'll have to un-comment a few lines in _main_complete.
 Base:
   You will almost certainly want these files, too, which handle standard
   tasks like completing files.  However, you may want to edit them for
@@ -127,3 +156,201 @@ Commands:
   _most_recent_file, bound to \C-xm
     Insert the name of the most recent file matching the pattern
     so far on the command line.
+
+
+Configuration
+
+You can configure several aspects of the completion functions and the
+overall completion behavior using the compconf shell function defined
+in compinit. This function gets any number of arguments of the form
+`key=value', where `key' is one of the keys described below and `value'
+is any string. Most configuration keys are defined by the completer
+functions.
+
+The completion widget function _main_complete currently understands
+one configuration key:
+
+  completer
+    This should be set to the names of the functions to generate the
+    matches separated by colons. E.g. with
+
+      compconf completer=_complete:_correct:_approximate
+
+    the code will first try normal completion. If that doesn't yield
+    any matches, correction is tried and if that doesn't yield
+    anything either, correcting completion is attempted.
+
+The keys understood by the _approximate completer function are:
+
+  approximate_accept
+    This 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 this
+    key will be used. E.g. with `compconf approximate_accept=2n' two
+    errors will be accepted, but if the user gives another number
+    with the numeric argument, this will be prefered. Also, with
+    `compconf approximate_accept=0n', normally no correction will be
+    tried, but if a numeric argument is given, automatic correction
+    will be used. On the other hand, if the string contains an `!'
+    and a `n' or `N', correction is not attempted if a numeric
+    argument is given. 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.
+
+  approximate_original
+    This value 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 appear 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 immediately). Also, if the value 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.
+
+  approximate_prompt
+    This can 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.
+
+  approximate_insert
+    If this is set to a string starting with `unambig', the code
+    will try to insert a usable unambiguous string in the command
+    line instead of always cycling through the corrected strings.
+    If such a unambiguous string could be found, the original
+    string is not used, independent of the setting of
+    `approximate_original'. If no sensible string could be found,
+    one can cycle through the corrected strings as usual.
+
+If any of these keys is not set, but the the same key with the
+prefix `correct' instead of `approximate' is set, that value will
+be used. The forms beginning with `correct' are also used by the
+_correct completer function, and this function uses only them, not
+the ones starting with `approximate'. This allows one to give
+different value to be used with correction and correcting
+completion. For example, with:
+
+  compconf completer=_complete:_correct:_approximate
+  compconf correct_accept='2n!' approximate_accept=3n
+
+correction will accept up to two errors. If a numeric argument is
+given, correction will not be used, but correcting completion will
+and it will accept as many errors as given by the numeric argument.
+Without a numeric argument first correction and then correcting
+completion will be tried, with the first one accepting two errors 
+and the second one accepting three errors.
+
+The _match completer function, which allows to give patterns on the
+command line and complete all strings matching it from the set of
+possible completions, understands the following key:
+
+  match_original
+    If this is set to a `only', pattern matching will only be tried
+    with the string from the line. If it is set to any other non-empty
+    string, the original pattern will be tried first and if that yields
+    no completions, matching will be tried again with a `*' inserted
+    at the cursor position. If this key is not set or set to an empty
+    string, matching will only be attempted with the `*' inserted.
+
+The _expand completer allows one to do expansion on the word from the
+line. Note that you may either want to use the configuration keys or
+not use this completer at all if you also use the _match completer
+because the _expand completer will otherwise expand patterns before
+they are seen by the _match completer.
+Configuration keys supported are:
+
+  expand_substitute
+    If this is unset or set to the empty string, the code will first
+    try to expand all substitutions in the string (such as $(...) and
+    ${...}). If this is set to an non-empty string it should be 
+    an expression usable inside a $[...] arithmetical expression.
+    In this case, expansion of substitutions will be done if the
+    expression evaluates to `1'. For example, with
+
+      compconf expand_substitute='NUMERIC != 1'
+
+    substitution will be performed only if given an explicit numeric
+    argument other than `1', as by typing ESC 2 TAB.
+
+  expand_glob
+    If this is unset or set to an empty string, globbing will be
+    attempted on the word resulting from substitution or the
+    original string. The values accepted for this key are the same
+    as for expand_substitute.
+
+  expand_menu
+    If this is unset or set to the empty string, the words resulting
+    from expansion (if any) will simply be inserted in the ommand line,
+    replacing the original string. However, if this key is set to an
+    non-empty string, the user can cycle through the expansion as in
+    a menucompletion. Unless the value contains the sub-string `only',
+    the user will still be offered all expansions at once as one of
+    the strings to insert in the command line. Also, if the value
+    contains the sub-string `last', the string with all expansion will
+    be offered first, whereas normally it is offered as the last string
+    to insert. Finally, if the value contains the sub-string `sort',
+    the expansions will be sorted alphabetically, normally they are
+    kept in the order the expansion produced them in.
+    
+  expand_original
+    If this is set to an non-empty string, the original string from the
+    line will be included in the list of strings the user can cycle
+    through as in a menucompletion. If the value contains the sub-string
+    `last', the original string will appear as the last string, with
+    other values it is inserted as the first one (so that the command
+    line does not change immediatly).
+
+  expand_prompt
+    This may be set to a string that should be displayed before the
+    possible expansions. This is given to the -X option and thus may
+    contain the control sequences `%n', `%B', etc. Also, the sequence
+    `%o' in this string will be replaced by the original string.
+
+The _list completer allows one to delay the insertion of matches until
+completion is attempted a second time without the word on the line 
+being changed. On the first attempt, only the list of matches will
+be shown. Configuration keys understood are:
+
+  list_condition
+    If this key is unset or set to the empty string, this completer
+    will delay the insertion of matches unconditionally. However,
+    if this value is set, it should be set to an expression usable
+    inside a $[...] arithmetical expression. In this case, delaying
+    will be done if the expression evaluates to `1'.
+    For example, with
+
+      compconf list_condition='NUMERIC != 1'
+
+    delaying will be done only if given an explicit numeric argument
+    other than `1'.
+
+  list_word
+    To find out if only listing should be done, the code normally
+    compares the contents of the line with the contents the line
+    had at the time of the last invocation. If this key is set to
+    an non-empty string comparison is done using only the current
+    word. So if it is set, attempting completion on a word equal
+    to the one completion was called on the last time will not
+    delay the generation of matches.
+
+For more information about what the completers do, see the files
+containing them (_complete, _correct, _approximate, _match, and
+_expand in the Core directory).
+
+The _path_files function used to complete filenames has one
+configuration key:
+
+  path_expand
+    If this is set to a non-empty string, the partially typed path
+    from the line will be expanded as far as possible even if trailing
+    pathname components can not be completed.
diff --git a/Completion/User/_dd b/Completion/User/_dd
index 63ae40f50..6b07a382c 100644
--- a/Completion/User/_dd
+++ b/Completion/User/_dd
@@ -1,12 +1,12 @@
 #defcomp dd
 
-if [[ -iprefix conv= ]]; then
+if compset -P 1 'conv\='; then
   # If there's a comma present, ignore up to the last one.  The
   # test alone will have that effect.
-  [[ -string , ]]
+  compset -p '*,'
   compgen -S, -q \
       -k '(ascii ebcdic ibm block unblock lcase ucase swab noerror sync)'
-elif [[ -iprefix 'if=' || -iprefix 'of=' ]]; then
+elif compset -P 1 '[io]f\='; then
   _files
 else
   compgen -S '=' -k '(if of ibs obs bs cbs skip files seek count conv)'
diff --git a/Completion/User/_find b/Completion/User/_find
index cb637fc26..13d1d797e 100644
--- a/Completion/User/_find
+++ b/Completion/User/_find
@@ -2,14 +2,14 @@
 
 local prev="$words[CURRENT-1]"
 
-if [[ -mbetween -(ok|exec) \\\; ]]; then
+if compset -N '-(ok|exec)' '\;' then
   _normal
-elif [[ -iprefix - ]]; then
+elif compset -P 1 -; then
   compgen -s 'daystart {max,min,}depth follow noleaf version xdev \
     {a,c,}newer {a,c,m}{min,time} empty false {fs,x,}type gid inum links \
     {i,}{l,}name {no,}{user,group} path perm regex size true uid used \
     exec {f,}print{f,0,} ok prune ls'
-elif [[ -position 2 ]]; then
+elif [[ CURRENT -eq 2 ]]; then
   local ret=1
 
   compgen -g '. ..' && ret=0
diff --git a/Completion/User/_mh b/Completion/User/_mh
index c6c018220..ab78468d8 100644
--- a/Completion/User/_mh
+++ b/Completion/User/_mh
@@ -13,7 +13,7 @@ local prev="$words[CURRENT-1]"
 # To be on the safe side, check this exists and if not, get it anyway.
 [[ -d $mymhdir ]] || mymhdir=$(mhpath +)
 
-if [[ -iprefix - ]]; then
+if compset -P 1 -; then
   # get list of options, which MH commands can generate themselves
   # awk is just too icky to use for this, sorry.  send me one if
   # you come up with it.
@@ -23,7 +23,7 @@ if [[ -iprefix - ]]; then
     print $n =~ s/^\[([a-z]+)\]// ? "$n\n$1$n\n" : "$n\n";
   }')
   return
-elif [[ -iprefix '+' || -iprefix '@' || "$prev" = -draftfolder ]]; then
+elif compset -P 1 '[+@] || [ "$prev" = -draftfolder ]]; then
   # Complete folder names.
   local mhpath
 
diff --git a/Completion/User/_stty b/Completion/User/_stty
index d32d6bdff..73c860d54 100644
--- a/Completion/User/_stty
+++ b/Completion/User/_stty
@@ -5,7 +5,7 @@ if [[ "$words[CURRENT-1]" = \
 then
      compadd -Q '^-' '^h' '^?' '^c' '^u'
 else
-  [[ -string '-' || -string '+' ]]
+  compset -P '[-+]'
   compadd rows columns intr quit erase kill eof eol \
     eol2 start stop susp dsusp reprint discard werase lnext \
     parenb parodd cs8 cstopb hupcl cread clocal parext \
diff --git a/Config/version.mk b/Config/version.mk
index 005321c5d..747fa76a7 100644
--- a/Config/version.mk
+++ b/Config/version.mk
@@ -27,5 +27,5 @@
 # This must also serve as a shell script, so do not add spaces around the
 # `=' signs.
 
-VERSION=3.1.5-pws-13
-VERSION_DATE='March 20, 1999'
+VERSION=3.1.5-pws-14
+VERSION_DATE='March 26, 1999'
diff --git a/Doc/Zsh/compwid.yo b/Doc/Zsh/compwid.yo
index 46393167c..7d7e688b0 100644
--- a/Doc/Zsh/compwid.yo
+++ b/Doc/Zsh/compwid.yo
@@ -50,7 +50,7 @@ from the completion code to the completion widget and can be set to
 give information to the completion code from the completion
 widget. Some of the builtin commands and the condition codes use or
 change the current values of these parameters. While the completion
-widget is active, these parameters are reseton each function exit to
+widget is active, these parameters are reset on each function exit to
 the values they had when the function was entered.
 
 startitem()
@@ -80,6 +80,10 @@ taken as the string every possible match has to end with. The
 completion code sets this to the part of the current word from the
 cursor position to the end.
 )
+item(tt(ISUFFIX))(
+Like tt(IPREFIX), but for a suffix that should not be considered part
+of the matches.
+)
 item(tt(compstate))(
 This is an associative array with various keys and values the
 completion uses to give informtaion to the completion widget and to
@@ -201,11 +205,30 @@ otherwise.
 )
 item(tt(pattern_match))(
 If the option tt(GLOB_COMPLETE) is set, this is initially set to
-tt(yes) and unset otherwise. If the completion widget sets it to a
-non-empty string, the completion code will from then on behave as if
-tt(GLOB_COMPLETE) is set, i.e.. if the strings in tt(PREFIX) and
+`tt(*)' and unset otherwise. If the completion widget sets it to a
+`tt(*)', the completion code will from then on behave as if
+tt(GLOB_COMPLETE) is set, i.e. if the strings in tt(PREFIX) and
 tt(SUFFIX) contain unquoted metacharacters, they will be treated as
-patterns.
+patterns. If the string is set to any other non-empty string, the
+strings will be treated as patterns but the code will not automatically
+insert a star at the cursor position.
+)
+item(tt(pattern_insert))(
+Normally this is set to tt(menu) which means that menu-completion will be
+used whenever the matches were generated using pattern matching. If this
+is set to any other non-empty string by the user and menu-completion is
+not selected by other option settings, the code will insert an
+unambiguous string for the generated matches as with normal completion.
+)
+item(tt(unambiguous))(
+This key is read-only and will always be set to the unambiguous string
+the completion code has generated for all matches added so far.
+)
+item(tt(unambiguous_cursor))(
+This gives the position the cursor would be placed at when the
+unambiguous string would be inserted, relative to the value of the
+tt(unambiguous) key. The cursor would be placed before the character
+whise index is given by this key.
 )
 enditem()
 )
@@ -241,7 +264,8 @@ if at least one match was added and non-zero otherwise.
 xitem(tt(compadd) [ tt(-qQfnUam) ] [ tt(-F) var(array) ])
 xitem([ tt(-P) var(prefix) ] [ tt(-S) var(suffix) ])
 xitem([ tt(-p) var(hidden-prefix) ] [ tt(-s) var(hidden-suffix) ])
-xitem([ tt(-i) var(ignored-prefix) ] [ tt(-W) var(file-prefix) ])
+xitem([ tt(-i) var(ignored-prefix) ] [ tt(-I) var(ignored-suffix) ])
+xitem([ tt(-W) var(file-prefix) ])
 xitem([ tt(-J) var(name) ] [ tt(-V) var(name) ] [ tt(-X) var(explanation) ])
 xitem([ tt(-r) var(remove-chars) ] [ tt(-R) var(remove-func) ])
 item([ tt(-M) var(match-spec) ] [ tt(--) ] [ var(words) ... ])(
@@ -255,7 +279,7 @@ The completion code breaks the string to complete into six fields in
 the order: 
 
 indent(
-var(<ipre><apre><hpre><word><hsuf><asuf>)
+var(<ipre><apre><hpre><word><hsuf><asuf><isuf>)
 )
 
 The first field
@@ -273,7 +297,8 @@ appear in the list of completions, one of the tt(words) given at the
 end. The field var(<hsuf>) is like var(<hpre>) but gives a suffix that 
 should be matched but will not be listed. Finally, var(<asuf>) is the
 suffix given with tt(-S) that should automatically be added by the
-completion code.
+completion code and var(<isuf>) is like var(<ipre>), but taken from
+the tt(ISUFFIX) parameter and the tt(-I) option.
 
 The supported flags are:
 
@@ -301,6 +326,9 @@ string given with the `tt(-P)' option. Without `tt(-P)' the string is
 inserted before the string given with `tt(-p)' or directly before the
 match.
 )
+item(tt(-I) var(ignored-suffix))(
+Like tt(-i), but gives an ignored suffix.
+)
 item(tt(-J) var(name))(
 As for tt(compctl) and tt(compgen) this gives the name of the group
 of matches the words should be stored in.
@@ -416,6 +444,87 @@ hyphens.
 )
 enditem()
 )
+xitem(tt(compset -p) var(number))
+xitem(tt(compset -P) [ var(number) ] var(pattern))
+xitem(tt(compset -s) var(number))
+xitem(tt(compset -S) [ var(number) ] var(pattern))
+xitem(tt(compset -n) var(begin) [ var(end) ])
+item(tt(compset -p) var(beg-pat) [ var(end-pat) ])(
+This builtin allows to easily modify the special parameters and at
+the same time, to do tests on their values.
+
+The options are:
+
+startitem()
+item(tt(-p) var(number))(
+If the contents of the tt(PREFIX) parameter is longer than var(number)
+characters, the first var(number) characters are removed from it and
+appended to the contents of the tt(IPREFIX) parameter.
+)
+item(tt(-P) [ var(number) ] var(pattern))(
+If the value of the tt(PREFIX) parameter begins with anything that
+matches the var(pattern), the matched portion is removed from
+tt(PREFIX) and appended to tt(IPREFIX).
+
+Without the optional var(number), the longest match is taken, but
+if var(number) is given, anything up to the var(number)'th match is
+moved. If the var(number) is negative, the var(number)'th longest
+match is moved. For example, if tt(PREFIX) contains the string
+`tt(a=b=c)' doing tt(compset -P '*\=') will move the string `tt(a=b=)' 
+into the tt(IPREFIX) parameter, but tt(compset -P 1 '*\=') moves only
+the string `tt(a=)'.
+)
+item(tt(-s) var(number))(
+Like tt(-p), but prepend the last var(number) characters from the
+parameter tt(SUFFIX) to the contents of the parameter tt(ISUFFIX).
+)
+item(tt(-S) [ var(number) ] var(pattern))(
+Like tt(-P), but matching from the end of tt(SUFFIX) and moving the
+matched portion into the parameter tt(ISUFFIX).
+)
+item(tt(-n) var(begin) [ var(end) ])(
+If the current word position as specified by the parameter tt(CURRENT) 
+is greater than or equal to var(begin), anything up to the
+var(begin)'th word is removed from the tt(words) array and the value
+of the parameter tt(CURRENT) is decremented by var(begin).
+
+If the optional var(end) is given, the modification is done only if
+the current word position is also less than or equal to var(end). In
+this case, the words from position var(end) onwards are removed from
+the tt(words) array, too.
+
+Both of these numbers may be negative to make them count backwards
+from the last element of the tt(words) array.
+)
+item(tt(-N) var(beg-pat) [ var(end-pat) ])(
+If one of the elements of the tt(words) array up to the one at the
+index given by the value of the parameter tt(CURRENT) matches the
+pattern var(beg-pat), all elements up to the matching one are removed
+from the tt(words) array and the value of tt(CURRENT) is changed to
+point to the same word in the changed array.
+
+If the optional pattern var(end-pat) is also given and there is an
+element in the tt(words) array matching this pattern, the parameters
+are modified only if the index of this word is higher than the one
+given by the tt(CURRENT) parameter (meaning that the matching word has 
+to be after the cursor). In this case, the words from the word
+matching tt(end-pat) onwards are also removed from the tt(words)
+array. If tt(words) contains no word matching var(end-pat), the
+testing and modification is done as if it were not given.
+)
+enditem()
+
+In all of these cases the return value is zero if the test succeded
+and the parameters were modified, and non-zero otherwise. This allows
+one to use this builtin in tests as in:
+
+indent(
+tt(if compset -P '*\='; then ...)
+)
+
+Which makes anything up to and including the last equal sign be
+ignored by the completion code.
+)
 item(tt(compcall) [ tt(-TD) ])(
 
 This allows one to use completion definitions given with the
@@ -441,84 +550,26 @@ Inside completion widgets not only the builtin commands described
 above can be used, but also some additional condition codes. These
 work on the special parameters and can be used to easily build
 completion functions that generate different matches depending on the
-strings on the line.
+strings on the line. All of these condition codes perform tests also
+done by the tt(compset) builtin, but they don't modify the contents of 
+the special parameters.
 
 The following condition codes are made available inside completion
 widgets:
 
 startitem()
-item(tt(-prefix) var(string))(
-true if the content of tt(PREFIX) starts with var(string)
-)
-item(tt(-iprefix) var(string))(
-like tt(-prefix), but the var(string) is removed from tt(PREFIX) and
-added to tt(IPREFIX)
-)
-item(tt(-position) var(beg) [ var(end) ])(
-true if tt(CURRENT) is equal to var(beg) or, if var(end) is given,
-equal to or greater than var(beg) and equal to or less than var(end);
-both of var(beg) and var(end) may be arithmetic expressions, if they
-are less than zero the number of words in tt(words) are added to them
-before comparing them to tt(CURRENT); thus, tt(-1) is the last word,
-tt(-2) is the word before that and so on; note that positions are
-taken as indexes into the tt(words) array and thus are counted as if
-the tt(ksharray) is not set
-)
-item(tt(-word) var(index) var(string))(
-true if the word number var(index) in tt(words) is equal to
-var(string); again, var(index) may be negative, counting backwards
-)
-item(tt(-mword) var(index) var(pattern))(
-like tt(-word) but using pattern matching
-)
-item(tt(-current) var(offset) var(string))(
-like tt(-word) but var(offset) is relative to the value of
-tt(CURRENT)
-)
-item(tt(-mcurrent) var(offset) var(pattern))(
-like tt(-current) but using pattern matching
-)
-item(tt(-string) [ var(number) ] var(string))(
-true if the current word contains var(string); anything up to the last 
-occurrence of this string will be ingnored by removing it from
-tt(PREFIX) and adding it to tt(IPREFIX); if var(number) is given,
-anything up to the var(number)'th occurrence of the var(string) will
-be ignored; again, var(number) may be any arithmetic expression and
-negative values count backward
-)
-item(tt(-class) [ var(number) ] var(class))(
-like tt(-string) but the var(class) is used as a character class so
-that anything up to and including the last or the var(number)'th
-occurrence of any character from the string var(class) is ignored
-)
-item(tt(-words) var(min) [ var(max) ])(
-true if the number of words is equal to var(min); if var(max) is
-given, it is true if the number of words is equal to or greater than
-var(min) and equal to or less than var(max)
-)
-item(tt(-after) var(string))(
-true if the cursor is after a word that is equal to var(string); this
-removes all words up to and including the matched word from the
-tt(words) array
-)
-item(tt(-mafter) var(pattern))(
-like tt(-after) but using pattern matching
-)
-item(tt(-between) var(string1) var(string2))(
-true if the cursor is after a word that is equal to var(string1), if
-there is also a word that is equal to var(string2), this is true only
-if the cursor is before it; as a side effect, all words before
-var(string1) and after var(string2) (both inclusive) are removed from
-the tt(words) array
-)
-item(tt(-mbetween) var(pattern1) var(pattern2))(
-like tt(-between) but using pattern matching
-)
-item(tt(-nmatches) var(number))(
-true if the the value of tt(compstate[nmatches]) is equal to var(number)
-)
-item(tt(-matcher) var(number))(
-true if the value of tt(compstate[matcher]) is equal to var(number)
+item(tt(-prefix) [ var(number) ] var(pattern))(
+true if the test for the tt(-P) option of tt(compset) would succeed
+)
+item(tt(-suffix) [ var(number) ] var(pattern))(
+true if the test for the tt(-S) option of tt(compset) would succeed
+)
+item(tt(-after) var(beg-pat))(
+true if the test of the tt(-N) option with only the var(beg-pat) given 
+would succeed
+)
+item(tt(-between) var(beg-pat end-pat))(
+true if the test for the tt(-N) option with both patterns would succeed
 )
 enditem()
 
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index 4f3a75199..1dced5b7d 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -329,7 +329,12 @@ item(tt(${)var(name)tt(}))(
 The value, if any, of the parameter var(name) is substituted.
 The braces are required if the expansion is to be followed by
 a letter, digit, or underscore that is not to be interpreted
-as part of var(name).
+as part of var(name).  In addition, more complicated forms of substitution
+usually require the braces to be present; exceptions, which only apply if
+the option tt(KSH_ARRAYS) is not set, are a single subscript or any colon
+modifiers appearing after the name, or any of the characters tt(^), tt(=),
+tt(~), tt(#) or tt(+) appearing before the name, all of which work with or
+without braces.
 
 If var(name) is an array parameter, then the value of each
 element of var(name) is substituted, one element per word.
@@ -492,14 +497,9 @@ tt($LPAR())...tt(RPAR()) type command substitution is used in place of
 var(name) above, it is expanded first and the result is used as if
 it were the value of var(name).  Thus it is
 possible to perform nested operations:  tt(${${foo#head}%tail})
-substitues the value of tt($foo) with both tt(head) and tt(tail)
+substitutes the value of tt($foo) with both tt(head) and tt(tail)
 deleted.  The form with tt($LPAR())...tt(RPAR()) is often useful in
-combination with the flags described next; see the example below.
-
-Note that when nested parameter expansion takes place the flags are em(not)
-propagated back.  Each level of expansion uses three factors: whether it
-is in double quotes, what flags it has been provided with, and whether the
-value it has is a scalar or an array.  Some examples are given below.
+combination with the flags described next; see the examples below.
 
 subsect(Parameter Expansion Flags)
 cindex(parameter expansion flags)
@@ -681,7 +681,7 @@ flag, or with tt(${)...tt(/)...tt(}) (only the var(expr)th match is
 substituted) or tt(${)...tt(//)...tt(}) (all matches from the
 var(expr)th on are substituted).  The var(expr)th match is counted
 such that there is either one or zero matches from each starting
-position in the string, although for global subsitution matches
+position in the string, although for global substitution matches
 overlapping previous replacements are ignored.
 )
 item(tt(M))(
@@ -703,57 +703,82 @@ enditem()
 
 subsect(Rules)
 
-Here is a summary of the rules for substitution.  Some particular examples
-are given below.  Note that the Zsh Development Group accepts em(no
-responsibility) for any brain damage which may occur during the reading of
-the following rules.
+Here is a summary of the rules for substitution; this assumes that braces
+are present around the substitution, i.e. tt(${...}).  Some particular
+examples are given below.  Note that the Zsh Development Group accepts
+em(no responsibility) for any brain damage which may occur during the
+reading of the following rules.
 
 startitem()
-item(tt(1.))(
+item(tt(1.) em(Nested Substitution))(
 If multiple nested tt(${...}) forms are present, substitution is
 performed from the inside outwards.  At each level, the substitution takes
 account of whether the current value is a scalar or an array, whether the
 whole substitution is in double quotes, and what flags are supplied to the
-current level of substitution.  If the value is a raw parameter reference
-with a subscript, such as tt(${)var(var)tt([3]}), the effect of
-subscripting is applied directly to the parameter.  The value passed back
-to an enclosing substitution is always an array, which however will consist
-of one word if the value was not itself an array.
-)
-item(tt(2.))(
+current level of substitution; the flags are not propagated up to enclosing
+substitutions.  The value passed back to an enclosing substitution is
+always an array, which however will consist of one word if the value was
+not itself an array.  All the following steps take place where applicable
+at all levels of substitution.
+)
+item(tt(2.) em(Parameter Subscripting))(
+If the value is a raw parameter reference with a subscript, such as
+tt(${)var(var)tt([3]}), the effect of subscripting is applied directly to
+the parameter.  If the parameter is an array, any second subscript,
+indexing on the character in the word, may appear,
+e.g. tt(${)var(var)tt([1][2]}).
+)
+item(tt(3.) em(Parameter Name Replacement))(
+The effect of any tt((P)) flag, which treats the value so far as a
+parameter name and replaces it with the corresponding value, is applied.
+)
+item(tt(4.) em(Double-Quoted Joining))(
 If the value after this process is an array, and the substitution
 appears in double quotes, and no tt((@)) flag is present at the current
 level, the words of the value are joined with the first character of the
 parameter tt($IFS), by default a space, between each word (single word
 arrays are not modified).  If the tt((j)) flag is present, that is used for
-joining instead of tt($IFS). Any remaining subscript is evaluated at
-this point, based on whether the value is an array or a scalar.
+joining instead of tt($IFS).
+)
+item(tt(5.) em(Nested Subscripting))(
+Any remaining subscript (i.e. of a nested substitution) is evaluated at
+this point, based on whether the value is an array or a scalar; if it was
+an array, a second subscript for the character in the word may also appear.
 )
-item(tt(3.))(
+item(tt(6.) em(Modifiers))(
 Any modifiers, as specified by a trailing tt(#), tt(%), tt(/)
 (possibly doubled) or by a set of modifiers of the form tt(:...) (see
 noderef(Modifiers) in noderef(History Expansion)), are applied to the words
 of the value at this level.
 )
-item(tt(4.))(
+item(tt(7.) em(Forced Joining))(
 If the tt((j)) flag is present, or no tt((j)) flag is present but
-the string is to be split as given by rules tt(5.) or tt(6.), and joining
-did not take place at step tt(2.), any words in the value are joined
+the string is to be split as given by rules tt(8.) or tt(9.), and joining
+did not take place at step tt(4.), any words in the value are joined
 together using the given string or the first character of tt($IFS) if none.
 Note that the tt((F)) flag implicitly supplies a string for joining in this
 manner.
 )
-item(tt(5.))(
+item(tt(8.) em(Forced Splitting))(
 If one of the tt((s)) or tt((f)) flags are present, or the tt(=)
-specifier was present (e.g. tt(${=)var(var)tt(})), the word is joined on
+specifier was present (e.g. tt(${=)var(var)tt(})), the word is split on
 occurrences of the specified string, or (for tt(=) with neither of the two
 flags present) any of the characters in tt($IFS).
 )
-item(tt(6.))(
+item(tt(9.) em(Shell Word Splitting))(
 If no tt((s)), tt((f)) or tt(=) was given, but the word is not
 quoted and the option tt(SH_WORD_SPLIT) is set, the word is split on
-occurrences of any of the characters in tt($IFS).  Note that all steps,
-including this one, take place at all levels of a nested substitution.
+occurrences of any of the characters in tt($IFS).  Note this step, too,
+take place at all levels of a nested substitution.
+)
+item(tt(10.) em(Re-Evaluation))(
+Any tt((e)) flag is applied to the value, forcing it to be re-examined for
+new parameter substitutions, but also for command and arithmetic
+substitutions.
+)
+item(tt(11.) em(Padding))(
+Any padding of the value by the tt(LPAR()l.)var(fill)tt(.RPAR()) or
+tt(LPAR()r.)var(fill)tt(.RPAR()) flags is applied.
 )
 enditem()
 
@@ -1272,13 +1297,13 @@ item(tt(f)var(spec))(
 files with access rights matching var(spec). This var(spec) may be a
 octal number optionally preceded by a `tt(=)', a `tt(PLUS())', or a
 `tt(-)'. If none of these characters is given, the behavior is the
-same as for `tt(=)'. The octal number decribes the mode bits to be
+same as for `tt(=)'. The octal number describes the mode bits to be
 expected, if combined with a `tt(=)', the value given must match the
 file-modes exactly, with a `tt(PLUS())', at least the bits in the
 given number must be set in the file-modes, and with a `tt(-)', the
 bits in the number must not be set. Giving a `tt(?)' instead of a
 octal digit anywhere in the number ensures that the corresponding bits 
-inthe file-modes are not checked, this is only useful in combination
+in the file-modes are not checked, this is only useful in combination
 with `tt(=)'.
 
 If the qualifier `tt(f)' is followed by any other character anything
@@ -1290,7 +1315,7 @@ described above or a list of any of the characters `tt(u)', `tt(g)',
 `tt(o)', and `tt(a)', followed by a `tt(=)', a `tt(PLUS())', or a
 `tt(-)', followed by a list of any of the characters `tt(r)', `tt(w)', 
 `tt(x)', `tt(s)', and `tt(t)', or a octal digit. The first list of
-characters specify which acess rights are to be checked. If a `tt(u)'
+characters specify which access rights are to be checked. If a `tt(u)'
 is given, those for the owner of the file are used, if a `tt(g)' is
 given, those of the group are checked, a `tt(o)' means to test those
 of other users, and the `tt(a)' says to test all three groups. The
@@ -1308,7 +1333,7 @@ no rights, independent of the permissions for other user. The pattern
 `tt(*(f-100))' gives all files for which the owner does not have
 execute permission, and `tt(*(f:gu+w,o-rx))' gives the files for which 
 the owner and the other members of the group have at least write
-permission, and fo which other users don't have read or execute
+permission, and for which other users don't have read or execute
 permission.
 )
 item(tt(d)var(dev))(
diff --git a/Doc/Zsh/files.yo b/Doc/Zsh/files.yo
index a90e2b3d5..6eb6c202e 100644
--- a/Doc/Zsh/files.yo
+++ b/Doc/Zsh/files.yo
@@ -20,6 +20,13 @@ commands are read from tt(/etc/zshrc) and then tt($ZDOTDIR/.zshrc).
 Finally, if the shell is a login shell, tt(/etc/zlogin) and
 tt($ZDOTDIR/.zlogin) are read.
 
+If the option tt(GLOBAL_RCS_FIRST) is set when the shell is invoked or in
+the file tt(/etc/zshenv), then all the applicable global startup files are
+read before all the users', so that the order becomes tt(/etc/zshenv)
+... tt(/etc/zlogin), tt($ZDOTDIR/.zshenv) ... tt($ZDOTDIR/.zlogin).
+After tt(/etc/zshenv) has been read, changing the option has no further
+effect.
+
 When a login shell exits, the files tt($ZDOTDIR/.zlogout) and then
 tt(/etc/zlogout) are read.  This happens with either an explicit exit
 via the tt(exit) or tt(logout) commands, or an implict exit by reading
diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index b59fa624a..c4a8a084e 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -326,6 +326,16 @@ item(tt(GLOB) (tt(PLUS()F), ksh: tt(PLUS()f)))(
 Perform filename generation (globbing).
 (See noderef(Filename Generation).)
 )
+pindex(GLOBAL_RCS_FIRST)
+cindex(startup files, changing order)
+cindex(files, startup, changing order)
+item(tt(GLOBAL_RCS_FIRST) (tt(-d)))(
+If this option is set, and the tt(RCS) option is also set (as it is by
+default), then the order in which startup files are sourced after
+tt(/etc/zshenv) changes to tt(/etc/zprofile), tt(/etc/zshrc),
+tt(/etc/zlogin), tt(.zshenv), tt(.zprofile), tt(.zshrc), tt(.zlogin).
+The order of tt(.zlogout) and tt(/etc/zlogout) is not affected.
+)
 pindex(GLOB_ASSIGN)
 item(tt(GLOB_ASSIGN))(
 If this option is set, filename generation (globbing) is
@@ -343,11 +353,11 @@ pindex(GLOB_COMPLETE)
 item(tt(GLOB_COMPLETE))(
 When the current word has a glob pattern, do not insert all the words
 resulting from the expansion but generate matches as for completion and
-cycle through them like tt(MENU_COMPLETE). If no matches are found, a
-`tt(*)' is added to the end of the word or inserted at the cursor if
-tt(COMPLETE_IN_WORD) is set, and completion is attempted again using
-pattern matching.  Since this doesn't use globbing, it works not only for
-files but for all completions, such as options, user names, etc.
+cycle through them like tt(MENU_COMPLETE). The matches are generated
+as if a `tt(*)' is added to the end of the word or inserted at the cursor if
+tt(COMPLETE_IN_WORD) is set.  Since this doesn't use globbing, it
+works not only for files but for all completions, such as options,
+user names, etc.
 )
 pindex(GLOB_DOTS)
 cindex(globbing, of . files)
@@ -1049,6 +1059,7 @@ endsitem()
 subsect(Also note)
 startsitem()
 sitem(tt(-A))(Used by tt(set) for setting arrays)
+sitem(tt(-b))(Used on the command line to specify end of option processing)
 sitem(tt(-c))(Used on the command line to specify a single command)
 sitem(tt(-m))(Used by tt(setopt) for pattern-matching option setting)
 sitem(tt(-o))(Used in all places to allow use of long option names)
diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo
index 439caa2f8..bd91f0efa 100644
--- a/Doc/Zsh/zle.yo
+++ b/Doc/Zsh/zle.yo
@@ -158,6 +158,10 @@ vindex(NUMERIC)
 item(tt(NUMERIC) (integer))(
 The numeric argument.
 )
+vindex(HISTNO)
+item(tt(HISTNO) (integer))(
+The current history number.
+)
 enditem()
 sect(Standard Widgets)
 cindex(widgets, standard)
diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c
index 413dd8f43..126aa061e 100644
--- a/Src/Modules/zftp.c
+++ b/Src/Modules/zftp.c
@@ -391,7 +391,7 @@ zfpipe()
 
 /**/
 static void
-zfunalarm()
+zfunalarm(void)
 {
     if (oalremain) {
 	/*
@@ -670,7 +670,7 @@ zfgetline(char *ln, int lnsize, int tmout)
 
 /**/
 static int 
-zfgetmsg()
+zfgetmsg(void)
 {
     char line[256], *ptr, *verbose;
     int stopit, printing = 0, tmout;
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index 1444197a3..42a647199 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -36,7 +36,6 @@ typedef struct patcomp   *Patcomp;
 typedef struct cmatcher  *Cmatcher;
 typedef struct cmlist    *Cmlist;
 typedef struct cpattern  *Cpattern;
-typedef struct cline     *Cline;
 
 /* node for compctl hash table (compctltab) */
 
@@ -203,6 +202,7 @@ struct cmatch {
     char *str;			/* the match itself */
     char *ipre;			/* ignored prefix, has to be re-inserted */
     char *ripre;		/* ignored prefix, unquoted */
+    char *isuf;			/* ignored suffix */
     char *ppre;			/* the path prefix */
     char *psuf;			/* the path suffix */
     char *prpre;		/* path prefix for opendir */
@@ -255,28 +255,6 @@ struct cpattern {
     int equiv;			/* if this is a {...} class */
 };
 
-
-struct cline {
-    Cline next;			/* next chunk */
-    char *line;			/* string to insert if !word */
-    int llen;			/* length of line */
-    char *word;			/* prefered string to insert */
-    int wlen;			/* length of word */
-    int flags;			/* see CLF_* below */
-    Cline prefix;		/* prefix we've build for new parts */
-    Cline suffix;		/* suffix we've build for new parts */
-};
-
-#define CLF_END    1
-#define CLF_MID    2
-#define CLF_MISS   4
-#define CLF_DIFF   8
-#define CLF_SUF   16
-#define CLF_PNEW  32
-#define CLF_SNEW  64
-#define CLF_VAR  128
-#define CLF_JOIN 256
-
 /* Flags for makecomplist*(). Things not to do. */
 
 #define CFN_FIRST   1
@@ -296,27 +274,31 @@ struct cline {
 #define CP_PREFIX     (1 <<  2)
 #define CP_SUFFIX     (1 <<  3)
 #define CP_IPREFIX    (1 <<  4)
-#define CP_COMPSTATE  (1 <<  5)
-
-#define CP_REALPARAMS        6
-
-#define CP_NMATCHES   (1 <<  6)
-#define CP_MATCHER    (1 <<  7)
-#define CP_MATCHERSTR (1 <<  8)
-#define CP_MATCHERTOT (1 <<  9)
-#define CP_CONTEXT    (1 << 10)
-#define CP_PARAMETER  (1 << 11)
-#define CP_REDIRECT   (1 << 12)
-#define CP_QUOTE      (1 << 13)
-#define CP_QUOTING    (1 << 14)
-#define CP_RESTORE    (1 << 15)
-#define CP_LIST       (1 << 16)
-#define CP_FORCELIST  (1 << 17)
-#define CP_INSERT     (1 << 18)
-#define CP_EXACT      (1 << 19)
-#define CP_EXACTSTR   (1 << 20)
-#define CP_PATMATCH   (1 << 21)
-
-#define CP_NUM              22
+#define CP_ISUFFIX    (1 <<  5)
+#define CP_COMPSTATE  (1 <<  6)
+
+#define CP_REALPARAMS        7
+
+#define CP_NMATCHES   (1 <<  7)
+#define CP_MATCHER    (1 <<  8)
+#define CP_MATCHERSTR (1 <<  9)
+#define CP_MATCHERTOT (1 << 10)
+#define CP_CONTEXT    (1 << 11)
+#define CP_PARAMETER  (1 << 12)
+#define CP_REDIRECT   (1 << 13)
+#define CP_QUOTE      (1 << 14)
+#define CP_QUOTING    (1 << 15)
+#define CP_RESTORE    (1 << 16)
+#define CP_LIST       (1 << 17)
+#define CP_FORCELIST  (1 << 18)
+#define CP_INSERT     (1 << 19)
+#define CP_EXACT      (1 << 20)
+#define CP_EXACTSTR   (1 << 21)
+#define CP_PATMATCH   (1 << 22)
+#define CP_PATINSERT  (1 << 23)
+#define CP_UNAMBIG    (1 << 24)
+#define CP_UNAMBIGC   (1 << 25)
+
+#define CP_NUM              26
 
 #define CP_ALLMASK    ((1 << CP_NUM) - 1)
diff --git a/Src/Zle/comp1.c b/Src/Zle/comp1.c
index 29836fbac..904f66305 100644
--- a/Src/Zle/comp1.c
+++ b/Src/Zle/comp1.c
@@ -52,10 +52,10 @@ void (*comp_setunsetptr) _((int, int));
 /* pointers to functions required by compctl and defined by zle */
 
 /**/
-int (*addmatchesptr) _((char *, char *, char *, char *, char *, char *, char *, char *, char *, char *, int, int, Cmatcher, char *, char **));
+int (*addmatchesptr) _((char *, char *, char *, char *, char *, char *, char *, char *, char *, char *, char *, int, int, Cmatcher, char *, char **));
 
 /**/
-char *(*comp_strptr) _((int*, int*, int));
+char *(*comp_strptr) _((int *, int *, int));
 
 /**/
 int (*getcpatptr) _((char *, int, char *, int));
@@ -66,6 +66,9 @@ int (*makecomplistcallptr) _((Compctl));
 /**/
 int (*makecomplistctlptr) _((int));
 
+/**/
+char *(*unambig_dataptr) _((int *));
+
 /* Hash table for completion info for commands */
  
 /**/
@@ -107,6 +110,7 @@ char **compwords,
      *compprefix,
      *compsuffix,
      *compiprefix,
+     *compisuffix,
      *compmatcherstr,
      *compcontext,
      *compparameter,
@@ -119,7 +123,8 @@ char **compwords,
      *compinsert,
      *compexact,
      *compexactstr,
-     *comppatmatch;
+     *comppatmatch,
+     *comppatinsert;
 
 /**/
 Param *comppms;
@@ -430,10 +435,11 @@ setup_comp1(Module m)
     cc_first.mask2 = CC_CCCONT;
     comppms = NULL;
     compwords = NULL;
-    compprefix = compsuffix = compiprefix = compmatcherstr = 
+    compprefix = compsuffix = compiprefix = compisuffix = compmatcherstr = 
 	compcontext = compparameter = compredirect = compquote =
 	compquoting = comprestore = complist = compinsert =
-	compexact = compexactstr = comppatmatch = compforcelist = NULL;
+	compexact = compexactstr = comppatmatch = comppatinsert =
+	compforcelist = NULL;
     makecompparamsptr = NULL;
     comp_setunsetptr = NULL;
     return 0;
@@ -466,6 +472,7 @@ finish_comp1(Module m)
     zsfree(compprefix);
     zsfree(compsuffix);
     zsfree(compiprefix);
+    zsfree(compisuffix);
     zsfree(compmatcherstr);
     zsfree(compcontext);
     zsfree(compparameter);
@@ -479,6 +486,7 @@ finish_comp1(Module m)
     zsfree(compexact);
     zsfree(compexactstr);
     zsfree(comppatmatch);
+    zsfree(comppatinsert);
     return 0;
 }
 
diff --git a/Src/Zle/comp1.export b/Src/Zle/comp1.export
index 23d2d8dd2..1be9b6492 100644
--- a/Src/Zle/comp1.export
+++ b/Src/Zle/comp1.export
@@ -18,12 +18,14 @@ compexactstr
 compforcelist
 compinsert
 compiprefix
+compisuffix
 complist
 compmatcher
 compmatcherstr
 compmatchertot
 compnmatches
 compparameter
+comppatinsert
 comppatmatch
 comppms
 compprefix
@@ -48,3 +50,4 @@ makecomplistctlptr
 makecompparamsptr
 patcomps
 rembslash
+unambig_dataptr
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index df38dcc96..2ebe2d4a4 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -1406,7 +1406,7 @@ printcompctl(char *s, Compctl cc, int printflags, int ispat)
 	    untokenize(p);
 	    quotedzputs(p, stdout);
 	} else
-	    quotedzputs(bslashquote(s, NULL, NULL, NULL, 0), stdout);
+	    quotedzputs(bslashquote(s, NULL, 0), stdout);
     }
 
     /* loop through flags w/o args that are set, printing them if so */
@@ -1537,7 +1537,7 @@ printcompctl(char *s, Compctl cc, int printflags, int ispat)
 		char *p = dupstring(s);
 
 		untokenize(p);
-		quotedzputs(bslashquote(p, NULL, NULL, NULL, 0), stdout);
+		quotedzputs(bslashquote(p, NULL, 0), stdout);
 	    }
 	}
 	putchar('\n');
@@ -1692,7 +1692,7 @@ static int
 bin_compadd(char *name, char **argv, char *ops, int func)
 {
     char *p, **sp, *e;
-    char *ipre = NULL, *ppre = NULL, *psuf = NULL, *prpre = NULL;
+    char *ipre = NULL, *isuf = NULL, *ppre = NULL, *psuf = NULL, *prpre = NULL;
     char *pre = NULL, *suf = NULL, *group = NULL, *m = NULL, *rs = NULL;
     char *ign = NULL, *rf = NULL, *expl = NULL;
     int f = 0, a = CAF_MATCH, dm;
@@ -1753,6 +1753,10 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 		sp = &ipre;
 		e = "string expected after -%c";
 		break;
+	    case 'I':
+		sp = &isuf;
+		e = "string expected after -%c";
+		break;
 	    case 'p':
 		sp = &ppre;
 		e = "string expected after -%c";
@@ -1821,13 +1825,286 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 	return 1;
 
     match = cpcmatcher(match);
-    a = addmatchesptr(ipre, ppre, psuf, prpre, pre, suf, group,
+    a = addmatchesptr(ipre, isuf, ppre, psuf, prpre, pre, suf, group,
 		      rs, rf, ign, f, a, match, expl, argv);
     freecmatcher(match);
 
     return a;
 }
 
+#define CVT_RANGENUM 0
+#define CVT_RANGEPAT 1
+#define CVT_PRENUM   2
+#define CVT_PREPAT   3
+#define CVT_SUFNUM   4
+#define CVT_SUFPAT   5
+
+static void
+ignore_prefix(int l)
+{
+    char *tmp, sav = compprefix[l];
+
+    compprefix[l] = '\0';
+    tmp = tricat(compiprefix, compprefix, "");
+    zsfree(compiprefix);
+    compiprefix = tmp;
+    compprefix[l] = sav;
+    tmp = ztrdup(compprefix + l);
+    zsfree(compprefix);
+    compprefix = tmp;
+}
+
+static void
+ignore_suffix(int l)
+{
+    char *tmp, sav;
+
+    l = strlen(compsuffix) - l;
+    tmp = tricat(compsuffix + l, compisuffix, "");
+    zsfree(compisuffix);
+    compisuffix = tmp;
+    sav = compsuffix[l];
+    compsuffix[l] = '\0';
+    tmp = ztrdup(compsuffix);
+    compsuffix[l] = sav;
+    zsfree(compsuffix);
+    compsuffix = tmp;
+}
+
+/**/
+static void
+restrict_range(int b, int e)
+{
+    int i = e - b + 1;
+    char **p = (char **) zcalloc((i + 1) * sizeof(char *)), **q, **pp;
+
+    for (q = p, pp = compwords + b; i; i--, q++, pp++)
+	*q = ztrdup(*pp);
+    freearray(compwords);
+    compwords = p;
+    compcurrent -= b;
+}
+
+static int
+do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod)
+{
+    switch (test) {
+    case CVT_RANGENUM:
+	{
+	    int l = arrlen(compwords);
+
+	    if (na < 0)
+		na += l;
+	    else
+		na--;
+	    if (nb < 0)
+		nb += l;
+	    else
+		nb--;
+
+	    if (compcurrent - 1 < na || compcurrent - 1 > nb)
+		return 0;
+
+	    restrict_range(na, nb);
+	    return 1;
+	}
+    case CVT_RANGEPAT:
+	{
+	    char **p;
+	    int i, l = arrlen(compwords), t = 0, b = 0, e = l - 1;
+	    Comp c;
+
+	    i = compcurrent - 1;
+	    if (i < 0 || i >= l)
+		return 0;
+
+	    singsub(&sa);
+	    c = parsereg(sa);
+
+	    for (i--, p = compwords + i; i >= 0; p--, i--) {
+		if (domatch(*p, c, 0)) {
+		    b = i + 1;
+		    t = 1;
+		    break;
+		}
+	    }
+	    if (t && sb) {
+		int tt = 0;
+
+		singsub(&sb);
+		c = parsereg(sb);
+
+		for (i++, p = compwords + i; i < l; p++, i++) {
+		    if (domatch(*p, c, 0)) {
+			e = i - 1;
+			tt = 1;
+			break;
+		    }
+		}
+		if (tt && i < compcurrent)
+		    t = 0;
+	    }
+	    if (e < b)
+		t = 0;
+	    if (t)
+		restrict_range(b, e);
+	    return t;
+	}
+    case CVT_PRENUM:
+    case CVT_SUFNUM:
+	if (!na)
+	    return 1;
+	if (na > 0 &&
+	    strlen(test == CVT_PRENUM ? compprefix : compsuffix) >= na) {
+	    if (mod) {
+		if (test == CVT_PRENUM)
+		    ignore_prefix(na);
+		else
+		    ignore_suffix(na);
+		return 1;
+	    }
+	    return 0;
+	}
+    case CVT_PREPAT:
+    case CVT_SUFPAT:
+	{
+	    Comp c;
+
+	    if (!na)
+		return 0;
+
+	    if (!(c = parsereg(sa)))
+		return 0;
+
+	    if (test == CVT_PREPAT) {
+		int l, add;
+		char *p, sav;
+
+		if (!(l = strlen(compprefix)))
+		    return 0;
+		if (na < 0) {
+		    p = compprefix + l;
+		    na = -na;
+		    add = -1;
+		} else {
+		    p = compprefix + 1;
+		    add = 1;
+		}
+		for (; l; l--, p += add) {
+		    sav = *p;
+		    *p = '\0';
+		    test = domatch(compprefix, c, 0);
+		    *p = sav;
+		    if (test && !--na)
+			break;
+		}
+		if (!l)
+		    return 0;
+
+		ignore_prefix(p - compprefix);
+	    } else {
+		int l, ol, add;
+		char *p;
+
+		if (!(ol = l = strlen(compsuffix)))
+		    return 0;
+		if (na < 0) {
+		    p = compsuffix;
+		    na = -na;
+		    add = 1;
+		} else {
+		    p = compsuffix + l - 1;
+		    add = -1;
+		}
+		for (; l; l--, p += add)
+		    if (domatch(p, c, 0) && !--na)
+			break;
+
+		if (!l)
+		    return 0;
+
+		ignore_suffix(ol - (p - compsuffix));
+	    }
+	    return 1;
+	}
+    }
+    return 0;
+}
+
+/**/
+static int
+bin_compset(char *name, char **argv, char *ops, int func)
+{
+    int test = 0, na = 0, nb = 0;
+    char *sa = NULL, *sb = NULL;
+
+    if (incompfunc != 1) {
+	zerrnam(name, "can only be called from completion function", NULL, 0);
+	return 1;
+    }
+    if (argv[0][0] != '-') {
+	zerrnam(name, "missing option", NULL, 0);
+	return 1;
+    }
+    switch (argv[0][1]) {
+    case 'n': test = CVT_RANGENUM; break;
+    case 'N': test = CVT_RANGEPAT; break;
+    case 'p': test = CVT_PRENUM; break;
+    case 'P': test = CVT_PREPAT; break;
+    case 's': test = CVT_SUFNUM; break;
+    case 'S': test = CVT_SUFPAT; break;
+    default:
+	zerrnam(name, "bad option -%c", NULL, argv[0][1]);
+	return 1;
+    }
+    if (argv[0][2]) {
+	sa = argv[0] + 2;
+	sb = argv[1];
+	na = 2;
+    } else {
+	if (!(sa = argv[1])) {
+	    zerrnam(name, "missing string for option -%c", NULL, argv[0][1]);
+	    return 1;
+	}
+	sb = argv[2];
+	na = 3;
+    }
+    if (((test == CVT_PRENUM || test == CVT_SUFNUM) ? !!sb :
+	 (sb && argv[na]))) {
+	zerrnam(name, "too many arguments", NULL, 0);
+	return 1;
+    }
+    switch (test) {
+    case CVT_RANGENUM:
+	na = atoi(sa);
+	nb = (sb ? atoi(sb) : -1);
+	break;
+    case CVT_RANGEPAT:
+	tokenize(sa);
+	sa = rembslash(sa);
+	if (sb) {
+	    tokenize(sb);
+	    sb = rembslash(sb);
+	}
+	break;
+    case CVT_PRENUM:
+    case CVT_SUFNUM:
+	na = atoi(sa);
+	break;
+    case CVT_PREPAT:
+    case CVT_SUFPAT:
+	if (sb) {
+	    na = atoi(sa);
+	    sa = sb;
+	} else
+	    na = -1;
+	tokenize(sa);
+	sa = rembslash(sa);
+	break;
+    }
+    return !do_comp_vars(test, na, sa, nb, sb, 1);
+}
+
 /**/
 static int
 bin_compcall(char *name, char **argv, char *ops, int func)
@@ -1843,36 +2120,40 @@ bin_compcall(char *name, char **argv, char *ops, int func)
 /* Definitions for the special parameters. Note that these have to match the
  * order of the CP_* bits in comp.h */
 
-#define VAR(X) ((void *) (&(X)))
+#define VAL(X) ((void *) (&(X)))
 static struct compparam {
     char *name;
     int type;
-    void *var;
+    void *var, *set, *get;
 } compparams[] = {
-    { "words", PM_ARRAY, VAR(compwords) },
-    { "CURRENT", PM_INTEGER, VAR(compcurrent) },
-    { "PREFIX", PM_SCALAR, VAR(compprefix) },
-    { "SUFFIX", PM_SCALAR, VAR(compsuffix) },
-    { "IPREFIX", PM_SCALAR, VAR(compiprefix) },
-    { NULL, 0, NULL },
-
-    { "nmatches", PM_INTEGER, VAR(compnmatches) },
-    { "matcher", PM_INTEGER, VAR(compmatcher) },
-    { "matcher_string", PM_SCALAR, VAR(compmatcherstr) },
-    { "total_matchers", PM_INTEGER, VAR(compmatchertot) },
-    { "context", PM_SCALAR, VAR(compcontext) },
-    { "parameter", PM_SCALAR, VAR(compparameter) },
-    { "redirect", PM_SCALAR, VAR(compredirect) },
-    { "quote", PM_SCALAR, VAR(compquote) },
-    { "quoting", PM_SCALAR, VAR(compquoting) },
-    { "restore", PM_SCALAR, VAR(comprestore) },
-    { "list", PM_SCALAR, VAR(complist) },
-    { "force_list", PM_SCALAR, VAR(compforcelist) },
-    { "insert", PM_SCALAR, VAR(compinsert) },
-    { "exact", PM_SCALAR, VAR(compexact) },
-    { "exact_string", PM_SCALAR, VAR(compexactstr) },
-    { "pattern_match", PM_SCALAR, VAR(comppatmatch) },
-    { NULL, 0, NULL }
+    { "words", PM_ARRAY, VAL(compwords), NULL, NULL },
+    { "CURRENT", PM_INTEGER, VAL(compcurrent), NULL, NULL },
+    { "PREFIX", PM_SCALAR, VAL(compprefix), NULL, NULL },
+    { "SUFFIX", PM_SCALAR, VAL(compsuffix), NULL, NULL },
+    { "IPREFIX", PM_SCALAR, VAL(compiprefix), NULL, NULL },
+    { "ISUFFIX", PM_SCALAR, VAL(compisuffix), NULL, NULL },
+    { NULL, 0, NULL, NULL, NULL },
+
+    { "nmatches", PM_INTEGER, VAL(compnmatches), NULL, NULL },
+    { "matcher", PM_INTEGER, VAL(compmatcher), NULL, NULL },
+    { "matcher_string", PM_SCALAR, VAL(compmatcherstr), NULL, NULL },
+    { "total_matchers", PM_INTEGER, VAL(compmatchertot), NULL, NULL },
+    { "context", PM_SCALAR, VAL(compcontext), NULL, NULL },
+    { "parameter", PM_SCALAR, VAL(compparameter), NULL, NULL },
+    { "redirect", PM_SCALAR, VAL(compredirect), NULL, NULL },
+    { "quote", PM_SCALAR, VAL(compquote), NULL, NULL },
+    { "quoting", PM_SCALAR, VAL(compquoting), NULL, NULL },
+    { "restore", PM_SCALAR, VAL(comprestore), NULL, NULL },
+    { "list", PM_SCALAR, VAL(complist), NULL, NULL },
+    { "force_list", PM_SCALAR, VAL(compforcelist), NULL, NULL },
+    { "insert", PM_SCALAR, VAL(compinsert), NULL, NULL },
+    { "exact", PM_SCALAR, VAL(compexact), NULL, NULL },
+    { "exact_string", PM_SCALAR, VAL(compexactstr), NULL, NULL },
+    { "pattern_match", PM_SCALAR, VAL(comppatmatch), NULL, NULL },
+    { "pattern_insert", PM_SCALAR, VAL(comppatinsert), NULL, NULL },
+    { "unambiguous", PM_SCALAR | PM_READONLY, NULL, NULL, VAL(get_unambig) },
+    { "unambiguous_cursor", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_unambig_curs) },
+    { NULL, 0, NULL, NULL, NULL }
 };
 
 #define COMPSTATENAME "compstate"
@@ -1883,28 +2164,32 @@ addcompparams(struct compparam *cp)
     Param *pp = comppms + (cp - compparams);
 
     for (; cp->name; cp++, pp++) {
-	Param pm = createparam(cp->name, cp->type | PM_SPECIAL|PM_REMOVABLE);
+	Param pm = createparam(cp->name, cp->type | PM_SPECIAL | PM_REMOVABLE);
 	if (!pm)
 	    pm = (Param) paramtab->getnode(paramtab, cp->name);
 	DPUTS(!pm, "param not set in addcompparams");
 
 	*pp = pm;
 	pm->level = locallevel;
-	pm->u.data = cp->var;
-	switch(PM_TYPE(cp->type)) {
-	case PM_SCALAR:
-	    pm->sets.cfn = strvarsetfn;
-	    pm->gets.cfn = strvargetfn;
-	    break;
-	case PM_INTEGER:
-	    pm->sets.ifn = intvarsetfn;
-	    pm->gets.ifn = intvargetfn;
-	    pm->ct = 10;
-	    break;
-	case PM_ARRAY:
-	    pm->sets.afn = arrvarsetfn;
-	    pm->gets.afn = arrvargetfn;
-	    break;
+	if ((pm->u.data = cp->var)) {
+	    switch(PM_TYPE(cp->type)) {
+	    case PM_SCALAR:
+		pm->sets.cfn = strvarsetfn;
+		pm->gets.cfn = strvargetfn;
+		break;
+	    case PM_INTEGER:
+		pm->sets.ifn = intvarsetfn;
+		pm->gets.ifn = intvargetfn;
+		pm->ct = 10;
+		break;
+	    case PM_ARRAY:
+		pm->sets.afn = arrvarsetfn;
+		pm->gets.afn = arrvargetfn;
+		break;
+	    }
+	} else {
+	    pm->sets.cfn = (void (*) _((Param, char *))) cp->set;
+	    pm->gets.cfn = (char *(*) _((Param))) cp->get;
 	}
 	pm->unsetfn = compunsetfn;
     }
@@ -1976,6 +2261,24 @@ set_compstate(Param pm, HashTable ht)
 }
 
 /**/
+static char *
+get_unambig(Param pm)
+{
+    return unambig_dataptr(NULL);
+}
+
+/**/
+static long
+get_unambig_curs(Param pm)
+{
+    int c;
+
+    unambig_dataptr(&c);
+
+    return c;
+}
+
+/**/
 static void
 compunsetfn(Param pm, int exp)
 {
@@ -2017,7 +2320,7 @@ comp_wrapper(List list, FuncWrap w, char *name)
     if (incompfunc != 1)
 	return 1;
     else {
-	char *orest, *opre, *osuf, *oipre, **owords;
+	char *orest, *opre, *osuf, *oipre, *oisuf, **owords;
 	long ocur;
 	int unset = 0, m, sm;
 	Param *pp;
@@ -2034,6 +2337,7 @@ comp_wrapper(List list, FuncWrap w, char *name)
 	opre = dupstring(compprefix);
 	osuf = dupstring(compsuffix);
 	oipre = dupstring(compiprefix);
+	oisuf = dupstring(compisuffix);
 
 	HEAPALLOC {
 	    owords = arrdup(compwords);
@@ -2049,6 +2353,8 @@ comp_wrapper(List list, FuncWrap w, char *name)
 	    compsuffix = ztrdup(osuf);
 	    zsfree(compiprefix);
 	    compiprefix = ztrdup(oipre);
+	    zsfree(compisuffix);
+	    compisuffix = ztrdup(oisuf);
 	    freearray(compwords);
 	    PERMALLOC {
 		compwords = arrdup(owords);
@@ -2068,22 +2374,6 @@ comp_wrapper(List list, FuncWrap w, char *name)
 }
 
 /**/
-static void
-ignore_prefix(int l)
-{
-    char *o, sav = compprefix[l];
-
-    compprefix[l] = '\0';
-    o = compiprefix;
-    compiprefix = tricat(o, compprefix, "");
-    zsfree(o);
-    compprefix[l] = sav;
-    o = compprefix;
-    compprefix = ztrdup(o + l);
-    zsfree(o);
-}
-
-/**/
 static int
 comp_check(void)
 {
@@ -2095,132 +2385,15 @@ comp_check(void)
 }
 
 /**/
-static void
-restrict_range(int b, int e)
-{
-    int i = e - b + 1;
-    char **p = (char **) zcalloc((i + 1) * sizeof(char *)), **q, **pp;
-
-    for (q = p, pp = compwords + b; i; i--, q++, pp++)
-	*q = ztrdup(*pp);
-    freearray(compwords);
-    compwords = p;
-    compcurrent -= b;
-}
-
-/**/
-static int
-cond_prefix(char **a, int id)
-{
-    if (comp_check())
-	return strpfx(cond_str(a, 0), compprefix);
-    return 0;
-}
-
-/**/
-static int
-cond_iprefix(char **a, int id)
-{
-    if (comp_check()) {
-	char *s = cond_str(a, 0);
-
-	if (strpfx(s, compprefix)) {
-	    ignore_prefix(strlen(s));
-	    return 1;
-	}
-    }
-    return 0;
-}
-
-/**/
-static int
-cond_position(char **a, int id)
-{
-    if (comp_check()) {
-	int b = cond_val(a, 0), e = (a[1] ? cond_val(a, 1) : b);
-	int l = arrlen(compwords), t, i = compcurrent - 1;
-
-	if (b > 0)
-	    b--;
-	if (e > 0)
-	    e--;
-	if (b < 0)
-	    b += l;
-	if (e < 0)
-	    e += l;
-	t = (b >= 0 && e >= 0 && i >= b && i <= e && b <= e);
-
-	if (t && a[1]) {
-	    if (b > l)
-		b = l;
-	    if (e > l)
-		e = l;
-	    restrict_range(b, e);
-	}
-	return t;
-    }
-    return 0;
-}
-
-/**/
 static int
-cond_word(char **a, int id)
+cond_psfix(char **a, int id)
 {
     if (comp_check()) {
-	int o = ((id & 2) ? compcurrent : 0) + cond_val(a, 0);
-	int l = arrlen(compwords);
-	char *s;
-
-	if (o < 0)
-	    o += l;
-
-	o--;
-	if (o < 0 || o >= l)
-	    return 0;
-
-	s = compwords[o];
-	return ((id & 1) ? cond_match(a, 1, s) : !strcmp(s, cond_str(a, 1)));
-    }
-    return 0;
-}
-
-/**/
-static int
-cond_strcl(char **a, int id)
-{
-    if (comp_check()) {
-	char *s;
-	int i, ipl;
-
-	if (a[1]) {
-	    s = cond_str(a, 1);
-	    i = cond_val(a, 0);
-	} else {
-	    s = cond_str(a, 0);
-	    i = -1;
-	}
-	if (!getcpatptr) {
-	    zerr("zle not loaded, zle condition not available", NULL, 0);
-	    return 1;
-	}
-	i = getcpatptr(comp_strptr(&ipl, NULL, 1), i, s, id);
-	if (i != -1 && i >= ipl) {
-	    ignore_prefix(i - ipl);
-	    return 1;
-	}
-    }
-    return 0;
-}
-
-/**/
-static int
-cond_words(char **a, int id)
-{
-    if (comp_check()) {
-	int b = cond_val(a, 0), e = (a[1] ? cond_val(a, 1) : -1);
-	int l = arrlen(compwords);
-
-	return (l >= b && l <= e);
+	if (a[1])
+	    return do_comp_vars(id, cond_val(a, 0), cond_str(a, 1),
+				0, NULL, 0);
+	else
+	    return do_comp_vars(id, -1, cond_str(a, 0), 0, NULL, 0);
     }
     return 0;
 }
@@ -2229,100 +2402,23 @@ cond_words(char **a, int id)
 static int
 cond_range(char **a, int id)
 {
-    if (comp_check()) {
-	char *s, **p;
-	int i, l = arrlen(compwords), t = 0, b = 0, e = l - 1;
-	Comp c = NULL;
-
-	i = compcurrent - 1;
-	if (i < 0 || i >= l)
-	    return 0;
-
-	if (id & 1) {
-	    s = a[0];
-	    singsub(&s);
-	    c = parsereg(s);
-	} else
-	    s = cond_str(a, 0);
-
-	for (i--, p = compwords + i; i >= 0; p--, i--) {
-	    if (((id & 1) ? domatch(*p, c, 0) : !strcmp(*p, s))) {
-		b = i + 1;
-		t = 1;
-		break;
-	    }
-	}
-	if (t && (id & 2)) {
-	    int tt = 0;
-
-	    if (id & 1) {
-		s = a[1];
-		singsub(&s);
-		c = parsereg(s);
-	    } else
-		s = cond_str(a, 1);
-
-	    for (i++, p = compwords + i; i < l; p++, i++) {
-		if (((id & 1) ? domatch(*p, c, 0) : !strcmp(*p, s))) {
-		    e = i - 1;
-		    tt = 1;
-		    break;
-		}
-	    }
-	    if (tt && i < compcurrent)
-		t = 0;
-	}
-	if (e < b)
-	    t = 0;
-	if (t)
-	    restrict_range(b, e);
-	return t;
-    }
-    return 0;
-}
-
-/**/
-static int
-cond_nmatches(char **a, int id)
-{
-    if (comp_check())
-	return compnmatches == cond_val(a, 0);
-    return 0;
-}
-
-/**/
-static int
-cond_matcher(char **a, int id)
-{
-    if (comp_check())
-	return compmatcher == cond_val(a, 0);
-    return 0;
+    return do_comp_vars(CVT_RANGEPAT, 0, cond_str(a, 0), 0,
+			(id ? cond_str(a, 1) : NULL), 0);
 }
 
 static struct builtin bintab[] = {
     BUILTIN("compctl", 0, bin_compctl, 0, -1, 0, NULL, NULL),
     BUILTIN("compgen", 0, bin_compgen, 1, -1, 0, NULL, NULL),
     BUILTIN("compadd", 0, bin_compadd, 0, -1, 0, NULL, NULL),
+    BUILTIN("compset", 0, bin_compset, 1, 3, 0, NULL, NULL),
     BUILTIN("compcall", 0, bin_compcall, 0, 0, 0, "TD", NULL),
 };
 
 static struct conddef cotab[] = {
-    CONDDEF("prefix", 0, cond_prefix, 1, 1, 0),
-    CONDDEF("iprefix", 0, cond_iprefix, 1, 1, 0),
-    CONDDEF("position", 0, cond_position, 1, 2, 0),
-    CONDDEF("word", 0, cond_word, 2, 2, 0),
-    CONDDEF("mword", 0, cond_word, 2, 2, 1),
-    CONDDEF("current", 0, cond_word, 2, 2, 2),
-    CONDDEF("mcurrent", 0, cond_word, 2, 2, 3),
-    CONDDEF("string", 0, cond_strcl, 1, 2, 0),
-    CONDDEF("class", 0, cond_strcl, 1, 2, 1),
-    CONDDEF("words", 0, cond_words, 1, 2, 0),
-    CONDDEF("between", 0, cond_range, 2, 2, 2),
-    CONDDEF("mbetween", 0, cond_range, 2, 2, 3),
+    CONDDEF("prefix", 0, cond_psfix, 1, 2, CVT_PREPAT),
+    CONDDEF("suffix", 0, cond_psfix, 1, 2, CVT_SUFPAT),
+    CONDDEF("between", 0, cond_range, 2, 2, 1),
     CONDDEF("after", 0, cond_range, 1, 1, 0),
-    CONDDEF("mafter", 0, cond_range, 1, 1, 1),
-    CONDDEF("nmatches", 0, cond_nmatches, 1, 1, 0),
-    CONDDEF("matcher", 0, cond_matcher, 1, 1, 0),
 };
 
 static struct funcwrap wrapper[] = {
diff --git a/Src/Zle/compctl.mdd b/Src/Zle/compctl.mdd
index 48aabe38a..113eef27e 100644
--- a/Src/Zle/compctl.mdd
+++ b/Src/Zle/compctl.mdd
@@ -1,7 +1,7 @@
 moddeps="comp1"
 
-autobins="compctl complist compadd"
+autobins="compctl complist compadd compset"
 
-autoprefixconds="prefix iprefix position word mword current mcurrent string class words between mbetween after mafter nmatches"
+autoprefixconds="prefix suffix between after"
 
 objects="compctl.o"
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 7ac4a7126..479994249 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -918,6 +918,7 @@ setup_zle(Module m)
     getcpatptr = getcpat;
     makecomplistcallptr = makecomplistcall;
     makecomplistctlptr = makecomplistctl;
+    unambig_dataptr = unambig_data;
 
     /* initialise the thingies */
     init_thingies();
@@ -989,6 +990,7 @@ finish_zle(Module m)
     getcpatptr = NULL;
     makecomplistcallptr = NULL;
     makecomplistctlptr = NULL;
+    unambig_dataptr = NULL;
 
     return 0;
 }
diff --git a/Src/Zle/zle_move.c b/Src/Zle/zle_move.c
index 8ed4c657a..d6e9c4b7d 100644
--- a/Src/Zle/zle_move.c
+++ b/Src/Zle/zle_move.c
@@ -30,7 +30,7 @@
 #include "zle.mdh"
 #include "zle_move.pro"
 
-static vimarkcs[27], vimarkline[27];
+static int vimarkcs[27], vimarkline[27];
 
 /**/
 void
diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c
index 2a35ac416..adde116f2 100644
--- a/Src/Zle/zle_params.c
+++ b/Src/Zle/zle_params.c
@@ -67,7 +67,9 @@ static struct zleparam {
         zleunsetfn, NULL },
     { "keys", PM_ARRAY | PM_READONLY, NULL, FN(get_keys),
         zleunsetfn, NULL },
-    { "NUMERIC", PM_INTEGER | PM_READONLY, FN(set_numeric), FN(get_numeric),
+    { "NUMERIC", PM_INTEGER, FN(set_numeric), FN(get_numeric),
+        zleunsetfn, NULL },
+    { "HISTNO", PM_INTEGER | PM_READONLY, NULL, FN(get_histno),
         zleunsetfn, NULL },
     { NULL, 0, NULL, NULL, NULL, NULL }
 };
@@ -261,3 +263,10 @@ get_numeric(Param pm)
 {
     return zmult;
 }
+
+/**/
+static long
+get_histno(Param pm)
+{
+    return histline;
+}
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index 28781e7f0..b879669ac 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -492,7 +492,7 @@ bin_zle_complete(char *name, char **args, char *ops, char func)
     t = rthingy(args[1]);
     cw = t->widget;
     unrefthingy(t);
-    if (!(cw->flags & ZLE_ISCOMP)) {
+    if (!cw || !(cw->flags & ZLE_ISCOMP)) {
 	zerrnam(name, "invalid widget `%s'", args[1], 0);
 	return 1;
     }
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index c303fd97d..4ae7495c3 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -143,7 +143,8 @@ static Comp patcomp, filecomp;
  * lppre/lpsuf -- the path prefix/suffix, unexpanded                       *
  * fpre/fsuf   -- prefix/suffix of the pathname component the cursor is in *
  * prpre       -- ppre in expanded form usable for opendir                 *
- * ipre,ripre  -- the ignored prefix (quotes and unquoted)                 *
+ * ipre,ripre  -- the ignored prefix (quoted and unquoted)                 *
+ * isuf        -- the ignored suffix                                       *
  *                                                                         *
  * The integer variables hold the lengths of lpre, lsuf, rpre, rsuf,       *
  * fpre, fsuf, lppre, and lpsuf.  noreal is non-zero if we have rpre/rsuf. */
@@ -153,6 +154,7 @@ static char *rpre, *rsuf;
 static char *ppre, *psuf, *lppre, *lpsuf, *prpre;
 static char *fpre, *fsuf;
 static char *ipre, *ripre;
+static char *isuf;
 static int lpl, lsl, rpl, rsl, fpl, fsl, lppl, lpsl;
 static int noreal;
 
@@ -177,6 +179,10 @@ static Cmgroup mgroup;
 
 static int mnum;
 
+/* The match counter when unambig_data() was called. */
+
+static int unambig_mnum;
+
 /* Match flags for all matches in this group. */
 
 static int mflags;
@@ -215,6 +221,39 @@ static Cmlist bmatchers;
 
 static LinkList matchers;
 
+/* Information about what to put on the line as the unambiguous string.
+ * The code always keeps lists of these structs up to date while
+ * matches are added (in the aminfo structs below).
+ * The lists have two levels: in the first one we have one struct per
+ * word-part, where parts are separated by the anchors of `*' patterns.
+ * These structs have pointers (in the prefix and suffix fields) to
+ * lists of cline structs describing the strings before or after the
+ * the anchor. */
+
+typedef struct cline *Cline;
+typedef struct clsub Clsub;
+
+struct cline {
+    Cline next;
+    int flags;
+    char *line;
+    int llen;
+    char *word;
+    int wlen;
+    char *orig;
+    int olen;
+    int slen;
+    Cline prefix, suffix;
+};
+
+#define CLF_MISS  1
+#define CLF_DIFF  2
+#define CLF_SUF   4
+#define CLF_MID   8
+#define CLF_NEW  16
+#define CLF_LINE 32
+#define CLF_JOIN 64
+
 /* A heap of free Cline structures. */
 
 static Cline freecl;
@@ -225,19 +264,11 @@ static Cline freecl;
 typedef struct aminfo *Aminfo;
 
 struct aminfo {
-    int cpl, csl, icpl, icsl;	/* common prefix/suffix lengths           */
-    int minlen;			/* minimum match length                   */
-    int suflen;			/* minimum suffix length                  */
     Cmatch firstm;		/* the first match                        */
-    char *pprefix;		/* common part of the -P prefixes         */
-    char *aprefix;		/* common line prefix                     */
-    int noipre;			/* if the was no ignored prefix           */
-    char *iprefix;		/* common ignored prefix                  */
-    char *iaprefix;		/* like aprefix, without ignored prefixes */
     int exact;			/* if there was an exact match            */
     Cmatch exactm;		/* the exact match (if any)               */
-    Cline linecl, ilinecl;	/* what to put on the line as a Cline     */
     int count;			/* number of matches                      */
+    Cline line;			/* unambiguous line string                */
 };
 
 static Aminfo ainfo, fainfo;
@@ -489,7 +520,7 @@ acceptandmenucomplete(void)
 /* These are flags saying if we are completing in the command *
  * position, in a redirection, or in a parameter expansion.   */
 
-static int lincmd, linredir, ispar;
+static int lincmd, linredir, ispar, linwhat;
 
 /* The string for the redirection operator. */
 
@@ -516,7 +547,7 @@ int instring, inbackt;
 /* Convenience macro for calling bslashquote() (formerly quotename()). *
  * This uses the instring variable above.                              */
 
-#define quotename(s, e, te, pl) bslashquote(s, e, te, pl, instring)
+#define quotename(s, e) bslashquote(s, e, instring)
 
 /* Check if the given string is the name of a parameter and if this *
  * parameter is one worth expanding.                                */
@@ -658,12 +689,12 @@ check_param(char *s, int set)
 		    mflags |= CMF_PARBR;
 
 		/* Get the prefix (anything up to the character before the name). */
-		lpsuf = dupstring(quotename(e, NULL, NULL, NULL));
+		lpsuf = dupstring(quotename(e, NULL));
 		*e = '\0';
 		lpsl = strlen(lpsuf);
 		ripre = dupstring(s);
 		ripre[b - s] = '\0';
-		ipre = dupstring(quotename(ripre, NULL, NULL, NULL));
+		ipre = dupstring(quotename(ripre, NULL));
 		untokenize(ipre);
 	    }
 	    /* And adjust wb, we, and offs again. */
@@ -1008,7 +1039,7 @@ static char *
 dupstrspace(const char *str)
 {
     int len = strlen((char *)str);
-    char *t = (char *)ncalloc(len + 2);
+    char *t = (char *) ncalloc(len + 2);
     strcpy(t, str);
     strcpy(t+len, " ");
     return t;
@@ -1572,7 +1603,7 @@ doexpansion(char *s, int lst, int olst, int explincmd)
 	foredel(we - wb);
 	while ((ss = (char *)ugetnode(vl))) {
 	    untokenize(ss);
-	    ss = quotename(ss, NULL, NULL, NULL);
+	    ss = quotename(ss, NULL);
 	    inststr(ss);
 #if 0
 	    if (nonempty(vl)) {
@@ -1604,99 +1635,6 @@ gotword(void)
     }
 }
 
-/* This adds a string to the currently built match. The last argument  *
- * is non zero if we are building the suffix, where we have to prepend *
- * the given string. */
-
-static char *
-addtoword(char **rwp, int *rwlenp, char *nw,
-	  Cmatcher m, char *l, char *w, int wl, int prep)
-{
-    int al, rwlen = *rwlenp, nl;
-    char *as, *rw = *rwp;
-
-    /* Get the string and length to insert: either from the line 
-     * or from the match. */
-    if (m && (m->flags & CMF_LINE)) {
-	al = m->llen;
-	as = l;
-    } else {
-	al = wl;
-	as = w;
-    }
-    /* Allocate some more space if needed. */
-    if (!rwlen || (nl = al + (nw - rw)) >= rwlen) {
-	char *np;
-
-	if (!rwlen)
-	    nl = al + 20;
-
-	np = (char *) zalloc(nl + 1);
-
-	*rwlenp = nl;
-	if (rwlen) {
-	    memcpy(np, rw, rwlen);
-	    nw += np - rw;
-	    zfree(rw, rwlen);
-	} else
-	    nw = np;
-	*rwp = rw = np;
-	rwlen = nl;
-    }
-    /* Copy it in the buffer. */
-    if (prep) {
-	memmove(rw + al, rw, rwlen - al);
-	memcpy(rw, as, al);
-    } else
-	memcpy(nw, as, al);
-
-    return nw + al;
-}
-
-/* This returns a new Cline structure. */
-
-static Cline
-getcline(char *l, int ll, char *w, int wl, int fl)
-{
-    Cline r;
-
-    /* Preverably take it from the buffer list (freecl), if there
-     * is none, allocate a new one. */
-    if ((r = freecl))
-	freecl = r->next;
-    else
-	r = (Cline) zhalloc(sizeof(*r));
-
-    r->next = NULL;
-    r->line = l;
-    r->llen = ll;
-    r->word = w;
-    r->wlen = wl;
-    r->flags = fl;
-    r->prefix = r->suffix = NULL;
-
-    return r;
-}
-
-/* This adds a Cline structure with the given parameters to the list we
- * are building during matching. */
-
-static void
-addtocline(Cline *retp, Cline *lrp,
-	   char *l, int ll, char *w, int wl, Cmatcher m, int fl)
-{
-    Cline ln = getcline(l, ll, w, wl, fl);
-
-    if (m && (m->flags & CMF_LINE))
-	ln->word = NULL;
-    if (*retp)
-	(*lrp)->next = ln;
-    else
-	*retp = ln;
-
-    *lrp = ln;
-}
-
 /* This compares two cpattern lists and returns non-zero if they are
  * equal. */
 
@@ -1774,18 +1712,119 @@ update_bmatchers(void)
     }
 }
 
-/* When building up cline lists that are to be inserted at the end of the
- * string or on the left hand side in the middle, we do this separately for
- * multiple runs of characters separated by the anchors of `*' match patterns.
- * This function builds such a list from the given string. */
+/* This returns a new Cline structure. */
+
+static Cline
+get_cline(char *l, int ll, char *w, int wl, char *o, int ol, int fl)
+{
+    Cline r;
+
+    /* Preverably take it from the buffer list (freecl), if there
+     * is none, allocate a new one. */
+
+    if ((r = freecl))
+	freecl = r->next;
+    else
+	r = (Cline) zhalloc(sizeof(*r));
+
+    r->next = NULL;
+    r->line = l; r->llen = ll;
+    r->word = w; r->wlen = wl;
+    r->orig = o; r->olen = ol;
+    r->slen = 0;
+    r->flags = fl;
+    r->prefix = r->suffix = NULL;
+    return r;
+}
+
+/* This frees a cline list. */
+
+static void
+free_cline(Cline l)
+{
+    Cline n;
+
+    while (l) {
+	n = l->next;
+	l->next = freecl;
+	freecl = l;
+	free_cline(l->prefix);
+	free_cline(l->suffix);
+	l = n;
+    }
+}
+
+/* This reverts the order of the elements of the given cline list and
+ * returns a pointer to the new head. */
 
 static Cline
-end_list(int len, char *str)
+revert_cline(Cline p)
 {
-    Cline ret = NULL, *q = &ret;
+    Cline r = NULL, n;
+
+    while (p) {
+	n = p->next;
+	p->next = r;
+	r = p;
+	p = n;
+    }
+    return r;
+}
+
+/* Check if the given pattern matches the given string.             *
+ * `in' and `out' are used for {...} classes. In `out' we store the *
+ * character number that was matched. In the word pattern this is   *
+ * given in `in' so that we can easily test if we found the         *
+ * corresponding character. */
+
+/**/
+static int
+pattern_match(Cpattern p, char *s, unsigned char *in, unsigned char *out)
+{
+    unsigned char c;
+
+    while (p) {
+	c = *((unsigned char *) s);
+
+	if (out)
+	    *out = 0;
+
+	if (p->equiv) {
+	    if (in) {
+		c = p->tab[c];
+		if ((*in && *in != c) || (!*in && !c))
+		    return 0;
+	    } else if (out) {
+		if (!(*out = p->tab[c]))
+		    return 0;
+	    } else if (!p->tab[c])
+		return 0;
+
+	    if (in && *in)
+		in++;
+	    if (out)
+		out++;
+	} else if (!p->tab[c])
+	    return 0;
+
+	s++;
+	p = p->next;
+    }
+    return 1;
+}
+
+/* This splits the given string into a list of cline structs, separated
+ * at those places where one of the anchors of an `*' pattern was found.
+ * plen gives the number of characters on the line that matched this
+ * string. In lp we return a pointer to the last cline struct we build. */
+
+static Cline
+bld_parts(char *str, int len, int plen, Cline *lp)
+{
+    Cline ret = NULL, *q = &ret, n;
     Cmlist ms;
     Cmatcher mp;
-    int t;
+    int t, op = plen;
     char *p = str;
 
     while (len) {
@@ -1794,250 +1833,678 @@ end_list(int len, char *str)
 	    if (mp->flags == CMF_RIGHT && mp->wlen == -1 &&
 		!mp->llen && len >= mp->ralen && mp->ralen &&
 		pattern_match(mp->right, str, NULL, NULL)) {
-		/* This is one of those patterns, so add a cline struct.
-		 * We store the anchor string in the line and the contents
-		 * (i.e. the strings between the anchors) in the word field. */
-		*q = getcline(str, mp->ralen, p, str - p, 0);
-		q = &((*q)->next);
-		str += mp->ralen;
-		len -= mp->ralen;
+		int olen = str - p, llen;
+
+		/* We found an anchor, create a new cline. The NEW flag
+		 * is set if the characters before the anchor were not
+		 * on the line. */
+		*q = n = get_cline(NULL, mp->ralen, str, mp->ralen, NULL, 0,
+				   ((plen < 0) ? CLF_NEW : 0));
+
+		/* If there were any characters before the anchor, add
+		 * them as a cline struct. */
+
+		if (p != str) {
+		    llen = (op < 0 ? 0 : op);
+
+		    if (llen > olen)
+			llen = olen;
+		    n->prefix = get_cline(NULL, llen, p, olen, NULL, 0, 0);
+		}
+		q = &(n->next);
+		str += mp->ralen; len -= mp->ralen;
+		plen -= mp->ralen;
+		op -= olen;
 		p = str;
 		t = 1;
 	    }
 	}
 	if (!t) {
-	    str++;
-	    len--;
+	    /* No anchor was found here, skip. */
+	    str++; len--;
+	    plen--;
 	}
     }
     /* This is the cline struct for the remaining string at the end. */
+
+    *q = n = get_cline(NULL, 0, NULL, 0, NULL, 0, (plen < 0 ? CLF_NEW : 0));
     if (p != str) {
-	*q = getcline("", 0, p, str - p, 0);
-	q = &((*q)->next);
+	int olen = str - p, llen = (op < 0 ? 0 : op);
+
+	if (llen > olen)
+	    llen = olen;
+	n->prefix = get_cline(NULL, llen, p, olen, NULL, 0, 0);
     }
-    *q = NULL;
+    n->next = NULL;
+
+    if (lp)
+	*lp = n;
 
     return ret;
 }
 
-/* This builds a string that may be put on the line that fully matches the
- * given strings. The return value is NULL if no such string could be built
- * or that string in local static memory, dup it.
- * All this is a lot like the procedure in bld_new_pfx(), only slightly
- * simpler, see that function for more comments. */
+/* Global variables used during matching: a char-buffer for the string to
+ * use for the match, and two cline lists for the two levels we use. */
 
-static char *
-join_strs(int la, char *sa, int lb, char *sb)
+static char *matchbuf = NULL;
+static int matchbuflen = 0, matchbufadded;
+
+static Cline matchparts, matchlastpart;
+static Cline matchsubs, matchlastsub;
+
+/* This initialises the variables above. */
+
+static void
+start_match(void)
 {
-    static unsigned char *ea = NULL;
-    static int ealen = 0;
-    static char *line = NULL;
-    static int llen = 0;
-    static char *rs = NULL;
-    static int rl = 0;
+    if (matchbuf)
+	*matchbuf = '\0';
+    matchbufadded = 0;
+    matchparts = matchlastpart = matchsubs = matchlastsub = NULL;
+}
+
+/* This aborts a matching, freeing the cline lists build. */
+
+static void
+abort_match(void)
+{
+    free_cline(matchparts);
+    free_cline(matchsubs);
+}
+
+/* This adds a new string in the static char buffer. The arguments are
+ * the matcher used (if any), the strings from the line and the word
+ * and the length of the string from the word. The last argument is
+ * non-zero if we are matching a suffix (where the given string has to 
+ * be prepended to the contents of the buffer). */
 
+static void
+add_match_str(Cmatcher m, char *l, char *w, int wl, int sfx)
+{
+    /* Get the string and length to insert: either from the line 
+     * or from the match. */
+    if (m && (m->flags & CMF_LINE)) {
+	wl = m->llen; w = l;
+    }
+    if (wl) {
+	/* Probably resie the buffer. */
+	if (matchbuflen - matchbufadded <= wl) {
+	    int blen = matchbuflen + wl + 20;
+	    char *buf;
+
+	    buf = (char *) zalloc(blen);
+	    memcpy(buf, matchbuf, matchbuflen);
+	    zfree(matchbuf, matchbuflen);
+	    matchbuf = buf;
+	    matchbuflen = blen;
+	}
+	/* Insert the string. */
+	if (sfx) {
+	    memmove(matchbuf + wl, matchbuf, matchbufadded + 1);
+	    memcpy(matchbuf, w, wl);
+	} else
+	    memcpy(matchbuf + matchbufadded, w, wl);
+	matchbufadded += wl;
+	matchbuf[matchbufadded] = '\0';
+    }
+}
+
+/* This adds a cline for a word-part during matching. Arguments are the
+ * matcher used, pointers to the line and word strings for the anchor,
+ * a pointer to the original line string for the whole part, the string
+ * before (or after) the anchor that has not yet been added, the length
+ * of the line-string for that, and a flag saying if we are matching a 
+ * suffix. */
+
+static void
+add_match_part(Cmatcher m, char *l, char *w, int wl,
+	       char *o, int ol, char *s, int sl, int osl, int sfx)
+{
+    Cline p, lp;
+
+    /* If the anchors are equal, we keep only one. */
+
+    if (!strncmp(l, w, wl))
+	l = NULL;
+
+    /* Split the new part into parts and turn the last one into a `suffix'
+     * if we have a left anchor. */
+
+    p = bld_parts(s, sl, osl, &lp);
+
+    p->flags &= ~CLF_NEW;
+    if (m && (m->flags & CMF_LEFT)) {
+	lp->flags |= CLF_SUF;
+	lp->suffix = lp->prefix;
+	lp->prefix = NULL;
+    }
+    /* cline lists for suffixes are sorted from back to front, so we have
+     * to revert the list we got. */
+    if (sfx)
+	p = revert_cline(lp = p);
+    /* Now add the sub-clines we already had. */
+    if (matchsubs) {
+	matchlastsub->next = p->prefix;
+	p->prefix = matchsubs;
+	matchsubs = matchlastsub = NULL;
+    }
+    /* Store the arguments in the last part-cline. */
+    lp->line = l; lp->llen = wl;
+    lp->word = w; lp->wlen = wl;
+    lp->orig = o; lp->olen = ol;
+    lp->flags &= ~CLF_NEW;
+
+    /* Finally, put the new parts on the list. */
+    if (matchlastpart)
+	matchlastpart->next = p;
+    else
+	matchparts = p;
+    matchlastpart = lp;
+}
+
+/* This adds a new sub-cline. Arguments are the matcher and the strings from
+ * the line and the word. */
+
+static void
+add_match_sub(Cmatcher m, char *l, int ll, char *w, int wl)
+{
+    int flags;
+    Cline n;
+
+    /* Check if we are interested only in the string from the line. */
+    if (m && (m->flags & CMF_LINE)) {
+	w = NULL; wl = 0;
+	flags = CLF_LINE;
+    } else
+	flags = 0;
+
+    /* And add the cline. */
+    if (wl || ll) {
+	n = get_cline(l, ll, w, wl, NULL, 0, flags);
+	if (matchlastsub)
+	    matchlastsub->next = n;
+	else
+	    matchsubs = n;
+	matchlastsub = n;
+    }
+}
+
+/* This tests if the string from the line l matches the word w. In bp
+ * the offset for the brace is returned, in rwlp the length of the
+ * matched prefix or suffix, not including the stuff before or after
+ * the last anchor is given. When sfx is non-zero matching is done from
+ * the ends of the strings backward, if test is zero, the global variables
+ * above are used to build the string for the match and the cline. */
+
+static int
+match_str(char *l, char *w, int *bp, int *rwlp, int sfx, int test)
+{
+    int ll = strlen(l), lw = strlen(w), oll = ll, olw = lw;
+    int il = 0, iw = 0, t, ind, add, bc;
+    VARARR(unsigned char, ea, ll + 1);
+    char *ow;
     Cmlist ms;
-    Cmatcher mp;
-    int t, bl, rr = rl;
-    char *rp = rs;
+    Cmatcher mp, lm = NULL;
+
+    if (!test)
+	start_match();
 
-    /* This is a buffer for the characters to use for pairs of correspondence
-     * classes. */
-    if (la + 1 > ealen || lb + 1 > ealen) {
-	if (ealen)
-	    zfree(ea, ealen);
-	ea = (unsigned char *) zalloc(ealen = (la > lb ? la : lb) + 20);
+    /* Adjust the pointers and get the values for subscripting and
+     * incrementing. */
+
+    if (sfx) {
+	l += ll; w += lw;
+	ind = -1; add = -1; bc = brsl;
+    } else {
+	ind = 0; add = 1; bc = brpl;
     }
-    while (la && lb) {
-	if (*sa != *sb) {
-	    for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
-		mp = ms->matcher;
-		if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
-		    mp->wlen <= la && mp->wlen <= lb) {
-		    if (pattern_match(mp->word, sa, NULL, ea)) {
-			if (mp->llen + 1 > llen) {
-			    if (llen)
-				zfree(line, llen);
-			    line = (char *) zalloc(llen = mp->llen + 20);
-			}
-			if ((bl = bld_line_pfx(mp->line, line, line,
-					       lb, sb, ea))) {
-			    line[mp->llen] = '\0';
-			    if (rr <= mp->llen) {
-				char *or = rs;
+    /* ow will always point to the beginning (or end) of that sub-string
+     * in w that wasn't put in the match-variables yet. */
 
-				rs = realloc(rs, (rl += 20));
-				rr += 20;
-				rp += rs - or;
-			    }
-			    memcpy(rp, line, mp->llen);
-			    rp += mp->llen;
-			    rr -= mp->llen;
-			    sa += mp->wlen;
-			    sb += bl;
-			    la -= mp->wlen;
-			    lb -= bl;
-			    t = 1;
+    ow = w;
+
+    /* If the brace is at the beginning, we have to treat it now. */
+
+    if (!test && !bc && bp) {
+	*bp = 0;
+	bp = NULL;
+    }
+    while (ll && lw) {
+	/* First try the matchers. */
+	for (mp = NULL, ms = mstack; !mp && ms; ms = ms->next) {
+	    for (mp = ms->matcher; mp; mp = mp->next) {
+		t = 1;
+		if (lm == mp ||
+		    ((oll == ll || olw == lw) && test && mp->wlen < 0))
+		    /* If we were called recursively, don't use `*' patterns
+		     * at the beginning (avoiding infinite recursion). */
+		    continue;
+
+		if (mp->wlen < 0) {
+		    int both, loff, aoff, llen, alen, zoff, moff, ct, ict;
+		    char *tp, savl = '\0', savw;
+		    Cpattern ap;
+
+		    /* This is for `*' patterns, first initialise some
+		     * local variables. */
+		    llen = mp->llen;
+		    alen = (mp->flags & CMF_LEFT ? mp->lalen : mp->ralen);
+
+		    /* Give up if we don't have enough characters for the
+		     * line-string and the anchor. */
+		    if (ll < llen + alen || lw < alen)
+			continue;
+
+		    if (mp->flags & CMF_LEFT) {
+			ap = mp->left; zoff = 0; moff = alen;
+			if (sfx) {
+			    both = 0; loff = -llen; aoff = -(llen + alen);
+			} else {
+			    both = 1; loff = alen; aoff = 0;
 			}
-		    } else if (pattern_match(mp->word, sb, NULL, ea)) {
-			if (mp->llen + 1 > llen) {
-			    if (llen)
-				zfree(line, llen);
-			    line = (char *) zalloc(llen = mp->llen + 20);
+		    } else {
+			ap = mp->right; zoff = alen; moff = 0;
+			if (sfx) {
+			    both = 1; loff = -(llen + alen); aoff = -alen;
+			} else {
+			    both = 0; loff = 0; aoff = llen;
 			}
-			if ((bl = bld_line_pfx(mp->line, line, line,
-					       la, sa, ea))) {
-			    line[mp->llen] = '\0';
-			    if (rr <= mp->llen) {
-				char *or = rs;
+		    }
+		    /* Try to match the line pattern and the anchor. */
+		    if (!pattern_match(mp->line, l + loff, NULL, NULL))
+			continue;
+		    if (ap) {
+			if (!pattern_match(ap, l + aoff, NULL, NULL) ||
+			    (both && !pattern_match(ap, w + aoff, NULL, NULL)))
+			    continue;
+		    } else if (!both || il || iw)
+			continue;
 
-				rs = realloc(rs, (rl += 20));
-				rr += 20;
-				rp += rs - or;
+		    /* Fine, now we call ourselves recursively to find the
+		     * string matched by the `*'. */
+		    if (sfx) {
+			savl = l[-(llen + zoff)];
+			l[-(llen + zoff)] = '\0';
+		    }
+		    for (t = 0, tp = w, ct = 0, ict = lw - alen;
+			 ict;
+			 tp += add, ct++, ict--) {
+			if (both ||
+			    pattern_match(ap, tp - moff, NULL, NULL)) {
+			    if (sfx) {
+				savw = tp[-zoff];
+				tp[-zoff] = '\0';
+				t = match_str(l - ll, w - lw,
+					      NULL, NULL, 1, 1);
+				tp[-zoff] = savw;
+			    } else
+				t = match_str(l + llen + moff, tp + moff,
+					      NULL, NULL, 0, 1);
+			    if (t || !both)
+				break;
+			}
+		    }
+		    ict = ct;
+		    if (sfx)
+			l[-(llen + zoff)] = savl;
+
+		    /* Have we found a position in w where the rest of l
+		     * matches? */
+		    if (!t)
+			continue;
+
+		    /* Yes, add the strings and clines if this is a 
+		     * top-level call. */
+		    if (!test) {
+			char *op, *lp, *map, *wap, *wmp;
+			int ol;
+
+			if (sfx) {
+			    op = w; ol = ow - w; lp = l - (llen + alen);
+			    map = tp - alen;
+			    if (mp->flags & CMF_LEFT) {
+				wap = tp - alen; wmp = tp;
+			    } else {
+				wap = w - alen; wmp = tp - alen;
 			    }
-			    memcpy(rp, line, mp->llen);
-			    rp += mp->llen;
-			    rr -= mp->llen;
-			    sa += bl;
-			    sb += mp->wlen;
-			    la -= bl;
-			    lb -= mp->wlen;
-			    t = 1;
+			} else {
+			    op = ow; ol = w - ow; lp = l;
+			    map = ow;
+			    if (mp->flags & CMF_LEFT) {
+				wap = w; wmp = w + alen;
+			    } else {
+				wap = tp; wmp = ow;
+			    }
+			}
+			/* If the matcher says that we are only interested
+			 * in the line pattern, we just add that and the
+			 * anchor and the string not added yet. Otherwise
+			 * we add a new part. */
+			if (mp->flags & CMF_LINE) {
+			    add_match_str(NULL, NULL, op, ol, sfx);
+			    add_match_str(NULL, NULL, lp, llen + alen, sfx);
+			    add_match_sub(NULL, NULL, ol, op, ol);
+			    add_match_sub(NULL, NULL, llen + alen,
+					  lp, llen + alen);
+			} else {
+			    add_match_str(NULL, NULL,
+					  map, ct + ol + alen, sfx);
+			    if (both) {
+				add_match_sub(NULL, NULL, ol, op, ol);
+				ol = -1;
+			    } else
+				ct += ol;
+			    add_match_part(mp, l + aoff, wap, alen,
+					   l + loff, llen, wmp, ct, ol, sfx);
 			}
 		    }
+		    /* Now skip over the matched portion and the anchor. */
+		    llen += alen; alen += ict;
+		    if (sfx) {
+			l -= llen; w -= alen;
+		    } else {
+			l += llen; w += alen;
+		    }
+		    ll -= llen; il += llen;
+		    lw -= alen; iw += alen;
+		    bc -= llen;
+
+		    if (!test && bc <= 0 && bp) {
+			*bp = matchbufadded + bc;
+			bp = NULL;
+		    }
+		    ow = w;
+
+		    if (!ict)
+			lm = mp;
+		    else
+			lm = NULL;
+		    break;
+		} else if (ll >= mp->llen && lw >= mp->wlen) {
+		    /* Non-`*'-pattern. */
+		    char *tl, *tw;
+		    int tll, tlw, til, tiw;
+
+		    /* We do this only if the line- and word-substrings
+		     * are not equal. */
+		    if (!(mp->flags & (CMF_LEFT | CMF_RIGHT)) &&
+			mp->llen == mp->wlen &&
+			!(sfx ? strncmp(l - mp->llen, w - mp->wlen, mp->llen) :
+			  strncmp(l, w, mp->llen)))
+			continue;
+
+		    /* Using local variables to make the following
+		     * independent of whether we match a prefix or a
+		     * suffix. */
+		    if (sfx) {
+			tl = l - mp->llen; tw = w - mp->wlen;
+			til = ll - mp->llen; tiw = lw - mp->wlen;
+			tll = il + mp->llen; tlw = iw + mp->wlen;
+		    } else {
+			tl = l; tw = w;
+			til = il; tiw = iw;
+			tll = ll; tlw = lw;
+		    }
+		    if (mp->flags & CMF_LEFT) {
+			/* Try to match the left anchor, if any. */
+			if (til < mp->lalen || tiw < mp->lalen)
+			    continue;
+			else if (mp->left)
+			    t = pattern_match(mp->left, tl - mp->lalen,
+					      NULL, NULL) &&
+				pattern_match(mp->left, tw - mp->lalen,
+					      NULL, NULL);
+			else
+			    t = (!sfx && !il && !iw);
+		    }
+		    if (mp->flags & CMF_RIGHT) {
+			/* Try to match the right anchor, if any. */
+			if (tll < mp->llen + mp->ralen ||
+			    tlw < mp->wlen + mp->ralen)
+			    continue;
+			else if (mp->left)
+			    t = pattern_match(mp->right,
+					      tl + mp->llen - mp->ralen,
+					      NULL, NULL) &&
+				pattern_match(mp->right,
+					      tw + mp->wlen - mp->ralen,
+					      NULL, NULL);
+			else
+			    t = (sfx && !il && !iw);
+		    }
+		    /* Now try to match the line and word patterns. */
+		    if (!t ||
+			!pattern_match(mp->line, tl, NULL, ea) ||
+			!pattern_match(mp->word, tw, ea, NULL))
+			continue;
+
+		    /* Probably add the matched strings. */
+		    if (!test) {
+			if (sfx)
+			    add_match_str(NULL, NULL, w, ow - w, 0);
+			else
+			    add_match_str(NULL, NULL, ow, w - ow, 0);
+			add_match_str(mp, tl, tw, mp->wlen, 0);
+			if (sfx)
+			    add_match_sub(NULL, NULL, 0, w, ow - w);
+			else
+			    add_match_sub(NULL, NULL, 0, ow, w - ow);
+
+			add_match_sub(mp, tl, mp->llen, tw, mp->wlen);
+		    }
+		    if (sfx) {
+			l = tl;	w = tw;
+		    } else {
+			l += mp->llen; w += mp->wlen;
+		    }
+		    il += mp->llen; iw += mp->wlen;
+		    ll -= mp->llen; lw -= mp->wlen;
+		    bc -= mp->llen;
+
+		    if (!test && bc <= 0 && bp) {
+			*bp = matchbufadded + bc;
+			bp = NULL;
+		    }
+		    ow = w;
+		    lm = NULL;
+		    break;
 		}
 	    }
-	    if (!t)
-		break;
-	} else {
-	    if (rr <= 1) {
-		char *or = rs;
+	}
+	if (mp)
+	    continue;
 
-		rs = realloc(rs, (rl += 20));
-		rr += 20;
-		rp += rs - or;
+	if (l[ind] == w[ind]) {
+	    /* No matcher could be used, but the strings have the same
+	     * character here, skip over it. */
+	    l += add; w += add;
+	    il++; iw++;
+	    ll--; lw--;
+	    bc--;
+	    if (!test && bc <= 0 && bp) {
+		*bp = matchbufadded + (sfx ? (ow - w) : (w - ow));
+		bp = NULL;
 	    }
-	    *rp++ = *sa;
-	    rr--;
-	    sa++;
-	    sb++;
-	    la--;
-	    lb--;
+	    lm = NULL;
+	} else {
+	    /* No matcher and different characters: l does not match w. */
+	    abort_match();
+
+	    return (test ? 0 : -1);
 	}
     }
-    if (la || lb)
-	return NULL;
+    /* If this is a recursive call, we just return if l matched w or not. */
+    if (test)
+	return !ll;
 
-    *rp = '\0';
+    /* In top-level calls, if ll is non-zero (unmatched portion in l),
+     * we have to free the collected clines. */
+    if (ll) {
+	abort_match();
 
-    return rs;
+	return -1;
+    }
+    if (rwlp)
+	*rwlp = iw - (sfx ? ow - w : w - ow);
+
+    /* If we matched a suffix, the anchors stored in the top-clines
+     * will be in the wrong clines: shifted by one. Adjust this. */
+    if (sfx && matchparts) {
+	Cline t, tn, s;
+
+	if (matchparts->prefix || matchparts->suffix) {
+	    t = get_cline(NULL, 0, NULL, 0, NULL, 0, 0);
+	    t->next = matchparts;
+	    if (matchparts->prefix)
+		t->prefix = (Cline) 1;
+	    else
+		t->suffix = (Cline) 1;
+	    matchparts = t;
+	}
+	for (t = matchparts; (tn = t->next); t = tn) {
+	    s = (tn->prefix ? tn->prefix : tn->suffix);
+	    if (t->prefix)
+		t->prefix = s;
+	    else
+		t->suffix = s;
+	}
+	t->prefix = t->suffix = NULL;
+    }
+    /* Finally, return the number of matched characters. */
+
+    return iw;
 }
 
-/* This gets two cline lists with separated runs and joins the corresponding
- * elements in these lists. In olp and nlp we return the lengths of the matched
- * portions in o and n respectively. As always the list in o is the one that
- * contains the joined result after this function. */
+/* Check if the word w is matched by the strings in pfx and sfx (the prefix
+ * and the suffix from the line) or the pattern cp. In clp a cline list for
+ * w is returned.
+ * qu is non-zero if the words has to be quoted before processed any further.
+ * bpl and bsl are used to report the positions where the brace-strings in
+ * the prefix and the suffix have to be re-inserted if this match is inserted
+ * in the line.
+ * The return value is the string to use as a completion or NULL if the prefix
+ * and the suffix don't match the word w. */
 
-static Cline
-join_ends(Cline o, Cline n, int *olp, int *nlp)
+static char *
+comp_match(char *pfx, char *sfx, char *w, Comp cp,
+	   Cline *clp, int qu, int *bpl, int *bsl, int *exact)
 {
-    Cline ret = o;
-    int mol, mnl, smol = 0, smnl = 0;
-    char *j;
+    char *r = NULL;
 
-    while (o && n) {
-	if (o->llen == n->llen && !strncmp(o->line, n->line, o->llen)) {
-	    /* The anchors are the same, so join the contents. */
-	    bld_pfx(o, n, &mol, &mnl);
-	    smol += mol + o->llen;
-	    smnl += mnl + n->llen;
-	} else if (!(o->flags & CLF_JOIN) &&
-		   (j = join_strs(o->llen, o->line, n->llen, n->line))) {
-	    /* We could build a string matching both anchors, so use that
-	     * and mark the cline so that we don't try to join it again. */
-	    o->flags |= CLF_JOIN;
-	    o->llen = strlen(j);
-	    o->line = dupstring(j);
-	    bld_pfx(o, n, &mol, &mnl);
-	    smol += mol + o->llen;
-	    smnl += mnl + n->llen;
-	} else {
-	    /* Different anchors, see if we can find matching anchors
-	     * further down the lists. */
-	    Cline to, tn = NULL;
-	    int t = 0;
-
-	    /* But first build the common prefix. */
-	    bld_pfx(o, n, &mol, &mnl);
-	    smol += mol;
-	    smnl += mnl;
-
-	    for (to = o; to && !t; to = to->next) {
-		for (tn = n; tn && !t; tn = tn->next) {
-		    if ((t = ((to->llen == tn->llen &&
-			       !strncmp(to->line, tn->line, to->llen)) ||
-			      (!(to->flags & CLF_JOIN) &&
-			       join_strs(to->llen, to->line,
-					 tn->llen, tn->line)))))
-			break;
-		}
-		if (t)
-		    break;
+    if (cp) {
+	/* We have a globcomplete-like pattern, just use that. */
+	int wl;
+
+	r = w;
+	if (!domatch(r, cp, 0))
+	    return NULL;
+    
+	r = (qu ? quotename(r, NULL) : dupstring(r));
+
+	/* We still break it into parts here, trying to build a sensible
+	 * cline list for these matches, too. */
+	wl = strlen(w);
+	*clp = bld_parts(w, wl, wl, NULL);
+	*exact = 0;
+    } else {
+	Cline pli, plil;
+	int mpl, rpl, wl;
+
+	if (qu)
+	    w = quotename(w, NULL);
+
+	wl = strlen(w);
+
+	/* Always try to match the prefix. */
+
+	if ((mpl = match_str(pfx, w, bpl, &rpl, 0, 0)) < 0)
+	    return NULL;
+
+	if (sfx && *sfx) {
+	    int wpl = matchbufadded, msl, rsl;
+	    VARARR(char, wpfx, wpl);
+	    Cline mli, mlil;
+
+	    /* We also have a suffix to match, so first save the
+	     * contents of the global matching variables. */
+	    memcpy(wpfx, matchbuf, wpl);
+	    if (matchsubs) {
+		Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, 0);
+
+		tmp->prefix = matchsubs;
+		if (matchlastpart)
+		    matchlastpart->next = tmp;
+		else
+		    matchparts = tmp;
 	    }
-	    if (t) {
-		/* Found matching anchors, continue with them. */
-		o->line = to->line;
-		o->llen = to->llen;
-		o->next = to->next;
-		o->flags |= CLF_MISS;
-		n = tn;
-	    } else {
-		/* No matching anchors found, shorten the list. */
-		o->flags |= CLF_MISS;
-		o->next = NULL;
-		o->llen = 0;
-		if (olp)
-		    *olp = smol;
-		if (nlp)
-		    *nlp = smnl;
-		return ret;
+	    pli = matchparts;
+	    plil = matchlastpart;
+
+	    /* The try to match the suffix. */
+	    if ((msl = match_str(sfx, w + mpl, bsl, &rsl, 1, 0)) < 0) {
+		free_cline(pli);
+
+		return NULL;
+	    }
+	    /* Matched, so add the string in the middle and the saved
+	     * string for the prefix, and build a combined cline list
+	     * for the prefix and the suffix. */
+	    if (matchsubs) {
+		Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, CLF_SUF);
+
+		tmp->suffix = matchsubs;
+		if (matchlastpart)
+		    matchlastpart->next = tmp;
+		else
+		    matchparts = tmp;
 	    }
+	    add_match_str(NULL, NULL, w + rpl, wl - rpl - rsl, 1);
+	    add_match_str(NULL, NULL, wpfx, wpl, 1);
+
+	    mli = bld_parts(w + rpl, wl - rpl - rsl, (mpl - rpl), &mlil);
+	    mlil->flags |= CLF_MID;
+	    mlil->slen = msl - rsl;
+	    mlil->next = revert_cline(matchparts);
+
+	    if (plil)
+		plil->next = mli;
+	    else
+		pli = mli;
+	} else {
+	    /* Only a prefix, add the string and a part-cline for it. */
+	    add_match_str(NULL, NULL, w + rpl, wl - rpl, 0);
+
+	    add_match_part(NULL, NULL, NULL, 0, NULL, 0, w + rpl, wl - rpl,
+			   mpl - rpl, 0);
+	    pli = matchparts;
 	}
-	/* If we reached the end of the new list but not the end of the old
-	 * list, we mark the old list (saying that characters are missing 
-	 * here). */
-	if (!(n = n->next) && o->next)
-	    o->flags |= CLF_MISS;
-	o = o->next;
-    }
-    if (o) {
-	/* Shorten the list if we haven't reached the end. */
-	if (n)
-	    o->flags |= CLF_MISS;
-	o->next = NULL;
-	o->llen = 0;
+	r = dupstring(matchbuf);
+	*clp = pli;
+
+	/* Test if the string built is equal to the one from the line. */
+	if (sfx && *sfx) {
+	    int pl = strlen(pfx);
+
+	    *exact = (!strncmp(pfx, w, pl) && !strcmp(sfx, w + pl));
+	} else
+	    *exact = !strcmp(pfx, w);
     }
-    if (olp)
-	*olp = smol;
-    if (nlp)
-	*nlp = smnl;
-    return ret;
+    return r;
 }
 
 /* This builds all the possible line patterns for the pattern pat in the
  * buffer line. Initially line is the same as lp, but during recursive
  * calls lp is incremented for storing successive characters. Whenever
  * a full possible string is build, we test if this line matches the
- * string given by wlen and word. The last argument contains the characters
+ * string given by wlen and word. The in argument contains the characters
  * to use for the correspondence classes, it was filled by a call to 
  * pattern_match() in the calling function.
  * The return value is the length of the string matched in the word, it
  * is zero if we couldn't build a line that matches the word. */
 
-/**/
 static int
-bld_line_pfx(Cpattern pat, char *line, char *lp,
-	     int wlen, char *word, unsigned char *in)
+bld_line(Cpattern pat, char *line, char *lp,
+	 char *word, int wlen, unsigned char *in, int sfx)
 {
     if (pat) {
 	/* Still working on the pattern. */
@@ -2058,53 +2525,52 @@ bld_line_pfx(Cpattern pat, char *line, char *lp,
 		*lp = i;
 		/* We stored the character, now call ourselves to build
 		 * the rest. */
-		if ((l = bld_line_pfx(pat->next, line, lp + 1,
-				      wlen, word, in)))
+		if ((l = bld_line(pat->next, line, lp + 1, word, wlen,
+				  in, sfx)))
 		    return l;
 	    }
     } else {
 	/* We reached the end, i.e. the line string is fully build, now
 	 * see if it matches the given word. */
-	static unsigned char *ea = NULL;
-	static int ealen = 0;
 
 	Cmlist ms;
 	Cmatcher mp;
-	int l = lp - line, t, rl = 0;
+	int l = lp - line, t, rl = 0, ind, add;
+	VARARR(unsigned char, ea, l + 1);
 
 	/* Quick test if the strings are exactly the same. */
 	if (l == wlen && !strncmp(line, word, l))
 	    return l;
 
-	/* We need another buffer for correspondence classes. */
-	if (l + 1 > ealen) {
-	    if (ealen)
-		zfree(ea, ealen);
-	    ea = (unsigned char *) zalloc(ealen = l + 20);
+	if (sfx) {
+	    line = lp; word += wlen;
+	    ind = -1; add = -1;
+	} else {
+	    ind = 0; add = 1;
 	}
 	/* We loop through the whole line string built. */
 	while (l && wlen) {
-	    if (*word == *line) {
+	    if (word[ind] == line[ind]) {
 		/* The same character in both strings, skip over. */
-		line++;
-		word++;
-		l--;
-		wlen--;
-		rl++;
+		line += add; word += add;
+		l--; wlen--; rl++;
 	    } else {
 		t = 0;
 		for (ms = bmatchers; ms && !t; ms = ms->next) {
 		    mp = ms->matcher;
 		    if (!mp->flags && mp->wlen <= wlen && mp->llen <= l &&
-			pattern_match(mp->line, line, NULL, ea) &&
-			pattern_match(mp->word, word, ea, NULL)) {
+			pattern_match(mp->line, (sfx ? line - mp->llen : line),
+				      NULL, ea) &&
+			pattern_match(mp->word, (sfx ? word - mp->wlen : word),
+				      ea, NULL)) {
 			/* Both the line and the word pattern matched,
 			 * now skip over the matched portions. */
-			line += mp->llen;
-			word += mp->wlen;
-			l -= mp->llen;
-			wlen -= mp->wlen;
-			rl += mp->wlen;
+			if (sfx) {
+			    line -= mp->llen; word -= mp->wlen;
+			} else {
+			    line += mp->llen; word += mp->wlen;
+			}
+			l -= mp->llen; wlen -= mp->wlen; rl += mp->wlen;
 			t = 1;
 		    }
 		}
@@ -2114,1916 +2580,822 @@ bld_line_pfx(Cpattern pat, char *line, char *lp,
 	    }
 	}
 	if (!l)
-	    /* Unmatched portion in the line built, return no-match. */
+	    /* Unmatched portion in the line built, return matched length. */
 	    return rl;
     }
     return 0;
 }
 
-/* This builds a list of cline structs describing a string to insert in
- * the line in a place where we didn't had something on the line when
- * completion was invoked. This is called after we get the second match
- * so we have two strings to start with given by (ol,ow) and (nl,nw) and
- * the cline list returned describes a string that matches both of these
- * strings.
- * In olp and nlp we return the number of characters in ow and nw that
- * are not matched by the cline list returned. Especially this means that
- * they are non-zero if there are such unmatched portions.
- * In lp we return a pointer to the last cline struct created. */
+/* This builds a string that may be put on the line that fully matches the
+ * given strings. The return value is NULL if no such string could be built
+ * or that string in local static memory, dup it. */
 
-static Cline
-bld_new_pfx(int ol, char *ow, int nl, char *nw, int *olp, int *nlp, Cline *lp)
+static char *
+join_strs(int la, char *sa, int lb, char *sb)
 {
-    static unsigned char *ea = NULL;
-    static int ealen = 0;
-    static char *line = NULL;
-    static int llen = 0;
+    static char *rs = NULL;
+    static int rl = 0;
 
+    VARARR(unsigned char, ea, (la > lb ? la : lb) + 1);
     Cmlist ms;
     Cmatcher mp;
-    Cline ret = NULL, l = NULL, n;
-    char *p = ow;
-    int t, bl;
-
-    /* This is a buffer for the characters to use for pairs of correspondence
-     * classes. */
-    if (ol + 1 > ealen || nl + 1 > ealen) {
-	if (ealen)
-	    zfree(ea, ealen);
-	ea = (unsigned char *) zalloc(ealen = (ol > nl ? ol : nl) + 20);
-    }
-    /* Loop through the strings. */
-    while (ol && nl) {
-	if (*ow != *nw) {
-	    /* Not the same character, use the patterns. */
+    int t, bl, rr = rl;
+    char *rp = rs;
+
+    while (la && lb) {
+	if (*sa != *sb) {
+	    /* Different characters, try the matchers. */
 	    for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
 		mp = ms->matcher;
-		/* We use only those patterns that match a non-empty
-		 * string in both the line and the word, that match
-		 * strings no longer than the string we still have
-		 * to compare, that don't have anchors, and that don't
-		 * use the line strings for insertion. */
 		if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
-		    mp->wlen <= ol && mp->wlen <= nl) {
-		    /* Try the pattern only if the word-pattern matches
-		     * one of the strings. */
-		    if (pattern_match(mp->word, ow, NULL, ea)) {
-			/* Make the buffer where we build the possible
-			 * line patterns big enough. */
-			if (mp->llen + 1 > llen) {
-			    if (llen)
-				zfree(line, llen);
-			    line = (char *) zalloc(llen = mp->llen + 20);
+		    mp->wlen <= la && mp->wlen <= lb) {
+		    /* The pattern has no anchors and the word
+		     * pattern fits, try it. */
+		    if ((t = pattern_match(mp->word, sa, NULL, ea)) ||
+			pattern_match(mp->word, sb, NULL, ea)) {
+			/* It matched one of the strings, t says which one. */
+			VARARR(char, line, mp->llen + 1);
+			char **ap, **bp;
+			int *alp, *blp;
+
+			if (t) {
+			    ap = &sa; alp = &la;
+			    bp = &sb; blp = &lb;
+			} else {
+			    ap = &sb; alp = &lb;
+			    bp = &sa; blp = &la;
 			}
-			/* Then build all the possible lines and see
-			 * if one of them matches the othe string. */
-			if ((bl = bld_line_pfx(mp->line, line, line,
-					       nl, nw, ea))) {
-			    /* Yep, one of the lines matched the other
-			     * string. */
-			    if (p != ow) {
-				/* There is still a substring that is
-				 * the same in both strings, so add
-				 * a cline for it. */
-				char sav = *ow;
-
-				*ow = '\0';
-				n = getcline(NULL, 0, dupstring(p),
-					     ow - p, 0);
-				*ow = sav;
-				if (l)
-				    l->next = n;
-				else
-				    ret = n;
-				l = n;
-			    }
-			    /* Then we add the line string built. */
+			/* Now try to build a string that matches the other
+			 * string. */
+			if ((bl = bld_line(mp->line, line, line,
+					   *bp, *blp, ea, 0))) {
+			    /* Found one, put it into the return string. */
 			    line[mp->llen] = '\0';
-			    n = getcline(dupstring(line), mp->llen,
-					 NULL, 0, CLF_DIFF);
-			    if (l)
-				l->next = n;
-			    else
-				ret = n;
-			    l = n;
-			    ow += mp->wlen;
-			    nw += bl;
-			    ol -= mp->wlen;
-			    nl -= bl;
-			    p = ow;
-			    t = 1;
-			}
-		    } else if (pattern_match(mp->word, nw, NULL, ea)) {
-			/* Now do the same for the other string. */
-			if (mp->llen + 1 > llen) {
-			    if (llen)
-				zfree(line, llen);
-			    line = (char *) zalloc(llen = mp->llen + 20);
-			}
-			if ((bl = bld_line_pfx(mp->line, line, line,
-					       ol, ow, ea))) {
-			    if (p != ow) {
-				char sav = *ow;
-
-				*ow = '\0';
-				n = getcline(NULL, 0, dupstring(p),
-					     ow - p, 0);
-				*ow = sav;
-				if (l)
-				    l->next = n;
-				else
-				    ret = n;
-				l = n;
+			    if (rr <= mp->llen) {
+				char *or = rs;
+
+				rs = realloc(rs, (rl += 20));
+				rr += 20;
+				rp += rs - or;
 			    }
-			    line[mp->llen] = '\0';
-			    n = getcline(dupstring(line), mp->llen,
-					 NULL, 0, CLF_DIFF);
-			    if (l)
-				l->next = n;
-			    else
-				ret = n;
-			    l = n;
-			    ow += bl;
-			    nw += mp->wlen;
-			    ol -= bl;
-			    nl -= mp->wlen;
-			    p = ow;
+			    memcpy(rp, line, mp->llen);
+			    rp += mp->llen; rr -= mp->llen;
+			    *ap += mp->wlen; *alp -= mp->wlen;
+			    *bp += bl; *blp -= bl;
 			    t = 1;
 			}
 		    }
 		}
 	    }
 	    if (!t)
-		/* No pattern matched, so give up. */
 		break;
 	} else {
-	    /* Same character, skip over. */
-	    ow++;
-	    nw++;
-	    ol--;
-	    nl--;
-	}
-    }
-    if (p != ow) {
-	/* There is a equal substring in both strings, build a cline
-	 * for it. */
-	char sav = *ow;
-
-	*ow = '\0';
-	n = getcline(NULL, 0, dupstring(p), ow - p, 0);
-	*ow = sav;
-	if (l)
-	    l->next = n;
-	else
-	    ret = n;
-	l = n;
-    }
-    if (l)
-	l->next = NULL;
-    else
-	ret = NULL;
-
-    if (olp)
-	*olp = ol;
-    if (nlp)
-	*nlp = nl;
-    if (lp)
-	*lp = l;
-
-    return ret;
-}
-
-/* Given a cline list for an unmatched part of the string to insert in the
- * line and a new match substring, modify the cline list so that it also
- * matches this string. The cline list is shortened in the place where
- * we can't build a cline matching the new string.
- * However, the resulting cline list is returned. The string is described
- * by len and word. In missp we return non-zero if the cline list returned
- * had to be shortened (and hence doesn't fully match the strings it was
- * built from anymore) or if it doesn't fully match the given string.
- * This function checks the string left to right and thus is to be used
- * for strings where we want a common prefix. */
+	    /* Same character, just take it. */
+	    if (rr <= 1) {
+		char *or = rs;
 
-static Cline
-join_new_pfx(Cline line, int len, char *word, int *missp)
-{
-    static unsigned char *ea = NULL;
-    static int ealen = 0;
-
-    Cline ret = NULL, l = NULL, next;
-    int miss = 0;
-
-    /* Walk through the list and the string. */
-    while (line && len) {
-	next = line->next;
-	/* The line element is used in those places where a new line
-	 * string was built. */
-	if (line->line) {
-	    Cmlist ms;
-	    Cmatcher mp;
-	    int ll = line->llen, t;
-	    char *p = line->line;
-
-	    /* Make the buffer for the correspondence classes big enough. */
-	    if (line->llen + 1 > ealen) {
-		if (ealen)
-		    zfree(ea, ealen);
-		ea = (unsigned char *) zalloc(ealen = line->llen + 20);
-	    }
-	    /* Check if the line string from the cline list matches the
-	     * new string. */
-	    while (ll && len) {
-		if (*p == *word) {
-		    p++;
-		    word++;
-		    ll--;
-		    len--;
-		} else {
-		    for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
-			mp = ms->matcher;
-			if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
-			    mp->wlen <= len && mp->llen <= len &&
-			    pattern_match(mp->word, word, NULL, ea) &&
-			    pattern_match(mp->line, p, ea, NULL)) {
-			    /* We have a matched substring, skip over. */
-			    p += mp->llen;
-			    word += mp->wlen;
-			    ll -= mp->llen;
-			    len -= mp->wlen;
-			    t = 1;
-			}
-		    }
-		    if (!t)
-			/* No match, give up. */
-			break;
-		}
-	    }
-	    if (p == line->line) {
-		/* We couldn't match any character from the string in the
-		 * cline, so shorten the list and don't even keep this
-		 * struct. */
-		miss = 1;
-		len = 0;
-	    } else {
-		/* At least the beginning of the cline string can be used. */
-		if (ll) {
-		    /* But there is a portion of the string that can't be
-		     * used, so we have to shorten the list. */
-		    miss = 1;
-		    *p = '\0';
-		    line->llen -= ll;
-		    len = 0;
-		}
-		line->next = NULL;
-		if (l)
-		    l->next = line;
-		else
-		    ret = line;
-		l = line;
-	    }
-	} else {
-	    /* The cline doesn't have a string built by reverse matching,
-	     * so we have to work on the original substring in the cline
-	     * and the new string. */
-	    if (line->wlen == len && !strncmp(line->word, word, len)) {
-		/* They are equal, accept and return. If there was
-		 * another element in the list, shorten the list. */
-		if (next)
-		    miss = 1;
-		line->next = NULL;
-		if (l)
-		    l->next = line;
-		else
-		    ret = line;
-		l = line;
-		len = 0;
-	    } else {
-		char sav = word[len];
-
-		/* Check if one is the prefix of the other one. */
-		word[len] = '\0';
-		if (strpfx(word, line->word)) {
-		    word[len] = sav;
-
-		    line->word[len] = '\0';
-		    line->wlen = len;
-		    miss = 1;
-		    line->next = NULL;
-		    if (l)
-			l->next = line;
-		    else
-			ret = line;
-		    l = line;
-		    len = 0;
-		} else if (strpfx(line->word, word)) {
-		    word[len] = sav;
-
-		    miss = 1;
-		    line->next = NULL;
-		    if (l)
-			l->next = line;
-		    else
-			ret = line;
-		    l = line;
-		    len = 0;
-		} else {
-		    /* Not the same and no prefix, so we try to build a
-		     * new line string matching the string in the cline
-		     * and the new string. */
-		    int mol, mnl;
-		    Cline sl, send;
-
-		    word[len] = sav;
-
-		    if ((sl = bld_new_pfx(line->wlen, line->word,
-					  len, word, &mol, &mnl, &send))) {
-			/* We could build such a string, use it in the
-			 * cline structure. */
-			if (l)
-			    l->next = sl;
-			else
-			    ret = sl;
-			l = sl;
-			if (!mol) {
-			    send->next = next;
-			    word += len - mnl;
-			    len = mnl;
-			} else
-			    len = 0;
-			l = send;
-		    } else
-			len = 0;
-		}
+		rs = realloc(rs, (rl += 20));
+		rr += 20;
+		rp += rs - or;
 	    }
+	    *rp++ = *sa; rr--;
+	    sa++; sb++;
+	    la--; lb--;
 	}
-	line = next;
     }
-    *missp = (line || len || miss);
-
-    return ret;
-}
-
-/* This function gets two cline structs for which we want to build a
- * common prefix to put on the line. The result is placed in the cline
- * struct given as first argument.
- * In olp and nlp we return the matched lengths for o and n, respectively
- * (but this is only guaranteed to give correct results if this is the
- * first call for the given o and n). */
-
-/**/
-static void
-bld_pfx(Cline o, Cline n, int *olp, int *nlp)
-{
-    if (olp)
-	*olp = 0;
-    if (nlp)
-	*nlp = 0;
-    if (o->flags & CLF_PNEW) {
-	if (o->flags & (CLF_END | CLF_MID))
-	    /* We split the suffix in the middle and at the end into
-	     * separate runs. */
-	    o->prefix = join_ends(o->prefix, end_list(n->wlen, n->word),
-				  NULL, NULL);
-	else {
-	    /* This flag is set if we already built a cline list for such
-	     * a prefix. We join it with the string from the other cline
-	     * struct. */
-	    int miss;
-
-	    o->prefix = join_new_pfx(o->prefix, n->wlen, n->word, &miss);
-	    if (miss)
-		o->flags |= CLF_MISS;
-	}
-    } else if (o->flags & (CLF_END | CLF_MID)) {
-	o->flags |= CLF_PNEW;
-	o->prefix = join_ends(end_list(o->wlen, o->word),
-			      end_list(n->wlen, n->word), olp, nlp);
-    } else if (o->wlen && n->wlen) {
-	/* We haven't built a cline list for it yet. */
-	char so = o->word[o->wlen], sn = n->word[n->wlen];
-	char *new = o->word;
-	int newl = o->wlen, mol, mnl;
-
-	/* If one of the strings is a prefix of the other one, just keep
-	 * that prefix. */
-	o->word[o->wlen] = n->word[n->wlen] = '\0';
-	if (strpfx(n->word, o->word)) {
-	    new = dupstring(n->word);
-	    newl = n->wlen;
-	    if (olp)
-		*olp = *nlp = n->wlen;
-	} else if (strpfx(o->word, n->word)) {
-	    if (olp)
-		*olp = *nlp = o->wlen;
-	} else {
-	    /* Otherwise build a cline list describing a string that
-	     * matches both strings from the original cline structs
-	     * and thus can be put in the command line to represent
-	     * them. This cline list is stored in o. */
-	    o->flags |= CLF_PNEW;
-	    o->prefix = bld_new_pfx(o->wlen, o->word, n->wlen, n->word,
-				    &mol, &mnl, NULL);
-	    newl = 0;
-	    new = "";
-	    if (mol || mnl)
-		o->flags |= CLF_MISS;
-	    if (olp) {
-		*olp = o->wlen - mol;
-		*nlp = n->wlen - mnl;
-	    }
-	}
-	o->word[o->wlen] = so;
-	n->word[n->wlen] = sn;
+    if (la || lb)
+	return NULL;
 
-	o->word = new;
-	o->wlen = newl;
+    *rp = '\0';
 
-	if (!o->prefix && n->wlen != o->wlen)
-	    o->flags |= CLF_MISS;
-    } else
-	o->wlen = 0;
+    return rs;
 }
 
-/* The following function are like their counterparts above, only for
- * the other direction. */
+/* This compares the anchors stored in two top-level clines. */
 
 static int
-bld_line_sfx(Cpattern pat, char *line, char *lp,
-	     int wlen, char *word, unsigned char *in)
+cmp_anchors(Cline o, Cline n, int join)
 {
-    if (pat) {
-	int i, l;
-	unsigned char c = 0;
-
-	if (pat->equiv)
-	    if ((c = *in))
-		in++;
-
-	for (i = 0; i < 256; i++)
-	    if ((pat->equiv && c) ? (c == pat->tab[i]) : pat->tab[i]) {
-		*lp = i;
-		if ((l = bld_line_pfx(pat->next, line, lp + 1,
-				      wlen, word, in)))
-		    return l;
-	    }
-    } else {
-	static unsigned char *ea = NULL;
-	static int ealen = 0;
-
-	Cmlist ms;
-	Cmatcher mp;
-	int l = lp - line, t, rl = 0;
-
-	if (l == wlen && !strncmp(line, word, l))
-	    return l;
+    int line = 0;
+    char *j;
 
-	line = lp;
-	word += wlen;
-	
-	if (l + 1 > ealen) {
-	    if (ealen)
-		zfree(ea, ealen);
-	    ea = (unsigned char *) zalloc(ealen = l + 20);
+    /* First try the exact strings. */
+    if ((!(o->flags & CLF_LINE) && o->wlen == n->wlen &&
+	 (!o->word || !strncmp(o->word, n->word, o->wlen))) ||
+	(line = ((!o->line && !n->line) ||
+		 (o->llen == n->llen && o->line && n->line &&
+		  !strncmp(o->line, n->line, o->llen))))) {
+	if (line) {
+	    o->flags |= CLF_LINE;
+	    o->word = NULL;
+	    n->wlen = 0;
 	}
-	while (l && wlen) {
-	    if (word[-1] == line[-1]) {
-		line--;
-		word--;
-		l--;
-		wlen--;
-		rl++;
-	    } else {
-		t = 0;
-		for (ms = bmatchers; ms && !t; ms = ms->next) {
-		    mp = ms->matcher;
-		    if (!mp->flags && mp->wlen <= wlen && mp->llen <= l &&
-			pattern_match(mp->line, line - mp->llen,
-				      NULL, ea) &&
-			pattern_match(mp->word, word - mp->wlen,
-				      ea, NULL)) {
-			line -= mp->llen;
-			word -= mp->wlen;
-			l -= mp->llen;
-			wlen -= mp->wlen;
-			rl += mp->wlen;
-			t = 1;
-		    }
-		}
-		if (!t)
-		    return 0;
-	    }
-	}
-	if (!l)
-	    return rl;
+	return 1;
+    }
+    /* Didn't work, try to build a string matching both anchors. */
+    if (join && !(o->flags & CLF_JOIN) &&
+	(j = join_strs(o->wlen, o->word, n->wlen, n->word))) {
+	o->flags |= CLF_JOIN;
+	o->wlen = strlen(j);
+	o->word = dupstring(j);
+
+	return 2;
     }
     return 0;
 }
 
-static Cline
-bld_new_sfx(int ol, char *ow, int nl, char *nw, int *olp, int *nlp, Cline *lp)
-{
-    static unsigned char *ea = NULL;
-    static int ealen = 0;
-    static char *line = NULL;
-    static int llen = 0;
+/* Below is the code to join two cline lists. This struct is used to walk
+ * through a sub-list. */
 
-    Cmlist ms;
-    Cmatcher mp;
-    Cline ret = NULL, l = NULL, n;
-    char *p;
-    int t, bl;
-
-    ow += ol;
-    nw += nl;
-    p = ow;
-
-    if (ol + 1 > ealen || nl + 1 > ealen) {
-	if (ealen)
-	    zfree(ea, ealen);
-	ea = (unsigned char *) zalloc((ealen = (ol > nl ? ol : nl) + 20));
-    }
-    while (ol && nl) {
-	if (ow[-1] != nw[-1]) {
-	    for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
-		mp = ms->matcher;
-		if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
-		    mp->wlen <= ol && mp->wlen <= nl) {
-		    if (pattern_match(mp->word, ow - mp->wlen,
-				      NULL, ea)) {
-			if (mp->llen + 1 > llen) {
-			    if (llen)
-				zfree(line, llen);
-			    line = (char *) zalloc(llen = mp->llen + 20);
-			}
-			if ((bl = bld_line_sfx(mp->line, line, line,
-					       nl, nw, ea))) {
-			    if (p != ow) {
-				char sav = *p;
-
-				*p = '\0';
-				n = getcline(NULL, 0, dupstring(ow),
-					     p - ow, 0);
-				*p = sav;
-				if (l)
-				    l->next = n;
-				else
-				    ret = n;
-				l = n;
-			    }
-			    line[mp->llen] = '\0';
-			    n = getcline(dupstring(line), mp->llen,
-					 NULL, 0, CLF_DIFF);
-			    if (l)
-				l->next = n;
-			    else
-				ret = n;
-			    l = n;
-			    ow -= mp->wlen;
-			    nw -= bl;
-			    ol -= mp->wlen;
-			    nl -= bl;
-			    p = ow;
-			    t = 1;
-			}
-		    } else if (pattern_match(mp->word, nw - mp->wlen,
-					     NULL, ea)) {
-			if (mp->llen + 1 > llen) {
-			    if (llen)
-				zfree(line, llen);
-			    line = (char *) zalloc(llen = mp->llen + 20);
-			}
-			if ((bl = bld_line_sfx(mp->line, line, line,
-					       ol, ow, ea))) {
-			    if (p != ow) {
-				char sav = *p;
-
-				*p = '\0';
-				n = getcline(NULL, 0, dupstring(ow),
-					     p - ow, 0);
-				*p = sav;
-				if (l)
-				    l->next = n;
-				else
-				    ret = n;
-				l = n;
-			    }
-			    line[mp->llen] = '\0';
-			    n = getcline(dupstring(line), mp->llen,
-					 NULL, 0, CLF_DIFF);
-			    if (l)
-				l->next = n;
-			    else
-				ret = n;
-			    l = n;
-			    ow -= bl;
-			    nw -= mp->wlen;
-			    ol -= bl;
-			    nl -= mp->wlen;
-			    p = ow;
-			    t = 1;
-			}
-		    }
-		}
-	    }
-	    if (!t)
-		break;
-	} else {
-	    ow--;
-	    nw--;
-	    ol--;
-	    nl--;
-	}
-    }
-    if (p != ow) {
-	char sav = *p;
-
-	*p = '\0';
-	n = getcline(NULL, 0, dupstring(ow), p - ow, 0);
-	*p = sav;
-	if (l)
-	    l->next = n;
-	else
-	    ret = n;
-	l = n;
-    }
-    if (l)
-	l->next = NULL;
-    else
-	ret = NULL;
+typedef struct cmdata *Cmdata;
 
-    if (olp)
-	*olp = ol;
-    if (nlp)
-	*nlp = nl;
-    if (lp)
-	*lp = l;
+struct cmdata {
+    Cline cl, pcl;
+    char *str, *astr;
+    int len, alen, olen, line;
+};
 
-    return ret;
-}
+/* This is used to ensure that a cmdata struct contains usable data.
+ * The return value is non-zero if we reached the end. */
 
-static Cline
-join_new_sfx(Cline line, int len, char *word, int *missp)
+static int
+check_cmdata(Cmdata md, int sfx)
 {
-    static unsigned char *ea = NULL;
-    static int ealen = 0;
-
-    Cline ret = NULL, l = NULL, next;
-    int miss = 0, ind = 0;
-
-    word += len;
-
-    while (line && len) {
-	next = line->next;
-	if (line->line) {
-	    Cmlist ms;
-	    Cmatcher mp;
-	    int ll = line->llen, t;
-	    char *p = line->line + ll;
-
-	    if (line->llen + 1 > ealen) {
-		if (ealen)
-		    zfree(ea, ealen);
-		ea = (unsigned char *) zalloc(ealen = line->llen + 20);
-	    }
-	    while (ll && len) {
-		if (p[-1] == word[-1]) {
-		    p--;
-		    word--;
-		    ll--;
-		    len--;
-		    ind++;
-		} else {
-		    for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
-			mp = ms->matcher;
-			if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
-			    mp->wlen <= len && mp->llen <= len &&
-			    pattern_match(mp->word, word - mp->wlen,
-					  NULL, ea) &&
-			    pattern_match(mp->line, p - mp->llen,
-					  ea, NULL)) {
-			    p -= mp->llen;
-			    word -= mp->wlen;
-			    ll -= mp->llen;
-			    len -= mp->wlen;
-			    ind += mp->wlen;
-			    t = 1;
-			}
-		    }
-		    if (!t)
-			break;
-		}
-	    }
-	    if (p == line->line + line->llen) {
-		miss = 1;
-		len = 0;
-	    } else {
-		if (ll) {
-		    miss = 1;
-		    line->line = p;
-		    line->llen -= ll;
-		    len = 0;
-		}
-		line->next = NULL;
-		if (l)
-		    l->next = line;
-		else
-		    ret = line;
-	    }
+    /* We will use the str and len fields to contain the next sub-string
+     * in the list. If len is zero, we have to use the next cline. */
+    if (!md->len) {
+	/* If there is none, we reached the end. */
+	if (!md->cl)
+	    return 1;
+
+	/* Otherwise, get the string. Only the line-string or both.
+	 * We also have to adjust the pointer if this is for a suffix. */
+	if (md->cl->flags & CLF_LINE) {
+	    md->line = 1;
+	    md->len = md->cl->llen;
+	    md->str = md->cl->line;
 	} else {
-	    if (line->wlen == len && !strncmp(line->word, word - len, len)) {
-		if (next)
-		    miss = 1;
-		line->next = NULL;
-		if (l)
-		    l->next = line;
-		else
-		    ret = line;
-		len = 0;
-	    } else {
-		char sav = word[ind];
-
-		word[ind] = '\0';
-		if (strpfx(word - len, line->word)) {
-		    word[ind] = sav;
-
-		    line->word += line->wlen - len;
-		    line->wlen = ind;
-		    miss = 1;
-		    line->next = NULL;
-		    if (l)
-			l->next = line;
-		    else
-			ret = line;
-		    len = 0;
-		} else if (strpfx(line->word, word - len)) {
-		    word[ind] = sav;
-
-		    miss = 1;
-		    line->next = NULL;
-		    if (l)
-			l->next = line;
-		    else
-			ret = line;
-		    len = 0;
-		} else {
-		    int mol, mnl;
-		    Cline sl, send;
-
-		    word[len] = sav;
-
-		    if ((sl = bld_new_sfx(line->wlen, line->word,
-					  len, word - len,
-					  &mol, &mnl, &send))) {
-			if (l)
-			    l->next = sl;
-			else
-			    ret = sl;
-			if (!mol) {
-			    send->next = next;
-			    word -= len - mnl;
-			    len = mnl;
-			} else
-			    len = 0;
-			l = send;
-		    } else
-			len = 0;
-		}
-	    }
+	    md->line = 0;
+	    md->len = md->olen = md->cl->wlen;
+	    if ((md->str = md->cl->word) && sfx)
+		md->str += md->len;
+	    md->alen = md->cl->llen;
+	    if ((md->astr = md->cl->line) && sfx)
+		md->astr += md->alen;
 	}
-	line = next;
+	md->pcl = md->cl;
+	md->cl = md->cl->next;
     }
-    *missp = (line || len || miss);
-
-    return ret;
+    return 0;
 }
 
-/**/
-static void
-bld_sfx(Cline o, Cline n)
-{
-    if (o->flags & CLF_SNEW) {
-	int miss;
-
-	o->suffix = join_new_sfx(o->suffix, n->wlen, n->word, &miss);
-	if (miss)
-	    o->flags |= CLF_MISS;
-    } else if (o->wlen && n->wlen) {
-	char so = o->word[o->wlen], sn = n->word[n->wlen];
-	char *new = o->word;
-	int newl = o->wlen, mol, mnl;
-
-	o->word[o->wlen] = n->word[n->wlen] = '\0';
-	if (strpfx(n->word, o->word)) {
-	    new = dupstring(n->word);
-	    newl = n->wlen;
-	} else if (!strpfx(o->word, n->word)) {
-	    o->flags |= CLF_SNEW;
-	    o->suffix = bld_new_sfx(o->wlen, o->word, n->wlen, n->word,
-				    &mol, &mnl, NULL);
-	    newl = 0;
-	    new = "";
-	    if (mol || mnl)
-		o->flags |= CLF_MISS;
-	}
-	o->word[o->wlen] = so;
-	n->word[n->wlen] = sn;
-
-	o->word = new;
-	o->wlen = newl;
+/* This puts the not-yet-matched portion back into the last cline and 
+ * returns that. */
 
-	if (!o->suffix && n->wlen != o->wlen)
-	    o->flags |= CLF_MISS;
-    } else
-	o->wlen = 0;
+static Cline
+undo_cmdata(Cmdata md, int sfx)
+{
+    Cline r = md->pcl;
+
+    if (md->line) {
+	r->word = NULL;
+	r->wlen = 0;
+	r->flags |= CLF_LINE;
+	r->llen = md->len;
+	r->line = md->str - (sfx ? md->len : 0);
+    } else if (md->len != md->olen) {
+	r->wlen = md->len;
+	r->word = md->str - (sfx ? md->len : 0);
+    }
+    return r;
 }
 
-/* Joins two Cline lists, building the most specific line string *
- * that is possible and returns it. This is done by modifying the
- * cline list given as the first argument. */
+/* This tries to build a string matching a sub-string in a sub-cline
+ * that could not be matched otherwise. */
 
 static Cline
-join_clines(Cline o, Cline n)
+join_sub(Cmdata md, char *str, int len, int *mlen, int sfx, int join)
 {
-    Cline oo = o;
-
-    if (!o)
-	/* This is the first time we are being called, so just return
-	 * the second list. In future calls we will get this list as
-	 * the first argument. */
-	return n;
-    else {
-	Cline f = freecl, q, op = NULL;
-	int ol, nl;
-
-	freecl = NULL;
+    if (!check_cmdata(md, sfx)) {
+	char *ow = str, *nw = md->str;
+	int ol = len, nl = md->len;
+	Cmlist ms;
+	Cmatcher mp;
+	VARARR(unsigned char, ea, (ol > nl ? ol : nl) + 1);
+	int t;
 
-	while (o && n) {
-	    /* CLF_MID is set in the cline struct where the prefix and the
-	     * suffix from the line meet. If we have reached the cline
-	     * for it in one of the lists, search the corresponding 
-	     * cline in the other list, removing all structs up to it. */
-	    if (o->flags & CLF_MID) {
-		while (n && !(n->flags & CLF_MID)) {
-		    q = n->next;
-		    n->next = f;
-		    f = n;
-
-		    n = q;
-		}
-	    }
-	    if (n && n->flags & CLF_MID) {
-		while (o && !(o->flags & CLF_MID)) {
-		    o->word = NULL;
-		    o->flags |= CLF_DIFF;
+	if (sfx) {
+	    ow += ol; nw += nl;
+	}
+	for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
+	    mp = ms->matcher;
+	    /* We use only those patterns that match a non-empty
+	     * string in both the line and the word and that have
+	     * no anchors. */
+	    if (!mp->flags && mp->wlen > 0 && mp->llen > 0) {
+		/* We first test, if the old string matches already the
+		 * new one. */
+		if (mp->llen <= ol && mp->wlen <= nl &&
+		    pattern_match(mp->line, ow - (sfx ? mp->llen : 0),
+				  NULL, ea) &&
+		    pattern_match(mp->word, nw - (sfx ? mp->wlen : 0),
+				  ea, NULL)) {
+		    /* It did, update the contents of the cmdata struct
+		     * and return a cline for the matched part. */
+		    if (sfx)
+			md->str -= mp->wlen;
+		    else
+			md->str += mp->wlen;
+		    md->len -= mp->wlen;
+		    *mlen = mp->llen;
 
-		    o = o->next;
+		    return get_cline(NULL, 0, ow - (sfx ? mp->llen : 0),
+				     mp->llen, NULL, 0, 0);
 		}
-	    }
-	    if (o && n && !((o->flags | n->flags) & CLF_MID)) {
-		ol = o->llen;
-		nl = n->llen;
-
-		while (o && n && ol != nl) {
-		    /* The matched strings have different lengths, so    *
-		     * continue walking the lists until we have the same *
-		     * matched lengths. */
-		    o->word = NULL;
-		    o->flags |= CLF_DIFF;
-		    if (ol < nl) {
-			op = o;
-			if ((o = o->next))
-			    ol += o->llen;
-		    } else {
-			q = n->next;
-			n->next = f;
-			f = n;
+		/* Otherwise we will try to build a string that matches
+		 * both strings. But try the pattern only if the word-
+		 * pattern matches one of the strings. */
+		if (join && mp->wlen <= ol && mp->wlen <= nl &&
+		    ((t = pattern_match(mp->word, ow - (sfx ? mp->wlen : 0),
+				       NULL, ea)) ||
+		     pattern_match(mp->word, nw - (sfx ? mp->wlen : 0),
+				   NULL, ea))) {
+		    VARARR(char, line, mp->llen + 1);
+		    int bl;
+
+		    /* Then build all the possible lines and see
+		     * if one of them matches the other string. */
+		    if ((bl = bld_line(mp->line, line, line,
+				       (t ? nw : ow), (t ? nl : ol),
+				       ea, sfx))) {
+			/* Yep, one of the lines matched the other
+			 * string. */
+			line[mp->llen] = '\0';
+
+			if (t) {
+			    ol = mp->wlen; nl = bl;
+			} else {
+			    ol = bl; nl = mp->wlen;
+			}
+			if (sfx)
+			    md->str -= nl;
+			else
+			    md->str += nl;
+			md->len -= nl;
+			*mlen = ol;
 
-			if ((n = q))
-			    nl += n->llen;
+			return get_cline(NULL, 0, dupstring(line), mp->llen,
+					 NULL, 0, CLF_JOIN);
 		    }
 		}
 	    }
-	    if (!o || !n)
-		break;
-	    if (o->flags & CLF_MID) {
-		/* These are the structs in the middle, so simplify the
-		 * prefix and the suffix in it to their longest common
-		 * versions. */
-		
-		char *os, *ns, *oss = o->line, *nss = n->line;
-		int ol, nl, mol, mnl, oll = o->llen, nll = n->llen;
-
-		os = o->word;
-		ol = o->wlen;
-		ns = n->word;
-		nl = n->wlen;
-
-		o->word = o->line;
-		o->wlen = o->llen;
-		n->word = n->line;
-		n->wlen = n->llen;
-		bld_pfx(o, n, &mol, &mnl);
-		o->line = o->word;
-		o->llen = o->wlen;
-
-		o->word = os;
-		o->wlen = ol;
-		n->word = ns;
-		n->wlen = nl;
-
-		if (o->wlen < 0) {
-		    o->word = oss + mol;
-		    o->wlen = oll - mol;
-		}
-		if (n->wlen < 0) {
-		    n->word = nss + mnl;
-		    n->wlen = nll - mnl;
-		}
-		bld_sfx(o, n);
-	    } else if (o->word) {
-		if (n->word) {
-		    if (o->llen == n->llen &&
-			(o->flags & CLF_VAR) && (n->flags & CLF_VAR) &&
-			(o->flags & (CLF_END | CLF_SUF)) ==
-			(n->flags & (CLF_END | CLF_SUF))) {
-			/* We have two chunks from `*' patterns,
-			 * reduce them to the common prefix/suffix. */
-			if (o->flags & CLF_SUF)
-			    bld_sfx(o, n);
-			else
-			    bld_pfx(o, n, NULL, NULL);
-		    } else if (o->wlen == n->wlen) {
-			/* Otherwise keep them if they are equal. */
-			if (strncmp(o->word, n->word, o->wlen)) {
-			    /* If they are not equal, we make them *
-			     * be left unchanged on the line. */
-			    o->word = NULL;
-			    o->flags |= CLF_DIFF;
-			}
-		    } else {
-			o->word = NULL;
-			o->flags |= CLF_DIFF;
-		    }
-		} else {
-		    o->word = NULL;
-		    o->flags |= CLF_DIFF;
-		}
-	    } else if (n->word)
-		o->flags |= CLF_DIFF;
-
-	    q = n->next;
-	    n->next = f;
-	    f = n;
-
-	    n = q;
-	    op = o;
-	    o = o->next;
-	}
-	if (o) {
-	    /* The old list has elements not matched by the second list
-	     * we put all these elements in the free list. */
-	    for (q = o; q->next; q = q->next);
-
-	    q->next = f;
-	    f = o;
-
-	    if (op)
-		op->next = NULL;
-	    else
-		return NULL;
-	}
-	if (n) {
-	    /* We always put the chunks from the second list back on *
-	     * the free list. */
-	    for (q = n; q->next; q = q->next);
-
-	    q->next = f;
-	    f = n;
 	}
-	freecl = f;
     }
-    return oo;
-}
-
-/* This returns a Cline for the given string. In lp we return a pointer to
- * the last cline struct created. */
-
-static Cline
-str_cline(char *s, int l, Cline *lp)
-{
-    Cline r = NULL, *p = &r, n = NULL;
-
-    if (l < 0)
-	l = strlen(s);
-    /* We build one struct per character, this makes joining it faster
-     * and easier. */
-    while (l) {
-	*p = n = getcline(s, 1, NULL, 0, 0);
-
-	p = &(n->next);
-	s++;
-	l--;
-    }
-    if (lp)
-	*lp = n;
-
-    return r;
+    return NULL;
 }
 
-/* This reverts the order of the elements of the given cline list and
- * returns a pointer to the new head. */
+/* This is used to match a sub-string in a sub-cline. The length of the
+ * matched portion is returned. This tests only for exact equality. */
 
-static Cline
-revert_clines(Cline p)
+static int
+sub_match(Cmdata md, char *str, int len, int sfx)
 {
-    Cline r = NULL, n;
+    int ret = 0, l, ind, add;
+    char *p, *q;
 
-    while (p) {
-	n = p->next;
-	p->next = r;
-	r = p;
-	p = n;
+    if (sfx) {
+	str += len;
+	ind = -1; add = -1;
+    } else {
+	ind = 0; add = 1;
     }
-    return r;
-}
-
-/* Prepend a string to a Cline and return a pointer to the new cline list. */
-
-static Cline
-prepend_cline(char *s, Cline l)
-{
-    Cline n, r = str_cline(s, -1, &n), *p = &(n->next);
+    /* str and len describe the old string, in md we have the new one. */
+    while (len) {
+	if (check_cmdata(md, sfx))
+	    return ret;
 
-    while (l) {
-	*p = n = getcline(l->line, l->llen, l->word, l->wlen,
-			  l->flags);
+	for (l = 0, p = str, q = md->str;
+	     l < len && l < md->len && p[ind] == q[ind];
+	     l++, p += add, q += add);
 
-	p = &(n->next);
-	l = l->next;
+	if (l) {
+	    /* There was a common prefix, use it. */
+	    md->len -= l; len -= l;
+	    if (sfx) {
+		md->str -= l; str -= l;
+	    } else {
+		md->str += l; str += l;
+	    }
+	    ret += l;
+	} else if (md->line || md->len != md->olen || !md->astr)
+	    return ret;
+	else {
+	    /* We still have the line string to try. */
+	    md->line = 1;
+	    md->len = md->alen;
+	    md->str = md->astr;
+	}
     }
-    return r;
+    return ret;
 }
 
-/* This simplifies the Cline to match the given strings. The strings are:
- * the common prefix and its length, a string with the suffix at the end
- * and the suffix length. */
+/* This is used to build a common prefix or suffix sub-list. If requested
+ * it returns the unmatched cline lists in orest and nrest. */
 
 static void
-merge_cline(Cline lc, char *p, int pl, char *s, int sl, int psl)
+join_psfx(Cline ot, Cline nt, Cline *orest, Cline *nrest, int sfx)
 {
-    int pll, sll;
-    Cline l = NULL, *q = &l, n;
-
-    pll = strlen(p);
-    if (s) {
-	/* If there is a suffix, get a pointer to the beginning of the
-	 * common suffix. */
-	int i = strlen(s);
-
-	if (ainfo->suflen < 10000)
-	    s = s + i - ainfo->suflen;
-	else {
-	    s = s + i - sl;
-	    p[pll - (sl - psl)] = '\0';
-	    pll -= sl - psl;
-	}
-	sll = strlen(s) - sl;
-    } else
-	sll = 0;
-
-    pll -= pl;
+    Cline p = NULL, o, n;
+    struct cmdata md, omd;
+    char **sstr = NULL;
+    int len, join = 0, line = 0, *slen = NULL;
 
-    /* Now build a cline list from the string(s) and join it with the
-     * cline list we have built while testing possible matches. */
-    l = str_cline(p, pl, &n);
-    q = &(n->next);
-    if (!sl)
-	*q = getcline(NULL, 0, p + pl, pll, CLF_END | CLF_VAR);
-    else {
-	*q = n = getcline(p + pl, pll, s, sll, CLF_MID);
+    if (sfx) {
+	o = ot->suffix; n = nt->suffix;
+    } else {
+	o = ot->prefix;	n = nt->prefix;
+    }
+    if (!o) {
+	if (orest)
+	    *orest = NULL;
+	if (nrest)
+	    *nrest = n;
 
-	n->next = str_cline(s + sll, sl, NULL);
+	return;
     }
-    join_clines(lc, l);
-}
+    if (!n) {
+	if (sfx)
+	    ot->suffix = NULL;
+	else
+	    ot->prefix = NULL;
 
-/* This inserts the Cline built into the command line. The last two
- * arguments are the relative positions where the begining and the
- * end of the brace expansion strings have to be re-inserted. */
+	if (orest)
+	    *orest = o;
+	else
+	    free_cline(o);
+	if (nrest)
+	    *nrest = NULL;
+	return;
+    }
+    md.cl = n;
+    md.len = 0;
 
-static void
-inst_cline(Cline l, int pl, int sl)
-{
-    int m = -1, d = -1, b = -1, hb = 0, i = 0;
+    /* Walk through the old list. */
+    while (o) {
+	join = 0;
+	memcpy(&omd, &md, sizeof(struct cmdata));
 
-    /* Adjust the position of the braces. */
-    pl += brpl;
-    sl += brbsl;
+	/* We first get the length of the prefix equal in both strings. */
+	if (o->flags & CLF_LINE) {
+	    if ((len = sub_match(&md, o->line, o->llen, sfx)) != o->llen) {
+		join = 1; line = 1; slen = &(o->llen); sstr = &(o->line);
+	    }
+	} else if ((len = sub_match(&md, o->word, o->wlen, sfx)) != o->wlen) {
+	    if (o->line) {
+		memcpy(&md, &omd, sizeof(struct cmdata));
+		o->flags |= CLF_LINE | CLF_DIFF;
 
-    i = cs - wb;
+		continue;
+	    }
+	    join = 1; line = 0; slen = &(o->wlen); sstr = &(o->word);
+	}
+	if (join) {
+	    /* There is a rest that is different in the two lists,
+	     * we try to build a new cline matching both strings. */
+	    Cline joinl;
+	    int jlen;
+
+	    if ((joinl = join_sub(&md, *sstr + len, *slen - len,
+				  &jlen, sfx, !(o->flags & CLF_JOIN)))) {
+		/* We have one, insert it into the list. */
+		joinl->flags |= CLF_DIFF;
+		if (len + jlen != *slen) {
+		    Cline rest;
+
+		    rest = get_cline(NULL, 0, *sstr + (sfx ? 0 : len + jlen),
+				     *slen - len - jlen, NULL, 0, 0);
+
+		    rest->next = o->next;
+		    joinl->next = rest;
+		} else
+		    joinl->next = o->next;
 
-    /* Insert the brace strings now? */
-    if (pl >= 0 && i >= pl && brbeg && *brbeg) {
-	inststrlen(brbeg, 1, -1);
-	pl = -1;
-	hb = 1;
-    }
-    if (sl >= 0 && i >= sl && brend && *brend) {
-	inststrlen(brend, 1, -1);
-	sl = -1;
-	hb = 1;
-    }
-    /* Walk the list. */
-    while (l) {
-	/* If this cline describes a suffix where something is missing
-	 * for some matches, remember the position. */
-	if (m < 0 && (l->flags & (CLF_MISS | CLF_SUF)) == (CLF_MISS | CLF_SUF))
-	    m = cs;
-
-	if (l->prefix) {
-	    Cline sl = l->prefix, ssl;
-	    int hm;
-
-	    if (l->flags & (CLF_END | CLF_MID)) {
-		/* At the end and in the middle the suffix we have
-		 * separate runs. */
-		for (; sl; sl = sl->next) {
-		    hm = 0;
-		    if (sl->prefix) {
-			for (ssl = sl->prefix; ssl; ssl = ssl->next) {
-			    if (ssl->line)
-				/* line for a string derived from applying
-				 * the matchers the other way round. */
-				inststrlen(ssl->line, 1, ssl->llen);
-			    else
-				/* and word for substrings equal in all
-				 * matches. */
-				inststrlen(ssl->word, 1, ssl->wlen);
-			    /* If the string differs from any of the 
-			     * matches, remember the position. */
-			    if (d < 0 && (ssl->flags & CLF_DIFF))
-				d = cs;
-			}
-		    } else if (sl->wlen)
-			inststrlen(sl->word, 1, sl->wlen);
-
-		    if (m < 0 && (sl->flags & CLF_MISS))
-			m = cs;
-		    if (sl->llen)
-			inststrlen(sl->line, 1, sl->llen);
-		    hm = (sl->flags & CLF_MISS);
-		}
-		if ((l->flags & CLF_MID) &&hm && b < 0) {
-		    b = cs;
-		    hb = 1;
-		}
-	    } else {
-		/* The cline contains a newly build part of the string 
-		 * in a sub-list. */
-		for (; sl; sl = sl->next) {
-		    if (sl->line)
-			/* line for a string derived from applying the 
-			 * matchers the other way round. */
-			inststrlen(sl->line, 1, sl->llen);
+		if (len) {
+		    if (sfx)
+			*sstr += *slen - len;
+		    *slen = len;
+		    o->next = joinl;
+		} else {
+		    o->next = NULL;
+		    free_cline(o);
+		    if (p)
+			p->next = joinl;
+		    else if (sfx)
+			ot->suffix = joinl;
 		    else
-			/* and word for substrings equal in all matches. */
-			inststrlen(sl->word, 1, sl->wlen);
-		    /* If the string differs from any of the matches,
-		     * remember the position. */
-		    if (d < 0 && (sl->flags & CLF_DIFF))
-			d = cs;
+			ot->prefix = joinl;
 		}
+		o = joinl;
+		join = 0;
 	    }
-	    if (!(l->flags & (CLF_END | CLF_MID)))
-		i += l->llen;
 	}
-	if (l->suffix) {
-	    Cline sl = revert_clines(l->suffix);
+	if (join) {
+	    /* We couldn't build a cline for a common string, so we
+	     * cut the list here. */
+	    if (len) {
+		Cline r;
 
-	    if ((sl->flags & CLF_MISS) && b < 0) {
-		b = cs;
-		hb = 1;
-	    }
-	    for (; sl; sl = sl->next) {
-		if (sl->line)
-		    inststrlen(sl->line, 1, sl->llen);
-		else
-		    inststrlen(sl->word, 1, sl->wlen);
-		if (d < 0 && (sl->flags & CLF_DIFF))
-		    d = cs;
-	    }
-	}
-	if (l->flags & CLF_MID) {
-	    /* The cline in the middle, insert the prefix and the 
-	     * suffix. */
-	    if (!l->prefix && l->llen) {
-		inststrlen(l->line, 1, l->llen);
-		if (b < 0) {
-		    b = cs;
-		    hb = l->flags & CLF_MISS;
+		if (orest) {
+		    if (line)
+			r = get_cline(o->line + len, *slen - len,
+				      NULL, 0, NULL, 0, o->flags);
+		    else
+			r = get_cline(NULL, 0, o->word + len, *slen - len,
+				      NULL, 0, o->flags);
+
+		    r->next = o->next;
+		    *orest = r;
+
+		    *slen = len;
+		    o->next = NULL;
+		} else {
+		    if (sfx)
+			*sstr += *slen - len;
+		    *slen = len;
+		    free_cline(o->next);
+		    o->next = NULL;
 		}
-	    }
-	    if (!l->suffix && l->wlen > 0)
-		inststrlen(l->word, 1, l->wlen);
-	} else if (!l->prefix && !l->suffix) {
-	    if (l->word &&
-		!((pl >= 0 && brbeg && *brbeg &&
-		   i < pl && i + l->llen >= pl) ||
-		  (sl >= 0 && brend && *brend &&
-		   i < sl && i + l->llen >= sl))) {
-		/* We insert the prefered string stored in word only if we
-		 * don't have to put the brace beginning or end here. */
-		inststrlen(l->word, 1, l->wlen);
 	    } else {
-		/* Otherwise just re-insert the original string. */
-		inststrlen(l->line, 1, l->llen);
+		if (p)
+		    p->next = NULL;
+		else if (sfx)
+		    ot->suffix = NULL;
+		else
+		    ot->prefix = NULL;
+
+		if (orest)
+		    *orest = o;
+		else
+		    free_cline(o);
 	    }
-	    i += l->llen;
-	}
-	/* Remember the position if there is a difference or a missing
-	 * substring. */
-	if (d < 0 && (l->flags & CLF_DIFF))
-	    d = cs;
-	if (m < 0 && (l->flags & CLF_MISS))
-	    m = cs;
-
-	/* Probably insert the brace beginning or end. */
-	if (pl >= 0 && i >= pl && brbeg && *brbeg) {
-	    cs -= i - pl;
-	    inststrlen(brbeg, 1, -1);
-	    cs += i - pl;
-	    pl = -1;
-	    hb = 1;
-	}
-	if (sl >= 0 && i > sl && brend && *brend) {
-	    cs -= i - sl;
-	    inststrlen(brend, 1, -1);
-	    cs += i - sl;
-	    sl = -1;
-	    hb = 1;
+	    if (!orest || !nrest)
+		ot->flags |= CLF_MISS;
+
+	    if (nrest)
+		*nrest = undo_cmdata(&md, sfx);
+
+	    return;
 	}
-	l = l->next;
+	p = o;
+	o = o->next;
     }
-    lastend = cs;
-    /* Now place the cursor. Preferably in a position where something
-     * is missing, otherwise in a place where the string differs from
-     * any of the matches, or just leave it at the end. */
-    cs = (b >= 0 && hb ? b : (m >= 0 ? m : (d >= 0 ? d : cs)));
-    if (cs > ll)
-	cs = ll;
+    if (md.len || md.cl)
+	ot->flags |= CLF_MISS;
+    if (orest)
+	*orest = NULL;
+    if (nrest)
+	*nrest = undo_cmdata(&md, sfx);
 }
 
-/* Check if the given pattern matches the given string.             *
- * `in' and `out' are used for {...} classes. In `out' we store the *
- * character number that was matched. In the word pattern this is   *
- * given in `in' so that we can easily test if we found the         *
- * corresponding character. */
+/* This builds the common prefix and suffix for a mid-cline -- the one
+ * describing the place where the prefix and the suffix meet. */
 
-/**/
-static int
-pattern_match(Cpattern p, char *s, unsigned char *in, unsigned char *out)
+static void
+join_mid(Cline o, Cline n)
 {
-    unsigned char c;
+    if (o->flags & CLF_JOIN) {
+	/* The JOIN flag is set in the old cline struct if it was
+	 * already joined with another one. In this case the suffix
+	 * field contains the suffix from previous calls. */
+	Cline nr;
 
-    while (p) {
-	c = *((unsigned char *) s);
+	join_psfx(o, n, NULL, &nr, 0);
 
-	if (out)
-	    *out = 0;
+	n->suffix = revert_cline(nr);
 
-	if (p->equiv) {
-	    if (in) {
-		c = p->tab[c];
-		if ((*in && *in != c) || (!*in && !c))
-		    return 0;
-	    } else if (out) {
-		if (!(*out = p->tab[c]))
-		    return 0;
-	    } else if (!p->tab[c])
-		return 0;
+	join_psfx(o, n, NULL, NULL, 1);
+    } else {
+	/* This is the first time for both structs, so the prefix field
+	 * contains the whole sub-list. */
+	Cline or, nr;
 
-	    if (in && *in)
-		in++;
-	    if (out)
-		out++;
-	} else if (!p->tab[c])
-	    return 0;
+	o->flags |= CLF_JOIN;
 
-	s++;
-	p = p->next;
+	/* We let us give both rests and use them as the suffixes. */
+	join_psfx(o, n, &or, &nr, 0);
+
+	if (or)
+	    or->llen = (o->slen > or->wlen ? or->wlen : o->slen);
+	o->suffix = revert_cline(or);
+	n->suffix = revert_cline(nr);
+
+	join_psfx(o, n, NULL, NULL, 1);
     }
-    return 1;
+    n->suffix = NULL;
 }
 
-/* Do the matching for a prefix. l and w contain the strings on the line and
- * for the generated word, respectively. In nlp we return a cline list for this
- * match. In lp we return the length of the matched string. rlp is used to
- * return a pointer to the last cline struct in the list returned in nlp, and
- * in bplp we return the relative position where the brace beginning would
- * have to be insrted in the string returned, which is the string to use
- * as the completion. */
+/* This simplifies the cline list given as the first argument so that
+ * it also matches the second list. */
 
-static char *
-match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp)
+static Cline
+join_clines(Cline o, Cline n)
 {
-    static unsigned char *ea;
-    static int ealen = 0;
-    static char *rw;
-    static int rwlen;
-
-    int ll = strlen(l), lw = strlen(w), oll = ll, olw = lw, mlw;
-    int il = 0, iw = 0, t, bc = brpl;
-    char *nw = rw;
-    Cmlist ms;
-    Cmatcher mp, lm = NULL;
-    Cline lr = NULL;
-
-    if (nlp) {
-	*nlp = NULL;
-
-	if (ll + 1 > ealen) {
-	    /* This is the `in'/`out' string for pattern matching. */
-	    if (ealen)
-		zfree(ea, ealen);
-	    ea = (unsigned char *) zalloc(ealen = ll + 20);
-	}
-    }
-    while (ll && lw) {
-	t = 0;
-	/* First try the matchers. */
-	for (ms = mstack; ms; ms = ms->next) {
-	    for (mp = ms->matcher; mp; mp = mp->next) {
-		if (lm == mp)
-		    continue;
+    /* First time called, just return the new list. On further invocations
+     * we will get it as the first argument. */
+    if (!o)
+	return n;
+    else {
+	Cline oo = o, nn = n, po = NULL, pn = NULL;
 
-		t = 1;
-		if ((oll == ll || olw == lw) && !nlp && mp->wlen < 0)
-		    /* If we were called recursively, don't use `*' patterns
-		     * at the beginning (avoiding infinite recursion). */
-		    t = 0;
-		else if (mp->flags & CMF_LEFT) {
-		    /* Try to match the left anchor, if any. */
-		    if (il < mp->lalen || iw < mp->lalen)
-			t = 0;
-		    else if (mp->left)
-			t = pattern_match(mp->left, l - mp->lalen, NULL, NULL) &&
-			    pattern_match(mp->left, w - mp->lalen, NULL, NULL);
-		    else
-			t = (!il && !iw);
-		}
-		if (t) {
-		    /* Now match the line pattern. */
-		    if (ll < mp->llen || lw < mp->wlen)
-			t = 0;
-		    else if (mp->wlen < 0) {
-			/* This is reached if we have a `*' pattern. */
-			if ((t = pattern_match(mp->line, l, NULL, NULL))) {
-			    if (mp->flags & CMF_RIGHT)
-				/* Check if the anchor matches what's on the
-				 * line. If it also matches the word, we don't
-				 * use the matcher since we don't want one of
-				 * these patterns on the line to match more
-				 * than one such sub-string in the word. */
-				t = (mp->right && ll >= mp->llen + mp->ralen &&
-				     pattern_match(mp->right, l + mp->llen,
-						   NULL, NULL) &&
-				     lw >= mp->ralen &&
-				     !pattern_match(mp->right, w, NULL, NULL));
-			    if (t) {
-				/* The anchor matched, so find out how many
-				 * characters are matched by the `*' pattern.
-				 * We do this by looping over the string
-				 * and calling this function recursively. */
-				int i = 0, j = iw, k = lw, m = 0;
-				int jj = il + mp->llen, kk = ll - mp->llen;
-				char *p = l + mp->llen, *q = w;
-
-				for (; k; i++, j++, k--, q++) {
-				    if ((mp->flags & CMF_RIGHT) &&
-					(mp->right && k >= mp->ralen &&
-					 pattern_match(mp->right, q, 
-						       NULL, NULL))) {
-					if (m++) {
-					    k = 0;
-					    break;
-					}
-				    }
-				    if (match_pfx(p, q, NULL, NULL, NULL, NULL))
-					break;
-				}
-				if (k && i) {
-				    /* We found a position where the rest of
-				     * the line matched again (k != 0) and
-				     * we skipped over at least one character
-				     * (i != 0), so add a cline for this */
-				    if (nlp) {
-					nw = addtoword(&rw, &rwlen, nw, mp,
-						       l, w, i, 0);
-					addtocline(nlp, &lr, l, mp->llen,
-						   w, i, mp, 
-						   ((mp->flags & CMF_LEFT) ?
-						    CLF_SUF : 0) | CLF_VAR);
-				    }
-				    /* ...and adjust the pointers and counters
-				     * to continue after the matched portion. */
-				    w = q;
-				    iw = j;
-				    lw = k;
-				    l = p;
-				    il = jj;
-				    ll = kk;
-				    bc -= mp->llen;
-
-				    /* In bc we count the characters used
-				     * backward starting with the original
-				     * position of the brace beginning. So, 
-				     * if it becomes less than or equal to
-				     * zero, we have reached the place where
-				     * the brace string would have to be 
-				     * inserted. */
-				    if (bc <= 0 && bplp) {
-					*bplp = nw - rw;
-					bplp = NULL;
-				    }
-				    lm = mp;
-				    break;
-				} else
-				    t = 0;
-			    } else
-				t = 0;
-			}
-		    } else {
-			/* No `*', just try to match the line and word *
-			 * patterns. */
-			t = pattern_match(mp->line, l, NULL, ea) &&
-			    pattern_match(mp->word, w, ea, NULL);
-			mlw = mp->wlen;
-		    }
-		}
-		/* Now test the right anchor, if any. */
-		if (t && (mp->flags & CMF_RIGHT)) {
-		    if (ll < mp->llen + mp->ralen || lw < mlw + mp->ralen)
-			t = 0;
-		    else if (mp->right)
-			t = pattern_match(mp->right, l + mp->llen, NULL, NULL) &&
-			    pattern_match(mp->right, w + mlw, NULL, NULL);
+	/* Walk through the lists. */
+	while (o && n) {
+	    /* If one of them describes a new part and the other one does
+	     * not, synchronise them by searching an old part in the
+	     * other list. */
+	    if ((o->flags & CLF_NEW) && !(n->flags & CLF_NEW)) {
+		Cline t, tn;
+
+		for (t = o; (tn = t->next) && (tn->flags & CLF_NEW); t = tn);
+		if (tn && cmp_anchors(tn, n, 0)) {
+		    Cline tmp;
+
+		    tmp = o->prefix;
+		    o->prefix = tn->prefix;
+		    tn->prefix = tmp;
+
+		    if (po)
+			po->next = tn;
 		    else
-			t = 0;
-		}
-		if (t) {
-		    /* If it matched, build a new chunk on the Cline list *
-		     * and add the string to the built match. */
-		    if (nlp) {
-			nw = addtoword(&rw, &rwlen, nw, mp, l, w, mlw, 0);
-			addtocline(nlp, &lr, l, mp->llen, w, mlw, mp, 0);
-		    }
-		    l += mp->llen;
-		    w += mlw;
-		    ll -= mp->llen;
-		    lw -= mlw;
-		    il += mp->llen;
-		    iw += mlw;
-		    bc -= mp->llen;
-
-		    if (bc <= 0 && bplp) {
-			*bplp = nw - rw;
-			bplp = NULL;
-		    }
-		    break;
+			oo = tn;
+		    t->next = NULL;
+		    free_cline(o);
+		    o = tn;
+		    o->flags |= CLF_MISS;
+		    continue;
 		}
 	    }
-	    if (mp) {
-		if (mp != lm)
-		    lm = NULL;
-		break;
-	    }
-	}
-	if (t)
-	    continue;
-	if (*l == *w) {
-	    /* Same character, take it. */
-	    if (nlp) {
-		nw = addtoword(&rw, &rwlen, nw, NULL, NULL, l, 1, 0);
-		addtocline(nlp, &lr, l, 1, NULL, 0, NULL, 0);
-	    }
-	    l++;
-	    w++;
-	    il++;
-	    iw++;
-	    ll--;
-	    lw--;
-	    bc--;
-
-	    if (bc <= 0 && bplp) {
-		*bplp = nw - rw;
-		bplp = NULL;
-	    }
-	    lm = NULL;
-	} else {
-	    if (nlp && *nlp) {
-		lr->next = freecl;
-		freecl = *nlp;
-	    }
-	    return NULL;
-	}
-    }
-    if (lp)
-	*lp = iw;
-    if (lw) {
-	if (nlp) {
-	    /* There is a unmatched portion in the word, keep it. */
-	    if (rlp) {
-		w = dupstring(w);
-		addtocline(nlp, &lr, w, lw, w, -1, NULL, CLF_MID);
-
-		*rlp = lr;
-	    } else {
-		addtocline(nlp, &lr, l, 0, dupstring(w), lw, NULL,
-			   CLF_VAR | CLF_END);
-		nw = addtoword(&rw, &rwlen, nw, NULL, NULL, w, lw, 0);
-	    }
-	}
-    } else if (rlp) {
-	if (nlp && lr) {
-	    lr->next = freecl;
-	    freecl = *nlp;
-	}
-	return NULL;
-    }
-    if (nlp && nw)
-	*nw = '\0';
+	    if (!(o->flags & CLF_NEW) && (n->flags & CLF_NEW)) {
+		Cline t, tn;
 
-    if (ll) {
-	if (nlp && *nlp) {
-	    lr->next = freecl;
-	    freecl = *nlp;
-	}
-	return NULL;
-    }
-    if (nlp)
-	/* Finally, return the built match string. */
-	return dupstring(rw);
-    
-    return ((char *) 1);
-}
+		for (t = n; (tn = t->next) && (tn->flags & CLF_NEW); t = tn);
+		if (tn && cmp_anchors(o, tn, 0)) {
+		    Cline tmp;
 
-/* Do the matching for a suffix. Almost the same as match_pfx(), only in the
-* other direction. */
+		    tmp = n->prefix;
+		    n->prefix = tn->prefix;
+		    tn->prefix = tmp;
 
-static char *
-match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp)
-{
-    static unsigned char *ea;
-    static int ealen = 0;
-    static char *rw;
-    static int rwlen;
-
-    int ll = strlen(l), lw = strlen(w), mlw;
-    int il = 0, iw = 0, t, bc = brsl;
-    char *nw = rw;
-    Cmlist ms;
-    Cmatcher mp, lm = NULL;
-    Cline lr = NULL;
-
-    l += ll;
-    w += lw;
-
-    if (nlp) {
-	*nlp = NULL;
-
-	if (ll + 1 > ealen) {
-	    if (ealen)
-		zfree(ea, ealen);
-	    ea = (unsigned char *) zalloc(ealen = ll + 20);
-	}
-    }
-    while (ll && lw) {
-	t = 0;
-	for (ms = mstack; ms; ms = ms->next) {
-	    for (mp = ms->matcher; mp; mp = mp->next) {
-		if (lm == mp)
+		    n = tn;
+		    o->flags |= CLF_MISS;
 		    continue;
-
-		t = 1;
-		if (mp->flags & CMF_RIGHT) {
-		    if (il < mp->ralen || iw < mp->ralen)
-			t = 0;
-		    else if (mp->right)
-			t = pattern_match(mp->right, l, NULL, NULL) &&
-			    pattern_match(mp->right, w, NULL, NULL);
-		    else
-			t = (!il && !iw);
 		}
-		if (t) {
-		    if (ll < mp->llen || lw < mp->wlen)
-			t = 0;
-		    else if (mp->wlen < 0) {
-			if ((t = pattern_match(mp->line, l - mp->llen,
-					       NULL, NULL))) {
-			    if (mp->flags & CMF_LEFT)
-				t = (mp->left && ll >= mp->llen + mp->lalen &&
-				     pattern_match(mp->left,
-						   l - mp->llen - mp->lalen,
-						   NULL, NULL) &&
-				     lw >= mp->lalen &&
-				     !pattern_match(mp->left, w - mp->lalen,
-						    NULL, NULL));
-			    if (t) {
-				int i = 0, j = iw, k = lw, m = 0;
-				int jj = il + mp->llen, kk = ll - mp->llen;
-				char *p = l - mp->llen - 1, *q = w - 1;
-
-				for (; k; i++, j++, k--, q--) {
-				    if ((mp->flags & CMF_LEFT) &&
-					(mp->left && k >= mp->lalen &&
-					 pattern_match(mp->left, q - mp->lalen, 
-						       NULL, NULL))) {
-					if (m++) {
-					    k = 0;
-					    break;
-					}
-				    }
-				    if (match_pfx(p, q, NULL, NULL, NULL, NULL))
-					break;
-				}
-				if (k && i) {
-				    if (nlp) {
-					nw = addtoword(&rw, &rwlen, nw, mp,
-						       l - mp->llen, w - i,
-						       i, 1);
-					addtocline(nlp, &lr, l - mp->llen,
-						   mp->llen, w - i, i, mp, 
-						   ((mp->flags & CMF_LEFT) ?
-						    CLF_SUF : 0) | CLF_VAR);
-				    }
-				    w = q + 1;
-				    iw = j;
-				    lw = k;
-				    l = p + 1;
-				    il = jj;
-				    ll = kk;
-				    bc -= mp->llen;
-
-				    if (bc <= 0 && bslp) {
-					*bslp = nw - rw;
-					bslp = NULL;
-				    }
-				    lm = mp;
-				    break;
-				} else
-				    t = 0;
-			    }
-			}
-		    } else {
-			t = pattern_match(mp->line, l - mp->llen, NULL, ea) &&
-			    pattern_match(mp->word, w - mp->wlen, ea, NULL);
-			mlw = mp->wlen;
-		    }
+	    }
+	    /* Almost the same as above, but for the case that they
+	     * describe different types of parts (prefix, suffix, or mid). */
+	    if ((o->flags & (CLF_SUF | CLF_MID)) !=
+		(n->flags & (CLF_SUF | CLF_MID))) {
+		Cline t, tn;
+
+		for (t = n;
+		     (tn = t->next) &&
+			 (tn->flags & (CLF_SUF | CLF_MID)) !=
+			 (o->flags  & (CLF_SUF | CLF_MID));
+		     t = tn);
+		if (tn && cmp_anchors(o, tn, 1)) {
+		    n = tn;
+		    continue;
 		}
-		if (t && (mp->flags & CMF_LEFT)) {
-		    if (ll < mp->llen + mp->lalen || lw < mlw + mp->lalen)
-			t = 0;
-		    else if (mp->left)
-			t = pattern_match(mp->right, l - mp->llen - mp->lalen,
-					  NULL, NULL) &&
-			    pattern_match(mp->right, w - mlw - mp->lalen,
-					  NULL, NULL);
+		for (t = o;
+		     (tn = t->next) &&
+			 (tn->flags & (CLF_SUF | CLF_MID)) !=
+			 (n->flags  & (CLF_SUF | CLF_MID));
+		     t = tn);
+		if (tn && cmp_anchors(tn, n, 1)) {
+		    if (po)
+			po->next = tn;
 		    else
-			t = 0;
+			oo = tn;
+		    t->next = NULL;
+		    free_cline(o);
+		    o = tn;
+		    continue;
 		}
-		if (t) {
-		    if (nlp) {
-			nw = addtoword(&rw, &rwlen, nw, mp, l - mp->llen,
-				       w - mlw, mlw, 1);
-			addtocline(nlp, &lr, l - mp->llen, mp->llen,
-				   w - mlw, mlw, mp, 0);
-		    }
-		    l -= mp->llen;
-		    w -= mlw;
-		    ll -= mp->llen;
-		    lw -= mlw;
-		    il += mp->llen;
-		    iw += mlw;
-		    bc -= mp->llen;
-		    if (bc <= 0 && bslp) {
-			*bslp = nw - rw;
-			bslp = NULL;
+		if (o->flags & CLF_MID) {
+		    o->flags = (o->flags & ~CLF_MID) | (n->flags & CLF_SUF);
+		    if (n->flags & CLF_SUF) {
+			free_cline(o->prefix);
+			o->prefix = NULL;
+		    } else {
+			free_cline(o->suffix);
+			o->suffix = NULL;
 		    }
-		    break;
 		}
-	    }
-	    if (mp) {
-		if (mp != lm)
-		    lm = NULL;
 		break;
 	    }
-	}
-	if (t)
-	    continue;
-	if (l[-1] == w[-1]) {
-	    if (nlp) {
-		nw = addtoword(&rw, &rwlen, nw, NULL, NULL, l - 1, 1, 1);
-		addtocline(nlp, &lr, l - 1, 1, NULL, 0, NULL, 0);
-	    }
-	    l--;
-	    w--;
-	    il++;
-	    iw++;
-	    ll--;
-	    lw--;
-	    bc--;
-	    if (bc <= 0 && bslp) {
-		*bslp = nw - rw;
-		bslp = NULL;
-	    }
-	    lm = NULL;
-	} else {
-	    if (nlp && *nlp) {
-		lr->next = freecl;
-		freecl = *nlp;
+	    /* Now see if they have matching anchors. If not, cut the list. */
+	    if (!(o->flags & CLF_MID) && !cmp_anchors(o, n, 1)) {
+		Cline t, tn;
+
+		for (t = n; (tn = t->next) && !cmp_anchors(o, tn, 1); t = tn);
+
+		if (tn) {
+		    n = tn;
+		    continue;
+		} else {
+		    if (o->flags & CLF_SUF)
+			break;
+
+		    o->word = o->line = o->orig = NULL;
+		    o->wlen = 0;
+		    free_cline(o->next);
+		    o->next = NULL;
+		}
 	    }
-	    return NULL;
+	    /* Ok, they are equal, now join the sub-lists. */
+	    if (o->flags & CLF_MID)
+		join_mid(o, n);
+	    else
+		join_psfx(o, n, NULL, NULL, (o->flags & CLF_SUF));
+
+	    po = o;
+	    o = o->next;
+	    pn = n;
+	    n = n->next;
 	}
-    }
-    if (lp)
-	*lp = iw;
-    if (nlp && nw)
-	*nw = '\0';
+	/* Free the rest of the old list. */
+	if (o) {
+	    if (po)
+		po->next = NULL;
+	    else
+		oo = NULL;
 
-    if (ll) {
-	if (nlp && *nlp) {
-	    lr->next = freecl;
-	    freecl = *nlp;
+	    free_cline(o);
 	}
-	return NULL;
-    }
-    if (nlp)
-	return dupstring(rw);
+	free_cline(nn);
 
-    return ((char *) 1);
+	return oo;
+    }
 }
 
-/* Check if the word `w' is matched by the strings in pfx and sfx (the prefix
- * and the suffix from the line. In clp a cline list is returned for w.
- * qu is non-zero if the words has to be quoted before processed any further.
- * bpl and bsl are used to report the positions where the brace-strings in
- * the prefix and the suffix have to be re-inserted if this match is inserted
- * in the line.
- * The return value is the string to use as a completion or NULL if the prefix
- * and the suffix don't match the word w. */
+/* This adds all the data we have for a match. */
 
-static char *
-comp_match(char *pfx, char *sfx, char *w, Cline *clp, int qu, int *bpl, int *bsl)
+static Cmatch
+add_match_data(int alt, char *str, Cline line,
+	       char *ipre, char *ripre, char *isuf,
+	       char *pre, char *prpre, char *ppre, char *psuf, char *suf,
+	       int bpl, int bsl, int flags, int exact)
 {
-    char *r = NULL;
-    Cline pli;
-    int pl;
-
-    if (qu)
-	w = quotename(w, NULL, NULL, NULL);
-
-    if (*sfx) {
-	/* We have a suffix, so this gets a bit more complicated. */
-	char *p, *s;
-	int sl;
-	Cline sli, last;
-
-	/* First see if the prefix matches. In pl we get the length of
-	 * the string matched by it. */
-	if ((p = match_pfx(pfx, w, &pli, &pl, &last, bpl))) {
-	    /* Then try to match the rest of the string with the suffix. */
-	    if ((s = match_sfx(sfx, w + pl, &sli, &sl, bsl))) {
-		int pml, sml;
-
-		/* Now append the cline list for the suffix to the one
-		 * for the prefix. */
-		last->llen -= sl;
-		last->next = revert_clines(sli);
-
-		/* And store the correct parts of the prefix and the suffix
-		 * in the cline struct in the middle. */
-		pml = strlen(p);
-		sml = strlen(s);
-		r = (char *) zhalloc(pml + sml + last->llen + 1);
-		strcpy(r, p);
-		strncpy(r + pml, last->line, last->llen);
-		strcpy(r + pml + last->llen, s);
-	    } else {
-		/* Suffix didn't match, so free the cline list for the
-		 * prefix. */
-		last->next = freecl;
-		freecl = pli;
-
-		return NULL;
-	    }
-	} else
-	    return NULL;
-    } else if (!(r = match_pfx(pfx, w, &pli, &pl, NULL, bpl)))
-	/* We had only the prefix to match and that didn't match. */
-	return NULL;
+    Cmatch cm;
+    Aminfo ai = (alt ? fainfo : ainfo);
+    int palen = 0, salen = 0, ipl = 0, pl = 0, ppl = 0, isl = 0, psl = 0;
 
-    /* If there are path prefixes or suffixes, pre- and append them to
-     * the cline list built. */
-    if (lppre && *lppre) {
-	Cline l, t = str_cline(lppre, -1, &l);
+    DPUTS(!line, "BUG: add_match_data() without cline");
 
-	l->next = pli;
-	pli = t;
-    }
-    if (lpsuf && *lpsuf) {
-	Cline n, t = str_cline(lpsuf, -1, NULL);
+    /* If there is a path suffix, we build a cline list for it and
+     * append it to the list for the match itself. */
+    if (psuf)
+	salen = (psl = strlen(psuf));
+    if (isuf)
+	salen += (isl = strlen(isuf));
 
-	if ((n = pli)) {
-	    while (n->next) n = n->next;
+    if (salen) {
+	char *asuf = (char *) zhalloc(salen);
+	Cline pp, p, s;
 
-	    n->next = t;
-	} else
-	    pli = t;
-    }
-    *clp = pli;
+	if (psl)
+	    memcpy(asuf, psuf, psl);
+	if (isl)
+	    memcpy(asuf + psl, isuf, isl);
 
-    return r;
-}
+	s = bld_parts(asuf, salen, salen, NULL);
 
-/* Insert the given string into the command line.  If move is non-zero, *
- * the cursor position is changed and len is the length of the string   *
- * to insert (if it is -1, the length is calculated here).              */
+	for (pp = NULL, p = line; p->next; pp = p, p = p->next);
 
-/**/
-static void
-inststrlen(char *str, int move, int len)
-{
-    if (!len || !str)
-	return;
-    if (len == -1)
-	len = strlen(str);
-    spaceinline(len);
-    strncpy((char *)(line + cs), str, len);
-    if (move)
-	cs += len;
-}
+	if (!(p->flags & (CLF_SUF | CLF_MID)) &&
+	    !p->llen && !p->wlen && !p->olen) {
+	    if (p->prefix) {
+		Cline q;
 
-/* Insert the given match. This returns the number of characters inserted.*/
+		for (q = p->prefix; q->next; q = q->next);
+		q->next = s->prefix;
+		s->prefix = p->prefix;
+		p->prefix = NULL;
+	    }
+	    free_cline(p);
+	    if (pp)
+		pp->next = s;
+	    else
+		line = s;
+	} else
+	    p->next = s;
+    }
+    /* And the same for the prefix. */
+    if (ipre)
+	palen = (ipl = strlen(ipre));
+    if (pre)
+	palen += (pl = strlen(pre));
+    if (ppre)
+	palen += (ppl = strlen(ppre));
+
+    if (palen) {
+	char *apre = (char *) zhalloc(palen);
+	Cline p, lp;
+
+	if (ipl)
+	    memcpy(apre, ipre, ipl);
+	if (pl)
+	    memcpy(apre + ipl, pre, pl);
+	if (ppl)
+	    memcpy(apre + ipl + pl, ppre, ppl);
+
+	p = bld_parts(apre, palen, palen, &lp);
+	if (lp->prefix && !(line->flags & (CLF_SUF | CLF_MID))) {
+	    lp->prefix->next = line->prefix;
+	    line->prefix = lp->prefix;
+	    lp->prefix = NULL;
+
+	    free_cline(lp);
+
+	    if (p != lp) {
+		Cline q;
+
+		for (q = p; q->next != lp; q = q->next);
+
+		q->next = line;
+		line = p;
+	    }
+	} else {
+	    lp->next = line;
+	    line = p;
+	}
+    }
+    /* Then build the unambiguous cline list. */
+    ai->line = join_clines(ai->line, line);
 
-/**/
-static int
-instmatch(Cmatch m)
-{
-    int l, r = 0, ocs, a = cs;
+    mnum++;
+    ai->count++;
+    
+    /* Allocate and fill the match structure. */
+    cm = (Cmatch) zhalloc(sizeof(struct cmatch));
+    cm->str = str;
+    cm->ppre = (ppre && *ppre ? ppre : NULL);
+    cm->psuf = (psuf && *psuf ? psuf : NULL);
+    cm->prpre = ((flags & CMF_FILE) && prpre && *prpre ? prpre : NULL);
+    cm->ipre = (ipre && *ipre ? ipre : NULL);
+    cm->ripre = (ripre && *ripre ? ripre : NULL);
+    cm->isuf = (isuf && *isuf ? isuf : NULL);
+    cm->pre = pre;
+    cm->suf = suf;
+    cm->flags = flags;
+    cm->brpl = bpl;
+    cm->brsl = bsl;
+    cm->rems = cm->remf = NULL;
+    addlinknode((alt ? fmatches : matches), cm);
 
-    /* Ignored prefix. */
-    if (m->ipre) {
-	inststrlen(m->ipre, 1, (l = strlen(m->ipre)));
-	r += l;
-    }
-    /* -P prefix. */
-    if (m->pre) {
-	inststrlen(m->pre, 1, (l = strlen(m->pre)));
-	r += l;
-    }
-    /* Path prefix. */
-    if (m->ppre) {
-	inststrlen(m->ppre, 1, (l = strlen(m->ppre)));
-	r += l;
-    }
-    /* The string itself. */
-    inststrlen(m->str, 1, (l = strlen(m->str)));
-    r += l;
-    ocs = cs;
-    /* Re-insert the brace beginning, if any. */
-    if (brbeg && *brbeg) {
-	cs = a + m->brpl + (m->pre ? strlen(m->pre) : 0);
-	l = strlen(brbeg);
-	brpcs = cs;
-	inststrlen(brbeg, 1, l);
-	r += l;
-	ocs += l;
-	cs = ocs;
-    }
-    /* Path suffix. */
-    if (m->psuf) {
-	inststrlen(m->psuf, 1, (l = strlen(m->psuf)));
-	r += l;
+    /* One more match for this explanation. */
+    if (expl) {
+	if (alt)
+	    expl->fcount++;
+	else
+	    expl->count++;
     }
-    /* Re-insert the brace end. */
-    if (brend && *brend) {
-	a = cs;
-	cs -= m->brsl;
-	ocs = brscs = cs;
-	l = strlen(brend);
-	inststrlen(brend, 1, l);
-	r += l;
-	cs = a + l;
-    } else
-	brscs = -1;
-    /* -S suffix */
-    if (m->suf) {
-	inststrlen(m->suf, 1, (l = strlen(m->suf)));
-	r += l;
+    if (!ai->firstm)
+	ai->firstm = cm;
+
+    /* Do we have an exact match? More than one? */
+    if (exact) {
+	if (!ai->exact) {
+	    ai->exact = 1;
+	    if (incompfunc) {
+		/* If a completion widget is active, we make the exact
+		 * string available in `compstate'. */
+
+		int sl = strlen(str);
+		int lpl = (cm->ppre ? strlen(cm->ppre) : 0);
+		int lsl = (cm->psuf ? strlen(cm->psuf) : 0);
+		char *e;
+
+		zsfree(compexactstr);
+		compexactstr = e = (char *) zalloc(lpl + sl + lsl + 1);
+		if (cm->ppre) {
+		    strcpy(e, cm->ppre);
+		    e += lpl;
+		}
+		strcpy(e, str);
+		e += sl;
+		if (cm->psuf)
+		    strcpy(e, cm->psuf);
+		comp_setunsetptr(CP_EXACTSTR, 0);
+	    }
+	    ai->exactm = cm;
+	} else {
+	    ai->exact = 2;
+	    ai->exactm = NULL;
+	    if (incompfunc)
+		comp_setunsetptr(0, CP_EXACTSTR);
+	}
     }
-    lastend = cs;
-    cs = ocs;
-    return r;
+    return cm;
 }
 
 /* This is used by compadd to add a couple of matches. The arguments are
@@ -4032,16 +3404,16 @@ instmatch(Cmatch m)
 
 /**/
 int
-addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
+addmatches(char *ipre, char *isuf,
+	   char *ppre, char *psuf, char *prpre, char *pre,
 	   char *suf, char *group, char *rems, char *remf, char *ign,
 	   int flags, int aflags, Cmatcher match, char *exp, char **argv)
 {
-    char *s, *t, *e, *me, *ms, *lipre = NULL, *lpre = NULL, *lsuf = NULL;
+    char *s, *ms, *lipre = NULL, *lisuf = NULL, *lpre = NULL, *lsuf = NULL;
     char **aign = NULL;
-    int lpl, lsl, i, pl, sl, test, bpl, bsl, llpl = 0, llsl = 0, nm = mnum;
-    Aminfo ai = NULL;
+    int lpl, lsl, pl, sl, bpl, bsl, llpl = 0, llsl = 0, nm = mnum;
+    int oisalt = 0, isalt, isexact;
     Cline lc = NULL;
-    LinkList l = NULL;
     Cmatch cm;
     struct cmlist mst;
     Cmlist oms = mstack;
@@ -4080,6 +3452,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 	     * to perform matching. */
 	    if (aflags & CAF_MATCH) {
 		lipre = dupstring(compiprefix);
+		lisuf = dupstring(compisuffix);
 		lpre = dupstring(compprefix);
 		lsuf = dupstring(compsuffix);
 		llpl = strlen(lpre);
@@ -4091,16 +3464,18 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		    lpre += pl;
 		}
 		if (comppatmatch && *comppatmatch) {
+		    int is = (*comppatmatch == '*');
 		    char *tmp = (char *) zhalloc(2 + llpl + llsl);
 
 		    strcpy(tmp, lpre);
 		    tmp[llpl] = 'x';
-		    strcpy(tmp + llpl + 1, lsuf);
+		    strcpy(tmp + llpl + is, lsuf);
 
 		    tokenize(tmp);
 		    remnulargs(tmp);
 		    if (haswilds(tmp)) {
-			tmp[llpl] = Star;
+			if (is)
+			    tmp[llpl] = Star;
 			if ((cp = parsereg(tmp)))
 			    haspattern = 1;
 		    }
@@ -4111,6 +3486,10 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		ipre = (lipre ? dyncat(lipre, ipre) : dupstring(ipre));
 	    else if (lipre)
 		ipre = lipre;
+	    if (isuf)
+		isuf = (lisuf ? dyncat(lisuf, isuf) : dupstring(isuf));
+	    else if (lisuf)
+		isuf = lisuf;
 	    if (ppre) {
 		ppre = dupstring(ppre);
 		lpl = strlen(ppre);
@@ -4162,237 +3541,45 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		    begcmgroup("default", 0);
 		}
 		/* Select the set of matches. */
-		if (aflags & CAF_ALT) {
-		    l = fmatches;
-		    ai = fainfo;
-		} else {
-		    l = matches;
-		    ai = ainfo;
-		}
+		oisalt = (aflags & CAF_ALT);
+
 		if (remf) {
 		    remf = dupstring(remf);
 		    rems = NULL;
 		} else if (rems)
 		    rems = dupstring(rems);
-		/* Build the common -P prefix. */
-		if (ai->pprefix) {
-		    if (pre)
-			ai->pprefix[pfxlen(ai->pprefix, pre)] = '\0';
-		    else
-			ai->pprefix[0] = '\0';
-		} else
-		    ai->pprefix = dupstring(pre ? pre : "");
 	    }
 	    /* Walk through the matches given. */
 	    for (; (s = dupstring(*argv)); argv++) {
-		sl = pl = strlen(s);
-		lc = NULL;
-		ms = NULL;
+		sl = strlen(s);
 		bpl = brpl;
 		bsl = brsl;
+		isalt = oisalt;
 		if ((!psuf || !*psuf) && aign) {
 		    /* Do the suffix-test. If the match has one of the
 		     * suffixes from ign, we put it in the alternate set. */
 		    char **pt = aign;
 		    int filell;
 
-		    for (test = 1; test && *pt; pt++)
+		    for (isalt = 0; !isalt && *pt; pt++)
 			if ((filell = strlen(*pt)) < sl
 			    && !strcmp(*pt, s + sl - filell))
-			    test = 0;
-
-		    if (!test) {
-			l = fmatches;
-			ai = fainfo;
-		    } else {
-			l = matches;
-			ai = ainfo;
-		    }
-		}
-		if (aflags & CAF_MATCH) {
-		    /* Do the matching. */
-		    if (cp) {
-			if ((test = domatch(s, cp, 0)))
-			    e = me = s + sl;
-			else
-			    continue;
-		    } else {
-			test = (sl >= llpl + llsl &&
-				strpfx(lpre, s) && strsfx(lsuf, s));
-			if (!test && mstack &&
-			    (ms = comp_match(lpre, lsuf, s,
-					     &lc, (aflags & CAF_QUOTE),
-					     &bpl, &bsl)))
-			    test = 1;
-
-			if (!test)
-			    continue;
-			pl = sl - llsl;
-			me = s + sl - llsl;
-			e = s + llpl;
-		    }
-		} else {
-		    e = s;
-		    me = s + sl;
-		    pl = sl;
-		}
-		/* Quoting? */
-		if (!(aflags & CAF_QUOTE)) {
-		    int tmp = me - s;
-
-		    s = quotename(s, &e, me, &tmp);
-		    me = s + tmp;
-		    sl = strlen(s);
-		}
-		/* The rest is almost the same as in addmatch(). */
-		if (!ms) {
-		    if (sl < ai->minlen)
-			ai->minlen = sl;
-		    if (!cp && !mstack && ai->firstm &&
-			(i = sfxlen(ai->firstm->str, s)) < ai->suflen)
-			ai->suflen = i;
-		}
-		t = s;
-		if (ppre)
-		    t = dyncat(ppre, t);
-		if (!cp && !ms && (mstack || psuf)) {
-		    int bl = ((aflags & CAF_MATCH) ? llpl : 0);
-		    Cline *clp = &lc, tlc;
-		    char *ss = dupstring(s), *ee = me + (ss - s);
-
-		    DPUTS(me < s || me > s + sl,
-			  "BUG: invalid end-pointer (me)");
-
-		    if (ppre && *ppre) {
-			*clp = tlc = getcline(NULL, 0, ppre, lpl, CLF_VAR);
-			clp = &(tlc->next);
-		    }
-		    if (bl) {
-			*clp = str_cline(ss, bl, &tlc);
-			clp = &(tlc->next);
-		    }
-		    if (ee != ss + sl) {
-			*clp = tlc = getcline(ss + bl, ee - ss - bl,
-					      NULL, 0, CLF_MID);
-			clp = &(tlc->next);
-			*clp = str_cline(ee, (ss + sl) - ee, &tlc);
-			clp = &(tlc->next);
-		    } else {
-			*clp = tlc = getcline(NULL, 0, ss + bl, sl - bl,
-					      CLF_END | CLF_VAR);
-			clp = &(tlc->next);
-		    }
-		    if (psuf && *psuf) {
-			*clp = tlc = getcline(NULL, 0, psuf, lsl,
-					      CLF_END | CLF_VAR);
-			clp = &(tlc->next);
-		    }
-		    *clp = NULL;
-		} else if (!cp && mstack) {
-		    Cline tlc;
-
-		    if (ppre && *ppre) {
-			tlc = getcline(NULL, 0, ppre, lpl, CLF_VAR);
-			tlc->next = lc;
-			lc = tlc;
-		    }
-		    if (psuf && *psuf) {
-			if (lc) {
-			    for (tlc = lc; tlc->next; tlc = tlc->next);
-			    tlc->next = getcline(NULL, 0, psuf, lsl,
-						 CLF_END | CLF_VAR);
-			} else
-			    lc = getcline(NULL, 0, psuf, lsl,
-					  CLF_END | CLF_VAR);
-		    }
+			    isalt = 1;
 		}
-		if (ipre && *ipre) {
-		    Cline tlc = prepend_cline(ipre, lc);
-
-		    ai->noipre = 0;
-		    if (!ms && !mstack) {
-			if ((aflags & CAF_MATCH) || ai->icpl > pl)
-			    ai->icpl = pl;
-			if ((aflags & CAF_MATCH) || ai->icsl > lsl)
-			    ai->icsl = lsl;
-			if (ai->iaprefix)
-			    ai->iaprefix[pfxlen(ai->iaprefix, t)] = '\0';
-			else
-			    ai->iaprefix = dupstring(t);
-		    } else
-			ai->ilinecl = join_clines(ai->ilinecl, lc);
-		    if (ai->iprefix) {
-			if (strcmp(ipre, ai->iprefix))
-			    ai->iprefix = "";
-		    } else
-			ai->iprefix = dupstring(ipre);
+		if (!(aflags & CAF_MATCH)) {
+		    ms = s;
+		    lc = bld_parts(s, sl, -1, NULL);
+		    isexact = 0;
+		} else if (!(ms = comp_match(lpre, lsuf, s, cp, &lc,
+					     (aflags & CAF_QUOTE),
+					     &bpl, &bsl, &isexact)))
+		    continue;
 
-		    t = dyncat(ipre, t);
-		    lc = tlc;
-		} else
-		    ai->iprefix = "";
-		if (!ms && !mstack && !lc) {
-		    if ((aflags & CAF_MATCH) || ai->cpl > pl)
-			ai->cpl = pl;
-		    if ((aflags & CAF_MATCH) || ai->csl > lsl)
-			ai->csl = lsl;
-		    if (ai->aprefix)
-			ai->aprefix[pfxlen(ai->aprefix, t)] = '\0';
-		    else
-			ai->aprefix = dupstring(t);
-		} else
-		    ai->linecl = join_clines(ai->linecl, lc);
-
-		mnum++;
-		ai->count++;
-
-		/* Finally add the match. */
-		cm = (Cmatch) zhalloc(sizeof(struct cmatch));
-		cm->ppre = ppre;
-		cm->psuf = psuf;
-		cm->prpre = prpre;
-		cm->str = (ms ? ms : dupstring(s));
-		cm->ipre = cm->ripre = (ipre && *ipre ? ipre : NULL);
-		cm->pre = pre;
-		cm->suf = suf;
-		cm->flags = flags;
-		cm->brpl = bpl;
-		cm->brsl = bsl;
-		cm->remf = remf;
+		cm = add_match_data(isalt, ms, lc, ipre, ipre, isuf, pre, prpre,
+				    ppre, psuf, suf, bpl, bsl,
+				    flags, isexact);
 		cm->rems = rems;
-		addlinknode(l, cm);
-
-		if (exp) {
-		    if (l == matches)
-			expl->count++;
-		    else
-			expl->fcount++;
-		}
-		if (!ms) {
-		    if (!ai->firstm)
-			ai->firstm = cm;
-		    if (!cp && (aflags & CAF_MATCH) && !(e - (s + pl))) {
-			if (!ai->exact) {
-			    ai->exact = 1;
-			    zsfree(compexactstr);
-			    compexactstr = e = (char *) zalloc(lpl + sl + lsl + 1);
-			    if (ppre) {
-				strcpy(e, ppre);
-				e += lpl;
-			    }
-			    strcpy(e, s);
-			    e += sl;
-			    if (psuf)
-				strcpy(e, psuf);
-			    comp_setunsetptr(CP_EXACTSTR, 0);
-			} else {
-			    ai->exact = 2;
-			    cm = NULL;
-			    comp_setunsetptr(0, CP_EXACTSTR);
-			}
-			ai->exactm = cm;
-		    }
-		}
+		cm->remf = remf;
 	    }
 	    compnmatches = mnum;
 	    if (exp)
@@ -4419,16 +3606,11 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 static void
 addmatch(char *s, char *t)
 {
-    int test = 0, sl = strlen(s), pl = rpl, cc = 0, isf = 0;
-    int mpl = 0, msl = 0, bpl = brpl, bsl = brsl;
-    char *e = NULL, *tt, *te, *ms = NULL;
-    Comp cp = patcomp;
+    int isfile = 0, isalt = 0, isexact, bpl = brpl, bsl = brsl;
+    char *ms = NULL, *tt;
     HashNode hn;
     Param pm;
-    LinkList l = matches;
-    Cmatch cm;
     Cline lc = NULL;
-    Aminfo ai = ainfo;
 
 /*
  * addwhat: -5 is for files,
@@ -4440,7 +3622,7 @@ addmatch(char *s, char *t)
  *          -3 is for executable command names.
  *          -2 is for anything unquoted
  *          -1 is for other file specifications
- *          (things with `~' of `=' at the beginning, ...).
+ *          (things with `~' or `=' at the beginning, ...).
  */
 
     /* Just to make the code cleaner */
@@ -4449,90 +3631,34 @@ addmatch(char *s, char *t)
 
     if (incompfunc)
 	s = dupstring(s);
-    e = s + sl;
-    if (!addwhat) {
-	test = 1;
-    } else if (addwhat == -1 || addwhat == -5 || addwhat == -6 ||
-	       addwhat == CC_FILES || addwhat == -7 || addwhat == -8) {
-	if (sl < fpl + fsl)
-	    return;
 
+    if (addwhat == -1 || addwhat == -5 || addwhat == -6 ||
+	addwhat == CC_FILES || addwhat == -7 || addwhat == -8) {
 	if ((addwhat == CC_FILES ||
 	     addwhat == -5) && !*psuf) {
 	    /* If this is a filename, do the fignore check. */
 	    char **pt = fignore;
-	    int filell;
+	    int filell, sl = strlen(s);
 
-	    for (test = 1; test && *pt; pt++)
-		if ((filell = strlen(*pt)) < sl
-		    && !strcmp(*pt, s + sl - filell))
-		    test = 0;
-
-	    if (!test) {
-		l = fmatches;
-		ai = fainfo;
-	    }
-	}
-	pl = fpl;
-	if (addwhat == -5 || addwhat == -8) {
-	    test = 1;
-	    cp = filecomp;
-	    cc = cp || ispattern;
-	    e = s + sl - fsl;
-	    mpl = fpl; msl = fsl;
-	} else {
-	    if ((cp = filecomp)) {
-		if ((test = domatch(s, filecomp, 0))) {
-		    e = s + sl;
-		    cc = 1;
-		}
-	    } else {
-		e = s + sl - fsl;
-		if ((test = !strncmp(s, fpre, fpl)))
-		    if ((test = !strcmp(e, fsuf))) {
-			mpl = fpl; msl = fsl;
-		    }
-		if (!test && mstack &&
-		    (ms = comp_match(fpre, fsuf, s, &lc,
-				     (addwhat == CC_FILES ||
-				      addwhat == -6), &bpl, &bsl)))
-		    test = 1;
-		if (ispattern)
-		    cc = 1;
-	    }
+	    for (isalt = 0; !isalt && *pt; pt++)
+		if ((filell = strlen(*pt)) < sl &&
+		    !strcmp(*pt, s + sl - filell))
+		    isalt = 1;
 	}
-	if (test) {
-	    if (addwhat == -7 && !findcmd(s, 0))
-		return;
-	    isf = CMF_FILE;
+	if (!(ms = comp_match(fpre, fsuf, s, filecomp, &lc,
+			      (addwhat == CC_FILES || addwhat == -6 ||
+			       addwhat == -5 || addwhat == -8),
+			      &bpl, &bsl, &isexact)))
+	    return;
 
-	    if (addwhat == CC_FILES || addwhat == -6 ||
-		addwhat == -5 || addwhat == -8) {
-		te = s + pl;
-		s = quotename(s, &e, te, &pl);
-		sl = strlen(s);
-	    } else if (!cc) {
-		s = dupstring(t = s);
-		e += s - t;
-	    }
-	    if (cc) {
-		tt = (char *)zhalloc(lppl + lpsl + sl + 1);
-		tt[0] = '\0';
-		if (lppre)
-		    strcpy(tt, lppre);
-		strcat(tt, s);
-		if (lpsuf)
-		    strcat(tt, lpsuf);
-		e += (tt - s);
-		untokenize(s = tt);
-		sl = strlen(s);
-	    }
-	}
+	if (addwhat == -7 && !findcmd(s, 0))
+	    return;
+	isfile = CMF_FILE;
     } else if (addwhat == CC_QUOTEFLAG || addwhat == -2  ||
 	      (addwhat == -3 && !(hn->flags & DISABLED)) ||
 	      (addwhat == -4 && (PM_TYPE(pm->flags) == PM_SCALAR) &&
-	       (tt = pm->gets.cfn(pm)) && *tt == '/')    ||
-	      (addwhat == -9 && !(hn->flags & PM_UNSET)) ||
+	       !pm->level && (tt = pm->gets.cfn(pm)) && *tt == '/')    ||
+	      (addwhat == -9 && !(hn->flags & PM_UNSET) && !pm->level) ||
 	      (addwhat > 0 &&
 	       ((!(hn->flags & PM_UNSET) &&
 		 (((addwhat & CC_ARRAYS)    &&  (hn->flags & PM_ARRAY))    ||
@@ -4541,7 +3667,8 @@ addmatch(char *s, char *t)
 		  ((addwhat & CC_SCALARS)   &&  (hn->flags & PM_SCALAR))   ||
 		  ((addwhat & CC_READONLYS) &&  (hn->flags & PM_READONLY)) ||
 		  ((addwhat & CC_SPECIALS)  &&  (hn->flags & PM_SPECIAL))  ||
-		  ((addwhat & CC_PARAMS)    && !(hn->flags & PM_EXPORTED)))) ||
+		  ((addwhat & CC_PARAMS)    && !(hn->flags & PM_EXPORTED))) &&
+		 !pm->level) ||
 		((( addwhat & CC_SHFUNCS)				  ||
 		  ( addwhat & CC_BUILTINS)				  ||
 		  ( addwhat & CC_EXTCMDS)				  ||
@@ -4551,211 +3678,23 @@ addmatch(char *s, char *t)
 		 (((addwhat & CC_DISCMDS) && (hn->flags & DISABLED)) ||
 		  ((addwhat & CC_EXCMDS)  && !(hn->flags & DISABLED)))) ||
 		((addwhat & CC_BINDINGS) && !(hn->flags & DISABLED))))) {
-	if (sl >= rpl + rsl || mstack || cp) {
-	    if (cp) {
-		test = domatch(s, patcomp, 0);
-		e = s + sl;
-	    } else {
-		e = s + sl - rsl;
-		if ((test = !strncmp(s, rpre, rpl)))
-		    if ((test = !strcmp(e, rsuf))) {
-			mpl = rpl; msl = rsl;
-		    }
-		if (!test && mstack &&
-		    (ms = comp_match(rpre, rsuf, s, &lc,
-				     (addwhat == CC_QUOTEFLAG), &bpl, &bsl)))
-		    test = 1;
-	    }
-	}
-	if (!test && sl < lpl + lsl && !mstack)
+	if (!(ms = comp_match(rpre, rsuf, s, patcomp, &lc,
+			      (addwhat == CC_QUOTEFLAG),
+			      &bpl, &bsl, &isexact)) &&
+	    !(ms = comp_match(lpre, lsuf, s, NULL, &lc,
+			      (addwhat == CC_QUOTEFLAG),
+			      &bpl, &bsl, &isexact)))
 	    return;
-	if (!test && lpre && lsuf) {
-	    e = s + sl - lsl;
-	    if ((test = !strncmp(s, lpre, lpl)))
-		if ((test = !strcmp(e, lsuf))) {
-		    mpl = lpl; msl = lsl;
-		}
-	    if (!test && mstack &&
-		(ms = comp_match(lpre, lsuf, s, &lc,
-				 (addwhat == CC_QUOTEFLAG), &bpl, &bsl)))
-		test = 1;
-	    pl = lpl;
-	}
-	if (addwhat == CC_QUOTEFLAG) {
-	    te = s + pl;
-	    s = quotename(s, &e, te, &pl);
-	    sl = strlen(s);
-	}
     }
-    if (!test)
+    if (!ms)
 	return;
-    if (!ms) {
-	if (sl < ai->minlen)
-	    ai->minlen = sl;
-	if (!mstack && !ispattern && ai->firstm &&
-	    (test = sfxlen(ai->firstm->str, s)) < ai->suflen)
-	    ai->suflen = test;
-    }
-
-    /* Generate the common -P prefix. */
-
-    if (ai->pprefix) {
-	if (curcc->prefix)
-	    ai->pprefix[pfxlen(ai->pprefix, curcc->prefix)] = '\0';
-	else
-	    ai->pprefix[0] = '\0';
-    } else
-	ai->pprefix = dupstring(curcc->prefix ? curcc->prefix : "");
-
-    /* Generate the prefix to insert for ambiguous completions. */
-    t = s;
-    if (lppre)
-	t = dyncat(lppre, t);
-    if (!ispattern && !ms && mstack) {
-	Cline *clp = &lc, tlc;
-	char *ss = dupstring(s), *ee = e + (ss - s);
-
-	DPUTS(e < s || e > s + sl, "BUG: invalid end-pointer (e)");
-
-	if (lppre && *lppre) {
-	    *clp = str_cline(lppre, strlen(lppre), &tlc);
-	    clp = &(tlc->next);
-	}
-	if (pl) {
-	    *clp = str_cline(ss, pl, &tlc);
-	    clp = &(tlc->next);
-	}
-	if (ee != ss + sl || (lpsuf && *lpsuf)) {
-	    *clp = tlc = getcline(ss + pl, (ee - ss) - pl, NULL, 0, CLF_MID);
-	    clp = &(tlc->next);
-	    if (ee != ss + sl) {
-		*clp = str_cline(ee, (ss + sl) - ee, &tlc);
-		clp = &(tlc->next);
-	    }
-	    if (lpsuf && *lpsuf) {
-		*clp = str_cline(lpsuf, strlen(lpsuf), &tlc);
-		clp = &(tlc->next);
-	    }
-	} else {
-	    *clp = tlc = getcline(NULL, 0, ss + pl, sl - pl,
-				  CLF_END | CLF_VAR);
-	    clp = &(tlc->next);
-	}
-	*clp = NULL;
-    }
-    if (ipre && *ipre) {
-	Cline tlc = prepend_cline(ipre, lc);
-
-	ai->noipre = 0;
-	if (!ms && !mstack) {
-	    ai->icpl = lppl + mpl;
-	    ai->icsl = lpsl + msl;
-	    if (ai->iaprefix)
-		ai->iaprefix[pfxlen(ai->iaprefix, t)] = '\0';
-	    else
-		ai->iaprefix = dupstring(t);
-	} else
-	    ai->ilinecl = join_clines(ai->ilinecl, lc);
-	if (ai->iprefix) {
-	    if (strcmp(ipre, ai->iprefix))
-		ai->iprefix = "";
-	} else
-	    ai->iprefix = dupstring(ipre);
-
-	t = dyncat(ipre, t);
-	lc = tlc;
-    } else
-	ai->iprefix = "";
-
-    if (!ms && !mstack) {
-	ai->cpl = lppl + mpl;
-	ai->csl = lpsl + msl;
-	if (ai->aprefix)
-	    ai->aprefix[pfxlen(ai->aprefix, t)] = '\0';
-	else
-	    ai->aprefix = dupstring(t);
-    } else
-	ai->linecl = join_clines(ai->linecl, lc);
-
-    mnum++;
-    ai->count++;
-
-    /* Allocate and fill the match structure. */
-    cm = (Cmatch) zhalloc(sizeof(struct cmatch));
-    if (ispattern) {
-	if (lpsuf && *lpsuf && strsfx(lpsuf, s)) {
-	    s[sl - lpsl] = '\0';
-	    cm->psuf = lpsuf;
-	} else
-	    cm->psuf = NULL;
-
-	if (lppre && *lppre && strpfx(lppre, s)) {
-	    s += lppl;
-	    cm->ppre = lppre;
-	    cm->prpre = (isf && prpre && *prpre ? prpre : NULL);
-	} else
-	    cm->ppre = cm->prpre = NULL;
-    } else {
-	cm->ppre = (lppre && *lppre ? lppre : NULL);
-	cm->psuf = (lpsuf && *lpsuf ? lpsuf : NULL);
-	cm->prpre = (isf && prpre && *prpre ? prpre : NULL);
-    }
-    cm->str = (ms ? ms : s);
-    cm->ipre = (ipre && *ipre ? ipre : NULL);
-    cm->ripre = (ripre && *ripre ? ripre : NULL);
-    if (incompfunc) {
-	cm->pre = dupstring(curcc->prefix);
-	cm->suf = dupstring(curcc->suffix);
-    } else {
-	cm->pre = curcc->prefix;
-	cm->suf = curcc->suffix;
-    }
-    cm->flags = mflags | isf;
-    cm->brpl = bpl;
-    cm->brsl = bsl;
-    cm->rems = cm->remf = NULL;
-    addlinknode(l, cm);
-
-    /* One more match for this explanation. */
-    if (expl) {
-	if (l == matches)
-	    expl->count++;
-	else
-	    expl->fcount++;
-    }
-    if (!ms) {
-	if (!ai->firstm)
-	    ai->firstm = cm;
-
-	/* Do we have an exact match? More than one? */
-	if (!ispattern && !(e - (s + pl))) {
-	    if (!ai->exact) {
-		ai->exact = 1;
-		if (incompfunc) {
-		    int lpl = (cm->ppre ? strlen(cm->ppre) : 0);
-		    int lsl = (cm->psuf ? strlen(cm->psuf) : 0);
-
-		    zsfree(compexactstr);
-		    compexactstr = e = (char *) zalloc(lpl + sl + lsl + 1);
-		    if (cm->ppre) {
-			strcpy(e, cm->ppre);
-			e += lpl;
-		    }
-		    strcpy(e, s);
-		    e += sl;
-		    if (cm->psuf)
-			strcpy(e, cm->psuf);
-		    comp_setunsetptr(CP_EXACTSTR, 0);
-		}
-	    } else {
-		ai->exact = 2;
-		cm = NULL;
-		if (incompfunc)
-		    comp_setunsetptr(0, CP_EXACTSTR);
-	    }
-	    ai->exactm = cm;
-	}
-    }
+    add_match_data(isalt, ms, lc, ipre, ripre, isuf, 
+		   (incompfunc ? dupstring(curcc->prefix) : curcc->prefix),
+		   prpre, 
+		   (isfile ? lppre : NULL),
+		   (isfile ? lpsuf : NULL),
+		   (incompfunc ? dupstring(curcc->suffix) : curcc->suffix),
+		   bpl, bsl, (mflags | isfile), isexact);
 }
 
 #ifdef HAVE_NIS_PLUS
@@ -5011,9 +3950,10 @@ gen_matches_files(int dirs, int execs, int all)
 			strcpy(p + o, psuf);
 
 			/* Do we have to use globbing? */
-			if (ispattern || (ns && comppatmatch && *comppatmatch)) {
+			if (ispattern ||
+			    (ns && comppatmatch && *comppatmatch)) {
 			    /* Yes, so append a `*' if needed. */
-			    if (ns) {
+			    if (ns && comppatmatch && *comppatmatch == '*') {
 				int tl = strlen(p);
 
 				p[tl] = Star;
@@ -5068,7 +4008,9 @@ docompletion(char *s, int lst, int incmd)
 		   ((isset(AUTOLIST) && !isset(BASHAUTOLIST)) ? 
 		    (isset(LISTAMBIGUOUS) ? 3 : 2) : 0) : 1);
 	zsfree(comppatmatch);
-	opm = comppatmatch = ztrdup(useglob ? "yes" : "");
+	opm = comppatmatch = ztrdup(useglob ? "*" : "");
+	zsfree(comppatinsert);
+	comppatinsert = ztrdup("menu");
 	zsfree(compforcelist);
 	compforcelist = ztrdup("");
 	haspattern = 0;
@@ -5173,7 +4115,7 @@ callcompfunc(char *s, char *fn)
 	compparameter = compredirect = "";
 	if (ispar)
 	    compcontext = (ispar == 2 ? "brace_parameter" : "parameter");
-	else if (inwhat == IN_MATH) {
+	else if (linwhat == IN_MATH) {
 	    if (insubscr) {
 		compcontext = "subscript";
 		if (varname) {
@@ -5195,7 +4137,7 @@ callcompfunc(char *s, char *fn)
 		compredirect = rdstr;
 	    set |= CP_REDIRECT;
 	} else
-	    switch (inwhat) {
+	    switch (linwhat) {
 	    case IN_ENV:
 		compcontext = "array_value";
 		compparameter = varname;
@@ -5263,20 +4205,20 @@ callcompfunc(char *s, char *fn)
 	zsfree(compprefix);
 	zsfree(compsuffix);
 	if (unset(COMPLETEINWORD)) {
-	    if (inwhat == IN_MATH)
+	    if (linwhat == IN_MATH)
 		tmp = s;
 	    else
-		tmp = quotename(s, NULL, NULL, NULL);
+		tmp = quotename(s, NULL);
 	    untokenize(tmp);
 	    compprefix = ztrdup(tmp);
 	    compsuffix = ztrdup("");
 	} else {
 	    char *ss = s + offs, sav;
 	    
-	    if (inwhat == IN_MATH)
+	    if (linwhat == IN_MATH)
 		tmp = s;
 	    else
-		tmp = quotename(s, &ss, NULL, NULL);
+		tmp = quotename(s, &ss);
 	    sav = *ss;
 	    *ss = '\0';
 	    untokenize(tmp);
@@ -5287,6 +4229,8 @@ callcompfunc(char *s, char *fn)
 	}
 	zsfree(compiprefix);
 	compiprefix = ztrdup("");
+	zsfree(compisuffix);
+	compisuffix = ztrdup("");
 	compcurrent = (usea ? (clwpos + 1 - aadd) : 0);
 	compnmatches = mnum;
 
@@ -5411,6 +4355,8 @@ makecomplist(char *s, int incmd, int lst)
     } else
 	compmatcher = 0;
 
+    linwhat = inwhat;
+
     /* Walk through the global matchers. */
     for (;;) {
 	bmatchers = NULL;
@@ -5432,16 +4378,14 @@ makecomplist(char *s, int incmd, int lst)
 	ainfo = (Aminfo) hcalloc(sizeof(struct aminfo));
 	fainfo = (Aminfo) hcalloc(sizeof(struct aminfo));
 
-	ainfo->minlen = ainfo->suflen = 
-	    fainfo->minlen = fainfo->suflen = 10000;
-	ainfo->noipre = fainfo->noipre= 1;
-
 	freecl = NULL;
 
 	if (!validlist)
 	    lastambig = 0;
 	amatches = 0;
 	mnum = 0;
+	unambig_mnum = -1;
+	isuf = NULL;
 	begcmgroup("default", 0);
 
 	ccused = newlinklist();
@@ -5529,9 +4473,9 @@ comp_str(int *ipl, int *pl, int untok)
 	ctokenize(ip);
 	remnulargs(ip);
     }
+    lp = strlen(p);
     ls = strlen(s);
     lip = strlen(ip);
-    lp = strlen(p);
     str = zhalloc(lip + lp + ls + 1);
     strcpy(str, ip);
     strcat(str, p);
@@ -5555,12 +4499,17 @@ makecomplistcall(Compctl cc)
 	HEAPALLOC {
 	    int ooffs = offs, lip, lp;
 	    char *str = comp_str(&lip, &lp, 0);
+	    char *oisuf = isuf;
 
+	    isuf = dupstring(compisuffix);
+	    ctokenize(isuf);
+	    remnulargs(isuf);
 	    offs = lip + lp;
 	    cc->refc++;
 	    ccont = 0;
 	    makecomplistor(cc, str, lincmd, lip, 0);
 	    offs = ooffs;
+	    isuf = oisuf;
 	    compnmatches = mnum;
 	} LASTALLOC;
     } SWITCHBACKHEAPS;
@@ -5591,7 +4540,11 @@ makecomplistctl(int flags)
 	    char *str = comp_str(&lip, &lp, 0), *t;
 	    char *os = cmdstr, **ow = clwords, **p, **q;
 	    int on = clwnum, op = clwpos;
+	    char *oisuf = isuf;
 
+	    isuf = dupstring(compisuffix);
+	    ctokenize(isuf);
+	    remnulargs(isuf);
 	    clwnum = arrlen(compwords);
 	    clwpos = compcurrent - 1;
 	    cmdstr = ztrdup(compwords[0]);
@@ -5607,6 +4560,7 @@ makecomplistctl(int flags)
 	    incompfunc = 2;
 	    ret = makecomplistglobal(str, !clwpos, COMP_COMPLETE, flags);
 	    incompfunc = 1;
+	    isuf = oisuf;
 	    offs = ooffs;
 	    compnmatches = mnum;
 	    zsfree(cmdstr);
@@ -5634,15 +4588,15 @@ makecomplistglobal(char *os, int incmd, int lst, int flags)
 
     ccont = CC_CCCONT;
 
-    if (inwhat == IN_ENV) {
+    if (linwhat == IN_ENV) {
         /* Default completion for parameter values. */
         cc = &cc_default;
-    } else if (inwhat == IN_MATH) {
+    } else if (linwhat == IN_MATH) {
         /* Parameter names inside mathematical expression. */
         cc_dummy.mask = CC_PARAMS;
 	cc = &cc_dummy;
 	cc_dummy.refc = 10000;
-    } else if (inwhat == IN_COND) {
+    } else if (linwhat == IN_COND) {
 	/* We try to be clever here: in conditions we complete option   *
 	 * names after a `-o', file names after `-nt', `-ot', and `-ef' *
 	 * and file names and parameter names elsewhere.                */
@@ -6206,9 +5160,10 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 
     if (ispattern) {
 	/* The word should be treated as a pattern, so compute the matcher. */
-	p = (char *)ncalloc(rpl + rsl + 2);
+	p = (char *) zhalloc(rpl + rsl + 2);
 	strcpy(p, rpre);
-	if (rpl && p[rpl - 1] != Star) {
+	if (rpl && p[rpl - 1] != Star &&
+	    (!comppatmatch || *comppatmatch == '*')) {
 	    p[rpl] = Star;
 	    strcpy(p + rpl + 1, rsuf);
 	} else
@@ -6294,9 +5249,10 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 
 	    /* We have to use globbing, so compute the pattern from *
 	     * the file prefix and suffix with a `*' between them.  */
-	    p = (char *)ncalloc((t2 = strlen(fpre)) + strlen(fsuf) + 2);
+	    p = (char *) zhalloc((t2 = strlen(fpre)) + strlen(fsuf) + 2);
 	    strcpy(p, fpre);
-	    if ((!t2 || p[t2 - 1] != Star) && *fsuf != Star)
+	    if ((!t2 || p[t2 - 1] != Star) && *fsuf != Star &&
+		(!comppatmatch || *comppatmatch == '*'))
 		p[t2++] = Star;
 	    strcpy(p + t2, fsuf);
 	    filecomp = parsereg(p);
@@ -6340,7 +5296,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 		opts[NULLGLOB] = 1;
 
 		addwhat = 0;
-		p = (char *)zhalloc(lpl + lsl + 3);
+		p = (char *) zhalloc(lpl + lsl + 3);
 		strcpy(p, lpre);
 		if (*lsuf != '*' && *lpre && lpre[lpl - 1] != '*')
 		    strcat(p, "*");
@@ -6888,9 +5844,9 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 void
 invalidatelist(void)
 {
-    if(showinglist == -2)
+    if (showinglist == -2)
 	listmatches();
-    if(validlist)
+    if (validlist)
 	freematches();
     lastambig = menucmp = validlist = showinglist = fromcomp = 0;
     menucur = NULL;
@@ -6930,7 +5886,7 @@ get_user_var(char *nam)
 		notempty = 0;
 	    } else {
 		notempty = 1;
-		if(*ptr == Meta)
+		if (*ptr == Meta)
 		    ptr++;
 	    }
 	    if (brk)
@@ -6939,7 +5895,7 @@ get_user_var(char *nam)
 	if (!brk || !count)
 	    return NULL;
 	*ptr = '\0';
-	aptr = uarr = (char **)ncalloc(sizeof(char *) * (count + 1));
+	aptr = uarr = (char **) zhalloc(sizeof(char *) * (count + 1));
 
 	while ((*aptr++ = (char *)ugetnode(arrlist)));
 	uarr[count] = NULL;
@@ -6952,7 +5908,7 @@ get_user_var(char *nam)
 	    return (incompfunc ? arrdup(arr) : arr);
 
 	if ((val = getsparam(nam))) {
-	    arr = (char **)ncalloc(2*sizeof(char *));
+	    arr = (char **) zhalloc(2*sizeof(char *));
 	    arr[0] = (incompfunc ? dupstring(val) : val);
 	    arr[1] = NULL;
 	}
@@ -7021,8 +5977,8 @@ makearray(LinkList l, int s, int *np, int *nlp)
     int n, nl = 0;
 
     /* Build an array for the matches. */
-    rp = ap = (Cmatch *)ncalloc(((n = countlinknodes(l)) + 1) *
-				sizeof(Cmatch));
+    rp = ap = (Cmatch *) ncalloc(((n = countlinknodes(l)) + 1) *
+				 sizeof(Cmatch));
 
     /* And copy them into it. */
     for (nod = firstnode(l); nod; incnode(nod))
@@ -7156,6 +6112,7 @@ dupmatch(Cmatch m)
     r->str = ztrdup(m->str);
     r->ipre = ztrdup(m->ipre);
     r->ripre = ztrdup(m->ripre);
+    r->isuf = ztrdup(m->isuf);
     r->ppre = ztrdup(m->ppre);
     r->psuf = ztrdup(m->psuf);
     r->prpre = ztrdup(m->prpre);
@@ -7272,6 +6229,7 @@ freematch(Cmatch m)
     zsfree(m->str);
     zsfree(m->ipre);
     zsfree(m->ripre);
+    zsfree(m->isuf);
     zsfree(m->ppre);
     zsfree(m->psuf);
     zsfree(m->pre);
@@ -7325,14 +6283,323 @@ freematches(void)
     }
 }
 
+/* Insert the given string into the command line.  If move is non-zero, *
+ * the cursor position is changed and len is the length of the string   *
+ * to insert (if it is -1, the length is calculated here).              */
+
+/**/
+static void
+inststrlen(char *str, int move, int len)
+{
+    if (!len || !str)
+	return;
+    if (len == -1)
+	len = strlen(str);
+    spaceinline(len);
+    strncpy((char *)(line + cs), str, len);
+    if (move)
+	cs += len;
+}
+
+/* This builds the unambiguous string. If ins is non-zero, it is
+ * immediatly inserted in the line. Otherwise csp is used to return
+ * the relative cursor position in the string returned. */
+
+static char *
+cline_str(Cline l, int ins, int *csp)
+{
+    Cline s;
+    int ocs = cs, ncs, pcs, pm, sm, d, b, i, j, li = 0;
+    int pl, sl, hasp, hass, ppos, spos, plen, slen;
+
+    ppos = spos = plen = slen = hasp = hass = 0;
+    pm = sm = d = b = pl = sl = -1;
+
+    /* Get the information about the brace beginning and end we have
+     * to re-insert. */
+    if (ins) {
+	if ((hasp = (brbeg && *brbeg))) {
+	    plen = strlen(brbeg); pl = brpl;
+	}
+	if ((hass = (brend && *brend))) {
+	    slen = strlen(brend); sl = we - wb - brsl - plen - slen + 1;
+	}
+	if (!pl) {
+	    inststrlen(brbeg, 1, -1);
+	    pl = -1; hasp = 0;
+	}
+	if (!sl) {
+	    inststrlen(brend, 1, -1);
+	    sl = -1; hass = 0;
+	}
+    }
+    /* Walk through the top-level cline list. */
+    while (l) {
+	if (pl >= 0)
+	    ppos = -1;
+	if (sl >= 0)
+	    spos = -1;
+	/* Insert the original string if no prefix. */
+	if (l->olen && !(l->flags & CLF_SUF) && !l->prefix) {
+	    inststrlen(l->orig, 1, l->olen);
+	    if (ins) {
+		li += l->olen;
+		if (pl >= 0 && li >= pl) {
+		    ppos = cs - (li - pl); pl = -1;
+		}
+		if (sl >= 0 && li >= sl) {
+		    spos = cs - (li - sl) - 1; sl = -1;
+		}
+	    }
+	} else {
+	    /* Otherwise insert the prefix. */
+	    for (s = l->prefix; s; s = s->next) {
+		pcs = cs;
+		if (s->flags & CLF_LINE)
+		    inststrlen(s->line, 1, s->llen);
+		else
+		    inststrlen(s->word, 1, s->wlen);
+		if (d < 0 && (s->flags & CLF_DIFF))
+		    d = cs;
+		if (ins) {
+		    li += s->llen;
+		    if (pl >= 0 && li >= pl) {
+			ppos = pcs + s->llen - (li - pl); pl = -1;
+		    }
+		    if (sl >= 0 && li >= sl) {
+			spos = pcs + s->llen - (li - sl) - 1; sl = -1;
+		    }
+		}
+	    }
+	}
+	/* Remember the position if this is the first prefix with
+	 * missing characters. */
+	if (pm < 0 && (l->flags & CLF_MISS) && !(l->flags & CLF_SUF))
+	    pm = cs;
+	pcs = cs;
+	/* Insert the anchor. */
+	if (l->flags & CLF_LINE)
+	    inststrlen(l->line, 1, l->llen);
+	else
+	    inststrlen(l->word, 1, l->wlen);
+	if (ins) {
+	    li += l->llen;
+	    if (pl >= 0 && li >= pl) {
+		ppos = pcs + l->llen - (li - pl); pl = -1;
+	    }
+	    if (sl >= 0 && li >= sl) {
+		spos = pcs + l->llen - (li - sl) - 1; sl = -1;
+	    }
+	}
+	/* Remember the cursor position for suffixes and mids. */
+	if (l->flags & CLF_MISS) {
+	    if (l->flags & CLF_MID)
+		b = cs;
+	    else if (sm < 0 && (l->flags & CLF_SUF))
+		sm = cs;
+	}
+	/* And now insert the suffix or the original string. */
+	if (l->olen && (l->flags & CLF_SUF) && !l->suffix) {
+	    pcs = cs;
+	    inststrlen(l->orig, 1, l->olen);
+	    if (ins) {
+		li += l->olen;
+		if (pl >= 0 && li >= pl) {
+		    ppos = pcs + l->olen - (li - pl); pl = -1;
+		}
+		if (sl >= 0 && li >= sl) {
+		    spos = pcs + l->olen - (li - sl) - 1; sl = -1;
+		}
+	    }
+	} else {
+	    int hp = 0, hs = 0;
+
+	    for (j = -1, i = 0, s = l->suffix; s; s = s->next) {
+		if (j < 0 && (s->flags & CLF_DIFF))
+		    j = i;
+		if (s->flags & CLF_LINE) {
+		    inststrlen(s->line, 0, s->llen);
+		    i += s->llen; pcs = cs + s->llen;
+		} else {
+		    inststrlen(s->word, 0, s->wlen);
+		    i += s->wlen; pcs = cs + s->wlen;
+		}
+		if (ins) {
+		    li += s->llen;
+		    if (pl >= 0 && li >= pl) {
+			hp = 1; ppos = pcs - (li - pl) - i; pl = -1;
+		    }
+		    if (sl >= 0 && li >= sl) {
+			hs = 1; spos = pcs - (li - sl) - i; sl = -1;
+		    }
+		}
+	    }
+	    if (hp)
+		ppos += i;
+	    if (hs)
+		spos += i;
+	    cs += i;
+	    if (d < 0 && j >= 0)
+		d = cs - j;
+	}
+	/* If we reached the right positions, re-insert the braces. */
+	if (ins) {
+	    if (hasp && ppos >= 0) {
+		i = cs;
+		cs = ppos;
+		inststrlen(brbeg, 1, plen);
+		cs = i + plen;
+		hasp = 0;
+	    }
+	    if (hass && spos >= 0) {
+		i = cs;
+		cs = spos;
+		inststrlen(brend, 1, slen);
+		cs = i + slen;
+		hass = 0;
+	    }
+	}
+	l = l->next;
+    }
+    if (pl >= 0)
+	inststrlen(brbeg, 1, plen);
+    if (sl >= 0)
+	inststrlen(brend, 1, slen);
+
+    /* This calculates the new cursor position. If we had a mid cline
+     * with missing characters, we take this, otherwise if we have a
+     * prefix with missing characters, we take that, the same for a
+     * suffix, and finally a place where the matches differ. */
+    ncs = (b >= 0 ? b : (pm >= 0 ? pm : (sm >= 0 ? sm : (d >= 0 ? d : cs))));
+
+    if (!ins) {
+	/* We always inserted the string in the line. If that was not
+	 * requested, we copy it and remove from the line. */
+	char *r = zalloc((i = cs - ocs) + 1);
+
+	memcpy(r, (char *) (line + ocs), i);
+	r[i] = '\0';
+	cs = ocs;
+	foredel(i);
+
+	*csp = ncs - ocs;
+
+	return r;
+    }
+    if (ncs >= ppos)
+	ncs += plen;
+    if (ncs > spos)
+	ncs += slen;
+
+    lastend = cs;
+    cs = ncs;
+
+    return NULL;
+}
+
+/* This is a utility function using the function above to allow access
+ * to the unambiguous string and cursor position via compstate. */
+
+/**/
+char *
+unambig_data(int *cp)
+{
+    static char *scache = NULL;
+    static int ccache;
+
+    if (mnum && ainfo) {
+	if (mnum != unambig_mnum) {
+	    zsfree(scache);
+	    scache = cline_str((ainfo->count ? ainfo->line : fainfo->line),
+			       0, &ccache);
+	}
+    } else {
+	zsfree(scache);
+	scache = ztrdup("");
+	ccache = 0;
+    }
+    unambig_mnum = mnum;
+    if (cp)
+	*cp = ccache + 1;
+
+    return scache;
+}
+
+/* Insert the given match. This returns the number of characters inserted.*/
+
+/**/
+static int
+instmatch(Cmatch m)
+{
+    int l, r = 0, ocs, a = cs;
+
+    /* Ignored prefix. */
+    if (m->ipre) {
+	inststrlen(m->ipre, 1, (l = strlen(m->ipre)));
+	r += l;
+    }
+    /* -P prefix. */
+    if (m->pre) {
+	inststrlen(m->pre, 1, (l = strlen(m->pre)));
+	r += l;
+    }
+    /* Path prefix. */
+    if (m->ppre) {
+	inststrlen(m->ppre, 1, (l = strlen(m->ppre)));
+	r += l;
+    }
+    /* The string itself. */
+    inststrlen(m->str, 1, (l = strlen(m->str)));
+    r += l;
+    ocs = cs;
+    /* Re-insert the brace beginning, if any. */
+    if (brbeg && *brbeg) {
+	cs = a + m->brpl + (m->pre ? strlen(m->pre) : 0);
+	l = strlen(brbeg);
+	brpcs = cs;
+	inststrlen(brbeg, 1, l);
+	r += l;
+	ocs += l;
+	cs = ocs;
+    }
+    /* Path suffix. */
+    if (m->psuf) {
+	inststrlen(m->psuf, 1, (l = strlen(m->psuf)));
+	r += l;
+    }
+    /* Re-insert the brace end. */
+    if (brend && *brend) {
+	a = cs;
+	cs -= m->brsl;
+	ocs = brscs = cs;
+	l = strlen(brend);
+	inststrlen(brend, 1, l);
+	r += l;
+	cs = a + l;
+    } else
+	brscs = -1;
+    /* -S suffix */
+    if (m->suf) {
+	inststrlen(m->suf, 1, (l = strlen(m->suf)));
+	r += l;
+    }
+    /* ignored suffix */
+    if (m->isuf) {
+	inststrlen(m->isuf, 1, (l = strlen(m->isuf)));
+	r += l;
+    }
+    lastend = cs;
+    cs = ocs;
+
+    return r;
+}
+
 /* Handle the case were we found more than one match. */
 
 /**/
 static void
 do_ambiguous(void)
 {
-    int p = (usemenu || haspattern), atend = (cs == we);
-
     menucmp = 0;
 
     /* If we have to insert the first match, call do_single().  This is *
@@ -7349,22 +6616,17 @@ do_ambiguous(void)
      * unambiguous prefix.                                               */
     lastambig = 1;
 
-    if (p) {
-	/* p is set if we are in a position to start using menu completion *
-	 * due to one of the menu completion options, or due to the        *
-	 * menu-complete-word command, or due to using GLOB_COMPLETE which *
-	 * does menu-style completion regardless of the setting of the     *
-	 * normal menu completion options.                                 */
+    if (usemenu || (haspattern && comppatinsert &&
+		    !strcmp(comppatinsert, "menu"))) {
+	/* We are in a position to start using menu completion due to one  *
+	 * of the menu completion options, or due to the menu-complete-    *
+	 * word command, or due to using GLOB_COMPLETE which does menu-    *
+	 * style completion regardless of the setting of the normal menu   *
+	 * completion options.                                             */
 	do_ambig_menu();
-    } else {
+    } else if (ainfo) {
+	int atend = (cs == we), oll = ll, la;
 	VARARR(char, oline, ll);
-	int sl = 0, oll = ll;
-	int ocs, pl = 0, l, lp, ls, la = 0;
-	char *ps;
-	Cline lc;
-
-	if (!ainfo)
-	    return;
 
 	/* Copy the line buffer to be able to easily test if it changed. */
 	memcpy(oline, line, ll);
@@ -7375,69 +6637,9 @@ do_ambiguous(void)
 	cs = wb;
 	foredel(we - wb);
 
-	/* Sort-of general case: we have an ambiguous completion, and aren't *
-	 * starting menu completion or doing anything really weird.  We need *
-	 * to insert any unambiguous prefix and suffix, if possible.         */
-
-	if (ainfo->iprefix && *ainfo->iprefix) {
-	    inststrlen(ainfo->iprefix, 1, -1);
-	    inststrlen(ainfo->pprefix, 1, -1);
-	    ps = ainfo->iaprefix;
-	    lc = ainfo->ilinecl;
-	    lp = ainfo->icpl;
-	    ls = ainfo->icsl;
-	} else {
-	    if (ainfo->noipre && ainfo->pprefix) {
-		pl = strlen(ainfo->pprefix);
-		inststrlen(ainfo->pprefix, 1, pl);
-	    }
-	    ps = ainfo->aprefix;
-	    lc = ainfo->linecl;
-	    lp = ainfo->cpl;
-	    ls = ainfo->csl;
-	}
-	if (lc) {
-	    if (!ps)
-		ps = "";
-	    if (lp) {
-		if (ls) {
-		    DPUTS(!ainfo->firstm, "BUG: merge without firtsm");
-		    if (ainfo->firstm->psuf)
-			merge_cline(lc, ps, lp,
-				    dyncat(ainfo->firstm->str,
-					   ainfo->firstm->psuf),
-				    ls, (sl = strlen(ainfo->firstm->psuf)));
-		    else
-			merge_cline(lc, ps, lp, ainfo->firstm->str, ls, 0);
-		} else
-		    merge_cline(lc, ps, lp, NULL, 0, 0);
-	    }
-	    inst_cline(lc, pl, sl);
-	} else {
-	    inststrlen(ps, 1, -1);
-	    ocs = cs;
-	    if (brbeg && *brbeg) {
-		cs = wb + brpl + pl;
-		l = strlen(brbeg);
-		inststrlen(brbeg, 1, l);
-		ocs += l;
-		cs = ocs;
-	    }
-	    if(ainfo->suflen && !atend) {
-		DPUTS(!ainfo->firstm, "BUG: suffix without firstm");
-		inststrlen(ainfo->firstm->str +
-			   strlen(ainfo->firstm->str) - ainfo->suflen, 1,
-			   ainfo->suflen);
-	    }
-	    if (ainfo->firstm && ainfo->firstm->psuf)
-		inststrlen(ainfo->firstm->psuf, 0, -1);
-	    if (brend && *brend) {
-		cs -= brsl;
-		inststrlen(brend, 1, -1);
-	    }
-	    lastend = cs;
-	    cs = ocs;
-	}
+	/* Now get the unambiguous string and insert it into the line. */
+	cline_str(ainfo->line, 1, NULL);
+
 	/* la is non-zero if listambiguous may be used. Copying and
 	 * comparing the line looks like BFI but it is the easiest
 	 * solution. Really. */
@@ -7451,8 +6653,7 @@ do_ambiguous(void)
 	fromcomp = ((isset(AUTOMENU) ? FC_LINE : 0) |
 		    ((atend && cs != lastend) ? FC_INWORD : 0));
 
-	/*
-	 * If the LIST_AMBIGUOUS option (meaning roughly `show a list only *
+	/* If the LIST_AMBIGUOUS option (meaning roughly `show a list only *
 	 * if the completion is completely ambiguous') is set, and some    *
 	 * prefix was inserted, return now, bypassing the list-displaying  *
 	 * code.  On the way, invalidate the list and note that we don't   *
@@ -7463,9 +6664,12 @@ do_ambiguous(void)
 	    invalidatelist();
 	    fromcomp = fc;
 	    lastambig = 0;
+	    clearlist = 1;
 	    return;
 	}
-    }
+    } else
+	return;
+
     /* At this point, we might want a completion listing.  Show the listing *
      * if it is needed.                                                     */
     if (isset(LISTBEEP))
@@ -7503,9 +6707,8 @@ ztat(char *nam, struct stat *buf, int ls)
 static void
 do_single(Cmatch m)
 {
-    int l;
+    int l, sr = 0;
     int havesuff = 0;
-
     char *str = m->str, *ppre = m->ppre, *psuf = m->psuf, *prpre = m->prpre;
 
     if (!prpre) prpre = "";
@@ -7553,7 +6756,7 @@ do_single(Cmatch m)
     } else {
 	/* There is no user-specified suffix, *
 	 * so generate one automagically.     */
-	if(m->ripre && (m->flags & CMF_PARBR)) {
+	if (m->ripre && (m->flags & CMF_PARBR)) {
 	    /*{{*/
 	    /* Completing a parameter in braces.  Add a removable `}' suffix. */
 	    inststrlen("}", 1, 1);
@@ -7561,48 +6764,20 @@ do_single(Cmatch m)
 	    if (menuwe)
 		menuend++;
 	}
-	if((m->flags & CMF_FILE) || (m->ripre && isset(AUTOPARAMSLASH))) {
+	if ((m->flags & CMF_FILE) || (m->ripre && isset(AUTOPARAMSLASH))) {
 	    /* If we have a filename or we completed a parameter name      *
 	     * and AUTO_PARAM_SLASH is set, lets see if it is a directory. *
 	     * If it is, we append a slash.                                */
-	    char *p;
 	    struct stat buf;
+	    char *p;
 
 	    /* Build the path name. */
-	    if (haspattern || ic || m->ripre) {
-		int ne = noerrs;
-
-		noerrs = 1;
-
-		if (m->ripre) {
-		    int pl = strlen(m->ripre);
-
-		    p = (char *) ncalloc(pl + strlen(str) + strlen(psuf) + 1);
-		    sprintf(p, "%s%s%s", m->ripre, str, psuf);
-		    if (pl && p[pl-1] == Inbrace)
-			strcpy(p+pl-1, p+pl);
-		} else if (ic) {
-		    p = (char *) ncalloc(strlen(ppre) + strlen(str) +
-					 strlen(psuf) + 2);
-		    sprintf(p, "%c%s%s%s", ic, ppre, str, psuf);
-		} else {
-		    p = (char *) ncalloc(strlen(ppre) + strlen(str) +
-					 strlen(psuf) + 1);
-		    sprintf(p, "%s%s%s", ppre, str, psuf);
-		}
-		parsestr(p);
-		if (ic)
-		    *p = ic;
-		singsub(&p);
+	    p = (char *) zhalloc(strlen(prpre) + strlen(str) +
+				 strlen(psuf) + 3);
+	    sprintf(p, "%s%s%s", (prpre && *prpre) ? prpre : "./", str, psuf);
 
-		noerrs = ne;
-	    } else {
-		p = (char *) ncalloc(strlen(prpre) + strlen(str) +
-				     strlen(psuf) + 3);
-		sprintf(p, "%s%s%s", (prpre && *prpre) ? prpre : "./", str, psuf);
-	    }
 	    /* And do the stat. */
-	    if (!ztat(p, &buf, 0) && S_ISDIR(buf.st_mode)) {
+	    if (!(sr = ztat(p, &buf, 0)) && S_ISDIR(buf.st_mode)) {
 		/* It is a directory, so add the slash. */
 		havesuff = 1;
 		inststrlen("/", 1, 1);
@@ -7632,12 +6807,13 @@ do_single(Cmatch m)
 	    inststrlen(",", 1, 1);
 	    menuinsc++;
 	    makesuffix(1);
-	    if (menuwe && isset(AUTOPARAMKEYS))
+	    if ((!menucmp || menuwe) && isset(AUTOPARAMKEYS))
 		suffixlen[','] = suffixlen['}'] = 1;
 	}
-    } else if (!menucmp && !havesuff) {
+    } else if (!menucmp && !havesuff && (!(m->flags & CMF_FILE) || !sr)) {
 	/* If we didn't add a suffix, add a space, unless we are *
-	 * doing menu completion.                                */
+	 * doing menu completion or we are completing files and  *
+	 * the string doesn't name an existing file.             */
 	inststrlen(" ", 1, 1);
 	menuinsc++;
 	if (menuwe)
@@ -7680,7 +6856,7 @@ pfxlen(char *s, char *t)
 
 /* Return the length of the common suffix of s and t. */
 
-/**/
+#if 0
 static int
 sfxlen(char *s, char *t)
 {
@@ -7695,6 +6871,7 @@ sfxlen(char *s, char *t)
     } else
 	return 0;
 }
+#endif
 
 /* This is used to print the explanation string. *
  * It returns the number of lines printed.       */
@@ -7798,7 +6975,7 @@ listmatches(void)
 
 #ifdef DEBUG
     /* Sanity check */
-    if(!validlist) {
+    if (!validlist) {
 	showmsg("BUG: listmatches called with bogus list");
 	return;
     }
@@ -8199,7 +7376,7 @@ processcmd(void)
     inststr(" ");
     untokenize(s);
     HEAPALLOC {
-	inststr(quotename(s, NULL, NULL, NULL));
+	inststr(quotename(s, NULL));
     } LASTALLOC;
     zsfree(s);
     done = 1;
diff --git a/Src/glob.c b/Src/glob.c
index 56e0ac133..f8fd2aa4a 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -583,7 +583,7 @@ compalloc(void)
 
 /**/
 static int
-getglobflags()
+getglobflags(void)
 {
     char *nptr;
     /* (#X): assumes we are still positioned on the initial '(' */
diff --git a/Src/init.c b/Src/init.c
index f3576aadd..9fc4a81fb 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -757,25 +757,43 @@ run_init_scripts(void)
 	source(GLOBAL_ZSHENV);
 #endif
 	if (isset(RCS)) {
+	    int globalfirst = isset(GLOBALRCSFIRST);
+	    if (globalfirst) {
+#ifdef GLOBAL_ZPROFILE
+		if (islogin)
+		    source(GLOBAL_ZPROFILE);
+#endif
+#ifdef GLOBAL_ZSHRC
+		if (interact)
+		    source(GLOBAL_ZSHRC);
+#endif
+#ifdef GLOBAL_ZLOGIN
+		if (islogin)
+		    source(GLOBAL_ZLOGIN);
+#endif
+	    }
 	    if (unset(PRIVILEGED))
 		sourcehome(".zshenv");
 	    if (islogin) {
 #ifdef GLOBAL_ZPROFILE
-		source(GLOBAL_ZPROFILE);
+		if (!globalfirst)
+		    source(GLOBAL_ZPROFILE);
 #endif
 		if (unset(PRIVILEGED))
 		    sourcehome(".zprofile");
 	    }
 	    if (interact) {
 #ifdef GLOBAL_ZSHRC
-		source(GLOBAL_ZSHRC);
+		if (!globalfirst)
+		    source(GLOBAL_ZSHRC);
 #endif
 		if (unset(PRIVILEGED))
 		    sourcehome(".zshrc");
 	    }
 	    if (islogin) {
 #ifdef GLOBAL_ZLOGIN
-		source(GLOBAL_ZLOGIN);
+		if (!globalfirst)
+		    source(GLOBAL_ZLOGIN);
 #endif
 		if (unset(PRIVILEGED))
 		    sourcehome(".zlogin");
diff --git a/Src/options.c b/Src/options.c
index bfe146e1e..9010831c0 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -114,6 +114,7 @@ static struct optname optns[] = {
 {NULL, "flowcontrol",	      OPT_ALL,			 FLOWCONTROL},
 {NULL, "functionargzero",     OPT_EMULATE|OPT_NONBOURNE, FUNCTIONARGZERO},
 {NULL, "glob",		      OPT_ALL,			 GLOBOPT},
+{NULL, "globalrcsfirst",      0,			 GLOBALRCSFIRST},
 {NULL, "globassign",	      OPT_EMULATE|OPT_CSH,	 GLOBASSIGN},
 {NULL, "globcomplete",	      0,			 GLOBCOMPLETE},
 {NULL, "globdots",	      0,			 GLOBDOTS},
@@ -228,7 +229,7 @@ static short zshletters[LAST_OPT - FIRST_OPT + 1] = {
     /* > */  0,
     /* ? */  0,
     /* @ */  0,
-    /* A */  0,
+    /* A */  0,			/* use with set for arrays */
     /* B */ -BEEP,
     /* C */ -CLOBBER,
     /* D */  PUSHDTOHOME,
@@ -261,9 +262,9 @@ static short zshletters[LAST_OPT - FIRST_OPT + 1] = {
     /* _ */  0,
     /* ` */  0,
     /* a */  ALLEXPORT,
-    /* b */  0,
-    /* c */  0,
-    /* d */  0,
+    /* b */  0,			/* in non-Bourne shells, end of options */
+    /* c */  0,			/* command follows */
+    /* d */  GLOBALRCSFIRST,
     /* e */  ERREXIT,
     /* f */ -RCS,
     /* g */  HISTIGNORESPACE,
@@ -274,7 +275,7 @@ static short zshletters[LAST_OPT - FIRST_OPT + 1] = {
     /* l */  LOGINSHELL,
     /* m */  MONITOR,
     /* n */ -EXECOPT,
-    /* o */  0,
+    /* o */  0,			/* long option name follows */
     /* p */  PRIVILEGED,
     /* q */  0,
     /* r */  RESTRICTED,
diff --git a/Src/params.c b/Src/params.c
index 7ae6a75ec..dbc6ce8d8 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -2709,6 +2709,8 @@ printparamnode(HashNode hn, int printflags)
 	    printf("array ");
 	else if (p->flags & PM_HASHED)
 	    printf("association ");
+	if (p->level)
+	    printf("local ");
 	if (p->flags & PM_LEFT)
 	    printf("left justified %d ", p->ct);
 	if (p->flags & PM_RIGHT_B)
diff --git a/Src/subst.c b/Src/subst.c
index 651179b72..c62fcce6b 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -702,7 +702,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
     int spbreak = isset(SHWORDSPLIT) && !ssub && !qt;
     char *val = NULL, **aval = NULL;
     unsigned int fwidth = 0;
-    Value v;
+    Value v = NULL;
     int flags = 0;
     int flnum = 0;
     int sortit = 0, casind = 0;
@@ -1828,7 +1828,7 @@ modify(char **str, char **ptr)
 			    subst(&copy, hsubl, hsubr, gbal);
 			break;
 		    case 'q':
-			copy = bslashquote(copy, NULL, NULL, NULL, 0);
+			copy = bslashquote(copy, NULL, 0);
 			break;
 		    }
 		    tc = *tt;
@@ -1882,7 +1882,7 @@ modify(char **str, char **ptr)
 		    }
 		    break;
 		case 'q':
-		    *str = bslashquote(*str, NULL, NULL, NULL, 0);
+		    *str = bslashquote(*str, NULL, 0);
 		    break;
 		}
 	    }
diff --git a/Src/utils.c b/Src/utils.c
index 3f8177271..12b7fc7eb 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -3140,7 +3140,7 @@ hasspecial(char const *s)
 
 /**/
 char *
-bslashquote(const char *s, char **e, char *te, int *pl, int instring)
+bslashquote(const char *s, char **e, int instring)
 {
     const char *u, *tt;
     char *v, buf[PATH_MAX * 2];
@@ -3151,8 +3151,6 @@ bslashquote(const char *s, char **e, char *te, int *pl, int instring)
     for (; *u; u++) {
 	if (e && *e == u)
 	    *e = v, sf |= 1;
-	if (te == u)
-	    *pl = v - tt, sf |= 2;
 	if (ispecial(*u) &&
 	    (!instring || (isset(BANGHIST) &&
 			   *u == (char)bangchar) ||
@@ -3189,8 +3187,6 @@ bslashquote(const char *s, char **e, char *te, int *pl, int instring)
 
     if (e && *e == u)
 	*e = v;
-    if (te == u)
-	*pl = v - tt;
 
     return (char *) tt;
 }
diff --git a/Src/zsh.h b/Src/zsh.h
index d0b566e3f..c3c853dfd 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -1085,6 +1085,7 @@ enum {
     EXTENDEDHISTORY,
     FLOWCONTROL,
     FUNCTIONARGZERO,
+    GLOBALRCSFIRST,
     GLOBOPT,
     GLOBASSIGN,
     GLOBCOMPLETE,
diff --git a/patchlist.txt b/patchlist.txt
index 131be8c10..9cefc77ae 100644
--- a/patchlist.txt
+++ b/patchlist.txt
@@ -798,8 +798,66 @@ for some other reason).
 Sven: 5831: in subscripts and math environments, the whole string is always
 passed down to the function handler.
 
+pws: 5844: don't set errflag if noerrs = 2.
+
 Sven: 5852: warnings about _long_options
 
 pws: 5854: man page dependencies in Doc/Makefile.in
 
 Sven: 5862: _path_files (turning on menu) and _cd (include . in path)
+
+pws: 5863: substitution rules
+
+  pws-14
+
+Bart: 5868: expn.yo fixes
+
+Sven: 5871, 5875: big zle_tricky.c cleanup, with compstate changes and
+IFSUFFIX
+
+Sven: 5872, 5881, 5889: corresponding Completion function rewrite
+
+Sven: 5879, 5899: completion documentation
+
+Sven: 5890: _match completer
+
+Sven: 5895, 5898, 5906: fix completion prefixes
+
+Sven: 5904: print local for parameters
+
+pws: 5905: _main_complete should at least try to get _unset_options correct.
+
+Sven: 5912: compiler warnings
+
+Sven: 5913: zle -C test
+
+Sven: 5914: _main_complete takes optional completer arguments
+
+pws: 5915: minor type fixes
+
+Sven: 5916: _expand completer
+
+Sven: 5918: _list completer
+
+Sven: 5925: path_expand
+
+Sven: 5926: $HISTNO
+
+Sven: 5928: copy context in zle_tricky
+
+pws: 5931: more parameter substitution rules
+
+Sven: 5933: don't complete local parameters; _setopt/_unsetopt complete all
+options (code to use currently unset/set options remains in comments)
+
+pws: 5934: option GLOBAL_RCS_FIRST runs /etc/z* files before user's files.
+
+Sven: 5938, 5937: compset to replace modifying conditions
+
+Sven: 5940: move cursor
+
+Sven: 5942: spaces in file names with old completion
+
+Sven: 5947: completion functions fix
+
+pws: unposted: updated .distfiles under Completion