about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Completion/Commands/_correct_filename2
-rw-r--r--Completion/Commands/_correct_word17
-rw-r--r--Completion/Core/compinit111
-rw-r--r--Completion/Core/compinstall415
-rw-r--r--Config/version.mk4
-rw-r--r--Doc/Zsh/builtins.yo146
-rw-r--r--Doc/Zsh/compsys.yo55
-rw-r--r--Doc/Zsh/expn.yo43
-rw-r--r--Doc/Zsh/options.yo57
-rw-r--r--Src/Builtins/rlimits.c4
-rw-r--r--Src/Zle/comp1.c3
-rw-r--r--Src/Zle/iwidgets.list1
-rw-r--r--Src/Zle/zle_hist.c318
-rw-r--r--Src/Zle/zle_keymap.c2
-rw-r--r--Src/Zle/zle_main.c42
-rw-r--r--Src/Zle/zle_move.c15
-rw-r--r--Src/Zle/zle_thingy.c17
-rw-r--r--Src/Zle/zle_tricky.c63
-rw-r--r--Src/Zle/zle_utils.c12
-rw-r--r--Src/builtin.c71
-rw-r--r--Src/exec.c6
-rw-r--r--Src/hashtable.c176
-rw-r--r--Src/hist.c747
-rw-r--r--Src/init.c17
-rw-r--r--Src/jobs.c34
-rw-r--r--Src/module.c39
-rw-r--r--Src/options.c13
-rw-r--r--Src/params.c25
-rw-r--r--Src/signals.c5
-rw-r--r--Src/subst.c25
-rw-r--r--Src/text.c3
-rw-r--r--Src/utils.c61
-rw-r--r--Src/zsh.export6
-rw-r--r--Src/zsh.h36
-rw-r--r--Util/reporter6
-rw-r--r--acconfig.h3
-rw-r--r--aczsh.m441
-rw-r--r--configure.in23
-rw-r--r--patchlist.txt66
39 files changed, 1871 insertions, 859 deletions
diff --git a/Completion/Commands/_correct_filename b/Completion/Commands/_correct_filename
index 53ed6d113..baef38edf 100644
--- a/Completion/Commands/_correct_filename
+++ b/Completion/Commands/_correct_filename
@@ -1,4 +1,4 @@
-#compdef -k complete-word \C-xc
+#compdef -k complete-word \C-xC
 
 # Function to correct a filename.  Can be used as a completion widget,
 # or as a function in its own right, in which case it will print the
diff --git a/Completion/Commands/_correct_word b/Completion/Commands/_correct_word
index db3023860..d0abcd4fe 100644
--- a/Completion/Commands/_correct_word
+++ b/Completion/Commands/_correct_word
@@ -3,10 +3,23 @@
 # Simple completion front-end implementing spelling correction.
 # The maximum number of errors is set quite high, and
 # the numeric prefix can be used to specify a different value.
+#
+# If configurations keys with the prefix `correctword_' are
+# given they override those starting with `correct_'.
 
 local oca="$compconfig[correct_accept]"
-compconfig[correct_accept]=6n
+local oco="$compconfig[correct_original]"
+local ocp="$compconfig[correct_prompt]"
+local oci="$compconfig[correct_insert]"
+
+compconfig[correct_accept]="${compconfig[correctword_accept]-6n}"
+compconfig[correct_original]="${compconfig[correctword_original]-$oco}"
+compconfig[correct_prompt]="${compconfig[correctword_prompt]-$ocp}"
+compconfig[correct_insert]="${compconfig[correctword_insert]}"
 
 _main_complete _correct
 
-compconfig[correct_accept]=$oca
+compconfig[correct_accept]="$oca"
+compconfig[correct_original]="$oco"
+compconfig[correct_prompt]="$ocp"
+compconfig[correct_insert]="$oci"
diff --git a/Completion/Core/compinit b/Completion/Core/compinit
index 72d5fde28..77f918502 100644
--- a/Completion/Core/compinit
+++ b/Completion/Core/compinit
@@ -42,14 +42,32 @@
 
 # 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.
 
-if [[ "$1" = -d ]]; then
-  _i_autodump=1
-  shift
-else
-  _i_autodump=0
+_i_fdir=''
+_i_dumpfile=''
+_i_autodump=0
+while [[ $1 = -[df] ]]; do
+  if [[ "$1" = -d ]]; then
+    _i_autodump=1
+    shift
+    if [[ -n "$1" && "$1" != -[df] ]]; then
+      _i_dumpfile="$1"
+      shift
+    fi
+  elif [[ "$1" = -f ]]; then
+    # Used by compinstall to pass down directory where compinit was found
+    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.
@@ -63,9 +81,19 @@ _patcomps=()
 typeset -A compconfig
 
 # Standard initialisation for `compconfig'.
-
-(( $# )) && compconfig[dumpfile]="$1"
-[[ -z "$compconfig[dumpfile]" ]] && compconfig[dumpfile]="$0.dump"
+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
+  # Now we're stuck, but we'd better do something.
+  compconfig[dumpfile]="$HOME/.compinit.dump"
+fi
 
 compconfig[correct_accept]=2n
 compconfig[correct_prompt]='correct to:'
@@ -180,9 +208,15 @@ compdef() {
     *)
       # For commands store the function name in the `_comps'
       # associative array, command names as keys.
-      for i; do
-        [[ -z "$new" || "${+_comps[$i]}" -eq 0 ]] && _comps[$i]="$func"
-      done
+      if [[ -z "$new" ]]; then
+	for i; do
+	  _comps[$i]="$func"
+	done
+      else
+        for i; do
+          [[ "${+_comps[$i]}" -eq 0 ]] && _comps[$i]="$func"
+        done
+      fi
       ;;
     esac
   else
@@ -265,8 +299,24 @@ compconf() {
 # Now we automatically make the definition files autoloaded.
 
 typeset -U _i_files
-_i_files=( ${^~fpath}/_(|*[^~])(N:t) )
-_i_initname=$0
+_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
+      # Add all the Completion subdirectories
+      fpath=(${_i_fdir:h}/*(/) $fpath)
+    elif [[ -d $_i_fdir/Core ]]; then
+      # Likewise
+      fpath=(${_i_fdir}/*(/) $fpath)
+    else
+      fpath=($_i_fdir $fpath)
+    fi
+    _i_files=( ${^~fpath:/.}/_(|*[^~])(N:t) )
+  fi
+fi
+
 _i_done=''
 
 # If we have a dump file, load it.
@@ -286,38 +336,45 @@ if [[ -z "$_i_done" ]]; then
       read -rA _i_line < $_i_file
       _i_tag=$_i_line[1]
       shift _i_line
-      if [[ $_i_tag = '#compdef' ]]; then
+      case $_i_tag in
+      (\#compdef)
 	if [[ $_i_line[1] = -[pk] ]]; then
 	  compdef ${_i_line[1]}a "${_i_file:t}" "${(@)_i_line[2,-1]}"
 	else
 	  compdef -na "${_i_file:t}" "${_i_line[@]}"
 	fi
-      elif [[ $_i_tag = '#autoload' ]]; then
+	;;
+      (\#autoload)
 	autoload ${_i_file:t}
-      fi
+	;;
+      esac
     done
   done
 
   bindkey |
     while read -rA _i_line; do
-      if [[ "$_i_line[2]" = complete-word ||
-	"$_i_line[2]" = delete-char-or-list ||
-	"$_i_line[2]" = expand-or-complete ||
-	"$_i_line[2]" = expand-or-complete-prefix ||
-	"$_i_line[2]" = list-choices ||
-	"$_i_line[2]" = menu-complete ||
-	"$_i_line[2]" = menu-expand-or-complete ||
-	"$_i_line[2]" = reverse-menu-complete ]]; then
+      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]
-      fi
+	;;
+      esac
     done
 
   unset _i_dir _i_line _i_file _i_tag
 
   # If autodumping was requested, do it now.
 
-  (( _i_autodump )) && builtin . ${_i_initname:h}/compdump
+  if [[ -n ${_i_fdir} && $_i_autodump = 1 ]]; then
+    builtin . ${_i_fdir}/compdump
+  fi
 fi
 
-unset _i_files _i_initname _i_done _i_autodump
+unset _i_files _i_initname _i_done _i_autodump _i_fdir _i_dumpfile
diff --git a/Completion/Core/compinstall b/Completion/Core/compinstall
index d96121cf2..d494ba302 100644
--- a/Completion/Core/compinstall
+++ b/Completion/Core/compinstall
@@ -1,49 +1,129 @@
-# This script is to be run by a user to setup the new function based
+# This script is to be run by a user to set up the new function based
 # completion system.  The functions themselves are assumed to be already
 # available in some directory; they should have been installed with the
 # the shell (except we haven't written that yet).
 #
-# Run it as a script under zsh and answer the questions.
-# You can run it as `zsh compinstall $FPATH' and it will be able to check
-# your function path for the completion functions.
+# Source this script (e.g. `. /path/compinstall') 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 instead.
-#
-# You can use this script to modify what compinstall previously
-# added to ~/.zshrc.
+# 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
+# instead.  It doesn't matter if .zshrc didn't exist before.  If your
+# .zshrc usually exits before the end, then you should take the code added
+# by compinstall and put it (including the comment lines at the start and
+# end) at the point you want it to be executed.  If you run compinstall
+# again it will find and replace those lines, so you can use this script to
+# modify what compinstall previously added to ~/.zshrc.
 #
 # It is safe to abort with ^C any time you are being prompted for
 # information; your .zshrc will not be altered.
 #
 # To do:
-#  - Maybe this should be sourced, then it can check the user's current
-#    setup better.  But then there is a potentially horrendous option
-#    setting/resetting problem.  (Maybe we need another way of doing that.)
 #  - Should probably offer to set different options for _approximate than
 #    for _complete if both are used.
 #  - Could add code for setting other completers and options.
 #  - Could add keys for context-sensitive help.
-#  - Probably should allow a set of directories to be added to $fpath,
-#    like Core, Base, etc.
 
-# In case a startup script changed options
+# 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
 
-[[ -n $1 ]] && FPATH=$1
+TRAPINT() { 
+  unsetopt ${(k)_ci_options[(R)off]}
+  setopt ${(k)_ci_options[(R)on]}
 
-for f in $fpath; do
-  if [[ $f != . && -f $f/compinit && -f $f/compdump ]]; then
-    fdir=$f
-    break
+  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
-done
+  return 0
+}
+
+
+# Look for the defaults.
+_ci_startline='# The following lines were added by compinstall'
+_ci_endline='# End of lines added by compinstall'
+
+_ci_ifile=${ZDOTDIR:-~}/.zshrc
+_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.
+  _ci_compconf=0
+  sed -n "/^$_ci_startline/,/^$_ci_endline/p" $_ci_ifile |
+  while read -rA _ci_line; do
+    if (( $_ci_compconf )); then
+      # parse a compconf component as first argument
+      if [[ $_ci_line[-1] == \\ ]]; then
+	_ci_line=(${_ci_line[1,-2]})
+      else
+	_ci_compconf=0
+      fi
+      if [[ $_ci_line[1] = *=* ]]; then
+	_ci_f="${${_ci_line[*]}#*=}"
+	if [[ $_ci_f = \'*\' ]]; then
+	  # strip quotes
+	  _ci_f=${_ci_f[2,-2]//\'\\\'\'/\'}
+	fi
+	_ci_defaults[${_ci_line[1]%%\=*}]=$_ci_f
+      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]
+      [[ $_ci_line[-2] = -d ]] && _ci_dumpfile=$_ci_line[-1]
+    elif [[ $_ci_line[1] = compconf ]]; then
+      # parse a compconf component as second argument (should be completer)
+      [[ $_ci_line[2] = completer=* ]] &&
+        _ci_completer=${_ci_line[2]#completer=}
+      [[ $_ci_line[-1] == \\ ]] && _ci_compconf=1
+      _ci_existing="${_ci_existing}$_ci_line
+"
+    elif [[ $_ci_line[1] != \#* ]]; then
+      if [[ -z $_ci_warn ]]; then
+	_ci_warn=1
+	print "Warning:  existing lines in compinstall setup not understood:"
+      fi
+      print - $_ci_line
+      _ci_existing="${_ci_existing}$_ci_line
+"
+    fi
+  done
+fi
+
+
+# Find out where the completion functions are kept.
 
-if [[ -z $fdir ]]; then
+if [[ -z $_ci_fdir || ! -f $_ci_f/compinit || ! -f $_ci_f/compdump ]]; then
+  for _ci_f in $fpath; do
+    if [[ $_ci_f != . && -f $_ci_f/compinit && -f $_ci_f/compdump ]]; then
+      _ci_fdir=$_ci_f
+      break
+    fi
+  done
+fi
+
+if [[ -z $_ci_fdir || ! -d ${~_ci_fdir} ]]; then
   print "Trying to find where the completion functions are..."
-  if [[ $0 = */* && -f $0:h/compinit && -f $0:h/compdump ]]; then
-    fdir=$0:h
+  if [[ $0 = */* && -o functionargzero &&
+        -f $0:h/compinit && -f $0:h/compdump ]]; then
+    _ci_fdir=$0:h
+    print "Using my directory, $_ci_fdir"
   else
     # more guesses?
     print \
@@ -51,22 +131,26 @@ if [[ -z $fdir ]]; then
 installed.  If they are not installed, you will need to find them in the
 Completion/* directories of the zsh distribution and install them yourself,
 or insult your system manager for incompetence."
-    vared -c fdir
-    while [[ ! -d ${~fdir} || ! -f ${~fdir}/compinit || 
-      ! -f ${~fdir}/compdump ]]; do
+    vared -c _ci_fdir
+    while [[ ! -d ${~_ci_fdir} || ! -f ${~_ci_fdir}/compinit || 
+      ! -f ${~_ci_fdir}/compdump ]]; do
       print "I can't find them in that directory.  Try again or abort."
-      vared fdir
+      vared _ci_fdir
     done
   fi
-  eval "fpath=($fdir \$fpath)"
-  fdir=${fdir/#$HOME/\~}
-  lines="fpath=($fdir \$fpath)\n"
 else
-  print "Found completion functions in your fpath, will not alter it."
+  print "Keeping existing completion directiory $_ci_fdir"
 fi
 
-files=( ${^~fpath:/.}/_(|*[^~])(N:t) )
-if [[ $#files -lt 20 ]]; then
+# Check if this is in fpath already, else put it there (with ~'s expanded).
+_ci_f=${~_ci_fdir}
+[[ -z ${fpath[(r)$_ci_f]} ]] && fpath=($_ci_f $fpath)
+# Contract $HOME to ~ in the parameter to be used for writing.
+_ci_fdir=${_ci_fdir/#$HOME/\~}
+
+# Now check the fpath, ignoring the directory .
+_ci_files=( ${^~fpath:/.}/_(|*[^~])(N:t) )
+if [[ $#_ci_files -lt 20 ]]; then
   print "
 Hmmm, completion functions seem a bit thin on the ground.  There should
 be lots of files with names beginning with an underscore (_).  You should
@@ -75,12 +159,20 @@ look and see what's happened to these.
   read
 fi
 
-if [[ -w ${~fdir} && ( ! -f ${~fdir}/compinit.dump ||
-  -w ${~fdir}/compinit.dump ) ]]
+
+# Set up the dumpfile
+_ci_dtype=existing
+if [[ -z $_ci_dumpfile ]]; then
+  _ci_dumpfile="${_ci_fdir}/compinit.dump"
+  _ci_dtype=standard
+fi
+
+if [[ -w ${~_ci_dumpfile:h} && ( ! -f ${~_ci_dumpfile} ||
+  -w ${~_ci_dumpfile} ) ]]
 then
   print "
-Using standard dumpfile
-  ${~fdir}/compinit.dump
+Using $_ci_dtype dumpfile
+  ${_ci_dumpfile}
 to speed up initialisation.
 [Hit return to continue]"
   read
@@ -88,23 +180,30 @@ else
   print "
 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
-$fdir/compinit.dump.  Please edit a replacement."
-  dumpfile='~/.compinit.dump'
-  vared dumpfile
-  while ! touch ${~dumpfile} >& /dev/null; do
+${_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."
-    vared dumpfile
+    vared _ci_dumpfile
   done
-  [[ -s $dumpfile ]] || rm -f $dumpfile
-  dumpfile=" $dumpfile"
+  [[ -s $_ci_dumpfile ]] || rm -f $_ci_dumpfile
 fi
 
-fdir=${fdir/#$HOME/\~}
-
-lines="${lines}. $fdir/compinit -d$dumpfile\n"
+_ci_lines="${_ci_lines}. $_ci_fdir/compinit -f $_ci_fdir -d"
+[[ $_ci_dtype != standard ]] && _ci_lines="${_ci_lines} $_ci_dumpfile"
+_ci_lines="${_ci_lines}
+"
 
 
 print "
+Would you like to set some more advanced options?  Otherwise, you
+can re-run compinstall later to set these. [n]"
+
+# The whole of the next part should be indented, but I can't be bothered.
+if read -q; then
+
+ print "
 In addition to completion, zsh can also perform correction of the
 current word, or approximate completion, i.e. completion where the part of
 the word typed so far can be corrected; or it can try correction, then
@@ -112,105 +211,155 @@ approximate completion if that fails.  Would you like:
   0:  Just ordinary completion
   C:  Correction
   A:  Approximate completion
-  B:  Both?
-Please type one of the keys above:"
-while read -k type; do
-  print
-  case $type in
-    0*) completer=_complete
-	break
-	;;
-    [cC]*) completer=_complete:_correct
-	   break
-	   ;;
-    [aA]*) completer=_complete:_approximate
-	   break;
-	   ;;
-    [bB]*) completer=_complete:_correct:_approximate
-	   break
-	   ;;
-    *) print Try again
-       ;;
-  esac
-done
-
-lines="${lines}compconf completer=$completer"
-
-
-if [[ $completer = *(correct|approx)* ]]; then
-  print "
-Correction and approximation will normally allow up to two errors,
-and you will be able to use a numeric prefix (e.g. <Esc>4) to allow
-more.  The standard prompt is \`correct to:'. Do you want to change
-any of this? [n]"
-  if read -q; then
-    print "Number of errors to accept normally (0 is OK):"
-    read accept
-    while [[ $accept != <-> ]]; do
-      read accept"?Please enter a number: "
-    done
-    print \
+  B:  Both"
+  if [[ -n $_ci_completer ]]; then
+    print "  Default: use the current completer:\n$_ci_completer"
+  else
+    print "Please type one of the keys above."
+  fi
+  while read -k _ci_type; do
+    print
+    case $_ci_type in
+      0*) _ci_completer=_complete
+	  break
+	  ;;
+      [cC]*) _ci_completer=_complete:_correct
+	     break
+	     ;;
+      [aA]*) _ci_completer=_complete:_approximate
+	     break;
+	     ;;
+      [bB]*) _ci_completer=_complete:_correct:_approximate
+	     break
+	     ;;
+      *) [[ -n $_ci_completer ]] && break
+	 print Try again
+	 ;;
+    esac
+  done
+
+  _ci_lines="${_ci_lines}compconf completer=$_ci_completer"
+
+
+  if [[ $_ci_completer = *(correct|approx)* ]]; then
+    _ci_accept=${_ci_defaults[correct_accept]}
+    _ci_cprompt=${_ci_defaults[correct_prompt]}
+    print "
+Correction and approximation will allow up to ${${_ci_accept:-2}%%[^0-9]*} \
+errors. "
+    case $_ci_accept in
+      *n*!*|*!*n) print "A numeric prefix, if not 1, will cause correction \
+not to be done."
+		  ;;
+      *n*) print "A numeric prefix gives the maximum number of errors which \
+will be accepted."
+           ;;
+      *) print "The numeric prefix will not be used."
+    esac
+print "The correction prompt is \`${_ci_cprompt:-correct to:}'.
+Do you want to change any of this? [n]"
+    if read -q; then
+      print "Number of errors to accept normally (0 is OK):"
+      _ci_accept=${_ci_accept%%[^0-9]*}
+      vared _ci_accept
+      while [[ $_ci_accept != <-> ]]; do
+	print "Please enter a number:"
+	vared _ci_accept
+      done
+      print \
 "How would you like the numeric prefix to be treated:
   0:  Not used by correction
-  U:  Used to given the number of errors
+  U:  The number gives the largest number of errors which will be
+      accepted when correcting
   I:  If present, and not 1, do not perform correction?
 Please type one of the keys above:"
-    while read -k type; do
-      print
-      case $type in
-	0*) break
-	    ;;
-	[uU]*) accept="${accept}n"
-	       break
-	       ;;
-	[Ii]*) accept="${accept}!n"
-	       break
-	       ;;
-	*) print Try again
-	   ;;
-      esac
-    done
-    lines="$lines \\\\
-  correct_accept='$accept'"
-    print "
+      while read -k _ci_type; do
+	print
+	case $_ci_type in
+	  0*) break
+	      ;;
+	  [uU]*) _ci_accept="${_ci_accept}n"
+		 break
+		 ;;
+	  [Ii]*) _ci_accept="${_ci_accept}!n"
+		 break
+		 ;;
+	  *) print Try again
+	     ;;
+	esac
+      done
+      print "
 Instead of the prompt \`correct to:', you can have no prompt, or a
 prompt of your choosing which can display the number of errors found by
 containing the string \`%e'.  Do you wish to change the correction
 prompt? [n]"
-    if read -q; then
-      cprompt=''
-      print "Edit a new prompt (may be empty):"
-      vared cprompt
-      lines="$lines \\\\
-  correct_prompt='${cprompt//\'/\'\\\'\'}'"
+      if read -q; then
+	print "Edit a new prompt (may be empty):"
+	vared _ci_cprompt
+	[[ -z $_ci_cprompt ]] && _ci_cprompt=':empty:'
+      fi
+    fi
+    if [[ -n $_ci_accept ]]; then
+      _ci_lines="$_ci_lines \\
+  correct_accept='$_ci_accept'"
+      unset '_ci_defaults[correct_accept]'
+    fi
+    if [[ -n $_ci_cprompt ]]; then
+      _ci_cprompt=${_ci_cprompt##:empty:}
+      _ci_lines="$_ci_lines \\
+  correct_prompt='${_ci_cprompt//\'/\'\\\'\'}'"
+      unset '_ci_defaults[correct_prompt]'
     fi
   fi
-fi
 
-lines="$lines\n"
+  _ci_warn=''
+  for _ci_f in ${(k)_ci_defaults}; do
+    if [[ -z $_ci_warn ]]; then
+      print "
+(Keeping other existing configuration settings...)"
+      _ci_warn=1
+    fi
+    _ci_lines="$_ci_lines \\
+  ${_ci_f}='${_ci_defaults[$_ci_f]//\'/\'\\\'\'}'"
+  done
 
+  _ci_lines="$_ci_lines
+"
 
-startline='# The following lines were added by compinstall'
-endline='# End of lines added by compinstall'
+else
 
-ifile=${ZDOTDIR:-~}/.zshrc
-[[ -f $ifile ]] || touch $ifile
-tmpf=${TMPPPREFIX:-/tmp/zsh}compinstall$$
+  if [[ -n $_ci_existing ]]; then
+    print -nr "
+I will retain the following lines from the existing completion setup:
+$_ci_existing"
+    _ci_lines="$_ci_lines${_ci_existing}"
+  fi
+
+fi				# End of advanced options
 
-if [[ ! -w $ifile ]]; then
-  print "\nI can't write to $ifile.  I will leave the lines to add in
-\`$tmpf' and you must add them by hand."
-  print "\n$startline\n$lines\n$endline" >$tmpf
-  return 0
-fi
 
-if grep $endline $ifile >& /dev/null; then
-  print -- "$startline\n$lines$endline" >$tmpf
-  sed -e "/^$endline/r $tmpf
-/^$startline/,/^$endline/d" $ifile >${tmpf}2 && mv ${tmpf}2 $ifile &&
-  print "\nSuccesfully modified old compinstall lines in $ifile."
-  rm -f $tmpf ${tmpf}2
+[[ -f $_ci_ifile ]] || touch $_ci_ifile
+_ci_tmpf=${TMPPPREFIX:-/tmp/zsh}compinstall$$
+
+if [[ ! -w $_ci_ifile ]]; then
+  print "\nI can't write to $_ci_ifile.  I will leave the lines to add in
+\`$_ci_tmpf' and you must add them by hand."
+  print -r - "$_ci_startline
+$_ci_lines$_ci_endline" >$_ci_tmpf
+elif grep $_ci_endline $_ci_ifile >& /dev/null; then
+  print -r - "$_ci_startline
+$_ci_lines$_ci_endline" >$_ci_tmpf
+  sed -e "/^$_ci_endline/r $_ci_tmpf
+/^$_ci_startline/,/^$_ci_endline/d" $_ci_ifile >${_ci_tmpf}2 && 
+  mv ${_ci_tmpf}2 $_ci_ifile &&
+  print "\nSuccesfully modified old compinstall lines in $_ci_ifile."
+  rm -f $_ci_tmpf ${_ci_tmpf}2
 else
-  print "\n$startline\n$lines\n$endline" >>$ifile &&
-  print "\nSuccessfully appended lines to $ifile."
+  print -r - "$_ci_startline
+$_ci_lines$_ci_endline" >>$_ci_ifile &&
+  print "\nSuccessfully appended lines to $_ci_ifile."
 fi
+
+TRAPINT 0
+
+return 0
diff --git a/Config/version.mk b/Config/version.mk
index be39ecad4..c7af8d188 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-17
-VERSION_DATE='April 30, 1999'
+VERSION=3.1.5-pws-18
+VERSION_DATE='May 8, 1999'
diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index e9bc2ab16..7cbdb1114 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -318,10 +318,13 @@ cindex(file, history)
 tt(fc -R) reads the history from the given file,
 tt(fc -W) writes the history out to the given file,
 and tt(fc -A) appends the history out to the given file.
-If the tt(-I) option is added, only those
-events that are new since last incremental append (write) to
-the history file are appended (written).
-In any case the file will have no more than tt($SAVEHIST)
+If no filename is specified, the tt($HISTFILE) is assumed.
+If the tt(-I) option is added to tt(-R), only those events that are
+not already contained within the internal history list are added.
+If the tt(-I) option is added to tt(-A) or tt(-W), only those
+events that are new since last incremental append/write to
+the history file are appended/written.
+In any case, the created file will have no more than tt($SAVEHIST)
 entries.
 )
 findex(fg)
@@ -1169,30 +1172,26 @@ Equivalent to tt(whence -c).
 findex(zmodload)
 cindex(modules, loading)
 cindex(loading modules)
-xitem(tt(zmodload) [ tt(-iL) ] [ var(name) ... ])
-xitem(tt(zmodload) tt(-u) [ tt(-i) ] var(name) ...)
-xitem(tt(zmodload) tt(-d) [ tt(-L) ] [ var(name) [ var(dep) ... ] ])
-xitem(tt(zmodload) tt(-du) var(name) [ var(dep) ... ])
-xitem(tt(zmodload) tt(-a) [ tt(-iL) ] [ var(name) [ var(builtin) ... ] ])
-xitem(tt(zmodload) tt(-au) [ tt(-i) ] var(builtin) ...)
-xitem(tt(zmodload) tt(-c) [ tt(-iI) ] var(name) [ var(cond) ... ])
-xitem(tt(zmodload) tt(-cu) [ tt(-iI) ] var(cond) ...)
-xitem(tt(zmodload) tt(-c) [ tt(-IL) ])
-xitem(tt(zmodload) tt(-p) [ tt(-i) ] var(name) [ var(parameter) ... ])
-xitem(tt(zmodload) tt(-pu) [ tt(-i) ] var(parameter) ... ])
-item(tt(zmodload) tt(-p) [ tt(-L) ])(
+xitem(tt(zmodload) [ tt(-dL) ] [ ... ])
+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.
 This feature is not available on all operating systems,
 or on all installations on a particular operating system.
 
-In the simplest case,
-tt(zmodload) loads a binary module.  The module must be in a file with a
-name consisting of the specified var(name) followed by a standard suffix,
-usually `tt(.so)'.  If this can't be found, the var(name) is tried without
-the suffix.
-If the module to be loaded is already loaded and the tt(-i)
-option is given, the duplicate module is ignored.  Otherwise
-tt(zmodload) prints an error message.
+Without arguments all currently loaded binary modules are printed.
+The tt(-L) option causes this list to be in the form of a series of
+tt(zmodload) commands.  Forms with arguments are:
+
+startitem()
+xitem(tt(zmodload) [ tt(-i) ] var(name) ... )
+item(tt(zmodload) tt(-u) [ tt(-i) ] var(name) ...)(
+In the simplest case, tt(zmodload) loads a binary module.  The module must
+be in a file with a name consisting of the specified var(name) followed by
+a standard suffix, usually `tt(.so)'.  If this can't be found, the
+var(name) is tried without the suffix.  If the module to be loaded is
+already loaded and the tt(-i) option is given, the duplicate module is
+ignored.  Otherwise tt(zmodload) prints an error message.
 
 The var(name)d module is searched for in the same way a command is, using
 tt($module_path) instead of tt($path).  If var(name) contains a `tt(/)',
@@ -1209,55 +1208,66 @@ unloaded (or was never loaded).
 Each module has a boot and a cleanup function.  The module
 will not be loaded if its boot function fails.  Similarly a module
 can only be unloaded if its cleanup function runs successfully.
-
-Without arguments all currently loaded binary modules are printed.
-The tt(-L) option causes this list to be in the form of a series of
-tt(zmodload) commands.
-
-The tt(-d) option can be used to specify module dependencies.
-This operation is idempotent regardless of the tt(-i) option.
-The modules named in the second and subsequent arguments will be
-loaded before the module named in the first argument.
-
-With tt(-d) and one
-argument, all dependencies for that module are listed.  With tt(-d) and no
-arguments, all module dependencies are listed.
-This listing is by default in a Makefile-like format.
-The tt(-L) option changes this format to a list of
-tt(zmodload -d) commands.
-
-If tt(-d) and tt(-u) are both used, dependencies are removed.
-This operation is idempotent regardless of the tt(-i) option.
-If only one argument is given, all dependencies for that module are removed.
-
-The tt(-a) option defines autoloaded builtins.  It defines the
-specified var(builtin)s.  When any of those builtins is called, the module
-specified in the first argument is loaded.  If only one argument is given,
-one builtin is defined, with the same name as the module.
-tt(-i) suppresses the error if the builtin is already defined or
-autoloaded, regardless of which module it came from.
-
-With tt(-a) and no arguments, all
-autoloaded builtins are listed, with the module name (if different)
-shown in parentheses after the builtin name.  The tt(-L) option changes
-this format to a list of tt(zmodload -a) commands.
-
-If tt(-a) is used
-together with the tt(-u) option it removes builtins defined with
-tt(zmodload -a).  This is only possible if the builtin is not yet
-loaded.  tt(-i) suppresses the error if the builtin is already
+)
+xitem(tt(zmodload) tt(-d) [ tt(-L) ] [ var(name) ])
+xitem(tt(zmodload) tt(-d) var(name) var(dep) ...)
+item(tt(zmodload) tt(-ud) var(name) [ var(dep) ... ])(
+The tt(-d) option can be used to specify module dependencies.  The modules
+named in the second and subsequent arguments will be loaded before the
+module named in the first argument.
+
+With tt(-d) and one argument, all dependencies for that module are listed.
+With tt(-d) and no arguments, all module dependencies are listed.  This
+listing is by default in a Makefile-like format.  The tt(-L) option
+changes this format to a list of tt(zmodload -d) commands.
+
+If tt(-d) and tt(-u) are both used, dependencies are removed.  If only one
+argument is given, all dependencies for that module are removed.
+)
+xitem(tt(zmodload) tt(-ab) [ tt(-L) ])
+xitem(tt(zmodload) tt(-ab) [ tt(-i) ] var(name) [ var(builtin) ... ])
+item(tt(zmodload) tt(-ub) [ tt(-i) ] var(builtin) ...)(
+The tt(-ab) option defines autoloaded builtins.  It defines the specified
+var(builtin)s.  When any of those builtins is called, the module specified
+in the first argument is loaded.  If only the var(name) is given, one
+builtin is defined, with the same name as the module.  tt(-i) suppresses
+the error if the builtin is already defined or autoloaded, regardless of
+which module it came from.
+
+With tt(-ab) and no arguments, all autoloaded builtins are listed, with the
+module name (if different) shown in parentheses after the builtin name.
+The tt(-L) option changes this format to a list of tt(zmodload -a)
+commands.
+
+If tt(-b) is used together with the tt(-u) option, it removes builtins
+previously defined with tt(-ab).  This is only possible if the builtin is
+not yet loaded.  tt(-i) suppresses the error if the builtin is already
 removed (or never existed).
-
-The tt(-c) option is used to define autoloaded condition codes. The
+)
+xitem(tt(zmodload) tt(-ac) [ tt(-IL) ])
+xitem(tt(zmodload) tt(-ac) [ tt(-iI) ] var(name) [ var(cond) ... ])
+item(tt(zmodload) tt(-uc) [ tt(-iI) ] var(cond) ...)(
+The tt(-ac) option is used to define autoloaded condition codes. The
 var(cond) strings give the names of the conditions defined by the
 module. The optional tt(-I) option is used to define infix condition
 names. Without this option prefix condition names are defined.
 
-Together with the tt(-u) option definitions for autoloaded conditions
-are removed. If given no condition names all defined names are listed
-(as a series of tt(zmodload) commands if the tt(-L) option is given).
+If given no condition names, all defined names are listed (as a series of
+tt(zmodload) commands if the tt(-L) option is given).
 
-The tt(-p) option is like the tt(-c) option, but makes tt(zmodload)
-work on autoloaded parameters instead of condition codes.
+The tt(-uc) option removes definitions for autoloaded conditions.
+)
+xitem(tt(zmodload) tt(-ap) [ tt(-L) ])
+xitem(tt(zmodload) tt(-ap) [ tt(-i) ] var(name) [ var(parameter) ... ])
+item(tt(zmodload) tt(-up) [ tt(-i) ] var(parameter) ...)(
+The tt(-p) option is like the tt(-b) and tt(-c) options, but makes
+tt(zmodload) work on autoloaded parameters instead.
+)
+xitem(tt(zmodload) tt(-a) [ tt(-L) ])
+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).
+)
+enditem()
 )
 enditem()
diff --git a/Doc/Zsh/compsys.yo b/Doc/Zsh/compsys.yo
index cedc4ec8c..90f6b2910 100644
--- a/Doc/Zsh/compsys.yo
+++ b/Doc/Zsh/compsys.yo
@@ -49,14 +49,42 @@ endmenu()
 texinode(Initialisation)(Control Functions)()(Completion System)
 sect(Initialisation)
 
+The script tt(compinstall) can be run by a user to set up the completion
+system for use.  It will usually insert code into tt(.zshrc), although if
+that is not writeable it will save it in another file and tell you that
+file's locations.  Note that it is up to you to make sure that the lines
+added to tt(.zshrc) are actually run; you may, for example, need to move
+them to an earlier place in the file if tt(.zshrc) usually returns early.
+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).
+
+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
+locates the completion files and decides where to put your personal
+dumpfile, used to speed up initialisation 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.
+
+You can abort the installation any time you are being prompted for
+information, and your tt(.zshrc) will not be altered at all.
+
+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.
 
-subsect(Arguments)
-
 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.
@@ -65,6 +93,15 @@ but with tt(.dump) appended to the end; 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).
+
 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
 arguments in the first line of a tt(#compdef) funcion (as described below)
@@ -80,8 +117,18 @@ 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.  When
-tt(compinit) is sourced, it searches all such files accessible via
+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.
+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
 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
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index 0326e646d..5853d792f 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -717,18 +717,24 @@ If multiple nested tt(${...}) forms are present, substitution is
 performed from the inside outwards.  At each level, the substitution takes
 account of whether the current value is a scalar or an array, whether the
 whole substitution is in double quotes, and what flags are supplied to the
-current level of substitution; the flags are not propagated up to enclosing
-substitutions.  The value passed back to an enclosing substitution is
-always an array, which however will consist of one word if the value was
-not itself an array.  All the following steps take place where applicable
-at all levels of substitution.
+current level of substitution, just as if the nested substitution were the
+outermost.  The flags are not propagated up to enclosing
+substitutions; the nested subsitution will return either a scalar or an
+array as determined by the flags, possibly adjusted for quoting.  All the
+following steps take place where applicable at all levels of substitution.
+Note that, unless the tt((P)) flag is present, the flags and any subscripts
+apply directly to the value of the nested substitution; for example, the
+expansion tt(${${foo}}) behaves exactly the same as tt(${foo}).
 )
 item(tt(2.) em(Parameter Subscripting))(
 If the value is a raw parameter reference with a subscript, such as
 tt(${)var(var)tt([3]}), the effect of subscripting is applied directly to
-the parameter.  If the parameter is an array, any second subscript,
-indexing on the character in the word, may appear,
-e.g. tt(${)var(var)tt([1][2]}).
+the parameter.  Subscripts are evaluated left to right; subsequent
+subscripts apply to the scalar or array value yielded by the previous
+subscript.  Thus if tt(var) is an array, tt(${var[1][2]}) is the second
+character of the first word, but tt(${var[2,4][2]}) is the entire third
+word (the second word of the range of words two through four of the
+original array).  Any number of subscripts may appear.
 )
 item(tt(3.) em(Parameter Name Replacement))(
 The effect of any tt((P)) flag, which treats the value so far as a
@@ -746,6 +752,10 @@ item(tt(5.) em(Nested Subscripting))(
 Any remaining subscript (i.e. of a nested substitution) is evaluated at
 this point, based on whether the value is an array or a scalar; if it was
 an array, a second subscript for the character in the word may also appear.
+Note that tt(${foo[2,4][2]}) is thus equivalent to tt(${${foo[2,4]}[2]})
+and also to tt("${${(@)foo[2,4]}[2]}") (the nested substitution returns an
+array in both cases), but not to tt("${${foo[2,4]}[2]}") (the nested
+substitution returns a scalar because of the quotes).
 )
 item(tt(6.) em(Modifiers))(
 Any modifiers, as specified by a trailing tt(#), tt(%), tt(/)
@@ -798,20 +808,17 @@ Suppose that tt($foo) contains the array tt(LPAR()bar baz)tt(RPAR()):
 
 startitem()
 item(tt("${(@)${foo}[1]}"))(
-This produces the result tt(bar baz).  First, the inner substitution
+This produces the result tt(b).  First, the inner substitution
 tt("${foo}"), which has no array (tt(@)) flag, produces a single word
-result.  The outer substitution tt("${(@)...[1]}") acts on this result as
-if it were a one word array, because of the array flag, so the result is
-just that single word.
+result tt("bar baz").  The outer substitution tt("${(@)...[1]}") detects
+that this is a scalar, so that (despite the tt((@)) flag) the subscript
+picks the first character. 
 )
 item(tt("${${(@)foo}[1]}"))(
-The produces the result tt(b).  In this case, the inner substitution
+The produces the result tt(bar).  In this case, the inner substitution
 tt("${(@)foo}") produces the array tt(LPAR()bar baz)tt(RPAR()).  The outer
-substitution tt("${...[1]}"), however, has no array flag, so that it joins
-the array it has to a single word and indexes as if it were a string.  Note
-that this is not identical to the case tt("${foo[1]}"), since here the
-expression tt(foo[1]) is recognised immediately as an index into an array,
-so that the result in that case is tt(bar).
+substitution tt("${...[1]}") detects that this is an array and picks the
+first word.  This is similar to the simple case tt("${foo[1]}").
 )
 enditem()
 
diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index 92de8c2d8..41c5ce24e 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -302,9 +302,10 @@ always produces named directory expansion.)
 pindex(EXTENDED_HISTORY)
 cindex(history, timestamping)
 item(tt(EXTENDED_HISTORY))(
-Save beginning and ending timestamps to the history file.
-The format of these timestamps is
-`tt(:)var(<beginning time>)tt(:)var(<ending time>)tt(:)var(<command>)'.
+Save each command's beginning timestamp (in seconds since the epoch)
+and the duration (in seconds) to the history file.  The format of
+this prefixed data is
+`tt(:)var(<beginning time>)tt(:)var(<elapsed seconds>)tt(:)var(<command>)'.
 )
 pindex(FLOW_CONTROL)
 cindex(flow control)
@@ -405,6 +406,20 @@ item(tt(HIST_BEEP))(
 Beep when an attempt is made to access a history entry which
 isn't there.
 )
+pindex(HIST_EXPIRE_DUPS_FIRST)
+cindex(history, expiring duplicates)
+item(tt(HIST_EXPIRE_DUPS_FIRST))(
+If the history needs to be trimmed to add a new line, setting this
+option will cause the oldest duplicate history line to be lost before
+losing a unique line from the internal history list.
+)
+pindex(HIST_IGNORE_ALL_DUPS)
+cindex(history, ignoring all duplicates)
+item(tt(HIST_IGNORE_ALL_DUPS) (tt(-h)))(
+If a new command line being added to the history list duplicates an
+older one, the older command is removed from the list (even if it is
+not the previous event).
+)
 pindex(HIST_IGNORE_DUPS)
 cindex(history, ignoring duplicates)
 item(tt(HIST_IGNORE_DUPS) (tt(-h)))(
@@ -426,6 +441,11 @@ item(tt(HIST_NO_STORE))(
 Remove the tt(history) (tt(fc -l)) command from
 the history when invoked.
 )
+pindex(HIST_SAVE_NO_DUPS)
+item(tt(HIST_SAVE_NO_DUPS))(
+When writing out the history file, older commands that duplicate
+newer ones are omitted.
+)
 pindex(HIST_REDUCE_BLANKS)
 item(tt(HIST_REDUCE_BLANKS))(
 Remove superfluous blanks from each command line
@@ -459,6 +479,15 @@ of tt(exit) or tt(logout) instead.
 However, ten consecutive EOFs will cause the shell to exit anyway,
 to avoid the shell hanging if its tty goes away.
 )
+pindex(INCREMENTAL_APPEND_HISTORY)
+cindex(history, incremental appending to a file)
+item(tt(INCREMENTAL_APPEND_HISTORY))(
+This options works like APPEND_HISTORY except that new history lines
+are added to the tt($HISTFILE) when they finish running, rather than
+waiting until the shell is killed. The file is periodically trimmed
+to the number of lines specified by tt($SAVEHIST), but can exceed this
+value between trimmings.
+)
 pindex(INTERACTIVE)
 item(tt(INTERACTIVE) (tt(-i), ksh: tt(-i)))(
 This is an interactive shell.  This option is set upon initialisation if
@@ -798,6 +827,28 @@ This avoids the problem of reflexively answering `yes' to the query
 when one didn't really mean it.  The wait and query can always be
 avoided by expanding the `tt(*)' in ZLE (with tab).
 )
+pindex(SHARE_HISTORY)
+cindex(share history)
+cindex(history, sharing)
+item(tt(SHARE_HISTORY))(
+
+This option both imports new commands from the history file, and also
+causes your typed commands to be appended to the history file (like
+specifiying tt(INCREMENTAL_APPEND_HISTORY)).  The history lines are also
+output with timestamps ala tt(EXTENDED_HISTORY) (which makes it easier to
+find the spot where we left off reading the file after it gets re-written).
+
+By default, history movement commands visit the imported lines as well as
+the local lines, but you can toggle this on and off with the
+set-local-history zle binding.  It is also possible to create a zle
+widget that will make some commands ignore imported commands, and some
+include them.
+
+If you find that you want more control over when commands
+get imported, you may wish to turn tt(SHARE_HISTORY) off,
+tt(INCREMENTAL_APPEND_HISTORY) on, and then manually import
+commands whenever you need them using `fc -RI'.
+)
 pindex(SH_FILE_EXPANSION)
 cindex(sh, expansion style)
 cindex(expansion style, sh)
diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c
index 7825a900d..34344f5c1 100644
--- a/Src/Builtins/rlimits.c
+++ b/Src/Builtins/rlimits.c
@@ -43,14 +43,14 @@ zstrtorlimt(const char *s, char **t, int base)
 {
     rlim_t ret = 0;
  
-    if (!base)
+    if (!base) {
 	if (*s != '0')
 	    base = 10;
 	else if (*++s == 'x' || *s == 'X')
 	    base = 16, s++;
 	else
 	    base = 8;
- 
+    } 
     if (base <= 10)
 	for (; *s >= '0' && *s < ('0' + base); s++)
 	    ret = ret * base + *s - '0';
diff --git a/Src/Zle/comp1.c b/Src/Zle/comp1.c
index c51aad297..f32e5f5c0 100644
--- a/Src/Zle/comp1.c
+++ b/Src/Zle/comp1.c
@@ -47,7 +47,7 @@ Cmlist cmatcher;
 void (*makecompparamsptr) _((void));
 
 /**/
-void (*comp_setunsetptr) _((int, int));
+void (*comp_setunsetptr) _((unsigned int, unsigned int));
 
 /* pointers to functions required by compctl and defined by zle */
 
@@ -147,6 +147,7 @@ createcompctltable(void)
     compctltab->hash        = hasher;
     compctltab->emptytable  = emptyhashtable;
     compctltab->filltable   = NULL;
+    compctltab->cmpnodes    = strcmp;
     compctltab->addnode     = addhashnode;
     compctltab->getnode     = gethashnode2;
     compctltab->getnode2    = gethashnode2;
diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list
index 61ad0e24a..e0c93bd03 100644
--- a/Src/Zle/iwidgets.list
+++ b/Src/Zle/iwidgets.list
@@ -91,6 +91,7 @@
 "send-break", sendbreak, 0
 "set-mark-command", setmarkcommand, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
 "spell-word", spellword, 0
+"set-local-history", setlocalhistory, 0
 "transpose-chars", transposechars, 0
 "transpose-words", transposewords, 0
 "undefined-key", undefinedkey, 0
diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c
index 0064d4ef0..d2b9a3233 100644
--- a/Src/Zle/zle_hist.c
+++ b/Src/Zle/zle_hist.c
@@ -47,27 +47,16 @@ int lastcol;
 /**/
 int histline;
 
-/* the last line in the history (the current one), metafied */
-
-/**/
-char *curhistline;
+#define ZLETEXT(X) ((X)->zle_text ? (X)->zle_text : (X)->text)
 
 /**/
 void
 remember_edits(void)
 {
-    if (histline == curhist) {
-	zsfree(curhistline);
-	curhistline = metafy((char *) line, ll, META_DUP);
-    }
-    else {
-	Histent ent = gethistent(histline);
-
-	if (metadiffer(ent->zle_text ? ent->zle_text : ent->text,
-		       (char *) line, ll)) {
-	    zsfree(ent->zle_text);
-	    ent->zle_text = metafy((char *) line, ll, META_DUP);
-	}
+    Histent ent = quietgethist(histline);
+    if (metadiffer(ZLETEXT(ent), (char *) line, ll)) {
+	zsfree(ent->zle_text);
+	ent->zle_text = metafy((char *) line, ll, META_DUP);
     }
 }
 
@@ -75,11 +64,11 @@ remember_edits(void)
 void
 forget_edits(void)
 {
-    int i;
+    Histent he;
 
-    for (i = 0; i < histentct; i++) {
-	zsfree(histentarr[i].zle_text);
-	histentarr[i].zle_text = NULL;
+    for (he = hist_ring; he; he = up_histent(he)) {
+	zsfree(he->zle_text);
+	he->zle_text = NULL;
     }
 }
 
@@ -87,16 +76,12 @@ forget_edits(void)
 void
 uphistory(void)
 {
-    if (zmult < 0) {
-	zmult = -zmult;
-	downhistory();
-	zmult = -zmult;
-    } else if(!zle_goto_hist(histline - zmult) && isset(HISTBEEP))
+    if (!zle_goto_hist(histline, -zmult) && isset(HISTBEEP))
 	feep();
 }
 
 /**/
-int
+static int
 upline(void)
 {
     int n = zmult;
@@ -159,7 +144,6 @@ viuplineorhistory(void)
     vifirstnonblank();
 }
 
-
 /**/
 void
 uplineorsearch(void)
@@ -181,7 +165,7 @@ uplineorsearch(void)
 }
 
 /**/
-int
+static int
 downline(void)
 {
     int n = zmult;
@@ -268,26 +252,22 @@ downlineorsearch(void)
 void
 acceptlineanddownhistory(void)
 {
-    char *s;
+    Histent he;
 
-    if (!(s = zle_get_event(histline + 1))) {
+    if (!(he = movehistent(quietgethist(histline), 1, HIST_FOREIGN))) {
 	feep();
 	return;
     }
-    pushnode(bufstack, ztrdup(s));
+    pushnode(bufstack, ztrdup(ZLETEXT(he)));
     done = 1;
-    stackhist = histline + 1;
+    stackhist = he->histnum;
 }
 
 /**/
 void
 downhistory(void)
 {
-    if (zmult < 0) {
-	zmult = -zmult;
-	uphistory();
-	zmult = -zmult;
-    } else if(!zle_goto_hist(histline + zmult) && isset(HISTBEEP))
+    if (!zle_goto_hist(histline, zmult) && isset(HISTBEEP))
 	feep();
 }
 
@@ -298,7 +278,7 @@ static char *srch_str;
 void
 historysearchbackward(void)
 {
-    int hl = histline;
+    Histent he;
     int n = zmult;
     char *s;
 
@@ -308,7 +288,7 @@ historysearchbackward(void)
 	zmult = n;
 	return;
     }
-    if (hl == curhist || hl != srch_hl || cs != srch_cs || mark != 0
+    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++) ;
@@ -317,28 +297,29 @@ historysearchbackward(void)
 	srch_str = zalloc(histpos);
 	memcpy(srch_str, line, histpos);
     }
-    for (;;) {
-	hl--;
-	if (!(s = zle_get_event(hl))) {
-	    feep();
-	    return;
-	}
+    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 (--n <= 0)
-		break;
+	    if (--n <= 0) {
+		zle_setline(he);
+		srch_hl = histline;
+		srch_cs = cs;
+		return;
+	    }
 	}
     }
-    zle_goto_hist(hl);
-    srch_hl = hl;
-    srch_cs = cs;
+    feep();
 }
 
 /**/
 void
 historysearchforward(void)
 {
-    int hl = histline;
+    Histent he;
     int n = zmult;
     char *s;
 
@@ -348,7 +329,7 @@ historysearchforward(void)
 	zmult = n;
 	return;
     }
-    if (hl == curhist || hl != srch_hl || cs != srch_cs || mark != 0
+    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++) ;
@@ -357,20 +338,22 @@ historysearchforward(void)
 	srch_str = zalloc(histpos);
 	memcpy(srch_str, line, histpos);
     }
-    for (;;) {
-	hl++;
-	if (!(s = zle_get_event(hl))) {
-	    feep();
-	    return;
+    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 (--n <= 0) {
+		zle_setline(he);
+		srch_hl = histline;
+		srch_cs = cs;
+		return;
+	    }
 	}
-	if (metadiffer(s, srch_str, histpos) < (hl == curhist) &&
-	    metadiffer(s, srch_str, ll))
-	    if (--n <= 0)
-		break;
     }
-    zle_goto_hist(hl);
-    srch_hl = hl;
-    srch_cs = cs;
+    feep();
 }
 
 /**/
@@ -387,7 +370,7 @@ beginningofbufferorhistory(void)
 void
 beginningofhistory(void)
 {
-    if (!zle_goto_hist(firsthist()) && isset(HISTBEEP))
+    if (!zle_goto_hist(firsthist(), 0) && isset(HISTBEEP))
 	feep();
 }
 
@@ -405,7 +388,7 @@ endofbufferorhistory(void)
 void
 endofhistory(void)
 {
-    zle_goto_hist(curhist);
+    zle_goto_hist(curhist, 0);
 }
 
 /**/
@@ -419,7 +402,7 @@ insertlastword(void)
 /* multiple calls will now search back through the history, pem */
     static char *lastinsert;
     static int lasthist, lastpos;
-    int evhist = curhist - 1, save;
+    int evhist = addhistnum(curhist, -1, HIST_FOREIGN), save;
 
     if (lastinsert) {
 	int lastlen = ztrlen(lastinsert);
@@ -428,7 +411,7 @@ insertlastword(void)
 	if (lastpos <= pos &&
 	    lastlen == pos - lastpos &&
 	    memcmp(lastinsert, (char *)&line[lastpos], lastlen) == 0) {
-	    evhist = --lasthist;
+	    evhist = addhistnum(lasthist, -1, HIST_FOREIGN);
 	    cs = lastpos;
 	    foredel(pos - cs);
 	}
@@ -464,41 +447,37 @@ insertlastword(void)
 }
 
 /**/
-char *
-qgetevent(int ev)
+void
+zle_setline(Histent he)
 {
-    return ((ev == curhist) ? curhistline : quietgetevent(ev));
+    remember_edits();
+    mkundoent();
+    histline = he->histnum;
+    setline(ZLETEXT(he));
+    setlastline();
+    clearlist = 1;
 }
 
 /**/
-char *
-zle_get_event(int ev)
+void
+setlocalhistory(void)
 {
-    Histent ent;
-
-    if (ev == curhist)
-	return curhistline;
-    if (! (ent = quietgethist(ev)))
-	return NULL;
-    if (ent->zle_text)
-	return ent->zle_text;
-    return ent->text;
+    if (zmod.flags & MOD_MULT) {
+	hist_skip_flags = zmult? HIST_FOREIGN : 0;
+    }
+    else {
+	hist_skip_flags ^= HIST_FOREIGN;
+    }
 }
 
 /**/
-static int
-zle_goto_hist(int ev)
+int
+zle_goto_hist(int ev, int n)
 {
-    char *t;
-
-    remember_edits();
-    if(!(t = zle_get_event(ev)))
+    Histent he = movehistent(quietgethist(ev), n, hist_skip_flags);
+    if (!he)
 	return 0;
-    mkundoent();
-    histline = ev;
-    setline(t);
-    setlastline();
-    clearlist = 1;
+    zle_setline(he);
     return 1;
 }
 
@@ -603,6 +582,7 @@ static struct isrch_spot {
 
 static int max_spot = 0;
 
+/**/
 #ifdef MODULE
 
 /**/
@@ -612,6 +592,7 @@ free_isrch_spots(void)
     zfree(isrch_spots, max_spot * sizeof(*isrch_spots));
 }
 
+/**/
 #endif /* MODULE */
 
 /**/
@@ -665,13 +646,15 @@ doisearch(int dir)
     char *okeymap = curkeymapname;
     static char *previous_search = NULL;
     static int previous_search_len = 0;
+    Histent he;
 
     clearlist = 1;
 
     strcpy(ibuf, ISEARCH_PROMPT);
     memcpy(ibuf + NORM_PROMPT_POS, (dir == 1) ? "fwd" : "bck", 3);
     remember_edits();
-    s = zle_get_event(hl);
+    he = quietgethist(hl);
+    s = ZLETEXT(he);
     selectkeymap("main", 1);
     pos = metalen(s, cs);
     for (;;) {
@@ -705,15 +688,14 @@ doisearch(int dir)
 		if (!skip_line && ((sbuf[0] == '^') ?
 		    (t = metadiffer(s, sbuf + 1, sbptr - 1) < sens ? s : NULL) :
 		    (t = hstrnstr(s, pos, sbuf, sbptr, dir, sens)))) {
-		    zle_goto_hist(hl);
+		    zle_setline(he);
 		    pos = t - s;
 		    cs = ztrsub(t, s) + (dir == 1? sbptr - (sbuf[0]=='^') : 0);
 	    	    nomatch = 0;
 		    statusline = ibuf + NORM_PROMPT_POS;
 		    break;
 		}
-		hl += dir;
-		if (!(s = zle_get_event(hl))) {
+		if (!(he = movehistent(he, dir, hist_skip_flags))) {
 		    if (sbptr == (int)isrch_spots[top_spot-1].len
 		     && (isrch_spots[top_spot-1].flags & ISS_FAILING))
 			top_spot--;
@@ -723,13 +705,17 @@ doisearch(int dir)
 			feep();
 			nomatch = 1;
 		    }
-		    s = last_line;
+		    he = quietgethist(hl);
+		    s = ZLETEXT(he);
 		    skip_line = 0;
 		    statusline = ibuf;
 		    break;
 		}
+		hl = he->histnum;
+		s = ZLETEXT(he);
 		pos = dir == 1? 0 : strlen(s);
-		skip_line = !strcmp(last_line, s);
+		skip_line = isset(HISTFINDNODUPS)? !!(he->flags & HIST_DUP)
+						 : !strcmp(last_line, s);
 	    }
 	} else {
 	    top_spot = 0;
@@ -743,8 +729,9 @@ doisearch(int dir)
 	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) {
 	    int i;
 	    get_isrch_spot(0, &hl, &pos, &i, &sbptr, &dir, &nomatch);
-	    s = zle_get_event(hl);
-	    zle_goto_hist(hl);
+	    he = quietgethist(hl);
+	    zle_setline(he);
+	    s = ZLETEXT(he);
 	    cs = i;
 	    break;
 	}
@@ -769,10 +756,11 @@ doisearch(int dir)
 		statusline = ibuf;
 		skip_pos = 1;
 	    }
-	    s = zle_get_event(hl);
+	    he = quietgethist(hl);
+	    s = ZLETEXT(he);
 	    if (nomatch || !sbptr || (sbptr == 1 && sbuf[0] == '^')) {
 		int i = cs;
-		zle_goto_hist(hl);
+		zle_setline(he);
 		cs = i;
 	    }
 	    memcpy(ibuf + NORM_PROMPT_POS, (dir == 1) ? "fwd" : "bck", 3);
@@ -874,42 +862,35 @@ doisearch(int dir)
 void
 acceptandinfernexthistory(void)
 {
-    int t0;
-    char *s;
+    Histent he;
 
     done = 1;
-    for (t0 = histline - 2;; t0--) {
-	if (!(s = qgetevent(t0)))
+    for (he = movehistent(quietgethist(histline), -2, HIST_FOREIGN);
+	 he; he = movehistent(he, -1, HIST_FOREIGN)) {
+	if (!metadiffer(ZLETEXT(he), (char *) line, ll)) {
+	    he = movehistent(he, 1, HIST_FOREIGN);
+	    pushnode(bufstack, ztrdup(ZLETEXT(he)));
+	    stackhist = he->histnum;
 	    return;
-	if (!metadiffer(s, (char *) line, ll))
-	    break;
+	}
     }
-    if (!(s = qgetevent(t0 + 1)))
-	return;
-    pushnode(bufstack, ztrdup(s));
-    stackhist = t0 + 1;
 }
 
 /**/
 void
 infernexthistory(void)
 {
-    int t0;
-    char *s;
+    Histent he;
 
-    for (t0 = histline - 2;; t0--) {
-	if (!(s = qgetevent(t0))) {
-	    feep();
+    for (he = movehistent(quietgethist(histline), -2, HIST_FOREIGN);
+	 he; he = movehistent(he, -1, HIST_FOREIGN)) {
+	if (!metadiffer(ZLETEXT(he), (char *) line, ll)) {
+	    he = movehistent(he, 1, HIST_FOREIGN);
+	    zle_setline(he);
 	    return;
 	}
-	if (! metadiffer(s, (char *) line, ll))
-	    break;
     }
-    if (!(s = qgetevent(t0 + 1))) {
-	feep();
-	return;
-    }
-    zle_goto_hist(t0 + 1);
+    feep();
 }
 
 /**/
@@ -925,7 +906,7 @@ vifetchhistory(void)
 	    return;
 	}
     }
-    if (!zle_goto_hist((zmod.flags & MOD_MULT) ? zmult : curhist) &&
+    if (!zle_goto_hist((zmod.flags & MOD_MULT) ? zmult : curhist, 0) &&
 	isset(HISTBEEP))
 	feep();
 }
@@ -1041,7 +1022,8 @@ vihistorysearchbackward(void)
 void
 virepeatsearch(void)
 {
-    int hl = histline, t0;
+    Histent he;
+    int t0;
     int n = zmult;
     char *s;
 
@@ -1054,23 +1036,21 @@ virepeatsearch(void)
 	visrchsense = -visrchsense;
     }
     t0 = strlen(visrchstr);
-    for (;;) {
-	hl += visrchsense;
-	if (!(s = zle_get_event(hl))) {
-	    feep();
-	    return;
-	}
-	if (!metadiffer(s, (char *) line, ll))
-	    continue;
-	if (*visrchstr == '^') {
-	    if (strncmp(s, visrchstr + 1, t0 - 1) != 0)
-		continue;
-	} else if (!hstrnstr(s, 0, visrchstr, t0, 1, 1))
+    he = quietgethist(histline);
+    while ((he = movehistent(he, visrchsense, hist_skip_flags))) {
+	if (isset(HISTFINDNODUPS) && he->flags & HIST_DUP)
 	    continue;
-	if (--n <= 0)
-	    break;
+	s = ZLETEXT(he);
+	if (metadiffer(s, (char *) line, ll)
+	 && (*visrchstr == '^'? strncmp(s, visrchstr + 1, t0 - 1) == 0
+			      : hstrnstr(s, 0, visrchstr, t0, 1, 1) != 0)) {
+	    if (--n <= 0) {
+		zle_setline(he);
+		return;
+	    }
+	}
     }
-    zle_goto_hist(hl);
+    feep();
 }
 
 /**/
@@ -1090,8 +1070,8 @@ virevrepeatsearch(void)
 void
 historybeginningsearchbackward(void)
 {
+    Histent he;
     int cpos = cs;		/* save cursor position */
-    int hl = histline;
     int n = zmult;
     char *s;
 
@@ -1101,20 +1081,21 @@ historybeginningsearchbackward(void)
 	zmult = n;
 	return;
     }
-    for (;;) {
-	hl--;
-	if (!(s = zle_get_event(hl))) {
-	    feep();
-	    return;
-	}
+    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, (char *)line, cs) < 0 &&
-	    metadiffer(s, (char *)line, ll))
-	    if (--n <= 0)
-		break;
+	    metadiffer(s, (char *)line, ll)) {
+	    if (--n <= 0) {
+		zle_setline(he);
+		cs = cpos;
+		return;
+	    }
+	}
     }
-
-    zle_goto_hist(hl);
-    cs = cpos;
+    feep();
 }
 
 /* Extra function added by A.R. Iano-Fletcher.	*/
@@ -1124,8 +1105,8 @@ historybeginningsearchbackward(void)
 void
 historybeginningsearchforward(void)
 {
+    Histent he;
     int cpos = cs;		/* save cursor position */
-    int hl = histline;
     int n = zmult;
     char *s;
 
@@ -1135,18 +1116,19 @@ historybeginningsearchforward(void)
 	zmult = n;
 	return;
     }
-    for (;;) {
-	hl++;
-	if (!(s = zle_get_event(hl))) {
-	    feep();
-	    return;
+    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, (char *)line, cs) < (he->histnum == curhist) &&
+	    metadiffer(s, (char *)line, ll)) {
+	    if (--n <= 0) {
+		zle_setline(he);
+		cs = cpos;
+		return;
+	    }
 	}
-	if (metadiffer(s, (char *)line, cs) < (hl == curhist) &&
-	    metadiffer(s, (char *)line, ll))
-	    if (--n <= 0)
-		break;
     }
-
-    zle_goto_hist(hl);
-    cs = cpos;
+    feep();
 }
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index 1baa3a845..7504ed809 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -130,6 +130,7 @@ createkeymapnamtab(void)
     keymapnamtab->hash        = hasher;
     keymapnamtab->emptytable  = emptyhashtable;
     keymapnamtab->filltable   = NULL;
+    keymapnamtab->cmpnodes    = strcmp;
     keymapnamtab->addnode     = addhashnode;
     keymapnamtab->getnode     = gethashnode2;
     keymapnamtab->getnode2    = gethashnode2;
@@ -172,6 +173,7 @@ newkeytab(char *kmname)
     ht->hash        = hasher;
     ht->emptytable  = emptyhashtable;
     ht->filltable   = NULL;
+    ht->cmpnodes    = strcmp;
     ht->addnode     = addhashnode;
     ht->getnode     = gethashnode2;
     ht->getnode2    = gethashnode2;
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index a5da84b66..dfb437308 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -111,6 +111,10 @@ int prefixflag;
 /**/
 int feepflag;
 
+#ifdef FIONREAD
+static int delayzsetterm;
+#endif
+
 /* set up terminal */
 
 /**/
@@ -119,12 +123,30 @@ zsetterm(void)
 {
     struct ttyinfo ti;
 
-#if defined(CLOBBERS_TYPEAHEAD) && defined(FIONREAD)
+#if defined(FIONREAD)
     int val;
 
     ioctl(SHTTY, FIONREAD, (char *)&val);
-    if (val)
+    if (val) {
+	/*
+	 * Problems can occur on some systems when switching from
+	 * canonical to non-canonical input.  The former is usually
+	 * set while running programmes, but the latter is necessary
+	 * for zle.  If there is input in canonical mode, then we
+	 * need to read it without setting up the terminal.  Furthermore,
+	 * while that input gets processed there may be more input
+	 * being typed (i.e. further typeahead).  This means that
+	 * we can't set up the terminal for zle *at all* until
+	 * we are sure there is no more typeahead to come.  So
+	 * if there is typeahead, we set the flag delayzsetterm.
+	 * Then getkey() performs another FIONREAD call; if that is
+	 * 0, we have finally used up all the typeahead, and it is
+	 * safe to alter the terminal, which we do at that point.
+	 */
+	delayzsetterm = 1;
 	return;
+    } else
+	delayzsetterm = 0;
 #endif
 
 /* sanitize the tty */
@@ -298,7 +320,19 @@ getkey(int keytmout)
     if (kungetct)
 	ret = STOUC(kungetbuf[--kungetct]);
     else {
-	if (keytmout) {
+#ifdef FIONREAD
+	if (delayzsetterm) {
+	    int val;
+	    ioctl(SHTTY, FIONREAD, (char *)&val);
+	    if (!val)
+		zsetterm();
+	}
+#endif
+	if (keytmout
+#ifdef FIONREAD
+	    && ! delayzsetterm
+#endif
+	    ) {
 	    if (keytimeout > 500)
 		exp100ths = 500;
 	    else if (keytimeout > 0)
@@ -461,7 +495,6 @@ zleread(char *lp, char *rp, int flags)
 	undoing = 1;
 	line = (unsigned char *)zalloc((linesz = 256) + 2);
 	virangeflag = lastcmd = done = cs = ll = mark = 0;
-	curhistline = NULL;
 	vichgflag = 0;
 	viinsbegin = 0;
 	statusline = NULL;
@@ -542,7 +575,6 @@ zleread(char *lp, char *rp, int flags)
 	zleactive = no_restore_tty = 0;
 	alarm(0);
     } LASTALLOC;
-    zsfree(curhistline);
     freeundo();
     if (eofsent) {
 	free(line);
diff --git a/Src/Zle/zle_move.c b/Src/Zle/zle_move.c
index d6e9c4b7d..7169f5700 100644
--- a/Src/Zle/zle_move.c
+++ b/Src/Zle/zle_move.c
@@ -476,17 +476,10 @@ vigotomark(void)
 	feep();
 	return;
     }
-    if (curhist != vimarkline[ch]) {
-	char *s;
-
-	remember_edits();
-	if (!(s = qgetevent(vimarkline[ch]))) {
-	    vimarkline[ch] = 0;
-	    feep();
-	    return;
-	}
-	histline = vimarkline[ch];
-	setline(s);
+    if (curhist != vimarkline[ch] && !zle_goto_hist(vimarkline[ch], 0)) {
+	vimarkline[ch] = 0;
+	feep();
+	return;
     }
     cs = vimarkcs[ch];
     if (cs > ll)
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index b879669ac..dbf982899 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -64,6 +64,7 @@ createthingytab(void)
     thingytab->hash        = hasher;
     thingytab->emptytable  = emptythingytab;
     thingytab->filltable   = NULL;
+    thingytab->cmpnodes    = strcmp;
     thingytab->addnode     = addhashnode;
     thingytab->getnode     = gethashnode;
     thingytab->getnode2    = gethashnode2;
@@ -356,7 +357,7 @@ 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) {
+    if(!op->o && n != 1 && n != 2) {
 	zerrnam(name, "wrong number of arguments", NULL, 0);
 	return 1;
     }
@@ -515,17 +516,31 @@ static int
 bin_zle_call(char *name, char **args, char *ops, char func)
 {
     Thingy t;
+    struct modifier modsave;
 
     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;
+	}
+	else {
+	    zmod.mult = 1;
+	    zmod.flags &= ~MOD_MULT;
+	}
+    }
     t = rthingy(args[0]);
     PERMALLOC {
       execzlefunc(t);
     } LASTALLOC;
     unrefthingy(t);
+    if (args[1])
+	zmod = modsave;
     return 0;
 }
 
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 8db571d0b..bc6cff8b6 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -578,6 +578,10 @@ static char *varname;
 
 static int insubscr;
 
+/* Parameter pointer for completing keys of an assoc array. */
+
+static Param keypm;
+
 /* 1 if we are completing in a quoted string (or inside `...`) */
 
 /**/
@@ -1364,9 +1368,15 @@ get_comp_string(void)
 	    zsfree(varname);
 	    varname = ztrdup(tt);
 	    *s = sav;
-	    if (skipparens(Inbrack, Outbrack, &s) > 0 || s > tt + cs - wb)
-		s = NULL, inwhat = IN_MATH, insubscr = 1;
-	    else if (*s == '=' && cs > wb + (s - tt)) {
+	    if (skipparens(Inbrack, Outbrack, &s) > 0 || s > tt + cs - wb) {
+		s = NULL;
+		inwhat = IN_MATH;
+		if ((keypm = (Param) paramtab->getnode(paramtab, varname)) &&
+		    (keypm->flags & PM_HASHED))
+		    insubscr = 2;
+		else
+		    insubscr = 1;
+	    } else if (*s == '=' && cs > wb + (s - tt)) {
 		s++;
 		wb += s - tt;
 		t0 = STRING;
@@ -1424,11 +1434,15 @@ get_comp_string(void)
 		    zsfree(varname);
 		    varname = ztrdup(nb);
 		    *ne = sav;
+		    if ((keypm = (Param) paramtab->getnode(paramtab,
+							   varname)) &&
+			(keypm->flags & PM_HASHED))
+			insubscr = 2;
 		}
 	    }
 	}
 	if (inwhat == IN_MATH) {
-	    if (compfunc) {
+	    if (compfunc || insubscr == 2) {
 		int lev;
 		char *p;
 
@@ -1472,7 +1486,11 @@ get_comp_string(void)
 		zsfree(varname);
 		varname = ztrdup((char *) line + i + 1);
 		line[wb - 1] = sav;
-		insubscr = 1;
+		if ((keypm = (Param) paramtab->getnode(paramtab, varname)) &&
+		    (keypm->flags & PM_HASHED))
+		    insubscr = 2;
+		else
+		    insubscr = 1;
 	    }
 	}
 	/* This variable will hold the current word in quoted form. */
@@ -3524,7 +3542,7 @@ int
 addmatches(Cadata dat, char **argv)
 {
     char *s, *ms, *lipre = NULL, *lisuf = NULL, *lpre = NULL, *lsuf = NULL;
-    char **aign = NULL, **dparr;
+    char **aign = NULL, **dparr = NULL;
     int lpl, lsl, pl, sl, bpl, bsl, llpl = 0, llsl = 0, nm = mnum;
     int oisalt = 0, isalt, isexact, doadd;
     Cline lc = NULL;
@@ -4360,7 +4378,7 @@ callcompfunc(char *s, char *fn)
 
 	    PERMALLOC {
 		q = compwords = (char **)
-		    zalloc((clwnum - aadd + 1) * sizeof(char *));
+		    zalloc((clwnum + 1) * sizeof(char *));
 		for (p = clwords + aadd; *p; p++, q++) {
 		    tmp = dupstring(*p);
 		    untokenize(tmp);
@@ -4829,13 +4847,22 @@ makecomplistglobal(char *os, int incmd, int lst, int flags)
     char *s;
 
     ccont = CC_CCCONT;
+    cc_dummy.suffix = NULL;
 
     if (linwhat == IN_ENV) {
         /* Default completion for parameter values. */
         cc = &cc_default;
+	keypm = NULL;
     } else if (linwhat == IN_MATH) {
-        /* Parameter names inside mathematical expression. */
-        cc_dummy.mask = CC_PARAMS;
+	if (insubscr == 2) {
+	    /* Inside subscript of assoc array, complete keys. */
+	    cc_dummy.mask = 0;
+	    cc_dummy.suffix = "]";
+	} else {
+	    /* Other math environment, complete paramete names. */
+	    keypm = NULL;
+	    cc_dummy.mask = CC_PARAMS;
+	}
 	cc = &cc_dummy;
 	cc_dummy.refc = 10000;
     } else if (linwhat == IN_COND) {
@@ -4851,13 +4878,16 @@ makecomplistglobal(char *os, int incmd, int lst, int flags)
 	    (CC_FILES | CC_PARAMS);
 	cc = &cc_dummy;
 	cc_dummy.refc = 10000;
-    } else if (linredir)
+	keypm = NULL;
+    } else if (linredir) {
 	/* In redirections use default completion. */
 	cc = &cc_default;
-    else
+	keypm = NULL;
+    } else {
 	/* Otherwise get the matches for the command. */
+	keypm = NULL;
 	return makecomplistcmd(os, incmd, flags);
-
+    }
     if (cc) {
 	/* First, use the -T compctl. */
 	if (!(flags & CFN_FIRST)) {
@@ -5917,7 +5947,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	Comp compc = NULL;
 	char *e, *h, hpatsav;
 	Histent he;
-	int i = curhist - 1, n = cc->hnum;
+	int i = addhistnum(curhist,-1,HIST_FOREIGN), n = cc->hnum;
 
 	/* Parse the pattern, if it isn't the null string. */
 	if (*(cc->hpat)) {
@@ -5971,7 +6001,12 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
     if ((t = cc->mask & (CC_ALREG | CC_ALGLOB)))
 	/* Add the two types of aliases. */
 	dumphashtable(aliastab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
-
+    if (keypm && cc == &cc_dummy) {
+	/* Add the keys of the parameter in keypm. */
+	scanhashtable(keypm->gets.hfn(keypm), 0, 0, PM_UNSET, addhnmatch, 0);
+	keypm = NULL;
+	cc_dummy.suffix = NULL;
+    }
     if (!errflag && cc->ylist) {
 	/* generate the user-defined display list: if anything fails, *
 	 * we silently allow the normal completion list to be used.   */
diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c
index 4cdb3c52d..8524fd21e 100644
--- a/Src/Zle/zle_utils.c
+++ b/Src/Zle/zle_utils.c
@@ -572,10 +572,8 @@ undo(void)
 static void
 unapplychange(struct change *ch)
 {
-    if(ch->hist != histline) {
-	remember_edits();
-	setline(zle_get_event(histline = ch->hist));
-    }
+    if(ch->hist != histline)
+	zle_setline(quietgethist(ch->hist));
     cs = ch->off;
     if(ch->ins)
 	foredel(ztrlen(ch->ins));
@@ -613,10 +611,8 @@ redo(void)
 static void
 applychange(struct change *ch)
 {
-    if(ch->hist != histline) {
-	remember_edits();
-	setline(zle_get_event(histline = ch->hist));
-    }
+    if(ch->hist != histline)
+	zle_setline(quietgethist(ch->hist));
     cs = ch->off;
     if(ch->del)
 	foredel(ztrlen(ch->del));
diff --git a/Src/builtin.c b/Src/builtin.c
index 9ffcad1b3..8436cde33 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -120,7 +120,7 @@ static struct builtin builtins[] =
     BUILTIN("which", 0, bin_whence, 0, -1, 0, "ampsw", "c"),
 
 #ifdef DYNAMIC
-    BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "LaudicIp", NULL),
+    BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "ILabcdipu", NULL),
 #endif
 };
 
@@ -142,6 +142,7 @@ createbuiltintable(void)
     builtintab->hash        = hasher;
     builtintab->emptytable  = NULL;
     builtintab->filltable   = NULL;
+    builtintab->cmpnodes    = strcmp;
     builtintab->addnode     = addhashnode;
     builtintab->getnode     = gethashnode;
     builtintab->getnode2    = gethashnode2;
@@ -1146,19 +1147,17 @@ bin_fc(char *nam, char **argv, char *ops, int func)
     }
     if (ops['R']) {
 	/* read history from a file */
-	readhistfile(*argv ? *argv : getsparam("HISTFILE"), 1);
+	readhistfile(*argv, 1, ops['I'] ? HFILE_SKIPOLD : 0);
 	return 0;
     }
     if (ops['W']) {
 	/* write history to a file */
-	savehistfile(*argv ? *argv : getsparam("HISTFILE"), 1,
-		     (ops['I'] ? 2 : 0));
+	savehistfile(*argv, 1, ops['I'] ? HFILE_SKIPOLD : 0);
 	return 0;
     }
     if (ops['A']) {
 	/* append history to a file */
-	savehistfile(*argv ? *argv : getsparam("HISTFILE"), 1,
-		     (ops['I'] ? 3 : 1));
+	savehistfile(*argv, 1, HFILE_APPEND | (ops['I'] ? HFILE_SKIPOLD : 0));
 	return 0;
     }
     if (!(ops['l'] && unset(HISTNOSTORE)))
@@ -1200,9 +1199,9 @@ bin_fc(char *nam, char **argv, char *ops, int func)
     }
     /* default values of first and last, and range checking */
     if (first == -1)
-	first = (ops['l']) ? curhist - 16 : curhist - 1;
+	first = ops['l']? addhistnum(curhist,-16,0) : addhistnum(curhist,-1,0);
     if (last == -1)
-	last = (ops['l']) ? curhist - 1 : first;
+	last = ops['l']? addhistnum(curhist,-1,0) : first;
     if (first < firsthist())
 	first = firsthist();
     if (last == -1)
@@ -1267,7 +1266,7 @@ fcgetcomm(char *s)
      * numbers indicate reversed numbering.           */
     if ((cmd = atoi(s))) {
 	if (cmd < 0)
-	    cmd = curhist + cmd;
+	    cmd = addhistnum(curhist,cmd,HIST_FOREIGN);
 	if (cmd >= curhist) {
 	    zwarnnam("fc", "bad history number: %d", 0, cmd);
 	    return -1;
@@ -1331,7 +1330,7 @@ static int
 fclist(FILE *f, int n, int r, int D, int d, int first, int last, struct asgment *subs, Comp com)
 {
     int fclistdone = 0;
-    char *s, *hs;
+    char *s;
     Histent ent;
 
     /* reverse range if required */
@@ -1344,28 +1343,31 @@ fclist(FILE *f, int n, int r, int D, int d, int first, int last, struct asgment
     if (!subs)
 	fclistdone = 1;
 
-    for (;;) {
-	hs = quietgetevent(first);
-	if (!hs) {
+    ent = gethistent(first, first < last? GETHIST_DOWNWARD : GETHIST_UPWARD);
+    if (!ent || ent->histnum < first || ent->histnum > last) {
+	if (first == last)
 	    zwarnnam("fc", "no such event: %d", NULL, first);
-	    return 1;
-	}
-	s = dupstring(hs);
+	else
+	    zwarnnam("fc", "no events in that range", NULL, 0);
+	return 1;
+    }
+
+    for (;;) {
+	s = dupstring(ent->text);
 	/* this if does the pattern matching, if required */
 	if (!com || domatch(s, com, 0)) {
 	    /* perform substitution */
 	    fclistdone |= fcsubs(&s, subs);
 
 	    /* do numbering */
-	    if (n)
-		fprintf(f, "%5d  ", first);
-	    ent = NULL;
+	    if (n) {
+		fprintf(f, "%5d%c ", ent->histnum,
+			ent->flags & HIST_FOREIGN? '*' : ' ');
+	    }
 	    /* output actual time (and possibly date) of execution of the
 	    command, if required */
 	    if (d) {
 		struct tm *ltm;
-		if (!ent)
-		    ent = gethistent(first);
 		ltm = localtime(&ent->stim);
 		if (d >= 2) {
 		    if (d >= 8) {
@@ -1387,8 +1389,6 @@ fclist(FILE *f, int n, int r, int D, int d, int first, int last, struct asgment
 	    /* display the time taken by the command, if required */
 	    if (D) {
 		long diff;
-		if (!ent)
-		    ent = gethistent(first);
 		diff = (ent->ftim) ? ent->ftim - ent->stim : 0;
 		fprintf(f, "%ld:%02ld  ", diff / 60, diff % 60);
 	    }
@@ -1401,12 +1401,14 @@ fclist(FILE *f, int n, int r, int D, int d, int first, int last, struct asgment
 		fprintf(f, "%s\n", s);
 	}
 	/* move on to the next history line, or quit the loop */
-	if (first == last)
-	    break;
-	else if (first > last)
-	    first--;
-	else
-	    first++;
+	if (first < last) {
+	    if (!(ent = down_histent(ent)) || ent->histnum > last)
+		break;
+	}
+	else {
+	    if (!(ent = up_histent(ent)) || ent->histnum < last)
+		break;
+	}
     }
 
     /* final processing */
@@ -1768,7 +1770,8 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 	    for (i = 0; i < paramtab->hsize; i++) {
 		for (pm = (Param) paramtab->nodes[i]; pm;
 		     pm = (Param) pm->next) {
-		    if ((pm->flags & PM_RESTRICTED) && isset(RESTRICTED))
+		    if (((pm->flags & PM_RESTRICTED) && isset(RESTRICTED)) ||
+			(pm->flags & PM_UNSET))
 			continue;
 		    if (domatch(pm->nam, com, 0))
 			addlinknode(pmlist, pm);
@@ -2523,10 +2526,7 @@ bin_print(char *name, char **args, char *ops, int func)
 	char **pargs = args;
 
 	PERMALLOC {
-	    ent = gethistent(++curhist);
-	    zsfree(ent->text);
-	    if (ent->nwords)
-		zfree(ent->words, ent->nwords*2*sizeof(short));
+	    ent = prepnexthistent(++curhist);
 	    while (*pargs++)
 		nwords++;
 	    if ((ent->nwords = nwords)) {
@@ -2543,6 +2543,7 @@ bin_print(char *name, char **args, char *ops, int func)
 	    ent->text = zjoin(args, ' ');
 	    ent->stim = ent->ftim = time(NULL);
 	    ent->flags = 0;
+	    addhistnode(histtab, ent->text, ent);
 	} LASTALLOC;
 	return 0;
     }
@@ -2952,7 +2953,7 @@ zexit(int val, int from_signal)
 	    killrunjobs(from_signal);
 	if (isset(RCS) && interact) {
 	    if (!nohistsave)
-		savehistfile(getsparam("HISTFILE"), 1, isset(APPENDHISTORY) ? 3 : 0);
+		savehistfile(NULL, 1, HFILE_USE_OPTIONS);
 	    if (islogin && !subsh) {
 		sourcehome(".zlogout");
 #ifdef GLOBAL_ZLOGOUT
diff --git a/Src/exec.c b/Src/exec.c
index 764b7140c..da2bf9fe2 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -1862,7 +1862,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 		/* If we are exec'ing a command, and we are not in a subshell, *
 		 * then check if we should save the history file.              */
 		if (isset(RCS) && interact && !nohistsave)
-		    savehistfile(getsparam("HISTFILE"), 1, isset(APPENDHISTORY) ? 3 : 0);
+		    savehistfile(NULL, 1, HFILE_USE_OPTIONS);
 		exit(lastval);
 	    }
 
@@ -1874,7 +1874,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
 		/* If we are exec'ing a command, and we are not *
 		 * in a subshell, then save the history file.   */
 		if (!subsh && isset(RCS) && interact && !nohistsave)
-		    savehistfile(getsparam("HISTFILE"), 1, isset(APPENDHISTORY) ? 3 : 0);
+		    savehistfile(NULL, 1, HFILE_USE_OPTIONS);
 	    }
 	    if (type == SIMPLE) {
 		if (cmd->vars) {
@@ -2232,7 +2232,7 @@ getoutput(char *cmd, int qt)
 	zclose(pipes[1]);
 	retval = readoutput(pipes[0], qt);
 	fdtable[pipes[0]] = 0;
-	child_suspend(0);		/* unblocks */
+	waitforpid(pid);		/* unblocks */
 	lastval = cmdoutval;
 	return retval;
     }
diff --git a/Src/hashtable.c b/Src/hashtable.c
index 72e4db21b..0a640349b 100644
--- a/Src/hashtable.c
+++ b/Src/hashtable.c
@@ -83,7 +83,7 @@ hasher(char *str)
     unsigned hashval = 0;
 
     while (*str)
-	hashval += (hashval << 5) + ((unsigned) *str++);
+	hashval += (hashval << 5) + *(unsigned char *)str++;
 
     return hashval;
 }
@@ -138,7 +138,7 @@ deletehashtable(HashTable ht)
 }
 
 /* Add a node to a hash table.                          *
- * nam is the key to use in hashing.  dat is a pointer  *
+ * nam is the key to use in hashing.  nodeptr points    *
  * to the node to add.  If there is already a node in   *
  * the table with the same key, it is first freed, and  *
  * then the new node is added.  If the number of nodes  *
@@ -149,6 +149,17 @@ deletehashtable(HashTable ht)
 void
 addhashnode(HashTable ht, char *nam, void *nodeptr)
 {
+    HashNode oldnode = addhashnode2(ht, nam, nodeptr);
+    if (oldnode)
+	ht->freenode(oldnode);
+}
+
+/* Add a node to a hash table, returning the old node on replacment. */
+
+/**/
+HashNode
+addhashnode2(HashTable ht, char *nam, void *nodeptr)
+{
     unsigned hashval;
     HashNode hn, hp, hq;
 
@@ -164,11 +175,11 @@ addhashnode(HashTable ht, char *nam, void *nodeptr)
 	ht->nodes[hashval] = hn;
 	if (++ht->ct >= ht->hsize * 2 && !ht->scan)
 	    expandhashtable(ht);
-	return;
+	return NULL;
     }
 
     /* else check if the first node contains the same key */
-    if (!strcmp(hp->nam, hn->nam)) {
+    if (ht->cmpnodes(hp->nam, hn->nam) == 0) {
 	ht->nodes[hashval] = hn;
 	replacing:
 	hn->next = hp->next;
@@ -182,15 +193,14 @@ addhashnode(HashTable ht, char *nam, void *nodeptr)
 	    } else if(ht->scan->u.u == hp)
 		ht->scan->u.u = hn;
 	}
-	ht->freenode(hp);
-	return;
+	return hp;
     }
 
     /* else run through the list and check all the keys */
     hq = hp;
     hp = hp->next;
     for (; hp; hq = hp, hp = hp->next) {
-	if (!strcmp(hp->nam, hn->nam)) {
+	if (ht->cmpnodes(hp->nam, hn->nam) == 0) {
 	    hq->next = hn;
 	    goto replacing;
 	}
@@ -201,6 +211,7 @@ addhashnode(HashTable ht, char *nam, void *nodeptr)
     ht->nodes[hashval] = hn;
     if (++ht->ct >= ht->hsize * 2 && !ht->scan)
         expandhashtable(ht);
+    return NULL;
 }
 
 /* Get an enabled entry in a hash table.  *
@@ -217,7 +228,7 @@ gethashnode(HashTable ht, char *nam)
 
     hashval = ht->hash(nam) % ht->hsize;
     for (hp = ht->nodes[hashval]; hp; hp = hp->next) {
-	if (!strcmp(hp->nam, nam)) {
+	if (ht->cmpnodes(hp->nam, nam) == 0) {
 	    if (hp->flags & DISABLED)
 		return NULL;
 	    else
@@ -241,7 +252,7 @@ gethashnode2(HashTable ht, char *nam)
 
     hashval = ht->hash(nam) % ht->hsize;
     for (hp = ht->nodes[hashval]; hp; hp = hp->next) {
-	if (!strcmp(hp->nam, nam))
+	if (ht->cmpnodes(hp->nam, nam) == 0)
 	    return hp;
     }
     return NULL;
@@ -267,7 +278,7 @@ removehashnode(HashTable ht, char *nam)
 	return NULL;
 
     /* else check if the key in the first one matches */
-    if (!strcmp(hp->nam, nam)) {
+    if (ht->cmpnodes(hp->nam, nam) == 0) {
 	ht->nodes[hashval] = hp->next;
 	gotit:
 	ht->ct--;
@@ -288,7 +299,7 @@ removehashnode(HashTable ht, char *nam)
     hq = hp;
     hp = hp->next;
     for (; hp; hq = hp, hp = hp->next) {
-	if (!strcmp(hp->nam, nam)) {
+	if (ht->cmpnodes(hp->nam, nam) == 0) {
 	    hq->next = hp->next;
 	    goto gotit;
 	}
@@ -316,7 +327,7 @@ enablehashnode(HashNode hn, int flags)
     hn->flags &= ~DISABLED;
 }
 
-/* Compare two hash table entries */
+/* Compare two hash table entries by name */
 
 /**/
 static int
@@ -498,6 +509,7 @@ emptyhashtable(HashTable ht)
     resizehashtable(ht, ht->hsize);
 }
 
+/**/
 #ifdef ZSH_HASH_DEBUG
 
 /* Print info about hash table */
@@ -550,6 +562,7 @@ bin_hashinfo(char *nam, char **args, char *ops, int func)
     return 0;
 }
 
+/**/
 #endif /* ZSH_HASH_DEBUG */
 
 /********************************/
@@ -577,6 +590,7 @@ createcmdnamtable(void)
     cmdnamtab->hash        = hasher;
     cmdnamtab->emptytable  = emptycmdnamtable;
     cmdnamtab->filltable   = fillcmdnamtable;
+    cmdnamtab->cmpnodes    = strcmp;
     cmdnamtab->addnode     = addhashnode;
     cmdnamtab->getnode     = gethashnode2;
     cmdnamtab->getnode2    = gethashnode2;
@@ -747,6 +761,7 @@ createshfunctable(void)
     shfunctab->hash        = hasher;
     shfunctab->emptytable  = NULL;
     shfunctab->filltable   = NULL;
+    shfunctab->cmpnodes    = strcmp;
     shfunctab->addnode     = addhashnode;
     shfunctab->getnode     = gethashnode;
     shfunctab->getnode2    = gethashnode2;
@@ -920,6 +935,7 @@ createreswdtable(void)
     reswdtab->hash        = hasher;
     reswdtab->emptytable  = NULL;
     reswdtab->filltable   = NULL;
+    reswdtab->cmpnodes    = strcmp;
     reswdtab->addnode     = addhashnode;
     reswdtab->getnode     = gethashnode;
     reswdtab->getnode2    = gethashnode2;
@@ -980,6 +996,7 @@ createaliastable(void)
     aliastab->hash        = hasher;
     aliastab->emptytable  = NULL;
     aliastab->filltable   = NULL;
+    aliastab->cmpnodes    = strcmp;
     aliastab->addnode     = addhashnode;
     aliastab->getnode     = gethashnode;
     aliastab->getnode2    = gethashnode2;
@@ -1109,6 +1126,7 @@ createnameddirtable(void)
     nameddirtab->hash        = hasher;
     nameddirtab->emptytable  = emptynameddirtable;
     nameddirtab->filltable   = fillnameddirtable;
+    nameddirtab->cmpnodes    = strcmp;
     nameddirtab->addnode     = addnameddirnode;
     nameddirtab->getnode     = gethashnode;
     nameddirtab->getnode2    = gethashnode2;
@@ -1219,3 +1237,137 @@ printnameddirnode(HashNode hn, int printflags)
     quotedzputs(nd->dir, stdout);
     putchar('\n');
 }
+
+/*************************************/
+/* History Line Hash Table Functions */
+/*************************************/
+
+/**/
+void
+createhisttable(void)
+{
+    histtab = newhashtable(599, "histtab", NULL);
+
+    histtab->hash        = histhasher;
+    histtab->emptytable  = emptyhisttable;
+    histtab->filltable   = NULL;
+    histtab->cmpnodes    = histstrcmp;
+    histtab->addnode     = addhistnode;
+    histtab->getnode     = gethashnode2;
+    histtab->getnode2    = gethashnode2;
+    histtab->removenode  = removehashnode;
+    histtab->disablenode = NULL;
+    histtab->enablenode  = NULL;
+    histtab->freenode    = freehistnode;
+    histtab->printnode   = NULL;
+}
+
+/**/
+unsigned
+histhasher(char *str)
+{
+    unsigned hashval = 0;
+
+    while (inblank(*str)) str++;
+
+    while (*str) {
+	if (inblank(*str)) {
+	    do str++; while (inblank(*str));
+	    if (*str)
+		hashval += (hashval << 5) + ' ';
+	}
+	else
+	    hashval += (hashval << 5) + *(unsigned char *)str++;
+    }
+    return hashval;
+}
+
+/**/
+void
+emptyhisttable(HashTable ht)
+{
+    emptyhashtable(ht);
+    if (hist_ring)
+	histremovedups();
+}
+
+/* Compare two strings with normalized white-space */
+
+/**/
+int
+histstrcmp(const char *str1, const char *str2)
+{
+    while (inblank(*str1)) str1++;
+    while (inblank(*str2)) str2++;
+    while (*str1 && *str2) {
+	if (inblank(*str1)) {
+	    if (!inblank(*str2))
+		break;
+	    do str1++; while (inblank(*str1));
+	    do str2++; while (inblank(*str2));
+	}
+	else {
+	    if (*str1 != *str2)
+		break;
+	    str1++;
+	    str2++;
+	}
+    }
+    return *str1 - *str2;
+}
+
+/**/
+void
+addhistnode(HashTable ht, char *nam, void *nodeptr)
+{
+    HashNode oldnode = addhashnode2(ht, nam, nodeptr);
+    Histent he = (Histent)nodeptr;
+    if (oldnode && oldnode != nodeptr) {
+	if (he->flags & HIST_MAKEUNIQUE
+	 || (he->flags & HIST_FOREIGN && (Histent)oldnode == he->up)) {
+	    he->flags |= HIST_DUP;
+	    addhashnode(ht, oldnode->nam, oldnode); /* Remove the new dup */
+	}
+	else {
+	    oldnode->flags |= HIST_DUP;
+	    if (hist_ignore_all_dups)
+		freehistnode(oldnode); /* Remove the old dup */
+	}
+    }
+    else
+	he->flags &= ~HIST_MAKEUNIQUE;
+}
+
+/**/
+void
+freehistnode(HashNode nodeptr)
+{
+    freehistdata((Histent)nodeptr, 1);
+    zfree(nodeptr, sizeof (struct histent));
+}
+
+/**/
+void
+freehistdata(Histent he, int unlink)
+{
+    if (!he)
+	return;
+
+    if (!(he->flags & HIST_DUP))
+	removehashnode(histtab, he->text);
+
+    zsfree(he->text);
+    if (he->nwords)
+	zfree(he->words, he->nwords*2*sizeof(short));
+
+    if (unlink) {
+	if (!--histlinect)
+	    hist_ring = NULL;
+	else {
+	    if (he == hist_ring)
+		hist_ring = hist_ring->up;
+	    he->up->down = he->down;
+	    he->down->up = he->up;
+	}
+    }
+}
diff --git a/Src/hist.c b/Src/hist.c
index e104e1f3e..55209a1c0 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -56,23 +56,29 @@ int excs, exlast;
  * Note on curhist: with history inactive, this points to the
  * last line actually added to the history list.  With history active,
  * the line does not get added to the list until hend(), if at all.
- * However, curhist is incremented to reflect the current line anyway.
- * Thus if the line is not added to the list, curhist must be
+ * However, curhist is incremented to reflect the current line anyway
+ * and a temporary history entry is inserted while the user is editing.
+ * If the resulting line was not added to the list, curhist is
  * decremented in hend().
  */
  
 /**/
 int curhist;
 
-/* number of history entries */
- 
 /**/
-int histentct;
- 
-/* array of history entries */
- 
+struct histent curline;
+
+/* current line count of allocated history entries */
+
+/**/
+int histlinect;
+
+/* The history lines are kept in a hash, and also doubly-linked in a ring */
+
+/**/
+HashTable histtab;
 /**/
-Histent histentarr;
+Histent hist_ring;
  
 /* capacity of history lists */
  
@@ -90,6 +96,17 @@ int histdone;
 /**/
 int histactive;
 
+/* Current setting of the associated option, but sometimes also includes
+ * the setting of the HIST_SAVE_NO_DUPS option. */
+
+/**/
+int hist_ignore_all_dups;
+
+/* What flags (if any) we should skip when moving through the history */
+
+/**/
+int hist_skip_flags;
+
 /* Bits of histactive variable */
 #define HA_ACTIVE	(1<<0)	/* History mechanism is active */
 #define HA_NOSTORE	(1<<1)	/* Don't store the line when finished */
@@ -395,7 +412,7 @@ histsubchar(int c)
 	    if (!*buf) {
 		if (c != '%') {
 		    if (isset(CSHJUNKIEHISTORY))
-			ev = curhist - 1;
+			ev = addhistnum(curhist,-1,HIST_FOREIGN);
 		    else
 			ev = defev;
 		    if (c == ':' && evset == -1)
@@ -410,10 +427,10 @@ histsubchar(int c)
 		    evset = 0;
 		}
 	    } else if ((t0 = atoi(buf))) {
-		ev = (t0 < 0) ? curhist + t0 : t0;
+		ev = (t0 < 0) ? addhistnum(curhist,t0,HIST_FOREIGN) : t0;
 		evset = 1;
 	    } else if ((unsigned)*buf == bangchar) {
-		ev = curhist - 1;
+		ev = addhistnum(curhist,-1,HIST_FOREIGN);
 		evset = 1;
 	    } else if (*buf == '#') {
 		ev = curhist;
@@ -650,8 +667,6 @@ strinend(void)
 void
 hbegin(void)
 {
-    Histent curhistent;
-
     isfirstln = isfirstch = 1;
     errflag = histdone = spaceflag = 0;
     stophist = (!interact || unset(BANGHIST) || unset(SHINSTDIN)) << 1;
@@ -661,43 +676,23 @@ hbegin(void)
 
     if (histactive & HA_JUNKED)
 	curhist--;
-    curhistent = gethistent(curhist);
-    if (!curhistent->ftim)
-	curhistent->ftim = time(NULL);
-    histactive = HA_ACTIVE;
+    if (hist_ring && !hist_ring->ftim)
+	hist_ring->ftim = time(NULL);
     if (interact && isset(SHINSTDIN) && !strin) {
+	histactive = HA_ACTIVE;
 	attachtty(mypgrp);
-	defev = curhist;
-	curhist++;
+	if (!hist_ring)
+	    hist_ring = curline.up = curline.down = &curline;
+	else {
+	    curline.up = hist_ring;
+	    curline.down = hist_ring->down;
+	    hist_ring->down = hist_ring->down->up = &curline;
+	    hist_ring = &curline;
+	}
+	curline.histnum = ++curhist;
+	defev = addhistnum(curhist, -1, HIST_FOREIGN);
     } else
-	histactive |= HA_NOINC;
-}
-
-/* compare current line with history entry using only text in words */
-
-/**/
-static int
-histcmp(Histent he)
-{
-    int kword, lword;
-    int nwords = chwordpos/2;
-
-    /* If the history entry came from a file, the words were not
-     * divided by the lexer so we have to resort to strcmp.
-     */
-    if (he->flags & HIST_READ)
-	return strcmp(he->text, chline);
-
-    if (nwords != he->nwords)
-	return 1;
-
-    for (kword = 0; kword < 2*nwords; kword += 2)
-	if ((lword = chwords[kword+1]-chwords[kword])
-	    != he->words[kword+1]-he->words[kword] ||
-	    memcmp(he->text+he->words[kword], chline+chwords[kword], lword))
-	    return 1;
-
-    return 0;
+	histactive = HA_ACTIVE | HA_NOINC;
 }
 
 /**/
@@ -726,6 +721,140 @@ histreduceblanks(void)
     chline[pos] = '\0';
 }
 
+/**/
+void
+histremovedups(void)
+{
+    Histent he, next;
+    for (he = hist_ring; he; he = next) {
+	next = up_histent(he);
+	if (he->flags & HIST_DUP)
+	    freehistnode((HashNode)he);
+    }
+}
+
+/**/
+int
+addhistnum(int hl, int n, int xflags)
+{
+    int dir = n < 0? -1 : n > 0? 1 : 0;
+    Histent he = gethistent(hl, dir);
+			     
+    if (!he)
+	return 0;
+    if (he->histnum != hl)
+	n -= dir;
+    if (n)
+	he = movehistent(he, n, xflags);
+    if (!he)
+	return dir < 0? firsthist() : curhist;
+    return he->histnum;
+}
+
+/**/
+Histent
+movehistent(Histent he, int n, int xflags)
+{
+    while (n < 0) {
+	if (!(he = up_histent(he)))
+	    return NULL;
+	if (!(he->flags & xflags))
+	    n++;
+    }
+    while (n > 0) {
+	if (!(he = down_histent(he)))
+	    return NULL;
+	if (!(he->flags & xflags))
+	    n--;
+    }
+    return he;
+}
+
+/**/
+Histent
+up_histent(Histent he)
+{
+    return he->up == hist_ring? NULL : he->up;
+}
+
+/**/
+Histent
+down_histent(Histent he)
+{
+    return he == hist_ring? NULL : he->down;
+}
+
+/**/
+Histent
+gethistent(int ev, int nearmatch)
+{
+    Histent he;
+
+    if (!hist_ring)
+	return NULL;
+
+    if (ev - hist_ring->down->histnum < hist_ring->histnum - ev) {
+	for (he = hist_ring->down; he->histnum <= ev; he = he->down) {
+	    if (he->histnum == ev)
+		return he;
+	}
+	if (nearmatch < 0)
+	    return up_histent(he);
+	if (nearmatch > 0)
+	    return he;
+    }
+    else {
+	for (he = hist_ring; he->histnum >= ev; he = he->up) {
+	    if (he->histnum == ev)
+		return he;
+	}
+	if (nearmatch < 0)
+	    return he;
+	if (nearmatch > 0)
+	    return down_histent(he);
+    }
+
+    return NULL;
+}
+
+/**/
+Histent
+prepnexthistent(int histnum)
+{
+    Histent he;
+
+    if (histlinect < histsiz) {
+	he = (Histent)zcalloc(sizeof *he);
+	if (!hist_ring)
+	    hist_ring = he->up = he->down = he;
+	else {
+	    he->up = hist_ring;
+	    he->down = hist_ring->down;
+	    hist_ring->down = he->down->up = he;
+	    hist_ring = he;
+	}
+	histlinect++;
+    }
+    else {
+	he = hist_ring->down;
+	if (isset(HISTEXPIREDUPSFIRST) && !(he->flags & HIST_DUP)) {
+	    do {
+		he = he->down;
+	    } while (he != hist_ring->down && !(he->flags & HIST_DUP)) ;
+	    if (he != hist_ring->down) {
+		he->up->down = he->down;
+		he->down->up = he->up;
+		he->up = hist_ring;
+		he->down = hist_ring->down;
+		hist_ring->down = he->down->up = he;
+	    }
+	}
+	freehistdata(hist_ring = he, 0);
+    }
+    hist_ring->histnum = histnum;
+    return hist_ring;
+}
+
 /* say we're done using the history mechanism */
 
 /**/
@@ -733,19 +862,35 @@ int
 hend(void)
 {
     int flag, save = 1;
+    char *hf = getsparam("HISTFILE");
 
     DPUTS(!chline, "BUG: chline is NULL in hend()");
     if (histdone & HISTFLAG_SETTY)
 	settyinfo(&shttyinfo);
+    if (!(histactive & HA_NOINC)) {
+	curline.up->down = curline.down;
+	curline.down->up = curline.up;
+	if (hist_ring == &curline) {
+	    if (!histlinect)
+		hist_ring = NULL;
+	    else
+		hist_ring = curline.up;
+	}
+	curhist--;
+    }
     if (histactive & (HA_NOSTORE|HA_NOINC)) {
 	zfree(chline, hlinesz);
 	zfree(chwords, chwordlen*sizeof(short));
 	chline = NULL;
-	if (!(histactive & HA_NOINC))
-	    curhist--;
 	histactive = 0;
 	return 1;
     }
+    if (hist_ignore_all_dups != isset(HISTIGNOREALLDUPS)
+     && (hist_ignore_all_dups = isset(HISTIGNOREALLDUPS)) != 0)
+	histremovedups();
+    /* For history sharing, lock history file once for both read and write */
+    if (isset(SHAREHISTORY) && lockhistfile(hf, 0))
+	readhistfile(hf, 0, HFILE_USE_OPTIONS | HFILE_FAST);
     flag = histdone;
     histdone = 0;
     if (hptr < chline + 1)
@@ -781,8 +926,10 @@ hend(void)
     }
     if (save) {
 	Histent he;
-	int keepflags = 0;
+	int keepflags;
 
+	for (he = hist_ring; he && he->flags & hist_skip_flags;
+	     he = up_histent(he)) ;
 #ifdef DEBUG
 	/* debugging only */
 	if (chwordpos%2) {
@@ -796,23 +943,21 @@ hend(void)
 	/* strip superfluous blanks, if desired */
 	if (isset(HISTREDUCEBLANKS))
 	    histreduceblanks();
-
-	if (isset(HISTIGNOREDUPS) && (he = gethistent(curhist - 1))
-	 && he->text && !histcmp(he)) {
+	if ((isset(HISTIGNOREDUPS) || isset(HISTIGNOREALLDUPS)) && he
+	 && histstrcmp(chline, he->text) == 0) {
 	    /* This history entry compares the same as the previous.
 	     * In case minor changes were made, we overwrite the
-	     * previous one with the current one.  This also gets
-	     * the timestamp right.  However, keep the old flags.
+	     * previous one with the current one.  This also gets the
+	     * timestamp right.  Perhaps, preserve the HIST_OLD flag.
 	     */
-	    keepflags = he->flags;
-	    curhist--;
+	    keepflags = he->flags & HIST_OLD; /* Avoid re-saving */
+	    freehistdata(he, 0);
+	} else {
+	    keepflags = 0;
+	    he = prepnexthistent(++curhist);
 	}
 
-	he =  gethistent(curhist);
-	zsfree(he->text);
 	he->text = ztrdup(chline);
-	if (he->nwords)
-	    zfree(he->words, he->nwords*2*sizeof(short));
 	he->stim = time(NULL);
 	he->ftim = 0L;
 	he->flags = keepflags;
@@ -821,12 +966,15 @@ hend(void)
 	    he->words = (short *)zalloc(chwordpos * sizeof(short));
 	    memcpy(he->words, chwords, chwordpos * sizeof(short));
 	}
-    } else
-	curhist--;
+	addhistnode(histtab, he->text, he);
+    }
     zfree(chline, hlinesz);
     zfree(chwords, chwordlen*sizeof(short));
     chline = NULL;
     histactive = 0;
+    if (isset(SHAREHISTORY) || isset(INCREMENTALAPPENDHISTORY))
+	savehistfile(hf, 1, HFILE_USE_OPTIONS | HFILE_FAST);
+    unlockhistfile(hf); /* It's OK to call this even if we aren't locked */
     return !(flag & HISTFLAG_NOEXEC || errflag);
 }
 
@@ -838,10 +986,7 @@ remhist(void)
 {
     if (!(histactive & HA_ACTIVE)) {
 	if (!(histactive & HA_JUNKED)) {
-	    /* make sure this doesn't show up when we do firsthist() */
-	    Histent he = gethistent(curhist);
-	    zsfree(he->text);
-	    he->text = NULL;
+	    freehistnode((HashNode)hist_ring);
 	    histactive |= HA_JUNKED;
 	    /* curhist-- is delayed until the next hbegin() */
 	}
@@ -1026,18 +1171,21 @@ getargspec(int argc, int marg, int evset)
 static int
 hconsearch(char *str, int *marg)
 {
-    int t0, t1 = 0;
+    int t1 = 0;
     char *s;
     Histent he;
 
-    for (t0 = curhist - 1; (he = quietgethist(t0)); t0--)
+    for (he = up_histent(hist_ring); he; he = up_histent(he)) {
+	if (he->flags & HIST_FOREIGN)
+	    continue;
 	if ((s = strstr(he->text, str))) {
 	    int pos = s - he->text;
 	    while (t1 < he->nwords && he->words[2*t1] <= pos)
 		t1++;
 	    *marg = t1 - 1;
-	    return t0;
+	    return he->histnum;
 	}
+    }
     return -1;
 }
 
@@ -1047,12 +1195,15 @@ hconsearch(char *str, int *marg)
 int
 hcomsearch(char *str)
 {
-    int t0;
-    char *hs;
+    Histent he;
+    int len = strlen(str);
 
-    for (t0 = curhist - 1; (hs = quietgetevent(t0)); t0--)
-	if (!strncmp(hs, str, strlen(str)))
-	    return t0;
+    for (he = up_histent(hist_ring); he; he = up_histent(he)) {
+	if (he->flags & HIST_FOREIGN)
+	    continue;
+	if (strncmp(he->text, str, len) == 0)
+	    return he->histnum;
+    }
     return -1;
 }
 
@@ -1206,33 +1357,15 @@ convamps(char *out, char *in, int inlen)
 }
 
 /**/
-struct histent *
+Histent
 quietgethist(int ev)
 {
-    static struct histent storehist;
-
-    if (ev < firsthist() || ev > curhist)
-	return NULL;
     if (ev == curhist && (histactive & HA_ACTIVE)) {
-	/* The current history line has not been stored.  Build it up
-	 * from other variables.
-	 */
-	storehist.text = chline;
-	storehist.nwords = chwordpos/2;
-	storehist.words = chwords;
-
-	return &storehist;
-    } else
-	return gethistent(ev);
-}
-
-/**/
-char *
-quietgetevent(int ev)
-{
-    Histent ent = quietgethist(ev);
-
-    return ent ? ent->text : NULL;
+	curline.text = chline;
+	curline.nwords = chwordpos/2;
+	curline.words = chwords;
+    }
+    return gethistent(ev, GETHIST_EXACT);
 }
 
 /**/
@@ -1362,10 +1495,9 @@ quotebreak(char **tr)
     return 0;
 }
 
-#if 0
 /* read an arbitrary amount of data into a buffer until stop is found */
 
-/**/
+#if 0 /**/
 char *
 hdynread(int stop)
 {
@@ -1424,61 +1556,80 @@ hdynread2(int stop)
 void
 inithist(void)
 {
-    histentct = histsiz;
-    histentarr = (Histent) zcalloc(histentct * sizeof *histentarr);
+    createhisttable();
 }
 
 /**/
 void
 resizehistents(void)
 {
-    int newentct, t0, t1, firstlex;
-    Histent newarr;
-
-    newentct = histsiz;
-    newarr = (Histent) zcalloc(newentct * sizeof *newarr);
-    firstlex = curhist - histsiz + 1;
-    t0 = firsthist();
-    if (t0 < curhist - newentct)
-	t0 = curhist - newentct;
-    t1 = t0 % newentct;
-    for (; t0 <= curhist; t0++) {
-	newarr[t1] = *gethistent(t0);
-	if (t0 < firstlex) {
-	    zsfree(newarr[t1].text);
-	    newarr[t1].text = NULL;
-	}
-	t1++;
-	if (t1 == newentct)
-	    t1 = 0;
-    }
-    free(histentarr);
-    histentarr = newarr;
-    histentct = newentct;
+    while (histlinect > histsiz)
+	freehistnode((HashNode)hist_ring->down);
 }
 
+/* Remember the last line in the history file so we can find it again. */
+static struct {
+    char *text;
+    time_t stim, mtim;
+    off_t fpos, fsiz;
+    int next_write_ev;
+} lasthist;
+
+static int histfile_linect;
+
 /**/
 void
-readhistfile(char *s, int err)
+readhistfile(char *fn, int err, int readflags)
 {
-    char *buf;
+    char *buf, *start = NULL;
     FILE *in;
-    Histent ent;
-    time_t tim = time(NULL);
+    Histent he;
+    time_t stim, ftim, tim = time(NULL);
+    off_t fpos;
     short *wordlist;
+    struct stat sb;
     int nwordpos, nwordlist, bufsiz;
+    int searching, newflags;
 
-    if (!s)
+    if (!fn && !(fn = getsparam("HISTFILE")))
+	return;
+    if (readflags & HFILE_FAST) {
+	if (stat(fn, &sb) < 0
+	 || (lasthist.fsiz == sb.st_size && lasthist.mtim == sb.st_mtime)
+	 || !lockhistfile(fn, 0))
+	    return;
+	lasthist.fsiz = sb.st_size;
+	lasthist.mtim = sb.st_mtime;
+    }
+    else if (!lockhistfile(fn, 1))
 	return;
-    if ((in = fopen(unmeta(s), "r"))) {
+    if ((in = fopen(unmeta(fn), "r"))) {
 	nwordlist = 16;
 	wordlist = (short *)zalloc(nwordlist*sizeof(short));
 	bufsiz = 1024;
 	buf = zalloc(bufsiz);
 
-	while (fgets(buf, bufsiz, in)) {
+	if (readflags & HFILE_FAST && lasthist.text) {
+	    if (lasthist.fpos < lasthist.fsiz) {
+		fseek(in, lasthist.fpos, 0);
+		searching = 1;
+	    }
+	    else {
+		histfile_linect = 0;
+		searching = -1;
+	    }
+	} else
+	    searching = 0;
+
+	newflags = HIST_OLD | HIST_READ;
+	if (readflags & HFILE_FAST)
+	    newflags |= HIST_FOREIGN;
+	if (readflags & HFILE_SKIPOLD
+	 || (hist_ignore_all_dups && newflags & hist_skip_flags))
+	    newflags |= HIST_MAKEUNIQUE;
+	while (fpos = ftell(in), fgets(buf, bufsiz, in)) {
 	    int l = strlen(buf);
-	    char *pt, *start;
+	    char *pt;
 
 	    while (l) {
 		while (buf[l - 1] != '\n') {
@@ -1488,114 +1639,184 @@ readhistfile(char *s, int err)
 			l++;
 			break;
 		    }
-		    l = strlen(buf);
+		    l += strlen(buf+l);
 		}
 		buf[l - 1] = '\0';
 		if (l > 1 && buf[l - 2] == '\\') {
-		    buf[l - 2] = '\n';
-		    fgets(buf + l - 1, bufsiz - (l - 1), in);
-		    l = strlen(buf);
+		    buf[--l - 1] = '\n';
+		    fgets(buf + l, bufsiz - l, in);
+		    l += strlen(buf+l);
 		} else
 		    break;
 	    }
 
-	    ent = gethistent(++curhist);
 	    pt = buf;
 	    if (*pt == ':') {
 		pt++;
-		ent->stim = zstrtol(pt, NULL, 0);
+		stim = zstrtol(pt, NULL, 0);
 		for (; *pt != ':' && *pt; pt++);
 		if (*pt) {
 		    pt++;
-		    ent->ftim = zstrtol(pt, NULL, 0);
+		    ftim = zstrtol(pt, NULL, 0);
 		    for (; *pt != ';' && *pt; pt++);
 		    if (*pt)
 			pt++;
-		} else {
-		    ent->ftim = tim;
-		}
-		if (ent->stim == 0)
-		    ent->stim = tim;
-		if (ent->ftim == 0)
-		    ent->ftim = tim;
+		} else
+		    ftim = stim;
 	    } else {
-		ent->ftim = ent->stim = tim;
+		if (*pt == '\\' && pt[1] == ':')
+		    pt++;
+		stim = ftim = 0;
+	    }
+
+	    if (searching) {
+		if (searching > 0) {
+		    if (stim == lasthist.stim
+		     && histstrcmp(pt, lasthist.text) == 0)
+			searching = 0;
+		    else {
+			fseek(in, 0, 0);
+			histfile_linect = 0;
+			searching = -1;
+		    }
+		    continue;
+		}
+		else if (stim < lasthist.stim) {
+		    histfile_linect++;
+		    continue;
+		}
+		searching = 0;
 	    }
 
-	    zsfree(ent->text);
-	    ent->text = ztrdup(pt);
-	    ent->flags = HIST_OLD|HIST_READ;
-	    if (ent->nwords)
-		zfree(ent->words, ent->nwords*2*sizeof(short));
+	    if (readflags & HFILE_USE_OPTIONS) {
+		histfile_linect++;
+		lasthist.fpos = fpos;
+		lasthist.stim = stim;
+	    }
+
+	    he = prepnexthistent(++curhist);
+	    he->text = ztrdup(pt);
+	    he->flags = newflags;
+	    if ((he->stim = stim) == 0)
+		he->stim = he->ftim = tim;
+	    else if (ftim < stim)
+		he->ftim = stim + ftim;
+	    else
+		he->ftim = ftim;
 
 	    /* Divide up the words.  We don't know how it lexes,
-	       so just look for spaces.
+	       so just look for white-space.
 	       */
 	    nwordpos = 0;
 	    start = pt;
 	    do {
-		while (*pt == ' ')
+		while (inblank(*pt))
 		    pt++;
 		if (*pt) {
 		    if (nwordpos >= nwordlist)
 			wordlist = (short *) realloc(wordlist,
 					(nwordlist += 16)*sizeof(short));
 		    wordlist[nwordpos++] = pt - start;
-		    while (*pt && *pt != ' ')
+		    while (*pt && !inblank(*pt))
 			pt++;
 		    wordlist[nwordpos++] = pt - start;
 		}
 	    } while (*pt);
 
-	    ent->nwords = nwordpos/2;
-	    if (ent->nwords) {
-		ent->words = (short *)zalloc(nwordpos*sizeof(short));
-		memcpy(ent->words, wordlist, nwordpos*sizeof(short));
+	    he->nwords = nwordpos/2;
+	    if (he->nwords) {
+		he->words = (short *)zalloc(nwordpos*sizeof(short));
+		memcpy(he->words, wordlist, nwordpos*sizeof(short));
 	    } else
-		ent->words = (short *)NULL;
+		he->words = (short *)NULL;
+	    addhistnode(histtab, he->text, he);
+	    if (hist_ring != he)
+		curhist--; /* We discarded a foreign duplicate */
+	}
+	if (start && readflags & HFILE_USE_OPTIONS) {
+	    zsfree(lasthist.text);
+	    lasthist.text = ztrdup(start);
 	}
-	fclose(in);
-
 	zfree(wordlist, nwordlist*sizeof(short));
 	zfree(buf, bufsiz);
+
+	fclose(in);
     } else if (err)
-	zerr("can't read history file", s, 0);
+	zerr("can't read history file", fn, 0);
+
+    unlockhistfile(fn);
 }
 
 /**/
 void
-savehistfile(char *s, int err, int app)
+savehistfile(char *fn, int err, int writeflags)
 {
-    char *t;
+    char *t, *start = NULL;
     FILE *out;
-    int ev;
-    Histent ent;
+    Histent he;
+    int xcurhist = curhist - !!(histactive & HA_ACTIVE);
     int savehist = getiparam("SAVEHIST");
+    int extended_history = isset(EXTENDEDHISTORY);
 
-    if (!s || !interact || savehist <= 0)
+    if (!interact || savehist <= 0 || !hist_ring
+     || (!fn && !(fn = getsparam("HISTFILE"))))
 	return;
-    ev = curhist - savehist + 1;
-    if (ev < firsthist())
-	ev = firsthist();
-    if (app & 1)
-	out = fdopen(open(unmeta(s),
-		     O_CREAT | O_WRONLY | O_APPEND | O_NOCTTY, 0600), "a");
-    else
-	out = fdopen(open(unmeta(s),
-		     O_CREAT | O_WRONLY | O_TRUNC | O_NOCTTY, 0600), "w");
+    if (writeflags & HFILE_FAST) {
+	he = gethistent(lasthist.next_write_ev, GETHIST_DOWNWARD);
+	while (he && he->flags & HIST_OLD) {
+	    lasthist.next_write_ev = he->histnum + 1;
+	    he = down_histent(he);
+	}
+	if (!he || !lockhistfile(fn, 0))
+	    return;
+	if (histfile_linect > savehist + savehist / 5)
+	    writeflags &= ~HFILE_FAST;
+    }
+    else {
+	if (!lockhistfile(fn, 1))
+	    return;
+	he = hist_ring->down;
+    }
+    if (writeflags & HFILE_USE_OPTIONS) {
+	if (isset(APPENDHISTORY) || isset(INCREMENTALAPPENDHISTORY)
+	 || isset(SHAREHISTORY))
+	    writeflags |= HFILE_APPEND | HFILE_SKIPOLD;
+	else
+	    histfile_linect = 0;
+	if (isset(HISTSAVENODUPS))
+	    writeflags |= HFILE_SKIPDUPS;
+	if (isset(SHAREHISTORY))
+	    extended_history = 1;
+    }
+    if (writeflags & HFILE_APPEND) {
+	out = fdopen(open(unmeta(fn),
+			O_CREAT | O_WRONLY | O_APPEND | O_NOCTTY, 0600), "a");
+    }
+    else {
+	out = fdopen(open(unmeta(fn),
+			 O_CREAT | O_WRONLY | O_TRUNC | O_NOCTTY, 0600), "w");
+    }
     if (out) {
-	for (; ev <= curhist - !!(histactive & HA_ACTIVE); ev++) {
-	    ent = gethistent(ev);
-	    if (app & 2) {
-		if (ent->flags & HIST_OLD)
+	for (; he && he->histnum <= xcurhist; he = down_histent(he)) {
+	    if ((writeflags & HFILE_SKIPDUPS && he->flags & HIST_DUP)
+	     || (writeflags & HFILE_SKIPFOREIGN && he->flags & HIST_FOREIGN))
+		continue;
+	    if (writeflags & HFILE_SKIPOLD) {
+		if (he->flags & HIST_OLD)
 		    continue;
-		ent->flags |= HIST_OLD;
+		he->flags |= HIST_OLD;
+		if (writeflags & HFILE_USE_OPTIONS)
+		    lasthist.next_write_ev = he->histnum + 1;
 	    }
-	    t = ent->text;
-	    if (isset(EXTENDEDHISTORY)) {
-		fprintf(out, ": %ld:%ld;",
-			(long)ent->stim,
-			(long)ent->ftim);
+	    if (writeflags & HFILE_USE_OPTIONS) {
+		lasthist.fpos = ftell(out);
+		lasthist.stim = he->stim;
+		histfile_linect++;
+	    }
+	    t = start = he->text;
+	    if (extended_history) {
+		fprintf(out, ": %ld:%ld;", (long)he->stim,
+			he->ftim? (long)(he->ftim - he->stim) : 0L);
 	    } else if (*t == ':')
 		fputc('\\', out);
 
@@ -1606,69 +1827,109 @@ savehistfile(char *s, int err, int app)
 	    }
 	    fputc('\n', out);
 	}
+	if (start && writeflags & HFILE_USE_OPTIONS) {
+	    struct stat sb;
+	    fflush(out);
+	    if (fstat(fileno(out), &sb) == 0) {
+		lasthist.fsiz = sb.st_size;
+		lasthist.mtim = sb.st_mtime;
+	    }
+	    zsfree(lasthist.text);
+	    lasthist.text = ztrdup(start);
+	}
 	fclose(out);
 
-	if (app & 2 && (out = fopen(unmeta(s), "r"))) {
-	    char **store, buf[1024], **ptr;
-	    int i, l, histnum = 0;
-
-	    store = (char **)zcalloc((savehist + 1) * sizeof *store);
-	    while (fgets(buf, sizeof(buf), out)) {
-		char *t;
-
-		if (store[i = histnum % savehist])
-		    free(store[i]);
-		store[i] = ztrdup(buf);
-		l = strlen(buf);
-		if (l > 1) {
-		    t = store[i] + l;
-		    while ((t[-1] != '\n' ||
-			    (t[-1] == '\n' && t[-2] == '\\')) &&
-			   fgets(buf, sizeof(buf), out)) {
-			l += strlen(buf);
-			store[i] = zrealloc(store[i], l + 1);
-			t = store[i] + l;
-			strcat(store[i], buf);
-		    }
-		}
-		histnum++;
-	    }
-	    fclose(out);
-	    if ((out = fdopen(open(unmeta(s),
-			    O_WRONLY | O_TRUNC | O_NOCTTY, 0600), "w"))) {
-		if (histnum < savehist)
-		    for (i = 0; i < histnum; i++)
-			fprintf(out, "%s", store[i]);
-		else
-		    for (i = histnum; i < histnum + savehist; i++)
-			fprintf(out, "%s", store[i % savehist]);
-		fclose(out);
-	    }
-	    for (ptr = store; *ptr; ptr++)
-		zsfree(*ptr);
-	    free(store);
+	if ((writeflags & (HFILE_SKIPOLD | HFILE_FAST)) == HFILE_SKIPOLD) {
+	    HashTable remember_histtab = histtab;
+	    Histent remember_hist_ring = hist_ring;
+	    int remember_histlinect = histlinect;
+	    int remember_curhist = curhist;
+
+	    hist_ring = NULL;
+	    curhist = histlinect = 0;
+	    histsiz = savehist;
+	    createhisttable(); /* sets histtab */
+
+	    hist_ignore_all_dups |= isset(HISTSAVENODUPS);
+	    readhistfile(fn, err, 0);
+	    hist_ignore_all_dups = isset(HISTIGNOREALLDUPS);
+	    savehistfile(fn, err, 0);
+	    deletehashtable(histtab);
+
+	    curhist = remember_curhist;
+	    histlinect = remember_histlinect;
+	    hist_ring = remember_hist_ring;
+	    histtab = remember_histtab;
 	}
     } else if (err)
-	zerr("can't write history file %s", s, 0);
+	zerr("can't write history file %s", fn, 0);
+
+    unlockhistfile(fn);
 }
 
+static int lockhistct;
+
 /**/
 int
-firsthist(void)
+lockhistfile(char *fn, int keep_trying)
 {
-    int ev;
-    Histent ent;
+    int ct = lockhistct;
 
-    ev = curhist - histentct + 1;
-    if (ev < 1)
-	ev = 1;
-    do {
-	ent = gethistent(ev);
-	if (ent->text)
-	    break;
-	ev++;
+    if (!fn && !(fn = getsparam("HISTFILE")))
+	return 0;
+    if (!lockhistct++) {
+	struct stat sb;
+	int fd, len = strlen(fn);
+	char *tmpfile, *lockfile;
+
+	tmpfile = zalloc(len + 10 + 1);
+	sprintf(tmpfile, "%s.%ld", fn, mypid);
+	if ((fd = open(tmpfile, O_RDWR|O_CREAT|O_EXCL, 0644)) >= 0) {
+	    write(fd, "0\n", 2);
+	    close(fd);
+	    lockfile = zalloc(len + 5 + 1);
+	    sprintf(lockfile, "%s.LOCK", fn);
+	    while (link(tmpfile, lockfile) < 0) {
+		if (stat(lockfile, &sb) < 0) {
+		    if (errno == ENOENT)
+			continue;
+		}
+		else if (keep_trying) {
+		    if (time(NULL) - sb.st_mtime < 10)
+			sleep(1);
+		    else
+			unlink(lockfile);
+		    continue;
+		}
+		lockhistct--;
+		break;
+	    }
+	    free(lockfile);
+	}
+	unlink(tmpfile);
+	free(tmpfile);
     }
-    while (ev < curhist);
-    return ev;
+    return ct != lockhistct;
 }
 
+/* Unlock the history file if this corresponds to the last nested lock
+ * request.  If we don't have the file locked, just return.
+ */
+
+/**/
+void
+unlockhistfile(char *fn)
+{
+    if (!fn && !(fn = getsparam("HISTFILE")))
+	return;
+    if (--lockhistct) {
+	if (lockhistct < 0)
+	    lockhistct = 0;
+    }
+    else {
+	char *lockfile = zalloc(strlen(fn) + 5 + 1);
+	sprintf(lockfile, "%s.LOCK", fn);
+	unlink(lockfile);
+	free(lockfile);
+    }
+}
diff --git a/Src/init.c b/Src/init.c
index 6ad1e5100..e92a5000f 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -110,15 +110,14 @@ loop(int toplevel, int justonce)
 	    List prelist;
 
 	    if (toplevel && (prelist = getshfunc("preexec")) != &dummy_list) {
-		Histent he = gethistent(curhist);
 		LinkList args;
 		int osc = sfcontext;
 
 		PERMALLOC {
 		    args = newlinklist();
 		    addlinknode(args, "preexec");
-		    if (he && he->text)
-			addlinknode(args, he->text);
+		    if (hist_ring)
+			addlinknode(args, hist_ring->text);
 		} LASTALLOC;
 		sfcontext = SFC_HOOK;
 		doshfunc("preexec", prelist, args, 0, 1);
@@ -642,11 +641,13 @@ setupvals(void)
     wrappers = NULL;
 
 #ifdef TIOCGWINSZ
-    adjustwinsize();
+    adjustwinsize(0);
 #else
-    /* Using zero below sets the defaults from termcap */
-    setiparam("COLUMNS", 0);
-    setiparam("LINES", 0);
+    /* columns and lines are normally zero, unless something different *
+     * was inhereted from the environment.  If either of them are zero *
+     * the setiparam calls below set them to the defaults from termcap */
+    setiparam("COLUMNS", columns);
+    setiparam("LINES", lines);
 #endif
 
 #ifdef HAVE_GETRLIMIT
@@ -828,7 +829,7 @@ init_misc(void)
     }
 
     if (interact && isset(RCS))
-	readhistfile(getsparam("HISTFILE"), 0);
+	readhistfile(NULL, 0, HFILE_USE_OPTIONS);
 }
 
 /* source a file */
diff --git a/Src/jobs.c b/Src/jobs.c
index e94dd77e2..6ddef0f70 100644
--- a/Src/jobs.c
+++ b/Src/jobs.c
@@ -180,11 +180,14 @@ update_job(Job jn)
     } else {                   /* job is done, so remember return value */
 	lastval2 = val;
 	/* If last process was run in the current shell, keep old status
-	 * and let it handle its own traps
+	 * and let it handle its own traps, but always allow the test
+	 * for the pgrp.
 	 */
-	if (job == thisjob && !(jn->stat & STAT_CURSH)) {
-	  lastval = val;
-	  inforeground = 1;
+	if (jn->stat & STAT_CURSH)
+	    inforeground = 1;
+	else if (job == thisjob) {
+	    lastval = val;
+	    inforeground = 2;
 	}
     }
 
@@ -198,8 +201,20 @@ update_job(Job jn)
 	/* is this job in the foreground of an interactive shell? */
 	if (mypgrp != pgrp && inforeground &&
 	    (jn->gleader == pgrp || (pgrp > 1 && kill(-pgrp, 0) == -1))) {
-	    attachtty(mypgrp);
-	    adjustwinsize();   /* check window size and adjust if necessary */
+	    if (list_pipe) {
+		/*
+		 * Oh, dear, we're right in the middle of some confusion
+		 * of shell jobs on the righthand side of a pipeline, so
+		 * it's death to call attachtty() just yet.  Mark the
+		 * fact in the job, so that the attachtty() will be called
+		 * when the job is finally deleted.
+		 */
+		jn->stat |= STAT_ATTACH;
+	    } else {
+		attachtty(mypgrp);
+		/* check window size and adjust if necessary */
+		adjustwinsize(0);
+	    }
 	}
     }
 
@@ -223,7 +238,7 @@ update_job(Job jn)
      * process group from the shell, so the shell will not receive     *
      * terminal signals, therefore we we pretend that the shell got    *
      * the signal too.                                                 */
-    if (inforeground && isset(MONITOR) && WIFSIGNALED(status)) {
+    if (inforeground == 2 && isset(MONITOR) && WIFSIGNALED(status)) {
 	int sig = WTERMSIG(status);
 
 	if (sig == SIGINT || sig == SIGQUIT) {
@@ -609,6 +624,11 @@ deletejob(Job jn)
 {
     struct process *pn, *nx;
 
+    if (jn->stat & STAT_ATTACH) {
+	attachtty(mypgrp);
+	adjustwinsize(0);
+    }
+
     pn = jn->procs;
     jn->procs = NULL;
     for (; pn; pn = nx) {
diff --git a/Src/module.c b/Src/module.c
index 09f1fd5db..c966d4497 100644
--- a/Src/module.c
+++ b/Src/module.c
@@ -142,6 +142,7 @@ addwrapper(Module m, FuncWrap w)
     return 0;
 }
 
+/**/
 #ifdef DYNAMIC
 
 /* $module_path ($MODULE_PATH) */
@@ -238,6 +239,7 @@ deletewrapper(Module m, FuncWrap w)
     return 1;
 }
 
+/**/
 #ifdef AIXDYNAMIC
 
 #include <sys/ldr.h>
@@ -273,6 +275,7 @@ load_and_bind(const char *fn)
 #define dlclose(X)  unload(X)
 #define dlerror()   (dlerrstr[0])
 
+/**/
 #else
 
 #ifdef HAVE_DLFCN_H
@@ -289,6 +292,7 @@ load_and_bind(const char *fn)
 # endif
 #endif
 
+/**/
 #ifdef HPUXDYNAMIC
 # define dlopen(file,mode) (void *)shl_load((file), (mode), (long) 0)
 # define dlclose(handle) shl_unload((shl_t)(handle))
@@ -309,6 +313,7 @@ hpux_dlsym(void *handle, char *name)
 # ifndef HAVE_DLCLOSE
 #  define dlclose(X) ((X), 0)
 # endif
+/**/
 #endif
 
 #ifdef DLSYM_NEEDS_UNDERSCORE
@@ -331,6 +336,7 @@ hpux_dlsym(void *handle, char *name)
 # define STR_FINISH_S  "finish_%s"
 #endif /* !DLSYM_NEEDS_UNDERSCORE */
 
+/**/
 #endif /* !AIXDYNAMIC */
 
 #ifndef RTLD_LAZY
@@ -408,6 +414,7 @@ find_module(const char *name)
     return NULL;
 }
 
+/**/
 #ifdef AIXDYNAMIC
 
 /**/
@@ -438,6 +445,7 @@ finish_module(Module m)
     return ((int (*)_((int,Module))) m->handle)(3, m);
 }
 
+/**/
 #else
 
 static Module_func
@@ -523,6 +531,7 @@ finish_module(Module m)
     return r;
 }
 
+/**/
 #endif /* !AIXDYNAMIC */
 
 /**/
@@ -664,7 +673,7 @@ autoloadscan(HashNode hn, int printflags)
     if(bn->flags & BINF_ADDED)
 	return;
     if(printflags & PRINT_LIST) {
-	fputs("zmodload -a ", stdout);
+	fputs("zmodload -ab ", stdout);
 	if(bn->optstr[0] == '-')
 	    fputs("-- ", stdout);
 	quotedzputs(bn->optstr, stdout);
@@ -687,7 +696,12 @@ autoloadscan(HashNode hn, int printflags)
 int
 bin_zmodload(char *nam, char **args, char *ops, int func)
 {
-    if(ops['d'] && ops['a']) {
+    if ((ops['b'] || ops['c'] || ops['p']) && !(ops['a'] || ops['u'])) {
+	zwarnnam(nam, "-b, -c, and -p must be combined with -a or -u",
+		 NULL, 0);
+	return 1;
+    }
+    if (ops['d'] && ops['a']) {
 	zwarnnam(nam, "-d cannot be combined with -a", NULL, 0);
 	return 1;
     }
@@ -695,16 +709,20 @@ 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['d'])
 	return bin_zmodload_dep(nam, args, ops);
-    else if(ops['a'])
+    else if ((ops['a'] || ops['b']) && !(ops['c'] || ops['p']))
 	return bin_zmodload_auto(nam, args, ops);
-    else if (ops['c'] || ops['C'])
+    else if (ops['c'] && !(ops['b'] || ops['p']))
 	return bin_zmodload_cond(nam, args, ops);
-    else if (ops['p'])
+    else if (ops['p'] && !(ops['b'] || ops['c']))
 	return bin_zmodload_param(nam, args, ops);
-    else
+    else if (!(ops['a'] || ops['b'] || ops['c'] || ops['p']))
 	return bin_zmodload_load(nam, args, ops);
+    else
+	zwarnnam(nam, "use only one of -b, -c, or -p", NULL, 0);
+
+    return 1;
 }
 
 /**/
@@ -865,7 +883,7 @@ bin_zmodload_cond(char *nam, char **args, char *ops)
 	for (p = condtab; p; p = p->next) {
 	    if (p->module) {
 		if (ops['L']) {
-		    fputs("zmodload -c", stdout);
+		    fputs("zmodload -ac", stdout);
 		    if (p->flags & CONDF_INFIX)
 			putchar('I');
 		    printf(" %s %s\n", p->module, p->name);
@@ -908,7 +926,7 @@ printautoparams(HashNode hn, int lon)
 
     if (pm->flags & PM_AUTOLOAD) {
 	if (lon)
-	    printf("zmodload -p %s %s\n", pm->u.str, pm->nam);
+	    printf("zmodload -ap %s %s\n", pm->u.str, pm->nam);
 	else
 	    printf("%s (%s)\n", pm->nam, pm->u.str);
     }
@@ -1097,6 +1115,7 @@ bin_zmodload_load(char *nam, char **args, char *ops)
     }
 }
 
+/**/
 #endif /* DYNAMIC */
 
 /* The list of module-defined conditions. */
@@ -1252,6 +1271,7 @@ deleteparamdefs(char const *nam, Paramdef d, int size)
     return 1;
 }
 
+/**/
 #ifdef DYNAMIC
 
 /* This adds a definition for autoloading a module for a condition. */
@@ -1342,4 +1362,5 @@ add_autoparam(char *nam, char *module)
     pm->flags |= PM_AUTOLOAD;
 }
 
+/**/
 #endif
diff --git a/Src/options.c b/Src/options.c
index 9010831c0..c0042a662 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -124,15 +124,20 @@ static struct optname optns[] = {
 {NULL, "hashlistall",	      OPT_ALL,			 HASHLISTALL},
 {NULL, "histallowclobber",    0,			 HISTALLOWCLOBBER},
 {NULL, "histbeep",	      OPT_ALL,			 HISTBEEP},
+{NULL, "histexpiredupsfirst", 0,			 HISTEXPIREDUPSFIRST},
+{NULL, "histfindnodups",      0,			 HISTFINDNODUPS},
+{NULL, "histignorealldups",   0,			 HISTIGNOREALLDUPS},
 {NULL, "histignoredups",      0,			 HISTIGNOREDUPS},
 {NULL, "histignorespace",     0,			 HISTIGNORESPACE},
 {NULL, "histnofunctions",     0,			 HISTNOFUNCTIONS},
 {NULL, "histnostore",	      0,			 HISTNOSTORE},
 {NULL, "histreduceblanks",    0,			 HISTREDUCEBLANKS},
+{NULL, "histsavenodups",      0,			 HISTSAVENODUPS},
 {NULL, "histverify",	      0,			 HISTVERIFY},
 {NULL, "hup",		      OPT_EMULATE|OPT_ZSH,	 HUP},
 {NULL, "ignorebraces",	      OPT_EMULATE|OPT_SH,	 IGNOREBRACES},
 {NULL, "ignoreeof",	      0,			 IGNOREEOF},
+{NULL, "incrementalappendhistory",0,			 INCREMENTALAPPENDHISTORY},
 {NULL, "interactive",	      OPT_SPECIAL,		 INTERACTIVE},
 {NULL, "interactivecomments", OPT_EMULATE|OPT_BOURNE,	 INTERACTIVECOMMENTS},
 {NULL, "ksharrays",	      OPT_EMULATE|OPT_BOURNE,	 KSHARRAYS},
@@ -176,6 +181,7 @@ static struct optname optns[] = {
 {NULL, "restricted",	      OPT_SPECIAL,		 RESTRICTED},
 {NULL, "rmstarsilent",	      OPT_BOURNE,		 RMSTARSILENT},
 {NULL, "rmstarwait",	      0,			 RMSTARWAIT},
+{NULL, "sharehistory",	      OPT_KSH,			 SHAREHISTORY},
 {NULL, "shfileexpansion",     OPT_EMULATE|OPT_BOURNE,	 SHFILEEXPANSION},
 {NULL, "shglob",	      OPT_EMULATE|OPT_BOURNE,	 SHGLOB},
 {NULL, "shinstdin",	      OPT_SPECIAL,		 SHINSTDIN},
@@ -378,9 +384,9 @@ printoptionnode(HashNode hn, int set)
 	optno = -optno;
     if (isset(KSHOPTIONPRINT)) {
 	if (defset(on))
-	    printf("no%-20s%s\n", on->nam, isset(optno) ? "off" : "on");
+	    printf("no%-19s %s\n", on->nam, isset(optno) ? "off" : "on");
 	else
-	    printf("%-22s%s\n", on->nam, isset(optno) ? "on" : "off");
+	    printf("%-21s %s\n", on->nam, isset(optno) ? "on" : "off");
     } else if (set == (isset(optno) ^ defset(on))) {
 	if (set ^ isset(optno))
 	    fputs("no", stdout);
@@ -399,6 +405,7 @@ createoptiontable(void)
     optiontab->hash        = hasher;
     optiontab->emptytable  = NULL;
     optiontab->filltable   = NULL;
+    optiontab->cmpnodes    = strcmp;
     optiontab->addnode     = addhashnode;
     optiontab->getnode     = gethashnode;
     optiontab->getnode2    = gethashnode2;
@@ -635,6 +642,8 @@ dosetopt(int optno, int value, int force)
 	}
     } else if(!force && (optno == INTERACTIVE || optno == SHINSTDIN ||
 	    optno == SINGLECOMMAND)) {
+	if (opts[optno] == value)
+	    return 0;
 	/* it is not permitted to change the value of these options */
 	return -1;
     } else if(!force && optno == USEZLE && value) {
diff --git a/Src/params.c b/Src/params.c
index d60f91990..a8b027988 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -270,6 +270,7 @@ newparamtable(int size, char const *name)
     ht->hash        = hasher;
     ht->emptytable  = emptyhashtable;
     ht->filltable   = NULL;
+    ht->cmpnodes    = strcmp;
     ht->addnode     = addhashnode;
     ht->getnode     = getparamnode;
     ht->getnode2    = getparamnode;
@@ -282,6 +283,7 @@ newparamtable(int size, char const *name)
     return ht;
 }
 
+/**/
 #ifdef DYNAMIC
 /**/
 static HashNode
@@ -299,6 +301,7 @@ getparamnode(HashTable ht, char *nam)
     }
     return hn;
 }
+/**/
 #endif /* DYNAMIC */
 
 /* Copy a parameter hash table */
@@ -1753,7 +1756,7 @@ unsetparam_pm(Param pm, int altflag, int exp)
      * Some specials, such as those used in zle, still need removing
      * from the parameter table; they have the PM_REMOVABLE flag.
      */
-    if ((locallevel && locallevel >= pm->level) ||
+    if ((pm->level && locallevel >= pm->level) ||
 	(pm->flags & (PM_SPECIAL|PM_REMOVABLE)) == PM_SPECIAL)
 	return;
 
@@ -1963,23 +1966,11 @@ intvarsetfn(Param pm, long x)
 void
 zlevarsetfn(Param pm, long x)
 {
-    if ((long *)pm->u.data == & columns) {
-	if(x <= 0)
-	    x = tccolumns > 0 ? tccolumns : 80;
-	if (x > 2)
-	    termflags &= ~TERM_NARROW;
-	else
-	    termflags |= TERM_NARROW;
-    } else if ((long *)pm->u.data == & lines) {
-	if(x <= 0)
-	    x = tclines > 0 ? tclines : 24;
-	if (x > 2)
-	    termflags &= ~TERM_SHORT;
-	else
-	    termflags |= TERM_SHORT;
-    }
+    long *p = (long *)pm->u.data;
 
-    *((long *)pm->u.data) = x;
+    *p = x;
+    if (p == &lines || p == &columns)
+	adjustwinsize(2 + (p == &columns));
 }
 
 /* Function to set value of generic special scalar    *
diff --git a/Src/signals.c b/Src/signals.c
index 65bac0f52..3e655cbfd 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -129,10 +129,9 @@ intr(void)
         install_handler(SIGINT);
 }
 
-#if 0
 /* disable ^C interrupts */
  
-/**/
+#if 0 /**/
 void
 nointr(void)
 {
@@ -505,7 +504,7 @@ handler(int sig)
 
 #ifdef SIGWINCH
     case SIGWINCH:
-        adjustwinsize();  /* check window size and adjust */
+        adjustwinsize(1);  /* check window size and adjust */
 	if (sigtrapped[SIGWINCH])
 	    dotrap(SIGWINCH);
         break;
diff --git a/Src/subst.c b/Src/subst.c
index e8e22f943..4b60de120 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -245,35 +245,43 @@ singsub(char **s)
  * the result is stored in *a. If `a' is zero a multiple word result is *
  * joined using sep or the IFS parameter if sep is zero and the result  *
  * is returned in *s.  The return value is true iff the expansion       *
- * resulted in an empty list                                            */
+ * resulted in an empty list.                                           *
+ * The mult_isarr variable is used by paramsubst() to tell if it yields *
+ * an array.                                                            */
+
+static int mult_isarr;
 
 /**/
 static int
 multsub(char **s, char ***a, int *isarr, char *sep)
 {
     LinkList foo;
-    int l;
+    int l, omi = mult_isarr;
     char **r, **p;
 
+    mult_isarr = 0;
     foo = newlinklist();
     addlinknode(foo, *s);
     prefork(foo, 0);
     if (errflag) {
 	if (isarr)
 	    *isarr = 0;
+	mult_isarr = omi;
 	return 0;
     }
-    if ((l = countlinknodes(foo)) > 1 || a) {
+    if ((l = countlinknodes(foo))) {
 	p = r = ncalloc((l + 1) * sizeof(char*));
 	while (nonempty(foo))
 	    *p++ = (char *)ugetnode(foo);
 	*p = NULL;
-	if (a) {
+	if (a && mult_isarr) {
 	    *a = r;
 	    *isarr = 1;
+	    mult_isarr = omi;
 	    return 0;
 	}
 	*s = sepjoin(r, NULL);
+	mult_isarr = omi;
 	return 0;
     }
     if (l)
@@ -282,6 +290,7 @@ multsub(char **s, char ***a, int *isarr, char *sep)
 	*s = dupstring("");
     if (isarr)
 	*isarr = 0;
+    mult_isarr = omi;
     return !l;
 }
 
@@ -977,16 +986,12 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	skipparens(*s, *s == Inpar ? Outpar : Outbrace, &s);
 	sav = *s;
 	*s = 0;
-	if (multsub(&val, ((!aspar && (!quoted || nojoin)) ? &aval : NULL),
-		    &isarr, NULL) &&
-	    quoted) {
+	if (multsub(&val, (aspar ? NULL : &aval), &isarr, NULL) && quoted) {
 	    isarr = -1;
 	    aval = alloc(sizeof(char *));
 	    aspar = 0;
 	} else if (aspar)
 	    idbeg = val;
-	if (isarr)
-	    isarr = -1;
 	copied = 1;
 	*s = sav;
 	v = (Value) NULL;
@@ -1465,6 +1470,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	val = dupstring(buf);
 	isarr = 0;
     }
+    mult_isarr = isarr;
     if (isarr > 0 && !plan9 && (!aval || !aval[0])) {
 	val = dupstring("");
 	isarr = 0;
@@ -1485,6 +1491,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		val = aval[0];
 	    else
 		isarr = 2;
+	    mult_isarr = isarr;
 	}
     }
     if (casmod) {
diff --git a/Src/text.c b/Src/text.c
index 88a27495d..d3eafaf9f 100644
--- a/Src/text.c
+++ b/Src/text.c
@@ -72,10 +72,9 @@ taddstr(char *s)
     tptr += sl;
 }
 
-#if 0
 /* add an integer to the text buffer */
 
-/**/
+#if 0 /**/
 void
 taddint(int x)
 {
diff --git a/Src/utils.c b/Src/utils.c
index 86679e90f..faeeb0c58 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -209,10 +209,9 @@ nicechar(int c)
     return buf;
 }
 
-#if 0
 /* Output a string's visible representation. */
 
-/**/
+#if 0 /**/
 void
 nicefputs(char *s, FILE *f)
 {
@@ -853,27 +852,64 @@ int resetneeded;
 /**/
 int winchanged;
 #endif
- 
-/* check the size of the window and adjust if necessary */
+
+/* check the size of the window and adjust if necessary. *
+ * The value of from:					 *
+ *   0: called from update_job or setupvals		 *
+ *   1: called from the SIGWINCH handler		 *
+ *   2: the user have just changed LINES manually	 *
+ *   3: the user have just changed COLUMNS manually      */
 
 /**/
 void
-adjustwinsize(void)
+adjustwinsize(int from)
 {
-#ifdef TIOCGWINSZ
     int oldcols = columns, oldrows = lines;
 
+#ifdef TIOCGWINSZ
+    static int userlines, usercols;
+
     if (SHTTY == -1)
 	return;
 
-    ioctl(SHTTY, TIOCGWINSZ, (char *)&shttyinfo.winsize);
-    setiparam("COLUMNS", shttyinfo.winsize.ws_col);
-    setiparam("LINES", shttyinfo.winsize.ws_row);
-    if (zleactive && (oldcols != columns || oldrows != lines)) {
+    if (from == 2)
+	userlines = lines > 0;
+    if (from == 3)
+	usercols = columns > 0;
+
+    if (!ioctl(SHTTY, TIOCGWINSZ, (char *)&shttyinfo.winsize)) {
+	if (!userlines || from == 1)
+	    lines = shttyinfo.winsize.ws_row;
+	if (!usercols || from == 1)
+	    columns = shttyinfo.winsize.ws_col;
+    }
+#endif   /* TIOCGWINSZ */
+
+    if (lines <= 0)
+	lines = tclines > 0 ? tclines : 24;
+    if (columns <= 0)
+	columns = tccolumns > 0 ? tccolumns : 80;
+    if (lines > 2)
+	termflags &= ~TERM_SHORT;
+    else
+	termflags |= TERM_SHORT;
+    if (columns > 2)
+	termflags &= ~TERM_NARROW;
+    else
+	termflags |= TERM_NARROW;
+
+#ifdef TIOCGWINSZ
+    if (interact && from >= 2) {
+	shttyinfo.winsize.ws_row = lines;
+	shttyinfo.winsize.ws_col = columns;
+	ioctl(SHTTY, TIOCSWINSZ, (char *)&shttyinfo.winsize);
+    }
+#endif
+
+    if (zleactive && (from >= 2 || oldcols != columns || oldrows != lines)) {
 	resetneeded = winchanged = 1;
 	zrefresh();
     }
-#endif   /* TIOCGWINSZ */
 }
 
 /* Move a fd to a place >= 10 and mark the new fd in fdtable.  If the fd *
@@ -3343,10 +3379,9 @@ dquotedztrdup(char const *s)
     return ret;
 }
 
-#if 0
 /* Unmetafy and output a string, double quoting it in its entirety. */
 
-/**/
+#if 0 /**/
 int
 dquotedzputs(char const *s, FILE *stream)
 {
diff --git a/Src/zsh.export b/Src/zsh.export
index 7f994bf29..b62621e0f 100644
--- a/Src/zsh.export
+++ b/Src/zsh.export
@@ -4,6 +4,7 @@ addbuiltins
 addconddefs
 addedx
 addhashnode
+addhistnum
 addparamdefs
 addwrapper
 arrvargetfn
@@ -45,6 +46,7 @@ deleteparamdefs
 deletewrapper
 domatch
 doshfunc
+down_histent
 dputs
 dquotedztrdup
 dummy_list
@@ -97,6 +99,8 @@ hgetc
 hgetline
 histentarr
 histentct
+hist_ring
+hist_skip_flags
 holdintr
 hptr
 hrealloc
@@ -132,6 +136,7 @@ metadiffer
 metafy
 metalen
 mode_to_octal
+movehistent
 mypgrp
 mypid
 nameddirtab
@@ -240,6 +245,7 @@ unmetafy
 unsetparam
 unsetparam_pm
 untokenize
+up_histent
 uremnode
 useheap
 winchanged
diff --git a/Src/zsh.h b/Src/zsh.h
index 1eefc51c1..1d635afdc 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -294,7 +294,7 @@ struct linklist {
 #define peekfirst(X) ((X)->first->dat)
 #define pushnode(X,Y) insertlinknode(X,(LinkNode) X,Y)
 #define incnode(X) (X = nextnode(X))
-#define gethistent(X) (histentarr+((X)%histentct))
+#define firsthist() (hist_ring? hist_ring->down->histnum : curhist)
 
 
 /********************************/
@@ -611,6 +611,7 @@ struct job {
 #define STAT_CURSH	(1<<9)	/* last command is in current shell     */
 #define STAT_NOSTTY	(1<<10)	/* the tty settings are not inherited   */
 				/* from this job when it exits.         */
+#define STAT_ATTACH	(1<<11)	/* delay reattaching shell to tty       */
 
 #define SP_RUNNING -1		/* fake status for jobs currently running */
 
@@ -679,6 +680,7 @@ typedef void     (*AddNodeFunc)    _((HashTable, char *, void *));
 typedef HashNode (*GetNodeFunc)    _((HashTable, char *));
 typedef HashNode (*RemoveNodeFunc) _((HashTable, char *));
 typedef void     (*FreeNodeFunc)   _((HashNode));
+typedef int      (*CompareFunc)    _((const char *, const char *));
 
 /* type of function that is passed to *
  * scanhashtable or scanmatchtable    */
@@ -698,6 +700,7 @@ struct hashtable {
     HashFunc hash;		/* pointer to hash function for this table    */
     TableFunc emptytable;	/* pointer to function to empty table         */
     TableFunc filltable;	/* pointer to function to fill table          */
+    CompareFunc cmpnodes;	/* pointer to function to compare two nodes     */
     AddNodeFunc addnode;	/* pointer to function to add new node        */
     GetNodeFunc getnode;	/* pointer to function to get an enabled node */
     GetNodeFunc getnode2;	/* pointer to function to get node            */
@@ -1015,18 +1018,30 @@ struct nameddir {
 /* history entry */
 
 struct histent {
+    HashNode hash_next;		/* next in hash chain               */
     char *text;			/* the history line itself          */
+    int flags;			/* Misc flags                       */
+
+    Histent up;			/* previous line (moving upward)    */
+    Histent down;		/* next line (moving downward)      */
     char *zle_text;		/* the edited history line          */
     time_t stim;		/* command started time (datestamp) */
     time_t ftim;		/* command finished time            */
     short *words;		/* Position of words in history     */
 				/*   line:  as pairs of start, end  */
     int nwords;			/* Number of words in history line  */
-    int flags;			/* Misc flags                       */
+    int histnum;		/* A sequential history number      */
 };
 
-#define HIST_OLD	0x00000001	/* Command is already written to disk*/
-#define HIST_READ	0x00000002	/* Command was read back from disk*/
+#define HIST_MAKEUNIQUE	0x00000001	/* Kill this new entry if not unique */
+#define HIST_OLD	0x00000002	/* Command is already written to disk*/
+#define HIST_READ	0x00000004	/* Command was read back from disk*/
+#define HIST_DUP	0x00000008	/* Command duplicates a later line */
+#define HIST_FOREIGN	0x00000010	/* Command came from another shell */
+
+#define GETHIST_UPWARD  (-1)
+#define GETHIST_DOWNWARD  1
+#define GETHIST_EXACT     0
 
 /* Parts of the code where history expansion is disabled *
  * should be within a pair of STOPHIST ... ALLOWHIST     */
@@ -1039,6 +1054,13 @@ struct histent {
 #define HISTFLAG_RECALL 4
 #define HISTFLAG_SETTY  8
 
+#define HFILE_APPEND		0x0001
+#define HFILE_SKIPOLD		0x0002
+#define HFILE_SKIPDUPS		0x0004
+#define HFILE_SKIPFOREIGN	0x0008
+#define HFILE_FAST		0x0010
+#define HFILE_USE_OPTIONS	0x8000
+
 /******************************************/
 /* Definitions for programable completion */
 /******************************************/
@@ -1120,15 +1142,20 @@ enum {
     HASHLISTALL,
     HISTALLOWCLOBBER,
     HISTBEEP,
+    HISTEXPIREDUPSFIRST,
+    HISTFINDNODUPS,
+    HISTIGNOREALLDUPS,
     HISTIGNOREDUPS,
     HISTIGNORESPACE,
     HISTNOFUNCTIONS,
     HISTNOSTORE,
     HISTREDUCEBLANKS,
+    HISTSAVENODUPS,
     HISTVERIFY,
     HUP,
     IGNOREBRACES,
     IGNOREEOF,
+    INCREMENTALAPPENDHISTORY,
     INTERACTIVE,
     INTERACTIVECOMMENTS,
     KSHARRAYS,
@@ -1172,6 +1199,7 @@ enum {
     RESTRICTED,
     RMSTARSILENT,
     RMSTARWAIT,
+    SHAREHISTORY,
     SHFILEEXPANSION,
     SHGLOB,
     SHINSTDIN,
diff --git a/Util/reporter b/Util/reporter
index 8f8f530ae..76328820a 100644
--- a/Util/reporter
+++ b/Util/reporter
@@ -302,7 +302,11 @@ then
 		echo
 		zmodload -d -L
 		echo
-		zmodload -a -L
+		zmodload -ab -L
+		echo
+		zmodload -ac -L
+		echo
+		zmodload -ap -L
 		echo
 		zmodload -L
 	else
diff --git a/acconfig.h b/acconfig.h
index 1e0473557..58ad6aa2b 100644
--- a/acconfig.h
+++ b/acconfig.h
@@ -189,9 +189,6 @@
 /* Define to 1 if struct timezone is defined by a system header */
 #undef HAVE_STRUCT_TIMEZONE
 
-/* Define if your system's typeahead disappears from the shell editor. */
-#undef CLOBBERS_TYPEAHEAD
-
 /* Define to 1 if there is a prototype defined for brk() on your system */
 #undef HAVE_BRK_PROTO
 
diff --git a/aczsh.m4 b/aczsh.m4
index 08c167d2f..9eee4b357 100644
--- a/aczsh.m4
+++ b/aczsh.m4
@@ -25,6 +25,47 @@ dnl  support, updates, enhancements, or modifications.
 dnl
 
 dnl
+dnl Code from the configure system for bash 2.03 (not zsh copyright).
+dnl If available, use support for large files unless the user specified
+dnl one of the CPPFLAGS, LDFLAGS, or LIBS variables (<eggert@twinsun.com>
+dnl via GNU patch 2.5)
+dnl
+AC_DEFUN(zsh_LARGE_FILE_SUPPORT,
+[AC_MSG_CHECKING(whether large file support needs explicit enabling)
+ac_getconfs=''
+ac_result=yes
+ac_set=''
+ac_shellvars='CPPFLAGS LDFLAGS LIBS'
+for ac_shellvar in $ac_shellvars; do
+  case $ac_shellvar in
+  CPPFLAGS) ac_lfsvar=LFS_CFLAGS ac_lfs64var=LFS64_CFLAGS ;;
+  *) ac_lfsvar=LFS_$ac_shellvar ac_lfs64var=LFS64_$ac_shellvar ;;
+  esac
+  eval test '"${'$ac_shellvar'+set}"' = set && ac_set=$ac_shellvar
+  (getconf $ac_lfsvar) >/dev/null 2>&1 || { ac_result=no; break; }
+  ac_getconf=`getconf $ac_lfsvar`
+  ac_getconf64=`getconf $ac_lfs64var`
+  ac_getconfs=$ac_getconfs$ac_getconf\ $ac_getconf64
+  eval ac_test_$ac_shellvar="\$ac_getconf\ \$ac_getconf64"
+done
+case "$ac_result$ac_getconfs" in
+yes) ac_result=no ;;
+esac
+case "$ac_result$ac_set" in
+yes?*) test "x$ac_set" != "xLDFLAGS" -o "x$auto_ldflags" = x && {
+  ac_result="yes, but $ac_set is already set, so use its settings"
+}
+esac
+AC_MSG_RESULT($ac_result)
+case $ac_result in
+yes)
+  for ac_shellvar in $ac_shellvars; do
+    eval $ac_shellvar=\$ac_test_$ac_shellvar
+  done ;;
+esac
+])
+
+dnl
 dnl zsh_SYS_DYNAMIC_BROKEN
 dnl   Check whether static/shared library linking is broken.
 dnl
diff --git a/configure.in b/configure.in
index f022744f4..5c459840e 100644
--- a/configure.in
+++ b/configure.in
@@ -204,6 +204,10 @@ test -z "${LDFLAGS+set}" && LDFLAGS= auto_ldflags=1
 
 AC_PROG_CC
 
+dnl Check for large file support (Solaris).
+dnl This needs to be done early to get the stuff into the flags.
+zsh_LARGE_FILE_SUPPORT
+
 dnl if the user hasn't specified CFLAGS, then
 dnl   if compiler is gcc, then use -O2 and some warning flags
 dnl   else use -O
@@ -883,25 +887,6 @@ zsh_PATH_UTMP(wtmp)
 zsh_PATH_UTMP(utmpx)
 zsh_PATH_UTMP(wtmpx)
 
-dnl ----------------
-dnl TYPEAHEAD KLUDGE
-dnl ----------------
-dnl Some systems clobber typeahead when you go from canonical input
-dnl processing to non-canonical, so we need a FIONREAD ioctl.
-dnl I don't know how to check this with configure, so I am using the
-dnl system names directly.
-dnl The doubled square brackets are necessary because autoconf uses m4.
-AC_CACHE_CHECK(if typeahead needs FIONREAD, zsh_cv_sys_clobbers_typeahead,
-[case x-$host_vendor-$host_os in
-    x-*-ultrix* | x-*-dgux* | x-sni-sysv4* | x-*-irix*)
-	zsh_cv_sys_clobbers_typeahead=yes;;
-    *)
-	zsh_cv_sys_clobbers_typeahead=no;;
-esac])
-if test $zsh_cv_sys_clobbers_typeahead = yes; then
-  AC_DEFINE(CLOBBERS_TYPEAHEAD)
-fi
-
 dnl -------------------
 dnl brk/sbrk PROTOTYPES
 dnl -------------------
diff --git a/patchlist.txt b/patchlist.txt
index faeade93b..3c88576c2 100644
--- a/patchlist.txt
+++ b/patchlist.txt
@@ -1012,7 +1012,71 @@ pws: 6165: globsubst'd foo='~/bin' depended on extendedglob being set
 
 Sven: 6167: show unloaded parameters as undefined
 
-Bart: 6171 as rewritten in 6174: old RedHat Linux doesn't have normall
+Bart: 6171 as rewritten in 6174: old RedHat Linux doesn't have normal
 definitions for poll.
 
 pws: 6180: Completion/Core/compinstall
+
+  pws-18
+
+Bart: 6188: compinit speedup
+
+pws: 6193: [un]setopt shouldn't complain when setting an unsettable option
+to the value it already has
+
+Sven: 6194: complete assoc array arguments by default where necessary
+
+Sven: 6195: _expand_word and _correct_word change.
+
+Sven: 6197: off by one error parsing assignment in completion
+
+pws: 6202: trivial _correct_filename change, ^Xc -> ^XC
+
+pws: 6205: use FIONREAD wherever defined, read chars immediately into
+buffer
+
+Bart: 6213: race condition in $(...), use waitforpid() instead of
+unblocking child (which shouldn't happen until later).
+
+Tanaka Akira: 6219: initialize a variable in zle_tricky.c
+
+Wayne: 6220: various compilation warnings
+
+pws: 6224: alter 6205 to read chars only when necessary, but ensure
+terminal is set appropriately.
+
+pws: 6227: configuration for large file support (from bash aclocal.m4).
+
+pws: 6235: unset -m shouldn't restore unset parameters; unsetting a global
+should remove it from paramtab even inside a function.
+
+Wayne: 6236: history changes to improve management of duplicate lines,
+incremental history read/write, and sharing history
+
+pws: 6237: window size code upgraded from 3.0.6-pre2, plus Bart's patch
+4447.
+
+pws: 6238: Wayne's share_history option set in ksh emulation
+
+pws: 6239: need space after incrementalappendhistory for kshoptionprint
+
+pws: 6240: a pipeline ending in a builtin didn't attach to the tty pgrp.
+
+Wayne: 6241: history editing can use foreign history commands; history
+appended in hend() instead of hbegin()
+
+Sven: 6046: nested parameter expansions can return either arrays or
+scalars.
+
+pws: 6246: doc changes for 6046, plus subscripts done properly
+
+Sven: 6249: fix for 6046 (problem showed up with $(...))
+
+Wayne: 6255: more history: zle toggle between local/global history;  `zle
+widget' can now take a direct numeric argument; small tweaks
+
+pws: 6257: rewrite 6240 for any old builtin structure after the pipeline
+
+pws: 6258: yet another attempt at the same problem
+
+pws: 6259: second version of compinstall