From 5ac242f44924ab4f165326fba5f4d6bf8af29626 Mon Sep 17 00:00:00 2001 From: Sven Wischnowsky Date: Wed, 12 Apr 2000 09:12:15 +0000 Subject: comment out the code to allow $compstate[insert] to select the group (10690) --- ChangeLog | 4 + Doc/Zsh/compwid.yo | 19 +- Src/Zle/compcore.c | 18 +- Src/Zle/compctl.c | 2984 ++++++++++++++++++++++++++++++++++++++++++++++++-- Src/Zle/compresult.c | 4 + 5 files changed, 2948 insertions(+), 81 deletions(-) diff --git a/ChangeLog b/ChangeLog index d7b95acea..da48708ba 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2000-04-12 Sven Wischnowsky + * 10690: Doc/Zsh/compwid.yo, Src/Zle/compcore.c, Src/Zle/compctl.c, + Src/Zle/compresult.c: comment out the code to allow + $compstate[insert] to select the group + * 10686: Src/Zle/compresult.c, Src/Zle/zle_tricky.c: display number of lines instead of number of matches when asking whether to show completion lists diff --git a/Doc/Zsh/compwid.yo b/Doc/Zsh/compwid.yo index a89c0cc16..340dc0752 100644 --- a/Doc/Zsh/compwid.yo +++ b/Doc/Zsh/compwid.yo @@ -280,24 +280,19 @@ latter case this will happen because the tt(AUTO_MENU) is set). On exit it may be set to any of the values above (where setting it to the empty string is the same as unsetting it), or to a number, in which case the match whose number is given will be inserted into the command line. -It may also be set to a string of the form `var(group):var(match)' which -specifies a match from a group of matches to be inserted, counting from 1 -upwards (e.g. `tt(2:4)' specifies the fourth match of the second group). -Negative numbers count backward from the last match or group (with `tt(-1)' -selecting the last match or group) and out-of-range values are wrapped -around, so that a value of zero selects the last match or group and a value +Negative numbers count backward from the last match (with `tt(-1)' +selecting the last match) and out-of-range values are wrapped +around, so that a value of zero selects the last match group and a value one more than the maximum selects the first. Unless the value of this key ends in a space, the match is inserted as in a menu-completion, i.e. without automatically appending a space. Both tt(menu) and tt(automenu) may also specify the the number of the -match to insert, given after a colon, optionally followed by a second -colon and a group number. For example, `tt(menu:2)' says to start -menu-completion, beginning with the second match and `tt(menu:3:2)' -says to start menu-completion with the third match in the second group. +match to insert, given after a colon. For example, `tt(menu:2)' says +to start menu-completion, beginning with the second match. -It may also be set to tt(all), which makes all matches generated be -inserted into the line. +Finally, it may also be set to tt(all), which makes all matches +generated be inserted into the line. ) vindex(to_end, compstate) item(tt(to_end))( diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c index e40873764..e308798e7 100644 --- a/Src/Zle/compcore.c +++ b/Src/Zle/compcore.c @@ -60,7 +60,11 @@ int movetoend; /* The match and group number to insert when starting menucompletion. */ /**/ -mod_export int insmnum, insgnum, insgroup, insspace; +mod_export int insmnum, insspace; + +#if 0 +int insgnum, insgroup; /* mod_export */ +#endif /* Information about menucompletion. */ @@ -753,10 +757,12 @@ callcompfunc(char *s, char *fn) useline = 1; usemenu = 3; insmnum = atoi(compinsert); +#if 0 if ((m = strchr(compinsert, ':'))) { insgroup = 1; insgnum = atoi(m + 1); } +#endif insspace = (compinsert[strlen(compinsert) - 1] == ' '); } else { char *p; @@ -770,10 +776,12 @@ callcompfunc(char *s, char *fn) if (useline && (p = strchr(compinsert, ':'))) { insmnum = atoi(++p); +#if 0 if ((p = strchr(p, ':'))) { insgroup = 1; insgnum = atoi(p + 1); } +#endif } } startauto = ((compinsert && @@ -840,8 +848,12 @@ makecomplist(char *s, int incmd, int lst) mnum = 0; unambig_mnum = -1; isuf = NULL; - insmnum = insgnum = 1; - insgroup = oldlist = oldins = 0; + insmnum = 1; +#if 0 + insgnum = 1; + insgroup = 0; +#endif + oldlist = oldins = 0; begcmgroup("default", 0); menucmp = menuacc = newmatches = onlyexpl = 0; 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; } -static struct builtin bintab[] = { - BUILTIN("compctl", 0, bin_compctl, 0, -1, 0, NULL, NULL), -}; +/* 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 -boot_compctl(Module m) +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) { - if(!addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab))) + 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; - compctltab->printnode = printcompctlp; + 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; } -#ifdef MODULE +/* 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 +setup_(Module m) +{ + 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; +} + +/**/ +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; +} diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c index 78b22ff59..3322c3157 100644 --- a/Src/Zle/compresult.c +++ b/Src/Zle/compresult.c @@ -1076,6 +1076,7 @@ do_ambig_menu(void) } else minfo.cur = NULL; } +#if 0 if (insgroup) { insgnum = comp_mod(insgnum, lastpermgnum); for (minfo.group = amatches; @@ -1088,6 +1089,7 @@ do_ambig_menu(void) } insmnum = comp_mod(insmnum, (minfo.group)->mcount); } else { +#endif insmnum = comp_mod(insmnum, lastpermmnum); for (minfo.group = amatches; minfo.group && (minfo.group)->mcount <= insmnum; @@ -1098,7 +1100,9 @@ do_ambig_menu(void) minfo.asked = 0; return; } +#if 0 } +#endif mc = (minfo.group)->matches + insmnum; do_single(*mc); minfo.cur = mc; -- cgit 1.4.1