about summary refs log tree commit diff
path: root/Src/Zle
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Zle')
-rw-r--r--Src/Zle/comp.h449
-rw-r--r--Src/Zle/compcore.c1500
-rw-r--r--Src/Zle/complete.c436
-rw-r--r--Src/Zle/compmatch.c124
-rw-r--r--Src/Zle/compresult.c515
-rw-r--r--Src/Zle/computil.c1498
-rw-r--r--Src/Zle/iwidgets.list43
-rw-r--r--Src/Zle/zle_utils.c92
8 files changed, 2949 insertions, 1708 deletions
diff --git a/Src/Zle/comp.h b/Src/Zle/comp.h
index 5d7ef74e2..f513d4a5a 100644
--- a/Src/Zle/comp.h
+++ b/Src/Zle/comp.h
@@ -27,111 +27,362 @@
  *
  */
 
-#undef compctlread
+typedef struct cmatcher  *Cmatcher;
+typedef struct cmlist    *Cmlist;
+typedef struct cpattern  *Cpattern;
+typedef struct menuinfo  *Menuinfo;
+typedef struct cexpl *Cexpl;
+typedef struct cmgroup *Cmgroup;
+typedef struct cmatch *Cmatch;
 
-typedef struct compctlp  *Compctlp;
-typedef struct compctl   *Compctl;
-typedef struct compcond  *Compcond;
+/* This is for explantion strings. */
 
-/* node for compctl hash table (compctltab) */
+struct cexpl {
+    char *str;			/* the string */
+    int count;			/* the number of matches */
+    int fcount;			/* number of matches with fignore ignored */
+};
+
+/* This describes a group of matches. */
+
+struct cmgroup {
+    char *name;			/* the name of this group */
+    Cmgroup prev;		/* previous on the list */
+    Cmgroup next;		/* next one in list */
+    int flags;			/* see CGF_* below */
+    int mcount;			/* number of matches */
+    Cmatch *matches;		/* the matches */
+    int lcount;			/* number of things to list here */
+    int llcount;		/* number of line-displays */
+    char **ylist;		/* things to list */
+    int ecount;			/* number of explanation string */
+    Cexpl *expls;		/* explanation strings */
+    int ccount;			/* number of compctls used */
+    LinkList lexpls;		/* list of explanation string while building */
+    LinkList lmatches;		/* list of matches */
+    LinkList lfmatches;		/* list of matches without fignore */
+    LinkList lallccs;		/* list of used compctls */
+    int num;			/* number of this group */
+    int nbrbeg;			/* number of opened braces */
+    int nbrend;			/* number of closed braces */
+    /* The following is collected/used during listing. */
+    int dcount;			/* number of matches to list in columns */
+    int cols;			/* number of columns */
+    int lins;			/* number of lines */
+    int width;			/* column width */
+    int *widths;		/* column widths for listpacked */
+    int totl;			/* total length */
+    int shortest;		/* length of shortest match */
+    Cmgroup perm;		/* perm. alloced version of this group */
+    int new;			/* new matches since last permalloc() */
+};
+
+
+#define CGF_NOSORT   1		/* don't sort this group */
+#define CGF_LINES    2		/* these are to be printed on different lines */
+#define CGF_HASDL    4		/* has display strings printed on separate lines */
+#define CGF_UNIQALL  8		/* remove all duplicates */
+#define CGF_UNIQCON 16		/* remove consecutive duplicates */
+#define CGF_PACKED  32		/* LIST_PACKED for this group */
+#define CGF_ROWS    64		/* LIST_ROWS_FIRST for this group */
+
+/* This is the struct used to hold matches. */
+
+struct cmatch {
+    char *str;			/* the match itself */
+    char *ipre;			/* ignored prefix, has to be re-inserted */
+    char *ripre;		/* ignored prefix, unquoted */
+    char *isuf;			/* ignored suffix */
+    char *ppre;			/* the path prefix */
+    char *psuf;			/* the path suffix */
+    char *prpre;		/* path prefix for opendir */
+    char *pre;			/* prefix string from -P */
+    char *suf;			/* suffix string from -S */
+    char *disp;			/* string to display (compadd -d) */
+    char *autoq;		/* closing quote to add automatically */
+    int flags;			/* see CMF_* below */
+    int *brpl;			/* places where to put the brace prefixes */
+    int *brsl;			/* ...and the suffixes */
+    char *rems;			/* when to remove the suffix */
+    char *remf;			/* shell function to call for suffix-removal */
+    int qipl;			/* length of quote-prefix */
+    int qisl;			/* length of quote-suffix */
+    int rnum;			/* group relative number */
+    int gnum;			/* global number */
+};
+
+#define CMF_FILE     (1<< 0)	/* this is a file */
+#define CMF_REMOVE   (1<< 1)	/* remove the suffix */
+#define CMF_ISPAR    (1<< 2)	/* is paramter expansion */
+#define CMF_PARBR    (1<< 3)	/* paramter expansion with a brace */
+#define CMF_PARNEST  (1<< 4)	/* nested paramter expansion */
+#define CMF_NOLIST   (1<< 5)	/* should not be listed */
+#define CMF_DISPLINE (1<< 6)	/* display strings one per line */
+#define CMF_HIDE     (1<< 7)	/* temporarily hide this one */
+#define CMF_NOSPACE  (1<< 8)	/* don't add a space */
+#define CMF_PACKED   (1<< 9)	/* prefer LIST_PACKED */
+#define CMF_ROWS     (1<<10)	/* prefer LIST_ROWS_FIRST */
+#define CMF_MULT     (1<<11)	/* string appears more than once */
+#define CMF_FMULT    (1<<12)	/* first of multiple equal strings */
+
+/* Stuff for completion matcher control. */
+
+struct cmlist {
+    Cmlist next;		/* next one in the list of global matchers */
+    Cmatcher matcher;		/* the matcher definition */
+    char *str;			/* the string for it */
+};
+
+struct cmatcher {
+    int refc;			/* reference counter */
+    Cmatcher next;		/* next matcher */
+    int flags;			/* see CMF_* below */
+    Cpattern line;		/* what matches on the line */
+    int llen;			/* length of line pattern */
+    Cpattern word;		/* what matches in the word */
+    int wlen;			/* length of word pattern */
+    Cpattern left;		/* left anchor */
+    int lalen;			/* length of left anchor */
+    Cpattern right;		/* right anchor */
+    int ralen;			/* length of right anchor */
+};
+
+#define CMF_LINE  1
+#define CMF_LEFT  2
+#define CMF_RIGHT 4
+
+struct cpattern {
+    Cpattern next;		/* next sub-pattern */
+    unsigned char tab[256];	/* table of matched characters */
+    int equiv;			/* if this is a {...} class */
+};
+
+/* This is a special return value for parse_cmatcher(), *
+ * signalling an error. */
+
+#define pcm_err ((Cmatcher) 1)
+
+/* Information about what to put on the line as the unambiguous string.
+ * The code always keeps lists of these structs up to date while
+ * matches are added (in the aminfo structs below).
+ * The lists have two levels: in the first one we have one struct per
+ * word-part, where parts are separated by the anchors of `*' patterns.
+ * These structs have pointers (in the prefix and suffix fields) to
+ * lists of cline structs describing the strings before or after the
+ * the anchor. */
+
+typedef struct cline *Cline;
+typedef struct clsub Clsub;
+
+struct cline {
+    Cline next;
+    int flags;
+    char *line;
+    int llen;
+    char *word;
+    int wlen;
+    char *orig;
+    int olen;
+    int slen;
+    Cline prefix, suffix;
+    int min, max;
+};
+
+#define CLF_MISS      1
+#define CLF_DIFF      2
+#define CLF_SUF       4
+#define CLF_MID       8
+#define CLF_NEW      16
+#define CLF_LINE     32
+#define CLF_JOIN     64
+#define CLF_MATCHED 128
+
+/* Information for ambiguous completions. One for fignore ignored and   *
+ * one for normal completion. */
+
+typedef struct aminfo *Aminfo;
 
-struct compctlp {
-    HashNode next;		/* next in hash chain               */
-    char *nam;			/* command name                     */
-    int flags;			/* CURRENTLY UNUSED                 */
-    Compctl cc;			/* pointer to the compctl desc.     */
+struct aminfo {
+    Cmatch firstm;		/* the first match                        */
+    int exact;			/* if there was an exact match            */
+    Cmatch exactm;		/* the exact match (if any)               */
+    int count;			/* number of matches                      */
+    Cline line;			/* unambiguous line string                */
 };
 
-/* compctl -x condition */
-
-struct compcond {
-    Compcond and, or;		/* the next or'ed/and'ed conditions    */
-    int type;			/* the type (CCT_*)                    */
-    int n;			/* the array length                    */
-    union {			/* these structs hold the data used to */
-	struct {		/* test this condition                 */
-	    int *a, *b;		/* CCT_POS, CCT_NUMWORDS               */
-	}
-	r;
-	struct {		/* CCT_CURSTR, CCT_CURPAT,... */
-	    int *p;
-	    char **s;
-	}
-	s;
-	struct {		/* CCT_RANGESTR,... */
-	    char **a, **b;
-	}
-	l;
-    }
-    u;
+/* Information about menucompletion stuff. */
+
+struct menuinfo {
+    Cmgroup group;		/* position in the group list */
+    Cmatch *cur;		/* match currently inserted */
+    int pos;			/* begin on line */
+    int len;			/* length of inserted string */
+    int end;			/* end on the line */
+    int we;			/* non-zero if the cursor was at the end */
+    int insc;			/* length of suffix inserted */
+    int asked;			/* we asked if the list should be shown */
+    char *prebr;		/* prefix before a brace, if any */
+    char *postbr;		/* suffix after a brace */
+};
+
+/* Flags for compadd and addmatches(). */
+
+#define CAF_QUOTE    1
+#define CAF_NOSORT   2
+#define CAF_MATCH    4
+#define CAF_UNIQCON  8
+#define CAF_UNIQALL 16
+
+/* Data for compadd and addmatches() */
+
+typedef struct cadata *Cadata;
+
+struct cadata {
+    char *ipre;			/* ignored prefix (-i) */
+    char *isuf;			/* ignored suffix (-I) */
+    char *ppre;			/* `path' prefix (-p) */
+    char *psuf;			/* `path' suffix (-s) */
+    char *prpre;		/* expanded `path' prefix (-W) */
+    char *pre;			/* prefix to insert (-P) */
+    char *suf;			/* suffix to insert (-S) */
+    char *group;		/* name of the group (-[JV]) */
+    char *rems;			/* remove suffix on chars... (-r) */
+    char *remf;			/* function to remove suffix (-R) */
+    char *ign;			/* ignored suffixes (-F) */
+    int flags;			/* CMF_* flags (-[fqn]) */
+    int aflags;			/* CAF_* flags (-[QUa]) */
+    Cmatcher match;		/* match spec (parsed from -M) */
+    char *exp;			/* explanation (-X) */
+    char *apar;			/* array to store matches in (-A) */
+    char *opar;			/* array to store originals in (-O) */
+    char *dpar;			/* array to delete non-matches in (-D) */
+    char *disp;			/* array with display lists (-d) */
 };
 
-#define CCT_UNUSED     0
-#define CCT_POS        1
-#define CCT_CURSTR     2
-#define CCT_CURPAT     3
-#define CCT_WORDSTR    4
-#define CCT_WORDPAT    5
-#define CCT_CURSUF     6
-#define CCT_CURPRE     7
-#define CCT_CURSUB     8
-#define CCT_CURSUBC    9
-#define CCT_NUMWORDS  10
-#define CCT_RANGESTR  11
-#define CCT_RANGEPAT  12
-
-/* Contains the real description for compctls */
-
-struct compctl {
-    int refc;			/* reference count                         */
-    Compctl next;		/* next compctl for -x                     */
-    unsigned long mask;		/* mask of things to complete (CC_*)       */
-    char *keyvar;		/* for -k (variable)                       */
-    char *glob;			/* for -g (globbing)                       */
-    char *str;			/* for -s (expansion)                      */
-    char *func;			/* for -K (function)                       */
-    char *explain;		/* for -X (explanation)                    */
-    char *ylist;		/* for -y (user-defined desc. for listing) */
-    char *prefix, *suffix;	/* for -P and -S (prefix, suffix)          */
-    char *subcmd;		/* for -l (command name to use)            */
-    char *withd;		/* for -w (with directory                  */
-    char *hpat;			/* for -H (history pattern)                */
-    int hnum;			/* for -H (number of events to search)     */
-    Compctl ext;		/* for -x (first of the compctls after -x) */
-    Compcond cond;		/* for -x (condition for this compctl)     */
-    Compctl xor;		/* for + (next of the xor'ed compctls)     */
+/* List data. */
+
+typedef struct cldata *Cldata;
+
+struct cldata {
+    int columns;		/* screen width */
+    int lines;			/* screen height */
+    int menuacc;		/* value of global menuacc */
+    int valid;			/* no need to calculate anew */
+    int nlist;			/* number of matches to list */
+    int nlines;			/* number of lines needed */
+    int hidden;			/* != 0 if there are hidden matches */
+    int onlyexpl;		/* != 0 if only explanations to print */
+    int showall;		/* != 0 if hidden matches should be shown */
+};
+
+typedef void (*CLPrintFunc)(Cmgroup, Cmatch *, int, int, int, int,
+			    char *, struct stat *);
+
+/* Flags for fromcomp. */
+
+#define FC_LINE   1
+#define FC_INWORD 2
+
+/* Flags for special parameters. */
+
+#define CPN_WORDS      0
+#define CP_WORDS       (1 <<  CPN_WORDS)
+#define CPN_CURRENT    1
+#define CP_CURRENT     (1 <<  CPN_CURRENT)
+#define CPN_PREFIX     2
+#define CP_PREFIX      (1 <<  CPN_PREFIX)
+#define CPN_SUFFIX     3
+#define CP_SUFFIX      (1 <<  CPN_SUFFIX)
+#define CPN_IPREFIX    4
+#define CP_IPREFIX     (1 <<  CPN_IPREFIX)
+#define CPN_ISUFFIX    5
+#define CP_ISUFFIX     (1 <<  CPN_ISUFFIX)
+#define CPN_QIPREFIX   6
+#define CP_QIPREFIX    (1 <<  CPN_QIPREFIX)
+#define CPN_QISUFFIX   7
+#define CP_QISUFFIX    (1 <<  CPN_QISUFFIX)
+#define CPN_COMPSTATE  8
+#define CP_COMPSTATE   (1 <<  CPN_COMPSTATE)
+
+#define CP_REALPARAMS  9
+#define CP_ALLREALS    ((unsigned int) 0x1ff)
+
+
+#define CPN_NMATCHES   0
+#define CP_NMATCHES    (1 << CPN_NMATCHES)
+#define CPN_CONTEXT    1
+#define CP_CONTEXT     (1 << CPN_CONTEXT)
+#define CPN_PARAMETER  2
+#define CP_PARAMETER   (1 << CPN_PARAMETER)
+#define CPN_REDIRECT   3
+#define CP_REDIRECT    (1 << CPN_REDIRECT)
+#define CPN_QUOTE      4
+#define CP_QUOTE       (1 << CPN_QUOTE)
+#define CPN_QUOTING    5
+#define CP_QUOTING     (1 << CPN_QUOTING)
+#define CPN_RESTORE    6
+#define CP_RESTORE     (1 << CPN_RESTORE)
+#define CPN_LIST       7
+#define CP_LIST        (1 << CPN_LIST)
+#define CPN_INSERT     8
+#define CP_INSERT      (1 << CPN_INSERT)
+#define CPN_EXACT      9
+#define CP_EXACT       (1 << CPN_EXACT)
+#define CPN_EXACTSTR   10
+#define CP_EXACTSTR    (1 << CPN_EXACTSTR)
+#define CPN_PATMATCH   11
+#define CP_PATMATCH    (1 << CPN_PATMATCH)
+#define CPN_PATINSERT  12
+#define CP_PATINSERT   (1 << CPN_PATINSERT)
+#define CPN_UNAMBIG    13
+#define CP_UNAMBIG     (1 << CPN_UNAMBIG)
+#define CPN_UNAMBIGC   14
+#define CP_UNAMBIGC    (1 << CPN_UNAMBIGC)
+#define CPN_LISTMAX    15
+#define CP_LISTMAX     (1 << CPN_LISTMAX)
+#define CPN_LASTPROMPT 16
+#define CP_LASTPROMPT  (1 << CPN_LASTPROMPT)
+#define CPN_TOEND      17
+#define CP_TOEND       (1 << CPN_TOEND)
+#define CPN_OLDLIST    18
+#define CP_OLDLIST     (1 << CPN_OLDLIST)
+#define CPN_OLDINS     19
+#define CP_OLDINS      (1 << CPN_OLDINS)
+#define CPN_VARED      20
+#define CP_VARED       (1 << CPN_VARED)
+#define CPN_LISTLINES  21
+#define CP_LISTLINES   (1 << CPN_LISTLINES)
+#define CPN_QUOTES     22
+#define CP_QUOTES      (1 << CPN_QUOTES)
+#define CPN_IGNORED    23
+#define CP_IGNORED     (1 << CPN_IGNORED)
+
+#define CP_KEYPARAMS   24
+#define CP_ALLKEYS     ((unsigned int) 0xffffff)
+
+/* Hooks. */
+
+#define INSERTMATCHHOOK     (comphooks + 0)
+#define MENUSTARTHOOK       (comphooks + 1)
+#define COMPCTLMAKEHOOK     (comphooks + 2)
+#define COMPCTLCLEANUPHOOK  (comphooks + 3)
+#define COMPLISTMATCHESHOOK (comphooks + 4)
+
+/* 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 */
 };
 
-/* objects to complete */
-#define CC_FILES	(1<<0)
-#define CC_COMMPATH	(1<<1)
-#define CC_REMOVE	(1<<2)
-#define CC_OPTIONS	(1<<3)
-#define CC_VARS		(1<<4)
-#define CC_BINDINGS	(1<<5)
-#define CC_ARRAYS	(1<<6)
-#define CC_INTVARS	(1<<7)
-#define CC_SHFUNCS	(1<<8)
-#define CC_PARAMS	(1<<9)
-#define CC_ENVVARS	(1<<10)
-#define CC_JOBS		(1<<11)
-#define CC_RUNNING	(1<<12)
-#define CC_STOPPED	(1<<13)
-#define CC_BUILTINS	(1<<14)
-#define CC_ALREG	(1<<15)
-#define CC_ALGLOB	(1<<16)
-#define CC_USERS	(1<<17)
-#define CC_DISCMDS	(1<<18)
-#define CC_EXCMDS	(1<<19)
-#define CC_SCALARS	(1<<20)
-#define CC_READONLYS	(1<<21)
-#define CC_SPECIALS	(1<<22)
-#define CC_DELETE	(1<<23)
-#define CC_NAMED	(1<<24)
-#define CC_QUOTEFLAG	(1<<25)
-#define CC_EXTCMDS	(1<<26)
-#define CC_RESWDS	(1<<27)
-#define CC_DIRS		(1<<28)
-
-#define CC_EXPANDEXPL	(1<<30)
-#define CC_RESERVED	(1<<31)
diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c
index ea9ff5b20..3cc080cac 100644
--- a/Src/Zle/compcore.c
+++ b/Src/Zle/compcore.c
@@ -28,9 +28,6 @@
  */
 
 #include "complete.mdh"
-#define GLOBAL_PROTOTYPES
-#include "zle_tricky.pro"
-#undef GLOBAL_PROTOTYPES
 #include "compcore.pro"
 
 /* The last completion widget called. */
@@ -40,11 +37,17 @@ static Widget lastcompwidget;
 /* Flags saying what we have to do with the result. */
 
 /**/
-int useexact, useline, uselist;
+int useexact, useline, uselist, forcelist, startauto;
+
+/* Non-zero if we should go back to the last prompt. */
+
+/**/
+int dolastprompt;
 
 /* Non-zero if we should keep an old list. */
 
 /**/
+mod_export
 int oldlist, oldins;
 
 /* This is used to decide when the cursor should be moved to the end of    *
@@ -57,17 +60,17 @@ int movetoend;
 /* The match and group number to insert when starting menucompletion.   */
 
 /**/
-int insmnum, insgnum, insgroup, insspace;
+mod_export int insmnum, insgnum, insgroup, insspace;
 
 /* Information about menucompletion. */
 
 /**/
-struct menuinfo minfo;
+mod_export struct menuinfo minfo;
 
 /* Number of matches accepted with accept-and-menu-complete */
 
 /**/
-int menuacc;
+mod_export int menuacc;
 
 /* Brace insertion stuff. */
 
@@ -77,7 +80,7 @@ int hasunqu, useqbr, brpcs, brscs;
 /* Flags saying in what kind of string we are. */
 
 /**/
-int ispar, linwhat;
+mod_export int ispar, linwhat;
 
 /* A parameter expansion prefix (like ${). */
 
@@ -92,7 +95,7 @@ int parflags;
 /* Match flags for all matches in this group. */
 
 /**/
-int mflags;
+mod_export int mflags;
 
 /* Flags saying how the parameter expression we are in is quoted. */
 
@@ -101,17 +104,18 @@ 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                           */
+ * isuf        -- the ignored suffix                                       */
 
 /**/
-char *ipre, *ripre, *isuf;
+mod_export char *ipre, *ripre, *isuf;
 
 /* The list of matches.  fmatches contains the matches we first ignore *
  * because of fignore.                                                 */
 
 /**/
-LinkList matches, fmatches;
+mod_export LinkList matches;
+/**/
+LinkList fmatches;
 
 /* This holds the list of matches-groups. lastmatches holds the last list of 
  * permanently allocated matches, pmatches is the same for the list
@@ -120,47 +124,49 @@ LinkList matches, fmatches;
  * lmatches/lastlmatches is a pointer to the last element in the lists. */
 
 /**/
-Cmgroup lastmatches, pmatches, amatches, lmatches, lastlmatches;
+mod_export Cmgroup lastmatches, pmatches, amatches, lmatches, lastlmatches;
 
 /* Non-zero if we have permanently allocated matches (old and new). */
 
 /**/
-int hasoldlist, hasperm;
+mod_export int hasoldlist, hasperm;
 
 /* Non-zero if we have newly added matches. */
 
 /**/
-int newmatches;
+mod_export int newmatches;
 
 /* Number of permanently allocated matches and groups. */
 
 /**/
-int permmnum, permgnum, lastpermmnum, lastpermgnum;
+mod_export int permmnum, permgnum, lastpermmnum, lastpermgnum;
 
 /* The total number of matches and the number of matches to be listed. */
 
 /**/
-int nmatches, smatches;
+mod_export int nmatches;
+/**/
+mod_export int smatches;
 
 /* != 0 if only explanation strings should be printed */
 
 /**/
-int onlyexpl;
+mod_export int onlyexpl;
 
 /* Information about the matches for listing. */
 
 /**/
-struct cldata listdat;
+mod_export struct cldata listdat;
 
 /* This flag is non-zero if we are completing a pattern (with globcomplete) */
 
 /**/
-int ispattern, haspattern;
+mod_export int ispattern, haspattern;
 
-/* Non-zero if at least one match was added without -U. */
+/* Non-zero if at least one match was added without/with -U. */
 
 /**/
-int hasmatched;
+mod_export int hasmatched, hasunmatched;
 
 /* The current group of matches. */
 
@@ -170,12 +176,12 @@ Cmgroup mgroup;
 /* Match counter: all matches. */
 
 /**/
-int mnum;
+mod_export int mnum;
 
 /* The match counter when unambig_data() was called. */
 
 /**/
-int unambig_mnum;
+mod_export int unambig_mnum;
 
 /* Length of longest/shortest match. */
 
@@ -189,37 +195,37 @@ int maxmlen, minmlen;
 LinkList expls;
 
 /**/
-Cexpl curexpl;
+mod_export Cexpl curexpl;
 
 /* A stack of completion matchers to be used. */
 
 /**/
-Cmlist mstack;
+mod_export Cmlist mstack;
 
 /* The completion matchers used when building new stuff for the line. */
 
 /**/
-Cmlist bmatchers;
+mod_export Cmlist bmatchers;
 
 /* A list with references to all matchers we used. */
 
 /**/
-LinkList matchers;
+mod_export LinkList matchers;
 
 /* A heap of free Cline structures. */
 
 /**/
-Cline freecl;
+mod_export Cline freecl;
 
 /* Ambiguous information. */
 
 /**/
-Aminfo ainfo, fainfo;
+mod_export Aminfo ainfo, fainfo;
 
 /* The memory heap to use for new style completion generation. */
 
 /**/
-Heap compheap;
+mod_export Heap compheap;
 
 /* A list of some data.
  *
@@ -227,7 +233,7 @@ Heap compheap;
  * conceptually we don't know anything about compctls here... */
 
 /**/
-LinkList allccs;
+mod_export 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       *
@@ -246,10 +252,6 @@ int fromcomp;
 /**/
 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. */
@@ -260,153 +262,165 @@ do_completion(Hookdef dummy, Compldat dat)
 {
     int ret = 0, lst = dat->lst, incmd = dat->incmd;
     char *s = dat->s;
+    char *opm;
+    LinkNode n;
 
-    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;
+    pushheap();
+
+    ainfo = fainfo = NULL;
+    matchers = newlinklist();
+
+    zsfree(compqstack);
+    compqstack = ztrdup("\\");
+    if (instring == 2)
+	compqstack[0] = '"';
+    else if (instring)
+	compqstack[0] = '\'';
+
+    hasunqu = 0;
+    useline = (lst != COMP_LIST_COMPLETE);
+    useexact = isset(RECEXACT);
+    zsfree(compexactstr);
+    compexactstr = ztrdup("");
+    uselist = (useline ?
+	       ((isset(AUTOLIST) && !isset(BASHAUTOLIST)) ? 
+		(isset(LISTAMBIGUOUS) ? 3 : 2) : 0) : 1);
+    zsfree(comppatmatch);
+    opm = comppatmatch = ztrdup(useglob ? "*" : "");
+    zsfree(comppatinsert);
+    comppatinsert = ztrdup("menu");
+    forcelist = 0;
+    haspattern = 0;
+    complistmax = getiparam("LISTMAX");
+    zsfree(complastprompt);
+    complastprompt = ztrdup(((isset(ALWAYSLASTPROMPT) && zmult == 1) ||
+			     (unset(ALWAYSLASTPROMPT) && zmult != 1)) ?
+			    "yes" : "");
+    dolastprompt = 1;
+    zsfree(complist);
+    complist = ztrdup(isset(LISTROWSFIRST) ?
+		      (isset(LISTPACKED) ? "packed rows" : "rows") :
+		      (isset(LISTPACKED) ? "packed" : ""));
+    startauto = isset(AUTOMENU);
+    movetoend = ((cs == we || isset(ALWAYSTOEND)) ? 2 : 1);
+    showinglist = 0;
+    hasmatched = hasunmatched = 0;
+    minmlen = 1000000;
+    maxmlen = -1;
+    compignored = 0;
+
+    /* 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;
+	menucmp = 1;
+	menuacc = 0;
 
-	    for (minfo.group = amatches;
-		 minfo.group && !(minfo.group)->mcount;
-		 minfo.group = (minfo.group)->next);
+	for (minfo.group = amatches;
+	     minfo.group && !(minfo.group)->mcount;
+	     minfo.group = (minfo.group)->next);
 
-	    mc = (minfo.group)->matches;
+	mc = (minfo.group)->matches;
 
-	    while (1) {
-		if (!first)
-		    acceptlast();
-		first = 0;
+	while (1) {
+	    if (!first)
+		accept_last();
+	    first = 0;
 
-		if (!--nm)
-		    menucmp = 0;
+	    if (!--nm)
+		menucmp = 0;
 
-		do_single(*mc);
-		minfo.cur = mc;
+	    do_single(*mc);
+	    minfo.cur = mc;
 
-		if (!*++(minfo.cur)) {
-		    do {
-			if (!(minfo.group = (minfo.group)->next))
-			    break;
-		    } while (!(minfo.group)->mcount);
-		    if (!minfo.group)
+	    if (!*++(minfo.cur)) {
+		do {
+		    if (!(minfo.group = (minfo.group)->next))
 			break;
-		    minfo.cur = minfo.group->matches;
-		}
-		mc = minfo.cur;
+		} while (!(minfo.group)->mcount);
+		if (!minfo.group)
+		    break;
+		minfo.cur = minfo.group->matches;
 	    }
-	    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;
+	    mc = minfo.cur;
 	}
-	/* Print the explanation strings if needed. */
-	if (!showinglist && validlist && usemenu != 2 && nmatches != 1 &&
-	    useline != 2 && (!oldlist || !listshown)) {
-	    onlyexpl = 1;
+	menucmp = 0;
+	minfo.cur = NULL;
+
+	if (forcelist)
 	    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 (forcelist) {
+		if (uselist)
+		    showinglist = -2;
+		else
+		    clearlist = 1;
+	    } else
+		invalidatelist();
 	}
-      compend:
-	for (n = firstnode(matchers); n; incnode(n))
-	    freecmatcher((Cmatcher) getdata(n));
+    } else {
+	invalidatelist();
+	if (forcelist)
+	    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;
+    ll = strlen((char *)line);
+    if (cs > ll)
+	cs = ll;
+    popheap();
 
     return ret;
 }
@@ -428,7 +442,7 @@ before_complete(Hookdef dummy, int *lst)
     /* If we are doing a menu-completion... */
 
     if (menucmp && *lst != COMP_LIST_EXPAND && 
-	(!compwidget || compwidget == lastcompwidget)) {
+	(menucmp != 1 || !compwidget || compwidget == lastcompwidget)) {
 	do_menucmp(*lst);
 	return 1;
     }
@@ -442,13 +456,12 @@ before_complete(Hookdef dummy, int *lst)
     /* 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;
+    if ((fromcomp & FC_INWORD) && (cs = lastend) > ll)
+	cs = ll;
 
     /* Check if we have to start a menu-completion (via automenu). */
 
-    if (isset(AUTOMENU) && lastambig &&
+    if (startauto && lastambig &&
 	(!isset(BASHAUTOLIST) || lastambig == 2))
 	usemenu = 2;
 
@@ -477,11 +490,11 @@ after_complete(Hookdef dummy, Compldat dat)
 static void
 callcompfunc(char *s, char *fn)
 {
-    List list;
+    Eprog prog;
     int lv = lastval;
     char buf[20];
 
-    if ((list = getshfunc(fn)) != &dummy_list) {
+    if ((prog = getshfunc(fn)) != &dummy_eprog) {
 	char **p, *tmp;
 	int aadd = 0, usea = 1, icf = incompfunc, osc = sfcontext;
 	unsigned int rset, kset;
@@ -493,7 +506,7 @@ callcompfunc(char *s, char *fn)
 	rset = CP_ALLREALS;
 	kset = CP_ALLKEYS &
 	    ~(CP_PARAMETER | CP_REDIRECT | CP_QUOTE | CP_QUOTING |
-	      CP_EXACTSTR | CP_FORCELIST | CP_OLDLIST | CP_OLDINS |
+	      CP_EXACTSTR | CP_OLDLIST | CP_OLDINS |
 	      (useglob ? 0 : CP_PATMATCH));
 	zsfree(compvared);
 	if (varedarg) {
@@ -566,16 +579,11 @@ callcompfunc(char *s, char *fn)
 	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;
+	    q = compwords = (char **)
+		zalloc((clwnum + 1) * sizeof(char *));
+	    for (p = clwords + aadd; *p; p++, q++)
+		untokenize(*q = ztrdup(*p));
+	    *q = NULL;
 	} else
 	    compwords = (char **) zcalloc(sizeof(char *));
 
@@ -603,7 +611,7 @@ callcompfunc(char *s, char *fn)
 	zsfree(compprefix);
 	zsfree(compsuffix);
 	if (unset(COMPLETEINWORD)) {
-	    tmp = quotename(s, NULL);
+	    tmp = multiquote(s, 0);
 	    untokenize(tmp);
 	    compprefix = ztrdup(tmp);
 	    compsuffix = ztrdup("");
@@ -614,11 +622,11 @@ callcompfunc(char *s, char *fn)
 
 	    sav = *ss;
 	    *ss = '\0';
-	    tmp = quotename(s, NULL);
+	    tmp = multiquote(s, 0);
 	    untokenize(tmp);
 	    compprefix = ztrdup(tmp);
 	    *ss = sav;
-	    ss = quotename(ss, NULL);
+	    ss = multiquote(ss, 0);
 	    untokenize(ss);
 	    compsuffix = ztrdup(ss);
 	}
@@ -639,11 +647,20 @@ callcompfunc(char *s, char *fn)
 	case 2: complist = "autolist"; break;
 	case 3: complist = "ambiguous"; break;
 	}
+	if (isset(LISTPACKED))
+	    complist = dyncat(complist, " packed");
+	if (isset(LISTROWSFIRST))
+	    complist = dyncat(complist, " rows");
+
 	complist = ztrdup(complist);
 	zsfree(compinsert);
 	if (useline) {
 	    switch (usemenu) {
-	    case 0: compinsert = "unambiguous"; break;
+	    case 0:
+		compinsert = (isset(AUTOMENU) ?
+			      "automenu-unambiguous" :
+			      "unambiguous");
+		break;
 	    case 1: compinsert = "menu"; break;
 	    case 2: compinsert = "automenu"; break;
 	    }
@@ -701,7 +718,7 @@ callcompfunc(char *s, char *fn)
 		while (*p)
 		    addlinknode(largs, dupstring(*p++));
 	    }
-	    doshfunc(fn, list, largs, 0, 0);
+	    doshfunc(fn, prog, largs, 0, 0);
 	    cfret = lastval;
 	    lastval = olv;
 	} OLDHEAPS;
@@ -720,13 +737,14 @@ callcompfunc(char *s, char *fn)
 	    uselist = 3;
 	else
 	    uselist = 0;
-
+	forcelist = (complist && strstr(complist, "force"));
 	onlyexpl = (complist && strstr(complist, "expl"));
 
 	if (!compinsert)
 	    useline = 0;
 	else if (!strcmp(compinsert, "unambig") ||
-		 !strcmp(compinsert, "unambiguous"))
+		 !strcmp(compinsert, "unambiguous") ||
+		 !strcmp(compinsert, "automenu-unambiguous"))
 	    useline = 1, usemenu = 0;
 	else if (!strcmp(compinsert, "menu"))
 	    useline = 1, usemenu = 1;
@@ -747,6 +765,8 @@ callcompfunc(char *s, char *fn)
 	    insspace = (compinsert[strlen(compinsert) - 1] == ' ');
 	} else
 	    useline = usemenu = 0;
+	startauto = (compinsert &&
+		     !strcmp(compinsert, "automenu-unambiguous"));
 	useexact = (compexact && !strcmp(compexact, "accept"));
 
 	if (!comptoend || !*comptoend)
@@ -782,60 +802,21 @@ callcompfunc(char *s, char *fn)
 static int
 makecomplist(char *s, int incmd, int lst)
 {
-    struct cmlist ms;
-    Cmlist m;
-    char *p, *os = s;
-    int onm = nmatches, osi = movefd(0);
+    char *p;
 
     /* 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;
+	s = p;
 
     linwhat = inwhat;
 
-    /* Walk through the global matchers. */
-    for (;;) {
+    if (compfunc) {
+	char *os = s;
+	int onm = nmatches, osi = movefd(0);
+
 	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("");
-	}
+	mstack = NULL;
+
 	ainfo = (Aminfo) hcalloc(sizeof(struct aminfo));
 	fainfo = (Aminfo) hcalloc(sizeof(struct aminfo));
 
@@ -852,23 +833,12 @@ makecomplist(char *s, int incmd, int lst)
 	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);
-	}
+	callcompfunc(s, compfunc);
 	endcmgroup(NULL);
 
-	runhookdef(COMPCTLAFTERHOOK,
-		   (void *) ((amatches && !oldlist) ? 1L : 0L));
+	/* Needed for compcall. */
+	runhookdef(COMPCTLCLEANUPHOOK, NULL);
 
 	if (oldlist) {
 	    nmatches = onm;
@@ -884,16 +854,14 @@ makecomplist(char *s, int incmd, int lst)
 
 	    return 0;
 	}
-	PERMALLOC {
-	    if (lastmatches) {
-		freematches(lastmatches);
-		lastmatches = NULL;
-	    }
-	    permmatches(1);
-	    amatches = pmatches;
-	    lastpermmnum = permmnum;
-	    lastpermgnum = permgnum;
-	} LASTALLOC;
+	if (lastmatches) {
+	    freematches(lastmatches);
+	    lastmatches = NULL;
+	}
+	permmatches(1);
+	amatches = pmatches;
+	lastpermmnum = permmnum;
+	lastpermgnum = permgnum;
 
 	lastmatches = pmatches;
 	lastlmatches = lmatches;
@@ -908,20 +876,69 @@ makecomplist(char *s, int incmd, int lst)
 
 	    return 0;
 	}
-	if (!m || !(m = m->next))
-	    break;
+	redup(osi, 0);
+	return 1;
+    } else {
+	struct ccmakedat dat;
+
+	dat.str = s;
+	dat.incmd = incmd;
+	dat.lst = lst;
+	runhookdef(COMPCTLMAKEHOOK, (void *) &dat);
+
+	/* Needed for compcall. */
+	runhookdef(COMPCTLCLEANUPHOOK, NULL);
+
+	return dat.lst;
+    }
+}
+
+/**/
+mod_export char *
+multiquote(char *s, int ign)
+{
+    if (s) {
+	char *os = s, *p = compqstack;
+
+	if (p && *p && (ign < 1 || p[ign])) {
+	    if (ign > 0)
+		p += ign;
+	    while (*p) {
+		if (ign >= 0 || p[1])
+		    s = bslashquote(s, NULL,
+				    (*p == '\'' ? 1 : (*p == '"' ? 2 : 0)));
+		p++;
+	    }
+	}
+	return (s == os ? dupstring(s) : s);
+    }
+    DPUTS(1, "BUG: null pointer in multiquote()");
+    return NULL;
+}
 
-	errflag = 0;
-	compmatcher++;
+/**/
+mod_export char *
+tildequote(char *s, int ign)
+{
+    if (s) {
+	int tilde;
+
+	if ((tilde = (*s == '~')))
+	    *s = 'x';
+	s = multiquote(s, ign);
+	if (tilde)
+	    *s = '~';
+
+	return s;
     }
-    redup(osi, 0);
-    return 1;
+    DPUTS(1, "BUG: null pointer in tildequote()");
+    return NULL;
 }
 
 /* Check if we have to complete a parameter name. */
 
 /**/
-char *
+mod_export char *
 check_param(char *s, int set, int test)
 {
     char *p;
@@ -1046,7 +1063,7 @@ check_param(char *s, int set, int test)
 /* Copy the given string and remove backslashes from the copy and return it. */
 
 /**/
-char *
+mod_export char *
 rembslash(char *s)
 {
     char *t = s = dupstring(s);
@@ -1065,7 +1082,7 @@ rembslash(char *s)
 /* This should probably be moved into tokenize(). */
 
 /**/
-char *
+mod_export char *
 ctokenize(char *p)
 {
     char *r = p;
@@ -1091,7 +1108,7 @@ ctokenize(char *p)
 }
 
 /**/
-char *
+mod_export char *
 comp_str(int *ipl, int *pl, int untok)
 {
     char *p = dupstring(compprefix);
@@ -1122,23 +1139,21 @@ comp_str(int *ipl, int *pl, int untok)
     return str;
 }
 
-/* This is for compset -q. */
-
 /**/
 int
 set_comp_sep(void)
 {
     int lip, lp;
-    char *s = comp_str(&lip, &lp, 0);
+    char *s = comp_str(&lip, &lp, 1);
     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;
+    int tl, got = 0, i = 0, cur = -1, oll = ll, sl, remq;
+    int ois = instring, oib = inbackt, noffs = lp;
+    char *tmp, *p, *ns, *ol = (char *) line, sav, *qp, *qs, *ts, qc = '\0';
 
-    if (compisuffix)
-	s = dyncat(s, compisuffix);
+    s += lip;
+    wb += lip;
     untokenize(s);
 
     swb = swe = soffs = 0;
@@ -1155,7 +1170,8 @@ set_comp_sep(void)
     memcpy(tmp + 1, s, noffs);
     tmp[(scs = cs = 1 + noffs)] = 'x';
     strcpy(tmp + 2 + noffs, s + noffs);
-    tmp = rembslash(tmp);
+    if ((remq = (*compqstack == '\\')))
+	tmp = rembslash(tmp);
     inpush(dupstrspace(tmp), 0, NULL);
     line = (unsigned char *) tmp;
     ll = tl - 1;
@@ -1218,21 +1234,31 @@ set_comp_sep(void)
 		*p = '\'';
     }
     offs = owb;
+
+    untokenize(ts = dupstring(ns));
+
     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 ? '\'' : '"');
+	zsfree(autoq);
+	autoq = ztrdup(compqstack[1] ? "" :
+		       multiquote(*ns == Snull ? "'" : "\"", 1));
+	qc = (*ns == Snull ? '\'' : '"');
+	ts++;
     } else {
 	instring = 0;
-	autoq = '\0';
+	zsfree(autoq);
+	autoq = NULL;
     }
     for (p = ns, i = swb; *p; p++, i++) {
 	if (INULL(*p)) {
-	    if (i < scs)
-		soffs--;
+	    if (i < scs) {
+		if (remq && *p == Bnull && p[1])
+		    swb -= 2;
+	    }
 	    if (p[1] || *p != Bnull) {
 		if (*p == Bnull) {
 		    if (scs == i + 1)
@@ -1248,17 +1274,28 @@ set_comp_sep(void)
 	    chuck(p--);
 	}
     }
+    ns = ts;
+
+    if (instring && strchr(compqstack, '\\')) {
+	int rl = strlen(ns), ql = strlen(multiquote(ns, !!compqstack[1]));
+
+	if (ql > rl)
+	    swb -= ql - rl;
+    }
     sav = s[(i = swb - 1)];
     s[i] = '\0';
-    qp = tricat(qipre, rembslash(s), "");
+    qp = 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, "");
+    if (swe > sl) {
+	swe = sl;
+	if (strlen(ns) > swe - swb + 1)
+	    ns[swe - swb + 1] = '\0';
+    }
+    qs = rembslash(s + swe);
     sl = strlen(ns);
     if (soffs > sl)
 	soffs = sl;
@@ -1266,6 +1303,11 @@ set_comp_sep(void)
     {
 	int set = CP_QUOTE | CP_QUOTING, unset = 0;
 
+	p = tricat((instring ? (instring == 1 ? "'" : "\"") : "\\"),
+		   compqstack, "");
+	zsfree(compqstack);
+	compqstack = p;
+
 	zsfree(compquote);
 	zsfree(compquoting);
 	if (instring == 2) {
@@ -1283,11 +1325,11 @@ set_comp_sep(void)
 	compquoting = ztrdup(compquoting);
 	comp_setunset(0, 0, set, unset);
 
+	zsfree(compprefix);
+	zsfree(compsuffix);
 	if (unset(COMPLETEINWORD)) {
 	    untokenize(ns);
-	    zsfree(compprefix);
 	    compprefix = ztrdup(ns);
-	    zsfree(compsuffix);
 	    compsuffix = ztrdup("");
 	} else {
 	    char *ss, sav;
@@ -1302,21 +1344,16 @@ set_comp_sep(void)
 	    untokenize(ss);
 	    compsuffix = ztrdup(ss);
 	}
+	tmp = tricat(compqiprefix, compiprefix, multiquote(qp, 1));
+	zsfree(compqiprefix);
+	compqiprefix = tmp;
+	tmp = tricat(multiquote(qs, 1), compisuffix, compqisuffix);
+	zsfree(compqisuffix);
+	compqisuffix = tmp;
 	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 *));
@@ -1327,7 +1364,6 @@ set_comp_sep(void)
 	compcurrent = cur + 1;
 	compwords[i] = NULL;
     }
-    autoq = oaq;
     instring = ois;
     inbackt = oib;
 
@@ -1337,7 +1373,7 @@ set_comp_sep(void)
 /* This stores the strings from the list in an array. */
 
 /**/
-void
+mod_export void
 set_list_array(char *name, LinkList l)
 {
     char **a, **p;
@@ -1354,7 +1390,7 @@ set_list_array(char *name, LinkList l)
 /* Get the words from a variable or a (list of words). */
 
 /**/
-char **
+mod_export char **
 get_user_var(char *nam)
 {
     if (!nam)
@@ -1423,19 +1459,37 @@ 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;
+    char **aign = NULL, **dparr = NULL, *oaq = autoq, *oppre = dat->ppre;
+    char *oqp = qipre, *oqs = qisuf, qc, **disp = NULL, *ibuf = NULL;
     int lpl, lsl, pl, sl, bcp = 0, bcs = 0, bpadd = 0, bsadd = 0;
+    int ppl = 0, psl = 0;
     int llpl = 0, llsl = 0, nm = mnum, gflags = 0, ohp = haspattern;
-    int oisalt = 0, isalt, isexact, doadd, ois = instring, oib = inbackt;
+    int isexact, doadd, ois = instring, oib = inbackt;
     Cline lc = NULL, pline = NULL, sline = NULL;
     Cmatch cm;
     struct cmlist mst;
     Cmlist oms = mstack;
-    Patprog cp = NULL;
+    Patprog cp = NULL, *pign = NULL;
     LinkList aparl = NULL, oparl = NULL, dparl = NULL;
     Brinfo bp, bpl = brbeg, obpl, bsl = brend, obsl;
 
+    if (!*argv) {
+	SWITCHHEAPS(compheap) {
+	    /* 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);
+	    }
+	} SWITCHBACKHEAPS;
+
+	return 1;
+    }
     for (bp = brbeg; bp; bp = bp->next)
 	bp->curpos = ((dat->aflags & CAF_QUOTE) ? bp->pos : bp->qpos);
     for (bp = brend; bp; bp = bp->next)
@@ -1447,300 +1501,373 @@ addmatches(Cadata dat, char **argv)
 	if (qc == '`') {
 	    instring = 0;
 	    inbackt = 0;
-	    autoq = '\0';
+	    autoq = "";
 	} else {
+	    char buf[2];
+
 	    instring = (qc == '\'' ? 1 : 2);
 	    inbackt = 0;
-	    autoq = qc;
+	    buf[0] = qc;
+	    buf[1] = '\0';
+	    autoq = multiquote(buf, 1);
 	}
     } else {
 	instring = inbackt = 0;
-	autoq = '\0';
+	autoq = NULL;
     }
     qipre = ztrdup(compqiprefix ? compqiprefix : "");
     qisuf = ztrdup(compqisuffix ? compqisuffix : "");
 
+    useexact = (compexact && !strcmp(compexact, "accept"));
+
     /* 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))
+	if ((doadd = (!dat->apar && !dat->opar && !dat->dpar))) {
+	    if (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;
+	    else
+		hasunmatched = 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;
+	/* 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);
+	    add_bmatchers(dat->match);
 
-		addlinknode(matchers, dat->match);
-		dat->match->refc++;
+	    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))) {
+	    char **ap, **sp, *tmp;
+	    Patprog *pp, prog;
+
+	    pign = (Patprog *) zhalloc((arrlen(aign) + 1) * sizeof(Patprog));
+
+	    for (ap = sp = aign, pp = pign; (tmp = *ap); ap++) {
+		tokenize(tmp);
+		remnulargs(tmp);
+		if (((tmp[0] == Quest && tmp[1] == Star) ||
+		     (tmp[1] == Quest && tmp[0] == Star)) &&
+		    tmp[2] && !haswilds(tmp + 2))
+		    untokenize(*sp++ = tmp + 2);
+		else if ((prog = patcompile(tmp, 0, NULL)))
+		    *pp++ = prog;
 	    }
-	    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;
+	    *sp = NULL;
+	    *pp = NULL;
+	    if (!*aign)
+		aign = NULL;
+	    if (!*pign)
+		pign = NULL;
+	}
+	/* 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) {
+		pl = pfxlen(dat->pre, 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) {
+	    dat->ppre = ((dat->flags & CMF_FILE) ?
+			 tildequote(dat->ppre, !!(dat->aflags & CAF_QUOTE)) :
+			 multiquote(dat->ppre, !!(dat->aflags & CAF_QUOTE)));
+	    lpl = strlen(dat->ppre);
+	} else
+	    lpl = 0;
+	if (dat->psuf) {
+	    dat->psuf = multiquote(dat->psuf, !!(dat->aflags & CAF_QUOTE));
+	    lsl = strlen(dat->psuf);
+	} else
+	    lsl = 0;
+	if (dat->aflags & CAF_MATCH) {
+	    int ml, gfl = 0;
+	    char *globflag = NULL;
+
+	    if (comppatmatch && *comppatmatch &&
+		dat->ppre && lpre[0] == '(' && lpre[1] == '#') {
+		char *p;
+
+		for (p = lpre + 2; *p && *p != ')'; p++);
+
+		if (*p == ')') {
+		    char sav = p[1];
+
+		    p[1] = '\0';
+		    globflag = dupstring(lpre);
+		    gfl = p - lpre + 1;
+		    p[1] = sav;
+
+		    lpre = p + 1;
+		    llpl -= gfl;
 		}
 	    }
-	    /* 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;
+	    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
-			*argv = NULL;
-		    bcp = lpl;
+			matchparts = tmp;
 		}
-
-		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';
+		pline = matchparts;
+		lpre += ml;
+		llpl -= 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
-			*argv = NULL;
-		    bcs = lsl;
+			matchparts = tmp;
 		}
-		if (comppatmatch && *comppatmatch) {
-		    int is = (*comppatmatch == '*');
-		    char *tmp = (char *) zhalloc(2 + llpl + llsl);
+		sline = revert_cline(matchparts);
+		lsuf[llsl - ml] = '\0';
+		llsl -= ml;
+		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 + gfl);
 
+		if (gfl) {
+		    strcpy(tmp, globflag);
+		    strcat(tmp, lpre);
+		} else
 		    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;
-		    }
+		tmp[llpl + gfl] = 'x';
+		strcpy(tmp + llpl + gfl + is, lsuf);
+
+		tokenize(tmp);
+		remnulargs(tmp);
+		if (haswilds(tmp)) {
+		    if (is)
+			tmp[llpl + gfl] = 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);
+	}
+	/* 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 (*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 set of matches. */
+
+	    if (dat->remf) {
+		dat->remf = dupstring(dat->remf);
+		dat->rems = NULL;
+	    } else if (dat->rems)
+		dat->rems = dupstring(dat->rems);
+
+	    if (lpre)
+		lpre = ((!(dat->aflags & CAF_QUOTE) &&
+			 (!dat->ppre && (dat->flags & CMF_FILE))) ?
+			tildequote(lpre, 1) : multiquote(lpre, 1));
+	    if (lsuf)
+		lsuf = multiquote(lsuf, 1);
+	}
+	/* Walk through the matches given. */
+	obpl = bpl;
+	obsl = bsl;
+	if (aign || pign) {
+	    int max = 0;
+	    char **ap = argv;
+
+	    ppl = (dat->ppre ? strlen(dat->ppre) : 0);
+	    while ((s = *ap++))
+		if ((sl = strlen(s)) > max)
+		    max = sl;
+	    psl = (dat->psuf ? strlen(dat->psuf) : 0);
+	    ibuf = (char *) zhalloc(1 + ppl + max + psl);
+	}
+	for (; (s = *argv); argv++) {
+	    bpl = obpl;
+	    bsl = obsl;
+	    if (disp) {
+		if (!*++disp)
+		    disp = NULL;
 	    }
-	    /* 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) {
+	    sl = strlen(s);
+	    if (aign || pign) {
+		int il = ppl + sl + psl, addit = 1;
+
+		if (ppl)
+		    memcpy(ibuf, dat->ppre, ppl);
+		strcpy(ibuf + ppl, s);
+		if (psl)
+		    strcpy(ibuf + ppl + sl, dat->psuf);
+
+		if (aign) {
 		    /* Do the suffix-test. If the match has one of the
-		     * suffixes from ign, we put it in the alternate set. */
+		     * suffixes from aign, 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;
+		    for (; addit && *pt; pt++)
+			addit = !((filell = strlen(*pt)) < il &&
+				  !strcmp(*pt, ibuf + il - filell));
+		}
+		if (addit && pign) {
+		    Patprog *pt = pign;
 
-		    if (isalt && !doadd) {
-			if (dparr && !*++dparr)
-			    dparr = NULL;
-			continue;
-		    }
+		    for (; addit && *pt; pt++)
+			addit = !pattry(*pt, ibuf);
 		}
-		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 (!addit) {
+		    compignored++;
 		    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->aflags & CAF_MATCH)) {
+		if (dat->aflags & CAF_QUOTE)
+		    ms = dupstring(s);
+		else
+		    sl = strlen(ms = multiquote(s, 0));
+		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->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(0, 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;
+	}
+	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();
     } SWITCHBACKHEAPS;
 
     /* We switched back to the current heap, now restore the stack of
@@ -1764,7 +1891,7 @@ addmatches(Cadata dat, char **argv)
 /* This adds all the data we have for a match. */
 
 /**/
-Cmatch
+mod_export Cmatch
 add_match_data(int alt, char *str, Cline line,
 	       char *ipre, char *ripre, char *isuf,
 	       char *pre, char *prpre,
@@ -1797,21 +1924,12 @@ add_match_data(int alt, char *str, Cline line,
 	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 (psl) {
+	    s = bld_parts(psuf, psl, psl, &sl);
 
 	    if (sline) {
 		Cline sp;
@@ -1821,6 +1939,7 @@ add_match_data(int alt, char *str, Cline line,
 		for (sp = sline; sp->next; sp = sp->next);
 		sp->next = s;
 		s = sline;
+		sline = NULL;
 	    }
 	    if (!(p->flags & (CLF_SUF | CLF_MID)) &&
 		!p->llen && !p->wlen && !p->olen) {
@@ -1832,7 +1951,7 @@ add_match_data(int alt, char *str, Cline line,
 		    s->prefix = p->prefix;
 		    p->prefix = NULL;
 		}
-		s->flags |= (p->flags & CLF_MATCHED);
+		s->flags |= (p->flags & CLF_MATCHED) | CLF_MID;
 		free_cline(p);
 		if (pp)
 		    pp->next = s;
@@ -1841,8 +1960,29 @@ add_match_data(int alt, char *str, Cline line,
 	    } else
 		p->next = s;
 	}
+	if (isl) {
+	    Cline tsl;
+
+	    s = bld_parts(isuf, isl, isl, &tsl);
+
+	    if (sl)
+		sl->next = s;
+	    else if (sline) {
+		Cline sp;
+
+		sline = cp_cline(sline, 1);
+
+		for (sp = sline; sp->next; sp = sp->next);
+		sp->next = s;
+		p->next = sline;
+		sline = NULL;
+	    } else
+		p->next = s;
+
+	    sl = tsl;
+	}
 	if (qisl) {
-	    Cline qsl = bld_parts(asuf + psl + isl, qisl, qisl, NULL);
+	    Cline qsl = bld_parts(qisuf, qisl, qisl, NULL);
 
 	    qsl->flags |= CLF_SUF;
 	    qsl->suffix = qsl->prefix;
@@ -1888,7 +2028,7 @@ add_match_data(int alt, char *str, Cline line,
 		p = bld_parts(ppre, ppl, ppl, &lp);
 
 	    if (lp->prefix && !(line->flags & (CLF_SUF | CLF_MID)) &&
-		!p->llen && !p->wlen && !p->olen) {
+		!lp->llen && !lp->wlen && !lp->olen) {
 		Cline lpp;
 
 		for (lpp = lp->prefix; lpp->next; lpp = lpp->next);
@@ -1952,8 +2092,8 @@ add_match_data(int alt, char *str, Cline line,
 	} 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) {
+	if (lp->prefix && !(line->flags & CLF_SUF) &&
+	    !lp->llen && !lp->wlen && !lp->olen) {
 	    Cline lpp;
 
 	    for (lpp = lp->prefix; lpp->next; lpp = lpp->next);
@@ -1994,7 +2134,14 @@ add_match_data(int alt, char *str, Cline line,
 	cm->isuf = (isuf && *isuf ? isuf : NULL);
     cm->pre = pre;
     cm->suf = suf;
-    cm->flags = flags;
+    cm->flags = (flags |
+		 (complist ?
+		  ((strstr(complist, "packed") ? CMF_PACKED : 0) |
+		   (strstr(complist, "rows")   ? CMF_ROWS   : 0)) : 0));
+
+    if ((*compqstack == '\\' && compqstack[1]) ||
+	(autoq && *compqstack && compqstack[1] == '\\'))
+	cm->flags |= CMF_NOSPACE;
     if (nbrbeg) {
 	int *p;
 	Brinfo bp;
@@ -2017,7 +2164,7 @@ add_match_data(int alt, char *str, Cline line,
 	cm->brsl = NULL;
     cm->qipl = qipl;
     cm->qisl = qisl;
-    cm->autoq = (autoq ? autoq : (inbackt ? '`' : '\0'));
+    cm->autoq = dupstring(autoq ? autoq : (inbackt ? "`" : NULL));
     cm->rems = cm->remf = cm->disp = NULL;
 
     if ((lastprebr || lastpostbr) && !hasbrpsfx(cm, lastprebr, lastpostbr))
@@ -2032,7 +2179,12 @@ add_match_data(int alt, char *str, Cline line,
     addlinknode((alt ? fmatches : matches), cm);
 
     newmatches = 1;
+    mgroup->new = 1;
+    if (alt)
+	compignored++;
 
+    if (!complastprompt || !*complastprompt)
+	dolastprompt = 0;
     /* One more match for this explanation. */
     if (curexpl) {
 	if (alt)
@@ -2056,8 +2208,8 @@ add_match_data(int alt, char *str, Cline line,
     /* Do we have an exact match? More than one? */
     if (exact) {
 	if (!ai->exact) {
-	    ai->exact = 1;
-	    if (incompfunc) {
+	    ai->exact = useexact;
+	    if (incompfunc && (!compexactstr || !*compexactstr)) {
 		/* If a completion widget is active, we make the exact
 		 * string available in `compstate'. */
 
@@ -2076,7 +2228,7 @@ add_match_data(int alt, char *str, Cline line,
 		comp_setunset(0, 0, CP_EXACTSTR, 0);
 	    }
 	    ai->exactm = cm;
-	} else {
+	} else if (useexact) {
 	    ai->exact = 2;
 	    ai->exactm = NULL;
 	    if (incompfunc)
@@ -2089,7 +2241,7 @@ add_match_data(int alt, char *str, Cline line,
 /* This begins a new group of matches. */
 
 /**/
-void
+mod_export void
 begcmgroup(char *n, int flags)
 {
     if (n) {
@@ -2118,6 +2270,8 @@ begcmgroup(char *n, int flags)
     mgroup->matches = NULL;
     mgroup->ylist = NULL;
     mgroup->expls = NULL;
+    mgroup->perm = NULL;
+    mgroup->new = 0;
 
     mgroup->lexpls = expls = newlinklist();
     mgroup->lmatches = matches = newlinklist();
@@ -2132,7 +2286,7 @@ begcmgroup(char *n, int flags)
 /* End the current group for now. */
 
 /**/
-void
+mod_export void
 endcmgroup(char **ylist)
 {
     mgroup->ylist = ylist;
@@ -2141,7 +2295,7 @@ endcmgroup(char **ylist)
 /* Add an explanation string to the current group, joining duplicates. */
 
 /**/
-void
+mod_export void
 addexpl(void)
 {
     LinkNode n;
@@ -2221,7 +2375,7 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp)
     int n, nl = 0, ll = 0;
 
     /* Build an array for the matches. */
-    rp = ap = (Cmatch *) ncalloc(((n = countlinknodes(l)) + 1) *
+    rp = ap = (Cmatch *) hcalloc(((n = countlinknodes(l)) + 1) *
 				 sizeof(Cmatch));
 
     /* And copy them into it. */
@@ -2252,22 +2406,28 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp)
 		  (int (*) _((const void *, const void *)))matchcmp);
 
 	    if (!(flags & CGF_UNIQCON)) {
+		int dup;
+
 		/* 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;
+		    for (dup = 0; bp[1] && !(*ap)->disp && !(bp[1])->disp &&
+			     !strcmp((*ap)->str, (bp[1])->str); bp++) {
+			(bp[1])->flags |= CMF_MULT;
+			dup = 1;
+		    }
+		    if (dup)
+			(*ap)->flags |= CMF_FMULT;
 		}
 		*cp = NULL;
 	    }
 	    for (ap = rp; *ap; ap++) {
 		if ((*ap)->disp && ((*ap)->flags & CMF_DISPLINE))
 		    ll++;
-		if ((*ap)->flags & CMF_NOLIST)
+		if ((*ap)->flags & (CMF_NOLIST | CMF_MULT))
 		    nl++;
 	    }
 	} else {
@@ -2282,20 +2442,26 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp)
 		    *cp = NULL;
 		}
 	    } else if (!(flags & CGF_UNIQCON)) {
+		int dup;
+
 		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;
+		    for (dup = 0; bp[1] && !(*ap)->disp && !(bp[1])->disp &&
+			     !strcmp((*ap)->str, (bp[1])->str); bp++) {
+			(bp[1])->flags |= CMF_MULT;
+			dup = 1;
+		    }
+		    if (dup)
+			(*ap)->flags |= CMF_FMULT;
 		}
 		*cp = NULL;
 	    }
 	    for (ap = rp; *ap; ap++) {
 		if ((*ap)->disp && ((*ap)->flags & CMF_DISPLINE))
 		    ll++;
-		if ((*ap)->flags & CMF_NOLIST)
+		if ((*ap)->flags & (CMF_NOLIST | CMF_MULT))
 		    nl++;
 	    }
 	}
@@ -2317,7 +2483,7 @@ dupmatch(Cmatch m, int nbeg, int nend)
 {
     Cmatch r;
 
-    r = (Cmatch) ncalloc(sizeof(struct cmatch));
+    r = (Cmatch) zcalloc(sizeof(struct cmatch));
 
     r->str = ztrdup(m->str);
     r->ipre = ztrdup(m->ipre);
@@ -2349,10 +2515,10 @@ dupmatch(Cmatch m, int nbeg, int nend)
 	r->brsl = NULL;
     r->rems = ztrdup(m->rems);
     r->remf = ztrdup(m->remf);
-    r->autoq = m->autoq;
+    r->autoq = ztrdup(m->autoq);
     r->qipl = m->qipl;
     r->qisl = m->qisl;
-    r->disp = dupstring(m->disp);
+    r->disp = ztrdup(m->disp);
 
     return r;
 }
@@ -2360,24 +2526,24 @@ dupmatch(Cmatch m, int nbeg, int nend)
 /* This duplicates all groups of matches. */
 
 /**/
-int
+mod_export int
 permmatches(int last)
 {
-    Cmgroup g = amatches, n;
+    Cmgroup g = amatches, n, opm;
     Cmatch *p, *q;
     Cexpl *ep, *eq, e, o;
     LinkList mlist;
     static int fi = 0;
-    int nn, nl, ll, gn = 1, mn = 1, rn;
+    int nn, nl, ll, gn = 1, mn = 1, rn, ofi = fi;
 
-    if (pmatches && !newmatches)
+    if (pmatches && !newmatches) {
+	if (last && fi)
+	    ainfo = fainfo;
 	return fi;
-
+    }
     newmatches = fi = 0;
 
-    if (pmatches)
-	freematches(pmatches);
-
+    opm = pmatches;
     pmatches = lmatches = NULL;
     nmatches = smatches = 0;
 
@@ -2387,8 +2553,8 @@ permmatches(int last)
 	fi = 1;
     }
     while (g) {
-	HEAPALLOC {
-	    if (empty(g->lmatches))
+	if (fi != ofi || !g->perm || g->new) {
+	    if (fi)
 		/* We have no matches, try ignoring fignore. */
 		mlist = g->lfmatches;
 	    else
@@ -2407,51 +2573,67 @@ permmatches(int last)
 					   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;
+	    nmatches += g->mcount;
+	    smatches += g->lcount;
+
+	    n = (Cmgroup) zcalloc(sizeof(struct cmgroup));
+
+	    if (g->perm) {
+		g->perm->next = NULL;
+		freematches(g->perm);
+	    }
+	    g->perm = n;
+
+	    if (!lmatches)
+		lmatches = n;
+	    if (pmatches)
+		pmatches->prev = n;
+	    n->next = pmatches;
+	    pmatches = n;
+	    n->prev = NULL;
+	    n->num = gn++;
+	    n->flags = g->flags;
+	    n->mcount = g->mcount;
+	    n->matches = p = (Cmatch *) zcalloc((n->mcount + 1) * sizeof(Cmatch));
+	    n->name = ztrdup(g->name);
+	    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 = zarrdup(g->ylist);
+	    else
+		n->ylist = NULL;
+
+	    if ((n->ecount = g->ecount)) {
+		n->expls = ep = (Cexpl *) zcalloc((n->ecount + 1) * sizeof(Cexpl));
+		for (eq = g->expls; (o = *eq); eq++, ep++) {
+		    *ep = e = (Cexpl) zcalloc(sizeof(struct cexpl));
+		    e->count = (fi ? o->fcount : o->count);
+		    e->str = ztrdup(o->str);
+		}
+		*ep = NULL;
+	    } else
+		n->expls = NULL;
 
+	    n->widths = NULL;
+	} else {
+	    if (!lmatches)
+		lmatches = g->perm;
+	    if (pmatches)
+		pmatches->prev = g->perm;
+	    g->perm->next = pmatches;
+	    pmatches = g->perm;
+	    g->perm->prev = NULL;
+
+	    nmatches += g->mcount;
+	    smatches += g->lcount;
+	    g->num = gn++;
+	}
+	g->new = 0;
 	g = g->next;
     }
     for (g = pmatches; g; g = g->next) {
@@ -2490,6 +2672,7 @@ freematch(Cmatch m, int nbeg, int nend)
     zsfree(m->rems);
     zsfree(m->remf);
     zsfree(m->disp);
+    zsfree(m->autoq);
     zfree(m->brpl, nbeg * sizeof(int));
     zfree(m->brsl, nend * sizeof(int));
 
@@ -2499,7 +2682,7 @@ freematch(Cmatch m, int nbeg, int nend)
 /* This frees the groups of matches. */
 
 /**/
-void
+mod_export void
 freematches(Cmgroup g)
 {
     Cmgroup n;
@@ -2508,7 +2691,7 @@ freematches(Cmgroup g)
 
     while (g) {
 	n = g->next;
-	
+
 	for (m = g->matches; *m; m++)
 	    freematch(*m, g->nbrbeg, g->nbrend);
 
@@ -2523,6 +2706,7 @@ freematches(Cmgroup g)
 	    }
 	    free(g->expls);
 	}
+	zsfree(g->name);
 	free(g);
 
 	g = n;
diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c
index 283b8de62..ef961eeba 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.
  *
@@ -29,12 +29,50 @@
 
 #include "complete.mdh"
 #include "complete.pro"
-#define GLOBAL_PROTOTYPES
-#include "zle_tricky.pro"
-#undef GLOBAL_PROTOTYPES
+
+/* global variables for shell parameters in new style completion */
 
 /**/
-void
+mod_export zlong compcurrent;
+/**/
+zlong complistmax,
+      complistlines,
+      compignored;
+
+/**/
+mod_export
+char **compwords,
+     *compprefix,
+     *compsuffix,
+     *compisuffix,
+     *compqiprefix,
+     *compqisuffix,
+     *compquote,
+     *compqstack,
+     *comppatmatch,
+     *complastprompt;
+/**/
+char *compiprefix,
+     *compcontext,
+     *compparameter,
+     *compredirect,
+     *compquoting,
+     *comprestore,
+     *complist,
+     *compinsert,
+     *compexact,
+     *compexactstr,
+     *comppatinsert,
+     *comptoend,
+     *compoldlist,
+     *compoldins,
+     *compvared;
+
+/**/
+Param *comprpms, *compkpms;
+
+/**/
+mod_export void
 freecmlist(Cmlist l)
 {
     Cmlist n;
@@ -51,7 +89,7 @@ freecmlist(Cmlist l)
 }
 
 /**/
-void
+mod_export void
 freecmatcher(Cmatcher m)
 {
     Cmatcher n;
@@ -86,29 +124,10 @@ freecpattern(Cpattern p)
     }
 }
 
-/* Copy a list of completion matchers. */
-
-static Cmlist
-cpcmlist(Cmlist l)
-{
-    Cmlist r = NULL, *p = &r, n;
-
-    while (l) {
-	*p = n = (Cmlist) zalloc(sizeof(struct cmlist));
-	n->next = NULL;
-	n->matcher = cpcmatcher(l->matcher);
-	n->str = ztrdup(l->str);
-
-	p = &(n->next);
-	l = l->next;
-    }
-    return r;
-}
-
 /* Copy a completion matcher list. */
 
 /**/
-Cmatcher
+mod_export Cmatcher
 cpcmatcher(Cmatcher m)
 {
     Cmatcher r = NULL, *p = &r, n;
@@ -155,37 +174,10 @@ cpcpattern(Cpattern o)
     return r;
 }
 
-/* Set the global match specs. */
-
-/**/
-int
-set_gmatcher(char *name, char **argv)
-{
-    Cmlist l = NULL, *q = &l, n;
-    Cmatcher m;
-
-    while (*argv) {
-	if ((m = parse_cmatcher(name, *argv)) == pcm_err)
-	    return 1;
-	*q = n = (Cmlist) zhalloc(sizeof(struct cmlist));
-	n->next = NULL;
-	n->matcher = m;
-	n->str = *argv++;
-
-	q = &(n->next);
-    }
-    freecmlist(cmatcher);
-    PERMALLOC {
-	cmatcher = cpcmlist(l);
-    } LASTALLOC;
-
-    return 1;
-}
-
 /* Parse a string for matcher control, containing multiple matchers. */
 
 /**/
-Cmatcher
+mod_export Cmatcher
 parse_cmatcher(char *name, char *s)
 {
     Cmatcher ret = NULL, r = NULL, n;
@@ -262,8 +254,11 @@ parse_cmatcher(char *name, char *s)
 		return pcm_err;
 	    }
 	    word = NULL;
-	    wl = -1;
-	    s++;
+	    if (*++s == '*') {
+		s++;
+		wl = -2;
+	    } else
+		wl = -1;
 	} else {
 	    word = parse_pattern(name, &s, &wl, 0, &err);
 
@@ -389,17 +384,17 @@ static int
 bin_compadd(char *name, char **argv, char *ops, int func)
 {
     struct cadata dat;
-    char *p, **sp, *e, *m = NULL;
+    char *p, **sp, *e, *m = NULL, *mstr = NULL;
     int dm;
     Cmatcher match = NULL;
 
     if (incompfunc != 1) {
-	zerrnam(name, "can only be called from completion function", NULL, 0);
+	zwarnnam(name, "can only be called from completion function", NULL, 0);
 	return 1;
     }
     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 +457,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";
@@ -486,9 +477,6 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 		sp = &(dat.prpre);
 		e = "string expected after -%c";
 		break;
-	    case 'a':
-		dat.aflags |= CAF_ALT;
-		break;
 	    case 'M':
 		sp = &m;
 		e = "matching specification expected after -%c";
@@ -531,7 +519,7 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 		argv++;
 		goto ca_args;
 	    default:
-		zerrnam(name, "bad option: -%c", NULL, *p);
+		zwarnnam(name, "bad option: -%c", NULL, *p);
 		return 1;
 	    }
 	    if (sp) {
@@ -545,18 +533,29 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 			*sp = *argv;
 		    p = "" - 1;
 		} else {
-		    zerrnam(name, e, NULL, *p);
+		    zwarnnam(name, e, NULL, *p);
 		    return 1;
 		}
-		if (dm && (match = parse_cmatcher(name, m)) == pcm_err) {
-		    match = NULL;
-		    return 1;
+		if (dm) {
+		    if (mstr)
+			mstr = tricat(mstr, " ", m);
+		    else
+			mstr = ztrdup(m);
+		    m = NULL;
 		}
 	    }
 	}
     }
+    if (mstr && (match = parse_cmatcher(name, mstr)) == pcm_err) {
+	zsfree(mstr);
+	return 1;
+    }
+    zsfree(mstr);
+
  ca_args:
-    if (!*argv)
+
+    if (!*argv && !dat.group &&
+	!(dat.aflags & (CAF_NOSORT|CAF_UNIQALL|CAF_UNIQCON)))
 	return 1;
 
     dat.match = match = cpcmatcher(match);
@@ -574,7 +573,7 @@ bin_compadd(char *name, char **argv, char *ops, int func)
 #define CVT_SUFPAT   5
 
 /**/
-void
+mod_export void
 ignore_prefix(int l)
 {
     if (l) {
@@ -598,7 +597,7 @@ ignore_prefix(int l)
 }
 
 /**/
-void
+mod_export void
 ignore_suffix(int l)
 {
     if (l) {
@@ -621,7 +620,7 @@ ignore_suffix(int l)
 }
 
 /**/
-void
+mod_export void
 restrict_range(int b, int e)
 {
     int wl = arrlen(compwords) - 1;
@@ -644,6 +643,7 @@ restrict_range(int b, int e)
     }
 }
 
+/**/
 static int
 do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod)
 {
@@ -740,7 +740,7 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod)
 		char *p, sav;
 
 		if (!(l = strlen(compprefix)))
-		    return 0;
+		    return ((na == 1 || na == -1) && pattry(pp, compprefix));
 		if (na < 0) {
 		    p = compprefix + l;
 		    na = -na;
@@ -766,7 +766,7 @@ do_comp_vars(int test, int na, char *sa, int nb, char *sb, int mod)
 		char *p;
 
 		if (!(ol = l = strlen(compsuffix)))
-		    return 0;
+		    return ((na == 1 || na == -1) && pattry(pp, compsuffix));
 		if (na < 0) {
 		    p = compsuffix;
 		    na = -na;
@@ -798,11 +798,11 @@ bin_compset(char *name, char **argv, char *ops, int func)
     char *sa = NULL, *sb = NULL;
 
     if (incompfunc != 1) {
-	zerrnam(name, "can only be called from completion function", NULL, 0);
+	zwarnnam(name, "can only be called from completion function", NULL, 0);
 	return 1;
     }
     if (argv[0][0] != '-') {
-	zerrnam(name, "missing option", NULL, 0);
+	zwarnnam(name, "missing option", NULL, 0);
 	return 1;
     }
     switch (argv[0][1]) {
@@ -814,7 +814,7 @@ bin_compset(char *name, char **argv, char *ops, int func)
     case 'S': test = CVT_SUFPAT; break;
     case 'q': return set_comp_sep();
     default:
-	zerrnam(name, "bad option -%c", NULL, argv[0][1]);
+	zwarnnam(name, "bad option -%c", NULL, argv[0][1]);
 	return 1;
     }
     if (argv[0][2]) {
@@ -823,7 +823,7 @@ bin_compset(char *name, char **argv, char *ops, int func)
 	na = 2;
     } else {
 	if (!(sa = argv[1])) {
-	    zerrnam(name, "missing string for option -%c", NULL, argv[0][1]);
+	    zwarnnam(name, "missing string for option -%c", NULL, argv[0][1]);
 	    return 1;
 	}
 	sb = argv[2];
@@ -831,7 +831,7 @@ bin_compset(char *name, char **argv, char *ops, int func)
     }
     if (((test == CVT_PRENUM || test == CVT_SUFNUM) ? !!sb :
 	 (sb && argv[na]))) {
-	zerrnam(name, "too many arguments", NULL, 0);
+	zwarnnam(name, "too many arguments", NULL, 0);
 	return 1;
     }
     switch (test) {
@@ -841,11 +841,9 @@ bin_compset(char *name, char **argv, char *ops, int func)
 	break;
     case CVT_RANGEPAT:
 	tokenize(sa);
-	sa = rembslash(sa);
 	remnulargs(sa);
 	if (sb) {
 	    tokenize(sb);
-	    sb = rembslash(sb);
 	    remnulargs(sb);
 	}
 	break;
@@ -861,7 +859,6 @@ bin_compset(char *name, char **argv, char *ops, int func)
 	} else
 	    na = -1;
 	tokenize(sa);
-	sa = rembslash(sa);
 	remnulargs(sa);
 	break;
     }
@@ -892,9 +889,6 @@ static struct compparam comprparams[] = {
 
 static struct compparam compkparams[] = {
     { "nmatches", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_nmatches) },
-    { "matcher", PM_INTEGER, VAL(compmatcher), NULL, NULL },
-    { "matcher_string", PM_SCALAR, VAL(compmatcherstr), NULL, NULL },
-    { "total_matchers", PM_INTEGER, VAL(compmatchertot), NULL, NULL },
     { "context", PM_SCALAR, VAL(compcontext), NULL, NULL },
     { "parameter", PM_SCALAR, VAL(compparameter), NULL, NULL },
     { "redirect", PM_SCALAR, VAL(compredirect), NULL, NULL },
@@ -902,7 +896,6 @@ static struct compparam compkparams[] = {
     { "quoting", PM_SCALAR | PM_READONLY, VAL(compquoting), NULL, NULL },
     { "restore", PM_SCALAR, VAL(comprestore), NULL, NULL },
     { "list", PM_SCALAR, NULL, VAL(set_complist), VAL(get_complist) },
-    { "force_list", PM_SCALAR, VAL(compforcelist), NULL, NULL },
     { "insert", PM_SCALAR, VAL(compinsert), NULL, NULL },
     { "exact", PM_SCALAR, VAL(compexact), NULL, NULL },
     { "exact_string", PM_SCALAR, VAL(compexactstr), NULL, NULL },
@@ -917,8 +910,9 @@ static struct compparam compkparams[] = {
     { "old_list", PM_SCALAR, VAL(compoldlist), NULL, NULL },
     { "old_insert", PM_SCALAR, VAL(compoldins), NULL, NULL },
     { "vared", PM_SCALAR, VAL(compvared), NULL, NULL },
-    { "alternate_nmatches", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_anmatches) },
     { "list_lines", PM_INTEGER | PM_READONLY, NULL, NULL, VAL(get_listlines) },
+    { "all_quotes", PM_SCALAR | PM_READONLY, VAL(compqstack), NULL, NULL },
+    { "ignored", PM_INTEGER | PM_READONLY, VAL(compignored), NULL, NULL },
     { NULL, 0, NULL, NULL, NULL }
 };
 
@@ -976,7 +970,7 @@ makecompparams(void)
 
     comprpms[CPN_COMPSTATE] = cpm;
     tht = paramtab;
-    cpm->level = locallevel;
+    cpm->level = locallevel + 1;
     cpm->gets.hfn = get_compstate;
     cpm->sets.hfn = set_compstate;
     cpm->unsetfn = compunsetfn;
@@ -1029,14 +1023,7 @@ set_compstate(Param pm, HashTable ht)
 static zlong
 get_nmatches(Param pm)
 {
-    return num_matches(1);
-}
-
-/**/
-static zlong
-get_anmatches(Param pm)
-{
-    return num_matches(0);
+    return (permmatches(0) ? 0 : nmatches);
 }
 
 /**/
@@ -1083,14 +1070,37 @@ static void
 compunsetfn(Param pm, int exp)
 {
     if (exp) {
-	if (PM_TYPE(pm->flags) == PM_SCALAR) {
-	    zsfree(*((char **) pm->u.data));
-	    *((char **) pm->u.data) = ztrdup("");
-	} else if (PM_TYPE(pm->flags) == PM_ARRAY) {
-	    freearray(*((char ***) pm->u.data));
-	    *((char ***) pm->u.data) = zcalloc(sizeof(char *));
+	if (pm->u.data) {
+	    if (PM_TYPE(pm->flags) == PM_SCALAR) {
+		zsfree(*((char **) pm->u.data));
+		*((char **) pm->u.data) = ztrdup("");
+	    } else if (PM_TYPE(pm->flags) == PM_ARRAY) {
+		freearray(*((char ***) pm->u.data));
+		*((char ***) pm->u.data) = zcalloc(sizeof(char *));
+	    } else if (PM_TYPE(pm->flags) == PM_HASHED) {
+		deleteparamtable(pm->u.hash);
+		pm->u.hash = NULL;
+	    }
 	}
-	pm->flags |= PM_UNSET;
+    } else if (PM_TYPE(pm->flags) == PM_HASHED) {
+	Param *p;
+	int i;
+
+	deletehashtable(pm->u.hash);
+	pm->u.hash = NULL;
+
+	for (p = compkpms, i = CP_KEYPARAMS; i--; p++)
+	    *p = NULL;
+    }
+    if (!exp) {
+	Param *p;
+	int i;
+
+	for (p = comprpms, i = CP_REALPARAMS; i; p++, i--)
+	    if (*p == pm) {
+		*p = NULL;
+		break;
+	    }
     }
 }
 
@@ -1102,31 +1112,35 @@ comp_setunset(int rset, int runset, int kset, int kunset)
 
     if (comprpms && (rset >= 0 || runset >= 0)) {
 	for (p = comprpms; rset || runset; rset >>= 1, runset >>= 1, p++) {
-	    if (rset & 1)
-		(*p)->flags &= ~PM_UNSET;
-	    if (runset & 1)
-		(*p)->flags |= PM_UNSET;
+	    if (*p) {
+		if (rset & 1)
+		    (*p)->flags &= ~PM_UNSET;
+		if (runset & 1)
+		    (*p)->flags |= PM_UNSET;
+	    }
 	}
     }
-    if (comprpms && (kset >= 0 || kunset >= 0)) {
+    if (compkpms && (kset >= 0 || kunset >= 0)) {
 	for (p = compkpms; kset || kunset; kset >>= 1, kunset >>= 1, p++) {
-	    if (kset & 1)
-		(*p)->flags &= ~PM_UNSET;
-	    if (kunset & 1)
-		(*p)->flags |= PM_UNSET;
+	    if (*p) {
+		if (kset & 1)
+		    (*p)->flags &= ~PM_UNSET;
+		if (kunset & 1)
+		    (*p)->flags |= PM_UNSET;
+	    }
 	}
     }
 }
 
 /**/
 static int
-comp_wrapper(List list, FuncWrap w, char *name)
+comp_wrapper(Eprog prog, FuncWrap w, char *name)
 {
     if (incompfunc != 1)
 	return 1;
     else {
 	char *orest, *opre, *osuf, *oipre, *oisuf, **owords;
-	char *oqipre, *oqisuf, *oq, *oqi;
+	char *oqipre, *oqisuf, *oq, *oqi, *oqs, *oaq;
 	zlong ocur;
 	unsigned int runset = 0, kunset = 0, m, sm;
 	Param *pp;
@@ -1142,52 +1156,65 @@ comp_wrapper(List list, FuncWrap w, char *name)
 	orest = comprestore;
 	comprestore = ztrdup("auto");
 	ocur = compcurrent;
-	opre = dupstring(compprefix);
-	osuf = dupstring(compsuffix);
-	oipre = dupstring(compiprefix);
-	oisuf = dupstring(compisuffix);
-	oqipre = dupstring(compqiprefix);
-	oqisuf = dupstring(compqisuffix);
-	oq = dupstring(compquote);
-	oqi = dupstring(compquoting);
-
-	HEAPALLOC {
-	    owords = arrdup(compwords);
-	} LASTALLOC;
-
-	runshfunc(list, w, name);
+	opre = ztrdup(compprefix);
+	osuf = ztrdup(compsuffix);
+	oipre = ztrdup(compiprefix);
+	oisuf = ztrdup(compisuffix);
+	oqipre = ztrdup(compqiprefix);
+	oqisuf = ztrdup(compqisuffix);
+	oq = ztrdup(compquote);
+	oqi = ztrdup(compquoting);
+	oqs = ztrdup(compqstack);
+	oaq = ztrdup(autoq);
+	owords = zarrdup(compwords);
+
+	runshfunc(prog, w, name);
 
 	if (comprestore && !strcmp(comprestore, "auto")) {
 	    compcurrent = ocur;
 	    zsfree(compprefix);
-	    compprefix = ztrdup(opre);
+	    compprefix = opre;
 	    zsfree(compsuffix);
-	    compsuffix = ztrdup(osuf);
+	    compsuffix = osuf;
 	    zsfree(compiprefix);
-	    compiprefix = ztrdup(oipre);
+	    compiprefix = oipre;
 	    zsfree(compisuffix);
-	    compisuffix = ztrdup(oisuf);
+	    compisuffix = oisuf;
 	    zsfree(compqiprefix);
-	    compqiprefix = ztrdup(oqipre);
+	    compqiprefix = oqipre;
 	    zsfree(compqisuffix);
-	    compqisuffix = ztrdup(oqisuf);
+	    compqisuffix = oqisuf;
 	    zsfree(compquote);
-	    compquote = ztrdup(oq);
+	    compquote = oq;
 	    zsfree(compquoting);
-	    compquoting = ztrdup(oqi);
+	    compquoting = oqi;
+	    zsfree(compqstack);
+	    compqstack = oqs;
+	    zsfree(autoq);
+	    autoq = oaq;
 	    freearray(compwords);
-	    PERMALLOC {
-		compwords = arrdup(owords);
-	    } LASTALLOC;
+	    compwords = owords;
 	    comp_setunset(CP_COMPSTATE |
 			  (~runset & (CP_WORDS | CP_CURRENT | CP_PREFIX |
 				     CP_SUFFIX | CP_IPREFIX | CP_ISUFFIX |
 				     CP_QIPREFIX | CP_QISUFFIX)),
 			  (runset & CP_ALLREALS),
 			  (~kunset & CP_RESTORE), (kunset & CP_ALLKEYS));
-	} else
+	} else {
 	    comp_setunset(CP_COMPSTATE, 0, (~kunset & CP_RESTORE),
 			  (kunset & CP_RESTORE));
+	    zsfree(opre);
+	    zsfree(osuf);
+	    zsfree(oipre);
+	    zsfree(oisuf);
+	    zsfree(oqipre);
+	    zsfree(oqisuf);
+	    zsfree(oq);
+	    zsfree(oqi);
+	    zsfree(oqs);
+	    zsfree(oaq);
+	    freearray(owords);
+	}
 	zsfree(comprestore);
 	comprestore = orest;
 
@@ -1228,42 +1255,6 @@ cond_range(char **a, int id)
 			(id ? cond_str(a, 1, 1) : NULL), 0);
 }
 
-/**/
-static void
-cmsetfn(Param pm, char **v)
-{
-    set_gmatcher(pm->nam, v);
-}
-
-/**/
-static char **
-cmgetfn(Param pm)
-{
-    int num;
-    Cmlist p;
-    char **ret, **q;
-
-    for (num = 0, p = cmatcher; p; p = p->next, num++);
-
-    ret = (char **) zhalloc((num + 1) * sizeof(char *));
-
-    for (q = ret, p = cmatcher; p; p = p->next, q++)
-	*q = dupstring(p->str);
-    *q = NULL;
-
-    return ret;
-}
-
-/**/
-static void
-cmunsetfn(Param pm, int exp)
-{
-    char *dummy[1];
-
-    dummy[0] = NULL;
-    set_gmatcher(pm->nam, dummy);
-}
-
 static struct builtin bintab[] = {
     BUILTIN("compadd", 0, bin_compadd, 0, -1, 0, NULL, NULL),
     BUILTIN("compset", 0, bin_compset, 1, 3, 0, NULL, NULL),
@@ -1280,53 +1271,108 @@ static struct funcwrap wrapper[] = {
     WRAPDEF(comp_wrapper),
 };
 
-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_cleanup", NULL, 0),
+    HOOKDEF("comp_list_matches", ilistmatches, 0),
 };
 
 /**/
 int
-setup_complete(Module m)
+setup_(Module m)
 {
-    makecompparamsptr = makecompparams;
-    comp_setunsetptr = comp_setunset;
+    hasperm = 0;
+
+    comprpms = compkpms = NULL;
+    compwords = NULL;
+    compprefix = compsuffix = compiprefix = compisuffix = 
+	compqiprefix = compqisuffix =
+	compcontext = compparameter = compredirect = compquote =
+	compquoting = comprestore = complist = compinsert =
+	compexact = compexactstr = comppatmatch = comppatinsert =
+	complastprompt = comptoend = compoldlist = compoldins =
+	compvared = compqstack = NULL;
+
+    hascompmod = 1;
 
     return 0;
 }
 
 /**/
 int
-boot_complete(Module m)
+boot_(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)) |
 	  !addwrapper(m, wrapper)))
 	return 1;
     return 0;
 }
 
-#ifdef MODULE
-
 /**/
 int
-cleanup_complete(Module m)
+cleanup_(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));
     deletewrapper(m, wrapper);
     return 0;
 }
 
 /**/
 int
-finish_complete(Module m)
+finish_(Module m)
 {
-    makecompparamsptr = NULL;
-    comp_setunsetptr = NULL;
+    if (compwords)
+	freearray(compwords);
+    zsfree(compprefix);
+    zsfree(compsuffix);
+    zsfree(compiprefix);
+    zsfree(compisuffix);
+    zsfree(compqiprefix);
+    zsfree(compqisuffix);
+    zsfree(compcontext);
+    zsfree(compparameter);
+    zsfree(compredirect);
+    zsfree(compquote);
+    zsfree(compqstack);
+    zsfree(compquoting);
+    zsfree(comprestore);
+    zsfree(complist);
+    zsfree(compinsert);
+    zsfree(compexact);
+    zsfree(compexactstr);
+    zsfree(comppatmatch);
+    zsfree(comppatinsert);
+    zsfree(complastprompt);
+    zsfree(comptoend);
+    zsfree(compoldlist);
+    zsfree(compoldins);
+    zsfree(compvared);
+
+    hascompmod = 0;
 
     return 0;
 }
-
-#endif
diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c
index 32e0c3a68..3dda28e11 100644
--- a/Src/Zle/compmatch.c
+++ b/Src/Zle/compmatch.c
@@ -28,15 +28,8 @@
  */
 
 #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. */
 
@@ -75,14 +68,14 @@ cmp_cmatchers(Cmatcher a, Cmatcher b)
 /* Add the given matchers to the bmatcher list. */
 
 /**/
-void
+mod_export 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)) {
+	    (m->flags == CMF_RIGHT && m->wlen < 0 && !m->llen)) {
 	    *q = n = (Cmlist) zhalloc(sizeof(struct cmlist));
 	    n->matcher = m;
 	    q = &(n->next);
@@ -95,7 +88,7 @@ add_bmatchers(Cmatcher m)
  * ensure that the bmatchers list contains no matchers not in mstack. */
 
 /**/
-void
+mod_export void
 update_bmatchers(void)
 {
     Cmlist p = bmatchers, q = NULL, ms;
@@ -141,6 +134,7 @@ get_cline(char *l, int ll, char *w, int wl, char *o, int ol, int fl)
     r->slen = 0;
     r->flags = fl;
     r->prefix = r->suffix = NULL;
+    r->min = r->max = 0;
     return r;
 }
 
@@ -443,7 +437,7 @@ 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;
+    int il = 0, iw = 0, t, ind, add, he = 0, bpc, obc = bc, bslash;
     VARARR(unsigned char, ea, ll + 1);
     char *ow;
     Cmlist ms;
@@ -553,7 +547,7 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
 			    } else
 				t = match_str(l + llen + moff, tp + moff,
 					      NULL, 0, NULL, 0, 1, part);
-			    if (t || !both)
+			    if (t || (mp->wlen == -1 && !both))
 				break;
 			}
 		    }
@@ -743,12 +737,15 @@ match_str(char *l, char *w, Brinfo *bpp, int bc, int *rwlp,
 	if (mp)
 	    continue;
 
-	if (l[ind] == w[ind]) {
+	bslash = 0;
+	if (l[ind] == w[ind] ||
+	    (bslash = (lw > 1 && w[ind] == '\\' &&
+		       (ind ? (w[0] == l[0]) : (w[1] == l[0]))))) {
 	    /* No matcher could be used, but the strings have the same
 	     * character here, skip over it. */
-	    l += add; w += add;
-	    il++; iw++;
-	    ll--; lw--;
+	    l += add; w += (bslash ? (add + add ) : add);
+	    il++; iw += 1 + bslash;
+	    ll--; lw -= 1 + bslash;
 	    bc++;
 	    if (!test)
 		while (bp && bc >= (useqbr ? bp->qpos : bp->pos)) {
@@ -839,7 +836,7 @@ match_parts(char *l, char *w, int n, int part)
  * and the suffix don't match the word w. */
 
 /**/
-char *
+mod_export 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)
 {
@@ -853,9 +850,8 @@ comp_match(char *pfx, char *sfx, char *w, Patprog cp, Cline *clp, int qu,
 	if (!pattry(cp, r))
 	    return NULL;
     
-	r = (qu ? quotename(r, NULL) : dupstring(r));
-	if (qu == 2 && r[0] == '\\' && r[1] == '~')
-	    chuck(r);
+	r = (qu == 2 ? tildequote(r, 0) : multiquote(r, !qu));
+
 	/* We still break it into parts here, trying to build a sensible
 	 * cline list for these matches, too. */
 	w = dupstring(w);
@@ -866,10 +862,7 @@ comp_match(char *pfx, char *sfx, char *w, Patprog cp, Cline *clp, int qu,
 	Cline pli, plil;
 	int mpl, rpl, wl;
 
-	w = (qu ? quotename(w, NULL) : dupstring(w));
-	if (qu == 2 && w[0] == '\\' && w[1] == '~')
-	    chuck(w);
-
+	w = (qu == 2 ? tildequote(w, 0) : multiquote(w, !qu));
 	wl = strlen(w);
 
 	/* Always try to match the prefix. */
@@ -1016,7 +1009,7 @@ bld_parts(char *str, int len, int plen, Cline *lp)
     while (len) {
 	for (t = 0, ms = bmatchers; ms && !t; ms = ms->next) {
 	    mp = ms->matcher;
-	    if (mp->flags == CMF_RIGHT && mp->wlen == -1 &&
+	    if (mp && mp->flags == CMF_RIGHT && mp->wlen < 0 &&
 		!mp->llen && len >= mp->ralen && mp->ralen &&
 		pattern_match(mp->right, str, NULL, NULL)) {
 		int olen = str - p, llen;
@@ -1136,7 +1129,7 @@ bld_line(Cpattern pat, char *line, char *lp,
 		t = 0;
 		for (ms = bmatchers; ms && !t; ms = ms->next) {
 		    mp = ms->matcher;
-		    if (!mp->flags && mp->wlen <= wlen && mp->llen <= l &&
+		    if (mp && !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),
@@ -1186,7 +1179,7 @@ join_strs(int la, char *sa, int lb, char *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 &&
+		if (mp && !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. */
@@ -1373,7 +1366,7 @@ join_sub(Cmdata md, char *str, int len, int *mlen, int sfx, int join)
 	    /* 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) {
+	    if (mp && !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 &&
@@ -1684,7 +1677,7 @@ join_mid(Cline o, Cline n)
  * didn't. */
 
 /**/
-static void
+static int
 sub_join(Cline a, Cline b, Cline e, int anew)
 {
     if (!e->suffix && a->prefix) {
@@ -1707,31 +1700,28 @@ sub_join(Cline a, Cline b, Cline e, int anew)
 	*p = e->prefix;
 	ca = a->prefix;
 
-	while (n != op) {
+	while (n) {
 	    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;
-		}
+		if (e->prefix)
+		    return max - min;
 	    } else {
-		join_psfx(e, a, NULL, NULL, 0);
-		if (a->prefix) {
-		    a->min += min;
-		    a->max += max;
-		    break;
-		}
+		join_psfx(a, e, NULL, NULL, 0);
+		if (a->prefix)
+		    return max - min;
 	    }
 	    min -= n->min;
-	    max -= n->max;
 
+	    if (n == op)
+		break;
 	    n = n->next;
 	}
+	return max - min;
     }
+    return 0;
 }
 
 /* This simplifies the cline list given as the first argument so that
@@ -1748,7 +1738,8 @@ join_clines(Cline o, Cline n)
     if (!o)
 	return n;
     else {
-	Cline oo = o, nn = n, po = NULL, pn = NULL;
+	Cline oo = o, nn = n, po = NULL, pn = NULL, x;
+	int diff;
 
 	/* Walk through the lists. */
 	while (o && n) {
@@ -1760,7 +1751,7 @@ join_clines(Cline o, Cline n)
 
 		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);
+		    diff = sub_join(n, o, tn, 1);
 
 		    if (po)
 			po->next = tn;
@@ -1768,8 +1759,15 @@ join_clines(Cline o, Cline n)
 			oo = tn;
 		    t->next = NULL;
 		    free_cline(o);
+		    x = o;
 		    o = tn;
-		    o->flags |= CLF_MISS;
+		    if (po && cmp_anchors(x, po, 0)) {
+			po->flags |= CLF_MISS;
+			po->max += diff;
+		    } else {
+			o->flags |= CLF_MISS;
+			o->max += diff;
+		    }
 		    continue;
 		}
 	    }
@@ -1778,10 +1776,16 @@ join_clines(Cline o, Cline n)
 
 		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);
+		    diff = sub_join(o, n, tn, 0);
 
+		    if (po && cmp_anchors(n, pn, 0)) {
+			po->flags |= CLF_MISS;
+			po->max += diff;
+		    } else {
+			o->flags |= CLF_MISS;
+			o->max += diff;
+		    }
 		    n = tn;
-		    o->flags |= CLF_MISS;
 		    continue;
 		}
 	    }
@@ -1809,6 +1813,7 @@ join_clines(Cline o, Cline n)
 		     t = tn);
 		if (tn && cmp_anchors(tn, n, 1)) {
 		    sub_join(n, o, tn, 1);
+
 		    if (po)
 			po->next = tn;
 		    else
@@ -1837,24 +1842,41 @@ join_clines(Cline o, Cline n)
 		for (t = n; (tn = t->next) && !cmp_anchors(o, tn, 1); t = tn);
 
 		if (tn) {
-		    sub_join(o, n, tn, 0);
+		    diff = sub_join(o, n, tn, 0);
 
+		    if (po && cmp_anchors(n, pn, 0)) {
+			po->flags |= CLF_MISS;
+			po->max += diff;
+		    } else {
+			o->flags |= CLF_MISS;
+			o->max += diff;
+		    }
 		    n = tn;
-		    o->flags |= CLF_MISS;
+		    po = o;
+		    o = o->next;
+		    pn = n;
+		    n = n->next;
 		    continue;
 		} else {
 		    for (t = o; (tn = t->next) && !cmp_anchors(n, tn, 1);
 			 t = tn);
 
 		    if (tn) {
-			sub_join(n, o, tn, 1);
+			diff = sub_join(n, o, tn, 1);
 
 			if (po)
 			    po->next = tn;
 			else
 			    oo = tn;
+			x = o;
 			o = tn;
-			o->flags |= CLF_MISS;
+			if (po && cmp_anchors(x, po, 0)) {
+			    po->flags |= CLF_MISS;
+			    po->max += diff;
+			} else {
+			    o->flags |= CLF_MISS;
+			    o->max += diff;
+			}
 			continue;
 		    } else {
 			if (o->flags & CLF_SUF)
diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c
index fe997b12b..0d93b8727 100644
--- a/Src/Zle/compresult.c
+++ b/Src/Zle/compresult.c
@@ -28,16 +28,8 @@
  */
 
 #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
@@ -72,7 +64,8 @@ cut_cline(Cline l)
 	    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->flags & CLF_MATCHED) || (!q->prefix && !q->suffix)) &&
+	(q->word ? q->wlen : q->llen) < 3) {
 	q->word = q->line = NULL;
 	q->wlen = q->llen = 0;
     }
@@ -143,8 +136,9 @@ cline_str(Cline l, int ins, int *csp)
 
     l = cut_cline(l);
 
-    pmm = smm = dm = 0;
+    pmm = smm = dm = pcs = scs = 0;
     pm = pmax = sm = smax = d = mid = cbr = -1;
+    brp = brs = NULL;
 
     /* Get the information about the brace beginning and end we have
      * to re-insert. */
@@ -594,7 +588,7 @@ do_ambiguous(void)
     /* 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)) {
+    if (ainfo && ainfo->exact == 1 && !(fromcomp & FC_LINE)) {
 	minfo.cur = NULL;
 	do_single(ainfo->exactm);
 	invalidatelist();
@@ -616,6 +610,7 @@ do_ambiguous(void)
 	do_ambig_menu();
     } else if (ainfo) {
 	int atend = (cs == we), la, eq, tcs;
+	VARARR(char, old, we - wb);
 
 	minfo.cur = NULL;
 	minfo.asked = 0;
@@ -623,11 +618,24 @@ do_ambiguous(void)
 	fixsuffix();
 
 	/* First remove the old string from the line. */
+	tcs = cs;
 	cs = wb;
+	memcpy(old, (char *) line + wb, we - wb);
 	foredel(we - wb);
 
 	/* Now get the unambiguous string and insert it into the line. */
 	cline_str(ainfo->line, 1, NULL);
+
+	/* Sometimes the different match specs used may result in a cline
+	 * that gives an empty string. If that happened, we re-insert the
+         * old string. Unless there were matches added with -U, that is. */
+	if (!(lastend - wb) && !hasunmatched) {
+	    cs = wb;
+	    foredel(lastend - wb);
+	    inststrlen(old, 0, we - wb);
+	    lastend = we;
+	    cs = tcs;
+	}
 	if (eparq) {
 	    tcs = cs;
 	    cs = lastend;
@@ -677,7 +685,7 @@ do_ambiguous(void)
     if (uselist && (usemenu != 2 || (!listshown && !oldlist)) &&
 	((!showinglist && (!listshown || !oldlist)) ||
 	 (usemenu == 3 && !oldlist)) &&
-	(smatches >= 2 || (compforcelist && *compforcelist)))
+	(smatches >= 2 || forcelist))
 	showinglist = -2;
 
     return ret;
@@ -690,7 +698,7 @@ do_ambiguous(void)
  * (l)stat().                                                         */
 
 /**/
-int
+mod_export int
 ztat(char *nam, struct stat *buf, int ls)
 {
     char b[PATH_MAX], *p;
@@ -708,7 +716,7 @@ ztat(char *nam, struct stat *buf, int ls)
 /* Insert a single match in the command line. */
 
 /**/
-void
+mod_export void
 do_single(Cmatch m)
 {
     int l, sr = 0, scs;
@@ -726,7 +734,8 @@ do_single(Cmatch m)
 	/* We are currently not in a menu-completion, *
 	 * so set the position variables.             */
 	minfo.pos = wb;
-	minfo.we = (movetoend >= 2 || (movetoend == 1 && !menucmp));
+	minfo.we = (movetoend >= 2 || (movetoend == 1 && !menucmp) ||
+		    (!movetoend && cs == we));
 	minfo.end = we;
     }
     /* If we are already in a menu-completion or if we have done a *
@@ -792,19 +801,40 @@ do_single(Cmatch m)
 	    else {
 		/* Build the path name. */
 		if (partest && !*psuf && !(m->flags & CMF_PARNEST)) {
-		    int ne = noerrs;
+		    int ne = noerrs, tryit = 1;
 
 		    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;
+			    ((m->flags & CMF_PARBR) ? '}' : '\0'));
+		    if (*p == '$') {
+			char *n;
+			Param pm;
+
+			if (p[1] == '{') {
+			    char *e;
+
+			    n = dupstring(p + 2);
+			    e = n + strlen(n) - 1;
+
+			    if (*e == '}')
+				*e = '\0';
+			} else
+			    n = p + 1;
+
+			if ((pm = (Param) paramtab->getnode(paramtab, n)) &&
+			    PM_TYPE(pm->flags) != PM_SCALAR)
+			    tryit = 0;
+		    }
+		    if (tryit) {
+			noerrs = 1;
+			parsestr(p);
+			singsub(&p);
+			errflag = 0;
+			noerrs = ne;
+		    }
 		} else {
 		    p = (char *) zhalloc(strlen(prpre) + strlen(str) +
 				 strlen(psuf) + 3);
@@ -857,11 +887,13 @@ do_single(Cmatch m)
 	/* 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 (m->autoq && (!m->isuf || !strpfx(m->autoq, m->isuf))) {
+	    int al = strlen(m->autoq);
+	    inststrlen(m->autoq, 1, al);
+	    minfo.insc += al;
 	}
-	if (!menucmp && (usemenu != 3 || insspace)) {
+	if (!menucmp && !(m->flags & CMF_NOSPACE) &&
+	    (usemenu != 3 || insspace)) {
 	    inststrlen(" ", 1, 1);
 	    minfo.insc++;
 	    if (minfo.we)
@@ -897,7 +929,7 @@ do_single(Cmatch m)
  * insert the next completion.                                              */
 
 /**/
-void
+mod_export void
 do_menucmp(int lst)
 {
     /* Just list the matches if the list was requested. */
@@ -906,44 +938,40 @@ do_menucmp(int lst)
 	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;
+    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();
 }
 
 /**/
 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;
+    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();
 
     return 0;
 }
@@ -953,7 +981,7 @@ reverse_menu(Hookdef dummy, void *dummy2)
  * accept several selections from the list of matches.  */
 
 /**/
-int
+mod_export int
 accept_last(void)
 {
     if (!menuacc) {
@@ -1042,7 +1070,7 @@ do_ambig_menu(void)
     } else {
 	if (oldlist) {
 	    if (oldins && minfo.cur)
-		acceptlast();
+		accept_last();
 	} else
 	    minfo.cur = NULL;
     }
@@ -1074,24 +1102,6 @@ do_ambig_menu(void)
     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. */
 
 /**/
@@ -1100,14 +1110,12 @@ list_lines(void)
 {
     Cmgroup oam;
 
-    PERMALLOC {
-	permmatches(0);
-    } LASTALLOC;
+    permmatches(0);
 
     oam = amatches;
     amatches = pmatches;
     listdat.valid = 0;
-    calclist();
+    calclist(0);
     listdat.valid = 0;
     amatches = oam;
 
@@ -1124,132 +1132,25 @@ comp_list(char *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)
+skipnolist(Cmatch *p, int showall)
 {
-    while (*p && (((*p)->flags & (CMF_NOLIST | CMF_HIDE)) ||
-		  ((*p)->disp && ((*p)->flags & (CMF_DISPLINE | CMF_HIDE)))))
+    int mask = (showall ? 0 : (CMF_NOLIST | CMF_MULT)) | CMF_HIDE;
+
+    while (*p && (((*p)->flags & mask) ||
+		  ((*p)->disp &&
+		   ((*p)->flags & (CMF_DISPLINE | CMF_HIDE)))))
 	p++;
 
     return p;
 }
 
 /**/
-void
-calclist(void)
+mod_export void
+calclist(int showall)
 {
     Cmgroup g;
     Cmatch *p, m;
@@ -1259,7 +1160,7 @@ calclist(void)
     VARARR(int, mlens, nmatches + 1);
 
     if (listdat.valid && onlyexpl == listdat.onlyexpl &&
-	menuacc == listdat.menuacc &&
+	menuacc == listdat.menuacc && showall == listdat.showall &&
 	lines == listdat.lines && columns == listdat.columns)
 	return;
 
@@ -1267,6 +1168,8 @@ calclist(void)
 	char **pp = g->ylist;
 	int nl = 0, l, glong = 1, gshort = columns, ndisp = 0, totl = 0;
 
+	g->flags |= CGF_PACKED | CGF_ROWS;
+
 	if (!onlyexpl && pp) {
 	    /* We have an ylist, lets see, if it contains newlines. */
 	    hidden = 1;
@@ -1283,8 +1186,8 @@ calclist(void)
 		while ((sptr = *pp)) {
 		    while (sptr && *sptr) {
 			nlines += (nlptr = strchr(sptr, '\n'))
-			    ? 1 + (nlptr-sptr)/columns
-			    : strlen(sptr)/columns;
+			    ? 1 + (nlptr-sptr) / columns
+			    : strlen(sptr) / columns;
 			sptr = nlptr ? nlptr+1 : NULL;
 		    }
 		    nlines++;
@@ -1327,7 +1230,11 @@ calclist(void)
 			mlens[m->gnum] = l;
 		    }
 		    nlist++;
-		} else if (!(m->flags & CMF_NOLIST)) {
+		    if (!(m->flags & CMF_PACKED))
+			g->flags &= ~CGF_PACKED;
+		    if (!(m->flags & CMF_ROWS))
+			g->flags &= ~CGF_ROWS;
+		} else if (showall || !(m->flags & (CMF_NOLIST | CMF_MULT))) {
 		    l = niceztrlen(m->str);
 		    ndisp++;
 		    if (l > glong)
@@ -1337,6 +1244,10 @@ calclist(void)
 		    totl += l;
 		    mlens[m->gnum] = l;
 		    nlist++;
+		    if (!(m->flags & CMF_PACKED))
+			g->flags &= ~CGF_PACKED;
+		    if (!(m->flags & CMF_ROWS))
+			g->flags &= ~CGF_ROWS;
 		} else
 		    hidden = 1;
 	    }
@@ -1361,9 +1272,11 @@ calclist(void)
 	}
     }
     if (!onlyexpl) {
+	char **pp;
+	int *ws, tlines, tline, tcols, maxlen, nth, width, glines;
+
 	for (g = amatches; g; g = g->next) {
-	    char **pp;
-	    int glines = 0;
+	    glines = 0;
 
 	    zfree(g->widths, 0);
 	    g->widths = NULL;
@@ -1396,20 +1309,19 @@ calclist(void)
 			    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);
+			    } else if (showall ||
+				       !(m->flags & (CMF_NOLIST | CMF_MULT)))
+				glines += 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) {
+	    if (!(g->flags & CGF_PACKED))
+		continue;
+
 	    ws = g->widths = (int *) zalloc(columns * sizeof(int));
 	    memset(ws, 0, columns * sizeof(int));
 	    tlines = g->lins;
@@ -1424,11 +1336,13 @@ calclist(void)
 		    for (i = 0; *pp; i++, pp++)
 			ylens[i] = strlen(*pp) + add;
 
-		    if (isset(LISTROWSFIRST)) {
+		    if (g->flags & CGF_ROWS) {
 			int count, tcol, first, maxlines = 0, llines;
+			int beg = columns / g->shortest, end = g->cols;
+
+			while (1) {
+			    tcols = (beg + end) >> 1;
 
-			for (tcols = columns / g->shortest; tcols > g->cols;
-			     tcols--) {
 			    for (nth = first = maxlen = width = maxlines =
 				     llines = tcol = 0,
 				     count = g->dcount;
@@ -1452,17 +1366,33 @@ calclist(void)
 				ws[tcol++] = maxlen;
 				width += maxlen;
 			    }
-			    if (!count && width < columns)
+			    if (!count && width < columns &&
+				(tcols <= 0 || beg == end))
 				break;
+
+			    if (beg == end) {
+				beg--;
+				end--;
+			    } else if (width < columns) {
+				if ((end = tcols) == beg - 1)
+				    end++;
+			    } else {
+				if ((beg = tcols) - 1 == end)
+				    end++;
+			    }
 			}
 			if (tcols > g->cols)
 			    tlines = maxlines;
 		    } else {
-			for (tlines = ((g->totl + columns) / columns);
-			     tlines < g->lins; tlines++) {
+			int beg = ((g->totl + columns) / columns);
+			int end = g->lins;
+
+			while (1) {
+			    tlines = (beg + end) >> 1;
+
 			    for (pp = g->ylist, nth = tline = width =
 				     maxlen = tcols = 0;
-				 *pp; nth++, pp++) {
+				 *pp; pp++) {
 				if (ylens[nth] > maxlen)
 				    maxlen = ylens[nth];
 				if (++tline == tlines) {
@@ -1471,24 +1401,41 @@ calclist(void)
 				    ws[tcols++] = maxlen;
 				    maxlen = tline = 0;
 				}
+				nth++;
 			    }
 			    if (tline) {
 				ws[tcols++] = maxlen;
 				width += maxlen;
 			    }
-			    if (nth == yl && width < columns)
+			    if (nth == yl && width < columns &&
+				(beg == end || tlines >= g->lins))
 				break;
+
+			    if (beg == end) {
+				beg++;
+				end++;
+			    } else if (width < columns) {
+				if ((end = tlines) == beg + 1)
+				    end--;
+			    } else {
+				if ((beg = tlines) + 1 == end)
+				    end--;
+			    }
 			}
+			if (tlines > g->lins)
+			    tlines = g->lins;
 		    }
 		}
 	    } else if (g->width) {
-		if (isset(LISTROWSFIRST)) {
+		if (g->flags & CGF_ROWS) {
 		    int addlen, count, tcol, maxlines = 0, llines, i;
+		    int beg = columns / g->shortest, end = g->cols;
 		    Cmatch *first;
 
-		    for (tcols = columns / g->shortest; tcols > g->cols;
-			 tcols--) {
-			p = first = skipnolist(g->matches);
+		    while (1) {
+			tcols = (beg + end) >> 1;
+
+			p = first = skipnolist(g->matches, showall);
 			for (maxlen = width = maxlines = llines = tcol = 0,
 				 count = g->dcount;
 			     count > 0; count--) {
@@ -1497,7 +1444,7 @@ calclist(void)
 			    if (addlen > maxlen)
 				maxlen = addlen;
 			    for (i = tcols; i && *p; i--)
-				p = skipnolist(p + 1);
+				p = skipnolist(p + 1, showall);
 
 			    llines++;
 			    if (!*p) {
@@ -1510,29 +1457,46 @@ calclist(void)
 				ws[tcol++] = maxlen;
 				maxlen = 0;
 
-				p = first = skipnolist(first + 1);
+				p = first = skipnolist(first + 1, showall);
 			    }
 			}
 			if (tlines) {
 			    ws[tcol++] = maxlen;
 			    width += maxlen;
 			}
-			if (!count && width < columns)
+			if (!count && width < columns &&
+			    (tcols <= 0 || beg == end))
 			    break;
+
+			if (beg == end) {
+			    beg--;
+			    end--;
+			} else if (width < columns) {
+			    if ((end = tcols) == beg - 1)
+				end++;
+			} else {
+			    if ((beg = tcols) - 1 == end)
+				end++;
+			}
 		    }
 		    if (tcols > g->cols)
 			tlines = maxlines;
 		} else {
 		    int addlen;
+		    int smask = ((showall ? 0 : (CMF_NOLIST | CMF_MULT)) |
+				 CMF_HIDE);
+		    int beg = ((g->totl + columns) / columns);
+		    int end = g->lins;
+
+		    while (1) {
+			tlines = (beg + end) >> 1;
 
-		    for (tlines = ((g->totl + columns) / columns);
-			 tlines < g->lins; tlines++) {
 			for (p = g->matches, nth = tline = width =
 				 maxlen = tcols = 0;
-			     (m = *p); p++, nth++) {
+			     (m = *p); p++) {
 			    if (!(m->flags &
 				  (m->disp ? (CMF_DISPLINE | CMF_HIDE) :
-				   (CMF_NOLIST | CMF_HIDE)))) {
+				   smask))) {
 				addlen = mlens[m->gnum] + add;
 				if (addlen > maxlen)
 				    maxlen = addlen;
@@ -1542,15 +1506,30 @@ calclist(void)
 				    ws[tcols++] = maxlen;
 				    maxlen = tline = 0;
 				}
+				nth++;
 			    }
 			}
 			if (tline) {
 			    ws[tcols++] = maxlen;
 			    width += maxlen;
 			}
-			if (nth == g->dcount && width < columns)
+			if (nth == g->dcount && width < columns &&
+			    (beg == end || tlines >= g->lins))
 			    break;
+
+			if (beg == end) {
+			    beg++;
+			    end++;
+			} else if (width < columns) {
+			    if ((end = tlines) == beg + 1)
+				end--;
+			} else {
+			    if ((beg = tlines) + 1 == end)
+				end--;
+			}
 		    }
+		    if (tlines > g->lins)
+			tlines = g->lins;
 		}
 	    }
 	    if (tlines == g->lins) {
@@ -1584,21 +1563,23 @@ calclist(void)
     listdat.onlyexpl = onlyexpl;
     listdat.columns = columns;
     listdat.lines = lines;
+    listdat.showall = showall;
 }
 
 /**/
-int asklist(void)
+mod_export int asklist(void)
 {
     /* Set the cursor below the prompt. */
     trashzle();
     showinglist = listshown = 0;
 
-    clearflag = (isset(USEZLE) && !termflags &&
-		 complastprompt && *complastprompt);
+    clearflag = (isset(USEZLE) && !termflags && dolastprompt);
+    lastlistlen = 0;
 
     /* Maybe we have to ask if the user wants to see the list. */
     if ((!minfo.cur || !minfo.asked) &&
-	((complistmax && listdat.nlist > complistmax) ||
+	((complistmax > 0 && listdat.nlist >= complistmax) ||
+	 (complistmax < 0 && listdat.nlines <= -complistmax) ||
 	 (!complistmax && listdat.nlines >= lines))) {
 	int qup;
 	zsetterm();
@@ -1614,8 +1595,7 @@ int asklist(void)
 		tcmultout(TCUP, TCMULTUP, nlnct);
 	    } else
 		putc('\n', shout);
-	    if (minfo.cur)
-		minfo.asked = 2;
+	    minfo.asked = 2;
 	    return 1;
 	}
 	if (clearflag) {
@@ -1626,15 +1606,14 @@ int asklist(void)
 	} else
 	    putc('\n', shout);
 	settyinfo(&shttyinfo);
-	if (minfo.cur)
-	    minfo.asked = 1;
+	minfo.asked = 1;
     }
-    return 0;
+    return (minfo.asked ? minfo.asked - 1 : 0);
 }
 
 /**/
-int
-printlist(int over, CLPrintFunc printm)
+mod_export int
+printlist(int over, CLPrintFunc printm, int showall)
 {
     Cmgroup g;
     Cmatch *p, m;
@@ -1715,7 +1694,7 @@ printlist(int over, CLPrintFunc printm)
 			    while (a--)
 				putc(' ', shout);
 			}
-			pq += (isset(LISTROWSFIRST) ? 1 : nc);
+			pq += ((g->flags & CGF_ROWS) ? 1 : nc);
 			mc++;
 			n--;
 		    }
@@ -1728,10 +1707,11 @@ printlist(int over, CLPrintFunc printm)
 				tcout(TCCLEAREOD);
 			}
 		    }
-		    pp += (isset(LISTROWSFIRST) ? g->cols : 1);
+		    pp += ((g->flags & CGF_ROWS) ? g->cols : 1);
 		}
 	    }
-	} else if (!listdat.onlyexpl && g->lcount) {
+	} else if (!listdat.onlyexpl &&
+		   (g->lcount || (showall && g->mcount))) {
 	    int n = g->dcount, nl, nc, i, j, wid;
 	    Cmatch *q;
 
@@ -1765,7 +1745,7 @@ printlist(int over, CLPrintFunc printm)
 			tcout(TCCLEAREOD);
 		}
 	    }
-	    for (p = skipnolist(g->matches); n && nl--;) {
+	    for (p = skipnolist(g->matches, showall); n && nl--;) {
 		i = g->cols;
 		mc = 0;
 		q = p;
@@ -1794,14 +1774,16 @@ printlist(int over, CLPrintFunc printm)
 		    printed++;
 
 		    if (--n)
-			for (j = (isset(LISTROWSFIRST) ? 1 : nc); j && *q; j--)
-			    q = skipnolist(q + 1);
+			for (j = ((g->flags & CGF_ROWS) ? 1 : nc);
+			     j && *q; j--)
+			    q = skipnolist(q + 1, showall);
 		    mc++;
 		}
-		while (i-- > 0)
-		    printm(g, NULL, mc++, ml, (!i),
+		while (i-- > 0) {
+		    printm(g, NULL, mc, ml, (!i),
 			   (g->widths ? g->widths[mc] : g->width), NULL, NULL);
-
+		    mc++;
+		}
 		if (n) {
 		    putc('\n', shout);
 		    ml++;
@@ -1811,25 +1793,30 @@ printlist(int over, CLPrintFunc printm)
 			    tcout(TCCLEAREOD);
 		    }
 		    if (nl)
-			for (j = (isset(LISTROWSFIRST) ? g->cols : 1); j && *p; j--)
-			    p = skipnolist(p + 1);
+			for (j = ((g->flags & CGF_ROWS) ? g->cols : 1);
+			     j && *p; j--)
+			    p = skipnolist(p + 1, showall);
 		}
 	    }
 	}
-	if (g->lcount)
+	if (g->lcount || (showall && g->mcount))
 	    pnl = 1;
 	g = g->next;
     }
+    lastlistlen = 0;
     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;
+
+	    lastlistlen = listdat.nlines;
 	} else
 	    clearflag = 0, putc('\n', shout);
     } else
 	putc('\n', shout);
+
     listshown = (clearflag ? 1 : -1);
 
     return printed;
@@ -1858,9 +1845,8 @@ iprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width,
 	nicezputs(m->str, shout);
 	len = niceztrlen(m->str);
 
-	if (isset(LISTTYPES)) {
-	    if (buf)
-		putc(file_type(buf->st_mode), shout);
+	if (isset(LISTTYPES) && buf) {
+	    putc(file_type(buf->st_mode), shout);
 	    len++;
 	}
     }
@@ -1876,7 +1862,7 @@ iprintm(Cmgroup g, Cmatch *mp, int mc, int ml, int lastc, int width,
 int
 ilistmatches(Hookdef dummy, Chdata dat)
 {
-    calclist();
+    calclist(0);
 
     if (!listdat.nlines) {
 	showinglist = listshown = 0;
@@ -1885,7 +1871,7 @@ ilistmatches(Hookdef dummy, Chdata dat)
     if (asklist())
 	return 0;
 
-    printlist(0, iprintm);
+    printlist(0, iprintm, 0);
 
     return 0;
 }
@@ -1897,6 +1883,7 @@ int
 list_matches(Hookdef dummy, void *dummy2)
 {
     struct chdata dat;
+    int ret;
 
 #ifdef DEBUG
     /* Sanity check */
@@ -1909,18 +1896,20 @@ list_matches(Hookdef dummy, void *dummy2)
     dat.matches = amatches;
     dat.num = nmatches;
     dat.cur = NULL;
-    return runhookdef(COMPLISTMATCHESHOOK, (void *) &dat);
+    ret = runhookdef(COMPLISTMATCHESHOOK, (void *) &dat);
+
+    return ret;
 }
 
 /* Invalidate the completion list. */
 
 /**/
-int
+mod_export int
 invalidate_list(void)
 {
-    if (showinglist == -2)
-	listmatches();
     if (validlist) {
+	if (showinglist == -2)
+	    zrefresh();
 	freematches(lastmatches);
 	lastmatches = NULL;
 	hasoldlist = 0;
diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c
index aed3d9808..a844ee1ef 100644
--- a/Src/Zle/computil.c
+++ b/Src/Zle/computil.c
@@ -33,22 +33,32 @@
 
 /* Help for `_display'. */
 
+/* Calculation state. */
+
 typedef struct cdisp *Cdisp;
 
 struct cdisp {
-    int pre, suf, colon;
+    int pre;			/* prefix length */
+    int suf;			/* suffix length */
+    int colon;			/* number of strings with descriptions */
 };
 
+/* Calculate longest prefix and suffix and count the strings with
+ * descriptions. */
+
 static void
 cdisp_calc(Cdisp disp, char **args)
 {
     char *cp;
-    int i;
+    int i, nbc;
 
     for (; *args; args++) {
-	if ((cp = strchr(*args, ':')) && cp[1]) {
+	for (nbc = 0, cp = *args; *cp && *cp != ':'; cp++)
+	    if (*cp == '\\' && cp[1])
+		cp++, nbc++;
+	if (*cp == ':' && cp[1]) {
 	    disp->colon++;
-	    if ((i = cp - *args) > disp->pre)
+	    if ((i = cp - *args - nbc) > disp->pre)
 		disp->pre = i;
 	    if ((i = strlen(cp + 1)) > disp->suf)
 		disp->suf = i;
@@ -56,78 +66,29 @@ cdisp_calc(Cdisp disp, char **args)
     }
 }
 
-static char **
-cdisp_build(Cdisp disp, char *sep, char **args)
-{
-    int sl = strlen(sep), pre = disp->pre, suf;
-    VARARR(char, buf, disp->pre + disp->suf + sl + 1);
-    char **ret, **rp, *cp;
-
-    ret = (char **) zalloc((arrlen(args) + 1) * sizeof(char *));
-
-    memcpy(buf + pre, sep, sl);
-    suf = pre + sl;
-
-    for (rp = ret; *args; args++) {
-	if ((cp = strchr(*args, ':')) && cp[1]) {
-	    memset(buf, ' ', pre);
-	    memcpy(buf, *args, (cp - *args));
-	    strcpy(buf + suf, cp + 1);
-	    *rp++ = ztrdup(buf);
-	} else {
-	    if (cp)
-		*cp = '\0';
-	    *rp++ = ztrdup(*args);
-	    if (cp)
-		*cp = ':';
-	}
-    }
-    *rp = NULL;
-
-    return ret;
-}
-
-/**/
-static int
-bin_compdisplay(char *nam, char **args, char *ops, int func)
-{
-    struct cdisp disp;
-
-    if (incompfunc != 1) {
-	zerrnam(nam, "can only be called from completion function", NULL, 0);
-	return 1;
-    }
-    disp.pre = disp.suf = disp.colon = 0;
-
-    cdisp_calc(&disp, args + 2);
-    setaparam(args[0], cdisp_build(&disp, args[1], args + 2));
-
-    return !disp.colon;
-}
-
 /* Help fuer `_describe'. */
 
 typedef struct cdset *Cdset;
 
 struct cdstate {
-    int showd;
-    char *sep;
-    Cdset sets;
-    struct cdisp disp;
+    int showd;			/* != 0 if descriptions should be shown */
+    char *sep;			/* the separator string */
+    Cdset sets;			/* the sets of matches */
+    struct cdisp disp;		/* used to calculate the alignment */
 };
 
 struct cdset {
-    Cdset next;
-    char **opts;
-    char **strs;
-    char **matches;
+    Cdset next;			/* guess what */
+    char **opts;		/* the compadd-options */
+    char **strs;		/* the display-strings */
+    char **matches;		/* the matches (or NULL) */
 };
 
 static struct cdstate cd_state;
 static int cd_parsed = 0;
 
 static void
-free_cdsets(Cdset p)
+freecdsets(Cdset p)
 {
     Cdset n;
 
@@ -143,6 +104,8 @@ free_cdsets(Cdset p)
     }
 }
 
+/* Initialisation. Store and calculate the string and matches and so on. */
+
 static int
 cd_init(char *nam, char *sep, char **args, int disp)
 {
@@ -151,7 +114,7 @@ cd_init(char *nam, char *sep, char **args, int disp)
 
     if (cd_parsed) {
 	zsfree(cd_state.sep);
-	free_cdsets(cd_state.sets);
+	freecdsets(cd_state.sets);
     }
     setp = &(cd_state.sets);
     cd_state.sep = ztrdup(sep);
@@ -164,24 +127,20 @@ cd_init(char *nam, char *sep, char **args, int disp)
 	setp = &(set->next);
 
 	if (!(ap = get_user_var(*args))) {
-	    zerrnam(nam, "invalid argument: %s", *args, 0);
+	    zwarnnam(nam, "invalid argument: %s", *args, 0);
 	    return 1;
 	}
-	PERMALLOC {
-	    set->strs = arrdup(ap);
-	} LASTALLOC;
+	set->strs = zarrdup(ap);
 
 	if (disp)
 	    cdisp_calc(&(cd_state.disp), set->strs);
 
 	if (*++args && **args != '-') {
 	    if (!(ap = get_user_var(*args))) {
-		zerrnam(nam, "invalid argument: %s", *args, 0);
+		zwarnnam(nam, "invalid argument: %s", *args, 0);
 		return 1;
 	    }
-	    PERMALLOC {
-		set->matches = arrdup(ap);
-	    } LASTALLOC;
+	    set->matches = zarrdup(ap);
 	    args++;
 	}
 	for (ap = args; *args &&
@@ -190,15 +149,15 @@ cd_init(char *nam, char *sep, char **args, int disp)
 
 	tmp = *args;
 	*args = NULL;
-	PERMALLOC {
-	    set->opts = arrdup(ap);
-	} LASTALLOC;
+	set->opts = zarrdup(ap);
 	if ((*args = tmp))
 	    args++;
     }
     return 0;
 }
 
+/* Get the next set. */
+
 static int
 cd_get(char **params)
 {
@@ -206,15 +165,21 @@ cd_get(char **params)
 
     if ((set = cd_state.sets)) {
 	char **sd, **sdp, **md, **mdp, **ss, **ssp, **ms, **msp;
-	char **p, **mp, *cp;
+	char **p, **mp, *cp, *copy, *cpp, oldc;
 	int dl = 1, sl = 1, sepl = strlen(cd_state.sep);
 	int pre = cd_state.disp.pre, suf = cd_state.disp.suf;
 	VARARR(char, buf, pre + suf + sepl + 1);
 
 	for (p = set->strs; *p; p++)
-	    if (cd_state.showd && (cp = strchr(*p, ':')) && cp[1])
-		dl++;
-	    else
+	    if (cd_state.showd) {
+		for (cp = *p; *cp && *cp != ':'; cp++)
+		    if (*cp == '\\' && cp[1])
+			cp++;
+		if (*cp == ':' && cp[1])
+		    dl++;
+		else
+		    sl++;
+	    } else
 		sl++;
 
 	sd = (char **) zalloc(dl * sizeof(char *));
@@ -226,41 +191,44 @@ cd_get(char **params)
 	    memcpy(buf + pre, cd_state.sep, sepl);
 	    suf = pre + sepl;
 	}
+
+	/* Build the aligned display strings. */
+
 	for (sdp = sd, ssp = ss, mdp = md, msp = ms,
 		 p = set->strs, mp = set->matches; *p; p++) {
-	    if ((cp = strchr(*p, ':')) && cp[1] && cd_state.showd) {
+	    copy = dupstring(*p);
+	    for (cp = cpp = copy; *cp && *cp != ':'; cp++) {
+		if (*cp == '\\' && cp[1])
+		    cp++;
+		*cpp++ = *cp;
+	    }
+	    oldc = *cpp;
+	    *cpp = '\0';
+	    if (((cpp == cp && oldc == ':') || *cp == ':') && cp[1] &&
+		cd_state.showd) {
 		memset(buf, ' ', pre);
-		memcpy(buf, *p, (cp - *p));
+		memcpy(buf, copy, (cpp - copy));
 		strcpy(buf + suf, cp + 1);
 		*sdp++ = ztrdup(buf);
 		if (mp) {
 		    *mdp++ = ztrdup(*mp);
 		    if (*mp)
 			mp++;
-		} else {
-		    *cp = '\0';
-		    *mdp++ = ztrdup(*p);
-		    *cp = ':';
-		}
+		} else
+		    *mdp++ = ztrdup(copy);
 	    } else {
-		if (cp)
-		    *cp = '\0';
-		*ssp++ = ztrdup(*p);
+		*ssp++ = ztrdup(copy);
 		if (mp) {
 		    *msp++ = ztrdup(*mp);
 		    if (*mp)
 			mp++;
 		} else
-		    *msp++ = ztrdup(*p);
-		if (cp)
-		    *cp = ':';
+		    *msp++ = ztrdup(copy);
 	    }
 	}
 	*sdp = *ssp = *mdp = *msp = NULL;
 
-	PERMALLOC {
-	    p = arrdup(set->opts);
-	} LASTALLOC;
+	p = zarrdup(set->opts);
 
 	setaparam(params[0], p);
 	setaparam(params[1], sd);
@@ -270,7 +238,7 @@ cd_get(char **params)
 
 	cd_state.sets = set->next;
 	set->next = NULL;
-	free_cdsets(set);
+	freecdsets(set);
 
 	return 0;
     }
@@ -282,34 +250,36 @@ static int
 bin_compdescribe(char *nam, char **args, char *ops, int func)
 {
     if (incompfunc != 1) {
-	zerrnam(nam, "can only be called from completion function", NULL, 0);
+	zwarnnam(nam, "can only be called from completion function", NULL, 0);
 	return 1;
     }
     if (!args[0][0] || !args[0][1] || args[0][2]) {
-	zerrnam(nam, "invalid argument: %s", args[0], 0);
+	zwarnnam(nam, "invalid argument: %s", args[0], 0);
 	return 1;
     }
     switch (args[0][1]) {
     case 'i':
+	cd_parsed = 1;
+	return cd_init(nam, "", args + 1, 0);
     case 'I':
 	cd_parsed = 1;
-	return cd_init(nam, args[1], args + 2, (args[0][1] == 'I'));
+	return cd_init(nam, args[1], args + 2, 1);
     case 'g':
 	if (cd_parsed) {
 	    int n = arrlen(args);
 
 	    if (n != 6) {
-		zerrnam(nam, (n < 6 ? "not enough arguments" :
+		zwarnnam(nam, (n < 6 ? "not enough arguments" :
 			      "too many arguments"), NULL, 0);
 		return 1;
 	    }
 	    return cd_get(args + 1);
 	} else {
-	    zerrnam(nam, "no parsed state", NULL, 0);
+	    zwarnnam(nam, "no parsed state", NULL, 0);
 	    return 1;
 	}
     }
-    zerrnam(nam, "invalid option: %s", args[0], 0);
+    zwarnnam(nam, "invalid option: %s", args[0], 0);
     return 1;
 }
 
@@ -319,29 +289,34 @@ typedef struct cadef *Cadef;
 typedef struct caopt *Caopt;
 typedef struct caarg *Caarg;
 
+/* Cache for a set of _arguments-definitions. */
+
 struct cadef {
-    Cadef next;
-    Caopt opts;
-    int nopts, ndopts, nodopts;
-    Caarg args;
-    Caarg rest;
-    char **defs;
-    int ndefs;
-    int lastt;
-    Caopt *single;
-    char *match;
-    int argsactive;
+    Cadef next;			/* next in cache */
+    Caopt opts;			/* the options */
+    int nopts, ndopts, nodopts;	/* number of options/direct/optional direct */
+    Caarg args;			/* the normal arguments */
+    Caarg rest;			/* the rest-argument */
+    char **defs;		/* the original strings */
+    int ndefs;			/* number of ... */
+    int lastt;			/* last time this was used */
+    Caopt *single;		/* array of single-letter options */
+    char *match;		/* -M spec to use */
+    int argsactive;		/* if arguments are still allowed */
+				/* used while parsing a command line */
 };
 
+/* Description for an option. */
+
 struct caopt {
     Caopt next;
-    char *name;
-    char *descr;
-    char **xor;
-    int type;
-    Caarg args;
-    int active;
-    int num;
+    char *name;			/* option name */
+    char *descr;		/* the description */
+    char **xor;			/* if this, then not ... */
+    int type;			/* type, CAO_* */
+    Caarg args;			/* option arguments */
+    int active;			/* still allowed on command line */
+    int num;			/* it's the num'th option */
 };
 
 #define CAO_NEXT    1
@@ -349,13 +324,18 @@ struct caopt {
 #define CAO_ODIRECT 3
 #define CAO_EQUAL   4
 
+/* Description for an argument */
+
 struct caarg {
     Caarg next;
-    char *descr;
-    char *action;
-    int type;
-    char *end;
-    int num;
+    char *descr;		/* description */
+    char **xor;			/* if this, then not ... */
+    char *action;		/* what to do for it */
+    int type;			/* CAA_* below */
+    char *end;			/* end-pattern for ::<pat>:... */
+    char *opt;			/* option name if for an option */
+    int num;			/* it's the num'th argument */
+    int active;			/* still allowed on command line */
 };
 
 #define CAA_NORMAL 1
@@ -364,9 +344,13 @@ struct caarg {
 #define CAA_RARGS  4
 #define CAA_RREST  5
 
+/* The cache of parsed descriptons. */
+
 #define MAX_CACACHE 8
 static Cadef cadef_cache[MAX_CACACHE];
 
+/* Compare two arrays of strings for equality. */
+
 static int
 arrcmp(char **a, char **b)
 {
@@ -383,45 +367,54 @@ arrcmp(char **a, char **b)
     }
 }
 
+/* Memory stuff. Obviously. */
+
 static void
-free_caargs(Caarg a)
+freecaargs(Caarg a)
 {
     Caarg n;
 
     for (; a; a = n) {
 	n = a->next;
 	zsfree(a->descr);
+	if (a->xor)
+	    freearray(a->xor);
 	zsfree(a->action);
 	zsfree(a->end);
+	zsfree(a->opt);
 	zfree(a, sizeof(*a));
     }
 }
 
 static void
-free_cadef(Cadef d)
+freecadef(Cadef d)
 {
     if (d) {
 	Caopt p, n;
 
 	zsfree(d->match);
-	freearray(d->defs);
+	if (d->defs)
+	    freearray(d->defs);
 
 	for (p = d->opts; p; p = n) {
 	    n = p->next;
 	    zsfree(p->name);
 	    zsfree(p->descr);
-	    freearray(p->xor);
-	    free_caargs(p->args);
+	    if (p->xor)
+		freearray(p->xor);
+	    freecaargs(p->args);
 	    zfree(p, sizeof(*p));
 	}
-	free_caargs(d->args);
-	free_caargs(d->rest);
+	freecaargs(d->args);
+	freecaargs(d->rest);
 	if (d->single)
 	    zfree(d->single, 256 * sizeof(Caopt));
 	zfree(d, sizeof(*d));
     }
 }
 
+/* Remove backslashes before colons. */
+
 static char *
 rembslashcolon(char *s)
 {
@@ -439,16 +432,41 @@ rembslashcolon(char *s)
     return r;
 }
 
+/* Add backslashes before colons. */
+
+static char *
+bslashcolon(char *s)
+{
+    char *p, *r;
+
+    r = p = zhalloc((2 * strlen(s)) + 1);
+
+    while (*s) {
+	if (*s == ':')
+	    *p++ = '\\';
+	*p++ = *s++;
+    }
+    *p = '\0';
+
+    return r;
+}
+
+/* Parse an argument definition. */
+
 static Caarg
-parse_caarg(int mult, int type, int num, char **def)
+parse_caarg(int mult, int type, int num, char *oname, char **def)
 {
     Caarg ret = (Caarg) zalloc(sizeof(*ret));
     char *p = *def, *d, sav;
 
     ret->next = NULL;
     ret->descr = ret->action = ret->end = NULL;
+    ret->xor = NULL;
     ret->num = num;
     ret->type = type;
+    ret->opt = ztrdup(oname);
+
+    /* Get the description. */
 
     for (d = p; *p && *p != ':'; p++)
 	if (*p == '\\' && p[1])
@@ -456,6 +474,9 @@ parse_caarg(int mult, int type, int num, char **def)
     sav = *p;
     *p = '\0';
     ret->descr = ztrdup(rembslashcolon(d));
+
+    /* Get the action if there is one. */
+
     if (sav) {
 	if (mult) {
 	    for (d = ++p; *p && *p != ':'; p++)
@@ -474,6 +495,8 @@ parse_caarg(int mult, int type, int num, char **def)
     return ret;
 }
 
+/* Parse an array of definitions. */
+
 static Cadef
 parse_cadef(char *nam, char **args)
 {
@@ -485,6 +508,8 @@ parse_cadef(char *nam, char **args)
 
     nopts = ndopts = nodopts = 0;
 
+    /* First string is the auto-description definition. */
+
     for (p = args[0]; *p && (p[0] != '%' || p[1] != 'd'); p++);
 
     if (*p) {
@@ -495,6 +520,8 @@ parse_cadef(char *nam, char **args)
     } else
 	adpre = adsuf = NULL;
 
+    /* Now get the -s and -M options. */
+
     args++;
     while ((p = *args)) {
 	if (!strcmp(p, "-s"))
@@ -515,26 +542,30 @@ parse_cadef(char *nam, char **args)
     if (!*args)
 	return NULL;
 
-    PERMALLOC {
-	ret = (Cadef) zalloc(sizeof(*ret));
-	ret->next = NULL;
-	ret->opts = NULL;
-	ret->args = ret->rest = NULL;
-	ret->defs = arrdup(oargs);
-	ret->ndefs = arrlen(oargs);
-	ret->lastt = time(0);
-	if (single) {
-	    ret->single = (Caopt *) zalloc(256 * sizeof(Caopt));
-	    memset(ret->single, 0, 256 * sizeof(Caopt));
-	} else
-	    ret->single = NULL;
-	ret->match = ztrdup(match);
-    } LASTALLOC;
+    /* Looks good. Optimistically allocate the cadef structure. */
+
+    ret = (Cadef) zalloc(sizeof(*ret));
+    ret->next = NULL;
+    ret->opts = NULL;
+    ret->args = ret->rest = NULL;
+    ret->defs = zarrdup(oargs);
+    ret->ndefs = arrlen(oargs);
+    ret->lastt = time(0);
+    if (single) {
+	ret->single = (Caopt *) zalloc(256 * sizeof(Caopt));
+	memset(ret->single, 0, 256 * sizeof(Caopt));
+    } else
+	ret->single = NULL;
+    ret->match = ztrdup(match);
+
+    /* Get the definitions. */
 
     for (optp = &(ret->opts); *args; args++) {
 	p = dupstring(*args);
 	xnum = 0;
 	if (*p == '(') {
+	    /* There is a xor list, get it. */
+
 	    LinkList list = newlinklist();
 	    LinkNode node;
 	    char **xp, sav;
@@ -555,9 +586,10 @@ parse_cadef(char *nam, char **args)
 		xnum++;
 		*p = sav;
 	    }
+	    /* Oops, end-of-string. */
 	    if (*p != ')') {
-		free_cadef(ret);
-		zerrnam(nam, "invalid argument: %s", *args, 0);
+		freecadef(ret);
+		zwarnnam(nam, "invalid argument: %s", *args, 0);
 		return NULL;
 	    }
 	    xor = (char **) zalloc((xnum + 2) * sizeof(char *));
@@ -568,9 +600,10 @@ parse_cadef(char *nam, char **args)
 	    p++;
 	} else
 	    xor = NULL;
-	
+
 	if (*p == '-' || *p == '+' ||
 	    (*p == '*' && (p[1] == '-' || p[1] == '+'))) {
+	    /* It's an option. */
 	    Caopt opt;
 	    Caarg oargs = NULL;
 	    int multi, otype = CAO_NEXT, again = 0;
@@ -578,25 +611,39 @@ parse_cadef(char *nam, char **args)
 
 	    rec:
 
+	    /* Allowed more than once? */
 	    if ((multi = (*p == '*')))
 		p++;
 
-	    if ((p[0] == '-' && p[1] == '+') ||
-		(p[0] == '+' && p[1] == '-')) {
+	    if (((p[0] == '-' && p[1] == '+') ||
+		 (p[0] == '+' && p[1] == '-')) &&
+		p[2] && p[2] != ':' && p[2] != '[' &&
+		p[2] != '=' && p[2] != '-' && p[2] != '+') {
+		/* It's a -+ or +- definition. We just execute the whole
+		 * stuff twice for such things. */
 		name = ++p;
 		*p = (again ? '-' : '+');
 		again = 1 - again;
 	    } else {
 		name = p;
+		/* If it's a long option skip over the first `-'. */
 		if (p[0] == '-' && p[1] == '-')
 		    p++;
 	    }
+	    if (!p[1]) {
+		freecadef(ret);
+		zwarnnam(nam, "invalid argument: %s", *args, 0);
+		return NULL;
+	    }
+
+	    /* Skip over the name. */
 	    for (p++; *p && *p != ':' && *p != '[' &&
 		     ((*p != '-' && *p != '+' && *p != '=') ||
 		      (p[1] != ':' && p[1] != '[')); p++)
 		if (*p == '\\' && p[1])
 		    p++;
 
+	    /* The character after the option name specifies the type. */
 	    c = *p;
 	    *p = '\0';
 	    if (c == '-') {
@@ -609,14 +656,15 @@ parse_cadef(char *nam, char **args)
 		otype = CAO_EQUAL;
 		c = *++p;
 	    }
+	    /* Get the optional description, if any. */
 	    if (c == '[') {
 		for (descr = ++p; *p && *p != ']'; p++)
 		    if (*p == '\\' && p[1])
 			p++;
 
 		if (!*p) {
-		    free_cadef(ret);
-		    zerrnam(nam, "invalid option definition: %s", *args, 0);
+		    freecadef(ret);
+		    zwarnnam(nam, "invalid option definition: %s", *args, 0);
 		    return NULL;
 		}
 		*p++ = '\0';
@@ -625,10 +673,11 @@ parse_cadef(char *nam, char **args)
 		descr = NULL;
 
 	    if (c && c != ':') {
-		free_cadef(ret);
-		zerrnam(nam, "invalid option definition: %s", *args, 0);
+		freecadef(ret);
+		zwarnnam(nam, "invalid option definition: %s", *args, 0);
 		return NULL;
 	    }
+	    /* Add the option name to the xor list if not `*-...'. */
 	    if (!multi) {
 		if (!xor) {
 		    xor = (char **) zalloc(2 * sizeof(char *));
@@ -637,27 +686,39 @@ parse_cadef(char *nam, char **args)
 		xor[xnum] = ztrdup(name);
 	    }
 	    if (c == ':') {
+		/* There's at least one argument. */
+
 		Caarg *oargp = &oargs;
-		int atype, rest;
+		int atype, rest, oanum = 1;
 		char *end;
 
+		/* Loop over the arguments. */
+
 		while (c == ':') {
 		    rest = 0;
 		    end = NULL;
 
+		    /* Get the argument type. */
 		    if (*++p == ':') {
 			atype = CAA_OPT;
 			p++;
 		    } else if (*p == '*') {
 			if (*++p != ':') {
-			    for (end = ++p; *p && *p != ':'; p++)
+			    char sav;
+
+			    for (end = p++; *p && *p != ':'; p++)
 				if (*p == '\\' && p[1])
 				    p++;
+			    sav = *p;
+			    *p = '\0';
+			    end = dupstring(end);
+			    tokenize(end);
+			    *p = sav;
 			}
 			if (*p != ':') {
-			    free_cadef(ret);
-			    free_caargs(oargs);
-			    zerrnam(nam, "invalid option definition: %s",
+			    freecadef(ret);
+			    freecaargs(oargs);
+			    zwarnnam(nam, "invalid option definition: %s",
 				    *args, 0);
 			    return NULL;
 			}
@@ -672,54 +733,74 @@ parse_cadef(char *nam, char **args)
 			rest = 1;
 		    } else
 			atype = CAA_NORMAL;
-		    *oargp = parse_caarg(!rest, atype, 0, &p);
+
+		    /* And the definition. */
+
+		    *oargp = parse_caarg(!rest, atype, oanum++, name, &p);
+		    if (end)
+			(*oargp)->end = ztrdup(end);
 		    oargp = &((*oargp)->next);
 		    if (rest)
 			break;
 		    c = *p;
 		}
 	    }
-	    PERMALLOC {
-		*optp = opt = (Caopt) zalloc(sizeof(*opt));
-		optp = &((*optp)->next);
-
-		opt->next = NULL;
-		opt->name = ztrdup(name);
-		if (descr)
-		    opt->descr = ztrdup(descr);
-		else if (adpre && oargs && !oargs->next)
+	    /* Store the option definition. */
+
+	    *optp = opt = (Caopt) zalloc(sizeof(*opt));
+	    optp = &((*optp)->next);
+
+	    opt->next = NULL;
+	    opt->name = ztrdup(rembslashcolon(name));
+	    if (descr)
+		opt->descr = ztrdup(descr);
+	    else if (adpre && oargs && !oargs->next) {
+		char *d;
+
+		for (d = oargs->descr; *d; d++)
+		    if (!iblank(*d))
+			break;
+
+		if (*d)
 		    opt->descr = tricat(adpre, oargs->descr, adsuf);
 		else
 		    opt->descr = NULL;
-		opt->xor = xor;
-		opt->type = otype;
-		opt->args = oargs;
-		opt->num = nopts++;
-	    } LASTALLOC;
+	    } else
+		opt->descr = NULL;
+	    opt->xor = xor;
+	    opt->type = otype;
+	    opt->args = oargs;
+	    opt->num = nopts++;
 
 	    if (otype == CAO_DIRECT)
 		ndopts++;
 	    else if (otype == CAO_ODIRECT || otype == CAO_EQUAL)
 		nodopts++;
 
+	    /* If this is for single-letter option we also store a
+	     * pointer for the definition in the array for fast lookup. */
+
 	    if (single && name[1] && !name[2])
 		ret->single[STOUC(name[1])] = opt;
 
 	    if (again) {
+		/* Do it all again for `*-...'. */
 		p = dupstring(*args);
 		goto rec;
 	    }
 	} else if (*p == '*') {
+	    /* It's a rest-argument definition. */
+
 	    int type = CAA_REST;
 
 	    if (*++p != ':') {
-		free_cadef(ret);
-		zerrnam(nam, "invalid rest argument definition: %s", *args, 0);
+		freecadef(ret);
+		zwarnnam(nam, "invalid rest argument definition: %s", *args, 0);
 		return NULL;
 	    }
 	    if (ret->rest) {
-		free_cadef(ret);
-		zerrnam(nam, "doubled rest argument definition: %s", *args, 0);
+		freecadef(ret);
+		zwarnnam(nam, "doubled rest argument definition: %s", *args, 0);
 		return NULL;
 	    }
 	    if (*++p == ':') {
@@ -729,40 +810,49 @@ parse_cadef(char *nam, char **args)
 		} else
 		    type = CAA_RARGS;
 	    }
-	    ret->rest = parse_caarg(0, type, -1, &p);
+	    ret->rest = parse_caarg(0, type, -1, NULL, &p);
+	    ret->rest->xor = xor;
 	} else {
+	    /* It's a normal argument definition. */
+
 	    int type = CAA_NORMAL;
 	    Caarg arg, tmp, pre;
 
 	    if (idigit(*p)) {
+		/* Argment number is given. */
 		int num = 0;
 
 		while (*p && idigit(*p))
-		    num = (num * 10) + ((int) *p++);
+		    num = (num * 10) + (((int) *p++) - '0');
 
 		anum = num + 1;
 	    } else
+		/* Default number. */
 		anum++;
 
 	    if (*p != ':') {
-		free_cadef(ret);
-		zerrnam(nam, "invalid argument: %s", *args, 0);
+		freecadef(ret);
+		zwarnnam(nam, "invalid argument: %s", *args, 0);
 		return NULL;
 	    }
 	    if (*++p == ':') {
+		/* Optional argument. */
 		type = CAA_OPT;
 		p++;
 	    }
-	    arg = parse_caarg(0, type, anum - 1, &p);
+	    arg = parse_caarg(0, type, anum - 1, NULL, &p);
+	    arg->xor = xor;
+
+	    /* Sort the new definition into the existing list. */
 
 	    for (tmp = ret->args, pre = NULL;
 		 tmp && tmp->num < anum - 1;
 		 pre = tmp, tmp = tmp->next);
 
 	    if (tmp && tmp->num == anum - 1) {
-		free_cadef(ret);
-		free_caargs(arg);
-		zerrnam(nam, "doubled argument definition: %s", *args, 0);
+		freecadef(ret);
+		freecaargs(arg);
+		zwarnnam(nam, "doubled argument definition: %s", *args, 0);
 		return NULL;
 	    }
 	    arg->next = tmp;
@@ -779,13 +869,16 @@ parse_cadef(char *nam, char **args)
     return ret;
 }
 
+/* Given an array of definitions, return the cadef for it. From the cache
+ * are newly built. */
+
 static Cadef
 get_cadef(char *nam, char **args)
 {
     Cadef *p, *min, new;
     int i, na = arrlen(args);
 
-    for (i = MAX_CACACHE, p = cadef_cache, min = NULL; *p && i--; p++)
+    for (i = MAX_CACACHE, p = cadef_cache, min = NULL; *p && i; p++, i--)
 	if (*p && na == (*p)->ndefs && arrcmp(args, (*p)->defs)) {
 	    (*p)->lastt = time(0);
 
@@ -795,26 +888,36 @@ get_cadef(char *nam, char **args)
     if (i)
 	min = p;
     if ((new = parse_cadef(nam, args))) {
-	free_cadef(*min);
+	freecadef(*min);
 	*min = new;
     }
     return new;
 }
 
+/* Get the option used in a word from the line, if any. */
+
 static Caopt
 ca_get_opt(Cadef d, char *line, int full, char **end)
 {
     Caopt p;
 
-    if (full) {
-	for (p = d->opts; p; p = p->next)
-	    if (p->active && !strcmp(p->name, line))
-		return p;
-    } else {
+    /* The full string may be an option. */
+
+    for (p = d->opts; p; p = p->next)
+	if (p->active && !strcmp(p->name, line)) {
+	    if (end)
+		*end = line + strlen(line);
+
+	    return p;
+	}
+
+    if (!full) {
+	/* The string from the line probably only begins with an option. */
 	for (p = d->opts; p; p = p->next)
-	    if (p->active && p->args && p->type != CAO_NEXT &&
-		strpfx(p->name, line)) {
+	    if (p->active && ((!p->args || p->type == CAO_NEXT) ?
+			      !strcmp(p->name, line) : strpfx(p->name, line))) {
 		if (end) {
+		    /* Return a pointer to the end of the option. */
 		    int l = strlen(p->name);
 
 		    if (p->type == CAO_EQUAL && line[l] == '=')
@@ -828,12 +931,13 @@ ca_get_opt(Cadef d, char *line, int full, char **end)
     return NULL;
 }
 
+/* Same as above, only for single-letter-style. */
+
 static Caopt
 ca_get_sopt(Cadef d, char *line, int full, char **end)
 {
     Caopt p;
-
-    line++;
+    char pre = *line++;
 
     if (full) {
 	for (p = NULL; *line; line++)
@@ -844,7 +948,7 @@ ca_get_sopt(Cadef d, char *line, int full, char **end)
     } else {
 	for (p = NULL; *line; line++)
 	    if ((p = d->single[STOUC(*line)]) && p->active &&
-		p->args && p->type != CAO_NEXT) {
+		p->args && p->type != CAO_NEXT && p->name[0] == pre) {
 		if (end) {
 		    line++;
 		    if (p->type == CAO_EQUAL && *line == '=')
@@ -852,13 +956,18 @@ ca_get_sopt(Cadef d, char *line, int full, char **end)
 		    *end = line;
 		}
 		break;
-	    } else if (!p || !p->active || (line[1] && p->args))
+	    } else if (!p || !p->active || (line[1] && p->args) ||
+		       p->name[0] != pre)
 		return NULL;
+	if (p && end)
+	    *end = line;
 	return p;
     }
     return NULL;
 }
 
+/* Return the n'th argument definition. */
+
 static Caarg
 ca_get_arg(Cadef d, int n)
 {
@@ -868,14 +977,16 @@ ca_get_arg(Cadef d, int n)
 	while (a && a->num < n)
 	    a = a->next;
 
-	if (a && a->num == n)
+	if (a && a->num == n && a->active)
 	    return a;
 
-	return d->rest;
+	return (d->rest && d->rest->active ? d->rest : NULL);
     }
     return NULL;
 }
 
+/* Use a xor list, marking options as inactive. */
+
 static void
 ca_inactive(Cadef d, char **xor)
 {
@@ -885,17 +996,32 @@ ca_inactive(Cadef d, char **xor)
 	for (; *xor; xor++) {
 	    if (xor[0][0] == ':' && !xor[0][1])
 		d->argsactive = 0;
-	    else if ((opt = ca_get_opt(d, *xor, 1, NULL)))
+	    else if (xor[0][0] == '*' && !xor[0][1]) {
+		if (d->rest)
+		    d->rest->active = 0;
+	    } else if (xor[0][0] >= '0' && xor[0][0] <= '9') {
+		int n = atoi(xor[0]);
+		Caarg a = d->args;
+
+		while (a && a->num < n)
+		    a = a->next;
+
+		if (a && a->num == n)
+		    a->active = 0;
+	    } else if ((opt = ca_get_opt(d, *xor, 1, NULL)))
 		opt->active = 0;
 	}
     }
 }
 
+/* State when parsing a command line. */
+
 struct castate {
     Cadef d;
+    int nopts;
     Caarg def, ddef;
     Caopt curopt;
-    int opt, arg, argbeg, optbeg, nargbeg, restbeg;
+    int opt, arg, argbeg, optbeg, nargbeg, restbeg, curpos;
     int inopt, inrest, inarg, nth, doff, singles;
     LinkList args;
     LinkList *oargs;
@@ -904,40 +1030,55 @@ struct castate {
 static struct castate ca_laststate;
 static int ca_parsed = 0, ca_alloced = 0;
 
+/* Pars a command line. */
+
 static void
 ca_parse_line(Cadef d)
 {
     Caarg adef, ddef;
-    Caopt ptr;
+    Caopt ptr, wasopt;
     struct castate state;
-    char *line, *pe;
+    char *line, *pe, **argxor = NULL;
     int cur, doff;
     Patprog endpat = NULL;
 
+    /* Free old state. */
+
     if (ca_alloced) {
-	int i = ca_laststate.d->nopts;
+	int i = ca_laststate.nopts;
 	LinkList *p = ca_laststate.oargs;
 
 	freelinklist(ca_laststate.args, freestr);
 	while (i--)
 	    if (*p++)
 		freelinklist(p[-1], freestr);
+
+	zfree(ca_laststate.oargs, ca_laststate.d->nopts * sizeof(LinkList));
     }
+    /* Mark everything as active. */
+
     for (ptr = d->opts; ptr; ptr = ptr->next)
 	ptr->active = 1;
     d->argsactive = 1;
+    if (d->rest)
+	d->rest->active = 1;
+    for (adef = d->args; adef; adef = adef->next)
+	adef->active = 1;
+
+    /* Default values for the state. */
 
     state.d = d;
+    state.nopts = d->nopts;
     state.def = state.ddef = NULL;
     state.curopt = NULL;
     state.argbeg = state.optbeg = state.nargbeg = state.restbeg =
 	state.nth = state.inopt = state.inarg = state.opt = state.arg = 1;
     state.inrest = state.doff = state.singles = state.doff = 0;
-    PERMALLOC {
-	state.args = newlinklist();
-	state.oargs = (LinkList *) zalloc(d->nopts * sizeof(LinkList));
-	memset(state.oargs, 0, d->nopts * sizeof(LinkList));
-    } LASTALLOC;
+    state.curpos = compcurrent;
+    state.args = znewlinklist();
+    state.oargs = (LinkList *) zalloc(d->nopts * sizeof(LinkList));
+    memset(state.oargs, 0, d->nopts * sizeof(LinkList));
+
     ca_alloced = 1;
 
     memcpy(&ca_laststate, &state, sizeof(state));
@@ -947,46 +1088,67 @@ ca_parse_line(Cadef d)
 
 	return;
     }
+    /* Loop over the words from the line. */
+
     for (line = compwords[1], cur = 2, state.curopt = NULL, state.def = NULL;
 	 line; line = compwords[cur++]) {
 	ddef = adef = NULL;
 	doff = state.singles = 0;
+
+	ca_inactive(d, argxor);
+
+	/* We've a definition for an argument, skip to the next. */
+
 	if (state.def) {
 	    state.arg = 0;
-	    if (state.curopt) {
-		PERMALLOC {
-		    addlinknode(state.oargs[state.curopt->num], ztrdup(line));
-		} LASTALLOC;
-	    }
-	    state.opt = (state.def->type == CAA_OPT && line[0] && line[1]);
+	    if (state.curopt)
+		zaddlinknode(state.oargs[state.curopt->num], ztrdup(line));
+
+	    state.opt = (state.def->type == CAA_OPT);
 
 	    if (state.def->type == CAA_REST || state.def->type == CAA_RARGS ||
 		state.def->type == CAA_RREST) {
 		if (state.def->end && pattry(endpat, line)) {
 		    state.def = NULL;
 		    state.curopt = NULL;
+		    state.opt = state.arg = 1;
 		    continue;
 		}
 	    } else if ((state.def = state.def->next))
 		state.argbeg = cur;
-	    else
+	    else {
 		state.curopt = NULL;
+		state.opt = 1;
+	    }
 	} else {
-	    state.opt = (line[0] && line[1]);
-	    state.arg = 1;
+	    state.opt = state.arg = 1;
 	    state.curopt = NULL;
 	}
+	if (state.opt)
+	    state.opt = (line[0] ? (line[1] ? 2 : 1) : 0);
+
 	pe = NULL;
 
-	if (state.opt && (state.curopt = ca_get_opt(d, line, 0, &pe))) {
+	wasopt = NULL;
+
+	/* See if it's an option. */
+
+	if (state.opt == 2 && (state.curopt = ca_get_opt(d, line, 0, &pe)) &&
+	    (state.curopt->type != CAO_EQUAL || 
+	     compwords[cur] || pe[-1] == '=')) {
+
 	    ddef = state.def = state.curopt->args;
 	    doff = pe - line;
 	    state.optbeg = state.argbeg = state.inopt = cur;
-	    PERMALLOC {
-		state.oargs[state.curopt->num] = newlinklist();
-	    } LASTALLOC;
+	    state.singles = (d->single && (!pe || !*pe) &&
+			     state.curopt->name[1] && !state.curopt->name[2]);
+
+	    state.oargs[state.curopt->num] = znewlinklist();
+
 	    ca_inactive(d, state.curopt->xor);
 
+	    /* Collect the argument strings. Maybe. */
+
 	    if (state.def &&
 		(state.curopt->type == CAO_DIRECT ||
 		 (state.curopt->type == CAO_ODIRECT && pe[0]) ||
@@ -996,27 +1158,32 @@ ca_parse_line(Cadef d)
 		    state.def->type != CAA_RARGS &&
 		    state.def->type != CAA_RREST)
 		    state.def = state.def->next;
-		PERMALLOC {
-		    addlinknode(state.oargs[state.curopt->num], ztrdup(pe));
-		} LASTALLOC;
+
+		zaddlinknode(state.oargs[state.curopt->num], ztrdup(pe));
 	    }
-	    if (!state.def)
+	    if (state.def)
+		state.opt = 0;
+	    else {
+		if (!d->single || (state.curopt->name[1] && state.curopt->name[2]))
+		    wasopt = state.curopt;
 		state.curopt = NULL;
-	} else if (state.opt && d->single &&
+	    }
+	} else if (state.opt == 2 && d->single &&
 		   (state.curopt = ca_get_sopt(d, line, 0, &pe))) {
+	    /* Or maybe it's a single-letter option? */
+
 	    char *p;
 	    Caopt tmpopt;
 
 	    ddef = state.def = state.curopt->args;
 	    doff = pe - line;
 	    state.optbeg = state.argbeg = state.inopt = cur;
-	    state.singles = !*pe;
+	    state.singles = (!pe || !*pe);
 
-	    for (p = line + 1; p <= pe; p++) {
+	    for (p = line + 1; p < pe; p++) {
 		if ((tmpopt = d->single[STOUC(*p)])) {
-		    PERMALLOC {
-			state.oargs[tmpopt->num] = newlinklist();
-		    } LASTALLOC;
+		    state.oargs[tmpopt->num] = znewlinklist();
+
 		    ca_inactive(d, tmpopt->xor);
 		}
 	    }
@@ -1029,31 +1196,40 @@ ca_parse_line(Cadef d)
 		    state.def->type != CAA_RARGS &&
 		    state.def->type != CAA_RREST)
 		    state.def = state.def->next;
-		PERMALLOC {
-		    addlinknode(state.oargs[state.curopt->num], ztrdup(pe));
-		} LASTALLOC;
+
+		zaddlinknode(state.oargs[state.curopt->num], ztrdup(pe));
 	    }
-	    if (!state.def)
+	    if (state.def)
+		state.opt = 0;
+	    else
 		state.curopt = NULL;
 	} else if (state.arg) {
-	    PERMALLOC {
-		addlinknode(state.args, ztrdup(line));
-	    } LASTALLOC;
+	    /* Otherwise it's a normal argument. */
+	    if (state.inopt) {
+		state.inopt = 0;
+		state.nargbeg = cur - 1;
+	    }
 	    if ((adef = state.def = ca_get_arg(d, state.nth)) &&
 		(state.def->type == CAA_RREST ||
 		 state.def->type == CAA_RARGS)) {
 		state.inrest = 0;
-		for (; line; line = compwords[cur++]) {
-		    PERMALLOC {
-			addlinknode(state.args, ztrdup(line));
-		    } LASTALLOC;
-		}
+		state.opt = (cur == state.nargbeg + 1);
+		state.optbeg = state.nargbeg;
+		state.argbeg = cur - 1;
+
+		for (; line; line = compwords[cur++])
+		    zaddlinknode(state.args, ztrdup(line));
+
+		memcpy(&ca_laststate, &state, sizeof(state));
+		ca_laststate.ddef = NULL;
+		ca_laststate.doff = 0;
 		break;
 	    }
-	    if (state.inopt) {
-		state.inopt = 0;
-		state.nargbeg = cur - 1;
-	    }
+	    zaddlinknode(state.args, ztrdup(line));
+
+	    if (state.def)
+		argxor = state.def->xor;
+
 	    if (state.def && state.def->type != CAA_NORMAL &&
 		state.def->type != CAA_OPT && state.inarg) {
 		state.restbeg = cur;
@@ -1064,6 +1240,8 @@ ca_parse_line(Cadef d)
 	    state.nth++;
 	    state.def = NULL;
 	}
+	/* Do the end-pattern test if needed. */
+
 	if (state.def && state.curopt &&
 	    (state.def->type == CAA_RREST || state.def->type == CAA_RARGS)) {
 	    if (state.def->end)
@@ -1071,52 +1249,77 @@ ca_parse_line(Cadef d)
 	    else {
 		LinkList l = state.oargs[state.curopt->num];
 
-		for (; line; line = compwords[cur++]) {
-		    PERMALLOC {
-			addlinknode(l, line);
-		    } LASTALLOC;
-		}
+		if (cur < compcurrent)
+		    memcpy(&ca_laststate, &state, sizeof(state));
+
+		for (; line; line = compwords[cur++])
+		    zaddlinknode(l, ztrdup(line));
+
+		ca_laststate.ddef = NULL;
+		ca_laststate.doff = 0;
 		break;
 	    }
-	}
+	} else if (state.def && state.def->end)
+	    endpat = patcompile(state.def->end, 0, NULL);
+
+	/* Copy the state into the global one. */
+
 	if (cur + 1 == compcurrent) {
 	    memcpy(&ca_laststate, &state, sizeof(state));
 	    ca_laststate.ddef = NULL;
 	    ca_laststate.doff = 0;
 	} else if (cur == compcurrent && !ca_laststate.def) {
-	    if ((ca_laststate.def = ddef))
-		ca_laststate.doff = doff;
-	    else {
+	    if ((ca_laststate.def = ddef)) {
+		ca_laststate.singles = state.singles;
+		if (state.curopt && state.curopt->type == CAO_NEXT) {
+		    ca_laststate.ddef = ddef;
+		    ca_laststate.def = NULL;
+		    ca_laststate.opt = 1;
+		    state.curopt->active = 1;
+		} else {
+		    ca_laststate.doff = doff;
+		    ca_laststate.opt = 0;
+		}
+	    } else {
 		ca_laststate.def = adef;
 		ca_laststate.ddef = NULL;
-		ca_laststate.argbeg = state.nargbeg;
-		ca_laststate.optbeg = state.restbeg;
+		ca_laststate.optbeg = state.nargbeg;
+		ca_laststate.argbeg = state.restbeg;
 		ca_laststate.singles = state.singles;
+		if (wasopt)
+		    wasopt->active = 1;
 	    }
 	}
     }
 }
 
+/* Build a colon-list from a list. */
+
 static char *
 ca_colonlist(LinkList l)
 {
     if (l) {
 	LinkNode n;
-	int len = 1;
+	int len = 0;
 	char *p, *ret, *q;
 
-	for (n = firstnode(l); n; incnode(n))
+	for (n = firstnode(l); n; incnode(n)) {
+	    len++;
 	    for (p = (char *) getdata(n); *p; p++)
 		len += (*p == ':' ? 2 : 1);
-
+	}
 	ret = q = (char *) zalloc(len);
 
-	for (n = firstnode(l); n; incnode(n))
+	for (n = firstnode(l); n;) {
 	    for (p = (char *) getdata(n); *p; p++) {
 		if (*p == ':')
 		    *q++ = '\\';
 		*q++ = *p;
 	    }
+	    incnode(n);
+	    if (n)
+		*q++ = ':';
+	}
 	*q = '\0';
 
 	return ret;
@@ -1130,36 +1333,37 @@ bin_comparguments(char *nam, char **args, char *ops, int func)
     int min, max, n;
 
     if (incompfunc != 1) {
-	zerrnam(nam, "can only be called from completion function", NULL, 0);
+	zwarnnam(nam, "can only be called from completion function", NULL, 0);
 	return 1;
     }
     if (args[0][0] != '-' || !args[0][1] || args[0][2]) {
-	zerrnam(nam, "invalid argument: %s", args[0], 0);
+	zwarnnam(nam, "invalid argument: %s", args[0], 0);
 	return 1;
     }
     if (args[0][1] != 'i' && !ca_parsed) {
-	zerrnam(nam, "no parsed state", NULL, 0);
+	zwarnnam(nam, "no parsed state", NULL, 0);
 	return 1;
     }
     switch (args[0][1]) {
     case 'i': min = 2; max = -1; break;
     case 'D': min = 2; max =  2; break;
+    case 'C': min = 1; max =  1; break;
     case 'O': min = 4; max =  4; break;
-    case 'L': min = 3; max =  3; break;
+    case 'L': min = 3; max =  4; break;
     case 's': min = 1; max =  1; break;
     case 'M': min = 1; max =  1; break;
     case 'a': min = 0; max =  0; break;
     case 'W': min = 2; max =  2; break;
     default:
-	zerrnam(nam, "invalid option: %s", args[0], 0);
+	zwarnnam(nam, "invalid option: %s", args[0], 0);
 	return 1;
     }
     n = arrlen(args) - 1;
     if (n < min) {
-	zerrnam(nam, "not enough arguments", NULL, 0);
+	zwarnnam(nam, "not enough arguments", NULL, 0);
 	return 1;
     } else if (max >= 0 && n > max) {
-	zerrnam(nam, "too many arguments", NULL, 0);
+	zwarnnam(nam, "too many arguments", NULL, 0);
 	return 1;
     }
     switch (args[0][1]) {
@@ -1189,19 +1393,45 @@ bin_comparguments(char *nam, char **args, char *ops, int func)
 		setsparam(args[1], ztrdup(arg->descr));
 		setsparam(args[2], ztrdup(arg->action));
 
-		ignore_prefix(ca_laststate.doff);
+		if (ca_laststate.doff > 0)
+		    ignore_prefix(ca_laststate.doff);
 		if (arg->type == CAA_RARGS)
-		    restrict_range(ca_laststate.argbeg - 1,
+		    restrict_range(ca_laststate.optbeg,
 				   arrlen(compwords) - 1);
 		else if (arg->type == CAA_RREST)
-		    restrict_range(ca_laststate.optbeg - 1,
+		    restrict_range(ca_laststate.argbeg,
 				   arrlen(compwords) - 1);
 		return 0;
 	    }
 	    return 1;
 	}
+    case 'C':
+	{
+	    Caarg arg = ca_laststate.def;
+
+	    if (arg) {
+		char buf[20];
+
+		if (arg->num > 0)
+		    sprintf(buf, "%d", arg->num);
+		else
+		    strcpy(buf, "rest");
+
+		setsparam(args[1], (arg->opt ? tricat(arg->opt, "-", buf) :
+				    tricat("argument-", buf, "")));
+		return 0;
+	    }
+	    return 1;
+	}
     case 'O':
-	if (ca_laststate.opt) {
+	if ((ca_laststate.opt || (ca_laststate.doff && ca_laststate.def) ||
+	     (ca_laststate.def &&
+	      (ca_laststate.def->type == CAA_OPT ||
+	       ca_laststate.def->type >= CAA_RARGS))) &&
+	    (!ca_laststate.def || ca_laststate.def->type < CAA_RARGS ||
+	     (ca_laststate.def->type == CAA_RARGS ?
+	      (ca_laststate.curpos == ca_laststate.argbeg + 1) :
+	      (compcurrent == 1)))) {
 	    LinkList next = newlinklist();
 	    LinkList direct = newlinklist();
 	    LinkList odirect = newlinklist();
@@ -1218,14 +1448,15 @@ bin_comparguments(char *nam, char **args, char *ops, int func)
 		    default:          l = equal;   break;
 		    }
 		    if (p->descr) {
-			int len = strlen(p->name) + strlen(p->descr) + 2;
+			char *n = bslashcolon(p->name);
+			int len = strlen(n) + strlen(p->descr) + 2;
 
 			str = (char *) zhalloc(len);
-			strcpy(str, p->name);
+			strcpy(str, n);
 			strcat(str, ":");
 			strcat(str, p->descr);
 		    } else
-			str = p->name;
+			str = bslashcolon(p->name);
 		    addlinknode(l, str);
 		}
 	    }
@@ -1235,8 +1466,8 @@ bin_comparguments(char *nam, char **args, char *ops, int func)
 	    set_list_array(args[4], equal);
 
 	    return 0;
-	} else
-	    return 1;
+	}
+	return 1;
     case 'L':
 	{
 	    Caopt opt = ca_get_opt(ca_laststate.d, args[1], 1, NULL);
@@ -1245,12 +1476,16 @@ bin_comparguments(char *nam, char **args, char *ops, int func)
 		setsparam(args[2], ztrdup(opt->args->descr));
 		setsparam(args[3], ztrdup(opt->args->action));
 
+		if (args[4])
+		    setsparam(args[4], tricat(opt->name, "-1", ""));
+
 		return 0;
 	    }
 	    return 1;
 	}
     case 's':
-	if (ca_laststate.d->single && ca_laststate.singles) {
+	if (ca_laststate.d->single && ca_laststate.singles &&
+	    ca_laststate.opt) {
 	    setsparam(args[1],
 		      ztrdup(ca_laststate.ddef ?
 			     (ca_laststate.ddef->type == CAO_DIRECT ?
@@ -1258,8 +1493,8 @@ bin_comparguments(char *nam, char **args, char *ops, int func)
 			      (ca_laststate.ddef->type == CAO_EQUAL ?
 			       "equal" : "next")) : ""));
 	    return 0;
-	} else
-	    return 1;
+	}
+	return 1;
     case 'M':
 	setsparam(args[1], ztrdup(ca_laststate.d->match));
 	return 0;
@@ -1310,67 +1545,79 @@ bin_comparguments(char *nam, char **args, char *ops, int func)
 typedef struct cvdef *Cvdef;
 typedef struct cvval *Cvval;
 
+/* Definitions for _values. */
+
 struct cvdef {
-    char *descr;
-    int hassep;
-    char sep;
-    Cvdef next;
-    Cvval vals;
-    char **defs;
-    int ndefs;
-    int lastt;
+    char *descr;		/* global description */
+    int hassep;			/* multiple values allowed */
+    char sep;			/* separator character */
+    Cvdef next;			/* next in cache */
+    Cvval vals;			/* value definitions */
+    char **defs;		/* original strings */
+    int ndefs;			/* number of ... */
+    int lastt;			/* last time used */
 };
 
+/* One value definition. */
+
 struct cvval {
     Cvval next;
-    char *name;
-    char *descr;
-    char **xor;
-    int type;
-    Caarg arg;
-    int active;
+    char *name;			/* value name */
+    char *descr;		/* description */
+    char **xor;			/* xor-list */
+    int type;			/* CVV_* below */
+    Caarg arg;			/* argument definition */
+    int active;			/* still allowed */
 };
 
 #define CVV_NOARG 0
 #define CVV_ARG   1
 #define CVV_OPT   2
 
+/* Cache. */
+
 #define MAX_CVCACHE 8
 static Cvdef cvdef_cache[MAX_CVCACHE];
 
+/* Memory stuff. */
+
 static void
-free_cvdef(Cvdef d)
+freecvdef(Cvdef d)
 {
     if (d) {
 	Cvval p, n;
 
 	zsfree(d->descr);
-	freearray(d->defs);
+	if (d->defs)
+	    freearray(d->defs);
 
 	for (p = d->vals; p; p = n) {
 	    n = p->next;
 	    zsfree(p->name);
 	    zsfree(p->descr);
-	    freearray(p->xor);
-	    free_caargs(p->arg);
+	    if (p->xor)
+		freearray(p->xor);
+	    freecaargs(p->arg);
 	    zfree(p, sizeof(*p));
 	}
 	zfree(d, sizeof(*d));
     }
 }
 
+/* Parse option definitions. */
+
 static Cvdef
 parse_cvdef(char *nam, char **args)
 {
     Cvdef ret;
     Cvval val, *valp;
     Caarg arg;
-    char **oargs = args, sep, *name, *descr, *p, *q, **xor, c;
-    int xnum, multi, vtype, hassep;
+    char **oargs = args, sep = '\0', *name, *descr, *p, *q, **xor, c;
+    int xnum, multi, vtype, hassep = 0;
 
     if (args[0][0] == '-' && args[0][1] == 's' && !args[0][2]) {
 	if (args[1][0] && args[1][1]) {
-	    zerrnam(nam, "invalid separator: %s", args[1], 0);
+	    zwarnnam(nam, "invalid separator: %s", args[1], 0);
 	    return NULL;
 	}
 	hassep = 1;
@@ -1378,26 +1625,26 @@ parse_cvdef(char *nam, char **args)
 	args += 2;
     }
     if (!args[0] || !args[1]) {
-	zerrnam(nam, "not enough arguments", NULL, 0);
+	zwarnnam(nam, "not enough arguments", NULL, 0);
 	return NULL;
     }
     descr = *args++;
 
-    PERMALLOC {
-	ret = (Cvdef) zalloc(sizeof(*ret));
-	ret->descr = ztrdup(descr);
-	ret->hassep = hassep;
-	ret->sep = sep;
-	ret->next = NULL;
-	ret->vals = NULL;
-	ret->defs = arrdup(oargs);
-	ret->ndefs = arrlen(oargs);
-	ret->lastt = time(0);
-    } LASTALLOC;
+    ret = (Cvdef) zalloc(sizeof(*ret));
+    ret->descr = ztrdup(descr);
+    ret->hassep = hassep;
+    ret->sep = sep;
+    ret->next = NULL;
+    ret->vals = NULL;
+    ret->defs = zarrdup(oargs);
+    ret->ndefs = arrlen(oargs);
+    ret->lastt = time(0);
 
     for (valp = &(ret->vals); *args; args++) {
 	p = dupstring(*args);
 	xnum = 0;
+
+	/* xor list? */
 	if (*p == '(') {
 	    LinkList list = newlinklist();
 	    LinkNode node;
@@ -1420,8 +1667,8 @@ parse_cvdef(char *nam, char **args)
 		*p = sav;
 	    }
 	    if (*p != ')') {
-		free_cvdef(ret);
-		zerrnam(nam, "invalid argument: %s", *args, 0);
+		freecvdef(ret);
+		zwarnnam(nam, "invalid argument: %s", *args, 0);
 		return NULL;
 	    }
 	    xor = (char **) zalloc((xnum + 2) * sizeof(char *));
@@ -1433,18 +1680,23 @@ parse_cvdef(char *nam, char **args)
 	} else
 	    xor = NULL;
 
+	/* More than once allowed? */
 	if ((multi = (*p == '*')))
 	    p++;
 
+	/* Skip option name. */
+
 	for (name = p; *p && *p != ':' && *p != '['; p++)
 	    if (*p == '\\' && p[1])
 		p++;
 
 	if (hassep && !sep && name + 1 != p) {
-	    free_cvdef(ret);
-	    zerrnam(nam, "no multi-letter values with empty separator allowed", NULL, 0);
+	    freecvdef(ret);
+	    zwarnnam(nam, "no multi-letter values with empty separator allowed", NULL, 0);
 	    return NULL;
 	}
+	/* Optional description? */
+
 	if ((c = *p) == '[') {
 	    *p = '\0';
 	    for (descr = ++p; *p && *p != ']'; p++)
@@ -1452,8 +1704,8 @@ parse_cvdef(char *nam, char **args)
 		    p++;
 
 	    if (!*p) {
-		free_cvdef(ret);
-		zerrnam(nam, "invalid value definition: %s", *args, 0);
+		freecvdef(ret);
+		zwarnnam(nam, "invalid value definition: %s", *args, 0);
 		return NULL;
 	    }
 	    *p++ = '\0';
@@ -1463,8 +1715,8 @@ parse_cvdef(char *nam, char **args)
 	    descr = NULL;
 	}
 	if (c && c != ':') {
-	    free_cvdef(ret);
-	    zerrnam(nam, "invalid value definition: %s", *args, 0);
+	    freecvdef(ret);
+	    zwarnnam(nam, "invalid value definition: %s", *args, 0);
 	    return NULL;
 	}
 	if (!multi) {
@@ -1474,10 +1726,12 @@ parse_cvdef(char *nam, char **args)
 	    }
 	    xor[xnum] = ztrdup(name);
 	}
+	/* Get argument? */
+
 	if (c == ':') {
 	    if (hassep && !sep) {
-		free_cvdef(ret);
-		zerrnam(nam, "no value with argument with empty separator allowed", NULL, 0);
+		freecvdef(ret);
+		zwarnnam(nam, "no value with argument with empty separator allowed", NULL, 0);
 		return NULL;
 	    }
 	    if (*++p == ':') {
@@ -1485,26 +1739,26 @@ parse_cvdef(char *nam, char **args)
 		vtype = CVV_OPT;
 	    } else
 		vtype = CVV_ARG;
-	    arg = parse_caarg(0, 0, 0, &p);
+	    arg = parse_caarg(0, 0, 0, name, &p);
 	} else {
 	    vtype = CVV_NOARG;
 	    arg = NULL;
 	}
-	PERMALLOC {
-	    *valp = val = (Cvval) zalloc(sizeof(*val));
-	    valp = &((*valp)->next);
-
-	    val->next = NULL;
-	    val->name = ztrdup(name);
-	    val->descr = ztrdup(descr);
-	    val->xor = xor;
-	    val->type = vtype;
-	    val->arg = arg;
-	} LASTALLOC;
+	*valp = val = (Cvval) zalloc(sizeof(*val));
+	valp = &((*valp)->next);
+
+	val->next = NULL;
+	val->name = ztrdup(name);
+	val->descr = ztrdup(descr);
+	val->xor = xor;
+	val->type = vtype;
+	val->arg = arg;
     }
     return ret;
 }
 
+/* Get the definition from the cache or newly built. */
+
 static Cvdef
 get_cvdef(char *nam, char **args)
 {
@@ -1521,12 +1775,14 @@ get_cvdef(char *nam, char **args)
     if (i)
 	min = p;
     if ((new = parse_cvdef(nam, args))) {
-	free_cvdef(*min);
+	freecvdef(*min);
 	*min = new;
     }
     return new;
 }
 
+/* Get the definition for a value. */
+
 static Cvval
 cv_get_val(Cvdef d, char *name)
 {
@@ -1539,6 +1795,8 @@ cv_get_val(Cvdef d, char *name)
     return NULL;
 }
 
+/* Handle a xor list. */
+
 static void
 cv_inactive(Cvdef d, char **xor)
 {
@@ -1551,6 +1809,8 @@ cv_inactive(Cvdef d, char **xor)
     }
 }
 
+/* Parse state. */
+
 struct cvstate {
     Cvdef d;
     Caarg def;
@@ -1561,6 +1821,8 @@ struct cvstate {
 static struct cvstate cv_laststate;
 static int cv_parsed = 0, cv_alloced = 0;
 
+/* Parse the current word. */
+
 static void
 cv_parse_word(Cvdef d)
 {
@@ -1577,9 +1839,8 @@ cv_parse_word(Cvdef d)
     state.d = d;
     state.def = NULL;
     state.val = NULL;
-    PERMALLOC {
-	state.vals = (LinkList) newlinklist();
-    } LASTALLOC;
+    state.vals = (LinkList) znewlinklist();
+
     cv_alloced = 1;
 
     if (d->hassep) {
@@ -1596,10 +1857,8 @@ cv_parse_word(Cvdef d)
 		    eq = "";
 
 		if ((ptr = cv_get_val(d, str))) {
-		    PERMALLOC {
-			addlinknode(state.vals, ztrdup(str));
-			addlinknode(state.vals, ztrdup(eq));
-		    } LASTALLOC;
+		    zaddlinknode(state.vals, ztrdup(str));
+		    zaddlinknode(state.vals, ztrdup(eq));
 
 		    cv_inactive(d, ptr->xor);
 		}
@@ -1625,10 +1884,8 @@ cv_parse_word(Cvdef d)
 			eq = "";
 
 		    if ((ptr = cv_get_val(d, str))) {
-			PERMALLOC {
-			    addlinknode(state.vals, ztrdup(str));
-			    addlinknode(state.vals, ztrdup(eq));
-			} LASTALLOC;
+			zaddlinknode(state.vals, ztrdup(str));
+			zaddlinknode(state.vals, ztrdup(eq));
 
 			cv_inactive(d, ptr->xor);
 		    }
@@ -1647,10 +1904,8 @@ cv_parse_word(Cvdef d)
 	    for (str = compprefix; *str; str++) {
 		tmp[0] = *str;
 		if ((ptr = cv_get_val(d, tmp))) {
-		    PERMALLOC {
-			addlinknode(state.vals, ztrdup(tmp));
-			addlinknode(state.vals, ztrdup(""));
-		    } LASTALLOC;
+		    zaddlinknode(state.vals, ztrdup(tmp));
+		    zaddlinknode(state.vals, ztrdup(""));
 
 		    cv_inactive(d, ptr->xor);
 		}
@@ -1658,10 +1913,8 @@ cv_parse_word(Cvdef d)
 	    for (str = compsuffix; *str; str++) {
 		tmp[0] = *str;
 		if ((ptr = cv_get_val(d, tmp))) {
-		    PERMALLOC {
-			addlinknode(state.vals, ztrdup(tmp));
-			addlinknode(state.vals, ztrdup(""));
-		    } LASTALLOC;
+		    zaddlinknode(state.vals, ztrdup(tmp));
+		    zaddlinknode(state.vals, ztrdup(""));
 
 		    cv_inactive(d, ptr->xor);
 		}
@@ -1696,35 +1949,36 @@ bin_compvalues(char *nam, char **args, char *ops, int func)
     int min, max, n;
 
     if (incompfunc != 1) {
-	zerrnam(nam, "can only be called from completion function", NULL, 0);
+	zwarnnam(nam, "can only be called from completion function", NULL, 0);
 	return 1;
     }
     if (args[0][0] != '-' || !args[0][1] || args[0][2]) {
-	zerrnam(nam, "invalid argument: %s", args[0], 0);
+	zwarnnam(nam, "invalid argument: %s", args[0], 0);
 	return 1;
     }
     if (args[0][1] != 'i' && !cv_parsed) {
-	zerrnam(nam, "no parsed state", NULL, 0);
+	zwarnnam(nam, "no parsed state", NULL, 0);
 	return 1;
     }
     switch (args[0][1]) {
     case 'i': min = 2; max = -1; break;
     case 'D': min = 2; max =  2; break;
+    case 'C': min = 1; max =  1; break;
     case 'V': min = 3; max =  3; break;
     case 's': min = 1; max =  1; break;
     case 'd': min = 1; max =  1; break;
-    case 'L': min = 3; max =  3; break;
+    case 'L': min = 3; max =  4; break;
     case 'v': min = 1; max =  1; break;
     default:
-	zerrnam(nam, "invalid option: %s", args[0], 0);
+	zwarnnam(nam, "invalid option: %s", args[0], 0);
 	return 1;
     }
     n = arrlen(args) - 1;
     if (n < min) {
-	zerrnam(nam, "not enough arguments", NULL, 0);
+	zwarnnam(nam, "not enough arguments", NULL, 0);
 	return 1;
     } else if (max >= 0 && n > max) {
-	zerrnam(nam, "too many arguments", NULL, 0);
+	zwarnnam(nam, "too many arguments", NULL, 0);
 	return 1;
     }
     switch (args[0][1]) {
@@ -1758,6 +2012,17 @@ bin_compvalues(char *nam, char **args, char *ops, int func)
 	    }
 	    return 1;
 	}
+    case 'C':
+	{
+	    Caarg arg = cv_laststate.def;
+
+	    if (arg) {
+		setsparam(args[1], ztrdup(arg->opt));
+
+		return 0;
+	    }
+	    return 1;
+	}
     case 'V':
 	{
 	    LinkList noarg = newlinklist();
@@ -1813,6 +2078,9 @@ bin_compvalues(char *nam, char **args, char *ops, int func)
 		setsparam(args[2], val->arg->descr);
 		setsparam(args[3], val->arg->action);
 
+		if (args[4])
+		    setsparam(args[4], ztrdup(val->name));
+
 		return 0;
 	    }
 	    return 1;
@@ -1838,37 +2106,508 @@ bin_compvalues(char *nam, char **args, char *ops, int func)
     return 1;
 }
 
+static int
+bin_compquote(char *nam, char **args, char *ops, int func)
+{
+    char *name;
+    struct value vbuf;
+    Value v;
+
+    /* Anything to do? */
+
+    if (!compqstack || !*compqstack)
+	return 0;
+
+    /* For all parameters given... */
+
+    while ((name = *args++)) {
+	name = dupstring(name);
+	if ((v = getvalue(&vbuf, &name, 0))) {
+	    switch (PM_TYPE(v->pm->flags)) {
+	    case PM_SCALAR:
+		{
+		    char *val = getstrvalue(v);
+
+		    val = bslashquote(val, NULL,
+				      (*compqstack == '\'' ? 1 :
+				       (*compqstack == '"' ? 2 : 0)));
+
+		    setstrvalue(v, ztrdup(val));
+		}
+		break;
+	    case PM_ARRAY:
+		{
+		    char **val = v->pm->gets.afn(v->pm);
+		    char **new = (char **) zalloc((arrlen(val) + 1) *
+						  sizeof(char *));
+		    char **p = new;
+
+		    for (; *val; val++, p++)
+			*p = ztrdup(bslashquote(*val, NULL,
+						(*compqstack == '\'' ? 1 :
+						 (*compqstack == '"' ? 2 :
+						  0))));
+		    *p = NULL;
+
+		    setarrvalue(v, new);
+		}
+		break;
+	    default:
+		zwarnnam(nam, "invalid parameter type: %s", args[-1], 0);
+	    }
+	} else
+	    zwarnnam(nam, "unknown parameter: %s", args[-1], 0);
+    }
+    return 0;
+}
+
+/* Tags stuff. */
+
+typedef struct ctags *Ctags;
+typedef struct ctset *Ctset;
+
+/* A bunch of tag sets. */
+
+struct ctags {
+    char **all;			/* all tags offered */
+    char *context;		/* the current context */
+    int init;			/* not yet used */
+    Ctset sets;			/* the tag sets */
+};
+
+/* A tag set. */
+
+struct ctset {
+    Ctset next;
+    char **tags;		/* the tags */
+    char *tag;			/* last tag checked for -A */
+    char **ptr;			/* ptr into tags for -A */
+};
+
+/* Array of tag-set infos. Index is the locallevel. */
+
+#define MAX_TAGS 256
+static Ctags comptags[MAX_TAGS];
+
+/* locallevel at last comptags -i */
+
+static int lasttaglevel;
+
+static void
+freectset(Ctset s)
+{
+    Ctset n;
+
+    while (s) {
+	n = s->next;
+
+	if (s->tags)
+	    freearray(s->tags);
+	zsfree(s->tag);
+	zfree(s, sizeof(*s));
+
+	s = n;
+    }
+}
+
+static void
+freectags(Ctags t)
+{
+    if (t) {
+	if (t->all)
+	    freearray(t->all);
+	zsfree(t->context);
+	freectset(t->sets);
+	zfree(t, sizeof(*t));
+    }
+}
+
+/* Set the tags for the current local level. */
+
+static void
+settags(int level, char **tags)
+{
+    Ctags t;
+
+    if (comptags[level])
+	freectags(comptags[level]);
+
+    comptags[level] = t = (Ctags) zalloc(sizeof(*t));
+
+    t->all = zarrdup(tags + 1);
+    t->context = ztrdup(*tags);
+    t->sets = NULL;
+    t->init = 1;
+}
+
+/* Check if an array contains a string. */
+
+static int
+arrcontains(char **a, char *s, int colon)
+{
+    char *p, *q;
+
+    while (*a) {
+	if (colon) {
+	    for (p = s, q = *a++; *p && *q && *p != ':' && *q != ':'; p++, q++)
+		if (*p != *q)
+		    break;
+	    if ((!*p || *p == ':') && (!*q || *q == ':'))
+		return 1;
+	} else if (!strcmp(*a++, s))
+	    return 1;
+    }
+    return 0;
+}
+
+static int
+bin_comptags(char *nam, char **args, char *ops, int func)
+{
+    int min, max, n, level;
+
+    if (incompfunc != 1) {
+	zwarnnam(nam, "can only be called from completion function", NULL, 0);
+	return 1;
+    }
+    if (args[0][0] != '-' || !args[0][1] ||
+	(args[0][2] && (args[0][2] != '-' || args[0][3]))) {
+	zwarnnam(nam, "invalid argument: %s", args[0], 0);
+	return 1;
+    }
+    level = locallevel - (args[0][2] ? 1 : 0);
+    if (level >= MAX_TAGS) {
+	zwarnnam(nam, "nesting level too deep", NULL, 0);
+	return 1;
+    }
+    if (args[0][1] != 'i' && args[0][1] != 'I' && !comptags[level]) {
+	zwarnnam(nam, "no tags registered", NULL, 0);
+	return 1;
+    }
+    switch (args[0][1]) {
+    case 'i': min = 2; max = -1; break;
+    case 'C': min = 1; max =  1; break;
+    case 'T': min = 0; max =  0; break;
+    case 'N': min = 0; max =  0; break;
+    case 'R': min = 1; max =  1; break;
+    case 'S': min = 1; max =  1; break;
+    case 'A': min = 2; max =  2; break;
+    default:
+	zwarnnam(nam, "invalid option: %s", args[0], 0);
+	return 1;
+    }
+    n = arrlen(args) - 1;
+    if (n < min) {
+	zwarnnam(nam, "not enough arguments", NULL, 0);
+	return 1;
+    } else if (max >= 0 && n > max) {
+	zwarnnam(nam, "too many arguments", NULL, 0);
+	return 1;
+    }
+    switch (args[0][1]) {
+    case 'i':
+	settags(level, args + 1);
+	lasttaglevel = level;
+	break;
+    case 'C':
+	setsparam(args[1], ztrdup(comptags[level]->context));
+	break;
+    case 'T':
+	return !comptags[level]->sets;
+    case 'N':
+	{
+	    Ctset s;
+
+	    if (comptags[level]->init)
+		comptags[level]->init = 0;
+	    else if ((s = comptags[level]->sets)) {
+		comptags[level]->sets = s->next;
+		s->next = NULL;
+		freectset(s);
+	    }
+	    return !comptags[level]->sets;
+	}
+    case 'R':
+	{
+	    Ctset s;
+
+	    return !((s = comptags[level]->sets) &&
+		     arrcontains(s->tags, args[1], 1));
+	}
+    case 'A':
+	{
+	    Ctset s;
+
+	    if (comptags[level] && (s = comptags[level]->sets)) {
+		char **q, *v = NULL;
+		int l = strlen(args[1]);
+
+		if (!s->tag || strcmp(s->tag, args[1])) {
+		    zsfree(s->tag);
+		    s->tag = ztrdup(args[1]);
+		    s->ptr = s->tags;
+		}
+		for (q = s->ptr; *q; q++) {
+		    if (strpfx(args[1], *q)) {
+			if (!(*q)[l]) {
+			    v = *q;
+			    break;
+			} else if ((*q)[l] == ':') {
+			    v = (*q) + l + 1;
+			    break;
+			}
+		    }
+		}
+		if (!v) {
+		    zsfree(s->tag);
+		    s->tag = NULL;
+		    return 1;
+		}
+		s->ptr = q + 1;
+		setsparam(args[2], ztrdup(*v == '-' ? dyncat(args[1], v) : v));
+		return 0;
+	    }
+	    return 1;
+	}
+    case 'S':
+	if (comptags[level]->sets) {
+	    char **ret;
+
+	    ret = zarrdup(comptags[level]->sets->tags);
+	    setaparam(args[1], ret);
+	} else
+	    return 1;
+
+	break;
+    }
+    return 0;
+}
+
+static int
+bin_comptry(char *nam, char **args, char *ops, int func)
+{
+    if (incompfunc != 1) {
+	zwarnnam(nam, "can only be called from completion function", NULL, 0);
+	return 1;
+    }
+    if (!lasttaglevel || !comptags[lasttaglevel]) {
+	zwarnnam(nam, "no tags registered", NULL, 0);
+	return 1;
+    }
+    if (*args) {
+	if (!strcmp(*args, "-m")) {
+	    char *s, *p, *q, *c, **all = comptags[lasttaglevel]->all;
+	    LinkList list = newlinklist();
+	    LinkNode node;
+	    int num = 0;
+	    Ctset set;
+
+	    while ((s = *++args)) {
+		while (*s) {
+		    while (*s && iblank(*s))
+			s++;
+		    for (p = q = s, c = NULL; *s && !iblank(*s); s++) {
+			if (!c && *s == ':')
+			    c = p;
+			if (*s == '\\' && s[1])
+			    s++;
+			*p++ = *s;
+		    }
+		    if (*s)
+			s++;
+		    *p = '\0';
+		    if (*q) {
+			char *qq = dupstring(q);
+			if (c)
+			    *c = '\0';
+
+			tokenize(qq);
+			if (haswilds(qq)) {
+			    Patprog prog;
+
+			    if ((prog = patcompile(qq, PAT_STATIC, NULL))) {
+				char **a, *n;
+				int l = (c ? strlen(c + 1) + 2 : 1), al;
+
+				for (a = all; *a; a++) {
+				    if (pattry(prog, *a)) {
+					n = (char *) zhalloc((al = strlen(*a)) + l);
+					strcpy(n, *a);
+					if (c) {
+					    n[al] = ':';
+					    strcpy(n + al + 1, c + 1);
+					}
+					addlinknode(list, n);
+					num++;
+				    }
+				}
+			    }
+			} else if (arrcontains(all, q, 0)) {
+			    for (set = comptags[lasttaglevel]->sets; set;
+				 set = set->next)
+				if (arrcontains(set->tags, q, 0))
+				    break;
+			    if (!set) {
+				addlinknode(list, q);
+				num++;
+			    }
+			}
+			if (c)
+			    *c = ':';
+		    }
+		}
+		if (num) {
+		    char **a;
+		    Ctset l;
+
+		    set = (Ctset) zalloc(sizeof(*set));
+
+		    a = set->tags = (char **) zalloc((num + 1) * sizeof(char *));
+		    for (node = firstnode(list); node; incnode(node))
+			*a++ = ztrdup((char *) getdata(node));
+
+		    *a = NULL;
+		    set->next = NULL;
+		    set->ptr = NULL;
+		    set->tag = NULL;
+
+		    if ((l = comptags[lasttaglevel]->sets)) {
+			while (l->next)
+			    l = l->next;
+
+			l->next = set;
+		    } else
+			comptags[lasttaglevel]->sets = set;
+		}
+	    }
+	} else {
+	    char **p, **q, **all;
+	    int sep = 0;
+
+	    if ((sep = !strcmp(*args, "-s")))
+		args++;
+
+	    for (p = q = args, all = comptags[lasttaglevel]->all; *p; p++)
+		if (arrcontains(all, *p, 1)) {
+		    Ctset s;
+
+		    for (s = comptags[lasttaglevel]->sets; s; s = s->next)
+			if (arrcontains(s->tags, *p, 0))
+			    break;
+
+		    if (!s)
+			*q++ = *p;
+		}
+	    *q = NULL;
+
+	    if (*args) {
+		char *dummy[2];
+
+		do {
+		    Ctset s = (Ctset) zalloc(sizeof(*s)), l;
+
+		    if (sep) {
+			dummy[0] = *args++;
+			dummy[1] = NULL;
+			s->tags = zarrdup(dummy);
+		    } else
+			s->tags = zarrdup(args);
+		    s->next = NULL;
+		    s->ptr = NULL;
+		    s->tag = NULL;
+
+		    if ((l = comptags[lasttaglevel]->sets)) {
+			while (l->next)
+			    l = l->next;
+
+			l->next = s;
+		    } else
+			comptags[lasttaglevel]->sets = s;
+		} while (sep && *args);
+	    }
+	}
+    }
+    return 0;
+}
+
+static char *
+fmtstr(char *str, char c, char *repl)
+{
+    int len, num, rlen;
+    char *s, *ret, *rp;
+
+    len = strlen(str);
+    rlen = strlen(repl);
+
+    for (num = 0, s = str; *s; s++)
+	if (*s == '%' && s[1] == c)
+	    num++, s++;
+
+    ret = (char *) zhalloc((num * (rlen - 2)) + len + 1);
+
+    for (s = str, rp = ret; *s; s++) {
+	if (*s == '%' && s[1] == c) {
+	    strcpy(rp, repl);
+	    rp += rlen;
+	    s++;
+	} else
+	    *rp++ = *s;
+    }
+    *rp = '\0';
+
+    return ret;
+}
+
+static int
+bin_compfmt(char *nam, char **args, char *ops, int func)
+{
+    char *param = args[0], *str = args[1];
+
+    for (args += 2; *args; args++) {
+	if (args[0][1] != ':') {
+	    zwarnnam(nam, "invalid argument `%s'", args[0], 0);
+	    return 1;
+	}
+	str = fmtstr(str, **args, *args + 2);
+    }
+    setsparam(param, ztrdup(str));
+    return 0;
+}
 
 static struct builtin bintab[] = {
-    BUILTIN("compdisplay", 0, bin_compdisplay, 2, -1, 0, NULL, NULL),
     BUILTIN("compdescribe", 0, bin_compdescribe, 3, -1, 0, NULL, NULL),
     BUILTIN("comparguments", 0, bin_comparguments, 1, -1, 0, NULL, NULL),
     BUILTIN("compvalues", 0, bin_compvalues, 1, -1, 0, NULL, NULL),
+    BUILTIN("compquote", 0, bin_compquote, 1, -1, 0, NULL, NULL),
+    BUILTIN("comptags", 0, bin_comptags, 1, -1, 0, NULL, NULL),
+    BUILTIN("comptry", 0, bin_comptry, 0, -1, 0, NULL, NULL),
+    BUILTIN("compfmt", 0, bin_compfmt, 2, -1, 0, NULL, NULL),
 };
 
 
 /**/
 int
-setup_computil(Module m)
+setup_(Module m)
 {
     memset(cadef_cache, 0, sizeof(cadef_cache));
     memset(cvdef_cache, 0, sizeof(cvdef_cache));
 
+    memset(comptags, 0, sizeof(comptags));
+
+    lasttaglevel = 0;
+
     return 0;
 }
 
 /**/
 int
-boot_computil(Module m)
+boot_(Module m)
 {
     return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
 }
 
-#ifdef MODULE
-
 /**/
 int
-cleanup_computil(Module m)
+cleanup_(Module m)
 {
     deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
     return 0;
@@ -1876,16 +2615,17 @@ cleanup_computil(Module m)
 
 /**/
 int
-finish_computil(Module m)
+finish_(Module m)
 {
     int i;
 
     for (i = 0; i < MAX_CACACHE; i++)
-	free_cadef(cadef_cache[i]);
+	freecadef(cadef_cache[i]);
     for (i = 0; i < MAX_CVCACHE; i++)
-	free_cvdef(cvdef_cache[i]);
+	freecvdef(cvdef_cache[i]);
+
+    for (i = 0; i < MAX_TAGS; i++)
+	freectags(comptags[i]);
 
     return 0;
 }
-
-#endif
diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list
index 01862160e..33d36a18c 100644
--- a/Src/Zle/iwidgets.list
+++ b/Src/Zle/iwidgets.list
@@ -19,20 +19,21 @@
 "backward-kill-line", backwardkillline, ZLE_KILL | ZLE_KEEPSUFFIX
 "backward-kill-word", backwardkillword, ZLE_KILL | ZLE_KEEPSUFFIX
 "backward-word", backwardword, 0
+"beep", handlefeep, 0
 "beginning-of-buffer-or-history", beginningofbufferorhistory, 0
 "beginning-of-history", beginningofhistory, 0
 "beginning-of-line", beginningofline, 0
 "beginning-of-line-hist", beginningoflinehist, 0
 "capitalize-word", capitalizeword, 0
-"clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
-"complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX
-"copy-prev-word", copyprevword, 0
+"clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
+"complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
+"copy-prev-word", copyprevword, ZLE_KEEPSUFFIX
 "copy-region-as-kill", copyregionaskill, ZLE_KEEPSUFFIX
 "delete-char", deletechar, ZLE_KEEPSUFFIX
-"delete-char-or-list", deletecharorlist, ZLE_MENUCMP | ZLE_KEEPSUFFIX
+"delete-char-or-list", deletecharorlist, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
 "delete-word", deleteword, ZLE_KEEPSUFFIX
 "describe-key-briefly", describekeybriefly, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
-"digit-argument", digitargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
+"digit-argument", digitargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
 "down-case-word", downcaseword, 0
 "down-history", downhistory, 0
 "down-line-or-history", downlineorhistory, ZLE_LINEMOVE | ZLE_LASTCOL
@@ -43,13 +44,14 @@
 "end-of-history", endofhistory, 0
 "end-of-line", endofline, 0
 "end-of-line-hist", endoflinehist, 0
+"end-of-list", endoflist, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
 "exchange-point-and-mark", exchangepointandmark, 0
 "execute-last-named-cmd", NULL, 0
 "execute-named-cmd", NULL, 0
 "expand-cmd-path", expandcmdpath, 0
 "expand-history", expandhistory, 0
-"expand-or-complete", expandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX
-"expand-or-complete-prefix", expandorcompleteprefix, ZLE_MENUCMP | ZLE_KEEPSUFFIX
+"expand-or-complete", expandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
+"expand-or-complete-prefix", expandorcompleteprefix, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
 "expand-word", expandword, 0
 "forward-char", forwardchar, 0
 "forward-word", forwardword, 0
@@ -68,12 +70,12 @@
 "kill-region", killregion, ZLE_KILL | ZLE_KEEPSUFFIX
 "kill-whole-line", killwholeline, ZLE_KILL | ZLE_KEEPSUFFIX
 "kill-word", killword, ZLE_KILL | ZLE_KEEPSUFFIX
-"list-choices", listchoices, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
+"list-choices", listchoices, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_ISCOMP
 "list-expand", listexpand, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
 "magic-space", magicspace, 0
-"menu-complete", menucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX
-"menu-expand-or-complete", menuexpandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX
-"neg-argument", negargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
+"menu-complete", menucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
+"menu-expand-or-complete", menuexpandorcomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
+"neg-argument", negargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
 "overwrite-mode", overwritemode, 0
 "pound-insert", poundinsert, 0
 "push-input", pushinput, 0
@@ -83,19 +85,20 @@
 "quote-line", quoteline, 0
 "quote-region", quoteregion, 0
 "redisplay", redisplay, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
-"redo", redo, 0
-"reverse-menu-complete", reversemenucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX
+"redo", redo, ZLE_KEEPSUFFIX
+"reverse-menu-complete", reversemenucomplete, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
 "run-help", processcmd, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
 "self-insert", selfinsert, ZLE_MENUCMP | ZLE_KEEPSUFFIX
 "self-insert-unmeta", selfinsertunmeta, ZLE_MENUCMP | ZLE_KEEPSUFFIX
 "send-break", sendbreak, 0
 "set-mark-command", setmarkcommand, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
 "spell-word", spellword, 0
+"set-local-history", setlocalhistory, 0
 "transpose-chars", transposechars, 0
 "transpose-words", transposewords, 0
 "undefined-key", undefinedkey, 0
-"undo", undo, 0
-"universal-argument", universalargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
+"undo", undo, ZLE_KEEPSUFFIX
+"universal-argument", universalargument, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
 "up-case-word", upcaseword, 0
 "up-history", uphistory, 0
 "up-line-or-history", uplineorhistory, ZLE_LINEMOVE | ZLE_LASTCOL
@@ -145,8 +148,8 @@
 "vi-open-line-below", viopenlinebelow, 0
 "vi-oper-swap-case", vioperswapcase, 0
 "vi-pound-insert", vipoundinsert, 0
-"vi-put-after", viputafter, ZLE_YANK
-"vi-put-before", viputbefore, ZLE_YANK
+"vi-put-after", viputafter, ZLE_YANK | ZLE_KEEPSUFFIX
+"vi-put-before", viputbefore, ZLE_YANK | ZLE_KEEPSUFFIX
 "vi-quoted-insert", viquotedinsert, ZLE_MENUCMP | ZLE_KEEPSUFFIX
 "vi-repeat-change", virepeatchange, 0
 "vi-repeat-find", virepeatfind, 0
@@ -159,7 +162,7 @@
 "vi-set-mark", visetmark, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
 "vi-substitute", visubstitute, 0
 "vi-swap-case", viswapcase, 0
-"vi-undo-change", viundochange, 0
+"vi-undo-change", viundochange, ZLE_KEEPSUFFIX
 "vi-unindent", viunindent, 0
 "vi-up-line-or-history", viuplineorhistory, ZLE_LINEMOVE
 "vi-yank", viyank, 0
@@ -168,5 +171,5 @@
 "what-cursor-position", whatcursorposition, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
 "where-is", whereis, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
 "which-command", processcmd, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL
-"yank", yank, ZLE_YANK
-"yank-pop", yankpop, ZLE_YANK
+"yank", yank, ZLE_YANK | ZLE_KEEPSUFFIX
+"yank-pop", yankpop, ZLE_YANK | ZLE_KEEPSUFFIX
diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c
index 8fe3e7f0b..51af32e0b 100644
--- a/Src/Zle/zle_utils.c
+++ b/Src/Zle/zle_utils.c
@@ -53,7 +53,7 @@ struct cutbuffer vibuf[35];
 /**/
 char *lastline;
 /**/
-int lastlinesz, lastll;
+int lastlinesz, lastll, lastcs;
 
 /* size of line buffer */
 
@@ -73,7 +73,7 @@ sizeline(int sz)
 /* insert space for ct chars at cursor position */
 
 /**/
-void
+mod_export void
 spaceinline(int ct)
 {
     int i;
@@ -105,7 +105,7 @@ shiftchars(int to, int cnt)
 }
 
 /**/
-void
+mod_export void
 backkill(int ct, int dir)
 {
     int i = (cs -= ct);
@@ -115,7 +115,7 @@ backkill(int ct, int dir)
 }
 
 /**/
-void
+mod_export void
 forekill(int ct, int dir)
 {
     int i = cs;
@@ -191,14 +191,14 @@ cut(int i, int ct, int dir)
 }
 
 /**/
-void
+mod_export void
 backdel(int ct)
 {
     shiftchars(cs -= ct, ct);
 }
 
 /**/
-void
+mod_export void
 foredel(int ct)
 {
     shiftchars(cs, ct);
@@ -283,7 +283,7 @@ hstrnstr(char *haystack, int pos, char *needle, int len, int dir, int sens)
  * characters are read.  Case is folded.                        */
 
 /**/
-int
+mod_export int
 getzlequery(void)
 {
     int c;
@@ -409,19 +409,11 @@ showmsg(char const *msg)
 /* handle the error flag */
 
 /**/
-void
-feep(void)
-{
-    feepflag = 1;
-}
-
-/**/
-void
-handlefeep(void)
+int
+handlefeep(char **args)
 {
-    if(feepflag)
-	beep();
-    feepflag = 0;
+    zbeep();
+    return 0;
 }
 
 /***************/
@@ -446,6 +438,7 @@ initundo(void)
     curchange->del = curchange->ins = NULL;
     lastline = zalloc(lastlinesz = linesz);
     memcpy(lastline, line, lastll = ll);
+    lastcs = cs;
 }
 
 /**/
@@ -519,6 +512,8 @@ mkundoent(void)
     ch->next = NULL;
     ch->hist = histline;
     ch->off = pre;
+    ch->old_cs = lastcs;
+    ch->new_cs = cs;
     if(suf + pre == lastll)
 	ch->del = NULL;
     else
@@ -549,32 +544,36 @@ setlastline(void)
     if(lastlinesz != linesz)
 	lastline = realloc(lastline, lastlinesz = linesz);
     memcpy(lastline, line, lastll = ll);
+    lastcs = cs;
 }
 
 /* move backwards through the change list */
 
 /**/
-void
-undo(void)
+int
+undo(char **args)
 {
     handleundo();
     do {
-	if(!curchange->prev) {
-	    feep();
-	    return;
-	}
-	unapplychange(curchange = curchange->prev);
+	if(!curchange->prev)
+	    return 1;
+	if (unapplychange(curchange->prev))
+	    curchange = curchange->prev;
+	else
+	    break;
     } while(curchange->flags & CH_PREV);
     setlastline();
+    return 0;
 }
 
 /**/
-static void
+static int
 unapplychange(struct change *ch)
 {
     if(ch->hist != histline) {
-	remember_edits();
-	setline(zle_get_event(histline = ch->hist));
+	zle_setline(quietgethist(ch->hist));
+	cs = ch->new_cs;
+	return 0;
     }
     cs = ch->off;
     if(ch->ins)
@@ -589,33 +588,37 @@ unapplychange(struct change *ch)
 	    else
 		line[cs++] = STOUC(*c);
     }
+    cs = ch->old_cs;
+    return 1;
 }
 
 /* move forwards through the change list */
 
 /**/
-void
-redo(void)
+int
+redo(char **args)
 {
     handleundo();
     do {
-	if(!curchange->next) {
-	    feep();
-	    return;
-	}
-	applychange(curchange);
-	curchange = curchange->next;
+	if(!curchange->next)
+	    return 1;
+	if (applychange(curchange))
+	    curchange = curchange->next;
+	else
+	    break;
     } while(curchange->prev->flags & CH_NEXT);
     setlastline();
+    return 0;
 }
 
 /**/
-static void
+static int
 applychange(struct change *ch)
 {
     if(ch->hist != histline) {
-	remember_edits();
-	setline(zle_get_event(histline = ch->hist));
+	zle_setline(quietgethist(ch->hist));
+	cs = ch->old_cs;
+	return 0;
     }
     cs = ch->off;
     if(ch->del)
@@ -630,13 +633,15 @@ applychange(struct change *ch)
 	    else
 		line[cs++] = STOUC(*c);
     }
+    cs = ch->new_cs;
+    return 1;
 }
 
 /* vi undo: toggle between the end of the undo list and the preceding point */
 
 /**/
-void
-viundochange(void)
+int
+viundochange(char **args)
 {
     handleundo();
     if(curchange->next) {
@@ -645,6 +650,7 @@ viundochange(void)
 	    curchange = curchange->next;
 	} while(curchange->next);
 	setlastline();
+	return 0;
     } else
-	undo();
+	return undo(args);
 }