about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-04-15 18:16:27 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-04-15 18:16:27 +0000
commitb4a5b9db8b528f9c9b6a9cbb00db381c95659380 (patch)
tree17bf8f93d47de0af7495ec6d11361aef1ca62f9b
parentba4f5e80ec9d7e145718e79fed6e57a852c86c12 (diff)
downloadzsh-b4a5b9db8b528f9c9b6a9cbb00db381c95659380.tar.gz
zsh-b4a5b9db8b528f9c9b6a9cbb00db381c95659380.tar.xz
zsh-b4a5b9db8b528f9c9b6a9cbb00db381c95659380.zip
zsh-3.1.5-pws-9 zsh-3.1.5-pws-9
-rw-r--r--Config/version.mk4
-rw-r--r--Doc/Makefile.in4
-rw-r--r--Doc/Zsh/.distfiles8
-rw-r--r--Doc/Zsh/compctl.yo4
-rw-r--r--Doc/Zsh/compwid.yo428
-rw-r--r--Doc/Zsh/guide.yo8
-rw-r--r--Doc/Zsh/intro.yo1
-rw-r--r--Doc/Zsh/metafaq.yo2
-rw-r--r--Doc/Zsh/mod_compctl.yo9
-rw-r--r--Doc/Zsh/mod_zftp.yo4
-rw-r--r--Doc/Zsh/mod_zle.yo27
-rw-r--r--Doc/Zsh/modules.yo5
-rw-r--r--Doc/Zsh/options.yo6
-rw-r--r--Doc/Zsh/seealso.yo1
-rw-r--r--Doc/zsh.yo2
-rw-r--r--Doc/zshcompwid.yo3
-rw-r--r--Etc/MACHINES5
-rw-r--r--Functions/Completion/_aliases3
-rw-r--r--Functions/Completion/_arrays3
-rw-r--r--Functions/Completion/_autoload3
-rw-r--r--Functions/Completion/_bg_jobs3
-rw-r--r--Functions/Completion/_bindkey7
-rw-r--r--Functions/Completion/_builtin7
-rw-r--r--Functions/Completion/_cd3
-rw-r--r--Functions/Completion/_command7
-rw-r--r--Functions/Completion/_command_names3
-rw-r--r--Functions/Completion/_compress3
-rw-r--r--Functions/Completion/_condition10
-rw-r--r--Functions/Completion/_configure12
-rw-r--r--Functions/Completion/_dd13
-rw-r--r--Functions/Completion/_default13
-rw-r--r--Functions/Completion/_dirs3
-rw-r--r--Functions/Completion/_disable6
-rw-r--r--Functions/Completion/_dvi3
-rw-r--r--Functions/Completion/_echotc3
-rw-r--r--Functions/Completion/_enable6
-rw-r--r--Functions/Completion/_fc7
-rw-r--r--Functions/Completion/_files10
-rw-r--r--Functions/Completion/_find21
-rw-r--r--Functions/Completion/_functions3
-rw-r--r--Functions/Completion/_gunzip3
-rw-r--r--Functions/Completion/_gzip3
-rw-r--r--Functions/Completion/_hash13
-rw-r--r--Functions/Completion/_hosts3
-rw-r--r--Functions/Completion/_jobs3
-rw-r--r--Functions/Completion/_kill8
-rw-r--r--Functions/Completion/_kill_helper3
-rw-r--r--Functions/Completion/_limits3
-rw-r--r--Functions/Completion/_main_complete48
-rw-r--r--Functions/Completion/_make3
-rw-r--r--Functions/Completion/_man11
-rw-r--r--Functions/Completion/_mh70
-rw-r--r--Functions/Completion/_most_recent_file3
-rw-r--r--Functions/Completion/_normal56
-rw-r--r--Functions/Completion/_path_files272
-rw-r--r--Functions/Completion/_pdf3
-rw-r--r--Functions/Completion/_precommand5
-rw-r--r--Functions/Completion/_ps3
-rw-r--r--Functions/Completion/_rcs9
-rw-r--r--Functions/Completion/_rlogin9
-rw-r--r--Functions/Completion/_sched3
-rw-r--r--Functions/Completion/_set7
-rw-r--r--Functions/Completion/_setopt7
-rw-r--r--Functions/Completion/_source7
-rw-r--r--Functions/Completion/_strip2
-rw-r--r--Functions/Completion/_stty16
-rw-r--r--Functions/Completion/_subscript4
-rw-r--r--Functions/Completion/_tar11
-rw-r--r--Functions/Completion/_tex3
-rw-r--r--Functions/Completion/_trap7
-rw-r--r--Functions/Completion/_uncompress3
-rw-r--r--Functions/Completion/_unhash6
-rw-r--r--Functions/Completion/_unsetopt7
-rw-r--r--Functions/Completion/_vars3
-rw-r--r--Functions/Completion/_vars_eq3
-rw-r--r--Functions/Completion/_wait4
-rw-r--r--Functions/Completion/_which3
-rw-r--r--Functions/Completion/_x_options5
-rw-r--r--Functions/Completion/_xfig3
-rw-r--r--Functions/Completion/_zftp50
-rw-r--r--Functions/Completion/_zle7
-rw-r--r--Functions/Completion/_zmodload9
-rw-r--r--Functions/Completion/dump89
-rw-r--r--Functions/Completion/init246
-rw-r--r--Misc/zftp-functions329
-rw-r--r--Src/Modules/zftp.c25
-rw-r--r--Src/Zle/comp.h16
-rw-r--r--Src/Zle/compctl.c8
-rw-r--r--Src/Zle/zle_tricky.c1847
-rw-r--r--Src/cond.c60
-rw-r--r--Src/exec.c42
-rw-r--r--Src/glob.c2
-rw-r--r--Src/params.c10
-rw-r--r--Src/subst.c32
-rw-r--r--Src/utils.c2
-rw-r--r--Src/zsh.h5
-rwxr-xr-xconfig.guess3
-rw-r--r--configure.in2
-rw-r--r--patchlist.txt58
99 files changed, 3672 insertions, 478 deletions
diff --git a/Config/version.mk b/Config/version.mk
index 675dede6e..3e36faf5c 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-8
-VERSION_DATE='February 13, 1999'
+VERSION=3.1.5-pws-9
+VERSION_DATE='February 18, 1999'
diff --git a/Doc/Makefile.in b/Doc/Makefile.in
index e22f3c71d..7bc5d7ada 100644
--- a/Doc/Makefile.in
+++ b/Doc/Makefile.in
@@ -38,7 +38,7 @@ TEXI2HTML = texi2html -expandinfo -split_chapter
 .SUFFIXES: .yo .1
 
 # man pages to install
-MAN = zsh.1 zshbuiltins.1 zshcompctl.1 \
+MAN = zsh.1 zshbuiltins.1 zshcompctl.1 zshcompwid.1 \
 zshexpn.1 zshmisc.1 zshmodules.1 \
 zshoptions.1 zshparam.1 zshzle.1 zshall.1
 
@@ -48,7 +48,7 @@ YODLSRC = zmacros.yo zman.yo ztexi.yo Zsh/arith.yo Zsh/builtins.yo \
 Zsh/compat.yo Zsh/compctl.yo Zsh/cond.yo Zsh/exec.yo Zsh/expn.yo \
 Zsh/filelist.yo Zsh/files.yo Zsh/func.yo Zsh/grammar.yo Zsh/guide.yo \
 Zsh/index.yo Zsh/intro.yo Zsh/invoke.yo Zsh/jobs.yo Zsh/metafaq.yo \
-Zsh/modules.yo Zsh/mod_cap.yo \
+Zsh/modules.yo Zsh/mod_cap.yo Zsh/compwid.yo \
 Zsh/mod_clone.yo Zsh/mod_comp1.yo Zsh/mod_compctl.yo Zsh/mod_deltochar.yo \
 Zsh/mod_example.yo Zsh/mod_files.yo Zsh/mod_stat.yo \
 Zsh/mod_zle.yo Zsh/options.yo \
diff --git a/Doc/Zsh/.distfiles b/Doc/Zsh/.distfiles
index 42517f507..c4a6d8080 100644
--- a/Doc/Zsh/.distfiles
+++ b/Doc/Zsh/.distfiles
@@ -1,9 +1,9 @@
 DISTFILES_SRC='
     .distfiles
-    arith.yo builtins.yo compat.yo compctl.yo cond.yo exec.yo expn.yo
-    filelist.yo files.yo func.yo grammar.yo guide.yo index.yo intro.yo
+    arith.yo builtins.yo compat.yo compctl.yo compwid.yo cond.yo exec.yo
+    expn.yo filelist.yo files.yo func.yo grammar.yo guide.yo index.yo intro.yo
     invoke.yo jobs.yo metafaq.yo mod_cap.yo mod_clone.yo mod_comp1.yo
     mod_compctl.yo mod_deltochar.yo mod_example.yo mod_files.yo mod_sched.yo
-    mod_stat.yo mod_zle.yo modules.yo options.yo params.yo prompt.yo
-    redirect.yo restricted.yo seealso.yo zle.yo
+    mod_stat.yo mod_zftp.yo mod_zle.yo modules.yo options.yo params.yo
+    prompt.yo redirect.yo restricted.yo seealso.yo zle.yo
 '
diff --git a/Doc/Zsh/compctl.yo b/Doc/Zsh/compctl.yo
index 6a7f19023..124f9e604 100644
--- a/Doc/Zsh/compctl.yo
+++ b/Doc/Zsh/compctl.yo
@@ -1,4 +1,4 @@
-texinode(Programmable Completion)(Zsh Modules)(Zsh Line Editor)(Top)
+texinode(Programmable Completion)(Completion Widgets)(Zsh Line Editor)(Top)
 chapter(Programmable Completion)
 cindex(completion, programmable)
 cindex(completion, controlling)
@@ -89,7 +89,7 @@ to the user database is too slow and/or it contains too many users (so
 that completion after `tt(~)' is too slow to be usable), you can use
 
 indent(
-tt(compctl -T -x 'C[0,*/*]' -f - 's[~]' -k friends -S/ -tn)
+tt(compctl -T -x 's[~] C[0,[^/]#]' -k friends -S/ -tn)
 )
 
 to complete the strings in the array tt(friends) after a `tt(~)'.
diff --git a/Doc/Zsh/compwid.yo b/Doc/Zsh/compwid.yo
new file mode 100644
index 000000000..2cb12e2c2
--- /dev/null
+++ b/Doc/Zsh/compwid.yo
@@ -0,0 +1,428 @@
+texinode(Completion Widgets)(Zsh Modules)(Programmable Completion)(Top)
+chapter(Completion Widgets)
+cindex(completion, widgets)
+cindex(completion, programmable)
+cindex(completion, controlling)
+sect(Description)
+Completion widgets are defined using the tt(-C) option to the tt(zle)
+builtin command provided by the tt(zle) module (see
+ifzman(zmanref(zshzle))\
+ifnzman(noderef(The zle Module))\
+). For example, the invocation:
+
+indent(nofill(
+tt(zle -C complete expand-or-complete completer)))
+
+defines a widget named tt(complete). If this widget is bound to a key
+using the tt(bindkey) builtin command defined in the tt(zle) module
+(see 
+ifzman(zmanref(zshzle))\
+ifnzman(noderef(Zsh Line Editor))\
+) typing that key will make the completion code call the shell
+function tt(completer). This function is responsible for generating
+the possible matches using the builtins described below. Once the
+function returns, the completion code takes over control again and
+treats the matches the way the builtin widget tt(expand-or-complete)
+would do it. For this second argument, the name of any of the builtin
+widgets that handle completions can be given, i.e. it may be any of
+tt(complete-word), tt(expand-or-complete),
+tt(expand-or-complete-prefix), tt(menu-complete),
+tt(menu-expand-or-complete), tt(reverse-menu-complete),
+tt(list-choices), or tt(delete-char-or-list).
+
+startmenu()
+menu(Special Parameters)
+menu(Builtin Commands)
+menu(Condition Codes)
+menu(Examples)
+endmenu()
+
+texinode(Special Parameters)(Builtin Commands)()(Completion Widgets)
+sect(Special Parameters)
+
+Inside completion widgets some parameters have special meaning. They
+will be used inside the widget function and other shell functions
+called from it. Outside of these function they are not special to the
+shell in any way.
+
+The parameters are used to give information about the internal state
+from the completion code to the completion widget and can be set to
+give information to the completion code from the completion
+widget. Some of the builtin commands and the condition codes use or
+change the current values of these parameters. While the completion
+widget is active, these parameters are reseton each function exit to
+the values they had when the function was entered.
+
+startitem()
+item(tt(argv))(
+The positional parameters are set to the arguments on the command line
+when the widget function is invoked from the completion code.
+)
+item(tt(CURRENT))(
+This is the number of the current word, i.e. the word the cursor is
+currently on in the tt(argv) array.
+)
+item(tt(CONTEXT))(
+This will be set by the completion code to the overall context
+completion is attempted in. Possible values are:
+
+startitem()
+item(tt(command))(
+when completing in a command position, e.g. in the first word on the
+command line
+)
+item(tt(argument))(
+when completing an argument for a command
+)
+item(tt(redirect))(
+when completing after a redirection operator; in this case the
+positional parameters contain not only the arguments but also the
+command name itself as the first element
+)
+item(tt(condition))(
+when completing inside a `tt([[)...tt(]])' conditional expressing; in
+this case the positional parameters are set to the words inside the
+conditional expressions
+)
+item(tt(math))(
+when completing in a mathematical environment such as a
+`tt(LPAR()LPAR())...tt(RPAR()RPAR())' construct
+)
+item(tt(value))(
+when completing the value of a parameter assignment; in case of an
+array value the positional parameters are set to the words in
+parentheses
+)
+item(tt(subscript))(
+when completing inside a parameter expansion subscript
+)
+enditem()
+)
+item(tt(COMMAND))(
+In most cases this is set to name of the command for which completion
+is tried. When completing after a redirection operator it contains the 
+string forming that operator. Also, when completing in the value of a
+parameter assignment or in a parameter subscript it is set to the name
+of the parameter.
+)
+item(tt(PREFIX))(
+This should be set to that part of the current word that should be
+taken as the string every possible match has to begin with. Initially
+this will be set to the part of the current word from the beginning of
+the word up to the position of the cursor. When 
+)
+item(tt(IPREFIX))(
+When a part of the current word should not be considered part of the
+matches, this part should be taken from the tt(PREFIX) parameter and
+appended to this parameter. This will initially be set to the empty
+string when called from the completion code.
+)
+item(tt(SUFFIX))(
+This should be set to that part of the current word that should be
+taken as the string every possible match has to end with. The
+completion code sets this to the part of the current word from the
+cursor position to the end.
+)
+item(tt(NMATCHES))(
+This is always set to the number of matches generated and accepted by
+the completion code so far.
+)
+item(tt(MATCHER))(
+When completion is used with a global match specification (i.e. a
+tt(compctl) with only a tt(-M) option), this parameter is set to the
+number of the specification string which is currently used.
+)
+enditem()
+
+texinode(Builtin Commands)(Condition Codes)(Special Parameters)(Completion Widgets)
+sect(Builtin Commands)
+startitem()
+findex(complist)
+item(tt(complist) var(flags ...))(
+
+Generate matches according to the given var(flags) which can be any of 
+the option flags supported by the tt(compctl) builtin command (see
+ifzman(zmanref(zshcompctl))\
+ifnzman(noderef(Programmable Completion))\
+) except for the tt(-t) and tt(-l) flags. Also, when using the tt(-K)
+flag, the function given as argument to it can not access the command
+line with the tt(read) builtin command.
+
+The matches will be generated in the same way as if the completion code
+generated them directly from a tt(compctl)-definition with the same
+flags. The completion code will consider only those matches as
+possible completions that match the prefix and suffix from the special 
+parameters desribed above. These strings will be compared with the
+generated matches using the normal matching rules and any matching
+specifications given with the tt(-M) flag to tt(complist) and the
+global matching specifications given to the tt(compctl) builtin
+command.
+)
+xitem(tt(compadd) [ tt(-qQfnUam) ] [ tt(-F) var(array) ])
+xitem([ tt(-P) var(prefix) ] [ tt(-S) var(suffix) ])
+xitem([ tt(-p) var(hidden-prefix) ] [ tt(-s) var(hidden-suffix) ])
+xitem([ tt(-i) var(ignored-prefix) ] [ tt(-W) var(file-prefix) ])
+xitem([ tt(-J) var(name) ] [ tt(-V) var(name) ])
+xitem([ tt(-r) var(remove-chars) ] [ tt(-R) var(remove-func) ])
+item([ tt(-M) var(match-spec) ] [ tt(--) ] [ var(words) ... ])(
+
+This builtin command can be used to add matches and directly control
+all the information the completion code stores with each possible
+match.
+
+The supported flags are:
+
+startitem()
+item(tt(-P) var(prefix))(
+The same as for tt(compctl) and tt(complist), it gives a string that
+should be inserted before the given words when they are completed. The
+string given is not considered to be part of the match.
+)
+item(tt(-S) var(suffix))(
+Like tt(-P) but gives a string that has to be inserted after the match.
+)
+item(tt(-p) var(hidden-prefix))(
+This gives a string that should be 
+...
+)
+item(tt(-s) var(hidden-suffix))(
+...
+)
+item(tt(-i) var(ignored-prefix))(
+...
+)
+item(tt(-J) var(name))(
+As for tt(compctl) and tt(complist) this gives the name of the group
+of matches the words should be stored in.
+)
+item(tt(-V) var(name))(
+Like tt(-J) but naming a unsorted group.
+)
+item(tt(-q))(
+This flag has the same meaning as for tt(compctl) and tt(complist),
+too. It makes the suffix given with tt(-S) be automatically removed if 
+the next character typed is a blank or does not insert anything or if
+the suffix consists of only one character and the next character typed 
+is the same character.
+)
+item(tt(-r) var(remove-chars))(
+This makes the suffix given with tt(-S) be automatically removed if
+the next character typed inserts one of the characters given in the
+var(remove-chars). This string is parsed as a characters class with
+the usual backslash-sequences understood, e.g. using `tt(-r "a-z\t")'
+removes the suffix if the next character typed inserts one of the
+lower case letters or a TAB, and `tt(-r "^0-9")' removes the suffix if 
+the next character typed inserts anything but a digit. One extra
+backslash sequence is understood in this string: `tt(\-)' stands for
+all characters that insert nothing. Thus `tt(-S "=" -q)' is the same
+as `tt(-S "=" -r "= \t\n\-")'.
+)
+item(tt(-R) var(remove-func))(
+For the cases where one wants to remove suffix and the tt(-r) option
+does not give enough control, this option can be used. It stores the
+name of the shell function var(remove-func) in the matches. If one of
+the matches is finally accepted and the tt(-S)-suffix inserted, this
+function will be called after the next character typed. It gets the
+length of the suffix as its argument and can use the special
+parameters available in zle widgets (see
+ifzman(zmanref(zshzle))\
+ifnzman(noderef(Zsh Line Editor))\
+) to analyse and modify the command line.
+)
+item(tt(-f))(
+If this flag is given, the matches build are marked as being the names 
+of files. They need not be actual filenames, though. But if they are
+and the option tt(LIST_TYPES) is set, the characters describing the
+types of the files in the completion lists will be shown. This also
+makes a slash automatically be added when the name of a directory is
+completed.
+)
+item(tt(-W) var(file-prefix))(
+This option has the same meaning as for the tt(compctl) and
+tt(complist) builtin commands. Here, however, only one string may be
+given, not an array. This string is used as a pathname that will be
+prepended to the given words and the prefix given with the tt(-p)
+option to perform the file-tests when showing completion
+listings. Hence it is only useful if combined with the tt(-f) flag,
+since the tests will only be performed if that flag is given.
+)
+item(tt(-a))(
+When used by tt(compctl) or tt(complist) the completion code normally
+builds two sets of matches: the normal one where words with one of the 
+suffixes in the array parameter tt(fignore) are not considered
+possible matches, and the alternate set where the words excluded
+from the first set are stored. Normally only the matches in the first
+set are used. But if this set is empty, the words from the alternate
+set are used.
+
+The tt(compadd) builtin does not use tt(fignore) parameter and
+normally stores all words in the first set. With the tt(-a)-flag
+given, however, they are all stored in the alternate set unless this
+flag is overridden by the tt(-F) option.
+)
+item(tt(-F) var(array))(
+This can be used to give an array containing suffixes like the
+tt(fignore) parameter. Words with one of these suffixes are stored in
+the alternate set of matches and words without one of these suffixes
+are stored in the normal set.
+
+The var(array) may be the name of an array parameter or a list of
+literal suffixes enclosed in parentheses as in `tt(-F "(.o .h)")'. If
+the name of an array is given, the elements of the array are taken as
+the suffixes.
+)
+item(tt(-Q))(
+As for tt(compctl) and tt(complist) this flag instructs the completion 
+code not to quote any metacharacters in the words when inserting them
+in the command line.
+)
+item(tt(-m))(
+Normally the matches added by tt(compadd) will not be compared with
+what is already on the line. If this flag is given, this comparison is 
+performed as usual and the match specifications given with the tt(-M)
+option to tt(compadd) and the global match specifications defined with 
+tt(compctl) will be used. This means that probably not all the word
+given will be stored as matches since some of them may not match the
+string on the line.
+)
+item(tt(-M) var(match-spec))(
+This option allows one to give local match specifications with the
+same meaning and format as for the tt(compctl) and tt(complist)
+builtin commands. Note that they will only be used if the tt(-m) is
+given, too.
+)
+item(tt(-n))(
+Words added with tt(compadd) with this flag will be used as possible
+matches as usual but they not appear in the completion listing.
+)
+item(tt(-U))(
+If this flag is given to one of the calls to tt(compadd) and the
+option tt(AUTO_MENU) is set, the completion code will immediatly
+switch to menucompletion.
+)
+item(tt(-), tt(--))(
+This flag ends the list of flags and options. All arguments after it
+will be taken as the words to use as matches even if they begin with
+hyphens.
+)
+enditem()
+)
+item(tt(compcall) [ tt(-TD) ])(
+
+This allows one to use completion definitions given with the
+tt(compctl) builtin from within completion widgets. It makes
+completion code complete the current word according to the
+tt(compctl)s defined. Normally only tt(compctl)s given for specific
+commands are used. To make the code use the completion flags given to
+the tt(-T) option of tt(compctl), one can give the tt(-T) flag to
+tt(compctl). Likewise, the tt(-D) flag to tt(compcall) makes the
+default completion flags given to tt(compctl) with the tt(-D) option
+be used.
+)
+enditem()
+
+texinode(Condition Codes)(Examples)(Builtin Commands)(Completion Widgets)
+sect(Condition Codes)
+
+Inside completion widgets not only the builtin commands described
+above can be used, but also some additional condition codes. These
+work on the special parameters and can be used to easily build
+completion functions that generate different matches depending on the
+strings on the line.
+
+The following condition codes are made available inside completion
+widgets:
+
+startitem()
+item(tt(-prefix) var(string))(
+true if the content of tt(PREFIX) starts with var(string)
+)
+item(tt(-iprefix) var(string))(
+like tt(-prefix), but the var(string) is removed from tt(PREFIX) and
+added to tt(IPREFIX)
+)
+item(tt(-position) var(beg) [ var(end) ])(
+true if tt(CURRENT) is equal to var(beg) or, if var(end) is given,
+equal to or greater than var(beg) and equal to or less than var(end);
+both of var(beg) and var(end) may be arithmetic expressions, if they
+are less than zero the number of words in tt(argv) are added to them
+before comparing them to tt(CURRENT); thus, tt(-1) is the last word,
+tt(-2) is the word before that and so on
+)
+item(tt(-word) var(index) var(string))(
+true if the word number var(index) in tt(argv) is equal to
+var(string); again, var(index) may be negative, counting backwards
+)
+item(tt(-mword) var(index) var(pattern))(
+like tt(-word) but using pattern matching
+)
+item(tt(-current) var(offset) var(string))(
+like tt(-word) but var(offset) is relative to the value of
+tt(CURRENT)
+)
+item(tt(-mcurrent) var(offset) var(pattern))(
+like tt(-current) but using pattern matching
+)
+item(tt(-string) [ var(number) ] var(string))(
+true if the current word contains var(string); anything up to the last 
+occurrence of this string will be ingnored by removing it from
+tt(PREFIX) and adding it to tt(IPREFIX); if var(number) is given,
+anything up to the var(number)'th occurrence of the var(string) will
+be ignored; again, var(nmuber) may be any arithmetic expression and
+negative values count backward
+)
+item(tt(-class) [ var(number) ] var(class))(
+like tt(-string) but the var(class) is used as a character class so
+that anything up to and including the last or the var(number)'th
+occurrence of any character from the string var(class) is ignored
+)
+item(tt(-words) var(min) [ var(max) ])(
+true if the number of words is equal to var(min); if var(max) is
+given, it is true if the number of words is equal to or greater than
+var(min) and equal to or less than var(max)
+)
+item(tt(-after) var(string))(
+true if the cursor is after a word that is equal to var(string)
+)
+item(tt(-mafter) var(pattern))(
+like tt(-after) but using pattern matching
+)
+item(tt(-between) var(string1) var(string2))(
+true if the cursor is after a word that is equal to var(string1), if
+there is also a word that is equal to va(string2), this is true only
+if the cursor is before it
+)
+item(tt(-mbetween) var(pattern1) var(pattern2))(
+like tt(-between) but using pattern matching
+)
+item(tt(-nmatches) var(number))(
+true if the the value of tt(NMATCHES) is equal to var(number)
+)
+item(tt(-matcher) var(number))(
+true if the value of tt(MATCHER) is equal to var(number)
+)
+enditem()
+
+texinode(Examples)()(Condition Codes)(Completion Widgets)
+sect(Examples)
+
+The first step is to define the widget:
+
+indent(nofill(
+tt(zle -C complete complete-word complete-history)))
+
+Then the widget can be bound to a key using the tt(bindkey) builtin
+command:
+
+indent(nofill(
+tt(bindkey '^X\t' complete)))
+
+After that the shell function tt(complete-history) will be invoked
+after typing control-X and TAB. The function should then generte the
+matches, e.g.:
+
+indent(nofill(
+tt(complete-history LPAR()RPAR() { complist -H 0 '' })))
+
+In this the function will complete words from the history matching the 
+current word.
diff --git a/Doc/Zsh/guide.yo b/Doc/Zsh/guide.yo
index 0fb954e26..e0005e339 100644
--- a/Doc/Zsh/guide.yo
+++ b/Doc/Zsh/guide.yo
@@ -29,6 +29,7 @@ menu(Options)
 menu(Shell Builtin Commands)
 menu(Zsh Line Editor)
 menu(Programmable Completion)
+menu(Completion Widgets)
 menu(Zsh Modules)
 
  --- Indices ---
@@ -103,6 +104,13 @@ menu(Alternative Completion)
 menu(Extended Completion)
 menu(Example)
 
+Completion Widgets
+
+menu(Special Parameters)
+menu(Builtin Commands)
+menu(Condition Codes)
+menu(Examples)
+
 Zsh Modules
 
 menu(The cap Module)
diff --git a/Doc/Zsh/intro.yo b/Doc/Zsh/intro.yo
index ebb678f70..f619f82fe 100644
--- a/Doc/Zsh/intro.yo
+++ b/Doc/Zsh/intro.yo
@@ -21,6 +21,7 @@ list(em(zshoptions)   Zsh options)
 list(em(zshbuiltins)  Zsh built-in functions)
 list(em(zshzle)       Zsh command line editing)
 list(em(zshcompctl)   Zsh completion control)
+list(em(zshcompwid)   Zsh completion widgets)
 list(em(zshmodules)   Zsh loadable modules)
 ifzshone(\
 list(em(zshall)       Meta-man page containing all of the above)
diff --git a/Doc/Zsh/metafaq.yo b/Doc/Zsh/metafaq.yo
index cad82ee65..1d1636942 100644
--- a/Doc/Zsh/metafaq.yo
+++ b/Doc/Zsh/metafaq.yo
@@ -11,7 +11,7 @@ sect(Author)
 cindex(author)
 Zsh was originally written by Paul Falstad tt(<pf@zsh.org>).
 Zsh is now maintained by the members of the zsh-workers mailing
-list tt(<zsh-workers@math.gatech.edu>).  The development is currently
+list tt(<zsh-workers@sunsite.auc.dk>).  The development is currently
 coordinated by Andrew Main (Zefram) tt(<zefram@zsh.org>).  The coordinator
 can be contacted at tt(<coordinator@zsh.org>), but matters relating to
 the code should generally go to the mailing list.
diff --git a/Doc/Zsh/mod_compctl.yo b/Doc/Zsh/mod_compctl.yo
index b7adf8fa6..cfd615b2d 100644
--- a/Doc/Zsh/mod_compctl.yo
+++ b/Doc/Zsh/mod_compctl.yo
@@ -1,7 +1,12 @@
 texinode(The compctl Module)(The deltochar Module)(The comp1 Module)(Zsh Modules)
 sect(The compctl Module)
-The tt(compctl) module makes available one builtin command, tt(compctl),
-which is the standard way to control completions for ZLE.  See
+The tt(compctl) module makes available several builtin commands. tt(compctl),
+is the standard way to control completions for ZLE.  See
 ifzman(zmanref(zshcompctl))\
 ifnzman(noderef(Programmable Completion))\
 .
+The other builtin commands can be used in user-defined completion widgets,
+see
+ifzman(zmanref(zshcompwid))\
+ifnzman(noderef(Completion Widgets))\
+.
diff --git a/Doc/Zsh/mod_zftp.yo b/Doc/Zsh/mod_zftp.yo
index c08130bf5..395bf26de 100644
--- a/Doc/Zsh/mod_zftp.yo
+++ b/Doc/Zsh/mod_zftp.yo
@@ -439,4 +439,8 @@ until the next call to tt(zftp).  Other status changes in subshells
 will not be reflected by changes to the variables (but should
 be otherwise harmless).
 
+On some operatings systems, the control connection is not valid after a
+fork(), so that operations in subshells or on the left hand side of a
+pipeline are not possible.
+
 enditem()
diff --git a/Doc/Zsh/mod_zle.yo b/Doc/Zsh/mod_zle.yo
index 86d145636..ad88c94c7 100644
--- a/Doc/Zsh/mod_zle.yo
+++ b/Doc/Zsh/mod_zle.yo
@@ -165,7 +165,7 @@ xitem(tt(zle) tt(-l) [ tt(-L) ])
 xitem(tt(zle) tt(-D) var(widget) ...)
 xitem(tt(zle) tt(-A) var(old-widget) var(new-widget))
 xitem(tt(zle) tt(-N) var(widget) [ var(function) ])
-xitem(tt(zle) tt(-C) [ tt(-mMgG) ] var(widget) [ var(compctl-options) ])
+xitem(tt(zle) tt(-C) var(widget) var(completion-widget) var(function))
 item(tt(zle) var(widget))(
 The tt(zle) builtin performs a number of different actions concerning
 ZLE.  Which operation it performs depends on its options:
@@ -193,22 +193,15 @@ widget is invoked from within the editor, the specified shell var(function)
 is called.  If no function name is specified, it defaults to
 the same name as the widget.
 )
-item(tt(-C) [ tt(-mMgG) ] var(widget) [ var(compctl-options) ])(
-Create a user-defined widget which will perform completion according
-to var(compctl-options).  These are passed directly to the
-tt(compctl) command, see
-ifzman(zmanref(zshcompctl))\
-ifnzman(noderef(Programmable Completion))\
-; no command names or special options (tt(-LDCT)) may be used.  If the
-var(compctl-options) are missing the widget will have normal
-completion behaviour as modified by the tt(zle) options.
-
-There are four additional tt(zle) options, which must precede the
-widget name: tt(-m) and tt(-M) force the widget to use or not to use
-menu completion, respectively, while tt(-g) and tt(-G) likewise force
-the widget to use or not to use glob completion.  The defaults are to
-use the current settings of tt(MENU_COMPLETE) and tt(GLOB_COMPLETE)
-as with normal completion.
+item(tt(-C) var(widget) var(completion-widget) var(function))(
+Create a user-defined completion widget names var(widget). The 
+completion widget will behave like the built-in completion-widget
+whose name is given as var(completion-widget). To generate the
+completions, the shell function var(function) will be called.
+For further information, see
+ifzman(zmanref(zshcompwid))\
+ifnzman(noderef(Completion Widgets))\
+.
 )
 item(var(widget))(
 Invoke the specified widget.  This can only be done when ZLE is
diff --git a/Doc/Zsh/modules.yo b/Doc/Zsh/modules.yo
index 300553845..97c420eed 100644
--- a/Doc/Zsh/modules.yo
+++ b/Doc/Zsh/modules.yo
@@ -1,4 +1,4 @@
-texinode(Zsh Modules)()(Programmable Completion)(Top)
+texinode(Zsh Modules)()(Completion Widgets)(Top)
 chapter(Zsh Modules)
 cindex(modules)
 sect(Description)
@@ -19,7 +19,8 @@ item(tt(comp1))(
 Base of the completion system.  Used by the tt(compctl) and tt(zle) modules.
 )
 item(tt(compctl))(
-The tt(compctl) builtin for controlling completion.
+The tt(compctl) builtin for controlling completion and the builtins for
+completion widgets.
 )
 item(tt(deltochar))(
 A ZLE function duplicating EMACS' tt(zap-to-char).
diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index b9455f578..b59fa624a 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -544,12 +544,14 @@ List jobs in the long format by default.
 )
 pindex(MAGIC_EQUAL_SUBST)
 item(tt(MAGIC_EQUAL_SUBST))(
-All unquoted arguments of the form `var(identifier)tt(=)var(expression)'
+All unquoted arguments of the form `var(anything)tt(=)var(expression)'
 appearing after the command name have filename expansion (that is,
 where var(expression) has a leading `tt(~)' or `tt(=)') performed on
 var(expression) as if it were a parameter assignment.  The argument is
 not otherwise treated specially; it is passed to the command as a single
-argument, and not used as an actual parameter assignment.
+argument, and not used as an actual parameter assignment.  For example, in
+tt(echo foo=~/bar:~/rod), both occurrences of tt(~) would be replaced.
+Note that this happens anyway with tt(typeset) and similar statements.
 )
 pindex(MAIL_WARNING)
 cindex(mail, warning of reading)
diff --git a/Doc/Zsh/seealso.yo b/Doc/Zsh/seealso.yo
index 8289e6628..5231e8828 100644
--- a/Doc/Zsh/seealso.yo
+++ b/Doc/Zsh/seealso.yo
@@ -10,6 +10,7 @@ ifzshone(\
 ,
 zmanref(zshbuiltins),
 zmanref(zshcompctl),
+zmanref(zshcompwid),
 zmanref(zshexpn),
 zmanref(zshmisc),
 zmanref(zshmodules),
diff --git a/Doc/zsh.yo b/Doc/zsh.yo
index a97f023e7..e6cef6a32 100644
--- a/Doc/zsh.yo
+++ b/Doc/zsh.yo
@@ -64,6 +64,7 @@ ifnzman(includefile(Zsh/options.yo))
 ifnzman(includefile(Zsh/builtins.yo))
 ifnzman(includefile(Zsh/zle.yo))
 ifnzman(includefile(Zsh/compctl.yo))
+ifnzman(includefile(Zsh/compwid.yo))
 ifnzman(includefile(Zsh/modules.yo))
 ifzshall(\
 def(source)(1)(NOTRANS(.so )man1/ARG1NOTRANS(.)1)\
@@ -75,6 +76,7 @@ source(zshoptions)
 source(zshbuiltins)
 source(zshzle)
 source(zshcompctl)
+source(zshcompwid)
 source(zshmodules)
 manpage(ZSHALL)(1)(date())(zsh version())
 )\
diff --git a/Doc/zshcompwid.yo b/Doc/zshcompwid.yo
new file mode 100644
index 000000000..d876e422d
--- /dev/null
+++ b/Doc/zshcompwid.yo
@@ -0,0 +1,3 @@
+manpage(ZSHCOMPWID)(1)(date())(zsh version())
+manpagename(zshcompwid)(zsh completion widgets)
+includefile(Zsh/compwid.yo)
diff --git a/Etc/MACHINES b/Etc/MACHINES
index b9d3290c0..27e21f330 100644
--- a/Etc/MACHINES
+++ b/Etc/MACHINES
@@ -76,9 +76,12 @@ Reliant: SINIX
 	linking; configure now detects this and will disable dynamic
 	linking even if you requested it.
 
-SGI: IRIX 5.1.1.1, 5.2, 5.3, 6.2, 6.3
+SGI: IRIX 5.1.1.1, 5.2, 5.3, 6.2, 6.3, 6.5
 	Should build `out-of-the-box'.
 
+        On 6.5.2, zsh malloc routines are reported not to work; also
+        full optimization (cc -O3 -OPT:Olimit=0) causes problems.
+
 Sun: SunOS 4.1.*
 	Under 4.1.3 if yellow pages is used, username completion may cause
 	segmentation violation.  This is a bug in the shared library not
diff --git a/Functions/Completion/_aliases b/Functions/Completion/_aliases
new file mode 100644
index 000000000..1038a726e
--- /dev/null
+++ b/Functions/Completion/_aliases
@@ -0,0 +1,3 @@
+#defcomp unalias
+
+complist -a
diff --git a/Functions/Completion/_arrays b/Functions/Completion/_arrays
new file mode 100644
index 000000000..cbeac7118
--- /dev/null
+++ b/Functions/Completion/_arrays
@@ -0,0 +1,3 @@
+#defcomp shift
+
+complist -A
diff --git a/Functions/Completion/_autoload b/Functions/Completion/_autoload
new file mode 100644
index 000000000..4f506baeb
--- /dev/null
+++ b/Functions/Completion/_autoload
@@ -0,0 +1,3 @@
+#defcomp autoload
+
+complist -s '${^fpath}/*(N:t)'
diff --git a/Functions/Completion/_bg_jobs b/Functions/Completion/_bg_jobs
new file mode 100644
index 000000000..511bb8308
--- /dev/null
+++ b/Functions/Completion/_bg_jobs
@@ -0,0 +1,3 @@
+#defcomp bg
+
+complist -z -P '%'
diff --git a/Functions/Completion/_bindkey b/Functions/Completion/_bindkey
new file mode 100644
index 000000000..8eddeb2a8
--- /dev/null
+++ b/Functions/Completion/_bindkey
@@ -0,0 +1,7 @@
+#defcomp bindkey
+
+if [[ -mword 1 -*[DAN]* || -mcurrent -1 -*M ]]; then
+  complist -s '$(bindkey -l)'
+else
+  complist -b
+fi
diff --git a/Functions/Completion/_builtin b/Functions/Completion/_builtin
new file mode 100644
index 000000000..d2b11226b
--- /dev/null
+++ b/Functions/Completion/_builtin
@@ -0,0 +1,7 @@
+#defcomp builtin
+
+if [[ -position 2 -1 ]]; then
+  compsub
+else
+  complist -eB
+fi
diff --git a/Functions/Completion/_cd b/Functions/Completion/_cd
new file mode 100644
index 000000000..f3ce67ec7
--- /dev/null
+++ b/Functions/Completion/_cd
@@ -0,0 +1,3 @@
+#defcomp cd
+
+_files -W cdpath -g '*(-/)'
diff --git a/Functions/Completion/_command b/Functions/Completion/_command
new file mode 100644
index 000000000..b2812de25
--- /dev/null
+++ b/Functions/Completion/_command
@@ -0,0 +1,7 @@
+#defcomp command
+
+if [[ -position 2 -1 ]]; then
+  _normal "$@"
+else
+  complist -em
+fi
diff --git a/Functions/Completion/_command_names b/Functions/Completion/_command_names
new file mode 100644
index 000000000..d3b8a109a
--- /dev/null
+++ b/Functions/Completion/_command_names
@@ -0,0 +1,3 @@
+#defcomp -command-
+
+complist -c
diff --git a/Functions/Completion/_compress b/Functions/Completion/_compress
new file mode 100644
index 000000000..860aeb5b0
--- /dev/null
+++ b/Functions/Completion/_compress
@@ -0,0 +1,3 @@
+#defcomp compress
+
+_files -g '*~*.Z'
diff --git a/Functions/Completion/_condition b/Functions/Completion/_condition
new file mode 100644
index 000000000..3e45e1b8f
--- /dev/null
+++ b/Functions/Completion/_condition
@@ -0,0 +1,10 @@
+#defcomp -condition-
+
+if [[ -current -1 -o ]]; then
+  complist -o -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}'
+elif [[ -current -1 -nt || -current -1 -ot || -current -1 -ef ]]; then
+  _files
+else
+  _files
+  complist -v
+fi
diff --git a/Functions/Completion/_configure b/Functions/Completion/_configure
new file mode 100644
index 000000000..de8d5fba5
--- /dev/null
+++ b/Functions/Completion/_configure
@@ -0,0 +1,12 @@
+#defcomp configure
+
+if [[ $PREFIX = *=* ]]; then
+  # Complete filenames after e.g. --prefix=
+  IPREFIX=${PREFIX%%=*}=
+  PREFIX=${PREFIX#*=}
+  complist -f
+else
+  # Generate a list of options from configure --help
+  complist -s '$($COMMAND --help |
+  sed -n -e '\''s/^ *\(--[-a-z0-9]*\)[    =,].*$/\1/p'\'')'
+fi
diff --git a/Functions/Completion/_dd b/Functions/Completion/_dd
new file mode 100644
index 000000000..2458541ea
--- /dev/null
+++ b/Functions/Completion/_dd
@@ -0,0 +1,13 @@
+#defcomp dd
+
+if [[ -iprefix conv= ]]; then
+  # If there's a comma present, ignore up to the last one.  The
+  # test alone will have that effect.
+  [[ -string , ]]
+  complist -S, -q \
+  -k '(ascii ebcdic ibm block unblock lcase ucase swab noerror sync)'
+elif [[ -iprefix 'if=' || -iprefix 'of=' ]]; then
+  _files
+else
+  complist -S '=' -k '(if of ibs obs bs cbs skip files seek count conv)'
+fi
diff --git a/Functions/Completion/_default b/Functions/Completion/_default
new file mode 100644
index 000000000..8bcf14f6a
--- /dev/null
+++ b/Functions/Completion/_default
@@ -0,0 +1,13 @@
+#defcomp -default-
+
+# 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 there is a `compctl' for the command we are working on, we return
+# immediatly. If you want to use new style completion anyway, remove the
+# `|| return'. Also, you may want to use new style completion if the 
+# `compctl' didn't produce any matches. In that case remove the `|| return'
+# and at the line `[[ -nmatches 0 ]] || return' after `compcall'.
+
+compcall || return
+
+_files
diff --git a/Functions/Completion/_dirs b/Functions/Completion/_dirs
new file mode 100644
index 000000000..c79080d58
--- /dev/null
+++ b/Functions/Completion/_dirs
@@ -0,0 +1,3 @@
+#defcomp rmdir df du dircmp
+
+_files -/ '*(-/)'
diff --git a/Functions/Completion/_disable b/Functions/Completion/_disable
new file mode 100644
index 000000000..063b65a7d
--- /dev/null
+++ b/Functions/Completion/_disable
@@ -0,0 +1,6 @@
+#defcomp disable
+
+[[ -mcurrent -1 -*a* ]] && complist -ea
+[[ -mcurrent -1 -*f* ]] && complist -eF
+[[ -mcurrent -1 -*r* ]] && complist -ew
+[[ ! -mcurrent -1 -* ]] && complist -eB
diff --git a/Functions/Completion/_dvi b/Functions/Completion/_dvi
new file mode 100644
index 000000000..bb2fc293e
--- /dev/null
+++ b/Functions/Completion/_dvi
@@ -0,0 +1,3 @@
+#defcomp xdvi dvips dvibook dviconcat dvicopy dvidvi dviselect dvitodvi dvitype
+
+_files -g '*.(dvi|DVI)'
diff --git a/Functions/Completion/_echotc b/Functions/Completion/_echotc
new file mode 100644
index 000000000..85ebb97ce
--- /dev/null
+++ b/Functions/Completion/_echotc
@@ -0,0 +1,3 @@
+#defcomp echotc
+
+complist -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)'
diff --git a/Functions/Completion/_enable b/Functions/Completion/_enable
new file mode 100644
index 000000000..22ff53ee7
--- /dev/null
+++ b/Functions/Completion/_enable
@@ -0,0 +1,6 @@
+#defcomp enable
+
+[[ -mcurrent -1 -*a* ]] && complist -da
+[[ -mcurrent -1 -*f* ]] && complist -dF
+[[ -mcurrent -1 -*r* ]] && complist -dw
+[[ ! -mcurrent -1 -* ]] && complist -dB
diff --git a/Functions/Completion/_fc b/Functions/Completion/_fc
new file mode 100644
index 000000000..f0d2c03fd
--- /dev/null
+++ b/Functions/Completion/_fc
@@ -0,0 +1,7 @@
+#defcomp fc
+
+if [[ -mcurrent -1 -*e ]]; then
+  complist -c
+elif [[ -mcurrent -1 -[ARWI]## ]]; then
+  _files
+fi
diff --git a/Functions/Completion/_files b/Functions/Completion/_files
new file mode 100644
index 000000000..ab3006e39
--- /dev/null
+++ b/Functions/Completion/_files
@@ -0,0 +1,10 @@
+#autoload
+
+# Utility function for completing files of a given type or any file.
+# In many cases you will want to call this one instead of _path_files().
+
+local nm=$NMATCHES
+
+_path_files "$@"
+
+[[ $# -ne 0 && -nmatches nm ]] && _path_files
diff --git a/Functions/Completion/_find b/Functions/Completion/_find
new file mode 100644
index 000000000..ca4f79908
--- /dev/null
+++ b/Functions/Completion/_find
@@ -0,0 +1,21 @@
+#defcomp find
+
+if [[ -mbetween -(ok|exec) \\\; ]]; then
+  _normal "$@"
+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'
+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
diff --git a/Functions/Completion/_functions b/Functions/Completion/_functions
new file mode 100644
index 000000000..8a352ea08
--- /dev/null
+++ b/Functions/Completion/_functions
@@ -0,0 +1,3 @@
+#defcomp unfunction
+
+complist -F
diff --git a/Functions/Completion/_gunzip b/Functions/Completion/_gunzip
new file mode 100644
index 000000000..35a27e774
--- /dev/null
+++ b/Functions/Completion/_gunzip
@@ -0,0 +1,3 @@
+#defcomp gunzip zcat
+
+_files -g '*.[gG][z]'
diff --git a/Functions/Completion/_gzip b/Functions/Completion/_gzip
new file mode 100644
index 000000000..3cda1e4ed
--- /dev/null
+++ b/Functions/Completion/_gzip
@@ -0,0 +1,3 @@
+#defcomp gzip
+
+_files -g '*~*.[gG][zZ]'
diff --git a/Functions/Completion/_hash b/Functions/Completion/_hash
new file mode 100644
index 000000000..a6109563d
--- /dev/null
+++ b/Functions/Completion/_hash
@@ -0,0 +1,13 @@
+#defcomp hash
+
+if [[ -mword 1 -*d* ]]; then
+  if [[ -string 1 '=' ]]; then
+    _path_files -g '*(-/)'
+  else
+    complist -n -q -S '='
+  fi
+elif [[ -string 1 '=' ]]; then
+  _files -g '*(*)' '*(-/)'
+else
+  complist -m -q -S '='
+fi
diff --git a/Functions/Completion/_hosts b/Functions/Completion/_hosts
new file mode 100644
index 000000000..3acc327ac
--- /dev/null
+++ b/Functions/Completion/_hosts
@@ -0,0 +1,3 @@
+#defcomp ftp ncftp ping rwho rup xping traceroute nslookup
+
+complist -k hosts
diff --git a/Functions/Completion/_jobs b/Functions/Completion/_jobs
new file mode 100644
index 000000000..018883c61
--- /dev/null
+++ b/Functions/Completion/_jobs
@@ -0,0 +1,3 @@
+#defcomp fg jobs
+
+complist -j -P '%'
diff --git a/Functions/Completion/_kill b/Functions/Completion/_kill
new file mode 100644
index 000000000..bf0e1d3f8
--- /dev/null
+++ b/Functions/Completion/_kill
@@ -0,0 +1,8 @@
+#defcomp kill
+
+if [[ -iprefix '-' ]]; then
+  complist -k "($signals[1,-3])"
+else
+  complist -P '%' -j
+  complist -y _kill_helper -s '`ps 2>/dev/null | tail +2 | cut -c1-5`'
+fi
diff --git a/Functions/Completion/_kill_helper b/Functions/Completion/_kill_helper
new file mode 100644
index 000000000..d3e9aaf4a
--- /dev/null
+++ b/Functions/Completion/_kill_helper
@@ -0,0 +1,3 @@
+#autoload
+
+reply=( "$(ps 2>/dev/null)" )
diff --git a/Functions/Completion/_limits b/Functions/Completion/_limits
new file mode 100644
index 000000000..35ccbe07e
--- /dev/null
+++ b/Functions/Completion/_limits
@@ -0,0 +1,3 @@
+#defcomp limit unlimit
+
+complist -k "(${(j: :)${(f)$(limit)}%% *})"
diff --git a/Functions/Completion/_main_complete b/Functions/Completion/_main_complete
new file mode 100644
index 000000000..003a01785
--- /dev/null
+++ b/Functions/Completion/_main_complete
@@ -0,0 +1,48 @@
+#autoload
+
+# The main loop of the completion code. This is what is called when 
+# completion is attempted from the command line.
+# The completion code gives us the special variables and the arguments
+# from the command line are given as positional parameters.
+
+local comp name
+
+setopt localoptions nullglob rcexpandparam globdots
+unsetopt markdirs globsubst shwordsplit nounset
+
+# An entry for `-first-' is the replacement for `compctl -T'
+# Completion functions may set `COMPSKIP' to any value to make the 
+# main loops stop calling other completion functions.
+
+comp="$comps[-first-]"
+if [[ ! -z "$comp" ]]; then
+  "$comp" "$@"
+  if (( $+COMPSKIP )); then
+    unset COMPSKIP
+    return
+  fi
+fi
+
+# For arguments we use the `_normal function.
+
+if [[ $CONTEXT == argument || $CONTEXT == command ]]; then
+  _normal "$@"
+else
+  # Let's see if we have a special completion definition for the other
+  # possible contexts.
+
+  comp=''
+
+  case $CONTEXT in
+  redirect)  comp="$comps[-redirect-]";;
+  math)      comp="$comps[-math-]";;
+  subscript) comp="$comps[-subscript-]";;
+  value)     comp="$comps[-value-]";;
+  condition) comp="$comps[-condition-]";;
+  esac
+
+  # If not, we use default completion, if any.
+
+  [[ -z "$comp" ]] && comp="$comps[-default-]"
+  [[ -z "$comp" ]] || "$comp" "$@"
+fi
diff --git a/Functions/Completion/_make b/Functions/Completion/_make
new file mode 100644
index 000000000..d576b0308
--- /dev/null
+++ b/Functions/Completion/_make
@@ -0,0 +1,3 @@
+#defcomp make gmake pmake
+
+complist -s "\$(awk '/^[a-zA-Z0-9][^/ 	]+:/ {print \$1}' FS=: [mM]akefile)"
diff --git a/Functions/Completion/_man b/Functions/Completion/_man
new file mode 100644
index 000000000..8204fba0b
--- /dev/null
+++ b/Functions/Completion/_man
@@ -0,0 +1,11 @@
+#defcomp man
+setopt localoptions rcexpandparam
+
+local rep
+if [[ $2 = (<->*|ln) ]]; then
+  rep=( $manpath/(man|cat)$2/$PREFIX*$SUFFIX.<->*(N:t:r) )
+else
+  rep=( $manpath/(man|cat)*/$PREFIX*$SUFFIX.<->*(N:t:r) )
+fi
+
+(( $#rep )) && compadd -m $rep
diff --git a/Functions/Completion/_mh b/Functions/Completion/_mh
new file mode 100644
index 000000000..67ce49fd2
--- /dev/null
+++ b/Functions/Completion/_mh
@@ -0,0 +1,70 @@
+#defcomp folder comp inc mark refile repl scan show next prev rmm pick whom mhn mhpath mhpatch
+
+# Completion for all possible MH commands.
+# Alter the following two to your own mh directory and the directory
+# where standard mh library files live.  (It works anyway, but this
+# will save a little time.)
+local mymhdir=~/Mail
+local mhlib=/usr/lib/mh
+
+# To be on the safe side, check this exists and if not, get it anyway.
+[[ -d $mymhdir ]] || mymhdir=$(mhpath +)
+
+if [[ -iprefix - ]]; then
+  # get list of options, which MH commands can generate themselves
+  # awk is just too icky to use for this, sorry.  send me one if
+  # you come up with it.
+  compadd -m $($COMMAND -help | perl -ne 'if (/^\s*-\(?(\S+)/) {
+    $n = $1;
+    $n =~ s/\)//g;
+    print $n =~ s/^\[([a-z]+)\]// ? "$n\n$1$n\n" : "$n\n";
+  }')
+  return
+elif [[ -iprefix '+' || -iprefix '@' || -current -1 -draftfolder ]]; then
+  # Complete folder names.
+  local mhpath
+  if [[ $IPREFIX != '@' ]]; then
+    [[ $IPREFIX = '+' ]] || IPREFIX=+
+    mhpath=$mymhdir
+  else
+    mhpath=$(mhpath)
+  fi
+
+  # painless, or what?
+  complist -W mhpath -/
+elif [[ -mcurrent -1 -(editor|(whatnow|rmm|show|more)proc) ]]; then
+  complist -c
+elif [[ -current -1 -file ]]; then
+  complist -f
+elif [[ -mcurrent -1 -(form|audit|filter) ]]; then
+  # Need some MH template file, which may be in our own MH directory
+  # or with the standard library.
+  local mhfpath
+  # This is the only place we need mhlib, so leave the test till here.
+  [[ -d $mhlib ]] || { mhlib=$(mhparam mhlproc); mhlib=$mhlib:h; }
+  mhfpath=($mymhdir $mhlib)
+
+  complist -W mhfpath -g '*(.)'
+elif [[ -mcurrent -1 -(no|)cc ]]; then
+  compadd -m all to cc me
+elif [[ -mcurrent -1 -[rw]cache ]]; then
+  compadd -m public private never ask
+else
+  # Generate sequences.
+  local foldnam folddir f
+  for f in $argv; do
+    [[ $f = [@+]* ]] && foldnam=$f
+  done
+  if [[ $foldnam = '+'* ]]; then
+    folddir=$mymhdir/${foldnam#+}
+  elif [[ $foldnam = '@'* ]]; then
+    folddir=$(mhpath)/${foldnam#@}
+  else
+    folddir=$(mhpath)
+    # leaving foldnam empty works here
+  fi
+
+  complist -s '$(mark $foldnam | awk -F: '\''{ print $1 }'\'')'
+  compadd -m reply next cur prev first last all unseen
+  complist -W folddir -g '<->'
+fi
diff --git a/Functions/Completion/_most_recent_file b/Functions/Completion/_most_recent_file
new file mode 100644
index 000000000..69900e6ff
--- /dev/null
+++ b/Functions/Completion/_most_recent_file
@@ -0,0 +1,3 @@
+#defkeycomp expand-or-complete \C-xm
+
+complist -g '*(om[1])'
diff --git a/Functions/Completion/_normal b/Functions/Completion/_normal
new file mode 100644
index 000000000..22cc1decf
--- /dev/null
+++ b/Functions/Completion/_normal
@@ -0,0 +1,56 @@
+#autoload
+
+local comp cmd1 cmd2 pat val name
+
+# Completing in command position? If not we set up `cmd1' and `cmd2' as
+# two strings we have search in the completion definition arrays (e.g.
+# a path and the last path name component).
+
+if [[ $CONTEXT == command ]]; then
+  comp="$comps[-command-]"
+  [[ -z "$comp" ]] || "$comp" "$@"
+  return
+elif [[ "$COMMAND[1]" == '=' ]]; then
+  eval cmd1\=$COMMAND
+  cmd2="$COMMAND[2,-1]"
+elif [[ "$COMMAND" == */* ]]; then
+  cmd1="$COMMAND"
+  cmd2="${COMMAND:t}"
+else
+  cmd1="$COMMAND"
+  eval cmd2=$(whence -p $COMMAND)
+fi
+
+# See if there are any matching pattern completions.
+
+if (( $#patcomps )); then
+  for i in "$patcomps[@]"; do
+    pat="${i% *}"
+    val="${i#* }"
+    if [[ "$cmd1" == $~pat || "$cmd2" == $~pat ]]; then
+      "$val" "$@"
+      if (( $+COMPSKIP )); then
+        unset COMPSKIP
+        return
+      fi
+    fi
+  done
+fi
+
+# Now look up the two names in the normal completion array.
+
+name="$cmd1"
+comp="$comps[$cmd1]"
+
+if [[ -z "$comp" ]]; then
+  name="$cmd2"
+  comp="$comps[$cmd2]"
+fi
+
+# And generate the matches, probably using default completion.
+
+if [[ -z "$comp" ]]; then
+  name=-default-
+  comp="$comps[-default-]"
+fi
+[[ -z "$comp" ]] || "$comp" "$@"
diff --git a/Functions/Completion/_path_files b/Functions/Completion/_path_files
new file mode 100644
index 000000000..7db364b7d
--- /dev/null
+++ b/Functions/Completion/_path_files
@@ -0,0 +1,272 @@
+#autoload
+
+# 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.
+#
+# E.g.: _path_files -g '*.tex' '*.texi'
+#
+# This is intended as a replacement for `complist -f', `complist -/', and
+# `complist -g ...' (but don't use it with other options).
+#
+# You may also give the `-W <spec>' option as with `compctl' and `complist',
+# but only as the first argument.
+#
+# This function also accepts an optional `-F <string>' option as its first
+# argument or just after the `-W <spec>'. This can be used to define file
+# name extension (a la `fignore'). Files with such an extension will not
+# be considered possible completions.
+#
+# This function behaves as if you have a matcher definition like:
+#   compctl -M 'r:|[-.,_/]=* r:|=* m:{a-z}={A-Z} m:-=_ m:.=,' \
+#              'm:{a-z}={A-Z} l:|=* r:|=*'
+# so you may want to modify this.
+
+local nm prepaths str linepath realpath donepath patstr prepath testpath rest
+local tmp1 collect tmp2 suffixes i ignore
+
+setopt localoptions nullglob rcexpandparam globdots extendedglob
+unsetopt markdirs globsubst shwordsplit nounset
+
+# Get the optional `-W' option and its argument.
+if [[ "$1" = -W ]]; then
+  tmp1="$2"
+  if [[ "$tmp1[1]" = '(' ]]; then
+    prepaths=( $tmp1[2,-2]/ )
+  else
+    prepaths=( ${(P)${tmp1}} )
+    [[ $#prepaths -eq 0 ]] && prepaths=( $tmp1/ )
+  fi
+  [[ $#prepaths -eq 0 ]] && prepaths=( '' )
+  shift 2
+else
+  prepaths=( '' )
+fi
+
+# Get the optional `-F' option and its argument.
+if [[ "$1" = -F ]]; then
+  ignore=(-F "$2")
+  shift 2
+else
+  ignore=''
+fi
+
+# str holds the whole string from the command line with a `*' between
+# the prefix and the suffix.
+
+str="${PREFIX:q}*${SUFFIX:q}"
+
+# We will first try normal completion called with `complist', but only if we
+# weren't given a `-F' option.
+
+if [[ -z "$ignore" ]]; then
+  # First build an array containing the `-W' option, if there is any and we
+  # want to use it. We don't want to use it if the string from the command line
+  # is a absolute path or relative to the current directory.
+
+  if [[ -z "$tmp1[1]" || "$str[1]" = [~/] || "$str" = (.|..)/* ]]; then
+    tmp1=()
+  else
+    tmp1=(-W "( $prepaths )")
+  fi
+
+  # Now call complist.
+
+  nm=$NMATCHES
+  if [[ $# -eq 0 ]]; then
+    complist "$tmp1[@]" -f
+  elif [[ "$1" = -g ]]; then
+    complist "$tmp1[@]" -g "$argv[2,-1]"
+    shift
+  else
+    complist "$tmp1[@]" $1
+    shift
+  fi
+
+  # If this generated any matches, we don't wnat to do in-path completion.
+
+  [[ -nmatches nm ]] || return
+
+  # No `-F' option, so we want to use `fignore'.
+
+  ignore=(-F fignore)
+fi
+
+# If we weren't given any file patterns as arguments, we trick ourselves
+# into believing that we were given the pattern `*'. This is just to simplify
+# the following code.
+
+[[ -z "$1" ]] && 1='*'
+
+# Now let's have a closer look at the string to complete.
+
+if [[ "$str[1]" = \~ ]]; then
+  # It begins with `~', so remember anything before the first slash to be able
+  # to report it to the completion code. Also get an expanded version of it
+  # (in `realpath'), so that we can generate the matches. Then remove that
+  # prefix from the string to complete, set `donepath' to build the correct
+  # paths and make sure that the loop below is run only once with an empty
+  # prefix path by setting `prepaths'.
+  
+  linepath="${str%%/*}/"
+  eval realpath\=path
+  str="${str#*/}"
+  donepath=''
+  prepaths=( '' )
+else
+  # If the string does not start with a `~' we don't remove a prefix from the
+  # string.
+
+  liniepath=''
+  realpath=''
+
+  if [[ "$str[1]" = / ]]; then
+    # If it is a absolut path name, we remove the first slash and put it in
+    # `donepath' meaning that we treat it as the path that was already handled.
+    # Also, we don't use the paths from `-W'.
+
+    str="$str[2,-1]"
+    donepath='/'
+    prepaths=( '' )
+  else
+    # The common case, we just use the string as it is, unless it begins with
+    # `./' or `../' in which case we don't use the paths from `-W'.
+    
+    [[ "$str" = (.|..)/* ]] && prepaths=( '' )
+    donepath=''
+  fi
+fi
+
+# First we skip over all pathname components in `str' which really exist in
+# the file-system, so that `/usr/lib/l<TAB>' doesn't offer you `lib' and
+# `lib5'. Pathname components skipped this way are taken from `str' and added
+# to `donepath'.
+
+while [[ "$str" = */* ]] do
+  [[ -e "$realpath$donepath${str%%/*}" ]] || break
+  donepath="$donepath${str%%/*}/"
+  str="${str#*/}"
+done
+
+# Now build the glob pattern. As noted above, this function behaves as if
+# a global matcher with two matching specifications are given.
+
+if [[ -matcher 1 ]]; then
+  patstr="$str:gs/,/*,/:gs/_/*_/:gs./.*/.:gs/-/*[-_]/:gs/./*[.,]/:gs-*[.,]*[.,]*/-../-:gs.**.*."
+else
+  patstr="${str%/*}/*${str##*/}*"
+  patstr="$patstr:gs./.*/.:gs.**.*."
+fi
+
+# Finally, generate the matches. First we loop over all the paths from `-W'.
+# Note that in this loop `str' is used as a modifyable version of `patstr'
+# and `testpath' is a modifyable version of `donepath'.
+
+for prepath in "$prepaths[@]"; do
+  str="$patstr"
+  testpath="$donepath"
+
+  # The second loop tests the components of the path in `str' to get the
+  # possible matches.
+
+  while [[ "$str" = */* ]] do
+    # `rest' is the pathname after the first slash that is left. In `tmp1'
+    # we get the globbing matches for the pathname component currently
+    # handled.
+
+    rest="${str#*/}"
+    tmp1="${prepath}${realpath}${testpath}(#l)${str%%/*}(-/)"
+    tmp1=( $~tmp1 )
+
+    if [[ $#tmp1 -eq 0 ]]; then
+      # If this didn't produce any matches, we don't need to test this path
+      # any further, so continue with the next `-W' path, if any.
+
+      continue 2
+    elif [[ $#tmp1 -gt 1 ]]; then
+      # If it produced more than one match, we want to remove those which
+      # don't have possible following pathname components matching the 
+      # rest of the string we are completing. (The case with only one
+      # match is handled below.)
+      # In `collect' we will collect those of the produced pathnames that
+      # have a matching possible path-suffix. In `suffixes' we build an
+      # array containing strings build from the rest of the string to 
+      # complete and the glob patterns we were given as arguments.
+
+      collect=()
+      suffixes=( $rest$@ )
+      suffixes=( "${(@)suffixes:gs.**.*.}" )
+
+      # In the loop the prefixes from the `tmp1' array produced above and
+      # the suffixes we just built are used to produce possible matches
+      # via globbing.
+
+      for i in $tmp1; do
+        tmp2=( $~i/(#l)$~suffixes )
+        [[ $#tmp2 -ne 0 ]] && collect=( $collect $i )
+      done
+
+      # If this test showed that none of the matches from the glob in `tmp1'
+      # has a possible sub-path matching what's on the line, we give up and
+      # continue with the next `-W' path.
+
+      if [[ $#collect -eq 0 ]]; then
+        continue 2
+      elif [[ $#collect -ne 1 ]]; then
+        # If we have more than one possible match, this means that the
+	# pathname component currently handled is ambiguous, so we give
+	# it to the completion code.
+	# First we build the full path prefix in `tmp1'.
+
+        tmp1="$prepath$realpath$testpath"
+
+	# Now produce all matching pathnames in `collect'.
+
+        collect=( $~collect/(#l)$~suffixes )
+
+	# And then remove the common path prefix from all these matches.
+
+        collect=( ${collect#$tmp1} )
+
+	# Finally, we add all these matches with the common (unexpanded)
+	# pathprefix (the `-p' option), the path-prefix (the `-W' option)
+	# to allow the completion code to test file type, and the path-
+	# suffix (the `-s' option). We also tell the completion code that
+	# these are file names and that `fignore' should be used as usual
+	# (the `-f' and `-F' options).
+
+        for i in $collect; do
+          compadd -p "$linepath$testpath" -W "$tmp1" -s "/${i#*/}" -f "$ignore[@]" - "${i%%/*}"
+        done
+
+	# We have just finished handling all the matches from above, so we
+	# can continue with the next `-W' path.
+
+	continue 2
+      fi
+      # We reach this point if only one of the path prefixes in `tmp1'
+      # has a existing path-suffix matching the string from the line.
+      # In this case we accept this match and continue with the next
+      # path-name component.
+
+      tmp1=( "$collect[1]" )
+    fi
+    # This is also reached if the first globbing produced only one match
+    # in this case we just continue with the next pathname component, too.
+
+    tmp1="$tmp1[1]"
+    testpath="$testpath${tmp1##*/}/"
+    str="$rest"
+  done
+
+  # We are here if all pathname components except the last one (which is still
+  # not tested) are unambiguous. So we add matches with the full path prefix, 
+  # no path suffix, the `-W' we are currently handling, all the matches we
+  # can produce in this directory, if any.
+
+  tmp1="$prepath$realpath$testpath"
+  suffixes=( $str$@ )
+  suffixes=( "${(@)suffixes:gs.**.*.}" )
+  tmp2=( $~tmp1(#l)$~suffixes )
+  compadd -p "$linepath$testpath" -W "$prepath$realpath$testpath" -f "$ignore[@]" - ${tmp2#$tmp1}
+done
diff --git a/Functions/Completion/_pdf b/Functions/Completion/_pdf
new file mode 100644
index 000000000..7d7756c3e
--- /dev/null
+++ b/Functions/Completion/_pdf
@@ -0,0 +1,3 @@
+function acroread
+
+_files -g '*.(pdf|PDF)'
diff --git a/Functions/Completion/_precommand b/Functions/Completion/_precommand
new file mode 100644
index 000000000..2cf661147
--- /dev/null
+++ b/Functions/Completion/_precommand
@@ -0,0 +1,5 @@
+#defcomp - nohup nice eval time rusage noglob nocorrect exec
+
+[[ -position 1 -1 ]]
+
+_normal "$@"
diff --git a/Functions/Completion/_ps b/Functions/Completion/_ps
new file mode 100644
index 000000000..6bc0643b2
--- /dev/null
+++ b/Functions/Completion/_ps
@@ -0,0 +1,3 @@
+#defcomp  gs ghostview gview psnup psselect pswrap pstops pstruct lpr
+
+_files -g '*([pP][sS]|eps)'
diff --git a/Functions/Completion/_rcs b/Functions/Completion/_rcs
new file mode 100644
index 000000000..537db6278
--- /dev/null
+++ b/Functions/Completion/_rcs
@@ -0,0 +1,9 @@
+#defcomp co ci rcs
+
+[[ $COMMAND = ci || $COMMAND = rcs ]] && _files
+
+if [[ $NMATCHES -eq 0 && -d RCS && $COMMAND != ci ]]; then
+  local rep
+  rep=(RCS/$PREFIX*$SUFFIX,v(:t:s/\,v//))
+  (( $#rep )) && compadd -m $rep
+fi
diff --git a/Functions/Completion/_rlogin b/Functions/Completion/_rlogin
new file mode 100644
index 000000000..e36554f23
--- /dev/null
+++ b/Functions/Completion/_rlogin
@@ -0,0 +1,9 @@
+#defcomp rlogin rsh ssh
+
+if [[ -position 1 ]]; then
+  complist -k hosts
+elif [[ -position 2 ]]; then
+  complist -k '(-l)'
+else
+  complist -u
+fi
diff --git a/Functions/Completion/_sched b/Functions/Completion/_sched
new file mode 100644
index 000000000..ee785879b
--- /dev/null
+++ b/Functions/Completion/_sched
@@ -0,0 +1,3 @@
+#defcomp sched
+
+[[ -position 2 -1 ]] && compsub
diff --git a/Functions/Completion/_set b/Functions/Completion/_set
new file mode 100644
index 000000000..e3069f134
--- /dev/null
+++ b/Functions/Completion/_set
@@ -0,0 +1,7 @@
+#defcomp  set
+
+if [[ -mcurrent -1 [-+]o ]]; then
+  complist -o
+elif [[ -current -1 -A ]]; then
+  complist -A
+fi
diff --git a/Functions/Completion/_setopt b/Functions/Completion/_setopt
new file mode 100644
index 000000000..4abb3ccee
--- /dev/null
+++ b/Functions/Completion/_setopt
@@ -0,0 +1,7 @@
+#defcomp 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
diff --git a/Functions/Completion/_source b/Functions/Completion/_source
new file mode 100644
index 000000000..94e3fbeb1
--- /dev/null
+++ b/Functions/Completion/_source
@@ -0,0 +1,7 @@
+#defcomp source
+
+if [[ -position 2 -1 ]]; then
+  compsub
+else
+  _files
+fi
diff --git a/Functions/Completion/_strip b/Functions/Completion/_strip
new file mode 100644
index 000000000..6962ac455
--- /dev/null
+++ b/Functions/Completion/_strip
@@ -0,0 +1,2 @@
+#defcomp strip
+_files -g '*(*)'
diff --git a/Functions/Completion/_stty b/Functions/Completion/_stty
new file mode 100644
index 000000000..6b54b5007
--- /dev/null
+++ b/Functions/Completion/_stty
@@ -0,0 +1,16 @@
+#defcomp stty
+
+if [[ -mcurrent -1 \
+  (*erase|discard|status|dsusp|intr|kill|lnext|quit|reprint|start|s*p) ]]
+then
+     compadd -m -Q '^-' '^h' '^?' '^c' '^u'
+else
+  [[ -string '-' || -string '+' ]]
+  compadd -m rows columns intr quit erase kill eof eol \
+    eol2 start stop susp dsusp reprint discard werase lnext \
+    parenb parodd cs8 cstopb hupcl cread clocal parext \
+    ignbrk brkint ignpar parmrk inpck istrip inlcr igncr icrnl iuclc \
+    ixon ixany ixoff imaxbel isig icanon xcase echo echoe echok \
+    echonl noflsh tostop echoctl echoprt echoke flusho pending iexten \
+    opost olcuc onlcr ocrnl onocr onlret ofill ofdel 
+fi
diff --git a/Functions/Completion/_subscript b/Functions/Completion/_subscript
new file mode 100644
index 000000000..2b827a117
--- /dev/null
+++ b/Functions/Completion/_subscript
@@ -0,0 +1,4 @@
+#defcomp -subscript-
+
+_compalso -math- "$@"
+[[ ${(Pt)${COMMAND}} = assoc* ]] && complist -k "( ${(kP)${COMMAND}} )"
diff --git a/Functions/Completion/_tar b/Functions/Completion/_tar
new file mode 100644
index 000000000..91767e44d
--- /dev/null
+++ b/Functions/Completion/_tar
@@ -0,0 +1,11 @@
+#defcomp tar
+
+local nm=$NMATCHES tf="$2"
+
+if [[ ( -mword 1 *t*f* || -mword 1 *x*f* ) && -position 3 100000 ]]; then
+  complist -k "( $(tar tf $tf) )"
+elif [[ -mword 1 *c*f* && -position 3 100000 ]]; then
+  _files
+elif [[ -mcurrent -1 *f* && -position 2 ]]; then
+  _files -g '*.(tar|TAR)'
+fi
diff --git a/Functions/Completion/_tex b/Functions/Completion/_tex
new file mode 100644
index 000000000..1f645e2a2
--- /dev/null
+++ b/Functions/Completion/_tex
@@ -0,0 +1,3 @@
+#defcomp tex latex slitex
+
+_files -g '*.(tex|TEX|texinfo|texi)'
diff --git a/Functions/Completion/_trap b/Functions/Completion/_trap
new file mode 100644
index 000000000..59e81c589
--- /dev/null
+++ b/Functions/Completion/_trap
@@ -0,0 +1,7 @@
+#defcomp trap
+
+if [[ -position 1 ]]; then
+  complist -c
+else
+  complist -k signals
+fi
diff --git a/Functions/Completion/_uncompress b/Functions/Completion/_uncompress
new file mode 100644
index 000000000..e25805d50
--- /dev/null
+++ b/Functions/Completion/_uncompress
@@ -0,0 +1,3 @@
+#defcomp uncompress zmore
+
+_files -g '*.Z'
diff --git a/Functions/Completion/_unhash b/Functions/Completion/_unhash
new file mode 100644
index 000000000..fe40c25a2
--- /dev/null
+++ b/Functions/Completion/_unhash
@@ -0,0 +1,6 @@
+#defcomp unhash
+
+[[ -mword 1 -*d* ]] && complist -n
+[[ -mword 1 -*a* ]] && complist -a
+[[ -mword 1 -*f* ]] && complist -F
+[[ ! -mword 1 -* ]] && complist -m
diff --git a/Functions/Completion/_unsetopt b/Functions/Completion/_unsetopt
new file mode 100644
index 000000000..90d642b51
--- /dev/null
+++ b/Functions/Completion/_unsetopt
@@ -0,0 +1,7 @@
+#defcomp 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
diff --git a/Functions/Completion/_vars b/Functions/Completion/_vars
new file mode 100644
index 000000000..7153b6f38
--- /dev/null
+++ b/Functions/Completion/_vars
@@ -0,0 +1,3 @@
+#defcomp -math- getopts read unset vared
+
+complist -v
diff --git a/Functions/Completion/_vars_eq b/Functions/Completion/_vars_eq
new file mode 100644
index 000000000..fcbb0148c
--- /dev/null
+++ b/Functions/Completion/_vars_eq
@@ -0,0 +1,3 @@
+#defcomp declare export integer local readonly typeset
+
+complist -v -q -S '='
diff --git a/Functions/Completion/_wait b/Functions/Completion/_wait
new file mode 100644
index 000000000..34bc53ee3
--- /dev/null
+++ b/Functions/Completion/_wait
@@ -0,0 +1,4 @@
+#defcomp wait
+
+complist -P '%' -j
+complist -y _kill_helper -s '`ps 2>/dev/null | tail +2 | cut -c1-5`'
diff --git a/Functions/Completion/_which b/Functions/Completion/_which
new file mode 100644
index 000000000..324256e3d
--- /dev/null
+++ b/Functions/Completion/_which
@@ -0,0 +1,3 @@
+#defcomp which whence where type
+
+complist -caF
diff --git a/Functions/Completion/_x_options b/Functions/Completion/_x_options
new file mode 100644
index 000000000..cc469286d
--- /dev/null
+++ b/Functions/Completion/_x_options
@@ -0,0 +1,5 @@
+#defpatcomp */X11/*
+
+# A simple pattern completion, just as an example.
+
+complist -J options -k '(-display -name -xrm)'
diff --git a/Functions/Completion/_xfig b/Functions/Completion/_xfig
new file mode 100644
index 000000000..fcd2bba9f
--- /dev/null
+++ b/Functions/Completion/_xfig
@@ -0,0 +1,3 @@
+#defcomp xfig
+
+_files -g '*.fig'
diff --git a/Functions/Completion/_zftp b/Functions/Completion/_zftp
new file mode 100644
index 000000000..4ff707cc6
--- /dev/null
+++ b/Functions/Completion/_zftp
@@ -0,0 +1,50 @@
+#defpatcomp zf*
+
+# Don't try any more completion after this.
+COMPSKIP=1
+
+# Completion for zftp builtin and zf* functions.  The functions
+# zfcd_match and zfget_match (used for old-style completion)
+# need to be installed for remote file and directory completion to work.
+
+local subcom
+
+if [[ $COMMAND = zftp ]]; then
+  if [[ $CURRENT -eq 1 ]]; then
+    compadd -m open params user login type ascii binary mode put \
+      putat get getat append appendat ls dir local remote mkdir rmdir
+    return
+  fi
+  subcom=$1
+else
+  subcom=$COMMAND
+fi
+
+case $subcom in
+  *(cd|ls|dir))
+   # complete remote directories; we could be smarter about hiding prefixes
+   zfcd_match $PREFIX $SUFFIX
+   (( $#reply )) && compadd -m -S/ -q $reply
+   ;;
+
+  *(get(|at)|gcp|delete|remote))
+   # complete remote files
+   zfget_match $PREFIX $SUFFIX
+   (( $#reply )) && compadd -F fignore -m $reply
+   ;;
+
+  *(put(|at)|pcp))
+   # complete local files
+   _files
+   ;;
+
+  *(open|anon|params))
+  # complete hosts:  should do cleverer stuff with user names
+  complist -k hosts
+  ;;
+
+  *)
+  # dunno... try ordinary completion after all.
+  unset COMPSKIP   
+  ;;
+esac
diff --git a/Functions/Completion/_zle b/Functions/Completion/_zle
new file mode 100644
index 000000000..bb1102e74
--- /dev/null
+++ b/Functions/Completion/_zle
@@ -0,0 +1,7 @@
+#defcomp zle
+
+if [[ -word 1 -N && -position 3 ]]; then
+  complist -F
+else
+  complist -b
+fi
diff --git a/Functions/Completion/_zmodload b/Functions/Completion/_zmodload
new file mode 100644
index 000000000..112acb57c
--- /dev/null
+++ b/Functions/Completion/_zmodload
@@ -0,0 +1,9 @@
+#defcomp 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
diff --git a/Functions/Completion/dump b/Functions/Completion/dump
new file mode 100644
index 000000000..ad5547bea
--- /dev/null
+++ b/Functions/Completion/dump
@@ -0,0 +1,89 @@
+# This is a file to be sourced to dump the definitions for new-style
+# completion defined by 'init' in the same directory.  For best results,
+# it should be run immediately after init, before any of the completions
+# have been autoloaded.  The output should be directed into the "init.dump"
+# in the same directory as init.  If you rename init, just stick .dump onto
+# the end of whatever you have called it and put it in the same directory.
+#
+# You will need to update the dump every time you add a new completion.
+# To do this, simply remove the .dump file, start a new shell, and
+# create the .dump file as before.
+#
+# It relies on KSH_ARRAYS not being set.
+
+# Print the number of files used for completion. This is used in init
+# to see if auto-dump should re-dump the dump-file.
+
+_d_file=${COMPDUMP-${0:h}/init.dump}
+
+_d_files=( ${^~fpath}/_*~*~ )
+
+print "#files: $#_d_files" > $_d_file
+
+unset _d_files
+
+# First dump the arrays comps and patcomps.  The quoting hieroglyphyics
+# ensure that a single quote inside a variable is itself correctly quoted.
+
+print "comps=(" >> $_d_file
+for _d_f in ${(k)comps}; do
+    print -r - "'${_d_f//\'/'\\''}'" "'${comps[$_d_f]//\'/'\\''}'"
+done  >> $_d_file
+print ")" >> $_d_file
+
+if (( $#patcomps )); then
+  print "\npatcomps=(" >> $_d_file
+  for _d_f in "$patcomps[@]"; do
+    print -r - "'${_d_f//\'/'\\''}'"
+  done >> $_d_file
+  print ")" >> $_d_file
+fi
+
+print >> $_d_file
+
+# Now dump the key bindings. We dump all bindings for zle widgets
+# whose names start with a underscore.
+# We need both the zle -C's and the bindkey's to recreate.
+
+_d_bks=()
+zle -lL |
+  while read -rA _d_line; do
+    if [[ ${_d_line[5]} = _* ]]; then
+      print -r - ${_d_line}
+      _d_bks=($_d_bks ${_d_line[3]})
+    fi
+  done >> $_d_file
+bindkey |
+  while read -rA _d_line; do
+    if [[ ${_d_line[2]} = (${(j.|.)~_d_bks}) ]]; then
+      print -r "bindkey '${_d_line[1][2,-2]}' ${_d_line[2]}"
+    fi
+  done >> $_d_file
+
+print >> $_d_file
+
+
+# Autoloads: whence -w produces "_d_foo: function", so look for
+# all functions beginning with `_'.
+
+_d_als=($(whence -wm '_*' |
+while read -rA _d_line; do
+  [[ ${_d_line[2]} = function ]] && print -r - ${_d_line[1]%:}
+done))
+
+# print them out:  about six to a line looks neat
+
+while (( $#_d_als )); do
+  print -n autoload
+  for (( _i = 0; _i < 6; _i++ )); do
+    if (( $#_d_als )); then
+      print -n " $_d_als[1]"
+      shift _d_als
+    fi
+  done
+  print
+done >> $_d_file
+
+print >> $_d_file
+
+unset _d_line _d_zle _d_bks _d_als _d_f _f_file
diff --git a/Functions/Completion/init b/Functions/Completion/init
index a40c5f61b..35e7f6a3c 100644
--- a/Functions/Completion/init
+++ b/Functions/Completion/init
@@ -3,34 +3,22 @@
 # directory that will automatically be made autoloaded (see the end of this
 # file).
 # The names of the files that will be considered for autoloading have to
-# start with two underscores (like `__setopt).
+# start with a underscores (like `_setopt).
 # The first line of these files will be read and has to say what should be
 # done with its contents:
 #
-#   `#function <names ...>'
+#   `#defcomp <names ...>'
 #     if the first line looks like this, the file is
 #     autoloaded as a function and that function will
 #     be called to generate the matches when completing
 #     for one of the commands whose <name> is given
 #
-#   `#array <names ...>'
-#     with a first line like this, the filename is taken as
-#     the name of an array; when trying to generate matches
-#     for the command <name>, the file will be sourced and
-#     should define this array, the builtin `complist' will
-#     then be called with the elements of this array as its
-#     arguments; this is intended for simple definitions
-#     for which you don't need a shell function
-#
-#   `#pattern-function <pattern>'
+#   `#defpatcomp <pattern>'
 #     this defines a function that should be called to generate
 #     matches for commands whose name matches <pattern>; note
 #     that only one pattern may be given
 #
-#   `#pattern-array <pattern>'
-#     like `#pattern-function' but defining an array
-#
-#   `#key-function <style> [ <key-sequence> ... ]
+#   `#defkeycomp <style> [ <key-sequence> ... ]
 #     this is used to bind special completions to all the given
 #     <key-sequence>(s). The <style> is the name of one of the built-in
 #     completion widgets (complete-word, delete-char-or-list,
@@ -41,45 +29,47 @@
 #     rather than by the context.  The widget has the same name as
 #     the autoload file and can be bound using bindkey in the normal way.
 #
-#   `#key-array <style> [ <key-sequence> ... ]
-#     like `#key-function', but defining an array instead
-#
-#   `#helper'
+#   `#autoload'
 #     this is for helper functions that are not used to
 #     generate matches, but should automatically be loaded
 #     when they are called
 #
 # Note that no white space is allowed between the `#' and the rest of
 # the string.
+#
+# See the file `dump' for how to speed up initialiation.
 
+# If we got the `-d'-flag, we will automatically dump the new state (at
+# the end).
 
-# An associative array for completions definitions. The keys of the entries
-# are the names of the command, the values are names of functions or variables
-# that are to be used to generate the matches.
-# Pattern completions will be stored in an normal array named `patcomps'.
-# Completion definitions bound directly to keys are stored in an assoc array
-# named `keycomps'.
+if [[ "$1" = -d ]]; then
+  _i_autodump=1
+else
+  _i_autodump=0
+fi
+
+# The associative array containing the definitions for the commands.
+# Definitions for patterns will be stored in the normal array `patcomps'.
 
 typeset -A comps
-typeset -A keycomps
 
 
 # This may be used to define completion handlers. The first argument is the
-# name of the function or variable containing the definition, the other
-# arguments are the command names for which this definition should be used.
-# With only one argument the function/variable-name __$1 is used.
+# name of the function containing the definition, the other arguments are the
+# command names for which this definition should be used.
+# With only one argument the function/variable-name _$1 is used.
 # If given the `-a' option, the function is defined as being autoloaded.
 
 defcomp() {
-  local name autol=''
+  local name
 
   if [[ "$1" = -a ]]; then
     shift
     autol=yes
   fi
   if [[ $# -eq 1 ]]; then
-    comps[$1]="__$1"
-    [[ -z "$autol" ]] || autoload "__$1"
+    comps[$1]="_$1"
+    [[ -z "$autol" ]] || autoload "_$1"
   else
     name="$1"
     shift
@@ -90,10 +80,9 @@ defcomp() {
   fi
 }
 
-
 # Almost like `defcomp', but this always gets two arguments: the name of a
-# variable or function describing what should be completed and the pattern
-# that will be compared to the command names for which completion is attempted.
+# function describing what should be completed and the pattern that will be
+# compared to the command names for which completion is attempted.
 
 defpatcomp() {
   if [[ "$1" = -a ]]; then
@@ -101,13 +90,12 @@ defpatcomp() {
     autoload "$1"
   fi
   if (( $+patcomps )) then
-    patcomps=("$patcomps[@]" "$2 $1" )
+    patcomps=("$patcomps[@]" "$2 $1")
   else
-    patcomps=( "$2 $1" )
+    patcomps=("$2 $1")
   fi
 }
 
-
 # This is used to define completion handlers directly bound to keys. The
 # first argument is as for `defcomp', giving the handler. The second
 # argument is the name of one of the built-in completion widgets. Any
@@ -122,15 +110,10 @@ defkeycomp() {
   if [[ "$1" = -a ]]; then
     shift
     autoload "$1"
-    name="$1"
-  elif [[ "${1[1]}" = ' ' ]]; then
-    name="${1:t}"
-  else
-    name="$1"
   fi
-  keycomps[$name]="$1"
+  name="$1"
   shift
-  zle -C "$name" "$1" __main_key_complete
+  zle -C "$name" "$1" "$name"
   shift
   while (( $# )); do
     bindkey "$1" "$name"
@@ -138,116 +121,101 @@ defkeycomp() {
   done
 }
 
-# These can be used to easily save and restore the state of the special
-# variables used by the completion code.
-
-alias compsave='local _oprefix _oiprefix _oargv _ocurrent; \
-                _oprefix="$PREFIX"; \
-                _oiprefix="$IPREFIX"; \
-                _oargv=( "$@" ); \
-                _ocurrent="$CURRENT"'
-alias compreset='PREFIX="$_oprefix"; \
-                 IPREFIX="$_oiprefix"; \
-                 argv=( "$_oargv[@]" ); \
-		 CURRENT="$_ocur"'
-
-
-# This is an easy way to get completion for sub-commands.
-
-alias compsub='__normal "$@" || return 1'
-
-
 # This searches $1 in the array for normal completions and calls the result.
 
-compalso() {
+_compalso() {
   local tmp
 
   tmp="$comps[$1]"
-  [[ -z "$tmp" ]] || callcomplete comps "$1" "$@"
+  [[ -z "$tmp" ]] || "$tmp" "$@"
 }
 
+# These can be used to easily save and restore the state of the special
+# variables used by the completion code.
 
-# This generates matches. The first argument is the name of one of the
-# arrays containing completion definitions. The second argument is the index
-# into this array. The other arguments are the positional parameters to give
-# to the completion function (containing the arguments from the command line).
-
-callcomplete() {
-  local file def
+alias _compsave='local _oprefix$_level _oiprefix$_level _oargv$_level _ocurrent$_level _ocommand$_level _ocontext$_level; \
+                eval "_oprefix${_level}=\"\$PREFIX\"; \
+                      _oiprefix${_level}=\"\$IPREFIX\"; \
+                      _oargv${_level}=( \"\$@\" ); \
+                      _ocurrent${_level}=\"\$CURRENT\"; \
+		      _ocommand${_level}=\"\$COMMAND\"; \
+		      _ocontext${_level}=\"\$CONTEXT\""'
+alias _compreset='eval "PREFIX=\"\$_oprefix${_level}\"; \
+                        IPREFIX=\"\$_oiprefix${_level}\"; \
+                        argv=( \"\$_oargv${_level}[@]\" ); \
+	  	        CURRENT=\"\$_ocur${_level}\"; \
+                        COMMAND=\"\$_ocommand${_level}\"; \
+                        CONTEXT=\"\$_ocontext${_level}\""'
 
-  # Get the definition from the array.
+# These can be used to build tests that modify the special parameters
+# without having to reset them by hand.
 
-  eval "def=\$${1}[${2}]"
+alias _if='(( $+_level )) || local _level=0; (( _level++ )); _compsave; if'
+alias _elif='_compreset; elif'
+alias _else='_compreset; else'
+alias _fi='_compreset; fi; unset _oprefix$_level _oiprefix$_level _oargv$_level _ocurrent$_level _ocommand$_level _ocontext$_level; (( _level-- ))'
 
-  # If the definition starts with a space then this means that we should
-  # source a file to get the definition for an array.
 
-  if [[ "$def[1]" = ' ' ]]; then
-    # The definition starts with a space, so source the file and change
-    # the definition.
+# Now we automatically make the definition files autoloaded.
 
-    file="$def[2,-1]"
-    builtin . "$file"
-    def="${file:t}"
-    eval "${1}[${2}]=$def"
-  fi
+# First we get the name of a dump file if this will be used.
 
-  # Get rid of the array-name and -index.
+: ${COMPDUMP:=$0.dump}
 
-  shift 2
-  if [[ ${(P)+def} -eq 1 ]]; then
-    # It is a parameter name, call complist directly.
+_i_files=( ${^~fpath}/_*~*~ )
+_i_initname=$0
+_i_done=''
 
-    complist "${(@P)def}"
-  else
-    # Otherwise it's a function name, call this function.
+# If we have a dump file, load it.
 
-    "$def" "$@"
+if [[ -f "$COMPDUMP" ]]; then
+  read -rA _i_line < "$COMPDUMP"
+  if [[ _i_autodump -eq 1 || $_i_line[2] -eq $#_i_files ]]; then
+    builtin . "$COMPDUMP"
+    _i_done=yes
   fi
-}
+  unset _i_line
+fi
+if [[ -z "$_i_done" ]]; then
+
+  for _i_dir in $fpath; do
+    [[ $_i_dir = . ]] && continue
+    for _i_file in $_i_dir/_*~*~(N); do
+      read -rA _i_line < $_i_file
+      _i_tag=$_i_line[1]
+      shift _i_line
+      if [[ $_i_tag = '#defcomp' ]]; then
+	defcomp -a ${_i_file:t} "${_i_line[@]}"
+      elif [[ $_i_tag = '#defpatcomp' ]]; then
+	defpatcomp -a "${_i_file:t}" "${_i_line[@]}"
+      elif [[ $_i_tag = '#defkeycomp' ]]; then
+	defkeycomp -a "${_i_file:t}" "${_i_line[@]}"
+      elif [[ $_i_tag = '#autoload' ]]; then
+	autoload ${_i_file:t}
+      fi
+    done
+  done
+
+  bindkey |
+    while read -rA _i_line; do
+      if [[ "$_i_line[2]" = complete-word ||
+	"$_i_line[2]" = delete-char-or-list ||
+	"$_i_line[2]" = expand-or-complete ||
+	"$_i_line[2]" = expand-or-complete-prefix ||
+	"$_i_line[2]" = list-choices ||
+	"$_i_line[2]" = menu-complete ||
+	"$_i_line[2]" = menu-expand-or-complete ||
+	"$_i_line[2]" = reverse-menu-complete ]]; then
+	zle -C _complete_$_i_line[2] $_i_line[2] _main_complete
+	bindkey "${_i_line[1][2,-2]}" _complete_$_i_line[2]
+      fi
+    done
 
+  unset _i_dir _i_line _i_file _i_tag
 
-# Now we make the files automatically autoloaded.
-
-local dir file line func
-
-for dir in $fpath; do
-  [[ $dir = . ]] && continue
-  for file in $dir/__*~*~(N); do
-    read -rA line < $file
-    func=$line[1]
-    shift line
-    if [[ $func = '#function' ]]; then
-      defcomp -a ${file:t} "${line[@]}"
-    elif [[ $func = '#array' ]]; then
-      defcomp " $file" "${line[@]}"
-    elif [[ $func = '#pattern-function' ]]; then
-      defpatcomp -a ${file:t} "${line[@]}"
-    elif [[ $func = '#pattern-array' ]]; then
-      defcomp " $file" "${line[@]}"
-    elif [[ $func = '#key-function' ]]; then
-      defkeycomp -a "${file:t}" "${line[@]}"
-    elif [[ $func = '#key-array' ]]; then
-      defkeycomp " $file" "${line[@]}"
-    elif [[ $func = '#helper' ]]; then
-      autoload ${file:t}
-    fi
-  done
-done
-
-
-# Finally we make all this be called by changing the key bindings.
-
-bindkey | while read -A line; do
-            if [[ "$line[2]" = complete-word ||
-	    	  "$line[2]" = delete-char-or-list ||
-	    	  "$line[2]" = expand-or-complete ||
-	    	  "$line[2]" = expand-or-complete-prefix ||
-	    	  "$line[2]" = list-choices ||
-	    	  "$line[2]" = menu-complete ||
-	    	  "$line[2]" = menu-expand-or-complete ||
-	    	  "$line[2]" = reverse-menu-complete ]]; then
-              zle -C __complete_$line[2] $line[2] __main_complete
-              bindkey "${line[1][2,-2]}" __complete_$line[2]
-            fi
-          done
+  # if autodumping was requested, do it now.
+
+  (( _i_autodump )) && builtin . ${_i_initname:h}/dump
+fi
+
+unset _i_files _i_initname _i_done _i_autodump
diff --git a/Misc/zftp-functions b/Misc/zftp-functions
index a07e46d72..9d943fc1f 100644
--- a/Misc/zftp-functions
+++ b/Misc/zftp-functions
@@ -164,18 +164,21 @@ alias zfuget='noglob zfuget'
 setopt completealiases
 
 #
-# zftp completions
+# zftp completions: only use these if new-style completion is not
+# active.
 #
-compctl -f -x 'p[1]' \
-  -k '(open params user login type ascii binary mode put putat
-  get getat append appendat ls dir local remote mkdir rmdir delete
-  close quit)'  - \
-  'w[1,cd][1,ls][1,dir][1,rmdir]' -K zfcd_match -S/ -q - \
-  'W[1,get*]' -K zfget_match - 'w[1,delete][1,remote]' -K zfget_match - \
-  'w[1,open][1,params]' -k hosts -- zftp
-compctl -K zfcd_match -S/ -q zfcd zfdir zfls
-compctl -K zfget_match zfget zfgcp zfuget zfcget
-compctl -k hosts zfopen zfparams
+if [[ ${#patcomps} -eq 0 || ${patcomps[(i)zf*]} -gt ${#patcomps} ]]; then
+  compctl -f -x 'p[1]' \
+    -k '(open params user login type ascii binary mode put putat
+    get getat append appendat ls dir local remote mkdir rmdir delete
+    close quit)'  - \
+    'w[1,cd][1,ls][1,dir][1,rmdir]' -K zfcd_match -S/ -q - \
+    'W[1,get*]' -K zfget_match - 'w[1,delete][1,remote]' -K zfget_match - \
+    'w[1,open][1,params]' -k hosts -- zftp
+  compctl -K zfcd_match -S/ -q zfcd zfdir zfls
+  compctl -K zfget_match zfget zfgcp zfuget zfcget
+  compctl -k hosts zfanon zfopen zfparams
+fi
 
 function zfanon {
   local opt optlist once
@@ -245,6 +248,40 @@ function zfanon {
   fi
 }
 
+function zfautocheck {
+  # This function is used to implement auto-open behaviour.
+  #
+  # With first argument including n, don't change to the old directory; else do.
+  #
+  # Set do_close to 1 if the connection was not previously open, 0 otherwise
+  # With first arguemnt including d, don't set do_close to 1.  Broadly
+  # speaking, we use this mechanism to shut the connection after use
+  # if the connection had been explicitly closed (i.e. didn't time out,
+  # which zftp test investigates) and we are not using a directory
+  # command, which implies we are looking for something so should stay open
+  # for it.
+  
+  # Remember the old session:  zflastsession will be overwritten by
+  # a successful open.
+  local lastsession=$zflastsession
+  
+  if [[ -z $ZFTP_HOST ]]; then
+    zfopen || return 1
+    [[ $1 = *d* ]] || do_close=1
+  elif zftp test 2>/dev/null; then
+    return 0
+  else
+    zfopen || return 1
+  fi
+  
+  if [[ $1 = *n* ]]; then
+    return 0
+  else
+    zfcd ${lastsession#*:}
+  fi
+  
+}
+
 function zfcd {
   # zfcd:  change directory on the remote server.
   #
@@ -266,9 +303,11 @@ function zfcd {
   #   second, we can no do 'zfcd $PWD' and the like, and that will
   #     work just as long as the directory structures under the home match.
   
-  # Autoopen:  if not already open, hope there are parameters set up to
-  # do so.  If not, we get the right error message, so no harm done.
-  [[ -z $ZFTP_HOST ]] && { zfopen  || return 1; }
+  if [[ $1 = /* ]]; then
+    zfautocheck -dn
+  else
+    zfautocheck -d
+  fi
   
   if [[ $1 = $HOME || $1 = $HOME/* ]]; then
     1="~${1#$HOME}"
@@ -291,6 +330,7 @@ function zfcd {
   local lastdir=$ZFTP_PWD
   
   zftp cd "$@"  &&  zflastdir=$lastdir
+  print $zflastsession
 }
 
 function zfcd_match {
@@ -303,6 +343,8 @@ function zfcd_match {
   local ZFTP_VERBOSE=45
   # should we redirect 2>/dev/null or let the user see it?
   
+  local tmpf=${TMPPREFIX}zfcm$$
+  
   if [[ $ZFTP_SYSTEM = UNIX* ]]; then
     # hoo, aren't we lucky: this makes things so much easier
     setopt localoptions rcexpandparam
@@ -315,8 +357,9 @@ function zfcd_match {
     # If we're using -F, we get away with using a directory
     # to list, but not a glob.  Don't ask me why.
     # I hate having to rely on awk here.
-    reply=($(zftp ls -F $dir | 
-    awk '/\/$/ { print substr($1, 0, length($1)-1) }'))
+    zftp ls -F $dir >$tmpf
+    reply=($(awk '/\/$/ { print substr($1, 0, length($1)-1) }' $tmpf))
+    rm -f $tmpf
     if [[ $dir = / ]]; then
       reply=(${dir}$reply)
     elif [[ -n $dir ]]; then
@@ -423,6 +466,84 @@ function zfclose {
   zftp close
 }
 
+function zfcput {
+  # Continuation put of files from remote server.
+  # For each file, if it's shorter over there, put the remainder from
+  # over here.  This uses append, which is standard, so unlike zfcget it's
+  # expected to work on any reasonable server... err, as long as it
+  # supports SIZE and MDTM.  (It could be enhanced so you can enter the
+  # size so far by hand.)  You should probably be in binary transfer
+  # mode, thought it's not enforced.
+  #
+  # To read from midway through a local file, `tail +<n>c' is used.
+  # It would be nice to find a way of doing this which works on all OS's.
+  
+  setopt localoptions
+  unsetopt ksharrays shwordsplit
+  
+  local loc rem stat=0 locst remst offs tailtype
+  local tmpfile=${TMPPREFIX}zfcget$$ rstat
+  
+  # find how tail works.  this is intensely annoying, since it's completely
+  # standard in C.  od's no use, since we can only skip whole blocks.
+  if [[ $(echo abcd | tail +2c) = bcd ]]; then
+    tailtype=c
+  elif [[ $(echo abcd | tail --bytes=+2) = bcd ]]; then
+    tailtype=b
+  else
+    print "I can't get your \`tail' to start from from arbitrary characters.\n" \
+    "If you know how to do this, let me know." 2>&1
+    return 1
+  fi
+  
+  for loc in $*; do
+    # zfcd directory hack to put the front back to ~
+    rem=$loc
+    if [[ $rem = $HOME || $rem = $HOME/* ]]; then
+      rem="~${rem#$HOME}"
+    fi
+    if [[ ! -r $loc ]]; then
+      print "Can't read file $loc"
+      stat=1
+    else
+      # Compare the sizes.
+      locst=($(zftp local $loc))
+      zftp remote $rem >$tmpfile
+      rstat=$?
+      remst=($(<$tmpfile))
+      rm -f $tmpfile
+      if [[ $rstat = 2 ]]; then
+        print "Server does not support remote status commands.\n" \
+        "You will have to find out the size by hand and use zftp append." 2>&1
+        stat=1
+        continue
+      elif [[ $rstat = 1 ]]; then
+        # Not found, so just do a standard put.
+        zftp put $rem <$loc
+      elif [[ $remst[1] -gt $locst[1] ]]; then
+        print "Remote file is larger!" 2>&1
+        continue;
+      elif [[ $locst[1] == $remst[1] ]]; then
+        print "Files are already the same size." 2>&1
+        continue
+      else
+        # tail +<N>c takes the count of the character
+        # to start from, not the offset from zero. if we did
+        # this with years, then 2000 would be 1999.  no y2k bug!
+        # brilliant.
+        (( offs = $remst[1] + 1 ))
+        if [[ $tailtype = c ]]; then
+  	tail +${offs}c $loc | zftp append $rem || stat=1
+        else
+  	tail --bytes=+$offs $loc | zftp append $rem || stat=1
+        fi
+      fi
+    fi
+  done
+  
+  return $stat
+}
+
 function zfdir {
   # Long directory of remote server.
   # The remote directory is cached.  In fact, two caches are kept:
@@ -440,9 +561,11 @@ function zfdir {
   # zfdir -f will force the existing cache to be ignored, e.g. if you know
   #          or suspect the directory has changed.
   # zfdir -d will remove both caches without listing anything.
-  # If you need to pass -r, -f or -d to the dir itself, use zfdir -- -d etc.
+  # If you need to pass -r, -f or -d to the dir itself, use zfdir -- -d etc.;
+  # unrecognised options are passed through to dir, but zfdir options must
+  # appear first and unmixed with the others.
   
-  setopt localoptions unset
+  setopt localoptions unset extendedglob
   unsetopt shwordsplit ksharrays
   
   local file opt optlist redir i newargs force
@@ -451,6 +574,9 @@ function zfdir {
     if [[ $1 = - || $1 = -- ]]; then
       shift;
       break;
+    elif [[ $1 != -[rfd]## ]]; then
+      # pass options through to ls
+      break;
     fi
     optlist=${1#-}
     for (( i = 1; i <= $#optlist; i++)); do
@@ -465,13 +591,13 @@ function zfdir {
   	 zftp_fcache=()
   	 return 0
   	 ;;
-        *) print option $opt not recognised >&2
-  	 ;;
       esac
     done
     shift
   done
   
+  zfautocheck -d
+  
   # directory hack, see zfcd
   for (( i = 1; i <= $#argv; i++ )); do
     if [[ $argv[$i] = $HOME || $argv[$i] = $HOME/* ]]; then
@@ -507,27 +633,38 @@ function zfdir {
   if [[ -n $file && -f $file ]]; then
     eval ${PAGER:-more} \$file
   else
-    zftp dir $* | tee $file | eval ${PAGER-:more}
+    if (zftp test); then
+      # Works OK in subshells
+      zftp dir $* | tee $file | eval ${PAGER-:more}
+    else
+      # Doesn't work in subshells (IRIX 6.2 --- why?)
+      zftp dir $* >$file
+      eval ${PAGER-:more} >$file
+    fi
   fi
 }
 
 function zfgcp {
   # ZFTP get as copy:  i.e. first arguments are remote, last is local.
   # Supposed to work exactly like a normal copy otherwise, i.e.
-  #  zfcp rfile lfile
+  #  zfgcp rfile lfile
   # or
-  #  zfcp rfile1 rfile2 rfile3 ... ldir
+  #  zfgcp rfile1 rfile2 rfile3 ... ldir
   # Options:
   #   -G   don't to remote globbing, else do
   #   -t   update the local file times to the same time as the remote.
   #        Currently this only works if you have the `perl' command,
   #        and that perl is version 5 with the standard library.
   #        See the function zfrtime for more gory details.
+  #
+  # If there is no current connection, try to use the existing set of open
+  # parameters to establish one and close it immediately afterwards.
   
   setopt localoptions
   unsetopt shwordsplit
   
-  local opt optlist nglob remlist rem loc stat=0 time
+  local opt optlist nglob remlist rem loc time
+  integer stat do_close
   
   while [[ $1 == -* ]]; do
     if [[ $1 == - || $1 == -- ]]; then
@@ -549,6 +686,8 @@ function zfgcp {
     shift
   done
   
+  zfautocheck
+  
   # hmm, we should really check this after expanding the glob,
   # but we shouldn't expand the last argument remotely anyway.
   if [[ $# -gt 2 && ! -d $argv[-1] ]]; then
@@ -584,6 +723,9 @@ function zfgcp {
   else
     zftp get $1 >$2 || stat=$?
   fi
+  
+  (( $do_close )) && zfclose
+  
   return $stat
 }
 
@@ -594,8 +736,14 @@ function zfget {
   #        Currently this only works if you have the `perl' command,
   #        and that perl is version 5 with the standard library.
   #        See the function zfrtime for more gory details.
+  #
+  # If the connection is not currently open, try to open it with the current
+  # parameters (set by a previous zfopen or zfparams), then close it after
+  # use.  The file is put in the current directory (i.e. using the basename
+  # of the remote file only); for more control, use zfgcp.
   
-  local loc rem stat=0 optlist opt nglob remlist time
+  local loc rem optlist opt nglob remlist time
+  integer stat do_close
   
   while [[ $1 == -* ]]; do
     if [[ $1 == - || $1 == -- ]]; then
@@ -617,6 +765,8 @@ function zfget {
     shift
   done
   
+  zfautocheck
+  
   for remlist in $*; do
     # zfcd directory hack to put the front back to ~
     if [[ $remlist == $HOME || $remlist == $HOME/* ]]; then
@@ -637,6 +787,8 @@ function zfget {
     fi
   done
   
+  (( $do_close )) && zfclose
+  
   return $stat
 }
 
@@ -646,15 +798,20 @@ function zfget_match {
     1="~${1#$HOME}"
   fi
   
+  local tmpf=${TMPPREFIX}zfgm$$
   
   if [[ $ZFTP_SYSTEM == UNIX* && $1 == */* ]]; then
     # On the first argument to ls, we usually get away with a glob.
-    reply=($(zftp ls "$1*$2"))
+    zftp ls "$1*$2" >$tmpf
+    reply=($(<$tmpf))
+    rm -f $tmpf
   else
     if (( $#zftp_fcache == 0 )); then
       # Always cache the current directory and use it
       # even if the system is UNIX.
-      zftp_fcache=($(zftp ls))
+      zftp ls >$tmpf
+      zftp_fcache=($(<$tmpf))
+      rm -f $tmpf
     fi
     reply=($zftp_fcache);
   fi
@@ -671,6 +828,9 @@ function zfls {
   if [[ $1 = $HOME || $1 = $HOME/* ]]; then
     1="~${1#$HOME}"
   fi
+  
+  zfautocheck -d
+  
   zftp ls $*
 }
 
@@ -725,15 +885,62 @@ function zfparams {
     zftp params $*
 }
 
+function zfpcp {
+  # ZFTP put as copy:  i.e. first arguments are remote, last is local.
+  # Currently only supports
+  #  zfcp lfile rfile
+  # if and only if there are two arguments
+  # or
+  #  zfcp lfile1 lfile2 lfile3 ... rdir
+  # if and only if there are more than two (because otherwise it doesn't
+  # know if the last argument is a directory on the remote machine).
+  # argument.
+  
+  setopt localoptions
+  unsetopt shwordsplit
+  
+  local rem loc
+  integer stat do_close
+  
+  zfautocheck
+  
+  if (( $# > 2 )); then
+    local dir=$argv[-1]
+    argv[-1]=
+    # zfcd directory hack to put the front back to ~
+    if [[ $dir = $HOME || $dir = $HOME/* ]]; then
+      dir="~${dir#$HOME}"
+    fi
+    for loc in $*; do
+      rem=$dir/${loc:t}
+      zftp put $rem <$loc || stat=1
+    done
+  else
+    zftp put $2 <$1 || stat=$?
+  fi
+  
+  (( $do_close )) && zfclose
+  
+  return $stat
+}
+
 function zfput {
   # Simple put:  dump every file under the same name, but stripping
   # off any directory parts.
-  local loc rem stat=0
+  
+  local loc rem
+  integer stat do_close
+  
+  zfautocheck
+  
   for loc in $*; do
     rem=${loc:t}
     zftp put $rem <$loc
     [[ $? == 0 ]] || stat=$?
   done
+  
+  (( $do_close )) && zfclose
+  
   return $stat
 }
 
@@ -764,13 +971,16 @@ function zfrglob {
   # globbing characters, and if extendedglob is set and we are
   # using zsh for the actual pattern matching also look for
   # extendedglob characters.
-  if [[ $remlist != *[][*?]* &&
-    ( -n $zfrglob || ! -o extendedglob || $remlist != *[(|)~#^]* ) ]]; then
+  if [[ $pat != *[][*?]* &&
+    ( -n $zfrglob || ! -o extendedglob || $pat != *[(|)#^]* ) ]]; then
     return 0
   fi
+  local tmpf={$TMPPREFIX}zfrglob$$
   
   if [[ $zfrglob != '' ]]; then
-    eval "$1=(\$(zftp ls \"$pat\" 2>/dev/null))"
+    zftp ls "$pat" >$tmpf 2>/dev/null
+    eval "$1=(\$(<\$tmpf))"
+    rm -f $tmpf
   else
     if [[ $ZFTP_SYSTEM = UNIX* && $pat = */* ]]; then
       # not the current directory and we know how to handle paths
@@ -781,12 +991,18 @@ function zfrglob {
         dir=/
       fi
       nondir=${pat##*/}
-      files=($(zftp ls "$dir" 2>/dev/null))
+      zftp ls "$dir" 2>/dev/null >$tmpf
+      files=($(<$tmpf))
+      files=(${files:t})
+      rm -f $tmpf
     else
       # we just have to do an ls and hope that's right
       nondir=$pat
       if (( $#zftp_fcache == 0 )); then
-        zftp_fcache=($(zftp ls))
+        # Why does `zftp_fcache=($(zftp ls))' sometimes not work?
+        zftp ls >$tmpf
+        zftp_fcache=($(<$tmpf))
+        rm -f $tmpf
       fi
       files=($zftp_fcache)
     fi
@@ -848,7 +1064,7 @@ function zfrtime {
 
 function zfstat {
   # Give a zftp status report using local variables.
-  # With option -v, connect the remote host and ask it what it
+  # With option -v, connect to the remote host and ask it what it
   # thinks the status is.  
   
   setopt localoptions unset
@@ -874,10 +1090,6 @@ function zfstat {
     shift
   done
   
-  # hack:  in case the status from a subshell process hasn't been
-  # fixed yet
-  zftp type >&/dev/null
-  
   if [[ -n $ZFTP_HOST ]]; then
     print "Host:\t\t$ZFTP_HOST"
     print "IP:\t\t$ZFTP_IP"
@@ -907,12 +1119,14 @@ function zfstat {
     fi
   else
     print "Not connected."
+    [[ -n $zflastsession ]] && print "Last session:\t$zflastsession"
     stat=1
   fi
   
   # things which may be set even if not connected:
   [[ -n $ZFTP_REPLY ]] && print "Last reply:\t$ZFTP_REPLY"
   print "Verbosity:\t$ZFTP_VERBOSE"
+  print "Timeout:\t$ZFTP_TMOUT"
   print -n "Preferences:\t"
   for (( i = 1; i <= ${#ZFTP_PREFS}; i++ )); do
     case $ZFTP_PREFS[$i] in
@@ -928,6 +1142,7 @@ function zfstat {
   print
   
   if [[ -n $ZFTP_HOST && $verbose = 1 ]]; then
+    zfautocheck -d
     print "Status of remote server:"
     # make sure we print the reply
     local ZFTP_VERBOSE=045
@@ -939,8 +1154,6 @@ function zfstat {
 
 function zftp_chpwd {
   # You may want to alter chpwd to call this when $ZFTP_USER is set.
-  # If so, call it with a non-zero first argument so it doesn't
-  # print the new FTP directory.
   
   # Cancel the filename cache for the current directory.
   zftp_fcache=()
@@ -948,26 +1161,26 @@ function zftp_chpwd {
   # As this function is called when we close the connection, this
   # is the only place we need to do these two things.
   [[ -n $zfcurdir && -f $zfcurdir ]] && rm -f $zfcurdir
+  zfotherargs=
   
   if [[ -z $ZFTP_USER ]]; then
     # last call, after an FTP logout
   
     # delete the non-current cached directory
     [[ -n $zfotherdir && -f $zfotherdir ]] && rm -f $zfotherdir
-    zfotherargs=
   
-    # don't keep zflastdir between opens
+    # don't keep zflastdir between opens (do keep zflastsession)
     zflastdir=
   
     # return the display to standard
     # uncomment the following line if you have a chpwd which shows directories
-    # chpwd
+    chpwd
   else
-    [[ -z $zflastdir ]] && zflastdir=$ZFTP_PWD
+    [[ -n $ZFTP_PWD ]] && zflastdir=$ZFTP_PWD
+    zflastsession="$ZFTP_HOST:$ZFTP_PWD"
     local args
     if [[ -t 1 && -t 2 ]]; then
-      local str="$ZFTP_HOST:$ZFTP_PWD"
-      [[ -z $1 ]] && print $str
+      local str=$zflastsession
       [[ ${#str} -lt 70 ]] && str="%m: %~  $str"
       case $TERM in
         sun-cmd) print -n -P "\033]l$str\033\\"
@@ -999,10 +1212,14 @@ function zftp_progress {
 }
 
 function zftype {
-  local type
+  local type zftmp=${TMPPREFIX}zftype$$
+  
+  zfautocheck -d
   
   if (( $# == 0 )); then
-    type=$(zftp type)
+    zftp type >$zftmp
+    type=$(<$zftmp)
+    rm -f $zftmp
     if [[ $type = I ]]; then
       print "Current type is image (binary)"
       return 0
@@ -1054,8 +1271,9 @@ function zfuget {
   setopt localoptions
   unsetopt ksharrays shwordsplit
   
-  local loc rem stat=0 locstats remstats doit tmpfile=${TMPPREFIX}zfuget$$
+  local loc rem locstats remstats doit tmpfile=${TMPPREFIX}zfuget$$
   local rstat remlist verbose optlist opt bad i silent nglob time
+  integer stat do_close
   
   zfuget_print_time() {
     local tim=$1
@@ -1097,6 +1315,8 @@ function zfuget {
   
   [[ -n $bad ]] && return 1
   
+  zfautocheck
+  
   for remlist in $*; do
     # zfcd directory hack to put the front back to ~
     if [[ $remlist == $HOME || $remlist == $HOME/* ]]; then
@@ -1166,6 +1386,9 @@ function zfuget {
       done
     fi
   done
+  
+  (( do_close )) && zfclose
+  
   return $stat
 }
 
@@ -1183,8 +1406,9 @@ function zfuput {
   setopt localoptions
   unsetopt ksharrays shwordsplit
   
-  local loc rem stat=0 locstats remstats doit tmpfile=${TMPPREFIX}zfuput$$
+  local loc rem locstats remstats doit tmpfile=${TMPPREFIX}zfuput$$
   local rstat verbose optlist opt bad i silent
+  integer stat do_close
   
   zfuput_print_time() {
     local tim=$1
@@ -1222,6 +1446,8 @@ function zfuput {
   
   [[ -n $bad ]] && return 1
   
+  zfautocheck
+  
   if [[ $ZFTP_VERBOSE = *5* ]]; then
     # should we turn it off locally?
     print "Messages with code 550 are harmless." >&2
@@ -1277,5 +1503,8 @@ function zfuput {
       zftp put $rem <$loc || stat=$?
     fi
   done
+  
+  (( do_close )) && zfclose
+  
   return $stat
 }
diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c
index d71fa642f..413dd8f43 100644
--- a/Src/Modules/zftp.c
+++ b/Src/Modules/zftp.c
@@ -554,7 +554,7 @@ zfgetline(char *ln, int lnsize, int tmout)
     if (setjmp(zfalrmbuf)) {
 	alarm(0);
 	zwarnnam("zftp", "timeout getting response", NULL, 0);
-	return 5;
+	return 6;
     }
     zfalarm(tmout);
 
@@ -676,7 +676,7 @@ zfgetmsg()
     int stopit, printing = 0, tmout;
 
     if (zcfd == -1)
-	return 5;
+	return 6;
     if (!(verbose = getsparam("ZFTP_VERBOSE")))
 	verbose = "";
     zsfree(lastmsg);
@@ -763,7 +763,7 @@ zfgetmsg()
 	zfclose();
 	/* unexpected, so tell user */
 	zwarnnam("zftp", "remote server has closed connection", NULL, 0);
-	return 6;		/* pretend it failed, because it did */
+	return 6;
     }
     if (lastcode == 530) {
 	/* user not logged in */
@@ -802,20 +802,21 @@ zfsendcmd(char *cmd)
     int ret, tmout;
 
     if (zcfd == -1)
-	return 5;
+	return 6;
     tmout = getiparam("ZFTP_TMOUT");
     if (setjmp(zfalrmbuf)) {
 	alarm(0);
 	zwarnnam("zftp", "timeout sending message", NULL, 0);
-	return 5;
+	return 6;
     }
     zfalarm(tmout);
     ret = write(zcfd, cmd, strlen(cmd));
     alarm(0);
 
     if (ret <= 0) {
-	zwarnnam("zftp send", "failed sending control message", NULL, 0);
-	return 5;		/* FTP status code */
+	zwarnnam("zftp send", "failure sending control message: %e",
+		 NULL, errno);
+	return 6;
     }
 
     return zfgetmsg();
@@ -1023,11 +1024,11 @@ zfgetdata(char *name, char *rest, char *cmd, int getsize)
 	/* accept the connection */
 	len = sizeof(zdsock);
 	newfd = zfmovefd(accept(zdfd, (struct sockaddr *)&zdsock, &len));
+	if (newfd < 0)
+	    zwarnnam(name, "unable to accept data: %e", NULL, errno);
 	zfclosedata();
-	if (newfd < 0) {
-	    zwarnnam(name, "unable to accept data.", NULL, 0);
+	if (newfd < 0)
 	    return 1;
-	}
 	zdfd = newfd;		/* this is now the actual data fd */
     } else {
 	/*
@@ -1270,7 +1271,7 @@ zfread_block(int fd, char *bf, size_t sz, int tmout)
 	    n = zfread(fd, (char *)&hdr, sizeof(hdr), tmout);
 	} while (n < 0 && errno == EINTR);
 	if (n != 3 && !zfdrrrring) {
-	    zwarnnam("zftp", "failed to read FTP block header", NULL, 0);
+	    zwarnnam("zftp", "failure reading FTP block header", NULL, 0);
 	    return n;
 	}
 	/* size is stored in network byte order */
@@ -1324,7 +1325,7 @@ zfwrite_block(int fd, char *bf, size_t sz, int tmout)
 	n = zfwrite(fd, (char *)&hdr, sizeof(hdr), tmout);
     } while (n < 0 && errno == EINTR);
     if (n != 3 && !zfdrrrring) {
-	zwarnnam("zftp", "failed to write FTP block header", NULL, 0);
+	zwarnnam("zftp", "failure writing FTP block header", NULL, 0);
 	return n;
     }
     bfptr = bf;
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index 124ae454a..0d5419817 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -261,15 +261,19 @@ struct cline {
     int llen;			/* length of line */
     char *word;			/* prefered string to insert */
     int wlen;			/* length of word */
-    Cmatcher matcher;		/* which matcher was used */
     int flags;			/* see CLF_* below */
+    Cline prefix;		/* prefix we've build for new parts */
+    Cline suffix;		/* suffix we've build for new parts */
 };
 
-#define CLF_END   1
-#define CLF_MID   2
-#define CLF_MISS  4
-#define CLF_DIFF  8
-#define CLF_SUF  16
+#define CLF_END    1
+#define CLF_MID    2
+#define CLF_MISS   4
+#define CLF_DIFF   8
+#define CLF_SUF   16
+#define CLF_NEW   32
+#define CLF_VAR   64
+#define CLF_JOIN 128
 
 /* Flags for makecomplist*(). Things not to do. */
 
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index cec71fc5b..e3b778508 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -1685,6 +1685,10 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 	return 1;
     }
     for (; *argv && **argv ==  '-'; argv++) {
+	if (!(*argv)[1]) {
+	    argv++;
+	    break;
+	}
 	for (p = *argv + 1; *p; p++) {
 	    sp = NULL;
 	    dm = 0;
@@ -1945,9 +1949,9 @@ restrict_range(int b, int e)
     pparams = p;
     zsfree(compcontext);
     if ((compcurrent -= b + 1))
-	compcontext = ztrdup("arg");
+	compcontext = ztrdup("argument");
     else
-	compcontext = ztrdup("cmd");
+	compcontext = ztrdup("command");
 }
 
 /**/
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index f1078ef11..af78e1c06 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -98,12 +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, brbsl 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, brpcs, brscs;
+static int brpl, brsl, brbsl, brpcs, brscs;
 
 /* The list of matches.  fmatches contains the matches we first ignore *
  * because of fignore.                                                 */
@@ -212,6 +212,12 @@ static LinkList ccstack;
 
 static Cmlist mstack;
 
+/* The completion matchers used when building new stuff for the line. */
+
+static struct cmlist bmstack0, bmstack1;
+static Cmlist bmstack;
+static int had_lm;
+
 /* A list with references to all matcher we used. */
 
 static LinkList matchers;
@@ -287,6 +293,12 @@ enum { COMP_COMPLETE,
 
 static int lastambig;
 
+/* Non-zero if the string on the line came from a previous completion. *
+ * This is used to detect if the string should be taken as an exact    *
+ * match (see do_ambiguous()).                                         */
+
+static int fromcomp;
+
 /**/
 void
 completecall(void)
@@ -1356,6 +1368,7 @@ get_comp_string(void)
 		    if (*p == Outbrace)
 			chuck(p);
 		    brsl = strlen(s) - (p - s);
+		    brbsl = p - s;
 		    brend[sl] = '\0';
 		}
 		/* we are still waiting for an outbrace and maybe commas */
@@ -1469,7 +1482,9 @@ addtoword(char **rwp, int *rwlenp, char *nw,
 {
     int al, rwlen = *rwlenp, nl;
     char *as, *rw = *rwp;
-    
+
+    /* Get the string and length to insert: either from the line 
+     * or from the match. */
     if (m && (m->flags & CMF_LINE)) {
 	al = m->llen;
 	as = l;
@@ -1477,6 +1492,7 @@ addtoword(char **rwp, int *rwlenp, char *nw,
 	al = wl;
 	as = w;
     }
+    /* Allocate some more space if needed. */
     if (!rwlen || (nl = al + (nw - rw)) >= rwlen) {
 	char *np;
 
@@ -1496,6 +1512,7 @@ addtoword(char **rwp, int *rwlenp, char *nw,
 	*rwp = rw = np;
 	rwlen = nl;
     }
+    /* Copy it in the buffer. */
     if (prep) {
 	memmove(rw + al, rw, rwlen - al);
 	memcpy(rw, as, al);
@@ -1506,13 +1523,15 @@ addtoword(char **rwp, int *rwlenp, char *nw,
     return nw + al;
 }
 
-/* This get a new Cline structure. */
+/* This returns a new Cline structure. */
 
 static Cline
-getcline(char *l, int ll, char *w, int wl, Cmatcher m, int fl)
+getcline(char *l, int ll, char *w, int wl, int fl)
 {
     Cline r;
 
+    /* Preverably take it from the buffer list (freecl), if there
+     * is none, allocate a new one. */
     if ((r = freecl))
 	freecl = r->next;
     else
@@ -1523,19 +1542,20 @@ getcline(char *l, int ll, char *w, int wl, Cmatcher m, int fl)
     r->llen = ll;
     r->word = w;
     r->wlen = wl;
-    r->matcher = m;
     r->flags = fl;
+    r->prefix = r->suffix = NULL;
 
     return r;
 }
 
-/* This add a Cline structure with the given parameters. */
+/* This adds a Cline structure with the given parameters to the list we
+ * are building during matching. */
 
 static void
 addtocline(Cline *retp, Cline *lrp,
 	   char *l, int ll, char *w, int wl, Cmatcher m, int fl)
 {
-    Cline ln = getcline(l, ll, w, wl, m, fl);
+    Cline ln = getcline(l, ll, w, wl, fl);
 
     if (m && (m->flags & CMF_LINE))
 	ln->word = NULL;
@@ -1547,8 +1567,1140 @@ addtocline(Cline *retp, Cline *lrp,
     *lrp = ln;
 }
 
+/* When building up cline lists that are to be inserted at the end of the
+ * string or on the left hand side in the middle, we do this separately for
+ * multiple runs of characters separated by the anchors of `*' match patterns.
+ * This function builds such a list from the given string. */
+
+static Cline
+end_list(int len, char *str)
+{
+    Cline ret = NULL, *q = &ret;
+    Cmlist ms;
+    Cmatcher mp;
+    int t;
+    char *p = str;
+
+    while (len) {
+	for (t = 0, ms = bmstack; ms && !t; ms = ms->next) {
+	    for (mp = ms->matcher; mp && !t; mp = mp->next) {
+		if (mp->flags == CMF_RIGHT && mp->wlen == -1 &&
+		    !mp->llen && len >= mp->ralen && mp->ralen &&
+		    pattern_match(mp->right, str, NULL, NULL)) {
+		    /* This is one of those patterns, so add a cline struct.
+		     * We store the anchor string in the line and the contents
+		     * (i.e. the strings between the anchors) in the word field. */
+		    *q = getcline(str, mp->ralen, p, str - p, 0);
+		    q = &((*q)->next);
+		    str += mp->ralen;
+		    len -= mp->ralen;
+		    p = str;
+		    t = 1;
+		}
+	    }
+	}
+	if (!t) {
+	    str++;
+	    len--;
+	}
+    }
+    /* This is the cline struct for the remaining string at the end. */
+    if (p != str) {
+	*q = getcline("", 0, p, str - p, 0);
+	q = &((*q)->next);
+    }
+    *q = NULL;
+
+    return ret;
+}
+
+/* This builds a string that may be put on the line that fully matches the
+ * given strings. The return value is NULL if no such string could be built
+ * or that string, allocated on the heap.
+ * All this is a lot like the procedure in bld_new_pfx(), only slightly
+ * simpler, see that function for more comments. */
+
+static char *
+join_strs(int la, char *sa, int lb, char *sb)
+{
+    static unsigned char *ea = NULL;
+    static int ealen = 0;
+    static char *line = NULL;
+    static int llen = 0;
+    static char *rs = NULL;
+    static int rl = 0;
+
+    Cmlist ms;
+    Cmatcher mp;
+    int t, bl, rr = rl;
+    char *rp = rs;
+
+    /* This is a buffer for the characters to use for pairs of correspondence
+     * classes. */
+    if (la + 1 > ealen || lb + 1 > ealen) {
+	if (ealen)
+	    zfree(ea, ealen);
+	ea = (unsigned char *) zalloc(ealen = (la > lb ? la : lb) + 20);
+    }
+    while (la && lb) {
+	if (*sa != *sb) {
+	    for (t = 0, ms = bmstack; ms && !t; ms = ms->next) {
+		for (mp = ms->matcher; mp && !t; mp = mp->next) {
+		    if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
+			mp->wlen <= la && mp->wlen <= lb) {
+			if (pattern_match(mp->word, sa, NULL, ea)) {
+			    if (mp->llen + 1 > llen) {
+				if (llen)
+				    zfree(line, llen);
+				line = (char *) zalloc(llen = mp->llen + 20);
+			    }
+			    if ((bl = bld_line_pfx(mp->line, line, line,
+						   lb, sb, ea))) {
+				line[mp->llen] = '\0';
+				if (rr <= mp->llen) {
+				    char *or = rs;
+
+				    rs = realloc(rs, (rl += 20));
+				    rr += 20;
+				    rp += rs - or;
+				}
+				memcpy(rp, line, mp->llen);
+				rp += mp->llen;
+				rr -= mp->llen;
+				sa += mp->wlen;
+				sb += bl;
+				la -= mp->wlen;
+				lb -= bl;
+				t = 1;
+			    }
+			} else if (pattern_match(mp->word, sb, NULL, ea)) {
+			    if (mp->llen + 1 > llen) {
+				if (llen)
+				    zfree(line, llen);
+				line = (char *) zalloc(llen = mp->llen + 20);
+			    }
+			    if ((bl = bld_line_pfx(mp->line, line, line,
+						   la, sa, ea))) {
+				line[mp->llen] = '\0';
+				if (rr <= mp->llen) {
+				    char *or = rs;
+
+				    rs = realloc(rs, (rl += 20));
+				    rr += 20;
+				    rp += rs - or;
+				}
+				memcpy(rp, line, mp->llen);
+				rp += mp->llen;
+				rr -= mp->llen;
+				sa += bl;
+				sb += mp->wlen;
+				la -= bl;
+				lb -= mp->wlen;
+				t = 1;
+			    }
+			}
+		    }
+		}
+	    }
+	    if (!t)
+		break;
+	} else {
+	    if (rr <= mp->llen) {
+		char *or = rs;
+
+		rs = realloc(rs, (rl += 20));
+		rr += 20;
+		rp += rs - or;
+	    }
+	    *rp++ = *sa;
+	    rr--;
+	    sa++;
+	    sb++;
+	    la--;
+	    lb--;
+	}
+    }
+    if (la || lb)
+	return NULL;
+
+    *rp = '\0';
+
+    return dupstring(rs);
+}
+
+/* This gets two cline lists with separated runs and joins the corresponding
+ * elements in these lists. In olp and nlp we return the lengths of the matched
+ * portions in o and n respectively. As always the list in o is the one that
+ * contains the joined result after this function. */
+
+static Cline
+join_ends(Cline o, Cline n, int *olp, int *nlp)
+{
+    Cline ret = o;
+    int mol, mnl, smol = 0, smnl = 0;
+    char *j;
+
+    while (o && n) {
+	if (o->llen == n->llen && !strncmp(o->line, n->line, o->llen)) {
+	    /* The anchors are the same, so join the contents. */
+	    bld_pfx(o, n, &mol, &mnl);
+	    smol += mol + o->llen;
+	    smnl += mnl + n->llen;
+	} else if (!(o->flags & CLF_JOIN) &&
+		   (j = join_strs(o->llen, o->line, n->llen, n->line))) {
+	    /* We could build a string matching both anchors, so use that
+	     * and mark the cline so that we don't try to join it again. */
+	    o->flags |= CLF_JOIN;
+	    o->llen = strlen(j);
+	    o->line = j;
+	    bld_pfx(o, n, &mol, &mnl);
+	    smol += mol + o->llen;
+	    smnl += mnl + n->llen;
+	} else {
+	    /* Different anchor strings, so shorten the list. */
+	    bld_pfx(o, n, &mol, &mnl);
+	    smol += mol;
+	    smnl += mnl;
+	    o->flags |= CLF_MISS;
+	    o->next = NULL;
+	    o->llen = 0;
+	    if (olp)
+		*olp = smol;
+	    if (nlp)
+		*nlp = smnl;
+	    return ret;
+	}
+	/* If we reached the end of the new list but not the end of the old
+	 * list, we mark the old list (saying that characters are missing here). */
+	if (!(n = n->next) && o->next)
+	    o->flags |= CLF_MISS;
+	o = o->next;
+    }
+    if (o) {
+	/* Shorten the list if we haven't reached the end. */
+	if (n)
+	    o->flags |= CLF_MISS;
+	o->next = NULL;
+	o->llen = 0;
+    }
+    if (olp)
+	*olp = smol;
+    if (nlp)
+	*nlp = smnl;
+    return ret;
+}
+
+/* This builds all the possible line patterns for the pattern pat in the
+ * buffer line. Initially line is the same as lp, but during recursive
+ * calls lp is incremented for storing successive characters. Whenever
+ * a full possible string is build, we test if this line matches the
+ * string given by wlen and word. The last argument contains the characters
+ * to use for the correspondence classes, it was filled by a call to 
+ * pattern_match() in the calling function.
+ * The return value is the length of the string matched in the word, it
+ * is zero if we couldn't build a line that matches the word. */
+
+/**/
+static int
+bld_line_pfx(Cpattern pat, char *line, char *lp,
+	     int wlen, char *word, unsigned char *in)
+{
+    if (pat) {
+	/* Still working on the pattern. */
+
+	int i, l;
+	unsigned char c = 0;
+
+	/* Get the number of the character for a correspondence class
+	 * if it has a correxponding class. */
+	if (pat->equiv)
+	    if ((c = *in))
+		in++;
+
+	/* Walk through the table in the pattern and try the characters
+	 * that may appear in the current position. */
+	for (i = 0; i < 256; i++)
+	    if ((pat->equiv && c) ? (c == pat->tab[i]) : pat->tab[i]) {
+		*lp = i;
+		/* We stored the character, now call ourselves to build
+		 * the rest. */
+		if ((l = bld_line_pfx(pat->next, line, lp + 1,
+				      wlen, word, in)))
+		    return l;
+	    }
+    } else {
+	/* We reached the end, i.e. the line string is fully build, now
+	 * see if it matches the given word. */
+	static unsigned char *ea = NULL;
+	static int ealen = 0;
+
+	Cmlist ms;
+	Cmatcher mp;
+	int l = lp - line, t, rl = 0;
+
+	/* Quick test if the strings are exactly the same. */
+	if (l == wlen && !strncmp(line, word, l))
+	    return l;
+
+	/* We need another buffer for correspondence classes. */
+	if (l + 1 > ealen) {
+	    if (ealen)
+		zfree(ea, ealen);
+	    ea = (unsigned char *) zalloc(ealen = l + 20);
+	}
+	/* We loop through the whole line string built. */
+	while (l && wlen) {
+	    if (*word == *line) {
+		/* The same character in both strings, skip over. */
+		line++;
+		word++;
+		l--;
+		wlen--;
+		rl++;
+	    } else {
+		t = 0;
+		for (ms = bmstack; ms && !t; ms = ms->next) {
+		    for (mp = ms->matcher; mp && !t; mp = mp->next) {
+			if (!mp->flags && mp->wlen <= wlen && mp->llen <= l &&
+			    pattern_match(mp->line, line, NULL, ea) &&
+			    pattern_match(mp->word, word, ea, NULL)) {
+			    /* Both the line and the word pattern matched,
+			     * now skip over the matched portions. */
+			    line += mp->llen;
+			    word += mp->wlen;
+			    l -= mp->llen;
+			    wlen -= mp->wlen;
+			    rl += mp->wlen;
+			    t = 1;
+			}
+		    }
+		}
+		if (!t)
+		    /* Didn't match, give up. */
+		    return 0;
+	    }
+	}
+	if (!l)
+	    /* Unmatched portion in the line built, return no-match. */
+	    return rl;
+    }
+    return 0;
+}
+
+/* This builds a list of cline structs describing a string to insert in
+ * the line in a place where we didn't had something on the line when
+ * completion was invoked. This is called after we get the second match
+ * so we have two strings to start with given by (ol,ow) and (nl,nw) and
+ * the cline list returned describes a string that matches both of these
+ * strings.
+ * In olp and nlp we return the number of characters in ow and nw that
+ * are not matched by the cline list returned. Especially this means that
+ * they are non-zero if there are such unmatched portions.
+ * In lp we return a pointer to the last cline struct created. */
+
+static Cline
+bld_new_pfx(int ol, char *ow, int nl, char *nw, int *olp, int *nlp, Cline *lp)
+{
+    static unsigned char *ea = NULL;
+    static int ealen = 0;
+    static char *line = NULL;
+    static int llen = 0;
+
+    Cmlist ms;
+    Cmatcher mp;
+    Cline ret = NULL, l = NULL, n;
+    char *p = ow;
+    int t, bl;
+
+    /* This is a buffer for the characters to use for pairs of correspondence
+     * classes. */
+    if (ol + 1 > ealen || nl + 1 > ealen) {
+	if (ealen)
+	    zfree(ea, ealen);
+	ea = (unsigned char *) zalloc(ealen = (ol > nl ? ol : nl) + 20);
+    }
+    /* Loop through the strings. */
+    while (ol && nl) {
+	if (*ow != *nw) {
+	    /* Not the same character, use the patterns. */
+	    for (t = 0, ms = bmstack; ms && !t; ms = ms->next) {
+		for (mp = ms->matcher; mp && !t; mp = mp->next) {
+		    /* We use only those patterns that match a non-empty
+		     * string in both the line and the word, that match
+		     * strings no longer than the string we still have
+		     * to compare, that don't have anchors, and that don't
+		     * use the line strings for insertion. */
+		    if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
+			mp->wlen <= ol && mp->wlen <= nl) {
+			/* Try the pattern only if the word-pattern matches
+			 * one of the strings. */
+			if (pattern_match(mp->word, ow, NULL, ea)) {
+			    /* Make the buffer where we build the possible
+			     * line patterns big enough. */
+			    if (mp->llen + 1 > llen) {
+				if (llen)
+				    zfree(line, llen);
+				line = (char *) zalloc(llen = mp->llen + 20);
+			    }
+			    /* Then build all the possible lines and see
+			     * if one of them matches the othe string. */
+			    if ((bl = bld_line_pfx(mp->line, line, line,
+						   nl, nw, ea))) {
+				/* Yep, one of the lines matched the other
+				 * string. */
+				if (p != ow) {
+				    /* There is still a substring that is
+				     * the same in both strings, so add
+				     * a cline for it. */
+				    char sav = *ow;
+
+				    *ow = '\0';
+				    n = getcline(NULL, 0, dupstring(p),
+						 ow - p, 0);
+				    *ow = sav;
+				    if (l)
+					l->next = n;
+				    else
+					ret = n;
+				    l = n;
+				}
+				/* Then we add the line string built. */
+				line[mp->llen] = '\0';
+				n = getcline(dupstring(line), mp->llen,
+					     NULL, 0, CLF_DIFF);
+				if (l)
+				    l->next = n;
+				else
+				    ret = n;
+				l = n;
+				ow += mp->wlen;
+				nw += bl;
+				ol -= mp->wlen;
+				nl -= bl;
+				p = ow;
+				t = 1;
+			    }
+			} else if (pattern_match(mp->word, nw, NULL, ea)) {
+			    /* Now do the same for the other string. */
+			    if (mp->llen + 1 > llen) {
+				if (llen)
+				    zfree(line, llen);
+				line = (char *) zalloc(llen = mp->llen + 20);
+			    }
+			    if ((bl = bld_line_pfx(mp->line, line, line,
+						   ol, ow, ea))) {
+				if (p != ow) {
+				    char sav = *ow;
+
+				    *ow = '\0';
+				    n = getcline(NULL, 0, dupstring(p),
+						 ow - p, 0);
+				    *ow = sav;
+				    if (l)
+					l->next = n;
+				    else
+					ret = n;
+				    l = n;
+				}
+				line[mp->llen] = '\0';
+				n = getcline(dupstring(line), mp->llen,
+					     NULL, 0, CLF_DIFF);
+				if (l)
+				    l->next = n;
+				else
+				    ret = n;
+				l = n;
+				ow += bl;
+				nw += mp->wlen;
+				ol -= bl;
+				nl -= mp->wlen;
+				p = ow;
+				t = 1;
+			    }
+			}
+		    }
+		}
+	    }
+	    if (!t)
+		/* No pattern matched, so give up. */
+		break;
+	} else {
+	    /* Same character, skip over. */
+	    ow++;
+	    nw++;
+	    ol--;
+	    nl--;
+	}
+    }
+    if (p != ow) {
+	/* There is a equal substring in both strings, build a cline
+	 * for it. */
+	char sav = *ow;
+
+	*ow = '\0';
+	n = getcline(NULL, 0, dupstring(p), ow - p, 0);
+	*ow = sav;
+	if (l)
+	    l->next = n;
+	else
+	    ret = n;
+	l = n;
+    }
+    if (l)
+	l->next = NULL;
+    else
+	ret = NULL;
+
+    if (olp)
+	*olp = ol;
+    if (nlp)
+	*nlp = nl;
+    if (lp)
+	*lp = l;
+
+    return ret;
+}
+
+/* Given a cline list for an unmatched part of the string to insert in the
+ * line and a new match substring, modify the cline list so that it also
+ * matches this string. The cline list is shortened in the place where
+ * we can't build a cline matching the new string.
+ * However, the resulting cline list is returned. The string is described
+ * by len and word. In missp we return non-zero if the cline list returned
+ * had to be shortened (and hence doesn't fully match the strings it was
+ * built from anymore) or if it doesn't fully match the given string.
+ * This function checks the string left to right and thus is to be used
+ * for strings where we want a common prefix. */
+
+static Cline
+join_new_pfx(Cline line, int len, char *word, int *missp)
+{
+    static unsigned char *ea = NULL;
+    static int ealen = 0;
+
+    Cline ret = NULL, l = NULL, next;
+    int miss = 0;
+
+    /* Walk through the list and the string. */
+    while (line && len) {
+	next = line->next;
+	/* The line element is used in those places where a new line
+	 * string was built. */
+	if (line->line) {
+	    Cmlist ms;
+	    Cmatcher mp;
+	    int ll = line->llen, t;
+	    char *p = line->line;
+
+	    /* Make the buffer for the correspondence classes big enough. */
+	    if (line->llen + 1 > ealen) {
+		if (ealen)
+		    zfree(ea, ealen);
+		ea = (unsigned char *) zalloc(ealen = line->llen + 20);
+	    }
+	    /* Check if the line string from the cline list matches the
+	     * new string. */
+	    while (ll && len) {
+		if (*p == *word) {
+		    p++;
+		    word++;
+		    ll--;
+		    len--;
+		} else {
+		    for (t = 0, ms = bmstack; ms && !t; ms = ms->next) {
+			for (mp = ms->matcher; mp && !t; mp = mp->next) {
+			    if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
+				mp->wlen <= len && mp->llen <= len &&
+				pattern_match(mp->word, word, NULL, ea) &&
+				pattern_match(mp->line, p, ea, NULL)) {
+				/* We have a matched substring, skip over. */
+				p += mp->llen;
+				word += mp->wlen;
+				ll -= mp->llen;
+				len -= mp->wlen;
+				t = 1;
+			    }
+			}
+		    }
+		    if (!t)
+			/* No match, give up. */
+			break;
+		}
+	    }
+	    if (p == line->line) {
+		/* We couldn't match any character from the string in the
+		 * cline, so shorten the list and don't even keep this
+		 * struct. */
+		miss = 1;
+		len = 0;
+	    } else {
+		/* At least the beginning of the cline string can be used. */
+		if (ll) {
+		    /* But there is a portion of the string that can't be
+		     * used, so we have to shorten the list. */
+		    miss = 1;
+		    *p = '\0';
+		    line->llen -= ll;
+		    len = 0;
+		}
+		line->next = NULL;
+		if (l)
+		    l->next = line;
+		else
+		    ret = line;
+	    }
+	} else {
+	    /* The cline doesn't have a string built by reverse matching,
+	     * so we have to work on the original substring in the cline
+	     * and the new string. */
+	    if (line->wlen == len && strncmp(line->word, word, len)) {
+		/* They are equal, accept and return. If there was
+		 * another element in the list, shorten the list. */
+		if (next)
+		    miss = 1;
+		line->next = NULL;
+		if (l)
+		    l->next = line;
+		else
+		    ret = line;
+		len = 0;
+	    } else {
+		char sav = word[len];
+
+		/* Check if one is the prefix of the other one. */
+		word[len] = '\0';
+		if (strpfx(word, line->word)) {
+		    word[len] = sav;
+
+		    line->word[len] = '\0';
+		    line->wlen = len;
+		    miss = 1;
+		    line->next = NULL;
+		    if (l)
+			l->next = line;
+		    else
+			ret = line;
+		    len = 0;
+		} else if (strpfx(line->word, word)) {
+		    word[len] = sav;
+
+		    miss = 1;
+		    line->next = NULL;
+		    if (l)
+			l->next = line;
+		    else
+			ret = line;
+		    len = 0;
+		} else {
+		    /* Not the same and no prefix, so we try to build a
+		     * new line string matching the string in the cline
+		     * and the new string. */
+		    int mol, mnl;
+		    Cline sl, send;
+
+		    word[len] = sav;
+
+		    if ((sl = bld_new_pfx(line->wlen, line->word,
+					  len, word, &mol, &mnl, &send))) {
+			/* We could build such a string, use it in the
+			 * cline structure. */
+			if (l)
+			    l->next = sl;
+			else
+			    ret = sl;
+			if (!mol) {
+			    send->next = next;
+			    word += len - mnl;
+			    len = mnl;
+			} else
+			    len = 0;
+			l = send;
+		    } else
+			len = 0;
+		}
+	    }
+	}
+	line = next;
+    }
+    *missp = (line || len || miss);
+
+    return ret;
+}
+
+/* This function gets two cline structs for which we want to build a
+ * common prefix to put on the line. The result is placed in the cline
+ * struct given as first argument.
+ * In olp and nlp we return the matched lengths for o and n, respectively
+ * (but this is only guaranteed to give correct results if this is the
+ * first call for the given o and n). */
+
+/**/
+static void
+bld_pfx(Cline o, Cline n, int *olp, int *nlp)
+{
+    if (o->flags & CLF_NEW) {
+	if (o->flags & (CLF_END | CLF_MID))
+	    /* We split the suffix in the middle and at the end into
+	     * separate runs. */
+	    o->prefix = join_ends(o->prefix, end_list(n->wlen, n->word),
+				  NULL, NULL);
+	else {
+	    /* This flag is set if we already built a cline list for such
+	     * a prefix. We join it with the string from the other cline
+	     * struct. */
+	    int miss;
+
+	    o->prefix = join_new_pfx(o->prefix, n->wlen, n->word, &miss);
+	    if (miss)
+		o->flags |= CLF_MISS;
+	}
+    } else if (o->flags & (CLF_END | CLF_MID)) {
+	o->flags |= CLF_NEW;
+	o->prefix = join_ends(end_list(o->wlen, o->word),
+			      end_list(n->wlen, n->word), olp, nlp);
+    } else if (o->wlen && n->wlen) {
+	/* We haven't built a cline list for it yet. */
+	char so = o->word[o->wlen], sn = n->word[n->wlen];
+	char *new = o->word;
+	int newl = o->wlen, mol, mnl;
+
+	/* If one of the strings is a prefix of the other one, just keep
+	 * that prefix. */
+	o->word[o->wlen] = n->word[n->wlen] = '\0';
+	if (strpfx(n->word, o->word)) {
+	    new = dupstring(n->word);
+	    newl = n->wlen;
+	    if (olp)
+		*olp = *nlp = n->wlen;
+	} else if (strpfx(o->word, n->word)) {
+	    if (olp)
+		*olp = *nlp = o->wlen;
+	} else {
+	    /* Otherwise build a cline list describing a string that
+	     * matches both strings from the original cline structs
+	     * and thus can be put in the command line to represent
+	     * them. This cline list is stored in o. */
+	    o->flags |= CLF_NEW;
+	    o->prefix = bld_new_pfx(o->wlen, o->word, n->wlen, n->word,
+				    &mol, &mnl, NULL);
+	    newl = 0;
+	    new = "";
+	    if (mol || mnl)
+		o->flags |= CLF_MISS;
+	    if (olp) {
+		*olp = o->wlen - mol;
+		*nlp = n->wlen - mnl;
+	    }
+	}
+	o->word[o->wlen] = so;
+	n->word[n->wlen] = sn;
+
+	o->word = new;
+	o->wlen = newl;
+
+	if (!o->prefix && n->wlen != o->wlen)
+	    o->flags |= CLF_MISS;
+    }
+}
+
+/* The following function are like their counterparts above, only for
+ * the other direction. */
+
+static int
+bld_line_sfx(Cpattern pat, char *line, char *lp,
+	     int wlen, char *word, unsigned char *in)
+{
+    if (pat) {
+	int i, l;
+	unsigned char c = 0;
+
+	if (pat->equiv)
+	    if ((c = *in))
+		in++;
+
+	for (i = 0; i < 256; i++)
+	    if ((pat->equiv && c) ? (c == pat->tab[i]) : pat->tab[i]) {
+		*lp = i;
+		if ((l = bld_line_pfx(pat->next, line, lp + 1,
+				      wlen, word, in)))
+		    return l;
+	    }
+    } else {
+	static unsigned char *ea = NULL;
+	static int ealen = 0;
+
+	Cmlist ms;
+	Cmatcher mp;
+	int l = lp - line, t, rl = 0;
+
+	if (l == wlen && !strncmp(line, word, l))
+	    return l;
+
+	line = lp;
+	word += wlen;
+	
+	if (l + 1 > ealen) {
+	    if (ealen)
+		zfree(ea, ealen);
+	    ea = (unsigned char *) zalloc(ealen = l + 20);
+	}
+	while (l && wlen) {
+	    if (word[-1] == line[-1]) {
+		line--;
+		word--;
+		l--;
+		wlen--;
+		rl++;
+	    } else {
+		t = 0;
+		for (ms = bmstack; ms && !t; ms = ms->next) {
+		    for (mp = ms->matcher; mp && !t; mp = mp->next) {
+			if (!mp->flags && mp->wlen <= wlen && mp->llen <= l &&
+			    pattern_match(mp->line, line - mp->llen,
+					  NULL, ea) &&
+			    pattern_match(mp->word, word - mp->wlen,
+					  ea, NULL)) {
+			    line -= mp->llen;
+			    word -= mp->wlen;
+			    l -= mp->llen;
+			    wlen -= mp->wlen;
+			    rl += mp->wlen;
+			    t = 1;
+			}
+		    }
+		}
+		if (!t)
+		    return 0;
+	    }
+	}
+	if (!l)
+	    return rl;
+    }
+    return 0;
+}
+
+static Cline
+bld_new_sfx(int ol, char *ow, int nl, char *nw, int *olp, int *nlp, Cline *lp)
+{
+    static unsigned char *ea = NULL;
+    static int ealen = 0;
+    static char *line = NULL;
+    static int llen = 0;
+
+    Cmlist ms;
+    Cmatcher mp;
+    Cline ret = NULL, l = NULL, n;
+    char *p;
+    int t, bl;
+
+    ow += ol;
+    nw += nl;
+    p = ow;
+
+    if (ol + 1 > ealen || nl + 1 > ealen) {
+	if (ealen)
+	    zfree(ea, ealen);
+	ea = (unsigned char *) zalloc((ealen = (ol > nl ? ol : nl) + 20));
+    }
+    while (ol && nl) {
+	if (ow[-1] != nw[-1]) {
+	    for (t = 0, ms = bmstack; ms && !t; ms = ms->next) {
+		for (mp = ms->matcher; mp && !t; mp = mp->next) {
+		    if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
+			mp->wlen <= ol && mp->wlen <= nl) {
+			if (pattern_match(mp->word, ow - mp->wlen,
+					  NULL, ea)) {
+			    if (mp->llen + 1 > llen) {
+				if (llen)
+				    zfree(line, llen);
+				line = (char *) zalloc(llen = mp->llen + 20);
+			    }
+			    if ((bl = bld_line_sfx(mp->line, line, line,
+						   nl, nw, ea))) {
+				if (p != ow) {
+				    char sav = *p;
+
+				    *p = '\0';
+				    n = getcline(NULL, 0, dupstring(ow),
+						 p - ow, 0);
+				    *p = sav;
+				    if (l)
+					l->next = n;
+				    else
+					ret = n;
+				    l = n;
+				}
+				line[mp->llen] = '\0';
+				n = getcline(dupstring(line), mp->llen,
+					     NULL, 0, CLF_DIFF);
+				if (l)
+				    l->next = n;
+				else
+				    ret = n;
+				l = n;
+				ow -= mp->wlen;
+				nw -= bl;
+				ol -= mp->wlen;
+				nl -= bl;
+				p = ow;
+				t = 1;
+			    }
+			} else if (pattern_match(mp->word, nw - mp->wlen,
+						 NULL, ea)) {
+			    if (mp->llen + 1 > llen) {
+				if (llen)
+				    zfree(line, llen);
+				line = (char *) zalloc(llen = mp->llen + 20);
+			    }
+			    if ((bl = bld_line_sfx(mp->line, line, line,
+						   ol, ow, ea))) {
+				if (p != ow) {
+				    char sav = *p;
+
+				    *p = '\0';
+				    n = getcline(NULL, 0, dupstring(ow),
+						 p - ow, 0);
+				    *p = sav;
+				    if (l)
+					l->next = n;
+				    else
+					ret = n;
+				    l = n;
+				}
+				line[mp->llen] = '\0';
+				n = getcline(dupstring(line), mp->llen,
+					     NULL, 0, CLF_DIFF);
+				if (l)
+				    l->next = n;
+				else
+				    ret = n;
+				l = n;
+				ow -= bl;
+				nw -= mp->wlen;
+				ol -= bl;
+				nl -= mp->wlen;
+				p = ow;
+				t = 1;
+			    }
+			}
+		    }
+		}
+	    }
+	    if (!t)
+		break;
+	} else {
+	    ow--;
+	    nw--;
+	    ol--;
+	    nl--;
+	}
+    }
+    if (p != ow) {
+	char sav = *p;
+
+	*p = '\0';
+	n = getcline(NULL, 0, dupstring(ow), p - ow, 0);
+	*p = sav;
+	if (l)
+	    l->next = n;
+	else
+	    ret = n;
+	l = n;
+    }
+    if (l)
+	l->next = NULL;
+    else
+	ret = NULL;
+
+    if (olp)
+	*olp = ol;
+    if (nlp)
+	*nlp = nl;
+    if (lp)
+	*lp = l;
+
+    return ret;
+}
+
+static Cline
+join_new_sfx(Cline line, int len, char *word, int *missp)
+{
+    static unsigned char *ea = NULL;
+    static int ealen = 0;
+
+    Cline ret = NULL, l = NULL, next;
+    int miss = 0, ind = 0;
+
+    word += len;
+
+    while (line && len) {
+	next = line->next;
+	if (line->line) {
+	    Cmlist ms;
+	    Cmatcher mp;
+	    int ll = line->llen, t;
+	    char *p = line->line + ll;
+
+	    if (line->llen + 1 > ealen) {
+		if (ealen)
+		    zfree(ea, ealen);
+		ea = (unsigned char *) zalloc(ealen = line->llen + 20);
+	    }
+	    while (ll && len) {
+		if (p[-1] == word[-1]) {
+		    p--;
+		    word--;
+		    ll--;
+		    len--;
+		    ind++;
+		} else {
+		    for (t = 0, ms = bmstack; ms && !t; ms = ms->next) {
+			for (mp = ms->matcher; mp && !t; mp = mp->next) {
+			    if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
+				mp->wlen <= len && mp->llen <= len &&
+				pattern_match(mp->word, word - mp->wlen,
+					      NULL, ea) &&
+				pattern_match(mp->line, p - mp->llen,
+					      ea, NULL)) {
+				p -= mp->llen;
+				word -= mp->wlen;
+				ll -= mp->llen;
+				len -= mp->wlen;
+				ind += mp->wlen;
+				t = 1;
+			    }
+			}
+		    }
+		    if (!t)
+			break;
+		}
+	    }
+	    if (p == line->line + line->llen) {
+		miss = 1;
+		len = 0;
+	    } else {
+		if (ll) {
+		    miss = 1;
+		    line->line = p;
+		    line->llen -= ll;
+		    len = 0;
+		}
+		line->next = NULL;
+		if (l)
+		    l->next = line;
+		else
+		    ret = line;
+	    }
+	} else {
+	    if (line->wlen == len && strncmp(line->word, word - len, len)) {
+		if (next)
+		    miss = 1;
+		line->next = NULL;
+		if (l)
+		    l->next = line;
+		else
+		    ret = line;
+		len = 0;
+	    } else {
+		char sav = word[ind];
+
+		word[ind] = '\0';
+		if (strpfx(word - len, line->word)) {
+		    word[ind] = sav;
+
+		    line->word += line->wlen - len;
+		    line->wlen = ind;
+		    miss = 1;
+		    line->next = NULL;
+		    if (l)
+			l->next = line;
+		    else
+			ret = line;
+		    len = 0;
+		} else if (strpfx(line->word, word - len)) {
+		    word[ind] = sav;
+
+		    miss = 1;
+		    line->next = NULL;
+		    if (l)
+			l->next = line;
+		    else
+			ret = line;
+		    len = 0;
+		} else {
+		    int mol, mnl;
+		    Cline sl, send;
+
+		    word[len] = sav;
+
+		    if ((sl = bld_new_sfx(line->wlen, line->word,
+					  len, word - len,
+					  &mol, &mnl, &send))) {
+			if (l)
+			    l->next = sl;
+			else
+			    ret = sl;
+			if (!mol) {
+			    send->next = next;
+			    word -= len - mnl;
+			    len = mnl;
+			} else
+			    len = 0;
+			l = send;
+		    } else
+			len = 0;
+		}
+	    }
+	}
+	line = next;
+    }
+    *missp = (line || len || miss);
+
+    return ret;
+}
+
+/**/
+static void
+bld_sfx(Cline o, Cline n)
+{
+    if (o->flags & CLF_NEW) {
+	int miss;
+
+	o->suffix = join_new_sfx(o->suffix, n->wlen, n->word, &miss);
+	if (miss)
+	    o->flags |= CLF_MISS;
+    } else {
+	char so = o->word[o->wlen], sn = n->word[n->wlen];
+	char *new = o->word;
+	int newl = o->wlen, mol, mnl;
+
+	o->word[o->wlen] = n->word[n->wlen] = '\0';
+	if (strpfx(n->word, o->word)) {
+	    new = dupstring(n->word);
+	    newl = n->wlen;
+	} else if (!strpfx(o->word, n->word)) {
+	    o->flags |= CLF_NEW;
+	    o->suffix = bld_new_sfx(o->wlen, o->word, n->wlen, n->word,
+				    &mol, &mnl, NULL);
+	    newl = 0;
+	    new = "";
+	    if (mol || mnl)
+		o->flags |= CLF_MISS;
+	}
+	o->word[o->wlen] = so;
+	n->word[n->wlen] = sn;
+
+	o->word = new;
+	o->wlen = newl;
+
+	if (!o->suffix && n->wlen != o->wlen)
+	    o->flags |= CLF_MISS;
+    }
+}
+
 /* Joins two Cline lists, building the most specific line string *
- * that is possible. */
+ * that is possible and returns it. This is done by modifying the
+ * cline list given as the first argument. */
 
 static Cline
 join_clines(Cline o, Cline n)
@@ -1556,12 +2708,21 @@ join_clines(Cline o, Cline n)
     Cline oo = o;
 
     if (!o)
+	/* This is the first time this was called, so just return the
+	 * the second list. In future calls we will get this list as
+	 * the first argument. */
 	return n;
     else {
 	Cline f = freecl, q, op = NULL;
 	int ol, nl;
 
+	freecl = NULL;
+
 	while (o && n) {
+	    /* CLF_MID is set in the cline struct where the prefix and the
+	     * suffix from the line meet. If we have reached the cline
+	     * for it in one of the lists, search the corresponding 
+	     * cline in the other list, removing all structs up to it. */
 	    if (o->flags & CLF_MID) {
 		while (n && !(n->flags & CLF_MID)) {
 		    q = n->next;
@@ -1606,64 +2767,52 @@ join_clines(Cline o, Cline n)
 	    if (!o || !n)
 		break;
 	    if (o->flags & CLF_MID) {
-		char so, sn, *os, *ns;
-		int ol = o->llen, l, lo, ln;
-
-		so = o->line[o->llen];
-		sn = n->line[n->llen];
-		o->line[o->llen] = '\0';
-		n->line[n->llen] = '\0';
-		l = pfxlen(o->line, n->line);
-		if (l != o->llen)
-		    o->flags |= CLF_MISS;
-		o->line[o->llen] = so;
-		n->line[n->llen] = sn;
-		o->llen = l;
-		if ((lo = o->wlen) < 0) {
-		    os = o->line + l;
-		    lo = ol - l;
-		} else
-		    os = o->word;
-		ns = n->line + l;
-		ln = n->llen - l;
-		so = os[lo];
-		sn = ns[ln];
-		os[lo] = '\0';
-		ns[ln] = '\0';
-		l = sfxlen(os, ns);
-		os[lo] = so;
-		ns[ln] = sn;
-		o->word = os + lo - l;
-		o->wlen = l;
+		/* These are the structs in the middle, so simplify the
+		 * prefix and the suffix in it to their longest common
+		 * versions. */
+		
+		char *os, *ns, *oss = o->line, *nss = n->line;
+		int ol, nl, mol, mnl, oll = o->llen, nll = n->llen;
+
+		os = o->word;
+		ol = o->wlen;
+		ns = n->word;
+		nl = n->wlen;
+
+		o->word = o->line;
+		o->wlen = o->llen;
+		n->word = n->line;
+		n->wlen = n->llen;
+		bld_pfx(o, n, &mol, &mnl);
+		o->line = o->word;
+		o->llen = o->wlen;
+
+		o->word = os;
+		o->wlen = ol;
+		n->word = ns;
+		n->wlen = nl;
+
+		if (o->wlen < 0) {
+		    o->word = oss + mol;
+		    o->wlen = oll - mol;
+		}
+		if (n->wlen < 0) {
+		    n->word = nss + mnl;
+		    n->wlen = nll - mnl;
+		}
+		bld_sfx(o, n);
 	    } else if (o->word) {
 		if (n->word) {
-		    if (o->matcher == n->matcher &&
-			(o->flags & CLF_SUF) == (n->flags & CLF_SUF) &&
-			((o->flags & CLF_END) || o->matcher->wlen < 0)) {
-			char so = o->word[o->wlen];
-			char sn = n->word[n->wlen];
-			int l;
-
-			o->word[o->wlen] = '\0';
-			n->word[n->wlen] = '\0';
-
-			/* We have two chunks from `*' patterns.    *
+		    if (o->llen == n->llen &&
+			(o->flags & CLF_VAR) && (n->flags & CLF_VAR) &&
+			(o->flags & (CLF_END | CLF_SUF)) ==
+			(n->flags & (CLF_END | CLF_SUF))) {
+			/* We have two chunks from `*' patterns,
 			 * reduce them to the common prefix/suffix. */
-			if (o->flags & CLF_SUF) {
-			    l = sfxlen(o->word, n->word);
-			    o->word[o->wlen] = so;
-			    n->word[n->wlen] = sn;
-			    o->word += o->wlen - l;
-			} else {
-			    l = pfxlen(o->word, n->word);
-			    o->word[o->wlen] = so;
-			    n->word[n->wlen] = sn;
-			    o->word = dupstring(o->word);
-			    o->word[l] = '\0';
-			}
-			o->wlen = l;
-			if (l < o->wlen || l < n->wlen)
-			    o->flags |= CLF_MISS;
+			if (o->flags & CLF_SUF)
+			    bld_sfx(o, n);
+			else
+			    bld_pfx(o, n, NULL, NULL);
 		    } else if (o->wlen == n->wlen) {
 			/* Otherwise keep them if they are equal. */
 			if (strncmp(o->word, n->word, o->wlen)) {
@@ -1694,6 +2843,8 @@ join_clines(Cline o, Cline n)
 	    o = o->next;
 	}
 	if (o) {
+	    /* The old list has elements not matched by the second list
+	     * we put all these elements in the free list. */
 	    for (q = o; q->next; q = q->next);
 
 	    q->next = f;
@@ -1717,7 +2868,8 @@ join_clines(Cline o, Cline n)
     return oo;
 }
 
-/* This returns a Cline for the given string. */
+/* This returns a Cline for the given string. In lp we return a pointer to
+ * the last cline struct created. */
 
 static Cline
 str_cline(char *s, int l, Cline *lp)
@@ -1726,8 +2878,10 @@ str_cline(char *s, int l, Cline *lp)
 
     if (l < 0)
 	l = strlen(s);
+    /* We build one struct per character, this makes joining it faster
+     * and easier. */
     while (l) {
-	*p = n = getcline(s, 1, NULL, 0, NULL, 0);
+	*p = n = getcline(s, 1, NULL, 0, 0);
 
 	p = &(n->next);
 	s++;
@@ -1739,7 +2893,8 @@ str_cline(char *s, int l, Cline *lp)
     return r;
 }
 
-/* This reverts the order of the chunks. */
+/* This reverts the order of the elements of the given cline list and
+ * returns a pointer to the new head. */
 
 static Cline
 revert_clines(Cline p)
@@ -1755,7 +2910,7 @@ revert_clines(Cline p)
     return r;
 }
 
-/* Prepend a string to a Cline. */
+/* Prepend a string to a Cline and return a pointer to the new cline list. */
 
 static Cline
 prepend_cline(char *s, Cline l)
@@ -1764,7 +2919,7 @@ prepend_cline(char *s, Cline l)
 
     while (l) {
 	*p = n = getcline(l->line, l->llen, l->word, l->wlen,
-			  l->matcher, l->flags);
+			  l->flags);
 
 	p = &(n->next);
 	l = l->next;
@@ -1772,8 +2927,8 @@ prepend_cline(char *s, Cline l)
     return r;
 }
 
-/* This simplifies the Cline to match the given strings. The strings are: *
- * the common prefix and its length, a string with the suffix at the end  *
+/* This simplifies the Cline to match the given strings. The strings are:
+ * the common prefix and its length, a string with the suffix at the end
  * and the suffix length. */
 
 static void
@@ -1784,6 +2939,8 @@ merge_cline(Cline lc, char *p, int pl, char *s, int sl, int psl)
 
     pll = strlen(p);
     if (s) {
+	/* If there is a suffix, get a pointer to the beginning of the
+	 * common suffix. */
 	int i = strlen(s);
 
 	if (ainfo->suflen < 10000)
@@ -1800,44 +2957,36 @@ merge_cline(Cline lc, char *p, int pl, char *s, int sl, int psl)
 
     pll -= pl;
 
+    /* Now build a cline list from the string(s) and join it with the
+     * cline list we have built while testing possible matches. */
     l = str_cline(p, pl, &n);
     q = &(n->next);
     if (!sl)
-	*q = getcline(NULL, 0, p + pl, pll, NULL, CLF_END);
+	*q = getcline(NULL, 0, p + pl, pll, CLF_END | CLF_VAR);
     else {
-	*q = n = getcline(p + pl, pll, s, sll, NULL, CLF_MID);
+	*q = n = getcline(p + pl, pll, s, sll, CLF_MID);
 
 	n->next = str_cline(s + sll, sl, NULL);
     }
     join_clines(lc, l);
 }
 
-/* This inserts the Cline built into the command line. */
+/* This inserts the Cline built into the command line. The last two
+ * arguments are the relative positions where the begining and the
+ * end of the brace expansion strings have to be re-inserted. */
 
 static void
 inst_cline(Cline l, int pl, int sl)
 {
     int m = -1, d = -1, b = -1, hb = 0, i = 0;
-    Cline p = l;
-
-    if (brend && *brend) {
-	while (p) {
-	    if (l->flags & CLF_MID) {
-		i += l->llen;
-		if (l->wlen >= 0)
-		    i += l->wlen;
-	    } else
-		i += l->llen;
 
-	    p = p->next;
-	}
-	sl = i - (brsl + sl) - 1;
-    }
-    else
-	sl = -1;
+    /* Adjust the position of the braces. */
     pl += brpl;
+    sl += brbsl;
 
     i = cs - wb;
+
+    /* Insert the brace strings now? */
     if (pl >= 0 && i >= pl && brbeg && *brbeg) {
 	inststrlen(brbeg, 1, -1);
 	pl = -1;
@@ -1848,37 +2997,140 @@ inst_cline(Cline l, int pl, int sl)
 	sl = -1;
 	hb = 1;
     }
+    /* Walk the list. */
     while (l) {
+	/* If this cline describes a suffix where something is missing
+	 * for some matches, remember the position. */
 	if (m < 0 && (l->flags & (CLF_MISS | CLF_SUF)) == (CLF_MISS | CLF_SUF))
 	    m = cs;
-	if (l->flags & CLF_MID) {
-	    inststrlen(l->line, 1, l->llen);
-	    if (b < 0)
+
+	if (l->prefix) {
+	    Cline sl = l->prefix, ssl;
+	    int hm;
+
+	    if (l->flags & (CLF_END | CLF_MID)) {
+		/* At the end and in the middle the suffix we have
+		 * separate runs. */
+		for (; sl; sl = sl->next) {
+		    hm = 0;
+		    if (sl->prefix) {
+			for (ssl = sl->prefix; ssl; ssl = ssl->next) {
+			    if (ssl->line)
+				/* line for a string derived from applying
+				 * the matchers the other way round. */
+				inststrlen(ssl->line, 1, ssl->llen);
+			    else
+				/* and word for substrings equal in all
+				 * matches. */
+				inststrlen(ssl->word, 1, ssl->wlen);
+			    /* If the string differs from any of the 
+			     * matches, remember the position. */
+			    if (d < 0 && (ssl->flags & CLF_DIFF))
+				d = cs;
+			}
+		    } else if (sl->wlen)
+			inststrlen(sl->word, 1, sl->wlen);
+
+		    if (m < 0 && (sl->flags & CLF_MISS))
+			m = cs;
+		    if (sl->llen)
+			inststrlen(sl->line, 1, sl->llen);
+		    hm = (sl->flags & CLF_MISS);
+		}
+		if ((l->flags & CLF_MID) &&hm && b < 0) {
+		    b = cs;
+		    hb = 1;
+		}
+	    } else {
+		/* The cline contains a newly build part of the string 
+		 * in a sub-list. */
+		for (; sl; sl = sl->next) {
+		    if (sl->line)
+			/* line for a string derived from applying the 
+			 * matchers the other way round. */
+			inststrlen(sl->line, 1, sl->llen);
+		    else
+			/* and word for substrings equal in all matches. */
+			inststrlen(sl->word, 1, sl->wlen);
+		    /* If the string differs from any of the matches,
+		     * remember the position. */
+		    if (d < 0 && (sl->flags & CLF_DIFF))
+			d = cs;
+		}
+	    }
+	    if (!(l->flags & (CLF_END | CLF_MID)))
+		i += l->llen;
+	}
+	if (l->suffix) {
+	    Cline sl = revert_clines(l->suffix);
+
+	    if ((sl->flags & CLF_MISS) && b < 0) {
 		b = cs;
-	    if (l->wlen > 0)
+		hb = 1;
+	    }
+	    for (; sl; sl = sl->next) {
+		if (sl->line)
+		    inststrlen(sl->line, 1, sl->llen);
+		else
+		    inststrlen(sl->word, 1, sl->wlen);
+		if (d < 0 && (sl->flags & CLF_DIFF))
+		    d = cs;
+	    }
+	}
+	if (l->flags & CLF_MID) {
+	    /* The cline in the middle, insert the prefix and the 
+	     * suffix. */
+	    if (!l->prefix && l->llen) {
+		inststrlen(l->line, 1, l->llen);
+		if (b < 0) {
+		    b = cs;
+		    hb = l->flags & CLF_MISS;
+		}
+	    }
+	    if (!l->suffix && l->wlen > 0)
 		inststrlen(l->word, 1, l->wlen);
-	} else if (l->word) {
-	    inststrlen(l->word, 1, l->wlen);
-	} else {
-	    inststrlen(l->line, 1, l->llen);
+	} else if (!l->prefix && !l->suffix) {
+	    if (l->word &&
+		!((pl >= 0 && brbeg && *brbeg &&
+		   i < pl && i + l->llen >= pl) ||
+		  (sl >= 0 && brend && *brend &&
+		   i < sl && i + l->llen >= sl))) {
+		/* We insert the prefered string stored in word only if we
+		 * don't have to put the brace beginning or end here. */
+		inststrlen(l->word, 1, l->wlen);
+	    } else {
+		/* Otherwise just re-insert the original string. */
+		inststrlen(l->line, 1, l->llen);
+	    }
+	    i += l->llen;
 	}
+	/* Remember the position if there is a difference or a missing
+	 * substring. */
 	if (d < 0 && (l->flags & CLF_DIFF))
 	    d = cs;
 	if (m < 0 && (l->flags & CLF_MISS))
 	    m = cs;
-	i += l->llen;
+
+	/* Probably insert the brace beginning or end. */
 	if (pl >= 0 && i >= pl && brbeg && *brbeg) {
+	    cs -= i - pl;
 	    inststrlen(brbeg, 1, -1);
+	    cs += i - pl;
 	    pl = -1;
 	    hb = 1;
 	}
-	if (sl >= 0 && i >= sl && brend && *brend) {
+	if (sl >= 0 && i > sl && brend && *brend) {
+	    cs -= i - sl;
 	    inststrlen(brend, 1, -1);
+	    cs += i - sl;
 	    sl = -1;
 	    hb = 1;
 	}
 	l = l->next;
     }
+    /* Now place the cursor. Preferably in a position where something
+     * is missing, otherwise in a place where the string differs from
+     * any of the matches, or just leave it at the end. */
     cs = (b >= 0 && hb ? b : (m >= 0 ? m : (d >= 0 ? d : cs)));
     if (cs > ll)
 	cs = ll;
@@ -1890,6 +3142,7 @@ inst_cline(Cline l, int pl, int sl)
  * given in `in' so that we can easily test if we found the         *
  * corresponding character. */
 
+/**/
 static int
 pattern_match(Cpattern p, char *s, unsigned char *in, unsigned char *out)
 {
@@ -1898,22 +3151,26 @@ pattern_match(Cpattern p, char *s, unsigned char *in, unsigned char *out)
     while (p) {
 	c = *((unsigned char *) s);
 
-	if (out) *out = 0;
+	if (out)
+	    *out = 0;
 
 	if (p->equiv) {
 	    if (in) {
 		c = p->tab[c];
-		if ((*in && *in != c) || (!*in && !c)) return 0;
+		if ((*in && *in != c) || (!*in && !c))
+		    return 0;
 	    } else if (out) {
-		if (!(*out = p->tab[c])) return 0;
-	    } else {
-		if (!p->tab[c]) return 0;
-	    }
-	    if (in) in++;
-	    if (out) out++;
-	} else {
-	    if (!p->tab[c]) return 0;
-	}
+		if (!(*out = p->tab[c]))
+		    return 0;
+	    } else if (!p->tab[c])
+		return 0;
+
+	    if (in && *in)
+		in++;
+	    if (out)
+		out++;
+	} else if (!p->tab[c])
+	    return 0;
 
 	s++;
 	p = p->next;
@@ -1921,18 +3178,23 @@ pattern_match(Cpattern p, char *s, unsigned char *in, unsigned char *out)
     return 1;
 }
 
-/* Do the matching for a prefix. */
+/* Do the matching for a prefix. l and w contain the strings on the line and
+ * for the generated word, respectively. In nlp we return a cline list for this
+ * match. In lp we return the length of the matched string. rlp is used to
+ * return a pointer to the last cline struct in the list returned in nlp, and
+ * in bplp we return the relative position where the brace beginning would
+ * have to be insrted in the string returned, which is the string to use
+ * as the completion. */
 
 static char *
-match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp,
-	  Cmatcher nm)
+match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp)
 {
     static unsigned char *ea;
     static int ealen = 0;
     static char *rw;
     static int rwlen;
 
-    int ll = strlen(l), lw = strlen(w), mlw;
+    int ll = strlen(l), lw = strlen(w), oll = ll, olw = lw, mlw;
     int il = 0, iw = 0, t, bc = brpl;
     char *nw = rw;
     Cmlist ms;
@@ -1942,7 +3204,7 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp,
     if (nlp) {
 	*nlp = NULL;
 
-	if (ll > ealen) {
+	if (ll + 1 > ealen) {
 	    /* This is the `in'/`out' string for pattern matching. */
 	    if (ealen)
 		zfree(ea, ealen);
@@ -1950,15 +3212,19 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp,
 	}
     }
     while (ll && lw) {
+	/* First try the matchers. */
 	for (ms = mstack; ms; ms = ms->next) {
 	    for (mp = ms->matcher; mp; mp = mp->next) {
-		if (nm == mp || lm == mp) {
-		    nm = NULL;
+		if (lm == mp)
 		    continue;
-		}
+
 		t = 1;
-		/* Try to match the prefix, if any. */
-		if (mp->flags & CMF_LEFT) {
+		if ((oll == ll || olw == lw) && !nlp && mp->wlen < 0)
+		    /* If we were called recursively, don't use `*' patterns
+		     * at the beginning (avoiding infinite recursion). */
+		    t = 0;
+		else if (mp->flags & CMF_LEFT) {
+		    /* Try to match the left anchor, if any. */
 		    if (il < mp->lalen || iw < mp->lalen)
 			t = 0;
 		    else if (mp->left)
@@ -1972,42 +3238,61 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp,
 		    if (ll < mp->llen || lw < mp->wlen)
 			t = 0;
 		    else if (mp->wlen < 0) {
-			/* This is reached, if we have a `*' pattern. */
+			/* This is reached if we have a `*' pattern. */
 			if ((t = pattern_match(mp->line, l, NULL, NULL))) {
-			    if (mp->flags & CMF_RIGHT) {
-				if (mp->right && ll >= mp->llen + mp->ralen)
-				    t = pattern_match(mp->right, l + mp->llen,
-						      NULL, NULL);
-				else
-				    t = 0;
-			    }
+			    if (mp->flags & CMF_RIGHT)
+				/* Check if the anchor matches what's on the
+				 * line. If it also matches the word, we don't
+				 * use the matcher since we don't want one of
+				 * these patterns on the line to match more
+				 * than one such sub-string in the word. */
+				t = (mp->right && ll >= mp->llen + mp->ralen &&
+				     pattern_match(mp->right, l + mp->llen,
+						   NULL, NULL) &&
+				     lw >= mp->ralen &&
+				     !pattern_match(mp->right, w, NULL, NULL));
 			    if (t) {
+				/* The anchor matched, so find out how many
+				 * characters are matched by the `*' pattern.
+				 * We do this by looping over the string
+				 * and calling this function recursively. */
 				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))
+				    if (match_pfx(p, q, NULL, NULL, NULL, NULL))
 					break;
 				}
 				if (k && i) {
+				    /* We found a position where the rest of
+				     * the line matched again (k != 0) and
+				     * we skipped over at least one character
+				     * (i != 0), so add a cline for this */
 				    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));
+						    CLF_SUF : 0) | CLF_VAR);
 				    }
+				    /* ...and adjust the pointers and counters
+				     * to continue after the matched portion. */
 				    w = q;
 				    iw = j;
-				    lw = k;
 				    l = p;
 				    il = jj;
 				    ll = kk;
-				    bc -= i;
-
+				    bc -= mp->llen;
+
+				    /* In bc we count the characters used
+				     * backward starting with the original
+				     * position of the brace beginning. So, 
+				     * if it becomes less than or equal to
+				     * zero, we have reached the place where
+				     * the brace string would have to be 
+				     * inserted. */
 				    if (bc <= 0 && bplp) {
 					*bplp = nw - rw;
 					bplp = NULL;
@@ -2107,7 +3392,8 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp,
 
 		*rlp = lr;
 	    } else {
-		addtocline(nlp, &lr, l, 0, dupstring(w), lw, NULL, CLF_END);
+		addtocline(nlp, &lr, l, 0, dupstring(w), lw, NULL,
+			   CLF_VAR | CLF_END);
 		nw = addtoword(&rw, &rwlen, nw, NULL, NULL, w, lw, 0);
 	    }
 	}
@@ -2135,10 +3421,11 @@ match_pfx(char *l, char *w, Cline *nlp, int *lp, Cline *rlp, int *bplp,
     return ((char *) 1);
 }
 
-/* Do the matching for a suffix. */
+/* Do the matching for a suffix. Almost the same as match_pfx(), only in the
+* other direction. */
 
 static char *
-match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm)
+match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp)
 {
     static unsigned char *ea;
     static int ealen = 0;
@@ -2158,7 +3445,7 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm)
     if (nlp) {
 	*nlp = NULL;
 
-	if (ll > ealen) {
+	if (ll + 1 > ealen) {
 	    if (ealen)
 		zfree(ea, ealen);
 	    ea = (unsigned char *) zalloc(ealen = ll + 20);
@@ -2167,10 +3454,9 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm)
     while (ll && lw) {
 	for (ms = mstack; ms; ms = ms->next) {
 	    for (mp = ms->matcher; mp; mp = mp->next) {
-		if (nm == mp || lm == mp) {
-		    nm = NULL;
+		if (lm == mp)
 		    continue;
-		}
+
 		t = 1;
 		if (mp->flags & CMF_RIGHT) {
 		    if (il < mp->ralen || iw < mp->ralen)
@@ -2187,22 +3473,21 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm)
 		    else if (mp->wlen < 0) {
 			if ((t = pattern_match(mp->line, l - mp->llen,
 					       NULL, NULL))) {
-			    if (mp->flags & CMF_LEFT) {
-				if (mp->left && ll >= mp->llen + mp->lalen)
-				    t = pattern_match(mp->left,
-						      l - mp->llen - mp->lalen,
-						      NULL, NULL);
-				else
-				    t = 0;
-			    }
+			    if (mp->flags & CMF_LEFT)
+				t = (mp->left && ll >= mp->llen + mp->lalen &&
+				     pattern_match(mp->left,
+						   l - mp->llen - mp->lalen,
+						   NULL, NULL) &&
+				     lw >= mp->lalen &&
+				     !pattern_match(mp->left, w - mp->lalen,
+						    NULL, NULL));
 			    if (t) {
 				int i = 0, j = iw, k = lw;
 				int jj = il + mp->llen, kk = ll - mp->llen;
 				char *p = l - mp->llen - 1, *q = w - 1;
 
 				for (; k; i++, j++, k--, q--)
-				    if (match_pfx(p, q, NULL, NULL,
-						  NULL, NULL, mp))
+				    if (match_pfx(p, q, NULL, NULL, NULL, NULL))
 					break;
 				if (k && i) {
 				    if (nlp) {
@@ -2212,15 +3497,14 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm)
 					addtocline(nlp, &lr, l - mp->llen,
 						   mp->llen, w - i, i, mp, 
 						   ((mp->flags & CMF_LEFT) ?
-						    CLF_SUF : 0));
+						    CLF_SUF : 0) | CLF_VAR);
 				    }
 				    w = q + 1;
 				    iw = j;
-				    lw = k;
 				    l = p + 1;
 				    il = jj;
 				    ll = kk;
-				    bc -= i;
+				    bc -= mp->llen;
 
 				    if (bc <= 0 && bslp) {
 					*bslp = nw - rw;
@@ -2252,7 +3536,8 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm)
 		}
 		if (t) {
 		    if (nlp) {
-			nw = addtoword(&rw, &rwlen, nw, mp, l, w, mlw, 1);
+			nw = addtoword(&rw, &rwlen, nw, mp, l - mp->llen,
+				       w - mlw, mlw, 1);
 			addtocline(nlp, &lr, l - mp->llen, mp->llen,
 				   w - mlw, mlw, mp, 0);
 		    }
@@ -2321,7 +3606,14 @@ match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp, Cmatcher nm)
     return ((char *) 1);
 }
 
-/* Check if the word `w' matches. */
+/* Check if the word `w' is matched by the strings in pfx and sfx (the prefix
+ * and the suffix from the line. In clp a cline list is returned for w.
+ * qu is non-zero if the words has to be quoted before processed any further.
+ * bpl and bsl are used to report the positions where the brace-strings in
+ * the prefix and the suffix have to be re-inserted if this match is inserted
+ * in the line.
+ * The return value is the string to use as a completion or NULL if the prefix
+ * and the suffix don't match the word w. */
 
 static char *
 comp_match(char *pfx, char *sfx, char *w, Cline *clp, int qu, int *bpl, int *bsl)
@@ -2332,18 +3624,27 @@ comp_match(char *pfx, char *sfx, char *w, Cline *clp, int qu, int *bpl, int *bsl
 
     if (qu)
 	w = quotename(w, NULL, NULL, NULL);
+
     if (*sfx) {
+	/* We have a suffix, so this gets a bit more complicated. */
 	char *p, *s;
 	int sl;
 	Cline sli, last;
 
-	if ((p = match_pfx(pfx, w, &pli, &pl, &last, bpl, NULL))) {
-	    if ((s = match_sfx(sfx, w + pl, &sli, &sl, bsl, NULL))) {
+	/* First see if the prefix matches. In pl we get the length of
+	 * the string matched by it. */
+	if ((p = match_pfx(pfx, w, &pli, &pl, &last, bpl))) {
+	    /* Then try to match the rest of the string with the suffix. */
+	    if ((s = match_sfx(sfx, w + pl, &sli, &sl, bsl))) {
 		int pml, sml;
 
+		/* Now append the cline list for the suffix to the one
+		 * for the prefix. */
 		last->llen -= sl;
 		last->next = revert_clines(sli);
 
+		/* And store the correct parts of the prefix and the suffix
+		 * in the cline struct in the middle. */
 		pml = strlen(p);
 		sml = strlen(s);
 		r = (char *) halloc(pml + sml + last->llen + 1);
@@ -2351,6 +3652,8 @@ comp_match(char *pfx, char *sfx, char *w, Cline *clp, int qu, int *bpl, int *bsl
 		strncpy(r + pml, last->line, last->llen);
 		strcpy(r + pml + last->llen, s);
 	    } else {
+		/* Suffix didn't match, so free the cline list for the
+		 * prefix. */
 		last->next = freecl;
 		freecl = pli;
 
@@ -2359,9 +3662,12 @@ 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, NULL)))
+    } else if (!(r = match_pfx(pfx, w, &pli, &pl, NULL, bpl)))
+	/* We had only the prefix to match and that didn't match. */
 	return NULL;
 
+    /* If there are path prefixes or suffixes, pre- and append them to
+     * the cline list built. */
     if (lppre && *lppre) {
 	Cline l, t = str_cline(lppre, -1, &l);
 
@@ -2409,21 +3715,26 @@ instmatch(Cmatch m)
 {
     int l, r = 0, ocs, a = cs;
 
+    /* Ignored prefix. */
     if (m->ipre) {
 	inststrlen(m->ipre, 1, (l = strlen(m->ipre)));
 	r += l;
-    } 
+    }
+    /* -P prefix. */
     if (m->pre) {
 	inststrlen(m->pre, 1, (l = strlen(m->pre)));
 	r += l;
     }
+    /* Path prefix. */
     if (m->ppre) {
 	inststrlen(m->ppre, 1, (l = strlen(m->ppre)));
 	r += l;
     }
+    /* The string itself. */
     inststrlen(m->str, 1, (l = strlen(m->str)));
     r += l;
     ocs = cs;
+    /* Re-insert the brace beginning, if any. */
     if (brbeg && *brbeg) {
 	cs = a + m->brpl + (m->pre ? strlen(m->pre) : 0);
 	l = strlen(brbeg);
@@ -2433,10 +3744,12 @@ instmatch(Cmatch m)
 	ocs += l;
 	cs = ocs;
     }
+    /* Path suffix. */
     if (m->psuf) {
 	inststrlen(m->psuf, 1, (l = strlen(m->psuf)));
 	r += l;
     }
+    /* Re-insert the brace end. */
     if (brend && *brend) {
 	a = cs;
 	cs -= m->brsl;
@@ -2447,6 +3760,7 @@ instmatch(Cmatch m)
 	cs = a + l;
     } else
 	brscs = -1;
+    /* -S suffix */
     if (m->suf) {
 	inststrlen(m->suf, 1, (l = strlen(m->suf)));
 	r += l;
@@ -2455,13 +3769,17 @@ instmatch(Cmatch m)
     return r;
 }
 
+/* This is used by compadd to add a couple of matches. The arguments are
+ * the strings given via options. The last argument is the array with
+ * the matches. */
+
 /**/
 void
 addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 	   char *suf, char *group, char *rems, char *remf, char *ign,
 	   int flags, int aflags, Cmatcher match, char **argv)
 {
-    char *s, *t, *e, *te, *ms, *lipre = NULL, *lpre, *lsuf, **aign = NULL;
+    char *s, *t, *e, *me, *te, *ms, *lipre = NULL, *lpre, *lsuf, **aign = NULL;
     int lpl, lsl, i, pl, sl, test, bpl, bsl, lsm, llpl;
     Aminfo ai;
     Cline lc = NULL;
@@ -2470,6 +3788,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
     struct cmlist mst;
     Cmlist oms = mstack;
 
+    /* Select the set of matches. */
     if (aflags & CAF_ALT) {
 	l = fmatches;
 	ai = fainfo;
@@ -2477,26 +3796,44 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 	l = matches;
 	ai = ainfo;
     }
+    /* Store the matcher in our stack of matchers. */
     if (match) {
 	mst.next = mstack;
 	mst.matcher = match;
 	mstack = &mst;
+	if (had_lm && mnum)
+	    bmstack1.matcher = NULL;
+	else {
+	    bmstack1.matcher = match;
+	    had_lm = 1;
+	}
 	addlinknode(matchers, match);
 	match->refc++;
+    } else {
+	bmstack1.matcher = NULL;
+	if (mnum)
+	    had_lm = 1;
     }
+    /* Use menu-completion (-U)? */
     if ((aflags & CAF_MENU) && isset(AUTOMENU))
 	usemenu = 1;
+    /* Get the suffixes to ignore. */
     if (ign)
 	aign = get_user_var(ign);
 
+    /* Switch back to the heap that was used when the completion widget
+     * was invoked. */
     SWITCHHEAPS(compheap) {
 	HEAPALLOC {
+	    /* Get the contents of the completion variables if we have
+	     * to perform matching. */
 	    if (aflags & CAF_MATCH) {
 		lipre = dupstring(compiprefix);
 		lpre = dupstring(compprefix);
 		llpl = strlen(lpre);
 		lsuf = dupstring(compsuffix);
 	    }
+	    /* Now duplicate the strings we have from the command line. */
 	    if (ipre)
 		ipre = (lipre ? dyncat(lipre, ipre) : dupstring(ipre));
 	    else if (lipre)
@@ -2522,6 +3859,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		untokenize(prpre);
 	    } else
 		prpre = dupstring(prpre);
+	    /* Select the group in which to store the matches. */
 	    if (group) {
 		endcmgroup(NULL);
 		begcmgroup(group, (aflags & CAF_NOSORT));
@@ -2533,6 +3871,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		rems = NULL;
 	    } else if (rems)
 		rems = dupstring(rems);
+	    /* Build the common -P prefix. */
     	    if (ai->pprefix) {
 		if (pre)
 		    ai->pprefix[pfxlen(ai->pprefix, pre)] = '\0';
@@ -2541,12 +3880,16 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 	    } else
 		ai->pprefix = dupstring(pre ? pre : "");
 
+	    /* Walk through the matches given. */
 	    for (; (s = dupstring(*argv)); argv++) {
 		sl = strlen(s);
+		lc = NULL;
 		ms = NULL;
 		bpl = brpl;
 		bsl = brsl;
 		if ((!psuf || !*psuf) && aign) {
+		    /* Do the suffix-test. If the match has one of the
+		     * suffixes from ign, we put it in the alternate set. */
 		    char **pt = aign;
 		    int filell;
 
@@ -2564,6 +3907,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		    }
 		}
 		if (aflags & CAF_MATCH) {
+		    /* Do the matching. */
 		    t = (ppre ? dyncat(ppre, s) : s);
 		    pl = sl + lpl;
 		    if ((test = (llpl <= pl && !strncmp(t, lpre, pl))))
@@ -2576,30 +3920,61 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 			test = 1;
 		    if (!test)
 			continue;
-		    e = s + sl;
+		    me = e = s + sl;
 		} else {
 		    e = s;
-		    pl = lpl;
+		    me = s + sl;
+		    pl = sl;
 		}
+		/* Quoting? */
 		if (!(aflags & CAF_QUOTE)) {
 		    te = s + pl;
 		    s = quotename(s, &e, te, &pl);
 		    sl = strlen(s);
 		}
-		if (!ms && ai->firstm) {
+		/* The rest is almost the same as in addmatch(). */
+		if (!ms) {
 		    if (sl < ai->minlen)
 			ai->minlen = sl;
-		    if ((i = sfxlen(ai->firstm->str, s)) < ai->suflen)
+		    if (!mstack && ai->firstm &&
+			(i = sfxlen(ai->firstm->str, s)) < ai->suflen)
 			ai->suflen = i;
 		}
 		t = s;
 		if (ppre)
 		    t = dyncat(ppre, t);
+		if (!ms && mstack) {
+		    Cline *clp = &lc, tlc;
+		    char *ss = dupstring(s), *ee = me + (ss - s);
+
+		    if (ppre && *ppre) {
+			*clp = str_cline(ppre, strlen(ppre), &tlc);
+			clp = &(tlc->next);
+		    }
+		    if (ee != ss + sl || (lpsuf && *lpsuf)) {
+			*clp = tlc = getcline(ss, ee - ss,
+					      NULL, 0, CLF_MID);
+			clp = &(tlc->next);
+			if (ee != ss + sl) {
+			    *clp = str_cline(ee, (ss + sl) - ee, &tlc);
+			    clp = &(tlc->next);
+			}
+			if (lpsuf && *lpsuf) {
+			    *clp = str_cline(lpsuf, strlen(lpsuf), &tlc);
+			    clp = &(tlc->next);
+			}
+		    } else {
+			*clp = tlc = getcline(NULL, 0, ss, sl,
+					      CLF_END | CLF_VAR);
+			clp = &(tlc->next);
+		    }
+		    *clp = NULL;
+		}
 		if (ipre && *ipre) {
 		    Cline tlc = prepend_cline(ipre, lc);
 
 		    ai->noipre = 0;
-		    if (!ms) {
+		    if (!ms && !mstack) {
 			if ((aflags & CAF_MATCH) || ai->icpl > pl)
 			    ai->icpl = pl;
 			if ((aflags & CAF_MATCH) || ai->icsl > lsl)
@@ -2620,7 +3995,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		    lc = tlc;
 		} else
 		    ai->iprefix = "";
-		if (!ms) {
+		if (!ms && !mstack) {
 		    if ((aflags & CAF_MATCH) || ai->cpl > pl)
 			ai->cpl = pl;
 		    if ((aflags & CAF_MATCH) || ai->csl > lsl)
@@ -2635,6 +4010,7 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 		mnum++;
 		ai->count++;
 
+		/* Finally add the match. */
 		cm = (Cmatch) halloc(sizeof(struct cmatch));
 		cm->ppre = ppre;
 		cm->psuf = psuf;
@@ -2674,6 +4050,8 @@ addmatches(char *ipre, char *ppre, char *psuf, char *prpre, char *pre,
 	} LASTALLOC;
     } SWITCHBACKHEAPS;
 
+    /* We switched back to the current heap, now restore the stack of
+     * matchers. */
     mstack = oms;
 }
 
@@ -2854,10 +4232,11 @@ addmatch(char *s, char *t)
     }
     if (!test)
 	return;
-    if (!ms && !ispattern && ai->firstm) {
+    if (!ms) {
 	if (sl < ai->minlen)
 	    ai->minlen = sl;
-	if ((test = sfxlen(ai->firstm->str, s)) < ai->suflen)
+	if (!mstack && !ispattern && ai->firstm &&
+	    (test = sfxlen(ai->firstm->str, s)) < ai->suflen)
 	    ai->suflen = test;
     }
 
@@ -2875,11 +4254,41 @@ addmatch(char *s, char *t)
     t = s;
     if (lppre)
 	t = dyncat(lppre, t);
+    if (!ms && mstack) {
+	Cline *clp = &lc, tlc;
+	char *ss = dupstring(s), *ee = e + (ss - s);
+
+	if (lppre && *lppre) {
+	    *clp = str_cline(lppre, strlen(lppre), &tlc);
+	    clp = &(tlc->next);
+	}
+	if (pl) {
+	    *clp = str_cline(ss, pl, &tlc);
+	    clp = &(tlc->next);
+	}
+	if (ee != ss + sl || (lpsuf && *lpsuf)) {
+	    *clp = tlc = getcline(ss + pl, (ee - ss) - pl, NULL, 0, CLF_MID);
+	    clp = &(tlc->next);
+	    if (ee != ss + sl) {
+		*clp = str_cline(ee, (ss + sl) - ee, &tlc);
+		clp = &(tlc->next);
+	    }
+	    if (lpsuf && *lpsuf) {
+		*clp = str_cline(lpsuf, strlen(lpsuf), &tlc);
+		clp = &(tlc->next);
+	    }
+	} else {
+	    *clp = tlc = getcline(NULL, 0, ss + pl, sl - pl,
+				  CLF_END | CLF_VAR);
+	    clp = &(tlc->next);
+	}
+	*clp = NULL;
+    }
     if (ipre && *ipre) {
 	Cline tlc = prepend_cline(ipre, lc);
 
 	ai->noipre = 0;
-	if (!ms) {
+	if (!ms && !mstack) {
 	    ai->icpl = lppl + mpl;
 	    ai->icsl = lpsl + msl;
 	    if (ai->iaprefix)
@@ -2899,7 +4308,7 @@ addmatch(char *s, char *t)
     } else
 	ai->iprefix = "";
 
-    if (!ms) {
+    if (!ms && !mstack) {
 	ai->cpl = lppl + mpl;
 	ai->csl = lpsl + msl;
 	if (ai->aprefix)
@@ -3146,7 +4555,8 @@ getreal(char *str)
     addlinknode(l, dupstring(str));
     prefork(l, 0);
     noerrs = ne;
-    if (!errflag && nonempty(l))
+    if (!errflag && nonempty(l) &&
+	((char *) peekfirst(l)) && ((char *) peekfirst(l))[0])
 	return dupstring(peekfirst(l));
     errflag = 0;
 
@@ -3464,14 +4874,13 @@ static int brange, erange;
 
 static unsigned long ccont;
 
-/* Create the completion list.  This is called whenever some bit of  *
- * completion code needs the list.  If the list is already available *
- * (validlist!=0), this function doesn't do anything.  Along with    *
- * the list is maintained the prefixes/suffixes etc.  When any of    *
- * this becomes invalid -- e.g. if some text is changed on the       *
- * command line -- invalidatelist() should be called, to set         *
- * validlist to zero and free up the memory used.  This function     *
- * returns non-zero on error.                                        */
+/* Create the completion list.  This is called whenever some bit of   *
+ * completion code needs the list.                                    *
+ * Along with the list is maintained the prefixes/suffixes etc.  When *
+ * any of this becomes invalid -- e.g. if some text is changed on the *
+ * command line -- invalidatelist() should be called, to set          *
+ * validlist to zero and free up the memory used.  This function      *
+ * returns non-zero on error.                                         */
 
 /**/
 static int
@@ -3482,8 +4891,6 @@ makecomplist(char *s, int incmd, int lst)
 
     /* If we already have a list from a previous execution of this *
      * function, skip the list building code.                      */
-    if (validlist)
-	return !nmatches;
 
     if ((m = cmatcher)) {
 	Cmlist mm, *mp = &mm;
@@ -3499,13 +4906,19 @@ makecomplist(char *s, int incmd, int lst)
 	m = mm;
     }
     compmatcher = 1;
+    bmstack = &bmstack1;
+    bmstack1.next = &bmstack0;
+    bmstack0.next = NULL;
+    bmstack0.matcher = bmstack1.matcher = NULL;
     for (;;) {
 	if (m) {
 	    ms.next = NULL;
 	    ms.matcher = m->matcher;
 	    mstack = &ms;
+	    bmstack0.matcher = m->matcher;
 	} else
 	    mstack = NULL;
+
 	ainfo = (Aminfo) hcalloc(sizeof(struct aminfo));
 	fainfo = (Aminfo) hcalloc(sizeof(struct aminfo));
 
@@ -3515,9 +4928,11 @@ makecomplist(char *s, int incmd, int lst)
 
 	freecl = NULL;
 
-	lastambig = 0;
+	if (!validlist)
+	    lastambig = 0;
 	amatches = 0;
 	mnum = 0;
+	had_lm = 0;
 	begcmgroup("default", 0);
 
 	ccused = newlinklist();
@@ -3573,11 +4988,13 @@ ctokenize(char *p)
 	if (*p == '\\')
 	    bslash = 1;
 	else {
-	    if (*p == '$' || *p == '=') {
+	    if (*p == '$' || *p == '=' || *p == '{' || *p == '}') {
 		if (bslash)
 		    p[-1] = Bnull;
 		else
-		    *p = (*p == '$' ? String : Equals);
+		    *p = (*p == '$' ? String :
+			  (*p == '=' ? Equals :
+			   (*p == '{' ? Inbrace : Outbrace)));
 	    }
 	    bslash = 0;
 	}
@@ -4154,8 +5571,18 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	ms.next = mstack;
 	ms.matcher = cc->matcher;
 	mstack = &ms;
+	if (had_lm && mnum)
+	    bmstack1.matcher = NULL;
+	else {
+	    bmstack1.matcher = cc->matcher;
+	    had_lm = 1;
+	}
 	addlinknode(matchers, cc->matcher);
 	cc->matcher->refc++;
+    } else {
+	bmstack1.matcher = NULL;
+	if (mnum)
+	    had_lm = 1;
     }
     /* Insert the prefix (compctl -P), if any. */
     if (cc->prefix) {
@@ -5001,7 +6428,7 @@ invalidatelist(void)
 	listmatches();
     if(validlist)
 	freematches();
-    lastambig = menucmp = validlist = showinglist = 0;
+    lastambig = menucmp = validlist = showinglist = fromcomp = 0;
     menucur = NULL;
     compwidget = NULL;
 }
@@ -5056,15 +6483,17 @@ get_user_var(char *nam)
     } else {
 	/* Otherwise it should be a parameter name. */
 	char **arr = NULL, *val;
-	if (!(arr = getaparam(nam)) && !(arr = gethparam(nam)) &&
-	    (val = getsparam(nam))) {
+
+	if ((arr = getaparam(nam)) || (arr = gethparam(nam)))
+	    return (incompfunc ? arrdup(arr) : arr);
+
+	if ((val = getsparam(nam))) {
 	    arr = (char **)ncalloc(2*sizeof(char *));
 	    arr[0] = val;
 	    arr[1] = NULL;
 	}
 	return arr;
     }
-
 }
 
 /* This is strcmp with ignoring backslashes. */
@@ -5437,14 +6866,13 @@ static void
 do_ambiguous(void)
 {
     int p = (usemenu || ispattern), atend = (cs == we);
-    int am = 0;
 
     menucmp = 0;
 
     /* If we have to insert the first match, call do_single().  This is *
      * how REC_EXACT takes effect.  We effectively turn the ambiguous   *
      * completion into an unambiguous one.                              */
-    if (ainfo && ainfo->exact == 1 && isset(RECEXACT) &&
+    if (ainfo && ainfo->exact == 1 && isset(RECEXACT) && !fromcomp &&
 	(usemenu == 0 || unset(AUTOMENU))) {
 	do_single(ainfo->exactm);
 	invalidatelist();
@@ -5464,16 +6892,21 @@ do_ambiguous(void)
 	 * normal menu completion options.                                 */
 	do_ambig_menu();
     } else {
-	int ics = cs, ocs, pl = 0, l, lp, ls;
+	VARARR(char, oline, ll);
+	int sl = 0, oll = ll;
+	int ocs, pl = 0, l, lp, ls, la = 0;
 	char *ps;
 	Cline lc;
 
 	if (!ainfo)
 	    return;
 
+	/* Copy the line buffer to be able to easily test if it changed. */
+	memcpy(oline, line, ll);
+
 	fixsuffix();
 
-	/* Delete the old stuff from the command line. */
+	/* First remove the old string from the line. */
 	cs = wb;
 	foredel(we - wb);
 
@@ -5499,8 +6932,6 @@ do_ambiguous(void)
 	    ls = ainfo->csl;
 	}
 	if (lc) {
-	    int sl = 0;
-
 	    if (lp) {
 		if (ls) {
 		    if (ainfo->firstm->psuf)
@@ -5514,6 +6945,7 @@ do_ambiguous(void)
 		    merge_cline(lc, ps, lp, NULL, 0, 0);
 	    }
 	    inst_cline(lc, pl, sl);
+
 	} else {
 	    inststrlen(ps, 1, -1);
 	    ocs = cs;
@@ -5536,13 +6968,16 @@ do_ambiguous(void)
 	    }
 	    cs = ocs;
 	}
-	/* If REC_EXACT and AUTO_MENU are set and what we inserted is an   *
-	 * exact match, we want to start menu completion now. Otherwise    *
-	 * on the next call to completion the inserted string would be     *
-	 * taken as a match and no menu completion would be started.       */
+	/* la is non-zero if listambiguous may be used. Copying and
+	 * comparing the line looks like BFI but it is the easiest
+	 * solution. Really. */
+	la = (ll != oll || strncmp(oline, (char *) line, ll));
 
-	if (isset(RECEXACT) && !lc && ps && ainfo->minlen == strlen(ps))
-	    am = 1;
+	/* If REC_EXACT and AUTO_MENU are set and what we inserted is an *
+	 * exact match, we want menu completion the next time round      *
+	 * so we set fromcomp,to ensure that the word on the line is not *
+	 * taken as an exact match.                                      */
+	fromcomp = isset(AUTOMENU);
 
 	/*
 	 * If the LIST_AMBIGUOUS option (meaning roughly `show a list only *
@@ -5550,9 +6985,11 @@ do_ambiguous(void)
 	 * prefix was inserted, return now, bypassing the list-displaying  *
 	 * code.  On the way, invalidate the list and note that we don't   *
 	 * want to enter an AUTO_MENU imediately.                          */
-	if(isset(LISTAMBIGUOUS) && !am &&
-	   (ics != cs || (ainfo->suflen && !atend))) {
+	if(isset(LISTAMBIGUOUS) && la) {
+	    int fc = fromcomp;
+
 	    invalidatelist();
+	    fromcomp = fc;
 	    lastambig = 0;
 	    return;
 	}
@@ -5564,8 +7001,6 @@ do_ambiguous(void)
     if (isset(AUTOLIST) && !isset(BASHAUTOLIST) && !amenu && !showinglist &&
 	smatches >= 2)
 	showinglist = -2;
-    if (am)
-	lastambig = 1;
 }
 
 /* This is a stat that ignores backslashes in the filename.  The `ls' *
diff --git a/Src/cond.c b/Src/cond.c
index a46833f66..00beb8e65 100644
--- a/Src/cond.c
+++ b/Src/cond.c
@@ -30,6 +30,13 @@
 #include "zsh.mdh"
 #include "cond.pro"
 
+int tracingcond;
+
+static char *condstr[COND_MOD] = {
+    "!", "&&", "||", "==", "!=", "<", ">", "-nt", "-ot", "-ef", "-eq",
+    "-ne", "-lt", "-gt", "-le", "-ge"
+};
+
 /**/
 int
 evalcond(Cond c)
@@ -38,11 +45,23 @@ evalcond(Cond c)
 
     switch (c->type) {
     case COND_NOT:
+	if (tracingcond)
+	    fprintf(stderr, " %s", condstr[c->type]);
 	return !evalcond(c->left);
     case COND_AND:
-	return evalcond(c->left) && evalcond(c->right);
+	if (evalcond(c->left)) {
+	    if (tracingcond)
+		fprintf(stderr, " %s", condstr[c->type]);
+	    return evalcond(c->right);
+	} else
+	    return 0;
     case COND_OR:
-	return evalcond(c->left) || evalcond(c->right);
+	if (!evalcond(c->left)) {
+	    if (tracingcond)
+		fprintf(stderr, " %s", condstr[c->type]);
+	    return evalcond(c->right);
+	} else
+	    return 1;
     case COND_MOD:
     case COND_MODI:
 	{
@@ -58,6 +77,9 @@ evalcond(Cond c)
 			return 0;
 		    }
 		}
+		if (tracingcond)
+		    tracemodcond((char *)c->left, (char **)c->right,
+				 c->type == COND_MODI);
 		return cd->handler((char **) c->right, cd->condid);
 	    }
 	    else {
@@ -71,6 +93,8 @@ evalcond(Cond c)
 			zerr("unrecognized condition: `%s'", (char *) c->left, 0);
 			return 0;
 		    }
+		    if (tracingcond)
+			tracemodcond((char *)c->left, a, c->type == COND_MODI);
 		    a[0] = (char *) c->left;
 		    return cd->handler(a, cd->condid);
 		} else
@@ -86,6 +110,20 @@ evalcond(Cond c)
 	if (c->type != COND_STREQ && c->type != COND_STRNEQ)
 	    untokenize(c->right);
     }
+
+    if (tracingcond) {
+	if (c->type < COND_MOD) {
+	    char *rt = (char *)c->right;
+	    if (c->type == COND_STREQ || c->type == COND_STRNEQ) {
+		rt = dupstring(rt);
+		untokenize(rt);
+	    }
+	    fprintf(stderr, " %s %s %s", (char *)c->left, condstr[c->type],
+		    rt);
+	} else
+	    fprintf(stderr, " -%c %s", c->type, (char *)c->left);
+    }
+
     switch (c->type) {
     case COND_STREQ:
 	return matchpat(c->left, c->right);
@@ -294,3 +332,21 @@ cond_match(char **args, int num, char *str)
 
     return matchpat(str, s);
 }
+
+/**/
+static void
+tracemodcond(char *name, char **args, int inf)
+{
+    char **aptr;
+    MUSTUSEHEAP("tracemodcond");
+    args = duparray(args, (VFunc) dupstring);
+    for (aptr = args; *aptr; aptr++)
+	untokenize(*aptr);
+    if (inf) {
+	fprintf(stderr, " %s %s %s", args[0], name, args[1]);
+    } else {
+	fprintf(stderr, " %s", name);
+	while (*args)
+	    fprintf(stderr, " %s", *args++);
+    }
+}
diff --git a/Src/exec.c b/Src/exec.c
index bb331f426..59061af3f 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -1208,7 +1208,7 @@ addvars(LinkList l, int export)
 	    addlinknode(vl, v->str);
 	} else
 	    vl = v->arr;
-	prefork(vl, v->type == PM_SCALAR ? 7 : 3);
+	prefork(vl, v->type == PM_SCALAR ? (PF_SINGLE|PF_ASSIGN) : PF_ASSIGN);
 	if (errflag)
 	    return;
 	if (isset(GLOBASSIGN) || v->type != PM_SCALAR)
@@ -1356,7 +1356,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
     }
 
     /* Do prefork substitutions */
-    prefork(args, assign ? 2 : isset(MAGICEQUALSUBST));
+    prefork(args, (assign || isset(MAGICEQUALSUBST)) ? PF_TYPESET : 0);
 
     if (type == SIMPLE) {
 	int unglobbed = 0;
@@ -2519,13 +2519,26 @@ spawnpipes(LinkList l)
     }
 }
 
+extern int tracingcond;
+
 /* evaluate a [[ ... ]] */
 
 /**/
 static int
 execcond(Cmd cmd)
 {
-    return !evalcond(cmd->u.cond);
+    int stat;
+    if (isset(XTRACE)) {
+	fprintf(stderr, "%s[[", prompt4 ? prompt4 : "");
+	tracingcond++;
+    }
+    stat = !evalcond(cmd->u.cond);
+    if (isset(XTRACE)) {
+	fprintf(stderr, " ]]\n");
+	fflush(stderr);
+	tracingcond--;
+    }
+    return stat;
 }
 
 /* evaluate a ((...)) arithmetic command */
@@ -2537,8 +2550,17 @@ execarith(Cmd cmd)
     char *e;
     long val = 0;
 
-    while ((e = (char *) ugetnode(cmd->args)))
+    if (isset(XTRACE))
+	fprintf(stderr, "%s((", prompt4 ? prompt4 : "");
+    while ((e = (char *) ugetnode(cmd->args))) {
+	if (isset(XTRACE))
+	    fprintf(stderr, " %s", e);
 	val = matheval(e);
+    }
+    if (isset(XTRACE)) {
+	fprintf(stderr, " ))\n", stderr);
+	fflush(stderr);
+    }
     errflag = 0;
     return !val;
 }
@@ -2613,6 +2635,18 @@ execshfunc(Cmd cmd, Shfunc shf)
 	deletejob(jobtab + thisjob);
     }
 
+    if (isset(XTRACE)) {
+	LinkNode lptr;
+	fprintf(stderr, "%s", prompt4 ? prompt4 : prompt4);
+	for (lptr = firstnode(cmd->args); lptr; incnode(lptr)) {
+	    if (lptr != firstnode(cmd->args))
+		fputc(' ', stderr);
+	    fprintf(stderr, "%s", (char *)getdata(lptr));
+	}
+	fputc('\n', stderr);
+	fflush(stderr);
+    }
+
     doshfunc(shf->nam, shf->funcdef, cmd->args, shf->flags, 0);
 
     if (!list_pipe)
diff --git a/Src/glob.c b/Src/glob.c
index 5815f05a8..516f75618 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -1810,7 +1810,7 @@ xpandredir(struct redir *fn, LinkList tab)
     fake = newlinklist();
     addlinknode(fake, fn->name);
     /* ...which undergoes all the usual shell expansions */
-    prefork(fake, isset(MULTIOS) ? 0 : 4);
+    prefork(fake, isset(MULTIOS) ? 0 : PF_SINGLE);
     /* Globbing is only done for multios. */
     if (!errflag && isset(MULTIOS))
 	globlist(fake);
diff --git a/Src/params.c b/Src/params.c
index 88b5fdf73..77166209f 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -568,8 +568,10 @@ createparam(char *name, int flags)
 
 	if (isset(ALLEXPORT) && !oldpm)
 	    flags |= PM_EXPORTED;
-    } else
+    } else {
 	pm = (Param) alloc(sizeof *pm);
+	pm->nam = nulstring;
+    }
     pm->flags = flags;
 
     if(!(pm->flags & PM_SPECIAL))
@@ -1835,6 +1837,12 @@ arrhashsetfn(Param pm, char **val)
     while (*aptr) {
 	/* The parameter name is ztrdup'd... */
 	v->pm = createparam(*aptr, PM_SCALAR|PM_UNSET);
+	/*
+	 * createparam() doesn't return anything if the parameter
+	 * already existed.
+	 */
+	if (!v->pm)
+	    v->pm = (Param) paramtab->getnode(paramtab, *aptr);
 	zsfree(*aptr++);
 	/* ...but we can use the value without copying. */
 	setstrvalue(v, *aptr++);
diff --git a/Src/subst.c b/Src/subst.c
index 422c9c4e9..66c363145 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -42,11 +42,7 @@ char nulstring[] = {Nularg, '\0'};
  *  - Brace expansion
  *  - Tilde and equals substitution
  *
- * Bits 0 and 1 of flags are used in filesub.
- * bit 0 is set when we are doing MAGIC_EQUALSUBST or normal
- *	 assignment but not a typeset.
- * bit 1 is set on a real assignment (both typeset and normal).
- * bit 2 is a flag to paramsubst (single word sub)
+ * PF_* flags are defined in zsh.h
  */
 
 /**/
@@ -70,20 +66,22 @@ prefork(LinkList list, int flags)
 		return;
 	} else {
 	    if (isset(SHFILEEXPANSION))
-		filesub((char **)getaddrdata(node), flags & 3);
-	    if (!(node = stringsubst(list, node, flags & 4)))
+		filesub((char **)getaddrdata(node),
+			flags & (PF_TYPESET|PF_ASSIGN));
+	    if (!(node = stringsubst(list, node, flags & PF_SINGLE)))
 		return;
 	}
     }
     for (node = firstnode(list); node; incnode(node)) {
 	if (*(char *)getdata(node)) {
 	    remnulargs(getdata(node));
-	    if (unset(IGNOREBRACES) && !(flags & 4))
+	    if (unset(IGNOREBRACES) && !(flags & PF_SINGLE))
 		while (hasbraces(getdata(node)))
 		    xpandbraces(list, &node);
 	    if (unset(SHFILEEXPANSION))
-		filesub((char **)getaddrdata(node), flags & 3);
-	} else if (!(flags & 4))
+		filesub((char **)getaddrdata(node),
+			flags & (PF_TYPESET|PF_ASSIGN));
+	} else if (!(flags & PF_SINGLE))
 	    uremnode(list, node);
 	if (errflag)
 	    return;
@@ -234,7 +232,7 @@ singsub(char **s)
 
     foo = newlinklist();
     addlinknode(foo, *s);
-    prefork(foo, 4);
+    prefork(foo, PF_SINGLE);
     if (errflag)
 	return;
     *s = (char *) ugetnode(foo);
@@ -287,8 +285,10 @@ multsub(char **s, char ***a, int *isarr, char *sep)
     return !l;
 }
 
-/* ~, = subs: assign = 2 => typeset; assign = 1 => something that looks
-	like an assignment but may not be; assign = 3 => normal assignment */
+/*
+ * ~, = subs: assign & PF_TYPESET => typeset or magic equals
+ *            assign & PF_ASSIGN => normal assignment
+ */
 
 /**/
 void
@@ -302,12 +302,8 @@ filesub(char **namptr, int assign)
     if (!assign)
 	return;
 
-    if (assign < 3) {
+    if (assign & PF_TYPESET) {
 	if ((*namptr)[1] && (sub = strchr(*namptr + 1, Equals))) {
-	    if (assign == 1)
-		for (ptr = *namptr; ptr != sub; ptr++)
-		    if (!iident(*ptr) && !INULL(*ptr))
-			return;
 	    str = sub + 1;
 	    if ((sub[1] == Tilde || sub[1] == Equals) && filesubstr(&str, assign)) {
 		sub[1] = '\0';
diff --git a/Src/utils.c b/Src/utils.c
index ee06cfeca..4ff4a91ba 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -2261,7 +2261,7 @@ duplist(LinkList l, VFunc func)
 }
 
 /**/
-static char **
+char **
 duparray(char **arr, VFunc func)
 {
     char **ret, **rr;
diff --git a/Src/zsh.h b/Src/zsh.h
index 87ec3e0c0..fa5b5632c 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -950,6 +950,11 @@ struct param {
 #define SUB_ALL		0x0100	/* match complete string */
 #define SUB_GLOBAL	0x0200	/* global substitution ${..//all/these} */
 
+/* Flags as the second argument to prefork */
+#define PF_TYPESET	0x01	/* argument handled like typeset foo=bar */
+#define PF_ASSIGN	0x02	/* argument handled like the RHS of foo=bar */
+#define PF_SINGLE	0x04	/* single word substitution */
+
 /* node for named directory hash table (nameddirtab) */
 
 struct nameddir {
diff --git a/config.guess b/config.guess
index 206b98874..0d66b2e56 100755
--- a/config.guess
+++ b/config.guess
@@ -526,6 +526,9 @@ EOF
     RM*:SINIX-*:*:*)
 	echo mips-sni-sysv4
 	exit 0 ;;
+    RM*:ReliantUNIX-*:*:*)
+	echo mips-sni-sysv4
+	exit 0 ;;
     *:SINIX-*:*:*)
 	if uname -p 2>/dev/null >/dev/null ; then
 		UNAME_MACHINE=`(uname -p) 2>/dev/null`
diff --git a/configure.in b/configure.in
index dd6ce1e69..e41bf47d6 100644
--- a/configure.in
+++ b/configure.in
@@ -1036,7 +1036,7 @@ char *argv[];
       #
       sni_cc_version=`$CC -V 2>&1 | head -1`
       case "$sni_cc_version" in
-        CDS* )
+        *CDS* )
          EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-Wl,-Blargedynsym}"
        ;;
        * )
diff --git a/patchlist.txt b/patchlist.txt
index 05fc91e98..9ec40d8dd 100644
--- a/patchlist.txt
+++ b/patchlist.txt
@@ -445,3 +445,61 @@ new-style completions: not posted
 Me: 5281 now works, 5364
 
 Me: make dependencies for main.o, Makemod, zshpaths.h, 5365
+
+  pws-9
+
+Bart: CVS should ignore version.h, 5367
+
+Oliver Kiddle: another change of mailing list host, 5372
+
+Oliver: compctl -T documentation for ~dirs, 5374
+
+Andrej: Reliant UNIX configuration, 5377
+
+Sven: Manual for new completion (so far), 5384, 5397
+
+Me: dump new completion status for fast initialisation, 5393
+
+Me: bug fixlet for __path_files, 5398
+
+Sven: overhaul of do_ambiguous with some bug-fixage, 5399, 5407
+
+Sven: print needs - in Functions/Completion/dump, 5400; auto-dump and use
+$COMPDUMP file, 5402
+
+Sven: files -> __files, 5401
+
+Me: magicequalsubst now affects all arguments ...=~...:~..., 5403
+
+Me: set -x output for [[ ... ]], 5408
+
+Me: IRIX 6.5 problems in Etc/MACHINES (see 5410 from Helmut Jarausch).
+
+Sven: 5412: better matcher control.
+
+Sven: 5415: anchors in matchers shouldn't match variable part of completion
+
+Sven: 5417: multiple subscripts with undefined array
+
+Sven: 5418: small addmatches fixes
+
+Me: 5421: setting same element of assoc array in full array assignment crashed
+
+Sven: 5422: braces in completions were not tokenized; array parameters were
+used uncopied
+
+Sven: 5423: compadd accepts either - or -- to end options
+
+Sven: 5424: addmatches fix when not doing matching
+
+Me: 5425: fix pattern matching for new completion
+
+Sven: 5429: $CONTEXT strings
+
+Sven: 5430: rewrite Functions/Completions with simplified syntax (no #array
+type completions).
+
+Me: 5436: set -x for function calls and ((...)).
+
+Me: unposted (but see 5440): zftp changes: more return 6's, functions now
+do auto-open and avoid subshells.