summaryrefslogtreecommitdiff
path: root/Src/Zle/computil.c
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>1999-10-12 09:33:05 +0000
committerTanaka Akira <akr@users.sourceforge.net>1999-10-12 09:33:05 +0000
commita564f353d9f0e45d35cfbdee8f4340deae5b8691 (patch)
treea34161d456c2dd732112fc8e5fae25c6d9246743 /Src/Zle/computil.c
parentd3170c8ee6eb6aa6050d6531536e5e9fba278b57 (diff)
downloadzsh-a564f353d9f0e45d35cfbdee8f4340deae5b8691.tar.gz
zsh-a564f353d9f0e45d35cfbdee8f4340deae5b8691.zip
Initial revision
Diffstat (limited to 'Src/Zle/computil.c')
-rw-r--r--Src/Zle/computil.c1891
1 files changed, 1891 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