about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog245
-rw-r--r--Completion/Base/_first72
-rw-r--r--Completion/Base/_subscript19
-rw-r--r--Completion/Builtins/_cd4
-rw-r--r--Completion/Commands/.distfiles2
-rw-r--r--Completion/Commands/_expand_word26
-rw-r--r--Completion/Core/_expand3
-rw-r--r--Completion/Core/_path_files6
-rw-r--r--Completion/Core/compdump13
-rw-r--r--Completion/Core/compinit93
-rw-r--r--Completion/Core/compinstall107
-rw-r--r--Completion/User/_hosts6
-rw-r--r--Config/.cvsignore1
-rw-r--r--Config/version.mk4
-rw-r--r--Doc/.distfiles10
-rw-r--r--Doc/Makefile.in13
-rw-r--r--Doc/Zsh/.distfiles2
-rw-r--r--Doc/Zsh/builtins.yo48
-rw-r--r--Doc/Zsh/compctl.yo5
-rw-r--r--Doc/Zsh/compsys.yo177
-rw-r--r--Doc/Zsh/compwid.yo4
-rw-r--r--Doc/Zsh/mod_compctl.yo2
-rw-r--r--Doc/Zsh/mod_complist.yo16
-rw-r--r--Doc/Zsh/mod_deltochar.yo2
-rw-r--r--Doc/Zsh/mod_zle.yo30
-rw-r--r--Doc/Zsh/modules.yo5
-rw-r--r--Doc/Zsh/options.yo5
-rw-r--r--Doc/Zsh/params.yo24
-rw-r--r--Doc/Zsh/prompt.yo14
-rw-r--r--Doc/Zsh/zle.yo7
-rw-r--r--Etc/FAQ.yo145
-rw-r--r--Misc/compctl-examples6
-rw-r--r--Src/Makefile.in4
-rw-r--r--Src/Zle/.distfiles1
-rw-r--r--Src/Zle/comp.h24
-rw-r--r--Src/Zle/compctl.c6
-rw-r--r--Src/Zle/complist.c141
-rw-r--r--Src/Zle/deltochar.c2
-rw-r--r--Src/Zle/iwidgets.list1
-rw-r--r--Src/Zle/zle.export36
-rw-r--r--Src/Zle/zle.h6
-rw-r--r--Src/Zle/zle_hist.c78
-rw-r--r--Src/Zle/zle_keymap.c65
-rw-r--r--Src/Zle/zle_main.c31
-rw-r--r--Src/Zle/zle_misc.c48
-rw-r--r--Src/Zle/zle_move.c35
-rw-r--r--Src/Zle/zle_params.c2
-rw-r--r--Src/Zle/zle_refresh.c2
-rw-r--r--Src/Zle/zle_thingy.c41
-rw-r--r--Src/Zle/zle_tricky.c359
-rw-r--r--Src/Zle/zle_utils.c24
-rw-r--r--Src/Zle/zle_vi.c126
-rw-r--r--Src/Zle/zle_word.c16
-rw-r--r--Src/builtin.c38
-rw-r--r--Src/exec.c80
-rw-r--r--Src/glob.c2
-rw-r--r--Src/hashtable.c1
-rw-r--r--Src/init.c7
-rw-r--r--Src/jobs.c55
-rw-r--r--Src/loop.c2
-rw-r--r--Src/module.c254
-rw-r--r--Src/params.c9
-rw-r--r--Src/prompt.c8
-rw-r--r--Src/signals.c5
-rw-r--r--Src/subst.c11
-rw-r--r--Src/utils.c16
-rw-r--r--Src/xmods.conf1
-rw-r--r--Src/zsh.export5
-rw-r--r--Src/zsh.h40
-rw-r--r--Util/zsh-development-guide148
-rw-r--r--aczsh.m42
71 files changed, 1937 insertions, 911 deletions
diff --git a/ChangeLog b/ChangeLog
index 9dde3dac4..91e131201 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,248 @@
+1999-07-03  Peter Stephenson  <pws@ibmth.difi.unipi.it>
+
+	* pws: 6955, 6956: Completion/Core/compinit,
+	  Completion/Core/compinstall, Doc/Zsh/compsys.yo: better handling
+	  of subdirectory structure for completion functions; -d is default
+	  for compinit, -D turns it off; documentation updated for installed
+	  files; bugfix for compinstall re-using compconf arguments.
+
+1999-07-02  Peter Stephenson  <pws@ibmth.difi.unipi.it>
+
+	* Tanaka Akira: 6953: Completion/Builtins/_cd: use pushd code for cd
+
+	* Sven: 6945: Completion/Core/_expand: error expanding :]
+
+	* Sven: 6941: Src/exec.c, Src/jobs.c: neater fix for time builtin.
+
+	* Oliver: 6939: Completion/User/_hosts,
+	  Completion/Base/_subscript, Misc/compctl-examples: better way of
+	  using /etc/hosts; complete POSIX character classes.
+	
+	* Sven: 6937: Doc/Zsh/mod_complist.yo, Src/Zle/complist.c: use
+	  send-break to exit menu selection and return to normal menu
+	  completion.
+
+1999-07-01  Peter Stephenson  <pws@ibmth.difi.unipi.it>
+
+	* Sven: 6936: Src/jobs.c: time builtin was broken.
+
+	* Sven: 6933: Src/exec.c, Src/jobs.c, Src/Signals.c:  More job
+	  control fixes:  running external command after loop in function;
+	  function on left hand side of pipeline with loop on right.
+
+1999-06-29  Peter Stephenson  <pws@ibmth.difi.unipi.it>
+
+	* Sven: 6908, 6926: Src/exec.c, Src/jobs.c, Src/zsh.h: more job
+	  control fixes:  functions with programs that send strange
+	  signals to parents should work.
+
+	* Sven: 6904: Src/Makefile.in: bugs with cleaning up modules
+	  in Src.
+
+	* Sven: 6903: Src/Zle/zle_tricky.c, Doc/Zsh/compwid.yo:
+	  compstate[insert] can have a space at the end to force inserting
+	  a space after completion.
+
+	* Sven: 6902: Src/Zle/complist.c, Doc/Zsh/mod_complist.yo:
+	  menu-select will use %S/%s if ZLS_COLO[U]RS is not defined.
+
+	* Sven: 6901: Src/exec.c, Src/init.c, Src/jobs.c:  back off
+	  some (but not all) recent job handling patches to avoid more
+	  serious problems with recalcitrant programs.
+
+1999-06-28  Peter Stephenson  <pws@ibmth.difi.unipi.it>
+
+	* pws: 6899: Doc/Zsh/params.yo, Doc/Zsh/prompt.yo, Src/init.c:
+	  default PS4 is now "+%N:%i> ", except in [k]sh emulation.
+
+	* Andrej: 6893: aczsh.m4: use existing environment value when
+	  testing for large file support.
+
+	* pws: 6890: Doc/Zsh/params.yo, Doc/Zsh/prompt.yo, Src/prompt.c:
+	  %N and %i prompt escapes provide script or function name and
+	  line number.
+
+	* Sven: 6887: Src/exec.c, Src/jobs.c: another loop killing problem.
+
+	* Geoff: 6884: Src/Zle/zle_refresh.c: bug when redisplaying line
+	  one shorter than terminal width
+
+1999-06-27  Peter Stephenson  <pws@ibmth.difi.unipi.it>
+
+        * pws: 6881: Doc/Zsh/mod_zle.yo: explain keymap linking
+
+	* pws: 6879: Src/exec.c: update command hash pointer if builtin
+	  is autoloaded after a prefork().
+
+	* Bart: 6873: Src/Zle/zle_tricky.c: magic-space doesn't feep
+	  quite so much.
+
+	* Geoff: 6871: Src/Zle/complist.c: optimi[sz]ations for outputing
+	  coloured/colorized output.
+
+	* pws: 6866: Src/exec.c, Src/init.c: signed/unsigned character
+	  problems; one was causing SunOS 4.1.3_U1 with gcc 2.7.0 to hang.
+
+1999-06-26  Peter Stephenson  <pws@ibmth.difi.unipi.it>
+
+	* 3.1.5-pws-24 made available
+
+1999-06-25  Peter Stephenson  <pws@ibmth.difi.unipi.it>
+
+	* pws: 6857: Completion/Core/compinit,
+	  Completion/Core/compinstall, Doc/Zsh/compsys.yo: compinit and
+	  compinstall are now functions which unfunction and autoload
+	  themselves.  _compdir is used by compinstall to record where
+	  it found the completion directories.  compinit is now otherwise
+	  stuck with fpath.
+
+	* pws: 6851, 6853: typeset -g doesn't locallize parameters; bug
+	  that unset parameters were recreated global instead of at
+	  some higher local level; handle PM_AUTOLOAD consistent with other
+	  flags.
+
+	* Sven: 6850: Src/init.c: always generate a new pgrp for the
+	  shell, since the parent (e.g. xterm) may not have done that
+	  and zsh now runs programs in its own pgrp.
+
+	* Sven: 6848: Src/exec.c: don't suspend if the shell is the
+	  only thing to suspend (or something like that).
+
+	* Sven: 6841: Src/loop.c: %_ in else branches for PS4
+
+1999-06-24  Peter Stephenson  <pws@ibmth.difi.unipi.it>
+
+	* pws: 6834: Src/glob.c, Src/hashtable.c: dyncat() changed always
+	  to use heap memory (as it erroneously claimed); hashtable element
+	  tablename (used for debugging) freed.
+
+	* Bart: 6830: Src/params.c: don't create the hashtable for an
+	  assoc array on assignment unless there is something to put in it.
+
+	* Sven: 6825: Src/Zle_tricky.c: make sure path prefix and suffix
+	  are quoted in filename completion; recalculate length of match
+	  string.
+
+	* Sven: 6824: Src/exec.c, Src/signals.c: functions got deleted
+	  from the process table too early for job control.
+
+	* pws: 6823: Src/exec.c, Src/utils.c:  names and line numbers
+	  of functions printed for errors during execution.
+
+	* Sven: 6822: Src/Zle/complist.c, Src/Zle/zle_tricky.c: assorted
+	  completion fixes: crash with old completion; too many spaces
+	  with menu inserting; too many beeps with LISTBEEP.
+
+	* Sven: 6819: Src/exec.c, Src/jobs.c, Src/signals.c:  Run
+	  jobs inside shell constructs in the same process group as the
+	  shell itself.
+
+	* Sven: 6817: Src/Zle/comp.h, Src/Zle/complist.c,
+	  Src/Zle/zle_tricky.c: Change ZLS_SELECT to SELECTMIN;
+	  don't automatically switch on select widget until there are
+	  $SELECTMIN choices.
+
+1999-06-23  Peter Stephenson  <pws@ibmth.difi.unipi.it>
+
+	* pws: 6816: Doc/Zsh/params.yo, Src/utils.c:  ZBEEP parameter
+	  gives string to output instead of beeping.
+
+	* Sven: 6815: Src/Zle/complist.c: switch off menu-select for
+	  hidden matches.
+
+	* pws: 6814: Doc/Zsh/mod_zle.yo, Doc/Zsh/options.yo,
+	  Doc/Zsh/zle.yo, Src/Zle/deltochar.c, Src/Zle/iwidgets.list,
+	  Src/Zle/zle_hist.c, Src/Zle/zle_main.c, Src/Zle/zle_misc.c,
+	  Src/Zle/zle_move.c, Src/Zle/zle_thingy.c, Src/Zle/zle_tricky.c,
+	  Src/Zle/zle_utils.c, Src/Zle/zle_vi.c, Src/Zle/zle_word.c:
+	  Zle determines whether to feep by the return status (except
+	  for some inner code loops); completion widgets return 1 if
+	  something failed in the hierarchy outside the widget function;
+	  the -n and -N options work properly.
+
+	* pws: 6812: Src/subst.c: ${(AA)foo=}, or anything that gives
+	  a null string after the =, creates an empty assoc array.
+
+	* pws: 6806: Completion/Core/compdump, Completion/Core/compinit,
+	  Completion/Core/compinstall, Doc/Zsh/compsys.yo: compdump is now
+	  a function which unfunctions itself; default dumpfile location
+	  is now ${ZDOTDIR:-$HOME}/.zcompdump
+
+	* Sven: 6807: Src/Zle/complist.c: accept-and-menu-complete
+	  advances the menu-select selection too.
+
+	* Sven: 6802: Src/Zle/complist.c: change some default colours
+
+	* pws: 6801: Doc/Zsh/builtins.yo, Src/builtin.c: Make emulate -L
+	  turn on LOCAL_TRAPS, too.
+
+	* Sven: 6796: Src/Zle/zle_main.c, Src/Zle/zle_thingy.c,
+	  Doc/Zsh/mod_zle.yo, Completion/Core/compinit: zle -la lists
+	  all widgets, just the name; zle -la <NAME> tests if <NAME> is
+	  defined.
+
+	* Sven: 6793: Src/Zle/complist.c, Src/Zle/zle_keymap.c,
+	  Src/Zle/zle_main.c, Doc/Zsh/mod_complist.yo: Local keymaps can be
+	  defined, currently only used with menu-select.
+
+1999-06-22  Peter Stephenson  <pws@ibmth.difi.unipi.it>
+
+	* Sven: 6786: Src/Zle/zle_tricky.c, Doc/Zsh/compctl.yo: only use
+	  a range when you are after the first pattern in it.
+
+	* Sven: 6780: Src/Zle/complist.c: wasn't showinglist when it
+	  thought it was.
+
+	* Sven: 6778: don't delete function from job tables
+	  
+	 * pws: 6776: Doc/Makefile.in, Doc/Zsh/compsys.yo,
+	  Doc/Zsh/mod_complist.yo:  Tweakchen for menu-select patch;
+	  also (unposted) changed name collist to complist wherever it
+	  occurs.
+
+	* Sven: 6774, 6775: Src/Zle/collist.c, Src/Zle/comp.h,
+	  Src/Zle/zle.h, Src/Zle/zle_main.c, Src/Zle/zle_tricky.c,
+	  Doc/Zsh/compsys.yo, Doc/Zsh/mod_collist.yo,
+	  Doc/Zsh/mod_compctl.yo, Doc/Zsh/mod_deltochar.yo,
+	  Doc/Zsh/modules.yo, Completion/Core/_path_files:  Improvements
+	  for menu-select widget; ZLS_SELECT turns menu-completion
+	  into menu-selection; key bindings more natural; highlighting
+	  more useful
+
+	* Oliver: 6772: Src/Zle/zle_tricky.c: use the whole
+	  command text for job completion.
+
+1999-06-21  Peter Stephenson  <pws@ibmth.difi.unipi.it>
+
+	* Sven: 6760: Src/Zle/zle_tricky.c: menucompletion displaying new
+	  list of matches sometimes got confused.
+
+	* Sven: 6755: Src/jobs.c: status of restarted job including
+	  current shell processes was wrong.
+
+	* Sven: 6753: Src/Zle/collist.c, Src/xmods.conf: 
+	  load collist
+
+	* Sven: 6747: Src/Zle/collist.c, Src/Zle/zle_main.c,
+	  Src/Zle/zle_tricky.c: menu-select allows you to move
+	  cursor to select completions; uses ma list colouring
+	  capability.
+
+	* Sven: 6742: Src/Zle/collist.c, Src/Zle/collist.mdd,
+	  Src/Zle/zle_main.c, Src/Zle/zle_tricky.c, Src/module.c,
+	  Src/zsh.h, Doc/Zsh/mod_collist.yo, Doc/Zsh/mod_compctly.yo,
+	  Doc/Zsh/mod_deltochar.yo, Doc/Zsh/modules.yo,
+	  Util/zsh-development-guide:  collist module: colour completion
+	  lists by setting ZLS_COLOURS (or even ZLS_COLORS) variables.
+
+	* pws: 6737: Doc/Zsh/builtins.yo, Doc/Zsh/zle.yo: simplifications
+	  suggested by Bart.
+
+	* Bart: 6732: Doc/Zsh/compsys.yo: restore missing bits of Sven's
+	  patches (not the zle_tricky.c hunk).
+
+	* Bart: 6731: Doc/Zsh/compctl.yo: spelling correction
+
 1999-06-20  Peter Stephenson  <pws@ibmth.difi.unipi.it>
 
 	* zsh-3.1.5-pws-23 made available
diff --git a/Completion/Base/_first b/Completion/Base/_first
index 655e3569f..8b4da019d 100644
--- a/Completion/Base/_first
+++ b/Completion/Base/_first
@@ -1,11 +1,63 @@
-#compdef -subscript-
+#compdef -first-
 
-if [[ ${(Pt)${compstate[parameter]}} = assoc* ]]; then
-  if [[ "$RBUFFER" = \]* ]]; then
-    compadd -S '' - "${(@kP)${compstate[parameter]}}"
-  else
-    compadd -S ']' - "${(@kP)${compstate[parameter]}}"
-  fi
-else
-  _compalso -math-
-fi
+# This function is called at the very beginning before any other
+# function for a specific context.
+#
+# This just gives some examples of things you might want to do here.
+#
+#
+# If you use the vared builtin and want completion in there to act the 
+# way completion on the right hand side of assignments is done, add
+# (or un-comment) this code:
+#
+#     if [[ -n $compstate[vared] ]]; then
+#       if [[ $compstate[vared] = *\[* ]]; then
+#         # vared on an array-element
+#         compstate[parameter]=${compstate[vared]%%\[*}
+#         compstate[context]=value
+#       else
+#         # vared on a parameter, let's see if it is an array
+#         compstate[parameter]=$compstate[vared]
+#         if [[ ${(tP)compstate[vared]} = *(array|assoc)* ]]; then
+#           compstate[context]=array_value
+#         else
+#           compstate[context]=value
+#         fi
+#       fi
+#       return
+#     fi
+#
+#
+#
+# Other things you can do here is to complete different things if the
+# word on the line matches a certain pattern. This example allows
+# completion of words from the history by adding two commas at the end 
+# and hitting TAB.
+#
+#     if [[ "$PREFIX" = *,, ]]; then
+#       local max i=1
+#     
+#       PREFIX="$PREFIX[1,-2]"
+#       # If a numeric prefix is given, we use it as the number of
+#       # lines (multiplied by ten below) in the history to search.
+#       if [[ ${NUMERIC:-1} -gt 1 ]]; then
+#         max=$NUMERIC
+#         unset NUMERIC
+#       else
+#         # The default is to search the last 100 lines.
+#         max=10
+#       fi
+#       # We first search in the last ten lines, then in the last
+#       # twenty lines, and so on...
+#       while [[ i -le max ]]; do
+#         if compgen -X "%Bhistory ($n):%b" -Q -H $(( i*10 )) ''; 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 _comp_skip.
+#           compstate[insert]=menu
+#           _comp_skip=1
+#           return
+#         fi
+#         (( i++ ))
+#       done
+#     fi
diff --git a/Completion/Base/_subscript b/Completion/Base/_subscript
index 00b2b6506..be5f08f62 100644
--- a/Completion/Base/_subscript
+++ b/Completion/Base/_subscript
@@ -1,6 +1,9 @@
 #compdef -subscript-
 
-if [[ ${(Pt)${compstate[parameter]}} = assoc* ]]; then
+if [[ "$PREFIX" = :* ]]; then
+  compadd -p: -S ':]' alnum alpha blank cntrl digit graph lower print punct \
+      space upper xdigit
+elif [[ ${(Pt)${compstate[parameter]}} = assoc* ]]; then
   if [[ "$RBUFFER" = \]* ]]; then
     compadd -S '' - "${(@kP)${compstate[parameter]}}"
   else
@@ -13,15 +16,15 @@ elif [[ ${(Pt)${compstate[parameter]}} = array* ]]; then
   list=()
   for i in "$ind[@]"; do
     [[ "$i" = ${PREFIX}*${SUFFIX} ]] &&
-        list=( "$list[@]" "${(r:4:: ::):)i} ${(P)${compstate[parameter]}[$i]}
-)
+        list=( "$list[@]" 
+	  "${(r:4:: ::):)i} $(print -D ${(P)${compstate[parameter]}[$i]})" )
   done
 
- if [[ "$RBUFFER" = \]* ]]; then
-   compadd -S '' -y list - "$ind[@]"
- else
-   compadd -S ']' -y list - "$ind[@]"
- fi
+  if [[ "$RBUFFER" = \]* ]]; then
+    compadd -S '' -V default -y list - "$ind[@]"
+  else
+    compadd -S ']' -V default -y list - "$ind[@]"
+  fi
 else
   _compalso -math-
 fi
diff --git a/Completion/Builtins/_cd b/Completion/Builtins/_cd
index 61abd2068..4a846c6aa 100644
--- a/Completion/Builtins/_cd
+++ b/Completion/Builtins/_cd
@@ -5,7 +5,7 @@
 #    and the string doesn't begin with ~, /, ./ or ../.
 #  - In the second argument to cd for the form `cd old new', completes
 #    possible `new' strings by examining `old' and $PWD.
-#  - After pushd - or pushd +, completes numbers, but the listing
+#  - After - or +, completes numbers, but the listing
 #    gives you the list of directories to complete.  This turns on
 #    menu-completion and lists the possibilities automatically, otherwise
 #    it's not a lot of use.  If you don't type the + or - it will
@@ -25,7 +25,7 @@ if [[ CURRENT -eq 3 ]]; then
   # Now remove all the common parts of $PWD and the completions from this
   rep=(${${rep#${PWD%%$words[2]*}}%${PWD#*$words[2]}})
   (( ! $#rep )) || compadd $rep
-elif [[ $words[1] = pu* && $PREFIX = [-+]* ]]; then
+elif [[ $PREFIX = [-+]* ]]; then
   # pushd: just complete the numbers, but show the full directory list with
   # numbers.
   # For - we do the same thing, but reverse the numbering (other
diff --git a/Completion/Commands/.distfiles b/Completion/Commands/.distfiles
index 19a02ef39..7b2a319fe 100644
--- a/Completion/Commands/.distfiles
+++ b/Completion/Commands/.distfiles
@@ -1,3 +1,3 @@
 DISTFILES_SRC='
-    .distfiles _correct_filename _correct_word _most_recent_file 
+    .distfiles _correct_filename _correct_word _expand_word _most_recent_file 
 '
diff --git a/Completion/Commands/_expand_word b/Completion/Commands/_expand_word
new file mode 100644
index 000000000..570f06987
--- /dev/null
+++ b/Completion/Commands/_expand_word
@@ -0,0 +1,26 @@
+#compdef -k complete-word \C-xe
+
+# Simple completion front-end implementing expansion.
+#
+# If configurations keys with the prefix `expandword_' are
+# given they override those starting with `expand_'.
+
+local oes="$compconfig[expand_substitute]"
+local oeg="$compconfig[expand_glob]"
+local oem="$compconfig[expand_menu]"
+local oeo="$compconfig[expand_original]"
+local oep="$compconfig[expand_prompt]"
+
+compconfig[expand_substitute]="${compconfig[expandword_substitute]}"
+compconfig[expand_glob]="${compconfig[expandword_glob]-$oeg}"
+compconfig[expand_menu]="${compconfig[expandword_menu]-$oem}"
+compconfig[expand_original]="${compconfig[expandword_original]-$oeo}"
+compconfig[expand_prompt]="${compconfig[expandword_prompt]-$oep}"
+
+_main_complete _expand
+
+compconfig[expand_substitute]="$oes"
+compconfig[expand_glob]="$oeg"
+compconfig[expand_menu]="$oem"
+compconfig[expand_original]="$oeo"
+compconfig[expand_prompt]="$oep"
diff --git a/Completion/Core/_expand b/Completion/Core/_expand
index 58e184657..778293a8b 100644
--- a/Completion/Core/_expand
+++ b/Completion/Core/_expand
@@ -57,7 +57,6 @@
 #    `%o' in this string will be replaced by the original string.
 
 local exp word="$PREFIX$SUFFIX" group=-V
-
 # Do this only for the first global matcher.
 
 [[ "$compstate[matcher]" -le 1 ]] || return 1
@@ -88,7 +87,7 @@ exp=("$word")
 # as the original string, we let other completers run.
 
 [[ $#exp -eq 0 ||
-   ( $#exp -eq 1 && "$exp[1]" = "$word" ) ]] && return 1
+   ( $#exp -eq 1 && "$exp[1]" = "$word"(|\(N\)) ) ]] && return 1
 
 # We have expansions, should we menucomplete them?
 
diff --git a/Completion/Core/_path_files b/Completion/Core/_path_files
index 77365a01a..58f343367 100644
--- a/Completion/Core/_path_files
+++ b/Completion/Core/_path_files
@@ -377,9 +377,9 @@ done
 exppaths=( "${(@)exppaths:#$orig}" )
 
 if [[ -n "$compconfig[path_expand]" &&
-      $#exppaths -ne 0 && nm -eq compstate[nmatches] ]]; then
-  compadd -U -S '' "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
-          -p "$linepath" - "${(@)exppaths}"
+      $#exppaths -eq 0 && nm -eq compstate[nmatches] ]]; then
+  compadd -QU -S '' "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
+          -M 'r:|/=* r:|=*' -p "$linepath" - "${(@)exppaths}"
 fi
 
 [[ nm -eq compstate[nmatches] ]]
diff --git a/Completion/Core/compdump b/Completion/Core/compdump
index b7682bd50..f2729acc5 100644
--- a/Completion/Core/compdump
+++ b/Completion/Core/compdump
@@ -1,4 +1,4 @@
-# This is a file to be sourced to dump the definitions for new-style
+# This is a function to dump the definitions for new-style
 # completion defined by 'compinit' in the same directory.  The output
 # should be directed into the "compinit.dump" in the same directory as
 # compinit. If you rename init, just stick .dump onto the end of whatever
@@ -9,12 +9,14 @@
 # To do this, simply remove the .dump file, start a new shell, and
 # create the .dump file as before.  Again, compinit -d handles this
 # automatically.
-#
-# It relies on KSH_ARRAYS not being set.
 
 # Print the number of files used for completion. This is used in compinit
 # to see if auto-dump should re-dump the dump-file.
 
+emulate -L zsh
+
+typeset _d_file _d_f _d_bks _d_line _d_als
+
 _d_file=${compconfig[dumpfile]-${0:h}/compinit.dump}
 
 typeset -U _d_files
@@ -22,8 +24,6 @@ _d_files=( ${^~fpath}/_(|*[^~])(N:t) )
 
 print "#files: $#_d_files" > $_d_file
 
-unset _d_files
-
 # First dump the arrays _comps and _patcomps.  The quoting hieroglyphyics
 # ensure that a single quote inside a variable is itself correctly quoted.
 
@@ -88,4 +88,5 @@ done >> $_d_file
 
 print >> $_d_file
 
-unset _d_line _d_zle _d_bks _d_als _d_f _f_file
+unfunction compdump
+autoload -U compdump
diff --git a/Completion/Core/compinit b/Completion/Core/compinit
index f4aa80f21..9302d8243 100644
--- a/Completion/Core/compinit
+++ b/Completion/Core/compinit
@@ -41,15 +41,14 @@
 # See the file `compdump' for how to speed up initialisation.
 
 # If we got the `-d'-flag, we will automatically dump the new state (at
-# the end).
-# `-f dir' is used to pass down the directory where this file was
-#   found.  This is necessary if functionargzero is not set.
-# If we were given an argument, this will be taken as the name of the
-# file in which to store the dump.
-
-_i_fdir=''
-_i_dumpfile=''
-_i_autodump=0
+# the end).  This takes the dumpfile as an argument.  -d (with the
+# default dumpfile) is now the default; to turn off dumping use -D.
+
+emulate -L zsh
+
+typeset _i_dumpfile _i_files _i_line _i_done _i_dir _i_autodump=1
+typeset _i_tag _i_file _i_addfiles
+
 while [[ $# -gt 0 && $1 = -[df] ]]; do
   if [[ "$1" = -d ]]; then
     _i_autodump=1
@@ -58,57 +57,30 @@ while [[ $# -gt 0 && $1 = -[df] ]]; do
       _i_dumpfile="$1"
       shift
     fi
+  elif [[ "$1" = -D ]]; then
+    _i_autodump=0
   elif [[ "$1" = -f ]]; then
-    # Used by compinstall to pass down directory where compinit was found
+    # Not used any more; use _compdir
     shift
-    _i_fdir="$1"
     shift
   fi
 done
-# Get the directory if we don't have it already and we can
-if [[ -z "$_i_fdir" && -o functionargzero && $0 = */* ]]; then
-  _i_fdir=${0:h}
-fi
 
 # The associative array containing the definitions for the commands.
 # Definitions for patterns will be stored in the normal array `_patcomps'.
 
-typeset -A _comps
+typeset -gA _comps
 _patcomps=()
 
 # This is the associative array used for configuration.
 
-typeset -A compconfig
+typeset -gA compconfig
 
 # Standard initialisation for `compconfig'.
 if [[ -n $_i_dumpfile ]]; then
   # Explicitly supplied dumpfile.
   compconfig[dumpfile]="$_i_dumpfile"
-elif [[ -o functionargzero ]]; then
-  # We can deduce it from the name of this script
-  compconfig[dumpfile]="$0.dump"
-elif [[ -n $_i_fdir ]]; then
-  # We were told what directory to use.
-  compconfig[dumpfile]="$_i_fdir/compinit.dump"
 else
-  compconfig[dumpfile]=''
-fi
-
-if [[ -n $compconfig[dumpfile] ]]; then
-  # Check the file is writeable.  If it doesn't exist, the
-  # only safe way is to try and create it.
-  if [[ -f $compconfig[dumpfile] ]]; then
-    [[ -w $compconfig[dumpfile] ]] || compconfig[dumpfile]=''
-  elif touch $compconfig[dumpfile] >& /dev/null; then
-    rm -f $compconfig[dumpfile]
-  else
-    compconfig[dumpfile]=''
-  fi
-fi
-
-if [[ -z $compconfig[dumpfile] ]]; then
-  # If no dumpfile given, or it was not writeable, then use
-  # user's ZDOTDIR.
   compconfig[dumpfile]="${ZDOTDIR:-$HOME}/.zcompdump"
 fi
 
@@ -322,19 +294,24 @@ compconf() {
 
 typeset -U _i_files
 _i_files=( ${^~fpath:/.}/_(|*[^~])(N:t) )
-if [[ $#_i_files -lt 20 ]]; then
-  # Too few files:  we need some more directories
-  # Assume that we need to add the compinit directory to fpath.
-  if [[ -n $_i_fdir ]]; then
-    if [[ $_i_fdir = */Core ]]; then
+if [[ $#_i_files -lt 20 || $_compdir = */Core || -d $_compdir/Core ]]; then
+  # Too few files:  we need some more directories,
+  # or we need to check that all directories (not just Core) are present.
+  if [[ -n $_compdir ]]; then
+    _i_addfiles=()
+    if [[ $_compdir = */Core ]]; then
       # Add all the Completion subdirectories
-      fpath=(${_i_fdir:h}/*(/) $fpath)
-    elif [[ -d $_i_fdir/Core ]]; then
+      _i_addfiles=(${_compdir:h}/*(/))
+    elif [[ -d $_compdir/Core ]]; then
       # Likewise
-      fpath=(${_i_fdir}/*(/) $fpath)
-    else
-      fpath=($_i_fdir $fpath)
+      _i_addfiles=(${_compdir}/*(/))
     fi
+    for _i_line in {1..$#i_addfiles}; do
+      _i_file=${_i_addfiles[$_i_line]}
+      [[ -d $_i_file && -z ${fpath[(r)$_i_$file]} ]] ||
+        _i_addfiles[$_i_line]=
+    done
+    fpath=($_i_addfiles $fpath)
     _i_files=( ${^~fpath:/.}/_(|*[^~])(N:t) )
   fi
 fi
@@ -346,9 +323,13 @@ for _i_line in complete-word delete-char-or-list expand-or-complete \
   menu-expand-or-complete reverse-menu-complete; do
   zle -C $_i_line .$_i_line _main_complete
 done
+zle -la menu-select && zle -C menu-select .menu-select _main_complete
 
 _i_done=''
 
+# Make sure compdump is available, even if we aren't going to use it.
+autoload -U compdump compinstall
+
 # If we have a dump file, load it.
 
 if [[ -f "$compconfig[dumpfile]" ]]; then
@@ -357,7 +338,6 @@ if [[ -f "$compconfig[dumpfile]" ]]; then
     builtin . "$compconfig[dumpfile]"
     _i_done=yes
   fi
-  unset _i_line
 fi
 if [[ -z "$_i_done" ]]; then
   for _i_dir in $fpath; do
@@ -381,13 +361,12 @@ if [[ -z "$_i_done" ]]; then
     done
   done
 
-  unset _i_dir _i_line _i_file _i_tag
-
   # If autodumping was requested, do it now.
 
-  if [[ -n ${_i_fdir} && $_i_autodump = 1 ]]; then
-    builtin . ${_i_fdir}/compdump
+  if [[ $_i_autodump = 1 ]]; then
+    compdump
   fi
 fi
 
-unset _i_files _i_initname _i_done _i_autodump _i_fdir _i_dumpfile
+unfunction compinit
+autoload -U compinit
diff --git a/Completion/Core/compinstall b/Completion/Core/compinstall
index 5086cc7d2..52b4bf707 100644
--- a/Completion/Core/compinstall
+++ b/Completion/Core/compinstall
@@ -1,11 +1,12 @@
 # This script is to be run by a user to set up the new function based
 # completion system.  The functions themselves are assumed to be already
 # available in some directory; they should have been installed with the
-# the shell (except we haven't written that yet).
+# the shell.  If they have been, the commands `autoload -U compinit; compinit'
+# in the shell startup file should be enough, although you can run
+# compinstall for more configuration choices.
 #
-# Source this script (e.g. `. /path/compinstall') and answer the questions.
-#
-# Normally, this will alter ~/.zshrc (or wherever ZDOTDIR puts it), but you
+# Simply run this script as a function and answer the questions.
+# Normally it will alter ~/.zshrc (or wherever ZDOTDIR puts it), but you
 # can make that unwritable and it will leave the lines in a temporary file
 # instead.  It doesn't matter if .zshrc didn't exist before.  If your
 # .zshrc usually exits before the end, then you should take the code added
@@ -23,33 +24,13 @@
 #  - Could add code for setting other completers and options.
 #  - Could add keys for context-sensitive help.
 
-# Save the options.  We will need to trap ^C to make sure they get
-# restored properly.
-typeset -A _ci_options
-_ci_options=($(setopt kshoptionprint;setopt))
-[[ -o kshoptionprint ]] || _ci_options[kshoptionprint]=off
-[[ -o monitor ]] && _ci_options[monitor]=on
-[[ -o zle ]] && _ci_options[zle]=on
-
-emulate zsh
-
-TRAPINT() { 
-  unsetopt ${(k)_ci_options[(R)off]}
-  setopt ${(k)_ci_options[(R)on]}
-
-  unset _ci_options _ci_f _ci_fdir _ci_files _ci_dumpfile _ci_lines
-  unset _ci_type _ci_completer _ci_accept _ci_cprompt _ci_startline
-  unset _ci_endline _ci_ifile _ci_tmpf _ci_defaults _ci_compconf _ci_warn
-  unset _ci_dtype _ci_existing _ci_line
-
-  if (( $1 )); then
-    print Aborted.
-    unfunction TRAPINT
-    return 1
-  fi
-  return 0
-}
 
+emulate -L zsh
+
+typeset _ci_options _ci_f _ci_fdir _ci_files _ci_dumpfile _ci_lines
+typeset _ci_type _ci_completer _ci_accept _ci_cprompt _ci_startline
+typeset _ci_endline _ci_ifile _ci_tmpf _ci_compconf _ci_warn
+typeset _ci_dtype _ci_existing _ci_line _ci_end
 
 # Look for the defaults.
 _ci_startline='# The following lines were added by compinstall'
@@ -60,6 +41,7 @@ _ci_lines=''
 _ci_existing=''
 
 typeset -A _ci_defaults
+
 if [[ -f $_ci_ifile ]]; then
   # This assumes the lines haven't been altered by the user too much
   # after they were added.
@@ -68,13 +50,14 @@ if [[ -f $_ci_ifile ]]; then
   while read -rA _ci_line; do
     if (( $_ci_compconf )); then
       # parse a compconf component as first argument
-      if [[ $_ci_line[-1] == \\ ]]; then
-	_ci_line=(${_ci_line[1,-2]})
-      else
+      if [[ $_ci_line[-1] != \\ ]]; then
+	_ci_end=-1
 	_ci_compconf=0
+      else
+	_ci_end=-2
       fi
       if [[ $_ci_line[1] = *=* ]]; then
-	_ci_f="${${_ci_line[*]}#*=}"
+	_ci_f="${${_ci_line[1,$_ci_end]}#*=}"
 	if [[ $_ci_f = \'*\' ]]; then
 	  # strip quotes
 	  _ci_f=${_ci_f[2,-2]//\'\\\'\'/\'}
@@ -83,10 +66,12 @@ if [[ -f $_ci_ifile ]]; then
       fi
       _ci_existing="${_ci_existing}  $_ci_line
 "
-    elif [[ $_ci_line[1] = . && $_ci_line[2] = */compinit ]]; then
-      # parse the line sourcing compinit
-      [[ $_ci_line[3] = -f ]]  && _ci_fdir=$_ci_line[4]
+    elif [[ $_ci_line[1] = compinit ]]; then
+      # parse the line running compinit
+      [[ $_ci_line[2] = -f ]]  && _ci_fdir=$_ci_line[3]
       [[ $_ci_line[-2] = -d ]] && _ci_dumpfile=$_ci_line[-1]
+    elif [[ $_ci_line[1] = _compdir=* ]]; then
+      _ci_fdir=${_ci_line[1]##_compdir=}
     elif [[ $_ci_line[1] = compconf ]]; then
       # parse a compconf component as second argument (should be completer)
       [[ $_ci_line[2] = completer=* ]] &&
@@ -94,7 +79,7 @@ if [[ -f $_ci_ifile ]]; then
       [[ $_ci_line[-1] == \\ ]] && _ci_compconf=1
       _ci_existing="${_ci_existing}$_ci_line
 "
-    elif [[ $_ci_line[1] != \#* ]]; then
+    elif [[ $_ci_line[1] != \#* && $_ci_line[1] != (autoload|\[\[) ]]; then
       if [[ -z $_ci_warn ]]; then
 	_ci_warn=1
 	print "Warning:  existing lines in compinstall setup not understood:"
@@ -109,38 +94,42 @@ fi
 
 # Find out where the completion functions are kept.
 
-if [[ -z $_ci_fdir || ! -f $_ci_f/compinit || ! -f $_ci_f/compdump ]]; then
+if [[ -z $_ci_fdir || ! -f ${~_ci_fdir}/compinit ||
+  ! -f ${~_ci_fdir}/compdump ]]; then
   for _ci_f in $fpath; do
     if [[ $_ci_f != . && -f $_ci_f/compinit && -f $_ci_f/compdump ]]; then
       _ci_fdir=$_ci_f
       break
+    elif [[ $_ci_f != . && -f $_ci_f/Core/compinit &&
+      -f $_ci_f/Core/compdump ]]
+    then
+      _ci_fdir=$_ci_f/Core
+      break
     fi
   done
 fi
 
 if [[ -z $_ci_fdir || ! -d ${~_ci_fdir} ]]; then
-  print "Trying to find where the completion functions are..."
-  if [[ $0 = */* && -o functionargzero &&
-        -f $0:h/compinit && -f $0:h/compdump ]]; then
-    _ci_fdir=$0:h
-    print "Using my directory, $_ci_fdir"
-  else
-    # more guesses?
-    print \
+  print \
 "Please edit the name of the directory where the completion functions are
 installed.  If they are not installed, you will need to find them in the
 Completion/* directories of the zsh distribution and install them yourself,
 or insult your system manager for incompetence."
-    vared -c _ci_fdir
-    while [[ ! -d ${~_ci_fdir} || ! -f ${~_ci_fdir}/compinit || 
-      ! -f ${~_ci_fdir}/compdump ]]; do
-      print "I can't find them in that directory.  Try again or abort."
-      vared _ci_fdir
-    done
+  vared -c _ci_fdir
+  while [[ ! -d ${~_ci_fdir} || 
+    ((! -f ${~_ci_fdir}/compinit || ! -f ${~_ci_fdir}/compdump) &&
+    (! -f ${~_ci_fdir}/Core/compinit || ! -f ${~_ci_fdir}/Core/compdump)) ]]
+  do
+    print "I can't find them in that directory.  Try again or abort."
+    vared _ci_fdir
+  done
+  if [[ -f ${~_ci_fdir}/Core/compinit && ! -f ${~_ci_fdir}/compinit ]]; then
+    _ci_fdir=$_ci_fdir/Core
   fi
 else
   print "Keeping existing completion directiory $_ci_fdir"
 fi
+
 if [[ ${~_ci_fdir} != /* ]]; then
   _ci_fdir=$(cd $_ci_fdir;builtin pwd)
 fi
@@ -148,6 +137,7 @@ fi
 # Check if this is in fpath already, else put it there (with ~'s expanded).
 _ci_f=${~_ci_fdir}
 [[ -z ${fpath[(r)$_ci_f]} ]] && fpath=($_ci_f $fpath)
+
 # Contract $HOME to ~ in the parameter to be used for writing.
 _ci_fdir=${_ci_fdir/#$HOME/\~}
 
@@ -166,7 +156,7 @@ fi
 # Set up the dumpfile
 _ci_dtype=existing
 if [[ -z $_ci_dumpfile ]]; then
-  _ci_dumpfile="${_ci_fdir}/compinit.dump"
+  _ci_dumpfile="${ZDOTDIR:-$HOME}/.zcompdump"
   _ci_dtype=standard
 fi
 
@@ -184,7 +174,6 @@ else
 I will force completion to dump its status, which will speed up the shell's
 start-up considerably.  However, I can't write the file I'd like to, namely
 ${_ci_dumpfile}.  Please edit a replacement."
-  _ci_dumpfile='~/.compinit.dump'
   vared _ci_dumpfile
   while ! touch ${~_ci_dumpfile} >& /dev/null; do
     print "Sorry, I can't write that either.  Try again."
@@ -193,7 +182,10 @@ ${_ci_dumpfile}.  Please edit a replacement."
   [[ -s $_ci_dumpfile ]] || rm -f $_ci_dumpfile
 fi
 
-_ci_lines="${_ci_lines}. $_ci_fdir/compinit -f $_ci_fdir -d"
+_ci_lines="${_ci_lines}_compdir=$_ci_fdir
+[[ -z \$fpath[(r)\$_compdir] ]] && fpath=(\$_compdir \$fpath)
+autoload -U compinit
+compinit"
 [[ $_ci_dtype != standard ]] && _ci_lines="${_ci_lines} $_ci_dumpfile"
 _ci_lines="${_ci_lines}
 "
@@ -363,6 +355,7 @@ $_ci_lines$_ci_endline" >>$_ci_ifile &&
   print "\nSuccessfully appended lines to $_ci_ifile."
 fi
 
-TRAPINT 0
+unfunction compinstall
+autoload -U compinstall
 
 return 0
diff --git a/Completion/User/_hosts b/Completion/User/_hosts
index 911cea03b..a0aca0a62 100644
--- a/Completion/User/_hosts
+++ b/Completion/User/_hosts
@@ -1,3 +1,5 @@
-#compdef ftp ncftp ping rwho rup xping traceroute nslookup
+#compdef ftp ncftp ping rwho rup xping traceroute nslookup telnet
 
-compgen -k hosts
+: ${(A)hosts:=${(s: :)${(ps:\t:)${${(f)"$(</etc/hosts)"}%%\#*}##[:blank:]#[^[:blank:]]#}}}
+
+compgen -M 'm:{a-zA-Z}={A-Za-z} r:|.=* r:|=*' -k hosts
diff --git a/Config/.cvsignore b/Config/.cvsignore
index e69de29bb..1eda58e7d 100644
--- a/Config/.cvsignore
+++ b/Config/.cvsignore
@@ -0,0 +1 @@
+defs.mk
diff --git a/Config/version.mk b/Config/version.mk
index d5e5cd530..ab59f05ce 100644
--- a/Config/version.mk
+++ b/Config/version.mk
@@ -27,5 +27,5 @@
 # This must also serve as a shell script, so do not add spaces around the
 # `=' signs.
 
-VERSION=3.1.5-pws-23
-VERSION_DATE='June 20, 1999'
+VERSION=3.1.5-pws-25
+VERSION_DATE='July 3, 1999'
diff --git a/Doc/.distfiles b/Doc/.distfiles
index 4c21f9fb1..7fd1cdcc1 100644
--- a/Doc/.distfiles
+++ b/Doc/.distfiles
@@ -2,13 +2,11 @@ DISTFILES_SRC='
     .cvsignore .distfiles Makefile.in
     META-FAQ.yo intro.ms
     version.yo zmacros.yo zman.yo ztexi.yo
-    zsh.yo zshbuiltins.yo zshcompctl.yo zshcompsys.yo zshcompwid.yo
-    zshexpn.yo zshmisc.yo zshmodules.yo zshoptions.yo zshparam.yo
-    zshzftpsys.yo zshzle.yo
+    zsh.yo zshbuiltins.yo zshcompctl.yo zshexpn.yo zshmisc.yo
+    zshmodules.yo zshoptions.yo zshparam.yo zshzle.yo
     zsh.texi
-    zsh.1 zshbuiltins.1 zshcompctl.1 zshcompsys.1 zshcompwid.1
-    zshexpn.1 zshmisc.1 zshmodules.1 zshoptions.1 zshparam.1
-    zshzftpsys.1 zshzle.1 zshall.1
+    zsh.1 zshbuiltins.1 zshcompctl.1 zshexpn.1 zshmisc.1 zshmodules.1
+    zshoptions.1 zshparam.1 zshzle.1 zshall.1
 '
 
 DISTFILES_DOC='
diff --git a/Doc/Makefile.in b/Doc/Makefile.in
index 83bba17f8..9e70f88b1 100644
--- a/Doc/Makefile.in
+++ b/Doc/Makefile.in
@@ -56,9 +56,9 @@ Zsh/compat.yo Zsh/compctl.yo Zsh/cond.yo Zsh/exec.yo Zsh/expn.yo \
 Zsh/filelist.yo Zsh/files.yo Zsh/func.yo Zsh/grammar.yo Zsh/guide.yo \
 Zsh/index.yo Zsh/intro.yo Zsh/invoke.yo Zsh/jobs.yo Zsh/metafaq.yo \
 Zsh/modules.yo Zsh/mod_cap.yo Zsh/compwid.yo Zsh/compsys.yo \
-Zsh/mod_clone.yo Zsh/mod_comp1.yo Zsh/mod_compctl.yo Zsh/mod_deltochar.yo \
-Zsh/mod_example.yo Zsh/mod_files.yo Zsh/mod_mapfile.yo Zsh/mod_stat.yo \
-Zsh/mod_zle.yo Zsh/options.yo \
+Zsh/mod_clone.yo Zsh/mod_comp1.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_stat.yo Zsh/mod_zle.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
 
@@ -131,9 +131,10 @@ zshmisc.1: Zsh/grammar.yo Zsh/redirect.yo Zsh/exec.yo Zsh/func.yo \
            Zsh/prompt.yo Zsh/restricted.yo
 
 zshmodules.1: Zsh/modules.yo Zsh/mod_cap.yo Zsh/mod_clone.yo \
-              Zsh/mod_comp1.yo Zsh/mod_compctl.yo Zsh/mod_deltochar.yo \
-              Zsh/mod_example.yo Zsh/mod_files.yo Zsh/mod_mapfile.yo \
-              Zsh/mod_sched.yo Zsh/mod_stat.yo Zsh/mod_zftp.yo Zsh/mod_zle.yo
+              Zsh/mod_comp1.yo Zsh/mod_complist.yo Zsh/mod_compctl.yo \
+              Zsh/mod_deltochar.yo Zsh/mod_example.yo Zsh/mod_files.yo \
+              Zsh/mod_mapfile.yo Zsh/mod_sched.yo Zsh/mod_stat.yo \
+              Zsh/mod_zftp.yo Zsh/mod_zle.yo
 
 zshoptions.1: Zsh/options.yo
 
diff --git a/Doc/Zsh/.distfiles b/Doc/Zsh/.distfiles
index 3fbde5ef1..691cd8749 100644
--- a/Doc/Zsh/.distfiles
+++ b/Doc/Zsh/.distfiles
@@ -3,7 +3,7 @@ DISTFILES_SRC='
     arith.yo builtins.yo compat.yo compctl.yo compsys.yo compwid.yo
     cond.yo exec.yo expn.yo filelist.yo files.yo func.yo grammar.yo
     guide.yo index.yo intro.yo invoke.yo jobs.yo metafaq.yo
-    mod_cap.yo mod_clone.yo mod_comp1.yo
+    mod_cap.yo mod_clone.yo mod_comp1.yo mod_complist.yo
     mod_compctl.yo mod_deltochar.yo mod_example.yo mod_files.yo
     mod_mapfile.yo mod_parameter.yo mod_sched.yo mod_stat.yo
     mod_zftp.yo mod_zle.yo
diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index 3b3ba70c0..10202dbce 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -224,10 +224,11 @@ are reset to their default value corresponding to the specified emulation
 mode, except for certain options describing the interactive
 environment; otherwise, only those options likely to cause portability
 problems in scripts and functions are altered.  If the tt(-L) option
-is given, the option tt(LOCAL_OPTIONS) will be set as well, causing
-the effect of the tt(emulate) command to be local to the immediately
-surrounding shell function, if any; normally this is turned off in all
-emulation modes except tt(ksh).
+is given, the options tt(LOCAL_OPTIONS) and tt(LOCAL_TRAPS) will be set as
+well, causing the effects of the tt(emulate) command and any tt(setopt) and
+tt(trap) commands to be local to the immediately surrounding shell
+function, if any; normally these options are turned off in all emulation
+modes except tt(ksh).
 )
 findex(enable)
 cindex(enabling commands)
@@ -263,8 +264,7 @@ findex(export)
 item(tt(export) [ var(name)[tt(=)var(value)] ... ])(
 The specified var(name)s are marked for automatic export
 to the environment of subsequently executed commands.
-Equivalent to tt(typeset -x), except that no parameter will be created
-to hide an existing one in an outer scope.
+Equivalent to tt(typeset -gx).
 If a parameter specified does not
 already exist, it is created in the global scope.
 )
@@ -428,7 +428,7 @@ added by explicit specification.  If has no effect if used with tt(-f).
 )
 alias(history)(fc -l)
 findex(integer)
-item(tt(integer) [ {tt(PLUS())|tt(-)}tt(lrtux) ] [ var(name)[tt(=)var(value)] ... ])(
+item(tt(integer) [ {tt(PLUS())|tt(-)}tt(glrtux) ] [ var(name)[tt(=)var(value)] ... ])(
 Equivalent to tt(typeset -i), except that options irrelevant to
 integers are not permitted.
 )
@@ -522,7 +522,7 @@ endsitem()
 )
 findex(local)
 item(tt(local) [ {tt(PLUS())|tt(-)}tt(ALRUZailrtu) [var(n)]] [ var(name)[tt(=)var(value)] ] ...)(
-Same as tt(typeset), except that the options tt(-x) and
+Same as tt(typeset), except that the options tt(-g), tt(-x) and
 tt(-f) are not permitted.
 )
 findex(log)
@@ -682,15 +682,13 @@ Read only one character from the terminal and set var(name) to
 With this flag set the return value is zero only if the character was
 `tt(y)' or `tt(Y)'.  Note that this always reads from the terminal, even
 if used with the tt(-p) or tt(-u) or tt(-z) flags or with redirected input.
-May be called from a zle widget, in which case it will use zle to retrieve
-a key.
+This option may also be used within zle widgets.
 )
 item(tt(-k) [ var(num) ])(
 Read only one (or var(num)) characters.  All are assigned to the first
 var(name), without word splitting.  This flag is ignored when tt(-q) is
 present.  Input is read from the terminal unless one of tt(-u) or tt(-p)
-is present.  May be called from a zle widget with no tt(-u)
-or tt(-p) argument, in which case it will use zle to retrieve a key.
+is present.  This option may also be used within zle widgets.
 )
 item(tt(-z))(
 Read one entry from the editor buffer stack and assign it to the first
@@ -905,7 +903,7 @@ Equivalent to tt(whence -v).
 findex(typeset)
 cindex(parameters, setting)
 cindex(parameters, declaring)
-xitem(tt(typeset) [ {tt(PLUS())|tt(-)}tt(ALRUZafilrtuxm) [var(n)]] [ \
+xitem(tt(typeset) [ {tt(PLUS())|tt(-)}tt(ALRUZafgilrtuxm) [var(n)]] [ \
 var(name)[tt(=)var(value)] ... ])
 item(tt(typeset) -T [ {tt(PLUS()|tt(-))}tt(LRUZrux) ] \
   var(SCALAR)[tt(=)var(value)] var(array))(
@@ -944,6 +942,14 @@ to var(array) sets it to be a single-element array.  Note that
 both tt(typeset -xT ...) and tt(export -T ...) work, but only the
 scalar will be marked for export.
 
+The flag tt(-g) (global) flag is treated specially: it means that any
+resulting parameter will not be restricted to local scope.  Note that this
+does not necessarily mean that the parameter will be global, as the flag
+will apply to any existing parameter (even if unset) from an enclosing
+function.  This flag does not affect the parameter after creation, hence it
+has no effect when listing existing parameters, nor does the flag tt(+g)
+have any effect.
+
 If no var(name) is present, the names and values of all parameters are
 printed.  In this case the attribute flags restrict the display to
 only those parameters that have the specified attributes.  Using
@@ -1192,6 +1198,7 @@ findex(zmodload)
 cindex(modules, loading)
 cindex(loading modules)
 xitem(tt(zmodload) [ tt(-dL) ] [ ... ])
+xitem(tt(zmodload -e) [ ... ])
 xitem(tt(zmodload) [ tt(-a) [ tt(-bcp) [ tt(-I) ] ] ] [ tt(-iL) ] ...)
 item(tt(zmodload) tt(-u) [ tt(-abcdp) [ tt(-I) ] ] [ tt(-iL) ] ...)(
 tt(zmodload) performs operations relating to zsh's loadable modules.
@@ -1287,6 +1294,21 @@ xitem(tt(zmodload) tt(-a) [ tt(-i) ] var(name) [ var(builtin) ... ])
 item(tt(zmodload) tt(-ua) [ tt(-i) ] var(builtin) ...)(
 Equivalent to tt(-ab) and tt(-ub).
 )
+item(tt(zmodload -e) [ var(string) ... ])(
+The tt(-e) option without arguments lists all modules loaded or linked 
+into the shell. With arguments only the return status is set to zero
+if all var(string)s given as arguments are names of modules loaded or
+linked in and to one if at least on var(string) is not the name of a
+module loaded or linked. This can be used to test for the availability 
+of things implemented by modules.
+)
 enditem()
+
+In a shell without dynamic loading only the tt(-e) option is
+supported. In such a shell the return status of tt(zmodload) without
+arguments or options is one whereas in a shell with dynamic loading
+the return status without arguments or options is always zero. This
+can be used to test if the shell supports dynamic loading of modules
+or not.
 )
 enditem()
diff --git a/Doc/Zsh/compctl.yo b/Doc/Zsh/compctl.yo
index 20721dfec..8f7c23f25 100644
--- a/Doc/Zsh/compctl.yo
+++ b/Doc/Zsh/compctl.yo
@@ -13,7 +13,7 @@ tt(compctl) command.
 )\
 ifnzman(\
 sect(Types of completion)
-This version of zsh has two ways of performing completino of words on the
+This version of zsh has two ways of performing completion of words on the
 command line.  New users of the shell may prefer to use the newer
 and more powerful system based on shell functions; this is described
 in noderef(Completion System), and the basic shell mechanisms which support
@@ -633,7 +633,8 @@ var(max) inclusive.
 )
 item(tt(r[)var(str1)tt(,)var(str2)tt(])...)(
 Matches if the cursor is after a word with prefix var(str1).  If there
-is also a word with prefix var(str2) on the command line it matches
+is also a word with prefix var(str2) on the command line after the one 
+matched by var(str1) it matches
 only if the cursor is before this word. If the comma and var(str2) are
 omitted, it matches if the cursor is after a word with prefix var(str1).
 )
diff --git a/Doc/Zsh/compsys.yo b/Doc/Zsh/compsys.yo
index f481ee4c0..2de4e4bd7 100644
--- a/Doc/Zsh/compsys.yo
+++ b/Doc/Zsh/compsys.yo
@@ -6,51 +6,29 @@ cindex(completion, controlling)
 sect(Description)
 
 This describes the shell code for the new completion system.  It consists
-of two scripts and a few other files that define shell functions.
-The shell functions which implement completion behaviour and which may
-be bound to keystrokes, are referred to as `widgets'.  All are contained
-in the following subdirectories of the tt(Completion) directory of the main
-distribution directory.
-
-startitem()
-item(tt(Core))(
-The core scripts and functions.  You will certainly need these, though will
-probably not need to alter them.  The contents of this directory is
-described in more detail below.
-)
-item(tt(Base))(
-Other functions you will almost certainly want if you are going to use
-any of the standard completion functions.  You may want to edit some of
-these files.
-)
-item(tt(Builtins))(
-Functions for completing arguments of shell builtin commands.
-)
-item(tt(User))(
-Functions for completing arguments of external commands and suites of
-commands.  They may need modifying for your system.
-)
-item(tt(Commands))(
-Functions which implement special types of completion to be bound to
-keystrokes rather than called by context.
-)
-enditem()
-
-You should decide which files you will be using and copy them to a
-directory (or multiple directories) of your own which should appear in your
-tt($fpath) variable so that the functions can be autoloaded.
+of varsious shell functions; those beginning `tt(comp)' are to be called
+directly by the user, while those beginning `tt(_)' are called by the
+completion code.  The shell functions of the second set which implement
+completion behaviour and which may be bound to keystrokes, are referred to
+as `widgets'.
 
 startmenu()
 menu(Initialization)
 menu(Control Functions)
 menu(Completion Functions)
+menu(Completion Directories)
 endmenu()
 
 texinode(Initialization)(Control Functions)()(Completion System)
 sect(Initialization)
 
-The script tt(compinstall) can be run by a user to set up the completion
-system for use.  It will usually insert code into tt(.zshrc), although if
+The function tt(compinstall) can be run by a user to set up the completion
+system for use, which also provides options for more advanced usage.
+However, if the system was installed completely, it should be enough to
+call the shell function tt(compinit) from your initialization file; see the
+next section.
+
+Usually, tt(compinstall) will insert code into tt(.zshrc), although if
 that is not writable it will save it in another file and tell you that
 file's locations.  Note that it is up to you to make sure that the lines
 added to tt(.zshrc) are actually run; you may, for example, need to move
@@ -62,10 +40,10 @@ this section by hand is likely to be lost if you rerun tt(compinstall).
 The new code will take effect next time you start the shell, or run
 tt(.zshrc) by hand.
 
-You can run it as `tt(source )var(<path>)tt(/compinstall)' or
-`tt(. )var(<path>)tt(/compinstall)', where var(<path>) is where the
-completion functions are stored.  It will ask you various questions about
-how you would like completion set up.  It is in two parts; the basic part
+To run it, you will need to make sure it is in a directory mentioned in your
+tt($fpath) parameter, and that it is autoloaded (`tt(autoload -U
+compinstall)' is recommended).  It will ask you various questions about how
+you would like completion set up.  It is in two parts; the basic part
 locates the completion files and decides where to put your personal
 dumpfile, used to speed up initialization after the first time.  After
 that, you will be asked if you wish to go on to the advanced set-up; if you
@@ -86,30 +64,21 @@ This section describes the use of tt(compinit) to initialize completion for
 the current session when run directly by the user; if you have run
 tt(compinstall) it will be called automatically from your tt(.zshrc).
 
-To initialize the system, the script tt(compinit) should be sourced with
-`tt(source )var(<path>)tt(/compinit)' or
-`tt(. )var(<path>)tt(/compinit)'. This will define a few utility functions,
-arrange for all the necessary shell functions to be autoloaded, and will
-then re-bind all keys that do completion to use the new system.
+To initialize the system, the function tt(compinit) should be in a
+directory mentioned in the tt($fpath) variable, and should be autoloaded
+(`tt(autoload -U compinit)' is recommended).  When run, it will define a
+few utility functions, arrange for all the necessary shell functions to be
+autoloaded, and will then re-bind all keys that do completion to use the
+new system.
 
 To speed up the running of tt(compinit), it can be made to produce a dumped
-configuration which will be read in on future invocations.  The easiest way
-to do this is by adding the option tt(-d) whenever tt(compinit) is sourced.
-In this case the dumped file will have the same name as the sourced file,
-but with tt(.dump) appended to the end, or, if that is not writable by the
-user, the file tt(.zcompdump) in the same directory as the startup files
-(i.e. tt($ZDOTDIR) or tt($HOME)); alternatively, an explicit file name can
-be given following the tt(-d).  On the next call to tt(compinit -d), the
-dumped file will be read instead.
-
-The other option accepted by tt(compinit) is tt(-f )var(dir), which gives
-the directory in which tt(compinit) resides.  If you source tt(compinit) by
-its full pathname, and the option tt(FUNCTION_ARGZERO) is set, as it is by
-default unless tt(zsh) is emulating tt(sh) or tt(ksh), this is unnecessary
-as tt(compinit) can deduce the directory for itself.  It is used in two
-ways: to find the program tt(compdump) used by the tt(-d) option, and to
-check if the directory should be added to the function search path to find
-the completion functions (see below).
+configuration which will be read in on future invocations; this is the
+default, although it can be turned off by calling tt(compinit) with the
+option tt(-D).  The dumped file is tt(.zcompdump) in the same
+directory as the startup files (i.e. tt($ZDOTDIR) or tt($HOME));
+alternatively, an explicit file name can be given following the option
+tt(-d).  On the next call to tt(compinit -d), the dumped file will be read
+instead.
 
 If the number of completion files changes, tt(compinit) will recognise this
 and produce a new dump file.  However, if the name of a function or the
@@ -117,27 +86,34 @@ arguments in the first line of a tt(#compdef) function (as described below)
 change, it is easiest to delete the dump file by hand so that the next time
 tt(compinit) will re-create it.
 
-The dumping is actually done by another script, tt(compdump), but you will
-only need to source this yourself if you change the configuration
+The dumping is actually done by another function, tt(compdump), but you
+will only need to run this yourself if you change the configuration
 (e.g. using tt(compdef)) and then want to dump the new one.  The name of
-the old dumped file will be remembered for this.
+the old dumped file will be remembered for this purpose.
+
+If the parameter tt(_compdir) is set, tt(compinit) uses it has a directory
+where completion functions can be found; this is only necessary if they are
+not already in the function search path.
 
 subsect(Autoloaded files)
 
 The convention for autoloaded functions used in completion is that they
 start with an underscore; as already mentioned, the tt(fpath/FPATH)
-parameter must contain the directory in which they are stored.  If
-tt(compinit) does not find enough files beginning with an underscore (fewer
-than twenty) in the search path, it will try to find more by adding its own
-directory to the search path.  If you keep all the completion files in this
-directory, this means you never have to alter tt(fpath/FPATH) yourself.
+parameter must contain the directory in which they are stored.  If tt(zsh)
+was properly installed on your system, then tt(fpath/FPATH) automatically
+contains the required directories.
+
+For incomplete installations, if tt(compinit) does not find enough files
+beginning with an underscore (fewer than twenty) in the search path, it
+will try to find more by adding the directory tt(_compdir) to the search
+path; if you have run tt(compinstall), this will be set automatically.
 Furthermore, if the directory in question ends in the path segment
 tt(Core), or has a subdirectory named tt(Core), tt(compinit) will add all
 subdirectories of the directory where tt(Core) is to the path: this allows
 the functions to be in the same format as in the tt(zsh) source
 distribution.
 
-When tt(compinit) is sourced, it searches all such files accessible via
+When tt(compinit) is run, it searches all such files accessible via
 tt(fpath/FPATH) and reads the first line of each of them.  This line should
 contain one of the tags described below. Files whose first line does not
 start with one of these tags are not considered to be part of the
@@ -165,7 +141,11 @@ var(key-sequences).  It creates a widget behaving like the builtin widget
 var(style), which must be one of those that perform completion, namely
 tt(complete-word), tt(delete-char-or-list), tt(expand-or-complete),
 tt(expand-or-complete-prefix), tt(list-choices), tt(menu-complete),
-tt(menu-expand-or-complete), or tt(reverse-menu-complete).
+tt(menu-expand-or-complete), or tt(reverse-menu-complete). If the
+tt(complist) module is loaded (see
+ifzman(zmanref(zshmodules))\
+ifnzman(noderef(The complist Module))\
+), the tt(menu-select) widget can be used, too.
 
 The widget is then bound to all the var(key-sequences) given, if any: when
 one of the var(key-sequences) is typed, the function in the file will
@@ -248,8 +228,8 @@ enditem()
 texinode(Control Functions)(Completion Functions)(Initialization)(Completion System)
 sect(Control Functions)
 
-The initialization script tt(compinit) re-binds all the keys which perform
-completion to newly created widgets that all call the supplied widget
+The initialization script tt(compinit) redefines all the widgets
+which perform completion to call the supplied widget
 function tt(_main_complete). This function acts as a wrapper calling
 the so-called `completer' functions that generate matches. If
 tt(_main_complete) is
@@ -474,6 +454,12 @@ configuration key tt(match_original) has a value of `tt(only)', no
 `tt(*)' will be inserted. If tt(match_original) has any other non-empty
 string as its value, this completer will first try to generate matches
 without, then with a `tt(*)' inserted at the cursor position.
+
+The generated matches will be offered in a menucompletion unless the
+tt(match_insert) configuration key is set to a string starting with
+`tt(unambig)'. In this case menucompletion will only be started if no
+unambiguous string could be generated that is at least as long as the
+original string.
 )
 item(tt(_expand))(
 This completer function does not really do completion, but instead
@@ -620,7 +606,7 @@ enditem()
 )
 enditem()
 
-texinode(Completion Functions)()(Control Functions)(Completion System)
+texinode(Completion Functions)(Completion Directories)(Control Functions)(Completion System)
 sect(Utility Functions)
 
 Descriptions follow for utility functions that may be
@@ -707,10 +693,13 @@ These functions also accept the `tt(-J)', `tt(-V)', `tt(-X)', `tt(-P)',
 `tt(-S)', `tt(-q)', `tt(-r)', and `tt(-R)' options from the
 tt(compadd) builtin.
 
-Finally, the tt(_path_files) function supports one configuration key:
-tt(path_expand). If this is set to any non-empty string, the partially
+Finally, the tt(_path_files) function supports two configuration keys.
+If tt(path_expand) is set to any non-empty string, the partially
 typed path from the line will be expanded as far as possible even if
-trailing pathname components can not be completed.
+trailing pathname components can not be completed. And if
+tt(path_cursor) is set to a non-empty string, the cursor will be left
+after the first ambiguous pathname component even when menucompletion
+is used.
 )
 item(tt(_parameters))(
 This should be used to complete parameter names if you need some of the
@@ -779,3 +768,37 @@ accepts the option `tt(-t)'; if this is given, completion is only done
 on words starting with two hyphens.
 )
 enditem()
+
+texinode(Completion Directories)()(Completion Functions)(Completion System)
+sect(Completion Directories)
+
+In the source distribution, the files are contained in various
+subdirectories of the tt(Completion) directory.  They may have been
+installed in the same structure, or into one single function directory.
+The following is a description of the files found in the original directory
+structure.  If you wish to alter an installed file, you will need to copy
+it to some directory which appears earlier in your tt(fpath) than the
+standard directory where it appears.
+
+startitem()
+item(tt(Core))(
+The core scripts and functions.  You will certainly need these, though will
+probably not need to alter them.  Many of these are docmented above.
+)
+item(tt(Base))(
+Other functions you will almost certainly want if you are going to use
+any of the standard completion functions.  You may want to edit some of
+these files.
+)
+item(tt(Builtins))(
+Functions for completing arguments of shell builtin commands.
+)
+item(tt(User))(
+Functions for completing arguments of external commands and suites of
+commands.  They may need modifying for your system.
+)
+item(tt(Commands))(
+Functions which implement special types of completion to be bound to
+keystrokes rather than called by context.
+)
+enditem()
diff --git a/Doc/Zsh/compwid.yo b/Doc/Zsh/compwid.yo
index 923323aa7..644578354 100644
--- a/Doc/Zsh/compwid.yo
+++ b/Doc/Zsh/compwid.yo
@@ -238,7 +238,9 @@ upwards (e.g. `tt(2:4)' specifies the fourth match of the second group).
 Negative numbers count backward from the last match or group (with `tt(-1)'
 selecting the last match or group) and out-of-range values are wrapped
 around, so that a value of zero selects the last match or group and a value
-one more than the maximum selects the first.
+one more than the maximum selects the first. Unless the value of this
+key ends in a space, the match is inserted as in a menu-completion,
+i.e. without automatically appending a space.
 )
 item(tt(to_end))(
 Specifies the occasions on which the cursor is moved to the end of a string
diff --git a/Doc/Zsh/mod_compctl.yo b/Doc/Zsh/mod_compctl.yo
index 91f6112ee..785767e45 100644
--- a/Doc/Zsh/mod_compctl.yo
+++ b/Doc/Zsh/mod_compctl.yo
@@ -1,4 +1,4 @@
-texinode(The compctl Module)(The deltochar Module)(The comp1 Module)(Zsh Modules)
+texinode(The compctl Module)(The complist Module)(The comp1 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
diff --git a/Doc/Zsh/mod_complist.yo b/Doc/Zsh/mod_complist.yo
index 460d4a609..101f4e69c 100644
--- a/Doc/Zsh/mod_complist.yo
+++ b/Doc/Zsh/mod_complist.yo
@@ -92,12 +92,17 @@ matches to insert into the command line can be selected from this
 list. In the list one match is highlighted using the value for tt(ma)
 from the tt(ZLS_COLORS) or tt(ZLS_COLOURS) parameter. The default
 value for this it `tt(7)' which forces the selected match to be
-highlighted using standout mode on a vt100 compatible terminal.
+highlighted using standout mode on a vt100 compatible terminal. If
+neither tt(ZLS_COLORS) nor tt(ZLS_COLOURS) is set, the same terminal
+control sequence is used as for the `tt(%S)' escape in prompts.
 
 Selecting matches is done by moving the mark around using the zle movement
 functions. The zle functions tt(send-break) and tt(accept-line) can be used
 to leave menu-selection, leaving the match currently inserted into the line
-in place. The functions tt(accept-and-hold) and
+in place. In the case of tt(accept-line), the match currently inserted
+will be accepted and the immediatly trying completion again will
+complete after it. Using tt(send-break) leaves menu-selection and
+continues with normal menu-completion. The functions tt(accept-and-hold) and
 tt(accept-and-menu-complete) can be used to accept the match currently
 inserted and continue inserting matches after that. Matches inserted this
 way can be removed by invoking the tt(undo) function. Keys bound to one of
@@ -116,4 +121,9 @@ keys in the the tt(menuselect) keymap can be modified directly using the
 tt(bindkey) builtin command (see
 ifzman(zmanref(zshmodules))\
 ifnzman(noderef(The zle Module))\
-).
+). For example, to make the return key leave menu-selection and
+continue with normal menu-completion one can call
+
+indent(tt(bindkey -M menuselect '^J' send-break))
+
+after loading the tt(complist) module.
diff --git a/Doc/Zsh/mod_deltochar.yo b/Doc/Zsh/mod_deltochar.yo
index f92a3da0d..4c3cf58e6 100644
--- a/Doc/Zsh/mod_deltochar.yo
+++ b/Doc/Zsh/mod_deltochar.yo
@@ -1,4 +1,4 @@
-texinode(The deltochar Module)(The example Module)(The compctl Module)(Zsh Modules)
+texinode(The deltochar Module)(The example Module)(The complist Module)(Zsh Modules)
 sect(The deltochar Module)
 The tt(deltochar) module makes available one ZLE function:
 
diff --git a/Doc/Zsh/mod_zle.yo b/Doc/Zsh/mod_zle.yo
index 185f5118a..cae4eadc2 100644
--- a/Doc/Zsh/mod_zle.yo
+++ b/Doc/Zsh/mod_zle.yo
@@ -72,6 +72,17 @@ be empty.
 )
 enditem()
 
+To use a newly created keymamp, it should be linked to tt(main).  Hence
+the sequence of commands to create and use a new keymap `tt(mymap)'
+initialized from the tt(emacs) keymap (which remains unchanged) is:
+
+example(bindkey -N mymap emacs
+bindkey -A mymap main)
+
+Note that while `tt(bindkey -A) var(newmap) tt(main)' will work when
+var(newmap) is tt(emacs) or tt(viins), it will not work for tt(vicmd), as
+switching from vi insert to command mode becomes impossible.
+
 The following operations require a keymap to be selected:
 
 startitem()
@@ -161,7 +172,7 @@ cindex(widgets, calling)
 cindex(calling widgets)
 cindex(widgets, defining)
 cindex(defining widgets)
-xitem(tt(zle) tt(-l) [ tt(-L) ])
+xitem(tt(zle) tt(-l) [ tt(-L) ] [ tt(-a) ] [ var(string) ... ])
 xitem(tt(zle) tt(-D) var(widget) ...)
 xitem(tt(zle) tt(-A) var(old-widget) var(new-widget))
 xitem(tt(zle) tt(-N) var(widget) [ var(function) ])
@@ -176,7 +187,15 @@ item(tt(-l) [ tt(-L) ])(
 List all existing user-defined widgets.  If the tt(-L)
 option is used, list in the form of tt(zle)
 commands to create the widgets.
-Built-in widgets are not listed.
+
+When combined with the tt(-a) option, all widget names are listed,
+including the builtin ones. In this case the tt(-L) option is ignored.
+
+If at least one var(string) is given, nothing will be printed but the
+return status will be zero if all var(string)s are names of existing
+widgets (or of user-defined widgets if the tt(-a) flag is not given)
+and non-zero if at least one var(string) is not a name of an defined
+widget.
 )
 item(tt(-D) var(widget) ...)(
 Delete the named var(widget)s.
@@ -234,7 +253,12 @@ them.
 
 The return status reflects the success or failure of the operation carried
 out by the widget, or if it is a user-defined widget the return status of
-the shell function.
+the shell function.  
+
+A non-zero return status causes the shell to beep when the widget exits,
+unless the tt(BEEP) options was unset or the widget was called via the
+tt(zle) command.  Thus if a user defined widget requires an immediate beep,
+it should call the tt(beep) widget directly.
 )
 enditem()
 )
diff --git a/Doc/Zsh/modules.yo b/Doc/Zsh/modules.yo
index 0b487f673..12066b773 100644
--- a/Doc/Zsh/modules.yo
+++ b/Doc/Zsh/modules.yo
@@ -22,6 +22,9 @@ item(tt(compctl))(
 The tt(compctl) builtin for controlling completion and the builtins for
 completion widgets.
 )
+item(tt(complist))(
+Completion listing extensions.
+)
 item(tt(deltochar))(
 A ZLE function duplicating EMACS' tt(zap-to-char).
 )
@@ -55,6 +58,7 @@ menu(The cap Module)
 menu(The clone Module)
 menu(The comp1 Module)
 menu(The compctl Module)
+menu(The complist Module)
 menu(The deltochar Module)
 menu(The example Module)
 menu(The files Module)
@@ -69,6 +73,7 @@ includefile(Zsh/mod_cap.yo)
 includefile(Zsh/mod_clone.yo)
 includefile(Zsh/mod_comp1.yo)
 includefile(Zsh/mod_compctl.yo)
+includefile(Zsh/mod_complist.yo)
 includefile(Zsh/mod_deltochar.yo)
 includefile(Zsh/mod_example.yo)
 includefile(Zsh/mod_files.yo)
diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index e7029370d..265c5e184 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -557,7 +557,10 @@ pindex(LIST_BEEP)
 cindex(beep, ambiguous completion)
 cindex(completion, beep on ambiguous)
 item(tt(LIST_BEEP))(
-Beep on an ambiguous completion.
+Beep on an ambiguous completion.  More accurately, this forces the
+completion widgets to return status 1 on an ambiguous completion, which
+causes the shell to beep if the option tt(BEEP) is also set; this may
+be modified if completion is called from a user-defined widget.
 )
 pindex(LIST_TYPES)
 cindex(marking file types)
diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index 9f0b34193..ecf93864f 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -299,8 +299,11 @@ The current hostname.
 )
 vindex(LINENO)
 item(tt(LINENO) <S>)(
-The line number of the current line within the current script
-being executed.
+The line number of the current line within the current script, sourced
+file, or shell function being executed, whichever was started most
+recently.  Note that in the case of shell functions the line
+number refers to the function as it appeared in the original definition,
+not necesarily as displayed by the tt(functions) builtin.
 )
 vindex(LOGNAME)
 item(tt(LOGNAME))(
@@ -647,7 +650,9 @@ The default is `tt(?# )'.
 )
 vindex(PS4)
 item(tt(PS4) <S>)(
-The execution trace prompt.  Default is `tt(PLUS() )'.
+The execution trace prompt.  Default is `tt(PLUS()%N:%i> )', which displays
+the name of the current shell structure and the line number within it.
+In sh or ksh emulation, the default is `tt(PLUS() )'.
 )
 vindex(PROMPT)
 xitem(tt(PROMPT) <S> <Z>)
@@ -858,6 +863,19 @@ item(tt(WORDCHARS) <S>)(
 A list of non-alphanumeric characters considered part of a word
 by the line editor.
 )
+vindex(ZBEEP)
+item(tt(ZBEEP))(
+If set, this gives a string of characters, which can use all the same codes
+as the tt(bindkey) command as described in
+ifzman(zmanref(zshzle))\
+ifnzman(noderef(Zsh Line Editor))\
+, that will be output to the terminal
+instead of beeping.  This may have a visible instead of an audible effect;
+for example, the string `tt(\e[?5h\e[?5l)' on a vt100 or xterm will have
+the effect of flashing reverse video on and off (if you usually use reverse
+video, you should use the string `tt(\e[?5l\e[?5h)' instead).  This takes
+precedence over the tt(NOBEEP) option.
+)
 vindex(ZDOTDIR)
 item(tt(ZDOTDIR))(
 The directory to search for shell startup files (.zshrc, etc),
diff --git a/Doc/Zsh/prompt.yo b/Doc/Zsh/prompt.yo
index 685cca8c4..29f1f2c22 100644
--- a/Doc/Zsh/prompt.yo
+++ b/Doc/Zsh/prompt.yo
@@ -95,6 +95,16 @@ Current time of day in 24-hour format, with seconds.
 item(tt(%n))(
 tt($USERNAME).
 )
+item(tt(%N))(
+The name of the script, sourced file, or shell function that zsh is
+currently executing, whichever was started most recently.  If there is
+none, this is equivalent to the parameter tt($0).
+)
+item(tt(%i))(
+The line number currently being executed in the script, sourced file, or
+shell function given by tt(%N).  This is most useful for debugging as part
+of tt($PS4).
+)
 item(tt(%w))(
 The date in var(day)tt(-)var(dd) format.
 )
@@ -122,7 +132,9 @@ item(tt(%_))(
 The status of the parser, i.e. the shell constructs (like `tt(if)' and
 `tt(for)') that have been started on the command line. If given an integer
 number that many strings will be printed; zero or no integer means
-print as many as there are.
+print as many as there are.  This is most useful in prompts tt(PS2) for
+continuation lines and tt(PS4) for debugging with the tt(XTRACE) option; in
+the latter case it will also work non-interactively.
 )
 item(tt(%E))(
 Clears to end of line.
diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo
index ba197facf..ce1b27fec 100644
--- a/Doc/Zsh/zle.yo
+++ b/Doc/Zsh/zle.yo
@@ -103,8 +103,7 @@ User-defined widgets, being implemented as shell functions,
 can execute any normal shell command.  They can also run other widgets
 (whether built-in or user-defined) using the tt(zle) builtin command.
 They can use tt(read -k) or tt(read -q) to read characters from standard
-input, which will use the appropriate tt(zle) key input mechanism.
-Finally, they can examine and edit the ZLE buffer being edited by
+input.  Finally, they can examine and edit the ZLE buffer being edited by
 reading and setting the special parameters described below.
 
 cindex(parameters, editor)
@@ -957,6 +956,10 @@ item(tt(accept-line-and-down-history) (^O) (unbound) (unbound))(
 Execute the current line, and push the next history
 event on the the buffer stack.
 )
+tindex(beep)
+item(tt(beep))(
+Beep, unless the tt(BEEP) option is unset.
+)
 tindex(vi-cmd-mode)
 item(tt(vi-cmd-mode) (^X^V) (unbound) (^[))(
 Enter command mode; that is, select the `tt(vicmd)' keymap.
diff --git a/Etc/FAQ.yo b/Etc/FAQ.yo
index 75a732a2b..840a17196 100644
--- a/Etc/FAQ.yo
+++ b/Etc/FAQ.yo
@@ -49,22 +49,11 @@ description(
 mydit(Archive-Name:) unix-faq/shell/zsh
 mydit(Last-Modified:) 1999/05/24
 mydit(Submitted-By:) email(pws@ibmth.df.unipi.it (Peter Stephenson))
-mydit(Version:) $Id: FAQ.yo,v 1.1.1.6 1999/06/27 06:54:50 akr Exp $
+mydit(Version:) $Id: FAQ.yo,v 1.1.1.7 1999/07/03 13:17:25 akr Exp $
 mydit(Posting-Frequency:) Monthly
 mydit(Copyright:) (C) P.W. Stephenson, 1995--1999 (see end of document)
 )
 
-bf(Changes since issue posted April 1999:)
-description(
-mydit(*.*)  Orthography for option names standardised.
-mydit(1.4)  don't change config.h.in, change acconfig.h
-mydit(1.6)  email zefram@zsh.org
-mydit(2.4)  new widget version of tcsh run-fg-editor
-mydit(3.7)  should have mentioned tt(PRINT_EIGHT_BIT) is new in 3.1
-mydit(3.23) new: describe tt(PROMPT_CR) option.
-mydit(5.1)  Description of tt(LIST_AMBIGUOUS) corrected.
-)
-
 This document contains a list of frequently-asked (or otherwise
 significant) questions concerning the Z-shell, a command interpreter
 for many UNIX systems which is freely available to anyone with FTP
@@ -125,6 +114,7 @@ Chapter 3:  How to get various things to work
 3.21. Why is my history not being saved?
 3.22. How do I get a variable's value to be evaluated as another variable?
 3.23. How do I prevent the prompt overwriting output when there is no newline?
+3.24. What's wrong with cut and paste on my xterm?
 
 Chapter 4:  The mysteries of completion
 4.1. What is completion?
@@ -309,11 +299,11 @@ sect(What's the latest version?)
   list of incompatibilities at the end of link(5.1)(51).  This is longer
   than usual due to enhanced sh, ksh and POSIX compatibility.
 
-  The beta version 3.1.5 is also available.  Development of zsh is
-  usually patch by patch, with each intermediate version publicly
-  available.  Note that this `open' development system does mean bugs
-  are sometimes introduced into the most recent archived version.
-  These are usually fixed quickly.
+  The beta version 3.1.5 is also available, and 3.1.6 should appear over
+  the summer.  Development of zsh is usually patch by patch, with each
+  intermediate version publicly available.  Note that this `open'
+  development system does mean bugs are sometimes introduced into the most
+  recent archived version.  These are usually fixed quickly.
 
   Note also that as the shell changes, it may become incompatible with
   older versions; see the end of question link(5.1)(51) for a partial list.
@@ -326,17 +316,20 @@ sect(What's the latest version?)
 sect(Where do I get it?)
 label(16)
 
-  The archive is now run by email(Andrew Main <zefram@zsh.org>).
-  The following are known mirrors (kept frequently up to date); the
+  The coordinator of development is currently me; the alias
+  email(coordinator@zsh.org) can be used to contact whoever is in the hot
+  seat.  The following are known mirrors (kept frequently up to date); the
   first is the official archive site, currently in Australia.  All are
-  available by anonymous FTP.  The major sites keep test versions in
-  the 'testing' subdirectory: such up-to-the-minute development
-  versions should only be retrieved if you actually plan to help test
-  the latest version of the shell.  The following list also appears
-  on the WWW at url(http://www.zsh.org)(http://www.zsh.org) .
+  available by anonymous FTP.  The major sites keep test versions in the
+  `testing' subdirectory: such up-to-the-minute development versions should
+  only be retrieved if you actually plan to help test the latest version of
+  the shell.  The following list also appears on the WWW at
+  url(http://www.zsh.org)(http://www.zsh.org) .
 
   description(
     mydit(Home site) url(ftp://ftp.zsh.org)(ftp://ftp.zsh.org)
+    mydit()          url(http://www.zsh.org/pub/zsh/)
+(http://www.zsh.org/pub/zsh/)
     mydit(Australia) url(ftp://ftp.ips.gov.au/mirror/zsh/)
 (ftp://ftp.ips.gov.au/mirror/zsh/)
     mydit(Denmark)   url(ftp://sunsite.auc.dk/pub/unix/shells/zsh)
@@ -354,7 +347,9 @@ label(16)
     mydit(Hungary)   url(ftp://ftp.cs.elte.hu/pub/zsh/)
 (ftp://ftp.cs.elte.hu/pub/zsh/)
     mydit()          (also url(http://www.cs.elte.hu/pub/zsh/)
-                   (http://www.cs.elte.hu/pub/zsh/) )
+(http://www.cs.elte.hu/pub/zsh/) )
+    mydit()          url(ftp://ftp.kfki.hu/pub/packages/zsh/)
+(ftp://ftp.kfki.hu/pub/packages/zsh/)
     mydit(Israel)    \
 url(ftp://ftp.math.technion.ac.il/mirror/ftp.zsh.org/pub/zsh/)
 (ftp://ftp.math.technion.ac.il/mirror/ftp.zsh.org/pub/zsh/)
@@ -363,7 +358,7 @@ url(http://www.math.technion.ac.il/mirror/ftp.zsh.org/pub/zsh/)
 (http://www.math.technion.ac.il/mirror/ftp.zsh.org/pub/zsh/)
     mydit(Japan)     url(ftp://ftp.tohoku.ac.jp/mirror/zsh/)
 (ftp://ftp.tohoku.ac.jp/mirror/zsh/)
-    mydit()          url(ftp://ftp.nis.co.jp/pub/shells/zsh/)
+    mydit()          url(ftp://ftp.nisiq.net/pub/shells/zsh/)
 (ftp://ftp.nis.co.jp/pub/shells/zsh/)
     mydit()          url(ftp://ftp.win.ne.jp/pub/shell/zsh/)
 (ftp://ftp.win.ne.jp/pub/shell/zsh/)
@@ -378,8 +373,8 @@ url(http://www.math.technion.ac.il/mirror/ftp.zsh.org/pub/zsh/)
     mydit(UK)        url(ftp://ftp.net.lut.ac.uk/zsh/)
 (ftp://ftp.net.lut.ac.uk/zsh/)
     mydit()          (also by FSP at port 21)
-    mydit()          url(ftp://src.doc.ic.ac.uk/packages/unix/shells/zsh/)
-(ftp://src.doc.ic.ac.uk/packages/unix/shells/zsh/)
+    mydit()          url(ftp://sunsite.org.uk/packages/zsh/)
+(ftp://sunsite.org.uk/packages/zsh/)
     mydit(USA)       url(ftp://ftp.math.gatech.edu/pub/zsh/)
 (ftp://ftp.math.gatech.edu/pub/zsh/)
     mydit()          url(ftp://uiarchive.uiuc.edu/pub/packages/shells/zsh/)
@@ -388,6 +383,10 @@ url(http://www.math.technion.ac.il/mirror/ftp.zsh.org/pub/zsh/)
 (ftp://ftp.sterling.com/zsh/)
     mydit()          url(ftp://ftp.rge.com/pub/shells/zsh/)
 (ftp://ftp.rge.com/pub/shells/zsh/)
+    mydit()          url(ftp://foad.org/pub/zsh/)
+(ftp://foad.org/pub/zsh/)
+    mydit()          url(http://foad.org/zsh/)
+(http://foad.org/zsh/)
   )
 
   The Windows port mentioned above is maintained separately by email(Amol
@@ -464,7 +463,6 @@ sect(I don't have root access: how do I make zsh my login shell?)
     endif
   )
 
-
   It's not a good idea to put this (even without the -l) into .cshrc,
   at least without some tests on what the csh is supposed to be doing,
   as that will cause _every_ instance of csh to turn into a zsh and
@@ -577,10 +575,6 @@ tt(SH_OPTION_LETTERS),
     it()* The results of parameter substitutions are treated as plain text:
         mytt(foo="*"; print $foo) prints all files in ksh but mytt(*) in zsh
         (uset tt(GLOB_SUBST)).
-    it()  The backslash in tt($(echo '\$x')) is treated differently:  in \
-ksh, it
-        is not stripped, in zsh it is.  (The tt(`...`) form gives the same in
-        both shells.)
     it()* tt($PSn) do not do parameter substitution by default (use \
 PROMPT_SUBST).
     it()* Standard globbing does not allow ksh-style `pattern-lists'.
@@ -641,11 +635,11 @@ link(2.3)(23).
     it()* Options are not local to functions (use LOCAL_OPTIONS; note this
         may always be unset locally to propagate options settings from a
         function to the calling level).
-    it()  Function definitions themselves are not local to functions.
   )
     it() Traps and signals:
   itemize(
-    it()  Traps are not local to functions.
+    it()* Traps are not local to functions.  The option LOCAL_TRAPS will
+          be available from 3.1.6.
     it()  TRAPERR has become TRAPZERR (this was forced by UNICOS which
         has SIGERR).
   )
@@ -660,6 +654,8 @@ link(2.3)(23).
         release either.)
     it()  Management of histories in multiple shells is different:
         the history list is not saved and restored after each command.
+        (The option tt(SHARE_HISTORY) will appear in 3.1.6 and will be
+        set in ksh compatibility mode to remedy this.)
     it()  mytt(\) does not escape editing chars (use mytt(^V)).
     it()  Not all ksh bindings are set (e.g. mytt(<ESC>#); try mytt(<ESC>q)).
     it()* mytt(#) in an interactive shell is not treated as a comment by
@@ -1155,15 +1151,16 @@ sect(How do I automatically display the directory in my xterm title bar?)
 
 sect(How do I make the completion list use eight bit characters?)
 
-  If you are sure your terminal handles this, the easiest way from version
-  3.1 of the shell is to set the option tt(PRINT_EIGHT_BIT).  In principle,
-  this will work automatically if your computer uses the `locale' system
-  and your locale variables are set properly, as zsh understands this.
-  However, it is quite complicated, so if it isn't already set up, trying
-  the option is a lot easier.  For 3.0, you are stuck with trying to
-  understand locales, see the tt(setlocale(3)) and tt(zshparam(1)) manual
-  pages:  the simplest possibility may be to set tt(LC_ALL=en_US).  For older
-  versions of the shell, there is no easy way out.
+  If you are sure your terminal handles this, the easiest way from versions
+  3.0.6 and 3.1 of the shell is to set the option tt(PRINT_EIGHT_BIT).  In
+  principle, this will work automatically if your computer uses the
+  `locale' system and your locale variables are set properly, as zsh
+  understands this.  However, it is quite complicated, so if it isn't
+  already set up, trying the option is a lot easier.  For earlier versions
+  of zsh 3, you are stuck with trying to understand locales, see the
+  tt(setlocale(3)) and tt(zshparam(1)) manual pages: the simplest
+  possibility may be to set tt(LC_ALL=en_US).  For older versions of the
+  shell, there is no easy way out.
 
 
 sect(Why do the cursor (arrow) keys not work?)
@@ -1538,8 +1535,8 @@ sect(How do I get a variable's value to be evaluated as another variable?)
   )
   produces the same result.
 
-  Future versions of zsh will probably allow you to do this directly,
-  with a new flag; mytt(${(P)E}).
+  Versions 3.1.6 of zsh will allow you to do this directly with a new flag;
+  mytt(${(P)E}).
 
   As a slight aside, sometimes people note that the syntax mytt(${${E}})
   is valid and expect it to have this effect.  It probably ought to, but
@@ -1569,6 +1566,39 @@ sect(How do I prevent the prompt overwriting output when there is no newline?)
   prompt (see question link(3.13)(313) for that).
 
 
+sect(What's wrong with cut and paste on my xterm?)
+
+  On the majority of modern UNIX systems, cutting text from one window and
+  pasting it into another should work fine.  On a few, however, there are
+  problems due to issues about how the terminal is handled:  most programs
+  expect the terminal to be in `canonical input mode', which means that the
+  program is passed a whole line of input at a time, while for editing
+  the shell needs a single character at a time and must be in
+  `non-canonical input mode'.  On the systems in question, input can be
+  lost or re-ordered when the mode changes.  There are actually two
+  slightly different problems:
+  enumerate(
+  myeit() When you paste something in while a programme is running, so that
+     the shell only retrieves it later.  Traditionally, there was a test
+     which was used only on systems where the problem was known to exist,
+     so it is possible some other systems were not handled (for example,
+     certain versions of IRIX, it appears); also, continuation lines were
+     not handled properly.  A more reliable approach will appear in
+     versions 3.0.6 and 3.1.6.
+  myeit() When the shell is waiting for input, and you paste in a chunk of
+     text consisting of more than one complete set of commands.
+     Unfortunately, this is a much harder problem: the line editor is
+     already active, and needs to be turned off when the first command is
+     executed.  The shell doesn't even know if the remaining text is input
+     to a command or for the shell, so there's simply nothing it can do.
+     However, if you have problems you can trick it: type `tt({)' on a line
+     by itself, then paste the input, then type `tt(})' on a line by
+     itself.  The shell will not execute anything until the final brace is
+     read; all input is read as continuation lines (this may require the
+     fixes referred to above in order to be reliable).
+  )
+
+
 chapter(The mysteries of completion)
 
 Programmable completion using the `compctl' command is one of the most
@@ -1673,7 +1703,7 @@ sect(How does zsh deal with ambiguous completions?)
   flexibility for what it does here via its options.  The default is
   for it to beep and completion to stop until you type another
   character.  You can type tt(\C-D) to see all the possible completions.
-  (That's assuming your at the end of the line, otherwise tt(\C-D) will
+  (That's assuming you're at the end of the line, otherwise tt(\C-D) will
   delete the next character and you have to use tt(ESC-\C-D).)  This can be
   changed by the following options, among others:
   itemize(
@@ -1887,13 +1917,15 @@ this applies
 
   itemize(
   it() mytt(time) is ignored with builtins and can't be used with mytt({...}).
-  it() mytt(set -x) (mytt(setopt xtrace)) still has a few glitches.
+  it() mytt(set -x) (mytt(setopt xtrace)) still has a few glitches; these
+     are mostly fixed in 3.1.6.
   it() Zsh's notion of the current line number (via tt($LINENO)) is
      sometimes not well handled, particularly when using functions and traps.
+     This should also work reliably from 3.1.6.
   it() In vi mode, mytt(u) can go past the original modification point.
   it() The singlelinezle option has problems with prompts containing escapes.
   it() The mytt(r) command does not work inside mytt($(...)) or mytt(`...`)
-     expansions.   (This is fixed in 3.1.)
+     expansions.   This is fixed in 3.1.
   it() mytt(typeset) handling is non-optimal, particularly with regard to
      flags, and is ksh-incompatible in unpredictable ways. 
   it() Nested closures in extended globbing and pattern matching, such as
@@ -1901,8 +1933,8 @@ this applies
       [[ fofo = (fo#)# ]]
   )
      were not correctly handled, and there were problems with
-     complicated exclusions using mytt(^) or mytt(~).  (These
-     are fixed in version 3.1.3.)
+     complicated exclusions using mytt(^) or mytt(~).  These
+     are fixed in version 3.1.3.
   )
 
   Note that a few recent changes introduce incompatibilities (these
@@ -1918,7 +1950,7 @@ this applies
      zsh features are never noticed by many users.  To turn them off,
      just put mytt(unsetopt alwayslastprompt listambiguous) in your
      tt(.zshrc) file.
-  it() tt(history-search-{forward,backward}) now only find previous
+  it() In 3.1.5, tt(history-search-{forward,backward}) only find previous
      lines where the first word is the same as the current one.  For
      example, 
     verb(
@@ -2051,11 +2083,7 @@ label(52)
   email(zsh-workers-owner@sunsite.auc.dk).  The list maintainer's
   real name is email(Karsten Thygesen <karthy@kom.auc.dk>).
   
-  The list from May 1992 to May 1995 is archived in
-    url(ftp://ftp.sterling.com/zsh/zsh-list/YY-MM)
-(ftp://ftp.sterling.com/zsh/zsh-list/YY-MM)
-  where YY-MM are the year and month in digits.  More recent
-  mailings up to date are to be found at
+  An archive of mailings for the last few years can be found at
     url(http://www.zsh.org/mla/)(http://www.zsh.org/mla/)
   at the main zsh archive in Australia.
 
@@ -2089,7 +2117,8 @@ sect(What's on the wish-list?)
      case-insensitive matching and wild card anchors, e.g. mytt(z_t<TAB>)
      can allow a wildcard before the mytt(_) so that this will expand
      to mytt(zle_tricky.c) --- all under user control; completions can
-     be grouped.
+     be grouped; a new completion command, menu-select, allows real menu
+     selection --- you can move the cursor around to choose a completion.
   it() Case-insensitive and approximate matching in the globbing code:
      for example, mytt((#ia2)readme) matches the string mytt(readme)
      case-insensitively with up to two errors, such as tt(README),
diff --git a/Misc/compctl-examples b/Misc/compctl-examples
index 3aa9b15f4..464660477 100644
--- a/Misc/compctl-examples
+++ b/Misc/compctl-examples
@@ -11,15 +11,15 @@
 # page.
 #
 #------------------------------------------------------------------------------
-hosts=("${(@)${(@s: :)${(@s:	:)${(@)${(@f)$(</etc/hosts)}%%\#*}#*[ 	]*}}:#}")
+hosts=( ${(s: :)${(ps:\t:)${${(f)"$(</etc/hosts)"}%%\#*}##[:blank:]#[^[:blank:]]#}} )
 ports=( "${(@)${(@)${(@f)$(</etc/services)}:#\#*}%%[ 	]*}" )
 
 # groups=( $(cut -d: -f1 /etc/group) )
 # groups=( $(ypcat group.byname | cut -d: -f1) ) # if you use NIS
 
 # It can be done without forking, but it used too much memory in old zsh's:
-groups=( "${(@)${(@f)$(</etc/group)}%%:*}" )
-#groups=( "${(@)${(@f)$(ypcat groups)}%%:*}" ) # if you use NIS
+groups=( ${${(s: :)$(</etc/group)}%%:*} )
+#groups=( ${${(s: :)$(ypcat groups)}%%:*} ) # if you use NIS
 
 # Completion for zsh builtins.
 compctl -z -P '%' bg
diff --git a/Src/Makefile.in b/Src/Makefile.in
index e98a696e4..0dcfbdee1 100644
--- a/Src/Makefile.in
+++ b/Src/Makefile.in
@@ -206,9 +206,9 @@ distclean: distclean-modules
 realclean: realclean-modules
 
 # Don't remake Makemod just to delete things, even if it doesn't exist.
-mostlyclean-modules clean-modules distclean-modules realclean-modules:
+mostlyclean-modules clean-modules distclean-modules realclean-modules: modules.index
 	if test -f Makemod; then \
-	  @$(MAKE) -f Makemod $(MAKEDEFS) `echo $@ | sed 's/-modules//'`; \
+	  $(MAKE) -f Makemod $(MAKEDEFS) `echo $@ | sed 's/-modules//'`; \
 	fi; \
 	exit 0
 
diff --git a/Src/Zle/.distfiles b/Src/Zle/.distfiles
index 9dbf27758..add8f8466 100644
--- a/Src/Zle/.distfiles
+++ b/Src/Zle/.distfiles
@@ -2,6 +2,7 @@ DISTFILES_SRC='
     .cvsignore .distfiles .exrc
     comp1.mdd comp.h comp1.c comp1.export
     compctl.mdd compctl.c
+    complist.mdd complist.c
     deltochar.mdd deltochar.c
     zle.mdd iwidgets.list zle.h zle_bindings.c zle_hist.c
     zle_keymap.c zle_main.c zle_misc.c zle_move.c zle_params.c
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index 78c65ffeb..8264890df 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -36,6 +36,7 @@ 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) */
 
@@ -266,6 +267,19 @@ struct cpattern {
 #define CFN_FIRST   1
 #define CFN_DEFAULT 2
 
+/* Information about menucompletion stuff. */
+
+struct menuinfo {
+    Cmgroup group;		/* position in the group list */
+    Cmatch *cur;		/* match currently inserted */
+    int pos;			/* begin on line */
+    int len;			/* length of inserted string */
+    int end;			/* end on the line */
+    int we;			/* non-zero if the cursor was at the end */
+    int insc;			/* length of suffix inserted */
+    int asked;			/* we asked if the list should be shown */
+};
+
 /* Flags for compadd and addmatches(). */
 
 #define CAF_QUOTE    1
@@ -299,6 +313,16 @@ struct cadata {
     char *dpar;			/* array to delete non-matches in (-D) */
 };
 
+/* Data given to hooks. */
+
+typedef struct chdata *Chdata;
+
+struct chdata {
+    Cmgroup matches;		/* the matches generated */
+    int num;			/* the number of matches */
+    Cmatch cur;			/* current match or NULL */
+};
+
 /* Flags for special parameters. */
 
 #define CPN_WORDS      0
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index e0f9a9089..2b426492e 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -2221,7 +2221,8 @@ static void
 addcompparams(struct compparam *cp, Param *pp)
 {
     for (; cp->name; cp++, pp++) {
-	Param pm = createparam(cp->name, cp->type | PM_SPECIAL | PM_REMOVABLE);
+	Param pm = createparam(cp->name,
+			       cp->type |PM_SPECIAL|PM_REMOVABLE|PM_LOCAL);
 	if (!pm)
 	    pm = (Param) paramtab->getnode(paramtab, cp->name);
 	DPUTS(!pm, "param not set in addcompparams");
@@ -2261,7 +2262,8 @@ makecompparams(void)
 
     addcompparams(comprparams, comprpms);
 
-    if (!(cpm = createparam(COMPSTATENAME, PM_SPECIAL|PM_REMOVABLE|PM_HASHED)))
+    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");
 
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index 63bc4c6e3..aac8b410d 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -198,7 +198,7 @@ getcoldef(Listcols c, char *s)
 
 /* This initializes the given terminal color structure. */
 
-static int
+static void
 getcols(Listcols c)
 {
     char *s;
@@ -206,16 +206,17 @@ getcols(Listcols c)
 
     if (!(s = getsparam("ZLS_COLORS")) &&
 	!(s = getsparam("ZLS_COLOURS"))) {
-	if (!c)
-	    return 1;
 	for (i = 0; i < NUM_COLS; i++)
 	    c->cols[i] = "";
-	
 	c->exts = NULL;
-	return 1;
+	
+	if (!(c->cols[COL_MA] = tcstr[TCSTANDOUTBEG]) ||
+	    !c->cols[COL_MA][0])
+	    c->cols[COL_MA] = "";
+	else
+	    c->cols[COL_EC] = tcstr[TCSTANDOUTEND];
+	return;
     }
-    if (!c)
-	return 0;
     /* We have one of the parameters, use it. */
     memset(c, 0, sizeof(*c));
     s = dupstring(s);
@@ -230,48 +231,65 @@ getcols(Listcols c)
     if (!c->cols[COL_MI])
 	c->cols[COL_MI] = c->cols[COL_FI];
 
-    if (!c->cols[COL_EC]) {
-	char *e = (char *) zhalloc(strlen(c->cols[COL_LC]) +
-				   strlen(c->cols[COL_NO]) +
-				   strlen(c->cols[COL_RC]) + 1);
+    return;
+}
 
-	/* If no `ec' was given, we is `<lc><no><rc>' as the default. */
-	strcpy(e, c->cols[COL_LC]);
-	strcat(e, c->cols[COL_NO]);
-	strcat(e, c->cols[COL_RC]);
-	c->cols[COL_EC] = e;
+static int last_col = COL_NO;
+
+static void
+zcputs(Listcols c, int colour)
+{
+    if (colour != last_col
+	&& (last_col < COL_NO
+	    || strcmp(c->cols[last_col], c->cols[colour]))) {
+	fputs(c->cols[COL_LC], shout);
+	fputs(c->cols[colour], shout);
+	fputs(c->cols[COL_RC], shout);
+	last_col = colour;
     }
-    return 0;
+    return;
 }
 
 /* Get the terminal color string for the file with the given name and
  * file modes. */
 
-static char *
-getcolstr(Listcols c, char *n, mode_t m)
+static void
+putcolstr(Listcols c, char *n, mode_t m)
 {
+    int colour;
     Extcol e;
 
     for (e = c->exts; e; e = e->next)
-	if (strsfx(e->ext, n))
-	    return e->col;
+	if (strsfx(e->ext, n)) {	/* XXX: unoptimised if used */
+	    if (last_col < COL_NO
+		|| strcmp(c->cols[last_col], e->col)) {
+		fputs(c->cols[COL_LC], shout);
+		fputs(e->col, shout);
+		fputs(c->cols[COL_RC], shout);
+	    }
+	    last_col = COL_NO - 1;
+	    return;
+	}
 
     if (S_ISDIR(m))
-	return c->cols[COL_DI];
+	colour = COL_DI;
     else if (S_ISLNK(m))
-	return c->cols[COL_LN];
+	colour = COL_LN;
     else if (S_ISFIFO(m))
-	return c->cols[COL_PI];
+	colour = COL_PI;
     else if (S_ISSOCK(m))
-	return c->cols[COL_SO];
+	colour = COL_SO;
     else if (S_ISBLK(m))
-	return c->cols[COL_BD];
+	colour = COL_BD;
     else if (S_ISCHR(m))
-	return c->cols[COL_CD];
+	colour = COL_CD;
     else if (S_ISREG(m) && (m & S_IXUGO))
-	return c->cols[COL_EX];
+	colour = COL_EX;
+    else
+	colour = COL_FI;
 
-    return c->cols[COL_FI];
+    zcputs(c, colour);
+    return;
 }
 
 /* Information about the list shown. */
@@ -505,14 +523,15 @@ complistmatches(Hookdef dummy, Chdata dat)
 		mc = 0;
 		q = p;
 		while (n && i--) {
-		    fputs(col.cols[COL_LC], shout);
 		    if (!(m = *q)) {
-			fputs(col.cols[COL_MI], shout);
-			fputs(col.cols[COL_RC], shout);
+			zcputs(&col, COL_MI);
 			a = longest - 2;
 			while (a--)
 			    putc(' ', shout);
-			fputs(col.cols[COL_EC], shout);
+			if (col.cols[COL_EC])
+			    fputs(col.cols[COL_EC], shout);
+			else
+			    zcputs(&col, COL_NO);
 			break;
 		    }
 		    hasm = 1;
@@ -539,20 +558,18 @@ complistmatches(Hookdef dummy, Chdata dat)
 
 			zt = ztat(pb, &buf, 1);
 			if (cc >= 0)
-			    fputs(col.cols[cc], shout);
+			    zcputs(&col, cc);
 			else if (zt)
-			    fputs(col.cols[COL_NO], shout);
+			    zcputs(&col, COL_NO);
 			else
-			    fputs(getcolstr(&col, pb, buf.st_mode), shout);
-			fputs(col.cols[COL_RC], shout);
+			    putcolstr(&col, pb, buf.st_mode);
 			nicezputs(m->str, shout);
 			if (zt)
 			    putc(' ', shout);
 			else
 			    putc(file_type(buf.st_mode), shout);
 		    } else {
-			fputs(col.cols[cc >= 0 ? cc : COL_NO], shout);
-			fputs(col.cols[COL_RC], shout);
+			zcputs(&col, cc >= 0 ? cc : COL_NO);
 			nicezputs(m->str, shout);
 			if (of)
 			    putc(' ', shout);
@@ -560,13 +577,17 @@ complistmatches(Hookdef dummy, Chdata dat)
 		    a = longest - niceztrlen(m->str) - 2 - of;
 		    while (a--)
 			putc(' ', shout);
-		    fputs(col.cols[COL_EC], shout);
+		    if (col.cols[COL_EC])
+			fputs(col.cols[COL_EC], shout);
+		    else
+			zcputs(&col, COL_NO);
 		    if (i) {
-			fputs(col.cols[COL_LC], shout);
-			fputs(col.cols[COL_NO], shout);
-			fputs(col.cols[COL_RC], shout);
+			zcputs(&col, COL_NO);
 			fputs("  ", shout);
-			fputs(col.cols[COL_EC], shout);
+			if (col.cols[COL_EC])
+			    fputs(col.cols[COL_EC], shout);
+			else
+			    zcputs(&col, COL_NO);
 		    }
 		    if (--n)
 			for (j = nc; j && *q; j--)
@@ -574,13 +595,14 @@ complistmatches(Hookdef dummy, Chdata dat)
 		    mc++;
 		}
 		if (i > 0) {
-		    fputs(col.cols[COL_LC], shout);
-		    fputs(col.cols[COL_MI], shout);
-		    fputs(col.cols[COL_RC], shout);
+		    zcputs(&col, COL_MI);
 		    a = longest - 2;
 		    while (a--)
 			putc(' ', shout);
-		    fputs(col.cols[COL_EC], shout);
+		    if (col.cols[COL_EC])
+			fputs(col.cols[COL_EC], shout);
+		    else
+			zcputs(&col, COL_NO);
 		}
 		if (n) {
 		    putc('\n', shout);
@@ -627,12 +649,12 @@ domenuselect(Hookdef dummy, Chdata dat)
     Cmgroup *pg;
     Thingy cmd;
     Menustack u = NULL;
-    int i = 0;
+    int i = 0, acc = 0;
     char *s;
 
-    if (getcols(NULL) || (dummy && (!(s = getsparam("SELECTMIN")) ||
-				    (dat && dat->num < atoi(s)))))
-	return 1;
+    if (dummy && (!(s = getsparam("SELECTMIN")) ||
+		  (dat && dat->num < atoi(s))))
+	return 0;
 
     selectlocalmap(mskeymap);
     noselect = 0;
@@ -659,10 +681,12 @@ domenuselect(Hookdef dummy, Chdata dat)
 
     getk:
 
-	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak) ||
-	    cmd == Th(z_acceptline))
+	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak))
+	    break;
+	else if (cmd == Th(z_acceptline)) {
+	    acc = 1;
 	    break;
-	else if (cmd == Th(z_acceptandhold) ||
+	} else if (cmd == Th(z_acceptandhold) ||
 		 cmd == Th(z_acceptandmenucomplete)) {
 	    Menustack s = (Menustack) zhalloc(sizeof(*s));
 
@@ -825,11 +849,16 @@ domenuselect(Hookdef dummy, Chdata dat)
     selectlocalmap(NULL);
     mselect = -1;
     inselect = 0;
+    if (acc) {
+	menucmp = 0;
+	lastambig = 0;
+	do_single(*(minfo.cur));
+    }
     if (!noselect) {
 	showinglist = -2;
 	zrefresh();
     }
-    return noselect;
+    return (!noselect ^ acc);
 }
 
 /* The widget function. */
diff --git a/Src/Zle/deltochar.c b/Src/Zle/deltochar.c
index b9fb34f10..f0df33f95 100644
--- a/Src/Zle/deltochar.c
+++ b/Src/Zle/deltochar.c
@@ -67,8 +67,6 @@ deltochar(char **args)
 	    }
 	}
     }
-    if (!ok)
-	feep();
     return !ok;
 }
 
diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list
index e0c93bd03..c28acb438 100644
--- a/Src/Zle/iwidgets.list
+++ b/Src/Zle/iwidgets.list
@@ -19,6 +19,7 @@
 "backward-kill-line", backwardkillline, ZLE_KILL | ZLE_KEEPSUFFIX
 "backward-kill-word", backwardkillword, ZLE_KILL | ZLE_KEEPSUFFIX
 "backward-word", backwardword, 0
+"beep", handlefeep, 0
 "beginning-of-buffer-or-history", beginningofbufferorhistory, 0
 "beginning-of-history", beginningofhistory, 0
 "beginning-of-line", beginningofline, 0
diff --git a/Src/Zle/zle.export b/Src/Zle/zle.export
index ccd5df98a..e6f469ad0 100644
--- a/Src/Zle/zle.export
+++ b/Src/Zle/zle.export
@@ -1,10 +1,46 @@
 #!
+acceptlast
 addzlefunction
 backdel
 backkill
+bindkey
+clearflag
+clearscreen
 deletezlefunction
+do_menucmp
+do_single
 feep
 foredel
 forekill
 getkey
+getkeycmd
+getzlequery
+lastambig
+linkkeymap
+listshown
+menucmp
+menucomplete
+menucur
+menugrp
+minfo
+newkeymap
+nlnct
+printfmt
+redisplay
+refthingy
+reversemenucomplete
+selectlocalmap
+showinglist
+skipnolist
+spaceinline
+tcmultout
+tcout
+thingies
+trashzle
+ungetkeycmd
+unlinkkeymap
+zlenoargs
 zmod
+zrefresh
+zsetterm
+ztat
diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h
index 8ab86f042..ff515e7c4 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -143,3 +143,9 @@ typedef struct cutbuffer *Cutbuffer;
 #define CUTBUFFER_LINE 1   /* for vi: buffer contains whole lines of data */
 
 #define KRINGCT 8   /* number of buffers in the kill ring */
+
+/* Convenience macros for the hooks */
+
+#define LISTMATCHESHOOK (zlehooks + 0)
+#define INSERTMATCHHOOK (zlehooks + 1)
+#define MENUSTARTHOOK   (zlehooks + 2)
diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c
index 66abbe438..f85fa00a6 100644
--- a/Src/Zle/zle_hist.c
+++ b/Src/Zle/zle_hist.c
@@ -77,10 +77,8 @@ int
 uphistory(char **args)
 {
     int nodups = isset(HISTIGNOREDUPS);
-    if (!zle_goto_hist(histline, -zmult, nodups) && isset(HISTBEEP)) {
-	feep();
+    if (!zle_goto_hist(histline, -zmult, nodups) && isset(HISTBEEP))
 	return 1;
-    }
     return 0;
 }
 
@@ -128,10 +126,8 @@ uplineorhistory(char **args)
 	int m = zmult, ret;
 
 	cs = ocs;
-	if (virangeflag || !histallowed) {
-	    feep();
+	if (virangeflag || !histallowed)
 	    return 1;
-	}
 	zmult = n;
 	ret = uphistory(args);
 	zmult = m;
@@ -160,10 +156,8 @@ uplineorsearch(char **args)
 	int m = zmult, ret;
 
 	cs = ocs;
-	if (virangeflag || !histallowed) {
-	    feep();
+	if (virangeflag || !histallowed)
 	    return 1;
-	}
 	zmult = n;
 	ret = historysearchbackward(args);
 	zmult = m;
@@ -216,10 +210,8 @@ downlineorhistory(char **args)
 	int m = zmult, ret;
 
 	cs = ocs;
-	if (virangeflag || !histallowed) {
-	    feep();
+	if (virangeflag || !histallowed)
 	    return 1;
-	}
 	zmult = n;
 	ret = downhistory(args);
 	zmult = m;
@@ -248,10 +240,8 @@ downlineorsearch(char **args)
 	int m = zmult, ret;
 
 	cs = ocs;
-	if (virangeflag || !histallowed) {
-	    feep();
+	if (virangeflag || !histallowed)
 	    return 1;
-	}
 	zmult = n;
 	ret = historysearchforward(args);
 	zmult = m;
@@ -266,10 +256,8 @@ acceptlineanddownhistory(char **args)
 {
     Histent he;
 
-    if (!(he = movehistent(quietgethist(histline), 1, HIST_FOREIGN))) {
-	feep();
+    if (!(he = movehistent(quietgethist(histline), 1, HIST_FOREIGN)))
 	return 1;
-    }
     pushnode(bufstack, ztrdup(ZLETEXT(he)));
     done = 1;
     stackhist = he->histnum;
@@ -281,10 +269,8 @@ int
 downhistory(char **args)
 {
     int nodups = isset(HISTIGNOREDUPS);
-    if (!zle_goto_hist(histline, zmult, nodups) && isset(HISTBEEP)) {
-	feep();
+    if (!zle_goto_hist(histline, zmult, nodups) && isset(HISTBEEP))
 	return 1;
-    }
     return 0;
 }
 
@@ -336,7 +322,6 @@ historysearchbackward(char **args)
 	    }
 	}
     }
-    feep();
     return 1;
 }
 
@@ -385,7 +370,6 @@ historysearchforward(char **args)
 	    }
 	}
     }
-    feep();
     return 1;
 }
 
@@ -404,10 +388,8 @@ beginningofbufferorhistory(char **args)
 int
 beginningofhistory(char **args)
 {
-    if (!zle_goto_hist(firsthist(), 0, 0) && isset(HISTBEEP)) {
-	feep();
+    if (!zle_goto_hist(firsthist(), 0, 0) && isset(HISTBEEP))
 	return 1;
-    }
     return 0;
 }
 
@@ -457,19 +439,15 @@ insertlastword(char **args)
 	zsfree(lastinsert);
 	lastinsert = NULL;
     }
-    if (!(he = quietgethist(evhist)) || !he->nwords) {
-	feep();
+    if (!(he = quietgethist(evhist)) || !he->nwords)
 	return 1;
-    }
     if (zmult > 0) {
 	n = he->nwords - (zmult - 1);
     } else {
 	n = 1 - zmult;
     }
-    if (n < 1 || n > he->nwords) {
-	feep();
+    if (n < 1 || n > he->nwords)
 	return 1;
-    }
     s = he->text + he->words[2*n-2];
     t = he->text + he->words[2*n-1];
     save = *t;
@@ -591,7 +569,6 @@ getline(char **args)
     char *s = (char *)getlinknode(bufstack);
 
     if (!s) {
-	feep();
 	return 1;
     } else {
 	int cc;
@@ -693,7 +670,7 @@ doisearch(char **args, int dir)
     int sbptr = 0, top_spot = 0, pos, sibuf = 80;
     int nomatch = 0, skip_line = 0, skip_pos = 0;
     int odir = dir, sens = zmult == 1 ? 3 : 1;
-    int hl = histline, savekeys = -1;
+    int hl = histline, savekeys = -1, feep = 0;
     Thingy cmd;
     char *okeymap = curkeymapname;
     static char *previous_search = NULL;
@@ -762,7 +739,7 @@ doisearch(char **args, int dir)
 		    get_isrch_spot(top_spot, &hl, &pos, &cs, &sbptr,
 				   &dir, &nomatch);
 		    if (!nomatch) {
-			feep();
+			feep = 1;
 			nomatch = 1;
 		    }
 		    he = quietgethist(hl);
@@ -803,7 +780,7 @@ doisearch(char **args, int dir)
 	    goto ref;
 	} else if(cmd == Th(z_vicmdmode)) {
 	    if(selectkeymap(invicmdmode() ? "main" : "vicmd", 0))
-		feep();
+		feep = 1;
 	    goto ref;
 	} else if(cmd == Th(z_vibackwarddeletechar) ||
 	    	cmd == Th(z_backwarddeletechar)) {
@@ -811,7 +788,7 @@ doisearch(char **args, int dir)
 		get_isrch_spot(--top_spot, &hl, &pos, &cs, &sbptr,
 			       &dir, &nomatch);
 	    else
-		feep();
+		feep = 1;
 	    if (nomatch) {
 		statusline = ibuf;
 		skip_pos = 1;
@@ -878,7 +855,7 @@ doisearch(char **args, int dir)
 		zrefresh();
 	    }
 	    if ((c = getkey(0)) == EOF)
-		feep();
+		feep = 1;
 	    else
 		goto ins;
 	} else {
@@ -896,7 +873,7 @@ doisearch(char **args, int dir)
 	    }
 	ins:
 	    if (sbptr == PATH_MAX) {
-		feep();
+		feep = 1;
 		continue;
 	    }
 	    set_isrch_spot(top_spot++, hl, pos, cs, sbptr, dir, nomatch);
@@ -907,7 +884,9 @@ doisearch(char **args, int dir)
 	    }
 	    sbuf[sbptr++] = c;
 	}
-	handlefeep();
+	if (feep)
+	    handlefeep(zlenoargs);
+	feep = 0;
     }
     if (sbptr) {
 	zfree(previous_search, previous_search_len);
@@ -957,7 +936,6 @@ infernexthistory(char **args)
 	    return 0;
 	}
     }
-    feep();
     return 1;
 }
 
@@ -976,7 +954,6 @@ vifetchhistory(char **args)
     }
     if (!zle_goto_hist((zmod.flags & MOD_MULT) ? zmult : curhist, 0, 0) &&
 	isset(HISTBEEP)) {
-	feep();
 	return 1;
     }
     return 0;
@@ -992,7 +969,7 @@ static int
 getvisrchstr(void)
 {
     char *sbuf = zhalloc(80);
-    int sptr = 1, ret = 0, ssbuf = 80;
+    int sptr = 1, ret = 0, ssbuf = 80, feep = 0;
     Thingy cmd;
     char *okeymap = curkeymapname;
 
@@ -1045,7 +1022,7 @@ getvisrchstr(void)
 		zrefresh();
 	    }
 	    if ((c = getkey(0)) == EOF)
-		feep();
+		feep = 1;
 	    else
 		goto ins;
 	} else if(cmd == Th(z_selfinsertunmeta) || cmd == Th(z_selfinsert)) {
@@ -1062,9 +1039,11 @@ getvisrchstr(void)
 	    }
 	    sbuf[sptr++] = c;
 	} else {
-	    feep();
+	    feep = 1;
 	}
-	handlefeep();
+	if (feep)
+	    handlefeep(zlenoargs);
+	feep = 0;
     }
     statusline = NULL;
     selectkeymap(okeymap, 1);
@@ -1122,10 +1101,8 @@ virepeatsearch(char **args)
     int n = zmult;
     char *s;
 
-    if (!visrchstr) {
-	feep();
+    if (!visrchstr)
 	return 1;
-    }
     if (zmult < 0) {
 	n = -n;
 	visrchsense = -visrchsense;
@@ -1145,7 +1122,6 @@ virepeatsearch(char **args)
 	    }
 	}
     }
-    feep();
     return 1;
 }
 
@@ -1194,7 +1170,6 @@ historybeginningsearchbackward(char **args)
 	    }
 	}
     }
-    feep();
     return 1;
 }
 
@@ -1231,6 +1206,5 @@ historybeginningsearchforward(char **args)
 	    }
 	}
     }
-    feep();
     return 1;
 }
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index 7504ed809..28bc96b64 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -98,7 +98,7 @@ struct bindstate {
 /* currently selected keymap, and its name */
 
 /**/
-Keymap curkeymap;
+Keymap curkeymap, localkeymap;
 /**/
 char *curkeymapname;
 
@@ -216,7 +216,7 @@ freekeynode(HashNode hn)
 static HashTable copyto;
 
 /**/
-static Keymap
+Keymap
 newkeymap(Keymap tocopy, char *kmname)
 {
     Keymap km = zcalloc(sizeof(*km));
@@ -250,7 +250,7 @@ scancopykeys(HashNode hn, int flags)
 }
 
 /**/
-static void
+void
 deletekeymap(Keymap km)
 {
     int i;
@@ -322,21 +322,21 @@ openkeymap(char *name)
 }
 
 /**/
-static int
-unlinkkeymap(char *name)
+int
+unlinkkeymap(char *name, int ignm)
 {
     KeymapName n = (KeymapName) keymapnamtab->getnode(keymapnamtab, name);
     if(!n)
 	return 2;
-    if(n->flags & KMN_IMMORTAL)
+    if(!ignm && (n->flags & KMN_IMMORTAL))
 	return 1;
     keymapnamtab->freenode(keymapnamtab->removenode(keymapnamtab, name));
     return 0;
 }
 
 /**/
-static int
-linkkeymap(Keymap km, char *name)
+int
+linkkeymap(Keymap km, char *name, int imm)
 {
     KeymapName n = (KeymapName) keymapnamtab->getnode(keymapnamtab, name);
     if(n) {
@@ -347,9 +347,12 @@ linkkeymap(Keymap km, char *name)
 	if(!--n->keymap->rc)
 	    deletekeymap(n->keymap);
 	n->keymap = km;
-    } else
-	keymapnamtab->addnode(keymapnamtab, ztrdup(name),
-	    makekeymapnamnode(km));
+    } else {
+	n = makekeymapnamnode(km);
+	if (imm)
+	    n->flags |= KMN_IMMORTAL;
+	keymapnamtab->addnode(keymapnamtab, ztrdup(name), n);
+    }
     km->rc++;
     return 0;
 }
@@ -379,6 +382,15 @@ selectkeymap(char *name, int fb)
     return 0;
 }
 
+/* Select a local key map. */
+
+/**/
+void
+selectlocalmap(Keymap m)
+{
+    localkeymap = m;
+}
+
 /* Reopen the currently selected keymap, in case it got deleted.  This *
  * should be called after doing anything that might have run an        *
  * arbitrary user-specified command.                                   */
@@ -642,7 +654,7 @@ bin_bindkey(char *name, char **argv, char *ops, int func)
 	    return 1;
 	}
 	if(ops['e'] || ops['v'])
-	    linkkeymap(km, "main");
+	    linkkeymap(km, "main", 0);
     } else {
 	kmname = NULL;
 	km = NULL;
@@ -715,7 +727,7 @@ bin_bindkey_del(char *name, char *kmname, Keymap km, char **argv, char *ops, cha
     int ret = 0;
 
     do {
-	int r = unlinkkeymap(*argv);
+	int r = unlinkkeymap(*argv, 0);
 	if(r == 1)
 	    zwarnnam(name, "keymap name `%s' is protected", *argv, 0);
 	else if(r == 2)
@@ -735,7 +747,7 @@ bin_bindkey_link(char *name, char *kmname, Keymap km, char **argv, char *ops, ch
     if(!km) {
 	zwarnnam(name, "no such keymap `%s'", argv[0], 0);
 	return 1;
-    } else if(linkkeymap(km, argv[1])) {
+    } else if(linkkeymap(km, argv[1], 0)) {
 	zwarnnam(name, "keymap name `%s' is protected", argv[1], 0);
 	return 1;
     }
@@ -762,7 +774,7 @@ bin_bindkey_new(char *name, char *kmname, Keymap km, char **argv, char *ops, cha
 	}
     } else
 	km = NULL;
-    linkkeymap(newkeymap(km, argv[0]), argv[0]);
+    linkkeymap(newkeymap(km, argv[0]), argv[0], 0);
     return 0;
 }
 
@@ -1108,20 +1120,18 @@ default_bindings(void)
      * will be linked to the "emacs" keymap, except that if VISUAL *
      * or EDITOR contain the string "vi" then it will be linked to *
      * the "viins" keymap.                                         */
-    linkkeymap(vmap, "viins");
-    linkkeymap(emap, "emacs");
-    linkkeymap(amap, "vicmd");
-    linkkeymap(smap, ".safe");
+    linkkeymap(vmap, "viins", 0);
+    linkkeymap(emap, "emacs", 0);
+    linkkeymap(amap, "vicmd", 0);
+    linkkeymap(smap, ".safe", 1);
     if (((ed = zgetenv("VISUAL")) && strstr(ed, "vi")) ||
 	((ed = zgetenv("EDITOR")) && strstr(ed, "vi")))
-	linkkeymap(vmap, "main");
+	linkkeymap(vmap, "main", 0);
     else
-	linkkeymap(emap, "main");
+	linkkeymap(emap, "main", 0);
 
     /* the .safe map cannot be modified or deleted */
     smap->flags |= KM_IMMUTABLE;
-    ((KeymapName) keymapnamtab->getnode(keymapnamtab, ".safe"))->flags
-	|= KMN_IMMORTAL;
 }
 
 /*************************/
@@ -1142,7 +1152,12 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp)
     keybuf[0] = 0;
     while((c = getkeybuf(!!lastlen)) != EOF) {
 	char *s;
-	Thingy f = keybind(km, keybuf, &s);
+	Thingy f;
+	int loc = 1;
+
+	if (!localkeymap ||
+	    (f = keybind(localkeymap, keybuf, &s)) == t_undefinedkey)
+	    loc = 0, f = keybind(km, keybuf, &s);
 
 	if(f != t_undefinedkey) {
 	    lastlen = keybuflen;
@@ -1150,7 +1165,7 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp)
 	    str = s;
 	    lastc = c;
 	}
-	if(!keyisprefix(km, keybuf))
+	if(!keyisprefix((loc ? localkeymap : km), keybuf))
 	    break;
     }
     if(!lastlen && keybuflen)
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index e2b64cb3c..8f6dfdf75 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -106,13 +106,7 @@ struct modifier zmod;
 /**/
 int prefixflag;
 
-/* != 0 if there is a pending beep (usually indicating an error) */
-
-/**/
-int feepflag;
- 
 /* Number of characters waiting to be read by the ungetkeys mechanism */
-
 /**/
 int kungetct;
 
@@ -507,6 +501,7 @@ zleread(char *lp, char *rp, int flags)
 	viinsbegin = 0;
 	statusline = NULL;
 	selectkeymap("main", 1);
+	selectlocalmap(NULL);
 	fixsuffix();
 	if ((s = (unsigned char *)getlinknode(bufstack))) {
 	    setline((char *)s);
@@ -535,20 +530,21 @@ zleread(char *lp, char *rp, int flags)
 	lastcol = -1;
 	initmodifier(&zmod);
 	prefixflag = 0;
-	feepflag = 0;
 	zrefresh();
 	while (!done && !errflag) {
 
 	    statusline = NULL;
 	    vilinerange = 0;
 	    reselectkeymap();
+	    selectlocalmap(NULL);
 	    bindk = getkeycmd();
 	    if (!ll && isfirstln && c == eofchar) {
 		eofsent = 1;
 		break;
 	    }
 	    if (bindk) {
-		execzlefunc(bindk, zlenoargs);
+		if (execzlefunc(bindk, zlenoargs))
+		    handlefeep(zlenoargs);
 		handleprefixes();
 		/* for vi mode, make sure the cursor isn't somewhere illegal */
 		if (invicmdmode() && cs > findbol() &&
@@ -573,7 +569,6 @@ zleread(char *lp, char *rp, int flags)
 #endif
 		if (!kungetct)
 		    zrefresh();
-	    handlefeep();
 	}
 	statusline = NULL;
 	invalidatelist();
@@ -613,7 +608,7 @@ execzlefunc(Thingy func, char **args)
 	zsfree(nm);
 	showmsg(msg);
 	zsfree(msg);
-	feep();
+	ret = 1;
     } else if((w = func->widget)->flags & (WIDGET_INT|WIDGET_NCOMP)) {
 	int wflags = w->flags;
 
@@ -646,7 +641,7 @@ execzlefunc(Thingy func, char **args)
 	    zsfree(nm);
 	    showmsg(msg);
 	    zsfree(msg);
-	    feep();
+	    ret = 1;
 	} else {
 	    int osc = sfcontext, osi = movefd(0), olv = lastval;
 	    LinkList largs = NULL;
@@ -953,7 +948,17 @@ trashzle(void)
 static struct builtin bintab[] = {
     BUILTIN("bindkey", 0, bin_bindkey, 0, -1, 0, "evaMldDANmrsLR", NULL),
     BUILTIN("vared",   0, bin_vared,   1,  7, 0, NULL,             NULL),
-    BUILTIN("zle",     0, bin_zle,     0, -1, 0, "lDANCLmMgGcR",  NULL),
+    BUILTIN("zle",     0, bin_zle,     0, -1, 0, "lDANCLmMgGcRa",  NULL),
+};
+
+/* The order of the entries in this table has to match the *HOOK
+ * macros in zle.h */
+
+/**/
+struct hookdef zlehooks[] = {
+    HOOKDEF("list_matches", ilistmatches, 0),
+    HOOKDEF("insert_match", NULL, HOOKF_ALL),
+    HOOKDEF("menu_start", NULL, HOOKF_ALL),
 };
 
 /**/
@@ -999,6 +1004,7 @@ int
 boot_zle(Module m)
 {
     addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    addhookdefs(m->nam, zlehooks, sizeof(zlehooks)/sizeof(*zlehooks));
     return 0;
 }
 
@@ -1014,6 +1020,7 @@ cleanup_zle(Module m)
 	return 1;
     }
     deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    deletehookdefs(m->nam, zlehooks, sizeof(zlehooks)/sizeof(*zlehooks));
     return 0;
 }
 
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index 4d731a21c..a51cdf92e 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -98,7 +98,6 @@ deletechar(char **args)
 	backdel(zmult);
 	return 0;
     }
-    feep();
     return 1;
 }
 
@@ -182,7 +181,6 @@ gosmacstransposechars(char **args)
 	if (cs == ll || line[cs] == '\n' ||
 	    ((cs + 1 == ll || line[cs + 1] == '\n') &&
 	     (!cs || line[cs - 1] == '\n'))) {
-	    feep();
 	    return 1;
 	}
 	cs += (cs == 0 || line[cs - 1] == '\n') ? 2 : 1;
@@ -205,10 +203,8 @@ transposechars(char **args)
 	n = -n;
     while (n--) {
 	if (!(ct = cs) || line[cs - 1] == '\n') {
-	    if (ll == cs || line[cs] == '\n') {
-		feep();
+	    if (ll == cs || line[cs] == '\n')
 		return 1;
-	    }
 	    if (!neg)
 		cs++;
 	    ct++;
@@ -225,10 +221,8 @@ transposechars(char **args)
 	}
 	if (ct == ll || line[ct] == '\n')
 	    ct--;
-	if (ct < 1 || line[ct - 1] == '\n') {
-	    feep();
+	if (ct < 1 || line[ct - 1] == '\n')
 	    return 1;
-	}
 	cc = line[ct - 1];
 	line[ct - 1] = line[ct];
 	line[ct] = cc;
@@ -350,10 +344,8 @@ yank(char **args)
 	return 1;
     if (zmod.flags & MOD_VIBUF)
 	buf = &vibuf[zmod.vibuf];
-    if (!buf->buf) {
-	feep();
+    if (!buf->buf)
 	return 1;
-    }
     mark = cs;
     yankb = cs;
     while (n--) {
@@ -372,10 +364,8 @@ yankpop(char **args)
 {
     int cc;
 
-    if (!(lastcmd & ZLE_YANK) || !kring[kct].buf) {
-	feep();
+    if (!(lastcmd & ZLE_YANK) || !kring[kct].buf)
 	return 1;
-    }
     cs = yankb;
     foredel(yanke - yankb);
     cc = kring[kct].len;
@@ -438,7 +428,6 @@ whatcursorposition(char **args)
 int
 undefinedkey(char **args)
 {
-    feep();
     return 1;
 }
 
@@ -457,10 +446,9 @@ quotedinsert(char **args)
 #ifndef HAS_TIO
     zsetterm();
 #endif
-    if (c < 0) {
-	feep();
+    if (c < 0)
 	return 1;
-    } else
+    else
 	return selfinsert(args);
 }
 
@@ -470,10 +458,8 @@ digitargument(char **args)
 {
     int sign = (zmult < 0) ? -1 : 1;
 
-    if (c < '0' || c > '9') {
-	feep();
+    if (c < '0' || c > '9')
 	return 1;
-    }
 
     if (!(zmod.flags & MOD_TMULT))
 	zmod.tmult = 0;
@@ -493,10 +479,8 @@ digitargument(char **args)
 int
 negargument(char **args)
 {
-    if (zmod.flags & MOD_TMULT) {
-	feep();
+    if (zmod.flags & MOD_TMULT)
 	return 1;
-    }
     zmod.tmult = -1;
     zmod.flags |= MOD_TMULT|MOD_NEG;
     prefixflag = 1;
@@ -655,7 +639,7 @@ Thingy
 executenamedcommand(char *prmt)
 {
     Thingy cmd;
-    int len, l = strlen(prmt), ols = listshown;
+    int len, l = strlen(prmt), ols = listshown, feep = 0;
     char *ptr;
     char *okeymap = curkeymapname;
 
@@ -688,12 +672,12 @@ executenamedcommand(char *prmt)
 	    zrefresh();
 	    c = getkey(0);
 	    if(c == EOF || !c || len == NAMLEN)
-		feep();
+		feep = 1;
 	    else
 		*ptr++ = c, len++;
 	} else if(cmd == Th(z_quotedinsert)) {
 	    if((c = getkey(0)) == EOF || !c || len == NAMLEN)
-		feep();
+		feep = 1;
 	    else
 		*ptr++ = c, len++;
 	} else if(cmd == Th(z_backwarddeletechar) ||
@@ -744,7 +728,7 @@ executenamedcommand(char *prmt)
 		    scanhashtable(thingytab, 1, 0, DISABLED, scancompcmd, 0);
 		} LASTALLOC;
 		if (empty(cmdll))
-		    feep();
+		    feep = 1;
 		else if (cmd == Th(z_listchoices) ||
 		    cmd == Th(z_deletecharorlist)) {
 		    int zmultsav = zmult;
@@ -766,7 +750,7 @@ executenamedcommand(char *prmt)
 			!(isset(LISTAMBIGUOUS) && cmdambig > len)) {
 			int zmultsav = zmult;
 			if (isset(LISTBEEP))
-			    feep();
+			    feep = 1;
 			statusll = l + cmdambig + 1;
 			zmult = 1;
 			listlist(cmdll);
@@ -776,12 +760,14 @@ executenamedcommand(char *prmt)
 		}
 	    } else {
 		if (len == NAMLEN || icntrl(c) || cmd != Th(z_selfinsert))
-		    feep();
+		    feep = 1;
 		else
 		    *ptr++ = c, len++;
 	    }
 	}
-	handlefeep();
+	if (feep)
+	    handlefeep(zlenoargs);
+	feep = 0;
     }
 }
 
diff --git a/Src/Zle/zle_move.c b/Src/Zle/zle_move.c
index e2096f6cf..9189218f1 100644
--- a/Src/Zle/zle_move.c
+++ b/Src/Zle/zle_move.c
@@ -228,7 +228,6 @@ vimatchbracket(char **args)
 
   otog:
     if (cs == ll || line[cs] == '\n') {
-	feep();
 	cs = ocs;
 	return 1;
     }
@@ -273,7 +272,6 @@ vimatchbracket(char **args)
 	    ct++;
     }
     if (cs < 0 || cs >= ll) {
-	feep();
 	cs = ocs;
 	return 1;
     } else if(dir > 0 && virangeflag)
@@ -295,10 +293,8 @@ viforwardchar(char **args)
 	zmult = n;
 	return ret;
     }
-    if (cs >= lim) {
-	feep();
+    if (cs >= lim)
 	return 1;
-    }
     while (n-- && cs < lim)
 	cs++;
     return 0;
@@ -317,10 +313,8 @@ vibackwardchar(char **args)
 	zmult = n;
 	return ret;
     }
-    if (cs == findbol()) {
-	feep();
+    if (cs == findbol())
 	return 1;
-    }
     while (n--) {
 	cs--;
 	if (cs < 0 || line[cs] == '\n') {
@@ -337,14 +331,11 @@ viendofline(char **args)
 {
     int oldcs = cs, n = zmult;
 
-    if (n < 1) {
-	feep();
+    if (n < 1)
 	return 1;
-    }
     while(n--) {
 	if (cs > ll) {
 	    cs = oldcs;
-	    feep();
 	    return 1;
 	}
 	cs = findeol() + 1;
@@ -385,7 +376,7 @@ vifindprevchar(char **args)
 	tailadd = 0;
 	return virepeatfind(args);
     }
-    return 0;
+    return 1;
 }
 
 /**/
@@ -418,10 +409,8 @@ virepeatfind(char **args)
 {
     int ocs = cs, n = zmult;
 
-    if (!vfinddir) {
-	feep();
+    if (!vfinddir)
 	return 1;
-    }
     if (n < 0) {
 	int ret;
 	zmult = -n;
@@ -434,7 +423,6 @@ virepeatfind(char **args)
 	    cs += vfinddir;
 	while (cs >= 0 && cs < ll && line[cs] != vfindchar && line[cs] != '\n');
 	if (cs < 0 || cs >= ll || line[cs] == '\n') {
-	    feep();
 	    cs = ocs;
 	    return 1;
 	}
@@ -480,10 +468,8 @@ visetmark(char **args)
     int ch;
 
     ch = getkey(0);
-    if (ch < 'a' || ch > 'z') {
-	feep();
+    if (ch < 'a' || ch > 'z')
 	return 1;
-    }
     ch -= 'a';
     vimarkcs[ch] = cs;
     vimarkline[ch] = histline;
@@ -500,19 +486,14 @@ vigotomark(char **args)
     if (ch == c)
 	ch = 26;
     else {
-	if (ch < 'a' || ch > 'z') {
-	    feep();
+	if (ch < 'a' || ch > 'z')
 	    return 1;
-	}
 	ch -= 'a';
     }
-    if (!vimarkline[ch]) {
-	feep();
+    if (!vimarkline[ch])
 	return 1;
-    }
     if (curhist != vimarkline[ch] && !zle_goto_hist(vimarkline[ch], 0, 0)) {
 	vimarkline[ch] = 0;
-	feep();
 	return 1;
     }
     cs = vimarkcs[ch];
diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c
index 221440a46..bac399e7d 100644
--- a/Src/Zle/zle_params.c
+++ b/Src/Zle/zle_params.c
@@ -84,7 +84,7 @@ makezleparams(int ro)
 
     for(zp = zleparams; zp->name; zp++) {
 	Param pm = createparam(zp->name, (zp->type |PM_SPECIAL|PM_REMOVABLE|
-					  (ro ? PM_READONLY : 0)));
+					  PM_LOCAL|(ro ? PM_READONLY : 0)));
 	if (!pm)
 	    pm = (Param) paramtab->getnode(paramtab, zp->name);
 	DPUTS(!pm, "param not set in makezleparams");
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index da0b38bfe..dd4310078 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -495,7 +495,7 @@ zrefresh(void)
     /* if old line and new line are different,
        see if we can insert/delete a line to speed up update */
 
-	if (ln < olnct - 1 && !(hasam && vcs == winw) &&
+	if (ln > 0 && ln < olnct - 1 && !(hasam && vcs == winw) &&
 	    nbuf[ln] && obuf[ln] &&
 	    strncmp(nbuf[ln], obuf[ln], 16)) {
 	    if (tccan(TCDELLINE) && obuf[ln + 1] && obuf[ln + 1][0] &&
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index 2950d55d9..cf01f2fc1 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -318,9 +318,10 @@ deletezlefunction(Widget w)
 /*
  * The available operations are:
  *
- *   -l   list user-defined widgets (no arguments)
+ *   -l   list widgets/test for existence
  *   -D   delete widget names
  *   -A   link the two named widgets (2 arguments)
+ *   -C   create completion widget (3 arguments)
  *   -N   create new user-defined widget (1 or 2 arguments)
  *        invoke a widget (1 argument)
  */
@@ -334,7 +335,7 @@ bin_zle(char *name, char **args, char *ops, int func)
 	int (*func) _((char *, char **, char *, char));
 	int min, max;
     } const opns[] = {
-	{ 'l', bin_zle_list, 0,  0 },
+	{ 'l', bin_zle_list, 0, -1 },
 	{ 'D', bin_zle_del,  1, -1 },
 	{ 'A', bin_zle_link, 2,  2 },
 	{ 'N', bin_zle_new,  1,  2 },
@@ -373,8 +374,21 @@ bin_zle(char *name, char **args, char *ops, int func)
 static int
 bin_zle_list(char *name, char **args, char *ops, char func)
 {
-    scanhashtable(thingytab, 1, 0, DISABLED, scanlistwidgets, ops['L']);
-    return 0;
+    if (!*args) {
+	scanhashtable(thingytab, 1, 0, DISABLED, scanlistwidgets,
+		      (ops['a'] ? -1 : ops['L']));
+	return 0;
+    } else {
+	int ret = 0;
+	Thingy t;
+
+	for (; *args && !ret; args++) {
+	    if (!(t = (Thingy) thingytab->getnode2(thingytab, *args)) ||
+		(!ops['a'] && (t->widget->flags & WIDGET_INT)))
+		ret = 1;
+	}
+	return ret;
+    }
 }
 
 /**/
@@ -405,6 +419,10 @@ scanlistwidgets(HashNode hn, int list)
     Thingy t = (Thingy) hn;
     Widget w = t->widget;
 
+    if(list < 0) {
+	printf("%s\n", hn->nam);
+	return;
+    }
     if(w->flags & WIDGET_INT)
 	return;
     if(list) {
@@ -536,6 +554,7 @@ bin_zle_call(char *name, char **args, char *ops, char func)
     Thingy t;
     struct modifier modsave;
     int ret, saveflag = 0;
+    char *wname = *args++;
 
     if(!zleactive || incompctlfunc || incompfunc) {
 	zerrnam(name, "widgets can only be called when ZLE is active",
@@ -543,6 +562,12 @@ bin_zle_call(char *name, char **args, char *ops, char func)
 	return 1;
     }
 
+    if (!wname) {
+	zwarnnam(name, "wrong number of arguments", NULL, 0);
+	if (saveflag)
+	    zmod = modsave;
+	return 1;
+    }
     while (*args && **args == '-') {
 	char *num;
 	if (!args[0][1] || args[0][1] == '-') {
@@ -577,14 +602,8 @@ bin_zle_call(char *name, char **args, char *ops, char func)
 	}
 	args++;
     }
-    if (!args[0]) {
-	zwarnnam(name, "wrong number of arguments", NULL, 0);
-	if (saveflag)
-	    zmod = modsave;
-	return 1;
-    }
 
-    t = rthingy(*args++);
+    t = rthingy(wname);
     PERMALLOC {
         ret = execzlefunc(t, args);
     } LASTALLOC;
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index bb0a3cac0..f7b243d31 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -98,7 +98,7 @@ static int showagain = 0;
 
 /* The match and group number to insert when starting menucompletion.   */
 
-static int insmnum, insgnum, insgroup;
+static int insmnum, insgnum, insgroup, insspace;
 
 /* This is used to decide when the cursor should be moved to the end of    *
  * the inserted word: 0 - never, 1 - only when a single match is inserted, *
@@ -108,22 +108,13 @@ static int movetoend;
 
 /* != 0 if we are in the middle of a menu completion */
 
-static int menucmp;
-
-/* Pointers to the current position in the groups list and in the menu-    *
- * completion array (the one that was put in the command line last).       */
-
-static Cmgroup menugrp;
-static Cmatch *menucur;
+/**/
+int menucmp;
 
-/* menupos is the point (in the command line) where the menu-completion   *
- * strings are inserted.  menulen is the length of the string that was    *
- * inserted last.  menuend is the end position of this string in the      *
- * command line.  menuwe is non-zero if the cursor was at the end of the  *
- * word (meaning that suffixes should go before the cursor).  menuinsc is *
- * the length of any suffix that has been temporarily added.              */
+/* Information about menucompletion. */
 
-static int menupos, menulen, menuend, menuwe, menuinsc;
+/**/
+struct menuinfo minfo;
 
 /* This is for completion inside a brace expansion. brbeg and brend hold  *
  * strings that were temporarily removed from the string to complete.     *
@@ -356,7 +347,8 @@ enum { COMP_COMPLETE,
  * on the second tab, a` la bash, and then automenu kicks in when     *
  * lastambig == 2.                                                    */
 
-static int lastambig;
+/**/
+int lastambig;
 
 /* This says what of the state the line is in when completion is started *
  * came from a previous completion. If the FC_LINE bit is set, the       *
@@ -388,7 +380,8 @@ completecall(char **args)
     cfargs = args;
     cfret = 0;
     compfunc = compwidget->u.comp.func;
-    compwidget->u.comp.fn(zlenoargs);
+    if (compwidget->u.comp.fn(zlenoargs) && !cfret)
+	cfret = 1;
     compfunc = NULL;
 
     return cfret;
@@ -446,8 +439,8 @@ spellword(char **args)
 int
 deletecharorlist(char **args)
 {
-    Cmgroup mg = menugrp;
-    Cmatch *mc = menucur;
+    Cmgroup mg = minfo.group;
+    Cmatch *mc = minfo.cur;
     int ret;
 
     usemenu = !!isset(MENUCOMPLETE);
@@ -458,8 +451,8 @@ deletecharorlist(char **args)
     } else
 	ret = docomplete(COMP_LIST_COMPLETE);
 
-    menucur = mc;
-    menugrp = mg;
+    minfo.cur = mc;
+    minfo.group = mg;
     return ret;
 }
 
@@ -522,16 +515,16 @@ reversemenucomplete(char **args)
 	return menucomplete(args);
 
     HEAPALLOC {
-	if (menucur == menugrp->matches) {
+	if (minfo.cur == (minfo.group)->matches) {
 	    do {
-		if (!(menugrp = menugrp->prev))
-		    menugrp = lmatches;
-	    } while (!menugrp->mcount);
-	    menucur = menugrp->matches + menugrp->mcount - 1;
+		if (!(minfo.group = (minfo.group)->prev))
+		    minfo.group = lmatches;
+	    } while (!(minfo.group)->mcount);
+	    minfo.cur = (minfo.group)->matches + (minfo.group)->mcount - 1;
 	} else
-	    menucur--;
+	    minfo.cur--;
 	metafy_line();
-	do_single(*menucur);
+	do_single(*(minfo.cur));
 	unmetafy_line();
     } LASTALLOC;
     return 0;
@@ -541,7 +534,8 @@ reversemenucomplete(char **args)
  * with the next completions. This gives you a way to   *
  * accept several selections from the list of matches.  */
 
-static void
+/**/
+void
 acceptlast(void)
 {
     if (brbeg && *brbeg) {
@@ -557,13 +551,13 @@ acceptlast(void)
 	brbeg[l] = ',';
 	brbeg[l + 1] = '\0';
     } else {
-	cs = menupos + menulen + menuinsc;
+	cs = minfo.pos + minfo.len + minfo.insc;
 	iremovesuffix(' ', 1);
 
 	inststrlen(" ", 1, 1);
-	menuinsc = menulen = 0;
-	menupos = cs;
-	menuwe = 1;
+	minfo.insc = minfo.len = 0;
+	minfo.pos = cs;
+	minfo.we = 1;
     }
 }
 
@@ -571,10 +565,8 @@ acceptlast(void)
 int
 acceptandmenucomplete(char **args)
 {
-    if (!menucmp) {
-	feep();
+    if (!menucmp)
 	return 1;
-    }
     acceptlast();
     return menucomplete(args);
 }
@@ -807,7 +799,7 @@ static int
 docomplete(int lst)
 {
     char *s, *ol;
-    int olst = lst, chl = 0, ne = noerrs, ocs, ret = 0;;
+    int olst = lst, chl = 0, ne = noerrs, ocs, ret = 0, omc = menucmp;
 
     if (showagain && validlist)
 	showinglist = -2;
@@ -889,7 +881,6 @@ docomplete(int lst)
 	    ll = strlen((char *) line);
 	    cs = ocs;
 	    unmetafy_line();
-	    feep();
 	    return 1;
 	}
 	ocs = cs;
@@ -1062,6 +1053,16 @@ docomplete(int lst)
     popheap();
     zsfree(qword);
     unmetafy_line();
+
+    if (menucmp && !omc) {
+	struct chdata dat;
+
+	dat.matches = amatches;
+	dat.num = nmatches;
+	dat.cur = NULL;
+	if (runhookdef(MENUSTARTHOOK, (void *) &dat))
+	    menucmp = 0;
+    }
     return ret;
 }
 
@@ -1071,7 +1072,7 @@ docomplete(int lst)
  * insert the next completion.                                              */
 
 /**/
-static void
+void
 do_menucmp(int lst)
 {
     /* Just list the matches if the list was requested. */
@@ -1081,16 +1082,16 @@ do_menucmp(int lst)
     }
     /* Otherwise go to the next match in the array... */
     HEAPALLOC {
-	if (!*++menucur) {
+	if (!*++(minfo.cur)) {
 	    do {
-		if (!(menugrp = menugrp->next))
-		    menugrp = amatches;
-	    } while (!menugrp->mcount);
-	    menucur = menugrp->matches;
+		if (!(minfo.group = (minfo.group)->next))
+		    minfo.group = amatches;
+	    } while (!(minfo.group)->mcount);
+	    minfo.cur = minfo.group->matches;
 	}
 	/* ... and insert it into the command line. */
 	metafy_line();
-	do_single(*menucur);
+	do_single(*(minfo.cur));
 	unmetafy_line();
     } LASTALLOC;
 }
@@ -1444,7 +1445,6 @@ get_comp_string(void)
 		lexrestore();
 		goto start;
 	    }
-	    feep();
 	    noaliases = 0;
 	    lexrestore();
 	    LASTALLOC_RETURN NULL;
@@ -1757,11 +1757,8 @@ doexpansion(char *s, int lst, int olst, int explincmd)
 	}
 	if (errflag)
 	    goto end;
-	if (empty(vl) || !*(char *)peekfirst(vl)) {
-	    if (!noerrs)
-		feep();
+	if (empty(vl) || !*(char *)peekfirst(vl))
 	    goto end;
-	}
 	if (peekfirst(vl) == (void *) ss ||
 		(olst == COMP_EXPAND_COMPLETE &&
 		 !nextnode(firstnode(vl)) && *s == Tilde &&
@@ -1771,8 +1768,6 @@ doexpansion(char *s, int lst, int olst, int explincmd)
 	     * expandorcomplete was called, otherwise, just beep.     */
 	    if (lst == COMP_EXPAND_COMPLETE)
 		docompletion(s, COMP_COMPLETE, explincmd);
-	    else
-		feep();
 	    goto end;
 	}
 	if (lst == COMP_LIST_EXPAND) {
@@ -3824,6 +3819,10 @@ addmatches(Cadata dat, char **argv)
 		    lpre = quotename(lpre, NULL);
 		    lsuf = quotename(lsuf, NULL);
 		}
+		if (dat->ppre)
+		    dat->ppre = quotename(dat->ppre, NULL);
+		if (dat->psuf)
+		    dat->psuf = quotename(dat->psuf, NULL);
 	    }
 	    /* Walk through the matches given. */
 	    for (; (s = *argv); argv++) {
@@ -3849,8 +3848,10 @@ addmatches(Cadata dat, char **argv)
 		    }
 		}
 		if (!(dat->aflags & CAF_MATCH)) {
-		    ms = ((dat->aflags & CAF_QUOTE) ? dupstring(s) :
-			  quotename(s, NULL));
+		    if (dat->aflags & CAF_QUOTE)
+			ms = dupstring(s);
+		    else
+			sl = strlen(ms = quotename(s, NULL));
 		    lc = bld_parts(ms, sl, -1, NULL);
 		    isexact = 0;
 		} else if (!(ms = comp_match(lpre, lsuf, s, cp, &lc,
@@ -4358,7 +4359,6 @@ docompletion(char *s, int lst, int incmd)
 	    foredel(ll);
 	    inststr(origline);
 	    cs = origcs;
-	    feep();
 	    clearlist = 1;
 	    ret = 1;
 	    goto compend;
@@ -4370,17 +4370,17 @@ docompletion(char *s, int lst, int incmd)
 	    showinglist = -2;
 	else if (useline) {
 	    /* We have matches. */
-	    if (nmatches > 1)
+	    if (nmatches > 1) {
 		/* There is more than one match. */
-		    do_ambiguous();
-
-	    else if (nmatches == 1) {
+		ret = do_ambiguous();
+	    } else if (nmatches == 1) {
 		/* Only one match. */
 		Cmgroup m = amatches;
 
 		while (!m->mcount)
 		    m = m->next;
-		menucur = NULL;
+		minfo.cur = NULL;
+		minfo.asked = 0;
 		do_single(m->matches[0]);
 		invalidatelist();
 	    }
@@ -4394,7 +4394,7 @@ docompletion(char *s, int lst, int incmd)
 	    int up = 0, tr = 1, nn = 0;
 
 	    if (!nmatches)
-		feep();
+		ret = 1;
 
 	    while (g) {
 		if ((e = g->expls))
@@ -4636,8 +4636,8 @@ callcompfunc(char *s, char *fn)
 	    else
 		compoldlist = "yes";
 	    kset |= CP_OLDLIST;
-	    if (menucur) {
-		sprintf(buf, "%d", (*menucur)->gnum);
+	    if (minfo.cur) {
+		sprintf(buf, "%d", (*(minfo.cur))->gnum);
 		compoldins = buf;
 		kset |= CP_OLDINS;
 	    } else
@@ -4704,6 +4704,7 @@ callcompfunc(char *s, char *fn)
 		insgroup = 1;
 		insgnum = atoi(m + 1);
 	    }
+	    insspace = (compinsert[strlen(compinsert) - 1] == ' ');
 	} else
 	    useline = usemenu = 0;
 	useexact = (compexact && !strcmp(compexact, "accept"));
@@ -4718,7 +4719,7 @@ callcompfunc(char *s, char *fn)
 	    movetoend = 2;
 
 	oldlist = (hasperm && compoldlist && !strcmp(compoldlist, "keep"));
-	oldins = (hasperm && menucur &&
+	oldins = (hasperm && minfo.cur &&
 		  compoldins && !strcmp(compoldins, "keep"));
 
 	zfree(comprpms, CP_REALPARAMS * sizeof(Param));
@@ -5635,7 +5636,7 @@ makecomplistext(Compctl occ, char *os, int incmd)
 		    case CCT_RANGEPAT:
 			if (cc->type == CCT_RANGEPAT)
 			    tokenize(sc = dupstring(cc->u.l.a[i]));
-			for (j = clwpos; j; j--) {
+			for (j = clwpos - 1; j > 0; j--) {
 			    untokenize(s = ztrdup(clwords[j]));
 			    if (cc->type == CCT_RANGESTR)
 				sc = rembslash(cc->u.l.a[i]);
@@ -6365,20 +6366,14 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
     if (cc->mask & (CC_JOBS | CC_RUNNING | CC_STOPPED)) {
 	/* Get job names. */
 	int i;
-	char *j, *jj;
+	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 = jj = dupstring(jobtab[i].procs->text);
-		/* Find the first word. */
-		for (; *jj; jj++)
-		    if (*jj == ' ') {
-			*jj = '\0';
-			break;
-		    }
+		j = dupstring(jobtab[i].procs->text);
 		if ((cc->mask & CC_JOBS) ||
 		    (stopped && (cc->mask & CC_STOPPED)) ||
 		    (!stopped && (cc->mask & CC_RUNNING)))
@@ -6625,7 +6620,8 @@ invalidatelist(void)
     if (validlist)
 	freematches();
     lastambig = menucmp = validlist = showinglist = fromcomp = 0;
-    menucur = NULL;
+    minfo.cur = NULL;
+    minfo.asked = 0;
     compwidget = NULL;
 }
 
@@ -7392,19 +7388,20 @@ instmatch(Cmatch m, int *scs)
 /* Handle the case were we found more than one match. */
 
 /**/
-static void
+static int
 do_ambiguous(void)
 {
+    int ret = 0;
     menucmp = 0;
 
     /* If we have to insert the first match, call do_single().  This is *
      * how REC_EXACT takes effect.  We effectively turn the ambiguous   *
      * completion into an unambiguous one.                              */
     if (ainfo && ainfo->exact == 1 && useexact && !(fromcomp & FC_LINE)) {
-	menucur = NULL;
+	minfo.cur = NULL;
 	do_single(ainfo->exactm);
 	invalidatelist();
-	return;
+	return ret;
     }
     /* Setting lastambig here means that the completion is ambiguous and *
      * AUTO_MENU might want to start a menu completion next time round,  *
@@ -7424,7 +7421,8 @@ do_ambiguous(void)
 	int atend = (cs == we), oll = ll, la, eq, tcs;
 	VARARR(char, oline, ll);
 
-	menucur = NULL;
+	minfo.cur = NULL;
+	minfo.asked = 0;
 
 	/* Copy the line buffer to be able to easily test if it changed. */
 	memcpy(oline, line, ll);
@@ -7473,19 +7471,21 @@ do_ambiguous(void)
 	    fromcomp = fc;
 	    lastambig = 0;
 	    clearlist = 1;
-	    return;
+	    return ret;
 	}
     } else
-	return;
+	return ret;
 
     /* At this point, we might want a completion listing.  Show the listing *
      * if it is needed.                                                     */
     if (isset(LISTBEEP))
-	feep();
-    if (uselist && usemenu != 2 &&
-	(!showinglist || (usemenu == 3 && !oldlist)) &&
+	ret = 1;
+    if (uselist && (usemenu != 2 || (!showinglist && !oldlist)) &&
+	((!showinglist && (!listshown || !oldlist)) ||
+	 (usemenu == 3 && !oldlist)) &&
 	(smatches >= 2 || (compforcelist && *compforcelist)))
 	showinglist = -2;
+    return ret;
 }
 
 /* This is a stat that ignores backslashes in the filename.  The `ls' *
@@ -7495,7 +7495,7 @@ do_ambiguous(void)
  * (l)stat().                                                         */
 
 /**/
-static int
+int
 ztat(char *nam, struct stat *buf, int ls)
 {
     char b[PATH_MAX], *p;
@@ -7513,7 +7513,7 @@ ztat(char *nam, struct stat *buf, int ls)
 /* Insert a single match in the command line. */
 
 /**/
-static void
+void
 do_single(Cmatch m)
 {
     int l, sr = 0, scs;
@@ -7526,39 +7526,39 @@ do_single(Cmatch m)
 
     fixsuffix();
 
-    if (!menucur) {
+    if (!minfo.cur) {
 	/* We are currently not in a menu-completion, *
 	 * so set the position variables.             */
-	menupos = wb;
-	menuwe = (movetoend >= 2 || (movetoend == 1 && !menucmp));
-	menuend = we;
+	minfo.pos = wb;
+	minfo.we = (movetoend >= 2 || (movetoend == 1 && !menucmp));
+	minfo.end = we;
     }
     /* If we are already in a menu-completion or if we have done a *
      * glob completion, we have to delete some of the stuff on the *
      * command line.                                               */
-    if (menucur)
-	l = menulen + menuinsc;
+    if (minfo.cur)
+	l = minfo.len + minfo.insc;
     else
 	l = we - wb;
 
-    menuinsc = 0;
-    cs = menupos;
+    minfo.insc = 0;
+    cs = minfo.pos;
     foredel(l);
 
     /* And then we insert the new string. */
-    menulen = instmatch(m, &scs);
-    menuend = cs;
-    cs = menupos + menulen;
+    minfo.len = instmatch(m, &scs);
+    minfo.end = cs;
+    cs = minfo.pos + minfo.len;
 
     if (m->suf) {
 	havesuff = 1;
-	menuinsc = ztrlen(m->suf);
-	menulen -= menuinsc;
-	if (menuwe) {
-	    menuend += menuinsc;
+	minfo.insc = ztrlen(m->suf);
+	minfo.len -= minfo.insc;
+	if (minfo.we) {
+	    minfo.end += minfo.insc;
 	    if (m->flags & CMF_REMOVE) {
-		makesuffixstr(m->remf, m->rems, menuinsc);
-		if (menuinsc == 1)
+		makesuffixstr(m->remf, m->rems, minfo.insc);
+		if (minfo.insc == 1)
 		    suffixlen[STOUC(m->suf[0])] = 1;
 	    }
 	}
@@ -7574,11 +7574,11 @@ do_single(Cmatch m)
 	    cs += eparq;
 	    for (pq = parq; pq; pq--)
 		inststrlen("\"", 1, 1);
-	    menuinsc += parq;
+	    minfo.insc += parq;
 	    inststrlen("}", 1, 1);
-	    menuinsc++;
-	    if (menuwe)
-		menuend += menuinsc;
+	    minfo.insc++;
+	    if (minfo.we)
+		minfo.end += minfo.insc;
 	}
 	if ((m->flags & CMF_FILE) || (m->ripre && isset(AUTOPARAMSLASH))) {
 	    /* If we have a filename or we completed a parameter name      *
@@ -7614,10 +7614,10 @@ do_single(Cmatch m)
 		/* It is a directory, so add the slash. */
 		havesuff = 1;
 		inststrlen("/", 1, 1);
-		menuinsc++;
-		if (menuwe)
-		    menuend++;
-		if (!menucmp || menuwe) {
+		minfo.insc++;
+		if (minfo.we)
+		    minfo.end++;
+		if (!menucmp || minfo.we) {
 		    if (m->remf || m->rems)
 			makesuffixstr(m->remf, m->rems, 1);
 		    else if (isset(AUTOREMOVESLASH)) {
@@ -7627,8 +7627,8 @@ do_single(Cmatch m)
 		}
 	    }
 	}
-	if (!menuinsc)
-	    cs = menupos + menulen;
+	if (!minfo.insc)
+	    cs = minfo.pos + minfo.len;
     }
     /* If completing in a brace expansion... */
     if (brbeg) {
@@ -7644,9 +7644,9 @@ do_single(Cmatch m)
 	    cs = scs;
 	    havesuff = 1;
 	    inststrlen(",", 1, 1);
-	    menuinsc++;
+	    minfo.insc++;
 	    makesuffix(1);
-	    if ((!menucmp || menuwe) && isset(AUTOPARAMKEYS))
+	    if ((!menucmp || minfo.we) && isset(AUTOPARAMKEYS))
 		suffixlen[','] = suffixlen['}'] = 1;
 	}
     } else if (!havesuff && (!(m->flags & CMF_FILE) || !sr)) {
@@ -7655,20 +7655,33 @@ do_single(Cmatch m)
 	 * the string doesn't name an existing file.             */
 	if (m->autoq && (!m->isuf || m->isuf[0] != m->autoq)) {
 	    inststrlen(&(m->autoq), 1, 1);
-	    menuinsc++;
+	    minfo.insc++;
 	}
-	if (!menucmp) {
+	if (!menucmp && (usemenu != 3 || insspace)) {
 	    inststrlen(" ", 1, 1);
-	    menuinsc++;
-	    if (menuwe)
+	    minfo.insc++;
+	    if (minfo.we)
 		makesuffix(1);
 	}
     }
-    if (menuwe && m->ripre && isset(AUTOPARAMKEYS))
-	makeparamsuffix(((m->flags & CMF_PARBR) ? 1 : 0), menuinsc - parq);
+    if (minfo.we && m->ripre && isset(AUTOPARAMKEYS))
+	makeparamsuffix(((m->flags & CMF_PARBR) ? 1 : 0), minfo.insc - parq);
+
+    if ((menucmp && !minfo.we) || !movetoend)
+	cs = minfo.end;
+    {
+	Cmatch *om = minfo.cur;
+	struct chdata dat;
 
-    if ((menucmp && !menuwe) || !movetoend)
-	cs = menuend;
+	dat.matches = amatches;
+	dat.num = nmatches;
+	dat.cur = m;
+
+	if (menucmp)
+	    minfo.cur = &m;
+	runhookdef(INSERTMATCHHOOK, (void *) &dat);
+	minfo.cur = om;
+    }
 }
 
 /* This maps the value in v into the range [0,m-1], decrementing v
@@ -7698,40 +7711,42 @@ do_ambig_menu(void)
 
     if (usemenu != 3) {
 	menucmp = 1;
-	menucur = NULL;
+	minfo.cur = NULL;
     } else {
 	if (oldlist) {
 	    if (oldins)
 		acceptlast();
 	} else
-	    menucur = NULL;
+	    minfo.cur = NULL;
     }
     if (insgroup) {
 	insgnum = comp_mod(insgnum, permgnum);
-	for (menugrp = amatches;
-	     menugrp && menugrp->num != insgnum + 1;
-	     menugrp = menugrp->next);
-	if (!menugrp || !menugrp->mcount) {
-	    menucur = NULL;
+	for (minfo.group = amatches;
+	     minfo.group && (minfo.group)->num != insgnum + 1;
+	     minfo.group = (minfo.group)->next);
+	if (!minfo.group || !(minfo.group)->mcount) {
+	    minfo.cur = NULL;
+	    minfo.asked = 0;
 	    return;
 	}
-	insmnum = comp_mod(insmnum, menugrp->mcount);
+	insmnum = comp_mod(insmnum, (minfo.group)->mcount);
     } else {
 	int c = 0;
 
 	insmnum = comp_mod(insmnum, permmnum);
-	for (menugrp = amatches;
-	     menugrp && (c += menugrp->mcount) <= insmnum;
-	     menugrp = menugrp->next)
-	    insmnum -= menugrp->mcount;
-	if (!menugrp) {
-	    menucur = NULL;
+	for (minfo.group = amatches;
+	     minfo.group && (c += (minfo.group)->mcount) <= insmnum;
+	     minfo.group = (minfo.group)->next)
+	    insmnum -= (minfo.group)->mcount;
+	if (!minfo.group) {
+	    minfo.cur = NULL;
+	    minfo.asked = 0;
 	    return;
 	}
     }
-    mc = menugrp->matches + insmnum;
+    mc = (minfo.group)->matches + insmnum;
     do_single(*mc);
-    menucur = mc;
+    minfo.cur = mc;
 }
 
 /* Return the length of the common prefix of s and t. */
@@ -7770,7 +7785,7 @@ sfxlen(char *s, char *t)
  * It returns the number of lines printed.       */
 
 /**/
-static int
+int
 printfmt(char *fmt, int n, int dopr)
 {
     char *p = fmt, nc[DIGBUFSIZE];
@@ -7853,7 +7868,8 @@ printfmt(char *fmt, int n, int dopr)
 
 /* This skips over matches that are not to be listed. */
 
-static Cmatch *
+/**/
+Cmatch *
 skipnolist(Cmatch *p)
 {
     while (*p && ((*p)->flags & CMF_NOLIST))
@@ -7868,11 +7884,7 @@ skipnolist(Cmatch *p)
 void
 listmatches(void)
 {
-    Cmgroup g;
-    Cmatch *p, m;
-    Cexpl *e;
-    int nlines = 0, ncols, nlist = 0, longest = 1, pnl = 0;
-    int of = isset(LISTTYPES), opl = 0;
+    struct chdata dat;
 
 #ifdef DEBUG
     /* Sanity check */
@@ -7882,6 +7894,22 @@ listmatches(void)
     }
 #endif
 
+    dat.matches = amatches;
+    dat.num = nmatches;
+    dat.cur = NULL;
+    runhookdef(LISTMATCHESHOOK, (void *) &dat);
+}
+
+/**/
+int
+ilistmatches(Hookdef dummy, Chdata dat)
+{
+    Cmgroup g;
+    Cmatch *p, m;
+    Cexpl *e;
+    int nlines = 0, ncols, nlist = 0, longest = 1, pnl = 0;
+    int of = isset(LISTTYPES), opl = 0;
+
     /* Set the cursor below the prompt. */
     trashzle();
     showinglist = listshown = 0;
@@ -7966,8 +7994,9 @@ listmatches(void)
     }
 
     /* Maybe we have to ask if the user wants to see the list. */
-    if ((complistmax && nlist > complistmax) ||
-	(!complistmax && nlines >= lines)) {
+    if ((!minfo.cur || !minfo.asked) &&
+	((complistmax && nlist > complistmax) ||
+	 (!complistmax && nlines >= lines))) {
 	int qup;
 	zsetterm();
 	qup = printfmt("zsh: do you wish to see all %n possibilities? ", nlist, 1);
@@ -7981,7 +8010,9 @@ listmatches(void)
 		tcmultout(TCUP, TCMULTUP, nlnct);
 	    } else
 		putc('\n', shout);
-	    return;
+	    if (minfo.cur)
+		minfo.asked = 2;
+	    return 0;
 	}
 	if (clearflag) {
 	    putc('\r', shout);
@@ -7991,6 +8022,8 @@ listmatches(void)
 	} else
 	    putc('\n', shout);
 	settyinfo(&shttyinfo);
+	if (minfo.cur)
+	    minfo.asked = 1;
     }
 
     /* Now print the matches. */
@@ -8110,6 +8143,7 @@ listmatches(void)
 	    clearflag = 0, putc('\n', shout);
     } else
 	putc('\n', shout);
+    return 0;
 }
 
 /* This is used to print expansions. */
@@ -8119,9 +8153,9 @@ void
 listlist(LinkList l)
 {
     struct cmgroup dg;
-    Cmgroup am = amatches;
     int vl = validlist, sm = smatches;
     char *oclp = complastprompt;
+    Cmgroup am = amatches;
 
     if (listshown)
 	showagain = 1;
@@ -8129,12 +8163,12 @@ listlist(LinkList l)
     complastprompt = ((zmult == 1) == !!isset(ALWAYSLASTPROMPT) ? "yes" : NULL);
     smatches = 1;
     validlist = 1;
-    amatches = &dg;
     memset(&dg, 0, sizeof(struct cmgroup));
     dg.ylist = (char **) makearray(l, 1, &(dg.lcount), NULL);
-    listmatches();
-
+    amatches = &dg;
+    ilistmatches(NULL, NULL);
     amatches = am;
+
     validlist = vl;
     smatches = sm;
     complastprompt = oclp;
@@ -8213,18 +8247,17 @@ magicspace(char **args)
 {
     int ret;
     c = ' ';
-    ret = selfinsert(args);
-    return !doexpandhist();
+    if (!(ret = selfinsert(args)))
+	doexpandhist();
+    return ret;
 }
 
 /**/
 int
 expandhistory(char **args)
 {
-    if (!doexpandhist()) {
-	feep();
+    if (!doexpandhist())
 	return 1;
-    }
     return 0;
 }
 
@@ -8276,10 +8309,8 @@ processcmd(char **args)
     int m = zmult;
 
     s = getcurcmd();
-    if (!s) {
-	feep();
+    if (!s)
 	return 1;
-    }
     zmult = 1;
     pushline(zlenoargs);
     zmult = m;
@@ -8304,16 +8335,12 @@ expandcmdpath(char **args)
     noaliases = 1;
     s = getcurcmd();
     noaliases = na;
-    if (!s || cmdwb < 0 || cmdwe < cmdwb) {
-	feep();
+    if (!s || cmdwb < 0 || cmdwe < cmdwb)
 	return 1;
-    }
     str = findcmd(s, 1);
     zsfree(s);
-    if (!str) {
-	feep();
+    if (!str)
 	return 1;
-    }
     cs = cmdwb;
     foredel(cmdwe - cmdwb);
     spaceinline(strlen(str));
diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c
index cc9c9bf37..bd2f39a06 100644
--- a/Src/Zle/zle_utils.c
+++ b/Src/Zle/zle_utils.c
@@ -409,19 +409,11 @@ showmsg(char const *msg)
 /* handle the error flag */
 
 /**/
-void
-feep(void)
-{
-    feepflag = 1;
-}
-
-/**/
-void
-handlefeep(void)
+int
+handlefeep(char **args)
 {
-    if(feepflag)
-	zbeep();
-    feepflag = 0;
+    zbeep();
+    return 0;
 }
 
 /***************/
@@ -559,10 +551,8 @@ undo(char **args)
 {
     handleundo();
     do {
-	if(!curchange->prev) {
-	    feep();
+	if(!curchange->prev)
 	    return 1;
-	}
 	unapplychange(curchange = curchange->prev);
     } while(curchange->flags & CH_PREV);
     setlastline();
@@ -598,10 +588,8 @@ redo(char **args)
 {
     handleundo();
     do {
-	if(!curchange->next) {
-	    feep();
+	if(!curchange->next)
 	    return 1;
-	}
 	applychange(curchange);
 	curchange = curchange->next;
     } while(curchange->prev->flags & CH_NEXT);
diff --git a/Src/Zle/zle_vi.c b/Src/Zle/zle_vi.c
index 08de69c19..71f766739 100644
--- a/Src/Zle/zle_vi.c
+++ b/Src/Zle/zle_vi.c
@@ -102,10 +102,8 @@ vigetkey(void)
     char m[3], *str;
     Thingy cmd;
 
-    if((c = getkey(0)) == EOF) {
-	feep();
+    if((c = getkey(0)) == EOF)
 	return -1;
-    }
 
     m[0] = c;
     metafy(m, 1, META_NOALLOC);
@@ -115,13 +113,10 @@ vigetkey(void)
 	cmd = t_undefinedkey;
 
     if (!cmd || cmd == Th(z_sendbreak)) {
-	feep();
 	return -1;
     } else if (cmd == Th(z_quotedinsert)) {
-	if ((c = getkey(0)) == EOF) {
-	    feep();
+	if ((c = getkey(0)) == EOF)
 	    return -1;
-	}
     } else if(cmd == Th(z_viquotedinsert)) {
 	char sav = line[cs];
 
@@ -129,10 +124,8 @@ vigetkey(void)
 	zrefresh();
 	c = getkey(0);
 	line[cs] = sav;
-	if(c == EOF) {
-	    feep();
+	if(c == EOF)
 	    return -1;
-	}
     } else if (cmd == Th(z_vicmdmode))
 	return -1;
     return c;
@@ -142,7 +135,7 @@ vigetkey(void)
 static int
 getvirange(int wf)
 {
-    int pos = cs;
+    int pos = cs, ret = 0;
     int mult1 = zmult, hist1 = histline;
     Thingy k2;
 
@@ -168,39 +161,37 @@ getvirange(int wf)
 		k2 == Th(z_sendbreak)) {
 	    wordflag = 0;
 	    virangeflag = 0;
-	    feep();
 	    return -1;
 	}
-	if(k2 == bindk)
-	    /* The command key is repeated: a number of lines is used. */
-	    dovilinerange();
-	else
-	    execzlefunc(k2, zlenoargs);
+	/*
+	 * With k2 == bindk, the command key is repeated:
+	 * a number of lines is used.  If the function used
+	 * returns 1, we fail.
+	 */
+	if ((k2 == bindk) ? dovilinerange() : execzlefunc(k2, zlenoargs))
+	    ret = -1;
 	if(vichgrepeat)
 	    zmult = mult1;
 	else
 	    zmult = mult1 * zmod.tmult;
-    } while(prefixflag);
+    } while(prefixflag && !ret);
     wordflag = 0;
     virangeflag = 0;
 
     /* It is an error to use a non-movement command to delimit the *
      * range.  We here reject the case where the command modified  *
      * the line, or selected a different history line.             */
-    if(histline != hist1 || ll != lastll || memcmp(line, lastline, ll)) {
+    if (histline != hist1 || ll != lastll || memcmp(line, lastline, ll)) {
 	histline = hist1;
 	memcpy(line, lastline, ll = lastll);
 	cs = pos;
-	feep();
 	return -1;
     }
 
     /* Can't handle an empty file.  Also, if the movement command *
      * failed, or didn't move, it is an error.                    */
-    if (!ll || (cs == pos && virangeflag != 2)) {
-	feep();
+    if (!ll || (cs == pos && virangeflag != 2) || ret == -1)
 	return -1;
-    }
 
     /* vi-match-bracket changes the value of virangeflag when *
      * moving to the opening bracket, meaning that we need to *
@@ -233,7 +224,7 @@ getvirange(int wf)
 }
 
 /**/
-static void
+static int
 dovilinerange(void)
 {
     int pos = cs, n = zmult;
@@ -243,17 +234,14 @@ dovilinerange(void)
      * downward, otherwise upward.  The repeat count gives the    *
      * number of lines.                                           */
     vilinerange = 1;
-    if (!n) {
-	feep();
-	return;
-    }
+    if (!n)
+	return 1;
     if (n > 0) {
 	while(n-- && cs <= ll)
 	    cs = findeol() + 1;
 	if (n != -1) {
 	    cs = pos;
-	    feep();
-	    return;
+	    return 1;
 	}
 	cs--;
     } else {
@@ -261,12 +249,12 @@ dovilinerange(void)
 	    cs = findbol() - 1;
 	if (n != 1) {
 	    cs = pos;
-	    feep();
-	    return;
+	    return 1;
 	}
 	cs++;
     }
     virangeflag = 2;
+    return 0;
 }
 
 /**/
@@ -309,11 +297,12 @@ viinsertbol(char **args)
 int
 videlete(char **args)
 {
-    int c2;
+    int c2, ret = 1;
 
     startvichange(1);
     if ((c2 = getvirange(0)) != -1) {
 	forekill(c2 - cs, 0);
+	ret = 0;
 	if (vilinerange && ll) {
 	    if (cs == ll)
 		cs--;
@@ -322,7 +311,7 @@ videlete(char **args)
 	}
     }
     vichgflag = 0;
-    return 0;
+    return ret;
 }
 
 /**/
@@ -341,10 +330,8 @@ videletechar(char **args)
 	return ret;
     }
     /* it is an error to be on the end of line */
-    if (cs == ll || line[cs] == '\n') {
-	feep();
+    if (cs == ll || line[cs] == '\n')
 	return 1;
-    }
     /* Put argument into the acceptable range -- it is not an error to  *
      * specify a greater count than the number of available characters. */
     if (n > findeol() - cs)
@@ -358,16 +345,17 @@ videletechar(char **args)
 int
 vichange(char **args)
 {
-    int c2;
+    int c2, ret = 1;
 
     startvichange(1);
     if ((c2 = getvirange(1)) != -1) {
+	ret = 0;
 	forekill(c2 - cs, 0);
 	selectkeymap("main", 1);
 	viinsbegin = cs;
 	undoing = 0;
     }
-    return 0;
+    return ret;
 }
 
 /**/
@@ -377,15 +365,11 @@ visubstitute(char **args)
     int n = zmult;
 
     startvichange(1);
-    if (n < 0) {
-	feep();
+    if (n < 0)
 	return 1;
-    }
     /* it is an error to be on the end of line */
-    if (cs == ll || line[cs] == '\n') {
-	feep();
+    if (cs == ll || line[cs] == '\n')
 	return 1;
-    }
     /* Put argument into the acceptable range -- it is not an error to  *
      * specify a greater count than the number of available characters. */
     if (n > findeol() - cs)
@@ -436,10 +420,8 @@ viyankeol(char **args)
     int x = findeol();
 
     startvichange(-1);
-    if (x == cs) {
-	feep();
+    if (x == cs)
 	return 1;
-    }
     cut(cs, x - cs, 0);
     return 0;
 }
@@ -456,7 +438,6 @@ viyankwholeline(char **args)
 	return 1;
     while(n--) {
      if (cs > ll) {
-	feep();
 	cs = oldcs;
 	return 1;
      }
@@ -498,23 +479,18 @@ vireplacechars(char **args)
     startvichange(1);
     /* check argument range */
     if (n < 1 || n + cs > findeol()) {
-	if(vichgrepeat) {
-	    int ofeep = feepflag;
+	if(vichgrepeat)
 	    vigetkey();
-	    feepflag = ofeep;
-	}
 	if(vichgflag) {
 	    free(vichgbuf);
 	    vichgbuf = NULL;
 	    vichgflag = 0;
 	}
-	feep();
 	return 1;
     }
     /* get key */
     if((ch = vigetkey()) == -1) {
 	vichgflag = 0;
-	feep();
 	return 1;
     }
     /* do change */
@@ -537,7 +513,7 @@ int
 vicmdmode(char **args)
 {
     if (invicmdmode() || selectkeymap("vicmd", 0))
-	feep();
+	return 1;
     undoing = 1;
     vichgflag = 0;
     if (cs != findbol())
@@ -573,7 +549,7 @@ viopenlineabove(char **args)
 int
 vioperswapcase(char **args)
 {
-    int oldcs, c2;
+    int oldcs, c2, ret = 1;
 
     /* get the range */
     startvichange(1);
@@ -589,12 +565,13 @@ vioperswapcase(char **args)
 	}
 	/* go back to the first line of the range */
 	cs = oldcs;
+	ret = 0;
 #if 0
 	vifirstnonblank();
 #endif
     }
     vichgflag = 0;
-    return 0;
+    return ret;
 }
 
 /**/
@@ -602,10 +579,8 @@ int
 virepeatchange(char **args)
 {
     /* make sure we have a change to repeat */
-    if (!vichgbuf || vichgflag) {
-	feep();
+    if (!vichgbuf || vichgflag)
 	return 1;
-    }
     /* restore or update the saved count and buffer */
     if (zmod.flags & MOD_MULT) {
 	lastmod.mult = zmod.mult;
@@ -637,7 +612,6 @@ viindent(char **args)
     vichgflag = 0;
     /* must be a line range */
     if (!vilinerange) {
-	feep();
 	cs = oldcs;
 	return 1;
     }
@@ -669,7 +643,6 @@ viunindent(char **args)
     vichgflag = 0;
     /* must be a line range */
     if (!vilinerange) {
-	feep();
 	cs = oldcs;
 	return 1;
     }
@@ -705,7 +678,6 @@ vibackwarddeletechar(char **args)
     /* It is an error to be at the beginning of the line, or (in *
      * insert mode) to delete past the beginning of insertion.   */
     if ((!invicmdmode() && cs - n < viinsbegin) || cs == findbol()) {
-	feep();
 	return 1;
     }
     /* Put argument into the acceptable range -- it is not an error to  *
@@ -721,10 +693,8 @@ vibackwarddeletechar(char **args)
 int
 vikillline(char **args)
 {
-    if (viinsbegin > cs) {
-	feep();
+    if (viinsbegin > cs)
 	return 1;
-    }
     backdel(cs - viinsbegin);
     return 0;
 }
@@ -741,10 +711,8 @@ viputbefore(char **args)
 	return 1;
     if (zmod.flags & MOD_VIBUF)
 	buf = &vibuf[zmod.vibuf];
-    if (!buf->buf) {
-	feep();
+    if (!buf->buf)
 	return 1;
-    }
     if(buf->flags & CUTBUFFER_LINE) {
 	cs = findbol();
 	spaceinline(buf->len + 1);
@@ -775,10 +743,8 @@ viputafter(char **args)
 	return 1;
     if (zmod.flags & MOD_VIBUF)
 	buf = &vibuf[zmod.vibuf];
-    if (!buf->buf) {
-	feep();
+    if (!buf->buf)
 	return 1;
-    }
     if(buf->flags & CUTBUFFER_LINE) {
 	cs = findeol();
 	spaceinline(buf->len + 1);
@@ -806,10 +772,8 @@ vijoin(char **args)
     int x;
 
     startvichange(-1);
-    if ((x = findeol()) == ll) {
-	feep();
+    if ((x = findeol()) == ll)
 	return 1;
-    }
     cs = x + 1;
     for (x = 1; cs != ll && iblank(line[cs]); cs++, x++);
     backdel(x);
@@ -866,10 +830,8 @@ visetbuffer(char **args)
 
     if ((zmod.flags & MOD_VIBUF) ||
 	(((ch = getkey(0)) < '1' || ch > '9') &&
-	 (ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z'))) {
-	feep();
+	 (ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z')))
 	return 1;
-    }
     if (ch >= 'A' && ch <= 'Z')	/* needed in cut() */
 	zmod.flags |= MOD_VIAPP;
     else
@@ -889,7 +851,6 @@ vikilleol(char **args)
     startvichange(-1);
     if (!n) {
 	/* error -- line already empty */
-	feep();
 	return 1;
     }
     /* delete to end of line */
@@ -941,10 +902,9 @@ viquotedinsert(char **args)
     zsetterm();
 #endif
     foredel(1);
-    if(c < 0) {
-	feep();
+    if(c < 0)
 	return 1;
-    } else
+    else
 	return selfinsert(args);
 }
 
diff --git a/Src/Zle/zle_word.c b/Src/Zle/zle_word.c
index e768cbefa..31f83a2df 100644
--- a/Src/Zle/zle_word.c
+++ b/Src/Zle/zle_word.c
@@ -300,10 +300,8 @@ vibackwardkillword(char **args)
     int x = cs, lim = (viinsbegin > findbol()) ? viinsbegin : findbol();
     int n = zmult;
 
-    if (n < 0) {
-	feep();
+    if (n < 0)
 	return 1;
-    }
 /* this taken from "vibackwardword" */
     while (n--) {
 	while ((x > lim) && iblank(line[x - 1]))
@@ -479,22 +477,16 @@ transposewords(char **args)
 	    x = cs;
 	    while (x && line[x - 1] != '\n' && !iword(line[x]))
 		x--;
-	    if (!x || line[x - 1] == '\n') {
-		feep();
+	    if (!x || line[x - 1] == '\n')
 		return 1;
-	    }
 	}
 	for (p4 = x; p4 != ll && iword(line[p4]); p4++);
 	for (p3 = p4; p3 && iword(line[p3 - 1]); p3--);
-	if (!p3) {
-	    feep();
+	if (!p3)
 	    return 1;
-	}
 	for (p2 = p3; p2 && !iword(line[p2 - 1]); p2--);
-	if (!p2) {
-	    feep();
+	if (!p2)
 	    return 1;
-	}
 	for (p1 = p2; p1 && iword(line[p1 - 1]); p1--);
 	pp = temp = (char *)zhalloc(p4 - p1 + 1);
 	struncpy(&pp, (char *) line + p3, p4 - p3);
diff --git a/Src/builtin.c b/Src/builtin.c
index 26bd9d74f..8fe112cb5 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -50,7 +50,7 @@ static struct builtin builtins[] =
     BUILTIN("cd", 0, bin_cd, 0, 2, BIN_CD, NULL, NULL),
     BUILTIN("chdir", 0, bin_cd, 0, 2, BIN_CD, NULL, NULL),
     BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL),
-    BUILTIN("declare", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafilrtux", NULL),
+    BUILTIN("declare", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafgilrtux", NULL),
     BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "v", NULL),
     BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmr", NULL),
     BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL),
@@ -60,7 +60,7 @@ static struct builtin builtins[] =
     BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmr", NULL),
     BUILTIN("eval", BINF_PSPECIAL, bin_eval, 0, -1, BIN_EVAL, NULL, NULL),
     BUILTIN("exit", BINF_PSPECIAL, bin_break, 0, 1, BIN_EXIT, NULL, NULL),
-    BUILTIN("export", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "LRTUZafilrtu", "x"),
+    BUILTIN("export", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "LRTUZafilrtu", "xg"),
     BUILTIN("false", 0, bin_false, 0, -1, 0, NULL, NULL),
     BUILTIN("fc", BINF_FCOPTS, bin_fc, 0, -1, BIN_FC, "nlreIRWAdDfEim", NULL),
     BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL),
@@ -74,7 +74,7 @@ static struct builtin builtins[] =
 #endif
 
     BUILTIN("history", 0, bin_fc, 0, -1, BIN_FC, "nrdDfEim", "l"),
-    BUILTIN("integer", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "lrtux", "i"),
+    BUILTIN("integer", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "glrtux", "i"),
     BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL),
     BUILTIN("kill", 0, bin_kill, 0, -1, 0, NULL, NULL),
     BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL),
@@ -93,7 +93,7 @@ static struct builtin builtins[] =
     BUILTIN("pwd", 0, bin_pwd, 0, 0, 0, "rLP", NULL),
     BUILTIN("r", BINF_R, bin_fc, 0, -1, BIN_FC, "nrl", NULL),
     BUILTIN("read", 0, bin_read, 0, -1, 0, "rzu0123456789pkqecnAlE", NULL),
-    BUILTIN("readonly", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafiltux", "r"),
+    BUILTIN("readonly", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafgiltux", "r"),
     BUILTIN("rehash", 0, bin_hash, 0, 0, 0, "dfv", "r"),
     BUILTIN("return", BINF_PSPECIAL, bin_break, 0, 1, BIN_RETURN, NULL, NULL),
     BUILTIN("set", BINF_PSPECIAL, bin_set, 0, -1, 0, NULL, NULL),
@@ -107,7 +107,7 @@ static struct builtin builtins[] =
     BUILTIN("trap", BINF_PSPECIAL, bin_trap, 0, -1, 0, NULL, NULL),
     BUILTIN("true", 0, bin_true, 0, -1, 0, NULL, NULL),
     BUILTIN("type", 0, bin_whence, 0, -1, 0, "ampfsw", "v"),
-    BUILTIN("typeset", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafilrtuxm", NULL),
+    BUILTIN("typeset", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafgilrtuxm", NULL),
     BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL),
     BUILTIN("unalias", 0, bin_unhash, 1, -1, 0, "m", "a"),
     BUILTIN("unfunction", 0, bin_unhash, 1, -1, 0, "m", "f"),
@@ -120,7 +120,9 @@ static struct builtin builtins[] =
     BUILTIN("which", 0, bin_whence, 0, -1, 0, "ampsw", "c"),
 
 #ifdef DYNAMIC
-    BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "ILabcdipu", NULL),
+    BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "ILabcdipue", NULL),
+#else
+    BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "e", NULL),
 #endif
 };
 
@@ -1492,8 +1494,14 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 {
     int usepm, tc, keeplocal = 0;
 
-    /* use the existing pm? */
-    usepm = pm && !(pm->flags & (PM_UNSET | PM_AUTOLOAD));
+    /*
+     * Do we use the existing pm?  Note that this isn't the end of the
+     * story, because if we try and create a new pm at the same
+     * locallevel as an unset one we use the pm struct anyway: that's
+     * handled in createparam().  Here we just avoid using it for the
+     * present tests if it's unset.
+     */
+    usepm = pm && !(pm->flags & PM_UNSET);
 
     /* Always use an existing pm if special at current locallevel */
     if (pm && (pm->flags & PM_SPECIAL) && pm->level == locallevel)
@@ -1502,15 +1510,15 @@ typeset_single(char *cname, char *pname, Param pm, int func,
     /*
      * Don't use a non-special existing param if
      *   - the local level has changed, and
-     *   - the function is not `export'.
+     *   - we are really locallizing the parameter
      */
     if (usepm && !(pm->flags & PM_SPECIAL) &&
-	locallevel != pm->level && func != BIN_EXPORT)
+	locallevel != pm->level && (on & PM_LOCAL))
 	usepm = 0;
 
     /* attempting a type conversion, or making a tied colonarray? */
     if ((tc = usepm && (((off & pm->flags) | (on & ~pm->flags)) &
-			(PM_INTEGER|PM_HASHED|PM_ARRAY|PM_TIED))))
+			(PM_INTEGER|PM_HASHED|PM_ARRAY|PM_TIED|PM_AUTOLOAD))))
 	usepm = 0;
     if (tc && (pm->flags & PM_SPECIAL)) {
 	zerrnam(cname, "%s: can't change type of a special parameter",
@@ -1519,6 +1527,7 @@ typeset_single(char *cname, char *pname, Param pm, int func,
     }
 
     if (usepm) {
+	on &= ~PM_LOCAL;
 	if (!on && !roff && !value) {
 	    paramtab->printnode((HashNode)pm, 0);
 	    return pm;
@@ -1605,7 +1614,7 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 
     if (keeplocal)
 	pm->level = keeplocal;
-    else if (func != BIN_EXPORT)
+    else if (on & PM_LOCAL)
 	pm->level = locallevel;
     if (value && !(pm->flags & (PM_ARRAY|PM_HASHED)))
 	setsparam(pname, ztrdup(value));
@@ -1678,6 +1687,9 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 	return 0;
     }
 
+    if (!ops['g'])
+	on |= PM_LOCAL;
+
     if (on & PM_TIED) {
 	Param apm;
 	struct asgment asg0;
@@ -3074,7 +3086,7 @@ bin_emulate(char *nam, char **argv, char *ops, int func)
 {
     emulate(*argv, ops['R']);
     if (ops['L'])
-	dosetopt(LOCALOPTIONS, 1, 0);
+	opts[LOCALOPTIONS] = opts[LOCALTRAPS] = 1;
     return 0;
 }
 
diff --git a/Src/exec.c b/Src/exec.c
index 768706f4c..bc5548f1e 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -302,7 +302,7 @@ static char list_pipe_text[JOBTEXTSIZE];
 static int
 execcursh(Cmd cmd, LinkList args, int flags)
 {
-    if (!list_pipe)
+    if (!list_pipe && thisjob != list_pipe_job)
 	deletejob(jobtab + thisjob);
     cmdpush(CS_CURSH);
     execlist(cmd->u.list, 1, flags & CFLAG_EXEC);
@@ -828,7 +828,7 @@ execpline(Sublist l, int how, int last1)
     int ipipe[2], opipe[2];
     int pj, newjob;
     int old_simple_pline = simple_pline;
-    static int lastwj;
+    static int lastwj, lpforked;
 
     if (!l->left)
 	return lastval = (l->flags & PFLAG_NOT) != 0;
@@ -865,7 +865,7 @@ execpline(Sublist l, int how, int last1)
 	nowait = 0;
 	simple_pline = (l->left->type == END);
     }
-    lastwj = 0;
+    lastwj = lpforked = 0;
     execpline2(l->left, how, opipe[0], ipipe[1], last1);
     pline_level--;
     if (how & Z_ASYNC) {
@@ -890,7 +890,7 @@ execpline(Sublist l, int how, int last1)
 
 	    lastwj = thisjob = newjob;
 
-	    if (list_pipe)
+	    if (list_pipe || (pline_level && !(how & Z_TIMED)))
 		jn->stat |= STAT_NOPRINT;
 
 	    if (nowait) {
@@ -901,6 +901,11 @@ execpline(Sublist l, int how, int last1)
 		    DPUTS(!list_pipe_pid, "invalid list_pipe_pid");
 		    addproc(list_pipe_pid, list_pipe_text);
 
+		    /* If the super-job contains only the sub-shell, the
+		       sub-shell is the group leader. */
+		    if (!jn->procs->next || lpforked == 2)
+			jn->gleader = list_pipe_pid;
+
 		    for (pn = jobtab[jn->other].procs; pn; pn = pn->next)
 			if (WIFSTOPPED(pn->status))
 			    break;
@@ -914,12 +919,14 @@ execpline(Sublist l, int how, int last1)
 		    jn->stat |= STAT_STOPPED | STAT_CHANGED;
 		    printjob(jn, !!isset(LONGLISTJOBS), 1);
 		}
-		else
+		else if (newjob != list_pipe_job)
 		    deletejob(jn);
+		else
+		    lastwj = -1;
 	    }
 
 	    for (; !nowait;) {
-		if (list_pipe_child || pline_level) {
+		if (list_pipe_child) {
 		    jn->stat |= STAT_NOPRINT;
 		    makerunning(jn);
 		}
@@ -930,8 +937,11 @@ execpline(Sublist l, int how, int last1)
 		    jn->stat & STAT_DONE &&
 		    lastval2 & 0200)
 		    killpg(mypgrp, lastval2 & ~0200);
-		if ((list_pipe || last1 || pline_level) &&
-		    !list_pipe_child && jn->stat & STAT_STOPPED) {
+		if (!list_pipe_child && !lpforked && !subsh &&
+		    (list_pipe || last1 || pline_level) &&
+		    ((jn->stat & STAT_STOPPED) ||
+		     (list_pipe_job && pline_level &&
+		      (jobtab[list_pipe_job].stat & STAT_STOPPED)))) {
 		    pid_t pid;
 		    int synch[2];
 
@@ -951,28 +961,41 @@ execpline(Sublist l, int how, int last1)
 		    else if (pid) {
 			char dummy;
 
+			lpforked = 
+			    (killpg(jobtab[list_pipe_job].gleader, 0) == -1 ? 2 : 1);
 			list_pipe_pid = pid;
 			nowait = errflag = 1;
 			breaks = loops;
 			close(synch[1]);
 			read(synch[0], &dummy, 1);
 			close(synch[0]);
-			jobtab[list_pipe_job].other = newjob;
-			jobtab[list_pipe_job].stat |= STAT_SUPERJOB;
-			jn->stat |= STAT_SUBJOB | STAT_NOPRINT;
-			jn->other = pid;
-			if (list_pipe || last1)
+			/* If this job has finished, we leave it as a
+			 * normal (non-super-) job. */
+			if (!(jn->stat & STAT_DONE)) {
+			    jobtab[list_pipe_job].other = newjob;
+			    jobtab[list_pipe_job].stat |= STAT_SUPERJOB;
+			    jn->stat |= STAT_SUBJOB | STAT_NOPRINT;
+			    jn->other = pid;
+			}
+			if ((list_pipe || last1) && jobtab[list_pipe_job].procs)
 			    killpg(jobtab[list_pipe_job].gleader, SIGSTOP);
 			break;
 		    }
 		    else {
 			close(synch[0]);
 			entersubsh(Z_ASYNC, 0, 0);
-			setpgrp(0L, mypgrp = jobtab[list_pipe_job].gleader);
+			if (jobtab[list_pipe_job].procs) {
+			    if (setpgrp(0L, mypgrp = jobtab[list_pipe_job].gleader)
+				== -1) {
+				setpgrp(0L, mypgrp = getpid());
+			    }
+			} else
+			    setpgrp(0L, mypgrp = getpid());
 			close(synch[1]);
 			kill(getpid(), SIGSTOP);
 			list_pipe = 0;
 			list_pipe_child = 1;
+			opts[INTERACTIVE] = 0;
 			break;
 		    }
 		}
@@ -985,12 +1008,11 @@ execpline(Sublist l, int how, int last1)
 
 	    if (list_pipe && (lastval & 0200) && pj >= 0 &&
 		(!(jn->stat & STAT_INUSE) || (jn->stat & STAT_DONE))) {
+		deletejob(jn);
 		jn = jobtab + pj;
-		jn->stat |= STAT_NOPRINT;
-		killjb(jobtab + pj, lastval & ~0200);
+		killjb(jn, lastval & ~0200);
 	    }
-	    if (list_pipe_child || ((list_pipe || pline_level) &&
-				    (jn->stat & STAT_DONE)))
+	    if (list_pipe_child || (list_pipe && (jn->stat & STAT_DONE)))
 		deletejob(jn);
 	    thisjob = pj;
 
@@ -1117,7 +1139,8 @@ void
 untokenize(char *s)
 {
     if (*s) {
-	char *p = s, c;
+	char *p = s;
+	int c;
 
 	while ((c = *s++))
 	    if (itok(c)) {
@@ -1569,8 +1592,10 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 		is_builtin = 1;
 #ifdef DYNAMIC
 		/* autoload the builtin if necessary */
-		if (!((Builtin) hn)->handlerfunc)
+		if (!((Builtin) hn)->handlerfunc) {
 		    load_module(((Builtin) hn)->optstr);
+		    hn = builtintab->getnode(builtintab, cmdarg);
+		}
 #endif
 		break;
 	    }
@@ -2187,15 +2212,14 @@ entersubsh(int how, int cl, int fake)
 	    if (kill(jobtab[list_pipe_job].gleader, 0) == -1 ||
 		setpgrp(0L, jobtab[list_pipe_job].gleader) == -1) {
 		jobtab[list_pipe_job].gleader =
-		    jobtab[thisjob].gleader = mypgrp;
-		setpgrp(0L, mypgrp);
-
+		    jobtab[thisjob].gleader = (list_pipe_child ? mypgrp : getpid());
+		setpgrp(0L, jobtab[list_pipe_job].gleader);
 		if (how & Z_SYNC)
 		    attachtty(jobtab[thisjob].gleader);
 	    }
 	}
 	else if (!jobtab[thisjob].gleader ||
-		 (setpgrp(0L, jobtab[thisjob].gleader) == -1)) {
+		 setpgrp(0L, jobtab[thisjob].gleader) == -1) {
 	    jobtab[thisjob].gleader = getpid();
 	    if (list_pipe_job != thisjob &&
 		!jobtab[list_pipe_job].gleader)
@@ -2783,14 +2807,13 @@ execshfunc(Cmd cmd, Shfunc shf, LinkList args)
     if (errflag)
 	return;
 
-    if (!list_pipe) {
+    if (!list_pipe && thisjob != list_pipe_job) {
 	/* Without this deletejob the process table *
 	 * would be filled by a recursive function. */
 	last_file_list = jobtab[thisjob].filelist;
 	jobtab[thisjob].filelist = NULL;
 	deletejob(jobtab + thisjob);
     }
-
     if (isset(XTRACE)) {
 	LinkNode lptr;
 	printprompt4();
@@ -2869,7 +2892,7 @@ doshfunc(char *name, List list, LinkList doshargs, int flags, int noreturnval)
 {
     char **tab, **x, *oargv0 = NULL;
     int oldzoptind, oldlastval;
-    char saveopts[OPT_SIZE];
+    char saveopts[OPT_SIZE], *oldscriptname;
     int obreaks = breaks;
 
     HEAPALLOC {
@@ -2881,6 +2904,8 @@ doshfunc(char *name, List list, LinkList doshargs, int flags, int noreturnval)
 	starttrapscope();
 
 	tab = pparams;
+	oldscriptname = scriptname;
+	scriptname = name;
 	oldzoptind = zoptind;
 	zoptind = 1;
 
@@ -2923,6 +2948,7 @@ doshfunc(char *name, List list, LinkList doshargs, int flags, int noreturnval)
 	    argzero = oargv0;
 	}
 	zoptind = oldzoptind;
+	scriptname = oldscriptname;
 	pparams = tab;
 
 	if (isset(LOCALOPTIONS)) {
diff --git a/Src/glob.c b/Src/glob.c
index 93b497632..ea4980b8b 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -1993,7 +1993,7 @@ dyncat(char *s1, char *s2)
     char *ptr;
     int l1 = strlen(s1);
 
-    ptr = (char *)ncalloc(l1 + strlen(s2) + 1);
+    ptr = (char *)zhalloc(l1 + strlen(s2) + 1);
     strcpy(ptr, s1);
     strcpy(ptr + l1, s2);
     return ptr;
diff --git a/Src/hashtable.c b/Src/hashtable.c
index e80461b4e..c4c0b00ac 100644
--- a/Src/hashtable.c
+++ b/Src/hashtable.c
@@ -133,6 +133,7 @@ deletehashtable(HashTable ht)
 	ht->last->next = ht->next;
     else
 	firstht = ht->next;
+    zsfree(ht->tablename);
 #endif /* ZSH_HASH_DEBUG */
     zfree(ht->nodes, ht->hsize * sizeof(HashNode));
     zfree(ht, sizeof(*ht));
diff --git a/Src/init.c b/Src/init.c
index 10286ea87..2da409415 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -235,10 +235,10 @@ parseargs(char **argv)
 		else
 		    dosetopt(optno, action, 1);
               break;
-	    } else if (isspace(**argv)) {
+	    } else if (isspace(STOUC(**argv))) {
 		/* zsh's typtab not yet set, have to use ctype */
 		while (*++*argv)
-		    if (!isspace(**argv)) {
+		    if (!isspace(STOUC(**argv))) {
 			zerr("bad option string: `%s'", args, 0);
 			exit(1);
 		    }
@@ -615,7 +615,8 @@ setupvals(void)
 	prompt2 = ztrdup("%_> ");
     }
     prompt3 = ztrdup("?# ");
-    prompt4 = ztrdup("+ ");
+    prompt4 = (emulation == EMULATE_KSH || emulation == EMULATE_SH)
+	? ztrdup("+ ") : ztrdup("+%N:%i> ");
     sprompt = ztrdup("zsh: correct '%R' to '%r' [nyae]? ");
 
     ifs         = ztrdup(DEFAULT_IFS);
diff --git a/Src/jobs.c b/Src/jobs.c
index 31861e284..b2d7e9af1 100644
--- a/Src/jobs.c
+++ b/Src/jobs.c
@@ -92,9 +92,13 @@ makerunning(Job jn)
 
     jn->stat &= ~STAT_STOPPED;
     for (pn = jn->procs; pn; pn = pn->next)
+#if 0
 	if (WIFSTOPPED(pn->status) && 
 	    (!(jn->stat & STAT_SUPERJOB) || pn->next))
 	    pn->status = SP_RUNNING;
+#endif
+        if (WIFSTOPPED(pn->status))
+	    pn->status = SP_RUNNING;
 
     if (jn->stat & STAT_SUPERJOB)
 	makerunning(jobtab + jn->other);
@@ -176,13 +180,14 @@ update_job(Job jn)
 		/* If we have `cat foo|while read a; grep $a bar;done'
 		 * and have hit ^Z, the sub-job is stopped, but the
 		 * super-job may still be running, waiting to be stopped
-		 * or to exit. So we have to send it a SIGSTOP. */
+		 * or to exit. So we have to send it a SIGTSTP. */
 		int i;
 
 		for (i = 1; i < MAXJOB; i++)
 		    if ((jobtab[i].stat & STAT_SUPERJOB) &&
-			jobtab[i].other == job) {
-			killpg(jobtab[i].gleader, SIGSTOP);
+			jobtab[i].other == job &&
+			jobtab[i].gleader) {
+			killpg(jobtab[i].gleader, SIGTSTP);
 			break;
 		    }
 	    }
@@ -667,6 +672,8 @@ deletejob(Job jn)
     if (jn->ty)
 	zfree(jn->ty, sizeof(struct ttyinfo));
 
+    if (jn->stat & STAT_WASSUPER)
+	deletejob(jobtab + jn->other);
     jn->gleader = jn->other = 0;
     jn->stat = jn->stty_in_env = 0;
     jn->procs = NULL;
@@ -780,22 +787,38 @@ waitjob(int job, int sig)
 	       what this might be.  --oberon
 
 	    errflag = 0; */
+	    if (subsh) {
+		killjb(jn, SIGCONT);
+		jn->stat &= ~STAT_STOPPED;
+	    }
 	    if (jn->stat & STAT_SUPERJOB) {
 		Job sj = jobtab + jn->other;
-		if (sj->stat & STAT_DONE) {
+		if ((sj->stat & STAT_DONE) || !sj->procs) {
 		    struct process *p;
 		    
 		    for (p = sj->procs; p; p = p->next)
 			if (WIFSIGNALED(p->status)) {
-			    killpg(jn->gleader, WTERMSIG(p->status));
+			    if (jn->gleader != mypgrp && jn->procs->next)
+				killpg(jn->gleader, WTERMSIG(p->status));
+			    else
+				kill(jn->procs->pid, WTERMSIG(p->status));
 			    kill(sj->other, SIGCONT);
 			    kill(sj->other, WTERMSIG(p->status));
 			    break;
 			}
 		    if (!p) {
+			int cp;
+
 			jn->stat &= ~STAT_SUPERJOB;
-			if (WIFEXITED(jn->procs->status))
-			    jn->gleader = mypgrp;
+			jn->stat |= STAT_WASSUPER;
+
+			if ((cp = ((WIFEXITED(jn->procs->status) ||
+				    WIFSIGNALED(jn->procs->status)) &&
+				   killpg(jn->gleader, 0) == -1))) {
+			    Process p;
+			    for (p = jn->procs; p->next; p = p->next);
+			    jn->gleader = p->pid;
+			}
 			/* This deleted the job too early if the parent
 			   shell waited for a command in a list that will
 			   be executed by the sub-shell (e.g.: if we have
@@ -804,6 +827,11 @@ waitjob(int job, int sig)
 			   but the parent shell gets notified for the
 			   sleep.
 			   deletejob(sj); */
+			/* If this super-job contains only the sub-shell,
+			   we have to attach the tty to our process group
+			   (which is shared by the sub-shell) now. */
+			if (!jn->procs->next || cp || jn->procs->pid != jn->gleader)
+			    attachtty(jn->gleader);
 			kill(sj->other, SIGCONT);
 		    }
 		    curjob = jn - jobtab;
@@ -813,7 +841,9 @@ waitjob(int job, int sig)
 
 		    jn->stat |= STAT_STOPPED;
 		    for (p = jn->procs; p; p = p->next)
-			p->status = sj->procs->status;
+			if (p->status == SP_RUNNING ||
+			    (!WIFEXITED(p->status) && !WIFSIGNALED(p->status)))
+			    p->status = sj->procs->status;
 		    curjob = jn - jobtab;
 		    printjob(jn, !!isset(LONGLISTJOBS), 1);
 		    break;
@@ -1232,7 +1262,14 @@ bin_fg(char *name, char **argv, char *ops, int func)
 		fflush(shout);
 		if (func != BIN_WAIT) {		/* fg */
 		    thisjob = job;
-		    attachtty(jobtab[job].gleader);
+		    if ((jobtab[job].stat & STAT_SUPERJOB) &&
+			((!jobtab[job].procs->next ||
+			  WIFEXITED(jobtab[job].procs->status) ||
+			  WIFSIGNALED(jobtab[job].procs->status))) &&
+			jobtab[jobtab[job].other].gleader)
+			attachtty(jobtab[jobtab[job].other].gleader);
+		    else
+			attachtty(jobtab[job].gleader);
 		}
 	    }
 	    if (stopped) {
diff --git a/Src/loop.c b/Src/loop.c
index b35024d6b..16e4ff314 100644
--- a/Src/loop.c
+++ b/Src/loop.c
@@ -378,7 +378,7 @@ execif(Cmd cmd, LinkList args, int flags)
     noerrexit = olderrexit;
 
     if (*t) {
-	cmdpush(s ? CS_ELIFTHEN : CS_IFTHEN);
+	cmdpush(*i ? (s ? CS_ELIFTHEN : CS_IFTHEN) : CS_ELSE);
 	execlist(*t, 1, flags & CFLAG_EXEC);
 	cmdpop();
     } else
diff --git a/Src/module.c b/Src/module.c
index fa7dd2774..ce926c27a 100644
--- a/Src/module.c
+++ b/Src/module.c
@@ -709,7 +709,14 @@ bin_zmodload(char *nam, char **args, char *ops, int func)
 	zwarnnam(nam, "what do you want to unload?", NULL, 0);
 	return 1;
     }
-    if (ops['d'])
+    if (ops['e'] && (ops['I'] || ops['L'] || ops['a'] || ops['d'] ||
+		     ops['i'] || ops['u'])) {
+	zwarnnam(nam, "-e cannot be combined with other options", NULL, 0);
+	return 1;
+    }
+    if (ops['e'])
+	return bin_zmodload_exist(nam, args, ops);
+    else if (ops['d'])
 	return bin_zmodload_dep(nam, args, ops);
     else if ((ops['a'] || ops['b']) && !(ops['c'] || ops['p']))
 	return bin_zmodload_auto(nam, args, ops);
@@ -727,6 +734,46 @@ bin_zmodload(char *nam, char **args, char *ops, int func)
 
 /**/
 static int
+bin_zmodload_exist(char *nam, char **args, char *ops)
+{
+    LinkNode node;
+    Module m;
+
+    if (!*args) {
+	for (node = firstnode(bltinmodules); node; incnode(node)) {
+	    nicezputs((char *) getdata(node), stdout);
+	    putchar('\n');
+	}
+	for (node = firstnode(modules); node; incnode(node)) {
+	    m = (Module) getdata(node);
+	    if (m->handle && !(m->flags & MOD_UNLOAD)) {
+		nicezputs(m->nam, stdout);
+		putchar('\n');
+	    }
+	}
+	return 0;
+    } else {
+	int ret = 0, f;
+
+	for (; !ret && *args; args++) {
+	    f = 0;
+	    for (node = firstnode(bltinmodules);
+		 !f && node; incnode(node))
+		f = !strcmp(*args, (char *) getdata(node));
+	    for (node = firstnode(modules);
+		 !f && node; incnode(node)) {
+		m = (Module) getdata(node);
+		if (m->handle && !(m->flags & MOD_UNLOAD))
+		    f = !strcmp(*args, m->nam);
+	    }
+	    ret = !f;
+	}
+	return ret;
+    }
+}
+
+/**/
+static int
 bin_zmodload_dep(char *nam, char **args, char *ops)
 {
     LinkNode node;
@@ -1116,6 +1163,37 @@ bin_zmodload_load(char *nam, char **args, char *ops)
 }
 
 /**/
+#else /* DYNAMIC */
+
+/* This is the version for shells without dynamic linking. */
+
+/**/
+int
+bin_zmodload(char *nam, char **args, char *ops, int func)
+{
+    /* We understand only the -e option. */
+
+    if (ops['e']) {
+	LinkNode node;
+
+	if (!*args) {
+	    for (node = firstnode(bltinmodules); node; incnode(node)) {
+		nicezputs((char *) getdata(node), stdout);
+		putchar('\n');
+	    }
+	} else {
+	    for (; *args; args++)
+		for (node = firstnode(bltinmodules); node; incnode(node))
+		    if (strcmp(*args, (char *) getdata(node)))
+			return 1;
+	}
+	return 0;
+    }
+    /* Otherwise we return 1 -- different from the dynamic version. */
+    return 1;
+}
+
+/**/
 #endif /* DYNAMIC */
 
 /* The list of module-defined conditions. */
@@ -1206,6 +1284,180 @@ addconddefs(char const *nam, Conddef c, int size)
     return hadf ? hads : 1;
 }
 
+/* This list of hook functions defined. */
+
+/**/
+Hookdef hooktab;
+
+/* Find a hook definition given the name. */
+
+/**/
+Hookdef
+gethookdef(char *n)
+{
+    Hookdef p;
+
+    for (p = hooktab; p; p = p->next)
+	if (!strcmp(n, p->name))
+	    return p;
+    return NULL;
+}
+
+/* This adds the given hook definition. The return value is zero on      *
+ * success and 1 on failure.                                             */
+
+/**/
+int
+addhookdef(Hookdef h)
+{
+    if (gethookdef(h->name))
+	return 1;
+
+    h->next = hooktab;
+    hooktab = h;
+    PERMALLOC {
+	h->funcs = newlinklist();
+    } LASTALLOC;
+
+    return 0;
+}
+
+/* This adds multiple hook definitions. This is like addbuiltins(). */
+
+/**/
+int
+addhookdefs(char const *nam, Hookdef h, int size)
+{
+    int hads = 0, hadf = 0;
+
+    while (size--) {
+	if (addhookdef(h)) {
+	    zwarnnam(nam, "name clash when adding condition `%s'", h->name, 0);
+	    hadf = 1;
+	} else
+	    hads = 2;
+	h++;
+    }
+    return hadf ? hads : 1;
+}
+
+/* Delete hook definitions. */
+
+/**/
+int
+deletehookdef(Hookdef h)
+{
+    Hookdef p, q;
+
+    for (p = hooktab, q = NULL; p && p != h; q = p, p = p->next);
+
+    if (!p)
+	return 1;
+
+    if (q)
+	q->next = p->next;
+    else
+	hooktab = p->next;
+    freelinklist(p->funcs, NULL);
+    return 0;
+}
+
+/**/
+int
+deletehookdefs(char const *nam, Hookdef h, int size)
+{
+    while (size--) {
+	deletehookdef(h);
+	h++;
+    }
+    return 1;
+}
+
+/* Add a function to a hook. */
+
+/**/
+int
+addhookdeffunc(Hookdef h, Hookfn f)
+{
+    PERMALLOC {
+	addlinknode(h->funcs, (void *) f);
+    } LASTALLOC;
+    return 0;
+}
+
+/**/
+int
+addhookfunc(char *n, Hookfn f)
+{
+    Hookdef h = gethookdef(n);
+
+    if (h)
+	return addhookdeffunc(h, f);
+    return 1;
+}
+
+/* Delete a function from a hook. */
+
+/**/
+int
+deletehookdeffunc(Hookdef h, Hookfn f)
+{
+    LinkNode p;
+
+    for (p = firstnode(h->funcs); p; incnode(p))
+	if (f == (Hookfn) getdata(p)) {
+	    remnode(h->funcs, p);
+	    return 0;
+	}
+    return 1;
+}
+
+/**/
+int
+deletehookfunc(char *n, Hookfn f)
+{
+    Hookdef h = gethookdef(n);
+
+    if (h)
+	return deletehookdeffunc(h, f);
+    return 1;
+}
+
+/* Run the function(s) for a hook. */
+
+/**/
+int
+runhookdef(Hookdef h, void *d)
+{
+    if (empty(h->funcs)) {
+	if (h->def)
+	    return h->def(h, d);
+	return 0;
+    } else if (h->flags & HOOKF_ALL) {
+	LinkNode p;
+	int r;
+
+	for (p = firstnode(h->funcs); p; incnode(p))
+	    if ((r = ((Hookfn) getdata(p))(h, d)))
+		return r;
+	if (h->def)
+	    return h->def(h, d);
+	return 0;
+    } else
+	return ((Hookfn) getdata(lastnode(h->funcs)))(h, d);
+}
+
+/**/
+int
+runhook(char *n, void *d)
+{
+    Hookdef h = gethookdef(n);
+
+    if (h)
+	return runhookdef(h, d);
+    return 0;
+}
+
 /* This adds the given parameter definition. The return value is zero on *
  * success and 1 on failure. */
 
diff --git a/Src/params.c b/Src/params.c
index b9f3d33a0..d71cfb46e 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -588,7 +588,7 @@ createparam(char *name, int flags)
 
 	DPUTS(oldpm && oldpm->level > locallevel,
 	      "BUG:  old local parameter not deleteed");
-	if (oldpm && oldpm->level == locallevel) {
+	if (oldpm && (oldpm->level == locallevel || !(flags & PM_LOCAL))) {
 	    if (!(oldpm->flags & PM_UNSET) || (oldpm->flags & PM_SPECIAL)) {
 		oldpm->flags &= ~PM_UNSET;
 		return NULL;
@@ -616,7 +616,7 @@ createparam(char *name, int flags)
 	pm = (Param) alloc(sizeof *pm);
 	pm->nam = nulstring;
     }
-    pm->flags = flags;
+    pm->flags = flags & ~PM_LOCAL;
 
     if(!(pm->flags & PM_SPECIAL))
 	assigngetset(pm);
@@ -1898,7 +1898,7 @@ arrhashsetfn(Param pm, char **val)
      * since that could cause trouble for special hashes.  This way, *
      * it's up to pm->sets.hfn() what to do.                         */
     int alen = arrlen(val);
-    HashTable opmtab = paramtab, ht;
+    HashTable opmtab = paramtab, ht = 0;
     char **aptr = val;
     Value v = (Value) hcalloc(sizeof *v);
     v->b = -1;
@@ -1909,7 +1909,8 @@ arrhashsetfn(Param pm, char **val)
 	     NULL, 0);
 	return;
     }
-    ht = paramtab = newparamtable(17, pm->nam);
+    if (alen)
+	ht = paramtab = newparamtable(17, pm->nam);
     while (*aptr) {
 	/* The parameter name is ztrdup'd... */
 	v->pm = createparam(*aptr, PM_SCALAR|PM_UNSET);
diff --git a/Src/prompt.c b/Src/prompt.c
index ad7cdbc31..89407694e 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -531,6 +531,14 @@ putpromptchar(int doprint, int endchar)
 		if(Rstring)
 		    stradd(Rstring);
 		break;
+	    case 'i':
+		addbufspc(DIGBUFSIZE);
+		sprintf(bp, "%ld", (long)lineno);
+		bp += strlen(bp);
+		break;
+	    case 'N':
+		stradd(scriptname ? scriptname : argzero);
+		break;
 	    case '\0':
 		return 0;
 	    case Meta:
diff --git a/Src/signals.c b/Src/signals.c
index e9851b57a..d29bdd4b0 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -586,7 +586,10 @@ killjb(Job jn, int sig)
  
                 for (pn = jn->procs; pn->next; pn = pn->next)
                     err = kill(pn->pid, sig);
- 
+
+		if (!jobtab[jn->other].procs && pn)
+		    err = kill(pn->pid, sig);
+
                 return err;
             }
  
diff --git a/Src/subst.c b/Src/subst.c
index ab6bf84fb..18d815404 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -1328,10 +1328,15 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 			else
 			    t = aval;
 		    } else if (!isarr) {
-			arr[0] = val;
-			arr[1] = NULL;
+			if (!*val && arrasg > 1) {
+			    arr[0] = NULL;
+			    l = 0;
+			} else {
+			    arr[0] = val;
+			    arr[1] = NULL;
+			    l = 1;
+			}
 			t = aval = arr;
-			l = 1;
 		    } else
 			l = arrlen(aval), t = aval;
 		    p = a = zalloc(sizeof(char *) * (l + 1));
diff --git a/Src/utils.c b/Src/utils.c
index 16dbe9229..a409ab03c 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -62,9 +62,10 @@ zerr(const char *fmt, const char *str, int num)
     /*
      * scriptname is set when sourcing scripts, so that we get the
      * correct name instead of the generic name of whatever
-     * program/script is running.
+     * program/script is running.  It's also set in shell functions,
+     * so test locallevel, too.
      */
-    nicezputs(isset(SHINSTDIN) ? "zsh" :
+    nicezputs((isset(SHINSTDIN) && !locallevel) ? "zsh" :
 	      scriptname ? scriptname : argzero, stderr);
     fputs(": ", stderr);
     zerrnam(NULL, fmt, str, num);
@@ -79,7 +80,7 @@ zerrnam(const char *cmd, const char *fmt, const char *str, int num)
 	    return;
 	errflag = 1;
 	trashzle();
-	if(unset(SHINSTDIN)) {
+	if (unset(SHINSTDIN) || locallevel) {
 	    nicezputs(scriptname ? scriptname : argzero, stderr);
 	    fputs(": ", stderr);
 	}
@@ -133,7 +134,7 @@ zerrnam(const char *cmd, const char *fmt, const char *str, int num)
 	    putc(*fmt == Meta ? *++fmt ^ 32 : *fmt, stderr);
 	    fmt++;
 	}
-    if (unset(SHINSTDIN) && lineno)
+    if ((unset(SHINSTDIN) || locallevel) && lineno)
 	fprintf(stderr, " [%ld]\n", (long)lineno);
     else
 	putc('\n', stderr);
@@ -2133,7 +2134,12 @@ mkarray(char *s)
 void
 zbeep(void)
 {
-    if (isset(BEEP))
+    char *vb;
+    if ((vb = getsparam("ZBEEP"))) {
+	int len;
+	vb = getkeystring(vb, &len, 2, NULL);
+	write(SHTTY, vb, len);
+    } else if (isset(BEEP))
 	write(SHTTY, "\07", 1);
 }
 
diff --git a/Src/xmods.conf b/Src/xmods.conf
index c36105721..6b7f1701a 100644
--- a/Src/xmods.conf
+++ b/Src/xmods.conf
@@ -3,3 +3,4 @@ comp1
 zle
 compctl
 sched
+complist
diff --git a/Src/zsh.export b/Src/zsh.export
index 9cb186ffa..5a3a94c70 100644
--- a/Src/zsh.export
+++ b/Src/zsh.export
@@ -5,6 +5,8 @@ addconddefs
 addedx
 addhashnode
 addhistnum
+addhookdefs
+addhookfunc
 addparamdefs
 addwrapper
 arrvargetfn
@@ -43,6 +45,8 @@ current_limits
 deletebuiltins
 deleteconddefs
 deletehashtable
+deletehookdefs
+deletehookfunc
 deleteparamdefs
 deleteparamtable
 deletewrapper
@@ -207,6 +211,7 @@ resetneeded
 restoredir
 reswdtab
 retflag
+runhookdef
 runshfunc
 scancountparams
 scanhashtable
diff --git a/Src/zsh.h b/Src/zsh.h
index 23e84b2ec..88175eea6 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -278,8 +278,9 @@ typedef struct heapstack *Heapstack;
 typedef struct histent   *Histent;
 typedef struct forcmd    *Forcmd;
 typedef struct autofn    *AutoFn;
+typedef struct hookdef   *Hookdef;
 
-typedef struct asgment  *Asgment;
+typedef struct asgment   *Asgment;
 
 
 /********************************/
@@ -628,10 +629,12 @@ struct job {
 #define STAT_INUSE	(1<<6)	/* this job entry is in use             */
 #define STAT_SUPERJOB	(1<<7)	/* job has a subjob                     */
 #define STAT_SUBJOB	(1<<8)	/* job is a subjob                      */
-#define STAT_CURSH	(1<<9)	/* last command is in current shell     */
-#define STAT_NOSTTY	(1<<10)	/* the tty settings are not inherited   */
+#define STAT_WASSUPER   (1<<9)  /* was a super-job, sub-job needs to be */
+				/* deleted */
+#define STAT_CURSH	(1<<10)	/* last command is in current shell     */
+#define STAT_NOSTTY	(1<<11)	/* the tty settings are not inherited   */
 				/* from this job when it exits.         */
-#define STAT_ATTACH	(1<<11)	/* delay reattaching shell to tty       */
+#define STAT_ATTACH	(1<<12)	/* delay reattaching shell to tty       */
 
 #define SP_RUNNING -1		/* fake status for jobs currently running */
 
@@ -887,6 +890,22 @@ struct module {
 #define MOD_UNLOAD  (1<<1)
 #define MOD_SETUP   (1<<2)
 
+/* C-function hooks */
+
+typedef int (*Hookfn) _((Hookdef, void *));
+
+struct hookdef {
+    Hookdef next;
+    char *name;
+    Hookfn def;
+    int flags;
+    LinkList funcs;
+};
+
+#define HOOKF_ALL 1
+
+#define HOOKDEF(name, func, flags) { NULL, name, (Hookfn) func, flags, NULL }
+
 /* node used in parameter hash table (paramtab) */
 
 struct param {
@@ -959,12 +978,13 @@ struct param {
 #define PM_UNALIASED	(1<<11)	/* do not expand aliases when autoloading     */
 
 #define PM_TIED 	(1<<12)	/* array tied to colon-path or v.v. */
-#define PM_SPECIAL	(1<<13) /* special builtin parameter                  */
-#define PM_DONTIMPORT	(1<<14)	/* do not import this variable                */
-#define PM_RESTRICTED	(1<<15) /* cannot be changed in restricted mode       */
-#define PM_UNSET	(1<<16)	/* has null value                             */
-#define PM_REMOVABLE	(1<<17)	/* special can be removed from paramtab */
-#define PM_AUTOLOAD     (1<<18) /* autoloaded from module */
+#define PM_LOCAL	(1<<13) /* this parameter will be made local */
+#define PM_SPECIAL	(1<<14) /* special builtin parameter                  */
+#define PM_DONTIMPORT	(1<<15)	/* do not import this variable                */
+#define PM_RESTRICTED	(1<<16) /* cannot be changed in restricted mode       */
+#define PM_UNSET	(1<<17)	/* has null value                             */
+#define PM_REMOVABLE	(1<<18)	/* special can be removed from paramtab */
+#define PM_AUTOLOAD     (1<<19) /* autoloaded from module */
 
 /* Flags for extracting elements of arrays and associative arrays */
 #define SCANPM_WANTVALS   (1<<0)
diff --git a/Util/zsh-development-guide b/Util/zsh-development-guide
index f8e331f54..6e8eb1b2d 100644
--- a/Util/zsh-development-guide
+++ b/Util/zsh-development-guide
@@ -397,6 +397,154 @@ builtins and condition codes:
     ...
   }
 
+Modules can also define function hooks. Other modules can then add
+functions to these hooks to make the first module call these functions
+instead of the default.
+
+Again, an array is used to define hooks:
+
+  static struct hookdef foohooks[] = {
+    HOOKDEF("foo", foofunc, 0),
+  };
+
+The first argument of the macro is the name of the hook. This name
+is used whenever the hook is used. The second argument is the default
+function for the hook or NULL if no default function exists. The
+last argument is used to define flags for the hook. Currently only one
+such flag is defined: `HOOKF_ALL'. If this flag is given and more than
+one function was added to the hook, all functions will be called
+(including the default function). Otherwise only the last function
+added will be called.
+
+The functions that can be used as default functions or that can be
+added to a hook have to be defined like:
+
+  /**/
+  static int
+  foofunc(Hookdef h, void *data)
+  {
+    ...
+  }
+
+The first argument is a pointer to the struct defining the hook. The
+second argument is an arbitrary pointer that is given to the function
+used to invoke hooks (see below).
+
+The functions to register and de-register hooks look like those for
+the other things that can be defined by modules:
+
+  /**/
+  int
+  boot_foo(Module m)
+  {
+    int ret;
+
+    ret = addhookdefs(m->nam, foohooks, sizeof(foohooks)/sizeof(*foohooks))
+    ...
+  }
+  ...
+  /**/
+  int
+  cleanup_foo(Module m)
+  {
+    deletehookdefs(m->nam, foohooks, sizeof(foohooks)/sizeof(*foohooks));
+    ...
+  }
+
+Modules that define hooks can invoke the function(s) registered for
+them by calling the function `runhook(name, data)'. The first argument 
+is the name of the hook and the second one is the pointer given to the 
+hook functions as their second argument. Hooks that have the `HOOKF_ALL' 
+flag call all function defined for them until one returns non-zero.
+The return value of `runhook()' is the return value of the last hook
+function called or zero if none was called.
+
+To add a function to a hook, the function `addhookfunc(name, func)' is 
+called with the name of the hook and a hook function as arguments.
+Deleting them is done by calling `deletehookfunc(name, func)' with the 
+same arguments as for the corresponding call to `addhookfunc()'.
+
+Alternative forms of the last three function are provided for hooks
+that are changed or called very often. These functions,
+`runhookdef(def, data)', `addhookdeffunc(def, func)', and
+`deletehookdeffunc(def, func)' get a pointer to the `hookdef'
+structure defining the hook instead of the name and otherwise behave
+like their counterparts.
+
+Modules can also define function hooks. Other modules can then add
+functions to these hooks to make the first module call these functions
+instead of the default.
+
+Again, an array is used to define hooks:
+
+  static struct hookdef foohooks[] = {
+    HOOKDEF("foo", foofunc, 0),
+  };
+
+The first argument of the macro is the name of the hook. This name
+is used whenever the hook is used. The second argument is the default
+function for the hook or NULL if no default function exists. The
+last argument is used to define flags for the hook. Currently only one
+such flag is defined: `HOOKF_ALL'. If this flag is given and more than
+one function was added to the hook, all functions will be called
+(including the default function). Otherwise only the last function
+added will be called.
+
+The functions that can be used as default functions or that can be
+added to a hook have to be defined like:
+
+  /**/
+  static int
+  foofunc(Hookdef h, void *data)
+  {
+    ...
+  }
+
+The first argument is a pointer to the struct defining the hook. The
+second argument is an arbitrary pointer that is given to the function
+used to invoke hooks (see below).
+
+The functions to register and de-register hooks look like those for
+the other things that can be defined by modules:
+
+  /**/
+  int
+  boot_foo(Module m)
+  {
+    int ret;
+
+    ret = addhookdefs(m->nam, foohooks, sizeof(foohooks)/sizeof(*foohooks))
+    ...
+  }
+  ...
+  /**/
+  int
+  cleanup_foo(Module m)
+  {
+    deletehookdefs(m->nam, foohooks, sizeof(foohooks)/sizeof(*foohooks));
+    ...
+  }
+
+Modules that define hooks can invoke the function(s) registered for
+them by calling the function `runhook(name, data)'. The first argument 
+is the name of the hook and the second one is the pointer given to the 
+hook functions as their second argument. Hooks that have the `HOOKF_ALL' 
+flag call all function defined for them until one returns non-zero.
+The return value of `runhook()' is the return value of the last hook
+function called or zero if none was called.
+
+To add a function to a hook, the function `addhookfunc(name, func)' is 
+called with the name of the hook and a hook function as arguments.
+Deleting them is done by calling `deletehookfunc(name, func)' with the 
+same arguments as for the corresponding call to `addhookfunc()'.
+
+Alternative forms of the last three function are provided for hooks
+that are changed or called very often. These functions,
+`runhookdef(def, data)', `addhookdeffunc(def, func)', and
+`deletehookdeffunc(def, func)' get a pointer to the `hookdef'
+structure defining the hook instead of the name and otherwise behave
+like their counterparts.
+
 Finally, modules can define wrapper functions. These functions are
 called whenever a shell function is to be executed.
 
diff --git a/aczsh.m4 b/aczsh.m4
index cdbd607a8..2e6c65899 100644
--- a/aczsh.m4
+++ b/aczsh.m4
@@ -47,6 +47,8 @@ for ac_shellvar in $ac_shellvars; do
     eval test '"${'$ac_shellvar'+set}"' = set && ac_set=$ac_shellvar
     ac_getconfs=$ac_getconfs$ac_getconf
     eval ac_test_$ac_shellvar="\$ac_getconf"
+  else
+    eval ac_test_$ac_shellvar="\$$ac_shellvar"
   fi
 done
 case "$ac_result$ac_getconfs" in