about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-11-01 09:37:12 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-11-01 09:37:12 +0000
commit00464365490f4c6f77ba5e16d7992cb6bbefc693 (patch)
tree6baa4a083ce2197c5f66451e4c98941f4dfaee8c
parent8f35520cb3ef271407d73404a59d51a6b815e8f7 (diff)
downloadzsh-00464365490f4c6f77ba5e16d7992cb6bbefc693.tar.gz
zsh-00464365490f4c6f77ba5e16d7992cb6bbefc693.tar.xz
zsh-00464365490f4c6f77ba5e16d7992cb6bbefc693.zip
manual/8478
-rw-r--r--Doc/Zsh/compctl.yo201
-rw-r--r--Doc/Zsh/compsys.yo32
-rw-r--r--Doc/Zsh/compwid.yo207
-rw-r--r--Doc/Zsh/intro.yo2
-rw-r--r--Doc/Zsh/manual.yo2
-rw-r--r--Doc/Zsh/mod_complete.yo2
-rw-r--r--Doc/Zsh/modules.yo5
-rw-r--r--Doc/Zsh/zle.yo2
-rw-r--r--Doc/zsh.yo4
-rw-r--r--Src/Zle/comp.h59
-rw-r--r--Src/Zle/compcore.c2530
-rw-r--r--Src/Zle/complete.c131
-rw-r--r--Src/Zle/complete.mdd4
-rw-r--r--Src/Zle/complist.c8
-rw-r--r--Src/Zle/compmatch.c1906
-rw-r--r--Src/Zle/compresult.c1940
-rw-r--r--Src/Zle/zle.h54
-rw-r--r--Src/Zle/zle.mdd2
-rw-r--r--Src/Zle/zle_main.c108
-rw-r--r--Src/Zle/zle_misc.c51
-rw-r--r--Src/Zle/zle_thingy.c2
-rw-r--r--Src/Zle/zle_tricky.c6373
22 files changed, 7091 insertions, 6534 deletions
diff --git a/Doc/Zsh/compctl.yo b/Doc/Zsh/compctl.yo
index b55aa91be..b65666d5d 100644
--- a/Doc/Zsh/compctl.yo
+++ b/Doc/Zsh/compctl.yo
@@ -1,4 +1,4 @@
-texinode(Programmable Completion Using compctl)(Completion Widgets)(Zsh Line Editor)(Top)
+texinode(Programmable Completion Using compctl)(Zsh Modules)(Completion Widgets)(Top)
 chapter(Programmable Completion Using compctl)
 cindex(completion, programmable)
 cindex(completion, controlling)
@@ -47,7 +47,6 @@ menu(Command Flags)
 menu(Option Flags)
 menu(Alternative Completion)
 menu(Extended Completion)
-menu(Matching Control)
 menu(Example)
 endmenu()
 
@@ -132,8 +131,10 @@ 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, as described below in noderef(Matching
-Control).
+matching specifications, as described in 
+ifzman(zshcompwid)\
+ifnzman(noderef(Matching Control))\
+.
 
 texinode(Option Flags)(Alternative Completion)(Command Flags)(Programmable Completion Using compctl)
 sect(Option Flags)
@@ -523,7 +524,10 @@ different name spaces.
 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).
+of the var(match-spec) string is described in 
+ifzman(zshcompwid)\
+ifnzman(noderef(Matching Control))\
+.
 )
 enditem()
 
@@ -542,7 +546,7 @@ up to that point, default completion is tried.
 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 Using compctl)
+texinode(Extended Completion)(Example)(Alternative Completion)(Programmable Completion Using compctl)
 sect(Extended Completion)
 startlist()
 list(nofill(tt(compctl) [ tt(-CDT) ] var(options) \
@@ -653,190 +657,7 @@ completion is done in backticks and var(str) starts with a `b'.
 )
 enditem()
 
-texinode(Matching Control)(Example)(Extended Completion)(Programmable Completion Using compctl)
-sect(Matching Control)
-
-It is possible by use of the tt(-M) var(spec) flag to specify how the
-characters in the string to be completed (referred to here as the
-command line) map onto the characters in the list of matches produced by
-the completion code (referred to here as the trial completions). Note
-that this is not used if the command line contains a glob pattern and
-the tt(GLOB_COMPLETE) option is set.
-
-The var(spec) consists of one or more matching descriptions separated by
-whitespace. Each description consists of a letter followed by a colon,
-then the patterns describing which character sequences on the line match
-which character sequences in the trial completion.  Any sequence of characters not
-handled in this fashion must match exactly, as usual.
-
-The forms of var(spec) understood are as follows. In each case, the
-form with an uppercase initial character retains the string already
-typed on the command line as the final result of completion, while with
-a lowercase initial character the string on the command line is changed
-into the corresponding part of the trial completion.
-
-startitem()
-xitem(tt(m:)var(lpat)tt(=)var(tpat))
-item(tt(M:)var(lpat)tt(=)var(tpat))(
-Here, var(lpat) is a pattern that matches on the command line,
-corresponding to var(tpat) which matches in the trial completion.
-)
-xitem(tt(l:)var(anchor)tt(|)var(lpat)tt(=)var(tpat))
-item(tt(L:)var(anchor)tt(|)var(lpat)tt(=)var(tpat))(
-These letters are for patterns that are anchored by another pattern on
-the left side. Matching for var(lpat) and var(tpat) is as for tt(m) and
-tt(M), but the pattern var(lpat) matched on the command line must be
-preceeded by the pattern var(anchor).  The var(anchor) can be blank to
-anchor the match to the start of the command line string; otherwise the
-anchor can occur anywhere, but must match in both the command line and
-trial completion strings.
-)
-xitem(tt(r:)var(lpat)tt(|)var(anchor)tt(=)var(tpat))
-item(tt(R:)var(lpat)tt(|)var(anchor)tt(=)var(tpat))(
-As tt(l) and tt(L) with the difference that the command line and trial
-completion patterns are anchored on the right side.  Here an empty
-var(anchor) forces the match to the end of the command line string.
-)
-enditem()
-
-Each var(lpat), var(tpat) or var(anchor) is either an empty string or
-consists of a sequence of literal characters (which may be quoted with a
-backslash), question marks, character classes, and correspondence
-classes; ordinary shell patterns are not used.  Literal characters match
-only themselves, question marks match any character, and character
-classes are formed as for globbing and match any character in the given
-set.
-
-Correspondence classes are defined like character classes, but with two
-differences: they are delimited by a pair of braces, and negated classes
-are not allowed, so the characters tt(!) and tt(^) have no special
-meaning directly after the opening brace.  They indicate that a range of
-characters on the line match a range of characters in the trial
-completion, but (unlike ordinary character classes) paired according to
-the corresponding position in the sequence. For example, to make any
-lowercase letter on the line match the corresponding uppercase letter in
-the trial completion, you can use `tt(m:{a-z}={A-Z})'.  More than one
-pair of classes can occur, in which case the first class before the
-tt(=) corresponds to the first after it, 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
-also behave like normal character classes.
-
-The pattern var(tpat) may also be a single star, `tt(*)'. This means
-that the pattern on the command line can match any number of characters
-in the trial completion. In this case the pattern must be anchored (on
-either side); the var(anchor) then determines how much of the trial
-completion is to be included --- only the characters up to the next
-appearance of the anchor will be matched.
-
-Examples:
-
-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 builtins tt(setopt) and tt(unsetopt) understand option names
-with uppercase letters, underscores, and the optional tt(no).  The
-following alters the matching rules so that the prefix tt(no) and any
-underscore are ignored when trying to match the trial completions
-generated and uppercase letters on the line match the corresponding
-lowercase letters in the words:
-
-example(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
-(the empty anchor before the pipe symbol) of the string on the
-line matches the empty string in the list of words generated by
-completion, so it will be ignored if present. The second part does the
-same for an underscore anywhere in the command line string, and the
-third part uses correspondence classes so 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 what has
-already been typed on the command line (in particular the prefix
-tt(no)) will not be deleted.
-
-The second example makes completion case insensitive.  By using
-tt(compctl) with the tt(-M) option alone this applies to every
-completion.  This is just the same as in the tt(setopt) example, except
-here we wish to retain the characters in the list of completions:
-
-example(compctl -M 'm:{a-z}={A-Z}')
-
-This makes lowercase letters match their uppercase counterparts.
-To make uppercase letters match the lowercase forms as well:
-
-example(compctl -M 'm:{a-zA-Z}={A-Za-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. the word on the
-command line consists of multiple parts, separated by a dot in this
-example, where each part should be completed separately --- note,
-however, that the case where each part of the word, i.e. tt(comp),
-tt(source) and tt(unix) in this example, is to be completed separately
-is a different problem to be solved by extended completion.  The
-example can be handled by:
-
-example(compctl -M 'r:|.=* r:|=*' \ 
-  -k '(comp.sources.unix comp.sources.misc ...)' ngroups)
-
-The first specification says that tt(lpat) is the empty string, while
-tt(anchor) is a dot; tt(tpat) is tt(*), so this can match anything
-except for the `tt(.)' from the anchor in
-the trial completion word.  So in tt(c.s.u), the matcher sees tt(c),
-followed by the empty string, followed by the anchor `tt(.)', and
-likewise for the second dot, and replaces the empty strings before the
-anchors, giving tt(c)[tt(omp)]tt(.s)[tt(ources)]tt(.u)[tt(nix)], where
-the last part of the completion is just as normal.
-
-The second specification is needed to make this work when the cursor is
-in the middle of the string on the command line and the option
-tt(COMPLETE_IN_WORD) is set. In this case the completion code would
-normally try to match trial completions that end with the string as
-typed so far, i.e. it will only insert new characters at the cursor
-position rather then at the end.  However in our example we would like
-the code to recognise matches which contain extra characters after the
-string on the line (the tt(nix) in the example).  Hence we say that the
-empty string at the end of the string on the line matches any characters
-at the end of the trial completion.
-
-More generally, the specification
-
-example(compctl -M 'r:|[.,_-]=* r:|=*')
-
-allows one to complete words with abbreviations before any of the
-characters in the square brackets in any completion.  For example, to
-complete tt(veryverylongfile.c) rather than tt(veryverylongheader.h)
-with the above in effect, you can just type tt(very.c) before attempting
-completion.
-
-The form tt(compctl -M) that defines global matching actually accepts
-any number of specification strings, unlike the case where the tt(-M)
-option applies only to a particular command.  In this case, when
-completion is attempted for any command, the code will try the
-specifications in order until one matches.  This allows one to define
-simple and fast matches to be used first, more powerful matchers as a
-second choice, and so on. These global matchers can also be defined by 
-setting the tt(compmatchers) special array to the strings that would
-otherwise be given to tt(compctl -M) as arguments.
-
-For example, one can make the code match trial completions that contain
-the string on the command line as a substring, not just at the
-beginning.  Since this might produce more matches than we want,
-we arrange for it to be tried only if the matchers described above don't
-produce any matches:
-
-example(compctl -M 'r:|[.,_-]=* r:|=*' 'l:|=* r:|=*')
-
-Here, if the string on the command line is tt(foo.bar), tt(compctl)
-first tries matching tt(foo)var(anything)tt(.bar)var(anything), as
-with the previous example.  If that fails, the two descriptions in the
-second string after the tt(-M) say that the blanks at the beginning
-and end of the string on the command line can match any set of
-characters at the beginning or end of the trial completion, so it will
-look for var(anything)tt(foo.bar)var(anything).
-
-texinode(Example)()(Matching Control)(Programmable Completion Using compctl)
+texinode(Example)()(Extended Completion)(Programmable Completion Using compctl)
 sect(Example)
 
 example(compctl -u -x 's[tt(PLUS())] c[-1,-f],s[-f+PLUS()]' \ 
diff --git a/Doc/Zsh/compsys.yo b/Doc/Zsh/compsys.yo
index a6ebeef6b..d741ee9c0 100644
--- a/Doc/Zsh/compsys.yo
+++ b/Doc/Zsh/compsys.yo
@@ -19,7 +19,7 @@ global matching control, such as case-insensitivity (`tt(abc)' will complete
 to a string beginning `tt(ABC)'), or wildcard behaviour on
 certain anchors (`tt(a-d)' will complete to abc-def as if there were a
 `tt(*)' after the `a').  See
-ifzman(the section `Matching Control' in zmanref(zshcompctl))\
+ifzman(the section `Matching Control' in zmanref(zshcompwid))\
 ifnzman(noderef(Matching Control))
 for further details.
 
@@ -383,7 +383,7 @@ for completing the name of a parameter expansion within braces
 )
 item(tt(-first-))(
 for adding completions before any other other completion functions are
-tried (similar to the `tt(-T)' flag of tt(compctl)); if this
+tried; if this
 function sets the tt(_compskip) parameter to `tt(all)', no other
 completion functions will be called, if it is set to a string
 containing `tt(patterns)', no pattern completion functions will be
@@ -392,12 +392,10 @@ function for the `tt(-default-)' context will not be called, but
 functions defined for commands will.
 )
 item(tt(-default-))(
-for generating completions when no special completion function is used 
-(similar to the `tt(-D)' option of tt(compctl)).
+for generating completions when no special completion function is used.
 )
 item(tt(-command-))(
-for completing in a command position (as with the `tt(-C)' option of
-tt(compctl)).
+for completing in a command position.
 )
 enditem()
 
@@ -696,15 +694,21 @@ to the tt(compadd) builtin used to add the matches.
 )
 item(tt(_path_files) and tt(_files))(
 The function tt(_path_files) is used throughout the shell code
-to complete filenames. The advantage over the builtin
-completion functions is that it allows completion of partial paths. For
+to complete filenames. It allows completion of partial paths. For
 example, the string `tt(/u/i/s/sig)' may be completed to
-`tt(/usr/include/sys/signal.h)'.  The options `tt(-/)', `tt(-f)', `tt(-g)',
-and `tt(-W)' are available as for the tt(compctl)
-and builtin command; tt(-f) is the default. Additionally, the `tt(-F)'
+`tt(/usr/include/sys/signal.h)'.  The option `tt(-/)' specifies that
+only directories should be completed. The option `tt(-g) var(pattern)' 
+says that only files matching the var(pattern) should be completed,
+and the `tt(-f)' option, which is the default, completes all
+filenames. The option `tt(-W) var(paths)' may be used to specify path
+prefixes that are to be prepended to the string from the line to
+generate the filenames but that should not be inserted in the line or
+shown in a completion listing. The var(paths) may be the name of an
+array parameter or a literal list of paths enclosed in parentheses.
+Additionally, the `tt(-F)'
 option from the tt(compadd) builtin is supported, giving direct control
-over which filenames should be ignored as done by the tt(fignore)
-parameter in normal completion.
+over which filenames should be ignored. If no such option is given,
+the tt(fignore) parameter is used.
 
 The function tt(_files) calls tt(_path_files) with all the arguments
 it was passed and, if that generated no matches, calls tt(_path_files) again
@@ -1488,7 +1492,7 @@ item(tt(approximate_prompt))(
 This can be set to a string to be displayed on top of the
 corrected strings generated when cycling through them. This string
 may contain the control sequences `tt(%n)', `tt(%B)', etc. known from
-the `tt(-X)' option of tt(compctl). Also, the sequence `tt(%e)' will
+the `tt(-X)' option of tt(compadd). Also, the sequence `tt(%e)' will
 be replaced by the number of errors accepted to generate the corrected 
 strings.
 )
diff --git a/Doc/Zsh/compwid.yo b/Doc/Zsh/compwid.yo
index c22a7bb14..fc2285e40 100644
--- a/Doc/Zsh/compwid.yo
+++ b/Doc/Zsh/compwid.yo
@@ -1,4 +1,4 @@
-texinode(Completion Widgets)(Zsh Modules)(Programmable Completion Using compctl)(Top)
+texinode(Completion Widgets)(Programmable Completion Using compctl)(Zsh Line Editor)(Top)
 chapter(Completion Widgets)
 cindex(completion, widgets)
 cindex(completion, programmable)
@@ -33,6 +33,7 @@ startmenu()
 menu(Special Parameters)
 menu(Builtin Commands)
 menu(Condition Codes)
+menu(Matching Control)
 menu(Examples)
 endmenu()
 
@@ -364,7 +365,7 @@ xitem(tt(compadd) [ tt(-qQfenUaml12) ] [ tt(-F) var(array) ])
 xitem([ tt(-P) var(prefix) ] [ tt(-S) var(suffix) ])
 xitem([ tt(-p) var(hidden-prefix) ] [ tt(-s) var(hidden-suffix) ])
 xitem([ tt(-i) var(ignored-prefix) ] [ tt(-I) var(ignored-suffix) ])
-xitem([ tt(-W) var(file-prefix) ] [ tt(-y) var(array) ] [ tt(-d) var(array) ])
+xitem([ tt(-W) var(file-prefix) ] [ tt(-d) var(array) ])
 xitem([ tt(-J) var(name) ] [ tt(-V) var(name) ] [ tt(-X) var(explanation) ])
 xitem([ tt(-r) var(remove-chars) ] [ tt(-R) var(remove-func) ])
 xitem([ tt(-M) var(match-spec) ] [ tt(-O) var(array) ] [ tt(-A) var(array) ])
@@ -541,7 +542,8 @@ code not to quote any metacharacters in the words when inserting them
 into the command line.
 )
 item(tt(-M) var(match-spec))(
-This gives local match specifications.
+This gives local match specifications as described below in
+noderef(Matching Control).
 Note that they will only be used if the tt(-U) option is not given.
 )
 item(tt(-n))(
@@ -552,10 +554,6 @@ item(tt(-U))(
 If this flag is given, all words given will be accepted and no matching
 will be done by the completion code. Normally this is used in
 functions that do the matching themselves.
-
-Note that with tt(compadd) this option does not automatically turn on
-menu completion if tt(AUTO_LIST) is set, unlike the corresponding option of
-tt(compctl).
 )
 item(tt(-O) var(array))(
 If this option is given, the var(words) are em(not) added to the set of
@@ -693,7 +691,7 @@ Note that this builtin is defined by the tt(compctl) module.
 )
 enditem()
 
-texinode(Condition Codes)(Examples)(Builtin Commands)(Completion Widgets)
+texinode(Condition Codes)(Matching Control)(Builtin Commands)(Completion Widgets)
 sect(Condition Codes)
 cindex(completion widgets, condition codes)
 
@@ -719,26 +717,209 @@ true if the test for the tt(-N) option with both patterns would succeed.
 )
 enditem()
 
-texinode(Examples)()(Condition Codes)(Completion Widgets)
+texinode(Matching Control)(Examples)(Condition Codes)(Completion Widgets)
+sect(Matching Control)
+
+It is possible by use of the tt(compmatchers) special array and the
+tt(-M) option of the tt(compadd) builtin command to specify how the
+characters in the string to be completed (referred to here as the
+command line) map onto the characters in the list of matches produced by
+the completion code (referred to here as the trial completions). Note
+that this is not used if the command line contains a glob pattern and
+the tt(GLOB_COMPLETE) option is set.
+
+The var(spec) consists of one or more matching descriptions separated by
+whitespace. Each description consists of a letter followed by a colon,
+then the patterns describing which character sequences on the line match
+which character sequences in the trial completion.  Any sequence of characters not
+handled in this fashion must match exactly, as usual.
+
+The forms of var(spec) understood are as follows. In each case, the
+form with an uppercase initial character retains the string already
+typed on the command line as the final result of completion, while with
+a lowercase initial character the string on the command line is changed
+into the corresponding part of the trial completion.
+
+startitem()
+xitem(tt(m:)var(lpat)tt(=)var(tpat))
+item(tt(M:)var(lpat)tt(=)var(tpat))(
+Here, var(lpat) is a pattern that matches on the command line,
+corresponding to var(tpat) which matches in the trial completion.
+)
+xitem(tt(l:)var(anchor)tt(|)var(lpat)tt(=)var(tpat))
+item(tt(L:)var(anchor)tt(|)var(lpat)tt(=)var(tpat))(
+These letters are for patterns that are anchored by another pattern on
+the left side. Matching for var(lpat) and var(tpat) is as for tt(m) and
+tt(M), but the pattern var(lpat) matched on the command line must be
+preceeded by the pattern var(anchor).  The var(anchor) can be blank to
+anchor the match to the start of the command line string; otherwise the
+anchor can occur anywhere, but must match in both the command line and
+trial completion strings.
+)
+xitem(tt(r:)var(lpat)tt(|)var(anchor)tt(=)var(tpat))
+item(tt(R:)var(lpat)tt(|)var(anchor)tt(=)var(tpat))(
+As tt(l) and tt(L) with the difference that the command line and trial
+completion patterns are anchored on the right side.  Here an empty
+var(anchor) forces the match to the end of the command line string.
+)
+enditem()
+
+Each var(lpat), var(tpat) or var(anchor) is either an empty string or
+consists of a sequence of literal characters (which may be quoted with a
+backslash), question marks, character classes, and correspondence
+classes; ordinary shell patterns are not used.  Literal characters match
+only themselves, question marks match any character, and character
+classes are formed as for globbing and match any character in the given
+set.
+
+Correspondence classes are defined like character classes, but with two
+differences: they are delimited by a pair of braces, and negated classes
+are not allowed, so the characters tt(!) and tt(^) have no special
+meaning directly after the opening brace.  They indicate that a range of
+characters on the line match a range of characters in the trial
+completion, but (unlike ordinary character classes) paired according to
+the corresponding position in the sequence. For example, to make any
+lowercase letter on the line match the corresponding uppercase letter in
+the trial completion, you can use `tt(m:{a-z}={A-Z})'.  More than one
+pair of classes can occur, in which case the first class before the
+tt(=) corresponds to the first after it, 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
+also behave like normal character classes.
+
+The pattern var(tpat) may also be a single star, `tt(*)'. This means
+that the pattern on the command line can match any number of characters
+in the trial completion. In this case the pattern must be anchored (on
+either side); the var(anchor) then determines how much of the trial
+completion is to be included --- only the characters up to the next
+appearance of the anchor will be matched.
+
+Examples:
+
+The keys of the tt(options) association defined by the tt(parameter)
+module are the option names in all-lowercase form, without
+underscores, and without the optional tt(no) at the beginning even
+though the builtins tt(setopt) and tt(unsetopt) understand option names
+with uppercase letters, underscores, and the optional tt(no).  The
+following alters the matching rules so that the prefix tt(no) and any
+underscore are ignored when trying to match the trial completions
+generated and uppercase letters on the line match the corresponding
+lowercase letters in the words:
+
+example(compadd -M 'L:|[nN][oO]= M:_= M:{A-Z}={a-z}' - \ 
+  ${(k)options} )
+
+The first part says that the pattern `tt([nN][oO])' at the beginning
+(the empty anchor before the pipe symbol) of the string on the
+line matches the empty string in the list of words generated by
+completion, so it will be ignored if present. The second part does the
+same for an underscore anywhere in the command line string, and the
+third part uses correspondence classes so 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 what has
+already been typed on the command line (in particular the prefix
+tt(no)) will not be deleted.
+
+The second example makes completion case insensitive.  By setting the
+tt(compmatchers) array this applies to every
+completion.  This is just the same as in the option example, except
+here we wish to retain the characters in the list of completions:
+
+example(compmatchers=( 'm:{a-z}={A-Z}' ) )
+
+This makes lowercase letters match their uppercase counterparts.
+To make uppercase letters match the lowercase forms as well:
+
+example(compmatchers=( 'm:{a-zA-Z}={A-Za-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. the word on the
+command line consists of multiple parts, separated by a dot in this
+example, where each part should be completed separately --- note,
+however, that the case where each part of the word, i.e. tt(comp),
+tt(source) and tt(unix) in this example, is to be completed separately
+is a different problem to be solved by extended completion.  The
+example can be handled by:
+
+example(compadd -M 'r:|.=* r:|=*' \ 
+  - comp.sources.unix comp.sources.misc ...)
+
+The first specification says that tt(lpat) is the empty string, while
+tt(anchor) is a dot; tt(tpat) is tt(*), so this can match anything
+except for the `tt(.)' from the anchor in
+the trial completion word.  So in tt(c.s.u), the matcher sees tt(c),
+followed by the empty string, followed by the anchor `tt(.)', and
+likewise for the second dot, and replaces the empty strings before the
+anchors, giving tt(c)[tt(omp)]tt(.s)[tt(ources)]tt(.u)[tt(nix)], where
+the last part of the completion is just as normal.
+
+The second specification is needed to make this work when the cursor is
+in the middle of the string on the command line and the option
+tt(COMPLETE_IN_WORD) is set. In this case the completion code would
+normally try to match trial completions that end with the string as
+typed so far, i.e. it will only insert new characters at the cursor
+position rather then at the end.  However in our example we would like
+the code to recognise matches which contain extra characters after the
+string on the line (the tt(nix) in the example).  Hence we say that the
+empty string at the end of the string on the line matches any characters
+at the end of the trial completion.
+
+More generally, the specification
+
+example(compmatchers=( 'r:|[.,_-]=* r:|=*' ) )
+
+allows one to complete words with abbreviations before any of the
+characters in the square brackets in any completion.  For example, to
+complete tt(veryverylongfile.c) rather than tt(veryverylongheader.h)
+with the above in effect, you can just type tt(very.c) before attempting
+completion.
+
+The tt(compmatchers) array that defines global matching can actually 
+contain any number of specification strings, unlike the case where the
+tt(-M) option is used with the tt(compadd) builtin command.  In this case, when
+completion is attempted for any command, the code will try the
+specifications in order until one matches.  This allows one to define
+simple and fast matches to be used first, more powerful matchers as a
+second choice, and so on.
+
+For example, one can make the code match trial completions that contain
+the string on the command line as a substring, not just at the
+beginning.  Since this might produce more matches than we want,
+we arrange for it to be tried only if the matchers described above don't
+produce any matches:
+
+example(compmatchers=( 'r:|[.,_-]=* r:|=*' 'l:|=* r:|=*' ) )
+
+Here, if the string on the command line is tt(foo.bar), the completion 
+code first tries matching tt(foo)var(anything)tt(.bar)var(anything), as
+with the previous example.  If that fails, the two descriptions in the
+second string say that the blanks at the beginning
+and end of the string on the command line can match any set of
+characters at the beginning or end of the trial completion, so it will
+look for var(anything)tt(foo.bar)var(anything).
+
+texinode(Examples)()(Matching Control)(Completion Widgets)
 sect(Examples)
 cindex(completion widgets, examples)
 
 The first step is to define the widget:
 
-example(zle -C complete complete-word complete-history)
+example(zle -C complete complete-word complete-files)
 
 Then the widget can be bound to a key using the tt(bindkey) builtin
 command:
 
 example(bindkey '^X\t' complete)
 
-After that the shell function tt(complete-history) will be invoked
+After that the shell function tt(complete-files) will be invoked
 after typing control-X and TAB. The function should then generate the
 matches, e.g.:
 
-example(complete-history LPAR()RPAR() { compadd - $historywords })
+example(complete-filles LPAR()RPAR() { compadd - * })
 
-This function will complete words from the history matching the 
+This function will complete files in the current directory matching the 
 current word.
 
 For a description of the widget-based completion system provided with the
diff --git a/Doc/Zsh/intro.yo b/Doc/Zsh/intro.yo
index 0e4e1048f..fa593b76d 100644
--- a/Doc/Zsh/intro.yo
+++ b/Doc/Zsh/intro.yo
@@ -20,8 +20,8 @@ list(em(zshparam)     Zsh parameters)
 list(em(zshoptions)   Zsh options)
 list(em(zshbuiltins)  Zsh built-in functions)
 list(em(zshzle)       Zsh command line editing)
-list(em(zshcompctl)   Zsh completion control)
 list(em(zshcompwid)   Zsh completion widgets)
+list(em(zshcompctl)   Zsh completion control)
 list(em(zshmodules)   Zsh loadable modules)
 list(em(zshcompsys)   Zsh completion system)
 ifzshone(\
diff --git a/Doc/Zsh/manual.yo b/Doc/Zsh/manual.yo
index 16a1bdec6..30c8a0345 100644
--- a/Doc/Zsh/manual.yo
+++ b/Doc/Zsh/manual.yo
@@ -28,8 +28,8 @@ menu(Parameters)
 menu(Options)
 menu(Shell Builtin Commands)
 menu(Zsh Line Editor)
-menu(Programmable Completion Using compctl)
 menu(Completion Widgets)
+menu(Programmable Completion Using compctl)
 menu(Zsh Modules)
 menu(Completion System)
 menu(Zftp Function System)
diff --git a/Doc/Zsh/mod_complete.yo b/Doc/Zsh/mod_complete.yo
index 558bbc4ae..0443fcde8 100644
--- a/Doc/Zsh/mod_complete.yo
+++ b/Doc/Zsh/mod_complete.yo
@@ -1,6 +1,6 @@
 texinode(The complete Module)(The compctl Module)(The clone Module)(Zsh Modules)
 sect(The complete Module)
-The tt(compctl) module makes available several builtin commands which
+The tt(complete) module makes available several builtin commands which
 can be used in user-defined completion widgets, see
 ifzman(zmanref(zshcompwid))\
 ifnzman(noderef(Completion Widgets))\
diff --git a/Doc/Zsh/modules.yo b/Doc/Zsh/modules.yo
index 9ce81c264..827f68b4c 100644
--- a/Doc/Zsh/modules.yo
+++ b/Doc/Zsh/modules.yo
@@ -1,4 +1,4 @@
-texinode(Zsh Modules)(Completion System)(Completion Widgets)(Top)
+texinode(Zsh Modules)(Completion System)(Programmable Completion Using compctl)(Top)
 chapter(Zsh Modules)
 cindex(modules)
 sect(Description)
@@ -15,6 +15,9 @@ Builtins for manipulating POSIX.1e (POSIX.6) capability (privilege) sets.
 item(tt(clone))(
 A builtin that can clone a running shell onto another terminal.
 )
+item(tt(complete))(
+The basic completion code.
+)
 item(tt(compctl))(
 The tt(compctl) builtin for controlling completion and the builtins for
 completion widgets.
diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo
index 17fe7b345..baca81e62 100644
--- a/Doc/Zsh/zle.yo
+++ b/Doc/Zsh/zle.yo
@@ -1,4 +1,4 @@
-texinode(Zsh Line Editor)(Programmable Completion Using compctl)(Shell Builtin Commands)(Top)
+texinode(Zsh Line Editor)(Completion Widgets)(Shell Builtin Commands)(Top)
 chapter(Zsh Line Editor)
 cindex(line editor)
 cindex(editor, line)
diff --git a/Doc/zsh.yo b/Doc/zsh.yo
index dad958b80..c38313b4e 100644
--- a/Doc/zsh.yo
+++ b/Doc/zsh.yo
@@ -61,8 +61,8 @@ ifnzman(includefile(Zsh/params.yo))
 ifnzman(includefile(Zsh/options.yo))
 ifnzman(includefile(Zsh/builtins.yo))
 ifnzman(includefile(Zsh/zle.yo))
-ifnzman(includefile(Zsh/compctl.yo))
 ifnzman(includefile(Zsh/compwid.yo))
+ifnzman(includefile(Zsh/compctl.yo))
 ifnzman(includefile(Zsh/modules.yo))
 ifnzman(includefile(Zsh/compsys.yo))
 ifnzman(includefile(Zsh/zftpsys.yo))
@@ -75,8 +75,8 @@ source(zshparam)
 source(zshoptions)
 source(zshbuiltins)
 source(zshzle)
-source(zshcompctl)
 source(zshcompwid)
+source(zshcompctl)
 source(zshmodules)
 source(zshcompsys)
 source(zshzftpsys)
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index 68575cff4..6097f256c 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -238,7 +238,6 @@ struct cadata {
     char *pre;			/* prefix to insert (-P) */
     char *suf;			/* suffix to insert (-S) */
     char *group;		/* name of the group (-[JV]) */
-    char *ylist;		/* display list (-y) */
     char *rems;			/* remove suffix on chars... (-r) */
     char *remf;			/* function to remove suffix (-R) */
     char *ign;			/* ignored suffixes (-F) */
@@ -270,28 +269,10 @@ struct cldata {
 typedef void (*CLPrintFunc)(Cmgroup, Cmatch *, int, int, int, int,
 			    char *, struct stat *);
 
-/* Information about one brace run. */
+/* Flags for fromcomp. */
 
-typedef struct brinfo *Brinfo;
-
-struct brinfo {
-    Brinfo next;		/* next in list */
-    Brinfo prev;		/* previous (only for closing braces) */
-    char *str;			/* the string to insert */
-    int pos;			/* original position */
-    int qpos;			/* original position, with quoting */
-    int curpos;			/* position for current match */
-};
-
-/* Data given to hooks. */
-
-typedef struct chdata *Chdata;
-
-struct chdata {
-    Cmgroup matches;		/* the matches generated */
-    int num;			/* the number of matches */
-    Cmatch cur;			/* current match or NULL */
-};
+#define FC_LINE   1
+#define FC_INWORD 2
 
 /* Flags for special parameters. */
 
@@ -376,12 +357,30 @@ struct chdata {
 #define CP_KEYPARAMS   27
 #define CP_ALLKEYS     ((unsigned int) 0x7ffffff)
 
-/* Types of completion. */
+/* Hooks. */
+
+#define INSERTMATCHHOOK     (comphooks + 0)
+#define MENUSTARTHOOK       (comphooks + 1)
+#define COMPCTLMAKEHOOK     (comphooks + 2)
+#define COMPCTLBEFOREHOOK   (comphooks + 3)
+#define COMPCTLAFTERHOOK    (comphooks + 4)
+#define COMPLISTMATCHESHOOK (comphooks + 5)
+
+/* compctl hook data struct */
+
+struct ccmakedat {
+    char *str;
+    int incmd;
+    int lst;
+};
+
+/* Data given to offered hooks. */
+
+typedef struct chdata *Chdata;
+
+struct chdata {
+    Cmgroup matches;		/* the matches generated */
+    int num;			/* the number of matches */
+    Cmatch cur;			/* current match or NULL */
+};
 
-#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
-#define COMP_ISEXPAND(X) ((X) >= COMP_EXPAND)
diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c
new file mode 100644
index 000000000..ea9ff5b20
--- /dev/null
+++ b/Src/Zle/compcore.c
@@ -0,0 +1,2530 @@
+/*
+ * compcore.c - the complete module, completion core code
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1999 Sven Wischnowsky
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Sven Wischnowsky or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Sven Wischnowsky and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Sven Wischnowsky and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose.  The software
+ * provided hereunder is on an "as is" basis, and Sven Wischnowsky and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "complete.mdh"
+#define GLOBAL_PROTOTYPES
+#include "zle_tricky.pro"
+#undef GLOBAL_PROTOTYPES
+#include "compcore.pro"
+
+/* The last completion widget called. */
+
+static Widget lastcompwidget;
+
+/* Flags saying what we have to do with the result. */
+
+/**/
+int useexact, useline, uselist;
+
+/* Non-zero if we should keep an old list. */
+
+/**/
+int oldlist, oldins;
+
+/* This is used to decide when the cursor should be moved to the end of    *
+ * the inserted word: 0 - never, 1 - only when a single match is inserted, *
+ * 2 - when a full match is inserted (single or menu), 3 - always.         */
+
+/**/
+int movetoend;
+
+/* The match and group number to insert when starting menucompletion.   */
+
+/**/
+int insmnum, insgnum, insgroup, insspace;
+
+/* Information about menucompletion. */
+
+/**/
+struct menuinfo minfo;
+
+/* Number of matches accepted with accept-and-menu-complete */
+
+/**/
+int menuacc;
+
+/* Brace insertion stuff. */
+
+/**/
+int hasunqu, useqbr, brpcs, brscs;
+
+/* Flags saying in what kind of string we are. */
+
+/**/
+int ispar, linwhat;
+
+/* A parameter expansion prefix (like ${). */
+
+/**/
+char *parpre;
+
+/* Flags for parameter expansions for new style completion. */
+
+/**/
+int parflags;
+
+/* Match flags for all matches in this group. */
+
+/**/
+int mflags;
+
+/* Flags saying how the parameter expression we are in is quoted. */
+
+/**/
+int parq, eparq;
+
+/* We store the following prefixes/suffixes:                               *
+ * ipre,ripre  -- the ignored prefix (quoted and unquoted)                 *
+ * isuf        -- the ignored suffix                                       *
+ * autoq       -- quotes to automatically insert                           */
+
+/**/
+char *ipre, *ripre, *isuf;
+
+/* The list of matches.  fmatches contains the matches we first ignore *
+ * because of fignore.                                                 */
+
+/**/
+LinkList matches, fmatches;
+
+/* This holds the list of matches-groups. lastmatches holds the last list of 
+ * permanently allocated matches, pmatches is the same for the list
+ * currently built, amatches is the heap allocated stuff during completion
+ * (after all matches have been generated it is an alias for pmatches), and
+ * lmatches/lastlmatches is a pointer to the last element in the lists. */
+
+/**/
+Cmgroup lastmatches, pmatches, amatches, lmatches, lastlmatches;
+
+/* Non-zero if we have permanently allocated matches (old and new). */
+
+/**/
+int hasoldlist, hasperm;
+
+/* Non-zero if we have newly added matches. */
+
+/**/
+int newmatches;
+
+/* Number of permanently allocated matches and groups. */
+
+/**/
+int permmnum, permgnum, lastpermmnum, lastpermgnum;
+
+/* The total number of matches and the number of matches to be listed. */
+
+/**/
+int nmatches, smatches;
+
+/* != 0 if only explanation strings should be printed */
+
+/**/
+int onlyexpl;
+
+/* Information about the matches for listing. */
+
+/**/
+struct cldata listdat;
+
+/* This flag is non-zero if we are completing a pattern (with globcomplete) */
+
+/**/
+int ispattern, haspattern;
+
+/* Non-zero if at least one match was added without -U. */
+
+/**/
+int hasmatched;
+
+/* The current group of matches. */
+
+/**/
+Cmgroup mgroup;
+
+/* Match counter: all matches. */
+
+/**/
+int mnum;
+
+/* The match counter when unambig_data() was called. */
+
+/**/
+int unambig_mnum;
+
+/* Length of longest/shortest match. */
+
+/**/
+int maxmlen, minmlen;
+
+/* This holds the explanation strings we have to print in this group and *
+ * a pointer to the current cexpl structure. */
+
+/**/
+LinkList expls;
+
+/**/
+Cexpl curexpl;
+
+/* A stack of completion matchers to be used. */
+
+/**/
+Cmlist mstack;
+
+/* The completion matchers used when building new stuff for the line. */
+
+/**/
+Cmlist bmatchers;
+
+/* A list with references to all matchers we used. */
+
+/**/
+LinkList matchers;
+
+/* A heap of free Cline structures. */
+
+/**/
+Cline freecl;
+
+/* Ambiguous information. */
+
+/**/
+Aminfo ainfo, fainfo;
+
+/* The memory heap to use for new style completion generation. */
+
+/**/
+Heap compheap;
+
+/* A list of some data.
+ *
+ * Well, actually, it's the list of all compctls used so far, but since
+ * conceptually we don't know anything about compctls here... */
+
+/**/
+LinkList allccs;
+
+/* This says what of the state the line is in when completion is started *
+ * came from a previous completion. If the FC_LINE bit is set, the       *
+ * string was inserted. If FC_INWORD is set, the last completion moved   *
+ * the cursor into the word although it was at the end of it when the    *
+ * last completion was invoked.                                          *
+ * This is used to detect if the string should be taken as an exact      *
+ * match (see do_ambiguous()) and if the cursor has to be moved to the   *
+ * end of the word before generating the completions.                    */
+
+/**/
+int fromcomp;
+
+/* This holds the end-position of the last string inserted into the line. */
+
+/**/
+int lastend;
+
+/* Convenience macro for calling bslashquote() (formerly quotename()). */
+
+#define quotename(s, e) bslashquote(s, e, instring)
+
+#define inststr(X) inststrlen((X),1,-1)
+
+/* Main completion entry point, called from zle. */
+
+/**/
+int
+do_completion(Hookdef dummy, Compldat dat)
+{
+    int ret = 0, lst = dat->lst, incmd = dat->incmd;
+    char *s = dat->s;
+
+    HEAPALLOC {
+	char *opm;
+	LinkNode n;
+
+	pushheap();
+
+	ainfo = fainfo = NULL;
+	matchers = newlinklist();
+
+	hasunqu = 0;
+	useline = (lst != COMP_LIST_COMPLETE);
+	useexact = isset(RECEXACT);
+	uselist = (useline ?
+		   ((isset(AUTOLIST) && !isset(BASHAUTOLIST)) ? 
+		    (isset(LISTAMBIGUOUS) ? 3 : 2) : 0) : 1);
+	zsfree(comppatmatch);
+	opm = comppatmatch = ztrdup(useglob ? "*" : "");
+	zsfree(comppatinsert);
+	comppatinsert = ztrdup("menu");
+	zsfree(compforcelist);
+	compforcelist = ztrdup("");
+	haspattern = 0;
+	complistmax = getiparam("LISTMAX");
+	zsfree(complastprompt);
+	complastprompt = ztrdup(((isset(ALWAYSLASTPROMPT) && zmult == 1) ||
+				(unset(ALWAYSLASTPROMPT) && zmult != 1)) ?
+				"yes" : "");
+	movetoend = ((cs == we || isset(ALWAYSTOEND)) ? 2 : 1);
+	showinglist = 0;
+	hasmatched = 0;
+	minmlen = 1000000;
+	maxmlen = -1;
+
+	/* Make sure we have the completion list and compctl. */
+	if (makecomplist(s, incmd, lst)) {
+	    /* Error condition: feeeeeeeeeeeeep(). */
+	    cs = 0;
+	    foredel(ll);
+	    inststr(origline);
+	    cs = origcs;
+	    clearlist = 1;
+	    ret = 1;
+	    minfo.cur = NULL;
+	    goto compend;
+	}
+	zsfree(lastprebr);
+	zsfree(lastpostbr);
+	lastprebr = lastpostbr = NULL;
+
+	if (comppatmatch && *comppatmatch && comppatmatch != opm)
+	    haspattern = 1;
+	if (!useline && uselist) {
+	    /* All this and the guy only wants to see the list, sigh. */
+	    cs = 0;
+	    foredel(ll);
+	    inststr(origline);
+	    cs = origcs;
+	    showinglist = -2;
+	} else if (useline == 2 && nmatches > 1) {
+	    int first = 1, nm = nmatches;
+	    Cmatch *mc;
+
+	    menucmp = 1;
+	    menuacc = 0;
+
+	    for (minfo.group = amatches;
+		 minfo.group && !(minfo.group)->mcount;
+		 minfo.group = (minfo.group)->next);
+
+	    mc = (minfo.group)->matches;
+
+	    while (1) {
+		if (!first)
+		    acceptlast();
+		first = 0;
+
+		if (!--nm)
+		    menucmp = 0;
+
+		do_single(*mc);
+		minfo.cur = mc;
+
+		if (!*++(minfo.cur)) {
+		    do {
+			if (!(minfo.group = (minfo.group)->next))
+			    break;
+		    } while (!(minfo.group)->mcount);
+		    if (!minfo.group)
+			break;
+		    minfo.cur = minfo.group->matches;
+		}
+		mc = minfo.cur;
+	    }
+	    menucmp = 0;
+	    minfo.cur = NULL;
+
+	    if (compforcelist && *compforcelist && uselist)
+		showinglist = -2;
+	    else
+		invalidatelist();
+	} else if (useline) {
+	    /* We have matches. */
+	    if (nmatches > 1) {
+		/* There is more than one match. */
+		ret = do_ambiguous();
+	    } else if (nmatches == 1) {
+		/* Only one match. */
+		Cmgroup m = amatches;
+
+		while (!m->mcount)
+		    m = m->next;
+		minfo.cur = NULL;
+		minfo.asked = 0;
+		do_single(m->matches[0]);
+		if (compforcelist && *compforcelist) {
+		    if (uselist)
+			showinglist = -2;
+		    else
+			clearlist = 1;
+		} else
+		    invalidatelist();
+	    }
+	} else {
+	    invalidatelist();
+	    if (compforcelist && *compforcelist)
+		clearlist = 1;
+	    cs = 0;
+	    foredel(ll);
+	    inststr(origline);
+	    cs = origcs;
+	}
+	/* Print the explanation strings if needed. */
+	if (!showinglist && validlist && usemenu != 2 && nmatches != 1 &&
+	    useline != 2 && (!oldlist || !listshown)) {
+	    onlyexpl = 1;
+	    showinglist = -2;
+	}
+      compend:
+	for (n = firstnode(matchers); n; incnode(n))
+	    freecmatcher((Cmatcher) getdata(n));
+
+	ll = strlen((char *)line);
+	if (cs > ll)
+	    cs = ll;
+	popheap();
+    } LASTALLOC;
+
+    return ret;
+}
+
+/* Before and after hooks called by zle. */
+
+static int oldmenucmp;
+
+/**/
+int
+before_complete(Hookdef dummy, int *lst)
+{
+    oldmenucmp = menucmp;
+
+    if (showagain && validlist)
+	showinglist = -2;
+    showagain = 0;
+
+    /* If we are doing a menu-completion... */
+
+    if (menucmp && *lst != COMP_LIST_EXPAND && 
+	(!compwidget || compwidget == lastcompwidget)) {
+	do_menucmp(*lst);
+	return 1;
+    }
+    if (menucmp && validlist && *lst == COMP_LIST_COMPLETE) {
+	showinglist = -2;
+	onlyexpl = listdat.valid = 0;
+	return 1;
+    }
+    lastcompwidget = compwidget;
+
+    /* We may have to reset the cursor to its position after the   *
+     * string inserted by the last completion. */
+
+    if (fromcomp & FC_INWORD)
+	if ((cs = lastend) > ll)
+	    cs = ll;
+
+    /* Check if we have to start a menu-completion (via automenu). */
+
+    if (isset(AUTOMENU) && lastambig &&
+	(!isset(BASHAUTOLIST) || lastambig == 2))
+	usemenu = 2;
+
+    return 0;
+}
+
+/**/
+int
+after_complete(Hookdef dummy, Compldat dat)
+{
+    if (menucmp && !oldmenucmp) {
+	struct chdata dat;
+
+	dat.matches = amatches;
+	dat.num = nmatches;
+	dat.cur = NULL;
+	if (runhookdef(MENUSTARTHOOK, (void *) &dat))
+	    menucmp = menuacc = 0;
+    }
+    return 0;
+}
+
+/* This calls the given completion widget function. */
+
+/**/
+static void
+callcompfunc(char *s, char *fn)
+{
+    List list;
+    int lv = lastval;
+    char buf[20];
+
+    if ((list = getshfunc(fn)) != &dummy_list) {
+	char **p, *tmp;
+	int aadd = 0, usea = 1, icf = incompfunc, osc = sfcontext;
+	unsigned int rset, kset;
+	Param *ocrpms = comprpms, *ockpms = compkpms;
+
+	comprpms = (Param *) zalloc(CP_REALPARAMS * sizeof(Param));
+	compkpms = (Param *) zalloc(CP_KEYPARAMS * sizeof(Param));
+
+	rset = CP_ALLREALS;
+	kset = CP_ALLKEYS &
+	    ~(CP_PARAMETER | CP_REDIRECT | CP_QUOTE | CP_QUOTING |
+	      CP_EXACTSTR | CP_FORCELIST | CP_OLDLIST | CP_OLDINS |
+	      (useglob ? 0 : CP_PATMATCH));
+	zsfree(compvared);
+	if (varedarg) {
+	    compvared = ztrdup(varedarg);
+	    kset |= CP_VARED;
+	} else
+	    compvared = ztrdup("");
+	if (!*complastprompt)
+	    kset &= ~CP_LASTPROMPT;
+	zsfree(compcontext);
+	zsfree(compparameter);
+	zsfree(compredirect);
+	compparameter = compredirect = "";
+	if (ispar)
+	    compcontext = (ispar == 2 ? "brace_parameter" : "parameter");
+	else if (linwhat == IN_MATH) {
+	    if (insubscr) {
+		compcontext = "subscript";
+		if (varname) {
+		    compparameter = varname;
+		    kset |= CP_PARAMETER;
+		}
+	    } else
+		compcontext = "math";
+	    usea = 0;
+	} else if (lincmd) {
+	    if (insubscr) {
+		compcontext = "subscript";
+		kset |= CP_PARAMETER;
+	    } else
+		compcontext = "command";
+	} else if (linredir) {
+	    compcontext = "redirect";
+	    if (rdstr)
+		compredirect = rdstr;
+	    kset |= CP_REDIRECT;
+	} else
+	    switch (linwhat) {
+	    case IN_ENV:
+		compcontext = (linarr ? "array_value" : "value");
+		compparameter = varname;
+		kset |= CP_PARAMETER;
+		if (!clwpos) {
+		    clwpos = 1;
+		    clwnum = 2;
+		    zsfree(clwords[1]);
+		    clwords[1] = ztrdup(s);
+		    zsfree(clwords[2]);
+		    clwords[2] = NULL;
+		}
+		aadd = 1;
+		break;
+	    case IN_COND:
+		compcontext = "condition";
+		break;
+	    default:
+		if (cmdstr)
+		    compcontext = "command";
+		else {
+		    compcontext = "value";
+		    kset |= CP_PARAMETER;
+		    if (clwords[0])
+			compparameter = clwords[0];
+		    aadd = 1;
+		}
+	    }
+	compcontext = ztrdup(compcontext);
+	if (compwords)
+	    freearray(compwords);
+	if (usea && (!aadd || clwords[0])) {
+	    char **q;
+
+	    PERMALLOC {
+		q = compwords = (char **)
+		    zalloc((clwnum + 1) * sizeof(char *));
+		for (p = clwords + aadd; *p; p++, q++) {
+		    tmp = dupstring(*p);
+		    untokenize(tmp);
+		    *q = ztrdup(tmp);
+		}
+		*q = NULL;
+	    } LASTALLOC;
+	} else
+	    compwords = (char **) zcalloc(sizeof(char *));
+
+	compparameter = ztrdup(compparameter);
+	compredirect = ztrdup(compredirect);
+	zsfree(compquote);
+	zsfree(compquoting);
+	if (instring) {
+	    if (instring == 1) {
+		compquote = ztrdup("\'");
+		compquoting = ztrdup("single");
+	    } else {
+		compquote = ztrdup("\"");
+		compquoting = ztrdup("double");
+	    }
+	    kset |= CP_QUOTE | CP_QUOTING;
+	} else if (inbackt) {
+	    compquote = ztrdup("`");
+	    compquoting = ztrdup("backtick");
+	    kset |= CP_QUOTE | CP_QUOTING;
+	} else {
+	    compquote = ztrdup("");
+	    compquoting = ztrdup("");
+	}
+	zsfree(compprefix);
+	zsfree(compsuffix);
+	if (unset(COMPLETEINWORD)) {
+	    tmp = quotename(s, NULL);
+	    untokenize(tmp);
+	    compprefix = ztrdup(tmp);
+	    compsuffix = ztrdup("");
+	} else {
+	    char *ss, sav;
+	    
+	    ss = s + offs;
+
+	    sav = *ss;
+	    *ss = '\0';
+	    tmp = quotename(s, NULL);
+	    untokenize(tmp);
+	    compprefix = ztrdup(tmp);
+	    *ss = sav;
+	    ss = quotename(ss, NULL);
+	    untokenize(ss);
+	    compsuffix = ztrdup(ss);
+	}
+	zsfree(compiprefix);
+	compiprefix = ztrdup("");
+	zsfree(compisuffix);
+	compisuffix = ztrdup("");
+	zsfree(compqiprefix);
+	compqiprefix = ztrdup(qipre ? qipre : "");
+	zsfree(compqisuffix);
+	compqisuffix = ztrdup(qisuf ? qisuf : "");
+	compcurrent = (usea ? (clwpos + 1 - aadd) : 0);
+
+	zsfree(complist);
+	switch (uselist) {
+	case 0: complist = ""; kset &= ~CP_LIST; break;
+	case 1: complist = "list"; break;
+	case 2: complist = "autolist"; break;
+	case 3: complist = "ambiguous"; break;
+	}
+	complist = ztrdup(complist);
+	zsfree(compinsert);
+	if (useline) {
+	    switch (usemenu) {
+	    case 0: compinsert = "unambiguous"; break;
+	    case 1: compinsert = "menu"; break;
+	    case 2: compinsert = "automenu"; break;
+	    }
+	} else {
+	    compinsert = "";
+	    kset &= ~CP_INSERT;
+	}
+	compinsert = ztrdup(compinsert);
+	if (useexact)
+	    compexact = ztrdup("accept");
+	else {
+	    compexact = ztrdup("");
+	    kset &= ~CP_EXACT;
+	}
+	zsfree(comptoend);
+	if (movetoend == 1)
+	    comptoend = ztrdup("single");
+	else
+	    comptoend = ztrdup("match");
+	zsfree(compoldlist);
+	zsfree(compoldins);
+	if (hasoldlist && lastpermmnum) {
+	    if (listshown)
+		compoldlist = "shown";
+	    else
+		compoldlist = "yes";
+	    kset |= CP_OLDLIST;
+	    if (minfo.cur) {
+		sprintf(buf, "%d", (*(minfo.cur))->gnum);
+		compoldins = buf;
+		kset |= CP_OLDINS;
+	    } else
+		compoldins = "";
+	} else
+	    compoldlist = compoldins = "";
+	compoldlist = ztrdup(compoldlist);
+	compoldins = ztrdup(compoldins);
+
+	incompfunc = 1;
+	startparamscope();
+	makecompparams();
+	comp_setunset(rset, (~rset & CP_ALLREALS),
+		      kset, (~kset & CP_ALLKEYS));
+	makezleparams(1);
+	sfcontext = SFC_CWIDGET;
+	NEWHEAPS(compheap) {
+	    LinkList largs = NULL;
+	    int olv = lastval;
+
+	    if (*cfargs) {
+		char **p = cfargs;
+
+		largs = newlinklist();
+		addlinknode(largs, dupstring(fn));
+		while (*p)
+		    addlinknode(largs, dupstring(*p++));
+	    }
+	    doshfunc(fn, list, largs, 0, 0);
+	    cfret = lastval;
+	    lastval = olv;
+	} OLDHEAPS;
+	sfcontext = osc;
+	endparamscope();
+	lastcmd = 0;
+	incompfunc = icf;
+
+	if (!complist)
+	    uselist = 0;
+	else if (!strncmp(complist, "list", 4))
+	    uselist = 1;
+	else if (!strncmp(complist, "auto", 4))
+	    uselist = 2;
+	else if (!strncmp(complist, "ambig", 5))
+	    uselist = 3;
+	else
+	    uselist = 0;
+
+	onlyexpl = (complist && strstr(complist, "expl"));
+
+	if (!compinsert)
+	    useline = 0;
+	else if (!strcmp(compinsert, "unambig") ||
+		 !strcmp(compinsert, "unambiguous"))
+	    useline = 1, usemenu = 0;
+	else if (!strcmp(compinsert, "menu"))
+	    useline = 1, usemenu = 1;
+	else if (!strcmp(compinsert, "auto") ||
+		 !strcmp(compinsert, "automenu"))
+	    useline = 1, usemenu = 2;
+	else if (!strcmp(compinsert, "all"))
+	    useline = 2, usemenu = 0;
+	else if (idigit(*compinsert)) {
+	    char *m;
+
+	    useline = 1; usemenu = 3;
+	    insmnum = atoi(compinsert);
+	    if ((m = strchr(compinsert, ':'))) {
+		insgroup = 1;
+		insgnum = atoi(m + 1);
+	    }
+	    insspace = (compinsert[strlen(compinsert) - 1] == ' ');
+	} else
+	    useline = usemenu = 0;
+	useexact = (compexact && !strcmp(compexact, "accept"));
+
+	if (!comptoend || !*comptoend)
+	    movetoend = 0;
+	else if (!strcmp(comptoend, "single"))
+	    movetoend = 1;
+	else if (!strcmp(comptoend, "always"))
+	    movetoend = 3;
+	else
+	    movetoend = 2;
+
+	oldlist = (hasoldlist && compoldlist && !strcmp(compoldlist, "keep"));
+	oldins = (hasoldlist && minfo.cur &&
+		  compoldins && !strcmp(compoldins, "keep"));
+
+	zfree(comprpms, CP_REALPARAMS * sizeof(Param));
+	zfree(compkpms, CP_KEYPARAMS * sizeof(Param));
+	comprpms = ocrpms;
+	compkpms = ockpms;
+    }
+    lastval = lv;
+}
+
+/* Create the completion list.  This is called whenever some bit of   *
+ * completion code needs the list.                                    *
+ * Along with the list is maintained the prefixes/suffixes etc.  When *
+ * any of this becomes invalid -- e.g. if some text is changed on the *
+ * command line -- invalidatelist() should be called, to set          *
+ * validlist to zero and free up the memory used.  This function      *
+ * returns non-zero on error.                                         */
+
+/**/
+static int
+makecomplist(char *s, int incmd, int lst)
+{
+    struct cmlist ms;
+    Cmlist m;
+    char *p, *os = s;
+    int onm = nmatches, osi = movefd(0);
+
+    /* Inside $... ? */
+    if (compfunc && (p = check_param(s, 0, 0)))
+	os = s = p;
+
+    /* We build a copy of the list of matchers to use to make sure that this
+     * works even if a shell function called from the completion code changes
+     * the global matchers. */
+
+    if ((m = cmatcher)) {
+	Cmlist mm, *mp = &mm;
+	int n;
+
+	for (n = 0; m; m = m->next, n++) {
+	    if (m->matcher) {
+		*mp = (Cmlist) zhalloc(sizeof(struct cmlist));
+		(*mp)->matcher = m->matcher;
+		(*mp)->next = NULL;
+		(*mp)->str = dupstring(m->str);
+		mp = &((*mp)->next);
+		addlinknode(matchers, m->matcher);
+		m->matcher->refc++;
+	    }
+	}
+	m = mm;
+	compmatcher = 1;
+	compmatchertot = n;
+    } else
+	compmatcher = 0;
+
+    linwhat = inwhat;
+
+    /* Walk through the global matchers. */
+    for (;;) {
+	bmatchers = NULL;
+	zsfree(compmatcherstr);
+	if (m) {
+	    ms.next = NULL;
+	    ms.matcher = m->matcher;
+	    mstack = &ms;
+
+	    /* Store the matchers used in the bmatchers list which is used
+	     * when building new parts for the string to insert into the 
+	     * line. */
+	    add_bmatchers(m->matcher);
+	    compmatcherstr = ztrdup(m->str);
+	} else {
+	    mstack = NULL;
+	    compmatcherstr = ztrdup("");
+	}
+	ainfo = (Aminfo) hcalloc(sizeof(struct aminfo));
+	fainfo = (Aminfo) hcalloc(sizeof(struct aminfo));
+
+	freecl = NULL;
+
+	if (!validlist)
+	    lastambig = 0;
+	amatches = NULL;
+	mnum = 0;
+	unambig_mnum = -1;
+	isuf = NULL;
+	insmnum = insgnum = 1;
+	insgroup = oldlist = oldins = 0;
+	begcmgroup("default", 0);
+	menucmp = menuacc = newmatches = onlyexpl = 0;
+
+	runhookdef(COMPCTLBEFOREHOOK, NULL);
+
+	s = dupstring(os);
+	if (compfunc)
+	    callcompfunc(s, compfunc);
+	else {
+	    struct ccmakedat dat;
+
+	    dat.str = s;
+	    dat.incmd = incmd;
+	    dat.lst = lst;
+	    runhookdef(COMPCTLMAKEHOOK, (void *) &dat);
+	}
+	endcmgroup(NULL);
+
+	runhookdef(COMPCTLAFTERHOOK,
+		   (void *) ((amatches && !oldlist) ? 1L : 0L));
+
+	if (oldlist) {
+	    nmatches = onm;
+	    validlist = 1;
+	    amatches = lastmatches;
+	    lmatches = lastlmatches;
+	    if (pmatches) {
+		freematches(pmatches);
+		pmatches = NULL;
+		hasperm = 0;
+	    }
+	    redup(osi, 0);
+
+	    return 0;
+	}
+	PERMALLOC {
+	    if (lastmatches) {
+		freematches(lastmatches);
+		lastmatches = NULL;
+	    }
+	    permmatches(1);
+	    amatches = pmatches;
+	    lastpermmnum = permmnum;
+	    lastpermgnum = permgnum;
+	} LASTALLOC;
+
+	lastmatches = pmatches;
+	lastlmatches = lmatches;
+	pmatches = NULL;
+	hasperm = 0;
+	hasoldlist = 1;
+
+	if (nmatches && !errflag) {
+	    validlist = 1;
+
+	    redup(osi, 0);
+
+	    return 0;
+	}
+	if (!m || !(m = m->next))
+	    break;
+
+	errflag = 0;
+	compmatcher++;
+    }
+    redup(osi, 0);
+    return 1;
+}
+
+/* Check if we have to complete a parameter name. */
+
+/**/
+char *
+check_param(char *s, int set, int test)
+{
+    char *p;
+
+    zsfree(parpre);
+    parpre = NULL;
+
+    if (!test)
+	ispar = parq = eparq = 0;
+    /* Try to find a `$'. */
+    for (p = s + offs; p > s && *p != String && *p != Qstring; p--);
+    if (*p == String || *p == Qstring) {
+	/* Handle $$'s */
+	while (p > s && (p[-1] == String || p[-1] == Qstring))
+	    p--;
+	while ((p[1] == String || p[1] == Qstring) &&
+	       (p[2] == String || p[2] == Qstring))
+	    p += 2;
+    }
+    if ((*p == String || *p == Qstring) && p[1] != Inpar && p[1] != Inbrack) {
+	/* This is really a parameter expression (not $(...) or $[...]). */
+	char *b = p + 1, *e = b;
+	int n = 0, br = 1, nest = 0;
+
+	if (*b == Inbrace) {
+	    char *tb = b;
+
+	    /* If this is a ${...}, see if we are before the '}'. */
+	    if (!skipparens(Inbrace, Outbrace, &tb))
+		return NULL;
+
+	    /* Ignore the possible (...) flags. */
+	    b++, br++;
+	    n = skipparens(Inpar, Outpar, &b);
+
+	    for (tb = p - 1; tb > s && *tb != Outbrace && *tb != Inbrace; tb--);
+	    if (tb > s && *tb == Inbrace && (tb[-1] == String || *tb == Qstring))
+		nest = 1;
+	}
+
+	/* Ignore the stuff before the parameter name. */
+	for (; *b; b++)
+	    if (*b != '^' && *b != Hat &&
+		*b != '=' && *b != Equals &&
+		*b != '~' && *b != Tilde)
+		break;
+	if (*b == '#' || *b == Pound || *b == '+')
+	    b++;
+
+	e = b;
+	if (br) {
+	    while (*e == (test ? Dnull : '"'))
+		e++, parq++;
+	    if (!test)
+		b = e;
+	}
+	/* Find the end of the name. */
+	if (*e == Quest || *e == Star || *e == String || *e == Qstring ||
+	    *e == '?'   || *e == '*'  || *e == '$'    ||
+	    *e == '-'   || *e == '!'  || *e == '@')
+	    e++;
+	else if (idigit(*e))
+	    while (idigit(*e))
+		e++;
+	else if (iident(*e))
+	    while (iident(*e) ||
+		   (comppatmatch && *comppatmatch && (*e == Star || *e == Quest)))
+		e++;
+
+	/* Now make sure that the cursor is inside the name. */
+	if (offs <= e - s && offs >= b - s && n <= 0) {
+	    char sav;
+
+	    if (br) {
+		p = e;
+		while (*p == (test ? Dnull : '"'))
+		    p++, parq--, eparq++;
+	    }
+	    /* It is. */
+	    if (test)
+		return b;
+	    /* If we were called from makecomplistflags(), we have to set the
+	     * global variables. */
+
+	    if (set) {
+		if (br >= 2) {
+		    mflags |= CMF_PARBR;
+		    if (nest)
+			mflags |= CMF_PARNEST;
+		}
+		/* Get the prefix (anything up to the character before the name). */
+		isuf = dupstring(e);
+		untokenize(isuf);
+		sav = *b;
+		*b = *e = '\0';
+		ripre = dyncat((ripre ? ripre : ""), s);
+		ipre = dyncat((ipre ? ipre : ""), s);
+		*b = sav;
+
+		untokenize(ipre);
+	    }
+	    /* Save the prefix. */
+	    if (compfunc) {
+		parflags = (br >= 2 ? CMF_PARBR : 0);
+		sav = *b;
+		*b = '\0';
+		untokenize(parpre = ztrdup(s));
+		*b = sav;
+	    }
+	    /* And adjust wb, we, and offs again. */
+	    offs -= b - s;
+	    wb = cs - offs;
+	    we = wb + e - b;
+	    ispar = (br >= 2 ? 2 : 1);
+	    b[we-wb] = '\0';
+	    return b;
+	}
+    }
+    return NULL;
+}
+
+/* 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;
+}
+
+/* This should probably be moved into tokenize(). */
+
+/**/
+char *
+ctokenize(char *p)
+{
+    char *r = p;
+    int bslash = 0;
+
+    tokenize(p);
+
+    for (p = r; *p; p++) {
+	if (*p == '\\')
+	    bslash = 1;
+	else {
+	    if (*p == '$' || *p == '{' || *p == '}') {
+		if (bslash)
+		    p[-1] = Bnull;
+		else
+		    *p = (*p == '$' ? String :
+			  (*p == '{' ? Inbrace : Outbrace));
+	    }
+	    bslash = 0;
+	}
+    }
+    return r;
+}
+
+/**/
+char *
+comp_str(int *ipl, int *pl, int untok)
+{
+    char *p = dupstring(compprefix);
+    char *s = dupstring(compsuffix);
+    char *ip = dupstring(compiprefix);
+    char *str;
+    int lp, ls, lip;
+
+    if (!untok) {
+	ctokenize(p);
+	remnulargs(p);
+	ctokenize(s);
+	remnulargs(s);
+    }
+    lp = strlen(p);
+    ls = strlen(s);
+    lip = strlen(ip);
+    str = zhalloc(lip + lp + ls + 1);
+    strcpy(str, ip);
+    strcat(str, p);
+    strcat(str, s);
+
+    if (ipl)
+	*ipl = lip;
+    if (pl)
+	*pl = lp;
+
+    return str;
+}
+
+/* This is for compset -q. */
+
+/**/
+int
+set_comp_sep(void)
+{
+    int lip, lp;
+    char *s = comp_str(&lip, &lp, 0);
+    LinkList foo = newlinklist();
+    LinkNode n;
+    int owe = we, owb = wb, ocs = cs, swb, swe, scs, soffs, ne = noerrs;
+    int tl, got = 0, i = 0, cur = -1, oll = ll, sl;
+    int ois = instring, oib = inbackt, noffs = lip + lp;
+    char *tmp, *p, *ns, *ol = (char *) line, sav, oaq = autoq, *qp, *qs;
+
+    if (compisuffix)
+	s = dyncat(s, compisuffix);
+    untokenize(s);
+
+    swb = swe = soffs = 0;
+    ns = NULL;
+
+    /* Put the string in the lexer buffer and call the lexer to *
+     * get the words we have to expand.                        */
+    zleparse = 1;
+    addedx = 1;
+    noerrs = 1;
+    lexsave();
+    tmp = (char *) zhalloc(tl = 3 + strlen(s));
+    tmp[0] = ' ';
+    memcpy(tmp + 1, s, noffs);
+    tmp[(scs = cs = 1 + noffs)] = 'x';
+    strcpy(tmp + 2 + noffs, s + noffs);
+    tmp = rembslash(tmp);
+    inpush(dupstrspace(tmp), 0, NULL);
+    line = (unsigned char *) tmp;
+    ll = tl - 1;
+    strinbeg(0);
+    noaliases = 1;
+    do {
+	ctxtlex();
+	if (tok == LEXERR) {
+	    int j;
+
+	    if (!tokstr)
+		break;
+	    for (j = 0, p = tokstr; *p; p++)
+		if (*p == Snull || *p == Dnull)
+		    j++;
+	    if (j & 1) {
+		tok = STRING;
+		if (p > tokstr && p[-1] == ' ')
+		    p[-1] = '\0';
+	    }
+	}
+	if (tok == ENDINPUT || tok == LEXERR)
+	    break;
+	if (tokstr && *tokstr)
+	    addlinknode(foo, (p = ztrdup(tokstr)));
+	else
+	    p = NULL;
+	if (!got && !zleparse) {
+	    DPUTS(!p, "no current word in substr");
+	    got = 1;
+	    cur = i;
+	    swb = wb - 1;
+	    swe = we - 1;
+	    soffs = cs - swb;
+	    chuck(p + soffs);
+	    ns = dupstring(p);
+	}
+	i++;
+    } while (tok != ENDINPUT && tok != LEXERR);
+    noaliases = 0;
+    strinend();
+    inpop();
+    errflag = zleparse = 0;
+    noerrs = ne;
+    lexrestore();
+    wb = owb;
+    we = owe;
+    cs = ocs;
+    line = (unsigned char *) ol;
+    ll = oll;
+    if (cur < 0 || i < 1)
+	return 1;
+    owb = offs;
+    offs = soffs;
+    if ((p = check_param(ns, 0, 1))) {
+	for (p = ns; *p; p++)
+	    if (*p == Dnull)
+		*p = '"';
+	    else if (*p == Snull)
+		*p = '\'';
+    }
+    offs = owb;
+    if (*ns == Snull || *ns == Dnull) {
+	instring = (*ns == Snull ? 1 : 2);
+	inbackt = 0;
+	swb++;
+	if (ns[strlen(ns) - 1] == *ns && ns[1])
+	    swe--;
+	autoq = (*ns == Snull ? '\'' : '"');
+    } else {
+	instring = 0;
+	autoq = '\0';
+    }
+    for (p = ns, i = swb; *p; p++, i++) {
+	if (INULL(*p)) {
+	    if (i < scs)
+		soffs--;
+	    if (p[1] || *p != Bnull) {
+		if (*p == Bnull) {
+		    if (scs == i + 1)
+			scs++, soffs++;
+		} else {
+		    if (scs > i--)
+			scs--;
+		}
+	    } else {
+		if (scs == swe)
+		    scs--;
+	    }
+	    chuck(p--);
+	}
+    }
+    sav = s[(i = swb - 1)];
+    s[i] = '\0';
+    qp = tricat(qipre, rembslash(s), "");
+    s[i] = sav;
+    if (swe < swb)
+	swe = swb;
+    swe--;
+    sl = strlen(s);
+    if (swe > sl)
+	swe = sl, ns[swe - swb + 1] = '\0';
+    qs = tricat(rembslash(s + swe), qisuf, "");
+    sl = strlen(ns);
+    if (soffs > sl)
+	soffs = sl;
+
+    {
+	int set = CP_QUOTE | CP_QUOTING, unset = 0;
+
+	zsfree(compquote);
+	zsfree(compquoting);
+	if (instring == 2) {
+	    compquote = "\"";
+	    compquoting = "double";
+	} else if (instring == 1) {
+	    compquote = "'";
+	    compquoting = "single";
+	} else {
+	    compquote = compquoting = "";
+	    unset = set;
+	    set = 0;
+	}
+	compquote = ztrdup(compquote);
+	compquoting = ztrdup(compquoting);
+	comp_setunset(0, 0, set, unset);
+
+	if (unset(COMPLETEINWORD)) {
+	    untokenize(ns);
+	    zsfree(compprefix);
+	    compprefix = ztrdup(ns);
+	    zsfree(compsuffix);
+	    compsuffix = ztrdup("");
+	} else {
+	    char *ss, sav;
+	    
+	    ss = ns + soffs;
+
+	    sav = *ss;
+	    *ss = '\0';
+	    untokenize(ns);
+	    compprefix = ztrdup(ns);
+	    *ss = sav;
+	    untokenize(ss);
+	    compsuffix = ztrdup(ss);
+	}
+	zsfree(compiprefix);
+	compiprefix = ztrdup("");
+	zsfree(compisuffix);
+	compisuffix = ztrdup("");
+	zsfree(compqiprefix);
+	zsfree(compqisuffix);
+	if (ois) {
+	    compqiprefix = qp;
+	    compqisuffix = qs;
+	} else {
+	    compqiprefix = ztrdup(quotename(qp, NULL));
+	    zsfree(qp);
+	    compqisuffix = ztrdup(quotename(qs, NULL));
+	    zsfree(qs);
+	}
+	freearray(compwords);
+	i = countlinknodes(foo);
+	compwords = (char **) zalloc((i + 1) * sizeof(char *));
+	for (n = firstnode(foo), i = 0; n; incnode(n), i++) {
+	    p = compwords[i] = (char *) getdata(n);
+	    untokenize(p);
+	}
+	compcurrent = cur + 1;
+	compwords[i] = NULL;
+    }
+    autoq = oaq;
+    instring = ois;
+    inbackt = oib;
+
+    return 0;
+}
+
+/* This stores the strings from the list in an array. */
+
+/**/
+void
+set_list_array(char *name, LinkList l)
+{
+    char **a, **p;
+    LinkNode n;
+
+    a = (char **) zalloc((countlinknodes(l) + 1) * sizeof(char *));
+    for (p = a, n = firstnode(l); n; incnode(n))
+	*p++ = ztrdup((char *) getdata(n));
+    *p = NULL;
+
+    setaparam(name, a);
+}
+
+/* Get the words from a variable or a (list of words). */
+
+/**/
+char **
+get_user_var(char *nam)
+{
+    if (!nam)
+	return NULL;
+    else if (*nam == '(') {
+	/* It's a (...) list, not a parameter name. */
+	char *ptr, *s, **uarr, **aptr;
+	int count = 0, notempty = 0, brk = 0;
+	LinkList arrlist = newlinklist();
+
+	ptr = dupstring(nam);
+	s = ptr + 1;
+	while (*++ptr) {
+	    if (*ptr == '\\' && ptr[1])
+		chuck(ptr), notempty = 1;
+	    else if (*ptr == ',' || inblank(*ptr) || *ptr == ')') {
+		if (*ptr == ')')
+		    brk++;
+		if (notempty) {
+		    *ptr = '\0';
+		    count++;
+		    if (*s == '\n')
+			s++;
+		    addlinknode(arrlist, s);
+		}
+		s = ptr + 1;
+		notempty = 0;
+	    } else {
+		notempty = 1;
+		if (*ptr == Meta)
+		    ptr++;
+	    }
+	    if (brk)
+		break;
+	}
+	if (!brk || !count)
+	    return NULL;
+	*ptr = '\0';
+	aptr = uarr = (char **) zhalloc(sizeof(char *) * (count + 1));
+
+	while ((*aptr++ = (char *)ugetnode(arrlist)));
+	uarr[count] = NULL;
+	return uarr;
+    } else {
+	/* Otherwise it should be a parameter name. */
+	char **arr = NULL, *val;
+
+	if ((arr = getaparam(nam)) || (arr = gethparam(nam)))
+	    return (incompfunc ? arrdup(arr) : arr);
+
+	if ((val = getsparam(nam))) {
+	    arr = (char **) zhalloc(2*sizeof(char *));
+	    arr[0] = (incompfunc ? dupstring(val) : val);
+	    arr[1] = NULL;
+	}
+	return arr;
+    }
+}
+
+/* This is used by compadd to add a couple of matches. The arguments are
+ * the strings given via options. The last argument is the array with
+ * the matches. */
+
+/**/
+int
+addmatches(Cadata dat, char **argv)
+{
+    char *s, *ms, *lipre = NULL, *lisuf = NULL, *lpre = NULL, *lsuf = NULL;
+    char **aign = NULL, **dparr = NULL, oaq = autoq, *oppre = dat->ppre;
+    char *oqp = qipre, *oqs = qisuf, qc, **disp = NULL;
+    int lpl, lsl, pl, sl, bcp = 0, bcs = 0, bpadd = 0, bsadd = 0;
+    int llpl = 0, llsl = 0, nm = mnum, gflags = 0, ohp = haspattern;
+    int oisalt = 0, isalt, isexact, doadd, ois = instring, oib = inbackt;
+    Cline lc = NULL, pline = NULL, sline = NULL;
+    Cmatch cm;
+    struct cmlist mst;
+    Cmlist oms = mstack;
+    Patprog cp = NULL;
+    LinkList aparl = NULL, oparl = NULL, dparl = NULL;
+    Brinfo bp, bpl = brbeg, obpl, bsl = brend, obsl;
+
+    for (bp = brbeg; bp; bp = bp->next)
+	bp->curpos = ((dat->aflags & CAF_QUOTE) ? bp->pos : bp->qpos);
+    for (bp = brend; bp; bp = bp->next)
+	bp->curpos = ((dat->aflags & CAF_QUOTE) ? bp->pos : bp->qpos);
+
+    if (dat->flags & CMF_ISPAR)
+	dat->flags |= parflags;
+    if (compquote && (qc = *compquote)) {
+	if (qc == '`') {
+	    instring = 0;
+	    inbackt = 0;
+	    autoq = '\0';
+	} else {
+	    instring = (qc == '\'' ? 1 : 2);
+	    inbackt = 0;
+	    autoq = qc;
+	}
+    } else {
+	instring = inbackt = 0;
+	autoq = '\0';
+    }
+    qipre = ztrdup(compqiprefix ? compqiprefix : "");
+    qisuf = ztrdup(compqisuffix ? compqisuffix : "");
+
+    /* Switch back to the heap that was used when the completion widget
+     * was invoked. */
+    SWITCHHEAPS(compheap) {
+	HEAPALLOC {
+	    if ((doadd = (!dat->apar && !dat->opar && !dat->dpar)) &&
+		(dat->aflags & CAF_MATCH))
+		hasmatched = 1;
+	    if (dat->apar)
+		aparl = newlinklist();
+	    if (dat->opar)
+		oparl = newlinklist();
+	    if (dat->dpar) {
+		if (*(dat->dpar) == '(')
+		    dparr = NULL;
+		else if ((dparr = get_user_var(dat->dpar)) && !*dparr)
+		    dparr = NULL;
+		dparl = newlinklist();
+	    }
+	    if (dat->exp) {
+		curexpl = (Cexpl) zhalloc(sizeof(struct cexpl));
+		curexpl->count = curexpl->fcount = 0;
+		curexpl->str = dupstring(dat->exp);
+	    } else
+		curexpl = NULL;
+
+	    /* Store the matcher in our stack of matchers. */
+	    if (dat->match) {
+		mst.next = mstack;
+		mst.matcher = dat->match;
+		mstack = &mst;
+
+		if (!mnum)
+		    add_bmatchers(dat->match);
+
+		addlinknode(matchers, dat->match);
+		dat->match->refc++;
+	    }
+	    if (mnum && (mstack || bmatchers))
+		update_bmatchers();
+
+	    /* Get the suffixes to ignore. */
+	    if (dat->ign)
+		aign = get_user_var(dat->ign);
+	    /* Get the display strings. */
+	    if (dat->disp)
+		if ((disp = get_user_var(dat->disp)))
+		    disp--;
+	    /* Get the contents of the completion variables if we have
+	     * to perform matching. */
+	    if (dat->aflags & CAF_MATCH) {
+		lipre = dupstring(compiprefix);
+		lisuf = dupstring(compisuffix);
+		lpre = dupstring(compprefix);
+		lsuf = dupstring(compsuffix);
+		llpl = strlen(lpre);
+		llsl = strlen(lsuf);
+		/* Test if there is an existing -P prefix. */
+		if (dat->pre && *dat->pre) {
+		    char *dp = rembslash(dat->pre);
+
+		    pl = pfxlen(dp, lpre);
+		    llpl -= pl;
+		    lpre += pl;
+		}
+	    }
+	    /* Now duplicate the strings we have from the command line. */
+	    if (dat->ipre)
+		dat->ipre = (lipre ? dyncat(lipre, dat->ipre) :
+			     dupstring(dat->ipre));
+	    else if (lipre)
+		dat->ipre = lipre;
+	    if (dat->isuf)
+		dat->isuf = (lisuf ? dyncat(lisuf, dat->isuf) :
+			     dupstring(dat->isuf));
+	    else if (lisuf)
+		dat->isuf = lisuf;
+	    if (dat->ppre) {
+		if (!(dat->aflags & CAF_QUOTE)) {
+		    dat->ppre = quotename(dat->ppre, NULL);
+		    if ((dat->flags & CMF_FILE) &&
+			dat->ppre[0] == '\\' && dat->ppre[1] == '~')
+			chuck(dat->ppre);
+		} else
+		    dat->ppre = dupstring(dat->ppre);
+		lpl = strlen(dat->ppre);
+	    } else
+		lpl = 0;
+	    if (dat->psuf) {
+		if (!(dat->aflags & CAF_QUOTE))
+		    dat->psuf = quotename(dat->psuf, NULL);
+		else
+		    dat->psuf = dupstring(dat->psuf);
+		lsl = strlen(dat->psuf);
+	    } else
+		lsl = 0;
+	    if (dat->aflags & CAF_MATCH) {
+		int ml;
+
+		s = dat->ppre ? dat->ppre : "";
+		if ((ml = match_str(lpre, s, &bpl, 0, NULL, 0, 0, 1)) >= 0) {
+		    if (matchsubs) {
+			Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, 0);
+
+			tmp->prefix = matchsubs;
+			if (matchlastpart)
+			    matchlastpart->next = tmp;
+			else
+			    matchparts = tmp;
+		    }
+		    pline = matchparts;
+		    lpre += ml;
+		    bcp = ml;
+		    bpadd = strlen(s) - ml;
+		} else {
+		    if (llpl <= lpl && strpfx(lpre, s))
+			lpre = "";
+		    else if (llpl > lpl && strpfx(s, lpre))
+			lpre += lpl;
+		    else
+			*argv = NULL;
+		    bcp = lpl;
+		}
+
+		s = dat->psuf ? dat->psuf : "";
+		if ((ml = match_str(lsuf, s, &bsl, 0, NULL, 1, 0, 1)) >= 0) {
+		    if (matchsubs) {
+			Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, CLF_SUF);
+
+			tmp->suffix = matchsubs;
+			if (matchlastpart)
+			    matchlastpart->next = tmp;
+			else
+			    matchparts = tmp;
+		    }
+		    sline = revert_cline(matchparts);
+		    lsuf[llsl - ml] = '\0';
+		    bcs = ml;
+		    bsadd = strlen(s) - ml;
+		} else {
+		    if (llsl <= lsl && strsfx(lsuf, s))
+			lsuf = "";
+		    else if (llsl > lsl && strsfx(s, lsuf))
+			lsuf[llsl - lsl] = '\0';
+		    else
+			*argv = NULL;
+		    bcs = lsl;
+		}
+		if (comppatmatch && *comppatmatch) {
+		    int is = (*comppatmatch == '*');
+		    char *tmp = (char *) zhalloc(2 + llpl + llsl);
+
+		    strcpy(tmp, lpre);
+		    tmp[llpl] = 'x';
+		    strcpy(tmp + llpl + is, lsuf);
+
+		    tokenize(tmp);
+		    remnulargs(tmp);
+		    if (haswilds(tmp)) {
+			if (is)
+			    tmp[llpl] = Star;
+			if ((cp = patcompile(tmp, 0, NULL)))
+			    haspattern = 1;
+		    }
+		}
+	    }
+	    if (*argv) {
+		if (dat->pre)
+		    dat->pre = dupstring(dat->pre);
+		if (dat->suf)
+		    dat->suf = dupstring(dat->suf);
+		if (!dat->prpre && (dat->prpre = oppre)) {
+		    singsub(&(dat->prpre));
+		    untokenize(dat->prpre);
+		} else
+		    dat->prpre = dupstring(dat->prpre);
+		/* Select the group in which to store the matches. */
+		gflags = (((dat->aflags & CAF_NOSORT ) ? CGF_NOSORT  : 0) |
+			  ((dat->aflags & CAF_UNIQALL) ? CGF_UNIQALL : 0) |
+			  ((dat->aflags & CAF_UNIQCON) ? CGF_UNIQCON : 0));
+		if (dat->group) {
+		    endcmgroup(NULL);
+		    begcmgroup(dat->group, gflags);
+		} else {
+		    endcmgroup(NULL);
+		    begcmgroup("default", 0);
+		}
+		/* Select the set of matches. */
+		oisalt = (dat->aflags & CAF_ALT);
+
+		if (dat->remf) {
+		    dat->remf = dupstring(dat->remf);
+		    dat->rems = NULL;
+		} else if (dat->rems)
+		    dat->rems = dupstring(dat->rems);
+	    }
+	    /* Walk through the matches given. */
+	    obpl = bpl;
+	    obsl = bsl;
+	    for (; (s = *argv); argv++) {
+		bpl = obpl;
+		bsl = obsl;
+		if (disp) {
+		    if (!*++disp)
+			disp = NULL;
+		}
+		sl = strlen(s);
+		isalt = oisalt;
+		if ((!dat->psuf || !*(dat->psuf)) && aign) {
+		    /* Do the suffix-test. If the match has one of the
+		     * suffixes from ign, we put it in the alternate set. */
+		    char **pt = aign;
+		    int filell;
+
+		    for (isalt = 0; !isalt && *pt; pt++)
+			if ((filell = strlen(*pt)) < sl
+			    && !strcmp(*pt, s + sl - filell))
+			    isalt = 1;
+
+		    if (isalt && !doadd) {
+			if (dparr && !*++dparr)
+			    dparr = NULL;
+			continue;
+		    }
+		}
+		if (!(dat->aflags & CAF_MATCH)) {
+		    if (dat->aflags & CAF_QUOTE)
+			ms = dupstring(s);
+		    else
+			sl = strlen(ms = quotename(s, NULL));
+		    lc = bld_parts(ms, sl, -1, NULL);
+		    isexact = 0;
+		} else if (!(ms = comp_match(lpre, lsuf, s, cp, &lc,
+					     (!(dat->aflags & CAF_QUOTE) ?
+					      ((dat->ppre && dat->ppre) ||
+					       !(dat->flags & CMF_FILE) ? 1 : 2) : 0),
+					     &bpl, bcp, &bsl, bcs,
+					     &isexact))) {
+		    if (dparr && !*++dparr)
+			dparr = NULL;
+		    continue;
+		}
+		if (doadd) {
+		    Brinfo bp;
+
+		    for (bp = obpl; bp; bp = bp->next)
+			bp->curpos += bpadd;
+		    for (bp = obsl; bp; bp = bp->next)
+			bp->curpos += bsadd;
+
+		    if ((cm = add_match_data(isalt, ms, lc, dat->ipre, NULL,
+					     dat->isuf, dat->pre, dat->prpre,
+					     dat->ppre, pline,
+					     dat->psuf, sline,
+					     dat->suf, dat->flags, isexact))) {
+			cm->rems = dat->rems;
+			cm->remf = dat->remf;
+			if (disp)
+			    cm->disp = dupstring(*disp);
+		    }
+		} else {
+		    if (dat->apar)
+			addlinknode(aparl, ms);
+		    if (dat->opar)
+			addlinknode(oparl, s);
+		    if (dat->dpar && dparr) {
+			addlinknode(dparl, *dparr);
+			if (!*++dparr)
+			    dparr = NULL;
+		    }
+		    free_cline(lc);
+		}
+	    }
+	    if (dat->apar)
+		set_list_array(dat->apar, aparl);
+	    if (dat->opar)
+		set_list_array(dat->opar, oparl);
+	    if (dat->dpar)
+		set_list_array(dat->dpar, dparl);
+	    if (dat->exp)
+		addexpl();
+	} LASTALLOC;
+    } SWITCHBACKHEAPS;
+
+    /* We switched back to the current heap, now restore the stack of
+     * matchers. */
+    mstack = oms;
+
+    instring = ois;
+    inbackt = oib;
+    autoq = oaq;
+    zsfree(qipre);
+    zsfree(qisuf);
+    qipre = oqp;
+    qisuf = oqs;
+
+    if (mnum == nm)
+	haspattern = ohp;
+
+    return (mnum == nm);
+}
+
+/* This adds all the data we have for a match. */
+
+/**/
+Cmatch
+add_match_data(int alt, char *str, Cline line,
+	       char *ipre, char *ripre, char *isuf,
+	       char *pre, char *prpre,
+	       char *ppre, Cline pline,
+	       char *psuf, Cline sline,
+	       char *suf, int flags, int exact)
+{
+    Cmatch cm;
+    Aminfo ai = (alt ? fainfo : ainfo);
+    int palen, salen, qipl, ipl, pl, ppl, qisl, isl, psl;
+    int sl, lpl, lsl, ml;
+
+    palen = salen = qipl = ipl = pl = ppl = qisl = isl = psl = 0;
+
+    DPUTS(!line, "BUG: add_match_data() without cline");
+
+    cline_matched(line);
+    if (pline)
+	cline_matched(pline);
+    if (sline)
+	cline_matched(sline);
+
+    /* If there is a path suffix, we build a cline list for it and
+     * append it to the list for the match itself. */
+    if (!sline && psuf)
+	salen = (psl = strlen(psuf));
+    if (isuf)
+	salen += (isl = strlen(isuf));
+    if (qisuf)
+	salen += (qisl = strlen(qisuf));
+
+    if (salen) {
+	char *asuf = (char *) zhalloc(salen);
+	Cline pp, p, s, sl = NULL;
+	
+
+	if (psl)
+	    memcpy(asuf, psuf, psl);
+	if (isl)
+	    memcpy(asuf + psl, isuf, isl);
+	if (qisl)
+	    memcpy(asuf + psl + isl, qisuf, qisl);
+
+	for (pp = NULL, p = line; p->next; pp = p, p = p->next);
+
+	if (salen > qisl) {
+	    s = bld_parts(asuf, salen - qisl, salen - qisl, &sl);
+
+	    if (sline) {
+		Cline sp;
+
+		sline = cp_cline(sline, 1);
+
+		for (sp = sline; sp->next; sp = sp->next);
+		sp->next = s;
+		s = sline;
+	    }
+	    if (!(p->flags & (CLF_SUF | CLF_MID)) &&
+		!p->llen && !p->wlen && !p->olen) {
+		if (p->prefix) {
+		    Cline q;
+
+		    for (q = p->prefix; q->next; q = q->next);
+		    q->next = s->prefix;
+		    s->prefix = p->prefix;
+		    p->prefix = NULL;
+		}
+		s->flags |= (p->flags & CLF_MATCHED);
+		free_cline(p);
+		if (pp)
+		    pp->next = s;
+		else
+		    line = s;
+	    } else
+		p->next = s;
+	}
+	if (qisl) {
+	    Cline qsl = bld_parts(asuf + psl + isl, qisl, qisl, NULL);
+
+	    qsl->flags |= CLF_SUF;
+	    qsl->suffix = qsl->prefix;
+	    qsl->prefix = NULL;
+	    if (sl)
+		sl->next = qsl;
+	    else if (sline) {
+		Cline sp;
+
+		sline = cp_cline(sline, 1);
+
+		for (sp = sline; sp->next; sp = sp->next);
+		sp->next = qsl;
+		p->next = sline;
+	    } else
+		p->next = qsl;
+	}
+    } else if (sline) {
+	Cline p;
+
+	for (p = line; p->next; p = p->next);
+	p->next = cp_cline(sline, 1);
+    }
+    /* The prefix is handled differently because the completion code
+     * is much more eager to insert the -P prefix than it is to insert
+     * the -S suffix. */
+    if (qipre)
+	palen = (qipl = strlen(qipre));
+    if (ipre)
+	palen += (ipl = strlen(ipre));
+    if (pre)
+	palen += (pl = strlen(pre));
+    if (!pline && ppre)
+	palen += (ppl = strlen(ppre));
+
+    if (pl) {
+	if (ppl || pline) {
+	    Cline lp, p;
+
+	    if (pline)
+		for (p = cp_cline(pline, 1), lp = p; lp->next; lp = lp->next);
+	    else
+		p = bld_parts(ppre, ppl, ppl, &lp);
+
+	    if (lp->prefix && !(line->flags & (CLF_SUF | CLF_MID)) &&
+		!p->llen && !p->wlen && !p->olen) {
+		Cline lpp;
+
+		for (lpp = lp->prefix; lpp->next; lpp = lpp->next);
+
+		lpp->next = line->prefix;
+		line->prefix = lp->prefix;
+		lp->prefix = NULL;
+
+		free_cline(lp);
+
+		if (p != lp) {
+		    Cline q;
+
+		    for (q = p; q->next != lp; q = q->next);
+
+		    q->next = line;
+		    line = p;
+		}
+	    } else {
+		lp->next = line;
+		line = p;
+	    }
+	}
+	if (pl) {
+	    Cline lp, p = bld_parts(pre, pl, pl, &lp);
+
+	    lp->next = line;
+	    line = p;
+	}
+	if (ipl) {
+	    Cline lp, p = bld_parts(ipre, ipl, ipl, &lp);
+
+	    lp->next = line;
+	    line = p;
+	}
+	if (qipl) {
+	    Cline lp, p = bld_parts(qipre, qipl, qipl, &lp);
+
+	    lp->next = line;
+	    line = p;
+	}
+    } else if (palen || pline) {
+	Cline p, lp;
+
+	if (palen) {
+	    char *apre = (char *) zhalloc(palen);
+
+	    if (qipl)
+		memcpy(apre, qipre, qipl);
+	    if (ipl)
+		memcpy(apre + qipl, ipre, ipl);
+	    if (pl)
+		memcpy(apre + qipl + ipl, pre, pl);
+	    if (ppl)
+		memcpy(apre + qipl + ipl + pl, ppre, ppl);
+
+	    p = bld_parts(apre, palen, palen, &lp);
+
+	    if (pline)
+		for (lp->next = cp_cline(pline, 1); lp->next; lp = lp->next);
+	} else
+	    for (p = lp = cp_cline(pline, 1); lp->next; lp = lp->next);
+
+	if (lp->prefix && !(line->flags & (CLF_SUF | CLF_MID)) &&
+	    !p->llen && !p->wlen && !p->olen) {
+	    Cline lpp;
+
+	    for (lpp = lp->prefix; lpp->next; lpp = lpp->next);
+
+	    lpp->next = line->prefix;
+	    line->prefix = lp->prefix;
+	    lp->prefix = NULL;
+
+	    free_cline(lp);
+
+	    if (p != lp) {
+		Cline q;
+
+		for (q = p; q->next != lp; q = q->next);
+
+		q->next = line;
+		line = p;
+	    }
+	} else {
+	    lp->next = line;
+	    line = p;
+	}
+    }
+    /* Allocate and fill the match structure. */
+    cm = (Cmatch) zhalloc(sizeof(struct cmatch));
+    cm->str = str;
+    cm->ppre = (ppre && *ppre ? ppre : NULL);
+    cm->psuf = (psuf && *psuf ? psuf : NULL);
+    cm->prpre = ((flags & CMF_FILE) && prpre && *prpre ? prpre : NULL);
+    if (qipre && *qipre)
+	cm->ipre = (ipre && *ipre ? dyncat(qipre, ipre) : dupstring(qipre));
+    else
+	cm->ipre = (ipre && *ipre ? ipre : NULL);
+    cm->ripre = (ripre && *ripre ? ripre : NULL);
+    if (qisuf && *qisuf)
+	cm->isuf = (isuf && *isuf ? dyncat(isuf, qisuf) : dupstring(qisuf));
+    else
+	cm->isuf = (isuf && *isuf ? isuf : NULL);
+    cm->pre = pre;
+    cm->suf = suf;
+    cm->flags = flags;
+    if (nbrbeg) {
+	int *p;
+	Brinfo bp;
+
+	cm->brpl = (int *) zhalloc(nbrbeg * sizeof(int));
+
+	for (p = cm->brpl, bp = brbeg; bp; p++, bp = bp->next)
+	    *p = bp->curpos;
+    } else
+	cm->brpl = NULL;
+    if (nbrend) {
+	int *p;
+	Brinfo bp;
+
+	cm->brsl = (int *) zhalloc(nbrend * sizeof(int));
+
+	for (p = cm->brsl, bp = brend; bp; p++, bp = bp->next)
+	    *p = bp->curpos;
+    } else
+	cm->brsl = NULL;
+    cm->qipl = qipl;
+    cm->qisl = qisl;
+    cm->autoq = (autoq ? autoq : (inbackt ? '`' : '\0'));
+    cm->rems = cm->remf = cm->disp = NULL;
+
+    if ((lastprebr || lastpostbr) && !hasbrpsfx(cm, lastprebr, lastpostbr))
+	return NULL;
+
+    /* Then build the unambiguous cline list. */
+    ai->line = join_clines(ai->line, line);
+
+    mnum++;
+    ai->count++;
+
+    addlinknode((alt ? fmatches : matches), cm);
+
+    newmatches = 1;
+
+    /* One more match for this explanation. */
+    if (curexpl) {
+	if (alt)
+	    curexpl->fcount++;
+	else
+	    curexpl->count++;
+    }
+    if (!ai->firstm)
+	ai->firstm = cm;
+
+    sl = strlen(str);
+    lpl = (cm->ppre ? strlen(cm->ppre) : 0);
+    lsl = (cm->psuf ? strlen(cm->psuf) : 0);
+    ml = sl + lpl + lsl;
+
+    if (ml < minmlen)
+	minmlen = ml;
+    if (ml > maxmlen)
+	maxmlen = ml;
+
+    /* Do we have an exact match? More than one? */
+    if (exact) {
+	if (!ai->exact) {
+	    ai->exact = 1;
+	    if (incompfunc) {
+		/* If a completion widget is active, we make the exact
+		 * string available in `compstate'. */
+
+		char *e;
+
+		zsfree(compexactstr);
+		compexactstr = e = (char *) zalloc(ml + 1);
+		if (cm->ppre) {
+		    strcpy(e, cm->ppre);
+		    e += lpl;
+		}
+		strcpy(e, str);
+		e += sl;
+		if (cm->psuf)
+		    strcpy(e, cm->psuf);
+		comp_setunset(0, 0, CP_EXACTSTR, 0);
+	    }
+	    ai->exactm = cm;
+	} else {
+	    ai->exact = 2;
+	    ai->exactm = NULL;
+	    if (incompfunc)
+		comp_setunset(0, 0, 0, CP_EXACTSTR);
+	}
+    }
+    return cm;
+}
+
+/* This begins a new group of matches. */
+
+/**/
+void
+begcmgroup(char *n, int flags)
+{
+    if (n) {
+	Cmgroup p = amatches;
+
+	while (p) {
+	    if (p->name &&
+		flags == (p->flags & (CGF_NOSORT|CGF_UNIQALL|CGF_UNIQCON)) &&
+		!strcmp(n, p->name)) {
+		mgroup = p;
+
+		expls = p->lexpls;
+		matches = p->lmatches;
+		fmatches = p->lfmatches;
+		allccs = p->lallccs;
+
+		return;
+	    }
+	    p = p->next;
+	}
+    }
+    mgroup = (Cmgroup) zhalloc(sizeof(struct cmgroup));
+    mgroup->name = dupstring(n);
+    mgroup->lcount = mgroup->llcount = mgroup->mcount = 0;
+    mgroup->flags = flags;
+    mgroup->matches = NULL;
+    mgroup->ylist = NULL;
+    mgroup->expls = NULL;
+
+    mgroup->lexpls = expls = newlinklist();
+    mgroup->lmatches = matches = newlinklist();
+    mgroup->lfmatches = fmatches = newlinklist();
+
+    mgroup->lallccs = allccs = ((flags & CGF_NOSORT) ? NULL : newlinklist());
+
+    mgroup->next = amatches;
+    amatches = mgroup;
+}
+
+/* End the current group for now. */
+
+/**/
+void
+endcmgroup(char **ylist)
+{
+    mgroup->ylist = ylist;
+}
+
+/* Add an explanation string to the current group, joining duplicates. */
+
+/**/
+void
+addexpl(void)
+{
+    LinkNode n;
+    Cexpl e;
+
+    for (n = firstnode(expls); n; incnode(n)) {
+	e = (Cexpl) getdata(n);
+	if (!strcmp(curexpl->str, e->str)) {
+	    e->count += curexpl->count;
+	    e->fcount += curexpl->fcount;
+
+	    return;
+	}
+    }
+    addlinknode(expls, curexpl);
+    newmatches = 1;
+}
+
+/* The comparison function for matches (used for sorting). */
+
+/**/
+static int
+matchcmp(Cmatch *a, Cmatch *b)
+{
+    if ((*a)->disp) {
+	if ((*b)->disp) {
+	    if ((*a)->flags & CMF_DISPLINE) {
+		if ((*b)->flags & CMF_DISPLINE)
+		    return strcmp((*a)->disp, (*b)->disp);
+		else
+		    return -1;
+	    } else {
+		if ((*b)->flags & CMF_DISPLINE)
+		    return 1;
+		else
+		    return strcmp((*a)->disp, (*b)->disp);
+	    }
+	}
+	return -1;
+    }
+    if ((*b)->disp)
+	return 1;
+
+    return strbpcmp(&((*a)->str), &((*b)->str));
+}
+
+/* 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->psuf, b->psuf) &&
+	matchstreq(a->suf, b->suf) &&
+	((!a->disp && !b->disp && matchstreq(a->str, b->str)) ||
+	 (a->disp && b->disp && !strcmp(a->disp, b->disp) &&
+	  matchstreq(a->str, b->str)));
+}
+
+/* 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 Cmatch *
+makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp)
+{
+    Cmatch *ap, *bp, *cp, *rp;
+    LinkNode nod;
+    int n, nl = 0, ll = 0;
+
+    /* Build an array for the matches. */
+    rp = ap = (Cmatch *) ncalloc(((n = countlinknodes(l)) + 1) *
+				 sizeof(Cmatch));
+
+    /* And copy them into it. */
+    for (nod = firstnode(l); nod; incnode(nod))
+	*ap++ = (Cmatch) getdata(nod);
+    *ap = NULL;
+
+    if (!type) {
+	if (flags) {
+	    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 (!(flags & CGF_NOSORT)) {
+	    /* Now sort the array (it contains matches). */
+	    qsort((void *) rp, n, sizeof(Cmatch),
+		  (int (*) _((const void *, const void *)))matchcmp);
+
+	    if (!(flags & CGF_UNIQCON)) {
+		/* 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] && !(*ap)->disp && !(bp[1])->disp &&
+			     !strcmp((*ap)->str, (bp[1])->str); bp++)
+			(bp[1])->flags |= CMF_NOLIST;
+		}
+		*cp = NULL;
+	    }
+	    for (ap = rp; *ap; ap++) {
+		if ((*ap)->disp && ((*ap)->flags & CMF_DISPLINE))
+		    ll++;
+		if ((*ap)->flags & CMF_NOLIST)
+		    nl++;
+	    }
+	} else {
+	    if (!(flags & CGF_UNIQALL) && !(flags & CGF_UNIQCON)) {
+		for (ap = rp; *ap; ap++) {
+		    for (bp = cp = ap + 1; *bp; bp++) {
+			if (!matcheq(*ap, *bp))
+			    *cp++ = *bp;
+			else
+			    n--;
+		    }
+		    *cp = NULL;
+		}
+	    } else if (!(flags & CGF_UNIQCON)) {
+		for (ap = cp = rp; *ap; ap++) {
+		    *cp++ = *ap;
+		    for (bp = ap; bp[1] && matcheq(*ap, bp[1]); bp++, n--);
+		    ap = bp;
+		    for (; bp[1] && !(*ap)->disp && !(bp[1])->disp &&
+			     !strcmp((*ap)->str, (bp[1])->str); bp++)
+			(bp[1])->flags |= CMF_NOLIST;
+		}
+		*cp = NULL;
+	    }
+	    for (ap = rp; *ap; ap++) {
+		if ((*ap)->disp && ((*ap)->flags & CMF_DISPLINE))
+		    ll++;
+		if ((*ap)->flags & CMF_NOLIST)
+		    nl++;
+	    }
+	}
+    }
+    if (np)
+	*np = n;
+    if (nlp)
+	*nlp = nl;
+    if (llp)
+	*llp = ll;
+    return rp;
+}
+
+/* This duplicates one match. */
+
+/**/
+static Cmatch
+dupmatch(Cmatch m, int nbeg, int nend)
+{
+    Cmatch r;
+
+    r = (Cmatch) ncalloc(sizeof(struct cmatch));
+
+    r->str = ztrdup(m->str);
+    r->ipre = ztrdup(m->ipre);
+    r->ripre = ztrdup(m->ripre);
+    r->isuf = ztrdup(m->isuf);
+    r->ppre = ztrdup(m->ppre);
+    r->psuf = ztrdup(m->psuf);
+    r->prpre = ztrdup(m->prpre);
+    r->pre = ztrdup(m->pre);
+    r->suf = ztrdup(m->suf);
+    r->flags = m->flags;
+    if (nbeg) {
+	int *p, *q, i;
+
+	r->brpl = (int *) zalloc(nbeg * sizeof(int));
+
+	for (p = r->brpl, q = m->brpl, i = nbeg; i--; p++, q++)
+	    *p = *q;
+    } else
+	r->brpl = NULL;
+    if (nend) {
+	int *p, *q, i;
+
+	r->brsl = (int *) zalloc(nend * sizeof(int));
+
+	for (p = r->brsl, q = m->brsl, i = nend; i--; p++, q++)
+	    *p = *q;
+    } else
+	r->brsl = NULL;
+    r->rems = ztrdup(m->rems);
+    r->remf = ztrdup(m->remf);
+    r->autoq = m->autoq;
+    r->qipl = m->qipl;
+    r->qisl = m->qisl;
+    r->disp = dupstring(m->disp);
+
+    return r;
+}
+
+/* This duplicates all groups of matches. */
+
+/**/
+int
+permmatches(int last)
+{
+    Cmgroup g = amatches, n;
+    Cmatch *p, *q;
+    Cexpl *ep, *eq, e, o;
+    LinkList mlist;
+    static int fi = 0;
+    int nn, nl, ll, gn = 1, mn = 1, rn;
+
+    if (pmatches && !newmatches)
+	return fi;
+
+    newmatches = fi = 0;
+
+    if (pmatches)
+	freematches(pmatches);
+
+    pmatches = lmatches = NULL;
+    nmatches = smatches = 0;
+
+    if (!ainfo->count) {
+	if (last)
+	    ainfo = fainfo;
+	fi = 1;
+    }
+    while (g) {
+	HEAPALLOC {
+	    if (empty(g->lmatches))
+		/* We have no matches, try ignoring fignore. */
+		mlist = g->lfmatches;
+	    else
+		mlist = g->lmatches;
+
+	    g->matches = makearray(mlist, 1, g->flags, &nn, &nl, &ll);
+	    g->mcount = nn;
+	    if ((g->lcount = nn - nl) < 0)
+		g->lcount = 0;
+	    g->llcount = ll;
+	    if (g->ylist) {
+		g->lcount = arrlen(g->ylist);
+		smatches = 2;
+	    }
+	    g->expls = (Cexpl *) makearray(g->lexpls, 0, 0, &(g->ecount),
+					   NULL, NULL);
+
+	    g->ccount = 0;
+	} LASTALLOC;
+
+	nmatches += g->mcount;
+	smatches += g->lcount;
+
+	n = (Cmgroup) ncalloc(sizeof(struct cmgroup));
+
+	if (!lmatches)
+	    lmatches = n;
+	if (pmatches)
+	    pmatches->prev = n;
+	n->next = pmatches;
+	pmatches = n;
+	n->prev = 0;
+	n->num = gn++;
+
+	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, nbrbeg, nbrend);
+	*p = NULL;
+
+	n->lcount = g->lcount;
+	n->llcount = g->llcount;
+	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;
+
+	n->widths = NULL;
+
+	g = g->next;
+    }
+    for (g = pmatches; g; g = g->next) {
+	g->nbrbeg = nbrbeg;
+	g->nbrend = nbrend;
+	for (rn = 1, q = g->matches; *q; q++) {
+	    (*q)->rnum = rn++;
+	    (*q)->gnum = mn++;
+	}
+    }
+    hasperm = 1;
+    permmnum = mn - 1;
+    permgnum = gn - 1;
+    listdat.valid = 0;
+
+    return fi;
+}
+
+/* This frees one match. */
+
+/**/
+static void
+freematch(Cmatch m, int nbeg, int nend)
+{
+    if (!m) return;
+
+    zsfree(m->str);
+    zsfree(m->ipre);
+    zsfree(m->ripre);
+    zsfree(m->isuf);
+    zsfree(m->ppre);
+    zsfree(m->psuf);
+    zsfree(m->pre);
+    zsfree(m->suf);
+    zsfree(m->prpre);
+    zsfree(m->rems);
+    zsfree(m->remf);
+    zsfree(m->disp);
+    zfree(m->brpl, nbeg * sizeof(int));
+    zfree(m->brsl, nend * sizeof(int));
+
+    zfree(m, sizeof(m));
+}
+
+/* This frees the groups of matches. */
+
+/**/
+void
+freematches(Cmgroup g)
+{
+    Cmgroup n;
+    Cmatch *m;
+    Cexpl *e;
+
+    while (g) {
+	n = g->next;
+	
+	for (m = g->matches; *m; m++)
+	    freematch(*m, g->nbrbeg, g->nbrend);
+
+	if (g->ylist)
+	    freearray(g->ylist);
+
+	if ((e = g->expls)) {
+	    while (*e) {
+		zsfree((*e)->str);
+		free(*e);
+		e++;
+	    }
+	    free(g->expls);
+	}
+	free(g);
+
+	g = n;
+    }
+}
diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c
index 283b8de62..b4d32a34c 100644
--- a/Src/Zle/complete.c
+++ b/Src/Zle/complete.c
@@ -1,5 +1,5 @@
 /*
- * complete.c - the complete module
+ * complete.c - the complete module, interface part
  *
  * This file is part of zsh, the Z shell.
  *
@@ -28,10 +28,55 @@
  */
 
 #include "complete.mdh"
-#include "complete.pro"
 #define GLOBAL_PROTOTYPES
 #include "zle_tricky.pro"
 #undef GLOBAL_PROTOTYPES
+#include "complete.pro"
+
+/* Global matcher. */
+
+/**/
+Cmlist cmatcher;
+
+/* global variables for shell parameters in new style completion */
+
+/**/
+zlong compcurrent,
+      compmatcher,
+      compmatchertot,
+      complistmax,
+      complistlines;
+
+/**/
+char **compwords,
+     *compprefix,
+     *compsuffix,
+     *compiprefix,
+     *compisuffix,
+     *compqiprefix,
+     *compqisuffix,
+     *compmatcherstr,
+     *compcontext,
+     *compparameter,
+     *compredirect,
+     *compquote,
+     *compquoting,
+     *comprestore,
+     *complist,
+     *compforcelist,
+     *compinsert,
+     *compexact,
+     *compexactstr,
+     *comppatmatch,
+     *comppatinsert,
+     *complastprompt,
+     *comptoend,
+     *compoldlist,
+     *compoldins,
+     *compvared;
+
+/**/
+Param *comprpms, *compkpms;
 
 /**/
 void
@@ -88,6 +133,7 @@ freecpattern(Cpattern p)
 
 /* Copy a list of completion matchers. */
 
+/**/
 static Cmlist
 cpcmlist(Cmlist l)
 {
@@ -399,7 +445,7 @@ bin_compadd(char *name, char **argv, char *ops, int func)
     }
     dat.ipre = dat.isuf = dat.ppre = dat.psuf = dat.prpre =
 	dat.pre = dat.suf = dat.group = dat.rems = dat.remf = dat.disp = 
-	dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = dat.ylist = NULL;
+	dat.ign = dat.exp = dat.apar = dat.opar = dat.dpar = NULL;
     dat.match = NULL;
     dat.flags = 0;
     dat.aflags = CAF_MATCH;
@@ -462,10 +508,6 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 		if (!(dat.aflags & CAF_UNIQALL))
 		    dat.aflags |= CAF_UNIQCON;
 		break;
-	    case 'y':
-		sp = &(dat.ylist);
-		e = "string expected after -%c";
-		break;
 	    case 'i':
 		sp = &(dat.ipre);
 		e = "string expected after -%c";
@@ -644,6 +686,7 @@ restrict_range(int b, int e)
     }
 }
 
+/**/
 static int
 do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod)
 {
@@ -1284,12 +1327,36 @@ static struct paramdef patab[] = {
     PARAMDEF("compmatchers", PM_ARRAY|PM_SPECIAL, NULL, cmsetfn, cmgetfn, cmunsetfn)
 };
 
+/* The order of the entries in this table has to match the *HOOK
+ * macros in comp.h */
+
+/**/
+struct hookdef comphooks[] = {
+    HOOKDEF("insert_match", NULL, HOOKF_ALL),
+    HOOKDEF("menu_start", NULL, HOOKF_ALL),
+    HOOKDEF("compctl_make", NULL, 0),
+    HOOKDEF("compctl_before", NULL, 0),
+    HOOKDEF("compctl_after", NULL, 0),
+    HOOKDEF("comp_list_matches", ilistmatches, 0),
+};
+
 /**/
 int
 setup_complete(Module m)
 {
-    makecompparamsptr = makecompparams;
-    comp_setunsetptr = comp_setunset;
+    hasperm = 0;
+
+    comprpms = compkpms = NULL;
+    compwords = NULL;
+    compprefix = compsuffix = compiprefix = compisuffix = 
+	compqiprefix = compqisuffix = compmatcherstr = 
+	compcontext = compparameter = compredirect = compquote =
+	compquoting = comprestore = complist = compinsert =
+	compexact = compexactstr = comppatmatch = comppatinsert =
+	compforcelist = complastprompt = comptoend = 
+	compoldlist = compoldins = compvared = NULL;
+
+    hascompmod = 1;
 
     return 0;
 }
@@ -1298,6 +1365,14 @@ setup_complete(Module m)
 int
 boot_complete(Module m)
 {
+    addhookfunc("complete", (Hookfn) do_completion);
+    addhookfunc("before_complete", (Hookfn) before_complete);
+    addhookfunc("after_complete", (Hookfn) after_complete);
+    addhookfunc("accept_completion", (Hookfn) accept_last);
+    addhookfunc("reverse_menu", (Hookfn) reverse_menu);
+    addhookfunc("list_matches", (Hookfn) list_matches);
+    addhookfunc("invalidate_list", (Hookfn) invalidate_list);
+    addhookdefs(m->nam, comphooks, sizeof(comphooks)/sizeof(*comphooks));
     if (!(addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) |
 	  addconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab)) |
 	  addparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab)) |
@@ -1312,6 +1387,14 @@ boot_complete(Module m)
 int
 cleanup_complete(Module m)
 {
+    deletehookfunc("complete", (Hookfn) do_completion);
+    deletehookfunc("before_complete", (Hookfn) before_complete);
+    deletehookfunc("after_complete", (Hookfn) after_complete);
+    deletehookfunc("accept_completion", (Hookfn) accept_last);
+    deletehookfunc("reverse_menu", (Hookfn) reverse_menu);
+    deletehookfunc("list_matches", (Hookfn) list_matches);
+    deletehookfunc("invalidate_list", (Hookfn) invalidate_list);
+    deletehookdefs(m->nam, comphooks, sizeof(comphooks)/sizeof(*comphooks));
     deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
     deleteconddefs(m->nam, cotab, sizeof(cotab)/sizeof(*cotab));
     deleteparamdefs(m->nam, patab, sizeof(patab)/sizeof(*patab));
@@ -1323,8 +1406,34 @@ cleanup_complete(Module m)
 int
 finish_complete(Module m)
 {
-    makecompparamsptr = NULL;
-    comp_setunsetptr = NULL;
+    freearray(compwords);
+    zsfree(compprefix);
+    zsfree(compsuffix);
+    zsfree(compiprefix);
+    zsfree(compisuffix);
+    zsfree(compqiprefix);
+    zsfree(compqisuffix);
+    zsfree(compmatcherstr);
+    zsfree(compcontext);
+    zsfree(compparameter);
+    zsfree(compredirect);
+    zsfree(compquote);
+    zsfree(compquoting);
+    zsfree(comprestore);
+    zsfree(complist);
+    zsfree(compforcelist);
+    zsfree(compinsert);
+    zsfree(compexact);
+    zsfree(compexactstr);
+    zsfree(comppatmatch);
+    zsfree(comppatinsert);
+    zsfree(complastprompt);
+    zsfree(comptoend);
+    zsfree(compoldlist);
+    zsfree(compoldins);
+    zsfree(compvared);
+
+    hascompmod = 0;
 
     return 0;
 }
diff --git a/Src/Zle/complete.mdd b/Src/Zle/complete.mdd
index 628058e2a..3ea802065 100644
--- a/Src/Zle/complete.mdd
+++ b/Src/Zle/complete.mdd
@@ -8,4 +8,6 @@ autoprefixconds="prefix suffix between after"
 
 autoparams="compmatchers"
 
-objects="complete.o"
+headers="comp.h"
+
+objects="complete.o compcore.o compmatch.o compresult.o"
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index cc2ac7517..405f3a6d2 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -622,7 +622,7 @@ domenuselect(Hookdef dummy, Chdata dat)
 		fixsuffix();
 		validlist = 0;
 		amatches = pmatches = lastmatches = NULL;
-		invalidatelist();
+		invalidate_list();
 		PERMALLOC {
 		    menucomplete(zlenoargs);
 		} LASTALLOC;
@@ -653,7 +653,7 @@ domenuselect(Hookdef dummy, Chdata dat)
 		s->nbrbeg = nbrbeg;
 		s->nbrend = nbrend;
 		s->nmatches = nmatches;
-		acceptlast();
+		accept_last();
 		do_menucmp(0);
 		mselect = (*(minfo.cur))->gnum;
 		setwish = 1;
@@ -913,7 +913,7 @@ boot_complist(Module m)
 		 NULL, 0);
 	return -1;
     }
-    addhookfunc("list_matches", (Hookfn) complistmatches);
+    addhookfunc("comp_list_matches", (Hookfn) complistmatches);
     addhookfunc("menu_start", (Hookfn) domenuselect);
     mskeymap = newkeymap(NULL, "menuselect");
     linkkeymap(mskeymap, "menuselect", 1);
@@ -941,7 +941,7 @@ cleanup_complist(Module m)
     free(mgtab);
 
     deletezlefunction(w_menuselect);
-    deletehookfunc("list_matches", (Hookfn) complistmatches);
+    deletehookfunc("comp_list_matches", (Hookfn) complistmatches);
     deletehookfunc("menu_start", (Hookfn) domenuselect);
     unlinkkeymap("menuselect", 1);
     return 0;
diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c
new file mode 100644
index 000000000..32e0c3a68
--- /dev/null
+++ b/Src/Zle/compmatch.c
@@ -0,0 +1,1906 @@
+/*
+ * compmatch.c - the complete module, completion matching code
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1999 Sven Wischnowsky
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Sven Wischnowsky or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Sven Wischnowsky and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Sven Wischnowsky and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose.  The software
+ * provided hereunder is on an "as is" basis, and Sven Wischnowsky and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "complete.mdh"
+#define GLOBAL_PROTOTYPES
+#include "zle_tricky.pro"
+#undef GLOBAL_PROTOTYPES
+#include "compmatch.pro"
+
+/* Convenience macro for calling bslashquote() (formerly quotename()). */
+
+#define quotename(s, e) bslashquote(s, e, instring)
+
+/* This compares two cpattern lists and returns non-zero if they are
+ * equal. */
+
+/**/
+static int
+cmp_cpatterns(Cpattern a, Cpattern b)
+{
+    while (a) {
+	if (a->equiv != b->equiv || memcmp(a->tab, b->tab, 256))
+	    return 0;
+	a = a->next;
+	b = b->next;
+    }
+    return 1;
+}
+
+/* This compares two cmatchers and returns non-zero if they are equal. */
+
+/**/
+static int
+cmp_cmatchers(Cmatcher a, Cmatcher b)
+{
+    return (a == b ||
+	    (a->flags == b->flags &&
+	     a->llen == b->llen && a->wlen == b->wlen &&
+	     (!a->llen || cmp_cpatterns(a->line, b->line)) &&
+	     (a->wlen <= 0 || cmp_cpatterns(a->word, b->word)) &&
+	     (!(a->flags & CMF_LEFT) ||
+	      (a->lalen == b->lalen &&
+	       (!a->lalen || cmp_cpatterns(a->left, b->left)))) &&
+	     (!(a->flags & CMF_RIGHT) ||
+	      (a->ralen == b->ralen &&
+	       (!a->ralen || cmp_cpatterns(a->right, b->right))))));
+}
+
+/* Add the given matchers to the bmatcher list. */
+
+/**/
+void
+add_bmatchers(Cmatcher m)
+{
+    Cmlist old = bmatchers, *q = &bmatchers, n;
+
+    for (; m; m = m->next) {
+	if ((!m->flags && m->wlen > 0 && m->llen > 0) ||
+	    (m->flags == CMF_RIGHT && m->wlen == -1 && !m->llen)) {
+	    *q = n = (Cmlist) zhalloc(sizeof(struct cmlist));
+	    n->matcher = m;
+	    q = &(n->next);
+	}
+    }
+    *q = old;
+}
+
+/* This is called when the matchers in the mstack have changed to
+ * ensure that the bmatchers list contains no matchers not in mstack. */
+
+/**/
+void
+update_bmatchers(void)
+{
+    Cmlist p = bmatchers, q = NULL, ms;
+    Cmatcher mp;
+    int t;
+
+    while (p) {
+	t = 0;
+	for (ms = mstack; ms && !t; ms = ms->next)
+	    for (mp = ms->matcher; mp && !t; mp = mp->next)
+		t = cmp_cmatchers(mp, p->matcher);
+
+	p = p->next;
+	if (!t) {
+	    if (q)
+		q->next = p;
+	    else
+		bmatchers = p;
+	}
+    }
+}
+
+/* This returns a new Cline structure. */
+
+/**/
+Cline
+get_cline(char *l, int ll, char *w, int wl, char *o, int ol, int fl)
+{
+    Cline r;
+
+    /* Prefer to take it from the buffer list (freecl), if there
+     * is none, allocate a new one. */
+
+    if ((r = freecl))
+	freecl = r->next;
+    else
+	r = (Cline) zhalloc(sizeof(*r));
+
+    r->next = NULL;
+    r->line = l; r->llen = ll;
+    r->word = w; r->wlen = wl;
+    r->orig = o; r->olen = ol;
+    r->slen = 0;
+    r->flags = fl;
+    r->prefix = r->suffix = NULL;
+    return r;
+}
+
+/* This frees a cline list. */
+
+/**/
+void
+free_cline(Cline l)
+{
+    Cline n;
+
+    while (l) {
+	n = l->next;
+	l->next = freecl;
+	freecl = l;
+	free_cline(l->prefix);
+	free_cline(l->suffix);
+	l = n;
+    }
+}
+
+/* Copy a cline list. */
+
+/**/
+Cline
+cp_cline(Cline l, int deep)
+{
+    Cline r = NULL, *p = &r, t, lp = NULL;
+
+    while (l) {
+	if ((t = freecl))
+	    freecl = t->next;
+	else
+	    t = (Cline) zhalloc(sizeof(*t));
+	memcpy(t, l, sizeof(*t));
+	if (deep) {
+	    if (t->prefix)
+		t->prefix = cp_cline(t->prefix, 0);
+	    if (t->suffix)
+		t->suffix = cp_cline(t->suffix, 0);
+	}
+	*p = lp = t;
+	p = &(t->next);
+	l = l->next;
+    }
+    *p = NULL;
+
+    return r;
+}
+
+/* Calculate the length of a cline and its sub-lists. */
+
+/**/
+int
+cline_sublen(Cline l)
+{
+    int len = ((l->flags & CLF_LINE) ? l->llen : l->wlen);
+
+    if (l->olen && !((l->flags & CLF_SUF) ? l->suffix : l->prefix))
+	len += l->olen;
+    else {
+	Cline p;
+
+	for (p = l->prefix; p; p = p->next)
+	    len += ((p->flags & CLF_LINE) ? p->llen : p->wlen);
+	for (p = l->suffix; p; p = p->next)
+	    len += ((p->flags & CLF_LINE) ? p->llen : p->wlen);
+    }
+    return len;
+}
+
+/* Set the lengths in the cline lists. */
+
+/**/
+void
+cline_setlens(Cline l, int both)
+{
+    while (l) {
+	l->max = cline_sublen(l);
+	if (both)
+	    l->min = l->max;
+	l = l->next;
+    }
+}
+
+/* This sets the CLF_MATCHED flag in the given clines. */
+
+/**/
+void
+cline_matched(Cline p)
+{
+    while (p) {
+	p->flags |= CLF_MATCHED;
+	cline_matched(p->prefix);
+	cline_matched(p->suffix);
+
+	p = p->next;
+    }
+}
+
+/* This reverts the order of the elements of the given cline list and
+ * returns a pointer to the new head. */
+
+/**/
+Cline
+revert_cline(Cline p)
+{
+    Cline r = NULL, n;
+
+    while (p) {
+	n = p->next;
+	p->next = r;
+	r = p;
+	p = n;
+    }
+    return r;
+}
+
+/* Global variables used during matching: a char-buffer for the string to
+ * use for the match, and two cline lists for the two levels we use. */
+
+/**/
+char *matchbuf = NULL;
+/**/
+int matchbuflen = 0, matchbufadded;
+
+/**/
+Cline matchparts, matchlastpart;
+/**/
+Cline matchsubs, matchlastsub;
+
+/* This initialises the variables above. */
+
+/**/
+static void
+start_match(void)
+{
+    if (matchbuf)
+	*matchbuf = '\0';
+    matchbufadded = 0;
+    matchparts = matchlastpart = matchsubs = matchlastsub = NULL;
+}
+
+/* This aborts a matching, freeing the cline lists build. */
+
+/**/
+static void
+abort_match(void)
+{
+    free_cline(matchparts);
+    free_cline(matchsubs);
+    matchparts = matchsubs = NULL;
+}
+
+/* This adds a new string in the static char buffer. The arguments are
+ * the matcher used (if any), the strings from the line and the word
+ * and the length of the string from the word. The last argument is
+ * non-zero if we are matching a suffix (where the given string has to 
+ * be prepended to the contents of the buffer). */
+
+/**/
+static void
+add_match_str(Cmatcher m, char *l, char *w, int wl, int sfx)
+{
+    /* Get the string and length to insert: either from the line 
+     * or from the match. */
+    if (m && (m->flags & CMF_LINE)) {
+	wl = m->llen; w = l;
+    }
+    if (wl) {
+	/* Probably resize the buffer. */
+	if (matchbuflen - matchbufadded <= wl) {
+	    int blen = matchbuflen + wl + 20;
+	    char *buf;
+
+	    buf = (char *) zalloc(blen);
+	    memcpy(buf, matchbuf, matchbuflen);
+	    zfree(matchbuf, matchbuflen);
+	    matchbuf = buf;
+	    matchbuflen = blen;
+	}
+	/* Insert the string. */
+	if (sfx) {
+	    memmove(matchbuf + wl, matchbuf, matchbufadded + 1);
+	    memcpy(matchbuf, w, wl);
+	} else
+	    memcpy(matchbuf + matchbufadded, w, wl);
+	matchbufadded += wl;
+	matchbuf[matchbufadded] = '\0';
+    }
+}
+
+/* This adds a cline for a word-part during matching. Arguments are the
+ * matcher used, pointers to the line and word strings for the anchor,
+ * a pointer to the original line string for the whole part, the string
+ * before (or after) the anchor that has not yet been added, the length
+ * of the line-string for that, and a flag saying if we are matching a 
+ * suffix. */
+
+/**/
+static void
+add_match_part(Cmatcher m, char *l, char *w, int wl,
+	       char *o, int ol, char *s, int sl, int osl, int sfx)
+{
+    Cline p, lp;
+
+    /* If the anchors are equal, we keep only one. */
+
+    if (!strncmp(l, w, wl))
+	l = NULL;
+
+    /* Split the new part into parts and turn the last one into a
+     * `suffix' if we have a left anchor. */
+
+    p = bld_parts(s, sl, osl, &lp);
+
+    p->flags &= ~CLF_NEW;
+    if (m && (m->flags & CMF_LEFT)) {
+	lp->flags |= CLF_SUF;
+	lp->suffix = lp->prefix;
+	lp->prefix = NULL;
+    }
+    /* cline lists for suffixes are sorted from back to front, so we have
+     * to revert the list we got. */
+    if (sfx)
+	p = revert_cline(lp = p);
+    /* Now add the sub-clines we already had. */
+    if (matchsubs) {
+	if (sfx) {
+	    Cline q;
+
+	    if ((q = lp->prefix)) {
+		while (q->next)
+		    q = q->next;
+		q->next = matchsubs;
+	    } else
+		lp->prefix = matchsubs;
+
+	    matchlastsub->next = NULL;
+	} else {
+	    matchlastsub->next = p->prefix;
+	    p->prefix = matchsubs;
+	}
+	matchsubs = matchlastsub = NULL;
+    }
+    /* Store the arguments in the last part-cline. */
+    lp->line = l; lp->llen = wl;
+    lp->word = w; lp->wlen = wl;
+    lp->orig = o; lp->olen = ol;
+    lp->flags &= ~CLF_NEW;
+
+    /* Finally, put the new parts on the list. */
+    if (matchlastpart)
+	matchlastpart->next = p;
+    else
+	matchparts = p;
+    matchlastpart = lp;
+}
+
+/* This adds a new sub-cline. Arguments are the matcher and the strings from
+ * the line and the word. */
+
+/**/
+static void
+add_match_sub(Cmatcher m, char *l, int ll, char *w, int wl)
+{
+    int flags;
+    Cline n;
+
+    /* Check if we are interested only in the string from the line. */
+    if (m && (m->flags & CMF_LINE)) {
+	w = NULL; wl = 0;
+	flags = CLF_LINE;
+    } else
+	flags = 0;
+
+    /* And add the cline. */
+    if (wl || ll) {
+	n = get_cline(l, ll, w, wl, NULL, 0, flags);
+	if (matchlastsub)
+	    matchlastsub->next = n;
+	else
+	    matchsubs = n;
+	matchlastsub = n;
+    }
+}
+
+/* This tests if the string from the line l matches the word w. In bp
+ * the offset for the brace is returned, in rwlp the length of the
+ * matched prefix or suffix, not including the stuff before or after
+ * the last anchor is given. When sfx is non-zero matching is done from
+ * the ends of the strings backward, if test is zero, the global variables
+ * above are used to build the string for the match and the cline. If
+ * part is non-zero, we are satisfied if only a part of the line-string
+ * is used (and return the length used). */
+
+/**/
+int
+match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
+	  int sfx, int test, int part)
+{
+    int ll = strlen(l), lw = strlen(w), oll = ll, olw = lw;
+    int il = 0, iw = 0, t, ind, add, he = 0, bpc, obc = bc;
+    VARARR(unsigned char, ea, ll + 1);
+    char *ow;
+    Cmlist ms;
+    Cmatcher mp, lm = NULL;
+    Brinfo bp = NULL;
+
+    if (!test) {
+	start_match();
+	bp = *bpp;
+    }
+    /* Adjust the pointers and get the values for subscripting and
+     * incrementing. */
+
+    if (sfx) {
+	l += ll; w += lw;
+	ind = -1; add = -1;
+    } else {
+	ind = 0; add = 1;
+    }
+    /* ow will always point to the beginning (or end) of that sub-string
+     * in w that wasn't put in the match-variables yet. */
+
+    ow = w;
+
+    /* If the brace is at the beginning, we have to treat it now. */
+
+    if (!test && bp && bc >= bp->pos) {
+	bp->curpos = bc;
+	bp = bp->next;
+    }
+    while (ll && lw) {
+	/* First try the matchers. */
+	for (mp = NULL, ms = mstack; !mp && ms; ms = ms->next) {
+	    for (mp = ms->matcher; mp; mp = mp->next) {
+		t = 1;
+		if ((lm && lm == mp) ||
+		    ((oll == ll || olw == lw) &&
+		     (test == 1 || (test && !mp->left && !mp->right)) &&
+		     mp->wlen < 0))
+		    /* If we were called recursively, don't use `*' patterns
+		     * at the beginning (avoiding infinite recursion). */
+		    continue;
+
+		if (mp->wlen < 0) {
+		    int both, loff, aoff, llen, alen, zoff, moff, ct, ict;
+		    char *tp, savl = '\0', savw;
+		    Cpattern ap;
+
+		    /* This is for `*' patterns, first initialise some
+		     * local variables. */
+		    llen = mp->llen;
+		    alen = (mp->flags & CMF_LEFT ? mp->lalen : mp->ralen);
+
+		    /* Give up if we don't have enough characters for the
+		     * line-string and the anchor. */
+		    if (ll < llen + alen || lw < alen)
+			continue;
+
+		    if (mp->flags & CMF_LEFT) {
+			ap = mp->left; zoff = 0; moff = alen;
+			if (sfx) {
+			    both = 0; loff = -llen; aoff = -(llen + alen);
+			} else {
+			    both = 1; loff = alen; aoff = 0;
+			}
+		    } else {
+			ap = mp->right; zoff = alen; moff = 0;
+			if (sfx) {
+			    both = 1; loff = -(llen + alen); aoff = -alen;
+			} else {
+			    both = 0; loff = 0; aoff = llen;
+			}
+		    }
+		    /* Try to match the line pattern and the anchor. */
+		    if (!pattern_match(mp->line, l + loff, NULL, NULL))
+			continue;
+		    if (ap) {
+			if (!pattern_match(ap, l + aoff, NULL, NULL) ||
+			    (both && (!pattern_match(ap, w + aoff, NULL, NULL) ||
+				      !match_parts(l + aoff, w + aoff, alen,
+						   part))))
+				continue;
+		    } else if (!both || il || iw)
+			continue;
+
+		    /* Fine, now we call ourselves recursively to find the
+		     * string matched by the `*'. */
+		    if (sfx) {
+			savl = l[-(llen + zoff)];
+			l[-(llen + zoff)] = '\0';
+		    }
+		    for (t = 0, tp = w, ct = 0, ict = lw - alen + 1;
+			 ict;
+			 tp += add, ct++, ict--) {
+			if ((both &&
+			     (!ap || !test ||
+			      !pattern_match(ap, tp + aoff, NULL, NULL))) ||
+			    (!both &&
+			     pattern_match(ap, tp - moff, NULL, NULL) &&
+			     match_parts(l + aoff , tp - moff, alen, part))) {
+			    if (sfx) {
+				savw = tp[-zoff];
+				tp[-zoff] = '\0';
+				t = match_str(l - ll, w - lw,
+					      NULL, 0, NULL, 1, 2, part);
+				tp[-zoff] = savw;
+			    } else
+				t = match_str(l + llen + moff, tp + moff,
+					      NULL, 0, NULL, 0, 1, part);
+			    if (t || !both)
+				break;
+			}
+		    }
+		    ict = ct;
+		    if (sfx)
+			l[-(llen + zoff)] = savl;
+
+		    /* Have we found a position in w where the rest of l
+		     * matches? */
+		    if (!t)
+			continue;
+
+		    /* Yes, add the strings and clines if this is a 
+		     * top-level call. */
+		    if (!test && (!he || (llen + alen))) {
+			char *op, *lp, *map, *wap, *wmp;
+			int ol;
+
+			if (sfx) {
+			    op = w; ol = ow - w; lp = l - (llen + alen);
+			    map = tp - alen;
+			    if (mp->flags & CMF_LEFT) {
+				wap = tp - alen; wmp = tp;
+			    } else {
+				wap = w - alen; wmp = tp - alen;
+			    }
+			} else {
+			    op = ow; ol = w - ow; lp = l;
+			    map = ow;
+			    if (mp->flags & CMF_LEFT) {
+				wap = w; wmp = w + alen;
+			    } else {
+				wap = tp; wmp = ow;
+			    }
+			}
+			/* If the matcher says that we are only interested
+			 * in the line pattern, we just add that and the
+			 * anchor and the string not added yet. Otherwise
+			 * we add a new part. */
+			if (mp->flags & CMF_LINE) {
+			    add_match_str(NULL, NULL, op, ol, sfx);
+			    add_match_str(NULL, NULL, lp, llen + alen, sfx);
+			    add_match_sub(NULL, NULL, ol, op, ol);
+			    add_match_sub(NULL, NULL, llen + alen,
+					  lp, llen + alen);
+			} else if (sfx) {
+			    add_match_str(NULL, NULL,
+					  map, ct + ol + alen, sfx);
+			    add_match_part(mp, l + aoff, wap, alen,
+					   l + loff, llen, op, ol, ol, sfx);
+			    add_match_sub(NULL, NULL, 0, wmp, ct);
+			} else {
+			    add_match_str(NULL, NULL,
+					  map, ct + ol + alen, sfx);
+			    if (both) {
+				add_match_sub(NULL, NULL, ol, op, ol);
+				ol = -1;
+			    } else
+				ct += ol;
+			    add_match_part(mp, l + aoff, wap, alen,
+					   l + loff, llen, wmp, ct, ol, sfx);
+			}
+		    }
+		    /* Now skip over the matched portion and the anchor. */
+		    llen += alen; alen += ict;
+		    if (sfx) {
+			l -= llen; w -= alen;
+		    } else {
+			l += llen; w += alen;
+		    }
+		    ll -= llen; il += llen;
+		    lw -= alen; iw += alen;
+		    bc += llen;
+
+		    if (!test)
+			while (bp &&
+			       bc >= (bpc = (useqbr ? bp->qpos : bp->pos))) {
+			    bp->curpos = matchbufadded + bpc - bc + obc;
+			    bp = bp->next;
+			}
+		    ow = w;
+
+		    if (!llen && !alen) {
+			lm = mp;
+			if (he)
+			    mp = NULL;
+			else
+			    he = 1;
+		    } else {
+			lm = NULL; he = 0;
+		    }
+		    break;
+		} else if (ll >= mp->llen && lw >= mp->wlen) {
+		    /* Non-`*'-pattern. */
+		    char *tl, *tw;
+		    int tll, tlw, til, tiw;
+
+		    /* We do this only if the line- and word-substrings
+		     * are not equal. */
+		    if (!(mp->flags & (CMF_LEFT | CMF_RIGHT)) &&
+			mp->llen == mp->wlen &&
+			!(sfx ? strncmp(l - mp->llen, w - mp->wlen, mp->llen) :
+			  strncmp(l, w, mp->llen)))
+			continue;
+
+		    /* Using local variables to make the following
+		     * independent of whether we match a prefix or a
+		     * suffix. */
+		    if (sfx) {
+			tl = l - mp->llen; tw = w - mp->wlen;
+			til = ll - mp->llen; tiw = lw - mp->wlen;
+			tll = il + mp->llen; tlw = iw + mp->wlen;
+		    } else {
+			tl = l; tw = w;
+			til = il; tiw = iw;
+			tll = ll; tlw = lw;
+		    }
+		    if (mp->flags & CMF_LEFT) {
+			/* Try to match the left anchor, if any. */
+			if (til < mp->lalen || tiw < mp->lalen)
+			    continue;
+			else if (mp->left)
+			    t = pattern_match(mp->left, tl - mp->lalen,
+					      NULL, NULL) &&
+				pattern_match(mp->left, tw - mp->lalen,
+					      NULL, NULL);
+			else
+			    t = (!sfx && !il && !iw);
+		    }
+		    if (mp->flags & CMF_RIGHT) {
+			/* Try to match the right anchor, if any. */
+			if (tll < mp->llen + mp->ralen ||
+			    tlw < mp->wlen + mp->ralen)
+			    continue;
+			else if (mp->left)
+			    t = pattern_match(mp->right,
+					      tl + mp->llen - mp->ralen,
+					      NULL, NULL) &&
+				pattern_match(mp->right,
+					      tw + mp->wlen - mp->ralen,
+					      NULL, NULL);
+			else
+			    t = (sfx && !il && !iw);
+		    }
+		    /* Now try to match the line and word patterns. */
+		    if (!t ||
+			!pattern_match(mp->line, tl, NULL, ea) ||
+			!pattern_match(mp->word, tw, ea, NULL))
+			continue;
+
+		    /* Probably add the matched strings. */
+		    if (!test) {
+			if (sfx)
+			    add_match_str(NULL, NULL, w, ow - w, 0);
+			else
+			    add_match_str(NULL, NULL, ow, w - ow, 0);
+			add_match_str(mp, tl, tw, mp->wlen, 0);
+			if (sfx)
+			    add_match_sub(NULL, NULL, 0, w, ow - w);
+			else
+			    add_match_sub(NULL, NULL, 0, ow, w - ow);
+
+			add_match_sub(mp, tl, mp->llen, tw, mp->wlen);
+		    }
+		    if (sfx) {
+			l = tl;	w = tw;
+		    } else {
+			l += mp->llen; w += mp->wlen;
+		    }
+		    il += mp->llen; iw += mp->wlen;
+		    ll -= mp->llen; lw -= mp->wlen;
+		    bc += mp->llen;
+
+		    if (!test)
+			while (bp &&
+			       bc >= (bpc = (useqbr ? bp->qpos : bp->pos))) {
+			    bp->curpos = matchbufadded + bpc - bc + obc;
+			    bp = bp->next;
+			}
+		    ow = w;
+		    lm = NULL;
+		    he = 0;
+		    break;
+		}
+	    }
+	}
+	if (mp)
+	    continue;
+
+	if (l[ind] == w[ind]) {
+	    /* No matcher could be used, but the strings have the same
+	     * character here, skip over it. */
+	    l += add; w += add;
+	    il++; iw++;
+	    ll--; lw--;
+	    bc++;
+	    if (!test)
+		while (bp && bc >= (useqbr ? bp->qpos : bp->pos)) {
+		    bp->curpos = matchbufadded + (sfx ? (ow - w) : (w - ow)) + obc;
+		    bp = bp->next;
+		}
+	    lm = NULL;
+	    he = 0;
+	} else {
+	    /* No matcher and different characters: l does not match w. */
+	    if (test)
+		return 0;
+
+	    abort_match();
+
+	    return -1;
+	}
+    }
+    /* If this is a recursive call, we just return if l matched w or not. */
+    if (test)
+	return (part || !ll);
+
+    /* In top-level calls, if ll is non-zero (unmatched portion in l),
+     * we have to free the collected clines. */
+    if (!part && ll) {
+	abort_match();
+
+	return -1;
+    }
+    if (rwlp)
+	*rwlp = iw - (sfx ? ow - w : w - ow);
+
+    /* If we matched a suffix, the anchors stored in the top-clines
+     * will be in the wrong clines: shifted by one. Adjust this. */
+    if (sfx && matchparts) {
+	Cline t, tn, s;
+
+	if (matchparts->prefix || matchparts->suffix) {
+	    t = get_cline(NULL, 0, NULL, 0, NULL, 0, 0);
+	    t->next = matchparts;
+	    if (matchparts->prefix)
+		t->prefix = (Cline) 1;
+	    else
+		t->suffix = (Cline) 1;
+	    matchparts = t;
+	}
+	for (t = matchparts; (tn = t->next); t = tn) {
+	    s = (tn->prefix ? tn->prefix : tn->suffix);
+	    if (t->suffix)
+		t->suffix = s;
+	    else
+		t->prefix = s;
+	}
+	t->prefix = t->suffix = NULL;
+    }
+    /* Finally, return the number of matched characters. */
+
+    *bpp = bp;
+    return (part ? il : iw);
+}
+
+/* Wrapper for match_str(), only for a certain length and only doing
+ * the test. */
+
+/**/
+static int
+match_parts(char *l, char *w, int n, int part)
+{
+    char lsav = l[n], wsav = w[n];
+    int ret;
+
+    l[n] = w[n] = '\0';
+    ret = match_str(l, w, NULL, 0, NULL, 0, 1, part);
+    l[n] = lsav;
+    w[n] = wsav;
+
+    return ret;
+}
+
+/* Check if the word w is matched by the strings in pfx and sfx (the prefix
+ * and the suffix from the line) or the pattern cp. In clp a cline list for
+ * w is returned.
+ * qu is non-zero if the words has to be quoted before processed any further.
+ * bpl and bsl are used to report the positions where the brace-strings in
+ * the prefix and the suffix have to be re-inserted if this match is inserted
+ * in the line.
+ * The return value is the string to use as a completion or NULL if the prefix
+ * and the suffix don't match the word w. */
+
+/**/
+char *
+comp_match(char *pfx, char *sfx, char *w, Patprog cp, Cline *clp, int qu,
+	   Brinfo *bpl, int bcp, Brinfo *bsl, int bcs, int *exact)
+{
+    char *r = NULL;
+
+    if (cp) {
+	/* We have a globcomplete-like pattern, just use that. */
+	int wl;
+
+	r = w;
+	if (!pattry(cp, r))
+	    return NULL;
+    
+	r = (qu ? quotename(r, NULL) : dupstring(r));
+	if (qu == 2 && r[0] == '\\' && r[1] == '~')
+	    chuck(r);
+	/* We still break it into parts here, trying to build a sensible
+	 * cline list for these matches, too. */
+	w = dupstring(w);
+	wl = strlen(w);
+	*clp = bld_parts(w, wl, wl, NULL);
+	*exact = 0;
+    } else {
+	Cline pli, plil;
+	int mpl, rpl, wl;
+
+	w = (qu ? quotename(w, NULL) : dupstring(w));
+	if (qu == 2 && w[0] == '\\' && w[1] == '~')
+	    chuck(w);
+
+	wl = strlen(w);
+
+	/* Always try to match the prefix. */
+
+	useqbr = qu;
+	if ((mpl = match_str(pfx, w, bpl, bcp, &rpl, 0, 0, 0)) < 0)
+	    return NULL;
+
+	if (sfx && *sfx) {
+	    int wpl = matchbufadded, msl, rsl;
+	    VARARR(char, wpfx, wpl);
+	    Cline mli, mlil;
+
+	    /* We also have a suffix to match, so first save the
+	     * contents of the global matching variables. */
+	    memcpy(wpfx, matchbuf, wpl);
+	    if (matchsubs) {
+		Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, 0);
+
+		tmp->prefix = matchsubs;
+		if (matchlastpart)
+		    matchlastpart->next = tmp;
+		else
+		    matchparts = tmp;
+	    }
+	    pli = matchparts;
+	    plil = matchlastpart;
+
+	    /* The try to match the suffix. */
+
+	    if ((msl = match_str(sfx, w + mpl, bsl, bcs, &rsl, 1, 0, 0)) < 0) {
+		free_cline(pli);
+
+		return NULL;
+	    }
+	    /* Matched, so add the string in the middle and the saved
+	     * string for the prefix, and build a combined cline list
+	     * for the prefix and the suffix. */
+	    if (matchsubs) {
+		Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, CLF_SUF);
+
+		tmp->suffix = matchsubs;
+		if (matchlastpart)
+		    matchlastpart->next = tmp;
+		else
+		    matchparts = tmp;
+	    }
+	    add_match_str(NULL, NULL, w + rpl, wl - rpl - rsl, 1);
+	    add_match_str(NULL, NULL, wpfx, wpl, 1);
+
+	    mli = bld_parts(w + rpl, wl - rpl - rsl,
+			    (mpl - rpl) + (msl - rsl), &mlil);
+	    mlil->flags |= CLF_MID;
+	    mlil->slen = msl - rsl;
+	    mlil->next = revert_cline(matchparts);
+
+	    if (plil)
+		plil->next = mli;
+	    else
+		pli = mli;
+	} else {
+	    /* Only a prefix, add the string and a part-cline for it. */
+	    add_match_str(NULL, NULL, w + rpl, wl - rpl, 0);
+
+	    add_match_part(NULL, NULL, NULL, 0, NULL, 0, w + rpl, wl - rpl,
+			   mpl - rpl, 0);
+	    pli = matchparts;
+	}
+	r = dupstring(matchbuf ? matchbuf : "");
+
+	*clp = pli;
+
+	/* Test if the string built is equal to the one from the line. */
+	if (sfx && *sfx) {
+	    int pl = strlen(pfx);
+
+	    *exact = (!strncmp(pfx, w, pl) && !strcmp(sfx, w + pl));
+	} else
+	    *exact = !strcmp(pfx, w);
+    }
+    if (!qu)
+	hasunqu = 1;
+
+    return r;
+}
+
+/* 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)
+		in++;
+	    if (out)
+		out++;
+	} else if (!p->tab[c])
+	    return 0;
+
+	s++;
+	p = p->next;
+    }
+    return 1;
+}
+
+/* This splits the given string into a list of cline structs, separated
+ * at those places where one of the anchors of an `*' pattern was found.
+ * plen gives the number of characters on the line that matched this
+ * string. In lp we return a pointer to the last cline struct we build. */
+
+/**/
+Cline
+bld_parts(char *str, int len, int plen, Cline *lp)
+{
+    Cline ret = NULL, *q = &ret, n;
+    Cmlist ms;
+    Cmatcher mp;
+    int t, op = plen;
+    char *p = str;
+
+    while (len) {
+	for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
+	    mp = ms->matcher;
+	    if (mp->flags == CMF_RIGHT && mp->wlen == -1 &&
+		!mp->llen && len >= mp->ralen && mp->ralen &&
+		pattern_match(mp->right, str, NULL, NULL)) {
+		int olen = str - p, llen;
+
+		/* We found an anchor, create a new cline. The NEW flag
+		 * is set if the characters before the anchor were not
+		 * on the line. */
+		*q = n = get_cline(NULL, mp->ralen, str, mp->ralen, NULL, 0,
+				   ((plen < 0) ? CLF_NEW : 0));
+
+		/* If there were any characters before the anchor, add
+		 * them as a cline struct. */
+
+		if (p != str) {
+		    llen = (op < 0 ? 0 : op);
+
+		    if (llen > olen)
+			llen = olen;
+		    n->prefix = get_cline(NULL, llen, p, olen, NULL, 0, 0);
+		}
+		q = &(n->next);
+		str += mp->ralen; len -= mp->ralen;
+		plen -= mp->ralen;
+		op -= olen;
+		p = str;
+		t = 1;
+	    }
+	}
+	if (!t) {
+	    /* No anchor was found here, skip. */
+	    str++; len--;
+	    plen--;
+	}
+    }
+    /* This is the cline struct for the remaining string at the end. */
+
+    *q = n = get_cline(NULL, 0, NULL, 0, NULL, 0, (plen < 0 ? CLF_NEW : 0));
+    if (p != str) {
+	int olen = str - p, llen = (op < 0 ? 0 : op);
+
+	if (llen > olen)
+	    llen = olen;
+	n->prefix = get_cline(NULL, llen, p, olen, NULL, 0, 0);
+    }
+    n->next = NULL;
+
+    if (lp)
+	*lp = n;
+
+    return ret;
+}
+
+/* This builds all the possible line patterns for the pattern pat in the
+ * buffer line. Initially line is the same as lp, but during recursive
+ * calls lp is incremented for storing successive characters. Whenever
+ * a full possible string is build, we test if this line matches the
+ * string given by wlen and word. The in argument contains the characters
+ * to use for the correspondence classes, it was filled by a call to 
+ * pattern_match() in the calling function.
+ * The return value is the length of the string matched in the word, it
+ * is zero if we couldn't build a line that matches the word. */
+
+/**/
+static int
+bld_line(Cpattern pat, char *line, char *lp,
+	 char *word, int wlen, unsigned char *in, int sfx)
+{
+    if (pat) {
+	/* Still working on the pattern. */
+
+	int i, l;
+	unsigned char c = 0;
+
+	/* Get the number of the character for a correspondence class
+	 * if it has a correxponding class. */
+	if (pat->equiv)
+	    if ((c = *in))
+		in++;
+
+	/* Walk through the table in the pattern and try the characters
+	 * that may appear in the current position. */
+	for (i = 0; i < 256; i++)
+	    if ((pat->equiv && c) ? (c == pat->tab[i]) : pat->tab[i]) {
+		*lp = i;
+		/* We stored the character, now call ourselves to build
+		 * the rest. */
+		if ((l = bld_line(pat->next, line, lp + 1, word, wlen,
+				  in, sfx)))
+		    return l;
+	    }
+    } else {
+	/* We reached the end, i.e. the line string is fully build, now
+	 * see if it matches the given word. */
+
+	Cmlist ms;
+	Cmatcher mp;
+	int l = lp - line, t, rl = 0, ind, add;
+	VARARR(unsigned char, ea, l + 1);
+
+	/* Quick test if the strings are exactly the same. */
+	if (l == wlen && !strncmp(line, word, l))
+	    return l;
+
+	if (sfx) {
+	    line = lp; word += wlen;
+	    ind = -1; add = -1;
+	} else {
+	    ind = 0; add = 1;
+	}
+	/* We loop through the whole line string built. */
+	while (l && wlen) {
+	    if (word[ind] == line[ind]) {
+		/* The same character in both strings, skip over. */
+		line += add; word += add;
+		l--; wlen--; rl++;
+	    } else {
+		t = 0;
+		for (ms = bmatchers; ms && !t; ms = ms->next) {
+		    mp = ms->matcher;
+		    if (!mp->flags && mp->wlen <= wlen && mp->llen <= l &&
+			pattern_match(mp->line, (sfx ? line - mp->llen : line),
+				      NULL, ea) &&
+			pattern_match(mp->word, (sfx ? word - mp->wlen : word),
+				      ea, NULL)) {
+			/* Both the line and the word pattern matched,
+			 * now skip over the matched portions. */
+			if (sfx) {
+			    line -= mp->llen; word -= mp->wlen;
+			} else {
+			    line += mp->llen; word += mp->wlen;
+			}
+			l -= mp->llen; wlen -= mp->wlen; rl += mp->wlen;
+			t = 1;
+		    }
+		}
+		if (!t)
+		    /* Didn't match, give up. */
+		    return 0;
+	    }
+	}
+	if (!l)
+	    /* Unmatched portion in the line built, return matched length. */
+	    return rl;
+    }
+    return 0;
+}
+
+/* This builds a string that may be put on the line that fully matches the
+ * given strings. The return value is NULL if no such string could be built
+ * or that string in local static memory, dup it. */
+
+/**/
+static char *
+join_strs(int la, char *sa, int lb, char *sb)
+{
+    static char *rs = NULL;
+    static int rl = 0;
+
+    VARARR(unsigned char, ea, (la > lb ? la : lb) + 1);
+    Cmlist ms;
+    Cmatcher mp;
+    int t, bl, rr = rl;
+    char *rp = rs;
+
+    while (la && lb) {
+	if (*sa != *sb) {
+	    /* Different characters, try the matchers. */
+	    for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
+		mp = ms->matcher;
+		if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
+		    mp->wlen <= la && mp->wlen <= lb) {
+		    /* The pattern has no anchors and the word
+		     * pattern fits, try it. */
+		    if ((t = pattern_match(mp->word, sa, NULL, ea)) ||
+			pattern_match(mp->word, sb, NULL, ea)) {
+			/* It matched one of the strings, t says which one. */
+			VARARR(char, line, mp->llen + 1);
+			char **ap, **bp;
+			int *alp, *blp;
+
+			if (t) {
+			    ap = &sa; alp = &la;
+			    bp = &sb; blp = &lb;
+			} else {
+			    ap = &sb; alp = &lb;
+			    bp = &sa; blp = &la;
+			}
+			/* Now try to build a string that matches the other
+			 * string. */
+			if ((bl = bld_line(mp->line, line, line,
+					   *bp, *blp, ea, 0))) {
+			    /* Found one, put it into the return string. */
+			    line[mp->llen] = '\0';
+			    if (rr <= mp->llen) {
+				char *or = rs;
+
+				rs = realloc(rs, (rl += 20));
+				rr += 20;
+				rp += rs - or;
+			    }
+			    memcpy(rp, line, mp->llen);
+			    rp += mp->llen; rr -= mp->llen;
+			    *ap += mp->wlen; *alp -= mp->wlen;
+			    *bp += bl; *blp -= bl;
+			    t = 1;
+			} else
+			    t = 0;
+		    }
+		}
+	    }
+	    if (!t)
+		break;
+	} else {
+	    /* Same character, just take it. */
+	    if (rr <= 1) {
+		char *or = rs;
+
+		rs = realloc(rs, (rl += 20));
+		rr += 20;
+		rp += rs - or;
+	    }
+	    *rp++ = *sa; rr--;
+	    sa++; sb++;
+	    la--; lb--;
+	}
+    }
+    if (la || lb)
+	return NULL;
+
+    *rp = '\0';
+
+    return rs;
+}
+
+/* This compares the anchors stored in two top-level clines. */
+
+/**/
+static int
+cmp_anchors(Cline o, Cline n, int join)
+{
+    int line = 0;
+    char *j;
+
+    /* First try the exact strings. */
+    if ((!(o->flags & CLF_LINE) && o->wlen == n->wlen &&
+	 (!o->word || !strncmp(o->word, n->word, o->wlen))) ||
+	(line = ((!o->line && !n->line && !o->wlen && !n->wlen) ||
+		 (o->llen == n->llen && o->line && n->line &&
+		  !strncmp(o->line, n->line, o->llen))))) {
+	if (line) {
+	    o->flags |= CLF_LINE;
+	    o->word = NULL;
+	    n->wlen = 0;
+	}
+	return 1;
+    }
+    /* Didn't work, try to build a string matching both anchors. */
+    if (join && !(o->flags & CLF_JOIN) && o->word && n->word &&
+	(j = join_strs(o->wlen, o->word, n->wlen, n->word))) {
+	o->flags |= CLF_JOIN;
+	o->wlen = strlen(j);
+	o->word = dupstring(j);
+
+	return 2;
+    }
+    return 0;
+}
+
+/* Below is the code to join two cline lists. This struct is used to walk
+ * through a sub-list. */
+
+typedef struct cmdata *Cmdata;
+
+struct cmdata {
+    Cline cl, pcl;
+    char *str, *astr;
+    int len, alen, olen, line;
+};
+
+/* This is used to ensure that a cmdata struct contains usable data.
+ * The return value is non-zero if we reached the end. */
+
+static int
+check_cmdata(Cmdata md, int sfx)
+{
+    /* We will use the str and len fields to contain the next sub-string
+     * in the list. If len is zero, we have to use the next cline. */
+    if (!md->len) {
+	/* If there is none, we reached the end. */
+	if (!md->cl)
+	    return 1;
+
+	/* Otherwise, get the string. Only the line-string or both.
+	 * We also have to adjust the pointer if this is for a suffix. */
+	if (md->cl->flags & CLF_LINE) {
+	    md->line = 1;
+	    md->len = md->cl->llen;
+	    md->str = md->cl->line;
+	} else {
+	    md->line = 0;
+	    md->len = md->olen = md->cl->wlen;
+	    if ((md->str = md->cl->word) && sfx)
+		md->str += md->len;
+	    md->alen = md->cl->llen;
+	    if ((md->astr = md->cl->line) && sfx)
+		md->astr += md->alen;
+	}
+	md->pcl = md->cl;
+	md->cl = md->cl->next;
+    }
+    return 0;
+}
+
+/* This puts the not-yet-matched portion back into the last cline and 
+ * returns that. */
+
+static Cline
+undo_cmdata(Cmdata md, int sfx)
+{
+    Cline r = md->pcl;
+
+    if (md->line) {
+	r->word = NULL;
+	r->wlen = 0;
+	r->flags |= CLF_LINE;
+	r->llen = md->len;
+	r->line = md->str - (sfx ? md->len : 0);
+    } else if (md->len != md->olen) {
+	r->wlen = md->len;
+	r->word = md->str - (sfx ? md->len : 0);
+    }
+    return r;
+}
+
+/* This tries to build a string matching a sub-string in a sub-cline
+ * that could not be matched otherwise. */
+
+static Cline
+join_sub(Cmdata md, char *str, int len, int *mlen, int sfx, int join)
+{
+    if (!check_cmdata(md, sfx)) {
+	char *ow = str, *nw = md->str;
+	int ol = len, nl = md->len;
+	Cmlist ms;
+	Cmatcher mp;
+	VARARR(unsigned char, ea, (ol > nl ? ol : nl) + 1);
+	int t;
+
+	if (sfx) {
+	    ow += ol; nw += nl;
+	}
+	for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
+	    mp = ms->matcher;
+	    /* We use only those patterns that match a non-empty
+	     * string in both the line and the word and that have
+	     * no anchors. */
+	    if (!mp->flags && mp->wlen > 0 && mp->llen > 0) {
+		/* We first test, if the old string matches already the
+		 * new one. */
+		if (mp->llen <= ol && mp->wlen <= nl &&
+		    pattern_match(mp->line, ow - (sfx ? mp->llen : 0),
+				  NULL, ea) &&
+		    pattern_match(mp->word, nw - (sfx ? mp->wlen : 0),
+				  ea, NULL)) {
+		    /* It did, update the contents of the cmdata struct
+		     * and return a cline for the matched part. */
+		    if (sfx)
+			md->str -= mp->wlen;
+		    else
+			md->str += mp->wlen;
+		    md->len -= mp->wlen;
+		    *mlen = mp->llen;
+
+		    return get_cline(NULL, 0, ow - (sfx ? mp->llen : 0),
+				     mp->llen, NULL, 0, 0);
+		}
+		/* Otherwise we will try to build a string that matches
+		 * both strings. But try the pattern only if the word-
+		 * pattern matches one of the strings. */
+		if (join && mp->wlen <= ol && mp->wlen <= nl &&
+		    ((t = pattern_match(mp->word, ow - (sfx ? mp->wlen : 0),
+				       NULL, ea)) ||
+		     pattern_match(mp->word, nw - (sfx ? mp->wlen : 0),
+				   NULL, ea))) {
+		    VARARR(char, line, mp->llen + 1);
+		    int bl;
+
+		    /* Then build all the possible lines and see
+		     * if one of them matches the other string. */
+		    if ((bl = bld_line(mp->line, line, line,
+				       (t ? nw : ow), (t ? nl : ol),
+				       ea, sfx))) {
+			/* Yep, one of the lines matched the other
+			 * string. */
+			line[mp->llen] = '\0';
+
+			if (t) {
+			    ol = mp->wlen; nl = bl;
+			} else {
+			    ol = bl; nl = mp->wlen;
+			}
+			if (sfx)
+			    md->str -= nl;
+			else
+			    md->str += nl;
+			md->len -= nl;
+			*mlen = ol;
+
+			return get_cline(NULL, 0, dupstring(line), mp->llen,
+					 NULL, 0, CLF_JOIN);
+		    }
+		}
+	    }
+	}
+    }
+    return NULL;
+}
+
+/* This is used to match a sub-string in a sub-cline. The length of the
+ * matched portion is returned. This tests only for exact equality. */
+
+static int
+sub_match(Cmdata md, char *str, int len, int sfx)
+{
+    int ret = 0, l, ind, add;
+    char *p, *q;
+
+    if (sfx) {
+	str += len;
+	ind = -1; add = -1;
+    } else {
+	ind = 0; add = 1;
+    }
+    /* str and len describe the old string, in md we have the new one. */
+    while (len) {
+	if (check_cmdata(md, sfx))
+	    return ret;
+
+	for (l = 0, p = str, q = md->str;
+	     l < len && l < md->len && p[ind] == q[ind];
+	     l++, p += add, q += add);
+
+	if (l) {
+	    /* There was a common prefix, use it. */
+	    md->len -= l; len -= l;
+	    if (sfx) {
+		md->str -= l; str -= l;
+	    } else {
+		md->str += l; str += l;
+	    }
+	    ret += l;
+	} else if (md->line || md->len != md->olen || !md->astr)
+	    return ret;
+	else {
+	    /* We still have the line string to try. */
+	    md->line = 1;
+	    md->len = md->alen;
+	    md->str = md->astr;
+	}
+    }
+    return ret;
+}
+
+/* This is used to build a common prefix or suffix sub-list. If requested
+ * it returns the unmatched cline lists in orest and nrest. */
+
+/**/
+static void
+join_psfx(Cline ot, Cline nt, Cline *orest, Cline *nrest, int sfx)
+{
+    Cline p = NULL, o, n;
+    struct cmdata md, omd;
+    char **sstr = NULL;
+    int len, join = 0, line = 0, *slen = NULL;
+
+    if (sfx) {
+	o = ot->suffix; n = nt->suffix;
+    } else {
+	o = ot->prefix;	n = nt->prefix;
+    }
+    if (!o) {
+	if (orest)
+	    *orest = NULL;
+	if (nrest)
+	    *nrest = n;
+
+	return;
+    }
+    if (!n) {
+	if (sfx)
+	    ot->suffix = NULL;
+	else
+	    ot->prefix = NULL;
+
+	if (orest)
+	    *orest = o;
+	else
+	    free_cline(o);
+	if (nrest)
+	    *nrest = NULL;
+	return;
+    }
+    md.cl = n;
+    md.len = 0;
+
+    /* Walk through the old list. */
+    while (o) {
+	join = 0;
+	memcpy(&omd, &md, sizeof(struct cmdata));
+
+	/* We first get the length of the prefix equal in both strings. */
+	if (o->flags & CLF_LINE) {
+	    if ((len = sub_match(&md, o->line, o->llen, sfx)) != o->llen) {
+		join = 1; line = 1; slen = &(o->llen); sstr = &(o->line);
+	    }
+	} else if ((len = sub_match(&md, o->word, o->wlen, sfx)) != o->wlen) {
+	    if (o->line) {
+		memcpy(&md, &omd, sizeof(struct cmdata));
+		o->flags |= CLF_LINE | CLF_DIFF;
+
+		continue;
+	    }
+	    join = 1; line = 0; slen = &(o->wlen); sstr = &(o->word);
+	}
+	if (join) {
+	    /* There is a rest that is different in the two lists,
+	     * we try to build a new cline matching both strings. */
+	    Cline joinl;
+	    int jlen;
+
+	    if ((joinl = join_sub(&md, *sstr + len, *slen - len,
+				  &jlen, sfx, !(o->flags & CLF_JOIN)))) {
+		/* We have one, insert it into the list. */
+		joinl->flags |= CLF_DIFF;
+		if (len + jlen != *slen) {
+		    Cline rest;
+
+		    rest = get_cline(NULL, 0, *sstr + (sfx ? 0 : len + jlen),
+				     *slen - len - jlen, NULL, 0, 0);
+
+		    rest->next = o->next;
+		    joinl->next = rest;
+		} else
+		    joinl->next = o->next;
+
+		if (len) {
+		    if (sfx)
+			*sstr += *slen - len;
+		    *slen = len;
+		    o->next = joinl;
+		} else {
+		    o->next = NULL;
+		    free_cline(o);
+		    if (p)
+			p->next = joinl;
+		    else if (sfx)
+			ot->suffix = joinl;
+		    else
+			ot->prefix = joinl;
+		}
+		o = joinl;
+		join = 0;
+	    }
+	}
+	if (join) {
+	    /* We couldn't build a cline for a common string, so we
+	     * cut the list here. */
+	    if (len) {
+		Cline r;
+
+		if (orest) {
+		    if (line)
+			r = get_cline(o->line + len, *slen - len,
+				      NULL, 0, NULL, 0, o->flags);
+		    else
+			r = get_cline(NULL, 0, o->word + len, *slen - len,
+				      NULL, 0, o->flags);
+
+		    r->next = o->next;
+		    *orest = r;
+
+		    *slen = len;
+		    o->next = NULL;
+		} else {
+		    if (sfx)
+			*sstr += *slen - len;
+		    *slen = len;
+		    free_cline(o->next);
+		    o->next = NULL;
+		}
+	    } else {
+		if (p)
+		    p->next = NULL;
+		else if (sfx)
+		    ot->suffix = NULL;
+		else
+		    ot->prefix = NULL;
+
+		if (orest)
+		    *orest = o;
+		else
+		    free_cline(o);
+	    }
+	    if (!orest || !nrest)
+		ot->flags |= CLF_MISS;
+
+	    if (nrest)
+		*nrest = undo_cmdata(&md, sfx);
+
+	    return;
+	}
+	p = o;
+	o = o->next;
+    }
+    if (md.len || md.cl)
+	ot->flags |= CLF_MISS;
+    if (orest)
+	*orest = NULL;
+    if (nrest)
+	*nrest = undo_cmdata(&md, sfx);
+}
+
+/* This builds the common prefix and suffix for a mid-cline -- the one
+ * describing the place where the prefix and the suffix meet. */
+
+/**/
+static void
+join_mid(Cline o, Cline n)
+{
+    if (o->flags & CLF_JOIN) {
+	/* The JOIN flag is set in the old cline struct if it was
+	 * already joined with another one. In this case the suffix
+	 * field contains the suffix from previous calls. */
+	Cline nr;
+
+	join_psfx(o, n, NULL, &nr, 0);
+
+	n->suffix = revert_cline(nr);
+
+	join_psfx(o, n, NULL, NULL, 1);
+    } else {
+	/* This is the first time for both structs, so the prefix field
+	 * contains the whole sub-list. */
+	Cline or, nr;
+
+	o->flags |= CLF_JOIN;
+
+	/* We let us give both rests and use them as the suffixes. */
+	join_psfx(o, n, &or, &nr, 0);
+
+	if (or)
+	    or->llen = (o->slen > or->wlen ? or->wlen : o->slen);
+	o->suffix = revert_cline(or);
+	n->suffix = revert_cline(nr);
+
+	join_psfx(o, n, NULL, NULL, 1);
+    }
+    n->suffix = NULL;
+}
+
+/* This turns the sequence of anchor cline structs from b to e into a
+ * prefix sequence, puts it before the prefix of e and then tries to
+ * join that with the prefix of a.
+ * This is needed if some matches had a anchor match spec and others
+ * didn't. */
+
+/**/
+static void
+sub_join(Cline a, Cline b, Cline e, int anew)
+{
+    if (!e->suffix && a->prefix) {
+	Cline op = e->prefix, n = NULL, *p = &n, t, ca;
+	int min = 0, max = 0;
+
+	for (; b != e; b = b->next) {
+	    if ((*p = t = b->prefix)) {
+		while (t->next)
+		    t = t->next;
+		p = &(t->next);
+	    }
+	    b->suffix = b->prefix = NULL;
+	    b->flags &= ~CLF_SUF;
+	    min += b->min;
+	    max += b->max;
+	    *p = b;
+	    p = &(b->next);
+	}
+	*p = e->prefix;
+	ca = a->prefix;
+
+	while (n != op) {
+	    e->prefix = cp_cline(n, 0);
+	    a->prefix = cp_cline(ca, 0);
+
+	    if (anew) {
+		join_psfx(e, a, NULL, NULL, 0);
+		if (e->prefix) {
+		    e->min += min;
+		    e->max += max;
+		    break;
+		}
+	    } else {
+		join_psfx(e, a, NULL, NULL, 0);
+		if (a->prefix) {
+		    a->min += min;
+		    a->max += max;
+		    break;
+		}
+	    }
+	    min -= n->min;
+	    max -= n->max;
+
+	    n = n->next;
+	}
+    }
+}
+
+/* This simplifies the cline list given as the first argument so that
+ * it also matches the second list. */
+
+/**/
+Cline
+join_clines(Cline o, Cline n)
+{
+    cline_setlens(n, 1);
+
+    /* First time called, just return the new list. On further invocations
+     * we will get it as the first argument. */
+    if (!o)
+	return n;
+    else {
+	Cline oo = o, nn = n, po = NULL, pn = NULL;
+
+	/* Walk through the lists. */
+	while (o && n) {
+	    /* If one of them describes a new part and the other one does
+	     * not, synchronise them by searching an old part in the
+	     * other list. */
+	    if ((o->flags & CLF_NEW) && !(n->flags & CLF_NEW)) {
+		Cline t, tn;
+
+		for (t = o; (tn = t->next) && (tn->flags & CLF_NEW); t = tn);
+		if (tn && cmp_anchors(tn, n, 0)) {
+		    sub_join(n, o, tn, 1);
+
+		    if (po)
+			po->next = tn;
+		    else
+			oo = tn;
+		    t->next = NULL;
+		    free_cline(o);
+		    o = tn;
+		    o->flags |= CLF_MISS;
+		    continue;
+		}
+	    }
+	    if (!(o->flags & CLF_NEW) && (n->flags & CLF_NEW)) {
+		Cline t, tn;
+
+		for (t = n; (tn = t->next) && (tn->flags & CLF_NEW); t = tn);
+		if (tn && cmp_anchors(o, tn, 0)) {
+		    sub_join(o, n, tn, 0);
+
+		    n = tn;
+		    o->flags |= CLF_MISS;
+		    continue;
+		}
+	    }
+	    /* Almost the same as above, but for the case that they
+	     * describe different types of parts (prefix, suffix, or mid). */
+	    if ((o->flags & (CLF_SUF | CLF_MID)) !=
+		(n->flags & (CLF_SUF | CLF_MID))) {
+		Cline t, tn;
+
+		for (t = n;
+		     (tn = t->next) &&
+			 (tn->flags & (CLF_SUF | CLF_MID)) !=
+			 (o->flags  & (CLF_SUF | CLF_MID));
+		     t = tn);
+		if (tn && cmp_anchors(o, tn, 1)) {
+		    sub_join(o, n, tn, 0);
+
+		    n = tn;
+		    continue;
+		}
+		for (t = o;
+		     (tn = t->next) &&
+			 (tn->flags & (CLF_SUF | CLF_MID)) !=
+			 (n->flags  & (CLF_SUF | CLF_MID));
+		     t = tn);
+		if (tn && cmp_anchors(tn, n, 1)) {
+		    sub_join(n, o, tn, 1);
+		    if (po)
+			po->next = tn;
+		    else
+			oo = tn;
+		    t->next = NULL;
+		    free_cline(o);
+		    o = tn;
+		    continue;
+		}
+		if (o->flags & CLF_MID) {
+		    o->flags = (o->flags & ~CLF_MID) | (n->flags & CLF_SUF);
+		    if (n->flags & CLF_SUF) {
+			free_cline(o->prefix);
+			o->prefix = NULL;
+		    } else {
+			free_cline(o->suffix);
+			o->suffix = NULL;
+		    }
+		}
+		break;
+	    }
+	    /* Now see if they have matching anchors. If not, cut the list. */
+	    if (!(o->flags & CLF_MID) && !cmp_anchors(o, n, 1)) {
+		Cline t, tn;
+
+		for (t = n; (tn = t->next) && !cmp_anchors(o, tn, 1); t = tn);
+
+		if (tn) {
+		    sub_join(o, n, tn, 0);
+
+		    n = tn;
+		    o->flags |= CLF_MISS;
+		    continue;
+		} else {
+		    for (t = o; (tn = t->next) && !cmp_anchors(n, tn, 1);
+			 t = tn);
+
+		    if (tn) {
+			sub_join(n, o, tn, 1);
+
+			if (po)
+			    po->next = tn;
+			else
+			    oo = tn;
+			o = tn;
+			o->flags |= CLF_MISS;
+			continue;
+		    } else {
+			if (o->flags & CLF_SUF)
+			    break;
+
+			o->word = o->line = o->orig = NULL;
+			o->wlen = 0;
+			free_cline(o->next);
+			o->next = NULL;
+			o->flags |= CLF_MISS;
+		    }
+		}
+	    }
+	    /* Ok, they are equal, now copy the information about the
+             * original string if needed, calculate minimum and maximum
+	     * lengths, and join the sub-lists. */
+	    if (!o->orig && !o->olen) {
+		o->orig = n->orig;
+		o->olen = n->olen;
+	    }
+	    if (n->min < o->min)
+		o->min = n->min;
+	    if (n->max > o->max)
+		o->max = n->max;
+	    if (o->flags & CLF_MID)
+		join_mid(o, n);
+	    else
+		join_psfx(o, n, NULL, NULL, (o->flags & CLF_SUF));
+
+	    po = o;
+	    o = o->next;
+	    pn = n;
+	    n = n->next;
+	}
+	/* Free the rest of the old list. */
+	if (o) {
+	    if (po)
+		po->next = NULL;
+	    else
+		oo = NULL;
+
+	    free_cline(o);
+	}
+	free_cline(nn);
+
+	return oo;
+    }
+}
+
diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c
new file mode 100644
index 000000000..fe997b12b
--- /dev/null
+++ b/Src/Zle/compresult.c
@@ -0,0 +1,1940 @@
+/*
+ * compresult.c - the complete module, completion result handling
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1999 Sven Wischnowsky
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Sven Wischnowsky or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Sven Wischnowsky and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Sven Wischnowsky and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose.  The software
+ * provided hereunder is on an "as is" basis, and Sven Wischnowsky and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "complete.mdh"
+#define GLOBAL_PROTOTYPES
+#include "zle_tricky.pro"
+#undef GLOBAL_PROTOTYPES
+#include "compresult.pro"
+
+/* Convenience macro for calling bslashquote() (formerly quotename()). *
+ * This uses the instring variable above.                              */
+
+#define quotename(s, e) bslashquote(s, e, instring)
+
+#define inststr(X) inststrlen((X),1,-1)
+
+/* This cuts the cline list before the stuff that isn't worth
+ * inserting in the line. */
+
+/**/
+static Cline
+cut_cline(Cline l)
+{
+    Cline q, p, e = NULL, maxp = NULL;
+    int sum = 0, max = 0, tmp, ls = 0;
+
+    /* If no match was added with matching, we don't really know
+     * which parts of the unambiguous string are worth keeping,
+     * so for now we keep everything (in the hope that this
+     * produces a string containing at least everything that was 
+     * originally on the line). */
+
+    if (!hasmatched) {
+	cline_setlens(l, 0);
+	return l;
+    }
+    e = l = cp_cline(l, 0);
+
+    /* First, search the last struct for which we have something on
+     * the line. Anything before that is kept. */
+
+    for (q = NULL, p = l; p; p = p->next) {
+	if (p->orig || p->olen || !(p->flags & CLF_NEW))
+	    e = p->next;
+	if (!p->suffix && (p->wlen || p->llen || p->prefix))
+	    q = p;
+    }
+    if (!e && q && !q->orig && !q->olen && (q->flags & CLF_MISS) &&
+	!(q->flags & CLF_MATCHED) && (q->word ? q->wlen : q->llen) < 3) {
+	q->word = q->line = NULL;
+	q->wlen = q->llen = 0;
+    }
+    /* Then keep all structs without missing characters. */
+
+    while (e && !(e->flags & CLF_MISS))
+	e = e->next;
+
+    if (e) {
+	/* Then we see if there is another struct with missing
+	 * characters. If not, we keep the whole list. */
+
+	for (p = e->next; p && !(p->flags & CLF_MISS); p = p->next);
+
+	if (p) {
+	    for (p = e; p; p = p->next) {
+		if (!(p->flags & CLF_MISS))
+		    sum += p->max;
+		else {
+		    tmp = cline_sublen(p);
+		    if (tmp > 2 && tmp > ((p->max + p->min) >> 1))
+			sum += tmp - (p->max - tmp);
+		    else if (tmp < p->min)
+			sum -= (((p->max + p->min) >> 1) - tmp) << (tmp < 2);
+		}
+		if (sum > max) {
+		    max = sum;
+		    maxp = p;
+		}
+	    }
+	    if (max)
+		e = maxp;
+	    else {
+		int len = 0;
+
+		cline_setlens(l, 0);
+		ls = 1;
+
+		for (p = e; p; p = p->next)
+		    len += p->max;
+
+		if (len > ((minmlen << 1) / 3))
+		    return l;
+	    }
+	    e->line = e->word = NULL;
+	    e->llen = e->wlen = e->olen = 0;
+	    e->next = NULL;
+	}
+    }
+    if (!ls)
+	cline_setlens(l, 0);
+
+    return l;
+}
+
+/* This builds the unambiguous string. If ins is non-zero, it is
+ * immediatly inserted in the line. Otherwise csp is used to return
+ * the relative cursor position in the string returned. */
+
+/**/
+static char *
+cline_str(Cline l, int ins, int *csp)
+{
+    Cline s;
+    int ocs = cs, ncs, pcs, scs, pm, pmax, pmm, sm, smax, smm, d, dm, mid;
+    int i, j, li = 0, cbr;
+    Brinfo brp, brs;
+
+    l = cut_cline(l);
+
+    pmm = smm = dm = 0;
+    pm = pmax = sm = smax = d = mid = cbr = -1;
+
+    /* Get the information about the brace beginning and end we have
+     * to re-insert. */
+    if (ins) {
+	Brinfo bp;
+	int olen = we - wb;
+
+	if ((brp = brbeg)) {
+	    for (bp = brbeg; bp; bp = bp->next) {
+		bp->curpos = (hasunqu ? bp->pos : bp->qpos);
+		olen -= strlen(bp->str);
+	    }
+	}
+	if ((brs = lastbrend)) {
+	    for (bp = brend; bp; bp = bp->next)
+		olen -= strlen(bp->str);
+
+	    for (bp = brend; bp; bp = bp->next)
+		bp->curpos = olen - (hasunqu ? bp->pos : bp->qpos);
+	}
+	while (brp && !brp->curpos) {
+	    inststrlen(brp->str, 1, -1);
+	    brp = brp->next;
+	}
+	while (brs && !brs->curpos) {
+	    if (cbr < 0)
+		cbr = cs;
+	    inststrlen(brs->str, 1, -1);
+	    brs = brs->prev;
+	}
+    }
+    /* Walk through the top-level cline list. */
+    while (l) {
+	/* Insert the original string if no prefix. */
+	if (l->olen && !(l->flags & CLF_SUF) && !l->prefix) {
+	    pcs = cs + l->olen;
+	    inststrlen(l->orig, 1, l->olen);
+	} else {
+	    /* Otherwise insert the prefix. */
+	    for (s = l->prefix; s; s = s->next) {
+		pcs = cs + s->llen;
+		if (s->flags & CLF_LINE)
+		    inststrlen(s->line, 1, s->llen);
+		else
+		    inststrlen(s->word, 1, s->wlen);
+		scs = cs;
+
+		if ((s->flags & CLF_DIFF) && (!dm || (s->flags & CLF_MATCHED))) {
+		    d = cs; dm = s->flags & CLF_MATCHED;
+		}
+		li += s->llen;
+	    }
+	}
+	if (ins) {
+	    int ocs, bl;
+
+	    while (brp && li >= brp->curpos) {
+		ocs = cs;
+		bl = strlen(brp->str);
+		cs = pcs - (li - brp->curpos);
+		inststrlen(brp->str, 1, bl);
+		cs = ocs + bl;
+		pcs += bl;
+		scs += bl;
+		brp = brp->next;
+	    }
+	}
+	/* Remember the position if this is the first prefix with
+	 * missing characters. */
+	if ((l->flags & CLF_MISS) && !(l->flags & CLF_SUF) &&
+	    ((pmax < (l->min - l->max) && (!pmm || (l->flags & CLF_MATCHED))) ||
+	     ((l->flags & CLF_MATCHED) && !pmm))) {
+	    pm = cs; pmax = l->min - l->max; pmm = l->flags & CLF_MATCHED;
+	}
+	if (ins) {
+	    int ocs, bl;
+
+	    while (brs && li >= brs->curpos) {
+		ocs = cs;
+		bl = strlen(brs->str);
+		cs = scs - (li - brs->curpos);
+		if (cbr < 0)
+		    cbr = cs;
+		inststrlen(brs->str, 1, bl);
+		cs = ocs + bl;
+		pcs += bl;
+		brs = brs->prev;
+	    }
+	}
+	pcs = cs;
+	/* Insert the anchor. */
+	if (l->flags & CLF_LINE)
+	    inststrlen(l->line, 1, l->llen);
+	else
+	    inststrlen(l->word, 1, l->wlen);
+	scs = cs;
+	if (ins) {
+	    int ocs, bl;
+
+	    li += l->llen;
+
+	    while (brp && li >= brp->curpos) {
+		ocs = cs;
+		bl = strlen(brp->str);
+		cs = pcs + l->llen - (li - brp->curpos);
+		inststrlen(brp->str, 1, bl);
+		cs = ocs + bl;
+		pcs += bl;
+		scs += bl;
+		brp = brp->next;
+	    }
+	}
+	/* Remember the cursor position for suffixes and mids. */
+	if (l->flags & CLF_MISS) {
+	    if (l->flags & CLF_MID)
+		mid = cs;
+	    else if ((l->flags & CLF_SUF) && 
+		     ((smax < (l->min - l->max) &&
+		       (!smm || (l->flags & CLF_MATCHED))) ||
+		      ((l->flags & CLF_MATCHED) && !smm))) {
+		sm = cs; smax = l->min - l->max; smm = l->flags & CLF_MATCHED;
+	    }
+	}
+	if (ins) {
+	    int ocs, bl;
+
+	    while (brs && li >= brs->curpos) {
+		ocs = cs;
+		bl = strlen(brs->str);
+		cs = scs - (li - brs->curpos);
+		if (cbr < 0)
+		    cbr = cs;
+		inststrlen(brs->str, 1, bl);
+		cs = ocs + bl;
+		pcs += bl;
+		brs = brs->prev;
+	    }
+	}
+	/* And now insert the suffix or the original string. */
+	if (l->olen && (l->flags & CLF_SUF) && !l->suffix) {
+	    pcs = cs;
+	    inststrlen(l->orig, 1, l->olen);
+	    if (ins) {
+		int ocs, bl;
+
+		li += l->olen;
+
+		while (brp && li >= brp->curpos) {
+		    ocs = cs;
+		    bl = strlen(brp->str);
+		    cs = pcs + l->olen - (li - brp->curpos);
+		    inststrlen(brp->str, 1, bl);
+		    cs = ocs + bl;
+		    pcs += bl;
+		    brp = brp->next;
+		}
+		while (brs && li >= brs->curpos) {
+		    ocs = cs;
+		    bl = strlen(brs->str);
+		    cs = pcs + l->olen - (li - brs->curpos);
+		    if (cbr < 0)
+			cbr = cs;
+		    inststrlen(brs->str, 1, bl);
+		    cs = ocs + bl;
+		    pcs += bl;
+		    brs = brs->prev;
+		}
+	    }
+	} else {
+	    Cline js = NULL;
+
+	    for (j = -1, i = 0, s = l->suffix; s; s = s->next) {
+		if (j < 0 && (s->flags & CLF_DIFF))
+		    j = i, js = s;
+		pcs = cs;
+		if (s->flags & CLF_LINE) {
+		    inststrlen(s->line, 0, s->llen);
+		    i += s->llen; scs = cs + s->llen;
+		} else {
+		    inststrlen(s->word, 0, s->wlen);
+		    i += s->wlen; scs = cs + s->wlen;
+		}
+		if (ins) {
+		    int ocs, bl;
+
+		    li += s->llen;
+
+		    while (brp && li >= brp->curpos) {
+			ocs = cs;
+			bl = strlen(brp->str);
+			cs = pcs + (li - brp->curpos);
+			inststrlen(brp->str, 1, bl);
+			cs = ocs + bl;
+			pcs += bl;
+			scs += bl;
+			brp = brp->next;
+		    }
+		    while (brs && li >= brs->curpos) {
+			ocs = cs;
+			bl = strlen(brs->str);
+			cs = scs - (li - brs->curpos);
+			if (cbr < 0)
+			    cbr = cs;
+			inststrlen(brs->str, 1, bl);
+			cs = ocs + bl;
+			pcs += bl;
+			brs = brs->prev;
+		    }
+		}
+	    }
+	    cs += i;
+	    if (j >= 0 && (!dm || (js->flags & CLF_MATCHED))) {
+		d = cs - j; dm = js->flags & CLF_MATCHED;
+	    }
+	}
+	l = l->next;
+    }
+    if (ins) {
+	int ocs = cs;
+
+	for (; brp; brp = brp->next)
+	    inststrlen(brp->str, 1, -1);
+	for (; brs; brs = brs->prev) {
+	    if (cbr < 0)
+		cbr = cs;
+	    inststrlen(brs->str, 1, -1);
+	}
+	if (mid >= ocs)
+	    mid += cs - ocs;
+	if (pm >= ocs)
+	    pm += cs - ocs;
+	if (sm >= ocs)
+	    sm += cs - ocs;
+	if (d >= ocs)
+	    d += cs - ocs;
+    }
+    /* This calculates the new cursor position. If we had a mid cline
+     * with missing characters, we take this, otherwise if we have a
+     * prefix with missing characters, we take that, the same for a
+     * suffix, and finally a place where the matches differ. */
+    ncs = (cbr >= 0 ? cbr :
+	   (mid >= 0 ? mid :
+	    (pm >= 0 ? pm : (sm >= 0 ? sm : (d >= 0 ? d : cs)))));
+
+    if (!ins) {
+	/* We always inserted the string in the line. If that was not
+	 * requested, we copy it and remove from the line. */
+	char *r = zalloc((i = cs - ocs) + 1);
+
+	memcpy(r, (char *) (line + ocs), i);
+	r[i] = '\0';
+	cs = ocs;
+	foredel(i);
+
+	*csp = ncs - ocs;
+
+	return r;
+    }
+    lastend = cs;
+    cs = ncs;
+
+    return NULL;
+}
+
+/* This is a utility function using the function above to allow access
+ * to the unambiguous string and cursor position via compstate. */
+
+/**/
+char *
+unambig_data(int *cp)
+{
+    static char *scache = NULL;
+    static int ccache;
+
+    if (mnum && ainfo) {
+	if (mnum != unambig_mnum) {
+	    zsfree(scache);
+	    scache = cline_str((ainfo->count ? ainfo->line : fainfo->line),
+			       0, &ccache);
+	}
+    } else if (mnum != unambig_mnum || !ainfo || !scache) {
+	zsfree(scache);
+	scache = ztrdup("");
+	ccache = 0;
+    }
+    unambig_mnum = mnum;
+    if (cp)
+	*cp = ccache + 1;
+
+    return scache;
+}
+
+/* Insert the given match. This returns the number of characters inserted.
+ * scs is used to return the position where a automatically created suffix
+ * has to be inserted. */
+
+/**/
+static int
+instmatch(Cmatch m, int *scs)
+{
+    int l, r = 0, ocs, a = cs, brb = 0, bradd, *brpos;
+    Brinfo bp;
+
+    zsfree(lastprebr);
+    zsfree(lastpostbr);
+    lastprebr = lastpostbr = NULL;
+
+    /* Ignored prefix. */
+    if (m->ipre) {
+	char *p = m->ipre + (menuacc ? m->qipl : 0);
+
+	inststrlen(p, 1, (l = strlen(p)));
+	r += l;
+    }
+    /* -P prefix. */
+    if (m->pre) {
+	inststrlen(m->pre, 1, (l = strlen(m->pre)));
+	r += l;
+    }
+    /* Path prefix. */
+    if (m->ppre) {
+	inststrlen(m->ppre, 1, (l = strlen(m->ppre)));
+	r += l;
+    }
+    /* The string itself. */
+    inststrlen(m->str, 1, (l = strlen(m->str)));
+    r += l;
+    ocs = cs;
+    /* Re-insert the brace beginnings, if any. */
+    if (brbeg) {
+	int pcs = cs;
+
+	l = 0;
+	for (bp = brbeg, brpos = m->brpl,
+		 bradd = (m->pre ? strlen(m->pre) : 0);
+	     bp; bp = bp->next, brpos++) {
+	    cs = a + *brpos + bradd;
+	    pcs = cs;
+	    l = strlen(bp->str);
+	    bradd += l;
+	    brpcs = cs;
+	    inststrlen(bp->str, 1, l);
+	    r += l;
+	    ocs += l;
+	}
+	lastprebr = (char *) zalloc(pcs - a + 1);
+	memcpy(lastprebr, (char *) line + a, pcs - a);
+	lastprebr[pcs - a] = '\0';
+	cs = ocs;
+    }
+    /* Path suffix. */
+    if (m->psuf) {
+	inststrlen(m->psuf, 1, (l = strlen(m->psuf)));
+	r += l;
+    }
+    /* Re-insert the brace end. */
+    if (brend) {
+	a = cs;
+	for (bp = brend, brpos = m->brsl, bradd = 0; bp; bp = bp->next, brpos++) {
+	    cs = a - *brpos;
+	    ocs = brscs = cs;
+	    l = strlen(bp->str);
+	    bradd += l;
+	    inststrlen(bp->str, 1, l);
+	    brb = cs;
+	    r += l;
+	}
+	cs = a + bradd;
+	if (scs)
+	    *scs = ocs;
+    } else {
+	brscs = -1;
+
+	if (scs)
+	    *scs = cs;
+    }
+    /* -S suffix */
+    if (m->suf) {
+	inststrlen(m->suf, 1, (l = strlen(m->suf)));
+	r += l;
+    }
+    /* ignored suffix */
+    if (m->isuf) {
+	inststrlen(m->isuf, 1, (l = strlen(m->isuf)));
+	r += l;
+    }
+    if (brend) {
+	lastpostbr = (char *) zalloc(cs - brb + 1);
+	memcpy(lastpostbr, (char *) line + brb, cs - brb);
+	lastpostbr[cs - brb] = '\0';
+    }
+    lastend = cs;
+    cs = ocs;
+
+    return r;
+}
+
+/* Check if the match has the given prefix/suffix before/after the
+ * braces. */
+
+/**/
+int
+hasbrpsfx(Cmatch m, char *pre, char *suf)
+{
+    char *op = lastprebr, *os = lastpostbr;
+    VARARR(char, oline, ll);
+    int oll = ll, ocs = cs, ole = lastend, opcs = brpcs, oscs = brscs, ret;
+
+    memcpy(oline, line, ll);
+
+    lastprebr = lastpostbr = NULL;
+
+    instmatch(m, NULL);
+
+    cs = 0;
+    foredel(ll);
+    spaceinline(oll);
+    memcpy(line, oline, oll);
+    cs = ocs;
+    lastend = ole;
+    brpcs = opcs;
+    brscs = oscs;
+
+    ret = (((!pre && !lastprebr) ||
+	    (pre && lastprebr && !strcmp(pre, lastprebr))) &&
+	   ((!suf && !lastpostbr) ||
+	    (suf && lastpostbr && !strcmp(suf, lastpostbr))));
+
+    zsfree(lastprebr);
+    zsfree(lastpostbr);
+    lastprebr = op;
+    lastpostbr = os;
+
+    return ret;
+}
+
+/* Handle the case were we found more than one match. */
+
+/**/
+int
+do_ambiguous(void)
+{
+    int ret = 0;
+
+    menucmp = menuacc = 0;
+
+    /* If we have to insert the first match, call do_single().  This is *
+     * how REC_EXACT takes effect.  We effectively turn the ambiguous   *
+     * completion into an unambiguous one.                              */
+    if (ainfo && ainfo->exact == 1 && useexact && !(fromcomp & FC_LINE)) {
+	minfo.cur = NULL;
+	do_single(ainfo->exactm);
+	invalidatelist();
+	return ret;
+    }
+    /* Setting lastambig here means that the completion is ambiguous and *
+     * AUTO_MENU might want to start a menu completion next time round,  *
+     * but this might be overridden below if we can complete an          *
+     * unambiguous prefix.                                               */
+    lastambig = 1;
+
+    if (usemenu || (haspattern && comppatinsert &&
+		    !strcmp(comppatinsert, "menu"))) {
+	/* We are in a position to start using menu completion due to one  *
+	 * of the menu completion options, or due to the menu-complete-    *
+	 * word command, or due to using GLOB_COMPLETE which does menu-    *
+	 * style completion regardless of the setting of the normal menu   *
+	 * completion options.                                             */
+	do_ambig_menu();
+    } else if (ainfo) {
+	int atend = (cs == we), la, eq, tcs;
+
+	minfo.cur = NULL;
+	minfo.asked = 0;
+
+	fixsuffix();
+
+	/* First remove the old string from the line. */
+	cs = wb;
+	foredel(we - wb);
+
+	/* Now get the unambiguous string and insert it into the line. */
+	cline_str(ainfo->line, 1, NULL);
+	if (eparq) {
+	    tcs = cs;
+	    cs = lastend;
+	    for (eq = eparq; eq; eq--)
+		inststrlen("\"", 0, 1);
+	    cs = tcs;
+	}
+	/* la is non-zero if listambiguous may be used. Copying and
+	 * comparing the line looks like BFI but it is the easiest
+	 * solution. Really. */
+	la = (ll != origll || strncmp(origline, (char *) line, ll));
+
+	/* If REC_EXACT and AUTO_MENU are set and what we inserted is an  *
+	 * exact match, we want menu completion the next time round       *
+	 * so we set fromcomp, to ensure that the word on the line is not *
+	 * taken as an exact match. Also we remember if we just moved the *
+	 * cursor into the word.                                          */
+	fromcomp = ((isset(AUTOMENU) ? FC_LINE : 0) |
+		    ((atend && cs != lastend) ? FC_INWORD : 0));
+
+	/* Probably move the cursor to the end. */
+	if (movetoend == 3)
+	    cs = lastend;
+
+	/* 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 (uselist == 3 && la) {
+	    int fc = fromcomp;
+
+	    invalidatelist();
+	    fromcomp = fc;
+	    lastambig = 0;
+	    clearlist = 1;
+	    return ret;
+	}
+    } else
+	return ret;
+
+    /* At this point, we might want a completion listing.  Show the listing *
+     * if it is needed.                                                     */
+    if (isset(LISTBEEP))
+	ret = 1;
+
+    if (uselist && (usemenu != 2 || (!listshown && !oldlist)) &&
+	((!showinglist && (!listshown || !oldlist)) ||
+	 (usemenu == 3 && !oldlist)) &&
+	(smatches >= 2 || (compforcelist && *compforcelist)))
+	showinglist = -2;
+
+    return ret;
+}
+
+/* This is a stat that ignores backslashes in the filename.  The `ls' *
+ * parameter says if we have to do lstat() or stat().  I think this   *
+ * should instead be done by use of a general function to expand a    *
+ * filename (stripping backslashes), combined with the actual         *
+ * (l)stat().                                                         */
+
+/**/
+int
+ztat(char *nam, struct stat *buf, int ls)
+{
+    char b[PATH_MAX], *p;
+
+    for (p = b; p < b + sizeof(b) - 1 && *nam; nam++)
+	if (*nam == '\\' && nam[1])
+	    *p++ = *++nam;
+	else
+	    *p++ = *nam;
+    *p = '\0';
+
+    return ls ? lstat(b, buf) : stat(b, buf);
+}
+
+/* Insert a single match in the command line. */
+
+/**/
+void
+do_single(Cmatch m)
+{
+    int l, sr = 0, scs;
+    int havesuff = 0;
+    int partest = (m->ripre || ((m->flags & CMF_ISPAR) && parpre));
+    char *str = m->str, *ppre = m->ppre, *psuf = m->psuf, *prpre = m->prpre;
+
+    if (!prpre) prpre = "";
+    if (!ppre) ppre = "";
+    if (!psuf) psuf = "";
+
+    fixsuffix();
+
+    if (!minfo.cur) {
+	/* We are currently not in a menu-completion, *
+	 * so set the position variables.             */
+	minfo.pos = wb;
+	minfo.we = (movetoend >= 2 || (movetoend == 1 && !menucmp));
+	minfo.end = 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 (minfo.cur)
+	l = minfo.len + minfo.insc;
+    else
+	l = we - wb;
+
+    minfo.insc = 0;
+    cs = minfo.pos;
+    foredel(l);
+
+    /* And then we insert the new string. */
+    minfo.len = instmatch(m, &scs);
+    minfo.end = cs;
+    cs = minfo.pos + minfo.len;
+
+    if (m->suf) {
+	havesuff = 1;
+	minfo.insc = ztrlen(m->suf);
+	minfo.len -= minfo.insc;
+	if (minfo.we) {
+	    minfo.end += minfo.insc;
+	    if (m->flags & CMF_REMOVE) {
+		makesuffixstr(m->remf, m->rems, minfo.insc);
+		if (minfo.insc == 1)
+		    suffixlen[STOUC(m->suf[0])] = 1;
+	    }
+	}
+    } else {
+	/* There is no user-specified suffix, *
+	 * so generate one automagically.     */
+	cs = scs;
+	if (partest && (m->flags & CMF_PARBR)) {
+	    int pq;
+
+	    /*{{*/
+	    /* Completing a parameter in braces.  Add a removable `}' suffix. */
+	    cs += eparq;
+	    for (pq = parq; pq; pq--)
+		inststrlen("\"", 1, 1);
+	    minfo.insc += parq;
+	    inststrlen("}", 1, 1);
+	    minfo.insc++;
+	    if (minfo.we)
+		minfo.end += minfo.insc;
+	    if (m->flags & CMF_PARNEST)
+		havesuff = 1;
+	}
+	if (((m->flags & CMF_FILE) || (partest && isset(AUTOPARAMSLASH))) &&
+	    cs > 0 && line[cs - 1] != '/') {
+	    /* 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.                                */
+	    struct stat buf;
+	    char *p;
+	    int t = 0;
+
+	    if (m->ipre && m->ipre[0] == '~' && !m->ipre[1])
+		t = 1;
+	    else {
+		/* Build the path name. */
+		if (partest && !*psuf && !(m->flags & CMF_PARNEST)) {
+		    int ne = noerrs;
+
+		    p = (char *) zhalloc(strlen((m->flags & CMF_ISPAR) ?
+						parpre : m->ripre) +
+					 strlen(str) + 2);
+		    sprintf(p, "%s%s%c",
+			    ((m->flags & CMF_ISPAR) ? parpre : m->ripre), str,
+			    ((m->flags & CMF_PARBR) ? Outbrace : '\0'));
+		    noerrs = 1;
+		    parsestr(p);
+		    singsub(&p);
+		    errflag = 0;
+		    noerrs = ne;
+		} else {
+		    p = (char *) zhalloc(strlen(prpre) + strlen(str) +
+				 strlen(psuf) + 3);
+		    sprintf(p, "%s%s%s", ((prpre && *prpre) ?
+					  prpre : "./"), str, psuf);
+		}
+		/* And do the stat. */
+		t = (!(sr = ztat(p, &buf, 0)) && S_ISDIR(buf.st_mode));
+	    }
+	    if (t) {
+		/* It is a directory, so add the slash. */
+		havesuff = 1;
+		inststrlen("/", 1, 1);
+		minfo.insc++;
+		if (minfo.we)
+		    minfo.end++;
+		if (!menucmp || minfo.we) {
+		    if (m->remf || m->rems)
+			makesuffixstr(m->remf, m->rems, 1);
+		    else if (isset(AUTOREMOVESLASH)) {
+			makesuffix(1);
+			suffixlen['/'] = 1;
+		    }
+		}
+	    }
+	}
+	if (!minfo.insc)
+	    cs = minfo.pos + minfo.len - m->qisl;
+    }
+    /* If completing in a brace expansion... */
+    if (brbeg) {
+	if (havesuff) {
+	    /*{{*/
+	    /* If a suffix was added, and is removable, let *
+	     * `,' and `}' remove it.                       */
+	    if (isset(AUTOPARAMKEYS))
+		suffixlen[','] = suffixlen['}'] = suffixlen[256];
+	} else if (!menucmp) {
+	    /*{{*/
+	    /* Otherwise, add a `,' suffix, and let `}' remove it. */
+	    cs = scs;
+	    havesuff = 1;
+	    inststrlen(",", 1, 1);
+	    minfo.insc++;
+	    makesuffix(1);
+	    if ((!menucmp || minfo.we) && isset(AUTOPARAMKEYS))
+		suffixlen[','] = suffixlen['}'] = 1;
+	}
+    } else if (!havesuff && (!(m->flags & CMF_FILE) || !sr)) {
+	/* If we didn't add a suffix, add a space, unless we are *
+	 * doing menu completion or we are completing files and  *
+	 * the string doesn't name an existing file.             */
+	if (m->autoq && (!m->isuf || m->isuf[0] != m->autoq)) {
+	    inststrlen(&(m->autoq), 1, 1);
+	    minfo.insc++;
+	}
+	if (!menucmp && (usemenu != 3 || insspace)) {
+	    inststrlen(" ", 1, 1);
+	    minfo.insc++;
+	    if (minfo.we)
+		makesuffix(1);
+	}
+    }
+    if (minfo.we && partest && isset(AUTOPARAMKEYS))
+	makeparamsuffix(((m->flags & CMF_PARBR) ? 1 : 0), minfo.insc - parq);
+
+    if ((menucmp && !minfo.we) || !movetoend) {
+	cs = minfo.end;
+	if (cs + m->qisl == lastend)
+	    cs += minfo.insc;
+    }
+    {
+	Cmatch *om = minfo.cur;
+	struct chdata dat;
+
+	dat.matches = amatches;
+	dat.num = nmatches;
+	dat.cur = m;
+
+	if (menucmp)
+	    minfo.cur = &m;
+	runhookdef(INSERTMATCHHOOK, (void *) &dat);
+	minfo.cur = om;
+    }
+}
+
+/* Do completion, given that we are in the middle of a menu completion.  We *
+ * don't need to generate a list of matches, because that's already been    *
+ * done by previous commands.  We will either list the completions, or      *
+ * insert the next completion.                                              */
+
+/**/
+void
+do_menucmp(int lst)
+{
+    /* Just list the matches if the list was requested. */
+    if (lst == COMP_LIST_COMPLETE) {
+	showinglist = -2;
+	return;
+    }
+    /* Otherwise go to the next match in the array... */
+    HEAPALLOC {
+	do {
+	    if (!*++(minfo.cur)) {
+		do {
+		    if (!(minfo.group = (minfo.group)->next))
+			minfo.group = amatches;
+		} while (!(minfo.group)->mcount);
+		minfo.cur = minfo.group->matches;
+	    }
+	} while (menuacc &&
+		 !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr));
+	/* ... and insert it into the command line. */
+	metafy_line();
+	do_single(*(minfo.cur));
+	unmetafy_line();
+    } LASTALLOC;
+}
+
+/**/
+int
+reverse_menu(Hookdef dummy, void *dummy2)
+{
+    HEAPALLOC {
+	do {
+	    if (minfo.cur == (minfo.group)->matches) {
+		do {
+		    if (!(minfo.group = (minfo.group)->prev))
+			minfo.group = lmatches;
+		} while (!(minfo.group)->mcount);
+		minfo.cur = (minfo.group)->matches + (minfo.group)->mcount - 1;
+	    } else
+		minfo.cur--;
+	} while (menuacc &&
+		 !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr));
+	metafy_line();
+	do_single(*(minfo.cur));
+	unmetafy_line();
+    } LASTALLOC;
+
+    return 0;
+}
+
+/* Accepts the current completion and starts a new arg, *
+ * with the next completions. This gives you a way to   *
+ * accept several selections from the list of matches.  */
+
+/**/
+int
+accept_last(void)
+{
+    if (!menuacc) {
+	zsfree(minfo.prebr);
+	minfo.prebr = ztrdup(lastprebr);
+	zsfree(minfo.postbr);
+	minfo.postbr = ztrdup(lastpostbr);
+
+	if (listshown && (lastprebr || lastpostbr)) {
+	    Cmgroup g;
+	    Cmatch *m;
+
+	    for (g = amatches, m = NULL; g && (!m || !*m); g = g->next)
+		for (m = g->matches; *m; m++)
+		    if (!hasbrpsfx(*m, minfo.prebr, minfo.postbr)) {
+			showinglist = -2;
+			break;
+		    }
+	}
+    }
+    menuacc++;
+
+    if (brbeg) {
+	int l;
+
+	iremovesuffix(',', 1);
+
+	l = (brscs >= 0 ? brscs : cs) - brpcs;
+
+	zsfree(lastbrbeg->str);
+	lastbrbeg->str = (char *) zalloc(l + 2);
+	memcpy(lastbrbeg->str, line + brpcs, l);
+	lastbrbeg->str[l] = ',';
+	lastbrbeg->str[l + 1] = '\0';
+    } else {
+	int l;
+
+	cs = minfo.pos + minfo.len + minfo.insc;
+	iremovesuffix(' ', 1);
+	l = cs;
+	cs = minfo.pos + minfo.len + minfo.insc - (*(minfo.cur))->qisl;
+	if (cs < l)
+	    foredel(l - cs);
+	else if (cs > ll)
+	    cs = ll;
+	inststrlen(" ", 1, 1);
+	if (parpre)
+	    inststr(parpre);
+	minfo.insc = minfo.len = 0;
+	minfo.pos = cs;
+	minfo.we = 1;
+    }
+    return 0;
+}
+
+/* This maps the value in v into the range [0,m-1], decrementing v
+ * if it is non-negative and making negative values count backwards. */
+
+/**/
+static int
+comp_mod(int v, int m)
+{
+    if (v >= 0)
+	v--;
+    if (v >= 0)
+	return v % m;
+    else {
+	while (v < 0)
+	    v += m;
+	return v;
+    }
+}
+
+/* This handles the beginning of menu-completion. */
+
+/**/
+static void
+do_ambig_menu(void)
+{
+    Cmatch *mc;
+
+    if (usemenu != 3) {
+	menucmp = 1;
+	menuacc = 0;
+	minfo.cur = NULL;
+    } else {
+	if (oldlist) {
+	    if (oldins && minfo.cur)
+		acceptlast();
+	} else
+	    minfo.cur = NULL;
+    }
+    if (insgroup) {
+	insgnum = comp_mod(insgnum, lastpermgnum);
+	for (minfo.group = amatches;
+	     minfo.group && (minfo.group)->num != insgnum + 1;
+	     minfo.group = (minfo.group)->next);
+	if (!minfo.group || !(minfo.group)->mcount) {
+	    minfo.cur = NULL;
+	    minfo.asked = 0;
+	    return;
+	}
+	insmnum = comp_mod(insmnum, (minfo.group)->mcount);
+    } else {
+	insmnum = comp_mod(insmnum, lastpermmnum);
+	for (minfo.group = amatches;
+	     minfo.group && (minfo.group)->mcount <= insmnum;
+	     minfo.group = (minfo.group)->next)
+	    insmnum -= (minfo.group)->mcount;
+	if (!minfo.group) {
+	    minfo.cur = NULL;
+	    minfo.asked = 0;
+	    return;
+	}
+    }
+    mc = (minfo.group)->matches + insmnum;
+    do_single(*mc);
+    minfo.cur = mc;
+}
+
+/* Return the real number of matches. */
+
+/**/
+zlong
+num_matches(int normal)
+{
+    int alt;
+
+    PERMALLOC {
+	alt = permmatches(0);
+    } LASTALLOC;
+
+    if (normal)
+	return (alt ? 0 : nmatches);
+    else
+	return (alt ? nmatches : 0);
+}
+
+/* Return the number of screen lines needed for the list. */
+
+/**/
+zlong
+list_lines(void)
+{
+    Cmgroup oam;
+
+    PERMALLOC {
+	permmatches(0);
+    } LASTALLOC;
+
+    oam = amatches;
+    amatches = pmatches;
+    listdat.valid = 0;
+    calclist();
+    listdat.valid = 0;
+    amatches = oam;
+
+    return listdat.nlines;
+}
+
+/**/
+void
+comp_list(char *v)
+{
+    zsfree(complist);
+    complist = ztrdup(v);
+
+    onlyexpl = (v && strstr(v, "expl"));
+}
+
+/* This is used to print the explanation string. *
+ * It returns the number of lines printed.       */
+
+/**/
+int
+printfmt(char *fmt, int n, int dopr, int doesc)
+{
+    char *p = fmt, nc[DIGBUFSIZE];
+    int l = 0, cc = 0, b = 0, s = 0, u = 0, m;
+
+    for (; *p; p++) {
+	/* Handle the `%' stuff (%% == %, %n == <number of matches>). */
+	if (doesc && *p == '%') {
+	    if (*++p) {
+		m = 0;
+		switch (*p) {
+		case '%':
+		    if (dopr)
+			putc('%', shout);
+		    cc++;
+		    break;
+		case 'n':
+		    sprintf(nc, "%d", n);
+		    if (dopr)
+			fprintf(shout, nc);
+		    cc += strlen(nc);
+		    break;
+		case 'B':
+		    b = 1;
+		    if (dopr)
+			tcout(TCBOLDFACEBEG);
+		    break;
+		case 'b':
+		    b = 0; m = 1;
+		    if (dopr)
+			tcout(TCALLATTRSOFF);
+		    break;
+		case 'S':
+		    s = 1;
+		    if (dopr)
+			tcout(TCSTANDOUTBEG);
+		    break;
+		case 's':
+		    s = 0; m = 1;
+		    if (dopr)
+			tcout(TCSTANDOUTEND);
+		    break;
+		case 'U':
+		    u = 1;
+		    if (dopr)
+			tcout(TCUNDERLINEBEG);
+		    break;
+		case 'u':
+		    u = 0; m = 1;
+		    if (dopr)
+			tcout(TCUNDERLINEEND);
+		    break;
+		case '{':
+		    for (p++; *p && (*p != '%' || p[1] != '}'); p++, cc++)
+			if (dopr)
+			    putc(*p, shout);
+		    if (*p)
+			p++;
+		    else
+			p--;
+		    break;
+		}
+		if (dopr && m) {
+		    if (b)
+			tcout(TCBOLDFACEBEG);
+		    if (s)
+			tcout(TCSTANDOUTBEG);
+		    if (u)
+			tcout(TCUNDERLINEBEG);
+		}
+	    } else
+		break;
+	} else {
+	    cc++;
+	    if (*p == '\n') {
+		if (dopr) {
+		    if (tccan(TCCLEAREOL))
+			tcout(TCCLEAREOL);
+		    else {
+			int s = columns - 1 - (cc % columns);
+
+			while (s-- > 0)
+			    putc(' ', shout);
+		    }
+		}
+		l += 1 + (cc / columns);
+		cc = 0;
+	    }
+	    if (dopr)
+		putc(*p, shout);
+	}
+    }
+    if (dopr) {
+	if (tccan(TCCLEAREOL))
+	    tcout(TCCLEAREOL);
+	else {
+	    int s = columns - 1 - (cc % columns);
+
+	    while (s-- > 0)
+		putc(' ', shout);
+	}
+    }
+    return l + (cc / columns);
+}
+
+/* This skips over matches that are not to be listed. */
+
+/**/
+Cmatch *
+skipnolist(Cmatch *p)
+{
+    while (*p && (((*p)->flags & (CMF_NOLIST | CMF_HIDE)) ||
+		  ((*p)->disp && ((*p)->flags & (CMF_DISPLINE | CMF_HIDE)))))
+	p++;
+
+    return p;
+}
+
+/**/
+void
+calclist(void)
+{
+    Cmgroup g;
+    Cmatch *p, m;
+    Cexpl *e;
+    int hidden = 0, nlist = 0, nlines = 0, add = 2 + isset(LISTTYPES);
+    int max = 0, i;
+    VARARR(int, mlens, nmatches + 1);
+
+    if (listdat.valid && onlyexpl == listdat.onlyexpl &&
+	menuacc == listdat.menuacc &&
+	lines == listdat.lines && columns == listdat.columns)
+	return;
+
+    for (g = amatches; g; g = g->next) {
+	char **pp = g->ylist;
+	int nl = 0, l, glong = 1, gshort = columns, ndisp = 0, totl = 0;
+
+	if (!onlyexpl && pp) {
+	    /* We have an ylist, lets see, if it contains newlines. */
+	    hidden = 1;
+	    while (!nl && *pp)
+		nl = !!strchr(*pp++, '\n');
+
+	    pp = g->ylist;
+	    if (nl || !pp[1]) {
+		/* Yup, there are newlines, count lines. */
+		char *nlptr, *sptr;
+
+		g->flags |= CGF_LINES;
+		hidden = 1;
+		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) {
+		    l = strlen(*pp);
+		    ndisp++;
+		    if (l > glong)
+			glong = l;
+		    if (l < gshort)
+			gshort = l;
+		    totl += l;
+		    nlist++;
+		    pp++;
+		}
+	    }
+	} else if (!onlyexpl) {
+	    for (p = g->matches; (m = *p); p++) {
+		if (menuacc && !hasbrpsfx(m, minfo.prebr, minfo.postbr)) {
+		    m->flags |= CMF_HIDE;
+		    continue;
+		}
+		m->flags &= ~CMF_HIDE;
+
+		if (m->disp) {
+		    if (m->flags & CMF_DISPLINE) {
+			nlines += 1 + printfmt(m->disp, 0, 0, 0);
+			g->flags |= CGF_HASDL;
+		    } else {
+			l = niceztrlen(m->disp);
+			ndisp++;
+			if (l > glong)
+			    glong = l;
+			if (l < gshort)
+			    gshort = l;
+			totl += l;
+			mlens[m->gnum] = l;
+		    }
+		    nlist++;
+		} else if (!(m->flags & CMF_NOLIST)) {
+		    l = niceztrlen(m->str);
+		    ndisp++;
+		    if (l > glong)
+			glong = l;
+		    if (l < gshort)
+			gshort = l;
+		    totl += l;
+		    mlens[m->gnum] = l;
+		    nlist++;
+		} else
+		    hidden = 1;
+	    }
+	}
+	if ((e = g->expls)) {
+	    while (*e) {
+		if ((*e)->count)
+		    nlines += 1 + printfmt((*e)->str, (*e)->count, 0, 1);
+		e++;
+	    }
+	}
+	g->totl = totl + (ndisp * add);
+	g->dcount = ndisp;
+	g->width = glong + add;
+	g->shortest = gshort + add;
+	if ((g->cols = columns / g->width) > g->dcount)
+	    g->cols = g->dcount;
+	if (g->cols) {
+	    i = g->cols * g->width - add;
+	    if (i > max)
+		max = i;
+	}
+    }
+    if (!onlyexpl) {
+	for (g = amatches; g; g = g->next) {
+	    char **pp;
+	    int glines = 0;
+
+	    zfree(g->widths, 0);
+	    g->widths = NULL;
+
+	    if ((pp = g->ylist)) {
+		if (!(g->flags & CGF_LINES)) {
+		    if (g->cols) {
+			glines += (arrlen(pp) + g->cols - 1) / g->cols;
+			if (g->cols > 1)
+			    g->width += (max - (g->width * g->cols - add)) / g->cols;
+		    } else {
+			g->cols = 1;
+			g->width = 1;
+			
+			while (*pp)
+			    glines += 1 + (strlen(*pp++) / columns);
+		    }
+		}
+	    } else {
+		if (g->cols) {
+		    glines += (g->dcount + g->cols - 1) / g->cols;
+		    if (g->cols > 1)
+			g->width += (max - (g->width * g->cols - add)) / g->cols;
+		} else if (!(g->flags & CGF_LINES)) {
+		    g->cols = 1;
+		    g->width = 0;
+		    
+		    for (p = g->matches; (m = *p); p++)
+			if (!(m->flags & CMF_HIDE)) {
+			    if (m->disp) {
+				if (!(m->flags & CMF_DISPLINE))
+				    glines += 1 + (mlens[m->gnum] / columns);
+			    } else if (!(m->flags & CMF_NOLIST))
+				glines += 1 + ((1 + mlens[m->gnum]) / columns);
+			}
+		}
+	    }
+	    g->lins = glines;
+	    nlines += glines;
+	}
+    }
+    if (!onlyexpl && isset(LISTPACKED)) {
+	char **pp;
+	int *ws, tlines, tline, tcols, maxlen, nth, width;
+
+	for (g = amatches; g; g = g->next) {
+	    ws = g->widths = (int *) zalloc(columns * sizeof(int));
+	    memset(ws, 0, columns * sizeof(int));
+	    tlines = g->lins;
+	    tcols = g->cols;
+	    width = 0;
+
+	    if ((pp = g->ylist)) {
+		if (!(g->flags & CGF_LINES)) {
+		    int yl = arrlen(pp), i;
+		    VARARR(int, ylens, yl);
+
+		    for (i = 0; *pp; i++, pp++)
+			ylens[i] = strlen(*pp) + add;
+
+		    if (isset(LISTROWSFIRST)) {
+			int count, tcol, first, maxlines = 0, llines;
+
+			for (tcols = columns / g->shortest; tcols > g->cols;
+			     tcols--) {
+			    for (nth = first = maxlen = width = maxlines =
+				     llines = tcol = 0,
+				     count = g->dcount;
+				 count > 0; count--) {
+				if (ylens[nth] > maxlen)
+				    maxlen = ylens[nth];
+				nth += tcols;
+				tlines++;
+				if (nth >= g->dcount) {
+				    if ((width += maxlen) >= columns)
+					break;
+				    ws[tcol++] = maxlen;
+				    maxlen = 0;
+				    nth = ++first;
+				    if (llines > maxlines)
+					maxlines = llines;
+				    llines = 0;
+				}
+			    }
+			    if (nth < yl) {
+				ws[tcol++] = maxlen;
+				width += maxlen;
+			    }
+			    if (!count && width < columns)
+				break;
+			}
+			if (tcols > g->cols)
+			    tlines = maxlines;
+		    } else {
+			for (tlines = ((g->totl + columns) / columns);
+			     tlines < g->lins; tlines++) {
+			    for (pp = g->ylist, nth = tline = width =
+				     maxlen = tcols = 0;
+				 *pp; nth++, pp++) {
+				if (ylens[nth] > maxlen)
+				    maxlen = ylens[nth];
+				if (++tline == tlines) {
+				    if ((width += maxlen) >= columns)
+					break;
+				    ws[tcols++] = maxlen;
+				    maxlen = tline = 0;
+				}
+			    }
+			    if (tline) {
+				ws[tcols++] = maxlen;
+				width += maxlen;
+			    }
+			    if (nth == yl && width < columns)
+				break;
+			}
+		    }
+		}
+	    } else if (g->width) {
+		if (isset(LISTROWSFIRST)) {
+		    int addlen, count, tcol, maxlines = 0, llines, i;
+		    Cmatch *first;
+
+		    for (tcols = columns / g->shortest; tcols > g->cols;
+			 tcols--) {
+			p = first = skipnolist(g->matches);
+			for (maxlen = width = maxlines = llines = tcol = 0,
+				 count = g->dcount;
+			     count > 0; count--) {
+			    m = *p;
+			    addlen = mlens[m->gnum] + add;
+			    if (addlen > maxlen)
+				maxlen = addlen;
+			    for (i = tcols; i && *p; i--)
+				p = skipnolist(p + 1);
+
+			    llines++;
+			    if (!*p) {
+				if (llines > maxlines)
+				    maxlines = llines;
+				llines = 0;
+
+				if ((width += maxlen) >= columns)
+				    break;
+				ws[tcol++] = maxlen;
+				maxlen = 0;
+
+				p = first = skipnolist(first + 1);
+			    }
+			}
+			if (tlines) {
+			    ws[tcol++] = maxlen;
+			    width += maxlen;
+			}
+			if (!count && width < columns)
+			    break;
+		    }
+		    if (tcols > g->cols)
+			tlines = maxlines;
+		} else {
+		    int addlen;
+
+		    for (tlines = ((g->totl + columns) / columns);
+			 tlines < g->lins; tlines++) {
+			for (p = g->matches, nth = tline = width =
+				 maxlen = tcols = 0;
+			     (m = *p); p++, nth++) {
+			    if (!(m->flags &
+				  (m->disp ? (CMF_DISPLINE | CMF_HIDE) :
+				   (CMF_NOLIST | CMF_HIDE)))) {
+				addlen = mlens[m->gnum] + add;
+				if (addlen > maxlen)
+				    maxlen = addlen;
+				if (++tline == tlines) {
+				    if ((width += maxlen) >= columns)
+					break;
+				    ws[tcols++] = maxlen;
+				    maxlen = tline = 0;
+				}
+			    }
+			}
+			if (tline) {
+			    ws[tcols++] = maxlen;
+			    width += maxlen;
+			}
+			if (nth == g->dcount && width < columns)
+			    break;
+		    }
+		}
+	    }
+	    if (tlines == g->lins) {
+		zfree(ws, columns * sizeof(int));
+		g->widths = NULL;
+	    } else {
+		nlines += tlines - g->lins;
+		g->lins = tlines;
+		g->cols = tcols;
+		g->totl = width;
+		width -= add;
+		if (width > max)
+		    max = width;
+	    }
+	}
+	for (g = amatches; g; g = g->next) {
+	    if (g->widths) {
+		int *p, a = (max - g->totl + add) / g->cols;
+
+		for (i = g->cols, p = g->widths; i; i--, p++)
+		    *p += a;
+	    } else if (g->width && g->cols > 1)
+		g->width += (max - (g->width * g->cols - add)) / g->cols;
+	}
+    }
+    listdat.valid = 1;
+    listdat.hidden = hidden;
+    listdat.nlist = nlist;
+    listdat.nlines = nlines;
+    listdat.menuacc = menuacc;
+    listdat.onlyexpl = onlyexpl;
+    listdat.columns = columns;
+    listdat.lines = lines;
+}
+
+/**/
+int asklist(void)
+{
+    /* Set the cursor below the prompt. */
+    trashzle();
+    showinglist = listshown = 0;
+
+    clearflag = (isset(USEZLE) && !termflags &&
+		 complastprompt && *complastprompt);
+
+    /* Maybe we have to ask if the user wants to see the list. */
+    if ((!minfo.cur || !minfo.asked) &&
+	((complistmax && listdat.nlist > complistmax) ||
+	 (!complistmax && listdat.nlines >= lines))) {
+	int qup;
+	zsetterm();
+	qup = printfmt("zsh: do you wish to see all %n possibilities? ",
+		       listdat.nlist, 1, 1);
+	fflush(shout);
+	if (getzlequery() != 'y') {
+	    if (clearflag) {
+		putc('\r', shout);
+		tcmultout(TCUP, TCMULTUP, qup);
+		if (tccan(TCCLEAREOD))
+		    tcout(TCCLEAREOD);
+		tcmultout(TCUP, TCMULTUP, nlnct);
+	    } else
+		putc('\n', shout);
+	    if (minfo.cur)
+		minfo.asked = 2;
+	    return 1;
+	}
+	if (clearflag) {
+	    putc('\r', shout);
+	    tcmultout(TCUP, TCMULTUP, qup);
+	    if (tccan(TCCLEAREOD))
+		tcout(TCCLEAREOD);
+	} else
+	    putc('\n', shout);
+	settyinfo(&shttyinfo);
+	if (minfo.cur)
+	    minfo.asked = 1;
+    }
+    return 0;
+}
+
+/**/
+int
+printlist(int over, CLPrintFunc printm)
+{
+    Cmgroup g;
+    Cmatch *p, m;
+    Cexpl *e;
+    int pnl = 0, cl = (over ? listdat.nlines : -1);
+    int mc = 0, ml = 0, printed = 0;
+
+    if (cl < 2) {
+	cl = -1;
+	if (tccan(TCCLEAREOD))
+	    tcout(TCCLEAREOD);
+    }
+    g = amatches;
+    while (g) {
+	char **pp = g->ylist;
+
+	if ((e = g->expls)) {
+	    int l;
+
+	    while (*e) {
+		if ((*e)->count) {
+		    if (pnl) {
+			putc('\n', shout);
+			pnl = 0;
+			ml++;
+			if (cl >= 0 && --cl <= 1) {
+			    cl = -1;
+			    if (tccan(TCCLEAREOD))
+				tcout(TCCLEAREOD);
+			}
+		    }
+		    l = printfmt((*e)->str, (*e)->count, 1, 1);
+		    ml += l;
+		    if (cl >= 0 && (cl -= l) <= 1) {
+			cl = -1;
+			if (tccan(TCCLEAREOD))
+			    tcout(TCCLEAREOD);
+		    }
+		    pnl = 1;
+		}
+		e++;
+	    }
+	}
+	if (!listdat.onlyexpl && pp && *pp) {
+	    if (pnl) {
+		putc('\n', shout);
+		pnl = 0;
+		ml++;
+		if (cl >= 0 && --cl <= 1) {
+		    cl = -1;
+		    if (tccan(TCCLEAREOD))
+			tcout(TCCLEAREOD);
+		}
+	    }
+	    if (g->flags & CGF_LINES) {
+		while (*pp) {
+		    zputs(*pp, shout);
+		    if (*++pp)
+			putc('\n', shout);
+		}
+	    } else {
+		int n = g->lcount, nl, nc, i, a;
+		char **pq;
+
+		nl = nc = g->lins;
+
+		while (n && nl--) {
+		    i = g->cols;
+		    mc = 0;
+		    pq = pp;
+		    while (n && i--) {
+			if (pq - g->ylist >= g->lcount)
+			    break;
+			zputs(*pq, shout);
+			if (i) {
+			    a = (g->widths ? g->widths[mc] : g->width) -
+				strlen(*pq);
+			    while (a--)
+				putc(' ', shout);
+			}
+			pq += (isset(LISTROWSFIRST) ? 1 : nc);
+			mc++;
+			n--;
+		    }
+		    if (n) {
+			putc('\n', shout);
+			ml++;
+			if (cl >= 0 && --cl <= 1) {
+			    cl = -1;
+			    if (tccan(TCCLEAREOD))
+				tcout(TCCLEAREOD);
+			}
+		    }
+		    pp += (isset(LISTROWSFIRST) ? g->cols : 1);
+		}
+	    }
+	} else if (!listdat.onlyexpl && g->lcount) {
+	    int n = g->dcount, nl, nc, i, j, wid;
+	    Cmatch *q;
+
+	    nl = nc = g->lins;
+
+	    if (g->flags & CGF_HASDL) {
+		for (p = g->matches; (m = *p); p++)
+		    if (m->disp && (m->flags & CMF_DISPLINE)) {
+			if (pnl) {
+			    putc('\n', shout);
+			    pnl = 0;
+			    ml++;
+			    if (cl >= 0 && --cl <= 1) {
+				cl = -1;
+				if (tccan(TCCLEAREOD))
+				    tcout(TCCLEAREOD);
+			    }
+			}
+			printed++;
+			printm(g, p, 0, ml, 1, 0, NULL, NULL);
+			pnl = 1;
+		    }
+	    }
+	    if (n && pnl) {
+		putc('\n', shout);
+		pnl = 0;
+		ml++;
+		if (cl >= 0 && --cl <= 1) {
+		    cl = -1;
+		    if (tccan(TCCLEAREOD))
+			tcout(TCCLEAREOD);
+		}
+	    }
+	    for (p = skipnolist(g->matches); n && nl--;) {
+		i = g->cols;
+		mc = 0;
+		q = p;
+		while (n && i--) {
+		    wid = (g->widths ? g->widths[mc] : g->width);
+		    if (!(m = *q)) {
+			printm(g, NULL, mc, ml, (!i), wid, NULL, NULL);
+			break;
+		    }
+		    if (!m->disp && (m->flags & CMF_FILE)) {
+			struct stat buf;
+			char *pb;
+
+			pb = (char *) zhalloc((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))
+			    printm(g, q, mc, ml, (!i), wid, NULL, NULL);
+			else
+			    printm(g, q, mc, ml, (!i), wid, pb, &buf);
+		    } else
+			printm(g, q, mc, ml, (!i), wid, NULL, NULL);
+
+		    printed++;
+
+		    if (--n)
+			for (j = (isset(LISTROWSFIRST) ? 1 : nc); j && *q; j--)
+			    q = skipnolist(q + 1);
+		    mc++;
+		}
+		while (i-- > 0)
+		    printm(g, NULL, mc++, ml, (!i),
+			   (g->widths ? g->widths[mc] : g->width), NULL, NULL);
+
+		if (n) {
+		    putc('\n', shout);
+		    ml++;
+		    if (cl >= 0 && --cl <= 1) {
+			cl = -1;
+			if (tccan(TCCLEAREOD))
+			    tcout(TCCLEAREOD);
+		    }
+		    if (nl)
+			for (j = (isset(LISTROWSFIRST) ? g->cols : 1); j && *p; j--)
+			    p = skipnolist(p + 1);
+		}
+	    }
+	}
+	if (g->lcount)
+	    pnl = 1;
+	g = g->next;
+    }
+    if (clearflag) {
+	/* Move the cursor up to the prompt, if always_last_prompt *
+	 * is set and all that...                                  */
+	if ((ml = listdat.nlines + nlnct - 1) < lines) {
+	    tcmultout(TCUP, TCMULTUP, ml);
+	    showinglist = -1;
+	} else
+	    clearflag = 0, putc('\n', shout);
+    } else
+	putc('\n', shout);
+    listshown = (clearflag ? 1 : -1);
+
+    return printed;
+}
+
+/**/
+static void
+iprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width,
+	char *path, struct stat *buf)
+{
+    Cmatch m;
+    int len = 0;
+
+    if (!mp)
+	return;
+
+    m = *mp;
+    if (m->disp) {
+	if (m->flags & CMF_DISPLINE) {
+	    printfmt(m->disp, 0, 1, 0);
+	    return;
+	}
+	nicezputs(m->disp, shout);
+	len = niceztrlen(m->disp);
+    } else {
+	nicezputs(m->str, shout);
+	len = niceztrlen(m->str);
+
+	if (isset(LISTTYPES)) {
+	    if (buf)
+		putc(file_type(buf->st_mode), shout);
+	    len++;
+	}
+    }
+    if (!lastc) {
+	len = width - len;
+
+	while (len-- > 0)
+	    putc(' ', shout);
+    }
+}
+
+/**/
+int
+ilistmatches(Hookdef dummy, Chdata dat)
+{
+    calclist();
+
+    if (!listdat.nlines) {
+	showinglist = listshown = 0;
+	return 1;
+    }
+    if (asklist())
+	return 0;
+
+    printlist(0, iprintm);
+
+    return 0;
+}
+
+/* List the matches.  Note that the list entries are metafied. */
+
+/**/
+int
+list_matches(Hookdef dummy, void *dummy2)
+{
+    struct chdata dat;
+
+#ifdef DEBUG
+    /* Sanity check */
+    if (!validlist) {
+	showmsg("BUG: listmatches called with bogus list");
+	return 1;
+    }
+#endif
+
+    dat.matches = amatches;
+    dat.num = nmatches;
+    dat.cur = NULL;
+    return runhookdef(COMPLISTMATCHESHOOK, (void *) &dat);
+}
+
+/* Invalidate the completion list. */
+
+/**/
+int
+invalidate_list(void)
+{
+    if (showinglist == -2)
+	listmatches();
+    if (validlist) {
+	freematches(lastmatches);
+	lastmatches = NULL;
+	hasoldlist = 0;
+    }
+    lastambig = menucmp = menuacc = validlist = showinglist = fromcomp = 0;
+    listdat.valid = 0;
+    if (listshown < 0)
+	listshown = 0;
+    minfo.cur = NULL;
+    minfo.asked = 0;
+    zsfree(minfo.prebr);
+    zsfree(minfo.postbr);
+    minfo.postbr = minfo.prebr = NULL;
+    compwidget = NULL;
+
+    return 0;
+}
diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h
index 470aa890f..2dfeb6950 100644
--- a/Src/Zle/zle.h
+++ b/Src/Zle/zle.h
@@ -144,19 +144,53 @@ typedef struct cutbuffer *Cutbuffer;
 
 #define KRINGCT 8   /* number of buffers in the kill ring */
 
+/* Types of completion. */
+
+#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
+#define COMP_ISEXPAND(X) ((X) >= COMP_EXPAND)
+
+/* Information about one brace run. */
+
+typedef struct brinfo *Brinfo;
+
+struct brinfo {
+    Brinfo next;		/* next in list */
+    Brinfo prev;		/* previous (only for closing braces) */
+    char *str;			/* the string to insert */
+    int pos;			/* original position */
+    int qpos;			/* original position, with quoting */
+    int curpos;			/* position for current match */
+};
+
 /* Convenience macros for the hooks */
 
-#define LISTMATCHESHOOK   (zlehooks + 0)
-#define INSERTMATCHHOOK   (zlehooks + 1)
-#define MENUSTARTHOOK     (zlehooks + 2)
-#define COMPCTLMAKEHOOK   (zlehooks + 3)
-#define COMPCTLBEFOREHOOK (zlehooks + 4)
-#define COMPCTLAFTERHOOK  (zlehooks + 5)
+#define LISTMATCHESHOOK    (zlehooks + 0)
+#define COMPLETEHOOK       (zlehooks + 1)
+#define BEFORECOMPLETEHOOK (zlehooks + 2)
+#define AFTERCOMPLETEHOOK  (zlehooks + 3)
+#define ACCEPTCOMPHOOK     (zlehooks + 4)
+#define REVERSEMENUHOOK    (zlehooks + 5)
+#define INVALIDATELISTHOOK (zlehooks + 6)
 
-/* compctl hook data structs */
+/* complete hook data struct */
 
-struct ccmakedat {
-    char *str;
-    int incmd;
+typedef struct compldat *Compldat;
+
+struct compldat {
+    char *s;
     int lst;
+    int incmd;
 };
+
+/* List completion matches. */
+
+#define listmatches() runhookdef(LISTMATCHESHOOK, NULL)
+
+/* Invalidate the completion list. */
+
+#define invalidatelist() runhookdef(INVALIDATELISTHOOK, NULL)
diff --git a/Src/Zle/zle.mdd b/Src/Zle/zle.mdd
index c3d6672a4..3235c88a1 100644
--- a/Src/Zle/zle.mdd
+++ b/Src/Zle/zle.mdd
@@ -6,7 +6,7 @@ objects="zle_bindings.o zle_hist.o zle_keymap.o zle_main.o \
 zle_misc.o zle_move.o zle_params.o zle_refresh.o \
 zle_thingy.o zle_tricky.o zle_utils.o zle_vi.o zle_word.o"
 
-headers="zle.h zle_things.h comp.h"
+headers="zle.h zle_things.h"
 
 :<<\Make
 zle_things.h: thingies.list zle_things.sed
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 56ecd02ba..f29da3e0e 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -30,14 +30,6 @@
 #include "zle.mdh"
 #include "zle_main.pro"
 
-/* Defined by the complete module, called in zle_tricky.c. */
-
-/**/
-void (*makecompparamsptr) _((void));
-
-/**/
-void (*comp_setunsetptr) _((int, int, int, int));
-
 /* != 0 if in a shell function called from completion, such that read -[cl]  *
  * will work (i.e., the line is metafied, and the above word arrays are OK). */
 
@@ -49,50 +41,10 @@ int incompctlfunc;
 /**/
 int incompfunc;
 
-/* Global matcher. */
-
-/**/
-Cmlist cmatcher;
-
-/* global variables for shell parameters in new style completion */
+/* != 0 if completion module is loaded */
 
 /**/
-zlong compcurrent,
-      compmatcher,
-      compmatchertot,
-      complistmax,
-      complistlines;
-
-/**/
-char **compwords,
-     *compprefix,
-     *compsuffix,
-     *compiprefix,
-     *compisuffix,
-     *compqiprefix,
-     *compqisuffix,
-     *compmatcherstr,
-     *compcontext,
-     *compparameter,
-     *compredirect,
-     *compquote,
-     *compquoting,
-     *comprestore,
-     *complist,
-     *compforcelist,
-     *compinsert,
-     *compexact,
-     *compexactstr,
-     *comppatmatch,
-     *comppatinsert,
-     *complastprompt,
-     *comptoend,
-     *compoldlist,
-     *compoldins,
-     *compvared;
-
-/**/
-Param *comprpms, *compkpms;
+int hascompmod;
 
 /* != 0 if we're done editing */
 
@@ -1022,12 +974,13 @@ static struct builtin bintab[] = {
 
 /**/
 struct hookdef zlehooks[] = {
-    HOOKDEF("list_matches", ilistmatches, 0),
-    HOOKDEF("insert_match", NULL, HOOKF_ALL),
-    HOOKDEF("menu_start", NULL, HOOKF_ALL),
-    HOOKDEF("compctl_make", NULL, 0),
-    HOOKDEF("compctl_before", NULL, 0),
-    HOOKDEF("compctl_after", NULL, 0),
+    HOOKDEF("list_matches", NULL, 0),
+    HOOKDEF("complete", NULL, 0),
+    HOOKDEF("before_complete", NULL, 0),
+    HOOKDEF("after_complete", NULL, 0),
+    HOOKDEF("accept_completion", NULL, 0),
+    HOOKDEF("reverse_menu", NULL, 0),
+    HOOKDEF("invalidate_list", NULL, 0),
 };
 
 /**/
@@ -1050,30 +1003,16 @@ setup_zle(Module m)
     /* miscellaneous initialisations */
     stackhist = stackcs = -1;
     kungetbuf = (char *) zalloc(kungetsz = 32);
-    hasperm = 0;
 
     /* initialise the keymap system */
     init_keymaps();
 
     varedarg = NULL;
 
-    incompfunc = incompctlfunc = 0;
-
-    comprpms = compkpms = NULL;
-    compwords = NULL;
-    compprefix = compsuffix = compiprefix = compisuffix = 
-	compqiprefix = compqisuffix = compmatcherstr = 
-	compcontext = compparameter = compredirect = compquote =
-	compquoting = comprestore = complist = compinsert =
-	compexact = compexactstr = comppatmatch = comppatinsert =
-	compforcelist = complastprompt = comptoend = 
-	compoldlist = compoldins = compvared = NULL;
+    incompfunc = incompctlfunc = hascompmod = 0;
 
     clwords = (char **) zcalloc((clwsize = 16) * sizeof(char *));
 
-    makecompparamsptr = NULL;
-    comp_setunsetptr = NULL;
-
     return 0;
 }
 
@@ -1132,33 +1071,6 @@ finish_zle(Module m)
 
     getkeyptr = NULL;
 
-    freearray(compwords);
-    zsfree(compprefix);
-    zsfree(compsuffix);
-    zsfree(compiprefix);
-    zsfree(compisuffix);
-    zsfree(compqiprefix);
-    zsfree(compqisuffix);
-    zsfree(compmatcherstr);
-    zsfree(compcontext);
-    zsfree(compparameter);
-    zsfree(compredirect);
-    zsfree(compquote);
-    zsfree(compquoting);
-    zsfree(comprestore);
-    zsfree(complist);
-    zsfree(compforcelist);
-    zsfree(compinsert);
-    zsfree(compexact);
-    zsfree(compexactstr);
-    zsfree(comppatmatch);
-    zsfree(comppatinsert);
-    zsfree(complastprompt);
-    zsfree(comptoend);
-    zsfree(compoldlist);
-    zsfree(compoldins);
-    zsfree(compvared);
-
     zfree(clwords, clwsize * sizeof(char *));
 
     return 0;
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index 136a9d432..18e38ee3a 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -640,7 +640,7 @@ Thingy
 executenamedcommand(char *prmt)
 {
     Thingy cmd;
-    int len, l = strlen(prmt), ols = listshown, feep = 0;
+    int len, l = strlen(prmt), ols = listshown, feep = 0, listed = 0, curlist = 0;
     char *ptr;
     char *okeymap = curkeymapname;
 
@@ -660,14 +660,31 @@ executenamedcommand(char *prmt)
 	    selectkeymap(okeymap, 1);
 	    if ((listshown = ols))
 		showinglist = -2;
-	    else
-		clearlist = 1;
+	    else if (listed)
+		clearlist = listshown = 1;
+
 	    return NULL;
 	}
 	if(cmd == Th(z_clearscreen)) {
 	    clearscreen(zlenoargs);
+	    if (curlist) {
+		int zmultsav = zmult;
+
+		zmult = 1;
+		listlist(cmdll);
+		showinglist = 0;
+		zmult = zmultsav;
+	    }
 	} else if(cmd == Th(z_redisplay)) {
 	    redisplay(zlenoargs);
+	    if (curlist) {
+		int zmultsav = zmult;
+
+		zmult = 1;
+		listlist(cmdll);
+		showinglist = 0;
+		zmult = zmultsav;
+	    }
 	} else if(cmd == Th(z_viquotedinsert)) {
 	    *ptr = '^';
 	    zrefresh();
@@ -675,23 +692,28 @@ executenamedcommand(char *prmt)
 	    if(c == EOF || !c || len == NAMLEN)
 		feep = 1;
 	    else
-		*ptr++ = c, len++;
+		*ptr++ = c, len++, curlist = 0;
 	} else if(cmd == Th(z_quotedinsert)) {
 	    if((c = getkey(0)) == EOF || !c || len == NAMLEN)
 		feep = 1;
 	    else
-		*ptr++ = c, len++;
+		*ptr++ = c, len++, curlist = 0;
 	} else if(cmd == Th(z_backwarddeletechar) ||
 	    	cmd == Th(z_vibackwarddeletechar)) {
 	    if (len)
-		len--, ptr--;
+		len--, ptr--, curlist = 0;
 	} else if(cmd == Th(z_killregion) || cmd == Th(z_backwardkillword) ||
-	    	cmd == Th(z_vibackwardkillword)) {
+		  cmd == Th(z_vibackwardkillword)) {
+	    if (len)
+		curlist = 0;
 	    while (len && (len--, *--ptr != '-'));
 	} else if(cmd == Th(z_killwholeline) || cmd == Th(z_vikillline) ||
 	    	cmd == Th(z_backwardkillline)) {
 	    len = 0;
 	    ptr = cmdbuf;
+	    if (listed)
+		clearlist = listshown = 1;
+	    curlist = 0;
 	} else {
 	    if(cmd == Th(z_acceptline) || cmd == Th(z_vicmdmode)) {
 		Thingy r;
@@ -704,8 +726,8 @@ executenamedcommand(char *prmt)
 		    selectkeymap(okeymap, 1);
 		    if ((listshown = ols))
 			showinglist = -2;
-		    else
-			clearlist = 1;
+		    else if (listed)
+			clearlist = listshown = 1;
 		    return r;
 		}
 		unrefthingy(r);
@@ -728,15 +750,19 @@ executenamedcommand(char *prmt)
 
 		    scanhashtable(thingytab, 1, 0, DISABLED, scancompcmd, 0);
 		} LASTALLOC;
-		if (empty(cmdll))
+		if (empty(cmdll)) {
 		    feep = 1;
-		else if (cmd == Th(z_listchoices) ||
+		    if (listed)
+			clearlist = listshown = 1;
+		    curlist = 0;
+		} else if (cmd == Th(z_listchoices) ||
 		    cmd == Th(z_deletecharorlist)) {
 		    int zmultsav = zmult;
 		    *ptr = '_';
 		    statusll = l + len + 1;
 		    zmult = 1;
 		    listlist(cmdll);
+		    listed = curlist = 1;
 		    showinglist = 0;
 		    zmult = zmultsav;
 		} else if (!nextnode(firstnode(cmdll))) {
@@ -756,6 +782,7 @@ executenamedcommand(char *prmt)
 			statusll = l + cmdambig + 1;
 			zmult = 1;
 			listlist(cmdll);
+			listed = curlist = 1;
 			showinglist = 0;
 			zmult = zmultsav;
 		    }
@@ -765,7 +792,7 @@ executenamedcommand(char *prmt)
 		if (len == NAMLEN || icntrl(c) || cmd != Th(z_selfinsert))
 		    feep = 1;
 		else
-		    *ptr++ = c, len++;
+		    *ptr++ = c, len++, curlist = 0;
 	    }
 	}
 	if (feep)
diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c
index 72a0f120d..a4191a9a4 100644
--- a/Src/Zle/zle_thingy.c
+++ b/Src/Zle/zle_thingy.c
@@ -424,7 +424,7 @@ bin_zle_refresh(char *name, char **args, char *ops, char func)
 	} else if (ops['c'])
 	    clearlist = 1;
     } else if (ops['c'])
-	clearlist = 1;
+	clearlist = listshown = 1;
     zrefresh();
 
     clearlist = ocl;
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index a5f6dbc88..14c1791d1 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -42,13 +42,14 @@
  * file only are not translated: they remain indexes into the metafied  *
  * line.                                                                */
 
-
 #define inststr(X) inststrlen((X),1,-1)
 
 /* The line before completion was tried. */
 
-static char *origline;
-static int origcs, origll;
+/**/
+char *origline;
+/**/
+int origcs, origll;
 
 /* Words on the command line, for use in completion */
  
@@ -68,54 +69,18 @@ int wb, we;
 /**/
 int offs;
 
-/* We store the following prefixes/suffixes:                               *
- * ipre,ripre  -- the ignored prefix (quoted and unquoted)                 *
- * isuf        -- the ignored suffix                                       *
- * autoq       -- quotes to automatically insert                           */
+/* These control the type of completion that will be done.  They are       *
+ * affected by the choice of ZLE command and by relevant shell options.    *
+ * usemenu is set to 2 if we have to start automenu and 3 if we have to    *
+ * insert a match as if for menucompletion but without really starting it. */
 
 /**/
-char *ipre, *ripre, *isuf, *qipre, *qisuf, autoq;
-
-/* the last completion widget called */
+int usemenu, useglob;
 
-static Widget lastcompwidget;
-
-/* These control the type of completion that will be done.  They are      *
- * affected by the choice of ZLE command and by relevant shell options.   *
- * usemenu is set to 2 if we have to start automenu and 3 if we have to   *
- * insert a match as if for menucompletion but without really stating it. */
+/* != 0 if we are in the middle of a menu completion. */
 
 /**/
-int usemenu, useglob, useexact, useline, uselist;
-
-/* Non-zero if we should keep an old list. */
-
-static int oldlist, oldins;
-
-/* Non-zero if we have to redisplay the list of matches. */
-
-static int showagain = 0;
-
-/* The match and group number to insert when starting menucompletion.   */
-
-static int insmnum, insgnum, insgroup, insspace;
-
-/* This is used to decide when the cursor should be moved to the end of    *
- * the inserted word: 0 - never, 1 - only when a single match is inserted, *
- * 2 - when a full match is inserted (single or menu), 3 - always.         */
-
-static int movetoend;
-
-/* != 0 if we are in the middle of a menu completion and number of matches
-* accepted with accept-and-menu-complete */
-
-/**/
-int menucmp, menuacc;
-
-/* Information about menucompletion. */
-
-/**/
-struct menuinfo minfo;
+int menucmp;
 
 /* Lists of brace-infos before/after cursor (first and last for each). */
 
@@ -125,164 +90,34 @@ Brinfo brbeg, lastbrbeg, brend, lastbrend;
 /**/
 int nbrbeg, nbrend;
 
-static char *lastprebr, *lastpostbr;
-static int hasunqu, useqbr, brpcs, brscs;
-
-/* The list of matches.  fmatches contains the matches we first ignore *
- * because of fignore.                                                 */
-
-/**/
-LinkList matches, fmatches;
-
-/* This holds the list of matches-groups. lastmatches holds the last list of 
- * permanently allocated matches, pmatches is the same for the list
- * currently built, amatches is the heap allocated stuff during completion
- * (after all matches have been generated it is an alias for pmatches), and
- * lmatches/lastlmatches is a pointer to the last element in the lists. */
-
 /**/
-Cmgroup lastmatches, pmatches, amatches, lmatches, lastlmatches;
-
-/* Non-zero if we have permanently allocated matches (old and new). */
-
-/**/
-int hasoldlist, hasperm;
-
-/* Non-zero if we have newly added matches. */
-
-/**/
-int newmatches;
-
-/* Number of permanently allocated matches and groups. */
-
-static int permmnum, permgnum, lastpermmnum, lastpermgnum;
-
-/* The total number of matches and the number of matches to be listed. */
-
-/**/
-int nmatches, smatches;
+char *lastprebr, *lastpostbr;
 
 /* !=0 if we have a valid completion list. */
 
 /**/
 int validlist;
 
-/* != 0 if only explanation strings should be printed */
-
-/**/
-int onlyexpl;
-
-/* Information about the matches for listing. */
-
-/**/
-struct cldata listdat;
-
-/* This flag is non-zero if we are completing a pattern (with globcomplete) */
-
-/**/
-int ispattern, haspattern;
-
-/* Non-zero if at least one match was added without -U. */
+/* Non-zero if we have to redisplay the list of matches. */
 
 /**/
-int hasmatched;
-
-/* A parameter expansion prefix (like ${). */
-
-static char *parpre;
-
-/* Flags for parameter expansions for new style completion. */
-
-static int parflags;
+int showagain = 0;
 
 /* This holds the word we are completing in quoted from. */
 
 static char *qword;
 
-/* The current group of matches. */
-
-static Cmgroup mgroup;
-
-/* Match counters: all matches, normal matches (not alternate set). */
-
-/**/
-int mnum;
-
-/* The match counter when unambig_data() was called. */
-
-static int unambig_mnum;
-
-/* Match flags for all matches in this group. */
-
-/**/
-int mflags;
-
-/* Length of longest/shortest match. */
-
-static int maxmlen, minmlen;
-
-/* This holds the explanation strings we have to print in this group and *
- * a pointer to the current cexpl structure. */
-
-static LinkList expls;
+/* The quoted prefix/suffix and a flag saying if we want to add the
+ * closing quote. */
 
 /**/
-Cexpl curexpl;
-
-/* A stack of completion matchers to be used. */
-
-/**/
-Cmlist mstack;
-
-/* The completion matchers used when building new stuff for the line. */
-
-/**/
-Cmlist bmatchers;
-
-/* A list with references to all matchers we used. */
-
-/**/
-LinkList matchers;
-
-/* A heap of free Cline structures. */
-
-static Cline freecl;
-
-/* Ambiguous information. */
-
-static Aminfo ainfo, fainfo;
+char *qipre, *qisuf, autoq;
 
 /* This contains the name of the function to call if this is for a new  *
  * style completion. */
 
-static char *compfunc = NULL;
-
-/* The memory heap to use for new style completion generation. */
-
-/**/
-Heap compheap;
-
-/* Find out if we have to insert a tab (instead of trying to complete). */
-
-/* A list of some data.
- *
- * Well, actually, it's the list of all compctls used so far, but since
- * conceptually we don't know anything about compctls here... */
-
 /**/
-LinkList allccs;
-
-/**/
-static int
-usetab(void)
-{
-    unsigned char *s = line + cs - 1;
-
-    for (; s >= line && *s != '\n'; s--)
-	if (*s != '\t' && *s != ' ')
-	    return 0;
-    return 1;
-}
+char *compfunc = NULL;
 
 /* Non-zero if the last completion done was ambiguous (used to find   *
  * out if AUTOMENU should start).  More precisely, it's nonzero after *
@@ -298,28 +133,26 @@ usetab(void)
 /**/
 int lastambig;
 
-/* This says what of the state the line is in when completion is started *
- * came from a previous completion. If the FC_LINE bit is set, the       *
- * string was inserted. If FC_INWORD is set, the last completion moved   *
- * the cursor into the word although it was at the end of it when the    *
- * last completion was invoked.                                          *
- * This is used to detect if the string should be taken as an exact      *
- * match (see do_ambiguous()) and if the cursor has to be moved to the   *
- * end of the word before generating the completions.                    */
-
-static int fromcomp;
-
-/* This holds the end-position of the last string inserted into the line. */
+/* Arguments for and return value of completion widget. */
 
-static int lastend;
+/**/
+char **cfargs;
+/**/
+int cfret;
 
-#define FC_LINE   1
-#define FC_INWORD 2
+/* Find out if we have to insert a tab (instead of trying to complete). */
 
-/* Arguments for and return value of completion widget. */
+/**/
+static int
+usetab(void)
+{
+    unsigned char *s = line + cs - 1;
 
-static char **cfargs;
-static int cfret;
+    for (; s >= line && *s != '\n'; s--)
+	if (*s != '\t' && *s != ' ')
+	    return 0;
+    return 1;
+}
 
 /**/
 int
@@ -387,21 +220,15 @@ spellword(char **args)
 int
 deletecharorlist(char **args)
 {
-    Cmgroup mg = minfo.group;
-    Cmatch *mc = minfo.cur;
-    int ret;
-
     usemenu = !!isset(MENUCOMPLETE);
     useglob = isset(GLOBCOMPLETE);
+
     if (cs != ll) {
 	fixsuffix();
-	ret = deletechar(args);
-    } else
-	ret = docomplete(COMP_LIST_COMPLETE);
-
-    minfo.cur = mc;
-    minfo.group = mg;
-    return ret;
+	invalidatelist();
+	return deletechar(args);
+    }
+    return docomplete(COMP_LIST_COMPLETE);
 }
 
 /**/
@@ -462,92 +289,17 @@ reversemenucomplete(char **args)
     if (!menucmp)
 	return menucomplete(args);
 
-    HEAPALLOC {
-	do {
-	    if (minfo.cur == (minfo.group)->matches) {
-		do {
-		    if (!(minfo.group = (minfo.group)->prev))
-			minfo.group = lmatches;
-		} while (!(minfo.group)->mcount);
-		minfo.cur = (minfo.group)->matches + (minfo.group)->mcount - 1;
-	    } else
-		minfo.cur--;
-	} while (menuacc &&
-		 !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr));
-	metafy_line();
-	do_single(*(minfo.cur));
-	unmetafy_line();
-    } LASTALLOC;
+    runhookdef(REVERSEMENUHOOK, NULL);
     return 0;
 }
 
-/* Accepts the current completion and starts a new arg, *
- * with the next completions. This gives you a way to   *
- * accept several selections from the list of matches.  */
-
-/**/
-void
-acceptlast(void)
-{
-    if (!menuacc) {
-	zsfree(minfo.prebr);
-	minfo.prebr = ztrdup(lastprebr);
-	zsfree(minfo.postbr);
-	minfo.postbr = ztrdup(lastpostbr);
-
-	if (listshown && (lastprebr || lastpostbr)) {
-	    Cmgroup g;
-	    Cmatch *m;
-
-	    for (g = amatches, m = NULL; g && (!m || !*m); g = g->next)
-		for (m = g->matches; *m; m++)
-		    if (!hasbrpsfx(*m, minfo.prebr, minfo.postbr)) {
-			showinglist = -2;
-			break;
-		    }
-	}
-    }
-    menuacc++;
-
-    if (brbeg) {
-	int l;
-
-	iremovesuffix(',', 1);
-
-	l = (brscs >= 0 ? brscs : cs) - brpcs;
-
-	zsfree(lastbrbeg->str);
-	lastbrbeg->str = (char *) zalloc(l + 2);
-	memcpy(lastbrbeg->str, line + brpcs, l);
-	lastbrbeg->str[l] = ',';
-	lastbrbeg->str[l + 1] = '\0';
-    } else {
-	int l;
-
-	cs = minfo.pos + minfo.len + minfo.insc;
-	iremovesuffix(' ', 1);
-	l = cs;
-	cs = minfo.pos + minfo.len + minfo.insc - (*(minfo.cur))->qisl;
-	if (cs < l)
-	    foredel(l - cs);
-	else if (cs > ll)
-	    cs = ll;
-	inststrlen(" ", 1, 1);
-	if (parpre)
-	    inststr(parpre);
-	minfo.insc = minfo.len = 0;
-	minfo.pos = cs;
-	minfo.we = 1;
-    }
-}
-
 /**/
 int
 acceptandmenucomplete(char **args)
 {
     if (!menucmp)
 	return 1;
-    acceptlast();
+    runhookdef(ACCEPTCOMPHOOK, NULL);
     return menucomplete(args);
 }
 
@@ -555,11 +307,12 @@ acceptandmenucomplete(char **args)
  * position, in a redirection, or in a parameter expansion.   */
 
 /**/
-int lincmd, linredir, ispar, parq, eparq, linwhat, linarr;
+int lincmd, linredir, linarr;
 
 /* The string for the redirection operator. */
 
-static char *rdstr;
+/**/
+char *rdstr;
 
 /* This holds the name of the current command (used to find the right *
  * compctl).                                                          */
@@ -569,7 +322,8 @@ char *cmdstr;
 
 /* This hold the name of the variable we are working on. */
 
-static char *varname;
+/**/
+char *varname;
 
 /* != 0 if we are in a subscript */
 
@@ -591,25 +345,6 @@ int instring, inbackt;
 
 #define quotename(s, e) bslashquote(s, e, instring)
 
-/* 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;
-}
-
 /* Check if the given string is the name of a parameter and if this *
  * parameter is one worth expanding.                                */
 
@@ -628,7 +363,7 @@ checkparams(char *p)
 		    e = 1;
 	    }
     return (n == 1) ? (getsparam(p) != NULL) :
-	(!menucmp && e && isset(RECEXACT));
+	(!menucmp && e && (!hascompmod || isset(RECEXACT)));
 }
 
 /* Check if the given string has wildcards.  The difficulty is that we *
@@ -692,15 +427,10 @@ cmphaswilds(char *str)
 
 /**/
 char *
-check_param(char *s, int set, int test)
+parambeg(char *s)
 {
     char *p;
 
-    zsfree(parpre);
-    parpre = NULL;
-
-    if (!test)
-	ispar = parq = eparq = 0;
     /* Try to find a `$'. */
     for (p = s + offs; p > s && *p != String && *p != Qstring; p--);
     if (*p == String || *p == Qstring) {
@@ -743,10 +473,8 @@ check_param(char *s, int set, int test)
 
 	e = b;
 	if (br) {
-	    while (*e == (test ? Dnull : '"'))
-		e++, parq++;
-	    if (!test)
-		b = e;
+	    while (*e == Dnull)
+		e++;
 	}
 	/* Find the end of the name. */
 	if (*e == Quest || *e == Star || *e == String || *e == Qstring ||
@@ -757,60 +485,17 @@ check_param(char *s, int set, int test)
 	    while (idigit(*e))
 		e++;
 	else if (iident(*e))
-	    while (iident(*e) ||
-		   (comppatmatch && *comppatmatch &&
-		    (*e == Star || *e == Quest)))
+	    while (iident(*e))
 		e++;
 
 	/* Now make sure that the cursor is inside the name. */
 	if (offs <= e - s && offs >= b - s && n <= 0) {
-	    char sav;
-
 	    if (br) {
 		p = e;
-		while (*p == (test ? Dnull : '"'))
-		    p++, parq--, eparq++;
+		while (*p == Dnull)
+		    p++;
 	    }
 	    /* It is. */
-	    if (test)
-		return b;
-	    /* If we were called from makecomplistflags(), we have to set the
-	     * global variables. */
-
-	    if (set) {
-		if (br >= 2) {
-		    mflags |= CMF_PARBR;
-		    if (nest)
-			mflags |= CMF_PARNEST;
-		}
-		/* Get the prefix (anything up to the character before the name). */
-		isuf = dupstring(e);
-		untokenize(isuf);
-		sav = *b;
-		*b = *e = '\0';
-		ripre = dyncat((ripre ? ripre : ""), s);
-		ipre = dyncat((ipre ? ipre : ""), s);
-		*b = sav;
-
-		untokenize(ipre);
-	    }
-	    else
-		parq = eparq = 0;
-
-	    /* Save the prefix. */
-	    if (compfunc) {
-		parflags = (br >= 2 ? CMF_PARBR : 0);
-		sav = *b;
-		*b = '\0';
-		untokenize(parpre = ztrdup(s));
-		*b = sav;
-	    }
-	    /* And adjust wb, we, and offs again. */
-	    offs -= b - s;
-	    wb = cs - offs;
-	    we = wb + e - b;
-	    ispar = (br >= 2 ? 2 : 1);
-	    b[we-wb] = '\0';
 	    return b;
 	}
     }
@@ -824,33 +509,10 @@ static int
 docomplete(int lst)
 {
     char *s, *ol;
-    int olst = lst, chl = 0, ne = noerrs, ocs, ret = 0, omc = menucmp;
+    int olst = lst, chl = 0, ne = noerrs, ocs, ret = 0;
 
-    if (showagain && validlist)
-	showinglist = -2;
-    showagain = 0;
-
-    /* If we are doing a menu-completion... */
-
-    if (menucmp && lst != COMP_LIST_EXPAND && 
-	(!compwidget || compwidget == lastcompwidget)) {
-	do_menucmp(lst);
+    if (runhookdef(BEFORECOMPLETEHOOK, (void *) &lst))
 	return 0;
-    }
-    lastcompwidget = compwidget;
-
-    /* We may have to reset the cursor to its position after the   *
-     * string inserted by the last completion. */
-
-    if (fromcomp & FC_INWORD)
-	if ((cs = lastend) > ll)
-	    cs = ll;
-
-    /* Check if we have to start a menu-completion (via automenu). */
-
-    if (isset(AUTOMENU) && lastambig &&
-	(!isset(BASHAUTOLIST) || lastambig == 2))
-	usemenu = 2;
 
     /* Expand history references before starting completion.  If anything *
      * changed, do no more.                                               */
@@ -930,7 +592,7 @@ docomplete(int lst)
 		/* The word starts with `=', see if we can expand it. */
 		q = s + 1;
 		if (cmdnamtab->getnode(cmdnamtab, q) || hashcmd(q, pathchecked)) {
-		    if (isset(RECEXACT))
+		    if (!hascompmod || isset(RECEXACT))
 			lst = COMP_EXPAND;
 		    else {
 			int t0, n = 0;
@@ -1081,51 +743,11 @@ docomplete(int lst)
     zsfree(qword);
     unmetafy_line();
 
-    if (menucmp && !omc) {
-	struct chdata dat;
+    runhookdef(AFTERCOMPLETEHOOK, (void *) &lst);
 
-	dat.matches = amatches;
-	dat.num = nmatches;
-	dat.cur = NULL;
-	if (runhookdef(MENUSTARTHOOK, (void *) &dat))
-	    menucmp = menuacc = 0;
-    }
     return ret;
 }
 
-/* Do completion, given that we are in the middle of a menu completion.  We *
- * don't need to generate a list of matches, because that's already been    *
- * done by previous commands.  We will either list the completions, or      *
- * insert the next completion.                                              */
-
-/**/
-void
-do_menucmp(int lst)
-{
-    /* Just list the matches if the list was requested. */
-    if (lst == COMP_LIST_COMPLETE) {
-	showinglist = -2;
-	return;
-    }
-    /* Otherwise go to the next match in the array... */
-    HEAPALLOC {
-	do {
-	    if (!*++(minfo.cur)) {
-		do {
-		    if (!(minfo.group = (minfo.group)->next))
-			minfo.group = amatches;
-		} while (!(minfo.group)->mcount);
-		minfo.cur = minfo.group->matches;
-	    }
-	} while (menuacc &&
-		 !hasbrpsfx(*(minfo.cur), minfo.prebr, minfo.postbr));
-	/* ... and insert it into the command line. */
-	metafy_line();
-	do_single(*(minfo.cur));
-	unmetafy_line();
-    } LASTALLOC;
-}
-
 /* 1 if we are completing the prefix */
 static int comppref;
 
@@ -1193,7 +815,7 @@ dupstrspace(const char *str)
  * functions (there's one for each direction).                             */
 
 /**/
-static void
+void
 metafy_line(void)
 {
     int len = ll;
@@ -1209,7 +831,7 @@ metafy_line(void)
 }
 
 /**/
-static void
+void
 unmetafy_line(void)
 {
     cs = ztrsub((char *) line + cs, (char *) line);
@@ -1634,7 +1256,7 @@ get_comp_string(void)
 	/* This variable will hold the current word in quoted form. */
 	qword = ztrdup(s);
 	offs = cs - wb;
-	if ((p = check_param(s, 0, 1))) {
+	if ((p = parambeg(s))) {
 	    for (p = s; *p; p++)
 		if (*p == Dnull)
 		    *p = '"';
@@ -1925,6 +1547,26 @@ get_comp_string(void)
     return (char *)s;
 }
 
+/* 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).              *
+ * The last argument says if we should quote the string.                */
+
+/**/
+int
+inststrlen(char *str, int move, int len)
+{
+    if (!len || !str)
+	return 0;
+    if (len == -1)
+	len = strlen(str);
+    spaceinline(len);
+    strncpy((char *)(line + cs), str, len);
+    if (move)
+	cs += len;
+    return len;
+}
+
 /* Expand the current word. */
 
 /**/
@@ -1980,12 +1622,6 @@ doexpansion(char *s, int lst, int olst, int explincmd)
 	    untokenize(ss);
 	    ss = quotename(ss, NULL);
 	    inststr(ss);
-#if 0
-	    if (nonempty(vl)) {
-		spaceinline(1);
-		line[cs++] = ' ';
-	    }
-#endif
 	    if (olst != COMP_EXPAND_COMPLETE || nonempty(vl) ||
 		(cs && line[cs-1] != '/')) {
 		spaceinline(1);
@@ -2012,3513 +1648,55 @@ gotword(void)
     }
 }
 
-/* This compares two cpattern lists and returns non-zero if they are
- * equal. */
-
-static int
-cmp_cpatterns(Cpattern a, Cpattern b)
-{
-    while (a) {
-	if (a->equiv != b->equiv || memcmp(a->tab, b->tab, 256))
-	    return 0;
-	a = a->next;
-	b = b->next;
-    }
-    return 1;
-}
-
-/* This compares two cmatchers and returns non-zero if they are equal. */
-
-static int
-cmp_cmatchers(Cmatcher a, Cmatcher b)
-{
-    return (a == b ||
-	    (a->flags == b->flags &&
-	     a->llen == b->llen && a->wlen == b->wlen &&
-	     (!a->llen || cmp_cpatterns(a->line, b->line)) &&
-	     (a->wlen <= 0 || cmp_cpatterns(a->word, b->word)) &&
-	     (!(a->flags & CMF_LEFT) ||
-	      (a->lalen == b->lalen &&
-	       (!a->lalen || cmp_cpatterns(a->left, b->left)))) &&
-	     (!(a->flags & CMF_RIGHT) ||
-	      (a->ralen == b->ralen &&
-	       (!a->ralen || cmp_cpatterns(a->right, b->right))))));
-}
-
-/* Add the given matchers to the bmatcher list. */
-
-static void
-add_bmatchers(Cmatcher m)
-{
-    Cmlist old = bmatchers, *q = &bmatchers, n;
-
-    for (; m; m = m->next) {
-	if ((!m->flags && m->wlen > 0 && m->llen > 0) ||
-	    (m->flags == CMF_RIGHT && m->wlen == -1 && !m->llen)) {
-	    *q = n = (Cmlist) zhalloc(sizeof(struct cmlist));
-	    n->matcher = m;
-	    q = &(n->next);
-	}
-    }
-    *q = old;
-}
-
-/* This is called when the matchers in the mstack have changed to
- * ensure that the bmatchers list contains no matchers not in mstack. */
-
-static void
-update_bmatchers(void)
-{
-    Cmlist p = bmatchers, q = NULL, ms;
-    Cmatcher mp;
-    int t;
-
-    while (p) {
-	t = 0;
-	for (ms = mstack; ms && !t; ms = ms->next)
-	    for (mp = ms->matcher; mp && !t; mp = mp->next)
-		t = cmp_cmatchers(mp, p->matcher);
-
-	p = p->next;
-	if (!t) {
-	    if (q)
-		q->next = p;
-	    else
-		bmatchers = p;
-	}
-    }
-}
-
-/* This returns a new Cline structure. */
-
-static Cline
-get_cline(char *l, int ll, char *w, int wl, char *o, int ol, int fl)
-{
-    Cline r;
-
-    /* Prefer to take it from the buffer list (freecl), if there
-     * is none, allocate a new one. */
-
-    if ((r = freecl))
-	freecl = r->next;
-    else
-	r = (Cline) zhalloc(sizeof(*r));
-
-    r->next = NULL;
-    r->line = l; r->llen = ll;
-    r->word = w; r->wlen = wl;
-    r->orig = o; r->olen = ol;
-    r->slen = 0;
-    r->flags = fl;
-    r->prefix = r->suffix = NULL;
-    return r;
-}
-
-/* This frees a cline list. */
-
-static void
-free_cline(Cline l)
-{
-    Cline n;
-
-    while (l) {
-	n = l->next;
-	l->next = freecl;
-	freecl = l;
-	free_cline(l->prefix);
-	free_cline(l->suffix);
-	l = n;
-    }
-}
-
-/* Copy a cline list. */
-
-static Cline
-cp_cline(Cline l, int deep)
-{
-    Cline r = NULL, *p = &r, t, lp = NULL;
-
-    while (l) {
-	if ((t = freecl))
-	    freecl = t->next;
-	else
-	    t = (Cline) zhalloc(sizeof(*t));
-	memcpy(t, l, sizeof(*t));
-	if (deep) {
-	    if (t->prefix)
-		t->prefix = cp_cline(t->prefix, 0);
-	    if (t->suffix)
-		t->suffix = cp_cline(t->suffix, 0);
-	}
-	*p = lp = t;
-	p = &(t->next);
-	l = l->next;
-    }
-    *p = NULL;
-
-    return r;
-}
-
-/* Calculate the length of a cline and its sub-lists. */
-
-static int
-cline_sublen(Cline l)
-{
-    int len = ((l->flags & CLF_LINE) ? l->llen : l->wlen);
-
-    if (l->olen && !((l->flags & CLF_SUF) ? l->suffix : l->prefix))
-	len += l->olen;
-    else {
-	Cline p;
-
-	for (p = l->prefix; p; p = p->next)
-	    len += ((p->flags & CLF_LINE) ? p->llen : p->wlen);
-	for (p = l->suffix; p; p = p->next)
-	    len += ((p->flags & CLF_LINE) ? p->llen : p->wlen);
-    }
-    return len;
-}
-
-/* Set the lengths in the cline lists. */
-
-static void
-cline_setlens(Cline l, int both)
-{
-    while (l) {
-	l->max = cline_sublen(l);
-	if (both)
-	    l->min = l->max;
-	l = l->next;
-    }
-}
-
-/* This sets the CLF_MATCHED flag in the given clines. */
-
-static void
-cline_matched(Cline p)
-{
-    while (p) {
-	p->flags |= CLF_MATCHED;
-	cline_matched(p->prefix);
-	cline_matched(p->suffix);
-
-	p = p->next;
-    }
-}
-
-/* This reverts the order of the elements of the given cline list and
- * returns a pointer to the new head. */
-
-static Cline
-revert_cline(Cline p)
-{
-    Cline r = NULL, n;
-
-    while (p) {
-	n = p->next;
-	p->next = r;
-	r = p;
-	p = n;
-    }
-    return r;
-}
-
-/* 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)
-		in++;
-	    if (out)
-		out++;
-	} else if (!p->tab[c])
-	    return 0;
-
-	s++;
-	p = p->next;
-    }
-    return 1;
-}
-
-/* This splits the given string into a list of cline structs, separated
- * at those places where one of the anchors of an `*' pattern was found.
- * plen gives the number of characters on the line that matched this
- * string. In lp we return a pointer to the last cline struct we build. */
-
-static Cline
-bld_parts(char *str, int len, int plen, Cline *lp)
-{
-    Cline ret = NULL, *q = &ret, n;
-    Cmlist ms;
-    Cmatcher mp;
-    int t, op = plen;
-    char *p = str;
-
-    while (len) {
-	for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
-	    mp = ms->matcher;
-	    if (mp->flags == CMF_RIGHT && mp->wlen == -1 &&
-		!mp->llen && len >= mp->ralen && mp->ralen &&
-		pattern_match(mp->right, str, NULL, NULL)) {
-		int olen = str - p, llen;
-
-		/* We found an anchor, create a new cline. The NEW flag
-		 * is set if the characters before the anchor were not
-		 * on the line. */
-		*q = n = get_cline(NULL, mp->ralen, str, mp->ralen, NULL, 0,
-				   ((plen < 0) ? CLF_NEW : 0));
-
-		/* If there were any characters before the anchor, add
-		 * them as a cline struct. */
-
-		if (p != str) {
-		    llen = (op < 0 ? 0 : op);
-
-		    if (llen > olen)
-			llen = olen;
-		    n->prefix = get_cline(NULL, llen, p, olen, NULL, 0, 0);
-		}
-		q = &(n->next);
-		str += mp->ralen; len -= mp->ralen;
-		plen -= mp->ralen;
-		op -= olen;
-		p = str;
-		t = 1;
-	    }
-	}
-	if (!t) {
-	    /* No anchor was found here, skip. */
-	    str++; len--;
-	    plen--;
-	}
-    }
-    /* This is the cline struct for the remaining string at the end. */
-
-    *q = n = get_cline(NULL, 0, NULL, 0, NULL, 0, (plen < 0 ? CLF_NEW : 0));
-    if (p != str) {
-	int olen = str - p, llen = (op < 0 ? 0 : op);
-
-	if (llen > olen)
-	    llen = olen;
-	n->prefix = get_cline(NULL, llen, p, olen, NULL, 0, 0);
-    }
-    n->next = NULL;
-
-    if (lp)
-	*lp = n;
-
-    return ret;
-}
-
-/* Global variables used during matching: a char-buffer for the string to
- * use for the match, and two cline lists for the two levels we use. */
-
-static char *matchbuf = NULL;
-static int matchbuflen = 0, matchbufadded;
-
-static Cline matchparts, matchlastpart;
-static Cline matchsubs, matchlastsub;
-
-/* This initialises the variables above. */
-
-static void
-start_match(void)
-{
-    if (matchbuf)
-	*matchbuf = '\0';
-    matchbufadded = 0;
-    matchparts = matchlastpart = matchsubs = matchlastsub = NULL;
-}
-
-/* This aborts a matching, freeing the cline lists build. */
-
-static void
-abort_match(void)
-{
-    free_cline(matchparts);
-    free_cline(matchsubs);
-    matchparts = matchsubs = NULL;
-}
-
-/* This adds a new string in the static char buffer. The arguments are
- * the matcher used (if any), the strings from the line and the word
- * and the length of the string from the word. The last argument is
- * non-zero if we are matching a suffix (where the given string has to 
- * be prepended to the contents of the buffer). */
-
-static void
-add_match_str(Cmatcher m, char *l, char *w, int wl, int sfx)
-{
-    /* Get the string and length to insert: either from the line 
-     * or from the match. */
-    if (m && (m->flags & CMF_LINE)) {
-	wl = m->llen; w = l;
-    }
-    if (wl) {
-	/* Probably resize the buffer. */
-	if (matchbuflen - matchbufadded <= wl) {
-	    int blen = matchbuflen + wl + 20;
-	    char *buf;
-
-	    buf = (char *) zalloc(blen);
-	    memcpy(buf, matchbuf, matchbuflen);
-	    zfree(matchbuf, matchbuflen);
-	    matchbuf = buf;
-	    matchbuflen = blen;
-	}
-	/* Insert the string. */
-	if (sfx) {
-	    memmove(matchbuf + wl, matchbuf, matchbufadded + 1);
-	    memcpy(matchbuf, w, wl);
-	} else
-	    memcpy(matchbuf + matchbufadded, w, wl);
-	matchbufadded += wl;
-	matchbuf[matchbufadded] = '\0';
-    }
-}
-
-/* This adds a cline for a word-part during matching. Arguments are the
- * matcher used, pointers to the line and word strings for the anchor,
- * a pointer to the original line string for the whole part, the string
- * before (or after) the anchor that has not yet been added, the length
- * of the line-string for that, and a flag saying if we are matching a 
- * suffix. */
-
-static void
-add_match_part(Cmatcher m, char *l, char *w, int wl,
-	       char *o, int ol, char *s, int sl, int osl, int sfx)
-{
-    Cline p, lp;
-
-    /* If the anchors are equal, we keep only one. */
-
-    if (!strncmp(l, w, wl))
-	l = NULL;
-
-    /* Split the new part into parts and turn the last one into a
-     * `suffix' if we have a left anchor. */
-
-    p = bld_parts(s, sl, osl, &lp);
-
-    p->flags &= ~CLF_NEW;
-    if (m && (m->flags & CMF_LEFT)) {
-	lp->flags |= CLF_SUF;
-	lp->suffix = lp->prefix;
-	lp->prefix = NULL;
-    }
-    /* cline lists for suffixes are sorted from back to front, so we have
-     * to revert the list we got. */
-    if (sfx)
-	p = revert_cline(lp = p);
-    /* Now add the sub-clines we already had. */
-    if (matchsubs) {
-	if (sfx) {
-	    Cline q;
-
-	    if ((q = lp->prefix)) {
-		while (q->next)
-		    q = q->next;
-		q->next = matchsubs;
-	    } else
-		lp->prefix = matchsubs;
-
-	    matchlastsub->next = NULL;
-	} else {
-	    matchlastsub->next = p->prefix;
-	    p->prefix = matchsubs;
-	}
-	matchsubs = matchlastsub = NULL;
-    }
-    /* Store the arguments in the last part-cline. */
-    lp->line = l; lp->llen = wl;
-    lp->word = w; lp->wlen = wl;
-    lp->orig = o; lp->olen = ol;
-    lp->flags &= ~CLF_NEW;
-
-    /* Finally, put the new parts on the list. */
-    if (matchlastpart)
-	matchlastpart->next = p;
-    else
-	matchparts = p;
-    matchlastpart = lp;
-}
-
-/* This adds a new sub-cline. Arguments are the matcher and the strings from
- * the line and the word. */
-
-static void
-add_match_sub(Cmatcher m, char *l, int ll, char *w, int wl)
-{
-    int flags;
-    Cline n;
-
-    /* Check if we are interested only in the string from the line. */
-    if (m && (m->flags & CMF_LINE)) {
-	w = NULL; wl = 0;
-	flags = CLF_LINE;
-    } else
-	flags = 0;
-
-    /* And add the cline. */
-    if (wl || ll) {
-	n = get_cline(l, ll, w, wl, NULL, 0, flags);
-	if (matchlastsub)
-	    matchlastsub->next = n;
-	else
-	    matchsubs = n;
-	matchlastsub = n;
-    }
-}
-
-/* This tests if the string from the line l matches the word w. In bp
- * the offset for the brace is returned, in rwlp the length of the
- * matched prefix or suffix, not including the stuff before or after
- * the last anchor is given. When sfx is non-zero matching is done from
- * the ends of the strings backward, if test is zero, the global variables
- * above are used to build the string for the match and the cline. If
- * part is non-zero, we are satisfied if only a part of the line-string
- * is used (and return the length used). */
-
-static int
-match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
-	  int sfx, int test, int part)
-{
-    int ll = strlen(l), lw = strlen(w), oll = ll, olw = lw;
-    int il = 0, iw = 0, t, ind, add, he = 0, bpc, obc = bc;
-    VARARR(unsigned char, ea, ll + 1);
-    char *ow;
-    Cmlist ms;
-    Cmatcher mp, lm = NULL;
-    Brinfo bp = NULL;
-
-    if (!test) {
-	start_match();
-	bp = *bpp;
-    }
-    /* Adjust the pointers and get the values for subscripting and
-     * incrementing. */
-
-    if (sfx) {
-	l += ll; w += lw;
-	ind = -1; add = -1;
-    } else {
-	ind = 0; add = 1;
-    }
-    /* ow will always point to the beginning (or end) of that sub-string
-     * in w that wasn't put in the match-variables yet. */
-
-    ow = w;
-
-    /* If the brace is at the beginning, we have to treat it now. */
-
-    if (!test && bp && bc >= bp->pos) {
-	bp->curpos = bc;
-	bp = bp->next;
-    }
-    while (ll && lw) {
-	/* First try the matchers. */
-	for (mp = NULL, ms = mstack; !mp && ms; ms = ms->next) {
-	    for (mp = ms->matcher; mp; mp = mp->next) {
-		t = 1;
-		if ((lm && lm == mp) ||
-		    ((oll == ll || olw == lw) &&
-		     (test == 1 || (test && !mp->left && !mp->right)) &&
-		     mp->wlen < 0))
-		    /* If we were called recursively, don't use `*' patterns
-		     * at the beginning (avoiding infinite recursion). */
-		    continue;
-
-		if (mp->wlen < 0) {
-		    int both, loff, aoff, llen, alen, zoff, moff, ct, ict;
-		    char *tp, savl = '\0', savw;
-		    Cpattern ap;
-
-		    /* This is for `*' patterns, first initialise some
-		     * local variables. */
-		    llen = mp->llen;
-		    alen = (mp->flags & CMF_LEFT ? mp->lalen : mp->ralen);
-
-		    /* Give up if we don't have enough characters for the
-		     * line-string and the anchor. */
-		    if (ll < llen + alen || lw < alen)
-			continue;
-
-		    if (mp->flags & CMF_LEFT) {
-			ap = mp->left; zoff = 0; moff = alen;
-			if (sfx) {
-			    both = 0; loff = -llen; aoff = -(llen + alen);
-			} else {
-			    both = 1; loff = alen; aoff = 0;
-			}
-		    } else {
-			ap = mp->right; zoff = alen; moff = 0;
-			if (sfx) {
-			    both = 1; loff = -(llen + alen); aoff = -alen;
-			} else {
-			    both = 0; loff = 0; aoff = llen;
-			}
-		    }
-		    /* Try to match the line pattern and the anchor. */
-		    if (!pattern_match(mp->line, l + loff, NULL, NULL))
-			continue;
-		    if (ap) {
-			if (!pattern_match(ap, l + aoff, NULL, NULL) ||
-			    (both && (!pattern_match(ap, w + aoff, NULL, NULL) ||
-				      !match_parts(l + aoff, w + aoff, alen,
-						   part))))
-				continue;
-		    } else if (!both || il || iw)
-			continue;
-
-		    /* Fine, now we call ourselves recursively to find the
-		     * string matched by the `*'. */
-		    if (sfx) {
-			savl = l[-(llen + zoff)];
-			l[-(llen + zoff)] = '\0';
-		    }
-		    for (t = 0, tp = w, ct = 0, ict = lw - alen + 1;
-			 ict;
-			 tp += add, ct++, ict--) {
-			if ((both &&
-			     (!ap || !test ||
-			      !pattern_match(ap, tp + aoff, NULL, NULL))) ||
-			    (!both &&
-			     pattern_match(ap, tp - moff, NULL, NULL) &&
-			     match_parts(l + aoff , tp - moff, alen, part))) {
-			    if (sfx) {
-				savw = tp[-zoff];
-				tp[-zoff] = '\0';
-				t = match_str(l - ll, w - lw,
-					      NULL, 0, NULL, 1, 2, part);
-				tp[-zoff] = savw;
-			    } else
-				t = match_str(l + llen + moff, tp + moff,
-					      NULL, 0, NULL, 0, 1, part);
-			    if (t || !both)
-				break;
-			}
-		    }
-		    ict = ct;
-		    if (sfx)
-			l[-(llen + zoff)] = savl;
-
-		    /* Have we found a position in w where the rest of l
-		     * matches? */
-		    if (!t)
-			continue;
-
-		    /* Yes, add the strings and clines if this is a 
-		     * top-level call. */
-		    if (!test && (!he || (llen + alen))) {
-			char *op, *lp, *map, *wap, *wmp;
-			int ol;
-
-			if (sfx) {
-			    op = w; ol = ow - w; lp = l - (llen + alen);
-			    map = tp - alen;
-			    if (mp->flags & CMF_LEFT) {
-				wap = tp - alen; wmp = tp;
-			    } else {
-				wap = w - alen; wmp = tp - alen;
-			    }
-			} else {
-			    op = ow; ol = w - ow; lp = l;
-			    map = ow;
-			    if (mp->flags & CMF_LEFT) {
-				wap = w; wmp = w + alen;
-			    } else {
-				wap = tp; wmp = ow;
-			    }
-			}
-			/* If the matcher says that we are only interested
-			 * in the line pattern, we just add that and the
-			 * anchor and the string not added yet. Otherwise
-			 * we add a new part. */
-			if (mp->flags & CMF_LINE) {
-			    add_match_str(NULL, NULL, op, ol, sfx);
-			    add_match_str(NULL, NULL, lp, llen + alen, sfx);
-			    add_match_sub(NULL, NULL, ol, op, ol);
-			    add_match_sub(NULL, NULL, llen + alen,
-					  lp, llen + alen);
-			} else if (sfx) {
-			    add_match_str(NULL, NULL,
-					  map, ct + ol + alen, sfx);
-			    add_match_part(mp, l + aoff, wap, alen,
-					   l + loff, llen, op, ol, ol, sfx);
-			    add_match_sub(NULL, NULL, 0, wmp, ct);
-			} else {
-			    add_match_str(NULL, NULL,
-					  map, ct + ol + alen, sfx);
-			    if (both) {
-				add_match_sub(NULL, NULL, ol, op, ol);
-				ol = -1;
-			    } else
-				ct += ol;
-			    add_match_part(mp, l + aoff, wap, alen,
-					   l + loff, llen, wmp, ct, ol, sfx);
-			}
-		    }
-		    /* Now skip over the matched portion and the anchor. */
-		    llen += alen; alen += ict;
-		    if (sfx) {
-			l -= llen; w -= alen;
-		    } else {
-			l += llen; w += alen;
-		    }
-		    ll -= llen; il += llen;
-		    lw -= alen; iw += alen;
-		    bc += llen;
-
-		    if (!test)
-			while (bp &&
-			       bc >= (bpc = (useqbr ? bp->qpos : bp->pos))) {
-			    bp->curpos = matchbufadded + bpc - bc + obc;
-			    bp = bp->next;
-			}
-		    ow = w;
-
-		    if (!llen && !alen) {
-			lm = mp;
-			if (he)
-			    mp = NULL;
-			else
-			    he = 1;
-		    } else {
-			lm = NULL; he = 0;
-		    }
-		    break;
-		} else if (ll >= mp->llen && lw >= mp->wlen) {
-		    /* Non-`*'-pattern. */
-		    char *tl, *tw;
-		    int tll, tlw, til, tiw;
-
-		    /* We do this only if the line- and word-substrings
-		     * are not equal. */
-		    if (!(mp->flags & (CMF_LEFT | CMF_RIGHT)) &&
-			mp->llen == mp->wlen &&
-			!(sfx ? strncmp(l - mp->llen, w - mp->wlen, mp->llen) :
-			  strncmp(l, w, mp->llen)))
-			continue;
-
-		    /* Using local variables to make the following
-		     * independent of whether we match a prefix or a
-		     * suffix. */
-		    if (sfx) {
-			tl = l - mp->llen; tw = w - mp->wlen;
-			til = ll - mp->llen; tiw = lw - mp->wlen;
-			tll = il + mp->llen; tlw = iw + mp->wlen;
-		    } else {
-			tl = l; tw = w;
-			til = il; tiw = iw;
-			tll = ll; tlw = lw;
-		    }
-		    if (mp->flags & CMF_LEFT) {
-			/* Try to match the left anchor, if any. */
-			if (til < mp->lalen || tiw < mp->lalen)
-			    continue;
-			else if (mp->left)
-			    t = pattern_match(mp->left, tl - mp->lalen,
-					      NULL, NULL) &&
-				pattern_match(mp->left, tw - mp->lalen,
-					      NULL, NULL);
-			else
-			    t = (!sfx && !il && !iw);
-		    }
-		    if (mp->flags & CMF_RIGHT) {
-			/* Try to match the right anchor, if any. */
-			if (tll < mp->llen + mp->ralen ||
-			    tlw < mp->wlen + mp->ralen)
-			    continue;
-			else if (mp->left)
-			    t = pattern_match(mp->right,
-					      tl + mp->llen - mp->ralen,
-					      NULL, NULL) &&
-				pattern_match(mp->right,
-					      tw + mp->wlen - mp->ralen,
-					      NULL, NULL);
-			else
-			    t = (sfx && !il && !iw);
-		    }
-		    /* Now try to match the line and word patterns. */
-		    if (!t ||
-			!pattern_match(mp->line, tl, NULL, ea) ||
-			!pattern_match(mp->word, tw, ea, NULL))
-			continue;
-
-		    /* Probably add the matched strings. */
-		    if (!test) {
-			if (sfx)
-			    add_match_str(NULL, NULL, w, ow - w, 0);
-			else
-			    add_match_str(NULL, NULL, ow, w - ow, 0);
-			add_match_str(mp, tl, tw, mp->wlen, 0);
-			if (sfx)
-			    add_match_sub(NULL, NULL, 0, w, ow - w);
-			else
-			    add_match_sub(NULL, NULL, 0, ow, w - ow);
-
-			add_match_sub(mp, tl, mp->llen, tw, mp->wlen);
-		    }
-		    if (sfx) {
-			l = tl;	w = tw;
-		    } else {
-			l += mp->llen; w += mp->wlen;
-		    }
-		    il += mp->llen; iw += mp->wlen;
-		    ll -= mp->llen; lw -= mp->wlen;
-		    bc += mp->llen;
-
-		    if (!test)
-			while (bp &&
-			       bc >= (bpc = (useqbr ? bp->qpos : bp->pos))) {
-			    bp->curpos = matchbufadded + bpc - bc + obc;
-			    bp = bp->next;
-			}
-		    ow = w;
-		    lm = NULL;
-		    he = 0;
-		    break;
-		}
-	    }
-	}
-	if (mp)
-	    continue;
-
-	if (l[ind] == w[ind]) {
-	    /* No matcher could be used, but the strings have the same
-	     * character here, skip over it. */
-	    l += add; w += add;
-	    il++; iw++;
-	    ll--; lw--;
-	    bc++;
-	    if (!test)
-		while (bp && bc >= (useqbr ? bp->qpos : bp->pos)) {
-		    bp->curpos = matchbufadded + (sfx ? (ow - w) : (w - ow)) + obc;
-		    bp = bp->next;
-		}
-	    lm = NULL;
-	    he = 0;
-	} else {
-	    /* No matcher and different characters: l does not match w. */
-	    if (test)
-		return 0;
-
-	    abort_match();
-
-	    return -1;
-	}
-    }
-    /* If this is a recursive call, we just return if l matched w or not. */
-    if (test)
-	return (part || !ll);
-
-    /* In top-level calls, if ll is non-zero (unmatched portion in l),
-     * we have to free the collected clines. */
-    if (!part && ll) {
-	abort_match();
-
-	return -1;
-    }
-    if (rwlp)
-	*rwlp = iw - (sfx ? ow - w : w - ow);
-
-    /* If we matched a suffix, the anchors stored in the top-clines
-     * will be in the wrong clines: shifted by one. Adjust this. */
-    if (sfx && matchparts) {
-	Cline t, tn, s;
-
-	if (matchparts->prefix || matchparts->suffix) {
-	    t = get_cline(NULL, 0, NULL, 0, NULL, 0, 0);
-	    t->next = matchparts;
-	    if (matchparts->prefix)
-		t->prefix = (Cline) 1;
-	    else
-		t->suffix = (Cline) 1;
-	    matchparts = t;
-	}
-	for (t = matchparts; (tn = t->next); t = tn) {
-	    s = (tn->prefix ? tn->prefix : tn->suffix);
-	    if (t->suffix)
-		t->suffix = s;
-	    else
-		t->prefix = s;
-	}
-	t->prefix = t->suffix = NULL;
-    }
-    /* Finally, return the number of matched characters. */
-
-    *bpp = bp;
-    return (part ? il : iw);
-}
-
-/* Wrapper for match_str(), only for a certain length and only doing
- * the test. */
-
-/**/
-static int
-match_parts(char *l, char *w, int n, int part)
-{
-    char lsav = l[n], wsav = w[n];
-    int ret;
-
-    l[n] = w[n] = '\0';
-    ret = match_str(l, w, NULL, 0, NULL, 0, 1, part);
-    l[n] = lsav;
-    w[n] = wsav;
-
-    return ret;
-}
-
-/* Check if the word w is matched by the strings in pfx and sfx (the prefix
- * and the suffix from the line) or the pattern cp. In clp a cline list for
- * w is returned.
- * qu is non-zero if the words has to be quoted before processed any further.
- * bpl and bsl are used to report the positions where the brace-strings in
- * the prefix and the suffix have to be re-inserted if this match is inserted
- * in the line.
- * The return value is the string to use as a completion or NULL if the prefix
- * and the suffix don't match the word w. */
-
-/**/
-char *
-comp_match(char *pfx, char *sfx, char *w, Patprog cp, Cline *clp, int qu,
-	   Brinfo *bpl, int bcp, Brinfo *bsl, int bcs, int *exact)
-{
-    char *r = NULL;
-
-    if (cp) {
-	/* We have a globcomplete-like pattern, just use that. */
-	int wl;
-
-	r = w;
-	if (!pattry(cp, r))
-	    return NULL;
-    
-	r = (qu ? quotename(r, NULL) : dupstring(r));
-	if (qu == 2 && r[0] == '\\' && r[1] == '~')
-	    chuck(r);
-	/* We still break it into parts here, trying to build a sensible
-	 * cline list for these matches, too. */
-	w = dupstring(w);
-	wl = strlen(w);
-	*clp = bld_parts(w, wl, wl, NULL);
-	*exact = 0;
-    } else {
-	Cline pli, plil;
-	int mpl, rpl, wl;
-
-	w = (qu ? quotename(w, NULL) : dupstring(w));
-	if (qu == 2 && w[0] == '\\' && w[1] == '~')
-	    chuck(w);
-
-	wl = strlen(w);
-
-	/* Always try to match the prefix. */
-
-	useqbr = qu;
-	if ((mpl = match_str(pfx, w, bpl, bcp, &rpl, 0, 0, 0)) < 0)
-	    return NULL;
-
-	if (sfx && *sfx) {
-	    int wpl = matchbufadded, msl, rsl;
-	    VARARR(char, wpfx, wpl);
-	    Cline mli, mlil;
-
-	    /* We also have a suffix to match, so first save the
-	     * contents of the global matching variables. */
-	    memcpy(wpfx, matchbuf, wpl);
-	    if (matchsubs) {
-		Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, 0);
-
-		tmp->prefix = matchsubs;
-		if (matchlastpart)
-		    matchlastpart->next = tmp;
-		else
-		    matchparts = tmp;
-	    }
-	    pli = matchparts;
-	    plil = matchlastpart;
-
-	    /* The try to match the suffix. */
-
-	    if ((msl = match_str(sfx, w + mpl, bsl, bcs, &rsl, 1, 0, 0)) < 0) {
-		free_cline(pli);
-
-		return NULL;
-	    }
-	    /* Matched, so add the string in the middle and the saved
-	     * string for the prefix, and build a combined cline list
-	     * for the prefix and the suffix. */
-	    if (matchsubs) {
-		Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, CLF_SUF);
-
-		tmp->suffix = matchsubs;
-		if (matchlastpart)
-		    matchlastpart->next = tmp;
-		else
-		    matchparts = tmp;
-	    }
-	    add_match_str(NULL, NULL, w + rpl, wl - rpl - rsl, 1);
-	    add_match_str(NULL, NULL, wpfx, wpl, 1);
-
-	    mli = bld_parts(w + rpl, wl - rpl - rsl,
-			    (mpl - rpl) + (msl - rsl), &mlil);
-	    mlil->flags |= CLF_MID;
-	    mlil->slen = msl - rsl;
-	    mlil->next = revert_cline(matchparts);
-
-	    if (plil)
-		plil->next = mli;
-	    else
-		pli = mli;
-	} else {
-	    /* Only a prefix, add the string and a part-cline for it. */
-	    add_match_str(NULL, NULL, w + rpl, wl - rpl, 0);
-
-	    add_match_part(NULL, NULL, NULL, 0, NULL, 0, w + rpl, wl - rpl,
-			   mpl - rpl, 0);
-	    pli = matchparts;
-	}
-	r = dupstring(matchbuf ? matchbuf : "");
-
-	*clp = pli;
-
-	/* Test if the string built is equal to the one from the line. */
-	if (sfx && *sfx) {
-	    int pl = strlen(pfx);
-
-	    *exact = (!strncmp(pfx, w, pl) && !strcmp(sfx, w + pl));
-	} else
-	    *exact = !strcmp(pfx, w);
-    }
-    if (!qu)
-	hasunqu = 1;
-
-    return r;
-}
-
-/* This builds all the possible line patterns for the pattern pat in the
- * buffer line. Initially line is the same as lp, but during recursive
- * calls lp is incremented for storing successive characters. Whenever
- * a full possible string is build, we test if this line matches the
- * string given by wlen and word. The in argument contains the characters
- * to use for the correspondence classes, it was filled by a call to 
- * pattern_match() in the calling function.
- * The return value is the length of the string matched in the word, it
- * is zero if we couldn't build a line that matches the word. */
-
-static int
-bld_line(Cpattern pat, char *line, char *lp,
-	 char *word, int wlen, unsigned char *in, int sfx)
-{
-    if (pat) {
-	/* Still working on the pattern. */
-
-	int i, l;
-	unsigned char c = 0;
-
-	/* Get the number of the character for a correspondence class
-	 * if it has a correxponding class. */
-	if (pat->equiv)
-	    if ((c = *in))
-		in++;
-
-	/* Walk through the table in the pattern and try the characters
-	 * that may appear in the current position. */
-	for (i = 0; i < 256; i++)
-	    if ((pat->equiv && c) ? (c == pat->tab[i]) : pat->tab[i]) {
-		*lp = i;
-		/* We stored the character, now call ourselves to build
-		 * the rest. */
-		if ((l = bld_line(pat->next, line, lp + 1, word, wlen,
-				  in, sfx)))
-		    return l;
-	    }
-    } else {
-	/* We reached the end, i.e. the line string is fully build, now
-	 * see if it matches the given word. */
-
-	Cmlist ms;
-	Cmatcher mp;
-	int l = lp - line, t, rl = 0, ind, add;
-	VARARR(unsigned char, ea, l + 1);
-
-	/* Quick test if the strings are exactly the same. */
-	if (l == wlen && !strncmp(line, word, l))
-	    return l;
-
-	if (sfx) {
-	    line = lp; word += wlen;
-	    ind = -1; add = -1;
-	} else {
-	    ind = 0; add = 1;
-	}
-	/* We loop through the whole line string built. */
-	while (l && wlen) {
-	    if (word[ind] == line[ind]) {
-		/* The same character in both strings, skip over. */
-		line += add; word += add;
-		l--; wlen--; rl++;
-	    } else {
-		t = 0;
-		for (ms = bmatchers; ms && !t; ms = ms->next) {
-		    mp = ms->matcher;
-		    if (!mp->flags && mp->wlen <= wlen && mp->llen <= l &&
-			pattern_match(mp->line, (sfx ? line - mp->llen : line),
-				      NULL, ea) &&
-			pattern_match(mp->word, (sfx ? word - mp->wlen : word),
-				      ea, NULL)) {
-			/* Both the line and the word pattern matched,
-			 * now skip over the matched portions. */
-			if (sfx) {
-			    line -= mp->llen; word -= mp->wlen;
-			} else {
-			    line += mp->llen; word += mp->wlen;
-			}
-			l -= mp->llen; wlen -= mp->wlen; rl += mp->wlen;
-			t = 1;
-		    }
-		}
-		if (!t)
-		    /* Didn't match, give up. */
-		    return 0;
-	    }
-	}
-	if (!l)
-	    /* Unmatched portion in the line built, return matched length. */
-	    return rl;
-    }
-    return 0;
-}
-
-/* This builds a string that may be put on the line that fully matches the
- * given strings. The return value is NULL if no such string could be built
- * or that string in local static memory, dup it. */
-
-static char *
-join_strs(int la, char *sa, int lb, char *sb)
-{
-    static char *rs = NULL;
-    static int rl = 0;
-
-    VARARR(unsigned char, ea, (la > lb ? la : lb) + 1);
-    Cmlist ms;
-    Cmatcher mp;
-    int t, bl, rr = rl;
-    char *rp = rs;
-
-    while (la && lb) {
-	if (*sa != *sb) {
-	    /* Different characters, try the matchers. */
-	    for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
-		mp = ms->matcher;
-		if (!mp->flags && mp->wlen > 0 && mp->llen > 0 &&
-		    mp->wlen <= la && mp->wlen <= lb) {
-		    /* The pattern has no anchors and the word
-		     * pattern fits, try it. */
-		    if ((t = pattern_match(mp->word, sa, NULL, ea)) ||
-			pattern_match(mp->word, sb, NULL, ea)) {
-			/* It matched one of the strings, t says which one. */
-			VARARR(char, line, mp->llen + 1);
-			char **ap, **bp;
-			int *alp, *blp;
-
-			if (t) {
-			    ap = &sa; alp = &la;
-			    bp = &sb; blp = &lb;
-			} else {
-			    ap = &sb; alp = &lb;
-			    bp = &sa; blp = &la;
-			}
-			/* Now try to build a string that matches the other
-			 * string. */
-			if ((bl = bld_line(mp->line, line, line,
-					   *bp, *blp, ea, 0))) {
-			    /* Found one, put it into the return string. */
-			    line[mp->llen] = '\0';
-			    if (rr <= mp->llen) {
-				char *or = rs;
-
-				rs = realloc(rs, (rl += 20));
-				rr += 20;
-				rp += rs - or;
-			    }
-			    memcpy(rp, line, mp->llen);
-			    rp += mp->llen; rr -= mp->llen;
-			    *ap += mp->wlen; *alp -= mp->wlen;
-			    *bp += bl; *blp -= bl;
-			    t = 1;
-			} else
-			    t = 0;
-		    }
-		}
-	    }
-	    if (!t)
-		break;
-	} else {
-	    /* Same character, just take it. */
-	    if (rr <= 1) {
-		char *or = rs;
-
-		rs = realloc(rs, (rl += 20));
-		rr += 20;
-		rp += rs - or;
-	    }
-	    *rp++ = *sa; rr--;
-	    sa++; sb++;
-	    la--; lb--;
-	}
-    }
-    if (la || lb)
-	return NULL;
-
-    *rp = '\0';
-
-    return rs;
-}
-
-/* This compares the anchors stored in two top-level clines. */
-
-static int
-cmp_anchors(Cline o, Cline n, int join)
-{
-    int line = 0;
-    char *j;
-
-    /* First try the exact strings. */
-    if ((!(o->flags & CLF_LINE) && o->wlen == n->wlen &&
-	 (!o->word || !strncmp(o->word, n->word, o->wlen))) ||
-	(line = ((!o->line && !n->line && !o->wlen && !n->wlen) ||
-		 (o->llen == n->llen && o->line && n->line &&
-		  !strncmp(o->line, n->line, o->llen))))) {
-	if (line) {
-	    o->flags |= CLF_LINE;
-	    o->word = NULL;
-	    n->wlen = 0;
-	}
-	return 1;
-    }
-    /* Didn't work, try to build a string matching both anchors. */
-    if (join && !(o->flags & CLF_JOIN) && o->word && n->word &&
-	(j = join_strs(o->wlen, o->word, n->wlen, n->word))) {
-	o->flags |= CLF_JOIN;
-	o->wlen = strlen(j);
-	o->word = dupstring(j);
-
-	return 2;
-    }
-    return 0;
-}
-
-/* Below is the code to join two cline lists. This struct is used to walk
- * through a sub-list. */
-
-typedef struct cmdata *Cmdata;
-
-struct cmdata {
-    Cline cl, pcl;
-    char *str, *astr;
-    int len, alen, olen, line;
-};
-
-/* This is used to ensure that a cmdata struct contains usable data.
- * The return value is non-zero if we reached the end. */
-
-static int
-check_cmdata(Cmdata md, int sfx)
-{
-    /* We will use the str and len fields to contain the next sub-string
-     * in the list. If len is zero, we have to use the next cline. */
-    if (!md->len) {
-	/* If there is none, we reached the end. */
-	if (!md->cl)
-	    return 1;
-
-	/* Otherwise, get the string. Only the line-string or both.
-	 * We also have to adjust the pointer if this is for a suffix. */
-	if (md->cl->flags & CLF_LINE) {
-	    md->line = 1;
-	    md->len = md->cl->llen;
-	    md->str = md->cl->line;
-	} else {
-	    md->line = 0;
-	    md->len = md->olen = md->cl->wlen;
-	    if ((md->str = md->cl->word) && sfx)
-		md->str += md->len;
-	    md->alen = md->cl->llen;
-	    if ((md->astr = md->cl->line) && sfx)
-		md->astr += md->alen;
-	}
-	md->pcl = md->cl;
-	md->cl = md->cl->next;
-    }
-    return 0;
-}
-
-/* This puts the not-yet-matched portion back into the last cline and 
- * returns that. */
-
-static Cline
-undo_cmdata(Cmdata md, int sfx)
-{
-    Cline r = md->pcl;
-
-    if (md->line) {
-	r->word = NULL;
-	r->wlen = 0;
-	r->flags |= CLF_LINE;
-	r->llen = md->len;
-	r->line = md->str - (sfx ? md->len : 0);
-    } else if (md->len != md->olen) {
-	r->wlen = md->len;
-	r->word = md->str - (sfx ? md->len : 0);
-    }
-    return r;
-}
-
-/* This tries to build a string matching a sub-string in a sub-cline
- * that could not be matched otherwise. */
-
-static Cline
-join_sub(Cmdata md, char *str, int len, int *mlen, int sfx, int join)
-{
-    if (!check_cmdata(md, sfx)) {
-	char *ow = str, *nw = md->str;
-	int ol = len, nl = md->len;
-	Cmlist ms;
-	Cmatcher mp;
-	VARARR(unsigned char, ea, (ol > nl ? ol : nl) + 1);
-	int t;
-
-	if (sfx) {
-	    ow += ol; nw += nl;
-	}
-	for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
-	    mp = ms->matcher;
-	    /* We use only those patterns that match a non-empty
-	     * string in both the line and the word and that have
-	     * no anchors. */
-	    if (!mp->flags && mp->wlen > 0 && mp->llen > 0) {
-		/* We first test, if the old string matches already the
-		 * new one. */
-		if (mp->llen <= ol && mp->wlen <= nl &&
-		    pattern_match(mp->line, ow - (sfx ? mp->llen : 0),
-				  NULL, ea) &&
-		    pattern_match(mp->word, nw - (sfx ? mp->wlen : 0),
-				  ea, NULL)) {
-		    /* It did, update the contents of the cmdata struct
-		     * and return a cline for the matched part. */
-		    if (sfx)
-			md->str -= mp->wlen;
-		    else
-			md->str += mp->wlen;
-		    md->len -= mp->wlen;
-		    *mlen = mp->llen;
-
-		    return get_cline(NULL, 0, ow - (sfx ? mp->llen : 0),
-				     mp->llen, NULL, 0, 0);
-		}
-		/* Otherwise we will try to build a string that matches
-		 * both strings. But try the pattern only if the word-
-		 * pattern matches one of the strings. */
-		if (join && mp->wlen <= ol && mp->wlen <= nl &&
-		    ((t = pattern_match(mp->word, ow - (sfx ? mp->wlen : 0),
-				       NULL, ea)) ||
-		     pattern_match(mp->word, nw - (sfx ? mp->wlen : 0),
-				   NULL, ea))) {
-		    VARARR(char, line, mp->llen + 1);
-		    int bl;
-
-		    /* Then build all the possible lines and see
-		     * if one of them matches the other string. */
-		    if ((bl = bld_line(mp->line, line, line,
-				       (t ? nw : ow), (t ? nl : ol),
-				       ea, sfx))) {
-			/* Yep, one of the lines matched the other
-			 * string. */
-			line[mp->llen] = '\0';
-
-			if (t) {
-			    ol = mp->wlen; nl = bl;
-			} else {
-			    ol = bl; nl = mp->wlen;
-			}
-			if (sfx)
-			    md->str -= nl;
-			else
-			    md->str += nl;
-			md->len -= nl;
-			*mlen = ol;
-
-			return get_cline(NULL, 0, dupstring(line), mp->llen,
-					 NULL, 0, CLF_JOIN);
-		    }
-		}
-	    }
-	}
-    }
-    return NULL;
-}
-
-/* This is used to match a sub-string in a sub-cline. The length of the
- * matched portion is returned. This tests only for exact equality. */
-
-static int
-sub_match(Cmdata md, char *str, int len, int sfx)
-{
-    int ret = 0, l, ind, add;
-    char *p, *q;
-
-    if (sfx) {
-	str += len;
-	ind = -1; add = -1;
-    } else {
-	ind = 0; add = 1;
-    }
-    /* str and len describe the old string, in md we have the new one. */
-    while (len) {
-	if (check_cmdata(md, sfx))
-	    return ret;
-
-	for (l = 0, p = str, q = md->str;
-	     l < len && l < md->len && p[ind] == q[ind];
-	     l++, p += add, q += add);
-
-	if (l) {
-	    /* There was a common prefix, use it. */
-	    md->len -= l; len -= l;
-	    if (sfx) {
-		md->str -= l; str -= l;
-	    } else {
-		md->str += l; str += l;
-	    }
-	    ret += l;
-	} else if (md->line || md->len != md->olen || !md->astr)
-	    return ret;
-	else {
-	    /* We still have the line string to try. */
-	    md->line = 1;
-	    md->len = md->alen;
-	    md->str = md->astr;
-	}
-    }
-    return ret;
-}
-
-/* This is used to build a common prefix or suffix sub-list. If requested
- * it returns the unmatched cline lists in orest and nrest. */
-
-static void
-join_psfx(Cline ot, Cline nt, Cline *orest, Cline *nrest, int sfx)
-{
-    Cline p = NULL, o, n;
-    struct cmdata md, omd;
-    char **sstr = NULL;
-    int len, join = 0, line = 0, *slen = NULL;
-
-    if (sfx) {
-	o = ot->suffix; n = nt->suffix;
-    } else {
-	o = ot->prefix;	n = nt->prefix;
-    }
-    if (!o) {
-	if (orest)
-	    *orest = NULL;
-	if (nrest)
-	    *nrest = n;
-
-	return;
-    }
-    if (!n) {
-	if (sfx)
-	    ot->suffix = NULL;
-	else
-	    ot->prefix = NULL;
-
-	if (orest)
-	    *orest = o;
-	else
-	    free_cline(o);
-	if (nrest)
-	    *nrest = NULL;
-	return;
-    }
-    md.cl = n;
-    md.len = 0;
-
-    /* Walk through the old list. */
-    while (o) {
-	join = 0;
-	memcpy(&omd, &md, sizeof(struct cmdata));
-
-	/* We first get the length of the prefix equal in both strings. */
-	if (o->flags & CLF_LINE) {
-	    if ((len = sub_match(&md, o->line, o->llen, sfx)) != o->llen) {
-		join = 1; line = 1; slen = &(o->llen); sstr = &(o->line);
-	    }
-	} else if ((len = sub_match(&md, o->word, o->wlen, sfx)) != o->wlen) {
-	    if (o->line) {
-		memcpy(&md, &omd, sizeof(struct cmdata));
-		o->flags |= CLF_LINE | CLF_DIFF;
-
-		continue;
-	    }
-	    join = 1; line = 0; slen = &(o->wlen); sstr = &(o->word);
-	}
-	if (join) {
-	    /* There is a rest that is different in the two lists,
-	     * we try to build a new cline matching both strings. */
-	    Cline joinl;
-	    int jlen;
-
-	    if ((joinl = join_sub(&md, *sstr + len, *slen - len,
-				  &jlen, sfx, !(o->flags & CLF_JOIN)))) {
-		/* We have one, insert it into the list. */
-		joinl->flags |= CLF_DIFF;
-		if (len + jlen != *slen) {
-		    Cline rest;
-
-		    rest = get_cline(NULL, 0, *sstr + (sfx ? 0 : len + jlen),
-				     *slen - len - jlen, NULL, 0, 0);
-
-		    rest->next = o->next;
-		    joinl->next = rest;
-		} else
-		    joinl->next = o->next;
-
-		if (len) {
-		    if (sfx)
-			*sstr += *slen - len;
-		    *slen = len;
-		    o->next = joinl;
-		} else {
-		    o->next = NULL;
-		    free_cline(o);
-		    if (p)
-			p->next = joinl;
-		    else if (sfx)
-			ot->suffix = joinl;
-		    else
-			ot->prefix = joinl;
-		}
-		o = joinl;
-		join = 0;
-	    }
-	}
-	if (join) {
-	    /* We couldn't build a cline for a common string, so we
-	     * cut the list here. */
-	    if (len) {
-		Cline r;
-
-		if (orest) {
-		    if (line)
-			r = get_cline(o->line + len, *slen - len,
-				      NULL, 0, NULL, 0, o->flags);
-		    else
-			r = get_cline(NULL, 0, o->word + len, *slen - len,
-				      NULL, 0, o->flags);
-
-		    r->next = o->next;
-		    *orest = r;
-
-		    *slen = len;
-		    o->next = NULL;
-		} else {
-		    if (sfx)
-			*sstr += *slen - len;
-		    *slen = len;
-		    free_cline(o->next);
-		    o->next = NULL;
-		}
-	    } else {
-		if (p)
-		    p->next = NULL;
-		else if (sfx)
-		    ot->suffix = NULL;
-		else
-		    ot->prefix = NULL;
-
-		if (orest)
-		    *orest = o;
-		else
-		    free_cline(o);
-	    }
-	    if (!orest || !nrest)
-		ot->flags |= CLF_MISS;
-
-	    if (nrest)
-		*nrest = undo_cmdata(&md, sfx);
-
-	    return;
-	}
-	p = o;
-	o = o->next;
-    }
-    if (md.len || md.cl)
-	ot->flags |= CLF_MISS;
-    if (orest)
-	*orest = NULL;
-    if (nrest)
-	*nrest = undo_cmdata(&md, sfx);
-}
-
-/* This builds the common prefix and suffix for a mid-cline -- the one
- * describing the place where the prefix and the suffix meet. */
-
-static void
-join_mid(Cline o, Cline n)
-{
-    if (o->flags & CLF_JOIN) {
-	/* The JOIN flag is set in the old cline struct if it was
-	 * already joined with another one. In this case the suffix
-	 * field contains the suffix from previous calls. */
-	Cline nr;
-
-	join_psfx(o, n, NULL, &nr, 0);
-
-	n->suffix = revert_cline(nr);
-
-	join_psfx(o, n, NULL, NULL, 1);
-    } else {
-	/* This is the first time for both structs, so the prefix field
-	 * contains the whole sub-list. */
-	Cline or, nr;
-
-	o->flags |= CLF_JOIN;
-
-	/* We let us give both rests and use them as the suffixes. */
-	join_psfx(o, n, &or, &nr, 0);
-
-	if (or)
-	    or->llen = (o->slen > or->wlen ? or->wlen : o->slen);
-	o->suffix = revert_cline(or);
-	n->suffix = revert_cline(nr);
-
-	join_psfx(o, n, NULL, NULL, 1);
-    }
-    n->suffix = NULL;
-}
-
-/* This turns the sequence of anchor cline structs from b to e into a
- * prefix sequence, puts it before the prefix of e and then tries to
- * join that with the prefix of a.
- * This is needed if some matches had a anchor match spec and others
- * didn't. */
-
-static void
-sub_join(Cline a, Cline b, Cline e, int anew)
-{
-    if (!e->suffix && a->prefix) {
-	Cline op = e->prefix, n = NULL, *p = &n, t, ca;
-	int min = 0, max = 0;
-
-	for (; b != e; b = b->next) {
-	    if ((*p = t = b->prefix)) {
-		while (t->next)
-		    t = t->next;
-		p = &(t->next);
-	    }
-	    b->suffix = b->prefix = NULL;
-	    b->flags &= ~CLF_SUF;
-	    min += b->min;
-	    max += b->max;
-	    *p = b;
-	    p = &(b->next);
-	}
-	*p = e->prefix;
-	ca = a->prefix;
-
-	while (n != op) {
-	    e->prefix = cp_cline(n, 0);
-	    a->prefix = cp_cline(ca, 0);
-
-	    if (anew) {
-		join_psfx(e, a, NULL, NULL, 0);
-		if (e->prefix) {
-		    e->min += min;
-		    e->max += max;
-		    break;
-		}
-	    } else {
-		join_psfx(e, a, NULL, NULL, 0);
-		if (a->prefix) {
-		    a->min += min;
-		    a->max += max;
-		    break;
-		}
-	    }
-	    min -= n->min;
-	    max -= n->max;
-
-	    n = n->next;
-	}
-    }
-}
-
-/* This simplifies the cline list given as the first argument so that
- * it also matches the second list. */
-
-static Cline
-join_clines(Cline o, Cline n)
-{
-    cline_setlens(n, 1);
-
-    /* First time called, just return the new list. On further invocations
-     * we will get it as the first argument. */
-    if (!o)
-	return n;
-    else {
-	Cline oo = o, nn = n, po = NULL, pn = NULL;
-
-	/* Walk through the lists. */
-	while (o && n) {
-	    /* If one of them describes a new part and the other one does
-	     * not, synchronise them by searching an old part in the
-	     * other list. */
-	    if ((o->flags & CLF_NEW) && !(n->flags & CLF_NEW)) {
-		Cline t, tn;
-
-		for (t = o; (tn = t->next) && (tn->flags & CLF_NEW); t = tn);
-		if (tn && cmp_anchors(tn, n, 0)) {
-		    sub_join(n, o, tn, 1);
-
-		    if (po)
-			po->next = tn;
-		    else
-			oo = tn;
-		    t->next = NULL;
-		    free_cline(o);
-		    o = tn;
-		    o->flags |= CLF_MISS;
-		    continue;
-		}
-	    }
-	    if (!(o->flags & CLF_NEW) && (n->flags & CLF_NEW)) {
-		Cline t, tn;
-
-		for (t = n; (tn = t->next) && (tn->flags & CLF_NEW); t = tn);
-		if (tn && cmp_anchors(o, tn, 0)) {
-		    sub_join(o, n, tn, 0);
-
-		    n = tn;
-		    o->flags |= CLF_MISS;
-		    continue;
-		}
-	    }
-	    /* Almost the same as above, but for the case that they
-	     * describe different types of parts (prefix, suffix, or mid). */
-	    if ((o->flags & (CLF_SUF | CLF_MID)) !=
-		(n->flags & (CLF_SUF | CLF_MID))) {
-		Cline t, tn;
-
-		for (t = n;
-		     (tn = t->next) &&
-			 (tn->flags & (CLF_SUF | CLF_MID)) !=
-			 (o->flags  & (CLF_SUF | CLF_MID));
-		     t = tn);
-		if (tn && cmp_anchors(o, tn, 1)) {
-		    sub_join(o, n, tn, 0);
-
-		    n = tn;
-		    continue;
-		}
-		for (t = o;
-		     (tn = t->next) &&
-			 (tn->flags & (CLF_SUF | CLF_MID)) !=
-			 (n->flags  & (CLF_SUF | CLF_MID));
-		     t = tn);
-		if (tn && cmp_anchors(tn, n, 1)) {
-		    sub_join(n, o, tn, 1);
-		    if (po)
-			po->next = tn;
-		    else
-			oo = tn;
-		    t->next = NULL;
-		    free_cline(o);
-		    o = tn;
-		    continue;
-		}
-		if (o->flags & CLF_MID) {
-		    o->flags = (o->flags & ~CLF_MID) | (n->flags & CLF_SUF);
-		    if (n->flags & CLF_SUF) {
-			free_cline(o->prefix);
-			o->prefix = NULL;
-		    } else {
-			free_cline(o->suffix);
-			o->suffix = NULL;
-		    }
-		}
-		break;
-	    }
-	    /* Now see if they have matching anchors. If not, cut the list. */
-	    if (!(o->flags & CLF_MID) && !cmp_anchors(o, n, 1)) {
-		Cline t, tn;
-
-		for (t = n; (tn = t->next) && !cmp_anchors(o, tn, 1); t = tn);
-
-		if (tn) {
-		    sub_join(o, n, tn, 0);
-
-		    n = tn;
-		    o->flags |= CLF_MISS;
-		    continue;
-		} else {
-		    for (t = o; (tn = t->next) && !cmp_anchors(n, tn, 1);
-			 t = tn);
-
-		    if (tn) {
-			sub_join(n, o, tn, 1);
-
-			if (po)
-			    po->next = tn;
-			else
-			    oo = tn;
-			o = tn;
-			o->flags |= CLF_MISS;
-			continue;
-		    } else {
-			if (o->flags & CLF_SUF)
-			    break;
-
-			o->word = o->line = o->orig = NULL;
-			o->wlen = 0;
-			free_cline(o->next);
-			o->next = NULL;
-			o->flags |= CLF_MISS;
-		    }
-		}
-	    }
-	    /* Ok, they are equal, now copy the information about the
-             * original string if needed, calculate minimum and maximum
-	     * lengths, and join the sub-lists. */
-	    if (!o->orig && !o->olen) {
-		o->orig = n->orig;
-		o->olen = n->olen;
-	    }
-	    if (n->min < o->min)
-		o->min = n->min;
-	    if (n->max > o->max)
-		o->max = n->max;
-	    if (o->flags & CLF_MID)
-		join_mid(o, n);
-	    else
-		join_psfx(o, n, NULL, NULL, (o->flags & CLF_SUF));
-
-	    po = o;
-	    o = o->next;
-	    pn = n;
-	    n = n->next;
-	}
-	/* Free the rest of the old list. */
-	if (o) {
-	    if (po)
-		po->next = NULL;
-	    else
-		oo = NULL;
-
-	    free_cline(o);
-	}
-	free_cline(nn);
-
-	return oo;
-    }
-}
-
-/* This adds all the data we have for a match. */
-
-/**/
-Cmatch
-add_match_data(int alt, char *str, Cline line,
-	       char *ipre, char *ripre, char *isuf,
-	       char *pre, char *prpre,
-	       char *ppre, Cline pline,
-	       char *psuf, Cline sline,
-	       char *suf, int flags, int exact)
-{
-    Cmatch cm;
-    Aminfo ai = (alt ? fainfo : ainfo);
-    int palen, salen, qipl, ipl, pl, ppl, qisl, isl, psl;
-    int sl, lpl, lsl, ml;
-
-    palen = salen = qipl = ipl = pl = ppl = qisl = isl = psl = 0;
-
-    DPUTS(!line, "BUG: add_match_data() without cline");
-
-    cline_matched(line);
-    if (pline)
-	cline_matched(pline);
-    if (sline)
-	cline_matched(sline);
-
-    /* If there is a path suffix, we build a cline list for it and
-     * append it to the list for the match itself. */
-    if (!sline && psuf)
-	salen = (psl = strlen(psuf));
-    if (isuf)
-	salen += (isl = strlen(isuf));
-    if (qisuf)
-	salen += (qisl = strlen(qisuf));
-
-    if (salen) {
-	char *asuf = (char *) zhalloc(salen);
-	Cline pp, p, s, sl = NULL;
-	
-
-	if (psl)
-	    memcpy(asuf, psuf, psl);
-	if (isl)
-	    memcpy(asuf + psl, isuf, isl);
-	if (qisl)
-	    memcpy(asuf + psl + isl, qisuf, qisl);
-
-	for (pp = NULL, p = line; p->next; pp = p, p = p->next);
-
-	if (salen > qisl) {
-	    s = bld_parts(asuf, salen - qisl, salen - qisl, &sl);
-
-	    if (sline) {
-		Cline sp;
-
-		sline = cp_cline(sline, 1);
-
-		for (sp = sline; sp->next; sp = sp->next);
-		sp->next = s;
-		s = sline;
-	    }
-	    if (!(p->flags & (CLF_SUF | CLF_MID)) &&
-		!p->llen && !p->wlen && !p->olen) {
-		if (p->prefix) {
-		    Cline q;
-
-		    for (q = p->prefix; q->next; q = q->next);
-		    q->next = s->prefix;
-		    s->prefix = p->prefix;
-		    p->prefix = NULL;
-		}
-		s->flags |= (p->flags & CLF_MATCHED);
-		free_cline(p);
-		if (pp)
-		    pp->next = s;
-		else
-		    line = s;
-	    } else
-		p->next = s;
-	}
-	if (qisl) {
-	    Cline qsl = bld_parts(asuf + psl + isl, qisl, qisl, NULL);
-
-	    qsl->flags |= CLF_SUF;
-	    qsl->suffix = qsl->prefix;
-	    qsl->prefix = NULL;
-	    if (sl)
-		sl->next = qsl;
-	    else if (sline) {
-		Cline sp;
-
-		sline = cp_cline(sline, 1);
-
-		for (sp = sline; sp->next; sp = sp->next);
-		sp->next = qsl;
-		p->next = sline;
-	    } else
-		p->next = qsl;
-	}
-    } else if (sline) {
-	Cline p;
-
-	for (p = line; p->next; p = p->next);
-	p->next = cp_cline(sline, 1);
-    }
-    /* The prefix is handled differently because the completion code
-     * is much more eager to insert the -P prefix than it is to insert
-     * the -S suffix. */
-    if (qipre)
-	palen = (qipl = strlen(qipre));
-    if (ipre)
-	palen += (ipl = strlen(ipre));
-    if (pre)
-	palen += (pl = strlen(pre));
-    if (!pline && ppre)
-	palen += (ppl = strlen(ppre));
-
-    if (pl) {
-	if (ppl || pline) {
-	    Cline lp, p;
-
-	    if (pline)
-		for (p = cp_cline(pline, 1), lp = p; lp->next; lp = lp->next);
-	    else
-		p = bld_parts(ppre, ppl, ppl, &lp);
-
-	    if (lp->prefix && !(line->flags & (CLF_SUF | CLF_MID)) &&
-		!p->llen && !p->wlen && !p->olen) {
-		Cline lpp;
-
-		for (lpp = lp->prefix; lpp->next; lpp = lpp->next);
-
-		lpp->next = line->prefix;
-		line->prefix = lp->prefix;
-		lp->prefix = NULL;
-
-		free_cline(lp);
-
-		if (p != lp) {
-		    Cline q;
-
-		    for (q = p; q->next != lp; q = q->next);
-
-		    q->next = line;
-		    line = p;
-		}
-	    } else {
-		lp->next = line;
-		line = p;
-	    }
-	}
-	if (pl) {
-	    Cline lp, p = bld_parts(pre, pl, pl, &lp);
-
-	    lp->next = line;
-	    line = p;
-	}
-	if (ipl) {
-	    Cline lp, p = bld_parts(ipre, ipl, ipl, &lp);
-
-	    lp->next = line;
-	    line = p;
-	}
-	if (qipl) {
-	    Cline lp, p = bld_parts(qipre, qipl, qipl, &lp);
-
-	    lp->next = line;
-	    line = p;
-	}
-    } else if (palen || pline) {
-	Cline p, lp;
-
-	if (palen) {
-	    char *apre = (char *) zhalloc(palen);
-
-	    if (qipl)
-		memcpy(apre, qipre, qipl);
-	    if (ipl)
-		memcpy(apre + qipl, ipre, ipl);
-	    if (pl)
-		memcpy(apre + qipl + ipl, pre, pl);
-	    if (ppl)
-		memcpy(apre + qipl + ipl + pl, ppre, ppl);
-
-	    p = bld_parts(apre, palen, palen, &lp);
-
-	    if (pline)
-		for (lp->next = cp_cline(pline, 1); lp->next; lp = lp->next);
-	} else
-	    for (p = lp = cp_cline(pline, 1); lp->next; lp = lp->next);
-
-	if (lp->prefix && !(line->flags & (CLF_SUF | CLF_MID)) &&
-	    !p->llen && !p->wlen && !p->olen) {
-	    Cline lpp;
-
-	    for (lpp = lp->prefix; lpp->next; lpp = lpp->next);
-
-	    lpp->next = line->prefix;
-	    line->prefix = lp->prefix;
-	    lp->prefix = NULL;
-
-	    free_cline(lp);
-
-	    if (p != lp) {
-		Cline q;
-
-		for (q = p; q->next != lp; q = q->next);
-
-		q->next = line;
-		line = p;
-	    }
-	} else {
-	    lp->next = line;
-	    line = p;
-	}
-    }
-    /* Allocate and fill the match structure. */
-    cm = (Cmatch) zhalloc(sizeof(struct cmatch));
-    cm->str = str;
-    cm->ppre = (ppre && *ppre ? ppre : NULL);
-    cm->psuf = (psuf && *psuf ? psuf : NULL);
-    cm->prpre = ((flags & CMF_FILE) && prpre && *prpre ? prpre : NULL);
-    if (qipre && *qipre)
-	cm->ipre = (ipre && *ipre ? dyncat(qipre, ipre) : dupstring(qipre));
-    else
-	cm->ipre = (ipre && *ipre ? ipre : NULL);
-    cm->ripre = (ripre && *ripre ? ripre : NULL);
-    if (qisuf && *qisuf)
-	cm->isuf = (isuf && *isuf ? dyncat(isuf, qisuf) : dupstring(qisuf));
-    else
-	cm->isuf = (isuf && *isuf ? isuf : NULL);
-    cm->pre = pre;
-    cm->suf = suf;
-    cm->flags = flags;
-    if (nbrbeg) {
-	int *p;
-	Brinfo bp;
-
-	cm->brpl = (int *) zhalloc(nbrbeg * sizeof(int));
-
-	for (p = cm->brpl, bp = brbeg; bp; p++, bp = bp->next)
-	    *p = bp->curpos;
-    } else
-	cm->brpl = NULL;
-    if (nbrend) {
-	int *p;
-	Brinfo bp;
-
-	cm->brsl = (int *) zhalloc(nbrend * sizeof(int));
-
-	for (p = cm->brsl, bp = brend; bp; p++, bp = bp->next)
-	    *p = bp->curpos;
-    } else
-	cm->brsl = NULL;
-    cm->qipl = qipl;
-    cm->qisl = qisl;
-    cm->autoq = (autoq ? autoq : (inbackt ? '`' : '\0'));
-    cm->rems = cm->remf = cm->disp = NULL;
-
-    if ((lastprebr || lastpostbr) && !hasbrpsfx(cm, lastprebr, lastpostbr))
-	return NULL;
-
-    /* Then build the unambiguous cline list. */
-    ai->line = join_clines(ai->line, line);
-
-    mnum++;
-    ai->count++;
-
-    addlinknode((alt ? fmatches : matches), cm);
-
-    newmatches = 1;
-
-    /* One more match for this explanation. */
-    if (curexpl) {
-	if (alt)
-	    curexpl->fcount++;
-	else
-	    curexpl->count++;
-    }
-    if (!ai->firstm)
-	ai->firstm = cm;
-
-    sl = strlen(str);
-    lpl = (cm->ppre ? strlen(cm->ppre) : 0);
-    lsl = (cm->psuf ? strlen(cm->psuf) : 0);
-    ml = sl + lpl + lsl;
-
-    if (ml < minmlen)
-	minmlen = ml;
-    if (ml > maxmlen)
-	maxmlen = ml;
-
-    /* Do we have an exact match? More than one? */
-    if (exact) {
-	if (!ai->exact) {
-	    ai->exact = 1;
-	    if (incompfunc) {
-		/* If a completion widget is active, we make the exact
-		 * string available in `compstate'. */
-
-		char *e;
-
-		zsfree(compexactstr);
-		compexactstr = e = (char *) zalloc(ml + 1);
-		if (cm->ppre) {
-		    strcpy(e, cm->ppre);
-		    e += lpl;
-		}
-		strcpy(e, str);
-		e += sl;
-		if (cm->psuf)
-		    strcpy(e, cm->psuf);
-		comp_setunsetptr(0, 0, CP_EXACTSTR, 0);
-	    }
-	    ai->exactm = cm;
-	} else {
-	    ai->exact = 2;
-	    ai->exactm = NULL;
-	    if (incompfunc)
-		comp_setunsetptr(0, 0, 0, CP_EXACTSTR);
-	}
-    }
-    return cm;
-}
-
-/* This stores the strings from the list in an array. */
-
-/**/
-void
-set_list_array(char *name, LinkList l)
-{
-    char **a, **p;
-    LinkNode n;
-
-    a = (char **) zalloc((countlinknodes(l) + 1) * sizeof(char *));
-    for (p = a, n = firstnode(l); n; incnode(n))
-	*p++ = ztrdup((char *) getdata(n));
-    *p = NULL;
-
-    setaparam(name, a);
-}
-
-/* This is used by compadd to add a couple of matches. The arguments are
- * the strings given via options. The last argument is the array with
- * the matches. */
-
-/**/
-int
-addmatches(Cadata dat, char **argv)
-{
-    char *s, *ms, *lipre = NULL, *lisuf = NULL, *lpre = NULL, *lsuf = NULL;
-    char **aign = NULL, **dparr = NULL, oaq = autoq, *oppre = dat->ppre;
-    char *oqp = qipre, *oqs = qisuf, qc, **disp = NULL;
-    int lpl, lsl, pl, sl, bcp = 0, bcs = 0, bpadd = 0, bsadd = 0;
-    int llpl = 0, llsl = 0, nm = mnum, gflags = 0, ohp = haspattern;
-    int oisalt = 0, isalt, isexact, doadd, ois = instring, oib = inbackt;
-    Cline lc = NULL, pline = NULL, sline = NULL;
-    Cmatch cm;
-    struct cmlist mst;
-    Cmlist oms = mstack;
-    Patprog cp = NULL;
-    LinkList aparl = NULL, oparl = NULL, dparl = NULL;
-    Brinfo bp, bpl = brbeg, obpl, bsl = brend, obsl;
-
-    for (bp = brbeg; bp; bp = bp->next)
-	bp->curpos = ((dat->aflags & CAF_QUOTE) ? bp->pos : bp->qpos);
-    for (bp = brend; bp; bp = bp->next)
-	bp->curpos = ((dat->aflags & CAF_QUOTE) ? bp->pos : bp->qpos);
-
-    if (dat->flags & CMF_ISPAR)
-	dat->flags |= parflags;
-    if (compquote && (qc = *compquote)) {
-	if (qc == '`') {
-	    instring = 0;
-	    inbackt = 0;
-	    autoq = '\0';
-	} else {
-	    instring = (qc == '\'' ? 1 : 2);
-	    inbackt = 0;
-	    autoq = qc;
-	}
-    } else {
-	instring = inbackt = 0;
-	autoq = '\0';
-    }
-    qipre = ztrdup(compqiprefix ? compqiprefix : "");
-    qisuf = ztrdup(compqisuffix ? compqisuffix : "");
-
-    /* Switch back to the heap that was used when the completion widget
-     * was invoked. */
-    SWITCHHEAPS(compheap) {
-	HEAPALLOC {
-	    if ((doadd = (!dat->apar && !dat->opar && !dat->dpar)) &&
-		(dat->aflags & CAF_MATCH))
-		hasmatched = 1;
-	    if (dat->apar)
-		aparl = newlinklist();
-	    if (dat->opar)
-		oparl = newlinklist();
-	    if (dat->dpar) {
-		if (*(dat->dpar) == '(')
-		    dparr = NULL;
-		else if ((dparr = get_user_var(dat->dpar)) && !*dparr)
-		    dparr = NULL;
-		dparl = newlinklist();
-	    }
-	    if (dat->exp) {
-		curexpl = (Cexpl) zhalloc(sizeof(struct cexpl));
-		curexpl->count = curexpl->fcount = 0;
-		curexpl->str = dupstring(dat->exp);
-	    } else
-		curexpl = NULL;
-
-	    /* Store the matcher in our stack of matchers. */
-	    if (dat->match) {
-		mst.next = mstack;
-		mst.matcher = dat->match;
-		mstack = &mst;
-
-		if (!mnum)
-		    add_bmatchers(dat->match);
-
-		addlinknode(matchers, dat->match);
-		dat->match->refc++;
-	    }
-	    if (mnum && (mstack || bmatchers))
-		update_bmatchers();
-
-	    /* Get the suffixes to ignore. */
-	    if (dat->ign)
-		aign = get_user_var(dat->ign);
-	    /* Get the display strings. */
-	    if (dat->disp)
-		if ((disp = get_user_var(dat->disp)))
-		    disp--;
-	    /* Get the contents of the completion variables if we have
-	     * to perform matching. */
-	    if (dat->aflags & CAF_MATCH) {
-		lipre = dupstring(compiprefix);
-		lisuf = dupstring(compisuffix);
-		lpre = dupstring(compprefix);
-		lsuf = dupstring(compsuffix);
-		llpl = strlen(lpre);
-		llsl = strlen(lsuf);
-		/* Test if there is an existing -P prefix. */
-		if (dat->pre && *dat->pre) {
-		    char *dp = rembslash(dat->pre);
-
-		    pl = pfxlen(dp, lpre);
-		    llpl -= pl;
-		    lpre += pl;
-		}
-	    }
-	    /* Now duplicate the strings we have from the command line. */
-	    if (dat->ipre)
-		dat->ipre = (lipre ? dyncat(lipre, dat->ipre) :
-			     dupstring(dat->ipre));
-	    else if (lipre)
-		dat->ipre = lipre;
-	    if (dat->isuf)
-		dat->isuf = (lisuf ? dyncat(lisuf, dat->isuf) :
-			     dupstring(dat->isuf));
-	    else if (lisuf)
-		dat->isuf = lisuf;
-	    if (dat->ppre) {
-		if (!(dat->aflags & CAF_QUOTE)) {
-		    dat->ppre = quotename(dat->ppre, NULL);
-		    if ((dat->flags & CMF_FILE) &&
-			dat->ppre[0] == '\\' && dat->ppre[1] == '~')
-			chuck(dat->ppre);
-		} else
-		    dat->ppre = dupstring(dat->ppre);
-		lpl = strlen(dat->ppre);
-	    } else
-		lpl = 0;
-	    if (dat->psuf) {
-		if (!(dat->aflags & CAF_QUOTE))
-		    dat->psuf = quotename(dat->psuf, NULL);
-		else
-		    dat->psuf = dupstring(dat->psuf);
-		lsl = strlen(dat->psuf);
-	    } else
-		lsl = 0;
-	    if (dat->aflags & CAF_MATCH) {
-		int ml;
-
-		s = dat->ppre ? dat->ppre : "";
-		if ((ml = match_str(lpre, s, &bpl, 0, NULL, 0, 0, 1)) >= 0) {
-		    if (matchsubs) {
-			Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, 0);
-
-			tmp->prefix = matchsubs;
-			if (matchlastpart)
-			    matchlastpart->next = tmp;
-			else
-			    matchparts = tmp;
-		    }
-		    pline = matchparts;
-		    lpre += ml;
-		    bcp = ml;
-		    bpadd = strlen(s) - ml;
-		} else {
-		    if (llpl <= lpl && strpfx(lpre, s))
-			lpre = "";
-		    else if (llpl > lpl && strpfx(s, lpre))
-			lpre += lpl;
-		    else
-			*argv = NULL;
-		    bcp = lpl;
-		}
-
-		s = dat->psuf ? dat->psuf : "";
-		if ((ml = match_str(lsuf, s, &bsl, 0, NULL, 1, 0, 1)) >= 0) {
-		    if (matchsubs) {
-			Cline tmp = get_cline(NULL, 0, NULL, 0, NULL, 0, CLF_SUF);
-
-			tmp->suffix = matchsubs;
-			if (matchlastpart)
-			    matchlastpart->next = tmp;
-			else
-			    matchparts = tmp;
-		    }
-		    sline = revert_cline(matchparts);
-		    lsuf[llsl - ml] = '\0';
-		    bcs = ml;
-		    bsadd = strlen(s) - ml;
-		} else {
-		    if (llsl <= lsl && strsfx(lsuf, s))
-			lsuf = "";
-		    else if (llsl > lsl && strsfx(s, lsuf))
-			lsuf[llsl - lsl] = '\0';
-		    else
-			*argv = NULL;
-		    bcs = lsl;
-		}
-		if (comppatmatch && *comppatmatch) {
-		    int is = (*comppatmatch == '*');
-		    char *tmp = (char *) zhalloc(2 + llpl + llsl);
-
-		    strcpy(tmp, lpre);
-		    tmp[llpl] = 'x';
-		    strcpy(tmp + llpl + is, lsuf);
-
-		    tokenize(tmp);
-		    remnulargs(tmp);
-		    if (haswilds(tmp)) {
-			if (is)
-			    tmp[llpl] = Star;
-			if ((cp = patcompile(tmp, 0, NULL)))
-			    haspattern = 1;
-		    }
-		}
-	    }
-	    if (*argv) {
-		if (dat->pre)
-		    dat->pre = dupstring(dat->pre);
-		if (dat->suf)
-		    dat->suf = dupstring(dat->suf);
-		if (!dat->prpre && (dat->prpre = oppre)) {
-		    singsub(&(dat->prpre));
-		    untokenize(dat->prpre);
-		} else
-		    dat->prpre = dupstring(dat->prpre);
-		/* Select the group in which to store the matches. */
-		gflags = (((dat->aflags & CAF_NOSORT ) ? CGF_NOSORT  : 0) |
-			  ((dat->aflags & CAF_UNIQALL) ? CGF_UNIQALL : 0) |
-			  ((dat->aflags & CAF_UNIQCON) ? CGF_UNIQCON : 0));
-		if (dat->group) {
-		    endcmgroup(NULL);
-		    begcmgroup(dat->group, gflags);
-		} else {
-		    endcmgroup(NULL);
-		    begcmgroup("default", 0);
-		}
-		if (dat->ylist) {
-		    endcmgroup(NULL);
-		    begcmgroup(NULL, gflags);
-		}
-		/* Select the set of matches. */
-		oisalt = (dat->aflags & CAF_ALT);
-
-		if (dat->remf) {
-		    dat->remf = dupstring(dat->remf);
-		    dat->rems = NULL;
-		} else if (dat->rems)
-		    dat->rems = dupstring(dat->rems);
-	    }
-	    /* Walk through the matches given. */
-	    obpl = bpl;
-	    obsl = bsl;
-	    for (; (s = *argv); argv++) {
-		bpl = obpl;
-		bsl = obsl;
-		if (disp) {
-		    if (!*++disp)
-			disp = NULL;
-		}
-		sl = strlen(s);
-		isalt = oisalt;
-		if ((!dat->psuf || !*(dat->psuf)) && aign) {
-		    /* Do the suffix-test. If the match has one of the
-		     * suffixes from ign, we put it in the alternate set. */
-		    char **pt = aign;
-		    int filell;
-
-		    for (isalt = 0; !isalt && *pt; pt++)
-			if ((filell = strlen(*pt)) < sl
-			    && !strcmp(*pt, s + sl - filell))
-			    isalt = 1;
-
-		    if (isalt && !doadd) {
-			if (dparr && !*++dparr)
-			    dparr = NULL;
-			continue;
-		    }
-		}
-		if (!(dat->aflags & CAF_MATCH)) {
-		    if (dat->aflags & CAF_QUOTE)
-			ms = dupstring(s);
-		    else
-			sl = strlen(ms = quotename(s, NULL));
-		    lc = bld_parts(ms, sl, -1, NULL);
-		    isexact = 0;
-		} else if (!(ms = comp_match(lpre, lsuf, s, cp, &lc,
-					     (!(dat->aflags & CAF_QUOTE) ?
-					      ((dat->ppre && dat->ppre) ||
-					       !(dat->flags & CMF_FILE) ? 1 : 2) : 0),
-					     &bpl, bcp, &bsl, bcs,
-					     &isexact))) {
-		    if (dparr && !*++dparr)
-			dparr = NULL;
-		    continue;
-		}
-		if (doadd) {
-		    Brinfo bp;
-
-		    for (bp = obpl; bp; bp = bp->next)
-			bp->curpos += bpadd;
-		    for (bp = obsl; bp; bp = bp->next)
-			bp->curpos += bsadd;
-
-		    if ((cm = add_match_data(isalt, ms, lc, dat->ipre, NULL,
-					     dat->isuf, dat->pre, dat->prpre,
-					     dat->ppre, pline,
-					     dat->psuf, sline,
-					     dat->suf, dat->flags, isexact))) {
-			cm->rems = dat->rems;
-			cm->remf = dat->remf;
-			if (disp)
-			    cm->disp = dupstring(*disp);
-		    }
-		} else {
-		    if (dat->apar)
-			addlinknode(aparl, ms);
-		    if (dat->opar)
-			addlinknode(oparl, s);
-		    if (dat->dpar && dparr) {
-			addlinknode(dparl, *dparr);
-			if (!*++dparr)
-			    dparr = NULL;
-		    }
-		    free_cline(lc);
-		}
-	    }
-	    if (dat->apar)
-		set_list_array(dat->apar, aparl);
-	    if (dat->opar)
-		set_list_array(dat->opar, oparl);
-	    if (dat->dpar)
-		set_list_array(dat->dpar, dparl);
-	    if (dat->ylist) {
-		if (dat->group) {
-		    endcmgroup(get_user_var(dat->ylist));
-		    begcmgroup(dat->group, gflags);
-		    if (dat->exp)
-			addexpl();
-		} else {
-		    if (dat->exp)
-			addexpl();
-		    endcmgroup(get_user_var(dat->ylist));
-		    begcmgroup("default", 0);
-		}
-	    } else if (dat->exp)
-		addexpl();
-	} LASTALLOC;
-    } SWITCHBACKHEAPS;
-
-    /* We switched back to the current heap, now restore the stack of
-     * matchers. */
-    mstack = oms;
-
-    instring = ois;
-    inbackt = oib;
-    autoq = oaq;
-    zsfree(qipre);
-    zsfree(qisuf);
-    qipre = oqp;
-    qisuf = oqs;
-
-    if (mnum == nm)
-	haspattern = ohp;
-
-    return (mnum == nm);
-}
-
 /**/
 static int
 docompletion(char *s, int lst, int incmd)
 {
-    int ret = 0;
-
-    HEAPALLOC {
-	char *opm;
-	LinkNode n;
-
-	pushheap();
-
-	ainfo = fainfo = NULL;
-	matchers = newlinklist();
-
-	hasunqu = 0;
-	useline = (lst != COMP_LIST_COMPLETE);
-	useexact = isset(RECEXACT);
-	uselist = (useline ?
-		   ((isset(AUTOLIST) && !isset(BASHAUTOLIST)) ? 
-		    (isset(LISTAMBIGUOUS) ? 3 : 2) : 0) : 1);
-	zsfree(comppatmatch);
-	opm = comppatmatch = ztrdup(useglob ? "*" : "");
-	zsfree(comppatinsert);
-	comppatinsert = ztrdup("menu");
-	zsfree(compforcelist);
-	compforcelist = ztrdup("");
-	haspattern = 0;
-	complistmax = getiparam("LISTMAX");
-	zsfree(complastprompt);
-	complastprompt = ztrdup(((isset(ALWAYSLASTPROMPT) && zmult == 1) ||
-				(unset(ALWAYSLASTPROMPT) && zmult != 1)) ?
-				"yes" : "");
-	movetoend = ((cs == we || isset(ALWAYSTOEND)) ? 2 : 1);
-	showinglist = 0;
-	hasmatched = 0;
-	minmlen = 1000000;
-	maxmlen = -1;
-
-	/* Make sure we have the completion list and compctl. */
-	if (makecomplist(s, incmd, lst)) {
-	    /* Error condition: feeeeeeeeeeeeep(). */
-	    cs = 0;
-	    foredel(ll);
-	    inststr(origline);
-	    cs = origcs;
-	    clearlist = 1;
-	    ret = 1;
-	    minfo.cur = NULL;
-	    goto compend;
-	}
-	zsfree(lastprebr);
-	zsfree(lastpostbr);
-	lastprebr = lastpostbr = NULL;
-
-	if (comppatmatch && *comppatmatch && comppatmatch != opm)
-	    haspattern = 1;
-	if (!useline && uselist) {
-	    /* All this and the guy only wants to see the list, sigh. */
-	    cs = 0;
-	    foredel(ll);
-	    inststr(origline);
-	    cs = origcs;
-	    showinglist = -2;
-	} else if (useline == 2 && nmatches > 1) {
-	    int first = 1, nm = nmatches;
-	    Cmatch *mc;
-
-	    menucmp = 1;
-	    menuacc = 0;
-
-	    for (minfo.group = amatches;
-		 minfo.group && !(minfo.group)->mcount;
-		 minfo.group = (minfo.group)->next);
-
-	    mc = (minfo.group)->matches;
-
-	    while (1) {
-		if (!first)
-		    acceptlast();
-		first = 0;
-
-		if (!--nm)
-		    menucmp = 0;
-
-		do_single(*mc);
-		minfo.cur = mc;
-
-		if (!*++(minfo.cur)) {
-		    do {
-			if (!(minfo.group = (minfo.group)->next))
-			    break;
-		    } while (!(minfo.group)->mcount);
-		    if (!minfo.group)
-			break;
-		    minfo.cur = minfo.group->matches;
-		}
-		mc = minfo.cur;
-	    }
-	    menucmp = 0;
-	    minfo.cur = NULL;
-
-	    if (compforcelist && *compforcelist && uselist)
-		showinglist = -2;
-	    else
-		invalidatelist();
-	} else if (useline) {
-	    /* We have matches. */
-	    if (nmatches > 1) {
-		/* There is more than one match. */
-		ret = do_ambiguous();
-	    } else if (nmatches == 1) {
-		/* Only one match. */
-		Cmgroup m = amatches;
-
-		while (!m->mcount)
-		    m = m->next;
-		minfo.cur = NULL;
-		minfo.asked = 0;
-		do_single(m->matches[0]);
-		if (compforcelist && *compforcelist) {
-		    if (uselist)
-			showinglist = -2;
-		    else
-			clearlist = 1;
-		} else
-		    invalidatelist();
-	    }
-	} else {
-	    invalidatelist();
-	    if (compforcelist && *compforcelist)
-		clearlist = 1;
-	    cs = 0;
-	    foredel(ll);
-	    inststr(origline);
-	    cs = origcs;
-	}
-	/* Print the explanation strings if needed. */
-	if (!showinglist && validlist && usemenu != 2 && nmatches != 1 &&
-	    useline != 2 && (!oldlist || !listshown)) {
-	    onlyexpl = 1;
-	    showinglist = -2;
-	}
-      compend:
-	for (n = firstnode(matchers); n; incnode(n))
-	    freecmatcher((Cmatcher) getdata(n));
-
-	ll = strlen((char *)line);
-	if (cs > ll)
-	    cs = ll;
-	popheap();
-    } LASTALLOC;
-    return ret;
-}
-
-/* This calls the given function for new style completion. */
-
-/**/
-static void
-callcompfunc(char *s, char *fn)
-{
-    List list;
-    int lv = lastval;
-    char buf[20];
-
-    if ((list = getshfunc(fn)) != &dummy_list) {
-	char **p, *tmp;
-	int aadd = 0, usea = 1, icf = incompfunc, osc = sfcontext;
-	unsigned int rset, kset;
-	Param *ocrpms = comprpms, *ockpms = compkpms;
-
-	comprpms = (Param *) zalloc(CP_REALPARAMS * sizeof(Param));
-	compkpms = (Param *) zalloc(CP_KEYPARAMS * sizeof(Param));
-
-	rset = CP_ALLREALS;
-	kset = CP_ALLKEYS &
-	    ~(CP_PARAMETER | CP_REDIRECT | CP_QUOTE | CP_QUOTING |
-	      CP_EXACTSTR | CP_FORCELIST | CP_OLDLIST | CP_OLDINS |
-	      (useglob ? 0 : CP_PATMATCH));
-	zsfree(compvared);
-	if (varedarg) {
-	    compvared = ztrdup(varedarg);
-	    kset |= CP_VARED;
-	} else
-	    compvared = ztrdup("");
-	if (!*complastprompt)
-	    kset &= ~CP_LASTPROMPT;
-	zsfree(compcontext);
-	zsfree(compparameter);
-	zsfree(compredirect);
-	compparameter = compredirect = "";
-	if (ispar)
-	    compcontext = (ispar == 2 ? "brace_parameter" : "parameter");
-	else if (linwhat == IN_MATH) {
-	    if (insubscr) {
-		compcontext = "subscript";
-		if (varname) {
-		    compparameter = varname;
-		    kset |= CP_PARAMETER;
-		}
-	    } else
-		compcontext = "math";
-	    usea = 0;
-	} else if (lincmd) {
-	    if (insubscr) {
-		compcontext = "subscript";
-		kset |= CP_PARAMETER;
-	    } else
-		compcontext = "command";
-	} else if (linredir) {
-	    compcontext = "redirect";
-	    if (rdstr)
-		compredirect = rdstr;
-	    kset |= CP_REDIRECT;
-	} else
-	    switch (linwhat) {
-	    case IN_ENV:
-		compcontext = (linarr ? "array_value" : "value");
-		compparameter = varname;
-		kset |= CP_PARAMETER;
-		if (!clwpos) {
-		    clwpos = 1;
-		    clwnum = 2;
-		    zsfree(clwords[1]);
-		    clwords[1] = ztrdup(s);
-		    zsfree(clwords[2]);
-		    clwords[2] = NULL;
-		}
-		aadd = 1;
-		break;
-	    case IN_COND:
-		compcontext = "condition";
-		break;
-	    default:
-		if (cmdstr)
-		    compcontext = "command";
-		else {
-		    compcontext = "value";
-		    kset |= CP_PARAMETER;
-		    if (clwords[0])
-			compparameter = clwords[0];
-		    aadd = 1;
-		}
-	    }
-	compcontext = ztrdup(compcontext);
-	if (compwords)
-	    freearray(compwords);
-	if (usea && (!aadd || clwords[0])) {
-	    char **q;
-
-	    PERMALLOC {
-		q = compwords = (char **)
-		    zalloc((clwnum + 1) * sizeof(char *));
-		for (p = clwords + aadd; *p; p++, q++) {
-		    tmp = dupstring(*p);
-		    untokenize(tmp);
-		    *q = ztrdup(tmp);
-		}
-		*q = NULL;
-	    } LASTALLOC;
-	} else
-	    compwords = (char **) zcalloc(sizeof(char *));
-
-	compparameter = ztrdup(compparameter);
-	compredirect = ztrdup(compredirect);
-	zsfree(compquote);
-	zsfree(compquoting);
-	if (instring) {
-	    if (instring == 1) {
-		compquote = ztrdup("\'");
-		compquoting = ztrdup("single");
-	    } else {
-		compquote = ztrdup("\"");
-		compquoting = ztrdup("double");
-	    }
-	    kset |= CP_QUOTE | CP_QUOTING;
-	} else if (inbackt) {
-	    compquote = ztrdup("`");
-	    compquoting = ztrdup("backtick");
-	    kset |= CP_QUOTE | CP_QUOTING;
-	} else {
-	    compquote = ztrdup("");
-	    compquoting = ztrdup("");
-	}
-	zsfree(compprefix);
-	zsfree(compsuffix);
-	if (unset(COMPLETEINWORD)) {
-	    tmp = quotename(s, NULL);
-	    untokenize(tmp);
-	    compprefix = ztrdup(tmp);
-	    compsuffix = ztrdup("");
-	} else {
-	    char *ss, sav;
-	    
-	    ss = s + offs;
-
-	    sav = *ss;
-	    *ss = '\0';
-	    tmp = quotename(s, NULL);
-	    untokenize(tmp);
-	    compprefix = ztrdup(tmp);
-	    *ss = sav;
-	    ss = quotename(ss, NULL);
-	    untokenize(ss);
-	    compsuffix = ztrdup(ss);
-	}
-	zsfree(compiprefix);
-	compiprefix = ztrdup("");
-	zsfree(compisuffix);
-	compisuffix = ztrdup("");
-	zsfree(compqiprefix);
-	compqiprefix = ztrdup(qipre ? qipre : "");
-	zsfree(compqisuffix);
-	compqisuffix = ztrdup(qisuf ? qisuf : "");
-	compcurrent = (usea ? (clwpos + 1 - aadd) : 0);
-
-	zsfree(complist);
-	switch (uselist) {
-	case 0: complist = ""; kset &= ~CP_LIST; break;
-	case 1: complist = "list"; break;
-	case 2: complist = "autolist"; break;
-	case 3: complist = "ambiguous"; break;
-	}
-	complist = ztrdup(complist);
-	zsfree(compinsert);
-	if (useline) {
-	    switch (usemenu) {
-	    case 0: compinsert = "unambiguous"; break;
-	    case 1: compinsert = "menu"; break;
-	    case 2: compinsert = "automenu"; break;
-	    }
-	} else {
-	    compinsert = "";
-	    kset &= ~CP_INSERT;
-	}
-	compinsert = ztrdup(compinsert);
-	if (useexact)
-	    compexact = ztrdup("accept");
-	else {
-	    compexact = ztrdup("");
-	    kset &= ~CP_EXACT;
-	}
-	zsfree(comptoend);
-	if (movetoend == 1)
-	    comptoend = ztrdup("single");
-	else
-	    comptoend = ztrdup("match");
-	zsfree(compoldlist);
-	zsfree(compoldins);
-	if (hasoldlist && lastpermmnum) {
-	    if (listshown)
-		compoldlist = "shown";
-	    else
-		compoldlist = "yes";
-	    kset |= CP_OLDLIST;
-	    if (minfo.cur) {
-		sprintf(buf, "%d", (*(minfo.cur))->gnum);
-		compoldins = buf;
-		kset |= CP_OLDINS;
-	    } else
-		compoldins = "";
-	} else
-	    compoldlist = compoldins = "";
-	compoldlist = ztrdup(compoldlist);
-	compoldins = ztrdup(compoldins);
-
-	incompfunc = 1;
-	startparamscope();
-	makecompparamsptr();
-	comp_setunsetptr(rset, (~rset & CP_ALLREALS),
-			 kset, (~kset & CP_ALLKEYS));
-	makezleparams(1);
-	sfcontext = SFC_CWIDGET;
-	NEWHEAPS(compheap) {
-	    LinkList largs = NULL;
-	    int olv = lastval;
-
-	    if (*cfargs) {
-		char **p = cfargs;
-
-		largs = newlinklist();
-		addlinknode(largs, dupstring(fn));
-		while (*p)
-		    addlinknode(largs, dupstring(*p++));
-	    }
-	    doshfunc(fn, list, largs, 0, 0);
-	    cfret = lastval;
-	    lastval = olv;
-	} OLDHEAPS;
-	sfcontext = osc;
-	endparamscope();
-	lastcmd = 0;
-	incompfunc = icf;
-
-	if (!complist)
-	    uselist = 0;
-	else if (!strncmp(complist, "list", 4))
-	    uselist = 1;
-	else if (!strncmp(complist, "auto", 4))
-	    uselist = 2;
-	else if (!strncmp(complist, "ambig", 5))
-	    uselist = 3;
-	else
-	    uselist = 0;
-
-	onlyexpl = (complist && strstr(complist, "expl"));
-
-	if (!compinsert)
-	    useline = 0;
-	else if (!strcmp(compinsert, "unambig") ||
-		 !strcmp(compinsert, "unambiguous"))
-	    useline = 1, usemenu = 0;
-	else if (!strcmp(compinsert, "menu"))
-	    useline = 1, usemenu = 1;
-	else if (!strcmp(compinsert, "auto") ||
-		 !strcmp(compinsert, "automenu"))
-	    useline = 1, usemenu = 2;
-	else if (!strcmp(compinsert, "all"))
-	    useline = 2, usemenu = 0;
-	else if (idigit(*compinsert)) {
-	    char *m;
-
-	    useline = 1; usemenu = 3;
-	    insmnum = atoi(compinsert);
-	    if ((m = strchr(compinsert, ':'))) {
-		insgroup = 1;
-		insgnum = atoi(m + 1);
-	    }
-	    insspace = (compinsert[strlen(compinsert) - 1] == ' ');
-	} else
-	    useline = usemenu = 0;
-	useexact = (compexact && !strcmp(compexact, "accept"));
-
-	if (!comptoend || !*comptoend)
-	    movetoend = 0;
-	else if (!strcmp(comptoend, "single"))
-	    movetoend = 1;
-	else if (!strcmp(comptoend, "always"))
-	    movetoend = 3;
-	else
-	    movetoend = 2;
-
-	oldlist = (hasoldlist && compoldlist && !strcmp(compoldlist, "keep"));
-	oldins = (hasoldlist && minfo.cur &&
-		  compoldins && !strcmp(compoldins, "keep"));
-
-	zfree(comprpms, CP_REALPARAMS * sizeof(Param));
-	zfree(compkpms, CP_KEYPARAMS * sizeof(Param));
-	comprpms = ocrpms;
-	compkpms = ockpms;
-    }
-    lastval = lv;
-}
-
-/* Create the completion list.  This is called whenever some bit of   *
- * completion code needs the list.                                    *
- * Along with the list is maintained the prefixes/suffixes etc.  When *
- * any of this becomes invalid -- e.g. if some text is changed on the *
- * command line -- invalidatelist() should be called, to set          *
- * validlist to zero and free up the memory used.  This function      *
- * returns non-zero on error.                                         */
-
-/**/
-static int
-makecomplist(char *s, int incmd, int lst)
-{
-    struct cmlist ms;
-    Cmlist m;
-    char *p, *os = s;
-    int onm = nmatches, osi = movefd(0);
-
-    /* Inside $... ? */
-    if (compfunc && (p = check_param(s, 0, 0)))
-	os = s = p;
-
-    /* We build a copy of the list of matchers to use to make sure that this
-     * works even if a shell function called from the completion code changes
-     * the global matchers. */
-
-    if ((m = cmatcher)) {
-	Cmlist mm, *mp = &mm;
-	int n;
-
-	for (n = 0; m; m = m->next, n++) {
-	    if (m->matcher) {
-		*mp = (Cmlist) zhalloc(sizeof(struct cmlist));
-		(*mp)->matcher = m->matcher;
-		(*mp)->next = NULL;
-		(*mp)->str = dupstring(m->str);
-		mp = &((*mp)->next);
-		addlinknode(matchers, m->matcher);
-		m->matcher->refc++;
-	    }
-	}
-	m = mm;
-	compmatcher = 1;
-	compmatchertot = n;
-    } else
-	compmatcher = 0;
-
-    linwhat = inwhat;
-
-    /* Walk through the global matchers. */
-    for (;;) {
-	bmatchers = NULL;
-	zsfree(compmatcherstr);
-	if (m) {
-	    ms.next = NULL;
-	    ms.matcher = m->matcher;
-	    mstack = &ms;
-
-	    /* Store the matchers used in the bmatchers list which is used
-	     * when building new parts for the string to insert into the 
-	     * line. */
-	    add_bmatchers(m->matcher);
-	    compmatcherstr = ztrdup(m->str);
-	} else {
-	    mstack = NULL;
-	    compmatcherstr = ztrdup("");
-	}
-	ainfo = (Aminfo) hcalloc(sizeof(struct aminfo));
-	fainfo = (Aminfo) hcalloc(sizeof(struct aminfo));
-
-	freecl = NULL;
-
-	if (!validlist)
-	    lastambig = 0;
-	amatches = NULL;
-	mnum = 0;
-	unambig_mnum = -1;
-	isuf = NULL;
-	insmnum = insgnum = 1;
-	insgroup = oldlist = oldins = 0;
-	begcmgroup("default", 0);
-	menucmp = menuacc = newmatches = onlyexpl = 0;
-
-	runhookdef(COMPCTLBEFOREHOOK, NULL);
-
-	s = dupstring(os);
-	if (compfunc)
-	    callcompfunc(s, compfunc);
-	else {
-	    struct ccmakedat dat;
-
-	    dat.str = s;
-	    dat.incmd = incmd;
-	    dat.lst = lst;
-	    runhookdef(COMPCTLMAKEHOOK, (void *) &dat);
-	}
-	endcmgroup(NULL);
-
-	runhookdef(COMPCTLAFTERHOOK,
-		   (void *) ((amatches && !oldlist) ? 1L : 0L));
-
-	if (oldlist) {
-	    nmatches = onm;
-	    validlist = 1;
-	    amatches = lastmatches;
-	    lmatches = lastlmatches;
-	    if (pmatches) {
-		freematches(pmatches);
-		pmatches = NULL;
-		hasperm = 0;
-	    }
-	    redup(osi, 0);
-
-	    return 0;
-	}
-	PERMALLOC {
-	    if (lastmatches) {
-		freematches(lastmatches);
-		lastmatches = NULL;
-	    }
-	    permmatches(1);
-	    amatches = pmatches;
-	    lastpermmnum = permmnum;
-	    lastpermgnum = permgnum;
-	} LASTALLOC;
-
-	lastmatches = pmatches;
-	lastlmatches = lmatches;
-	pmatches = NULL;
-	hasperm = 0;
-	hasoldlist = 1;
-
-	if (nmatches && !errflag) {
-	    validlist = 1;
-
-	    redup(osi, 0);
-
-	    return 0;
-	}
-	if (!m || !(m = m->next))
-	    break;
+    struct compldat dat;
 
-	errflag = 0;
-	compmatcher++;
-    }
-    redup(osi, 0);
-    return 1;
-}
-
-/* This should probably be moved into tokenize(). */
+    dat.s = s;
+    dat.lst = lst;
+    dat.incmd = incmd;
 
-/**/
-static char *
-ctokenize(char *p)
-{
-    char *r = p;
-    int bslash = 0;
-
-    tokenize(p);
-
-    for (p = r; *p; p++) {
-	if (*p == '\\')
-	    bslash = 1;
-	else {
-	    if (*p == '$' || *p == '{' || *p == '}') {
-		if (bslash)
-		    p[-1] = Bnull;
-		else
-		    *p = (*p == '$' ? String :
-			  (*p == '{' ? Inbrace : Outbrace));
-	    }
-	    bslash = 0;
-	}
-    }
-    return r;
+    return runhookdef(COMPLETEHOOK, (void *) &dat);
 }
 
-/**/
-char *
-comp_str(int *ipl, int *pl, int untok)
-{
-    char *p = dupstring(compprefix);
-    char *s = dupstring(compsuffix);
-    char *ip = dupstring(compiprefix);
-    char *str;
-    int lp, ls, lip;
-
-    if (!untok) {
-	ctokenize(p);
-	remnulargs(p);
-	ctokenize(s);
-	remnulargs(s);
-    }
-    lp = strlen(p);
-    ls = strlen(s);
-    lip = strlen(ip);
-    str = zhalloc(lip + lp + ls + 1);
-    strcpy(str, ip);
-    strcat(str, p);
-    strcat(str, s);
-
-    if (ipl)
-	*ipl = lip;
-    if (pl)
-	*pl = lp;
-
-    return str;
-}
+/* Return the length of the common prefix of s and t. */
 
 /**/
 int
-set_comp_sep(void)
+pfxlen(char *s, char *t)
 {
-    int lip, lp;
-    char *s = comp_str(&lip, &lp, 0);
-    LinkList foo = newlinklist();
-    LinkNode n;
-    int owe = we, owb = wb, ocs = cs, swb, swe, scs, soffs, ne = noerrs;
-    int tl, got = 0, i = 0, cur = -1, oll = ll, sl;
-    int ois = instring, oib = inbackt, noffs = lip + lp;
-    char *tmp, *p, *ns, *ol = (char *) line, sav, oaq = autoq, *qp, *qs;
-
-    if (compisuffix)
-	s = dyncat(s, compisuffix);
-    untokenize(s);
-
-    swb = swe = soffs = 0;
-    ns = NULL;
-
-    /* Put the string in the lexer buffer and call the lexer to *
-     * get the words we have to expand.                        */
-    zleparse = 1;
-    addedx = 1;
-    noerrs = 1;
-    lexsave();
-    tmp = (char *) zhalloc(tl = 3 + strlen(s));
-    tmp[0] = ' ';
-    memcpy(tmp + 1, s, noffs);
-    tmp[(scs = cs = 1 + noffs)] = 'x';
-    strcpy(tmp + 2 + noffs, s + noffs);
-    tmp = rembslash(tmp);
-    inpush(dupstrspace(tmp), 0, NULL);
-    line = (unsigned char *) tmp;
-    ll = tl - 1;
-    strinbeg(0);
-    noaliases = 1;
-    do {
-	ctxtlex();
-	if (tok == LEXERR) {
-	    int j;
-
-	    if (!tokstr)
-		break;
-	    for (j = 0, p = tokstr; *p; p++)
-		if (*p == Snull || *p == Dnull)
-		    j++;
-	    if (j & 1) {
-		tok = STRING;
-		if (p > tokstr && p[-1] == ' ')
-		    p[-1] = '\0';
-	    }
-	}
-	if (tok == ENDINPUT || tok == LEXERR)
-	    break;
-	if (tokstr && *tokstr)
-	    addlinknode(foo, (p = ztrdup(tokstr)));
-	else
-	    p = NULL;
-	if (!got && !zleparse) {
-	    DPUTS(!p, "no current word in substr");
-	    got = 1;
-	    cur = i;
-	    swb = wb - 1;
-	    swe = we - 1;
-	    soffs = cs - swb;
-	    chuck(p + soffs);
-	    ns = dupstring(p);
-	}
-	i++;
-    } while (tok != ENDINPUT && tok != LEXERR);
-    noaliases = 0;
-    strinend();
-    inpop();
-    errflag = zleparse = 0;
-    noerrs = ne;
-    lexrestore();
-    wb = owb;
-    we = owe;
-    cs = ocs;
-    line = (unsigned char *) ol;
-    ll = oll;
-    if (cur < 0 || i < 1)
-	return 1;
-    owb = offs;
-    offs = soffs;
-    if ((p = check_param(ns, 0, 1))) {
-	for (p = ns; *p; p++)
-	    if (*p == Dnull)
-		*p = '"';
-	    else if (*p == Snull)
-		*p = '\'';
-    }
-    offs = owb;
-    if (*ns == Snull || *ns == Dnull) {
-	instring = (*ns == Snull ? 1 : 2);
-	inbackt = 0;
-	swb++;
-	if (ns[strlen(ns) - 1] == *ns && ns[1])
-	    swe--;
-	autoq = (*ns == Snull ? '\'' : '"');
-    } else {
-	instring = 0;
-	autoq = '\0';
-    }
-    for (p = ns, i = swb; *p; p++, i++) {
-	if (INULL(*p)) {
-	    if (i < scs)
-		soffs--;
-	    if (p[1] || *p != Bnull) {
-		if (*p == Bnull) {
-		    if (scs == i + 1)
-			scs++, soffs++;
-		} else {
-		    if (scs > i--)
-			scs--;
-		}
-	    } else {
-		if (scs == swe)
-		    scs--;
-	    }
-	    chuck(p--);
-	}
-    }
-    sav = s[(i = swb - 1)];
-    s[i] = '\0';
-    qp = tricat(qipre, rembslash(s), "");
-    s[i] = sav;
-    if (swe < swb)
-	swe = swb;
-    swe--;
-    sl = strlen(s);
-    if (swe > sl)
-	swe = sl, ns[swe - swb + 1] = '\0';
-    qs = tricat(rembslash(s + swe), qisuf, "");
-    sl = strlen(ns);
-    if (soffs > sl)
-	soffs = sl;
-
-    {
-	int set = CP_QUOTE | CP_QUOTING, unset = 0;
-
-	zsfree(compquote);
-	zsfree(compquoting);
-	if (instring == 2) {
-	    compquote = "\"";
-	    compquoting = "double";
-	} else if (instring == 1) {
-	    compquote = "'";
-	    compquoting = "single";
-	} else {
-	    compquote = compquoting = "";
-	    unset = set;
-	    set = 0;
-	}
-	compquote = ztrdup(compquote);
-	compquoting = ztrdup(compquoting);
-	comp_setunsetptr(0, 0, set, unset);
-
-	if (unset(COMPLETEINWORD)) {
-	    untokenize(ns);
-	    zsfree(compprefix);
-	    compprefix = ztrdup(ns);
-	    zsfree(compsuffix);
-	    compsuffix = ztrdup("");
-	} else {
-	    char *ss, sav;
-	    
-	    ss = ns + soffs;
-
-	    sav = *ss;
-	    *ss = '\0';
-	    untokenize(ns);
-	    compprefix = ztrdup(ns);
-	    *ss = sav;
-	    untokenize(ss);
-	    compsuffix = ztrdup(ss);
-	}
-	zsfree(compiprefix);
-	compiprefix = ztrdup("");
-	zsfree(compisuffix);
-	compisuffix = ztrdup("");
-	zsfree(compqiprefix);
-	zsfree(compqisuffix);
-	if (ois) {
-	    compqiprefix = qp;
-	    compqisuffix = qs;
-	} else {
-	    compqiprefix = ztrdup(quotename(qp, NULL));
-	    zsfree(qp);
-	    compqisuffix = ztrdup(quotename(qs, NULL));
-	    zsfree(qs);
-	}
-	freearray(compwords);
-	i = countlinknodes(foo);
-	compwords = (char **) zalloc((i + 1) * sizeof(char *));
-	for (n = firstnode(foo), i = 0; n; incnode(n), i++) {
-	    p = compwords[i] = (char *) getdata(n);
-	    untokenize(p);
-	}
-	compcurrent = cur + 1;
-	compwords[i] = NULL;
-    }
-    autoq = oaq;
-    instring = ois;
-    inbackt = oib;
-
-    return 0;
-}
-
-/* Invalidate the completion list. */
+    int i = 0;
 
-/**/
-void
-invalidatelist(void)
-{
-    if (showinglist == -2)
-	listmatches();
-    if (validlist) {
-	freematches(lastmatches);
-	lastmatches = NULL;
-	hasoldlist = 0;
-    }
-    lastambig = menucmp = menuacc = validlist = showinglist = fromcomp = 0;
-    listdat.valid = 0;
-    if (listshown < 0)
-	listshown = 0;
-    minfo.cur = NULL;
-    minfo.asked = 0;
-    zsfree(minfo.prebr);
-    zsfree(minfo.postbr);
-    minfo.postbr = minfo.prebr = NULL;
-    compwidget = NULL;
+    while (*s && *s == *t)
+	s++, t++, i++;
+    return i;
 }
 
-/* Get the words from a variable or a compctl -k list. */
+/* Return the length of the common suffix of s and t. */
 
-/**/
-char **
-get_user_var(char *nam)
+#if 0
+static int
+sfxlen(char *s, char *t)
 {
-    if (!nam)
-	return NULL;
-    else if (*nam == '(') {
-	/* It's a (...) list, not a parameter name. */
-	char *ptr, *s, **uarr, **aptr;
-	int count = 0, notempty = 0, brk = 0;
-	LinkList arrlist = newlinklist();
-
-	ptr = dupstring(nam);
-	s = ptr + 1;
-	while (*++ptr) {
-	    if (*ptr == '\\' && ptr[1])
-		chuck(ptr), notempty = 1;
-	    else if (*ptr == ',' || inblank(*ptr) || *ptr == ')') {
-		if (*ptr == ')')
-		    brk++;
-		if (notempty) {
-		    *ptr = '\0';
-		    count++;
-		    if (*s == '\n')
-			s++;
-		    addlinknode(arrlist, s);
-		}
-		s = ptr + 1;
-		notempty = 0;
-	    } else {
-		notempty = 1;
-		if (*ptr == Meta)
-		    ptr++;
-	    }
-	    if (brk)
-		break;
-	}
-	if (!brk || !count)
-	    return NULL;
-	*ptr = '\0';
-	aptr = uarr = (char **) zhalloc(sizeof(char *) * (count + 1));
-
-	while ((*aptr++ = (char *)ugetnode(arrlist)));
-	uarr[count] = NULL;
-	return uarr;
-    } else {
-	/* Otherwise it should be a parameter name. */
-	char **arr = NULL, *val;
+    if (*s && *t) {
+	int i = 0;
+	char *s2 = s + strlen(s) - 1, *t2 = t + strlen(t) - 1;
 
-	if ((arr = getaparam(nam)) || (arr = gethparam(nam)))
-	    return (incompfunc ? arrdup(arr) : arr);
+	while (s2 >= s && t2 >= t && *s2 == *t2)
+	    s2--, t2--, i++;
 
-	if ((val = getsparam(nam))) {
-	    arr = (char **) zhalloc(2*sizeof(char *));
-	    arr[0] = (incompfunc ? dupstring(val) : val);
-	    arr[1] = NULL;
-	}
-	return arr;
-    }
+	return i;
+    } else
+	return 0;
 }
+#endif
 
 /* This is strcmp with ignoring backslashes. */
 
 /**/
-static int
+int
 strbpcmp(char **aa, char **bb)
 {
     char *a = *aa, *b = *bb;
@@ -5560,1966 +1738,123 @@ strbpcmp(char **aa, char **bb)
     return (int)(*a - *b);
 }
 
-/* The comparison function for matches (used for sorting). */
-
-static int
-matchcmp(Cmatch *a, Cmatch *b)
-{
-    if ((*a)->disp) {
-	if ((*b)->disp) {
-	    if ((*a)->flags & CMF_DISPLINE) {
-		if ((*b)->flags & CMF_DISPLINE)
-		    return strcmp((*a)->disp, (*b)->disp);
-		else
-		    return -1;
-	    } else {
-		if ((*b)->flags & CMF_DISPLINE)
-		    return 1;
-		else
-		    return strcmp((*a)->disp, (*b)->disp);
-	    }
-	}
-	return -1;
-    }
-    if ((*b)->disp)
-	return 1;
-
-    return strbpcmp(&((*a)->str), &((*b)->str));
-}
-
-/* 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->psuf, b->psuf) &&
-	matchstreq(a->suf, b->suf) &&
-	((!a->disp && !b->disp && matchstreq(a->str, b->str)) ||
-	 (a->disp && b->disp && !strcmp(a->disp, b->disp) &&
-	  matchstreq(a->str, b->str)));
-}
-
-/* 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 Cmatch *
-makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp)
-{
-    Cmatch *ap, *bp, *cp, *rp;
-    LinkNode nod;
-    int n, nl = 0, ll = 0;
-
-    /* Build an array for the matches. */
-    rp = ap = (Cmatch *) ncalloc(((n = countlinknodes(l)) + 1) *
-				 sizeof(Cmatch));
-
-    /* And copy them into it. */
-    for (nod = firstnode(l); nod; incnode(nod))
-	*ap++ = (Cmatch) getdata(nod);
-    *ap = NULL;
-
-    if (!type) {
-	if (flags) {
-	    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 (!(flags & CGF_NOSORT)) {
-	    /* Now sort the array (it contains matches). */
-	    qsort((void *) rp, n, sizeof(Cmatch),
-		  (int (*) _((const void *, const void *)))matchcmp);
-
-	    if (!(flags & CGF_UNIQCON)) {
-		/* 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] && !(*ap)->disp && !(bp[1])->disp &&
-			     !strcmp((*ap)->str, (bp[1])->str); bp++)
-			(bp[1])->flags |= CMF_NOLIST;
-		}
-		*cp = NULL;
-	    }
-	    for (ap = rp; *ap; ap++) {
-		if ((*ap)->disp && ((*ap)->flags & CMF_DISPLINE))
-		    ll++;
-		if ((*ap)->flags & CMF_NOLIST)
-		    nl++;
-	    }
-	} else {
-	    if (!(flags & CGF_UNIQALL) && !(flags & CGF_UNIQCON)) {
-		for (ap = rp; *ap; ap++) {
-		    for (bp = cp = ap + 1; *bp; bp++) {
-			if (!matcheq(*ap, *bp))
-			    *cp++ = *bp;
-			else
-			    n--;
-		    }
-		    *cp = NULL;
-		}
-	    } else if (!(flags & CGF_UNIQCON)) {
-		for (ap = cp = rp; *ap; ap++) {
-		    *cp++ = *ap;
-		    for (bp = ap; bp[1] && matcheq(*ap, bp[1]); bp++, n--);
-		    ap = bp;
-		    for (; bp[1] && !(*ap)->disp && !(bp[1])->disp &&
-			     !strcmp((*ap)->str, (bp[1])->str); bp++)
-			(bp[1])->flags |= CMF_NOLIST;
-		}
-		*cp = NULL;
-	    }
-	    for (ap = rp; *ap; ap++) {
-		if ((*ap)->disp && ((*ap)->flags & CMF_DISPLINE))
-		    ll++;
-		if ((*ap)->flags & CMF_NOLIST)
-		    nl++;
-	    }
-	}
-    }
-    if (np)
-	*np = n;
-    if (nlp)
-	*nlp = nl;
-    if (llp)
-	*llp = ll;
-    return rp;
-}
-
-/* This begins a new group of matches. */
-
-/**/
-static void
-begcmgroup(char *n, int flags)
-{
-    if (n) {
-	Cmgroup p = amatches;
-
-	while (p) {
-	    if (p->name &&
-		flags == (p->flags & (CGF_NOSORT|CGF_UNIQALL|CGF_UNIQCON)) &&
-		!strcmp(n, p->name)) {
-		mgroup = p;
-
-		expls = p->lexpls;
-		matches = p->lmatches;
-		fmatches = p->lfmatches;
-		allccs = p->lallccs;
-
-		return;
-	    }
-	    p = p->next;
-	}
-    }
-    mgroup = (Cmgroup) zhalloc(sizeof(struct cmgroup));
-    mgroup->name = dupstring(n);
-    mgroup->lcount = mgroup->llcount = mgroup->mcount = 0;
-    mgroup->flags = flags;
-    mgroup->matches = NULL;
-    mgroup->ylist = NULL;
-    mgroup->expls = NULL;
-
-    mgroup->lexpls = expls = newlinklist();
-    mgroup->lmatches = matches = newlinklist();
-    mgroup->lfmatches = fmatches = newlinklist();
-
-    mgroup->lallccs = allccs = ((flags & CGF_NOSORT) ? 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(curexpl->str, e->str)) {
-	    e->count += curexpl->count;
-	    e->fcount += curexpl->fcount;
-
-	    return;
-	}
-    }
-    addlinknode(expls, curexpl);
-    newmatches = 1;
-}
-
-/* This duplicates one match. */
-
-/**/
-static Cmatch
-dupmatch(Cmatch m, int nbeg, int nend)
-{
-    Cmatch r;
-
-    r = (Cmatch) ncalloc(sizeof(struct cmatch));
-
-    r->str = ztrdup(m->str);
-    r->ipre = ztrdup(m->ipre);
-    r->ripre = ztrdup(m->ripre);
-    r->isuf = ztrdup(m->isuf);
-    r->ppre = ztrdup(m->ppre);
-    r->psuf = ztrdup(m->psuf);
-    r->prpre = ztrdup(m->prpre);
-    r->pre = ztrdup(m->pre);
-    r->suf = ztrdup(m->suf);
-    r->flags = m->flags;
-    if (nbeg) {
-	int *p, *q, i;
-
-	r->brpl = (int *) zalloc(nbeg * sizeof(int));
-
-	for (p = r->brpl, q = m->brpl, i = nbeg; i--; p++, q++)
-	    *p = *q;
-    } else
-	r->brpl = NULL;
-    if (nend) {
-	int *p, *q, i;
-
-	r->brsl = (int *) zalloc(nend * sizeof(int));
-
-	for (p = r->brsl, q = m->brsl, i = nend; i--; p++, q++)
-	    *p = *q;
-    } else
-	r->brsl = NULL;
-    r->rems = ztrdup(m->rems);
-    r->remf = ztrdup(m->remf);
-    r->autoq = m->autoq;
-    r->qipl = m->qipl;
-    r->qisl = m->qisl;
-    r->disp = dupstring(m->disp);
-
-    return r;
-}
-
-/* This duplicates all groups of matches. */
-
-/**/
-static int
-permmatches(int last)
-{
-    Cmgroup g = amatches, n;
-    Cmatch *p, *q;
-    Cexpl *ep, *eq, e, o;
-    LinkList mlist;
-    static int fi = 0;
-    int nn, nl, ll, gn = 1, mn = 1, rn;
-
-    if (pmatches && !newmatches)
-	return fi;
-
-    newmatches = fi = 0;
-
-    if (pmatches)
-	freematches(pmatches);
-
-    pmatches = lmatches = NULL;
-    nmatches = smatches = 0;
-
-    if (!ainfo->count) {
-	if (last)
-	    ainfo = fainfo;
-	fi = 1;
-    }
-    while (g) {
-	HEAPALLOC {
-	    if (empty(g->lmatches))
-		/* We have no matches, try ignoring fignore. */
-		mlist = g->lfmatches;
-	    else
-		mlist = g->lmatches;
-
-	    g->matches = makearray(mlist, 1, g->flags, &nn, &nl, &ll);
-	    g->mcount = nn;
-	    if ((g->lcount = nn - nl) < 0)
-		g->lcount = 0;
-	    g->llcount = ll;
-	    if (g->ylist) {
-		g->lcount = arrlen(g->ylist);
-		smatches = 2;
-	    }
-	    g->expls = (Cexpl *) makearray(g->lexpls, 0, 0, &(g->ecount),
-					   NULL, NULL);
-
-	    g->ccount = 0;
-	} LASTALLOC;
-
-	nmatches += g->mcount;
-	smatches += g->lcount;
-
-	n = (Cmgroup) ncalloc(sizeof(struct cmgroup));
-
-	if (!lmatches)
-	    lmatches = n;
-	if (pmatches)
-	    pmatches->prev = n;
-	n->next = pmatches;
-	pmatches = n;
-	n->prev = 0;
-	n->num = gn++;
-
-	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, nbrbeg, nbrend);
-	*p = NULL;
-
-	n->lcount = g->lcount;
-	n->llcount = g->llcount;
-	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;
-
-	n->widths = NULL;
-
-	g = g->next;
-    }
-    for (g = pmatches; g; g = g->next) {
-	g->nbrbeg = nbrbeg;
-	g->nbrend = nbrend;
-	for (rn = 1, q = g->matches; *q; q++) {
-	    (*q)->rnum = rn++;
-	    (*q)->gnum = mn++;
-	}
-    }
-    hasperm = 1;
-    permmnum = mn - 1;
-    permgnum = gn - 1;
-    listdat.valid = 0;
-
-    return fi;
-}
-
-/* Return the real number of matches. */
-
-/**/
-zlong
-num_matches(int normal)
-{
-    int alt;
-
-    PERMALLOC {
-	alt = permmatches(0);
-    } LASTALLOC;
-
-    if (normal)
-	return (alt ? 0 : nmatches);
-    else
-	return (alt ? nmatches : 0);
-}
-
-/* Return the number of screen lines needed for the list. */
-
-/**/
-zlong
-list_lines(void)
-{
-    Cmgroup oam;
-
-    PERMALLOC {
-	permmatches(0);
-    } LASTALLOC;
-
-    oam = amatches;
-    amatches = pmatches;
-    listdat.valid = 0;
-    calclist();
-    listdat.valid = 0;
-    amatches = oam;
-
-    return listdat.nlines;
-}
-
-/**/
-void
-comp_list(char *v)
-{
-    zsfree(complist);
-    complist = ztrdup(v);
-
-    onlyexpl = (v && strstr(v, "expl"));
-}
-
-/**/
-
-/* This frees one match. */
-
-/**/
-static void
-freematch(Cmatch m, int nbeg, int nend)
-{
-    if (!m) return;
-
-    zsfree(m->str);
-    zsfree(m->ipre);
-    zsfree(m->ripre);
-    zsfree(m->isuf);
-    zsfree(m->ppre);
-    zsfree(m->psuf);
-    zsfree(m->pre);
-    zsfree(m->suf);
-    zsfree(m->prpre);
-    zsfree(m->rems);
-    zsfree(m->remf);
-    zsfree(m->disp);
-    zfree(m->brpl, nbeg * sizeof(int));
-    zfree(m->brsl, nend * sizeof(int));
-
-    zfree(m, sizeof(m));
-}
-
-/* This frees the groups of matches. */
-
-/**/
-void
-freematches(Cmgroup g)
-{
-    Cmgroup n;
-    Cmatch *m;
-    Cexpl *e;
-
-    while (g) {
-	n = g->next;
-	
-	for (m = g->matches; *m; m++)
-	    freematch(*m, g->nbrbeg, g->nbrend);
-
-	if (g->ylist)
-	    freearray(g->ylist);
-
-	if ((e = g->expls)) {
-	    while (*e) {
-		zsfree((*e)->str);
-		free(*e);
-		e++;
-	    }
-	    free(g->expls);
-	}
-	free(g);
-
-	g = n;
-    }
-}
-
-/* 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).              *
- * The last argument says if we should quote the string.                */
-
-/**/
-static int
-inststrlen(char *str, int move, int len)
-{
-    if (!len || !str)
-	return 0;
-    if (len == -1)
-	len = strlen(str);
-    spaceinline(len);
-    strncpy((char *)(line + cs), str, len);
-    if (move)
-	cs += len;
-    return len;
-}
-
-/* This cuts the cline list before the stuff that isn't worth
- * inserting in the line. */
-
-static Cline
-cut_cline(Cline l)
-{
-    Cline q, p, e = NULL, maxp = NULL;
-    int sum = 0, max = 0, tmp, ls = 0;
-
-    /* If no match was added with matching, we don't really know
-     * which parts of the unambiguous string are worth keeping,
-     * so for now we keep everything (in the hope that this
-     * produces a string containing at least everything that was 
-     * originally on the line). */
-
-    if (!hasmatched) {
-	cline_setlens(l, 0);
-	return l;
-    }
-    e = l = cp_cline(l, 0);
-
-    /* First, search the last struct for which we have something on
-     * the line. Anything before that is kept. */
-
-    for (q = NULL, p = l; p; p = p->next) {
-	if (p->orig || p->olen || !(p->flags & CLF_NEW))
-	    e = p->next;
-	if (!p->suffix && (p->wlen || p->llen || p->prefix))
-	    q = p;
-    }
-    if (!e && q && !q->orig && !q->olen && (q->flags & CLF_MISS) &&
-	!(q->flags & CLF_MATCHED) && (q->word ? q->wlen : q->llen) < 3) {
-	q->word = q->line = NULL;
-	q->wlen = q->llen = 0;
-    }
-    /* Then keep all structs without missing characters. */
-
-    while (e && !(e->flags & CLF_MISS))
-	e = e->next;
-
-    if (e) {
-	/* Then we see if there is another struct with missing
-	 * characters. If not, we keep the whole list. */
-
-	for (p = e->next; p && !(p->flags & CLF_MISS); p = p->next);
-
-	if (p) {
-	    for (p = e; p; p = p->next) {
-		if (!(p->flags & CLF_MISS))
-		    sum += p->max;
-		else {
-		    tmp = cline_sublen(p);
-		    if (tmp > 2 && tmp > ((p->max + p->min) >> 1))
-			sum += tmp - (p->max - tmp);
-		    else if (tmp < p->min)
-			sum -= (((p->max + p->min) >> 1) - tmp) << (tmp < 2);
-		}
-		if (sum > max) {
-		    max = sum;
-		    maxp = p;
-		}
-	    }
-	    if (max)
-		e = maxp;
-	    else {
-		int len = 0;
-
-		cline_setlens(l, 0);
-		ls = 1;
-
-		for (p = e; p; p = p->next)
-		    len += p->max;
-
-		if (len > ((minmlen << 1) / 3))
-		    return l;
-	    }
-	    e->line = e->word = NULL;
-	    e->llen = e->wlen = e->olen = 0;
-	    e->next = NULL;
-	}
-    }
-    if (!ls)
-	cline_setlens(l, 0);
-
-    return l;
-}
-
-/* This builds the unambiguous string. If ins is non-zero, it is
- * immediatly inserted in the line. Otherwise csp is used to return
- * the relative cursor position in the string returned. */
-
-static char *
-cline_str(Cline l, int ins, int *csp)
-{
-    Cline s;
-    int ocs = cs, ncs, pcs, scs, pm, pmax, pmm, sm, smax, smm, d, dm, mid;
-    int i, j, li = 0, cbr;
-    Brinfo brp, brs;
-
-    l = cut_cline(l);
-
-    pmm = smm = dm = 0;
-    pm = pmax = sm = smax = d = mid = cbr = -1;
-
-    /* Get the information about the brace beginning and end we have
-     * to re-insert. */
-    if (ins) {
-	Brinfo bp;
-	int olen = we - wb;
-
-	if ((brp = brbeg)) {
-	    for (bp = brbeg; bp; bp = bp->next) {
-		bp->curpos = (hasunqu ? bp->pos : bp->qpos);
-		olen -= strlen(bp->str);
-	    }
-	}
-	if ((brs = lastbrend)) {
-	    for (bp = brend; bp; bp = bp->next)
-		olen -= strlen(bp->str);
-
-	    for (bp = brend; bp; bp = bp->next)
-		bp->curpos = olen - (hasunqu ? bp->pos : bp->qpos);
-	}
-	while (brp && !brp->curpos) {
-	    inststrlen(brp->str, 1, -1);
-	    brp = brp->next;
-	}
-	while (brs && !brs->curpos) {
-	    if (cbr < 0)
-		cbr = cs;
-	    inststrlen(brs->str, 1, -1);
-	    brs = brs->prev;
-	}
-    }
-    /* Walk through the top-level cline list. */
-    while (l) {
-	/* Insert the original string if no prefix. */
-	if (l->olen && !(l->flags & CLF_SUF) && !l->prefix) {
-	    pcs = cs + l->olen;
-	    inststrlen(l->orig, 1, l->olen);
-	} else {
-	    /* Otherwise insert the prefix. */
-	    for (s = l->prefix; s; s = s->next) {
-		pcs = cs + s->llen;
-		if (s->flags & CLF_LINE)
-		    inststrlen(s->line, 1, s->llen);
-		else
-		    inststrlen(s->word, 1, s->wlen);
-		scs = cs;
-
-		if ((s->flags & CLF_DIFF) && (!dm || (s->flags & CLF_MATCHED))) {
-		    d = cs; dm = s->flags & CLF_MATCHED;
-		}
-		li += s->llen;
-	    }
-	}
-	if (ins) {
-	    int ocs, bl;
-
-	    while (brp && li >= brp->curpos) {
-		ocs = cs;
-		bl = strlen(brp->str);
-		cs = pcs - (li - brp->curpos);
-		inststrlen(brp->str, 1, bl);
-		cs = ocs + bl;
-		pcs += bl;
-		scs += bl;
-		brp = brp->next;
-	    }
-	}
-	/* Remember the position if this is the first prefix with
-	 * missing characters. */
-	if ((l->flags & CLF_MISS) && !(l->flags & CLF_SUF) &&
-	    ((pmax < (l->min - l->max) && (!pmm || (l->flags & CLF_MATCHED))) ||
-	     ((l->flags & CLF_MATCHED) && !pmm))) {
-	    pm = cs; pmax = l->min - l->max; pmm = l->flags & CLF_MATCHED;
-	}
-	if (ins) {
-	    int ocs, bl;
-
-	    while (brs && li >= brs->curpos) {
-		ocs = cs;
-		bl = strlen(brs->str);
-		cs = scs - (li - brs->curpos);
-		if (cbr < 0)
-		    cbr = cs;
-		inststrlen(brs->str, 1, bl);
-		cs = ocs + bl;
-		pcs += bl;
-		brs = brs->prev;
-	    }
-	}
-	pcs = cs;
-	/* Insert the anchor. */
-	if (l->flags & CLF_LINE)
-	    inststrlen(l->line, 1, l->llen);
-	else
-	    inststrlen(l->word, 1, l->wlen);
-	scs = cs;
-	if (ins) {
-	    int ocs, bl;
-
-	    li += l->llen;
-
-	    while (brp && li >= brp->curpos) {
-		ocs = cs;
-		bl = strlen(brp->str);
-		cs = pcs + l->llen - (li - brp->curpos);
-		inststrlen(brp->str, 1, bl);
-		cs = ocs + bl;
-		pcs += bl;
-		scs += bl;
-		brp = brp->next;
-	    }
-	}
-	/* Remember the cursor position for suffixes and mids. */
-	if (l->flags & CLF_MISS) {
-	    if (l->flags & CLF_MID)
-		mid = cs;
-	    else if ((l->flags & CLF_SUF) && 
-		     ((smax < (l->min - l->max) &&
-		       (!smm || (l->flags & CLF_MATCHED))) ||
-		      ((l->flags & CLF_MATCHED) && !smm))) {
-		sm = cs; smax = l->min - l->max; smm = l->flags & CLF_MATCHED;
-	    }
-	}
-	if (ins) {
-	    int ocs, bl;
-
-	    while (brs && li >= brs->curpos) {
-		ocs = cs;
-		bl = strlen(brs->str);
-		cs = scs - (li - brs->curpos);
-		if (cbr < 0)
-		    cbr = cs;
-		inststrlen(brs->str, 1, bl);
-		cs = ocs + bl;
-		pcs += bl;
-		brs = brs->prev;
-	    }
-	}
-	/* And now insert the suffix or the original string. */
-	if (l->olen && (l->flags & CLF_SUF) && !l->suffix) {
-	    pcs = cs;
-	    inststrlen(l->orig, 1, l->olen);
-	    if (ins) {
-		int ocs, bl;
-
-		li += l->olen;
-
-		while (brp && li >= brp->curpos) {
-		    ocs = cs;
-		    bl = strlen(brp->str);
-		    cs = pcs + l->olen - (li - brp->curpos);
-		    inststrlen(brp->str, 1, bl);
-		    cs = ocs + bl;
-		    pcs += bl;
-		    brp = brp->next;
-		}
-		while (brs && li >= brs->curpos) {
-		    ocs = cs;
-		    bl = strlen(brs->str);
-		    cs = pcs + l->olen - (li - brs->curpos);
-		    if (cbr < 0)
-			cbr = cs;
-		    inststrlen(brs->str, 1, bl);
-		    cs = ocs + bl;
-		    pcs += bl;
-		    brs = brs->prev;
-		}
-	    }
-	} else {
-	    Cline js = NULL;
-
-	    for (j = -1, i = 0, s = l->suffix; s; s = s->next) {
-		if (j < 0 && (s->flags & CLF_DIFF))
-		    j = i, js = s;
-		pcs = cs;
-		if (s->flags & CLF_LINE) {
-		    inststrlen(s->line, 0, s->llen);
-		    i += s->llen; scs = cs + s->llen;
-		} else {
-		    inststrlen(s->word, 0, s->wlen);
-		    i += s->wlen; scs = cs + s->wlen;
-		}
-		if (ins) {
-		    int ocs, bl;
-
-		    li += s->llen;
-
-		    while (brp && li >= brp->curpos) {
-			ocs = cs;
-			bl = strlen(brp->str);
-			cs = pcs + (li - brp->curpos);
-			inststrlen(brp->str, 1, bl);
-			cs = ocs + bl;
-			pcs += bl;
-			scs += bl;
-			brp = brp->next;
-		    }
-		    while (brs && li >= brs->curpos) {
-			ocs = cs;
-			bl = strlen(brs->str);
-			cs = scs - (li - brs->curpos);
-			if (cbr < 0)
-			    cbr = cs;
-			inststrlen(brs->str, 1, bl);
-			cs = ocs + bl;
-			pcs += bl;
-			brs = brs->prev;
-		    }
-		}
-	    }
-	    cs += i;
-	    if (j >= 0 && (!dm || (js->flags & CLF_MATCHED))) {
-		d = cs - j; dm = js->flags & CLF_MATCHED;
-	    }
-	}
-	l = l->next;
-    }
-    if (ins) {
-	int ocs = cs;
-
-	for (; brp; brp = brp->next)
-	    inststrlen(brp->str, 1, -1);
-	for (; brs; brs = brs->prev) {
-	    if (cbr < 0)
-		cbr = cs;
-	    inststrlen(brs->str, 1, -1);
-	}
-	if (mid >= ocs)
-	    mid += cs - ocs;
-	if (pm >= ocs)
-	    pm += cs - ocs;
-	if (sm >= ocs)
-	    sm += cs - ocs;
-	if (d >= ocs)
-	    d += cs - ocs;
-    }
-    /* This calculates the new cursor position. If we had a mid cline
-     * with missing characters, we take this, otherwise if we have a
-     * prefix with missing characters, we take that, the same for a
-     * suffix, and finally a place where the matches differ. */
-    ncs = (cbr >= 0 ? cbr :
-	   (mid >= 0 ? mid :
-	    (pm >= 0 ? pm : (sm >= 0 ? sm : (d >= 0 ? d : cs)))));
-
-    if (!ins) {
-	/* We always inserted the string in the line. If that was not
-	 * requested, we copy it and remove from the line. */
-	char *r = zalloc((i = cs - ocs) + 1);
-
-	memcpy(r, (char *) (line + ocs), i);
-	r[i] = '\0';
-	cs = ocs;
-	foredel(i);
-
-	*csp = ncs - ocs;
-
-	return r;
-    }
-    lastend = cs;
-    cs = ncs;
-
-    return NULL;
-}
-
-/* This is a utility function using the function above to allow access
- * to the unambiguous string and cursor position via compstate. */
-
-/**/
-char *
-unambig_data(int *cp)
-{
-    static char *scache = NULL;
-    static int ccache;
-
-    if (mnum && ainfo) {
-	if (mnum != unambig_mnum) {
-	    zsfree(scache);
-	    scache = cline_str((ainfo->count ? ainfo->line : fainfo->line),
-			       0, &ccache);
-	}
-    } else if (mnum != unambig_mnum || !ainfo || !scache) {
-	zsfree(scache);
-	scache = ztrdup("");
-	ccache = 0;
-    }
-    unambig_mnum = mnum;
-    if (cp)
-	*cp = ccache + 1;
-
-    return scache;
-}
-
-/* Insert the given match. This returns the number of characters inserted.
- * scs is used to return the position where a automatically created suffix
- * has to be inserted. */
-
-/**/
-static int
-instmatch(Cmatch m, int *scs)
-{
-    int l, r = 0, ocs, a = cs, brb = 0, bradd, *brpos;
-    Brinfo bp;
-
-    zsfree(lastprebr);
-    zsfree(lastpostbr);
-    lastprebr = lastpostbr = NULL;
-
-    /* Ignored prefix. */
-    if (m->ipre) {
-	char *p = m->ipre + (menuacc ? m->qipl : 0);
-
-	inststrlen(p, 1, (l = strlen(p)));
-	r += l;
-    }
-    /* -P prefix. */
-    if (m->pre) {
-	inststrlen(m->pre, 1, (l = strlen(m->pre)));
-	r += l;
-    }
-    /* Path prefix. */
-    if (m->ppre) {
-	inststrlen(m->ppre, 1, (l = strlen(m->ppre)));
-	r += l;
-    }
-    /* The string itself. */
-    inststrlen(m->str, 1, (l = strlen(m->str)));
-    r += l;
-    ocs = cs;
-    /* Re-insert the brace beginnings, if any. */
-    if (brbeg) {
-	int pcs = cs;
-
-	l = 0;
-	for (bp = brbeg, brpos = m->brpl,
-		 bradd = (m->pre ? strlen(m->pre) : 0);
-	     bp; bp = bp->next, brpos++) {
-	    cs = a + *brpos + bradd;
-	    pcs = cs;
-	    l = strlen(bp->str);
-	    bradd += l;
-	    brpcs = cs;
-	    inststrlen(bp->str, 1, l);
-	    r += l;
-	    ocs += l;
-	}
-	lastprebr = (char *) zalloc(pcs - a + 1);
-	memcpy(lastprebr, (char *) line + a, pcs - a);
-	lastprebr[pcs - a] = '\0';
-	cs = ocs;
-    }
-    /* Path suffix. */
-    if (m->psuf) {
-	inststrlen(m->psuf, 1, (l = strlen(m->psuf)));
-	r += l;
-    }
-    /* Re-insert the brace end. */
-    if (brend) {
-	a = cs;
-	for (bp = brend, brpos = m->brsl, bradd = 0; bp; bp = bp->next, brpos++) {
-	    cs = a - *brpos;
-	    ocs = brscs = cs;
-	    l = strlen(bp->str);
-	    bradd += l;
-	    inststrlen(bp->str, 1, l);
-	    brb = cs;
-	    r += l;
-	}
-	cs = a + bradd;
-	if (scs)
-	    *scs = ocs;
-    } else {
-	brscs = -1;
-
-	if (scs)
-	    *scs = cs;
-    }
-    /* -S suffix */
-    if (m->suf) {
-	inststrlen(m->suf, 1, (l = strlen(m->suf)));
-	r += l;
-    }
-    /* ignored suffix */
-    if (m->isuf) {
-	inststrlen(m->isuf, 1, (l = strlen(m->isuf)));
-	r += l;
-    }
-    if (brend) {
-	lastpostbr = (char *) zalloc(cs - brb + 1);
-	memcpy(lastpostbr, (char *) line + brb, cs - brb);
-	lastpostbr[cs - brb] = '\0';
-    }
-    lastend = cs;
-    cs = ocs;
-
-    return r;
-}
-
-/* Check if the match has the given prefix/suffix before/after the
- * braces. */
-
-/**/
-int
-hasbrpsfx(Cmatch m, char *pre, char *suf)
-{
-    char *op = lastprebr, *os = lastpostbr;
-    VARARR(char, oline, ll);
-    int oll = ll, ocs = cs, ole = lastend, opcs = brpcs, oscs = brscs, ret;
-
-    memcpy(oline, line, ll);
-
-    lastprebr = lastpostbr = NULL;
-
-    instmatch(m, NULL);
-
-    cs = 0;
-    foredel(ll);
-    spaceinline(oll);
-    memcpy(line, oline, oll);
-    cs = ocs;
-    lastend = ole;
-    brpcs = opcs;
-    brscs = oscs;
-
-    ret = (((!pre && !lastprebr) ||
-	    (pre && lastprebr && !strcmp(pre, lastprebr))) &&
-	   ((!suf && !lastpostbr) ||
-	    (suf && lastpostbr && !strcmp(suf, lastpostbr))));
-
-    zsfree(lastprebr);
-    zsfree(lastpostbr);
-    lastprebr = op;
-    lastpostbr = os;
-
-    return ret;
-}
-
-/* Handle the case were we found more than one match. */
-
-/**/
-static int
-do_ambiguous(void)
-{
-    int ret = 0;
-
-    menucmp = menuacc = 0;
-
-    /* If we have to insert the first match, call do_single().  This is *
-     * how REC_EXACT takes effect.  We effectively turn the ambiguous   *
-     * completion into an unambiguous one.                              */
-    if (ainfo && ainfo->exact == 1 && useexact && !(fromcomp & FC_LINE)) {
-	minfo.cur = NULL;
-	do_single(ainfo->exactm);
-	invalidatelist();
-	return ret;
-    }
-    /* Setting lastambig here means that the completion is ambiguous and *
-     * AUTO_MENU might want to start a menu completion next time round,  *
-     * but this might be overridden below if we can complete an          *
-     * unambiguous prefix.                                               */
-    lastambig = 1;
-
-    if (usemenu || (haspattern && comppatinsert &&
-		    !strcmp(comppatinsert, "menu"))) {
-	/* We are in a position to start using menu completion due to one  *
-	 * of the menu completion options, or due to the menu-complete-    *
-	 * word command, or due to using GLOB_COMPLETE which does menu-    *
-	 * style completion regardless of the setting of the normal menu   *
-	 * completion options.                                             */
-	do_ambig_menu();
-    } else if (ainfo) {
-	int atend = (cs == we), la, eq, tcs;
-
-	minfo.cur = NULL;
-	minfo.asked = 0;
-
-	fixsuffix();
-
-	/* First remove the old string from the line. */
-	cs = wb;
-	foredel(we - wb);
-
-	/* Now get the unambiguous string and insert it into the line. */
-	cline_str(ainfo->line, 1, NULL);
-	if (eparq) {
-	    tcs = cs;
-	    cs = lastend;
-	    for (eq = eparq; eq; eq--)
-		inststrlen("\"", 0, 1);
-	    cs = tcs;
-	}
-	/* la is non-zero if listambiguous may be used. Copying and
-	 * comparing the line looks like BFI but it is the easiest
-	 * solution. Really. */
-	la = (ll != origll || strncmp(origline, (char *) line, ll));
-
-	/* If REC_EXACT and AUTO_MENU are set and what we inserted is an  *
-	 * exact match, we want menu completion the next time round       *
-	 * so we set fromcomp, to ensure that the word on the line is not *
-	 * taken as an exact match. Also we remember if we just moved the *
-	 * cursor into the word.                                          */
-	fromcomp = ((isset(AUTOMENU) ? FC_LINE : 0) |
-		    ((atend && cs != lastend) ? FC_INWORD : 0));
-
-	/* Probably move the cursor to the end. */
-	if (movetoend == 3)
-	    cs = lastend;
-
-	/* 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 (uselist == 3 && la) {
-	    int fc = fromcomp;
-
-	    invalidatelist();
-	    fromcomp = fc;
-	    lastambig = 0;
-	    clearlist = 1;
-	    return ret;
-	}
-    } else
-	return ret;
-
-    /* At this point, we might want a completion listing.  Show the listing *
-     * if it is needed.                                                     */
-    if (isset(LISTBEEP))
-	ret = 1;
-
-    if (uselist && (usemenu != 2 || (!listshown && !oldlist)) &&
-	((!showinglist && (!listshown || !oldlist)) ||
-	 (usemenu == 3 && !oldlist)) &&
-	(smatches >= 2 || (compforcelist && *compforcelist)))
-	showinglist = -2;
-
-    return ret;
-}
-
-/* This is a stat that ignores backslashes in the filename.  The `ls' *
- * parameter says if we have to do lstat() or stat().  I think this   *
- * should instead be done by use of a general function to expand a    *
- * filename (stripping backslashes), combined with the actual         *
- * (l)stat().                                                         */
-
-/**/
-int
-ztat(char *nam, struct stat *buf, int ls)
-{
-    char b[PATH_MAX], *p;
-
-    for (p = b; p < b + sizeof(b) - 1 && *nam; nam++)
-	if (*nam == '\\' && nam[1])
-	    *p++ = *++nam;
-	else
-	    *p++ = *nam;
-    *p = '\0';
-
-    return ls ? lstat(b, buf) : stat(b, buf);
-}
-
-/* Insert a single match in the command line. */
-
-/**/
-void
-do_single(Cmatch m)
-{
-    int l, sr = 0, scs;
-    int havesuff = 0;
-    int partest = (m->ripre || ((m->flags & CMF_ISPAR) && parpre));
-    char *str = m->str, *ppre = m->ppre, *psuf = m->psuf, *prpre = m->prpre;
-
-    if (!prpre) prpre = "";
-    if (!ppre) ppre = "";
-    if (!psuf) psuf = "";
-
-    fixsuffix();
-
-    if (!minfo.cur) {
-	/* We are currently not in a menu-completion, *
-	 * so set the position variables.             */
-	minfo.pos = wb;
-	minfo.we = (movetoend >= 2 || (movetoend == 1 && !menucmp));
-	minfo.end = 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 (minfo.cur)
-	l = minfo.len + minfo.insc;
-    else
-	l = we - wb;
-
-    minfo.insc = 0;
-    cs = minfo.pos;
-    foredel(l);
-
-    /* And then we insert the new string. */
-    minfo.len = instmatch(m, &scs);
-    minfo.end = cs;
-    cs = minfo.pos + minfo.len;
-
-    if (m->suf) {
-	havesuff = 1;
-	minfo.insc = ztrlen(m->suf);
-	minfo.len -= minfo.insc;
-	if (minfo.we) {
-	    minfo.end += minfo.insc;
-	    if (m->flags & CMF_REMOVE) {
-		makesuffixstr(m->remf, m->rems, minfo.insc);
-		if (minfo.insc == 1)
-		    suffixlen[STOUC(m->suf[0])] = 1;
-	    }
-	}
-    } else {
-	/* There is no user-specified suffix, *
-	 * so generate one automagically.     */
-	cs = scs;
-	if (partest && (m->flags & CMF_PARBR)) {
-	    int pq;
-
-	    /*{{*/
-	    /* Completing a parameter in braces.  Add a removable `}' suffix. */
-	    cs += eparq;
-	    for (pq = parq; pq; pq--)
-		inststrlen("\"", 1, 1);
-	    minfo.insc += parq;
-	    inststrlen("}", 1, 1);
-	    minfo.insc++;
-	    if (minfo.we)
-		minfo.end += minfo.insc;
-	    if (m->flags & CMF_PARNEST)
-		havesuff = 1;
-	}
-	if (((m->flags & CMF_FILE) || (partest && isset(AUTOPARAMSLASH))) &&
-	    cs > 0 && line[cs - 1] != '/') {
-	    /* 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.                                */
-	    struct stat buf;
-	    char *p;
-	    int t = 0;
-
-	    if (m->ipre && m->ipre[0] == '~' && !m->ipre[1])
-		t = 1;
-	    else {
-		/* Build the path name. */
-		if (partest && !*psuf && !(m->flags & CMF_PARNEST)) {
-		    int ne = noerrs;
-
-		    p = (char *) zhalloc(strlen((m->flags & CMF_ISPAR) ?
-						parpre : m->ripre) +
-					 strlen(str) + 2);
-		    sprintf(p, "%s%s%c",
-			    ((m->flags & CMF_ISPAR) ? parpre : m->ripre), str,
-			    ((m->flags & CMF_PARBR) ? Outbrace : '\0'));
-		    noerrs = 1;
-		    parsestr(p);
-		    singsub(&p);
-		    errflag = 0;
-		    noerrs = ne;
-		} else {
-		    p = (char *) zhalloc(strlen(prpre) + strlen(str) +
-				 strlen(psuf) + 3);
-		    sprintf(p, "%s%s%s", ((prpre && *prpre) ?
-					  prpre : "./"), str, psuf);
-		}
-		/* And do the stat. */
-		t = (!(sr = ztat(p, &buf, 0)) && S_ISDIR(buf.st_mode));
-	    }
-	    if (t) {
-		/* It is a directory, so add the slash. */
-		havesuff = 1;
-		inststrlen("/", 1, 1);
-		minfo.insc++;
-		if (minfo.we)
-		    minfo.end++;
-		if (!menucmp || minfo.we) {
-		    if (m->remf || m->rems)
-			makesuffixstr(m->remf, m->rems, 1);
-		    else if (isset(AUTOREMOVESLASH)) {
-			makesuffix(1);
-			suffixlen['/'] = 1;
-		    }
-		}
-	    }
-	}
-	if (!minfo.insc)
-	    cs = minfo.pos + minfo.len - m->qisl;
-    }
-    /* If completing in a brace expansion... */
-    if (brbeg) {
-	if (havesuff) {
-	    /*{{*/
-	    /* If a suffix was added, and is removable, let *
-	     * `,' and `}' remove it.                       */
-	    if (isset(AUTOPARAMKEYS))
-		suffixlen[','] = suffixlen['}'] = suffixlen[256];
-	} else if (!menucmp) {
-	    /*{{*/
-	    /* Otherwise, add a `,' suffix, and let `}' remove it. */
-	    cs = scs;
-	    havesuff = 1;
-	    inststrlen(",", 1, 1);
-	    minfo.insc++;
-	    makesuffix(1);
-	    if ((!menucmp || minfo.we) && isset(AUTOPARAMKEYS))
-		suffixlen[','] = suffixlen['}'] = 1;
-	}
-    } else if (!havesuff && (!(m->flags & CMF_FILE) || !sr)) {
-	/* If we didn't add a suffix, add a space, unless we are *
-	 * doing menu completion or we are completing files and  *
-	 * the string doesn't name an existing file.             */
-	if (m->autoq && (!m->isuf || m->isuf[0] != m->autoq)) {
-	    inststrlen(&(m->autoq), 1, 1);
-	    minfo.insc++;
-	}
-	if (!menucmp && (usemenu != 3 || insspace)) {
-	    inststrlen(" ", 1, 1);
-	    minfo.insc++;
-	    if (minfo.we)
-		makesuffix(1);
-	}
-    }
-    if (minfo.we && partest && isset(AUTOPARAMKEYS))
-	makeparamsuffix(((m->flags & CMF_PARBR) ? 1 : 0), minfo.insc - parq);
-
-    if ((menucmp && !minfo.we) || !movetoend) {
-	cs = minfo.end;
-	if (cs + m->qisl == lastend)
-	    cs += minfo.insc;
-    }
-    {
-	Cmatch *om = minfo.cur;
-	struct chdata dat;
-
-	dat.matches = amatches;
-	dat.num = nmatches;
-	dat.cur = m;
-
-	if (menucmp)
-	    minfo.cur = &m;
-	runhookdef(INSERTMATCHHOOK, (void *) &dat);
-	minfo.cur = om;
-    }
-}
-
-/* This maps the value in v into the range [0,m-1], decrementing v
- * if it is non-negative and making negative values count backwards. */
-
-static int
-comp_mod(int v, int m)
-{
-    if (v >= 0)
-	v--;
-    if (v >= 0)
-	return v % m;
-    else {
-	while (v < 0)
-	    v += m;
-	return v;
-    }
-}
-
-/* This handles the beginning of menu-completion. */
-
-/**/
-static void
-do_ambig_menu(void)
-{
-    Cmatch *mc;
-
-    if (usemenu != 3) {
-	menucmp = 1;
-	menuacc = 0;
-	minfo.cur = NULL;
-    } else {
-	if (oldlist) {
-	    if (oldins && minfo.cur)
-		acceptlast();
-	} else
-	    minfo.cur = NULL;
-    }
-    if (insgroup) {
-	insgnum = comp_mod(insgnum, lastpermgnum);
-	for (minfo.group = amatches;
-	     minfo.group && (minfo.group)->num != insgnum + 1;
-	     minfo.group = (minfo.group)->next);
-	if (!minfo.group || !(minfo.group)->mcount) {
-	    minfo.cur = NULL;
-	    minfo.asked = 0;
-	    return;
-	}
-	insmnum = comp_mod(insmnum, (minfo.group)->mcount);
-    } else {
-	insmnum = comp_mod(insmnum, lastpermmnum);
-	for (minfo.group = amatches;
-	     minfo.group && (minfo.group)->mcount <= insmnum;
-	     minfo.group = (minfo.group)->next)
-	    insmnum -= (minfo.group)->mcount;
-	if (!minfo.group) {
-	    minfo.cur = NULL;
-	    minfo.asked = 0;
-	    return;
-	}
-    }
-    mc = (minfo.group)->matches + insmnum;
-    do_single(*mc);
-    minfo.cur = mc;
-}
-
-/* Return the length of the common prefix of s and t. */
-
-/**/
-int
-pfxlen(char *s, char *t)
-{
-    int i = 0;
-
-    while (*s && *s == *t)
-	s++, t++, i++;
-    return i;
-}
-
-/* Return the length of the common suffix of s and t. */
-
-#if 0
-static int
-sfxlen(char *s, char *t)
-{
-    if (*s && *t) {
-	int i = 0;
-	char *s2 = s + strlen(s) - 1, *t2 = t + strlen(t) - 1;
-
-	while (s2 >= s && t2 >= t && *s2 == *t2)
-	    s2--, t2--, i++;
-
-	return i;
-    } else
-	return 0;
-}
-#endif
-
-/* This is used to print the explanation string. *
- * It returns the number of lines printed.       */
+/* This is used to print expansions. */
 
 /**/
 int
-printfmt(char *fmt, int n, int dopr, int doesc)
-{
-    char *p = fmt, nc[DIGBUFSIZE];
-    int l = 0, cc = 0, b = 0, s = 0, u = 0, m;
-
-    for (; *p; p++) {
-	/* Handle the `%' stuff (%% == %, %n == <number of matches>). */
-	if (doesc && *p == '%') {
-	    if (*++p) {
-		m = 0;
-		switch (*p) {
-		case '%':
-		    if (dopr)
-			putc('%', shout);
-		    cc++;
-		    break;
-		case 'n':
-		    sprintf(nc, "%d", n);
-		    if (dopr)
-			fprintf(shout, nc);
-		    cc += strlen(nc);
-		    break;
-		case 'B':
-		    b = 1;
-		    if (dopr)
-			tcout(TCBOLDFACEBEG);
-		    break;
-		case 'b':
-		    b = 0; m = 1;
-		    if (dopr)
-			tcout(TCALLATTRSOFF);
-		    break;
-		case 'S':
-		    s = 1;
-		    if (dopr)
-			tcout(TCSTANDOUTBEG);
-		    break;
-		case 's':
-		    s = 0; m = 1;
-		    if (dopr)
-			tcout(TCSTANDOUTEND);
-		    break;
-		case 'U':
-		    u = 1;
-		    if (dopr)
-			tcout(TCUNDERLINEBEG);
-		    break;
-		case 'u':
-		    u = 0; m = 1;
-		    if (dopr)
-			tcout(TCUNDERLINEEND);
-		    break;
-		case '{':
-		    for (p++; *p && (*p != '%' || p[1] != '}'); p++, cc++)
-			if (dopr)
-			    putc(*p, shout);
-		    if (*p)
-			p++;
-		    else
-			p--;
-		    break;
-		}
-		if (dopr && m) {
-		    if (b)
-			tcout(TCBOLDFACEBEG);
-		    if (s)
-			tcout(TCSTANDOUTBEG);
-		    if (u)
-			tcout(TCUNDERLINEBEG);
-		}
-	    } else
-		break;
-	} else {
-	    cc++;
-	    if (*p == '\n') {
-		if (dopr) {
-		    if (tccan(TCCLEAREOL))
-			tcout(TCCLEAREOL);
-		    else {
-			int s = columns - 1 - (cc % columns);
-
-			while (s-- > 0)
-			    putc(' ', shout);
-		    }
-		}
-		l += 1 + (cc / columns);
-		cc = 0;
-	    }
-	    if (dopr)
-		putc(*p, shout);
-	}
-    }
-    if (dopr) {
-	if (tccan(TCCLEAREOL))
-	    tcout(TCCLEAREOL);
-	else {
-	    int s = columns - 1 - (cc % columns);
-
-	    while (s-- > 0)
-		putc(' ', shout);
-	}
-    }
-    return l + (cc / columns);
-}
-
-/* This skips over matches that are not to be listed. */
-
-/**/
-Cmatch *
-skipnolist(Cmatch *p)
-{
-    while (*p && (((*p)->flags & (CMF_NOLIST | CMF_HIDE)) ||
-		  ((*p)->disp && ((*p)->flags & (CMF_DISPLINE | CMF_HIDE)))))
-	p++;
-
-    return p;
-}
-
-/* List the matches.  Note that the list entries are metafied. */
-
-/**/
-void
-listmatches(void)
-{
-    struct chdata dat;
-
-#ifdef DEBUG
-    /* Sanity check */
-    if (!validlist) {
-	showmsg("BUG: listmatches called with bogus list");
-	return;
-    }
-#endif
-
-    dat.matches = amatches;
-    dat.num = nmatches;
-    dat.cur = NULL;
-    runhookdef(LISTMATCHESHOOK, (void *) &dat);
-}
-
-/**/
-void
-calclist(void)
+listlist(LinkList l)
 {
-    Cmgroup g;
-    Cmatch *p, m;
-    Cexpl *e;
-    int hidden = 0, nlist = 0, nlines = 0, add = 2 + isset(LISTTYPES);
-    int max = 0, i;
-    VARARR(int, mlens, nmatches + 1);
-
-    if (listdat.valid && onlyexpl == listdat.onlyexpl &&
-	menuacc == listdat.menuacc &&
-	lines == listdat.lines && columns == listdat.columns)
-	return;
-
-    for (g = amatches; g; g = g->next) {
-	char **pp = g->ylist;
-	int nl = 0, l, glong = 1, gshort = columns, ndisp = 0, totl = 0;
-
-	if (!onlyexpl && pp) {
-	    /* We have an ylist, lets see, if it contains newlines. */
-	    hidden = 1;
-	    while (!nl && *pp)
-		nl = !!strchr(*pp++, '\n');
-
-	    pp = g->ylist;
-	    if (nl || !pp[1]) {
-		/* Yup, there are newlines, count lines. */
-		char *nlptr, *sptr;
-
-		g->flags |= CGF_LINES;
-		hidden = 1;
-		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) {
-		    l = strlen(*pp);
-		    ndisp++;
-		    if (l > glong)
-			glong = l;
-		    if (l < gshort)
-			gshort = l;
-		    totl += l;
-		    nlist++;
-		    pp++;
-		}
-	    }
-	} else if (!onlyexpl) {
-	    for (p = g->matches; (m = *p); p++) {
-		if (menuacc && !hasbrpsfx(m, minfo.prebr, minfo.postbr)) {
-		    m->flags |= CMF_HIDE;
-		    continue;
-		}
-		m->flags &= ~CMF_HIDE;
+    int num = countlinknodes(l);
+    VARARR(char *, data, (num + 1));
+    LinkNode node;
+    char **p;
+    VARARR(int, lens, num);
+    VARARR(int, widths, columns);
+    int longest = 0, shortest = columns, totl = 0;
+    int len, ncols, nlines, tolast, col, i, max, pack = 0, *lenp;
+
+    for (node = firstnode(l), p = data; node; incnode(node), p++)
+	*p = (char *) getdata(node);
+    *p = NULL;
 
-		if (m->disp) {
-		    if (m->flags & CMF_DISPLINE) {
-			nlines += 1 + printfmt(m->disp, 0, 0, 0);
-			g->flags |= CGF_HASDL;
-		    } else {
-			l = niceztrlen(m->disp);
-			ndisp++;
-			if (l > glong)
-			    glong = l;
-			if (l < gshort)
-			    gshort = l;
-			totl += l;
-			mlens[m->gnum] = l;
+    qsort((void *) data, num, sizeof(char *),
+	  (int (*) _((const void *, const void *))) strbpcmp);
+
+    for (p = data, lenp = lens; *p; p++, lenp++) {
+	len = *lenp = niceztrlen(*p) + 2;
+	if (len > longest)
+	    longest = len;
+	if (len < shortest)
+	    shortest = len;
+	totl += len;
+    }
+    if ((ncols = ((columns + 2) / longest))) {
+	int tlines, tline, tcols, maxlen, nth, width;
+
+	nlines = (num + ncols - 1) / ncols;
+
+	if (isset(LISTPACKED)) {
+	    if (isset(LISTROWSFIRST)) {
+		int count, tcol, first, maxlines = 0, llines;
+
+		for (tcols = columns / shortest; tcols > ncols;
+		     tcols--) {
+		    for (nth = first = maxlen = width = maxlines =
+			     llines = tcol = 0,
+			     count = num;
+			 count > 0; count--) {
+			if (!(nth % tcols))
+			    llines++;
+			if (lens[nth] > maxlen)
+			    maxlen = lens[nth];
+			nth += tcols;
+			tlines++;
+			if (nth >= num) {
+			    if ((width += maxlen) >= columns)
+				break;
+			    widths[tcol++] = maxlen;
+			    maxlen = 0;
+			    nth = ++first;
+			    if (llines > maxlines)
+				maxlines = llines;
+			    llines = 0;
+			}
 		    }
-		    nlist++;
-		} else if (!(m->flags & CMF_NOLIST)) {
-		    l = niceztrlen(m->str);
-		    ndisp++;
-		    if (l > glong)
-			glong = l;
-		    if (l < gshort)
-			gshort = l;
-		    totl += l;
-		    mlens[m->gnum] = l;
-		    nlist++;
-		} else
-		    hidden = 1;
-	    }
-	}
-	if ((e = g->expls)) {
-	    while (*e) {
-		if ((*e)->count)
-		    nlines += 1 + printfmt((*e)->str, (*e)->count, 0, 1);
-		e++;
-	    }
-	}
-	g->totl = totl + (ndisp * add);
-	g->dcount = ndisp;
-	g->width = glong + add;
-	g->shortest = gshort + add;
-	if ((g->cols = columns / g->width) > g->dcount)
-	    g->cols = g->dcount;
-	if (g->cols) {
-	    i = g->cols * g->width - add;
-	    if (i > max)
-		max = i;
-	}
-    }
-    if (!onlyexpl) {
-	for (g = amatches; g; g = g->next) {
-	    char **pp;
-	    int glines = 0;
-
-	    zfree(g->widths, 0);
-	    g->widths = NULL;
-
-	    if ((pp = g->ylist)) {
-		if (!(g->flags & CGF_LINES)) {
-		    if (g->cols) {
-			glines += (arrlen(pp) + g->cols - 1) / g->cols;
-			if (g->cols > 1)
-			    g->width += (max - (g->width * g->cols - add)) / g->cols;
-		    } else {
-			g->cols = 1;
-			g->width = 1;
-			
-			while (*pp)
-			    glines += 1 + (strlen(*pp++) / columns);
+		    if (nth < num) {
+			widths[tcol++] = maxlen;
+			width += maxlen;
 		    }
+		    if (!count && width < columns)
+			break;
 		}
+		if (tcols > ncols)
+		    tlines = maxlines;
 	    } else {
-		if (g->cols) {
-		    glines += (g->dcount + g->cols - 1) / g->cols;
-		    if (g->cols > 1)
-			g->width += (max - (g->width * g->cols - add)) / g->cols;
-		} else if (!(g->flags & CGF_LINES)) {
-		    g->cols = 1;
-		    g->width = 0;
-		    
-		    for (p = g->matches; (m = *p); p++)
-			if (!(m->flags & CMF_HIDE)) {
-			    if (m->disp) {
-				if (!(m->flags & CMF_DISPLINE))
-				    glines += 1 + (mlens[m->gnum] / columns);
-			    } else if (!(m->flags & CMF_NOLIST))
-				glines += 1 + ((1 + mlens[m->gnum]) / columns);
-			}
-		}
-	    }
-	    g->lins = glines;
-	    nlines += glines;
-	}
-    }
-    if (!onlyexpl && isset(LISTPACKED)) {
-	char **pp;
-	int *ws, tlines, tline, tcols, maxlen, nth, width;
-
-	for (g = amatches; g; g = g->next) {
-	    ws = g->widths = (int *) zalloc(columns * sizeof(int));
-	    memset(ws, 0, columns * sizeof(int));
-	    tlines = g->lins;
-	    tcols = g->cols;
-	    width = 0;
-
-	    if ((pp = g->ylist)) {
-		if (!(g->flags & CGF_LINES)) {
-		    int yl = arrlen(pp), i;
-		    VARARR(int, ylens, yl);
-
-		    for (i = 0; *pp; i++, pp++)
-			ylens[i] = strlen(*pp) + add;
-
-		    if (isset(LISTROWSFIRST)) {
-			int count, tcol, first, maxlines = 0, llines;
-
-			for (tcols = columns / g->shortest; tcols > g->cols;
-			     tcols--) {
-			    for (nth = first = maxlen = width = maxlines =
-				     llines = tcol = 0,
-				     count = g->dcount;
-				 count > 0; count--) {
-				if (ylens[nth] > maxlen)
-				    maxlen = ylens[nth];
-				nth += tcols;
-				tlines++;
-				if (nth >= g->dcount) {
-				    if ((width += maxlen) >= columns)
-					break;
-				    ws[tcol++] = maxlen;
-				    maxlen = 0;
-				    nth = ++first;
-				    if (llines > maxlines)
-					maxlines = llines;
-				    llines = 0;
-				}
-			    }
-			    if (nth < yl) {
-				ws[tcol++] = maxlen;
-				width += maxlen;
-			    }
-			    if (!count && width < columns)
-				break;
-			}
-			if (tcols > g->cols)
-			    tlines = maxlines;
-		    } else {
-			for (tlines = ((g->totl + columns) / columns);
-			     tlines < g->lins; tlines++) {
-			    for (pp = g->ylist, nth = tline = width =
-				     maxlen = tcols = 0;
-				 *pp; nth++, pp++) {
-				if (ylens[nth] > maxlen)
-				    maxlen = ylens[nth];
-				if (++tline == tlines) {
-				    if ((width += maxlen) >= columns)
-					break;
-				    ws[tcols++] = maxlen;
-				    maxlen = tline = 0;
-				}
-			    }
-			    if (tline) {
-				ws[tcols++] = maxlen;
-				width += maxlen;
-			    }
-			    if (nth == yl && width < columns)
+		for (tlines = ((totl + columns) / columns);
+		     tlines < nlines; tlines++) {
+		    for (p = data, nth = tline = width =
+			     maxlen = tcols = 0;
+			 *p; nth++, p++) {
+			if (lens[nth] > maxlen)
+			    maxlen = lens[nth];
+			if (++tline == tlines) {
+			    if ((width += maxlen) >= columns)
 				break;
+			    widths[tcols++] = maxlen;
+			    maxlen = tline = 0;
 			}
 		    }
-		}
-	    } else if (g->width) {
-		if (isset(LISTROWSFIRST)) {
-		    int addlen, count, tcol, maxlines = 0, llines, i;
-		    Cmatch *first;
-
-		    for (tcols = columns / g->shortest; tcols > g->cols;
-			 tcols--) {
-			p = first = skipnolist(g->matches);
-			for (maxlen = width = maxlines = llines = tcol = 0,
-				 count = g->dcount;
-			     count > 0; count--) {
-			    m = *p;
-			    addlen = mlens[m->gnum] + add;
-			    if (addlen > maxlen)
-				maxlen = addlen;
-			    for (i = tcols; i && *p; i--)
-				p = skipnolist(p + 1);
-
-			    llines++;
-			    if (!*p) {
-				if (llines > maxlines)
-				    maxlines = llines;
-				llines = 0;
-
-				if ((width += maxlen) >= columns)
-				    break;
-				ws[tcol++] = maxlen;
-				maxlen = 0;
-
-				p = first = skipnolist(first + 1);
-			    }
-			}
-			if (tlines) {
-			    ws[tcol++] = maxlen;
-			    width += maxlen;
-			}
-			if (!count && width < columns)
-			    break;
-		    }
-		    if (tcols > g->cols)
-			tlines = maxlines;
-		} else {
-		    int addlen;
-
-		    for (tlines = ((g->totl + columns) / columns);
-			 tlines < g->lins; tlines++) {
-			for (p = g->matches, nth = tline = width =
-				 maxlen = tcols = 0;
-			     (m = *p); p++, nth++) {
-			    if (!(m->flags &
-				  (m->disp ? (CMF_DISPLINE | CMF_HIDE) :
-				   (CMF_NOLIST | CMF_HIDE)))) {
-				addlen = mlens[m->gnum] + add;
-				if (addlen > maxlen)
-				    maxlen = addlen;
-				if (++tline == tlines) {
-				    if ((width += maxlen) >= columns)
-					break;
-				    ws[tcols++] = maxlen;
-				    maxlen = tline = 0;
-				}
-			    }
-			}
-			if (tline) {
-			    ws[tcols++] = maxlen;
-			    width += maxlen;
-			}
-			if (nth == g->dcount && width < columns)
-			    break;
+		    if (tline) {
+			widths[tcols++] = maxlen;
+			width += maxlen;
 		    }
+		    if (nth == num && width < columns)
+			break;
 		}
 	    }
-	    if (tlines == g->lins) {
-		zfree(ws, columns * sizeof(int));
-		g->widths = NULL;
-	    } else {
-		nlines += tlines - g->lins;
-		g->lins = tlines;
-		g->cols = tcols;
-		g->totl = width;
-		width -= add;
-		if (width > max)
-		    max = width;
+	    if ((pack = (tlines < nlines))) {
+		nlines = tlines;
+		ncols = tcols;
 	    }
 	}
-	for (g = amatches; g; g = g->next) {
-	    if (g->widths) {
-		int *p, a = (max - g->totl + add) / g->cols;
-
-		for (i = g->cols, p = g->widths; i; i--, p++)
-		    *p += a;
-	    } else if (g->width && g->cols > 1)
-		g->width += (max - (g->width * g->cols - add)) / g->cols;
-	}
+    } else {
+	nlines = 0;
+	for (p = data; *p; p++)
+	    nlines += 1 + (strlen(*p) / columns);
     }
-    listdat.valid = 1;
-    listdat.hidden = hidden;
-    listdat.nlist = nlist;
-    listdat.nlines = nlines;
-    listdat.menuacc = menuacc;
-    listdat.onlyexpl = onlyexpl;
-    listdat.columns = columns;
-    listdat.lines = lines;
-}
-
-/**/
-int asklist(void)
-{
     /* Set the cursor below the prompt. */
     trashzle();
-    showinglist = listshown = 0;
 
-    clearflag = (isset(USEZLE) && !termflags &&
-		 complastprompt && *complastprompt);
+    tolast = ((zmult == 1) == !!isset(ALWAYSLASTPROMPT));
+    clearflag = (isset(USEZLE) && !termflags && tolast);
 
-    /* Maybe we have to ask if the user wants to see the list. */
-    if ((!minfo.cur || !minfo.asked) &&
-	((complistmax && listdat.nlist > complistmax) ||
-	 (!complistmax && listdat.nlines >= lines))) {
+    max = getiparam("LISTMAX");
+    if ((max && num > max) || (!max && nlines > lines)) {
 	int qup;
+
 	zsetterm();
 	qup = printfmt("zsh: do you wish to see all %n possibilities? ",
-		       listdat.nlist, 1, 1);
+		       num, 1, 1);
 	fflush(shout);
 	if (getzlequery() != 'y') {
 	    if (clearflag) {
@@ -7530,8 +1865,6 @@ int asklist(void)
 		tcmultout(TCUP, TCMULTUP, nlnct);
 	    } else
 		putc('\n', shout);
-	    if (minfo.cur)
-		minfo.asked = 2;
 	    return 1;
 	}
 	if (clearflag) {
@@ -7542,304 +1875,60 @@ int asklist(void)
 	} else
 	    putc('\n', shout);
 	settyinfo(&shttyinfo);
-	if (minfo.cur)
-	    minfo.asked = 1;
     }
-    return 0;
-}
-
-/**/
-int
-printlist(int over, CLPrintFunc printm)
-{
-    Cmgroup g;
-    Cmatch *p, m;
-    Cexpl *e;
-    int pnl = 0, cl = (over ? listdat.nlines : -1);
-    int mc = 0, ml = 0, printed = 0;
-
-    if (cl < 2) {
-	cl = -1;
-	if (tccan(TCCLEAREOD))
-	    tcout(TCCLEAREOD);
-    }
-    g = amatches;
-    while (g) {
-	char **pp = g->ylist;
-
-	if ((e = g->expls)) {
-	    int l;
-
-	    while (*e) {
-		if ((*e)->count) {
-		    if (pnl) {
+    if (ncols) {
+	if (isset(LISTROWSFIRST)) {
+	    for (col = 1, p = data, lenp = lens; *p;
+		 p++, lenp++, col++) {
+		nicezputs(*p, shout);
+		if (col == ncols) {
+		    col = 0;
+		    if (p[1])
 			putc('\n', shout);
-			pnl = 0;
-			ml++;
-			if (cl >= 0 && --cl <= 1) {
-			    cl = -1;
-			    if (tccan(TCCLEAREOD))
-				tcout(TCCLEAREOD);
-			}
-		    }
-		    l = printfmt((*e)->str, (*e)->count, 1, 1);
-		    ml += l;
-		    if (cl >= 0 && (cl -= l) <= 1) {
-			cl = -1;
-			if (tccan(TCCLEAREOD))
-			    tcout(TCCLEAREOD);
-		    }
-		    pnl = 1;
-		}
-		e++;
-	    }
-	}
-	if (!listdat.onlyexpl && pp && *pp) {
-	    if (pnl) {
-		putc('\n', shout);
-		pnl = 0;
-		ml++;
-		if (cl >= 0 && --cl <= 1) {
-		    cl = -1;
-		    if (tccan(TCCLEAREOD))
-			tcout(TCCLEAREOD);
-		}
-	    }
-	    if (g->flags & CGF_LINES) {
-		while (*pp) {
-		    zputs(*pp, shout);
-		    if (*++pp)
-			putc('\n', shout);
-		}
-	    } else {
-		int n = g->lcount, nl, nc, i, a;
-		char **pq;
-
-		nl = nc = g->lins;
-
-		while (n && nl--) {
-		    i = g->cols;
-		    mc = 0;
-		    pq = pp;
-		    while (n && i--) {
-			if (pq - g->ylist >= g->lcount)
-			    break;
-			zputs(*pq, shout);
-			if (i) {
-			    a = (g->widths ? g->widths[mc] : g->width) -
-				strlen(*pq);
-			    while (a--)
-				putc(' ', shout);
-			}
-			pq += (isset(LISTROWSFIRST) ? 1 : nc);
-			mc++;
-			n--;
-		    }
-		    if (n) {
-			putc('\n', shout);
-			ml++;
-			if (cl >= 0 && --cl <= 1) {
-			    cl = -1;
-			    if (tccan(TCCLEAREOD))
-				tcout(TCCLEAREOD);
-			}
-		    }
-		    pp += (isset(LISTROWSFIRST) ? g->cols : 1);
-		}
-	    }
-	} else if (!listdat.onlyexpl && g->lcount) {
-	    int n = g->dcount, nl, nc, i, j, wid;
-	    Cmatch *q;
-
-	    nl = nc = g->lins;
-
-	    if (g->flags & CGF_HASDL) {
-		for (p = g->matches; (m = *p); p++)
-		    if (m->disp && (m->flags & CMF_DISPLINE)) {
-			if (pnl) {
-			    putc('\n', shout);
-			    pnl = 0;
-			    ml++;
-			    if (cl >= 0 && --cl <= 1) {
-				cl = -1;
-				if (tccan(TCCLEAREOD))
-				    tcout(TCCLEAREOD);
-			    }
-			}
-			printed++;
-			printm(g, p, 0, ml, 1, 0, NULL, NULL);
-			pnl = 1;
-		    }
-	    }
-	    if (n && pnl) {
-		putc('\n', shout);
-		pnl = 0;
-		ml++;
-		if (cl >= 0 && --cl <= 1) {
-		    cl = -1;
-		    if (tccan(TCCLEAREOD))
-			tcout(TCCLEAREOD);
+		} else {
+		    if ((i = (pack ? widths[col - 1] : longest) - *lenp + 2) > 0)
+			while (i--)
+			    putc(' ', shout);
 		}
 	    }
-	    for (p = skipnolist(g->matches); n && nl--;) {
-		i = g->cols;
-		mc = 0;
-		q = p;
-		while (n && i--) {
-		    wid = (g->widths ? g->widths[mc] : g->width);
-		    if (!(m = *q)) {
-			printm(g, NULL, mc, ml, (!i), wid, NULL, NULL);
+	} else {
+	    char **f;
+	    int *fl, line;
+
+	    for (f = data, fl = lens, line = 0; line < nlines;
+		 f++, fl++, line++) {
+		for (col = 1, p = f, lenp = fl; *p; col++) {
+		    nicezputs(*p, shout);
+		    if (col == ncols)
 			break;
-		    }
-		    if (!m->disp && (m->flags & CMF_FILE)) {
-			struct stat buf;
-			char *pb;
-
-			pb = (char *) zhalloc((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))
-			    printm(g, q, mc, ml, (!i), wid, NULL, NULL);
-			else
-			    printm(g, q, mc, ml, (!i), wid, pb, &buf);
-		    } else
-			printm(g, q, mc, ml, (!i), wid, NULL, NULL);
-
-		    printed++;
-
-		    if (--n)
-			for (j = (isset(LISTROWSFIRST) ? 1 : nc); j && *q; j--)
-			    q = skipnolist(q + 1);
-		    mc++;
+		    if ((i = (pack ? widths[col - 1] : longest) - *lenp + 2) > 0)
+			while (i--)
+			    putc(' ', shout);
+		    for (i = nlines; i && *p; i--, p++, lenp++);
 		}
-		while (i-- > 0)
-		    printm(g, NULL, mc++, ml, (!i),
-			   (g->widths ? g->widths[mc] : g->width), NULL, NULL);
-
-		if (n) {
+		if (line + 1 < nlines)
 		    putc('\n', shout);
-		    ml++;
-		    if (cl >= 0 && --cl <= 1) {
-			cl = -1;
-			if (tccan(TCCLEAREOD))
-			    tcout(TCCLEAREOD);
-		    }
-		    if (nl)
-			for (j = (isset(LISTROWSFIRST) ? g->cols : 1); j && *p; j--)
-			    p = skipnolist(p + 1);
-		}
 	    }
 	}
-	if (g->lcount)
-	    pnl = 1;
-	g = g->next;
+    } else {
+	for (p = data; *p; p++) {
+	    nicezputs(*p, shout);
+	    putc('\n', shout);
+	}
     }
     if (clearflag) {
-	/* Move the cursor up to the prompt, if always_last_prompt *
-	 * is set and all that...                                  */
-	if ((ml = listdat.nlines + nlnct - 1) < lines) {
-	    tcmultout(TCUP, TCMULTUP, ml);
+	if ((nlines += nlnct - 1) < lines) {
+	    tcmultout(TCUP, TCMULTUP, nlines);
 	    showinglist = -1;
 	} else
 	    clearflag = 0, putc('\n', shout);
     } else
 	putc('\n', shout);
-    listshown = (clearflag ? 1 : -1);
-
-    return printed;
-}
-
-static void
-iprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width,
-	char *path, struct stat *buf)
-{
-    Cmatch m;
-    int len = 0;
-
-    if (!mp)
-	return;
-
-    m = *mp;
-    if (m->disp) {
-	if (m->flags & CMF_DISPLINE) {
-	    printfmt(m->disp, 0, 1, 0);
-	    return;
-	}
-	nicezputs(m->disp, shout);
-	len = niceztrlen(m->disp);
-    } else {
-	nicezputs(m->str, shout);
-	len = niceztrlen(m->str);
-
-	if (isset(LISTTYPES)) {
-	    if (buf)
-		putc(file_type(buf->st_mode), shout);
-	    len++;
-	}
-    }
-    if (!lastc) {
-	len = width - len;
-
-	while (len-- > 0)
-	    putc(' ', shout);
-    }
-}
-
-/**/
-int
-ilistmatches(Hookdef dummy, Chdata dat)
-{
-    calclist();
-
-    if (!listdat.nlines) {
-	showinglist = listshown = 0;
-	return 1;
-    }
-    if (asklist())
-	return 0;
-
-    printlist(0, iprintm);
-
-    return 0;
-}
-
-/* This is used to print expansions. */
-
-/**/
-int
-listlist(LinkList l)
-{
-    struct cmgroup dg;
-    int vl = validlist, sm = smatches, nm = nmatches;
-    char *oclp = complastprompt;
-    Cmgroup am = amatches;
-    struct cldata odat;
-
-    memcpy(&odat, &listdat, sizeof(struct cldata));
 
     if (listshown)
 	showagain = 1;
 
-    complastprompt = ((zmult == 1) == !!isset(ALWAYSLASTPROMPT) ? "yes" : NULL);
-    smatches = 1;
-    validlist = 1;
-    memset(&dg, 0, sizeof(struct cmgroup));
-    dg.ylist = (char **) makearray(l, 0, 1, &(dg.lcount), NULL, NULL);
-    nmatches = dg.lcount;
-    amatches = &dg;
-    listdat.valid = 0;
-    ilistmatches(NULL, NULL);
-    amatches = am;
-
-    validlist = vl;
-    smatches = sm;
-    nmatches = nm;
-    complastprompt = oclp;
-    memcpy(&listdat, &odat, sizeof(struct cldata));
-
-    return !dg.lcount;
+    return !num;
 }
 
 /* Expand the history references. */