about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-04-15 18:12:56 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-04-15 18:12:56 +0000
commit7a40d6c258ad87d147ee5d6839e746c33ebc0ac7 (patch)
treef1330b87515dca18f200b2ea435842de26ada0e9
parent20d67907c95265356b51dbdce8ecc0c1ede9e66b (diff)
downloadzsh-3.1.5-pws-6.tar.gz
zsh-3.1.5-pws-6.tar.xz
zsh-3.1.5-pws-6.zip
zsh-3.1.5-pws-6 zsh-3.1.5-pws-6
-rw-r--r--Config/version.mk4
-rw-r--r--Doc/Makefile.in11
-rw-r--r--Doc/Zsh/builtins.yo20
-rw-r--r--Doc/Zsh/expn.yo108
-rw-r--r--Doc/ztexi.yo6
-rw-r--r--Functions/zless37
-rw-r--r--Functions/zls37
-rw-r--r--INSTALL15
-rw-r--r--Makefile.in6
-rw-r--r--Misc/new-completion-examples519
-rw-r--r--Src/Zle/comp.h5
-rw-r--r--Src/Zle/comp1.c8
-rw-r--r--Src/Zle/comp1.export3
-rw-r--r--Src/Zle/compctl.c67
-rw-r--r--Src/Zle/iwidgets.list2
-rw-r--r--Src/Zle/zle.h24
-rw-r--r--Src/Zle/zle_main.c29
-rw-r--r--Src/Zle/zle_params.c23
-rw-r--r--Src/Zle/zle_thingy.c57
-rw-r--r--Src/Zle/zle_tricky.c847
-rw-r--r--Src/builtin.c132
-rw-r--r--Src/glob.c321
-rw-r--r--Src/init.c45
-rw-r--r--Src/lex.c5
-rw-r--r--Src/params.c66
-rw-r--r--Src/subst.c45
-rw-r--r--Src/zsh.export1
-rw-r--r--Src/zsh.h11
-rw-r--r--configure.in21
-rw-r--r--patchlist.txt70
30 files changed, 1747 insertions, 798 deletions
diff --git a/Config/version.mk b/Config/version.mk
index 05e8483fd..29f486012 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-5
-VERSION_DATE='January 19, 1998'
+VERSION=3.1.5-pws-6
+VERSION_DATE='January 28, 1998'
diff --git a/Doc/Makefile.in b/Doc/Makefile.in
index 1f8fa5ff9..e22f3c71d 100644
--- a/Doc/Makefile.in
+++ b/Doc/Makefile.in
@@ -112,11 +112,11 @@ version.yo: $(sdir_top)/Config/version.mk
 
 # ========== DEPENDENCIES FOR INSTALLING ==========
 
-# install all the documentation
-install: install.man install.info
+# install just installs the manual pages
+install: install.man
 
-# uninstall all the documentation
-uninstall: uninstall.man uninstall.info
+# uninstall just unistalls the manual pages
+uninstall: uninstall.man
 
 # install man pages, creating install directory if necessary
 install.man: $(MAN)
@@ -141,6 +141,9 @@ install.info: zsh.info
 	  else :; \
 	   fi || exit 1; \
 	done
+	if $(SHELL) -c 'install-info --version' >/dev/null 2>&1; then \
+	  install-info --dir-file=$(infodir)/dir $(infodir)/zsh.info; \
+	else true; fi
 
 install.html: zsh_toc.html
 	$(sdir_top)/mkinstalldirs $(htmldir)
diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index 82562fcbe..98110d122 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -878,7 +878,10 @@ Equivalent to tt(whence -v).
 findex(typeset)
 cindex(parameters, setting)
 cindex(parameters, declaring)
-item(tt(typeset) [ {tt(PLUS())|tt(-)}tt(ALRUZafilrtuxm) [var(n)]] [ var(name)[tt(=)var(value)] ... ])(
+xitem(tt(typeset) [ {tt(PLUS())|tt(-)}tt(ALRUZafilrtuxm) [var(n)]] [ \
+var(name)[tt(=)var(value)] ... ])
+item(tt(typeset) -T [ {tt(PLUS()|tt(-))}tt(LRUZrux) ] \
+  var(SCALAR)[tt(=)var(value)] var(array))(
 Set or display attributes and values for shell parameters.
 
 A parameter is created for each var(name) that does not already refer
@@ -891,7 +894,7 @@ ifnzman(noderef(Local Parameters))\
 which case the parameter is exported em(only) when var(name) does not
 already appear in the environment.
 
-For each nofill(var(name)tt(=)var(value)) assignment, the parameter
+For each var(name)tt(=)var(value) assignment, the parameter
 var(name) set to var(value).  Note that arrays currently cannot be
 assigned in tt(typeset) expressions; scalars and integers only.
 
@@ -899,6 +902,19 @@ For each remaining var(name) that refers to a parameter that is set,
 the name and value of the parameter are printed in the form of an
 assignment.  Nothing is printed for newly-created parameters.
 
+If the tt(-T) option is given, exactly two (or zero) var(name)
+arguments must be present.  They represent a scalar and an array (in
+that order) that will be tied together in the manner of tt($PATH) and
+tt($path).  In other words, an array present in the latter variable
+appears as a scalar with the elements of the array joined by colons in
+the former.  Only the scalar may have an initial value.  Both the
+scalar and the array may otherwise be manipulated as normal.  If one
+is unset, the other will automatically be unset too.  There is no way
+of untying the variables without unsetting them, or converting the
+type of one them with another tt(typeset) command; tt(+T) does not work,
+assigning an array to var(SCALAR) is an error, and assigning a scalar
+to var(array) sets it to be a single-element array.
+
 If no var(name) is present, the names and values of all parameters are
 printed.  In this case the attribute flags restrict the the display to
 only those parameters that have the specified attributes.  Using
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index 32cc27f4e..87ef13d06 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -602,6 +602,46 @@ item(tt(f))(
 Split the result of the expansion to lines. This is a shorthand
 for `tt(ps:\n:)'.
 )
+item(tt(t))(
+Don't work on the value of the parameter, but on a string describing
+the type of the parameter. This string consists of keywords separated
+by hyphens (`tt(-)'). The first keyword in the string describes the
+main type, it can be one of `tt(scalar)', `tt(array)', `tt(integer)',
+or `tt(association)'. The other keywords describe the type in more
+detail:
+
+startitem()
+item(`tt(left)')(
+for left justified parameters
+)
+item(`tt(right_blanks)')(
+for right justified parameters with leading blanks
+)
+item(`tt(right_zeros)')(
+for right justified parameters with leading zeros
+)
+item(`tt(lower)')(
+for parameters whose value is converted to all lower case when it is
+expanded
+)
+item(`tt(upper)')(
+for parameters whose value is converted to all upper case when it is
+expanded
+)
+item(`tt(readonly)')(
+for readonly parameters
+)
+item(`tt(tag)')(
+for tagged parameters
+)
+item(`tt(export)')(
+for exported parameters
+)
+item(`tt(unique)')(
+for arrays which keep only the first occurrence of duplicated values
+)
+enditem()
+)
 enditem()
 
 The following flags are meaningful with the tt(${)...tt(#)...tt(}),
@@ -1048,6 +1088,49 @@ setgid files (02000)
 item(tt(t))(
 files with the sticky bit (01000)
 )
+item(tt(o)var(spec))(
+files with access rights matching var(spec). This var(spec) may be a
+octal number optionally preceded by a `tt(=)', a `tt(PLUS())', or a
+`tt(-)'. If none of these characters is given, the behavior is the
+same as for `tt(=)'. The octal number decribes the mode bits to be
+expected, if combined with a `tt(=)', the value given must match the
+file-modes exactly, with a `tt(PLUS())', at least the bits in the
+given number must be set in the file-modes, and with a `tt(-)', the
+bits in the number must not be set. Giving a `tt(?)' instead of a
+octal digit anywhere in the number ensures that the corresponding bits 
+inthe file-modes are not checked, this is only useful in combination
+with `tt(=)'.
+
+If the qualifier `tt(o)' is followed by any other character anything
+up to the next matching character (`tt([)', `tt({)', and `tt(<)' match 
+`tt(])', `tt(})', and `tt(>)' respectively, any other character
+matches itself) is taken as a list of comma-separated
+var(sub-spec)s. Each var(sub-spec) may be either a octal number as
+described above or a list of any of the characters `tt(u)', `tt(g)',
+`tt(o)', and `tt(a)', followed by a `tt(=)', a `tt(PLUS())', or a
+`tt(-)', followed by a list of any of the characters `tt(r)', `tt(w)', 
+`tt(x)', `tt(s)', and `tt(t)', or a octal digit. The first list of
+characters specify which acess rights are to be checked. If a `tt(u)'
+is given, those for the owner of the file are used, if a `tt(g)' is
+given, those of the group are checked, a `tt(o)' means to test those
+of other users, and the `tt(a)' says to test all three groups. The
+`tt(=)', `tt(PLUS())', and `tt(-)' again says how the modes are to be
+checked and have the same meaning as described for the first form
+above. The second list of characters finally says which access rights
+are to be expected: `tt(r)' for read access, `tt(w)' for write access, 
+`tt(x)' for the right to execute the file (or to search a directory),
+`tt(s)' for the setuid and setgid bits, and `tt(t)' for the sticky
+bit.
+
+Thus, `tt(*(o70?))' 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
+`tt(*(o-100))' gives all files for which the owner does not have
+execute permission, and `tt(*(o:gu+w,o-rx))' gives the files for which 
+the owner and the other members of the group have at least write
+permission, and fo which other users don't have read or execute
+permission.
+)
 item(tt(d)var(dev))(
 files on the device var(dev)
 )
@@ -1065,8 +1148,8 @@ item(tt(u)var(id))(
 files owned by user ID var(id) if it is a number, if not, than the
 character after the `tt(u)' will be used as a separator and the string
 between it and the next matching separator
-(`tt(LPAR())', `tt([)', `tt({)', and `tt(<)'
-match `tt(RPAR())', `tt(])', `tt(})', and `tt(>)' respectively,
+(`tt([)', `tt({)', and `tt(<)'
+match `tt(])', `tt(})', and `tt(>)' respectively,
 any other character matches
 itself) will be taken as a user name, and the user ID of this user will
 be taken (e.g. `tt(u:foo:)' or `tt(u[foo])' for user `tt(foo)')
@@ -1122,6 +1205,27 @@ item(tt(D))(
 sets the tt(GLOB_DOTS) option for the current pattern
 pindex(GLOB_DOTS, setting in pattern)
 )
+item(tt(O)var(c))(
+specifies how the names of the files should be sorted. If var(c) is
+tt(n) they are sorted by name (the default), if var(c) is tt(L) they
+are sorted depending on the size (length) of the files, tt(l) makes
+them be sorted by the number of links, and tt(a), tt(m), and tt(c)
+make them be sorted by the time of the last access, modification, and
+inode change respectively. Note that tt(a), tt(m), and tt(c) compare
+the age to the current time, so the first name in the list is the 
+one of the youngest file. Also note that the modifiers tt(^) and tt(-) are 
+used, so `tt(*(^-OL))' gives a list of all files sorted by file size in 
+descending order working not on symbolic links but on the files they
+point to.
+)
+item(tt([)var(beg)[tt(,)var(end)]tt(]))(
+specifies which of the matched filenames should be included in the
+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.
+)
 enditem()
 
 More than one of these lists can be combined, separated by commas. The
diff --git a/Doc/ztexi.yo b/Doc/ztexi.yo
index b8e115ec0..7388c61aa 100644
--- a/Doc/ztexi.yo
+++ b/Doc/ztexi.yo
@@ -57,6 +57,12 @@ def(texinfo)(2)(\
   NOTRANS(@iftex)NL()\
   NOTRANS(@set dsq '{}')NL()\
   NOTRANS(@end iftex)NL()\
+  NOTRANS(@ifinfo)NL()\
+  NOTRANS(@dircategory Utilities)NL()\
+  NOTRANS(@direntry)NL()\
+  NOTRANS(     * ZSH: (zsh).                     The Z Shell Guide.)NL()\
+  NOTRANS(@end direntry)NL()\
+  NOTRANS(@end ifinfo)NL()\
 )
 
 def(texiifinfo)(1)(\
diff --git a/Functions/zless b/Functions/zless
new file mode 100644
index 000000000..809ce35c7
--- /dev/null
+++ b/Functions/zless
@@ -0,0 +1,37 @@
+#!/usr/bin/zsh -f
+#
+# zsh function script to run less on various inputs, decompressing as required.
+# Author: Phil Pennock.  zsh-hacks@athenaeum.demon.co.uk
+# Modified by Bart Schaefer.
+# Thanks to zefram@fysh.org for a great deal of help in sorting this out,
+# ie wrt syntax for unsetting members of arrays and eval "$(...)" when I
+# asked for something better than . =(...)
+#
+# Use -zforce to pass through a display-formatting command
+#  zless -zforce 'bzip2 -dc' foo-no-dotbz2
+#  zless -zforce 'od -hc' foo-binfile
+#
+# If you can understand all of this without reference to zshexpn(1)
+# and zshparam(1) then you either have a photographic memory or you
+# need to get out more.
+#
+
+emulate -R zsh
+setopt localoptions
+
+[[ $# -ge 1 ]] || return
+local lessopts
+set -A lessopts
+integer i=1 loi=1
+while ((i <= $#))
+do
+  case $argv[i] in
+  -zforce) argv[i,i+2]=("=($argv[i+1] \"$argv[i+2]\")"); ((++i));;
+  -*) lessopts[loi++]=\"$argv[i]\"; argv[i]=(); continue;;
+  *.(gz|Z)) argv[i]="=(zcat \"$argv[i]\")";;
+  *.bz2) argv[i]="=(bzip2 -dc \"$argv[i]\")";;
+  *.bz) argv[i]="=(bzip -dc \"$argv[i]\")";;
+  esac
+  ((++i))
+done
+eval command less $lessopts $*
diff --git a/Functions/zls b/Functions/zls
index da6eff856..f2299903c 100644
--- a/Functions/zls
+++ b/Functions/zls
@@ -6,12 +6,13 @@ zmodload -i stat || return 1
 emulate -R zsh
 setopt localoptions
 
-local f stat opts='' L=L mod=: dirs list
+local f opts='' L=L mod=: dirs list
+typeset -A stat
 
 dirs=()
 list=()
 
-while getopts ailLFd f
+while getopts ailLFdtuc f
 do
     opts=$opts$f
     if [[ $f == '?' ]] then
@@ -25,24 +26,40 @@ shift OPTIND-1
 [[ $opts == *F* ]] && mod=T$mod
 [[ $opts == *a* ]] && setopt globdots
 
+local time=mtime tmod=m
+[[ $opts == *u* ]] && time=atime tmod=a
+[[ $opts == *c* ]] && time=ctime tmod=c
+
 if ((! ARGC)) then
-    set *
+    if [[ $opts = *t* ]]; then
+        set *(O$tmod)
+    else
+        set *
+    fi
     opts=d$opts
+elif [[ $opts = *t* && $ARGC -gt 1 ]]; then
+    # another glaringly obvious zsh trick:  reorder the argv list
+    # by time, without messing up metacharacters inside
+    local n='$1'
+    for (( f = 2; f <= $ARGC; f++ )); do
+	n="$n|\$$f"
+    done
+    eval "argv=(($n)(O$tmod))"
 fi
 
 for f in $*
 do
-    stat -s$L -A stat -F "%b %e %H:%M" - $f || continue
-    if [[ $opts != *d* && $stat[3] == d* ]] then
+    stat -s$L -H stat -F "%b %e %H:%M" - $f || continue
+    if [[ $opts != *d* && $stat[mode] == d* ]] then
 	dirs=( $dirs $f )
     elif [[ $opts == *l* ]] then
-	[[ $opts == *i* ]] && print -n "${(l:7:)stat[2]} "
-	[[ -n $stat[14] ]] && f=( $f '->' $stat[14] ) || f=( $f($mod) )
-	print -r -- "$stat[3] ${(l:3:)stat[4]} ${(r:8:)stat[5]} " \
-		    "${(r:8:)stat[6]} ${(l:8:)stat[8]} $stat[10] $f"
+	[[ $opts == *i* ]] && print -n "${(l:7:)stat[inode]} "
+	[[ -n $stat[link] ]] && f=( $f '->' $stat[link] ) || f=( $f($mod) )
+	print -r -- "$stat[mode] ${(l:3:)stat[nlink]} ${(r:8:)stat[uid]} " \
+		    "${(r:8:)stat[gid]} ${(l:8:)stat[size]} $stat[$time] $f"
     else
 	f=( $f($mod) )
-	list=( "$list[@]" "${${(M)opts:%*i*}:+${(l:7:)stat[2]} }$f" )
+	list=( "$list[@]" "${${(M)opts:%*i*}:+${(l:7:)stat[inode]} }$f" )
     fi
 done
 (($#list)) && print -cr -- "$list[@]"
diff --git a/INSTALL b/INSTALL
index 4019a16b8..1ed6847e3 100644
--- a/INSTALL
+++ b/INSTALL
@@ -114,15 +114,18 @@ To install the dynamically-loadable modules, do the command:
 To install the zsh man page, do the command:
     make install.man
 
-To install the zsh info files, do the command:
-    make install.info
-
 Or alternatively, you can install all the above with the command:
     make install
 
-"make install.info" will only move the info files into the info directory.
-You will have to edit the topmost node of the info tree "dir" manually
-in order to have the zsh info files available to your info reader.
+To install the zsh info files (this must be done separately), do the
+command:
+    make install.info
+
+If the programme install-info is available, "make install.info" will
+insert an entry in the file "dir" in the same directory as the info
+files.  Otherwise you will have to edit the topmost node of the info
+tree "dir" manually in order to have the zsh info files available to
+your info reader.
 
 Building Zsh On Additional Architectures
 ----------------------------------------
diff --git a/Makefile.in b/Makefile.in
index b2be5bf44..498e3bfd9 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -55,9 +55,9 @@ META-FAQ: FORCE
 install-strip:
 	$(MAKE) install STRIPFLAGS="-s"
 
-# install/uninstall everything
-install: install.bin install.modules install.man install.info
-uninstall: uninstall.bin uninstall.modules uninstall.man uninstall.info
+# install/uninstall everything (except info)
+install: install.bin install.modules install.man
+uninstall: uninstall.bin uninstall.modules uninstall.man
 
 # install/uninstall just the binary
 install.bin uninstall.bin:
diff --git a/Misc/new-completion-examples b/Misc/new-completion-examples
index 659679891..b98e523d6 100644
--- a/Misc/new-completion-examples
+++ b/Misc/new-completion-examples
@@ -19,21 +19,31 @@ typeset -A comps
 # With only one argument the function/variable-name __$1 is used.
 
 defcomp() {
-  local v
+  local v a=''
 
+  if [[ "$1" = -a ]] then
+    shift
+    a=yes
+  fi
   if [[ $# -eq 1 ]] then
     comps[$1]="__$1"
+    [[ -z "$a" ]] || autoload "__$1"
   else
     v="$1"
     shift
     for i; do
       comps[$i]="$v"
     done
+    [[ -z "$a" ]] || autoload "$v"
   fi
 }
 
 defpatcomp() {
-  if [[ ${+patcomps} == 1 ]] then
+  if [[ "$1" = -a ]] then
+    shift
+    autoload "$1"
+  fi
+  if (( $+patcomps )) then
     patcomps=("$patcomps[@]" "$2 $1" )
   else
     patcomps=( "$2 $1" )
@@ -68,10 +78,10 @@ call-complete() {
   local var
 
   eval var\=\$\{\+$1\}
-  if [[ "$var" == 0 ]] then
-    "$@"
-  else
+  if (( var )); then
     eval complist \$\{${1}\[\@\]\}
+  else
+    "$@"
   fi
 }
 
@@ -80,11 +90,21 @@ call-complete() {
 # arguments from the command line are gives as positional parameters.
 
 main-complete() {
-  emulate -R zsh
+  # emulate -R zsh
   local comp
+
   setopt localoptions nullglob rcexpandparam globdots
   unsetopt markdirs globsubst shwordsplit nounset
 
+  # We first try the `compctl's. This is without first (-T) and default (-D)
+  # completion. If you want them add `-T' and/or `-D' to this command.
+  # If this produces any matches, we don't try new style completion. If you
+  # want to have that tried anyway, remove the `[[ -nmatches ... ]] ...'
+  # below.
+
+  compcall
+  [[ -nmatches 0 ]] || return
+
   # An entry for `--first--' is the replacement for `compctl -T'
   # The `|| return 1' is used throughout: if a function producing matches
   # returns non-zero this is interpreted as `do not try to produce more matches'
@@ -164,21 +184,6 @@ do-complete() {
   [[ -z "$comp" ]] || call-complete "$comp" "$@" || return 1
 }
 
-# Do sub-completion for pre-command modifiers.
-
-defcomp __precmd - noglob nocorrect exec command builtin
-__precmd() {
-  COMMAND="$1"
-  shift
-  (( CURRENT-- ))
-  if [[ CURRENT -eq 0 ]] then
-    CONTEXT=command
-  else
-    CONTEXT=argument
-  fi
-  compsub
-}
-
 # Utility function for in-path completion.
 # First argument should be an complist-option (e.g. -f, -/, -g). The other
 # arguments should be glob patterns, one per argument.
@@ -190,90 +195,123 @@ __precmd() {
 # so you may want to modify this.
 
 pfiles() {
-  local nm str pa pre epre a b c s rest
+  local nm ostr opa pre epre a b c s rest ppres
 
   setopt localoptions nullglob rcexpandparam globdots extendedglob
   unsetopt markdirs globsubst shwordsplit nounset
 
+  if [[ "$1" = -W ]] then
+    a="$2"
+    if [[ "$a[1]" = '(' ]] then
+      ppres=( $a[2,-2]/ )
+    else
+      eval ppres\=\( \$$a/ \)
+      [[ $#ppres -eq 0 ]] && ppres=( $a/ )
+    fi
+    [[ $#ppres -eq 0 ]] && ppres=( '' )
+    shift 2
+  else
+    ppres=( '' )
+  fi
+
+  str="$PREFIX*$SUFFIX"
+
+  if [[ -z "$a[1]" || "$str[1]" = [~/] || "$str" = (.|..)/* ]] then
+    a=()
+  else
+    a=(-W "( $ppres )")
+  fi
   nm=$NMATCHES
   if [[ $# -eq 0 ]] then
-    complist -f
+    complist "$a[@]" -f
   elif [[ "$1" = -g ]] then
-    complist -g "$argv[2,-1]"
+    complist "$a[@]" -g "$argv[2,-1]"
     shift
   else
-    complist $1
+    complist "$a[@]" $1
     shift
   fi
   [[ -nmatches nm ]] || return
 
-  str="$PREFIX*$SUFFIX"
-
   [[ -z "$1" ]] && 1='*'
-  if [[ $str[1] = \~ ]] then
+
+  if [[ "$str[1]" = \~ ]] then
     pre="${str%%/*}/"
     eval epre\=$pre
     str="${str#*/}"
-    pa=''
+    opa=''
+    ppres=( '' )
   else
     pre=''
     epre=''
-    if [[ $str[1] = / ]] then
+    if [[ "$str[1]" = / ]] then
       str="$str[2,-1]"
-      pa='/'
+      opa='/'
+      ppres=( '' )
     else
-      pa=''
+      [[ "$str" = (.|..)/* ]] && ppres=( '' )
+      opa=''
     fi
   fi
-  str="$str:gs/,/*,/:gs/_/*_/:gs./.*/.:gs/-/*[-_]/:gs/./*[.,]/:gs-*[.,]*[.,]*/-../-:gs.**.*."
   while [[ "$str" = */* ]] do
-    rest="${str#*/}"
-    a="${epre}${pa}(#l)${str%%/*}(-/)"
-    a=( $~a )
-    if [[ $#a -eq 0 ]] then
-      return
-    elif [[ $#a -gt 1 ]] then
-      c=()
-      s=( $rest$@ )
-      s=( "${(@)s:gs.**.*.}" )
-      for i in $a; do
-        b=( $~i/(#l)$~s )
-        eval b\=\( \$\{b:/\*\(${(j:|:)fignore}\)\} \)
-        [[ $#b -ne 0 ]] && c=( $c $i )
-      done
-      if [[ $#c -eq 0 ]] then
-        return
-      elif [[ $#c -ne 1 ]] then
-        a="$epre$pa"
-        c=( $~c/(#l)$~s )
-        eval c\=\( \$\{c:/\*\(${(j:|:)fignore}\)\} \)
-	c=( ${c#$a} )
-        for i in $c; do
-          compadd -p "$pre$pa" -W "$a" -s "/${i#*/}" -f "${i%%/*}"
+    [[ -e "$epre$opa${str%%/*}" ]] || break
+    opa="$opa${str%%/*}/"
+    str="${str#*/}"
+  done
+
+  ostr="$str:gs/,/*,/:gs/_/*_/:gs./.*/.:gs/-/*[-_]/:gs/./*[.,]/:gs-*[.,]*[.,]*/-../-:gs.**.*."
+
+  for ppre in "$ppres[@]"; do
+    str="$ostr"
+    pa="$opa"
+    while [[ "$str" = */* ]] do
+      rest="${str#*/}"
+      a="${ppre}${epre}${pa}(#l)${str%%/*}(-/)"
+      a=( $~a )
+      if [[ $#a -eq 0 ]] then
+        continue 2
+      elif [[ $#a -gt 1 ]] then
+        c=()
+        s=( $rest$@ )
+        s=( "${(@)s:gs.**.*.}" )
+        for i in $a; do
+          b=( $~i/(#l)$~s )
+          eval b\=\( \$\{b:/\*\(${(j:|:)fignore}\)\} \)
+          [[ $#b -ne 0 ]] && c=( $c $i )
         done
-	return
+        if [[ $#c -eq 0 ]] then
+          continue 2
+        elif [[ $#c -ne 1 ]] then
+          a="$ppre$epre$pa"
+          c=( $~c/(#l)$~s )
+          eval c\=\( \$\{c:/\*\(${(j:|:)fignore}\)\} \)
+  	  c=( ${c#$a} )
+          for i in $c; do
+            compadd -p "$pre$pa" -W "$a" -s "/${i#*/}" -f "${i%%/*}"
+          done
+  	  continue 2
+        fi
+        a=( "$c[1]" )
       fi
-      a=( "$c[1]" )
-    fi
-    a="$a[1]"
-    pa="$pa${a##*/}/"
-    str="$rest"
+      a="$a[1]"
+      pa="$pa${a##*/}/"
+      str="$rest"
+    done
+    a="$ppre$epre$pa"
+    s=( $str$@ )
+    s=( "${(@)s:gs.**.*.}" )
+    b=( $~a(#l)$~s )
+    eval b\=\( \$\{b:/\*\(${(j:|:)fignore}\)\} \)
+    compadd -p "$pre$pa" -W "$ppre$epre$pa" -f ${b#$a}
   done
-  a="$epre$pa"
-  s=( $str$@ )
-  s=( "${(@)s:gs.**.*.}" )
-  b=( $~a(#l)$~s )
-  eval b\=\( \$\{b:/\*\(${(j:|:)fignore}\)\} \)
-  compadd -p "$pre$pa" -W "$epre$pa" -f ${b#$a}
 }
 
 # Utility function for completing files of a given type or any file.
 # In many cases you will want to call this one instead of pfiles().
 
 files() {
-  local nm
+  local nm=$NMATCHES
 
-  nm=$NMATCHES
   pfiles "$@"
 
   [[ $# -ne 0 && -nmatches nm ]] && pfiles
@@ -289,51 +327,247 @@ __default() {
 defcomp __command --command--
 __command=( -c )
 
-defcomp __math --math--
-__math=( -v )
+defcomp __vars --math--
+__vars=( -v )
 
 defcomp __subscr --subscr--
 __subscr() {
+  local t
+
+  eval t\=\$\{\(t\)$COMMAND\}
   compalso --math-- "$@"
-  # ...probably other stuff
+  [[ $t = assoc* ]] && eval complist -k \"\(\$\{\(k\)$COMMAND\}\)\"
 }
 
-# A simple pattern completion, just as an example.
+# Do sub-completion for pre-command modifiers.
 
-defpatcomp __x_options '*/X11/*'
-__x_options() {
-  complist -J options -k '(-display -name -xrm)'
+defcomp __precmd - nohup nice eval time rusage noglob nocorrect exec
+__precmd() {
+  COMMAND="$1"
+  shift
+  (( CURRENT-- ))
+  if [[ CURRENT -eq 0 ]] then
+    CONTEXT=command
+  else
+    CONTEXT=argument
+  fi
+  compsub
+}
+
+defcomp builtin
+__builtin() {
+  if [[ -position 2 -1 ]] then
+    compsub
+  else
+    complist -eB
+  fi
 }
 
-# A better example: completion for `find'.
+defcomp command
+__command() {
+  if [[ -position 2 -1 ]] then
+    compsub
+  else
+    complist -em
+  fi
+}
+  
+# Various completions...
 
-defcomp find
-__find() {
-  compsave
+defcomp __jobs fg jobs
+__jobs=(-j -P '%')
 
-  if [[ -mbetween -(ok|exec) \\\; ]] then
+defcomp __bjobs bg
+__bjobs=(-z -P '%')
+
+defcomp __arrays shift
+__arrays=(-A)
+
+defcomp __which which whence where type
+__which=( -caF )
+
+defcomp unhash
+__unhash() {
+  [[ -mword 1 -*d* ]] && complist -n
+  [[ -mword 1 -*a* ]] && complist -a
+  [[ -mword 1 -*f* ]] && complist -F
+  [[ ! -mword 1 -* ]] && complist -m
+}
+
+defcomp hash
+__hash() {
+  if [[ -mword 1 -*d* ]] then
+    if [[ -string 1 '=' ]] then
+      pfiles -g '*(-/)'
+    else
+      complist -n -q -S '='
+    fi
+  elif [[ -string 1 '=' ]] then
+    files -g '*(*)' '*(-/)'
+  else
+    complist -m -q -S '='
+  fi
+}
+
+defcomp __funcs unfunction
+__funcs=(-F)
+
+defcomp echotc
+__echotc=(-k '(al dc dl do le up al bl cd ce cl cr dc dl do ho is le ma nd nl se so up)')
+
+defcomp __aliases unalias
+__aliases=(-a)
+
+defcomp __vars getopts read unset vared
+
+defcomp __varseq declare export integer local readonly typeset
+__varseq=(-v -S '=')
+
+defcomp disable
+__disable() {
+  [[ -mcurrent -1 -*a* ]] && complist -ea
+  [[ -mcurrent -1 -*f* ]] && complist -eF
+  [[ -mcurrent -1 -*r* ]] && complist -ew
+  [[ ! -mcurrent -1 -* ]] && complist -eB
+}
+
+defcomp enable
+__enable() {
+  [[ -mcurrent -1 -*a* ]] && complist -da
+  [[ -mcurrent -1 -*f* ]] && complist -dF
+  [[ -mcurrent -1 -*r* ]] && complist -dw
+  [[ ! -mcurrent -1 -* ]] && complist -dB
+}
+
+defcomp __limits limit unlimit
+__limits=(-k "(${(j: :)${(f)$(limit)}%% *})")
+
+defcomp source
+__source() {
+  if [[ -position 2 -1 ]] then
     compsub
-  elif [[ -iprefix - ]] then
-    complist -s 'daystart {max,min,}depth follow noleaf version xdev \
-	{a,c,}newer {a,c,m}{min,time} empty false {fs,x,}type gid inum links \
-	{i,}{l,}name {no,}{user,group} path perm regex size true uid used \
-	exec {f,}print{f,0,} ok prune ls'
-    compreset
-  elif [[ -position 1 ]] then
-    complist -g '. ..'
-    files -g '(-/)'
-  elif [[ -mcurrent -1 -((a|c|)newer|fprint(|0|f)) ]] then
+  else
     files
-  elif [[ -current -1 -fstype ]] then
-    complist -k '(ufs 4.2 4.3 nfs tmp mfs S51K S52K)'
-  elif [[ -current -1 -group ]] then
-    complist -k groups
-  elif [[ -current -1 -user ]] then
-    complist -u
   fi
 }
 
-# Various completions...
+defcomp setopt
+__setopt() {
+  local nm=$NMATCHES
+
+  complist -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' \
+           -s '$({ unsetopt kshoptionprint; unsetopt } 2>/dev/null)'
+  [[ -nmatches nm ]] && complist -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o
+}
+
+defcomp unsetopt
+__unsetopt() {
+  local nm=$NMATCHES
+
+  complist -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' \
+           -s '$({ unsetopt kshoptionprint; setopt } 2>/dev/null)'
+  [[ -nmatches nm ]] && complist -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o
+}
+
+defcomp autoload
+__autoload=(-s '${^fpath}/*(N:t)')
+
+defcomp bindkey
+__bindkey() {
+  if [[ -mword 1 -*[DAN]* || -mcurrent -1 -*M ]] then
+    complist -s '$(bindkey -l)'
+  else
+    complist -b
+  fi
+}
+
+defcomp fc
+__fc() {
+  if [[ -mcurrent -1 -*e ]] then
+    complist -c
+  elif [[ -mcurrent -1 -[ARWI]## ]] then
+    files
+  fi
+}
+
+defcomp sched
+__sched() {
+  [[ -position 2 -1 ]] && compsub
+}
+
+defcomp set
+__set() {
+  if [[ -mcurrent -1 [-+]o ]] then
+    complist -o
+  elif [[ -current -1 -A ]] then
+    complist -A
+  fi
+}
+
+defcomp zle
+__zle() {
+  if [[ -word 1 -N && -position 3 ]] then
+    complist -F
+  else
+    complist -b
+  fi
+}
+
+defcomp zmodload
+__zmodload() {
+  if [[ -mword 1 -*(a*u|u*a)* || -mword 1 -*a* && -position 3 -1 ]] then
+    complist -B
+  elif [[ -mword 1 -*u* ]] then
+    complist -s '$(zmodload)'
+  else
+    complist -s '${^module_path}/*(N:t:r)'
+  fi
+}
+
+defcomp trap
+__trap() {
+  if [[ -position 1 ]] then
+    complist -c
+  else
+    complist -k signals
+  fi
+}
+
+killfunc() {
+  reply=( "$(ps -x 2>/dev/null)" )
+}
+
+defcomp kill
+__kill() {
+  if [[ -iprefix '-' ]] then
+    complist -k "($signals[1,-3])"
+  else
+    complist -P '%' -j
+    complist -y killfunc -s '`ps -x 2>/dev/null | tail +2 | cut -c1-5`'
+  fi
+}
+
+defcomp wait
+__wait() {
+  complist -P '%' -j
+  complist -y killfunc -s '`ps -x 2>/dev/null | tail +2 | cut -c1-5`'
+}
+
+defcomp cd
+__cd() {
+  files -W cdpath -g '*(-/)'
+}
+
+defcomp __rlogin rlogin rsh ssh
+__rlogin() {
+  if [[ -position 1 ]] then
+    complist -k hosts
+  elif [[ -position 2 ]] then
+    complist -k '(-l)'
+  elif [[ -position 3 && -word 1 artus ]] then
+    complist -k '(puck root)'
+  fi
+}
 
 defcomp __gunzip gunzip zcat
 __gunzip() {
@@ -350,7 +584,7 @@ __xfig() {
   files -g '*.fig'
 }
 
-defcomp __make make gmake
+defcomp __make make gmake pmake
 __make() {
   complist -s "\$(awk '/^[a-zA-Z0-9][^/ 	]+:/ {print \$1}' FS=: [mM]akefile)"
 }
@@ -360,42 +594,16 @@ __ps() {
   files -g '*([pP][sS]|eps)'
 }
 
-defcomp __which which whence
-__which=( -caF )
-
-defcomp __rlogin rlogin rsh ssh
-__rlogin() {
-  if [[ -position 1 ]] then
-    complist -k hosts
-  elif [[ -position 2 ]] then
-    complist -k '(-l)'
-  elif [[ -position 3 && -word 1 artus ]] then
-    complist -k '(puck root)'
-  fi
-}
-
 defcomp __dvi xdvi dvips dvibook dviconcat dvicopy dvidvi dviselect dvitodvi dvitype
 __dvi() {
   files -g '*.(dvi|DVI)'
 }
 
-defcomp __dirs rmdir df du dircmp cd
+defcomp __dirs rmdir df du dircmp
 __dirs() {
   files -/ '*(-/)'
 }
 
-defcomp __jobs fg bg jobs
-__jobs=(-j -P '%?')
-
-defcomp kill
-__kill() {
-  if [[ -iprefix '-' ]] then
-    complist -k signals
-  else
-    complist -P '%?' -j
-  fi
-}
-
 defcomp __uncompress uncompress zmore
 __uncompress() {
   files -g '*.Z'
@@ -406,29 +614,11 @@ __compress() {
   files -g '*~*.Z'
 }
 
-defcomp __tex tex latex glatex slitex gslitex
+defcomp __tex tex latex slitex
 __tex() {
   files -g '*.(tex|TEX|texinfo|texi)'
 }
 
-defcomp __options setopt unsetopt
-__options=(-M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o)
-
-defcomp __funcs unfunction
-__funcs=(-F)
-
-defcomp __aliases unalias
-__aliases=(-a)
-
-defcomp __vars unset
-__vars=(-v)
-
-defcomp __enabled disable
-__enabled=(-FBwa)
-
-defcomp __disabled enable
-__disabled=(-dFBwa)
-
 defcomp __pdf acroread
 __pdf() {
   files -g '*.(pdf|PDF)'
@@ -436,11 +626,9 @@ __pdf() {
 
 defcomp tar
 __tar() {
-  local nm tf
+  local nm=$NMATCHES tf="$2"
   compsave
 
-  tf="$2"
-  nm=$NMATCHES
   if [[ ( -mword 1 *t*f* || -mword 1 *x*f* ) && -position 3 100000 ]] then
     complist -k "( $(tar tf $tf) )"
     compreset
@@ -451,3 +639,36 @@ __tar() {
     files -g '*.(tar|TAR)'
   fi
 }
+
+defcomp find
+__find() {
+  compsave
+
+  if [[ -mbetween -(ok|exec) \\\; ]] then
+    compsub
+  elif [[ -iprefix - ]] then
+    complist -s 'daystart {max,min,}depth follow noleaf version xdev \
+	{a,c,}newer {a,c,m}{min,time} empty false {fs,x,}type gid inum links \
+	{i,}{l,}name {no,}{user,group} path perm regex size true uid used \
+	exec {f,}print{f,0,} ok prune ls'
+    compreset
+  elif [[ -position 1 ]] then
+    complist -g '. ..'
+    files -g '(-/)'
+  elif [[ -mcurrent -1 -((a|c|)newer|fprint(|0|f)) ]] then
+    files
+  elif [[ -current -1 -fstype ]] then
+    complist -k '(ufs 4.2 4.3 nfs tmp mfs S51K S52K)'
+  elif [[ -current -1 -group ]] then
+    complist -k groups
+  elif [[ -current -1 -user ]] then
+    complist -u
+  fi
+}
+
+# A simple pattern completion, just as an example.
+
+defpatcomp __x_options '*/X11/*'
+__x_options() {
+  complist -J options -k '(-display -name -xrm)'
+}
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index afd55b7f1..20e3d2cce 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -267,3 +267,8 @@ struct cline {
 #define CLF_MISS  4
 #define CLF_DIFF  8
 #define CLF_SUF  16
+
+/* Flags for makecomplist*(). Things not to do. */
+
+#define CFN_FIRST   1
+#define CFN_DEFAULT 2
diff --git a/Src/Zle/comp1.c b/Src/Zle/comp1.c
index 42bc92bb2..36588a385 100644
--- a/Src/Zle/comp1.c
+++ b/Src/Zle/comp1.c
@@ -44,12 +44,6 @@ Cmlist cmatcher;
 /* pointers to functions required by zle and defined by compctl */
 
 /**/
-void (*printcompctlptr) _((char *, Compctl, int, int));
-
-/**/
-Compctl (*compctl_widgetptr) _((char *, char **));
-
-/**/
 void (*makecompparamsptr) _((void));
 
 /* pointers to functions required by compctl and defined by zle */
@@ -66,6 +60,8 @@ int (*getcpatptr) _((char *, int, char *, int));
 /**/
 void (*makecomplistcallptr) _((Compctl));
 
+/**/
+void (*makecomplistctlptr) _((int));
 
 /* Hash table for completion info for commands */
  
diff --git a/Src/Zle/comp1.export b/Src/Zle/comp1.export
index c90161740..72581173b 100644
--- a/Src/Zle/comp1.export
+++ b/Src/Zle/comp1.export
@@ -11,7 +11,6 @@ clwsize
 cmatcher
 compcommand
 compcontext
-compctl_widgetptr
 compctltab
 compcurrent
 compiprefix
@@ -28,8 +27,8 @@ incompctlfunc
 incompfunc
 instring
 makecomplistcallptr
+makecomplistctlptr
 makecompparamsptr
 patcomps
-printcompctlptr
 quotename
 rembslash
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index 1913d3828..879042cf3 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -1638,38 +1638,6 @@ bin_compctl(char *name, char **argv, char *ops, int func)
     return ret;
 }
 
-/* Externally callable version of get_compctl.  Used for completion widgets */
-
-/**/
-static Compctl
-compctl_widget(char *name, char **argv)
-{
-  Compctl cc = (Compctl) zcalloc(sizeof(*cc));
-  cclist = 0;
-  showmask = 0;
-
-  if (get_compctl(name, &argv, cc, 1, 0, 0)) {
-      freecompctl(cc);
-      return NULL;
-  }
-
-  if (cclist & COMP_REMOVE) {
-      zwarnnam(name, "use -D to delete widget", NULL, 0);
-      return NULL;
-  } else if (cclist) {
-      zwarnnam(name, "special options illegal in widget", NULL, 0);
-      freecompctl(cc);
-      return NULL;
-  } else if (*argv) {
-      zwarnnam(name, "command names illegal in widget", NULL, 0);
-      freecompctl(cc);
-      return NULL;
-  }
-  cc->refc++;
-
-  return cc;
-}
-
 /**/
 static int
 bin_complist(char *name, char **argv, char *ops, int func)
@@ -1677,7 +1645,7 @@ bin_complist(char *name, char **argv, char *ops, int func)
     Compctl cc;
     int ret = 0;
 
-    if (!incompfunc) {
+    if (incompfunc != 1) {
 	zerrnam(name, "can only be called from completion function", NULL, 0);
 	return 1;
     }
@@ -1706,7 +1674,7 @@ bin_compadd(char *name, char **argv, char *ops, int func)
     char *pre = NULL, *suf = NULL, *group = NULL;
     int f = 0, q = 0, m = 0, ns = 0, a = 0;
 
-    if (!incompfunc) {
+    if (incompfunc != 1) {
 	zerrnam(name, "can only be called from completion function", NULL, 0);
 	return 1;
     }
@@ -1792,15 +1760,27 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 	}
     }
  ca_args:
-    if (!*argv) {
-	zerrnam(name, "missing completions", NULL, 0);
+    if (!*argv)
 	return 1;
-    }
+
     addmatchesptr(ipre, ppre, psuf, prpre, pre, suf, group,
 		  f, q, m, ns, a, argv);
     return 0;
 }
 
+/**/
+static int
+bin_compcall(char *name, char **argv, char *ops, int func)
+{
+    if (incompfunc != 1) {
+	zerrnam(name, "can only be called from completion function", NULL, 0);
+	return 1;
+    }
+    makecomplistctlptr((ops['T'] ? 0 : CFN_FIRST) |
+		       (ops['D'] ? 0 : CFN_DEFAULT));
+    return 0;
+}
+
 #define VAR(X) ((void *) (&(X)))
 static struct compparam {
     char *name;
@@ -1823,7 +1803,7 @@ void makecompparams(void)
     struct compparam *cp;
 
     for (cp = compparams; cp->name; cp++) {
-	Param pm = createparam(cp->name, cp->type | PM_SPECIAL);
+	Param pm = createparam(cp->name, cp->type | PM_SPECIAL|PM_REMOVABLE);
 	if (!pm)
 	    pm = (Param) paramtab->getnode(paramtab, cp->name);
 	DPUTS(!pm, "param not set in makecompparams");
@@ -1856,7 +1836,7 @@ compunsetfn(Param pm, int exp)
 static int
 comp_wrapper(List list, FuncWrap w, char *name)
 {
-    if (!incompfunc)
+    if (incompfunc != 1)
 	return 1;
     else {
 	char *octxt, *ocmd, *opre, *osuf, *oipre;
@@ -1907,7 +1887,7 @@ ignore_prefix(int l)
 static int
 comp_check(void)
 {
-    if (!incompfunc) {
+    if (incompfunc != 1) {
 	zerr("condition can only be used in completion function", NULL, 0);
 	return 0;
     }
@@ -2119,7 +2099,8 @@ cond_nmatches(char **a, int id)
 static struct builtin bintab[] = {
     BUILTIN("compctl", 0, bin_compctl, 0, -1, 0, NULL, NULL),
     BUILTIN("complist", 0, bin_complist, 1, -1, 0, NULL, NULL),
-    BUILTIN("compadd", 0, bin_compadd, 1, -1, 0, NULL, NULL),
+    BUILTIN("compadd", 0, bin_compadd, 0, -1, 0, NULL, NULL),
+    BUILTIN("compcall", 0, bin_compcall, 0, 0, 0, "TD", NULL),
 };
 
 static struct conddef cotab[] = {
@@ -2149,8 +2130,6 @@ int
 setup_compctl(Module m)
 {
     compctltab->printnode = printcompctlp;
-    printcompctlptr = printcompctl;
-    compctl_widgetptr = compctl_widget;
     makecompparamsptr = makecompparams;
     return 0;
 }
@@ -2183,8 +2162,6 @@ int
 finish_compctl(Module m)
 {
     compctltab->printnode = NULL;
-    printcompctlptr = NULL;
-    compctl_widgetptr = NULL;
     makecompparamsptr = NULL;
     return 0;
 }
diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list
index 12425d872..61ad0e24a 100644
--- a/Src/Zle/iwidgets.list
+++ b/Src/Zle/iwidgets.list
@@ -26,7 +26,7 @@
 "capitalize-word", capitalizeword, 0
 "clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
 "complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
-"copy-prev-word", copyprevword, 0
+"copy-prev-word", copyprevword, ZLE_KEEPSUFFIX
 "copy-region-as-kill", copyregionaskill, ZLE_KEEPSUFFIX
 "delete-char", deletechar, ZLE_KEEPSUFFIX
 "delete-char-or-list", deletecharorlist, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h
index f12505bd3..144bb1996 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -46,7 +46,6 @@ struct widget {
     union {
 	ZleIntFunc fn;	/* pointer to internally implemented widget */
 	char *fnnam;	/* name of the shell function for user-defined widget */
-        Compctl cc;     /* for use with a WIDGET_COMP widget */
 	struct {
 	    ZleIntFunc fn; /* internal widget function to call */
 	    char *wid;     /* name of widget to call */
@@ -56,20 +55,15 @@ struct widget {
 };
 
 #define WIDGET_INT	(1<<0)    /* widget is internally implemented */
-#define WIDGET_COMP	(1<<1)	  /* Special completion widget */
-#define WIDGET_NCOMP    (1<<2)    /* new style completion widget */
-#define ZLE_MENUCMP	(1<<3)    /* DON'T invalidate completion list */
-#define ZLE_YANK	(1<<4)
-#define ZLE_LINEMOVE	(1<<5)    /* command is a line-oriented movement */
-#define ZLE_LASTCOL     (1<<6)    /* command maintains lastcol correctly */
-#define ZLE_KILL	(1<<7)
-#define ZLE_KEEPSUFFIX	(1<<9)    /* DON'T remove added suffix */
-#define ZLE_USEMENU	(1<<10)   /* Do    ) use menu completion for   */
-#define ZLE_NOMENU	(1<<11)   /* Don't )  widget, else use default */
-#define ZLE_USEGLOB	(1<<12)   /* Do    ) use glob completion for   */
-#define ZLE_NOGLOB	(1<<13)   /* Don't )  widget, else use default */
-#define ZLE_NOTCOMMAND  (1<<14)   /* widget should not alter lastcmd */
-#define ZLE_ISCOMP      (1<<15)   /* usable for new style completion */
+#define WIDGET_NCOMP    (1<<1)    /* new style completion widget */
+#define ZLE_MENUCMP	(1<<2)    /* DON'T invalidate completion list */
+#define ZLE_YANK	(1<<3)
+#define ZLE_LINEMOVE	(1<<4)    /* command is a line-oriented movement */
+#define ZLE_LASTCOL     (1<<5)    /* command maintains lastcol correctly */
+#define ZLE_KILL	(1<<6)
+#define ZLE_KEEPSUFFIX	(1<<7)    /* DON'T remove added suffix */
+#define ZLE_NOTCOMMAND  (1<<8)    /* widget should not alter lastcmd */
+#define ZLE_ISCOMP      (1<<9)	  /* usable for new style completion */
 /* thingies */
 
 struct thingy {
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 2c0d3655e..03549b5d0 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -45,10 +45,10 @@ int mark;
 /**/
 int c;
 
-/* the binding for this key */
+/* the bindings for the previous and for this key */
 
 /**/
-Thingy bindk;
+Thingy lbindk, bindk;
 
 /* insert mode/overwrite mode flag */
 
@@ -554,6 +554,7 @@ zleread(char *lp, char *rp, int ha)
 void
 execzlefunc(Thingy func)
 {
+    int r = 0;
     Widget w;
 
     if(func->flags & DISABLED) {
@@ -565,14 +566,13 @@ execzlefunc(Thingy func)
 	showmsg(msg);
 	zsfree(msg);
 	feep();
-    } else if((w = func->widget)->flags &
-	      (WIDGET_INT|WIDGET_COMP | WIDGET_NCOMP)) {
+    } else if((w = func->widget)->flags & (WIDGET_INT|WIDGET_NCOMP)) {
 	int wflags = w->flags;
 
 	if(!(wflags & ZLE_KEEPSUFFIX))
 	    removesuffix();
 	if(!(wflags & ZLE_MENUCMP) ||
-	   ((wflags & (WIDGET_COMP|WIDGET_NCOMP)) && compwidget != w)) {
+	   ((wflags & WIDGET_NCOMP) && compwidget != w)) {
 	    /* If we are doing a special completion, and the widget
 	     * is not the one currently in use for special completion,
 	     * we are starting a new completion.
@@ -584,16 +584,14 @@ execzlefunc(Thingy func)
 	    vilinerange = 1;
 	if(!(wflags & ZLE_LASTCOL))
 	    lastcol = -1;
-	if (wflags & WIDGET_COMP) {
-	    compwidget = w;
-	    completespecial();
-	} else if (wflags & WIDGET_NCOMP) {
+	if (wflags & WIDGET_NCOMP) {
 	    compwidget = w;
 	    completecall();
 	} else
 	    w->u.fn();
 	if (!(wflags & ZLE_NOTCOMMAND))
 	    lastcmd = wflags;
+	r = 1;
     } else {
 	List l = getshfunc(w->u.fnnam);
 
@@ -610,14 +608,20 @@ execzlefunc(Thingy func)
 	    int osc = sfcontext;
 
 	    startparamscope();
-	    makezleparams();
+	    makezleparams(0);
 	    sfcontext = SFC_WIDGET;
 	    doshfunc(w->u.fnnam, l, NULL, 0, 1);
 	    sfcontext = osc;
 	    endparamscope();
 	    lastcmd = 0;
+	    r = 1;
 	}
     }
+    if (r) {
+	unrefthingy(lbindk);
+	refthingy(func);
+	lbindk = func;
+    }
 }
 
 /* initialise command modifiers */
@@ -877,9 +881,11 @@ setup_zle(Module m)
     comp_strptr = comp_str;
     getcpatptr = getcpat;
     makecomplistcallptr = makecomplistcall;
+    makecomplistctlptr = makecomplistctl;
 
     /* initialise the thingies */
     init_thingies();
+    lbindk = NULL;
 
     /* miscellaneous initialisations */
     stackhist = stackcs = -1;
@@ -920,6 +926,8 @@ finish_zle(Module m)
 {
     int i;
 
+    unrefthingy(lbindk);
+
     cleanup_keymaps();
     deletehashtable(thingytab);
 
@@ -944,6 +952,7 @@ finish_zle(Module m)
     comp_strptr = NULL;
     getcpatptr = NULL;
     makecomplistcallptr = NULL;
+    makecomplistctlptr = NULL;
 
     return 0;
 }
diff --git a/Src/Zle/zle_params.c b/Src/Zle/zle_params.c
index e0c4e94ec..74f905ef4 100644
--- a/Src/Zle/zle_params.c
+++ b/Src/Zle/zle_params.c
@@ -61,17 +61,22 @@ static struct zleparam {
 	zleunsetfn, NULL },
     { "RBUFFER", PM_SCALAR,  FN(set_rbuffer), FN(get_rbuffer),
 	zleunsetfn, NULL },
+    { "WIDGET", PM_SCALAR | PM_READONLY, NULL, FN(get_widget),
+        zleunsetfn, NULL },
+    { "LASTWIDGET", PM_SCALAR | PM_READONLY, NULL, FN(get_lwidget),
+        zleunsetfn, NULL },
     { NULL, 0, NULL, NULL, NULL, NULL }
 };
 
 /**/
 void
-makezleparams(void)
+makezleparams(int ro)
 {
     struct zleparam *zp;
 
     for(zp = zleparams; zp->name; zp++) {
-	Param pm = createparam(zp->name, zp->type | PM_SPECIAL);
+	Param pm = createparam(zp->name, (zp->type |PM_SPECIAL|PM_REMOVABLE|
+					  (ro ? PM_READONLY : 0)));
 	if (!pm)
 	    pm = (Param) paramtab->getnode(paramtab, zp->name);
 	DPUTS(!pm, "param not set in makezleparams");
@@ -197,3 +202,17 @@ get_rbuffer(Param pm)
 {
     return metafy((char *)line + cs, ll - cs, META_HEAPDUP);
 }
+
+/**/
+static char *
+get_widget(Param pm)
+{
+    return bindk->nam;
+}
+
+/**/
+static char *
+get_lwidget(Param pm)
+{
+    return (lbindk ? lbindk->nam : "");
+}
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index 2e21b5add..629d5a84e 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -244,9 +244,7 @@ unbindwidget(Thingy t, int override)
 static void
 freewidget(Widget w)
 {
-    if ((w->flags & WIDGET_COMP) && w->u.cc)
-	freecompctl(w->u.cc);
-    else if (w->flags & WIDGET_NCOMP) {
+    if (w->flags & WIDGET_NCOMP) {
 	zsfree(w->u.comp.wid);
 	zsfree(w->u.comp.func);
     } else if(!(w->flags & WIDGET_INT))
@@ -339,7 +337,7 @@ bin_zle(char *name, char **args, char *ops, int func)
 	{ 'D', bin_zle_del,  1, -1 },
 	{ 'A', bin_zle_link, 2,  2 },
 	{ 'N', bin_zle_new,  1,  2 },
-	{ 'C', bin_zle_compctl, 1, -1},
+	{ 'C', bin_zle_complete, 3, 3 },
 	{ 'c', bin_zle_complete, 3, 3 },
 	{ 0,   bin_zle_call, 0, -1 },
     };
@@ -392,14 +390,11 @@ scanlistwidgets(HashNode hn, int list)
     if(w->flags & WIDGET_INT)
 	return;
     if(list) {
-	fputs((w->flags & WIDGET_COMP) ? "zle -C " : "zle -N ", stdout);
+	fputs("zle -N ", stdout);
 	if(t->nam[0] == '-')
 	    fputs("-- ", stdout);
 	quotedzputs(t->nam, stdout);
-	if (w->flags & WIDGET_COMP) {
-	    if (printcompctlptr && w->u.cc)
-		printcompctlptr(NULL, w->u.cc, PRINT_LIST, 0);
-	} else if (w->flags & WIDGET_NCOMP) {
+	if (w->flags & WIDGET_NCOMP) {
 	    fputc(' ', stdout);
 	    quotedzputs(w->u.comp.wid, stdout);
 	    fputc(' ', stdout);
@@ -410,11 +405,7 @@ scanlistwidgets(HashNode hn, int list)
 	}
     } else {
 	nicezputs(t->nam, stdout);
-	if (w->flags & WIDGET_COMP) {
-	    fputs(" -C", stdout);
-	    if (printcompctlptr && w->u.cc)
-		printcompctlptr(NULL, w->u.cc, PRINT_TYPE, 0);
-	} else if (w->flags & WIDGET_NCOMP) {
+	if (w->flags & WIDGET_NCOMP) {
 	    fputs(" -c ", stdout);
 	    nicezputs(w->u.comp.wid, stdout);
 	    fputc(' ', stdout);
@@ -482,44 +473,6 @@ bin_zle_new(char *name, char **args, char *ops, char func)
 
 /**/
 static int
-bin_zle_compctl(char *name, char **args, char *ops, char func)
-{
-    Compctl cc = NULL;
-    Widget w;
-    char *wname = args[0];
-
-    if (!compctl_widgetptr) {
-	zwarnnam(name, "compctl module is not loaded", NULL, 0);
-	return 1;
-    }
-
-    args++;
-
-    if (*args && !(cc = compctl_widgetptr(name, args)))
-	return 1;
-
-    w = zalloc(sizeof(*w));
-    w->flags = WIDGET_COMP|ZLE_MENUCMP|ZLE_KEEPSUFFIX;
-    w->first = NULL;
-    w->u.cc = cc;
-    if(bindwidget(w, rthingy(wname))) {
-	freewidget(w);
-	zerrnam(name, "widget name `%s' is protected", wname, 0);
-	return 1;
-    }
-    if (ops['m'])
-	w->flags |= ZLE_USEMENU;
-    else if (ops['M'])
-	w->flags |= ZLE_NOMENU;
-    if (ops['g'])
-	w->flags |= ZLE_USEGLOB;
-    else if (ops['G'])
-	w->flags |= ZLE_NOGLOB;
-    return 0;
-}
-
-/**/
-static int
 bin_zle_complete(char *name, char **args, char *ops, char func)
 {
     Thingy t;
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index a958752ca..df3e11f46 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -98,10 +98,12 @@ static int menupos, menulen, menuend, menuwe, menuinsc;
 
 /* This is for completion inside a brace expansion. brbeg and brend hold  *
  * strings that were temporarily removed from the string to complete.     *
- * brpl and brsl hold the offset of these strings.                        */
+ * brpl and brsl hold the offset of these strings.                        *
+ * brpcs and brscs hold the positions of the re-inserted string in the    *
+ * line.                                                                  */
 
 static char *brbeg = NULL, *brend = NULL;
-static int brpl, brsl;
+static int brpl, brsl, brpcs, brscs;
 
 /* The list of matches.  fmatches contains the matches we first ignore *
  * because of fignore.                                                 */
@@ -261,7 +263,6 @@ usetab(void)
 }
 
 enum { COMP_COMPLETE,
-       COMP_WIDGET,
        COMP_LIST_COMPLETE,
        COMP_SPELL,
        COMP_EXPAND,
@@ -271,18 +272,6 @@ enum { COMP_COMPLETE,
 
 /**/
 void
-completespecial(void)
-{
-    int flags = compwidget->flags;
-    usemenu = (flags & ZLE_USEMENU) ? 1 : (flags & ZLE_NOMENU) ? 0
-	: isset(MENUCOMPLETE);
-    useglob = (flags & ZLE_USEGLOB) ? 1 : (flags & ZLE_NOGLOB) ? 0
-	: isset(GLOBCOMPLETE);
-    docomplete(compwidget->u.cc ? COMP_WIDGET : COMP_COMPLETE);
-}
-
-/**/
-void
 completecall(void)
 {
     compfunc = compwidget->u.comp.func;
@@ -426,19 +415,30 @@ reversemenucomplete(void)
 void
 acceptandmenucomplete(void)
 {
-    int sl = suffixlen[' '];
-
     if (!menucmp) {
 	feep();
 	return;
     }
-    cs = menupos + menulen + menuinsc;
-    if (sl)
-	backdel(sl);
-    inststrlen(" ", 1, 1);
-    menuinsc = menulen = 0;
-    menupos = cs;
-    menuwe = 1;
+    if (brbeg && *brbeg) {
+	int l = (brscs >= 0 ? brscs : cs) - brpcs;
+
+	zsfree(brbeg);
+	brbeg = (char *) zalloc(l + 2);
+	memcpy(brbeg, line + brpcs, l);
+	brbeg[l] = ',';
+	brbeg[l + 1] = '\0';
+    } else {
+	int sl = suffixlen[' '];
+
+	cs = menupos + menulen + menuinsc;
+	if (sl)
+	    backdel(sl);
+
+	inststrlen(" ", 1, 1);
+	menuinsc = menulen = 0;
+	menupos = cs;
+	menuwe = 1;
+    }
     menucomplete();
 }
 
@@ -447,6 +447,10 @@ acceptandmenucomplete(void)
 
 static int lincmd, linredir;
 
+/* The string for the redirection operator. */
+
+static char *rdstr;
+
 /* Non-zero if the last completion done was ambiguous (used to find   *
  * out if AUTOMENU should start).  More precisely, it's nonzero after *
  * successfully doing any completion, unless the completion was       *
@@ -998,6 +1002,8 @@ get_comp_string(void)
 	    oins = ins;
 	    /* Get the next token. */
 	    ctxtlex();
+	    if (inredir)
+		rdstr = tokstrings[tok];
 	    if (tok == DINPAR)
 		tokstr = NULL;
 
@@ -1859,7 +1865,8 @@ pattern_match(Cpattern p, char *s, unsigned char *in, unsigned char *out)
 /* Do the matching for a prefix. */
 
 static char *
-match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp)
+match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp,
+	  Cmatcher nm)
 {
     static unsigned char *ea;
     static int ealen = 0;
@@ -1867,61 +1874,27 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp)
     static int rwlen;
 
     int ll = strlen(l), lw = strlen(w), mlw;
-    int il = 0, iw = 0, t, stil, stiw, std, bc = brpl;
-    char *nw = rw, *stl = NULL, *stw;
+    int il = 0, iw = 0, t, bc = brpl;
+    char *nw = rw;
     Cmlist ms;
-    Cmatcher mp, stm;
+    Cmatcher mp, lm = NULL;
     Cline lr = NULL;
 
-    *nlp = NULL;
+    if (nlp) {
+	*nlp = NULL;
 
-    if (ll > ealen) {
-	/* This is the `in'/`out' string for pattern matching. */
-	if (ealen)
-	    zfree(ea, ealen);
-	ea = (unsigned char *) zalloc(ealen = ll + 20);
+	if (ll > ealen) {
+	    /* This is the `in'/`out' string for pattern matching. */
+	    if (ealen)
+		zfree(ea, ealen);
+	    ea = (unsigned char *) zalloc(ealen = ll + 20);
+	}
     }
     while (ll && lw) {
-	if (*l == *w) {
-	    /* Same character, take it. */
-
-	    if (stl) {
-		/* But first check, if we were collecting characters *
-		 * for a `*'. */
-		int sl = iw - stiw;
-
-		nw = addtoword(&rw, &rwlen, nw, stm, stl, stw, sl, 0);
-
-		addtocline(nlp, &lr, stl, stm->llen,
-			   stw, sl, stm, (std ? CLF_SUF : 0));
-
-		stl = NULL;
-
-		if (bc <= 0 && bplp) {
-		    *bplp = nw - rw;
-		    bplp = NULL;
-		}
-	    }
-	    nw = addtoword(&rw, &rwlen, nw, NULL, NULL, l, 1, 0);
-
-	    addtocline(nlp, &lr, l, 1, NULL, 0, NULL, 0);
-
-	    l++;
-	    w++;
-	    il++;
-	    iw++;
-	    ll--;
-	    lw--;
-	    bc--;
-
-	    if (bc <= 0 && bplp) {
-		*bplp = nw - rw;
-		bplp = NULL;
-	    }
-	    continue;
-	}
 	for (ms = mstack; ms; ms = ms->next) {
 	    for (mp = ms->matcher; mp; mp = mp->next) {
+		if (nm == mp || lm == mp)
+		    continue;
 		t = 1;
 		/* Try to match the prefix, if any. */
 		if (mp->flags & CMF_LEFT) {
@@ -1947,22 +1920,42 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp)
 				else
 				    t = 0;
 			    }
-			    if (t && !stl) {
-				/* We simply keep the current position   *
-				 * and start collecting characters until *
-				 * another matcher matches. */
-				std = (mp->flags & CMF_LEFT);
-				stl = l;
-				stil = il;
-				stw = w;
-				stiw = iw;
-				stm = mp;
-				t = 0;
-				l += mp->llen;
-				il += mp->llen;
-				ll -= mp->llen;
-				
-				break;
+			    if (t) {
+				int i = 0, j = iw, k = lw;
+				int jj = il + mp->llen, kk = ll - mp->llen;
+				char *p = l + mp->llen, *q = w;
+
+				for (; k; i++, j++, k--, q++) {
+				    if (match_pfx(p, q, NULL, NULL,
+						  NULL, NULL, mp))
+					break;
+				}
+				if (k) {
+				    if (nlp) {
+					nw = addtoword(&rw, &rwlen, nw, mp,
+						       l, w, i, 0);
+					addtocline(nlp, &lr, l, mp->llen,
+						   w, i, mp, 
+						   ((mp->flags & CMF_LEFT) ?
+						    CLF_SUF : 0));
+				    }
+				    w = q;
+				    iw = j;
+				    lw = k;
+				    l = p;
+				    il = jj;
+				    ll = kk;
+				    bc -= i;
+
+				    if (bc <= 0 && bplp) {
+					*bplp = nw - rw;
+					bplp = NULL;
+				    }
+				    lm = mp;
+				    break;
+				}
+				else
+				    t = 0;
 			    }
 			    else
 				t = 0;
@@ -1988,26 +1981,10 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp)
 		if (t) {
 		    /* If it matched, build a new chunk on the Cline list *
 		     * and add the string to the built match. */
-		    if (stl) {
-			int sl = iw - stiw;
-			
-			nw = addtoword(&rw, &rwlen, nw, stm, stl, stw, sl, 0);
-			
-			addtocline(nlp, &lr, 
-				   stl, stm->llen, stw, sl, stm,
-				   (std ? CLF_SUF : 0));
-			
-			stl = NULL;
-
-			if (bc <= 0 && bplp) {
-			    *bplp = nw - rw;
-			    bplp = NULL;
-			}
+		    if (nlp) {
+			nw = addtoword(&rw, &rwlen, nw, mp, l, w, mlw, 0);
+			addtocline(nlp, &lr, l, mp->llen, w, mlw, mp, 0);
 		    }
-		    nw = addtoword(&rw, &rwlen, nw, mp, l, w, mlw, 0);
-		    
-		    addtocline(nlp, &lr, l, mp->llen, w, mlw, mp, 0);
-		    
 		    l += mp->llen;
 		    w += mlw;
 		    ll -= mp->llen;
@@ -2023,62 +2000,84 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp)
 		    break;
 		}
 	    }
-	    if (mp)
+	    if (mp) {
+		if (mp != lm)
+		    lm = NULL;
 		break;
+	    }
 	}
-	if (!stl && !t) {
-	    if (*nlp) {
+	if (t)
+	    continue;
+	if (*l == *w) {
+	    /* Same character, take it. */
+	    if (nlp) {
+		nw = addtoword(&rw, &rwlen, nw, NULL, NULL, l, 1, 0);
+		addtocline(nlp, &lr, l, 1, NULL, 0, NULL, 0);
+	    }
+	    l++;
+	    w++;
+	    il++;
+	    iw++;
+	    ll--;
+	    lw--;
+	    bc--;
+
+	    if (bc <= 0 && bplp) {
+		*bplp = nw - rw;
+		bplp = NULL;
+	    }
+	    lm = NULL;
+	} else {
+	    if (nlp && *nlp) {
 		lr->next = freecl;
 		freecl = *nlp;
 	    }
 	    return NULL;
 	}
-	if (stl) {
-	    /* We are collecting characters, just skip over. */
-	    w++;
-	    lw--;
-	    iw++;
-	}
     }
-    *lp = iw;
+    if (lp)
+	*lp = iw;
     if (lw) {
-	/* There is a unmatched portion in the word, keep it. */
-	if (rlp) {
-	    w = dupstring(w);
-	    addtocline(nlp, &lr, w, lw, w, -1, NULL, CLF_MID);
-
-	    *rlp = lr;
-	} else {
-	    addtocline(nlp, &lr, l, 0, dupstring(w), lw, NULL, CLF_END);
+	if (nlp) {
+	    /* There is a unmatched portion in the word, keep it. */
+	    if (rlp) {
+		w = dupstring(w);
+		addtocline(nlp, &lr, w, lw, w, -1, NULL, CLF_MID);
 
-	    nw = addtoword(&rw, &rwlen, nw, NULL, NULL, w, lw, 0);
+		*rlp = lr;
+	    } else {
+		addtocline(nlp, &lr, l, 0, dupstring(w), lw, NULL, CLF_END);
+		nw = addtoword(&rw, &rwlen, nw, NULL, NULL, w, lw, 0);
+	    }
 	}
-    }
-    else if (rlp) {
-	if (lr) {
+    } else if (rlp) {
+	if (nlp && lr) {
 	    lr->next = freecl;
 	    freecl = *nlp;
 	}
 	return NULL;
     }
-    if (nw)
+    if (nlp && nw)
 	*nw = '\0';
 
     if (ll) {
-	if (*nlp) {
+	if (nlp && *nlp) {
 	    lr->next = freecl;
 	    freecl = *nlp;
 	}
-	return 0;
+	return NULL;
     }
-    /* Finally, return the built match string. */
-    return dupstring(rw);
+    if (nlp)
+	/* Finally, return the built match string. */
+	return dupstring(rw);
+    
+    return ((char *) 1);
 }
 
 /* Do the matching for a suffix. */
 
 static char *
-match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp)
+match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm)
 {
     static unsigned char *ea;
     static int ealen = 0;
@@ -2086,60 +2085,29 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp)
     static int rwlen;
 
     int ll = strlen(l), lw = strlen(w), mlw;
-    int il = 0, iw = 0, t, stil, stiw, std, bc = brsl;
-    char *nw = rw, *stl = NULL, *stw;
+    int il = 0, iw = 0, t, bc = brsl;
+    char *nw = rw;
     Cmlist ms;
-    Cmatcher mp, stm;
+    Cmatcher mp, lm = NULL;
     Cline lr = NULL;
 
     l += ll;
     w += lw;
 
-    *nlp = NULL;
+    if (nlp) {
+	*nlp = NULL;
 
-    if (ll > ealen) {
-	if (ealen)
-	    zfree(ea, ealen);
-	ea = (unsigned char *) zalloc(ealen = ll + 20);
+	if (ll > ealen) {
+	    if (ealen)
+		zfree(ea, ealen);
+	    ea = (unsigned char *) zalloc(ealen = ll + 20);
+	}
     }
     while (ll && lw) {
-	if (l[-1] == w[-1]) {
-	    if (stl) {
-		int sl = iw - stiw;
-
-		stl -= stm->llen;
-		stw -= sl;
-		nw = addtoword(&rw, &rwlen, nw, stm, stl, stw, sl, 1);
-
-		addtocline(nlp, &lr, stl, stm->llen,
-			   stw, sl, stm, (std ? CLF_SUF : 0));
-
-		stl = NULL;
-
-		if (bc <= 0 && bslp) {
-		    *bslp = nw - rw;
-		    bslp = NULL;
-		}
-	    }
-	    nw = addtoword(&rw, &rwlen, nw, NULL, NULL, l - 1, 1, 1);
-
-	    addtocline(nlp, &lr, l - 1, 1, NULL, 0, NULL, 0);
-
-	    l--;
-	    w--;
-	    il++;
-	    iw++;
-	    ll--;
-	    lw--;
-	    bc--;
-	    if (bc <= 0 && bslp) {
-		*bslp = nw - rw;
-		bslp = NULL;
-	    }
-	    continue;
-	}
 	for (ms = mstack; ms; ms = ms->next) {
 	    for (mp = ms->matcher; mp; mp = mp->next) {
+		if (nm == mp || lm == mp)
+		    continue;
 		t = 1;
 		if (mp->flags & CMF_RIGHT) {
 		    if (il < mp->ralen || iw < mp->ralen)
@@ -2164,22 +2132,43 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp)
 				else
 				    t = 0;
 			    }
-			    if (t && !stl) {
-				std = (mp->flags & CMF_LEFT);
-				stl = l;
-				stil = il;
-				stw = w;
-				stiw = iw;
-				stm = mp;
-				t = 0;
-				l -= mp->llen;
-				il += mp->llen;
-				ll -= mp->llen;
-				
-				break;
+			    if (t) {
+				int i = 0, j = iw, k = lw;
+				int jj = il + mp->llen, kk = ll - mp->llen;
+				char *p = l - mp->llen, *q = w;
+
+				for (; k; i++, j++, k--, q--)
+				    if (match_sfx(p, q, NULL, NULL,
+						  NULL, mp))
+					break;
+				if (k) {
+				    if (nlp) {
+					nw = addtoword(&rw, &rwlen, nw, mp,
+						       l - mp->llen, w - i,
+						       i, 1);
+					addtocline(nlp, &lr, l - mp->llen,
+						   mp->llen, w - i, i, mp, 
+						   ((mp->flags & CMF_LEFT) ?
+						    CLF_SUF : 0));
+				    }
+				    w = q;
+				    iw = j;
+				    lw = k;
+				    l = p;
+				    il = jj;
+				    ll = kk;
+				    bc -= i;
+
+				    if (bc <= 0 && bslp) {
+					*bslp = nw - rw;
+					bslp = NULL;
+				    }
+				    lm = mp;
+				    break;
+				}
+				else
+				    t = 0;
 			    }
-			    else
-				t = 0;
 			}
 		    } else {
 			t = pattern_match(mp->line, l - mp->llen, NULL, ea) &&
@@ -2199,30 +2188,11 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp)
 			t = 0;
 		}
 		if (t) {
-		    if (stl) {
-			int sl = iw - stiw;
-			
-			stl -= stm->llen;
-			stw -= sl;
-			
-			nw = addtoword(&rw, &rwlen, nw, stm, stl, stw, sl, 1);
-			
-			addtocline(nlp, &lr,
-				   stl, stm->llen, stw, sl, stm,
-				   (std ? CLF_SUF : 0));
-			
-			stl = NULL;
-
-			if (bc <= 0 && bslp) {
-			    *bslp = nw - rw;
-			    bslp = NULL;
-			}
+		    if (nlp) {
+			nw = addtoword(&rw, &rwlen, nw, mp, l, w, mlw, 1);
+			addtocline(nlp, &lr, l - mp->llen, mp->llen,
+				   w - mlw, mlw, mp, 0);
 		    }
-		    nw = addtoword(&rw, &rwlen, nw, mp, l, w, mlw, 1);
-		    
-		    addtocline(nlp, &lr, l - mp->llen, mp->llen,
-			       w - mlw, mlw, mp, 0);
-		    
 		    l -= mp->llen;
 		    w -= mlw;
 		    ll -= mp->llen;
@@ -2237,34 +2207,55 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp)
 		    break;
 		}
 	    }
-	    if (mp)
+	    if (mp) {
+		if (mp != lm)
+		    lm = NULL;
 		break;
+	    }
 	}
-	if (!stl && !t) {
-	    if (*nlp) {
+	if (t)
+	    continue;
+	if (l[-1] == w[-1]) {
+	    if (nlp) {
+		nw = addtoword(&rw, &rwlen, nw, NULL, NULL, l - 1, 1, 1);
+		addtocline(nlp, &lr, l - 1, 1, NULL, 0, NULL, 0);
+	    }
+	    l--;
+	    w--;
+	    il++;
+	    iw++;
+	    ll--;
+	    lw--;
+	    bc--;
+	    if (bc <= 0 && bslp) {
+		*bslp = nw - rw;
+		bslp = NULL;
+	    }
+	    lm = NULL;
+	} else {
+	    if (nlp && *nlp) {
 		lr->next = freecl;
 		freecl = *nlp;
 	    }
 	    return NULL;
 	}
-	if (stl) {
-	    w--;
-	    lw--;
-	    iw++;
-	}
     }
-    *lp = iw;
-    if (nw)
+    if (lp)
+	*lp = iw;
+    if (nlp && nw)
 	*nw = '\0';
 
     if (ll) {
-	if (*nlp) {
+	if (nlp && *nlp) {
 	    lr->next = freecl;
 	    freecl = *nlp;
 	}
-	return 0;
+	return NULL;
     }
-    return dupstring(rw);
+    if (nlp)
+	return dupstring(rw);
+
+    return ((char *) 1);
 }
 
 /* Check if the word `w' matches. */
@@ -2283,8 +2274,8 @@ comp_match(char *pfx, char *sfx, char *w, Cline *clp, int qu, int *bpl, int *bsl
 	int sl;
 	Cline sli, last;
 
-	if ((p = match_pfx(pfx, w, &pli, &pl, &last, bpl))) {
-	    if ((s = match_sfx(sfx, w + pl, &sli, &sl, bsl))) {
+	if ((p = match_pfx(pfx, w, &pli, &pl, &last, bpl, NULL))) {
+	    if ((s = match_sfx(sfx, w + pl, &sli, &sl, bsl, NULL))) {
 		int pml, sml;
 
 		last->llen -= sl;
@@ -2305,7 +2296,7 @@ comp_match(char *pfx, char *sfx, char *w, Cline *clp, int qu, int *bpl, int *bsl
 	}
 	else
 	    return NULL;
-    } else if (!(r = match_pfx(pfx, w, &pli, &pl, NULL, bpl)))
+    } else if (!(r = match_pfx(pfx, w, &pli, &pl, NULL, bpl, NULL)))
 	return NULL;
 
     if (lppre && *lppre) {
@@ -2373,6 +2364,7 @@ instmatch(Cmatch m)
     if (brbeg && *brbeg) {
 	cs = a + m->brpl + (m->pre ? strlen(m->pre) : 0);
 	l = strlen(brbeg);
+	brpcs = cs;
 	inststrlen(brbeg, 1, l);
 	r += l;
 	ocs += l;
@@ -2385,12 +2377,13 @@ instmatch(Cmatch m)
     if (brend && *brend) {
 	a = cs;
 	cs -= m->brsl;
-	ocs = cs;
+	ocs = brscs = cs;
 	l = strlen(brend);
 	inststrlen(brend, 1, l);
 	r += l;
 	cs = a + l;
-    }
+    } else
+	brscs = -1;
     if (m->suf) {
 	inststrlen(m->suf, 1, (l = strlen(m->suf)));
 	r += l;
@@ -2509,6 +2502,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		if (!ai->firstm)
 		    ai->firstm = cm;
 	    }
+	    compnmatches = mnum;
 	} LASTALLOC;
     } SWITCHBACKHEAPS;
 }
@@ -3180,6 +3174,108 @@ docompletion(char *s, int lst, int incmd)
     } LASTALLOC;
 }
 
+/* This calls the given function for new style completion. */
+
+/**/
+static void
+callcompfunc(char *s, char *fn)
+{
+    List list;
+    int lv = lastval;
+    
+    if ((list = getshfunc(fn)) != &dummy_list) {
+	LinkList args = newlinklist();
+	char **p, *tmp;
+	int aadd = 0, usea = 1, icf = incompfunc, osc = sfcontext;
+	
+	addlinknode(args, fn);
+	
+	zsfree(compcontext);
+	zsfree(compcommand);
+	compcommand = "";
+	if (inwhat == IN_MATH) {
+	    if (insubscr) {
+		compcontext = "subscript";
+		compcommand = varname ? varname : "";
+	    } else
+		compcontext = "math";
+	    usea = 0;
+	} else if (lincmd)
+	    compcontext = (insubscr ? "subscript" : "command");
+	else if (linredir) {
+	    compcontext = "redirect";
+	    if (rdstr)
+		compcommand = rdstr;
+	} else
+	    switch (inwhat) {
+	    case IN_ENV:
+		compcontext = "value";
+		compcommand = varname;
+		usea = 0;
+		break;
+	    case IN_COND:
+		compcontext = "condition";
+		break;
+	    default:
+		if (cmdstr) {
+		    compcontext = "argument";
+		    compcommand = cmdstr;
+		} else {
+		    compcontext = "value";
+		    if (clwords[0])
+			compcommand = clwords[0];
+		}
+		aadd = 1;
+	    }
+	compcontext = ztrdup(compcontext);
+	tmp = quotename(compcommand, NULL, NULL, NULL);
+	untokenize(tmp);
+	compcommand = ztrdup(tmp);
+	if (usea && (!aadd || clwords[0]))
+	    for (p = clwords + aadd; *p; p++) {
+		tmp = dupstring(*p);
+		untokenize(tmp);
+		addlinknode(args, tmp);
+	    }
+	zsfree(compprefix);
+	zsfree(compsuffix);
+	if (unset(COMPLETEINWORD)) {
+	    tmp = quotename(s, NULL, NULL, NULL);
+	    untokenize(tmp);
+	    compprefix = ztrdup(tmp);
+	    compsuffix = ztrdup("");
+	} else {
+	    char *ss = s + offs, sav;
+	    
+	    tmp = quotename(s, &ss, NULL, NULL);
+	    sav = *ss;
+	    *ss = '\0';
+	    untokenize(tmp);
+	    compprefix = ztrdup(tmp);
+	    *ss = sav;
+	    untokenize(ss);
+	    compsuffix = ztrdup(ss);
+	}
+	zsfree(compiprefix);
+	compiprefix = ztrdup("");
+	compcurrent = (usea ? (clwpos + 1 - aadd) : 1);
+	compnmatches = mnum;
+	incompfunc = 1;
+	startparamscope();
+	makecompparamsptr();
+	makezleparams(1);
+	sfcontext = SFC_CWIDGET;
+	NEWHEAPS(compheap) {
+	    doshfunc(fn, list, args, 0, 1);
+	} OLDHEAPS;
+	sfcontext = osc;
+	endparamscope();
+	lastcmd = 0;
+	incompfunc = icf;
+    }
+    lastval = lv;
+}
+
 /* The beginning and end of a word range to be used by -l. */
 
 static int brange, erange;
@@ -3232,98 +3328,10 @@ makecomplist(char *s, int incmd, int lst)
 	ccused = newlinklist();
 	ccstack = newlinklist();
 
-	if (compfunc) {
-	    List list;
-	    int lv = lastval;
-
-	    if ((list = getshfunc(compfunc)) != &dummy_list) {
-		LinkList args = newlinklist();
-		char **p, *tmp;
-		int aadd = 0, usea = 1;
-
-		addlinknode(args, compfunc);
-
-		zsfree(compcontext);
-		zsfree(compcommand);
-		compcommand = "";
-		if (inwhat == IN_MATH) {
-		    if (insubscr) {
-			compcontext = "subscript";
-			compcommand = varname ? varname : "";
-		    } else
-			compcontext = "math";
-		    usea = 0;
-		} else if (lincmd)
-		    compcontext = (insubscr ? "subscript" : "command");
-		else if (linredir)
-		    compcontext = "redirect";
-		else
-		    switch (inwhat) {
-		    case IN_ENV:
-			compcontext = "value";
-			compcommand = varname;
-			usea = 0;
-			break;
-		    case IN_COND:
-			compcontext = "condition";
-			break;
-		    default:
-			if (cmdstr) {
-			    compcontext = "argument";
-			    compcommand = cmdstr;
-			} else {
-			    compcontext = "value";
-			    if (clwords[0])
-				compcommand = clwords[0];
-			}
-			aadd = 1;
-		    }
-		compcontext = ztrdup(compcontext);
-		tmp = quotename(compcommand, NULL, NULL, NULL);
-		untokenize(tmp);
-		compcommand = ztrdup(tmp);
-		if (usea && (!aadd || clwords[0]))
-		    for (p = clwords + aadd; *p; p++) {
-			tmp = dupstring(*p);
-			untokenize(tmp);
-			addlinknode(args, tmp);
-		    }
-		zsfree(compprefix);
-		zsfree(compsuffix);
-		if (unset(COMPLETEINWORD)) {
-		    tmp = quotename(s, NULL, NULL, NULL);
-		    untokenize(tmp);
-		    compprefix = ztrdup(tmp);
-		    compsuffix = ztrdup("");
-		} else {
-		    char *ss = s + offs, sav;
-
-		    tmp = quotename(s, &ss, NULL, NULL);
-		    sav = *ss;
-		    *ss = '\0';
-		    untokenize(tmp);
-		    compprefix = ztrdup(tmp);
-		    *ss = sav;
-		    untokenize(ss);
-		    compsuffix = ztrdup(ss);
-		}
-		zsfree(compiprefix);
-		compiprefix = ztrdup("");
-		compcurrent = (usea ? (clwpos + 1 - aadd) : 1);
-		compnmatches = mnum;
-		incompfunc = 1;
-		startparamscope();
-		makecompparamsptr();
-		NEWHEAPS(compheap) {
-		    doshfunc(compfunc, list, args, 0, 1);
-		} OLDHEAPS;
-		endparamscope();
-		lastcmd = 9;
-		incompfunc = 0;
-	    }
-	    lastval = lv;
-	} else
-	    makecomplistglobal(s, incmd, lst);
+	if (compfunc)
+	    callcompfunc(s, compfunc);
+	else
+	    makecomplistglobal(s, incmd, lst, 0);
 
 	endcmgroup(NULL);
 
@@ -3368,11 +3376,11 @@ ctokenize(char *p)
 	if (*p == '\\')
 	    bslash = 1;
 	else {
-	    if (*p == '$') {
+	    if (*p == '$' || *p == '=') {
 		if (bslash)
 		    p[-1] = Bnull;
 		else
-		    *p = String;
+		    *p = (*p == '$' ? String : Equals);
 	    }
 	    bslash = 0;
 	}
@@ -3431,22 +3439,75 @@ makecomplistcall(Compctl cc)
     } SWITCHBACKHEAPS;
 }
 
+/* A simple counter to avoid endless recursion between old and new style *
+ * completion. */
+
+static int cdepth = 0;
+
+#define MAX_CDEPTH 16
+
+/**/
+void
+makecomplistctl(int flags)
+{
+    if (cdepth == MAX_CDEPTH)
+	return;
+
+    cdepth++;
+    SWITCHHEAPS(compheap) {
+	HEAPALLOC {
+	    int ooffs = offs, lip, lp;
+	    char *str = comp_str(&lip, &lp), *t;
+	    char *os = cmdstr, **ow = clwords, **p, **q;
+	    int on = clwnum, op = clwpos;
+
+	    clwnum = arrlen(pparams) + 1;
+	    clwpos = compcurrent - 1;
+	    cmdstr = ztrdup(compcommand);
+	    clwords = (char **) zalloc((clwnum + 1) * sizeof(char *));
+	    clwords[0] = ztrdup(cmdstr);
+	    for (p = pparams, q = clwords + 1; *p; p++, q++) {
+		t = dupstring(*p);
+		ctokenize(t);
+		remnulargs(t);
+		*q = ztrdup(t);
+	    }
+	    *q = NULL;
+	    offs = lip + lp;
+	    incompfunc = 2;
+	    makecomplistglobal(str,
+			       (!clwpos && !strcmp(compcontext, "command")),
+			       COMP_COMPLETE, flags);
+	    incompfunc = 1;
+	    offs = ooffs;
+	    compnmatches = mnum;
+	    zsfree(cmdstr);
+	    freearray(clwords);
+	    cmdstr = os;
+	    clwords = ow;
+	    clwnum = on;
+	    clwpos = op;
+	} LASTALLOC;
+    } SWITCHBACKHEAPS;
+    cdepth--;
+}
+
 /* This function gets the compctls for the given command line and *
  * adds all completions for them. */
 
 /**/
 static void
-makecomplistglobal(char *os, int incmd, int lst)
+makecomplistglobal(char *os, int incmd, int lst, int flags)
 {
     Compctl cc;
     char *s;
 
-    if (lst == COMP_WIDGET) {
-	cc = compwidget->u.cc;
-    } else if (inwhat == IN_ENV)
+    ccont = CC_CCCONT;
+
+    if (inwhat == IN_ENV) {
         /* Default completion for parameter values. */
         cc = &cc_default;
-    else if (inwhat == IN_MATH) {
+    } else if (inwhat == IN_MATH) {
         /* Parameter names inside mathematical expression. */
         cc_dummy.mask = CC_PARAMS;
 	cc = &cc_dummy;
@@ -3469,16 +3530,17 @@ makecomplistglobal(char *os, int incmd, int lst)
 	cc = &cc_default;
     else {
 	/* Otherwise get the matches for the command. */
-	makecomplistcmd(os, incmd);
+	makecomplistcmd(os, incmd, flags);
 	cc = NULL;
     }
     if (cc) {
 	/* First, use the -T compctl. */
-	makecomplistcc(&cc_first, os, incmd);
-
-	if (!(ccont & CC_CCCONT))
-	    return;
+	if (!(flags & CFN_FIRST)) {
+	    makecomplistcc(&cc_first, os, incmd);
 
+	    if (!(ccont & CC_CCCONT))
+		return;
+	}
 	makecomplistcc(cc, os, incmd);
     }
 }
@@ -3487,18 +3549,19 @@ makecomplistglobal(char *os, int incmd, int lst)
 
 /**/
 static void
-makecomplistcmd(char *os, int incmd)
+makecomplistcmd(char *os, int incmd, int flags)
 {
     Compctl cc;
     Compctlp ccp;
     char *s;
 
     /* First, use the -T compctl. */
-    makecomplistcc(&cc_first, os, incmd);
-
-    if (!(ccont & CC_CCCONT))
-	return;
+    if (!(flags & CFN_FIRST)) {
+	makecomplistcc(&cc_first, os, incmd);
 
+	if (!(ccont & CC_CCCONT))
+	    return;
+    }
     /* Then search the pattern compctls, with the command name and the *
      * full pathname of the command. */
     makecomplistpc(os, incmd);
@@ -3526,9 +3589,11 @@ makecomplistcmd(char *os, int incmd)
 	    (cc = ccp->cc)) ||
 	   ((s = dupstring(cmdstr)) && remlpaths(&s) &&
 	    (ccp = (Compctlp) compctltab->getnode(compctltab, s)) &&
-	    (cc = ccp->cc)))))
+	    (cc = ccp->cc))))) {
+	if (flags & CFN_DEFAULT)
+	    return;
 	cc = &cc_default;
-
+    }
     makecomplistcc(cc, os, incmd);
 }
 
@@ -3817,12 +3882,12 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 
     ccont |= (cc->mask2 & (CC_CCCONT | CC_DEFCONT | CC_PATCONT));
 
-    if (!incompfunc && findnode(ccstack, cc))
+    if (incompfunc != 1 && findnode(ccstack, cc))
 	return;
 
     addlinknode(ccstack, cc);
 
-    if (!incompfunc && allccs) {
+    if (incompfunc != 1 && allccs) {
 	if (findnode(allccs, cc)) {
 	    uremnode(ccstack, firstnode(ccstack));
 	    return;
@@ -4430,45 +4495,50 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	/* Add user names. */
 	maketildelist();
     if (cc->func) {
-	/* This handles the compctl -K flag. */
-	List list;
-	char **r;
-	int lv = lastval;
-
-	/* Get the function. */
-	if ((list = getshfunc(cc->func)) != &dummy_list) {
-	    /* We have it, so build a argument list. */
-	    LinkList args = newlinklist();
-	    int osc = sfcontext;
-
-	    addlinknode(args, cc->func);
-
-	    if (delit) {
-		p = dupstrpfx(os, ooffs);
-		untokenize(p);
-		addlinknode(args, p);
-		p = dupstring(os + ooffs);
-		untokenize(p);
-		addlinknode(args, p);
-	    } else {
-		addlinknode(args, lpre);
-		addlinknode(args, lsuf);
+	if (cc->func[0] == ' ')
+	    /* Temporary hack for access to new style completione. */
+	    callcompfunc(os, cc->func + 1);
+	else {
+	    /* This handles the compctl -K flag. */
+	    List list;
+	    char **r;
+	    int lv = lastval;
+	    
+	    /* Get the function. */
+	    if ((list = getshfunc(cc->func)) != &dummy_list) {
+		/* We have it, so build a argument list. */
+		LinkList args = newlinklist();
+		int osc = sfcontext;
+		
+		addlinknode(args, cc->func);
+		
+		if (delit) {
+		    p = dupstrpfx(os, ooffs);
+		    untokenize(p);
+		    addlinknode(args, p);
+		    p = dupstring(os + ooffs);
+		    untokenize(p);
+		    addlinknode(args, p);
+		} else {
+		    addlinknode(args, lpre);
+		    addlinknode(args, lsuf);
+		}
+		
+		/* This flag allows us to use read -l and -c. */
+		if (incompfunc != 1)
+		    incompctlfunc = 1;
+		sfcontext = SFC_COMPLETE;
+		/* Call the function. */
+		doshfunc(cc->func, list, args, 0, 1);
+		sfcontext = osc;
+		incompctlfunc = 0;
+		/* And get the result from the reply parameter. */
+		if ((r = get_user_var("reply")))
+		    while (*r)
+			addmatch(*r++, NULL);
 	    }
-
-	    /* This flag allows us to use read -l and -c. */
-	    if (!incompfunc)
-		incompctlfunc = 1;
-	    sfcontext = SFC_COMPLETE;
-	    /* Call the function. */
-	    doshfunc(cc->func, list, args, 0, 1);
-	    sfcontext = osc;
-	    incompctlfunc = 0;
-	    /* And get the result from the reply parameter. */
-	    if ((r = get_user_var("reply")))
-		while (*r)
-		    addmatch(*r++, NULL);
+	    lastval = lv;
 	}
-	lastval = lv;
     }
     if (cc->mask & (CC_JOBS | CC_RUNNING | CC_STOPPED)) {
 	/* Get job names. */
@@ -4625,7 +4695,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	    }
 
 	    /* No harm in allowing read -l and -c here, too */
-	    if (!incompfunc)
+	    if (incompfunc != 1)
 		incompctlfunc = 1;
 	    sfcontext = SFC_COMPLETE;
 	    doshfunc(cc->ylist, list, args, 0, 1);
@@ -4692,7 +4762,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	    clwords += brange;
 	}
 	/* Produce the matches. */
-	makecomplistcmd(s, incmd);
+	makecomplistcmd(s, incmd, CFN_FIRST);
 
 	/* And restore the things we changed. */
 	clwords = ow;
@@ -5021,9 +5091,10 @@ permmatches(void)
 				   &nn, &nl);
 	    g->mcount = nn;
 	    g->lcount = nn - nl;
-	    if (g->ylist) 
+	    if (g->ylist) {
 		g->lcount = arrlen(g->ylist);
-
+		smatches = 2;
+	    }
 	    g->expls = (Cexpl *) makearray(g->lexpls, 0, &(g->ecount), NULL);
 
 	    g->ccount = 0;
diff --git a/Src/builtin.c b/Src/builtin.c
index f6941286d..ea1ac8ab9 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -50,7 +50,7 @@ static struct builtin builtins[] =
     BUILTIN("cd", 0, bin_cd, 0, 2, BIN_CD, NULL, NULL),
     BUILTIN("chdir", 0, bin_cd, 0, 2, BIN_CD, NULL, NULL),
     BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL),
-    BUILTIN("declare", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRUZafilrtux", NULL),
+    BUILTIN("declare", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafilrtux", NULL),
     BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "v", NULL),
     BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmr", NULL),
     BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL),
@@ -60,7 +60,7 @@ static struct builtin builtins[] =
     BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmr", NULL),
     BUILTIN("eval", BINF_PSPECIAL, bin_eval, 0, -1, BIN_EVAL, NULL, NULL),
     BUILTIN("exit", BINF_PSPECIAL, bin_break, 0, 1, BIN_EXIT, NULL, NULL),
-    BUILTIN("export", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "LRUZafilrtu", "x"),
+    BUILTIN("export", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "LRTUZafilrtu", "x"),
     BUILTIN("false", 0, bin_false, 0, -1, 0, NULL, NULL),
     BUILTIN("fc", BINF_FCOPTS, bin_fc, 0, -1, BIN_FC, "nlreIRWAdDfEim", NULL),
     BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL),
@@ -78,7 +78,7 @@ static struct builtin builtins[] =
     BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL),
     BUILTIN("kill", 0, bin_kill, 0, -1, 0, NULL, NULL),
     BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL),
-    BUILTIN("local", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRUZailrtu", NULL),
+    BUILTIN("local", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZailrtu", NULL),
     BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL),
     BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL),
 
@@ -93,7 +93,7 @@ static struct builtin builtins[] =
     BUILTIN("pwd", 0, bin_pwd, 0, 0, 0, "rLP", NULL),
     BUILTIN("r", BINF_R, bin_fc, 0, -1, BIN_FC, "nrl", NULL),
     BUILTIN("read", 0, bin_read, 0, -1, 0, "rzu0123456789pkqecnAlE", NULL),
-    BUILTIN("readonly", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRUZafiltux", "r"),
+    BUILTIN("readonly", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafiltux", "r"),
     BUILTIN("rehash", 0, bin_hash, 0, 0, 0, "dfv", "r"),
     BUILTIN("return", BINF_PSPECIAL, bin_break, 0, 1, BIN_RETURN, NULL, NULL),
     BUILTIN("set", BINF_PSPECIAL, bin_set, 0, -1, 0, NULL, NULL),
@@ -107,7 +107,7 @@ static struct builtin builtins[] =
     BUILTIN("trap", BINF_PSPECIAL, bin_trap, 0, -1, 0, NULL, NULL),
     BUILTIN("true", 0, bin_true, 0, -1, 0, NULL, NULL),
     BUILTIN("type", 0, bin_whence, 0, -1, 0, "ampfsw", "v"),
-    BUILTIN("typeset", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRUZafilrtuxm", NULL),
+    BUILTIN("typeset", BINF_TYPEOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "ALRTUZafilrtuxm", NULL),
     BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL),
     BUILTIN("unalias", 0, bin_unhash, 1, -1, 0, "m", "a"),
     BUILTIN("unfunction", 0, bin_unhash, 1, -1, 0, "m", "f"),
@@ -1468,11 +1468,11 @@ getasg(char *s)
 /* function to set a single parameter */
 
 /**/
-int
+Param
 typeset_single(char *cname, char *pname, Param pm, int func,
-	       int on, int off, int roff, char *value)
+	       int on, int off, int roff, char *value, Param altpm)
 {
-    int usepm, tc;
+    int usepm, tc, keeplocal = 0;
 
     /* use the existing pm? */
     usepm = pm && !(pm->flags & PM_UNSET);
@@ -1490,24 +1490,24 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 	locallevel != pm->level && func != BIN_EXPORT)
 	usepm = 0;
 
-    /* attempting a type conversion? */
+    /* attempting a type conversion, or making a tied colonarray? */
     if ((tc = usepm && (((off & pm->flags) | (on & ~pm->flags)) &
-			(PM_INTEGER|PM_HASHED|PM_ARRAY))))
+			(PM_INTEGER|PM_HASHED|PM_ARRAY|PM_TIED))))
 	usepm = 0;
     if (tc && (pm->flags & PM_SPECIAL)) {
 	zerrnam(cname, "%s: can't change type of a special parameter",
 		pname, 0);
-	return 1;
+	return NULL;
     }
 
     if (usepm) {
 	if (!on && !roff && !value) {
 	    paramtab->printnode((HashNode)pm, 0);
-	    return 0;
+	    return pm;
 	}
 	if ((pm->flags & PM_RESTRICTED && isset(RESTRICTED))) {
 	    zerrnam(cname, "%s: restricted", pname, 0);
-	    return 1;
+	    return pm;
 	}
 	if (PM_TYPE(pm->flags) == PM_ARRAY && (on & PM_UNIQUE) &&
 	    !(pm->flags & PM_READONLY & ~off))
@@ -1530,9 +1530,9 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 		setsparam(pname, ztrdup(value));
 	} else if (value) {
 	    zwarnnam(cname, "can't assign new value for array %s", pname, 0);
-	    return 1;
+	    return NULL;
 	}
-	return 0;
+	return pm;
     }
 
     /*
@@ -1542,10 +1542,15 @@ typeset_single(char *cname, char *pname, Param pm, int func,
      * last case only, we need to delete the old parameter.
      */
     if (tc) {
-	if (pm->flags & PM_READONLY) {
-	    on |= ~off & PM_READONLY;
-	    pm->flags &= ~PM_READONLY;
-	}
+	/* Maintain existing readonly/exported status... */
+	on |= ~off & (PM_READONLY|PM_EXPORTED) & pm->flags;
+	/* ...but turn off existing readonly so we can delete it */
+	pm->flags &= ~PM_READONLY;
+	/*
+	 * If we're just changing the type, we should keep the
+	 * variable at the current level of localness.
+	 */
+	keeplocal = pm->level;
 	/*
 	 * Try to carry over a value, but not when changing from,
 	 * to, or between non-scalar types.
@@ -1563,17 +1568,33 @@ typeset_single(char *cname, char *pname, Param pm, int func,
     pm = createparam(pname, on & ~PM_READONLY);
     DPUTS(!pm, "BUG: parameter not created");
     pm->ct = auxlen;
-    if (func != BIN_EXPORT)
+
+    if (altpm && PM_TYPE(pm->flags) == PM_SCALAR) {
+	/*
+	 * It seems safer to set this here than in createparam(),
+	 * to make sure we only ever use the colonarr functions
+	 * when u.data is correctly set.
+	 */
+	pm->sets.cfn = colonarrsetfn;
+	pm->gets.cfn = colonarrgetfn;
+	pm->u.data = &altpm->u.arr;
+    }
+
+    if (keeplocal)
+	pm->level = keeplocal;
+    else if (func != BIN_EXPORT)
 	pm->level = locallevel;
     if (value && !(pm->flags & (PM_ARRAY|PM_HASHED)))
 	setsparam(pname, ztrdup(value));
     pm->flags |= (on & PM_READONLY);
     if (value && (pm->flags & (PM_ARRAY|PM_HASHED))) {
 	zerrnam(cname, "%s: can't assign initial value for array", pname, 0);
-	return 1;
+	/* the only safe thing to do here seems to be unset the param */
+	unsetparam_pm(pm, 0, 1);
+	return NULL;
     }
 
-    return 0;
+    return pm;
 }
 
 /* declare, export, integer, local, readonly, typeset */
@@ -1585,7 +1606,7 @@ bin_typeset(char *name, char **argv, char *ops, int func)
     Param pm;
     Asgment asg;
     Comp com;
-    char *optstr = "aiALRZlurtxU";
+    char *optstr = "aiALRZlurtxUT";
     int on = 0, off = 0, roff, bit = PM_ARRAY;
     int i;
     int returnval = 0, printflags = 0;
@@ -1619,6 +1640,9 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 	off |= PM_UPPER;
     if (on & PM_HASHED)
 	off |= PM_ARRAY;
+    if (on & PM_TIED)
+	off |= PM_INTEGER | PM_ARRAY | PM_HASHED;
+
     on &= ~off;
 
     /* Given no arguments, list whatever the options specify. */
@@ -1631,6 +1655,58 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 	return 0;
     }
 
+    if (on & PM_TIED) {
+	Param apm;
+	char *name1;
+
+	if (ops['m']) {
+	    zwarnnam(name, "incompatible options for -T", NULL, 0);
+	    return 1;
+	}
+	on &= ~off;
+	if (!argv[1] || argv[2]) {
+	    zwarnnam(name, "-T requires names of scalar and array", NULL, 0);
+	    return 1;
+	}
+
+	/*
+	 * Create the tied array; this is normal except that
+	 * it has the PM_TIED flag set.  Do it first because
+	 * we need the address.
+	 */
+	if (!(asg = getasg(argv[1])))
+	    return 1;
+	name1 = ztrdup(asg->name);
+	if (!(apm=typeset_single(name, asg->name,
+				 (Param)paramtab->getnode(paramtab,
+							  asg->name),
+				 func, on | PM_ARRAY, off, roff,
+				 asg->value, NULL)))
+	    return 1;
+
+	/*
+	 * Create the tied colonarray.  We make it as a normal scalar
+	 * and fix up the oddities later.
+	 */
+	if (!(asg = getasg(argv[0])) ||
+	    !(pm=typeset_single(name, asg->name,
+				(Param)paramtab->getnode(paramtab,
+							 asg->name),
+				func, on, off, roff, asg->value, apm))) {
+	    unsetparam_pm(apm, 1, 1);
+	    return 1;
+	}
+
+	pm->ename = name1;
+	apm->ename = ztrdup(asg->name);
+
+	return 0;
+    }
+    if (off & PM_TIED) {
+	zerrnam(name, "use unset to remove tied variables", NULL, 0);
+	return 1;
+    }
+
     /* With the -m option, treat arguments as glob patterns */
     if (ops['m']) {
 	MUSTUSEHEAP("typeset -m");
@@ -1664,8 +1740,8 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 	    }
 	    for (pmnode = firstnode(pmlist); pmnode; incnode(pmnode)) {
 		pm = (Param) getdata(pmnode);
-		if (typeset_single(name, pm->nam, pm, func, on, off, roff,
-				   asg->value))
+		if (!typeset_single(name, pm->nam, pm, func, on, off, roff,
+				    asg->value, NULL))
 		    returnval = 1;
 	    }
 	}
@@ -1680,9 +1756,9 @@ bin_typeset(char *name, char **argv, char *ops, int func)
 	    returnval = 1;
 	    continue;
 	}
-	if (typeset_single(name, asg->name,
-			   (Param)paramtab->getnode(paramtab, asg->name),
-			   func, on, off, roff, asg->value))
+	if (!typeset_single(name, asg->name,
+			    (Param)paramtab->getnode(paramtab, asg->name),
+			    func, on, off, roff, asg->value, NULL))
 	    returnval = 1;
     }
     return returnval;
diff --git a/Src/glob.c b/Src/glob.c
index 6536e0f06..7a3839576 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -31,7 +31,42 @@
 #include "glob.pro"
 
 /* flag for CSHNULLGLOB */
- 
+
+typedef struct gmatch *Gmatch; 
+
+struct gmatch {
+    char *name;
+    long size;
+    long atime;
+    long mtime;
+    long ctime;
+    long links;
+    long _size;
+    long _atime;
+    long _mtime;
+    long _ctime;
+    long _links;
+};
+
+#define GS_NAME   1
+#define GS_SIZE   2
+#define GS_ATIME  4
+#define GS_MTIME  8
+#define GS_CTIME 16
+#define GS_LINKS 32
+
+#define GS_SHIFT  5
+#define GS__SIZE  (GS_SIZE << GS_SHIFT)
+#define GS__ATIME (GS_ATIME << GS_SHIFT)
+#define GS__MTIME (GS_MTIME << GS_SHIFT)
+#define GS__CTIME (GS_CTIME << GS_SHIFT)
+#define GS__LINKS (GS_LINKS << GS_SHIFT)
+
+#define GS_DESC  2048
+
+#define GS_NORMAL (GS_SIZE | GS_ATIME | GS_MTIME | GS_CTIME | GS_LINKS)
+#define GS_LINKED (GS_NORMAL << GS_SHIFT)
+
 /**/
 int badcshglob;
  
@@ -42,8 +77,8 @@ static int matchct;		/* number of matches found              */
 static char *pathbuf;		/* pathname buffer                      */
 static int pathbufsz;		/* size of pathbuf			*/
 static int pathbufcwd;		/* where did we chdir()'ed		*/
-static char **matchbuf;		/* array of matches                     */
-static char **matchptr;		/* &matchbuf[matchct]                   */
+static Gmatch matchbuf;		/* array of matches                     */
+static Gmatch matchptr;		/* &matchbuf[matchct]                   */
 static char *colonmod;		/* colon modifiers in qualifier list    */
 
 typedef struct stat *Statptr;	 /* This makes the Ultrix compiler happy.  Go figure. */
@@ -81,6 +116,7 @@ static struct qual *quals;
 static int qualct, qualorct;
 static int range, amc, units;
 static int gf_nullglob, gf_markdirs, gf_noglobdots, gf_listtypes, gf_follow;
+static int gf_sorts, gf_nsorts, gf_sortlist[11];
 
 /* Prefix, suffix for doing zle trickery */
 
@@ -213,6 +249,7 @@ insert(char *s, int checked)
 
 	if (!statted && statfullpath(s, &buf, 1))
 	    return;
+	statted = 1;
 	qo = quals;
 	for (qn = qo; qn && qn->func;) {
 	    range = qn->range;
@@ -237,19 +274,44 @@ insert(char *s, int checked)
 	    }
 	    qn = qn->next;
 	}
-    } else if (!checked && statfullpath(s, NULL, 1))
-	return;
-
+    } else if (!checked) {
+	if (statfullpath(s, NULL, 1))
+	    return;
+	statted = 1;
+    }
     news = dyncat(pathbuf, news);
     if (colonmod) {
 	/* Handle the remainder of the qualifer:  e.g. (:r:s/foo/bar/). */
 	s = colonmod;
 	modify(&news, &s);
     }
-    *matchptr++ = news;
+    if (!statted && (gf_sorts & GS_NORMAL)) {
+	statfullpath(s, &buf, 1);
+	statted = 1;
+    }
+    if (statted != 2 && (gf_sorts & GS_LINKED)) {
+	if (statted) {
+	    if (!S_ISLNK(buf.st_mode) || statfullpath(s, &buf2, 0))
+		memcpy(&buf2, &buf, sizeof(buf));
+	} else if (statfullpath(s, &buf2, 0))
+	    statfullpath(s, &buf2, 1);
+    }
+    matchptr->name = news;
+    matchptr->size = buf.st_size;
+    matchptr->atime = buf.st_atime;
+    matchptr->mtime = buf.st_mtime;
+    matchptr->ctime = buf.st_ctime;
+    matchptr->links = buf.st_nlink;
+    matchptr->_size = buf2.st_size;
+    matchptr->_atime = buf2.st_atime;
+    matchptr->_mtime = buf2.st_mtime;
+    matchptr->_ctime = buf2.st_ctime;
+    matchptr->_links = buf2.st_nlink;
+    matchptr++;
+
     if (++matchct == matchsz) {
-	matchbuf = (char **)realloc((char *)matchbuf,
-				    sizeof(char **) * (matchsz *= 2));
+	matchbuf = (Gmatch )realloc((char *)matchbuf,
+				    sizeof(struct gmatch) * (matchsz *= 2));
 
 	matchptr = matchbuf + matchct;
     }
@@ -944,21 +1006,144 @@ qgetnum(char **s)
     return v;
 }
 
-/* get octal number after qualifier */
+/* get mode spec after qualifier */
 
 /**/
 static long
-qgetoctnum(char **s)
+qgetmodespec(char **s)
 {
-    long v = 0;
+    long yes = 0, no = 0, val, mask, t;
+    char *p = *s, c, how, end;
 
-    if (!idigit(**s)) {
-	zerr("octal number expected", NULL, 0);
-	return 0;
+    if ((c = *p) == '=' || c == Equals || c == '+' || c == '-' ||
+	c == '?' || c == Quest || (c >= '0' && c <= '7')) {
+	end = 0;
+	c = 0;
+    } else {
+	end = (c == '<' ? '>' :
+	       (c == '[' ? ']' :
+		(c == '{' ? '}' :
+		 (c == Inang ? Outang :
+		  (c == Inbrack ? Outbrack :
+		   (c == Inbrace ? Outbrace : c))))));
+	p++;
     }
-    while (**s >= '0' && **s <= '7')
-	v = v * 010 + *(*s)++ - '0';
-    return v;
+    do {
+	mask = 0;
+	while (((c = *p) == 'u' || c == 'g' || c == 'o' || c == 'a') && end) {
+	    switch (c) {
+	    case 'o': mask |= 01007; break;
+	    case 'g': mask |= 02070; break;
+	    case 'u': mask |= 04700; break;
+	    case 'a': mask |= 07777; break;
+	    }
+	    p++;
+	}
+	how = ((c == '+' || c == '-') ? c : '=');
+	if (c == '+' || c == '-' || c == '=' || c == Equals)
+	    p++;
+	val = 0;
+	if (mask) {
+	    while ((c = *p++) != ',' && c != end) {
+		switch (c) {
+		case 'x': val |= 00111; break;
+		case 'w': val |= 00222; break;
+		case 'r': val |= 00444; break;
+		case 's': val |= 06000; break;
+		case 't': val |= 01000; break;
+		case '0': case '1': case '2': case '3':
+		case '4': case '5': case '6': case '7':
+		    t = ((long) c - '0');
+		    val |= t | (t << 3) | (t << 6);
+		    break;
+		default:
+		    zerr("invalid mode specification", NULL, 0);
+		    return 0;
+		}
+	    }
+	    if (how == '=' || how == '+') {
+		yes |= val & mask;
+		val = ~val;
+	    }
+	    if (how == '=' || how == '-')
+		no |= val & mask;
+	} else {
+	    t = 07777;
+	    while ((c = *p) == '?' || c == Quest ||
+		   (c >= '0' && c <= '7')) {
+		if (c == '?' || c == Quest) {
+		    t = (t << 3) | 7;
+		    val <<= 3;
+		} else {
+		    t <<= 3;
+		    val = (val << 3) | ((long) c - '0');
+		}
+		p++;
+	    }
+	    if (end && c != end && c != ',') {
+		zerr("invalid mode specification", NULL, 0);
+		return 0;
+	    }
+	    if (how == '=') {
+		yes = (yes & ~t) | val;
+		no = (no & ~t) | (~val & ~t);
+	    } else if (how == '+')
+		yes |= val;
+	    else
+		no |= val;
+	}
+    } while (end && c != end);
+
+    *s = p;
+    return ((yes & 07777) | ((no & 07777) << 12));
+}
+
+static int
+gmatchcmp(Gmatch a, Gmatch b)
+{
+    int i, *s;
+    long r;
+
+    for (i = gf_nsorts, s = gf_sortlist; i; i--, s++) {
+	switch (*s & ~GS_DESC) {
+	case GS_NAME:
+	    r = notstrcmp(&a->name, &b->name);
+	    break;
+	case GS_SIZE:
+	    r = b->size - a->size;
+	    break;
+	case GS_ATIME:
+	    r = a->atime - b->atime;
+	    break;
+	case GS_MTIME:
+	    r = a->mtime - b->mtime;
+	    break;
+	case GS_CTIME:
+	    r = a->ctime - b->ctime;
+	    break;
+	case GS_LINKS:
+	    r = b->links - a->links;
+	    break;
+	case GS__SIZE:
+	    r = b->_size - a->_size;
+	    break;
+	case GS__ATIME:
+	    r = a->_atime - b->_atime;
+	    break;
+	case GS__MTIME:
+	    r = a->_mtime - b->_mtime;
+	    break;
+	case GS__CTIME:
+	    r = a->_ctime - b->_ctime;
+	    break;
+	case GS__LINKS:
+	    r = b->_links - a->_links;
+	    break;
+	}
+	if (r)
+	    return (int) ((*s & GS_DESC) ? -r : r);
+    }
+    return 0;
 }
 
 /* Main entry point to the globbing code for filename globbing. *
@@ -976,7 +1161,8 @@ glob(LinkList list, LinkNode np)
     Complist q;				/* pattern after parsing         */
     char *ostr = (char *)getdata(np);	/* the pattern before the parser */
 					/* chops it up                   */
-
+    int first = 0, last = -1;		/* index of first/last match to  */
+				        /* return */
     MUSTUSEHEAP("glob");
     if (unset(GLOBOPT) || !haswilds(ostr)) {
 	untokenize(ostr);
@@ -994,6 +1180,7 @@ glob(LinkList list, LinkNode np)
     gf_markdirs = isset(MARKDIRS);
     gf_listtypes = gf_follow = 0;
     gf_noglobdots = unset(GLOBDOTS);
+    gf_sorts = gf_nsorts = 0;
 
     /* Check for qualifiers */
     if (isset(BAREGLOBQUAL) && str[sl - 1] == Outpar) {
@@ -1246,10 +1433,9 @@ glob(LinkList list, LinkNode np)
 			}
 			break;
 		    case 'o':
-			/* Match octal mode of file exactly. *
-			 * Currently undocumented.           */
-			func = qualeqflags;
-			data = qgetoctnum(&s);
+			/* Match modes with chmod-spec. */
+			func = qualmodeflags;
+			data = qgetmodespec(&s);
 			break;
 		    case 'M':
 			/* Mark directories with a / */
@@ -1315,6 +1501,51 @@ glob(LinkList list, LinkNode np)
 			data = qgetnum(&s);
 			break;
 
+		    case 'O':
+			{
+			    int t;
+
+			    switch (*s) {
+			    case 'n': t = GS_NAME; break;
+			    case 'L': t = GS_SIZE; break;
+			    case 'l': t = GS_LINKS; break;
+			    case 'a': t = GS_ATIME; break;
+			    case 'm': t = GS_MTIME; break;
+			    case 'c': t = GS_CTIME; break;
+			    default:
+				zerr("unknown sort specifier", NULL, 0);
+				return;
+			    }
+			    if ((sense & 2) && t != GS_NAME)
+				t <<= GS_SHIFT;
+			    if (gf_sorts & t) {
+				zerr("doubled sort specifier", NULL, 0);
+				return;
+			    }
+			    gf_sorts |= t;
+			    gf_sortlist[gf_nsorts++] = t |
+				((sense & 1) ? GS_DESC : 0);
+			    s++;
+			    break;
+			}
+		    case '[':
+		    case Inbrack:
+			{
+			    char *os = --s;
+			    struct value v;
+
+			    v.isarr = SCANPM_WANTVALS;
+			    v.pm = NULL;
+			    v.b = -1;
+			    v.inv = 0;
+			    if (getindex(&s, &v) || s == os) {
+				zerr("invalid subscript", NULL, 0);
+				return;
+			    }
+			    first = v.a;
+			    last = v.b;
+			    break;
+			}
 		    default:
 			zerr("unknown file attribute", NULL, 0);
 			return;
@@ -1353,10 +1584,14 @@ glob(LinkList list, LinkNode np)
 	zerr("bad pattern: %s", ostr, 0);
 	return;
     }
-
+    if (!gf_nsorts) {
+	gf_sortlist[0] = gf_sorts = GS_NAME;
+	gf_nsorts = 1;
+    }
     /* Initialise receptacle for matched files, *
      * expanded by insert() where necessary.    */
-    matchptr = matchbuf = (char **)zalloc((matchsz = 16) * sizeof(char *));
+    matchptr = matchbuf = (Gmatch)zalloc((matchsz = 16) *
+					 sizeof(struct gmatch));
     matchct = 0;
 
     /* The actual processing takes place here: matches go into  *
@@ -1375,18 +1610,32 @@ glob(LinkList list, LinkNode np)
 	    return;
 	} else {
 	    /* treat as an ordinary string */
-	    untokenize(*matchptr++ = dupstring(ostr));
+	    untokenize(matchptr->name = dupstring(ostr));
+	    matchptr++;
 	    matchct = 1;
 	}
     }
     /* Sort arguments in to lexical (and possibly numeric) order. *
      * This is reversed to facilitate insertion into the list.    */
-    qsort((void *) & matchbuf[0], matchct, sizeof(char *),
-	       (int (*) _((const void *, const void *)))notstrcmp);
-
-    matchptr = matchbuf;
-    while (matchct--)		/* insert matches in the arg list */
-	insertlinknode(list, node, *matchptr++);
+    qsort((void *) & matchbuf[0], matchct, sizeof(struct gmatch),
+	       (int (*) _((const void *, const void *)))gmatchcmp);
+
+    if (first < 0)
+	first += matchct;
+    if (last < 0)
+	last += matchct;
+    if (first < 0)
+	first = 0;
+    if (last >= matchct)
+	last = matchct - 1;
+    if (first <= last) {
+	matchptr = matchbuf + matchct - 1 - last;
+	last -= first;
+	while (last-- >= 0) {		/* insert matches in the arg list */
+	    insertlinknode(list, node, matchptr->name);
+	    matchptr++;
+	}
+    }
     free(matchbuf);
 }
 
@@ -2918,13 +3167,15 @@ qualflags(struct stat *buf, long mod)
     return mode_to_octal(buf->st_mode) & mod;
 }
 
-/* mode matches number supplied exactly  */
+/* mode matches specification */
 
 /**/
 static int
-qualeqflags(struct stat *buf, long mod)
+qualmodeflags(struct stat *buf, long mod)
 {
-    return mode_to_octal(buf->st_mode) == mod;
+    long v = mode_to_octal(buf->st_mode), y = mod & 07777, n = mod >> 12;
+
+    return ((v & y) == y && !(v & n));
 }
 
 /* regular executable file? */
diff --git a/Src/init.c b/Src/init.c
index 0c874eead..5e0a550dd 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -300,18 +300,61 @@ init_io(void)
 
     /* Make sure the tty is opened read/write. */
     if (isatty(0)) {
+#ifdef TIOCNXCL
+	/*
+	 * See if the terminal claims to be busy.  If so, and fd 0
+	 * is a terminal, try and set non-exclusive use for that.
+	 * This is something to do with Solaris over-cleverness.
+	 */
+	int tmpfd;
+	if ((tmpfd = open("/dev/tty", O_RDWR | O_NOCTTY)) < 0) {
+	    if (errno == EBUSY)
+		ioctl(0, TIOCNXCL, 0);
+	} else
+	    close(tmpfd);
+#endif
 	zsfree(ttystrname);
 	if ((ttystrname = ztrdup(ttyname(0))))
 	    SHTTY = movefd(open(ttystrname, O_RDWR | O_NOCTTY));
+	/*
+	 * xterm, rxvt and probably all terminal emulators except
+	 * dtterm on Solaris 2.6 & 7 have a bug. Applications are
+	 * unable to open /dev/tty or /dev/pts/<terminal number here>
+	 * because something in Sun's STREAMS modules doesn't like
+	 * it. The open() call fails with EBUSY which is not even
+	 * listed as a possibility in the open(2) man page.  So we'll
+	 * try to outsmart The Company.  -- <dave@srce.hr>
+	 *
+	 * Presumably there's no harm trying this on any OS, given that
+	 * isatty(0) worked but opening the tty didn't.  Possibly we won't
+	 * get the tty read/write, but it's the best we can do -- pws
+	 *
+	 * Try both stdin and stdout before trying /dev/tty. -- Bart
+	 */
+#if defined(HAVE_FCNTL_H) && defined(F_GETFL)
+#define rdwrtty(fd)	((fcntl(fd, F_GETFL) & O_RDWR) == O_RDWR)
+#else
+#define rdwrtty(fd)	1
+#endif
+	if (SHTTY == -1 && rdwrtty(0)) {
+	    SHTTY = movefd(dup(0));
+	}
+    }
+    if (SHTTY == -1 && isatty(1) && rdwrtty(1) &&
+	(SHTTY = movefd(dup(1))) != -1) {
+	zsfree(ttystrname);
+	ttystrname = ztrdup(ttyname(1));
     }
     if (SHTTY == -1 &&
 	(SHTTY = movefd(open("/dev/tty", O_RDWR | O_NOCTTY))) != -1) {
 	zsfree(ttystrname);
-	ttystrname = ztrdup("/dev/tty");
+	ttystrname = ztrdup(ttyname(SHTTY));
     }
     if (SHTTY == -1) {
 	zsfree(ttystrname);
 	ttystrname = ztrdup("");
+    } else if (!ttystrname) {
+	ttystrname = ztrdup("/dev/tty");
     }
 
     /* We will only use zle if shell is interactive, *
diff --git a/Src/lex.c b/Src/lex.c
index b08dfed5b..7371243a7 100644
--- a/Src/lex.c
+++ b/Src/lex.c
@@ -109,7 +109,8 @@ int parend;
  
 /* text of puctuation tokens */
 
-static char *tokstrings[WHILE + 1] = {
+/**/
+char *tokstrings[WHILE + 1] = {
     NULL,	/* NULLTOK	  0  */
     ";",	/* SEPER	     */
     "\\n",	/* NEWLIN	     */
@@ -120,7 +121,7 @@ static char *tokstrings[WHILE + 1] = {
     ")",	/* OUTPAR	     */
     "||",	/* DBAR		     */
     "&&",	/* DAMPER	     */
-    ")",	/* OUTANG	  10 */
+    ">",	/* OUTANG	  10 */
     ">|",	/* OUTANGBANG	     */
     ">>",	/* DOUTANG	     */
     ">>|",	/* DOUTANGBANG	     */
diff --git a/Src/params.c b/Src/params.c
index 3dc7e1a1b..f57413a2e 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -679,7 +679,7 @@ getarg(char **str, int *inv, Value v, int a2, long *w)
     Comp c;
 
     /* first parse any subscription flags */
-    if (*s == '(' || *s == Inpar) {
+    if (v->pm && (*s == '(' || *s == Inpar)) {
 	int escapes = 0;
 	int waste;
 	for (s++; *s != ')' && *s != Outpar && s != *str; s++) {
@@ -765,7 +765,7 @@ getarg(char **str, int *inv, Value v, int a2, long *w)
 	} else if (rev) {
 	    v->isarr |= SCANPM_WANTVALS;
 	}
-	if (!down && PM_TYPE(v->pm->flags) == PM_HASHED)
+	if (!down && v->pm && PM_TYPE(v->pm->flags) == PM_HASHED)
 	    v->isarr &= ~SCANPM_MATCHMANY;
 	*inv = ind;
     }
@@ -785,7 +785,7 @@ getarg(char **str, int *inv, Value v, int a2, long *w)
     singsub(&s);
 
     if (!rev) {
-	if (PM_TYPE(v->pm->flags) == PM_HASHED) {
+	if (v->pm && PM_TYPE(v->pm->flags) == PM_HASHED) {
 	    HashTable ht = v->pm->gets.hfn(v->pm);
 	    if (!ht) {
 		ht = newparamtable(17, v->pm->nam);
@@ -1371,7 +1371,7 @@ setarrvalue(Value v, char **val)
 	freearray(val);
 	return;
     }
-    if (PM_TYPE(v->pm->flags) & ~(PM_ARRAY|PM_HASHED)) {
+    if (!(PM_TYPE(v->pm->flags) & (PM_ARRAY|PM_HASHED))) {
 	freearray(val);
 	zerr("attempt to assign array value to non-array", NULL, 0);
 	return;
@@ -1501,7 +1501,7 @@ setsparam(char *s, char *val)
 	if (!(v = getvalue(&s, 1)))
 	    createparam(t, PM_SCALAR);
 	else if (PM_TYPE(v->pm->flags) == PM_ARRAY &&
-		 !(v->pm->flags & PM_SPECIAL) && unset(KSHARRAYS)) {
+		 !(v->pm->flags & (PM_SPECIAL|PM_TIED)) && unset(KSHARRAYS)) {
 	    unsetparam(t);
 	    createparam(t, PM_SCALAR);
 	    v = NULL;
@@ -1545,7 +1545,7 @@ setaparam(char *s, char **val)
 	if (!(v = getvalue(&s, 1)))
 	    createparam(t, PM_ARRAY);
 	else if (!(PM_TYPE(v->pm->flags) & (PM_ARRAY|PM_HASHED)) &&
-		 !(v->pm->flags & PM_SPECIAL)) {
+		 !(v->pm->flags & (PM_SPECIAL|PM_TIED))) {
 	    int uniq = v->pm->flags & PM_UNIQUE;
 	    unsetparam(t);
 	    createparam(t, PM_ARRAY | uniq);
@@ -1662,26 +1662,36 @@ unsetparam_pm(Param pm, int altflag, int exp)
 	    unsetparam_pm(altpm, 1, exp);
     }
 
-    /* If this was a local variable, we need to keep the old     *
-     * struct so that it is resurrected at the right level.      *
-     * This is partly because when an array/scalar value is set  *
-     * and the parameter used to be the other sort, unsetparam() *
-     * is called.  Beyond that, there is an ambiguity:  should   *
-     * foo() { local bar; unset bar; } make the global bar       *
-     * available or not?  The following makes the answer "no".   */
-    if ((locallevel && locallevel >= pm->level) || (pm->flags & PM_SPECIAL))
+    /*
+     * If this was a local variable, we need to keep the old
+     * struct so that it is resurrected at the right level.
+     * This is partly because when an array/scalar value is set
+     * and the parameter used to be the other sort, unsetparam()
+     * is called.  Beyond that, there is an ambiguity:  should
+     * foo() { local bar; unset bar; } make the global bar
+     * available or not?  The following makes the answer "no".
+     *
+     * 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) ||
+	(pm->flags & (PM_SPECIAL|PM_REMOVABLE)) == PM_SPECIAL)
 	return;
 
-    paramtab->removenode(paramtab, pm->nam); /* remove parameter node from table */
+    /* remove parameter node from table */
+    paramtab->removenode(paramtab, pm->nam);
 
     if (pm->old) {
 	oldpm = pm->old;
 	paramtab->addnode(paramtab, oldpm->nam, oldpm);
-	if ((PM_TYPE(oldpm->flags) == PM_SCALAR) && oldpm->sets.cfn == strsetfn)
+	if ((PM_TYPE(oldpm->flags) == PM_SCALAR) &&
+	    oldpm->sets.cfn == strsetfn)
 	    adduserdir(oldpm->nam, oldpm->u.str, 0, 0);
     }
 
-    paramtab->freenode((HashNode) pm); /* free parameter node */
+    /* Even removable specials shouldn't be deleted. */
+    if (!(pm->flags & PM_SPECIAL))
+	paramtab->freenode((HashNode) pm); /* free parameter node */
 }
 
 /* Standard function to unset a parameter.  This is mostly delegated to *
@@ -1759,6 +1769,9 @@ arrsetfn(Param pm, char **x)
     if (pm->flags & PM_UNIQUE)
 	uniqarray(x);
     pm->u.arr = x;
+    /* Arrays tied to colon-arrays may need to fix the environment */
+    if (pm->ename && x)
+	arrfixenv(pm->ename, x);
 }
 
 /* Function to get value of an association parameter */
@@ -1950,7 +1963,8 @@ arrvarsetfn(Param pm, char **x)
 char *
 colonarrgetfn(Param pm)
 {
-    return zjoin(*(char ***)pm->u.data, ':');
+    char ***dptr = (char ***)pm->u.data;
+    return *dptr ? zjoin(*dptr, ':') : "";
 }
 
 /**/
@@ -1959,8 +1973,15 @@ colonarrsetfn(Param pm, char *x)
 {
     char ***dptr = (char ***)pm->u.data;
 
-    freearray(*dptr);
-    *dptr = x ? colonsplit(x, pm->flags & PM_UNIQUE) : mkarray(NULL);
+    /*
+     * If this is tied to a parameter (rather than internal) array,
+     * the array itself may be NULL.  Otherwise, we have to make
+     * sure it doesn't ever get null.
+     */
+    if (*dptr)
+	freearray(*dptr);
+    *dptr = x ? colonsplit(x, pm->flags & PM_UNIQUE) :
+	(pm->flags & PM_TIED) ? NULL : mkarray(NULL);
     if (pm->ename)
 	arrfixenv(pm->nam, *dptr);
     zsfree(x);
@@ -2399,7 +2420,7 @@ arrfixenv(char *s, char **t)
     MUSTUSEHEAP("arrfixenv");
     if (t == path)
 	cmdnamtab->emptytable(cmdnamtab);
-    u = zjoin(t, ':');
+    u = t ? zjoin(t, ':') : "";
     len_s = strlen(s);
     pm = (Param) paramtab->getnode(paramtab, s);
     for (ep = environ; *ep; ep++)
@@ -2602,6 +2623,9 @@ freeparamnode(HashNode hn)
     if (delunset)
 	pm->unsetfn(pm, 1);
     zsfree(pm->nam);
+    /* If this variable was tied by the user, ename was ztrdup'd */
+    if (pm->flags & PM_TIED)
+	zsfree(pm->ename);
     zfree(pm, sizeof(struct param));
 }
 
diff --git a/Src/subst.c b/Src/subst.c
index 5211ee0ee..b77dbd4a4 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -702,6 +702,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
     int whichlen = 0;
     int chkset = 0;
     int vunset = 0;
+    int wantt = 0;
     int spbreak = isset(SHWORDSPLIT) && !ssub && !qt;
     char *val = NULL, **aval = NULL;
     unsigned int fwidth = 0;
@@ -902,6 +903,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 		    hvals = SCANPM_WANTVALS;
 		    break;
 
+		case 't':
+		    wantt = 1;
+		    break;
+
 		default:
 		  flagerr:
 		    zerr("error in flags", NULL, 0);
@@ -978,9 +983,47 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int ssub)
 	*s = sav;
 	v = (Value) NULL;
     } else {
-	if (!(v = fetchvalue(&s, (unset(KSHARRAYS) || inbrace) ? 1 : -1,
+	if (!(v = fetchvalue(&s, (wantt ? -1 :
+				  ((unset(KSHARRAYS) || inbrace) ? 1 : -1)),
 			     hkeys|hvals)))
 	    vunset = 1;
+
+	if (wantt) {
+	    if (v) {
+		int f = v->pm->flags;
+
+		switch (PM_TYPE(f)) {
+		case PM_SCALAR:  val = "scalar"; break;
+		case PM_ARRAY:   val = "array"; break;
+		case PM_INTEGER: val = "integer"; break;
+		case PM_HASHED:  val = "association"; break;
+		}
+		val = dupstring(val);
+		if (f & PM_LEFT)
+		    val = dyncat(val, "-left");
+		if (f & PM_RIGHT_B)
+		    val = dyncat(val, "-right_blanks");
+		if (f & PM_RIGHT_Z)
+		    val = dyncat(val, "-right_zeros");
+		if (f & PM_LOWER)
+		    val = dyncat(val, "-lower");
+		if (f & PM_UPPER)
+		    val = dyncat(val, "-upper");
+		if (f & PM_READONLY)
+		    val = dyncat(val, "-readonly");
+		if (f & PM_TAGGED)
+		    val = dyncat(val, "-tag");
+		if (f & PM_EXPORTED)
+		    val = dyncat(val, "-export");
+		if (f & PM_UNIQUE)
+		    val = dyncat(val, "-unique");
+		vunset = 0;
+	    } else
+		val = dupstring("");
+
+	    v = NULL;
+	    isarr = 0;
+	}
     }
     while (v || ((inbrace || (unset(KSHARRAYS) && vunset)) && isbrack(*s))) {
 	if (!v) {
diff --git a/Src/zsh.export b/Src/zsh.export
index c85bfbad4..32c0e7d3c 100644
--- a/Src/zsh.export
+++ b/Src/zsh.export
@@ -214,6 +214,7 @@ tgoto
 tok
 tokenize
 tokstr
+tokstrings
 tputs
 trashzleptr
 tricat
diff --git a/Src/zsh.h b/Src/zsh.h
index dabcd90c8..e23f9c895 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -780,6 +780,7 @@ struct shfunc {
 #define SFC_HOOK     2		/* one of the special functions */
 #define SFC_WIDGET   3		/* user defined widget */
 #define SFC_COMPLETE 4		/* called from completion code */
+#define SFC_CWIDGET  5		/* new style completion widget */
 
 /* node in list of function call wrappers */
 
@@ -916,10 +917,12 @@ struct param {
 #define PM_TAGGED	(1<<9)	/* tagged                                     */
 #define PM_EXPORTED	(1<<10)	/* exported                                   */
 #define PM_UNIQUE	(1<<11)	/* remove duplicates                          */
-#define PM_SPECIAL	(1<<12) /* special builtin parameter                  */
-#define PM_DONTIMPORT	(1<<13)	/* do not import this variable                */
-#define PM_RESTRICTED	(1<<14) /* cannot be changed in restricted mode       */
-#define PM_UNSET	(1<<15)	/* has null value                             */
+#define PM_TIED 	(1<<12)	/* array tied to colon-path or v.v. */
+#define PM_SPECIAL	(1<<13) /* special builtin parameter                  */
+#define PM_DONTIMPORT	(1<<14)	/* do not import this variable                */
+#define PM_RESTRICTED	(1<<15) /* cannot be changed in restricted mode       */
+#define PM_UNSET	(1<<16)	/* has null value                             */
+#define PM_REMOVABLE	(1<<17)	/* special can be removed from paramtab */
 
 /* Flags for extracting elements of arrays and associative arrays */
 #define SCANPM_WANTVALS   (1<<0)
diff --git a/configure.in b/configure.in
index 1b2692ea1..975e02e49 100644
--- a/configure.in
+++ b/configure.in
@@ -1008,9 +1008,24 @@ char *argv[];
     aix*)         DLLDFLAGS="${DLLDFLAGS=-G -bexpall -lc}" ;;
     solaris*|sysv4*|esix*) DLLDFLAGS="${DLLDFLAGS=-G}" ;;
   esac
-  case "$host_os" in
-    hpux*)  EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-Wl,-E}" ;;
-    linux*) EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-rdynamic}" ;;
+  case "$host" in
+    *-hpux*)  EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-Wl,-E}" ;;
+    *-linux*) EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-rdynamic}" ;;
+    mips-sni-sysv4)
+      #
+      # unfortunately, we have different compilers
+      # that need different flags
+      #
+      sni_cc_version=`$CC -V 2>&1 | head -1`
+      case "$sni_cc_version" in
+        CDS* )
+         EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-Wl,-Blargedynsym}"
+       ;;
+       * )
+         EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-LD-Blargedynsym}"
+       ;;
+      esac
+    ;;
   esac
   AC_CACHE_CHECK(if your dlsym() needs a leading underscore,
    zsh_cv_func_dlsym_needs_underscore,
diff --git a/patchlist.txt b/patchlist.txt
index 6ec3ec351..7a2760329 100644
--- a/patchlist.txt
+++ b/patchlist.txt
@@ -1,5 +1,5 @@
 This version of zsh is based on 3.1.5 and includes the following
-patches.  (The version number built into the shell has not been changed.)
+patches.
 
   Old stuff:
 
@@ -111,7 +111,7 @@ My special parameter unset fix, 4662
   Third edition
 
 I've taken the plunge and changed $ZSH_VERSION, the current one is now
-3.1.5.pws-1 .  It seemed rational to have something incremental at the
+3.1.5.pws-3 .  It seemed rational to have something incremental at the
 end for testing, so I abandoned using the date.
 
 4482 (cdmatch2)and 4641 (${assoc[0]}) now applied; 4641 was supposed
@@ -261,8 +261,70 @@ need '-tc' with -T.  However, you now do need '-tn' in cases where you
 don't want normal completion tried after a -T matches.
 
 Sven's new completion functions, 4850, 4881, 4941, 4942, 4943, 4944,
-4946, 4949, plus my addition of function pointers, 4945.  The example
-file is now in Misc/new-completion-examples.
+4946, 4949, 4950, plus my addition of function pointers, 4945.  The
+example file is now in Misc/new-completion-examples.
 
 (Effect of) fix from Helmut Jarausch in 4947 partly due to change
 missed in patch.
+
+  pws-6
+
+Sven: fix for completion after redirection, 4957
+
+Bart: add-on, 4965
+
+Andrej: configure patch for Reliant UNIX et al., 5021 (as resubmitted)
+
+Sven: compctl list with a single string, 4974
+
+Sven: compctl -M matches with *'s, 4975, 5007
+
+Sven: compadd and new-completion-examples, 4976
+
+Sven: funky new glob modifiers:  change sort order, select
+item from list, 4979; make time order work like ls -t, 4987
+
+Sven: fix completion whitespace for copy-previous-word, 4981
+
+Sven: fix for new-style completion after redirection, 4986, 4988
+
+New mirror site ftp://ftp.win.ne.jp/pub/shell/zsh/ in META-FAQ (not
+posted)
+
+Andrej: when installing info files, insert zsh.info into dir, 5016
+
+Sven: ${(t)param} flag, 5022, 5045; no unset behaviour, 5078
+
+Phil: zless, 5032, simplified by Bart, 5037, also added a `setopt
+localoptions' after spending an hour wondering why nothing worked any
+more.
+
+Me: `make install' does not do `make install.info', 5047
+
+Sven:  compcall tries old-style completion from new-style function,
+compctl -K ' func' handles newstyle completion, 5059; avoid recursion,
+5065; my dynamic fix-up, 5085
+
+Sven: inserting completion inside brace expansion, 5060
+
+Sven: extra completion context, 5092
+
+Me: typeset -T MYPATH mypath, 5094, plus fix for MYPATH=(foo),
+mypath=foo (and also existing PATH=(foo) bug), 5120
+
+Sven: doc fix for glob qualifiers, 5102
+
+Drazen Kacar, modified by me: workaround for terminal bug on Solaris,
+5103; modified by Bart, 5113
+
+Sven:  zle and widget information via variables in new completion
+functions, 5104
+
+Me: remove old zle -C, zle -C now does new completion, 5105
+
+Sven:  glob qualifier o for modes, 5107
+
+Me: fix for unsetting special zle variables, 5111
+
+Drazen Kacar, modified by me: unlock terminal device on Solaris, 5118
+(5117 was wrong)