1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-10-03 20:20:56 +02:00

pat(-) in long-option patterns to say that argument should be completed only after the `='; merge _arguments and _argument_sets; slightly better test when not to complete options (11489)

This commit is contained in:
Sven Wischnowsky 2000-05-22 09:28:34 +00:00
parent 007cfdff55
commit ce90081ea8
5 changed files with 424 additions and 379 deletions

View file

@ -1,5 +1,10 @@
2000-05-22 Sven Wischnowsky <wischnow@zsh.org> 2000-05-22 Sven Wischnowsky <wischnow@zsh.org>
* 11489: Completion/Base/_arguments, Doc/Zsh/compsys.yo,
Src/Zle/computil.c: pat(-) in long-option patterns to say that
argument should be completed only after the `='; merge _arguments
and _argument_sets; slightly better test when not to complete options
* 11487: Completion/Core/_expand, Src/Zle/complist.c: fix for * 11487: Completion/Core/_expand, Src/Zle/complist.c: fix for
a-a-m-c in menu selection; scroll explanations onto screen again a-a-m-c in menu selection; scroll explanations onto screen again
when cursor comes near them when cursor comes near them

View file

@ -1,86 +0,0 @@
#autoload
local all ret=1 end xor has_args had_args ostate ocontext oopt_args r
local nm="$compstate[nmatches]"
local opre="$PREFIX" oipre="$IPREFIX" ocur="$CURRENT"
local osuf="$SUFFIX" oisuf="$ISUFFIX" owords
local _ms_match _ms_opt _ms_soptmid _ms_soptmidadd _ms_soptend
local _ms_optnext _ms_optdirect _ms_optequal
_ms_soptmid=()
_ms_soptmidadd=()
_ms_soptend=()
_ms_optnext=()
_ms_optdirect=()
_ms_optequal=()
owords=("$words[@]")
end=$argv[(i)-]
[[ end -gt $# ]] && return 1
all=( "${(@)argv[1,end]}" )
shift end
xor=()
ostate=()
ocontext=()
oopt_args=()
while true; do
end=$argv[(i)-]
if [[ "$1" = \(*\) ]]; then
_arguments -m xor "${1[2,-2]}" "$all[@]" \
"$1${(@)^argv[2,end-1]:#\(*}" \
"${1[1,-2]} ${(@)${(@M)^argv[2,end-1]:#\(*}#?}"
else
_arguments -m xor "$1" "$all[@]" "${(@)argv[2,end-1]}"
fi
r=$?
oopt_args=( "$oopt_args[@]" "${(@kv)opt_args}" )
if [[ r -eq 300 ]]; then
ret=300
ostate=( "$ostate[@]" "$state[@]" )
ocontext=( "$ocontext[@]" "$context[@]" )
PREFIX="$opre" SUFFIX="$osuf"
IPREFIX="$oipre" ISUFFIX="$oisuf"
CURRENT="$ocur" words=( "$owords[@]" )
elif [[ "$r$ret" = 01 ]]; then
ret=0
fi
[[ end -gt $# ]] && break
shift end
done
[[ -n "$_ms_opt" ]] &&
{ ! zstyle -T ":completion:${curcontext}:options" prefix-needed ||
[[ "$PREFIX" = [-+]* ||
( -z "$has_args" && ret -ne 300 && nm -eq compstate[nmatches] ) ]] } &&
has_args=yes &&
_describe -o option \
_ms_soptmid _ms_soptmidadd -Q -S '' -- \
_ms_soptend -Q -- \
_ms_optnext -Q -M "$_ms_match" -- \
_ms_optdirect -QS '' -M "$_ms_match" -- \
_ms_optequal -QqS= -M "$_ms_match" && [[ ret -eq 1 ]] && ret=0
opt_args=( "$oopt_args[@]" )
if [[ ret -eq 300 ]]; then
state=( "$ostate[@]" )
context=( "$ocontext[@]" )
elif [[ -z "$has_args" ]]; then
if [[ -n "$had_args" ]]; then
_message "no more arguments"
else
_message "no arguments"
fi
fi
return ret

View file

@ -4,7 +4,7 @@
# descriptions given as arguments to this function. # descriptions given as arguments to this function.
local long cmd="$words[1]" descr mesg subopts opt usecc autod local long cmd="$words[1]" descr mesg subopts opt usecc autod
local oldcontext="$curcontext" hasopts multi ismulti local oldcontext="$curcontext" hasopts
long=$argv[(I)--] long=$argv[(I)--]
if (( long )); then if (( long )); then
@ -23,7 +23,7 @@ if (( long )); then
name="${name//[^a-zA-Z0-9_]/_}" name="${name//[^a-zA-Z0-9_]/_}"
if (( ! ${(P)+name} )); then if (( ! ${(P)+name} )); then
local iopts sopts pattern tmpo cur cache local iopts sopts pattern tmpo dir cur cache
typeset -U lopts typeset -U lopts
cache=() cache=()
@ -98,6 +98,12 @@ if (( long )); then
pattern="${${${(M)1#*[^\\]:}[1,-2]}//\\\\:/:}" pattern="${${${(M)1#*[^\\]:}[1,-2]}//\\\\:/:}"
descr="${1#${pattern}}" descr="${1#${pattern}}"
if [[ "$pattern" = *\(-\) ]]; then
pattern="$pattern[1,-4]"
dir=-
else
dir=
fi
shift shift
# We get all options matching the pattern and take them from the # We get all options matching the pattern and take them from the
@ -111,7 +117,7 @@ if (( long )); then
opt='' opt=''
# If there are option strings with a `[=', we take these get an # If there are option strings with a `[=', we take these to get an
# optional argument. # optional argument.
tmpo=("${(@M)tmp:#*\[\=*}") tmpo=("${(@M)tmp:#*\[\=*}")
@ -120,9 +126,9 @@ if (( long )); then
tmpo=("${(@)${(@)tmpo%%\=*}//[^a-z0-9-]}") tmpo=("${(@)${(@)tmpo%%\=*}//[^a-z0-9-]}")
if [[ "$descr" = ::* ]]; then if [[ "$descr" = ::* ]]; then
cache=( "$cache[@]" "${(@)^tmpo}=${descr}" ) cache=( "$cache[@]" "${(@)^tmpo}=${dir}${descr}" )
else else
cache=( "$cache[@]" "${(@)^tmpo}=:${descr}" ) cache=( "$cache[@]" "${(@)^tmpo}=${dir}:${descr}" )
fi fi
fi fi
@ -133,10 +139,10 @@ if (( long )); then
tmp=("${(@)tmp:#*\=*}") tmp=("${(@)tmp:#*\=*}")
tmpo=("${(@)${(@)tmpo%%\=*}//[^a-z0-9-]}") tmpo=("${(@)${(@)tmpo%%\=*}//[^a-z0-9-]}")
cache=( "$cache[@]" "${(@)^tmpo}=${descr}" ) cache=( "$cache[@]" "${(@)^tmpo}=${dir}${descr}" )
fi fi
# Everything else is just added as a option without arguments. # Everything else is just added as an option without arguments.
if (( $#tmp )); then if (( $#tmp )); then
tmp=("${(@)tmp//[^a-zA-Z0-9-]}") tmp=("${(@)tmp//[^a-zA-Z0-9-]}")
@ -148,27 +154,22 @@ if (( long )); then
set -- "$tmpargv[@]" "${(@P)name}" set -- "$tmpargv[@]" "${(@P)name}"
fi fi
multi=(-i)
subopts=() subopts=()
while [[ "$1" = -(O*|C|m*) ]]; do while [[ "$1" = -(O*|C|m*) ]]; do
case "$1" in case "$1" in
-C) usecc=yes; shift ;; -C) usecc=yes; shift ;;
-O) subopts=( "${(@P)2}" ); shift 2 ;; -O) subopts=( "${(@P)2}" ); shift 2 ;;
-O*) subopts=( "${(@P)1[3,-1]}" ); shift ;; -O*) subopts=( "${(@P)1[3,-1]}" ); shift ;;
-m) ismulti=yes multi=(-I "$2" "$3"); shift 3 ;;
-m*) ismulti=yes multi=(-I "${1[3,-1]}" "$2"); shift 2 ;;
esac esac
done done
zstyle -s ":completion:${curcontext}:options" auto-description autod zstyle -s ":completion:${curcontext}:options" auto-description autod
if (( $# )) && comparguments "$multi[@]" "$autod" "$@"; then if (( $# )) && comparguments -i "$autod" "$@"; then
local action noargs aret expl local tried local action noargs aret expl local tried
local next direct odirect equal single matcher matched ws tmp1 tmp2 tmp3 local next direct odirect equal single matcher matched ws tmp1 tmp2 tmp3
local opts subc tc prefix suffix descrs actions subcs local opts subc tc prefix suffix descrs actions subcs
local origpre="$PREFIX" origipre="$IPREFIX" local origpre="$PREFIX" origipre="$IPREFIX" nm="$compstate[nmatches]"
[[ -z "$ismulti" ]] && local nm="$compstate[nmatches]"
if comparguments -D descrs actions subcs; then if comparguments -D descrs actions subcs; then
if comparguments -O next direct odirect equal; then if comparguments -O next direct odirect equal; then
@ -180,7 +181,6 @@ if (( $# )) && comparguments "$multi[@]" "$autod" "$@"; then
else else
if comparguments -a; then if comparguments -a; then
noargs='no more arguments' noargs='no more arguments'
had_args=yes
else else
noargs='no arguments' noargs='no arguments'
fi fi
@ -299,8 +299,7 @@ if (( $# )) && comparguments "$multi[@]" "$autod" "$@"; then
done done
if [[ -z "$matched$hasopts" ]] && _requested options && if [[ -z "$matched$hasopts" ]] && _requested options &&
{ [[ -n "$ismulti" ]] || { ! zstyle -T ":completion:${curcontext}:options" prefix-needed ||
! zstyle -T ":completion:${curcontext}:options" prefix-needed ||
[[ "$origpre" = [-+]* || -z "$aret$mesg$tried" ]] } ; then [[ "$origpre" = [-+]* || -z "$aret$mesg$tried" ]] } ; then
local prevpre="$PREFIX" previpre="$IPREFIX" local prevpre="$PREFIX" previpre="$IPREFIX"
@ -326,37 +325,22 @@ if (( $# )) && comparguments "$multi[@]" "$autod" "$@"; then
tmp1=( "${(M@)tmp1:#[-+]?(|:*)}" ) tmp1=( "${(M@)tmp1:#[-+]?(|:*)}" )
tmp2=( "${PREFIX}${(@M)^${(@)${(@)tmp1%%:*}#[-+]}:#?}" ) tmp2=( "${PREFIX}${(@M)^${(@)${(@)tmp1%%:*}#[-+]}:#?}" )
if [[ -n "$ismulti" ]]; then _describe -o option \
_ms_opt=yes tmp1 tmp2 -Q -S '' -- \
_ms_soptmid=( "$_ms_soptmid[@]" "$tmp1[@]" ) tmp3 -Q
_ms_soptmidadd=( "$_ms_soptmidadd[@]" "$tmp2[@]" )
_ms_soptend=( "$_ms_soptend[@]" "$tmp3[@]" )
else
_describe -o option \
tmp1 tmp2 -Q -S '' -- \
tmp3 -Q
fi
fi fi
single=yes single=yes
else else
next=( "$next[@]" "$odirect[@]" ) next=( "$next[@]" "$odirect[@]" )
if [[ -n "$ismulti" ]]; then _describe -o option \
_ms_opt=yes next -Q -M "$matcher" -- \
_ms_match="$_ms_match $matcher" direct -QS '' -M "$matcher" -- \
_ms_optnext=( "$_ms_optnext[@]" "$next[@]" ) equal -QqS= -M "$matcher"
_ms_optdirect=( "$_ms_optdirect[@]" "$direct[@]" )
_ms_optequal=( "$_ms_optequal[@]" "$equal[@]" )
else
_describe -o option \
next -Q -M "$matcher" -- \
direct -QS '' -M "$matcher" -- \
equal -QqS= -M "$matcher"
fi
fi fi
PREFIX="$prevpre" PREFIX="$prevpre"
IPREFIX="$previpre" IPREFIX="$previpre"
fi fi
[[ -n "$tried" ]] && break [[ -n "$tried" && "$PREFIX" != [-+]* ]] && break
done done
if [[ -n "$opts" && -z "$aret$matched$mesg" && if [[ -n "$opts" && -z "$aret$matched$mesg" &&
nm -eq compstate[nmatches] ]]; then nm -eq compstate[nmatches] ]]; then
@ -391,11 +375,7 @@ if (( $# )) && comparguments "$multi[@]" "$autod" "$@"; then
[[ -n "$aret" ]] && return 300 [[ -n "$aret" ]] && return 300
if [[ -n "$noargs" ]]; then [[ -n "$noargs" && nm -eq "$compstate[nmatches]" ]] && _message "$noargs"
[[ -z "$ismulti" && nm -eq "$compstate[nmatches]" ]] && _message "$noargs"
else
has_args=yes
fi
# Set the return value. # Set the return value.

View file

@ -2756,6 +2756,45 @@ Also, during the evaluation of the var(action), the context name in
the tt(curcontext) parameter will be changed by appending the same the tt(curcontext) parameter will be changed by appending the same
string that is stored in the tt(context) parameter. string that is stored in the tt(context) parameter.
The aruments also allows to specify multiple sets of options and
arguments separated by single hyphens. The specifications before
the first hyphen are shared by all sets given after the first
hyphen. The first word in every other set gives the name of the
set. This name may appear in exclusion lists in the specifications,
either alone or before one of the possible values described above
(with a `tt(-)' between the name and the rest).
For example:
example(_argument_sets \
-a \
- set1 \
-c \
- set2 \
-d \
':arg:(x2 y2)')
This defines two sets. When the command line contains the option
`tt(-c)', the `tt(-d)' option and the argument will not be considered
possible completions. When it contains `tt(-d)' or an argument, the
option `tt(-c)' will not be completed any more, but if `tt(-a)' is
given, both sets will still be considered valid, because it appears
before the first hyphen, so both sets contain this option.
If the name-string is of the form `tt(LPAR())var(name)tt(RPAR())' then
all specifications in the set have an implicit exclusion list
containing the name of the set, i.e. all specifications are mutual
exclusive with all other specifications in the same set. This is
useful for defining multiple sets of options which are mutually
exclusive and in which the options are aliases for each other. E.g.:
example(_argument_sets \
-a -b \
- '(compress)' \
{-c,--compress}'[compress]' \
- '(uncompress)' \
{-d,--decompress}'[decompress]')
Normally the option names are taken as multi-character names and a Normally the option names are taken as multi-character names and a
word from the line is considered to contain only one option (or word from the line is considered to contain only one option (or
none). By giving the tt(-s) option to this function (before the first none). By giving the tt(-s) option to this function (before the first
@ -2834,6 +2873,11 @@ argument for an option is optional. If it fails to automatically
detect this, the colon before the var(message) can be doubled to tell detect this, the colon before the var(message) can be doubled to tell
it about this as described for the normal option descriptions above. it about this as described for the normal option descriptions above.
If the var(pattern) ends in `tt((-))', this will removed from the
pattern and the var(action) will be used only directly after the
`tt(=)', not in the next word. I.e., this is like a normal
specification as descrobed above using `tt(=-)'.
The option `tt(-i) var(patterns)' (which must be given after the The option `tt(-i) var(patterns)' (which must be given after the
`tt(-)tt(-)') can be used to give patterns for options which should not be `tt(-)tt(-)') can be used to give patterns for options which should not be
completed. The patterns can be given as the name of an array parameter completed. The patterns can be given as the name of an array parameter
@ -2875,55 +2919,6 @@ arguments. The first one describes the first argument as a
be completed. The last description says that all other arguments are be completed. The last description says that all other arguments are
`var(page numbers)' but does not give possible completions. `var(page numbers)' but does not give possible completions.
) )
findex(_argument_sets)
item(tt(_argument_sets) var(sets) ...)(
This is like tt(_arguments) but allows to specify multiple sets of
options and arguments. The arguments are sets of specifications for
tt(_arguments) separated by single hyphens. The specifications before
the first hyphen are shared by all sets given after the first
hyphen. The first word in every other set gives the name of the
set. This name may appear in exclusion lists in the specifications,
either alone or before one of the possible values described for
tt(_arguments) above (with a `tt(-)' between the name and the rest).
For example:
example(_argument_sets \
-a \
- set1 \
-c \
- set2 \
-d \
':arg:(x2 y2)')
This defines two sets. When the command line contains the option
`tt(-c)', the `tt(-d)' option and the argument will not be considered
possible completions. When it contains `tt(-d)' or an argument, the
option `tt(-c)' will not be completed any more, but if `tt(-a)' is
given, both sets will still be considered valid, because it appears
before the first hyphen, so both sets contain this option.
If the name-string is of the form `tt(LPAR())var(name)tt(RPAR())' then
all specifications in the set have an implicit exclusion list
containing the name of the set, i.e. all specifications are mutual
exclusive with all other specifications in the same set. This is
useful for defining multiple sets of options which are mutual
exclusive and in which the options are aliases for each other. E.g.:
example(_argument_sets \
-a -b \
- '(compress)' \
{-c,--compress}'[compress]' \
- '(uncompress)' \
{-d,--decompress}'[decompress]')
Don't expect too much with complicated options that get their
arguments in the same string and `tt(->)var(state)' actions or with
the tt(-C) option that is given to tt(_arguments), otherwise most
things should work. Note that the contexts reported in the tt(context)
array and the options in the tt(opt_args) association are prefixed
with the set names and a hyphen.
)
findex(_call) findex(_call)
item(tt(_call) var(tag) var(string) ...)( item(tt(_call) var(tag) var(string) ...)(
This function is used in places where a command is called and the user This function is used in places where a command is called and the user

View file

@ -293,6 +293,7 @@ typedef struct caarg *Caarg;
struct cadef { struct cadef {
Cadef next; /* next in cache */ Cadef next; /* next in cache */
Cadef snext; /* next set */
Caopt opts; /* the options */ Caopt opts; /* the options */
int nopts, ndopts, nodopts; /* number of options/direct/optional direct */ int nopts, ndopts, nodopts; /* number of options/direct/optional direct */
Caarg args; /* the normal arguments */ Caarg args; /* the normal arguments */
@ -304,7 +305,8 @@ struct cadef {
char *match; /* -M spec to use */ char *match; /* -M spec to use */
int argsactive; /* if arguments are still allowed */ int argsactive; /* if arguments are still allowed */
/* used while parsing a command line */ /* used while parsing a command line */
char *set; /* set name, shared */ char *set; /* set name prefix (<name>-), shared */
char *sname; /* set name */
int flags; /* see CDF_* below */ int flags; /* see CDF_* below */
}; };
@ -399,11 +401,14 @@ freecaargs(Caarg a)
static void static void
freecadef(Cadef d) freecadef(Cadef d)
{ {
if (d) { Cadef s;
Caopt p, n; Caopt p, n;
while (d) {
s = d->snext;
zsfree(d->match); zsfree(d->match);
zsfree(d->set); zsfree(d->set);
zsfree(d->sname);
if (d->defs) if (d->defs)
freearray(d->defs); freearray(d->defs);
@ -421,6 +426,7 @@ freecadef(Cadef d)
if (d->single) if (d->single)
zfree(d->single, 256 * sizeof(Caopt)); zfree(d->single, 256 * sizeof(Caopt));
zfree(d, sizeof(*d)); zfree(d, sizeof(*d));
d = s;
} }
} }
@ -510,25 +516,63 @@ parse_caarg(int mult, int type, int num, int opt, char *oname, char **def,
return ret; return ret;
} }
static Cadef
alloc_cadef(char **args, int single, char *match, int flags)
{
Cadef ret;
ret = (Cadef) zalloc(sizeof(*ret));
ret->next = ret->snext = NULL;
ret->opts = NULL;
ret->args = ret->rest = NULL;
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. */ /* Parse an array of definitions. */
static Cadef static Cadef
parse_cadef(char *nam, char **args, int multi) parse_cadef(char *nam, char **args)
{ {
Cadef ret; Cadef all, ret;
Caopt *optp; Caopt *optp;
Caarg argp; char **oargs = args, *p, *q, *match = "r:|[_-]=* r:|=*", **xor, **sargs;
char **oargs = args, *p, *q, *match = "r:|[_-]=* r:|=*", **xor; char *adpre, *adsuf, *axor = NULL, *doset = NULL, **setp = NULL;
char *adpre, *adsuf, *set = NULL, *doset = NULL;
int single = 0, anum = 1, xnum, nopts, ndopts, nodopts, flags = 0; int single = 0, anum = 1, xnum, nopts, ndopts, nodopts, flags = 0;
int state = 0;
nopts = ndopts = nodopts = 0; nopts = ndopts = nodopts = 0;
if (multi) {
if (!args[1])
return NULL;
set = tricat(*args++, "-", "");
}
/* First string is the auto-description definition. */ /* First string is the auto-description definition. */
for (p = args[0]; *p && (p[0] != '%' || p[1] != 'd'); p++); for (p = args[0]; *p && (p[0] != '%' || p[1] != 'd'); p++);
@ -582,27 +626,47 @@ parse_cadef(char *nam, char **args, int multi)
/* Looks good. Optimistically allocate the cadef structure. */ /* Looks good. Optimistically allocate the cadef structure. */
ret = (Cadef) zalloc(sizeof(*ret)); all = ret = alloc_cadef(oargs, single, match, flags);
ret->next = NULL; optp = &(ret->opts);
ret->opts = NULL; single = flags = 0;
ret->args = ret->rest = NULL; anum = 1;
ret->defs = zarrdup(oargs);
ret->ndefs = arrlen(oargs); sargs = args;
ret->lastt = time(0);
ret->set = set;
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;
/* Get the definitions. */ /* Get the definitions. */
for (optp = &(ret->opts); *args; args++) { for (; *args; args++) {
if (args[0][0] == '-' && !args[0][1]) { if (args[0][0] == '-' && !args[0][1] && args[1]) {
doset = set; 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, flags);
optp = &(ret->opts);
single = flags = nopts = ndopts = nodopts = 0;
anum = 1;
}
continue; continue;
} }
p = dupstring(*args); p = dupstring(*args);
@ -632,16 +696,25 @@ parse_cadef(char *nam, char **args, int multi)
} }
/* Oops, end-of-string. */ /* Oops, end-of-string. */
if (*p != ')') { if (*p != ')') {
freecadef(ret); freecadef(all);
zwarnnam(nam, "invalid argument: %s", *args, 0); zwarnnam(nam, "invalid argument: %s", *args, 0);
return NULL; return NULL;
} }
if (doset && axor)
xnum++;
xor = (char **) zalloc((xnum + 2) * sizeof(char *)); xor = (char **) zalloc((xnum + 2) * sizeof(char *));
for (node = firstnode(list), xp = xor; node; incnode(node), xp++) for (node = firstnode(list), xp = xor; node; incnode(node), xp++)
*xp = ztrdup((char *) getdata(node)); *xp = ztrdup((char *) getdata(node));
if (doset && axor)
*xp++ = ztrdup(axor);
xp[0] = xp[1] = NULL; xp[0] = xp[1] = NULL;
p++; p++;
} else if (doset && axor) {
xnum = 1;
xor = (char **) zalloc(3 * sizeof(char *));
xor[0] = ztrdup(axor);
xor[1] = xor[2] = NULL;
} else } else
xor = NULL; xor = NULL;
@ -676,7 +749,7 @@ parse_cadef(char *nam, char **args, int multi)
p++; p++;
} }
if (!p[1]) { if (!p[1]) {
freecadef(ret); freecadef(all);
zwarnnam(nam, "invalid argument: %s", *args, 0); zwarnnam(nam, "invalid argument: %s", *args, 0);
return NULL; return NULL;
} }
@ -713,7 +786,7 @@ parse_cadef(char *nam, char **args, int multi)
p++; p++;
if (!*p) { if (!*p) {
freecadef(ret); freecadef(all);
zwarnnam(nam, "invalid option definition: %s", *args, 0); zwarnnam(nam, "invalid option definition: %s", *args, 0);
return NULL; return NULL;
} }
@ -723,7 +796,7 @@ parse_cadef(char *nam, char **args, int multi)
descr = NULL; descr = NULL;
if (c && c != ':') { if (c && c != ':') {
freecadef(ret); freecadef(all);
zwarnnam(nam, "invalid option definition: %s", *args, 0); zwarnnam(nam, "invalid option definition: %s", *args, 0);
return NULL; return NULL;
} }
@ -766,7 +839,7 @@ parse_cadef(char *nam, char **args, int multi)
*p = sav; *p = sav;
} }
if (*p != ':') { if (*p != ':') {
freecadef(ret); freecadef(all);
freecaargs(oargs); freecaargs(oargs);
zwarnnam(nam, "invalid option definition: %s", zwarnnam(nam, "invalid option definition: %s",
*args, 0); *args, 0);
@ -848,12 +921,12 @@ parse_cadef(char *nam, char **args, int multi)
int type = CAA_REST; int type = CAA_REST;
if (*++p != ':') { if (*++p != ':') {
freecadef(ret); freecadef(all);
zwarnnam(nam, "invalid rest argument definition: %s", *args, 0); zwarnnam(nam, "invalid rest argument definition: %s", *args, 0);
return NULL; return NULL;
} }
if (ret->rest) { if (ret->rest) {
freecadef(ret); freecadef(all);
zwarnnam(nam, "doubled rest argument definition: %s", *args, 0); zwarnnam(nam, "doubled rest argument definition: %s", *args, 0);
return NULL; return NULL;
} }
@ -885,7 +958,7 @@ parse_cadef(char *nam, char **args, int multi)
anum++; anum++;
if (*p != ':') { if (*p != ':') {
freecadef(ret); freecadef(all);
zwarnnam(nam, "invalid argument: %s", *args, 0); zwarnnam(nam, "invalid argument: %s", *args, 0);
return NULL; return NULL;
} }
@ -905,7 +978,7 @@ parse_cadef(char *nam, char **args, int multi)
pre = tmp, tmp = tmp->next); pre = tmp, tmp = tmp->next);
if (tmp && tmp->num == anum - 1) { if (tmp && tmp->num == anum - 1) {
freecadef(ret); freecadef(all);
freecaargs(arg); freecaargs(arg);
zwarnnam(nam, "doubled argument definition: %s", *args, 0); zwarnnam(nam, "doubled argument definition: %s", *args, 0);
return NULL; return NULL;
@ -920,21 +993,16 @@ parse_cadef(char *nam, char **args, int multi)
ret->nopts = nopts; ret->nopts = nopts;
ret->ndopts = ndopts; ret->ndopts = ndopts;
ret->nodopts = nodopts; ret->nodopts = nodopts;
set_cadef_opts(ret);
for (argp = ret->args, xnum = 0; argp; argp = argp->next) { return all;
if (!argp->direct)
argp->min = argp->num - xnum;
if (argp->type == CAA_OPT)
xnum++;
}
return ret;
} }
/* Given an array of definitions, return the cadef for it. From the cache /* Given an array of definitions, return the cadef for it. From the cache
* are newly built. */ * are newly built. */
static Cadef static Cadef
get_cadef(char *nam, char **args, int multi) get_cadef(char *nam, char **args)
{ {
Cadef *p, *min, new; Cadef *p, *min, new;
int i, na = arrlen(args); int i, na = arrlen(args);
@ -948,7 +1016,7 @@ get_cadef(char *nam, char **args, int multi)
min = p; min = p;
if (i) if (i)
min = p; min = p;
if ((new = parse_cadef(nam, args, multi))) { if ((new = parse_cadef(nam, args))) {
freecadef(*min); freecadef(*min);
*min = new; *min = new;
} }
@ -1120,7 +1188,10 @@ ca_inactive(Cadef d, char **xor, int cur, int opts)
/* State when parsing a command line. */ /* State when parsing a command line. */
typedef struct castate *Castate;
struct castate { struct castate {
Castate snext;
Cadef d; Cadef d;
int nopts; int nopts;
Caarg def, ddef; Caarg def, ddef;
@ -1134,10 +1205,23 @@ struct castate {
static struct castate ca_laststate; static struct castate ca_laststate;
static int ca_parsed = 0, ca_alloced = 0; 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. */ /* Parse a command line. */
static int static int
ca_parse_line(Cadef d, int multi) ca_parse_line(Cadef d, int multi, int first)
{ {
Caarg adef, ddef; Caarg adef, ddef;
Caopt ptr, wasopt, dopt; Caopt ptr, wasopt, dopt;
@ -1148,16 +1232,17 @@ ca_parse_line(Cadef d, int multi)
/* Free old state. */ /* Free old state. */
if (ca_alloced) { if (first && ca_alloced) {
int i = ca_laststate.nopts; Castate s = &ca_laststate, ss;
LinkList *p = ca_laststate.oargs; int f = 1;
freelinklist(ca_laststate.args, freestr); while (s) {
while (i--) ss = s->snext;
if (*p++) freecastate(s);
freelinklist(p[-1], freestr); if (!f)
zfree(s, sizeof(*s));
zfree(ca_laststate.oargs, ca_laststate.d->nopts * sizeof(LinkList)); s = ss;
}
} }
/* Mark everything as active. */ /* Mark everything as active. */
@ -1171,6 +1256,7 @@ ca_parse_line(Cadef d, int multi)
/* Default values for the state. */ /* Default values for the state. */
state.snext = NULL;
state.d = d; state.d = d;
state.nopts = d->nopts; state.nopts = d->nopts;
state.def = state.ddef = NULL; state.def = state.ddef = NULL;
@ -1482,16 +1568,13 @@ ca_colonlist(LinkList l)
} }
static void static void
ca_set_data(char *opt, Caarg arg, char **args, int single) ca_set_data(LinkList descr, LinkList act, LinkList subc,
char *opt, Caarg arg, int single)
{ {
LinkList descr, act, subc; LinkNode dnode, anode;
char nbuf[40], *buf; char nbuf[40], *buf;
int restr = 0, onum, miss = 0, rest, oopt = 1, lopt = 0, addopt; int restr = 0, onum, miss = 0, rest, oopt = 1, lopt = 0, addopt;
descr = newlinklist();
act = newlinklist();
subc = newlinklist();
rec: rec:
addopt = (opt ? 0 : ca_laststate.oopt); addopt = (opt ? 0 : ca_laststate.oopt);
@ -1503,33 +1586,40 @@ ca_set_data(char *opt, Caarg arg, char **args, int single)
if (!opt && !lopt && oopt > 0) if (!opt && !lopt && oopt > 0)
oopt = 0; oopt = 0;
addlinknode(descr, arg->descr); for (dnode = firstnode(descr), anode = firstnode(act);
addlinknode(act, arg->action); dnode; incnode(dnode), incnode(anode))
if (!strcmp((char *) getdata(dnode), arg->descr) &&
!strcmp((char *) getdata(anode), arg->action))
break;
if (!restr) { if (!dnode) {
if ((restr = (arg->type == CAA_RARGS))) addlinknode(descr, arg->descr);
restrict_range(ca_laststate.optbeg, ca_laststate.argend); addlinknode(act, arg->action);
else if ((restr = (arg->type == CAA_RREST)))
restrict_range(ca_laststate.argbeg, ca_laststate.argend); 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 (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) if (single)
break; break;
@ -1565,15 +1655,13 @@ ca_set_data(char *opt, Caarg arg, char **args, int single)
goto rec; goto rec;
} }
set_list_array(args[0], descr);
set_list_array(args[1], act);
set_list_array(args[2], subc);
} }
static int static int
bin_comparguments(char *nam, char **args, char *ops, int func) bin_comparguments(char *nam, char **args, char *ops, int func)
{ {
int min, max, n; int min, max, n;
Castate lstate = &ca_laststate;
if (incompfunc != 1) { if (incompfunc != 1) {
zwarnnam(nam, "can only be called from completion function", NULL, 0); zwarnnam(nam, "can only be called from completion function", NULL, 0);
@ -1588,8 +1676,7 @@ bin_comparguments(char *nam, char **args, char *ops, int func)
return 1; return 1;
} }
switch (args[0][1]) { switch (args[0][1]) {
case 'i': case 'i': min = 2; max = -1; break;
case 'I': min = 2; max = -1; break;
case 'D': min = 3; max = 3; break; case 'D': min = 3; max = 3; break;
case 'O': min = 4; max = 4; break; case 'O': min = 4; max = 4; break;
case 'L': min = 3; max = 4; break; case 'L': min = 3; max = 4; break;
@ -1611,171 +1698,235 @@ bin_comparguments(char *nam, char **args, char *ops, int func)
} }
switch (args[0][1]) { switch (args[0][1]) {
case 'i': case 'i':
case 'I':
if (compcurrent > 1 && compwords[0]) { if (compcurrent > 1 && compwords[0]) {
Cadef def; Cadef def;
int cap = ca_parsed; int cap = ca_parsed, multi, first = 1, use, ret = 0;
LinkList cax = ca_xor; LinkList cax = ca_xor, nx;
LinkNode node;
Castate states = NULL, sp;
char *xor[2];
ca_parsed = 0; ca_parsed = 0;
xor[1] = NULL;
if (args[0][1] == 'I') { if (!(def = get_cadef(nam, args + 1)))
char **xor; return 1;
if (!(def = get_cadef(nam, args + 2, 1))) multi = !!def->snext;
return 1; ca_parsed = cap;
ca_xor = (multi ? newlinklist() : NULL);
ca_parsed = cap; while (def) {
ca_xor = newlinklist(); use = !ca_parse_line(def, multi, first);
if ((xor = getaparam(args[1]))) { nx = ca_xor;
if (arrcontains(xor, args[2], 0) || ca_xor = NULL;
ca_inactive(def, xor, compcurrent, 0)) { while ((def = def->snext)) {
ca_xor = cax; if (nx) {
return 1; 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;
} }
} }
if (ca_parse_line(def, 1)) { ca_xor = nx;
ca_xor = cax; if (use && def) {
return 1; 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;
} }
set_list_array(args[1], ca_xor); first = 0;
} else {
if (!(def = get_cadef(nam, args + 1, 0)))
return 1;
ca_parsed = cap;
ca_xor = NULL;
ca_parse_line(def, 0);
} }
ca_xor = cax; ca_xor = cax;
ca_parsed = 1; ca_parsed = 1;
ca_laststate.snext = states;
return 0; return ret;
} }
return 1; return 1;
case 'D': case 'D':
{ {
Caarg arg = ca_laststate.def; LinkList descr, act, subc;
Caarg arg;
int ign = 0, ret = 1;
if (arg) { descr = newlinklist();
if (ca_laststate.doff > 0) act = newlinklist();
ignore_prefix(ca_laststate.doff); subc = newlinklist();
ca_set_data(arg->opt, arg, args + 1, (ca_laststate.doff > 0)); while (lstate) {
arg = lstate->def;
return 0; if (arg) {
ret = 0;
if (!ign && lstate->doff > 0) {
ign = 1;
ignore_prefix(lstate->doff);
}
ca_set_data(descr, act, subc, arg->opt, arg,
(lstate->doff > 0));
}
lstate = lstate->snext;
} }
return 1; if (!ret) {
set_list_array(args[1], descr);
set_list_array(args[2], act);
set_list_array(args[3], subc);
}
return ret;
} }
case 'O': case 'O':
if (ca_laststate.actopts && {
(ca_laststate.opt || (ca_laststate.doff && ca_laststate.def) ||
(ca_laststate.def &&
(ca_laststate.def->type == CAA_OPT ||
(ca_laststate.def->type >= CAA_RARGS &&
ca_laststate.def->num < 0)))) &&
(!ca_laststate.def || ca_laststate.def->type < CAA_RARGS ||
(ca_laststate.def->type == CAA_RARGS ?
(ca_laststate.curpos == ca_laststate.argbeg + 1) :
(compcurrent == 1)))) {
LinkList next = newlinklist(); LinkList next = newlinklist();
LinkList direct = newlinklist(); LinkList direct = newlinklist();
LinkList odirect = newlinklist(); LinkList odirect = newlinklist();
LinkList equal = newlinklist(), l; LinkList equal = newlinklist(), l;
Caopt p; Caopt p;
char *str; char *str;
int ret = 1;
for (p = ca_laststate.d->opts; p; p = p->next) { for (; lstate; lstate = lstate->snext) {
if (p->active) { if (lstate->actopts &&
switch (p->type) { (lstate->opt || (lstate->doff && lstate->def) ||
case CAO_NEXT: l = next; break; (lstate->def &&
case CAO_DIRECT: l = direct; break; (lstate->def->type == CAA_OPT ||
case CAO_ODIRECT: l = odirect; break; (lstate->def->type >= CAA_RARGS &&
default: l = equal; break; 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) {
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 (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);
} }
} }
set_list_array(args[1], next); if (!ret) {
set_list_array(args[2], direct); set_list_array(args[1], next);
set_list_array(args[3], odirect); set_list_array(args[2], direct);
set_list_array(args[4], equal); set_list_array(args[3], odirect);
set_list_array(args[4], equal);
return 0;
}
return (ca_laststate.singles ? 2 : 1);
case 'L':
{
Caopt opt = ca_get_opt(ca_laststate.d, args[1], 1, NULL);
if (opt && opt->args) {
ca_set_data(opt->name, opt->args, args + 2, 1);
return 0; return 0;
} }
return 1; 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': case 's':
if (ca_laststate.d->single && ca_laststate.singles && for (; lstate; lstate = lstate->snext)
ca_laststate.actopts && ca_laststate.opt) { if (lstate->d->single && lstate->singles &&
setsparam(args[1], lstate->actopts && lstate->opt) {
ztrdup((ca_laststate.ddef && ca_laststate.dopt) ? setsparam(args[1],
(ca_laststate.dopt->type == CAO_DIRECT ? ztrdup((lstate->ddef && lstate->dopt) ?
"direct" : (lstate->dopt->type == CAO_DIRECT ?
((ca_laststate.dopt->type == CAO_OEQUAL || "direct" :
ca_laststate.dopt->type == CAO_EQUAL) ? ((lstate->dopt->type == CAO_OEQUAL ||
"equal" : "next")) : "")); lstate->dopt->type == CAO_EQUAL) ?
return 0; "equal" : "next")) : ""));
} return 0;
}
return 1; return 1;
case 'M': case 'M':
setsparam(args[1], ztrdup(ca_laststate.d->match)); setsparam(args[1], ztrdup(ca_laststate.d->match));
return 0; return 0;
case 'a': case 'a':
return !(ca_laststate.d->args || ca_laststate.d->rest); for (; lstate; lstate = lstate->snext)
if (lstate->d->args || lstate->d->rest)
return 0;
return 1;
case 'W': case 'W':
{ {
Castate s;
char **ret, **p; char **ret, **p;
LinkNode n; LinkNode n;
LinkList *a; LinkList *a;
Caopt o; Caopt o;
int num; int num;
ret = p = zalloc((countlinknodes(ca_laststate.args) + 1) * for (num = 0, s = lstate; s; s = s->snext)
sizeof(char *)); num += countlinknodes(s->args);
for (n = firstnode(ca_laststate.args); n; incnode(n)) ret = p = zalloc((num + 1) * sizeof(char *));
*p++ = ztrdup((char *) getdata(n));
for (s = lstate; s; s = s->snext)
for (n = firstnode(s->args); n; incnode(n))
*p++ = ztrdup((char *) getdata(n));
*p = NULL; *p = NULL;
setaparam(args[1], ret); setaparam(args[1], ret);
for (num = 0, o = ca_laststate.d->opts, a = ca_laststate.oargs; o; for (num = 0, s = lstate; s; s = s->snext)
o = o->next, a++) for (o = s->d->opts, a = s->oargs; o; o = o->next, a++)
if (*a) if (*a)
num += 2; num += 2;
ret = p = zalloc((num + 1) * sizeof(char *)); ret = p = zalloc((num + 1) * sizeof(char *));
for (o = ca_laststate.d->opts, a = ca_laststate.oargs; o; for (s = lstate; s; s = s->snext)
o = o->next, a++) { for (o = s->d->opts, a = s->oargs; o; o = o->next, a++)
if (*a) { if (*a) {
*p++ = (o->set ? tricat(o->set, o->name, "") : *p++ = (o->set ? tricat(o->set, o->name, "") :
ztrdup(o->name)); ztrdup(o->name));
*p++ = ca_colonlist(*a); *p++ = ca_colonlist(*a);
} }
}
*p = NULL; *p = NULL;
sethparam(args[2], ret); sethparam(args[2], ret);