mirror of
git://git.code.sf.net/p/zsh/code
synced 2024-11-19 21:44:11 +01:00
3709 lines
81 KiB
C
3709 lines
81 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'. */
|
|
|
|
/* Calculation state. */
|
|
|
|
typedef struct cdisp *Cdisp;
|
|
|
|
struct cdisp {
|
|
int pre; /* prefix length */
|
|
int suf; /* suffix length */
|
|
int colon; /* number of strings with descriptions */
|
|
};
|
|
|
|
/* Calculate longest prefix and suffix and count the strings with
|
|
* descriptions. */
|
|
|
|
static void
|
|
cdisp_calc(Cdisp disp, char **args)
|
|
{
|
|
char *cp;
|
|
int i, nbc;
|
|
|
|
for (; *args; args++) {
|
|
for (nbc = 0, cp = *args; *cp && *cp != ':'; cp++)
|
|
if (*cp == '\\' && cp[1])
|
|
cp++, nbc++;
|
|
if (*cp == ':' && cp[1]) {
|
|
disp->colon++;
|
|
if ((i = cp - *args - nbc) > disp->pre)
|
|
disp->pre = i;
|
|
if ((i = strlen(cp + 1)) > disp->suf)
|
|
disp->suf = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Help fuer `_describe'. */
|
|
|
|
typedef struct cdset *Cdset;
|
|
|
|
struct cdstate {
|
|
int showd; /* != 0 if descriptions should be shown */
|
|
char *sep; /* the separator string */
|
|
Cdset sets; /* the sets of matches */
|
|
struct cdisp disp; /* used to calculate the alignment */
|
|
};
|
|
|
|
struct cdset {
|
|
Cdset next; /* guess what */
|
|
char **opts; /* the compadd-options */
|
|
char **strs; /* the display-strings */
|
|
char **matches; /* the matches (or NULL) */
|
|
};
|
|
|
|
static struct cdstate cd_state;
|
|
static int cd_parsed = 0;
|
|
|
|
static void
|
|
freecdsets(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));
|
|
}
|
|
}
|
|
|
|
/* Initialisation. Store and calculate the string and matches and so on. */
|
|
|
|
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);
|
|
freecdsets(cd_state.sets);
|
|
cd_parsed = 0;
|
|
}
|
|
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))) {
|
|
zwarnnam(nam, "invalid argument: %s", *args, 0);
|
|
return 1;
|
|
}
|
|
set->strs = zarrdup(ap);
|
|
|
|
if (disp)
|
|
cdisp_calc(&(cd_state.disp), set->strs);
|
|
|
|
if (*++args && **args != '-') {
|
|
if (!(ap = get_user_var(*args))) {
|
|
zwarnnam(nam, "invalid argument: %s", *args, 0);
|
|
return 1;
|
|
}
|
|
set->matches = zarrdup(ap);
|
|
args++;
|
|
}
|
|
for (ap = args; *args &&
|
|
(args[0][0] != '-' || args[0][1] != '-' || args[0][2]);
|
|
args++);
|
|
|
|
tmp = *args;
|
|
*args = NULL;
|
|
set->opts = zarrdup(ap);
|
|
if ((*args = tmp))
|
|
args++;
|
|
}
|
|
cd_parsed = 1;
|
|
return 0;
|
|
}
|
|
|
|
/* Get the next set. */
|
|
|
|
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, *copy, *cpp, oldc;
|
|
int dl = 1, sl = 1, sepl = strlen(cd_state.sep);
|
|
int pre = cd_state.disp.pre, suf = cd_state.disp.suf;
|
|
VARARR(char, buf, pre + suf + sepl + 1);
|
|
|
|
for (p = set->strs; *p; p++)
|
|
if (cd_state.showd) {
|
|
for (cp = *p; *cp && *cp != ':'; cp++)
|
|
if (*cp == '\\' && cp[1])
|
|
cp++;
|
|
if (*cp == ':' && cp[1])
|
|
dl++;
|
|
else
|
|
sl++;
|
|
} else
|
|
sl++;
|
|
|
|
sd = (char **) zalloc(dl * sizeof(char *));
|
|
ss = (char **) zalloc(sl * sizeof(char *));
|
|
md = (char **) zalloc(dl * sizeof(char *));
|
|
ms = (char **) zalloc(sl * sizeof(char *));
|
|
|
|
if (cd_state.showd) {
|
|
memcpy(buf + pre, cd_state.sep, sepl);
|
|
suf = pre + sepl;
|
|
}
|
|
|
|
/* Build the aligned display strings. */
|
|
|
|
for (sdp = sd, ssp = ss, mdp = md, msp = ms,
|
|
p = set->strs, mp = set->matches; *p; p++) {
|
|
copy = dupstring(*p);
|
|
for (cp = cpp = copy; *cp && *cp != ':'; cp++) {
|
|
if (*cp == '\\' && cp[1])
|
|
cp++;
|
|
*cpp++ = *cp;
|
|
}
|
|
oldc = *cpp;
|
|
*cpp = '\0';
|
|
if (((cpp == cp && oldc == ':') || *cp == ':') && cp[1] &&
|
|
cd_state.showd) {
|
|
memset(buf, ' ', pre);
|
|
memcpy(buf, copy, (cpp - copy));
|
|
strcpy(buf + suf, cp + 1);
|
|
*sdp++ = ztrdup(buf);
|
|
if (mp) {
|
|
*mdp++ = ztrdup(*mp);
|
|
if (*mp)
|
|
mp++;
|
|
} else
|
|
*mdp++ = ztrdup(copy);
|
|
} else {
|
|
*ssp++ = ztrdup(copy);
|
|
if (mp) {
|
|
*msp++ = ztrdup(*mp);
|
|
if (*mp)
|
|
mp++;
|
|
} else
|
|
*msp++ = ztrdup(copy);
|
|
}
|
|
}
|
|
*sdp = *ssp = *mdp = *msp = NULL;
|
|
|
|
p = zarrdup(set->opts);
|
|
|
|
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;
|
|
freecdsets(set);
|
|
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/**/
|
|
static int
|
|
bin_compdescribe(char *nam, char **args, char *ops, int func)
|
|
{
|
|
if (incompfunc != 1) {
|
|
zwarnnam(nam, "can only be called from completion function", NULL, 0);
|
|
return 1;
|
|
}
|
|
if (!args[0][0] || !args[0][1] || args[0][2]) {
|
|
zwarnnam(nam, "invalid argument: %s", args[0], 0);
|
|
return 1;
|
|
}
|
|
switch (args[0][1]) {
|
|
case 'i':
|
|
return cd_init(nam, "", args + 1, 0);
|
|
case 'I':
|
|
return cd_init(nam, args[1], args + 2, 1);
|
|
case 'g':
|
|
if (cd_parsed) {
|
|
int n = arrlen(args);
|
|
|
|
if (n != 6) {
|
|
zwarnnam(nam, (n < 6 ? "not enough arguments" :
|
|
"too many arguments"), NULL, 0);
|
|
return 1;
|
|
}
|
|
return cd_get(args + 1);
|
|
} else {
|
|
zwarnnam(nam, "no parsed state", NULL, 0);
|
|
return 1;
|
|
}
|
|
}
|
|
zwarnnam(nam, "invalid option: %s", args[0], 0);
|
|
return 1;
|
|
}
|
|
|
|
/* Help for `_arguments'. */
|
|
|
|
typedef struct cadef *Cadef;
|
|
typedef struct caopt *Caopt;
|
|
typedef struct caarg *Caarg;
|
|
|
|
/* Cache for a set of _arguments-definitions. */
|
|
|
|
struct cadef {
|
|
Cadef next; /* next in cache */
|
|
Cadef snext; /* next set */
|
|
Caopt opts; /* the options */
|
|
int nopts, ndopts, nodopts; /* number of options/direct/optional direct */
|
|
Caarg args; /* the normal arguments */
|
|
Caarg rest; /* the rest-argument */
|
|
char **defs; /* the original strings */
|
|
int ndefs; /* number of ... */
|
|
int lastt; /* last time this was used */
|
|
Caopt *single; /* array of single-letter options */
|
|
char *match; /* -M spec to use */
|
|
int argsactive; /* if arguments are still allowed */
|
|
/* used while parsing a command line */
|
|
char *set; /* set name prefix (<name>-), shared */
|
|
char *sname; /* set name */
|
|
int flags; /* see CDF_* below */
|
|
char *nonarg; /* pattern for non-args (-A argument) */
|
|
};
|
|
|
|
#define CDF_SEP 1
|
|
|
|
/* Description for an option. */
|
|
|
|
struct caopt {
|
|
Caopt next;
|
|
char *name; /* option name */
|
|
char *descr; /* the description */
|
|
char **xor; /* if this, then not ... */
|
|
int type; /* type, CAO_* */
|
|
Caarg args; /* option arguments */
|
|
int active; /* still allowed on command line */
|
|
int num; /* it's the num'th option */
|
|
char *set; /* set name, shared */
|
|
int not; /* don't complete this option (`!...') */
|
|
};
|
|
|
|
#define CAO_NEXT 1
|
|
#define CAO_DIRECT 2
|
|
#define CAO_ODIRECT 3
|
|
#define CAO_EQUAL 4
|
|
#define CAO_OEQUAL 5
|
|
|
|
/* Description for an argument */
|
|
|
|
struct caarg {
|
|
Caarg next;
|
|
char *descr; /* description */
|
|
char **xor; /* if this, then not ... */
|
|
char *action; /* what to do for it */
|
|
int type; /* CAA_* below */
|
|
char *end; /* end-pattern for ::<pat>:... */
|
|
char *opt; /* option name if for an option */
|
|
int num; /* it's the num'th argument */
|
|
int min; /* it's also this argument, using opt. args */
|
|
int direct; /* number was given directly */
|
|
int active; /* still allowed on command line */
|
|
char *set; /* set name, shared */
|
|
};
|
|
|
|
#define CAA_NORMAL 1
|
|
#define CAA_OPT 2
|
|
#define CAA_REST 3
|
|
#define CAA_RARGS 4
|
|
#define CAA_RREST 5
|
|
|
|
/* The cache of parsed descriptons. */
|
|
|
|
#define MAX_CACACHE 8
|
|
static Cadef cadef_cache[MAX_CACACHE];
|
|
|
|
/* Compare two arrays of strings for equality. */
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
/* Memory stuff. Obviously. */
|
|
|
|
static void
|
|
freecaargs(Caarg a)
|
|
{
|
|
Caarg n;
|
|
|
|
for (; a; a = n) {
|
|
n = a->next;
|
|
zsfree(a->descr);
|
|
if (a->xor)
|
|
freearray(a->xor);
|
|
zsfree(a->action);
|
|
zsfree(a->end);
|
|
zsfree(a->opt);
|
|
zfree(a, sizeof(*a));
|
|
}
|
|
}
|
|
|
|
static void
|
|
freecadef(Cadef d)
|
|
{
|
|
Cadef s;
|
|
Caopt p, n;
|
|
|
|
while (d) {
|
|
s = d->snext;
|
|
zsfree(d->match);
|
|
zsfree(d->set);
|
|
zsfree(d->sname);
|
|
if (d->defs)
|
|
freearray(d->defs);
|
|
|
|
for (p = d->opts; p; p = n) {
|
|
n = p->next;
|
|
zsfree(p->name);
|
|
zsfree(p->descr);
|
|
if (p->xor)
|
|
freearray(p->xor);
|
|
freecaargs(p->args);
|
|
zfree(p, sizeof(*p));
|
|
}
|
|
freecaargs(d->args);
|
|
freecaargs(d->rest);
|
|
zsfree(d->nonarg);
|
|
if (d->single)
|
|
zfree(d->single, 256 * sizeof(Caopt));
|
|
zfree(d, sizeof(*d));
|
|
d = s;
|
|
}
|
|
}
|
|
|
|
/* Remove backslashes before colons. */
|
|
|
|
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;
|
|
}
|
|
|
|
/* Add backslashes before colons. */
|
|
|
|
static char *
|
|
bslashcolon(char *s)
|
|
{
|
|
char *p, *r;
|
|
|
|
r = p = zhalloc((2 * strlen(s)) + 1);
|
|
|
|
while (*s) {
|
|
if (*s == ':')
|
|
*p++ = '\\';
|
|
*p++ = *s++;
|
|
}
|
|
*p = '\0';
|
|
|
|
return r;
|
|
}
|
|
|
|
/* Parse an argument definition. */
|
|
|
|
static Caarg
|
|
parse_caarg(int mult, int type, int num, int opt, char *oname, char **def,
|
|
char *set)
|
|
{
|
|
Caarg ret = (Caarg) zalloc(sizeof(*ret));
|
|
char *p = *def, *d, sav;
|
|
|
|
ret->next = NULL;
|
|
ret->descr = ret->action = ret->end = NULL;
|
|
ret->xor = NULL;
|
|
ret->num = num;
|
|
ret->min = num - opt;
|
|
ret->type = type;
|
|
ret->opt = ztrdup(oname);
|
|
ret->direct = 0;
|
|
ret->set = set;
|
|
|
|
/* Get the description. */
|
|
|
|
for (d = p; *p && *p != ':'; p++)
|
|
if (*p == '\\' && p[1])
|
|
p++;
|
|
sav = *p;
|
|
*p = '\0';
|
|
ret->descr = ztrdup(rembslashcolon(d));
|
|
|
|
/* Get the action if there is one. */
|
|
|
|
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));
|
|
} else
|
|
ret->action = ztrdup("");
|
|
*def = p;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static Cadef
|
|
alloc_cadef(char **args, int single, char *match, char *nonarg, int flags)
|
|
{
|
|
Cadef ret;
|
|
|
|
ret = (Cadef) zalloc(sizeof(*ret));
|
|
ret->next = ret->snext = NULL;
|
|
ret->opts = NULL;
|
|
ret->args = ret->rest = NULL;
|
|
ret->nonarg = ztrdup(nonarg);
|
|
if (args) {
|
|
ret->defs = zarrdup(args);
|
|
ret->ndefs = arrlen(args);
|
|
} else {
|
|
ret->defs = NULL;
|
|
ret->ndefs = 0;
|
|
}
|
|
ret->lastt = time(0);
|
|
ret->set = ret->sname = NULL;
|
|
if (single) {
|
|
ret->single = (Caopt *) zalloc(256 * sizeof(Caopt));
|
|
memset(ret->single, 0, 256 * sizeof(Caopt));
|
|
} else
|
|
ret->single = NULL;
|
|
ret->match = ztrdup(match);
|
|
ret->flags = flags;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
set_cadef_opts(Cadef def)
|
|
{
|
|
Caarg argp;
|
|
int xnum;
|
|
|
|
for (argp = def->args, xnum = 0; argp; argp = argp->next) {
|
|
if (!argp->direct)
|
|
argp->min = argp->num - xnum;
|
|
if (argp->type == CAA_OPT)
|
|
xnum++;
|
|
}
|
|
}
|
|
|
|
/* Parse an array of definitions. */
|
|
|
|
static Cadef
|
|
parse_cadef(char *nam, char **args)
|
|
{
|
|
Cadef all, ret;
|
|
Caopt *optp;
|
|
char **oargs = args, *p, *q, *match = "r:|[_-]=* r:|=*", **xor, **sargs;
|
|
char *adpre, *adsuf, *axor = NULL, *doset = NULL, **setp = NULL;
|
|
char *nonarg = NULL;
|
|
int single = 0, anum = 1, xnum, nopts, ndopts, nodopts, flags = 0;
|
|
int state = 0, not = 0;
|
|
|
|
nopts = ndopts = nodopts = 0;
|
|
|
|
/* First string is the auto-description definition. */
|
|
|
|
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;
|
|
|
|
/* Now get the -s, -A, -S and -M options. */
|
|
|
|
args++;
|
|
while ((p = *args) && *p == '-' && p[1]) {
|
|
for (q = ++p; *q; q++)
|
|
if (*q == 'M' || *q == 'A') {
|
|
q = "";
|
|
break;
|
|
} else if (*q != 's' && *q != 'S')
|
|
break;
|
|
|
|
if (*q)
|
|
break;
|
|
|
|
for (; *p; p++) {
|
|
if (*p == 's')
|
|
single = 1;
|
|
else if (*p == 'S')
|
|
flags |= CDF_SEP;
|
|
else if (*p == 'A') {
|
|
if (p[1]) {
|
|
nonarg = p + 1;
|
|
p = "" - 1;
|
|
} else if (args[1])
|
|
nonarg = *++args;
|
|
else
|
|
break;
|
|
} else if (*p == 'M') {
|
|
if (p[1]) {
|
|
match = p + 1;
|
|
p = "" - 1;
|
|
} else if (args[1])
|
|
match = *++args;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
if (*p)
|
|
break;
|
|
|
|
args++;
|
|
}
|
|
if (!*args)
|
|
return NULL;
|
|
|
|
if (nonarg)
|
|
tokenize(nonarg = dupstring(nonarg));
|
|
|
|
/* Looks good. Optimistically allocate the cadef structure. */
|
|
|
|
all = ret = alloc_cadef(oargs, single, match, nonarg, flags);
|
|
optp = &(ret->opts);
|
|
anum = 1;
|
|
|
|
sargs = args;
|
|
|
|
/* Get the definitions. */
|
|
|
|
for (; *args; args++) {
|
|
if (args[0][0] == '-' && !args[0][1] && args[1]) {
|
|
if (!state) {
|
|
char *p;
|
|
int l;
|
|
|
|
if (setp)
|
|
args = setp;
|
|
p = *++args;
|
|
l = strlen(p) - 1;
|
|
if (*p == '(' && p[l] == ')') {
|
|
axor = p = dupstring(p + 1);
|
|
p[l - 1] = '\0';
|
|
} else
|
|
axor = NULL;
|
|
ret->set = doset = tricat(p, "-", "");
|
|
ret->sname = ztrdup(p);
|
|
state = 1;
|
|
} else {
|
|
setp = args;
|
|
state = 0;
|
|
args = sargs - 1;
|
|
doset = NULL;
|
|
ret->nopts = nopts;
|
|
ret->ndopts = ndopts;
|
|
ret->nodopts = nodopts;
|
|
set_cadef_opts(ret);
|
|
ret = ret->snext = alloc_cadef(NULL, single, NULL, nonarg, flags);
|
|
optp = &(ret->opts);
|
|
nopts = ndopts = nodopts = 0;
|
|
anum = 1;
|
|
}
|
|
continue;
|
|
}
|
|
p = dupstring(*args);
|
|
xnum = 0;
|
|
if ((not = (*p == '!')))
|
|
p++;
|
|
if (*p == '(') {
|
|
/* There is a xor list, get it. */
|
|
|
|
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;
|
|
}
|
|
/* Oops, end-of-string. */
|
|
if (*p != ')') {
|
|
freecadef(all);
|
|
zwarnnam(nam, "invalid argument: %s", *args, 0);
|
|
return NULL;
|
|
}
|
|
if (doset && axor)
|
|
xnum++;
|
|
xor = (char **) zalloc((xnum + 2) * sizeof(char *));
|
|
for (node = firstnode(list), xp = xor; node; incnode(node), xp++)
|
|
*xp = ztrdup((char *) getdata(node));
|
|
if (doset && axor)
|
|
*xp++ = ztrdup(axor);
|
|
xp[0] = xp[1] = NULL;
|
|
|
|
p++;
|
|
} else if (doset && axor) {
|
|
xnum = 1;
|
|
xor = (char **) zalloc(3 * sizeof(char *));
|
|
xor[0] = ztrdup(axor);
|
|
xor[1] = xor[2] = NULL;
|
|
} else
|
|
xor = NULL;
|
|
|
|
if (*p == '-' || *p == '+' ||
|
|
(*p == '*' && (p[1] == '-' || p[1] == '+'))) {
|
|
/* It's an option. */
|
|
Caopt opt;
|
|
Caarg oargs = NULL;
|
|
int multi, otype = CAO_NEXT, again = 0;
|
|
char *name, *descr, c, *againp = NULL;
|
|
|
|
rec:
|
|
|
|
/* Allowed more than once? */
|
|
if ((multi = (*p == '*')))
|
|
p++;
|
|
|
|
if (((p[0] == '-' && p[1] == '+') ||
|
|
(p[0] == '+' && p[1] == '-')) &&
|
|
p[2] && p[2] != ':' && p[2] != '[' &&
|
|
p[2] != '=' && p[2] != '-' && p[2] != '+') {
|
|
/* It's a -+ or +- definition. We just execute the whole
|
|
* stuff twice for such things. */
|
|
againp = dupstring(p);
|
|
name = ++p;
|
|
*p = (again ? '-' : '+');
|
|
again++;
|
|
} else {
|
|
name = p;
|
|
/* If it's a long option skip over the first `-'. */
|
|
if (p[0] == '-' && p[1] == '-')
|
|
p++;
|
|
}
|
|
if (!p[1]) {
|
|
freecadef(all);
|
|
zwarnnam(nam, "invalid argument: %s", *args, 0);
|
|
return NULL;
|
|
}
|
|
|
|
/* Skip over the name. */
|
|
for (p++; *p && *p != ':' && *p != '[' &&
|
|
((*p != '-' && *p != '+') ||
|
|
(p[1] != ':' && p[1] != '[')) &&
|
|
(*p != '=' ||
|
|
(p[1] != ':' && p[1] != '[' && p[1] != '-')); p++)
|
|
if (*p == '\\' && p[1])
|
|
p++;
|
|
|
|
/* The character after the option name specifies the type. */
|
|
c = *p;
|
|
*p = '\0';
|
|
if (c == '-') {
|
|
otype = CAO_DIRECT;
|
|
c = *++p;
|
|
} else if (c == '+') {
|
|
otype = CAO_ODIRECT;
|
|
c = *++p;
|
|
} else if (c == '=') {
|
|
otype = CAO_OEQUAL;
|
|
if ((c = *++p) == '-') {
|
|
otype = CAO_EQUAL;
|
|
c = *++p;
|
|
}
|
|
}
|
|
/* Get the optional description, if any. */
|
|
if (c == '[') {
|
|
for (descr = ++p; *p && *p != ']'; p++)
|
|
if (*p == '\\' && p[1])
|
|
p++;
|
|
|
|
if (!*p) {
|
|
freecadef(all);
|
|
zwarnnam(nam, "invalid option definition: %s", *args, 0);
|
|
return NULL;
|
|
}
|
|
*p++ = '\0';
|
|
c = *p;
|
|
} else
|
|
descr = NULL;
|
|
|
|
if (c && c != ':') {
|
|
freecadef(all);
|
|
zwarnnam(nam, "invalid option definition: %s", *args, 0);
|
|
return NULL;
|
|
}
|
|
/* Add the option name to the xor list if not `*-...'. */
|
|
if (!multi) {
|
|
if (!xor) {
|
|
xor = (char **) zalloc(2 * sizeof(char *));
|
|
xor[1] = NULL;
|
|
}
|
|
xor[xnum] = ztrdup(name);
|
|
}
|
|
if (c == ':') {
|
|
/* There's at least one argument. */
|
|
|
|
Caarg *oargp = &oargs;
|
|
int atype, rest, oanum = 1, onum = 0;
|
|
char *end;
|
|
|
|
/* Loop over the arguments. */
|
|
|
|
while (c == ':') {
|
|
rest = 0;
|
|
end = NULL;
|
|
|
|
/* Get the argument type. */
|
|
if (*++p == ':') {
|
|
atype = CAA_OPT;
|
|
p++;
|
|
} else if (*p == '*') {
|
|
if (*++p != ':') {
|
|
char sav;
|
|
|
|
for (end = p++; *p && *p != ':'; p++)
|
|
if (*p == '\\' && p[1])
|
|
p++;
|
|
sav = *p;
|
|
*p = '\0';
|
|
end = dupstring(end);
|
|
tokenize(end);
|
|
*p = sav;
|
|
}
|
|
if (*p != ':') {
|
|
freecadef(all);
|
|
freecaargs(oargs);
|
|
zwarnnam(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;
|
|
|
|
/* And the definition. */
|
|
|
|
*oargp = parse_caarg(!rest, atype, oanum++, onum,
|
|
name, &p, doset);
|
|
if (atype == CAA_OPT)
|
|
onum++;
|
|
if (end)
|
|
(*oargp)->end = ztrdup(end);
|
|
oargp = &((*oargp)->next);
|
|
if (rest)
|
|
break;
|
|
c = *p;
|
|
}
|
|
}
|
|
/* Store the option definition. */
|
|
|
|
*optp = opt = (Caopt) zalloc(sizeof(*opt));
|
|
optp = &((*optp)->next);
|
|
|
|
opt->next = NULL;
|
|
opt->set = doset;
|
|
opt->name = ztrdup(rembslashcolon(name));
|
|
if (descr)
|
|
opt->descr = ztrdup(descr);
|
|
else if (adpre && oargs && !oargs->next) {
|
|
char *d;
|
|
|
|
for (d = oargs->descr; *d; d++)
|
|
if (!iblank(*d))
|
|
break;
|
|
|
|
if (*d)
|
|
opt->descr = tricat(adpre, oargs->descr, adsuf);
|
|
else
|
|
opt->descr = NULL;
|
|
} else
|
|
opt->descr = NULL;
|
|
opt->xor = (again == 1 ? zarrdup(xor) : xor);
|
|
opt->type = otype;
|
|
opt->args = oargs;
|
|
opt->num = nopts++;
|
|
opt->not = not;
|
|
|
|
if (otype == CAO_DIRECT || otype == CAO_EQUAL)
|
|
ndopts++;
|
|
else if (otype == CAO_ODIRECT || otype == CAO_OEQUAL)
|
|
nodopts++;
|
|
|
|
/* If this is for single-letter option we also store a
|
|
* pointer for the definition in the array for fast lookup. */
|
|
|
|
if (single && name[1] && !name[2])
|
|
ret->single[STOUC(name[1])] = opt;
|
|
|
|
if (again == 1) {
|
|
/* Do it all again for `*-...'. */
|
|
p = againp;
|
|
goto rec;
|
|
}
|
|
} else if (*p == '*') {
|
|
/* It's a rest-argument definition. */
|
|
|
|
int type = CAA_REST;
|
|
|
|
if (not)
|
|
continue;
|
|
|
|
if (*++p != ':') {
|
|
freecadef(all);
|
|
zwarnnam(nam, "invalid rest argument definition: %s", *args, 0);
|
|
return NULL;
|
|
}
|
|
if (ret->rest) {
|
|
freecadef(all);
|
|
zwarnnam(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, 0, NULL, &p, doset);
|
|
ret->rest->xor = xor;
|
|
} else {
|
|
/* It's a normal argument definition. */
|
|
|
|
int type = CAA_NORMAL, direct;
|
|
Caarg arg, tmp, pre;
|
|
|
|
if (not)
|
|
continue;
|
|
|
|
if ((direct = idigit(*p))) {
|
|
/* Argment number is given. */
|
|
int num = 0;
|
|
|
|
while (*p && idigit(*p))
|
|
num = (num * 10) + (((int) *p++) - '0');
|
|
|
|
anum = num + 1;
|
|
} else
|
|
/* Default number. */
|
|
anum++;
|
|
|
|
if (*p != ':') {
|
|
freecadef(all);
|
|
zwarnnam(nam, "invalid argument: %s", *args, 0);
|
|
return NULL;
|
|
}
|
|
if (*++p == ':') {
|
|
/* Optional argument. */
|
|
type = CAA_OPT;
|
|
p++;
|
|
}
|
|
arg = parse_caarg(0, type, anum - 1, 0, NULL, &p, doset);
|
|
arg->xor = xor;
|
|
arg->direct = direct;
|
|
|
|
/* Sort the new definition into the existing list. */
|
|
|
|
for (tmp = ret->args, pre = NULL;
|
|
tmp && tmp->num < anum - 1;
|
|
pre = tmp, tmp = tmp->next);
|
|
|
|
if (tmp && tmp->num == anum - 1) {
|
|
freecadef(all);
|
|
freecaargs(arg);
|
|
zwarnnam(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;
|
|
set_cadef_opts(ret);
|
|
|
|
return all;
|
|
}
|
|
|
|
/* Given an array of definitions, return the cadef for it. From the cache
|
|
* are newly built. */
|
|
|
|
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++, i--)
|
|
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))) {
|
|
freecadef(*min);
|
|
*min = new;
|
|
}
|
|
return new;
|
|
}
|
|
|
|
/* Get the option used in a word from the line, if any. */
|
|
|
|
static Caopt
|
|
ca_get_opt(Cadef d, char *line, int full, char **end)
|
|
{
|
|
Caopt p;
|
|
|
|
/* The full string may be an option. */
|
|
|
|
for (p = d->opts; p; p = p->next)
|
|
if (p->active && !strcmp(p->name, line)) {
|
|
if (end)
|
|
*end = line + strlen(line);
|
|
|
|
return p;
|
|
}
|
|
|
|
if (!full) {
|
|
/* The string from the line probably only begins with an option. */
|
|
for (p = d->opts; p; p = p->next)
|
|
if (p->active && ((!p->args || p->type == CAO_NEXT) ?
|
|
!strcmp(p->name, line) : strpfx(p->name, line))) {
|
|
if (end) {
|
|
/* Return a pointer to the end of the option. */
|
|
int l = strlen(p->name);
|
|
|
|
if ((p->type == CAO_OEQUAL || p->type == CAO_EQUAL) &&
|
|
line[l] == '=')
|
|
l++;
|
|
|
|
*end = line + l;
|
|
}
|
|
return p;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Same as above, only for single-letter-style. */
|
|
|
|
static Caopt
|
|
ca_get_sopt(Cadef d, char *line, char **end, LinkList *lp)
|
|
{
|
|
Caopt p, pp = NULL;
|
|
char pre = *line++;
|
|
LinkList l = NULL;
|
|
|
|
*lp = NULL;
|
|
for (p = NULL; *line; line++) {
|
|
if ((p = d->single[STOUC(*line)]) && p->active &&
|
|
p->args && p->name[0] == pre) {
|
|
if (p->type == CAO_NEXT) {
|
|
if (!l)
|
|
*lp = l = newlinklist();
|
|
addlinknode(l, p);
|
|
} else {
|
|
if (end) {
|
|
line++;
|
|
if ((p->type == CAO_OEQUAL || p->type == CAO_EQUAL) &&
|
|
*line == '=')
|
|
line++;
|
|
*end = line;
|
|
}
|
|
break;
|
|
}
|
|
} else if (!p || (p && !p->active))
|
|
return NULL;
|
|
pp = (p->name[0] == pre ? p : NULL);
|
|
p = NULL;
|
|
}
|
|
if (pp && end)
|
|
*end = line;
|
|
return pp;
|
|
}
|
|
|
|
/* Return the n'th argument definition. */
|
|
|
|
static Caarg
|
|
ca_get_arg(Cadef d, int n)
|
|
{
|
|
if (d->argsactive) {
|
|
Caarg a = d->args;
|
|
|
|
while (a && (n < a->min || n > a->num))
|
|
a = a->next;
|
|
|
|
if (a && a->min <= n && a->num >= n && a->active)
|
|
return a;
|
|
|
|
return (d->rest && d->rest->active ? d->rest : NULL);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Use a xor list, marking options as inactive. */
|
|
|
|
static LinkList ca_xor;
|
|
|
|
static int
|
|
ca_inactive(Cadef d, char **xor, int cur, int opts)
|
|
{
|
|
if ((xor || opts) && cur <= compcurrent) {
|
|
Caopt opt;
|
|
char *x;
|
|
int sl = (d->set ? strlen(d->set) : -1), set = 0;
|
|
|
|
for (; (x = (opts ? "-" : *xor)); xor++) {
|
|
if (ca_xor)
|
|
addlinknode(ca_xor, x);
|
|
set = 0;
|
|
if (sl > 0) {
|
|
if (strpfx(d->set, x)) {
|
|
x += sl;
|
|
set = 1;
|
|
} else if (!strncmp(d->set, x, sl - 1)) {
|
|
Caopt p;
|
|
|
|
for (p = d->opts; p; p = p->next)
|
|
if (p->set)
|
|
p->active = 0;
|
|
|
|
x = ":";
|
|
set = 1;
|
|
}
|
|
}
|
|
if (x[0] == ':' && !x[1]) {
|
|
if (set) {
|
|
Caarg a;
|
|
|
|
for (a = d->args; a; a = a->next)
|
|
if (a->set)
|
|
a->active = 0;
|
|
if (d->rest && (!set || d->rest->set))
|
|
d->rest->active = 0;
|
|
} else
|
|
d->argsactive = 0;
|
|
} else if (x[0] == '-' && !x[1]) {
|
|
Caopt p;
|
|
|
|
for (p = d->opts; p; p = p->next)
|
|
if (!set || p->set)
|
|
p->active = 0;
|
|
} else if (x[0] == '*' && !x[1]) {
|
|
if (d->rest && (!set || d->rest->set))
|
|
d->rest->active = 0;
|
|
} else if (x[0] >= '0' && x[0] <= '9') {
|
|
int n = atoi(x);
|
|
Caarg a = d->args;
|
|
|
|
while (a && a->num < n)
|
|
a = a->next;
|
|
|
|
if (a && a->num == n && (!set || a->set))
|
|
a->active = 0;
|
|
} else if ((opt = ca_get_opt(d, x, 1, NULL)) && (!set || opt->set))
|
|
opt->active = 0;
|
|
|
|
if (opts)
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* State when parsing a command line. */
|
|
|
|
typedef struct castate *Castate;
|
|
|
|
struct castate {
|
|
Castate snext;
|
|
Cadef d;
|
|
int nopts;
|
|
Caarg def, ddef;
|
|
Caopt curopt, dopt;
|
|
int opt, arg, argbeg, optbeg, nargbeg, restbeg, curpos, argend;
|
|
int inopt, inrest, inarg, nth, doff, singles, oopt, actopts;
|
|
LinkList args;
|
|
LinkList *oargs;
|
|
};
|
|
|
|
static struct castate ca_laststate;
|
|
static int ca_parsed = 0, ca_alloced = 0;
|
|
|
|
static void
|
|
freecastate(Castate s)
|
|
{
|
|
int i;
|
|
LinkList *p;
|
|
|
|
freelinklist(s->args, freestr);
|
|
for (i = s->nopts, p = s->oargs; i--; p++)
|
|
if (*p)
|
|
freelinklist(*p, freestr);
|
|
zfree(s->oargs, s->d->nopts * sizeof(LinkList));
|
|
}
|
|
|
|
/* Parse a command line. */
|
|
|
|
static int
|
|
ca_parse_line(Cadef d, int multi, int first)
|
|
{
|
|
Caarg adef, ddef;
|
|
Caopt ptr, wasopt = NULL, dopt;
|
|
struct castate state;
|
|
char *line, *pe, **argxor = NULL;
|
|
int cur, doff, argend, arglast;
|
|
Patprog endpat = NULL, napat = NULL;
|
|
LinkList sopts = NULL;
|
|
|
|
/* Free old state. */
|
|
|
|
if (first && ca_alloced) {
|
|
Castate s = &ca_laststate, ss;
|
|
int f = 1;
|
|
|
|
while (s) {
|
|
ss = s->snext;
|
|
freecastate(s);
|
|
if (!f)
|
|
zfree(s, sizeof(*s));
|
|
s = ss;
|
|
}
|
|
}
|
|
/* Mark everything as active. */
|
|
|
|
for (ptr = d->opts; ptr; ptr = ptr->next)
|
|
ptr->active = 1;
|
|
d->argsactive = 1;
|
|
if (d->rest)
|
|
d->rest->active = 1;
|
|
for (adef = d->args; adef; adef = adef->next)
|
|
adef->active = 1;
|
|
|
|
/* Default values for the state. */
|
|
|
|
state.snext = NULL;
|
|
state.d = d;
|
|
state.nopts = d->nopts;
|
|
state.def = state.ddef = NULL;
|
|
state.curopt = state.dopt = NULL;
|
|
state.argbeg = state.optbeg = state.nargbeg = state.restbeg = state.actopts =
|
|
state.nth = state.inopt = state.inarg = state.opt = state.arg = 1;
|
|
state.argend = argend = arrlen(compwords) - 1;
|
|
state.inrest = state.doff = state.singles = state.doff = state.oopt = 0;
|
|
state.curpos = compcurrent;
|
|
state.args = znewlinklist();
|
|
state.oargs = (LinkList *) zalloc(d->nopts * sizeof(LinkList));
|
|
memset(state.oargs, 0, d->nopts * sizeof(LinkList));
|
|
|
|
ca_alloced = 1;
|
|
|
|
memcpy(&ca_laststate, &state, sizeof(state));
|
|
|
|
if (!compwords[1]) {
|
|
ca_laststate.opt = ca_laststate.arg = 0;
|
|
|
|
goto end;
|
|
}
|
|
if (d->nonarg)
|
|
napat = patcompile(d->nonarg, 0, NULL);
|
|
|
|
/* Loop over the words from the line. */
|
|
|
|
for (line = compwords[1], cur = 2, state.curopt = NULL, state.def = NULL;
|
|
line; line = compwords[cur++]) {
|
|
ddef = adef = NULL;
|
|
dopt = NULL;
|
|
doff = state.singles = arglast = 0;
|
|
|
|
if (ca_inactive(d, argxor, cur, 0) ||
|
|
((d->flags & CDF_SEP) && !strcmp(line, "--"))) {
|
|
if (ca_inactive(d, NULL, cur, 1))
|
|
return 1;
|
|
continue;
|
|
}
|
|
/* We've got a definition for an argument, skip to the next. */
|
|
|
|
if (state.def) {
|
|
state.arg = 0;
|
|
if (state.curopt)
|
|
zaddlinknode(state.oargs[state.curopt->num], ztrdup(line));
|
|
|
|
if ((state.opt = (state.def->type == CAA_OPT)) && state.def->opt)
|
|
state.oopt++;
|
|
|
|
if (state.def->type == CAA_REST || state.def->type == CAA_RARGS ||
|
|
state.def->type == CAA_RREST) {
|
|
if (state.def->end && pattry(endpat, line)) {
|
|
state.def = NULL;
|
|
state.curopt = NULL;
|
|
state.opt = state.arg = 1;
|
|
state.argend = ca_laststate.argend = cur - 1;
|
|
goto cont;
|
|
}
|
|
} else if ((state.def = state.def->next)) {
|
|
state.argbeg = cur;
|
|
state.argend = argend;
|
|
} else if (sopts && nonempty(sopts)) {
|
|
state.curopt = (Caopt) uremnode(sopts, firstnode(sopts));
|
|
state.def = state.curopt->args;
|
|
state.opt = 0;
|
|
state.argbeg = state.optbeg = state.inopt = cur;
|
|
state.argend = argend;
|
|
doff = state.doff = 0;
|
|
state.singles = 1;
|
|
if (!state.oargs[state.curopt->num])
|
|
state.oargs[state.curopt->num] = znewlinklist();
|
|
goto cont;
|
|
} else {
|
|
state.curopt = NULL;
|
|
state.opt = 1;
|
|
}
|
|
} else {
|
|
state.opt = state.arg = 1;
|
|
state.curopt = NULL;
|
|
}
|
|
if (state.opt)
|
|
state.opt = (line[0] ? (line[1] ? 2 : 1) : 0);
|
|
|
|
pe = NULL;
|
|
|
|
wasopt = NULL;
|
|
|
|
/* See if it's an option. */
|
|
|
|
if (state.opt == 2 && (state.curopt = ca_get_opt(d, line, 0, &pe)) &&
|
|
(state.curopt->type == CAO_OEQUAL ?
|
|
(compwords[cur] || pe[-1] == '=') :
|
|
(state.curopt->type == CAO_EQUAL ?
|
|
(pe[-1] == '=' || !pe[0]) : 1))) {
|
|
|
|
if ((ddef = state.def = ((state.curopt->type != CAO_EQUAL ||
|
|
pe[-1] == '=') ?
|
|
state.curopt->args : NULL)))
|
|
dopt = state.curopt;
|
|
|
|
doff = pe - line;
|
|
state.optbeg = state.argbeg = state.inopt = cur;
|
|
state.argend = argend;
|
|
state.singles = (d->single && (!pe || !*pe) &&
|
|
state.curopt->name[1] && !state.curopt->name[2]);
|
|
|
|
if (!state.oargs[state.curopt->num])
|
|
state.oargs[state.curopt->num] = znewlinklist();
|
|
|
|
if (ca_inactive(d, state.curopt->xor, cur, 0))
|
|
return 1;
|
|
|
|
/* Collect the argument strings. Maybe. */
|
|
|
|
if (state.def &&
|
|
(state.curopt->type == CAO_DIRECT ||
|
|
state.curopt->type == CAO_EQUAL ||
|
|
(state.curopt->type == CAO_ODIRECT && pe[0]) ||
|
|
(state.curopt->type == CAO_OEQUAL &&
|
|
(pe[0] || pe[-1] == '=')))) {
|
|
if (state.def->type != CAA_REST &&
|
|
state.def->type != CAA_RARGS &&
|
|
state.def->type != CAA_RREST)
|
|
state.def = state.def->next;
|
|
|
|
zaddlinknode(state.oargs[state.curopt->num], ztrdup(pe));
|
|
}
|
|
if (state.def)
|
|
state.opt = 0;
|
|
else {
|
|
if (!d->single || (state.curopt->name[1] && state.curopt->name[2]))
|
|
wasopt = state.curopt;
|
|
state.curopt = NULL;
|
|
}
|
|
} else if (state.opt == 2 && d->single &&
|
|
((state.curopt = ca_get_sopt(d, line, &pe, &sopts)) ||
|
|
(sopts && nonempty(sopts)))) {
|
|
/* Or maybe it's a single-letter option? */
|
|
|
|
char *p;
|
|
Caopt tmpopt;
|
|
|
|
if (sopts && nonempty(sopts))
|
|
state.curopt = (Caopt) uremnode(sopts, firstnode(sopts));
|
|
|
|
if (!state.oargs[state.curopt->num])
|
|
state.oargs[state.curopt->num] = znewlinklist();
|
|
|
|
ddef = state.def = state.curopt->args;
|
|
dopt = state.curopt;
|
|
doff = pe - line;
|
|
state.optbeg = state.argbeg = state.inopt = cur;
|
|
state.argend = argend;
|
|
state.singles = (!pe || !*pe);
|
|
|
|
for (p = line + 1; p < pe; p++) {
|
|
if ((tmpopt = d->single[STOUC(*p)])) {
|
|
if (!state.oargs[tmpopt->num])
|
|
state.oargs[tmpopt->num] = znewlinklist();
|
|
|
|
if (ca_inactive(d, tmpopt->xor, cur, 0))
|
|
return 1;
|
|
}
|
|
}
|
|
if (state.def &&
|
|
(state.curopt->type == CAO_DIRECT ||
|
|
state.curopt->type == CAO_EQUAL ||
|
|
(state.curopt->type == CAO_ODIRECT && pe[0]) ||
|
|
(state.curopt->type == CAO_OEQUAL &&
|
|
(pe[0] || pe[-1] == '=')))) {
|
|
if (state.def->type != CAA_REST &&
|
|
state.def->type != CAA_RARGS &&
|
|
state.def->type != CAA_RREST)
|
|
state.def = state.def->next;
|
|
|
|
zaddlinknode(state.oargs[state.curopt->num], ztrdup(pe));
|
|
}
|
|
if (state.def)
|
|
state.opt = 0;
|
|
else
|
|
state.curopt = NULL;
|
|
} else if (multi && (*line == '-' || *line == '+') && cur != compcurrent
|
|
#if 0
|
|
/**** Ouch. Using this will disable the mutual exclusion
|
|
of different sets. Not using it will make the -A
|
|
pattern be effectively ignored with multiple sets. */
|
|
&& (!napat || !pattry(napat, line))
|
|
#endif
|
|
)
|
|
return 1;
|
|
else if (state.arg && (!napat || !pattry(napat, line))) {
|
|
/* Otherwise it's a normal argument. */
|
|
if (napat && ca_inactive(d, NULL, cur + 1, 1))
|
|
return 1;
|
|
|
|
arglast = 1;
|
|
if (state.inopt) {
|
|
state.inopt = 0;
|
|
state.nargbeg = cur - 1;
|
|
state.argend = argend;
|
|
}
|
|
if (!d->args && !d->rest && *line && *line != '-' && *line != '+')
|
|
return 1;
|
|
if ((adef = state.def = ca_get_arg(d, state.nth)) &&
|
|
(state.def->type == CAA_RREST ||
|
|
state.def->type == CAA_RARGS)) {
|
|
state.inrest = 0;
|
|
state.opt = (cur == state.nargbeg + 1 &&
|
|
(!multi || !*line ||
|
|
*line == '-' || *line == '+'));
|
|
state.optbeg = state.nargbeg;
|
|
state.argbeg = cur - 1;
|
|
state.argend = argend;
|
|
|
|
for (; line; line = compwords[cur++])
|
|
zaddlinknode(state.args, ztrdup(line));
|
|
|
|
memcpy(&ca_laststate, &state, sizeof(state));
|
|
ca_laststate.ddef = NULL;
|
|
ca_laststate.dopt = NULL;
|
|
ca_laststate.doff = 0;
|
|
break;
|
|
}
|
|
zaddlinknode(state.args, ztrdup(line));
|
|
|
|
if (state.def)
|
|
argxor = state.def->xor;
|
|
|
|
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;
|
|
}
|
|
/* Do the end-pattern test if needed. */
|
|
|
|
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];
|
|
|
|
if (cur < compcurrent)
|
|
memcpy(&ca_laststate, &state, sizeof(state));
|
|
|
|
for (; line; line = compwords[cur++])
|
|
zaddlinknode(l, ztrdup(line));
|
|
|
|
ca_laststate.ddef = NULL;
|
|
ca_laststate.dopt = NULL;
|
|
ca_laststate.doff = 0;
|
|
break;
|
|
}
|
|
} else if (state.def && state.def->end)
|
|
endpat = patcompile(state.def->end, 0, NULL);
|
|
|
|
/* Copy the state into the global one. */
|
|
|
|
cont:
|
|
|
|
if (cur + 1 == compcurrent) {
|
|
memcpy(&ca_laststate, &state, sizeof(state));
|
|
ca_laststate.ddef = NULL;
|
|
ca_laststate.dopt = NULL;
|
|
ca_laststate.doff = 0;
|
|
} else if (cur == compcurrent && !ca_laststate.def) {
|
|
if ((ca_laststate.def = ddef)) {
|
|
ca_laststate.singles = state.singles;
|
|
if (state.curopt && state.curopt->type == CAO_NEXT) {
|
|
ca_laststate.ddef = ddef;
|
|
ca_laststate.dopt = dopt;
|
|
ca_laststate.def = NULL;
|
|
ca_laststate.opt = 1;
|
|
state.curopt->active = 1;
|
|
} else {
|
|
ca_laststate.doff = doff;
|
|
ca_laststate.opt = 0;
|
|
}
|
|
} else {
|
|
ca_laststate.def = adef;
|
|
ca_laststate.opt = (!arglast || !multi || !*line ||
|
|
*line == '-' || *line == '+');
|
|
ca_laststate.ddef = NULL;
|
|
ca_laststate.dopt = NULL;
|
|
ca_laststate.optbeg = state.nargbeg;
|
|
ca_laststate.argbeg = state.restbeg;
|
|
ca_laststate.argend = state.argend;
|
|
ca_laststate.singles = state.singles;
|
|
ca_laststate.oopt = state.oopt;
|
|
if (wasopt)
|
|
wasopt->active = 1;
|
|
}
|
|
}
|
|
}
|
|
end:
|
|
|
|
ca_laststate.actopts = 0;
|
|
for (ptr = d->opts; ptr; ptr = ptr->next)
|
|
if (ptr->active)
|
|
ca_laststate.actopts++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Build a colon-list from a list. */
|
|
|
|
static char *
|
|
ca_colonlist(LinkList l)
|
|
{
|
|
if (l) {
|
|
LinkNode n;
|
|
int len = 0;
|
|
char *p, *ret, *q;
|
|
|
|
for (n = firstnode(l); n; incnode(n)) {
|
|
len++;
|
|
for (p = (char *) getdata(n); *p; p++)
|
|
len += (*p == ':' ? 2 : 1);
|
|
}
|
|
ret = q = (char *) zalloc(len);
|
|
|
|
for (n = firstnode(l); n;) {
|
|
for (p = (char *) getdata(n); *p; p++) {
|
|
if (*p == ':')
|
|
*q++ = '\\';
|
|
*q++ = *p;
|
|
}
|
|
incnode(n);
|
|
if (n)
|
|
*q++ = ':';
|
|
}
|
|
*q = '\0';
|
|
|
|
return ret;
|
|
} else
|
|
return ztrdup("");
|
|
}
|
|
|
|
static void
|
|
ca_set_data(LinkList descr, LinkList act, LinkList subc,
|
|
char *opt, Caarg arg, int single)
|
|
{
|
|
LinkNode dnode, anode;
|
|
char nbuf[40], *buf;
|
|
int restr = 0, onum, miss = 0, rest, oopt = 1, lopt = 0, addopt;
|
|
|
|
rec:
|
|
|
|
addopt = (opt ? 0 : ca_laststate.oopt);
|
|
|
|
for (; arg && (opt || (arg->num < 0 ||
|
|
(arg->min <= ca_laststate.nth + addopt &&
|
|
arg->num >= ca_laststate.nth)));) {
|
|
lopt = (arg->type == CAA_OPT);
|
|
if (!opt && !lopt && oopt > 0)
|
|
oopt = 0;
|
|
|
|
for (dnode = firstnode(descr), anode = firstnode(act);
|
|
dnode; incnode(dnode), incnode(anode))
|
|
if (!strcmp((char *) getdata(dnode), arg->descr) &&
|
|
!strcmp((char *) getdata(anode), arg->action))
|
|
break;
|
|
|
|
if (!dnode) {
|
|
addlinknode(descr, arg->descr);
|
|
addlinknode(act, arg->action);
|
|
|
|
if (!restr) {
|
|
if ((restr = (arg->type == CAA_RARGS)))
|
|
restrict_range(ca_laststate.optbeg, ca_laststate.argend);
|
|
else if ((restr = (arg->type == CAA_RREST)))
|
|
restrict_range(ca_laststate.argbeg, ca_laststate.argend);
|
|
}
|
|
if (arg->opt) {
|
|
buf = (char *) zhalloc((arg->set ? strlen(arg->set) : 0) +
|
|
strlen(arg->opt) + 40);
|
|
if (arg->num > 0 && arg->type < CAA_REST)
|
|
sprintf(buf, "%soption%s-%d",
|
|
(arg->set ? arg->set : ""), arg->opt, arg->num);
|
|
else
|
|
sprintf(buf, "%soption%s-rest",
|
|
(arg->set ? arg->set : ""), arg->opt);
|
|
} else if (arg->num > 0) {
|
|
sprintf(nbuf, "argument-%d", arg->num);
|
|
buf = (arg->set ? dyncat(arg->set, nbuf) : dupstring(nbuf));
|
|
} else
|
|
buf = (arg->set ? dyncat(arg->set, "argument-rest") :
|
|
dupstring("argument-rest"));
|
|
|
|
addlinknode(subc, buf);
|
|
}
|
|
if (single)
|
|
break;
|
|
|
|
if (!opt) {
|
|
if (arg->num >= 0 && !arg->next && miss)
|
|
arg = ca_laststate.d->rest;
|
|
else {
|
|
onum = arg->num;
|
|
rest = (onum != arg->min && onum == ca_laststate.nth);
|
|
if ((arg = arg->next)) {
|
|
if (arg->num != onum + 1)
|
|
miss = 1;
|
|
} else if (rest || (oopt > 0 && !opt)) {
|
|
arg = ca_laststate.d->rest;
|
|
oopt = -1;
|
|
}
|
|
}
|
|
} else {
|
|
if (!lopt)
|
|
break;
|
|
arg = arg->next;
|
|
}
|
|
}
|
|
if (!single && opt && (lopt || ca_laststate.oopt)) {
|
|
opt = NULL;
|
|
arg = ca_get_arg(ca_laststate.d, ca_laststate.nth);
|
|
|
|
goto rec;
|
|
}
|
|
if (!opt && oopt > 0) {
|
|
oopt = -1;
|
|
arg = ca_laststate.d->rest;
|
|
|
|
goto rec;
|
|
}
|
|
}
|
|
|
|
static int
|
|
bin_comparguments(char *nam, char **args, char *ops, int func)
|
|
{
|
|
int min, max, n;
|
|
Castate lstate = &ca_laststate;
|
|
|
|
if (incompfunc != 1) {
|
|
zwarnnam(nam, "can only be called from completion function", NULL, 0);
|
|
return 1;
|
|
}
|
|
if (args[0][0] != '-' || !args[0][1] || args[0][2]) {
|
|
zwarnnam(nam, "invalid argument: %s", args[0], 0);
|
|
return 1;
|
|
}
|
|
if (args[0][1] != 'i' && args[0][1] != 'I' && !ca_parsed) {
|
|
zwarnnam(nam, "no parsed state", NULL, 0);
|
|
return 1;
|
|
}
|
|
switch (args[0][1]) {
|
|
case 'i': min = 2; max = -1; break;
|
|
case 'D': min = 3; max = 3; break;
|
|
case 'O': min = 4; max = 4; break;
|
|
case 'L': min = 3; max = 4; break;
|
|
case 's': min = 1; max = 1; break;
|
|
case 'M': min = 1; max = 1; break;
|
|
case 'a': min = 0; max = 0; break;
|
|
case 'W': min = 2; max = 2; break;
|
|
default:
|
|
zwarnnam(nam, "invalid option: %s", args[0], 0);
|
|
return 1;
|
|
}
|
|
n = arrlen(args) - 1;
|
|
if (n < min) {
|
|
zwarnnam(nam, "not enough arguments", NULL, 0);
|
|
return 1;
|
|
} else if (max >= 0 && n > max) {
|
|
zwarnnam(nam, "too many arguments", NULL, 0);
|
|
return 1;
|
|
}
|
|
switch (args[0][1]) {
|
|
case 'i':
|
|
if (compcurrent > 1 && compwords[0]) {
|
|
Cadef def;
|
|
int cap = ca_parsed, multi, first = 1, use, ret = 0;
|
|
LinkList cax = ca_xor, nx;
|
|
LinkNode node;
|
|
Castate states = NULL, sp;
|
|
char *xor[2];
|
|
|
|
ca_parsed = 0;
|
|
xor[1] = NULL;
|
|
|
|
if (!(def = get_cadef(nam, args + 1)))
|
|
return 1;
|
|
|
|
multi = !!def->snext;
|
|
ca_parsed = cap;
|
|
ca_xor = (multi ? newlinklist() : NULL);
|
|
|
|
while (def) {
|
|
use = !ca_parse_line(def, multi, first);
|
|
nx = ca_xor;
|
|
ca_xor = NULL;
|
|
while ((def = def->snext)) {
|
|
if (nx) {
|
|
for (node = firstnode(nx); node; incnode(node)) {
|
|
xor[0] = (char *) getdata(node);
|
|
if (!strcmp(xor[0], def->sname) ||
|
|
ca_inactive(def, xor, compcurrent, 0))
|
|
break;
|
|
}
|
|
if (!node)
|
|
break;
|
|
}
|
|
}
|
|
ca_xor = nx;
|
|
if (use && def) {
|
|
sp = (Castate) zalloc(sizeof(*sp));
|
|
memcpy(sp, &ca_laststate, sizeof(*sp));
|
|
sp->snext = states;
|
|
states = sp;
|
|
} else if (!use && !def) {
|
|
if (states) {
|
|
freecastate(&ca_laststate);
|
|
memcpy(&ca_laststate, states, sizeof(*sp));
|
|
sp = states->snext;
|
|
zfree(states, sizeof(*states));
|
|
states = sp;
|
|
} else
|
|
ret = 1;
|
|
}
|
|
first = 0;
|
|
}
|
|
ca_xor = cax;
|
|
ca_parsed = 1;
|
|
ca_laststate.snext = states;
|
|
|
|
return ret;
|
|
}
|
|
return 1;
|
|
|
|
case 'D':
|
|
{
|
|
LinkList descr, act, subc;
|
|
Caarg arg;
|
|
int ign = 0, ret = 1;
|
|
|
|
descr = newlinklist();
|
|
act = newlinklist();
|
|
subc = newlinklist();
|
|
|
|
while (lstate) {
|
|
arg = lstate->def;
|
|
|
|
if (arg) {
|
|
ret = 0;
|
|
if (!ign && lstate->doff > 0) {
|
|
ign = 1;
|
|
ignore_prefix(lstate->doff);
|
|
}
|
|
ca_set_data(descr, act, subc, arg->opt, arg,
|
|
(lstate->doff > 0));
|
|
}
|
|
lstate = lstate->snext;
|
|
}
|
|
if (!ret) {
|
|
set_list_array(args[1], descr);
|
|
set_list_array(args[2], act);
|
|
set_list_array(args[3], subc);
|
|
}
|
|
return ret;
|
|
}
|
|
case 'O':
|
|
{
|
|
LinkList next = newlinklist();
|
|
LinkList direct = newlinklist();
|
|
LinkList odirect = newlinklist();
|
|
LinkList equal = newlinklist(), l;
|
|
Caopt p;
|
|
char *str;
|
|
int ret = 1;
|
|
|
|
for (; lstate; lstate = lstate->snext) {
|
|
if (lstate->actopts &&
|
|
(lstate->opt || (lstate->doff && lstate->def) ||
|
|
(lstate->def && lstate->def->opt &&
|
|
(lstate->def->type == CAA_OPT ||
|
|
(lstate->def->type >= CAA_RARGS &&
|
|
lstate->def->num < 0)))) &&
|
|
(!lstate->def || lstate->def->type < CAA_RARGS ||
|
|
(lstate->def->type == CAA_RARGS ?
|
|
(lstate->curpos == lstate->argbeg + 1) :
|
|
(compcurrent == 1)))) {
|
|
ret = 0;
|
|
for (p = lstate->d->opts; p; p = p->next) {
|
|
if (p->active && !p->not) {
|
|
switch (p->type) {
|
|
case CAO_NEXT: l = next; break;
|
|
case CAO_DIRECT: l = direct; break;
|
|
case CAO_ODIRECT: l = odirect; break;
|
|
default: l = equal; break;
|
|
}
|
|
if (p->descr) {
|
|
char *n = bslashcolon(p->name);
|
|
int len = strlen(n) + strlen(p->descr) + 2;
|
|
|
|
str = (char *) zhalloc(len);
|
|
strcpy(str, n);
|
|
strcat(str, ":");
|
|
strcat(str, p->descr);
|
|
} else
|
|
str = bslashcolon(p->name);
|
|
addlinknode(l, str);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!ret) {
|
|
set_list_array(args[1], next);
|
|
set_list_array(args[2], direct);
|
|
set_list_array(args[3], odirect);
|
|
set_list_array(args[4], equal);
|
|
|
|
return 0;
|
|
}
|
|
return (ca_laststate.singles ? 2 : 1);
|
|
}
|
|
case 'L':
|
|
{
|
|
LinkList descr, act, subc;
|
|
Caopt opt;
|
|
int ret = 1;
|
|
|
|
descr = newlinklist();
|
|
act = newlinklist();
|
|
subc = newlinklist();
|
|
|
|
while (lstate) {
|
|
opt = ca_get_opt(lstate->d, args[1], 1, NULL);
|
|
|
|
if (opt && opt->args) {
|
|
ret = 0;
|
|
ca_set_data(descr, act, subc, opt->name, opt->args, 1);
|
|
}
|
|
lstate = lstate->snext;
|
|
}
|
|
if (!ret) {
|
|
set_list_array(args[2], descr);
|
|
set_list_array(args[3], act);
|
|
set_list_array(args[4], subc);
|
|
}
|
|
return ret;
|
|
}
|
|
case 's':
|
|
for (; lstate; lstate = lstate->snext)
|
|
if (lstate->d->single && lstate->singles &&
|
|
lstate->actopts && lstate->opt) {
|
|
setsparam(args[1],
|
|
ztrdup((lstate->ddef && lstate->dopt) ?
|
|
(lstate->dopt->type == CAO_DIRECT ?
|
|
"direct" :
|
|
((lstate->dopt->type == CAO_OEQUAL ||
|
|
lstate->dopt->type == CAO_EQUAL) ?
|
|
"equal" : "next")) : ""));
|
|
return 0;
|
|
}
|
|
return 1;
|
|
case 'M':
|
|
setsparam(args[1], ztrdup(ca_laststate.d->match));
|
|
return 0;
|
|
case 'a':
|
|
for (; lstate; lstate = lstate->snext)
|
|
if (lstate->d->args || lstate->d->rest)
|
|
return 0;
|
|
return 1;
|
|
case 'W':
|
|
{
|
|
Castate s;
|
|
char **ret, **p;
|
|
LinkNode n;
|
|
LinkList *a;
|
|
Caopt o;
|
|
int num;
|
|
|
|
for (num = 0, s = lstate; s; s = s->snext)
|
|
num += countlinknodes(s->args);
|
|
|
|
ret = p = zalloc((num + 1) * sizeof(char *));
|
|
|
|
for (s = lstate; s; s = s->snext)
|
|
for (n = firstnode(s->args); n; incnode(n))
|
|
*p++ = ztrdup((char *) getdata(n));
|
|
*p = NULL;
|
|
|
|
setaparam(args[1], ret);
|
|
|
|
for (num = 0, s = lstate; s; s = s->snext)
|
|
for (o = s->d->opts, a = s->oargs; o; o = o->next, a++)
|
|
if (*a)
|
|
num += 2;
|
|
|
|
ret = p = zalloc((num + 1) * sizeof(char *));
|
|
|
|
for (s = lstate; s; s = s->snext)
|
|
for (o = s->d->opts, a = s->oargs; o; o = o->next, a++)
|
|
if (*a) {
|
|
*p++ = (o->set ? tricat(o->set, o->name, "") :
|
|
ztrdup(o->name));
|
|
*p++ = ca_colonlist(*a);
|
|
}
|
|
*p = NULL;
|
|
|
|
sethparam(args[2], ret);
|
|
}
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* Help for `_values'. */
|
|
|
|
typedef struct cvdef *Cvdef;
|
|
typedef struct cvval *Cvval;
|
|
|
|
/* Definitions for _values. */
|
|
|
|
struct cvdef {
|
|
char *descr; /* global description */
|
|
int hassep; /* multiple values allowed */
|
|
char sep; /* separator character */
|
|
Cvdef next; /* next in cache */
|
|
Cvval vals; /* value definitions */
|
|
char **defs; /* original strings */
|
|
int ndefs; /* number of ... */
|
|
int lastt; /* last time used */
|
|
};
|
|
|
|
/* One value definition. */
|
|
|
|
struct cvval {
|
|
Cvval next;
|
|
char *name; /* value name */
|
|
char *descr; /* description */
|
|
char **xor; /* xor-list */
|
|
int type; /* CVV_* below */
|
|
Caarg arg; /* argument definition */
|
|
int active; /* still allowed */
|
|
};
|
|
|
|
#define CVV_NOARG 0
|
|
#define CVV_ARG 1
|
|
#define CVV_OPT 2
|
|
|
|
/* Cache. */
|
|
|
|
#define MAX_CVCACHE 8
|
|
static Cvdef cvdef_cache[MAX_CVCACHE];
|
|
|
|
/* Memory stuff. */
|
|
|
|
static void
|
|
freecvdef(Cvdef d)
|
|
{
|
|
if (d) {
|
|
Cvval p, n;
|
|
|
|
zsfree(d->descr);
|
|
if (d->defs)
|
|
freearray(d->defs);
|
|
|
|
for (p = d->vals; p; p = n) {
|
|
n = p->next;
|
|
zsfree(p->name);
|
|
zsfree(p->descr);
|
|
if (p->xor)
|
|
freearray(p->xor);
|
|
freecaargs(p->arg);
|
|
zfree(p, sizeof(*p));
|
|
}
|
|
zfree(d, sizeof(*d));
|
|
}
|
|
}
|
|
|
|
/* Parse option definitions. */
|
|
|
|
static Cvdef
|
|
parse_cvdef(char *nam, char **args)
|
|
{
|
|
Cvdef ret;
|
|
Cvval val, *valp;
|
|
Caarg arg;
|
|
char **oargs = args, sep = '\0', *name, *descr, *p, *q, **xor, c;
|
|
int xnum, multi, vtype, hassep = 0;
|
|
|
|
if (args[0][0] == '-' && args[0][1] == 's' && !args[0][2]) {
|
|
if (args[1][0] && args[1][1]) {
|
|
zwarnnam(nam, "invalid separator: %s", args[1], 0);
|
|
return NULL;
|
|
}
|
|
hassep = 1;
|
|
sep = args[1][0];
|
|
args += 2;
|
|
}
|
|
if (!args[0] || !args[1]) {
|
|
zwarnnam(nam, "not enough arguments", NULL, 0);
|
|
return NULL;
|
|
}
|
|
descr = *args++;
|
|
|
|
ret = (Cvdef) zalloc(sizeof(*ret));
|
|
ret->descr = ztrdup(descr);
|
|
ret->hassep = hassep;
|
|
ret->sep = sep;
|
|
ret->next = NULL;
|
|
ret->vals = NULL;
|
|
ret->defs = zarrdup(oargs);
|
|
ret->ndefs = arrlen(oargs);
|
|
ret->lastt = time(0);
|
|
|
|
for (valp = &(ret->vals); *args; args++) {
|
|
p = dupstring(*args);
|
|
xnum = 0;
|
|
|
|
/* xor list? */
|
|
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 != ')') {
|
|
freecvdef(ret);
|
|
zwarnnam(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;
|
|
|
|
/* More than once allowed? */
|
|
if ((multi = (*p == '*')))
|
|
p++;
|
|
|
|
/* Skip option name. */
|
|
|
|
for (name = p; *p && *p != ':' && *p != '['; p++)
|
|
if (*p == '\\' && p[1])
|
|
p++;
|
|
|
|
if (hassep && !sep && name + 1 != p) {
|
|
freecvdef(ret);
|
|
zwarnnam(nam, "no multi-letter values with empty separator allowed", NULL, 0);
|
|
return NULL;
|
|
}
|
|
/* Optional description? */
|
|
|
|
if ((c = *p) == '[') {
|
|
*p = '\0';
|
|
for (descr = ++p; *p && *p != ']'; p++)
|
|
if (*p == '\\' && p[1])
|
|
p++;
|
|
|
|
if (!*p) {
|
|
freecvdef(ret);
|
|
zwarnnam(nam, "invalid value definition: %s", *args, 0);
|
|
return NULL;
|
|
}
|
|
*p++ = '\0';
|
|
c = *p;
|
|
} else {
|
|
*p = '\0';
|
|
descr = NULL;
|
|
}
|
|
if (c && c != ':') {
|
|
freecvdef(ret);
|
|
zwarnnam(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);
|
|
}
|
|
/* Get argument? */
|
|
|
|
if (c == ':') {
|
|
if (hassep && !sep) {
|
|
freecvdef(ret);
|
|
zwarnnam(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, 0, name, &p, NULL);
|
|
} else {
|
|
vtype = CVV_NOARG;
|
|
arg = NULL;
|
|
}
|
|
*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;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* Get the definition from the cache or newly built. */
|
|
|
|
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))) {
|
|
freecvdef(*min);
|
|
*min = new;
|
|
}
|
|
return new;
|
|
}
|
|
|
|
/* Get the definition for a value. */
|
|
|
|
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;
|
|
}
|
|
|
|
/* Handle a xor list. */
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/* Parse state. */
|
|
|
|
struct cvstate {
|
|
Cvdef d;
|
|
Caarg def;
|
|
Cvval val;
|
|
LinkList vals;
|
|
};
|
|
|
|
static struct cvstate cv_laststate;
|
|
static int cv_parsed = 0, cv_alloced = 0;
|
|
|
|
/* Parse the current word. */
|
|
|
|
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;
|
|
state.vals = (LinkList) znewlinklist();
|
|
|
|
cv_alloced = 1;
|
|
|
|
if (d->hassep) {
|
|
if (d->sep) {
|
|
char *end;
|
|
int heq;
|
|
|
|
for (str = compprefix, end = strchr(str, d->sep); end;) {
|
|
*end = '\0';
|
|
|
|
if ((heq = !!(eq = strchr(str, '='))))
|
|
*eq++ = '\0';
|
|
else
|
|
eq = "";
|
|
|
|
if ((ptr = cv_get_val(d, str))) {
|
|
zaddlinknode(state.vals, ztrdup(str));
|
|
zaddlinknode(state.vals, ztrdup(eq));
|
|
|
|
cv_inactive(d, ptr->xor);
|
|
}
|
|
if (heq)
|
|
eq[-1] = '=';
|
|
|
|
*end = d->sep;
|
|
str = end + 1;
|
|
end = strchr(str, d->sep);
|
|
}
|
|
ignore_prefix(str - compprefix);
|
|
|
|
if ((str = strchr(compsuffix, d->sep))) {
|
|
char *beg = str;
|
|
|
|
for (str++; str; str = end) {
|
|
if ((end = strchr(str, d->sep)))
|
|
*end = '\0';
|
|
|
|
if ((heq = !!(eq = strchr(str, '='))))
|
|
*eq++ = '\0';
|
|
else
|
|
eq = "";
|
|
|
|
if ((ptr = cv_get_val(d, str))) {
|
|
zaddlinknode(state.vals, ztrdup(str));
|
|
zaddlinknode(state.vals, ztrdup(eq));
|
|
|
|
cv_inactive(d, ptr->xor);
|
|
}
|
|
if (heq)
|
|
eq[-1] = '=';
|
|
if (end)
|
|
*end++ = d->sep;
|
|
}
|
|
ignore_suffix(strlen(beg));
|
|
}
|
|
} else {
|
|
char tmp[2];
|
|
|
|
tmp[1] = '\0';
|
|
|
|
for (str = compprefix; *str; str++) {
|
|
tmp[0] = *str;
|
|
if ((ptr = cv_get_val(d, tmp))) {
|
|
zaddlinknode(state.vals, ztrdup(tmp));
|
|
zaddlinknode(state.vals, ztrdup(""));
|
|
|
|
cv_inactive(d, ptr->xor);
|
|
}
|
|
}
|
|
for (str = compsuffix; *str; str++) {
|
|
tmp[0] = *str;
|
|
if ((ptr = cv_get_val(d, tmp))) {
|
|
zaddlinknode(state.vals, ztrdup(tmp));
|
|
zaddlinknode(state.vals, ztrdup(""));
|
|
|
|
cv_inactive(d, ptr->xor);
|
|
}
|
|
}
|
|
ignore_prefix(strlen(compprefix));
|
|
ignore_suffix(strlen(compsuffix));
|
|
}
|
|
}
|
|
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) {
|
|
zwarnnam(nam, "can only be called from completion function", NULL, 0);
|
|
return 1;
|
|
}
|
|
if (args[0][0] != '-' || !args[0][1] || args[0][2]) {
|
|
zwarnnam(nam, "invalid argument: %s", args[0], 0);
|
|
return 1;
|
|
}
|
|
if (args[0][1] != 'i' && !cv_parsed) {
|
|
zwarnnam(nam, "no parsed state", NULL, 0);
|
|
return 1;
|
|
}
|
|
switch (args[0][1]) {
|
|
case 'i': min = 2; max = -1; break;
|
|
case 'D': min = 2; max = 2; break;
|
|
case 'C': min = 1; max = 1; break;
|
|
case '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 = 4; break;
|
|
case 'v': min = 1; max = 1; break;
|
|
default:
|
|
zwarnnam(nam, "invalid option: %s", args[0], 0);
|
|
return 1;
|
|
}
|
|
n = arrlen(args) - 1;
|
|
if (n < min) {
|
|
zwarnnam(nam, "not enough arguments", NULL, 0);
|
|
return 1;
|
|
} else if (max >= 0 && n > max) {
|
|
zwarnnam(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 'C':
|
|
{
|
|
Caarg arg = cv_laststate.def;
|
|
|
|
if (arg) {
|
|
setsparam(args[1], ztrdup(arg->opt));
|
|
|
|
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);
|
|
|
|
if (args[4])
|
|
setsparam(args[4], ztrdup(val->name));
|
|
|
|
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 int
|
|
bin_compquote(char *nam, char **args, char *ops, int func)
|
|
{
|
|
char *name;
|
|
struct value vbuf;
|
|
Value v;
|
|
|
|
/* Anything to do? */
|
|
|
|
if (!compqstack || !*compqstack)
|
|
return 0;
|
|
|
|
/* For all parameters given... */
|
|
|
|
while ((name = *args++)) {
|
|
name = dupstring(name);
|
|
if ((v = getvalue(&vbuf, &name, 0))) {
|
|
switch (PM_TYPE(v->pm->flags)) {
|
|
case PM_SCALAR:
|
|
{
|
|
char *val = getstrvalue(v);
|
|
|
|
val = bslashquote(val, NULL,
|
|
(*compqstack == '\'' ? 1 :
|
|
(*compqstack == '"' ? 2 : 0)));
|
|
|
|
setstrvalue(v, ztrdup(val));
|
|
}
|
|
break;
|
|
case PM_ARRAY:
|
|
{
|
|
char **val = v->pm->gets.afn(v->pm);
|
|
char **new = (char **) zalloc((arrlen(val) + 1) *
|
|
sizeof(char *));
|
|
char **p = new;
|
|
|
|
for (; *val; val++, p++)
|
|
*p = ztrdup(bslashquote(*val, NULL,
|
|
(*compqstack == '\'' ? 1 :
|
|
(*compqstack == '"' ? 2 :
|
|
0))));
|
|
*p = NULL;
|
|
|
|
setarrvalue(v, new);
|
|
}
|
|
break;
|
|
default:
|
|
zwarnnam(nam, "invalid parameter type: %s", args[-1], 0);
|
|
}
|
|
} else
|
|
zwarnnam(nam, "unknown parameter: %s", args[-1], 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Tags stuff. */
|
|
|
|
typedef struct ctags *Ctags;
|
|
typedef struct ctset *Ctset;
|
|
|
|
/* A bunch of tag sets. */
|
|
|
|
struct ctags {
|
|
char **all; /* all tags offered */
|
|
char *context; /* the current context */
|
|
int init; /* not yet used */
|
|
Ctset sets; /* the tag sets */
|
|
};
|
|
|
|
/* A tag set. */
|
|
|
|
struct ctset {
|
|
Ctset next;
|
|
char **tags; /* the tags */
|
|
char *tag; /* last tag checked for -A */
|
|
char **ptr; /* ptr into tags for -A */
|
|
};
|
|
|
|
/* Array of tag-set infos. Index is the locallevel. */
|
|
|
|
#define MAX_TAGS 256
|
|
static Ctags comptags[MAX_TAGS];
|
|
|
|
/* locallevel at last comptags -i */
|
|
|
|
static int lasttaglevel;
|
|
|
|
static void
|
|
freectset(Ctset s)
|
|
{
|
|
Ctset n;
|
|
|
|
while (s) {
|
|
n = s->next;
|
|
|
|
if (s->tags)
|
|
freearray(s->tags);
|
|
zsfree(s->tag);
|
|
zfree(s, sizeof(*s));
|
|
|
|
s = n;
|
|
}
|
|
}
|
|
|
|
static void
|
|
freectags(Ctags t)
|
|
{
|
|
if (t) {
|
|
if (t->all)
|
|
freearray(t->all);
|
|
zsfree(t->context);
|
|
freectset(t->sets);
|
|
zfree(t, sizeof(*t));
|
|
}
|
|
}
|
|
|
|
/* Set the tags for the current local level. */
|
|
|
|
static void
|
|
settags(int level, char **tags)
|
|
{
|
|
Ctags t;
|
|
|
|
if (comptags[level])
|
|
freectags(comptags[level]);
|
|
|
|
comptags[level] = t = (Ctags) zalloc(sizeof(*t));
|
|
|
|
t->all = zarrdup(tags + 1);
|
|
t->context = ztrdup(*tags);
|
|
t->sets = NULL;
|
|
t->init = 1;
|
|
}
|
|
|
|
/* Check if an array contains a string. */
|
|
|
|
/**/
|
|
static int
|
|
arrcontains(char **a, char *s, int colon)
|
|
{
|
|
char *p, *q;
|
|
|
|
while (*a) {
|
|
if (colon) {
|
|
for (p = s, q = *a++; *p && *q && *p != ':' && *q != ':'; p++, q++)
|
|
if (*p != *q)
|
|
break;
|
|
if ((!*p || *p == ':') && (!*q || *q == ':'))
|
|
return 1;
|
|
} else if (!strcmp(*a++, s))
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
bin_comptags(char *nam, char **args, char *ops, int func)
|
|
{
|
|
int min, max, n, level;
|
|
|
|
if (incompfunc != 1) {
|
|
zwarnnam(nam, "can only be called from completion function", NULL, 0);
|
|
return 1;
|
|
}
|
|
if (args[0][0] != '-' || !args[0][1] ||
|
|
(args[0][2] && (args[0][2] != '-' || args[0][3]))) {
|
|
zwarnnam(nam, "invalid argument: %s", args[0], 0);
|
|
return 1;
|
|
}
|
|
level = locallevel - (args[0][2] ? 1 : 0);
|
|
if (level >= MAX_TAGS) {
|
|
zwarnnam(nam, "nesting level too deep", NULL, 0);
|
|
return 1;
|
|
}
|
|
if (args[0][1] != 'i' && args[0][1] != 'I' && !comptags[level]) {
|
|
zwarnnam(nam, "no tags registered", NULL, 0);
|
|
return 1;
|
|
}
|
|
switch (args[0][1]) {
|
|
case 'i': min = 2; max = -1; break;
|
|
case 'C': min = 1; max = 1; break;
|
|
case 'T': min = 0; max = 0; break;
|
|
case 'N': min = 0; max = 0; break;
|
|
case 'R': min = 1; max = 1; break;
|
|
case 'S': min = 1; max = 1; break;
|
|
case 'A': min = 2; max = 3; break;
|
|
default:
|
|
zwarnnam(nam, "invalid option: %s", args[0], 0);
|
|
return 1;
|
|
}
|
|
n = arrlen(args) - 1;
|
|
if (n < min) {
|
|
zwarnnam(nam, "not enough arguments", NULL, 0);
|
|
return 1;
|
|
} else if (max >= 0 && n > max) {
|
|
zwarnnam(nam, "too many arguments", NULL, 0);
|
|
return 1;
|
|
}
|
|
switch (args[0][1]) {
|
|
case 'i':
|
|
settags(level, args + 1);
|
|
lasttaglevel = level;
|
|
break;
|
|
case 'C':
|
|
setsparam(args[1], ztrdup(comptags[level]->context));
|
|
break;
|
|
case 'T':
|
|
return !comptags[level]->sets;
|
|
case 'N':
|
|
{
|
|
Ctset s;
|
|
|
|
if (comptags[level]->init)
|
|
comptags[level]->init = 0;
|
|
else if ((s = comptags[level]->sets)) {
|
|
comptags[level]->sets = s->next;
|
|
s->next = NULL;
|
|
freectset(s);
|
|
}
|
|
return !comptags[level]->sets;
|
|
}
|
|
case 'R':
|
|
{
|
|
Ctset s;
|
|
|
|
return !((s = comptags[level]->sets) &&
|
|
arrcontains(s->tags, args[1], 1));
|
|
}
|
|
case 'A':
|
|
{
|
|
Ctset s;
|
|
|
|
if (comptags[level] && (s = comptags[level]->sets)) {
|
|
char **q, *v = NULL;
|
|
int l = strlen(args[1]);
|
|
|
|
if (!s->tag || strcmp(s->tag, args[1])) {
|
|
zsfree(s->tag);
|
|
s->tag = ztrdup(args[1]);
|
|
s->ptr = s->tags;
|
|
}
|
|
for (q = s->ptr; *q; q++) {
|
|
if (strpfx(args[1], *q)) {
|
|
if (!(*q)[l]) {
|
|
v = *q;
|
|
break;
|
|
} else if ((*q)[l] == ':') {
|
|
v = (*q) + l + 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!v) {
|
|
zsfree(s->tag);
|
|
s->tag = NULL;
|
|
return 1;
|
|
}
|
|
s->ptr = q + 1;
|
|
setsparam(args[2], ztrdup(*v == '-' ? dyncat(args[1], v) : v));
|
|
if (args[3]) {
|
|
char *r = dupstring(*q), *p;
|
|
|
|
for (p = r + (v - *q); *p && *p != ':'; p++);
|
|
*p = '\0';
|
|
|
|
setsparam(args[3], ztrdup(r));
|
|
}
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
case 'S':
|
|
if (comptags[level]->sets) {
|
|
char **ret;
|
|
|
|
ret = zarrdup(comptags[level]->sets->tags);
|
|
setaparam(args[1], ret);
|
|
} else
|
|
return 1;
|
|
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
bin_comptry(char *nam, char **args, char *ops, int func)
|
|
{
|
|
if (incompfunc != 1) {
|
|
zwarnnam(nam, "can only be called from completion function", NULL, 0);
|
|
return 1;
|
|
}
|
|
if (!lasttaglevel || !comptags[lasttaglevel]) {
|
|
zwarnnam(nam, "no tags registered", NULL, 0);
|
|
return 1;
|
|
}
|
|
if (*args) {
|
|
if (!strcmp(*args, "-m")) {
|
|
char *s, *p, *q, *c, **all = comptags[lasttaglevel]->all;
|
|
LinkList list = newlinklist();
|
|
LinkNode node;
|
|
int num = 0;
|
|
Ctset set;
|
|
|
|
while ((s = *++args)) {
|
|
while (*s) {
|
|
while (*s && iblank(*s))
|
|
s++;
|
|
for (p = q = s, c = NULL; *s && !inblank(*s); s++) {
|
|
if (!c && *s == ':')
|
|
c = p;
|
|
if (*s == '\\' && s[1])
|
|
s++;
|
|
*p++ = *s;
|
|
}
|
|
if (*s)
|
|
s++;
|
|
*p = '\0';
|
|
if (*q) {
|
|
char *qq, *qqq;
|
|
|
|
if (c)
|
|
*c = '\0';
|
|
|
|
qqq = qq = dupstring(q);
|
|
while (*qqq) {
|
|
if (qqq == qq || qqq[-1] != '\\') {
|
|
if (*qqq == '{')
|
|
*qqq = Inbrace;
|
|
else if (*qqq == '}')
|
|
*qqq = Outbrace;
|
|
else if (*qqq == ',')
|
|
*qqq = Comma;
|
|
}
|
|
qqq++;
|
|
}
|
|
tokenize(qq);
|
|
if (haswilds(qq) || hasbraces(qq)) {
|
|
Patprog prog;
|
|
LinkNode bnode, node;
|
|
LinkList blist = newlinklist();
|
|
|
|
addlinknode(blist, qq);
|
|
for (bnode = firstnode(blist); bnode; incnode(bnode))
|
|
while (hasbraces(getdata(bnode)))
|
|
xpandbraces(blist, &bnode);
|
|
|
|
for (bnode = firstnode(blist); bnode; incnode(bnode)) {
|
|
qq = (char *) getdata(bnode);
|
|
if ((prog = patcompile(qq, PAT_STATIC, NULL))) {
|
|
char **a, *n;
|
|
int l = (c ? strlen(c + 1) + 2 : 1), al;
|
|
|
|
for (a = all; *a; a++) {
|
|
for (node = firstnode(list); node;
|
|
incnode(node)) {
|
|
char *as, *ls;
|
|
|
|
for (as = *a, ls = (char *) getdata(node);
|
|
*as && *ls && *ls != ':'; as++, ls++)
|
|
if (*as != *ls)
|
|
break;
|
|
if (!*as && (!*ls || *ls == ':'))
|
|
break;
|
|
}
|
|
if (node)
|
|
continue;
|
|
if (pattry(prog, *a)) {
|
|
n = (char *) zhalloc((al = strlen(*a)) + l);
|
|
strcpy(n, *a);
|
|
if (c) {
|
|
n[al] = ':';
|
|
strcpy(n + al + 1, c + 1);
|
|
}
|
|
addlinknode(list, n);
|
|
num++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (arrcontains(all, q, 0)) {
|
|
for (set = comptags[lasttaglevel]->sets; set;
|
|
set = set->next)
|
|
if (arrcontains(set->tags, q, 0))
|
|
break;
|
|
if (!set) {
|
|
addlinknode(list, q);
|
|
num++;
|
|
}
|
|
}
|
|
if (c)
|
|
*c = ':';
|
|
}
|
|
}
|
|
if (num) {
|
|
char **a;
|
|
Ctset l;
|
|
|
|
set = (Ctset) zalloc(sizeof(*set));
|
|
|
|
a = set->tags = (char **) zalloc((num + 1) * sizeof(char *));
|
|
for (node = firstnode(list); node; incnode(node))
|
|
*a++ = ztrdup((char *) getdata(node));
|
|
|
|
*a = NULL;
|
|
set->next = NULL;
|
|
set->ptr = NULL;
|
|
set->tag = NULL;
|
|
|
|
if ((l = comptags[lasttaglevel]->sets)) {
|
|
while (l->next)
|
|
l = l->next;
|
|
|
|
l->next = set;
|
|
} else
|
|
comptags[lasttaglevel]->sets = set;
|
|
}
|
|
}
|
|
} else {
|
|
char **p, **q, **all;
|
|
int sep = 0;
|
|
|
|
if ((sep = !strcmp(*args, "-s")))
|
|
args++;
|
|
|
|
for (p = q = args, all = comptags[lasttaglevel]->all; *p; p++)
|
|
if (arrcontains(all, *p, 1)) {
|
|
Ctset s;
|
|
|
|
for (s = comptags[lasttaglevel]->sets; s; s = s->next)
|
|
if (arrcontains(s->tags, *p, 0))
|
|
break;
|
|
|
|
if (!s)
|
|
*q++ = *p;
|
|
}
|
|
*q = NULL;
|
|
|
|
if (*args) {
|
|
char *dummy[2];
|
|
|
|
do {
|
|
Ctset s = (Ctset) zalloc(sizeof(*s)), l;
|
|
|
|
if (sep) {
|
|
dummy[0] = *args++;
|
|
dummy[1] = NULL;
|
|
s->tags = zarrdup(dummy);
|
|
} else
|
|
s->tags = zarrdup(args);
|
|
s->next = NULL;
|
|
s->ptr = NULL;
|
|
s->tag = NULL;
|
|
|
|
if ((l = comptags[lasttaglevel]->sets)) {
|
|
while (l->next)
|
|
l = l->next;
|
|
|
|
l->next = s;
|
|
} else
|
|
comptags[lasttaglevel]->sets = s;
|
|
} while (sep && *args);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#define PATH_MAX2 (PATH_MAX * 2)
|
|
|
|
static LinkList
|
|
cfp_test_exact(LinkList names, char **accept, char *skipped)
|
|
{
|
|
char buf[PATH_MAX2 + 1], *suf, *p;
|
|
int l, sl, found = 0;
|
|
struct stat st;
|
|
LinkNode node;
|
|
LinkList ret = newlinklist(), alist = NULL;
|
|
|
|
if ((!(compprefix && *compprefix) && !(compsuffix && *compsuffix)) ||
|
|
(!accept || !*accept ||
|
|
((!strcmp(*accept, "false") || !strcmp(*accept, "no") ||
|
|
!strcmp(*accept, "off") || !strcmp(*accept, "0")) && !accept[1])))
|
|
return NULL;
|
|
|
|
if (accept[1] ||
|
|
(strcmp(*accept, "true") && strcmp(*accept, "yes") &&
|
|
strcmp(*accept, "on") && strcmp(*accept, "1"))) {
|
|
Patprog prog;
|
|
|
|
alist = newlinklist();
|
|
|
|
for (; (p = *accept); accept++) {
|
|
if (*p == '*' && !p[1]) {
|
|
alist = NULL;
|
|
break;
|
|
}
|
|
tokenize(p = dupstring(p));
|
|
if ((prog = patcompile(p, 0, NULL)))
|
|
addlinknode(alist, prog);
|
|
}
|
|
}
|
|
sl = strlen(skipped) + (compprefix ? strlen(compprefix) : 0) +
|
|
(compsuffix ? strlen(compsuffix) : 0);
|
|
|
|
if (sl > PATH_MAX2)
|
|
return NULL;
|
|
|
|
suf = dyncat(skipped, rembslash(dyncat(compprefix, compsuffix)));
|
|
|
|
for (node = firstnode(names); node; incnode(node)) {
|
|
l = strlen(p = (char *) getdata(node));
|
|
if (l + sl < PATH_MAX2) {
|
|
strcpy(buf, p);
|
|
strcpy(buf + l, suf);
|
|
|
|
if (!ztat(buf, &st, 0)) {
|
|
if (alist) {
|
|
LinkNode anode;
|
|
|
|
for (anode = firstnode(alist); anode; incnode(anode))
|
|
if (pattry((Patprog) getdata(anode), buf))
|
|
break;
|
|
|
|
if (!anode)
|
|
continue;
|
|
}
|
|
found = 1;
|
|
addlinknode(ret, dupstring(buf));
|
|
}
|
|
}
|
|
}
|
|
return (found ? ret : NULL);
|
|
}
|
|
|
|
static char *
|
|
cfp_matcher_pats(char *matcher, char *add)
|
|
{
|
|
Cmatcher m = parse_cmatcher(NULL, matcher);
|
|
|
|
if (m && m != pcm_err) {
|
|
char *tmp;
|
|
int al = strlen(add), tl;
|
|
VARARR(Cmatcher, ms, al);
|
|
Cmatcher *mp;
|
|
Cpattern stopp;
|
|
int stopl = 0;
|
|
|
|
memset(ms, 0, al * sizeof(Cmatcher));
|
|
|
|
for (; m && *add; m = m->next) {
|
|
stopp = NULL;
|
|
if (!(m->flags & (CMF_LEFT|CMF_RIGHT))) {
|
|
if (m->llen == 1 && m->wlen == 1) {
|
|
for (tmp = add, tl = al, mp = ms; tl; tl--, tmp++, mp++) {
|
|
if (pattern_match(m->line, tmp, NULL, NULL)) {
|
|
if (*mp) {
|
|
*tmp = '\0';
|
|
al = tmp - add;
|
|
break;
|
|
} else
|
|
*mp = m;
|
|
}
|
|
}
|
|
} else {
|
|
stopp = m->line;
|
|
stopl = m->llen;
|
|
}
|
|
} else if (m->flags & CMF_RIGHT) {
|
|
if (m->wlen < 0 && !m->llen && m->ralen == 1) {
|
|
for (tmp = add, tl = al, mp = ms; tl; tl--, tmp++, mp++) {
|
|
if (pattern_match(m->right, tmp, NULL, NULL)) {
|
|
if (*mp || (tmp == add && *tmp == '.')) {
|
|
*tmp = '\0';
|
|
al = tmp - add;
|
|
break;
|
|
} else
|
|
*mp = m;
|
|
}
|
|
}
|
|
} else if (m->llen) {
|
|
stopp = m->line;
|
|
stopl = m->llen;
|
|
} else {
|
|
stopp = m->right;
|
|
stopl = m->ralen;
|
|
}
|
|
} else {
|
|
if (!m->lalen)
|
|
return "";
|
|
|
|
stopp = m->left;
|
|
stopl = m->lalen;
|
|
}
|
|
if (stopp)
|
|
for (tmp = add, tl = al; tl >= stopl; tl--, tmp++)
|
|
if (pattern_match(stopp, tmp, NULL, NULL)) {
|
|
*tmp = '\0';
|
|
al = tmp - add;
|
|
break;
|
|
}
|
|
}
|
|
if (*add) {
|
|
char *ret = "", buf[259];
|
|
|
|
for (mp = ms; *add; add++, mp++) {
|
|
if (!(m = *mp)) {
|
|
buf[0] = *add;
|
|
buf[1] = '\0';
|
|
} else if (m->flags & CMF_RIGHT) {
|
|
buf[0] = '*';
|
|
buf[1] = *add;
|
|
buf[2] = '\0';
|
|
} else {
|
|
unsigned char *t, c;
|
|
char *p = buf;
|
|
int i;
|
|
|
|
for (i = 256, t = m->word->tab; i--; t++)
|
|
if (*t)
|
|
break;
|
|
if (i) {
|
|
t = m->word->tab;
|
|
*p++ = '[';
|
|
if (m->line->equiv && m->word->equiv) {
|
|
*p++ = *add;
|
|
c = m->line->tab[STOUC(*add)];
|
|
for (i = 0; i < 256; i++)
|
|
if (m->word->tab[i] == c) {
|
|
*p++ = (char) i;
|
|
break;
|
|
}
|
|
} else {
|
|
if (*add == ']' || t[STOUC(']')])
|
|
*p++ = ']';
|
|
for (i = 0; i < 256; i++, t++)
|
|
if (*t && ((char) i) != *add &&
|
|
i != ']' && i != '-' &&
|
|
i != '^' && i != '!')
|
|
*p++ = (char) i;
|
|
*p++ = *add;
|
|
t = m->word->tab;
|
|
if (*add != '^' && t[STOUC('^')])
|
|
*p++ = '^';
|
|
if (*add != '!' && t[STOUC('!')])
|
|
*p++ = '!';
|
|
if (*add != '-' && t[STOUC('-')])
|
|
*p++ = '-';
|
|
}
|
|
*p++ = ']';
|
|
*p = '\0';
|
|
} else {
|
|
*p = '?';
|
|
p[1] = '\0';
|
|
}
|
|
}
|
|
ret = dyncat(ret, buf);
|
|
}
|
|
return ret;
|
|
}
|
|
}
|
|
return add;
|
|
}
|
|
|
|
static void
|
|
cfp_opt_pats(char **pats, char *matcher)
|
|
{
|
|
char *add, **p, *q, *t, *s;
|
|
|
|
if (!compprefix || !*compprefix)
|
|
return;
|
|
|
|
if (comppatmatch && *comppatmatch) {
|
|
tokenize(t = rembslash(dyncat(compprefix, compsuffix)));
|
|
remnulargs(t);
|
|
if (haswilds(t))
|
|
return;
|
|
}
|
|
add = (char *) zhalloc(sizeof(compprefix) * 2 + 1);
|
|
for (s = compprefix, t = add; *s; s++) {
|
|
if (*s != '\\' || !s[1] || s[1] == '*' || s[1] == '?' ||
|
|
s[1] == '<' || s[1] == '>' || s[1] == '(' || s[1] == ')' ||
|
|
s[1] == '[' || s[1] == ']' || s[1] == '|' || s[1] == '#' ||
|
|
s[1] == '^' || s[1] == '~') {
|
|
if ((s == compprefix || s[-1] != '\\') &&
|
|
(*s == '*' || *s == '?' || *s == '<' || *s == '>' ||
|
|
*s == '(' || *s == ')' || *s == '[' || *s == ']' ||
|
|
*s == '|' || *s == '#' || *s == '^' || *s == '~'))
|
|
*t++ = '\\';
|
|
*t++ = *s;
|
|
}
|
|
}
|
|
*t = '\0';
|
|
for (p = pats; *add && (q = *p); p++) {
|
|
if (*q) {
|
|
q = dupstring(q);
|
|
t = q + strlen(q) - 1;
|
|
if (*t == ')') {
|
|
for (s = t--; t > q; t--)
|
|
if (*t == ')' || *t == '|' || *t == '~' || *t == '(')
|
|
break;
|
|
if (t != q && *t == '(')
|
|
*t = '\0';
|
|
}
|
|
for (; *q && *add; q++) {
|
|
if (*q == '\\' && q[1]) {
|
|
for (s = add, q++; *s && *s != *q; s++);
|
|
*s = '\0';
|
|
} else if (*q == '<') {
|
|
for (s = add; *s && !idigit(*s); s++);
|
|
*s = '\0';
|
|
} else if (*q == '[') {
|
|
int not, first = 1;
|
|
char *x = ++q;
|
|
|
|
if ((not = (*x == '!' || *x == '^')))
|
|
x++;
|
|
for (; *x && (first || *x != ']'); x++) {
|
|
if (x[1] == '-' && x[2]) {
|
|
char c1 = *x, c2 = x[2];
|
|
|
|
for (s = add; *s && (*x < c1 || *x > c2); s++);
|
|
*s = '\0';
|
|
} else {
|
|
for (s = add; *s && *s != *x; s++);
|
|
*s = '\0';
|
|
}
|
|
}
|
|
} else if (*q != '?' && *q != '*' && *q != '(' && *q != ')' &&
|
|
*q != '|' && *q != '~' && *q != '#') {
|
|
for (s = add; *s && *s != *q; s++);
|
|
*s = '\0';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (*add) {
|
|
if (*matcher && !(add = cfp_matcher_pats(matcher, add)))
|
|
return;
|
|
|
|
for (p = pats; *p; p++)
|
|
if (**p == '*')
|
|
*p = dyncat(add, *p);
|
|
}
|
|
}
|
|
|
|
static LinkList
|
|
cfp_bld_pats(int dirs, LinkList names, char *skipped, char **pats)
|
|
{
|
|
LinkList ret = newlinklist();
|
|
LinkNode node;
|
|
int ol, sl = strlen(skipped), pl, dot;
|
|
char **p, *o, *str;
|
|
|
|
dot = (unset(GLOBDOTS) && compprefix && *compprefix == '.');
|
|
for (node = firstnode(names); node; incnode(node)) {
|
|
ol = strlen(o = (char *) getdata(node));
|
|
for (p = pats; *p; p++) {
|
|
pl = strlen(*p);
|
|
str = (char *) zhalloc(ol + sl + pl + 1);
|
|
strcpy(str, o);
|
|
strcpy(str + ol, skipped);
|
|
strcpy(str + ol + sl, *p);
|
|
addlinknode(ret, str);
|
|
if (dot && **p != '.') {
|
|
str = (char *) zhalloc(ol + sl + pl + 2);
|
|
strcpy(str, o);
|
|
strcpy(str + ol, skipped);
|
|
str[ol + sl] = '.';
|
|
strcpy(str + ol + sl + 1, *p);
|
|
addlinknode(ret, str);
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static LinkList
|
|
cfp_add_sdirs(LinkList final, LinkList orig, char *skipped,
|
|
char *sdirs, char **fake)
|
|
{
|
|
int add = 0;
|
|
|
|
if (*sdirs && (isset(GLOBDOTS) || (compprefix && *compprefix == '.'))) {
|
|
if (!strcmp(sdirs, "yes") || !strcmp(sdirs, "true") ||
|
|
!strcmp(sdirs, "on") || !strcmp(sdirs, "1"))
|
|
add = 2;
|
|
else if (!strcmp(sdirs, ".."))
|
|
add = 1;
|
|
}
|
|
if (add) {
|
|
LinkNode node;
|
|
char *s1 = dyncat(skipped, "..");
|
|
char *s2 = (add == 2 ? dyncat(skipped, ".") : NULL), *m;
|
|
|
|
for (node = firstnode(orig); node; incnode(node)) {
|
|
if ((m = (char *) getdata(node))) {
|
|
addlinknode(final, dyncat(m, s1));
|
|
if (s2)
|
|
addlinknode(final, dyncat(m, s2));
|
|
}
|
|
}
|
|
}
|
|
if (fake && *fake) {
|
|
LinkNode node;
|
|
char *m, *f, *p, *t, *a, c;
|
|
int sl = strlen(skipped) + 1;
|
|
struct stat st1, st2;
|
|
|
|
for (; (f = *fake); fake++) {
|
|
f = dupstring(f);
|
|
for (p = t = f; *p; p++) {
|
|
if (*p == ':')
|
|
break;
|
|
else if (*p == '\\' && p[1])
|
|
p++;
|
|
*t++ = *p;
|
|
}
|
|
if (*p) {
|
|
*t = *p++ = '\0';
|
|
if (!*p)
|
|
continue;
|
|
|
|
for (node = firstnode(orig); node; incnode(node)) {
|
|
if ((m = (char *) getdata(node)) &&
|
|
(!strcmp(f, m) ||
|
|
(!stat(f, &st1) && !stat((*m ? m : "."), &st2) &&
|
|
st1.st_dev == st2.st_dev &&
|
|
st1.st_ino == st2.st_ino))) {
|
|
while (*p) {
|
|
while (*p && inblank(*p))
|
|
p++;
|
|
if (!*p)
|
|
break;
|
|
for (f = t = p; *p; p++) {
|
|
if (inblank(*p))
|
|
break;
|
|
else if (*p == '\\' && p[1])
|
|
p++;
|
|
*t++ = *p;
|
|
}
|
|
c = *t;
|
|
*t = '\0';
|
|
a = (char *) zhalloc(strlen(m) + sl + strlen(f));
|
|
strcpy(a, m);
|
|
strcat(a, skipped);
|
|
strcat(a, f);
|
|
addlinknode(final, a);
|
|
*t = c;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return final;
|
|
}
|
|
|
|
static LinkList
|
|
cf_pats(int dirs, int noopt, LinkList names, char **accept, char *skipped,
|
|
char *matcher, char *sdirs, char **fake, char **pats)
|
|
{
|
|
LinkList ret;
|
|
char *dpats[2];
|
|
|
|
if ((ret = cfp_test_exact(names, accept, skipped)))
|
|
return cfp_add_sdirs(ret, names, skipped, sdirs, fake);
|
|
|
|
if (dirs) {
|
|
dpats[0] = "*(-/)";
|
|
dpats[1] = NULL;
|
|
pats = dpats;
|
|
}
|
|
if (!noopt)
|
|
cfp_opt_pats(pats, matcher);
|
|
|
|
return cfp_add_sdirs(cfp_bld_pats(dirs, names, skipped, pats),
|
|
names, skipped, sdirs, fake);
|
|
}
|
|
|
|
static void
|
|
cf_ignore(char **names, LinkList ign, char *style, char *path)
|
|
{
|
|
int pl = strlen(path), tpar, tpwd, found;
|
|
struct stat nst, est, st;
|
|
char *n, *c, *e;
|
|
|
|
tpar = !!strstr(style, "parent");
|
|
if ((tpwd = !!strstr(style, "pwd")) && stat(pwd, &est))
|
|
tpwd = 0;
|
|
|
|
if (!tpar && !tpwd)
|
|
return;
|
|
|
|
for (; (n = *names); names++) {
|
|
if (!ztat(n, &nst, 0) && S_ISDIR(nst.st_mode)) {
|
|
if (tpwd && nst.st_dev == est.st_dev && nst.st_ino == est.st_ino) {
|
|
addlinknode(ign, bslashquote(n, NULL, 0));
|
|
continue;
|
|
}
|
|
if (tpar && !strncmp((c = dupstring(n)), path, pl)) {
|
|
found = 0;
|
|
while ((e = strrchr(c, '/')) && e > c + pl) {
|
|
*e = '\0';
|
|
if (!ztat(c, &st, 0) &&
|
|
st.st_dev == nst.st_dev && st.st_ino == nst.st_ino) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (found || ((e = strrchr(c, '/')) && e > c + pl &&
|
|
!ztat(c, &st, 0) && st.st_dev == nst.st_dev &&
|
|
st.st_ino == nst.st_ino))
|
|
addlinknode(ign, bslashquote(n, NULL, 0));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static LinkList
|
|
cf_remove_other(char **names, char *pre, int *amb)
|
|
{
|
|
char *p;
|
|
|
|
if ((p = strchr(pre, '/'))) {
|
|
char **n;
|
|
|
|
*p = '\0';
|
|
pre = dyncat(pre, "/");
|
|
*p = '/';
|
|
|
|
for (n = names; *n; n++)
|
|
if (strpfx(pre, *n))
|
|
break;
|
|
|
|
if (*n) {
|
|
LinkList ret = newlinklist();
|
|
|
|
for (; *names; names++)
|
|
if (strpfx(pre, *names))
|
|
addlinknode(ret, dupstring(*names));
|
|
|
|
*amb = 0;
|
|
|
|
return ret;
|
|
} else {
|
|
if (!(p = *names++))
|
|
*amb = 0;
|
|
else {
|
|
char *q;
|
|
|
|
if ((q = strchr((p = dupstring(p)), '/')))
|
|
*q = '\0';
|
|
|
|
for (; *names; names++)
|
|
if (!strpfx(p, *names)) {
|
|
*amb = 1;
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (!(p = *names++))
|
|
*amb = 0;
|
|
else
|
|
for (; *names; names++)
|
|
if (strcmp(p, *names)) {
|
|
*amb = 1;
|
|
return NULL;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
bin_compfiles(char *nam, char **args, char *ops, int func)
|
|
{
|
|
if (**args != '-') {
|
|
zwarnnam(nam, "missing option: %s", *args, 0);
|
|
return 1;
|
|
}
|
|
switch (args[0][1]) {
|
|
case 'p':
|
|
case 'P':
|
|
if (args[0][2] && (args[0][2] != '-' || args[0][3])) {
|
|
zwarnnam(nam, "invalid option: %s", *args, 0);
|
|
return 1;
|
|
} else {
|
|
char **tmp;
|
|
LinkList l;
|
|
|
|
if (!args[1] || !args[2] || !args[3] || !args[4] || !args[5] ||
|
|
!args[6] || (args[0][1] == 'p' && !args[7])) {
|
|
zwarnnam(nam, "too few arguments", NULL, 0);
|
|
return 1;
|
|
}
|
|
if (!(tmp = getaparam(args[1]))) {
|
|
zwarnnam(nam, "unknown parameter: %s", args[1], 0);
|
|
return 0;
|
|
}
|
|
for (l = newlinklist(); *tmp; tmp++)
|
|
addlinknode(l, *tmp);
|
|
set_list_array(args[1], cf_pats((args[0][1] == 'P'), !!args[0][2],
|
|
l, getaparam(args[2]), args[3],
|
|
args[4], args[5],
|
|
getaparam(args[6]), args + 7));
|
|
return 0;
|
|
}
|
|
case 'i':
|
|
if (args[0][2]) {
|
|
zwarnnam(nam, "invalid option: %s", *args, 0);
|
|
return 1;
|
|
} else {
|
|
char **tmp;
|
|
LinkList l;
|
|
|
|
if (!args[1] || !args[2] || !args[3] || !args[4]) {
|
|
zwarnnam(nam, "too few arguments", NULL, 0);
|
|
return 1;
|
|
}
|
|
if (args[5]) {
|
|
zwarnnam(nam, "too many arguments", NULL, 0);
|
|
return 1;
|
|
}
|
|
tmp = getaparam(args[2]);
|
|
l = newlinklist();
|
|
if (tmp)
|
|
for (; *tmp; tmp++)
|
|
addlinknode(l, *tmp);
|
|
if (!(tmp = getaparam(args[1]))) {
|
|
zwarnnam(nam, "unknown parameter: %s", args[1], 0);
|
|
return 0;
|
|
}
|
|
cf_ignore(tmp, l, args[3], args[4]);
|
|
set_list_array(args[2], l);
|
|
return 0;
|
|
}
|
|
case 'r':
|
|
{
|
|
char **tmp;
|
|
LinkList l;
|
|
int ret = 0;
|
|
|
|
if (!args[1] || !args[2]) {
|
|
zwarnnam(nam, "too few arguments", NULL, 0);
|
|
return 1;
|
|
}
|
|
if (args[3]) {
|
|
zwarnnam(nam, "too many arguments", NULL, 0);
|
|
return 1;
|
|
}
|
|
if (!(tmp = getaparam(args[1]))) {
|
|
zwarnnam(nam, "unknown parameter: %s", args[1], 0);
|
|
return 0;
|
|
}
|
|
if ((l = cf_remove_other(tmp, args[2], &ret)))
|
|
set_list_array(args[1], l);
|
|
return ret;
|
|
}
|
|
}
|
|
zwarnnam(nam, "invalid option: %s", *args, 0);
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
bin_compgroups(char *nam, char **args, char *ops, int func)
|
|
{
|
|
Heap oldheap;
|
|
char *n;
|
|
|
|
SWITCHHEAPS(oldheap, compheap) {
|
|
while ((n = *args++)) {
|
|
endcmgroup(NULL);
|
|
begcmgroup(n, 0);
|
|
endcmgroup(NULL);
|
|
begcmgroup(n, CGF_NOSORT);
|
|
endcmgroup(NULL);
|
|
begcmgroup(n, CGF_UNIQALL);
|
|
endcmgroup(NULL);
|
|
begcmgroup(n, CGF_NOSORT|CGF_UNIQCON);
|
|
endcmgroup(NULL);
|
|
begcmgroup(n, CGF_UNIQALL);
|
|
endcmgroup(NULL);
|
|
begcmgroup(n, CGF_NOSORT|CGF_UNIQCON);
|
|
}
|
|
} SWITCHBACKHEAPS(oldheap);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin bintab[] = {
|
|
BUILTIN("compdescribe", 0, bin_compdescribe, 3, -1, 0, NULL, NULL),
|
|
BUILTIN("comparguments", 0, bin_comparguments, 1, -1, 0, NULL, NULL),
|
|
BUILTIN("compvalues", 0, bin_compvalues, 1, -1, 0, NULL, NULL),
|
|
BUILTIN("compquote", 0, bin_compquote, 1, -1, 0, NULL, NULL),
|
|
BUILTIN("comptags", 0, bin_comptags, 1, -1, 0, NULL, NULL),
|
|
BUILTIN("comptry", 0, bin_comptry, 0, -1, 0, NULL, NULL),
|
|
BUILTIN("compfiles", 0, bin_compfiles, 1, -1, 0, NULL, NULL),
|
|
BUILTIN("compgroups", 0, bin_compgroups, 1, -1, 0, NULL, NULL),
|
|
};
|
|
|
|
|
|
/**/
|
|
int
|
|
setup_(Module m)
|
|
{
|
|
memset(cadef_cache, 0, sizeof(cadef_cache));
|
|
memset(cvdef_cache, 0, sizeof(cvdef_cache));
|
|
|
|
memset(comptags, 0, sizeof(comptags));
|
|
|
|
lasttaglevel = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**/
|
|
int
|
|
boot_(Module m)
|
|
{
|
|
return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
|
|
}
|
|
|
|
/**/
|
|
int
|
|
cleanup_(Module m)
|
|
{
|
|
deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
|
|
return 0;
|
|
}
|
|
|
|
/**/
|
|
int
|
|
finish_(Module m)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_CACACHE; i++)
|
|
freecadef(cadef_cache[i]);
|
|
for (i = 0; i < MAX_CVCACHE; i++)
|
|
freecvdef(cvdef_cache[i]);
|
|
|
|
for (i = 0; i < MAX_TAGS; i++)
|
|
freectags(comptags[i]);
|
|
|
|
return 0;
|
|
}
|