summaryrefslogtreecommitdiff
path: root/Src/Zle/compctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Zle/compctl.c')
-rw-r--r--Src/Zle/compctl.c2980
1 files changed, 2916 insertions, 64 deletions
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index 658cf4161..b1fc694c4 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -30,11 +30,32 @@
#include "compctl.mdh"
#include "compctl.pro"
+/* Global matcher. */
+
+/**/
+static Cmlist cmatcher;
+
+/* Default completion infos */
+
+/**/
+struct compctl cc_compos, cc_default, cc_first, cc_dummy;
+
+/* Hash table for completion info for commands */
+
+/**/
+HashTable compctltab;
+
+/* List of pattern compctls */
+
+/**/
+Patcomp patcomps;
+
#define COMP_LIST (1<<0) /* -L */
#define COMP_COMMAND (1<<1) /* -C */
#define COMP_DEFAULT (1<<2) /* -D */
#define COMP_FIRST (1<<3) /* -T */
#define COMP_REMOVE (1<<4)
+#define COMP_LISTMATCH (1<<5) /* -L and -M */
#define COMP_SPECIAL (COMP_COMMAND|COMP_DEFAULT|COMP_FIRST)
@@ -44,11 +65,312 @@ static int cclist;
/* Mask for determining what to print */
static unsigned long showmask = 0;
+/**/
+static void
+createcompctltable(void)
+{
+ compctltab = newhashtable(23, "compctltab", NULL);
+
+ compctltab->hash = hasher;
+ compctltab->emptytable = emptyhashtable;
+ compctltab->filltable = NULL;
+ compctltab->cmpnodes = strcmp;
+ compctltab->addnode = addhashnode;
+ compctltab->getnode = gethashnode2;
+ compctltab->getnode2 = gethashnode2;
+ compctltab->removenode = removehashnode;
+ compctltab->disablenode = NULL;
+ compctltab->enablenode = NULL;
+ compctltab->freenode = freecompctlp;
+ compctltab->printnode = printcompctlp;
+
+ patcomps = NULL;
+}
+
+/**/
+static void
+freecompctlp(HashNode hn)
+{
+ Compctlp ccp = (Compctlp) hn;
+
+ zsfree(ccp->nam);
+ freecompctl(ccp->cc);
+ zfree(ccp, sizeof(struct compctlp));
+}
+
+/**/
+void
+freecompctl(Compctl cc)
+{
+ if (cc == &cc_default ||
+ cc == &cc_first ||
+ cc == &cc_compos ||
+ --cc->refc > 0)
+ return;
+
+ zsfree(cc->keyvar);
+ zsfree(cc->glob);
+ zsfree(cc->str);
+ zsfree(cc->func);
+ zsfree(cc->explain);
+ zsfree(cc->ylist);
+ zsfree(cc->prefix);
+ zsfree(cc->suffix);
+ zsfree(cc->hpat);
+ zsfree(cc->gname);
+ zsfree(cc->subcmd);
+ zsfree(cc->substr);
+ if (cc->cond)
+ freecompcond(cc->cond);
+ if (cc->ext) {
+ Compctl n, m;
+
+ n = cc->ext;
+ do {
+ m = (Compctl) (n->next);
+ freecompctl(n);
+ n = m;
+ }
+ while (n);
+ }
+ if (cc->xor && cc->xor != &cc_default)
+ freecompctl(cc->xor);
+ if (cc->matcher)
+ freecmatcher(cc->matcher);
+ zsfree(cc->mstr);
+ zfree(cc, sizeof(struct compctl));
+}
+
+/**/
+void
+freecompcond(void *a)
+{
+ Compcond cc = (Compcond) a;
+ Compcond and, or, c;
+ int n;
+
+ for (c = cc; c; c = or) {
+ or = c->or;
+ for (; c; c = and) {
+ and = c->and;
+ if (c->type == CCT_POS ||
+ c->type == CCT_NUMWORDS) {
+ free(c->u.r.a);
+ free(c->u.r.b);
+ } else if (c->type == CCT_CURSUF ||
+ c->type == CCT_CURPRE) {
+ for (n = 0; n < c->n; n++)
+ if (c->u.s.s[n])
+ zsfree(c->u.s.s[n]);
+ free(c->u.s.s);
+ } else if (c->type == CCT_RANGESTR ||
+ c->type == CCT_RANGEPAT) {
+ for (n = 0; n < c->n; n++)
+ if (c->u.l.a[n])
+ zsfree(c->u.l.a[n]);
+ free(c->u.l.a);
+ for (n = 0; n < c->n; n++)
+ if (c->u.l.b[n])
+ zsfree(c->u.l.b[n]);
+ free(c->u.l.b);
+ } else {
+ for (n = 0; n < c->n; n++)
+ if (c->u.s.s[n])
+ zsfree(c->u.s.s[n]);
+ free(c->u.s.p);
+ free(c->u.s.s);
+ }
+ zfree(c, sizeof(struct compcond));
+ }
+ }
+}
+
+/**/
+int
+compctlread(char *name, char **args, char *ops, char *reply)
+{
+ char *buf, *bptr;
+
+ /* only allowed to be called for completion */
+ if (!incompctlfunc) {
+ zwarnnam(name, "option valid only in functions called for completion",
+ NULL, 0);
+ return 1;
+ }
+
+ if (ops['l']) {
+ /* -ln gives the index of the word the cursor is currently on, which is
+ available in cs (but remember that Zsh counts from one, not zero!) */
+ if (ops['n']) {
+ char nbuf[14];
+
+ if (ops['e'] || ops['E'])
+ printf("%d\n", cs + 1);
+ if (!ops['e']) {
+ sprintf(nbuf, "%d", cs + 1);
+ setsparam(reply, ztrdup(nbuf));
+ }
+ return 0;
+ }
+ /* without -n, the current line is assigned to the given parameter as a
+ scalar */
+ if (ops['e'] || ops['E']) {
+ zputs((char *) line, stdout);
+ putchar('\n');
+ }
+ if (!ops['e'])
+ setsparam(reply, ztrdup((char *) line));
+ } else {
+ int i;
+
+ /* -cn gives the current cursor position within the current word, which
+ is available in clwpos (but remember that Zsh counts from one, not
+ zero!) */
+ if (ops['n']) {
+ char nbuf[14];
+
+ if (ops['e'] || ops['E'])
+ printf("%d\n", clwpos + 1);
+ if (!ops['e']) {
+ sprintf(nbuf, "%d", clwpos + 1);
+ setsparam(reply, ztrdup(nbuf));
+ }
+ return 0;
+ }
+ /* without -n, the words of the current line are assigned to the given
+ parameters separately */
+ if (ops['A'] && !ops['e']) {
+ /* the -A option means that one array is specified, instead of
+ many parameters */
+ char **p, **b = (char **)zcalloc((clwnum + 1) * sizeof(char *));
+
+ for (i = 0, p = b; i < clwnum; p++, i++)
+ *p = ztrdup(clwords[i]);
+
+ setaparam(reply, b);
+ return 0;
+ }
+ if (ops['e'] || ops['E']) {
+ for (i = 0; i < clwnum; i++) {
+ zputs(clwords[i], stdout);
+ putchar('\n');
+ }
+
+ if (ops['e'])
+ return 0;
+ }
+
+ for (i = 0; i < clwnum && *args; reply = *args++, i++)
+ setsparam(reply, ztrdup(clwords[i]));
+
+ if (i < clwnum) {
+ int j, len;
+
+ for (j = i, len = 0; j < clwnum; len += strlen(clwords[j++]));
+ bptr = buf = zalloc(len + j - i);
+ while (i < clwnum) {
+ strucpy(&bptr, clwords[i++]);
+ *bptr++ = ' ';
+ }
+ bptr[-1] = '\0';
+ } else
+ buf = ztrdup("");
+ setsparam(reply, buf);
+ }
+ return 0;
+}
+
+/* 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;
+}
+
+/* Set the global match specs. */
+
+/**/
+static 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);
+ cmatcher = cpcmlist(l);
+
+ return 1;
+}
+
+/* Try to get the global matcher from the given compctl. */
+
+/**/
+static int
+get_gmatcher(char *name, char **argv)
+{
+ if (!strcmp(*argv, "-M")) {
+ char **p = ++argv;
+
+ while (*p) {
+ if (**p++ == '-')
+ return 0;
+ }
+ if (set_gmatcher(name, argv))
+ return 2;
+
+ return 1;
+ }
+ return 0;
+}
+
+/* This prints the global matcher definitions. */
+
+/**/
+static void
+print_gmatcher(int ac)
+{
+ Cmlist p;
+
+ if ((p = cmatcher)) {
+ printf((ac ? "compctl -M" : "MATCH"));
+
+ while (p) {
+ printf(" \'%s\'", p->str);
+
+ p = p->next;
+ }
+ putchar('\n');
+ }
+}
+
/* Parse the basic flags for `compctl' */
/**/
static int
-get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
+get_compctl(char *name, char ***av, Compctl cc, int first, int isdef, int cl)
{
/* Parse the basic flags for completion:
* first is a flag that we are not in extended completion,
@@ -69,12 +391,17 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
if(argv[0] && argv[0][0] == '-')
argv++;
*av = argv;
- freecompctl(cc);
- cclist = COMP_REMOVE;
- return 0;
+ if (cl)
+ return 1;
+ else {
+ freecompctl(cc);
+ cclist = COMP_REMOVE;
+ return 0;
+ }
}
memset((void *)&cct, 0, sizeof(cct));
+ cct.mask2 = CC_CCCONT;
/* Loop through the flags until we have no more: *
* those with arguments are not properly allocated yet, *
@@ -176,6 +503,50 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
case '/':
cct.mask |= CC_DIRS;
break;
+ case 't':
+ {
+ char *p;
+
+ if (cl) {
+ zwarnnam(name, "illegal option -%c", NULL, **argv);
+ return 1;
+ }
+ if ((*argv)[1]) {
+ p = (*argv) + 1;
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "retry specification expected after -%c", NULL,
+ **argv);
+ return 1;
+ } else {
+ p = *++argv;
+ *argv = "" - 1;
+ }
+ switch (*p) {
+ case '+':
+ cct.mask2 = CC_XORCONT;
+ break;
+ case 'n':
+ cct.mask2 = 0;
+ break;
+ case '-':
+ cct.mask2 = CC_PATCONT;
+ break;
+ case 'x':
+ cct.mask2 = CC_DEFCONT;
+ break;
+ default:
+ zwarnnam(name, "invalid retry specification character `%c'",
+ NULL, *p);
+ return 1;
+ }
+ if (p[1]) {
+ zwarnnam(name, "too many retry specification characters: `%s'",
+ p + 1, 0);
+ return 1;
+ }
+ }
+ break;
case 'k':
if ((*argv)[1]) {
cct.keyvar = (*argv) + 1;
@@ -282,7 +653,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
}
break;
case 'l':
- if ((*argv)[1]) {
+ if (cl) {
+ zwarnnam(name, "illegal option -%c", NULL, **argv);
+ return 1;
+ } else if ((*argv)[1]) {
cct.subcmd = (*argv) + 1;
*argv = "" - 1;
} else if (!argv[1]) {
@@ -294,6 +668,22 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
*argv = "" - 1;
}
break;
+ case 'h':
+ if (cl) {
+ zwarnnam(name, "illegal option -%c", NULL, **argv);
+ return 1;
+ } else if ((*argv)[1]) {
+ cct.substr = (*argv) + 1;
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "command name expected after -%c", NULL,
+ **argv);
+ return 1;
+ } else {
+ cct.substr = *++argv;
+ *argv = "" - 1;
+ }
+ break;
case 'W':
if ((*argv)[1]) {
cct.withd = (*argv) + 1;
@@ -307,6 +697,68 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
*argv = "" - 1;
}
break;
+ case 'J':
+ if ((*argv)[1]) {
+ cct.gname = (*argv) + 1;
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "group name expected after -%c", NULL,
+ **argv);
+ return 1;
+ } else {
+ cct.gname = *++argv;
+ *argv = "" - 1;
+ }
+ break;
+ case 'V':
+ if ((*argv)[1]) {
+ cct.gname = (*argv) + 1;
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "group name expected after -%c", NULL,
+ **argv);
+ return 1;
+ } else {
+ cct.gname = *++argv;
+ *argv = "" - 1;
+ }
+ cct.mask2 |= CC_NOSORT;
+ break;
+ case '1':
+ cct.mask2 |= CC_UNIQALL;
+ cct.mask2 &= ~CC_UNIQCON;
+ break;
+ case '2':
+ cct.mask2 |= CC_UNIQCON;
+ cct.mask2 &= ~CC_UNIQALL;
+ break;
+ case 'M':
+ if (cclist & COMP_LIST) {
+ cclist |= COMP_LISTMATCH;
+ } else if ((*argv)[1]) {
+ if ((cct.matcher =
+ parse_cmatcher(name, (cct.mstr = (*argv) + 1))) ==
+ pcm_err) {
+ cct.matcher = NULL;
+ cct.mstr = NULL;
+ return 1;
+ }
+ *argv = "" - 1;
+ } else if (!argv[1]) {
+ zwarnnam(name, "matching specification expected after -%c", NULL,
+ **argv);
+ return 1;
+ } else {
+ if ((cct.matcher =
+ parse_cmatcher(name, (cct.mstr = *++argv))) ==
+ pcm_err) {
+ cct.matcher = NULL;
+ cct.mstr = NULL;
+ return 1;
+ }
+ *argv = "" - 1;
+ }
+ break;
case 'H':
if ((*argv)[1])
cct.hnum = atoi((*argv) + 1);
@@ -330,6 +782,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
*argv = "" - 1;
break;
case 'C':
+ if (cl) {
+ zwarnnam(name, "illegal option -%c", NULL, **argv);
+ return 1;
+ }
if (first && !hx) {
cclist |= COMP_COMMAND;
} else {
@@ -339,6 +795,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
}
break;
case 'D':
+ if (cl) {
+ zwarnnam(name, "illegal option -%c", NULL, **argv);
+ return 1;
+ }
if (first && !hx) {
isdef = 1;
cclist |= COMP_DEFAULT;
@@ -349,7 +809,11 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
}
break;
case 'T':
- if (first && !hx) {
+ if (cl) {
+ zwarnnam(name, "illegal option -%c", NULL, **argv);
+ return 1;
+ }
+ if (first && !hx) {
cclist |= COMP_FIRST;
} else {
zwarnnam(name, "misplaced first completion (-T) flag",
@@ -358,6 +822,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
}
break;
case 'L':
+ if (cl) {
+ zwarnnam(name, "illegal option -%c", NULL, **argv);
+ return 1;
+ }
if (!first || hx) {
zwarnnam(name, "illegal use of -L flag", NULL, 0);
return 1;
@@ -365,6 +833,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
cclist |= COMP_LIST;
break;
case 'x':
+ if (cl) {
+ zwarnnam(name, "extended completion not allowed", NULL, 0);
+ return 1;
+ }
if (!argv[1]) {
zwarnnam(name, "condition expected after -%c", NULL,
**argv);
@@ -396,6 +868,10 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
if (*++argv && (!ready || ready == 2) &&
**argv == '+' && !argv[0][1]) {
+ if (cl) {
+ zwarnnam(name, "xor'ed completion illegal", NULL, 0);
+ return 1;
+ }
/* There's an alternative (+) completion: assign
* what we have so far before moving on to that.
*/
@@ -420,6 +896,7 @@ get_compctl(char *name, char ***av, Compctl cc, int first, int isdef)
cc->xor = (Compctl) zcalloc(sizeof(*cc));
cc = cc->xor;
memset((void *)&cct, 0, sizeof(cct));
+ cct.mask2 = CC_CCCONT;
}
}
}
@@ -460,6 +937,9 @@ get_xcompctl(char *name, char ***av, Compctl cc, int isdef)
t++;
/* First get the condition code */
switch (*t) {
+ case 'q':
+ c->type = CCT_QUOTE;
+ break;
case 's':
c->type = CCT_CURSUF;
break;
@@ -544,7 +1024,8 @@ get_xcompctl(char *name, char ***av, Compctl cc, int isdef)
c->u.r.a = (int *)zcalloc(n * sizeof(int));
c->u.r.b = (int *)zcalloc(n * sizeof(int));
} else if (c->type == CCT_CURSUF ||
- c->type == CCT_CURPRE)
+ c->type == CCT_CURPRE ||
+ c->type == CCT_QUOTE)
c->u.s.s = (char **)zcalloc(n * sizeof(char *));
else if (c->type == CCT_RANGESTR ||
@@ -586,7 +1067,8 @@ get_xcompctl(char *name, char ***av, Compctl cc, int isdef)
c->u.r.b[l] = atoi(tt);
}
} else if (c->type == CCT_CURSUF ||
- c->type == CCT_CURPRE) {
+ c->type == CCT_CURPRE ||
+ c->type == CCT_QUOTE) {
/* -s[..] or -S[..]: single string expected */
for (; *t && *t != '\200'; t++)
if (*t == '\201')
@@ -600,27 +1082,34 @@ get_xcompctl(char *name, char ***av, Compctl cc, int isdef)
c->u.s.s[l] = ztrdup(tt);
} else if (c->type == CCT_RANGESTR ||
c->type == CCT_RANGEPAT) {
+ int hc;
+
/* -r[..,..] or -R[..,..]: two strings expected */
- for (; *t && *t != '\201'; t++);
+ for (; *t && *t != '\201' && *t != '\200'; t++);
if (!*t) {
zwarnnam(name, "error in condition", NULL, 0);
freecompcond(m);
return 1;
}
+ hc = (*t == '\201');
*t = '\0';
c->u.l.a[l] = ztrdup(tt);
- tt = ++t;
- /* any more commas are text, not active */
- for (; *t && *t != '\200'; t++)
- if (*t == '\201')
- *t = ',';
- if (!*t) {
- zwarnnam(name, "error in condition", NULL, 0);
- freecompcond(m);
- return 1;
+ if (hc) {
+ tt = ++t;
+ /* any more commas are text, not active */
+ for (; *t && *t != '\200'; t++)
+ if (*t == '\201')
+ *t = ',';
+ if (!*t) {
+ zwarnnam(name, "error in condition", NULL, 0);
+ freecompcond(m);
+ return 1;
+ }
+ *t = '\0';
+ c->u.l.b[l] = ztrdup(tt);
}
- *t = '\0';
- c->u.l.b[l] = ztrdup(tt);
+ else
+ c->u.l.b[l] = NULL;
} else {
/* remaining patterns are number followed by string */
for (; *t && *t != '\200' && *t != '\201'; t++);
@@ -662,7 +1151,7 @@ get_xcompctl(char *name, char ***av, Compctl cc, int isdef)
(*next)->cond = m;
argv++;
/* End of the condition; get the flags that go with it. */
- if (get_compctl(name, &argv, *next, 0, isdef))
+ if (get_compctl(name, &argv, *next, 0, isdef, 0))
return 1;
if ((!argv || !*argv) && (cclist & COMP_SPECIAL))
/* default, first, or command completion finished */
@@ -700,13 +1189,6 @@ cc_assign(char *name, Compctl *ccptr, Compctl cct, int reass)
/* Copy over the details from the values in cct to those in *ccptr */
Compctl cc;
- if (cct->subcmd && (cct->keyvar || cct->glob || cct->str ||
- cct->func || cct->explain || cct->ylist ||
- cct->prefix)) {
- zwarnnam(name, "illegal combination of options", NULL, 0);
- return 1;
- }
-
/* Handle assignment of new default or command completion */
if (reass && !(cclist & COMP_LIST)) {
/* if not listing */
@@ -744,13 +1226,18 @@ cc_assign(char *name, Compctl *ccptr, Compctl cct, int reass)
zsfree(cc->prefix);
zsfree(cc->suffix);
zsfree(cc->subcmd);
+ zsfree(cc->substr);
zsfree(cc->withd);
zsfree(cc->hpat);
-
+ zsfree(cc->gname);
+ zsfree(cc->mstr);
+ freecmatcher(cc->matcher);
+
/* and copy over the new stuff, (permanently) allocating
* space for strings.
*/
cc->mask = cct->mask;
+ cc->mask2 = cct->mask2;
cc->keyvar = ztrdup(cct->keyvar);
cc->glob = ztrdup(cct->glob);
cc->str = ztrdup(cct->str);
@@ -760,9 +1247,13 @@ cc_assign(char *name, Compctl *ccptr, Compctl cct, int reass)
cc->prefix = ztrdup(cct->prefix);
cc->suffix = ztrdup(cct->suffix);
cc->subcmd = ztrdup(cct->subcmd);
+ cc->substr = ztrdup(cct->substr);
cc->withd = ztrdup(cct->withd);
+ cc->gname = ztrdup(cct->gname);
cc->hpat = ztrdup(cct->hpat);
cc->hnum = cct->hnum;
+ cc->matcher = cpcmatcher(cct->matcher);
+ cc->mstr = ztrdup(cct->mstr);
/* careful with extended completion: it's already allocated */
cc->ext = cct->ext;
@@ -790,16 +1281,62 @@ cc_reassign(Compctl cc)
cc->ext = cc->xor = NULL;
}
+/* Check if the given string is a pattern. If so, return one and tokenize *
+ * it. If not, we just remove the backslashes. */
+
+static int
+compctl_name_pat(char **p)
+{
+ char *s = *p;
+
+ tokenize(s = dupstring(s));
+ remnulargs(s);
+
+ if (haswilds(s)) {
+ *p = s;
+ return 1;
+ } else
+ *p = rembslash(*p);
+
+ return 0;
+}
+
+/* Delete the pattern compctl with the given name. */
+
+static void
+delpatcomp(char *n)
+{
+ Patcomp p, q;
+
+ for (q = 0, p = patcomps; p; q = p, p = p->next) {
+ if (!strcmp(n, p->pat)) {
+ if (q)
+ q->next = p->next;
+ else
+ patcomps = p->next;
+ zsfree(p->pat);
+ freecompctl(p->cc);
+ free(p);
+
+ break;
+ }
+ }
+}
+
/**/
static void
compctl_process_cc(char **s, Compctl cc)
{
Compctlp ccp;
+ char *n;
if (cclist & COMP_REMOVE) {
/* Delete entries for the commands listed */
for (; *s; s++) {
- if ((ccp = (Compctlp) compctltab->removenode(compctltab, *s)))
+ n = *s;
+ if (compctl_name_pat(&n))
+ delpatcomp(n);
+ else if ((ccp = (Compctlp) compctltab->removenode(compctltab, n)))
compctltab->freenode((HashNode) ccp);
}
} else {
@@ -807,10 +1344,23 @@ compctl_process_cc(char **s, Compctl cc)
cc->refc = 0;
for (; *s; s++) {
+ n = *s;
+
cc->refc++;
- ccp = (Compctlp) zalloc(sizeof *ccp);
- ccp->cc = cc;
- compctltab->addnode(compctltab, ztrdup(*s), ccp);
+ if (compctl_name_pat(&n)) {
+ Patcomp pc;
+
+ delpatcomp(n);
+ pc = zalloc(sizeof *pc);
+ pc->pat = ztrdup(n);
+ pc->cc = cc;
+ pc->next = patcomps;
+ patcomps = pc;
+ } else {
+ ccp = (Compctlp) zalloc(sizeof *ccp);
+ ccp->cc = cc;
+ compctltab->addnode(compctltab, ztrdup(n), ccp);
+ }
}
}
}
@@ -819,15 +1369,21 @@ compctl_process_cc(char **s, Compctl cc)
/**/
static void
-printcompctl(char *s, Compctl cc, int printflags)
+printcompctl(char *s, Compctl cc, int printflags, int ispat)
{
Compctl cc2;
char *css = "fcqovbAIFpEjrzBRGudeNOZUnQmw/";
- char *mss = " pcCwWsSnNmrR";
+ char *mss = " pcCwWsSnNmrRq";
unsigned long t = 0x7fffffff;
- unsigned long flags = cc->mask;
+ unsigned long flags = cc->mask, flags2 = cc->mask2;
unsigned long oldshowmask;
+ /* Printflags is used outside the standard compctl commands*/
+ if (printflags & PRINT_LIST)
+ cclist |= COMP_LIST;
+ else if (printflags & PRINT_TYPE)
+ cclist &= ~COMP_LIST;
+
if ((flags & CC_EXCMDS) && !(flags & CC_DISCMDS))
flags &= ~CC_EXCMDS;
@@ -851,12 +1407,17 @@ printcompctl(char *s, Compctl cc, int printflags)
printf(" -D");
if (cc == &cc_first)
printf(" -T");
+ } else if (ispat) {
+ char *p = dupstring(s);
+
+ untokenize(p);
+ quotedzputs(p, stdout);
} else
- quotedzputs(s, stdout);
+ quotedzputs(bslashquote(s, NULL, 0), stdout);
}
/* loop through flags w/o args that are set, printing them if so */
- if (flags & t) {
+ if ((flags & t) || (flags2 & (CC_UNIQALL | CC_UNIQCON))) {
printf(" -");
if ((flags & (CC_ALREG | CC_ALGLOB)) == (CC_ALREG | CC_ALGLOB))
putchar('a'), flags &= ~(CC_ALREG | CC_ALGLOB);
@@ -867,10 +1428,27 @@ printcompctl(char *s, Compctl cc, int printflags)
flags >>= 1;
t >>= 1;
}
+ if (flags2 & CC_UNIQALL)
+ putchar('1');
+ else if (flags2 & CC_UNIQCON)
+ putchar('2');
}
-
+ if (flags2 & (CC_XORCONT | CC_PATCONT | CC_DEFCONT)) {
+ printf(" -t");
+ if (flags2 & CC_XORCONT)
+ putchar('+');
+ if (flags2 & CC_PATCONT)
+ putchar('-');
+ if (flags2 & CC_DEFCONT)
+ putchar('x');
+ } else if (!(flags2 & CC_CCCONT))
+ printf(" -tn");
/* now flags with arguments */
- flags = cc->mask;
+ printif(cc->mstr, 'M');
+ if (flags2 & CC_NOSORT)
+ printif(cc->gname, 'V');
+ else
+ printif(cc->gname, 'J');
printif(cc->keyvar, 'k');
printif(cc->func, 'K');
printif(cc->explain, (cc->mask & CC_EXPANDEXPL) ? 'Y' : 'X');
@@ -880,6 +1458,7 @@ printcompctl(char *s, Compctl cc, int printflags)
printif(cc->glob, 'g');
printif(cc->str, 's');
printif(cc->subcmd, 'l');
+ printif(cc->substr, 'h');
printif(cc->withd, 'W');
if (cc->hpat) {
printf(" -H %d ", cc->hnum);
@@ -916,6 +1495,7 @@ printcompctl(char *s, Compctl cc, int printflags)
break;
case CCT_CURSUF:
case CCT_CURPRE:
+ case CCT_QUOTE:
printqt(c->u.s.s[i]);
break;
case CCT_RANGESTR:
@@ -940,7 +1520,7 @@ printcompctl(char *s, Compctl cc, int printflags)
c = cc2->cond;
cc2->cond = NULL;
/* now print the flags for the current condition */
- printcompctl(NULL, cc2, 0);
+ printcompctl(NULL, cc2, 0, 0);
cc2->cond = c;
if ((cc2 = (Compctl) (cc2->next)))
printf(" -");
@@ -952,7 +1532,7 @@ printcompctl(char *s, Compctl cc, int printflags)
/* print xor'd (+) completions */
printf(" +");
if (cc->xor != &cc_default)
- printcompctl(NULL, cc->xor, 0);
+ printcompctl(NULL, cc->xor, 0, 0);
}
if (s) {
if ((cclist & COMP_LIST) && (cc != &cc_compos)
@@ -960,7 +1540,17 @@ printcompctl(char *s, Compctl cc, int printflags)
if(s[0] == '-' || s[0] == '+')
printf(" -");
putchar(' ');
- quotedzputs(s, stdout);
+ if (ispat) {
+ char *p = dupstring(s);
+
+ untokenize(p);
+ quotedzputs(p, stdout);
+ } else {
+ char *p = dupstring(s);
+
+ untokenize(p);
+ quotedzputs(bslashquote(p, NULL, 0), stdout);
+ }
}
putchar('\n');
}
@@ -975,7 +1565,7 @@ printcompctlp(HashNode hn, int printflags)
Compctlp ccp = (Compctlp) hn;
/* Function needed for use by scanhashtable() */
- printcompctl(ccp->nam, ccp->cc, printflags);
+ printcompctl(ccp->nam, ccp->cc, printflags, 0);
}
/* Main entry point for the `compctl' builtin */
@@ -993,8 +1583,12 @@ bin_compctl(char *name, char **argv, char *ops, int func)
/* Parse all the arguments */
if (*argv) {
+ /* Let's see if this is a global matcher definition. */
+ if ((ret = get_gmatcher(name, argv)))
+ return ret - 1;
+
cc = (Compctl) zcalloc(sizeof(*cc));
- if (get_compctl(name, &argv, cc, 1, 0)) {
+ if (get_compctl(name, &argv, cc, 1, 0, 0)) {
freecompctl(cc);
return 1;
}
@@ -1012,11 +1606,17 @@ bin_compctl(char *name, char **argv, char *ops, int func)
/* If no commands and no -C, -T, or -D, print all the compctl's *
* If some flags (other than -C, -T, or -D) were given, then *
* only print compctl containing those flags. */
- if (!*argv && !(cclist & COMP_SPECIAL)) {
+ if (!*argv && !(cclist & (COMP_SPECIAL|COMP_LISTMATCH))) {
+ Patcomp pc;
+
+ for (pc = patcomps; pc; pc = pc->next)
+ printcompctl(pc->pat, pc->cc, 0, 1);
+
scanhashtable(compctltab, 1, 0, 0, compctltab->printnode, 0);
- printcompctl((cclist & COMP_LIST) ? "" : "COMMAND", &cc_compos, 0);
- printcompctl((cclist & COMP_LIST) ? "" : "DEFAULT", &cc_default, 0);
- printcompctl((cclist & COMP_LIST) ? "" : "FIRST", &cc_first, 0);
+ printcompctl((cclist & COMP_LIST) ? "" : "COMMAND", &cc_compos, 0, 0);
+ printcompctl((cclist & COMP_LIST) ? "" : "DEFAULT", &cc_default, 0, 0);
+ printcompctl((cclist & COMP_LIST) ? "" : "FIRST", &cc_first, 0, 0);
+ print_gmatcher((cclist & COMP_LIST));
return ret;
}
@@ -1024,23 +1624,37 @@ bin_compctl(char *name, char **argv, char *ops, int func)
* or a COMP_SPECIAL flag (-D, -C, -T), so print only those. */
if (cclist & COMP_LIST) {
HashNode hn;
- char **ptr;
+ char **ptr, *n;
showmask = 0;
for (ptr = argv; *ptr; ptr++) {
- if ((hn = compctltab->getnode(compctltab, *ptr))) {
+ n = *ptr;
+ if (compctl_name_pat(&n)) {
+ Patcomp pc;
+
+ for (pc = patcomps; pc; pc = pc->next)
+ if (!strcmp(n, pc->pat)) {
+ printcompctl(pc->pat, pc->cc, 0, 1);
+ n = NULL;
+ break;
+ }
+ } else if ((hn = compctltab->getnode(compctltab, n))) {
compctltab->printnode(hn, 0);
- } else {
- zwarnnam(name, "no compctl defined for %s", *ptr, 0);
+ n = NULL;
+ }
+ if (n) {
+ zwarnnam(name, "no compctl defined for %s", n, 0);
ret = 1;
}
}
if (cclist & COMP_COMMAND)
- printcompctl("", &cc_compos, 0);
+ printcompctl("", &cc_compos, 0, 0);
if (cclist & COMP_DEFAULT)
- printcompctl("", &cc_default, 0);
+ printcompctl("", &cc_default, 0, 0);
if (cclist & COMP_FIRST)
- printcompctl("", &cc_first, 0);
+ printcompctl("", &cc_first, 0, 0);
+ if (cclist & COMP_LISTMATCH)
+ print_gmatcher(COMP_LIST);
return ret;
}
@@ -1058,28 +1672,2266 @@ bin_compctl(char *name, char **argv, char *ops, int func)
return ret;
}
+/* Flags for makecomplist*(). Things not to do. */
+
+#define CFN_FIRST 1
+#define CFN_DEFAULT 2
+
+static int
+bin_compcall(char *name, char **argv, char *ops, int func)
+{
+ if (incompfunc != 1) {
+ zwarnnam(name, "can only be called from completion function", NULL, 0);
+ return 1;
+ }
+ return makecomplistctl((ops['T'] ? 0 : CFN_FIRST) |
+ (ops['D'] ? 0 : CFN_DEFAULT));
+}
+
+/*
+ * Functions to generate matches.
+ */
+
+/* A pointer to the compctl we are using. */
+
+static Compctl curcc;
+
+/* A list of all compctls we have already used. */
+
+static LinkList ccused, lastccused;
+
+/* A stack of currently used compctls. */
+
+static LinkList ccstack;
+
+/* The beginning and end of a word range to be used by -l. */
+
+static int brange, erange;
+
+/* This is used to detect when and what to continue. */
+
+static unsigned long ccont;
+
+/* Two patterns used when doing glob-completion. The first one is built *
+ * from the whole word we are completing and the second one from that *
+ * part of the word that was identified as a possible filename. */
+
+static Patprog patcomp, filecomp;
+
+/* We store the following prefixes/suffixes: *
+ * lpre/lsuf -- what's on the line *
+ * rpre/rsuf -- same as lpre/lsuf, but expanded *
+ * ppre/psuf -- the path prefix/suffix *
+ * lppre/lpsuf -- the path prefix/suffix, unexpanded *
+ * fpre/fsuf -- prefix/suffix of the pathname component the cursor is in *
+ * prpre -- ppre in expanded form usable for opendir *
+ * qipre, qisuf-- ingnored quoted string *
+ * *
+ * The integer variables hold the lengths of lpre, lsuf, rpre, rsuf, *
+ * fpre, fsuf, lppre, and lpsuf. noreal is non-zero if we have rpre/rsuf. */
+
+static char *lpre, *lsuf;
+static char *rpre, *rsuf;
+static char *ppre, *psuf, *lppre, *lpsuf, *prpre;
+static char *fpre, *fsuf;
+static char *qfpre, *qfsuf, *qrpre, *qrsuf, *qlpre, *qlsuf;
+static int lpl, lsl, rpl, rsl, fpl, fsl, lppl, lpsl;
+static int noreal;
+
+/* This is either zero or equal to the special character the word we are *
+ * trying to complete starts with (e.g. Tilde or Equals). */
+
+static char ic;
+
+/* This variable says what we are currently adding to the list of matches. */
+
+static int addwhat;
+
+/* Convenience macro for calling bslashquote() (formerly quotename()). *
+ * This uses the instring variable above. */
+
+#define quotename(s, e) bslashquote(s, e, instring)
+
+/* Hook functions */
+
+static int
+ccmakehookfn(Hookdef dummy, struct ccmakedat *dat)
+{
+ char *s = dat->str;
+ int incmd = dat->incmd, lst = dat->lst;
+ struct cmlist ms;
+ Cmlist m;
+ char *os = s;
+ int onm = nmatches, osi = movefd(0);
+ LinkNode n;
+
+ /* 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++) {
+ *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);
+ if (m->matcher)
+ m->matcher->refc++;
+ }
+ m = mm;
+ }
+
+ /* Walk through the global matchers. */
+ for (;;) {
+ bmatchers = NULL;
+ 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);
+ } else
+ mstack = NULL;
+
+ ainfo = (Aminfo) hcalloc(sizeof(struct aminfo));
+ fainfo = (Aminfo) hcalloc(sizeof(struct aminfo));
+
+ freecl = NULL;
+
+ if (!validlist)
+ lastambig = 0;
+ amatches = NULL;
+ mnum = 0;
+ unambig_mnum = -1;
+ isuf = NULL;
+ insmnum = 1;
+#if 0
+ insgnum = 1;
+ insgroup = 0;
+#endif
+ oldlist = oldins = 0;
+ begcmgroup("default", 0);
+ menucmp = menuacc = newmatches = onlyexpl = 0;
+
+ ccused = newlinklist();
+ ccstack = newlinklist();
+
+ s = dupstring(os);
+ makecomplistglobal(s, incmd, lst, 0);
+ endcmgroup(NULL);
+
+ if (amatches && !oldlist) {
+ if (lastccused)
+ freelinklist(lastccused, (FreeFunc) freecompctl);
+
+ lastccused = znewlinklist();
+ for (n = firstnode(ccused); n; incnode(n))
+ zaddlinknode(lastccused, getdata(n));
+ } else if (ccused)
+ for (n = firstnode(ccused); n; incnode(n))
+ if (((Compctl) getdata(n)) != &cc_dummy)
+ freecompctl((Compctl) getdata(n));
+
+ if (oldlist) {
+ nmatches = onm;
+ validlist = 1;
+ amatches = lastmatches;
+ lmatches = lastlmatches;
+ if (pmatches) {
+ freematches(pmatches);
+ pmatches = NULL;
+ hasperm = 0;
+ }
+ redup(osi, 0);
+
+ dat->lst = 0;
+ return 0;
+ }
+ if (lastmatches) {
+ freematches(lastmatches);
+ lastmatches = NULL;
+ }
+ permmatches(1);
+ amatches = pmatches;
+ lastpermmnum = permmnum;
+ lastpermgnum = permgnum;
+
+ lastmatches = pmatches;
+ lastlmatches = lmatches;
+ pmatches = NULL;
+ hasperm = 0;
+ hasoldlist = 1;
+
+ if (nmatches && !errflag) {
+ validlist = 1;
+
+ redup(osi, 0);
+
+ dat->lst = 0;
+ return 0;
+ }
+ if (!m || !(m = m->next))
+ break;
+
+ errflag = 0;
+ }
+ redup(osi, 0);
+ dat->lst = 1;
+ return 0;
+}
+
+static int
+cccleanuphookfn(Hookdef dummy, void *dat)
+{
+ ccused = ccstack = NULL;
+ return 0;
+}
+
+/* This adds a match to the list of matches. The string to add is given *
+ * in s, the type of match is given in the global variable addwhat and *
+ * the parameter t (if not NULL) is a pointer to a hash node node which *
+ * may be used to give other information to this function. *
+ * *
+ * addwhat contains either one of the special values (negative, see below) *
+ * or the inclusive OR of some of the CC_* flags used for compctls. */
+
+/**/
+static void
+addmatch(char *s, char *t)
+{
+ int isfile = 0, isalt = 0, isexact;
+ char *ms = NULL, *tt;
+ HashNode hn;
+ Param pm;
+ Cline lc = NULL;
+ Brinfo bp, bpl = brbeg, bsl = brend, bpt, bst;
+
+ for (bp = brbeg; bp; bp = bp->next)
+ bp->curpos = ((addwhat == CC_QUOTEFLAG) ? bp->qpos : bp->pos);
+ for (bp = brend; bp; bp = bp->next)
+ bp->curpos = ((addwhat == CC_QUOTEFLAG) ? bp->qpos : bp->pos);
+
+ /*
+ * addwhat: -5 is for files,
+ * -6 is for glob expansions,
+ * -8 is for executable files (e.g. command paths),
+ * -9 is for parameters
+ * -7 is for command names (from cmdnamtab)
+ * -4 is for a cdable parameter
+ * -3 is for executable command names.
+ * -2 is for anything unquoted
+ * -1 is for other file specifications
+ * (things with `~' or `=' at the beginning, ...).
+ */
+
+ /* Just to make the code cleaner */
+ hn = (HashNode) t;
+ pm = (Param) t;
+
+ if (addwhat == -1 || addwhat == -5 || addwhat == -6 ||
+ addwhat == CC_FILES || addwhat == -7 || addwhat == -8) {
+ int ppl = (ppre ? strlen(ppre) : 0), psl = (psuf ? strlen(psuf) : 0);
+
+ while (bpl && bpl->curpos < ppl)
+ bpl = bpl->next;
+ while (bsl && bsl->curpos < psl)
+ bsl = bsl->next;
+
+ if ((addwhat == CC_FILES ||
+ addwhat == -5) && !*psuf) {
+ /* If this is a filename, do the fignore check. */
+ char **pt = fignore;
+ int filell, sl = strlen(s);
+
+ for (isalt = 0; !isalt && *pt; pt++)
+ if ((filell = strlen(*pt)) < sl &&
+ !strcmp(*pt, s + sl - filell))
+ isalt = 1;
+ }
+ ms = ((addwhat == CC_FILES || addwhat == -6 ||
+ addwhat == -5 || addwhat == -8) ?
+ comp_match(tildequote(qfpre, 1), multiquote(qfsuf, 1),
+ s, filecomp, &lc, (ppre && *ppre ? 1 : 2),
+ &bpl, ppl ,&bsl, psl, &isexact) :
+ comp_match(multiquote(fpre, 1), multiquote(fsuf, 1),
+ s, filecomp, &lc, 0,
+ &bpl, ppl, &bsl, psl, &isexact));
+ if (!ms)
+ return;
+
+ if (addwhat == -7 && !findcmd(s, 0))
+ return;
+ isfile = CMF_FILE;
+ } else if (addwhat == CC_QUOTEFLAG || addwhat == -2 ||
+ (addwhat == -3 && !(hn->flags & DISABLED)) ||
+ (addwhat == -4 && (PM_TYPE(pm->flags) == PM_SCALAR) &&
+ !pm->level && (tt = pm->gets.cfn(pm)) && *tt == '/') ||
+ (addwhat == -9 && !(hn->flags & PM_UNSET) && !pm->level) ||
+ (addwhat > 0 &&
+ ((!(hn->flags & PM_UNSET) &&
+ (((addwhat & CC_ARRAYS) && (hn->flags & PM_ARRAY)) ||
+ ((addwhat & CC_INTVARS) && (hn->flags & PM_INTEGER)) ||
+ ((addwhat & CC_ENVVARS) && (hn->flags & PM_EXPORTED)) ||
+ ((addwhat & CC_SCALARS) && (hn->flags & PM_SCALAR)) ||
+ ((addwhat & CC_READONLYS) && (hn->flags & PM_READONLY)) ||
+ ((addwhat & CC_SPECIALS) && (hn->flags & PM_SPECIAL)) ||
+ ((addwhat & CC_PARAMS) && !(hn->flags & PM_EXPORTED))) &&
+ !pm->level) ||
+ ((( addwhat & CC_SHFUNCS) ||
+ ( addwhat & CC_BUILTINS) ||
+ ( addwhat & CC_EXTCMDS) ||
+ ( addwhat & CC_RESWDS) ||
+ ((addwhat & CC_ALREG) && !(hn->flags & ALIAS_GLOBAL)) ||
+ ((addwhat & CC_ALGLOB) && (hn->flags & ALIAS_GLOBAL))) &&
+ (((addwhat & CC_DISCMDS) && (hn->flags & DISABLED)) ||
+ ((addwhat & CC_EXCMDS) && !(hn->flags & DISABLED)))) ||
+ ((addwhat & CC_BINDINGS) && !(hn->flags & DISABLED))))) {
+ char *p1, *s1, *p2, *s2;
+
+ if (addwhat == CC_QUOTEFLAG) {
+ p1 = qrpre; s1 = qrsuf;
+ p2 = rpre; s2 = rsuf;
+ } else {
+ p1 = qlpre; s1 = qlsuf;
+ p2 = lpre; s2 = lsuf;
+ }
+ p1 = multiquote(p1, 1); s1 = multiquote(s1, 1);
+ p2 = multiquote(p2, 1); s2 = multiquote(s2, 1);
+ bpt = bpl;
+ bst = bsl;
+
+ if (!(ms = comp_match(p1, s1, s, patcomp, &lc,
+ (addwhat == CC_QUOTEFLAG),
+ &bpl, strlen(p1), &bsl, strlen(s1),
+ &isexact))) {
+ bpl = bpt;
+ bsl = bst;
+ if (!(ms = comp_match(p2, s2, s, NULL, &lc,
+ (addwhat == CC_QUOTEFLAG),
+ &bpl, strlen(p2), &bsl, strlen(s2),
+ &isexact)))
+ return;
+ }
+ }
+ if (!ms)
+ return;
+ add_match_data(isalt, ms, lc, ipre, ripre, isuf,
+ (incompfunc ? dupstring(curcc->prefix) : curcc->prefix),
+ prpre,
+ (isfile ? lppre : NULL), NULL,
+ (isfile ? lpsuf : NULL), NULL,
+ (incompfunc ? dupstring(curcc->suffix) : curcc->suffix),
+ (mflags | isfile), isexact);
+}
+
+/**/
+static void
+maketildelist(void)
+{
+ /* add all the usernames to the named directory table */
+ nameddirtab->filltable(nameddirtab);
+
+ scanhashtable(nameddirtab, 0, (addwhat==-1) ? 0 : ND_USERNAME, 0,
+ addhnmatch, 0);
+}
+
+/* This does the check for compctl -x `n' and `N' patterns. */
+
+/**/
+int
+getcpat(char *str, int cpatindex, char *cpat, int class)
+{
+ char *s, *t, *p;
+ int d = 0;
+
+ if (!str || !*str)
+ return -1;
+
+ cpat = rembslash(cpat);
+
+ if (!cpatindex)
+ cpatindex++, d = 0;
+ else if ((d = (cpatindex < 0)))
+ cpatindex = -cpatindex;
+
+ for (s = d ? str + strlen(str) - 1 : str;
+ d ? (s >= str) : *s;
+ d ? s-- : s++) {
+ for (t = s, p = cpat; *t && *p; p++) {
+ if (class) {
+ if (*p == *s && !--cpatindex)
+ return (int)(s - str + 1);
+ } else if (*t++ != *p)
+ break;
+ }
+ if (!class && !*p && !--cpatindex)
+ return t - str;
+ }
+ return -1;
+}
+
+/* Dump a hash table (without sorting). For each element the addmatch *
+ * function is called and at the beginning the addwhat variable is set. *
+ * This could be done using scanhashtable(), but this is easy and much *
+ * more efficient. */
+
+/**/
+static void
+dumphashtable(HashTable ht, int what)
+{
+ HashNode hn;
+ int i;
+
+ addwhat = what;
+
+ for (i = 0; i < ht->hsize; i++)
+ for (hn = ht->nodes[i]; hn; hn = hn->next)
+ addmatch(hn->nam, (char *) hn);
+}
+
+/* ScanFunc used by maketildelist() et al. */
+
+/**/
+static void
+addhnmatch(HashNode hn, int flags)
+{
+ addmatch(hn->nam, NULL);
+}
+
+/* Perform expansion on the given string and return the result. *
+ * During this errors are not reported. */
+
+/**/
+static char *
+getreal(char *str)
+{
+ LinkList l = newlinklist();
+ int ne = noerrs;
+
+ noerrs = 1;
+ addlinknode(l, dupstring(str));
+ prefork(l, 0);
+ noerrs = ne;
+ if (!errflag && nonempty(l) &&
+ ((char *) peekfirst(l)) && ((char *) peekfirst(l))[0])
+ return dupstring(peekfirst(l));
+ errflag = 0;
+
+ return dupstring(str);
+}
+
+/* This reads a directory and adds the files to the list of *
+ * matches. The parameters say which files should be added. */
+
+/**/
+static void
+gen_matches_files(int dirs, int execs, int all)
+{
+ DIR *d;
+ struct stat buf;
+ char *n, p[PATH_MAX], *q = NULL, *e;
+ LinkList l = NULL;
+ int ns = 0, ng = opts[NULLGLOB], test, aw = addwhat;
+
+ opts[NULLGLOB] = 1;
+
+ if (*psuf) {
+ /* If there is a path suffix, check if it doesn't have a `*' or *
+ * `)' at the end (this is used to determine if we should use *
+ * globbing). */
+ q = psuf + strlen(psuf) - 1;
+ ns = !(*q == Star || *q == Outpar);
+ l = newlinklist();
+ /* And generate only directory names. */
+ dirs = 1;
+ all = execs = 0;
+ }
+ /* Open directory. */
+ if ((d = opendir((prpre && *prpre) ? prpre : "."))) {
+ /* If we search only special files, prepare a path buffer for stat. */
+ if (!all && prpre) {
+ strcpy(p, prpre);
+ q = p + strlen(prpre);
+ }
+ /* Fine, now read the directory. */
+ while ((n = zreaddir(d, 1)) && !errflag) {
+ /* Ignore files beginning with `.' unless the thing we found on *
+ * the command line also starts with a dot or GLOBDOTS is set. */
+ if (*n != '.' || *fpre == '.' || isset(GLOBDOTS)) {
+ addwhat = execs ? -8 : -5;
+ if (filecomp)
+ /* If we have a pattern for the filename check, use it. */
+ test = pattry(filecomp, n);
+ else {
+ /* Otherwise use the prefix and suffix strings directly. */
+ e = n + strlen(n) - fsl;
+ if ((test = !strncmp(n, fpre, fpl)))
+ test = !strcmp(e, fsuf);
+ if (!test && mstack) {
+ test = 1;
+ addwhat = CC_FILES;
+ }
+ }
+ /* Filename didn't match? */
+ if (!test)
+ continue;
+ if (!all) {
+ /* We still have to check the file type, so prepare *
+ * the path buffer by appending the filename. */
+ strcpy(q, n);
+ /* And do the stat. */
+ if (stat(p, &buf) < 0)
+ continue;
+ }
+ if (all ||
+ (dirs && S_ISDIR(buf.st_mode)) ||
+ (execs && S_ISREG(buf.st_mode) && (buf.st_mode&S_IXUGO))) {
+ /* If we want all files or the file has the right type... */
+ if (*psuf) {
+ /* We have to test for a path suffix. */
+ int o = strlen(p), tt;
+
+ /* Append it to the path buffer. */
+ strcpy(p + o, psuf);
+
+ /* Do we have to use globbing? */
+ if (ispattern ||
+ (ns && comppatmatch && *comppatmatch)) {
+ /* Yes, so append a `*' if needed. */
+ if (ns && comppatmatch && *comppatmatch == '*') {
+ int tl = strlen(p);
+
+ p[tl] = Star;
+ p[tl + 1] = '\0';
+ }
+ /* Do the globbing... */
+ remnulargs(p);
+ addlinknode(l, p);
+ globlist(l, 0);
+ /* And see if that produced a filename. */
+ tt = nonempty(l);
+ while (ugetnode(l));
+ } else
+ /* Otherwise just check, if we have access *
+ * to the file. */
+ tt = !access(p, F_OK);
+
+ p[o] = '\0';
+ if (tt)
+ /* Ok, we can add the filename to the *
+ * list of matches. */
+ addmatch(dupstring(n), NULL);
+ } else
+ /* We want all files, so just add the name *
+ * to the matches. */
+ addmatch(dupstring(n), NULL);
+ }
+ }
+ }
+ closedir(d);
+ }
+ opts[NULLGLOB] = ng;
+ addwhat = aw;
+}
+
+/* This returns the node with the given data. */
+/* ...should probably be moved to linklist.c. */
+
+static LinkNode
+findnode(LinkList list, void *dat)
+{
+ LinkNode tmp = list->first;
+
+ while (tmp && tmp->dat != dat) tmp = tmp->next;
+
+ return tmp;
+}
+
+/* A simple counter to avoid endless recursion between old and new style *
+ * completion. */
+
+static int cdepth = 0;
+
+#define MAX_CDEPTH 16
+
+/**/
+static int
+makecomplistctl(int flags)
+{
+ int ret;
+
+ if (cdepth == MAX_CDEPTH)
+ return 0;
+
+ cdepth++;
+ SWITCHHEAPS(compheap) {
+ int ooffs = offs, lip, lp;
+ char *str = comp_str(&lip, &lp, 0), *t;
+ char *os = cmdstr, **ow = clwords, **p, **q, qc;
+ int on = clwnum, op = clwpos, ois = instring, oib = inbackt;
+ char *oisuf = isuf, *oqp = qipre, *oqs = qisuf, *oaq = autoq;
+ char buf[2];
+
+ if (compquote && (qc = *compquote)) {
+ if (qc == '`') {
+ instring = 0;
+ inbackt = 0;
+ autoq = "";
+ } else {
+ buf[0] = qc;
+ buf[1] = '\0';
+ instring = (qc == '\'' ? 1 : 2);
+ inbackt = 0;
+ autoq = buf;
+ }
+ } else {
+ instring = inbackt = 0;
+ autoq = "";
+ }
+ qipre = ztrdup(compqiprefix ? compqiprefix : "");
+ qisuf = ztrdup(compqisuffix ? compqisuffix : "");
+ isuf = dupstring(compisuffix);
+ ctokenize(isuf);
+ remnulargs(isuf);
+ clwnum = arrlen(compwords);
+ clwpos = compcurrent - 1;
+ cmdstr = ztrdup(compwords[0]);
+ clwords = (char **) zalloc((clwnum + 1) * sizeof(char *));
+ for (p = compwords, q = clwords; *p; p++, q++) {
+ t = dupstring(*p);
+ tokenize(t);
+ remnulargs(t);
+ *q = ztrdup(t);
+ }
+ *q = NULL;
+ offs = lip + lp;
+ incompfunc = 2;
+ ret = makecomplistglobal(str, !clwpos, COMP_COMPLETE, flags);
+ incompfunc = 1;
+ isuf = oisuf;
+ zsfree(qipre);
+ zsfree(qisuf);
+ qipre = oqp;
+ qisuf = oqs;
+ instring = ois;
+ inbackt = oib;
+ autoq = oaq;
+ offs = ooffs;
+ zsfree(cmdstr);
+ freearray(clwords);
+ cmdstr = os;
+ clwords = ow;
+ clwnum = on;
+ clwpos = op;
+ } SWITCHBACKHEAPS;
+ cdepth--;
+
+ return ret;
+}
+
+/* This function gets the compctls for the given command line and *
+ * adds all completions for them. */
+
+/**/
+static int
+makecomplistglobal(char *os, int incmd, int lst, int flags)
+{
+ Compctl cc = NULL;
+ char *s;
+
+ ccont = CC_CCCONT;
+ cc_dummy.suffix = NULL;
+
+ if (linwhat == IN_ENV) {
+ /* Default completion for parameter values. */
+ if (!(flags & CFN_DEFAULT)) {
+ cc = &cc_default;
+ keypm = NULL;
+ }
+ } else if (linwhat == IN_MATH) {
+ if (!(flags & CFN_DEFAULT)) {
+ if (insubscr >= 2) {
+ /* Inside subscript of assoc array, complete keys. */
+ cc_dummy.mask = 0;
+ cc_dummy.suffix = (insubscr == 2 ? "]" : "");
+ } else {
+ /* Other math environment, complete paramete names. */
+ keypm = NULL;
+ cc_dummy.mask = CC_PARAMS;
+ }
+ cc = &cc_dummy;
+ cc_dummy.refc = 10000;
+ }
+ } else if (linwhat == IN_COND) {
+ /* We try to be clever here: in conditions we complete option *
+ * names after a `-o', file names after `-nt', `-ot', and `-ef' *
+ * and file names and parameter names elsewhere. */
+ if (!(flags & CFN_DEFAULT)) {
+ s = clwpos ? clwords[clwpos - 1] : "";
+ cc_dummy.mask = !strcmp("-o", s) ? CC_OPTIONS :
+ ((*s == '-' && s[1] && !s[2]) ||
+ !strcmp("-nt", s) ||
+ !strcmp("-ot", s) ||
+ !strcmp("-ef", s)) ? CC_FILES :
+ (CC_FILES | CC_PARAMS);
+ cc = &cc_dummy;
+ cc_dummy.refc = 10000;
+ keypm = NULL;
+ }
+ } else if (linredir) {
+ if (!(flags & CFN_DEFAULT)) {
+ /* In redirections use default completion. */
+ cc = &cc_default;
+ keypm = NULL;
+ }
+ } else {
+ /* Otherwise get the matches for the command. */
+ keypm = NULL;
+ return makecomplistcmd(os, incmd, flags);
+ }
+ if (cc) {
+ /* First, use the -T compctl. */
+ if (!(flags & CFN_FIRST)) {
+ makecomplistcc(&cc_first, os, incmd);
+
+ if (!(ccont & CC_CCCONT))
+ return 0;
+ }
+ makecomplistcc(cc, os, incmd);
+ return 1;
+ }
+ return 0;
+}
+
+/* This produces the matches for a command. */
+
+/**/
+static int
+makecomplistcmd(char *os, int incmd, int flags)
+{
+ Compctl cc;
+ Compctlp ccp;
+ char *s;
+ int ret = 0;
+
+ /* First, use the -T compctl. */
+ if (!(flags & CFN_FIRST)) {
+ makecomplistcc(&cc_first, os, incmd);
+
+ if (!(ccont & CC_CCCONT))
+ return 0;
+ }
+ /* Then search the pattern compctls, with the command name and the *
+ * full pathname of the command. */
+ if (cmdstr) {
+ ret |= makecomplistpc(os, incmd);
+ if (!(ccont & CC_CCCONT))
+ return ret;
+ }
+ /* If the command string starts with `=', try the path name of the *
+ * command. */
+ if (cmdstr && cmdstr[0] == Equals) {
+ char *c = findcmd(cmdstr + 1, 1);
+
+ if (c) {
+ zsfree(cmdstr);
+ cmdstr = ztrdup(c);
+ }
+ }
+
+ /* Find the compctl for this command, trying the full name and then *
+ * the trailing pathname component. If that doesn't yield anything, *
+ * use default completion. */
+ if (incmd)
+ cc = &cc_compos;
+ else if (!(cmdstr &&
+ (((ccp = (Compctlp) compctltab->getnode(compctltab, cmdstr)) &&
+ (cc = ccp->cc)) ||
+ ((s = dupstring(cmdstr)) && remlpaths(&s) &&
+ (ccp = (Compctlp) compctltab->getnode(compctltab, s)) &&
+ (cc = ccp->cc))))) {
+ if (flags & CFN_DEFAULT)
+ return ret;
+ cc = &cc_default;
+ } else
+ ret|= 1;
+ makecomplistcc(cc, os, incmd);
+ return ret;
+}
+
+/* This add the matches for the pattern compctls. */
+
+/**/
+static int
+makecomplistpc(char *os, int incmd)
+{
+ Patcomp pc;
+ Patprog pat;
+ char *s = findcmd(cmdstr, 1);
+ int ret = 0;
+
+ for (pc = patcomps; pc; pc = pc->next) {
+ if ((pat = patcompile(pc->pat, PAT_STATIC, NULL)) &&
+ (pattry(pat, cmdstr) ||
+ (s && pattry(pat, s)))) {
+ makecomplistcc(pc->cc, os, incmd);
+ ret |= 2;
+ if (!(ccont & CC_CCCONT))
+ return ret;
+ }
+ }
+ return ret;
+}
+
+/* This produces the matches for one compctl. */
+
+/**/
+static void
+makecomplistcc(Compctl cc, char *s, int incmd)
+{
+ cc->refc++;
+ if (!ccused)
+ ccused = newlinklist();
+ addlinknode(ccused, cc);
+
+ ccont = 0;
+
+ makecomplistor(cc, s, incmd, 0, 0);
+}
+
+/* This adds the completions for one run of [x]or'ed completions. */
+
+/**/
+static void
+makecomplistor(Compctl cc, char *s, int incmd, int compadd, int sub)
+{
+ int mn, ct, um = usemenu;
+
+ /* Loop over xors. */
+ do {
+ mn = mnum;
+
+ /* Loop over ors. */
+ do {
+ /* Reset the range information if we are not in a sub-list. */
+ if (!sub) {
+ brange = 0;
+ erange = clwnum - 1;
+ }
+ usemenu = 0;
+ makecomplistlist(cc, s, incmd, compadd);
+ um |= usemenu;
+
+ ct = cc->mask2 & CC_XORCONT;
+
+ cc = cc->xor;
+ } while (cc && ct);
+
+ /* Stop if we got some matches. */
+ if (mn != mnum)
+ break;
+ if (cc) {
+ ccont &= ~(CC_DEFCONT | CC_PATCONT);
+ if (!sub)
+ ccont &= ~CC_CCCONT;
+ }
+ } while (cc);
+
+ usemenu = um;
+}
+
+/* This dispatches for simple and extended completion. */
+
+/**/
+static void
+makecomplistlist(Compctl cc, char *s, int incmd, int compadd)
+{
+ int oloffs = offs, owe = we, owb = wb, ocs = cs;
+
+ if (cc->ext)
+ /* Handle extended completion. */
+ makecomplistext(cc, s, incmd);
+ else
+ /* Only normal flags. */
+ makecomplistflags(cc, s, incmd, compadd);
+
+ /* Reset some information variables for the next try. */
+ errflag = 0;
+ offs = oloffs;
+ wb = owb;
+ we = owe;
+ cs = ocs;
+}
+
+/* This add matches for extended completion patterns */
+
+/**/
+static void
+makecomplistext(Compctl occ, char *os, int incmd)
+{
+ Compctl compc;
+ Compcond or, cc;
+ Patprog pprog;
+ int compadd, m = 0, d = 0, t, tt, i, j, a, b, ins;
+ char *sc = NULL, *s, *ss;
+
+ ins = (instring ? instring : (inbackt ? 3 : 0));
+
+ /* This loops over the patterns separated by `-'s. */
+ for (compc = occ->ext; compc; compc = compc->next) {
+ compadd = t = brange = 0;
+ erange = clwnum - 1;
+ /* This loops over OR'ed patterns. */
+ for (cc = compc->cond; cc && !t; cc = or) {
+ or = cc->or;
+ /* This loops over AND'ed patterns. */
+ for (t = 1; cc && t; cc = cc->and) {
+ /* And this loops over [...] pairs. */
+ for (t = i = 0; i < cc->n && !t; i++) {
+ s = NULL;
+ brange = 0;
+ erange = clwnum - 1;
+ switch (cc->type) {
+ case CCT_QUOTE:
+ t = ((cc->u.s.s[i][0] == 's' && ins == 1) ||
+ (cc->u.s.s[i][0] == 'd' && ins == 2) ||
+ (cc->u.s.s[i][0] == 'b' && ins == 3));
+ break;
+ case CCT_POS:
+ tt = clwpos;
+ goto cct_num;
+ case CCT_NUMWORDS:
+ tt = clwnum;
+ cct_num:
+ if ((a = cc->u.r.a[i]) < 0)
+ a += clwnum;
+ if ((b = cc->u.r.b[i]) < 0)
+ b += clwnum;
+ if (cc->type == CCT_POS)
+ brange = a, erange = b;
+ t = (tt >= a && tt <= b);
+ break;
+ case CCT_CURSUF:
+ case CCT_CURPRE:
+ s = ztrdup(clwpos < clwnum ? os : "");
+ untokenize(s);
+ if (isset(COMPLETEINWORD)) s[offs] = '\0';
+ sc = rembslash(cc->u.s.s[i]);
+ a = strlen(sc);
+ if (!strncmp(s, sc, a)) {
+ compadd = (cc->type == CCT_CURSUF ? a : 0);
+ t = 1;
+ }
+ break;
+ case CCT_CURSUB:
+ case CCT_CURSUBC:
+ if (clwpos < 0 || clwpos >= clwnum)
+ t = 0;
+ else {
+ s = ztrdup(os);
+ untokenize(s);
+ if (isset(COMPLETEINWORD)) s[offs] = '\0';
+ a = getcpat(s,
+ cc->u.s.p[i],
+ cc->u.s.s[i],
+ cc->type == CCT_CURSUBC);
+ if (a != -1)
+ compadd = a, t = 1;
+ }
+ break;
+
+ case CCT_CURPAT:
+ case CCT_CURSTR:
+ tt = clwpos;
+ goto cct_str;
+ case CCT_WORDPAT:
+ case CCT_WORDSTR:
+ tt = 0;
+ cct_str:
+ if ((a = tt + cc->u.s.p[i]) < 0)
+ a += clwnum;
+ s = ztrdup((a < 0 || a >= clwnum) ? "" :
+ clwords[a]);
+ untokenize(s);
+
+ if (cc->type == CCT_CURPAT ||
+ cc->type == CCT_WORDPAT) {
+ tokenize(ss = dupstring(cc->u.s.s[i]));
+ t = ((pprog = patcompile(ss, PAT_STATIC, NULL)) &&
+ pattry(pprog, s));
+ } else
+ t = (!strcmp(s, rembslash(cc->u.s.s[i])));
+ break;
+ case CCT_RANGESTR:
+ case CCT_RANGEPAT:
+ if (cc->type == CCT_RANGEPAT)
+ tokenize(sc = dupstring(cc->u.l.a[i]));
+ for (j = clwpos - 1; j > 0; j--) {
+ untokenize(s = ztrdup(clwords[j]));
+ if (cc->type == CCT_RANGESTR)
+ sc = rembslash(cc->u.l.a[i]);
+ if (cc->type == CCT_RANGESTR ?
+ !strncmp(s, sc, strlen(sc)) :
+ ((pprog = patcompile(sc, PAT_STATIC, 0)) &&
+ pattry(pprog, s))) {
+ zsfree(s);
+ brange = j + 1;
+ t = 1;
+ break;
+ }
+ zsfree(s);
+ }
+ if (t && cc->u.l.b[i]) {
+ if (cc->type == CCT_RANGEPAT)
+ tokenize(sc = dupstring(cc->u.l.b[i]));
+ for (j++; j < clwnum; j++) {
+ untokenize(s = ztrdup(clwords[j]));
+ if (cc->type == CCT_RANGESTR)
+ sc = rembslash(cc->u.l.b[i]);
+ if (cc->type == CCT_RANGESTR ?
+ !strncmp(s, sc, strlen(sc)) :
+ ((pprog = patcompile(sc, PAT_STATIC, 0)) &&
+ pattry(pprog, s))) {
+ zsfree(s);
+ erange = j - 1;
+ t = clwpos <= erange;
+ break;
+ }
+ zsfree(s);
+ }
+ }
+ s = NULL;
+ }
+ zsfree(s);
+ }
+ }
+ }
+ if (t) {
+ /* The patterns matched, use the flags. */
+ m = 1;
+ ccont &= ~(CC_PATCONT | CC_DEFCONT);
+ makecomplistor(compc, os, incmd, compadd, 1);
+ if (!d && (ccont & CC_DEFCONT)) {
+ d = 1;
+ compadd = 0;
+ brange = 0;
+ erange = clwnum - 1;
+ makecomplistflags(occ, os, incmd, 0);
+ }
+ if (!(ccont & CC_PATCONT))
+ break;
+ }
+ }
+ /* If no pattern matched, use the standard flags. */
+ if (!m) {
+ compadd = 0;
+ brange = 0;
+ erange = clwnum - 1;
+ makecomplistflags(occ, os, incmd, 0);
+ }
+}
+
+/**/
+static int
+sep_comp_string(char *ss, char *s, int noffs)
+{
+ LinkList foo = newlinklist();
+ LinkNode n;
+ int owe = we, owb = wb, ocs = cs, swb, swe, scs, soffs, ne = noerrs;
+ int sl = strlen(ss), tl, got = 0, i = 0, cur = -1, oll = ll, remq;
+ int ois = instring, oib = inbackt;
+ char *tmp, *p, *ns, *ol = (char *) line, sav, *oaq = autoq, *qp, *qs;
+ char *ts, qc = '\0';
+
+ swb = swe = soffs = 0;
+ ns = NULL;
+
+ /* Put the string in the lexer buffer and call the lexer to *
+ * get the words we have to expand. */
+ zleparse = 1;
+ addedx = 1;
+ noerrs = 1;
+ lexsave();
+ tmp = (char *) zhalloc(tl = sl + 3 + strlen(s));
+ strcpy(tmp, ss);
+ tmp[sl] = ' ';
+ memcpy(tmp + sl + 1, s, noffs);
+ tmp[(scs = cs = sl + 1 + noffs)] = 'x';
+ strcpy(tmp + sl + 2 + noffs, s + noffs);
+ if ((remq = (*compqstack == '\\')))
+ tmp = rembslash(tmp);
+ inpush(dupstrspace(tmp), 0, NULL);
+ line = (unsigned char *) tmp;
+ ll = tl - 1;
+ strinbeg(0);
+ noaliases = 1;
+ do {
+ ctxtlex();
+ if (tok == LEXERR) {
+ int j;
+
+ if (!tokstr)
+ break;
+ for (j = 0, p = tokstr; *p; p++)
+ if (*p == Snull || *p == Dnull)
+ j++;
+ if (j & 1) {
+ tok = STRING;
+ if (p > tokstr && p[-1] == ' ')
+ p[-1] = '\0';
+ }
+ }
+ if (tok == ENDINPUT || tok == LEXERR)
+ break;
+ if (tokstr && *tokstr)
+ addlinknode(foo, (p = ztrdup(tokstr)));
+ else
+ p = NULL;
+ if (!got && !zleparse) {
+ DPUTS(!p, "no current word in substr");
+ got = 1;
+ cur = i;
+ swb = wb - 1;
+ swe = we - 1;
+ soffs = cs - swb;
+ chuck(p + soffs);
+ ns = dupstring(p);
+ }
+ i++;
+ } while (tok != ENDINPUT && tok != LEXERR);
+ noaliases = 0;
+ strinend();
+ inpop();
+ errflag = zleparse = 0;
+ noerrs = ne;
+ lexrestore();
+ wb = owb;
+ we = owe;
+ cs = ocs;
+ line = (unsigned char *) ol;
+ ll = oll;
+ if (cur < 0 || i < 1)
+ return 1;
+ owb = offs;
+ offs = soffs;
+ if ((p = check_param(ns, 0, 1))) {
+ for (p = ns; *p; p++)
+ if (*p == Dnull)
+ *p = '"';
+ else if (*p == Snull)
+ *p = '\'';
+ }
+ offs = owb;
+
+ 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 = compqstack[1] ? "" : multiquote(*ns == Snull ? "'" : "\"", 1);
+ qc = (*ns == Snull ? '\'' : '"');
+ ts++;
+ } else {
+ instring = 0;
+ autoq = "";
+ }
+ for (p = ns, i = swb; *p; p++, i++) {
+ if (INULL(*p)) {
+ if (i < scs) {
+ soffs--;
+ if (remq && *p == Bnull && p[1])
+ swb -= 2;
+ }
+ if (p[1] || *p != Bnull) {
+ if (*p == Bnull) {
+ if (scs == i + 1)
+ scs++, soffs++;
+ } else {
+ if (scs > i--)
+ scs--;
+ }
+ } else {
+ if (scs == swe)
+ scs--;
+ }
+ chuck(p--);
+ }
+ }
+ 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 - sl - 1)];
+ s[i] = '\0';
+ qp = tricat(qipre, multiquote(s, 0), "");
+ s[i] = sav;
+ if (swe < swb)
+ swe = swb;
+ swe -= sl + 1;
+ sl = strlen(s);
+ if (swe > sl) {
+ swe = sl;
+ if (strlen(ns) > swe - swb + 1)
+ ns[swe - swb + 1] = '\0';
+ }
+ qs = tricat(multiquote(s + swe, 0), qisuf, "");
+ sl = strlen(ns);
+ if (soffs > sl)
+ soffs = sl;
+
+ {
+ char **ow = clwords, *os = cmdstr, *oqp = qipre, *oqs = qisuf;
+ char *oqst = compqstack;
+ int olws = clwsize, olwn = clwnum, olwp = clwpos;
+ int obr = brange, oer = erange, oof = offs;
+ unsigned long occ = ccont;
+
+ compqstack = tricat((instring ? (instring == 1 ? "'" : "\"") : "\\"),
+ compqstack, "");
+
+ clwsize = clwnum = countlinknodes(foo);
+ clwords = (char **) zalloc((clwnum + 1) * sizeof(char *));
+ for (n = firstnode(foo), i = 0; n; incnode(n), i++) {
+ p = clwords[i] = (char *) getdata(n);
+ untokenize(p);
+ }
+ clwords[i] = NULL;
+ clwpos = cur;
+ cmdstr = ztrdup(clwords[0]);
+ brange = 0;
+ erange = clwnum - 1;
+ qipre = qp;
+ qisuf = qs;
+ offs = soffs;
+ ccont = CC_CCCONT;
+ makecomplistcmd(ns, !clwpos, CFN_FIRST);
+ ccont = occ;
+ offs = oof;
+ zsfree(cmdstr);
+ cmdstr = os;
+ freearray(clwords);
+ clwords = ow;
+ clwsize = olws;
+ clwnum = olwn;
+ clwpos = olwp;
+ brange = obr;
+ erange = oer;
+ zsfree(qipre);
+ qipre = oqp;
+ zsfree(qisuf);
+ qisuf = oqs;
+ zsfree(compqstack);
+ compqstack = oqst;
+ }
+ autoq = oaq;
+ instring = ois;
+ inbackt = oib;
+
+ return 0;
+}
+
+/* This adds the completions for the flags in the given compctl. */
+
+/**/
+static void
+makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
+{
+ int t, sf1, sf2, ooffs, um = usemenu, delit, oaw, gflags;
+ int mn = mnum, ohp = haspattern;
+ char *p, *sd = NULL, *tt, *s1, *s2, *os = dupstring(s);
+ struct cmlist ms;
+
+ ccont |= (cc->mask2 & (CC_CCCONT | CC_DEFCONT | CC_PATCONT));
+
+ if (incompfunc != 1 && ccstack && findnode(ccstack, cc))
+ return;
+
+ if (!ccstack)
+ ccstack = newlinklist();
+ addlinknode(ccstack, cc);
+
+ if (incompfunc != 1 && allccs) {
+ if (findnode(allccs, cc)) {
+ uremnode(ccstack, firstnode(ccstack));
+ return;
+ }
+ addlinknode(allccs, cc);
+ }
+ /* Go to the end of the word if complete_in_word is not set. */
+ if (unset(COMPLETEINWORD) && cs != we)
+ cs = we, offs = strlen(s);
+
+ s = dupstring(s);
+ delit = ispattern = 0;
+ usemenu = um;
+ patcomp = filecomp = NULL;
+ rpre = rsuf = lpre = lsuf = ppre = psuf = lppre = lpsuf =
+ fpre = fsuf = ipre = ripre = prpre =
+ qfpre = qfsuf = qrpre = qrsuf = qlpre = qlsuf = NULL;
+
+ curcc = cc;
+
+ mflags = 0;
+ gflags = (((cc->mask2 & CC_NOSORT ) ? CGF_NOSORT : 0) |
+ ((cc->mask2 & CC_UNIQALL) ? CGF_UNIQALL : 0) |
+ ((cc->mask2 & CC_UNIQCON) ? CGF_UNIQCON : 0));
+ if (cc->gname) {
+ endcmgroup(NULL);
+ begcmgroup(cc->gname, gflags);
+ }
+ if (cc->ylist) {
+ endcmgroup(NULL);
+ begcmgroup(NULL, gflags);
+ }
+ if (cc->mask & CC_REMOVE)
+ mflags |= CMF_REMOVE;
+ if (cc->explain) {
+ curexpl = (Cexpl) zhalloc(sizeof(struct cexpl));
+ curexpl->count = curexpl->fcount = 0;
+ } else
+ curexpl = NULL;
+ /* compadd is the number of characters we have to ignore at the *
+ * beginning of the word. */
+ if (compadd) {
+ ipre = dupstring(s);
+ ipre[compadd] = '\0';
+ untokenize(ipre);
+ wb += compadd;
+ s += compadd;
+ if ((offs -= compadd) < 0) {
+ /* It's bigger than our word prefix, so we can't help here... */
+ uremnode(ccstack, firstnode(ccstack));
+ return;
+ }
+ } else
+ ipre = NULL;
+
+ if (cc->matcher) {
+ ms.next = mstack;
+ ms.matcher = cc->matcher;
+ mstack = &ms;
+
+ if (!mnum)
+ add_bmatchers(cc->matcher);
+
+ addlinknode(matchers, cc->matcher);
+ cc->matcher->refc++;
+ }
+ if (mnum && (mstack || bmatchers))
+ update_bmatchers();
+
+ /* Insert the prefix (compctl -P), if any. */
+ if (cc->prefix) {
+ int pl = 0;
+
+ if (*s) {
+ char *dp = rembslash(cc->prefix);
+ /* First find out how much of the prefix is already on the line. */
+ sd = dupstring(s);
+ untokenize(sd);
+ pl = pfxlen(dp, sd);
+ s += pl;
+ sd += pl;
+ offs -= pl;
+ }
+ }
+ /* Does this compctl have a suffix (compctl -S)? */
+ if (cc->suffix) {
+ char *sdup = rembslash(cc->suffix);
+ int sl = strlen(sdup), suffixll;
+
+ /* Ignore trailing spaces. */
+ for (p = sdup + sl - 1; p >= sdup && *p == ' '; p--, sl--);
+ p[1] = '\0';
+
+ if (!sd) {
+ sd = dupstring(s);
+ untokenize(sd);
+ }
+ /* If the suffix is already there, ignore it (and don't add *
+ * it again). */
+ if (*sd && (suffixll = strlen(sd)) >= sl &&
+ offs <= suffixll - sl && !strcmp(sdup, sd + suffixll - sl))
+ s[suffixll - sl] = '\0';
+ }
+ /* Do we have one of the special characters `~' and `=' at the beginning? */
+ if (incompfunc || ((ic = *s) != Tilde && ic != Equals))
+ ic = 0;
+
+ /* Check if we have to complete a parameter name... */
+ if (!incompfunc && (p = check_param(s, 1, 0))) {
+ s = p;
+ /* And now make sure that we complete parameter names. */
+ cc = &cc_dummy;
+ cc_dummy.refc = 10000;
+ cc_dummy.mask = CC_PARAMS | CC_ENVVARS;
+ }
+ ooffs = offs;
+ /* If we have to ignore the word, do that. */
+ if (cc->mask & CC_DELETE) {
+ delit = 1;
+ *s = '\0';
+ offs = 0;
+ if (isset(AUTOMENU))
+ usemenu = 1;
+ }
+
+ /* Compute line prefix/suffix. */
+ lpl = offs;
+ lpre = zhalloc(lpl + 1);
+ memcpy(lpre, s, lpl);
+ lpre[lpl] = '\0';
+ qlpre = quotename(lpre, NULL);
+ lsuf = dupstring(s + offs);
+ lsl = strlen(lsuf);
+ qlsuf = quotename(lsuf, NULL);
+
+ /* First check for ~.../... */
+ if (ic == Tilde) {
+ for (p = lpre + lpl; p > lpre; p--)
+ if (*p == '/')
+ break;
+
+ if (*p == '/')
+ ic = 0;
+ }
+ /* Compute real prefix/suffix. */
+
+ noreal = !delit;
+ for (p = lpre; *p && *p != String && *p != Tick; p++);
+ tt = ic && !ispar ? lpre + 1 : lpre;
+ rpre = (*p || *lpre == Tilde || *lpre == Equals) ?
+ (noreal = 0, getreal(tt)) :
+ dupstring(tt);
+ qrpre = quotename(rpre, NULL);
+
+ for (p = lsuf; *p && *p != String && *p != Tick; p++);
+ rsuf = *p ? (noreal = 0, getreal(lsuf)) : dupstring(lsuf);
+ qrsuf = quotename(rsuf, NULL);
+
+ /* Check if word is a pattern. */
+
+ for (s1 = NULL, sf1 = 0, p = rpre + (rpl = strlen(rpre)) - 1;
+ p >= rpre && (ispattern != 3 || !sf1);
+ p--)
+ if (itok(*p) && (p > rpre || (*p != Equals && *p != Tilde)))
+ ispattern |= sf1 ? 1 : 2;
+ else if (*p == '/') {
+ sf1++;
+ if (!s1)
+ s1 = p;
+ }
+ rsl = strlen(rsuf);
+ for (s2 = NULL, sf2 = t = 0, p = rsuf; *p && (!t || !sf2); p++)
+ if (itok(*p))
+ t |= sf2 ? 4 : 2;
+ else if (*p == '/') {
+ sf2++;
+ if (!s2)
+ s2 = p;
+ }
+ ispattern = ispattern | t;
+
+ /* But if we were asked not to do glob completion, we never treat the *
+ * thing as a pattern. */
+ if (!comppatmatch || !*comppatmatch)
+ ispattern = 0;
+
+ if (ispattern) {
+ /* The word should be treated as a pattern, so compute the matcher. */
+ p = (char *) zhalloc(rpl + rsl + 2);
+ strcpy(p, rpre);
+ if (rpl && p[rpl - 1] != Star &&
+ (!comppatmatch || *comppatmatch == '*')) {
+ p[rpl] = Star;
+ strcpy(p + rpl + 1, rsuf);
+ } else
+ strcpy(p + rpl, rsuf);
+ patcomp = patcompile(p, 0, NULL);
+ haspattern = 1;
+ }
+ if (!patcomp) {
+ untokenize(rpre);
+ untokenize(rsuf);
+
+ rpl = strlen(rpre);
+ rsl = strlen(rsuf);
+ }
+ untokenize(lpre);
+ untokenize(lsuf);
+
+ if (!(cc->mask & CC_DELETE))
+ hasmatched = 1;
+
+ /* Handle completion of files specially (of course). */
+
+ if ((cc->mask & (CC_FILES | CC_DIRS | CC_COMMPATH)) || cc->glob) {
+ /* s1 and s2 point to the last/first slash in the prefix/suffix. */
+ if (!s1)
+ s1 = rpre;
+ if (!s2)
+ s2 = rsuf + rsl;
+
+ /* Compute the path prefix/suffix. */
+ if (*s1 != '/')
+ ppre = "";
+ else
+ ppre = dupstrpfx(rpre, s1 - rpre + 1);
+ psuf = dupstring(s2);
+
+ if (cs != wb) {
+ char save = line[cs];
+
+ line[cs] = 0;
+ lppre = dupstring((char *) line + wb +
+ (qipre && *qipre ?
+ (strlen(qipre) -
+ (*qipre == '\'' || *qipre == '\"')) : 0));
+ line[cs] = save;
+ if (brbeg) {
+ Brinfo bp;
+
+ for (bp = brbeg; bp; bp = bp->next)
+ strcpy(lppre + bp->qpos,
+ lppre + bp->qpos + strlen(bp->str));
+ }
+ if ((p = strrchr(lppre, '/'))) {
+ p[1] = '\0';
+ lppl = strlen(lppre);
+ } else if (!sf1) {
+ lppre = NULL;
+ lppl = 0;
+ } else {
+ lppre = ppre;
+ lppl = strlen(lppre);
+ }
+ } else {
+ lppre = NULL;
+ lppl = 0;
+ }
+ if (cs != we) {
+ int end = we;
+ char save = line[end];
+
+ if (qisuf && *qisuf) {
+ int ql = strlen(qisuf);
+
+ end -= ql - (qisuf[ql-1] == '\'' || qisuf[ql-1] == '"');
+ }
+ line[end] = 0;
+ lpsuf = dupstring((char *) (line + cs));
+ line[end] = save;
+ if (brend) {
+ Brinfo bp;
+ char *p;
+ int bl;
+
+ for (bp = brend; bp; bp = bp->next) {
+ p = lpsuf + (we - cs) - bp->qpos - (bl = strlen(bp->str));
+ strcpy(p, p + bl);
+ }
+ }
+ if (!(lpsuf = strchr(lpsuf, '/')) && sf2)
+ lpsuf = psuf;
+ lpsl = (lpsuf ? strlen(lpsuf) : 0);
+ } else {
+ lpsuf = NULL;
+ lpsl = 0;
+ }
+
+ /* And get the file prefix. */
+ fpre = dupstring(((s1 == s || s1 == rpre || ic) &&
+ (*s != '/' || cs == wb)) ? s1 : s1 + 1);
+ qfpre = quotename(fpre, NULL);
+ /* And the suffix. */
+ fsuf = dupstrpfx(rsuf, s2 - rsuf);
+ qfsuf = quotename(fsuf, NULL);
+
+ if (comppatmatch && *comppatmatch && (ispattern & 2)) {
+ int t2;
+
+ /* We have to use globbing, so compute the pattern from *
+ * the file prefix and suffix with a `*' between them. */
+ p = (char *) zhalloc((t2 = strlen(fpre)) + strlen(fsuf) + 2);
+ strcpy(p, fpre);
+ if ((!t2 || p[t2 - 1] != Star) && *fsuf != Star &&
+ (!comppatmatch || *comppatmatch == '*'))
+ p[t2++] = Star;
+ strcpy(p + t2, fsuf);
+ filecomp = patcompile(p, 0, NULL);
+ }
+ if (!filecomp) {
+ untokenize(fpre);
+ untokenize(fsuf);
+
+ fpl = strlen(fpre);
+ fsl = strlen(fsuf);
+ }
+ addwhat = -1;
+
+ /* Completion after `~', maketildelist adds the usernames *
+ * and named directories. */
+ if (ic == Tilde) {
+ char *oi = ipre;
+
+ ipre = (ipre ? dyncat("~", ipre) : "~");
+ maketildelist();
+ ipre = oi;
+ } else if (ic == Equals) {
+ /* Completion after `=', get the command names from *
+ * the cmdnamtab and aliases from aliastab. */
+ char *oi = ipre;
+
+ ipre = (ipre ? dyncat("=", ipre) : "=");
+ if (isset(HASHLISTALL))
+ cmdnamtab->filltable(cmdnamtab);
+ dumphashtable(cmdnamtab, -7);
+ dumphashtable(aliastab, -2);
+ ipre = oi;
+ } else {
+ /* Normal file completion... */
+ if (ispattern & 1) {
+ /* But with pattern matching. */
+ LinkList l = newlinklist();
+ LinkNode n;
+ int ng = opts[NULLGLOB];
+
+ opts[NULLGLOB] = 1;
+
+ addwhat = 0;
+ p = (char *) zhalloc(lpl + lsl + 3);
+ strcpy(p, lpre);
+ if (*lsuf != '*' && *lpre && lpre[lpl - 1] != '*')
+ strcat(p, "*");
+ strcat(p, lsuf);
+ if (*lsuf && lsuf[lsl - 1] != '*' && lsuf[lsl - 1] != ')')
+ strcat(p, "*");
+
+ /* Do the globbing. */
+ tokenize(p);
+ remnulargs(p);
+ addlinknode(l, p);
+ globlist(l, 0);
+
+ if (nonempty(l)) {
+ /* And add the resulting words. */
+ mflags |= CMF_FILE;
+ for (n = firstnode(l); n; incnode(n))
+ addmatch(getdata(n), NULL);
+ mflags &= !CMF_FILE;
+ }
+ opts[NULLGLOB] = ng;
+ } else {
+ char **dirs = 0, *ta[2];
+
+ /* No pattern matching. */
+ addwhat = CC_FILES;
+
+ if (cc->withd) {
+ char **pp, **npp, *tp;
+ int tl = strlen(ppre) + 2, pl;
+
+ if ((pp = get_user_var(cc->withd))) {
+ dirs = npp =
+ (char**) zhalloc(sizeof(char *)*(arrlen(pp)+1));
+ while (*pp) {
+ pl = strlen(*pp);
+ tp = (char *) zhalloc(strlen(*pp) + tl);
+ strcpy(tp, *pp);
+ tp[pl] = '/';
+ strcpy(tp + pl + 1, ppre);
+ *npp++ = tp;
+ pp++;
+ }
+ *npp = '\0';
+ }
+ }
+ if (!dirs) {
+ dirs = ta;
+ if (cc->withd) {
+ char *tp;
+ int pl = strlen(cc->withd);
+
+ ta[0] = tp = (char *) zhalloc(strlen(ppre) + pl + 2);
+ strcpy(tp, cc->withd);
+ tp[pl] = '/';
+ strcpy(tp + pl + 1, ppre);
+ } else
+ ta[0] = ppre;
+ ta[1] = NULL;
+ }
+ while (*dirs) {
+ prpre = *dirs;
+
+ if (sf2)
+ /* We are in the path, so add only directories. */
+ gen_matches_files(1, 0, 0);
+ else {
+ if (cc->mask & CC_FILES)
+ /* Add all files. */
+ gen_matches_files(0, 0, 1);
+ else if (cc->mask & CC_COMMPATH) {
+ /* Completion of command paths. */
+ if (sf1 || cc->withd)
+ /* There is a path prefix, so add *
+ * directories and executables. */
+ gen_matches_files(1, 1, 0);
+ else {
+ /* No path prefix, so add the things *
+ * reachable via the PATH variable. */
+ char **pc = path, *pp = prpre;
+
+ for (; *pc; pc++)
+ if (!**pc || (pc[0][0] == '.' && !pc[0][1]))
+ break;
+ if (*pc) {
+ prpre = "./";
+ gen_matches_files(1, 1, 0);
+ prpre = pp;
+ }
+ }
+ } else if (cc->mask & CC_DIRS)
+ gen_matches_files(1, 0, 0);
+ /* The compctl has a glob pattern (compctl -g). */
+ if (cc->glob) {
+ int ns, pl = strlen(prpre), o, paalloc;
+ char *g = dupstring(cc->glob), *pa;
+ char *p2, *p3;
+ int ne = noerrs, md = opts[MARKDIRS];
+
+ /* These are used in the globbing code to make *
+ * things a bit faster. */
+ if (ispattern || mstack)
+ glob_pre = glob_suf = NULL;
+ else {
+ glob_pre = fpre;
+ glob_suf = fsuf;
+ }
+ noerrs = 1;
+ addwhat = -6;
+ o = strlen(prpre);
+ pa = (char *)zalloc(paalloc = o + PATH_MAX);
+ strcpy(pa, prpre);
+ opts[MARKDIRS] = 0;
+
+ /* The compctl -g string may contain more than *
+ * one pattern, so we need a loop. */
+ while (*g) {
+ LinkList l = newlinklist();
+ int ng;
+
+ /* Find the blank terminating the pattern. */
+ while (*g && inblank(*g))
+ g++;
+ /* Oops, we already reached the end of the
+ string. */
+ if (!*g)
+ break;
+ for (p = g + 1; *p && !inblank(*p); p++)
+ if (*p == '\\' && p[1])
+ p++;
+ /* Get the pattern string. */
+ tokenize(g = dupstrpfx(g, p - g));
+ if (*g == '=')
+ *g = Equals;
+ if (*g == '~')
+ *g = Tilde;
+ remnulargs(g);
+ if ((*g == Equals || *g == Tilde) && !cc->withd) {
+ /* The pattern has a `~' or `=' at the *
+ * beginning, so we expand this and use *
+ * the result. */
+ filesub(&g, 0);
+ addlinknode(l, dupstring(g));
+ } else if (*g == '/' && !cc->withd)
+ /* The pattern is a full path (starting *
+ * with '/'), so add it unchanged. */
+ addlinknode(l, dupstring(g));
+ else {
+ /* It's a simple pattern, so append it to *
+ * the path we have on the command line. */
+ int minlen = o + strlen(g);
+ if (minlen >= paalloc)
+ pa = (char *)
+ zrealloc(pa, paalloc = minlen+1);
+ strcpy(pa + o, g);
+ addlinknode(l, dupstring(pa));
+ }
+ /* Do the globbing. */
+ ng = opts[NULLGLOB];
+ opts[NULLGLOB] = 1;
+ globlist(l, 0);
+ opts[NULLGLOB] = ng;
+ /* Get the results. */
+ if (nonempty(l) && peekfirst(l)) {
+ for (p2 = (char *)peekfirst(l); *p2; p2++)
+ if (itok(*p2))
+ break;
+ if (!*p2) {
+ if ((*g == Equals || *g == Tilde ||
+ *g == '/') || cc->withd) {
+ /* IF the pattern started with `~', *
+ * `=', or `/', add the result only, *
+ * if it really matches what we have *
+ * on the line. *
+ * Do this if an initial directory *
+ * was specified, too. */
+ while ((p2 = (char *)ugetnode(l)))
+ if (strpfx(prpre, p2))
+ addmatch(p2 + pl, NULL);
+ } else {
+ /* Otherwise ignore the path we *
+ * prepended to the pattern. */
+ while ((p2 = p3 =
+ (char *)ugetnode(l))) {
+ for (ns = sf1; *p3 && ns; p3++)
+ if (*p3 == '/')
+ ns--;
+
+ addmatch(p3, NULL);
+ }
+ }
+ }
+ }
+ pa[o] = '\0';
+ g = p;
+ }
+ glob_pre = glob_suf = NULL;
+ noerrs = ne;
+ opts[MARKDIRS] = md;
+
+ zfree(pa, paalloc);
+ }
+ }
+ dirs++;
+ }
+ prpre = NULL;
+ }
+ }
+ lppre = lpsuf = NULL;
+ lppl = lpsl = 0;
+ }
+ if (ic) {
+ /* Now change the `~' and `=' tokens to the real characters so *
+ * that things starting with these characters will be added. */
+ rpre = dyncat((ic == Tilde) ? "~" : "=", rpre);
+ rpl++;
+ qrpre = dyncat((ic == Tilde) ? "~" : "=", qrpre);
+ }
+ if (!ic && (cc->mask & CC_COMMPATH) && !*ppre && !*psuf) {
+ /* If we have to complete commands, add alias names, *
+ * shell functions and builtins too. */
+ dumphashtable(aliastab, -3);
+ dumphashtable(reswdtab, -3);
+ dumphashtable(shfunctab, -3);
+ dumphashtable(builtintab, -3);
+ if (isset(HASHLISTALL))
+ cmdnamtab->filltable(cmdnamtab);
+ dumphashtable(cmdnamtab, -3);
+ /* And parameter names if autocd and cdablevars are set. */
+ if (isset(AUTOCD) && isset(CDABLEVARS))
+ dumphashtable(paramtab, -4);
+ }
+ oaw = addwhat = (cc->mask & CC_QUOTEFLAG) ? -2 : CC_QUOTEFLAG;
+
+ if (cc->mask & CC_NAMED)
+ /* Add named directories. */
+ dumphashtable(nameddirtab, addwhat);
+ if (cc->mask & CC_OPTIONS)
+ /* Add option names. */
+ dumphashtable(optiontab, addwhat);
+ if (cc->mask & CC_VARS) {
+ /* And parameter names. */
+ dumphashtable(paramtab, -9);
+ addwhat = oaw;
+ }
+ if (cc->mask & CC_BINDINGS) {
+ /* And zle function names... */
+ dumphashtable(thingytab, CC_BINDINGS);
+ addwhat = oaw;
+ }
+ if (cc->keyvar) {
+ /* This adds things given to the compctl -k flag *
+ * (from a parameter or a list of words). */
+ char **usr = get_user_var(cc->keyvar);
+
+ if (usr)
+ while (*usr)
+ addmatch(*usr++, NULL);
+ }
+ if (cc->mask & CC_USERS) {
+ /* Add user names. */
+ maketildelist();
+ addwhat = oaw;
+ }
+ if (cc->func) {
+ /* This handles the compctl -K flag. */
+ Eprog prog;
+ char **r;
+ int lv = lastval;
+
+ /* Get the function. */
+ if ((prog = getshfunc(cc->func)) != &dummy_eprog) {
+ /* We have it, so build a argument list. */
+ LinkList args = newlinklist();
+ int osc = sfcontext;
+
+ addlinknode(args, cc->func);
+
+ if (delit) {
+ p = dupstrpfx(os, ooffs);
+ untokenize(p);
+ addlinknode(args, p);
+ p = dupstring(os + ooffs);
+ untokenize(p);
+ addlinknode(args, p);
+ } else {
+ addlinknode(args, lpre);
+ addlinknode(args, lsuf);
+ }
+
+ /* This flag allows us to use read -l and -c. */
+ if (incompfunc != 1)
+ incompctlfunc = 1;
+ sfcontext = SFC_COMPLETE;
+ /* Call the function. */
+ doshfunc(cc->func, prog, args, 0, 1);
+ sfcontext = osc;
+ incompctlfunc = 0;
+ /* And get the result from the reply parameter. */
+ if ((r = get_user_var("reply")))
+ while (*r)
+ addmatch(*r++, NULL);
+ }
+ lastval = lv;
+ }
+ if (cc->mask & (CC_JOBS | CC_RUNNING | CC_STOPPED)) {
+ /* Get job names. */
+ int i;
+ char *j;
+
+ for (i = 0; i < MAXJOB; i++)
+ if ((jobtab[i].stat & STAT_INUSE) &&
+ jobtab[i].procs && jobtab[i].procs->text) {
+ int stopped = jobtab[i].stat & STAT_STOPPED;
+
+ j = dupstring(jobtab[i].procs->text);
+ if ((cc->mask & CC_JOBS) ||
+ (stopped && (cc->mask & CC_STOPPED)) ||
+ (!stopped && (cc->mask & CC_RUNNING)))
+ addmatch(j, NULL);
+ }
+ }
+ if (cc->str) {
+ /* Get the stuff from a compctl -s. */
+ LinkList foo = newlinklist();
+ LinkNode n;
+ int first = 1, ng = opts[NULLGLOB], oowe = we, oowb = wb;
+ char *tmpbuf;
+
+ opts[NULLGLOB] = 1;
+
+ /* Put the string in the lexer buffer and call the lexer to *
+ * get the words we have to expand. */
+ zleparse = 1;
+ lexsave();
+ tmpbuf = (char *)zhalloc(strlen(cc->str) + 5);
+ sprintf(tmpbuf, "foo %s", cc->str); /* KLUDGE! */
+ inpush(tmpbuf, 0, NULL);
+ strinbeg(0);
+ noaliases = 1;
+ do {
+ ctxtlex();
+ if (tok == ENDINPUT || tok == LEXERR)
+ break;
+ if (!first && tokstr && *tokstr)
+ addlinknode(foo, ztrdup(tokstr));
+ first = 0;
+ } while (tok != ENDINPUT && tok != LEXERR);
+ noaliases = 0;
+ strinend();
+ inpop();
+ errflag = zleparse = 0;
+ lexrestore();
+ /* Fine, now do full expansion. */
+ prefork(foo, 0);
+ if (!errflag) {
+ globlist(foo, 0);
+ if (!errflag)
+ /* And add the resulting words as matches. */
+ for (n = firstnode(foo); n; incnode(n))
+ addmatch((char *)n->dat, NULL);
+ }
+ opts[NULLGLOB] = ng;
+ we = oowe;
+ wb = oowb;
+ }
+ if (cc->hpat) {
+ /* We have a pattern to take things from the history. */
+ Patprog pprogc = NULL;
+ char *e, *h, hpatsav;
+ int i = addhistnum(curhist,-1,HIST_FOREIGN), n = cc->hnum;
+ Histent he = quietgethistent(i, GETHIST_UPWARD);
+
+ /* Parse the pattern, if it isn't the null string. */
+ if (*(cc->hpat)) {
+ char *thpat = dupstring(cc->hpat);
+
+ tokenize(thpat);
+ pprogc = patcompile(thpat, 0, NULL);
+ }
+ /* n holds the number of history line we have to search. */
+ if (!n)
+ n = -1;
+
+ /* Now search the history. */
+ while (n-- && he) {
+ int iwords;
+ for (iwords = he->nwords - 1; iwords >= 0; iwords--) {
+ h = he->text + he->words[iwords*2];
+ e = he->text + he->words[iwords*2+1];
+ hpatsav = *e;
+ *e = '\0';
+ /* We now have a word from the history, ignore it *
+ * if it begins with a quote or `$'. */
+ if (*h != '\'' && *h != '"' && *h != '`' && *h != '$' &&
+ (!pprogc || pattry(pprogc, h)))
+ /* Otherwise add it if it was matched. */
+ addmatch(dupstring(h), NULL);
+ if (hpatsav)
+ *e = hpatsav;
+ }
+ he = up_histent(he);
+ }
+ }
+ if ((t = cc->mask & (CC_ARRAYS | CC_INTVARS | CC_ENVVARS | CC_SCALARS |
+ CC_READONLYS | CC_SPECIALS | CC_PARAMS)))
+ /* Add various flavours of parameters. */
+ dumphashtable(paramtab, t);
+ if ((t = cc->mask & CC_SHFUNCS))
+ /* Add shell functions. */
+ dumphashtable(shfunctab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
+ if ((t = cc->mask & CC_BUILTINS))
+ /* Add builtins. */
+ dumphashtable(builtintab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
+ if ((t = cc->mask & CC_EXTCMDS)) {
+ /* Add external commands */
+ if (isset(HASHLISTALL))
+ cmdnamtab->filltable(cmdnamtab);
+ dumphashtable(cmdnamtab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
+ }
+ if ((t = cc->mask & CC_RESWDS))
+ /* Add reserved words */
+ dumphashtable(reswdtab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
+ if ((t = cc->mask & (CC_ALREG | CC_ALGLOB)))
+ /* Add the two types of aliases. */
+ dumphashtable(aliastab, t | (cc->mask & (CC_DISCMDS|CC_EXCMDS)));
+ if (keypm && cc == &cc_dummy) {
+ /* Add the keys of the parameter in keypm. */
+ scanhashtable(keypm->gets.hfn(keypm), 0, 0, PM_UNSET, addhnmatch, 0);
+ keypm = NULL;
+ cc_dummy.suffix = NULL;
+ }
+ if (!errflag && cc->ylist) {
+ /* generate the user-defined display list: if anything fails, *
+ * we silently allow the normal completion list to be used. */
+ char **yaptr = NULL, *uv = NULL;
+ Eprog prog;
+
+ if (cc->ylist[0] == '$' || cc->ylist[0] == '(') {
+ /* from variable */
+ uv = cc->ylist + (cc->ylist[0] == '$');
+ } else if ((prog = getshfunc(cc->ylist)) != &dummy_eprog) {
+ /* from function: pass completions as arg list */
+ LinkList args = newlinklist();
+ LinkNode ln;
+ Cmatch m;
+ int osc = sfcontext;
+
+ addlinknode(args, cc->ylist);
+ for (ln = firstnode(matches); ln; ln = nextnode(ln)) {
+ m = (Cmatch) getdata(ln);
+ if (m->ppre) {
+ char *s = (m->psuf ? m->psuf : "");
+ char *p = (char *) zhalloc(strlen(m->ppre) + strlen(m->str) +
+ strlen(s) + 1);
+
+ sprintf(p, "%s%s%s", m->ppre, m->str, s);
+ addlinknode(args, dupstring(p));
+ } else
+ addlinknode(args, dupstring(m->str));
+ }
+
+ /* No harm in allowing read -l and -c here, too */
+ if (incompfunc != 1)
+ incompctlfunc = 1;
+ sfcontext = SFC_COMPLETE;
+ doshfunc(cc->ylist, prog, args, 0, 1);
+ sfcontext = osc;
+ incompctlfunc = 0;
+ uv = "reply";
+ }
+ if (uv)
+ yaptr = get_user_var(uv);
+ if ((tt = cc->explain)) {
+ tt = dupstring(tt);
+ if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) {
+ singsub(&tt);
+ untokenize(tt);
+ }
+ curexpl->str = tt;
+ if (cc->gname) {
+ endcmgroup(yaptr);
+ begcmgroup(cc->gname, gflags);
+ addexpl();
+ } else {
+ addexpl();
+ endcmgroup(yaptr);
+ begcmgroup("default", 0);
+ }
+ } else {
+ endcmgroup(yaptr);
+ begcmgroup("default", 0);
+ }
+ } else if ((tt = cc->explain)) {
+ tt = dupstring(tt);
+ if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) {
+ singsub(&tt);
+ untokenize(tt);
+ }
+ curexpl->str = tt;
+ addexpl();
+ }
+ if (cc->subcmd) {
+ /* Handle -l sub-completion. */
+ char **ow = clwords, *os = cmdstr, *ops = NULL;
+ int oldn = clwnum, oldp = clwpos, br;
+ unsigned long occ = ccont;
+
+ ccont = CC_CCCONT;
+
+ /* So we restrict the words-array. */
+ if (brange >= clwnum)
+ brange = clwnum - 1;
+ if (brange < 1)
+ brange = 1;
+ if (erange >= clwnum)
+ erange = clwnum - 1;
+ if (erange < 1)
+ erange = 1;
+ clwnum = erange - brange + 1;
+ clwpos = clwpos - brange;
+ br = brange;
+
+ if (cc->subcmd[0]) {
+ /* And probably put the command name given to the flag *
+ * in the array. */
+ clwpos++;
+ clwnum++;
+ incmd = 0;
+ ops = clwords[br - 1];
+ clwords[br - 1] = ztrdup(cc->subcmd);
+ cmdstr = ztrdup(cc->subcmd);
+ clwords += br - 1;
+ } else {
+ cmdstr = ztrdup(clwords[br]);
+ incmd = !clwpos;
+ clwords += br;
+ }
+ /* Produce the matches. */
+ makecomplistcmd(s, incmd, CFN_FIRST);
+
+ /* And restore the things we changed. */
+ clwords = ow;
+ zsfree(cmdstr);
+ cmdstr = os;
+ clwnum = oldn;
+ clwpos = oldp;
+ if (ops) {
+ zsfree(clwords[br - 1]);
+ clwords[br - 1] = ops;
+ }
+ ccont = occ;
+ }
+ if (cc->substr)
+ sep_comp_string(cc->substr, s, offs);
+ uremnode(ccstack, firstnode(ccstack));
+ if (cc->matcher)
+ mstack = mstack->next;
+
+ if (mn == mnum)
+ haspattern = ohp;
+}
+
+
static struct builtin bintab[] = {
BUILTIN("compctl", 0, bin_compctl, 0, -1, 0, NULL, NULL),
+ BUILTIN("compcall", 0, bin_compcall, 0, 0, 0, "TD", NULL),
};
/**/
int
-boot_compctl(Module m)
+setup_(Module m)
{
- if(!addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)))
- return 1;
- compctltab->printnode = printcompctlp;
+ compctlreadptr = compctlread;
+ createcompctltable();
+ cc_compos.mask = CC_COMMPATH;
+ cc_compos.mask2 = 0;
+ cc_default.refc = 10000;
+ cc_default.mask = CC_FILES;
+ cc_default.mask2 = 0;
+ cc_first.refc = 10000;
+ cc_first.mask = 0;
+ cc_first.mask2 = CC_CCCONT;
+
+ lastccused = NULL;
+
return 0;
}
-#ifdef MODULE
+/**/
+int
+boot_(Module m)
+{
+ addhookfunc("compctl_make", (Hookfn) ccmakehookfn);
+ addhookfunc("compctl_cleanup", (Hookfn) cccleanuphookfn);
+ return (addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)) != 1);
+}
/**/
int
-cleanup_compctl(Module m)
+cleanup_(Module m)
{
- compctltab->printnode = NULL;
+ deletehookfunc("compctl_make", (Hookfn) ccmakehookfn);
+ deletehookfunc("compctl_cleanup", (Hookfn) cccleanuphookfn);
deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
return 0;
}
-#endif
+
+/**/
+int
+finish_(Module m)
+{
+ deletehashtable(compctltab);
+
+ if (lastccused)
+ freelinklist(lastccused, (FreeFunc) freecompctl);
+
+ compctlreadptr = fallback_compctlread;
+ return 0;
+}