diff options
Diffstat (limited to 'Src')
-rw-r--r-- | Src/Zle/computil.c | 1891 | ||||
-rw-r--r-- | Src/Zle/computil.mdd | 3 |
2 files changed, 1894 insertions, 0 deletions
diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c new file mode 100644 index 000000000..aed3d9808 --- /dev/null +++ b/Src/Zle/computil.c @@ -0,0 +1,1891 @@ +/* + * computil.c - completion utilities + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1999 Sven Wischnowsky + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Sven Wischnowsky or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Sven Wischnowsky and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Sven Wischnowsky and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Sven Wischnowsky and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "computil.mdh" +#include "computil.pro" + + +/* Help for `_display'. */ + +typedef struct cdisp *Cdisp; + +struct cdisp { + int pre, suf, colon; +}; + +static void +cdisp_calc(Cdisp disp, char **args) +{ + char *cp; + int i; + + for (; *args; args++) { + if ((cp = strchr(*args, ':')) && cp[1]) { + disp->colon++; + if ((i = cp - *args) > disp->pre) + disp->pre = i; + if ((i = strlen(cp + 1)) > disp->suf) + disp->suf = i; + } + } +} + +static char ** +cdisp_build(Cdisp disp, char *sep, char **args) +{ + int sl = strlen(sep), pre = disp->pre, suf; + VARARR(char, buf, disp->pre + disp->suf + sl + 1); + char **ret, **rp, *cp; + + ret = (char **) zalloc((arrlen(args) + 1) * sizeof(char *)); + + memcpy(buf + pre, sep, sl); + suf = pre + sl; + + for (rp = ret; *args; args++) { + if ((cp = strchr(*args, ':')) && cp[1]) { + memset(buf, ' ', pre); + memcpy(buf, *args, (cp - *args)); + strcpy(buf + suf, cp + 1); + *rp++ = ztrdup(buf); + } else { + if (cp) + *cp = '\0'; + *rp++ = ztrdup(*args); + if (cp) + *cp = ':'; + } + } + *rp = NULL; + + return ret; +} + +/**/ +static int +bin_compdisplay(char *nam, char **args, char *ops, int func) +{ + struct cdisp disp; + + if (incompfunc != 1) { + zerrnam(nam, "can only be called from completion function", NULL, 0); + return 1; + } + disp.pre = disp.suf = disp.colon = 0; + + cdisp_calc(&disp, args + 2); + setaparam(args[0], cdisp_build(&disp, args[1], args + 2)); + + return !disp.colon; +} + +/* Help fuer `_describe'. */ + +typedef struct cdset *Cdset; + +struct cdstate { + int showd; + char *sep; + Cdset sets; + struct cdisp disp; +}; + +struct cdset { + Cdset next; + char **opts; + char **strs; + char **matches; +}; + +static struct cdstate cd_state; +static int cd_parsed = 0; + +static void +free_cdsets(Cdset p) +{ + Cdset n; + + 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); + zfree(p, sizeof(*p)); + } +} + +static int +cd_init(char *nam, char *sep, char **args, int disp) +{ + Cdset *setp, set; + char **ap, *tmp; + + if (cd_parsed) { + zsfree(cd_state.sep); + free_cdsets(cd_state.sets); + } + setp = &(cd_state.sets); + cd_state.sep = ztrdup(sep); + cd_state.sets = NULL; + cd_state.disp.pre = cd_state.disp.suf = cd_state.disp.colon = 0; + cd_state.showd = disp; + + while (*args) { + *setp = set = (Cdset) zcalloc(sizeof(*set)); + setp = &(set->next); + + if (!(ap = get_user_var(*args))) { + zerrnam(nam, "invalid argument: %s", *args, 0); + return 1; + } + PERMALLOC { + set->strs = arrdup(ap); + } LASTALLOC; + + if (disp) + cdisp_calc(&(cd_state.disp), set->strs); + + if (*++args && **args != '-') { + if (!(ap = get_user_var(*args))) { + zerrnam(nam, "invalid argument: %s", *args, 0); + return 1; + } + PERMALLOC { + set->matches = arrdup(ap); + } LASTALLOC; + args++; + } + for (ap = args; *args && + (args[0][0] != '-' || args[0][1] != '-' || args[0][2]); + args++); + + tmp = *args; + *args = NULL; + PERMALLOC { + set->opts = arrdup(ap); + } LASTALLOC; + if ((*args = tmp)) + args++; + } + return 0; +} + +static int +cd_get(char **params) +{ + Cdset set; + + if ((set = cd_state.sets)) { + char **sd, **sdp, **md, **mdp, **ss, **ssp, **ms, **msp; + char **p, **mp, *cp; + int dl = 1, sl = 1, sepl = strlen(cd_state.sep); + int pre = cd_state.disp.pre, suf = cd_state.disp.suf; + VARARR(char, buf, pre + suf + sepl + 1); + + for (p = set->strs; *p; p++) + if (cd_state.showd && (cp = strchr(*p, ':')) && cp[1]) + dl++; + else + 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; + } + for (sdp = sd, ssp = ss, mdp = md, msp = ms, + p = set->strs, mp = set->matches; *p; p++) { + if ((cp = strchr(*p, ':')) && cp[1] && cd_state.showd) { + memset(buf, ' ', pre); + memcpy(buf, *p, (cp - *p)); + strcpy(buf + suf, cp + 1); + *sdp++ = ztrdup(buf); + if (mp) { + *mdp++ = ztrdup(*mp); + if (*mp) + mp++; + } else { + *cp = '\0'; + *mdp++ = ztrdup(*p); + *cp = ':'; + } + } else { + if (cp) + *cp = '\0'; + *ssp++ = ztrdup(*p); + if (mp) { + *msp++ = ztrdup(*mp); + if (*mp) + mp++; + } else + *msp++ = ztrdup(*p); + if (cp) + *cp = ':'; + } + } + *sdp = *ssp = *mdp = *msp = NULL; + + PERMALLOC { + p = arrdup(set->opts); + } LASTALLOC; + + setaparam(params[0], p); + setaparam(params[1], sd); + setaparam(params[2], md); + setaparam(params[3], ss); + setaparam(params[4], ms); + + cd_state.sets = set->next; + set->next = NULL; + free_cdsets(set); + + return 0; + } + return 1; +} + +/**/ +static int +bin_compdescribe(char *nam, char **args, char *ops, int func) +{ + if (incompfunc != 1) { + zerrnam(nam, "can only be called from completion function", NULL, 0); + return 1; + } + if (!args[0][0] || !args[0][1] || args[0][2]) { + zerrnam(nam, "invalid argument: %s", args[0], 0); + return 1; + } + switch (args[0][1]) { + case 'i': + case 'I': + cd_parsed = 1; + return cd_init(nam, args[1], args + 2, (args[0][1] == 'I')); + case 'g': + if (cd_parsed) { + int n = arrlen(args); + + if (n != 6) { + zerrnam(nam, (n < 6 ? "not enough arguments" : + "too many arguments"), NULL, 0); + return 1; + } + return cd_get(args + 1); + } else { + zerrnam(nam, "no parsed state", NULL, 0); + return 1; + } + } + zerrnam(nam, "invalid option: %s", args[0], 0); + return 1; +} + +/* Help for `_arguments'. */ + +typedef struct cadef *Cadef; +typedef struct caopt *Caopt; +typedef struct caarg *Caarg; + +struct cadef { + Cadef next; + Caopt opts; + int nopts, ndopts, nodopts; + Caarg args; + Caarg rest; + char **defs; + int ndefs; + int lastt; + Caopt *single; + char *match; + int argsactive; +}; + +struct caopt { + Caopt next; + char *name; + char *descr; + char **xor; + int type; + Caarg args; + int active; + int num; +}; + +#define CAO_NEXT 1 +#define CAO_DIRECT 2 +#define CAO_ODIRECT 3 +#define CAO_EQUAL 4 + +struct caarg { + Caarg next; + char *descr; + char *action; + int type; + char *end; + int num; +}; + +#define CAA_NORMAL 1 +#define CAA_OPT 2 +#define CAA_REST 3 +#define CAA_RARGS 4 +#define CAA_RREST 5 + +#define MAX_CACACHE 8 +static Cadef cadef_cache[MAX_CACACHE]; + +static int +arrcmp(char **a, char **b) +{ + if (!a && !b) + return 1; + else if (!a || !b) + return 0; + else { + while (*a && *b) + if (strcmp(*a++, *b++)) + return 0; + + return (!*a && !*b); + } +} + +static void +free_caargs(Caarg a) +{ + Caarg n; + + for (; a; a = n) { + n = a->next; + zsfree(a->descr); + zsfree(a->action); + zsfree(a->end); + zfree(a, sizeof(*a)); + } +} + +static void +free_cadef(Cadef d) +{ + if (d) { + Caopt p, n; + + zsfree(d->match); + freearray(d->defs); + + for (p = d->opts; p; p = n) { + n = p->next; + zsfree(p->name); + zsfree(p->descr); + freearray(p->xor); + free_caargs(p->args); + zfree(p, sizeof(*p)); + } + free_caargs(d->args); + free_caargs(d->rest); + if (d->single) + zfree(d->single, 256 * sizeof(Caopt)); + zfree(d, sizeof(*d)); + } +} + +static char * +rembslashcolon(char *s) +{ + char *p, *r; + + r = p = s = dupstring(s); + + while (*s) { + if (s[0] != '\\' || s[1] != ':') + *p++ = *s; + s++; + } + *p = '\0'; + + return r; +} + +static Caarg +parse_caarg(int mult, int type, int num, char **def) +{ + Caarg ret = (Caarg) zalloc(sizeof(*ret)); + char *p = *def, *d, sav; + + ret->next = NULL; + ret->descr = ret->action = ret->end = NULL; + ret->num = num; + ret->type = type; + + for (d = p; *p && *p != ':'; p++) + if (*p == '\\' && p[1]) + p++; + sav = *p; + *p = '\0'; + ret->descr = ztrdup(rembslashcolon(d)); + if (sav) { + if (mult) { + for (d = ++p; *p && *p != ':'; p++) + if (*p == '\\' && p[1]) + p++; + sav = *p; + *p = '\0'; + ret->action = ztrdup(rembslashcolon(d)); + if (sav) + *p = ':'; + } else + ret->action = ztrdup(rembslashcolon(p + 1)); + } + *def = p; + + return ret; +} + +static Cadef +parse_cadef(char *nam, char **args) +{ + Cadef ret; + Caopt *optp; + char **oargs = args, *p, *q, *match = "r:|[_-]=* r:|=*", **xor; + char *adpre, *adsuf; + int single = 0, anum = 1, xnum, nopts, ndopts, nodopts; + + nopts = ndopts = nodopts = 0; + + for (p = args[0]; *p && (p[0] != '%' || p[1] != 'd'); p++); + + if (*p) { + *p = '\0'; + adpre = dupstring(args[0]); + *p = '%'; + adsuf = dupstring(p + 2); + } else + adpre = adsuf = NULL; + + 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++; + break; + } + } else + break; + args++; + } + if (!*args) + return NULL; + + PERMALLOC { + ret = (Cadef) zalloc(sizeof(*ret)); + ret->next = NULL; + ret->opts = NULL; + ret->args = ret->rest = NULL; + ret->defs = arrdup(oargs); + ret->ndefs = arrlen(oargs); + ret->lastt = time(0); + if (single) { + ret->single = (Caopt *) zalloc(256 * sizeof(Caopt)); + memset(ret->single, 0, 256 * sizeof(Caopt)); + } else + ret->single = NULL; + ret->match = ztrdup(match); + } LASTALLOC; + + for (optp = &(ret->opts); *args; args++) { + p = dupstring(*args); + xnum = 0; + if (*p == '(') { + LinkList list = newlinklist(); + LinkNode node; + char **xp, sav; + + while (*p && *p != ')') { + for (p++; inblank(*p); p++); + + if (*p == ')') + break; + for (q = p++; *p && *p != ')' && !inblank(*p); p++); + + if (!*p) + break; + + sav = *p; + *p = '\0'; + addlinknode(list, dupstring(q)); + xnum++; + *p = sav; + } + if (*p != ')') { + free_cadef(ret); + zerrnam(nam, "invalid argument: %s", *args, 0); + return NULL; + } + xor = (char **) zalloc((xnum + 2) * sizeof(char *)); + for (node = firstnode(list), xp = xor; node; incnode(node), xp++) + *xp = ztrdup((char *) getdata(node)); + xp[0] = xp[1] = NULL; + + p++; + } else + xor = NULL; + + if (*p == '-' || *p == '+' || + (*p == '*' && (p[1] == '-' || p[1] == '+'))) { + Caopt opt; + Caarg oargs = NULL; + int multi, otype = CAO_NEXT, again = 0; + char *name, *descr, c; + + rec: + + if ((multi = (*p == '*'))) + p++; + + if ((p[0] == '-' && p[1] == '+') || + (p[0] == '+' && p[1] == '-')) { + name = ++p; + *p = (again ? '-' : '+'); + again = 1 - again; + } else { + name = p; + if (p[0] == '-' && p[1] == '-') + p++; + } + for (p++; *p && *p != ':' && *p != '[' && + ((*p != '-' && *p != '+' && *p != '=') || + (p[1] != ':' && p[1] != '[')); p++) + if (*p == '\\' && p[1]) + p++; + + c = *p; + *p = '\0'; + if (c == '-') { + otype = CAO_DIRECT; + c = *++p; + } else if (c == '+') { + otype = CAO_ODIRECT; + c = *++p; + } else if (c == '=') { + otype = CAO_EQUAL; + c = *++p; + } + if (c == '[') { + for (descr = ++p; *p && *p != ']'; p++) + if (*p == '\\' && p[1]) + p++; + + if (!*p) { + free_cadef(ret); + zerrnam(nam, "invalid option definition: %s", *args, 0); + return NULL; + } + *p++ = '\0'; + c = *p; + } else + descr = NULL; + + if (c && c != ':') { + free_cadef(ret); + zerrnam(nam, "invalid option definition: %s", *args, 0); + return NULL; + } + if (!multi) { + if (!xor) { + xor = (char **) zalloc(2 * sizeof(char *)); + xor[1] = NULL; + } + xor[xnum] = ztrdup(name); + } + if (c == ':') { + Caarg *oargp = &oargs; + int atype, rest; + char *end; + + while (c == ':') { + rest = 0; + end = NULL; + + if (*++p == ':') { + atype = CAA_OPT; + p++; + } else if (*p == '*') { + if (*++p != ':') { + for (end = ++p; *p && *p != ':'; p++) + if (*p == '\\' && p[1]) + p++; + } + if (*p != ':') { + free_cadef(ret); + free_caargs(oargs); + zerrnam(nam, "invalid option definition: %s", + *args, 0); + return NULL; + } + if (*++p == ':') { + if (*++p == ':') { + atype = CAA_RREST; + p++; + } else + atype = CAA_RARGS; + } else + atype = CAA_REST; + rest = 1; + } else + atype = CAA_NORMAL; + *oargp = parse_caarg(!rest, atype, 0, &p); + oargp = &((*oargp)->next); + if (rest) + break; + c = *p; + } + } + PERMALLOC { + *optp = opt = (Caopt) zalloc(sizeof(*opt)); + optp = &((*optp)->next); + + opt->next = NULL; + opt->name = ztrdup(name); + if (descr) + opt->descr = ztrdup(descr); + else if (adpre && oargs && !oargs->next) + opt->descr = tricat(adpre, oargs->descr, adsuf); + else + opt->descr = NULL; + opt->xor = xor; + opt->type = otype; + opt->args = oargs; + opt->num = nopts++; + } LASTALLOC; + + if (otype == CAO_DIRECT) + ndopts++; + else if (otype == CAO_ODIRECT || otype == CAO_EQUAL) + nodopts++; + + if (single && name[1] && !name[2]) + ret->single[STOUC(name[1])] = opt; + + if (again) { + p = dupstring(*args); + goto rec; + } + } else if (*p == '*') { + int type = CAA_REST; + + if (*++p != ':') { + free_cadef(ret); + zerrnam(nam, "invalid rest argument definition: %s", *args, 0); + return NULL; + } + if (ret->rest) { + free_cadef(ret); + zerrnam(nam, "doubled rest argument definition: %s", *args, 0); + return NULL; + } + if (*++p == ':') { + if (*++p == ':') { + type = CAA_RREST; + p++; + } else + type = CAA_RARGS; + } + ret->rest = parse_caarg(0, type, -1, &p); + } else { + int type = CAA_NORMAL; + Caarg arg, tmp, pre; + + if (idigit(*p)) { + int num = 0; + + while (*p && idigit(*p)) + num = (num * 10) + ((int) *p++); + + anum = num + 1; + } else + anum++; + + if (*p != ':') { + free_cadef(ret); + zerrnam(nam, "invalid argument: %s", *args, 0); + return NULL; + } + if (*++p == ':') { + type = CAA_OPT; + p++; + } + arg = parse_caarg(0, type, anum - 1, &p); + + for (tmp = ret->args, pre = NULL; + tmp && tmp->num < anum - 1; + pre = tmp, tmp = tmp->next); + + if (tmp && tmp->num == anum - 1) { + free_cadef(ret); + free_caargs(arg); + zerrnam(nam, "doubled argument definition: %s", *args, 0); + return NULL; + } + arg->next = tmp; + if (pre) + pre->next = arg; + else + ret->args = arg; + } + } + ret->nopts = nopts; + ret->ndopts = ndopts; + ret->nodopts = nodopts; + + return ret; +} + +static Cadef +get_cadef(char *nam, char **args) +{ + Cadef *p, *min, new; + int i, na = arrlen(args); + + for (i = MAX_CACACHE, p = cadef_cache, min = NULL; *p && i--; p++) + if (*p && na == (*p)->ndefs && arrcmp(args, (*p)->defs)) { + (*p)->lastt = time(0); + + return *p; + } else if (!min || !*p || (*p)->lastt < (*min)->lastt) + min = p; + if (i) + min = p; + if ((new = parse_cadef(nam, args))) { + free_cadef(*min); + *min = new; + } + return new; +} + +static Caopt +ca_get_opt(Cadef d, char *line, int full, char **end) +{ + Caopt p; + + if (full) { + for (p = d->opts; p; p = p->next) + if (p->active && !strcmp(p->name, line)) + return p; + } else { + for (p = d->opts; p; p = p->next) + if (p->active && p->args && p->type != CAO_NEXT && + strpfx(p->name, line)) { + if (end) { + int l = strlen(p->name); + + if (p->type == CAO_EQUAL && line[l] == '=') + l++; + + *end = line + l; + } + return p; + } + } + return NULL; +} + +static Caopt +ca_get_sopt(Cadef d, char *line, int full, char **end) +{ + Caopt p; + + 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) { + if (end) { + line++; + if (p->type == CAO_EQUAL && *line == '=') + line++; + *end = line; + } + break; + } else if (!p || !p->active || (line[1] && p->args)) + return NULL; + return p; + } + return NULL; +} + +static Caarg +ca_get_arg(Cadef d, int n) +{ + if (d->argsactive) { + Caarg a = d->args; + + while (a && a->num < n) + a = a->next; + + if (a && a->num == n) + return a; + + return d->rest; + } + return NULL; +} + +static void +ca_inactive(Cadef d, char **xor) +{ + if (xor) { + Caopt opt; + + for (; *xor; xor++) { + if (xor[0][0] == ':' && !xor[0][1]) + d->argsactive = 0; + else if ((opt = ca_get_opt(d, *xor, 1, NULL))) + opt->active = 0; + } + } +} + +struct castate { + Cadef d; + Caarg def, ddef; + Caopt curopt; + int opt, arg, argbeg, optbeg, nargbeg, restbeg; + int inopt, inrest, inarg, nth, doff, singles; + LinkList args; + LinkList *oargs; +}; + +static struct castate ca_laststate; +static int ca_parsed = 0, ca_alloced = 0; + +static void +ca_parse_line(Cadef d) +{ + Caarg adef, ddef; + Caopt ptr; + struct castate state; + char *line, *pe; + int cur, doff; + Patprog endpat = NULL; + + if (ca_alloced) { + int i = ca_laststate.d->nopts; + LinkList *p = ca_laststate.oargs; + + freelinklist(ca_laststate.args, freestr); + while (i--) + if (*p++) + freelinklist(p[-1], freestr); + } + for (ptr = d->opts; ptr; ptr = ptr->next) + ptr->active = 1; + d->argsactive = 1; + + state.d = d; + state.def = state.ddef = NULL; + state.curopt = NULL; + state.argbeg = state.optbeg = state.nargbeg = state.restbeg = + state.nth = state.inopt = state.inarg = state.opt = state.arg = 1; + state.inrest = state.doff = state.singles = state.doff = 0; + PERMALLOC { + state.args = newlinklist(); + state.oargs = (LinkList *) zalloc(d->nopts * sizeof(LinkList)); + memset(state.oargs, 0, d->nopts * sizeof(LinkList)); + } LASTALLOC; + ca_alloced = 1; + + memcpy(&ca_laststate, &state, sizeof(state)); + + if (!compwords[1]) { + ca_laststate.opt = ca_laststate.arg = 0; + + return; + } + for (line = compwords[1], cur = 2, state.curopt = NULL, state.def = NULL; + line; line = compwords[cur++]) { + ddef = adef = NULL; + doff = state.singles = 0; + if (state.def) { + state.arg = 0; + if (state.curopt) { + PERMALLOC { + addlinknode(state.oargs[state.curopt->num], ztrdup(line)); + } LASTALLOC; + } + state.opt = (state.def->type == CAA_OPT && line[0] && line[1]); + + if (state.def->type == CAA_REST || state.def->type == CAA_RARGS || + state.def->type == CAA_RREST) { + if (state.def->end && pattry(endpat, line)) { + state.def = NULL; + state.curopt = NULL; + continue; + } + } else if ((state.def = state.def->next)) + state.argbeg = cur; + else + state.curopt = NULL; + } else { + state.opt = (line[0] && line[1]); + state.arg = 1; + state.curopt = NULL; + } + pe = NULL; + + if (state.opt && (state.curopt = ca_get_opt(d, line, 0, &pe))) { + ddef = state.def = state.curopt->args; + doff = pe - line; + state.optbeg = state.argbeg = state.inopt = cur; + PERMALLOC { + state.oargs[state.curopt->num] = newlinklist(); + } LASTALLOC; + ca_inactive(d, state.curopt->xor); + + if (state.def && + (state.curopt->type == CAO_DIRECT || + (state.curopt->type == CAO_ODIRECT && pe[0]) || + (state.curopt->type == CAO_EQUAL && + (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; + PERMALLOC { + addlinknode(state.oargs[state.curopt->num], ztrdup(pe)); + } LASTALLOC; + } + if (!state.def) + state.curopt = NULL; + } else if (state.opt && d->single && + (state.curopt = ca_get_sopt(d, line, 0, &pe))) { + char *p; + Caopt tmpopt; + + ddef = state.def = state.curopt->args; + doff = pe - line; + state.optbeg = state.argbeg = state.inopt = cur; + state.singles = !*pe; + + for (p = line + 1; p <= pe; p++) { + if ((tmpopt = d->single[STOUC(*p)])) { + PERMALLOC { + state.oargs[tmpopt->num] = newlinklist(); + } LASTALLOC; + ca_inactive(d, tmpopt->xor); + } + } + if (state.def && + (state.curopt->type == CAO_DIRECT || + (state.curopt->type == CAO_ODIRECT && pe[0]) || + (state.curopt->type == CAO_EQUAL && + (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; + PERMALLOC { + addlinknode(state.oargs[state.curopt->num], ztrdup(pe)); + } LASTALLOC; + } + if (!state.def) + state.curopt = NULL; + } else if (state.arg) { + PERMALLOC { + addlinknode(state.args, ztrdup(line)); + } LASTALLOC; + if ((adef = state.def = ca_get_arg(d, state.nth)) && + (state.def->type == CAA_RREST || + state.def->type == CAA_RARGS)) { + state.inrest = 0; + for (; line; line = compwords[cur++]) { + PERMALLOC { + addlinknode(state.args, ztrdup(line)); + } LASTALLOC; + } + break; + } + if (state.inopt) { + state.inopt = 0; + state.nargbeg = cur - 1; + } + if (state.def && state.def->type != CAA_NORMAL && + state.def->type != CAA_OPT && state.inarg) { + state.restbeg = cur; + state.inarg = 0; + } else if (!state.def || state.def->type == CAA_NORMAL || + state.def->type == CAA_OPT) + state.inarg = 1; + state.nth++; + state.def = NULL; + } + if (state.def && state.curopt && + (state.def->type == CAA_RREST || state.def->type == CAA_RARGS)) { + if (state.def->end) + endpat = patcompile(state.def->end, 0, NULL); + else { + LinkList l = state.oargs[state.curopt->num]; + + for (; line; line = compwords[cur++]) { + PERMALLOC { + addlinknode(l, line); + } LASTALLOC; + } + break; + } + } + if (cur + 1 == compcurrent) { + memcpy(&ca_laststate, &state, sizeof(state)); + ca_laststate.ddef = NULL; + ca_laststate.doff = 0; + } else if (cur == compcurrent && !ca_laststate.def) { + if ((ca_laststate.def = ddef)) + ca_laststate.doff = doff; + else { + ca_laststate.def = adef; + ca_laststate.ddef = NULL; + ca_laststate.argbeg = state.nargbeg; + ca_laststate.optbeg = state.restbeg; + ca_laststate.singles = state.singles; + } + } + } +} + +static char * +ca_colonlist(LinkList l) +{ + if (l) { + LinkNode n; + int len = 1; + char *p, *ret, *q; + + for (n = firstnode(l); n; incnode(n)) + for (p = (char *) getdata(n); *p; p++) + len += (*p == ':' ? 2 : 1); + + ret = q = (char *) zalloc(len); + + for (n = firstnode(l); n; incnode(n)) + for (p = (char *) getdata(n); *p; p++) { + if (*p == ':') + *q++ = '\\'; + *q++ = *p; + } + *q = '\0'; + + return ret; + } else + return ztrdup(""); +} + +static int +bin_comparguments(char *nam, char **args, char *ops, int func) +{ + int min, max, n; + + if (incompfunc != 1) { + zerrnam(nam, "can only be called from completion function", NULL, 0); + return 1; + } + if (args[0][0] != '-' || !args[0][1] || args[0][2]) { + zerrnam(nam, "invalid argument: %s", args[0], 0); + return 1; + } + if (args[0][1] != 'i' && !ca_parsed) { + zerrnam(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 'O': min = 4; max = 4; break; + case 'L': min = 3; max = 3; break; + case 's': min = 1; max = 1; break; + case 'M': min = 1; max = 1; break; + case 'a': min = 0; max = 0; break; + case 'W': min = 2; max = 2; break; + default: + zerrnam(nam, "invalid option: %s", args[0], 0); + return 1; + } + n = arrlen(args) - 1; + if (n < min) { + zerrnam(nam, "not enough arguments", NULL, 0); + return 1; + } else if (max >= 0 && n > max) { + zerrnam(nam, "too many arguments", NULL, 0); + return 1; + } + switch (args[0][1]) { + case 'i': + if (compcurrent > 1 && compwords[0]) { + Cadef def = get_cadef(nam, args + 1); + int cap = ca_parsed; + + ca_parsed = 0; + + if (!def) + return 1; + + ca_parsed = cap; + ca_parse_line(def); + ca_parsed = 1; + + return 0; + } + return 1; + + case 'D': + { + Caarg arg = ca_laststate.def; + + if (arg) { + setsparam(args[1], ztrdup(arg->descr)); + setsparam(args[2], ztrdup(arg->action)); + + ignore_prefix(ca_laststate.doff); + if (arg->type == CAA_RARGS) + restrict_range(ca_laststate.argbeg - 1, + arrlen(compwords) - 1); + else if (arg->type == CAA_RREST) + restrict_range(ca_laststate.optbeg - 1, + arrlen(compwords) - 1); + return 0; + } + return 1; + } + case 'O': + if (ca_laststate.opt) { + LinkList next = newlinklist(); + LinkList direct = newlinklist(); + LinkList odirect = newlinklist(); + LinkList equal = newlinklist(), l; + Caopt p; + char *str; + + for (p = ca_laststate.d->opts; p; p = p->next) { + if (p->active) { + switch (p->type) { + case CAO_NEXT: l = next; break; + case CAO_DIRECT: l = direct; break; + case CAO_ODIRECT: l = odirect; break; + default: l = equal; break; + } + if (p->descr) { + int len = strlen(p->name) + strlen(p->descr) + 2; + + str = (char *) zhalloc(len); + strcpy(str, p->name); + strcat(str, ":"); + strcat(str, p->descr); + } else + str = p->name; + addlinknode(l, str); + } + } + set_list_array(args[1], next); + set_list_array(args[2], direct); + set_list_array(args[3], odirect); + set_list_array(args[4], equal); + + return 0; + } else + return 1; + case 'L': + { + Caopt opt = ca_get_opt(ca_laststate.d, args[1], 1, NULL); + + if (opt && opt->args) { + setsparam(args[2], ztrdup(opt->args->descr)); + setsparam(args[3], ztrdup(opt->args->action)); + + return 0; + } + return 1; + } + case 's': + if (ca_laststate.d->single && ca_laststate.singles) { + setsparam(args[1], + ztrdup(ca_laststate.ddef ? + (ca_laststate.ddef->type == CAO_DIRECT ? + "direct" : + (ca_laststate.ddef->type == CAO_EQUAL ? + "equal" : "next")) : "")); + return 0; + } else + return 1; + case 'M': + setsparam(args[1], ztrdup(ca_laststate.d->match)); + return 0; + case 'a': + return !(ca_laststate.d->args || ca_laststate.d->rest); + case 'W': + { + char **ret, **p; + LinkNode n; + LinkList *a; + Caopt o; + int num; + + ret = p = zalloc((countlinknodes(ca_laststate.args) + 1) * + sizeof(char *)); + + for (n = firstnode(ca_laststate.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; + + 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); + } + } + *p = NULL; + + sethparam(args[2], ret); + } + return 0; + } + return 1; +} + +/* Help for `_values'. */ + +typedef struct cvdef *Cvdef; +typedef struct cvval *Cvval; + +struct cvdef { + char *descr; + int hassep; + char sep; + Cvdef next; + Cvval vals; + char **defs; + int ndefs; + int lastt; +}; + +struct cvval { + Cvval next; + char *name; + char *descr; + char **xor; + int type; + Caarg arg; + int active; +}; + +#define CVV_NOARG 0 +#define CVV_ARG 1 +#define CVV_OPT 2 + +#define MAX_CVCACHE 8 +static Cvdef cvdef_cache[MAX_CVCACHE]; + +static void +free_cvdef(Cvdef d) +{ + if (d) { + Cvval p, n; + + zsfree(d->descr); + freearray(d->defs); + + for (p = d->vals; p; p = n) { + n = p->next; + zsfree(p->name); + zsfree(p->descr); + freearray(p->xor); + free_caargs(p->arg); + zfree(p, sizeof(*p)); + } + zfree(d, sizeof(*d)); + } +} + +static Cvdef +parse_cvdef(char *nam, char **args) +{ + Cvdef ret; + Cvval val, *valp; + Caarg arg; + char **oargs = args, sep, *name, *descr, *p, *q, **xor, c; + int xnum, multi, vtype, hassep; + + if (args[0][0] == '-' && args[0][1] == 's' && !args[0][2]) { + if (args[1][0] && args[1][1]) { + zerrnam(nam, "invalid separator: %s", args[1], 0); + return NULL; + } + hassep = 1; + sep = args[1][0]; + args += 2; + } + if (!args[0] || !args[1]) { + zerrnam(nam, "not enough arguments", NULL, 0); + return NULL; + } + descr = *args++; + + PERMALLOC { + ret = (Cvdef) zalloc(sizeof(*ret)); + ret->descr = ztrdup(descr); + ret->hassep = hassep; + ret->sep = sep; + ret->next = NULL; + ret->vals = NULL; + ret->defs = arrdup(oargs); + ret->ndefs = arrlen(oargs); + ret->lastt = time(0); + } LASTALLOC; + + for (valp = &(ret->vals); *args; args++) { + p = dupstring(*args); + xnum = 0; + if (*p == '(') { + LinkList list = newlinklist(); + LinkNode node; + char **xp, sav; + + while (*p && *p != ')') { + for (p++; inblank(*p); p++); + + if (*p == ')') + break; + for (q = p++; *p && *p != ')' && !inblank(*p); p++); + + if (!*p) + break; + + sav = *p; + *p = '\0'; + addlinknode(list, dupstring(q)); + xnum++; + *p = sav; + } + if (*p != ')') { + free_cvdef(ret); + zerrnam(nam, "invalid argument: %s", *args, 0); + return NULL; + } + xor = (char **) zalloc((xnum + 2) * sizeof(char *)); + for (node = firstnode(list), xp = xor; node; incnode(node), xp++) + *xp = ztrdup((char *) getdata(node)); + xp[0] = xp[1] = NULL; + + p++; + } else + xor = NULL; + + if ((multi = (*p == '*'))) + p++; + + for (name = p; *p && *p != ':' && *p != '['; p++) + if (*p == '\\' && p[1]) + p++; + + if (hassep && !sep && name + 1 != p) { + free_cvdef(ret); + zerrnam(nam, "no multi-letter values with empty separator allowed", NULL, 0); + return NULL; + } + if ((c = *p) == '[') { + *p = '\0'; + for (descr = ++p; *p && *p != ']'; p++) + if (*p == '\\' && p[1]) + p++; + + if (!*p) { + free_cvdef(ret); + zerrnam(nam, "invalid value definition: %s", *args, 0); + return NULL; + } + *p++ = '\0'; + c = *p; + } else { + *p = '\0'; + descr = NULL; + } + if (c && c != ':') { + free_cvdef(ret); + zerrnam(nam, "invalid value definition: %s", *args, 0); + return NULL; + } + if (!multi) { + if (!xor) { + xor = (char **) zalloc(2 * sizeof(char *)); + xor[1] = NULL; + } + xor[xnum] = ztrdup(name); + } + if (c == ':') { + if (hassep && !sep) { + free_cvdef(ret); + zerrnam(nam, "no value with argument with empty separator allowed", NULL, 0); + return NULL; + } + if (*++p == ':') { + p++; + vtype = CVV_OPT; + } else + vtype = CVV_ARG; + arg = parse_caarg(0, 0, 0, &p); + } else { + vtype = CVV_NOARG; + arg = NULL; + } + PERMALLOC { + *valp = val = (Cvval) zalloc(sizeof(*val)); + valp = &((*valp)->next); + + val->next = NULL; + val->name = ztrdup(name); + val->descr = ztrdup(descr); + val->xor = xor; + val->type = vtype; + val->arg = arg; + } LASTALLOC; + } + return ret; +} + +static Cvdef +get_cvdef(char *nam, char **args) +{ + Cvdef *p, *min, new; + int i, na = arrlen(args); + + for (i = MAX_CVCACHE, p = cvdef_cache, min = NULL; *p && i--; p++) + if (*p && na == (*p)->ndefs && arrcmp(args, (*p)->defs)) { + (*p)->lastt = time(0); + + return *p; + } else if (!min || !*p || (*p)->lastt < (*min)->lastt) + min = p; + if (i) + min = p; + if ((new = parse_cvdef(nam, args))) { + free_cvdef(*min); + *min = new; + } + return new; +} + +static Cvval +cv_get_val(Cvdef d, char *name) +{ + Cvval p; + + for (p = d->vals; p; p = p->next) + if (!strcmp(name, p->name)) + return p; + + return NULL; +} + +static void +cv_inactive(Cvdef d, char **xor) +{ + if (xor) { + Cvval val; + + for (; *xor; xor++) + if ((val = cv_get_val(d, *xor))) + val->active = 0; + } +} + +struct cvstate { + Cvdef d; + Caarg def; + Cvval val; + LinkList vals; +}; + +static struct cvstate cv_laststate; +static int cv_parsed = 0, cv_alloced = 0; + +static void +cv_parse_word(Cvdef d) +{ + Cvval ptr; + struct cvstate state; + char *str, *eq; + + if (cv_alloced) + freelinklist(cv_laststate.vals, freestr); + + for (ptr = d->vals; ptr; ptr = ptr->next) + ptr->active = 1; + + state.d = d; + state.def = NULL; + state.val = NULL; + PERMALLOC { + state.vals = (LinkList) newlinklist(); + } LASTALLOC; + 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))) { + PERMALLOC { + addlinknode(state.vals, ztrdup(str)); + addlinknode(state.vals, ztrdup(eq)); + } LASTALLOC; + + 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))) { + PERMALLOC { + addlinknode(state.vals, ztrdup(str)); + addlinknode(state.vals, ztrdup(eq)); + } LASTALLOC; + + 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))) { + PERMALLOC { + addlinknode(state.vals, ztrdup(tmp)); + addlinknode(state.vals, ztrdup("")); + } LASTALLOC; + + cv_inactive(d, ptr->xor); + } + } + for (str = compsuffix; *str; str++) { + tmp[0] = *str; + if ((ptr = cv_get_val(d, tmp))) { + PERMALLOC { + addlinknode(state.vals, ztrdup(tmp)); + addlinknode(state.vals, ztrdup("")); + } LASTALLOC; + + cv_inactive(d, ptr->xor); + } + } + ignore_prefix(strlen(compprefix)); + ignore_suffix(strlen(compsuffix)); + } + } + 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] = '='; + } + memcpy(&cv_laststate, &state, sizeof(state)); +} + +static int +bin_compvalues(char *nam, char **args, char *ops, int func) +{ + int min, max, n; + + if (incompfunc != 1) { + zerrnam(nam, "can only be called from completion function", NULL, 0); + return 1; + } + if (args[0][0] != '-' || !args[0][1] || args[0][2]) { + zerrnam(nam, "invalid argument: %s", args[0], 0); + return 1; + } + if (args[0][1] != 'i' && !cv_parsed) { + zerrnam(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 'V': min = 3; max = 3; break; + case 's': min = 1; max = 1; break; + case 'd': min = 1; max = 1; break; + case 'L': min = 3; max = 3; break; + case 'v': min = 1; max = 1; break; + default: + zerrnam(nam, "invalid option: %s", args[0], 0); + return 1; + } + n = arrlen(args) - 1; + if (n < min) { + zerrnam(nam, "not enough arguments", NULL, 0); + return 1; + } else if (max >= 0 && n > max) { + zerrnam(nam, "too many arguments", NULL, 0); + return 1; + } + switch (args[0][1]) { + case 'i': + { + Cvdef def = get_cvdef(nam, args + 1); + int cvp = cv_parsed; + + cv_parsed = 0; + + if (!def) + return 1; + + cv_parsed = cvp; + cv_parse_word(def); + cv_parsed = 1; + + return 0; + } + return 1; + + case 'D': + { + Caarg arg = cv_laststate.def; + + if (arg) { + setsparam(args[1], ztrdup(arg->descr)); + setsparam(args[2], ztrdup(arg->action)); + + return 0; + } + return 1; + } + case 'V': + { + LinkList noarg = newlinklist(); + LinkList arg = newlinklist(); + LinkList opt = newlinklist(), l; + Cvval p; + char *str; + + for (p = cv_laststate.d->vals; p; p = p->next) { + if (p->active) { + switch (p->type) { + case CVV_NOARG: l = noarg; break; + case CVV_ARG: l = arg; break; + default: l = opt; break; + } + if (p->descr) { + int len = strlen(p->name) + strlen(p->descr) + 2; + + str = (char *) zhalloc(len); + strcpy(str, p->name); + strcat(str, ":"); + strcat(str, p->descr); + } else + str = p->name; + addlinknode(l, str); + } + } + set_list_array(args[1], noarg); + set_list_array(args[2], arg); + set_list_array(args[3], opt); + + return 0; + } + case 's': + if (cv_laststate.d->hassep) { + char tmp[2]; + + tmp[0] = cv_laststate.d->sep; + tmp[1] = '\0'; + setsparam(args[1], ztrdup(tmp)); + + return 0; + } + return 1; + case 'd': + setsparam(args[1], ztrdup(cv_laststate.d->descr)); + return 0; + case 'L': + { + Cvval val = cv_get_val(cv_laststate.d, args[1]); + + if (val && val->arg) { + setsparam(args[2], val->arg->descr); + setsparam(args[3], val->arg->action); + + return 0; + } + return 1; + } + case 'v': + if (cv_laststate.vals) { + char **ret, **p; + LinkNode n; + + ret = (char **) zalloc((countlinknodes(cv_laststate.vals) + 1) * + sizeof(char *)); + + for (n = firstnode(cv_laststate.vals), p = ret; n; incnode(n), p++) + *p = ztrdup((char *) getdata(n)); + *p = NULL; + + sethparam(args[1], ret); + + return 0; + } + return 1; + } + return 1; +} + + +static struct builtin bintab[] = { + BUILTIN("compdisplay", 0, bin_compdisplay, 2, -1, 0, NULL, NULL), + BUILTIN("compdescribe", 0, bin_compdescribe, 3, -1, 0, NULL, NULL), + BUILTIN("comparguments", 0, bin_comparguments, 1, -1, 0, NULL, NULL), + BUILTIN("compvalues", 0, bin_compvalues, 1, -1, 0, NULL, NULL), +}; + + +/**/ +int +setup_computil(Module m) +{ + memset(cadef_cache, 0, sizeof(cadef_cache)); + memset(cvdef_cache, 0, sizeof(cvdef_cache)); + + return 0; +} + +/**/ +int +boot_computil(Module m) +{ + return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); +} + +#ifdef MODULE + +/**/ +int +cleanup_computil(Module m) +{ + deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); + return 0; +} + +/**/ +int +finish_computil(Module m) +{ + int i; + + for (i = 0; i < MAX_CACACHE; i++) + free_cadef(cadef_cache[i]); + for (i = 0; i < MAX_CVCACHE; i++) + free_cvdef(cvdef_cache[i]); + + return 0; +} + +#endif diff --git a/Src/Zle/computil.mdd b/Src/Zle/computil.mdd new file mode 100644 index 000000000..a1453ae8a --- /dev/null +++ b/Src/Zle/computil.mdd @@ -0,0 +1,3 @@ +moddeps="compctl zle" + +objects="computil.o" |