about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog141
-rw-r--r--Completion/.cvsignore1
-rw-r--r--Completion/.distfiles2
-rw-r--r--Completion/Base/_brace_parameter2
-rw-r--r--Completion/Core/_path_files38
-rw-r--r--Completion/User/.distfiles6
-rw-r--r--Completion/User/_gdb50
-rw-r--r--Completion/User/_hosts2
-rw-r--r--Completion/User/_make2
-rw-r--r--Completion/User/_rlogin2
-rw-r--r--Completion/User/_sh8
-rw-r--r--Completion/User/_su22
-rw-r--r--Completion/User/_x_options5
-rw-r--r--Config/version.mk4
-rw-r--r--Doc/.distfiles10
-rw-r--r--Doc/Zsh/compsys.yo26
-rw-r--r--Doc/Zsh/compwid.yo18
-rw-r--r--Doc/Zsh/expn.yo6
-rw-r--r--Doc/Zsh/mod_complist.yo13
-rw-r--r--Doc/Zsh/options.yo29
-rw-r--r--Doc/Zsh/params.yo5
-rw-r--r--Doc/Zsh/zle.yo9
-rw-r--r--Etc/MACHINES13
-rw-r--r--Etc/NEWS1
-rw-r--r--Functions/.cvsignore1
-rw-r--r--Functions/.distfiles2
-rw-r--r--Functions/Zftp/zfgoto5
-rw-r--r--Functions/Zftp/zfinit2
-rw-r--r--Functions/Zle/incremental-complete-word91
-rw-r--r--INSTALL9
-rw-r--r--Misc/job-control-tests45
-rw-r--r--Src/Modules/zftp.c6
-rw-r--r--Src/Zle/comp.h26
-rw-r--r--Src/Zle/comp1.c1
-rw-r--r--Src/Zle/comp1.export1
-rw-r--r--Src/Zle/compctl.c1
-rw-r--r--Src/Zle/complist.c42
-rw-r--r--Src/Zle/zle.export1
-rw-r--r--Src/Zle/zle_params.c25
-rw-r--r--Src/Zle/zle_refresh.c8
-rw-r--r--Src/Zle/zle_tricky.c162
-rw-r--r--Src/builtin.c114
-rw-r--r--Src/exec.c17
-rw-r--r--Src/glob.c10
-rw-r--r--Src/lex.c1
-rw-r--r--Src/options.c1
-rw-r--r--Src/parse.c26
-rw-r--r--Src/utils.c99
-rw-r--r--Src/zsh.h1
-rw-r--r--configure.in6
50 files changed, 822 insertions, 296 deletions
diff --git a/ChangeLog b/ChangeLog
index 963e4ec89..feea8c23f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,146 @@
+1999-07-19  Peter Stephenson  <pws@ibmth.df.unipi.it>
+
+	* pws: version 3.1.6-test-2
+
+	* pws: 7192: Src/glob.c: bug with null in pattern if at end of
+	  test string (matched as if a real character).
+
+1999-07-18  Peter Stephenson  <pws@ibmth.df.unipi.it>
+
+	* pws: 7185: Src/glob.c: don't use strcoll() for character
+	  ranges [...] because it can have side effects.
+
+	* pws: 7184: Src/lex.c: histactive didn't get get zeroed before
+	  non-interactive history use, hence interactive lines with
+	  remhist() were junked more than once.
+
+	* Wayne: 7181: Doc/Zsh/options.y, Doc/Zsh/params.yo: history docs.
+
+	* Wayne: 7180: Src/Zle/complist.c, Src/utils.c: warnings.
+
+1999-07-16  Peter Stephenson  <pws@ibmth.df.unipi.it>
+
+	* pws: 7172: Doc/Zsh/options.yo, Src/builtin.c, Src/exec.c,
+	  Src/options.c, Src/utils.c, Src/zsh.h: minor bugs with
+	  7164 fixed; CHASE_DOTS resolves ..'s to physical path;
+	  CHASE_LINKS doesn't do logical path rationalization;
+	  xsymlink() and xsymlinks() simplified and option-dependence
+	  removed.
+
+	* Sven: 7171: Src/builtin.c: alternate form of Digital/gcc
+	  bug workaround.
+
+1999-07-15  Peter Stephenson  <pws@ibmth.df.unipi.it>
+
+	* pws: 7164: Src/builtin.c, Src/exec.c: AUTOCD now allows
+	  paths with symlinks (as real cd always did); simplify code
+	  for testing for existing directory on foo/.. before removing
+	  foo/..; as a side effect, relative cd's from directory which
+	  has been deleted don't work.
+
+	* Sven: 7161: Src/Zle/comp.h, Src/Zle/complist.c,
+	  Src/Zle/zle_tricky.c, Completion/Base/_brace_parameter:
+	  be more careful with quote-prefix/suffix and path-prefix/suffix
+	  especially with accept-and-menu-complete.
+
+	* pws: 7155: Functions/Zftp/zfgoto, Functions/Zftp/zfinit:
+	  zfgoto can cd without needing to re-login; zfinit uses
+	  zmodload -e to check for zftp.
+
+	* Sven: 7154: Src/Zle/complist.c, Src/Zle/zle_refresh.c,
+	  Src/Zle/zle_tricky.c, Doc/Zsh/mod_complist.yo: don't
+	  list too many times on ambiguous completion; don't do
+	  menu-selection if no alwayslastprompt behaviour.
+
+1999-07-14  Peter Stephenson  <pws@ibmth.df.unipi.it>
+
+	* pws: 7148: INSTALL: User subdirectory
+
+	* Thomas Köhler: 7146: Completion/User/_make: didn't work.
+
+	* pws: 7145: Completion/User/_sh: use compset -q for completing
+	  after -c option	  
+
+	* Sven: 7143: Src/Zle/complist.c, Src/Zle/zle_tricky.c:
+	  listing got confused with only hidden matches.
+
+	* Sven: 7141: Completion/Core/_path_files: if there are
+	  no completions in a valid directory when we have a path ending
+	  in /, use the directory as a completion, to avoid
+	  correction/approximation of the existing directory.
+
+	* pws: 7139: Src/builtin.c: not particularly pleasant fix
+	  to problem that `cd nonexistent/..' silently did nothing, while
+	  making `cd ..' work even if current directory has gone.
+
+	* Sven: 7138: Completion/User/_hosts, Completion/User/_x_options:
+	  _hosts passes arguments as options to compadd.
+
+	* Oliver: 7136: Completion/User/_rlogin,
+	  Completion/User/_x_options: use _hosts.
+
+	* Sven: 7135: Src/Zle/zle_tricky.c, Completion/Core/_path_files:
+	  change quoting of files again.
+
+	* Sven: 7133: Doc/Zsh/expn.yo, Doc/Zsh/mod_complist.yo:
+	  ZLS_COLOURS not required for complist to work.
+
+	* pws: 7127: configure.in: help now shows --disable-dynamic
+	  and --disable-lfs.
+
+	* Sven: 7126: Src/Zle/comp.h, Src/Zle/comp1.c, Src/Zle/compctl.c,
+	  Src/Zle/zle_tricky.c, Doc/Zsh/compwid.yo,
+	  Functions/Zle/incremental-complete-word:  compstate key
+	  normal_nmatches; more i-c-w prompt escapes
+
+	* Sven: 7123: Src/Zle/zle_tricky.c: clear list on expansion
+	  failure.
+
+1999-07-13  Peter Stephenson  <pws@ibmth.df.unipi.it>
+
+	* pws: 7119: Src/Zle/zle_tricky.c: status from expansion
+	  functions.
+
+	* Sven: 7116: Doc/Zsh/compwid.yo, Doc/Zsh/expn.yo: minor
+	  changes.
+
+	* pws: 7114: Src/parse.c, Src/utils.c: line numbers again:
+	  flushing line in a script made lineno appear one too large;
+	  introduce zwarn() function.
+
+	* Sven/pws: 7112: Src/Zle/zle_params.c, Doc/Zsh/zle.yo: change
+	  array keys to scalar KEYS, works more like read -k;
+	  documentation.
+
+	* Sven: 7110: Src/Modules/zftp.c, Doc/Zsh/compsys.yo,
+	  Etc/MACHINES: signed char warnings in zftp; document
+	  _long_options options; Digital UNIX problem.
+
+	* Sven: 2432: Src/builtin.c: workaround Digital UNIX 4.0 +
+	  gcc 2.8.1 bug.
+	
+	* Sven: zsh-users/2430: Etc/NEWS: mention chmod-like mode glob
+	  qualifier.
+
+	* Sven: 7108: Misc/job-control-tests: more tests
+
+	* Bart: 7107: configure.in: too many x's in lfs handling.
+
+	* Sven: 7106: Functions/Zle/incremental-complete-word: prompting
+	  changes.
+
+	* pws: unposted: update .distfiles and .cvsignore:  _sh, _su
+	  zshcompsys.yo, zshcompwid.yo, zshzftp.yo, zshcompsys.1,
+	  zshcompwid.1, zshzftp.1 were missing from the distribution.
+
+	* Sven: 7105: Src/Zle/Zle_tricky.c: restore the command line
+	  in more places.
+
 1999-07-12  Peter Stephenson  <pws@ibmth.df.unipi.it>
 
+	* Sven: 7103: Src/Zle/zle_tricky.c, Doc/Zsh/compwid.yo: update
+	  CURRENT with compset -q; modify test for quoted delimiters.
+
 	* pws: version 3.1.6-test-1
 
 	* Sven: 7099: Completion/Core/_main_complete, Doc/Zsh/compsys.yo:
diff --git a/Completion/.cvsignore b/Completion/.cvsignore
new file mode 100644
index 000000000..f3c7a7c5d
--- /dev/null
+++ b/Completion/.cvsignore
@@ -0,0 +1 @@
+Makefile
diff --git a/Completion/.distfiles b/Completion/.distfiles
index e85e122ef..f1e1c87b3 100644
--- a/Completion/.distfiles
+++ b/Completion/.distfiles
@@ -1,3 +1,3 @@
 DISTFILES_SRC='
-    .distfiles README Makefile.in
+    .cvsignore .distfiles README Makefile.in
 '
diff --git a/Completion/Base/_brace_parameter b/Completion/Base/_brace_parameter
index 5993aecba..020dc81e9 100644
--- a/Completion/Base/_brace_parameter
+++ b/Completion/Base/_brace_parameter
@@ -18,4 +18,4 @@ q=${(M)lp%%\"#}
 
 [[ n -gt 0 ]] && suf=''
 
-_parameters -s "${q[1,-n-1]}" -S "$suf" -r '-:?#%+=[/'
+_parameters -Qs "${q[1,-n-1]}" -S "$suf" -r '-:?#%+=[/'
diff --git a/Completion/Core/_path_files b/Completion/Core/_path_files
index e65f814e1..019e3abcf 100644
--- a/Completion/Core/_path_files
+++ b/Completion/Core/_path_files
@@ -244,7 +244,7 @@ for prepath in "$prepaths[@]"; do
  	if [[ "$tmp2[1]" = */* ]]; then
 	  tmp2=( "${(@)tmp2#${prepath}${realpath}}" )
 	  if [[ "$tmp2[1]" = */* ]]; then
-	    exppaths=( "$exppaths[@]" ${^tmp2:h}/${tpre}${tsuf} )
+	    exppaths=( "$exppaths[@]" ${^tmp2:h:q}/${tpre}${tsuf} )
           else
 	    exppaths=( "$exppaths[@]" ${tpre}${tsuf} )
 	  fi
@@ -252,6 +252,18 @@ for prepath in "$prepaths[@]"; do
         continue 2
       fi
     elif (( ! $#tmp1 )); then
+      # A little extra hack: if we were completing `foo/<TAB>' and `foo'
+      # contains no files, this will normally produce no matches and other
+      # completers might think that's it's their time now. But if the next
+      # completer is _correct or something like that, this will result in
+      # an attempt to correct a valid directory name. So we just add the
+      # original string in such a case so that the command line doesn't 
+      # change but other completers still think there are matches.
+
+      if [[ -z "$tpre$tsuf" && "$pre" = */ && -z "$suf" ]]; then
+        compadd -nQS '' - "$linepath$donepath$orig"
+        tmp4=-
+      fi
       continue 2
     fi
 
@@ -310,33 +322,33 @@ for prepath in "$prepaths[@]"; do
       if [[ -n $menu ]]; then
         [[ -n "$compconfig[path_cursor]" ]] && compstate[to_end]=''
         if [[ "$tmp3" = */* ]]; then
-	  compadd -Uf -p "$linepath$testpath" -s "/${tmp3#*/}" \
+	  compadd -QUf -p "$linepath${testpath:q}" -s "/${tmp3#*/}" \
 	          -W "$prepath$realpath$testpath" "$ignore[@]" \
 		  "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" -M 'r:|/=* r:|=*' \
 		  "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
-		  - "${(@)tmp1%%/*}"
+		  - "${(@)${(@)tmp1%%/*}:q}"
 	else
-	  compadd -Uf -p "$linepath$testpath" \
+	  compadd -QUf -p "$linepath${testpath:q}" \
 	          -W "$prepath$realpath$testpath" "$ignore[@]" \
 		   "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \
 		   "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
-		   - "$tmp1[@]"
+		   - "${(@)tmp1:q}"
 	fi
       else
         if [[ "$tmp3" = */* ]]; then
           for i in "$tmp1[@]"; do
-	    compadd -Uf -p "$linepath$testpath" -s "/${i#*/}" \
+	    compadd -QUf -p "$linepath${testpath:q}" -s "/${${i#*/}:q}" \
 		    -W "$prepath$realpath$testpath" "$ignore[@]" \
 		    "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" -M 'r:|/=* r:|=*' \
 		    "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
-		    - "${i%%/*}"
+		    - "${${i%%/*}:q}"
 	  done
         else
-	  compadd -Uf -p "$linepath$testpath" \
+	  compadd -QUf -p "$linepath${testpath:q}" \
 		  -W "$prepath$realpath$testpath" "$ignore[@]" \
 		  "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \
 		  "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
-		  - "$tmp1[@]"
+		  - "${(@)tmp1:q}"
         fi
       fi
       tmp4=-
@@ -361,11 +373,11 @@ for prepath in "$prepaths[@]"; do
   done
 
   if [[ -z "$tmp4" ]]; then
-    compadd -Uf -p "$linepath$testpath" \
+    compadd -QUf -p "$linepath${testpath:q}" \
 	    -W "$prepath$realpath$testpath" "$ignore[@]" \
 	    "$addpfx[@]" "$addsfx[@]" "$remsfx[@]" \
 	    "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
-	    - "$tmp1[@]"
+	    - "${(@)tmp1:q}"
   fi
 done
 
@@ -376,9 +388,9 @@ done
 exppaths=( "${(@)exppaths:#$orig}" )
 
 if [[ -n "$compconfig[path_expand]" &&
-      $#exppaths -eq 1 && nm -eq compstate[nmatches] ]]; then
+      $#exppaths -gt 0 && nm -eq compstate[nmatches] ]]; then
   compadd -QU -S '' "$group[@]" "$expl[@]" -i "$IPREFIX" -I "$ISUFFIX" \
-          -M 'r:|/=* r:|=*' -p "$linepath" - "${(@)exppaths}"
+          -M 'r:|/=* r:|=*' -p "$linepath" - "$exppaths[@]"
 fi
 
 [[ nm -ne compstate[nmatches] ]]
diff --git a/Completion/User/.distfiles b/Completion/User/.distfiles
index c96f2ba3e..9187fa520 100644
--- a/Completion/User/.distfiles
+++ b/Completion/User/.distfiles
@@ -1,6 +1,8 @@
 DISTFILES_SRC='
     .distfiles
-    _a2ps _chown _compress _configure _dd _dvi _find _groups _gunzip _gzip
-    _hosts _use_lo _make _man _mh _pdf _ps _rcs _rlogin _strip _stty
+    _a2ps _chown _compress _configure _dd _dvi _find
+    _gdb _groups _gunzip _gzip
+    _hosts _use_lo _make _man _mh _pdf _ps
+    _rcs _rlogin _sh _strip _stty _su
     _tar _tar_archive _tex _uncompress _x_options _xfig 
 '
diff --git a/Completion/User/_gdb b/Completion/User/_gdb
new file mode 100644
index 000000000..ff54e6a07
--- /dev/null
+++ b/Completion/User/_gdb
@@ -0,0 +1,50 @@
+#compdef gdb
+
+# This uses the configuration keys `ps_args' and `ps_listargs'
+# described in the `_wait' function.
+
+local cur="$words[CURRENT]" prev w list ret=1
+
+_long_options -t '*=(CORE|SYM)FILE'	'_files' \
+		 '*=EXECFILE'   	'_files *(*)' \
+		 '*=TTY'		'compadd /dev/tty*' && return 0
+
+if compset -P '-(cd|directory)='; then
+  _files -/
+elif compset -P '-tty='; then
+  compadd - /dev/tty*
+elif compset -P '-(exec|se)='; then
+  _files -/g '*(*)'
+elif compset -P '-(symbols|core|command)='; then
+  _files
+elif compset -P -; then
+  compadd -QS '' - symbols\= exec\= se\= core\= command\= directory\= \
+	           cd\= tty\=
+  compadd - help h s e c x d nx n quiet q batch fullname f b
+else
+  prev="$words[CURRENT-1]"
+
+  case "$prev" in
+  (-d) _files -/ && return 0 ;;
+  (-e) _files -/g '*(*)' && return 0 ;;
+  (-[csx]) _files && return 0 ;;
+  (-b) compadd -V baud 0 50 75 110 134 150 200 300 600 1200 1800 2400 4800 \
+                       9600 19200 38400 57600 115200 230400 && return 0 ;;
+  esac
+  w=( "${(@)words[2,-1]}" )
+  while [[ "$w[1]" = -* ]]; do
+    [[ "$w[1]" = -[decsxb] ]] && shift 1 w
+    shift 1 w
+  done
+
+  if [[ $#w -gt 1 ]]; then
+    _files && ret=0
+    list=("${(F)${(@Mr:COLUMNS-1:)${(f)$(ps ${compconfig[ps_listargs]:-$compconfig[ps_args]} 2>/dev/null)}[2,-1]:#[ 	]#${PREFIX}[0-9]#${SUFFIX}[ 	]*${w[1]:t}}}
+")
+    compadd -y list - ${${${(M)${(f)"$(ps $compconfig[ps_args] 2>/dev/null)"}:#*${w[1]:t}*}## #}%% *} && ret=0
+
+    return ret
+  else
+    _files -/g '*(*)'
+  fi
+fi
diff --git a/Completion/User/_hosts b/Completion/User/_hosts
index a0aca0a62..d498425bc 100644
--- a/Completion/User/_hosts
+++ b/Completion/User/_hosts
@@ -2,4 +2,4 @@
 
 : ${(A)hosts:=${(s: :)${(ps:\t:)${${(f)"$(</etc/hosts)"}%%\#*}##[:blank:]#[^[:blank:]]#}}}
 
-compgen -M 'm:{a-zA-Z}={A-Za-z} r:|.=* r:|=*' -k hosts
+compadd -M 'm:{a-zA-Z}={A-Za-z} r:|.=* r:|=*' "$@" - "$hosts[@]"
diff --git a/Completion/User/_make b/Completion/User/_make
index add58dbf0..7a19484f0 100644
--- a/Completion/User/_make
+++ b/Completion/User/_make
@@ -19,6 +19,6 @@ else
   fi
 
   [[ -n "$file" ]] &&
-    compadd - $(awk '/^[a-zA-Z0-9][^/ 	]+:/ {print $1}' FS=: $file) && ret=0
+    compadd - $(awk '/^[a-zA-Z0-9][^\/ 	]+:/ {print $1}' FS=: $file) && ret=0
   (( ret )) && _files
 fi
diff --git a/Completion/User/_rlogin b/Completion/User/_rlogin
index b792ba0d1..36ee7ffe8 100644
--- a/Completion/User/_rlogin
+++ b/Completion/User/_rlogin
@@ -1,7 +1,7 @@
 #compdef rlogin rsh ssh
 
 if [[ CURRENT -eq 2 ]]; then
-  compgen -k hosts
+  _hosts
 elif [[ CURRENT -eq 3 ]]; then
   compadd - -l
 else
diff --git a/Completion/User/_sh b/Completion/User/_sh
new file mode 100644
index 000000000..ea0fcfc11
--- /dev/null
+++ b/Completion/User/_sh
@@ -0,0 +1,8 @@
+#compdef sh ksh bash zsh csh tcsh rc
+
+if (( CURRENT == ${words[(i)-c]} + 1 )); then
+  compset -q
+  _normal
+else
+  _default
+fi
diff --git a/Completion/User/_su b/Completion/User/_su
new file mode 100644
index 000000000..e5bf09d80
--- /dev/null
+++ b/Completion/User/_su
@@ -0,0 +1,22 @@
+#compdef su
+
+local shell comp name usr base
+
+[[ $words[2] != - ]]
+(( base=$?+2 ))
+
+if [[ CURRENT -eq base ]]; then
+  compgen -u && return
+  usr=root
+elif [[ CURRENT -ge base+1 ]]; then
+  usr=$words[base]
+else
+  return
+fi
+
+shell=${"$(egrep "^$usr:" </etc/passwd)"##*:}
+compset -n $base
+for name in $shell $shell:t -default-; do
+  comp="$_comps[$name]"
+  [[ ! -z "$comp" ]] && "$comp" && return
+done  
diff --git a/Completion/User/_x_options b/Completion/User/_x_options
index a9c18b0d1..fddec2af4 100644
--- a/Completion/User/_x_options
+++ b/Completion/User/_x_options
@@ -3,7 +3,8 @@
 # A simple pattern completion, just as an example.
 
 if [ "$words[CURRENT-1]" = "-display" ]; then
-  compgen -k hosts -S':0'
+  _compskip=''
+  _hosts -S :0
 else
-  compadd -J options - -display -name -xrm
+  compadd -P- -J options - display name xrm
 fi
diff --git a/Config/version.mk b/Config/version.mk
index 30858c298..640fe6f8e 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.6-test-1
-VERSION_DATE='July 9, 1999'
+VERSION=3.1.6-test-2
+VERSION_DATE='July 19, 1999'
diff --git a/Doc/.distfiles b/Doc/.distfiles
index 7fd1cdcc1..46f1e7406 100644
--- a/Doc/.distfiles
+++ b/Doc/.distfiles
@@ -2,11 +2,13 @@ DISTFILES_SRC='
     .cvsignore .distfiles Makefile.in
     META-FAQ.yo intro.ms
     version.yo zmacros.yo zman.yo ztexi.yo
-    zsh.yo zshbuiltins.yo zshcompctl.yo zshexpn.yo zshmisc.yo
-    zshmodules.yo zshoptions.yo zshparam.yo zshzle.yo
+    zsh.yo zshbuiltins.yo zshcompctl.yo zshcompsys.yo zshcompwid.yo
+    zshexpn.yo zshmisc.yo zshmodules.yo zshoptions.yo zshparam.yo
+    zshzftpsys.yo zshzle.yo
     zsh.texi
-    zsh.1 zshbuiltins.1 zshcompctl.1 zshexpn.1 zshmisc.1 zshmodules.1
-    zshoptions.1 zshparam.1 zshzle.1 zshall.1
+    zsh.1 zshbuiltins.1 zshcompctl.1 zshcompsys.1 zshcompwid.1 zshexpn.1
+    zshmisc.1 zshmodules.1 zshoptions.1 zshparam.1 zshzle.1 zshall.1
+    zshzftpsys.1
 '
 
 DISTFILES_DOC='
diff --git a/Doc/Zsh/compsys.yo b/Doc/Zsh/compsys.yo
index 477fbdc77..cca636cdc 100644
--- a/Doc/Zsh/compsys.yo
+++ b/Doc/Zsh/compsys.yo
@@ -31,7 +31,7 @@ next section.
 
 Usually, tt(compinstall) will insert code into tt(.zshrc), although if
 that is not writable it will save it in another file and tell you that
-file's locations.  Note that it is up to you to make sure that the lines
+file's location.  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
@@ -92,7 +92,7 @@ will only need to run this yourself if you change the configuration
 (e.g. using tt(compdef)) and then want to dump the new one.  The name of
 the old dumped file will be remembered for this purpose.
 
-If the parameter tt(_compdir) is set, tt(compinit) uses it has a directory
+If the parameter tt(_compdir) is set, tt(compinit) uses it as a directory
 where completion functions can be found; this is only necessary if they are
 not already in the function search path.
 
@@ -489,7 +489,7 @@ non-empty string it should be an expression usable inside a `tt($((...)))'
 arithmetical expression. In this case, expansion of substitutions will
 be done if the expression evaluates to `tt(1)'. For example, with
 
-example(compconf expand_substitute='NUMERIC != 1')
+example(compconf expand_substitute='${NUMERIC:-1} != 1')
 
 substitution will be performed only if given an explicit numeric
 argument other than `tt(1)', as by typing `tt(ESC 2 TAB)'.
@@ -720,8 +720,8 @@ is used.
 )
 item(tt(_parameters))(
 This should be used to complete parameter names if you need some of the
-extra options of tt(compadd). It first tries to complete only non-local
-parameters. All arguments are passed unchanged to the tt(compadd) builtin.
+extra options of tt(compadd). All arguments are passed unchanged to
+the tt(compadd) builtin.
 )
 item(tt(_options))(
 This can be used to complete option names. The difference to the
@@ -780,9 +780,19 @@ descriptions that contain `tt(=DIR)' or `tt(=PATH)'. These builtin
 patterns can be overridden by patterns given as arguments, however.
 
 This function also accepts the `tt(-X)', `tt(-J)', and `tt(-V)'
-options which are passed unchanged to `tt(compadd)'. Finally, it
-accepts the option `tt(-t)'; if this is given, completion is only done
-on words starting with two hyphens.
+options which are passed unchanged to `tt(compadd)'. If the
+option `tt(-t)' is given, completion is only done on words starting
+with two hyphens. The option `tt(-i) var(patterns)' can be used to
+give patterns for options which should not be completed. The patterns
+can be given as the name of an array parameter or as a literal list in 
+parentheses. E.g. `tt(-i "(--(en|dis)able-FEATURE*)")' will ignore the
+options `tt(--enable-FEATURE)' and `tt(--diable-FEATURE)'. Finally,
+the option `tt(-s) var(pairs)' can be used to describe options
+aliases. Each var(pair) consists of a pattern and a
+replacement. E.g. some tt(configure)-scripts describe options only as
+`tt(--enable-foo)', but also accept `tt(disable-foo)'. To allow
+completion of the second form, one would use
+`tt(-s "(#--enable- --disable-)")'.
 )
 enditem()
 
diff --git a/Doc/Zsh/compwid.yo b/Doc/Zsh/compwid.yo
index 4d439ab30..62c243740 100644
--- a/Doc/Zsh/compwid.yo
+++ b/Doc/Zsh/compwid.yo
@@ -175,6 +175,12 @@ is unset.
 item(tt(nmatches))(
 The number of matches generated and accepted by the completion code so far.
 )
+item(tt(normal_nmatches))(
+Like tt(nmatches), but counts only matches in the normal set. I.e. file
+names with one of the suffixes from the tt(fignore) array and matches
+put into the alternate set using the tt(-a) option of the tt(compadd)
+builtin command (see below) are not counted.
+)
 item(tt(matcher))(
 When completion is performed with a global match specification as defined
 by
@@ -410,7 +416,7 @@ item(tt(-I) var(ignored-suffix))(
 Like tt(-i), but gives an ignored suffix.
 )
 item(tt(-y) var(array))(
-This gives a number of string to display instead of the matches. This
+This gives a number of strings to display instead of the matches. This
 is like the tt(-y) option of the tt(compctl) builtin command but the
 var(array) argument may only be the name of an array parameter or a
 literal array in parentheses containing the strings to display.
@@ -483,7 +489,7 @@ from the first set are stored. Normally only the matches in the first
 set are used, but if this set is empty, the words from the alternate
 set are used.
 
-The tt(compadd) builtin does not use tt(fignore) parameter and
+The tt(compadd) builtin does not use the tt(fignore) parameter and
 normally stores all words in the first set.  With the tt(-a)-flag
 given, however, the given var(words) are stored in the alternate set unless
 this flag is overridden by the tt(-F) option.
@@ -518,7 +524,7 @@ will be done by the completion code. Normally this is used in
 functions that do the matching themselves.
 
 Note that with tt(compadd) this option does not automatically turn on
-menu completion if tt(AUTO_LIST), unlike the corresponding option of
+menu completion if tt(AUTO_LIST) is set, unlike the corresponding option of
 tt(compctl) and tt(compgen) commands.
 )
 item(tt(-O) var(array))(
@@ -622,9 +628,9 @@ testing and modification is performed as if it were not given.
 item(tt(-q))(
 The word
 currently being completed is split in separate words at the spaces. The 
-resulting words are stored in the tt(words) array, and tt(PREFIX),
-tt(SUFFIX), tt(QIPREFIX), and tt(QISUFFIX) are modified to reflect the 
-word part that is completed.
+resulting words are stored in the tt(words) array, and tt(CURRENT),
+tt(PREFIX), tt(SUFFIX), tt(QIPREFIX), and tt(QISUFFIX) are modified to
+reflect the word part that is completed.
 )
 enditem()
 
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index 89f94a507..bb75d4865 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -1192,7 +1192,7 @@ grouping the string as tt([d][cb][a]) and tt([a][bc][d]).
 Non-literal parts of the pattern must match exactly, including characters
 in character ranges: hence tt(LPAR()#a1)tt(RPAR()???)  matches strings of
 length four, by applying rule 4 to an empty part of the pattern, but not
-strings of length three, since all the tt(?) must match.  Other characters
+strings of length two, since all the tt(?) must match.  Other characters
 which must match exactly are initial dots in filenames (unless the
 tt(GLOB_DOTS) option is set), and all slashes in file names, so that
 tt(a/bc) is two errors from tt(ab/c) (the slash cannot be transposed with
@@ -1351,7 +1351,7 @@ bit.
 
 Thus, `tt(*(f70?))' gives the files for which the owner has read,
 write, and execute permission, and for which other group members have
-no rights, independent of the permissions for other user. The pattern
+no rights, independent of the permissions for other users. The pattern
 `tt(*(f-100))' gives all files for which the owner does not have
 execute permission, and `tt(*(f:gu+w,o-rx))' gives the files for which 
 the owner and the other members of the group have at least write
@@ -1455,7 +1455,7 @@ returned list. The syntax is the same as for array
 subscripts. var(beg) and the optional var(end) may be mathematical
 expressions. As in parameter subscripting they may be negative to make 
 them count from the last match backward. E.g.: `tt(*(-OL[1,3]))'
-gives a list of the names of three biggest files.
+gives a list of the names of the three largest files.
 )
 enditem()
 
diff --git a/Doc/Zsh/mod_complist.yo b/Doc/Zsh/mod_complist.yo
index 236896e43..b37252582 100644
--- a/Doc/Zsh/mod_complist.yo
+++ b/Doc/Zsh/mod_complist.yo
@@ -12,9 +12,9 @@ not automatically be loaded if it is not linked in:  on systems with
 dynamic loading, `tt(zmodload complist)' is required.
 
 subsect(Parameters)
-For both extensions one of the parameters tt(ZLS_COLORS) or tt(ZLS_COLOURS)
-must be set, even if the value is empty (which uses all the default values
-given below). These describe how matches are highlighted. The format of the
+The parameters tt(ZLS_COLORS) and tt(ZLS_COLOURS) describe how matches
+are highlighted. To turn on highlighting an empty value suffices, in
+which case all the default values given below will be used. The format of the
 value of these parameters is the same as used by the GNU version of the
 tt(ls) command: a colon-separated list of specifications of the form
 `var(name)=var(value)'. The var(name) may be one of the following strings,
@@ -79,7 +79,12 @@ the default values will have no visual effect.
 
 subsect(Menu selection)
 The tt(complist) module also offers an alternative style of selecting
-matches from a list, called menu-selection. It can be invoked directly by
+matches from a list, called menu-selection, which can be used if the
+shell is set up to return to the last prompt after showing a
+completion list (see the tt(ALWAYS_LAST_PROMPT) option in
+ifzman(zmanref(zshoptions))\
+ifnzman(noderef(Options))\
+). It can be invoked directly by
 the widget tt(menu-select) defined by the module.  Alternatively,
 the parameter tt(SELECTMIN) can be set to an integer, which give the
 minimum number of matches that must be present before menu selection is
diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index 265c5e184..bfd7bc201 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -205,11 +205,30 @@ tt(AUTO_CD) option set) is not a directory, and does not begin with a
 slash, try to expand the expression as if it were preceded by a `tt(~)' (see
 noderef(Filename Expansion)).
 )
+pindex(CHASE_DOTS)
+cindex(cd, with .. in argument)
+item(tt(CHASE_DOTS))(
+When changing to a directory containing a path segment `tt(..)' which would
+otherwise be treated as cancelling the previous segment in the path (in
+other words, `tt(foo/..)' would be removed from the path, or if `tt(..)' is
+the first part of the path, the last part of tt($PWD) would be deleted),
+instead resolve the path to the physical directory.  This option is
+overridden by tt(CHASE_LINKS).
+
+For example, suppose tt(/foo/bar) is a link to the directory tt(/alt/rod).
+Without this option set, `tt(cd /foo/bar/..)' changes to tt(/foo); with it
+set, it changes to tt(/alt).  The same applies if the current directory
+is tt(/foo/bar) and `tt(cd ..)' is used.  Note that all other symbolic
+links in the path will also be resolved.
+)
 pindex(CHASE_LINKS)
 cindex(links, symbolic)
 cindex(symbolic links)
 item(tt(CHASE_LINKS) (tt(-w)))(
 Resolve symbolic links to their true values when changing directory.
+This also has the effect of tt(CHASE_DOTS), i.e. a `tt(..)' path segment
+will be treated as referring to the physical parent, even if the preceeding
+path segment is a symbolic link.
 )
 pindex(CLOBBER)
 cindex(clobbering, of files)
@@ -416,9 +435,13 @@ isn't there.
 pindex(HIST_EXPIRE_DUPS_FIRST)
 cindex(history, expiring duplicates)
 item(tt(HIST_EXPIRE_DUPS_FIRST))(
-If the internal 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 list.
+If the internal history needs to be trimmed to add the current command line,
+setting this option will cause the oldest history event that has a duplicate
+to be lost before losing a unique event from the list.
+You should be sure to set the value of tt(HISTSIZE) to a larger number
+than tt(SAVEHIST) in order to give you some room for the duplicated
+events, otherwise this option will behave just like
+tt(HIST_IGNORE_ALL_DUPS) once the history fills up with unique events.
 )
 pindex(HIST_IGNORE_ALL_DUPS)
 cindex(history, ignoring all duplicates)
diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index 05bd24613..2dbff771e 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -507,7 +507,10 @@ If unset, the history is not saved.
 )
 vindex(HISTSIZE)
 item(tt(HISTSIZE) <S>)(
-The maximum size of the history list.
+The maximum number of events stored in the internal history list.
+If you use the tt(HIST_EXPIRE_DUPS_FIRST) option, setting this value
+larger than the tt(SAVEHIST) size will give you the difference as a
+cushion for saving duplicated history events.
 )
 vindex(HOME)
 item(tt(HOME) <S>)(
diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo
index ce1b27fec..60c003533 100644
--- a/Doc/Zsh/zle.yo
+++ b/Doc/Zsh/zle.yo
@@ -154,12 +154,9 @@ vindex(LASTWIDGET)
 item(tt(LASTWIDGET) (scalar))(
 The name of the last widget that was executed.
 )
-vindex(keys)
-item(tt(keys) (array))(
-The keys typed to invoke this widget, one element per
-key. Control-keys are reported with a leading `tt(^)', as in `tt(^A)',
-and meta-keys are reported with a leading `tt(M-)', as in `tt(M-a)' and 
-`tt(M-^A)'.
+vindex(KEYS)
+item(tt(KEYS) (scalar))(
+The keys typed to invoke this widget, as a literal string.
 )
 vindex(NUMERIC)
 item(tt(NUMERIC) (integer))(
diff --git a/Etc/MACHINES b/Etc/MACHINES
index fd386c7e0..038340e79 100644
--- a/Etc/MACHINES
+++ b/Etc/MACHINES
@@ -39,15 +39,21 @@ DEC: OSF/1 1.2, 1.3, 2.0, 3.*, DEC Unix 4.* (Alpha)
 	remove the bogus strip and use /bin/strip instead.
 
         On Digital UNIX 4.0, compilation with gcc and with --enable-dynamic
-        apparently needs configuring with explicit flags:
+        apparently needs configuring with explicit flags when compiling
+        with debugging enabled:
           DLLD=gcc LDFLAGS='-g -rpath <path-to-.so-files>' ./configure ...
 
 FreeBSD: FreeBSD 2.2.7 [3.1.4]
 	Should build `out-of-the-box'.
 
-HP: HP-UX 9, 10.20
+HP: HP-UX 9, 10.20, 11.0
 	Should build `out-of-the-box'.
 
+	Problems with dynamic loading have been reported under 11, but
+	this should compile using the standard dlopen() function set
+	(rather than the 10.20 shl_load() function set).  More details of
+	any difficulties would be appreciated.
+
 IBM: AIX
 	Should build `out-of-the-box'.  On AIX 3.x (at least),
 	--enable-zsh-mem will not work.
@@ -87,9 +93,6 @@ SGI: IRIX 5.1.1.1, 5.2, 5.3, 6.2, 6.3, 6.5
         full optimization (cc -O3 -OPT:Olimit=0) causes problems.
 
 Sun: SunOS 4.1.*
-	Dynamic loading does not work under SunOS 4.1.  Sometimes,
-	you may need to turn it off explicitly with --disable-dynamic.
-
 	Under 4.1.3 if yellow pages is used, username completion may cause
 	segmentation violation.  This is a bug in the shared library not
 	in zsh.  Some libc.so.1.9.* has this bug (it fails in yp_all).
diff --git a/Etc/NEWS b/Etc/NEWS
index 9d58c9cc6..aad7ccceb 100644
--- a/Etc/NEWS
+++ b/Etc/NEWS
@@ -34,6 +34,7 @@ Globbing changes:
   - Case-insensitive and approximate globbing.
   - Ordering and indexing of globbing matches, e.g. *(om[1]) picks
     most recently modified file.
+  - General file mode qualifier with chmod(1)-like syntax, e.g. *(f:u+wx:)
 
 New loadable modules:
   - zftp, plus associated function suite, for turning your zsh session
diff --git a/Functions/.cvsignore b/Functions/.cvsignore
new file mode 100644
index 000000000..f3c7a7c5d
--- /dev/null
+++ b/Functions/.cvsignore
@@ -0,0 +1 @@
+Makefile
diff --git a/Functions/.distfiles b/Functions/.distfiles
index fe60df9b7..03aebe308 100644
--- a/Functions/.distfiles
+++ b/Functions/.distfiles
@@ -1,3 +1,3 @@
 DISTFILES_SRC='
-    .distfiles Makefile.in README.zftp
+    .cvsignore .distfiles Makefile.in README.zftp
 '
diff --git a/Functions/Zftp/zfgoto b/Functions/Zftp/zfgoto
index 8d6f00a3a..bd1cdbfe5 100644
--- a/Functions/Zftp/zfgoto
+++ b/Functions/Zftp/zfgoto
@@ -67,7 +67,10 @@ line=${line#*@}
 host=${line%%:*}
 dir=${line#*:}
 
-if [[ $user = ftp || $user = anonymous ]]; then
+if [[ $ZFTP_USER = $user && $ZFTP_HOST = $host ]]; then
+  # We're already there, just change directory
+  zfcd ${dir:-~}
+elif [[ $user = ftp || $user = anonymous ]]; then
   # Anonymous ftp, so we don't need password etc.
   zfanon $host && [[ -n $dir ]] && zfcd $dir
 elif [[ $zflastsession = ${host}:* && $user = $zflastuser ]]; then
diff --git a/Functions/Zftp/zfinit b/Functions/Zftp/zfinit
index fbe6c5979..2a5fd9b47 100644
--- a/Functions/Zftp/zfinit
+++ b/Functions/Zftp/zfinit
@@ -1,6 +1,6 @@
 emulate -L zsh
 
-[[ $1 = -n ]] || zmodload -ia zftp
+[[ $1 = -n ]] || zmodload -e zftp || zmodload -ia zftp
 
 alias zfcd='noglob zfcd'
 alias zfget='noglob zfget'
diff --git a/Functions/Zle/incremental-complete-word b/Functions/Zle/incremental-complete-word
index 2a9c1aff2..3831ecaa6 100644
--- a/Functions/Zle/incremental-complete-word
+++ b/Functions/Zle/incremental-complete-word
@@ -4,32 +4,55 @@
 # to a key.
 
 # This allows incremental completion of a word.  After starting this
-# command, a list of completion choices is shown after every character you
-# type, which you can delete with ^h or DEL.  RET will accept the
-# completion so far.  You can hit TAB to do normal completion and ^g to
-# abort back to the state when you started.
+# command, a list of completion choices can be shown after every character
+# you type, which you can delete with ^h or DEL.  RET will accept the
+# completion so far.  You can hit TAB to do normal completion, ^g to
+# abort back to the state when you started, and ^d to list the matches.
 #
-# Completion keys:
-#   incremental_prompt   Prompt to show in status line during icompletion;
-#                        the sequence `%u' is replaced by the unambiguous
-#                        part of all matches if there is any and it is
-#                        different from the word on the line
-# incremental_stop       Pattern matching keys which will cause icompletion
-#                         to stop and the key to be re-executed
-# incremental_break      Pattern matching keys which will cause icompletion
-#                         to stop and the key to be discarded
-# incremental_completer  Set of completers, like the `completer' key
-#   incremental_list     If set to a non-empty string, the matches will be
-#                        listed on every key-press
+# This works best with the new function based completion system.
+#
+# Configuration keys:
+#
+#  incremental_prompt
+#    Prompt to show in status line during icompletion. The sequence `%u'
+#    is replaced by the unambiguous part of all matches if there is any
+#    and it is different from the word on the line. A `%s' is replaced
+#    with `-no match-', `-no prefix-', or an empty string if there is
+#    no completion matching the word on the line, if the matches have 
+#    no common prefix different from the word on the line or if there is
+#    such a common prefix, respectively. The sequence `%c' is replaced
+#    by the name of the completer function that generated the matches
+#    (without the leading underscore). Finally, `%n' is replaced by the
+#    number of matches generated and `%a' is replaced by an empty string
+#    if the matches are in the normal set (i.e. the one without file names
+#    with one of the suffixes from `fignore') and with ` -alt-' if the
+#    matches are in the alternate set.
+#
+#  incremental_stop
+#    Pattern matching keys which will cause icompletion to stop and the
+#    key to be re-executed.
+#
+#  incremental_break
+#    Pattern matching keys which will cause icompletion to stop and the
+#    key to be discarded.
+#
+#  incremental_completer
+#    Set of completers, like the `completer' key for normal completion.
+#
+#  incremental_list
+#    If set to a non-empty string, the matches will be listed on every
+#    key-press.
+
 
 emulate -L zsh
 unsetopt autolist menucomplete automenu # doesn't work well
 
-local key lbuf="$LBUFFER" rbuf="$RBUFFER" pmpt word lastl lastr wid twid
+local key lbuf="$LBUFFER" rbuf="$RBUFFER" pmpt word
+local lastl lastr wid twid num alt
 
 [[ -n "$compconfig[incremental_completer]" ]] &&
-set ${(s.:.)compconfig[incremental_completer]}
-pmpt="${compconfig[incremental_prompt]-incremental completion...}"
+    set ${(s.:.)compconfig[incremental_completer]}
+pmpt="${compconfig[incremental_prompt]-incremental (%c): %u%s}"
 
 if [[ -n "$compconfig[incremental_list]" ]]; then
   wid=list-choices
@@ -40,12 +63,22 @@ fi
 zle $wid "$@"
 LBUFFER="$lbuf"
 RBUFFER="$rbuf"
-if [[ "${LBUFFER}${RBUFFER}" = *${_lastcomp[unambiguous]}* ]]; then
+if (( ! _lastcomp[nmatches] )); then
+  word=''
+  state='-no match-'
+elif [[ "${LBUFFER}${RBUFFER}" = *${_lastcomp[unambiguous]}* ]]; then
   word=''
+  state='-no prefix-'
 else
   word="${_lastcomp[unambiguous]}"
+  state=''
+fi
+num=$_lastcomp[normal_nmatches]
+if (( ! num )); then
+  num="${_lastcomp[nmatches]}"
+  alt=' -alt-'
 fi
-zle -R "${pmpt//\\%u/$word}"
+zle -R "${${${${${pmpt//\\%u/$word}//\\%s/$state}//\\%c/${_lastcomp[completer][2,-1]}}//\\%n/$num}//\\%a/$alt}"
 read -k key
 
 while [[ '#key' -ne '#\\r' && '#key' -ne '#\\n' &&
@@ -72,12 +105,24 @@ while [[ '#key' -ne '#\\r' && '#key' -ne '#\\n' &&
   zle $twid "$@"
   LBUFFER="$lastl"
   RBUFFER="$lastr"
-  if [[ "${LBUFFER}${RBUFFER}" = *${_lastcomp[unambiguous]}* ]]; then
+  if (( ! _lastcomp[nmatches] )); then
     word=''
+    state='-no match-'
+  elif [[ "${LBUFFER}${RBUFFER}" = *${_lastcomp[unambiguous]}* ]]; then
+    word=''
+    state='-no prefix-'
   else
     word="${_lastcomp[unambiguous]}"
+    state=''
+  fi
+  num=$_lastcomp[normal_nmatches]
+  if (( ! num )); then
+    num="${_lastcomp[nmatches]}"
+    alt=' -alt-'
+  else
+    alt=''
   fi
-  zle -R "${pmpt//\\%u/$word}"
+  zle -R "${${${${${pmpt//\\%u/$word}//\\%s/$state}//\\%c/${_lastcomp[completer][2,-1]}}//\\%n/$num}//\\%a/$alt}"
   read -k key
 done
 
diff --git a/INSTALL b/INSTALL
index b8eaa561c..5e61f0795 100644
--- a/INSTALL
+++ b/INSTALL
@@ -246,9 +246,12 @@ plus those provide functions for the line editor, i.e.
   FUNCTIONS_INSTALL='Core/* Base/* Builtins/* User/* Commands/* Zle/*'
 and if the --enable-dynamic option was given, the functions in
 Functions/Zftp, which require the zftp module to be available (see the
-zshzftpsys manual page), will be included as well.  There are also some
-miscellaneous functions with documentation in comments; the complete set
-of functions can be installed with
+zshzftpsys manual page), will be included as well.  Note, however, that
+some of the functions in the User subdirectory are version- and
+system-specific.
+
+There are also some miscellaneous functions with documentation in comments;
+the complete set of functions can be installed with
   FUNCTIONS_INSTALL='Core/* Base/* Builtins/* User/* Commands/* \
   Misc/* Zftp/* Zle/*'
 Note you should set this by hand to include `Zftp/*' if you have zftp
diff --git a/Misc/job-control-tests b/Misc/job-control-tests
index 7e35fba0b..ecb9a7694 100644
--- a/Misc/job-control-tests
+++ b/Misc/job-control-tests
@@ -29,3 +29,48 @@ while true; do sed -e 's/foo/bar/' Src/builtin.c >/dev/null; done
 # ignoring the error messages from sed.
 # ^Z is more of a problem since you have to catch the sed.
 while true; do sed -e 's/foo/bar/' non-existent-file >/dev/null; done
+
+# Try
+#   ^Z
+#   fg
+#   ^Z
+#   fg
+fn() {
+  local a
+  while read a; do :; done
+  less "$@"
+}
+cat foo | fn bar
+
+# Try
+#   ^Z
+#   fg
+fn() {
+  cat builtin.c
+}
+fn | while read a; do :; done
+
+# Try
+#   ^Z
+#   fg
+#   q
+#   ^Z
+#   fg
+#   q
+fn() {
+  less builtin.c
+  echo done
+}
+x=2; while (( x-- )); do f; done
+
+# Try
+#   ^C
+# This won't work because zcat doesn't tell us that it received a signal.
+# But
+#   ^Z
+#   fg
+#   ^C (probably a second ^C is needed: if the continued zcat is still running)
+# works.
+# (See also the file Etc/BUGS)
+while true; do zcat foo.gz > /dev/null; done
+
diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c
index 63bca854c..738d596fa 100644
--- a/Src/Modules/zftp.c
+++ b/Src/Modules/zftp.c
@@ -2261,7 +2261,7 @@ zftp_type(char *name, char **args, int flags)
 	fflush(stdout);
 	return 0;
     } else {
-	nt = toupper(*str);
+	nt = toupper(STOUC(*str));
 	/*
 	 * RFC959 specifies other types, but these are the only
 	 * ones we know what to do with.
@@ -2294,7 +2294,7 @@ zftp_mode(char *name, char **args, int flags)
 	fflush(stdout);
 	return 0;
     }
-    nt = str[0] = toupper(*str);
+    nt = str[0] = toupper(STOUC(*str));
     if (str[1] || (nt != 'S' && nt != 'B')) {
 	zwarnnam(name, "transfer mode %s not recognised", str, 0);
 	return 1;
@@ -2651,7 +2651,7 @@ bin_zftp(char *name, char **args, char *ops, int func)
     if ((prefs = getsparam("ZFTP_PREFS"))) {
 	zfprefs = 0;
 	for (ptr = prefs; *ptr; ptr++) {
-	    switch (toupper(*ptr)) {
+	    switch (toupper(STOUC(*ptr))) {
 	    case 'S':
 		/* sendport */
 		zfprefs |= ZFPF_SNDP;
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index 8264890df..b0fbf3ac6 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -218,6 +218,8 @@ struct cmatch {
     int brsl;			/* ...and the suffix */
     char *rems;			/* when to remove the suffix */
     char *remf;			/* shell function to call for suffix-removal */
+    int qipl;			/* length of quote-prefix */
+    int qisl;			/* length of quote-suffix */
     int rnum;			/* group relative number */
     int gnum;			/* global number */
 };
@@ -349,25 +351,25 @@ struct chdata {
 
 
 #define CPN_NMATCHES   0
-#define CP_NMATCHES    (1 <<  CPN_NMATCHES)
+#define CP_NMATCHES    (1 << CPN_NMATCHES)
 #define CPN_MATCHER    1
-#define CP_MATCHER     (1 <<  CPN_MATCHER)
+#define CP_MATCHER     (1 << CPN_MATCHER)
 #define CPN_MATCHERSTR 2
-#define CP_MATCHERSTR  (1 <<  CPN_MATCHERSTR)
+#define CP_MATCHERSTR  (1 << CPN_MATCHERSTR)
 #define CPN_MATCHERTOT 3
-#define CP_MATCHERTOT  (1 <<  CPN_MATCHERTOT)
+#define CP_MATCHERTOT  (1 << CPN_MATCHERTOT)
 #define CPN_CONTEXT    4
-#define CP_CONTEXT     (1 <<  CPN_CONTEXT)
+#define CP_CONTEXT     (1 << CPN_CONTEXT)
 #define CPN_PARAMETER  5
-#define CP_PARAMETER   (1 <<  CPN_PARAMETER)
+#define CP_PARAMETER   (1 << CPN_PARAMETER)
 #define CPN_REDIRECT   6
-#define CP_REDIRECT    (1 <<  CPN_REDIRECT)
+#define CP_REDIRECT    (1 << CPN_REDIRECT)
 #define CPN_QUOTE      7
-#define CP_QUOTE       (1 <<  CPN_QUOTE)
+#define CP_QUOTE       (1 << CPN_QUOTE)
 #define CPN_QUOTING    8
-#define CP_QUOTING     (1 <<  CPN_QUOTING)
+#define CP_QUOTING     (1 << CPN_QUOTING)
 #define CPN_RESTORE    9
-#define CP_RESTORE     (1 <<  CPN_RESTORE)
+#define CP_RESTORE     (1 << CPN_RESTORE)
 #define CPN_LIST       10
 #define CP_LIST        (1 << CPN_LIST)
 #define CPN_FORCELIST  11
@@ -398,6 +400,8 @@ struct chdata {
 #define CP_OLDINS      (1 << CPN_OLDINS)
 #define CPN_VARED      24
 #define CP_VARED       (1 << CPN_VARED)
+#define CPN_NNMATCHES  25
+#define CP_NNMATCHES   (1 << CPN_NNMATCHES)
 
-#define CP_KEYPARAMS   25
+#define CP_KEYPARAMS   26
 #define CP_ALLKEYS     ((unsigned int) 0xffffff)
diff --git a/Src/Zle/comp1.c b/Src/Zle/comp1.c
index ba8bcc868..c1e2bfb57 100644
--- a/Src/Zle/comp1.c
+++ b/Src/Zle/comp1.c
@@ -105,6 +105,7 @@ int incompfunc;
 /**/
 zlong compcurrent,
       compnmatches,
+      compnnmatches,
       compmatcher,
       compmatchertot,
       complistmax;
diff --git a/Src/Zle/comp1.export b/Src/Zle/comp1.export
index 9b738cc78..4b6dd92fd 100644
--- a/Src/Zle/comp1.export
+++ b/Src/Zle/comp1.export
@@ -27,6 +27,7 @@ compmatcher
 compmatcherstr
 compmatchertot
 compnmatches
+compnnmatches
 compoldlist
 compoldins
 compparameter
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index 694af8429..e9ff83387 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -2210,6 +2210,7 @@ static struct compparam compkparams[] = {
     { "old_list", PM_SCALAR, VAL(compoldlist), NULL, NULL },
     { "old_insert", PM_SCALAR, VAL(compoldins), NULL, NULL },
     { "vared", PM_SCALAR, VAL(compvared), NULL, NULL },
+    { "normal_nmatches", PM_INTEGER, VAL(compnnmatches), NULL, NULL },
     { NULL, 0, NULL, NULL, NULL }
 };
 
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index 0e7152866..4bf3fec0e 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -308,8 +308,8 @@ complistmatches(Hookdef dummy, Chdata dat)
     Cmatch *p, m;
     Cexpl *e;
     int nlines = 0, ncols, nlist = 0, longest = 1, pnl = 0, opl = 0;
-    int of = isset(LISTTYPES);
-    int mc, ml = 0, cc, hasm = 0, cl;
+    int of = isset(LISTTYPES), cf;
+    int mc, ml = 0, cc, hasm = 0, cl = -1;
     struct listcols col;
 
     if (minfo.asked == 2) {
@@ -318,15 +318,6 @@ complistmatches(Hookdef dummy, Chdata dat)
     }
     getcols(&col);
 
-    /* Set the cursor below the prompt. */
-    if (inselect)
-	clearflag = 0;
-    trashzle();
-    showinglist = listshown = 0;
-
-    clearflag = (isset(USEZLE) && !termflags &&
-		 complastprompt && *complastprompt);
-
     for (g = amatches; g; g = g->next) {
 	char **pp = g->ylist;
 	int nl = 0, l;
@@ -403,6 +394,19 @@ complistmatches(Hookdef dummy, Chdata dat)
 			nlines += 1 + ((1 + niceztrlen(m->str)) / columns);
 	}
     }
+    cf = (isset(USEZLE) && !termflags && complastprompt && *complastprompt);
+    if (!nlines || (mselect >= 0 && (!cf || (nlines + nlnct - 1) >= lines))) {
+	showinglist = listshown = 0;
+	noselect = 1;
+	return 1;
+    }
+    /* Set the cursor below the prompt. */
+    if (inselect)
+	clearflag = 0;
+    trashzle();
+    showinglist = listshown = 0;
+
+    clearflag = cf;
 
     /* Maybe we have to ask if the user wants to see the list. */
     if ((!minfo.cur || !minfo.asked) &&
@@ -654,20 +658,20 @@ complistmatches(Hookdef dummy, Chdata dat)
 	    pnl = 1;
 	g = g->next;
     }
-
     if (clearflag) {
 	/* Move the cursor up to the prompt, if always_last_prompt *
 	 * is set and all that...                                  */
 	if ((nlines += nlnct - 1) < lines) {
 	    tcmultout(TCUP, TCMULTUP, nlines);
 	    showinglist = -1;
-	    listshown = 1;
 	} else
 	    clearflag = 0, putc('\n', shout);
     } else
 	putc('\n', shout);
+    listshown = (clearflag ? 1 : -1);
     if (!hasm || nlines >= lines)
 	noselect = 1;
+
     return noselect;
 }
 
@@ -676,7 +680,7 @@ typedef struct menustack *Menustack;
 struct menustack {
     Menustack prev;
     char *line;
-    int cs;
+    int cs, acc;
     struct menuinfo info;
     Cmgroup amatches, pmatches, lmatches;
 };
@@ -742,19 +746,19 @@ domenuselect(Hookdef dummy, Chdata dat)
 	    s->amatches = amatches;
 	    s->pmatches = pmatches;
 	    s->lmatches = lmatches;
-	    menucmp = 0;
+	    s->acc = menuacc;
+	    menucmp = menuacc = 0;
 	    fixsuffix();
 	    validlist = 0;
 	    pmatches = NULL;
 	    invalidatelist();
 	    menucomplete(zlenoargs);
 	    if (dat->num < 2 || !minfo.cur || !*(minfo.cur)) {
-		noselect = 1;
-		clearlist = 1;
+		noselect = clearlist = listshown = 1;
 		zrefresh();
 		break;
 	    }
-	    clearlist = 1;
+	    clearlist = listshown = 1;
 	    mselect = (*(minfo.cur))->gnum;
 	    continue;
 	} else if (cmd == Th(z_acceptandhold) ||
@@ -767,6 +771,7 @@ domenuselect(Hookdef dummy, Chdata dat)
 	    s->cs = cs;
 	    memcpy(&(s->info), &minfo, sizeof(struct menuinfo));
 	    s->amatches = s->pmatches = s->lmatches = NULL;
+	    s->acc = menuacc;
 	    acceptlast();
 	    do_menucmp(0);
 	    mselect = (*(minfo.cur))->gnum;
@@ -782,6 +787,7 @@ domenuselect(Hookdef dummy, Chdata dat)
 	    spaceinline(l = strlen(u->line));
 	    strncpy((char *) line, u->line, l);
 	    cs = u->cs;
+	    menuacc = u->acc;
 	    memcpy(&minfo, &(u->info), sizeof(struct menuinfo));
 	    p = &(minfo.cur);
 	    if (u->pmatches && pmatches != u->pmatches) {
diff --git a/Src/Zle/zle.export b/Src/Zle/zle.export
index 8bc049e16..f63f45eb8 100644
--- a/Src/Zle/zle.export
+++ b/Src/Zle/zle.export
@@ -24,6 +24,7 @@ lastambig
 linkkeymap
 listshown
 lmatches
+menuacc
 menucmp
 menucomplete
 menucur
diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c
index dc4e27685..5ed846cd6 100644
--- a/Src/Zle/zle_params.c
+++ b/Src/Zle/zle_params.c
@@ -67,7 +67,7 @@ static struct zleparam {
         zleunsetfn, NULL },
     { "LASTWIDGET", PM_SCALAR | PM_READONLY, NULL, FN(get_lwidget),
         zleunsetfn, NULL },
-    { "keys", PM_ARRAY | PM_READONLY, NULL, FN(get_keys),
+    { "KEYS", PM_SCALAR | PM_READONLY, NULL, FN(get_keys),
         zleunsetfn, NULL },
     { "NUMERIC", PM_INTEGER | PM_UNSET, FN(set_numeric), FN(get_numeric),
         unset_numeric, NULL },
@@ -247,29 +247,10 @@ get_lwidget(Param pm)
 }
 
 /**/
-static char **
+static char *
 get_keys(Param pm)
 {
-    char **r, **q, *p, *k, c;
-
-    r = (char **) zhalloc((strlen(keybuf) + 1) * sizeof(char *));
-    for (q = r, p = keybuf; (c = *p); q++, p++) {
-	k = *q = (char *) zhalloc(5);
-	if (c & 0x80) {
-	    *k++ = 'M';
-	    *k++ = '-';
-	    c &= 0x7f;
-	}
-	if (c < 32 || c == 0x7f) {
-	    *k++ = '^';
-	    c ^= 64;
-	}
-	*k++ = c;
-	*k = '\0';
-    }
-    *q = NULL;
-
-    return r;
+    return keybuf;
 }
 
 /**/
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index 48e1071b8..1dbffc21c 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -53,7 +53,8 @@ int nlnct;
 /**/
 int showinglist;
 
-/* Non-zero if a completion list was displayed. */
+/* > 0 if a completion list is displayed below the prompt,
+ * < 0 if a list is displayed above the prompt. */
 
 /**/
 int listshown;
@@ -265,7 +266,7 @@ zrefresh(void)
     if (inlist)
 	return;
 
-    if (clearlist && listshown) {
+    if (clearlist && listshown > 0) {
 	if (tccan(TCCLEAREOD)) {
 	    int ovln = vln, ovcs = vcs;
 	    char *nb = nbuf[vln];
@@ -331,7 +332,8 @@ zrefresh(void)
                 tcout(TCCLEAREOD);
             else
                 cleareol = 1;   /* request: clear to end of line */
-	    listshown = 0;
+	    if (listshown > 0)
+		listshown = 0;
 	}
         if (t0 > -1)
             olnct = t0;
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 5461079cb..b678a5cad 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -106,10 +106,11 @@ static int insmnum, insgnum, insgroup, insspace;
 
 static int movetoend;
 
-/* != 0 if we are in the middle of a menu completion */
+/* != 0 if we are in the middle of a menu completion and number of matches
+* accepted with accept-and-menu-complete */
 
 /**/
-int menucmp;
+int menucmp, menuacc;
 
 /* Information about menucompletion. */
 
@@ -192,6 +193,10 @@ static char *qipre, *qisuf, autoq;
 static int lpl, lsl, rpl, rsl, fpl, fsl, lppl, lpsl;
 static int noreal;
 
+/* A parameter expansion prefix (like ${). */
+
+static char *parpre;
+
 /* This is either zero or equal to the special character the word we are *
  * trying to complete starts with (e.g. Tilde or Equals).                */
 
@@ -209,9 +214,9 @@ static char *qword;
 
 static Cmgroup mgroup;
 
-/* A match counter. */
+/* Match counters: all matches, normal matches (not alternate set). */
 
-static int mnum;
+static int mnum, nmnum;
 
 /* The match counter when unambig_data() was called. */
 
@@ -540,6 +545,8 @@ reversemenucomplete(char **args)
 void
 acceptlast(void)
 {
+    menuacc++;
+
     if (brbeg && *brbeg) {
 	int l;
 
@@ -553,10 +560,16 @@ acceptlast(void)
 	brbeg[l] = ',';
 	brbeg[l + 1] = '\0';
     } else {
+	int l;
+
 	cs = minfo.pos + minfo.len + minfo.insc;
 	iremovesuffix(' ', 1);
-
+	l = cs;
+	cs = minfo.pos + minfo.len - (*(minfo.cur))->qisl;
+	foredel(l - cs);
 	inststrlen(" ", 1, 1);
+	if (parpre)
+	    inststr(parpre);
 	minfo.insc = minfo.len = 0;
 	minfo.pos = cs;
 	minfo.we = 1;
@@ -694,6 +707,9 @@ check_param(char *s, int set, int test)
 {
     char *p;
 
+    zsfree(parpre);
+    parpre = NULL;
+
     if (!test)
 	ispar = parq = eparq = 0;
     /* Try to find a `$'. */
@@ -755,6 +771,8 @@ check_param(char *s, int set, int test)
 
 	/* Now make sure that the cursor is inside the name. */
 	if (offs <= e - s && offs >= b - s && n <= 0) {
+	    char sav;
+
 	    if (br) {
 		p = e;
 		while (*p == (test ? Dnull : '"'))
@@ -782,6 +800,12 @@ check_param(char *s, int set, int test)
 	    else
 		parq = eparq = 0;
 
+	    /* Save the prefix. */
+	    sav = *b;
+	    *b = '\0';
+	    untokenize(parpre = ztrdup(s));
+	    *b = sav;
+
 	    /* And adjust wb, we, and offs again. */
 	    offs -= b - s;
 	    wb = cs - offs;
@@ -1017,7 +1041,7 @@ docomplete(int lst)
 	    int ocs = cs, ne = noerrs;
 
 	    noerrs = 1;
-	    doexpansion(s, lst, olst, lincmd);
+	    ret = doexpansion(s, lst, olst, lincmd);
 	    lastambig = 0;
 	    noerrs = ne;
 
@@ -1043,8 +1067,8 @@ docomplete(int lst)
 			}
 		}
 		ret = docompletion(s, lst, lincmd);
-	    } else
-		ret = !strcmp(ol, (char *) line);
+	    } else if (ret)
+		clearlist = 1;
 	} else
 	    /* Just do completion. */
 	    ret = docompletion(s, lst, lincmd);
@@ -1064,7 +1088,7 @@ docomplete(int lst)
 	dat.num = nmatches;
 	dat.cur = NULL;
 	if (runhookdef(MENUSTARTHOOK, (void *) &dat))
-	    menucmp = 0;
+	    menucmp = menuacc = 0;
     }
     return ret;
 }
@@ -1736,9 +1760,10 @@ get_comp_string(void)
 /* Expand the current word. */
 
 /**/
-static void
+static int
 doexpansion(char *s, int lst, int olst, int explincmd)
 {
+    int ret = 1;
     LinkList vl;
     char *ss;
 
@@ -1775,7 +1800,7 @@ doexpansion(char *s, int lst, int olst, int explincmd)
 	}
 	if (lst == COMP_LIST_EXPAND) {
 	    /* Only the list of expansions was requested. */
-	    listlist(vl);
+	    ret = listlist(vl);
 	    showinglist = 0;
 	    goto end;
 	}
@@ -1783,6 +1808,7 @@ doexpansion(char *s, int lst, int olst, int explincmd)
 	cs = wb;
 	foredel(we - wb);
 	while ((ss = (char *)ugetnode(vl))) {
+	    ret = 0;
 	    untokenize(ss);
 	    ss = quotename(ss, NULL);
 	    inststr(ss);
@@ -1801,6 +1827,8 @@ doexpansion(char *s, int lst, int olst, int explincmd)
       end:
 	popheap();
     } LASTALLOC;
+
+    return ret;
 }
 
 /* This is called from the lexer to give us word positions. */
@@ -3547,6 +3575,8 @@ add_match_data(int alt, char *str, Cline line,
     ai->line = join_clines(ai->line, line);
 
     mnum++;
+    if (!alt)
+	nmnum++;
     ai->count++;
     
     /* Allocate and fill the match structure. */
@@ -3569,6 +3599,8 @@ add_match_data(int alt, char *str, Cline line,
     cm->flags = flags;
     cm->brpl = bpl;
     cm->brsl = bsl;
+    cm->qipl = qipl;
+    cm->qisl = qisl;
     cm->autoq = (autoq ? autoq : (inbackt ? '`' : '\0'));
     cm->rems = cm->remf = NULL;
     addlinknode((alt ? fmatches : matches), cm);
@@ -3896,6 +3928,7 @@ addmatches(Cadata dat, char **argv)
 		}
 	    }
 	    compnmatches = mnum;
+	    compnnmatches = nmnum;
 	    if (dat->exp)
 		addexpl();
 	    if (dat->apar)
@@ -4380,10 +4413,14 @@ docompletion(char *s, int lst, int incmd)
 	}
 	if (comppatmatch && *comppatmatch && comppatmatch != opm)
 	    haspattern = 1;
-	if (!useline && uselist)
+	if (!useline && uselist) {
 	    /* All this and the guy only wants to see the list, sigh. */
+	    cs = 0;
+	    foredel(ll);
+	    inststr(origline);
+	    cs = origcs;
 	    showinglist = -2;
-	else if (useline) {
+	} else if (useline) {
 	    /* We have matches. */
 	    if (nmatches > 1) {
 		/* There is more than one match. */
@@ -4399,9 +4436,13 @@ docompletion(char *s, int lst, int incmd)
 		do_single(m->matches[0]);
 		invalidatelist();
 	    }
-	} else
+	} else {
 	    invalidatelist();
-
+	    cs = 0;
+	    foredel(ll);
+	    inststr(origline);
+	    cs = origcs;
+	}
 	/* Print the explanation strings if needed. */
 	if (!showinglist && validlist && usemenu != 2 && nmatches != 1) {
 	    Cmgroup g = amatches;
@@ -4582,22 +4623,22 @@ callcompfunc(char *s, char *fn)
 	zsfree(compprefix);
 	zsfree(compsuffix);
 	if (unset(COMPLETEINWORD)) {
-	    /* Maybe we'll have to do quoting here some time. */
-	    tmp = dupstring(s);
+	    tmp = quotename(s, NULL);
 	    untokenize(tmp);
 	    compprefix = ztrdup(tmp);
 	    compsuffix = ztrdup("");
 	} else {
 	    char *ss, sav;
 	    
-	    tmp = dupstring(s);
-	    ss = tmp + offs;
+	    ss = s + offs;
 
 	    sav = *ss;
 	    *ss = '\0';
+	    tmp = quotename(s, NULL);
 	    untokenize(tmp);
 	    compprefix = ztrdup(tmp);
 	    *ss = sav;
+	    ss = quotename(ss, NULL);
 	    untokenize(ss);
 	    compsuffix = ztrdup(ss);
 	}
@@ -4611,6 +4652,7 @@ callcompfunc(char *s, char *fn)
 	compqisuffix = ztrdup(qisuf ? qisuf : "");
 	compcurrent = (usea ? (clwpos + 1 - aadd) : 0);
 	compnmatches = mnum;
+	compnnmatches = nmnum;
 
 	zsfree(complist);
 	switch (uselist) {
@@ -4825,13 +4867,13 @@ makecomplist(char *s, int incmd, int lst)
 	if (!validlist)
 	    lastambig = 0;
 	amatches = NULL;
-	mnum = 0;
+	mnum = nmnum = 0;
 	unambig_mnum = -1;
 	isuf = NULL;
 	insmnum = insgnum = 1;
 	insgroup = oldlist = oldins = 0;
 	begcmgroup("default", 0);
-	menucmp = 0;
+	menucmp = menuacc = 0;
 
 	ccused = newlinklist();
 	ccstack = newlinklist();
@@ -4984,6 +5026,8 @@ sep_comp_string(char *ss, char *s, int noffs, int rec)
     memcpy(tmp + sl + 1, s, noffs);
     tmp[(scs = cs = sl + 1 + noffs)] = 'x';
     strcpy(tmp + sl + 2 + noffs, s + noffs);
+    if (incompfunc)
+	tmp = rembslash(tmp);
     inpush(dupstrspace(tmp), 0, NULL);
     line = (unsigned char *) tmp;
     ll = tl - 1;
@@ -5013,6 +5057,7 @@ sep_comp_string(char *ss, char *s, int noffs, int rec)
 	    p = NULL;
 	if (!got && !zleparse) {
 	    DPUTS(!p, "no current word in substr");
+	    got = 1;
 	    cur = i;
 	    swb = wb - 1;
 	    swe = we - 1;
@@ -5077,7 +5122,7 @@ sep_comp_string(char *ss, char *s, int noffs, int rec)
     }
     sav = s[(i = swb - sl - 1)];
     s[i] = '\0';
-    qp = tricat(qipre, s, "");
+    qp = tricat(qipre, (incompfunc ? rembslash(s) : s), "");
     s[i] = sav;
     if (swe < swb)
 	swe = swb;
@@ -5085,7 +5130,7 @@ sep_comp_string(char *ss, char *s, int noffs, int rec)
     sl = strlen(s);
     if (swe > sl)
 	swe = sl, ns[swe - swb + 1] = '\0';
-    qs = tricat(s + swe, qisuf, "");
+    qs = tricat((incompfunc ? rembslash(s + swe) : s + swe), qisuf, "");
     sl = strlen(ns);
     if (soffs > sl)
 	soffs = sl;
@@ -5172,7 +5217,7 @@ sep_comp_string(char *ss, char *s, int noffs, int rec)
 	compisuffix = ztrdup("");
 	zsfree(compqiprefix);
 	zsfree(compqisuffix);
-	if (instring) {
+	if (ois) {
 	    compqiprefix = qp;
 	    compqisuffix = qs;
 	} else {
@@ -5188,6 +5233,7 @@ sep_comp_string(char *ss, char *s, int noffs, int rec)
 	    p = compwords[i] = (char *) getdata(n);
 	    untokenize(p);
 	}
+	compcurrent = cur + 1;
 	compwords[i] = NULL;
     }
     autoq = oaq;
@@ -5242,6 +5288,7 @@ makecomplistcall(Compctl cc)
 	    inbackt = oib;
 	    autoq = oaq;
 	    compnmatches = mnum;
+	    compnnmatches = nmnum;
 	} LASTALLOC;
     } SWITCHBACKHEAPS;
 
@@ -5317,6 +5364,7 @@ makecomplistctl(int flags)
 	    autoq = oaq;
 	    offs = ooffs;
 	    compnmatches = mnum;
+	    compnnmatches = nmnum;
 	    zsfree(cmdstr);
 	    freearray(clwords);
 	    cmdstr = os;
@@ -5979,7 +6027,10 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	    char save = line[cs];
 
 	    line[cs] = 0;
-	    lppre = dupstring((char *) (line + wb));
+	    lppre = dupstring((char *) line + wb +
+			      (qipre && *qipre ?
+			       (strlen(qipre) -
+				(*qipre == '\'' || *qipre == '\"')) : 0));
 	    line[cs] = save;
 	    if (brbeg && *brbeg)
 		strcpy(lppre + qbrpl, lppre + qbrpl + strlen(brbeg));
@@ -5998,11 +6049,17 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	    lppl = 0;
 	}
 	if (cs != we) {
-	    char save = line[we];
+	    int end = we;
+	    char save = line[end];
+
+	    if (qisuf && *qisuf) {
+		int ql = strlen(qisuf);
 
-	    line[we] = 0;
+		end -= ql - (qisuf[ql-1] == '\'' || qisuf[ql-1] == '"');
+	    }
+	    line[end] = 0;
 	    lpsuf = dupstring((char *) (line + cs));
-	    line[we] = save;
+	    line[end] = save;
 	    if (brend && *brend) {
 		char *p = lpsuf + qbrsl - (cs - wb);
 
@@ -6640,7 +6697,8 @@ invalidatelist(void)
 	listmatches();
     if (validlist)
 	freematches();
-    lastambig = menucmp = validlist = showinglist = fromcomp = 0;
+    lastambig = menucmp = menuacc = validlist = showinglist =
+	fromcomp = listshown = 0;
     minfo.cur = NULL;
     minfo.asked = 0;
     compwidget = NULL;
@@ -6917,6 +6975,8 @@ dupmatch(Cmatch m)
     r->rems = ztrdup(m->rems);
     r->remf = ztrdup(m->remf);
     r->autoq = m->autoq;
+    r->qipl = m->qipl;
+    r->qisl = m->qisl;
 
     return r;
 }
@@ -7346,7 +7406,9 @@ instmatch(Cmatch m, int *scs)
 
     /* Ignored prefix. */
     if (m->ipre) {
-	inststrlen(m->ipre, 1, (l = strlen(m->ipre)));
+	char *p = m->ipre + (menuacc ? m->qipl : 0);
+
+	inststrlen(p, 1, (l = strlen(p)));
 	r += l;
     }
     /* -P prefix. */
@@ -7413,7 +7475,8 @@ static int
 do_ambiguous(void)
 {
     int ret = 0;
-    menucmp = 0;
+
+    menucmp = menuacc = 0;
 
     /* If we have to insert the first match, call do_single().  This is *
      * how REC_EXACT takes effect.  We effectively turn the ambiguous   *
@@ -7501,11 +7564,13 @@ do_ambiguous(void)
      * if it is needed.                                                     */
     if (isset(LISTBEEP))
 	ret = 1;
-    if (uselist && (usemenu != 2 || (!showinglist && !oldlist)) &&
+
+    if (uselist && (usemenu != 2 || (!listshown && !oldlist)) &&
 	((!showinglist && (!listshown || !oldlist)) ||
 	 (usemenu == 3 && !oldlist)) &&
 	(smatches >= 2 || (compforcelist && *compforcelist)))
 	showinglist = -2;
+
     return ret;
 }
 
@@ -7649,7 +7714,7 @@ do_single(Cmatch m)
 	    }
 	}
 	if (!minfo.insc)
-	    cs = minfo.pos + minfo.len;
+	    cs = minfo.pos + minfo.len - m->qisl;
     }
     /* If completing in a brace expansion... */
     if (brbeg) {
@@ -7688,8 +7753,11 @@ do_single(Cmatch m)
     if (minfo.we && m->ripre && isset(AUTOPARAMKEYS))
 	makeparamsuffix(((m->flags & CMF_PARBR) ? 1 : 0), minfo.insc - parq);
 
-    if ((menucmp && !minfo.we) || !movetoend)
+    if ((menucmp && !minfo.we) || !movetoend) {
 	cs = minfo.end;
+	if (cs + m->qisl == lastend)
+	    cs += minfo.insc;
+    }
     {
 	Cmatch *om = minfo.cur;
 	struct chdata dat;
@@ -7732,6 +7800,7 @@ do_ambig_menu(void)
 
     if (usemenu != 3) {
 	menucmp = 1;
+	menuacc = 0;
 	minfo.cur = NULL;
     } else {
 	if (oldlist) {
@@ -7931,13 +8000,6 @@ ilistmatches(Hookdef dummy, Chdata dat)
     int nlines = 0, ncols, nlist = 0, longest = 1, pnl = 0;
     int of = isset(LISTTYPES), opl = 0;
 
-    /* Set the cursor below the prompt. */
-    trashzle();
-    showinglist = listshown = 0;
-
-    clearflag = (isset(USEZLE) && !termflags &&
-		 complastprompt && *complastprompt);
-
     for (g = amatches; g; g = g->next) {
 	char **pp = g->ylist;
 	int nl = 0, l;
@@ -8013,6 +8075,16 @@ ilistmatches(Hookdef dummy, Chdata dat)
 			nlines += 1 + ((1 + niceztrlen(m->str)) / columns);
 	}
     }
+    if (!nlines) {
+	showinglist = listshown = 0;
+	return 1;
+    }
+    /* Set the cursor below the prompt. */
+    trashzle();
+    showinglist = listshown = 0;
+
+    clearflag = (isset(USEZLE) && !termflags &&
+		 complastprompt && *complastprompt);
 
     /* Maybe we have to ask if the user wants to see the list. */
     if ((!minfo.cur || !minfo.asked) &&
@@ -8152,25 +8224,25 @@ ilistmatches(Hookdef dummy, Chdata dat)
 	    pnl = 1;
 	g = g->next;
     }
-
     if (clearflag) {
 	/* Move the cursor up to the prompt, if always_last_prompt *
 	 * is set and all that...                                  */
 	if ((nlines += nlnct - 1) < lines) {
 	    tcmultout(TCUP, TCMULTUP, nlines);
 	    showinglist = -1;
-	    listshown = 1;
 	} else
 	    clearflag = 0, putc('\n', shout);
     } else
 	putc('\n', shout);
+    listshown = (clearflag ? 1 : -1);
+
     return 0;
 }
 
 /* This is used to print expansions. */
 
 /**/
-void
+int
 listlist(LinkList l)
 {
     struct cmgroup dg;
@@ -8193,6 +8265,8 @@ listlist(LinkList l)
     validlist = vl;
     smatches = sm;
     complastprompt = oclp;
+
+    return !dg.lcount;
 }
 
 /* Expand the history references. */
diff --git a/Src/builtin.c b/Src/builtin.c
index 0e9baf9be..5c6b24601 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -658,6 +658,9 @@ set_pwd_env(void)
     }
 }
 
+/* set if we are resolving links to their true paths */
+static int chasinglinks;
+
 /* The main pwd changing function.  The real work is done by other     *
  * functions.  cd_get_dest() does the initial argument processing;     *
  * cd_do_chdir() actually changes directory, if possible; cd_new_pwd() *
@@ -670,7 +673,6 @@ bin_cd(char *nam, char **argv, char *ops, int func)
 {
     LinkNode dir;
     struct stat st1, st2;
-    int chaselinks;
 
     if (isset(RESTRICTED)) {
 	zwarnnam(nam, "restricted", NULL, 0);
@@ -694,7 +696,7 @@ bin_cd(char *nam, char **argv, char *ops, int func)
 	for (s = *argv; *++s; ops[STOUC(*s)] = 1);
     }
   brk:
-    chaselinks = ops['P'] || (isset(CHASELINKS) && !ops['L']);
+    chasinglinks = ops['P'] || (isset(CHASELINKS) && !ops['L']);
     PERMALLOC {
 	pushnode(dirstack, ztrdup(pwd));
 	if (!(dir = cd_get_dest(nam, argv, ops, func))) {
@@ -702,7 +704,7 @@ bin_cd(char *nam, char **argv, char *ops, int func)
 	    LASTALLOC_RETURN 1;
 	}
     } LASTALLOC;
-    cd_new_pwd(func, dir, chaselinks);
+    cd_new_pwd(func, dir);
 
     if (stat(unmeta(pwd), &st1) < 0) {
 	zsfree(pwd);
@@ -710,7 +712,7 @@ bin_cd(char *nam, char **argv, char *ops, int func)
     } else if (stat(".", &st2) < 0)
 	chdir(unmeta(pwd));
     else if (st1.st_ino != st2.st_ino || st1.st_dev != st2.st_dev) {
-	if (chaselinks) {
+	if (chasinglinks) {
 	    zsfree(pwd);
 	    pwd = metafy(zgetcwd(), -1, META_DUP);
 	} else {
@@ -915,40 +917,49 @@ static char *
 cd_try_chdir(char *pfix, char *dest, int hard)
 {
     char *buf;
+    int dlen, dochaselinks = 0;
 
     /* handle directory prefix */
     if (pfix && *pfix) {
 	if (*pfix == '/')
 	    buf = tricat(pfix, "/", dest);
 	else {
-	    int pwl = strlen(pwd);
 	    int pfl = strlen(pfix);
+	    dlen = strlen(pwd);
 
-	    buf = zalloc(pwl + pfl + strlen(dest) + 3);
+	    buf = zalloc(dlen + pfl + strlen(dest) + 3);
 	    strcpy(buf, pwd);
-	    buf[pwl] = '/';
-	    strcpy(buf + pwl + 1, pfix);
-	    buf[pwl + 1 + pfl] = '/';
-	    strcpy(buf + pwl + pfl + 2, dest);
+	    buf[dlen] = '/';
+	    strcpy(buf + dlen + 1, pfix);
+	    buf[dlen + 1 + pfl] = '/';
+	    strcpy(buf + dlen + pfl + 2, dest);
 	}
     } else if (*dest == '/')
 	buf = ztrdup(dest);
     else {
-	int pwl = strlen(pwd);
+	dlen = strlen(pwd);
 
-	buf = zalloc(pwl + strlen(dest) + 2);
+	buf = zalloc(dlen + strlen(dest) + 2);
 	strcpy(buf, pwd);
-	buf[pwl] = '/';
-	strcpy(buf + pwl + 1, dest);
+	buf[dlen] = '/';
+	strcpy(buf + dlen + 1, dest);
     }
 
-    /* Normalise path.  See the definition of fixdir() for what this means. */
-    fixdir(buf);
+    /* Normalise path.  See the definition of fixdir() for what this means.
+     * We do not do this if we are chasing links.
+     */
+    if (!chasinglinks)
+	dochaselinks = fixdir(buf);
+    else
+	unmetafy(buf, &dlen);
 
     if (lchdir(buf, NULL, hard)) {
-	zsfree(buf);
+	free(buf);
 	return NULL;
     }
+    /* the chdir succeeded, so decide if we should force links to be chased */
+    if (dochaselinks)
+	chasinglinks = 1;
     return metafy(buf, -1, META_NOALLOC);
 }
 
@@ -956,7 +967,7 @@ cd_try_chdir(char *pfix, char *dest, int hard)
 
 /**/
 static void
-cd_new_pwd(int func, LinkNode dir, int chaselinks)
+cd_new_pwd(int func, LinkNode dir)
 {
     List l;
     char *new_pwd, *s;
@@ -972,7 +983,7 @@ cd_new_pwd(int func, LinkNode dir, int chaselinks)
     } else if (func == BIN_CD && unset(AUTOPUSHD))
 	zsfree(getlinknode(dirstack));
 
-    if (chaselinks) {
+    if (chasinglinks) {
 	s = new_pwd;
 	new_pwd = findpwd(s);
 	zsfree(s);
@@ -1039,17 +1050,20 @@ printdirstack(void)
 }
 
 /* Normalise a path.  Segments consisting of ., and foo/.. *
- * combinations, are removed and the path is unmetafied.   */
+ * combinations, are removed and the path is unmetafied.
+ * Returns 1 if we found a ../ path which should force links to
+ * be chased, 0 otherwise.
+ */
 
 /**/
-static void
+int
 fixdir(char *src)
 {
-    char *dest = src;
-    char *d0 = dest;
-#ifdef __CYGWIN__
+    char *dest = src, *d0 = dest;
+#ifdef __CYGWIN
     char *s0 = src;
 #endif
+    int ret = 0;
 
 /*** if have RFS superroot directory ***/
 #ifdef HAVE_SUPERROOT
@@ -1081,19 +1095,40 @@ fixdir(char *src)
 	    while (dest > d0 + 1 && dest[-1] == '/')
 		dest--;
 	    *dest = '\0';
-	    return;
+	    return ret;
 	}
 	if (src[0] == '.' && src[1] == '.' &&
-	  (src[2] == '\0' || src[2] == '/')) {
-	    if (dest > d0 + 1) {
-		/* remove a foo/.. combination */
-		for (dest--; dest > d0 + 1 && dest[-1] != '/'; dest--);
-		if (dest[-1] != '/')
-		    dest--;
-	    }
-	    src++;
-	    while (*++src == '/');
-	} else if (src[0] == '.' && (src[1] == '/' || src[1] == '\0')) {
+	    (src[2] == '\0' || src[2] == '/')) {
+	    if (isset(CHASEDOTS)) {
+		ret = 1;
+		/* and treat as normal path segment */
+	    } else {
+		if (dest > d0 + 1) {
+		    /*
+		     * remove a foo/.. combination:
+		     * first check foo exists, else return.
+		     */
+		    struct stat st;
+		    *dest = '\0';
+		    if (stat(d0, &st) < 0 || !S_ISDIR(st.st_mode)) {
+			char *ptrd, *ptrs;
+			if (dest == src)
+			    *dest = '.';
+			for (ptrs = src, ptrd = dest; *ptrs; ptrs++, ptrd++)
+			    *ptrd = (*ptrs == Meta) ? (*++ptrs ^ 32) : *ptrs;
+			*ptrd = '\0';
+			return 1;
+		    }
+		    for (dest--; dest > d0 + 1 && dest[-1] != '/'; dest--);
+		    if (dest[-1] != '/')
+			dest--;
+		}
+		src++;
+		while (*++src == '/');
+		continue;
+	    }
+	}
+	if (src[0] == '.' && (src[1] == '/' || src[1] == '\0')) {
 	    /* skip a . section */
 	    while (*++src == '/');
 	} else {
@@ -3249,12 +3284,11 @@ bin_read(char *name, char **args, char *ops, int func)
 	    nchars = 1;
 	args++;
     }
-
-    firstarg = *args;
-    if (*args && **args == '?')
-	args++;
-    /* default result parameter */
+    /* This `*args++ : *args' looks a bit weird, but it works around a bug
+     * in gcc-2.8.1 under DU 4.0. */
+    firstarg = (*args && **args == '?' ? *args++ : *args);
     reply = *args ? *args++ : ops['A'] ? "reply" : "REPLY";
+
     if (ops['A'] && *args) {
 	zwarnnam(name, "only one array argument allowed", NULL, 0);
 	return 1;
diff --git a/Src/exec.c b/Src/exec.c
index 9c7a1ceb5..dc281675f 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -3144,9 +3144,24 @@ static int
 cancd2(char *s)
 {
     struct stat buf;
-    char *us = unmeta(s);
+    char *us, *us2 = NULL;
 
+    /*
+     * If CHASEDOTS and CHASELINKS are not set, we want to rationalize the
+     * path by removing foo/.. combinations in the logical rather than
+     * the physical path.  If either is set, we test the physical path.
+     */
+    if (!isset(CHASEDOTS) && !isset(CHASELINKS)) {
+	if (*s != '/')
+	    us = tricat(pwd[1] ? pwd : "", "/", s);
+	else
+	    us = ztrdup(s);
+	fixdir(us2 = us);
+    } else
+	us = unmeta(s);
     return !(access(us, X_OK) || stat(us, &buf) || !S_ISDIR(buf.st_mode));
+    if (us2)
+	free(us2);
 }
 
 /**/
diff --git a/Src/glob.c b/Src/glob.c
index ea4980b8b..cf22ef923 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -2724,6 +2724,9 @@ charmatch(Comp c, char *x, char *y)
      * Here we bypass tulower() and tuupper() for speed.
      */
     int xi = (STOUC(UNMETA(x)) & 0xff), yi = (STOUC(UNMETA(y)) & 0xff);
+    /* A NULL is a real null, since a \000 would be metafied. */
+    if (!*x || !*y)
+	return 0;
     return xi == yi ||
 	(((c->stat & C_IGNCASE) ?
 	  ((isupper(xi) ? tolower(xi) : xi) ==
@@ -2926,7 +2929,10 @@ rangematch(char **patptr, int ch, int rchar)
      * and optional ^ have already been skipped.          */
 
     char *pat = *patptr;
-#ifdef HAVE_STRCOLL
+    /* We don't use strcoll() for ranges, since it can have side
+     * effects.  It's less necessary now we have [:posix:] ranges.
+     */
+#if 0
     char l_buf[2], r_buf[2], ch_buf[2];
 
     ch_buf[0] = ch;
@@ -2944,7 +2950,7 @@ rangematch(char **patptr, int ch, int rchar)
 		break;
 	} else if (*pat == '-' && pat[-1] != rchar &&
 		   pat[1] != Outbrack) {
-#ifdef HAVE_STRCOLL
+#if 0
 	    l_buf[0] = PPAT(-1);
 	    r_buf[0] = PAT(1);
 	    if (strcoll(l_buf, ch_buf) <= 0 &&
diff --git a/Src/lex.c b/Src/lex.c
index 33b6598b9..069f9b39b 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -241,6 +241,7 @@ lexsave(void)
     cmdsp = 0;
     inredir = 0;
     hdocs = NULL;
+    histactive = 0;
 
     ls->next = lstack;
     lstack = ls;
diff --git a/Src/options.c b/Src/options.c
index 2eb73690e..0207cd232 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -91,6 +91,7 @@ static struct optname optns[] = {
 {NULL, "braceccl",	      0,			 BRACECCL},
 {NULL, "bsdecho",	      OPT_EMULATE|OPT_SH,	 BSDECHO},
 {NULL, "cdablevars",	      0,			 CDABLEVARS},
+{NULL, "chasedots",	      0,			 CHASEDOTS},
 {NULL, "chaselinks",	      0,			 CHASELINKS},
 {NULL, "clobber",	      OPT_ALL,			 CLOBBER},
 {NULL, "completealiases",     0,			 COMPLETEALIASES},
diff --git a/Src/parse.c b/Src/parse.c
index 626ffc982..658a66660 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -72,7 +72,13 @@ struct list dummy_list;
 
 #define YYERROR  { tok = LEXERR; return NULL; }
 #define YYERRORV { tok = LEXERR; return; }
-#define COND_ERROR(X,Y) do{herrflush();zerr(X,Y,0);YYERROR}while(0)
+#define COND_ERROR(X,Y) do { \
+  zwarn(X,Y,0); \
+  herrflush(); \
+  if (noerrs != 2) \
+    errflag = 1; \
+  YYERROR \
+} while(0)
 
 #define make_list()     allocnode(N_LIST)
 #define make_sublist()  allocnode(N_SUBLIST)
@@ -140,11 +146,13 @@ par_event(void)
     }
     if (!l) {
 	if (errflag) {
-	    yyerror();
+	    yyerror(0);
 	    return NULL;
 	}
+	yyerror(1);
 	herrflush();
-	yyerror();
+	if (noerrs != 2)
+	    errflag = 1;
 	return NULL;
     } else {
 	l->right = par_event();
@@ -163,7 +171,7 @@ parse_list(void)
     yylex();
     ret = par_list();
     if (tok == LEXERR) {
-	yyerror();
+	yyerror(0);
 	return NULL;
     }
     return ret;
@@ -1480,7 +1488,7 @@ par_cond_multi(char *a, LinkList l)
 
 /**/
 static void
-yyerror(void)
+yyerror(int noerr)
 {
     int t0;
 
@@ -1488,9 +1496,11 @@ yyerror(void)
 	if (!yytext || !yytext[t0] || yytext[t0] == '\n')
 	    break;
     if (t0 == 20)
-	zerr("parse error near `%l...'", yytext, 20);
+	zwarn("parse error near `%l...'", yytext, 20);
     else if (t0)
-	zerr("parse error near `%l'", yytext, t0);
+	zwarn("parse error near `%l'", yytext, t0);
     else
-	zerr("parse error", NULL, 0);
+	zwarn("parse error", NULL, 0);
+    if (!noerr && noerrs != 2)
+	errflag = 1;
 }
diff --git a/Src/utils.c b/Src/utils.c
index d82f62694..f86c18b16 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -30,23 +30,12 @@
 #include "zsh.mdh"
 #include "utils.pro"
 
-/* Print an error */
-
-/**/
-void
-zwarnnam(const char *cmd, const char *fmt, const char *str, int num)
-{
-    int waserr;
-
-    waserr = errflag;
-    zerrnam(cmd, fmt, str, num);
-    errflag = waserr;
-}
-
 /* name of script being sourced */
 
 /**/
 char *scriptname;
+
+/* Print an error */
  
 /**/
 void
@@ -57,7 +46,27 @@ zerr(const char *fmt, const char *str, int num)
 	    errflag = 1;
 	return;
     }
+    zwarn(fmt, str, num);
     errflag = 1;
+}
+
+/**/
+void
+zerrnam(const char *cmd, const char *fmt, const char *str, int num)
+{
+    if (errflag || noerrs)
+	return;
+
+    zwarnnam(cmd, fmt, str, num);
+    errflag = 1;
+}
+
+/**/
+void
+zwarn(const char *fmt, const char *str, int num)
+{
+    if (errflag || noerrs)
+	return;
     trashzle();
     /*
      * scriptname is set when sourcing scripts, so that we get the
@@ -68,25 +77,29 @@ zerr(const char *fmt, const char *str, int num)
     nicezputs((isset(SHINSTDIN) && !locallevel) ? "zsh" :
 	      scriptname ? scriptname : argzero, stderr);
     fputs(": ", stderr);
-    zerrnam(NULL, fmt, str, num);
+    zerrmsg(fmt, str, num);
 }
 
 /**/
 void
-zerrnam(const char *cmd, const char *fmt, const char *str, int num)
+zwarnnam(const char *cmd, const char *fmt, const char *str, int num)
 {
-    if (cmd) {
-	if (errflag || noerrs)
-	    return;
-	errflag = 1;
-	trashzle();
-	if (unset(SHINSTDIN) || locallevel) {
-	    nicezputs(scriptname ? scriptname : argzero, stderr);
-	    fputs(": ", stderr);
-	}
-	nicezputs(cmd, stderr);
+    if (errflag || noerrs)
+	return;
+    trashzle();
+    if (unset(SHINSTDIN) || locallevel) {
+	nicezputs(scriptname ? scriptname : argzero, stderr);
 	fputs(": ", stderr);
     }
+    nicezputs(cmd, stderr);
+    fputs(": ", stderr);
+    zerrmsg(fmt, str, num);
+}
+
+/**/
+void
+zerrmsg(const char *fmt, const char *str, int num)
+{
     while (*fmt)
 	if (*fmt == '%') {
 	    fmt++;
@@ -302,7 +315,7 @@ slashsplit(char *s)
 
 /**/
 static int
-xsymlinks(char *s, int flag)
+xsymlinks(char *s)
 {
     char **pp, **opp;
     char xbuf2[PATH_MAX*2], xbuf3[PATH_MAX*2];
@@ -325,15 +338,9 @@ xsymlinks(char *s, int flag)
 	    *p = '\0';
 	    continue;
 	}
-	if (unset(CHASELINKS)) {
-	    strcat(xbuf, "/");
-	    strcat(xbuf, *pp);
-	    zsfree(*pp);
-	    continue;
-	}
 	sprintf(xbuf2, "%s/%s", xbuf, *pp);
 	t0 = readlink(unmeta(xbuf2), xbuf3, PATH_MAX);
-	if (t0 == -1 || !flag) {
+	if (t0 == -1) {
 	    strcat(xbuf, "/");
 	    strcat(xbuf, *pp);
 	    zsfree(*pp);
@@ -342,9 +349,9 @@ xsymlinks(char *s, int flag)
 	    metafy(xbuf3, t0, META_NOALLOC);
 	    if (*xbuf3 == '/') {
 		strcpy(xbuf, "");
-		xsymlinks(xbuf3 + 1, flag);
+		xsymlinks(xbuf3 + 1);
 	    } else
-		xsymlinks(xbuf3, flag);
+		xsymlinks(xbuf3);
 	    zsfree(*pp);
 	}
     }
@@ -352,19 +359,19 @@ xsymlinks(char *s, int flag)
     return ret;
 }
 
-/* expand symlinks in s, and remove other weird things */
+/*
+ * expand symlinks in s, and remove other weird things:
+ * note that this always expands symlinks.
+ */
 
 /**/
 char *
 xsymlink(char *s)
 {
-    if (unset(CHASELINKS))
-	return ztrdup(s);
     if (*s != '/')
 	return NULL;
     *xbuf = '\0';
-    if (!xsymlinks(s + 1, 1))
-	return ztrdup(s);
+    xsymlinks(s + 1);
     if (!*xbuf)
 	return ztrdup("/");
     return ztrdup(xbuf);
@@ -374,15 +381,10 @@ xsymlink(char *s)
 void
 print_if_link(char *s)
 {
-    int chase;
-
     if (*s == '/') {
-	chase = opts[CHASELINKS];
-	opts[CHASELINKS] = 1;
 	*xbuf = '\0';
-	if (xsymlinks(s + 1, 1))
+	if (xsymlinks(s + 1))
 	    printf(" -> "), zputs(*xbuf ? xbuf : "/", stdout);
-	opts[CHASELINKS] = chase;
     }
 }
 
@@ -573,7 +575,8 @@ getnameddir(char *name)
 	/* Retrieve an entry from the password table/database for this user. */
 	struct passwd *pw;
 	if ((pw = getpwnam(name))) {
-	    char *dir = xsymlink(pw->pw_dir);
+	    char *dir = isset(CHASELINKS) ? xsymlink(pw->pw_dir)
+		: ztrdup(pw->pw_dir);
 	    adduserdir(name, dir, ND_USERNAME, 1);
 	    str = dupstring(dir);
 	    zsfree(dir);
@@ -3202,7 +3205,7 @@ getkeystring(char *s, int *len, int fromwhere, int *misc)
     int meta = 0, control = 0;
 
     if (fromwhere == 6)
-	t = tmp;
+	t = buf = tmp;
     else if (fromwhere != 4)
 	t = buf = zhalloc(strlen(s) + 1);
     else {
diff --git a/Src/zsh.h b/Src/zsh.h
index 2070e9b3f..d2b64b9bb 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -1171,6 +1171,7 @@ enum {
     BRACECCL,
     BSDECHO,
     CDABLEVARS,
+    CHASEDOTS,
     CHASELINKS,
     CLOBBER,
     COMPLETEALIASES,
diff --git a/configure.in b/configure.in
index a5dbc6413..1452d8c2d 100644
--- a/configure.in
+++ b/configure.in
@@ -100,7 +100,7 @@ fi])
 dnl Do you want large file support, if available?
 undefine([lfs])dnl
 AC_ARG_ENABLE(lfs,
-[  --enable-lfs               turn on support for large files],
+[  --disable-lfs              turn off support for large files],
 [lfs="$enableval"], [lfs=yes])
 
 dnl Pathnames for global zsh scripts
@@ -183,7 +183,7 @@ AC_SUBST(zlogout)dnl
 dnl Do you want dynamically loaded binary modules.
 undefine([dynamic])dnl
 AC_ARG_ENABLE(dynamic,
-[  --enable-dynamic           allow dynamically loaded binary modules],
+[  --disable-dynamic          turn off dynamically loaded binary modules],
 [dynamic="$enableval"], [dynamic=yes])
 
 dnl Do you want to compile as K&R C.
@@ -599,7 +599,7 @@ main() { return sizeof(ino_t) < 8; }
   if test $lfs != no -o $zsh_cv_off_t_is_64_bit = yes \
   -o $zsh_cv_ino_t_is_64_bit = yes; then
     AC_CACHE_CHECK(if compiler has a 64 bit type, zsh_cv_64_bit_type,
-    [if test $lfs != xyes -a $lfs != xno; then
+    [if test $lfs != yes -a $lfs != no; then
       zsh_64_BIT_TYPE(${lfs}, zsh_cv_64_bit_type, force)
      else
        zsh_64_BIT_TYPE(long long, zsh_cv_64_bit_type)