about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-06-27 05:33:04 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-06-27 05:33:04 +0000
commit346825df86466cf151be61b9429ef2c1734e66ea (patch)
treebd3e1ccd947b47f974c62b4113c5276416de2c9f
parenta2159285e80508bb682d90a71270fbddada8bd05 (diff)
downloadzsh-346825df86466cf151be61b9429ef2c1734e66ea.tar.gz
zsh-346825df86466cf151be61b9429ef2c1734e66ea.tar.xz
zsh-346825df86466cf151be61b9429ef2c1734e66ea.zip
zsh-3.1.5-pws-24 zsh-3.1.5-pws-24
-rw-r--r--ChangeLog290
-rw-r--r--Completion/Base/_first4
-rw-r--r--Completion/Base/_subscript22
-rw-r--r--Completion/Base/_tilde26
-rw-r--r--Completion/Builtins/_autoload2
-rw-r--r--Completion/Builtins/_bindkey2
-rw-r--r--Completion/Builtins/_echotc2
-rw-r--r--Completion/Builtins/_kill6
-rw-r--r--Completion/Builtins/_limits2
-rw-r--r--Completion/Builtins/_wait15
-rw-r--r--Completion/Builtins/_zmodload4
-rw-r--r--Completion/Commands/.distfiles2
-rw-r--r--Completion/Commands/_correct_filename2
-rw-r--r--Completion/Commands/_most_recent_file4
-rw-r--r--Completion/Core/_approximate4
-rw-r--r--Completion/Core/_expand2
-rw-r--r--Completion/Core/_list2
-rw-r--r--Completion/Core/_match20
-rw-r--r--Completion/Core/_path_files16
-rw-r--r--Completion/Core/compdump17
-rw-r--r--Completion/Core/compinit108
-rw-r--r--Completion/Core/compinstall56
-rw-r--r--Completion/User/.distfiles6
-rw-r--r--Completion/User/_chown15
-rw-r--r--Completion/User/_dd5
-rw-r--r--Completion/User/_find6
-rw-r--r--Completion/User/_groups6
-rw-r--r--Completion/User/_make24
-rw-r--r--Completion/User/_mh6
-rw-r--r--Completion/User/_rlogin2
-rw-r--r--Completion/User/_x_options6
-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.yo62
-rw-r--r--Doc/Zsh/compctl.yo21
-rw-r--r--Doc/Zsh/compsys.yo111
-rw-r--r--Doc/Zsh/compwid.yo11
-rw-r--r--Doc/Zsh/func.yo6
-rw-r--r--Doc/Zsh/guide.yo4
-rw-r--r--Doc/Zsh/mod_compctl.yo2
-rw-r--r--Doc/Zsh/mod_complist.yo119
-rw-r--r--Doc/Zsh/mod_deltochar.yo2
-rw-r--r--Doc/Zsh/mod_zle.yo54
-rw-r--r--Doc/Zsh/modules.yo5
-rw-r--r--Doc/Zsh/options.yo27
-rw-r--r--Doc/Zsh/params.yo21
-rw-r--r--Doc/Zsh/zftpsys.yo6
-rw-r--r--Doc/Zsh/zle.yo57
-rw-r--r--Doc/zsh.yo6
-rw-r--r--Doc/ztexi.yo5
-rw-r--r--Etc/FAQ.yo145
-rw-r--r--Functions/Zftp/zfgoto86
-rw-r--r--Functions/Zftp/zfmark49
-rw-r--r--Src/Modules/parameter.c2
-rw-r--r--Src/Zle/.distfiles1
-rw-r--r--Src/Zle/comp.h25
-rw-r--r--Src/Zle/compctl.c14
-rw-r--r--Src/Zle/complist.c918
-rw-r--r--Src/Zle/complist.mdd3
-rw-r--r--Src/Zle/deltochar.c7
-rw-r--r--Src/Zle/iwidgets.list1
-rw-r--r--Src/Zle/zle.export34
-rw-r--r--Src/Zle/zle.h25
-rw-r--r--Src/Zle/zle_hist.c467
-rw-r--r--Src/Zle/zle_keymap.c65
-rw-r--r--Src/Zle/zle_main.c79
-rw-r--r--Src/Zle/zle_misc.c241
-rw-r--r--Src/Zle/zle_move.c248
-rw-r--r--Src/Zle/zle_params.c22
-rw-r--r--Src/Zle/zle_refresh.c10
-rw-r--r--Src/Zle/zle_thingy.c111
-rw-r--r--Src/Zle/zle_tricky.c602
-rw-r--r--Src/Zle/zle_utils.c45
-rw-r--r--Src/Zle/zle_vi.c371
-rw-r--r--Src/Zle/zle_word.c194
-rw-r--r--Src/builtin.c144
-rw-r--r--Src/exec.c154
-rw-r--r--Src/glob.c2
-rw-r--r--Src/hashtable.c1
-rw-r--r--Src/hist.c9
-rw-r--r--Src/init.c32
-rw-r--r--Src/input.c35
-rw-r--r--Src/jobs.c9
-rw-r--r--Src/lex.c5
-rw-r--r--Src/loop.c21
-rw-r--r--Src/module.c254
-rw-r--r--Src/options.c1
-rw-r--r--Src/params.c9
-rw-r--r--Src/parse.c12
-rw-r--r--Src/signals.c159
-rw-r--r--Src/subst.c13
-rw-r--r--Src/utils.c49
-rw-r--r--Src/xmods.conf1
-rw-r--r--Src/zsh.export6
-rw-r--r--Src/zsh.h47
-rw-r--r--Util/zsh-development-guide148
-rw-r--r--configure.in3
100 files changed, 4532 insertions, 1580 deletions
diff --git a/ChangeLog b/ChangeLog
index 8daf08449..fb09a99ca 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,293 @@
+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
+	
+	* Sven/pws: 6616, 6609, 6726 (merged): `zle [ -N ] [ -n num ]
+	  widget args'; internal widgets handle arguments and return
+	  values; search commands use arguments; read -[kq] can use
+	  zle's key mechanism; zle -R [msg] redisplays line editor;
+	  digit-argument feeps if the keypress wasn't a digit;
+	  universal-argument handles argument as numeric.
+
+	* pws: 6725: Src/signals.c: more local traps:  now don't add trap
+	  to list to restore when outside a function, but do if the function
+	  simply unsets a trap which was set outside.
+
+1999-06-18  Peter Stephenson  <pws@ibmth.difi.unipi.it>
+
+	* pws: 6718: Completion/Core/compinit, Completion/Core/compdump,
+	  Doc/Zsh/compsys.yo: rebind existing completion widgets for new
+	  completion instead of defining new ones.
+
+	* pws: 6717: Doc/Zsh/options.yo, Src/exec.c, Src/options.c,
+	  Src/signals.c, Src/zsh.h: Option LOCAL_TRAPS saves and restores
+	  traps on exit from functions; set for ksh emulation.
+
+	* Tanaka Akira: 6716: Completion/User/_chown: some systems use `:'
+	  as separator in chown.
+
+	* Oliver/Sven: 6709, 6710: Completion files Base/_subscript,
+	  User/_chown, User/_groups, User/_x_options: _subscript shows
+	  listing for ordinary arrays; new chown and chgrp completions;
+	  example _x_options can complete displays.
+
+	* Sven: 6707: Src/exec.c: Shell structures not at the end
+	  of a pipeline are suspendable.  This is the only way
+	  of stopping a command in such a structure where the command
+	  handles interruptions in such a way that zsh doesn't see them.
+
+1999-06-17  Peter Stephenson  <pws@ibmth.difi.unipi.it>
+
+	* pws: 6705: Doc/Zsh/builtins.yo, Src/builtin.c, Src/exec.c,
+	  Src/init.c, Src/input.c, Src/parse.c: some more LINENO
+	  subtleties: embedded function definitions have correct line
+	  number; all lines, not just ones with new PS1, are counted
+	  interactively; traps with trap builtin use line no. of
+	  surrounding environment.
+
+	* Sven: 6693: Src/Modules/parameter.c, Src/builtin.c, Src/exec.c,
+	  Src/input.c, Src/zsh.h: update LINENO properly when parsing a
+	  string.
+
+	* Sven: 6692: Src/Zle/comp.h, Src/Zle/compctl.c,
+	  Src/Zle/zle_tricky.c, Doc/Zsh/compwid.yo,
+	  Completion/Builtins/_kill, Completion/Builtins/_wait,
+	  Completion/User/_gdb: allow the -y option to compadd for
+	  specifying an array to use when listing.
+
+	* Sven: 6689: Src/Zle/zle_tricky.c, Completion/Base/_subscript:
+	  test for closing bracket in get_comp_string().
+
+	* Oliver: 6688: Zsh/compsys.yo, Zsh/func.yo, Zsh/mod_zle.yo,
+	  Zsh/params.yo, Zsh/zftpsys.yo: spelling changes.
+
+	* Wayne: 6682: Src/hist.c: Uniquified history commands are limited
+	  to size $SAVEHIST, so that the last  $((HISTSIZE-SAVEHIST))
+	  commands are always available even with HIST_EXPIRE_DUPS_FIRST.
+
+	* Sven: 6686: Completion/Core/_match, Completion/Core/_path_files,
+	  Doc/Zsh/compsys.yo: New configuration keys path_cursor,
+	  match_insert.
+
+	* Sven: 6685: Src/Zle/compctl.c, Src/Zle/zle_params.c,
+	  Doc/Zsh/zle.yo, Completion/Base/_first,
+	  Completion/Commands/_correct_filename,
+	  Completion/Commands/_most_recent_file,
+	  Completion/Core/_approximate, Completion/Core/_expand,
+	  Completion/Core/_list: NUMERIC is unset if no prefix was given;
+	  it may be unset explicitly, and if set again will be restored in
+	  the expected way.
+
+	* Wayne: 6683: Src/exec.c: ambiguous brace
+
+	* Wayne: 6681: Src/builtin.c: start-of-loop check for history -r
+	  was wrong.
+
+1999-06-16  Peter Stephenson  <pws@ibmth.difi.unipi.it>
+
+	* pws: 6679: Src/Zle/zle_thingy.c, Doc/Zsh/compwid.yo:  always
+	  use .complete-word etc. as widget type in zle -C.
+
+	* pws: 6677: Doc/Zsh/guide.yo: avoid TeX overfull hbox problem.
+
+	* Andrej: 6674: Doc/zsh.yo, Doc/ztexi.yo: change order of initial
+	formatting instructions to make interaction with system
+	configuration files better.
+
+	* pws: 6660: Doc/Zsh/compctl.yo: pointer to new completion system
+	  at top of compctl documentation.
+
+	* pws: 6659: Src/Zle/zle_tricky.c: a couple of NULL's should be 0's
+
+	* pws: 6658: Doc/Zsh/options.yo, Src/init.c:  options strings at
+	  shell startup can end with whitespace (for #!), but anything
+	  after the whitespace causes an error.
+
+	* Sven: 6657: Completion files Base/_subscript,
+	  Builtins/_autoload, Builtins/_bindkey, Builtins/_echotc,
+	  Builtins/_kill, Builtins/_limits, Builtins/_wait,
+	  Builtins/_zmodload, User/_dd, User/_find, User/_gdb, User/_make,
+	  User/_mh, User/_rlogin, User/_x_options: New gdb completion;
+	  improved make completion; use compadd in preference to compgen
+	  -[sk]; configuration keys ps_args, ps_listsargs.
+
+	* Sven: 6654: Src/Zle/zle_tricky.c: empty display list after -y.
+
+	* Bart: 6652: Src/exec.c: trailing spaces confused #! emulation.
+
+	* Sven: 6649: Src/Zle/zle_tricky.c: behaviour of compctl -l.
+
+	* Bart: 6646: Src/lex.c: extra braces to enhance beauty of code
+	  which appeared in 3.0.5.
+
 1999-06-15  Peter Stephenson  <pws@ibmth.difi.unipi.it>
 
+	* Tanaka Akira: 6642: Completion/Base/_tilde: complete directory
+	  stack elements after ~+ or ~-.
+
+	* Tanaka Akira: 6641: Src/subst.c: treat ~-0 like ~0
+
+	* pws: 6639: configure.in: don't try type of $enable_val if empty
+	  (needed when off_t/ino_t are 64-bit without explicit enabling).
+
+	* zsh-3.1.5-pws-22 made available
+
 	* Oliver: 6636: Completion/Builtins/_limits: wasn't working
 
 	* Bart: 6617 + minor changes: Src/utils.c: Be more careful keeping
@@ -34,7 +322,7 @@
 
 	* Sven: 6614: Src/Zle/zle_tricky.c, Completion/Brace/_brace_parameter:
 	  completion after quotes in parameters
-	
+
 	* pws: 6610: Src/glob.c: globbing flags shouldn't be active
 	  without extendedglob.
 
diff --git a/Completion/Base/_first b/Completion/Base/_first
index d9e7ee82c..8b4da019d 100644
--- a/Completion/Base/_first
+++ b/Completion/Base/_first
@@ -40,9 +40,9 @@
 #       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 -gt 1 ]]; then
+#       if [[ ${NUMERIC:-1} -gt 1 ]]; then
 #         max=$NUMERIC
-#         NUMERIC=1
+#         unset NUMERIC
 #       else
 #         # The default is to search the last 100 lines.
 #         max=10
diff --git a/Completion/Base/_subscript b/Completion/Base/_subscript
index abaabec8c..66d88b00c 100644
--- a/Completion/Base/_subscript
+++ b/Completion/Base/_subscript
@@ -1,7 +1,27 @@
 #compdef -subscript-
 
 if [[ ${(Pt)${compstate[parameter]}} = assoc* ]]; then
-  compgen -S ']' -k "( ${(kP)${compstate[parameter]}} )"
+  if [[ "$RBUFFER" = \]* ]]; then
+    compadd -S '' - "${(@kP)${compstate[parameter]}}"
+  else
+    compadd -S ']' - "${(@kP)${compstate[parameter]}}"
+  fi
+elif [[ ${(Pt)${compstate[parameter]}} = array* ]]; then
+  local list i j
+
+  ind=( {1..${#${(P)${compstate[parameter]}}}} )
+  list=()
+  for i in "$ind[@]"; do
+    [[ "$i" = ${PREFIX}*${SUFFIX} ]] &&
+        list=( "$list[@]" 
+	  "${(r:4:: ::):)i} $(print -D ${(P)${compstate[parameter]}[$i]})" )
+  done
+
+  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/Base/_tilde b/Completion/Base/_tilde
index c24c65701..d03030821 100644
--- a/Completion/Base/_tilde
+++ b/Completion/Base/_tilde
@@ -7,10 +7,32 @@
 #   `(( compstate[nmatches] )) || compgen -nu -qS/'
 # below that.
 
+local c s dirs list
+
 if [[ "$SUFFIX" = */* ]]; then
   ISUFFIX="/${SUFFIX#*/}$ISUFFIX"
   SUFFIX="${SUFFIX%%/*}"
-  compgen -nu -S ''
+  s=(-S '')
 else
-  compgen -nu -qS/
+  s=(-qS/)
 fi
+
+if compset -P +; then
+  dirs="$(dirs -v)"
+  list=("${(f)dirs}")
+  [[ -o pushdminus ]] && dirs="$(awk '{ $1 = '$#list' - $1 - 1;
+  				        printf("%s\t%s\n", $1, $2); }' <<<$dirs)"
+  list=("${(@)list%	*}")
+  c=(-y '$dirs' -k "($list)")
+elif compset -P -; then
+  dirs="$(dirs -v)"
+  list=("${(f)dirs}")
+  [[ ! -o pushdminus ]] && dirs="$(awk '{ $1 = '$#list' - $1 - 1;
+					  printf("%s\t%s\n", $1, $2); }' <<<$dirs)"
+  list=("${(@)list%	*}")
+  c=(-y '$dirs' -k "($list)")
+else
+  c=(-nu)
+fi
+
+compgen "$c[@]" "$s[@]"
diff --git a/Completion/Builtins/_autoload b/Completion/Builtins/_autoload
index d1c255b73..f10fc34e7 100644
--- a/Completion/Builtins/_autoload
+++ b/Completion/Builtins/_autoload
@@ -1,3 +1,3 @@
 #compdef autoload
 
-compgen -s '${^fpath}/*(N:t)'
+compadd - ${^fpath}/*(N:t)
diff --git a/Completion/Builtins/_bindkey b/Completion/Builtins/_bindkey
index 57b3d8a85..91ecfcc28 100644
--- a/Completion/Builtins/_bindkey
+++ b/Completion/Builtins/_bindkey
@@ -8,7 +8,7 @@
 # Where appropriate, will complete keymaps instead of widgets.
 
 if [[ "$words[2]" = -*[DAN]* || "$words[CURRENT-1]" = -*M ]]; then
-  compgen -s '$(bindkey -l)'
+  compadd - $(bindkey -l)
 else
   compgen -b -M 'r:|-=* r:|=*'
 fi
diff --git a/Completion/Builtins/_echotc b/Completion/Builtins/_echotc
index ce282437d..15dfcef08 100644
--- a/Completion/Builtins/_echotc
+++ b/Completion/Builtins/_echotc
@@ -1,3 +1,3 @@
 #compdef echotc
 
-compgen -k '(al dc dl do le up al bl cd ce cl cr dc dl do ho is le ma nd nl se so up)'
+compadd al dc dl do le up al bl cd ce cl cr dc dl do ho is le ma nd nl se so up
diff --git a/Completion/Builtins/_kill b/Completion/Builtins/_kill
index 4f8c1db6d..26f6bf5cd 100644
--- a/Completion/Builtins/_kill
+++ b/Completion/Builtins/_kill
@@ -3,13 +3,13 @@
 local list
 
 if compset -P 1 -; then
-  compgen -k "($signals[1,-3])"
+  compadd $signals[1,-3]
 else
   local ret=1
 
   compgen -P '%' -j && ret=0
-  list=("$(ps 2>/dev/null)")
-  compgen -y '$list' -s '${${${(f)"$(ps 2>/dev/null)"}[2,-1]## #}%% *}' && 
+  list=("${(@Mr:COLUMNS-1:)${(f)$(ps ${compconfig[ps_listargs]:-$compconfig[ps_args]} 2>/dev/null)}[2,-1]:#[ 	]#${PREFIX}[0-9]#${SUFFIX}[ 	]*}")
+  compadd -y list - ${${${(f)"$(ps $compconfig[ps_args] 2>/dev/null)"}[2,-1]## #}%% *} &&
     ret=0
 
   return ret
diff --git a/Completion/Builtins/_limits b/Completion/Builtins/_limits
index 6835a6244..0b8837d67 100644
--- a/Completion/Builtins/_limits
+++ b/Completion/Builtins/_limits
@@ -1,3 +1,3 @@
 #compdef limit unlimit
 
-compgen -s '${${(f)"$(limit)"}%% *}'
+compadd ${${(f)"$(limit)"}%% *}
diff --git a/Completion/Builtins/_wait b/Completion/Builtins/_wait
index 41d09c9b2..8f9339ebd 100644
--- a/Completion/Builtins/_wait
+++ b/Completion/Builtins/_wait
@@ -1,9 +1,20 @@
 #compdef wait
 
+# This uses two configuration keys:
+#
+#  ps_args
+#    This can be set to options of the ps(1) command that should be
+#    used when invoking it to get the pids to complete.
+#
+#  ps_listargs
+#    This defaults to the value of the `ps_args' key and defines
+#    options for the ps command that are to be used when creating
+#    the list to display during completion.
+
 local list ret=1
 
 compgen -P '%' -j && ret=0
-list=("$(ps 2>/dev/null)")
-compgen -y '$list' -s '${${${(f)"$(ps 2>/dev/null)"}[2,-1]## #}%% *}' && ret=0
+list=("${(@Mr:COLUMNS-1:)${(f)$(ps ${compconfig[ps_listargs]:-$compconfig[ps_args]} 2>/dev/null)}[2,-1]:#[ 	]#${PREFIX}[0-9]#${SUFFIX}[ 	]*}")
+compadd -y list - ${${${(f)"$(ps $compconfig[ps_args] 2>/dev/null)"}[2,-1]## #}%% *} && ret=0
 
 return ret
diff --git a/Completion/Builtins/_zmodload b/Completion/Builtins/_zmodload
index 4b0e91442..d3a39b5de 100644
--- a/Completion/Builtins/_zmodload
+++ b/Completion/Builtins/_zmodload
@@ -5,7 +5,7 @@ local fl="$words[2]"
 if [[ "$fl" = -*(a*u|u*a)* || "$fl" = -*a* && CURRENT -ge 4 ]]; then
   compgen -B
 elif [[ "$fl" = -*u* ]]; then
-  compgen -s '$(zmodload)'
+  compadd - $(zmodload)
 else
-  compgen -s '${^module_path}/*(N:t:r)'
+  compadd - ${^module_path}/*(N:t:r)
 fi
diff --git a/Completion/Commands/.distfiles b/Completion/Commands/.distfiles
index 19a02ef39..f79d69704 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 _most_recent_file 
 '
diff --git a/Completion/Commands/_correct_filename b/Completion/Commands/_correct_filename
index baef38edf..7431a4831 100644
--- a/Completion/Commands/_correct_filename
+++ b/Completion/Commands/_correct_filename
@@ -23,7 +23,7 @@ if [[ -z $WIDGET ]]; then
   file=$1
   local IPREFIX
 else
-  (( NUMERIC > 1 )) && max_approx=$NUMERIC
+  (( ${NUMERIC:-1} > 1 )) && max_approx=$NUMERIC
 fi
 
 if [[ $file = \~*/* ]]; then
diff --git a/Completion/Commands/_most_recent_file b/Completion/Commands/_most_recent_file
index c571483ca..868da8993 100644
--- a/Completion/Commands/_most_recent_file
+++ b/Completion/Commands/_most_recent_file
@@ -14,9 +14,9 @@ local file tilde etilde
 if [[ $PREFIX = \~*/* ]]; then
   tilde=${PREFIX%%/*}
   etilde=${~tilde}
-  file=($~PREFIX*$~SUFFIX(om[$NUMERIC]N))
+  file=($~PREFIX*$~SUFFIX(om[${NUMERIC:-1}]N))
   file=(${file/#$etilde/$tilde})
 else
-  file=($~PREFIX*$~SUFFIX(om[$NUMERIC]N))
+  file=($~PREFIX*$~SUFFIX(om[${NUMERIC:-1}]N))
 fi
 (( $#file )) && compadd -U -i "$IPREFIX" -I "$ISUFFIX" -f -Q $file
diff --git a/Completion/Core/_approximate b/Completion/Core/_approximate
index c63416901..61f21c5b9 100644
--- a/Completion/Core/_approximate
+++ b/Completion/Core/_approximate
@@ -89,14 +89,14 @@ fi
 
 # Get the number of errors to accept.
 
-if [[ "$cfgacc" = *[nN]* && NUMERIC -ne 1 ]]; then
+if [[ "$cfgacc" = *[nN]* && ${NUMERIC:-1} -ne 1 ]]; then
   # Stop if we also have a `!'.
 
   [[ "$cfgacc" = *\!* ]] && return 1
 
   # Prefer the numeric argument if that has a sensible value.
 
-  comax="$NUMERIC"
+  comax="${NUMERIC:-1}"
 else
   comax="${cfgacc//[^0-9]}"
 fi
diff --git a/Completion/Core/_expand b/Completion/Core/_expand
index aca3839d4..58e184657 100644
--- a/Completion/Core/_expand
+++ b/Completion/Core/_expand
@@ -17,7 +17,7 @@
 #    In this case, expansion of substitutions will be done if the
 #    expression evaluates to `1'. For example, with
 #
-#      compconf expand_substitute='NUMERIC != 1'
+#      compconf expand_substitute='${NUMERIC:-1} != 1'
 #
 #    substitution will be performed only if given an explicit numeric
 #    argument other than `1', as by typing ESC 2 TAB.
diff --git a/Completion/Core/_list b/Completion/Core/_list
index 0d5651c23..28a5161d1 100644
--- a/Completion/Core/_list
+++ b/Completion/Core/_list
@@ -14,7 +14,7 @@
 #    will be done if the expression evaluates to `1'.
 #    For example, with
 #
-#      compconf list_condition='NUMERIC != 1'
+#      compconf list_condition='${NUMERIC:-1} != 1'
 #
 #    delaying will be done only if given an explicit numeric argument
 #    other than `1'.
diff --git a/Completion/Core/_match b/Completion/Core/_match
index 3c639935c..251c65381 100644
--- a/Completion/Core/_match
+++ b/Completion/Core/_match
@@ -9,7 +9,7 @@
 # expand-or-complete function because otherwise the pattern will
 # be expanded using globbing.
 #
-# Configuration key used:
+# Configuration keys used:
 #
 #  match_original
 #    If this is set to a `only', pattern matching will only be tried
@@ -18,6 +18,11 @@
 #    no completions, matching will be tried again with a `*' inserted
 #    at the cursor position. If this key is not set or set to an empty
 #    string, matching will only be attempted with the `*' inserted.
+#
+#  match_insert
+#    If this is set to a string starting with `unambig', menucompletion
+#    will only be turned on if no unambiguous string could be built
+#    that is at least as long as the original string.
 
 local tmp opm="$compstate[pattern_match]" ret=0
 
@@ -37,7 +42,12 @@ if [[ -n "$compconfig[match_original]" ]]; then
   compstate[pattern_match]="$opm"
   compstate[matcher]="$compstate[total_matchers]"
 
-  (( ret )) && return 0
+  if (( ret )); then
+    [[ "$compconfig[match_insert]" = unambig* &&
+       $#compstate[unambiguous] -ge ${#:-${PREFIX}${SUFFIX}} ]] && 
+        compstate[pattern_insert]=unambiguous
+    return 0
+  fi
 fi
 
 # No completion with inserting `*'?
@@ -50,4 +60,10 @@ _complete && ret=1
 compstate[pattern_match]="$opm"
 compstate[matcher]="$compstate[total_matchers]"
 
+if (( ! ret )); then
+  [[ "$compconfig[match_insert]" = unambig* &&
+     $#compstate[unambiguous] -ge ${#:-${PREFIX}${SUFFIX}} ]] && 
+      compstate[pattern_insert]=unambiguous
+fi
+
 return 1-ret
diff --git a/Completion/Core/_path_files b/Completion/Core/_path_files
index d0d1c6a0a..58f343367 100644
--- a/Completion/Core/_path_files
+++ b/Completion/Core/_path_files
@@ -11,12 +11,17 @@
 # with one of the suffixes thus given are treated like files with one
 # of the suffixes in the `fignore' array in normal completion.
 #
-# This function supports one configuration key:
+# This function supports two configuration keys:
 #
 #  path_expand
 #    If this is set to a non-empty string, the partially typed path
 #    from the line will be expanded as far as possible even if trailing
 #    pathname components can not be completed.
+#
+#  path_cursor
+#    If this is set to an non-empty string, the cursor will be placed
+#    in the path after the ambiguous pathname component even when using
+#    menucompletion.
 
 local linepath realpath donepath prepath testpath exppath
 local tmp1 tmp2 tmp3 tmp4 i orig pre suf tpre tsuf
@@ -304,10 +309,11 @@ for prepath in "$prepaths[@]"; do
       # it as far as possible.
 
       if [[ -n $menu ]]; then
+        [[ -n "$compconfig[path_cursor]" ]] && compstate[to_end]=''
         if [[ "$tmp3" = */* ]]; then
 	  compadd -Uf -p "$linepath$testpath" -s "/${tmp3#*/}" \
 	          -W "$prepath$realpath$testpath" "$ignore[@]" \
-		  "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \
+		  "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" -M 'r:|/=* r:|=*' \
 		  "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
 		  - "${(@)tmp1%%/*}"
 	else
@@ -371,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 5ee04e028..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.
 
@@ -44,11 +44,13 @@ print >> $_d_file
 # Now dump the key bindings. We dump all bindings for zle widgets
 # whose names start with a underscore.
 # We need both the zle -C's and the bindkey's to recreate.
+# We can ignore any zle -C which rebinds a standard widget (second
+# argument to zle does not begin with a `_').
 
 _d_bks=()
 zle -lL |
   while read -rA _d_line; do
-    if [[ ${_d_line[5]} = _* ]]; then
+    if [[ ${_d_line[3]} = _* && ${_d_line[5]} = _* ]]; then
       print -r - ${_d_line}
       _d_bks=($_d_bks ${_d_line[3]})
     fi
@@ -86,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 23bc94cf9..e078cc33c 100644
--- a/Completion/Core/compinit
+++ b/Completion/Core/compinit
@@ -41,15 +41,13 @@
 # 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.
+
+emulate -L zsh
+
+typeset _i_dumpfile _i_files _i_line _i_done _i_dir _i_autodump=0
+typeset _i_tag _i_file
+
 while [[ $# -gt 0 && $1 = -[df] ]]; do
   if [[ "$1" = -d ]]; then
     _i_autodump=1
@@ -59,56 +57,27 @@ while [[ $# -gt 0 && $1 = -[df] ]]; do
       shift
     fi
   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
 
@@ -215,7 +184,11 @@ compdef() {
       fi
 
       # Define the widget.
-      zle -C "$func" "$1" "$func"
+      if [[ $1 = .* ]]; then
+	zle -C "$func" "$1" "$func"
+      else
+	zle -C "$func" ".$1" "$func"
+      fi
       shift
 
       # And bind the keys...
@@ -321,22 +294,32 @@ _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 [[ -n $_compdir ]]; then
+    if [[ $_compdir = */Core ]]; then
       # Add all the Completion subdirectories
-      fpath=(${_i_fdir:h}/*(/) $fpath)
-    elif [[ -d $_i_fdir/Core ]]; then
+      fpath=(${_compdir:h}/*(/) $fpath)
+    elif [[ -d $_compdir/Core ]]; then
       # Likewise
-      fpath=(${_i_fdir}/*(/) $fpath)
-    else
-      fpath=($_i_fdir $fpath)
+      fpath=(${_compdir}/*(/) $fpath)
     fi
     _i_files=( ${^~fpath:/.}/_(|*[^~])(N:t) )
   fi
 fi
 
+
+# Rebind the standard widgets
+for _i_line in complete-word delete-char-or-list expand-or-complete \
+  expand-or-complete-prefix list-choices menu-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
@@ -345,7 +328,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
@@ -369,30 +351,12 @@ if [[ -z "$_i_done" ]]; then
     done
   done
 
-  bindkey |
-    while read -rA _i_line; do
-      case "$_i_line[2]" in
-      (complete-word) ;&
-      (delete-char-or-list) ;&
-      (expand-or-complete) ;&
-      (expand-or-complete-prefix) ;&
-      (list-choices) ;&
-      (menu-complete) ;&
-      (menu-expand-or-complete) ;&
-      (reverse-menu-complete)
-	zle -C _complete_$_i_line[2] $_i_line[2] _main_complete
-	bindkey "${_i_line[1][2,-2]}" _complete_$_i_line[2]
-	;;
-      esac
-    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..255565b3f 100644
--- a/Completion/Core/compinstall
+++ b/Completion/Core/compinstall
@@ -3,7 +3,7 @@
 # available in some directory; they should have been installed with the
 # the shell (except we haven't written that yet).
 #
-# Source this script (e.g. `. /path/compinstall') and answer the questions.
+# Run this script as a function and answer the questions.
 #
 # Normally, this will alter ~/.zshrc (or wherever ZDOTDIR puts it), but you
 # can make that unwritable and it will leave the lines in a temporary file
@@ -23,33 +23,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
 
 # Look for the defaults.
 _ci_startline='# The following lines were added by compinstall'
@@ -60,6 +40,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.
@@ -83,10 +64,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 +77,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:"
@@ -166,7 +149,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 +167,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 +175,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 -d"
 [[ $_ci_dtype != standard ]] && _ci_lines="${_ci_lines} $_ci_dumpfile"
 _ci_lines="${_ci_lines}
 "
@@ -363,6 +348,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/.distfiles b/Completion/User/.distfiles
index ee0017035..07fddc134 100644
--- a/Completion/User/.distfiles
+++ b/Completion/User/.distfiles
@@ -1,6 +1,6 @@
 DISTFILES_SRC='
     .distfiles
-    _a2ps _compress _configure _dd _dvi _find _gunzip _gzip _hosts
-    _make _man _mh _pdf _ps _rcs _rlogin _strip _stty _tar _tar_archive
-    _tex _uncompress _x_options _xfig 
+    _a2ps _chown _compress _configure _dd _dvi _find _groups _gunzip _gzip
+    _hosts _make _man _mh _pdf _ps _rcs _rlogin _strip _stty
+    _tar _tar_archive _tex _uncompress _x_options _xfig 
 '
diff --git a/Completion/User/_chown b/Completion/User/_chown
new file mode 100644
index 000000000..1ec76b39e
--- /dev/null
+++ b/Completion/User/_chown
@@ -0,0 +1,15 @@
+#compdef chown chgrp
+
+if [[ CURRENT -eq 2 || CURRENT -eq 3 && $words[CURRENT-1] = -* ]]; then
+  if [[ $words[1] = chgrp ]] || compset -P '*[:.]'; then
+    _groups
+  else
+    if [[ $OSTYPE = (solaris*|hpux*) ]]; then
+      compgen -u -S ':' -q
+    else
+      compgen -u -S '.' -q
+    fi
+  fi
+else
+  _files
+fi
diff --git a/Completion/User/_dd b/Completion/User/_dd
index 6094d60b7..e57074520 100644
--- a/Completion/User/_dd
+++ b/Completion/User/_dd
@@ -4,10 +4,9 @@ if compset -P 1 'conv\='; then
   # If there's a comma present, ignore up to the last one.  The
   # test alone will have that effect.
   compset -p '*,'
-  compgen -S, -q \
-      -k '(ascii ebcdic ibm block unblock lcase ucase swab noerror sync)'
+  compadd -qS, -q ascii ebcdic ibm block unblock lcase ucase swab noerror sync
 elif compset -P 1 '[io]f\='; then
   _files
 else
-  compgen -S '=' -k '(if of ibs obs bs cbs skip files seek count conv)'
+  compadd -S '=' if of ibs obs bs cbs skip files seek count conv
 fi
diff --git a/Completion/User/_find b/Completion/User/_find
index dd00b0ed1..de8c3bff9 100644
--- a/Completion/User/_find
+++ b/Completion/User/_find
@@ -5,10 +5,10 @@ local prev="$words[CURRENT-1]"
 if compset -N '-(ok|exec)' '\;'; then
   _normal
 elif compset -P 1 -; then
-  compgen -s 'daystart {max,min,}depth follow noleaf version xdev \
+  compadd daystart {max,min,}depth follow noleaf version xdev \
     {a,c,}newer {a,c,m}{min,time} empty false {fs,x,}type gid inum links \
     {i,}{l,}name {no,}{user,group} path perm regex size true uid used \
-    exec {f,}print{f,0,} ok prune ls'
+    exec {f,}print{f,0,} ok prune ls
 elif [[ CURRENT -eq 2 ]]; then
   local ret=1
 
@@ -19,7 +19,7 @@ elif [[ CURRENT -eq 2 ]]; then
 elif [[ "$prev" = -((a|c|)newer|fprint(|0|f)) ]]; then
   _files
 elif [[ "$prev" = -fstype ]]; then
-  compgen -k '(ufs 4.2 4.3 nfs tmp mfs S51K S52K)'
+  compadd ufs 4.2 4.3 nfs tmp mfs S51K S52K
 elif [[ "$prev" = -group ]]; then
   compgen -k groups
 elif [[ "$prev" = -user ]]; then
diff --git a/Completion/User/_groups b/Completion/User/_groups
new file mode 100644
index 000000000..975189174
--- /dev/null
+++ b/Completion/User/_groups
@@ -0,0 +1,6 @@
+#compdef newgrp
+
+: ${(A)groups:=${${(s: :)$(</etc/group)}%%:*}}
+# : ${(A)groups:=${${(s: :)$(ypcat group.byname)}%%:*}} # If you use NIS
+
+compadd $groups
diff --git a/Completion/User/_make b/Completion/User/_make
index 4770c0d7a..add58dbf0 100644
--- a/Completion/User/_make
+++ b/Completion/User/_make
@@ -1,4 +1,24 @@
 #compdef make gmake pmake
 
-compgen -s "\$(awk '/^[a-zA-Z0-9][^\/ 	]+:/ {print \$1}' FS=: [mM]akefile /dev/null)" ||
-_files
+local prev="$words[CURRENT-1]" file ret=1
+
+if [[ "$prev" = -[CI] ]]; then
+  _files -/
+elif [[ "$prev" = -[foW] ]]; then
+  _files
+else
+  file="$words[(I)-f]"
+  if (( file )); then
+    file="$words[file+1]"
+  elif [[ -e Makefile ]]; then
+    file=Makefile
+  elif [[ -e makefile ]]; then
+    file=makefile
+  else
+    file=''
+  fi
+
+  [[ -n "$file" ]] &&
+    compadd - $(awk '/^[a-zA-Z0-9][^/ 	]+:/ {print $1}' FS=: $file) && ret=0
+  (( ret )) && _files
+fi
diff --git a/Completion/User/_mh b/Completion/User/_mh
index e228d31b0..f03e3d827 100644
--- a/Completion/User/_mh
+++ b/Completion/User/_mh
@@ -23,7 +23,7 @@ if compset -P 1 -; then
     print $n =~ s/^\[([a-z]+)\]// ? "$n\n$1$n\n" : "$n\n";
   }')
   return
-elif compset -P 1 '[+@] || [ "$prev" = -draftfolder ]]; then
+elif compset -P 1 '[+@]' || [[ "$prev" = -draftfolder ]]; then
   # Complete folder names.
   local mhpath
 
@@ -39,7 +39,7 @@ elif compset -P 1 '[+@] || [ "$prev" = -draftfolder ]]; then
 elif [[ "$prev" = -(editor|(whatnow|rmm|show|more)proc) ]]; then
   compgen -c
 elif [[ "$prev" = -file ]]; then
-  compgen -f
+  _files
 elif [[ "$prev" = -(form|audit|filter) ]]; then
   # Need some MH template file, which may be in our own MH directory
   # or with the standard library.
@@ -69,7 +69,7 @@ else
     # leaving foldnam empty works here
   fi
 
-  compgen -s '$(mark $foldnam 2>/dev/null | awk -F: '\''{ print $1 }'\'')' &&
+  compadd $(mark $foldnam 2>/dev/null | awk -F: '{ print $1 }') &&
       ret=0
   compadd reply next cur prev first last all unseen && ret=0
   compgen -W folddir -g '<->' && ret=0
diff --git a/Completion/User/_rlogin b/Completion/User/_rlogin
index 9f5f3d628..b792ba0d1 100644
--- a/Completion/User/_rlogin
+++ b/Completion/User/_rlogin
@@ -3,7 +3,7 @@
 if [[ CURRENT -eq 2 ]]; then
   compgen -k hosts
 elif [[ CURRENT -eq 3 ]]; then
-  compgen -k '(-l)'
+  compadd - -l
 else
   compgen -u
 fi
diff --git a/Completion/User/_x_options b/Completion/User/_x_options
index cd45c87f6..a9c18b0d1 100644
--- a/Completion/User/_x_options
+++ b/Completion/User/_x_options
@@ -2,4 +2,8 @@
 
 # A simple pattern completion, just as an example.
 
-compgen -J options -k '(-display -name -xrm)'
+if [ "$words[CURRENT-1]" = "-display" ]; then
+  compgen -k hosts -S':0'
+else
+  compadd -J options - -display -name -xrm
+fi
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 21d3d6e24..741233926 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-22
-VERSION_DATE='June 13, 1999'
+VERSION=3.1.5-pws-24
+VERSION_DATE='June 26, 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 49c9dc26c..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,12 +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.
+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.
+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
@@ -842,7 +843,8 @@ findex(trap)
 cindex(signals, trapping)
 cindex(trapping signals)
 item(tt(trap) [ var(arg) [ var(sig) ... ] ])(
-var(arg) is a command to be read and executed when the shell
+var(arg) is a series of commands (usually quoted to protect it from
+immediate evaluation by the shell) to be read and executed when the shell
 receives var(sig).  Each var(sig) can be given as a number
 or as the name of a signal.
 If var(arg) is `tt(-)', then all traps var(sig) are reset to their
@@ -862,6 +864,20 @@ then the command var(arg) is executed when the shell terminates.
 
 The tt(trap) command with no arguments prints a list of commands
 associated with each signal.
+
+Note that traps defined with the tt(trap) builtin are slightly different
+from those defined as `tt(TRAP)var(NAL) () { ... }', as the latter have
+their own function environment (line numbers, local variables, etc.) while
+the former use the environment of the command in which they were called.
+For example,
+
+example(trap 'print $LINENO' DEBUG)
+
+will print the line number of command executed after it has run, while
+
+example(TRAPDEBUG() { print $LINENO; })
+
+will always print the number zero.
 )
 findex(true)
 cindex(doing nothing, successfully)
@@ -887,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))(
@@ -926,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
@@ -1174,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.
@@ -1269,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 48c7bf629..8f7c23f25 100644
--- a/Doc/Zsh/compctl.yo
+++ b/Doc/Zsh/compctl.yo
@@ -2,6 +2,24 @@ texinode(Programmable Completion Using compctl)(Completion Widgets)(Zsh Line Edi
 chapter(Programmable Completion Using compctl)
 cindex(completion, programmable)
 cindex(completion, controlling)
+ifzman(\
+sect(Synopsis)
+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
+zmanref(zshcompsys), and the basic shell mechanisms which support it are
+described in zmanref(zshcompwid).  This manual entry describes the older
+tt(compctl) command.
+)\
+ifnzman(\
+sect(Types of completion)
+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
+it are described in noderef(Completion Widgets).  This chapter describes
+the older tt(compctl) command.
+)\
 findex(compctl)
 sect(Description)
 startlist()
@@ -615,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 572660de3..9d6cc10ce 100644
--- a/Doc/Zsh/compsys.yo
+++ b/Doc/Zsh/compsys.yo
@@ -41,15 +41,15 @@ directory (or multiple directories) of your own which should appear in your
 tt($fpath) variable so that the functions can be autoloaded.
 
 startmenu()
-menu(Initialisation)
+menu(Initialization)
 menu(Control Functions)
 menu(Completion Functions)
 endmenu()
 
-texinode(Initialisation)(Control Functions)()(Completion System)
-sect(Initialisation)
+texinode(Initialization)(Control Functions)()(Completion System)
+sect(Initialization)
 
-The script tt(compinstall) can be run by a user to set up the completion
+The function 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
 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
@@ -59,13 +59,15 @@ So long as you keep them all together (including the comment lines at the
 start and finish), you can rerun tt(compinstall) and it will correctly
 locate and modify these lines.  Note, however, that any code you add to
 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 initialisation after the first time.  After
+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
 answer tt(n), you can rerun tt(compinstall) later without having to
 re-enter any of the basic settings.
@@ -73,36 +75,35 @@ re-enter any of the basic settings.
 You can abort the installation any time you are being prompted for
 information, and your tt(.zshrc) will not be altered at all.
 
+After initialization all the builtin completion widgets such as
+tt(expand-or-complete) will be redefined to use the new completion system.
+Should you need to, you can still bind keys to the old functions by putting
+a `tt(.)' in front, e.g. `tt(.expand-or-complete)'.
+
 subsect(Use of compinit)
 
 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 initialise 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).
+to do this is by adding the option tt(-d) whenever tt(compinit) is run.
+In this case 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 tt(-d).  On the next call to
+tt(compinit -d), the dumped file will be read instead.
+
+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.
 
 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
@@ -110,27 +111,30 @@ 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.
 
 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
@@ -158,7 +162,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
@@ -238,10 +246,10 @@ function, usable to be put in a setup script.
 )
 enditem()
 
-texinode(Control Functions)(Completion Functions)(Initialisation)(Completion System)
+texinode(Control Functions)(Completion Functions)(Initialization)(Completion System)
 sect(Control Functions)
 
-The initialisation script tt(compinit) re-binds all the keys which perform
+The initialization script tt(compinit) re-binds all the keys which perform
 completion to newly created widgets that all call the supplied widget
 function tt(_main_complete). This function acts as a wrapper calling
 the so-called `completer' functions that generate matches. If
@@ -382,7 +390,7 @@ with a numeric argument of six (as in `tt(ESC-6 TAB)'), up to six
 errors are accepted.  Hence with a value of `tt(0n)', no correcting
 completion will be attempted unless a numeric argument is given.
 
-If the value contains `tt(n)' or `tt(N)' and a exclamation mark
+If the value contains `tt(n)' or `tt(N)' and an exclamation mark
 (`tt(!)'), tt(_approximate) will var(not) try to generate corrected
 completions when given a numeric argument, so in this case the number given
 should be greater than zero.  For example, `tt(2n!)' specifies that
@@ -467,6 +475,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
@@ -652,7 +666,7 @@ one can write a pattern completion function that keeps other functions
 from being tried simply by setting this parameter to any value.
 )
 item(tt(_multi_parts))(
-This functions gets two arguments: a separator character and an
+This function gets two arguments: a separator character and an
 array.  As usual, the array may be either the
 name of an array parameter or a literal array in the form
 `tt(LPAR()foo bar)tt(RPAR())' (i.e. a list of words separated by white 
@@ -700,10 +714,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
diff --git a/Doc/Zsh/compwid.yo b/Doc/Zsh/compwid.yo
index 7f5ffe442..923323aa7 100644
--- a/Doc/Zsh/compwid.yo
+++ b/Doc/Zsh/compwid.yo
@@ -26,7 +26,8 @@ of the builtin widgets that handle completions can be given:
 tt(complete-word), tt(expand-or-complete),
 tt(expand-or-complete-prefix), tt(menu-complete),
 tt(menu-expand-or-complete), tt(reverse-menu-complete),
-tt(list-choices), or tt(delete-char-or-list).
+tt(list-choices), or tt(delete-char-or-list).  Note that this will still
+work even if the widget in question has been rebound.
 
 startmenu()
 menu(Special Parameters)
@@ -346,7 +347,7 @@ xitem(tt(compadd) [ tt(-qQfnUam) ] [ tt(-F) var(array) ])
 xitem([ tt(-P) var(prefix) ] [ tt(-S) var(suffix) ])
 xitem([ tt(-p) var(hidden-prefix) ] [ tt(-s) var(hidden-suffix) ])
 xitem([ tt(-i) var(ignored-prefix) ] [ tt(-I) var(ignored-suffix) ])
-xitem([ tt(-W) var(file-prefix) ])
+xitem([ tt(-W) var(file-prefix) ] [ tt(-y) var(array) ])
 xitem([ tt(-J) var(name) ] [ tt(-V) var(name) ] [ tt(-X) var(explanation) ])
 xitem([ tt(-r) var(remove-chars) ] [ tt(-R) var(remove-func) ])
 xitem([ tt(-M) var(match-spec) ] [ tt(-O) var(array) ] [ tt(-A) var(array) ])
@@ -406,6 +407,12 @@ match.
 item(tt(-I) var(ignored-suffix))(
 Like tt(-i), but gives an ignored suffix.
 )
+item(tt(-y) var(array))(
+This gives a number of string to display instead of the matches. This
+is like the tt(-y) option of the tt(compctl) builtin command but the
+var(array) argument may only be the name of an array parameter or a
+literal array in parentheses containing the strings to display.
+)
 item(tt(-J) var(name))(
 As for tt(compctl) and tt(compgen), this gives the name of the group
 of matches the words should be stored in.
diff --git a/Doc/Zsh/func.yo b/Doc/Zsh/func.yo
index 0b32acc4a..aa905b600 100644
--- a/Doc/Zsh/func.yo
+++ b/Doc/Zsh/func.yo
@@ -41,16 +41,16 @@ pindex(KSH_AUTOLOAD, use of)
 If the tt(KSH_AUTOLOAD) option is set, or the file contains only a simple
 definition of the function, the file's contents will be
 executed.  It would normally define the function in question, but may
-also perform initialisation.
+also perform initialization.
 It is executed in the context of the function
 execution, and may therefore define local parameters.
 
 Otherwise, the function is defined such that its body is the
 complete contents of the file.  This form allows the file to be
 used directly as an executable shell script.
-Initialisation code can be executed, but only as part of the first
+Initialization code can be executed, but only as part of the first
 function execution, so the function would have to redefine itself to
-avoid reinitialising on the next execution.
+avoid reinitializing on the next execution.
 
 If this processing of the file results in the function being
 fully defined, the function itself is then executed.
diff --git a/Doc/Zsh/guide.yo b/Doc/Zsh/guide.yo
index d1f3e3aa9..cc8fe0f7d 100644
--- a/Doc/Zsh/guide.yo
+++ b/Doc/Zsh/guide.yo
@@ -146,8 +146,10 @@ then be processed with bf(dvips) and optionally bf(gs) (Ghostscript) to
 produce a nicely formatted printed guide.
 )
 item(The HTML guide)(
-An HTML version of this guide is available at the Zsh web site via
+An HTML version of this guide is available at the Zsh web site via:
+
 tt(http://sunsite.auc.dk/zsh/Doc/index.html).
+
 (The HTML version is produced with bf(texi2html), which may be obtained
 from tt(http://wwwcn.cern.ch/dci/texi2html/). The command is
 `tt(texi2html -split_chapter -expandinfo zsh.texi)'.)
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
new file mode 100644
index 000000000..460d4a609
--- /dev/null
+++ b/Doc/Zsh/mod_complist.yo
@@ -0,0 +1,119 @@
+texinode(The complist Module)(The deltochar Module)(The compctl Module)(Zsh Modules)
+sect(The complist Module)
+cindex(completion, listing)
+The tt(complist) module offers two extensions to completion listings:
+the ability to highlight matches in such a list and a different
+style of menu-completion.
+
+subsect(Parameters)
+For both extensions one of the parameters tt(ZLS_COLORS) or tt(ZLS_COLOURS)
+must be set, even if the value is empty (which uses all the default values
+given below). These describe how matches are highlighted. The format of the
+value of these parameters is the same as used by the GNU version of the
+tt(ls) command: a colon-separated list of specifications of the form
+`var(name)=var(value)'. The var(name) may be one of the following strings,
+most of which specify file-types for which the var(value) will be used. The
+strings and their default values are:
+
+startitem()
+item(tt(no 0))(
+for normal text (not the string displayed for a match)
+)
+item(tt(fi 0))(
+for regular files
+)
+item(tt(di 32))(
+for directories
+)
+item(tt(ln 36))(
+for symbolic links
+)
+item(tt(pi 31))(
+for named pipes (FIFOs)
+)
+item(tt(so 33))(
+for sockets
+)
+item(tt(bd 44;37))(
+for block devices
+)
+item(tt(cd 44;37))(
+for character devices
+)
+item(tt(ex 35))(
+for executable files
+)
+item(tt(mi) var(none))(
+for names not naming a file (default is the value defined for tt(fi))
+)
+item(tt(lc \e[))(
+for the left code (see below)
+)
+item(tt(rc m))(
+for the right code
+)
+item(tt(ec) var(none))(
+for the end code
+)
+enditem()
+
+Apart from these strings, the var(name) may also be an asterisk
+(`tt(*)') followed by any string. The var(value) given for such a
+string will be used for all files whose name ends with the string.
+
+When printing a match, the code prints the value of tt(lc), the value
+for the file-type or the last matching specification with a `tt(*)',
+the value of tt(rc), the string to display for the match itself, and
+then the value of tt(ec) if that is defined or the values of tt(lc),
+tt(no), and tt(rc) if tt(ec) is not defined.
+
+The default values are ISO 6429 (ANSI) compliant and can be used on
+vt100 compatible terminals such as tt(xterm)s. On monochrome terminals
+the default values will have no visual effect.
+
+Whenever one of the parameters tt(ZLS_COLORS) or tt(ZLS_COLOURS) is set 
+and the tt(complist) module is loaded or linked into the shell,
+completion lists will be colored.
+
+subsect(Menu selection)
+The tt(complist) module also offers a different style of selecting
+matches from a list called menu-selection. It can be invoked directly by
+the widget tt(menu-select) defined by the module.  Alternatively,
+the parameter tt(SELECTMIN) can be set to an integer giving the minimum
+number of matches which must be present before menu selection is
+automatically turned on.  This second method requires that menu completion
+be started, either directly from a widget such as tt(menu-complete), or due
+to one of the options tt(MENU_COMPLETE) or tt(AUTO_MENU) being set.  If
+tt(SELECTMIN) is set, but is 0, 1 or empty, menu selection will always be
+started during menu completion if the completion is ambiguous.
+
+After menu-selection is started, the matches will be listed. The
+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.
+
+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
+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
+the completion functions will cycle to the next (or, in case of
+tt(reverse-menu-complete), the previous) match, and the tt(redisplay) and
+tt(clear-screen) functions work as usual without leaving
+menu-selection. Any other zle function leaves menu-selection and executes
+that function.
+
+During this selection the widget uses the keymap tt(menuselect). Any
+key that is not defined in this keymap or that is bound to
+tt(undefined-key) is looked up in the keymap currently selected. This
+is used to ensure that the most important keys used during selection
+have sensible default (namely the cursor keys, return, and TAB). However,
+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))\
+).
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 ad88c94c7..f29ab0fd6 100644
--- a/Doc/Zsh/mod_zle.yo
+++ b/Doc/Zsh/mod_zle.yo
@@ -67,7 +67,7 @@ with the var(new-keymap) name, it is deleted.
 item(tt(-N) var(new-keymap) [ var(old-keymap) ])(
 Create a new keymap, named var(new-keymap).  If a keymap already has that
 name, it is deleted.  If an var(old-keymap) name is given, the new keymap
-is initialised to be a duplicate of it, otherwise the new keymap will
+is initialized to be a duplicate of it, otherwise the new keymap will
 be empty.
 )
 enditem()
@@ -161,12 +161,13 @@ 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) ])
 xitem(tt(zle) tt(-C) var(widget) var(completion-widget) var(function))
-item(tt(zle) var(widget))(
+xitem(tt(zle) tt(-R) [ var(display-string) ])
+item(tt(zle) var(widget) tt([ -n) var(num) tt(]) tt([ -N ]) var(args) ...)(
 The tt(zle) builtin performs a number of different actions concerning
 ZLE.  Which operation it performs depends on its options:
 
@@ -175,7 +176,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.
@@ -191,7 +200,11 @@ Create a user-defined widget.  If there is already a widget with the
 specified name, it is overwritten.  When the new
 widget is invoked from within the editor, the specified shell var(function)
 is called.  If no function name is specified, it defaults to
-the same name as the widget.
+the same name as the widget.  For further information, see the section
+em(Widgets) in
+ifzman(zmanref(zshzle))\
+ifnzman(noderef(Zsh Line Editor))\
+.
 )
 item(tt(-C) var(widget) var(completion-widget) var(function))(
 Create a user-defined completion widget names var(widget). The 
@@ -203,9 +216,38 @@ ifzman(zmanref(zshcompwid))\
 ifnzman(noderef(Completion Widgets))\
 .
 )
-item(var(widget))(
+item(tt(-R) [ var(display-string) ])(
+Redisplay the command line; this is to be called from within a user-defined
+widget to allow changes to become visible.  If a var(display-string) is
+given, this is shown in the status line (immediately below the line being
+edited).
+)
+item(var(widget) tt([ -n) var(num) tt(]) tt([ -N ]) var(args) ...)(
 Invoke the specified widget.  This can only be done when ZLE is
 active; normally this will be within a user-defined widget.
+
+With the options tt(-n) and tt(-N), the current numerical argument will be
+saved and then restored after the call to tt(widget); `tt(-n) var(num)'
+sets the numerical argument temporarily to var(num), while `tt(-N)' sets it
+to the default, i.e. as if there were none.
+
+Any further arguments will be passed to the widget.  If it is a shell
+function, these are passed down as postional parameters; for builtin
+widgets it is up to the widget in question what it does with them.
+Currently arguments are only handled by the incremental-search commands,
+the tt(history-search-forward) and tt(-backward) and the corresponding
+functions prefixed by tt(vi-), and by tt(universal-argument).  No error is
+flagged if the command does not use the arguments, or only uses some of
+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.  
+
+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 472364d3e..265c5e184 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -34,6 +34,12 @@ Some of the single letter option names refer to an option being off,
 in which case the inversion of that name refers to the option being on.
 For example, `tt(PLUS()n)' is the short name of `tt(exec)', and
 `tt(-n)' is the short name of its inversion, `tt(noexec)'.
+
+In strings of single letter options supplied to the shell at startup,
+trailing whitespace will be ignored; for example the string `tt(-f    )'
+will be treated just as `tt(-f)', but the string `tt(-f i)' is an error.
+This is because many systems which implement the `tt(#!)' mechanism for
+calling scripts do not strip trailing whitespace.
 texinode(Description of Options)(Option Aliases)(Specifying Options)(Options)
 sect(Description of Options)
 cindex(options, description)
@@ -551,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)
@@ -572,6 +581,22 @@ A shell function can also guarantee itself a known shell configuration
 with a formulation like `tt(emulate -L zsh)'; the tt(-L) activates
 tt(LOCAL_OPTIONS).
 )
+pindex(LOCAL_TRAPS)
+item(tt(LOCAL_TRAPS))(
+If this option is set when a signal trap is set inside a function, then the
+previous status of the trap for that signal will be restored when the
+function exits.  Note that this option must be set em(prior) to altering the
+trap behaviour in a function; unlike tt(LOCAL_OPTIONS), the value on exit
+from the function is irrelevant.  However, it does not need to be set
+before any global trap for that to be correctly restored by a function.
+For example,
+
+example(unsetopt localtraps
+trap - INT
+fn() { setopt localtraps; trap '' INT; sleep 3; })
+
+will restore normally handling of tt(SIGINT) after the function exits.
+)
 pindex(LOGIN)
 item(tt(LOGIN) (tt(-l), ksh: tt(-l)))(
 This is a login shell.
diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index be820f160..9353ec977 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -26,7 +26,7 @@ In the parameter lists that follow, the mark `<S>' indicates that the
 parameter is special.
 Special parameters cannot have their type changed, and they stay special even
 if unset.  `<Z>' indicates that the parameter does not exist when the shell
-initialises in tt(sh) or tt(ksh) emulation mode.
+initializes in tt(sh) or tt(ksh) emulation mode.
 startmenu()
 menu(Array Parameters)
 menu(Positional Parameters)
@@ -316,7 +316,7 @@ as determined at compile time.
 )
 vindex(OLDPWD)
 item(tt(OLDPWD))(
-The previous working directory.  This is set when the shell initialises
+The previous working directory.  This is set when the shell initializes
 and whenever the directory changes.
 )
 vindex(OPTARG)
@@ -339,7 +339,7 @@ The process ID of the parent of the shell.
 )
 vindex(PWD)
 item(tt(PWD))(
-The present working directory.  This is set when the shell initialises
+The present working directory.  This is set when the shell initializes
 and whenever the directory changes.
 )
 vindex(RANDOM)
@@ -592,7 +592,7 @@ item(tt(module_path) <S> <Z> (tt(MODULE_PATH) <S>))(
 An array (colon-separated list)
 of directories that tt(zmodload)
 searches for dynamically loadable modules.
-This is initialised to a standard pathname,
+This is initialized to a standard pathname,
 usually `tt(/usr/local/lib/zsh/$ZSH_VERSION)'.
 (The `tt(/usr/local/lib)' part varies from installation to installation.)
 For security reasons, any value set in the environment when the shell
@@ -858,6 +858,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/zftpsys.yo b/Doc/Zsh/zftpsys.yo
index 60f5be60d..d806a49b5 100644
--- a/Doc/Zsh/zftpsys.yo
+++ b/Doc/Zsh/zftpsys.yo
@@ -47,7 +47,7 @@ two letters `tt(zf)'.  They may already have been installed on your system;
 otherwise, you will need to find them and copy them.  The directory should
 appear as one of the elements of the tt($fpath) array (this should already
 be the case if they were installed), and at least the function tt(zfinit)
-should be autoloaded; it will autoload the rest.  Finally, to initialise
+should be autoloaded; it will autoload the rest.  Finally, to initialize
 the use of the system you need to call the tt(zfinit) function.  The
 following code in your tt(.zshrc) will arrange for this; assume the
 functions are stored in the directory tt(~/myfns):
@@ -59,7 +59,7 @@ zfinit)
 Note that tt(zfinit) assumes you are using the tt(zmodload) method to
 load the tt(zftp) command.  If it is already built into the shell, change
 tt(zfinit) to tt(zfinit -n).  It is helpful (though not essential) if the
-call to tt(zfinit) appears after any code to initialise the new completion
+call to tt(zfinit) appears after any code to initialize the new completion
 system, else unnecessary tt(compctl) commands will be given.
 
 texinode(Zftp Functions)(Miscellaneous Features)(Installation)(Zftp Function System)
@@ -324,7 +324,7 @@ alter tt(zftp_chpwd) and tt(zftp_progress), in particular.
 
 startitem()
 item(tt(zfinit [ -n ]))(
-As described above, this is used to initialise the zftp function system.
+As described above, this is used to initialize the zftp function system.
 The tt(-n) option should be used if the zftp command is already built into
 the shell.
 )
diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo
index 743fb7f93..ce1b27fec 100644
--- a/Doc/Zsh/zle.yo
+++ b/Doc/Zsh/zle.yo
@@ -102,7 +102,8 @@ cindex(widgets, user-defined)
 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.
-Finally, they can examine and edit the ZLE buffer being edited by
+They can use tt(read -k) or tt(read -q) to read characters from standard
+input.  Finally, they can examine and edit the ZLE buffer being edited by
 reading and setting the special parameters described below.
 
 cindex(parameters, editor)
@@ -162,7 +163,11 @@ and meta-keys are reported with a leading `tt(M-)', as in `tt(M-a)' and
 )
 vindex(NUMERIC)
 item(tt(NUMERIC) (integer))(
-The numeric argument.
+The numeric argument. If no numeric argument was given, this parameter
+is unset. When this is set inside a widget function, builtin widgets
+called with the tt(zle) builtin command will use the value
+assigned. If it is unset inside awidget function, builtin widgets
+called behave as if no numeric argument was given.
 )
 vindex(HISTNO)
 item(tt(HISTNO) (integer))(
@@ -346,6 +351,10 @@ item(tt(down-line-or-search))(
 Move down a line in the buffer, or if already at the bottom line,
 search forward in the history for a line beginning with the first
 word in the buffer.
+
+If called from a function by the tt(zle) command with arguments, the first
+argument is taken as the string for which to search, rather than the
+first word in the buffer.
 )
 tindex(down-history)
 item(tt(down-history) (unbound) (^N) (unbound))(
@@ -414,6 +423,17 @@ search, leaving the last found line in the buffer. Any single character that
 is not bound to one of the above functions, or tt(self-insert) or
 tt(self-insert-unmeta), will have the same effect but the function will be
 executed.
+
+When called from a widget function by the tt(zle) command, the incremental
+search commands can take a string argument.  This will be treated as a
+string of keys, as for arguments to the tt(bindkey) command, and used as
+initial input for the command.  Any characters in the string which are
+unused by the incremental search will be silently ignored.  For example,
+
+example(zle history-incremental-search-backward forceps)
+
+will search backwards for tt(forceps), leaving the minibuffer containing
+the string `tt(forceps)'.
 )
 tindex(history-incremental-search-forward)
 item(tt(history-incremental-search-forward) (^S ^Xs) (unbound) (unbound))(
@@ -427,6 +447,10 @@ tindex(history-search-backward)
 item(tt(history-search-backward) (ESC-P ESC-p) (unbound) (unbound))(
 Search backward in the history for a line beginning with the first
 word in the buffer.
+
+If called from a function by the tt(zle) command with arguments, the first
+argument is taken as the string for which to search, rather than the
+first word in the buffer.
 )
 tindex(vi-history-search-backward)
 item(tt(vi-history-search-backward) (unbound) (/) (unbound))(
@@ -454,18 +478,27 @@ tt(magic-space) is treated as a space.
 Any other character that is not bound to self-insert or
 self-insert-unmeta will beep and be ignored. If the function is called from vi
 command mode, the bindings of the current insert mode will be used.
+
+If called from a function by the tt(zle) command with arguments, the first
+argument is taken as the string for which to search, rather than the
+first word in the buffer.
 )
 tindex(history-search-forward)
 item(tt(history-search-forward) (ESC-N ESC-n) (unbound) (unbound))(
 Search forward in the history for a line beginning with the first
 word in the buffer.
+
+If called from a function by the tt(zle) command with arguments, the first
+argument is taken as the string for which to search, rather than the
+first word in the buffer.
 )
 tindex(vi-history-search-forward)
 item(tt(vi-history-search-forward) (unbound) (?) (unbound))(
 Search forward in the history for a specified string.
 The string may begin with `tt(^)' to anchor the search to the
 beginning of the line. The functions available in the mini-buffer are the same
-as for tt(vi-history-search-backward).
+as for tt(vi-history-search-backward).  Argument handling is also the same
+as for that command.
 )
 tindex(infer-next-history)
 item(tt(infer-next-history) (^X^N) (unbound) (unbound))(
@@ -504,6 +537,10 @@ item(tt(up-line-or-search))(
 Move up a line in the buffer, or if already at the top line,
 search backward in the history for a line beginning with the
 first word in the buffer.
+
+If called from a function by the tt(zle) command with arguments, the first
+argument is taken as the string for which to search, rather than the
+first word in the buffer.
 )
 tindex(up-history)
 item(tt(up-history) (unbound) (^P) (unbound))(
@@ -800,7 +837,11 @@ startitem()
 tindex(digit-argument)
 item(tt(digit-argument) (ESC-0..ESC-9) (1-9) (unbound))(
 Start a new numeric argument, or add to the current one.
-See also tt(vi-digit-or-beginning-of-line).
+See also tt(vi-digit-or-beginning-of-line).  This only works if bound to a
+key sequence ending in a decimal digit.
+
+Inside a widget function, a call to this function treats the last key of
+the key sequence which called the widget as the digit.
 )
 tindex(neg-argument)
 item(tt(neg-argument) (ESC--) (unbound) (unbound))(
@@ -815,6 +856,10 @@ repeated using this command.  For example, if this command occurs
 twice, followed immediately by tt(forward-char), move forward sixteen
 spaces; if instead it is followed by tt(-2), then tt(forward-char),
 move backward two spaces.
+
+Inside a widget function, if passed an argument, i.e. `tt(zle
+universal-argument) var(num)', the numerical argument will be set to
+var(num); this is equivalent to `tt(NUMERIC=)var(num)'.
 )
 enditem()
 texinode(Completion)(Miscellaneous)(Arguments)(Zsh Line Editor)
@@ -911,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/Doc/zsh.yo b/Doc/zsh.yo
index b9279e660..d59afff7d 100644
--- a/Doc/zsh.yo
+++ b/Doc/zsh.yo
@@ -15,12 +15,10 @@ ifnzman(\
 def(ifzshone)(1)()\
 def(ifzshall)(1)()\
 )\
-ifztexi(\
+ifztexi(
 texinfo(zsh.info)(zsh)
-NOTRANS(@setchapternewpage off
-@iftex
+NOTRANS(@iftex
 @finalout
-@afourpaper
 @end iftex)
 texititlepage()
 texititle(The Z Shell Guide)
diff --git a/Doc/ztexi.yo b/Doc/ztexi.yo
index c3c1928cf..971947ac6 100644
--- a/Doc/ztexi.yo
+++ b/Doc/ztexi.yo
@@ -11,7 +11,6 @@ def(CMT)(0)(NOTRANS(@c))
 
 ATEXIT(\
   NL()\
-  NOTRANS(@setchapternewpage off)NL()\
   NOTRANS(@contents)NL()\
   NOTRANS(@bye)NL()\
 )
@@ -47,6 +46,10 @@ def(texinfo)(2)(\
   STDPAR()\
   NOTRANS(\input texinfo.tex)NL()\
   NOTRANS(@c %**start of header)NL()\
+  NOTRANS(@iftex)NL()\
+  NOTRANS(@afourpaper)NL()\
+  NOTRANS(@setchapternewpage off)NL()\
+  NOTRANS(@end iftex)NL()\
   NOTRANS(@setfilename )ARG1NL()\
   NOTRANS(@settitle )ARG2NL()\
   NOTRANS(@c %**end of header)NL()\
diff --git a/Etc/FAQ.yo b/Etc/FAQ.yo
index 0ba251f92..31ab71be4 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.4 1999/05/31 17:10:29 akr Exp $
+mydit(Version:) $Id: FAQ.yo,v 1.1.1.5 1999/06/27 05:33:49 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/Functions/Zftp/zfgoto b/Functions/Zftp/zfgoto
index e69de29bb..8d6f00a3a 100644
--- a/Functions/Zftp/zfgoto
+++ b/Functions/Zftp/zfgoto
@@ -0,0 +1,86 @@
+# zfgoto bname
+# Go to bookmark bname, a location on a remote FTP host.  Unless
+# this was the last session or is for anonymous FTP, prompt for
+# the user's password.
+
+emulate -L zsh
+setopt extendedglob
+
+# Set ZFTP_BMFILE if not already set.  This should agree with
+# the corresponding line in zfmark.
+: ${ZFTP_BMFILE:=${ZFDOTDIR:-$HOME}/.zfbkmarks}
+
+typeset -A bkmarks
+local line ncftp opt optlist
+
+while [[ $1 == -* ]]; do
+  if [[ $1 == - || $1 == -- ]]; then
+    shift;
+    break;
+  fi
+  optlist=${1#-}
+  for (( i = 1; i <= $#optlist; i++)); do
+    opt=$optlist[$i]
+    case $opt in
+      n) ncftp=1
+	 ;;
+      *) print option $opt not recognised >&2
+	 return 1
+	 ;;
+    esac
+  done
+  shift
+done
+
+if (( $# != 1 )); then
+  print "Usage: zfgoto bookmark" >&2
+  return 1
+fi
+
+if [[ -n $ncftp && -f ~/.ncftp/bookmarks ]]; then
+  local oldifs=$IFS
+  IFS=,
+  while read -rA line; do
+    bkmarks[$line[1]]="${line[3]:-anonymous}@${line[2]}:${line[6]}"
+  done < ~/.ncftp/bookmarks
+  IFS=$oldifs
+elif [[ -f $ZFTP_BMFILE ]]; then
+  # read in file:  could optimise this by recording last read time
+  # comparing with file
+  while read -r line; do
+    # ignore blank and comment lines
+    [[ $line = [[:blank:]]# || $line = [[:blank:]]#'#'* ]] && continue
+    bkmarks[${line%% *}]="${line#* }"
+  done <$ZFTP_BMFILE
+fi
+
+line=${bkmarks[$1]}
+
+if [[ -z $line ]]; then
+  print "Bookmark \`$1' not found" >&2
+  return 1
+fi
+
+local user host dir
+user=${line%%@*}
+line=${line#*@}
+host=${line%%:*}
+dir=${line#*:}
+
+if [[ $user = ftp || $user = anonymous ]]; then
+  # Anonymous ftp, so we don't need password etc.
+  zfanon $host && [[ -n $dir ]] && zfcd $dir
+elif [[ $zflastsession = ${host}:* && $user = $zflastuser ]]; then
+  # This was the last session, so assume it's still setup in the
+  # open parameters
+  zfopen && [[ -n $dir ]] && zfcd $dir
+else
+  # Last try: see if it's in the parameters.
+  local params
+  params=($(zftp params))
+  if [[ $host = $params[1] && $user = $params[2] ]]; then
+    zfopen && [[ -n $dir ]] && zfcd $dir
+  else
+    zfopen $host $user && [[ -n $dir ]] && zfcd $dir
+  fi
+fi
diff --git a/Functions/Zftp/zfmark b/Functions/Zftp/zfmark
index e69de29bb..8d35ce45a 100644
--- a/Functions/Zftp/zfmark
+++ b/Functions/Zftp/zfmark
@@ -0,0 +1,49 @@
+# zfmark [bname]
+# Set a bookmark for the current zftp connection, or use the
+# information about the last session if there isn't one.
+# A bookmark includes both the host *and* the directory on that host.
+#
+# Without bname, list the current bookmarks and their locations.
+
+emulate -L zsh
+setopt extendedglob
+
+# Set ZFTP_BMFILE if not already set.  This should agree with
+# the corresponding line in zfgoto.
+: ${ZFTP_BMFILE:=${ZDOTDIR:-$HOME}/.zfbkmarks}
+
+typeset -A bkmarks
+local line
+
+if [[ -f $ZFTP_BMFILE ]]; then
+  # read in file:  could optimise this by recording last read time
+  # comparing with file
+  while read -r line; do
+    # ignore blank and comment lines
+    [[ $line = [[:blank:]]# || $line = [[:blank:]]#'#'* ]] && continue
+    bkmarks[${line%% *}]="${line#* }"
+  done <$ZFTP_BMFILE
+fi
+
+if (( $# == 0 )); then
+  for line in ${(ko)bkmarks}; do
+    print -r- "$line ${bkmarks[$line]}"
+  done
+  return 0
+elif (( $# > 1 )); then
+  print "Usage: zfmark [bookmark]" >&2
+  return 1
+fi
+
+if [[ -n $ZFTP_HOST ]]; then
+  bkmarks[$1]="${ZFTP_USER}@${ZFTP_HOST}:${ZFTP_PWD}"
+elif [[ -n $zflastsession ]]; then
+  bkmarks[$1]="${zflastuser}@${zflastsession}"
+else
+  print "No current or recent ZFTP session to bookmark." >&2
+  return 1
+fi
+
+for line in ${(ko)bkmarks}; do
+  print -r- "$line ${bkmarks[$line]}"
+done >$ZFTP_BMFILE
diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c
index 4cb7b0aba..908a05ce9 100644
--- a/Src/Modules/parameter.c
+++ b/Src/Modules/parameter.c
@@ -326,7 +326,7 @@ setfunction(char *name, char *val)
     val = metafy(val, strlen(val), META_REALLOC);
 
     HEAPALLOC {
-	list = parse_string(val);
+	list = parse_string(val, 1);
     } LASTALLOC;
 
     if (!list || list == &dummy_list) {
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 bf3ea816f..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
@@ -286,6 +300,7 @@ struct cadata {
     char *pre;			/* prefix to insert (-P) */
     char *suf;			/* suffix to insert (-S) */
     char *group;		/* name of the group (-[JV]) */
+    char *ylist;		/* display list (-y) */
     char *rems;			/* remove suffix on chars... (-r) */
     char *remf;			/* function to remove suffix (-R) */
     char *ign;			/* ignored suffixes (-F) */
@@ -298,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 c5b24714e..2b426492e 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -1727,7 +1727,7 @@ bin_compadd(char *name, char **argv, char *ops, int func)
     }
     dat.ipre = dat.isuf = dat.ppre = dat.psuf = dat.prpre =
 	dat.pre = dat.suf = dat.group = dat.rems = dat.remf =
-	dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = NULL;
+	dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = dat.ylist = NULL;
     dat.match = NULL;
     dat.flags = 0;
     dat.aflags = CAF_MATCH;
@@ -1779,6 +1779,10 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 		sp = &(dat.group);
 		e = "group name expected after -%c";
 		break;
+	    case 'y':
+		sp = &(dat.ylist);
+		e = "string expected after -%c";
+		break;
 	    case 'i':
 		sp = &(dat.ipre);
 		e = "string expected after -%c";
@@ -2217,13 +2221,14 @@ 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");
 
 	*pp = pm;
-	pm->level = locallevel;
+	pm->level = locallevel + 1;
 	if ((pm->u.data = cp->var)) {
 	    switch(PM_TYPE(cp->type)) {
 	    case PM_SCALAR:
@@ -2257,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
new file mode 100644
index 000000000..63bc4c6e3
--- /dev/null
+++ b/Src/Zle/complist.c
@@ -0,0 +1,918 @@
+/*
+ * complist.c - completion listing enhancements
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1999 Sven Wischnowsky
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Sven Wischnowsky or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Sven Wischnowsky and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Sven Wischnowsky and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose.  The software
+ * provided hereunder is on an "as is" basis, and Sven Wischnowsky and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "complist.mdh"
+#include "complist.pro"
+
+
+/* We use the parameters ZLS_COLORS and ZLS_COLOURS in the same way as
+ * the color ls does. It's just that we don't support the `or' file
+ * type. */
+
+
+static Widget w_menuselect;
+static Keymap mskeymap;
+
+/* Indixes into the terminal string arrays. */
+
+#define COL_NO  0
+#define COL_FI  1
+#define COL_DI  2
+#define COL_LN  3
+#define COL_PI  4
+#define COL_SO  5
+#define COL_BD  6
+#define COL_CD  7
+#define COL_EX  8
+#define COL_MI  9
+#define COL_LC 10
+#define COL_RC 11
+#define COL_EC 12
+#define COL_MA 13
+
+#define NUM_COLS 14
+
+/* Names of the terminal strings. */
+
+static char *colnames[] = {
+    "no", "fi", "di", "ln", "pi", "so", "bd", "cd", "ex", "mi",
+    "lc", "rc", "ec", "ma", NULL
+};
+
+/* Default values. */
+
+static char *defcols[] = {
+    "0", "0", "1;34", "1;36", "33", "1;35", "1;33", "1;33", "1;32", NULL,
+    "\033[", "m", NULL, "7"
+};
+
+/* This describes a terminal string for a filename extension. */
+
+typedef struct extcol *Extcol;
+
+struct extcol {
+    char *ext;			/* the extension */
+    char *col;			/* the terminal color string */
+    Extcol next;		/* the next one in the list */
+};
+
+/* This holds all terminal strings. */
+
+typedef struct listcols *Listcols;
+
+struct listcols {
+    char *cols[NUM_COLS];	/* strings for file types */
+    Extcol exts;		/* strings for extensions */
+};
+
+/* This parses the value of a definition (the part after the `=').
+ * The return value is a pointer to the character after it. */
+
+static char *
+getcolval(char *s)
+{
+    char *p;
+
+    for (p = s; *s && *s != ':'; p++, s++) {
+	if (*s == '\\' && s[1]) {
+	    switch (*++s) {
+	    case 'a': *p = '\007'; break;
+	    case 'n': *p = '\n'; break;
+	    case 'b': *p = '\b'; break;
+	    case 't': *p = '\t'; break;
+	    case 'v': *p = '\v'; break;
+	    case 'f': *p = '\f'; break;
+	    case 'r': *p = '\r'; break;
+	    case 'e': *p = '\033'; break;
+	    case '_': *p = ' '; break;
+	    case '?': *p = '\177'; break;
+	    default:
+		if (*s >= '0' && *s <= '7') {
+		    int i = STOUC(*s);
+
+		    if (*++s >= '0' && *s <= '7') {
+			i = (i * 8) + STOUC(*s);
+			if (*++s >= '0' && *s <= '7')
+			    i = (i * 8) + STOUC(*s);
+		    }
+		    *p = (char) i;
+		} else
+		    *p = *s;
+	    }
+	} else if (*s == '^') {
+	    if ((s[1] >= '@' && s[1] <= '_') ||
+		(s[1] >= 'a' && s[1] <= 'z'))
+		*p = (char) (STOUC(*s) & ~0x60);
+	    else if (s[1] == '?')
+		*p = '\177';
+	    else {
+		*p++ = *s;
+		*p = s[1];
+	    }
+	    s++;
+	} else
+	    *p = *s;
+    }
+    if (p != s)
+	*p = '\0';
+    return s;
+}
+
+/* This parses one definition. Return value is a pointer to the
+ * character after it. */
+
+static char *
+getcoldef(Listcols c, char *s)
+{
+    if (*s == '*') {
+	Extcol ec;
+	char *n, *p;
+
+	/* This is for an extension. */
+
+	n = ++s;
+	while (*s && *s != '=')
+	    s++;
+	if (!*s )
+	    return s;
+	*s++ = '\0';
+	p = getcolval(s);
+	if (*n) {
+	    ec = (Extcol) zhalloc(sizeof(*ec));
+	    ec->ext = n;
+	    ec->col = s;
+	    ec->next = c->exts;
+	    c->exts = ec;
+	}
+	if (*p)
+	    *p++ = '\0';
+	return p;
+    } else {
+	char *n = s, *p, **nn;
+	int i;
+
+	/* This is for a file type. */
+
+	while (*s && *s != '=')
+	    s++;
+	if (!*s)
+	    return s;
+	*s++ = '\0';
+	for (i = 0, nn = colnames; *nn; i++, nn++)
+	    if (!strcmp(n ,*nn))
+		break;
+	p = getcolval(s);
+	if (*nn)
+	    c->cols[i] = s;
+	if (*p)
+	    *p++ = '\0';
+	return p;
+    }
+}
+
+/* This initializes the given terminal color structure. */
+
+static int
+getcols(Listcols c)
+{
+    char *s;
+    int i;
+
+    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)
+	return 0;
+    /* We have one of the parameters, use it. */
+    memset(c, 0, sizeof(*c));
+    s = dupstring(s);
+    while (*s)
+	s = getcoldef(c, s);
+
+    /* Use default values for those that aren't set explicitly. */
+    for (i = 0; i < NUM_COLS; i++)
+	if (!c->cols[i])
+	    c->cols[i] = defcols[i];
+    /* Default for missing files. */
+    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);
+
+	/* 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;
+    }
+    return 0;
+}
+
+/* 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)
+{
+    Extcol e;
+
+    for (e = c->exts; e; e = e->next)
+	if (strsfx(e->ext, n))
+	    return e->col;
+
+    if (S_ISDIR(m))
+	return c->cols[COL_DI];
+    else if (S_ISLNK(m))
+	return c->cols[COL_LN];
+    else if (S_ISFIFO(m))
+	return c->cols[COL_PI];
+    else if (S_ISSOCK(m))
+	return c->cols[COL_SO];
+    else if (S_ISBLK(m))
+	return c->cols[COL_BD];
+    else if (S_ISCHR(m))
+	return c->cols[COL_CD];
+    else if (S_ISREG(m) && (m & S_IXUGO))
+	return c->cols[COL_EX];
+
+    return c->cols[COL_FI];
+}
+
+/* Information about the list shown. */
+
+static int noselect, mselect, inselect, mcol, mline, mcols, mlines;
+static Cmatch *mmatch, **mtab;
+static Cmgroup mgroup, *mgtab;
+
+/* List the matches. Most of this is just taken from ilistmatches(),
+ * of course. */
+
+static int
+complistmatches(Hookdef dummy, Chdata dat)
+{
+    Cmgroup amatches = dat->matches, g;
+    Cmatch *p, m;
+    Cexpl *e;
+    int nlines = 0, ncols, nlist = 0, longest = 1, pnl = 0, opl = 0;
+    int of = isset(LISTTYPES);
+    int mc, ml = 0, cc, hasm = 0;
+    struct listcols col;
+
+    if (minfo.asked == 2) {
+	showinglist = 0;
+	return (noselect = 1);
+    }
+    getcols(&col);
+
+    /* Set the cursor below the prompt. */
+    if (inselect)
+	clearflag = 0;
+    trashzle();
+    showinglist = listshown = 0;
+
+    clearflag = (isset(USEZLE) && !termflags &&
+		 complastprompt && *complastprompt);
+
+    for (g = amatches; g; g = g->next) {
+	char **pp = g->ylist;
+	int nl = 0, l;
+
+	if (pp) {
+	    /* We have an ylist, lets see, if it contains newlines. */
+	    while (!nl && *pp)
+		nl = !!strchr(*pp++, '\n');
+
+	    pp = g->ylist;
+	    if (nl) {
+		/* Yup, there are newlines, count lines. */
+		char *nlptr, *sptr;
+
+		g->flags |= CGF_LINES;
+		noselect = 1;
+		while ((sptr = *pp)) {
+		    while (sptr && *sptr) {
+			nlines += (nlptr = strchr(sptr, '\n'))
+			    ? 1 + (nlptr-sptr)/columns
+			    : strlen(sptr)/columns;
+			sptr = nlptr ? nlptr+1 : NULL;
+		    }
+		    nlines++;
+		    pp++;
+		}
+		nlines--;
+	    } else {
+		while (*pp) {
+		    if ((l = strlen(*pp)) > longest)
+			longest = l;
+		    nlist++;
+		    pp++;
+		}
+	    }
+	} else {
+	    for (p = g->matches; (m = *p); p++) {
+		if (!(m->flags & CMF_NOLIST)) {
+		    if ((l = niceztrlen(m->str)) > longest)
+			longest = l;
+		    nlist++;
+		} else
+		    noselect = 1;
+	    }
+	}
+	if ((e = g->expls)) {
+	    while (*e) {
+		if ((*e)->count)
+		    nlines += 1 + printfmt((*e)->str, (*e)->count, 0);
+		e++;
+	    }
+	}
+    }
+    longest += 2 + of;
+    if ((ncols = (columns + 1) / longest)) {
+	for (g = amatches; g; g = g->next)
+	    nlines += (g->lcount + ncols - 1) / ncols;
+    } else {
+	ncols = 1;
+	opl = 1;
+	for (g = amatches; g; g = g->next) {
+	    char **pp = g->ylist;
+
+	    if (pp) {
+		if (!(g->flags & CGF_LINES)) {
+		    while (*pp) {
+			nlines += 1 + (strlen(*pp) / columns);
+			pp++;
+		    }
+		}
+	    } else
+		for (p = g->matches; (m = *p); p++)
+		    if (!(m->flags & CMF_NOLIST))
+			nlines += 1 + ((1 + niceztrlen(m->str)) / columns);
+	}
+    }
+
+    /* Maybe we have to ask if the user wants to see the list. */
+    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);
+	fflush(shout);
+	if (getzlequery() != 'y') {
+	    if (clearflag) {
+		putc('\r', shout);
+		tcmultout(TCUP, TCMULTUP, qup);
+		if (tccan(TCCLEAREOD))
+		    tcout(TCCLEAREOD);
+		tcmultout(TCUP, TCMULTUP, nlnct);
+	    } else
+		putc('\n', shout);
+	    noselect = 1;
+	    if (minfo.cur)
+		minfo.asked = 2;
+	    return 1;
+	}
+	if (clearflag) {
+	    putc('\r', shout);
+	    tcmultout(TCUP, TCMULTUP, qup);
+	    if (tccan(TCCLEAREOD))
+		tcout(TCCLEAREOD);
+	} else
+	    putc('\n', shout);
+	settyinfo(&shttyinfo);
+	if (minfo.cur)
+	    minfo.asked = 1;
+    }
+    if (mselect >= 0) {
+	int i;
+
+	i = ncols * nlines;
+	free(mtab);
+	mtab = (Cmatch **) zalloc(i * sizeof(Cmatch **));
+	memset(mtab, 0, i * sizeof(Cmatch **));
+	free(mgtab);
+	mgtab = (Cmgroup *) zalloc(i * sizeof(Cmgroup));
+	memset(mgtab, 0, i * sizeof(Cmgroup));
+	mcols = ncols;
+	mlines = nlines;
+    }
+    /* Now print the matches. */
+    g = amatches;
+    while (g) {
+	char **pp = g->ylist;
+
+	if ((e = g->expls)) {
+	    while (*e) {
+		if ((*e)->count) {
+		    if (pnl) {
+			putc('\n', shout);
+			pnl = 0;
+			ml++;
+		    }
+		    ml += printfmt((*e)->str, (*e)->count, 1);
+		    pnl = 1;
+		}
+		e++;
+	    }
+	}
+	if (pp && *pp) {
+	    if (pnl) {
+		putc('\n', shout);
+		pnl = 0;
+		ml++;
+	    }
+	    if (g->flags & CGF_LINES) {
+		while (*pp) {
+		    zputs(*pp, shout);
+		    if (*++pp)
+			putc('\n', shout);
+		}
+	    } else {
+		int n = g->lcount, nl = (n + ncols - 1) / ncols, nc = nl, i, a;
+		char **pq;
+
+		while (n && nl--) {
+		    i = ncols;
+		    mc = 0;
+		    pq = pp;
+		    while (n && i--) {
+			if (pq - g->ylist >= g->lcount)
+			    break;
+			zputs(*pq, shout);
+			if (i) {
+			    a = longest - strlen(*pq);
+			    while (a--)
+				putc(' ', shout);
+			}
+			pq += nc;
+			n--;
+		    }
+		    if (n) {
+			putc('\n', shout);
+			ml++;
+		    }
+		    pp++;
+		}
+	    }
+	} else if (g->lcount) {
+	    int n = g->lcount, nl = (n + ncols - 1) / ncols, nc = nl, i, j, a = 0;
+	    int zt;
+	    Cmatch *q;
+
+	    if (n && pnl) {
+		putc('\n', shout);
+		pnl = 0;
+		ml++;
+	    }
+	    for (p = skipnolist(g->matches); n && nl--;) {
+		i = ncols;
+		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);
+			a = longest - 2;
+			while (a--)
+			    putc(' ', shout);
+			fputs(col.cols[COL_EC], shout);
+			break;
+		    }
+		    hasm = 1;
+		    if (mselect >= 0) {
+			mtab[mc + (ncols * ml)] = q;
+			mgtab[mc + (ncols * ml)] = g;
+		    }
+		    if (m->gnum == mselect) {
+			mcol = mc;
+			mline = ml;
+			mmatch = q;
+			mgroup = g;
+			cc = COL_MA;
+		    } else
+			cc = -1;
+		    if (m->flags & CMF_FILE) {
+			struct stat buf;
+			char *pb;
+
+			pb = (char *) zhalloc((m->prpre ? strlen(m->prpre) : 0) +
+					     3 + strlen(m->str));
+			sprintf(pb, "%s%s", (m->prpre ? m->prpre : "./"),
+				m->str);
+
+			zt = ztat(pb, &buf, 1);
+			if (cc >= 0)
+			    fputs(col.cols[cc], shout);
+			else if (zt)
+			    fputs(col.cols[COL_NO], shout);
+			else
+			    fputs(getcolstr(&col, pb, buf.st_mode), shout);
+			fputs(col.cols[COL_RC], shout);
+			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);
+			nicezputs(m->str, shout);
+			if (of)
+			    putc(' ', shout);
+		    }
+		    a = longest - niceztrlen(m->str) - 2 - of;
+		    while (a--)
+			putc(' ', shout);
+		    fputs(col.cols[COL_EC], shout);
+		    if (i) {
+			fputs(col.cols[COL_LC], shout);
+			fputs(col.cols[COL_NO], shout);
+			fputs(col.cols[COL_RC], shout);
+			fputs("  ", shout);
+			fputs(col.cols[COL_EC], shout);
+		    }
+		    if (--n)
+			for (j = nc; j && *q; j--)
+			    q = skipnolist(q + 1);
+		    mc++;
+		}
+		if (i > 0) {
+		    fputs(col.cols[COL_LC], shout);
+		    fputs(col.cols[COL_MI], shout);
+		    fputs(col.cols[COL_RC], shout);
+		    a = longest - 2;
+		    while (a--)
+			putc(' ', shout);
+		    fputs(col.cols[COL_EC], shout);
+		}
+		if (n) {
+		    putc('\n', shout);
+		    ml++;
+		    if (n && nl)
+			p = skipnolist(p + 1);
+		}
+	    }
+	}
+	if (g->lcount)
+	    pnl = 1;
+	g = g->next;
+    }
+
+    if (clearflag) {
+	/* Move the cursor up to the prompt, if always_last_prompt *
+	 * is set and all that...                                  */
+	if ((nlines += nlnct - 1) < lines) {
+	    tcmultout(TCUP, TCMULTUP, nlines);
+	    showinglist = -1;
+	    listshown = 1;
+	} else
+	    clearflag = 0, putc('\n', shout);
+    } else
+	putc('\n', shout);
+    if (!hasm || nlines >= lines)
+	noselect = 1;
+    return noselect;
+}
+
+typedef struct menustack *Menustack;
+
+struct menustack {
+    Menustack prev;
+    char *line;
+    int cs;
+    struct menuinfo info;
+};
+
+static int
+domenuselect(Hookdef dummy, Chdata dat)
+{
+    Cmatch **p;
+    Cmgroup *pg;
+    Thingy cmd;
+    Menustack u = NULL;
+    int i = 0;
+    char *s;
+
+    if (getcols(NULL) || (dummy && (!(s = getsparam("SELECTMIN")) ||
+				    (dat && dat->num < atoi(s)))))
+	return 1;
+
+    selectlocalmap(mskeymap);
+    noselect = 0;
+    mselect = (*(minfo.cur))->gnum;
+    for (;;) {
+	showinglist = -2;
+	zrefresh();
+	inselect = 1;
+	if (noselect)
+	    break;
+	if (!i) {
+	    i = mcols * mlines;
+	    while (i--)
+		if (mtab[i])
+		    break;
+	    if (!i)
+		break;
+	    i = 1;
+	}
+	p = mtab + mcol + (mline * mcols);
+	pg = mgtab + mcol + (mline * mcols);
+	minfo.cur = *p;
+	minfo.group = *pg;
+
+    getk:
+
+	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak) ||
+	    cmd == Th(z_acceptline))
+	    break;
+	else if (cmd == Th(z_acceptandhold) ||
+		 cmd == Th(z_acceptandmenucomplete)) {
+	    Menustack s = (Menustack) zhalloc(sizeof(*s));
+
+	    s->prev = u;
+	    u = s;
+	    s->line = dupstring((char *) line);
+	    s->cs = cs;
+	    memcpy(&(s->info), &minfo, sizeof(struct menuinfo));
+	    acceptlast();
+	    do_menucmp(0);
+	    mselect = (*(minfo.cur))->gnum;
+	    continue;
+	} else if (cmd == Th(z_undo)) {
+	    int l;
+
+	    if (!u)
+		goto getk;
+
+	    cs = 0;
+	    foredel(ll);
+	    spaceinline(l = strlen(u->line));
+	    strncpy((char *) line, u->line, l);
+	    cs = u->cs;
+	    memcpy(&minfo, &(u->info), sizeof(struct menuinfo));
+	    p = &(minfo.cur);
+	    u = u->prev;
+	} else if (cmd == Th(z_redisplay)) {
+	    redisplay(zlenoargs);
+	    continue;
+	} else if (cmd == Th(z_clearscreen)) {
+	    clearscreen(zlenoargs);
+	    continue;
+	} else if (cmd == Th(z_downhistory) ||
+		   cmd == Th(z_downlineorhistory) ||
+		   cmd == Th(z_downlineorsearch) ||
+		   cmd == Th(z_vidownlineorhistory)) {
+	    do {
+		if (mline == mlines - 1) {
+		    p -= mline * mcols;
+		    mline = 0;
+		} else {
+		    mline++;
+		    p += mcols;
+		}
+	    } while (!*p);
+	} else if (cmd == Th(z_uphistory) ||
+		   cmd == Th(z_uplineorhistory) ||
+		   cmd == Th(z_uplineorsearch) ||
+		   cmd == Th(z_viuplineorhistory)) {
+	    do {
+		if (!mline) {
+		    mline = mlines - 1;
+		    p += mline * mcols;
+		} else {
+		    mline--;
+		    p -= mcols;
+		}
+	    } while (!*p);
+	} else if (cmd == Th(z_forwardchar) || cmd == Th(z_viforwardchar)) {
+	    do {
+		if (mcol == mcols - 1) {
+		    p -= mcol;
+		    mcol = 0;
+		} else {
+		    mcol++;
+		    p++;
+		}
+	    } while (!*p);
+	} else if (cmd == Th(z_backwardchar) || cmd == Th(z_vibackwardchar)) {
+	    do {
+		if (!mcol) {
+		    mcol = mcols - 1;
+		    p += mcol;
+		} else {
+		    mcol--;
+		    p--;
+		}
+	    } while (!*p);
+	} else if (cmd == Th(z_beginningofbufferorhistory) ||
+		   cmd == Th(z_beginningofline) ||
+		   cmd == Th(z_beginningoflinehist) ||
+		   cmd == Th(z_vibeginningofline)) {
+	    p -= mcol;
+	    mcol = 0;
+	    while (!*p) {
+		mcol++;
+		p++;
+	    }
+	} else if (cmd == Th(z_endofbufferorhistory) ||
+		   cmd == Th(z_endofline) ||
+		   cmd == Th(z_endoflinehist) ||
+		   cmd == Th(z_viendofline)) {
+	    p += mcols - mcol - 1;
+	    mcol = mcols - 1;
+	    while (!*p) {
+		mcol--;
+		p--;
+	    }
+	} else if (cmd == Th(z_forwardword) ||
+		   cmd == Th(z_emacsforwardword) ||
+		   cmd == Th(z_viforwardword) ||
+		   cmd == Th(z_viforwardwordend)) {
+	    Cmgroup g = *pg;
+	    int ol = mline;
+
+	    do {
+		if (mline == mlines - 1) {
+		    p -= mline * mcols;
+		    pg -= mline * mcols;
+		    mline = 0;
+		} else {
+		    mline++;
+		    p += mcols;
+		    pg += mcols;
+		}
+	    } while (ol != mline && (*pg == g || !*pg));
+	} else if (cmd == Th(z_backwardword) ||
+		   cmd == Th(z_emacsbackwardword) ||
+		   cmd == Th(z_vibackwardword)) {
+	    Cmgroup g = *pg;
+	    int ol = mline;
+
+	    do {
+		if (!mline) {
+		    mline = mlines - 1;
+		    p += mline * mcols;
+		    pg += mline * mcols;
+		} else {
+		    mline--;
+		    p -= mcols;
+		    pg -= mcols;
+		}
+	    } while (ol != mline && (*pg == g || !*pg));
+	} else if (cmd == Th(z_completeword) ||
+		   cmd == Th(z_expandorcomplete) ||
+		   cmd == Th(z_expandorcompleteprefix) ||
+		   cmd == Th(z_menucomplete) ||
+		   cmd == Th(z_menuexpandorcomplete) ||
+		   !strcmp(cmd->nam, "menu-select") ||
+		   !strcmp(cmd->nam, "complete-word") ||
+		   !strcmp(cmd->nam, "expand-or-complete") ||
+		   !strcmp(cmd->nam, "expand-or-complete-prefix") ||
+		   !strcmp(cmd->nam, "menu-complete") ||
+		   !strcmp(cmd->nam, "menu-expand-or-complete")) {
+	    do_menucmp(0);
+	    mselect = (*(minfo.cur))->gnum;
+	    continue;
+	} else if (cmd == Th(z_reversemenucomplete) ||
+		   !strcmp(cmd->nam, "reverse-menu-complete")) {
+	    reversemenucomplete(zlenoargs);
+	    mselect = (*(minfo.cur))->gnum;
+	    continue;
+	} else {
+	    ungetkeycmd();
+	    break;
+	}
+	do_single(**p);
+	mselect = (**p)->gnum;
+    }
+    selectlocalmap(NULL);
+    mselect = -1;
+    inselect = 0;
+    if (!noselect) {
+	showinglist = -2;
+	zrefresh();
+    }
+    return noselect;
+}
+
+/* The widget function. */
+
+static int
+menuselect(char **args)
+{
+    int d = 0;
+
+    if (!minfo.cur) {
+	menucomplete(args);
+	if ((minfo.cur && minfo.asked == 2) || getsparam("ZLS_SELECT"))
+	    return 0;
+	d = 1;
+    }
+    if (minfo.cur && (minfo.asked == 2 || domenuselect(NULL, NULL)) && !d)
+	menucomplete(args);
+
+    return 0;
+}
+
+/**/
+int
+setup_complist(Module m)
+{
+    return 0;
+}
+
+/**/
+int
+boot_complist(Module m)
+{
+    mtab = NULL;
+    mgtab = NULL;
+    mselect = -1;
+    inselect = 0;
+
+    w_menuselect = addzlefunction("menu-select", menuselect,
+                                    ZLE_MENUCMP|ZLE_KEEPSUFFIX|ZLE_ISCOMP);
+    if (!w_menuselect) {
+	zwarnnam(m->nam, "name clash when adding ZLE function `menu-select'",
+		 NULL, 0);
+	return -1;
+    }
+    addhookfunc("list_matches", (Hookfn) complistmatches);
+    addhookfunc("menu_start", (Hookfn) domenuselect);
+    mskeymap = newkeymap(NULL, "menuselect");
+    linkkeymap(mskeymap, "menuselect", 1);
+    bindkey(mskeymap, "\t", refthingy(t_completeword), NULL);
+    bindkey(mskeymap, "\n", refthingy(t_acceptline), NULL);
+    bindkey(mskeymap, "\r", refthingy(t_acceptline), NULL);
+    bindkey(mskeymap, "\33[A",  refthingy(t_uplineorhistory), NULL);
+    bindkey(mskeymap, "\33[B",  refthingy(t_downlineorhistory), NULL);
+    bindkey(mskeymap, "\33[C",  refthingy(t_forwardchar), NULL);
+    bindkey(mskeymap, "\33[D",  refthingy(t_backwardchar), NULL);
+    bindkey(mskeymap, "\33OA",  refthingy(t_uplineorhistory), NULL);
+    bindkey(mskeymap, "\33OB",  refthingy(t_downlineorhistory), NULL);
+    bindkey(mskeymap, "\33OC",  refthingy(t_forwardchar), NULL);
+    bindkey(mskeymap, "\33OD",  refthingy(t_backwardchar), NULL);
+    return 0;
+}
+
+#ifdef MODULE
+
+/**/
+int
+cleanup_complist(Module m)
+{
+    free(mtab);
+    free(mgtab);
+
+    deletezlefunction(w_menuselect);
+    deletehookfunc("list_matches", (Hookfn) complistmatches);
+    deletehookfunc("menu_start", (Hookfn) domenuselect);
+    unlinkkeymap("menuselect", 1);
+    return 0;
+}
+
+/**/
+int
+finish_complist(Module m)
+{
+    return 0;
+}
+
+#endif
diff --git a/Src/Zle/complist.mdd b/Src/Zle/complist.mdd
new file mode 100644
index 000000000..8ea60b0a8
--- /dev/null
+++ b/Src/Zle/complist.mdd
@@ -0,0 +1,3 @@
+moddeps="comp1 zle"
+
+objects="complist.o"
diff --git a/Src/Zle/deltochar.c b/Src/Zle/deltochar.c
index 66a301119..f0df33f95 100644
--- a/Src/Zle/deltochar.c
+++ b/Src/Zle/deltochar.c
@@ -33,8 +33,8 @@
 static Widget w_deletetochar;
 
 /**/
-static void
-deltochar(void)
+static int
+deltochar(char **args)
 {
     int c = getkey(0), dest = cs, ok = 0, n = zmult;
 
@@ -67,8 +67,7 @@ deltochar(void)
 	    }
 	}
     }
-    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..e0b88cd5c 100644
--- a/Src/Zle/zle.export
+++ b/Src/Zle/zle.export
@@ -1,10 +1,44 @@
 #!
+acceptlast
 addzlefunction
 backdel
 backkill
+bindkey
+clearflag
+clearscreen
 deletezlefunction
+do_menucmp
+do_single
 feep
 foredel
 forekill
 getkey
+getkeycmd
+getzlequery
+linkkeymap
+listshown
+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 d5e0ee89c..ff515e7c4 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -38,7 +38,7 @@ typedef struct thingy *Thingy;
 
 /* widgets (ZLE functions) */
 
-typedef void (*ZleIntFunc) _((void));
+typedef int (*ZleIntFunc) _((char **));
 
 struct widget {
     int flags;		/* flags (see below) */
@@ -54,16 +54,17 @@ struct widget {
     } u;
 };
 
-#define WIDGET_INT	(1<<0)    /* widget is internally implemented */
-#define WIDGET_NCOMP    (1<<1)    /* new style completion widget */
-#define ZLE_MENUCMP	(1<<2)    /* DON'T invalidate completion list */
+#define WIDGET_INT	(1<<0)	/* widget is internally implemented */
+#define WIDGET_NCOMP    (1<<1)	/* new style completion widget */
+#define ZLE_MENUCMP	(1<<2)	/* DON'T invalidate completion list */
 #define ZLE_YANK	(1<<3)
-#define ZLE_LINEMOVE	(1<<4)    /* command is a line-oriented movement */
-#define ZLE_LASTCOL     (1<<5)    /* command maintains lastcol correctly */
+#define ZLE_LINEMOVE	(1<<4)	/* command is a line-oriented movement */
+#define ZLE_LASTCOL     (1<<5)	/* command maintains lastcol correctly */
 #define ZLE_KILL	(1<<6)
-#define ZLE_KEEPSUFFIX	(1<<7)    /* DON'T remove added suffix */
-#define ZLE_NOTCOMMAND  (1<<8)    /* widget should not alter lastcmd */
-#define ZLE_ISCOMP      (1<<9)	  /* usable for new style completion */
+#define ZLE_KEEPSUFFIX	(1<<7)	/* DON'T remove added suffix */
+#define ZLE_NOTCOMMAND  (1<<8)	/* widget should not alter lastcmd */
+#define ZLE_ISCOMP      (1<<9)	/* usable for new style completion */
+
 /* thingies */
 
 struct thingy {
@@ -142,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 2fc55e98c..f85fa00a6 100644
--- a/Src/Zle/zle_hist.c
+++ b/Src/Zle/zle_hist.c
@@ -73,12 +73,13 @@ forget_edits(void)
 }
 
 /**/
-void
-uphistory(void)
+int
+uphistory(char **args)
 {
     int nodups = isset(HISTIGNOREDUPS);
     if (!zle_goto_hist(histline, -zmult, nodups) && isset(HISTBEEP))
-	feep();
+	return 1;
+    return 0;
 }
 
 /**/
@@ -116,53 +117,53 @@ upline(void)
 }
 
 /**/
-void
-uplineorhistory(void)
+int
+uplineorhistory(char **args)
 {
     int ocs = cs;
     int n = upline();
     if (n) {
-	int m = zmult;
+	int m = zmult, ret;
 
 	cs = ocs;
-	if (virangeflag || !histallowed) {
-	    feep();
-	    return;
-	}
+	if (virangeflag || !histallowed)
+	    return 1;
 	zmult = n;
-	uphistory();
+	ret = uphistory(args);
 	zmult = m;
+	return ret;
     }
+    return 0;
 }
 
 /**/
-void
-viuplineorhistory(void)
+int
+viuplineorhistory(char **args)
 {
     int col = lastcol;
-    uplineorhistory();
+    uplineorhistory(args);
     lastcol = col;
-    vifirstnonblank();
+    return vifirstnonblank(args);
 }
 
 /**/
-void
-uplineorsearch(void)
+int
+uplineorsearch(char **args)
 {
     int ocs = cs;
     int n = upline();
     if (n) {
-	int m = zmult;
+	int m = zmult, ret;
 
 	cs = ocs;
-	if (virangeflag || !histallowed) {
-	    feep();
-	    return;
-	}
+	if (virangeflag || !histallowed)
+	    return 1;
 	zmult = n;
-	historysearchbackward();
+	ret = historysearchbackward(args);
 	zmult = m;
+	return ret;
     }
+    return 0;
 }
 
 /**/
@@ -200,202 +201,220 @@ downline(void)
 }
 
 /**/
-void
-downlineorhistory(void)
+int
+downlineorhistory(char **args)
 {
     int ocs = cs;
     int n = downline();
     if (n) {
-	int m = zmult;
+	int m = zmult, ret;
 
 	cs = ocs;
-	if (virangeflag || !histallowed) {
-	    feep();
-	    return;
-	}
+	if (virangeflag || !histallowed)
+	    return 1;
 	zmult = n;
-	downhistory();
+	ret = downhistory(args);
 	zmult = m;
+	return ret;
     }
+    return 0;
 }
 
 /**/
-void
-vidownlineorhistory(void)
+int
+vidownlineorhistory(char **args)
 {
     int col = lastcol;
-    downlineorhistory();
+    downlineorhistory(args);
     lastcol = col;
-    vifirstnonblank();
+    return vifirstnonblank(zlenoargs);
 }
 
 /**/
-void
-downlineorsearch(void)
+int
+downlineorsearch(char **args)
 {
     int ocs = cs;
     int n = downline();
     if (n) {
-	int m = zmult;
+	int m = zmult, ret;
 
 	cs = ocs;
-	if (virangeflag || !histallowed) {
-	    feep();
-	    return;
-	}
+	if (virangeflag || !histallowed)
+	    return 1;
 	zmult = n;
-	historysearchforward();
+	ret = historysearchforward(args);
 	zmult = m;
+	return ret;
     }
+    return 0;
 }
 
 /**/
-void
-acceptlineanddownhistory(void)
+int
+acceptlineanddownhistory(char **args)
 {
     Histent he;
 
-    if (!(he = movehistent(quietgethist(histline), 1, HIST_FOREIGN))) {
-	feep();
-	return;
-    }
+    if (!(he = movehistent(quietgethist(histline), 1, HIST_FOREIGN)))
+	return 1;
     pushnode(bufstack, ztrdup(ZLETEXT(he)));
     done = 1;
     stackhist = he->histnum;
+    return 0;
 }
 
 /**/
-void
-downhistory(void)
+int
+downhistory(char **args)
 {
     int nodups = isset(HISTIGNOREDUPS);
     if (!zle_goto_hist(histline, zmult, nodups) && isset(HISTBEEP))
-	feep();
+	return 1;
+    return 0;
 }
 
 static int histpos, srch_hl, srch_cs = -1;
 static char *srch_str;
 
 /**/
-void
-historysearchbackward(void)
+int
+historysearchbackward(char **args)
 {
     Histent he;
-    int n = zmult;
-    char *s;
+    int n = zmult, hp;
+    char *s, *str;
 
     if (zmult < 0) {
+	int ret;
 	zmult = -n;
-	historysearchforward();
+	ret = historysearchforward(args);
 	zmult = n;
-	return;
+	return ret;
     }
-    if (histline == curhist || histline != srch_hl || cs != srch_cs || mark != 0
-     || memcmp(srch_str, line, histpos) != 0) {
-	zfree(srch_str, histpos);
-	for (histpos = 0; histpos < ll && !iblank(line[histpos]); histpos++) ;
-	if (histpos < ll)
-	    histpos++;
-	srch_str = zalloc(histpos);
-	memcpy(srch_str, line, histpos);
+    if ((str = *args))
+	hp = strlen(str);
+    else {
+	if (histline == curhist || histline != srch_hl || cs != srch_cs ||
+	    mark != 0 || memcmp(srch_str, line, histpos) != 0) {
+	    zfree(srch_str, histpos);
+	    for (histpos = 0; histpos < ll && !iblank(line[histpos]); histpos++) ;
+	    if (histpos < ll)
+		histpos++;
+	    srch_str = zalloc(histpos);
+	    memcpy(srch_str, line, histpos);
+	}
+	str = srch_str;
+	hp = histpos;
     }
     he = quietgethist(histline);
     while ((he = movehistent(he, -1, hist_skip_flags))) {
 	if (isset(HISTFINDNODUPS) && he->flags & HIST_DUP)
 	    continue;
 	s = ZLETEXT(he);
-	if (metadiffer(s, srch_str, histpos) < 0 &&
-	    metadiffer(s, srch_str, ll)) {
+	if (metadiffer(s, str, hp) < 0 &&
+	    (*args || metadiffer(s, str, ll))) {
 	    if (--n <= 0) {
 		zle_setline(he);
 		srch_hl = histline;
 		srch_cs = cs;
-		return;
+		return 0;
 	    }
 	}
     }
-    feep();
+    return 1;
 }
 
 /**/
-void
-historysearchforward(void)
+int
+historysearchforward(char **args)
 {
     Histent he;
-    int n = zmult;
-    char *s;
+    int n = zmult, hp;
+    char *s, *str;
 
     if (zmult < 0) {
+	int ret;
 	zmult = -n;
-	historysearchbackward();
+	ret = historysearchbackward(args);
 	zmult = n;
-	return;
+	return ret;
     }
-    if (histline == curhist || histline != srch_hl || cs != srch_cs || mark != 0
-     || memcmp(srch_str, line, histpos) != 0) {
-	zfree(srch_str, histpos);
-	for (histpos = 0; histpos < ll && !iblank(line[histpos]); histpos++) ;
-	if (histpos < ll)
-	    histpos++;
-	srch_str = zalloc(histpos);
-	memcpy(srch_str, line, histpos);
+    if ((str = *args))
+	hp = strlen(str);
+    else {
+	if (histline == curhist || histline != srch_hl || cs != srch_cs ||
+	    mark != 0 || memcmp(srch_str, line, histpos) != 0) {
+	    zfree(srch_str, histpos);
+	    for (histpos = 0; histpos < ll && !iblank(line[histpos]); histpos++) ;
+	    if (histpos < ll)
+		histpos++;
+	    srch_str = zalloc(histpos);
+	    memcpy(srch_str, line, histpos);
+	}
+	str = srch_str;
+	hp = histpos;
     }
     he = quietgethist(histline);
     while ((he = movehistent(he, 1, hist_skip_flags))) {
 	if (isset(HISTFINDNODUPS) && he->flags & HIST_DUP)
 	    continue;
 	s = ZLETEXT(he);
-	if (metadiffer(s, srch_str, histpos) < (he->histnum == curhist) &&
-	    metadiffer(s, srch_str, ll)) {
+	if (metadiffer(s, str, hp) < (he->histnum == curhist) &&
+	    (*args || metadiffer(s, str, ll))) {
 	    if (--n <= 0) {
 		zle_setline(he);
 		srch_hl = histline;
 		srch_cs = cs;
-		return;
+		return 0;
 	    }
 	}
     }
-    feep();
+    return 1;
 }
 
 /**/
-void
-beginningofbufferorhistory(void)
+int
+beginningofbufferorhistory(char **args)
 {
     if (findbol())
 	cs = 0;
     else
-	beginningofhistory();
+	return beginningofhistory(args);
+    return 0;
 }
 
 /**/
-void
-beginningofhistory(void)
+int
+beginningofhistory(char **args)
 {
     if (!zle_goto_hist(firsthist(), 0, 0) && isset(HISTBEEP))
-	feep();
+	return 1;
+    return 0;
 }
 
 /**/
-void
-endofbufferorhistory(void)
+int
+endofbufferorhistory(char **args)
 {
     if (findeol() != ll)
 	cs = ll;
     else
-	endofhistory();
+	return endofhistory(args);
+    return 0;
 }
 
 /**/
-void
-endofhistory(void)
+int
+endofhistory(char **args)
 {
     zle_goto_hist(curhist, 0, 0);
+    return 0;
 }
 
 /**/
-void
-insertlastword(void)
+int
+insertlastword(char **args)
 {
     int n;
     char *s, *t;
@@ -420,19 +439,15 @@ insertlastword(void)
 	zsfree(lastinsert);
 	lastinsert = NULL;
     }
-    if (!(he = quietgethist(evhist)) || !he->nwords) {
-	feep();
-	return;
-    }
+    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();
-	return;
-    }
+    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;
@@ -446,6 +461,7 @@ insertlastword(void)
     doinsert(s);
     zmult = n;
     *t = save;
+    return 0;
 }
 
 /**/
@@ -461,15 +477,15 @@ zle_setline(Histent he)
 }
 
 /**/
-void
-setlocalhistory(void)
+int
+setlocalhistory(char **args)
 {
     if (zmod.flags & MOD_MULT) {
 	hist_skip_flags = zmult? HIST_FOREIGN : 0;
-    }
-    else {
+    } else {
 	hist_skip_flags ^= HIST_FOREIGN;
     }
+    return 0;
 }
 
 /**/
@@ -489,13 +505,13 @@ zle_goto_hist(int ev, int n, int skipdups)
 }
 
 /**/
-void
-pushline(void)
+int
+pushline(char **args)
 {
     int n = zmult;
 
     if (n < 0)
-	return;
+	return 1;
     pushnode(bufstack, metafy((char *) line, ll, META_DUP));
     while (--n)
 	pushnode(bufstack, ztrdup(""));
@@ -503,18 +519,19 @@ pushline(void)
     *line = '\0';
     ll = cs = 0;
     clearlist = 1;
+    return 0;
 }
 
 /**/
-void
-pushlineoredit(void)
+int
+pushlineoredit(char **args)
 {
-    int ics;
+    int ics, ret;
     unsigned char *s;
     char *hline = hgetline();
 
     if (zmult < 0)
-	return;
+	return 1;
     if (hline && *hline) {
 	ics = ztrlen(hline);
 	sizeline(ics + ll + 1);
@@ -524,34 +541,36 @@ pushlineoredit(void)
 	ll += ics;
 	cs += ics;
     }
-    pushline();
+    ret = pushline(args);
     if (!isfirstln)
 	errflag = done = 1;
     clearlist = 1;
+    return ret;
 }
 
 /**/
-void
-pushinput(void)
+int
+pushinput(char **args)
 {
-    int i;
+    int i, ret;
 
     if (zmult < 0)
-	return;
+	return 1;
     zmult += i = !isfirstln;
-    pushlineoredit();
+    ret = pushlineoredit(args);
     zmult -= i;
+    return ret;
 }
 
 /**/
-void
-getline(void)
+int
+getline(char **args)
 {
     char *s = (char *)getlinknode(bufstack);
 
-    if (!s)
-	feep();
-    else {
+    if (!s) {
+	return 1;
+    } else {
 	int cc;
 
 	unmetafy(s, &cc);
@@ -561,20 +580,23 @@ getline(void)
 	free(s);
 	clearlist = 1;
     }
+    return 0;
 }
 
 /**/
-void
-historyincrementalsearchbackward(void)
+int
+historyincrementalsearchbackward(char **args)
 {
-    doisearch(-1);
+    doisearch(args, -1);
+    return 0;
 }
 
 /**/
-void
-historyincrementalsearchforward(void)
+int
+historyincrementalsearchforward(char **args)
 {
-    doisearch(1);
+    doisearch(args, 1);
+    return 0;
 }
 
 static struct isrch_spot {
@@ -642,13 +664,13 @@ get_isrch_spot(int num, int *hlp, int *posp, int *csp, int *lenp, int *dirp, int
 
 /**/
 static void
-doisearch(int dir)
+doisearch(char **args, int dir)
 {
     char *s, *ibuf = zhalloc(80), *sbuf = ibuf + FIRST_SEARCH_CHAR;
     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;
+    int hl = histline, savekeys = -1, feep = 0;
     Thingy cmd;
     char *okeymap = curkeymapname;
     static char *previous_search = NULL;
@@ -657,6 +679,14 @@ doisearch(int dir)
 
     clearlist = 1;
 
+    if (*args) {
+	int len;
+	char *arg;
+	savekeys = kungetct;
+	arg = getkeystring(*args, &len, 2, NULL);
+	ungetkeys(arg, len);
+    }
+
     strcpy(ibuf, ISEARCH_PROMPT);
     memcpy(ibuf + NORM_PROMPT_POS, (dir == 1) ? "fwd" : "bck", 3);
     remember_edits();
@@ -709,7 +739,7 @@ doisearch(int dir)
 		    get_isrch_spot(top_spot, &hl, &pos, &cs, &sbptr,
 				   &dir, &nomatch);
 		    if (!nomatch) {
-			feep();
+			feep = 1;
 			nomatch = 1;
 		    }
 		    he = quietgethist(hl);
@@ -743,14 +773,14 @@ doisearch(int dir)
 	    break;
 	}
 	if(cmd == Th(z_clearscreen)) {
-	    clearscreen();
+	    clearscreen(zlenoargs);
 	    goto ref;
 	} else if(cmd == Th(z_redisplay)) {
-	    redisplay();
+	    redisplay(zlenoargs);
 	    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)) {
@@ -758,7 +788,7 @@ doisearch(int dir)
 		get_isrch_spot(--top_spot, &hl, &pos, &cs, &sbptr,
 			       &dir, &nomatch);
 	    else
-		feep();
+		feep = 1;
 	    if (nomatch) {
 		statusline = ibuf;
 		skip_pos = 1;
@@ -773,16 +803,16 @@ doisearch(int dir)
 	    memcpy(ibuf + NORM_PROMPT_POS, (dir == 1) ? "fwd" : "bck", 3);
 	    continue;
 	} else if(cmd == Th(z_acceptandhold)) {
-	    acceptandhold();
+	    acceptandhold(zlenoargs);
 	    break;
 	} else if(cmd == Th(z_acceptandinfernexthistory)) {
-	    acceptandinfernexthistory();
+	    acceptandinfernexthistory(zlenoargs);
 	    break;
 	} else if(cmd == Th(z_acceptlineanddownhistory)) {
-	    acceptlineanddownhistory();
+	    acceptlineanddownhistory(zlenoargs);
 	    break;
 	} else if(cmd == Th(z_acceptline)) {
-	    acceptline();
+	    acceptline(zlenoargs);
 	    break;
 	} else if(cmd == Th(z_historyincrementalsearchbackward)) {
 	    set_isrch_spot(top_spot++, hl, pos, cs, sbptr, dir, nomatch);
@@ -825,7 +855,7 @@ doisearch(int dir)
 		zrefresh();
 	    }
 	    if ((c = getkey(0)) == EOF)
-		feep();
+		feep = 1;
 	    else
 		goto ins;
 	} else {
@@ -843,7 +873,7 @@ doisearch(int dir)
 	    }
 	ins:
 	    if (sbptr == PATH_MAX) {
-		feep();
+		feep = 1;
 		continue;
 	    }
 	    set_isrch_spot(top_spot++, hl, pos, cs, sbptr, dir, nomatch);
@@ -854,7 +884,9 @@ doisearch(int dir)
 	    }
 	    sbuf[sbptr++] = c;
 	}
-	handlefeep();
+	if (feep)
+	    handlefeep(zlenoargs);
+	feep = 0;
     }
     if (sbptr) {
 	zfree(previous_search, previous_search_len);
@@ -863,11 +895,17 @@ doisearch(int dir)
     }
     statusline = NULL;
     selectkeymap(okeymap, 1);
+    /*
+     * Don't allow unused characters provided as a string to the
+     * widget to overflow and be used as separated commands.
+     */
+    if (savekeys >= 0 && kungetct > savekeys)
+	kungetct = savekeys;
 }
 
 /**/
-void
-acceptandinfernexthistory(void)
+int
+acceptandinfernexthistory(char **args)
 {
     Histent he;
 
@@ -878,14 +916,15 @@ acceptandinfernexthistory(void)
 	    he = movehistent(he, 1, HIST_FOREIGN);
 	    pushnode(bufstack, ztrdup(ZLETEXT(he)));
 	    stackhist = he->histnum;
-	    return;
+	    return 0;
 	}
     }
+    return 1;
 }
 
 /**/
-void
-infernexthistory(void)
+int
+infernexthistory(char **args)
 {
     Histent he;
 
@@ -894,28 +933,30 @@ infernexthistory(void)
 	if (!metadiffer(ZLETEXT(he), (char *) line, ll)) {
 	    he = movehistent(he, 1, HIST_FOREIGN);
 	    zle_setline(he);
-	    return;
+	    return 0;
 	}
     }
-    feep();
+    return 1;
 }
 
 /**/
-void
-vifetchhistory(void)
+int
+vifetchhistory(char **args)
 {
     if (zmult < 0)
-	return;
+	return 1;
     if (histline == curhist) {
 	if (!(zmod.flags & MOD_MULT)) {
 	    cs = ll;
 	    cs = findbol();
-	    return;
+	    return 0;
 	}
     }
     if (!zle_goto_hist((zmod.flags & MOD_MULT) ? zmult : curhist, 0, 0) &&
-	isset(HISTBEEP))
-	feep();
+	isset(HISTBEEP)) {
+	return 1;
+    }
+    return 0;
 }
 
 /* the last vi search */
@@ -928,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;
 
@@ -953,9 +994,9 @@ getvisrchstr(void)
 	    cmd = Th(z_selfinsert);
 	}
 	if(cmd == Th(z_redisplay)) {
-	    redisplay();
+	    redisplay(zlenoargs);
 	} else if(cmd == Th(z_clearscreen)) {
-	    clearscreen();
+	    clearscreen(zlenoargs);
 	} else if(cmd == Th(z_acceptline) ||
 	    	cmd == Th(z_vicmdmode)) {
 	    sbuf[sptr] = 0;
@@ -981,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)) {
@@ -998,9 +1039,11 @@ getvisrchstr(void)
 	    }
 	    sbuf[sptr++] = c;
 	} else {
-	    feep();
+	    feep = 1;
 	}
-	handlefeep();
+	if (feep)
+	    handlefeep(zlenoargs);
+	feep = 0;
     }
     statusline = NULL;
     selectkeymap(okeymap, 1);
@@ -1008,36 +1051,58 @@ getvisrchstr(void)
 }
 
 /**/
-void
-vihistorysearchforward(void)
+int
+vihistorysearchforward(char **args)
 {
+    if (*args) {
+	int ose = visrchsense, ret;
+	char *ost = visrchstr;
+
+	visrchsense = 1;
+	visrchstr = *args;
+	ret = virepeatsearch(zlenoargs);
+	visrchsense = ose;
+	visrchstr = ost;
+	return ret;
+    }
     visrchsense = 1;
     if (getvisrchstr())
-	virepeatsearch();
+	return virepeatsearch(zlenoargs);
+    return 1;
 }
 
 /**/
-void
-vihistorysearchbackward(void)
+int
+vihistorysearchbackward(char **args)
 {
+    if (*args) {
+	int ose = visrchsense, ret;
+	char *ost = visrchstr;
+
+	visrchsense = -1;
+	visrchstr = *args;
+	ret = virepeatsearch(zlenoargs);
+	visrchsense = ose;
+	visrchstr = ost;
+	return ret;
+    }
     visrchsense = -1;
     if (getvisrchstr())
-	virepeatsearch();
+	return virepeatsearch(zlenoargs);
+    return 1;
 }
 
 /**/
-void
-virepeatsearch(void)
+int
+virepeatsearch(char **args)
 {
     Histent he;
     int t0;
     int n = zmult;
     char *s;
 
-    if (!visrchstr) {
-	feep();
-	return;
-    }
+    if (!visrchstr)
+	return 1;
     if (zmult < 0) {
 	n = -n;
 	visrchsense = -visrchsense;
@@ -1053,20 +1118,22 @@ virepeatsearch(void)
 			      : hstrnstr(s, 0, visrchstr, t0, 1, 1) != 0)) {
 	    if (--n <= 0) {
 		zle_setline(he);
-		return;
+		return 0;
 	    }
 	}
     }
-    feep();
+    return 1;
 }
 
 /**/
-void
-virevrepeatsearch(void)
+int
+virevrepeatsearch(char **args)
 {
+    int ret;
     visrchsense = -visrchsense;
-    virepeatsearch();
+    ret = virepeatsearch(args);
     visrchsense = -visrchsense;
+    return ret;
 }
 
 /* Extra function added by A.R. Iano-Fletcher.	*/
@@ -1074,8 +1141,8 @@ virevrepeatsearch(void)
 /* history-beginning-search-backward */
 
 /**/
-void
-historybeginningsearchbackward(void)
+int
+historybeginningsearchbackward(char **args)
 {
     Histent he;
     int cpos = cs;		/* save cursor position */
@@ -1083,10 +1150,11 @@ historybeginningsearchbackward(void)
     char *s;
 
     if (zmult < 0) {
+	int ret;
 	zmult = -n;
-	historybeginningsearchforward();
+	ret = historybeginningsearchforward(args);
 	zmult = n;
-	return;
+	return ret;
     }
     he = quietgethist(histline);
     while ((he = movehistent(he, -1, hist_skip_flags))) {
@@ -1098,19 +1166,19 @@ historybeginningsearchbackward(void)
 	    if (--n <= 0) {
 		zle_setline(he);
 		cs = cpos;
-		return;
+		return 0;
 	    }
 	}
     }
-    feep();
+    return 1;
 }
 
 /* Extra function added by A.R. Iano-Fletcher.	*/
 
 /* history-beginning-search-forward */
 /**/
-void
-historybeginningsearchforward(void)
+int
+historybeginningsearchforward(char **args)
 {
     Histent he;
     int cpos = cs;		/* save cursor position */
@@ -1118,10 +1186,11 @@ historybeginningsearchforward(void)
     char *s;
 
     if (zmult < 0) {
+	int ret;
 	zmult = -n;
-	historybeginningsearchbackward();
+	ret = historybeginningsearchbackward(args);
 	zmult = n;
-	return;
+	return ret;
     }
     he = quietgethist(histline);
     while ((he = movehistent(he, 1, hist_skip_flags))) {
@@ -1133,9 +1202,9 @@ historybeginningsearchforward(void)
 	    if (--n <= 0) {
 		zle_setline(he);
 		cs = cpos;
-		return;
+		return 0;
 	    }
 	}
     }
-    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 13f3cc402..8f6dfdf75 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -106,10 +106,12 @@ struct modifier zmod;
 /**/
 int prefixflag;
 
-/* != 0 if there is a pending beep (usually indicating an error) */
+/* Number of characters waiting to be read by the ungetkeys mechanism */
+/**/
+int kungetct;
 
 /**/
-int feepflag;
+char *zlenoargs[1] = { NULL };
 
 #ifdef FIONREAD
 static int delayzsetterm;
@@ -262,7 +264,7 @@ zsetterm(void)
 }
 
 static char *kungetbuf;
-static int kungetct, kungetsz;
+static int kungetsz;
 
 /**/
 void
@@ -499,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);
@@ -527,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);
+		if (execzlefunc(bindk, zlenoargs))
+		    handlefeep(zlenoargs);
 		handleprefixes();
 		/* for vi mode, make sure the cursor isn't somewhere illegal */
 		if (invicmdmode() && cs > findbol() &&
@@ -565,7 +569,6 @@ zleread(char *lp, char *rp, int flags)
 #endif
 		if (!kungetct)
 		    zrefresh();
-	    handlefeep();
 	}
 	statusline = NULL;
 	invalidatelist();
@@ -591,10 +594,10 @@ zleread(char *lp, char *rp, int flags)
 /* execute a widget */
 
 /**/
-void
-execzlefunc(Thingy func)
+int
+execzlefunc(Thingy func, char **args)
 {
-    int r = 0;
+    int r = 0, ret = 0;
     Widget w;
 
     if(func->flags & DISABLED) {
@@ -605,7 +608,7 @@ execzlefunc(Thingy func)
 	zsfree(nm);
 	showmsg(msg);
 	zsfree(msg);
-	feep();
+	ret = 1;
     } else if((w = func->widget)->flags & (WIDGET_INT|WIDGET_NCOMP)) {
 	int wflags = w->flags;
 
@@ -621,9 +624,9 @@ execzlefunc(Thingy func)
 	    lastcol = -1;
 	if (wflags & WIDGET_NCOMP) {
 	    compwidget = w;
-	    completecall();
+	    ret = completecall(args);
 	} else
-	    w->u.fn();
+	    ret = w->u.fn(args);
 	if (!(wflags & ZLE_NOTCOMMAND))
 	    lastcmd = wflags;
 	r = 1;
@@ -638,14 +641,23 @@ execzlefunc(Thingy func)
 	    zsfree(nm);
 	    showmsg(msg);
 	    zsfree(msg);
-	    feep();
+	    ret = 1;
 	} else {
-	    int osc = sfcontext, osi = movefd(0);
-
+	    int osc = sfcontext, osi = movefd(0), olv = lastval;
+	    LinkList largs = NULL;
+
+	    if (*args) {
+		largs = newlinklist();
+		addlinknode(largs, dupstring(w->u.fnnam));
+		while (*args)
+		    addlinknode(largs, dupstring(*args++));
+	    }
 	    startparamscope();
 	    makezleparams(0);
 	    sfcontext = SFC_WIDGET;
-	    doshfunc(w->u.fnnam, l, NULL, 0, 1);
+	    doshfunc(w->u.fnnam, l, largs, 0, 0);
+	    ret = lastval;
+	    lastval = olv;
 	    sfcontext = osc;
 	    endparamscope();
 	    lastcmd = 0;
@@ -658,6 +670,7 @@ execzlefunc(Thingy func)
 	refthingy(func);
 	lbindk = func;
     }
+    return ret;
 }
 
 /* initialise command modifiers */
@@ -826,14 +839,14 @@ bin_vared(char *name, char **args, char *ops, int func)
 }
 
 /**/
-void
-describekeybriefly(void)
+int
+describekeybriefly(char **args)
 {
     char *seq, *str, *msg, *is;
     Thingy func;
 
     if (statusline)
-	return;
+	return 1;
     clearlist = 1;
     statusline = "Describe key briefly: _";
     statusll = strlen(statusline);
@@ -841,7 +854,7 @@ describekeybriefly(void)
     seq = getkeymapcmd(curkeymap, &func, &str);
     statusline = NULL;
     if(!*seq)
-	return;
+	return 1;
     msg = bindztrdup(seq);
     msg = appstr(msg, " is ");
     if (!func)
@@ -852,6 +865,7 @@ describekeybriefly(void)
     zsfree(is);
     showmsg(msg);
     zsfree(msg);
+    return 0;
 }
 
 #define MAXFOUND 4
@@ -882,13 +896,13 @@ scanfindfunc(char *seq, Thingy func, char *str, void *magic)
 }
 
 /**/
-void
-whereis(void)
+int
+whereis(char **args)
 {
     struct findfunc ff;
 
     if (!(ff.func = executenamedcommand("Where is: ")))
-	return;
+	return 1;
     ff.found = 0;
     ff.msg = niceztrdup(ff.func->nam);
     scankeymap(curkeymap, 1, scanfindfunc, &ff);
@@ -898,6 +912,7 @@ whereis(void)
 	ff.msg = appstr(ff.msg, " et al");
     showmsg(ff.msg);
     zsfree(ff.msg);
+    return 0;
 }
 
 /**/
@@ -933,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, "lDANCLmMgGc",    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),
 };
 
 /**/
@@ -955,6 +980,8 @@ setup_zle(Module m)
     unambig_dataptr = unambig_data;
     set_comp_sepptr = set_comp_sep;
 
+    getkeyptr = getkey;
+
     /* initialise the thingies */
     init_thingies();
     lbindk = NULL;
@@ -977,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;
 }
 
@@ -992,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;
 }
 
@@ -1031,6 +1060,8 @@ finish_zle(Module m)
     unambig_dataptr = NULL;
     set_comp_sepptr = NULL;
 
+    getkeyptr = NULL;
+
     return 0;
 }
 
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index e2ccfbc46..a51cdf92e 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -57,8 +57,8 @@ doinsert(char *str)
 }
 
 /**/
-void
-selfinsert(void)
+int
+selfinsert(char **args)
 {
     char s[3], *p = s;
 
@@ -69,56 +69,61 @@ selfinsert(void)
     *p++ = c;
     *p = 0;
     doinsert(s);
+    return 0;
 }
 
 /**/
-void
-selfinsertunmeta(void)
+int
+selfinsertunmeta(char **args)
 {
     c &= 0x7f;
     if (c == '\r')
 	c = '\n';
-    selfinsert();
+    return selfinsert(args);
 }
 
 /**/
-void
-deletechar(void)
+int
+deletechar(char **args)
 {
     if (zmult < 0) {
+	int ret;
 	zmult = -zmult;
-	backwarddeletechar();
+	ret = backwarddeletechar(args);
 	zmult = -zmult;
-	return;
+	return ret;
     }
     if (cs + zmult <= ll) {
 	cs += zmult;
 	backdel(zmult);
-    } else
-	feep();
+	return 0;
+    }
+    return 1;
 }
 
 /**/
-void
-backwarddeletechar(void)
+int
+backwarddeletechar(char **args)
 {
     if (zmult < 0) {
+	int ret;
 	zmult = -zmult;
-	deletechar();
+	ret = deletechar(args);
 	zmult = -zmult;
-	return;
+	return ret;
     }
     backdel(zmult > cs ? cs : zmult);
+    return 0;
 }
 
 /**/
-void
-killwholeline(void)
+int
+killwholeline(char **args)
 {
     int i, fg, n = zmult;
 
     if (n < 0)
-	return;
+	return 1;
     while (n--) {
 	if ((fg = (cs && cs == ll)))
 	    cs--;
@@ -128,28 +133,31 @@ killwholeline(void)
 	forekill(i - cs + (i != ll), fg);
     }
     clearlist = 1;
+    return 0;
 }
 
 /**/
-void
-killbuffer(void)
+int
+killbuffer(char **args)
 {
     cs = 0;
     forekill(ll, 0);
     clearlist = 1;
+    return 0;
 }
 
 /**/
-void
-backwardkillline(void)
+int
+backwardkillline(char **args)
 {
     int i = 0, n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	killline();
+	ret = killline(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	if (cs && line[cs - 1] == '\n')
@@ -160,11 +168,12 @@ backwardkillline(void)
     }
     forekill(i, 1);
     clearlist = 1;
+    return 0;
 }
 
 /**/
-void
-gosmacstransposechars(void)
+int
+gosmacstransposechars(char **args)
 {
     int cc;
 
@@ -172,19 +181,19 @@ gosmacstransposechars(void)
 	if (cs == ll || line[cs] == '\n' ||
 	    ((cs + 1 == ll || line[cs + 1] == '\n') &&
 	     (!cs || line[cs - 1] == '\n'))) {
-	    feep();
-	    return;
+	    return 1;
 	}
 	cs += (cs == 0 || line[cs - 1] == '\n') ? 2 : 1;
     }
     cc = line[cs - 2];
     line[cs - 2] = line[cs - 1];
     line[cs - 1] = cc;
+    return 0;
 }
 
 /**/
-void
-transposechars(void)
+int
+transposechars(char **args)
 {
     int cc, ct;
     int n = zmult;
@@ -194,10 +203,8 @@ transposechars(void)
 	n = -n;
     while (n--) {
 	if (!(ct = cs) || line[cs - 1] == '\n') {
-	    if (ll == cs || line[cs] == '\n') {
-		feep();
-		return;
-	    }
+	    if (ll == cs || line[cs] == '\n')
+		return 1;
 	    if (!neg)
 		cs++;
 	    ct++;
@@ -214,29 +221,28 @@ transposechars(void)
 	}
 	if (ct == ll || line[ct] == '\n')
 	    ct--;
-	if (ct < 1 || line[ct - 1] == '\n') {
-	    feep();
-	    return;
-	}
+	if (ct < 1 || line[ct - 1] == '\n')
+	    return 1;
 	cc = line[ct - 1];
 	line[ct - 1] = line[ct];
 	line[ct] = cc;
     }
+    return 0;
 }
 
 /**/
-void
-poundinsert(void)
+int
+poundinsert(char **args)
 {
     cs = 0;
-    vifirstnonblank();
+    vifirstnonblank(zlenoargs);
     if (line[cs] != '#') {
 	spaceinline(1);
 	line[cs] = '#';
 	cs = findeol();
 	while(cs != ll) {
 	    cs++;
-	    vifirstnonblank();
+	    vifirstnonblank(zlenoargs);
 	    spaceinline(1);
 	    line[cs] = '#';
 	    cs = findeol();
@@ -246,42 +252,46 @@ poundinsert(void)
 	cs = findeol();
 	while(cs != ll) {
 	    cs++;
-	    vifirstnonblank();
+	    vifirstnonblank(zlenoargs);
 	    if(line[cs] == '#')
 		foredel(1);
 	    cs = findeol();
 	}
     }
     done = 1;
+    return 0;
 }
 
 /**/
-void
-acceptline(void)
+int
+acceptline(char **args)
 {
     done = 1;
+    return 0;
 }
 
 /**/
-void
-acceptandhold(void)
+int
+acceptandhold(char **args)
 {
     pushnode(bufstack, metafy((char *)line, ll, META_DUP));
     stackcs = cs;
     done = 1;
+    return 0;
 }
 
 /**/
-void
-killline(void)
+int
+killline(char **args)
 {
     int i = 0, n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	backwardkillline();
+	ret = backwardkillline(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	if (line[cs] == '\n')
@@ -292,11 +302,12 @@ killline(void)
     }
     backkill(i, 0);
     clearlist = 1;
+    return 0;
 }
 
 /**/
-void
-killregion(void)
+int
+killregion(char **args)
 {
     if (mark > ll)
 	mark = ll;
@@ -304,11 +315,12 @@ killregion(void)
 	forekill(mark - cs, 0);
     else
 	backkill(cs - mark, 1);
+    return 0;
 }
 
 /**/
-void
-copyregionaskill(void)
+int
+copyregionaskill(char **args)
 {
     if (mark > ll)
 	mark = ll;
@@ -316,25 +328,24 @@ copyregionaskill(void)
 	cut(cs, mark - cs, 0);
     else
 	cut(mark, cs - mark, 1);
+    return 0;
 }
 
 static int kct, yankb, yanke;
 
 /**/
-void
-yank(void)
+int
+yank(char **args)
 {
     Cutbuffer buf = &cutbuf;
     int n = zmult;
 
     if (n < 0)
-	return;
+	return 1;
     if (zmod.flags & MOD_VIBUF)
 	buf = &vibuf[zmod.vibuf];
-    if (!buf->buf) {
-	feep();
-	return;
-    }
+    if (!buf->buf)
+	return 1;
     mark = cs;
     yankb = cs;
     while (n--) {
@@ -344,18 +355,17 @@ yank(void)
 	cs += buf->len;
 	yanke = cs;
     }
+    return 0;
 }
 
 /**/
-void
-yankpop(void)
+int
+yankpop(char **args)
 {
     int cc;
 
-    if (!(lastcmd & ZLE_YANK) || !kring[kct].buf) {
-	feep();
-	return;
-    }
+    if (!(lastcmd & ZLE_YANK) || !kring[kct].buf)
+	return 1;
     cs = yankb;
     foredel(yanke - yankb);
     cc = kring[kct].len;
@@ -364,17 +374,20 @@ yankpop(void)
     cs += cc;
     yanke = cs;
     kct = (kct + KRINGCT - 1) % KRINGCT;
+    return 0;
 }
 
 /**/
-void
-overwritemode(void)
+int
+overwritemode(char **args)
 {
     insmode ^= 1;
+    return 0;
 }
+
 /**/
-void
-whatcursorposition(void)
+int
+whatcursorposition(char **args)
 {
     char msg[100];
     char *s = msg;
@@ -408,18 +421,19 @@ whatcursorposition(void)
     sprintf(s, "  point %d of %d(%d%%)  column %d", cs+1, ll+1,
 	    ll ? 100 * cs / ll : 0, cs - bol);
     showmsg(msg);
+    return 0;
 }
 
 /**/
-void
-undefinedkey(void)
+int
+undefinedkey(char **args)
 {
-    feep();
+    return 1;
 }
 
 /**/
-void
-quotedinsert(void)
+int
+quotedinsert(char **args)
 {
 #ifndef HAS_TIO
     struct sgttyb sob;
@@ -433,17 +447,20 @@ quotedinsert(void)
     zsetterm();
 #endif
     if (c < 0)
-	feep();
+	return 1;
     else
-	selfinsert();
+	return selfinsert(args);
 }
 
 /**/
-void
-digitargument(void)
+int
+digitargument(char **args)
 {
     int sign = (zmult < 0) ? -1 : 1;
 
+    if (c < '0' || c > '9')
+	return 1;
+
     if (!(zmod.flags & MOD_TMULT))
 	zmod.tmult = 0;
     if (zmod.flags & MOD_NEG) {
@@ -455,26 +472,31 @@ digitargument(void)
 	zmod.tmult = zmod.tmult * 10 + sign * (c & 0xf);
     zmod.flags |= MOD_TMULT;
     prefixflag = 1;
+    return 0;
 }
 
 /**/
-void
-negargument(void)
+int
+negargument(char **args)
 {
-    if(zmod.flags & MOD_TMULT) {
-	feep();
-	return;
-    }
+    if (zmod.flags & MOD_TMULT)
+	return 1;
     zmod.tmult = -1;
     zmod.flags |= MOD_TMULT|MOD_NEG;
     prefixflag = 1;
+    return 0;
 }
 
 /**/
-void
-universalargument(void)
+int
+universalargument(char **args)
 {
     int digcnt = 0, pref = 0, minus = 1, gotk;
+    if (*args) {
+	zmod.mult = atoi(*args);
+	zmod.flags |= MOD_MULT;
+	return 0;
+    }
     while ((gotk = getkey(0)) != EOF) {
 	if (gotk == '-' && !digcnt) {
 	    minus = -1;
@@ -493,11 +515,12 @@ universalargument(void)
 	zmod.tmult *= 4;
     zmod.flags |= MOD_TMULT;
     prefixflag = 1;
+    return 0;
 }
 
 /**/
-void
-copyprevword(void)
+int
+copyprevword(char **args)
 {
     int len, t0;
 
@@ -513,18 +536,20 @@ copyprevword(void)
     spaceinline(len);
     memcpy((char *)&line[cs], (char *)&line[t0], len);
     cs += len;
+    return 0;
 }
 
 /**/
-void
-sendbreak(void)
+int
+sendbreak(char **args)
 {
     errflag = 1;
+    return 1;
 }
 
 /**/
-void
-quoteregion(void)
+int
+quoteregion(char **args)
 {
     char *str;
     size_t len;
@@ -544,11 +569,12 @@ quoteregion(void)
     memcpy((char *)&line[cs], str, len);
     mark = cs;
     cs += len;
+    return 0;
 }
 
 /**/
-void
-quoteline(void)
+int
+quoteline(char **args)
 {
     char *str;
     size_t len = ll;
@@ -557,6 +583,7 @@ quoteline(void)
     sizeline(len);
     memcpy(line, str, len);
     cs = ll = len;
+    return 0;
 }
 
 /**/
@@ -612,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;
 
@@ -637,20 +664,20 @@ executenamedcommand(char *prmt)
 	    return NULL;
 	}
 	if(cmd == Th(z_clearscreen)) {
-	    clearscreen();
+	    clearscreen(zlenoargs);
 	} else if(cmd == Th(z_redisplay)) {
-	    redisplay();
+	    redisplay(zlenoargs);
 	} else if(cmd == Th(z_viquotedinsert)) {
 	    *ptr = '^';
 	    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) ||
@@ -701,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;
@@ -723,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);
@@ -733,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 aec1c1767..9189218f1 100644
--- a/Src/Zle/zle_move.c
+++ b/Src/Zle/zle_move.c
@@ -33,64 +33,69 @@
 static int vimarkcs[27], vimarkline[27];
 
 /**/
-void
-beginningofline(void)
+int
+beginningofline(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	endofline();
+	ret = endofline(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	if (cs == 0)
-	    return;
+	    return 0;
 	if (line[cs - 1] == '\n')
 	    if (!--cs)
-		return;
+		return 0;
 	while (cs && line[cs - 1] != '\n')
 	    cs--;
     }
+    return 0;
 }
 
 /**/
-void
-endofline(void)
+int
+endofline(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	beginningofline();
+	ret = beginningofline(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	if (cs >= ll) {
 	    cs = ll;
-	    return;
+	    return 0;
 	}
 	if (line[cs] == '\n')
 	    if (++cs == ll)
-		return;
+		return 0;
 	while (cs != ll && line[cs] != '\n')
 	    cs++;
     }
+    return 0;
 }
 
 /**/
-void
-beginningoflinehist(void)
+int
+beginningoflinehist(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	endoflinehist();
+	ret = endoflinehist(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n) {
 	if (cs == 0)
@@ -103,26 +108,29 @@ beginningoflinehist(void)
 	n--;
     }
     if (n) {
-	int m = zmult;
+	int m = zmult, ret;
 
 	zmult = n;
-	uphistory();
+	ret = uphistory(args);
 	zmult = m;
 	cs = 0;
+	return ret;
     }
+    return 0;
 }
 
 /**/
-void
-endoflinehist(void)
+int
+endoflinehist(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	beginningoflinehist();
+	ret = beginningoflinehist(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n) {
 	if (cs >= ll) {
@@ -137,46 +145,51 @@ endoflinehist(void)
 	n--;
     }
     if (n) {
-	int m = zmult;
+	int m = zmult, ret;
 
 	zmult = n;
-	downhistory();
+	ret = downhistory(args);
 	zmult = m;
+	return ret;
     }
+    return 0;
 }
 
 /**/
-void
-forwardchar(void)
+int
+forwardchar(char **args)
 {
     cs += zmult;
     if (cs > ll)
 	cs = ll;
     if (cs < 0)
 	cs = 0;
+    return 0;
 }
 
 /**/
-void
-backwardchar(void)
+int
+backwardchar(char **args)
 {
     cs -= zmult;
     if (cs > ll)
 	cs = ll;
     if (cs < 0)
 	cs = 0;
+    return 0;
 }
 
 /**/
-void
-setmarkcommand(void)
+int
+setmarkcommand(char **args)
 {
     mark = cs;
+    return 0;
 }
 
 /**/
-void
-exchangepointandmark(void)
+int
+exchangepointandmark(char **args)
 {
     int x;
 
@@ -185,11 +198,12 @@ exchangepointandmark(void)
     cs = x;
     if (cs > ll)
 	cs = ll;
+    return 0;
 }
 
 /**/
-void
-vigotocolumn(void)
+int
+vigotocolumn(char **args)
 {
     int x, y;
 
@@ -202,20 +216,20 @@ vigotocolumn(void)
 	cs = y;
     if (cs < x)
 	cs = x;
+    return 0;
 }
 
 /**/
-void
-vimatchbracket(void)
+int
+vimatchbracket(char **args)
 {
     int ocs = cs, dir, ct;
     unsigned char oth, me;
 
   otog:
     if (cs == ll || line[cs] == '\n') {
-	feep();
 	cs = ocs;
-	return;
+	return 1;
     }
     switch (me = line[cs]) {
     case '{':
@@ -258,49 +272,49 @@ vimatchbracket(void)
 	    ct++;
     }
     if (cs < 0 || cs >= ll) {
-	feep();
 	cs = ocs;
+	return 1;
     } else if(dir > 0 && virangeflag)
 	cs++;
+    return 0;
 }
 
 /**/
-void
-viforwardchar(void)
+int
+viforwardchar(char **args)
 {
     int lim = findeol() - invicmdmode();
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	vibackwardchar();
+	ret = vibackwardchar(args);
 	zmult = n;
-	return;
-    }
-    if (cs >= lim) {
-	feep();
-	return;
+	return ret;
     }
+    if (cs >= lim)
+	return 1;
     while (n-- && cs < lim)
 	cs++;
+    return 0;
 }
 
 /**/
-void
-vibackwardchar(void)
+int
+vibackwardchar(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	viforwardchar();
+	ret = viforwardchar(args);
 	zmult = n;
-	return;
-    }
-    if (cs == findbol()) {
-	feep();
-	return;
+	return ret;
     }
+    if (cs == findbol())
+	return 1;
     while (n--) {
 	cs--;
 	if (cs < 0 || line[cs] == '\n') {
@@ -308,157 +322,163 @@ vibackwardchar(void)
 	    break;
 	}
     }
+    return 0;
 }
 
 /**/
-void
-viendofline(void)
+int
+viendofline(char **args)
 {
     int oldcs = cs, n = zmult;
 
-    if (n < 1) {
-	feep();
-	return;
-    }
+    if (n < 1)
+	return 1;
     while(n--) {
 	if (cs > ll) {
 	    cs = oldcs;
-	    feep();
-	    return;
+	    return 1;
 	}
 	cs = findeol() + 1;
     }
     cs--;
     lastcol = 1<<30;
+    return 0;
 }
 
 /**/
-void
-vibeginningofline(void)
+int
+vibeginningofline(char **args)
 {
     cs = findbol();
+    return 0;
 }
 
 static int vfindchar, vfinddir, tailadd;
 
 /**/
-void
-vifindnextchar(void)
+int
+vifindnextchar(char **args)
 {
     if ((vfindchar = vigetkey()) != -1) {
 	vfinddir = 1;
 	tailadd = 0;
-	virepeatfind();
+	return virepeatfind(args);
     }
+    return 1;
 }
 
 /**/
-void
-vifindprevchar(void)
+int
+vifindprevchar(char **args)
 {
     if ((vfindchar = vigetkey()) != -1) {
 	vfinddir = -1;
 	tailadd = 0;
-	virepeatfind();
+	return virepeatfind(args);
     }
+    return 1;
 }
 
 /**/
-void
-vifindnextcharskip(void)
+int
+vifindnextcharskip(char **args)
 {
     if ((vfindchar = vigetkey()) != -1) {
 	vfinddir = 1;
 	tailadd = -1;
-	virepeatfind();
+	return virepeatfind(args);
     }
+    return 1;
 }
 
 /**/
-void
-vifindprevcharskip(void)
+int
+vifindprevcharskip(char **args)
 {
     if ((vfindchar = vigetkey()) != -1) {
 	vfinddir = -1;
 	tailadd = 1;
-	virepeatfind();
+	return virepeatfind(args);
     }
+    return 1;
 }
 
 /**/
-void
-virepeatfind(void)
+int
+virepeatfind(char **args)
 {
     int ocs = cs, n = zmult;
 
-    if (!vfinddir) {
-	feep();
-	return;
-    }
+    if (!vfinddir)
+	return 1;
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	virevrepeatfind();
+	ret = virevrepeatfind(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	do
 	    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;
+	    return 1;
 	}
     }
     cs += tailadd;
     if (vfinddir == 1 && virangeflag)
 	cs++;
+    return 0;
 }
 
 /**/
-void
-virevrepeatfind(void)
+int
+virevrepeatfind(char **args)
 {
+    int ret;
+
     if (zmult < 0) {
 	zmult = -zmult;
-	virepeatfind();
+	ret = virepeatfind(args);
 	zmult = -zmult;
-	return;
+	return ret;
     }
     vfinddir = -vfinddir;
-    virepeatfind();
+    ret = virepeatfind(args);
     vfinddir = -vfinddir;
+    return ret;
 }
 
 /**/
-void
-vifirstnonblank(void)
+int
+vifirstnonblank(char **args)
 {
     cs = findbol();
     while (cs != ll && iblank(line[cs]))
 	cs++;
+    return 0;
 }
 
 /**/
-void
-visetmark(void)
+int
+visetmark(char **args)
 {
     int ch;
 
     ch = getkey(0);
-    if (ch < 'a' || ch > 'z') {
-	feep();
-	return;
-    }
+    if (ch < 'a' || ch > 'z')
+	return 1;
     ch -= 'a';
     vimarkcs[ch] = cs;
     vimarkline[ch] = histline;
+    return 0;
 }
 
 /**/
-void
-vigotomark(void)
+int
+vigotomark(char **args)
 {
     int ch;
 
@@ -466,30 +486,26 @@ vigotomark(void)
     if (ch == c)
 	ch = 26;
     else {
-	if (ch < 'a' || ch > 'z') {
-	    feep();
-	    return;
-	}
+	if (ch < 'a' || ch > 'z')
+	    return 1;
 	ch -= 'a';
     }
-    if (!vimarkline[ch]) {
-	feep();
-	return;
-    }
+    if (!vimarkline[ch])
+	return 1;
     if (curhist != vimarkline[ch] && !zle_goto_hist(vimarkline[ch], 0, 0)) {
 	vimarkline[ch] = 0;
-	feep();
-	return;
+	return 1;
     }
     cs = vimarkcs[ch];
     if (cs > ll)
 	cs = ll;
+    return 0;
 }
 
 /**/
-void
-vigotomarkline(void)
+int
+vigotomarkline(char **args)
 {
-    vigotomark();
-    vifirstnonblank();
+    vigotomark(args);
+    return vifirstnonblank(zlenoargs);
 }
diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c
index 5c4feef50..bac399e7d 100644
--- a/Src/Zle/zle_params.c
+++ b/Src/Zle/zle_params.c
@@ -69,8 +69,8 @@ static struct zleparam {
         zleunsetfn, NULL },
     { "keys", PM_ARRAY | PM_READONLY, NULL, FN(get_keys),
         zleunsetfn, NULL },
-    { "NUMERIC", PM_INTEGER, FN(set_numeric), FN(get_numeric),
-        zleunsetfn, NULL },
+    { "NUMERIC", PM_INTEGER | PM_UNSET, FN(set_numeric), FN(get_numeric),
+        unset_numeric, NULL },
     { "HISTNO", PM_INTEGER | PM_READONLY, NULL, FN(get_histno),
         zleunsetfn, NULL },
     { NULL, 0, NULL, NULL, NULL, NULL }
@@ -84,12 +84,12 @@ 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");
 
-	pm->level = locallevel;
+	pm->level = locallevel + 1;
 	pm->u.data = zp->data;
 	switch(PM_TYPE(zp->type)) {
 	    case PM_SCALAR:
@@ -107,6 +107,8 @@ makezleparams(int ro)
 		break;
 	}
 	pm->unsetfn = zp->unsetfn;
+	if ((zp->type & PM_UNSET) && (zmod.flags & MOD_MULT))
+	    pm->flags &= ~PM_UNSET;
     }
 }
 
@@ -267,6 +269,7 @@ static void
 set_numeric(Param pm, zlong x)
 {
     zmult = x;
+    zmod.flags = MOD_MULT;
 }
 
 /**/
@@ -277,6 +280,17 @@ get_numeric(Param pm)
 }
 
 /**/
+static void
+unset_numeric(Param pm, int exp)
+{
+    if (exp) {
+	stdunsetfn(pm, exp);
+	zmod.flags = 0;
+	zmult = 1;
+    }
+}
+
+/**/
 static zlong
 get_histno(Param pm)
 {
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index ac18f52f0..da0b38bfe 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -1004,23 +1004,25 @@ tcoutarg(int cap, int arg)
 }
 
 /**/
-void
-clearscreen(void)
+int
+clearscreen(char **args)
 {
     tcout(TCCLEARSCREEN);
     resetneeded = 1;
     clearflag = 0;
+    return 0;
 }
 
 /**/
-void
-redisplay(void)
+int
+redisplay(char **args)
 {
     moveto(0, 0);
     zputc('\r', shout);		/* extra care */
     tc_upcurs(lprompth - 1);
     resetneeded = 1;
     clearflag = 0;
+    return 0;
 }
 
 /**/
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index dbf982899..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,12 +335,12 @@ 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 },
 	{ 'C', bin_zle_complete, 3, 3 },
-	{ 'c', bin_zle_complete, 3, 3 },
+	{ 'R', bin_zle_refresh, 0, 1 },
 	{ 0,   bin_zle_call, 0, -1 },
     };
     struct opn const *op, *opp;
@@ -357,10 +358,6 @@ bin_zle(char *name, char **args, char *ops, int func)
 
     /* check number of arguments */
     for(n = 0; args[n]; n++) ;
-    if(!op->o && n != 1 && n != 2) {
-	zerrnam(name, "wrong number of arguments", NULL, 0);
-	return 1;
-    }
     if(n < op->min) {
 	zerrnam(name, "not enough arguments for -%c", NULL, op->o);
 	return 1;
@@ -377,7 +374,41 @@ 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']);
+    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;
+    }
+}
+
+/**/
+static int
+bin_zle_refresh(char *name, char **args, char *ops, char func)
+{
+    char *s = statusline;
+    int sl = statusll;
+
+    if (*args) {
+	statusline = *args;
+	statusll = strlen(statusline);
+    } else {
+	statusline = NULL;
+	statusll = 0;
+    }
+    zrefresh();
+
+    statusline = s;
+    statusll = sl;
     return 0;
 }
 
@@ -388,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) {
@@ -490,7 +525,8 @@ bin_zle_complete(char *name, char **args, char *ops, char func)
 	return 1;
     }
 #endif
-    t = rthingy(args[1]);
+    
+    t = rthingy((args[1][0] == '.') ? args[1] : dyncat(".", args[1]));
     cw = t->widget;
     unrefthingy(t);
     if (!cw || !(cw->flags & ZLE_ISCOMP)) {
@@ -517,31 +553,64 @@ 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",
 	    NULL, 0);
 	return 1;
     }
-    if (args[1]) {
-	modsave = zmod;
-	if (isdigit(*args[1])) {
-	    zmod.mult = atoi(args[1]);
-	    zmod.flags |= MOD_MULT;
+
+    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] == '-') {
+	    args++;
+	    break;
 	}
-	else {
-	    zmod.mult = 1;
-	    zmod.flags &= ~MOD_MULT;
+	while (*++(*args)) {
+	    switch (**args) {
+	    case 'n':
+		num = args[0][1] ? args[0]+1 : args[1];
+		if (!num) {
+		    zwarnnam(name, "number expected after -%c", NULL, **args);
+		    return 1;
+		}
+		if (!args[0][1])
+		    args++;
+		modsave = zmod;
+		saveflag = 1;
+		zmod.mult = atoi(num);
+		zmod.flags |= MOD_MULT;
+		break;
+	    case 'N':
+		modsave = zmod;
+		saveflag = 1;
+		zmod.mult = 1;
+		zmod.flags &= ~MOD_MULT;
+		break;
+	    default:
+		zwarnnam(name, "unknown option: %s", *args, 0);
+		return 1;
+	    }
 	}
+	args++;
     }
-    t = rthingy(args[0]);
+
+    t = rthingy(wname);
     PERMALLOC {
-      execzlefunc(t);
+        ret = execzlefunc(t, args);
     } LASTALLOC;
     unrefthingy(t);
-    if (args[1])
+    if (saveflag)
 	zmod = modsave;
-    return 0;
+    return ret;
 }
 
 /*******************/
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 8c2ae7bb6..65e1aeae9 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -110,20 +110,10 @@ static int movetoend;
 
 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).       */
+/* Information about menucompletion. */
 
-static Cmgroup menugrp;
-static Cmatch *menucur;
-
-/* 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.              */
-
-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.     *
@@ -376,157 +366,174 @@ static int lastend;
 #define FC_LINE   1
 #define FC_INWORD 2
 
+/* Arguments for and return value of completion widget. */
+
+static char **cfargs;
+static int cfret;
+
 /**/
-void
-completecall(void)
+int
+completecall(char **args)
 {
+    cfargs = args;
+    cfret = 0;
     compfunc = compwidget->u.comp.func;
-    compwidget->u.comp.fn();
+    if (compwidget->u.comp.fn(zlenoargs) && !cfret)
+	cfret = 1;
     compfunc = NULL;
+
+    return cfret;
 }
 
 /**/
-void
-completeword(void)
+int
+completeword(char **args)
 {
     usemenu = !!isset(MENUCOMPLETE);
     useglob = isset(GLOBCOMPLETE);
     if (c == '\t' && usetab())
-	selfinsert();
+	return selfinsert(args);
     else {
+	int ret;
 	if (lastambig == 1 && isset(BASHAUTOLIST) && !usemenu && !menucmp) {
-	    docomplete(COMP_LIST_COMPLETE);
+	    ret = docomplete(COMP_LIST_COMPLETE);
 	    lastambig = 2;
 	} else
-	    docomplete(COMP_COMPLETE);
+	    ret = docomplete(COMP_COMPLETE);
+	return ret;
     }
 }
 
 /**/
-void
-menucomplete(void)
+int
+menucomplete(char **args)
 {
     usemenu = 1;
     useglob = isset(GLOBCOMPLETE);
     if (c == '\t' && usetab())
-	selfinsert();
+	return selfinsert(args);
     else
-	docomplete(COMP_COMPLETE);
+	return docomplete(COMP_COMPLETE);
 }
 
 /**/
-void
-listchoices(void)
+int
+listchoices(char **args)
 {
     usemenu = !!isset(MENUCOMPLETE);
     useglob = isset(GLOBCOMPLETE);
-    docomplete(COMP_LIST_COMPLETE);
+    return docomplete(COMP_LIST_COMPLETE);
 }
 
 /**/
-void
-spellword(void)
+int
+spellword(char **args)
 {
     usemenu = useglob = 0;
-    docomplete(COMP_SPELL);
+    return docomplete(COMP_SPELL);
 }
 
 /**/
-void
-deletecharorlist(void)
+int
+deletecharorlist(char **args)
 {
-    Cmgroup mg = menugrp;
-    Cmatch *mc = menucur;
+    Cmgroup mg = minfo.group;
+    Cmatch *mc = minfo.cur;
+    int ret;
 
     usemenu = !!isset(MENUCOMPLETE);
     useglob = isset(GLOBCOMPLETE);
     if (cs != ll) {
 	fixsuffix();
-	deletechar();
+	ret = deletechar(args);
     } else
-	docomplete(COMP_LIST_COMPLETE);
+	ret = docomplete(COMP_LIST_COMPLETE);
 
-    menucur = mc;
-    menugrp = mg;
+    minfo.cur = mc;
+    minfo.group = mg;
+    return ret;
 }
 
 /**/
-void
-expandword(void)
+int
+expandword(char **args)
 {
     usemenu = useglob = 0;
     if (c == '\t' && usetab())
-	selfinsert();
+	return selfinsert(args);
     else
-	docomplete(COMP_EXPAND);
+	return docomplete(COMP_EXPAND);
 }
 
 /**/
-void
-expandorcomplete(void)
+int
+expandorcomplete(char **args)
 {
     usemenu = !!isset(MENUCOMPLETE);
     useglob = isset(GLOBCOMPLETE);
     if (c == '\t' && usetab())
-	selfinsert();
+	return selfinsert(args);
     else {
+	int ret;
 	if (lastambig == 1 && isset(BASHAUTOLIST) && !usemenu && !menucmp) {
-	    docomplete(COMP_LIST_COMPLETE);
+	    ret = docomplete(COMP_LIST_COMPLETE);
 	    lastambig = 2;
 	} else
-	    docomplete(COMP_EXPAND_COMPLETE);
+	    ret = docomplete(COMP_EXPAND_COMPLETE);
+	return ret;
     }
 }
 
 /**/
-void
-menuexpandorcomplete(void)
+int
+menuexpandorcomplete(char **args)
 {
     usemenu = 1;
     useglob = isset(GLOBCOMPLETE);
     if (c == '\t' && usetab())
-	selfinsert();
+	return selfinsert(args);
     else
-	docomplete(COMP_EXPAND_COMPLETE);
+	return docomplete(COMP_EXPAND_COMPLETE);
 }
 
 /**/
-void
-listexpand(void)
+int
+listexpand(char **args)
 {
     usemenu = !!isset(MENUCOMPLETE);
     useglob = isset(GLOBCOMPLETE);
-    docomplete(COMP_LIST_EXPAND);
+    return docomplete(COMP_LIST_EXPAND);
 }
 
 /**/
-void
-reversemenucomplete(void)
+int
+reversemenucomplete(char **args)
 {
-    if (!menucmp) {
-	menucomplete();
-	return;
-    }
+    if (!menucmp)
+	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;
 }
 
 /* Accepts the current completion and starts a new arg, *
  * 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) {
@@ -542,26 +549,24 @@ 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;
     }
 }
 
 /**/
-void
-acceptandmenucomplete(void)
+int
+acceptandmenucomplete(char **args)
 {
-    if (!menucmp) {
-	feep();
-	return;
-    }
+    if (!menucmp)
+	return 1;
     acceptlast();
-    menucomplete();
+    return menucomplete(args);
 }
 
 /* These are flags saying if we are completing in the command *
@@ -788,11 +793,11 @@ check_param(char *s, int set, int test)
 /* The main entry point for completion. */
 
 /**/
-static void
+static int
 docomplete(int lst)
 {
     char *s, *ol;
-    int olst = lst, chl = 0, ne = noerrs, ocs;
+    int olst = lst, chl = 0, ne = noerrs, ocs, ret = 0, omc = menucmp;
 
     if (showagain && validlist)
 	showinglist = -2;
@@ -803,7 +808,7 @@ docomplete(int lst)
     if (menucmp && lst != COMP_LIST_EXPAND && 
 	(!compwidget || compwidget == lastcompwidget)) {
 	do_menucmp(lst);
-	return;
+	return 0;
     }
     lastcompwidget = compwidget;
 
@@ -823,7 +828,7 @@ docomplete(int lst)
      * changed, do no more.                                               */
 
     if (doexpandhist())
-	return;
+	return 0;
 
     metafy_line();
 
@@ -874,8 +879,7 @@ docomplete(int lst)
 	    ll = strlen((char *) line);
 	    cs = ocs;
 	    unmetafy_line();
-	    feep();
-	    return;
+	    return 1;
 	}
 	ocs = cs;
 	cs = 0;
@@ -985,7 +989,7 @@ docomplete(int lst)
 	    inwhat = IN_CMD;
 
 	if (lst == COMP_SPELL) {
-	    char *x, *q;
+	    char *x, *q, *ox;
 
 	    for (q = s; *q; q++)
 		if (INULL(*q))
@@ -993,10 +997,11 @@ docomplete(int lst)
 	    cs = wb;
 	    foredel(we - wb);
 	    HEAPALLOC {
-		untokenize(x = dupstring(s));
+		untokenize(x = ox = dupstring(s));
 		if (*s == Tilde || *s == Equals || *s == String)
 		    *x = *s;
 		spckword(&x, 0, lincmd, 0);
+		ret = !strcmp(x, ox);
 	    } LASTALLOC;
 	    untokenize(x);
 	    inststr(x);
@@ -1032,18 +1037,30 @@ docomplete(int lst)
 				p++, skipparens(Inbrace, Outbrace, &p);
 			}
 		}
-		docompletion(s, lst, lincmd);
-	    }
+		ret = docompletion(s, lst, lincmd);
+	    } else
+		ret = !strcmp(ol, (char *) line);
 	} else
 	    /* Just do completion. */
-	    docompletion(s, lst, lincmd);
+	    ret = docompletion(s, lst, lincmd);
 	zsfree(s);
-    }
+    } else
+	ret = 1;
     /* Reset the lexer state, pop the heap. */
     lexrestore();
     popheap();
     zsfree(qword);
     unmetafy_line();
+
+    if (menucmp && !omc) {
+	struct chdata dat;
+
+	dat.matches = amatches;
+	dat.num = nmatches;
+	dat.cur = NULL;
+	runhookdef(MENUSTARTHOOK, (void *) &dat);
+    }
+    return ret;
 }
 
 /* Do completion, given that we are in the middle of a menu completion.  We *
@@ -1052,7 +1069,7 @@ docomplete(int lst)
  * insert the next completion.                                              */
 
 /**/
-static void
+void
 do_menucmp(int lst)
 {
     /* Just list the matches if the list was requested. */
@@ -1062,16 +1079,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;
 }
@@ -1425,7 +1442,6 @@ get_comp_string(void)
 		lexrestore();
 		goto start;
 	    }
-	    feep();
 	    noaliases = 0;
 	    lexrestore();
 	    LASTALLOC_RETURN NULL;
@@ -1484,11 +1500,14 @@ get_comp_string(void)
 			if (lev)
 			    lev--;
 		    }
-		wb++;
 		p = (char *) line + wb;
+		wb++;
 		if (wb && (*p == '[' || *p == '(') &&
-		    !skipparens(*p, (*p == '[' ? ']' : ')'), &p))
-			we = p - (char *) line;
+		    !skipparens(*p, (*p == '[' ? ']' : ')'), &p)) {
+			we = (p - (char *) line) - 1;
+			if (insubscr == 2)
+			    insubscr = 3;
+		}
 	    } else {
 		/* In mathematical expression, we complete parameter names  *
 		 * (even if they don't have a `$' in front of them).  So we *
@@ -1513,9 +1532,10 @@ get_comp_string(void)
 		varname = ztrdup((char *) line + i + 1);
 		line[wb - 1] = sav;
 		if ((keypm = (Param) paramtab->getnode(paramtab, varname)) &&
-		    (keypm->flags & PM_HASHED))
-		    insubscr = 2;
-		else
+		    (keypm->flags & PM_HASHED)) {
+		    if (insubscr != 3)
+			insubscr = 2;
+		} else
 		    insubscr = 1;
 	    }
 	}
@@ -1734,11 +1754,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 &&
@@ -1748,8 +1765,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) {
@@ -3776,9 +3791,10 @@ addmatches(Cadata dat, char **argv)
 		} else
 		    dat->prpre = dupstring(dat->prpre);
 		/* Select the group in which to store the matches. */
-		if (dat->group) {
+		if (dat->group || dat->ylist) {
 		    endcmgroup(NULL);
-		    begcmgroup(dat->group, (dat->aflags & CAF_NOSORT));
+		    begcmgroup((dat->ylist ? NULL : dat->group),
+			       (dat->aflags & CAF_NOSORT));
 		    if (dat->aflags & CAF_NOSORT)
 			mgroup->flags |= CGF_NOSORT;
 		} else {
@@ -3800,6 +3816,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++) {
@@ -3825,8 +3845,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,
@@ -3865,6 +3887,10 @@ addmatches(Cadata dat, char **argv)
 		set_param(dat->opar, oparl);
 	    if (dat->dpar)
 		set_param(dat->dpar, dparl);
+	    if (dat->ylist) {
+		endcmgroup(get_user_var(dat->ylist));
+		begcmgroup("default", 0);
+	    }
 	} LASTALLOC;
     } SWITCHBACKHEAPS;
 
@@ -4288,9 +4314,11 @@ gen_matches_files(int dirs, int execs, int all)
 }
 
 /**/
-static void
+static int
 docompletion(char *s, int lst, int incmd)
 {
+    int ret = 0;
+
     HEAPALLOC {
 	char *opm;
 	LinkNode n;
@@ -4328,8 +4356,8 @@ docompletion(char *s, int lst, int incmd)
 	    foredel(ll);
 	    inststr(origline);
 	    cs = origcs;
-	    feep();
 	    clearlist = 1;
+	    ret = 1;
 	    goto compend;
 	}
 	if (comppatmatch && *comppatmatch && comppatmatch != opm)
@@ -4339,17 +4367,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();
 	    }
@@ -4363,7 +4391,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))
@@ -4404,6 +4432,7 @@ docompletion(char *s, int lst, int incmd)
 	    cs = ll;
 	popheap();
     } LASTALLOC;
+    return ret;
 }
 
 /* This calls the given function for new style completion. */
@@ -4604,8 +4633,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
@@ -4623,7 +4652,20 @@ callcompfunc(char *s, char *fn)
 	makezleparams(1);
 	sfcontext = SFC_CWIDGET;
 	NEWHEAPS(compheap) {
-	    doshfunc(fn, list, NULL, 0, 1);
+	    LinkList largs = NULL;
+	    int olv = lastval;
+
+	    if (*cfargs) {
+		char **p = cfargs;
+
+		largs = newlinklist();
+		addlinknode(largs, dupstring(fn));
+		while (*p)
+		    addlinknode(largs, dupstring(*p++));
+	    }
+	    doshfunc(fn, list, largs, 0, 0);
+	    cfret = lastval;
+	    lastval = olv;
 	} OLDHEAPS;
 	sfcontext = osc;
 	endparamscope();
@@ -4673,7 +4715,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));
@@ -4710,7 +4752,7 @@ makecomplist(char *s, int incmd, int lst)
     int onm = nmatches, osi = movefd(0);
 
     /* Inside $... ? */
-    if (compfunc && (p = check_param(s, 0, NULL)))
+    if (compfunc && (p = check_param(s, 0, 0)))
 	os = s = p;
 
     /* We build a copy of the list of matchers to use to make sure that this
@@ -5281,10 +5323,10 @@ makecomplistglobal(char *os, int incmd, int lst, int flags)
         cc = &cc_default;
 	keypm = NULL;
     } else if (linwhat == IN_MATH) {
-	if (insubscr == 2) {
+	if (insubscr >= 2) {
 	    /* Inside subscript of assoc array, complete keys. */
 	    cc_dummy.mask = 0;
-	    cc_dummy.suffix = "]";
+	    cc_dummy.suffix = (insubscr == 2 ? "]" : "");
 	} else {
 	    /* Other math environment, complete paramete names. */
 	    keypm = NULL;
@@ -5590,7 +5632,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]);
@@ -5791,7 +5833,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	ic = 0;
 
     /* Check if we have to complete a parameter name... */
-    if (!incompfunc && (p = check_param(s, 1, NULL))) {
+    if (!incompfunc && (p = check_param(s, 1, 0))) {
 	s = p;
 	/* And now make sure that we complete parameter names. */
 	cc = &cc_dummy;
@@ -6267,8 +6309,15 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	maketildelist();
 	addwhat = oaw;
     }
-    if (cc->widget)
+    if (cc->widget) {
+	char **ocfa = cfargs;
+	int ocfr = cfret;
+
+	cfargs = zlenoargs;
 	callcompfunc(os, cc->widget);
+	cfargs = ocfa;
+	cfret = ocfr;
+    }
     if (cc->func) {
 	/* This handles the compctl -K flag. */
 	List list;
@@ -6313,20 +6362,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)))
@@ -6507,7 +6550,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
     if (cc->subcmd) {
 	/* Handle -l sub-completion. */
 	char **ow = clwords, *os = cmdstr, *ops = NULL;
-	int oldn = clwnum, oldp = clwpos;
+	int oldn = clwnum, oldp = clwpos, br;
 	unsigned long occ = ccont;
 	
 	ccont = CC_CCCONT;
@@ -6523,21 +6566,22 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	    erange = 1;
 	clwnum = erange - brange + 1;
 	clwpos = clwpos - brange;
-	
+	br = brange;
+
 	if (cc->subcmd[0]) {
 	    /* And probably put the command name given to the flag *
 	     * in the array.                                       */
 	    clwpos++;
 	    clwnum++;
 	    incmd = 0;
-	    ops = clwords[brange - 1];
-	    clwords[brange - 1] = cc->subcmd;
+	    ops = clwords[br - 1];
+	    clwords[br - 1] = ztrdup(cc->subcmd);
 	    cmdstr = ztrdup(cc->subcmd);
-	    clwords += brange - 1;
+	    clwords += br - 1;
 	} else {
-	    cmdstr = ztrdup(clwords[brange]);
+	    cmdstr = ztrdup(clwords[br]);
 	    incmd = !clwpos;
-	    clwords += brange;
+	    clwords += br;
 	}
 	/* Produce the matches. */
 	makecomplistcmd(s, incmd, CFN_FIRST);
@@ -6548,8 +6592,10 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	cmdstr = os;
 	clwnum = oldn;
 	clwpos = oldp;
-	if (ops)
-	    clwords[brange - 1] = ops;
+	if (ops) {
+	    zsfree(clwords[br - 1]);
+	    clwords[br - 1] = ops;
+	}
 	ccont = occ;
     }
     if (cc->substr)
@@ -6570,7 +6616,8 @@ invalidatelist(void)
     if (validlist)
 	freematches();
     lastambig = menucmp = validlist = showinglist = fromcomp = 0;
-    menucur = NULL;
+    minfo.cur = NULL;
+    minfo.asked = 0;
     compwidget = NULL;
 }
 
@@ -7337,19 +7384,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,  *
@@ -7369,7 +7417,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);
@@ -7418,19 +7467,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' *
@@ -7440,7 +7491,7 @@ do_ambiguous(void)
  * (l)stat().                                                         */
 
 /**/
-static int
+int
 ztat(char *nam, struct stat *buf, int ls)
 {
     char b[PATH_MAX], *p;
@@ -7458,7 +7509,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;
@@ -7471,39 +7522,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;
 	    }
 	}
@@ -7519,11 +7570,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      *
@@ -7559,10 +7610,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)) {
@@ -7572,8 +7623,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) {
@@ -7589,9 +7640,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)) {
@@ -7600,20 +7651,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) {
 	    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 && !menuwe) || !movetoend)
-	cs = menuend;
+    if ((menucmp && !minfo.we) || !movetoend)
+	cs = minfo.end;
+    {
+	Cmatch *om = minfo.cur;
+	struct chdata dat;
+
+	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
@@ -7643,40 +7707,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. */
@@ -7715,7 +7781,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];
@@ -7798,7 +7864,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))
@@ -7813,11 +7880,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 */
@@ -7827,6 +7890,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;
@@ -7911,8 +7990,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);
@@ -7926,7 +8006,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);
@@ -7936,6 +8018,8 @@ listmatches(void)
 	} else
 	    putc('\n', shout);
 	settyinfo(&shttyinfo);
+	if (minfo.cur)
+	    minfo.asked = 1;
     }
 
     /* Now print the matches. */
@@ -7956,7 +8040,7 @@ listmatches(void)
 		e++;
 	    }
 	}
-	if (pp) {
+	if (pp && *pp) {
 	    if (pnl) {
 		putc('\n', shout);
 		pnl = 0;
@@ -8055,6 +8139,7 @@ listmatches(void)
 	    clearflag = 0, putc('\n', shout);
     } else
 	putc('\n', shout);
+    return 0;
 }
 
 /* This is used to print expansions. */
@@ -8064,9 +8149,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;
@@ -8074,12 +8159,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;
@@ -8153,20 +8238,22 @@ doexpandhist(void)
 }
 
 /**/
-void
-magicspace(void)
+int
+magicspace(char **args)
 {
+    int ret;
     c = ' ';
-    selfinsert();
-    doexpandhist();
+    ret = selfinsert(args);
+    return !doexpandhist();
 }
 
 /**/
-void
-expandhistory(void)
+int
+expandhistory(char **args)
 {
     if (!doexpandhist())
-	feep();
+	return 1;
+    return 0;
 }
 
 static int cmdwb, cmdwe;
@@ -8210,19 +8297,17 @@ getcurcmd(void)
 }
 
 /**/
-void
-processcmd(void)
+int
+processcmd(char **args)
 {
     char *s;
     int m = zmult;
 
     s = getcurcmd();
-    if (!s) {
-	feep();
-	return;
-    }
+    if (!s)
+	return 1;
     zmult = 1;
-    pushline();
+    pushline(zlenoargs);
     zmult = m;
     inststr(bindk->nam);
     inststr(" ");
@@ -8232,11 +8317,12 @@ processcmd(void)
     } LASTALLOC;
     zsfree(s);
     done = 1;
+    return 0;
 }
 
 /**/
-void
-expandcmdpath(void)
+int
+expandcmdpath(char **args)
 {
     int oldcs = cs, na = noaliases;
     char *s, *str;
@@ -8244,16 +8330,12 @@ expandcmdpath(void)
     noaliases = 1;
     s = getcurcmd();
     noaliases = na;
-    if (!s || cmdwb < 0 || cmdwe < cmdwb) {
-	feep();
-	return;
-    }
+    if (!s || cmdwb < 0 || cmdwe < cmdwb)
+	return 1;
     str = findcmd(s, 1);
     zsfree(s);
-    if (!str) {
-	feep();
-	return;
-    }
+    if (!str)
+	return 1;
     cs = cmdwb;
     foredel(cmdwe - cmdwb);
     spaceinline(strlen(str));
@@ -8263,16 +8345,20 @@ expandcmdpath(void)
 	cs += cmdwe - cmdwb + strlen(str);
     if (cs > ll)
 	cs = ll;
+    return 0;
 }
 
 /* Extra function added by AR Iano-Fletcher. */
 /* This is a expand/complete in the vein of wash. */
 
 /**/
-void
-expandorcompleteprefix(void)
+int
+expandorcompleteprefix(char **args)
 {
+    int ret;
+
     comppref = 1;
-    expandorcomplete();
+    ret = expandorcomplete(args);
     comppref = 0;
+    return ret;
 }
diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c
index 8524fd21e..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;
 }
 
 /***************/
@@ -554,18 +546,17 @@ setlastline(void)
 /* move backwards through the change list */
 
 /**/
-void
-undo(void)
+int
+undo(char **args)
 {
     handleundo();
     do {
-	if(!curchange->prev) {
-	    feep();
-	    return;
-	}
+	if(!curchange->prev)
+	    return 1;
 	unapplychange(curchange = curchange->prev);
     } while(curchange->flags & CH_PREV);
     setlastline();
+    return 0;
 }
 
 /**/
@@ -592,19 +583,18 @@ unapplychange(struct change *ch)
 /* move forwards through the change list */
 
 /**/
-void
-redo(void)
+int
+redo(char **args)
 {
     handleundo();
     do {
-	if(!curchange->next) {
-	    feep();
-	    return;
-	}
+	if(!curchange->next)
+	    return 1;
 	applychange(curchange);
 	curchange = curchange->next;
     } while(curchange->prev->flags & CH_NEXT);
     setlastline();
+    return 0;
 }
 
 /**/
@@ -631,8 +621,8 @@ applychange(struct change *ch)
 /* vi undo: toggle between the end of the undo list and the preceding point */
 
 /**/
-void
-viundochange(void)
+int
+viundochange(char **args)
 {
     handleundo();
     if(curchange->next) {
@@ -641,6 +631,7 @@ viundochange(void)
 	    curchange = curchange->next;
 	} while(curchange->next);
 	setlastline();
+	return 0;
     } else
-	undo();
+	return undo(args);
 }
diff --git a/Src/Zle/zle_vi.c b/Src/Zle/zle_vi.c
index 5b1548e25..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);
+	/*
+	 * 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,123 +249,127 @@ dovilinerange(void)
 	    cs = findbol() - 1;
 	if (n != 1) {
 	    cs = pos;
-	    feep();
-	    return;
+	    return 1;
 	}
 	cs++;
     }
     virangeflag = 2;
+    return 0;
 }
 
 /**/
-void
-viaddnext(void)
+int
+viaddnext(char **args)
 {
     if (cs != findeol())
 	cs++;
     startvitext(1);
+    return 0;
 }
 
 /**/
-void
-viaddeol(void)
+int
+viaddeol(char **args)
 {
     cs = findeol();
     startvitext(1);
+    return 0;
 }
 
 /**/
-void
-viinsert(void)
+int
+viinsert(char **args)
 {
     startvitext(1);
+    return 0;
 }
 
 /**/
-void
-viinsertbol(void)
+int
+viinsertbol(char **args)
 {
-    vifirstnonblank();
+    vifirstnonblank(zlenoargs);
     startvitext(1);
+    return 0;
 }
 
 /**/
-void
-videlete(void)
+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--;
 	    foredel(1);
-	    vifirstnonblank();
+	    vifirstnonblank(zlenoargs);
 	}
     }
     vichgflag = 0;
+    return ret;
 }
 
 /**/
-void
-videletechar(void)
+int
+videletechar(char **args)
 {
     int n = zmult;
 
     startvichange(-1);
     /* handle negative argument */
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	vibackwarddeletechar();
+	ret = vibackwarddeletechar(args);
 	zmult = n;
-	return;
+	return ret;
     }
     /* it is an error to be on the end of line */
-    if (cs == ll || line[cs] == '\n') {
-	feep();
-	return;
-    }
+    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)
 	n = findeol() - cs;
     /* do the deletion */
     forekill(n, 0);
+    return 0;
 }
 
 /**/
-void
-vichange(void)
+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 ret;
 }
 
 /**/
-void
-visubstitute(void)
+int
+visubstitute(char **args)
 {
     int n = zmult;
 
     startvichange(1);
-    if (n < 0) {
-	feep();
-	return;
-    }
+    if (n < 0)
+	return 1;
     /* it is an error to be on the end of line */
-    if (cs == ll || line[cs] == '\n') {
-	feep();
-	return;
-    }
+    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)
@@ -385,79 +377,84 @@ visubstitute(void)
     /* do the substitution */
     forekill(n, 0);
     startvitext(1);
+    return 0;
 }
 
 /**/
-void
-vichangeeol(void)
+int
+vichangeeol(char **args)
 {
     forekill(findeol() - cs, 0);
     startvitext(1);
+    return 0;
 }
 
 /**/
-void
-vichangewholeline(void)
+int
+vichangewholeline(char **args)
 {
-    vifirstnonblank();
-    vichangeeol();
+    vifirstnonblank(args);
+    return vichangeeol(zlenoargs);
 }
 
 /**/
-void
-viyank(void)
+int
+viyank(char **args)
 {
-    int oldcs = cs, c2;
+    int oldcs = cs, c2, ret = 1;
 
     startvichange(1);
-    if ((c2 = getvirange(0)) != -1)
+    if ((c2 = getvirange(0)) != -1) {
 	cut(cs, c2 - cs, 0);
+	ret = 0;
+    }
     vichgflag = 0;
     cs = oldcs;
+    return ret;
 }
 
 /**/
-void
-viyankeol(void)
+int
+viyankeol(char **args)
 {
     int x = findeol();
 
     startvichange(-1);
-    if (x == cs) {
-	feep();
-	return;
-    }
+    if (x == cs)
+	return 1;
     cut(cs, x - cs, 0);
+    return 0;
 }
 
 /**/
-void
-viyankwholeline(void)
+int
+viyankwholeline(char **args)
 {
     int bol = findbol(), oldcs = cs;
     int n = zmult;
 
     startvichange(-1);
     if (n < 1)
-	return;
+	return 1;
     while(n--) {
      if (cs > ll) {
-	feep();
 	cs = oldcs;
-	return;
+	return 1;
      }
      cs = findeol() + 1;
     }
     vilinerange = 1;
     cut(bol, cs - bol - 1, 0);
     cs = oldcs;
+    return 0;
 }
 
 /**/
-void
-vireplace(void)
+int
+vireplace(char **args)
 {
     startvitext(0);
+    return 0;
 }
 
 /* vi-replace-chars has some oddities relating to vi-repeat-change.  In *
@@ -474,32 +471,27 @@ vireplace(void)
  * without a rewrite of the repeat code.                                */
 
 /**/
-void
-vireplacechars(void)
+int
+vireplacechars(char **args)
 {
     int ch, n = zmult;
 
     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;
+	return 1;
     }
     /* get key */
     if((ch = vigetkey()) == -1) {
 	vichgflag = 0;
-	feep();
-	return;
+	return 1;
     }
     /* do change */
     if (ch == '\r' || ch == '\n') {
@@ -513,47 +505,51 @@ vireplacechars(void)
 	cs--;
     }
     vichgflag = 0;
+    return 0;
 }
 
 /**/
-void
-vicmdmode(void)
+int
+vicmdmode(char **args)
 {
     if (invicmdmode() || selectkeymap("vicmd", 0))
-	feep();
+	return 1;
     undoing = 1;
     vichgflag = 0;
     if (cs != findbol())
 	cs--;
+    return 0;
 }
 
 /**/
-void
-viopenlinebelow(void)
+int
+viopenlinebelow(char **args)
 {
     cs = findeol();
     spaceinline(1);
     line[cs++] = '\n';
     startvitext(1);
     clearlist = 1;
+    return 0;
 }
 
 /**/
-void
-viopenlineabove(void)
+int
+viopenlineabove(char **args)
 {
     cs = findbol();
     spaceinline(1);
     line[cs] = '\n';
     startvitext(1);
     clearlist = 1;
+    return 0;
 }
 
 /**/
-void
-vioperswapcase(void)
+int
+vioperswapcase(char **args)
 {
-    int oldcs, c2;
+    int oldcs, c2, ret = 1;
 
     /* get the range */
     startvichange(1);
@@ -569,22 +565,22 @@ vioperswapcase(void)
 	}
 	/* go back to the first line of the range */
 	cs = oldcs;
+	ret = 0;
 #if 0
 	vifirstnonblank();
 #endif
     }
     vichgflag = 0;
+    return ret;
 }
 
 /**/
-void
-virepeatchange(void)
+int
+virepeatchange(char **args)
 {
     /* make sure we have a change to repeat */
-    if (!vichgbuf || vichgflag) {
-	feep();
-	return;
-    }
+    if (!vichgbuf || vichgflag)
+	return 1;
     /* restore or update the saved count and buffer */
     if (zmod.flags & MOD_MULT) {
 	lastmod.mult = zmod.mult;
@@ -598,11 +594,12 @@ virepeatchange(void)
     /* repeat the command */
     inrepeat = 1;
     ungetkeys(vichgbuf, vichgbufptr);
+    return 0;
 }
 
 /**/
-void
-viindent(void)
+int
+viindent(char **args)
 {
     int oldcs = cs, c2;
 
@@ -610,14 +607,13 @@ viindent(void)
     startvichange(1);
     if ((c2 = getvirange(0)) == -1) {
 	vichgflag = 0;
-	return;
+	return 1;
     }
     vichgflag = 0;
     /* must be a line range */
     if (!vilinerange) {
-	feep();
 	cs = oldcs;
-	return;
+	return 1;
     }
     oldcs = cs;
     /* add a tab to the beginning of each line within range */
@@ -628,12 +624,13 @@ viindent(void)
     }
     /* go back to the first line of the range */
     cs = oldcs;
-    vifirstnonblank();
+    vifirstnonblank(zlenoargs);
+    return 0;
 }
 
 /**/
-void
-viunindent(void)
+int
+viunindent(char **args)
 {
     int oldcs = cs, c2;
 
@@ -641,14 +638,13 @@ viunindent(void)
     startvichange(1);
     if ((c2 = getvirange(0)) == -1) {
 	vichgflag = 0;
-	return;
+	return 1;
     }
     vichgflag = 0;
     /* must be a line range */
     if (!vilinerange) {
-	feep();
 	cs = oldcs;
-	return;
+	return 1;
     }
     oldcs = cs;
     /* remove a tab from the beginning of each line within range */
@@ -659,12 +655,13 @@ viunindent(void)
     }
     /* go back to the first line of the range */
     cs = oldcs;
-    vifirstnonblank();
+    vifirstnonblank(zlenoargs);
+    return 0;
 }
 
 /**/
-void
-vibackwarddeletechar(void)
+int
+vibackwarddeletechar(char **args)
 {
     int n = zmult;
 
@@ -672,16 +669,16 @@ vibackwarddeletechar(void)
 	startvichange(-1);
     /* handle negative argument */
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	videletechar();
+	ret = videletechar(args);
 	zmult = n;
-	return;
+	return ret;
     }
     /* 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;
+	return 1;
     }
     /* Put argument into the acceptable range -- it is not an error to  *
      * specify a greater count than the number of available characters. */
@@ -689,41 +686,39 @@ vibackwarddeletechar(void)
 	n = cs - findbol();
     /* do the deletion */
     backkill(n, 1);
+    return 0;
 }
 
 /**/
-void
-vikillline(void)
+int
+vikillline(char **args)
 {
-    if (viinsbegin > cs) {
-	feep();
-	return;
-    }
+    if (viinsbegin > cs)
+	return 1;
     backdel(cs - viinsbegin);
+    return 0;
 }
 
 /**/
-void
-viputbefore(void)
+int
+viputbefore(char **args)
 {
     Cutbuffer buf = &cutbuf;
     int n = zmult;
 
     startvichange(-1);
     if (n < 0)
-	return;
+	return 1;
     if (zmod.flags & MOD_VIBUF)
 	buf = &vibuf[zmod.vibuf];
-    if (!buf->buf) {
-	feep();
-	return;
-    }
+    if (!buf->buf)
+	return 1;
     if(buf->flags & CUTBUFFER_LINE) {
 	cs = findbol();
 	spaceinline(buf->len + 1);
 	memcpy((char *)line + cs, buf->buf, buf->len);
 	line[cs + buf->len] = '\n';
-	vifirstnonblank();
+	vifirstnonblank(zlenoargs);
     } else {
 	while (n--) {
 	    spaceinline(buf->len);
@@ -733,30 +728,29 @@ viputbefore(void)
 	if (cs)
 	    cs--;
     }
+    return 0;
 }
 
 /**/
-void
-viputafter(void)
+int
+viputafter(char **args)
 {
     Cutbuffer buf = &cutbuf;
     int n = zmult;
 
     startvichange(-1);
     if (n < 0)
-	return;
+	return 1;
     if (zmod.flags & MOD_VIBUF)
 	buf = &vibuf[zmod.vibuf];
-    if (!buf->buf) {
-	feep();
-	return;
-    }
+    if (!buf->buf)
+	return 1;
     if(buf->flags & CUTBUFFER_LINE) {
 	cs = findeol();
 	spaceinline(buf->len + 1);
 	line[cs++] = '\n';
 	memcpy((char *)line + cs, buf->buf, buf->len);
-	vifirstnonblank();
+	vifirstnonblank(zlenoargs);
     } else {
 	if (cs != findeol())
 	    cs++;
@@ -768,20 +762,18 @@ viputafter(void)
 	if (cs)
 	    cs--;
     }
-
+    return 0;
 }
 
 /**/
-void
-vijoin(void)
+int
+vijoin(char **args)
 {
     int x;
 
     startvichange(-1);
-    if ((x = findeol()) == ll) {
-	feep();
-	return;
-    }
+    if ((x = findeol()) == ll)
+	return 1;
     cs = x + 1;
     for (x = 1; cs != ll && iblank(line[cs]); cs++, x++);
     backdel(x);
@@ -791,17 +783,18 @@ vijoin(void)
 	spaceinline(1);
 	line[cs] = ' ';
     }
+    return 0;
 }
 
 /**/
-void
-viswapcase(void)
+int
+viswapcase(char **args)
 {
     int eol, n = zmult;
 
     startvichange(-1);
     if (n < 1)
-	return;
+	return 1;
     eol = findeol();
     while (cs < eol && n--) {
 	if (islower(line[cs]))
@@ -812,11 +805,12 @@ viswapcase(void)
     }
     if (cs && cs == eol)
 	cs--;
+    return 0;
 }
 
 /**/
-void
-vicapslockpanic(void)
+int
+vicapslockpanic(char **args)
 {
     clearlist = 1;
     zbeep();
@@ -825,20 +819,19 @@ vicapslockpanic(void)
     zrefresh();
     while (!islower(getkey(0)));
     statusline = NULL;
+    return 0;
 }
 
 /**/
-void
-visetbuffer(void)
+int
+visetbuffer(char **args)
 {
     int ch;
 
     if ((zmod.flags & MOD_VIBUF) ||
 	(((ch = getkey(0)) < '1' || ch > '9') &&
-	 (ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z'))) {
-	feep();
-	return;
-    }
+	 (ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z')))
+	return 1;
     if (ch >= 'A' && ch <= 'Z')	/* needed in cut() */
 	zmod.flags |= MOD_VIAPP;
     else
@@ -846,32 +839,33 @@ visetbuffer(void)
     zmod.vibuf = tulower(ch) + (idigit(ch) ? -'1' + 26 : -'a');
     zmod.flags |= MOD_VIBUF;
     prefixflag = 1;
+    return 0;
 }
 
 /**/
-void
-vikilleol(void)
+int
+vikilleol(char **args)
 {
     int n = findeol() - cs;
 
     startvichange(-1);
     if (!n) {
 	/* error -- line already empty */
-	feep();
-	return;
+	return 1;
     }
     /* delete to end of line */
     forekill(findeol() - cs, 0);
+    return 0;
 }
 
 /**/
-void
-vipoundinsert(void)
+int
+vipoundinsert(char **args)
 {
     int oldcs = cs;
 
     startvichange(-1);
-    vifirstnonblank();
+    vifirstnonblank(zlenoargs);
     if(line[cs] != '#') {
 	spaceinline(1);
 	line[cs] = '#';
@@ -884,11 +878,12 @@ vipoundinsert(void)
 	    viinsbegin--;
 	cs = oldcs - (cs < oldcs);
     }
+    return 0;
 }
 
 /**/
-void
-viquotedinsert(void)
+int
+viquotedinsert(char **args)
 {
 #ifndef HAS_TIO
     struct sgttyb sob;
@@ -908,23 +903,23 @@ viquotedinsert(void)
 #endif
     foredel(1);
     if(c < 0)
-	feep();
+	return 1;
     else
-	selfinsert();
+	return selfinsert(args);
 }
 
 /* the 0 key in vi: continue a repeat count in the manner of      *
  * digit-argument if possible, otherwise do vi-beginning-of-line. */
 
 /**/
-void
-vidigitorbeginningofline(void)
+int
+vidigitorbeginningofline(char **args)
 {
     if(zmod.flags & MOD_TMULT)
-	digitargument();
+	return digitargument(args);
     else {
 	removesuffix();
 	invalidatelist();
-	vibeginningofline();
+	return vibeginningofline(args);
     }
 }
diff --git a/Src/Zle/zle_word.c b/Src/Zle/zle_word.c
index f446d1769..31f83a2df 100644
--- a/Src/Zle/zle_word.c
+++ b/Src/Zle/zle_word.c
@@ -31,38 +31,41 @@
 #include "zle_word.pro"
 
 /**/
-void
-forwardword(void)
+int
+forwardword(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	backwardword();
+	ret = backwardword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	while (cs != ll && iword(line[cs]))
 	    cs++;
 	if (wordflag && !n)
-	    return;
+	    return 0;
 	while (cs != ll && !iword(line[cs]))
 	    cs++;
     }
+    return 0;
 }
 
 /**/
-void
-viforwardword(void)
+int
+viforwardword(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	backwardword();
+	ret = backwardword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	if (iident(line[cs]))
@@ -72,64 +75,69 @@ viforwardword(void)
 	    while (cs != ll && !iident(line[cs]) && !iblank(line[cs]))
 		cs++;
 	if (wordflag && !n)
-	    return;
+	    return 0;
 	while (cs != ll && (iblank(line[cs]) || line[cs] == '\n'))
 	    cs++;
     }
+    return 0;
 }
 
 /**/
-void
-viforwardblankword(void)
+int
+viforwardblankword(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	vibackwardblankword();
+	ret = vibackwardblankword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	while (cs != ll && !iblank(line[cs]))
 	    cs++;
 	if (wordflag && !n)
-	    return;
+	    return 0;
 	while (cs != ll && iblank(line[cs]))
 	    cs++;
     }
+    return 0;
 }
 
 /**/
-void
-emacsforwardword(void)
+int
+emacsforwardword(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	emacsbackwardword();
+	ret = emacsbackwardword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	while (cs != ll && !iword(line[cs]))
 	    cs++;
 	if (wordflag && !n)
-	    return;
+	    return 0;
 	while (cs != ll && iword(line[cs]))
 	    cs++;
     }
+    return 0;
 }
 
 /**/
-void
-viforwardblankwordend(void)
+int
+viforwardblankwordend(char **args)
 {
     int n = zmult;
 
     if (n < 0)
-	return;
+	return 1;
     while (n--) {
 	while (cs != ll && iblank(line[cs + 1]))
 	    cs++;
@@ -138,19 +146,21 @@ viforwardblankwordend(void)
     }
     if (cs != ll && virangeflag)
 	cs++;
+    return 0;
 }
 
 /**/
-void
-viforwardwordend(void)
+int
+viforwardwordend(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	backwardword();
+	ret = backwardword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	if (iblank(line[cs + 1]))
@@ -165,19 +175,21 @@ viforwardwordend(void)
     }
     if (cs != ll && virangeflag)
 	cs++;
+    return 0;
 }
 
 /**/
-void
-backwardword(void)
+int
+backwardword(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	forwardword();
+	ret = forwardword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	while (cs && !iword(line[cs - 1]))
@@ -185,19 +197,21 @@ backwardword(void)
 	while (cs && iword(line[cs - 1]))
 	    cs--;
     }
+    return 0;
 }
 
 /**/
-void
-vibackwardword(void)
+int
+vibackwardword(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	backwardword();
+	ret = backwardword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	while (cs && iblank(line[cs - 1]))
@@ -209,19 +223,21 @@ vibackwardword(void)
 	    while (cs && !iident(line[cs - 1]) && !iblank(line[cs - 1]))
 		cs--;
     }
+    return 0;
 }
 
 /**/
-void
-vibackwardblankword(void)
+int
+vibackwardblankword(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	viforwardblankword();
+	ret = viforwardblankword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	while (cs && iblank(line[cs - 1]))
@@ -229,19 +245,21 @@ vibackwardblankword(void)
 	while (cs && !iblank(line[cs - 1]))
 	    cs--;
     }
+    return 0;
 }
 
 /**/
-void
-emacsbackwardword(void)
+int
+emacsbackwardword(char **args)
 {
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	emacsforwardword();
+	ret = emacsforwardword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	while (cs && !iword(line[cs - 1]))
@@ -249,19 +267,21 @@ emacsbackwardword(void)
 	while (cs && iword(line[cs - 1]))
 	    cs--;
     }
+    return 0;
 }
 
 /**/
-void
-backwarddeleteword(void)
+int
+backwarddeleteword(char **args)
 {
     int x = cs, n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	deleteword();
+	ret = deleteword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	while (x && !iword(line[x - 1]))
@@ -270,19 +290,18 @@ backwarddeleteword(void)
 	    x--;
     }
     backdel(cs - x);
+    return 0;
 }
 
 /**/
-void
-vibackwardkillword(void)
+int
+vibackwardkillword(char **args)
 {
     int x = cs, lim = (viinsbegin > findbol()) ? viinsbegin : findbol();
     int n = zmult;
 
-    if (n < 0) {
-	feep();
-	return;
-    }
+    if (n < 0)
+	return 1;
 /* this taken from "vibackwardword" */
     while (n--) {
 	while ((x > lim) && iblank(line[x - 1]))
@@ -295,20 +314,22 @@ vibackwardkillword(void)
 		x--;
     }
     backkill(cs - x, 1);
+    return 0;
 }
 
 /**/
-void
-backwardkillword(void)
+int
+backwardkillword(char **args)
 {
     int x = cs;
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	killword();
+	ret = killword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	while (x && !iword(line[x - 1]))
@@ -317,11 +338,12 @@ backwardkillword(void)
 	    x--;
     }
     backkill(cs - x, 1);
+    return 0;
 }
 
 /**/
-void
-upcaseword(void)
+int
+upcaseword(char **args)
 {
     int n = zmult;
     int neg = n < 0, ocs = cs;
@@ -338,11 +360,12 @@ upcaseword(void)
     }
     if (neg)
 	cs = ocs;
+    return 0;
 }
 
 /**/
-void
-downcaseword(void)
+int
+downcaseword(char **args)
 {
     int n = zmult;
     int neg = n < 0, ocs = cs;
@@ -359,11 +382,12 @@ downcaseword(void)
     }
     if (neg)
 	cs = ocs;
+    return 0;
 }
 
 /**/
-void
-capitalizeword(void)
+int
+capitalizeword(char **args)
 {
     int first, n = zmult;
     int neg = n < 0, ocs = cs;
@@ -384,20 +408,22 @@ capitalizeword(void)
     }
     if (neg)
 	cs = ocs;
+    return 0;
 }
 
 /**/
-void
-deleteword(void)
+int
+deleteword(char **args)
 {
     int x = cs;
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	backwarddeleteword();
+	ret = backwarddeleteword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	while (x != ll && !iword(line[x]))
@@ -406,20 +432,22 @@ deleteword(void)
 	    x++;
     }
     foredel(x - cs);
+    return 0;
 }
 
 /**/
-void
-killword(void)
+int
+killword(char **args)
 {
     int x = cs;
     int n = zmult;
 
     if (n < 0) {
+	int ret;
 	zmult = -n;
-	backwardkillword();
+	ret = backwardkillword(args);
 	zmult = n;
-	return;
+	return ret;
     }
     while (n--) {
 	while (x != ll && !iword(line[x]))
@@ -428,11 +456,12 @@ killword(void)
 	    x++;
     }
     forekill(x - cs, 0);
+    return 0;
 }
 
 /**/
-void
-transposewords(void)
+int
+transposewords(char **args)
 {
     int p1, p2, p3, p4, x = cs;
     char *temp, *pp;
@@ -448,22 +477,16 @@ transposewords(void)
 	    x = cs;
 	    while (x && line[x - 1] != '\n' && !iword(line[x]))
 		x--;
-	    if (!x || line[x - 1] == '\n') {
-		feep();
-		return;
-	    }
+	    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();
-	    return;
-	}
+	if (!p3)
+	    return 1;
 	for (p2 = p3; p2 && !iword(line[p2 - 1]); p2--);
-	if (!p2) {
-	    feep();
-	    return;
-	}
+	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);
@@ -474,4 +497,5 @@ transposewords(void)
     }
     if (neg)
 	cs = ocs;
+    return 0;
 }
diff --git a/Src/builtin.c b/Src/builtin.c
index d74d9cd88..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
 };
 
@@ -351,7 +353,8 @@ execbuiltin(LinkList args, Builtin bn)
 
     /* display execution trace information, if required */
     if (xtr) {
-	fprintf(stderr, "%s%s", (prompt4) ? prompt4 : "", name);
+	printprompt4();
+	fprintf(stderr, "%s", name);
 	if (xarg)
 	    fprintf(stderr, " %s", xarg);
 	while (*oargv)
@@ -1349,7 +1352,7 @@ fclist(FILE *f, int n, int r, int D, int d, int first, int last, struct asgment
 	fclistdone = 1;
 
     ent = gethistent(first, first < last? GETHIST_DOWNWARD : GETHIST_UPWARD);
-    if (!ent || ent->histnum < first || ent->histnum > last) {
+    if (!ent || (first < last? ent->histnum > last : ent->histnum < last)) {
 	if (first == last)
 	    zwarnnam("fc", "no such event: %d", NULL, first);
 	else
@@ -1491,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)
@@ -1501,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",
@@ -1518,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;
@@ -1604,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));
@@ -1677,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;
@@ -3073,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;
 }
 
@@ -3121,7 +3134,7 @@ bin_read(char *name, char **args, char *ops, int func)
     char *reply, *readpmpt;
     int bsiz, c = 0, gotnl = 0, al = 0, first, nchars = 1, bslash;
     int haso = 0;	/* true if /dev/tty has been opened specially */
-    int isem = !strcmp(term, "emacs");
+    int isem = !strcmp(term, "emacs"), izle = zleactive && getkeyptr;
     char *buf, *bptr, *firstarg, *zbuforig;
     LinkList readll = newlinklist();
 
@@ -3146,33 +3159,37 @@ bin_read(char *name, char **args, char *ops, int func)
 	return compctlread(name, args, ops, reply);
 
     if ((ops['k'] && !ops['u'] && !ops['p']) || ops['q']) {
-	if (SHTTY == -1) {
-	    /* need to open /dev/tty specially */
-	    SHTTY = open("/dev/tty", O_RDWR|O_NOCTTY);
-	    haso = 1;
-	}
-	/* We should have a SHTTY opened by now. */
-	if (SHTTY == -1) {
-	    /* Unfortunately, we didn't. */
-	    fprintf(stderr, "not interactive and can't open terminal\n");
-	    fflush(stderr);
-	    return 1;
+	if (!zleactive) {
+	    if (SHTTY == -1) {
+		/* need to open /dev/tty specially */
+		SHTTY = open("/dev/tty", O_RDWR|O_NOCTTY);
+		haso = 1;
+	    }
+	    /* We should have a SHTTY opened by now. */
+	    if (SHTTY == -1) {
+		/* Unfortunately, we didn't. */
+		fprintf(stderr, "not interactive and can't open terminal\n");
+		fflush(stderr);
+		return 1;
+	    }
+	    if (unset(INTERACTIVE))
+		gettyinfo(&shttyinfo);
+	    /* attach to the tty */
+	    attachtty(mypgrp);
+	    if (!isem && ops['k'])
+		setcbreak();
+	    readfd = SHTTY;
 	}
-	if (unset(INTERACTIVE))
-	    gettyinfo(&shttyinfo);
-	/* attach to the tty */
-	attachtty(mypgrp);
-	if (!isem && ops['k'])
-	    setcbreak();
-	readfd = SHTTY;
     } else if (ops['u'] && !ops['p']) {
 	/* -u means take input from the specified file descriptor. *
 	 * -up means take input from the coprocess.                */
 	for (readfd = 9; readfd && !ops[readfd + '0']; --readfd);
-    } else if (ops['p'])
+	izle = 0;
+    } else if (ops['p']) {
 	readfd = coprocin;
-    else
-	readfd = 0;
+	izle = 0;
+    } else
+	readfd = izle = 0;
 
     /* handle prompt */
     if (firstarg) {
@@ -3196,18 +3213,25 @@ bin_read(char *name, char **args, char *ops, int func)
 	bptr = buf = (char *)zalloc(nchars+1);
 
 	do {
-	    /* If read returns 0, is end of file */
-	    if ((val = read(readfd, bptr, nchars)) <= 0)
-		break;
+	    if (izle) {
+		if ((val = getkeyptr(0)) < 0)
+		    break;
+		*bptr++ = (char) val;
+		nchars--;
+	    } else {
+		/* If read returns 0, is end of file */
+		if ((val = read(readfd, bptr, nchars)) <= 0)
+		    break;
 	    
-	    /* decrement number of characters read from number required */
-	    nchars -= val;
+		/* decrement number of characters read from number required */
+		nchars -= val;
 
-	    /* increment pointer past read characters */
-	    bptr += val;
+		/* increment pointer past read characters */
+		bptr += val;
+	    }
 	} while (nchars > 0);
 	
-	if (!ops['u'] && !ops['p']) {
+	if (!izle && !ops['u'] && !ops['p']) {
 	    /* dispose of result appropriately, etc. */
 	    if (isem)
 		while (val > 0 && read(SHTTY, &d, 1) == 1 && d != '\n');
@@ -3236,14 +3260,19 @@ bin_read(char *name, char **args, char *ops, int func)
 	readbuf[1] = '\0';
 
 	/* get, and store, reply */
-	readbuf[0] = ((char)getquery(NULL, 0)) == 'y' ? 'y' : 'n';
+	if (izle) {
+	    int key = getkeyptr(0);
 
-	/* dispose of result appropriately, etc. */
-	if (haso) {
-	    close(SHTTY);
-	    SHTTY = -1;
-	}
+	    readbuf[0] = (key == 'y' ? 'y' : 'n');
+	} else {
+	    readbuf[0] = ((char)getquery(NULL, 0)) == 'y' ? 'y' : 'n';
 
+	    /* dispose of result appropriately, etc. */
+	    if (haso) {
+		close(SHTTY);
+		SHTTY = -1;
+	    }
+	}
 	if (ops['e'] || ops['E'])
 	    printf("%s\n", readbuf);
 	if (!ops['e'])
@@ -3265,7 +3294,7 @@ bin_read(char *name, char **args, char *ops, int func)
 	buf = bptr = (char *)zalloc(bsiz = 64);
 	/* get input, a character at a time */
 	while (!gotnl) {
-	    c = zread();
+	    c = zread(izle);
 	    /* \ at the end of a line indicates a continuation *
 	     * line, except in raw mode (-r option)            */
 	    if (bslash && c == '\n') {
@@ -3355,7 +3384,7 @@ bin_read(char *name, char **args, char *ops, int func)
     bslash = 0;
     if (!gotnl)
 	for (;;) {
-	    c = zread();
+	    c = zread(izle);
 	    /* \ at the end of a line introduces a continuation line, except in
 	    raw mode (-r option) */
 	    if (bslash && c == '\n') {
@@ -3419,10 +3448,15 @@ bin_read(char *name, char **args, char *ops, int func)
 
 /**/
 static int
-zread(void)
+zread(int izle)
 {
     char cc, retry = 0;
 
+    if (izle) {
+	int c = getkeyptr(0);
+
+	return (c < 0 ? EOF : c);
+    }
     /* use zbuf if possible */
     if (zbuf) {
 	/* If zbuf points to anything, it points to the next character in the
@@ -3618,7 +3652,7 @@ bin_trap(char *name, char **argv, char *ops, int func)
     arg = *argv++;
     if (!*arg)
 	l = NULL;
-    else if (!(l = parse_string(arg))) {
+    else if (!(l = parse_string(arg, 0))) {
 	zwarnnam(name, "couldn't parse trap command", NULL, 0);
 	return 1;
     }
diff --git a/Src/exec.c b/Src/exec.c
index 5cfe3e3ef..5ae4f0d33 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -133,14 +133,17 @@ static int doneps4;
 
 /**/
 List
-parse_string(char *s)
+parse_string(char *s, int ln)
 {
     List l;
+    int oldlineno = lineno;
 
     lexsave();
-    inpush(s, 0, NULL);
+    inpush(s, (ln ? INP_LINENO : 0), NULL);
     strinbeg(0);
+    lineno = ln ? 1 : -1;
     l = parse_list();
+    lineno = oldlineno;
     strinend();
     inpop();
     lexrestore();
@@ -301,7 +304,9 @@ execcursh(Cmd cmd, LinkList args, int flags)
 {
     if (!list_pipe)
 	deletejob(jobtab + thisjob);
+    cmdpush(CS_CURSH);
     execlist(cmd->u.list, 1, flags & CFLAG_EXEC);
+    cmdpop();
 
     return lastval;
 }
@@ -354,7 +359,9 @@ zexecve(char *pth, char **argv)
 		    if (execvebuf[1] == '!') {
 			for (t0 = 0; t0 != ct; t0++)
 			    if (execvebuf[t0] == '\n')
-				execvebuf[t0] = '\0';
+				break;
+			while (inblank(execvebuf[t0]))
+			    execvebuf[t0--] = '\0';
 			execvebuf[POUNDBANGLIMIT] = '\0';
 			for (ptr = execvebuf + 2; *ptr && *ptr == ' '; ptr++);
 			for (ptr2 = ptr; *ptr && *ptr != ' '; ptr++);
@@ -676,7 +683,7 @@ execstring(char *s, int dont_change_job, int exiting)
     List list;
 
     pushheap();
-    if ((list = parse_string(s)))
+    if ((list = parse_string(s, 0)))
 	execlist(list, dont_change_job, exiting);
     popheap();
 }
@@ -694,8 +701,8 @@ execlist(List list, int dont_change_job, int exiting)
 {
     Sublist slist;
     static int donetrap;
-    int ret, cj;
-    int old_pline_level, old_list_pipe;
+    int ret, cj, csp;
+    int old_pline_level, old_list_pipe, oldlineno;
     /*
      * ERREXIT only forces the shell to exit if the last command in a &&
      * or || fails.  This is the case even if an earlier command is a
@@ -707,6 +714,7 @@ execlist(List list, int dont_change_job, int exiting)
     cj = thisjob;
     old_pline_level = pline_level;
     old_list_pipe = list_pipe;
+    oldlineno = lineno;
 
     if (sourcelevel && unset(SHINSTDIN))
 	pline_level = list_pipe = 0;
@@ -718,6 +726,7 @@ execlist(List list, int dont_change_job, int exiting)
 	 * called once for each sublist that fails.          */
 	donetrap = 0;
 	slist = list->left;
+	csp = cmdsp;
 
 	/* Loop through code followed by &&, ||, or end of sublist. */
 	while (slist) {
@@ -744,6 +753,7 @@ execlist(List list, int dont_change_job, int exiting)
 			goto sublist_done;
 		    }
 		}
+		cmdpush(CS_CMDAND);
 		break;
 	    case ORNEXT:
 		/* If the return code is zero, we skip pipelines until *
@@ -760,12 +770,14 @@ execlist(List list, int dont_change_job, int exiting)
 			goto sublist_done;
 		     }
 		}
+		cmdpush(CS_CMDOR);
 		break;
 	    }
 	    slist = slist->right;
 	}
 sublist_done:
 
+	cmdsp = csp;
 	noerrexit = oldnoerrexit;
 
 	if (sigtrapped[SIGDEBUG])
@@ -794,6 +806,7 @@ sublist_done:
 
     pline_level = old_pline_level;
     list_pipe = old_list_pipe;
+    lineno = oldlineno;
     if (dont_change_job)
 	thisjob = cj;
 }
@@ -888,6 +901,9 @@ execpline(Sublist l, int how, int last1)
 		    DPUTS(!list_pipe_pid, "invalid list_pipe_pid");
 		    addproc(list_pipe_pid, list_pipe_text);
 
+		    if (!jn->procs->next)
+			jn->gleader = mypgrp;
+
 		    for (pn = jobtab[jn->other].procs; pn; pn = pn->next)
 			if (WIFSTOPPED(pn->status))
 			    break;
@@ -901,12 +917,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) {
+		if (list_pipe_child || pline_level) {
 		    jn->stat |= STAT_NOPRINT;
 		    makerunning(jn);
 		}
@@ -917,8 +935,11 @@ execpline(Sublist l, int how, int last1)
 		    jn->stat & STAT_DONE &&
 		    lastval2 & 0200)
 		    killpg(mypgrp, lastval2 & ~0200);
-		if ((list_pipe || last1) && !list_pipe_child &&
-		    jn->stat & STAT_STOPPED) {
+		if ((list_pipe || last1 || pline_level) &&
+		    !list_pipe_child && 
+		    ((jn->stat & STAT_STOPPED) ||
+		     (list_pipe_job && pline_level &&
+		      (jobtab[list_pipe_job].stat & STAT_STOPPED)))) {
 		    pid_t pid;
 		    int synch[2];
 
@@ -944,21 +965,28 @@ execpline(Sublist l, int how, int last1)
 			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;
-			killpg(jobtab[list_pipe_job].gleader, SIGSTOP);
+			/* 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)
+			    setpgrp(0L, mypgrp = jobtab[list_pipe_job].gleader);
 			close(synch[1]);
 			kill(getpid(), SIGSTOP);
 			list_pipe = 0;
 			list_pipe_child = 1;
+			opts[INTERACTIVE] = 0;
 			break;
 		    }
 		}
@@ -975,7 +1003,8 @@ execpline(Sublist l, int how, int last1)
 		jn->stat |= STAT_NOPRINT;
 		killjb(jobtab + pj, lastval & ~0200);
 	    }
-	    if (list_pipe_child || (list_pipe && (jn->stat & STAT_DONE)))
+	    if (list_pipe_child || ((list_pipe || pline_level) &&
+				    (jn->stat & STAT_DONE)))
 		deletejob(jn);
 	    thisjob = pj;
 
@@ -998,19 +1027,19 @@ execpline2(Pline pline, int how, int input, int output, int last1)
 {
     pid_t pid;
     int pipes[2];
-    int oldlineno;
 
     if (breaks || retflag)
 	return;
 
-    oldlineno = lineno;
-    lineno = pline->left->lineno;
+    if (pline->left->lineno >= 0)
+	lineno = pline->left->lineno;
 
-    if (pline_level == 1)
+    if (pline_level == 1) {
 	if (!sfcontext)
 	    strcpy(list_pipe_text, getjobtext((void *) pline->left));
 	else
 	    list_pipe_text[0] = '\0';
+    }
     if (pline->type == END)
 	execcmd(pline->left, input, output, how, last1 ? 1 : 2);
     else {
@@ -1054,15 +1083,15 @@ execpline2(Pline pline, int how, int input, int output, int last1)
 	if (pline->right) {
 	    /* if another execpline() is invoked because the command is *
 	     * a list it must know that we're already in a pipeline     */
+	    cmdpush(CS_PIPE);
 	    list_pipe = 1;
 	    execpline2(pline->right, how, pipes[0], output, last1);
 	    list_pipe = old_list_pipe;
+	    cmdpop();
 	    zclose(pipes[0]);
 	    subsh_close = -1;
 	}
     }
-
-    lineno = oldlineno;
 }
 
 /* make the argv array */
@@ -1079,7 +1108,7 @@ makecline(LinkList list)
     argv = 2 + (char **) ncalloc((countlinknodes(list) + 4) * sizeof(char *));
     if (isset(XTRACE)) {
 	if (!doneps4)
-	    fprintf(stderr, "%s", (prompt4) ? prompt4 : "");
+	    printprompt4();
 
 	for (node = firstnode(list); node; incnode(node)) {
 	    *ptr++ = (char *)getdata(node);
@@ -1283,7 +1312,7 @@ addvars(LinkList l, int export)
 
     xtr = isset(XTRACE);
     if (xtr && nonempty(l)) {
-	fprintf(stderr, "%s", prompt4 ? prompt4 : "");
+	printprompt4();
 	doneps4 = 1;
     }
 
@@ -2179,8 +2208,9 @@ entersubsh(int how, int cl, int fake)
 		    attachtty(jobtab[thisjob].gleader);
 	    }
 	}
-	else if (!jobtab[thisjob].gleader ||
-		 (setpgrp(0L, jobtab[thisjob].gleader) == -1)) {
+	else if (!(list_pipe || list_pipe_child || pline_level > 1) &&
+		 (!jobtab[thisjob].gleader ||
+		  setpgrp(0L, jobtab[thisjob].gleader) == -1)) {
 	    jobtab[thisjob].gleader = getpid();
 	    if (list_pipe_job != thisjob &&
 		!jobtab[list_pipe_job].gleader)
@@ -2322,7 +2352,7 @@ getoutput(char *cmd, int qt)
     Cmd c;
     Redir r;
 
-    if (!(list = parse_string(cmd)))
+    if (!(list = parse_string(cmd, 0)))
 	return NULL;
     if (list != &dummy_list && !list->right && !list->left->flags &&
 	list->left->type == END && list->left->left->type == END &&
@@ -2451,7 +2481,7 @@ parsecmd(char *cmd)
 	return NULL;
     }
     *str = '\0';
-    if (str[1] || !(list = parse_string(cmd + 2))) {
+    if (str[1] || !(list = parse_string(cmd + 2, 0))) {
 	zerr("parse error in process substitution", NULL, 0);
 	return NULL;
     }
@@ -2661,7 +2691,8 @@ execcond(Cmd cmd, LinkList args, int flags)
 {
     int stat;
     if (isset(XTRACE)) {
-	fprintf(stderr, "%s[[", prompt4 ? prompt4 : "");
+	printprompt4();
+	fprintf(stderr, "[[");
 	tracingcond++;
     }
     stat = !evalcond(cmd->u.cond);
@@ -2682,8 +2713,10 @@ execarith(Cmd cmd, LinkList args, int flags)
     char *e;
     zlong val = 0;
 
-    if (isset(XTRACE))
-	fprintf(stderr, "%s((", prompt4 ? prompt4 : "");
+    if (isset(XTRACE)) {
+	printprompt4();
+	fprintf(stderr, "((");
+    }
     if (args)
 	while ((e = (char *) ugetnode(args))) {
 	    if (isset(XTRACE))
@@ -2759,21 +2792,22 @@ static void
 execshfunc(Cmd cmd, Shfunc shf, LinkList args)
 {
     LinkList last_file_list = NULL;
+    unsigned char *ocs;
+    int ocsp;
 
     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;
-	fprintf(stderr, "%s", prompt4 ? prompt4 : prompt4);
+	printprompt4();
 	if (args)
 	    for (lptr = firstnode(args); lptr; incnode(lptr)) {
 		if (lptr != firstnode(args))
@@ -2783,8 +2817,14 @@ execshfunc(Cmd cmd, Shfunc shf, LinkList args)
 	fputc('\n', stderr);
 	fflush(stderr);
     }
-
+    ocs = cmdstack;
+    ocsp = cmdsp;
+    cmdstack = (unsigned char *) zalloc(CMDSTACKSZ);
+    cmdsp = 0;
     doshfunc(shf->nam, shf->funcdef, args, shf->flags, 0);
+    free(cmdstack);
+    cmdstack = ocs;
+    cmdsp = ocsp;
 
     if (!list_pipe)
 	deletefilelist(last_file_list);
@@ -2842,9 +2882,8 @@ doshfunc(char *name, List list, LinkList doshargs, int flags, int noreturnval)
  * was executed.                                            */
 {
     char **tab, **x, *oargv0 = NULL;
-    int xexittr, newexittr, oldzoptind, oldlastval;
-    void *xexitfn, *newexitfn;
-    char saveopts[OPT_SIZE];
+    int oldzoptind, oldlastval;
+    char saveopts[OPT_SIZE], *oldscriptname;
     int obreaks = breaks;
 
     HEAPALLOC {
@@ -2852,14 +2891,12 @@ doshfunc(char *name, List list, LinkList doshargs, int flags, int noreturnval)
 	if (trapreturn < 0)
 	    trapreturn--;
 	oldlastval = lastval;
-	xexittr = sigtrapped[SIGEXIT];
-	if (xexittr & ZSIG_FUNC)
-	    xexitfn = shfunctab->removenode(shfunctab, "TRAPEXIT");
-	else
-	    xexitfn = sigfuncs[SIGEXIT];
-	sigtrapped[SIGEXIT] = 0;
-	sigfuncs[SIGEXIT] = NULL;
+
+	starttrapscope();
+
 	tab = pparams;
+	oldscriptname = scriptname;
+	scriptname = name;
 	oldzoptind = zoptind;
 	zoptind = 1;
 
@@ -2902,6 +2939,7 @@ doshfunc(char *name, List list, LinkList doshargs, int flags, int noreturnval)
 	    argzero = oargv0;
 	}
 	zoptind = oldzoptind;
+	scriptname = oldscriptname;
 	pparams = tab;
 
 	if (isset(LOCALOPTIONS)) {
@@ -2916,27 +2954,7 @@ doshfunc(char *name, List list, LinkList doshargs, int flags, int noreturnval)
 	    opts[LOCALOPTIONS] = saveopts[LOCALOPTIONS];
 	}
 
-	/*
-	 * The trap '...' EXIT runs in the environment of the caller,
-	 * so remember it here but run it after resetting the
-	 * traps for the parent.
-	 */
-	newexittr = sigtrapped[SIGEXIT];
-	newexitfn = sigfuncs[SIGEXIT];
-	if (newexittr & ZSIG_FUNC)
-	    shfunctab->removenode(shfunctab, "TRAPEXIT");
-
-	sigtrapped[SIGEXIT] = xexittr;
-	if (xexittr & ZSIG_FUNC) {
-	    shfunctab->addnode(shfunctab, ztrdup("TRAPEXIT"), xexitfn);
-	    sigfuncs[SIGEXIT] = ((Shfunc) xexitfn)->funcdef;
-	} else
-	    sigfuncs[SIGEXIT] = (List) xexitfn;
-
-	if (newexitfn) {
-	    dotrapargs(SIGEXIT, &newexittr, newexitfn);
-	    freestruct(newexitfn);
-	}
+	endtrapscope();
 
 	if (trapreturn < -1)
 	    trapreturn++;
@@ -3008,7 +3026,7 @@ getfpfunc(char *s)
 		    d[len] = '\0';
 		    d = metafy(d, len, META_REALLOC);
 		    HEAPALLOC {
-			r = parse_string(d);
+			r = parse_string(d, 1);
 		    } LASTALLOC;
 		    return r;
 		} else
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/hist.c b/Src/hist.c
index 49dac724c..edf74009e 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -888,9 +888,14 @@ prepnexthistent(int histnum)
     else {
 	he = hist_ring->down;
 	if (isset(HISTEXPIREDUPSFIRST) && !(he->flags & HIST_DUP)) {
+	    int max_unique_ct = getiparam("SAVEHIST");
 	    do {
+		if (max_unique_ct-- <= 0) {
+		    he = hist_ring->down;
+		    break;
+		}
 		he = he->down;
-	    } while (he != hist_ring->down && !(he->flags & HIST_DUP)) ;
+	    } while (he != hist_ring->down && !(he->flags & HIST_DUP));
 	    if (he != hist_ring->down) {
 		he->up->down = he->down;
 		he->down->up = he->up;
@@ -989,7 +994,7 @@ hend(void)
 	}
 #endif
 	/* get rid of pesky \n which we've already nulled out */
-	if (!chline[chwords[chwordpos-2]])
+	if (chwordpos > 1 && !chline[chwords[chwordpos-2]])
 	    chwordpos -= 2;
 	/* strip superfluous blanks, if desired */
 	if (isset(HISTREDUCEBLANKS))
diff --git a/Src/init.c b/Src/init.c
index 89cbf2a8b..51c2b88a5 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -75,6 +75,11 @@ int tclen[TC_COUNT];
 /**/
 int tclines, tccolumns, hasam;
 
+/* Pointer to read-key function from zle */
+
+/**/
+int (*getkeyptr) _((int));
+
 #ifdef DEBUG
 /* depth of allocation type stack */
 
@@ -194,6 +199,7 @@ parseargs(char **argv)
 
     /* loop through command line options (begins with "-" or "+") */
     while (*argv && (**argv == '-' || **argv == '+')) {
+	char *args = *argv;
 	action = (**argv == '-');
 	if(!argv[0][1])
 	    *argv = "--";
@@ -228,6 +234,14 @@ parseargs(char **argv)
 		    restricted = action;
 		else
 		    dosetopt(optno, action, 1);
+              break;
+	    } else if (isspace(**argv)) {
+		/* zsh's typtab not yet set, have to use ctype */
+		while (*++*argv)
+		    if (!isspace(**argv)) {
+			zerr("bad option string: `%s'", args, 0);
+			exit(1);
+		    }
 		break;
 	    } else {
 	    	if (!(optno = optlookupc(**argv))) {
@@ -376,7 +390,16 @@ init_io(void)
 #ifdef JOB_CONTROL
     /* If interactive, make the shell the foreground process */
     if (opts[MONITOR] && interact && (SHTTY != -1)) {
-	attachtty(GETPGRP());
+      /* Since we now sometimes execute programs in the process group
+       * of the parent shell even when using job-control, we have to
+       * make sure that we run in our own process group. Otherwise if
+       * we are called from a program that doesn't put us in our own
+       * group a SIGTSTP that we ignore might stop our parent process.
+       * Instead of the two calls below we once had:
+       *   attachtty(GETPGRP());
+       */
+	attachtty(getpid());
+	setpgrp(0L, 0L);
 	if ((mypgrp = GETPGRP()) > 0) {
 	    while ((ttpgrp = gettygrp()) != -1 && ttpgrp != mypgrp) {
 		sleep(1);
@@ -530,12 +553,15 @@ setupvals(void)
     int i;
 #endif
 
+    getkeyptr = NULL;
+
+    lineno = 1;
     noeval = 0;
     curhist = 0;
     histsiz = DEFAULT_HISTSIZE;
     inithist();
 
-    cmdstack = (unsigned char *) zalloc(256);
+    cmdstack = (unsigned char *) zalloc(CMDSTACKSZ);
     cmdsp = 0;
 
     bangchar = '!';
@@ -869,7 +895,7 @@ source(char *s)
     SHIN = tempfd;
     bshin = fdopen(SHIN, "r");
     subsh  = 0;
-    lineno = 0;
+    lineno = 1;
     loops  = 0;
     dosetopt(SHINSTDIN, 0, 1);
     scriptname = s;
diff --git a/Src/input.c b/Src/input.c
index b4a6fe22d..d05c75c0b 100644
--- a/Src/input.c
+++ b/Src/input.c
@@ -186,6 +186,8 @@ ingetc(void)
 	    inbufct--;
 	    if (itok(lastc = STOUC(*inbufptr++)))
 		continue;
+	    if (((inbufflags & INP_LINENO) || !strin) && lastc == '\n')
+		lineno++;
 	    return lastc;
 	}
 
@@ -279,23 +281,20 @@ inputline(void)
 	zputs(ingetcline, stderr);
 	fflush(stderr);
     }
-    if (*ingetcline && ingetcline[strlen(ingetcline) - 1] == '\n') {
-	/* We've now read a complete line. */
-	lineno++;
-	if (interact && isset(SUNKEYBOARDHACK) && isset(SHINSTDIN) &&
-	    SHTTY != -1 && *ingetcline && ingetcline[1] &&
-	    ingetcline[strlen(ingetcline) - 2] == '`') {
-	    /* Junk an unmatched "`" at the end of the line. */
-	    int ct;
-	    char *ptr;
-
-	    for (ct = 0, ptr = ingetcline; *ptr; ptr++)
-		if (*ptr == '`')
-		    ct++;
-	    if (ct & 1) {
-		ptr[-2] = '\n';
-		ptr[-1] = '\0';
-	    }
+    if (*ingetcline && ingetcline[strlen(ingetcline) - 1] == '\n' &&
+	interact && isset(SUNKEYBOARDHACK) && isset(SHINSTDIN) &&
+	SHTTY != -1 && *ingetcline && ingetcline[1] &&
+	ingetcline[strlen(ingetcline) - 2] == '`') {
+	/* Junk an unmatched "`" at the end of the line. */
+	int ct;
+	char *ptr;
+
+	for (ct = 0, ptr = ingetcline; *ptr; ptr++)
+	    if (*ptr == '`')
+		ct++;
+	if (ct & 1) {
+	    ptr[-2] = '\n';
+	    ptr[-1] = '\0';
 	}
     }
     isfirstch = 1;
@@ -359,6 +358,8 @@ inungetc(int c)
 	    inbufptr--;
 	    inbufct++;
 	    inbufleft++;
+	    if (((inbufflags & INP_LINENO) || !strin) && c == '\n')
+		lineno--;
 	}
 #ifdef DEBUG
         else if (!(inbufflags & INP_CONT)) {
diff --git a/Src/jobs.c b/Src/jobs.c
index 31861e284..a50b84fd7 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);
@@ -181,8 +185,9 @@ update_job(Job jn)
 
 		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;
 		    }
 	    }
diff --git a/Src/lex.c b/Src/lex.c
index 8dc836329..33b6598b9 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -219,7 +219,7 @@ lexsave(void)
     ls->hlinesz = hlinesz;
     ls->cstack = cmdstack;
     ls->csp = cmdsp;
-    cmdstack = (unsigned char *)zalloc(256);
+    cmdstack = (unsigned char *)zalloc(CMDSTACKSZ);
     ls->tok = tok;
     ls->isnewlin = isnewlin;
     ls->tokstr = tokstr;
@@ -1150,7 +1150,7 @@ gettokstr(int c, int sub)
 	    cmdpush(CS_BQUOTE);
 	    SETPARBEGIN
 	    inquote = 0;
-	    while ((c = hgetc()) != '`' && !lexstop)
+	    while ((c = hgetc()) != '`' && !lexstop) {
 		if (c == '\\') {
 		    c = hgetc();
 		    if (c != '\n') {
@@ -1171,6 +1171,7 @@ gettokstr(int c, int sub)
 			    ALLOWHIST
 		    }
 		}
+	    }
 	    if (inquote)
 		ALLOWHIST
 	    cmdpop();
diff --git a/Src/loop.c b/Src/loop.c
index 07a7a56b0..16e4ff314 100644
--- a/Src/loop.c
+++ b/Src/loop.c
@@ -71,6 +71,7 @@ execfor(Cmd cmd, LinkList args, int flags)
     lastval = 0;
     loops++;
     pushheap();
+    cmdpush(CS_FOR);
     for (;;) {
 	if (node->condition) {
 	    str = dupstring(node->condition);
@@ -119,6 +120,7 @@ execfor(Cmd cmd, LinkList args, int flags)
 	freeheap();
     }
     popheap();
+    cmdpop();
     loops--;
     return lastval;
 }
@@ -147,6 +149,7 @@ execselect(Cmd cmd, LinkList args, int flags)
     loops++;
     lastval = 0;
     pushheap();
+    cmdpush(CS_SELECT);
     inp = fdopen(dup((SHTTY == -1) ? 0 : SHTTY), "r");
     more = selectlist(args, 0);
     for (;;) {
@@ -201,6 +204,7 @@ execselect(Cmd cmd, LinkList args, int flags)
 	    break;
     }
   done:
+    cmdpop();
     popheap();
     fclose(inp);
     loops--;
@@ -279,6 +283,7 @@ execwhile(Cmd cmd, LinkList args, int flags)
     node = cmd->u.whilecmd;
     oldval = 0;
     pushheap();
+    cmdpush(node->cond ? CS_UNTIL : CS_WHILE);
     loops++;
     for (;;) {
 	noerrexit = 1;
@@ -304,6 +309,7 @@ execwhile(Cmd cmd, LinkList args, int flags)
 	}
 	oldval = lastval;
     }
+    cmdpop();
     popheap();
     loops--;
     return lastval;
@@ -322,6 +328,7 @@ execrepeat(Cmd cmd, LinkList args, int flags)
     }
     count = atoi(peekfirst(args));
     pushheap();
+    cmdpush(CS_REPEAT);
     loops++;
     while (count--) {
 	execlist(cmd->u.list, 1, 0);
@@ -337,6 +344,7 @@ execrepeat(Cmd cmd, LinkList args, int flags)
 	    break;
 	}
     }
+    cmdpop();
     popheap();
     loops--;
     return lastval;
@@ -347,7 +355,7 @@ int
 execif(Cmd cmd, LinkList args, int flags)
 {
     struct ifcmd *node;
-    int olderrexit;
+    int olderrexit, s = 0;
     List *i, *t;
 
     olderrexit = noerrexit;
@@ -358,17 +366,22 @@ execif(Cmd cmd, LinkList args, int flags)
     if (!noerrexit)
 	noerrexit = 1;
     while (*i) {
+	cmdpush(s ? CS_ELIF : CS_IF);
 	execlist(*i, 1, 0);
+	cmdpop();
 	if (!lastval)
 	    break;
+	s = 1;
 	i++;
 	t++;
     }
     noerrexit = olderrexit;
 
-    if (*t)
+    if (*t) {
+	cmdpush(*i ? (s ? CS_ELIFTHEN : CS_IFTHEN) : CS_ELSE);
 	execlist(*t, 1, flags & CFLAG_EXEC);
-    else
+	cmdpop();
+    } else
 	lastval = 0;
 
     return lastval;
@@ -393,6 +406,7 @@ execcase(Cmd cmd, LinkList args, int flags)
     lastval = 0;
 
     if (node) {
+	cmdpush(CS_CASE);
 	while (*p) {
 	    char *pat = dupstring(*p + 1);
 	    singsub(&pat);
@@ -405,6 +419,7 @@ execcase(Cmd cmd, LinkList args, int flags)
 	    p++;
 	    l++;
 	}
+	cmdpop();
     }
     return lastval;
 }
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/options.c b/Src/options.c
index 504ccdf7e..2eb73690e 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -143,6 +143,7 @@ static struct optname optns[] = {
 {NULL, "listbeep",	      OPT_ALL,			 LISTBEEP},
 {NULL, "listtypes",	      OPT_ALL,			 LISTTYPES},
 {NULL, "localoptions",	      OPT_EMULATE|OPT_KSH,	 LOCALOPTIONS},
+{NULL, "localtraps",	      OPT_EMULATE|OPT_KSH,	 LOCALTRAPS},
 {NULL, "login",		      OPT_SPECIAL,		 LOGINSHELL},
 {NULL, "longlistjobs",	      0,			 LONGLISTJOBS},
 {NULL, "magicequalsubst",     0,			 MAGICEQUALSUBST},
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/parse.c b/Src/parse.c
index 520181750..626ffc982 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -890,6 +890,8 @@ par_subsh(Cmd c)
 static void
 par_funcdef(Cmd c)
 {
+    int oldlineno = lineno;
+    lineno = 0;
     nocorrect = 1;
     incmdpos = 0;
     yylex();
@@ -912,13 +914,17 @@ par_funcdef(Cmd c)
     if (tok == INBRACE) {
 	yylex();
 	c->u.list = par_list();
-	if (tok != OUTBRACE)
+	if (tok != OUTBRACE) {
+	    lineno += oldlineno;
 	    YYERRORV;
+	}
 	yylex();
     } else if (unset(SHORTLOOPS)) {
+	lineno += oldlineno;
 	YYERRORV;
     } else
 	c->u.list = par_list1();
+    lineno += oldlineno;
 }
 
 /*
@@ -1023,6 +1029,8 @@ par_simple(Cmd c)
 		c->redir = newlinklist();
 	    par_redir(c->redir);
 	} else if (tok == INOUTPAR) {
+	    int oldlineno = lineno;
+	    lineno = 0;
 	    incmdpos = 1;
 	    cmdpush(CS_FUNCDEF);
 	    yylex();
@@ -1033,6 +1041,7 @@ par_simple(Cmd c)
 		c->u.list = par_list();
 		if (tok != OUTBRACE) {
 		    cmdpop();
+		    lineno += oldlineno;
 		    YYERROR;
 		}
 		yylex();
@@ -1051,6 +1060,7 @@ par_simple(Cmd c)
 	    }
 	    cmdpop();
 	    c->type = FUNCDEF;
+	    lineno += oldlineno;
 	} else
 	    break;
 	isnull = 0;
diff --git a/Src/signals.c b/Src/signals.c
index f69da0140..e9851b57a 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -602,6 +602,57 @@ killjb(Job jn, int sig)
     return err;
 }
 
+/*
+ * List for saving traps.  We don't usually have that many traps
+ * at once, so just use a linked list.
+ */
+struct savetrap {
+    int sig, flags, local;
+    void *list;
+};
+
+static LinkList savetraps;
+
+/* Flag to unsettrap not to free the structs, which we're keeping */
+
+/**/
+int notrapfree;
+
+/*
+ * Save the current trap and unset it.
+ */
+
+static void
+dosavetrap(int sig, int level)
+{
+    struct savetrap *st;
+    st = (struct savetrap *)zalloc(sizeof(*st));
+    st->sig = sig;
+    st->local = level;
+    notrapfree++;
+    if ((st->flags = sigtrapped[sig]) & ZSIG_FUNC) {
+	/*
+	 * Get the old function: this assumes we haven't added
+	 * the new one yet.
+	 */
+	char func[20];
+	sprintf(func, "TRAP%s", sigs[sig]);
+	st->list = shfunctab->removenode(shfunctab, func);
+    } else {
+	st->list = sigfuncs[sig];
+	unsettrap(sig);
+    }
+    notrapfree--;
+    PERMALLOC {
+	if (!savetraps)
+	    savetraps = newlinklist();
+	/*
+	 * Put this at the front of the list
+	 */
+	insertlinknode(savetraps, (LinkNode)savetraps, st);
+    } LASTALLOC;
+}
+
 /**/
 int
 settrap(int sig, List l)
@@ -612,7 +663,15 @@ settrap(int sig, List l)
         zerr("can't trap SIG%s in interactive shells", sigs[sig], 0);
         return 1;
     }
-    if (sigfuncs[sig])
+    /*
+     * Note that we save the trap here even if there isn't an existing
+     * one, to aid in removing this one.  However, if there's
+     * already one at the current locallevel we just overwrite it.
+     */
+    if (isset(LOCALTRAPS) && locallevel &&
+	(!sigtrapped[sig] || locallevel > (sigtrapped[sig] >> ZSIG_SHIFT))) {
+	dosavetrap(sig, locallevel);
+    } else if (sigfuncs[sig])
 	unsettrap(sig);
     sigfuncs[sig] = l;
     if (!l) {
@@ -632,6 +691,12 @@ settrap(int sig, List l)
             sig != SIGCHLD)
             install_handler(sig);
     }
+    /*
+     * Note that introducing the locallevel does not affect whether
+     * sigtrapped[sig] is zero or not, i.e. a test without a mask
+     * works just the same.
+     */
+    sigtrapped[sig] |= (locallevel << ZSIG_SHIFT);
     return 0;
 }
 
@@ -645,6 +710,19 @@ unsettrap(int sig)
 	(jobbing && (sig == SIGTTOU || sig == SIGTSTP || sig == SIGTTIN))) {
         return;
     }
+    if (isset(LOCALTRAPS) && locallevel &&
+	sigtrapped[sig] && locallevel > (sigtrapped[sig] >> ZSIG_SHIFT)) {
+	/*
+	 * This calls unsettrap recursively to do any dirty work, so
+	 * make sure this bit doesn't happen:  a bit messy, but hard
+	 * to avoid.
+	 */
+	int oldlt = opts[LOCALTRAPS];
+	opts[LOCALTRAPS] = 0;
+	dosavetrap(sig, locallevel);
+	opts[LOCALTRAPS] = oldlt;
+	return;
+    }
     sigtrapped[sig] = 0;
     if (sig == SIGINT && interact) {
 	/* PWS 1995/05/16:  added test for interactive, also noholdintr() *
@@ -659,6 +737,8 @@ unsettrap(int sig)
 #endif
              sig != SIGCHLD)
         signal_default(sig);
+    if (notrapfree)
+	return;
     if (trapped & ZSIG_FUNC) {
 	char func[20];
 	HashNode hn;
@@ -672,6 +752,83 @@ unsettrap(int sig)
     }
 }
 
+/**/
+void
+starttrapscope(void)
+{
+    /*
+     * SIGEXIT needs to be restored at the current locallevel,
+     * so give it the next higher one.
+     */
+    if (sigtrapped[SIGEXIT])
+	dosavetrap(SIGEXIT, locallevel+1);
+}
+
+/*
+ * Reset traps after the end of a function: must be called after
+ * endparamscope() so that the locallevel has been decremented.
+ */
+
+/**/
+void
+endtrapscope(void)
+{
+    LinkNode ln;
+    struct savetrap *st;
+    int exittr;
+    void *exitfn = NULL;
+
+    /*
+     * Remember the exit trap, but don't run it until
+     * after all the other traps have been put back.
+     */
+    if ((exittr = sigtrapped[SIGEXIT])) {
+	notrapfree++;
+	if (exittr & ZSIG_FUNC) {
+	    exitfn = shfunctab->removenode(shfunctab, "TRAPEXIT");
+	} else {
+	    exitfn = sigfuncs[SIGEXIT];
+	    unsettrap(SIGEXIT);
+	}
+	notrapfree--;
+    }
+
+    if (savetraps) {
+	while ((ln = firstnode(savetraps)) &&
+	       (st = (struct savetrap *) ln->dat) &&
+	       st->local > locallevel) {
+	    int sig = st->sig;
+
+	    remnode(savetraps, ln);
+
+	    if (sigtrapped[sig])
+		unsettrap(sig);
+	    if (st->flags) {
+		List list = (st->flags & ZSIG_FUNC) ?
+		    ((Shfunc) st->list)->funcdef : (List) st->list;
+		/* prevent settrap from saving this */
+		int oldlt = opts[LOCALTRAPS];
+		opts[LOCALTRAPS] = 0;
+		settrap(sig, list);
+		opts[LOCALTRAPS] = oldlt;
+		if ((sigtrapped[sig] = st->flags) & ZSIG_FUNC)
+		    shfunctab->addnode(shfunctab, ((Shfunc)st->list)->nam,
+				       (Shfunc) st->list);
+	    }
+	    zfree(st, sizeof(*st));
+	}
+    }
+
+    if (exittr) {
+	dotrapargs(SIGEXIT, &exittr, (exittr & ZSIG_FUNC) ?
+		   ((Shfunc)exitfn)->funcdef : (List) exitfn);
+	if (exittr & ZSIG_FUNC)
+	    shfunctab->freenode((HashNode)exitfn);
+	else
+	    freestruct(exitfn);
+    }
+}
+
 /* Execute a trap function for a given signal, possibly
  * with non-standard sigtrapped & sigfuncs values
  */
diff --git a/Src/subst.c b/Src/subst.c
index d85d73f58..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));
@@ -1944,6 +1949,8 @@ dstackent(char ch, int val)
     else
 	for (end=NULL, n=firstnode(dirstack); n && val; val--, n=nextnode(n));
     if (n == end) {
+	if (backwards && !val)
+	    return pwd;
 	if (isset(NOMATCH))
 	    zerr("not enough directory stack entries.", NULL, 0);
 	return NULL;
diff --git a/Src/utils.c b/Src/utils.c
index ba4f3dc9d..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);
@@ -774,6 +775,23 @@ checkmailpath(char **s)
     }
 }
 
+/* This prints the XTRACE prompt. */
+
+/**/
+void
+printprompt4(void)
+{
+    if (prompt4) {
+	int l;
+	char *s = dupstring(prompt4);
+
+	unmetafy(s, &l);
+	s = unmetafy(promptexpand(metafy(s, l, META_NOALLOC), 0, NULL, NULL), &l);
+
+	fprintf(stderr, "%s", s);
+    }
+}
+
 /**/
 void
 freestr(void *a)
@@ -2116,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);
 }
 
@@ -3150,6 +3173,22 @@ dquotedzputs(char const *s, FILE *stream)
 }
 #endif
 
+/*
+ * Decode a key string, turning it into the literal characters.
+ * The length is returned in len.
+ * fromwhere determines how the processing works.
+ *   0:  Don't handle keystring, just print-like escapes.
+ *       Expects misc to be present.
+ *   1:  Handle Emacs-like \C-X arguments etc., but not ^X
+ *       Expects misc to be present.
+ *   2:  Handle ^X as well as emacs-like keys; don't handle \c
+ *       for no newlines.
+ *   3:  As 1, but don't handle \c.
+ *   4:  Do $'...' quoting.  Overwrites the existing string instead of
+ *       zhalloc'ing 
+ *   5:  As 2, but \- is special.  Expects misc to be defined.
+ */
+
 /**/
 char *
 getkeystring(char *s, int *len, int fromwhere, int *misc)
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 72779e24a..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
@@ -83,6 +87,7 @@ gethashnode2
 gethparam
 getintvalue
 getiparam
+getkeyptr
 getkeystring
 getlinknode
 getpermtext
@@ -206,6 +211,7 @@ resetneeded
 restoredir
 reswdtab
 retflag
+runhookdef
 runshfunc
 scancountparams
 scanhashtable
diff --git a/Src/zsh.h b/Src/zsh.h
index e7b6e5ae0..45691039e 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -226,6 +226,7 @@ enum {
 #define INP_HIST      (1<<2)	/* expanding history                       */
 #define INP_CONT      (1<<3)	/* continue onto previously stacked input  */
 #define INP_ALCONT    (1<<4)	/* stack is continued from alias expn.     */
+#define INP_LINENO    (1<<5)    /* update line number                      */
 
 /* Flags for metafy */
 #define META_REALLOC	0
@@ -277,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;
 
 
 /********************************/
@@ -886,6 +888,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 {
@@ -958,12 +976,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)
@@ -1201,6 +1220,7 @@ enum {
     LISTBEEP,
     LISTTYPES,
     LOCALOPTIONS,
+    LOCALTRAPS,
     LOGINSHELL,
     LONGLISTJOBS,
     MAGICEQUALSUBST,
@@ -1358,7 +1378,8 @@ struct ttyinfo {
 /* Definitions for the %_ prompt escape */
 /****************************************/
 
-#define cmdpush(X) if (!(cmdsp >= 0 && cmdsp < 256)) {;} else cmdstack[cmdsp++]=(X)
+#define CMDSTACKSZ 256
+#define cmdpush(X) if (!(cmdsp >= 0 && cmdsp < CMDSTACKSZ)) {;} else cmdstack[cmdsp++]=(X)
 #ifdef DEBUG
 # define cmdpop()  if (cmdsp <= 0) { \
 			fputs("BUG: cmdstack empty\n", stderr); \
@@ -1491,9 +1512,13 @@ struct heap {
 
 /* These used in the sigtrapped[] array */
 
-#define ZSIG_TRAPPED	(1<<0)
-#define ZSIG_IGNORED	(1<<1)
-#define ZSIG_FUNC	(1<<2)
+#define ZSIG_TRAPPED	(1<<0)	/* Signal is trapped */
+#define ZSIG_IGNORED	(1<<1)	/* Signal is ignored */
+#define ZSIG_FUNC	(1<<2)	/* Trap is a function, not an eval list */
+/* Mask to get the above flags */
+#define ZSIG_MASK	(ZSIG_TRAPPED|ZSIG_IGNORED|ZSIG_FUNC)
+/* No. of bits to shift local level when storing in sigtrapped */
+#define ZSIG_SHIFT	3
 
 /**********************************/
 /* Flags to third argument of zle */
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/configure.in b/configure.in
index 59dcdb991..f134c97c1 100644
--- a/configure.in
+++ b/configure.in
@@ -598,7 +598,8 @@ main() { return sizeof(ino_t) < 8; }
   if test "x$enable_lfs" != xno -o $zsh_cv_off_t_is_64_bit = yes \
   -o $zsh_cv_ino_t_is_64_bit = yes; then
     AC_CACHE_CHECK(if compiler has a 64 bit type, zsh_cv_64_bit_type,
-    [if test "x$enable_lfs" != xyes -a "x$enable_lfs" != xno; then
+    [if test "x$enable_lfs" != x -a "x$enable_lfs" != xyes \
+     -a "x$enable_lfs" != xno; then
       zsh_64_BIT_TYPE(${enable_lfs}, zsh_cv_64_bit_type, force)
      else
        zsh_64_BIT_TYPE(long long, zsh_cv_64_bit_type)