From c68e8d226ae4d024c3a0b2d9e05ff03b704a79c2 Mon Sep 17 00:00:00 2001 From: Paul Ackersviller Date: Mon, 7 May 2007 03:23:05 +0000 Subject: Merge of 21428: fix null pointer deref on wrong number of arguments to compvalues. --- Src/Zle/computil.c | 3160 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 2570 insertions(+), 590 deletions(-) (limited to 'Src') diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index 088d89931..e56972cee 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -31,57 +31,59 @@ #include "computil.pro" -/* Help for `_display'. */ - -/* Calculation state. */ - -typedef struct cdisp *Cdisp; - -struct cdisp { - int pre; /* prefix length */ - int suf; /* suffix length */ - int colon; /* number of strings with descriptions */ -}; - -/* Calculate longest prefix and suffix and count the strings with - * descriptions. */ - -static void -cdisp_calc(Cdisp disp, char **args) -{ - char *cp; - int i, nbc; - - for (; *args; args++) { - for (nbc = 0, cp = *args; *cp && *cp != ':'; cp++) - if (*cp == '\\' && cp[1]) - cp++, nbc++; - if (*cp == ':' && cp[1]) { - disp->colon++; - if ((i = cp - *args - nbc) > disp->pre) - disp->pre = i; - if ((i = strlen(cp + 1)) > disp->suf) - disp->suf = i; - } - } -} - -/* Help fuer `_describe'. */ +/* Help for `_describe'. */ typedef struct cdset *Cdset; +typedef struct cdstr *Cdstr; +typedef struct cdrun *Cdrun; struct cdstate { int showd; /* != 0 if descriptions should be shown */ char *sep; /* the separator string */ + int slen; /* its length */ + int maxmlen; /* maximum length to allow for the matches */ Cdset sets; /* the sets of matches */ - struct cdisp disp; /* used to calculate the alignment */ + int pre; /* longest prefix (before description) */ + int suf; /* longest suffix (description) */ + int maxg; /* size of largest group */ + int maxglen; /* columns for matches of largest group */ + int groups; /* number of groups */ + int descs; /* number of non-group matches with desc */ + int gpre; /* prefix length for group display */ + Cdrun runs; /* runs to report to shell code */ }; +struct cdstr { + Cdstr next; /* the next one in this set */ + char *str; /* the string to display */ + char *desc; /* the description or NULL */ + char *match; /* the match to add */ + int len; /* length of str or match */ + Cdstr other; /* next string with the same description */ + int kind; /* 0: not in a group, 1: the first, 2: other */ + Cdset set; /* the set this string is in */ + Cdstr run; /* next in this run */ +}; + +struct cdrun { + Cdrun next; /* ... */ + int type; /* see CRT_* below */ + Cdstr strs; /* strings in this run */ + int count; /* number of strings in this run */ +}; + +#define CRT_SIMPLE 0 +#define CRT_DESC 1 +#define CRT_SPEC 2 +#define CRT_DUMMY 3 +#define CRT_EXPL 4 + struct cdset { Cdset next; /* guess what */ char **opts; /* the compadd-options */ - char **strs; /* the display-strings */ - char **matches; /* the matches (or NULL) */ + Cdstr strs; /* the strings/matches */ + int count; /* number of matches in this set */ + int desc; /* number of matches with description */ }; static struct cdstate cd_state; @@ -91,164 +93,634 @@ static void freecdsets(Cdset p) { Cdset n; + Cdstr s, sn; + Cdrun r, rn; for (; p; p = n) { n = p->next; if (p->opts) freearray(p->opts); - if (p->strs) - freearray(p->strs); - if (p->matches) - freearray(p->matches); + for (s = p->strs; s; s = sn) { + sn = s->next; + zsfree(s->str); + zsfree(s->desc); + if (s->match != s->str) + zsfree(s->match); + zfree(s, sizeof(*s)); + } + for (r = cd_state.runs; r; r = rn) { + rn = r->next; + zfree(r, sizeof(*r)); + } zfree(p, sizeof(*p)); } } +/* Find matches with same descriptions and group them. */ + +static void +cd_group(int maxg) +{ + Cdset set1, set2; + Cdstr str1, str2, *strp; + int num, len; + + cd_state.groups = cd_state.descs = cd_state.maxglen = 0; + cd_state.maxg = 0; + + for (set1 = cd_state.sets; set1; set1 = set1->next) + for (str1 = set1->strs; str1; str1 = str1->next) { + str1->kind = 0; + str1->other = NULL; + } + + for (set1 = cd_state.sets; set1; set1 = set1->next) { + for (str1 = set1->strs; str1; str1 = str1->next) { + if (!str1->desc || str1->kind != 0) + continue; + + num = 1; + len = str1->len + cd_state.slen; + if (len > cd_state.maxglen) + cd_state.maxglen = len; + strp = &(str1->other); + + for (set2 = set1; set2; set2 = set2->next) { + for (str2 = (set2 == set1 ? str1->next : set2->strs); + str2; str2 = str2->next) + if (str2->desc && !strcmp(str1->desc, str2->desc)) { + len += 2 + str2->len; + if (len > cd_state.maxmlen || num == maxg) + break; + if (len > cd_state.maxglen) + cd_state.maxglen = len; + str1->kind = 1; + str2->kind = 2; + num++; + *strp = str2; + strp = &(str2->other); + } + if (str2) + break; + } + *strp = NULL; + + if (num > 1) + cd_state.groups++; + else + cd_state.descs++; + + if (num > cd_state.maxg) + cd_state.maxg = num; + } + } +} + +/* Calculate longest prefix and suffix and count the strings with + * descriptions. */ + +static void +cd_calc() +{ + Cdset set; + Cdstr str; + int l; + + cd_state.pre = cd_state.suf = 0; + + for (set = cd_state.sets; set; set = set->next) { + set->count = set->desc = 0; + for (str = set->strs; str; str = str->next) { + set->count++; + if ((l = strlen(str->str)) > cd_state.pre) + cd_state.pre = l; + if (str->desc) { + set->desc++; + if ((l = strlen(str->desc)) > cd_state.suf) + cd_state.suf = l; + } + } + } +} + +static int +cd_sort(const void *a, const void *b) +{ + return strcmp((*((Cdstr *) a))->str, (*((Cdstr *) b))->str); +} + +static int +cd_prep() +{ + Cdrun run, *runp; + Cdset set; + Cdstr str, *strp; + + runp = &(cd_state.runs); + + if (cd_state.groups) { + int lines = cd_state.groups + cd_state.descs; + VARARR(Cdstr, grps, lines); + VARARR(int, wids, cd_state.maxg); + Cdstr gs, gp, gn, *gpp; + int i, j, d; + Cdrun expl; + Cdstr *strp2; + + memset(wids, 0, cd_state.maxg * sizeof(int)); + strp = grps; + + for (set = cd_state.sets; set; set = set->next) + for (str = set->strs; str; str = str->next) { + if (str->kind != 1) { + if (!str->kind && str->desc) { + if (str->len > wids[0]) + wids[0] = str->len; + str->other = NULL; + *strp++ = str; + } + continue; + } + gs = str; + gs->kind = 2; + gp = str->other; + gs->other = NULL; + for (; gp; gp = gn) { + gn = gp->other; + gp->other = NULL; + for (gpp = &gs; *gpp && (*gpp)->len > gp->len; + gpp = &((*gpp)->other)); + gp->other = *gpp; + *gpp = gp; + } + for (gp = gs, i = 0; gp; gp = gp->other, i++) + if (gp->len > wids[i]) + wids[i] = gp->len; + + *strp++ = gs; + } + + cd_state.gpre = 0; + for (i = 0; i < cd_state.maxg; i++) + cd_state.gpre += wids[i] + 2; + + if (cd_state.gpre > cd_state.maxmlen && cd_state.maxglen > 1) + return 1; + + qsort(grps, lines, sizeof(Cdstr), cd_sort); + + for (i = lines, strp = grps; i > 1; i--, strp++) { + strp2 = strp + 1; + if (!strcmp((*strp)->desc, (*strp2)->desc)) + continue; + for (j = i - 2, strp2++; j > 0; j--, strp2++) + if (!strcmp((*strp)->desc, (*strp2)->desc)) { + Cdstr tmp = *strp2; + + memmove(strp + 2, strp + 1, + (strp2 - strp - 1) * sizeof(Cdstr)); + + *++strp = tmp; + i--; + } + } + expl = (Cdrun) zalloc(sizeof(*run)); + expl->type = CRT_EXPL; + expl->strs = grps[0]; + expl->count = lines; + + for (i = lines, strp = grps, strp2 = NULL; i; i--, strp++) { + str = *strp; + *strp = str->other; + if (strp2) + *strp2 = str; + strp2 = &(str->run); + + *runp = run = (Cdrun) zalloc(sizeof(*run)); + runp = &(run->next); + run->type = CRT_SPEC; + run->strs = str; + run->count = 1; + } + *strp2 = NULL; + + for (i = cd_state.maxg - 1; i; i--) { + for (d = 0, j = lines, strp = grps; j; j--, strp++) { + if ((str = *strp)) { + if (d) { + *runp = run = (Cdrun) zalloc(sizeof(*run)); + runp = &(run->next); + run->type = CRT_DUMMY; + run->strs = expl->strs; + run->count = d; + d = 0; + } + *runp = run = (Cdrun) zalloc(sizeof(*run)); + runp = &(run->next); + run->type = CRT_SPEC; + run->strs = str; + run->strs->run = NULL; + run->count = 1; + + *strp = str->other; + } else + d++; + } + if (d) { + *runp = run = (Cdrun) zalloc(sizeof(*run)); + runp = &(run->next); + run->type = CRT_DUMMY; + run->strs = expl->strs; + run->count = d; + } + } + *runp = expl; + runp = &(expl->next); + + for (set = cd_state.sets; set; set = set->next) { + for (i = 0, gs = NULL, gpp = &gs, str = set->strs; + str; str = str->next) { + if (str->kind || str->desc) + continue; + + i++; + *gpp = str; + gpp = &(str->run); + } + *gpp = NULL; + if (i) { + *runp = run = (Cdrun) zalloc(sizeof(*run)); + runp = &(run->next); + run->type = CRT_SIMPLE; + run->strs = gs; + run->count = i; + } + } + } else if (cd_state.showd) { + for (set = cd_state.sets; set; set = set->next) { + if (set->desc) { + *runp = run = (Cdrun) zalloc(sizeof(*run)); + runp = &(run->next); + run->type = CRT_DESC; + strp = &(run->strs); + for (str = set->strs; str; str = str->next) + if (str->desc) { + *strp = str; + strp = &(str->run); + } + *strp = NULL; + run->count = set->desc; + } + if (set->desc != set->count) { + *runp = run = (Cdrun) zalloc(sizeof(*run)); + runp = &(run->next); + run->type = CRT_SIMPLE; + strp = &(run->strs); + for (str = set->strs; str; str = str->next) + if (!str->desc) { + *strp = str; + strp = &(str->run); + } + *strp = NULL; + run->count = set->count - set->desc; + } + } + } else { + for (set = cd_state.sets; set; set = set->next) + if (set->count) { + *runp = run = (Cdrun) zalloc(sizeof(*run)); + runp = &(run->next); + run->type = CRT_SIMPLE; + run->strs = set->strs; + for (str = set->strs; str; str = str->next) + str->run = str->next; + run->count = set->count; + } + } + *runp = NULL; + + return 0; +} + +/* Duplicate and concatenate two arrays. Return the result. */ + +static char ** +cd_arrcat(char **a, char **b) +{ + if (!b) + return zarrdup(a); + else { + char **r = (char **) zalloc((arrlen(a) + arrlen(b) + 1) * + sizeof(char *)); + char **p = r; + + for (; *a; a++) + *p++ = ztrdup(*a); + for (; *b; b++) + *p++ = ztrdup(*b); + + *p = NULL; + + return r; + } +} + /* Initialisation. Store and calculate the string and matches and so on. */ static int -cd_init(char *nam, char *sep, char **args, int disp) +cd_init(char *nam, char *hide, char *mlen, char *sep, + char **opts, char **args, int disp) { Cdset *setp, set; + Cdstr *strp, str; char **ap, *tmp; + int grp = 0, itmp; if (cd_parsed) { zsfree(cd_state.sep); freecdsets(cd_state.sets); + cd_parsed = 0; } setp = &(cd_state.sets); cd_state.sep = ztrdup(sep); + cd_state.slen = ztrlen(sep); cd_state.sets = NULL; - cd_state.disp.pre = cd_state.disp.suf = cd_state.disp.colon = 0; cd_state.showd = disp; - + cd_state.maxg = cd_state.groups = cd_state.descs = 0; + cd_state.maxmlen = atoi(mlen); + itmp = columns - cd_state.slen - 4; + if (cd_state.maxmlen > itmp) + cd_state.maxmlen = itmp; + if (cd_state.maxmlen < 4) + cd_state.maxmlen = 4; + if (*args && !strcmp(*args, "-g")) { + args++; + grp = 1; + } while (*args) { - *setp = set = (Cdset) zcalloc(sizeof(*set)); + *setp = set = (Cdset) zshcalloc(sizeof(*set)); setp = &(set->next); + *setp = NULL; + set->opts = NULL; + set->strs = NULL; if (!(ap = get_user_var(*args))) { zwarnnam(nam, "invalid argument: %s", *args, 0); + zsfree(cd_state.sep); + freecdsets(cd_state.sets); return 1; } - set->strs = zarrdup(ap); - - if (disp) - cdisp_calc(&(cd_state.disp), set->strs); + for (str = NULL, strp = &(set->strs); *ap; ap++) { + *strp = str = (Cdstr) zalloc(sizeof(*str)); + strp = &(str->next); + + str->kind = 0; + str->other = NULL; + str->set = set; + + for (tmp = *ap; *tmp && *tmp != ':'; tmp++) + if (*tmp == '\\' && tmp[1]) + tmp++; + + if (*tmp) + str->desc = ztrdup(rembslash(tmp + 1)); + else + str->desc = NULL; + *tmp = '\0'; + str->str = str->match = ztrdup(rembslash(*ap)); + str->len = strlen(str->str); + } + if (str) + str->next = NULL; if (*++args && **args != '-') { if (!(ap = get_user_var(*args))) { zwarnnam(nam, "invalid argument: %s", *args, 0); + zsfree(cd_state.sep); + freecdsets(cd_state.sets); return 1; } - set->matches = zarrdup(ap); + for (str = set->strs; str && *ap; str = str->next, ap++) + str->match = ztrdup(*ap); + args++; } + if (hide && *hide) { + for (str = set->strs; str; str = str->next) { + if (str->str == str->match) + str->str = ztrdup(str->str); + if (hide[1] && str->str[0] == '-' && str->str[1] == '-') + strcpy(str->str, str->str + 2); + else if (str->str[0] == '-' || str->str[0] == '+') + strcpy(str->str, str->str + 1); + } + } for (ap = args; *args && (args[0][0] != '-' || args[0][1] != '-' || args[0][2]); args++); tmp = *args; *args = NULL; - set->opts = zarrdup(ap); + set->opts = cd_arrcat(ap, opts); if ((*args = tmp)) args++; } + if (disp && grp) { + int mg = columns; + + do { + cd_group(mg); + mg = cd_state.maxg - 1; + cd_calc(); + } while (cd_prep()); + + } else { + cd_calc(); + cd_prep(); + } + cd_parsed = 1; return 0; } -/* Get the next set. */ +/* Copy an array with one element in reserve (at the beginning). */ -static int -cd_get(char **params) +static char ** +cd_arrdup(char **a) { - Cdset set; - - if ((set = cd_state.sets)) { - char **sd, **sdp, **md, **mdp, **ss, **ssp, **ms, **msp; - char **p, **mp, *cp, *copy, *cpp, oldc; - int dl = 1, sl = 1, sepl = strlen(cd_state.sep); - int pre = cd_state.disp.pre, suf = cd_state.disp.suf; - VARARR(char, buf, pre + suf + sepl + 1); - - for (p = set->strs; *p; p++) - if (cd_state.showd) { - for (cp = *p; *cp && *cp != ':'; cp++) - if (*cp == '\\' && cp[1]) - cp++; - if (*cp == ':' && cp[1]) - dl++; - else - sl++; - } else - sl++; - - sd = (char **) zalloc(dl * sizeof(char *)); - ss = (char **) zalloc(sl * sizeof(char *)); - md = (char **) zalloc(dl * sizeof(char *)); - ms = (char **) zalloc(sl * sizeof(char *)); - - if (cd_state.showd) { - memcpy(buf + pre, cd_state.sep, sepl); - suf = pre + sepl; - } - - /* Build the aligned display strings. */ - - for (sdp = sd, ssp = ss, mdp = md, msp = ms, - p = set->strs, mp = set->matches; *p; p++) { - copy = dupstring(*p); - for (cp = cpp = copy; *cp && *cp != ':'; cp++) { - if (*cp == '\\' && cp[1]) - cp++; - *cpp++ = *cp; - } - oldc = *cpp; - *cpp = '\0'; - if (((cpp == cp && oldc == ':') || *cp == ':') && cp[1] && - cd_state.showd) { - memset(buf, ' ', pre); - memcpy(buf, copy, (cpp - copy)); - strcpy(buf + suf, cp + 1); - *sdp++ = ztrdup(buf); - if (mp) { - *mdp++ = ztrdup(*mp); - if (*mp) - mp++; - } else - *mdp++ = ztrdup(copy); - } else { - *ssp++ = ztrdup(copy); - if (mp) { - *msp++ = ztrdup(*mp); - if (*mp) - mp++; - } else - *msp++ = ztrdup(copy); - } - } - *sdp = *ssp = *mdp = *msp = NULL; + char **r = (char **) zalloc((arrlen(a) + 2) * sizeof(char *)); + char **p = r + 1; - p = zarrdup(set->opts); + while (*a) + *p++ = ztrdup(*a++); + *p = NULL; - setaparam(params[0], p); - setaparam(params[1], sd); - setaparam(params[2], md); - setaparam(params[3], ss); - setaparam(params[4], ms); + return r; +} - cd_state.sets = set->next; - set->next = NULL; - freecdsets(set); +/* Get the next set. */ - return 0; +static int +cd_get(char **params) +{ + Cdrun run; + + if ((run = cd_state.runs)) { + Cdstr str; + char **mats, **mp, **dpys, **dp, **opts, *csl = ""; + + cd_state.runs = run->next; + + switch (run->type) { + case CRT_SIMPLE: + mats = mp = (char **) zalloc((run->count + 1) * sizeof(char *)); + dpys = dp = (char **) zalloc((run->count + 1) * sizeof(char *)); + + for (str = run->strs; str; str = str->run) { + *mp++ = ztrdup(str->match); + *dp++ = ztrdup(str->str ? str->str : str->match); + } + *mp = *dp = NULL; + opts = zarrdup(run->strs->set->opts); + if (cd_state.groups) { + /* We are building a columnised list with dummy matches + * but there are also matches without descriptions. + * Those end up in a different group, so make sure that + * group doesn't have an explanation. */ + + for (mp = dp = opts; *mp; mp++) { + if (dp[0][0] == '-' && dp[0][1] == 'X') { + if (!dp[0][2] && dp[1]) + mp++; + } else + *dp++ = *mp; + } + *dp = NULL; + } + break; + + case CRT_DESC: + { + VARARR(char, buf, + cd_state.pre + cd_state.suf + cd_state.slen + 3); + char *sufp = NULL; + + memcpy(buf + cd_state.pre + 2, cd_state.sep, cd_state.slen); + buf[cd_state.pre] = buf[cd_state.pre + 1] = ' '; + sufp = buf + cd_state.pre + cd_state.slen + 2; + + mats = mp = (char **) zalloc((run->count + 1) * sizeof(char *)); + dpys = dp = (char **) zalloc((run->count + 1) * sizeof(char *)); + + for (str = run->strs; str; str = str->run) { + *mp++ = ztrdup(str->match); + memset(buf, ' ', cd_state.pre); + memcpy(buf, str->str, str->len); + strcpy(sufp, str->desc); + if (strlen(buf) >= columns - 1) + buf[columns - 1] = '\0'; + *dp++ = ztrdup(buf); + } + *mp = *dp = NULL; + opts = cd_arrdup(run->strs->set->opts); + opts[0] = ztrdup("-l"); + break; + } + + case CRT_SPEC: + mats = (char **) zalloc(2 * sizeof(char *)); + dpys = (char **) zalloc(2 * sizeof(char *)); + mats[0] = ztrdup(run->strs->match); + dpys[0] = ztrdup(run->strs->str); + mats[1] = dpys[1] = NULL; + opts = cd_arrdup(run->strs->set->opts); + for (dp = opts + 1; *dp; dp++) + if (dp[0][0] == '-' && dp[0][1] == 'J') + break; + if (*dp) { + char *s = tricat("-2V", "", dp[0] + 2); + + zsfree(*dp); + *dp = s; + + memmove(opts, opts + 1, + (arrlen(opts + 1) + 1) * sizeof(char *)); + + } else + opts[0] = ztrdup("-2V-default-"); + csl = "packed"; + break; + + case CRT_DUMMY: + { + char buf[20]; + + sprintf(buf, "-E%d", run->count); + + mats = (char **) zalloc(sizeof(char *)); + dpys = (char **) zalloc(sizeof(char *)); + mats[0] = dpys[0] = NULL; + + opts = cd_arrdup(run->strs->set->opts); + opts[0] = ztrdup(buf); + + csl = "packed"; + } + break; + + default: /* This silences the "might be used uninitialized" warnings */ + case CRT_EXPL: + { + int dlen = columns - cd_state.gpre - cd_state.slen; + VARARR(char, dbuf, dlen + cd_state.slen); + char buf[20]; + int i = run->count; + + sprintf(buf, "-E%d", i); + + mats = (char **) zalloc(sizeof(char *)); + dpys = (char **) zalloc((i + 1) * sizeof(char *)); + + for (dp = dpys, str = run->strs; str; str = str->run) { + if (str->run && !strcmp(str->desc, str->run->desc)) { + *dp++ = ztrdup(""); + continue; + } + memset(dbuf + cd_state.slen, ' ', dlen - 1); + dbuf[dlen + cd_state.slen - 1] = '\0'; + strcpy(dbuf, cd_state.sep); + memcpy(dbuf + cd_state.slen, + str->desc, + ((int)strlen(str->desc) >= dlen ? dlen - 1 : + (int)strlen(str->desc))); + *dp++ = ztrdup(dbuf); + } + mats[0] = *dp = NULL; + + opts = cd_arrdup(run->strs->set->opts); + opts[0] = ztrdup(buf); + + csl = "packed"; + } + break; + } + setsparam(params[0], ztrdup(csl)); + setaparam(params[1], opts); + setaparam(params[2], mats); + setaparam(params[3], dpys); + + zfree(run, sizeof(*run)); + + return 0; } return 1; } /**/ static int -bin_compdescribe(char *nam, char **args, char *ops, int func) +bin_compdescribe(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) { + int n = arrlen(args); + if (incompfunc != 1) { zwarnnam(nam, "can only be called from completion function", NULL, 0); return 1; @@ -259,17 +731,30 @@ bin_compdescribe(char *nam, char **args, char *ops, int func) } switch (args[0][1]) { case 'i': - cd_parsed = 1; - return cd_init(nam, "", args + 1, 0); + if (n < 3) { + zwarnnam(nam, "not enough arguments", NULL, 0); + + return 1; + } + return cd_init(nam, args[1], args[2], "", NULL, args + 3, 0); case 'I': - cd_parsed = 1; - return cd_init(nam, args[1], args + 2, 1); + if (n < 6) { + zwarnnam(nam, "not enough arguments", NULL, 0); + + return 1; + } else { + char **opts; + + if (!(opts = getaparam(args[4]))) { + zwarnnam(nam, "unknown parameter: %s", args[4], 0); + return 1; + } + return cd_init(nam, args[1], args[2], args[3], opts, args + 5, 1); + } case 'g': if (cd_parsed) { - int n = arrlen(args); - - if (n != 6) { - zwarnnam(nam, (n < 6 ? "not enough arguments" : + if (n != 5) { + zwarnnam(nam, (n < 5 ? "not enough arguments" : "too many arguments"), NULL, 0); return 1; } @@ -293,6 +778,7 @@ typedef struct caarg *Caarg; struct cadef { Cadef next; /* next in cache */ + Cadef snext; /* next set */ Caopt opts; /* the options */ int nopts, ndopts, nodopts; /* number of options/direct/optional direct */ Caarg args; /* the normal arguments */ @@ -304,8 +790,14 @@ struct cadef { char *match; /* -M spec to use */ int argsactive; /* if arguments are still allowed */ /* used while parsing a command line */ + char *set; /* set name prefix (-), shared */ + char *sname; /* set name */ + int flags; /* see CDF_* below */ + char *nonarg; /* pattern for non-args (-A argument) */ }; +#define CDF_SEP 1 + /* Description for an option. */ struct caopt { @@ -317,12 +809,15 @@ struct caopt { Caarg args; /* option arguments */ int active; /* still allowed on command line */ int num; /* it's the num'th option */ + char *set; /* set name, shared */ + int not; /* don't complete this option (`!...') */ }; #define CAO_NEXT 1 #define CAO_DIRECT 2 #define CAO_ODIRECT 3 #define CAO_EQUAL 4 +#define CAO_OEQUAL 5 /* Description for an argument */ @@ -335,7 +830,10 @@ struct caarg { char *end; /* end-pattern for :::... */ char *opt; /* option name if for an option */ int num; /* it's the num'th argument */ + int min; /* it's also this argument, using opt. args */ + int direct; /* number was given directly */ int active; /* still allowed on command line */ + char *set; /* set name, shared */ }; #define CAA_NORMAL 1 @@ -389,10 +887,14 @@ freecaargs(Caarg a) static void freecadef(Cadef d) { - if (d) { - Caopt p, n; + Cadef s; + Caopt p, n; + while (d) { + s = d->snext; zsfree(d->match); + zsfree(d->set); + zsfree(d->sname); if (d->defs) freearray(d->defs); @@ -407,9 +909,11 @@ freecadef(Cadef d) } freecaargs(d->args); freecaargs(d->rest); + zsfree(d->nonarg); if (d->single) zfree(d->single, 256 * sizeof(Caopt)); zfree(d, sizeof(*d)); + d = s; } } @@ -454,7 +958,8 @@ bslashcolon(char *s) /* Parse an argument definition. */ static Caarg -parse_caarg(int mult, int type, int num, char *oname, char **def) +parse_caarg(int mult, int type, int num, int opt, char *oname, char **def, + char *set) { Caarg ret = (Caarg) zalloc(sizeof(*ret)); char *p = *def, *d, sav; @@ -463,8 +968,11 @@ parse_caarg(int mult, int type, int num, char *oname, char **def) ret->descr = ret->action = ret->end = NULL; ret->xor = NULL; ret->num = num; + ret->min = num - opt; ret->type = type; ret->opt = ztrdup(oname); + ret->direct = 0; + ret->set = set; /* Get the description. */ @@ -489,22 +997,69 @@ parse_caarg(int mult, int type, int num, char *oname, char **def) *p = ':'; } else ret->action = ztrdup(rembslashcolon(p + 1)); - } + } else + ret->action = ztrdup(""); *def = p; return ret; } +static Cadef +alloc_cadef(char **args, int single, char *match, char *nonarg, int flags) +{ + Cadef ret; + + ret = (Cadef) zalloc(sizeof(*ret)); + ret->next = ret->snext = NULL; + ret->opts = NULL; + ret->args = ret->rest = NULL; + ret->nonarg = ztrdup(nonarg); + if (args) { + ret->defs = zarrdup(args); + ret->ndefs = arrlen(args); + } else { + ret->defs = NULL; + ret->ndefs = 0; + } + ret->lastt = time(0); + ret->set = ret->sname = NULL; + if (single) { + ret->single = (Caopt *) zalloc(256 * sizeof(Caopt)); + memset(ret->single, 0, 256 * sizeof(Caopt)); + } else + ret->single = NULL; + ret->match = ztrdup(match); + ret->flags = flags; + + return ret; +} + +static void +set_cadef_opts(Cadef def) +{ + Caarg argp; + int xnum; + + for (argp = def->args, xnum = 0; argp; argp = argp->next) { + if (!argp->direct) + argp->min = argp->num - xnum; + if (argp->type == CAA_OPT) + xnum++; + } +} + /* Parse an array of definitions. */ static Cadef parse_cadef(char *nam, char **args) { - Cadef ret; + Cadef all, ret; Caopt *optp; - char **oargs = args, *p, *q, *match = "r:|[_-]=* r:|=*", **xor; - char *adpre, *adsuf; - int single = 0, anum = 1, xnum, nopts, ndopts, nodopts; + char **oargs = args, *p, *q, *match = "r:|[_-]=* r:|=*", **xor, **sargs; + char *adpre, *adsuf, *axor = NULL, *doset = NULL, **setp = NULL; + char *nonarg = NULL; + int single = 0, anum = 1, xnum, nopts, ndopts, nodopts, flags = 0; + int state = 0, not = 0; nopts = ndopts = nodopts = 0; @@ -520,49 +1075,104 @@ parse_cadef(char *nam, char **args) } else adpre = adsuf = NULL; - /* Now get the -s and -M options. */ + /* Now get the -s, -A, -S and -M options. */ args++; - while ((p = *args)) { - if (!strcmp(p, "-s")) - single = 1; - else if (p[0] == '-' && p[1] == 'M') { - if (p[2]) - match = p + 2; - else if (args[1]) - match = *++args; - else { - args++; + while ((p = *args) && *p == '-' && p[1]) { + for (q = ++p; *q; q++) + if (*q == 'M' || *q == 'A') { + q = ""; + break; + } else if (*q != 's' && *q != 'S') break; + + if (*q) + break; + + for (; *p; p++) { + if (*p == 's') + single = 1; + else if (*p == 'S') + flags |= CDF_SEP; + else if (*p == 'A') { + if (p[1]) { + nonarg = p + 1; + p = "" - 1; + } else if (args[1]) + nonarg = *++args; + else + break; + } else if (*p == 'M') { + if (p[1]) { + match = p + 1; + p = "" - 1; + } else if (args[1]) + match = *++args; + else + break; } - } else + } + if (*p) break; + args++; } + if (*args && !strcmp(*args, ":")) + args++; if (!*args) return NULL; + if (nonarg) + tokenize(nonarg = dupstring(nonarg)); + /* Looks good. Optimistically allocate the cadef structure. */ - ret = (Cadef) zalloc(sizeof(*ret)); - ret->next = NULL; - ret->opts = NULL; - ret->args = ret->rest = NULL; - ret->defs = zarrdup(oargs); - ret->ndefs = arrlen(oargs); - ret->lastt = time(0); - if (single) { - ret->single = (Caopt *) zalloc(256 * sizeof(Caopt)); - memset(ret->single, 0, 256 * sizeof(Caopt)); - } else - ret->single = NULL; - ret->match = ztrdup(match); + all = ret = alloc_cadef(oargs, single, match, nonarg, flags); + optp = &(ret->opts); + anum = 1; + + sargs = args; /* Get the definitions. */ - for (optp = &(ret->opts); *args; args++) { + for (; *args; args++) { + if (args[0][0] == '-' && !args[0][1] && args[1]) { + if (!state) { + char *p; + int l; + + if (setp) + args = setp; + p = *++args; + l = strlen(p) - 1; + if (*p == '(' && p[l] == ')') { + axor = p = dupstring(p + 1); + p[l - 1] = '\0'; + } else + axor = NULL; + ret->set = doset = tricat(p, "-", ""); + ret->sname = ztrdup(p); + state = 1; + } else { + setp = args; + state = 0; + args = sargs - 1; + doset = NULL; + ret->nopts = nopts; + ret->ndopts = ndopts; + ret->nodopts = nodopts; + set_cadef_opts(ret); + ret = ret->snext = alloc_cadef(NULL, single, NULL, nonarg, flags); + optp = &(ret->opts); + nopts = ndopts = nodopts = 0; + anum = 1; + } + continue; + } p = dupstring(*args); xnum = 0; + if ((not = (*p == '!'))) + p++; if (*p == '(') { /* There is a xor list, get it. */ @@ -588,16 +1198,25 @@ parse_cadef(char *nam, char **args) } /* Oops, end-of-string. */ if (*p != ')') { - freecadef(ret); + freecadef(all); zwarnnam(nam, "invalid argument: %s", *args, 0); return NULL; } + if (doset && axor) + xnum++; xor = (char **) zalloc((xnum + 2) * sizeof(char *)); for (node = firstnode(list), xp = xor; node; incnode(node), xp++) *xp = ztrdup((char *) getdata(node)); + if (doset && axor) + *xp++ = ztrdup(axor); xp[0] = xp[1] = NULL; p++; + } else if (doset && axor) { + xnum = 1; + xor = (char **) zalloc(3 * sizeof(char *)); + xor[0] = ztrdup(axor); + xor[1] = xor[2] = NULL; } else xor = NULL; @@ -607,7 +1226,7 @@ parse_cadef(char *nam, char **args) Caopt opt; Caarg oargs = NULL; int multi, otype = CAO_NEXT, again = 0; - char *name, *descr, c; + char *name, *descr, c, *againp = NULL; rec: @@ -621,9 +1240,10 @@ parse_cadef(char *nam, char **args) p[2] != '=' && p[2] != '-' && p[2] != '+') { /* It's a -+ or +- definition. We just execute the whole * stuff twice for such things. */ + againp = dupstring(p); name = ++p; *p = (again ? '-' : '+'); - again = 1 - again; + again++; } else { name = p; /* If it's a long option skip over the first `-'. */ @@ -631,15 +1251,17 @@ parse_cadef(char *nam, char **args) p++; } if (!p[1]) { - freecadef(ret); + freecadef(all); zwarnnam(nam, "invalid argument: %s", *args, 0); return NULL; } /* Skip over the name. */ for (p++; *p && *p != ':' && *p != '[' && - ((*p != '-' && *p != '+' && *p != '=') || - (p[1] != ':' && p[1] != '[')); p++) + ((*p != '-' && *p != '+') || + (p[1] != ':' && p[1] != '[')) && + (*p != '=' || + (p[1] != ':' && p[1] != '[' && p[1] != '-')); p++) if (*p == '\\' && p[1]) p++; @@ -653,8 +1275,11 @@ parse_cadef(char *nam, char **args) otype = CAO_ODIRECT; c = *++p; } else if (c == '=') { - otype = CAO_EQUAL; - c = *++p; + otype = CAO_OEQUAL; + if ((c = *++p) == '-') { + otype = CAO_EQUAL; + c = *++p; + } } /* Get the optional description, if any. */ if (c == '[') { @@ -663,7 +1288,7 @@ parse_cadef(char *nam, char **args) p++; if (!*p) { - freecadef(ret); + freecadef(all); zwarnnam(nam, "invalid option definition: %s", *args, 0); return NULL; } @@ -673,7 +1298,7 @@ parse_cadef(char *nam, char **args) descr = NULL; if (c && c != ':') { - freecadef(ret); + freecadef(all); zwarnnam(nam, "invalid option definition: %s", *args, 0); return NULL; } @@ -681,15 +1306,16 @@ parse_cadef(char *nam, char **args) if (!multi) { if (!xor) { xor = (char **) zalloc(2 * sizeof(char *)); - xor[1] = NULL; + xor[0] = xor[1] = NULL; } - xor[xnum] = ztrdup(name); + zsfree(xor[xnum]); + xor[xnum] = ztrdup(rembslashcolon(name)); } if (c == ':') { /* There's at least one argument. */ Caarg *oargp = &oargs; - int atype, rest, oanum = 1; + int atype, rest, oanum = 1, onum = 0; char *end; /* Loop over the arguments. */ @@ -716,7 +1342,7 @@ parse_cadef(char *nam, char **args) *p = sav; } if (*p != ':') { - freecadef(ret); + freecadef(all); freecaargs(oargs); zwarnnam(nam, "invalid option definition: %s", *args, 0); @@ -736,7 +1362,10 @@ parse_cadef(char *nam, char **args) /* And the definition. */ - *oargp = parse_caarg(!rest, atype, oanum++, name, &p); + *oargp = parse_caarg(!rest, atype, oanum++, onum, + name, &p, doset); + if (atype == CAA_OPT) + onum++; if (end) (*oargp)->end = ztrdup(end); oargp = &((*oargp)->next); @@ -751,6 +1380,7 @@ parse_cadef(char *nam, char **args) optp = &((*optp)->next); opt->next = NULL; + opt->set = doset; opt->name = ztrdup(rembslashcolon(name)); if (descr) opt->descr = ztrdup(descr); @@ -767,14 +1397,15 @@ parse_cadef(char *nam, char **args) opt->descr = NULL; } else opt->descr = NULL; - opt->xor = xor; + opt->xor = (again == 1 && xor ? zarrdup(xor) : xor); opt->type = otype; opt->args = oargs; opt->num = nopts++; + opt->not = not; - if (otype == CAO_DIRECT) + if (otype == CAO_DIRECT || otype == CAO_EQUAL) ndopts++; - else if (otype == CAO_ODIRECT || otype == CAO_EQUAL) + else if (otype == CAO_ODIRECT || otype == CAO_OEQUAL) nodopts++; /* If this is for single-letter option we also store a @@ -783,9 +1414,9 @@ parse_cadef(char *nam, char **args) if (single && name[1] && !name[2]) ret->single[STOUC(name[1])] = opt; - if (again) { + if (again == 1) { /* Do it all again for `*-...'. */ - p = dupstring(*args); + p = againp; goto rec; } } else if (*p == '*') { @@ -793,13 +1424,16 @@ parse_cadef(char *nam, char **args) int type = CAA_REST; + if (not) + continue; + if (*++p != ':') { - freecadef(ret); + freecadef(all); zwarnnam(nam, "invalid rest argument definition: %s", *args, 0); return NULL; } if (ret->rest) { - freecadef(ret); + freecadef(all); zwarnnam(nam, "doubled rest argument definition: %s", *args, 0); return NULL; } @@ -810,15 +1444,18 @@ parse_cadef(char *nam, char **args) } else type = CAA_RARGS; } - ret->rest = parse_caarg(0, type, -1, NULL, &p); + ret->rest = parse_caarg(0, type, -1, 0, NULL, &p, doset); ret->rest->xor = xor; } else { /* It's a normal argument definition. */ - int type = CAA_NORMAL; + int type = CAA_NORMAL, direct; Caarg arg, tmp, pre; - if (idigit(*p)) { + if (not) + continue; + + if ((direct = idigit(*p))) { /* Argment number is given. */ int num = 0; @@ -831,7 +1468,7 @@ parse_cadef(char *nam, char **args) anum++; if (*p != ':') { - freecadef(ret); + freecadef(all); zwarnnam(nam, "invalid argument: %s", *args, 0); return NULL; } @@ -840,8 +1477,9 @@ parse_cadef(char *nam, char **args) type = CAA_OPT; p++; } - arg = parse_caarg(0, type, anum - 1, NULL, &p); + arg = parse_caarg(0, type, anum - 1, 0, NULL, &p, doset); arg->xor = xor; + arg->direct = direct; /* Sort the new definition into the existing list. */ @@ -850,7 +1488,7 @@ parse_cadef(char *nam, char **args) pre = tmp, tmp = tmp->next); if (tmp && tmp->num == anum - 1) { - freecadef(ret); + freecadef(all); freecaargs(arg); zwarnnam(nam, "doubled argument definition: %s", *args, 0); return NULL; @@ -865,8 +1503,9 @@ parse_cadef(char *nam, char **args) ret->nopts = nopts; ret->ndopts = ndopts; ret->nodopts = nodopts; - - return ret; + set_cadef_opts(ret); + + return all; } /* Given an array of definitions, return the cadef for it. From the cache @@ -878,7 +1517,7 @@ get_cadef(char *nam, char **args) Cadef *p, *min, new; int i, na = arrlen(args); - for (i = MAX_CACACHE, p = cadef_cache, min = NULL; *p && i; p++, i--) + for (i = MAX_CACACHE, p = cadef_cache, min = NULL; i && *p; p++, i--) if (*p && na == (*p)->ndefs && arrcmp(args, (*p)->defs)) { (*p)->lastt = time(0); @@ -920,7 +1559,8 @@ ca_get_opt(Cadef d, char *line, int full, char **end) /* Return a pointer to the end of the option. */ int l = strlen(p->name); - if (p->type == CAO_EQUAL && line[l] == '=') + if ((p->type == CAO_OEQUAL || p->type == CAO_EQUAL) && + line[l] == '=') l++; *end = line + l; @@ -934,36 +1574,39 @@ ca_get_opt(Cadef d, char *line, int full, char **end) /* Same as above, only for single-letter-style. */ static Caopt -ca_get_sopt(Cadef d, char *line, int full, char **end) +ca_get_sopt(Cadef d, char *line, char **end, LinkList *lp) { - Caopt p; + Caopt p, pp = NULL; char pre = *line++; - - if (full) { - for (p = NULL; *line; line++) - if (!(p = d->single[STOUC(*line)]) || !p->active || - (line[1] && p->args)) - return NULL; - return p; - } else { - for (p = NULL; *line; line++) - if ((p = d->single[STOUC(*line)]) && p->active && - p->args && p->type != CAO_NEXT && p->name[0] == pre) { + LinkList l = NULL; + + *lp = NULL; + for (p = NULL; *line; line++) { + if ((p = d->single[STOUC(*line)]) && p->active && + p->args && p->name[0] == pre) { + if (p->type == CAO_NEXT) { + if (!l) + *lp = l = newlinklist(); + addlinknode(l, p); + } else { if (end) { line++; - if (p->type == CAO_EQUAL && *line == '=') + if ((p->type == CAO_OEQUAL || p->type == CAO_EQUAL) && + *line == '=') line++; *end = line; } + pp = p; break; - } else if (!p || !p->active || (line[1] && p->args) || - p->name[0] != pre) - return NULL; - if (p && end) - *end = line; - return p; + } + } else if (!p || (p && !p->active)) + return NULL; + pp = (p->name[0] == pre ? p : NULL); + p = NULL; } - return NULL; + if (pp && end) + *end = line; + return pp; } /* Return the n'th argument definition. */ @@ -974,10 +1617,12 @@ ca_get_arg(Cadef d, int n) if (d->argsactive) { Caarg a = d->args; - while (a && a->num < n) + while (a && (!a->active || n < a->min || n > a->num)) { + if (!a->active) + n++; a = a->next; - - if (a && a->num == n && a->active) + } + if (a && a->min <= n && a->num >= n && a->active) return a; return (d->rest && d->rest->active ? d->rest : NULL); @@ -987,42 +1632,88 @@ ca_get_arg(Cadef d, int n) /* Use a xor list, marking options as inactive. */ -static void -ca_inactive(Cadef d, char **xor) +static LinkList ca_xor; + +static int +ca_inactive(Cadef d, char **xor, int cur, int opts, char *optname) { - if (xor) { + if ((xor || opts) && cur <= compcurrent) { Caopt opt; - - for (; *xor; xor++) { - if (xor[0][0] == ':' && !xor[0][1]) - d->argsactive = 0; - else if (xor[0][0] == '*' && !xor[0][1]) { - if (d->rest) + char *x; + int sl = (d->set ? (int)strlen(d->set) : -1), set = 0; + + for (; (x = (opts ? "-" : *xor)); xor++) { + if (optname && optname[0] == x[0] && strcmp(optname, x)) + continue; + if (ca_xor) + addlinknode(ca_xor, x); + set = 0; + if (sl > 0) { + if (strpfx(d->set, x)) { + x += sl; + set = 1; + } else if (!strncmp(d->set, x, sl - 1)) { + Caopt p; + + for (p = d->opts; p; p = p->next) + if (p->set) + p->active = 0; + + x = ":"; + set = 1; + } + } + if (x[0] == ':' && !x[1]) { + if (set) { + Caarg a; + + for (a = d->args; a; a = a->next) + if (a->set) + a->active = 0; + if (d->rest && (!set || d->rest->set)) + d->rest->active = 0; + } else + d->argsactive = 0; + } else if (x[0] == '-' && !x[1]) { + Caopt p; + + for (p = d->opts; p; p = p->next) + if (!set || p->set) + p->active = 0; + } else if (x[0] == '*' && !x[1]) { + if (d->rest && (!set || d->rest->set)) d->rest->active = 0; - } else if (xor[0][0] >= '0' && xor[0][0] <= '9') { - int n = atoi(xor[0]); + } else if (x[0] >= '0' && x[0] <= '9') { + int n = atoi(x); Caarg a = d->args; while (a && a->num < n) a = a->next; - if (a && a->num == n) + if (a && a->num == n && (!set || a->set)) a->active = 0; - } else if ((opt = ca_get_opt(d, *xor, 1, NULL))) + } else if ((opt = ca_get_opt(d, x, 1, NULL)) && (!set || opt->set)) opt->active = 0; + + if (opts) + break; } } + return 0; } /* State when parsing a command line. */ +typedef struct castate *Castate; + struct castate { + Castate snext; Cadef d; int nopts; Caarg def, ddef; - Caopt curopt; - int opt, arg, argbeg, optbeg, nargbeg, restbeg, curpos; - int inopt, inrest, inarg, nth, doff, singles; + Caopt curopt, dopt; + int opt, arg, argbeg, optbeg, nargbeg, restbeg, curpos, argend; + int inopt, inrest, inarg, nth, doff, singles, oopt, actopts; LinkList args; LinkList *oargs; }; @@ -1030,30 +1721,72 @@ struct castate { static struct castate ca_laststate; static int ca_parsed = 0, ca_alloced = 0; -/* Pars a command line. */ - static void -ca_parse_line(Cadef d) +freecastate(Castate s) +{ + int i; + LinkList *p; + + freelinklist(s->args, freestr); + for (i = s->nopts, p = s->oargs; i--; p++) + if (*p) + freelinklist(*p, freestr); + zfree(s->oargs, s->d->nopts * sizeof(LinkList)); +} + +/* Return a copy of an option's argument, ignoring possible quoting + * in the option name. */ + +static char * +ca_opt_arg(Caopt opt, char *line) +{ + char *o = opt->name; + + while (1) { + if (*o == '\\') + o++; + if (*line == '\\' || *line == '\'' || *line == '"') + line++; + if (!*o || *o != *line) + break; + o++; + line++; + } + if (*line && (opt->type == CAO_EQUAL || opt->type == CAO_OEQUAL)) { + if (*line == '\\') + line++; + if (*line == '=') + line++; + } + return ztrdup(line); +} + +/* Parse a command line. */ + +static int +ca_parse_line(Cadef d, int multi, int first) { Caarg adef, ddef; - Caopt ptr, wasopt; + Caopt ptr, wasopt = NULL, dopt; struct castate state; - char *line, *pe, **argxor = NULL; - int cur, doff; - Patprog endpat = NULL; + char *line, *oline, *pe, **argxor = NULL; + int cur, doff, argend, arglast, ne; + Patprog endpat = NULL, napat = NULL; + LinkList sopts = NULL; /* Free old state. */ - if (ca_alloced) { - int i = ca_laststate.nopts; - LinkList *p = ca_laststate.oargs; - - freelinklist(ca_laststate.args, freestr); - while (i--) - if (*p++) - freelinklist(p[-1], freestr); + if (first && ca_alloced) { + Castate s = &ca_laststate, ss; + int f = 1; - zfree(ca_laststate.oargs, ca_laststate.d->nopts * sizeof(LinkList)); + while (s) { + ss = s->snext; + freecastate(s); + if (!f) + zfree(s, sizeof(*s)); + s = ss; + } } /* Mark everything as active. */ @@ -1067,13 +1800,15 @@ ca_parse_line(Cadef d) /* Default values for the state. */ + state.snext = NULL; state.d = d; state.nopts = d->nopts; state.def = state.ddef = NULL; - state.curopt = NULL; - state.argbeg = state.optbeg = state.nargbeg = state.restbeg = + state.curopt = state.dopt = NULL; + state.argbeg = state.optbeg = state.nargbeg = state.restbeg = state.actopts = state.nth = state.inopt = state.inarg = state.opt = state.arg = 1; - state.inrest = state.doff = state.singles = state.doff = 0; + state.argend = argend = arrlen(compwords) - 1; + state.inrest = state.doff = state.singles = state.doff = state.oopt = 0; state.curpos = compcurrent; state.args = znewlinklist(); state.oargs = (LinkList *) zalloc(d->nopts * sizeof(LinkList)); @@ -1086,25 +1821,44 @@ ca_parse_line(Cadef d) if (!compwords[1]) { ca_laststate.opt = ca_laststate.arg = 0; - return; + goto end; } + if (d->nonarg) + napat = patcompile(d->nonarg, 0, NULL); + /* Loop over the words from the line. */ for (line = compwords[1], cur = 2, state.curopt = NULL, state.def = NULL; line; line = compwords[cur++]) { ddef = adef = NULL; - doff = state.singles = 0; - - ca_inactive(d, argxor); - - /* We've a definition for an argument, skip to the next. */ + dopt = NULL; + doff = state.singles = arglast = 0; + + /* remove quotes */ + oline = line; + line = dupstring(line); + ne = noerrs; + noerrs = 2; + parse_subst_string(line); + noerrs = ne; + remnulargs(line); + untokenize(line); + + if (ca_inactive(d, argxor, cur, 0, NULL) || + ((d->flags & CDF_SEP) && cur != compcurrent && !strcmp(line, "--"))) { + if (ca_inactive(d, NULL, cur, 1, NULL)) + return 1; + continue; + } + /* We've got a definition for an argument, skip to the next. */ if (state.def) { state.arg = 0; if (state.curopt) - zaddlinknode(state.oargs[state.curopt->num], ztrdup(line)); + zaddlinknode(state.oargs[state.curopt->num], ztrdup(oline)); - state.opt = (state.def->type == CAA_OPT); + if ((state.opt = (state.def->type == CAA_OPT)) && state.def->opt) + state.oopt++; if (state.def->type == CAA_REST || state.def->type == CAA_RARGS || state.def->type == CAA_RREST) { @@ -1112,11 +1866,24 @@ ca_parse_line(Cadef d) state.def = NULL; state.curopt = NULL; state.opt = state.arg = 1; - continue; + state.argend = ca_laststate.argend = cur - 1; + goto cont; } - } else if ((state.def = state.def->next)) + } else if ((state.def = state.def->next)) { state.argbeg = cur; - else { + state.argend = argend; + } else if (sopts && nonempty(sopts)) { + state.curopt = (Caopt) uremnode(sopts, firstnode(sopts)); + state.def = state.curopt->args; + state.opt = 0; + state.argbeg = state.optbeg = state.inopt = cur; + state.argend = argend; + doff = state.doff = 0; + state.singles = 1; + if (!state.oargs[state.curopt->num]) + state.oargs[state.curopt->num] = znewlinklist(); + goto cont; + } else { state.curopt = NULL; state.opt = 1; } @@ -1134,32 +1901,44 @@ ca_parse_line(Cadef d) /* See if it's an option. */ if (state.opt == 2 && (state.curopt = ca_get_opt(d, line, 0, &pe)) && - (state.curopt->type != CAO_EQUAL || - compwords[cur] || pe[-1] == '=')) { + (state.curopt->type == CAO_OEQUAL ? + (compwords[cur] || pe[-1] == '=') : + (state.curopt->type == CAO_EQUAL ? + (pe[-1] == '=' || !pe[0]) : 1))) { + + if ((ddef = state.def = ((state.curopt->type != CAO_EQUAL || + pe[-1] == '=') ? + state.curopt->args : NULL))) + dopt = state.curopt; - ddef = state.def = state.curopt->args; doff = pe - line; state.optbeg = state.argbeg = state.inopt = cur; + state.argend = argend; state.singles = (d->single && (!pe || !*pe) && state.curopt->name[1] && !state.curopt->name[2]); - state.oargs[state.curopt->num] = znewlinklist(); + if (!state.oargs[state.curopt->num]) + state.oargs[state.curopt->num] = znewlinklist(); - ca_inactive(d, state.curopt->xor); + if (ca_inactive(d, state.curopt->xor, cur, 0, + (cur == compcurrent ? state.curopt->name : NULL))) + return 1; /* Collect the argument strings. Maybe. */ if (state.def && (state.curopt->type == CAO_DIRECT || + state.curopt->type == CAO_EQUAL || (state.curopt->type == CAO_ODIRECT && pe[0]) || - (state.curopt->type == CAO_EQUAL && + (state.curopt->type == CAO_OEQUAL && (pe[0] || pe[-1] == '=')))) { if (state.def->type != CAA_REST && state.def->type != CAA_RARGS && state.def->type != CAA_RREST) state.def = state.def->next; - zaddlinknode(state.oargs[state.curopt->num], ztrdup(pe)); + zaddlinknode(state.oargs[state.curopt->num], + ca_opt_arg(state.curopt, oline)); } if (state.def) state.opt = 0; @@ -1169,63 +1948,104 @@ ca_parse_line(Cadef d) state.curopt = NULL; } } else if (state.opt == 2 && d->single && - (state.curopt = ca_get_sopt(d, line, 0, &pe))) { + ((state.curopt = ca_get_sopt(d, line, &pe, &sopts)) || + (cur != compcurrent && sopts && nonempty(sopts)))) { /* Or maybe it's a single-letter option? */ char *p; Caopt tmpopt; - ddef = state.def = state.curopt->args; + if (cur != compcurrent && sopts && nonempty(sopts)) + state.curopt = (Caopt) uremnode(sopts, firstnode(sopts)); + + if (!state.oargs[state.curopt->num]) + state.oargs[state.curopt->num] = znewlinklist(); + + state.def = state.curopt->args; + ddef = (state.curopt->type == CAO_NEXT && cur == compcurrent ? + NULL : state.def); + dopt = state.curopt; doff = pe - line; state.optbeg = state.argbeg = state.inopt = cur; + state.argend = argend; state.singles = (!pe || !*pe); for (p = line + 1; p < pe; p++) { if ((tmpopt = d->single[STOUC(*p)])) { - state.oargs[tmpopt->num] = znewlinklist(); + if (!state.oargs[tmpopt->num]) + state.oargs[tmpopt->num] = znewlinklist(); - ca_inactive(d, tmpopt->xor); + if (ca_inactive(d, tmpopt->xor, cur, 0, + (cur == compcurrent ? tmpopt->name : NULL))) + return 1; } } if (state.def && (state.curopt->type == CAO_DIRECT || + state.curopt->type == CAO_EQUAL || (state.curopt->type == CAO_ODIRECT && pe[0]) || - (state.curopt->type == CAO_EQUAL && + (state.curopt->type == CAO_OEQUAL && (pe[0] || pe[-1] == '=')))) { if (state.def->type != CAA_REST && state.def->type != CAA_RARGS && state.def->type != CAA_RREST) state.def = state.def->next; - zaddlinknode(state.oargs[state.curopt->num], ztrdup(pe)); + zaddlinknode(state.oargs[state.curopt->num], + ca_opt_arg(state.curopt, line)); } if (state.def) state.opt = 0; else state.curopt = NULL; - } else if (state.arg) { + } else if (multi && (*line == '-' || *line == '+') && cur != compcurrent +#if 0 + /**** Ouch. Using this will disable the mutual exclusion + of different sets. Not using it will make the -A + pattern be effectively ignored with multiple sets. */ + && (!napat || !pattry(napat, line)) +#endif + ) + return 1; + else if (state.arg && (!napat || !pattry(napat, line))) { /* Otherwise it's a normal argument. */ + if (napat && ca_inactive(d, NULL, cur + 1, 1, NULL)) + return 1; + + arglast = 1; if (state.inopt) { state.inopt = 0; state.nargbeg = cur - 1; + state.argend = argend; + } + if (!d->args && !d->rest && *line && *line != '-' && *line != '+') { + if (!multi && cur > compcurrent) + break; + return 1; } if ((adef = state.def = ca_get_arg(d, state.nth)) && (state.def->type == CAA_RREST || state.def->type == CAA_RARGS)) { state.inrest = 0; - state.opt = (cur == state.nargbeg + 1); + state.opt = (cur == state.nargbeg + 1 && + (!multi || !*line || + *line == '-' || *line == '+')); state.optbeg = state.nargbeg; state.argbeg = cur - 1; + state.argend = argend; for (; line; line = compwords[cur++]) zaddlinknode(state.args, ztrdup(line)); memcpy(&ca_laststate, &state, sizeof(state)); ca_laststate.ddef = NULL; + ca_laststate.dopt = NULL; ca_laststate.doff = 0; break; } zaddlinknode(state.args, ztrdup(line)); + if (adef) + state.oopt = adef->num - state.nth; if (state.def) argxor = state.def->xor; @@ -1256,6 +2076,7 @@ ca_parse_line(Cadef d) zaddlinknode(l, ztrdup(line)); ca_laststate.ddef = NULL; + ca_laststate.dopt = NULL; ca_laststate.doff = 0; break; } @@ -1264,15 +2085,19 @@ ca_parse_line(Cadef d) /* Copy the state into the global one. */ + cont: + if (cur + 1 == compcurrent) { memcpy(&ca_laststate, &state, sizeof(state)); ca_laststate.ddef = NULL; + ca_laststate.dopt = NULL; ca_laststate.doff = 0; } else if (cur == compcurrent && !ca_laststate.def) { if ((ca_laststate.def = ddef)) { ca_laststate.singles = state.singles; if (state.curopt && state.curopt->type == CAO_NEXT) { ca_laststate.ddef = ddef; + ca_laststate.dopt = dopt; ca_laststate.def = NULL; ca_laststate.opt = 1; state.curopt->active = 1; @@ -1282,15 +2107,28 @@ ca_parse_line(Cadef d) } } else { ca_laststate.def = adef; + ca_laststate.opt = (!arglast || !multi || !*line || + *line == '-' || *line == '+'); ca_laststate.ddef = NULL; + ca_laststate.dopt = NULL; ca_laststate.optbeg = state.nargbeg; ca_laststate.argbeg = state.restbeg; + ca_laststate.argend = state.argend; ca_laststate.singles = state.singles; + ca_laststate.oopt = state.oopt; if (wasopt) wasopt->active = 1; } } } + end: + + ca_laststate.actopts = 0; + for (ptr = d->opts; ptr; ptr = ptr->next) + if (ptr->active) + ca_laststate.actopts++; + + return 0; } /* Build a colon-list from a list. */ @@ -1327,27 +2165,120 @@ ca_colonlist(LinkList l) return ztrdup(""); } -static int -bin_comparguments(char *nam, char **args, char *ops, int func) +static void +ca_set_data(LinkList descr, LinkList act, LinkList subc, + char *opt, Caarg arg, int single) { - int min, max, n; + LinkNode dnode, anode; + char nbuf[40], *buf; + int restr = 0, onum, miss = 0, rest, oopt = 1, lopt = 0, addopt; - if (incompfunc != 1) { - zwarnnam(nam, "can only be called from completion function", NULL, 0); - return 1; + rec: + + addopt = (opt ? 0 : ca_laststate.oopt); + + for (; arg && (opt || (arg->num < 0 || + (arg->min <= ca_laststate.nth + addopt && + arg->num >= ca_laststate.nth)));) { + lopt = (arg->type == CAA_OPT); + if (!opt && !lopt && oopt > 0) + oopt = 0; + + for (dnode = firstnode(descr), anode = firstnode(act); + dnode; incnode(dnode), incnode(anode)) + if (!strcmp((char *) getdata(dnode), arg->descr) && + !strcmp((char *) getdata(anode), arg->action)) + break; + + if (!dnode) { + addlinknode(descr, arg->descr); + addlinknode(act, arg->action); + + if (!restr) { + if ((restr = (arg->type == CAA_RARGS))) + restrict_range(ca_laststate.optbeg, ca_laststate.argend); + else if ((restr = (arg->type == CAA_RREST))) + restrict_range(ca_laststate.argbeg, ca_laststate.argend); + } + if (arg->opt) { + buf = (char *) zhalloc((arg->set ? strlen(arg->set) : 0) + + strlen(arg->opt) + 40); + if (arg->num > 0 && arg->type < CAA_REST) + sprintf(buf, "%soption%s-%d", + (arg->set ? arg->set : ""), arg->opt, arg->num); + else + sprintf(buf, "%soption%s-rest", + (arg->set ? arg->set : ""), arg->opt); + } else if (arg->num > 0) { + sprintf(nbuf, "argument-%d", arg->num); + buf = (arg->set ? dyncat(arg->set, nbuf) : dupstring(nbuf)); + } else + buf = (arg->set ? dyncat(arg->set, "argument-rest") : + dupstring("argument-rest")); + + addlinknode(subc, buf); + } + if (single) + break; + + if (!opt) { + if (arg->num >= 0 && !arg->next && miss) + arg = (ca_laststate.d->rest && ca_laststate.d->rest->active ? + ca_laststate.d->rest : NULL); + else { + onum = arg->num; + rest = (onum != arg->min && onum == ca_laststate.nth); + if ((arg = arg->next)) { + if (arg->num != onum + 1) + miss = 1; + } else if (rest || (oopt > 0 && !opt)) { + arg = (ca_laststate.d->rest && ca_laststate.d->rest->active ? + ca_laststate.d->rest : NULL); + oopt = -1; + } + } + } else { + if (!lopt) + break; + arg = arg->next; + } } - if (args[0][0] != '-' || !args[0][1] || args[0][2]) { + if (!single && opt && (lopt || ca_laststate.oopt)) { + opt = NULL; + arg = ca_get_arg(ca_laststate.d, ca_laststate.nth); + + goto rec; + } + if (!opt && oopt > 0) { + oopt = -1; + arg = (ca_laststate.d->rest && ca_laststate.d->rest->active ? + ca_laststate.d->rest : NULL); + + goto rec; + } +} + +static int +bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) +{ + int min, max, n; + Castate lstate = &ca_laststate; + + if (incompfunc != 1) { + zwarnnam(nam, "can only be called from completion function", NULL, 0); + return 1; + } + if (args[0][0] != '-' || !args[0][1] || args[0][2]) { zwarnnam(nam, "invalid argument: %s", args[0], 0); return 1; } - if (args[0][1] != 'i' && !ca_parsed) { + if (args[0][1] != 'i' && args[0][1] != 'I' && !ca_parsed) { zwarnnam(nam, "no parsed state", NULL, 0); return 1; } switch (args[0][1]) { case 'i': min = 2; max = -1; break; - case 'D': min = 2; max = 2; break; - case 'C': min = 1; max = 1; break; + case 'D': min = 3; max = 3; break; case 'O': min = 4; max = 4; break; case 'L': min = 3; max = 4; break; case 's': min = 1; max = 1; break; @@ -1368,169 +2299,281 @@ bin_comparguments(char *nam, char **args, char *ops, int func) } switch (args[0][1]) { case 'i': + /* This initialises the internal data structures. Arguments are the + * auto-description string, the optional -s, -S, -A and -M options + * given to _arguments and the specs. */ if (compcurrent > 1 && compwords[0]) { - Cadef def = get_cadef(nam, args + 1); - int cap = ca_parsed; + Cadef def; + int cap = ca_parsed, multi, first = 1, use, ret = 0; + LinkList cax = ca_xor, nx; + LinkNode node; + Castate states = NULL, sp; + char *xor[2]; ca_parsed = 0; + xor[1] = NULL; - if (!def) + if (!(def = get_cadef(nam, args + 1))) return 1; + multi = !!def->snext; ca_parsed = cap; - ca_parse_line(def); + ca_xor = (multi ? newlinklist() : NULL); + + while (def) { + use = !ca_parse_line(def, multi, first); + nx = ca_xor; + ca_xor = NULL; + while ((def = def->snext)) { + if (nx) { + for (node = firstnode(nx); node; incnode(node)) { + xor[0] = (char *) getdata(node); + if (!strcmp(xor[0], def->sname) || + ca_inactive(def, xor, compcurrent, 0, NULL)) + break; + } + if (!node) + break; + } + } + ca_xor = nx; + if (use && def) { + sp = (Castate) zalloc(sizeof(*sp)); + memcpy(sp, &ca_laststate, sizeof(*sp)); + sp->snext = states; + states = sp; + } else if (!use && !def) { + if (states) { + freecastate(&ca_laststate); + memcpy(&ca_laststate, states, sizeof(*sp)); + sp = states->snext; + zfree(states, sizeof(*states)); + states = sp; + } else + ret = 1; + } + first = 0; + } + ca_xor = cax; ca_parsed = 1; + ca_laststate.snext = states; - return 0; + return ret; } return 1; case 'D': + /* This returns the descriptions, actions and sub-contexts for the + * things _arguments has to execute at this place on the line (the + * sub-contexts are used as tags). + * The return value is particularly important here, it says if + * there are arguments to completely at all. */ { - Caarg arg = ca_laststate.def; - - if (arg) { - setsparam(args[1], ztrdup(arg->descr)); - setsparam(args[2], ztrdup(arg->action)); - - if (ca_laststate.doff > 0) - ignore_prefix(ca_laststate.doff); - if (arg->type == CAA_RARGS) - restrict_range(ca_laststate.optbeg, - arrlen(compwords) - 1); - else if (arg->type == CAA_RREST) - restrict_range(ca_laststate.argbeg, - arrlen(compwords) - 1); - return 0; + LinkList descr, act, subc; + Caarg arg; + int ign = 0, ret = 1; + + descr = newlinklist(); + act = newlinklist(); + subc = newlinklist(); + + while (lstate) { + arg = lstate->def; + + if (arg) { + ret = 0; + if (!ign && lstate->doff > 0) { + ign = 1; + ignore_prefix(lstate->doff); + } + ca_set_data(descr, act, subc, arg->opt, arg, + (lstate->doff > 0)); + } + lstate = lstate->snext; } - return 1; - } - case 'C': - { - Caarg arg = ca_laststate.def; - - if (arg) { - char buf[20]; - - if (arg->num > 0) - sprintf(buf, "%d", arg->num); - else - strcpy(buf, "rest"); - - setsparam(args[1], (arg->opt ? tricat(arg->opt, "-", buf) : - tricat("argument-", buf, ""))); - return 0; + if (!ret) { + set_list_array(args[1], descr); + set_list_array(args[2], act); + set_list_array(args[3], subc); } - return 1; + return ret; } case 'O': - if ((ca_laststate.opt || (ca_laststate.doff && ca_laststate.def) || - (ca_laststate.def && - (ca_laststate.def->type == CAA_OPT || - ca_laststate.def->type >= CAA_RARGS))) && - (!ca_laststate.def || ca_laststate.def->type < CAA_RARGS || - (ca_laststate.def->type == CAA_RARGS ? - (ca_laststate.curpos == ca_laststate.argbeg + 1) : - (compcurrent == 1)))) { + /* This returns the descriptions for the options in the arrays whose + * names are given as arguments. The descriptions are strings in a + * form usable by _describe. The return value says if there are any + * options to be completed. */ + { LinkList next = newlinklist(); LinkList direct = newlinklist(); LinkList odirect = newlinklist(); LinkList equal = newlinklist(), l; + LinkNode node; Caopt p; char *str; + int ret = 1; + + for (; lstate; lstate = lstate->snext) { + if (lstate->actopts && + (lstate->opt || (lstate->doff && lstate->def) || + (lstate->def && lstate->def->opt && + (lstate->def->type == CAA_OPT || + (lstate->def->type >= CAA_RARGS && + lstate->def->num < 0)))) && + (!lstate->def || lstate->def->type < CAA_RARGS || + (lstate->def->type == CAA_RARGS ? + (lstate->curpos == lstate->argbeg + 1) : + (compcurrent == 1)))) { + ret = 0; + for (p = lstate->d->opts; p; p = p->next) { + if (p->active && !p->not) { + switch (p->type) { + case CAO_NEXT: l = next; break; + case CAO_DIRECT: l = direct; break; + case CAO_ODIRECT: l = odirect; break; + default: l = equal; break; + } + if (p->descr) { + char *n = bslashcolon(p->name); + int len = strlen(n) + strlen(p->descr) + 2; + + str = (char *) zhalloc(len); + strcpy(str, n); + strcat(str, ":"); + strcat(str, p->descr); + } else + str = bslashcolon(p->name); - for (p = ca_laststate.d->opts; p; p = p->next) { - if (p->active) { - switch (p->type) { - case CAO_NEXT: l = next; break; - case CAO_DIRECT: l = direct; break; - case CAO_ODIRECT: l = odirect; break; - default: l = equal; break; - } - if (p->descr) { - char *n = bslashcolon(p->name); - int len = strlen(n) + strlen(p->descr) + 2; + for (node = firstnode(l); node; incnode(node)) + if (!strcmp(str, (char *) getdata(node))) + break; - str = (char *) zhalloc(len); - strcpy(str, n); - strcat(str, ":"); - strcat(str, p->descr); - } else - str = bslashcolon(p->name); - addlinknode(l, str); + if (!node) + addlinknode(l, str); + } + } } } - set_list_array(args[1], next); - set_list_array(args[2], direct); - set_list_array(args[3], odirect); - set_list_array(args[4], equal); + if (!ret) { + set_list_array(args[1], next); + set_list_array(args[2], direct); + set_list_array(args[3], odirect); + set_list_array(args[4], equal); - return 0; + return 0; + } + return (ca_laststate.singles ? 2 : 1); } - return 1; case 'L': + /* This tests if the beginning of the current word matches an option. + * It is for cases like `./configure --pre=/' which should + * complete to `--prefix=/...'. The options name isn't fully typed + * and _arguments finds out that there is no option `--pre' and that + * it should complete some argument to an option. It then uses -L + * to find the option the argument is for. */ { - Caopt opt = ca_get_opt(ca_laststate.d, args[1], 1, NULL); + LinkList descr, act, subc; + Caopt opt; + int ret = 1; - if (opt && opt->args) { - setsparam(args[2], ztrdup(opt->args->descr)); - setsparam(args[3], ztrdup(opt->args->action)); + descr = newlinklist(); + act = newlinklist(); + subc = newlinklist(); - if (args[4]) - setsparam(args[4], tricat(opt->name, "-1", "")); + while (lstate) { + opt = ca_get_opt(lstate->d, args[1], 1, NULL); - return 0; + if (opt && opt->args) { + ret = 0; + ca_set_data(descr, act, subc, opt->name, opt->args, 1); + } + lstate = lstate->snext; } - return 1; + if (!ret) { + set_list_array(args[2], descr); + set_list_array(args[3], act); + set_list_array(args[4], subc); + } + return ret; } case 's': - if (ca_laststate.d->single && ca_laststate.singles && - ca_laststate.opt) { - setsparam(args[1], - ztrdup(ca_laststate.ddef ? - (ca_laststate.ddef->type == CAO_DIRECT ? - "direct" : - (ca_laststate.ddef->type == CAO_EQUAL ? - "equal" : "next")) : "")); - return 0; - } + /* This returns zero if we are completing single letter options. + * It also uses its argument as the name of a parameter and sets + * that to a string describing the argument behaviour of the last + * option in the current word so that we can get the auto-suffix + * right. */ + for (; lstate; lstate = lstate->snext) + if (lstate->d->single && lstate->singles && + lstate->actopts +#if 0 + /* let's try without, for the -W option of _arguments */ + && lstate->opt +#endif + ) { + setsparam(args[1], + ztrdup((lstate->ddef && lstate->dopt) ? + (lstate->dopt->type == CAO_DIRECT ? + "direct" : + ((lstate->dopt->type == CAO_OEQUAL || + lstate->dopt->type == CAO_EQUAL) ? + "equal" : "next")) : "")); + return 0; + } return 1; case 'M': + /* This returns the match specs defined for the set of specs we are + * using. Returned, as usual in a parameter whose name is given as + * the argument. */ setsparam(args[1], ztrdup(ca_laststate.d->match)); return 0; case 'a': - return !(ca_laststate.d->args || ca_laststate.d->rest); + /* This just sets the return value. To zero if there would be or + * were any normal arguments to be completed. Used to decide if + * _arguments should say `no arguments' or `no more arguments'. */ + for (; lstate; lstate = lstate->snext) + if (lstate->d->args || lstate->d->rest) + return 0; + return 1; case 'W': + /* This gets two parameter names as arguments. The first is set to + * the current word sans any option prefixes handled by comparguments. + * The second parameter is set to an array containing the options on + * the line and their arguments. I.e. the stuff _arguments returns + * to its caller in the `line' and `opt_args' parameters. */ { + Castate s; char **ret, **p; LinkNode n; LinkList *a; Caopt o; int num; - ret = p = zalloc((countlinknodes(ca_laststate.args) + 1) * - sizeof(char *)); + for (num = 0, s = lstate; s; s = s->snext) + num += countlinknodes(s->args); - for (n = firstnode(ca_laststate.args); n; incnode(n)) - *p++ = ztrdup((char *) getdata(n)); + ret = p = zalloc((num + 1) * sizeof(char *)); + + for (s = lstate; s; s = s->snext) + for (n = firstnode(s->args); n; incnode(n)) + *p++ = ztrdup((char *) getdata(n)); *p = NULL; setaparam(args[1], ret); - for (num = 0, o = ca_laststate.d->opts, a = ca_laststate.oargs; o; - o = o->next, a++) - if (*a) - num += 2; + for (num = 0, s = lstate; s; s = s->snext) + for (o = s->d->opts, a = s->oargs; o; o = o->next, a++) + if (*a) + num += 2; ret = p = zalloc((num + 1) * sizeof(char *)); - for (o = ca_laststate.d->opts, a = ca_laststate.oargs; o; - o = o->next, a++) { - if (*a) { - *p++ = ztrdup(o->name); - *p++ = ca_colonlist(*a); - } - } + for (s = lstate; s; s = s->snext) + for (o = s->d->opts, a = s->oargs; o; o = o->next, a++) + if (*a) { + *p++ = (o->set ? tricat(o->set, o->name, "") : + ztrdup(o->name)); + *p++ = ca_colonlist(*a); + } *p = NULL; sethparam(args[2], ret); @@ -1551,11 +2594,13 @@ struct cvdef { char *descr; /* global description */ int hassep; /* multiple values allowed */ char sep; /* separator character */ + char argsep; /* argument separator */ Cvdef next; /* next in cache */ Cvval vals; /* value definitions */ char **defs; /* original strings */ int ndefs; /* number of ... */ int lastt; /* last time used */ + int words; /* if to look at other words */ }; /* One value definition. */ @@ -1612,17 +2657,25 @@ parse_cvdef(char *nam, char **args) Cvdef ret; Cvval val, *valp; Caarg arg; - char **oargs = args, sep = '\0', *name, *descr, *p, *q, **xor, c; - int xnum, multi, vtype, hassep = 0; - - if (args[0][0] == '-' && args[0][1] == 's' && !args[0][2]) { - if (args[1][0] && args[1][1]) { - zwarnnam(nam, "invalid separator: %s", args[1], 0); - return NULL; - } - hassep = 1; - sep = args[1][0]; - args += 2; + char **oargs = args, sep = '\0', asep = '=', *name, *descr, *p, *q, **xor, c; + int xnum, multi, vtype, hassep = 0, words = 0; + + while (args && args[0] && args[1] && + args[0][0] == '-' && + (args[0][1] == 's' || args[0][1] == 'S' || args[0][1] == 'w') && + !args[0][2]) { + + if (args[0][1] == 's') { + hassep = 1; + sep = args[1][0]; + args += 2; + } else if (args[0][1] == 'S') { + asep = args[1][0]; + args += 2; + } else { + words = 1; + args++; + } } if (!args[0] || !args[1]) { zwarnnam(nam, "not enough arguments", NULL, 0); @@ -1634,13 +2687,16 @@ parse_cvdef(char *nam, char **args) ret->descr = ztrdup(descr); ret->hassep = hassep; ret->sep = sep; + ret->argsep = asep; ret->next = NULL; ret->vals = NULL; ret->defs = zarrdup(oargs); ret->ndefs = arrlen(oargs); ret->lastt = time(0); + ret->words = words; for (valp = &(ret->vals); *args; args++) { + int bs = 0; p = dupstring(*args); xnum = 0; @@ -1688,9 +2744,9 @@ parse_cvdef(char *nam, char **args) for (name = p; *p && *p != ':' && *p != '['; p++) if (*p == '\\' && p[1]) - p++; + p++, bs = 1; - if (hassep && !sep && name + 1 != p) { + if (hassep && !sep && name + bs + 1 < p) { freecvdef(ret); zwarnnam(nam, "no multi-letter values with empty separator allowed", NULL, 0); return NULL; @@ -1739,7 +2795,7 @@ parse_cvdef(char *nam, char **args) vtype = CVV_OPT; } else vtype = CVV_ARG; - arg = parse_caarg(0, 0, 0, name, &p); + arg = parse_caarg(0, 0, 0, 0, name, &p, NULL); } else { vtype = CVV_NOARG; arg = NULL; @@ -1795,6 +2851,23 @@ cv_get_val(Cvdef d, char *name) return NULL; } +static Cvval +cv_quote_get_val(Cvdef d, char *name) +{ + int ne; + + /* remove quotes */ + name = dupstring(name); + ne = noerrs; + noerrs = 2; + parse_subst_string(name); + noerrs = ne; + remnulargs(name); + untokenize(name); + + return cv_get_val(d, name); +} + /* Handle a xor list. */ static void @@ -1821,20 +2894,122 @@ struct cvstate { static struct cvstate cv_laststate; static int cv_parsed = 0, cv_alloced = 0; +/* Get the next value in the string. Return it's definition and update the + * sp pointer to point to the end of the value (plus argument, if any). + * If there is no next value, the string pointer is set to null. In any + * case ap will point to the beginning of the argument or will be a null + * pointer if there is no argument. + */ + +static Cvval +cv_next(Cvdef d, char **sp, char **ap) +{ + Cvval r = NULL; + char *s = *sp; + + if (!*s) { + *sp = *ap = NULL; + + return NULL; + } + if ((d->hassep && !d->sep) || !d->argsep) { + char sav, ec, *v = s, *os; + + ec = ((d->hassep && d->sep) ? d->sep : d->argsep); + + do { + sav = *++s; + *s = '\0'; + if ((r = cv_quote_get_val(d, v))) { + *s = sav; + + break; + } + *s = sav; + } while (*s && *s != ec); + + os = s; + + if (d->hassep && d->sep) { + if ((s = strchr(s, d->sep))) + *sp = s + 1; + else + *sp = NULL; + } else + *sp = s; + if (d->argsep && *os == d->argsep) { + *ap = os + 1; + *sp = NULL; + } else if (r && r->type != CVV_NOARG) + *ap = os; + else + *ap = NULL; + + return r; + + } else if (d->hassep) { + char *ns = strchr(s, d->sep), *as = 0, *sap, sav = 0; + int skip = 0; + + if (d->argsep && (as = strchr(s, d->argsep)) && (!ns || as <= ns)) { + *ap = as + 1; + ns = strchr(as + 1, d->sep); + skip = 1; + sap = as; + } else { + *ap = NULL; + sap = ns; + } + if (sap) { + sav = *sap; + *sap = '\0'; + } + if ((!(r = cv_quote_get_val(d, s)) || r->type == CVV_NOARG) && skip) + ns = as; + + if (sap) + *sap = sav; + + *sp = ((!ns || (ns == as && r && r->type != CVV_NOARG)) ? NULL : ns + 1); + + return r; + } else { + char *as = strchr(s, d->argsep), *sap, sav = 0; + + *sp = NULL; + + if (as) { + *ap = as + 1; + sap = as; + sav = *as; + *sap = '\0'; + } else + *ap = sap = NULL; + + r = cv_quote_get_val(d, s); + + if (sap) + *sap = sav; + + return r; + } +} + /* Parse the current word. */ static void cv_parse_word(Cvdef d) { - Cvval ptr; + Cvval val; struct cvstate state; - char *str, *eq; + char *str, *arg = NULL, *pign = compprefix; + int nosfx = 0; if (cv_alloced) freelinklist(cv_laststate.vals, freestr); - for (ptr = d->vals; ptr; ptr = ptr->next) - ptr->active = 1; + for (val = d->vals; val; val = val->next) + val->active = 1; state.d = d; state.def = NULL; @@ -1843,108 +3018,126 @@ cv_parse_word(Cvdef d) cv_alloced = 1; - if (d->hassep) { - if (d->sep) { - char *end; - int heq; - - for (str = compprefix, end = strchr(str, d->sep); end;) { - *end = '\0'; - - if ((heq = !!(eq = strchr(str, '=')))) - *eq++ = '\0'; - else - eq = ""; - - if ((ptr = cv_get_val(d, str))) { - zaddlinknode(state.vals, ztrdup(str)); - zaddlinknode(state.vals, ztrdup(eq)); - - cv_inactive(d, ptr->xor); - } - if (heq) - eq[-1] = '='; - - *end = d->sep; - str = end + 1; - end = strchr(str, d->sep); - } - ignore_prefix(str - compprefix); - - if ((str = strchr(compsuffix, d->sep))) { - char *beg = str; - - for (str++; str; str = end) { - if ((end = strchr(str, d->sep))) - *end = '\0'; - - if ((heq = !!(eq = strchr(str, '=')))) - *eq++ = '\0'; - else - eq = ""; - - if ((ptr = cv_get_val(d, str))) { - zaddlinknode(state.vals, ztrdup(str)); - zaddlinknode(state.vals, ztrdup(eq)); - - cv_inactive(d, ptr->xor); - } - if (heq) - eq[-1] = '='; - if (end) - *end++ = d->sep; - } - ignore_suffix(strlen(beg)); - } - } else { - char tmp[2]; - - tmp[1] = '\0'; - - for (str = compprefix; *str; str++) { - tmp[0] = *str; - if ((ptr = cv_get_val(d, tmp))) { - zaddlinknode(state.vals, ztrdup(tmp)); - zaddlinknode(state.vals, ztrdup("")); - - cv_inactive(d, ptr->xor); - } - } - for (str = compsuffix; *str; str++) { - tmp[0] = *str; - if ((ptr = cv_get_val(d, tmp))) { - zaddlinknode(state.vals, ztrdup(tmp)); - zaddlinknode(state.vals, ztrdup("")); - - cv_inactive(d, ptr->xor); - } - } - ignore_prefix(strlen(compprefix)); - ignore_suffix(strlen(compsuffix)); - } + if (d->words && compwords[0]) { + int i; + + for (i = 1; compwords[i]; i++) + if (i != compcurrent - 1) + for (str = compwords[i]; str && *str; ) { + if ((val = cv_next(d, &str, &arg))) { + zaddlinknode(state.vals, ztrdup(val->name)); + if (arg) { + char sav = '\0'; + + if (str) { + sav = str[-1]; + str[-1] = '\0'; + } + zaddlinknode(state.vals, ztrdup(arg)); + if (str) + str[-1] = sav; + } else + zaddlinknode(state.vals, ztrdup("")); + + if (i + 1 < compcurrent) + cv_inactive(d, val->xor); + } + } + + val = NULL; + arg = NULL; } - str = tricat(compprefix, compsuffix, ""); - zsfree(compprefix); - zsfree(compsuffix); - compprefix = str; - compsuffix = ztrdup(""); - - if ((eq = strchr(str, '='))) { - *eq++ = '\0'; - - if ((ptr = cv_get_val(d, str)) && ptr->type != CVV_NOARG) { - eq[-1] = '='; - ignore_prefix(eq - str); - state.def = ptr->arg; - state.val = ptr; - } else - eq[-1] = '='; + for (str = compprefix; str && *str; ) { + if ((val = cv_next(d, &str, &arg))) { + zaddlinknode(state.vals, ztrdup(val->name)); + if (arg) { + if (str) { + char sav = str[-1]; + + str[-1] = '\0'; + zaddlinknode(state.vals, ztrdup(arg)); + str[-1] = sav; + } else { + zaddlinknode(state.vals, tricat(arg, compsuffix, "")); + nosfx = 1; + } + } else + zaddlinknode(state.vals, ztrdup("")); + + cv_inactive(d, val->xor); + + if (str) + pign = str; + else + val->active = 1; + } } + state.val = val; + if (val && arg && !str) + state.def = val->arg; + + if (!nosfx && d->hassep) { + int ign = 0; + char *more = NULL; + + ignore_prefix(pign - compprefix); + + if (!d->sep && (!val || val->type == CVV_NOARG)) { + ign = strlen(compsuffix); + more = compsuffix; + } else { + if (d->sep) { + char *ns = strchr(compsuffix, d->sep), *as; + + if (d->argsep && (as = strchr(compsuffix, d->argsep)) && + (!ns || as <= ns)) { + ign = strlen(as); + } else + ign = (ns ? strlen(ns) : 0); + + more = (ns ? ns + 1 : NULL); + } else if (d->argsep) { + char *as; + + if ((as = strchr(compsuffix, d->argsep))) + ign = strlen(as); + } + } + more = dupstring(more); + + if (ign) + ignore_suffix(ign); + + while (more && *more) { + if ((val = cv_next(d, &more, &arg))) { + zaddlinknode(state.vals, ztrdup(val->name)); + if (arg) { + if (more) { + char sav = more[-1]; + + more[-1] = '\0'; + zaddlinknode(state.vals, ztrdup(arg)); + more[-1] = sav; + } else { + zaddlinknode(state.vals, tricat(arg, compsuffix, "")); + nosfx = 1; + } + } else + zaddlinknode(state.vals, ztrdup("")); + + cv_inactive(d, val->xor); + } + } + } else if (arg) + ignore_prefix(arg - compprefix); + else + ignore_prefix(pign - compprefix); + memcpy(&cv_laststate, &state, sizeof(state)); } static int -bin_compvalues(char *nam, char **args, char *ops, int func) +bin_compvalues(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) { int min, max, n; @@ -1966,6 +3159,7 @@ bin_compvalues(char *nam, char **args, char *ops, int func) case 'C': min = 1; max = 1; break; case 'V': min = 3; max = 3; break; case 's': min = 1; max = 1; break; + case 'S': min = 1; max = 1; break; case 'd': min = 1; max = 1; break; case 'L': min = 3; max = 4; break; case 'v': min = 1; max = 1; break; @@ -1983,6 +3177,8 @@ bin_compvalues(char *nam, char **args, char *ops, int func) } switch (args[0][1]) { case 'i': + /* This initialises the internal data structures. The arguments are + * just the arguments that were given to _values itself. */ { Cvdef def = get_cvdef(nam, args + 1); int cvp = cv_parsed; @@ -2001,6 +3197,9 @@ bin_compvalues(char *nam, char **args, char *ops, int func) return 1; case 'D': + /* This returns the description and action to use if we are at + * a place where some action has to be used at all. In that case + * zero is returned and non-zero otherwise. */ { Caarg arg = cv_laststate.def; @@ -2013,6 +3212,8 @@ bin_compvalues(char *nam, char **args, char *ops, int func) return 1; } case 'C': + /* This returns the sub-context (i.e.: the tag) to use when executing + * an action. */ { Caarg arg = cv_laststate.def; @@ -2024,6 +3225,10 @@ bin_compvalues(char *nam, char **args, char *ops, int func) return 1; } case 'V': + /* This is what -O is for comparguments: it returns (in three arrays) + * the values for values without arguments, with arguments and with + * optional arguments (so that we can get the auto-suffixes right). + * As for comparguments, the strings returned are usable for _describe. */ { LinkList noarg = newlinklist(); LinkList arg = newlinklist(); @@ -2057,6 +3262,8 @@ bin_compvalues(char *nam, char **args, char *ops, int func) return 0; } case 's': + /* This returns the value separator, if any, and sets the return + * value to say if there is such a separator. */ if (cv_laststate.d->hassep) { char tmp[2]; @@ -2067,10 +3274,26 @@ bin_compvalues(char *nam, char **args, char *ops, int func) return 0; } return 1; + case 'S': + /* Like -s, but for the separator between values and their arguments. */ + { + char tmp[2]; + + tmp[0] = cv_laststate.d->argsep; + tmp[1] = '\0'; + setsparam(args[1], ztrdup(tmp)); + } + return 0; case 'd': + /* This returns the description string (first argument to _values) + * which is passed down to _describe. */ setsparam(args[1], ztrdup(cv_laststate.d->descr)); return 0; case 'L': + /* Almost the same as for comparguments. This gets a value name + * and returns the description and action of its first argument, if + * any. The rest (prefix matching) is in _values. Return non-zero + * if there is no such option. */ { Cvval val = cv_get_val(cv_laststate.d, args[1]); @@ -2086,6 +3309,8 @@ bin_compvalues(char *nam, char **args, char *ops, int func) return 1; } case 'v': + /* Again, as for comparguments. This returns the values and their + * arguments as an array which will be stored in val_args in _values. */ if (cv_laststate.vals) { char **ret, **p; LinkNode n; @@ -2106,13 +3331,35 @@ bin_compvalues(char *nam, char **args, char *ops, int func) return 1; } +static char * +comp_quote(char *str, int prefix) +{ + int x; + char *ret; + + if ((x = (prefix && *str == '='))) + *str = 'x'; + + ret = bslashquote(str, NULL, (*compqstack == '\'' ? 1 : + (*compqstack == '"' ? 2 : 0))); + + if (x) + *str = *ret = '='; + + return ret; +} + static int -bin_compquote(char *nam, char **args, char *ops, int func) +bin_compquote(char *nam, char **args, Options ops, UNUSED(int func)) { char *name; struct value vbuf; Value v; + if (incompfunc != 1) { + zwarnnam(nam, "can only be called from completion function", NULL, 0); + return 1; + } /* Anything to do? */ if (!compqstack || !*compqstack) @@ -2122,31 +3369,22 @@ bin_compquote(char *nam, char **args, char *ops, int func) while ((name = *args++)) { name = dupstring(name); + queue_signals(); if ((v = getvalue(&vbuf, &name, 0))) { switch (PM_TYPE(v->pm->flags)) { case PM_SCALAR: - { - char *val = getstrvalue(v); - - val = bslashquote(val, NULL, - (*compqstack == '\'' ? 1 : - (*compqstack == '"' ? 2 : 0))); - - setstrvalue(v, ztrdup(val)); - } + setstrvalue(v, ztrdup(comp_quote(getstrvalue(v), + OPT_ISSET(ops,'p')))); break; case PM_ARRAY: { - char **val = v->pm->gets.afn(v->pm); + char **val = v->pm->gsu.a->getfn(v->pm); char **new = (char **) zalloc((arrlen(val) + 1) * sizeof(char *)); char **p = new; for (; *val; val++, p++) - *p = ztrdup(bslashquote(*val, NULL, - (*compqstack == '\'' ? 1 : - (*compqstack == '"' ? 2 : - 0)))); + *p = ztrdup(comp_quote(*val, OPT_ISSET(ops,'p'))); *p = NULL; setarrvalue(v, new); @@ -2157,6 +3395,7 @@ bin_compquote(char *nam, char **args, char *ops, int func) } } else zwarnnam(nam, "unknown parameter: %s", args[-1], 0); + unqueue_signals(); } return 0; } @@ -2242,6 +3481,7 @@ settags(int level, char **tags) /* Check if an array contains a string. */ +/**/ static int arrcontains(char **a, char *s, int colon) { @@ -2261,7 +3501,7 @@ arrcontains(char **a, char *s, int colon) } static int -bin_comptags(char *nam, char **args, char *ops, int func) +bin_comptags(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) { int min, max, n, level; @@ -2290,7 +3530,7 @@ bin_comptags(char *nam, char **args, char *ops, int func) case 'N': min = 0; max = 0; break; case 'R': min = 1; max = 1; break; case 'S': min = 1; max = 1; break; - case 'A': min = 2; max = 2; break; + case 'A': min = 2; max = 3; break; default: zwarnnam(nam, "invalid option: %s", args[0], 0); return 1; @@ -2363,7 +3603,15 @@ bin_comptags(char *nam, char **args, char *ops, int func) return 1; } s->ptr = q + 1; - setsparam(args[2], ztrdup(v)); + setsparam(args[2], ztrdup(*v == '-' ? dyncat(args[1], v) : v)); + if (args[3]) { + char *r = dupstring(*q), *p; + + for (p = r + (v - *q); *p && *p != ':'; p++); + *p = '\0'; + + setsparam(args[3], ztrdup(r)); + } return 0; } return 1; @@ -2383,7 +3631,7 @@ bin_comptags(char *nam, char **args, char *ops, int func) } static int -bin_comptry(char *nam, char **args, char *ops, int func) +bin_comptry(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) { if (incompfunc != 1) { zwarnnam(nam, "can only be called from completion function", NULL, 0); @@ -2394,84 +3642,815 @@ bin_comptry(char *nam, char **args, char *ops, int func) return 1; } if (*args) { - char **p, **q, **all; + if (!strcmp(*args, "-m")) { + char *s, *p, *q, *c, **all = comptags[lasttaglevel]->all; + LinkList list = newlinklist(); + LinkNode node; + int num = 0; + Ctset set; + + while ((s = *++args)) { + while (*s) { + while (*s && iblank(*s)) + s++; + for (p = q = s, c = NULL; *s && !inblank(*s); s++) { + if (!c && *s == ':') + c = p; + if (*s == '\\' && s[1]) + s++; + *p++ = *s; + } + if (*s) + s++; + *p = '\0'; + if (*q) { + char *qq, *qqq; + + if (c) + *c = '\0'; + + qqq = qq = dupstring(q); + while (*qqq) { + if (qqq == qq || qqq[-1] != '\\') { + if (*qqq == '{') + *qqq = Inbrace; + else if (*qqq == '}') + *qqq = Outbrace; + else if (*qqq == ',') + *qqq = Comma; + } + qqq++; + } + tokenize(qq); + if (haswilds(qq) || hasbraces(qq)) { + Patprog prog; + LinkNode bnode, node; + LinkList blist = newlinklist(); + + addlinknode(blist, qq); + for (bnode = firstnode(blist); bnode; incnode(bnode)) + while (hasbraces(getdata(bnode))) + xpandbraces(blist, &bnode); + + for (bnode = firstnode(blist); bnode; incnode(bnode)) { + qq = (char *) getdata(bnode); + if ((prog = patcompile(qq, PAT_STATIC, NULL))) { + char **a, *n; + int l = (c ? strlen(c + 1) + 2 : 1), al; + + for (a = all; *a; a++) { + for (node = firstnode(list); node; + incnode(node)) { + char *as, *ls; + + for (as = *a, ls = (char *) getdata(node); + *as && *ls && *ls != ':'; as++, ls++) + if (*as != *ls) + break; + if (!*as && (!*ls || *ls == ':')) + break; + } + if (node) + continue; + if (pattry(prog, *a)) { + n = (char *) zhalloc((al = strlen(*a)) + l); + strcpy(n, *a); + if (c) { + n[al] = ':'; + strcpy(n + al + 1, c + 1); + } + addlinknode(list, n); + num++; + } + } + } + } + } else if (arrcontains(all, q, 0)) { + for (set = comptags[lasttaglevel]->sets; set; + set = set->next) + if (arrcontains(set->tags, q, 0)) + break; + if (!set) { + addlinknode(list, q); + num++; + } + } + if (c) + *c = ':'; + } + } + if (num) { + char **a; + Ctset l; - args = arrdup(args); + set = (Ctset) zalloc(sizeof(*set)); - for (p = q = args, all = comptags[lasttaglevel]->all; *p; p++) - if (arrcontains(all, *p, 1)) { - Ctset s; + a = set->tags = (char **) zalloc((num + 1) * sizeof(char *)); + for (node = firstnode(list); node; incnode(node)) + *a++ = ztrdup((char *) getdata(node)); - for (s = comptags[lasttaglevel]->sets; s; s = s->next) - if (arrcontains(s->tags, *p, 0)) - break; + *a = NULL; + set->next = NULL; + set->ptr = NULL; + set->tag = NULL; + + if ((l = comptags[lasttaglevel]->sets)) { + while (l->next) + l = l->next; - if (!s) - *q++ = *p; + l->next = set; + } else + comptags[lasttaglevel]->sets = set; + } } - *q = NULL; + } else { + char **p, **q, **all; + int sep = 0; - if (*args) { - Ctset s = (Ctset) zalloc(sizeof(*s)), l; + if ((sep = !strcmp(*args, "-s"))) + args++; - s->tags = zarrdup(args); - s->next = NULL; - s->ptr = NULL; - s->tag = NULL; + for (p = q = args, all = comptags[lasttaglevel]->all; *p; p++) + if (arrcontains(all, *p, 1)) { + Ctset s; - if ((l = comptags[lasttaglevel]->sets)) { - while (l->next) - l = l->next; + for (s = comptags[lasttaglevel]->sets; s; s = s->next) + if (arrcontains(s->tags, *p, 0)) + break; - l->next = s; - } else - comptags[lasttaglevel]->sets = s; + if (!s) + *q++ = *p; + } + *q = NULL; + + if (*args) { + char *dummy[2]; + + do { + Ctset s = (Ctset) zalloc(sizeof(*s)), l; + + if (sep) { + dummy[0] = *args++; + dummy[1] = NULL; + s->tags = zarrdup(dummy); + } else + s->tags = zarrdup(args); + s->next = NULL; + s->ptr = NULL; + s->tag = NULL; + + if ((l = comptags[lasttaglevel]->sets)) { + while (l->next) + l = l->next; + + l->next = s; + } else + comptags[lasttaglevel]->sets = s; + } while (sep && *args); + } } } return 0; } +#define PATH_MAX2 (PATH_MAX * 2) + +static LinkList +cfp_test_exact(LinkList names, char **accept, char *skipped) +{ + char buf[PATH_MAX2 + 1], *suf, *p; + int l, sl, found = 0; + struct stat st; + LinkNode node; + LinkList ret = newlinklist(), alist = NULL; + + if ((!(compprefix && *compprefix) && !(compsuffix && *compsuffix)) || + (!accept || !*accept || + ((!strcmp(*accept, "false") || !strcmp(*accept, "no") || + !strcmp(*accept, "off") || !strcmp(*accept, "0")) && !accept[1]))) + return NULL; + + if (accept[1] || + (strcmp(*accept, "true") && strcmp(*accept, "yes") && + strcmp(*accept, "on") && strcmp(*accept, "1"))) { + Patprog prog; + + alist = newlinklist(); + + for (; (p = *accept); accept++) { + if (*p == '*' && !p[1]) { + alist = NULL; + break; + } + tokenize(p = dupstring(p)); + if ((prog = patcompile(p, 0, NULL))) + addlinknode(alist, prog); + } + } + sl = strlen(skipped) + (compprefix ? strlen(compprefix) : 0) + + (compsuffix ? strlen(compsuffix) : 0); + + if (sl > PATH_MAX2) + return NULL; + + suf = dyncat(skipped, rembslash(dyncat(compprefix, compsuffix))); + + for (node = firstnode(names); node; incnode(node)) { + l = strlen(p = (char *) getdata(node)); + if (l + sl < PATH_MAX2) { + strcpy(buf, p); + strcpy(buf + l, suf); + + if (!ztat(buf, &st, 0)) { + if (alist) { + LinkNode anode; + + for (anode = firstnode(alist); anode; incnode(anode)) + if (pattry((Patprog) getdata(anode), buf)) + break; + + if (!anode) + continue; + } + found = 1; + addlinknode(ret, dupstring(buf)); + } + } + } + return (found ? ret : NULL); +} + static char * -fmtstr(char *str, char c, char *repl) +cfp_matcher_pats(char *matcher, char *add) { - int len, num, rlen; - char *s, *ret, *rp; + Cmatcher m = parse_cmatcher(NULL, matcher); + + if (m && m != pcm_err) { + char *tmp; + int al = strlen(add), tl; + VARARR(Cmatcher, ms, al); + Cmatcher *mp; + Cpattern stopp; + int stopl = 0; + + memset(ms, 0, al * sizeof(Cmatcher)); + + for (; m && *add; m = m->next) { + stopp = NULL; + if (!(m->flags & (CMF_LEFT|CMF_RIGHT))) { + if (m->llen == 1 && m->wlen == 1) { + for (tmp = add, tl = al, mp = ms; tl; tl--, tmp++, mp++) { + if (pattern_match(m->line, tmp, NULL, NULL)) { + if (*mp) { + *tmp = '\0'; + al = tmp - add; + break; + } else + *mp = m; + } + } + } else { + stopp = m->line; + stopl = m->llen; + } + } else if (m->flags & CMF_RIGHT) { + if (m->wlen < 0 && !m->llen && m->ralen == 1) { + for (tmp = add, tl = al, mp = ms; tl; tl--, tmp++, mp++) { + if (pattern_match(m->right, tmp, NULL, NULL)) { + if (*mp || (tmp == add && *tmp == '.')) { + *tmp = '\0'; + al = tmp - add; + break; + } else + *mp = m; + } + } + } else if (m->llen) { + stopp = m->line; + stopl = m->llen; + } else { + stopp = m->right; + stopl = m->ralen; + } + } else { + if (!m->lalen) + return ""; - len = strlen(str); - rlen = strlen(repl); + stopp = m->left; + stopl = m->lalen; + } + if (stopp) + for (tmp = add, tl = al; tl >= stopl; tl--, tmp++) + if (pattern_match(stopp, tmp, NULL, NULL)) { + *tmp = '\0'; + al = tmp - add; + break; + } + } + if (*add) { + char *ret = "", buf[259]; + + for (mp = ms; *add; add++, mp++) { + if (!(m = *mp)) { + buf[0] = *add; + buf[1] = '\0'; + } else if (m->flags & CMF_RIGHT) { + buf[0] = '*'; + buf[1] = *add; + buf[2] = '\0'; + } else { + unsigned char *t, c; + char *p = buf; + int i; - for (num = 0, s = str; *s; s++) - if (*s == '%' && s[1] == c) - num++, s++; + for (i = 256, t = m->word->tab; i--; t++) + if (*t) + break; + if (i) { + t = m->word->tab; + *p++ = '['; + if (m->line->equiv && m->word->equiv) { + *p++ = *add; + c = m->line->tab[STOUC(*add)]; + for (i = 0; i < 256; i++) + if (m->word->tab[i] == c) { + *p++ = (char) i; + break; + } + } else { + if (*add == ']' || t[STOUC(']')]) + *p++ = ']'; + for (i = 0; i < 256; i++, t++) + if (*t && ((char) i) != *add && + i != ']' && i != '-' && + i != '^' && i != '!') + *p++ = (char) i; + *p++ = *add; + t = m->word->tab; + if (*add != '^' && t[STOUC('^')]) + *p++ = '^'; + if (*add != '!' && t[STOUC('!')]) + *p++ = '!'; + if (*add != '-' && t[STOUC('-')]) + *p++ = '-'; + } + *p++ = ']'; + *p = '\0'; + } else { + *p = '?'; + p[1] = '\0'; + } + } + ret = dyncat(ret, buf); + } + return ret; + } + } + return add; +} + +static void +cfp_opt_pats(char **pats, char *matcher) +{ + char *add, **p, *q, *t, *s; - ret = (char *) zhalloc((num * (rlen - 2)) + len + 1); + if (!compprefix || !*compprefix) + return; - for (s = str, rp = ret; *s; s++) { - if (*s == '%' && s[1] == c) { - strcpy(rp, repl); - rp += rlen; - s++; - } else - *rp++ = *s; + if (comppatmatch && *comppatmatch) { + tokenize(t = rembslash(dyncat(compprefix, compsuffix))); + remnulargs(t); + if (haswilds(t)) + return; + } + add = (char *) zhalloc(strlen(compprefix) * 2 + 1); + for (s = compprefix, t = add; *s; s++) { + if (*s != '\\' || !s[1] || s[1] == '*' || s[1] == '?' || + s[1] == '<' || s[1] == '>' || s[1] == '(' || s[1] == ')' || + s[1] == '[' || s[1] == ']' || s[1] == '|' || s[1] == '#' || + s[1] == '^' || s[1] == '~' || s[1] == '=') { + if ((s == compprefix || s[-1] != '\\') && + (*s == '*' || *s == '?' || *s == '<' || *s == '>' || + *s == '(' || *s == ')' || *s == '[' || *s == ']' || + *s == '|' || *s == '#' || *s == '^' || *s == '~' || + *s == '=')) + *t++ = '\\'; + *t++ = *s; + } + } + *t = '\0'; + for (p = pats; *add && (q = *p); p++) { + if (*q) { + q = dupstring(q); + t = q + strlen(q) - 1; + if (*t == ')') { + for (s = t--; t > q; t--) + if (*t == ')' || *t == '|' || *t == '~' || *t == '(') + break; + if (t != q && *t == '(') + *t = '\0'; + } + for (; *q && *add; q++) { + if (*q == '\\' && q[1]) { + for (s = add, q++; *s && *s != *q; s++); + *s = '\0'; + } else if (*q == '<') { + for (s = add; *s && !idigit(*s); s++); + *s = '\0'; + } else if (*q == '[') { + int not, first = 1; + char *x = ++q; + + if ((not = (*x == '!' || *x == '^'))) + x++; + for (; *x && (first || *x != ']'); x++) { + if (x[1] == '-' && x[2]) { + char c1 = *x, c2 = x[2]; + + for (s = add; *s && (*x < c1 || *x > c2); s++); + *s = '\0'; + } else { + for (s = add; *s && *s != *x; s++); + *s = '\0'; + } + } + } else if (*q != '?' && *q != '*' && *q != '(' && *q != ')' && + *q != '|' && *q != '~' && *q != '#') { + for (s = add; *s && *s != *q; s++); + *s = '\0'; + } + } + } + } + if (*add) { + if (*matcher && !(add = cfp_matcher_pats(matcher, add))) + return; + + for (p = pats; *p; p++) + if (**p == '*') + *p = dyncat(add, *p); } - *rp = '\0'; +} +static LinkList +cfp_bld_pats(UNUSED(int dirs), LinkList names, char *skipped, char **pats) +{ + LinkList ret = newlinklist(); + LinkNode node; + int ol, sl = strlen(skipped), pl, dot; + char **p, *o, *str; + + dot = (unset(GLOBDOTS) && compprefix && *compprefix == '.'); + for (node = firstnode(names); node; incnode(node)) { + ol = strlen(o = (char *) getdata(node)); + for (p = pats; *p; p++) { + pl = strlen(*p); + str = (char *) zhalloc(ol + sl + pl + 1); + strcpy(str, o); + strcpy(str + ol, skipped); + strcpy(str + ol + sl, *p); + addlinknode(ret, str); + if (dot && **p != '.') { + str = (char *) zhalloc(ol + sl + pl + 2); + strcpy(str, o); + strcpy(str + ol, skipped); + str[ol + sl] = '.'; + strcpy(str + ol + sl + 1, *p); + addlinknode(ret, str); + } + } + } return ret; } +static LinkList +cfp_add_sdirs(LinkList final, LinkList orig, char *skipped, + char *sdirs, char **fake) +{ + int add = 0; + + if (*sdirs && (isset(GLOBDOTS) || (compprefix && *compprefix == '.'))) { + if (!strcmp(sdirs, "yes") || !strcmp(sdirs, "true") || + !strcmp(sdirs, "on") || !strcmp(sdirs, "1")) + add = 2; + else if (!strcmp(sdirs, "..")) + add = 1; + } + if (add) { + LinkNode node; + char *s1 = dyncat(skipped, ".."); + char *s2 = (add == 2 ? dyncat(skipped, ".") : NULL), *m; + + for (node = firstnode(orig); node; incnode(node)) { + if ((m = (char *) getdata(node))) { + addlinknode(final, dyncat(m, s1)); + if (s2) + addlinknode(final, dyncat(m, s2)); + } + } + } + if (fake && *fake) { + LinkNode node; + char *m, *f, *p, *t, *a, c; + int sl = strlen(skipped) + 1; + struct stat st1, st2; + + for (; (f = *fake); fake++) { + f = dupstring(f); + for (p = t = f; *p; p++) { + if (*p == ':') + break; + else if (*p == '\\' && p[1]) + p++; + *t++ = *p; + } + if (*p) { + *t = *p++ = '\0'; + if (!*p) + continue; + + for (node = firstnode(orig); node; incnode(node)) { + if ((m = (char *) getdata(node)) && + (!strcmp(f, m) || + (!stat(f, &st1) && !stat((*m ? m : "."), &st2) && + st1.st_dev == st2.st_dev && + st1.st_ino == st2.st_ino))) { + while (*p) { + while (*p && inblank(*p)) + p++; + if (!*p) + break; + for (f = t = p; *p; p++) { + if (inblank(*p)) + break; + else if (*p == '\\' && p[1]) + p++; + *t++ = *p; + } + c = *t; + *t = '\0'; + a = (char *) zhalloc(strlen(m) + sl + strlen(f)); + strcpy(a, m); + strcat(a, skipped); + strcat(a, f); + addlinknode(final, a); + *t = c; + } + } + } + } + } + } + return final; +} + +static LinkList +cf_pats(int dirs, int noopt, LinkList names, char **accept, char *skipped, + char *matcher, char *sdirs, char **fake, char **pats) +{ + LinkList ret; + char *dpats[2]; + + if ((ret = cfp_test_exact(names, accept, skipped))) + return cfp_add_sdirs(ret, names, skipped, sdirs, fake); + + if (dirs) { + dpats[0] = "*(-/)"; + dpats[1] = NULL; + pats = dpats; + } + if (!noopt) + cfp_opt_pats(pats, matcher); + + return cfp_add_sdirs(cfp_bld_pats(dirs, names, skipped, pats), + names, skipped, sdirs, fake); +} + +static void +cf_ignore(char **names, LinkList ign, char *style, char *path) +{ + int pl = strlen(path), tpar, tpwd, found; + struct stat nst, est, st; + char *n, *c, *e; + + tpar = !!strstr(style, "parent"); + if ((tpwd = !!strstr(style, "pwd")) && stat(pwd, &est)) + tpwd = 0; + + if (!tpar && !tpwd) + return; + + for (; (n = *names); names++) { + if (!ztat(n, &nst, 0) && S_ISDIR(nst.st_mode)) { + if (tpwd && nst.st_dev == est.st_dev && nst.st_ino == est.st_ino) { + addlinknode(ign, bslashquote(n, NULL, 0)); + continue; + } + if (tpar && !strncmp((c = dupstring(n)), path, pl)) { + found = 0; + while ((e = strrchr(c, '/')) && e > c + pl) { + *e = '\0'; + if (!ztat(c, &st, 0) && + st.st_dev == nst.st_dev && st.st_ino == nst.st_ino) { + found = 1; + break; + } + } + if (found || ((e = strrchr(c, '/')) && e > c + pl && + !ztat(c, &st, 0) && st.st_dev == nst.st_dev && + st.st_ino == nst.st_ino)) + addlinknode(ign, bslashquote(n, NULL, 0)); + } + } + } +} + +static LinkList +cf_remove_other(char **names, char *pre, int *amb) +{ + char *p; + + if ((p = strchr(pre, '/'))) { + char **n; + + *p = '\0'; + pre = dyncat(pre, "/"); + *p = '/'; + + for (n = names; *n; n++) + if (strpfx(pre, *n)) + break; + + if (*n) { + LinkList ret = newlinklist(); + + for (; *names; names++) + if (strpfx(pre, *names)) + addlinknode(ret, dupstring(*names)); + + *amb = 0; + + return ret; + } else { + if (!(p = *names++)) + *amb = 0; + else { + char *q; + + if ((q = strchr((p = dupstring(p)), '/'))) + *q = '\0'; + + p = dyncat(p, "/"); + + for (; *names; names++) + if (!strpfx(p, *names)) { + *amb = 1; + return NULL; + } + } + } + } else { + if (!(p = *names++)) + *amb = 0; + else + for (; *names; names++) + if (strcmp(p, *names)) { + *amb = 1; + return NULL; + } + } + return NULL; +} + static int -bin_compfmt(char *nam, char **args, char *ops, int func) +bin_compfiles(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) { - char *param = args[0], *str = args[1]; + if (incompfunc != 1) { + zwarnnam(nam, "can only be called from completion function", NULL, 0); + return 1; + } + if (**args != '-') { + zwarnnam(nam, "missing option: %s", *args, 0); + return 1; + } + switch (args[0][1]) { + case 'p': + case 'P': + if (args[0][2] && (args[0][2] != '-' || args[0][3])) { + zwarnnam(nam, "invalid option: %s", *args, 0); + return 1; + } else { + char **tmp; + LinkList l; - for (args += 2; *args; args++) { - if (args[0][1] != ':') { - zwarnnam(nam, "invalid argument `%s'", args[0], 0); + if (!args[1] || !args[2] || !args[3] || !args[4] || !args[5] || + !args[6] || (args[0][1] == 'p' && !args[7])) { + zwarnnam(nam, "too few arguments", NULL, 0); + return 1; + } + queue_signals(); + if (!(tmp = getaparam(args[1]))) { + zwarnnam(nam, "unknown parameter: %s", args[1], 0); + return 0; + } + for (l = newlinklist(); *tmp; tmp++) + addlinknode(l, *tmp); + set_list_array(args[1], cf_pats((args[0][1] == 'P'), !!args[0][2], + l, getaparam(args[2]), args[3], + args[4], args[5], + getaparam(args[6]), args + 7)); + unqueue_signals(); + return 0; + } + case 'i': + if (args[0][2]) { + zwarnnam(nam, "invalid option: %s", *args, 0); return 1; + } else { + char **tmp; + LinkList l; + + if (!args[1] || !args[2] || !args[3] || !args[4]) { + zwarnnam(nam, "too few arguments", NULL, 0); + return 1; + } + if (args[5]) { + zwarnnam(nam, "too many arguments", NULL, 0); + return 1; + } + queue_signals(); + tmp = getaparam(args[2]); + l = newlinklist(); + if (tmp) + for (; *tmp; tmp++) + addlinknode(l, *tmp); + if (!(tmp = getaparam(args[1]))) { + unqueue_signals(); + zwarnnam(nam, "unknown parameter: %s", args[1], 0); + return 0; + } + cf_ignore(tmp, l, args[3], args[4]); + unqueue_signals(); + set_list_array(args[2], l); + return 0; + } + case 'r': + { + char **tmp; + LinkList l; + int ret = 0; + + if (!args[1] || !args[2]) { + zwarnnam(nam, "too few arguments", NULL, 0); + return 1; + } + if (args[3]) { + zwarnnam(nam, "too many arguments", NULL, 0); + return 1; + } + queue_signals(); + if (!(tmp = getaparam(args[1]))) { + unqueue_signals(); + zwarnnam(nam, "unknown parameter: %s", args[1], 0); + return 0; + } + if ((l = cf_remove_other(tmp, args[2], &ret))) + set_list_array(args[1], l); + unqueue_signals(); + return ret; } - str = fmtstr(str, **args, *args + 2); } - setsparam(param, ztrdup(str)); + zwarnnam(nam, "invalid option: %s", *args, 0); + return 1; +} + +static int +bin_compgroups(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) +{ + Heap oldheap; + char *n; + + if (incompfunc != 1) { + zwarnnam(nam, "can only be called from completion function", NULL, 0); + return 1; + } + SWITCHHEAPS(oldheap, compheap) { + while ((n = *args++)) { + endcmgroup(NULL); + begcmgroup(n, CGF_NOSORT|CGF_UNIQCON); + endcmgroup(NULL); + begcmgroup(n, CGF_UNIQALL); + endcmgroup(NULL); + begcmgroup(n, CGF_NOSORT|CGF_UNIQCON); + endcmgroup(NULL); + begcmgroup(n, CGF_UNIQALL); + endcmgroup(NULL); + begcmgroup(n, CGF_NOSORT); + endcmgroup(NULL); + begcmgroup(n, 0); + } + } SWITCHBACKHEAPS(oldheap); + return 0; } @@ -2479,16 +4458,17 @@ static struct builtin bintab[] = { BUILTIN("compdescribe", 0, bin_compdescribe, 3, -1, 0, NULL, NULL), BUILTIN("comparguments", 0, bin_comparguments, 1, -1, 0, NULL, NULL), BUILTIN("compvalues", 0, bin_compvalues, 1, -1, 0, NULL, NULL), - BUILTIN("compquote", 0, bin_compquote, 1, -1, 0, NULL, NULL), + BUILTIN("compquote", 0, bin_compquote, 1, -1, 0, "p", NULL), BUILTIN("comptags", 0, bin_comptags, 1, -1, 0, NULL, NULL), BUILTIN("comptry", 0, bin_comptry, 0, -1, 0, NULL, NULL), - BUILTIN("compfmt", 0, bin_compfmt, 2, -1, 0, NULL, NULL), + BUILTIN("compfiles", 0, bin_compfiles, 1, -1, 0, NULL, NULL), + BUILTIN("compgroups", 0, bin_compgroups, 1, -1, 0, NULL, NULL), }; /**/ int -setup_(Module m) +setup_(UNUSED(Module m)) { memset(cadef_cache, 0, sizeof(cadef_cache)); memset(cvdef_cache, 0, sizeof(cvdef_cache)); @@ -2517,7 +4497,7 @@ cleanup_(Module m) /**/ int -finish_(Module m) +finish_(UNUSED(Module m)) { int i; -- cgit 1.4.1