about summary refs log tree commit diff
path: root/Src/Zle/computil.c
diff options
context:
space:
mode:
authorSven Wischnowsky <wischnow@users.sourceforge.net>2001-06-13 11:08:54 +0000
committerSven Wischnowsky <wischnow@users.sourceforge.net>2001-06-13 11:08:54 +0000
commitd8144700423a6a9b390718fa51c4c93f01e4ae2f (patch)
treecc81d55f75a961c815c8a3a882db118b1a0183b6 /Src/Zle/computil.c
parent874a12ae7aa286a7e09ea6e975cfa16e18103b97 (diff)
downloadzsh-d8144700423a6a9b390718fa51c4c93f01e4ae2f.tar.gz
zsh-d8144700423a6a9b390718fa51c4c93f01e4ae2f.tar.xz
zsh-d8144700423a6a9b390718fa51c4c93f01e4ae2f.zip
*** empty log message ***
Diffstat (limited to 'Src/Zle/computil.c')
-rw-r--r--Src/Zle/computil.c2259
1 files changed, 1828 insertions, 431 deletions
diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c
index 088d89931..4d795c815 100644
--- a/Src/Zle/computil.c
+++ b/Src/Zle/computil.c
@@ -115,6 +115,7 @@ cd_init(char *nam, char *sep, char **args, int disp)
     if (cd_parsed) {
 	zsfree(cd_state.sep);
 	freecdsets(cd_state.sets);
+	cd_parsed = 0;
     }
     setp = &(cd_state.sets);
     cd_state.sep = ztrdup(sep);
@@ -153,6 +154,7 @@ cd_init(char *nam, char *sep, char **args, int disp)
 	if ((*args = tmp))
 	    args++;
     }
+    cd_parsed = 1;
     return 0;
 }
 
@@ -259,10 +261,8 @@ bin_compdescribe(char *nam, char **args, char *ops, int func)
     }
     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, 1);
     case 'g':
 	if (cd_parsed) {
@@ -293,6 +293,7 @@ typedef struct caarg *Caarg;
 
 struct cadef {
     Cadef next;			/* next in cache */
+    Cadef snext;		/* next set */
     Caopt opts;			/* the options */
     int nopts, ndopts, nodopts;	/* number of options/direct/optional direct */
     Caarg args;			/* the normal arguments */
@@ -304,8 +305,14 @@ struct cadef {
     char *match;		/* -M spec to use */
     int argsactive;		/* if arguments are still allowed */
 				/* used while parsing a command line */
+    char *set;			/* set name prefix (<name>-), shared */
+    char *sname;		/* set name */
+    int flags;			/* see CDF_* below */
+    char *nonarg;		/* pattern for non-args (-A argument) */
 };
 
+#define CDF_SEP 1
+
 /* Description for an option. */
 
 struct caopt {
@@ -317,12 +324,15 @@ struct caopt {
     Caarg args;			/* option arguments */
     int active;			/* still allowed on command line */
     int num;			/* it's the num'th option */
+    char *set;			/* set name, shared */
+    int not;			/* don't complete this option (`!...') */
 };
 
 #define CAO_NEXT    1
 #define CAO_DIRECT  2
 #define CAO_ODIRECT 3
 #define CAO_EQUAL   4
+#define CAO_OEQUAL  5
 
 /* Description for an argument */
 
@@ -335,7 +345,10 @@ struct caarg {
     char *end;			/* end-pattern for ::<pat>:... */
     char *opt;			/* option name if for an option */
     int num;			/* it's the num'th argument */
+    int min;			/* it's also this argument, using opt. args */
+    int direct;			/* number was given directly */
     int active;			/* still allowed on command line */
+    char *set;			/* set name, shared */
 };
 
 #define CAA_NORMAL 1
@@ -389,10 +402,14 @@ freecaargs(Caarg a)
 static void
 freecadef(Cadef d)
 {
-    if (d) {
-	Caopt p, n;
+    Cadef s;
+    Caopt p, n;
 
+    while (d) {
+	s = d->snext;
 	zsfree(d->match);
+	zsfree(d->set);
+	zsfree(d->sname);
 	if (d->defs)
 	    freearray(d->defs);
 
@@ -407,9 +424,11 @@ freecadef(Cadef d)
 	}
 	freecaargs(d->args);
 	freecaargs(d->rest);
+	zsfree(d->nonarg);
 	if (d->single)
 	    zfree(d->single, 256 * sizeof(Caopt));
 	zfree(d, sizeof(*d));
+	d = s;
     }
 }
 
@@ -454,7 +473,8 @@ bslashcolon(char *s)
 /* Parse an argument definition. */
 
 static Caarg
-parse_caarg(int mult, int type, int num, char *oname, char **def)
+parse_caarg(int mult, int type, int num, int opt, char *oname, char **def,
+	    char *set)
 {
     Caarg ret = (Caarg) zalloc(sizeof(*ret));
     char *p = *def, *d, sav;
@@ -463,8 +483,11 @@ parse_caarg(int mult, int type, int num, char *oname, char **def)
     ret->descr = ret->action = ret->end = NULL;
     ret->xor = NULL;
     ret->num = num;
+    ret->min = num - opt;
     ret->type = type;
     ret->opt = ztrdup(oname);
+    ret->direct = 0;
+    ret->set = set;
 
     /* Get the description. */
 
@@ -489,22 +512,69 @@ parse_caarg(int mult, int type, int num, char *oname, char **def)
 		*p = ':';
 	} else
 	    ret->action = ztrdup(rembslashcolon(p + 1));
-    }
+    } else
+	ret->action = ztrdup("");
     *def = p;
 
     return ret;
 }
 
+static Cadef
+alloc_cadef(char **args, int single, char *match, char *nonarg, int flags)
+{
+    Cadef ret;
+
+    ret = (Cadef) zalloc(sizeof(*ret));
+    ret->next = ret->snext = NULL;
+    ret->opts = NULL;
+    ret->args = ret->rest = NULL;
+    ret->nonarg = ztrdup(nonarg);
+    if (args) {
+	ret->defs = zarrdup(args);
+	ret->ndefs = arrlen(args);
+    } else {
+	ret->defs = NULL;
+	ret->ndefs = 0;
+    }
+    ret->lastt = time(0);
+    ret->set = ret->sname = NULL;
+    if (single) {
+	ret->single = (Caopt *) zalloc(256 * sizeof(Caopt));
+	memset(ret->single, 0, 256 * sizeof(Caopt));
+    } else
+	ret->single = NULL;
+    ret->match = ztrdup(match);
+    ret->flags = flags;
+
+    return ret;
+}
+
+static void
+set_cadef_opts(Cadef def)
+{
+    Caarg argp;
+    int xnum;
+
+    for (argp = def->args, xnum = 0; argp; argp = argp->next) {
+	if (!argp->direct)
+	    argp->min = argp->num - xnum;
+	if (argp->type == CAA_OPT)
+	    xnum++;
+    }
+}
+
 /* Parse an array of definitions. */
 
 static Cadef
 parse_cadef(char *nam, char **args)
 {
-    Cadef ret;
+    Cadef all, ret;
     Caopt *optp;
-    char **oargs = args, *p, *q, *match = "r:|[_-]=* r:|=*", **xor;
-    char *adpre, *adsuf;
-    int single = 0, anum = 1, xnum, nopts, ndopts, nodopts;
+    char **oargs = args, *p, *q, *match = "r:|[_-]=* r:|=*", **xor, **sargs;
+    char *adpre, *adsuf, *axor = NULL, *doset = NULL, **setp = NULL;
+    char *nonarg = NULL;
+    int single = 0, anum = 1, xnum, nopts, ndopts, nodopts, flags = 0;
+    int state = 0, not = 0;
 
     nopts = ndopts = nodopts = 0;
 
@@ -520,49 +590,102 @@ parse_cadef(char *nam, char **args)
     } else
 	adpre = adsuf = NULL;
 
-    /* Now get the -s and -M options. */
+    /* Now get the -s, -A, -S and -M options. */
 
     args++;
-    while ((p = *args)) {
-	if (!strcmp(p, "-s"))
-	    single = 1;
-	else if (p[0] == '-' && p[1] == 'M') {
-	    if (p[2])
-		match = p + 2;
-	    else if (args[1])
-		match = *++args;
-	    else {
-		args++;
+    while ((p = *args) && *p == '-' && p[1]) {
+	for (q = ++p; *q; q++)
+	    if (*q == 'M' || *q == 'A') {
+		q = "";
+		break;
+	    } else if (*q != 's' && *q != 'S')
 		break;
+
+	if (*q)
+	    break;
+
+	for (; *p; p++) {
+	    if (*p == 's')
+		single = 1;
+	    else if (*p == 'S')
+		flags |= CDF_SEP;
+	    else if (*p == 'A') {
+		if (p[1]) {
+		    nonarg = p + 1;
+		    p = "" - 1;
+		} else if (args[1])
+		    nonarg = *++args;
+		else
+		    break;
+	    } else if (*p == 'M') {
+		if (p[1]) {
+		    match = p + 1;
+		    p = "" - 1;
+		} else if (args[1])
+		    match = *++args;
+		else
+		    break;
 	    }
-	} else
+	}
+	if (*p)
 	    break;
+
 	args++;
     }
     if (!*args)
 	return NULL;
 
+    if (nonarg)
+	tokenize(nonarg = dupstring(nonarg));
+
     /* 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);
+    all = ret = alloc_cadef(oargs, single, match, nonarg, flags);
+    optp = &(ret->opts);
+    anum = 1;
+
+    sargs = args;
 
     /* Get the definitions. */
 
-    for (optp = &(ret->opts); *args; args++) {
+    for (; *args; args++) {
+        if (args[0][0] == '-' && !args[0][1] && args[1]) {
+	    if (!state) {
+		char *p;
+		int l;
+
+		if (setp)
+		    args = setp;
+		p = *++args;
+		l = strlen(p) - 1;
+		if (*p == '(' && p[l] == ')') {
+		    axor = p = dupstring(p + 1);
+		    p[l - 1] = '\0';
+		} else
+		    axor = NULL;
+		ret->set = doset = tricat(p, "-", "");
+		ret->sname = ztrdup(p);
+		state = 1;
+	    } else {
+		setp = args;
+		state = 0;
+		args = sargs - 1;
+		doset = NULL;
+		ret->nopts = nopts;
+		ret->ndopts = ndopts;
+		ret->nodopts = nodopts;
+		set_cadef_opts(ret);
+		ret = ret->snext = alloc_cadef(NULL, single, NULL, nonarg, flags);
+		optp = &(ret->opts);
+		nopts = ndopts = nodopts = 0;
+		anum = 1;
+	    }
+	    continue;
+	}
 	p = dupstring(*args);
 	xnum = 0;
+	if ((not = (*p == '!')))
+	    p++;
 	if (*p == '(') {
 	    /* There is a xor list, get it. */
 
@@ -588,16 +711,25 @@ parse_cadef(char *nam, char **args)
 	    }
 	    /* Oops, end-of-string. */
 	    if (*p != ')') {
-		freecadef(ret);
+		freecadef(all);
 		zwarnnam(nam, "invalid argument: %s", *args, 0);
 		return NULL;
 	    }
+	    if (doset && axor)
+		xnum++;
 	    xor = (char **) zalloc((xnum + 2) * sizeof(char *));
 	    for (node = firstnode(list), xp = xor; node; incnode(node), xp++)
 		*xp = ztrdup((char *) getdata(node));
+	    if (doset && axor)
+		*xp++ = ztrdup(axor);
 	    xp[0] = xp[1] = NULL;
 
 	    p++;
+	} else if (doset && axor) {
+	    xnum = 1;
+	    xor = (char **) zalloc(3 * sizeof(char *));
+	    xor[0] = ztrdup(axor);
+	    xor[1] = xor[2] = NULL;
 	} else
 	    xor = NULL;
 
@@ -607,7 +739,7 @@ parse_cadef(char *nam, char **args)
 	    Caopt opt;
 	    Caarg oargs = NULL;
 	    int multi, otype = CAO_NEXT, again = 0;
-	    char *name, *descr, c;
+	    char *name, *descr, c, *againp = NULL;
 
 	    rec:
 
@@ -621,9 +753,10 @@ parse_cadef(char *nam, char **args)
 		p[2] != '=' && p[2] != '-' && p[2] != '+') {
 		/* It's a -+ or +- definition. We just execute the whole
 		 * stuff twice for such things. */
+		againp = dupstring(p);
 		name = ++p;
 		*p = (again ? '-' : '+');
-		again = 1 - again;
+		again++;
 	    } else {
 		name = p;
 		/* If it's a long option skip over the first `-'. */
@@ -631,15 +764,17 @@ parse_cadef(char *nam, char **args)
 		    p++;
 	    }
 	    if (!p[1]) {
-		freecadef(ret);
+		freecadef(all);
 		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++)
+		     ((*p != '-' && *p != '+') ||
+		      (p[1] != ':' && p[1] != '[')) &&
+		     (*p != '=' ||
+		      (p[1] != ':' && p[1] != '[' && p[1] != '-')); p++)
 		if (*p == '\\' && p[1])
 		    p++;
 
@@ -653,8 +788,11 @@ parse_cadef(char *nam, char **args)
 		otype = CAO_ODIRECT;
 		c = *++p;
 	    } else if (c == '=') {
-		otype = CAO_EQUAL;
-		c = *++p;
+		otype = CAO_OEQUAL;
+		if ((c = *++p) == '-') {
+		    otype = CAO_EQUAL;
+		    c = *++p;
+		}
 	    }
 	    /* Get the optional description, if any. */
 	    if (c == '[') {
@@ -663,7 +801,7 @@ parse_cadef(char *nam, char **args)
 			p++;
 
 		if (!*p) {
-		    freecadef(ret);
+		    freecadef(all);
 		    zwarnnam(nam, "invalid option definition: %s", *args, 0);
 		    return NULL;
 		}
@@ -673,7 +811,7 @@ parse_cadef(char *nam, char **args)
 		descr = NULL;
 
 	    if (c && c != ':') {
-		freecadef(ret);
+		freecadef(all);
 		zwarnnam(nam, "invalid option definition: %s", *args, 0);
 		return NULL;
 	    }
@@ -681,15 +819,16 @@ parse_cadef(char *nam, char **args)
 	    if (!multi) {
 		if (!xor) {
 		    xor = (char **) zalloc(2 * sizeof(char *));
-		    xor[1] = NULL;
+		    xor[0] = xor[1] = NULL;
 		}
-		xor[xnum] = ztrdup(name);
+                zsfree(xor[xnum]);
+		xor[xnum] = ztrdup(rembslashcolon(name));
 	    }
 	    if (c == ':') {
 		/* There's at least one argument. */
 
 		Caarg *oargp = &oargs;
-		int atype, rest, oanum = 1;
+		int atype, rest, oanum = 1, onum = 0;
 		char *end;
 
 		/* Loop over the arguments. */
@@ -716,7 +855,7 @@ parse_cadef(char *nam, char **args)
 			    *p = sav;
 			}
 			if (*p != ':') {
-			    freecadef(ret);
+			    freecadef(all);
 			    freecaargs(oargs);
 			    zwarnnam(nam, "invalid option definition: %s",
 				    *args, 0);
@@ -736,7 +875,10 @@ parse_cadef(char *nam, char **args)
 
 		    /* And the definition. */
 
-		    *oargp = parse_caarg(!rest, atype, oanum++, name, &p);
+		    *oargp = parse_caarg(!rest, atype, oanum++, onum,
+					 name, &p, doset);
+		    if (atype == CAA_OPT)
+			onum++;
 		    if (end)
 			(*oargp)->end = ztrdup(end);
 		    oargp = &((*oargp)->next);
@@ -751,6 +893,7 @@ parse_cadef(char *nam, char **args)
 	    optp = &((*optp)->next);
 
 	    opt->next = NULL;
+	    opt->set = doset;
 	    opt->name = ztrdup(rembslashcolon(name));
 	    if (descr)
 		opt->descr = ztrdup(descr);
@@ -767,14 +910,15 @@ parse_cadef(char *nam, char **args)
 		    opt->descr = NULL;
 	    } else
 		opt->descr = NULL;
-	    opt->xor = xor;
+	    opt->xor = (again == 1 ? zarrdup(xor) : xor);
 	    opt->type = otype;
 	    opt->args = oargs;
 	    opt->num = nopts++;
+	    opt->not = not;
 
-	    if (otype == CAO_DIRECT)
+	    if (otype == CAO_DIRECT || otype == CAO_EQUAL)
 		ndopts++;
-	    else if (otype == CAO_ODIRECT || otype == CAO_EQUAL)
+	    else if (otype == CAO_ODIRECT || otype == CAO_OEQUAL)
 		nodopts++;
 
 	    /* If this is for single-letter option we also store a
@@ -783,9 +927,9 @@ parse_cadef(char *nam, char **args)
 	    if (single && name[1] && !name[2])
 		ret->single[STOUC(name[1])] = opt;
 
-	    if (again) {
+	    if (again == 1) {
 		/* Do it all again for `*-...'. */
-		p = dupstring(*args);
+		p = againp;
 		goto rec;
 	    }
 	} else if (*p == '*') {
@@ -793,13 +937,16 @@ parse_cadef(char *nam, char **args)
 
 	    int type = CAA_REST;
 
+	    if (not)
+		continue;
+
 	    if (*++p != ':') {
-		freecadef(ret);
+		freecadef(all);
 		zwarnnam(nam, "invalid rest argument definition: %s", *args, 0);
 		return NULL;
 	    }
 	    if (ret->rest) {
-		freecadef(ret);
+		freecadef(all);
 		zwarnnam(nam, "doubled rest argument definition: %s", *args, 0);
 		return NULL;
 	    }
@@ -810,15 +957,18 @@ parse_cadef(char *nam, char **args)
 		} else
 		    type = CAA_RARGS;
 	    }
-	    ret->rest = parse_caarg(0, type, -1, NULL, &p);
+	    ret->rest = parse_caarg(0, type, -1, 0, NULL, &p, doset);
 	    ret->rest->xor = xor;
 	} else {
 	    /* It's a normal argument definition. */
 
-	    int type = CAA_NORMAL;
+	    int type = CAA_NORMAL, direct;
 	    Caarg arg, tmp, pre;
 
-	    if (idigit(*p)) {
+	    if (not)
+		continue;
+
+	    if ((direct = idigit(*p))) {
 		/* Argment number is given. */
 		int num = 0;
 
@@ -831,7 +981,7 @@ parse_cadef(char *nam, char **args)
 		anum++;
 
 	    if (*p != ':') {
-		freecadef(ret);
+		freecadef(all);
 		zwarnnam(nam, "invalid argument: %s", *args, 0);
 		return NULL;
 	    }
@@ -840,8 +990,9 @@ parse_cadef(char *nam, char **args)
 		type = CAA_OPT;
 		p++;
 	    }
-	    arg = parse_caarg(0, type, anum - 1, NULL, &p);
+	    arg = parse_caarg(0, type, anum - 1, 0, NULL, &p, doset);
 	    arg->xor = xor;
+	    arg->direct = direct;
 
 	    /* Sort the new definition into the existing list. */
 
@@ -850,7 +1001,7 @@ parse_cadef(char *nam, char **args)
 		 pre = tmp, tmp = tmp->next);
 
 	    if (tmp && tmp->num == anum - 1) {
-		freecadef(ret);
+		freecadef(all);
 		freecaargs(arg);
 		zwarnnam(nam, "doubled argument definition: %s", *args, 0);
 		return NULL;
@@ -865,8 +1016,9 @@ parse_cadef(char *nam, char **args)
     ret->nopts = nopts;
     ret->ndopts = ndopts;
     ret->nodopts = nodopts;
-    
-    return ret;
+    set_cadef_opts(ret);
+
+    return all;
 }
 
 /* Given an array of definitions, return the cadef for it. From the cache
@@ -878,7 +1030,7 @@ 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++, i--)
+    for (i = MAX_CACACHE, p = cadef_cache, min = NULL; i && *p; p++, i--)
 	if (*p && na == (*p)->ndefs && arrcmp(args, (*p)->defs)) {
 	    (*p)->lastt = time(0);
 
@@ -920,7 +1072,8 @@ ca_get_opt(Cadef d, char *line, int full, char **end)
 		    /* Return a pointer to the end of the option. */
 		    int l = strlen(p->name);
 
-		    if (p->type == CAO_EQUAL && line[l] == '=')
+		    if ((p->type == CAO_OEQUAL || p->type == CAO_EQUAL) &&
+			line[l] == '=')
 			l++;
 
 		    *end = line + l;
@@ -934,36 +1087,39 @@ ca_get_opt(Cadef d, char *line, int full, char **end)
 /* Same as above, only for single-letter-style. */
 
 static Caopt
-ca_get_sopt(Cadef d, char *line, int full, char **end)
+ca_get_sopt(Cadef d, char *line, char **end, LinkList *lp)
 {
-    Caopt p;
+    Caopt p, pp = NULL;
     char pre = *line++;
-
-    if (full) {
-	for (p = NULL; *line; line++)
-	    if (!(p = d->single[STOUC(*line)]) || !p->active ||
-		(line[1] && p->args))
-		return NULL;
-	return p;
-    } else {
-	for (p = NULL; *line; line++)
-	    if ((p = d->single[STOUC(*line)]) && p->active &&
-		p->args && p->type != CAO_NEXT && p->name[0] == pre) {
+    LinkList l = NULL;
+
+    *lp = NULL;
+    for (p = NULL; *line; line++) {
+	if ((p = d->single[STOUC(*line)]) && p->active &&
+	    p->args && p->name[0] == pre) {
+	    if (p->type == CAO_NEXT) {
+		if (!l)
+		    *lp = l = newlinklist();
+		addlinknode(l, p);
+	    } else {
 		if (end) {
 		    line++;
-		    if (p->type == CAO_EQUAL && *line == '=')
+		    if ((p->type == CAO_OEQUAL || p->type == CAO_EQUAL) &&
+			*line == '=')
 			line++;
 		    *end = line;
 		}
+		pp = p;
 		break;
-	    } else if (!p || !p->active || (line[1] && p->args) ||
-		       p->name[0] != pre)
-		return NULL;
-	if (p && end)
-	    *end = line;
-	return p;
+	    }
+	} else if (!p || (p && !p->active))
+	    return NULL;
+	pp = (p->name[0] == pre ? p : NULL);
+	p = NULL;
     }
-    return NULL;
+    if (pp && end)
+	*end = line;
+    return pp;
 }
 
 /* Return the n'th argument definition. */
@@ -974,10 +1130,12 @@ ca_get_arg(Cadef d, int n)
     if (d->argsactive) {
 	Caarg a = d->args;
 
-	while (a && a->num < n)
+	while (a && (!a->active || n < a->min || n > a->num)) {
+            if (!a->active)
+                n++;
 	    a = a->next;
-
-	if (a && a->num == n && a->active)
+        }
+	if (a && a->min <= n && a->num >= n && a->active)
 	    return a;
 
 	return (d->rest && d->rest->active ? d->rest : NULL);
@@ -987,42 +1145,88 @@ ca_get_arg(Cadef d, int n)
 
 /* Use a xor list, marking options as inactive. */
 
-static void
-ca_inactive(Cadef d, char **xor)
+static LinkList ca_xor;
+
+static int
+ca_inactive(Cadef d, char **xor, int cur, int opts, char *optname)
 {
-    if (xor) {
+    if ((xor || opts) && cur <= compcurrent) {
 	Caopt opt;
-
-	for (; *xor; xor++) {
-	    if (xor[0][0] == ':' && !xor[0][1])
-		d->argsactive = 0;
-	    else if (xor[0][0] == '*' && !xor[0][1]) {
-		if (d->rest)
+	char *x;
+	int sl = (d->set ? strlen(d->set) : -1), set = 0;
+
+	for (; (x = (opts ? "-" : *xor)); xor++) {
+            if (optname && optname[0] == x[0] && strcmp(optname, x))
+                continue;
+	    if (ca_xor)
+		addlinknode(ca_xor, x);
+	    set = 0;
+	    if (sl > 0) {
+		if (strpfx(d->set, x)) {
+		    x += sl;
+		    set = 1;
+		} else if (!strncmp(d->set, x, sl - 1)) {
+		    Caopt p;
+
+		    for (p = d->opts; p; p = p->next)
+			if (p->set)
+			    p->active = 0;
+			
+		    x = ":";
+		    set = 1;
+		}
+	    }
+	    if (x[0] == ':' && !x[1]) {
+		if (set) {
+		    Caarg a;
+
+		    for (a = d->args; a; a = a->next)
+			if (a->set)
+			    a->active = 0;
+		    if (d->rest && (!set || d->rest->set))
+			d->rest->active = 0;
+		} else
+		    d->argsactive = 0;
+	    } else if (x[0] == '-' && !x[1]) {
+		Caopt p;
+
+		for (p = d->opts; p; p = p->next)
+		    if (!set || p->set)
+			p->active = 0;
+	    } else if (x[0] == '*' && !x[1]) {
+		if (d->rest && (!set || d->rest->set))
 		    d->rest->active = 0;
-	    } else if (xor[0][0] >= '0' && xor[0][0] <= '9') {
-		int n = atoi(xor[0]);
+	    } else if (x[0] >= '0' && x[0] <= '9') {
+		int n = atoi(x);
 		Caarg a = d->args;
 
 		while (a && a->num < n)
 		    a = a->next;
 
-		if (a && a->num == n)
+		if (a && a->num == n && (!set || a->set))
 		    a->active = 0;
-	    } else if ((opt = ca_get_opt(d, *xor, 1, NULL)))
+	    } else if ((opt = ca_get_opt(d, x, 1, NULL)) && (!set || opt->set))
 		opt->active = 0;
+
+	    if (opts)
+		break;
 	}
     }
+    return 0;
 }
 
 /* State when parsing a command line. */
 
+typedef struct castate *Castate;
+
 struct castate {
+    Castate snext;
     Cadef d;
     int nopts;
     Caarg def, ddef;
-    Caopt curopt;
-    int opt, arg, argbeg, optbeg, nargbeg, restbeg, curpos;
-    int inopt, inrest, inarg, nth, doff, singles;
+    Caopt curopt, dopt;
+    int opt, arg, argbeg, optbeg, nargbeg, restbeg, curpos, argend;
+    int inopt, inrest, inarg, nth, doff, singles, oopt, actopts;
     LinkList args;
     LinkList *oargs;
 };
@@ -1030,30 +1234,45 @@ 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)
+freecastate(Castate s)
+{
+    int i;
+    LinkList *p;
+
+    freelinklist(s->args, freestr);
+    for (i = s->nopts, p = s->oargs; i--; p++)
+	if (*p)
+	    freelinklist(*p, freestr);
+    zfree(s->oargs, s->d->nopts * sizeof(LinkList));
+}
+
+/* Parse a command line. */
+
+static int
+ca_parse_line(Cadef d, int multi, int first)
 {
     Caarg adef, ddef;
-    Caopt ptr, wasopt;
+    Caopt ptr, wasopt = NULL, dopt;
     struct castate state;
     char *line, *pe, **argxor = NULL;
-    int cur, doff;
-    Patprog endpat = NULL;
+    int cur, doff, argend, arglast, ne;
+    Patprog endpat = NULL, napat = NULL;
+    LinkList sopts = NULL;
 
     /* Free old state. */
 
-    if (ca_alloced) {
-	int i = ca_laststate.nopts;
-	LinkList *p = ca_laststate.oargs;
+    if (first && ca_alloced) {
+	Castate s = &ca_laststate, ss;
+	int f = 1;
 
-	freelinklist(ca_laststate.args, freestr);
-	while (i--)
-	    if (*p++)
-		freelinklist(p[-1], freestr);
-
-	zfree(ca_laststate.oargs, ca_laststate.d->nopts * sizeof(LinkList));
+	while (s) {
+	    ss = s->snext;
+	    freecastate(s);
+	    if (!f)
+		zfree(s, sizeof(*s));
+	    s = ss;
+	}
     }
     /* Mark everything as active. */
 
@@ -1067,13 +1286,15 @@ ca_parse_line(Cadef d)
 
     /* Default values for the state. */
 
+    state.snext = NULL;
     state.d = d;
     state.nopts = d->nopts;
     state.def = state.ddef = NULL;
-    state.curopt = NULL;
-    state.argbeg = state.optbeg = state.nargbeg = state.restbeg =
+    state.curopt = state.dopt = NULL;
+    state.argbeg = state.optbeg = state.nargbeg = state.restbeg = state.actopts =
 	state.nth = state.inopt = state.inarg = state.opt = state.arg = 1;
-    state.inrest = state.doff = state.singles = state.doff = 0;
+    state.argend = argend = arrlen(compwords) - 1;
+    state.inrest = state.doff = state.singles = state.doff = state.oopt = 0;
     state.curpos = compcurrent;
     state.args = znewlinklist();
     state.oargs = (LinkList *) zalloc(d->nopts * sizeof(LinkList));
@@ -1086,25 +1307,43 @@ ca_parse_line(Cadef d)
     if (!compwords[1]) {
 	ca_laststate.opt = ca_laststate.arg = 0;
 
-	return;
+	goto end;
     }
+    if (d->nonarg)
+	napat = patcompile(d->nonarg, 0, NULL);
+
     /* 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. */
+	dopt = NULL;
+	doff = state.singles = arglast = 0;
+
+        /* remove quotes */
+        line = dupstring(line);
+        ne = noerrs;
+        noerrs = 2;
+        parse_subst_string(line);
+        noerrs = ne;
+        remnulargs(line);
+        untokenize(line);
+
+	if (ca_inactive(d, argxor, cur, 0, NULL) ||
+	    ((d->flags & CDF_SEP) && cur != compcurrent && !strcmp(line, "--"))) {
+	    if (ca_inactive(d, NULL, cur, 1, NULL))
+		return 1;
+	    continue;
+	}
+	/* We've got a definition for an argument, skip to the next. */
 
 	if (state.def) {
 	    state.arg = 0;
 	    if (state.curopt)
 		zaddlinknode(state.oargs[state.curopt->num], ztrdup(line));
 
-	    state.opt = (state.def->type == CAA_OPT);
+	    if ((state.opt = (state.def->type == CAA_OPT)) && state.def->opt)
+		state.oopt++;
 
 	    if (state.def->type == CAA_REST || state.def->type == CAA_RARGS ||
 		state.def->type == CAA_RREST) {
@@ -1112,11 +1351,24 @@ ca_parse_line(Cadef d)
 		    state.def = NULL;
 		    state.curopt = NULL;
 		    state.opt = state.arg = 1;
-		    continue;
+		    state.argend = ca_laststate.argend = cur - 1;
+		    goto cont;
 		}
-	    } else if ((state.def = state.def->next))
+	    } else if ((state.def = state.def->next)) {
 		state.argbeg = cur;
-	    else {
+		state.argend = argend;
+	    } else if (sopts && nonempty(sopts)) {
+		state.curopt = (Caopt) uremnode(sopts, firstnode(sopts));
+		state.def = state.curopt->args;
+		state.opt = 0;
+		state.argbeg = state.optbeg = state.inopt = cur;
+		state.argend = argend;
+		doff = state.doff = 0;
+		state.singles = 1;
+		if (!state.oargs[state.curopt->num])
+		    state.oargs[state.curopt->num] = znewlinklist();
+		goto cont;
+	    } else {
 		state.curopt = NULL;
 		state.opt = 1;
 	    }
@@ -1134,25 +1386,36 @@ ca_parse_line(Cadef d)
 	/* 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] == '=')) {
+	    (state.curopt->type == CAO_OEQUAL ?
+	     (compwords[cur] || pe[-1] == '=') :
+	     (state.curopt->type == CAO_EQUAL ?
+	      (pe[-1] == '=' || !pe[0]) : 1))) {
+
+	    if ((ddef = state.def = ((state.curopt->type != CAO_EQUAL ||
+				      pe[-1] == '=') ?
+				     state.curopt->args : NULL)))
+		dopt = state.curopt;
 
-	    ddef = state.def = state.curopt->args;
 	    doff = pe - line;
 	    state.optbeg = state.argbeg = state.inopt = cur;
+	    state.argend = argend;
 	    state.singles = (d->single && (!pe || !*pe) &&
 			     state.curopt->name[1] && !state.curopt->name[2]);
 
-	    state.oargs[state.curopt->num] = znewlinklist();
+	    if (!state.oargs[state.curopt->num])
+		state.oargs[state.curopt->num] = znewlinklist();
 
-	    ca_inactive(d, state.curopt->xor);
+	    if (ca_inactive(d, state.curopt->xor, cur, 0,
+                            (cur == compcurrent ? state.curopt->name : NULL)))
+		return 1;
 
 	    /* Collect the argument strings. Maybe. */
 
 	    if (state.def &&
 		(state.curopt->type == CAO_DIRECT ||
+		 state.curopt->type == CAO_EQUAL ||
 		 (state.curopt->type == CAO_ODIRECT && pe[0]) ||
-		 (state.curopt->type == CAO_EQUAL &&
+		 (state.curopt->type == CAO_OEQUAL &&
 		  (pe[0] || pe[-1] == '=')))) {
 		if (state.def->type != CAA_REST &&
 		    state.def->type != CAA_RARGS &&
@@ -1169,28 +1432,43 @@ ca_parse_line(Cadef d)
 		state.curopt = NULL;
 	    }
 	} else if (state.opt == 2 && d->single &&
-		   (state.curopt = ca_get_sopt(d, line, 0, &pe))) {
+		   ((state.curopt = ca_get_sopt(d, line, &pe, &sopts)) ||
+		    (cur != compcurrent && sopts && nonempty(sopts)))) {
 	    /* Or maybe it's a single-letter option? */
 
 	    char *p;
 	    Caopt tmpopt;
 
-	    ddef = state.def = state.curopt->args;
+	    if (cur != compcurrent && sopts && nonempty(sopts))
+		state.curopt = (Caopt) uremnode(sopts, firstnode(sopts));
+
+	    if (!state.oargs[state.curopt->num])
+		state.oargs[state.curopt->num] = znewlinklist();
+
+	    state.def = state.curopt->args;
+	    ddef = (state.curopt->type == CAO_NEXT && cur == compcurrent ?
+		    NULL : state.def);
+	    dopt = state.curopt;
 	    doff = pe - line;
 	    state.optbeg = state.argbeg = state.inopt = cur;
+	    state.argend = argend;
 	    state.singles = (!pe || !*pe);
 
 	    for (p = line + 1; p < pe; p++) {
 		if ((tmpopt = d->single[STOUC(*p)])) {
-		    state.oargs[tmpopt->num] = znewlinklist();
+		    if (!state.oargs[tmpopt->num])
+			state.oargs[tmpopt->num] = znewlinklist();
 
-		    ca_inactive(d, tmpopt->xor);
+		    if (ca_inactive(d, tmpopt->xor, cur, 0,
+                                    (cur == compcurrent ? tmpopt->name : NULL)))
+			return 1;
 		}
 	    }
 	    if (state.def &&
 		(state.curopt->type == CAO_DIRECT ||
+		 state.curopt->type == CAO_EQUAL ||
 		 (state.curopt->type == CAO_ODIRECT && pe[0]) ||
-		 (state.curopt->type == CAO_EQUAL &&
+		 (state.curopt->type == CAO_OEQUAL &&
 		  (pe[0] || pe[-1] == '=')))) {
 		if (state.def->type != CAA_REST &&
 		    state.def->type != CAA_RARGS &&
@@ -1203,29 +1481,54 @@ ca_parse_line(Cadef d)
 		state.opt = 0;
 	    else
 		state.curopt = NULL;
-	} else if (state.arg) {
+	} else if (multi && (*line == '-' || *line == '+') && cur != compcurrent
+#if 0
+		   /**** Ouch. Using this will disable the mutual exclusion
+			 of different sets. Not using it will make the -A
+			 pattern be effectively ignored with multiple sets. */
+		   && (!napat || !pattry(napat, line))
+#endif
+		   )
+	    return 1;
+	else if (state.arg && (!napat || !pattry(napat, line))) {
 	    /* Otherwise it's a normal argument. */
+	    if (napat && ca_inactive(d, NULL, cur + 1, 1, NULL))
+		return 1;
+
+	    arglast = 1;
 	    if (state.inopt) {
 		state.inopt = 0;
 		state.nargbeg = cur - 1;
+		state.argend = argend;
+	    }
+	    if (!d->args && !d->rest && *line && *line != '-' && *line != '+') {
+		if (!multi && cur > compcurrent)
+		    break;
+		return 1;
 	    }
 	    if ((adef = state.def = ca_get_arg(d, state.nth)) &&
 		(state.def->type == CAA_RREST ||
 		 state.def->type == CAA_RARGS)) {
 		state.inrest = 0;
-		state.opt = (cur == state.nargbeg + 1);
+		state.opt = (cur == state.nargbeg + 1 &&
+			     (!multi || !*line || 
+			      *line == '-' || *line == '+'));
 		state.optbeg = state.nargbeg;
 		state.argbeg = cur - 1;
+		state.argend = argend;
 
 		for (; line; line = compwords[cur++])
 		    zaddlinknode(state.args, ztrdup(line));
 
 		memcpy(&ca_laststate, &state, sizeof(state));
 		ca_laststate.ddef = NULL;
+		ca_laststate.dopt = NULL;
 		ca_laststate.doff = 0;
 		break;
 	    }
 	    zaddlinknode(state.args, ztrdup(line));
+            if (adef)
+                state.oopt = adef->num - state.nth;
 
 	    if (state.def)
 		argxor = state.def->xor;
@@ -1256,6 +1559,7 @@ ca_parse_line(Cadef d)
 		    zaddlinknode(l, ztrdup(line));
 
 		ca_laststate.ddef = NULL;
+		ca_laststate.dopt = NULL;
 		ca_laststate.doff = 0;
 		break;
 	    }
@@ -1264,15 +1568,19 @@ ca_parse_line(Cadef d)
 
 	/* Copy the state into the global one. */
 
+    cont:
+
 	if (cur + 1 == compcurrent) {
 	    memcpy(&ca_laststate, &state, sizeof(state));
 	    ca_laststate.ddef = NULL;
+	    ca_laststate.dopt = NULL;
 	    ca_laststate.doff = 0;
 	} else if (cur == compcurrent && !ca_laststate.def) {
 	    if ((ca_laststate.def = ddef)) {
 		ca_laststate.singles = state.singles;
 		if (state.curopt && state.curopt->type == CAO_NEXT) {
 		    ca_laststate.ddef = ddef;
+		    ca_laststate.dopt = dopt;
 		    ca_laststate.def = NULL;
 		    ca_laststate.opt = 1;
 		    state.curopt->active = 1;
@@ -1282,15 +1590,28 @@ ca_parse_line(Cadef d)
 		}
 	    } else {
 		ca_laststate.def = adef;
+		ca_laststate.opt = (!arglast || !multi || !*line || 
+				    *line == '-' || *line == '+');
 		ca_laststate.ddef = NULL;
+		ca_laststate.dopt = NULL;
 		ca_laststate.optbeg = state.nargbeg;
 		ca_laststate.argbeg = state.restbeg;
+		ca_laststate.argend = state.argend;
 		ca_laststate.singles = state.singles;
+		ca_laststate.oopt = state.oopt;
 		if (wasopt)
 		    wasopt->active = 1;
 	    }
 	}
     }
+ end:
+
+    ca_laststate.actopts = 0;
+    for (ptr = d->opts; ptr; ptr = ptr->next)
+	if (ptr->active)
+	    ca_laststate.actopts++;
+
+    return 0;
 }
 
 /* Build a colon-list from a list. */
@@ -1327,10 +1648,104 @@ ca_colonlist(LinkList l)
 	return ztrdup("");
 }
 
+static void
+ca_set_data(LinkList descr, LinkList act, LinkList subc,
+	    char *opt, Caarg arg, int single)
+{
+    LinkNode dnode, anode;
+    char nbuf[40], *buf;
+    int restr = 0, onum, miss = 0, rest, oopt = 1, lopt = 0, addopt;
+
+ rec:
+
+    addopt = (opt ? 0 : ca_laststate.oopt);
+
+    for (; arg && (opt || (arg->num < 0 ||
+			   (arg->min <= ca_laststate.nth + addopt &&
+			    arg->num >= ca_laststate.nth)));) {
+	lopt = (arg->type == CAA_OPT);
+	if (!opt && !lopt && oopt > 0)
+	    oopt = 0;
+
+	for (dnode = firstnode(descr), anode = firstnode(act);
+	     dnode; incnode(dnode), incnode(anode))
+	    if (!strcmp((char *) getdata(dnode), arg->descr) &&
+		!strcmp((char *) getdata(anode), arg->action))
+		break;
+
+	if (!dnode) {
+	    addlinknode(descr, arg->descr);
+	    addlinknode(act, arg->action);
+
+	    if (!restr) {
+		if ((restr = (arg->type == CAA_RARGS)))
+		    restrict_range(ca_laststate.optbeg, ca_laststate.argend);
+		else if ((restr = (arg->type == CAA_RREST)))
+		    restrict_range(ca_laststate.argbeg, ca_laststate.argend);
+	    }
+	    if (arg->opt) {
+		buf = (char *) zhalloc((arg->set ? strlen(arg->set) : 0) +
+				       strlen(arg->opt) + 40);
+		if (arg->num > 0 && arg->type < CAA_REST)
+		    sprintf(buf, "%soption%s-%d",
+			    (arg->set ? arg->set : ""), arg->opt, arg->num);
+		else
+		    sprintf(buf, "%soption%s-rest",
+			    (arg->set ? arg->set : ""), arg->opt);
+	    } else if (arg->num > 0) {
+		sprintf(nbuf, "argument-%d", arg->num);
+		buf = (arg->set ? dyncat(arg->set, nbuf) : dupstring(nbuf));
+	    } else
+		buf = (arg->set ? dyncat(arg->set, "argument-rest") :
+		       dupstring("argument-rest"));
+
+	    addlinknode(subc, buf);
+	}
+	if (single)
+	    break;
+
+	if (!opt) {
+	    if (arg->num >= 0 && !arg->next && miss)
+		arg = (ca_laststate.d->rest && ca_laststate.d->rest->active ?
+		       ca_laststate.d->rest : NULL);
+	    else {
+		onum = arg->num;
+		rest = (onum != arg->min && onum == ca_laststate.nth);
+		if ((arg = arg->next)) {
+		    if (arg->num != onum + 1)
+			miss = 1;
+		} else if (rest || (oopt > 0 && !opt)) {
+		    arg = (ca_laststate.d->rest && ca_laststate.d->rest->active ?
+			   ca_laststate.d->rest : NULL);
+		    oopt = -1;
+		}
+	    }
+	} else {
+	    if (!lopt)
+		break;
+	    arg = arg->next;
+	}
+    }
+    if (!single && opt && (lopt || ca_laststate.oopt)) {
+	opt = NULL;
+	arg = ca_get_arg(ca_laststate.d, ca_laststate.nth);
+
+	goto rec;
+    }
+    if (!opt && oopt > 0) {
+	oopt = -1;
+	arg = (ca_laststate.d->rest && ca_laststate.d->rest->active ?
+	       ca_laststate.d->rest : NULL);
+
+	goto rec;
+    }
+}
+
 static int
 bin_comparguments(char *nam, char **args, char *ops, int func)
 {
     int min, max, n;
+    Castate lstate = &ca_laststate;
 
     if (incompfunc != 1) {
 	zwarnnam(nam, "can only be called from completion function", NULL, 0);
@@ -1340,14 +1755,13 @@ bin_comparguments(char *nam, char **args, char *ops, int func)
 	zwarnnam(nam, "invalid argument: %s", args[0], 0);
 	return 1;
     }
-    if (args[0][1] != 'i' && !ca_parsed) {
+    if (args[0][1] != 'i' && args[0][1] != 'I' && !ca_parsed) {
 	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 'D': min = 3; max =  3; break;
     case 'O': min = 4; max =  4; break;
     case 'L': min = 3; max =  4; break;
     case 's': min = 1; max =  1; break;
@@ -1368,169 +1782,274 @@ bin_comparguments(char *nam, char **args, char *ops, int func)
     }
     switch (args[0][1]) {
     case 'i':
+        /* This initialises the internal data structures. Arguments are the
+         * auto-description string, the optional -s, -S, -A and -M options
+         * given to _arguments and the specs. */
 	if (compcurrent > 1 && compwords[0]) {
-	    Cadef def = get_cadef(nam, args + 1);
-	    int cap = ca_parsed;
+	    Cadef def;
+	    int cap = ca_parsed, multi, first = 1, use, ret = 0;
+	    LinkList cax = ca_xor, nx;
+	    LinkNode node;
+	    Castate states = NULL, sp;
+	    char *xor[2];
 
 	    ca_parsed = 0;
+	    xor[1] = NULL;
 
-	    if (!def)
+	    if (!(def = get_cadef(nam, args + 1)))
 		return 1;
 
+	    multi = !!def->snext;
 	    ca_parsed = cap;
-	    ca_parse_line(def);
+	    ca_xor = (multi ? newlinklist() : NULL);
+
+	    while (def) {
+		use = !ca_parse_line(def, multi, first);
+		nx = ca_xor;
+		ca_xor = NULL;
+		while ((def = def->snext)) {
+		    if (nx) {
+			for (node = firstnode(nx); node; incnode(node)) {
+			    xor[0] = (char *) getdata(node);
+			    if (!strcmp(xor[0], def->sname) ||
+				ca_inactive(def, xor, compcurrent, 0, NULL))
+				break;
+			}
+			if (!node)
+			    break;
+		    }
+		}
+		ca_xor = nx;
+		if (use && def) {
+		    sp = (Castate) zalloc(sizeof(*sp));
+		    memcpy(sp, &ca_laststate, sizeof(*sp));
+		    sp->snext = states;
+		    states = sp;
+		} else if (!use && !def) {
+		    if (states) {
+			freecastate(&ca_laststate);
+			memcpy(&ca_laststate, states, sizeof(*sp));
+			sp = states->snext;
+			zfree(states, sizeof(*states));
+			states = sp;
+		    } else
+			ret = 1;
+		}
+		first = 0;
+	    }
+	    ca_xor = cax;
 	    ca_parsed = 1;
+	    ca_laststate.snext = states;
 
-	    return 0;
+	    return ret;
 	}
 	return 1;
 
     case 'D':
+        /* This returns the descriptions, actions and sub-contexts for the
+         * things _arguments has to execute at this place on the line (the
+         * sub-contexts are used as tags).
+         * The return value is particularly important here, it says if 
+         * there are arguments to completely at all. */
 	{
-	    Caarg arg = ca_laststate.def;
-
-	    if (arg) {
-		setsparam(args[1], ztrdup(arg->descr));
-		setsparam(args[2], ztrdup(arg->action));
-
-		if (ca_laststate.doff > 0)
-		    ignore_prefix(ca_laststate.doff);
-		if (arg->type == CAA_RARGS)
-		    restrict_range(ca_laststate.optbeg,
-				   arrlen(compwords) - 1);
-		else if (arg->type == CAA_RREST)
-		    restrict_range(ca_laststate.argbeg,
-				   arrlen(compwords) - 1);
-		return 0;
+	    LinkList descr, act, subc;
+	    Caarg arg;
+	    int ign = 0, ret = 1;
+
+	    descr = newlinklist();
+	    act = newlinklist();
+	    subc = newlinklist();
+
+	    while (lstate) {
+		arg = lstate->def;
+
+		if (arg) {
+		    ret = 0;
+		    if (!ign && lstate->doff > 0) {
+			ign = 1;
+			ignore_prefix(lstate->doff);
+		    }
+		    ca_set_data(descr, act, subc, arg->opt, arg,
+				(lstate->doff > 0));
+		}
+		lstate = lstate->snext;
 	    }
-	    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;
+	    if (!ret) {
+		set_list_array(args[1], descr);
+		set_list_array(args[2], act);
+		set_list_array(args[3], subc);
 	    }
-	    return 1;
+	    return ret;
 	}
     case 'O':
-	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)))) {
+        /* This returns the descriptions for the options in the arrays whose
+         * names are given as arguments.  The descriptions are strings in a
+         * form usable by _describe.  The return value says if there are any
+         * options to be completed. */
+	{
 	    LinkList next = newlinklist();
 	    LinkList direct = newlinklist();
 	    LinkList odirect = newlinklist();
 	    LinkList equal = newlinklist(), l;
 	    Caopt p;
 	    char *str;
-
-	    for (p = ca_laststate.d->opts; p; p = p->next) {
-		if (p->active) {
-		    switch (p->type) {
-		    case CAO_NEXT:    l = next;    break;
-		    case CAO_DIRECT:  l = direct;  break;
-		    case CAO_ODIRECT: l = odirect; break;
-		    default:          l = equal;   break;
+	    int ret = 1;
+
+	    for (; lstate; lstate = lstate->snext) {
+		if (lstate->actopts &&
+		    (lstate->opt || (lstate->doff && lstate->def) ||
+		     (lstate->def && lstate->def->opt &&
+		      (lstate->def->type == CAA_OPT ||
+		       (lstate->def->type >= CAA_RARGS &&
+			lstate->def->num < 0)))) &&
+		    (!lstate->def || lstate->def->type < CAA_RARGS ||
+		     (lstate->def->type == CAA_RARGS ?
+		      (lstate->curpos == lstate->argbeg + 1) :
+		      (compcurrent == 1)))) {
+		    ret = 0;
+		    for (p = lstate->d->opts; p; p = p->next) {
+			if (p->active && !p->not) {
+			    switch (p->type) {
+			    case CAO_NEXT:    l = next;    break;
+			    case CAO_DIRECT:  l = direct;  break;
+			    case CAO_ODIRECT: l = odirect; break;
+			    default:          l = equal;   break;
+			    }
+			    if (p->descr) {
+				char *n = bslashcolon(p->name);
+				int len = strlen(n) + strlen(p->descr) + 2;
+
+				str = (char *) zhalloc(len);
+				strcpy(str, n);
+				strcat(str, ":");
+				strcat(str, p->descr);
+			    } else
+				str = bslashcolon(p->name);
+			    addlinknode(l, str);
+			}
 		    }
-		    if (p->descr) {
-			char *n = bslashcolon(p->name);
-			int len = strlen(n) + strlen(p->descr) + 2;
-
-			str = (char *) zhalloc(len);
-			strcpy(str, n);
-			strcat(str, ":");
-			strcat(str, p->descr);
-		    } else
-			str = bslashcolon(p->name);
-		    addlinknode(l, str);
 		}
 	    }
-	    set_list_array(args[1], next);
-	    set_list_array(args[2], direct);
-	    set_list_array(args[3], odirect);
-	    set_list_array(args[4], equal);
+	    if (!ret) {
+		set_list_array(args[1], next);
+		set_list_array(args[2], direct);
+		set_list_array(args[3], odirect);
+		set_list_array(args[4], equal);
 
-	    return 0;
+		return 0;
+	    }
+	    return (ca_laststate.singles ? 2 : 1);
 	}
-	return 1;
     case 'L':
+        /* This tests if the beginning of the current word matches an option.
+         * It is for cases like `./configure --pre=/<TAB>' which should
+         * complete to `--prefix=/...'.  The options name isn't fully typed
+         * and _arguments finds out that there is no option `--pre' and that
+         * it should complete some argument to an option.  It then uses -L
+         * to find the option the argument is for. */
 	{
-	    Caopt opt = ca_get_opt(ca_laststate.d, args[1], 1, NULL);
+	    LinkList descr, act, subc;
+	    Caopt opt;
+	    int ret = 1;
 
-	    if (opt && opt->args) {
-		setsparam(args[2], ztrdup(opt->args->descr));
-		setsparam(args[3], ztrdup(opt->args->action));
+	    descr = newlinklist();
+	    act = newlinklist();
+	    subc = newlinklist();
 
-		if (args[4])
-		    setsparam(args[4], tricat(opt->name, "-1", ""));
+	    while (lstate) {
+		opt = ca_get_opt(lstate->d, args[1], 1, NULL);
 
-		return 0;
+		if (opt && opt->args) {
+		    ret = 0;
+		    ca_set_data(descr, act, subc, opt->name, opt->args, 1);
+		}
+		lstate = lstate->snext;
 	    }
-	    return 1;
+	    if (!ret) {
+		set_list_array(args[2], descr);
+		set_list_array(args[3], act);
+		set_list_array(args[4], subc);
+	    }
+	    return ret;
 	}
     case 's':
-	if (ca_laststate.d->single && ca_laststate.singles &&
-	    ca_laststate.opt) {
-	    setsparam(args[1],
-		      ztrdup(ca_laststate.ddef ?
-			     (ca_laststate.ddef->type == CAO_DIRECT ?
-			      "direct" :
-			      (ca_laststate.ddef->type == CAO_EQUAL ?
-			       "equal" : "next")) : ""));
-	    return 0;
-	}
+        /* This returns zero if we are completing single letter options.
+         * It also uses its argument as the name of a parameter and sets
+         * that to a string describing the argument behaviour of the last
+         * option in the current word so that we can get the auto-suffix
+         * right. */
+	for (; lstate; lstate = lstate->snext)
+	    if (lstate->d->single && lstate->singles &&
+		lstate->actopts
+#if 0
+                /* let's try without, for the -W option of _arguments */
+                && lstate->opt
+#endif
+                ) {
+		setsparam(args[1],
+			  ztrdup((lstate->ddef && lstate->dopt) ?
+				 (lstate->dopt->type == CAO_DIRECT ?
+				  "direct" :
+				  ((lstate->dopt->type == CAO_OEQUAL ||
+				    lstate->dopt->type == CAO_EQUAL) ?
+				   "equal" : "next")) : ""));
+		return 0;
+	    }
 	return 1;
     case 'M':
+        /* This returns the match specs defined for the set of specs we are
+         * using.  Returned, as usual in a parameter whose name is given as
+         * the argument. */
 	setsparam(args[1], ztrdup(ca_laststate.d->match));
 	return 0;
     case 'a':
-	return !(ca_laststate.d->args || ca_laststate.d->rest);
+        /* This just sets the return value.  To zero if there would be or
+         * were any normal arguments to be completed.  Used to decide if
+         * _arguments should say `no arguments' or `no more arguments'. */
+	for (; lstate; lstate = lstate->snext)
+	    if (lstate->d->args || lstate->d->rest)
+		return 0;
+	return 1;
     case 'W':
+        /* This gets two parameter names as arguments.  The first is set to
+         * the current word sans any option prefixes handled by comparguments.
+         * The second parameter is set to an array containing the options on
+         * the line and their arguments.  I.e. the stuff _arguments returns
+         * to its caller in the `line' and `opt_args' parameters. */
 	{
+	    Castate s;
 	    char **ret, **p;
 	    LinkNode n;
 	    LinkList *a;
 	    Caopt o;
 	    int num;
 
-	    ret = p = zalloc((countlinknodes(ca_laststate.args) + 1) *
-			     sizeof(char *));
+	    for (num = 0, s = lstate; s; s = s->snext)
+		num += countlinknodes(s->args);
+
+	    ret = p = zalloc((num + 1) * sizeof(char *));
 
-	    for (n = firstnode(ca_laststate.args); n; incnode(n))
-		*p++ = ztrdup((char *) getdata(n));
+	    for (s = lstate; s; s = s->snext)
+		for (n = firstnode(s->args); n; incnode(n))
+		    *p++ = ztrdup((char *) getdata(n));
 	    *p = NULL;
 
 	    setaparam(args[1], ret);
 
-	    for (num = 0, o = ca_laststate.d->opts, a = ca_laststate.oargs; o;
-		 o = o->next, a++)
-		if (*a)
-		    num += 2;
+	    for (num = 0, s = lstate; s; s = s->snext)
+		for (o = s->d->opts, a = s->oargs; o; o = o->next, a++)
+		    if (*a)
+			num += 2;
 
 	    ret = p = zalloc((num + 1) * sizeof(char *));
 
-	    for (o = ca_laststate.d->opts, a = ca_laststate.oargs; o;
-		 o = o->next, a++) {
-		if (*a) {
-		    *p++ = ztrdup(o->name);
-		    *p++ = ca_colonlist(*a);
-		}
-	    }
+	    for (s = lstate; s; s = s->snext)
+		for (o = s->d->opts, a = s->oargs; o; o = o->next, a++)
+		    if (*a) {
+			*p++ = (o->set ? tricat(o->set, o->name, "") :
+				ztrdup(o->name));
+			*p++ = ca_colonlist(*a);
+		    }
 	    *p = NULL;
 
 	    sethparam(args[2], ret);
@@ -1551,6 +2070,7 @@ struct cvdef {
     char *descr;		/* global description */
     int hassep;			/* multiple values allowed */
     char sep;			/* separator character */
+    char argsep;                /* argument separator */
     Cvdef next;			/* next in cache */
     Cvval vals;			/* value definitions */
     char **defs;		/* original strings */
@@ -1612,16 +2132,18 @@ parse_cvdef(char *nam, char **args)
     Cvdef ret;
     Cvval val, *valp;
     Caarg arg;
-    char **oargs = args, sep = '\0', *name, *descr, *p, *q, **xor, c;
+    char **oargs = args, sep = '\0', asep = '=', *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]) {
-	    zwarnnam(nam, "invalid separator: %s", args[1], 0);
-	    return NULL;
-	}
-	hassep = 1;
-	sep = args[1][0];
+    while (args[0][0] == '-' && (args[0][1] == 's' || args[0][1] == 'S') &&
+           !args[0][2]) {
+
+        if (args[0][1] == 's') {
+            hassep = 1;
+            sep = args[1][0];
+        } else
+            asep = args[1][0];
+
 	args += 2;
     }
     if (!args[0] || !args[1]) {
@@ -1634,6 +2156,7 @@ parse_cvdef(char *nam, char **args)
     ret->descr = ztrdup(descr);
     ret->hassep = hassep;
     ret->sep = sep;
+    ret->argsep = asep;
     ret->next = NULL;
     ret->vals = NULL;
     ret->defs = zarrdup(oargs);
@@ -1641,6 +2164,7 @@ parse_cvdef(char *nam, char **args)
     ret->lastt = time(0);
 
     for (valp = &(ret->vals); *args; args++) {
+	int bs = 0;
 	p = dupstring(*args);
 	xnum = 0;
 
@@ -1688,9 +2212,9 @@ parse_cvdef(char *nam, char **args)
 
 	for (name = p; *p && *p != ':' && *p != '['; p++)
 	    if (*p == '\\' && p[1])
-		p++;
+		p++, bs = 1;
 
-	if (hassep && !sep && name + 1 != p) {
+	if (hassep && !sep && name + bs + 1 < p) {
 	    freecvdef(ret);
 	    zwarnnam(nam, "no multi-letter values with empty separator allowed", NULL, 0);
 	    return NULL;
@@ -1739,7 +2263,7 @@ parse_cvdef(char *nam, char **args)
 		vtype = CVV_OPT;
 	    } else
 		vtype = CVV_ARG;
-	    arg = parse_caarg(0, 0, 0, name, &p);
+	    arg = parse_caarg(0, 0, 0, 0, name, &p, NULL);
 	} else {
 	    vtype = CVV_NOARG;
 	    arg = NULL;
@@ -1821,20 +2345,122 @@ struct cvstate {
 static struct cvstate cv_laststate;
 static int cv_parsed = 0, cv_alloced = 0;
 
+/* Get the next value in the string.  Return it's definition and update the
+ * sp pointer to point to the end of the value (plus argument, if any).
+ * If there is no next value, the string pointer is set to null.  In any
+ * case ap will point to the beginning of the argument or will be a null
+ * pointer if there is no argument.
+ */
+
+static Cvval
+cv_next(Cvdef d, char **sp, char **ap)
+{
+    Cvval r = NULL;
+    char *s = *sp;
+
+    if (!*s) {
+        *sp = *ap = NULL;
+
+        return NULL;
+    }
+    if ((d->hassep && !d->sep) || !d->argsep) {
+        char sav, ec, *v = s, *os;
+
+        ec = ((d->hassep && d->sep) ? d->sep : d->argsep);
+
+        do {
+            sav = *++s;
+            *s = '\0';
+            if ((r = cv_get_val(d, v))) {
+                *s = sav;
+
+                break;
+            }
+            *s = sav;
+        } while (*s && *s != ec);
+
+        os = s;
+
+        if (d->hassep && d->sep) {
+            if ((s = strchr(s, d->sep)))
+                *sp = s + 1;
+            else
+                *sp = NULL;
+        } else
+            *sp = s;
+        if (d->argsep && *os == d->argsep) {
+            *ap = os + 1;
+            *sp = NULL;
+        } else if (r && r->type != CVV_NOARG)
+            *ap = os;
+        else
+            *ap = NULL;
+
+        return r;
+
+    } else if (d->hassep) {
+        char *ns = strchr(s, d->sep), *as, *sap, sav;
+        int skip = 0;
+
+        if (d->argsep && (as = strchr(s, d->argsep)) && (!ns || as <= ns)) {
+            *ap = as + 1;
+            ns = strchr(as + 1, d->sep);
+            skip = 1;
+            sap = as;
+        } else {
+            *ap = NULL;
+            sap = ns;
+        }
+        if (sap) {
+            sav = *sap;
+            *sap = '\0';
+        }
+        if ((!(r = cv_get_val(d, s)) || r->type == CVV_NOARG) && skip)
+            ns = as;
+
+        if (sap)
+            *sap = sav;
+
+        *sp = ((!ns || (ns == as && r && r->type != CVV_NOARG)) ? NULL : ns + 1);
+
+        return r;
+    } else {
+        char *as = strchr(s, d->argsep), *sap, sav;
+
+        *sp = NULL;
+
+        if (as) {
+            *ap = as + 1;
+            sap = as;
+            sav = *as;
+            *sap = '\0';
+        } else
+            *ap = sap = NULL;
+
+        r = cv_get_val(d, s);
+
+        if (sap)
+            *sap = sav;
+
+        return r;
+    }
+}
+
 /* Parse the current word. */
 
 static void
 cv_parse_word(Cvdef d)
 {
-    Cvval ptr;
+    Cvval val;
     struct cvstate state;
-    char *str, *eq;
+    char *str, *arg = NULL, *pign = compprefix;
+    int nosfx = 0;
 
     if (cv_alloced)
 	freelinklist(cv_laststate.vals, freestr);
 
-    for (ptr = d->vals; ptr; ptr = ptr->next)
-	ptr->active = 1;
+    for (val = d->vals; val; val = val->next)
+	val->active = 1;
 
     state.d = d;
     state.def = NULL;
@@ -1843,103 +2469,90 @@ cv_parse_word(Cvdef d)
 
     cv_alloced = 1;
 
-    if (d->hassep) {
-	if (d->sep) {
-	    char *end;
-	    int heq;
-
-	    for (str = compprefix, end = strchr(str, d->sep); end;) {
-		*end = '\0';
-
-		if ((heq = !!(eq = strchr(str, '='))))
-		    *eq++ = '\0';
-		else
-		    eq = "";
-
-		if ((ptr = cv_get_val(d, str))) {
-		    zaddlinknode(state.vals, ztrdup(str));
-		    zaddlinknode(state.vals, ztrdup(eq));
-
-		    cv_inactive(d, ptr->xor);
-		}
-		if (heq)
-		    eq[-1] = '=';
-
-		*end = d->sep;
-		str = end + 1;
-		end = strchr(str, d->sep);
-	    }
-	    ignore_prefix(str - compprefix);
-
-	    if ((str = strchr(compsuffix, d->sep))) {
-		char *beg = str;
-
-		for (str++; str; str = end) {
-		    if ((end = strchr(str, d->sep)))
-			*end = '\0';
-
-		    if ((heq = !!(eq = strchr(str, '='))))
-			*eq++ = '\0';
-		    else
-			eq = "";
-
-		    if ((ptr = cv_get_val(d, str))) {
-			zaddlinknode(state.vals, ztrdup(str));
-			zaddlinknode(state.vals, ztrdup(eq));
-
-			cv_inactive(d, ptr->xor);
-		    }
-		    if (heq)
-			eq[-1] = '=';
-		    if (end)
-			*end++ = d->sep;
-		}
-		ignore_suffix(strlen(beg));
-	    }
-	} else {
-	    char tmp[2];
-
-	    tmp[1] = '\0';
-
-	    for (str = compprefix; *str; str++) {
-		tmp[0] = *str;
-		if ((ptr = cv_get_val(d, tmp))) {
-		    zaddlinknode(state.vals, ztrdup(tmp));
-		    zaddlinknode(state.vals, ztrdup(""));
-
-		    cv_inactive(d, ptr->xor);
-		}
-	    }
-	    for (str = compsuffix; *str; str++) {
-		tmp[0] = *str;
-		if ((ptr = cv_get_val(d, tmp))) {
-		    zaddlinknode(state.vals, ztrdup(tmp));
-		    zaddlinknode(state.vals, ztrdup(""));
-
-		    cv_inactive(d, ptr->xor);
-		}
-	    }
-	    ignore_prefix(strlen(compprefix));
-	    ignore_suffix(strlen(compsuffix));
-	}
+    for (str = compprefix; str && *str; ) {
+        if ((val = cv_next(d, &str, &arg))) {
+            zaddlinknode(state.vals, ztrdup(val->name));
+            if (arg) {
+                if (str) {
+                    char sav = str[-1];
+
+                    str[-1] = '\0';
+                    zaddlinknode(state.vals, ztrdup(arg));
+                    str[-1] = sav;
+                } else {
+                    zaddlinknode(state.vals, tricat(arg, compsuffix, ""));
+                    nosfx = 1;
+                }
+            } else
+                zaddlinknode(state.vals, ztrdup(""));
+
+            cv_inactive(d, val->xor);
+
+            if (str)
+                pign = str;
+            else
+                val->active = 1;
+        }
     }
-    str = tricat(compprefix, compsuffix, "");
-    zsfree(compprefix);
-    zsfree(compsuffix);
-    compprefix = str;
-    compsuffix = ztrdup("");
-
-    if ((eq = strchr(str, '='))) {
-	*eq++ = '\0';
+    state.val = val;
+    if (val && arg && !str)
+        state.def = val->arg;
+
+    if (!nosfx && d->hassep) {
+        int ign = 0;
+        char *more = NULL;
+
+        ignore_prefix(pign - compprefix);
+
+        if (!d->sep && (!val || val->type == CVV_NOARG)) {
+            ign = strlen(compsuffix);
+            more = compsuffix;
+        } else {
+            if (d->sep) {
+                char *ns = strchr(compsuffix, d->sep), *as;
+
+                if (d->argsep && (as = strchr(compsuffix, d->argsep)) &&
+                    (!ns || as <= ns)) {
+                    ign = strlen(as);
+                } else
+                    ign = (ns ? strlen(ns) : 0);
+
+                more = (ns ? ns + 1 : NULL);
+            } else if (d->argsep) {
+                char *as;
+
+                if ((as = strchr(compsuffix, d->argsep)))
+                    ign = strlen(as);
+            }
+        }
+        if (ign)
+            ignore_suffix(ign);
+
+        while (more && *more) {
+            if ((val = cv_next(d, &str, &arg))) {
+                zaddlinknode(state.vals, ztrdup(val->name));
+                if (arg) {
+                    if (str) {
+                        char sav = str[-1];
+
+                        str[-1] = '\0';
+                        zaddlinknode(state.vals, ztrdup(arg));
+                        str[-1] = sav;
+                    } else {
+                        zaddlinknode(state.vals, tricat(arg, compsuffix, ""));
+                        nosfx = 1;
+                    }
+                } else
+                    zaddlinknode(state.vals, ztrdup(""));
+
+                cv_inactive(d, val->xor);
+            }
+        }
+    } else if (arg)
+        ignore_prefix(arg - compprefix);
+    else
+        ignore_prefix(pign - compprefix);
 
-	if ((ptr = cv_get_val(d, str)) && ptr->type != CVV_NOARG) {
-	    eq[-1] = '=';
-	    ignore_prefix(eq - str);
-	    state.def = ptr->arg;
-	    state.val = ptr;
-	} else
-	    eq[-1] = '=';
-    }
     memcpy(&cv_laststate, &state, sizeof(state));
 }
 
@@ -1966,6 +2579,7 @@ bin_compvalues(char *nam, char **args, char *ops, int func)
     case 'C': min = 1; max =  1; break;
     case 'V': min = 3; max =  3; break;
     case 's': min = 1; max =  1; break;
+    case 'S': min = 1; max =  1; break;
     case 'd': min = 1; max =  1; break;
     case 'L': min = 3; max =  4; break;
     case 'v': min = 1; max =  1; break;
@@ -1983,6 +2597,8 @@ bin_compvalues(char *nam, char **args, char *ops, int func)
     }
     switch (args[0][1]) {
     case 'i':
+        /* This initialises the internal data structures.  The arguments are
+         * just the arguments that were given to _values itself. */
 	{
 	    Cvdef def = get_cvdef(nam, args + 1);
 	    int cvp = cv_parsed;
@@ -2001,6 +2617,9 @@ bin_compvalues(char *nam, char **args, char *ops, int func)
 	return 1;
 
     case 'D':
+        /* This returns the description and action to use if we are at
+         * a place where some action has to be used at all.  In that case
+         * zero is returned and non-zero otherwise. */
 	{
 	    Caarg arg = cv_laststate.def;
 
@@ -2013,6 +2632,8 @@ bin_compvalues(char *nam, char **args, char *ops, int func)
 	    return 1;
 	}
     case 'C':
+        /* This returns the sub-context (i.e.: the tag) to use when executing
+         * an action. */
 	{
 	    Caarg arg = cv_laststate.def;
 
@@ -2024,6 +2645,10 @@ bin_compvalues(char *nam, char **args, char *ops, int func)
 	    return 1;
 	}
     case 'V':
+        /* This is what -O is for comparguments: it returns (in three arrays)
+         * the values for values without arguments, with arguments and with
+         * optional arguments (so that we can get the auto-suffixes right).
+         * As for comparguments, the strings returned are usable for _describe. */
 	{
 	    LinkList noarg = newlinklist();
 	    LinkList arg = newlinklist();
@@ -2057,6 +2682,8 @@ bin_compvalues(char *nam, char **args, char *ops, int func)
 	    return 0;
 	}
     case 's':
+        /* This returns the value separator, if any, and sets the return
+         * value to say if there is such a separator. */
 	if (cv_laststate.d->hassep) {
 	    char tmp[2];
 
@@ -2067,10 +2694,26 @@ bin_compvalues(char *nam, char **args, char *ops, int func)
 	    return 0;
 	}
 	return 1;
+    case 'S':
+        /* Like -s, but for the separator between values and their arguments. */
+	{
+	    char tmp[2];
+
+	    tmp[0] = cv_laststate.d->argsep;
+	    tmp[1] = '\0';
+	    setsparam(args[1], ztrdup(tmp));
+	}
+	return 0;
     case 'd':
+        /* This returns the description string (first argument to _values)
+         * which is passed down to _describe. */
 	setsparam(args[1], ztrdup(cv_laststate.d->descr));
 	return 0;
     case 'L':
+        /* Almost the same as for comparguments.  This gets a value name
+         * and returns the description and action of its first argument, if
+         * any.  The rest (prefix matching) is in _values.  Return non-zero
+         * if there is no such option. */
 	{
 	    Cvval val = cv_get_val(cv_laststate.d, args[1]);
 
@@ -2086,6 +2729,8 @@ bin_compvalues(char *nam, char **args, char *ops, int func)
 	    return 1;
 	}
     case 'v':
+        /* Again, as for comparguments.  This returns the values and their
+         * arguments as an array which will be stored in val_args in _values. */
 	if (cv_laststate.vals) {
 	    char **ret, **p;
 	    LinkNode n;
@@ -2106,6 +2751,24 @@ bin_compvalues(char *nam, char **args, char *ops, int func)
     return 1;
 }
 
+static char *
+comp_quote(char *str, int prefix)
+{
+    int x;
+    char *ret;
+
+    if ((x = (prefix && *str == '=')))
+	*str = 'x';
+
+    ret = bslashquote(str, NULL, (*compqstack == '\'' ? 1 :
+				  (*compqstack == '"' ? 2 : 0)));
+
+    if (x)
+	*str = *ret = '=';
+
+    return ret;
+}
+
 static int
 bin_compquote(char *nam, char **args, char *ops, int func)
 {
@@ -2113,6 +2776,10 @@ bin_compquote(char *nam, char **args, char *ops, int func)
     struct value vbuf;
     Value v;
 
+    if (incompfunc != 1) {
+	zwarnnam(nam, "can only be called from completion function", NULL, 0);
+	return 1;
+    }
     /* Anything to do? */
 
     if (!compqstack || !*compqstack)
@@ -2122,18 +2789,11 @@ bin_compquote(char *nam, char **args, char *ops, int func)
 
     while ((name = *args++)) {
 	name = dupstring(name);
+	queue_signals();
 	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));
-		}
+		setstrvalue(v, ztrdup(comp_quote(getstrvalue(v), ops['p'])));
 		break;
 	    case PM_ARRAY:
 		{
@@ -2143,10 +2803,7 @@ bin_compquote(char *nam, char **args, char *ops, int func)
 		    char **p = new;
 
 		    for (; *val; val++, p++)
-			*p = ztrdup(bslashquote(*val, NULL,
-						(*compqstack == '\'' ? 1 :
-						 (*compqstack == '"' ? 2 :
-						  0))));
+			*p = ztrdup(comp_quote(*val, ops['p']));
 		    *p = NULL;
 
 		    setarrvalue(v, new);
@@ -2157,6 +2814,7 @@ bin_compquote(char *nam, char **args, char *ops, int func)
 	    }
 	} else
 	    zwarnnam(nam, "unknown parameter: %s", args[-1], 0);
+	unqueue_signals();
     }
     return 0;
 }
@@ -2242,6 +2900,7 @@ settags(int level, char **tags)
 
 /* Check if an array contains a string. */
 
+/**/
 static int
 arrcontains(char **a, char *s, int colon)
 {
@@ -2290,7 +2949,7 @@ bin_comptags(char *nam, char **args, char *ops, int func)
     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;
+    case 'A': min = 2; max =  3; break;
     default:
 	zwarnnam(nam, "invalid option: %s", args[0], 0);
 	return 1;
@@ -2363,7 +3022,15 @@ bin_comptags(char *nam, char **args, char *ops, int func)
 		    return 1;
 		}
 		s->ptr = q + 1;
-		setsparam(args[2], ztrdup(v));
+		setsparam(args[2], ztrdup(*v == '-' ? dyncat(args[1], v) : v));
+		if (args[3]) {
+		    char *r = dupstring(*q), *p;
+
+		    for (p = r + (v - *q); *p && *p != ':'; p++);
+		    *p = '\0';
+
+		    setsparam(args[3], ztrdup(r));
+		}
 		return 0;
 	    }
 	    return 1;
@@ -2394,84 +3061,813 @@ bin_comptry(char *nam, char **args, char *ops, int func)
 	return 1;
     }
     if (*args) {
-	char **p, **q, **all;
+	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 && !inblank(*s); s++) {
+			if (!c && *s == ':')
+			    c = p;
+			if (*s == '\\' && s[1])
+			    s++;
+			*p++ = *s;
+		    }
+		    if (*s)
+			s++;
+		    *p = '\0';
+		    if (*q) {
+			char *qq, *qqq;
+
+			if (c)
+			    *c = '\0';
+
+			qqq = qq = dupstring(q);
+			while (*qqq) {
+			    if (qqq == qq || qqq[-1] != '\\') {
+				if (*qqq == '{')
+				    *qqq = Inbrace;
+				else if (*qqq == '}')
+				    *qqq = Outbrace;
+				else if (*qqq == ',')
+				    *qqq = Comma;
+			    }
+			    qqq++;
+			}
+			tokenize(qq);
+			if (haswilds(qq) || hasbraces(qq)) {
+			    Patprog prog;
+			    LinkNode bnode, node;
+			    LinkList blist = newlinklist();
+
+			    addlinknode(blist, qq);
+			    for (bnode = firstnode(blist); bnode; incnode(bnode))
+				while (hasbraces(getdata(bnode)))
+				    xpandbraces(blist, &bnode);
+
+			    for (bnode = firstnode(blist); bnode; incnode(bnode)) {
+				qq = (char *) getdata(bnode);
+				if ((prog = patcompile(qq, PAT_STATIC, NULL))) {
+				    char **a, *n;
+				    int l = (c ? strlen(c + 1) + 2 : 1), al;
+
+				    for (a = all; *a; a++) {
+					for (node = firstnode(list); node;
+					     incnode(node)) {
+					    char *as, *ls;
+
+					    for (as = *a, ls = (char *) getdata(node);
+						 *as && *ls && *ls != ':'; as++, ls++)
+						if (*as != *ls)
+						    break;
+					    if (!*as && (!*ls || *ls == ':'))
+						break;
+					}
+					if (node)
+					    continue;
+					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;
 
-	args = arrdup(args);
+		    set = (Ctset) zalloc(sizeof(*set));
 
-	for (p = q = args, all = comptags[lasttaglevel]->all; *p; p++)
-	    if (arrcontains(all, *p, 1)) {
-		Ctset s;
+		    a = set->tags = (char **) zalloc((num + 1) * sizeof(char *));
+		    for (node = firstnode(list); node; incnode(node))
+			*a++ = ztrdup((char *) getdata(node));
 
-		for (s = comptags[lasttaglevel]->sets; s; s = s->next)
-		    if (arrcontains(s->tags, *p, 0))
-			break;
+		    *a = NULL;
+		    set->next = NULL;
+		    set->ptr = NULL;
+		    set->tag = NULL;
 
-		if (!s)
-		    *q++ = *p;
+		    if ((l = comptags[lasttaglevel]->sets)) {
+			while (l->next)
+			    l = l->next;
+
+			l->next = set;
+		    } else
+			comptags[lasttaglevel]->sets = set;
+		}
 	    }
-	*q = NULL;
+	} else {
+	    char **p, **q, **all;
+	    int sep = 0;
 
-	if (*args) {
-	    Ctset s = (Ctset) zalloc(sizeof(*s)), l;
+	    if ((sep = !strcmp(*args, "-s")))
+		args++;
 
-	    s->tags = zarrdup(args);
-	    s->next = NULL;
-	    s->ptr = NULL;
-	    s->tag = NULL;
+	    for (p = q = args, all = comptags[lasttaglevel]->all; *p; p++)
+		if (arrcontains(all, *p, 1)) {
+		    Ctset s;
 
-	    if ((l = comptags[lasttaglevel]->sets)) {
-		while (l->next)
-		    l = l->next;
+		    for (s = comptags[lasttaglevel]->sets; s; s = s->next)
+			if (arrcontains(s->tags, *p, 0))
+			    break;
 
-		l->next = s;
-	    } else
-		comptags[lasttaglevel]->sets = s;
+		    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;
 }
 
+#define PATH_MAX2 (PATH_MAX * 2)
+
+static LinkList
+cfp_test_exact(LinkList names, char **accept, char *skipped)
+{
+    char buf[PATH_MAX2 + 1], *suf, *p;
+    int l, sl, found = 0;
+    struct stat st;
+    LinkNode node;
+    LinkList ret = newlinklist(), alist = NULL;
+
+    if ((!(compprefix && *compprefix) && !(compsuffix && *compsuffix)) ||
+	(!accept || !*accept ||
+	 ((!strcmp(*accept, "false") || !strcmp(*accept, "no") ||
+	   !strcmp(*accept, "off") || !strcmp(*accept, "0")) && !accept[1])))
+	return NULL;
+
+    if (accept[1] ||
+	(strcmp(*accept, "true") && strcmp(*accept, "yes") &&
+	 strcmp(*accept, "on") && strcmp(*accept, "1"))) {
+	Patprog prog;
+
+	alist = newlinklist();
+
+	for (; (p = *accept); accept++) {
+	    if (*p == '*' && !p[1]) {
+		alist = NULL;
+		break;
+	    }
+	    tokenize(p = dupstring(p));
+	    if ((prog = patcompile(p, 0, NULL)))
+		addlinknode(alist, prog);
+	}
+    }
+    sl = strlen(skipped) + (compprefix ? strlen(compprefix) : 0) +
+	(compsuffix ? strlen(compsuffix) : 0);
+
+    if (sl > PATH_MAX2)
+	return NULL;
+
+    suf = dyncat(skipped, rembslash(dyncat(compprefix, compsuffix)));
+
+    for (node = firstnode(names); node; incnode(node)) {
+	l = strlen(p = (char *) getdata(node));
+	if (l + sl < PATH_MAX2) {
+	    strcpy(buf, p);
+	    strcpy(buf + l, suf);
+
+	    if (!ztat(buf, &st, 0)) {
+		if (alist) {
+		    LinkNode anode;
+
+		    for (anode = firstnode(alist); anode; incnode(anode))
+			if (pattry((Patprog) getdata(anode), buf))
+			    break;
+
+		    if (!anode)
+			continue;
+		}
+		found = 1;
+		addlinknode(ret, dupstring(buf));
+	    }
+	}
+    }
+    return (found ? ret : NULL);
+}
+
 static char *
-fmtstr(char *str, char c, char *repl)
+cfp_matcher_pats(char *matcher, char *add)
 {
-    int len, num, rlen;
-    char *s, *ret, *rp;
+    Cmatcher m = parse_cmatcher(NULL, matcher);
+
+    if (m && m != pcm_err) {
+	char *tmp;
+	int al = strlen(add), tl;
+	VARARR(Cmatcher, ms, al);
+	Cmatcher *mp;
+	Cpattern stopp;
+	int stopl = 0;
+
+	memset(ms, 0, al * sizeof(Cmatcher));
+
+	for (; m && *add; m = m->next) {
+	    stopp = NULL;
+	    if (!(m->flags & (CMF_LEFT|CMF_RIGHT))) {
+		if (m->llen == 1 && m->wlen == 1) {
+		    for (tmp = add, tl = al, mp = ms; tl; tl--, tmp++, mp++) {
+			if (pattern_match(m->line, tmp, NULL, NULL)) {
+			    if (*mp) {
+				*tmp = '\0';
+				al = tmp - add;
+				break;
+			    } else
+				*mp = m;
+			}
+		    }
+		} else {
+		    stopp = m->line;
+		    stopl = m->llen;
+		}
+	    } else if (m->flags & CMF_RIGHT) {
+		if (m->wlen < 0 && !m->llen && m->ralen == 1) {
+		    for (tmp = add, tl = al, mp = ms; tl; tl--, tmp++, mp++) {
+			if (pattern_match(m->right, tmp, NULL, NULL)) {
+			    if (*mp || (tmp == add && *tmp == '.')) {
+				*tmp = '\0';
+				al = tmp - add;
+				break;
+			    } else
+				*mp = m;
+			}
+		    }
+		} else if (m->llen) {
+		    stopp = m->line;
+		    stopl = m->llen;
+		} else {
+		    stopp = m->right;
+		    stopl = m->ralen;
+		}
+	    } else {
+		if (!m->lalen)
+		    return "";
+
+		stopp = m->left;
+		stopl = m->lalen;
+	    }
+	    if (stopp)
+		for (tmp = add, tl = al; tl >= stopl; tl--, tmp++)
+		    if (pattern_match(stopp, tmp, NULL, NULL)) {
+			*tmp = '\0';
+			al = tmp - add;
+			break;
+		    }
+	}
+	if (*add) {
+	    char *ret = "", buf[259];
+
+	    for (mp = ms; *add; add++, mp++) {
+		if (!(m = *mp)) {
+		    buf[0] = *add;
+		    buf[1] = '\0';
+		} else if (m->flags & CMF_RIGHT) {
+		    buf[0] = '*';
+		    buf[1] = *add;
+		    buf[2] = '\0';
+		} else {
+		    unsigned char *t, c;
+		    char *p = buf;
+		    int i;
 
-    len = strlen(str);
-    rlen = strlen(repl);
+		    for (i = 256, t = m->word->tab; i--; t++)
+			if (*t)
+			    break;
+		    if (i) {
+			t = m->word->tab;
+			*p++ = '[';
+			if (m->line->equiv && m->word->equiv) {
+			    *p++ = *add;
+			    c = m->line->tab[STOUC(*add)];
+			    for (i = 0; i < 256; i++)
+				if (m->word->tab[i] == c) {
+				    *p++ = (char) i;
+				    break;
+				}
+			} else {
+			    if (*add == ']' || t[STOUC(']')])
+				*p++ = ']';
+			    for (i = 0; i < 256; i++, t++)
+				if (*t && ((char) i) != *add &&
+				    i != ']' && i != '-' &&
+				    i != '^' && i != '!')
+				    *p++ = (char) i;
+			    *p++ = *add;
+			    t = m->word->tab;
+			    if (*add != '^' && t[STOUC('^')])
+				*p++ = '^';
+			    if (*add != '!' && t[STOUC('!')])
+				*p++ = '!';
+			    if (*add != '-' && t[STOUC('-')])
+				*p++ = '-';
+			}
+			*p++ = ']';
+			*p = '\0';
+		    } else {
+			*p = '?';
+			p[1] = '\0';
+		    }
+		}
+		ret = dyncat(ret, buf);
+	    }
+	    return ret;
+	}
+    }
+    return add;
+}
 
-    for (num = 0, s = str; *s; s++)
-	if (*s == '%' && s[1] == c)
-	    num++, s++;
+static void
+cfp_opt_pats(char **pats, char *matcher)
+{
+    char *add, **p, *q, *t, *s;
 
-    ret = (char *) zhalloc((num * (rlen - 2)) + len + 1);
+    if (!compprefix || !*compprefix)
+	return;
 
-    for (s = str, rp = ret; *s; s++) {
-	if (*s == '%' && s[1] == c) {
-	    strcpy(rp, repl);
-	    rp += rlen;
-	    s++;
-	} else
-	    *rp++ = *s;
+    if (comppatmatch && *comppatmatch) {
+	tokenize(t = rembslash(dyncat(compprefix, compsuffix)));
+	remnulargs(t);
+	if (haswilds(t))
+	    return;
+    }
+    add = (char *) zhalloc(strlen(compprefix) * 2 + 1);
+    for (s = compprefix, t = add; *s; s++) {
+	if (*s != '\\' || !s[1] || s[1] == '*' || s[1] == '?' ||
+	    s[1] == '<' || s[1] == '>' || s[1] == '(' || s[1] == ')' ||
+	    s[1] == '[' || s[1] == ']' || s[1] == '|' || s[1] == '#' ||
+	    s[1] == '^' || s[1] == '~' || s[1] == '=') {
+	    if ((s == compprefix || s[-1] != '\\') &&
+		(*s == '*' || *s == '?' || *s == '<' || *s == '>' ||
+		 *s == '(' || *s == ')' || *s == '[' || *s == ']' ||
+		 *s == '|' || *s == '#' || *s == '^' || *s == '~' ||
+		 *s == '='))
+		*t++ = '\\';
+	    *t++ = *s;
+	}
     }
-    *rp = '\0';
+    *t = '\0';
+    for (p = pats; *add && (q = *p); p++) {
+	if (*q) {
+	    q = dupstring(q);
+	    t = q + strlen(q) - 1;
+	    if (*t == ')') {
+		for (s = t--; t > q; t--)
+		    if (*t == ')' || *t == '|' || *t == '~' || *t == '(')
+			break;
+		if (t != q && *t == '(')
+		    *t = '\0';
+	    }
+	    for (; *q && *add; q++) {
+		if (*q == '\\' && q[1]) {
+		    for (s = add, q++; *s && *s != *q; s++);
+		    *s = '\0';
+		} else if (*q == '<') {
+		    for (s = add; *s && !idigit(*s); s++);
+		    *s = '\0';
+		} else if (*q == '[') {
+		    int not, first = 1;
+		    char *x = ++q;
+
+		    if ((not = (*x == '!' || *x == '^')))
+			x++;
+		    for (; *x && (first || *x != ']'); x++) {
+			if (x[1] == '-' && x[2]) {
+			    char c1 = *x, c2 = x[2];
+
+			    for (s = add; *s && (*x < c1 || *x > c2); s++);
+			    *s = '\0';
+			} else {
+			    for (s = add; *s && *s != *x; s++);
+			    *s = '\0';
+			}
+		    }
+		} else if (*q != '?' && *q != '*' && *q != '(' && *q != ')' &&
+			   *q != '|' && *q != '~' && *q != '#') {
+		    for (s = add; *s && *s != *q; s++);
+		    *s = '\0';
+		}
+	    }
+	}
+    }
+    if (*add) {
+	if (*matcher && !(add = cfp_matcher_pats(matcher, add)))
+	    return;
 
+	for (p = pats; *p; p++)
+	    if (**p == '*')
+		*p = dyncat(add, *p);
+    }
+}
+
+static LinkList
+cfp_bld_pats(int dirs, LinkList names, char *skipped, char **pats)
+{
+    LinkList ret = newlinklist();
+    LinkNode node;
+    int ol, sl = strlen(skipped), pl, dot;
+    char **p, *o, *str;
+
+    dot = (unset(GLOBDOTS) && compprefix && *compprefix == '.');
+    for (node = firstnode(names); node; incnode(node)) {
+	ol = strlen(o = (char *) getdata(node));
+	for (p = pats; *p; p++) {
+	    pl = strlen(*p);
+	    str = (char *) zhalloc(ol + sl + pl + 1);
+	    strcpy(str, o);
+	    strcpy(str + ol, skipped);
+	    strcpy(str + ol + sl, *p);
+	    addlinknode(ret, str);
+	    if (dot && **p != '.') {
+		str = (char *) zhalloc(ol + sl + pl + 2);
+		strcpy(str, o);
+		strcpy(str + ol, skipped);
+		str[ol + sl] = '.';
+		strcpy(str + ol + sl + 1, *p);
+		addlinknode(ret, str);
+	    }
+	}
+    }
     return ret;
 }
 
+static LinkList
+cfp_add_sdirs(LinkList final, LinkList orig, char *skipped,
+	      char *sdirs, char **fake)
+{
+    int add = 0;
+
+    if (*sdirs && (isset(GLOBDOTS) || (compprefix && *compprefix == '.'))) {
+	if (!strcmp(sdirs, "yes") || !strcmp(sdirs, "true") ||
+	    !strcmp(sdirs, "on") || !strcmp(sdirs, "1"))
+	    add = 2;
+	else if (!strcmp(sdirs, ".."))
+	    add = 1;
+    }
+    if (add) {
+	LinkNode node;
+	char *s1 = dyncat(skipped, "..");
+	char *s2 = (add == 2 ? dyncat(skipped, ".") : NULL), *m;
+
+	for (node = firstnode(orig); node; incnode(node)) {
+	    if ((m = (char *) getdata(node))) {
+		addlinknode(final, dyncat(m, s1));
+		if (s2)
+		    addlinknode(final, dyncat(m, s2));
+	    }
+	}
+    }
+    if (fake && *fake) {
+	LinkNode node;
+	char *m, *f, *p, *t, *a, c;
+	int sl = strlen(skipped) + 1;
+	struct stat st1, st2;
+
+	for (; (f = *fake); fake++) {
+	    f = dupstring(f);
+	    for (p = t = f; *p; p++) {
+		if (*p == ':')
+		    break;
+		else if (*p == '\\' && p[1])
+		    p++;
+		*t++ = *p;
+	    }
+	    if (*p) {
+		*t = *p++ = '\0';
+		if (!*p)
+		    continue;
+
+		for (node = firstnode(orig); node; incnode(node)) {
+		    if ((m = (char *) getdata(node)) &&
+			(!strcmp(f, m) ||
+			 (!stat(f, &st1) && !stat((*m ? m : "."), &st2) &&
+			  st1.st_dev == st2.st_dev &&
+			  st1.st_ino == st2.st_ino))) {
+			while (*p) {
+			    while (*p && inblank(*p))
+				p++;
+			    if (!*p)
+				break;
+			    for (f = t = p; *p; p++) {
+				if (inblank(*p))
+				    break;
+				else if (*p == '\\' && p[1])
+				    p++;
+				*t++ = *p;
+			    }
+			    c = *t;
+			    *t = '\0';
+			    a = (char *) zhalloc(strlen(m) + sl + strlen(f));
+			    strcpy(a, m);
+			    strcat(a, skipped);
+			    strcat(a, f);
+			    addlinknode(final, a);
+			    *t = c;
+			}
+		    }
+		}
+	    }
+	}
+    }
+    return final;
+}
+
+static LinkList
+cf_pats(int dirs, int noopt, LinkList names, char **accept, char *skipped,
+	char *matcher, char *sdirs, char **fake, char **pats)
+{
+    LinkList ret;
+    char *dpats[2];
+
+    if ((ret = cfp_test_exact(names, accept, skipped)))
+	return cfp_add_sdirs(ret, names, skipped, sdirs, fake);
+
+    if (dirs) {
+	dpats[0] = "*(-/)";
+	dpats[1] = NULL;
+	pats = dpats;
+    }
+    if (!noopt)
+	cfp_opt_pats(pats, matcher);
+
+    return cfp_add_sdirs(cfp_bld_pats(dirs, names, skipped, pats),
+			 names, skipped, sdirs, fake);
+}
+
+static void
+cf_ignore(char **names, LinkList ign, char *style, char *path)
+{
+    int pl = strlen(path), tpar, tpwd, found;
+    struct stat nst, est, st;
+    char *n, *c, *e;
+
+    tpar = !!strstr(style, "parent");
+    if ((tpwd = !!strstr(style, "pwd")) && stat(pwd, &est))
+	tpwd = 0;
+
+    if (!tpar && !tpwd)
+	return;
+
+    for (; (n = *names); names++) {
+	if (!ztat(n, &nst, 0) && S_ISDIR(nst.st_mode)) {
+	    if (tpwd && nst.st_dev == est.st_dev && nst.st_ino == est.st_ino) {
+		addlinknode(ign, bslashquote(n, NULL, 0));
+		continue;
+	    }
+	    if (tpar && !strncmp((c = dupstring(n)), path, pl)) {
+		found = 0;
+		while ((e = strrchr(c, '/')) && e > c + pl) {
+		    *e = '\0';
+		    if (!ztat(c, &st, 0) &&
+			st.st_dev == nst.st_dev && st.st_ino == nst.st_ino) {
+			found = 1;
+			break;
+		    }
+		}
+		if (found || ((e = strrchr(c, '/')) && e > c + pl &&
+			      !ztat(c, &st, 0) && st.st_dev == nst.st_dev &&
+			      st.st_ino == nst.st_ino))
+		    addlinknode(ign, bslashquote(n, NULL, 0));
+	    }
+	}
+    }
+}
+
+static LinkList
+cf_remove_other(char **names, char *pre, int *amb)
+{
+    char *p;
+
+    if ((p = strchr(pre, '/'))) {
+	char **n;
+
+	*p = '\0';
+	pre = dyncat(pre, "/");
+	*p = '/';
+
+	for (n = names; *n; n++)
+	    if (strpfx(pre, *n))
+		break;
+
+	if (*n) {
+	    LinkList ret = newlinklist();
+
+	    for (; *names; names++)
+		if (strpfx(pre, *names))
+		    addlinknode(ret, dupstring(*names));
+
+	    *amb = 0;
+
+	    return ret;
+	} else {
+	    if (!(p = *names++))
+		*amb = 0;
+	    else {
+		char *q;
+
+		if ((q = strchr((p = dupstring(p)), '/')))
+		    *q = '\0';
+
+		for (; *names; names++)
+		    if (!strpfx(p, *names)) {
+			*amb = 1;
+			return NULL;
+		    }
+	    }
+	}
+    } else {
+	if (!(p = *names++))
+	    *amb = 0;
+	else
+	    for (; *names; names++)
+		if (strcmp(p, *names)) {
+		    *amb = 1;
+		    return NULL;
+		}
+    }
+    return NULL;
+}
+
 static int
-bin_compfmt(char *nam, char **args, char *ops, int func)
+bin_compfiles(char *nam, char **args, char *ops, int func)
 {
-    char *param = args[0], *str = args[1];
+    if (incompfunc != 1) {
+	zwarnnam(nam, "can only be called from completion function", NULL, 0);
+	return 1;
+    }
+    if (**args != '-') {
+	zwarnnam(nam, "missing option: %s", *args, 0);
+	return 1;
+    }
+    switch (args[0][1]) {
+    case 'p':
+    case 'P':
+	if (args[0][2] && (args[0][2] != '-' || args[0][3])) {
+	    zwarnnam(nam, "invalid option: %s", *args, 0);
+	    return 1;
+	} else {
+	    char **tmp;
+	    LinkList l;
 
-    for (args += 2; *args; args++) {
-	if (args[0][1] != ':') {
-	    zwarnnam(nam, "invalid argument `%s'", args[0], 0);
+	    if (!args[1] || !args[2] || !args[3] || !args[4] || !args[5] ||
+		!args[6] || (args[0][1] == 'p' && !args[7])) {
+		zwarnnam(nam, "too few arguments", NULL, 0);
+		return 1;
+	    }
+	    queue_signals();
+	    if (!(tmp = getaparam(args[1]))) {
+		zwarnnam(nam, "unknown parameter: %s", args[1], 0);
+		return 0;
+	    }
+	    for (l = newlinklist(); *tmp; tmp++)
+		addlinknode(l, *tmp);
+	    set_list_array(args[1], cf_pats((args[0][1] == 'P'), !!args[0][2],
+					    l, getaparam(args[2]), args[3],
+					    args[4], args[5],
+					    getaparam(args[6]), args + 7));
+	    unqueue_signals();
+	    return 0;
+	}
+    case 'i':
+	if (args[0][2]) {
+	    zwarnnam(nam, "invalid option: %s", *args, 0);
 	    return 1;
+	} else {
+	    char **tmp;
+	    LinkList l;
+
+	    if (!args[1] || !args[2] || !args[3] || !args[4]) {
+		zwarnnam(nam, "too few arguments", NULL, 0);
+		return 1;
+	    }
+	    if (args[5]) {
+		zwarnnam(nam, "too many arguments", NULL, 0);
+		return 1;
+	    }
+	    queue_signals();
+	    tmp = getaparam(args[2]);
+	    l = newlinklist();
+	    if (tmp)
+		for (; *tmp; tmp++)
+		    addlinknode(l, *tmp);
+	    if (!(tmp = getaparam(args[1]))) {
+		unqueue_signals();
+		zwarnnam(nam, "unknown parameter: %s", args[1], 0);
+		return 0;
+	    }
+	    cf_ignore(tmp, l, args[3], args[4]);
+	    unqueue_signals();
+	    set_list_array(args[2], l);
+	    return 0;
+	}
+    case 'r':
+	{
+	    char **tmp;
+	    LinkList l;
+	    int ret = 0;
+
+	    if (!args[1] || !args[2]) {
+		zwarnnam(nam, "too few arguments", NULL, 0);
+		return 1;
+	    }
+	    if (args[3]) {
+		zwarnnam(nam, "too many arguments", NULL, 0);
+		return 1;
+	    }
+	    queue_signals();
+	    if (!(tmp = getaparam(args[1]))) {
+		unqueue_signals();
+		zwarnnam(nam, "unknown parameter: %s", args[1], 0);
+		return 0;
+	    }
+	    if ((l = cf_remove_other(tmp, args[2], &ret)))
+		set_list_array(args[1], l);
+	    unqueue_signals();
+	    return ret;
 	}
-	str = fmtstr(str, **args, *args + 2);
     }
-    setsparam(param, ztrdup(str));
+    zwarnnam(nam, "invalid option: %s", *args, 0);
+    return 1;
+}
+
+static int
+bin_compgroups(char *nam, char **args, char *ops, int func)
+{
+    Heap oldheap;
+    char *n;
+
+    if (incompfunc != 1) {
+	zwarnnam(nam, "can only be called from completion function", NULL, 0);
+	return 1;
+    }
+    SWITCHHEAPS(oldheap, compheap) {
+	while ((n = *args++)) {
+	    endcmgroup(NULL);
+	    begcmgroup(n, 0);
+	    endcmgroup(NULL);
+	    begcmgroup(n, CGF_NOSORT);
+	    endcmgroup(NULL);
+	    begcmgroup(n, CGF_UNIQALL);
+	    endcmgroup(NULL);
+	    begcmgroup(n, CGF_NOSORT|CGF_UNIQCON);
+	    endcmgroup(NULL);
+	    begcmgroup(n, CGF_UNIQALL);
+	    endcmgroup(NULL);
+	    begcmgroup(n, CGF_NOSORT|CGF_UNIQCON);
+	}
+    } SWITCHBACKHEAPS(oldheap);
+
     return 0;
 }
 
@@ -2479,10 +3875,11 @@ static struct builtin bintab[] = {
     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("compquote", 0, bin_compquote, 1, -1, 0, "p", 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),
+    BUILTIN("compfiles", 0, bin_compfiles, 1, -1, 0, NULL, NULL),
+    BUILTIN("compgroups", 0, bin_compgroups, 1, -1, 0, NULL, NULL),
 };