mirror of
git://git.code.sf.net/p/zsh/code
synced 2024-10-03 01:11:37 +02:00
1892 lines
38 KiB
C
1892 lines
38 KiB
C
/*
|
|
* 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
|