about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-10-26 15:36:10 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-10-26 15:36:10 +0000
commite0b26186f1d3c1a3a580eb7e8a8199c25536f4e6 (patch)
treee4247c2507fa1a135740a3cd02e7405cbdbfa69a
parent56f338eb8bfd4bcdbf14b495ff8a34425c3527d4 (diff)
downloadzsh-e0b26186f1d3c1a3a580eb7e8a8199c25536f4e6.tar.gz
zsh-e0b26186f1d3c1a3a580eb7e8a8199c25536f4e6.tar.xz
zsh-e0b26186f1d3c1a3a580eb7e8a8199c25536f4e6.zip
manual/8424
-rw-r--r--Completion/Base/_arguments2
-rw-r--r--Completion/Base/_brace_parameter2
-rw-r--r--Completion/Base/_command_names24
-rw-r--r--Completion/Base/_describe2
-rw-r--r--Completion/Base/_equal4
-rw-r--r--Completion/Base/_first2
-rw-r--r--Completion/Base/_job26
-rw-r--r--Completion/Base/_regex_arguments3
-rw-r--r--Completion/Base/_tilde17
-rw-r--r--Completion/Base/_values2
-rw-r--r--Completion/Builtins/_aliases6
-rw-r--r--Completion/Builtins/_arrays2
-rw-r--r--Completion/Builtins/_bg_jobs5
-rw-r--r--Completion/Builtins/_bindkey6
-rw-r--r--Completion/Builtins/_builtin2
-rw-r--r--Completion/Builtins/_cd3
-rw-r--r--Completion/Builtins/_command4
-rw-r--r--Completion/Builtins/_compdef12
-rw-r--r--Completion/Builtins/_disable8
-rw-r--r--Completion/Builtins/_enable8
-rw-r--r--Completion/Builtins/_fc5
-rw-r--r--Completion/Builtins/_functions2
-rw-r--r--Completion/Builtins/_hash4
-rw-r--r--Completion/Builtins/_jobs5
-rw-r--r--Completion/Builtins/_kill6
-rw-r--r--Completion/Builtins/_popd39
-rw-r--r--Completion/Builtins/_set8
-rw-r--r--Completion/Builtins/_stat3
-rw-r--r--Completion/Builtins/_trap6
-rw-r--r--Completion/Builtins/_unhash9
-rw-r--r--Completion/Builtins/_wait17
-rw-r--r--Completion/Builtins/_which14
-rw-r--r--Completion/Builtins/_zle4
-rw-r--r--Completion/Builtins/_zmodload4
-rw-r--r--Completion/Commands/_bash_completions2
-rw-r--r--Completion/Commands/_history_complete_word123
-rw-r--r--Completion/Commands/_read_comp8
-rw-r--r--Completion/Core/_approximate25
-rw-r--r--Completion/Core/_main_complete11
-rw-r--r--Completion/Core/_normal6
-rw-r--r--Completion/Core/_options3
-rw-r--r--Completion/Core/_parameters8
-rw-r--r--Completion/Core/_path_files32
-rw-r--r--Completion/Core/compinit4
-rw-r--r--Completion/Debian/_apt2
-rw-r--r--Completion/User/_cvs10
-rw-r--r--Completion/User/_gdb3
-rw-r--r--Completion/User/_man2
-rw-r--r--Completion/User/_mh7
-rw-r--r--Completion/User/_nslookup2
-rw-r--r--Completion/User/_tar3
-rw-r--r--Completion/User/_urls2
-rw-r--r--Completion/User/_users2
-rw-r--r--Completion/User/_whereis5
-rw-r--r--Completion/User/_whois1
-rw-r--r--Completion/X/_x_font2
-rw-r--r--Completion/X/_x_window2
-rw-r--r--Completion/X/_xmodmap2
-rw-r--r--Doc/Makefile.in6
-rw-r--r--Doc/Zsh/compctl.yo9
-rw-r--r--Doc/Zsh/compsys.yo30
-rw-r--r--Doc/Zsh/compwid.yo65
-rw-r--r--Doc/Zsh/expn.yo6
-rw-r--r--Doc/Zsh/manual.yo3
-rw-r--r--Doc/Zsh/mod_clone.yo2
-rw-r--r--Doc/Zsh/mod_compctl.yo10
-rw-r--r--Doc/Zsh/mod_complete.yo7
-rw-r--r--Doc/Zsh/mod_computil.yo2
-rw-r--r--Doc/Zsh/mod_parameter.yo62
-rw-r--r--Doc/Zsh/mod_zle.yo2
-rw-r--r--Doc/Zsh/mod_zleparameter.yo28
-rw-r--r--Doc/Zsh/modules.yo12
-rw-r--r--Src/Modules/parameter.c1043
-rw-r--r--Src/Modules/parameter.mdd2
-rw-r--r--Src/Zle/comp.h202
-rw-r--r--Src/Zle/compctl.c3252
-rw-r--r--Src/Zle/compctl.h160
-rw-r--r--Src/Zle/compctl.mdd9
-rw-r--r--Src/Zle/complete.c1332
-rw-r--r--Src/Zle/complete.mdd11
-rw-r--r--Src/Zle/complist.c2
-rw-r--r--Src/Zle/complist.mdd2
-rw-r--r--Src/Zle/computil.mdd2
-rw-r--r--Src/Zle/zle.h17
-rw-r--r--Src/Zle/zle.mdd70
-rw-r--r--Src/Zle/zle_keymap.c3
-rw-r--r--Src/Zle/zle_main.c135
-rw-r--r--Src/Zle/zle_thingy.c8
-rw-r--r--Src/Zle/zle_tricky.c2355
-rw-r--r--Src/Zle/zleparameter.c257
-rw-r--r--Src/Zle/zleparameter.mdd5
-rw-r--r--Src/hashtable.c109
-rw-r--r--Src/module.c17
-rw-r--r--Src/params.c4
-rw-r--r--Src/subst.c4
-rw-r--r--Src/xmods.conf2
96 files changed, 5839 insertions, 3941 deletions
diff --git a/Completion/Base/_arguments b/Completion/Base/_arguments
index 18ddf0327..ca8f42c61 100644
--- a/Completion/Base/_arguments
+++ b/Completion/Base/_arguments
@@ -3,8 +3,6 @@
 # Complete the arguments of the current command according to the
 # descriptions given as arguments to this function.
 
-setopt localoptions extendedglob
-
 local long cmd="$words[1]" descr mesg
 
 long=$argv[(I)--]
diff --git a/Completion/Base/_brace_parameter b/Completion/Base/_brace_parameter
index 2bf7b6a6d..9ed4867ef 100644
--- a/Completion/Base/_brace_parameter
+++ b/Completion/Base/_brace_parameter
@@ -6,8 +6,6 @@ _parameters -e
 # Without the `-e' option, we would use the following (see the file
 # Core/_parameters for more enlightenment).
 
-# setopt localoptions extendedglob
-
 # local lp ls n q
 
 # if [[ "$SUFFIX" = *\}* ]]; then
diff --git a/Completion/Base/_command_names b/Completion/Base/_command_names
index 2218aac21..79bd46e84 100644
--- a/Completion/Base/_command_names
+++ b/Completion/Base/_command_names
@@ -4,24 +4,34 @@
 # complete only external commands and executable files. This and a
 # `-' as the first argument is then removed from the arguments.
 
-local nm=$compstate[nmatches] ret=1 expl type=-c
+local nm=$compstate[nmatches] ret=1 expl ext
 
 if [[ "$1" = -e ]]; then
-  type=-m
+  ext=yes
   shift
 elif [[ "$1" = - ]]; then
   shift
 fi
 
 # Complete jobs in implicit fg and bg
-if [[ $type = -c && "$PREFIX[1]" = "%" ]]; then
-  _description expl job
-  compgen "$expl[@]" "$@" -j -P '%'
+if [[ -z "$ext" && "$PREFIX[1]" = "%" ]]; then
+  _job -P '%'
   [[ nm -ne compstate[nmatches] ]] && return
 fi
 
-_description expl command
-compgen "$expl[@]" "$@" $type && ret=0
+_description expl 'external command'
+compadd "$expl[@]" "$@" - "${(k@)commands}" && ret=0
+
+if [[ -z "$ext" ]]; then
+  _description expl 'builtin command'
+  compadd "$expl[@]" "$@" - "${(k@)builtins[(R)^?disabled*]}" && ret=0
+  _description expl 'shell function'
+  compadd "$expl[@]" "$@" - "${(k@)functions[(R)^?disabled*]}" && ret=0
+  _description expl 'alias'
+  compadd "$expl[@]" "$@" - "${(k@)raliases[(R)^?disabled*]}" && ret=0
+  _description expl 'reserved word'
+  compadd "$expl[@]" "$@" - "${(k@)reswords[(R)^?disabled*]}" && ret=0
+fi
 
 if [[ nm -eq compstate[nmatches] ]]; then
   _description expl 'executable file or directory'
diff --git a/Completion/Base/_describe b/Completion/Base/_describe
index 2bb32ad35..e59dc1593 100644
--- a/Completion/Base/_describe
+++ b/Completion/Base/_describe
@@ -2,8 +2,6 @@
 
 # This can be used to add options or values with descriptions as matches.
 
-setopt localoptions extendedglob
-
 local isopt cmd opt expl tmps tmpd tmpmd tmpms ret=1 showd _nm hide
 
 cmd="$words[1]"
diff --git a/Completion/Base/_equal b/Completion/Base/_equal
index 14f28703c..1ba92cdc6 100644
--- a/Completion/Base/_equal
+++ b/Completion/Base/_equal
@@ -3,6 +3,6 @@
 local expl
 
 _description expl alias
-compgen "$expl[@]" -a
+compadd "$@" "$expl[@]" - "${(@k)aliases[(R)^?disabled*]}"
 _description expl command
-compgen "$expl[@]" -m
+compadd "$@" "$expl[@]" - "${(k@)commands}"
diff --git a/Completion/Base/_first b/Completion/Base/_first
index ba5ef6c24..d259824cc 100644
--- a/Completion/Base/_first
+++ b/Completion/Base/_first
@@ -55,7 +55,7 @@
 #         else
 #           _description -V expl "history ($n)"
 #         fi
-#         if compgen "$expl[@]" -Q -H $(( i*10 )) ''; then
+#         if compadd "$expl[@]" -Q - "${(@)historywords:#[\$'\"]*}"; then
 #           # We have found at least one matching word, so we switch
 #           # on menu-completion and make sure that no other
 #           # completion function is called by setting _compskip.
diff --git a/Completion/Base/_job b/Completion/Base/_job
new file mode 100644
index 000000000..081956c51
--- /dev/null
+++ b/Completion/Base/_job
@@ -0,0 +1,26 @@
+#autoload
+
+local expl disp jobs job jids
+
+if [[ "$1" = -r ]]; then
+  jids=( "${(@k)jobstates[(R)running*]}" )
+  shift
+  _description expl 'running job'
+elif [[ "$1" = -s ]]; then
+  jids=( "${(@k)jobstates[(R)running*]}" )
+  shift
+  _description expl 'suspended job'
+else
+  [[ "$1" = - ]] && shift
+  jids=( "${(@k)jobtexts}" )
+  _description expl job
+fi
+
+disp=()
+jobs=()
+for job in "$jids[@]"; do
+  disp=( "$disp[@]" "[${(l:2:: :)job}] ${jobtexts[$job]}" )
+  jobs=( "$jobs[@]" "$job" )
+done
+
+compadd "$@" "$expl[@]" -ld disp - "$jobs[@]"
diff --git a/Completion/Base/_regex_arguments b/Completion/Base/_regex_arguments
index 2608fda11..e2858e66c 100644
--- a/Completion/Base/_regex_arguments
+++ b/Completion/Base/_regex_arguments
@@ -206,7 +206,6 @@ _ra_gen_func () {
 
   print -lr - \
     "$funcname () {" \
-      'setopt localoptions extendedglob' \
       'local _ra_state _ra_left _ra_right _ra_actions' \
       "_ra_state=$start" \
       '_ra_left=' \
@@ -323,8 +322,6 @@ _ra_gen_parse_state () {
 }
 
 _regex_arguments () {
-  setopt localoptions extendedglob
-
   local funcname="_regex_arguments_tmp"
   local funcdef
 
diff --git a/Completion/Base/_tilde b/Completion/Base/_tilde
index d871ddc11..0b81f75a1 100644
--- a/Completion/Base/_tilde
+++ b/Completion/Base/_tilde
@@ -2,12 +2,7 @@
 
 # We use all named directories and user names here. If this is too slow
 # for you or if there are too many of them, you may want to use
-# `compgen -k friends -qS/' or something like that. To get all user names
-# if there are no matches in the `friends' array, add
-#   `(( compstate[nmatches] )) || compgen -nu -qS/'
-# below that.
-
-setopt localoptions extendedglob
+# `compadd -qS/ - "$friends[@]"' or something like that.
 
 local d s dirs list lines revlines i
 
@@ -20,21 +15,20 @@ else
 fi
 
 if [[ -prefix [-+] ]]; then
-  lines=(${(f)"$(dirs -v)"})
+  lines=("$PWD" "$dirstack[@]")
   integer i
   if [[ ( -prefix - && ! -o pushdminus ) ||
 	( -prefix + && -o pushdminus ) ]]; then
     revlines=( $lines )
     for (( i = 1; i <= $#lines; i++ )); do
-      lines[$i]="$((i-1)) -- ${revlines[-$i]##[0-9]#[	 ]#}"
+      lines[$i]="$((i-1)) -- ${revlines[-$i]}"
     done
   else
     for (( i = 1; i <= $#lines; i++ )); do
-      lines[$i]="$((i-1)) -- ${lines[$i]##[0-9]#[	 ]#}"
+      lines[$i]="$((i-1)) -- ${lines[$i]}"
     done
   fi
   list=(${lines%% *})
-
   compset -P '[-+]'
   _description d 'directory stack'
   compadd "$d[@]" -V dirs -S/ -ld lines -Q - "$list[@]" 
@@ -45,6 +39,5 @@ else
   else
     _description d 'named directory'
   fi
-  compgen "$d[@]" -n "$s[@]"
+  compadd "$d[@]" "$s[@]" - "${(@k)nameddirs}"
 fi
-
diff --git a/Completion/Base/_values b/Completion/Base/_values
index fb70f6e7f..e4ef8af68 100644
--- a/Completion/Base/_values
+++ b/Completion/Base/_values
@@ -1,7 +1,5 @@
 #autoload
 
-setopt localoptions extendedglob
-
 if compvalues -i "$@"; then
 
   local noargs args opts descr action expl sep
diff --git a/Completion/Builtins/_aliases b/Completion/Builtins/_aliases
index 3e4b789b6..0189d6cf3 100644
--- a/Completion/Builtins/_aliases
+++ b/Completion/Builtins/_aliases
@@ -2,5 +2,7 @@
 
 local expl
 
-_description expl alias
-compgen "$expl[@]" -a
+_description expl 'regular alias'
+compadd "$expl[@]" - "${(@k)raliases[(R)^?disabled*]}"
+_description expl 'global alias'
+compadd "$expl[@]" - "${(@k)galiases[(R)^?disabled*]}"
diff --git a/Completion/Builtins/_arrays b/Completion/Builtins/_arrays
index 20681f527..a2aa813b6 100644
--- a/Completion/Builtins/_arrays
+++ b/Completion/Builtins/_arrays
@@ -3,4 +3,4 @@
 local expl
 
 _description expl array
-compgen "$expl[@]" -A
+compadd "$expl[@]" - "${(@k)parameters[(R)*array*]}"
diff --git a/Completion/Builtins/_bg_jobs b/Completion/Builtins/_bg_jobs
index 65f21b483..6d6a8fcc4 100644
--- a/Completion/Builtins/_bg_jobs
+++ b/Completion/Builtins/_bg_jobs
@@ -1,6 +1,3 @@
 #compdef bg
 
-local expl
-
-_description expl 'suspended job'
-compgen "$expl[@]" -z -P '%'
+_job -s -P '%'
diff --git a/Completion/Builtins/_bindkey b/Completion/Builtins/_bindkey
index 891e2cedb..3f964e704 100644
--- a/Completion/Builtins/_bindkey
+++ b/Completion/Builtins/_bindkey
@@ -3,7 +3,7 @@
 # Normally, this completes names of zle widgets, whether the builtin ones
 # or ones defined by the user.  Note that a - allows a wildcard before it,
 # so h-b-s-b will complete to history-beginning-search-backward.  You
-# can alter this by removing the -M ... from the second compgen.
+# can alter this by removing the -M ... from the second compadd.
 #
 # Where appropriate, will complete keymaps instead of widgets.
 
@@ -11,8 +11,8 @@ local expl
 
 if [[ "$words[2]" = -*[DAN]* || "$words[CURRENT-1]" = -*M ]]; then
   _description expl keymap
-  compadd "$expl[@]" - $(bindkey -l)
+  compadd "$expl[@]" - "$zlekeymaps[@]"
 else
   _description expl widget
-  compgen "$expl[@]" -b -M 'r:|-=* r:|=*'
+  compadd "$expl[@]" -M 'r:|-=* r:|=*' - "${(@k)zlewidgets}"
 fi
diff --git a/Completion/Builtins/_builtin b/Completion/Builtins/_builtin
index f925b88a8..06ef1c246 100644
--- a/Completion/Builtins/_builtin
+++ b/Completion/Builtins/_builtin
@@ -8,5 +8,5 @@ else
   local expl
 
   _description expl 'builtin command'
-  compgen "$expl[@]" -eB
+  compadd "$expl[@]" "$@" - "${(k@)builtins[(R)^?disabled*]}"
 fi
diff --git a/Completion/Builtins/_cd b/Completion/Builtins/_cd
index 838b1af24..b407b5a85 100644
--- a/Completion/Builtins/_cd
+++ b/Completion/Builtins/_cd
@@ -11,8 +11,7 @@
 #    it's not a lot of use.  If you don't type the + or - it will
 #    complete directories as normal.
 
-emulate -L zsh
-setopt extendedglob nonomatch
+setopt localoptions nonomatch
 
 local expl
 
diff --git a/Completion/Builtins/_command b/Completion/Builtins/_command
index 4ae274f1f..9f54aae80 100644
--- a/Completion/Builtins/_command
+++ b/Completion/Builtins/_command
@@ -6,6 +6,6 @@ if [[ CURRENT -ge 3 ]]; then
 else
   local expl
 
-  _description expl command
-  compgen "$expl[@]" -em
+  _description expl 'external command'
+  compadd "$expl[@]" "$@" - "${(k@)commands}"
 fi
diff --git a/Completion/Builtins/_compdef b/Completion/Builtins/_compdef
index 1df5758b3..4208c2689 100644
--- a/Completion/Builtins/_compdef
+++ b/Completion/Builtins/_compdef
@@ -1,6 +1,6 @@
 #compdef compdef
 
-local func base=2
+local expl func base=2
 
 while [[ $words[base] = -* ]]; do
   case $words[base] in
@@ -12,11 +12,13 @@ while [[ $words[base] = -* ]]; do
 done
 
 if [ "$delete" ]; then
-  compadd ${(k)_comps}
+  _description expl 'completed command'
+  compadd "$expl[@]" - ${(k)_comps}
 else
-  if [[ CURRENT -eq base  ]]; then
-    for func in ${^~fpath:/.}/_(|*[^~])(N:t); compadd -P_ - ${func#_}
+  if [[ CURRENT -eq base ]]; then
+    _description expl 'completion function'
+    compadd "$expl[@]" - ${^fpath:/.}/_(|*[^~])(N:t)
   else
-    compgen -c
+    _command_names
   fi
 fi
diff --git a/Completion/Builtins/_disable b/Completion/Builtins/_disable
index b219f4b7e..fe6b991b2 100644
--- a/Completion/Builtins/_disable
+++ b/Completion/Builtins/_disable
@@ -4,19 +4,19 @@ local prev="$words[CURRENT-1]" ret=1 expl
 
 if [[ "$prev" = -*a* ]]; then
   _description expl alias
-  compgen "$expl[@]" -ea && ret=0
+  compadd "$expl[@]" "$@" - "${(k@)aliases[(R)^?disabled*]}" && ret=0
 fi
 if [[ "$prev" = -*f* ]]; then
   _description expl 'shell function'
-  compgen "$expl[@]" -eF && ret=0
+  compadd "$expl[@]" "$@" - "${(k@)functions[(R)^?disabled*]}" && ret=0
 fi
 if [[ "$prev" = -*r* ]]; then
   _description expl 'reserved word'
-  compgen "$expl[@]" -ew && ret=0
+  compadd "$expl[@]" "$@" - "${(k@)reswords[(R)^?disabled*]}" && ret=0
 fi
 if [[ "$prev" != -* ]]; then
   _description expl 'builtin command'
-  compgen "$expl[@]" -eB && ret=0
+  compadd "$expl[@]" "$@" - "${(k@)builtins[(R)^?disabled*]}" && ret=0
 fi
 
 return ret
diff --git a/Completion/Builtins/_enable b/Completion/Builtins/_enable
index a698a3895..b4f0356bc 100644
--- a/Completion/Builtins/_enable
+++ b/Completion/Builtins/_enable
@@ -4,19 +4,19 @@ local prev="$words[CURRENT-1]" ret=1 expl
 
 if [[ "$prev" = -*a* ]]; then
   _description expl alias
-  compgen "$expl[@]" -da && ret=0
+  compadd "$expl[@]" "$@" - "${(k@)aliases[(R)?disabled*]}" && ret=0
 fi
 if [[ "$prev" = -*f* ]]; then
   _description expl 'shell function'
-  compgen "$expl[@]" -dF && ret=0
+  compadd "$expl[@]" "$@" - "${(k@)functions[(R)?disabled*]}" && ret=0
 fi
 if [[ "$prev" = -*r* ]]; then
   _description expl 'reserved word'
-  compgen "$expl[@]" -dw && ret=0
+  compadd "$expl[@]" "$@" - "${(k@)reswords[(R)?disabled*]}" && ret=0
 fi
 if [[ "$prev" != -* ]]; then
   _description expl 'builtin command'
-  compgen "$expl[@]" -dB && ret=0
+  compadd "$expl[@]" "$@" - "${(k@)builtins[(R)?disabled*]}" && ret=0
 fi
 
 return ret
diff --git a/Completion/Builtins/_fc b/Completion/Builtins/_fc
index 8d5c82763..c2eaab7bb 100644
--- a/Completion/Builtins/_fc
+++ b/Completion/Builtins/_fc
@@ -1,10 +1,9 @@
 #compdef fc
 
-local prev="$words[CURRENT-1]" expl
+local prev="$words[CURRENT-1]"
 
 if [[ "$prev" = -*e ]]; then
-  _description expl command
-  compgen "$expl[@]" -c
+  _command_names -e
 elif [[ "$prev" = -[ARWI]## ]]; then
   _files
 fi
diff --git a/Completion/Builtins/_functions b/Completion/Builtins/_functions
index 7aa30b7bc..d5f32d2e3 100644
--- a/Completion/Builtins/_functions
+++ b/Completion/Builtins/_functions
@@ -3,4 +3,4 @@
 local expl
 
 _description expl 'shell function'
-compgen "$expl[@]" -F
+compadd "$expl[@]" "$@" - "${(k@)functions[(R)^?disabled*]}"
diff --git a/Completion/Builtins/_hash b/Completion/Builtins/_hash
index c8169c590..c06d63699 100644
--- a/Completion/Builtins/_hash
+++ b/Completion/Builtins/_hash
@@ -7,11 +7,11 @@ if [[ "$words[2]" = -*d* ]]; then
     _path_files -g '*(-/)'
   else
     _description expl 'named directory'
-    compgen "$expl[@]" -n -q -S '='
+    compadd "$expl[@]" -q -S '=' - "${(@k)nameddirs}"
   fi
 elif compset -P 1 '*\='; then
   _files -/g '*(*)'
 else
   _description expl command
-  compgen "$expl[@]" -m -q -S '='
+  compadd "$expl[@]" -q -S '=' - "${(@k)commands}"
 fi
diff --git a/Completion/Builtins/_jobs b/Completion/Builtins/_jobs
index b1ff31b4a..c17b73c92 100644
--- a/Completion/Builtins/_jobs
+++ b/Completion/Builtins/_jobs
@@ -1,6 +1,3 @@
 #compdef disown fg jobs
 
-local expl
-
-_description expl job
-compgen "$expl[@]" -j -P '%'
+_job -P '%'
diff --git a/Completion/Builtins/_kill b/Completion/Builtins/_kill
index da70cf753..b79bfd6c0 100644
--- a/Completion/Builtins/_kill
+++ b/Completion/Builtins/_kill
@@ -8,9 +8,9 @@ if compset -P 1 -; then
 else
   local ret=1
 
-  _description expl job
-  compgen "$expl[@]" -P '%' -j && ret=0
-  list=("${(@Mr:COLUMNS-1:)${(f@)$(ps ${=compconfig[ps_listargs]:-$=compconfig[ps_args]} 2>/dev/null)}[2,-1]:#[ 	]#${PREFIX}[0-9]#${SUFFIX}[ 	]*}")
+  _job && ret=0
+
+  list=("${(@M)${(f@)$(ps ${=compconfig[ps_listargs]:-$=compconfig[ps_args]} 2>/dev/null)}[2,-1]:#[ 	]#${PREFIX}[0-9]#${SUFFIX}[ 	]*}")
   _description expl 'process ID'
   compadd "$expl[@]" -ld list - ${${${(f)"$(ps $=compconfig[ps_args] 2>/dev/null)"}[2,-1]## #}%% *} &&
     ret=0
diff --git a/Completion/Builtins/_popd b/Completion/Builtins/_popd
index 9054befb7..07dc4a87f 100644
--- a/Completion/Builtins/_popd
+++ b/Completion/Builtins/_popd
@@ -5,6 +5,45 @@
 # way round if pushdminus is set). Note that this function is also called
 # from _cd for cd and pushd.
 
+setopt localoptions nonomatch
+
+[[ $PREFIX = [-+]* ]] || return 1
+
+local expl list lines revlines ret=1 i
+
+IPREFIX=$PREFIX[1]
+PREFIX=$PREFIX[2,-1]
+
+# get the list of directories with their canonical number
+# and turn the lines into an array, removing the current directory
+lines=( "$dirstack[@]" )
+if [[ ( $IPREFIX = - && ! -o pushdminus ) ||
+      ( $IPREFIX = + && -o pushdminus ) ]]; then
+  integer i
+  revlines=( $lines )
+  for (( i = 1; i <= $#lines; i++ )); do
+    lines[$i]="$((i-1)) -- ${revlines[-$i]}"
+  done
+else
+  for (( i = 1; i <= $#lines; i++ )); do
+    lines[$i]="$i -- ${lines[$i]}"
+  done
+fi
+# get the array of numbers only
+list=(${lines%% *})
+_description expl 'directory stack index'
+compadd "$expl[@]" -ld lines -V dirs -Q - "$list[@]" && ret=0
+[[ -z $compstate[list] ]] && compstate[list]=list && ret=0
+[[ -n $compstate[insert] ]] && compstate[insert]=menu && ret=0
+
+return ret
+#compdef popd
+
+# This just completes the numbers after +, showing the full directory list
+# with numbers. For - we do the same thing, but reverse the numbering (other
+# way round if pushdminus is set). Note that this function is also called
+# from _cd for cd and pushd.
+
 emulate -L zsh
 setopt extendedglob nonomatch
 
diff --git a/Completion/Builtins/_set b/Completion/Builtins/_set
index 297a15d3b..b96eb0efb 100644
--- a/Completion/Builtins/_set
+++ b/Completion/Builtins/_set
@@ -1,11 +1,9 @@
 #compdef set
 
-local prev="$words[CURRENT-1]" expl
+local prev="$words[CURRENT-1]"
 
 if [[ "$prev" = [-+]o ]]; then
-  _description expl 'zsh option'
-  compgen "$expl[@]" -o
+  _options
 elif [[ "$prev" = -A ]]; then
-  _description expl array
-  compgen "$expl[@]" -A
+  _arrays
 fi
diff --git a/Completion/Builtins/_stat b/Completion/Builtins/_stat
index 5a39e56e5..322f042ec 100644
--- a/Completion/Builtins/_stat
+++ b/Completion/Builtins/_stat
@@ -3,8 +3,7 @@
 local expl
 
 if [[ "$words[CURRENT-1]" = -[AH] ]]; then
-  _description expl array
-  compgen "$expl[@]" -A
+  _arrays
 else
   _description expl 'inode element'
   [[ "$PREFIX[1]" = + ]] &&
diff --git a/Completion/Builtins/_trap b/Completion/Builtins/_trap
index d6fc7b388..0f0209914 100644
--- a/Completion/Builtins/_trap
+++ b/Completion/Builtins/_trap
@@ -3,9 +3,9 @@
 local expl
 
 if [[ CURRENT -eq 2 ]]; then
-  _description expl command
-  compgen "$expl[@]" -c
+  compset -q
+  _normal
 else
   _description expl signal
-  compgen "$expl[@]" -k signals
+  compadd "$expl[@]" - "$signals[@]"
 fi
diff --git a/Completion/Builtins/_unhash b/Completion/Builtins/_unhash
index eb1787936..dfebd5399 100644
--- a/Completion/Builtins/_unhash
+++ b/Completion/Builtins/_unhash
@@ -4,19 +4,18 @@ local fl="$words[2]" ret=1 expl
 
 if [[ "$fl" = -*d* ]]; then
   _description expl 'named directory'
-  compgen "$expl[@]" -n && ret=0
+  compadd "$expl[@]" - "${(@k)nameddirs}" && ret=0
 fi
 if [[ "$fl" = -*a* ]]; then
   _description expl alias
-  compgen "$expl[@]" -a && ret=0
+  compadd "$expl[@]" - "${(@k)aliases}" && ret=0
 fi
 if [[ "$fl" = -*f* ]]; then
   _description expl 'shell function'
-  compgen "$expl[@]" -F && ret=0
+  compadd "$expl[@]" - "${(@k)functions}" && ret=0
 fi
 if [[ "$fl" != -* ]]; then
-  _description expl command
-  compgen "$expl[@]" -m && ret=0
+  _command_names -e && ret=0
 fi
 
 return ret
diff --git a/Completion/Builtins/_wait b/Completion/Builtins/_wait
index 17a030dad..68ca187e8 100644
--- a/Completion/Builtins/_wait
+++ b/Completion/Builtins/_wait
@@ -1,21 +1,10 @@
 #compdef wait
 
-# This uses two configuration keys:
-#
-#  ps_args
-#    This can be set to options of the ps(1) command that should be
-#    used when invoking it to get the pids to complete.
-#
-#  ps_listargs
-#    This defaults to the value of the `ps_args' key and defines
-#    options for the ps command that are to be used when creating
-#    the list to display during completion.
-
 local list ret=1 expl
 
-_description expl job
-compgen "$expl[@]" -P '%' -j && ret=0
-list=("${(@Mr:COLUMNS-1:)${(f)$(ps ${=compconfig[ps_listargs]:-$=compconfig[ps_args]} 2>/dev/null)}[2,-1]:#[ 	]#${PREFIX}[0-9]#${SUFFIX}[ 	]*}")
+_job -P '%' && ret=0
+
+list=("${(@M)${(f)$(ps ${=compconfig[ps_listargs]:-$=compconfig[ps_args]} 2>/dev/null)}[2,-1]:#[ 	]#${PREFIX}[0-9]#${SUFFIX}[ 	]*}")
 _description expl 'process ID'
 compadd "$expl[@]" -ld list - ${${${(f)"$(ps $=compconfig[ps_args] 2>/dev/null)"}[2,-1]## #}%% *} && ret=0
 
diff --git a/Completion/Builtins/_which b/Completion/Builtins/_which
index d67af7316..30d20ab36 100644
--- a/Completion/Builtins/_which
+++ b/Completion/Builtins/_which
@@ -2,9 +2,13 @@
 
 local expl
 
-_description expl command
-compgen "$expl[@]" -c
-_description expl alias
-compgen "$expl[@]" -a
+_description expl 'external command'
+compadd "$expl[@]" "$@" - "${(k@)commands}" && ret=0
+_description expl 'builtin command'
+compadd "$expl[@]" "$@" - "${(k@)builtins[(R)^?disabled*]}" && ret=0
 _description expl 'shell function'
-compgen "$expl[@]" -F
+compadd "$expl[@]" "$@" - "${(k@)functions[(R)^?disabled*]}" && ret=0
+_description expl 'alias'
+compadd "$expl[@]" "$@" - "${(k@)raliases[(R)^?disabled*]}" && ret=0
+_description expl 'reserved word'
+compadd "$expl[@]" "$@" - "${(k@)reswords[(R)^?disabled*]}" && ret=0
diff --git a/Completion/Builtins/_zle b/Completion/Builtins/_zle
index 06e8fcad3..327d67c62 100644
--- a/Completion/Builtins/_zle
+++ b/Completion/Builtins/_zle
@@ -4,8 +4,8 @@ local expl
 
 if [[ "$words[2]" = -N && CURRENT -eq 3 ]]; then
   _description expl 'widget shell function'
-  compgen "$expl[@]" -F
+  compadd "$expl[@]" "$@" - "${(k@)functions[(R)^?disabled*]}" && ret=0
 else
   _description expl widget
-  compgen "$expl[@]" -b
+  compadd "$expl[@]" - "${(@k)zlewidgets}"
 fi
diff --git a/Completion/Builtins/_zmodload b/Completion/Builtins/_zmodload
index 429128517..a0d5987c4 100644
--- a/Completion/Builtins/_zmodload
+++ b/Completion/Builtins/_zmodload
@@ -4,10 +4,10 @@ local fl="$words[2]" expl
 
 if [[ "$fl" = -*(a*u|u*a)* || "$fl" = -*a* && CURRENT -ge 4 ]]; then
   _description expl 'builtin command'
-  compgen "$expl[@]" -B
+  compadd "$expl[@]" "$@" - "${(k@)builtins[(R)^?disabled*]}" && ret=0
 elif [[ "$fl" = -*u* ]]; then
   _description expl module
-  compadd "$expl[@]" - $(zmodload)
+  compadd "$expl[@]" - "${(@k)modules}"
 else
   _description expl 'module file'
   compadd "$expl[@]" - ${^module_path}/*.s[ol](N:t:r)
diff --git a/Completion/Commands/_bash_completions b/Completion/Commands/_bash_completions
index 5f3adc1e0..ee663fc30 100644
--- a/Completion/Commands/_bash_completions
+++ b/Completion/Commands/_bash_completions
@@ -30,7 +30,7 @@ local key=$KEYS[-1]
 case $key in
   '!') _main_complete _command_names
        ;;
-  '$') compgen -E
+  '$') compadd - "${(@k)parameters[(R)*export*]}"
        ;;
   '@') _main_complete _hosts
        ;;
diff --git a/Completion/Commands/_history_complete_word b/Completion/Commands/_history_complete_word
index f459046d2..1112339af 100644
--- a/Completion/Commands/_history_complete_word
+++ b/Completion/Commands/_history_complete_word
@@ -1,31 +1,108 @@
-#compdef -k complete-word \e/
+#compdef -K _history-complete-older complete-word \e/ _history-complete-newer complete-word \e,
+#
+# Complete words from the history
+#
+# by Adam Spiers, with help gratefully received from
+# Sven Wischnowsky and Bart Schaefer
+#
+# Available configuration keys:
+#
+#   history_list -- display lists of available matches
+#   history_stop -- prevent looping at beginning and end of matches
+#                   during menu-completion
+#   history_sort -- sort matches lexically (default is to sort by age)
+#   history_remove_all_dups --
+#                   remove /all/ duplicate matches rather than just
+#                   consecutives
+#
 
-local expl
+_history_complete_word () {
+  local expl direction
 
-if [[ -n "$compstate[old_list]" && -n "$compconfig[history_stop]" ]]; then
-  if [[ -z "$_hist_menu_end" &&
-        compstate[old_insert] -lt _hist_menu_length ]]; then
-    compstate[old_list]=keep
-    compstate[insert]=$((compstate[old_insert]+1))
+  if [[ $WIDGET = *newer ]]; then
+    direction=older
   else
-    _hist_menu_end=yes
-    if [[ "$compconfig[history_stop]" = verbose ]]; then
-      _message 'end of history reached'
-    else
-      compstate[old_list]=keep
-      compstate[insert]=_hist_menu_length
+    direction=newer
+  fi
+
+  [[ -z "$compconfig[history_list]" ]] && compstate[list]=''
+
+  if [[ -n "$compstate[old_list]" &&
+    ( -n "$compconfig[history_stop]" || "$compstate[insert]" = menu ) ]]; then
+    # array of matches is newest -> oldest (reverse of history order)
+    if [[ "$direction" == 'older' ]]; then
+      if [[ compstate[old_insert] -eq $_hist_menu_length ||
+            "$_hist_stop" == 'oldest' ]]; then
+        _hist_stop='oldest'
+        [[ "$compconfig[history_stop]" = verbose ]] &&
+          _message 'beginning of history reached'
+      elif [[ "$_hist_stop" == 'newest' ]]; then
+        zle -Rc
+        _history_complete_word_gen_matches
+      else
+        compstate[old_list]=keep
+        (( compstate[insert] = compstate[old_insert] + 1 ))
+      fi
+    elif [[ "$direction" == 'newer' ]]; then
+      if [[ compstate[old_insert] -eq 1 || "$_hist_stop" == 'newest' ]]; then
+        _hist_stop='newest'
+        [[ "$compconfig[history_stop]" = verbose ]] &&
+          _message 'end of history reached'
+      elif [[ "$_hist_stop" == 'oldest' ]]; then
+        zle -Rc
+        _history_complete_word_gen_matches
+      else
+        compstate[old_list]=keep
+        (( compstate[insert] = compstate[old_insert] - 1 ))
+      fi
     fi
+  else
+    _hist_stop=''
+    _hist_old_prefix="$PREFIX"
+    _history_complete_word_gen_matches
   fi
-else
-  if [[ -n "$compconfig[history_sort]" ]]; then
-    _description expl 'history word'
+
+  [[ -n "$compstate[nmatches]" ]]
+}
+
+_history_complete_word_gen_matches () {
+  if [[ -n "$compconfig[history_list]" ]]; then
+    if [[ -n "$compconfig[history_sort]" ]]; then
+      _description expl 'history word'
+    else
+      _description -V expl 'history word'
+    fi
   else
-    _description -V expl 'history word'
+    if [[ -n "$compconfig[history_sort]" ]]; then
+      expl=()
+    else
+      expl=('-V' '')
+    fi
   fi
-  compgen "$expl[@]" -Q -H 0 ''
-  if [[ -n "$compconfig[history_stop]" ]]; then
-    compstate[insert]=1
-    _hist_menu_length="$compstate[nmatches]"
-    _hist_menu_end=''
+
+  [[ -n "$_hist_stop" ]] && PREFIX="$_hist_old_prefix"
+
+  local rem_dups
+  if [[ -n "$compconfig[history_remove_all_dups]" ]]; then
+    rem_dups=''
+  else
+    rem_dups='-1'
   fi
-fi
+
+  compadd "$expl[@]" $rem_dups -Q - "${(@)historywords:#[\$'\"]*}"
+
+  _hist_menu_length="$compstate[nmatches]"
+
+  case "$direction" in 
+    newer)  compstate[insert]=$_hist_menu_length
+	    [[ -n "$_hist_stop" ]] && (( compstate[insert]-- ))
+            ;;
+    older)  compstate[insert]=1
+	    [[ -n "$_hist_stop" ]] && (( compstate[insert]++ ))
+            ;;
+  esac
+
+  [[ -n "$_hist_stop" ]] && _hist_stop=''
+}
+
+_history_complete_word "$@"
diff --git a/Completion/Commands/_read_comp b/Completion/Commands/_read_comp
index a32879b56..8a46d8af2 100644
--- a/Completion/Commands/_read_comp
+++ b/Completion/Commands/_read_comp
@@ -6,9 +6,9 @@
 # evaluate to generate the completions; unambiguous strings in the function
 # name are automatically completed.
 #
-# Else it is taken to be a set of arguments for compgen to generate a list
+# Else it is taken to be a set of arguments for compadd to generate a list
 # of choices.  The possibilities are the same as the flags for generating
-# completions given in the zshcompctl manual page.  Note the arguments are
+# completions given in the zshcompwid manual page.  Note the arguments are
 # verbatim:  include minus signs, spaces, quotes, etc.
 #
 # On subsequent calls, the same completion will be re-performed.  To
@@ -37,7 +37,7 @@ if [[ compstate[matcher] -gt 1 ||
   if [[ $_read_comp = _* ]]; then
     eval $_read_comp
   else
-    eval "compgen $_read_comp"
+    eval "compadd $_read_comp"
   fi
   return
 fi
@@ -154,5 +154,5 @@ zle -cR ''
 if [[ $str = _* ]]; then
   eval $str
 else
-  eval "compgen $str"
+  eval "compadd $str"
 fi
diff --git a/Completion/Core/_approximate b/Completion/Core/_approximate
index 57b327e64..235e324f7 100644
--- a/Completion/Core/_approximate
+++ b/Completion/Core/_approximate
@@ -70,22 +70,6 @@ compadd() {
   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
@@ -99,11 +83,6 @@ 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
@@ -135,7 +114,7 @@ while [[ _comp_correct -le comax ]]; do
       compstate[force_list]=list
     fi
     compstate[matcher]="$compstate[total_matchers]"
-    unfunction compadd compgen
+    unfunction compadd
 
     return 0
   fi
@@ -147,6 +126,6 @@ while [[ _comp_correct -le comax ]]; do
 done
 
 compstate[matcher]="$compstate[total_matchers]"
-unfunction compadd compgen
+unfunction compadd
 
 return 1
diff --git a/Completion/Core/_main_complete b/Completion/Core/_main_complete
index 304a97828..72233a59b 100644
--- a/Completion/Core/_main_complete
+++ b/Completion/Core/_main_complete
@@ -9,13 +9,8 @@
 #
 #   local _set_options _unset_options
 #
-#   if zmodload -e parameter; then
-#     _set_options=(${(k)options[(R)on]})
-#     _unset_options=(${(k)options[(R)off]})
-#   else
-#     _set_options=("${(@f)$({ unsetopt kshoptionprint; setopt } 2>/dev/null)}")
-#     _unset_options=("${(@f)$({ unsetopt kshoptionprint; unsetopt } 2>/dev/null)}")
-#   fi
+#   _set_options=(${(k)options[(R)on]})
+#   _unset_options=(${(k)options[(R)off]})
 #
 # This is needed because completion functions may set options locally
 # which makes the output of setopt and unsetopt reflect a different
@@ -26,7 +21,7 @@ local comp post ret=1 _compskip
 
 typeset -U _lastdescr
 
-setopt localoptions nullglob rcexpandparam
+setopt localoptions nullglob rcexpandparam extendedglob
 unsetopt markdirs globsubst shwordsplit nounset ksharrays
 
 # Special completion contexts after `~' and `='.
diff --git a/Completion/Core/_normal b/Completion/Core/_normal
index 085c738d2..79efaeb97 100644
--- a/Completion/Core/_normal
+++ b/Completion/Core/_normal
@@ -27,11 +27,7 @@ elif [[ "$command" == */* ]]; then
   cmd2="${command:t}"
 else
   cmd1="$command"
-  if zmodload -e parameter; then
-    cmd2="$commands[$command]"
-  else
-    cmd2=$(whence -p - $command)
-  fi
+  cmd2="$commands[$command]"
 fi
 
 # See if there are any matching pattern completions.
diff --git a/Completion/Core/_options b/Completion/Core/_options
index 356cd70db..0232db857 100644
--- a/Completion/Core/_options
+++ b/Completion/Core/_options
@@ -5,4 +5,5 @@
 local expl
 
 _description expl 'zsh option'
-compgen "$expl[@]" "$@" -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o
+compadd "$expl[@]" "$@" -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' - \
+    "${(@k)options}"
diff --git a/Completion/Core/_parameters b/Completion/Core/_parameters
index 34a8c3e9b..fdb786231 100644
--- a/Completion/Core/_parameters
+++ b/Completion/Core/_parameters
@@ -3,17 +3,11 @@
 # This should be used to complete parameter names if you need some of the
 # extra options of compadd. It completes only non-local parameters.
 
-setopt localoptions extendedglob
-
 local pars expl
 
 _description expl parameter
 
-if zmodload -e parameter; then
-  pars=( ${(k)parameters[(R)^*local*]} )
-else
-  pars=( ${${${(f)"$(typeset +)"}:#*local *}##* } )
-fi
+pars=( ${(k)parameters[(R)^*local*]} )
 
 compadd "$expl[@]" "$@" - $pars
 
diff --git a/Completion/Core/_path_files b/Completion/Core/_path_files
index 9d44180c8..cbec82c8a 100644
--- a/Completion/Core/_path_files
+++ b/Completion/Core/_path_files
@@ -10,7 +10,7 @@ local nm=$compstate[nmatches] menu match matcher
 
 typeset -U prepaths exppaths
 
-setopt localoptions nullglob rcexpandparam extendedglob
+setopt localoptions nullglob rcexpandparam
 unsetopt markdirs globsubst shwordsplit nounset
 
 local sopt='-' gopt='' opt
@@ -110,36 +110,6 @@ orig="${PREFIX}${SUFFIX}"
    ( $#compstate[pattern_match] -ne 0 &&
      "${orig#\~}" != "${${orig#\~}:q}" ) ]] && menu=yes
 
-# We will first try normal completion called with `compgen', but only if we
-# weren't given a `-F', `-r', or `-R' option or we are in the string.
-
-if [[ -z "$suf" && $#ignore -eq 0 && $#remsfx -eq 0 &&
-      -z "$_comp_correct" ]]; then
-  # First build an array containing the `-W' option, if there is any and we
-  # want to use it. We don't want to use it if the string from the command line
-  # is a absolute path or relative to the current directory.
-
-  if [[ -z "$prepaths[1]" || "$pre[1]" = [~/] || "$pre" = (.|..)/* ]]; then
-    tmp1=()
-  else
-    tmp1=(-W "( $prepaths )")
-  fi
-
-  # Now call compgen.
-
-  if [[ -z "$gopt" ]]; then
-    compgen "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \
-            "$tmp1[@]" "$matcher[@]" $sopt
-  else
-    compgen "$addpfx[@]" "$addsfx[@]" "$group[@]" "$expl[@]" \
-            "$tmp1[@]" "$matcher[@]" $sopt -g "$pats"
-  fi
-
-  # If this generated any matches, we don't want to do in-path completion.
-
-  [[ compstate[nmatches] -eq nm ]] || return 0
-fi
-
 # If given no `-F' option, we want to use `fignore'.
 
 (( $#ignore )) || ignore=(-F fignore)
diff --git a/Completion/Core/compinit b/Completion/Core/compinit
index ca9240f92..b3472ca0e 100644
--- a/Completion/Core/compinit
+++ b/Completion/Core/compinit
@@ -78,6 +78,10 @@ while [[ $# -gt 0 && $1 = -[dDf] ]]; do
   fi
 done
 
+# We need the parameter modules.
+
+zmodload -i parameter zleparameter
+
 # The associative array containing the definitions for the commands.
 # Definitions for patterns will be stored in the normal arrays `_patcomps'
 # and `_postpatcomps'.
diff --git a/Completion/Debian/_apt b/Completion/Debian/_apt
index e4593d54e..352dd3f2d 100644
--- a/Completion/Debian/_apt
+++ b/Completion/Debian/_apt
@@ -473,7 +473,7 @@ _apt-config () {
     -- \
     /$'shell\0'/ \
       \( \
-	/$'[^\0]#\0'/ :'compgen "$expl_shell_var[@]" -v' \
+	/$'[^\0]#\0'/ :'compadd "$expl_shell_var[@]" - "${(@k)parameters}' \
 	/$'[^\0]#\0'/ :'compadd "$expl_config_key[@]" - ${${(f)"$(apt-config dump 2>&1)"}% *}' \
       \) \# \| \
     /$'dump\0'/ \| \
diff --git a/Completion/User/_cvs b/Completion/User/_cvs
index 24a21f8be..ba732cb63 100644
--- a/Completion/User/_cvs
+++ b/Completion/User/_cvs
@@ -514,7 +514,7 @@ builtin functions _cvs_directories >&- ||
 _cvs_directories () {
   if [[ -d ${pref}CVS ]]; then
     _cvs_setup_direntries
-    (( $#entries )) && compgen "$@" -g "${(j:|:)${(@)entries:q}}"
+    (( $#entries )) && _files "$@" -g "${(j:|:)${(@)entries:q}}"
   else
     _files "$@"
   fi
@@ -526,7 +526,7 @@ _cvs_files () {
   _cvs_setup_prefix
   if [[ -d ${pref}CVS ]]; then
     _cvs_setup_allentries
-    (( $#entries )) && compgen "$@" -g "${(j:|:)${(@)entries:q}}"
+    (( $#entries )) && _files "$@" -g "${(j:|:)${(@)entries:q}}"
   else
     _files "$@"
   fi
@@ -538,7 +538,7 @@ _cvs_files_modified () {
   _cvs_setup_prefix
   if [[ -d ${pref}CVS ]]; then
     _cvs_setup_modentries
-    (( $#entries )) && compgen "$@" -g "${(j:|:)${(@)entries:q}}"
+    (( $#entries )) && _files "$@" -g "${(j:|:)${(@)entries:q}}"
   else
     _files "$@"
   fi
@@ -572,8 +572,8 @@ _cvs_files_unmaintained () {
     omit=($_cvs_ignore_default ${entries:q} ${=cvsignore})
     [[ -r ~/.cvsignore ]] && omit=($omit $(<~/.cvsignore))
     [[ -r ${pref}.cvsignore ]] && omit=($omit $(<${pref}.cvsignore))
-    compgen "$@" -g '*~(*/|)('${(j:|:)omit}')(D)' ||
-    compgen "$@" -g '*~(*/|)('${(j:|:)${(@)entries:q}}')(D)' ||
+    _files "$@" -g '*~(*/|)('${(j:|:)omit}')(D)' ||
+    _files "$@" -g '*~(*/|)('${(j:|:)${(@)entries:q}}')(D)' ||
     _cvs_directories "$@"
   else
     _files "$@"
diff --git a/Completion/User/_gdb b/Completion/User/_gdb
index 4e21d7448..fc882ff90 100644
--- a/Completion/User/_gdb
+++ b/Completion/User/_gdb
@@ -47,8 +47,7 @@ else
   if [[ $#w -gt 1 ]]; then
     _files && ret=0
     _description expl 'process ID'
-    list=("${(F)${(@Mr:COLUMNS-1:)${(f)$(ps ${=compconfig[ps_listargs]:-$=compconfig[ps_args]} 2>/dev/null)}[2,-1]:#[ 	]#${PREFIX}[0-9]#${SUFFIX}[ 	]*${w[1]:t}}}
-")
+    list=("${(@M)${(f)$(ps ${=compconfig[ps_listargs]:-$=compconfig[ps_args]} 2>/dev/null)}[2,-1]:#[ 	]#${PREFIX}[0-9]#${SUFFIX}[ 	]*${w[1]:t}*}")
     compadd "$expl[@]" -ld list - ${${${(M)${(f)"$(ps $=compconfig[ps_args] 2>/dev/null)"}:#*${w[1]:t}*}## #}%% *} && ret=0
 
     return ret
diff --git a/Completion/User/_man b/Completion/User/_man
index fd5702227..50445edf6 100644
--- a/Completion/User/_man
+++ b/Completion/User/_man
@@ -1,6 +1,6 @@
 #compdef man apropos whatis
 
-setopt localoptions rcexpandparam extendedglob
+setopt localoptions rcexpandparam
 
 local rep expl star approx
 
diff --git a/Completion/User/_mh b/Completion/User/_mh
index 3a2e53794..87ab8a18e 100644
--- a/Completion/User/_mh
+++ b/Completion/User/_mh
@@ -39,8 +39,7 @@ elif compset -P 1 '[+@]' || [[ "$prev" = -draftfolder ]]; then
   _description expl 'MH folder'
   _path_files "$expl[@]" -W mhpath -/
 elif [[ "$prev" = -(editor|(whatnow|rmm|show|more)proc) ]]; then
-  _description expl command
-  compgen "$expl[@]" -c
+  _command_names -e
 elif [[ "$prev" = -file ]]; then
   _files
 elif [[ "$prev" = -(form|audit|filter) ]]; then
@@ -52,7 +51,7 @@ elif [[ "$prev" = -(form|audit|filter) ]]; then
   mhfpath=($mymhdir $mhlib)
 
   _description expl 'MH template file'
-  compgen "$expl[@]" -W mhfpath -g '*(.)'
+  _files "$expl[@]" -W mhfpath -g '*(.)'
 elif [[ "$prev" = -(no|)cc ]]; then
   _description expl 'CC address'
   compadd "$expl[@]" all to cc me
@@ -79,7 +78,7 @@ else
   compadd "$expl[@]" $(mark $foldnam 2>/dev/null | awk -F: '{ print $1 }') &&
       ret=0
   compadd "$expl[@]" reply next cur prev first last all unseen && ret=0
-  compgen "$expl[@]" -W folddir -g '<->' && ret=0
+  _files "$expl[@]" -W folddir -g '<->' && ret=0
 
   return ret
 fi
diff --git a/Completion/User/_nslookup b/Completion/User/_nslookup
index 6b2585f3d..7bf97a8ad 100644
--- a/Completion/User/_nslookup
+++ b/Completion/User/_nslookup
@@ -19,8 +19,6 @@
 # other characters than lower case letters, we try to call the function
 # `_nslookup_host'.
 
-setopt localoptions extendedglob
-
 local state expl ret=1 setopts
 
 setopts=(
diff --git a/Completion/User/_tar b/Completion/User/_tar
index 63838e83b..d779f6cf1 100644
--- a/Completion/User/_tar
+++ b/Completion/User/_tar
@@ -13,9 +13,6 @@
 #    tar itself (GNU tar)
 #  - Things like --directory=... are also completed correctly.
 
-emulate -LR zsh
-setopt extendedglob
-
 local _tar_cmd tf tmp del
 
 # First we collect in `_tar_cmd' single letter options describing what
diff --git a/Completion/User/_urls b/Completion/User/_urls
index 3b9afc4fd..1bc1ac76d 100644
--- a/Completion/User/_urls
+++ b/Completion/User/_urls
@@ -37,8 +37,6 @@
 #    name used by a user placing web pages within their home area.
 #    e.g. compconf urls_localhttp=www:/usr/local/apache/htdocs:public_html
 
-setopt localoptions extendedglob
-
 local ipre scheme host user hosts ret=1 expl
 local urls_path="${compconfig[urls_path]:-${ZDOTDIR:-$HOME}/.zsh/urls}"
 local localhttp_servername="${${(@s.:.)compconfig[urls_localhttp]}[1]}"
diff --git a/Completion/User/_users b/Completion/User/_users
index a054ccc66..6d0b1ce2f 100644
--- a/Completion/User/_users
+++ b/Completion/User/_users
@@ -10,4 +10,4 @@ _description expl user
 [[ "${(t)users}" = *array* ]] &&
     compadd "$expl[@]" "$@" - "$users[@]" && return 0
 
-compgen "$@" "$expl[@]" -u
+compadd "$@" "$expl[@]" - "${(@k)userdirs}"
diff --git a/Completion/User/_whereis b/Completion/User/_whereis
index 42fc30969..6f11b516c 100644
--- a/Completion/User/_whereis
+++ b/Completion/User/_whereis
@@ -1,6 +1,3 @@
 #compdef whereis
 
-local expl
-
-_description expl command
-compgen "$expl[@]" -m
+_command_names -e
diff --git a/Completion/User/_whois b/Completion/User/_whois
index 827ebe627..580529a72 100644
--- a/Completion/User/_whois
+++ b/Completion/User/_whois
@@ -1,7 +1,6 @@
 #compdef whois
 
 _whois () {
-  setopt localoptions extendedglob
   _whois_setup
   $_whois_comp
 }
diff --git a/Completion/X/_x_font b/Completion/X/_x_font
index f0b13c7a8..4ba21ddff 100644
--- a/Completion/X/_x_font
+++ b/Completion/X/_x_font
@@ -11,4 +11,4 @@ if (( ! $+_font_cache )); then
 fi
 
 _description expl font
-compgen -M 'r:|-=* r:|=*' "$expl[@]" "$@" -S '' -k _font_cache
+compadd -M 'r:|-=* r:|=*' "$expl[@]" "$@" -S '' - "$_font_cache[@]"
diff --git a/Completion/X/_x_window b/Completion/X/_x_window
index 118c7f131..65d2b72e2 100644
--- a/Completion/X/_x_window
+++ b/Completion/X/_x_window
@@ -1,7 +1,5 @@
 #autoload
 
-setopt localoptions extendedglob
-
 local list expl
 
 list=( "${(@)${(M@)${(@f)$(xwininfo -root -tree)}:#[ 	]#0x[0-9a-f]# \"*}##[ 	]#}" )
diff --git a/Completion/X/_xmodmap b/Completion/X/_xmodmap
index 29e584623..4e7a8bfc8 100644
--- a/Completion/X/_xmodmap
+++ b/Completion/X/_xmodmap
@@ -1,7 +1,5 @@
 #compdef xmodmap
 
-setopt localoptions extendedglob
-
 local state line ret=1
 typeset -A opt_args
 
diff --git a/Doc/Makefile.in b/Doc/Makefile.in
index 03ef6bbc0..a1443f2d5 100644
--- a/Doc/Makefile.in
+++ b/Doc/Makefile.in
@@ -57,11 +57,11 @@ Zsh/exec.yo Zsh/expn.yo \
 Zsh/filelist.yo Zsh/files.yo Zsh/func.yo Zsh/grammar.yo Zsh/manual.yo \
 Zsh/index.yo Zsh/intro.yo Zsh/invoke.yo Zsh/jobs.yo Zsh/metafaq.yo \
 Zsh/modules.yo Zsh/mod_cap.yo \
-Zsh/mod_clone.yo Zsh/mod_comp1.yo Zsh/mod_compctl.yo Zsh/mod_complist.yo \
+Zsh/mod_clone.yo Zsh/mod_complete.yo Zsh/mod_compctl.yo Zsh/mod_complist.yo \
 Zsh/mod_deltochar.yo Zsh/mod_example.yo Zsh/mod_files.yo \
 Zsh/mod_mapfile.yo Zsh/mod_mathfunc.yo Zsh/mod_parameter.yo Zsh/mod_sched.yo \
-Zsh/mod_stat.yo Zsh/mod_zftp.yo Zsh/mod_zle.yo Zsh/options.yo \
-Zsh/params.yo Zsh/prompt.yo Zsh/redirect.yo Zsh/restricted.yo \
+Zsh/mod_stat.yo Zsh/mod_zftp.yo Zsh/mod_zle.yo Zsh/mod_zleparameter.yo \
+Zsh/options.yo Zsh/params.yo Zsh/prompt.yo Zsh/redirect.yo Zsh/restricted.yo \
 Zsh/seealso.yo Zsh/zftpsys.yo Zsh/zle.yo
 
 # ========== DEPENDENCIES FOR BUILDING ==========
diff --git a/Doc/Zsh/compctl.yo b/Doc/Zsh/compctl.yo
index 85993e917..b55aa91be 100644
--- a/Doc/Zsh/compctl.yo
+++ b/Doc/Zsh/compctl.yo
@@ -141,7 +141,7 @@ startlist()
 list([ tt(-fcFBdeaRGovNAIOPZEnbjrzu/12) ])
 list([ tt(-k) var(array) ] [ tt(-g) var(globstring) ] \
   [ tt(-s) var(subststring) ])
-list([ tt(-K) var(function) ] [ tt(-i) var(function) ])
+list([ tt(-K) var(function) ])
 list([ tt(-Q) ] [ tt(-P) var(prefix) ] [ tt(-S) var(suffix) ])
 list([ tt(-W) var(file-prefix) ] [ tt(-H) var(num pattern) ])
 list([ tt(-q) ] [ tt(-X) var(explanation) ] [ tt(-Y) var(explanation) ])
@@ -314,13 +314,6 @@ compctl -K whoson talk)
 completes only logged-on users after `tt(talk)'.  Note that `tt(whoson)' must
 return an array, so `tt(reply=`users`)' would be incorrect.
 )
-item(tt(-i) var(function))(
-Like tt(-K), but the function is invoked in a context like that for
-completion widgets, see 
-ifzman(zmanref(zshzle))\
-ifnzman(noderef(The zle Module))
-for more information.
-)
 item(tt(-H) var(num pattern))(
 The possible completions are taken from the last var(num) history
 lines.  Only words matching var(pattern) are taken.  If var(num) is
diff --git a/Doc/Zsh/compsys.yo b/Doc/Zsh/compsys.yo
index 5e313e904..f2f551639 100644
--- a/Doc/Zsh/compsys.yo
+++ b/Doc/Zsh/compsys.yo
@@ -13,7 +13,7 @@ completion behaviour and which may be bound to keystrokes, are referred to
 as `widgets'.
 
 Note that with the function-based completions described here, it
-is also possible to use the `tt(compctl -M ...)' mechanism and the
+is also possible to use the 
 tt(compmatchers) special array to specify
 global matching control, such as case-insensitivity (`tt(abc)' will complete
 to a string beginning `tt(ABC)'), or wildcard behaviour on
@@ -21,10 +21,11 @@ certain anchors (`tt(a-d)' will complete to abc-def as if there were a
 `tt(*)' after the `a').  See
 ifzman(the section `Matching Control' in zmanref(zshcompctl))\
 ifnzman(noderef(Matching Control))
-for further details. Note that it is recommended to use the the
-tt(compmatchers) array instead of tt(compctl) to define global
-matchers when using the function based completion system, although
-using tt(compctl -M) still works.
+for further details.
+
+Also note that this completion system requires the tt(parameter) and
+tt(zleparameter) modules to be linked into the shell or to be
+dynamically loadable.
 
 startmenu()
 menu(Initialization)
@@ -595,7 +596,7 @@ item(tt(_description))(
 This function gets two arguments: the name of an array and a
 string. It tests if the configuration key tt(description_format) is
 set and if it is, it stores some options in the array that can then be 
-given to the tt(compadd) and tt(compgen) builtin commands to make the
+given to the tt(compadd) builtin command to make the
 value of the tt(description_format) key (with the sequence `tt(%d)'
 replaced by the string given as the second argument) be displayed
 above the matches added. These options also will make sure that the
@@ -704,7 +705,7 @@ completion functions is that it allows completion of partial paths. For
 example, the string `tt(/u/i/s/sig)' may be completed to
 `tt(/usr/include/sys/signal.h)'.  The options `tt(-/)', `tt(-f)', `tt(-g)',
 and `tt(-W)' are available as for the tt(compctl)
-and tt(compgen) builtins; tt(-f) is the default. Additionally, the `tt(-F)'
+and builtin command; tt(-f) is the default. Additionally, the `tt(-F)'
 option from the tt(compadd) builtin is supported, giving direct control
 over which filenames should be ignored as done by the tt(fignore)
 parameter in normal completion.
@@ -744,12 +745,11 @@ extra options of tt(compadd). All arguments are passed unchanged to
 the tt(compadd) builtin.
 )
 item(tt(_options))(
-This can be used to complete option names. The difference to the
-`tt(-o)' option of tt(compgen) is that this function uses a matching
+This can be used to complete option names. It uses a matching
 specification that ignores a leading `tt(no)', ignores underscores and 
 allows the user to type upper-case letters, making them match their
 lower-case counterparts. All arguments passed to this function are
-propagated unchanged to the tt(compgen) builtin.
+propagated unchanged to the tt(compadd) builtin.
 )
 item(tt(_set_options) and tt(_unset_options))(
 These functions complete only set or unset options, with the same
@@ -897,8 +897,8 @@ var(action) does not begin with an opening parentheses or brace, it
 will be split into separate words and executed. If the var(action)
 starts with a space, this list of words will be invoked unchanged,
 otherwise it will be invoked with some extra string placed after the
-first word which can be given as arguments to the tt(compadd) and
-tt(compgen) builtins and which make sure that the var(message) given
+first word which can be given as arguments to the tt(compadd) builtin
+command and which make sure that the var(message) given
 in the description will be shown above the matches. These arguments
 are taken from the array parameter tt(expl) which will be set up
 before executing the var(action) and hence may be used in it (normally 
@@ -1273,9 +1273,9 @@ completions.  Unambiguous parts of the function name will be completed
 automatically (normal completion is not available at this point) until a
 space is typed.
 
-Otherwise, any other string, for example `tt(-b)', will be passed as
-arguments to tt(compgen) and should hence be a set of flags specifying the
-type of completion.
+Otherwise, any other string, will be passed as
+arguments to tt(compadd) and should hence be an expression specifying
+what should be completed.
 
 A very restricted set of editing commands is available when reading the
 string:  `tt(DEL)' and `tt(^H)' delete the last character; `tt(^U)' deletes
diff --git a/Doc/Zsh/compwid.yo b/Doc/Zsh/compwid.yo
index 605a6d35a..c22a7bb14 100644
--- a/Doc/Zsh/compwid.yo
+++ b/Doc/Zsh/compwid.yo
@@ -199,7 +199,7 @@ item(tt(matcher))(
 When completion is performed with a global match specification as defined
 by
 
-indent(tt(compctl -M) var(spec1 ... specN ...))
+indent(tt(compmatchers=)tt(LPAR()) var(spec1 ... specN ...) tt(RPAR()))
 
 this gives the number of the specification string currently in use.
 In this case, matching is performed with each specification in turn.
@@ -358,32 +358,6 @@ enditem()
 texinode(Builtin Commands)(Condition Codes)(Special Parameters)(Completion Widgets)
 sect(Builtin Commands)
 startitem()
-cindex(completion widgets, generating matches with flags)
-findex(compgen)
-item(tt(compgen) var(flags ...))(
-
-Generate matches according to the given var(flags).  These can be any of 
-the normal option flags (not those for extended completion) supported by
-the tt(compctl) builtin command (see
-ifzman(zmanref(zshcompctl))\
-ifnzman(noderef(Programmable Completion Using compctl))\
-) except for the tt(-t) and tt(-l) flags.  However, when using the tt(-K)
-flag, the function given as argument to it cannot access the command
-line with the tt(read) builtin command.
-
-The matches will be generated in the same way as if the completion code
-generated them directly from a tt(compctl)-definition with the same
-flags. The completion code will consider only those matches as
-possible completions that match the prefix and suffix from the special 
-parameters described above. These strings will be compared with the
-generated matches using the normal matching rules and any matching
-specifications given with the tt(-M) flag to tt(compgen) and the
-global matching specifications given via the tt(compctl -M )var(spec1 ...)
-builtin command.
-
-The return value is zero if at least one match was added and non-zero
-otherwise.
-)
 findex(compadd)
 cindex(completion widgets, adding specified matches)
 xitem(tt(compadd) [ tt(-qQfenUaml12) ] [ tt(-F) var(array) ])
@@ -425,8 +399,7 @@ The supported flags are:
 
 startitem()
 item(tt(-P) var(prefix))(
-As for tt(compctl) and tt(compgen), it gives a string to
-be inserted before the given var(words).  The
+This gives a string to be inserted before the given var(words).  The
 string given is not considered as part of the match.
 )
 item(tt(-S) var(suffix))(
@@ -450,12 +423,6 @@ match.
 item(tt(-I) var(ignored-suffix))(
 Like tt(-i), but gives an ignored suffix.
 )
-item(tt(-y) var(array))(
-This gives a number of strings to display instead of the matches. This
-is like the tt(-y) option of the tt(compctl) builtin command but the
-var(array) argument may only be the name of an array parameter or a
-literal array in parentheses containing the strings to display.
-)
 item(tt(-d) var(array))(
 This adds per-match display strings. The var(array) should contain one 
 element per var(word) given. The completion code will then display the 
@@ -474,8 +441,7 @@ options. If it is given, the display strings are listed one per line,
 not arrayed in columns.
 )
 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.
+Gives the name of the group of matches the words should be stored in.
 )
 item(tt(-V) var(name))(
 Like tt(-J) but naming a unsorted group.
@@ -491,12 +457,10 @@ duplicates be kept. Again, groups with and without this flag are in
 different name spaces.
 )
 item(tt(-X) var(explanation))(
-As for tt(compctl) and tt(compgen), the var(explanation) string will be
-printed with the list of matches.
+The var(explanation) string will be printed with the list of matches.
 )
 item(tt(-q))(
-As for tt(compctl) and tt(compgen),
-the suffix given with tt(-S) will be automatically removed if 
+The suffix given with tt(-S) will be automatically removed if 
 the next character typed is a blank or does not insert anything, or if
 the suffix consists of only one character and the next character typed 
 is the same character.
@@ -540,17 +504,15 @@ the tt(AUTO_PARAM_SLASH) and tt(AUTO_PARAM_KEYS) options be used for
 the matches.
 )
 item(tt(-W) var(file-prefix))(
-This option has the same meaning as for the tt(compctl) and
-tt(compgen) builtin commands. Here, however, only one string may be
-given, not an array. This string is a pathname that will be
+This string is a pathname that will be
 prepended to each of the matches formed by the given var(words) together 
 with any prefix specified by the tt(-p) option to form a complete filename
 for testing.  Hence it is only useful if combined with the tt(-f) flag, as
 the tests will not otherwise be performed.
 )
 item(tt(-a))(
-In the tt(compctl) or tt(compgen) commands, the completion code normally
-builds two sets of matches: the normal one where words with one of the 
+The completion code may
+build two sets of matches: the normal one where words with one of the 
 suffixes in the array parameter tt(fignore) are not considered
 possible matches, and the alternate set where the words excluded
 from the first set are stored. Normally only the matches in the first
@@ -574,12 +536,12 @@ literal suffixes enclosed in parentheses and quoted, as in `tt(-F "(.o
 taken as the suffixes.
 )
 item(tt(-Q))(
-As for tt(compctl) and tt(compgen), this flag instructs the completion 
+This flag instructs the completion 
 code not to quote any metacharacters in the words when inserting them
 into the command line.
 )
 item(tt(-M) var(match-spec))(
-As for tt(compctl) and tt(compgen), this gives local match specifications.
+This gives local match specifications.
 Note that they will only be used if the tt(-U) option is not given.
 )
 item(tt(-n))(
@@ -593,7 +555,7 @@ functions that do the matching themselves.
 
 Note that with tt(compadd) this option does not automatically turn on
 menu completion if tt(AUTO_LIST) is set, unlike the corresponding option of
-tt(compctl) and tt(compgen) commands.
+tt(compctl).
 )
 item(tt(-O) var(array))(
 If this option is given, the var(words) are em(not) added to the set of
@@ -714,7 +676,6 @@ This forces anything up to and including the last equal sign to be
 ignored by the completion code.
 )
 item(tt(compcall) [ tt(-TD) ])(
-
 This allows the use of completions defined with the tt(compctl) builtin
 from within completion widgets.  The list of matches will be generated as
 if one of the non-widget completion function (tt(complete-word), etc.)
@@ -727,6 +688,8 @@ tt(-T) and/or tt(-D) flags can be passed to tt(compcall).
 The return value can be used to test if a matching tt(compctl)
 definition was found. It is non-zero if a tt(compctl) was found and
 zero otherwise.
+
+Note that this builtin is defined by the tt(compctl) module.
 )
 enditem()
 
@@ -773,7 +736,7 @@ After that the shell function tt(complete-history) will be invoked
 after typing control-X and TAB. The function should then generate the
 matches, e.g.:
 
-example(complete-history LPAR()RPAR() { compgen -H 0 '' })
+example(complete-history LPAR()RPAR() { compadd - $historywords })
 
 This function will complete words from the history matching the 
 current word.
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index 6b33c9a81..2220b8e45 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -706,6 +706,12 @@ for exported parameters
 item(tt(unique))(
 for arrays which keep only the first occurrence of duplicated values
 )
+item(tt(hide))(
+for parameters with the `hide' flag
+)
+item(tt(special))(
+for special parameters defined by the shell
+)
 enditem()
 )
 enditem()
diff --git a/Doc/Zsh/manual.yo b/Doc/Zsh/manual.yo
index c4691b89d..16a1bdec6 100644
--- a/Doc/Zsh/manual.yo
+++ b/Doc/Zsh/manual.yo
@@ -117,7 +117,7 @@ Zsh Modules
 
 menu(The cap Module)
 menu(The clone Module)
-menu(The comp1 Module)
+menu(The complete Module)
 menu(The compctl Module)
 menu(The complist Module)
 menu(The deltochar Module)
@@ -130,6 +130,7 @@ menu(The sched Module)
 menu(The stat Module)
 menu(The zftp Module)
 menu(The zle Module)
+menu(The zleparameter Module)
 endmenu()
 texinode(The Z Shell Manual)(Introduction)(Top)(Top)
 chapter(The Z Shell Manual)
diff --git a/Doc/Zsh/mod_clone.yo b/Doc/Zsh/mod_clone.yo
index 9bb7dc255..3914fffce 100644
--- a/Doc/Zsh/mod_clone.yo
+++ b/Doc/Zsh/mod_clone.yo
@@ -1,4 +1,4 @@
-texinode(The clone Module)(The comp1 Module)(The cap Module)(Zsh Modules)
+texinode(The clone Module)(The complete Module)(The cap Module)(Zsh Modules)
 sect(The clone Module)
 The tt(clone) module makes available one builtin command:
 
diff --git a/Doc/Zsh/mod_compctl.yo b/Doc/Zsh/mod_compctl.yo
index 785767e45..b69ae1e7d 100644
--- a/Doc/Zsh/mod_compctl.yo
+++ b/Doc/Zsh/mod_compctl.yo
@@ -1,12 +1,12 @@
-texinode(The compctl Module)(The complist Module)(The comp1 Module)(Zsh Modules)
+texinode(The compctl Module)(The complist Module)(The complete Module)(Zsh Modules)
 sect(The compctl Module)
-The tt(compctl) module makes available several builtin commands. tt(compctl),
-is the standard way to control completions for ZLE.  See
+The tt(compctl) module makes available two builtin commands. tt(compctl),
+is the old, deprecated way to control completions for ZLE.  See
 ifzman(zmanref(zshcompctl))\
 ifnzman(noderef(Programmable Completion Using compctl))\
 .
-The other builtin commands can be used in user-defined completion widgets,
-see
+The other builtin command, tt(compcall) can be used in user-defined
+completion widgets, see
 ifzman(zmanref(zshcompwid))\
 ifnzman(noderef(Completion Widgets))\
 .
diff --git a/Doc/Zsh/mod_complete.yo b/Doc/Zsh/mod_complete.yo
new file mode 100644
index 000000000..558bbc4ae
--- /dev/null
+++ b/Doc/Zsh/mod_complete.yo
@@ -0,0 +1,7 @@
+texinode(The complete Module)(The compctl Module)(The clone Module)(Zsh Modules)
+sect(The complete Module)
+The tt(compctl) module makes available several builtin commands which
+can be used in user-defined completion widgets, see
+ifzman(zmanref(zshcompwid))\
+ifnzman(noderef(Completion Widgets))\
+.
diff --git a/Doc/Zsh/mod_computil.yo b/Doc/Zsh/mod_computil.yo
index fc1959bf1..cc0abc38e 100644
--- a/Doc/Zsh/mod_computil.yo
+++ b/Doc/Zsh/mod_computil.yo
@@ -12,7 +12,7 @@ completion functions. In short, these builtin commands are:
 
 startitem()
 item(tt(compdisplay) var(name) var(string) var(defs) ...)(
-The var(defs) are strings should be of the form
+The var(defs) are strings which should be of the form
 `var(str)tt(:)var(descr)' (the intended use is that the var(descr)
 describes the var(str)) and tt(compdisplay) will convert them to
 strings in which the colon is replaced by the var(string) given as the 
diff --git a/Doc/Zsh/mod_parameter.yo b/Doc/Zsh/mod_parameter.yo
index 893bd03d9..0cf44d4b1 100644
--- a/Doc/Zsh/mod_parameter.yo
+++ b/Doc/Zsh/mod_parameter.yo
@@ -2,7 +2,7 @@ texinode(The parameter Module)(The sched Module)(The mathfunc Module)(Zsh Module
 sect(The parameter Module)
 cindex(parameters, special)
 The tt(parameter) module gives access to some of the internal hash
-tables used by the shell, by defining four special associative arrays.
+tables used by the shell by defining some special parameters.
 
 startitem()
 vindex(options)
@@ -32,6 +32,31 @@ key in it is like defining a function with the name given by the key
 and the body given by the value. Unsetting a key removes the
 definition for the function named by the key.
 )
+vindex(builtins)
+item(tt(builtins))(
+This association gives information about the builtin commands
+currently known. The keys are the names of the builtin commands and
+the values are either `tt(undefined)' for builtin commands that will
+automatically be loaded from a module if invoked or `tt(defined)' for
+builtin commands that are already loaded. Also, the string
+`tt(<disabled> )' will be prepended to the value if the builtin
+command is currently disabled.
+)
+vindex(reswords)
+item(tt(reswords))(
+This association maps the reserved words to one of `tt(<enabled>)' or
+`tt(<disabled>)' for enabled and disabled reserved words, respectively.
+)
+vindex(raliases)
+item(tt(raliases))(
+This maps the names of the regular aliases currently defined to their
+expansions. For disabled aliases the string `tt(<disabled>)' is
+prepended to their value.
+)
+vindex(galiases)
+item(tt(galiases))(
+Like tt(raliases), but for global aliases.
+)
 vindex(parameters)
 item(tt(parameters))(
 The keys in this associative array are the names of the parameters
@@ -61,4 +86,39 @@ A normal array holding the elements of the directory stack. Note that
 the output of the tt(dirs) builtin command includes one more
 directory, the current working directory.
 )
+vindex(history)
+item(tt(history))(
+This association maps history event numbers to the full history lines.
+)
+vindex(historywords)
+item(tt(historywords))(
+A special array containing the words stored in the history.
+)
+vindex(jobtexts)
+item(tt(jobtexts))(
+This association maps job numbers to the texts of the command lines
+that were used to start the jobs.
+)
+vindex(jobstates)
+item(tt(jobstates))(
+This association gives information about the states of the jobs
+currently known. The keys are the job numbers and the values are
+strings of the form
+`var(job-state):var(pid)tt(=)var(state)tt(...)'. The var(job-state)
+gives the state the whole job is currently in, one of `tt(running)',
+`tt(suspended)', or `tt(done)'. This is follwed by one
+`var(pid)tt(=)var(state)' for every process in the job. The var(pid)s
+are, of course, the process IDs and the var(state) describes the state 
+of that process.
+)
+vindex(nameddirs)
+item(tt(nameddirs))(
+This association maps the names of named directories to the pathnames
+they stand for.
+)
+vindex(userdirs)
+item(tt(userdirs))(
+This association maps user names to the pathnames of their home
+directories.
+)
 enditem()
diff --git a/Doc/Zsh/mod_zle.yo b/Doc/Zsh/mod_zle.yo
index 95aa27535..a346ab279 100644
--- a/Doc/Zsh/mod_zle.yo
+++ b/Doc/Zsh/mod_zle.yo
@@ -1,4 +1,4 @@
-texinode(The zle Module)()(The zftp Module)(Zsh Modules)
+texinode(The zle Module)(The zleparameter Module)(The zftp Module)(Zsh Modules)
 sect(The zle Module)
 The tt(zle) module contains the Zsh Line Editor.  See
 ifzman(zmanref(zshzle))\
diff --git a/Doc/Zsh/mod_zleparameter.yo b/Doc/Zsh/mod_zleparameter.yo
new file mode 100644
index 000000000..cf79668e4
--- /dev/null
+++ b/Doc/Zsh/mod_zleparameter.yo
@@ -0,0 +1,28 @@
+texinode(The zleparameter Module)()(The zle Module)(Zsh Modules)
+sect(The zleparameter Module)
+cindex(parameters, special)
+The tt(zleparameter) module defines two special parameters that can be 
+used to access internal information of the Zsh Line Editor (see
+ifzman(zmanref(zshzle))\
+ifnzman(noderef(Zsh Line Editor))\
+).
+
+startitem()
+vindex(zlekeymaps)
+item(tt(zlekeymaps))(
+This array contains the names of the keymaps currently defined.
+)
+vindex(zlewidgets)
+item(tt(zlewidgets))(
+This associative array contains one entry per widget defined. The name 
+of the widget is the key and the value gives information about the
+widget. It is either the string `tt(builtin)' for builtin widgets, a
+string of the form `tt(user:)var(name)' for user-defined widgets,
+where var(name) is the name of the shell function implementing the
+widget, or it is a string of the form
+`tt(completion:)var(type)tt(:)var(name)', for completion widgets. In
+the last case var(type) is the name of the builtin widgets the
+completion widget imitates in its behavior and var(name) is the name
+of the shell function implementing the completion widget.
+)
+enditem()
diff --git a/Doc/Zsh/modules.yo b/Doc/Zsh/modules.yo
index b06edfda2..9ce81c264 100644
--- a/Doc/Zsh/modules.yo
+++ b/Doc/Zsh/modules.yo
@@ -15,9 +15,6 @@ Builtins for manipulating POSIX.1e (POSIX.6) capability (privilege) sets.
 item(tt(clone))(
 A builtin that can clone a running shell onto another terminal.
 )
-item(tt(comp1))(
-Base of the completion system.  Used by the tt(compctl) and tt(zle) modules.
-)
 item(tt(compctl))(
 The tt(compctl) builtin for controlling completion and the builtins for
 completion widgets.
@@ -59,11 +56,14 @@ A builtin FTP client.
 item(tt(zle))(
 The Zsh Line Editor, including the tt(bindkey) and tt(vared) builtins.
 )
+item(tt(zleparameter))(
+Access to internals of the Zsh Line Editor via parameters.
+)
 enditem()
 startmenu()
 menu(The cap Module)
 menu(The clone Module)
-menu(The comp1 Module)
+menu(The complete Module)
 menu(The compctl Module)
 menu(The complist Module)
 menu(The computil Module)
@@ -77,10 +77,11 @@ menu(The sched Module)
 menu(The stat Module)
 menu(The zftp Module)
 menu(The zle Module)
+menu(The zleparameter Module)
 endmenu()
 includefile(Zsh/mod_cap.yo)
 includefile(Zsh/mod_clone.yo)
-includefile(Zsh/mod_comp1.yo)
+includefile(Zsh/mod_complete.yo)
 includefile(Zsh/mod_compctl.yo)
 includefile(Zsh/mod_complist.yo)
 includefile(Zsh/mod_computil.yo)
@@ -94,3 +95,4 @@ includefile(Zsh/mod_sched.yo)
 includefile(Zsh/mod_stat.yo)
 includefile(Zsh/mod_zftp.yo)
 includefile(Zsh/mod_zle.yo)
+includefile(Zsh/mod_zleparameter.yo)
diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c
index 91e993c73..edad0e468 100644
--- a/Src/Modules/parameter.c
+++ b/Src/Modules/parameter.c
@@ -30,6 +30,10 @@
 #include "parameter.mdh"
 #include "parameter.pro"
 
+/* This says if we are cleaning up when the module is unloaded. */
+
+static int incleanup;
+
 /* Empty dummy function for special hash parameters. */
 
 /**/
@@ -116,6 +120,10 @@ paramtypestr(Param pm)
 	    val = dyncat(val, "-export");
 	if (f & PM_UNIQUE)
 	    val = dyncat(val, "-unique");
+	if (f & PM_HIDE)
+	    val = dyncat(val, "-hide");
+	if (f & PM_SPECIAL)
+	    val = dyncat(val, "-special");
     } else
 	val = dupstring("");
 
@@ -144,7 +152,7 @@ getpmparameter(HashTable ht, char *name)
 	    !(rpm->flags & PM_UNSET))
 	    pm->u.str = paramtypestr(rpm);
 	else {
-	    pm->u.str = "";
+	    pm->u.str = dupstring("");
 	    pm->flags |= PM_UNSET;
 	}
     } LASTALLOC;
@@ -186,7 +194,7 @@ static void
 setpmcommand(Param pm, char *value)
 {
     if (isset(RESTRICTED))
-	zwarnnam(NULL, "restricted: %s", value, 0);
+	zwarn("restricted: %s", value, 0);
     else {
 	Cmdnam cn = zcalloc(sizeof(*cn));
 
@@ -270,7 +278,7 @@ getpmcommand(HashTable ht, char *name)
 		strcat(pm->u.str, name);
 	    }
 	} else {
-	    pm->u.str = "";
+	    pm->u.str = dupstring("");
 	    pm->flags |= PM_UNSET;
 	}
     } LASTALLOC;
@@ -328,8 +336,12 @@ setfunction(char *name, char *val)
     char *value = dupstring(val);
     Shfunc shf;
     List list;
-    int sn;
+    int sn, dis = 0;
 
+    if (strpfx("<disabled> ", val)) {
+	strcpy(val, val + 11);
+	dis = DISABLED;
+    }
     val = metafy(val, strlen(val), META_REALLOC);
 
     HEAPALLOC {
@@ -337,14 +349,14 @@ setfunction(char *name, char *val)
     } LASTALLOC;
 
     if (!list || list == &dummy_list) {
-	zwarnnam(NULL, "invalid function definition", value, 0);
+	zwarn("invalid function definition", value, 0);
 	zsfree(val);
 	return;
     }
     PERMALLOC {
 	shf = (Shfunc) zalloc(sizeof(*shf));
 	shf->funcdef = (List) dupstruct(list);
-	shf->flags = 0;
+	shf->flags = dis;
 
 	if (!strncmp(name, "TRAP", 4) &&
 	    (sn = getsignum(name + 4)) != -1) {
@@ -422,23 +434,25 @@ getpmfunction(HashTable ht, char *name)
 	pm->old = NULL;
 	pm->level = 0;
 
-	if ((shf = (Shfunc) shfunctab->getnode(shfunctab, name))) {
-	    if (shf->flags & PM_UNDEFINED)
-		pm->u.str = tricat("builtin autoload -X",
-				   ((shf->flags & PM_UNALIASED)? "U" : ""),
-				   ((shf->flags & PM_TAGGED)? "t" : ""));
-	    else {
+	if ((shf = (Shfunc) shfunctab->getnode2(shfunctab, name))) {
+	    if (shf->flags & PM_UNDEFINED) {
+		pm->u.str = dyncat("builtin autoload -X",
+				   ((shf->flags & PM_UNALIASED) ?
+				    ((shf->flags & PM_TAGGED) ? "Ut" : "U") :
+				    ((shf->flags & PM_TAGGED) ? "t" : "")));
+	    } else {
 		char *t = getpermtext((void *) dupstruct((void *)
 							 shf->funcdef)), *h;
 
-		h = dupstring(t);
+		h = ((shf->flags & DISABLED) ?
+		     dyncat("<disabled> ", t) : dupstring(t));
 		zsfree(t);
 		unmetafy(h, NULL);
 
 		pm->u.str = h;
 	    }
 	} else {
-	    pm->u.str = "";
+	    pm->u.str = dupstring("");
 	    pm->flags |= PM_UNSET;
 	}
     } LASTALLOC;
@@ -471,14 +485,19 @@ scanpmfunctions(HashTable ht, ScanFunc func, int flags)
 		if (func != scancountparams) {
 		    if (((Shfunc) hn)->flags & PM_UNDEFINED) {
 			Shfunc shf = (Shfunc) hn;
-			pm.u.str = tricat("builtin autoload -X",
-					  ((shf->flags & PM_UNALIASED)? "U" : ""),
-					  ((shf->flags & PM_TAGGED)? "t" : ""));
+			pm.u.str =
+			    dyncat("builtin autoload -X",
+				   ((shf->flags & PM_UNALIASED) ?
+				    ((shf->flags & PM_TAGGED) ? "Ut" : "U") :
+				    ((shf->flags & PM_TAGGED) ? "t" : "")));
 		    } else {
 			char *t = getpermtext((void *)
 					      dupstruct((void *) ((Shfunc) hn)->funcdef));
 
-			unmetafy((pm.u.str = dupstring(t)), NULL);
+			pm.u.str = ((hn->flags & DISABLED) ?
+				    dyncat("<disabled> ", t) :
+				    dupstring(t));
+			unmetafy(pm.u.str, NULL);
 			zsfree(t);
 		    }
 		}
@@ -487,6 +506,136 @@ scanpmfunctions(HashTable ht, ScanFunc func, int flags)
 	}
 }
 
+/* Functions for the builtins special parameter. */
+
+/**/
+static HashNode
+getpmbuiltin(HashTable ht, char *name)
+{
+    Param pm = NULL;
+    Builtin bn;
+
+    HEAPALLOC {
+	pm = (Param) zhalloc(sizeof(struct param));
+	pm->nam = dupstring(name);
+	pm->flags = PM_SCALAR | PM_READONLY;
+	pm->sets.cfn = NULL;
+	pm->gets.cfn = strgetfn;
+	pm->unsetfn = NULL;
+	pm->ct = 0;
+	pm->env = NULL;
+	pm->ename = NULL;
+	pm->old = NULL;
+	pm->level = 0;
+	if ((bn = (Builtin) builtintab->getnode2(builtintab, name))) {
+	    char *t = ((bn->handlerfunc || (bn->flags & BINF_PREFIX)) ?
+		       "defined" : "undefined");
+
+	    pm->u.str = ((bn->flags & DISABLED) ?
+			 dyncat("<disabled> ", t) : dupstring(t));
+	} else {
+	    pm->u.str = dupstring("");
+	    pm->flags |= PM_UNSET;
+	}
+    } LASTALLOC;
+
+    return (HashNode) pm;
+}
+
+/**/
+static void
+scanpmbuiltins(HashTable ht, ScanFunc func, int flags)
+{
+    struct param pm;
+    int i;
+    HashNode hn;
+
+    pm.flags = PM_SCALAR | PM_READONLY;
+    pm.sets.cfn = NULL;
+    pm.gets.cfn = strgetfn;
+    pm.unsetfn = NULL;
+    pm.ct = 0;
+    pm.env = NULL;
+    pm.ename = NULL;
+    pm.old = NULL;
+    pm.level = 0;
+
+    for (i = 0; i < builtintab->hsize; i++)
+	for (hn = builtintab->nodes[i]; hn; hn = hn->next) {
+	    pm.nam = hn->nam;
+	    if (func != scancountparams) {
+		char *t = ((((Builtin) hn)->handlerfunc ||
+			    (hn->flags & BINF_PREFIX)) ?
+			   "defined" : "undefined");
+
+		pm.u.str = ((((Builtin) hn)->flags & DISABLED) ?
+			    dyncat("<disabled> ", t) : dupstring(t));
+	    }
+	    func((HashNode) &pm, flags);
+	}
+}
+
+/* Functions for the reswords special parameter. */
+
+/**/
+static HashNode
+getpmresword(HashTable ht, char *name)
+{
+    Param pm = NULL;
+    HashNode hn;
+
+    HEAPALLOC {
+	pm = (Param) zhalloc(sizeof(struct param));
+	pm->nam = dupstring(name);
+	pm->flags = PM_SCALAR | PM_READONLY;
+	pm->sets.cfn = NULL;
+	pm->gets.cfn = strgetfn;
+	pm->unsetfn = NULL;
+	pm->ct = 0;
+	pm->env = NULL;
+	pm->ename = NULL;
+	pm->old = NULL;
+	pm->level = 0;
+	if ((hn = reswdtab->getnode2(reswdtab, name)))
+	    pm->u.str = dupstring((hn->flags & DISABLED) ?
+				  "<disabled>" : "<enabled>");
+	else {
+	    pm->u.str = dupstring("");
+	    pm->flags |= PM_UNSET;
+	}
+    } LASTALLOC;
+
+    return (HashNode) pm;
+}
+
+/**/
+static void
+scanpmreswords(HashTable ht, ScanFunc func, int flags)
+{
+    struct param pm;
+    int i;
+    HashNode hn;
+
+    pm.flags = PM_SCALAR | PM_READONLY;
+    pm.sets.cfn = NULL;
+    pm.gets.cfn = strgetfn;
+    pm.unsetfn = NULL;
+    pm.ct = 0;
+    pm.env = NULL;
+    pm.ename = NULL;
+    pm.old = NULL;
+    pm.level = 0;
+
+    for (i = 0; i < reswdtab->hsize; i++)
+	for (hn = reswdtab->nodes[i]; hn; hn = hn->next) {
+	    pm.nam = hn->nam;
+	    if (func != scancountparams)
+		pm.u.str = dupstring((hn->flags & DISABLED) ?
+				     "<disabled>" : "<enabled>");
+	    func((HashNode) &pm, flags);
+	}
+}
+
 /* Functions for the options special parameter. */
 
 /**/
@@ -496,11 +645,11 @@ setpmoption(Param pm, char *value)
     int n;
 
     if (!value || (strcmp(value, "on") && strcmp(value, "off")))
-	zwarnnam(NULL, "invalid value: %s", value, 0);
+	zwarn("invalid value: %s", value, 0);
     else if (!(n = optlookup(pm->nam)))
-	zwarnnam(NULL, "no such option: %s", pm->nam, 0);
+	zwarn("no such option: %s", pm->nam, 0);
     else if (dosetopt(n, (value && strcmp(value, "off")), 0))
-	zwarnnam(NULL, "can't change option: %s", pm->nam, 0);
+	zwarn("can't change option: %s", pm->nam, 0);
     zsfree(value);
 }
 
@@ -511,9 +660,9 @@ unsetpmoption(Param pm, int exp)
     int n;
 
     if (!(n = optlookup(pm->nam)))
-	zwarnnam(NULL, "no such option: %s", pm->nam, 0);
+	zwarn("no such option: %s", pm->nam, 0);
     else if (dosetopt(n, 0, 0))
-	zwarnnam(NULL, "can't change option: %s", pm->nam, 0);
+	zwarn("can't change option: %s", pm->nam, 0);
 }
 
 /**/
@@ -538,10 +687,10 @@ setpmoptions(Param pm, HashTable ht)
 
 	    val = getstrvalue(&v);
 	    if (!val || (strcmp(val, "on") && strcmp(val, "off")))
-		zwarnnam(NULL, "invalid value: %s", val, 0);
+		zwarn("invalid value: %s", val, 0);
 	    else if (dosetopt(optlookup(hn->nam),
 			      (val && strcmp(val, "off")), 0))
-		zwarnnam(NULL, "can't change option: %s", hn->nam, 0);
+		zwarn("can't change option: %s", hn->nam, 0);
 	}
     deleteparamtable(ht);
 }
@@ -569,7 +718,7 @@ getpmoption(HashTable ht, char *name)
 	if ((n = optlookup(name)))
 	    pm->u.str = dupstring(opts[n] ? "on" : "off");
 	else {
-	    pm->u.str = "";
+	    pm->u.str = dupstring("");
 	    pm->flags |= PM_UNSET;
 	}
     } LASTALLOC;
@@ -598,7 +747,7 @@ scanpmoptions(HashTable ht, ScanFunc func, int flags)
     for (i = 0; i < optiontab->hsize; i++)
 	for (hn = optiontab->nodes[i]; hn; hn = hn->next) {
 	    pm.nam = hn->nam;
-	    pm.u.str = opts[((Optname) hn)->optno] ? "on" : "off";
+	    pm.u.str = dupstring(opts[((Optname) hn)->optno] ? "on" : "off");
 	    func((HashNode) &pm, flags);
 	}
 }
@@ -698,9 +847,9 @@ getpmmodule(HashTable ht, char *name)
 	}
 #endif
 	if (type)
-	    pm->u.str = type;
+	    pm->u.str = dupstring(type);
 	else {
-	    pm->u.str = "";
+	    pm->u.str = dupstring("");
 	    pm->flags |= PM_UNSET;
 	}
     } LASTALLOC;
@@ -730,29 +879,29 @@ scanpmmodules(HashTable ht, ScanFunc func, int flags)
     pm.old = NULL;
     pm.level = 0;
 
+    pm.u.str = dupstring("builtin");
     for (node = firstnode(bltinmodules); node; incnode(node)) {
 	pm.nam = (char *) getdata(node);
 	addlinknode(done, pm.nam);
-	pm.u.str = "builtin";
 	func((HashNode) &pm, flags);
     }
 #ifdef DYNAMIC
+    pm.u.str = dupstring("loaded");
     for (node = firstnode(modules); node; incnode(node)) {
 	m = (Module) getdata(node);
 	if (m->handle && !(m->flags & MOD_UNLOAD)) {
 	    pm.nam = m->nam;
 	    addlinknode(done, pm.nam);
-	    pm.u.str = "loaded";
 	    func((HashNode) &pm, flags);
 	}
     }
+    pm.u.str = dupstring("autoloaded");
     for (i = 0; i < builtintab->hsize; i++)
 	for (hn = builtintab->nodes[i]; hn; hn = hn->next) {
 	    if (!(((Builtin) hn)->flags & BINF_ADDED) &&
 		!findmodnode(done, ((Builtin) hn)->optstr)) {
 		pm.nam = ((Builtin) hn)->optstr;
 		addlinknode(done, pm.nam);
-		pm.u.str = "autoloaded";
 		func((HashNode) &pm, flags);
 	    }
 	}
@@ -760,7 +909,6 @@ scanpmmodules(HashTable ht, ScanFunc func, int flags)
 	if (p->module && !findmodnode(done, p->module)) {
 	    pm.nam = p->module;
 	    addlinknode(done, pm.nam);
-	    pm.u.str = "autoloaded";
 	    func((HashNode) &pm, flags);
 	}
     for (i = 0; i < realparamtab->hsize; i++)
@@ -769,7 +917,6 @@ scanpmmodules(HashTable ht, ScanFunc func, int flags)
 		!findmodnode(done, ((Param) hn)->u.str)) {
 		pm.nam = ((Param) hn)->u.str;
 		addlinknode(done, pm.nam);
-		pm.u.str = "autoloaded";
 		func((HashNode) &pm, flags);
 	    }
 	}
@@ -781,12 +928,16 @@ scanpmmodules(HashTable ht, ScanFunc func, int flags)
 static void
 dirssetfn(Param pm, char **x)
 {
-    PERMALLOC {
-	freelinklist(dirstack, freestr);
-	dirstack = newlinklist();
-	while (x && *x)
-	    addlinknode(dirstack, ztrdup(*x++));
-    } LASTALLOC;
+    if (!incleanup) {
+	PERMALLOC {
+	    freelinklist(dirstack, freestr);
+	    dirstack = newlinklist();
+	    while (x && *x)
+		addlinknode(dirstack, ztrdup(*x++));
+	} LASTALLOC;
+    }
+    if (x)
+	freearray(x);
 }
 
 static char **
@@ -803,21 +954,742 @@ dirsgetfn(Param pm)
     return ret;
 }
 
-/* Names and Params for the special parameters. */
+/* Functions for the history special parameter. */
+
+/**/
+static HashNode
+getpmhistory(HashTable ht, char *name)
+{
+    Param pm = NULL;
+    Histent he;
+
+    HEAPALLOC {
+	pm = (Param) zhalloc(sizeof(struct param));
+	pm->nam = dupstring(name);
+	pm->flags = PM_SCALAR | PM_READONLY;
+	pm->sets.cfn = NULL;
+	pm->gets.cfn = strgetfn;
+	pm->unsetfn = NULL;
+	pm->ct = 0;
+	pm->env = NULL;
+	pm->ename = NULL;
+	pm->old = NULL;
+	pm->level = 0;
+	if ((he = quietgethist(atoi(name))))
+	    pm->u.str = dupstring(he->text);
+	else {
+	    pm->u.str = dupstring("");
+	    pm->flags |= PM_UNSET;
+	}
+    } LASTALLOC;
+
+    return (HashNode) pm;
+}
+
+/**/
+static void
+scanpmhistory(HashTable ht, ScanFunc func, int flags)
+{
+    struct param pm;
+    int i = addhistnum(curhist, -1, HIST_FOREIGN);
+    Histent he = quietgethistent(i, GETHIST_UPWARD);
+    char buf[40];
+
+    pm.flags = PM_SCALAR | PM_READONLY;
+    pm.sets.cfn = NULL;
+    pm.gets.cfn = strgetfn;
+    pm.unsetfn = NULL;
+    pm.ct = 0;
+    pm.env = NULL;
+    pm.ename = NULL;
+    pm.old = NULL;
+    pm.level = 0;
+
+    while (he) {
+	if (func != scancountparams) {
+	    sprintf(buf, "%d", he->histnum);
+	    pm.nam = dupstring(buf);
+	    pm.u.str = dupstring(he->text);
+	}
+	func((HashNode) &pm, flags);
+
+	he = up_histent(he);
+    }
+}
+
+/* Function for the historywords special parameter. */
+
+static char **
+histwgetfn(Param pm)
+{
+    char **ret, **p, *h, *e, sav;
+    LinkList l = newlinklist();
+    LinkNode n;
+    int i = addhistnum(curhist, -1, HIST_FOREIGN), iw;
+    Histent he = quietgethistent(i, GETHIST_UPWARD);
+
+    while (he) {
+	for (iw = he->nwords - 1; iw >= 0; iw--) {
+	    h = he->text + he->words[iw * 2];
+	    e = he->text + he->words[iw * 2 + 1];
+	    sav = *e;
+	    *e = '\0';
+	    addlinknode(l, dupstring(h));
+	    *e = sav;
+	}
+	he = up_histent(he);
+    }
+    ret = (char **) zhalloc((countlinknodes(l) + 1) * sizeof(char *));
+
+    for (p = ret, n = firstnode(l); n; incnode(n), p++)
+	*p = (char *) getdata(n);
+    *p = NULL;
+
+    return ret;
+}
+
+/* Functions for the jobtexts special parameter. */
+
+static char *
+pmjobtext(int job)
+{
+    Process pn;
+    int len = 1;
+    char *ret;
+
+    for (pn = jobtab[job].procs; pn; pn = pn->next)
+	len += strlen(pn->text) + 3;
+
+    ret = (char *) zhalloc(len);
+    ret[0] = '\0';
+
+    for (pn = jobtab[job].procs; pn; pn = pn->next) {
+	strcat(ret, pn->text);
+	if (pn->next)
+	    strcat(ret, " | ");
+    }
+    return ret;
+}
+
+/**/
+static HashNode
+getpmjobtext(HashTable ht, char *name)
+{
+    Param pm = NULL;
+    int job;
+
+    HEAPALLOC {
+	pm = (Param) zhalloc(sizeof(struct param));
+	pm->nam = dupstring(name);
+	pm->flags = PM_SCALAR | PM_READONLY;
+	pm->sets.cfn = NULL;
+	pm->gets.cfn = strgetfn;
+	pm->unsetfn = NULL;
+	pm->ct = 0;
+	pm->env = NULL;
+	pm->ename = NULL;
+	pm->old = NULL;
+	pm->level = 0;
+
+	if ((job = atoi(name)) >= 1 && job < MAXJOB &&
+	    jobtab[job].stat && jobtab[job].procs &&
+	    !(jobtab[job].stat & STAT_NOPRINT))
+	    pm->u.str = pmjobtext(job);
+	else {
+	    pm->u.str = dupstring("");
+	    pm->flags |= PM_UNSET;
+	}
+    } LASTALLOC;
+
+    return (HashNode) pm;
+}
+
+/**/
+static void
+scanpmjobtexts(HashTable ht, ScanFunc func, int flags)
+{
+    struct param pm;
+    int job;
+    char buf[40];
+
+    pm.flags = PM_SCALAR | PM_READONLY;
+    pm.sets.cfn = NULL;
+    pm.gets.cfn = strgetfn;
+    pm.unsetfn = NULL;
+    pm.ct = 0;
+    pm.env = NULL;
+    pm.ename = NULL;
+    pm.old = NULL;
+    pm.level = 0;
+
+    for (job = 1; job < MAXJOB; job++) {
+	if (jobtab[job].stat && jobtab[job].procs &&
+	    !(jobtab[job].stat & STAT_NOPRINT)) {
+	    if (func != scancountparams) {
+		sprintf(buf, "%d", job);
+		pm.nam = dupstring(buf);
+		pm.u.str = pmjobtext(job);
+	    }
+	    func((HashNode) &pm, flags);
+	}
+    }
+}
+
+/* Functions for the jobstates special parameter. */
+
+static char *
+pmjobstate(int job)
+{
+    Process pn;
+    char buf[256], buf2[128], *ret, *state;
+
+    if (jobtab[job].stat & STAT_DONE)
+	ret = dupstring("done");
+    else if (jobtab[job].stat & STAT_STOPPED)
+	ret = dupstring("suspended");
+    else
+	ret = dupstring("running");
+
+    for (pn = jobtab[job].procs; pn; pn = pn->next) {
+
+	if (pn->status == SP_RUNNING)
+	    state = "running";
+	else if (WIFEXITED(pn->status)) {
+	    if (WEXITSTATUS(pn->status))
+		sprintf((state = buf2), "exit %d", (pn->status));
+	    else
+		state = "done";
+	} else if (WIFSTOPPED(pn->status))
+	    state = sigmsg[WSTOPSIG(pn->status)];
+	else if (WCOREDUMP(pn->status))
+	    sprintf((state = buf2), "%s (core dumped)",
+		    sigmsg[WTERMSIG(pn->status)]);
+	else
+	    state = sigmsg[WTERMSIG(pn->status)];
+
+	sprintf(buf, ":%d=%s", pn->pid, state);
+
+	ret = dyncat(ret, buf);
+    }
+    return ret;
+}
+
+/**/
+static HashNode
+getpmjobstate(HashTable ht, char *name)
+{
+    Param pm = NULL;
+    int job;
+
+    HEAPALLOC {
+	pm = (Param) zhalloc(sizeof(struct param));
+	pm->nam = dupstring(name);
+	pm->flags = PM_SCALAR | PM_READONLY;
+	pm->sets.cfn = NULL;
+	pm->gets.cfn = strgetfn;
+	pm->unsetfn = NULL;
+	pm->ct = 0;
+	pm->env = NULL;
+	pm->ename = NULL;
+	pm->old = NULL;
+	pm->level = 0;
+
+	if ((job = atoi(name)) >= 1 && job < MAXJOB &&
+	    jobtab[job].stat && jobtab[job].procs &&
+	    !(jobtab[job].stat & STAT_NOPRINT))
+	    pm->u.str = pmjobstate(job);
+	else {
+	    pm->u.str = dupstring("");
+	    pm->flags |= PM_UNSET;
+	}
+    } LASTALLOC;
+
+    return (HashNode) pm;
+}
+
+/**/
+static void
+scanpmjobstates(HashTable ht, ScanFunc func, int flags)
+{
+    struct param pm;
+    int job;
+    char buf[40];
+
+    pm.flags = PM_SCALAR | PM_READONLY;
+    pm.sets.cfn = NULL;
+    pm.gets.cfn = strgetfn;
+    pm.unsetfn = NULL;
+    pm.ct = 0;
+    pm.env = NULL;
+    pm.ename = NULL;
+    pm.old = NULL;
+    pm.level = 0;
+
+    for (job = 1; job < MAXJOB; job++) {
+	if (jobtab[job].stat && jobtab[job].procs &&
+	    !(jobtab[job].stat & STAT_NOPRINT)) {
+	    if (func != scancountparams) {
+		sprintf(buf, "%d", job);
+		pm.nam = dupstring(buf);
+		pm.u.str = pmjobstate(job);
+	    }
+	    func((HashNode) &pm, flags);
+	}
+    }
+}
+
+/* Functions for the nameddirs special parameter. */
+
+/**/
+static void
+setpmnameddir(Param pm, char *value)
+{
+    if (!value || *value != '/' || strlen(value) >= PATH_MAX)
+	zwarn("invalid value: %s", value, 0);
+    else
+	adduserdir(pm->nam, value, 0, 1);
+    zsfree(value);
+}
+
+/**/
+static void
+unsetpmnameddir(Param pm, int exp)
+{
+    HashNode hd = nameddirtab->removenode(nameddirtab, pm->nam);
+
+    if (hd)
+	nameddirtab->freenode(hd);
+}
+
+/**/
+static void
+setpmnameddirs(Param pm, HashTable ht)
+{
+    int i;
+    HashNode hn, next, hd;
+
+    for (i = 0; i < nameddirtab->hsize; i++)
+	for (hn = nameddirtab->nodes[i]; hn; hn = next) {
+	    next = hn->next;
+	    if (!(((Nameddir) hn)->flags & ND_USERNAME) &&
+		(hd = nameddirtab->removenode(nameddirtab, hn->nam)))
+		nameddirtab->freenode(hd);
+	}
+
+    if (!ht)
+	return;
+
+    for (i = 0; i < ht->hsize; i++)
+	for (hn = ht->nodes[i]; hn; hn = hn->next) {
+	    struct value v;
+	    char *val;
+
+	    v.isarr = v.inv = v.a = 0;
+	    v.b = -1;
+	    v.arr = NULL;
+	    v.pm = (Param) hn;
+
+	    if (!(val = getstrvalue(&v)) || *val != '/' ||
+		strlen(val) >= PATH_MAX)
+		zwarn("invalid value: %s", val, 0);
+	    else
+		adduserdir(hn->nam, val, 0, 1);
+	}
+
+    /* The INTERACTIVE stuff ensures that the dirs are not immediatly removed
+     * when the sub-pms are deleted. */
+
+    i = opts[INTERACTIVE];
+    opts[INTERACTIVE] = 0;
+    deleteparamtable(ht);
+    opts[INTERACTIVE] = i;
+}
+
+/**/
+static HashNode
+getpmnameddir(HashTable ht, char *name)
+{
+    Param pm = NULL;
+    Nameddir nd;
+
+    HEAPALLOC {
+	pm = (Param) zhalloc(sizeof(struct param));
+	pm->nam = dupstring(name);
+	pm->flags = PM_SCALAR;
+	pm->sets.cfn = setpmnameddir;
+	pm->gets.cfn = strgetfn;
+	pm->unsetfn = unsetpmnameddir;
+	pm->ct = 0;
+	pm->env = NULL;
+	pm->ename = NULL;
+	pm->old = NULL;
+	pm->level = 0;
+	if ((nd = (Nameddir) nameddirtab->getnode(nameddirtab, name)) &&
+	    !(nd->flags & ND_USERNAME))
+	    pm->u.str = dupstring(nd->dir);
+	else {
+	    pm->u.str = dupstring("");
+	    pm->flags |= PM_UNSET;
+	}
+    } LASTALLOC;
+
+    return (HashNode) pm;
+}
+
+/**/
+static void
+scanpmnameddirs(HashTable ht, ScanFunc func, int flags)
+{
+    struct param pm;
+    int i;
+    HashNode hn;
+    Nameddir nd;
+
+    pm.flags = PM_SCALAR;
+    pm.sets.cfn = setpmnameddir;
+    pm.gets.cfn = strgetfn;
+    pm.unsetfn = unsetpmnameddir;
+    pm.ct = 0;
+    pm.env = NULL;
+    pm.ename = NULL;
+    pm.old = NULL;
+    pm.level = 0;
+
+    for (i = 0; i < nameddirtab->hsize; i++)
+	for (hn = nameddirtab->nodes[i]; hn; hn = hn->next) {
+	    if (!((nd = (Nameddir) hn)->flags & ND_USERNAME)) {
+		pm.nam = hn->nam;
+		if (func != scancountparams)
+		    pm.u.str = dupstring(nd->dir);
+		func((HashNode) &pm, flags);
+	    }
+	}
+}
+
+/* Functions for the userdirs special parameter. */
+
+/**/
+static HashNode
+getpmuserdir(HashTable ht, char *name)
+{
+    Param pm = NULL;
+    Nameddir nd;
+
+    nameddirtab->filltable(nameddirtab);
+
+    HEAPALLOC {
+	pm = (Param) zhalloc(sizeof(struct param));
+	pm->nam = dupstring(name);
+	pm->flags = PM_SCALAR | PM_READONLY;
+	pm->sets.cfn = NULL;
+	pm->gets.cfn = strgetfn;
+	pm->unsetfn = NULL;
+	pm->ct = 0;
+	pm->env = NULL;
+	pm->ename = NULL;
+	pm->old = NULL;
+	pm->level = 0;
+	if ((nd = (Nameddir) nameddirtab->getnode(nameddirtab, name)) &&
+	    (nd->flags & ND_USERNAME))
+	    pm->u.str = dupstring(nd->dir);
+	else {
+	    pm->u.str = dupstring("");
+	    pm->flags |= PM_UNSET;
+	}
+    } LASTALLOC;
+
+    return (HashNode) pm;
+}
+
+/**/
+static void
+scanpmuserdirs(HashTable ht, ScanFunc func, int flags)
+{
+    struct param pm;
+    int i;
+    HashNode hn;
+    Nameddir nd;
+
+    nameddirtab->filltable(nameddirtab);
+
+    pm.flags = PM_SCALAR | PM_READONLY;
+    pm.sets.cfn = NULL;
+    pm.gets.cfn = strgetfn;
+    pm.unsetfn = NULL;
+    pm.ct = 0;
+    pm.env = NULL;
+    pm.ename = NULL;
+    pm.old = NULL;
+    pm.level = 0;
+
+    for (i = 0; i < nameddirtab->hsize; i++)
+	for (hn = nameddirtab->nodes[i]; hn; hn = hn->next) {
+	    if ((nd = (Nameddir) hn)->flags & ND_USERNAME) {
+		pm.nam = hn->nam;
+		if (func != scancountparams)
+		    pm.u.str = dupstring(nd->dir);
+		func((HashNode) &pm, flags);
+	    }
+	}
+}
+
+/* Functions for the regularaliases and globalaliases special parameters. */
+
+/**/
+static void
+setpmralias(Param pm, char *value)
+{
+    int dis = 0;
+
+    if (strpfx("<disabled> ", value)) {
+	strcpy(value, value + 11);
+	dis = DISABLED;
+    }
+    aliastab->addnode(aliastab, ztrdup(pm->nam), createaliasnode(value, dis));
+}
+
+/**/
+static void
+setpmgalias(Param pm, char *value)
+{
+    int dis = 0;
+
+    if (strpfx("<disabled> ", value)) {
+	strcpy(value, value + 11);
+	dis = DISABLED;
+    }
+    aliastab->addnode(aliastab, ztrdup(pm->nam),
+		      createaliasnode(value, dis | ALIAS_GLOBAL));
+}
+
+/**/
+static void
+unsetpmalias(Param pm, int exp)
+{
+    HashNode hd = aliastab->removenode(aliastab, pm->nam);
+
+    if (hd)
+	aliastab->freenode(hd);
+}
+
+/**/
+static void
+setpmaliases(Param pm, HashTable ht, int global)
+{
+    int i;
+    HashNode hn, next, hd;
+
+    for (i = 0; i < aliastab->hsize; i++)
+	for (hn = aliastab->nodes[i]; hn; hn = next) {
+	    next = hn->next;
+	    if (((global && (((Alias) hn)->flags & ALIAS_GLOBAL)) ||
+		 (!global && !(((Alias) hn)->flags & ALIAS_GLOBAL))) &&
+		(hd = aliastab->removenode(aliastab, hn->nam)))
+		aliastab->freenode(hd);
+	}
+
+    if (!ht)
+	return;
+
+    for (i = 0; i < ht->hsize; i++)
+	for (hn = ht->nodes[i]; hn; hn = hn->next) {
+	    struct value v;
+	    char *val;
+
+	    v.isarr = v.inv = v.a = 0;
+	    v.b = -1;
+	    v.arr = NULL;
+	    v.pm = (Param) hn;
+
+	    if ((val = getstrvalue(&v)))
+		aliastab->addnode(aliastab, ztrdup(hn->nam),
+				  createaliasnode(ztrdup(val),
+						  (global ? ALIAS_GLOBAL : 0)));
+	}
+    deleteparamtable(ht);
+}
 
-#define PAR_NAM "parameters"
-#define CMD_NAM "commands"
-#define FUN_NAM "functions"
-#define OPT_NAM "options"
-#define MOD_NAM "modules"
-#define DIR_NAM "dirstack"
+/**/
+static void
+setpmraliases(Param pm, HashTable ht)
+{
+    setpmaliases(pm, ht, 0);
+}
 
-static Param parpm, cmdpm, funpm, optpm, modpm, dirpm;
+/**/
+static void
+setpmgaliases(Param pm, HashTable ht)
+{
+    setpmaliases(pm, ht, 1);
+}
+
+/**/
+static HashNode
+getpmalias(HashTable ht, char *name, int global)
+{
+    Param pm = NULL;
+    Alias al;
+
+    HEAPALLOC {
+	pm = (Param) zhalloc(sizeof(struct param));
+	pm->nam = dupstring(name);
+	pm->flags = PM_SCALAR;
+	pm->sets.cfn = (global ? setpmgalias : setpmralias);
+	pm->gets.cfn = strgetfn;
+	pm->unsetfn = unsetpmalias;
+	pm->ct = 0;
+	pm->env = NULL;
+	pm->ename = NULL;
+	pm->old = NULL;
+	pm->level = 0;
+	if ((al = (Alias) aliastab->getnode2(aliastab, name)) &&
+	    ((global && (al->flags & ALIAS_GLOBAL)) ||
+	     (!global && !(al->flags & ALIAS_GLOBAL))))
+	    pm->u.str = ((al->flags & DISABLED) ?
+			 dyncat("<disabled> ", al->text) :
+			 dupstring(al->text));
+	else {
+	    pm->u.str = dupstring("");
+	    pm->flags |= PM_UNSET;
+	}
+    } LASTALLOC;
+
+    return (HashNode) pm;
+}
+
+/**/
+static HashNode
+getpmralias(HashTable ht, char *name)
+{
+    return getpmalias(ht, name, 0);
+}
+
+/**/
+static HashNode
+getpmgalias(HashTable ht, char *name)
+{
+    return getpmalias(ht, name, 1);
+}
+
+/**/
+static void
+scanpmaliases(HashTable ht, ScanFunc func, int flags, int global)
+{
+    struct param pm;
+    int i;
+    HashNode hn;
+    Alias al;
+
+    pm.flags = PM_SCALAR;
+    pm.sets.cfn = (global ? setpmgalias : setpmralias);
+    pm.gets.cfn = strgetfn;
+    pm.unsetfn = unsetpmalias;
+    pm.ct = 0;
+    pm.env = NULL;
+    pm.ename = NULL;
+    pm.old = NULL;
+    pm.level = 0;
+
+    for (i = 0; i < aliastab->hsize; i++)
+	for (hn = aliastab->nodes[i]; hn; hn = hn->next) {
+	    if ((global && ((al = (Alias) hn)->flags & ALIAS_GLOBAL)) ||
+		(!global && !((al = (Alias) hn)->flags & ALIAS_GLOBAL))) {
+		pm.nam = hn->nam;
+		if (func != scancountparams)
+		    pm.u.str = ((hn->flags & DISABLED) ?
+				dyncat("<disabled> ", al->text) :
+				dupstring(al->text));
+		func((HashNode) &pm, flags);
+	    }
+	}
+}
+
+/**/
+static void
+scanpmraliases(HashTable ht, ScanFunc func, int flags)
+{
+    scanpmaliases(ht, func, flags, 0);
+}
+
+/**/
+static void
+scanpmgaliases(HashTable ht, ScanFunc func, int flags)
+{
+    scanpmaliases(ht, func, flags, 1);
+}
+
+/* Table for defined parameters. */
+
+struct pardef {
+    char *name;
+    int flags;
+    GetNodeFunc getnfn;
+    ScanTabFunc scantfn;
+    void (*hsetfn) _((Param, HashTable));
+    void (*setfn) _((Param, char **));
+    char **(*getfn) _((Param));
+    void (*unsetfn) _((Param, int));
+    Param pm;
+};
+
+static struct pardef partab[] = {
+    { "parameters", PM_READONLY,
+      getpmparameter, scanpmparameters, hashsetfn,
+      NULL, NULL, stdunsetfn, NULL },
+    { "commands", 0,
+      getpmcommand, scanpmcommands, setpmcommands,
+      NULL, NULL, stdunsetfn, NULL },
+    { "functions", 0,
+      getpmfunction, scanpmfunctions, setpmfunctions,
+      NULL, NULL, stdunsetfn, NULL },
+    { "builtins", PM_READONLY,
+      getpmbuiltin, scanpmbuiltins, hashsetfn,
+      NULL, NULL, stdunsetfn, NULL },
+    { "reswords", PM_READONLY,
+      getpmresword, scanpmreswords, hashsetfn,
+      NULL, NULL, stdunsetfn, NULL },
+    { "options", 0,
+      getpmoption, scanpmoptions, setpmoptions,
+      NULL, NULL, stdunsetfn, NULL },
+    { "modules", PM_READONLY,
+      getpmmodule, scanpmmodules, hashsetfn,
+      NULL, NULL, stdunsetfn, NULL },
+    { "dirstack", PM_ARRAY|PM_HIDE|PM_SPECIAL|PM_REMOVABLE,
+      NULL, NULL, NULL,
+      dirssetfn, dirsgetfn, stdunsetfn, NULL },
+    { "history", PM_READONLY,
+      getpmhistory, scanpmhistory, hashsetfn,
+      NULL, NULL, stdunsetfn, NULL },
+    { "historywords", PM_ARRAY|PM_HIDE|PM_SPECIAL|PM_READONLY,
+      NULL, NULL, NULL,
+      arrsetfn, histwgetfn, stdunsetfn, NULL },
+    { "jobtexts", PM_READONLY,
+      getpmjobtext, scanpmjobtexts, hashsetfn,
+      NULL, NULL, stdunsetfn, NULL },
+    { "jobstates", PM_READONLY,
+      getpmjobstate, scanpmjobstates, hashsetfn,
+      NULL, NULL, stdunsetfn, NULL },
+    { "nameddirs", 0,
+      getpmnameddir, scanpmnameddirs, setpmnameddirs,
+      NULL, NULL, stdunsetfn, NULL },
+    { "userdirs", PM_READONLY,
+      getpmuserdir, scanpmuserdirs, hashsetfn,
+      NULL, NULL, stdunsetfn, NULL },
+    { "raliases", 0,
+      getpmralias, scanpmraliases, setpmraliases,
+      NULL, NULL, stdunsetfn, NULL },
+    { "galiases", 0,
+      getpmgalias, scanpmgaliases, setpmgaliases,
+      NULL, NULL, stdunsetfn, NULL },
+    { NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+};
 
 /**/
 int
 setup_parameter(Module m)
 {
+    incleanup = 0;
+
     return 0;
 }
 
@@ -830,39 +1702,26 @@ boot_parameter(Module m)
      * example, because the zsh core doesn't support creation of
      * special hashes, yet. */
 
-    unsetparam(PAR_NAM);
-    if (!(parpm = createspecialhash(PAR_NAM, getpmparameter,
-				    scanpmparameters)))
-	return 1;
-    parpm->flags |= PM_READONLY;
-    unsetparam(CMD_NAM);
-    if (!(cmdpm = createspecialhash(CMD_NAM, getpmcommand,
-				    scanpmcommands)))
-	return 1;
-    cmdpm->sets.hfn = setpmcommands;
-    unsetparam(FUN_NAM);
-    if (!(funpm = createspecialhash(FUN_NAM, getpmfunction,
-				    scanpmfunctions)))
-	return 1;
-    funpm->sets.hfn = setpmfunctions;
-    unsetparam(OPT_NAM);
-    if (!(optpm = createspecialhash(OPT_NAM, getpmoption,
-				    scanpmoptions)))
-	return 1;
-    optpm->sets.hfn = setpmoptions;
-    unsetparam(MOD_NAM);
-    if (!(modpm = createspecialhash(MOD_NAM, getpmmodule,
-				    scanpmmodules)))
-	return 1;
-    modpm->flags |= PM_READONLY;
-    unsetparam(DIR_NAM);
-    if (!(dirpm = createparam(DIR_NAM,
-			      PM_ARRAY|PM_HIDE|PM_SPECIAL|PM_REMOVABLE)))
-	return 1;
-    dirpm->sets.afn = dirssetfn;
-    dirpm->gets.afn = dirsgetfn;
-    dirpm->unsetfn = stdunsetfn;
+    struct pardef *def;
 
+    for (def = partab; def->name; def++) {
+	unsetparam(def->name);
+
+	if (def->getnfn) {
+	    if (!(def->pm = createspecialhash(def->name, def->getnfn,
+					      def->scantfn)))
+		return 1;
+	    def->pm->flags |= def->flags;
+	    if (def->hsetfn)
+		def->pm->sets.hfn = def->hsetfn;
+	} else {
+	    if (!(def->pm = createparam(def->name, def->flags)))
+		return 1;
+	    def->pm->sets.afn = def->setfn;
+	    def->pm->gets.afn = def->getfn;
+	    def->pm->unsetfn = def->unsetfn;
+	}
+    }
     return 0;
 }
 
@@ -873,25 +1732,17 @@ int
 cleanup_parameter(Module m)
 {
     Param pm;
+    struct pardef *def;
 
-    /* Remove the special parameters if they are still the same. */
+    incleanup = 1;
 
-    if ((pm = (Param) paramtab->getnode(paramtab, PAR_NAM)) && pm == parpm) {
-	pm->flags &= ~PM_READONLY;
-	unsetparam_pm(pm, 0, 1);
-    }
-    if ((pm = (Param) paramtab->getnode(paramtab, CMD_NAM)) && pm == cmdpm)
-	unsetparam_pm(pm, 0, 1);
-    if ((pm = (Param) paramtab->getnode(paramtab, FUN_NAM)) && pm == funpm)
-	unsetparam_pm(pm, 0, 1);
-    if ((pm = (Param) paramtab->getnode(paramtab, OPT_NAM)) && pm == optpm)
-	unsetparam_pm(pm, 0, 1);
-    if ((pm = (Param) paramtab->getnode(paramtab, MOD_NAM)) && pm == modpm) {
-	pm->flags &= ~PM_READONLY;
-	unsetparam_pm(pm, 0, 1);
+    for (def = partab; def->name; def++) {
+	if ((pm = (Param) paramtab->getnode(paramtab, def->name)) &&
+	    pm == def->pm) {
+	    pm->flags &= ~PM_READONLY;
+	    unsetparam_pm(pm, 0, 1);
+	}
     }
-    if ((pm = (Param) paramtab->getnode(paramtab, DIR_NAM)) && pm == dirpm)
-	unsetparam_pm(pm, 0, 1);
     return 0;
 }
 
diff --git a/Src/Modules/parameter.mdd b/Src/Modules/parameter.mdd
index 57f975757..715b1ab55 100644
--- a/Src/Modules/parameter.mdd
+++ b/Src/Modules/parameter.mdd
@@ -1,3 +1,3 @@
-autoparams="parameters commands functions options modules dirstack"
+autoparams="parameters commands functions builtins reswords options modules dirstack history historywords jobtexts jobstates nameddirs userdirs raliases galiases"
 
 objects="parameter.o"
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index 8b31d97fa..68575cff4 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -1,5 +1,5 @@
 /*
- * comp.h - header file for completion
+ * complete.h - header file for completion
  *
  * This file is part of zsh, the Z shell.
  *
@@ -27,143 +27,10 @@
  *
  */
 
-#undef compctlread
-
-typedef struct compctlp  *Compctlp;
-typedef struct compctl   *Compctl;
-typedef struct compcond  *Compcond;
-typedef struct patcomp   *Patcomp;
 typedef struct cmatcher  *Cmatcher;
 typedef struct cmlist    *Cmlist;
 typedef struct cpattern  *Cpattern;
 typedef struct menuinfo  *Menuinfo;
-
-/* node for compctl hash table (compctltab) */
-
-struct compctlp {
-    HashNode next;		/* next in hash chain               */
-    char *nam;			/* command name                     */
-    int flags;			/* CURRENTLY UNUSED                 */
-    Compctl cc;			/* pointer to the compctl desc.     */
-};
-
-/* for the list of pattern compctls */
-
-struct patcomp {
-    Patcomp next;
-    char *pat;
-    Compctl cc;
-};
-
-/* compctl -x condition */
-
-struct compcond {
-    Compcond and, or;		/* the next or'ed/and'ed conditions    */
-    int type;			/* the type (CCT_*)                    */
-    int n;			/* the array length                    */
-    union {			/* these structs hold the data used to */
-	struct {		/* test this condition                 */
-	    int *a, *b;		/* CCT_POS, CCT_NUMWORDS               */
-	}
-	r;
-	struct {		/* CCT_CURSTR, CCT_CURPAT,... */
-	    int *p;
-	    char **s;
-	}
-	s;
-	struct {		/* CCT_RANGESTR,... */
-	    char **a, **b;
-	}
-	l;
-    }
-    u;
-};
-
-#define CCT_UNUSED     0
-#define CCT_POS        1
-#define CCT_CURSTR     2
-#define CCT_CURPAT     3
-#define CCT_WORDSTR    4
-#define CCT_WORDPAT    5
-#define CCT_CURSUF     6
-#define CCT_CURPRE     7
-#define CCT_CURSUB     8
-#define CCT_CURSUBC    9
-#define CCT_NUMWORDS  10
-#define CCT_RANGESTR  11
-#define CCT_RANGEPAT  12
-#define CCT_QUOTE     13
-
-/* Contains the real description for compctls */
-
-struct compctl {
-    int refc;			/* reference count                         */
-    Compctl next;		/* next compctl for -x                     */
-    unsigned long mask, mask2;	/* masks of things to complete (CC_*)      */
-    char *keyvar;		/* for -k (variable)                       */
-    char *glob;			/* for -g (globbing)                       */
-    char *str;			/* for -s (expansion)                      */
-    char *func;			/* for -K (function)                       */
-    char *widget;		/* for -i (function)                       */
-    char *explain;		/* for -X (explanation)                    */
-    char *ylist;		/* for -y (user-defined desc. for listing) */
-    char *prefix, *suffix;	/* for -P and -S (prefix, suffix)          */
-    char *subcmd;		/* for -l (command name to use)            */
-    char *substr;		/* for -1 (command name to use)            */
-    char *withd;		/* for -w (with directory                  */
-    char *hpat;			/* for -H (history pattern)                */
-    int hnum;			/* for -H (number of events to search)     */
-    char *gname;		/* for -J and -V (group name)              */
-    Compctl ext;		/* for -x (first of the compctls after -x) */
-    Compcond cond;		/* for -x (condition for this compctl)     */
-    Compctl xor;		/* for + (next of the xor'ed compctls)     */
-    Cmatcher matcher;		/* matcher control (-M) */
-    char *mstr;			/* matcher string */
-};
-
-/* objects to complete (mask) */
-#define CC_FILES	(1<<0)
-#define CC_COMMPATH	(1<<1)
-#define CC_REMOVE	(1<<2)
-#define CC_OPTIONS	(1<<3)
-#define CC_VARS		(1<<4)
-#define CC_BINDINGS	(1<<5)
-#define CC_ARRAYS	(1<<6)
-#define CC_INTVARS	(1<<7)
-#define CC_SHFUNCS	(1<<8)
-#define CC_PARAMS	(1<<9)
-#define CC_ENVVARS	(1<<10)
-#define CC_JOBS		(1<<11)
-#define CC_RUNNING	(1<<12)
-#define CC_STOPPED	(1<<13)
-#define CC_BUILTINS	(1<<14)
-#define CC_ALREG	(1<<15)
-#define CC_ALGLOB	(1<<16)
-#define CC_USERS	(1<<17)
-#define CC_DISCMDS	(1<<18)
-#define CC_EXCMDS	(1<<19)
-#define CC_SCALARS	(1<<20)
-#define CC_READONLYS	(1<<21)
-#define CC_SPECIALS	(1<<22)
-#define CC_DELETE	(1<<23)
-#define CC_NAMED	(1<<24)
-#define CC_QUOTEFLAG	(1<<25)
-#define CC_EXTCMDS	(1<<26)
-#define CC_RESWDS	(1<<27)
-#define CC_DIRS		(1<<28)
-
-#define CC_EXPANDEXPL	(1<<30)
-#define CC_RESERVED	(1<<31)
-
-/* objects to complete (mask2) */
-#define CC_NOSORT	(1<<0)
-#define CC_XORCONT	(1<<1)
-#define CC_CCCONT	(1<<2)
-#define CC_PATCONT	(1<<3)
-#define CC_DEFCONT	(1<<4)
-#define CC_UNIQCON      (1<<5)
-#define CC_UNIQALL      (1<<6)
-
 typedef struct cexpl *Cexpl;
 typedef struct cmgroup *Cmgroup;
 typedef struct cmatch *Cmatch;
@@ -191,7 +58,6 @@ struct cmgroup {
     int ecount;			/* number of explanation string */
     Cexpl *expls;		/* explanation strings */
     int ccount;			/* number of compctls used */
-    Compctl *ccs;		/* the compctls used */
     LinkList lexpls;		/* list of explanation string while building */
     LinkList lmatches;		/* list of matches */
     LinkList lfmatches;		/* list of matches without fignore */
@@ -272,22 +138,68 @@ struct cmatcher {
     int ralen;			/* length of right anchor */
 };
 
-
 #define CMF_LINE  1
 #define CMF_LEFT  2
 #define CMF_RIGHT 4
 
-
 struct cpattern {
     Cpattern next;		/* next sub-pattern */
     unsigned char tab[256];	/* table of matched characters */
     int equiv;			/* if this is a {...} class */
 };
 
-/* Flags for makecomplist*(). Things not to do. */
+/* This is a special return value for parse_cmatcher(), *
+ * signalling an error. */
+
+#define pcm_err ((Cmatcher) 1)
+
+/* 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;
+    int min, max;
+};
 
-#define CFN_FIRST   1
-#define CFN_DEFAULT 2
+#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
+#define CLF_MATCHED 128
+
+/* Information for ambiguous completions. One for fignore ignored and   *
+ * one for normal completion. */
+
+typedef struct aminfo *Aminfo;
+
+struct aminfo {
+    Cmatch firstm;		/* the first match                        */
+    int exact;			/* if there was an exact match            */
+    Cmatch exactm;		/* the exact match (if any)               */
+    int count;			/* number of matches                      */
+    Cline line;			/* unambiguous line string                */
+};
 
 /* Information about menucompletion stuff. */
 
@@ -463,3 +375,13 @@ struct chdata {
 
 #define CP_KEYPARAMS   27
 #define CP_ALLKEYS     ((unsigned int) 0x7ffffff)
+
+/* Types of completion. */
+
+#define COMP_COMPLETE        0
+#define COMP_LIST_COMPLETE   1
+#define COMP_SPELL           2
+#define COMP_EXPAND          3
+#define COMP_EXPAND_COMPLETE 4
+#define COMP_LIST_EXPAND     5
+#define COMP_ISEXPAND(X) ((X) >= COMP_EXPAND)
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index d09fff259..d00e317c2 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -31,8 +31,24 @@
 #include "compctl.pro"
 #define GLOBAL_PROTOTYPES
 #include "zle_tricky.pro"
+#include "complete.pro"
 #undef GLOBAL_PROTOTYPES
 
+/* Default completion infos */
+ 
+/**/
+struct compctl cc_compos, cc_default, cc_first, cc_dummy;
+ 
+/* Hash table for completion info for commands */
+ 
+/**/
+HashTable compctltab;
+
+/* List of pattern compctls */
+
+/**/
+Patcomp patcomps;
+
 #define COMP_LIST	(1<<0)	/* -L */
 #define COMP_COMMAND	(1<<1)	/* -C */
 #define COMP_DEFAULT	(1<<2)	/* -D */
@@ -48,105 +64,219 @@ static int cclist;
 /* Mask for determining what to print */
 static unsigned long showmask = 0;
 
-/* This is a special return value for parse_cmatcher(), *
- * signalling an error. */
+/**/
+static void
+createcompctltable(void)
+{
+    compctltab = newhashtable(23, "compctltab", NULL);
+
+    compctltab->hash        = hasher;
+    compctltab->emptytable  = emptyhashtable;
+    compctltab->filltable   = NULL;
+    compctltab->cmpnodes    = strcmp;
+    compctltab->addnode     = addhashnode;
+    compctltab->getnode     = gethashnode2;
+    compctltab->getnode2    = gethashnode2;
+    compctltab->removenode  = removehashnode;
+    compctltab->disablenode = NULL;
+    compctltab->enablenode  = NULL;
+    compctltab->freenode    = freecompctlp;
+    compctltab->printnode   = printcompctlp;
+
+    patcomps = NULL;
+}
 
-#define pcm_err ((Cmatcher) 1)
+/**/
+static void
+freecompctlp(HashNode hn)
+{
+    Compctlp ccp = (Compctlp) hn;
 
-/* Copy a list of completion matchers. */
+    zsfree(ccp->nam);
+    freecompctl(ccp->cc);
+    zfree(ccp, sizeof(struct compctlp));
+}
 
-static Cmlist
-cpcmlist(Cmlist l)
+/**/
+void
+freecompctl(Compctl cc)
 {
-    Cmlist r = NULL, *p = &r, n;
+    if (cc == &cc_default ||
+ 	cc == &cc_first ||
+	cc == &cc_compos ||
+	--cc->refc > 0)
+	return;
 
-    while (l) {
-	*p = n = (Cmlist) zalloc(sizeof(struct cmlist));
-	n->next = NULL;
-	n->matcher = cpcmatcher(l->matcher);
-	n->str = ztrdup(l->str);
+    zsfree(cc->keyvar);
+    zsfree(cc->glob);
+    zsfree(cc->str);
+    zsfree(cc->func);
+    zsfree(cc->explain);
+    zsfree(cc->ylist);
+    zsfree(cc->prefix);
+    zsfree(cc->suffix);
+    zsfree(cc->hpat);
+    zsfree(cc->gname);
+    zsfree(cc->subcmd);
+    zsfree(cc->substr);
+    if (cc->cond)
+	freecompcond(cc->cond);
+    if (cc->ext) {
+	Compctl n, m;
 
-	p = &(n->next);
-	l = l->next;
+	n = cc->ext;
+	do {
+	    m = (Compctl) (n->next);
+	    freecompctl(n);
+	    n = m;
+	}
+	while (n);
     }
-    return r;
+    if (cc->xor && cc->xor != &cc_default)
+	freecompctl(cc->xor);
+    if (cc->matcher)
+	freecmatcher(cc->matcher);
+    zsfree(cc->mstr);
+    zfree(cc, sizeof(struct compctl));
 }
 
-/* Copy a completion matcher list. */
-
 /**/
-static Cmatcher
-cpcmatcher(Cmatcher m)
+void
+freecompcond(void *a)
 {
-    Cmatcher r = NULL, *p = &r, n;
-
-    while (m) {
-	*p = n = (Cmatcher) zalloc(sizeof(struct cmatcher));
-
-	n->refc = 1;
-	n->next = NULL;
-	n->flags = m->flags;
-	n->line = cpcpattern(m->line);
-	n->llen = m->llen;
-	n->word = cpcpattern(m->word);
-	n->wlen = m->wlen;
-	n->left = cpcpattern(m->left);
-	n->lalen = m->lalen;
-	n->right = cpcpattern(m->right);
-	n->ralen = m->ralen;
-
-	p = &(n->next);
-	m = m->next;
-    }
-    return r;
+    Compcond cc = (Compcond) a;
+    Compcond and, or, c;
+    int n;
+
+    for (c = cc; c; c = or) {
+	or = c->or;
+	for (; c; c = and) {
+	    and = c->and;
+	    if (c->type == CCT_POS ||
+		c->type == CCT_NUMWORDS) {
+		free(c->u.r.a);
+		free(c->u.r.b);
+	    } else if (c->type == CCT_CURSUF ||
+		       c->type == CCT_CURPRE) {
+		for (n = 0; n < c->n; n++)
+		    if (c->u.s.s[n])
+			zsfree(c->u.s.s[n]);
+		free(c->u.s.s);
+	    } else if (c->type == CCT_RANGESTR ||
+		       c->type == CCT_RANGEPAT) {
+		for (n = 0; n < c->n; n++)
+		    if (c->u.l.a[n])
+			zsfree(c->u.l.a[n]);
+		free(c->u.l.a);
+		for (n = 0; n < c->n; n++)
+		    if (c->u.l.b[n])
+			zsfree(c->u.l.b[n]);
+		free(c->u.l.b);
+	    } else {
+		for (n = 0; n < c->n; n++)
+		    if (c->u.s.s[n])
+			zsfree(c->u.s.s[n]);
+		free(c->u.s.p);
+		free(c->u.s.s);
+	    }
+	    zfree(c, sizeof(struct compcond));
+	}
+    }
 }
 
-/* Copy a completion matcher pattern. */
-
 /**/
-static Cpattern
-cpcpattern(Cpattern o)
+int
+compctlread(char *name, char **args, char *ops, char *reply)
 {
-    Cpattern r = NULL, *p = &r, n;
+    char *buf, *bptr;
 
-    while (o) {
-	*p = n = (Cpattern) zalloc(sizeof(struct cpattern));
+    /* only allowed to be called for completion */
+    if (!incompctlfunc) {
+	zwarnnam(name, "option valid only in functions called for completion",
+		NULL, 0);
+	return 1;
+    }
 
-	n->next = NULL;
-	memcpy(n->tab, o->tab, 256);
-	n->equiv = o->equiv;
+    if (ops['l']) {
+	/* -ln gives the index of the word the cursor is currently on, which is
+	available in cs (but remember that Zsh counts from one, not zero!) */
+	if (ops['n']) {
+	    char nbuf[14];
+
+	    if (ops['e'] || ops['E'])
+		printf("%d\n", cs + 1);
+	    if (!ops['e']) {
+		sprintf(nbuf, "%d", cs + 1);
+		setsparam(reply, ztrdup(nbuf));
+	    }
+	    return 0;
+	}
+	/* without -n, the current line is assigned to the given parameter as a
+	scalar */
+	if (ops['e'] || ops['E']) {
+	    zputs((char *) line, stdout);
+	    putchar('\n');
+	}
+	if (!ops['e'])
+	    setsparam(reply, ztrdup((char *) line));
+    } else {
+	int i;
 
-	p = &(n->next);
-	o = o->next;
-    }
-    return r;
-}
+	/* -cn gives the current cursor position within the current word, which
+	is available in clwpos (but remember that Zsh counts from one, not
+	zero!) */
+	if (ops['n']) {
+	    char nbuf[14];
+
+	    if (ops['e'] || ops['E'])
+		printf("%d\n", clwpos + 1);
+	    if (!ops['e']) {
+		sprintf(nbuf, "%d", clwpos + 1);
+		setsparam(reply, ztrdup(nbuf));
+	    }
+	    return 0;
+	}
+	/* without -n, the words of the current line are assigned to the given
+	parameters separately */
+	if (ops['A'] && !ops['e']) {
+	    /* the -A option means that one array is specified, instead of
+	    many parameters */
+	    char **p, **b = (char **)zcalloc((clwnum + 1) * sizeof(char *));
 
-/* Set the global match specs. */
+	    for (i = 0, p = b; i < clwnum; p++, i++)
+		*p = ztrdup(clwords[i]);
 
-/**/
-static int
-set_gmatcher(char *name, char **argv)
-{
-    Cmlist l = NULL, *q = &l, n;
-    Cmatcher m;
+	    setaparam(reply, b);
+	    return 0;
+	}
+	if (ops['e'] || ops['E']) {
+	    for (i = 0; i < clwnum; i++) {
+		zputs(clwords[i], stdout);
+		putchar('\n');
+	    }
 
-    while (*argv) {
-	if ((m = parse_cmatcher(name, *argv)) == pcm_err)
-	    return 1;
-	*q = n = (Cmlist) zhalloc(sizeof(struct cmlist));
-	n->next = NULL;
-	n->matcher = m;
-	n->str = *argv++;
+	    if (ops['e'])
+		return 0;
+	}
 
-	q = &(n->next);
-    }
-    freecmlist(cmatcher);
-    PERMALLOC {
-	cmatcher = cpcmlist(l);
-    } LASTALLOC;
+	for (i = 0; i < clwnum && *args; reply = *args++, i++)
+	    setsparam(reply, ztrdup(clwords[i]));
+
+	if (i < clwnum) {
+	    int j, len;
 
-    return 1;
+	    for (j = i, len = 0; j < clwnum; len += strlen(clwords[j++]));
+	    bptr = buf = zalloc(len + j - i);
+	    while (i < clwnum) {
+		strucpy(&bptr, clwords[i++]);
+		*bptr++ = ' ';
+	    }
+	    bptr[-1] = '\0';
+	} else
+	    buf = ztrdup("");
+	setsparam(reply, buf);
+    }
+    return 0;
 }
 
 /* Try to get the global matcher from the given compctl. */
@@ -190,208 +320,6 @@ print_gmatcher(int ac)
     }
 }
 
-/* Parse a string for matcher control, containing multiple matchers. */
-
-/**/
-static Cmatcher
-parse_cmatcher(char *name, char *s)
-{
-    Cmatcher ret = NULL, r = NULL, n;
-    Cpattern line, word, left, right;
-    int fl, ll, wl, lal, ral, err;
-
-    if (!*s)
-	return NULL;
-
-    while (*s) {
-	while (*s && inblank(*s)) s++;
-
-	if (!*s) break;
-
-	switch (*s) {
-	case 'l': fl = CMF_LEFT; break;
-	case 'r': fl = CMF_RIGHT; break;
-	case 'm': fl = 0; break;
-	case 'L': fl = CMF_LEFT | CMF_LINE; break;
-	case 'R': fl = CMF_RIGHT | CMF_LINE; break;
-	case 'M': fl = CMF_LINE; break;
-	default:
-	    zwarnnam(name, "unknown match specification character `%c'", NULL, *s);
-	    return pcm_err;
-	}
-	if (s[1] != ':') {
-	    zwarnnam(name, "missing `:'", NULL, 0);
-	    return pcm_err;
-	}
-	s += 2;
-	if (!*s) {
-	    zwarnnam(name, "missing patterns", NULL, 0);
-	    return pcm_err;
-	}
-	if (fl & CMF_LEFT) {
-	    left = parse_pattern(name, &s, &lal, '|', &err);
-	    if (err)
-		return pcm_err;
-	    if (!*s || !*++s) {
-		zwarnnam(name, "missing line pattern", NULL, 0);
-		return pcm_err;
-	    }
-	} else
-	    left = NULL;
-
-	line = parse_pattern(name, &s, &ll, ((fl & CMF_RIGHT) ? '|' : '='),
-			     &err);
-	if (err)
-	    return pcm_err;
-	if ((fl & CMF_RIGHT) && (!*s || !*++s)) {
-	    zwarnnam(name, "missing right anchor", NULL, 0);
-	} else if (!(fl & CMF_RIGHT)) {
-	    if (!*s) {
-		zwarnnam(name, "missing word pattern", NULL, 0);
-		return pcm_err;
-	    }
-	    s++;
-	}
-	if (fl & CMF_RIGHT) {
-	    right = parse_pattern(name, &s, &ral, '=', &err);
-	    if (err)
-		return pcm_err;
-	    if (!*s) {
-		zwarnnam(name, "missing word pattern", NULL, 0);
-		return pcm_err;
-	    }
-	    s++;
-	} else
-	    right = NULL;
-
-	if (*s == '*') {
-	    if (!(fl & (CMF_LEFT | CMF_RIGHT))) {
-		zwarnnam(name, "need anchor for `*'", NULL, 0);
-		return pcm_err;
-	    }
-	    word = NULL;
-	    wl = -1;
-	    s++;
-	} else {
-	    word = parse_pattern(name, &s, &wl, 0, &err);
-
-	    if (!word && !line) {
-		zwarnnam(name, "need non-empty word or line pattern", NULL, 0);
-		return pcm_err;
-	    }
-	}
-	if (err)
-	    return pcm_err;
-
-	n = (Cmatcher) zcalloc(sizeof(*ret));
-	n->next = NULL;
-	n->flags = fl;
-	n->line = line;
-	n->llen = ll;
-	n->word = word;
-	n->wlen = wl;
-	n->left = left;
-	n->lalen = lal;
-	n->right = right;
-	n->ralen = ral;
-
-	if (ret)
-	    r->next = n;
-	else
-	    ret = n;
-
-	r = n;
-    }
-    return ret;
-}
-
-/* Parse a pattern for matcher control. */
-
-/**/
-static Cpattern
-parse_pattern(char *name, char **sp, int *lp, char e, int *err)
-{
-    Cpattern ret = NULL, r = NULL, n;
-    unsigned char *s = (unsigned char *) *sp;
-    int l = 0;
-
-    *err = 0;
-
-    while (*s && (e ? (*s != e) : !inblank(*s))) {
-	n = (Cpattern) hcalloc(sizeof(*n));
-	n->next = NULL;
-	n->equiv = 0;
-
-	if (*s == '[') {
-	    s = parse_class(n, s + 1, ']');
-	    if (!*s) {
-		*err = 1;
-		zwarnnam(name, "unterminated character class", NULL, 0);
-		return NULL;
-	    }
-	} else if (*s == '{') {
-	    n->equiv = 1;
-	    s = parse_class(n, s + 1, '}');
-	    if (!*s) {
-		*err = 1;
-		zwarnnam(name, "unterminated character class", NULL, 0);
-		return NULL;
-	    }
-	} else if (*s == '?') {
-	    memset(n->tab, 1, 256);
-	} else if (*s == '*' || *s == '(' || *s == ')' || *s == '=') {
-	    *err = 1;
-	    zwarnnam(name, "invalid pattern character `%c'", NULL, *s);
-	    return NULL;
-	} else {
-	    if (*s == '\\' && s[1])
-		s++;
-
-	    memset(n->tab, 0, 256);
-	    n->tab[*s] = 1;
-	}
-	if (ret)
-	    r->next = n;
-	else
-	    ret = n;
-
-	r = n;
-
-	l++;
-	s++;
-    }
-    *sp = (char *) s;
-    *lp = l;
-    return ret;
-}
-
-/* Parse a character class for matcher control. */
-
-/**/
-static unsigned char *
-parse_class(Cpattern p, unsigned char *s, unsigned char e)
-{
-    int n = 0, i = 1, j, eq = (e == '}'), k = 1;
-
-    if (!eq && (*s == '!' || *s == '^') && s[1] != e) { n = 1; s++; }
-
-    memset(p->tab, n, 256);
-
-    n = !n;
-    while (*s && (k || *s != e)) {
-	if (s[1] == '-' && s[2] != e) {
-	    /* a run of characters */
-	    for (j = (int) *s; j <= (int) s[2]; j++)
-		p->tab[j] = (eq ? i++ : n);
-
-	    s += 3;
-	} else
-	    p->tab[*s++] = (eq ? i++ : n);
-	k = 0;
-    }
-    return s;
-}
-
 /* Parse the basic flags for `compctl' */
 
 /**/
@@ -599,19 +527,6 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef, int cl)
 		    *argv = "" - 1;
 		}
 		break;
-	    case 'i':
-		if ((*argv)[1]) {
-		    cct.widget = (*argv) + 1;
-		    *argv = "" - 1;
-		} else if (!argv[1]) {
-		    zwarnnam(name, "function name expected after -%c", NULL,
-			    **argv);
-		    return 1;
-		} else {
-		    cct.widget = *++argv;
-		    *argv = "" - 1;
-		}
-		break;
 	    case 'Y':
 		cct.mask |= CC_EXPANDEXPL;
 		goto expl;
@@ -1260,7 +1175,6 @@ cc_assign(char *name, Compctl *ccptr, Compctl cct, int reass)
     zsfree(cc->glob);
     zsfree(cc->str);
     zsfree(cc->func);
-    zsfree(cc->widget);
     zsfree(cc->explain);
     zsfree(cc->ylist);
     zsfree(cc->prefix);
@@ -1282,7 +1196,6 @@ cc_assign(char *name, Compctl *ccptr, Compctl cct, int reass)
     cc->glob = ztrdup(cct->glob);
     cc->str = ztrdup(cct->str);
     cc->func = ztrdup(cct->func);
-    cc->widget = ztrdup(cct->widget);
     cc->explain = ztrdup(cct->explain);
     cc->ylist = ztrdup(cct->ylist);
     cc->prefix = ztrdup(cct->prefix);
@@ -1494,7 +1407,6 @@ printcompctl(char *s, Compctl cc, int printflags, int ispat)
 	printif(cc->gname, 'J');
     printif(cc->keyvar, 'k');
     printif(cc->func, 'K');
-    printif(cc->widget, 'i');
     printif(cc->explain, (cc->mask & CC_EXPANDEXPL) ? 'Y' : 'X');
     printif(cc->ylist, 'y');
     printif(cc->prefix, 'P');
@@ -1716,955 +1628,2094 @@ bin_compctl(char *name, char **argv, char *ops, int func)
     return ret;
 }
 
-/**/
-static int
-bin_compgen(char *name, char **argv, char *ops, int func)
-{
-    Compctl cc;
-    int ret = 0;
-
-    if (incompfunc != 1) {
-	zerrnam(name, "can only be called from completion function", NULL, 0);
-	return 1;
-    }
-    cc = (Compctl) zcalloc(sizeof(*cc));
-    cclist = 0;
-    showmask = 0;
+/* Flags for makecomplist*(). Things not to do. */
 
-    if (get_compctl(name, &argv, cc, 1, 0, 1))
-	ret = 1;
-    else if (*argv) {
-	zerrnam(name, "command names illegal", NULL, 0);
-	ret = 1;
-    } else
-	ret = makecomplistcallptr(cc);
+#define CFN_FIRST   1
+#define CFN_DEFAULT 2
 
-    freecompctl(cc);
-    return ret;
-}
-
-/**/
 static int
-bin_compadd(char *name, char **argv, char *ops, int func)
+bin_compcall(char *name, char **argv, char *ops, int func)
 {
-    struct cadata dat;
-    char *p, **sp, *e, *m = NULL;
-    int dm;
-    Cmatcher match = NULL;
-
     if (incompfunc != 1) {
 	zerrnam(name, "can only be called from completion function", NULL, 0);
 	return 1;
     }
-    dat.ipre = dat.isuf = dat.ppre = dat.psuf = dat.prpre =
-	dat.pre = dat.suf = dat.group = dat.rems = dat.remf = dat.disp = 
-	dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = dat.ylist = NULL;
-    dat.match = NULL;
-    dat.flags = 0;
-    dat.aflags = CAF_MATCH;
-
-    for (; *argv && **argv ==  '-'; argv++) {
-	if (!(*argv)[1]) {
-	    argv++;
-	    break;
-	}
-	for (p = *argv + 1; *p; p++) {
-	    sp = NULL;
-	    e = NULL;
-	    dm = 0;
-	    switch (*p) {
-	    case 'q':
-		dat.flags |= CMF_REMOVE;
-		break;
-	    case 'Q':
-		dat.aflags |= CAF_QUOTE;
-		break;
-	    case 'f':
-		dat.flags |= CMF_FILE;
-		break;
-	    case 'e':
-		dat.flags |= CMF_ISPAR;
-		break;
-	    case 'F':
-		sp = &(dat.ign);
-		e = "string expected after -%c";
-		break;
-	    case 'n':
-		dat.flags |= CMF_NOLIST;
-		break;
-	    case 'U':
-		dat.aflags &= ~CAF_MATCH;
-		break;
-	    case 'P':
-		sp = &(dat.pre);
-		e = "string expected after -%c";
-		break;
-	    case 'S':
-		sp = &(dat.suf);
-		e = "string expected after -%c";
-		break;
-	    case 'J':
-		sp = &(dat.group);
-		e = "group name expected after -%c";
-		break;
-	    case 'V':
-		if (!dat.group)
-		    dat.aflags |= CAF_NOSORT;
-		sp = &(dat.group);
-		e = "group name expected after -%c";
-		break;
-	    case '1':
-		if (!(dat.aflags & CAF_UNIQCON))
-		    dat.aflags |= CAF_UNIQALL;
-		break;
-	    case '2':
-		if (!(dat.aflags & CAF_UNIQALL))
-		    dat.aflags |= CAF_UNIQCON;
-		break;
-	    case 'y':
-		sp = &(dat.ylist);
-		e = "string expected after -%c";
-		break;
-	    case 'i':
-		sp = &(dat.ipre);
-		e = "string expected after -%c";
-		break;
-	    case 'I':
-		sp = &(dat.isuf);
-		e = "string expected after -%c";
-		break;
-	    case 'p':
-		sp = &(dat.ppre);
-		e = "string expected after -%c";
-		break;
-	    case 's':
-		sp = &(dat.psuf);
-		e = "string expected after -%c";
-		break;
-	    case 'W':
-		sp = &(dat.prpre);
-		e = "string expected after -%c";
-		break;
-	    case 'a':
-		dat.aflags |= CAF_ALT;
-		break;
-	    case 'M':
-		sp = &m;
-		e = "matching specification expected after -%c";
-		dm = 1;
-		break;
-	    case 'X':
-		sp = &(dat.exp);
-		e = "string expected after -%c";
-		break;
-	    case 'r':
-		dat.flags |= CMF_REMOVE;
-		sp = &(dat.rems);
-		e = "string expected after -%c";
-		break;
-	    case 'R':
-		dat.flags |= CMF_REMOVE;
-		sp = &(dat.remf);
-		e = "function name expected after -%c";
-		break;
-	    case 'A':
-		sp = &(dat.apar);
-		e = "parameter name expected after -%c";
-		break;
-	    case 'O':
-		sp = &(dat.opar);
-		e = "parameter name expected after -%c";
-		break;
-	    case 'D':
-		sp = &(dat.dpar);
-		e = "parameter name expected after -%c";
-		break;
-	    case 'd':
-		sp = &(dat.disp);
-		e = "parameter name expected after -%c";
-		break;
-	    case 'l':
-		dat.flags |= CMF_DISPLINE;
-		break;
-	    case '-':
-		argv++;
-		goto ca_args;
-	    default:
-		zerrnam(name, "bad option: -%c", NULL, *p);
-		return 1;
-	    }
-	    if (sp) {
-		if (p[1]) {
-		    if (!*sp)
-			*sp = p + 1;
-		    p = "" - 1;
-		} else if (argv[1]) {
-		    argv++;
-		    if (!*sp)
-			*sp = *argv;
-		    p = "" - 1;
-		} else {
-		    zerrnam(name, e, NULL, *p);
-		    return 1;
-		}
-		if (dm && (match = parse_cmatcher(name, m)) == pcm_err) {
-		    match = NULL;
-		    return 1;
-		}
-	    }
-	}
-    }
- ca_args:
-    if (!*argv)
-	return 1;
-
-    dat.match = match = cpcmatcher(match);
-    dm = addmatchesptr(&dat, argv);
-    freecmatcher(match);
-
-    return dm;
+    return makecomplistctl((ops['T'] ? 0 : CFN_FIRST) |
+			   (ops['D'] ? 0 : CFN_DEFAULT));
 }
 
-#define CVT_RANGENUM 0
-#define CVT_RANGEPAT 1
-#define CVT_PRENUM   2
-#define CVT_PREPAT   3
-#define CVT_SUFNUM   4
-#define CVT_SUFPAT   5
-
-/**/
-void
-ignore_prefix(int l)
-{
-    if (l) {
-	char *tmp, sav;
-	int pl = strlen(compprefix);
+/*
+ * Functions to generate matches.
+ */
 
-	if (l > pl)
-	    l = pl;
+/* A pointer to the compctl we are using. */
 
-	sav = compprefix[l];
+static Compctl curcc;
 
-	compprefix[l] = '\0';
-	tmp = tricat(compiprefix, compprefix, "");
-	zsfree(compiprefix);
-	compiprefix = tmp;
-	compprefix[l] = sav;
-	tmp = ztrdup(compprefix + l);
-	zsfree(compprefix);
-	compprefix = tmp;
-    }
-}
+/* A list of all compctls we have already used. */
 
-/**/
-void
-ignore_suffix(int l)
-{
-    if (l) {
-	char *tmp, sav;
-	int sl = strlen(compsuffix);
-
-	if ((l = sl - l) < 0)
-	    l = 0;
-
-	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 LinkList ccused, lastccused;
 
-/**/
-void
-restrict_range(int b, int e)
-{
-    int wl = arrlen(compwords) - 1;
+/* A stack of currently used compctls. */
 
-    if (wl && b >= 0 && e >= 0 && (b > 0 || e < wl)) {
-	int i;
-	char **p, **q, **pp;
+static LinkList ccstack;
 
-	if (e > wl)
-	    e = wl;
+/* The beginning and end of a word range to be used by -l. */
 
-	i = e - b + 1;
-	p = (char **) zcalloc((i + 1) * sizeof(char *));
+static int brange, erange;
 
-	for (q = p, pp = compwords + b; i; i--, q++, pp++)
-	    *q = ztrdup(*pp);
-	freearray(compwords);
-	compwords = p;
-	compcurrent -= b;
-    }
-}
+/* This is used to detect when and what to continue. */
 
-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);
+static unsigned long ccont;
 
-	    if (na < 0)
-		na += l;
-	    else
-		na--;
-	    if (nb < 0)
-		nb += l;
-	    else
-		nb--;
+/* Two patterns used when doing glob-completion.  The first one is built *
+ * from the whole word we are completing and the second one from that    *
+ * part of the word that was identified as a possible filename.          */
 
-	    if (compcurrent - 1 < na || compcurrent - 1 > nb)
-		return 0;
-	    if (mod)
-		restrict_range(na, nb);
-	    return 1;
-	}
-    case CVT_RANGEPAT:
-	{
-	    char **p;
-	    int i, l = arrlen(compwords), t = 0, b = 0, e = l - 1;
-	    Patprog pp;
+static Patprog patcomp, filecomp;
 
-	    i = compcurrent - 1;
-	    if (i < 0 || i >= l)
-		return 0;
+/* We store the following prefixes/suffixes:                               *
+ * lpre/lsuf -- what's on the line                                         *
+ * rpre/rsuf -- same as lpre/lsuf, but expanded                            *
+ * ppre/psuf   -- the path prefix/suffix                                   *
+ * 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                 *
+ * qipre, qisuf-- ingnored quoted string                                   *
+ *                                                                         *
+ * 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. */
 
-	    singsub(&sa);
-	    pp = patcompile(sa, PAT_STATIC, NULL);
+static char *lpre, *lsuf;
+static char *rpre, *rsuf;
+static char *ppre, *psuf, *lppre, *lpsuf, *prpre;
+static char *fpre, *fsuf;
+static char *qfpre, *qfsuf, *qrpre, *qrsuf, *qlpre, *qlsuf;
+static int lpl, lsl, rpl, rsl, fpl, fsl, lppl, lpsl;
+static int noreal;
 
-	    for (i--, p = compwords + i; i >= 0; p--, i--) {
-		if (pattry(pp, *p)) {
-		    b = i + 1;
-		    t = 1;
-		    break;
-		}
-	    }
-	    if (t && sb) {
-		int tt = 0;
+/* This is either zero or equal to the special character the word we are *
+ * trying to complete starts with (e.g. Tilde or Equals).                */
 
-		singsub(&sb);
-		pp = patcompile(sb, PAT_STATIC, NULL);
+static char ic;
 
-		for (i++, p = compwords + i; i < l; p++, i++) {
-		    if (pattry(pp, *p)) {
-			e = i - 1;
-			tt = 1;
-			break;
-		    }
-		}
-		if (tt && i < compcurrent)
-		    t = 0;
-	    }
-	    if (e < b)
-		t = 0;
-	    if (t && mod)
-		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:
-	{
-	    Patprog pp;
+/* This variable says what we are currently adding to the list of matches. */
 
-	    if (!na)
-		return 0;
+static int addwhat;
 
-	    if (!(pp = patcompile(sa, PAT_STATIC, 0)))
-		return 0;
+/* Convenience macro for calling bslashquote() (formerly quotename()). *
+ * This uses the instring variable above.                              */
 
-	    if (test == CVT_PREPAT) {
-		int l, add;
-		char *p, sav;
+#define quotename(s, e) bslashquote(s, e, instring)
 
-		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 = pattry(pp, compprefix);
-		    *p = sav;
-		    if (test && !--na)
-			break;
-		}
-		if (!l)
-		    return 0;
-		if (mod)
-		    ignore_prefix(p - compprefix);
-	    } else {
-		int l, ol, add;
-		char *p;
+/* Hook functions */
 
-		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 (pattry(pp, p) && !--na)
-			break;
+static int
+ccmakehookfn(Hookdef dummy, struct ccmakedat *dat)
+{
+    makecomplistglobal(dat->str, dat->incmd, dat->lst, 0);
 
-		if (!l)
-		    return 0;
-		if (mod)
-		    ignore_suffix(ol - (p - compsuffix));
-	    }
-	    return 1;
-	}
-    }
     return 0;
 }
 
-/**/
 static int
-bin_compset(char *name, char **argv, char *ops, int func)
+ccbeforehookfn(Hookdef dummy, void *zdup)
 {
-    int test = 0, na = 0, nb = 0;
-    char *sa = NULL, *sb = NULL;
+    ccused = newlinklist();
+    ccstack = newlinklist();
 
-    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;
-    case 'q': return set_comp_sepptr();
-    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);
-	remnulargs(sa);
-	if (sb) {
-	    tokenize(sb);
-	    sb = rembslash(sb);
-	    remnulargs(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);
-	remnulargs(sa);
-	break;
-    }
-    return !do_comp_vars(test, na, sa, nb, sb, 1);
+    return 0;
 }
 
-/**/
 static int
-bin_compcall(char *name, char **argv, char *ops, int func)
+ccafterhookfn(Hookdef dummy, void *zdup)
 {
-    if (incompfunc != 1) {
-	zerrnam(name, "can only be called from completion function", NULL, 0);
-	return 1;
-    }
-    return makecomplistctlptr((ops['T'] ? 0 : CFN_FIRST) |
-			      (ops['D'] ? 0 : CFN_DEFAULT));
-}
+    LinkNode n;
 
-/* Definitions for the special parameters. Note that these have to match the
- * order of the CP_* bits in comp.h */
+    if (zdup) {
+	if (lastccused)
+	    freelinklist(lastccused, (FreeFunc) freecompctl);
 
-#define VAL(X) ((void *) (&(X)))
-struct compparam {
-    char *name;
-    int type;
-    void *var, *set, *get;
-};
-
-static struct compparam comprparams[] = {
-    { "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 },
-    { "QIPREFIX", PM_SCALAR | PM_READONLY, VAL(compqiprefix), NULL, NULL },
-    { "QISUFFIX", PM_SCALAR | PM_READONLY, VAL(compqisuffix), NULL, NULL },
-    { NULL, 0, NULL, NULL, NULL }
-};
+	PERMALLOC {
+	    lastccused = newlinklist();
+	    for (n = firstnode(ccused); n; incnode(n))
+		addlinknode(lastccused, getdata(n));
+	} LASTALLOC;
+    } else
+	for (n = firstnode(ccused); n; incnode(n))
+	    if (((Compctl) getdata(n)) != &cc_dummy)
+		freecompctl((Compctl) getdata(n));
 
-static struct compparam compkparams[] = {
-    { "nmatches", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_nmatches) },
-    { "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 | PM_READONLY, VAL(compquote), NULL, NULL },
-    { "quoting", PM_SCALAR | PM_READONLY, VAL(compquoting), NULL, NULL },
-    { "restore", PM_SCALAR, VAL(comprestore), NULL, NULL },
-    { "list", PM_SCALAR, NULL, VAL(set_complist), VAL(get_complist) },
-    { "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) },
-    { "list_max", PM_INTEGER, VAL(complistmax), NULL, NULL },
-    { "last_prompt", PM_SCALAR, VAL(complastprompt), NULL, NULL },
-    { "to_end", PM_SCALAR, VAL(comptoend), NULL, NULL },
-    { "old_list", PM_SCALAR, VAL(compoldlist), NULL, NULL },
-    { "old_insert", PM_SCALAR, VAL(compoldins), NULL, NULL },
-    { "vared", PM_SCALAR, VAL(compvared), NULL, NULL },
-    { "alternate_nmatches", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_anmatches) },
-    { "list_lines", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_listlines) },
-    { NULL, 0, NULL, NULL, NULL }
-};
+    return 0;
+}
 
-#define COMPSTATENAME "compstate"
+/* This adds a match to the list of matches.  The string to add is given   *
+ * in s, the type of match is given in the global variable addwhat and     *
+ * the parameter t (if not NULL) is a pointer to a hash node node which    *
+ * may be used to give other information to this function.                 *
+ *                                                                         *
+ * addwhat contains either one of the special values (negative, see below) *
+ * or the inclusive OR of some of the CC_* flags used for compctls.        */
 
+/**/
 static void
-addcompparams(struct compparam *cp, Param *pp)
+addmatch(char *s, char *t)
 {
-    for (; cp->name; cp++, pp++) {
-	Param pm = createparam(cp->name,
-			       cp->type |PM_SPECIAL|PM_REMOVABLE|PM_LOCAL);
-	if (!pm)
-	    pm = (Param) paramtab->getnode(paramtab, cp->name);
-	DPUTS(!pm, "param not set in addcompparams");
-
-	*pp = pm;
-	pm->level = locallevel + 1;
-	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;
-	    }
+    int isfile = 0, isalt = 0, isexact;
+    char *ms = NULL, *tt;
+    HashNode hn;
+    Param pm;
+    Cline lc = NULL;
+    Brinfo bp, bpl = brbeg, bsl = brend, bpt, bst;
+
+    for (bp = brbeg; bp; bp = bp->next)
+	bp->curpos = ((addwhat == CC_QUOTEFLAG) ? bp->qpos : bp->pos);
+    for (bp = brend; bp; bp = bp->next)
+	bp->curpos = ((addwhat == CC_QUOTEFLAG) ? bp->qpos : bp->pos);
+
+    /*
+     * addwhat: -5 is for files,
+     *          -6 is for glob expansions,
+     *          -8 is for executable files (e.g. command paths),
+     *          -9 is for parameters
+     *          -7 is for command names (from cmdnamtab)
+     *          -4 is for a cdable parameter
+     *          -3 is for executable command names.
+     *          -2 is for anything unquoted
+     *          -1 is for other file specifications
+     *          (things with `~' or `=' at the beginning, ...).
+     */
+
+    /* Just to make the code cleaner */
+    hn = (HashNode) t;
+    pm = (Param) t;
+
+    if (addwhat == -1 || addwhat == -5 || addwhat == -6 ||
+	addwhat == CC_FILES || addwhat == -7 || addwhat == -8) {
+	int ppl = (ppre ? strlen(ppre) : 0), psl = (psuf ? strlen(psuf) : 0);
+
+	while (bpl && bpl->curpos < ppl)
+	    bpl = bpl->next;
+	while (bsl && bsl->curpos < psl)
+	    bsl = bsl->next;
+
+	if ((addwhat == CC_FILES ||
+	     addwhat == -5) && !*psuf) {
+	    /* If this is a filename, do the fignore check. */
+	    char **pt = fignore;
+	    int filell, sl = strlen(s);
+
+	    for (isalt = 0; !isalt && *pt; pt++)
+		if ((filell = strlen(*pt)) < sl &&
+		    !strcmp(*pt, s + sl - filell))
+		    isalt = 1;
+	}
+	ms = ((addwhat == CC_FILES || addwhat == -6 ||
+	       addwhat == -5 || addwhat == -8) ? 
+	      comp_match(qfpre, qfsuf, s, filecomp, &lc, (ppre && *ppre ? 1 : 2),
+			 &bpl, ppl ,&bsl, psl, &isexact) :
+	      comp_match(fpre, fsuf, s, filecomp, &lc, 0,
+			 &bpl, ppl, &bsl, psl, &isexact));
+	if (!ms)
+	    return;
+
+	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) &&
+	       !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))    ||
+		  ((addwhat & CC_INTVARS)   &&  (hn->flags & PM_INTEGER))  ||
+		  ((addwhat & CC_ENVVARS)   &&  (hn->flags & PM_EXPORTED)) ||
+		  ((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))) &&
+		 !pm->level) ||
+		((( addwhat & CC_SHFUNCS)				  ||
+		  ( addwhat & CC_BUILTINS)				  ||
+		  ( addwhat & CC_EXTCMDS)				  ||
+		  ( addwhat & CC_RESWDS)				  ||
+		  ((addwhat & CC_ALREG)   && !(hn->flags & ALIAS_GLOBAL)) ||
+		  ((addwhat & CC_ALGLOB)  &&  (hn->flags & ALIAS_GLOBAL))) &&
+		 (((addwhat & CC_DISCMDS) && (hn->flags & DISABLED)) ||
+		  ((addwhat & CC_EXCMDS)  && !(hn->flags & DISABLED)))) ||
+		((addwhat & CC_BINDINGS) && !(hn->flags & DISABLED))))) {
+	char *p1, *s1, *p2, *s2;
+
+	if (addwhat == CC_QUOTEFLAG) {
+	    p1 = qrpre; s1 = qrsuf;
+	    p2 = rpre;  s2 = rsuf;
 	} else {
-	    pm->sets.cfn = (void (*) _((Param, char *))) cp->set;
-	    pm->gets.cfn = (char *(*) _((Param))) cp->get;
+	    p1 = qlpre; s1 = qlsuf;
+	    p2 = lpre;  s2 = lsuf;
+	}
+	bpt = bpl;
+	bst = bsl;
+
+	if (!(ms = comp_match(p1, s1, s, patcomp, &lc,
+			      (addwhat == CC_QUOTEFLAG),
+			      &bpl, strlen(p1), &bsl, strlen(s1),
+			      &isexact))) {
+	    bpl = bpt;
+	    bsl = bst;
+	    if (!(ms = comp_match(p2, s2, s, NULL, &lc,
+				  (addwhat == CC_QUOTEFLAG),
+				  &bpl, strlen(p2), &bsl, strlen(s2),
+				  &isexact)))
+		return;
 	}
-	pm->unsetfn = compunsetfn;
     }
+    if (!ms)
+	return;
+    add_match_data(isalt, ms, lc, ipre, ripre, isuf, 
+		   (incompfunc ? dupstring(curcc->prefix) : curcc->prefix),
+		   prpre, 
+		   (isfile ? lppre : NULL), NULL,
+		   (isfile ? lpsuf : NULL), NULL,
+		   (incompfunc ? dupstring(curcc->suffix) : curcc->suffix),
+		   (mflags | isfile), isexact);
 }
 
 /**/
-void
-makecompparams(void)
+static void
+maketildelist(void)
 {
-    Param cpm;
-    HashTable tht;
-
-    addcompparams(comprparams, comprpms);
-
-    if (!(cpm = createparam(COMPSTATENAME,
-			    PM_SPECIAL|PM_REMOVABLE|PM_LOCAL|PM_HASHED)))
-	cpm = (Param) paramtab->getnode(paramtab, COMPSTATENAME);
-    DPUTS(!cpm, "param not set in makecompparams");
-
-    comprpms[CPN_COMPSTATE] = cpm;
-    tht = paramtab;
-    cpm->level = locallevel;
-    cpm->gets.hfn = get_compstate;
-    cpm->sets.hfn = set_compstate;
-    cpm->unsetfn = compunsetfn;
-    cpm->u.hash = paramtab = newparamtable(31, COMPSTATENAME);
-    addcompparams(compkparams, compkpms);
-    paramtab = tht;
+    /* add all the usernames to the named directory table */
+    nameddirtab->filltable(nameddirtab);
+
+    scanhashtable(nameddirtab, 0, (addwhat==-1) ? 0 : ND_USERNAME, 0,
+		  addhnmatch, 0);
 }
 
+/* This does the check for compctl -x `n' and `N' patterns. */
+
 /**/
-static HashTable
-get_compstate(Param pm)
+int
+getcpat(char *str, int cpatindex, char *cpat, int class)
 {
-    return pm->u.hash;
+    char *s, *t, *p;
+    int d = 0;
+
+    if (!str || !*str)
+	return -1;
+
+    cpat = rembslash(cpat);
+
+    if (!cpatindex)
+	cpatindex++, d = 0;
+    else if ((d = (cpatindex < 0)))
+	cpatindex = -cpatindex;
+
+    for (s = d ? str + strlen(str) - 1 : str;
+	 d ? (s >= str) : *s;
+	 d ? s-- : s++) {
+	for (t = s, p = cpat; *t && *p; p++) {
+	    if (class) {
+		if (*p == *s && !--cpatindex)
+		    return (int)(s - str + 1);
+	    } else if (*t++ != *p)
+		break;
+	}
+	if (!class && !*p && !--cpatindex)
+	    return t - str;
+    }
+    return -1;
 }
 
+/* Dump a hash table (without sorting).  For each element the addmatch  *
+ * function is called and at the beginning the addwhat variable is set. *
+ * This could be done using scanhashtable(), but this is easy and much  *
+ * more efficient.                                                      */
+
 /**/
 static void
-set_compstate(Param pm, HashTable ht)
+dumphashtable(HashTable ht, int what)
 {
-    struct compparam *cp;
-    Param *pp;
     HashNode hn;
     int i;
-    struct value v;
-    char *str;
+
+    addwhat = what;
 
     for (i = 0; i < ht->hsize; i++)
 	for (hn = ht->nodes[i]; hn; hn = hn->next)
-	    for (cp = compkparams,
-		 pp = compkpms; cp->name; cp++, pp++)
-		if (!strcmp(hn->nam, cp->name)) {
-		    v.isarr = v.inv = v.a = 0;
-		    v.b = -1;
-		    v.arr = NULL;
-		    v.pm = (Param) hn;
-		    if (cp->type == PM_INTEGER)
-			*((zlong *) cp->var) = getintvalue(&v);
-		    else if ((str = getstrvalue(&v))) {
-			zsfree(*((char **) cp->var));
-			*((char **) cp->var) = ztrdup(str);
-		    }
-		    (*pp)->flags &= ~PM_UNSET;
-
-		    break;
-		}
-    deleteparamtable(ht);
+	    addmatch(hn->nam, (char *) hn);
 }
 
-/**/
-static zlong
-get_nmatches(Param pm)
-{
-    return num_matchesptr(1);
-}
+/* ScanFunc used by maketildelist() et al. */
 
 /**/
-static zlong
-get_anmatches(Param pm)
+static void
+addhnmatch(HashNode hn, int flags)
 {
-    return num_matchesptr(0);
+    addmatch(hn->nam, NULL);
 }
 
+/* Perform expansion on the given string and return the result. *
+ * During this errors are not reported.                         */
+
 /**/
-static zlong
-get_listlines(Param pm)
+static char *
+getreal(char *str)
 {
-    return list_linesptr();
+    LinkList l = newlinklist();
+    int ne = noerrs;
+
+    noerrs = 1;
+    addlinknode(l, dupstring(str));
+    prefork(l, 0);
+    noerrs = ne;
+    if (!errflag && nonempty(l) &&
+	((char *) peekfirst(l)) && ((char *) peekfirst(l))[0])
+	return dupstring(peekfirst(l));
+    errflag = 0;
+
+    return dupstring(str);
 }
 
+/* This reads a directory and adds the files to the list of  *
+ * matches.  The parameters say which files should be added. */
+
 /**/
 static void
-set_complist(Param pm, char *v)
+gen_matches_files(int dirs, int execs, int all)
 {
-    comp_listptr(v);
+    DIR *d;
+    struct stat buf;
+    char *n, p[PATH_MAX], *q = NULL, *e;
+    LinkList l = NULL;
+    int ns = 0, ng = opts[NULLGLOB], test, aw = addwhat;
+
+    opts[NULLGLOB] = 1;
+
+    if (*psuf) {
+	/* If there is a path suffix, check if it doesn't have a `*' or *
+	 * `)' at the end (this is used to determine if we should use   *
+	 * globbing).                                                   */
+	q = psuf + strlen(psuf) - 1;
+	ns = !(*q == Star || *q == Outpar);
+	l = newlinklist();
+	/* And generate only directory names. */
+	dirs = 1;
+	all = execs = 0;
+    }
+    /* Open directory. */
+    if ((d = opendir((prpre && *prpre) ? prpre : "."))) {
+	/* If we search only special files, prepare a path buffer for stat. */
+	if (!all && prpre) {
+	    strcpy(p, prpre);
+	    q = p + strlen(prpre);
+	}
+	/* Fine, now read the directory. */
+	while ((n = zreaddir(d, 1)) && !errflag) {
+	    /* Ignore files beginning with `.' unless the thing we found on *
+	     * the command line also starts with a dot or GLOBDOTS is set.  */
+	    if (*n != '.' || *fpre == '.' || isset(GLOBDOTS)) {
+		addwhat = execs ? -8 : -5;
+		if (filecomp)
+		    /* If we have a pattern for the filename check, use it. */
+		    test = pattry(filecomp, n);
+		else {
+		    /* Otherwise use the prefix and suffix strings directly. */
+		    e = n + strlen(n) - fsl;
+		    if ((test = !strncmp(n, fpre, fpl)))
+			test = !strcmp(e, fsuf);
+		    if (!test && mstack) {
+			test = 1;
+			addwhat = CC_FILES;
+		    }
+		}
+		/* Filename didn't match? */
+		if (!test)
+		    continue;
+		if (!all) {
+		    /* We still have to check the file type, so prepare *
+		     * the path buffer by appending the filename.       */
+		    strcpy(q, n);
+		    /* And do the stat. */
+		    if (stat(p, &buf) < 0)
+			continue;
+		}
+		if (all ||
+		    (dirs && S_ISDIR(buf.st_mode)) ||
+		    (execs && S_ISREG(buf.st_mode) && (buf.st_mode&S_IXUGO))) {
+		    /* If we want all files or the file has the right type... */
+		    if (*psuf) {
+			/* We have to test for a path suffix. */
+			int o = strlen(p), tt;
+
+			/* Append it to the path buffer. */
+			strcpy(p + o, psuf);
+
+			/* Do we have to use globbing? */
+			if (ispattern ||
+			    (ns && comppatmatch && *comppatmatch)) {
+			    /* Yes, so append a `*' if needed. */
+			    if (ns && comppatmatch && *comppatmatch == '*') {
+				int tl = strlen(p);
+
+				p[tl] = Star;
+				p[tl + 1] = '\0';
+			    }
+			    /* Do the globbing... */
+			    remnulargs(p);
+			    addlinknode(l, p);
+			    globlist(l);
+			    /* And see if that produced a filename. */
+			    tt = nonempty(l);
+			    while (ugetnode(l));
+			} else
+			    /* Otherwise just check, if we have access *
+			     * to the file.                            */
+			    tt = !access(p, F_OK);
+
+			p[o] = '\0';
+			if (tt)
+			    /* Ok, we can add the filename to the *
+			     * list of matches.                   */
+			    addmatch(dupstring(n), NULL);
+		    } else
+			/* We want all files, so just add the name *
+			 * to the matches.                         */
+			addmatch(dupstring(n), NULL);
+		}
+	    }
+	}
+	closedir(d);
+    }
+    opts[NULLGLOB] = ng;
+    addwhat = aw;
 }
 
-/**/
-static char *
-get_complist(Param pm)
-{
-    return complist;
-}
+/* This returns the node with the given data. */
+/* ...should probably be moved to linklist.c. */
 
-/**/
-static char *
-get_unambig(Param pm)
+static LinkNode
+findnode(LinkList list, void *dat)
 {
-    return unambig_dataptr(NULL);
+    LinkNode tmp = list->first;
+
+    while (tmp && tmp->dat != dat) tmp = tmp->next;
+
+    return tmp;
 }
 
-/**/
-static zlong
-get_unambig_curs(Param pm)
-{
-    int c;
+/* A simple counter to avoid endless recursion between old and new style *
+ * completion. */
 
-    unambig_dataptr(&c);
+static int cdepth = 0;
 
-    return c;
-}
+#define MAX_CDEPTH 16
 
 /**/
-static void
-compunsetfn(Param pm, int exp)
+static int
+makecomplistctl(int flags)
 {
-    if (exp) {
-	if (PM_TYPE(pm->flags) == PM_SCALAR) {
-	    zsfree(*((char **) pm->u.data));
-	    *((char **) pm->u.data) = ztrdup("");
-	} else if (PM_TYPE(pm->flags) == PM_ARRAY) {
-	    freearray(*((char ***) pm->u.data));
-	    *((char ***) pm->u.data) = zcalloc(sizeof(char *));
-	}
-	pm->flags |= PM_UNSET;
-    }
+    int ret;
+
+    if (cdepth == MAX_CDEPTH)
+	return 0;
+
+    cdepth++;
+    SWITCHHEAPS(compheap) {
+	HEAPALLOC {
+	    int ooffs = offs, lip, lp;
+	    char *str = comp_str(&lip, &lp, 0), *t;
+	    char *os = cmdstr, **ow = clwords, **p, **q, qc;
+	    int on = clwnum, op = clwpos, ois =  instring, oib = inbackt;
+	    char *oisuf = isuf, *oqp = qipre, *oqs = qisuf, oaq = autoq;
+
+	    if (compquote && (qc = *compquote)) {
+		if (qc == '`') {
+		    instring = 0;
+		    inbackt = 0;
+		    autoq = '\0';
+		} else {
+		    instring = (qc == '\'' ? 1 : 2);
+		    inbackt = 0;
+		    autoq = qc;
+		}
+	    } else {
+		instring = inbackt = 0;
+		autoq = '\0';
+	    }
+	    qipre = ztrdup(compqiprefix ? compqiprefix : "");
+	    qisuf = ztrdup(compqisuffix ? compqisuffix : "");
+	    isuf = dupstring(compisuffix);
+	    ctokenize(isuf);
+	    remnulargs(isuf);
+	    clwnum = arrlen(compwords);
+	    clwpos = compcurrent - 1;
+	    cmdstr = ztrdup(compwords[0]);
+	    clwords = (char **) zalloc((clwnum + 1) * sizeof(char *));
+	    for (p = compwords, q = clwords; *p; p++, q++) {
+		t = dupstring(*p);
+		tokenize(t);
+		remnulargs(t);
+		*q = ztrdup(t);
+	    }
+	    *q = NULL;
+	    offs = lip + lp;
+	    incompfunc = 2;
+	    ret = makecomplistglobal(str, !clwpos, COMP_COMPLETE, flags);
+	    incompfunc = 1;
+	    isuf = oisuf;
+	    zsfree(qipre);
+	    zsfree(qisuf);
+	    qipre = oqp;
+	    qisuf = oqs;
+	    instring = ois;
+	    inbackt = oib;
+	    autoq = oaq;
+	    offs = ooffs;
+	    zsfree(cmdstr);
+	    freearray(clwords);
+	    cmdstr = os;
+	    clwords = ow;
+	    clwnum = on;
+	    clwpos = op;
+	} LASTALLOC;
+    } SWITCHBACKHEAPS;
+    cdepth--;
+
+    return ret;
 }
 
+/* This function gets the compctls for the given command line and *
+ * adds all completions for them. */
+
 /**/
-void
-comp_setunset(int rset, int runset, int kset, int kunset)
+static int
+makecomplistglobal(char *os, int incmd, int lst, int flags)
 {
-    Param *p;
+    Compctl cc = NULL;
+    char *s;
 
-    if (comprpms && (rset >= 0 || runset >= 0)) {
-	for (p = comprpms; rset || runset; rset >>= 1, runset >>= 1, p++) {
-	    if (rset & 1)
-		(*p)->flags &= ~PM_UNSET;
-	    if (runset & 1)
-		(*p)->flags |= PM_UNSET;
+    ccont = CC_CCCONT;
+    cc_dummy.suffix = NULL;
+
+    if (linwhat == IN_ENV) {
+        /* Default completion for parameter values. */
+	if (!(flags & CFN_DEFAULT)) {
+	    cc = &cc_default;
+	    keypm = NULL;
+	}
+    } else if (linwhat == IN_MATH) {
+	if (!(flags & CFN_DEFAULT)) {
+	    if (insubscr >= 2) {
+		/* Inside subscript of assoc array, complete keys. */
+		cc_dummy.mask = 0;
+		cc_dummy.suffix = (insubscr == 2 ? "]" : "");
+	    } else {
+		/* Other math environment, complete paramete names. */
+		keypm = NULL;
+		cc_dummy.mask = CC_PARAMS;
+	    }
+	    cc = &cc_dummy;
+	    cc_dummy.refc = 10000;
+	}
+    } 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.                */
+	if (!(flags & CFN_DEFAULT)) {
+	    s = clwpos ? clwords[clwpos - 1] : "";
+	    cc_dummy.mask = !strcmp("-o", s) ? CC_OPTIONS :
+		((*s == '-' && s[1] && !s[2]) ||
+		 !strcmp("-nt", s) ||
+		 !strcmp("-ot", s) ||
+		 !strcmp("-ef", s)) ? CC_FILES :
+		(CC_FILES | CC_PARAMS);
+	    cc = &cc_dummy;
+	    cc_dummy.refc = 10000;
+	    keypm = NULL;
 	}
+    } else if (linredir) {
+	if (!(flags & CFN_DEFAULT)) {
+	    /* In redirections use default completion. */
+	    cc = &cc_default;
+	    keypm = NULL;
+	}
+    } else {
+	/* Otherwise get the matches for the command. */
+	keypm = NULL;
+	return makecomplistcmd(os, incmd, flags);
     }
-    if (comprpms && (kset >= 0 || kunset >= 0)) {
-	for (p = compkpms; kset || kunset; kset >>= 1, kunset >>= 1, p++) {
-	    if (kset & 1)
-		(*p)->flags &= ~PM_UNSET;
-	    if (kunset & 1)
-		(*p)->flags |= PM_UNSET;
+    if (cc) {
+	/* First, use the -T compctl. */
+	if (!(flags & CFN_FIRST)) {
+	    makecomplistcc(&cc_first, os, incmd);
+
+	    if (!(ccont & CC_CCCONT))
+		return 0;
 	}
+	makecomplistcc(cc, os, incmd);
+	return 1;
     }
+    return 0;
 }
 
+/* This produces the matches for a command. */
+
 /**/
 static int
-comp_wrapper(List list, FuncWrap w, char *name)
+makecomplistcmd(char *os, int incmd, int flags)
 {
-    if (incompfunc != 1)
-	return 1;
-    else {
-	char *orest, *opre, *osuf, *oipre, *oisuf, **owords;
-	char *oqipre, *oqisuf, *oq, *oqi;
-	zlong ocur;
-	unsigned int runset = 0, kunset = 0, m, sm;
-	Param *pp;
-
-	m = CP_WORDS | CP_CURRENT | CP_PREFIX | CP_SUFFIX | 
-	    CP_IPREFIX | CP_ISUFFIX | CP_QIPREFIX | CP_QISUFFIX;
-	for (pp = comprpms, sm = 1; m; pp++, m >>= 1, sm <<= 1) {
-	    if ((m & 1) && ((*pp)->flags & PM_UNSET))
-		runset |= sm;
-	}
-	if (compkpms[CPN_RESTORE]->flags & PM_UNSET)
-	    kunset = CP_RESTORE;
-	orest = comprestore;
-	comprestore = ztrdup("auto");
-	ocur = compcurrent;
-	opre = dupstring(compprefix);
-	osuf = dupstring(compsuffix);
-	oipre = dupstring(compiprefix);
-	oisuf = dupstring(compisuffix);
-	oqipre = dupstring(compqiprefix);
-	oqisuf = dupstring(compqisuffix);
-	oq = dupstring(compquote);
-	oqi = dupstring(compquoting);
-
-	HEAPALLOC {
-	    owords = arrdup(compwords);
-	} LASTALLOC;
+    Compctl cc;
+    Compctlp ccp;
+    char *s;
+    int ret = 0;
 
-	runshfunc(list, w, name);
-
-	if (comprestore && !strcmp(comprestore, "auto")) {
-	    compcurrent = ocur;
-	    zsfree(compprefix);
-	    compprefix = ztrdup(opre);
-	    zsfree(compsuffix);
-	    compsuffix = ztrdup(osuf);
-	    zsfree(compiprefix);
-	    compiprefix = ztrdup(oipre);
-	    zsfree(compisuffix);
-	    compisuffix = ztrdup(oisuf);
-	    zsfree(compqiprefix);
-	    compqiprefix = ztrdup(oqipre);
-	    zsfree(compqisuffix);
-	    compqisuffix = ztrdup(oqisuf);
-	    zsfree(compquote);
-	    compquote = ztrdup(oq);
-	    zsfree(compquoting);
-	    compquoting = ztrdup(oqi);
-	    freearray(compwords);
-	    PERMALLOC {
-		compwords = arrdup(owords);
-	    } LASTALLOC;
-	    comp_setunset(CP_COMPSTATE |
-			  (~runset & (CP_WORDS | CP_CURRENT | CP_PREFIX |
-				     CP_SUFFIX | CP_IPREFIX | CP_ISUFFIX |
-				     CP_QIPREFIX | CP_QISUFFIX)),
-			  (runset & CP_ALLREALS),
-			  (~kunset & CP_RESTORE), (kunset & CP_ALLKEYS));
-	} else
-	    comp_setunset(CP_COMPSTATE, 0, (~kunset & CP_RESTORE),
-			  (kunset & CP_RESTORE));
-	zsfree(comprestore);
-	comprestore = orest;
+    /* First, use the -T compctl. */
+    if (!(flags & CFN_FIRST)) {
+	makecomplistcc(&cc_first, os, incmd);
 
-	return 0;
+	if (!(ccont & CC_CCCONT))
+	    return 0;
     }
+    /* Then search the pattern compctls, with the command name and the *
+     * full pathname of the command. */
+    if (cmdstr) {
+	ret |= makecomplistpc(os, incmd);
+	if (!(ccont & CC_CCCONT))
+	    return ret;
+    }
+    /* If the command string starts with `=', try the path name of the *
+     * command. */
+    if (cmdstr && cmdstr[0] == Equals) {
+	char *c = findcmd(cmdstr + 1, 1);
+
+	if (c) {
+	    zsfree(cmdstr);
+	    cmdstr = ztrdup(c);
+	}
+    }
+
+    /* Find the compctl for this command, trying the full name and then *
+     * the trailing pathname component. If that doesn't yield anything, *
+     * use default completion. */
+    if (incmd)
+	cc = &cc_compos;
+    else if (!(cmdstr &&
+	  (((ccp = (Compctlp) compctltab->getnode(compctltab, cmdstr)) &&
+	    (cc = ccp->cc)) ||
+	   ((s = dupstring(cmdstr)) && remlpaths(&s) &&
+	    (ccp = (Compctlp) compctltab->getnode(compctltab, s)) &&
+	    (cc = ccp->cc))))) {
+	if (flags & CFN_DEFAULT)
+	    return ret;
+	cc = &cc_default;
+    } else
+	ret|= 1;
+    makecomplistcc(cc, os, incmd);
+    return ret;
 }
 
+/* This add the matches for the pattern compctls. */
+
 /**/
 static int
-comp_check(void)
+makecomplistpc(char *os, int incmd)
 {
-    if (incompfunc != 1) {
-	zerr("condition can only be used in completion function", NULL, 0);
-	return 0;
+    Patcomp pc;
+    Patprog pat;
+    char *s = findcmd(cmdstr, 1);
+    int ret = 0;
+
+    for (pc = patcomps; pc; pc = pc->next) {
+	if ((pat = patcompile(pc->pat, PAT_STATIC, NULL)) &&
+	    (pattry(pat, cmdstr) ||
+	     (s && pattry(pat, s)))) {
+	    makecomplistcc(pc->cc, os, incmd);
+	    ret |= 2;
+	    if (!(ccont & CC_CCCONT))
+		return ret;
+	}
     }
-    return 1;
+    return ret;
 }
 
+/* This produces the matches for one compctl. */
+
 /**/
-static int
-cond_psfix(char **a, int id)
+static void
+makecomplistcc(Compctl cc, char *s, int incmd)
 {
-    if (comp_check()) {
-	if (a[1])
-	    return do_comp_vars(id, cond_val(a, 0), cond_str(a, 1, 1),
-				0, NULL, 0);
-	else
-	    return do_comp_vars(id, -1, cond_str(a, 0, 1), 0, NULL, 0);
-    }
-    return 0;
+    cc->refc++;
+    addlinknode(ccused, cc);
+
+    ccont = 0;
+
+    makecomplistor(cc, s, incmd, 0, 0);
 }
 
+/* This adds the completions for one run of [x]or'ed completions. */
+
 /**/
-static int
-cond_range(char **a, int id)
+static void
+makecomplistor(Compctl cc, char *s, int incmd, int compadd, int sub)
 {
-    return do_comp_vars(CVT_RANGEPAT, 0, cond_str(a, 0, 1), 0,
-			(id ? cond_str(a, 1, 1) : NULL), 0);
+    int mn, ct, um = usemenu;
+
+    /* Loop over xors. */
+    do {
+	mn = mnum;
+
+	/* Loop over ors. */
+	do {
+	    /* Reset the range information if we are not in a sub-list. */
+	    if (!sub) {
+		brange = 0;
+		erange = clwnum - 1;
+	    }
+	    usemenu = 0;
+	    makecomplistlist(cc, s, incmd, compadd);
+	    um |= usemenu;
+
+	    ct = cc->mask2 & CC_XORCONT;
+
+	    cc = cc->xor;
+	} while (cc && ct);
+
+	/* Stop if we got some matches. */
+	if (mn != mnum)
+	    break;
+	if (cc) {
+	    ccont &= ~(CC_DEFCONT | CC_PATCONT);
+	    if (!sub)
+		ccont &= ~CC_CCCONT;
+	}
+    } while (cc);
+
+    usemenu = um;
 }
 
+/* This dispatches for simple and extended completion. */
+
 /**/
 static void
-cmsetfn(Param pm, char **v)
+makecomplistlist(Compctl cc, char *s, int incmd, int compadd)
 {
-    set_gmatcher(pm->nam, v);
+    int oloffs = offs, owe = we, owb = wb, ocs = cs;
+
+    if (cc->ext)
+	/* Handle extended completion. */
+	makecomplistext(cc, s, incmd);
+    else
+	/* Only normal flags. */
+	makecomplistflags(cc, s, incmd, compadd);
+
+    /* Reset some information variables for the next try. */
+    errflag = 0;
+    offs = oloffs;
+    wb = owb;
+    we = owe;
+    cs = ocs;
 }
 
+/* This add matches for extended completion patterns */
+
 /**/
-static char **
-cmgetfn(Param pm)
+static void
+makecomplistext(Compctl occ, char *os, int incmd)
 {
-    int num;
-    Cmlist p;
-    char **ret, **q;
-
-    for (num = 0, p = cmatcher; p; p = p->next, num++);
-
-    ret = (char **) zhalloc((num + 1) * sizeof(char *));
+    Compctl compc;
+    Compcond or, cc;
+    Patprog pprog;
+    int compadd, m = 0, d = 0, t, tt, i, j, a, b, ins;
+    char *sc = NULL, *s, *ss;
+
+    ins = (instring ? instring : (inbackt ? 3 : 0));
+
+    /* This loops over the patterns separated by `-'s. */
+    for (compc = occ->ext; compc; compc = compc->next) {
+	compadd = t = brange = 0;
+	erange = clwnum - 1;
+	/* This loops over OR'ed patterns. */
+	for (cc = compc->cond; cc && !t; cc = or) {
+	    or = cc->or;
+	    /* This loops over AND'ed patterns. */
+	    for (t = 1; cc && t; cc = cc->and) {
+		/* And this loops over [...] pairs. */
+		for (t = i = 0; i < cc->n && !t; i++) {
+		    s = NULL;
+		    brange = 0;
+		    erange = clwnum - 1;
+		    switch (cc->type) {
+		    case CCT_QUOTE:
+			t = ((cc->u.s.s[i][0] == 's' && ins == 1) ||
+			     (cc->u.s.s[i][0] == 'd' && ins == 2) ||
+			     (cc->u.s.s[i][0] == 'b' && ins == 3));
+			break;
+		    case CCT_POS:
+			tt = clwpos;
+			goto cct_num;
+		    case CCT_NUMWORDS:
+			tt = clwnum;
+		    cct_num:
+			if ((a = cc->u.r.a[i]) < 0)
+			    a += clwnum;
+			if ((b = cc->u.r.b[i]) < 0)
+			    b += clwnum;
+			if (cc->type == CCT_POS)
+			    brange = a, erange = b;
+			t = (tt >= a && tt <= b);
+			break;
+		    case CCT_CURSUF:
+		    case CCT_CURPRE:
+			s = ztrdup(clwpos < clwnum ? os : "");
+			untokenize(s);
+			if (isset(COMPLETEINWORD)) s[offs] = '\0';
+			sc = rembslash(cc->u.s.s[i]);
+			a = strlen(sc);
+			if (!strncmp(s, sc, a)) {
+			    compadd = (cc->type == CCT_CURSUF ? a : 0);
+			    t = 1;
+			}
+			break;
+		    case CCT_CURSUB:
+		    case CCT_CURSUBC:
+			if (clwpos < 0 || clwpos >= clwnum)
+			    t = 0;
+			else {
+			    s = ztrdup(os);
+			    untokenize(s);
+			    if (isset(COMPLETEINWORD)) s[offs] = '\0';
+			    a = getcpat(s,
+					cc->u.s.p[i],
+					cc->u.s.s[i],
+					cc->type == CCT_CURSUBC);
+			    if (a != -1)
+				compadd = a, t = 1;
+			}
+			break;
+			
+		    case CCT_CURPAT:
+		    case CCT_CURSTR:
+			tt = clwpos;
+			goto cct_str;
+		    case CCT_WORDPAT:
+		    case CCT_WORDSTR:
+			tt = 0;
+		    cct_str:
+			if ((a = tt + cc->u.s.p[i]) < 0)
+			    a += clwnum;
+			s = ztrdup((a < 0 || a >= clwnum) ? "" :
+				   clwords[a]);
+			untokenize(s);
+			
+			if (cc->type == CCT_CURPAT ||
+			    cc->type == CCT_WORDPAT) {
+			    tokenize(ss = dupstring(cc->u.s.s[i]));
+			    t = ((pprog = patcompile(ss, PAT_STATIC, NULL)) &&
+				 pattry(pprog, s));
+			} else
+			    t = (!strcmp(s, rembslash(cc->u.s.s[i])));
+			break;
+		    case CCT_RANGESTR:
+		    case CCT_RANGEPAT:
+			if (cc->type == CCT_RANGEPAT)
+			    tokenize(sc = dupstring(cc->u.l.a[i]));
+			for (j = clwpos - 1; j > 0; j--) {
+			    untokenize(s = ztrdup(clwords[j]));
+			    if (cc->type == CCT_RANGESTR)
+				sc = rembslash(cc->u.l.a[i]);
+			    if (cc->type == CCT_RANGESTR ?
+				!strncmp(s, sc, strlen(sc)) :
+				((pprog = patcompile(sc, PAT_STATIC, 0)) &&
+				 pattry(pprog, s))) {
+				zsfree(s);
+				brange = j + 1;
+				t = 1;
+				break;
+			    }
+			    zsfree(s);
+			}
+			if (t && cc->u.l.b[i]) {
+			    if (cc->type == CCT_RANGEPAT)
+				tokenize(sc = dupstring(cc->u.l.b[i]));
+			    for (j++; j < clwnum; j++) {
+				untokenize(s = ztrdup(clwords[j]));
+				if (cc->type == CCT_RANGESTR)
+				    sc = rembslash(cc->u.l.b[i]);
+				if (cc->type == CCT_RANGESTR ?
+				    !strncmp(s, sc, strlen(sc)) :
+				    ((pprog = patcompile(sc, PAT_STATIC, 0)) &&
+				     pattry(pprog, s))) {
+				    zsfree(s);
+				    erange = j - 1;
+				    t = clwpos <= erange;
+				    break;
+				}
+				zsfree(s);
+			    }
+			}
+			s = NULL;
+		    }
+		    zsfree(s);
+		}
+	    }
+	}
+	if (t) {
+	    /* The patterns matched, use the flags. */
+	    m = 1;
+	    ccont &= ~(CC_PATCONT | CC_DEFCONT);
+	    makecomplistor(compc, os, incmd, compadd, 1);
+	    if (!d && (ccont & CC_DEFCONT)) {
+		d = 1;
+		compadd = 0;
+		brange = 0;
+		erange = clwnum - 1;
+		makecomplistflags(occ, os, incmd, 0);
+	    }
+	    if (!(ccont & CC_PATCONT))
+		break;
+	}
+    }
+    /* If no pattern matched, use the standard flags. */
+    if (!m) {
+	compadd = 0;
+	brange = 0;
+	erange = clwnum - 1;
+	makecomplistflags(occ, os, incmd, 0);
+    }
+}
 
-    for (q = ret, p = cmatcher; p; p = p->next, q++)
-	*q = dupstring(p->str);
-    *q = NULL;
+/**/
+static int
+sep_comp_string(char *ss, char *s, int noffs)
+{
+    LinkList foo = newlinklist();
+    LinkNode n;
+    int owe = we, owb = wb, ocs = cs, swb, swe, scs, soffs, ne = noerrs;
+    int sl = strlen(ss), tl, got = 0, i = 0, cur = -1, oll = ll;
+    int ois = instring, oib = inbackt;
+    char *tmp, *p, *ns, *ol = (char *) line, sav, oaq = autoq, *qp, *qs;
+
+    swb = swe = soffs = 0;
+    ns = NULL;
+
+    /* Put the string in the lexer buffer and call the lexer to *
+     * get the words we have to expand.                        */
+    zleparse = 1;
+    addedx = 1;
+    noerrs = 1;
+    lexsave();
+    tmp = (char *) zhalloc(tl = sl + 3 + strlen(s));
+    strcpy(tmp, ss);
+    tmp[sl] = ' ';
+    memcpy(tmp + sl + 1, s, noffs);
+    tmp[(scs = cs = sl + 1 + noffs)] = 'x';
+    strcpy(tmp + sl + 2 + noffs, s + noffs);
+    inpush(dupstrspace(tmp), 0, NULL);
+    line = (unsigned char *) tmp;
+    ll = tl - 1;
+    strinbeg(0);
+    noaliases = 1;
+    do {
+	ctxtlex();
+	if (tok == LEXERR) {
+	    int j;
+
+	    if (!tokstr)
+		break;
+	    for (j = 0, p = tokstr; *p; p++)
+		if (*p == Snull || *p == Dnull)
+		    j++;
+	    if (j & 1) {
+		tok = STRING;
+		if (p > tokstr && p[-1] == ' ')
+		    p[-1] = '\0';
+	    }
+	}
+	if (tok == ENDINPUT || tok == LEXERR)
+	    break;
+	if (tokstr && *tokstr)
+	    addlinknode(foo, (p = ztrdup(tokstr)));
+	else
+	    p = NULL;
+	if (!got && !zleparse) {
+	    DPUTS(!p, "no current word in substr");
+	    got = 1;
+	    cur = i;
+	    swb = wb - 1;
+	    swe = we - 1;
+	    soffs = cs - swb;
+	    chuck(p + soffs);
+	    ns = dupstring(p);
+	}
+	i++;
+    } while (tok != ENDINPUT && tok != LEXERR);
+    noaliases = 0;
+    strinend();
+    inpop();
+    errflag = zleparse = 0;
+    noerrs = ne;
+    lexrestore();
+    wb = owb;
+    we = owe;
+    cs = ocs;
+    line = (unsigned char *) ol;
+    ll = oll;
+    if (cur < 0 || i < 1)
+	return 1;
+    owb = offs;
+    offs = soffs;
+    if ((p = check_param(ns, 0, 1))) {
+	for (p = ns; *p; p++)
+	    if (*p == Dnull)
+		*p = '"';
+	    else if (*p == Snull)
+		*p = '\'';
+    }
+    offs = owb;
+    if (*ns == Snull || *ns == Dnull) {
+	instring = (*ns == Snull ? 1 : 2);
+	inbackt = 0;
+	swb++;
+	if (ns[strlen(ns) - 1] == *ns && ns[1])
+	    swe--;
+	autoq = (*ns == Snull ? '\'' : '"');
+    } else {
+	instring = 0;
+	autoq = '\0';
+    }
+    for (p = ns, i = swb; *p; p++, i++) {
+	if (INULL(*p)) {
+	    if (i < scs)
+		soffs--;
+	    if (p[1] || *p != Bnull) {
+		if (*p == Bnull) {
+		    if (scs == i + 1)
+			scs++, soffs++;
+		} else {
+		    if (scs > i--)
+			scs--;
+		}
+	    } else {
+		if (scs == swe)
+		    scs--;
+	    }
+	    chuck(p--);
+	}
+    }
+    sav = s[(i = swb - sl - 1)];
+    s[i] = '\0';
+    qp = tricat(qipre, s, "");
+    s[i] = sav;
+    if (swe < swb)
+	swe = swb;
+    swe -= sl + 1;
+    sl = strlen(s);
+    if (swe > sl)
+	swe = sl, ns[swe - swb + 1] = '\0';
+    qs = tricat(s + swe, qisuf, "");
+    sl = strlen(ns);
+    if (soffs > sl)
+	soffs = sl;
+
+    {
+	char **ow = clwords, *os = cmdstr, *oqp = qipre, *oqs = qisuf;
+	int olws = clwsize, olwn = clwnum, olwp = clwpos;
+	int obr = brange, oer = erange, oof = offs;
+	unsigned long occ = ccont;
+
+	clwsize = clwnum = countlinknodes(foo);
+	clwords = (char **) zalloc((clwnum + 1) * sizeof(char *));
+	for (n = firstnode(foo), i = 0; n; incnode(n), i++) {
+	    p = clwords[i] = (char *) getdata(n);
+	    untokenize(p);
+	}
+	clwords[i] = NULL;
+	clwpos = cur;
+	cmdstr = ztrdup(clwords[0]);
+	brange = 0;
+	erange = clwnum - 1;
+	qipre = qp;
+	qisuf = qs;
+	offs = soffs;
+	ccont = CC_CCCONT;
+	makecomplistcmd(ns, !clwpos, CFN_FIRST);
+	ccont = occ;
+	offs = oof;
+	zsfree(cmdstr);
+	cmdstr = os;
+	freearray(clwords);
+	clwords = ow;
+	clwsize = olws;
+	clwnum = olwn;
+	clwpos = olwp;
+	brange = obr;
+	erange = oer;
+	zsfree(qipre);
+	qipre = oqp;
+	zsfree(qisuf);
+	qisuf = oqs;
+    }
+    autoq = oaq;
+    instring = ois;
+    inbackt = oib;
 
-    return ret;
+    return 0;
 }
 
+/* This adds the completions for the flags in the given compctl. */
+
 /**/
 static void
-cmunsetfn(Param pm, int exp)
+makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 {
-    char *dummy[1];
+    int t, sf1, sf2, ooffs, um = usemenu, delit, oaw, gflags;
+    int mn = mnum, ohp = haspattern;
+    char *p, *sd = NULL, *tt, *s1, *s2, *os =  dupstring(s);
+    struct cmlist ms;
+
+    ccont |= (cc->mask2 & (CC_CCCONT | CC_DEFCONT | CC_PATCONT));
+
+    if (incompfunc != 1 && findnode(ccstack, cc))
+	return;
+
+    MUSTUSEHEAP("complistflags");
+
+    addlinknode(ccstack, cc);
+
+    if (incompfunc != 1 && allccs) {
+	if (findnode(allccs, cc)) {
+	    uremnode(ccstack, firstnode(ccstack));
+	    return;
+	}
+	addlinknode(allccs, cc);
+    }
+    /* Go to the end of the word if complete_in_word is not set. */
+    if (unset(COMPLETEINWORD) && cs != we)
+	cs = we, offs = strlen(s);
+
+    s = dupstring(s);
+    delit = ispattern = 0;
+    usemenu = um;
+    patcomp = filecomp = NULL;
+    rpre = rsuf = lpre = lsuf = ppre = psuf = lppre = lpsuf =
+	fpre = fsuf = ipre = ripre = prpre = 
+	qfpre = qfsuf = qrpre = qrsuf = qlpre = qlsuf = NULL;
+
+    curcc = cc;
+
+    mflags = 0;
+    gflags = (((cc->mask2 & CC_NOSORT ) ? CGF_NOSORT  : 0) |
+	      ((cc->mask2 & CC_UNIQALL) ? CGF_UNIQALL : 0) |
+	      ((cc->mask2 & CC_UNIQCON) ? CGF_UNIQCON : 0));
+    if (cc->gname) {
+	endcmgroup(NULL);
+	begcmgroup(cc->gname, gflags);
+    }
+    if (cc->ylist) {
+	endcmgroup(NULL);
+	begcmgroup(NULL, gflags);
+    }
+    if (cc->mask & CC_REMOVE)
+	mflags |= CMF_REMOVE;
+    if (cc->explain) {
+	curexpl = (Cexpl) zhalloc(sizeof(struct cexpl));
+	curexpl->count = curexpl->fcount = 0;
+    } else
+	curexpl = NULL;
+    /* compadd is the number of characters we have to ignore at the  *
+     * beginning of the word.                                        */
+    if (compadd) {
+	ipre = dupstring(s);
+	ipre[compadd] = '\0';
+	untokenize(ipre);
+	wb += compadd;
+	s += compadd;
+	if ((offs -= compadd) < 0) {
+	    /* It's bigger than our word prefix, so we can't help here... */
+	    uremnode(ccstack, firstnode(ccstack));
+	    return;
+	}
+    } else
+	ipre = NULL;
+
+    if (cc->matcher) {
+	ms.next = mstack;
+	ms.matcher = cc->matcher;
+	mstack = &ms;
+
+	if (!mnum)
+	    add_bmatchers(cc->matcher);
+
+	addlinknode(matchers, cc->matcher);
+	cc->matcher->refc++;
+    }
+    if (mnum && (mstack || bmatchers))
+	update_bmatchers();
+
+    /* Insert the prefix (compctl -P), if any. */
+    if (cc->prefix) {
+	int pl = 0;
+
+	if (*s) {
+	    char *dp = rembslash(cc->prefix);
+	    /* First find out how much of the prefix is already on the line. */
+	    sd = dupstring(s);
+	    untokenize(sd);
+	    pl = pfxlen(dp, sd);
+	    s += pl;
+	    sd += pl;
+	    offs -= pl;
+	}
+    }
+    /* Does this compctl have a suffix (compctl -S)? */
+    if (cc->suffix) {
+	char *sdup = rembslash(cc->suffix);
+	int sl = strlen(sdup), suffixll;
+
+	/* Ignore trailing spaces. */
+	for (p = sdup + sl - 1; p >= sdup && *p == ' '; p--, sl--);
+	p[1] = '\0';
+
+	if (!sd) {
+	    sd = dupstring(s);
+	    untokenize(sd);
+	}
+	/* If the suffix is already there, ignore it (and don't add *
+	 * it again).                                               */
+	if (*sd && (suffixll = strlen(sd)) >= sl &&
+	    offs <= suffixll - sl && !strcmp(sdup, sd + suffixll - sl))
+	    s[suffixll - sl] = '\0';
+    }
+    /* Do we have one of the special characters `~' and `=' at the beginning? */
+    if (incompfunc || ((ic = *s) != Tilde && ic != Equals))
+	ic = 0;
+
+    /* Check if we have to complete a parameter name... */
+    if (!incompfunc && (p = check_param(s, 1, 0))) {
+	s = p;
+	/* And now make sure that we complete parameter names. */
+	cc = &cc_dummy;
+	cc_dummy.refc = 10000;
+	cc_dummy.mask = CC_PARAMS | CC_ENVVARS;
+    }
+    ooffs = offs;
+    /* If we have to ignore the word, do that. */
+    if (cc->mask & CC_DELETE) {
+	delit = 1;
+	*s = '\0';
+	offs = 0;
+	if (isset(AUTOMENU))
+	    usemenu = 1;
+    }
+
+    /* Compute line prefix/suffix. */
+    lpl = offs;
+    lpre = zhalloc(lpl + 1);
+    memcpy(lpre, s, lpl);
+    lpre[lpl] = '\0';
+    qlpre = quotename(lpre, NULL);
+    lsuf = dupstring(s + offs);
+    lsl = strlen(lsuf);
+    qlsuf = quotename(lsuf, NULL);
+
+    /* First check for ~.../... */
+    if (ic == Tilde) {
+	for (p = lpre + lpl; p > lpre; p--)
+	    if (*p == '/')
+		break;
+
+	if (*p == '/')
+	    ic = 0;
+    }
+    /* Compute real prefix/suffix. */
+
+    noreal = !delit;
+    for (p = lpre; *p && *p != String && *p != Tick; p++);
+    tt = ic && !ispar ? lpre + 1 : lpre;
+    rpre = (*p || *lpre == Tilde || *lpre == Equals) ?
+	(noreal = 0, getreal(tt)) :
+	dupstring(tt);
+    qrpre = quotename(rpre, NULL);
+
+    for (p = lsuf; *p && *p != String && *p != Tick; p++);
+    rsuf = *p ? (noreal = 0, getreal(lsuf)) : dupstring(lsuf);
+    qrsuf = quotename(rsuf, NULL);
+
+    /* Check if word is a pattern. */
+
+    for (s1 = NULL, sf1 = 0, p = rpre + (rpl = strlen(rpre)) - 1;
+	 p >= rpre && (ispattern != 3 || !sf1);
+	 p--)
+	if (itok(*p) && (p > rpre || (*p != Equals && *p != Tilde)))
+	    ispattern |= sf1 ? 1 : 2;
+	else if (*p == '/') {
+	    sf1++;
+	    if (!s1)
+		s1 = p;
+	}
+    rsl = strlen(rsuf);
+    for (s2 = NULL, sf2 = t = 0, p = rsuf; *p && (!t || !sf2); p++)
+	if (itok(*p))
+	    t |= sf2 ? 4 : 2;
+	else if (*p == '/') {
+	    sf2++;
+	    if (!s2)
+		s2 = p;
+	}
+    ispattern = ispattern | t;
+
+    /* But if we were asked not to do glob completion, we never treat the *
+     * thing as a pattern.                                                */
+    if (!comppatmatch || !*comppatmatch)
+	ispattern = 0;
+
+    if (ispattern) {
+	/* The word should be treated as a pattern, so compute the matcher. */
+	p = (char *) zhalloc(rpl + rsl + 2);
+	strcpy(p, rpre);
+	if (rpl && p[rpl - 1] != Star &&
+	    (!comppatmatch || *comppatmatch == '*')) {
+	    p[rpl] = Star;
+	    strcpy(p + rpl + 1, rsuf);
+	} else
+	    strcpy(p + rpl, rsuf);
+	patcomp = patcompile(p, 0, NULL);
+	haspattern = 1;
+    }
+    if (!patcomp) {
+	untokenize(rpre);
+	untokenize(rsuf);
+
+	rpl = strlen(rpre);
+	rsl = strlen(rsuf);
+    }
+    untokenize(lpre);
+    untokenize(lsuf);
+
+    if (!(cc->mask & CC_DELETE))
+	hasmatched = 1;
+
+    /* Handle completion of files specially (of course). */
+
+    if ((cc->mask & (CC_FILES | CC_DIRS | CC_COMMPATH)) || cc->glob) {
+	/* s1 and s2 point to the last/first slash in the prefix/suffix. */
+	if (!s1)
+	    s1 = rpre;
+	if (!s2)
+	    s2 = rsuf + rsl;
+
+	/* Compute the path prefix/suffix. */
+	if (*s1 != '/')
+	    ppre = "";
+	else
+	    ppre = dupstrpfx(rpre, s1 - rpre + 1);
+	psuf = dupstring(s2);
+
+	if (cs != wb) {
+	    char save = line[cs];
+
+	    line[cs] = 0;
+	    lppre = dupstring((char *) line + wb +
+			      (qipre && *qipre ?
+			       (strlen(qipre) -
+				(*qipre == '\'' || *qipre == '\"')) : 0));
+	    line[cs] = save;
+	    if (brbeg) {
+		Brinfo bp;
+
+		for (bp = brbeg; bp; bp = bp->next)
+		    strcpy(lppre + bp->qpos,
+			   lppre + bp->qpos + strlen(bp->str));
+	    }
+	    if ((p = strrchr(lppre, '/'))) {
+		p[1] = '\0';
+		lppl = strlen(lppre);
+	    } else if (!sf1) {
+		lppre = NULL;
+		lppl = 0;
+	    } else {
+		lppre = ppre;
+		lppl = strlen(lppre);
+	    }
+	} else {
+	    lppre = NULL;
+	    lppl = 0;
+	}
+	if (cs != we) {
+	    int end = we;
+	    char save = line[end];
 
-    dummy[0] = NULL;
-    set_gmatcher(pm->nam, dummy);
+	    if (qisuf && *qisuf) {
+		int ql = strlen(qisuf);
+
+		end -= ql - (qisuf[ql-1] == '\'' || qisuf[ql-1] == '"');
+	    }
+	    line[end] = 0;
+	    lpsuf = dupstring((char *) (line + cs));
+	    line[end] = save;
+	    if (brend) {
+		Brinfo bp;
+		char *p;
+		int bl;
+
+		for (bp = brend; bp; bp = bp->next) {
+		    p = lpsuf + (we - cs) - bp->qpos - (bl = strlen(bp->str));
+		    strcpy(p, p + bl);
+		}
+	    }
+	    if (!(lpsuf = strchr(lpsuf, '/')) && sf2)
+		lpsuf = psuf;
+	    lpsl = (lpsuf ? strlen(lpsuf) : 0);
+	} else {
+	    lpsuf = NULL;
+	    lpsl = 0;
+	}
+
+	/* And get the file prefix. */
+	fpre = dupstring(((s1 == s || s1 == rpre || ic) &&
+			  (*s != '/' || cs == wb)) ? s1 : s1 + 1);
+	qfpre = quotename(fpre, NULL);
+	/* And the suffix. */
+	fsuf = dupstrpfx(rsuf, s2 - rsuf);
+	qfsuf = quotename(fsuf, NULL);
+
+	if (comppatmatch && *comppatmatch && (ispattern & 2)) {
+	    int t2;
+
+	    /* We have to use globbing, so compute the pattern from *
+	     * the file prefix and suffix with a `*' between them.  */
+	    p = (char *) zhalloc((t2 = strlen(fpre)) + strlen(fsuf) + 2);
+	    strcpy(p, fpre);
+	    if ((!t2 || p[t2 - 1] != Star) && *fsuf != Star &&
+		(!comppatmatch || *comppatmatch == '*'))
+		p[t2++] = Star;
+	    strcpy(p + t2, fsuf);
+	    filecomp = patcompile(p, 0, NULL);
+	}
+	if (!filecomp) {
+	    untokenize(fpre);
+	    untokenize(fsuf);
+
+	    fpl = strlen(fpre);
+	    fsl = strlen(fsuf);
+	}
+	addwhat = -1;
+
+	/* Completion after `~', maketildelist adds the usernames *
+	 * and named directories.                                 */
+	if (ic == Tilde) {
+	    char *oi = ipre;
+
+	    ipre = (ipre ? dyncat("~", ipre) : "~");
+	    maketildelist();
+	    ipre = oi;
+	} else if (ic == Equals) {
+	    /* Completion after `=', get the command names from *
+	     * the cmdnamtab and aliases from aliastab.         */
+	    char *oi = ipre;
+
+	    ipre = (ipre ? dyncat("=", ipre) : "=");
+	    if (isset(HASHLISTALL))
+		cmdnamtab->filltable(cmdnamtab);
+	    dumphashtable(cmdnamtab, -7);
+	    dumphashtable(aliastab, -2);
+	    ipre = oi;
+	} else {
+	    /* Normal file completion... */
+	    if (ispattern & 1) {
+		/* But with pattern matching. */
+		LinkList l = newlinklist();
+		LinkNode n;
+		int ng = opts[NULLGLOB];
+
+		opts[NULLGLOB] = 1;
+
+		addwhat = 0;
+		p = (char *) zhalloc(lpl + lsl + 3);
+		strcpy(p, lpre);
+		if (*lsuf != '*' && *lpre && lpre[lpl - 1] != '*')
+		    strcat(p, "*");
+		strcat(p, lsuf);
+		if (*lsuf && lsuf[lsl - 1] != '*' && lsuf[lsl - 1] != ')')
+		    strcat(p, "*");
+
+		/* Do the globbing. */
+		tokenize(p);
+		remnulargs(p);
+		addlinknode(l, p);
+		globlist(l);
+
+		if (nonempty(l)) {
+		    /* And add the resulting words. */
+		    mflags |= CMF_FILE;
+		    for (n = firstnode(l); n; incnode(n))
+			addmatch(getdata(n), NULL);
+		    mflags &= !CMF_FILE;
+		}
+		opts[NULLGLOB] = ng;
+	    } else {
+		char **dirs = 0, *ta[2];
+
+		/* No pattern matching. */
+		addwhat = CC_FILES;
+
+		if (cc->withd) {
+		    char **pp, **npp, *tp;
+		    int tl = strlen(ppre) + 2, pl;
+
+		    if ((pp = get_user_var(cc->withd))) {
+			dirs = npp =
+			    (char**) zhalloc(sizeof(char *)*(arrlen(pp)+1));
+			while (*pp) {
+			    pl = strlen(*pp);
+			    tp = (char *) zhalloc(strlen(*pp) + tl);
+			    strcpy(tp, *pp);
+			    tp[pl] = '/';
+			    strcpy(tp + pl + 1, ppre);
+			    *npp++ = tp;
+			    pp++;
+			}
+			*npp = '\0';
+		    }
+		}
+		if (!dirs) {
+		    dirs = ta;
+		    if (cc->withd) {
+			char *tp;
+			int pl = strlen(cc->withd);
+
+			ta[0] = tp = (char *) zhalloc(strlen(ppre) + pl + 2);
+			strcpy(tp, cc->withd);
+			tp[pl] = '/';
+			strcpy(tp + pl + 1, ppre);
+		    } else
+			ta[0] = ppre;
+		    ta[1] = NULL;
+		}
+		while (*dirs) {
+		    prpre = *dirs;
+
+		    if (sf2)
+			/* We are in the path, so add only directories. */
+			gen_matches_files(1, 0, 0);
+		    else {
+			if (cc->mask & CC_FILES)
+			    /* Add all files. */
+			    gen_matches_files(0, 0, 1);
+			else if (cc->mask & CC_COMMPATH) {
+			    /* Completion of command paths. */
+			    if (sf1 || cc->withd)
+				/* There is a path prefix, so add *
+				 * directories and executables.   */
+				gen_matches_files(1, 1, 0);
+			    else {
+				/* No path prefix, so add the things *
+				 * reachable via the PATH variable.  */
+				char **pc = path, *pp = prpre;
+
+				for (; *pc; pc++)
+				    if (!**pc || (pc[0][0] == '.' && !pc[0][1]))
+					break;
+				if (*pc) {
+				    prpre = "./";
+				    gen_matches_files(1, 1, 0);
+				    prpre = pp;
+				}
+			    }
+			} else if (cc->mask & CC_DIRS)
+			    gen_matches_files(1, 0, 0);
+			/* The compctl has a glob pattern (compctl -g). */
+			if (cc->glob) {
+			    int ns, pl = strlen(prpre), o, paalloc;
+			    char *g = dupstring(cc->glob), *pa;
+			    char *p2, *p3;
+			    int ne = noerrs, md = opts[MARKDIRS];
+
+			    /* These are used in the globbing code to make *
+			     * things a bit faster.                        */
+			    if (ispattern || mstack)
+				glob_pre = glob_suf = NULL;
+			    else {
+				glob_pre = fpre;
+				glob_suf = fsuf;
+			    }
+			    noerrs = 1;
+			    addwhat = -6;
+			    o = strlen(prpre);
+			    pa = (char *)zalloc(paalloc = o + PATH_MAX);
+			    strcpy(pa, prpre);
+			    opts[MARKDIRS] = 0;
+
+			    /* The compctl -g string may contain more than *
+			     * one pattern, so we need a loop.             */
+			    while (*g) {
+				LinkList l = newlinklist();
+				int ng;
+
+				/* Find the blank terminating the pattern. */
+				while (*g && inblank(*g))
+				    g++;
+				/* Oops, we already reached the end of the
+				   string. */
+				if (!*g)
+				    break;
+				for (p = g + 1; *p && !inblank(*p); p++)
+				    if (*p == '\\' && p[1])
+					p++;
+				/* Get the pattern string. */
+				tokenize(g = dupstrpfx(g, p - g));
+				if (*g == '=')
+				    *g = Equals;
+				if (*g == '~')
+				    *g = Tilde;
+				remnulargs(g);
+				if ((*g == Equals || *g == Tilde) && !cc->withd) {
+				/* The pattern has a `~' or `=' at the  *
+				 * beginning, so we expand this and use *
+				 * the result.                          */
+				    filesub(&g, 0);
+				    addlinknode(l, dupstring(g));
+				} else if (*g == '/' && !cc->withd)
+				/* The pattern is a full path (starting *
+				 * with '/'), so add it unchanged.      */
+				    addlinknode(l, dupstring(g));
+				else {
+				/* It's a simple pattern, so append it to *
+				 * the path we have on the command line.  */
+				    int minlen = o + strlen(g);
+				    if (minlen >= paalloc)
+					pa = (char *)
+					    zrealloc(pa, paalloc = minlen+1);
+				    strcpy(pa + o, g);
+				    addlinknode(l, dupstring(pa));
+				}
+				/* Do the globbing. */
+				ng = opts[NULLGLOB];
+				opts[NULLGLOB] = 1;
+				globlist(l);
+				opts[NULLGLOB] = ng;
+				/* Get the results. */
+				if (nonempty(l) && peekfirst(l)) {
+				    for (p2 = (char *)peekfirst(l); *p2; p2++)
+					if (itok(*p2))
+					    break;
+				    if (!*p2) {
+					if ((*g == Equals || *g == Tilde ||
+					     *g == '/') || cc->withd) {
+					    /* IF the pattern started with `~',  *
+					     * `=', or `/', add the result only, *
+					     * if it really matches what we have *
+					     * on the line.                      *
+					     * Do this if an initial directory   *
+					     * was specified, too.               */
+					    while ((p2 = (char *)ugetnode(l)))
+						if (strpfx(prpre, p2))
+						    addmatch(p2 + pl, NULL);
+					} else {
+					    /* Otherwise ignore the path we *
+					     * prepended to the pattern.    */
+					    while ((p2 = p3 =
+						    (char *)ugetnode(l))) {
+						for (ns = sf1; *p3 && ns; p3++)
+						    if (*p3 == '/')
+							ns--;
+
+						addmatch(p3, NULL);
+					    }
+					}
+				    }
+				}
+				pa[o] = '\0';
+				g = p;
+			    }
+			    glob_pre = glob_suf = NULL;
+			    noerrs = ne;
+			    opts[MARKDIRS] = md;
+
+			    zfree(pa, paalloc);
+			}
+		    }
+		    dirs++;
+		}
+		prpre = NULL;
+	    }
+	}
+	lppre = lpsuf = NULL;
+	lppl = lpsl = 0;
+    }
+    if (ic) {
+	/* Now change the `~' and `=' tokens to the real characters so *
+	 * that things starting with these characters will be added.   */
+	rpre = dyncat((ic == Tilde) ? "~" : "=", rpre);
+	rpl++;
+	qrpre = dyncat((ic == Tilde) ? "~" : "=", qrpre);
+    }
+    if (!ic && (cc->mask & CC_COMMPATH) && !*ppre && !*psuf) {
+	/* If we have to complete commands, add alias names, *
+	 * shell functions and builtins too.                 */
+	dumphashtable(aliastab, -3);
+	dumphashtable(reswdtab, -3);
+	dumphashtable(shfunctab, -3);
+	dumphashtable(builtintab, -3);
+	if (isset(HASHLISTALL))
+	    cmdnamtab->filltable(cmdnamtab);
+	dumphashtable(cmdnamtab, -3);
+	/* And parameter names if autocd and cdablevars are set. */
+	if (isset(AUTOCD) && isset(CDABLEVARS))
+	    dumphashtable(paramtab, -4);
+    }
+    oaw = addwhat = (cc->mask & CC_QUOTEFLAG) ? -2 : CC_QUOTEFLAG;
+
+    if (cc->mask & CC_NAMED)
+	/* Add named directories. */
+	dumphashtable(nameddirtab, addwhat);
+    if (cc->mask & CC_OPTIONS)
+	/* Add option names. */
+	dumphashtable(optiontab, addwhat);
+    if (cc->mask & CC_VARS) {
+	/* And parameter names. */
+	dumphashtable(paramtab, -9);
+	addwhat = oaw;
+    }
+    if (cc->mask & CC_BINDINGS) {
+	/* And zle function names... */
+	dumphashtable(thingytab, CC_BINDINGS);
+	addwhat = oaw;
+    }
+    if (cc->keyvar) {
+	/* This adds things given to the compctl -k flag *
+	 * (from a parameter or a list of words).        */
+	char **usr = get_user_var(cc->keyvar);
+
+	if (usr)
+	    while (*usr)
+		addmatch(*usr++, NULL);
+    }
+    if (cc->mask & CC_USERS) {
+	/* Add user names. */
+	maketildelist();
+	addwhat = oaw;
+    }
+    if (cc->func) {
+	/* This handles the compctl -K flag. */
+	List list;
+	char **r;
+	int lv = lastval;
+	    
+	/* Get the function. */
+	if ((list = getshfunc(cc->func)) != &dummy_list) {
+	    /* We have it, so build a argument list. */
+	    LinkList args = newlinklist();
+	    int osc = sfcontext;
+		
+	    addlinknode(args, cc->func);
+		
+	    if (delit) {
+		p = dupstrpfx(os, ooffs);
+		untokenize(p);
+		addlinknode(args, p);
+		p = dupstring(os + ooffs);
+		untokenize(p);
+		addlinknode(args, p);
+	    } else {
+		addlinknode(args, lpre);
+		addlinknode(args, lsuf);
+	    }
+		
+	    /* This flag allows us to use read -l and -c. */
+	    if (incompfunc != 1)
+		incompctlfunc = 1;
+	    sfcontext = SFC_COMPLETE;
+	    /* Call the function. */
+	    doshfunc(cc->func, list, args, 0, 1);
+	    sfcontext = osc;
+	    incompctlfunc = 0;
+	    /* And get the result from the reply parameter. */
+	    if ((r = get_user_var("reply")))
+		while (*r)
+		    addmatch(*r++, NULL);
+	}
+	lastval = lv;
+    }
+    if (cc->mask & (CC_JOBS | CC_RUNNING | CC_STOPPED)) {
+	/* Get job names. */
+	int i;
+	char *j;
+
+	for (i = 0; i < MAXJOB; i++)
+	    if ((jobtab[i].stat & STAT_INUSE) &&
+		jobtab[i].procs && jobtab[i].procs->text) {
+		int stopped = jobtab[i].stat & STAT_STOPPED;
+
+		j = dupstring(jobtab[i].procs->text);
+		if ((cc->mask & CC_JOBS) ||
+		    (stopped && (cc->mask & CC_STOPPED)) ||
+		    (!stopped && (cc->mask & CC_RUNNING)))
+		    addmatch(j, NULL);
+	    }
+    }
+    if (cc->str) {
+	/* Get the stuff from a compctl -s. */
+	LinkList foo = newlinklist();
+	LinkNode n;
+	int first = 1, ng = opts[NULLGLOB], oowe = we, oowb = wb;
+	char *tmpbuf;
+
+	opts[NULLGLOB] = 1;
+
+	/* Put the string in the lexer buffer and call the lexer to *
+	 * get the words we have to expand.                        */
+	zleparse = 1;
+	lexsave();
+	tmpbuf = (char *)zhalloc(strlen(cc->str) + 5);
+	sprintf(tmpbuf, "foo %s", cc->str); /* KLUDGE! */
+	inpush(tmpbuf, 0, NULL);
+	strinbeg(0);
+	noaliases = 1;
+	do {
+	    ctxtlex();
+	    if (tok == ENDINPUT || tok == LEXERR)
+		break;
+	    if (!first && tokstr && *tokstr)
+		addlinknode(foo, ztrdup(tokstr));
+	    first = 0;
+	} while (tok != ENDINPUT && tok != LEXERR);
+	noaliases = 0;
+	strinend();
+	inpop();
+	errflag = zleparse = 0;
+	lexrestore();
+	/* Fine, now do full expansion. */
+	prefork(foo, 0);
+	if (!errflag) {
+	    globlist(foo);
+	    if (!errflag)
+		/* And add the resulting words as matches. */
+		for (n = firstnode(foo); n; incnode(n))
+		    addmatch((char *)n->dat, NULL);
+	}
+	opts[NULLGLOB] = ng;
+	we = oowe;
+	wb = oowb;
+    }
+    if (cc->hpat) {
+	/* We have a pattern to take things from the history. */
+	Patprog pprogc = NULL;
+	char *e, *h, hpatsav;
+	int i = addhistnum(curhist,-1,HIST_FOREIGN), n = cc->hnum;
+	Histent he = quietgethistent(i, GETHIST_UPWARD);
+
+	/* Parse the pattern, if it isn't the null string. */
+	if (*(cc->hpat)) {
+	    char *thpat = dupstring(cc->hpat);
+
+	    tokenize(thpat);
+	    pprogc = patcompile(thpat, 0, NULL);
+	}
+	/* n holds the number of history line we have to search. */
+	if (!n)
+	    n = -1;
+
+	/* Now search the history. */
+	while (n-- && he) {
+	    int iwords;
+	    for (iwords = he->nwords - 1; iwords >= 0; iwords--) {
+		h = he->text + he->words[iwords*2];
+		e = he->text + he->words[iwords*2+1];
+		hpatsav = *e;
+		*e = '\0';
+		/* We now have a word from the history, ignore it *
+		 * if it begins with a quote or `$'.              */
+		if (*h != '\'' && *h != '"' && *h != '`' && *h != '$' &&
+		    (!pprogc || pattry(pprogc, h)))
+		    /* Otherwise add it if it was matched. */
+		    addmatch(dupstring(h), NULL);
+		if (hpatsav)
+		    *e = hpatsav;
+	    }
+	    he = up_histent(he);
+	}
+    }
+    if ((t = cc->mask & (CC_ARRAYS | CC_INTVARS | CC_ENVVARS | CC_SCALARS |
+			 CC_READONLYS | CC_SPECIALS | CC_PARAMS)))
+	/* Add various flavours of parameters. */
+	dumphashtable(paramtab, t);
+    if ((t = cc->mask & CC_SHFUNCS))
+	/* Add shell functions. */
+	dumphashtable(shfunctab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
+    if ((t = cc->mask & CC_BUILTINS))
+	/* Add builtins. */
+	dumphashtable(builtintab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
+    if ((t = cc->mask & CC_EXTCMDS)) {
+	/* Add external commands */
+	if (isset(HASHLISTALL))
+	    cmdnamtab->filltable(cmdnamtab);
+	dumphashtable(cmdnamtab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
+    }
+    if ((t = cc->mask & CC_RESWDS))
+	/* Add reserved words */
+	dumphashtable(reswdtab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
+    if ((t = cc->mask & (CC_ALREG | CC_ALGLOB)))
+	/* Add the two types of aliases. */
+	dumphashtable(aliastab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
+    if (keypm && cc == &cc_dummy) {
+	/* Add the keys of the parameter in keypm. */
+	scanhashtable(keypm->gets.hfn(keypm), 0, 0, PM_UNSET, addhnmatch, 0);
+	keypm = NULL;
+	cc_dummy.suffix = NULL;
+    }
+    if (!errflag && cc->ylist) {
+	/* generate the user-defined display list: if anything fails, *
+	 * we silently allow the normal completion list to be used.   */
+	char **yaptr = NULL, *uv = NULL;
+	List list;
+
+	if (cc->ylist[0] == '$' || cc->ylist[0] == '(') {
+	    /* from variable */
+	    uv = cc->ylist + (cc->ylist[0] == '$');
+	} else if ((list = getshfunc(cc->ylist)) != &dummy_list) {
+	    /* from function:  pass completions as arg list */
+	    LinkList args = newlinklist();
+	    LinkNode ln;
+	    Cmatch m;
+	    int osc = sfcontext;
+
+	    addlinknode(args, cc->ylist);
+	    for (ln = firstnode(matches); ln; ln = nextnode(ln)) {
+		m = (Cmatch) getdata(ln);
+		if (m->ppre) {
+		    char *p = (char *) zhalloc(strlen(m->ppre) + strlen(m->str) +
+					      strlen(m->psuf) + 1);
+
+		    sprintf(p, "%s%s%s", m->ppre, m->str, m->psuf);
+		    addlinknode(args, dupstring(p));
+		} else
+		    addlinknode(args, dupstring(m->str));
+	    }
+
+	    /* No harm in allowing read -l and -c here, too */
+	    if (incompfunc != 1)
+		incompctlfunc = 1;
+	    sfcontext = SFC_COMPLETE;
+	    doshfunc(cc->ylist, list, args, 0, 1);
+	    sfcontext = osc;
+	    incompctlfunc = 0;
+	    uv = "reply";
+	}
+	if (uv)
+	    yaptr = get_user_var(uv);
+	if ((tt = cc->explain)) {
+	    tt = dupstring(tt);
+	    if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) {
+		singsub(&tt);
+		untokenize(tt);
+	    }
+	    curexpl->str = tt;
+	    if (cc->gname) {
+		endcmgroup(yaptr);
+		begcmgroup(cc->gname, gflags);
+		addexpl();
+	    } else {
+		addexpl();
+		endcmgroup(yaptr);
+		begcmgroup("default", 0);
+	    }
+	}
+    } else if ((tt = cc->explain)) {
+	tt = dupstring(tt);
+	if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) {
+	    singsub(&tt);
+	    untokenize(tt);
+	}
+	curexpl->str = tt;
+	addexpl();
+    }
+    if (cc->subcmd) {
+	/* Handle -l sub-completion. */
+	char **ow = clwords, *os = cmdstr, *ops = NULL;
+	int oldn = clwnum, oldp = clwpos, br;
+	unsigned long occ = ccont;
+	
+	ccont = CC_CCCONT;
+	
+	/* So we restrict the words-array. */
+	if (brange >= clwnum)
+	    brange = clwnum - 1;
+	if (brange < 1)
+	    brange = 1;
+	if (erange >= clwnum)
+	    erange = clwnum - 1;
+	if (erange < 1)
+	    erange = 1;
+	clwnum = erange - brange + 1;
+	clwpos = clwpos - brange;
+	br = brange;
+
+	if (cc->subcmd[0]) {
+	    /* And probably put the command name given to the flag *
+	     * in the array.                                       */
+	    clwpos++;
+	    clwnum++;
+	    incmd = 0;
+	    ops = clwords[br - 1];
+	    clwords[br - 1] = ztrdup(cc->subcmd);
+	    cmdstr = ztrdup(cc->subcmd);
+	    clwords += br - 1;
+	} else {
+	    cmdstr = ztrdup(clwords[br]);
+	    incmd = !clwpos;
+	    clwords += br;
+	}
+	/* Produce the matches. */
+	makecomplistcmd(s, incmd, CFN_FIRST);
+
+	/* And restore the things we changed. */
+	clwords = ow;
+	zsfree(cmdstr);
+	cmdstr = os;
+	clwnum = oldn;
+	clwpos = oldp;
+	if (ops) {
+	    zsfree(clwords[br - 1]);
+	    clwords[br - 1] = ops;
+	}
+	ccont = occ;
+    }
+    if (cc->substr)
+	sep_comp_string(cc->substr, s, offs);
+    uremnode(ccstack, firstnode(ccstack));
+    if (cc->matcher)
+	mstack = mstack->next;
+
+    if (mn == mnum)
+	haspattern = ohp;
 }
 
+
 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_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),
-};
-
-static struct funcwrap wrapper[] = {
-    WRAPDEF(comp_wrapper),
-};
-
-static struct paramdef patab[] = {
-    PARAMDEF("compmatchers", PM_ARRAY|PM_SPECIAL, NULL, cmsetfn, cmgetfn, cmunsetfn)
-};
-
 /**/
 int
 setup_compctl(Module m)
 {
-    compctltab->printnode = printcompctlp;
-    makecompparamsptr = makecompparams;
-    comp_setunsetptr = comp_setunset;
+    compctlreadptr = compctlread;
+    createcompctltable();
+    cc_compos.mask = CC_COMMPATH;
+    cc_compos.mask2 = 0;
+    cc_default.refc = 10000;
+    cc_default.mask = CC_FILES;
+    cc_default.mask2 = 0;
+    cc_first.refc = 10000;
+    cc_first.mask = 0;
+    cc_first.mask2 = CC_CCCONT;
+
+    lastccused = NULL;
+
     return 0;
 }
 
@@ -2672,12 +3723,10 @@ setup_compctl(Module m)
 int
 boot_compctl(Module m)
 {
-    if(!(addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) |
-	 addconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab)) |
-	 addparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab)) |
-	 !addwrapper(m, wrapper)))
-	return 1;
-    return 0;
+    addhookfunc("compctl_make", (Hookfn) ccmakehookfn);
+    addhookfunc("compctl_before", (Hookfn) ccbeforehookfn);
+    addhookfunc("compctl_after", (Hookfn) ccafterhookfn);
+    return (addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) != 1);
 }
 
 #ifdef MODULE
@@ -2686,10 +3735,10 @@ boot_compctl(Module m)
 int
 cleanup_compctl(Module m)
 {
+    deletehookfunc("compctl_make", (Hookfn) ccmakehookfn);
+    deletehookfunc("compctl_before", (Hookfn) ccbeforehookfn);
+    deletehookfunc("compctl_after", (Hookfn) ccafterhookfn);
     deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
-    deleteconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab));
-    deleteparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab));
-    deletewrapper(m, wrapper);
     return 0;
 }
 
@@ -2697,9 +3746,12 @@ cleanup_compctl(Module m)
 int
 finish_compctl(Module m)
 {
-    compctltab->printnode = NULL;
-    makecompparamsptr = NULL;
-    comp_setunsetptr = NULL;
+    deletehashtable(compctltab);
+
+    if (lastccused)
+	freelinklist(lastccused, (FreeFunc) freecompctl);
+
+    compctlreadptr = fallback_compctlread;
     return 0;
 }
 
diff --git a/Src/Zle/compctl.h b/Src/Zle/compctl.h
new file mode 100644
index 000000000..9a8ba5692
--- /dev/null
+++ b/Src/Zle/compctl.h
@@ -0,0 +1,160 @@
+/*
+ * comp.h - header file for completion
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Paul Falstad or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose.  The software
+ * provided hereunder is on an "as is" basis, and Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#undef compctlread
+
+typedef struct compctlp  *Compctlp;
+typedef struct compctl   *Compctl;
+typedef struct compcond  *Compcond;
+typedef struct patcomp   *Patcomp;
+
+/* node for compctl hash table (compctltab) */
+
+struct compctlp {
+    HashNode next;		/* next in hash chain               */
+    char *nam;			/* command name                     */
+    int flags;			/* CURRENTLY UNUSED                 */
+    Compctl cc;			/* pointer to the compctl desc.     */
+};
+
+/* for the list of pattern compctls */
+
+struct patcomp {
+    Patcomp next;
+    char *pat;
+    Compctl cc;
+};
+
+/* compctl -x condition */
+
+struct compcond {
+    Compcond and, or;		/* the next or'ed/and'ed conditions    */
+    int type;			/* the type (CCT_*)                    */
+    int n;			/* the array length                    */
+    union {			/* these structs hold the data used to */
+	struct {		/* test this condition                 */
+	    int *a, *b;		/* CCT_POS, CCT_NUMWORDS               */
+	}
+	r;
+	struct {		/* CCT_CURSTR, CCT_CURPAT,... */
+	    int *p;
+	    char **s;
+	}
+	s;
+	struct {		/* CCT_RANGESTR,... */
+	    char **a, **b;
+	}
+	l;
+    }
+    u;
+};
+
+#define CCT_UNUSED     0
+#define CCT_POS        1
+#define CCT_CURSTR     2
+#define CCT_CURPAT     3
+#define CCT_WORDSTR    4
+#define CCT_WORDPAT    5
+#define CCT_CURSUF     6
+#define CCT_CURPRE     7
+#define CCT_CURSUB     8
+#define CCT_CURSUBC    9
+#define CCT_NUMWORDS  10
+#define CCT_RANGESTR  11
+#define CCT_RANGEPAT  12
+#define CCT_QUOTE     13
+
+/* Contains the real description for compctls */
+
+struct compctl {
+    int refc;			/* reference count                         */
+    Compctl next;		/* next compctl for -x                     */
+    unsigned long mask, mask2;	/* masks of things to complete (CC_*)      */
+    char *keyvar;		/* for -k (variable)                       */
+    char *glob;			/* for -g (globbing)                       */
+    char *str;			/* for -s (expansion)                      */
+    char *func;			/* for -K (function)                       */
+    char *explain;		/* for -X (explanation)                    */
+    char *ylist;		/* for -y (user-defined desc. for listing) */
+    char *prefix, *suffix;	/* for -P and -S (prefix, suffix)          */
+    char *subcmd;		/* for -l (command name to use)            */
+    char *substr;		/* for -1 (command name to use)            */
+    char *withd;		/* for -w (with directory                  */
+    char *hpat;			/* for -H (history pattern)                */
+    int hnum;			/* for -H (number of events to search)     */
+    char *gname;		/* for -J and -V (group name)              */
+    Compctl ext;		/* for -x (first of the compctls after -x) */
+    Compcond cond;		/* for -x (condition for this compctl)     */
+    Compctl xor;		/* for + (next of the xor'ed compctls)     */
+    Cmatcher matcher;		/* matcher control (-M) */
+    char *mstr;			/* matcher string */
+};
+
+/* objects to complete (mask) */
+#define CC_FILES	(1<<0)
+#define CC_COMMPATH	(1<<1)
+#define CC_REMOVE	(1<<2)
+#define CC_OPTIONS	(1<<3)
+#define CC_VARS		(1<<4)
+#define CC_BINDINGS	(1<<5)
+#define CC_ARRAYS	(1<<6)
+#define CC_INTVARS	(1<<7)
+#define CC_SHFUNCS	(1<<8)
+#define CC_PARAMS	(1<<9)
+#define CC_ENVVARS	(1<<10)
+#define CC_JOBS		(1<<11)
+#define CC_RUNNING	(1<<12)
+#define CC_STOPPED	(1<<13)
+#define CC_BUILTINS	(1<<14)
+#define CC_ALREG	(1<<15)
+#define CC_ALGLOB	(1<<16)
+#define CC_USERS	(1<<17)
+#define CC_DISCMDS	(1<<18)
+#define CC_EXCMDS	(1<<19)
+#define CC_SCALARS	(1<<20)
+#define CC_READONLYS	(1<<21)
+#define CC_SPECIALS	(1<<22)
+#define CC_DELETE	(1<<23)
+#define CC_NAMED	(1<<24)
+#define CC_QUOTEFLAG	(1<<25)
+#define CC_EXTCMDS	(1<<26)
+#define CC_RESWDS	(1<<27)
+#define CC_DIRS		(1<<28)
+
+#define CC_EXPANDEXPL	(1<<30)
+#define CC_RESERVED	(1<<31)
+
+/* objects to complete (mask2) */
+#define CC_NOSORT	(1<<0)
+#define CC_XORCONT	(1<<1)
+#define CC_CCCONT	(1<<2)
+#define CC_PATCONT	(1<<3)
+#define CC_DEFCONT	(1<<4)
+#define CC_UNIQCON      (1<<5)
+#define CC_UNIQALL      (1<<6)
diff --git a/Src/Zle/compctl.mdd b/Src/Zle/compctl.mdd
index c52285c72..1bebb9338 100644
--- a/Src/Zle/compctl.mdd
+++ b/Src/Zle/compctl.mdd
@@ -1,9 +1,6 @@
-moddeps="comp1"
+moddeps="complete"
 
-autobins="compctl compgen compadd compset"
-
-autoprefixconds="prefix suffix between after"
-
-autoparams="compmatchers"
+autobins="compctl"
 
+headers="compctl.h"
 objects="compctl.o"
diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c
new file mode 100644
index 000000000..283b8de62
--- /dev/null
+++ b/Src/Zle/complete.c
@@ -0,0 +1,1332 @@
+/*
+ * complete.c - the complete module
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1999 Sven Wischnowsky
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Sven Wischnowsky or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Sven Wischnowsky and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Sven Wischnowsky and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose.  The software
+ * provided hereunder is on an "as is" basis, and Sven Wischnowsky and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "complete.mdh"
+#include "complete.pro"
+#define GLOBAL_PROTOTYPES
+#include "zle_tricky.pro"
+#undef GLOBAL_PROTOTYPES
+
+/**/
+void
+freecmlist(Cmlist l)
+{
+    Cmlist n;
+
+    while (l) {
+	n = l->next;
+	freecmatcher(l->matcher);
+	zsfree(l->str);
+
+	zfree(l, sizeof(struct cmlist));
+
+	l = n;
+    }
+}
+
+/**/
+void
+freecmatcher(Cmatcher m)
+{
+    Cmatcher n;
+
+    if (!m || --(m->refc))
+	return;
+
+    while (m) {
+	n = m->next;
+	freecpattern(m->line);
+	freecpattern(m->word);
+	freecpattern(m->left);
+	freecpattern(m->right);
+
+	zfree(m, sizeof(struct cmatcher));
+
+	m = n;
+    }
+}
+
+/**/
+void
+freecpattern(Cpattern p)
+{
+    Cpattern n;
+
+    while (p) {
+	n = p->next;
+	zfree(p, sizeof(struct cpattern));
+
+	p = n;
+    }
+}
+
+/* Copy a list of completion matchers. */
+
+static Cmlist
+cpcmlist(Cmlist l)
+{
+    Cmlist r = NULL, *p = &r, n;
+
+    while (l) {
+	*p = n = (Cmlist) zalloc(sizeof(struct cmlist));
+	n->next = NULL;
+	n->matcher = cpcmatcher(l->matcher);
+	n->str = ztrdup(l->str);
+
+	p = &(n->next);
+	l = l->next;
+    }
+    return r;
+}
+
+/* Copy a completion matcher list. */
+
+/**/
+Cmatcher
+cpcmatcher(Cmatcher m)
+{
+    Cmatcher r = NULL, *p = &r, n;
+
+    while (m) {
+	*p = n = (Cmatcher) zalloc(sizeof(struct cmatcher));
+
+	n->refc = 1;
+	n->next = NULL;
+	n->flags = m->flags;
+	n->line = cpcpattern(m->line);
+	n->llen = m->llen;
+	n->word = cpcpattern(m->word);
+	n->wlen = m->wlen;
+	n->left = cpcpattern(m->left);
+	n->lalen = m->lalen;
+	n->right = cpcpattern(m->right);
+	n->ralen = m->ralen;
+
+	p = &(n->next);
+	m = m->next;
+    }
+    return r;
+}
+
+/* Copy a completion matcher pattern. */
+
+/**/
+static Cpattern
+cpcpattern(Cpattern o)
+{
+    Cpattern r = NULL, *p = &r, n;
+
+    while (o) {
+	*p = n = (Cpattern) zalloc(sizeof(struct cpattern));
+
+	n->next = NULL;
+	memcpy(n->tab, o->tab, 256);
+	n->equiv = o->equiv;
+
+	p = &(n->next);
+	o = o->next;
+    }
+    return r;
+}
+
+/* Set the global match specs. */
+
+/**/
+int
+set_gmatcher(char *name, char **argv)
+{
+    Cmlist l = NULL, *q = &l, n;
+    Cmatcher m;
+
+    while (*argv) {
+	if ((m = parse_cmatcher(name, *argv)) == pcm_err)
+	    return 1;
+	*q = n = (Cmlist) zhalloc(sizeof(struct cmlist));
+	n->next = NULL;
+	n->matcher = m;
+	n->str = *argv++;
+
+	q = &(n->next);
+    }
+    freecmlist(cmatcher);
+    PERMALLOC {
+	cmatcher = cpcmlist(l);
+    } LASTALLOC;
+
+    return 1;
+}
+
+/* Parse a string for matcher control, containing multiple matchers. */
+
+/**/
+Cmatcher
+parse_cmatcher(char *name, char *s)
+{
+    Cmatcher ret = NULL, r = NULL, n;
+    Cpattern line, word, left, right;
+    int fl, ll, wl, lal, ral, err;
+
+    if (!*s)
+	return NULL;
+
+    while (*s) {
+	while (*s && inblank(*s)) s++;
+
+	if (!*s) break;
+
+	switch (*s) {
+	case 'l': fl = CMF_LEFT; break;
+	case 'r': fl = CMF_RIGHT; break;
+	case 'm': fl = 0; break;
+	case 'L': fl = CMF_LEFT | CMF_LINE; break;
+	case 'R': fl = CMF_RIGHT | CMF_LINE; break;
+	case 'M': fl = CMF_LINE; break;
+	default:
+	    zwarnnam(name, "unknown match specification character `%c'", NULL, *s);
+	    return pcm_err;
+	}
+	if (s[1] != ':') {
+	    zwarnnam(name, "missing `:'", NULL, 0);
+	    return pcm_err;
+	}
+	s += 2;
+	if (!*s) {
+	    zwarnnam(name, "missing patterns", NULL, 0);
+	    return pcm_err;
+	}
+	if (fl & CMF_LEFT) {
+	    left = parse_pattern(name, &s, &lal, '|', &err);
+	    if (err)
+		return pcm_err;
+	    if (!*s || !*++s) {
+		zwarnnam(name, "missing line pattern", NULL, 0);
+		return pcm_err;
+	    }
+	} else
+	    left = NULL;
+
+	line = parse_pattern(name, &s, &ll, ((fl & CMF_RIGHT) ? '|' : '='),
+			     &err);
+	if (err)
+	    return pcm_err;
+	if ((fl & CMF_RIGHT) && (!*s || !*++s)) {
+	    zwarnnam(name, "missing right anchor", NULL, 0);
+	} else if (!(fl & CMF_RIGHT)) {
+	    if (!*s) {
+		zwarnnam(name, "missing word pattern", NULL, 0);
+		return pcm_err;
+	    }
+	    s++;
+	}
+	if (fl & CMF_RIGHT) {
+	    right = parse_pattern(name, &s, &ral, '=', &err);
+	    if (err)
+		return pcm_err;
+	    if (!*s) {
+		zwarnnam(name, "missing word pattern", NULL, 0);
+		return pcm_err;
+	    }
+	    s++;
+	} else
+	    right = NULL;
+
+	if (*s == '*') {
+	    if (!(fl & (CMF_LEFT | CMF_RIGHT))) {
+		zwarnnam(name, "need anchor for `*'", NULL, 0);
+		return pcm_err;
+	    }
+	    word = NULL;
+	    wl = -1;
+	    s++;
+	} else {
+	    word = parse_pattern(name, &s, &wl, 0, &err);
+
+	    if (!word && !line) {
+		zwarnnam(name, "need non-empty word or line pattern", NULL, 0);
+		return pcm_err;
+	    }
+	}
+	if (err)
+	    return pcm_err;
+
+	n = (Cmatcher) zcalloc(sizeof(*ret));
+	n->next = NULL;
+	n->flags = fl;
+	n->line = line;
+	n->llen = ll;
+	n->word = word;
+	n->wlen = wl;
+	n->left = left;
+	n->lalen = lal;
+	n->right = right;
+	n->ralen = ral;
+
+	if (ret)
+	    r->next = n;
+	else
+	    ret = n;
+
+	r = n;
+    }
+    return ret;
+}
+
+/* Parse a pattern for matcher control. */
+
+/**/
+static Cpattern
+parse_pattern(char *name, char **sp, int *lp, char e, int *err)
+{
+    Cpattern ret = NULL, r = NULL, n;
+    unsigned char *s = (unsigned char *) *sp;
+    int l = 0;
+
+    *err = 0;
+
+    while (*s && (e ? (*s != e) : !inblank(*s))) {
+	n = (Cpattern) hcalloc(sizeof(*n));
+	n->next = NULL;
+	n->equiv = 0;
+
+	if (*s == '[') {
+	    s = parse_class(n, s + 1, ']');
+	    if (!*s) {
+		*err = 1;
+		zwarnnam(name, "unterminated character class", NULL, 0);
+		return NULL;
+	    }
+	} else if (*s == '{') {
+	    n->equiv = 1;
+	    s = parse_class(n, s + 1, '}');
+	    if (!*s) {
+		*err = 1;
+		zwarnnam(name, "unterminated character class", NULL, 0);
+		return NULL;
+	    }
+	} else if (*s == '?') {
+	    memset(n->tab, 1, 256);
+	} else if (*s == '*' || *s == '(' || *s == ')' || *s == '=') {
+	    *err = 1;
+	    zwarnnam(name, "invalid pattern character `%c'", NULL, *s);
+	    return NULL;
+	} else {
+	    if (*s == '\\' && s[1])
+		s++;
+
+	    memset(n->tab, 0, 256);
+	    n->tab[*s] = 1;
+	}
+	if (ret)
+	    r->next = n;
+	else
+	    ret = n;
+
+	r = n;
+
+	l++;
+	s++;
+    }
+    *sp = (char *) s;
+    *lp = l;
+    return ret;
+}
+
+/* Parse a character class for matcher control. */
+
+/**/
+static unsigned char *
+parse_class(Cpattern p, unsigned char *s, unsigned char e)
+{
+    int n = 0, i = 1, j, eq = (e == '}'), k = 1;
+
+    if (!eq && (*s == '!' || *s == '^') && s[1] != e) { n = 1; s++; }
+
+    memset(p->tab, n, 256);
+
+    n = !n;
+    while (*s && (k || *s != e)) {
+	if (s[1] == '-' && s[2] != e) {
+	    /* a run of characters */
+	    for (j = (int) *s; j <= (int) s[2]; j++)
+		p->tab[j] = (eq ? i++ : n);
+
+	    s += 3;
+	} else
+	    p->tab[*s++] = (eq ? i++ : n);
+	k = 0;
+    }
+    return s;
+}
+
+/**/
+static int
+bin_compadd(char *name, char **argv, char *ops, int func)
+{
+    struct cadata dat;
+    char *p, **sp, *e, *m = NULL;
+    int dm;
+    Cmatcher match = NULL;
+
+    if (incompfunc != 1) {
+	zerrnam(name, "can only be called from completion function", NULL, 0);
+	return 1;
+    }
+    dat.ipre = dat.isuf = dat.ppre = dat.psuf = dat.prpre =
+	dat.pre = dat.suf = dat.group = dat.rems = dat.remf = dat.disp = 
+	dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = dat.ylist = NULL;
+    dat.match = NULL;
+    dat.flags = 0;
+    dat.aflags = CAF_MATCH;
+
+    for (; *argv && **argv ==  '-'; argv++) {
+	if (!(*argv)[1]) {
+	    argv++;
+	    break;
+	}
+	for (p = *argv + 1; *p; p++) {
+	    sp = NULL;
+	    e = NULL;
+	    dm = 0;
+	    switch (*p) {
+	    case 'q':
+		dat.flags |= CMF_REMOVE;
+		break;
+	    case 'Q':
+		dat.aflags |= CAF_QUOTE;
+		break;
+	    case 'f':
+		dat.flags |= CMF_FILE;
+		break;
+	    case 'e':
+		dat.flags |= CMF_ISPAR;
+		break;
+	    case 'F':
+		sp = &(dat.ign);
+		e = "string expected after -%c";
+		break;
+	    case 'n':
+		dat.flags |= CMF_NOLIST;
+		break;
+	    case 'U':
+		dat.aflags &= ~CAF_MATCH;
+		break;
+	    case 'P':
+		sp = &(dat.pre);
+		e = "string expected after -%c";
+		break;
+	    case 'S':
+		sp = &(dat.suf);
+		e = "string expected after -%c";
+		break;
+	    case 'J':
+		sp = &(dat.group);
+		e = "group name expected after -%c";
+		break;
+	    case 'V':
+		if (!dat.group)
+		    dat.aflags |= CAF_NOSORT;
+		sp = &(dat.group);
+		e = "group name expected after -%c";
+		break;
+	    case '1':
+		if (!(dat.aflags & CAF_UNIQCON))
+		    dat.aflags |= CAF_UNIQALL;
+		break;
+	    case '2':
+		if (!(dat.aflags & CAF_UNIQALL))
+		    dat.aflags |= CAF_UNIQCON;
+		break;
+	    case 'y':
+		sp = &(dat.ylist);
+		e = "string expected after -%c";
+		break;
+	    case 'i':
+		sp = &(dat.ipre);
+		e = "string expected after -%c";
+		break;
+	    case 'I':
+		sp = &(dat.isuf);
+		e = "string expected after -%c";
+		break;
+	    case 'p':
+		sp = &(dat.ppre);
+		e = "string expected after -%c";
+		break;
+	    case 's':
+		sp = &(dat.psuf);
+		e = "string expected after -%c";
+		break;
+	    case 'W':
+		sp = &(dat.prpre);
+		e = "string expected after -%c";
+		break;
+	    case 'a':
+		dat.aflags |= CAF_ALT;
+		break;
+	    case 'M':
+		sp = &m;
+		e = "matching specification expected after -%c";
+		dm = 1;
+		break;
+	    case 'X':
+		sp = &(dat.exp);
+		e = "string expected after -%c";
+		break;
+	    case 'r':
+		dat.flags |= CMF_REMOVE;
+		sp = &(dat.rems);
+		e = "string expected after -%c";
+		break;
+	    case 'R':
+		dat.flags |= CMF_REMOVE;
+		sp = &(dat.remf);
+		e = "function name expected after -%c";
+		break;
+	    case 'A':
+		sp = &(dat.apar);
+		e = "parameter name expected after -%c";
+		break;
+	    case 'O':
+		sp = &(dat.opar);
+		e = "parameter name expected after -%c";
+		break;
+	    case 'D':
+		sp = &(dat.dpar);
+		e = "parameter name expected after -%c";
+		break;
+	    case 'd':
+		sp = &(dat.disp);
+		e = "parameter name expected after -%c";
+		break;
+	    case 'l':
+		dat.flags |= CMF_DISPLINE;
+		break;
+	    case '-':
+		argv++;
+		goto ca_args;
+	    default:
+		zerrnam(name, "bad option: -%c", NULL, *p);
+		return 1;
+	    }
+	    if (sp) {
+		if (p[1]) {
+		    if (!*sp)
+			*sp = p + 1;
+		    p = "" - 1;
+		} else if (argv[1]) {
+		    argv++;
+		    if (!*sp)
+			*sp = *argv;
+		    p = "" - 1;
+		} else {
+		    zerrnam(name, e, NULL, *p);
+		    return 1;
+		}
+		if (dm && (match = parse_cmatcher(name, m)) == pcm_err) {
+		    match = NULL;
+		    return 1;
+		}
+	    }
+	}
+    }
+ ca_args:
+    if (!*argv)
+	return 1;
+
+    dat.match = match = cpcmatcher(match);
+    dm = addmatches(&dat, argv);
+    freecmatcher(match);
+
+    return dm;
+}
+
+#define CVT_RANGENUM 0
+#define CVT_RANGEPAT 1
+#define CVT_PRENUM   2
+#define CVT_PREPAT   3
+#define CVT_SUFNUM   4
+#define CVT_SUFPAT   5
+
+/**/
+void
+ignore_prefix(int l)
+{
+    if (l) {
+	char *tmp, sav;
+	int pl = strlen(compprefix);
+
+	if (l > pl)
+	    l = pl;
+
+	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;
+    }
+}
+
+/**/
+void
+ignore_suffix(int l)
+{
+    if (l) {
+	char *tmp, sav;
+	int sl = strlen(compsuffix);
+
+	if ((l = sl - l) < 0)
+	    l = 0;
+
+	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;
+    }
+}
+
+/**/
+void
+restrict_range(int b, int e)
+{
+    int wl = arrlen(compwords) - 1;
+
+    if (wl && b >= 0 && e >= 0 && (b > 0 || e < wl)) {
+	int i;
+	char **p, **q, **pp;
+
+	if (e > wl)
+	    e = wl;
+
+	i = e - b + 1;
+	p = (char **) zcalloc((i + 1) * sizeof(char *));
+
+	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;
+	    if (mod)
+		restrict_range(na, nb);
+	    return 1;
+	}
+    case CVT_RANGEPAT:
+	{
+	    char **p;
+	    int i, l = arrlen(compwords), t = 0, b = 0, e = l - 1;
+	    Patprog pp;
+
+	    i = compcurrent - 1;
+	    if (i < 0 || i >= l)
+		return 0;
+
+	    singsub(&sa);
+	    pp = patcompile(sa, PAT_STATIC, NULL);
+
+	    for (i--, p = compwords + i; i >= 0; p--, i--) {
+		if (pattry(pp, *p)) {
+		    b = i + 1;
+		    t = 1;
+		    break;
+		}
+	    }
+	    if (t && sb) {
+		int tt = 0;
+
+		singsub(&sb);
+		pp = patcompile(sb, PAT_STATIC, NULL);
+
+		for (i++, p = compwords + i; i < l; p++, i++) {
+		    if (pattry(pp, *p)) {
+			e = i - 1;
+			tt = 1;
+			break;
+		    }
+		}
+		if (tt && i < compcurrent)
+		    t = 0;
+	    }
+	    if (e < b)
+		t = 0;
+	    if (t && mod)
+		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:
+	{
+	    Patprog pp;
+
+	    if (!na)
+		return 0;
+
+	    if (!(pp = patcompile(sa, PAT_STATIC, 0)))
+		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 = pattry(pp, compprefix);
+		    *p = sav;
+		    if (test && !--na)
+			break;
+		}
+		if (!l)
+		    return 0;
+		if (mod)
+		    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 (pattry(pp, p) && !--na)
+			break;
+
+		if (!l)
+		    return 0;
+		if (mod)
+		    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;
+    case 'q': return set_comp_sep();
+    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);
+	remnulargs(sa);
+	if (sb) {
+	    tokenize(sb);
+	    sb = rembslash(sb);
+	    remnulargs(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);
+	remnulargs(sa);
+	break;
+    }
+    return !do_comp_vars(test, na, sa, nb, sb, 1);
+}
+
+/* Definitions for the special parameters. Note that these have to match the
+ * order of the CP_* bits in comp.h */
+
+#define VAL(X) ((void *) (&(X)))
+struct compparam {
+    char *name;
+    int type;
+    void *var, *set, *get;
+};
+
+static struct compparam comprparams[] = {
+    { "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 },
+    { "QIPREFIX", PM_SCALAR | PM_READONLY, VAL(compqiprefix), NULL, NULL },
+    { "QISUFFIX", PM_SCALAR | PM_READONLY, VAL(compqisuffix), NULL, NULL },
+    { NULL, 0, NULL, NULL, NULL }
+};
+
+static struct compparam compkparams[] = {
+    { "nmatches", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_nmatches) },
+    { "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 | PM_READONLY, VAL(compquote), NULL, NULL },
+    { "quoting", PM_SCALAR | PM_READONLY, VAL(compquoting), NULL, NULL },
+    { "restore", PM_SCALAR, VAL(comprestore), NULL, NULL },
+    { "list", PM_SCALAR, NULL, VAL(set_complist), VAL(get_complist) },
+    { "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) },
+    { "list_max", PM_INTEGER, VAL(complistmax), NULL, NULL },
+    { "last_prompt", PM_SCALAR, VAL(complastprompt), NULL, NULL },
+    { "to_end", PM_SCALAR, VAL(comptoend), NULL, NULL },
+    { "old_list", PM_SCALAR, VAL(compoldlist), NULL, NULL },
+    { "old_insert", PM_SCALAR, VAL(compoldins), NULL, NULL },
+    { "vared", PM_SCALAR, VAL(compvared), NULL, NULL },
+    { "alternate_nmatches", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_anmatches) },
+    { "list_lines", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_listlines) },
+    { NULL, 0, NULL, NULL, NULL }
+};
+
+#define COMPSTATENAME "compstate"
+
+static void
+addcompparams(struct compparam *cp, Param *pp)
+{
+    for (; cp->name; cp++, pp++) {
+	Param pm = createparam(cp->name,
+			       cp->type |PM_SPECIAL|PM_REMOVABLE|PM_LOCAL);
+	if (!pm)
+	    pm = (Param) paramtab->getnode(paramtab, cp->name);
+	DPUTS(!pm, "param not set in addcompparams");
+
+	*pp = pm;
+	pm->level = locallevel + 1;
+	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;
+    }
+}
+
+/**/
+void
+makecompparams(void)
+{
+    Param cpm;
+    HashTable tht;
+
+    addcompparams(comprparams, comprpms);
+
+    if (!(cpm = createparam(COMPSTATENAME,
+			    PM_SPECIAL|PM_REMOVABLE|PM_LOCAL|PM_HASHED)))
+	cpm = (Param) paramtab->getnode(paramtab, COMPSTATENAME);
+    DPUTS(!cpm, "param not set in makecompparams");
+
+    comprpms[CPN_COMPSTATE] = cpm;
+    tht = paramtab;
+    cpm->level = locallevel;
+    cpm->gets.hfn = get_compstate;
+    cpm->sets.hfn = set_compstate;
+    cpm->unsetfn = compunsetfn;
+    cpm->u.hash = paramtab = newparamtable(31, COMPSTATENAME);
+    addcompparams(compkparams, compkpms);
+    paramtab = tht;
+}
+
+/**/
+static HashTable
+get_compstate(Param pm)
+{
+    return pm->u.hash;
+}
+
+/**/
+static void
+set_compstate(Param pm, HashTable ht)
+{
+    struct compparam *cp;
+    Param *pp;
+    HashNode hn;
+    int i;
+    struct value v;
+    char *str;
+
+    for (i = 0; i < ht->hsize; i++)
+	for (hn = ht->nodes[i]; hn; hn = hn->next)
+	    for (cp = compkparams,
+		 pp = compkpms; cp->name; cp++, pp++)
+		if (!strcmp(hn->nam, cp->name)) {
+		    v.isarr = v.inv = v.a = 0;
+		    v.b = -1;
+		    v.arr = NULL;
+		    v.pm = (Param) hn;
+		    if (cp->type == PM_INTEGER)
+			*((zlong *) cp->var) = getintvalue(&v);
+		    else if ((str = getstrvalue(&v))) {
+			zsfree(*((char **) cp->var));
+			*((char **) cp->var) = ztrdup(str);
+		    }
+		    (*pp)->flags &= ~PM_UNSET;
+
+		    break;
+		}
+    deleteparamtable(ht);
+}
+
+/**/
+static zlong
+get_nmatches(Param pm)
+{
+    return num_matches(1);
+}
+
+/**/
+static zlong
+get_anmatches(Param pm)
+{
+    return num_matches(0);
+}
+
+/**/
+static zlong
+get_listlines(Param pm)
+{
+    return list_lines();
+}
+
+/**/
+static void
+set_complist(Param pm, char *v)
+{
+    comp_list(v);
+}
+
+/**/
+static char *
+get_complist(Param pm)
+{
+    return complist;
+}
+
+/**/
+static char *
+get_unambig(Param pm)
+{
+    return unambig_data(NULL);
+}
+
+/**/
+static zlong
+get_unambig_curs(Param pm)
+{
+    int c;
+
+    unambig_data(&c);
+
+    return c;
+}
+
+/**/
+static void
+compunsetfn(Param pm, int exp)
+{
+    if (exp) {
+	if (PM_TYPE(pm->flags) == PM_SCALAR) {
+	    zsfree(*((char **) pm->u.data));
+	    *((char **) pm->u.data) = ztrdup("");
+	} else if (PM_TYPE(pm->flags) == PM_ARRAY) {
+	    freearray(*((char ***) pm->u.data));
+	    *((char ***) pm->u.data) = zcalloc(sizeof(char *));
+	}
+	pm->flags |= PM_UNSET;
+    }
+}
+
+/**/
+void
+comp_setunset(int rset, int runset, int kset, int kunset)
+{
+    Param *p;
+
+    if (comprpms && (rset >= 0 || runset >= 0)) {
+	for (p = comprpms; rset || runset; rset >>= 1, runset >>= 1, p++) {
+	    if (rset & 1)
+		(*p)->flags &= ~PM_UNSET;
+	    if (runset & 1)
+		(*p)->flags |= PM_UNSET;
+	}
+    }
+    if (comprpms && (kset >= 0 || kunset >= 0)) {
+	for (p = compkpms; kset || kunset; kset >>= 1, kunset >>= 1, p++) {
+	    if (kset & 1)
+		(*p)->flags &= ~PM_UNSET;
+	    if (kunset & 1)
+		(*p)->flags |= PM_UNSET;
+	}
+    }
+}
+
+/**/
+static int
+comp_wrapper(List list, FuncWrap w, char *name)
+{
+    if (incompfunc != 1)
+	return 1;
+    else {
+	char *orest, *opre, *osuf, *oipre, *oisuf, **owords;
+	char *oqipre, *oqisuf, *oq, *oqi;
+	zlong ocur;
+	unsigned int runset = 0, kunset = 0, m, sm;
+	Param *pp;
+
+	m = CP_WORDS | CP_CURRENT | CP_PREFIX | CP_SUFFIX | 
+	    CP_IPREFIX | CP_ISUFFIX | CP_QIPREFIX | CP_QISUFFIX;
+	for (pp = comprpms, sm = 1; m; pp++, m >>= 1, sm <<= 1) {
+	    if ((m & 1) && ((*pp)->flags & PM_UNSET))
+		runset |= sm;
+	}
+	if (compkpms[CPN_RESTORE]->flags & PM_UNSET)
+	    kunset = CP_RESTORE;
+	orest = comprestore;
+	comprestore = ztrdup("auto");
+	ocur = compcurrent;
+	opre = dupstring(compprefix);
+	osuf = dupstring(compsuffix);
+	oipre = dupstring(compiprefix);
+	oisuf = dupstring(compisuffix);
+	oqipre = dupstring(compqiprefix);
+	oqisuf = dupstring(compqisuffix);
+	oq = dupstring(compquote);
+	oqi = dupstring(compquoting);
+
+	HEAPALLOC {
+	    owords = arrdup(compwords);
+	} LASTALLOC;
+
+	runshfunc(list, w, name);
+
+	if (comprestore && !strcmp(comprestore, "auto")) {
+	    compcurrent = ocur;
+	    zsfree(compprefix);
+	    compprefix = ztrdup(opre);
+	    zsfree(compsuffix);
+	    compsuffix = ztrdup(osuf);
+	    zsfree(compiprefix);
+	    compiprefix = ztrdup(oipre);
+	    zsfree(compisuffix);
+	    compisuffix = ztrdup(oisuf);
+	    zsfree(compqiprefix);
+	    compqiprefix = ztrdup(oqipre);
+	    zsfree(compqisuffix);
+	    compqisuffix = ztrdup(oqisuf);
+	    zsfree(compquote);
+	    compquote = ztrdup(oq);
+	    zsfree(compquoting);
+	    compquoting = ztrdup(oqi);
+	    freearray(compwords);
+	    PERMALLOC {
+		compwords = arrdup(owords);
+	    } LASTALLOC;
+	    comp_setunset(CP_COMPSTATE |
+			  (~runset & (CP_WORDS | CP_CURRENT | CP_PREFIX |
+				     CP_SUFFIX | CP_IPREFIX | CP_ISUFFIX |
+				     CP_QIPREFIX | CP_QISUFFIX)),
+			  (runset & CP_ALLREALS),
+			  (~kunset & CP_RESTORE), (kunset & CP_ALLKEYS));
+	} else
+	    comp_setunset(CP_COMPSTATE, 0, (~kunset & CP_RESTORE),
+			  (kunset & CP_RESTORE));
+	zsfree(comprestore);
+	comprestore = orest;
+
+	return 0;
+    }
+}
+
+/**/
+static int
+comp_check(void)
+{
+    if (incompfunc != 1) {
+	zerr("condition can only be used in completion function", NULL, 0);
+	return 0;
+    }
+    return 1;
+}
+
+/**/
+static int
+cond_psfix(char **a, int id)
+{
+    if (comp_check()) {
+	if (a[1])
+	    return do_comp_vars(id, cond_val(a, 0), cond_str(a, 1, 1),
+				0, NULL, 0);
+	else
+	    return do_comp_vars(id, -1, cond_str(a, 0, 1), 0, NULL, 0);
+    }
+    return 0;
+}
+
+/**/
+static int
+cond_range(char **a, int id)
+{
+    return do_comp_vars(CVT_RANGEPAT, 0, cond_str(a, 0, 1), 0,
+			(id ? cond_str(a, 1, 1) : NULL), 0);
+}
+
+/**/
+static void
+cmsetfn(Param pm, char **v)
+{
+    set_gmatcher(pm->nam, v);
+}
+
+/**/
+static char **
+cmgetfn(Param pm)
+{
+    int num;
+    Cmlist p;
+    char **ret, **q;
+
+    for (num = 0, p = cmatcher; p; p = p->next, num++);
+
+    ret = (char **) zhalloc((num + 1) * sizeof(char *));
+
+    for (q = ret, p = cmatcher; p; p = p->next, q++)
+	*q = dupstring(p->str);
+    *q = NULL;
+
+    return ret;
+}
+
+/**/
+static void
+cmunsetfn(Param pm, int exp)
+{
+    char *dummy[1];
+
+    dummy[0] = NULL;
+    set_gmatcher(pm->nam, dummy);
+}
+
+static struct builtin bintab[] = {
+    BUILTIN("compadd", 0, bin_compadd, 0, -1, 0, NULL, NULL),
+    BUILTIN("compset", 0, bin_compset, 1, 3, 0, NULL, NULL),
+};
+
+static struct conddef cotab[] = {
+    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),
+};
+
+static struct funcwrap wrapper[] = {
+    WRAPDEF(comp_wrapper),
+};
+
+static struct paramdef patab[] = {
+    PARAMDEF("compmatchers", PM_ARRAY|PM_SPECIAL, NULL, cmsetfn, cmgetfn, cmunsetfn)
+};
+
+/**/
+int
+setup_complete(Module m)
+{
+    makecompparamsptr = makecompparams;
+    comp_setunsetptr = comp_setunset;
+
+    return 0;
+}
+
+/**/
+int
+boot_complete(Module m)
+{
+    if (!(addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) |
+	  addconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab)) |
+	  addparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab)) |
+	  !addwrapper(m, wrapper)))
+	return 1;
+    return 0;
+}
+
+#ifdef MODULE
+
+/**/
+int
+cleanup_complete(Module m)
+{
+    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    deleteconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab));
+    deleteparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab));
+    deletewrapper(m, wrapper);
+    return 0;
+}
+
+/**/
+int
+finish_complete(Module m)
+{
+    makecompparamsptr = NULL;
+    comp_setunsetptr = NULL;
+
+    return 0;
+}
+
+#endif
diff --git a/Src/Zle/complete.mdd b/Src/Zle/complete.mdd
new file mode 100644
index 000000000..628058e2a
--- /dev/null
+++ b/Src/Zle/complete.mdd
@@ -0,0 +1,11 @@
+hasexport=1
+
+moddeps="zle"
+
+autobins="compgen compadd compset"
+
+autoprefixconds="prefix suffix between after"
+
+autoparams="compmatchers"
+
+objects="complete.o"
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index 2ffde6b48..dc1037122 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -415,7 +415,7 @@ clprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width,
 	    }
 	    putc(file_type(buf->st_mode), shout);
 	    len++;
-	}
+        }
 	if ((len = width - len - 2) > 0) {
 	    if (m->gnum != mselect) {
 		zcoff();
diff --git a/Src/Zle/complist.mdd b/Src/Zle/complist.mdd
index 8ea60b0a8..16cd5f1af 100644
--- a/Src/Zle/complist.mdd
+++ b/Src/Zle/complist.mdd
@@ -1,3 +1,3 @@
-moddeps="comp1 zle"
+moddeps="complete"
 
 objects="complist.o"
diff --git a/Src/Zle/computil.mdd b/Src/Zle/computil.mdd
index e4193610c..56f633414 100644
--- a/Src/Zle/computil.mdd
+++ b/Src/Zle/computil.mdd
@@ -1,4 +1,4 @@
-moddeps="compctl zle"
+moddeps="complete"
 
 objects="computil.o"
 
diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h
index ff515e7c4..470aa890f 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -146,6 +146,17 @@ typedef struct cutbuffer *Cutbuffer;
 
 /* Convenience macros for the hooks */
 
-#define LISTMATCHESHOOK (zlehooks + 0)
-#define INSERTMATCHHOOK (zlehooks + 1)
-#define MENUSTARTHOOK   (zlehooks + 2)
+#define LISTMATCHESHOOK   (zlehooks + 0)
+#define INSERTMATCHHOOK   (zlehooks + 1)
+#define MENUSTARTHOOK     (zlehooks + 2)
+#define COMPCTLMAKEHOOK   (zlehooks + 3)
+#define COMPCTLBEFOREHOOK (zlehooks + 4)
+#define COMPCTLAFTERHOOK  (zlehooks + 5)
+
+/* compctl hook data structs */
+
+struct ccmakedat {
+    char *str;
+    int incmd;
+    int lst;
+};
diff --git a/Src/Zle/zle.mdd b/Src/Zle/zle.mdd
index fec2e213b..95702e948 100644
--- a/Src/Zle/zle.mdd
+++ b/Src/Zle/zle.mdd
@@ -1,5 +1,75 @@
 hasexport=1
 
+autobins="bindkey vared zle"
+
+objects="zle_bindings.o zle_hist.o zle_keymap.o zle_main.o \
+zle_misc.o zle_move.o zle_params.o zle_refresh.o \
+zle_thingy.o zle_tricky.o zle_utils.o zle_vi.o zle_word.o"
+
+headers="zle.h zle_things.h comp.h"
+
+:<<\Make
+zle_things.h: thingies.list zle_things.sed
+	( \
+	    echo '/** zle_things.h                              **/'; \
+	    echo '/** indices of and pointers to known thingies **/'; \
+	    echo; \
+	    echo 'enum {'; \
+	    sed -n -f $(sdir)/zle_things.sed < thingies.list; \
+	    echo '    ZLE_BUILTIN_THINGY_COUNT'; \
+	    echo '};'; \
+	) > $@
+
+zle_widget.h: widgets.list zle_widget.sed
+	( \
+	    echo '/** zle_widget.h                                **/'; \
+	    echo '/** indices of and pointers to internal widgets **/'; \
+	    echo; \
+	    echo 'enum {'; \
+	    sed -n -f $(sdir)/zle_widget.sed < widgets.list; \
+	    echo '    ZLE_BUILTIN_WIDGET_COUNT'; \
+	    echo '};'; \
+	) > $@
+
+thingies.list: iwidgets.list
+	( \
+	    echo '/** thingies.list                            **/'; \
+	    echo '/** thingy structures for the known thingies **/'; \
+	    echo; \
+	    echo '/* format: T("name", TH_FLAGS, w_widget, t_nextthingy) */'; \
+	    echo; \
+	    sed -e 's/#.*//; /^$$/d; s/" *,.*/"/' \
+		-e 's/^"/T("/; s/$$/, 0,/; h' \
+		-e 's/-//g; s/^.*"\(.*\)".*/w_\1, t_D\1)/' \
+		-e 'H; g; s/\n/ /' \
+		< $(sdir)/iwidgets.list; \
+	    sed -e 's/#.*//; /^$$/d; s/" *,.*/"/' \
+		-e 's/^"/T("./; s/$$/, TH_IMMORTAL,/; h' \
+		-e 's/-//g; s/^.*"\.\(.*\)".*/w_\1, t_\1)/' \
+		-e 'H; g; s/\n/ /' \
+		< $(sdir)/iwidgets.list; \
+	) > $@
+
+widgets.list: iwidgets.list
+	( \
+	    echo '/** widgets.list                               **/'; \
+	    echo '/** widget structures for the internal widgets **/'; \
+	    echo; \
+	    echo '/* format: W(ZLE_FLAGS, t_firstname, functionname) */'; \
+	    echo; \
+	    sed -e 's/#.*//; /^$$/d; s/-//g' \
+		-e 's/^"\(.*\)" *, *\([^ ]*\) *, *\(.*\)/W(\3, t_\1, \2)/' \
+		< $(sdir)/iwidgets.list; \
+	) > $@
+
+zle_bindings.o zle_bindings..o: zle_widget.h widgets.list thingies.list
+
+clean-here: clean.zle
+clean.zle:
+	rm -f zle_things.h zle_widget.h widgets.list thingies.list
+Make
+hasexport=1
+
 moddeps="comp1"
 
 autobins="bindkey vared zle"
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index 28bc96b64..37c32d581 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -104,7 +104,8 @@ char *curkeymapname;
 
 /* the hash table of keymap names */
 
-static HashTable keymapnamtab;
+/**/
+HashTable keymapnamtab;
 
 /* key sequence reading data */
 
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index d7ffbe259..56ecd02ba 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -30,6 +30,70 @@
 #include "zle.mdh"
 #include "zle_main.pro"
 
+/* Defined by the complete module, called in zle_tricky.c. */
+
+/**/
+void (*makecompparamsptr) _((void));
+
+/**/
+void (*comp_setunsetptr) _((int, int, int, int));
+
+/* != 0 if in a shell function called from completion, such that read -[cl]  *
+ * will work (i.e., the line is metafied, and the above word arrays are OK). */
+
+/**/
+int incompctlfunc;
+
+/* != 0 if we are in a new style completion function */
+
+/**/
+int incompfunc;
+
+/* Global matcher. */
+
+/**/
+Cmlist cmatcher;
+
+/* global variables for shell parameters in new style completion */
+
+/**/
+zlong compcurrent,
+      compmatcher,
+      compmatchertot,
+      complistmax,
+      complistlines;
+
+/**/
+char **compwords,
+     *compprefix,
+     *compsuffix,
+     *compiprefix,
+     *compisuffix,
+     *compqiprefix,
+     *compqisuffix,
+     *compmatcherstr,
+     *compcontext,
+     *compparameter,
+     *compredirect,
+     *compquote,
+     *compquoting,
+     *comprestore,
+     *complist,
+     *compforcelist,
+     *compinsert,
+     *compexact,
+     *compexactstr,
+     *comppatmatch,
+     *comppatinsert,
+     *complastprompt,
+     *comptoend,
+     *compoldlist,
+     *compoldins,
+     *compvared;
+
+/**/
+Param *comprpms, *compkpms;
+
 /* != 0 if we're done editing */
 
 /**/
@@ -961,6 +1025,9 @@ struct hookdef zlehooks[] = {
     HOOKDEF("list_matches", ilistmatches, 0),
     HOOKDEF("insert_match", NULL, HOOKF_ALL),
     HOOKDEF("menu_start", NULL, HOOKF_ALL),
+    HOOKDEF("compctl_make", NULL, 0),
+    HOOKDEF("compctl_before", NULL, 0),
+    HOOKDEF("compctl_after", NULL, 0),
 };
 
 /**/
@@ -974,17 +1041,6 @@ setup_zle(Module m)
     spaceinlineptr = spaceinline;
     zlereadptr = zleread;
 
-    addmatchesptr = addmatches;
-    comp_strptr = comp_str;
-    getcpatptr = getcpat;
-    makecomplistcallptr = makecomplistcall;
-    makecomplistctlptr = makecomplistctl;
-    num_matchesptr = num_matches;
-    list_linesptr = list_lines;
-    comp_listptr = comp_list;
-    unambig_dataptr = unambig_data;
-    set_comp_sepptr = set_comp_sep;
-
     getkeyptr = getkey;
 
     /* initialise the thingies */
@@ -1001,6 +1057,23 @@ setup_zle(Module m)
 
     varedarg = NULL;
 
+    incompfunc = incompctlfunc = 0;
+
+    comprpms = compkpms = NULL;
+    compwords = NULL;
+    compprefix = compsuffix = compiprefix = compisuffix = 
+	compqiprefix = compqisuffix = compmatcherstr = 
+	compcontext = compparameter = compredirect = compquote =
+	compquoting = comprestore = complist = compinsert =
+	compexact = compexactstr = comppatmatch = comppatinsert =
+	compforcelist = complastprompt = comptoend = 
+	compoldlist = compoldins = compvared = NULL;
+
+    clwords = (char **) zcalloc((clwsize = 16) * sizeof(char *));
+
+    makecompparamsptr = NULL;
+    comp_setunsetptr = NULL;
+
     return 0;
 }
 
@@ -1057,19 +1130,37 @@ finish_zle(Module m)
     spaceinlineptr = noop_function_int;
     zlereadptr = fallback_zleread;
 
-    addmatchesptr = NULL;
-    comp_strptr = NULL;
-    getcpatptr = NULL;
-    makecomplistcallptr = NULL;
-    makecomplistctlptr = NULL;
-    num_matchesptr = NULL;
-    list_linesptr = NULL;
-    comp_listptr = NULL;
-    unambig_dataptr = NULL;
-    set_comp_sepptr = NULL;
-
     getkeyptr = NULL;
 
+    freearray(compwords);
+    zsfree(compprefix);
+    zsfree(compsuffix);
+    zsfree(compiprefix);
+    zsfree(compisuffix);
+    zsfree(compqiprefix);
+    zsfree(compqisuffix);
+    zsfree(compmatcherstr);
+    zsfree(compcontext);
+    zsfree(compparameter);
+    zsfree(compredirect);
+    zsfree(compquote);
+    zsfree(compquoting);
+    zsfree(comprestore);
+    zsfree(complist);
+    zsfree(compforcelist);
+    zsfree(compinsert);
+    zsfree(compexact);
+    zsfree(compexactstr);
+    zsfree(comppatmatch);
+    zsfree(comppatinsert);
+    zsfree(complastprompt);
+    zsfree(comptoend);
+    zsfree(compoldlist);
+    zsfree(compoldins);
+    zsfree(compvared);
+
+    zfree(clwords, clwsize * sizeof(char *));
+
     return 0;
 }
 
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index 4f1bb03f2..72a0f120d 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -551,13 +551,13 @@ bin_zle_complete(char *name, char **args, char *ops, char func)
     Widget w, cw;
 
 #ifdef DYNAMIC
-    if (!require_module(name, "compctl", 0, 0)) {
-	zerrnam(name, "can't load compctl module", NULL, 0);
+    if (!require_module(name, "complete", 0, 0)) {
+	zerrnam(name, "can't load complete module", NULL, 0);
 	return 1;
     }
 #else
-    if (!makecompparamsptr) {
-	zerrnam(name, "compctl module not available", NULL, 0);
+    if (!module_linked("complete")) {
+	zerrnam(name, "complete module not available", NULL, 0);
 	return 1;
     }
 #endif
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 5c21f946b..5803ac392 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -42,24 +42,6 @@
  * file only are not translated: they remain indexes into the metafied  *
  * line.                                                                */
 
-#ifdef HAVE_NIS_PLUS
-# include <rpcsvc/nis.h>
-#else
-# ifdef HAVE_NIS
-#  include	<rpc/types.h>
-#  include	<rpc/rpc.h>
-#  include	<rpcsvc/ypclnt.h>
-#  include	<rpcsvc/yp_prot.h>
-
-/* This is used when getting usernames from the NIS. */
-typedef struct {
-    int len;
-    char *s;
-}
-dopestring;
-# endif
-#endif
-
 
 #define inststr(X) inststrlen((X),1,-1)
 
@@ -68,14 +50,31 @@ dopestring;
 static char *origline;
 static int origcs, origll;
 
+/* Words on the command line, for use in completion */
+ 
+/**/
+int clwsize, clwnum, clwpos;
+/**/
+char **clwords;
+
 /* wb and we hold the beginning/end position of the word we are completing. */
 
-static int wb, we;
+/**/
+int wb, we;
 
 /* offs is the cursor position within the tokenized *
  * current word after removing nulargs.             */
 
-static int offs;
+/**/
+int offs;
+
+/* We store the following prefixes/suffixes:                               *
+ * ipre,ripre  -- the ignored prefix (quoted and unquoted)                 *
+ * isuf        -- the ignored suffix                                       *
+ * autoq       -- quotes to automatically insert                           */
+
+/**/
+char *ipre, *ripre, *isuf, *qipre, *qisuf, autoq;
 
 /* the last completion widget called */
 
@@ -86,7 +85,8 @@ static Widget lastcompwidget;
  * usemenu is set to 2 if we have to start automenu and 3 if we have to   *
  * insert a match as if for menucompletion but without really stating it. */
 
-static int usemenu, useglob, useexact, useline, uselist;
+/**/
+int usemenu, useglob, useexact, useline, uselist;
 
 /* Non-zero if we should keep an old list. */
 
@@ -131,7 +131,8 @@ static int hasunqu, useqbr, brpcs, brscs;
 /* The list of matches.  fmatches contains the matches we first ignore *
  * because of fignore.                                                 */
 
-static LinkList matches, fmatches;
+/**/
+LinkList matches, fmatches;
 
 /* This holds the list of matches-groups. lastmatches holds the last list of 
  * permanently allocated matches, pmatches is the same for the list
@@ -178,45 +179,13 @@ struct cldata listdat;
 
 /* This flag is non-zero if we are completing a pattern (with globcomplete) */
 
-static int ispattern, haspattern;
+/**/
+int ispattern, haspattern;
 
 /* Non-zero if at least one match was added without -U. */
 
-static int hasmatched;
-
-/* Two patterns used when doing glob-completion.  The first one is built *
- * from the whole word we are completing and the second one from that    *
- * part of the word that was identified as a possible filename.          */
-
-static Patprog patcomp, filecomp;
-
-/* We store the following prefixes/suffixes:                               *
- * lpre/lsuf -- what's on the line                                         *
- * rpre/rsuf -- same as lpre/lsuf, but expanded                            *
- *                                                                         *
- * ... and if we are completing files, too:                                *
- * ppre/psuf   -- the path prefix/suffix                                   *
- * 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 (quoted and unquoted)                 *
- * isuf        -- the ignored suffix                                       *
- * qipre, qisuf-- ingnored quoted string                                   *
- * autoq       -- quotes to automatically insert                           *
- *                                                                         *
- * 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. */
-
-static char *lpre, *lsuf;
-static char *rpre, *rsuf;
-static char *ppre, *psuf, *lppre, *lpsuf, *prpre;
-static char *fpre, *fsuf;
-static char *ipre, *ripre;
-static char *isuf;
-static char *qfpre, *qfsuf, *qrpre, *qrsuf, *qlpre, *qlsuf;
-static char *qipre, *qisuf, autoq;
-static int lpl, lsl, rpl, rsl, fpl, fsl, lppl, lpsl;
-static int noreal;
+/**/
+int hasmatched;
 
 /* A parameter expansion prefix (like ${). */
 
@@ -226,15 +195,6 @@ static char *parpre;
 
 static int parflags;
 
-/* This is either zero or equal to the special character the word we are *
- * trying to complete starts with (e.g. Tilde or Equals).                */
-
-static char ic;
-
-/* This variable says what we are currently adding to the list of matches. */
-
-static int addwhat;
-
 /* This holds the word we are completing in quoted from. */
 
 static char *qword;
@@ -245,7 +205,8 @@ static Cmgroup mgroup;
 
 /* Match counters: all matches, normal matches (not alternate set). */
 
-static int mnum;
+/**/
+int mnum;
 
 /* The match counter when unambig_data() was called. */
 
@@ -253,7 +214,8 @@ static int unambig_mnum;
 
 /* Match flags for all matches in this group. */
 
-static int mflags;
+/**/
+int mflags;
 
 /* Length of longest/shortest match. */
 
@@ -263,87 +225,30 @@ static int maxmlen, minmlen;
  * a pointer to the current cexpl structure. */
 
 static LinkList expls;
-static Cexpl expl;
-
-/* A pointer to the compctl we are using. */
-
-static Compctl curcc;
-
-/* A list of all compctls we have already used. */
-
-static LinkList ccused;
 
-/* A list of all compctls used so far. */
-
-static LinkList allccs;
-
-/* A stack of currently used compctls. */
-
-static LinkList ccstack;
+/**/
+Cexpl curexpl;
 
 /* A stack of completion matchers to be used. */
 
-static Cmlist mstack;
+/**/
+Cmlist mstack;
 
 /* The completion matchers used when building new stuff for the line. */
 
-static Cmlist bmatchers;
+/**/
+Cmlist bmatchers;
 
 /* A list with references to all matchers we used. */
 
-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;
-    int min, max;
-};
-
-#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
-#define CLF_MATCHED 128
+/**/
+LinkList matchers;
 
 /* A heap of free Cline structures. */
 
 static Cline freecl;
 
-/* Information for ambiguous completions. One for fignore ignored and   *
- * one for normal completion. */
-
-typedef struct aminfo *Aminfo;
-
-struct aminfo {
-    Cmatch firstm;		/* the first match                        */
-    int exact;			/* if there was an exact match            */
-    Cmatch exactm;		/* the exact match (if any)               */
-    int count;			/* number of matches                      */
-    Cline line;			/* unambiguous line string                */
-};
+/* Ambiguous information. */
 
 static Aminfo ainfo, fainfo;
 
@@ -354,10 +259,19 @@ static char *compfunc = NULL;
 
 /* The memory heap to use for new style completion generation. */
 
-static Heap compheap;
+/**/
+Heap compheap;
 
 /* Find out if we have to insert a tab (instead of trying to complete). */
 
+/* A list of some data.
+ *
+ * Well, actually, it's the list of all compctls used so far, but since
+ * conceptually we don't know anything about compctls here... */
+
+/**/
+LinkList allccs;
+
 /**/
 static int
 usetab(void)
@@ -370,14 +284,6 @@ usetab(void)
     return 1;
 }
 
-enum { COMP_COMPLETE,
-       COMP_LIST_COMPLETE,
-       COMP_SPELL,
-       COMP_EXPAND,
-       COMP_EXPAND_COMPLETE,
-       COMP_LIST_EXPAND };
-#define COMP_ISEXPAND(X) ((X) >= COMP_EXPAND)
-
 /* Non-zero if the last completion done was ambiguous (used to find   *
  * out if AUTOMENU should start).  More precisely, it's nonzero after *
  * successfully doing any completion, unless the completion was       *
@@ -648,7 +554,8 @@ acceptandmenucomplete(char **args)
 /* 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, parq, eparq, linwhat, linarr;
+/**/
+int lincmd, linredir, ispar, parq, eparq, linwhat, linarr;
 
 /* The string for the redirection operator. */
 
@@ -657,7 +564,8 @@ static char *rdstr;
 /* This holds the name of the current command (used to find the right *
  * compctl).                                                          */
 
-static char *cmdstr;
+/**/
+char *cmdstr;
 
 /* This hold the name of the variable we are working on. */
 
@@ -665,11 +573,13 @@ static char *varname;
 
 /* != 0 if we are in a subscript */
 
-static int insubscr;
+/**/
+int insubscr;
 
 /* Parameter pointer for completing keys of an assoc array. */
 
-static Param keypm;
+/**/
+Param keypm;
 
 /* 1 if we are completing in a quoted string (or inside `...`) */
 
@@ -681,6 +591,25 @@ int instring, inbackt;
 
 #define quotename(s, e) bslashquote(s, e, instring)
 
+/* Copy the given string and remove backslashes from the copy and return it. */
+
+/**/
+char *
+rembslash(char *s)
+{
+    char *t = s = dupstring(s);
+
+    while (*s)
+	if (*s == '\\') {
+	    chuck(s);
+	    if (*s)
+		s++;
+	} else
+	    s++;
+
+    return t;
+}
+
 /* Check if the given string is the name of a parameter and if this *
  * parameter is one worth expanding.                                */
 
@@ -761,7 +690,8 @@ cmphaswilds(char *str)
 
 /* Check if we have to complete a parameter name. */
 
-static char *
+/**/
+char *
 check_param(char *s, int set, int test)
 {
     char *p;
@@ -1245,7 +1175,7 @@ addx(char **ptmp)
 /* Like dupstring, but add an extra space at the end of the string. */
 
 /**/
-static char *
+char *
 dupstrspace(const char *str)
 {
     int len = strlen((char *)str);
@@ -1338,7 +1268,7 @@ dupbrinfo(Brinfo p, Brinfo *last)
 static char *
 get_comp_string(void)
 {
-    int t0, tt0, i, j, k, cp, rd, sl, ocs, ins, oins, ia, parct;
+    int t0, tt0, i, j, k, cp, rd, sl, ocs, ins, oins, ia, parct, varq = 0;
     char *s = NULL, *linptr, *tmp, *p, *tt = NULL;
 
     freebrinfo(brbeg);
@@ -1417,9 +1347,15 @@ get_comp_string(void)
 		for (j = 0, p = tokstr; *p; p++)
 		    if (*p == Snull || *p == Dnull)
 			j++;
-		if (j & 1)
-		    tok = STRING;
-	    }
+		if (j & 1) {
+		    if (lincmd && strchr(tokstr, '=')) {
+			varq = 1;
+			tok = ENVSTRING;
+		    } else
+			tok = STRING;
+		}
+	    } else if (tok == ENVSTRING)
+		varq = 0;
 	    if (tok == ENVARRAY) {
 		linarr = 1;
 		zsfree(varname);
@@ -1556,6 +1492,10 @@ get_comp_string(void)
 	} else if (t0 == ENVSTRING) {
 	    char sav;
 	    /* The cursor was inside a parameter assignment. */
+
+	    if (varq)
+		tt = clwords[clwpos];
+
 	    for (s = tt; iident(*s); s++);
 	    sav = *s;
 	    *s = '\0';
@@ -2964,7 +2904,8 @@ match_parts(char *l, char *w, int n, int part)
  * 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 char *
+/**/
+char *
 comp_match(char *pfx, char *sfx, char *w, Patprog cp, Cline *clp, int qu,
 	   Brinfo *bpl, int bcp, Brinfo *bsl, int bcs, int *exact)
 {
@@ -3911,7 +3852,8 @@ join_clines(Cline o, Cline n)
 
 /* This adds all the data we have for a match. */
 
-static Cmatch
+/**/
+Cmatch
 add_match_data(int alt, char *str, Cline line,
 	       char *ipre, char *ripre, char *isuf,
 	       char *pre, char *prpre,
@@ -4181,11 +4123,11 @@ add_match_data(int alt, char *str, Cline line,
     newmatches = 1;
 
     /* One more match for this explanation. */
-    if (expl) {
+    if (curexpl) {
 	if (alt)
-	    expl->fcount++;
+	    curexpl->fcount++;
 	else
-	    expl->count++;
+	    curexpl->count++;
     }
     if (!ai->firstm)
 	ai->firstm = cm;
@@ -4315,11 +4257,11 @@ addmatches(Cadata dat, char **argv)
 		dparl = newlinklist();
 	    }
 	    if (dat->exp) {
-		expl = (Cexpl) zhalloc(sizeof(struct cexpl));
-		expl->count = expl->fcount = 0;
-		expl->str = dupstring(dat->exp);
+		curexpl = (Cexpl) zhalloc(sizeof(struct cexpl));
+		curexpl->count = curexpl->fcount = 0;
+		curexpl->str = dupstring(dat->exp);
 	    } else
-		expl = NULL;
+		curexpl = NULL;
 
 	    /* Store the matcher in our stack of matchers. */
 	    if (dat->match) {
@@ -4613,431 +4555,6 @@ addmatches(Cadata dat, char **argv)
     return (mnum == nm);
 }
 
-/* This adds a match to the list of matches.  The string to add is given   *
- * in s, the type of match is given in the global variable addwhat and     *
- * the parameter t (if not NULL) is a pointer to a hash node node which    *
- * may be used to give other information to this function.                 *
- *                                                                         *
- * addwhat contains either one of the special values (negative, see below) *
- * or the inclusive OR of some of the CC_* flags used for compctls.        */
-
-/**/
-static void
-addmatch(char *s, char *t)
-{
-    int isfile = 0, isalt = 0, isexact;
-    char *ms = NULL, *tt;
-    HashNode hn;
-    Param pm;
-    Cline lc = NULL;
-    Brinfo bp, bpl = brbeg, bsl = brend, bpt, bst;
-
-    for (bp = brbeg; bp; bp = bp->next)
-	bp->curpos = ((addwhat == CC_QUOTEFLAG) ? bp->qpos : bp->pos);
-    for (bp = brend; bp; bp = bp->next)
-	bp->curpos = ((addwhat == CC_QUOTEFLAG) ? bp->qpos : bp->pos);
-
-    /*
-     * addwhat: -5 is for files,
-     *          -6 is for glob expansions,
-     *          -8 is for executable files (e.g. command paths),
-     *          -9 is for parameters
-     *          -7 is for command names (from cmdnamtab)
-     *          -4 is for a cdable parameter
-     *          -3 is for executable command names.
-     *          -2 is for anything unquoted
-     *          -1 is for other file specifications
-     *          (things with `~' or `=' at the beginning, ...).
-     */
-
-    /* Just to make the code cleaner */
-    hn = (HashNode) t;
-    pm = (Param) t;
-
-    if (addwhat == -1 || addwhat == -5 || addwhat == -6 ||
-	addwhat == CC_FILES || addwhat == -7 || addwhat == -8) {
-	int ppl = (ppre ? strlen(ppre) : 0), psl = (psuf ? strlen(psuf) : 0);
-
-	while (bpl && bpl->curpos < ppl)
-	    bpl = bpl->next;
-	while (bsl && bsl->curpos < psl)
-	    bsl = bsl->next;
-
-	if ((addwhat == CC_FILES ||
-	     addwhat == -5) && !*psuf) {
-	    /* If this is a filename, do the fignore check. */
-	    char **pt = fignore;
-	    int filell, sl = strlen(s);
-
-	    for (isalt = 0; !isalt && *pt; pt++)
-		if ((filell = strlen(*pt)) < sl &&
-		    !strcmp(*pt, s + sl - filell))
-		    isalt = 1;
-	}
-	ms = ((addwhat == CC_FILES || addwhat == -6 ||
-	       addwhat == -5 || addwhat == -8) ? 
-	      comp_match(qfpre, qfsuf, s, filecomp, &lc, (ppre && *ppre ? 1 : 2),
-			 &bpl, ppl ,&bsl, psl, &isexact) :
-	      comp_match(fpre, fsuf, s, filecomp, &lc, 0,
-			 &bpl, ppl, &bsl, psl, &isexact));
-	if (!ms)
-	    return;
-
-	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) &&
-	       !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))    ||
-		  ((addwhat & CC_INTVARS)   &&  (hn->flags & PM_INTEGER))  ||
-		  ((addwhat & CC_ENVVARS)   &&  (hn->flags & PM_EXPORTED)) ||
-		  ((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))) &&
-		 !pm->level) ||
-		((( addwhat & CC_SHFUNCS)				  ||
-		  ( addwhat & CC_BUILTINS)				  ||
-		  ( addwhat & CC_EXTCMDS)				  ||
-		  ( addwhat & CC_RESWDS)				  ||
-		  ((addwhat & CC_ALREG)   && !(hn->flags & ALIAS_GLOBAL)) ||
-		  ((addwhat & CC_ALGLOB)  &&  (hn->flags & ALIAS_GLOBAL))) &&
-		 (((addwhat & CC_DISCMDS) && (hn->flags & DISABLED)) ||
-		  ((addwhat & CC_EXCMDS)  && !(hn->flags & DISABLED)))) ||
-		((addwhat & CC_BINDINGS) && !(hn->flags & DISABLED))))) {
-	char *p1, *s1, *p2, *s2;
-
-	if (addwhat == CC_QUOTEFLAG) {
-	    p1 = qrpre; s1 = qrsuf;
-	    p2 = rpre;  s2 = rsuf;
-	} else {
-	    p1 = qlpre; s1 = qlsuf;
-	    p2 = lpre;  s2 = lsuf;
-	}
-	bpt = bpl;
-	bst = bsl;
-
-	if (!(ms = comp_match(p1, s1, s, patcomp, &lc,
-			      (addwhat == CC_QUOTEFLAG),
-			      &bpl, strlen(p1), &bsl, strlen(s1),
-			      &isexact))) {
-	    bpl = bpt;
-	    bsl = bst;
-	    if (!(ms = comp_match(p2, s2, s, NULL, &lc,
-				  (addwhat == CC_QUOTEFLAG),
-				  &bpl, strlen(p2), &bsl, strlen(s2),
-				  &isexact)))
-		return;
-	}
-    }
-    if (!ms)
-	return;
-    add_match_data(isalt, ms, lc, ipre, ripre, isuf, 
-		   (incompfunc ? dupstring(curcc->prefix) : curcc->prefix),
-		   prpre, 
-		   (isfile ? lppre : NULL), NULL,
-		   (isfile ? lpsuf : NULL), NULL,
-		   (incompfunc ? dupstring(curcc->suffix) : curcc->suffix),
-		   (mflags | isfile), isexact);
-}
-
-#ifdef HAVE_NIS_PLUS
-static int
-match_username(nis_name table, nis_object *object, void *userdata)
-{
-    if (errflag)
-	return 1;
-    else {
-	static char buf[40];
-	register entry_col *ec =
-	    object->zo_data.objdata_u.en_data.en_cols.en_cols_val;
-	register int l = minimum(ec->ec_value.ec_value_len, 39);
-
-	memcpy(buf, ec->ec_value.ec_value_val, l);
-	buf[l] = '\0';
-
-	addmatch(dupstring(buf), NULL);
-    }
-    return 0;
-}
-#else
-# ifdef HAVE_NIS
-static int
-match_username(int status, char *key, int keylen, char *val, int vallen, dopestring *data)
-{
-    if (errflag || status != YP_TRUE)
-	return 1;
-
-    if (vallen > keylen && val[keylen] == ':') {
-	val[keylen] = '\0';
-	addmatch(dupstring(val), NULL);
-    }
-    return 0;
-}
-# endif /* HAVE_NIS */
-#endif  /* HAVE_NIS_PLUS */
-
-/**/
-static void
-maketildelist(void)
-{
-#if defined(HAVE_NIS) || defined(HAVE_NIS_PLUS)
-    FILE *pwf;
-    char buf[BUFSIZ], *p;
-    int skipping;
-
-# ifndef HAVE_NIS_PLUS
-    char domain[YPMAXDOMAIN];
-    struct ypall_callback cb;
-    dopestring data;
-
-    data.s = fpre;
-    data.len = fpl;
-    /* Get potential matches from NIS and cull those without local accounts */
-    if (getdomainname(domain, YPMAXDOMAIN) == 0) {
-	cb.foreach = (int (*)()) match_username;
-	cb.data = (char *)&data;
-	yp_all(domain, PASSWD_MAP, &cb);
-/*	for (n = firstnode(matches); n; incnode(n))
-	    if (getpwnam(getdata(n)) == NULL)
-		uremnode(matches, n);*/
-    }
-# else  /* HAVE_NIS_PLUS */
-       /* Maybe we should turn this string into a #define'd constant...? */
-
-    nis_list("passwd.org_dir", EXPAND_NAME|ALL_RESULTS|FOLLOW_LINKS|FOLLOW_PATH,
-	     match_username, 0);
-# endif
-    /* Don't forget the non-NIS matches from the flat passwd file */
-    if ((pwf = fopen(PASSWD_FILE, "r")) != NULL) {
-	skipping = 0;
-	while (fgets(buf, BUFSIZ, pwf) != NULL) {
-	    if (strchr(buf, '\n') != NULL) {
-		if (!skipping) {
-		    if ((p = strchr(buf, ':')) != NULL) {
-			*p = '\0';
-			addmatch(dupstring(buf), NULL);
-		    }
-		} else
-		    skipping = 0;
-	    } else
-		skipping = 1;
-	}
-	fclose(pwf);
-    }
-#else  /* no NIS or NIS_PLUS */
-    /* add all the usernames to the named directory table */
-    nameddirtab->filltable(nameddirtab);
-#endif
-
-    scanhashtable(nameddirtab, 0, (addwhat==-1) ? 0 : ND_USERNAME, 0,
-	    addhnmatch, 0);
-}
-
-/* This does the check for compctl -x `n' and `N' patterns. */
-
-/**/
-int
-getcpat(char *str, int cpatindex, char *cpat, int class)
-{
-    char *s, *t, *p;
-    int d = 0;
-
-    if (!str || !*str)
-	return -1;
-
-    cpat = rembslash(cpat);
-
-    if (!cpatindex)
-	cpatindex++, d = 0;
-    else if ((d = (cpatindex < 0)))
-	cpatindex = -cpatindex;
-
-    for (s = d ? str + strlen(str) - 1 : str;
-	 d ? (s >= str) : *s;
-	 d ? s-- : s++) {
-	for (t = s, p = cpat; *t && *p; p++) {
-	    if (class) {
-		if (*p == *s && !--cpatindex)
-		    return (int)(s - str + 1);
-	    } else if (*t++ != *p)
-		break;
-	}
-	if (!class && !*p && !--cpatindex)
-	    return t - str;
-    }
-    return -1;
-}
-
-/* Dump a hash table (without sorting).  For each element the addmatch  *
- * function is called and at the beginning the addwhat variable is set. *
- * This could be done using scanhashtable(), but this is easy and much  *
- * more efficient.                                                      */
-
-/**/
-static void
-dumphashtable(HashTable ht, int what)
-{
-    HashNode hn;
-    int i;
-
-    addwhat = what;
-
-    for (i = 0; i < ht->hsize; i++)
-	for (hn = ht->nodes[i]; hn; hn = hn->next)
-	    addmatch(hn->nam, (char *) hn);
-}
-
-/* ScanFunc used by maketildelist() et al. */
-
-/**/
-static void
-addhnmatch(HashNode hn, int flags)
-{
-    addmatch(hn->nam, NULL);
-}
-
-/* Perform expansion on the given string and return the result. *
- * During this errors are not reported.                         */
-
-/**/
-static char *
-getreal(char *str)
-{
-    LinkList l = newlinklist();
-    int ne = noerrs;
-
-    noerrs = 1;
-    addlinknode(l, dupstring(str));
-    prefork(l, 0);
-    noerrs = ne;
-    if (!errflag && nonempty(l) &&
-	((char *) peekfirst(l)) && ((char *) peekfirst(l))[0])
-	return dupstring(peekfirst(l));
-    errflag = 0;
-
-    return dupstring(str);
-}
-
-/* This reads a directory and adds the files to the list of  *
- * matches.  The parameters say which files should be added. */
-
-/**/
-static void
-gen_matches_files(int dirs, int execs, int all)
-{
-    DIR *d;
-    struct stat buf;
-    char *n, p[PATH_MAX], *q = NULL, *e;
-    LinkList l = NULL;
-    int ns = 0, ng = opts[NULLGLOB], test, aw = addwhat;
-
-    opts[NULLGLOB] = 1;
-
-    if (*psuf) {
-	/* If there is a path suffix, check if it doesn't have a `*' or *
-	 * `)' at the end (this is used to determine if we should use   *
-	 * globbing).                                                   */
-	q = psuf + strlen(psuf) - 1;
-	ns = !(*q == Star || *q == Outpar);
-	l = newlinklist();
-	/* And generate only directory names. */
-	dirs = 1;
-	all = execs = 0;
-    }
-    /* Open directory. */
-    if ((d = opendir((prpre && *prpre) ? prpre : "."))) {
-	/* If we search only special files, prepare a path buffer for stat. */
-	if (!all && prpre) {
-	    strcpy(p, prpre);
-	    q = p + strlen(prpre);
-	}
-	/* Fine, now read the directory. */
-	while ((n = zreaddir(d, 1)) && !errflag) {
-	    /* Ignore files beginning with `.' unless the thing we found on *
-	     * the command line also starts with a dot or GLOBDOTS is set.  */
-	    if (*n != '.' || *fpre == '.' || isset(GLOBDOTS)) {
-		addwhat = execs ? -8 : -5;
-		if (filecomp)
-		    /* If we have a pattern for the filename check, use it. */
-		    test = pattry(filecomp, n);
-		else {
-		    /* Otherwise use the prefix and suffix strings directly. */
-		    e = n + strlen(n) - fsl;
-		    if ((test = !strncmp(n, fpre, fpl)))
-			test = !strcmp(e, fsuf);
-		    if (!test && mstack) {
-			test = 1;
-			addwhat = CC_FILES;
-		    }
-		}
-		/* Filename didn't match? */
-		if (!test)
-		    continue;
-		if (!all) {
-		    /* We still have to check the file type, so prepare *
-		     * the path buffer by appending the filename.       */
-		    strcpy(q, n);
-		    /* And do the stat. */
-		    if (stat(p, &buf) < 0)
-			continue;
-		}
-		if (all ||
-		    (dirs && S_ISDIR(buf.st_mode)) ||
-		    (execs && S_ISREG(buf.st_mode) && (buf.st_mode&S_IXUGO))) {
-		    /* If we want all files or the file has the right type... */
-		    if (*psuf) {
-			/* We have to test for a path suffix. */
-			int o = strlen(p), tt;
-
-			/* Append it to the path buffer. */
-			strcpy(p + o, psuf);
-
-			/* Do we have to use globbing? */
-			if (ispattern ||
-			    (ns && comppatmatch && *comppatmatch)) {
-			    /* Yes, so append a `*' if needed. */
-			    if (ns && comppatmatch && *comppatmatch == '*') {
-				int tl = strlen(p);
-
-				p[tl] = Star;
-				p[tl + 1] = '\0';
-			    }
-			    /* Do the globbing... */
-			    remnulargs(p);
-			    addlinknode(l, p);
-			    globlist(l);
-			    /* And see if that produced a filename. */
-			    tt = nonempty(l);
-			    while (ugetnode(l));
-			} else
-			    /* Otherwise just check, if we have access *
-			     * to the file.                            */
-			    tt = !access(p, F_OK);
-
-			p[o] = '\0';
-			if (tt)
-			    /* Ok, we can add the filename to the *
-			     * list of matches.                   */
-			    addmatch(dupstring(n), NULL);
-		    } else
-			/* We want all files, so just add the name *
-			 * to the matches.                         */
-			addmatch(dupstring(n), NULL);
-		}
-	    }
-	}
-	closedir(d);
-    }
-    opts[NULLGLOB] = ng;
-    addwhat = aw;
-}
-
 /**/
 static int
 docompletion(char *s, int lst, int incmd)
@@ -5180,44 +4697,6 @@ docompletion(char *s, int lst, int incmd)
 	    useline != 2 && (!oldlist || !listshown)) {
 	    onlyexpl = 1;
 	    showinglist = -2;
-#if 0
-	    Cmgroup g = amatches;
-	    Cexpl *e;
-	    int up = 0, tr = 1, nn = 0;
-
-	    if (!nmatches)
-		ret = 1;
-
-	    while (g) {
-		if ((e = g->expls))
-		    while (*e) {
-			if ((*e)->count) {
-			    if (tr) {
-				trashzle();
-				tr = 0;
-			    }
-			    if (nn) {
-				up++;
-				putc('\n', shout);
-			    }
-			    up += printfmt((*e)->str, (*e)->count, 1, 1);
-			    nn = 1;
-			}
-			e++;
-		    }
-		g = g->next;
-	    }
-	    if (!tr) {
-		clearflag = (isset(USEZLE) && !termflags &&
-			      complastprompt && *complastprompt);
-
-		if (clearflag && up + nlnct < lines)
-		    tcmultout(TCUP, TCMULTUP, up + nlnct);
-		else
-		    putc('\n', shout);
-		fflush(shout);
-	    }
-#endif
 	}
       compend:
 	for (n = firstnode(matchers); n; incnode(n))
@@ -5530,14 +5009,6 @@ callcompfunc(char *s, char *fn)
     lastval = lv;
 }
 
-/* The beginning and end of a word range to be used by -l. */
-
-static int brange, erange;
-
-/* This is used to detect when and what to continue. */
-
-static unsigned long ccont;
-
 /* Create the completion list.  This is called whenever some bit of   *
  * completion code needs the list.                                    *
  * Along with the list is maintained the prefixes/suffixes etc.  When *
@@ -5620,26 +5091,24 @@ makecomplist(char *s, int incmd, int lst)
 	begcmgroup("default", 0);
 	menucmp = menuacc = newmatches = onlyexpl = 0;
 
-	ccused = newlinklist();
-	ccstack = newlinklist();
+	runhookdef(COMPCTLBEFOREHOOK, NULL);
 
 	s = dupstring(os);
 	if (compfunc)
 	    callcompfunc(s, compfunc);
-	else
-	    makecomplistglobal(s, incmd, lst, 0);
+	else {
+	    struct ccmakedat dat;
 
+	    dat.str = s;
+	    dat.incmd = incmd;
+	    dat.lst = lst;
+	    runhookdef(COMPCTLMAKEHOOK, (void *) &dat);
+	}
 	endcmgroup(NULL);
 
-	if (amatches && !oldlist)
-	    amatches->ccs = (Compctl *) makearray(ccused, 0, 0,
-						  &(amatches->ccount), NULL, NULL);
-	else {
-	    LinkNode n;
+	runhookdef(COMPCTLAFTERHOOK,
+		   (void *) ((amatches && !oldlist) ? 1L : 0L));
 
-	    for (n = firstnode(ccused); n; incnode(n))
-		freecompctl((Compctl) getdata(n));
-	}
 	if (oldlist) {
 	    nmatches = onm;
 	    validlist = 1;
@@ -5754,25 +5223,17 @@ set_comp_sep(void)
 {
     int lip, lp;
     char *s = comp_str(&lip, &lp, 0);
-
-    if (compisuffix)
-	s = dyncat(s, compisuffix);
-    untokenize(s);
-
-    return sep_comp_string("", s, lip + lp, 0);
-}
-
-/**/
-static int
-sep_comp_string(char *ss, char *s, int noffs, int rec)
-{
     LinkList foo = newlinklist();
     LinkNode n;
     int owe = we, owb = wb, ocs = cs, swb, swe, scs, soffs, ne = noerrs;
-    int sl = strlen(ss), tl, got = 0, i = 0, cur = -1, oll = ll;
-    int ois = instring, oib = inbackt;
+    int tl, got = 0, i = 0, cur = -1, oll = ll, sl;
+    int ois = instring, oib = inbackt, noffs = lip + lp;
     char *tmp, *p, *ns, *ol = (char *) line, sav, oaq = autoq, *qp, *qs;
 
+    if (compisuffix)
+	s = dyncat(s, compisuffix);
+    untokenize(s);
+
     swb = swe = soffs = 0;
     ns = NULL;
 
@@ -5782,14 +5243,12 @@ sep_comp_string(char *ss, char *s, int noffs, int rec)
     addedx = 1;
     noerrs = 1;
     lexsave();
-    tmp = (char *) zhalloc(tl = sl + 3 + strlen(s));
-    strcpy(tmp, ss);
-    tmp[sl] = ' ';
-    memcpy(tmp + sl + 1, s, noffs);
-    tmp[(scs = cs = sl + 1 + noffs)] = 'x';
-    strcpy(tmp + sl + 2 + noffs, s + noffs);
-    if (incompfunc)
-	tmp = rembslash(tmp);
+    tmp = (char *) zhalloc(tl = 3 + strlen(s));
+    tmp[0] = ' ';
+    memcpy(tmp + 1, s, noffs);
+    tmp[(scs = cs = 1 + noffs)] = 'x';
+    strcpy(tmp + 2 + noffs, s + noffs);
+    tmp = rembslash(tmp);
     inpush(dupstrspace(tmp), 0, NULL);
     line = (unsigned char *) tmp;
     ll = tl - 1;
@@ -5882,59 +5341,22 @@ sep_comp_string(char *ss, char *s, int noffs, int rec)
 	    chuck(p--);
 	}
     }
-    sav = s[(i = swb - sl - 1)];
+    sav = s[(i = swb - 1)];
     s[i] = '\0';
-    qp = tricat(qipre, (incompfunc ? rembslash(s) : s), "");
+    qp = tricat(qipre, rembslash(s), "");
     s[i] = sav;
     if (swe < swb)
 	swe = swb;
-    swe -= sl + 1;
+    swe--;
     sl = strlen(s);
     if (swe > sl)
 	swe = sl, ns[swe - swb + 1] = '\0';
-    qs = tricat((incompfunc ? rembslash(s + swe) : s + swe), qisuf, "");
+    qs = tricat(rembslash(s + swe), qisuf, "");
     sl = strlen(ns);
     if (soffs > sl)
 	soffs = sl;
 
-    if (rec) {
-	char **ow = clwords, *os = cmdstr, *oqp = qipre, *oqs = qisuf;
-	int olws = clwsize, olwn = clwnum, olwp = clwpos;
-	int obr = brange, oer = erange, oof = offs;
-	unsigned long occ = ccont;
-
-	clwsize = clwnum = countlinknodes(foo);
-	clwords = (char **) zalloc((clwnum + 1) * sizeof(char *));
-	for (n = firstnode(foo), i = 0; n; incnode(n), i++) {
-	    p = clwords[i] = (char *) getdata(n);
-	    untokenize(p);
-	}
-	clwords[i] = NULL;
-	clwpos = cur;
-	cmdstr = ztrdup(clwords[0]);
-	brange = 0;
-	erange = clwnum - 1;
-	qipre = qp;
-	qisuf = qs;
-	offs = soffs;
-	ccont = CC_CCCONT;
-	makecomplistcmd(ns, !clwpos, CFN_FIRST);
-	ccont = occ;
-	offs = oof;
-	zsfree(cmdstr);
-	cmdstr = os;
-	freearray(clwords);
-	clwords = ow;
-	clwsize = olws;
-	clwnum = olwn;
-	clwpos = olwp;
-	brange = obr;
-	erange = oer;
-	zsfree(qipre);
-	qipre = oqp;
-	zsfree(qisuf);
-	qisuf = oqs;
-    } else {
+    {
 	int set = CP_QUOTE | CP_QUOTING, unset = 0;
 
 	zsfree(compquote);
@@ -6005,1492 +5427,6 @@ sep_comp_string(char *ss, char *s, int noffs, int rec)
     return 0;
 }
 
-/**/
-int
-makecomplistcall(Compctl cc)
-{
-    int nm = mnum;
-
-    SWITCHHEAPS(compheap) {
-	HEAPALLOC {
-	    int ooffs = offs, lip, lp, ois = instring, oib = inbackt;
-	    char *str = comp_str(&lip, &lp, 0), qc;
-	    char *oisuf = isuf, *oqp = qipre, *oqs = qisuf, oaq = autoq;
-
-	    if (compquote && (qc = *compquote)) {
-		if (qc == '`') {
-		    instring = 0;
-		    inbackt = 0;
-		    autoq = '\0';
-		} else {
-		    instring = (qc == '\'' ? 1 : 2);
-		    inbackt = 0;
-		    autoq = qc;
-		}
-	    } else {
-		instring = inbackt = 0;
-		autoq = '\0';
-	    }
-	    isuf = dupstring(compisuffix);
-	    ctokenize(isuf);
-	    remnulargs(isuf);
-	    qipre = ztrdup(compqiprefix ? compqiprefix : "");
-	    qisuf = ztrdup(compqisuffix ? compqisuffix : "");
-	    offs = lip + lp;
-	    cc->refc++;
-	    ccont = 0;
-	    if (!cc->ylist && !cc->gname) {
-		endcmgroup(NULL);
-		begcmgroup("default", 0);
-	    }
-	    makecomplistor(cc, str, lincmd, lip, 0);
-	    offs = ooffs;
-	    isuf = oisuf;
-	    zsfree(qipre);
-	    zsfree(qisuf);
-	    qipre = oqp;
-	    qisuf = oqs;
-	    instring = ois;
-	    inbackt = oib;
-	    autoq = oaq;
-	} LASTALLOC;
-    } SWITCHBACKHEAPS;
-
-    return (mnum == nm);
-}
-
-/* A simple counter to avoid endless recursion between old and new style *
- * completion. */
-
-static int cdepth = 0;
-
-#define MAX_CDEPTH 16
-
-/**/
-int
-makecomplistctl(int flags)
-{
-    int ret;
-
-    if (cdepth == MAX_CDEPTH)
-	return 0;
-
-    cdepth++;
-    SWITCHHEAPS(compheap) {
-	HEAPALLOC {
-	    int ooffs = offs, lip, lp;
-	    char *str = comp_str(&lip, &lp, 0), *t;
-	    char *os = cmdstr, **ow = clwords, **p, **q, qc;
-	    int on = clwnum, op = clwpos, ois =  instring, oib = inbackt;
-	    char *oisuf = isuf, *oqp = qipre, *oqs = qisuf, oaq = autoq;
-
-	    if (compquote && (qc = *compquote)) {
-		if (qc == '`') {
-		    instring = 0;
-		    inbackt = 0;
-		    autoq = '\0';
-		} else {
-		    instring = (qc == '\'' ? 1 : 2);
-		    inbackt = 0;
-		    autoq = qc;
-		}
-	    } else {
-		instring = inbackt = 0;
-		autoq = '\0';
-	    }
-	    qipre = ztrdup(compqiprefix ? compqiprefix : "");
-	    qisuf = ztrdup(compqisuffix ? compqisuffix : "");
-	    isuf = dupstring(compisuffix);
-	    ctokenize(isuf);
-	    remnulargs(isuf);
-	    clwnum = arrlen(compwords);
-	    clwpos = compcurrent - 1;
-	    cmdstr = ztrdup(compwords[0]);
-	    clwords = (char **) zalloc((clwnum + 1) * sizeof(char *));
-	    for (p = compwords, q = clwords; *p; p++, q++) {
-		t = dupstring(*p);
-		tokenize(t);
-		remnulargs(t);
-		*q = ztrdup(t);
-	    }
-	    *q = NULL;
-	    offs = lip + lp;
-	    incompfunc = 2;
-	    ret = makecomplistglobal(str, !clwpos, COMP_COMPLETE, flags);
-	    incompfunc = 1;
-	    isuf = oisuf;
-	    zsfree(qipre);
-	    zsfree(qisuf);
-	    qipre = oqp;
-	    qisuf = oqs;
-	    instring = ois;
-	    inbackt = oib;
-	    autoq = oaq;
-	    offs = ooffs;
-	    zsfree(cmdstr);
-	    freearray(clwords);
-	    cmdstr = os;
-	    clwords = ow;
-	    clwnum = on;
-	    clwpos = op;
-	} LASTALLOC;
-    } SWITCHBACKHEAPS;
-    cdepth--;
-
-    return ret;
-}
-
-/* This function gets the compctls for the given command line and *
- * adds all completions for them. */
-
-/**/
-static int
-makecomplistglobal(char *os, int incmd, int lst, int flags)
-{
-    Compctl cc = NULL;
-    char *s;
-
-    ccont = CC_CCCONT;
-    cc_dummy.suffix = NULL;
-
-    if (linwhat == IN_ENV) {
-        /* Default completion for parameter values. */
-	if (!(flags & CFN_DEFAULT)) {
-	    cc = &cc_default;
-	    keypm = NULL;
-	}
-    } else if (linwhat == IN_MATH) {
-	if (!(flags & CFN_DEFAULT)) {
-	    if (insubscr >= 2) {
-		/* Inside subscript of assoc array, complete keys. */
-		cc_dummy.mask = 0;
-		cc_dummy.suffix = (insubscr == 2 ? "]" : "");
-	    } else {
-		/* Other math environment, complete paramete names. */
-		keypm = NULL;
-		cc_dummy.mask = CC_PARAMS;
-	    }
-	    cc = &cc_dummy;
-	    cc_dummy.refc = 10000;
-	}
-    } 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.                */
-	if (!(flags & CFN_DEFAULT)) {
-	    s = clwpos ? clwords[clwpos - 1] : "";
-	    cc_dummy.mask = !strcmp("-o", s) ? CC_OPTIONS :
-		((*s == '-' && s[1] && !s[2]) ||
-		 !strcmp("-nt", s) ||
-		 !strcmp("-ot", s) ||
-		 !strcmp("-ef", s)) ? CC_FILES :
-		(CC_FILES | CC_PARAMS);
-	    cc = &cc_dummy;
-	    cc_dummy.refc = 10000;
-	    keypm = NULL;
-	}
-    } else if (linredir) {
-	if (!(flags & CFN_DEFAULT)) {
-	    /* In redirections use default completion. */
-	    cc = &cc_default;
-	    keypm = NULL;
-	}
-    } else {
-	/* Otherwise get the matches for the command. */
-	keypm = NULL;
-	return makecomplistcmd(os, incmd, flags);
-    }
-    if (cc) {
-	/* First, use the -T compctl. */
-	if (!(flags & CFN_FIRST)) {
-	    makecomplistcc(&cc_first, os, incmd);
-
-	    if (!(ccont & CC_CCCONT))
-		return 0;
-	}
-	makecomplistcc(cc, os, incmd);
-	return 1;
-    }
-    return 0;
-}
-
-/* This produces the matches for a command. */
-
-/**/
-static int
-makecomplistcmd(char *os, int incmd, int flags)
-{
-    Compctl cc;
-    Compctlp ccp;
-    char *s;
-    int ret = 0;
-
-    /* First, use the -T compctl. */
-    if (!(flags & CFN_FIRST)) {
-	makecomplistcc(&cc_first, os, incmd);
-
-	if (!(ccont & CC_CCCONT))
-	    return 0;
-    }
-    /* Then search the pattern compctls, with the command name and the *
-     * full pathname of the command. */
-    if (cmdstr) {
-	ret |= makecomplistpc(os, incmd);
-	if (!(ccont & CC_CCCONT))
-	    return ret;
-    }
-    /* If the command string starts with `=', try the path name of the *
-     * command. */
-    if (cmdstr && cmdstr[0] == Equals) {
-	char *c = findcmd(cmdstr + 1, 1);
-
-	if (c) {
-	    zsfree(cmdstr);
-	    cmdstr = ztrdup(c);
-	}
-    }
-
-    /* Find the compctl for this command, trying the full name and then *
-     * the trailing pathname component. If that doesn't yield anything, *
-     * use default completion. */
-    if (incmd)
-	cc = &cc_compos;
-    else if (!(cmdstr &&
-	  (((ccp = (Compctlp) compctltab->getnode(compctltab, cmdstr)) &&
-	    (cc = ccp->cc)) ||
-	   ((s = dupstring(cmdstr)) && remlpaths(&s) &&
-	    (ccp = (Compctlp) compctltab->getnode(compctltab, s)) &&
-	    (cc = ccp->cc))))) {
-	if (flags & CFN_DEFAULT)
-	    return ret;
-	cc = &cc_default;
-    } else
-	ret|= 1;
-    makecomplistcc(cc, os, incmd);
-    return ret;
-}
-
-/* This add the matches for the pattern compctls. */
-
-/**/
-static int
-makecomplistpc(char *os, int incmd)
-{
-    Patcomp pc;
-    Patprog pat;
-    char *s = findcmd(cmdstr, 1);
-    int ret = 0;
-
-    for (pc = patcomps; pc; pc = pc->next) {
-	if ((pat = patcompile(pc->pat, PAT_STATIC, NULL)) &&
-	    (pattry(pat, cmdstr) ||
-	     (s && pattry(pat, s)))) {
-	    makecomplistcc(pc->cc, os, incmd);
-	    ret |= 2;
-	    if (!(ccont & CC_CCCONT))
-		return ret;
-	}
-    }
-    return ret;
-}
-
-/* This produces the matches for one compctl. */
-
-/**/
-static void
-makecomplistcc(Compctl cc, char *s, int incmd)
-{
-    cc->refc++;
-    addlinknode(ccused, cc);
-
-    ccont = 0;
-
-    makecomplistor(cc, s, incmd, 0, 0);
-}
-
-/* This adds the completions for one run of [x]or'ed completions. */
-
-/**/
-static void
-makecomplistor(Compctl cc, char *s, int incmd, int compadd, int sub)
-{
-    int mn, ct, um = usemenu;
-
-    /* Loop over xors. */
-    do {
-	mn = mnum;
-
-	/* Loop over ors. */
-	do {
-	    /* Reset the range information if we are not in a sub-list. */
-	    if (!sub) {
-		brange = 0;
-		erange = clwnum - 1;
-	    }
-	    usemenu = 0;
-	    makecomplistlist(cc, s, incmd, compadd);
-	    um |= usemenu;
-
-	    ct = cc->mask2 & CC_XORCONT;
-
-	    cc = cc->xor;
-	} while (cc && ct);
-
-	/* Stop if we got some matches. */
-	if (mn != mnum)
-	    break;
-	if (cc) {
-	    ccont &= ~(CC_DEFCONT | CC_PATCONT);
-	    if (!sub)
-		ccont &= ~CC_CCCONT;
-	}
-    } while (cc);
-
-    usemenu = um;
-}
-
-/* This dispatches for simple and extended completion. */
-
-/**/
-static void
-makecomplistlist(Compctl cc, char *s, int incmd, int compadd)
-{
-    int oloffs = offs, owe = we, owb = wb, ocs = cs;
-
-    if (cc->ext)
-	/* Handle extended completion. */
-	makecomplistext(cc, s, incmd);
-    else
-	/* Only normal flags. */
-	makecomplistflags(cc, s, incmd, compadd);
-
-    /* Reset some information variables for the next try. */
-    errflag = 0;
-    offs = oloffs;
-    wb = owb;
-    we = owe;
-    cs = ocs;
-}
-
-/* This add matches for extended completion patterns */
-
-/**/
-static void
-makecomplistext(Compctl occ, char *os, int incmd)
-{
-    Compctl compc;
-    Compcond or, cc;
-    Patprog pprog;
-    int compadd, m = 0, d = 0, t, tt, i, j, a, b, ins;
-    char *sc = NULL, *s, *ss;
-
-    ins = (instring ? instring : (inbackt ? 3 : 0));
-
-    /* This loops over the patterns separated by `-'s. */
-    for (compc = occ->ext; compc; compc = compc->next) {
-	compadd = t = brange = 0;
-	erange = clwnum - 1;
-	/* This loops over OR'ed patterns. */
-	for (cc = compc->cond; cc && !t; cc = or) {
-	    or = cc->or;
-	    /* This loops over AND'ed patterns. */
-	    for (t = 1; cc && t; cc = cc->and) {
-		/* And this loops over [...] pairs. */
-		for (t = i = 0; i < cc->n && !t; i++) {
-		    s = NULL;
-		    brange = 0;
-		    erange = clwnum - 1;
-		    switch (cc->type) {
-		    case CCT_QUOTE:
-			t = ((cc->u.s.s[i][0] == 's' && ins == 1) ||
-			     (cc->u.s.s[i][0] == 'd' && ins == 2) ||
-			     (cc->u.s.s[i][0] == 'b' && ins == 3));
-			break;
-		    case CCT_POS:
-			tt = clwpos;
-			goto cct_num;
-		    case CCT_NUMWORDS:
-			tt = clwnum;
-		    cct_num:
-			if ((a = cc->u.r.a[i]) < 0)
-			    a += clwnum;
-			if ((b = cc->u.r.b[i]) < 0)
-			    b += clwnum;
-			if (cc->type == CCT_POS)
-			    brange = a, erange = b;
-			t = (tt >= a && tt <= b);
-			break;
-		    case CCT_CURSUF:
-		    case CCT_CURPRE:
-			s = ztrdup(clwpos < clwnum ? os : "");
-			untokenize(s);
-			if (isset(COMPLETEINWORD)) s[offs] = '\0';
-			sc = rembslash(cc->u.s.s[i]);
-			a = strlen(sc);
-			if (!strncmp(s, sc, a)) {
-			    compadd = (cc->type == CCT_CURSUF ? a : 0);
-			    t = 1;
-			}
-			break;
-		    case CCT_CURSUB:
-		    case CCT_CURSUBC:
-			if (clwpos < 0 || clwpos >= clwnum)
-			    t = 0;
-			else {
-			    s = ztrdup(os);
-			    untokenize(s);
-			    if (isset(COMPLETEINWORD)) s[offs] = '\0';
-			    a = getcpat(s,
-					cc->u.s.p[i],
-					cc->u.s.s[i],
-					cc->type == CCT_CURSUBC);
-			    if (a != -1)
-				compadd = a, t = 1;
-			}
-			break;
-			
-		    case CCT_CURPAT:
-		    case CCT_CURSTR:
-			tt = clwpos;
-			goto cct_str;
-		    case CCT_WORDPAT:
-		    case CCT_WORDSTR:
-			tt = 0;
-		    cct_str:
-			if ((a = tt + cc->u.s.p[i]) < 0)
-			    a += clwnum;
-			s = ztrdup((a < 0 || a >= clwnum) ? "" :
-				   clwords[a]);
-			untokenize(s);
-			
-			if (cc->type == CCT_CURPAT ||
-			    cc->type == CCT_WORDPAT) {
-			    tokenize(ss = dupstring(cc->u.s.s[i]));
-			    t = ((pprog = patcompile(ss, PAT_STATIC, NULL)) &&
-				 pattry(pprog, s));
-			} else
-			    t = (!strcmp(s, rembslash(cc->u.s.s[i])));
-			break;
-		    case CCT_RANGESTR:
-		    case CCT_RANGEPAT:
-			if (cc->type == CCT_RANGEPAT)
-			    tokenize(sc = dupstring(cc->u.l.a[i]));
-			for (j = clwpos - 1; j > 0; j--) {
-			    untokenize(s = ztrdup(clwords[j]));
-			    if (cc->type == CCT_RANGESTR)
-				sc = rembslash(cc->u.l.a[i]);
-			    if (cc->type == CCT_RANGESTR ?
-				!strncmp(s, sc, strlen(sc)) :
-				((pprog = patcompile(sc, PAT_STATIC, 0)) &&
-				 pattry(pprog, s))) {
-				zsfree(s);
-				brange = j + 1;
-				t = 1;
-				break;
-			    }
-			    zsfree(s);
-			}
-			if (t && cc->u.l.b[i]) {
-			    if (cc->type == CCT_RANGEPAT)
-				tokenize(sc = dupstring(cc->u.l.b[i]));
-			    for (j++; j < clwnum; j++) {
-				untokenize(s = ztrdup(clwords[j]));
-				if (cc->type == CCT_RANGESTR)
-				    sc = rembslash(cc->u.l.b[i]);
-				if (cc->type == CCT_RANGESTR ?
-				    !strncmp(s, sc, strlen(sc)) :
-				    ((pprog = patcompile(sc, PAT_STATIC, 0)) &&
-				     pattry(pprog, s))) {
-				    zsfree(s);
-				    erange = j - 1;
-				    t = clwpos <= erange;
-				    break;
-				}
-				zsfree(s);
-			    }
-			}
-			s = NULL;
-		    }
-		    zsfree(s);
-		}
-	    }
-	}
-	if (t) {
-	    /* The patterns matched, use the flags. */
-	    m = 1;
-	    ccont &= ~(CC_PATCONT | CC_DEFCONT);
-	    makecomplistor(compc, os, incmd, compadd, 1);
-	    if (!d && (ccont & CC_DEFCONT)) {
-		d = 1;
-		compadd = 0;
-		brange = 0;
-		erange = clwnum - 1;
-		makecomplistflags(occ, os, incmd, 0);
-	    }
-	    if (!(ccont & CC_PATCONT))
-		break;
-	}
-    }
-    /* If no pattern matched, use the standard flags. */
-    if (!m) {
-	compadd = 0;
-	brange = 0;
-	erange = clwnum - 1;
-	makecomplistflags(occ, os, incmd, 0);
-    }
-}
-
-/* This returns the node with the given data. */
-/* ...should probably be moved to linklist.c. */
-
-static LinkNode
-findnode(LinkList list, void *dat)
-{
-    LinkNode tmp = list->first;
-
-    while (tmp && tmp->dat != dat) tmp = tmp->next;
-
-    return tmp;
-}
-
-/* This adds the completions for the flags in the given compctl. */
-
-/**/
-static void
-makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
-{
-    int t, sf1, sf2, ooffs, um = usemenu, delit, oaw, gflags;
-    int mn = mnum, ohp = haspattern;
-    char *p, *sd = NULL, *tt, *s1, *s2, *os =  dupstring(s);
-    struct cmlist ms;
-
-    ccont |= (cc->mask2 & (CC_CCCONT | CC_DEFCONT | CC_PATCONT));
-
-    if (incompfunc != 1 && findnode(ccstack, cc))
-	return;
-
-    MUSTUSEHEAP("complistflags");
-
-    addlinknode(ccstack, cc);
-
-    if (incompfunc != 1 && allccs) {
-	if (findnode(allccs, cc)) {
-	    uremnode(ccstack, firstnode(ccstack));
-	    return;
-	}
-	addlinknode(allccs, cc);
-    }
-    /* Go to the end of the word if complete_in_word is not set. */
-    if (unset(COMPLETEINWORD) && cs != we)
-	cs = we, offs = strlen(s);
-
-    s = dupstring(s);
-    delit = ispattern = 0;
-    usemenu = um;
-    patcomp = filecomp = NULL;
-    rpre = rsuf = lpre = lsuf = ppre = psuf = lppre = lpsuf =
-	fpre = fsuf = ipre = ripre = prpre = 
-	qfpre = qfsuf = qrpre = qrsuf = qlpre = qlsuf = NULL;
-
-    curcc = cc;
-
-    mflags = 0;
-    gflags = (((cc->mask2 & CC_NOSORT ) ? CGF_NOSORT  : 0) |
-	      ((cc->mask2 & CC_UNIQALL) ? CGF_UNIQALL : 0) |
-	      ((cc->mask2 & CC_UNIQCON) ? CGF_UNIQCON : 0));
-    if (cc->gname) {
-	endcmgroup(NULL);
-	begcmgroup(cc->gname, gflags);
-    }
-    if (cc->ylist) {
-	endcmgroup(NULL);
-	begcmgroup(NULL, gflags);
-    }
-    if (cc->mask & CC_REMOVE)
-	mflags |= CMF_REMOVE;
-    if (cc->explain) {
-	expl = (Cexpl) zhalloc(sizeof(struct cexpl));
-	expl->count = expl->fcount = 0;
-    } else
-	expl = NULL;
-    /* compadd is the number of characters we have to ignore at the  *
-     * beginning of the word.                                        */
-    if (compadd) {
-	ipre = dupstring(s);
-	ipre[compadd] = '\0';
-	untokenize(ipre);
-	wb += compadd;
-	s += compadd;
-	if ((offs -= compadd) < 0) {
-	    /* It's bigger than our word prefix, so we can't help here... */
-	    uremnode(ccstack, firstnode(ccstack));
-	    return;
-	}
-    } else
-	ipre = NULL;
-
-    if (cc->matcher) {
-	ms.next = mstack;
-	ms.matcher = cc->matcher;
-	mstack = &ms;
-
-	if (!mnum)
-	    add_bmatchers(cc->matcher);
-
-	addlinknode(matchers, cc->matcher);
-	cc->matcher->refc++;
-    }
-    if (mnum && (mstack || bmatchers))
-	update_bmatchers();
-
-    /* Insert the prefix (compctl -P), if any. */
-    if (cc->prefix) {
-	int pl = 0;
-
-	if (*s) {
-	    char *dp = rembslash(cc->prefix);
-	    /* First find out how much of the prefix is already on the line. */
-	    sd = dupstring(s);
-	    untokenize(sd);
-	    pl = pfxlen(dp, sd);
-	    s += pl;
-	    sd += pl;
-	    offs -= pl;
-	}
-    }
-    /* Does this compctl have a suffix (compctl -S)? */
-    if (cc->suffix) {
-	char *sdup = rembslash(cc->suffix);
-	int sl = strlen(sdup), suffixll;
-
-	/* Ignore trailing spaces. */
-	for (p = sdup + sl - 1; p >= sdup && *p == ' '; p--, sl--);
-	p[1] = '\0';
-
-	if (!sd) {
-	    sd = dupstring(s);
-	    untokenize(sd);
-	}
-	/* If the suffix is already there, ignore it (and don't add *
-	 * it again).                                               */
-	if (*sd && (suffixll = strlen(sd)) >= sl &&
-	    offs <= suffixll - sl && !strcmp(sdup, sd + suffixll - sl))
-	    s[suffixll - sl] = '\0';
-    }
-    /* Do we have one of the special characters `~' and `=' at the beginning? */
-    if (incompfunc || ((ic = *s) != Tilde && ic != Equals))
-	ic = 0;
-
-    /* Check if we have to complete a parameter name... */
-    if (!incompfunc && (p = check_param(s, 1, 0))) {
-	s = p;
-	/* And now make sure that we complete parameter names. */
-	cc = &cc_dummy;
-	cc_dummy.refc = 10000;
-	cc_dummy.mask = CC_PARAMS | CC_ENVVARS;
-    }
-    ooffs = offs;
-    /* If we have to ignore the word, do that. */
-    if (cc->mask & CC_DELETE) {
-	delit = 1;
-	*s = '\0';
-	offs = 0;
-	if (isset(AUTOMENU))
-	    usemenu = 1;
-    }
-
-    /* Compute line prefix/suffix. */
-    lpl = offs;
-    lpre = zhalloc(lpl + 1);
-    memcpy(lpre, s, lpl);
-    lpre[lpl] = '\0';
-    qlpre = quotename(lpre, NULL);
-    lsuf = dupstring(s + offs);
-    lsl = strlen(lsuf);
-    qlsuf = quotename(lsuf, NULL);
-
-    /* First check for ~.../... */
-    if (ic == Tilde) {
-	for (p = lpre + lpl; p > lpre; p--)
-	    if (*p == '/')
-		break;
-
-	if (*p == '/')
-	    ic = 0;
-    }
-    /* Compute real prefix/suffix. */
-
-    noreal = !delit;
-    for (p = lpre; *p && *p != String && *p != Tick; p++);
-    tt = ic && !ispar ? lpre + 1 : lpre;
-    rpre = (*p || *lpre == Tilde || *lpre == Equals) ?
-	(noreal = 0, getreal(tt)) :
-	dupstring(tt);
-    qrpre = quotename(rpre, NULL);
-
-    for (p = lsuf; *p && *p != String && *p != Tick; p++);
-    rsuf = *p ? (noreal = 0, getreal(lsuf)) : dupstring(lsuf);
-    qrsuf = quotename(rsuf, NULL);
-
-    /* Check if word is a pattern. */
-
-    for (s1 = NULL, sf1 = 0, p = rpre + (rpl = strlen(rpre)) - 1;
-	 p >= rpre && (ispattern != 3 || !sf1);
-	 p--)
-	if (itok(*p) && (p > rpre || (*p != Equals && *p != Tilde)))
-	    ispattern |= sf1 ? 1 : 2;
-	else if (*p == '/') {
-	    sf1++;
-	    if (!s1)
-		s1 = p;
-	}
-    rsl = strlen(rsuf);
-    for (s2 = NULL, sf2 = t = 0, p = rsuf; *p && (!t || !sf2); p++)
-	if (itok(*p))
-	    t |= sf2 ? 4 : 2;
-	else if (*p == '/') {
-	    sf2++;
-	    if (!s2)
-		s2 = p;
-	}
-    ispattern = ispattern | t;
-
-    /* But if we were asked not to do glob completion, we never treat the *
-     * thing as a pattern.                                                */
-    if (!comppatmatch || !*comppatmatch)
-	ispattern = 0;
-
-    if (ispattern) {
-	/* The word should be treated as a pattern, so compute the matcher. */
-	p = (char *) zhalloc(rpl + rsl + 2);
-	strcpy(p, rpre);
-	if (rpl && p[rpl - 1] != Star &&
-	    (!comppatmatch || *comppatmatch == '*')) {
-	    p[rpl] = Star;
-	    strcpy(p + rpl + 1, rsuf);
-	} else
-	    strcpy(p + rpl, rsuf);
-	patcomp = patcompile(p, 0, NULL);
-	haspattern = 1;
-    }
-    if (!patcomp) {
-	untokenize(rpre);
-	untokenize(rsuf);
-
-	rpl = strlen(rpre);
-	rsl = strlen(rsuf);
-    }
-    untokenize(lpre);
-    untokenize(lsuf);
-
-    if (!(cc->mask & CC_DELETE))
-	hasmatched = 1;
-
-    /* Handle completion of files specially (of course). */
-
-    if ((cc->mask & (CC_FILES | CC_DIRS | CC_COMMPATH)) || cc->glob) {
-	/* s1 and s2 point to the last/first slash in the prefix/suffix. */
-	if (!s1)
-	    s1 = rpre;
-	if (!s2)
-	    s2 = rsuf + rsl;
-
-	/* Compute the path prefix/suffix. */
-	if (*s1 != '/')
-	    ppre = "";
-	else
-	    ppre = dupstrpfx(rpre, s1 - rpre + 1);
-	psuf = dupstring(s2);
-
-	if (cs != wb) {
-	    char save = line[cs];
-
-	    line[cs] = 0;
-	    lppre = dupstring((char *) line + wb +
-			      (qipre && *qipre ?
-			       (strlen(qipre) -
-				(*qipre == '\'' || *qipre == '\"')) : 0));
-	    line[cs] = save;
-	    if (brbeg) {
-		Brinfo bp;
-
-		for (bp = brbeg; bp; bp = bp->next)
-		    strcpy(lppre + bp->qpos,
-			   lppre + bp->qpos + strlen(bp->str));
-	    }
-	    if ((p = strrchr(lppre, '/'))) {
-		p[1] = '\0';
-		lppl = strlen(lppre);
-	    } else if (!sf1) {
-		lppre = NULL;
-		lppl = 0;
-	    } else {
-		lppre = ppre;
-		lppl = strlen(lppre);
-	    }
-	} else {
-	    lppre = NULL;
-	    lppl = 0;
-	}
-	if (cs != we) {
-	    int end = we;
-	    char save = line[end];
-
-	    if (qisuf && *qisuf) {
-		int ql = strlen(qisuf);
-
-		end -= ql - (qisuf[ql-1] == '\'' || qisuf[ql-1] == '"');
-	    }
-	    line[end] = 0;
-	    lpsuf = dupstring((char *) (line + cs));
-	    line[end] = save;
-	    if (brend) {
-		Brinfo bp;
-		char *p;
-		int bl;
-
-		for (bp = brend; bp; bp = bp->next) {
-		    p = lpsuf + (we - cs) - bp->qpos - (bl = strlen(bp->str));
-		    strcpy(p, p + bl);
-		}
-	    }
-	    if (!(lpsuf = strchr(lpsuf, '/')) && sf2)
-		lpsuf = psuf;
-	    lpsl = (lpsuf ? strlen(lpsuf) : 0);
-	} else {
-	    lpsuf = NULL;
-	    lpsl = 0;
-	}
-
-	/* And get the file prefix. */
-	fpre = dupstring(((s1 == s || s1 == rpre || ic) &&
-			  (*s != '/' || cs == wb)) ? s1 : s1 + 1);
-	qfpre = quotename(fpre, NULL);
-	/* And the suffix. */
-	fsuf = dupstrpfx(rsuf, s2 - rsuf);
-	qfsuf = quotename(fsuf, NULL);
-
-	if (comppatmatch && *comppatmatch && (ispattern & 2)) {
-	    int t2;
-
-	    /* We have to use globbing, so compute the pattern from *
-	     * the file prefix and suffix with a `*' between them.  */
-	    p = (char *) zhalloc((t2 = strlen(fpre)) + strlen(fsuf) + 2);
-	    strcpy(p, fpre);
-	    if ((!t2 || p[t2 - 1] != Star) && *fsuf != Star &&
-		(!comppatmatch || *comppatmatch == '*'))
-		p[t2++] = Star;
-	    strcpy(p + t2, fsuf);
-	    filecomp = patcompile(p, 0, NULL);
-	}
-	if (!filecomp) {
-	    untokenize(fpre);
-	    untokenize(fsuf);
-
-	    fpl = strlen(fpre);
-	    fsl = strlen(fsuf);
-	}
-	addwhat = -1;
-
-	/* Completion after `~', maketildelist adds the usernames *
-	 * and named directories.                                 */
-	if (ic == Tilde) {
-	    char *oi = ipre;
-
-	    ipre = (ipre ? dyncat("~", ipre) : "~");
-	    maketildelist();
-	    ipre = oi;
-	} else if (ic == Equals) {
-	    /* Completion after `=', get the command names from *
-	     * the cmdnamtab and aliases from aliastab.         */
-	    char *oi = ipre;
-
-	    ipre = (ipre ? dyncat("=", ipre) : "=");
-	    if (isset(HASHLISTALL))
-		cmdnamtab->filltable(cmdnamtab);
-	    dumphashtable(cmdnamtab, -7);
-	    dumphashtable(aliastab, -2);
-	    ipre = oi;
-	} else {
-	    /* Normal file completion... */
-	    if (ispattern & 1) {
-		/* But with pattern matching. */
-		LinkList l = newlinklist();
-		LinkNode n;
-		int ng = opts[NULLGLOB];
-
-		opts[NULLGLOB] = 1;
-
-		addwhat = 0;
-		p = (char *) zhalloc(lpl + lsl + 3);
-		strcpy(p, lpre);
-		if (*lsuf != '*' && *lpre && lpre[lpl - 1] != '*')
-		    strcat(p, "*");
-		strcat(p, lsuf);
-		if (*lsuf && lsuf[lsl - 1] != '*' && lsuf[lsl - 1] != ')')
-		    strcat(p, "*");
-
-		/* Do the globbing. */
-		tokenize(p);
-		remnulargs(p);
-		addlinknode(l, p);
-		globlist(l);
-
-		if (nonempty(l)) {
-		    /* And add the resulting words. */
-		    mflags |= CMF_FILE;
-		    for (n = firstnode(l); n; incnode(n))
-			addmatch(getdata(n), NULL);
-		    mflags &= !CMF_FILE;
-		}
-		opts[NULLGLOB] = ng;
-	    } else {
-		char **dirs = 0, *ta[2];
-
-		/* No pattern matching. */
-		addwhat = CC_FILES;
-
-		if (cc->withd) {
-		    char **pp, **npp, *tp;
-		    int tl = strlen(ppre) + 2, pl;
-
-		    if ((pp = get_user_var(cc->withd))) {
-			dirs = npp =
-			    (char**) zhalloc(sizeof(char *)*(arrlen(pp)+1));
-			while (*pp) {
-			    pl = strlen(*pp);
-			    tp = (char *) zhalloc(strlen(*pp) + tl);
-			    strcpy(tp, *pp);
-			    tp[pl] = '/';
-			    strcpy(tp + pl + 1, ppre);
-			    *npp++ = tp;
-			    pp++;
-			}
-			*npp = '\0';
-		    }
-		}
-		if (!dirs) {
-		    dirs = ta;
-		    if (cc->withd) {
-			char *tp;
-			int pl = strlen(cc->withd);
-
-			ta[0] = tp = (char *) zhalloc(strlen(ppre) + pl + 2);
-			strcpy(tp, cc->withd);
-			tp[pl] = '/';
-			strcpy(tp + pl + 1, ppre);
-		    } else
-			ta[0] = ppre;
-		    ta[1] = NULL;
-		}
-		while (*dirs) {
-		    prpre = *dirs;
-
-		    if (sf2)
-			/* We are in the path, so add only directories. */
-			gen_matches_files(1, 0, 0);
-		    else {
-			if (cc->mask & CC_FILES)
-			    /* Add all files. */
-			    gen_matches_files(0, 0, 1);
-			else if (cc->mask & CC_COMMPATH) {
-			    /* Completion of command paths. */
-			    if (sf1 || cc->withd)
-				/* There is a path prefix, so add *
-				 * directories and executables.   */
-				gen_matches_files(1, 1, 0);
-			    else {
-				/* No path prefix, so add the things *
-				 * reachable via the PATH variable.  */
-				char **pc = path, *pp = prpre;
-
-				for (; *pc; pc++)
-				    if (!**pc || (pc[0][0] == '.' && !pc[0][1]))
-					break;
-				if (*pc) {
-				    prpre = "./";
-				    gen_matches_files(1, 1, 0);
-				    prpre = pp;
-				}
-			    }
-			} else if (cc->mask & CC_DIRS)
-			    gen_matches_files(1, 0, 0);
-			/* The compctl has a glob pattern (compctl -g). */
-			if (cc->glob) {
-			    int ns, pl = strlen(prpre), o, paalloc;
-			    char *g = dupstring(cc->glob), *pa;
-			    char *p2, *p3;
-			    int ne = noerrs, md = opts[MARKDIRS];
-
-			    /* These are used in the globbing code to make *
-			     * things a bit faster.                        */
-			    if (ispattern || mstack)
-				glob_pre = glob_suf = NULL;
-			    else {
-				glob_pre = fpre;
-				glob_suf = fsuf;
-			    }
-			    noerrs = 1;
-			    addwhat = -6;
-			    o = strlen(prpre);
-			    pa = (char *)zalloc(paalloc = o + PATH_MAX);
-			    strcpy(pa, prpre);
-			    opts[MARKDIRS] = 0;
-
-			    /* The compctl -g string may contain more than *
-			     * one pattern, so we need a loop.             */
-			    while (*g) {
-				LinkList l = newlinklist();
-				int ng;
-
-				/* Find the blank terminating the pattern. */
-				while (*g && inblank(*g))
-				    g++;
-				/* Oops, we already reached the end of the
-				   string. */
-				if (!*g)
-				    break;
-				for (p = g + 1; *p && !inblank(*p); p++)
-				    if (*p == '\\' && p[1])
-					p++;
-				/* Get the pattern string. */
-				tokenize(g = dupstrpfx(g, p - g));
-				if (*g == '=')
-				    *g = Equals;
-				if (*g == '~')
-				    *g = Tilde;
-				remnulargs(g);
-				if ((*g == Equals || *g == Tilde) && !cc->withd) {
-				/* The pattern has a `~' or `=' at the  *
-				 * beginning, so we expand this and use *
-				 * the result.                          */
-				    filesub(&g, 0);
-				    addlinknode(l, dupstring(g));
-				} else if (*g == '/' && !cc->withd)
-				/* The pattern is a full path (starting *
-				 * with '/'), so add it unchanged.      */
-				    addlinknode(l, dupstring(g));
-				else {
-				/* It's a simple pattern, so append it to *
-				 * the path we have on the command line.  */
-				    int minlen = o + strlen(g);
-				    if (minlen >= paalloc)
-					pa = (char *)
-					    zrealloc(pa, paalloc = minlen+1);
-				    strcpy(pa + o, g);
-				    addlinknode(l, dupstring(pa));
-				}
-				/* Do the globbing. */
-				ng = opts[NULLGLOB];
-				opts[NULLGLOB] = 1;
-				globlist(l);
-				opts[NULLGLOB] = ng;
-				/* Get the results. */
-				if (nonempty(l) && peekfirst(l)) {
-				    for (p2 = (char *)peekfirst(l); *p2; p2++)
-					if (itok(*p2))
-					    break;
-				    if (!*p2) {
-					if ((*g == Equals || *g == Tilde ||
-					     *g == '/') || cc->withd) {
-					    /* IF the pattern started with `~',  *
-					     * `=', or `/', add the result only, *
-					     * if it really matches what we have *
-					     * on the line.                      *
-					     * Do this if an initial directory   *
-					     * was specified, too.               */
-					    while ((p2 = (char *)ugetnode(l)))
-						if (strpfx(prpre, p2))
-						    addmatch(p2 + pl, NULL);
-					} else {
-					    /* Otherwise ignore the path we *
-					     * prepended to the pattern.    */
-					    while ((p2 = p3 =
-						    (char *)ugetnode(l))) {
-						for (ns = sf1; *p3 && ns; p3++)
-						    if (*p3 == '/')
-							ns--;
-
-						addmatch(p3, NULL);
-					    }
-					}
-				    }
-				}
-				pa[o] = '\0';
-				g = p;
-			    }
-			    glob_pre = glob_suf = NULL;
-			    noerrs = ne;
-			    opts[MARKDIRS] = md;
-
-			    zfree(pa, paalloc);
-			}
-		    }
-		    dirs++;
-		}
-		prpre = NULL;
-	    }
-	}
-	lppre = lpsuf = NULL;
-	lppl = lpsl = 0;
-    }
-    if (ic) {
-	/* Now change the `~' and `=' tokens to the real characters so *
-	 * that things starting with these characters will be added.   */
-	rpre = dyncat((ic == Tilde) ? "~" : "=", rpre);
-	rpl++;
-	qrpre = dyncat((ic == Tilde) ? "~" : "=", qrpre);
-    }
-    if (!ic && (cc->mask & CC_COMMPATH) && !*ppre && !*psuf) {
-	/* If we have to complete commands, add alias names, *
-	 * shell functions and builtins too.                 */
-	dumphashtable(aliastab, -3);
-	dumphashtable(reswdtab, -3);
-	dumphashtable(shfunctab, -3);
-	dumphashtable(builtintab, -3);
-	if (isset(HASHLISTALL))
-	    cmdnamtab->filltable(cmdnamtab);
-	dumphashtable(cmdnamtab, -3);
-	/* And parameter names if autocd and cdablevars are set. */
-	if (isset(AUTOCD) && isset(CDABLEVARS))
-	    dumphashtable(paramtab, -4);
-    }
-    oaw = addwhat = (cc->mask & CC_QUOTEFLAG) ? -2 : CC_QUOTEFLAG;
-
-    if (cc->mask & CC_NAMED)
-	/* Add named directories. */
-	dumphashtable(nameddirtab, addwhat);
-    if (cc->mask & CC_OPTIONS)
-	/* Add option names. */
-	dumphashtable(optiontab, addwhat);
-    if (cc->mask & CC_VARS) {
-	/* And parameter names. */
-	dumphashtable(paramtab, -9);
-	addwhat = oaw;
-    }
-    if (cc->mask & CC_BINDINGS) {
-	/* And zle function names... */
-	dumphashtable(thingytab, CC_BINDINGS);
-	addwhat = oaw;
-    }
-    if (cc->keyvar) {
-	/* This adds things given to the compctl -k flag *
-	 * (from a parameter or a list of words).        */
-	char **usr = get_user_var(cc->keyvar);
-
-	if (usr)
-	    while (*usr)
-		addmatch(*usr++, NULL);
-    }
-    if (cc->mask & CC_USERS) {
-	/* Add user names. */
-	maketildelist();
-	addwhat = oaw;
-    }
-    if (cc->widget) {
-	char **ocfa = cfargs;
-	int ocfr = cfret;
-
-	cfargs = zlenoargs;
-	callcompfunc(os, cc->widget);
-	cfargs = ocfa;
-	cfret = ocfr;
-    }
-    if (cc->func) {
-	/* This handles the compctl -K flag. */
-	List list;
-	char **r;
-	int lv = lastval;
-	    
-	/* Get the function. */
-	if ((list = getshfunc(cc->func)) != &dummy_list) {
-	    /* We have it, so build a argument list. */
-	    LinkList args = newlinklist();
-	    int osc = sfcontext;
-		
-	    addlinknode(args, cc->func);
-		
-	    if (delit) {
-		p = dupstrpfx(os, ooffs);
-		untokenize(p);
-		addlinknode(args, p);
-		p = dupstring(os + ooffs);
-		untokenize(p);
-		addlinknode(args, p);
-	    } else {
-		addlinknode(args, lpre);
-		addlinknode(args, lsuf);
-	    }
-		
-	    /* This flag allows us to use read -l and -c. */
-	    if (incompfunc != 1)
-		incompctlfunc = 1;
-	    sfcontext = SFC_COMPLETE;
-	    /* Call the function. */
-	    doshfunc(cc->func, list, args, 0, 1);
-	    sfcontext = osc;
-	    incompctlfunc = 0;
-	    /* And get the result from the reply parameter. */
-	    if ((r = get_user_var("reply")))
-		while (*r)
-		    addmatch(*r++, NULL);
-	}
-	lastval = lv;
-    }
-    if (cc->mask & (CC_JOBS | CC_RUNNING | CC_STOPPED)) {
-	/* Get job names. */
-	int i;
-	char *j;
-
-	for (i = 0; i < MAXJOB; i++)
-	    if ((jobtab[i].stat & STAT_INUSE) &&
-		jobtab[i].procs && jobtab[i].procs->text) {
-		int stopped = jobtab[i].stat & STAT_STOPPED;
-
-		j = dupstring(jobtab[i].procs->text);
-		if ((cc->mask & CC_JOBS) ||
-		    (stopped && (cc->mask & CC_STOPPED)) ||
-		    (!stopped && (cc->mask & CC_RUNNING)))
-		    addmatch(j, NULL);
-	    }
-    }
-    if (cc->str) {
-	/* Get the stuff from a compctl -s. */
-	LinkList foo = newlinklist();
-	LinkNode n;
-	int first = 1, ng = opts[NULLGLOB], oowe = we, oowb = wb;
-	char *tmpbuf;
-
-	opts[NULLGLOB] = 1;
-
-	/* Put the string in the lexer buffer and call the lexer to *
-	 * get the words we have to expand.                        */
-	zleparse = 1;
-	lexsave();
-	tmpbuf = (char *)zhalloc(strlen(cc->str) + 5);
-	sprintf(tmpbuf, "foo %s", cc->str); /* KLUDGE! */
-	inpush(tmpbuf, 0, NULL);
-	strinbeg(0);
-	noaliases = 1;
-	do {
-	    ctxtlex();
-	    if (tok == ENDINPUT || tok == LEXERR)
-		break;
-	    if (!first && tokstr && *tokstr)
-		addlinknode(foo, ztrdup(tokstr));
-	    first = 0;
-	} while (tok != ENDINPUT && tok != LEXERR);
-	noaliases = 0;
-	strinend();
-	inpop();
-	errflag = zleparse = 0;
-	lexrestore();
-	/* Fine, now do full expansion. */
-	prefork(foo, 0);
-	if (!errflag) {
-	    globlist(foo);
-	    if (!errflag)
-		/* And add the resulting words as matches. */
-		for (n = firstnode(foo); n; incnode(n))
-		    addmatch((char *)n->dat, NULL);
-	}
-	opts[NULLGLOB] = ng;
-	we = oowe;
-	wb = oowb;
-    }
-    if (cc->hpat) {
-	/* We have a pattern to take things from the history. */
-	Patprog pprogc = NULL;
-	char *e, *h, hpatsav;
-	int i = addhistnum(curhist,-1,HIST_FOREIGN), n = cc->hnum;
-	Histent he = quietgethistent(i, GETHIST_UPWARD);
-
-	/* Parse the pattern, if it isn't the null string. */
-	if (*(cc->hpat)) {
-	    char *thpat = dupstring(cc->hpat);
-
-	    tokenize(thpat);
-	    pprogc = patcompile(thpat, 0, NULL);
-	}
-	/* n holds the number of history line we have to search. */
-	if (!n)
-	    n = -1;
-
-	/* Now search the history. */
-	while (n-- && he) {
-	    int iwords;
-	    for (iwords = he->nwords - 1; iwords >= 0; iwords--) {
-		h = he->text + he->words[iwords*2];
-		e = he->text + he->words[iwords*2+1];
-		hpatsav = *e;
-		*e = '\0';
-		/* We now have a word from the history, ignore it *
-		 * if it begins with a quote or `$'.              */
-		if (*h != '\'' && *h != '"' && *h != '`' && *h != '$' &&
-		    (!pprogc || pattry(pprogc, h)))
-		    /* Otherwise add it if it was matched. */
-		    addmatch(dupstring(h), NULL);
-		if (hpatsav)
-		    *e = hpatsav;
-	    }
-	    he = up_histent(he);
-	}
-    }
-    if ((t = cc->mask & (CC_ARRAYS | CC_INTVARS | CC_ENVVARS | CC_SCALARS |
-			 CC_READONLYS | CC_SPECIALS | CC_PARAMS)))
-	/* Add various flavours of parameters. */
-	dumphashtable(paramtab, t);
-    if ((t = cc->mask & CC_SHFUNCS))
-	/* Add shell functions. */
-	dumphashtable(shfunctab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
-    if ((t = cc->mask & CC_BUILTINS))
-	/* Add builtins. */
-	dumphashtable(builtintab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
-    if ((t = cc->mask & CC_EXTCMDS)) {
-	/* Add external commands */
-	if (isset(HASHLISTALL))
-	    cmdnamtab->filltable(cmdnamtab);
-	dumphashtable(cmdnamtab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
-    }
-    if ((t = cc->mask & CC_RESWDS))
-	/* Add reserved words */
-	dumphashtable(reswdtab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
-    if ((t = cc->mask & (CC_ALREG | CC_ALGLOB)))
-	/* Add the two types of aliases. */
-	dumphashtable(aliastab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
-    if (keypm && cc == &cc_dummy) {
-	/* Add the keys of the parameter in keypm. */
-	scanhashtable(keypm->gets.hfn(keypm), 0, 0, PM_UNSET, addhnmatch, 0);
-	keypm = NULL;
-	cc_dummy.suffix = NULL;
-    }
-    if (!errflag && cc->ylist) {
-	/* generate the user-defined display list: if anything fails, *
-	 * we silently allow the normal completion list to be used.   */
-	char **yaptr = NULL, *uv = NULL;
-	List list;
-
-	if (cc->ylist[0] == '$' || cc->ylist[0] == '(') {
-	    /* from variable */
-	    uv = cc->ylist + (cc->ylist[0] == '$');
-	} else if ((list = getshfunc(cc->ylist)) != &dummy_list) {
-	    /* from function:  pass completions as arg list */
-	    LinkList args = newlinklist();
-	    LinkNode ln;
-	    Cmatch m;
-	    int osc = sfcontext;
-
-	    addlinknode(args, cc->ylist);
-	    for (ln = firstnode(matches); ln; ln = nextnode(ln)) {
-		m = (Cmatch) getdata(ln);
-		if (m->ppre) {
-		    char *p = (char *) zhalloc(strlen(m->ppre) + strlen(m->str) +
-					      strlen(m->psuf) + 1);
-
-		    sprintf(p, "%s%s%s", m->ppre, m->str, m->psuf);
-		    addlinknode(args, dupstring(p));
-		} else
-		    addlinknode(args, dupstring(m->str));
-	    }
-
-	    /* No harm in allowing read -l and -c here, too */
-	    if (incompfunc != 1)
-		incompctlfunc = 1;
-	    sfcontext = SFC_COMPLETE;
-	    doshfunc(cc->ylist, list, args, 0, 1);
-	    sfcontext = osc;
-	    incompctlfunc = 0;
-	    uv = "reply";
-	}
-	if (uv)
-	    yaptr = get_user_var(uv);
-	if ((tt = cc->explain)) {
-	    tt = dupstring(tt);
-	    if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) {
-		singsub(&tt);
-		untokenize(tt);
-	    }
-	    expl->str = tt;
-	    if (cc->gname) {
-		endcmgroup(yaptr);
-		begcmgroup(cc->gname, gflags);
-		addexpl();
-	    } else {
-		addexpl();
-		endcmgroup(yaptr);
-		begcmgroup("default", 0);
-	    }
-	}
-    } else if ((tt = cc->explain)) {
-	tt = dupstring(tt);
-	if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) {
-	    singsub(&tt);
-	    untokenize(tt);
-	}
-	expl->str = tt;
-	addexpl();
-    }
-    if (cc->subcmd) {
-	/* Handle -l sub-completion. */
-	char **ow = clwords, *os = cmdstr, *ops = NULL;
-	int oldn = clwnum, oldp = clwpos, br;
-	unsigned long occ = ccont;
-	
-	ccont = CC_CCCONT;
-	
-	/* So we restrict the words-array. */
-	if (brange >= clwnum)
-	    brange = clwnum - 1;
-	if (brange < 1)
-	    brange = 1;
-	if (erange >= clwnum)
-	    erange = clwnum - 1;
-	if (erange < 1)
-	    erange = 1;
-	clwnum = erange - brange + 1;
-	clwpos = clwpos - brange;
-	br = brange;
-
-	if (cc->subcmd[0]) {
-	    /* And probably put the command name given to the flag *
-	     * in the array.                                       */
-	    clwpos++;
-	    clwnum++;
-	    incmd = 0;
-	    ops = clwords[br - 1];
-	    clwords[br - 1] = ztrdup(cc->subcmd);
-	    cmdstr = ztrdup(cc->subcmd);
-	    clwords += br - 1;
-	} else {
-	    cmdstr = ztrdup(clwords[br]);
-	    incmd = !clwpos;
-	    clwords += br;
-	}
-	/* Produce the matches. */
-	makecomplistcmd(s, incmd, CFN_FIRST);
-
-	/* And restore the things we changed. */
-	clwords = ow;
-	zsfree(cmdstr);
-	cmdstr = os;
-	clwnum = oldn;
-	clwpos = oldp;
-	if (ops) {
-	    zsfree(clwords[br - 1]);
-	    clwords[br - 1] = ops;
-	}
-	ccont = occ;
-    }
-    if (cc->substr)
-	sep_comp_string(cc->substr, s, offs, 1);
-    uremnode(ccstack, firstnode(ccstack));
-    if (cc->matcher)
-	mstack = mstack->next;
-
-    if (mn == mnum)
-	haspattern = ohp;
-}
-
 /* Invalidate the completion list. */
 
 /**/
@@ -7812,14 +5748,14 @@ addexpl(void)
 
     for (n = firstnode(expls); n; incnode(n)) {
 	e = (Cexpl) getdata(n);
-	if (!strcmp(expl->str, e->str)) {
-	    e->count += expl->count;
-	    e->fcount += expl->fcount;
+	if (!strcmp(curexpl->str, e->str)) {
+	    e->count += curexpl->count;
+	    e->fcount += curexpl->fcount;
 
 	    return;
 	}
     }
-    addlinknode(expls, expl);
+    addlinknode(expls, curexpl);
     newmatches = 1;
 }
 
@@ -7880,7 +5816,6 @@ permmatches(int last)
     Cmgroup g = amatches, n;
     Cmatch *p, *q;
     Cexpl *ep, *eq, e, o;
-    Compctl *cp, *cq;
     LinkList mlist;
     static int fi = 0;
     int nn, nl, ll, gn = 1, mn = 1, rn;
@@ -7922,7 +5857,6 @@ permmatches(int last)
 					   NULL, NULL);
 
 	    g->ccount = 0;
-	    g->ccs = NULL;
 	} LASTALLOC;
 
 	nmatches += g->mcount;
@@ -7966,14 +5900,6 @@ permmatches(int last)
 	} else
 	    n->expls = NULL;
 
-	if ((n->ccount = g->ccount)) {
-	    n->ccs = cp = (Compctl *) ncalloc((n->ccount + 1) *
-					      sizeof(Compctl));
-	    for (cq = g->ccs; *cq; cq++, cp++)
-		*cp = *cq;
-	    *cp = NULL;
-	} else
-	    n->ccs = NULL;
 	n->widths = NULL;
 
 	g = g->next;
@@ -8080,7 +6006,6 @@ freematches(Cmgroup g)
     Cmgroup n;
     Cmatch *m;
     Cexpl *e;
-    Compctl *c;
 
     while (g) {
 	n = g->next;
@@ -8099,14 +6024,6 @@ freematches(Cmgroup g)
 	    }
 	    free(g->expls);
 	}
-	if ((c = g->ccs)) {
-	    while (*c) {
-		if (*c != &cc_dummy)
-		    freecompctl(*c);
-		c++;
-	    }
-	    free(g->ccs);
-	}
 	free(g);
 
 	g = n;
diff --git a/Src/Zle/zleparameter.c b/Src/Zle/zleparameter.c
new file mode 100644
index 000000000..8a5bc0bc2
--- /dev/null
+++ b/Src/Zle/zleparameter.c
@@ -0,0 +1,257 @@
+/*
+ * zleparameter.c - parameter interface to zle internals
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1999 Sven Wischnowsky
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Sven Wischnowsky or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Sven Wischnowsky and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Sven Wischnowsky and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose.  The software
+ * provided hereunder is on an "as is" basis, and Sven Wischnowsky and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "zleparameter.mdh"
+#include "zleparameter.pro"
+
+/* Empty dummy function for special hash parameters. */
+
+/**/
+static void
+shempty(void)
+{
+}
+
+/* Create a simple special hash parameter. */
+
+/**/
+static Param
+createspecialhash(char *name, GetNodeFunc get, ScanTabFunc scan)
+{
+    Param pm;
+    HashTable ht;
+
+    if (!(pm = createparam(name, PM_SPECIAL|PM_HIDE|PM_REMOVABLE|PM_HASHED)))
+	return NULL;
+
+    pm->level = pm->old ? locallevel : 0;
+    pm->gets.hfn = hashgetfn;
+    pm->sets.hfn = hashsetfn;
+    pm->unsetfn = stdunsetfn;
+    pm->u.hash = ht = newhashtable(7, name, NULL);
+
+    ht->hash        = hasher;
+    ht->emptytable  = (TableFunc) shempty;
+    ht->filltable   = NULL;
+    ht->addnode     = (AddNodeFunc) shempty;
+    ht->getnode     = ht->getnode2 = get;
+    ht->removenode  = (RemoveNodeFunc) shempty;
+    ht->disablenode = NULL;
+    ht->enablenode  = NULL;
+    ht->freenode    = (FreeNodeFunc) shempty;
+    ht->printnode   = printparamnode;
+    ht->scantab     = scan;
+
+    return pm;
+}
+
+/* Functions for the zlewidgets special parameter. */
+
+/**/
+static char *
+widgetstr(Widget w)
+{
+    if (w->flags & WIDGET_INT)
+	return dupstring("builtin");
+    if (w->flags & WIDGET_NCOMP) {
+	char *t = (char *) zhalloc(13 + strlen(w->u.comp.wid) +
+				   strlen(w->u.comp.func));
+
+	strcpy(t, "completion:");
+	strcat(t, w->u.comp.wid);
+	strcat(t, ":");
+	strcat(t, w->u.comp.func);
+
+	return t;
+    }
+    return dyncat("user:", w->u.fnnam);
+}
+
+/**/
+static HashNode
+getpmwidgets(HashTable ht, char *name)
+{
+    Param pm = NULL;
+    Thingy th;
+
+    HEAPALLOC {
+	pm = (Param) zhalloc(sizeof(struct param));
+	pm->nam = dupstring(name);
+	pm->flags = PM_SCALAR | PM_READONLY;
+	pm->sets.cfn = NULL;
+	pm->gets.cfn = strgetfn;
+	pm->unsetfn = NULL;
+	pm->ct = 0;
+	pm->env = NULL;
+	pm->ename = NULL;
+	pm->old = NULL;
+	pm->level = 0;
+	if ((th = (Thingy) thingytab->getnode(thingytab, name)) &&
+	    !(th->flags & DISABLED))
+	    pm->u.str = widgetstr(th->widget);
+	else {
+	    pm->u.str = dupstring("");
+	    pm->flags |= PM_UNSET;
+	}
+    } LASTALLOC;
+
+    return (HashNode) pm;
+}
+
+/**/
+static void
+scanpmwidgets(HashTable ht, ScanFunc func, int flags)
+{
+    struct param pm;
+    int i;
+    HashNode hn;
+
+    pm.flags = PM_SCALAR | PM_READONLY;
+    pm.sets.cfn = NULL;
+    pm.gets.cfn = strgetfn;
+    pm.unsetfn = NULL;
+    pm.ct = 0;
+    pm.env = NULL;
+    pm.ename = NULL;
+    pm.old = NULL;
+    pm.level = 0;
+
+    for (i = 0; i < thingytab->hsize; i++)
+	for (hn = thingytab->nodes[i]; hn; hn = hn->next) {
+	    pm.nam = hn->nam;
+	    if (func != scancountparams)
+		pm.u.str = widgetstr(((Thingy) hn)->widget);
+	    func((HashNode) &pm, flags);
+	}
+}
+
+/* Functions for the zlekeymaps special parameter. */
+
+static char **
+keymapsgetfn(Param pm)
+{
+    int i;
+    HashNode hn;
+    char **ret, **p;
+
+    p = ret = (char **) zhalloc((keymapnamtab->ct + 1) * sizeof(char *));
+
+    for (i = 0; i < keymapnamtab->hsize; i++)
+	for (hn = keymapnamtab->nodes[i]; hn; hn = hn->next)
+	    *p++ = dupstring(hn->nam);
+    *p = NULL;
+
+    return ret;
+}
+
+/* Table for defined parameters. */
+
+struct pardef {
+    char *name;
+    int flags;
+    GetNodeFunc getnfn;
+    ScanTabFunc scantfn;
+    void (*hsetfn) _((Param, HashTable));
+    void (*setfn) _((Param, char **));
+    char **(*getfn) _((Param));
+    void (*unsetfn) _((Param, int));
+    Param pm;
+};
+
+static struct pardef partab[] = {
+    { "zlewidgets", PM_READONLY,
+      getpmwidgets, scanpmwidgets, hashsetfn,
+      NULL, NULL, stdunsetfn, NULL },
+    { "zlekeymaps", PM_ARRAY|PM_HIDE|PM_SPECIAL|PM_READONLY,
+      NULL, NULL, NULL,
+      arrsetfn, keymapsgetfn, stdunsetfn, NULL },
+    { NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+};
+
+/**/
+int
+setup_zleparameter(Module m)
+{
+    return 0;
+}
+
+/**/
+int
+boot_zleparameter(Module m)
+{
+    struct pardef *def;
+
+    for (def = partab; def->name; def++) {
+	unsetparam(def->name);
+
+	if (def->getnfn) {
+	    if (!(def->pm = createspecialhash(def->name, def->getnfn,
+					      def->scantfn)))
+		return 1;
+	    def->pm->flags |= def->flags;
+	    if (def->hsetfn)
+		def->pm->sets.hfn = def->hsetfn;
+	} else {
+	    if (!(def->pm = createparam(def->name, def->flags)))
+		return 1;
+	    def->pm->sets.afn = def->setfn;
+	    def->pm->gets.afn = def->getfn;
+	    def->pm->unsetfn = def->unsetfn;
+	}
+    }
+    return 0;
+}
+
+#ifdef MODULE
+
+/**/
+int
+cleanup_zleparameter(Module m)
+{
+    Param pm;
+    struct pardef *def;
+
+    for (def = partab; def->name; def++) {
+	if ((pm = (Param) paramtab->getnode(paramtab, def->name)) &&
+	    pm == def->pm) {
+	    pm->flags &= ~PM_READONLY;
+	    unsetparam_pm(pm, 0, 1);
+	}
+    }
+    return 0;
+}
+
+/**/
+int
+finish_zleparameter(Module m)
+{
+    return 0;
+}
+
+#endif
diff --git a/Src/Zle/zleparameter.mdd b/Src/Zle/zleparameter.mdd
new file mode 100644
index 000000000..f9d528661
--- /dev/null
+++ b/Src/Zle/zleparameter.mdd
@@ -0,0 +1,5 @@
+moddeps="zle"
+
+autoparams="zlewidgets zlekeymaps"
+
+objects="zleparameter.o"
diff --git a/Src/hashtable.c b/Src/hashtable.c
index 86258e66f..d4c832f16 100644
--- a/Src/hashtable.c
+++ b/Src/hashtable.c
@@ -1119,6 +1119,17 @@ printaliasnode(HashNode hn, int printflags)
 /* Named Directory Hash Table Functions */
 /****************************************/
 
+#ifdef HAVE_NIS_PLUS
+# include <rpcsvc/nis.h>
+#else
+# ifdef HAVE_NIS
+#  include	<rpc/types.h>
+#  include	<rpc/rpc.h>
+#  include	<rpcsvc/ypclnt.h>
+#  include	<rpcsvc/yp_prot.h>
+# endif
+#endif
+
 /* hash table containing named directories */
 
 /**/
@@ -1168,12 +1179,102 @@ emptynameddirtable(HashTable ht)
 /* Add all the usernames in the password file/database *
  * to the named directories table.                     */
 
+#ifdef HAVE_NIS_PLUS
+static int
+add_userdir(nis_name table, nis_object *object, void *userdata)
+{
+    if (object->zo_data.objdata_u.en_data.en_cols.en_cols >= 6) {
+	static char name[40], dir[PATH_MAX + 1];
+	register entry_col *ec =
+	    object->zo_data.objdata_u.en_data.en_cols.en_cols_val;
+	register int nl = minimum(ec[0].ec_value.ec_value_len, 39);
+	register int dl = minimum(ec[5].ec_value.ec_value_len, PATH_MAX);
+
+	memcpy(name, ec[0].ec_value.ec_value_val, nl);
+	name[nl] = '\0';
+	memcpy(dir, ec[5].ec_value.ec_value_val, dl);
+	dir[dl] = '\0';
+
+	adduserdir(name, dir, ND_USERNAME, 1);
+    }
+    return 0;
+}
+#else
+# ifdef HAVE_NIS
+static int
+add_userdir(int status, char *key, int keylen, char *val, int vallen, char *dummy)
+{
+    char *p, *d, *de;
+
+    if (status != YP_TRUE)
+	return 1;
+
+    if (vallen > keylen && *(p = val + keylen) == ':') {
+	*p++ = '\0';
+	if ((de = strrchr(p, ':'))) {
+	    *de = '\0';
+	    if ((d = strrchr(p, ':'))) {
+		if (*++d && val[0])
+		    adduserdir(val, d, ND_USERNAME, 1);
+	    }
+	}
+    }
+    return 0;
+}
+# endif /* HAVE_NIS */
+#endif  /* HAVE_NIS_PLUS */
+
 /**/
 static void
 fillnameddirtable(HashTable ht)
 {
-#ifdef HAVE_GETPWENT
     if (!allusersadded) {
+#if defined(HAVE_NIS) || defined(HAVE_NIS_PLUS)
+	FILE *pwf;
+	char buf[BUFSIZ], *p, *d, *de;
+	int skipping;
+
+# ifndef HAVE_NIS_PLUS
+	char domain[YPMAXDOMAIN];
+	struct ypall_callback cb;
+
+	/* Get potential matches from NIS and cull those without local accounts */
+	if (getdomainname(domain, YPMAXDOMAIN) == 0) {
+	    cb.foreach = (int (*)()) add_userdir;
+	    cb.data = NULL;
+	    yp_all(domain, PASSWD_MAP, &cb);
+    }
+# else  /* HAVE_NIS_PLUS */
+	/* Maybe we should turn this string into a #define'd constant...? */
+
+	nis_list("passwd.org_dir", EXPAND_NAME|ALL_RESULTS|FOLLOW_LINKS|FOLLOW_PATH,
+		 add_userdir, 0);
+# endif
+	/* Don't forget the non-NIS matches from the flat passwd file */
+	if ((pwf = fopen(PASSWD_FILE, "r")) != NULL) {
+	    skipping = 0;
+	    while (fgets(buf, BUFSIZ, pwf) != NULL) {
+		if (strchr(buf, '\n') != NULL) {
+		    if (!skipping) {
+			if ((p = strchr(buf, ':')) != NULL) {
+			    *p++ = '\0';
+			    if ((de = strrchr(p, ':'))) {
+				*de = '\0';
+				if ((d = strrchr(p, ':'))) {
+				    if (*++d && buf[0])
+					adduserdir(buf, d, ND_USERNAME, 1);
+				}
+			    }
+			}
+		    } else
+			skipping = 0;
+		} else
+		    skipping = 1;
+	    }
+	    fclose(pwf);
+	}
+#else  /* no NIS or NIS_PLUS */
+#ifdef HAVE_GETPWENT
 	struct passwd *pw;
  
 	setpwent();
@@ -1181,13 +1282,13 @@ fillnameddirtable(HashTable ht)
 	/* loop through the password file/database *
 	 * and add all entries returned.           */
 	while ((pw = getpwent()) && !errflag)
-	    adduserdir(ztrdup(pw->pw_name), pw->pw_dir, ND_USERNAME, 1);
+	    adduserdir(pw->pw_name, pw->pw_dir, ND_USERNAME, 1);
  
 	endpwent();
+#endif /* HAVE_GETPWENT */
+#endif
 	allusersadded = 1;
     }
-    return;
-#endif /* HAVE_GETPWENT */
 }
 
 /* Add an entry to the named directory hash *
diff --git a/Src/module.c b/Src/module.c
index 9922f84df..e620073ce 100644
--- a/Src/module.c
+++ b/Src/module.c
@@ -65,6 +65,21 @@ register_module(char *n)
     } LASTALLOC;
 }
 
+/* Check if a module is linked in. */
+
+/**/
+int
+module_linked(char *name)
+{
+    LinkNode node;
+
+    for (node = firstnode(bltinmodules); node; incnode(node))
+	if (!strcmp((char *) getdata(node), name))
+	    return 1;
+
+    return 0;
+}
+
 /* addbuiltin() can be used to add a new builtin.  It returns zero on *
  * success, 1 on failure.  The only possible type of failure is that  *
  * a builtin with the specified name already exists.  An autoloaded   *
@@ -1401,7 +1416,7 @@ addhookdefs(char const *nam, Hookdef h, int size)
 
     while (size--) {
 	if (addhookdef(h)) {
-	    zwarnnam(nam, "name clash when adding condition `%s'", h->name, 0);
+	    zwarnnam(nam, "name clash when adding hook `%s'", h->name, 0);
 	    hadf = 1;
 	} else
 	    hads = 2;
diff --git a/Src/params.c b/Src/params.c
index 64aa7c865..4c4f89978 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -2012,7 +2012,7 @@ strsetfn(Param pm, char *x)
 /* Function to get value of an array parameter */
 
 /**/
-static char **
+char **
 arrgetfn(Param pm)
 {
     static char *nullarray = NULL;
@@ -2023,7 +2023,7 @@ arrgetfn(Param pm)
 /* Function to set value of an array parameter */
 
 /**/
-static void
+void
 arrsetfn(Param pm, char **x)
 {
     if (pm->u.arr && pm->u.arr != x)
diff --git a/Src/subst.c b/Src/subst.c
index ffe6217f0..00b22da28 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -1076,6 +1076,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		    val = dyncat(val, "-export");
 		if (f & PM_UNIQUE)
 		    val = dyncat(val, "-unique");
+		if (f & PM_HIDE)
+		    val = dyncat(val, "-hide");
+		if (f & PM_SPECIAL)
+		    val = dyncat(val, "-special");
 		vunset = 0;
 	    } else
 		val = dupstring("");
diff --git a/Src/xmods.conf b/Src/xmods.conf
index 7e5e2928a..def807911 100644
--- a/Src/xmods.conf
+++ b/Src/xmods.conf
@@ -1,6 +1,6 @@
 rlimits
-comp1
 zle
+complete
 compctl
 sched
 complist