about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Config/defs.mk2
-rw-r--r--Doc/Zsh/compctl.yo250
-rw-r--r--Doc/Zsh/expn.yo40
-rw-r--r--Doc/Zsh/mod_zle.yo18
-rwxr-xr-xMisc/globtests9
-rwxr-xr-xMisc/globtests.ksh11
-rwxr-xr-xMisc/lete2ctl49
-rw-r--r--Src/.lastloc5
-rw-r--r--Src/Makefile.in12
-rw-r--r--Src/Modules/stat.c1
-rw-r--r--Src/Zle/.lastloc10
-rw-r--r--Src/Zle/comp.h137
-rw-r--r--Src/Zle/comp1.c174
-rw-r--r--Src/Zle/comp1.export22
-rw-r--r--Src/Zle/comp1.mdd2
-rw-r--r--Src/Zle/compctl.c664
-rw-r--r--Src/Zle/deltochar.c15
-rw-r--r--Src/Zle/iwidgets.list16
-rw-r--r--Src/Zle/zle.export10
-rw-r--r--Src/Zle/zle.h11
-rw-r--r--Src/Zle/zle.mdd2
-rw-r--r--Src/Zle/zle_hist.c83
-rw-r--r--Src/Zle/zle_main.c51
-rw-r--r--Src/Zle/zle_misc.c6
-rw-r--r--Src/Zle/zle_refresh.c14
-rw-r--r--Src/Zle/zle_thingy.c56
-rw-r--r--Src/Zle/zle_tricky.c4081
-rw-r--r--Src/Zle/zle_utils.c2
-rw-r--r--Src/Zle/zle_vi.c10
-rw-r--r--Src/builtin.c4
-rw-r--r--Src/exec.c4
-rw-r--r--Src/glob.c169
-rw-r--r--Src/jobs.c2
-rw-r--r--Src/mkmakemod.sh15
-rw-r--r--Src/modentry.c15
-rw-r--r--Src/module.c136
-rw-r--r--Src/system.h6
-rw-r--r--Src/utils.c16
-rw-r--r--Src/zsh.export235
-rw-r--r--Src/zsh.h2
-rw-r--r--Src/zsh.mdd2
-rw-r--r--acconfig.h3
-rw-r--r--aczsh.m41
-rw-r--r--configure.in81
44 files changed, 4998 insertions, 1456 deletions
diff --git a/Config/defs.mk b/Config/defs.mk
index 42cad5740..e0294f0de 100644
--- a/Config/defs.mk
+++ b/Config/defs.mk
@@ -56,6 +56,8 @@ EXELDFLAGS      = @EXELDFLAGS@
 LIBS            = @LIBS@
 DL_EXT          = @DL_EXT@
 DLLD            = @DLLD@
+EXPOPT          = @EXPOPT@
+IMPOPT          = @IMPOPT@
 
 # utilities
 AWK             = @AWK@
diff --git a/Doc/Zsh/compctl.yo b/Doc/Zsh/compctl.yo
index a78a18fa9..721d415a6 100644
--- a/Doc/Zsh/compctl.yo
+++ b/Doc/Zsh/compctl.yo
@@ -10,6 +10,7 @@ list(tt(compctl) [ tt(-CDT) ] var(options) \
   [ tt(-x) var(pattern) var(options) tt(-) ... tt(--) ] \
   [ tt(PLUS()) var(options) [ tt(-x) ... tt(--) ] ... [tt(PLUS())] ] \
   [ var(command) ... ])
+list(tt(compctl) tt(-M) var(match-specs) ...)
 list(tt(compctl) tt(-L) [ tt(-CDT) ] [ var(command) ... ])
 list(tt(compctl) tt(PLUS()) var(command) ...)
 endlist()
@@ -28,6 +29,7 @@ menu(Command Flags)
 menu(Option Flags)
 menu(Alternative Completion)
 menu(Extended Completion)
+menu(Matching Control)
 menu(Example)
 endmenu()
 texinode(Command Flags)(Option Flags)()(Programmable Completion)
@@ -44,7 +46,19 @@ item(var(command) ...)(
 controls completion for the named commands, which must be listed last
 on the command line.  If completion is attempted for a command with a
 pathname containing slashes and no completion definition is found, the
-search is retried with the last pathname component.  Note that aliases
+search is retried with the last pathname component. If the command starts
+with a tt(=), completion is tried with the pathname of the command.
+
+The strings may also be patterns (i.e. they may contain an unquoted
+occurrence of characters used to form patterns in the shell). When 
+completion is attempted, the shell first tries all such pattern compctls.
+If one matches the command name on the line or if the pathname of the
+command on the line matches a pattern, it is used. The patterns are tested
+in reverse order, i.e. the pattern compctl defined last overrides all
+previously defined pattern compctls. Unless the option list of that compctl
+contains an tt(-t) flag with a \tt(c) character, no more compctls are tried.
+
+Note that aliases
 are expanded before the command name is determined unless the
 tt(COMPLETE_ALIASES) option is set.  Commands should not be combined
 with the tt(-C), tt(-D) or tt(-T) flags.
@@ -63,7 +77,7 @@ been issued, filenames are completed.
 item(tt(-T))(
 supplies completion flags to be used before any other processing is
 done, even those given to specific commands with other compctl
-definitions.  This is only useful when combined with extended
+definitions.  This is especially useful when combined with extended
 completion (the tt(-x) flag, see noderef(Extended Completion) below).
 Using this flag you can define default behavior
 which will apply to all commands without exception, or you can alter
@@ -95,6 +109,9 @@ If the tt(PLUS()) flag is alone and followed immediately by the var(command)
 list, the completion behavior for all the commands in the list is reset to
 the default.  In other words, completion will subsequently use the
 options specified by the tt(-D) flag.
+
+The form with tt(-M) as the first and only option defines global
+matching specifications described below in noderef(Matching Control).
 texinode(Option Flags)(Alternative Completion)(Command Flags)(Programmable Completion)
 sect(Option Flags)
 startlist()
@@ -106,6 +123,8 @@ list([ tt(-Q) ] [ tt(-P) var(prefix) ] [ tt(-S) var(suffix) ])
 list([ tt(-W) var(file-prefix) ])
 list([ tt(-q) ] [ tt(-X) var(explanation) ] [ tt(-Y) var(explanation) ])
 list([ tt(-y) var(func-or-var) ] [ tt(-l) var(cmd) ] [ tt(-U) ])
+list([ tt(-t) var(continue) ] [ tt(-J) var(name) ] [ tt(-V) var(name) ])
+list([ tt(-M) var(match-spec) ])
 endlist()
 
 The remaining var(options) specify the type of command arguments
@@ -256,7 +275,7 @@ passed two arguments: the prefix and the suffix of the word on which
 completion is to be attempted, in other words those characters before
 the cursor position, and those from the cursor position onwards.  The
 whole command line can be accessed with the tt(-c) and tt(-l) flags
-of the tt(read) builtin.  The
+of the tt(read) builtin. The
 function should set the variable tt(reply) to an array containing
 the completions (one completion per element); note that tt(reply)
 should not be made local to the function.  From such a function the
@@ -322,6 +341,9 @@ nofill(tt(compctl -/ -W ~/Mail maildirs))
 
 completes any subdirectories to any depth beneath the directory
 tt(~/Mail), although that prefix does not appear on the command line.
+The var(suffix) may also be of the form accepted by the tt(-k) flag, i.e.
+the name of an array or a literal list in parenthesis. In this cases all
+words are used as prefixes.
 )
 item(tt(-q))(
 If used with a suffix as specified by the tt(-S) option, this
@@ -331,7 +353,7 @@ tt(AUTO_REMOVE_SLASH) option).  The option is most useful for list
 separators (comma, colon, etc.).
 )
 item(tt(-l) var(cmd))(
-This option cannot be combined with any other.  It restricts the range
+This option restricts the range
 of command line words that are considered to be arguments.  If
 combined with one of the extended completion patterns `tt(p[)...tt(])',
 `tt(r[)...tt(])', or `tt(R[)...tt(])'  (see noderef(Extended Completion)
@@ -355,7 +377,10 @@ will be deleted.  This is most useful with a function (given by the
 tt(-K) option) which can examine the word components passed to it
 (or via the tt(read) builtin's tt(-c) and tt(-l) flags) and
 use its own criteria to decide what matches.  If there is no
-completion, the original word is retained.
+completion, the original word is retained.  Since the produced 
+possible completions seldom seldom have interesting common prefixes
+and suffixes, menucompletion is started immediatly if tt(AUTO_MENU) is
+set and this flag is used.
 )
 item(tt(-y) var(func-or-var))(
 The list provided by var(func-or-var) is displayed instead of the list
@@ -382,6 +407,8 @@ Print var(explanation) when trying completion on the current set of
 options. A `tt(%n)' in this string is replaced by the number of matches.
 The explanation only appears if completion was tried and there was
 no unique match, or when listing completions.
+The sequences tt(%B), tt(%b), tt(%S), tt(%s), tt(%U), and tt(%u) specify
+output attributes (bold, standout, and underline) as in prompts.
 )
 item(tt(-Y) var(explanation))(
 Identical to tt(-X), except that the var(explanation) first undergoes
@@ -389,6 +416,37 @@ expansion following the usual rules for strings in double quotes.
 The expansion will be carried out after any functions are called for
 the tt(-K) or tt(-y) options, allowing them to set variables.
 )
+item(tt(-J))(
+This gives the name of the group the matches should be placed in. Groups
+are listed and sorted separately. Also, menucompletion will offer the matches
+in the groups in the order, in which the groups were defined. If no group
+name is explicitly given, the matches are stored in a group named var(default).
+The first time a group name is encountered, a group with that name is created.
+After that all matches with the same group name are stored in that group.
+)
+item(tt(-V))(
+Like tt(-J), but the matches in the group will not be sorted in the listing and
+with menucompletion. These unsorted groups are in a different name space than
+the sorted ones. I.e. it is possible to have a sorted and a unsorted group
+with the same name and the matches in those groups will not be mixed.
+)
+item(tt(-t) var(continue))(
+The var(continue)-string contains a set of characters that specify if 
+and when completion should continue to produce matches where it normally
+would not do that. The character tt(c) means that completion continues
+with the next suitable compctl (i.e. if you don't specify this in a
+tt(compctl -T), compctls for commands are never used). The character
+tt(PLUS()) is used to continue with the matches for the next alternative
+completion (see below). The characters tt(-) and tt(x) may be used in
+sub-lists for extended completion (see below). They will make the completion
+code use the flag list after the next tt(-) (if the corresponding pattern
+matches) and the default flag list (those before the tt(-x)), respectively.
+)
+item(tt(-M) var(match-spec))(
+This defines additional matching control specifications that should be used
+only when testing words for the list of flags this flag appears in. The format
+of the var(match-spec) string is described below in noderef(Matching Control).
+)
 enditem()
 texinode(Alternative Completion)(Extended Completion)(Option Flags)(Programmable Completion)
 sect(Alternative Completion)
@@ -402,7 +460,9 @@ tried with the options before the first `tt(PLUS())'. If this produces no
 matches completion is tried with the flags after the `tt(PLUS())' and so on. If
 there are no flags after the last `tt(PLUS())' and a match has not been found
 up to that point, default completion is tried.
-texinode(Extended Completion)(Example)(Alternative Completion)(Programmable Completion)
+If the list of flags contains a tt(-t) with a tt(PLUS()) character, the next
+list of flags is used even if the current list produced matches.
+texinode(Extended Completion)(Matching Control)(Alternative Completion)(Programmable Completion)
 sect(Extended Completion)
 startlist()
 list(tt(compctl) [ tt(-CDT) ] var(options) \
@@ -498,13 +558,187 @@ var(max) inclusive.
 item(tt(r[)var(str1)tt(,)var(str2)tt(])...)(
 Matches if the cursor is after a word with prefix var(str1).  If there
 is also a word with prefix var(str2) on the command line it matches
-only if the cursor is before this word.
+only if the cursor is before this word. If the comma and var(str2) are
+omitted, it matches if the cursor is after a word with prefix var(str1).
 )
 item(tt(R[)var(str1)tt(,)var(str2)tt(])...)(
 Like tt(r) but using pattern matching instead.
 )
 enditem()
-texinode(Example)()(Extended Completion)(Programmable Completion)
+texinode(Matching Control)(Example)(Extended Completion)(Programmable Completion)
+sect(Matching Control)
+
+Matching specifications are used to describe that certain strings
+on the command line match possibly different strings in the words produced
+by the completion code.
+
+Matching specification strings consist of one or more matching
+descriptions separated by whitespace. Each description consists of
+a letter followed by a colon and the patterns describing which character
+sequences on the line match which character sequences in the words.
+
+The letters understood are: tt(l), tt(r), tt(m), tt(L), tt(R), and tt(M).
+startitem()
+item(tt(m) and tt(M))(
+These describe patterns that match anywhere in the words. The colon should
+be followed by two patterns separated by an equal sign. The pattern on the
+left side describes the substrings that are to be matched on the command line,
+the pattern on the right side describes the substrings matched in the word.
+)
+item(tt(l) and tt(L))(
+These letters are for patterns that are anchored by another pattern on
+the left side. In this case the colon has to be followed by the pattern
+for the anchor, a pipe symbol, the pattern for the command line, an equal
+sign, and the pattern for the word. Patterns anchored on the left side match
+only if the anchor-pattern matches directly before the line pattern and if
+the string in the word before the word pattern matches the string before
+the line pattern in the line string.
+)
+item(tt(r) and tt(R))(
+Like tt(l) and tt(L) with the difference that the line and word patterns
+are anchored on the right side. Also, here the pattern for the anchor has
+to come after the pattern for the line, again separated by a pipe symbol.
+)
+enditem()
+
+Each pattern is either an empty string or consists of a sequence of
+character (possibly quoted), question marks, character classes, and
+correspondence classes. Normal characters match only themselves, question
+marks match any character, and character classes are formed as for
+globbing and match the same characters as there.
+Correspondence classes are formed like character classes with two
+differences: they are delimited by a pair of braces and negated
+classes are not allowed (i.e. the characters tt(!) and tt(^) have no
+special meaning directly after the opening brace).
+
+Correspondence classes are used to conveniently describe that several
+characters on the line match several other characters in the word. For 
+example, if you want to define the any lowercase letter on the line
+matches the corresponding uppercase letter in the word all you need to 
+write down is: `tt(m:{a-z}={A-Z})'. More than one correspondence class
+may be given on either side of the equal sign, in this case the first
+class on the left says which character matches for the first class on
+the right, the second class on either side work together, and so on.
+If one side has more such classes than the other side, the superfluous
+classes behave like normal character classes. In anchor patterns
+correspondence classes always behave like normal character classes.
+
+The word pattern may also be a single star (tt(*)). This means that
+the line pattern matches any number of characters in the word. In this 
+case the pattern has to be anchored (on any side) and the line pattern 
+matches all characters in the word up to a character sequence that
+matches the anchor.
+
+For anchors the empty string as a pattern has a special meaning. Such
+empty anchors match only the beginning (in the case of an left side
+anchor) or end (for right side anchors) of the command line string or
+word.
+
+The distinction between the lowercase and the uppercase forms of the
+specification characters is used to define which matched substring
+should be put in the match and the generated command line. The
+lowercase forms use the substring from the word, so this should be
+used if the exact words produced by the completion code need to be
+used. The uppercase forms use the substring from the command line and
+should be used if the typed string need to be retained.
+
+Examples:
+
+startitem()
+The option tt(-o) produces option names in all-lowercase form, without 
+underscores, and without the optional tt(no) at the beginning even
+though the buitlins tt(setopt) and tt(unsetopt) understand opotion
+names with uppercase letters, underscores, and the optional tt(no).
+So we want to be able to say, that in this case an prefix tt(no) and
+any underscore may be ignored when trying to match the produced words, 
+and that uppercase letters on the line match the corresponding
+lowercase letters in the words. This can be done with:
+
+indent(
+tt(compctl -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' -o setopt unsetopt)
+)
+
+The first part says that the pattern `tt([nN][oO])' at the beginning
+(note the empty anchor before the pipe symbol) of the string on the
+line matches the the empty string in the produced words, i.e. it need
+not be there. The second part says that an underscore anywhere on the
+line need not be present in the word, and the third part uses
+correspondence classes as in the example above to say that any
+uppercase letter on the line matches the corresponding lowercase
+letter in the word. The use of the uppercase forms of the
+specification characters (tt(L) and tt(M)) guarantees that the special 
+wrinting on the command line (and especially the option tt(no)) will
+not be erased.
+
+As a second example we will make completion case insensitive. For this 
+we use the form of tt(compctl) that defines matching specification that
+are to be used everywhere, i.e. a tt(compctl) with tt(-M) as the only
+option given.
+
+The pattern needed was already explained above, this gives us:
+
+indent(
+tt(compctl -M 'm:{a-z}={A-Z}')
+)
+
+This makes lowercase letters match their uppercase counterparts. If we 
+want to make uppercase letters match the lowercase forms, we would
+have to use:
+
+indent(
+tt(compctl -M 'm:{a-z}={A-Z} m:{A-Z}={a-z}')
+)
+
+A nice example for the use of tt(*) patterns is partial word
+completion. Sometimes you would like to make strings like tt(c.s.u)
+complete to strings like tt(comp.source.unix), i.e. you consider the
+word to consist of multiple parts (separated by the dot in the
+example) and each part should be completed separately. Defining such
+forms of matching is simple, for example if we want to separately
+complete word parts separated by dots, commas, underscores, and
+hyphens, we can do this by saying:
+
+indent(
+tt(compctl -M 'r:|[.,_-]=* r:|=*')
+)
+
+The first specification says that an empty string on the line before
+one of our special characters matches any number of characters in the
+word which has the effect we wanted. The second specification is
+needed to make this work when the cursor is in the middle of the word
+and the option tt(COMPLETE_IN_WORD) is set. In this case the
+completion code would normally try to match word that end with the
+string that is already on the command line, but in our example we
+would like the code to match words even if they contain extra
+characters after the string on the line. Hence we say that the empty
+string at the end of the string on the line matches any characters at
+the end of the word.
+
+The form of tt(compctl) that defines the global matching
+specifications is a bit more powerful than described until now. It
+accepts not only one specification strin, but any number of them. When 
+completion is attempted, the code first uses the definitions from the
+first string. If no words could be matched with these specifications,
+it tries the whole thing again with the specifications from the second 
+string, and so on. This allows one to define simple and fast matches
+to be used first, more powerful matchers as a second choice, and so on.
+
+As an example we would like to make the code match words that contain
+the string on the line as a substring (anywhere, not just at the
+beginning). But since this could produce more matches than we want,
+this should be tried only if the matchers described above don't
+produce any matches. E.g.:
+
+indent(
+tt(compctl -M 'r:|[.,_-]=* r:|=*' 'l:|=* r:|=*')
+)
+
+If using the first specification string does not produce matches, the
+second one is tried. The two descriptions it this string say that the
+empty string at the beginning and end of the string on the line
+matches any characters at the beginning or end of the word.
+enditem()
+texinode(Example)()(Matching Control)(Programmable Completion)
 sect(Example)
 nofill(tt(compctl -u -x 's[tt(PLUS())] c[-1,-f],s[-f+PLUS()]' -g '~/Mail/*(:t)' \ 
     - 's[-f],c[-1,-f]' -f -- mail))
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index 99844b42a..f201e4751 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -777,6 +777,9 @@ Matches the enclosed pattern.  This is used for grouping.
 If the tt(KSH_GLOB) option is set, then a
 `tt(@)', `tt(*)', `tt(+)', `tt(?)' or `tt(!)' immediately preceding
 the `tt(LPAR())' is treated specially, as detailed below.
+Note that grouping cannot currently extend over multiple directories:
+a `tt(/)' separating a directory terminates processing of the current
+group; processing resumes after the end of the group.
 )
 item(var(x)tt(|)var(y))(
 Matches either var(x) or var(y).
@@ -840,6 +843,43 @@ Match anything but the expression in parentheses.
 (Like `tt(LPAR()^LPAR())...tt(RPAR()RPAR())'.)
 )
 enditem()
+subsect(Globbing Flags)
+There are various flags which affect any text to their right up to the
+end of the enclosing group or to the end of the pattern; they require
+the tt(EXTENDED_GLOB) option. All take the form
+tt(LPAR()#)var(X)tt(RPAR()) where var(X) may be one of the following
+characters:
+
+startitem()
+item(i)(
+Case insensitive:  upper or lower case characters in the pattern match
+upper or lower case characters.
+)
+item(l)(
+Lower case characters in the pattern match upper or lower case
+characters; upper case characters in the pattern still only match
+upper case characters.
+)
+item(I)(
+Case sensitive:  locally negates the effect of tt(i) or tt(l) from
+that point on.
+)
+enditem()
+
+For example, the test string tt(fooxx) can be matched by the pattern
+tt(LPAR()#i)tt(RPAR()FOOXX), but not by tt(LPAR()#l)tt(RPAR()FOOXX),
+tt(LPAR()#i)tt(RPAR()FOO)tt(LPAR()#I)tt(RPAR()XX) or
+tt(LPAR()LPAR()#i)tt(RPAR()FOOX)tt(RPAR()X).
+
+When using the ksh syntax for grouping both tt(KSH_GLOB) and
+tt(EXTENDED_GLOB) must be set and the left parenthesis should be
+preceded by tt(@).  Note also that the flags do not affect letters
+inside tt([...]) groups, in other words tt(LPAR()#i)tt(RPAR()[a-z])
+still matches only lowercase letters.  Finally, note that when
+examining whole paths case-insensitively every directory must be
+searched for all files which match, so that a pattern of the form
+tt(LPAR()#i)tt(RPAR()/foo/bar/...) is potentially slow.
+
 subsect(Recursive Globbing)
 A pathname component of the form `tt(LPAR())var(foo)tt(/RPAR()#)'
 matches a path consisting of zero or more directories
diff --git a/Doc/Zsh/mod_zle.yo b/Doc/Zsh/mod_zle.yo
index 9aa03f7f8..b7c6a934f 100644
--- a/Doc/Zsh/mod_zle.yo
+++ b/Doc/Zsh/mod_zle.yo
@@ -157,6 +157,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) ])
 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:
@@ -184,6 +185,23 @@ 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(var(widget))(
 Invoke the specified widget.  This can only be done when ZLE is
 active; normally this will be within a user-defined widget.
diff --git a/Misc/globtests b/Misc/globtests
index 728aee5ae..b7d5fd0bd 100755
--- a/Misc/globtests
+++ b/Misc/globtests
@@ -103,5 +103,14 @@ t :]            [:]]#
 t [             [[]
 t ]             []]
 t []            [^]]]
+t fooxx         (#i)FOOXX
+f fooxx         (#l)FOOXX
+t FOOXX         (#l)fooxx
+f fooxx         (#i)FOO(#I)X(#i)X
+t fooXx         (#i)FOO(#I)X(#i)X
+t fooxx         ((#i)FOOX)x
+f fooxx         ((#i)FOOX)X
+f BAR           (bar|(#i)foo)
+t FOO           (bar|(#i)foo)
 EOT
 print "$failed tests failed."
diff --git a/Misc/globtests.ksh b/Misc/globtests.ksh
index b03cc488e..da5145845 100755
--- a/Misc/globtests.ksh
+++ b/Misc/globtests.ksh
@@ -1,6 +1,6 @@
 #!/usr/local/bin/zsh -f
 
-setopt kshglob
+setopt kshglob extendedglob
 
 failed=0
 while read res str pat; do
@@ -87,5 +87,14 @@ t zoox          @(!(z*)|*x)
 t foo           *(!(foo))
 f foob          !(foo)b*
 t foobb         !(foo)b*
+t fooxx         (#i)FOOXX
+f fooxx         (#l)FOOXX
+t FOOXX         (#l)fooxx
+f fooxx         (#i)FOO@(#I)X@(#i)X
+t fooXx         (#i)FOO@(#I)X@(#i)X
+t fooxx         @((#i)FOOX)x
+f fooxx         @((#i)FOOX)X
+f BAR           @(bar|(#i)foo)
+t FOO           @(bar|(#i)foo)
 EOT
 print "$failed tests failed."
diff --git a/Misc/lete2ctl b/Misc/lete2ctl
index ca00b8aee..273ea4674 100755
--- a/Misc/lete2ctl
+++ b/Misc/lete2ctl
@@ -9,6 +9,7 @@
 # Runs as a filter.  Should ignore anything which isn't a "complete".
 # It expects each "complete" statement to be the first thing on a line.
 # All the examples in the tcsh manual give sensible results.
+# Author:  Peter Stephenson <pws@ibmth.df.unipi.it>
 #
 # Option:
 # -x (exact): only applies in the case of command disambiguation (is
@@ -38,6 +39,11 @@
 # (5) Make sure all command names with wildcards are processed together --
 #     they need to be lumped into one "compctl -C" or "compctl -D"
 #     statement for zsh.
+# (6) Group completion (complete's g flag) is not built into zsh, so
+#     you need perl to be available to generate the groups.  If this
+#     script is useful, I assume that's not a problem.
+# (7) I don't know what `completing completions' means, so the X
+#     flag to complete is not handled.
 
 # Handle options
 if (@ARGV) {
@@ -113,6 +119,13 @@ sub gettype {
 # Nothing (n) can be handled by returning nothing.  (C.f. King Lear, I.i.)
     if ($c =~ /[abcjuv]/) {
 	$ret = "-$c";
+    } elsif ($c eq 'C') {
+	if (defined($glob)) {
+	    $ret = "-W $glob -/g '*(.*)'";
+	    undef($glob);
+	} else {
+	    $ret = '-c';
+	}
     } elsif ($c eq 'S') {
 	$ret = '-k signals';
     } elsif ($c eq 'd') {
@@ -121,18 +134,42 @@ sub gettype {
 	} else {
 	    $ret = '-/';
 	}
+    } elsif ($c eq 'D') {
+	if (defined($glob)) {
+	    $ret = "-W $glob -/";
+	    undef($glob);
+	} else {
+	    $ret = '-/';
+	}
     } elsif ($c eq 'e') {
 	$ret = '-E';
     } elsif ($c eq 'f' && !$glob) {
 	$ret = '-f';
+    } elsif ($c eq 'F') {
+	if (defined($glob)) {
+	    $ret = "-W $glob -f";
+	    undef($glob);
+	} else {
+	    $ret = '-f';
+	}
+    } elsif ($c eq 'g') {
+	$ret = "-s '\$(perl -e '\\''while ((\$name) = getgrent)\n" .
+	    "{ print \$name, \"\\n\"; }'\\'')'";
     } elsif ($c eq 'l') {
 	$ret = q!-k "(`limit | awk '{print $1}'`)"!;
     } elsif ($c eq 'p') {
-	$ret = "-W $glob -f", undef($glob) if defined($glob);
+        $ret = "-W $glob -f", undef($glob) if defined($glob);
     } elsif ($c eq 's') {
-	$ret = '-p';
+        $ret = '-p';
     } elsif ($c eq 't') {
 	$qual = '.';
+    } elsif ($c eq 'T') {
+        if (defined($glob)) {
+            $ret = "-W $glob -g '*(.)'";
+            undef($glob);
+        } else {
+            $ret = "-g '*(.)'";
+        }
     } elsif ($c eq 'x') {
 	$glob =~ s/'/'\\''/g;
 	$ret = "-X '$glob'";
@@ -190,7 +227,7 @@ $" = " - ";
 
 while (<>) {
     if (/^\s*complete\s/) {
-	undef(@stuff); 
+	undef(@stuff);
 	$default = '';
 	$_ = $';
 	while (/\\$/) {
@@ -211,7 +248,7 @@ while (<>) {
 	    # Loop over remaining arguments to "complete".
 	    $sep = substr($word,1,1);
 	    $sep =~ s/(\W)/\\$1/g;
-	    @split = split(/$sep/,$word);
+	    @split = split(/$sep/,$word,4);
 	    for ($i = 0; $i < 3; $i++) {
 		while ($split[$i] =~ /\\$/) {
 		    substr($split[$i],-1,1) = "";
@@ -225,7 +262,9 @@ while (<>) {
 		 # The "complete" catch-all:  treat this as compctl\'s
 		 # default (requiring no pattern matching).
 		$default .= &gettype($type) . ' ';
-		defined($suffix) && ($defsuf .= $suffix);
+		defined($suffix) &&
+		    (defined($defsuf) ? ($defsuf .= $suffix)
+		     : ($defsuf = $suffix));
 	    } else {
 		$pat = &getpat($pat,$arg);
 		$type = &gettype($type);
diff --git a/Src/.lastloc b/Src/.lastloc
new file mode 100644
index 000000000..b3c6cf0f3
--- /dev/null
+++ b/Src/.lastloc
@@ -0,0 +1,5 @@
+(("/home/user2/pws/src/zsh-3.1.5/Src/glob.c" . 15475)
+ ("/home/user2/pws/src/zsh-3.1.5/Src/zsh.export" . 794)
+ ("/home/user2/pws/src/zsh-3.1.5/Src/utils.c" . 10946)
+ ("/home/user2/pws/src/zsh-3.1.5/Src/Makefile" . 4282)
+ ("/home/user2/pws/src/zsh-3.1.5/Src/mkmakemod.sh" . 6832))
diff --git a/Src/Makefile.in b/Src/Makefile.in
index 453159e17..269ed7c1b 100644
--- a/Src/Makefile.in
+++ b/Src/Makefile.in
@@ -55,12 +55,17 @@ NLIST = `cat stamp-modobjs`
 LIBZSH = libzsh-$(VERSION).$(DL_EXT)
 NIBZSH =
 
+ZSH_EXPORT = $(EXPOPT)$(sdir)/zsh.export
+ZSH_NXPORT =
+ENTRYOBJ   = modentry..o
+NNTRYOBJ   =
+
 LDRUNPATH = LD_RUN_PATH=$(libdir)/zsh
 NDRUNPATH =
 
 zsh: $(@L@IBZSH) $(@L@STMP) $(MAIN_OBJS)
 	rm -f $@
-	$(@L@DRUNPATH) $(LINK) $(MAIN_OBJS) $(@L@LIST) $(@L@IBZSH) $(LIBS)
+	$(@L@DRUNPATH) $(LINK) $(MAIN_OBJS) $(@L@LIST) $(ZSH_@E@XPORT) $(@L@IBZSH) $(LIBS)
 
 $(LIBZSH): $(LIBOBJS) $(NSTMP)
 	rm -f $@
@@ -114,6 +119,11 @@ modules-bltin:
 	    echo > $@; \
 	fi
 
+modules: $(@E@NTRYOBJ)
+
+$(ENTRYOBJ): FORCE
+	@$(MAKE) -f Makemod $(MAKEDEFS) $@
+
 # ========== ANSI TO K&R CONVERSION ==========
 
 ANSI_KNR = ansi2knr
diff --git a/Src/Modules/stat.c b/Src/Modules/stat.c
index 09245b52f..64664abaf 100644
--- a/Src/Modules/stat.c
+++ b/Src/Modules/stat.c
@@ -85,6 +85,7 @@ statmodeprint(mode_t mode, char *outbuf, int flags)
 
 	for (i = 1; i <= 9; i++)
 	    pm[i] = (mode & *mfp++) ? modes[i] : '-';
+	pm[10] = '\0';
 
 	if (mode & S_ISUID)
 	    pm[3] = (mode & S_IXUSR) ? 's' : 'S';
diff --git a/Src/Zle/.lastloc b/Src/Zle/.lastloc
new file mode 100644
index 000000000..a060de394
--- /dev/null
+++ b/Src/Zle/.lastloc
@@ -0,0 +1,10 @@
+(("/home/user2/pws/src/zsh-3.1.5/Src/Zle/zle.h" . 2619)
+ ("/home/user2/pws/src/zsh-3.1.5/Src/Zle/zle.export" . 35)
+ ("/home/user2/pws/src/zsh-3.1.5/Src/Zle/zle_main.c" . 13806)
+ ("/home/user2/pws/src/zsh-3.1.5/Src/Zle/iwidgets.list" . 7831)
+ ("/home/user2/pws/src/zsh-3.1.5/Src/Zle/comp.h" . 8512)
+ ("/home/user2/pws/src/zsh-3.1.5/Src/Zle/zle_tricky.c" . 72920)
+ ("/home/user2/pws/src/zsh-3.1.5/Src/Zle/comp1.c" . 9640)
+ ("/home/user2/pws/src/zsh-3.1.5/Src/Zle/zle_vi.c" . 16035)
+ ("/home/user2/pws/src/zsh-3.1.5/Src/Zle/zle_utils.c" . 8406)
+ ("/home/user2/pws/src/zsh-3.1.5/Src/Zle/comp1.export" . 81))
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index 5d7ef74e2..84f8c3c98 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -32,6 +32,11 @@
 typedef struct compctlp  *Compctlp;
 typedef struct compctl   *Compctl;
 typedef struct compcond  *Compcond;
+typedef struct patcomp   *Patcomp;
+typedef struct cmatcher  *Cmatcher;
+typedef struct cmlist    *Cmlist;
+typedef struct cpattern  *Cpattern;
+typedef struct cline     *Cline;
 
 /* node for compctl hash table (compctltab) */
 
@@ -42,6 +47,14 @@ struct compctlp {
     Compctl cc;			/* pointer to the compctl desc.     */
 };
 
+/* for the list of pattern compctls */
+
+struct patcomp {
+    Patcomp next;
+    char *pat;
+    Compctl cc;
+};
+
 /* compctl -x condition */
 
 struct compcond {
@@ -85,7 +98,7 @@ struct compcond {
 struct compctl {
     int refc;			/* reference count                         */
     Compctl next;		/* next compctl for -x                     */
-    unsigned long mask;		/* mask of things to complete (CC_*)       */
+    unsigned long mask, mask2;		/* mask of things to complete (CC_*)       */
     char *keyvar;		/* for -k (variable)                       */
     char *glob;			/* for -g (globbing)                       */
     char *str;			/* for -s (expansion)                      */
@@ -97,12 +110,15 @@ struct compctl {
     char *withd;		/* for -w (with directory                  */
     char *hpat;			/* for -H (history pattern)                */
     int hnum;			/* for -H (number of events to search)     */
+    char *gname;
     Compctl ext;		/* for -x (first of the compctls after -x) */
     Compcond cond;		/* for -x (condition for this compctl)     */
     Compctl xor;		/* for + (next of the xor'ed compctls)     */
+    Cmatcher matcher;		/* matcher control (-M) */
+    char *mstr;			/* matcher string */
 };
 
-/* objects to complete */
+/* objects to complete (mask) */
 #define CC_FILES	(1<<0)
 #define CC_COMMPATH	(1<<1)
 #define CC_REMOVE	(1<<2)
@@ -135,3 +151,120 @@ struct compctl {
 
 #define CC_EXPANDEXPL	(1<<30)
 #define CC_RESERVED	(1<<31)
+
+/* objects to complete (mask2) */
+#define CC_NOSORT	(1<<0)
+#define CC_XORCONT	(1<<1)
+#define CC_CCCONT	(1<<2)
+#define CC_PATCONT	(1<<3)
+#define CC_DEFCONT	(1<<4)
+
+typedef struct cexpl *Cexpl;
+typedef struct cmgroup *Cmgroup;
+typedef struct cmatch *Cmatch;
+
+/* This is for explantion strings. */
+
+struct cexpl {
+    char *str;			/* the string */
+    int count;			/* the number of matches */
+    int fcount;			/* number of matches with fignore ignored */
+
+};
+
+/* This describes a group of matches. */
+
+struct cmgroup {
+    char *name;			/* the name of this group */
+    Cmgroup prev;		/* previous on the list */
+    Cmgroup next;		/* next one in list */
+    int flags;			/* see CGF_* below */
+    int mcount;			/* number of matches */
+    Cmatch *matches;		/* the matches */
+    int lcount;			/* number of things to list here */
+    char **ylist;		/* things to list */
+    int ecount;			/* number of explanation string */
+    Cexpl *expls;		/* explanation strings */
+    int ccount;			/* number of compctls used */
+    Compctl *ccs;		/* the compctls used */
+    LinkList lexpls;		/* list of explanation string while building */
+    LinkList lmatches;		/* list of matches */
+    LinkList lfmatches;		/* list of matches without fignore */
+    LinkList lallccs;		/* list of used compctls */
+};
+
+
+#define CGF_NOSORT  1		/* don't sort this group */
+#define CGF_LINES   2		/* these are to be printed on different lines */
+
+/* This is the struct used to hold matches. */
+
+struct cmatch {
+    char *str;			/* the match itself */
+    char *ipre;			/* ignored prefix, has to be re-inserted */
+    char *ripre;		/* ignored prefix, unquoted */
+    char *ppre;			/* the path prefix */
+    char *psuf;			/* the path suffix */
+    char *prpre;		/* path prefix for opendir */
+    char *pre;			/* prefix string from -P */
+    char *suf;			/* suffix string from -S */
+    int flags;			/* see CMF_* below */
+    int brpl;			/* the place where to put the brace prefix */
+    int brsl;			/* ...and the suffix */
+};
+
+#define CMF_FILE     1		/* this is a file */
+#define CMF_REMOVE   2		/* remove the suffix */
+#define CMF_PARBR    4		/* paramter expansion with a brace */
+#define CMF_NOLIST   8		/* should not be listed */
+
+
+/* Stuff for completion matcher control. */
+
+struct cmlist {
+    Cmlist next;		/* next one in the list of global matchers */
+    Cmatcher matcher;		/* the matcher definition */
+    char *str;			/* the string for it */
+};
+
+struct cmatcher {
+    Cmatcher next;		/* next matcher */
+    int flags;			/* see CMF_* below */
+    Cpattern line;		/* what matches on the line */
+    int llen;			/* length of line pattern */
+    Cpattern word;		/* what matches in the word */
+    int wlen;			/* length of word pattern */
+    Cpattern left;		/* left anchor */
+    int lalen;			/* length of left anchor */
+    Cpattern right;		/* right anchor */
+    int ralen;			/* length of right anchor */
+};
+
+
+#define CMF_LINE  1
+#define CMF_LEFT  2
+#define CMF_RIGHT 4
+
+
+struct cpattern {
+    Cpattern next;		/* next sub-pattern */
+    unsigned char tab[256];	/* table of matched characters */
+    int equiv;			/* if this is a {...} class */
+};
+
+
+struct cline {
+    Cline next;			/* next chunk */
+    char *line;			/* string to insert if !word */
+    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 */
+};
+
+#define CLF_END   1
+#define CLF_MID   2
+#define CLF_MISS  4
+#define CLF_DIFF  8
+#define CLF_SUF  16
diff --git a/Src/Zle/comp1.c b/Src/Zle/comp1.c
index acd1288a6..68aa5ef52 100644
--- a/Src/Zle/comp1.c
+++ b/Src/Zle/comp1.c
@@ -31,16 +31,34 @@
 
 #include "comp1.pro"
 
-/* default completion infos */
+/* Default completion infos */
  
 /**/
 struct compctl cc_compos, cc_default, cc_first, cc_dummy;
  
-/* hash table for completion info for commands */
+/* Global matcher. */
+
+/**/
+Cmlist cmatcher;
+
+/* pointers to functions required by zle */
+
+/**/
+void (*printcompctlptr) _((char *, Compctl, int));
+
+/**/
+Compctl (*compctl_widgetptr) _((char *, char **));
+
+/* Hash table for completion info for commands */
  
 /**/
 HashTable compctltab;
 
+/* List of pattern compctls */
+
+/**/
+Patcomp patcomps;
+
 /* Words on the command line, for use in completion */
  
 /**/
@@ -54,6 +72,16 @@ char **clwords;
 /**/
 int incompctlfunc;
 
+
+/* This variable and the functions rembslash() and quotename() came from     *
+ * zle_tricky.c, but are now used in compctl.c, too.                         */
+
+/* 1 if we are completing in a string */
+
+/**/
+int instring;
+
+
 /**/
 static void
 createcompctltable(void)
@@ -71,6 +99,8 @@ createcompctltable(void)
     compctltab->enablenode  = NULL;
     compctltab->freenode    = freecompctlp;
     compctltab->printnode   = NULL;
+
+    patcomps = NULL;
 }
 
 /**/
@@ -103,6 +133,7 @@ freecompctl(Compctl cc)
     zsfree(cc->prefix);
     zsfree(cc->suffix);
     zsfree(cc->hpat);
+    zsfree(cc->gname);
     zsfree(cc->subcmd);
     if (cc->cond)
 	freecompcond(cc->cond);
@@ -119,6 +150,9 @@ freecompctl(Compctl cc)
     }
     if (cc->xor && cc->xor != &cc_default)
 	freecompctl(cc->xor);
+    if (cc->matcher)
+	freecmatcher(cc->matcher);
+    zsfree(cc->mstr);
     zfree(cc, sizeof(struct compctl));
 }
 
@@ -167,6 +201,56 @@ freecompcond(void *a)
 }
 
 /**/
+void
+freecmlist(Cmlist l)
+{
+    Cmlist n;
+
+    while (l) {
+	n = l->next;
+	freecmatcher(l->matcher);
+	zsfree(l->str);
+
+	zfree(l, sizeof(struct cmlist));
+
+	l = n;
+    }
+}
+
+/**/
+void
+freecmatcher(Cmatcher m)
+{
+    Cmatcher n;
+
+    while (m) {
+	n = m->next;
+	freecpattern(m->line);
+	freecpattern(m->word);
+	freecpattern(m->left);
+	freecpattern(m->right);
+
+	zfree(m, sizeof(struct cmatcher));
+
+	m = n;
+    }
+}
+
+/**/
+void
+freecpattern(Cpattern p)
+{
+    Cpattern n;
+
+    while (p) {
+	n = p->next;
+	zfree(p, sizeof(struct cpattern));
+
+	p = n;
+    }
+}
+
+/**/
 int
 compctlread(char *name, char **args, char *ops, char *reply)
 {
@@ -261,6 +345,89 @@ compctlread(char *name, char **args, char *ops, char *reply)
     return 0;
 }
 
+/* Copy the given string and remove backslashes from the copy and return it. */
+
+/**/
+char *
+rembslash(char *s)
+{
+    char *t = s = dupstring(s);
+
+    while (*s)
+	if (*s == '\\') {
+	    chuck(s);
+	    if (*s)
+		s++;
+	} else
+	    s++;
+
+    return t;
+}
+
+/* Quote the string s and return the result.  If e is non-zero, the        *
+ * pointer it points to may point to a position in s and in e the position *
+ * of the corresponding character in the quoted string is returned.  Like  *
+ * e, te may point to a position in the string and pl is used to return    *
+ * the position of the character pointed to by te in the quoted string.    *
+ * The string is metafied and may contain tokens.                          */
+
+/**/
+char *
+quotename(const char *s, char **e, char *te, int *pl)
+{
+    const char *u, *tt;
+    char *v, buf[PATH_MAX * 2];
+    int sf = 0;
+
+    tt = v = buf;
+    u = s;
+    for (; *u; u++) {
+	if (e && *e == u)
+	    *e = v, sf |= 1;
+	if (te == u)
+	    *pl = v - tt, sf |= 2;
+	if (ispecial(*u) &&
+	    (!instring || (isset(BANGHIST) &&
+			   *u == (char)bangchar) ||
+	     (instring == 2 &&
+	      (*u == '$' || *u == '`' || *u == '\"')) ||
+	     (instring == 1 && *u == '\''))) {
+	    if (*u == '\n' || (instring == 1 && *u == '\'')) {
+		if (unset(RCQUOTES)) {
+		    *v++ = '\'';
+		    if (*u == '\'')
+			*v++ = '\\';
+		    *v++ = *u;
+		    *v++ = '\'';
+		} else if (*u == '\n')
+		    *v++ = '"', *v++ = '\n', *v++ = '"';
+		else
+		    *v++ = '\'', *v++ = '\'';
+		continue;
+	    } else
+		*v++ = '\\';
+	}
+	if(*u == Meta)
+	    *v++ = *u++;
+	*v++ = *u;
+    }
+    *v = '\0';
+    if (strcmp(buf, s))
+	tt = dupstring(buf);
+    else
+	tt = s;
+    v += tt - buf;
+    if (e && (sf & 1))
+	*e += tt - buf;
+
+    if (e && *e == u)
+	*e = v;
+    if (te == u)
+	*pl = v - tt;
+
+    return (char *) tt;
+}
+
 /**/
 int
 boot_comp1(Module m)
@@ -269,10 +436,13 @@ boot_comp1(Module m)
     clwords = (char **) zcalloc((clwsize = 16) * sizeof(char *));
     createcompctltable();
     cc_compos.mask = CC_COMMPATH;
+    cc_compos.mask2 = 0;
     cc_default.refc = 10000;
     cc_default.mask = CC_FILES;
+    cc_default.mask2 = 0;
     cc_first.refc = 10000;
     cc_first.mask = 0;
+    cc_first.mask2 = CC_CCCONT;
     return 0;
 }
 
diff --git a/Src/Zle/comp1.export b/Src/Zle/comp1.export
new file mode 100644
index 000000000..1ac9195df
--- /dev/null
+++ b/Src/Zle/comp1.export
@@ -0,0 +1,22 @@
+#!
+cc_compos
+cc_default
+cc_dummy
+cc_first
+clwnum
+clwords
+clwpos
+clwsize
+cmatcher
+compctl_widgetptr
+compctltab
+freecmatcher
+freecmlist
+freecompcond
+freecompctl
+incompctlfunc
+instring
+patcomps
+printcompctlptr
+quotename
+rembslash
diff --git a/Src/Zle/comp1.mdd b/Src/Zle/comp1.mdd
index 9037e568a..f8c5f0a0d 100644
--- a/Src/Zle/comp1.mdd
+++ b/Src/Zle/comp1.mdd
@@ -1,3 +1,5 @@
+hasexport=1
+
 objects="comp1.o"
 
 headers="comp.h"
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index 658cf4161..7104bfc6e 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -29,6 +29,9 @@
 
 #include "compctl.mdh"
 #include "compctl.pro"
+#define GLOBAL_PROTOTYPES
+#include "zle_tricky.pro"
+#undef GLOBAL_PROTOTYPES
 
 #define COMP_LIST	(1<<0)	/* -L */
 #define COMP_COMMAND	(1<<1)	/* -C */
@@ -44,6 +47,328 @@ static int cclist;
 /* Mask for determining what to print */
 static unsigned long showmask = 0;
 
+/* This is a special return value for parse_cmatcher(), *
+ * signalling an error. */
+
+#define pcm_err ((Cmatcher) 1)
+
+/* Copy a list of completion matchers. */
+
+static Cmlist
+cpcmlist(Cmlist l)
+{
+    Cmlist r = NULL, *p = &r, n;
+
+    while (l) {
+	*p = n = (Cmlist) zalloc(sizeof(struct cmlist));
+	n->next = NULL;
+	n->matcher = cpcmatcher(l->matcher);
+	n->str = ztrdup(l->str);
+
+	p = &(n->next);
+	l = l->next;
+    }
+    return r;
+}
+
+/* Copy a completion matcher list. */
+
+/**/
+static Cmatcher
+cpcmatcher(Cmatcher m)
+{
+    Cmatcher r = NULL, *p = &r, n;
+
+    while (m) {
+	*p = n = (Cmatcher) zalloc(sizeof(struct cmatcher));
+
+	n->next = NULL;
+	n->flags = m->flags;
+	n->line = cpcpattern(m->line);
+	n->llen = m->llen;
+	n->word = cpcpattern(m->word);
+	n->wlen = m->wlen;
+	n->left = cpcpattern(m->left);
+	n->lalen = m->lalen;
+	n->right = cpcpattern(m->right);
+	n->ralen = m->ralen;
+
+	p = &(n->next);
+	m = m->next;
+    }
+    return r;
+}
+
+/* Copy a completion matcher pattern. */
+
+/**/
+static Cpattern
+cpcpattern(Cpattern o)
+{
+    Cpattern r = NULL, *p = &r, n;
+
+    while (o) {
+	*p = n = (Cpattern) zalloc(sizeof(struct cpattern));
+
+	n->next = NULL;
+	memcpy(n->tab, o->tab, 256);
+	n->equiv = o->equiv;
+
+	p = &(n->next);
+	o = o->next;
+    }
+    return r;
+}
+
+/* Try to get the global matcher from the given compctl. */
+
+/**/
+static int
+get_gmatcher(char *name, char **argv)
+{
+    if (!strcmp(*argv, "-M")) {
+	char **p = ++argv;
+	Cmlist l = NULL, *q = &l, n;
+	Cmatcher m;
+
+	while (*p) {
+	    if (**p++ == '-')
+		return 0;
+	}
+	while (*argv) {
+	    if ((m = parse_cmatcher(name, *argv)) == pcm_err)
+		return 2;
+	    *q = n = (Cmlist) halloc(sizeof(struct cmlist));
+	    n->next = NULL;
+	    n->matcher = m;
+	    n->str = *argv++;
+
+	    q = &(n->next);
+	}
+	freecmlist(cmatcher);
+	PERMALLOC {
+	    cmatcher = cpcmlist(l);
+	} LASTALLOC;
+
+	return 1;
+    }
+    return 0;
+}
+
+/* This prints the global matcher definitions. */
+
+/**/
+static void
+print_gmatcher(int ac)
+{
+    Cmlist p;
+
+    if ((p = cmatcher)) {
+	printf((ac ? "compctl -M" : "MATCH"));
+
+	while (p) {
+	    printf(" \'%s\'", p->str);
+
+	    p = p->next;
+	}
+	putchar('\n');
+    }
+}
+
+/* Parse a string for matcher control, containing multiple matchers. */
+
+/**/
+static Cmatcher
+parse_cmatcher(char *name, char *s)
+{
+    Cmatcher ret = NULL, r, n;
+    Cpattern line, word, left, right;
+    int fl, ll, wl, lal, ral, err;
+
+    if (!*s)
+	return NULL;
+
+    while (*s) {
+	while (*s && inblank(*s)) s++;
+
+	if (!*s) break;
+
+	switch (*s) {
+	case 'l': fl = CMF_LEFT; break;
+	case 'r': fl = CMF_RIGHT; break;
+	case 'm': fl = 0; break;
+	case 'L': fl = CMF_LEFT | CMF_LINE; break;
+	case 'R': fl = CMF_RIGHT | CMF_LINE; break;
+	case 'M': fl = CMF_LINE; break;
+	default:
+	    zwarnnam(name, "unknown match specification character `%c'", NULL, *s);
+	    return pcm_err;
+	}
+	if (s[1] != ':') {
+	    zwarnnam(name, "missing `:'", NULL, 0);
+	    return pcm_err;
+	}
+	s += 2;
+	if (!*s) {
+	    zwarnnam(name, "missing patterns", NULL, 0);
+	    return pcm_err;
+	}
+	if (fl & CMF_LEFT) {
+	    left = parse_pattern(name, &s, &lal, '|', &err);
+	    if (err)
+		return pcm_err;
+	    if (!*s || !*++s) {
+		zwarnnam(name, "missing line pattern", NULL, 0);
+		return pcm_err;
+	    }
+	} else
+	    left = NULL;
+
+	line = parse_pattern(name, &s, &ll, ((fl & CMF_RIGHT) ? '|' : '='),
+			     &err);
+	if (err)
+	    return pcm_err;
+	if (!*s || !*++s) {
+	    zwarnnam(name, ((fl & CMF_RIGHT) ? "missing right anchor" : "missing word pattern"), NULL, 0);
+	    return pcm_err;
+	}
+	if (fl & CMF_RIGHT) {
+	    right = parse_pattern(name, &s, &ral, '=', &err);
+	    if (err)
+		return pcm_err;
+	    if (!*s || !*++s) {
+		zwarnnam(name, "missing word pattern", NULL, 0);
+		return pcm_err;
+	    }
+	} else
+	    right = NULL;
+
+	if (*s == '*') {
+	    if (!(fl & (CMF_LEFT | CMF_RIGHT))) {
+		zwarnnam(name, "need anchor for `*'", NULL, 0);
+		return pcm_err;
+	    }
+	    word = NULL;
+	    wl = -1;
+	    s++;
+	} else {
+	    word = parse_pattern(name, &s, &wl, 0, &err);
+
+	    if (!word && !line) {
+		zwarnnam(name, "need non-empty word or line pattern", NULL, 0);
+		return pcm_err;
+	    }
+	}
+	if (err)
+	    return pcm_err;
+
+	n = (Cmatcher) zcalloc(sizeof(*ret));
+	n->next = NULL;
+	n->flags = fl;
+	n->line = line;
+	n->llen = ll;
+	n->word = word;
+	n->wlen = wl;
+	n->left = left;
+	n->lalen = lal;
+	n->right = right;
+	n->ralen = ral;
+
+	if (ret) r->next = n;
+	else ret = n;
+
+	r = n;
+    }
+    return ret;
+}
+
+/* Parse a pattern for matcher control. */
+
+/**/
+static Cpattern
+parse_pattern(char *name, char **sp, int *lp, char e, int *err)
+{
+    Cpattern ret = NULL, r, n;
+    unsigned char *s = (unsigned char *) *sp;
+    int l = 0;
+
+    *err = 0;
+
+    while (*s && (e ? (*s != e) : !inblank(*s))) {
+	n = (Cpattern) hcalloc(sizeof(*n));
+	n->next = NULL;
+	n->equiv = 0;
+
+	if (*s == '[') {
+	    s = parse_class(n, s + 1, ']');
+	    if (!*s) {
+		*err = 1;
+		zwarnnam(name, "unterminated character class", NULL, 0);
+		return NULL;
+	    }
+	} else if (*s == '{') {
+	    n->equiv = 1;
+	    s = parse_class(n, s + 1, '}');
+	    if (!*s) {
+		*err = 1;
+		zwarnnam(name, "unterminated character class", NULL, 0);
+		return NULL;
+	    }
+	} else if (*s == '?') {
+	    memset(n->tab, 1, 256);
+	} else if (*s == '*' || *s == '(' || *s == ')' || *s == '=') {
+	    *err = 1;
+	    zwarnnam(name, "invalid pattern character `%c'", NULL, *s);
+	    return NULL;
+	} else {
+	    if (*s == '\\' && s[1])
+		s++;
+
+	    memset(n->tab, 0, 256);
+	    n->tab[*s] = 1;
+	}
+	if (ret)
+	    r->next = n;
+	else
+	    ret = n;
+
+	r = n;
+
+	l++;
+	s++;
+    }
+    *sp = (char *) s;
+    *lp = l;
+    return ret;
+}
+
+/* Parse a character class for matcher control. */
+
+/**/
+static unsigned char *
+parse_class(Cpattern p, unsigned char *s, unsigned char e)
+{
+    int n = 0, i = 1, j, eq = (e == '}'), k = 1;
+
+    if (!eq && (*s == '!' || *s == '^') && s[1] != e) { n = 1; s++; }
+
+    memset(p->tab, n, 256);
+
+    n = !n;
+    while (*s && (k || *s != e)) {
+	if (s[1] == '-' && s[2] != e) {
+	    /* a run of characters */
+	    for (j = (int) *s; j <= (int) s[2]; j++)
+		p->tab[j] = (eq ? i++ : n);
+
+	    s += 3;
+	} else
+	    p->tab[*s++] = (eq ? i++ : n);
+	k = 0;
+    }
+    return s;
+}
+
 /* Parse the basic flags for `compctl' */
 
 /**/
@@ -176,6 +501,44 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
 	    case '/':
 		cct.mask |= CC_DIRS;
 		break;
+	    case 't':
+		{
+		    char *p;
+
+		    if ((*argv)[1]) {
+			p = (*argv) + 1;
+			*argv = "" - 1;
+		    } else if (!argv[1]) {
+			zwarnnam(name, "retry specification expected after -%c", NULL,
+				 **argv);
+			return 1;
+		    } else {
+			p = *++argv;
+			*argv = "" - 1;
+		    }
+		    while (*p) {
+			switch (*p) {
+			case '+':
+			    cct.mask2 |= CC_XORCONT;
+			    break;
+			case 'c':
+			    cct.mask2 |= CC_CCCONT;
+			    break;
+			case '-':
+			    cct.mask2 |= CC_PATCONT;
+			    break;
+			case 'x':
+			    cct.mask2 |= CC_DEFCONT;
+			    break;
+			default:
+			    zwarnnam(name, "invalid retry specification character `%c'",
+				     NULL, *p);
+			    return 1;
+			}
+			p++;
+		    }
+		}
+		break;
 	    case 'k':
 		if ((*argv)[1]) {
 		    cct.keyvar = (*argv) + 1;
@@ -307,6 +670,58 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
 		    *argv = "" - 1;
 		}
 		break;
+	    case 'J':
+		if ((*argv)[1]) {
+		    cct.gname = (*argv) + 1;
+		    *argv = "" - 1;
+		} else if (!argv[1]) {
+		    zwarnnam(name, "group name expected after -%c", NULL,
+			    **argv);
+		    return 1;
+		} else {
+		    cct.gname = *++argv;
+		    *argv = "" - 1;
+		}
+		break;
+	    case 'V':
+		if ((*argv)[1]) {
+		    cct.gname = (*argv) + 1;
+		    *argv = "" - 1;
+		} else if (!argv[1]) {
+		    zwarnnam(name, "group name expected after -%c", NULL,
+			    **argv);
+		    return 1;
+		} else {
+		    cct.gname = *++argv;
+		    *argv = "" - 1;
+		}
+		cct.mask2 |= CC_NOSORT;
+		break;
+	    case 'M':
+		if ((*argv)[1]) {
+		    if ((cct.matcher =
+			 parse_cmatcher(name, (cct.mstr = (*argv) + 1))) ==
+			pcm_err) {
+			cct.matcher = NULL;
+			cct.mstr = NULL;
+			return 1;
+		    }
+		    *argv = "" - 1;
+		} else if (!argv[1]) {
+		    zwarnnam(name, "matching specification expected after -%c", NULL,
+			    **argv);
+		    return 1;
+		} else {
+		    if ((cct.matcher =
+			 parse_cmatcher(name, (cct.mstr = *++argv))) ==
+			pcm_err) {
+			cct.matcher = NULL;
+			cct.mstr = NULL;
+			return 1;
+		    }
+		    *argv = "" - 1;
+		}
+		break;
 	    case 'H':
 		if ((*argv)[1])
 		    cct.hnum = atoi((*argv) + 1);
@@ -600,27 +1015,34 @@ get_xcompctl(char *name, char ***av, Compctl cc, int isdef)
 		    c->u.s.s[l] = ztrdup(tt);
 		} else if (c->type == CCT_RANGESTR ||
 			   c->type == CCT_RANGEPAT) {
+		    int hc;
+
 		    /* -r[..,..] or -R[..,..]:  two strings expected */
-		    for (; *t && *t != '\201'; t++);
+		    for (; *t && *t != '\201' && *t != '\200'; t++);
 		    if (!*t) {
 			zwarnnam(name, "error in condition", NULL, 0);
 			freecompcond(m);
 			return 1;
 		    }
+		    hc = (*t == '\201');
 		    *t = '\0';
 		    c->u.l.a[l] = ztrdup(tt);
-		    tt = ++t;
-		    /* any more commas are text, not active */
-		    for (; *t && *t != '\200'; t++)
-			if (*t == '\201')
-			    *t = ',';
-		    if (!*t) {
-			zwarnnam(name, "error in condition", NULL, 0);
-			freecompcond(m);
-			return 1;
+		    if (hc) {
+			tt = ++t;
+			/* any more commas are text, not active */
+			for (; *t && *t != '\200'; t++)
+			    if (*t == '\201')
+				*t = ',';
+			if (!*t) {
+			    zwarnnam(name, "error in condition", NULL, 0);
+			    freecompcond(m);
+			    return 1;
+			}
+			*t = '\0';
+			c->u.l.b[l] = ztrdup(tt);
 		    }
-		    *t = '\0';
-		    c->u.l.b[l] = ztrdup(tt);
+		    else
+			c->u.l.b[l] = NULL;
 		} else {
 		    /* remaining patterns are number followed by string */
 		    for (; *t && *t != '\200' && *t != '\201'; t++);
@@ -700,13 +1122,6 @@ cc_assign(char *name, Compctl *ccptr, Compctl cct, int reass)
     /* Copy over the details from the values in cct to those in *ccptr */
     Compctl cc;
 
-    if (cct->subcmd && (cct->keyvar || cct->glob || cct->str ||
-			cct->func || cct->explain || cct->ylist ||
-			cct->prefix)) {
-	zwarnnam(name, "illegal combination of options", NULL, 0);
-	return 1;
-    }
-
     /* Handle assignment of new default or command completion */
     if (reass && !(cclist & COMP_LIST)) {
 	/* if not listing */
@@ -746,11 +1161,15 @@ cc_assign(char *name, Compctl *ccptr, Compctl cct, int reass)
     zsfree(cc->subcmd);
     zsfree(cc->withd);
     zsfree(cc->hpat);
-    
+    zsfree(cc->gname);
+    zsfree(cc->mstr);
+    freecmatcher(cc->matcher);
+
     /* and copy over the new stuff, (permanently) allocating
      * space for strings.
      */
     cc->mask = cct->mask;
+    cc->mask2 = cct->mask2;
     cc->keyvar = ztrdup(cct->keyvar);
     cc->glob = ztrdup(cct->glob);
     cc->str = ztrdup(cct->str);
@@ -761,8 +1180,13 @@ cc_assign(char *name, Compctl *ccptr, Compctl cct, int reass)
     cc->suffix = ztrdup(cct->suffix);
     cc->subcmd = ztrdup(cct->subcmd);
     cc->withd = ztrdup(cct->withd);
+    cc->gname = ztrdup(cct->gname);
     cc->hpat = ztrdup(cct->hpat);
     cc->hnum = cct->hnum;
+    PERMALLOC {
+	cc->matcher = cpcmatcher(cct->matcher);
+    } LASTALLOC;
+    cc->mstr = ztrdup(cct->mstr);
 
     /* careful with extended completion:  it's already allocated */
     cc->ext = cct->ext;
@@ -790,16 +1214,62 @@ cc_reassign(Compctl cc)
     cc->ext = cc->xor = NULL;
 }
 
+/* Check if the given string is a pattern. If so, return one and tokenize *
+ * it. If not, we just remove the backslashes. */
+
+static int
+compctl_name_pat(char **p)
+{
+    char *s = *p;
+
+    tokenize(s = dupstring(s));
+    remnulargs(s);
+
+    if (haswilds(s)) {
+	*p = s;
+	return 1;
+    } else
+	*p = rembslash(*p);
+
+    return 0;
+}
+
+/* Delete the pattern compctl with the given name. */
+
+static void
+delpatcomp(char *n)
+{
+    Patcomp p, q;
+
+    for (q = 0, p = patcomps; p; q = p, p = p->next) {
+	if (!strcmp(n, p->pat)) {
+	    if (q)
+		q->next = p->next;
+	    else
+		patcomps = p->next;
+	    zsfree(p->pat);
+	    freecompctl(p->cc);
+	    free(p);
+
+	    break;
+	}
+    }
+}
+
 /**/
 static void
 compctl_process_cc(char **s, Compctl cc)
 {
     Compctlp ccp;
+    char *n;
 
     if (cclist & COMP_REMOVE) {
 	/* Delete entries for the commands listed */
 	for (; *s; s++) {
-	    if ((ccp = (Compctlp) compctltab->removenode(compctltab, *s)))
+	    n = *s;
+	    if (compctl_name_pat(&n))
+		delpatcomp(n);
+	    else if ((ccp = (Compctlp) compctltab->removenode(compctltab, n)))
 		compctltab->freenode((HashNode) ccp);
 	}
     } else {
@@ -807,10 +1277,23 @@ compctl_process_cc(char **s, Compctl cc)
 
 	cc->refc = 0;
 	for (; *s; s++) {
+	    n = *s;
+
 	    cc->refc++;
-	    ccp = (Compctlp) zalloc(sizeof *ccp);
-	    ccp->cc = cc;
-	    compctltab->addnode(compctltab, ztrdup(*s), ccp);
+	    if (compctl_name_pat(&n)) {
+		Patcomp pc;
+
+		delpatcomp(n);
+		pc = zalloc(sizeof *pc);
+		pc->pat = ztrdup(n);
+		pc->cc = cc;
+		pc->next = patcomps;
+		patcomps = pc;
+	    } else {
+		ccp = (Compctlp) zalloc(sizeof *ccp);
+		ccp->cc = cc;
+		compctltab->addnode(compctltab, ztrdup(n), ccp);
+	    }
 	}
     }
 }
@@ -819,15 +1302,21 @@ compctl_process_cc(char **s, Compctl cc)
 
 /**/
 static void
-printcompctl(char *s, Compctl cc, int printflags)
+printcompctl(char *s, Compctl cc, int printflags, int ispat)
 {
     Compctl cc2;
     char *css = "fcqovbAIFpEjrzBRGudeNOZUnQmw/";
     char *mss = " pcCwWsSnNmrR";
     unsigned long t = 0x7fffffff;
-    unsigned long flags = cc->mask;
+    unsigned long flags = cc->mask, flags2 = cc->mask2;
     unsigned long oldshowmask;
 
+    /* Printflags is used outside the standard compctl commands*/
+    if (printflags & PRINT_LIST)
+	cclist |= COMP_LIST;
+    else if (printflags & PRINT_TYPE)
+	cclist &= ~COMP_LIST;
+
     if ((flags & CC_EXCMDS) && !(flags & CC_DISCMDS))
 	flags &= ~CC_EXCMDS;
 
@@ -851,8 +1340,13 @@ printcompctl(char *s, Compctl cc, int printflags)
 		printf(" -D");
 	    if (cc == &cc_first)
 		printf(" -T");
+	} else if (ispat) {
+	    char *p = dupstring(s);
+
+	    untokenize(p);
+	    quotedzputs(p, stdout);
 	} else
-	    quotedzputs(s, stdout);
+	    quotedzputs(quotename(s, NULL, NULL, NULL), stdout);
     }
 
     /* loop through flags w/o args that are set, printing them if so */
@@ -868,9 +1362,23 @@ printcompctl(char *s, Compctl cc, int printflags)
 	    t >>= 1;
 	}
     }
-
+    if (flags2 & (CC_XORCONT | CC_CCCONT | CC_PATCONT | CC_DEFCONT)) {
+	printf(" -t");
+	if (flags2 & CC_XORCONT)
+	    putchar('+');
+	if (flags2 & CC_CCCONT)
+	    putchar('c');
+	if (flags2 & CC_PATCONT)
+	    putchar('-');
+	if (flags2 & CC_DEFCONT)
+	    putchar('x');
+    }
     /* now flags with arguments */
-    flags = cc->mask;
+    printif(cc->mstr, 'M');
+    if (flags2 & CC_NOSORT)
+	printif(cc->gname, 'V');
+    else
+	printif(cc->gname, 'J');
     printif(cc->keyvar, 'k');
     printif(cc->func, 'K');
     printif(cc->explain, (cc->mask & CC_EXPANDEXPL) ? 'Y' : 'X');
@@ -940,7 +1448,7 @@ printcompctl(char *s, Compctl cc, int printflags)
 	    c = cc2->cond;
 	    cc2->cond = NULL;
 	    /* now print the flags for the current condition */
-	    printcompctl(NULL, cc2, 0);
+	    printcompctl(NULL, cc2, 0, 0);
 	    cc2->cond = c;
 	    if ((cc2 = (Compctl) (cc2->next)))
 		printf(" -");
@@ -952,7 +1460,7 @@ printcompctl(char *s, Compctl cc, int printflags)
 	/* print xor'd (+) completions */
 	printf(" +");
 	if (cc->xor != &cc_default)
-	    printcompctl(NULL, cc->xor, 0);
+	    printcompctl(NULL, cc->xor, 0, 0);
     }
     if (s) {
 	if ((cclist & COMP_LIST) && (cc != &cc_compos)
@@ -960,7 +1468,17 @@ printcompctl(char *s, Compctl cc, int printflags)
 	    if(s[0] == '-' || s[0] == '+')
 		printf(" -");
 	    putchar(' ');
-	    quotedzputs(s, stdout);
+	    if (ispat) {
+		char *p = dupstring(s);
+
+		untokenize(p);
+		quotedzputs(p, stdout);
+	    } else {
+		char *p = dupstring(s);
+
+		untokenize(p);
+		quotedzputs(quotename(p, NULL, NULL, NULL), stdout);
+	    }
 	}
 	putchar('\n');
     }
@@ -975,7 +1493,7 @@ printcompctlp(HashNode hn, int printflags)
     Compctlp ccp = (Compctlp) hn;
 
     /* Function needed for use by scanhashtable() */
-    printcompctl(ccp->nam, ccp->cc, printflags);
+    printcompctl(ccp->nam, ccp->cc, printflags, 0);
 }
 
 /* Main entry point for the `compctl' builtin */
@@ -991,8 +1509,14 @@ bin_compctl(char *name, char **argv, char *ops, int func)
     cclist = 0;
     showmask = 0;
 
+    instring = 0;
+
     /* Parse all the arguments */
     if (*argv) {
+	/* Let's see if this is a global matcher definition. */
+	if ((ret = get_gmatcher(name, argv)))
+	    return ret - 1;
+
 	cc = (Compctl) zcalloc(sizeof(*cc));
 	if (get_compctl(name, &argv, cc, 1, 0)) {
 	    freecompctl(cc);
@@ -1013,10 +1537,16 @@ bin_compctl(char *name, char **argv, char *ops, int func)
      * If some flags (other than -C, -T, or -D) were given, then    *
      * only print compctl containing those flags.                   */
     if (!*argv && !(cclist & COMP_SPECIAL)) {
+	Patcomp pc;
+
+	for (pc = patcomps; pc; pc = pc->next)
+	    printcompctl(pc->pat, pc->cc, 0, 1);
+
 	scanhashtable(compctltab, 1, 0, 0, compctltab->printnode, 0);
-	printcompctl((cclist & COMP_LIST) ? "" : "COMMAND", &cc_compos, 0);
-	printcompctl((cclist & COMP_LIST) ? "" : "DEFAULT", &cc_default, 0);
- 	printcompctl((cclist & COMP_LIST) ? "" : "FIRST", &cc_first, 0);
+	printcompctl((cclist & COMP_LIST) ? "" : "COMMAND", &cc_compos, 0, 0);
+	printcompctl((cclist & COMP_LIST) ? "" : "DEFAULT", &cc_default, 0, 0);
+ 	printcompctl((cclist & COMP_LIST) ? "" : "FIRST", &cc_first, 0, 0);
+	print_gmatcher((cclist & COMP_LIST));
 	return ret;
     }
 
@@ -1024,23 +1554,35 @@ bin_compctl(char *name, char **argv, char *ops, int func)
      * or a COMP_SPECIAL flag (-D, -C, -T), so print only those.            */
     if (cclist & COMP_LIST) {
 	HashNode hn;
-	char **ptr;
+	char **ptr, *n;
 
 	showmask = 0;
 	for (ptr = argv; *ptr; ptr++) {
-	    if ((hn = compctltab->getnode(compctltab, *ptr))) {
+	    n = *ptr;
+	    if (compctl_name_pat(&n)) {
+		Patcomp pc;
+
+		for (pc = patcomps; pc; pc = pc->next)
+		    if (!strcmp(n, pc->pat)) {
+			printcompctl(pc->pat, pc->cc, 0, 1);
+			n = NULL;
+			break;
+		    }
+	    } else if ((hn = compctltab->getnode(compctltab, n))) {
 		compctltab->printnode(hn, 0);
-	    } else {
-		zwarnnam(name, "no compctl defined for %s", *ptr, 0);
+		n = NULL;
+	    }
+	    if (n) {
+		zwarnnam(name, "no compctl defined for %s", n, 0);
 		ret = 1;
 	    }
 	}
 	if (cclist & COMP_COMMAND)
-	    printcompctl("", &cc_compos, 0);
+	    printcompctl("", &cc_compos, 0, 0);
 	if (cclist & COMP_DEFAULT)
-	    printcompctl("", &cc_default, 0);
+	    printcompctl("", &cc_default, 0, 0);
 	if (cclist & COMP_FIRST)
-	    printcompctl("", &cc_first, 0);
+	    printcompctl("", &cc_first, 0, 0);
 	return ret;
     }
 
@@ -1058,6 +1600,38 @@ bin_compctl(char *name, char **argv, char *ops, int func)
     return ret;
 }
 
+/* Externally callable version of get_compctl.  Used for completion widgets */
+
+/**/
+static Compctl
+compctl_widget(char *name, char **argv)
+{
+  Compctl cc = (Compctl) zcalloc(sizeof(*cc));
+  cclist = 0;
+  showmask = 0;
+
+  if (get_compctl(name, &argv, cc, 1, 0)) {
+      freecompctl(cc);
+      return NULL;
+  }
+
+  if (cclist & COMP_REMOVE) {
+      zwarnnam(name, "use -D to delete widget", NULL, 0);
+      return NULL;
+  } else if (cclist) {
+      zwarnnam(name, "special options illegal in widget", NULL, 0);
+      freecompctl(cc);
+      return NULL;
+  } else if (*argv) {
+      zwarnnam(name, "command names illegal in widget", NULL, 0);
+      freecompctl(cc);
+      return NULL;
+  }
+  cc->refc++;
+
+  return cc;
+}
+
 static struct builtin bintab[] = {
     BUILTIN("compctl", 0, bin_compctl, 0, -1, 0, NULL, NULL),
 };
@@ -1069,6 +1643,8 @@ boot_compctl(Module m)
     if(!addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)))
 	return 1;
     compctltab->printnode = printcompctlp;
+    printcompctlptr = printcompctl;
+    compctl_widgetptr = compctl_widget;
     return 0;
 }
 
@@ -1080,6 +1656,8 @@ cleanup_compctl(Module m)
 {
     compctltab->printnode = NULL;
     deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    printcompctlptr = NULL;
+    compctl_widgetptr = NULL;
     return 0;
 }
 #endif
diff --git a/Src/Zle/deltochar.c b/Src/Zle/deltochar.c
index 87f8593b8..8869eb147 100644
--- a/Src/Zle/deltochar.c
+++ b/Src/Zle/deltochar.c
@@ -45,7 +45,7 @@ deltochar(void)
 	    if (dest != ll) {
 		dest++;
 		if (!n) {
-		    foredel(dest - cs);
+		    forekill(dest - cs, 0);
 		    ok++;
 		}
 	    }
@@ -57,9 +57,13 @@ deltochar(void)
 	while (n++ && dest != 0) {
 	    while (dest != 0 && line[dest] != c)
 		dest--;
-	    if (line[dest] == c && !n) {
-		backdel(cs - dest);
-		ok++;
+	    if (line[dest] == c) {
+		if (!n) {
+		    backkill(cs - dest, 1);
+		    ok++;
+		}
+		if (dest)
+		    dest--;
 	    }
 	}
     }
@@ -71,7 +75,8 @@ deltochar(void)
 int
 boot_deltochar(Module m)
 {
-    w_deletetochar = addzlefunction("delete-to-char", deltochar, ZLE_KEEPSUFFIX);
+    w_deletetochar = addzlefunction("delete-to-char", deltochar,
+                                    ZLE_KILL | ZLE_KEEPSUFFIX);
     if (w_deletetochar)
 	return 0;
     zwarnnam(m->nam, "name clash when adding ZLE function `delete-to-char'",
diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list
index 01862160e..52e70fad5 100644
--- a/Src/Zle/iwidgets.list
+++ b/Src/Zle/iwidgets.list
@@ -24,7 +24,7 @@
 "beginning-of-line", beginningofline, 0
 "beginning-of-line-hist", beginningoflinehist, 0
 "capitalize-word", capitalizeword, 0
-"clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
+"clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
 "complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX
 "copy-prev-word", copyprevword, 0
 "copy-region-as-kill", copyregionaskill, ZLE_KEEPSUFFIX
@@ -32,7 +32,7 @@
 "delete-char-or-list", deletecharorlist, ZLE_MENUCMP | ZLE_KEEPSUFFIX
 "delete-word", deleteword, ZLE_KEEPSUFFIX
 "describe-key-briefly", describekeybriefly, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
-"digit-argument", digitargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
+"digit-argument", digitargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
 "down-case-word", downcaseword, 0
 "down-history", downhistory, 0
 "down-line-or-history", downlineorhistory, ZLE_LINEMOVE | ZLE_LASTCOL
@@ -73,7 +73,7 @@
 "magic-space", magicspace, 0
 "menu-complete", menucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX
 "menu-expand-or-complete", menuexpandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX
-"neg-argument", negargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
+"neg-argument", negargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
 "overwrite-mode", overwritemode, 0
 "pound-insert", poundinsert, 0
 "push-input", pushinput, 0
@@ -95,7 +95,7 @@
 "transpose-words", transposewords, 0
 "undefined-key", undefinedkey, 0
 "undo", undo, 0
-"universal-argument", universalargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
+"universal-argument", universalargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
 "up-case-word", upcaseword, 0
 "up-history", uphistory, 0
 "up-line-or-history", uplineorhistory, ZLE_LINEMOVE | ZLE_LASTCOL
@@ -145,8 +145,8 @@
 "vi-open-line-below", viopenlinebelow, 0
 "vi-oper-swap-case", vioperswapcase, 0
 "vi-pound-insert", vipoundinsert, 0
-"vi-put-after", viputafter, ZLE_YANK
-"vi-put-before", viputbefore, ZLE_YANK
+"vi-put-after", viputafter, ZLE_YANK | ZLE_KEEPSUFFIX
+"vi-put-before", viputbefore, ZLE_YANK | ZLE_KEEPSUFFIX
 "vi-quoted-insert", viquotedinsert, ZLE_MENUCMP | ZLE_KEEPSUFFIX
 "vi-repeat-change", virepeatchange, 0
 "vi-repeat-find", virepeatfind, 0
@@ -168,5 +168,5 @@
 "what-cursor-position", whatcursorposition, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
 "where-is", whereis, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
 "which-command", processcmd, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
-"yank", yank, ZLE_YANK
-"yank-pop", yankpop, ZLE_YANK
+"yank", yank, ZLE_YANK | ZLE_KEEPSUFFIX
+"yank-pop", yankpop, ZLE_YANK | ZLE_KEEPSUFFIX
diff --git a/Src/Zle/zle.export b/Src/Zle/zle.export
new file mode 100644
index 000000000..ccd5df98a
--- /dev/null
+++ b/Src/Zle/zle.export
@@ -0,0 +1,10 @@
+#!
+addzlefunction
+backdel
+backkill
+deletezlefunction
+feep
+foredel
+forekill
+getkey
+zmod
diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h
index faf6cf878..71a929f87 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -31,7 +31,7 @@
 #undef zleread
 #undef spaceinline
 #undef gotword
-#undef refresh
+#undef zrefresh
 
 typedef struct widget *Widget;
 typedef struct thingy *Thingy;
@@ -46,16 +46,23 @@ struct widget {
     union {
 	ZleIntFunc fn;	/* pointer to internally implemented widget */
 	char *fnnam;	/* name of the shell function for user-defined widget */
+        Compctl cc;     /* for use with a WIDGET_COMP widget */
     } u;
 };
 
 #define WIDGET_INT	(1<<0)    /* widget is internally implemented */
-#define ZLE_MENUCMP	(1<<1)    /* DON'T invalidate completion list */
+#define WIDGET_COMP	(1<<1)	  /* Special completion widget */
+#define ZLE_MENUCMP	(1<<2)    /* DON'T invalidate completion list */
 #define ZLE_YANK	(1<<3)
 #define ZLE_LINEMOVE	(1<<4)    /* command is a line-oriented movement */
 #define ZLE_LASTCOL     (1<<5)    /* command maintains lastcol correctly */
 #define ZLE_KILL	(1<<6)
 #define ZLE_KEEPSUFFIX	(1<<9)    /* DON'T remove added suffix */
+#define ZLE_USEMENU	(1<<10)   /* Do    ) use menu completion for   */
+#define ZLE_NOMENU	(1<<11)   /* Don't )  widget, else use default */
+#define ZLE_USEGLOB	(1<<12)   /* Do    ) use glob completion for   */
+#define ZLE_NOGLOB	(1<<13)   /* Don't )  widget, else use default */
+#define ZLE_NOTCOMMAND  (1<<14)   /* widget should not alter lastcmd */
 
 /* thingies */
 
diff --git a/Src/Zle/zle.mdd b/Src/Zle/zle.mdd
index 29f39d363..fec2e213b 100644
--- a/Src/Zle/zle.mdd
+++ b/Src/Zle/zle.mdd
@@ -1,3 +1,5 @@
+hasexport=1
+
 moddeps="comp1"
 
 autobins="bindkey vared zle"
diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c
index 76e421c1c..fda204222 100644
--- a/Src/Zle/zle_hist.c
+++ b/Src/Zle/zle_hist.c
@@ -291,73 +291,86 @@ downhistory(void)
 	feep();
 }
 
+static int histpos, srch_hl, srch_cs = -1;
+static char *srch_str;
+
 /**/
 void
 historysearchbackward(void)
 {
-    int histpos, histmpos, hl = histline;
+    int hl = histline;
     int n = zmult;
     char *s;
 
-    if (!n)
-	return;
-    if (n < 0) {
+    if (zmult < 0) {
 	zmult = -n;
 	historysearchforward();
 	zmult = n;
 	return;
     }
-    for (histpos = histmpos = 0; histpos < ll && !iblank(line[histpos]);
-	histpos++, histmpos++)
-	if(imeta(line[histpos]))
-	    histmpos++;
+    if (hl == curhist || hl != srch_hl || cs != srch_cs || mark != 0
+     || memcmp(srch_str, line, histpos) != 0) {
+	zfree(srch_str, histpos);
+	for (histpos = 0; histpos < ll && !iblank(line[histpos]); histpos++) ;
+	if (histpos < ll)
+	    histpos++;
+	srch_str = zalloc(histpos);
+	memcpy(srch_str, line, histpos);
+    }
     for (;;) {
 	hl--;
 	if (!(s = zle_get_event(hl))) {
 	    feep();
 	    return;
 	}
-	if (metadiffer(s, (char *) line, histpos) < 0 &&
-	    iblank(s[histmpos] == Meta ? s[histmpos+1]^32 : s[histmpos]) &&
-	    metadiffer(s, (char *) line, ll) && !--n)
-	    break;
+	if (metadiffer(s, srch_str, histpos) < 0 &&
+	    metadiffer(s, srch_str, ll)) {
+	    if (--n <= 0)
+		break;
+	}
     }
     zle_goto_hist(hl);
+    srch_hl = hl;
+    srch_cs = cs;
 }
 
 /**/
 void
 historysearchforward(void)
 {
-    int histpos, histmpos, hl = histline;
+    int hl = histline;
     int n = zmult;
     char *s;
 
-    if (!n)
-	return;
-    if (n < 0) {
+    if (zmult < 0) {
 	zmult = -n;
 	historysearchbackward();
 	zmult = n;
 	return;
     }
-    for (histpos = histmpos = 0; histpos < ll && !iblank(line[histpos]);
-	histpos++, histmpos++)
-	if(imeta(line[histpos]))
-	    histmpos++;
+    if (hl == curhist || hl != srch_hl || cs != srch_cs || mark != 0
+     || memcmp(srch_str, line, histpos) != 0) {
+	zfree(srch_str, histpos);
+	for (histpos = 0; histpos < ll && !iblank(line[histpos]); histpos++) ;
+	if (histpos < ll)
+	    histpos++;
+	srch_str = zalloc(histpos);
+	memcpy(srch_str, line, histpos);
+    }
     for (;;) {
 	hl++;
 	if (!(s = zle_get_event(hl))) {
 	    feep();
 	    return;
 	}
-	if (metadiffer(s, (char *) line, histpos) < (histline == curhist) &&
-	    (!s[histmpos] ||
-	     iblank(s[histmpos] == Meta ? s[histmpos+1]^32 : s[histmpos])) &&
-	    metadiffer(s, (char *) line, ll) && !--n)
-	    break;
+	if (metadiffer(s, srch_str, histpos) < (hl == curhist) &&
+	    metadiffer(s, srch_str, ll))
+	    if (--n <= 0)
+		break;
     }
     zle_goto_hist(hl);
+    srch_hl = hl;
+    srch_cs = cs;
 }
 
 /**/
@@ -721,7 +734,7 @@ doisearch(int dir)
 	sbuf[sbptr] = '_';
 	statusll = sbuf - statusline + sbptr + 1;
     ref:
-	refresh();
+	zrefresh();
 	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) {
 	    int i;
 	    get_isrch_spot(0, &hl, &pos, &i, &sbptr, &dir, &nomatch);
@@ -809,7 +822,7 @@ doisearch(int dir)
 	    	cmd == Th(z_quotedinsert)) {
 	    if(cmd == Th(z_viquotedinsert)) {
 		sbuf[sbptr] = '^';
-		refresh();
+		zrefresh();
 	    }
 	    if ((c = getkey(0)) == EOF)
 		feep();
@@ -936,7 +949,7 @@ getvisrchstr(void)
     while (sptr) {
 	sbuf[sptr] = '_';
 	statusll = sptr + 1;
-	refresh();
+	zrefresh();
 	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) {
 	    ret = 0;
 	    break;
@@ -971,7 +984,7 @@ getvisrchstr(void)
 	} else if(cmd == Th(z_viquotedinsert) || cmd == Th(z_quotedinsert)) {
 	    if(cmd == Th(z_viquotedinsert)) {
 		sbuf[sptr] = '^';
-		refresh();
+		zrefresh();
 	    }
 	    if ((c = getkey(0)) == EOF)
 		feep();
@@ -1030,9 +1043,7 @@ virepeatsearch(void)
 	feep();
 	return;
     }
-    if (!n)
-	return;
-    if (n < 0) {
+    if (zmult < 0) {
 	n = -n;
 	visrchsense = -visrchsense;
     }
@@ -1078,9 +1089,7 @@ historybeginningsearchbackward(void)
     int n = zmult;
     char *s;
 
-    if (!n)
-	return;
-    if (n < 0) {
+    if (zmult < 0) {
 	zmult = -n;
 	historybeginningsearchforward();
 	zmult = n;
@@ -1114,9 +1123,7 @@ historybeginningsearchforward(void)
     int n = zmult;
     char *s;
 
-    if (!n)
-	return;
-    if (n < 0) {
+    if (zmult < 0) {
 	zmult = -n;
 	historybeginningsearchbackward();
 	zmult = n;
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 338cdef41..254021bed 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -69,6 +69,9 @@ static int baud;
 /**/
 int lastcmd;
 
+/**/
+Widget compwidget;
+
 /* the status line, and its length */
 
 /**/
@@ -112,7 +115,7 @@ int feepflag;
 
 /**/
 void
-setterm(void)
+zsetterm(void)
 {
     struct ttyinfo ti;
 
@@ -362,7 +365,7 @@ getkey(int keytmout)
 		ret = opts[MONITOR];
 		opts[MONITOR] = 1;
 		attachtty(mypgrp);
-		refresh();	/* kludge! */
+		zrefresh();	/* kludge! */
 		opts[MONITOR] = ret;
 		die = 1;
 	    } else if (errno != 0) {
@@ -372,7 +375,7 @@ getkey(int keytmout)
 	    }
 	}
 	if (cc == '\r')		/* undo the exchange of \n and \r determined by */
-	    cc = '\n';		/* setterm() */
+	    cc = '\n';		/* zsetterm() */
 	else if (cc == '\n')
 	    cc = '\r';
 
@@ -485,7 +488,7 @@ zleread(char *lp, char *rp, int ha)
 	initmodifier(&zmod);
 	prefixflag = 0;
 	feepflag = 0;
-	refresh();
+	zrefresh();
 	while (!done && !errflag) {
 
 	    statusline = NULL;
@@ -516,11 +519,11 @@ zleread(char *lp, char *rp, int ha)
 		    tv.tv_usec = 500000;
 		if (!kungetct && select(SHTTY+1, (SELECT_ARG_2_T) & foofd,
 					NULL, NULL, &tv) <= 0)
-		    refresh();
+		    zrefresh();
 	    } else
 #endif
 		if (!kungetct)
-		    refresh();
+		    zrefresh();
 	    handlefeep();
 	}
 	statusline = NULL;
@@ -562,12 +565,17 @@ execzlefunc(Thingy func)
 	showmsg(msg);
 	zsfree(msg);
 	feep();
-    } else if((w = func->widget)->flags & WIDGET_INT) {
+    } else if((w = func->widget)->flags & (WIDGET_INT|WIDGET_COMP)) {
 	int wflags = w->flags;
 
 	if(!(wflags & ZLE_KEEPSUFFIX))
 	    removesuffix();
-	if(!(wflags & ZLE_MENUCMP)) {
+	if(!(wflags & ZLE_MENUCMP) ||
+	   ((wflags & WIDGET_COMP) && compwidget != w)) {
+	    /* If we are doing a special completion, and the widget
+	     * is not the one currently in use for special completion,
+	     * we are starting a new completion.
+	     */
 	    fixsuffix();
 	    invalidatelist();
 	}
@@ -575,8 +583,13 @@ execzlefunc(Thingy func)
 	    vilinerange = 1;
 	if(!(wflags & ZLE_LASTCOL))
 	    lastcol = -1;
-	w->u.fn();
-	lastcmd = wflags;
+	if (wflags & WIDGET_COMP) {
+	    compwidget = w;
+	    completespecial();
+	} else
+	    w->u.fn();
+	if (!(wflags & ZLE_NOTCOMMAND))
+	    lastcmd = wflags;
     } else {
 	List l = getshfunc(w->u.fnnam);
 
@@ -743,7 +756,7 @@ describekeybriefly(void)
 	return;
     statusline = "Describe key briefly: _";
     statusll = strlen(statusline);
-    refresh();
+    zrefresh();
     seq = getkeymapcmd(curkeymap, &func, &str);
     statusline = NULL;
     if(!*seq)
@@ -811,14 +824,14 @@ void
 trashzle(void)
 {
     if (zleactive) {
-	/* This refresh() is just to get the main editor display right and *
-	 * get the cursor in the right place.  For that reason, we disable *
-	 * list display (which would otherwise result in infinite          *
-	 * recursion [at least, it would if refresh() didn't have its      *
-	 * extra `inlist' check]).                                         */
+	/* This zrefresh() is just to get the main editor display right and *
+	 * get the cursor in the right place.  For that reason, we disable  *
+	 * list display (which would otherwise result in infinite           *
+	 * recursion [at least, it would if zrefresh() didn't have its      *
+	 * extra `inlist' check]).                                          */
 	int sl = showinglist;
 	showinglist = 0;
-	refresh();
+	zrefresh();
 	showinglist = sl;
 	moveto(nlnct, 0);
 	if (clearflag && tccan(TCCLEAREOD)) {
@@ -838,7 +851,7 @@ trashzle(void)
 static struct builtin bintab[] = {
     BUILTIN("bindkey", 0, bin_bindkey, 0, -1, 0, "evaMldDANmrsLR", NULL),
     BUILTIN("vared",   0, bin_vared,   1,  7, 0, NULL,             NULL),
-    BUILTIN("zle",     0, bin_zle,     0, -1, 0, "lDANL",          NULL),
+    BUILTIN("zle",     0, bin_zle,     0, -1, 0, "lDANCLmMgG",     NULL),
 };
 
 /**/
@@ -848,7 +861,7 @@ boot_zle(Module m)
     /* Set up editor entry points */
     trashzleptr = trashzle;
     gotwordptr = gotword;
-    refreshptr = refresh;
+    refreshptr = zrefresh;
     spaceinlineptr = spaceinline;
     zlereadptr = zleread;
 
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index 42953852f..836ff09ae 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -426,7 +426,7 @@ quotedinsert(void)
 #endif
     c = getkey(0);
 #ifndef HAS_TIO
-    setterm();
+    zsetterm();
 #endif
     if (c < 0)
 	feep();
@@ -621,7 +621,7 @@ executenamedcommand(char *prmt)
     for (;;) {
 	*ptr = '_';
 	statusll = l + len + 1;
-	refresh();
+	zrefresh();
 	if (!(cmd = getkeycmd()) || cmd == Th(z_sendbreak)) {
 	    statusline = NULL;
 	    selectkeymap(okeymap, 1);
@@ -633,7 +633,7 @@ executenamedcommand(char *prmt)
 	    redisplay();
 	} else if(cmd == Th(z_viquotedinsert)) {
 	    *ptr = '^';
-	    refresh();
+	    zrefresh();
 	    c = getkey(0);
 	    if(c == EOF || !c || len == NAMLEN)
 		feep();
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index 4621b5124..27e9f2071 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -47,7 +47,7 @@ int nlnct;
 
 /* Most lines of the buffer we've shown at once with the current list *
  * showing.  == 0 if there is no list.  == -1 if a new list has just  *
- * been put on the screen.  == -2 if refresh() needs to put up a new  *
+ * been put on the screen.  == -2 if zrefresh() needs to put up a new *
  * list.                                                              */
 
 /**/
@@ -55,7 +55,7 @@ int showinglist;
 
 /* Non-zero if ALWAYS_LAST_PROMPT has been used, meaning that the *
  * screen below the buffer display should not be cleared by       *
- * refresh(), but should be by trashzle().                        */
+ * zrefresh(), but should be by trashzle().                       */
 
 /**/
 int clearflag;
@@ -75,7 +75,7 @@ int cost;
 #endif
 
 /* Oct/Nov 94: <mason> some code savagely redesigned to fix several bugs -
-   refreshline() & tc_rightcurs() majorly rewritten; refresh() fixed -
+   refreshline() & tc_rightcurs() majorly rewritten; zrefresh() fixed -
    I've put my fingers into just about every routine in here -
    any queries about updates to mason@werple.net.au */
 
@@ -224,7 +224,7 @@ static int cleareol,		/* clear to end-of-line (if can't cleareod) */
 
 /**/
 void
-refresh(void)
+zrefresh(void)
 {
     static int inlist;		/* avoiding recursion                        */
     int canscroll = 0,		/* number of lines we are allowed to scroll  */
@@ -240,7 +240,7 @@ refresh(void)
     char **qbuf;		/* tmp					     */
 
     /* If this is called from listmatches() (indirectly via trashzle()), and *
-     * that was called from the end of refresh(), then we don't need to do   *
+     * that was called from the end of zrefresh(), then we don't need to do  *
      * anything.  All this `inlist' code is actually unnecessary, but it     *
      * improves speed a little in a common case.                             */
     if (inlist)
@@ -263,7 +263,7 @@ refresh(void)
 	termflags &= ~TERM_SHORT;
     if (resetneeded) {
 	onumscrolls = 0;
-	setterm();
+	zsetterm();
 #ifdef TIOCGWINSZ
 	if (winchanged) {
 	    moveto(0, 0);
@@ -547,7 +547,7 @@ individually */
 	inlist = 1;
 	listmatches();
 	inlist = 0;
-	refresh();
+	zrefresh();
     }
     if (showinglist == -1)
 	showinglist = nlnct;
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index c4f2e25e1..f8de14f17 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -244,7 +244,9 @@ unbindwidget(Thingy t, int override)
 static void
 freewidget(Widget w)
 {
-    if(!(w->flags & WIDGET_INT))
+    if ((w->flags & WIDGET_COMP) && w->u.cc)
+	freecompctl(w->u.cc);
+    else if(!(w->flags & WIDGET_INT))
 	zsfree(w->u.fnnam);
     zfree(w, sizeof(*w));
 }
@@ -334,6 +336,7 @@ bin_zle(char *name, char **args, char *ops, int func)
 	{ 'D', bin_zle_del,  1, -1 },
 	{ 'A', bin_zle_link, 2,  2 },
 	{ 'N', bin_zle_new,  1,  2 },
+	{ 'C', bin_zle_compctl, 1, -1},
 	{ 0,   bin_zle_call, 0, -1 },
     };
     struct opn const *op, *opp;
@@ -385,17 +388,24 @@ scanlistwidgets(HashNode hn, int list)
     if(w->flags & WIDGET_INT)
 	return;
     if(list) {
-	fputs("zle -N ", stdout);
+	fputs((w->flags & WIDGET_COMP) ? "zle -C " : "zle -N ", stdout);
 	if(t->nam[0] == '-')
 	    fputs("-- ", stdout);
 	quotedzputs(t->nam, stdout);
-	if(strcmp(t->nam, w->u.fnnam)) {
+	if (w->flags & WIDGET_COMP) {
+	    if (printcompctlptr && w->u.cc)
+		printcompctlptr(NULL, w->u.cc, PRINT_LIST);
+	} else if(strcmp(t->nam, w->u.fnnam)) {
 	    fputc(' ', stdout);
 	    quotedzputs(w->u.fnnam, stdout);
 	}
     } else {
 	nicezputs(t->nam, stdout);
-	if(strcmp(t->nam, w->u.fnnam)) {
+	if (w->flags & WIDGET_COMP) {
+	    fputs(" -C", stdout);
+	    if (printcompctlptr && w->u.cc)
+		printcompctlptr(NULL, w->u.cc, PRINT_TYPE);
+	} else if(strcmp(t->nam, w->u.fnnam)) {
 	    fputs(" (", stdout);
 	    nicezputs(w->u.fnnam, stdout);
 	    fputc(')', stdout);
@@ -458,6 +468,44 @@ bin_zle_new(char *name, char **args, char *ops, char func)
 
 /**/
 static int
+bin_zle_compctl(char *name, char **args, char *ops, char func)
+{
+    Compctl cc = NULL;
+    Widget w;
+    char *wname = args[0];
+
+    if (!compctl_widgetptr) {
+	zwarnnam(name, "compctl module is not loaded", NULL, 0);
+	return 1;
+    }
+
+    args++;
+
+    if (*args && !(cc = compctl_widgetptr(name, args)))
+	return 1;
+
+    w = zalloc(sizeof(*w));
+    w->flags = WIDGET_COMP|ZLE_MENUCMP|ZLE_KEEPSUFFIX;
+    w->first = NULL;
+    w->u.cc = cc;
+    if(bindwidget(w, rthingy(wname))) {
+	freewidget(w);
+	zerrnam(name, "widget name `%s' is protected", wname, 0);
+	return 1;
+    }
+    if (ops['m'])
+	w->flags |= ZLE_USEMENU;
+    else if (ops['M'])
+	w->flags |= ZLE_NOMENU;
+    if (ops['g'])
+	w->flags |= ZLE_USEGLOB;
+    else if (ops['G'])
+	w->flags |= ZLE_NOGLOB;
+    return 0;
+}
+
+/**/
+static int
 bin_zle_call(char *name, char **args, char *ops, char func)
 {
     Thingy t;
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 1aa1a008c..9ffef3b80 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -60,6 +60,7 @@ dopestring;
 # endif
 #endif
 
+
 #define inststr(X) inststrlen((X),1,-1)
 
 /* wb and we hold the beginning/end position of the word we are completing. */
@@ -80,10 +81,11 @@ static int usemenu, useglob;
 
 static int menucmp;
 
-/* A pointer to the current position in the menu-completion array (the one *
- * that was put in the command line last).                                 */
+/* Pointers to the current position in the groups list and in the menu-    *
+ * completion array (the one that was put in the command line last).       */
 
-static char **menucur;
+static Cmgroup menugrp;
+static Cmatch *menucur;
 
 /* menupos is the point (in the command line) where the menu-completion   *
  * strings are inserted.  menulen is the length of the string that was    *
@@ -94,29 +96,26 @@ static char **menucur;
 
 static int menupos, menulen, menuend, menuwe, menuinsc;
 
-/* This is used as a flag from get_comp_string() that we are doing *
- * completion inside a brace expansion.                            */
+/* 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.                        */
 
-static int complinbrace;
+static char *brbeg = NULL, *brend = NULL;
+static int brpl, brsl;
 
 /* The list of matches.  fmatches contains the matches we first ignore *
  * because of fignore.                                                 */
 
 static LinkList matches, fmatches;
 
-/* The list of matches turned into an array.  This is used to sort this *
- * list and when menu-completion is used (directly or via automenu).    */
-
-static char **amatches;
+/* This holds the list of matches-groups. lmatches is a pointer to the  *
+ * last element in this list. */
 
-/* The number of matches. */
+static Cmgroup amatches, lmatches;
 
-static int nmatches;
+/* The total number of matches and the number of matches to be listed. */
 
-/* A list of user-defined explanations for the completions to be shown *
- * instead of amatches when listing completions.                       */
-
-static char **aylist;
+static int nmatches, smatches;
 
 /* !=0 if we have a valid completion list. */
 
@@ -132,68 +131,112 @@ static int ispattern;
 
 static Comp patcomp, filecomp;
 
-/* We store the following prefixes/suffixes:                             *
- * lpre/lsuf -- what's on the line                                       *
- * rpre/rsuf -- same as lpre/lsuf, but expanded                          *
- *                                                                       *
- * ... and if we are completing files, too:                              *
- * ppre/psuf -- the path prefix/suffix                                   *
- * fpre/fsuf -- prefix/suffix of the pathname component the cursor is in *
- * prpre     -- ppre in expanded form usable for opendir                 *
- *                                                                       *
- * The integer variables hold the lengths of lpre, lsuf, rpre, rsuf,     *
- * fpre, and fsuf.  noreal is non-zero if we have rpre/rsuf.             */
+/* We store the following prefixes/suffixes:                               *
+ * lpre/lsuf -- what's on the line                                         *
+ * rpre/rsuf -- same as lpre/lsuf, but expanded                            *
+ *                                                                         *
+ * ... and if we are completing files, too:                                *
+ * ppre/psuf   -- the path prefix/suffix                                   *
+ * lppre/lpsuf -- the path prefix/suffix, unexpanded                       *
+ * fpre/fsuf   -- prefix/suffix of the pathname component the cursor is in *
+ * prpre       -- ppre in expanded form usable for opendir                 *
+ * ipre,ripre  -- the ignored prefix (quotes and unquoted)                 *
+ *                                                                         *
+ * The integer variables hold the lengths of lpre, lsuf, rpre, rsuf,       *
+ * fpre, fsuf, lppre, and lpsuf.  noreal is non-zero if we have rpre/rsuf. */
 
 static char *lpre, *lsuf;
 static char *rpre, *rsuf;
-static char *ppre, *psuf, *prpre;
+static char *ppre, *psuf, *lppre, *lpsuf, *prpre;
 static char *fpre, *fsuf;
-static int lpl, lsl, rpl, rsl, fpl, fsl;
+static char *ipre, *ripre;
+static int lpl, lsl, rpl, rsl, fpl, fsl, lppl, lpsl;
 static int noreal;
 
-/* This is used when completing after `$' and holds the whole prefix,   *
- * used in do_single() to check whether the word expands to a directory *
- * name (in that case and if autoparamslash is set, we add a `/').      *
- * qparampre is the same but quoted. The length of it is in qparprelen. *
- * parambr is != 0 if the parameter name is in braces.                  */
-
-static char *parampre = NULL, *qparampre = NULL;
-static int qparprelen, parambr;
-
 /* This is either zero or equal to the special character the word we are *
  * trying to complete starts with (e.g. Tilde or Equals).                */
 
 static char ic;
 
-/* These hold the minimum common prefix/suffix lengths (normal and for *
- * fignore ignored).                                                   */
-
-static int ab, ae, fab, fae;
-
 /* This variable says what we are currently adding to the list of matches. */
 
 static int addwhat;
 
-/* firstm hold the first match we found, shortest contains the shortest *
- * one (normal and for fignore ignored).                                */
-
-static char *firstm, *shortest, *ffirstm, *fshortest;
-
 /* This holds the word we are completing in quoted from. */
 
 static char *qword;
 
-/* This is the length of the shortest match we found (normal and for *
- * fignore ignored).                                                 */
-
-static int shortl, fshortl;
-
 /* This is non-zero if we are doing a menu-completion and this is not the *
  * first call (e.g. when automenu is set and menu-completion was entered  *
  * due to this). */
 
 static int amenu;
 
+/* The current group of matches. */
+
+static Cmgroup mgroup;
+
+/* A match counter. */
+
+static int mnum;
+
+/* Match flags for all matches in this group. */
+
+static int mflags;
+
+/* This holds the explanation strings we have to print in this group and *
+ * a pointer to the current cexpl structure. */
+
+static LinkList expls;
+static Cexpl expl;
+
+/* A pointer to the compctl we are using. */
+
+static Compctl curcc;
+
+/* A list of all compctls we have already used. */
+
+static LinkList ccused;
+
+/* A list of all compctls used so far. */
+
+static LinkList allccs;
+
+/* A stack of currently used compctls. */
+
+static LinkList ccstack;
+
+/* A stack of completion matchers to be used. */
+
+static Cmlist mstack;
+
+/* A heap of free Cline structures. */
+
+static Cline freecl;
+
+/* Information for ambiguous completions. One for fignore ignored and   *
+ * one for normal completion. */
+
+typedef struct aminfo *Aminfo;
+
+struct aminfo {
+    int cpl, csl, icpl, icsl;	/* common prefix/suffix lengths           */
+    int prerest;		/* minimum prefix rest                    */
+    int suflen;			/* minimum suffix length                  */
+    Cmatch firstm;		/* the first match                        */
+    char *pprefix;		/* common part of the -P prefixes         */
+    char *aprefix;		/* common line prefix                     */
+    int noipre;			/* if the was no ignored prefix           */
+    char *iprefix;		/* common ignored prefix                  */
+    char *iaprefix;		/* like aprefix, without ignored prefixes */
+    int exact;			/* if there was an exact match            */
+    Cmatch exactm;		/* the exact match (if any)               */
+    Cline linecl, ilinecl;	/* what to put on the line as a Cline     */
+    int count;			/* number of matches                      */
+};
+
+static Aminfo ainfo, fainfo;
+
 /* Find out if we have to insert a tab (instead of trying to complete). */
 
 /**/
@@ -208,16 +251,29 @@ usetab(void)
     return 1;
 }
 
-#define COMP_COMPLETE 0
-#define COMP_LIST_COMPLETE 1
-#define COMP_SPELL 2
-#define COMP_EXPAND 3
-#define COMP_EXPAND_COMPLETE 4
-#define COMP_LIST_EXPAND 5
+enum { COMP_COMPLETE,
+       COMP_WIDGET,
+       COMP_LIST_COMPLETE,
+       COMP_SPELL,
+       COMP_EXPAND,
+       COMP_EXPAND_COMPLETE,
+       COMP_LIST_EXPAND };
 #define COMP_ISEXPAND(X) ((X) >= COMP_EXPAND)
 
 /**/
 void
+completespecial(void)
+{
+    int flags = compwidget->flags;
+    usemenu = (flags & ZLE_USEMENU) ? 1 : (flags & ZLE_NOMENU) ? 0
+	: isset(MENUCOMPLETE);
+    useglob = (flags & ZLE_USEGLOB) ? 1 : (flags & ZLE_NOGLOB) ? 0
+	: isset(GLOBCOMPLETE);
+    docomplete(compwidget->u.cc ? COMP_WIDGET : COMP_COMPLETE);
+}
+
+/**/
+void
 completeword(void)
 {
     usemenu = isset(MENUCOMPLETE);
@@ -261,16 +317,19 @@ spellword(void)
 void
 deletecharorlist(void)
 {
-    char **mc = menucur;
+    Cmgroup mg = menugrp;
+    Cmatch *mc = menucur;
 
     usemenu = isset(MENUCOMPLETE);
     useglob = isset(GLOBCOMPLETE);
-    if (cs != ll)
+    if (cs != ll) {
+	fixsuffix();
 	deletechar();
-    else
+    } else
 	docomplete(COMP_LIST_COMPLETE);
 
     menucur = mc;
+    menugrp = mg;
 }
 
 /**/
@@ -326,8 +385,13 @@ reversemenucomplete(void)
 	return;
     }
     HEAPALLOC {
-	if (menucur == amatches)
-	    menucur = amatches + nmatches - 1;
+	if (menucur == menugrp->matches) {
+	    do {
+		if (!(menugrp = menugrp->prev))
+		    menugrp = lmatches;
+	    } while (!menugrp->mcount);
+	    menucur = menugrp->matches + menugrp->mcount - 1;
+	}
 	else
 	    menucur--;
 	metafy_line();
@@ -350,16 +414,8 @@ acceptandmenucomplete(void)
     }
     cs = menuend + menuinsc;
     inststrlen(" ", 1, 1);
-    if (qparampre)
-	inststrlen(qparampre, 1, qparprelen);
-    if (lpre && !ispattern)
-	inststrlen(lpre, 1, -1);
-    if (lsuf && !ispattern)
-	inststrlen(lsuf, 0, -1);
+    menuinsc = menulen = 0;
     menupos = cs;
-    menuend = cs + (lsuf ? strlen(lsuf) : 0);
-    menulen = 0;
-    menuinsc = 0;
     menuwe = 1;
     menucomplete();
 }
@@ -378,34 +434,7 @@ static int lincmd, linredir;
 
 static int lastambig;
 
-/* This describes some important things collected during the last *
- * completion.  Its value is zero or the inclusive OR of some of  *
- * the HAS_* things below.                                        */
-
-static int haswhat;
-
-/* We have a suffix to add (given with compctl -S). */
-
-#define HAS_SUFFIX  1
-
-/* We have filenames in the completion list. */
-
-#define HAS_FILES   2
-
-/* We have other things than files in the completion list.  If this is *
- * not set but HAS_FILES is, we probably put the file type characters  *
- * in the completion list (if listtypes is set) and we attempt to add  *
- * a slash to completed directories.                                   */
-
-#define HAS_MISC    4
-
-/* This is set if we have filenames in the completion list that were *
- * generated by a globcompletion pattern.                            */
-
-#define HAS_PATHPAT 8
-
-
-/* This holds the naem of the current command (used to find the right *
+/* This holds the name of the current command (used to find the right *
  * compctl).                                                          */
 
 static char *cmdstr;
@@ -580,7 +609,7 @@ docomplete(int lst)
 	    if (*q == Equals) {
 		/* The word starts with `=', see if we can expand it. */
 		q = s + 1;
-		if (cmdnamtab->getnode(cmdnamtab, q) || hashcmd(q, pathchecked))
+		if (cmdnamtab->getnode(cmdnamtab, q) || hashcmd(q, pathchecked)) {
 		    if (isset(RECEXACT))
 			lst = COMP_EXPAND;
 		    else {
@@ -602,6 +631,7 @@ docomplete(int lst)
 			if (n == 1)
 			    lst = COMP_EXPAND;
 		    }
+		}
 	    }
 	    if (lst == COMP_EXPAND_COMPLETE)
 		do {
@@ -710,16 +740,17 @@ docomplete(int lst)
 		if (*p == Tilde || *p == Equals)
 		    p++;
 		for (; *p; p++)
-		    if (itok(*p))
+		    if (itok(*p)) {
 			if (*p != String && *p != Qstring)
 			    *p = ztokens[*p - Pound];
 			else if (p[1] == Inbrace)
 			    p++, skipparens(Inbrace, Outbrace, &p);
-		docompletion(s, lst, lincmd, 1);
+		    }
+		docompletion(s, lst, lincmd);
 	    }
 	} else
 	    /* Just do completion. */
-	    docompletion(s, lst, lincmd, 0);
+	    docompletion(s, lst, lincmd);
 	zsfree(s);
     }
     /* Reset the lexer state, pop the heap. */
@@ -745,8 +776,13 @@ do_menucmp(int lst)
     }
     /* Otherwise go to the next match in the array... */
     HEAPALLOC {
-	if (!*++menucur)
-	    menucur = amatches;
+	if (!*++menucur) {
+	    do {
+		if (!(menugrp = menugrp->next))
+		    menugrp = amatches;
+	    } while (!menugrp->mcount);
+	    menucur = menugrp->matches;
+	}
 	/* ... and insert it into the command line. */
 	metafy_line();
 	do_single(*menucur);
@@ -754,9 +790,6 @@ do_menucmp(int lst)
     } LASTALLOC;
 }
 
-/* 1 if we are completing in a string */
-static int instring;
-
 /* 1 if we are completing the prefix */
 static int comppref;
 
@@ -859,7 +892,9 @@ get_comp_string(void)
     int t0, tt0, i, j, k, cp, rd, sl, ocs;
     char *s = NULL, *linptr, *tmp, *p, *tt = NULL;
 
-    complinbrace = 0;
+    zsfree(brbeg);
+    zsfree(brend);
+    brbeg = brend = NULL;
     /* This global flag is used to signal the lexer code if it should *
      * expand aliases or not.                                         */
     noaliases = isset(COMPLETEALIASES);
@@ -1167,6 +1202,12 @@ get_comp_string(void)
 	    if (tt && tt < s + myoffs) {
 		/* Braces are go:  delete opening brace */
 		char *com = NULL;
+		int pl, sl;
+
+		brbeg = dupstring(tt);
+		brpl = tt - s;
+		pl = 1;
+		sl = 0;
 		chuck(tt);
 		offs--;
 		myoffs--;
@@ -1177,25 +1218,33 @@ get_comp_string(void)
 			com = p;
 		if (com) {
 		    i = com - tt + 1;
-		    while (i--)
-			chuck(tt), offs--, myoffs--;
+		    offs -= i;
+		    myoffs -= i;
+		    strcpy(tt, tt + i);
+		    pl += i;
 		}
+		brbeg[pl] = '\0';
 
 		/* Look for text between subsequent comma
 		 * and closing brace or end of string and delete it
 		 */
-		for (p = s + myoffs; *p && *p != Outbrace; p++)
-		    if (*p == Comma) {
-			while (*p && *p != Outbrace)
-			    chuck(p);
-			break;
+		for (p = s + myoffs; *p && *p != Outbrace && *p != Comma; p++);
+		if (*p == Comma || *p == Outbrace) {
+		    brend = dupstring(p);
+		    sl = 1;
+		    while (*p && *p != Outbrace) {
+			chuck(p); sl++;
 		    }
-		if (*p == Outbrace)
-		    chuck(p);
-		else {
-		    /* we are still waiting for an outbrace and maybe commas */
-		    complinbrace = 1;
+		    if (*p == Outbrace)
+			chuck(p);
+		    brsl = strlen(s) - (p - s);
+		    brend[sl] = '\0';
 		}
+		/* we are still waiting for an outbrace and maybe commas */
+		if (brbeg)
+		    untokenize(brbeg = ztrdup(brbeg));
+		if (brend)
+		    untokenize(brend = ztrdup(brend));
 	    }
 	}
 
@@ -1245,7 +1294,7 @@ doexpansion(char *s, int lst, int olst, int explincmd)
 	    /* If expansion didn't change the word, try completion if *
 	     * expandorcomplete was called, otherwise, just beep.     */
 	    if (lst == COMP_EXPAND_COMPLETE)
-		docompletion(s, COMP_COMPLETE, explincmd, 0);
+		docompletion(s, COMP_COMPLETE, explincmd);
 	    else
 		feep();
 	    goto end;
@@ -1292,6 +1341,931 @@ gotword(void)
     }
 }
 
+/* This adds a string to the currently built match. The last argument  *
+ * is non zero if we are building the suffix, where we have to prepend *
+ * the given string. */
+
+static char *
+addtoword(char **rwp, int *rwlenp, char *nw,
+	  Cmatcher m, char *l, char *w, int wl, int prep)
+{
+    int al, rwlen = *rwlenp, nl;
+    char *as, *rw = *rwp;
+    
+    if (m && (m->flags & CMF_LINE)) {
+	al = m->llen;
+	as = l;
+    } else {
+	al = wl;
+	as = w;
+    }
+    if (!rwlen || (nl = al + (nw - rw)) >= rwlen) {
+	char *np;
+
+	if (!rwlen)
+	    nl = al + 20;
+
+	np = (char *) zalloc(nl + 1);
+
+	*rwlenp = nl;
+	if (rwlen) {
+	    memcpy(np, rw, rwlen);
+	    nw += np - rw;
+	    zfree(rw, rwlen);
+	}
+	else
+	    nw = np;
+	*rwp = rw = np;
+	rwlen = nl;
+    }
+    if (prep) {
+	memmove(rw + al, rw, rwlen - al);
+	memcpy(rw, as, al);
+    }
+    else
+	memcpy(nw, as, al);
+
+    return nw + al;
+}
+
+/* This get a new Cline structure. */
+
+static Cline
+getcline(char *l, int ll, char *w, int wl, Cmatcher m, int fl)
+{
+    Cline r;
+
+    if ((r = freecl))
+	freecl = r->next;
+    else
+	r = (Cline) halloc(sizeof(*r));
+
+    r->next = NULL;
+    r->line = l;
+    r->llen = ll;
+    r->word = w;
+    r->wlen = wl;
+    r->matcher = m;
+    r->flags = fl;
+
+    return r;
+}
+
+/* This add a Cline structure with the given parameters. */
+
+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);
+
+    if (m && (m->flags & CMF_LINE))
+	ln->word = NULL;
+    if (*retp)
+	(*lrp)->next = ln;
+    else
+	*retp = ln;
+
+    *lrp = ln;
+}
+
+/* Joins two Cline lists, building the most specific line string *
+ * that is possible. */
+
+static Cline
+join_clines(Cline o, Cline n)
+{
+    Cline oo = o;
+
+    if (!o)
+	return n;
+    else {
+	Cline f = freecl, q, op = NULL;
+	int ol, nl;
+
+	while (o && n) {
+	    if (o->flags & CLF_MID) {
+		while (n && !(n->flags & CLF_MID)) {
+		    q = n->next;
+		    n->next = f;
+		    f = n;
+
+		    n = q;
+		}
+	    }
+	    if (n->flags & CLF_MID) {
+		while (o && !(o->flags & CLF_MID)) {
+		    o->word = NULL;
+		    o->flags |= CLF_DIFF;
+
+		    o = o->next;
+		}
+	    }
+	    if (o && n && !((o->flags | n->flags) & CLF_MID)) {
+		ol = o->llen;
+		nl = n->llen;
+
+		while (o && n && ol != nl) {
+		    /* The matched strings have different lengths, so    *
+		     * continue walking the lists until we have the same *
+		     * matched lengths. */
+		    o->word = NULL;
+		    o->flags |= CLF_DIFF;
+		    if (ol < nl) {
+			op = o;
+			if ((o = o->next))
+			    ol += o->llen;
+		    } else {
+			q = n->next;
+			n->next = f;
+			f = n;
+
+			if ((n = q))
+			    nl += n->llen;
+		    }
+		}
+	    }
+	    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;
+	    } 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.    *
+			 * 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;
+		    } else if (o->wlen == n->wlen) {
+			/* Otherwise keep them if they are equal. */
+			if (strncmp(o->word, n->word, o->wlen)) {
+			    /* If they are not equal, we make them *
+			     * be left unchanged on the line. */
+			    o->word = NULL;
+			    o->flags |= CLF_DIFF;
+			}
+		    } else {
+			o->word = NULL;
+			o->flags |= CLF_DIFF;
+		    }
+		}
+		else {
+		    o->word = NULL;
+		    o->flags |= CLF_DIFF;
+		}
+	    }
+	    else if (n->word)
+		o->flags |= CLF_DIFF;
+
+	    q = n->next;
+	    n->next = f;
+	    f = n;
+
+	    n = q;
+	    op = o;
+	    o = o->next;
+	}
+	if (o) {
+	    for (q = o; q->next; q = q->next);
+
+	    q->next = f;
+	    f = o;
+
+	    if (op)
+		op->next = NULL;
+	    else
+		return NULL;
+	}
+	if (n) {
+	    /* We always put the chunks from the second list back on *
+	     * the free list. */
+	    for (q = n; q->next; q = q->next);
+
+	    q->next = f;
+	    f = n;
+	}
+	freecl = f;
+    }
+    return oo;
+}
+
+/* This returns a Cline for the given string. */
+
+static Cline
+str_cline(char *s, int l, Cline *lp)
+{
+    Cline r = NULL, *p = &r, n = NULL;
+
+    if (l < 0)
+	l = strlen(s);
+    while (l) {
+	*p = n = getcline(s, 1, NULL, 0, NULL, 0);
+
+	p = &(n->next);
+	s++;
+	l--;
+    }
+    if (lp)
+	*lp = n;
+
+    return r;
+}
+
+/* This reverts the order of the chunks. */
+
+static Cline
+revert_clines(Cline p)
+{
+    Cline r = NULL, n;
+
+    while (p) {
+	n = p->next;
+	p->next = r;
+	r = p;
+	p = n;
+    }
+    return r;
+}
+
+/* Prepend a string to a Cline. */
+
+static Cline
+prepend_cline(char *s, Cline l)
+{
+    Cline n, r = str_cline(s, -1, &n), *p = &(n->next);
+
+    while (l) {
+	*p = n = getcline(l->line, l->llen, l->word, l->wlen,
+			  l->matcher, l->flags);
+
+	p = &(n->next);
+	l = l->next;
+    }
+    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  *
+ * and the suffix length. */
+
+static void
+merge_cline(Cline lc, char *p, int pl, char *s, int sl, int psl)
+{
+    int pll, sll;
+    Cline l = NULL, *q = &l, n;
+
+    pll = strlen(p);
+    if (s) {
+	int i = strlen(s);
+
+	if (ainfo->suflen < 10000)
+	    s = s + i - ainfo->suflen;
+	else {
+	    s = s + i - sl;
+	    p[pll - (sl - psl)] = '\0';
+	    pll -= sl - psl;
+	}
+	sll = strlen(s) - sl;
+    }
+    else
+	sll = 0;
+
+    pll -= pl;
+
+    l = str_cline(p, pl, &n);
+    q = &(n->next);
+    if (!sl)
+	*q = getcline(NULL, 0, p + pl, pll, NULL, CLF_END);
+    else {
+	*q = n = getcline(p + pl, pll, s, sll, NULL, CLF_MID);
+
+	n->next = str_cline(s + sll, sl, NULL);
+    }
+    join_clines(lc, l);
+}
+
+/* This inserts the Cline built into the command line. */
+
+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;
+    pl += brpl;
+
+    i = cs - wb;
+    while (l) {
+	if (d < 0 && (l->flags & CLF_DIFF))
+	    d = cs;
+	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)
+		b = cs;
+	    if (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);
+	}
+	if (m < 0 && (l->flags & CLF_MISS))
+	    m = cs;
+	i += l->llen;
+	if (pl >= 0 && i >= pl && brbeg && *brbeg) {
+	    inststrlen(brbeg, 1, -1);
+	    pl = -1;
+	    hb = 1;
+	}
+	if (sl >= 0 && i >= sl && brend && *brend) {
+	    inststrlen(brend, 1, -1);
+	    sl = -1;
+	    hb = 1;
+	}
+	l = l->next;
+    }
+    cs = (b >= 0 && hb ? b : (m >= 0 ? m : (d >= 0 ? d : cs)));
+    if (cs > ll)
+	cs = ll;
+}
+
+/* Check if the given pattern matches the given string.             *
+ * `in' and `out' are used for {...} classes. In `out' we store the *
+ * character number that was matched. In the word pattern this is   *
+ * 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)
+{
+    unsigned char c;
+
+    while (p) {
+	c = *((unsigned char *) s);
+
+	if (out) *out = 0;
+
+	if (p->equiv) {
+	    if (in) {
+		c = p->tab[c];
+		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;
+	}
+
+	s++;
+	p = p->next;
+    }
+    return 1;
+}
+
+/* Do the matching for a prefix. */
+
+static char *
+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 il = 0, iw = 0, t, stil, stiw, std, bc = brpl;
+    char *nw = rw, *stl = NULL, *stw;
+    Cmlist ms;
+    Cmatcher mp, stm;
+    Cline lr = NULL;
+
+    *nlp = NULL;
+
+    if (ll > ealen) {
+	/* This is the `in'/`out' string for pattern matching. */
+	if (ealen)
+	    zfree(ea, ealen);
+	ea = (unsigned char *) zalloc(ealen = ll + 20);
+    }
+    while (ll && lw) {
+	if (*l == *w) {
+	    /* Same character, take it. */
+
+	    if (stl) {
+		/* But first check, if we were collecting characters *
+		 * for a `*'. */
+		int sl = iw - stiw;
+
+		nw = addtoword(&rw, &rwlen, nw, stm, stl, stw, sl, 0);
+
+		addtocline(nlp, &lr, stl, stm->llen,
+			   stw, sl, stm, (std ? CLF_SUF : 0));
+
+		stl = NULL;
+
+		if (bc <= 0 && bplp) {
+		    *bplp = nw - rw;
+		    bplp = NULL;
+		}
+	    }
+	    nw = addtoword(&rw, &rwlen, nw, NULL, NULL, l, 1, 0);
+
+	    addtocline(nlp, &lr, l, 1, NULL, 0, NULL, 0);
+
+	    l++;
+	    w++;
+	    il++;
+	    iw++;
+	    ll--;
+	    lw--;
+	    bc--;
+
+	    if (bc <= 0 && bplp) {
+		*bplp = nw - rw;
+		bplp = NULL;
+	    }
+	    continue;
+	}
+	for (ms = mstack; ms; ms = ms->next) {
+	    for (mp = ms->matcher; mp; mp = mp->next) {
+		t = 1;
+		/* Try to match the prefix, if any. */
+		if (mp->flags & CMF_LEFT) {
+		    if (il < mp->lalen || iw < mp->lalen)
+			t = 0;
+		    else if (mp->left)
+			t = pattern_match(mp->left, l - mp->lalen, NULL, NULL) &&
+			    pattern_match(mp->left, w - mp->lalen, NULL, NULL);
+		    else
+			t = (!il && !iw);
+		}
+		if (t) {
+		    /* Now match the line pattern. */
+		    if (ll < mp->llen || lw < mp->wlen)
+			t = 0;
+		    else if (mp->wlen < 0) {
+			/* 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 (t && !stl) {
+				/* We simply keep the current position   *
+				 * and start collecting characters until *
+				 * another matcher matches. */
+				std = (mp->flags & CMF_LEFT);
+				stl = l;
+				stil = il;
+				stw = w;
+				stiw = iw;
+				stm = mp;
+				t = 0;
+				l += mp->llen;
+				il += mp->llen;
+				ll -= mp->llen;
+				
+				break;
+			    }
+			    else
+				t = 0;
+			}
+		    } else {
+			/* No `*', just try to match the line and word *
+			 * patterns. */
+			t = pattern_match(mp->line, l, NULL, ea) &&
+			    pattern_match(mp->word, w, ea, NULL);
+			mlw = mp->wlen;
+		    }
+		}
+		/* Now test the right anchor, if any. */
+		if (t && (mp->flags & CMF_RIGHT)) {
+		    if (ll < mp->llen + mp->ralen || lw < mlw + mp->ralen)
+			t = 0;
+		    else if (mp->right)
+			t = pattern_match(mp->right, l + mp->llen, NULL, NULL) &&
+			    pattern_match(mp->right, w + mlw, NULL, NULL);
+		    else
+			t = 0;
+		}
+		if (t) {
+		    /* If it matched, build a new chunk on the Cline list *
+		     * and add the string to the built match. */
+		    if (stl) {
+			int sl = iw - stiw;
+			
+			nw = addtoword(&rw, &rwlen, nw, stm, stl, stw, sl, 0);
+			
+			addtocline(nlp, &lr, 
+				   stl, stm->llen, stw, sl, stm,
+				   (std ? CLF_SUF : 0));
+			
+			stl = NULL;
+
+			if (bc <= 0 && bplp) {
+			    *bplp = nw - rw;
+			    bplp = NULL;
+			}
+		    }
+		    nw = addtoword(&rw, &rwlen, nw, mp, l, w, mlw, 0);
+		    
+		    addtocline(nlp, &lr, l, mp->llen, w, mlw, mp, 0);
+		    
+		    l += mp->llen;
+		    w += mlw;
+		    ll -= mp->llen;
+		    lw -= mlw;
+		    il += mp->llen;
+		    iw += mlw;
+		    bc -= mp->llen;
+
+		    if (bc <= 0 && bplp) {
+			*bplp = nw - rw;
+			bplp = NULL;
+		    }
+		    break;
+		}
+	    }
+	    if (mp)
+		break;
+	}
+	if (!stl && !t) {
+	    if (*nlp) {
+		lr->next = freecl;
+		freecl = *nlp;
+	    }
+	    return NULL;
+	}
+	if (stl) {
+	    /* We are collecting characters, just skip over. */
+	    w++;
+	    lw--;
+	    iw++;
+	}
+    }
+    *lp = iw;
+    if (lw) {
+	/* There is a unmatched portion in the word, keep it. */
+	if (rlp) {
+	    w = dupstring(w);
+	    addtocline(nlp, &lr, w, lw, w, -1, NULL, CLF_MID);
+
+	    *rlp = lr;
+	} else {
+	    addtocline(nlp, &lr, l, 0, dupstring(w), lw, NULL, CLF_END);
+
+	    nw = addtoword(&rw, &rwlen, nw, NULL, NULL, w, lw, 0);
+	}
+    }
+    else if (rlp) {
+	if (lr) {
+	    lr->next = freecl;
+	    freecl = *nlp;
+	}
+	return NULL;
+    }
+    if (nw)
+	*nw = '\0';
+
+    if (ll) {
+	if (*nlp) {
+	    lr->next = freecl;
+	    freecl = *nlp;
+	}
+	return 0;
+    }
+    /* Finally, return the built match string. */
+    return dupstring(rw);
+}
+
+/* Do the matching for a suffix. */
+
+static char *
+match_sfx(char *l, char *w, Cline *nlp, int *lp, int *bslp)
+{
+    static unsigned char *ea;
+    static int ealen = 0;
+    static char *rw;
+    static int rwlen;
+
+    int ll = strlen(l), lw = strlen(w), mlw;
+    int il = 0, iw = 0, t, stil, stiw, std, bc = brsl;
+    char *nw = rw, *stl = NULL, *stw;
+    Cmlist ms;
+    Cmatcher mp, stm;
+    Cline lr = NULL;
+
+    l += ll;
+    w += lw;
+
+    *nlp = NULL;
+
+    if (ll > ealen) {
+	if (ealen)
+	    zfree(ea, ealen);
+	ea = (unsigned char *) zalloc(ealen = ll + 20);
+    }
+    while (ll && lw) {
+	if (l[-1] == w[-1]) {
+	    if (stl) {
+		int sl = iw - stiw;
+
+		stl -= stm->llen;
+		stw -= sl;
+		nw = addtoword(&rw, &rwlen, nw, stm, stl, stw, sl, 1);
+
+		addtocline(nlp, &lr, stl, stm->llen,
+			   stw, sl, stm, (std ? CLF_SUF : 0));
+
+		stl = NULL;
+
+		if (bc <= 0 && bslp) {
+		    *bslp = nw - rw;
+		    bslp = NULL;
+		}
+	    }
+	    nw = addtoword(&rw, &rwlen, nw, NULL, NULL, l - 1, 1, 1);
+
+	    addtocline(nlp, &lr, l - 1, 1, NULL, 0, NULL, 0);
+
+	    l--;
+	    w--;
+	    il++;
+	    iw++;
+	    ll--;
+	    lw--;
+	    bc--;
+	    if (bc <= 0 && bslp) {
+		*bslp = nw - rw;
+		bslp = NULL;
+	    }
+	    continue;
+	}
+	for (ms = mstack; ms; ms = ms->next) {
+	    for (mp = ms->matcher; mp; mp = mp->next) {
+		t = 1;
+		if (mp->flags & CMF_RIGHT) {
+		    if (il < mp->ralen || iw < mp->ralen)
+			t = 0;
+		    else if (mp->right)
+			t = pattern_match(mp->right, l, NULL, NULL) &&
+			    pattern_match(mp->right, w, NULL, NULL);
+		    else
+			t = (!il && !iw);
+		}
+		if (t) {
+		    if (ll < mp->llen || lw < mp->wlen)
+			t = 0;
+		    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 (t && !stl) {
+				std = (mp->flags & CMF_LEFT);
+				stl = l;
+				stil = il;
+				stw = w;
+				stiw = iw;
+				stm = mp;
+				t = 0;
+				l -= mp->llen;
+				il += mp->llen;
+				ll -= mp->llen;
+				
+				break;
+			    }
+			    else
+				t = 0;
+			}
+		    } else {
+			t = pattern_match(mp->line, l - mp->llen, NULL, ea) &&
+			    pattern_match(mp->word, w - mp->wlen, ea, NULL);
+			mlw = mp->wlen;
+		    }
+		}
+		if (t && (mp->flags & CMF_LEFT)) {
+		    if (ll < mp->llen + mp->lalen || lw < mlw + mp->lalen)
+			t = 0;
+		    else if (mp->left)
+			t = pattern_match(mp->right, l - mp->llen - mp->lalen,
+					  NULL, NULL) &&
+			    pattern_match(mp->right, w - mlw - mp->lalen,
+					  NULL, NULL);
+		    else
+			t = 0;
+		}
+		if (t) {
+		    if (stl) {
+			int sl = iw - stiw;
+			
+			stl -= stm->llen;
+			stw -= sl;
+			
+			nw = addtoword(&rw, &rwlen, nw, stm, stl, stw, sl, 1);
+			
+			addtocline(nlp, &lr,
+				   stl, stm->llen, stw, sl, stm,
+				   (std ? CLF_SUF : 0));
+			
+			stl = NULL;
+
+			if (bc <= 0 && bslp) {
+			    *bslp = nw - rw;
+			    bslp = NULL;
+			}
+		    }
+		    nw = addtoword(&rw, &rwlen, nw, mp, l, w, mlw, 1);
+		    
+		    addtocline(nlp, &lr, l - mp->llen, mp->llen,
+			       w - mlw, mlw, mp, 0);
+		    
+		    l -= mp->llen;
+		    w -= mlw;
+		    ll -= mp->llen;
+		    lw -= mlw;
+		    il += mp->llen;
+		    iw += mlw;
+		    bc -= mp->llen;
+		    if (bc <= 0 && bslp) {
+			*bslp = nw - rw;
+			bslp = NULL;
+		    }
+		    break;
+		}
+	    }
+	    if (mp)
+		break;
+	}
+	if (!stl && !t) {
+	    if (*nlp) {
+		lr->next = freecl;
+		freecl = *nlp;
+	    }
+	    return NULL;
+	}
+	if (stl) {
+	    w--;
+	    lw--;
+	    iw++;
+	}
+    }
+    *lp = iw;
+    if (nw)
+	*nw = '\0';
+
+    if (ll) {
+	if (*nlp) {
+	    lr->next = freecl;
+	    freecl = *nlp;
+	}
+	return 0;
+    }
+    return dupstring(rw);
+}
+
+/* Check if the word `w' matches. */
+
+static char *
+comp_match(char *pfx, char *sfx, char *w, Cline *clp, int qu, int *bpl, int *bsl)
+{
+    char *r = NULL;
+    Cline pli;
+    int pl;
+
+    if (qu)
+	w = quotename(w, NULL, NULL, NULL);
+    if (*sfx) {
+	char *p, *s;
+	int sl;
+	Cline sli, last;
+
+	if ((p = match_pfx(pfx, w, &pli, &pl, &last, bpl))) {
+	    if ((s = match_sfx(sfx, w + pl, &sli, &sl, bsl))) {
+		int pml, sml;
+
+		last->llen -= sl;
+		last->next = revert_clines(sli);
+
+		pml = strlen(p);
+		sml = strlen(s);
+		r = (char *) halloc(pml + sml + last->llen + 1);
+		strcpy(r, p);
+		strncpy(r + pml, last->line, last->llen);
+		strcpy(r + pml + last->llen, s);
+	    } else {
+		last->next = freecl;
+		freecl = pli;
+
+		return NULL;
+	    }
+	}
+	else
+	    return NULL;
+    } else if (!(r = match_pfx(pfx, w, &pli, &pl, NULL, bpl)))
+	return NULL;
+
+    if (lppre && *lppre) {
+	Cline l, t = str_cline(lppre, -1, &l);
+
+	l->next = pli;
+	pli = t;
+    }
+    if (lpsuf && *lpsuf) {
+	Cline n, t = str_cline(lpsuf, -1, NULL);
+
+	if ((n = pli)) {
+	    while (n->next) n = n->next;
+
+	    n->next = t;
+	} else
+	    pli = t;
+    }
+    *clp = pli;
+
+    return r;
+}
+
 /* Insert the given string into the command line.  If move is non-zero, *
  * the cursor position is changed and len is the length of the string   *
  * to insert (if it is -1, the length is calculated here).              */
@@ -1310,69 +2284,59 @@ inststrlen(char *str, int move, int len)
 	cs += len;
 }
 
-/* Quote the string s and return the result.  If e is non-zero, it the    *
- * pointer it points to may point to aposition in s and in e the position *
- * of the corresponding character in the quoted string is returned.  Like *
- * e, te may point to a position in the string and pl is used to return   *
- * the position of the character pointed to by te in the quoted string.   *
- * The string is metafied and may contain tokens.                         */
+/* Insert the given match. This returns the number of characters inserted.*/
 
 /**/
-static char *
-quotename(const char *s, char **e, char *te, int *pl)
-{
-    const char *u, *tt;
-    char *v, buf[PATH_MAX * 2];
-    int sf = 0;
-
-    tt = v = buf;
-    u = s;
-    for (; *u; u++) {
-	if (e && *e == u)
-	    *e = v, sf |= 1;
-	if (te == u)
-	    *pl = v - tt, sf |= 2;
-	if (ispecial(*u) &&
-	    (!instring || (isset(BANGHIST) &&
-			   *u == (char)bangchar) ||
-	     (instring == 2 &&
-	      (*u == '$' || *u == '`' || *u == '\"')) ||
-	     (instring == 1 && *u == '\'')))
-	    if (*u == '\n' || (instring == 1 && *u == '\'')) {
-		if (unset(RCQUOTES)) {
-		    *v++ = '\'';
-		    if (*u == '\'')
-			*v++ = '\\';
-		    *v++ = *u;
-		    *v++ = '\'';
-		} else if (*u == '\n')
-		    *v++ = '"', *v++ = '\n', *v++ = '"';
-		else
-		    *v++ = '\'', *v++ = '\'';
-		continue;
-	    } else
-		*v++ = '\\';
-	if(*u == Meta)
-	    *v++ = *u++;
-	*v++ = *u;
-    }
-    *v = '\0';
-    if (strcmp(buf, s))
-	tt = dupstring(buf);
-    else
-	tt = s;
-    v += tt - buf;
-    if (e && (sf & 1))
-	*e += tt - buf;
-
-    if (e && *e == u)
-	*e = v;
-    if (te == u)
-	*pl = v - tt;
-
-    return (char *) tt;
+static int
+instmatch(Cmatch m)
+{
+    int l, r = 0, ocs, a = cs;
+
+    if (m->ipre) {
+	inststrlen(m->ipre, 1, (l = strlen(m->ipre)));
+	r += l;
+    } 
+    if (m->pre) {
+	inststrlen(m->pre, 1, (l = strlen(m->pre)));
+	r += l;
+    }
+    if (m->ppre) {
+	inststrlen(m->ppre, 1, (l = strlen(m->ppre)));
+	r += l;
+    }
+    inststrlen(m->str, 1, (l = strlen(m->str)));
+    r += l;
+    ocs = cs;
+    if (brbeg && *brbeg) {
+	cs = a + m->brpl + (m->pre ? strlen(m->pre) : 0);
+	l = strlen(brbeg);
+	inststrlen(brbeg, 1, l);
+	r += l;
+	ocs += l;
+	cs = ocs;
+    }
+    if (m->psuf) {
+	inststrlen(m->psuf, 1, (l = strlen(m->psuf)));
+	r += l;
+    }
+    if (brend && *brend) {
+	a = cs;
+	cs -= m->brsl;
+	ocs = cs;
+	l = strlen(brend);
+	inststrlen(brend, 1, l);
+	r += l;
+	cs = a + l;
+    }
+    if (m->suf) {
+	inststrlen(m->suf, 1, (l = strlen(m->suf)));
+	r += l;
+    }
+    cs = ocs;
+    return r;
 }
 
+
 /* This adds a match to the list of matches.  The string to add is given   *
  * in s, the type of match is given in the global variable addwhat and     *
  * the parameter t (if not NULL) is a pointer to a hash node node which    *
@@ -1385,12 +2349,16 @@ quotename(const char *s, char **e, char *te, int *pl)
 static void
 addmatch(char *s, char *t)
 {
-    int test = 0, sl = strlen(s), pl = rpl, cc = 0, *bp, *ep, *sp;
-    char *e = NULL, *tt, *te, *fc, **fm;
+    int test = 0, sl = strlen(s), pl = rpl, cc = 0, isf = 0;
+    int mpl = 0, msl = 0, bpl = brpl, bsl = brsl;
+    char *e = NULL, *tt, *te, *fc, *ms = NULL;
     Comp cp = patcomp;
     HashNode hn;
     Param pm;
     LinkList l = matches;
+    Cmatch cm;
+    Cline lc = NULL;
+    Aminfo ai = ainfo;
 
 /*
  * addwhat: -5 is for files,
@@ -1427,8 +2395,10 @@ addmatch(char *s, char *t)
 		    && !strcmp(*pt, s + sl - filell))
 		    test = 0;
 
-	    if (!test)
+	    if (!test) {
 		l = fmatches;
+		ai = fainfo;
+	    }
 	}
 	pl = fpl;
 	if (addwhat == -5 || addwhat == -8) {
@@ -1436,6 +2406,7 @@ addmatch(char *s, char *t)
 	    cp = filecomp;
 	    cc = cp || ispattern;
 	    e = s + sl - fsl;
+	    mpl = fpl; msl = fsl;
 	} else {
 	    if ((cp = filecomp)) {
 		if ((test = domatch(s, filecomp, 0)))
@@ -1443,7 +2414,14 @@ addmatch(char *s, char *t)
 	    } else {
 		e = s + sl - fsl;
 		if ((test = !strncmp(s, fpre, fpl)))
-		    test = !strcmp(e, fsuf);
+		    if ((test = !strcmp(e, fsuf))) {
+			mpl = fpl; msl = fsl;
+		    }
+		if (!test && mstack &&
+		    (ms = comp_match(fpre, fsuf, s, &lc,
+				     (addwhat == CC_FILES ||
+				      addwhat == -6), &bpl, &bsl)))
+		    test = 1;
 		if (ispattern)
 		    cc = 1;
 	    }
@@ -1454,7 +2432,7 @@ addmatch(char *s, char *t)
 		return;
 	    if (fc)
 		zsfree(fc);
-	    haswhat |= HAS_FILES;
+	    isf = CMF_FILE;
 
 	    if (addwhat == CC_FILES || addwhat == -6 ||
 		addwhat == -5 || addwhat == -8) {
@@ -1466,11 +2444,15 @@ addmatch(char *s, char *t)
 		e += s - t;
 	    }
 	    if (cc) {
-		tt = (char *)halloc(strlen(ppre) + strlen(psuf) + sl + 1);
-		strcpy(tt, ppre);
+		tt = (char *)halloc(lppl + lpsl + sl + 1);
+		tt[0] = '\0';
+		if (lppre)
+		    strcpy(tt, lppre);
 		strcat(tt, s);
-		strcat(tt, psuf);
+		if (lpsuf)
+		    strcat(tt, lpsuf);
 		untokenize(s = tt);
+		sl = strlen(s);
 	    }
 	}
     } else if (addwhat == CC_QUOTEFLAG || addwhat == -2  ||
@@ -1496,21 +2478,33 @@ addmatch(char *s, char *t)
 		 (((addwhat & CC_DISCMDS) && (hn->flags & DISABLED)) ||
 		  ((addwhat & CC_EXCMDS)  && !(hn->flags & DISABLED)))) ||
 		((addwhat & CC_BINDINGS) && !(hn->flags & DISABLED))))) {
-	if (sl >= rpl + rsl) {
+	if (sl >= rpl + rsl || mstack) {
 	    if (cp)
 		test = domatch(s, patcomp, 0);
 	    else {
 		e = s + sl - rsl;
 		if ((test = !strncmp(s, rpre, rpl)))
-		    test = !strcmp(e, rsuf);
+		    if ((test = !strcmp(e, rsuf))) {
+			mpl = rpl; msl = rsl;
+		    }
+		if (!test && mstack &&
+		    (ms = comp_match(rpre, rsuf, s, &lc,
+				     (addwhat == CC_QUOTEFLAG), &bpl, &bsl)))
+		    test = 1;
 	    }
 	}
-	if (!test && sl < lpl + lsl)
+	if (!test && sl < lpl + lsl && !mstack)
 	    return;
-	if (!test && lpre && lsuf && sl >= lpl + lsl) {
+	if (!test && lpre && lsuf) {
 	    e = s + sl - lsl;
 	    if ((test = !strncmp(s, lpre, lpl)))
-		test = !strcmp(e, lsuf);
+		if ((test = !strcmp(e, lsuf))) {
+		    mpl = lpl; msl = lsl;
+		}
+	    if (!test && mstack &&
+		(ms = comp_match(lpre, lsuf, s, &lc,
+				 (addwhat == CC_QUOTEFLAG), &bpl, &bsl)))
+		test = 1;
 	    pl = lpl;
 	}
 	if (addwhat == CC_QUOTEFLAG) {
@@ -1518,56 +2512,124 @@ addmatch(char *s, char *t)
 	    s = quotename(s, &e, te, &pl);
 	    sl = strlen(s);
 	}
-	if (test)
-	    haswhat |= HAS_MISC;
     }
     if (!test)
 	return;
 
-    if (ispattern) {
-	t = s;
-    } else {
-	t = s += pl;
-	if (*e)
-	    t = s = dupstrpfx(t, e - t);
+    if (!ms && !ispattern && ai->firstm) {
+	if ((test = sl - pfxlen(ai->firstm->str, s)) < ai->prerest)
+	    ai->prerest = test;
+	if ((test = sfxlen(ai->firstm->str, s)) < ai->suflen)
+	    ai->suflen = test;
     }
 
-    if (l == fmatches) {
-	bp = &fab;
-	ep = &fae;
-	sp = &fshortl;
-	fm = &ffirstm;
-    } else {
-	bp = &ab;
-	ep = &ae;
-	sp = &shortl;
-	fm = &firstm;
-    }
-
-    if (!ispattern && *fm) {
-	if ((test = pfxlen(*fm, s)) < *bp)
-	    *bp = test;
-	if ((test = sfxlen(*fm, s)) < *ep)
-	    *ep = test;
-	if (*ep > *sp - *bp)
-	    *ep = *sp - *bp;
-    }
-
-    /* If we are doing a glob completion we store the whole string in *
-     * the list. Otherwise only the part that fits between the prefix *
-     * and the suffix is stored.                                      */
-    addlinknode(l, t);
-    if (!*fm) {
-	*bp = *ep = 10000;
-	*fm = t;
-	*sp = 100000;
-    }
-    if (!ispattern && (sl = strlen(t)) < *sp) {
-	*sp = sl;
-	if (l == fmatches)
-	    fshortest = t;
+    /* Generate the common -P prefix. */
+
+    if (ai->pprefix) {
+	if (curcc->prefix)
+	    ai->pprefix[pfxlen(ai->pprefix, curcc->prefix)] = '\0';
+	else
+	    ai->pprefix[0] = '\0';
+    } else
+	ai->pprefix = dupstring(curcc->prefix ? curcc->prefix : "");
+
+    /* Generate the prefix to insert for ambiguous completions. */
+    t = s;
+    if (lppre)
+	t = dyncat(lppre, t);
+    if (ipre && *ipre) {
+	Cline tlc = prepend_cline(ipre, lc);
+
+	ai->noipre = 0;
+	if (!ms) {
+	    ai->icpl = lppl + mpl;
+	    ai->icsl = lpsl + msl;
+	    if (ai->iaprefix)
+		ai->iaprefix[pfxlen(ai->iaprefix, t)] = '\0';
+	    else
+		ai->iaprefix = dupstring(t);
+	}
+	else
+	    ai->ilinecl = join_clines(ai->ilinecl, lc);
+	if (ai->iprefix) {
+	    if (strcmp(ipre, ai->iprefix))
+		ai->iprefix = "";
+	} else
+	    ai->iprefix = dupstring(ipre);
+
+	t = dyncat(ipre, t);
+	lc = tlc;
+    } else
+	ai->iprefix = "";
+
+    if (!ms) {
+	ai->cpl = lppl + mpl;
+	ai->csl = lpsl + msl;
+	if (ai->aprefix)
+	    ai->aprefix[pfxlen(ai->aprefix, t)] = '\0';
 	else
-	    shortest = t;
+	    ai->aprefix = dupstring(t);
+    }
+    else
+	ai->linecl = join_clines(ai->linecl, lc);
+
+    mnum++;
+    ai->count++;
+
+    /* Allocate and fill the match structure. */
+    cm = (Cmatch) halloc(sizeof(struct cmatch));
+    if (ispattern) {
+	if (lpsuf && *lpsuf && strsfx(lpsuf, s)) {
+	    s[sl - lpsl] = '\0';
+	    cm->psuf = lpsuf;
+	}
+	else
+	    cm->psuf = NULL;
+
+	if (lppre && *lppre && strpfx(lppre, s)) {
+	    s += lppl;
+	    cm->ppre = lppre;
+	    cm->prpre = (isf && prpre && *prpre ? prpre : NULL);
+	}
+	else
+	    cm->ppre = cm->prpre = NULL;
+    }
+    else {
+	cm->ppre = (lppre && *lppre ? lppre : NULL);
+	cm->psuf = (lpsuf && *lpsuf ? lpsuf : NULL);
+	cm->prpre = (isf && prpre && *prpre ? prpre : NULL);
+    }
+    cm->str = (ms ? ms : s);
+    cm->ipre = (ipre && *ipre ? ipre : NULL);
+    cm->ripre = (ripre && *ripre ? ripre : NULL);
+    cm->pre = curcc->prefix;
+    cm->suf = curcc->suffix;
+    cm->flags = mflags | isf;
+    cm->brpl = bpl;
+    cm->brsl = bsl;
+    addlinknode(l, cm);
+
+    /* One more match for this explanation. */
+    if (expl) {
+	if (l == matches)
+	    expl->count++;
+	else
+	    expl->fcount++;
+    }
+    if (!ms) {
+	if (!ai->firstm)
+	    ai->firstm = cm;
+
+	/* Do we have an exact match? More than one? */
+	if (!ispattern && !(e - (s + pl))) {
+	    if (!ai->exact)
+		ai->exact = 1;
+	    else {
+		ai->exact = 2;
+		cm = NULL;
+	    }
+	    ai->exactm = cm;
+	}
     }
 }
 
@@ -1628,6 +2690,9 @@ maketildelist(void)
 	cb.foreach = (int (*)()) match_username;
 	cb.data = (char *)&data;
 	yp_all(domain, PASSWD_MAP, &cb);
+/*	for (n = firstnode(matches); n; incnode(n))
+	    if (getpwnam(getdata(n)) == NULL)
+		uremnode(matches, n);*/
     }
 # else  /* HAVE_NIS_PLUS */
        /* Maybe we should turn this string into a #define'd constant...? */
@@ -1661,25 +2726,6 @@ maketildelist(void)
 	    addhnmatch, 0);
 }
 
-/* Copy the given string and remove backslashes from the copy and return it. */
-
-/**/
-static char *
-rembslash(char *s)
-{
-    char *t = s = dupstring(s);
-
-    while (*s)
-	if (*s == '\\') {
-	    chuck(s);
-	    if (*s)
-		s++;
-	} else
-	    s++;
-
-    return t;
-}
-
 /* This does the check for compctl -x `n' and `N' patterns. */
 
 /**/
@@ -1726,257 +2772,6 @@ getcpat(char *wrd, int cpatindex, char *cpat, int class)
     return -1;
 }
 
-/* This holds a pointer to the compctl we are using. */
-
-static Compctl ccmain;
-
-
-/* Find the compctl to use and return it.  The first argument gives a *
- * compctl to start searching with (if it is zero, the hash table is  *
- * searched).  compadd is used to return a number of characters that  *
- * should be ignored at the beginning of the word and incmd is        *
- * non-zero if we are in command position.                            */
-
-/**/
-static Compctl
-get_ccompctl(Compctl occ, int *compadd, int incmd)
-{
-    Compctl compc, ret;
-    Compctlp ccp;
-    int t, i, a, b, tt, ra, rb, j, isf = 1;
-    Compcond or, cc;
-    char *s, *ss, *sc, *cmd = dupstring(cmdstr);
-    Comp comp;
-
-   first_rec:
-    *compadd = 0;
-    ra = 0;
-    rb = clwnum - 1;
-    sc = NULL;
-
-    if (!(ret = compc = occ)) {
-      if (isf) {
-        isf = 0;
-        ret = &cc_first;
-      }
-      else if (inwhat == IN_ENV)
-        /* Default completion for parameter values. */
-        ret = &cc_default;
-      else if (inwhat == IN_MATH) {
-        /* Parameter names inside mathematical expression. */
-        cc_dummy.mask = CC_PARAMS;
-	    ret = &cc_dummy;
-	    cc_dummy.refc = 10000;
-	} else if (inwhat == IN_COND) {
-	    /* We try to be clever here: in conditions we complete option   *
-	     * names after a `-o', file names after `-nt', `-ot', and `-ef' *
-	     * and file names and parameter names elsewhere.                */
-	    s = clwpos ? clwords[clwpos - 1] : "";
-	    cc_dummy.mask = !strcmp("-o", s) ? CC_OPTIONS :
-		((*s == '-' && s[1] && !s[2]) ||
-		 !strcmp("-nt", s) ||
-		 !strcmp("-ot", s) ||
-		 !strcmp("-ef", s)) ? CC_FILES :
-		(CC_FILES | CC_PARAMS);
-	    ret = &cc_dummy;
-	    cc_dummy.refc = 10000;
-	} else if (incmd)
-	    ret = &cc_compos;
-	/* And in redirections or if there is no command name (and we are *
-	 * not in command position) or if no special compctl was given    *
-	 * for the command: use default completion.  Note that we first   *
-	 * search the complete command name and than the trailing         *
-	 * pathname component.                                            */
-	else if (linredir ||
- 		 !(cmd &&
- 		   (((ccp = (Compctlp) compctltab->getnode(compctltab, cmd)) &&
-		     (compc = ret = ccp->cc)) ||
- 		    ((s = dupstring(cmd)) && remlpaths(&s) &&
-		     (ccp = (Compctlp) compctltab->getnode(compctltab, s)) &&
-		     (compc = ret = ccp->cc)))))
-	    ret = &cc_default;
-
-	ccmain = compc = ret;
-	ccmain->refc++;
-    }
-    /* The compctl we found has extended completion patterns, check them. */
-    if (compc && compc->ext) {
-	compc = compc->ext;
-	/* This loops over the patterns separated by `--'. */
-	for (t = 0; compc && !t; compc = compc->next) {
-	    /* This loops over OR'ed patterns. */
-	    for (cc = compc->cond; cc && !t; cc = or) {
-		or = cc->or;
-		/* This loops over AND'ed patterns. */
-		for (t = 1; cc && t; cc = cc->and) {
-		    /* And this loops of [...] pairs. */
-		    for (t = i = 0; i < cc->n && !t; i++) {
-			s = NULL;
-			ra = 0;
-			rb = clwnum - 1;
-			switch (cc->type) {
-			case CCT_POS:
-			    tt = clwpos;
-			    goto cct_num;
-			case CCT_NUMWORDS:
-			    tt = clwnum;
-			  cct_num:
-			    if ((a = cc->u.r.a[i]) < 0)
-				a += clwnum;
-			    if ((b = cc->u.r.b[i]) < 0)
-				b += clwnum;
-			    if (cc->type == CCT_POS)
-				ra = a, rb = b;
-			    t = (tt >= a && tt <= b);
-			    break;
-			case CCT_CURSUF:
-			case CCT_CURPRE:
-			    s = ztrdup(clwpos < clwnum ? clwords[clwpos] : "");
-			    untokenize(s);
-			    sc = rembslash(cc->u.s.s[i]);
-			    a = strlen(sc);
-			    if (!strncmp(s, sc, a)) {
-				*compadd = (cc->type == CCT_CURSUF ? a : 0);
-				t = 1;
-			    }
-			    break;
-			case CCT_CURSUB:
-			case CCT_CURSUBC:
-			    if (clwpos < 0 || clwpos > clwnum)
-				t = 0;
-			    else {
-				a = getcpat(clwords[clwpos],
-					    cc->u.s.p[i],
-					    cc->u.s.s[i],
-					    cc->type == CCT_CURSUBC);
-				if (a != -1)
-				    *compadd = a, t = 1;
-			    }
-			    break;
-
-			case CCT_CURPAT:
-			case CCT_CURSTR:
-			    tt = clwpos;
-			    goto cct_str;
-			case CCT_WORDPAT:
-			case CCT_WORDSTR:
-			    tt = 0;
-			  cct_str:
-			    if ((a = tt + cc->u.s.p[i]) < 0)
-				a += clwnum;
-			    s = ztrdup((a < 0 || a >= clwnum) ? "" :
-				       clwords[a]);
-			    untokenize(s);
-
-			    if (cc->type == CCT_CURPAT ||
-				cc->type == CCT_WORDPAT) {
-				tokenize(ss = dupstring(cc->u.s.s[i]));
-				t = ((comp = parsereg(ss)) &&
-				     domatch(s, comp, 0));
-			    } else
-				t = (!strcmp(s, rembslash(cc->u.s.s[i])));
-			    break;
-			case CCT_RANGESTR:
-			case CCT_RANGEPAT:
-			    if (cc->type == CCT_RANGEPAT)
-				tokenize(sc = dupstring(cc->u.l.a[i]));
-			    for (j = clwpos; j; j--) {
-				untokenize(s = ztrdup(clwords[j]));
-				if (cc->type == CCT_RANGESTR)
-				    sc = rembslash(cc->u.l.a[i]);
-				if (cc->type == CCT_RANGESTR ?
-				    !strncmp(s, sc, strlen(sc)) :
-				    ((comp = parsereg(sc)) &&
-				     domatch(s, comp, 0))) {
-				    zsfree(s);
-				    ra = j + 1;
-				    t = 1;
-				    break;
-				}
-				zsfree(s);
-			    }
-			    if (t) {
-				if (cc->type == CCT_RANGEPAT)
-				    tokenize(sc = dupstring(cc->u.l.b[i]));
-				for (j++; j < clwnum; j++) {
-				    untokenize(s = ztrdup(clwords[j]));
-				    if (cc->type == CCT_RANGESTR)
-					sc = rembslash(cc->u.l.b[i]);
-				    if (cc->type == CCT_RANGESTR ?
-					!strncmp(s, sc, strlen(sc)) :
-					((comp = parsereg(sc)) &&
-					 domatch(s, comp, 0))) {
-					zsfree(s);
-					rb = j - 1;
-					t = clwpos <= rb;
-					break;
-				    }
-				    zsfree(s);
-				}
-			    }
-			    s = NULL;
-			}
-			zsfree(s);
-		    }
-		}
-	    }
-	    if (t)
-		break;
-	}
-	if (compc)
-	    /* We found a matching pattern, we may return it. */
-	    ret = compc;
-    }
-    if (ret->subcmd) {
-	/* The thing we want to return has a subcmd flag (-l). */
-	char **ow = clwords, *os = cmdstr, *ops = NULL;
-	int oldn = clwnum, oldp = clwpos;
-
-	/* So we restrict the words-array. */
-	if (ra >= clwnum)
-	    ra = clwnum - 1;
-	if (ra < 1)
-	    ra = 1;
-	if (rb >= clwnum)
-	    rb = clwnum - 1;
-	if (rb < 1)
-	    rb = 1;
-	clwnum = rb - ra + 1;
-	clwpos = clwpos - ra;
-
-	if (ret->subcmd[0]) {
-	    /* And probably put the command name given to the flag *
-	     * in the array.                                       */
-	    clwpos++;
-	    clwnum++;
-	    incmd = 0;
-	    ops = clwords[ra - 1];
-	    clwords[ra - 1] = cmdstr = ret->subcmd;
-	    clwords += ra - 1;
-	} else {
-	    cmdstr = clwords[ra];
-	    incmd = !clwpos;
-	    clwords += ra;
-	}
-	*compadd = 0;
-	if (ccmain != &cc_dummy)
-	    freecompctl(ccmain);
-	/* Then we call this function recursively. */
-
-	ret = get_ccompctl(NULL, compadd, incmd);
-	/* And restore the things we changed. */
-	clwords = ow;
-	cmdstr = os;
-	clwnum = oldn;
-	clwpos = oldp;
-	if (ops)
-	    clwords[ra - 1] = ops;
-    }
-    if (ret == &cc_first)
-      goto first_rec;
-    return ret;
-}
-
 /* Dump a hash table (without sorting).  For each element the addmatch  *
  * function is called and at the beginning the addwhat variable is set. *
  * This could be done using scanhashtable(), but this is easy and much  *
@@ -2021,10 +2816,10 @@ getreal(char *str)
     prefork(l, 0);
     noerrs = ne;
     if (!errflag && nonempty(l))
-	return ztrdup(peekfirst(l));
+	return dupstring(peekfirst(l));
     errflag = 0;
 
-    return ztrdup(str);
+    return dupstring(str);
 }
 
 /* This reads a directory and adds the files to the list of  *
@@ -2040,7 +2835,6 @@ gen_matches_files(int dirs, int execs, int all)
     LinkList l = NULL;
     int ns = 0, ng = opts[NULLGLOB], test, aw = addwhat;
 
-    addwhat = execs ? -8 : -5;
     opts[NULLGLOB] = 1;
 
     if (*psuf) {
@@ -2066,6 +2860,7 @@ gen_matches_files(int dirs, int execs, int all)
 	    /* Ignore files beginning with `.' unless the thing we found on *
 	     * the command line also starts with a dot or GLOBDOTS is set.  */
 	    if (*n != '.' || *fpre == '.' || isset(GLOBDOTS)) {
+		addwhat = execs ? -8 : -5;
 		if (filecomp)
 		    /* If we have a pattern for the filename check, use it. */
 		    test = domatch(n, filecomp, 0);
@@ -2074,6 +2869,10 @@ gen_matches_files(int dirs, int execs, int all)
 		    e = n + strlen(n) - fsl;
 		    if ((test = !strncmp(n, fpre, fpl)))
 			test = !strcmp(e, fsuf);
+		    if (!test && mstack) {
+			test = 1;
+			addwhat = CC_FILES;
+		    }
 		}
 		/* Filename didn't match? */
 		if (!test)
@@ -2136,45 +2935,17 @@ gen_matches_files(int dirs, int execs, int all)
     addwhat = aw;
 }
 
-/* This holds the explanation string we have to print. */
-
-static char *expl;
-
-/* This holds the suffix to add (given with compctl -S). */
-
-static char *ccsuffix;
-
-/* This s non-zero if the compctl -q flag was given (the suffix should *
- * be removed when a space or something like that is typed next).      */
-
-static int remsuffix;
-
-/**/
-static void
-quotepresuf(char **ps)
-{
-    if (*ps) {
-	char *p = quotename(*ps, NULL, NULL, NULL);
-
-	if (p != *ps) {
-	    zsfree(*ps);
-	    *ps = ztrdup(p);
-	}
-    }
-}
-
 /**/
 static void
-docompletion(char *s, int lst, int incmd, int untokenized)
+docompletion(char *s, int lst, int incmd)
 {
-    static int delit, compadd;
-
-    fixsuffix();
     HEAPALLOC {
 	pushheap();
 
+	ainfo = fainfo = NULL;
+
 	/* Make sure we have the completion list and compctl. */
-	if(makecomplist(s, incmd, &delit, &compadd, untokenized)) {
+	if (makecomplist(s, incmd)) {
 	    /* Error condition: feeeeeeeeeeeeep(). */
 	    feep();
 	    goto compend;
@@ -2185,42 +2956,51 @@ docompletion(char *s, int lst, int incmd, int untokenized)
 	    showinglist = -2;
 	else {
 	    /* We have matches. */
-	    if (delit) {
-		/* If we have to delete the word from the command line, *
-		 * do it now.                                           */
-		wb -= compadd;
-		strcpy((char *)line + wb, (char *)line + we);
-		we = cs = wb;
-	    }
 	    if (nmatches > 1)
 		/* There are more than one match. */
 		do_ambiguous();
+
 	    else if (nmatches == 1) {
 		/* Only one match. */
-		do_single(amatches[0]);
+		do_single(amatches->matches[0]);
 		invalidatelist();
 	    }
 	}
 
-	/* Print the explanation string if needed. */
-	if (!showinglist && expl && nmatches != 1) {
-	    int up;
+	/* Print the explanation strings if needed. */
+	if (!showinglist && validlist && nmatches != 1) {
+	    Cmgroup g = amatches;
+	    Cexpl *e;
+	    int up = 0, tr = 1;
 
 	    if (!nmatches)
 		feep();
-	    trashzle();
-
-	    clearflag = (isset(USEZLE) && !termflags &&
-			 (isset(ALWAYSLASTPROMPT) && zmult == 1)) ||
-			(unset(ALWAYSLASTPROMPT) && zmult != 1);
 
-	    up = printfmt(expl, nmatches, 1);
+	    while (g) {
+		if ((e = g->expls))
+		    while (*e) {
+			if ((*e)->count) {
+			    if (tr) {
+				trashzle();
+				tr = 0;
+			    }
+			    up += printfmt((*e)->str, (*e)->count, 1);
+			}
+			e++;
+		    }
+		g = g->next;
+	    }
+	    if (!tr) {
+		clearflag = ((isset(USEZLE) && !termflags &&
+			      (isset(ALWAYSLASTPROMPT) && zmult == 1)) ||
+			     (unset(ALWAYSLASTPROMPT) && zmult != 1));
 
-	    if (clearflag)
-		tcmultout(TCUP, TCMULTUP, up + nlnct);
-	    else
-		putc('\n', shout);
-	    fflush(shout);
+		if (clearflag && up + nlnct < lines)
+		    tcmultout(TCUP, TCMULTUP, up + nlnct);
+		else
+		    putc('\n', shout);
+		fflush(shout);
+	    }
 	}
       compend:
 	ll = strlen((char *)line);
@@ -2230,6 +3010,14 @@ docompletion(char *s, int lst, int incmd, int untokenized)
     } LASTALLOC;
 }
 
+/* The beginning and end of a word range to be used by -l. */
+
+static int brange, erange;
+
+/* This is used to detect when and what to continue. */
+
+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    *
@@ -2237,63 +3025,520 @@ docompletion(char *s, int lst, int incmd, int untokenized)
  * 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.  delit and compadd return information  *
- * about bits of the command line that need to be deleted.           */
+ * returns non-zero on error.                                        */
 
 /**/
 static int
-makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized)
+makecomplist(char *s, int incmd)
 {
-    Compctl cc = NULL;
-    int oloffs = offs, owe = we, owb = wb, ocs = cs, oll = ll, isf = 1;
-    int t, sf1, sf2, ooffs;
-    char *p, *sd = NULL, *tt, *s1, *s2, *os = NULL;
-    unsigned char *ol = NULL;
+    struct cmlist ms;
+    Cmlist m = cmatcher;
 
     /* If we already have a list from a previous execution of this *
      * function, skip the list building code.                      */
     if (validlist)
 	return !nmatches;
 
-    os = dupstring(s);
-    ol = (unsigned char *)dupstring((char *)line);
+    for (;;) {
+	if (m) {
+	    ms.next = NULL;
+	    ms.matcher = m->matcher;
+	    mstack = &ms;
+	}
+	ainfo = (Aminfo) hcalloc(sizeof(struct aminfo));
+	fainfo = (Aminfo) hcalloc(sizeof(struct aminfo));
+
+	ainfo->prerest = ainfo->suflen = 
+	    fainfo->prerest = fainfo->suflen = 10000;
+	ainfo->noipre = fainfo->noipre= 1;
+
+	freecl = NULL;
+
+	lastambig = 0;
+	amatches = 0;
+	mnum = 0;
+	begcmgroup("default", 0);
 
-  xorrec:
+	ccused = newlinklist();
+	ccstack = newlinklist();
+
+	makecomplistglobal(s, incmd);
+
+	endcmgroup(NULL);
+
+	if (amatches)
+	    amatches->ccs = (Compctl *) makearray(ccused, 0,
+						  &(amatches->ccount), NULL);
+	else {
+	    LinkNode n;
+
+	    for (n = firstnode(ccused); n; incnode(n))
+		freecompctl((Compctl) getdata(n));
+	}
+
+	PERMALLOC {
+	    permmatches();
+	} LASTALLOC;
+
+	if (nmatches && !errflag) {
+	    validlist = 1;
+
+	    return 0;
+	}
+	if (!m || !(m = m->next))
+	    break;
+
+	errflag = 0;
+    }
+    return 1;
+}
+
+/* This function gets the compctls for the given command line and *
+ * adds all completions for them. */
+
+/**/
+static void
+makecomplistglobal(char *os, int incmd)
+{
+    Compctl cc;
+    char *s;
+
+    if (inwhat == IN_ENV)
+        /* Default completion for parameter values. */
+        cc = &cc_default;
+    else if (inwhat == IN_MATH) {
+        /* Parameter names inside mathematical expression. */
+        cc_dummy.mask = CC_PARAMS;
+	cc = &cc_dummy;
+	cc_dummy.refc = 10000;
+    } else if (inwhat == IN_COND) {
+	/* We try to be clever here: in conditions we complete option   *
+	 * names after a `-o', file names after `-nt', `-ot', and `-ef' *
+	 * and file names and parameter names elsewhere.                */
+	s = clwpos ? clwords[clwpos - 1] : "";
+	cc_dummy.mask = !strcmp("-o", s) ? CC_OPTIONS :
+	    ((*s == '-' && s[1] && !s[2]) ||
+	     !strcmp("-nt", s) ||
+	     !strcmp("-ot", s) ||
+	     !strcmp("-ef", s)) ? CC_FILES :
+	    (CC_FILES | CC_PARAMS);
+	cc = &cc_dummy;
+	cc_dummy.refc = 10000;
+    } else if (linredir)
+	/* In redirections use default completion. */
+	cc = &cc_default;
+    else {
+	/* Otherwise get the matches for the command. */
+	makecomplistcmd(os, incmd);
+	cc = NULL;
+    }
+    if (cc) {
+	/* First, use the -T compctl. */
+	makecomplistcc(&cc_first, os, incmd);
+
+	if (!(ccont & CC_CCCONT))
+	    return;
+
+	makecomplistcc(cc, os, incmd);
+    }
+}
+
+/* This produces the matches for a command. */
+
+/**/
+static void
+makecomplistcmd(char *os, int incmd)
+{
+    Compctl cc;
+    Compctlp ccp;
+    char *s;
 
-    DPUTS(ll != strlen((char *) line), "BUG: xorrec: ll != strlen(line)");
+    /* First, use the -T compctl. */
+    makecomplistcc(&cc_first, os, incmd);
 
+    if (!(ccont & CC_CCCONT))
+	return;
+
+    /* Then search the pattern compctls, with the command name and the *
+     * full pathname of the command. */
+    makecomplistpc(os, incmd);
+    if (!(ccont & CC_CCCONT))
+	return;
+
+    /* If the command string starts with `=', try the path name of the *
+     * command. */
+    if (cmdstr && cmdstr[0] == Equals) {
+	char *c = findcmd(cmdstr + 1);
+
+	if (c) {
+	    zsfree(cmdstr);
+	    cmdstr = ztrdup(c);
+	}
+    }
+
+    /* Find the compctl for this command, trying the full name and then *
+     * the trailing pathname component. If that doesn't yield anything, *
+     * use default completion. */
+    if (incmd)
+	cc = &cc_compos;
+    else if (!(cmdstr &&
+	  (((ccp = (Compctlp) compctltab->getnode(compctltab, cmdstr)) &&
+	    (cc = ccp->cc)) ||
+	   ((s = dupstring(cmdstr)) && remlpaths(&s) &&
+	    (ccp = (Compctlp) compctltab->getnode(compctltab, s)) &&
+	    (cc = ccp->cc)))))
+	cc = &cc_default;
+
+    makecomplistcc(cc, os, incmd);
+}
+
+/* This add the matches for the pattern compctls. */
+
+/**/
+static void
+makecomplistpc(char *os, int incmd)
+{
+    Patcomp pc;
+    Comp pat;
+    char *s = findcmd(cmdstr);
+
+    for (pc = patcomps; pc; pc = pc->next) {
+	if ((pat = parsereg(pc->pat)) &&
+	    (domatch(cmdstr, pat, 0) ||
+	     (s && domatch(s, pat, 0)))) {
+	    makecomplistcc(pc->cc, os, incmd);
+	    if (!(ccont & CC_CCCONT))
+		return;
+	}
+    }
+}
+
+/* This produces the matches for one compctl. */
+
+/**/
+static void
+makecomplistcc(Compctl cc, char *s, int incmd)
+{
+    cc->refc++;
+    addlinknode(ccused, cc);
+
+    ccont = 0;
+
+    makecomplistor(cc, s, incmd, 0, 0);
+}
+
+/* This adds the completions for one run of [x]or'ed completions. */
+
+/**/
+static void
+makecomplistor(Compctl cc, char *s, int incmd, int compadd, int sub)
+{
+    int mn, ct, um = usemenu;
+
+    /* Loop over xors. */
+    do {
+	mn = mnum;
+
+	/* Loop over ors. */
+	do {
+	    /* Reset the range information if we are not in a sub-list. */
+	    if (!sub) {
+		brange = 0;
+		erange = clwnum - 1;
+	    }
+	    usemenu = 0;
+	    makecomplistlist(cc, s, incmd, compadd);
+	    um |= usemenu;
+
+	    ct = cc->mask2 & CC_XORCONT;
+
+	    cc = cc->xor;
+	} while (cc && ct);
+
+	/* Stop if we got some matches. */
+	if (mn != mnum)
+	    break;
+	if (cc) {
+	    ccont &= ~(CC_DEFCONT | CC_PATCONT);
+	    if (!sub)
+		ccont &= ~CC_CCCONT;
+	}
+    } while (cc);
+
+    usemenu = um;
+}
+
+/* This dispatches for simple and extended completion. */
+
+/**/
+static void
+makecomplistlist(Compctl cc, char *s, int incmd, int compadd)
+{
+    int oloffs = offs, owe = we, owb = wb, ocs = cs;
+
+    if (cc->ext)
+	/* Handle extended completion. */
+	makecomplistext(cc, s, incmd);
+    else
+	/* Only normal flags. */
+	makecomplistflags(cc, s, incmd, compadd);
+
+    /* Reset some information variables for the next try. */
+    errflag = 0;
+    offs = oloffs;
+    wb = owb;
+    we = owe;
+    cs = ocs;
+}
+
+/* This add matches for extended completion patterns */
+
+/**/
+static void
+makecomplistext(Compctl occ, char *os, int incmd)
+{
+    Compctl compc;
+    Compcond or, cc;
+    Comp comp;
+    int compadd, m = 0, d = 0, t, tt, i, j, a, b;
+    char *sc, *s, *ss;
+
+    /* This loops over the patterns separated by `-'s. */
+    for (compc = occ->ext; compc; compc = compc->next) {
+	compadd = t = brange = 0;
+	erange = clwnum - 1;
+	/* This loops over OR'ed patterns. */
+	for (cc = compc->cond; cc && !t; cc = or) {
+	    or = cc->or;
+	    /* This loops over AND'ed patterns. */
+	    for (t = 1; cc && t; cc = cc->and) {
+		/* And this loops over [...] pairs. */
+		for (t = i = 0; i < cc->n && !t; i++) {
+		    s = NULL;
+		    brange = 0;
+		    erange = clwnum - 1;
+		    switch (cc->type) {
+		    case CCT_POS:
+			tt = clwpos;
+			goto cct_num;
+		    case CCT_NUMWORDS:
+			tt = clwnum;
+		    cct_num:
+			if ((a = cc->u.r.a[i]) < 0)
+			    a += clwnum;
+			if ((b = cc->u.r.b[i]) < 0)
+			    b += clwnum;
+			if (cc->type == CCT_POS)
+			    brange = a, erange = b;
+			t = (tt >= a && tt <= b);
+			break;
+		    case CCT_CURSUF:
+		    case CCT_CURPRE:
+			s = ztrdup(clwpos < clwnum ? clwords[clwpos] : "");
+			untokenize(s);
+			sc = rembslash(cc->u.s.s[i]);
+			a = strlen(sc);
+			if (!strncmp(s, sc, a)) {
+			    compadd = (cc->type == CCT_CURSUF ? a : 0);
+			    t = 1;
+			}
+			break;
+		    case CCT_CURSUB:
+		    case CCT_CURSUBC:
+			if (clwpos < 0 || clwpos > clwnum)
+			    t = 0;
+			else {
+			    a = getcpat(clwords[clwpos],
+					cc->u.s.p[i],
+					cc->u.s.s[i],
+					cc->type == CCT_CURSUBC);
+			    if (a != -1)
+				compadd = a, t = 1;
+			}
+			break;
+			
+		    case CCT_CURPAT:
+		    case CCT_CURSTR:
+			tt = clwpos;
+			goto cct_str;
+		    case CCT_WORDPAT:
+		    case CCT_WORDSTR:
+			tt = 0;
+		    cct_str:
+			if ((a = tt + cc->u.s.p[i]) < 0)
+			    a += clwnum;
+			s = ztrdup((a < 0 || a >= clwnum) ? "" :
+				   clwords[a]);
+			untokenize(s);
+			
+			if (cc->type == CCT_CURPAT ||
+			    cc->type == CCT_WORDPAT) {
+			    tokenize(ss = dupstring(cc->u.s.s[i]));
+			    t = ((comp = parsereg(ss)) &&
+				 domatch(s, comp, 0));
+			} else
+			    t = (!strcmp(s, rembslash(cc->u.s.s[i])));
+			break;
+		    case CCT_RANGESTR:
+		    case CCT_RANGEPAT:
+			if (cc->type == CCT_RANGEPAT)
+			    tokenize(sc = dupstring(cc->u.l.a[i]));
+			for (j = clwpos; j; j--) {
+			    untokenize(s = ztrdup(clwords[j]));
+			    if (cc->type == CCT_RANGESTR)
+				sc = rembslash(cc->u.l.a[i]);
+			    if (cc->type == CCT_RANGESTR ?
+				!strncmp(s, sc, strlen(sc)) :
+				((comp = parsereg(sc)) &&
+				 domatch(s, comp, 0))) {
+				zsfree(s);
+				brange = j + 1;
+				t = 1;
+				break;
+			    }
+			    zsfree(s);
+			}
+			if (t && cc->u.l.b[i]) {
+			    if (cc->type == CCT_RANGEPAT)
+				tokenize(sc = dupstring(cc->u.l.b[i]));
+			    for (j++; j < clwnum; j++) {
+				untokenize(s = ztrdup(clwords[j]));
+				if (cc->type == CCT_RANGESTR)
+				    sc = rembslash(cc->u.l.b[i]);
+				if (cc->type == CCT_RANGESTR ?
+				    !strncmp(s, sc, strlen(sc)) :
+				    ((comp = parsereg(sc)) &&
+				     domatch(s, comp, 0))) {
+				    zsfree(s);
+				    erange = j - 1;
+				    t = clwpos <= erange;
+				    break;
+				}
+				zsfree(s);
+			    }
+			}
+			s = NULL;
+		    }
+		    zsfree(s);
+		}
+	    }
+	}
+	if (t) {
+	    /* The patterns matched, use the flags. */
+	    m = 1;
+	    ccont &= ~(CC_PATCONT | CC_DEFCONT);
+	    makecomplistor(compc, os, incmd, compadd, 1);
+	    if (!d && (ccont & CC_DEFCONT)) {
+		d = 1;
+		compadd = 0;
+		brange = 0;
+		erange = clwnum - 1;
+		makecomplistflags(occ, os, incmd, 0);
+	    }
+	    if (!(ccont & CC_PATCONT))
+		break;
+	}
+    }
+    /* If no pattern matched, use the standard flags. */
+    if (!m) {
+	compadd = 0;
+	brange = 0;
+	erange = clwnum - 1;
+	makecomplistflags(occ, os, incmd, 0);
+    }
+}
+
+/* This returns the node with the given data. */
+/* ...should probably be moved to linklist.c. */
+
+static LinkNode
+findnode(LinkList list, void *dat)
+{
+    LinkNode tmp = list->first;
+
+    while (tmp && tmp->dat != dat) tmp = tmp->next;
+
+    return tmp;
+}
+
+/* This adds the completions for the flags in the given compctl. */
+
+/**/
+static void
+makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
+{
+    int t, sf1, sf2, ooffs, um = usemenu, delit, ispar = 0;
+    char *p, *sd = NULL, *tt, *s1, *s2, *os =  dupstring(s);
+    struct cmlist ms;
+
+    ccont |= (cc->mask2 & (CC_CCCONT | CC_DEFCONT | CC_PATCONT));
+
+    if (findnode(ccstack, cc))
+	return;
+
+    addlinknode(ccstack, cc);
+
+    if (allccs) {
+	if (findnode(allccs, cc)) {
+	    uremnode(ccstack, firstnode(ccstack));
+	    return;
+	}
+	addlinknode(allccs, cc);
+    }
     /* Go to the end of the word if complete_in_word is not set. */
     if (unset(COMPLETEINWORD) && cs != we)
 	cs = we, offs = strlen(s);
 
-    ispattern = haswhat = lastambig = 0;
+    s = dupstring(s);
+    delit = ispattern = 0;
+    usemenu = um;
     patcomp = filecomp = NULL;
     menucur = NULL;
-    shortest = NULL;
-    fshortest = NULL;
-    rpre = rsuf = lpre = lsuf = ppre = psuf = prpre =
-	fpre = fsuf = firstm = ffirstm = parampre = qparampre = NULL;
+    rpre = rsuf = lpre = lsuf = ppre = psuf = lppre = lpsuf = prpre =
+	fpre = fsuf = ipre = ripre = prpre = NULL;
 
-    /* Blank out the lists. */
-    matches = newlinklist();
-    fmatches = newlinklist();
+    curcc = cc;
 
-    /* If we don't have a compctl definition yet or we have a compctl *
-     * with extended completion, get it (or the next one, resp.).     */
-    if (!cc || cc->ext)
-	cc = get_ccompctl(cc, compadd, incmd);
-
-    /* *compadd is the number of characters we have to ignore at the *
+    mflags = 0;
+    if (cc->ylist || cc->gname) {
+	endcmgroup(NULL);
+	begcmgroup((cc->ylist ? NULL : cc->gname), cc->mask2 & CC_NOSORT);
+    }
+    if (cc->mask & CC_REMOVE)
+	mflags |= CMF_REMOVE;
+    if (cc->mask2 & CC_NOSORT)
+	mgroup->flags |= CGF_NOSORT;
+    if (cc->explain) {
+	expl = (Cexpl) halloc(sizeof(struct cexpl));
+	expl->count = expl->fcount = 0;
+    }
+    else
+	expl = NULL;
+    /* compadd is the number of characters we have to ignore at the  *
      * beginning of the word.                                        */
-    wb += *compadd;
-    s += *compadd;
-    if ((offs -= *compadd) < 0)
-	/* It's bigger than our word prefix, so we can't help here... */
-	return 1;
+    if (compadd) {
+	ipre = dupstring(s);
+	ipre[compadd] = '\0';
+	untokenize(ipre);
+	wb += compadd;
+	s += compadd;
+	if ((offs -= compadd) < 0) {
+	    /* It's bigger than our word prefix, so we can't help here... */
+	    uremnode(ccstack, firstnode(ccstack));
+	    return;
+	}
+    }
+    else
+	ipre = NULL;
 
+    if (cc->matcher) {
+	ms.next = mstack;
+	ms.matcher = cc->matcher;
+	mstack = &ms;
+    }
     /* Insert the prefix (compctl -P), if any. */
     if (cc->prefix) {
-	int pl = 0, sl = strlen(cc->prefix);
+	int pl = 0;
 
 	if (*s) {
 	    /* First find out how much of the prefix is already on the line. */
@@ -2301,23 +3546,13 @@ makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized)
 	    untokenize(sd);
 	    pl = pfxlen(cc->prefix, sd);
 	    s += pl;
+	    sd += pl;
+	    offs -= pl;
 	}
-	if (pl < sl) {
-	    int savecs = cs;
-
-	    /* Then insert the prefix. */
-	    cs = wb + pl;
-	    inststrlen(cc->prefix + pl, 0, sl - pl);
-	    cs = savecs + sl - pl;
-	}
-	/* And adjust the word beginning/end variables. */
-	wb += sl;
-	we += sl - pl;
-	offs -= pl;
     }
     /* Does this compctl have a suffix (compctl -S)? */
-    if ((ccsuffix = cc->suffix) && *ccsuffix) {
-	char *sdup = dupstring(ccsuffix);
+    if (cc->suffix) {
+	char *sdup = dupstring(cc->suffix);
 	int sl = strlen(sdup), suffixll;
 
 	/* Ignore trailing spaces. */
@@ -2331,11 +3566,8 @@ makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized)
 	/* If the suffix is already there, ignore it (and don't add *
 	 * it again).                                               */
 	if (*sd && (suffixll = strlen(sd)) >= sl &&
-	    offs <= suffixll - sl && !strcmp(sdup, sd + suffixll - sl)) {
-	    ccsuffix = NULL;
-	    haswhat |= HAS_SUFFIX;
+	    offs <= suffixll - sl && !strcmp(sdup, sd + suffixll - sl))
 	    s[suffixll - sl] = '\0';
-	}
     }
     /* Do we have one of the special characters `~' and `=' at the beginning? */
     if ((ic = *s) != Tilde && ic != Equals)
@@ -2389,20 +3621,25 @@ makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized)
 	/* Now make sure that the cursor is inside the name. */
 	if (offs <= e - s && offs >= b - s && n <= 0) {
 	    /* It is. */
-	    parambr = br - 1;
+	    if (br >= 2)
+		mflags |= CMF_PARBR;
+
 	    /* Get the prefix (anything up to the character before the name). */
+	    lpsuf = dupstring(quotename(e, NULL, NULL, NULL));
 	    *e = '\0';
-	    parampre = ztrduppfx(s, b - s);
-	    qparampre = ztrdup(quotename(parampre, NULL, NULL, NULL));
-	    untokenize(qparampre);
-	    qparprelen = strlen(qparampre);
+	    lpsl = strlen(lpsuf);
+	    ripre = dupstring(s);
+	    ripre[b - s] = '\0';
+	    ipre = dupstring(quotename(ripre, NULL, NULL, NULL));
+	    untokenize(ipre);
+	    ispar = 1;
 	    /* And adjust wb, we, and offs again. */
 	    offs -= b - s;
 	    wb = cs - offs;
 	    we = wb + e - b;
 	    s = b;
 	    /* And now make sure that we complete parameter names. */
-	    cc = ccmain = &cc_dummy;
+	    cc = &cc_dummy;
 	    cc_dummy.refc = 10000;
 	    cc_dummy.mask = CC_PARAMS | CC_ENVVARS;
 	}
@@ -2410,38 +3647,19 @@ makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized)
     ooffs = offs;
     /* If we have to ignore the word, do that. */
     if (cc->mask & CC_DELETE) {
-	*delit = 1;
+	delit = 1;
 	*s = '\0';
 	offs = 0;
-    } else
-	*delit = 0;
+	if (isset(AUTOMENU)) usemenu = 1;
+    }
 
     /* Compute line prefix/suffix. */
-
     lpl = offs;
-    lpre = zalloc(lpl + 1);
+    lpre = halloc(lpl + 1);
     memcpy(lpre, s, lpl);
     lpre[lpl] = '\0';
-    p = quotename(lpre, NULL, NULL, NULL);
-    if (strcmp(p, lpre) && !strpfx(p, qword)) {
-	int l1, l2;
-
-	backdel(l1 = cs - wb);
-	untokenize(p);
-	inststrlen(p, 1, l2 = strlen(p));
-	we += l2 - l1;
-    }
-    lsuf = ztrdup(s + offs);
+    lsuf = dupstring(s + offs);
     lsl = strlen(lsuf);
-    if (lsl && (p = quotename(lsuf, NULL, NULL, NULL)) &&
-	(strcmp(p, lsuf) && !strsfx(p, qword))) {
-	int l1, l2;
-
-	foredel(l1 = strlen(s + offs));
-	untokenize(p);
-	inststrlen(p, 0, l2 = strlen(p));
-	we += l2 - l1;
-    }
 
     /* First check for ~.../... */
     if (ic == Tilde) {
@@ -2454,15 +3672,15 @@ makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized)
     }
     /* Compute real prefix/suffix. */
 
-    noreal = !*delit;
+    noreal = !delit;
     for (p = lpre; *p && *p != String && *p != Tick; p++);
-    tt = ic && !parampre ? lpre + 1 : lpre;
+    tt = ic && !ispar ? lpre + 1 : lpre;
     rpre = (*p || *lpre == Tilde || *lpre == Equals) ?
 	(noreal = 0, getreal(tt)) :
-	ztrdup(tt);
+	dupstring(tt);
 
     for (p = lsuf; *p && *p != String && *p != Tick; p++);
-    rsuf = *p ? (noreal = 0, getreal(lsuf)) : ztrdup(lsuf);
+    rsuf = *p ? (noreal = 0, getreal(lsuf)) : dupstring(lsuf);
 
     /* Check if word is a pattern. */
 
@@ -2523,16 +3741,47 @@ makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized)
 
 	/* Compute the path prefix/suffix. */
 	if (*s1 != '/')
-	    ppre = ztrdup("");
+	    ppre = "";
 	else
-	    ppre = ztrduppfx(rpre, s1 - rpre + 1);
-	psuf = ztrdup(s2);
+	    ppre = dupstrpfx(rpre, s1 - rpre + 1);
+	psuf = dupstring(s2);
+
+	if (cs != wb) {
+	    char save = line[cs];
+
+	    line[cs] = 0;
+	    lppre = dupstring((char *) (line + wb));
+	    line[cs] = save;
+	    if ((p = strrchr(lppre, '/'))) {
+		p[1] = '\0';
+		lppl = strlen(lppre);
+	    } else {
+		lppre = NULL;
+		lppl = 0;
+	    }
+	}
+	else {
+	    lppre = NULL;
+	    lppl = 0;
+	}
+	if (cs != we) {
+	    char save = line[we];
+
+	    line[we] = 0;
+	    lpsuf = strchr(dupstring((char *) (line + cs)), '/');
+	    line[we] = save;
+	    lpsl = (lpsuf ? strlen(lpsuf) : 0);
+	}
+	else {
+	    lpsuf = NULL;
+	    lpsl = 0;
+	}
 
 	/* And get the file prefix. */
-	fpre = ztrdup(((s1 == s || s1 == rpre || ic) &&
-		       (*s != '/' || cs == wb)) ? s1 : s1 + 1);
+	fpre = dupstring(((s1 == s || s1 == rpre || ic) &&
+			  (*s != '/' || cs == wb)) ? s1 : s1 + 1);
 	/* And the suffix. */
-	fsuf = ztrduppfx(rsuf, s2 - rsuf);
+	fsuf = dupstrpfx(rsuf, s2 - rsuf);
 
 	if (useglob && (ispattern & 2)) {
 	    int t2;
@@ -2593,161 +3842,200 @@ makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized)
 
 		if (nonempty(l)) {
 		    /* And add the resulting words. */
-		    haswhat |= HAS_PATHPAT;
+		    mflags |= CMF_FILE;
 		    for (n = firstnode(l); n; incnode(n))
 			addmatch(getdata(n), NULL);
+		    mflags &= !CMF_FILE;
 		}
 		opts[NULLGLOB] = ng;
 	    } else {
+		char **dirs = 0, *ta[2];
+
 		/* No pattern matching. */
 		addwhat = CC_FILES;
-		if (cc->withd) {
-		    prpre = tricat(cc->withd, "/", ppre);
-		} else
-		    prpre = ztrdup(ppre);
 
-		if (sf2)
-		    /* We are in the path, so add only directories. */
-		    gen_matches_files(1, 0, 0);
-		else {
-		    if (cc->mask & CC_FILES)
-			/* Add all files. */
-			gen_matches_files(0, 0, 1);
-		    else if (cc->mask & CC_COMMPATH) {
-			/* Completion of command paths. */
-			if (sf1 || cc->withd)
-			    /* There is a path prefix, so add *
-			     * directories and executables.   */
-			    gen_matches_files(1, 1, 0);
-			else {
-			    /* No path prefix, so add the things *
-			     * reachable via the PATH variable.  */
-			    char **pc = path, *pp = prpre;
+		if (cc->withd) {
+		    char **pp, **npp, *tp;
+		    int tl = strlen(ppre) + 2, pl;
+
+		    if ((pp = get_user_var(cc->withd))) {
+			dirs = npp =
+			    (char**) halloc(sizeof(char *)*(arrlen(pp)+1));
+			while (*pp) {
+			    pl = strlen(*pp);
+			    tp = (char *) halloc(strlen(*pp) + tl);
+			    strcpy(tp, *pp);
+			    tp[pl] = '/';
+			    strcpy(tp + pl + 1, ppre);
+			    *npp++ = tp;
+			    pp++;
+			}
+			*npp = '\0';
+		    }
+		}
+		if (!dirs) {
+		    dirs = ta;
+		    if (cc->withd) {
+			char *tp;
+			int pl = strlen(cc->withd);
+
+			ta[0] = tp = (char *) halloc(strlen(ppre) + pl + 2);
+			strcpy(tp, cc->withd);
+			tp[pl] = '/';
+			strcpy(tp + pl + 1, ppre);
+		    } else
+			ta[0] = ppre;
+		    ta[1] = NULL;
+		}
+		while (*dirs) {
+		    prpre = *dirs;
 
-			    for (; *pc; pc++)
-				if (!**pc || (pc[0][0] == '.' && !pc[0][1]))
-				    break;
-			    if (*pc) {
-				prpre = "./";
+		    if (sf2)
+			/* We are in the path, so add only directories. */
+			gen_matches_files(1, 0, 0);
+		    else {
+			if (cc->mask & CC_FILES)
+			    /* Add all files. */
+			    gen_matches_files(0, 0, 1);
+			else if (cc->mask & CC_COMMPATH) {
+			    /* Completion of command paths. */
+			    if (sf1 || cc->withd)
+				/* There is a path prefix, so add *
+				 * directories and executables.   */
 				gen_matches_files(1, 1, 0);
-				prpre = pp;
+			    else {
+				/* No path prefix, so add the things *
+				 * reachable via the PATH variable.  */
+				char **pc = path, *pp = prpre;
+
+				for (; *pc; pc++)
+				    if (!**pc || (pc[0][0] == '.' && !pc[0][1]))
+					break;
+				if (*pc) {
+				    prpre = "./";
+				    gen_matches_files(1, 1, 0);
+				    prpre = pp;
+				}
 			    }
-			}
-		    } else if (cc->mask & CC_DIRS)
-			gen_matches_files(1, 0, 0);
-		    /* The compctl has a glob pattern (compctl -g). */
-		    if (cc->glob) {
-			int ns, pl = strlen(prpre), o;
-			char *g = dupstring(cc->glob), pa[PATH_MAX];
-			char *p2, *p3;
-			int ne = noerrs, md = opts[MARKDIRS];
-
-			/* These are used in the globbing code to make *
-			 * things a bit faster.                        */
-			glob_pre = fpre;
-			glob_suf = fsuf;
-
-			noerrs = 1;
-			addwhat = -6;
-			strcpy(pa, prpre);
-			o = strlen(pa);
-			opts[MARKDIRS] = 0;
-
-			/* The compctl -g string may contain more than *
-			 * one pattern, so we need a loop.             */
-			while (*g) {
-			    LinkList l = newlinklist();
-			    int ng;
-
-			    /* Find the blank terminating the pattern. */
-			    while (*g && inblank(*g))
-				g++;
-			    /* Oops, we already reached the end of the
-			       string. */
-			    if (!*g)
-				break;
-			    for (p = g + 1; *p && !inblank(*p); p++)
-				if (*p == '\\' && p[1])
-				    p++;
-			    /* Get the pattern string. */
-			    tokenize(g = dupstrpfx(g, p - g));
-			    if (*g == '=')
-				*g = Equals;
-			    if (*g == '~')
-				*g = Tilde;
-			    remnulargs(g);
-			    if ((*g == Equals || *g == Tilde) && !cc->withd) {
+			} else if (cc->mask & CC_DIRS)
+			    gen_matches_files(1, 0, 0);
+			/* The compctl has a glob pattern (compctl -g). */
+			if (cc->glob) {
+			    int ns, pl = strlen(prpre), o;
+			    char *g = dupstring(cc->glob), pa[PATH_MAX];
+			    char *p2, *p3;
+			    int ne = noerrs, md = opts[MARKDIRS];
+
+			    /* These are used in the globbing code to make *
+			     * things a bit faster.                        */
+			    if (ispattern || mstack)
+				glob_pre = glob_suf = NULL;
+			    else {
+				glob_pre = fpre;
+				glob_suf = fsuf;
+			    }
+			    noerrs = 1;
+			    addwhat = -6;
+			    strcpy(pa, prpre);
+			    o = strlen(pa);
+			    opts[MARKDIRS] = 0;
+
+			    /* The compctl -g string may contain more than *
+			     * one pattern, so we need a loop.             */
+			    while (*g) {
+				LinkList l = newlinklist();
+				int ng;
+
+				/* Find the blank terminating the pattern. */
+				while (*g && inblank(*g))
+				    g++;
+				/* Oops, we already reached the end of the
+				   string. */
+				if (!*g)
+				    break;
+				for (p = g + 1; *p && !inblank(*p); p++)
+				    if (*p == '\\' && p[1])
+					p++;
+				/* Get the pattern string. */
+				tokenize(g = dupstrpfx(g, p - g));
+				if (*g == '=')
+				    *g = Equals;
+				if (*g == '~')
+				    *g = Tilde;
+				remnulargs(g);
+				if ((*g == Equals || *g == Tilde) && !cc->withd) {
 				/* The pattern has a `~' or `=' at the  *
 				 * beginning, so we expand this and use *
 				 * the result.                          */
-				filesub(&g, 0);
-				addlinknode(l, dupstring(g));
-			    } else if (*g == '/' && !cc->withd)
+				    filesub(&g, 0);
+				    addlinknode(l, dupstring(g));
+				} else if (*g == '/' && !cc->withd)
 				/* The pattern is a full path (starting *
 				 * with '/'), so add it unchanged.      */
-				addlinknode(l, dupstring(g));
-			    else {
+				    addlinknode(l, dupstring(g));
+				else {
 				/* It's a simple pattern, so append it to *
 				 * the path we have on the command line.  */
-				strcpy(pa + o, g);
-				addlinknode(l, dupstring(pa));
-			    }
-			    /* Do the globbing. */
-			    ng = opts[NULLGLOB];
-			    opts[NULLGLOB] = 1;
-			    globlist(l);
-			    opts[NULLGLOB] = ng;
-			    /* Get the results. */
-			    if (nonempty(l) && peekfirst(l)) {
-				for (p2 = (char *)peekfirst(l); *p2; p2++)
-				    if (itok(*p2))
-					break;
-				if (!*p2) {
-				    if ((*g == Equals || *g == Tilde ||
-					*g == '/') || cc->withd) {
-					/* IF the pattern started with `~',  *
-					 * `=', or `/', add the result only, *
-					 * if it really matches what we have *
-					 * on the line.                      *
-					 * Do this if an initial directory   *
-					 * was specified, too.               */
-					while ((p2 = (char *)ugetnode(l)))
-					    if (strpfx(prpre, p2))
-						addmatch(p2 + pl, NULL);
-				    } else {
-					/* Otherwise ignore the path we *
-					 * prepended to the pattern.    */
-					while ((p2 = p3 =
-						(char *)ugetnode(l))) {
-					    for (ns = sf1; *p3 && ns; p3++)
-						if (*p3 == '/')
-						    ns--;
-
-					    addmatch(p3, NULL);
+				    strcpy(pa + o, g);
+				    addlinknode(l, dupstring(pa));
+				}
+				/* Do the globbing. */
+				ng = opts[NULLGLOB];
+				opts[NULLGLOB] = 1;
+				globlist(l);
+				opts[NULLGLOB] = ng;
+				/* Get the results. */
+				if (nonempty(l) && peekfirst(l)) {
+				    for (p2 = (char *)peekfirst(l); *p2; p2++)
+					if (itok(*p2))
+					    break;
+				    if (!*p2) {
+					if ((*g == Equals || *g == Tilde ||
+					     *g == '/') || cc->withd) {
+					    /* IF the pattern started with `~',  *
+					     * `=', or `/', add the result only, *
+					     * if it really matches what we have *
+					     * on the line.                      *
+					     * Do this if an initial directory   *
+					     * was specified, too.               */
+					    while ((p2 = (char *)ugetnode(l)))
+						if (strpfx(prpre, p2))
+						    addmatch(p2 + pl, NULL);
+					} else {
+					    /* Otherwise ignore the path we *
+					     * prepended to the pattern.    */
+					    while ((p2 = p3 =
+						    (char *)ugetnode(l))) {
+						for (ns = sf1; *p3 && ns; p3++)
+						    if (*p3 == '/')
+							ns--;
+
+						addmatch(p3, NULL);
+					    }
 					}
 				    }
 				}
+				pa[o] = '\0';
+				g = p;
 			    }
-			    pa[o] = '\0';
-			    g = p;
+			    glob_pre = glob_suf = NULL;
+			    noerrs = ne;
+			    opts[MARKDIRS] = md;
 			}
-			glob_pre = glob_suf = NULL;
-			noerrs = ne;
-			opts[MARKDIRS] = md;
 		    }
+		    dirs++;
 		}
+		prpre = NULL;
 	    }
 	}
+	lppre = lpsuf = NULL;
+	lppl = lpsl = 0;
     }
-    /* Use tricat() instead of dyncat() to get zalloc()'d memory. */
     if (ic) {
 	/* Now change the `~' and `=' tokens to the real characters so *
 	 * that things starting with these characters will be added.   */
-	char *orpre = rpre;
-
-	rpre = tricat("", (ic == Tilde) ? "~" : "=", rpre);
+	rpre = dyncat((ic == Tilde) ? "~" : "=", rpre);
 	rpl++;
-	zsfree(orpre);
     }
     if (!ic && (cc->mask & CC_COMMPATH) && !*ppre && !*psuf) {
 	/* If we have to complete commands, add alias names, *
@@ -2802,7 +4090,7 @@ makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized)
 
 	    addlinknode(args, cc->func);
 
-	    if (*delit) {
+	    if (delit) {
 		p = dupstrpfx(os, ooffs);
 		untokenize(p);
 		addlinknode(args, p);
@@ -2949,41 +4237,6 @@ makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized)
 	/* Add the two types of aliases. */
 	dumphashtable(aliastab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
 
-    /* If we have no matches, ignore fignore. */
-    if (empty(matches)) {
-	matches = fmatches;
-	firstm = ffirstm;
-	shortest = fshortest;
-	ab = fab;
-	ae = fae;
-	shortl = fshortl;
-    }
-
-    /* Make an array from the list of matches. */
-    makearray(matches);
-    PERMALLOC {
-	amatches = arrdup(amatches);
-	if (firstm)
-	    firstm = ztrdup(firstm);
-	/* And quote the prefixes/suffixes. */
-	if (hasspecial(s)) {
-	    zfree(lpre, lpl);
-	    zfree(lsuf, lsl);
-	    lpre = zalloc(lpl + 1);
-	    memcpy(lpre, s, lpl);
-	    lpre[lpl] = '\0';
-	    lsuf = ztrdup(s + offs);
-	    quotepresuf(&lpre);
-	    quotepresuf(&lsuf);
-	    untokenize(lpre);
-	    untokenize(lsuf);
-	}
-	quotepresuf(&fpre);
-	quotepresuf(&fsuf);
-	quotepresuf(&ppre);
-	quotepresuf(&psuf);
-    } LASTALLOC;
-
     if (!errflag && cc->ylist) {
 	/* generate the user-defined display list: if anything fails, *
 	 * we silently allow the normal completion list to be used.   */
@@ -2996,14 +4249,21 @@ makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized)
 	} else if ((list = getshfunc(cc->ylist)) != &dummy_list) {
 	    /* from function:  pass completions as arg list */
 	    LinkList args = newlinklist();
-	    int addlen = strlen(rpre) + strlen(rsuf) + 1;
+	    LinkNode ln;
+	    Cmatch m;
 
 	    addlinknode(args, cc->ylist);
-	    for (yaptr = amatches; *yaptr; yaptr++) {
-		/* can't use tricat(). rats. */
-		char *ptr = (char *)halloc(addlen + strlen(*yaptr));
-		sprintf(ptr, "%s%s%s", rpre, *yaptr, rsuf);
-		addlinknode(args, ptr);
+	    for (ln = firstnode(matches); ln; ln = nextnode(ln)) {
+		m = (Cmatch) getdata(ln);
+		if (m->ppre) {
+		    char *p = (char *) halloc(strlen(m->ppre) + strlen(m->str) +
+					      strlen(m->psuf) + 1);
+
+		    sprintf(p, "%s%s%s", m->ppre, m->str, m->psuf);
+		    addlinknode(args, dupstring(p));
+		}
+		else
+		    addlinknode(args, dupstring(m->str));
 	    }
 
 	    /* No harm in allowing read -l and -c here, too */
@@ -3012,70 +4272,80 @@ makecomplist(char *s, int incmd, int *delit, int *compadd, int untokenized)
 	    incompctlfunc = 0;
 	    uv = "reply";
 	}
-	if (uv && (yaptr = get_user_var(uv))) {
-	    PERMALLOC {
-		aylist = arrdup(yaptr);
-	    } LASTALLOC;
+	
+	if ((tt = cc->explain)) {
+	    if (cc->mask & CC_EXPANDEXPL && !parsestr(tt = dupstring(tt))) {
+		singsub(&tt);
+		untokenize(tt);
+	    }
+	    expl->str = tt;
+	    addexpl();
 	}
+	if (uv && (yaptr = get_user_var(uv)))
+	    endcmgroup(yaptr);
+	else
+	    endcmgroup(NULL);
+	begcmgroup("default", 0);
     }
-
-    /* Get the explanation string we will have to print:    *
-     * do this here in case a -y function alters the messge */
-    if ((expl = cc->explain)) {
-	if (cc->mask & CC_EXPANDEXPL && !parsestr(expl = dupstring(expl))) {
-	    singsub(&expl);
-	    untokenize(expl);
+    else if ((tt = cc->explain)) {
+	if (cc->mask & CC_EXPANDEXPL && !parsestr(tt = dupstring(tt))) {
+	    singsub(&tt);
+	    untokenize(tt);
 	}
-	expl = ztrdup(expl);
+	expl->str = tt;
+	addexpl();
     }
+    if (cc->subcmd) {
+	/* Handle -l sub-completion. */
+	char **ow = clwords, *os = cmdstr, *ops = NULL;
+	int oldn = clwnum, oldp = clwpos;
+	unsigned long occ = ccont;
+	
+	ccont = 0;
+	
+	/* So we restrict the words-array. */
+	if (brange >= clwnum)
+	    brange = clwnum - 1;
+	if (brange < 1)
+	    brange = 1;
+	if (erange >= clwnum)
+	    erange = clwnum - 1;
+	if (erange < 1)
+	    erange = 1;
+	clwnum = erange - brange + 1;
+	clwpos = clwpos - brange;
+	
+	if (cc->subcmd[0]) {
+	    /* And probably put the command name given to the flag *
+	     * in the array.                                       */
+	    clwpos++;
+	    clwnum++;
+	    incmd = 0;
+	    ops = clwords[brange - 1];
+	    clwords[brange - 1] = cc->subcmd;
+	    cmdstr = ztrdup(cc->subcmd);
+	    clwords += brange - 1;
+	} else {
+	    cmdstr = ztrdup(clwords[brange]);
+	    incmd = !clwpos;
+	    clwords += brange;
+	}
+	/* Produce the matches. */
+	makecomplistcmd(s, incmd);
 
-    remsuffix = (cc->mask & CC_REMOVE);
-    ccsuffix = cc->suffix;
-
-    validlist = 1;
-    if (nmatches && !errflag)
-	return 0;
-
-    if ((isf || cc->xor) && !parampre) {
-	/* We found no matches, but there is a xor'ed completion: *
-	 * fine, so go back and continue with that compctl.       */
-	errflag = 0;
-	cc = cc->xor;
-	isf = 0;
-	wb = owb;
-	we = owe;
-	cs = ocs;
-	ll = oll;
-	strcpy((char *)line, (char *)ol);
-	offs = oloffs;
-	s = dupstring(os);
-	free(amatches);
-	zsfree(rpre);
-	zsfree(rsuf);
-	zsfree(lpre);
-	zsfree(lsuf);
-	zsfree(ppre);
-	zsfree(psuf);
-	zsfree(fpre);
-	zsfree(fsuf);
-	zsfree(prpre);
-	zsfree(parampre);
-	zsfree(qparampre);
-	zsfree(firstm);
-	if (expl)
-	    zsfree(expl);
-	expl = NULL;
-	if (aylist)
-	    freearray(aylist);
-	aylist = NULL;
-	goto xorrec;
+	/* And restore the things we changed. */
+	clwords = ow;
+	zsfree(cmdstr);
+	cmdstr = os;
+	clwnum = oldn;
+	clwpos = oldp;
+	if (ops)
+	    clwords[brange - 1] = ops;
+	ccont = occ;
     }
-
-    /* No matches and xor'ed completion: restore the command line if  *
-     * it was alredy quoted, which is the case when s is untokenized. */
-    if (untokenized)
-	strcpy((char *)line, (char *)ol);
-    return 1;
+    uremnode(ccstack, firstnode(ccstack));
+    if (cc->matcher)
+	mstack = mstack->next;
 }
 
 /* Invalidate the completion list. */
@@ -3086,31 +4356,11 @@ invalidatelist(void)
 {
     if(showinglist == -2)
 	listmatches();
-    if(validlist) {
-	freearray(amatches);
-	if (aylist)
-	    freearray(aylist);
-	aylist = NULL;
-	if (expl)
-	    zsfree(expl);
-	expl = 0;
-	zsfree(rpre);
-	zsfree(rsuf);
-	zsfree(lpre);
-	zsfree(lsuf);
-	zsfree(ppre);
-	zsfree(psuf);
-	zsfree(fpre);
-	zsfree(fsuf);
-	zsfree(prpre);
-	zsfree(parampre);
-	zsfree(qparampre);
-	zsfree(firstm);
-	if (ccmain != &cc_dummy)
-	    freecompctl(ccmain);
-    }
-    lastambig = menucmp = showinglist = validlist = 0;
+    if(validlist)
+	freematches();
+    lastambig = menucmp = validlist = showinglist = 0;
     menucur = NULL;
+    compwidget = NULL;
 }
 
 /* Get the words from a variable or a compctl -k list. */
@@ -3177,55 +4427,356 @@ get_user_var(char *nam)
 
 /**/
 static int
-strbpcmp(const void *a, const void *b)
+strbpcmp(char **aa, char **bb)
 {
-    char *aa = *((char **)a), *bb = *((char **)b);
+    char *a = *aa, *b = *bb;
 
-    while (*aa && *bb) {
-	if (*aa == '\\')
-	    aa++;
-	if (*bb == '\\')
-	    bb++;
-	if (*aa != *bb)
-	    return (int)(*aa - *bb);
-	if (*aa)
-	    aa++;
-	if (*bb)
-	    bb++;
+    while (*a && *b) {
+	if (*a == '\\')
+	    a++;
+	if (*b == '\\')
+	    b++;
+	if (*a != *b)
+	    return (int)(*a - *b);
+	if (*a)
+	    a++;
+	if (*b)
+	    b++;
     }
-    return (int)(*aa - *bb);
+    return (int)(*a - *b);
+}
+
+/* The comparison function for matches (used for sorting). */
+
+static int
+matchcmp(Cmatch *a, Cmatch *b)
+{
+    return strbpcmp(&((*a)->str), &((*b)->str));
 }
 
-/* Make an array from a linked list */
+/* This tests, whether two matches are equal (would produce the same  *
+ * strings on the command line). */
+
+#define matchstreq(a, b) ((!(a) && !(b)) || ((a) && (b) && !strcmp((a), (b))))
+
+static int
+matcheq(Cmatch a, Cmatch b)
+{
+    return matchstreq(a->ipre, b->ipre) &&
+	matchstreq(a->pre, b->pre) &&
+	matchstreq(a->ppre, b->ppre) &&
+	matchstreq(a->str, b->str) &&
+	matchstreq(a->psuf, b->psuf) &&
+	matchstreq(a->suf, b->suf);
+}
+
+/* Make an array from a linked list. The second argument says whether *
+ * the array should be sorted. The third argument is used to return   *
+ * the number of elements in the resulting array. The fourth argument *
+ * is used to return the number of NOLIST elements. */
 
 /**/
-static void
-makearray(LinkList l)
+static Cmatch *
+makearray(LinkList l, int s, int *np, int *nlp)
 {
-    char **ap, **bp, **cp;
+    Cmatch *ap, *bp, *cp, *rp;
     LinkNode nod;
+    int n, nl = 0;
 
     /* Build an array for the matches. */
-    ap = amatches = (char **)ncalloc(((nmatches = countlinknodes(l)) + 1) *
-				     sizeof(char *));
+    rp = ap = (Cmatch *)ncalloc(((n = countlinknodes(l)) + 1) *
+				sizeof(Cmatch));
 
     /* And copy them into it. */
     for (nod = firstnode(l); nod; incnode(nod))
-	*ap++ = (char *)getdata(nod);
+	*ap++ = (Cmatch) getdata(nod);
     *ap = NULL;
 
-    /* Now sort the array. */
-    qsort((void *) amatches, nmatches, sizeof(char *),
-	       (int (*) _((const void *, const void *)))strbpcmp);
+    if (s == 1) {
+	char **ap, **bp, **cp;
+
+	/* Now sort the array (it contains strings). */
+	qsort((void *) rp, n, sizeof(char *),
+	      (int (*) _((const void *, const void *)))strbpcmp);
+
+	/* And delete the ones that occur more than once. */
+	for (ap = cp = (char **) rp; *ap; ap++) {
+	    *cp++ = *ap;
+	    for (bp = ap; bp[1] && !strcmp(*ap, bp[1]); bp++, n--);
+	    ap = bp;
+	}
+	*cp = NULL;
+    }
+    else if (s) {
+	/* Now sort the array (it contains matches). */
+	qsort((void *) rp, n, sizeof(Cmatch),
+	      (int (*) _((const void *, const void *)))matchcmp);
+
+	/* And delete the ones that occur more than once. */
+	for (ap = cp = rp; *ap; ap++) {
+	    *cp++ = *ap;
+	    for (bp = ap; bp[1] && matcheq(*ap, bp[1]); bp++, n--);
+	    ap = bp;
+	    /* Mark those, that would show the same string in the list. */
+	    for (; bp[1] && !strcmp((*ap)->str, (bp[1])->str); bp++) {
+		(bp[1])->flags |= CMF_NOLIST; nl++;
+	    }
+	}
+	*cp = NULL;
+    }
+    if (np)
+	*np = n;
+    if (nlp)
+	*nlp = nl;
+    return rp;
+}
+
+/* This begins a new group of matches. */
+
+/**/
+static void
+begcmgroup(char *n, int nu)
+{
+    if (n) {
+	Cmgroup p = amatches;
+
+	while (p) {
+	    if (p->name && ((nu && !p->lallccs) || (!nu && p->lallccs)) &&
+		!strcmp(n, p->name)) {
+		mgroup = p;
+
+		expls = p->lexpls;
+		matches = p->lmatches;
+		fmatches = p->lfmatches;
+		allccs = p->lallccs;
+
+		return;
+	    }
+	    p = p->next;
+	}
+    }
+    mgroup = (Cmgroup) halloc(sizeof(struct cmgroup));
+    mgroup->name = n;
+    mgroup->flags = mgroup->lcount = mgroup->mcount = 0;
+    mgroup->matches = NULL;
+    mgroup->ylist = NULL;
+    mgroup->expls = NULL;
+
+    mgroup->lexpls = expls = newlinklist();
+    mgroup->lmatches = matches = newlinklist();
+    mgroup->lfmatches = fmatches = newlinklist();
+
+    mgroup->lallccs = allccs = (nu ? NULL : newlinklist());
+
+    mgroup->next = amatches;
+    amatches = mgroup;
+}
+
+/* End the current group for now. */
+
+/**/
+static void
+endcmgroup(char **ylist)
+{
+    mgroup->ylist = ylist;
+}
+
+/* Add an explanation string to the current group, joining duplicates. */
+
+/**/
+static void
+addexpl(void)
+{
+    LinkNode n;
+    Cexpl e;
+
+    for (n = firstnode(expls); n; incnode(n)) {
+	e = (Cexpl) getdata(n);
+	if (!strcmp(expl->str, e->str)) {
+	    e->count += expl->count;
+	    e->fcount += expl->fcount;
+
+	    return;
+	}
+    }
+    addlinknode(expls, expl);
+}
+
+/* This duplicates one match. */
+
+/**/
+static Cmatch
+dupmatch(Cmatch m)
+{
+    Cmatch r;
+
+    r = (Cmatch) ncalloc(sizeof(struct cmatch));
+
+    r->str = ztrdup(m->str);
+    r->ipre = ztrdup(m->ipre);
+    r->ripre = ztrdup(m->ripre);
+    r->ppre = ztrdup(m->ppre);
+    r->psuf = ztrdup(m->psuf);
+    r->prpre = ztrdup(m->prpre);
+    r->pre = m->pre;
+    r->suf = m->suf;
+    r->flags = m->flags;
+    r->brpl = m->brpl;
+    r->brsl = m->brsl;
+
+    return r;
+}
+
+/* This duplicates all groups of matches. */
+
+/**/
+static void
+permmatches(void)
+{
+    Cmgroup g = amatches, n;
+    Cmatch *p, *q;
+    Cexpl *ep, *eq, e, o;
+    Compctl *cp, *cq;
+    int nn, nl, fi = 0;
+
+    amatches = lmatches = NULL;
+    nmatches = smatches = 0;
+
+    if (!ainfo->count) {
+	ainfo = fainfo;
+	fi = 1;
+    }
+    while (g) {
+	HEAPALLOC {
+	    if (empty(g->lmatches))
+		/* We have no matches, try ignoring fignore. */
+		g->lmatches = g->lfmatches;
+
+	    g->matches = makearray(g->lmatches,
+				   ((g->flags & CGF_NOSORT) ? 0 : 2),
+				   &nn, &nl);
+	    g->mcount = nn;
+	    g->lcount = nn - nl;
+	    if (g->ylist) 
+		g->lcount = arrlen(g->ylist);
+
+	    g->expls = (Cexpl *) makearray(g->lexpls, 0, &(g->ecount), NULL);
+
+	    g->ccount = 0;
+	    g->ccs = NULL;
+	} LASTALLOC;
+
+	nmatches += g->mcount;
+	smatches += g->lcount;
+
+	n = (Cmgroup) ncalloc(sizeof(struct cmgroup));
+
+	if (!lmatches)
+	    lmatches = n;
+	if (amatches)
+	    amatches->prev = n;
+	n->next = amatches;
+	amatches = n;
+	n->prev = 0;
+
+	n->flags = g->flags;
+	n->mcount = g->mcount;
+	n->matches = p = (Cmatch *) ncalloc((n->mcount + 1) *
+					    sizeof(Cmatch));
+	for (q = g->matches; *q; q++, p++)
+	    *p = dupmatch(*q);
+	*p = NULL;
+
+	n->lcount = g->lcount;
+	if (g->ylist)
+	    n->ylist = arrdup(g->ylist);
+	else
+	    n->ylist = NULL;
+
+	if ((n->ecount = g->ecount)) {
+	    n->expls = ep = (Cexpl *) ncalloc((n->ecount + 1) *
+					      sizeof(Cexpl));
+	    for (eq = g->expls; (o = *eq); eq++, ep++) {
+		*ep = e = (Cexpl) ncalloc(sizeof(struct cexpl));
+		e->count = (fi ? o->fcount : o->count);
+		e->str = ztrdup(o->str);
+	    }
+	    *ep = NULL;
+	}
+	else
+	    n->expls = NULL;
+
+	if ((n->ccount = g->ccount)) {
+	    n->ccs = cp = (Compctl *) ncalloc((n->ccount + 1) *
+					      sizeof(Compctl));
+	    for (cq = g->ccs; *cq; cq++, cp++)
+		*cp = *cq;
+	    *cp = NULL;
+	}
+	else
+	    n->ccs = NULL;
+	g = g->next;
+    }
+}
+
+/* This frees one match. */
+
+/**/
+static void
+freematch(Cmatch m)
+{
+    if (!m) return;
+
+    zsfree(m->str);
+    zsfree(m->ipre);
+    zsfree(m->ripre);
+    zsfree(m->ppre);
+    zsfree(m->psuf);
+    zsfree(m->prpre);
+
+    zfree(m, sizeof(m));
+}
+
+/* This frees the groups of matches. */
+
+/**/
+static void
+freematches(void)
+{
+    Cmgroup g = amatches, n;
+    Cmatch *m;
+    Cexpl *e;
+    Compctl *c;
+
+    while (g) {
+	n = g->next;
+	
+	for (m = g->matches; *m; m++)
+	    freematch(*m);
+
+	if (g->ylist)
+	    freearray(g->ylist);
+
+	if ((e = g->expls)) {
+	    while (*e) {
+		zsfree((*e)->str);
+		free(*e);
+		e++;
+	    }
+	    free(g->expls);
+	}
+	if ((c = g->ccs)) {
+	    while (*c) {
+		if (*c != &cc_dummy)
+		    freecompctl(*c);
+		c++;
+	    }
+	    free(g->ccs);
+	}
+	free(g);
 
-    /* And delete the ones that occur more than once. */
-    for (ap = cp = amatches; *ap; ap++) {
-	*cp++ = *ap;
-	for (bp = ap; bp[1] && !strcmp(*ap, bp[1]); bp++);
-	ap = bp;
+	g = n;
     }
-    *cp = NULL;
-    nmatches = arrlen(amatches);
 }
 
 /* Handle the case were we found more than one match. */
@@ -3235,16 +4786,16 @@ static void
 do_ambiguous(void)
 {
     int p = (usemenu || ispattern), atend = (cs == we);
-    int inv = 0;
+    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 (shortest && shortl == 0 && isset(RECEXACT) &&
+    if (ainfo && ainfo->exact == 1 && isset(RECEXACT) &&
 	(usemenu == 0 || unset(AUTOMENU))) {
-	do_single(shortest);
+	do_single(ainfo->exactm);
 	invalidatelist();
 	return;
     }
@@ -3253,6 +4804,7 @@ do_ambiguous(void)
      * but this might be overridden below if we can complete an          *
      * unambiguous prefix.                                               */
     lastambig = 1;
+
     if(p) {
 	/* p is set if we are in a position to start using menu completion *
 	 * due to one of the menu completion options, or due to the        *
@@ -3261,21 +4813,93 @@ do_ambiguous(void)
 	 * normal menu completion options.                                 */
 	do_ambig_menu();
     } else {
+	int ics = cs, ocs, pl = 0, l, lp, ls;
+	char *ps;
+	Cline lc;
+
+	if (!ainfo)
+	    return;
+
+	fixsuffix();
+
+	/* Delete the old stuff from the command line. */
+	cs = wb;
+	foredel(we - wb);
+
 	/* Sort-of general case: we have an ambiguous completion, and aren't *
 	 * starting menu completion or doing anything really weird.  We need *
 	 * to insert any unambiguous prefix and suffix, if possible.         */
-	if(ab)
-	    inststrlen(firstm, 1, ab);
-	if(ae && !atend)
-	    inststrlen(firstm + strlen(firstm) - ae, 0, ae);
-	if(ab || (ae && !atend))
-	    inv = 1;
+
+	if (ainfo->iprefix && *ainfo->iprefix) {
+	    inststrlen(ainfo->iprefix, 1, -1);
+	    inststrlen(ainfo->pprefix, 1, -1);
+	    ps = ainfo->iaprefix;
+	    lc = ainfo->ilinecl;
+	    lp = ainfo->icpl;
+	    ls = ainfo->icsl;
+	} else {
+	    if (ainfo->noipre && ainfo->pprefix) {
+		pl = strlen(ainfo->pprefix);
+		inststrlen(ainfo->pprefix, 1, pl);
+	    }
+	    ps = ainfo->aprefix;
+	    lc = ainfo->linecl;
+	    lp = ainfo->cpl;
+	    ls = ainfo->csl;
+	}
+	if (lc) {
+	    int sl = 0;
+
+	    if (lp) {
+		if (ls) {
+		    if (ainfo->firstm->psuf)
+			merge_cline(lc, ps, lp,
+				    dyncat(ainfo->firstm->str,
+					   ainfo->firstm->psuf),
+				    ls, (sl = strlen(ainfo->firstm->psuf)));
+		    else
+			merge_cline(lc, ps, lp, ainfo->firstm->str, ls, 0);
+		} else
+		    merge_cline(lc, ps, lp, NULL, 0, 0);
+	    }
+	    inst_cline(lc, pl, sl);
+	} else {
+	    inststrlen(ps, 1, -1);
+	    ocs = cs;
+	    if (brbeg && *brbeg) {
+		cs = wb + brpl + pl;
+		l = strlen(brbeg);
+		inststrlen(brbeg, 1, l);
+		ocs += l;
+		cs = ocs;
+	    }
+	    if(ainfo->suflen && !atend)
+		inststrlen(ainfo->firstm->str +
+			   strlen(ainfo->firstm->str) - ainfo->suflen, 1,
+			   ainfo->suflen);
+	    if (ainfo->firstm->psuf)
+		inststrlen(ainfo->firstm->psuf, 0, -1);
+	    if (brend && *brend) {
+		cs -= brsl;
+		inststrlen(brend, 1, -1);
+	    }
+	    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.       */
+
+	if (isset(RECEXACT) && !lc && !ainfo->prerest)
+	    am = 1;
+
 	/* If the LIST_AMBIGUOUS option (meaning roughly `show a list only *
 	 * if the completion is completely ambiguous') is set, and some    *
 	 * 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) && inv) {
+	if(isset(LISTAMBIGUOUS) && !am &&
+	   (ics != cs || (ainfo->suflen && !atend))) {
 	    invalidatelist();
 	    lastambig = 0;
 	    return;
@@ -3287,8 +4911,8 @@ do_ambiguous(void)
 	feep();
     if (isset(AUTOLIST) && !amenu && !showinglist)
 	showinglist = -2;
-    if(inv)
-	invalidatelist();
+    if (am)
+	lastambig = 1;
 }
 
 /* This is a stat that ignores backslashes in the filename.  The `ls' *
@@ -3317,97 +4941,92 @@ ztat(char *nam, struct stat *buf, int ls)
 
 /**/
 static void
-do_single(char *str)
+do_single(Cmatch m)
 {
     int l;
     int havesuff = 0;
 
+    char *str = m->str, *ppre = m->ppre, *psuf = m->psuf, *prpre = m->prpre;
+
+    if (!prpre) prpre = "";
+    if (!ppre) ppre = "";
+    if (!psuf) psuf = "";
+
     fixsuffix();
 
     if (!menucur) {
 	/* We are currently not in a menu-completion, *
 	 * so set the position variables.             */
-	if (ispattern) {
-	    cs = we;
-	    menupos = wb;
-	} else
-	    menupos = cs;
+	menupos = wb;
 	menuwe = (cs == we) || isset(ALWAYSTOEND);
 	menuend = we;
     }
     /* If we are already in a menu-completion or if we have done a *
      * glob completion, we have to delete some of the stuff on the *
      * command line.                                               */
-    if (menucur) {
-	if (menuinsc) {
-	    cs = menuend + lsl;
-	    foredel(menuinsc);
-	}
-	l = menulen;
-    } else if (ispattern)
-	l = we - wb;
+    if (menucur)
+	l = menulen + menuinsc;
     else
-	l = 0;
+	l = we - wb;
 
     menuinsc = 0;
     cs = menupos;
     foredel(l);
 
-    /* And than we insert the new string. */
-    inststrlen(str, 1, menulen = strlen(str));
+    /* And then we insert the new string. */
+    menulen = instmatch(m);
     menuend = cs;
+    cs = menupos + menulen;
 
-    cs += lsl;
-
-    if (ccsuffix) {
-	/* There is a compctl -S suffix.  Add it. */
-	if (!(haswhat & HAS_SUFFIX) && *ccsuffix) {
-	    havesuff = 1;
-	    inststr(ccsuffix);
-	    menuinsc = ztrlen(ccsuffix);
-	    if (remsuffix && menuwe)
-		makesuffix(menuinsc);
-	}
+    if (m->suf) {
 	havesuff = 1;
+	menuinsc = ztrlen(m->suf);
+	if (menuwe && (m->flags & CMF_REMOVE)) {
+	    makesuffix(menuinsc);
+	    if (menuinsc == 1)
+		suffixlen[m->suf[0]] = 1;
+	}
     } else {
 	/* There is no user-specified suffix, *
 	 * so generate one automagically.     */
-	if(parampre && parambr) {
+	if(m->ripre && (m->flags & CMF_PARBR)) {
 	    /*{{*/
 	    /* Completing a parameter in braces.  Add a removable `}' suffix. */
 	    inststrlen("}", 1, 1);
 	    menuinsc++;
+	    if (menuwe)
+		menuend++;
 	}
-	if(!(haswhat & HAS_MISC) ||
-	    	  (parampre && isset(AUTOPARAMSLASH))) {
-	    /* If we have only filenames or we completed a parameter name  *
+	if((m->flags & CMF_FILE) || (m->ripre && isset(AUTOPARAMSLASH))) {
+	    /* If we have a filename or we completed a parameter name      *
 	     * and AUTO_PARAM_SLASH is set, lets see if it is a directory. *
 	     * If it is, we append a slash.                                */
 	    char *p;
 	    struct stat buf;
 
 	    /* Build the path name. */
-	    if (ispattern || ic || parampre) {
+	    if (ispattern || ic || m->ripre) {
 		int ne = noerrs;
 
 		noerrs = 1;
 
-		if (parampre) {
-		    int pl = strlen(parampre);
-		    p = (char *) ncalloc(pl + strlen(lpre) + strlen(str) +
-					 strlen(lsuf) + 1);
-		    sprintf(p, "%s%s%s%s", parampre, lpre, str, lsuf);
+		if (m->ripre) {
+		    int pl = strlen(m->ripre);
+
+		    p = (char *) ncalloc(pl + strlen(str) + strlen(psuf) + 1);
+		    sprintf(p, "%s%s%s", m->ripre, str, psuf);
 		    if (pl && p[pl-1] == Inbrace)
 			strcpy(p+pl-1, p+pl);
 		}
 		else if (ic) {
-		    p = (char *) ncalloc(strlen(ppre) + strlen(fpre) + strlen(str) +
-					 strlen(fsuf) + strlen(psuf) + 2);
-		    sprintf(p, "%c%s%s%s%s%s", ic,
-			    ppre, fpre, str, fsuf, psuf);
+		    p = (char *) ncalloc(strlen(ppre) + strlen(str) +
+					 strlen(psuf) + 2);
+		    sprintf(p, "%c%s%s%s", ic, ppre, str, psuf);
+		}
+		else {
+		    p = (char *) ncalloc(strlen(ppre) + strlen(str) +
+					 strlen(psuf) + 1);
 		}
-		else
-		    p = dupstring(str);
 		parsestr(p);
 		if (ic)
 		    *p = ic;
@@ -3415,11 +5034,9 @@ do_single(char *str)
 
 		noerrs = ne;
 	    } else {
-		p = (char *) ncalloc((prpre ? strlen(prpre) : 0) + strlen(fpre) +
-				     strlen(str) + strlen(fsuf) + strlen(psuf) + 3);
-		sprintf(p, "%s%s%s%s%s",
-			(prpre && *prpre) ? prpre : "./", fpre, str,
-			fsuf, psuf);
+		p = (char *) ncalloc(strlen(prpre) + strlen(str) +
+				     strlen(psuf) + 3);
+		sprintf(p, "%s%s%s", (prpre && *prpre) ? prpre : "./", str, psuf);
 	    }
 	    /* And do the stat. */
 	    if (!ztat(p, &buf, 0) && S_ISDIR(buf.st_mode)) {
@@ -3427,7 +5044,9 @@ do_single(char *str)
 		havesuff = 1;
 		inststrlen("/", 1, 1);
 		menuinsc++;
-		if(menuwe && isset(AUTOREMOVESLASH)) {
+		if (menuwe)
+		    menuend++;
+		if ((!menucmp || menuwe) && isset(AUTOREMOVESLASH)) {
 		    makesuffix(1);
 		    suffixlen['/'] = 1;
 		}
@@ -3435,34 +5054,36 @@ do_single(char *str)
 	}
     }
     /* If completing in a brace expansion... */
-    if(complinbrace) {
-	if(havesuff) {
+    if (brbeg) {
+	if (havesuff) {
 	    /*{{*/
 	    /* If a suffix was added, and is removable, let *
 	     * `,' and `}' remove it.                       */
-	    if(isset(AUTOPARAMKEYS))
+	    if (isset(AUTOPARAMKEYS))
 		suffixlen[','] = suffixlen['}'] = suffixlen[256];
-	} else {
+	} else if (!menucmp) {
 	    /*{{*/
 	    /* Otherwise, add a `,' suffix, and let `}' remove it. */
+	    cs = menuend;
 	    havesuff = 1;
 	    inststrlen(",", 1, 1);
 	    menuinsc++;
-	    if(menuwe && isset(AUTOPARAMKEYS))
+	    makesuffix(1);
+	    if (menuwe && isset(AUTOPARAMKEYS))
 		suffixlen[','] = suffixlen['}'] = 1;
 	}
-    } else if(!menucmp && !havesuff) {
+    } else if (!menucmp && !havesuff) {
 	/* If we didn't add a suffix, add a space, unless we are *
 	 * doing menu completion.                                */
 	inststrlen(" ", 1, 1);
 	menuinsc++;
-	if(menuwe)
+	if (menuwe)
 	    makesuffix(1);
     }
-    if(menuwe && parampre && isset(AUTOPARAMKEYS))
-	makeparamsuffix(parambr, menuinsc);
+    if (menuwe && m->ripre && isset(AUTOPARAMKEYS))
+	makeparamsuffix(((m->flags & CMF_PARBR) ? 1 : 0), menuinsc);
 
-    if (!menuwe)
+    if (menucmp && !menuwe)
 	cs = menuend;
 }
 
@@ -3474,8 +5095,11 @@ do_ambig_menu(void)
 {
     menucmp = 1;
     menucur = NULL;
-    do_single(amatches[0]);
-    menucur = amatches;
+    menugrp = amatches;
+    while (!menugrp->mcount)
+	menugrp = menugrp->next;
+    do_single(menugrp->matches[0]);
+    menucur = menugrp->matches;
 }
 
 /* Return the length of the common prefix of s and t. */
@@ -3517,12 +5141,13 @@ static int
 printfmt(char *fmt, int n, int dopr)
 {
     char *p = fmt, nc[DIGBUFSIZE];
-    int l = 0, cc = 0;
+    int l = 0, cc = 0, b = 0, s = 0, u = 0, m;
 
     for (; *p; p++) {
 	/* Handle the `%' stuff (%% == %, %n == <number of matches>). */
 	if (*p == '%') {
 	    if (*++p) {
+		m = 0;
 		switch (*p) {
 		case '%':
 		    if (dopr)
@@ -3535,6 +5160,38 @@ printfmt(char *fmt, int n, int dopr)
 			fprintf(shout, nc);
 		    cc += strlen(nc);
 		    break;
+		case 'B':
+		    b = 1;
+		    tcout(TCBOLDFACEBEG);
+		    break;
+		case 'b':
+		    b = 0; m = 1;
+		    tcout(TCALLATTRSOFF);
+		    break;
+		case 'S':
+		    s = 1;
+		    tcout(TCSTANDOUTBEG);
+		    break;
+		case 's':
+		    s = 0; m = 1;
+		    tcout(TCSTANDOUTEND);
+		    break;
+		case 'U':
+		    u = 1;
+		    tcout(TCUNDERLINEBEG);
+		    break;
+		case 'u':
+		    u = 0; m = 1;
+		    tcout(TCUNDERLINEEND);
+		    break;
+		}
+		if (m) {
+		    if (b)
+			tcout(TCBOLDFACEBEG);
+		    if (s)
+			tcout(TCSTANDOUTBEG);
+		    if (u)
+			tcout(TCUNDERLINEBEG);
 		}
 	    } else
 		break;
@@ -3552,20 +5209,34 @@ printfmt(char *fmt, int n, int dopr)
     return l + (cc / columns);
 }
 
+/* This skips over matches that are not to be listed. */
+
+static Cmatch *
+skipnolist(Cmatch *p)
+{
+    while (*p && ((*p)->flags & CMF_NOLIST))
+	p++;
+
+    return p;
+}
+
 /* List the matches.  Note that the list entries are metafied. */
 
 /**/
 void
 listmatches(void)
 {
-    int longest = 1, fct, fw, colsz, t0, t1, ct, up, cl, xup = 0;
-    int off = 0, boff = 0, nboff = 0;
-    int of = (!aylist && isset(LISTTYPES) && !(haswhat & HAS_MISC));
-    char **arr, **ap, sav;
-    int nfpl, nfsl, nlpl, nlsl;
-    int listmax = getiparam("LISTMAX"), litnl = 0;
-    size_t (*strlenfn) _((char const *));
-
+    Cmgroup g;
+    Cmatch *p, m;
+    Cexpl *e;
+    int nlines = 0, ncols, colsz, ngr = 0, nlist = 0, longest = 1, pnl = 0;
+    int of = isset(LISTTYPES), opl = 0;
+    int listmax = getiparam("LISTMAX");
+
+    if (smatches < 2) {
+	showinglist = 0;
+	return;
+    }
 #ifdef DEBUG
     /* Sanity check */
     if(!validlist) {
@@ -3574,23 +5245,6 @@ listmatches(void)
     }
 #endif
 
-    /* Calculate lengths of prefixes/suffixes to be added */
-    nfpl = fpre ? niceztrlen(fpre) : 0;
-    nfsl = fsuf ? niceztrlen(fsuf) : 0;
-    nlpl = lpre ? niceztrlen(lpre) : 0;
-    nlsl = lsuf ? niceztrlen(lsuf) : 0;
-
-    /* Calculate the lengths of the prefixes/suffixes we have to ignore
-       during printing. */
-    if (ispattern && !aylist && !(haswhat & (HAS_MISC | HAS_PATHPAT))) {
-	if (ppre && *ppre)
-	    off = strlen(ppre);
-	if (psuf && *psuf) {
-	    boff = strlen(psuf);
-	    nboff = niceztrlen(psuf);
-	}
-    }
-
     /* Set the cursor below the prompt. */
     trashzle();
     showinglist = 0;
@@ -3599,86 +5253,92 @@ listmatches(void)
 		 (isset(ALWAYSLASTPROMPT) && zmult == 1)) ||
 	(unset(ALWAYSLASTPROMPT) && zmult != 1);
 
-    /* just to keep gcc happy */
-    fw = colsz = up = 0;
-    if (aylist) {
-	arr = aylist;
-	/* If no literal newlines, the remaining code should use strlen() */
-	strlenfn = (size_t (*) _((char const *)))strlen;
-
-	/* The hard bit here is that we are handling newlines literally.   *
-	 * In fact, we are in principle handling all characters literally, *
-	 * but it's quite enough work with just newlines.                  *
-	 * If there are such, we give up trying to print the list as       *
-	 * columns and print as rows, counting the extra newlines.         */
-	ct = 0;
-	for (ap = arr; *ap; ap++) {
-	    ct++;
-	    if (strchr(*ap, '\n'))
-		litnl++;
-	}
-	if (litnl) {
-	    colsz = ct;
-	    up = colsz + nlnct - clearflag;
-	    /* Count real newlines, as well as overflowing lines. */
-	    for (ap = arr; *ap; ap++) {
-		char *nlptr, *sptr = *ap;
-		while (sptr && *sptr) {
-		    up += (nlptr = strchr(sptr, '\n'))
-			? 1 + (nlptr-sptr)/columns
-			   : strlen(sptr)/columns;
-		    sptr = nlptr ? nlptr+1 : NULL;
+    for (g = amatches; g; g = g->next) {
+	char **pp = g->ylist;
+	int nl = 0, l;
+
+	if (pp) {
+	    /* We have an ylist, lets see, if it contains newlines. */
+	    while (!nl && *pp)
+		nl = !!strchr(*pp++, '\n');
+
+	    pp = g->ylist;
+	    if (nl) {
+		/* Yup, there are newlines, count lines. */
+		char *nlptr, *sptr;
+
+		g->flags |= CGF_LINES;
+		
+		while ((sptr = *pp)) {
+		    while (sptr && *sptr) {
+			nlines += (nlptr = strchr(sptr, '\n'))
+			    ? 1 + (nlptr-sptr)/columns
+			    : strlen(sptr)/columns;
+			sptr = nlptr ? nlptr+1 : NULL;
+		    }
+		    nlines++;
+		    pp++;
+		}
+		nlines--;
+	    }
+	    else {
+		while (*pp) {
+		    if ((l = strlen(*pp)) > longest)
+			longest = l;
+		    nlist++;
+		    pp++;
 		}
 	    }
 	}
-    } else {
-	arr = amatches;
-	ct = nmatches;
-	strlenfn = niceztrlen;
-    }
-
-
-    if (!litnl) {
-	/* Calculate the column width, the number of columns and the
-	   number of lines. */
-	for (ap = arr; *ap; ap++)
-	    if ((cl = strlenfn(*ap + off) - nboff +
-		 ((ispattern || aylist) ? 0 :
-		  (!(haswhat & HAS_MISC) ?
-		   nfpl + nfsl : nlpl + nlsl))) > longest)
-		longest = cl;
-	if (of)
-	    longest++;
-
-	fw = longest + 2;
-	fct = (columns + 1) / fw;
-	if (fct == 0) {
-	    fct = 1;
-	    colsz = ct;
-	    up = colsz + nlnct - clearflag;
-	    for (ap = arr; *ap; ap++)
-		up += (strlenfn(*ap + off) - nboff + of +
-		       ((ispattern || aylist) ? 0 :
-			(!(haswhat & HAS_MISC) ?
-			 nfpl + nfsl : nlpl + nlsl))) / columns;
-	} else {
-	    colsz = (ct + fct - 1) / fct;
-	    up = colsz + nlnct - clearflag + (ct == 0);
+	else {
+	    for (p = g->matches; (m = *p); p++) {
+		if (!(m->flags & CMF_NOLIST)) {
+		    if ((l = niceztrlen(m->str)) > longest)
+			longest = l;
+		    nlist++;
+		}
+	    }
 	}
+	if ((e = g->expls)) {
+	    while (*e) {
+		if ((*e)->count)
+		    nlines += 1 + printfmt((*e)->str, (*e)->count, 0);
+		e++;
+	    }
+	}
+	if (g->lcount)
+	    ngr++;
     }
-
-    /* Print the explanation string, if any. */
-    if (expl) {
-	xup = printfmt(expl, ct, 1) + 1;
-	putc('\n', shout);
-	up += xup;
+    longest += 2 + of;
+    if ((ncols = (columns + 1) / longest)) {
+	colsz = (nlist + ncols - 1) / ncols;
+	nlines += ngr - 1 + colsz + (nlist == 0);
+    } else {
+	ncols = 1;
+	colsz = 1;
+	opl = 1;
+	for (g = amatches; g; g = g->next) {
+	    char **pp = g->ylist;
+
+	    if (pp) {
+		if (!(g->flags & CGF_LINES)) {
+		    while (*pp) {
+			nlines += 1 + (strlen(*pp) / columns);
+			pp++;
+		    }
+		}
+	    } else
+		for (p = g->matches; (m = *p); p++)
+		    if (!(m->flags & CMF_NOLIST))
+			nlines += 1 + ((1 + niceztrlen(m->str)) / columns);
+	}
     }
 
     /* Maybe we have to ask if the user wants to see the list. */
-    if ((listmax && ct > listmax) || (!listmax && up >= lines)) {
+    if ((listmax && nlist > listmax) || (!listmax && nlines >= lines)) {
 	int qup;
-	setterm();
-	qup = printfmt("zsh: do you wish to see all %n possibilities? ", ct, 1);
+	zsetterm();
+	qup = printfmt("zsh: do you wish to see all %n possibilities? ", nlist, 1);
 	fflush(shout);
 	if (getzlequery() != 'y') {
 	    if (clearflag) {
@@ -3686,7 +5346,7 @@ listmatches(void)
 		tcmultout(TCUP, TCMULTUP, qup);
 		if (tccan(TCCLEAREOD))
 		    tcout(TCCLEAREOD);
-		tcmultout(TCUP, TCMULTUP, nlnct + xup);
+		tcmultout(TCUP, TCMULTUP, nlnct);
 	    } else
 		putc('\n', shout);
 	    return;
@@ -3702,88 +5362,125 @@ listmatches(void)
     }
 
     /* Now print the matches. */
-    for (t1 = 0; t1 != colsz; t1++) {
-	ap = arr + t1;
-	if (of) {
-	    /* We have to print the file types. */
-	    while (*ap) {
-		int t2;
-		char *pb;
-		struct stat buf;
-
-		/* Build the path name for the stat. */
-		if (ispattern) {
-		    int cut = strlen(*ap) - boff;
-
-		    sav = ap[0][cut];
-		    ap[0][cut] = '\0';
-		    nicezputs(*ap + off, shout);
-		    t2 = niceztrlen(*ap + off);
-		    ap[0][cut] = sav;
-		    pb = *ap;
-		} else {
-		    nicezputs(fpre, shout);
-		    nicezputs(*ap, shout);
-		    nicezputs(fsuf, shout);
-		    t2 = nfpl + niceztrlen(*ap) + nfsl;
-		    pb = (char *) halloc((prpre ? strlen(prpre) : 0) + 3 +
-					 strlen(fpre) + strlen(*ap) + strlen(fsuf));
-		    sprintf(pb, "%s%s%s%s",
-			    (prpre && *prpre) ? prpre : "./", fpre, *ap, fsuf);
+    g = amatches;
+    while (g) {
+	char **pp = g->ylist;
+
+	if ((e = g->expls)) {
+	    if (pnl) {
+		putc('\n', shout);
+		pnl = 0;
+	    }
+	    while (*e) {
+		if ((*e)->count) {
+		    printfmt((*e)->str, (*e)->count, 1);
+		    putc('\n', shout);
 		}
-		if (ztat(pb, &buf, 1))
-		    putc(' ', shout);
-		else
-		    /* Print the file type character. */
-		    putc(file_type(buf.st_mode), shout);
-		for (t0 = colsz; t0 && *ap; t0--, ap++);
-		if (*ap)
-		    /* And add spaces to make the columns aligned. */
-		    for (++t2; t2 < fw; t2++)
-			putc(' ', shout);
+		e++;
 	    }
-	} else
-	    while (*ap) {
-		int t2;
-
-		if (aylist) {
-		    zputs(*ap, shout);
-		    t2 = strlen(*ap);
-		} else if (ispattern) {
-		    int cut = strlen(*ap) - boff;
-
-		    sav = ap[0][cut];
-		    ap[0][cut] = '\0';
-		    nicezputs(*ap + off, shout);
-		    t2 = niceztrlen(*ap + off);
-		    ap[0][cut] = sav;
-		} else if (!(haswhat & HAS_MISC)) {
-		    nicezputs(fpre, shout);
-		    nicezputs(*ap, shout);
-		    nicezputs(fsuf, shout);
-		    t2 = nfpl + niceztrlen(*ap) + nfsl;
-		} else {
-		    nicezputs(lpre, shout);
-		    nicezputs(*ap, shout);
-		    nicezputs(lsuf, shout);
-		    t2 = nlpl + niceztrlen(*ap) + nlsl;
+	}
+	if (pp) {
+	    if (pnl) {
+		putc('\n', shout);
+		pnl = 0;
+	    }
+	    if (g->flags & CGF_LINES) {
+		while (*pp) {
+		    zputs(*pp, shout);
+		    if (*++pp)
+			putc('\n', shout);
 		}
-		for (t0 = colsz; t0 && *ap; t0--, ap++);
-		if (*ap)
-		    for (; t2 < fw; t2++)
-			putc(' ', shout);
 	    }
-	if (t1 != colsz - 1 || !clearflag)
-	    putc('\n', shout);
+	    else {
+		int n = g->lcount, nl = (n + ncols - 1) / ncols, i, a;
+		int nc = (opl ? 1 : (n + colsz - 1) / colsz);
+		char **pq;
+
+		while (n && nl--) {
+		    i = nc;
+		    pq = pp;
+		    while (n && i--) {
+			if (pq - g->ylist >= g->lcount)
+			    break;
+			zputs(*pq, shout);
+			if (i) {
+			    a = longest - strlen(*pq);
+			    while (a--)
+				putc(' ', shout);
+			}
+			pq += colsz;
+			n--;
+		    }
+		    if (n)
+			putc('\n', shout);
+		    pp++;
+		}
+	    }
+	}
+	else if (g->lcount) {
+	    int n = g->lcount, nl = (n + ncols - 1) / ncols, i, j, a;
+	    int nc = (opl ? 1 : (n + colsz - 1) / colsz);
+	    Cmatch *q;
+
+	    if (n && pnl) {
+		putc('\n', shout);
+		pnl = 0;
+	    }
+	    for (p = skipnolist(g->matches); n && nl--;) {
+		i = nc;
+		q = p;
+		while (n && i--) {
+		    if (!(m = *q))
+			break;
+		    nicezputs(m->str, shout);
+		    if (i)
+			a = longest - niceztrlen(m->str);
+
+		    if (of && m->flags & CMF_FILE) {
+			struct stat buf;
+			char *pb;
+
+			pb = (char *) halloc((m->prpre ? strlen(m->prpre) : 0) +
+					     3 + strlen(m->str));
+			sprintf(pb, "%s%s", (m->prpre ? m->prpre : "./"),
+				m->str);
+
+			if (ztat(pb, &buf, 1))
+			    putc(' ', shout);
+			else
+			    putc(file_type(buf.st_mode), shout);
+
+			a--;
+		    }
+		    if (i && !opl)
+			while (a--)
+			    putc(' ', shout);
+		    if (--n)
+			for (j = colsz; j && *q; j--)
+			    q = skipnolist(q + 1);
+		}
+		if (n) {
+		    putc('\n', shout);
+		    p = skipnolist(p + 1);
+		}
+	    }
+	}
+	if (g->lcount)
+	    pnl = 1;
+	g = g->next;
     }
-    if (clearflag)
+
+    if (clearflag) {
 	/* Move the cursor up to the prompt, if always_last_prompt *
 	 * is set and all that...                                  */
-	if (up < lines) {
-	    tcmultout(TCUP, TCMULTUP, up);
+	if ((nlines += nlnct - 1) < lines) {
+	    tcmultout(TCUP, TCMULTUP, nlines);
 	    showinglist = -1;
 	} else
 	    clearflag = 0, putc('\n', shout);
+    }
+    else
+	putc('\n', shout);
 }
 
 /* This is used to print expansions. */
@@ -3792,32 +5489,20 @@ listmatches(void)
 void
 listlist(LinkList l)
 {
-    int hw = haswhat, ip = ispattern;
-    char *lp = lpre, *ls = lsuf;
-    int nm = nmatches, vl = validlist;
-    char **am = amatches, **ay = aylist;
-    char *ex = expl;
+    struct cmgroup dg;
+    Cmgroup am = amatches;
+    int vl = validlist, sm = smatches;
 
-    haswhat = HAS_MISC;
-    ispattern = 0;
+    smatches = 1;
     validlist = 1;
-    lpre = lsuf = "";
-    aylist = NULL;
-    expl = NULL;
-
-    makearray(l);
+    amatches = &dg;
+    memset(&dg, 0, sizeof(struct cmgroup));
+    dg.ylist = (char **) makearray(l, 1, &(dg.lcount), NULL);
     listmatches();
-    showinglist = 0;
 
-    expl = ex;
     amatches = am;
-    aylist = ay;
-    nmatches = nm;
     validlist = vl;
-    lpre = lp;
-    lsuf = ls;
-    ispattern = ip;
-    haswhat = hw;
+    smatches = sm;
 }
 
 /* Expand the history references. */
diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c
index 8fe3e7f0b..4cdb3c52d 100644
--- a/Src/Zle/zle_utils.c
+++ b/Src/Zle/zle_utils.c
@@ -420,7 +420,7 @@ void
 handlefeep(void)
 {
     if(feepflag)
-	beep();
+	zbeep();
     feepflag = 0;
 }
 
diff --git a/Src/Zle/zle_vi.c b/Src/Zle/zle_vi.c
index a599d8091..46c6a705b 100644
--- a/Src/Zle/zle_vi.c
+++ b/Src/Zle/zle_vi.c
@@ -126,7 +126,7 @@ vigetkey(void)
 	char sav = line[cs];
 
 	line[cs] = '^';
-	refresh();
+	zrefresh();
 	c = getkey(0);
 	line[cs] = sav;
 	if(c == EOF) {
@@ -814,10 +814,10 @@ viswapcase(void)
 void
 vicapslockpanic(void)
 {
-    beep();
+    zbeep();
     statusline = "press a lowercase key to continue";
     statusll = strlen(statusline);
-    refresh();
+    zrefresh();
     while (!islower(getkey(0)));
     statusline = NULL;
 }
@@ -891,7 +891,7 @@ viquotedinsert(void)
 
     spaceinline(1);
     line[cs] = '^';
-    refresh();
+    zrefresh();
 #ifndef HAS_TIO
     sob = shttyinfo.sgttyb;
     sob.sg_flags = (sob.sg_flags | RAW) & ~ECHO;
@@ -899,7 +899,7 @@ viquotedinsert(void)
 #endif
     c = getkey(0);
 #ifndef HAS_TIO
-    setterm();
+    zsetterm();
 #endif
     foredel(1);
     if(c < 0)
diff --git a/Src/builtin.c b/Src/builtin.c
index 31f396d93..3aae0f769 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -2627,14 +2627,14 @@ bin_getopts(char *name, char **argv, char *ops, int func)
     if(opch == ':' || !(p = memchr(optstr, opch, lenoptstr))) {
 	p = "?";
 err:
-      zsfree(zoptarg);
+	zsfree(zoptarg);
 	if(quiet) {
 	    setsparam(var, ztrdup(p));
 	    zoptarg = metafy(optbuf, lenoptbuf, META_DUP);
 	} else {
 	    zerr(*p == '?' ? "bad option: -%c" :
 		"argument expected after -%c option", NULL, opch);
-          zoptarg=ztrdup("");
+	    zoptarg=ztrdup("");
 	    errflag = 0;
 	}
 	return 0;
diff --git a/Src/exec.c b/Src/exec.c
index 1b355d028..22fa526ec 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -1992,8 +1992,8 @@ restore_params(LinkList restorelist, LinkList removelist)
 		}
 	    } else
 		paramtab->addnode(paramtab, pm->nam, pm);
-	    if (pm->flags & PM_EXPORTED)
-		pm->env = addenv(pm->nam, getsparam(pm->nam));
+	    if ((pm->flags & PM_EXPORTED) && ((s = getsparam(pm->nam))))
+		pm->env = addenv(pm->nam, s);
 	}
     }
 }
diff --git a/Src/glob.c b/Src/glob.c
index be7a04515..0c66adec8 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -110,6 +110,8 @@ struct comp {
 #define C_CLOSURE	(C_ONEHASH|C_TWOHASH|C_OPTIONAL|C_STAR)
 #define C_LAST		16
 #define C_PATHADD	32
+#define C_LCMATCHUC	64
+#define C_IGNCASE	128
 
 /* Test macros for the above */
 #define CLOSUREP(c)	(c->stat & C_CLOSURE)
@@ -305,15 +307,24 @@ scanner(Complist q)
     if (!q)
 	return;
 
-    if ((closure = q->closure))	/* (foo/)# - match zero or more dirs */
+    if ((closure = q->closure)) {
+	/* (foo/)# - match zero or more dirs */
 	if (q->closure == 2)	/* (foo/)## - match one or more dirs */
 	    q->closure = 1;
 	else
 	    scanner(q->next);
+    }
     c = q->comp;
     /* Now the actual matching for the current path section. */
-    if (!(c->next || c->left) && !haswilds(c->str)) {
-	/* It's a straight string to the end of the path section. */
+    if (!(c->next || c->left) && !haswilds(c->str)
+	&& (!(c->stat & (C_LCMATCHUC|C_IGNCASE))
+	    || !strcmp(".", c->str) || !strcmp("..", c->str))) {
+	/*
+	 * We always need to match . and .. explicitly, even if we're
+	 * checking other strings for case-insensitive matches.
+	 *
+	 * It's a straight string to the end of the path section.
+	 */
 	int l = strlen(c->str);
 
 	if (l + !l + pathpos - pathbufcwd >= PATH_MAX) {
@@ -436,6 +447,52 @@ scanner(Complist q)
 
 /* Parse a series of path components pointed to by pptr */
 
+/* Flags to apply to current level of grourping */
+
+static int addflags;
+
+/**/
+static Comp
+compalloc(void)
+{
+    Comp c = (Comp) alloc(sizeof *c);
+    c->stat |= addflags;
+    return c;
+}
+
+/**/
+static int
+getglobflags()
+{
+    /* (#X): assumes we are still positioned on the initial '(' */
+    pptr++;
+    while (*++pptr && *pptr != Outpar) {
+	switch (*pptr) {
+	case 'l':
+	    /* Lowercase in pattern matches lower or upper in target */
+	    addflags |= C_LCMATCHUC;
+	    break;
+
+	case 'i':
+	    /* Fully case insensitive */
+	    addflags |= C_IGNCASE;
+	    break;
+
+	case 'I':
+	    /* Restore case sensitivity */
+	    addflags &= ~(C_LCMATCHUC|C_IGNCASE);
+	    break;
+
+	default:
+	    return 1;
+	}
+    }
+    if (*pptr != Outpar)
+	return 1;
+    pptr++;
+    return 0;
+}
+
 /* enum used with ksh-like patterns, @(...) etc. */
 
 enum { KF_NONE, KF_AT, KF_QUEST, KF_STAR, KF_PLUS, KF_NOT };
@@ -447,7 +504,7 @@ static Comp
 parsecomp(int gflag)
 {
     int kshfunc;
-    Comp c = (Comp) alloc(sizeof *c), c1, c2;
+    Comp c = compalloc(), c1, c2;
     char *cstr, *ls = NULL;
 
     /* In case of alternatives, code coming up is stored in tail. */
@@ -468,10 +525,10 @@ parsecomp(int gflag)
 	    c->str = dupstrpfx(cstr, pptr - cstr);
 	    pptr++;
 
-	    c1 = (Comp) alloc(sizeof *c1);
+	    c1 = compalloc();
 	    c1->stat |= C_STAR;
 
-	    c2 = (Comp) alloc(sizeof *c2);
+	    c2 = compalloc();
 	    if (!(c2->exclude = parsecomp(gflag)))
 		return NULL;
 	    if (!*pptr || *pptr == '/')
@@ -513,6 +570,39 @@ parsecomp(int gflag)
 		pptr++;
 	}
 
+	if (*pptr == Inpar && pptr[1] == Pound) {
+	    /* Found some globbing flags */
+	    char *eptr = pptr;
+	    if (kshfunc != KF_NONE)
+		eptr--;
+	    if (getglobflags())
+		return NULL;
+	    if (eptr == cstr) {
+		/* if no string yet, carry on and get one. */
+		c->stat = addflags;
+		cstr = pptr;
+		continue;
+	    }
+	    c->str = dupstrpfx(cstr, eptr - cstr);
+	    /*
+	     * The next bit simply handles the case where . or ..
+	     * is followed by a set of flags, but we need to force
+	     * them to be handled as a string.  Hardly worth it.
+	     */
+	    if (!*pptr || (!mode && *pptr == '/') || *pptr == Bar ||
+		(isset(EXTENDEDGLOB) && *pptr == Tilde &&
+		 pptr[1] && pptr[1] != Outpar && pptr[1] != Bar) ||
+		*pptr == Outpar) {
+		if (*pptr == '/' || !*pptr ||
+		    (isset(EXTENDEDGLOB) && *pptr == Tilde &&
+		     (gflag & GF_TOPLEV)))
+		    c->stat |= C_LAST;
+		return c;
+	    }
+	    if (!(c->next = parsecomp(gflag)))
+		return NULL;
+	    return c;
+	}
 	if (*pptr == Inpar) {
 	    /* Found a group (...) */
 	    char *startp = pptr, *endp;
@@ -552,16 +642,16 @@ parsecomp(int gflag)
 	    pptr = startp;
 	    c->str = dupstrpfx(cstr, (pptr - cstr) - (kshfunc != KF_NONE));
 	    pptr++;
-	    c2 = (Comp) alloc(sizeof *c);
+	    c2 = compalloc();
 	    c->next = c2;
 	    c2->next = (dpnd || kshfunc == KF_NOT) ?
-		c1 : (Comp) alloc(sizeof *c);
+		c1 : compalloc();
 	    if (!(c2->left = parsecompsw(0)))
 		return NULL;
 	    if (kshfunc == KF_NOT) {
 		/* we'd actually rather it didn't match.  Instead, match *
 		 * a star and put the parsed pattern into exclude.       */
-		Comp c3 = (Comp) alloc(sizeof *c3);
+		Comp c3 = compalloc();
 		c3->stat |= C_STAR;
 
 		c2->exclude = c2->left;
@@ -584,7 +674,7 @@ parsecomp(int gflag)
 	     */
 	    c->str = dupstrpfx(cstr, pptr - cstr);
 	    pptr++;
-	    c1 = (Comp) alloc(sizeof *c1);
+	    c1 = compalloc();
 	    c1->stat |= C_STAR;
 	    if (!(c2 = parsecomp(gflag)))
 		return NULL;
@@ -596,13 +686,13 @@ parsecomp(int gflag)
 	    /* repeat whatever we've just had (ls) zero or more times */
 	    if (!ls)
 		return NULL;
-	    c2 = (Comp) alloc(sizeof *c);
+	    c2 = compalloc();
 	    c2->str = dupstrpfx(ls, pptr - ls);
 	    pptr++;
 	    if (*pptr == Pound) {
 		/* need one or more matches: cheat by copying previous char */
 		pptr++;
-		c->next = c1 = (Comp) alloc(sizeof *c);
+		c->next = c1 = compalloc();
 		c1->str = c2->str;
 	    } else
 		c1 = c;
@@ -669,6 +759,7 @@ static Comp
 parsecompsw(int gflag)
 {
     Comp c1, c2, c3, excl = NULL, stail = tail;
+    int oaddflags = addflags;
     char *sptr;
 
     /*
@@ -709,7 +800,7 @@ parsecompsw(int gflag)
     tail = stail;
     if (*pptr == Bar || excl) {
 	/* found an alternative or something to exclude */
-	c2 = (Comp) alloc(sizeof *c2);
+	c2 = compalloc();
 	if (*pptr == Bar) {
 	    /* get the next alternative after the | */
 	    pptr++;
@@ -728,8 +819,10 @@ parsecompsw(int gflag)
 	    c2->next = stail;
 	if (gflag & GF_PATHADD)
 	    c2->stat |= C_PATHADD;
-	return c2;
+	c1 = c2;
     }
+    if (!(gflag & GF_TOPLEV))
+	addflags = oaddflags;
     return c1;
 }
 
@@ -758,7 +851,7 @@ parsecomplist(void)
 	    errflag = 1;
 	    return NULL;
 	}
-	p1->comp = (Comp) alloc(sizeof *p1->comp);
+	p1->comp = compalloc();
 	p1->comp->stat |= C_LAST;	/* end of path component  */
 	p1->comp->str = dupstring("*");
 	*p1->comp->str = Star;		/* match anything...      */
@@ -814,8 +907,28 @@ static Complist
 parsepat(char *str)
 {
     mode = 0;			/* path components present */
+    addflags = 0;
     pptr = str;
     tail = NULL;
+    /*
+     * Check for initial globbing flags, so that they don't form
+     * a bogus path component.
+     */
+    if (*pptr == Inpar && pptr[1] == Pound && isset(EXTENDEDGLOB) &&
+	getglobflags())
+	return NULL;
+
+    /* Now there is no (#X) in front, we can check the path. */
+    if (!pathbuf)
+	pathbuf = zalloc(pathbufsz = PATH_MAX);
+    DPUTS(pathbufcwd, "BUG: glob changed directory");
+    if (*pptr == '/') {		/* pattern has absolute path */
+	pptr++;
+	pathbuf[0] = '/';
+	pathbuf[pathpos = 1] = '\0';
+    } else			/* pattern is relative to pwd */
+	pathbuf[pathpos = 0] = '\0';
+
     return parsecomplist();
 }
 
@@ -897,7 +1010,7 @@ glob(LinkList list, LinkNode np)
 	    if (*s == Bar || *s == Outpar ||
 		(isset(EXTENDEDGLOB) && *s == Tilde))
 		break;
-	if (*s == Inpar) {
+	if (*s == Inpar && (!isset(EXTENDEDGLOB) || s[1] != Pound)) {
 	    /* Real qualifiers found. */
 	    int sense = 0;	/* bit 0 for match (0)/don't match (1)   */
 				/* bit 1 for follow links (2), don't (0) */
@@ -1234,15 +1347,6 @@ glob(LinkList list, LinkNode np)
 	    }
 	}
     }
-    if (!pathbuf)
-	pathbuf = zalloc(pathbufsz = PATH_MAX);
-    DPUTS(pathbufcwd, "BUG: glob changed directory");
-    if (*str == '/') {		/* pattern has absolute path */
-	str++;
-	pathbuf[0] = '/';
-	pathbuf[pathpos = 1] = '\0';
-    } else			/* pattern is relative to pwd */
-	pathbuf[pathpos = 0] = '\0';
     q = parsepat(str);
     if (!q || errflag) {	/* if parsing failed */
 	if (unset(BADPATTERN)) {
@@ -1267,7 +1371,7 @@ glob(LinkList list, LinkNode np)
     /* Deal with failures to match depending on options */
     if (matchct)
 	badcshglob |= 2;	/* at least one cmd. line expansion O.K. */
-    else if (!gf_nullglob)
+    else if (!gf_nullglob) {
 	if (isset(CSHNULLGLOB)) {
 	    badcshglob |= 1;	/* at least one cmd. line expansion failed */
 	} else if (isset(NOMATCH)) {
@@ -1279,6 +1383,7 @@ glob(LinkList list, LinkNode np)
 	    untokenize(*matchptr++ = dupstring(ostr));
 	    matchct = 1;
 	}
+    }
     /* Sort arguments in to lexical (and possibly numeric) order. *
      * This is reversed to facilitate insertion into the list.    */
     qsort((void *) & matchbuf[0], matchct, sizeof(char *),
@@ -1373,11 +1478,12 @@ hasbraces(char *str)
 		    *str++ = '{', *str = '}';
 		else
 		    bc++;
-	    } else if (*str == Outbrace)
+	    } else if (*str == Outbrace) {
 		if (!bc)
 		    *str = '}';
 		else if (!--bc)
 		    return 1;
+	    }
 	return 0;
     }
     /* Otherwise we need to look for... */
@@ -1554,11 +1660,12 @@ xpandbraces(LinkList list, LinkNode *np)
 	else if (*str2 == Outbrace) {
 	    if (--bc == 0)
 		break;
-	} else if (bc == 1)
+	} else if (bc == 1) {
 	    if (*str2 == Comma)
 		++comma;	/* we have {foo,bar} */
 	    else if (*str2 == '.' && str2[1] == '.')
 		dotdot++;	/* we have {num1..num2} */
+	}
     DPUTS(bc, "BUG: unmatched brace in xpandbraces()");
     if (!comma && dotdot) {
 	/* Expand range like 0..10 numerically: comma or recursive
@@ -2489,7 +2596,10 @@ matchonce(Comp c)
 	    }
 	    continue;
 	}
-	if (*pptr == *pat) {
+	if (*pptr == *pat ||
+	    (((c->stat & C_IGNCASE) ? (tulower(*pat) == tulower(*pptr)) :
+	      (c->stat & C_LCMATCHUC) ?
+	      (islower(*pat) && tuupper(*pat) == *pptr) : 0))) {
 	    /* just plain old characters */
 	    pptr++;
 	    pat++;
@@ -2508,6 +2618,7 @@ parsereg(char *str)
 {
     remnulargs(str);
     mode = 1;			/* no path components */
+    addflags = 0;
     pptr = str;
     tail = NULL;
     return parsecompsw(GF_TOPLEV);
diff --git a/Src/jobs.c b/Src/jobs.c
index 331902d9f..74fa8cda0 100644
--- a/Src/jobs.c
+++ b/Src/jobs.c
@@ -214,7 +214,7 @@ update_job(Job jn)
     if ((isset(NOTIFY) || job == thisjob) && (jn->stat & STAT_LOCKED)) {
 	printjob(jn, !!isset(LONGLISTJOBS), 0);
 	if (zleactive)
-	    refresh();
+	    zrefresh();
     }
     if (sigtrapped[SIGCHLD] && job != thisjob)
 	dotrap(SIGCHLD);
diff --git a/Src/mkmakemod.sh b/Src/mkmakemod.sh
index b088929fa..9c90b75e5 100644
--- a/Src/mkmakemod.sh
+++ b/Src/mkmakemod.sh
@@ -145,6 +145,11 @@ if $first_stage; then
     echo "PROTOS  =$all_proto"
     echo "SUBDIRS =$all_subdirs"
     echo
+    echo "ENTRYOBJ = \$(dir_src)/modentry..o"
+    echo "NNTRYOBJ ="
+    echo "ENTRYOPT = -emodentry"
+    echo "NNTRYOPT ="
+    echo
 
     echo "##### ===== INCLUDING Makemod.in.in ===== #####"
     echo
@@ -161,7 +166,7 @@ if $first_stage; then
     remote_mdhs=
     for module in $here_modules; do
 
-	unset moddeps nozshdep alwayslink
+	unset moddeps nozshdep alwayslink hasexport
 	unset autobins
 	unset objects proto headers hdrdeps otherincs
 	. $top_srcdir/$the_subdir/${module}.mdd
@@ -172,8 +177,10 @@ if $first_stage; then
 
 	dobjects=`echo $objects '' | sed 's,\.o ,..o ,g'`
 	modhdeps=
+	imports=
 	for dep in $moddeps; do
 	    eval "loc=\$loc_$dep"
+	    imports="$imports \$(IMPOPT)\$(sdir_top)/$loc/$dep.export"
 	    case $the_subdir in
 		$loc)
 		    mdh="${dep}.mdh"
@@ -199,9 +206,11 @@ if $first_stage; then
 	echo "##### ===== DEPENDENCIES GENERATED FROM ${module}.mdd ===== #####"
 	echo
 	echo "MODOBJS_${module} = $objects"
-	echo "MODDOBJS_${module} = $dobjects"
+	echo "MODDOBJS_${module} = $dobjects \$(@E@NTRYOBJ)"
 	echo "PROTO_${module} = $proto"
 	echo "INCS_${module} = \$(PROTO_${module}) $otherincs"
+	echo "EXPIMP_${module} = $imports ${hasexport+\$(EXPOPT)\$(sdir)/$module.export}"
+	echo "NXPIMP_${module} ="
 	echo
 	echo "proto.${module}: \$(PROTO_${module})"
 	echo "\$(PROTO_${module}): \$(PROTODEPS)"
@@ -212,7 +221,7 @@ if $first_stage; then
 	if test -z "$alwayslink"; then
 	    echo "${module}.\$(DL_EXT): \$(MODDOBJS_${module})"
 	    echo '	rm -f $@'
-	    echo "	\$(DLLINK) \$(MODDOBJS_${module}) \$(LIBS)"
+	    echo "	\$(DLLINK) \$(@E@XPIMP_$module) \$(@E@NTRYOPT) \$(MODDOBJS_${module}) \$(LIBS)"
 	    echo
 	fi
 	echo "${module}.mdhi: ${module}.mdhs \$(INCS_${module})"
diff --git a/Src/modentry.c b/Src/modentry.c
new file mode 100644
index 000000000..63c4b825d
--- /dev/null
+++ b/Src/modentry.c
@@ -0,0 +1,15 @@
+#include "zsh.mdh"
+
+int boot_ _((Module));
+int cleanup_ _((Module));
+int modentry _((int boot, Module m));
+
+/**/
+int
+modentry(int boot, Module m)
+{
+    if (boot)
+	return boot_(m);
+    else
+	return cleanup_(m);
+}
diff --git a/Src/module.c b/Src/module.c
index ce5989f07..91687a21d 100644
--- a/Src/module.c
+++ b/Src/module.c
@@ -161,6 +161,44 @@ deletebuiltins(char const *nam, Builtin binl, int size)
     return hadf ? hads : 1;
 }
 
+#ifdef AIXDYNAMIC
+
+#include <sys/ldr.h>
+
+static char *dlerrstr[256];
+
+/**/
+static void *
+load_and_bind(const char *fn)
+{
+    void *ret = (void *) load((char *) fn, L_NOAUTODEFER, NULL);
+
+    if (ret) {
+	LinkNode node;
+	int err = loadbind(0, (void *) addbuiltin, ret);
+	for (node = firstnode(modules); !err && node; incnode(node)) {
+	    Module m = (Module) getdata(node);
+	    if (m->handle)
+		err |= loadbind(0, m->handle, ret);
+	}
+
+	if (err) {
+	    loadquery(L_GETMESSAGES, dlerrstr, sizeof(dlerrstr));
+	    unload(ret);
+	    ret = NULL;
+	}
+    } else
+	loadquery(L_GETMESSAGES, dlerrstr, sizeof(dlerrstr));
+
+    return ret;
+}
+
+#define dlopen(X,Y) load_and_bind(X)
+#define dlclose(X)  unload(X)
+#define dlerror()   (dlerrstr[0])
+
+#else
+
 #ifdef HAVE_DLFCN_H
 # include <dlfcn.h>
 #else
@@ -168,12 +206,6 @@ deletebuiltins(char const *nam, Builtin binl, int size)
 # include <nlist.h>
 # include <link.h>
 #endif
-#ifndef RTLD_LAZY
-# define RTLD_LAZY 1
-#endif
-#ifndef RTLD_GLOBAL
-# define RTLD_GLOBAL 0
-#endif
 #ifndef HAVE_DLCLOSE
 # define dlclose(X) ((X), 0)
 #endif
@@ -189,6 +221,16 @@ deletebuiltins(char const *nam, Builtin binl, int size)
 # define STR_CLEANUP   "cleanup_"
 # define STR_CLEANUP_S "cleanup_%s"
 #endif /* !DLSYM_NEEDS_UNDERSCORE */
+
+#endif /* !AIXDYNAMIC */
+
+#ifndef RTLD_LAZY
+# define RTLD_LAZY 1
+#endif
+#ifndef RTLD_GLOBAL
+# define RTLD_GLOBAL 0
+#endif
+
 typedef int (*Module_func) _((Module));
 
 /**/
@@ -257,6 +299,24 @@ find_module(const char *name)
     return NULL;
 }
 
+#ifdef AIXDYNAMIC
+
+/**/
+static int
+init_module(Module m)
+{
+    return ((int (*)_((int,Module))) m->handle)(1, m);
+}
+
+/**/
+static int
+cleanup_module(Module m)
+{
+    return ((int (*)_((int,Module))) m->handle)(0, m);
+}
+
+#else
+
 /**/
 static int
 init_module(Module m)
@@ -289,6 +349,39 @@ init_module(Module m)
 }
 
 /**/
+static int
+cleanup_module(Module m)
+{
+    char *s, *t;
+#ifndef DYNAMIC_NAME_CLASH_OK
+    char buf[PATH_MAX + 1];
+#endif
+    Module_func fn;
+
+    s = strrchr(m->nam, '/');
+    if (s)
+	s = dupstring(++s);
+    else
+	s = m->nam;
+    if ((t = strrchr(s, '.')))
+	*t = '\0';
+#ifdef DYNAMIC_NAME_CLASH_OK
+    fn = (Module_func) dlsym(m->handle, STR_CLEANUP);
+#else /* !DYNAMIC_NAME_CLASH_OK */
+    if (strlen(s) + 9 > PATH_MAX)
+	return 1;
+    sprintf(buf, STR_CLEANUP_S, s);
+    fn = (Module_func) dlsym(m->handle, buf);
+#endif /* !DYNAMIC_NAME_CLASH_OK */
+    if(fn)
+	return fn(m);
+    zwarnnam(m->nam, "no cleanup function", NULL, 0);
+    return 1;
+}
+
+#endif /* !AIXDYNAMIC */
+
+/**/
 Module
 load_module(char const *name)
 {
@@ -338,37 +431,6 @@ load_module(char const *name)
 }
 
 /**/
-static int
-cleanup_module(Module m)
-{
-    char *s, *t;
-#ifndef DYNAMIC_NAME_CLASH_OK
-    char buf[PATH_MAX + 1];
-#endif
-    Module_func fn;
-
-    s = strrchr(m->nam, '/');
-    if (s)
-	s = dupstring(++s);
-    else
-	s = m->nam;
-    if ((t = strrchr(s, '.')))
-	*t = '\0';
-#ifdef DYNAMIC_NAME_CLASH_OK
-    fn = (Module_func) dlsym(m->handle, STR_CLEANUP);
-#else /* !DYNAMIC_NAME_CLASH_OK */
-    if (strlen(s) + 9 > PATH_MAX)
-	return 1;
-    sprintf(buf, STR_CLEANUP_S, s);
-    fn = (Module_func) dlsym(m->handle, buf);
-#endif /* !DYNAMIC_NAME_CLASH_OK */
-    if(fn)
-	return fn(m);
-    zwarnnam(m->nam, "no cleanup function", NULL, 0);
-    return 1;
-}
-
-/**/
 void
 add_dep(char *name, char *from)
 {
diff --git a/Src/system.h b/Src/system.h
index e42f3b891..292943dd9 100644
--- a/Src/system.h
+++ b/Src/system.h
@@ -593,6 +593,12 @@ extern char PC, *BC, *UP;
 extern short ospeed;
 #endif
 
+/* Rename some global zsh variables to avoid *
+ * possible name clashes with libc           */
+
+#define cs zshcs
+#define ll zshll
+
 #ifndef O_NOCTTY
 # define O_NOCTTY 0
 #endif
diff --git a/Src/utils.c b/Src/utils.c
index 3619fa95d..87f82f7df 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -510,6 +510,10 @@ adduserdir(char *s, char *t, int flags, int always)
     if ((flags & ND_USERNAME) && nameddirtab->getnode2(nameddirtab, s))
 	return;
 
+    /* Never hash PWD, because it's never useful */
+    if (!strcmp(s, "PWD"))
+	return;
+
     /* Normal parameter assignments generate calls to this function, *
      * with always==0.  Unless the AUTO_NAME_DIRS option is set, we  *
      * don't let such assignments actually create directory names.   *
@@ -854,7 +858,7 @@ adjustwinsize(void)
     setiparam("LINES", shttyinfo.winsize.ws_row);
     if (zleactive && (oldcols != columns || oldrows != lines)) {
 	resetneeded = winchanged = 1;
-	refresh();
+	zrefresh();
     }
 #endif   /* TIOCGWINSZ */
 }
@@ -1129,13 +1133,13 @@ checkrmall(char *s)
     if(isset(RMSTARWAIT)) {
 	fputs("? (waiting ten seconds)", shout);
 	fflush(shout);
-	beep();
+	zbeep();
 	sleep(10);
 	fputc('\n', shout);
     }
     fputs(" [yn]? ", shout);
     fflush(shout);
-    beep();
+    zbeep();
     return (getquery("ny", 1) == 'y');
 }
 
@@ -1181,7 +1185,7 @@ getquery(char *valid_chars, int purge)
 	    write(SHTTY, "\n", 1);
 	    break;
 	}
-	beep();
+	zbeep();
 	if (icntrl(c))
 	    write(SHTTY, "\b \b", 3);
 	write(SHTTY, "\b \b", 3);
@@ -1327,7 +1331,7 @@ spckword(char **s, int hist, int cmd, int ask)
 	    zputs(pptbuf, shout);
 	    free(pptbuf);
 	    fflush(shout);
-	    beep();
+	    zbeep();
 	    x = getquery("nyae ", 0);
 	} else
 	    x = 'y';
@@ -2286,7 +2290,7 @@ mkarray(char *s)
 
 /**/
 void
-beep(void)
+zbeep(void)
 {
     if (isset(BEEP))
 	write(SHTTY, "\07", 1);
diff --git a/Src/zsh.export b/Src/zsh.export
new file mode 100644
index 000000000..8f676c7fd
--- /dev/null
+++ b/Src/zsh.export
@@ -0,0 +1,235 @@
+#!
+SHTTY
+addbuiltins
+addedx
+addhashnode
+aliastab
+alloc_stackp
+appstr
+arrdup
+arrlen
+attachtty
+bangchar
+bin_notavail
+breaks
+bufstack
+builtintab
+chline
+chuck
+clearjobtab
+closem
+cmdnamtab
+columns
+compctlreadptr
+coprocin
+coprocout
+countlinknodes
+countprompt
+createparam
+ctxtlex
+curhist
+current_limits
+deletebuiltins
+deletehashtable
+domatch
+doshfunc
+dputs
+dquotedztrdup
+dummy_list
+dupstring
+dupstrpfx
+dyncat
+emptyhashtable
+endparamscope
+errflag
+excs
+execstring
+exlast
+expanding
+fallback_compctlread
+fallback_zleread
+fignore
+file_type
+filesub
+filesubstr
+findcmd
+firsthist
+freearray
+freeheap
+getaparam
+gethashnode
+gethashnode2
+getiparam
+getkeystring
+getlinknode
+getshfunc
+getsparam
+glob_pre
+glob_suf
+global_heapalloc
+global_permalloc
+globlist
+gotwordptr
+halloc
+hasam
+hashcmd
+hasher
+hasspecial
+haswilds
+hcalloc
+hgetc
+hgetline
+histentarr
+histentct
+hptr
+hrealloc
+inbufct
+incmdpos
+incond
+init_io
+init_shout
+init_term
+inpop
+inpush
+inredir
+insertlinknode
+intr
+inwhat
+isfirstln
+jobtab
+lastpid
+lastval
+lchdir
+lexrestore
+lexsave
+lexstop
+limits
+line
+lines
+locallevel
+metadiffer
+metafy
+metalen
+mode_to_octal
+mypgrp
+mypid
+nameddirtab
+ncalloc
+newhashtable
+newlinklist
+nicechar
+nicezputs
+niceztrdup
+niceztrlen
+noaliases
+noerrs
+noop_function
+noop_function_int
+optiontab
+opts
+paramtab
+parbegin
+parend
+parsereg
+parsestr
+path
+pathchecked
+popheap
+postedit
+ppid
+prefork
+prepromptfns
+printif
+printqt
+promptexpand
+pushheap
+putshout
+pwd
+quietgetevent
+quietgethist
+quotedzputs
+refreshptr
+remlpaths
+remnulargs
+removehashnode
+resetneeded
+restoredir
+reswdtab
+retflag
+scanhashtable
+setaparam
+setlimits
+setsparam
+settyinfo
+shfunctab
+shingetline
+shout
+shttyinfo
+singsub
+skipparens
+spaceinlineptr
+spacesplit
+spckword
+startparamscope
+stdunsetfn
+stophist
+stopmsg
+strinbeg
+strinend
+strpfx
+strsfx
+strucpy
+struncpy
+tclen
+tcstr
+termflags
+tgoto
+tok
+tokenize
+tokstr
+tputs
+trashzleptr
+tricat
+tsetcap
+ttystrname
+tulower
+tuupper
+txtchange
+typtab
+ugetnode
+uinsertlinknode
+unmeta
+unmetafy
+untokenize
+uremnode
+useheap
+winchanged
+wordbeg
+zalloc
+zbeep
+zcalloc
+zchdir
+zerr
+zerrnam
+zexit
+zfree
+zgetdir
+zgetenv
+zjoin
+zleactive
+zleparse
+zlereadptr
+zputs
+zreaddir
+zsetlimit
+zsfree
+zshcs
+zshll
+zstrtol
+ztokens
+ztrdup
+ztrduppfx
+ztrftime
+ztrlen
+ztrsub
+zwarnnam
diff --git a/Src/zsh.h b/Src/zsh.h
index e96fc6e86..d833278c9 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -31,7 +31,7 @@
 #define zleread(X,Y,H)  zlereadptr(X,Y,H)
 #define spaceinline(X)  spaceinlineptr(X)
 #define gotword()       gotwordptr()
-#define refresh()       refreshptr()
+#define zrefresh()      refreshptr()
 
 #define compctlread(N,A,O,R) compctlreadptr(N,A,O,R)
 
diff --git a/Src/zsh.mdd b/Src/zsh.mdd
index 244029d65..f4a1e4015 100644
--- a/Src/zsh.mdd
+++ b/Src/zsh.mdd
@@ -12,7 +12,7 @@ headers="../config.h system.h zsh.h sigcount.h signals.h \
 prototypes.h hashtable.h ztype.h"
 
 :<<\Make
-signames.c: signames.awk @SIGNAL_H@
+signames.c: signames.awk ../config.h @SIGNAL_H@
 	$(AWK) -f $(sdir)/signames.awk @SIGNAL_H@ > $@
 
 sigcount.h: signames.c
diff --git a/acconfig.h b/acconfig.h
index c74d33966..417e651a2 100644
--- a/acconfig.h
+++ b/acconfig.h
@@ -219,3 +219,6 @@
 
 /* Define to 1 if you want to use dynamically loaded modules */
 #undef DYNAMIC
+
+/* Define to 1 if you want to use dynamically loaded modules on AIX */
+#undef AIXDYNAMIC
diff --git a/aczsh.m4 b/aczsh.m4
index 44dd81f27..8d86a4817 100644
--- a/aczsh.m4
+++ b/aczsh.m4
@@ -207,6 +207,7 @@ main()
 [zsh_cv_sys_dynamic_rtld_global=no],
 [zsh_cv_sys_dynamic_rtld_global=no]
 )
+    LDFLAGS=$save_ldflags
 else
     zsh_cv_sys_dynamic_rtld_global=no
 fi
diff --git a/configure.in b/configure.in
index aa3170732..822a7f477 100644
--- a/configure.in
+++ b/configure.in
@@ -210,11 +210,11 @@ if test -n "$auto_cflags"; then
   fi
 fi
 if test -n "$auto_ldflags"; then
-  if test "${enable_zsh_debug}" = yes; then
-    LDFLAGS=-g
-  else
-    LDFLAGS=-s
-  fi
+  case "${enable_zsh_debug}$host_os" in
+    yesaix*) ;;  # AIX ld does not accept -g
+    yes*)    LDFLAGS=-g ;;
+    *)       LDFLAGS=-s ;;
+  esac
 fi
 
 dnl ----------
@@ -401,12 +401,10 @@ AC_CHECK_LIB(c, printf)
 
 dnl Prefer BSD termcap library to SysV curses library, except on certain
 dnl versions of AIX and HP-UX.
-if test `echo $host_os | sed 's/^.*\(aix\)[[1-9]]\.[[0-9]].*$/\1/'` = aix ||
-    test `echo $host_os | sed 's/^.*\(hpux\)10\..*$/\1/'` = hpux; then
-  termcap_curses_order="curses ncurses termcap"
-else
-  termcap_curses_order="termcap curses ncurses"
-fi
+case "$host_os" in
+  aix*|hpux10.*) termcap_curses_order="curses ncurses termcap" ;;
+  *)             termcap_curses_order="termcap curses ncurses" ;;
+esac
 
 for lib in $termcap_curses_order; do
   AC_CHECK_LIB(${lib}, tgetent, [LIBS="-l$lib $LIBS"; break])
@@ -617,8 +615,9 @@ AC_CHECK_FUNCS(memcpy memmove \
               sigprocmask setuid seteuid setreuid setresuid setsid strerror \
               nis_list initgroups fchdir cap_get_proc readlink nice \
 	      getgrgid getgrnam getpwent getpwnam getpwuid)
+
 if test $dynamic = yes; then
-  AC_CHECK_FUNCS(dlopen dlerror dlsym dlclose)
+  AC_CHECK_FUNCS(dlopen dlerror dlsym dlclose load loadquery loadbind unload)
 fi
 
 
@@ -900,14 +899,37 @@ dnl ---------------
 dnl dynamic loading
 dnl ---------------
 L=N
-if test "$ac_cv_func_dlopen" != yes; then
-  dynamic=no
-elif test "$ac_cv_func_dlsym" != yes; then
-  dynamic=no
-elif test "$ac_cv_func_dlerror" != yes; then
-  dynamic=no
+aixdynamic=no
+if test "$ac_cv_func_dlopen"  != yes ||
+   test "$ac_cv_func_dlsym"   != yes ||
+   test "$ac_cv_func_dlerror" != yes; then
+  if test "$ac_cv_func_load"      != yes ||
+     test "$ac_cv_func_unload"    != yes ||
+     test "$ac_cv_func_loadbind"  != yes ||
+     test "$ac_cv_func_loadquery" != yes; then
+    dynamic=no
+  elif test "x$dynamic" = xyes; then
+    aixdynamic=yes
+  fi
 fi
-if test "x$dynamic" = xyes; then
+
+test -n "$GCC" && LDARG=-Wl,
+
+if test "x$aixdynamic" = xyes; then
+  DL_EXT="${DL_EXT=so}"
+  DLLD="${DLLD=$CC}"
+  zsh_cv_func_dlsym_needs_underscore=no
+  DLLDFLAGS=${DLLDFLAGS=}
+  EXTRA_LDFLAGS=${EXTRA_LDFLAGS=}
+  EXPOPT=${LDARG}-bE:
+  IMPOPT=${LDARG}-bI:
+  zsh_cv_sys_dynamic_clash_ok="${zsh_cv_sys_dynamic_clash_ok=yes}"
+  zsh_cv_sys_dynamic_rtld_global="${zsh_cv_sys_dynamic_rtld_global=yes}"
+  zsh_cv_sys_dynamic_execsyms="${zsh_cv_sys_dynamic_execsyms=yes}"
+  zsh_cv_sys_dynamic_strip_exe="${zsh_cv_sys_dynamic_strip_exe=yes}"
+  zsh_cv_sys_dynamic_strip_lib="${zsh_cv_sys_dynamic_strip_lib=yes}"
+  zsh_cv_sys_dynamic_broken="${zsh_cv_sys_dynamic_broken=no}"
+elif test "x$dynamic" = xyes; then
   AC_CACHE_CHECK(if your system use ELF binaries,
    zsh_cv_sys_elf,
    [AC_TRY_RUN([/* Test for whether ELF binaries are produced */
@@ -947,10 +969,11 @@ char *argv[];
   case "$host_os" in
     hpux*)        DLLDFLAGS="${DLLDFLAGS=-b}" ;;
     linux*|irix*|osf*) DLLDFLAGS="${DLLDFLAGS=-shared}" ;;
-    solaris*)     DLLDFLAGS="${DLLDFLAGS=-G}" ;;
     sunos*)       DLLDFLAGS="${DLLDFLAGS=-assert nodefinitions}" ;;
     sysv4*|esix*) DLLDFLAGS="${DLLDFLAGS=-G $ldflags}" ;;
     netbsd*)      DLLDFLAGS="${DLLDFLAGS=-x -shared --whole-archive}" ;;
+    aix*)         DLLDFLAGS="${DLLDFLAGS=-G -bexpall -lc}" ;;
+    solaris*|sysv4*|esix*) DLLDFLAGS="${DLLDFLAGS=-G}" ;;
   esac
   case "$host_os" in
     hpux*)  EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-Wl,-E}" ;;
@@ -958,7 +981,7 @@ char *argv[];
   esac
   AC_CACHE_CHECK(if your dlsym() needs a leading underscore,
    zsh_cv_func_dlsym_needs_underscore,
-   [cat >conftest.c <<EOM
+   [echo failed >conftestval && cat >conftest.c <<EOM
 fred () { }
 EOM
     $CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest.c 1>&5 2>&5 &&
@@ -1006,8 +1029,12 @@ main()
     zsh_cv_func_dlsym_needs_underscore=failed
     dynamic=no,
     zsh_cv_func_dlsym_needs_underscore=no)])
-  if test "$zsh_cv_func_dlsym_needs_underscore" = yes; then
+  if test "x$zsh_cv_func_dlsym_needs_underscore" = xyes; then
     AC_DEFINE(DLSYM_NEEDS_UNDERSCORE)
+  elif test "x$zsh_cv_func_dlsym_needs_underscore" != xno; then
+    dnl Do not cache failed value
+    unset zsh_cv_func_dlsym_needs_underscore
+    dynamic=no
   fi
 fi
 
@@ -1045,13 +1072,23 @@ else
   D=N
 fi
 
+if test "x$aixdynamic" = xyes; then
+  E=E
+  AC_DEFINE(AIXDYNAMIC)dnl
+else
+  E=N
+fi
+
 AC_DEFINE_UNQUOTED(DL_EXT, "$DL_EXT")dnl
 AC_SUBST(D)dnl
 AC_SUBST(DL_EXT)dnl
 AC_SUBST(DLLD)dnl
 AC_SUBST(DLCFLAGS)dnl
 AC_SUBST(DLLDFLAGS)dnl
+AC_SUBST(E)dnl
 AC_SUBST(EXTRA_LDFLAGS)dnl
+AC_SUBST(EXPOPT)dnl
+AC_SUBST(IMPOPT)dnl
 AC_SUBST(L)dnl
 AC_SUBST(RTLD_GLOBAL_OK)dnl