mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-10-26 04:30:27 +01:00
41008: Handle expansions for precommand modifiers
This commit is contained in:
parent
d78b02218e
commit
408b92b168
5 changed files with 177 additions and 35 deletions
|
|
@ -1,3 +1,9 @@
|
||||||
|
2017-04-26 Peter Stephenson <p.stephenson@samsung.com>
|
||||||
|
|
||||||
|
* 41008: Src/exec.c, Src/linklist.c, Test/A01grammar.ztst,
|
||||||
|
Test/E01options.ztst: Handle expansions when analysing
|
||||||
|
precommand modifiers.
|
||||||
|
|
||||||
2017-04-26 Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
|
2017-04-26 Jun-ichi Takimoto <takimoto-j@kba.biglobe.ne.jp>
|
||||||
|
|
||||||
* 41006: Completion/Unix/Command/_ls: add new options for BSDs,
|
* 41006: Completion/Unix/Command/_ls: add new options for BSDs,
|
||||||
|
|
|
||||||
141
Src/exec.c
141
Src/exec.c
|
|
@ -2641,6 +2641,27 @@ execcmd_analyse(Estate state, Execcmd_params eparams)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Transfer the first node of args to preargs, performing
|
||||||
|
* prefork expansion on the way if necessary.
|
||||||
|
*/
|
||||||
|
static void execcmd_getargs(LinkList preargs, LinkList args, int expand)
|
||||||
|
{
|
||||||
|
if (!firstnode(args)) {
|
||||||
|
return;
|
||||||
|
} else if (expand) {
|
||||||
|
local_list0(svl);
|
||||||
|
init_list0(svl);
|
||||||
|
/* not init_list1, as we need real nodes */
|
||||||
|
addlinknode(&svl, uremnode(args, firstnode(args)));
|
||||||
|
/* Analysing commands, so vanilla options to prefork */
|
||||||
|
prefork(&svl, 0, NULL);
|
||||||
|
joinlists(preargs, &svl);
|
||||||
|
} else {
|
||||||
|
addlinknode(preargs, uremnode(args, firstnode(args)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Execute a command at the lowest level of the hierarchy.
|
* Execute a command at the lowest level of the hierarchy.
|
||||||
*/
|
*/
|
||||||
|
|
@ -2671,6 +2692,11 @@ execcmd_exec(Estate state, Execcmd_params eparams,
|
||||||
LinkList redir = eparams->redir;
|
LinkList redir = eparams->redir;
|
||||||
Wordcode varspc = eparams->varspc;
|
Wordcode varspc = eparams->varspc;
|
||||||
int type = eparams->type;
|
int type = eparams->type;
|
||||||
|
/*
|
||||||
|
* preargs comes from expanding the head of the args list
|
||||||
|
* in order to check for prefix commands.
|
||||||
|
*/
|
||||||
|
LinkList preargs;
|
||||||
|
|
||||||
doneps4 = 0;
|
doneps4 = 0;
|
||||||
|
|
||||||
|
|
@ -2725,9 +2751,19 @@ execcmd_exec(Estate state, Execcmd_params eparams,
|
||||||
* command if it contains some tokens (e.g. x=ex; ${x}port), so this *
|
* command if it contains some tokens (e.g. x=ex; ${x}port), so this *
|
||||||
* only works in simple cases. has_token() is called to make sure *
|
* only works in simple cases. has_token() is called to make sure *
|
||||||
* this really is a simple case. */
|
* this really is a simple case. */
|
||||||
if (type == WC_SIMPLE || type == WC_TYPESET) {
|
if ((type == WC_SIMPLE || type == WC_TYPESET) && args) {
|
||||||
while (args && nonempty(args)) {
|
/*
|
||||||
char *cmdarg = (char *) peekfirst(args);
|
* preargs contains args that have been expanded by prefork.
|
||||||
|
* Running execcmd_getargs() causes the any argument available
|
||||||
|
* in args to be exanded where necessary and transferred to
|
||||||
|
* preargs. We call execcmd_getargs() every time we need to
|
||||||
|
* analyse an argument not available in preargs, though there is
|
||||||
|
* no guarantee a further argument will be available.
|
||||||
|
*/
|
||||||
|
preargs = newlinklist();
|
||||||
|
execcmd_getargs(preargs, args, eparams->htok);
|
||||||
|
while (nonempty(preargs)) {
|
||||||
|
char *cmdarg = (char *) peekfirst(preargs);
|
||||||
checked = !has_token(cmdarg);
|
checked = !has_token(cmdarg);
|
||||||
if (!checked)
|
if (!checked)
|
||||||
break;
|
break;
|
||||||
|
|
@ -2766,7 +2802,19 @@ execcmd_exec(Estate state, Execcmd_params eparams,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
checked = 0;
|
checked = 0;
|
||||||
if ((cflags & BINF_COMMAND) && nextnode(firstnode(args))) {
|
/*
|
||||||
|
* We usually don't need the argument containing the
|
||||||
|
* precommand modifier itself. Exception: when "command"
|
||||||
|
* will implemented by a call to "whence", in which case
|
||||||
|
* we'll simply re-insert the argument.
|
||||||
|
*/
|
||||||
|
uremnode(preargs, firstnode(preargs));
|
||||||
|
if (!firstnode(preargs)) {
|
||||||
|
execcmd_getargs(preargs, args, eparams->htok);
|
||||||
|
if (!firstnode(preargs))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((cflags & BINF_COMMAND)) {
|
||||||
/*
|
/*
|
||||||
* Check for options to "command".
|
* Check for options to "command".
|
||||||
* If just -p, this is handled here: use the default
|
* If just -p, this is handled here: use the default
|
||||||
|
|
@ -2776,10 +2824,11 @@ execcmd_exec(Estate state, Execcmd_params eparams,
|
||||||
* Otherwise, just leave marked as BINF_COMMAND
|
* Otherwise, just leave marked as BINF_COMMAND
|
||||||
* modifier with no additional action.
|
* modifier with no additional action.
|
||||||
*/
|
*/
|
||||||
LinkNode argnode = nextnode(firstnode(args));
|
LinkNode argnode, oldnode;
|
||||||
char *argdata = (char *) getdata(argnode);
|
char *argdata, *cmdopt;
|
||||||
char *cmdopt;
|
|
||||||
int has_p = 0, has_vV = 0, has_other = 0;
|
int has_p = 0, has_vV = 0, has_other = 0;
|
||||||
|
argnode = firstnode(preargs);
|
||||||
|
argdata = (char *) getdata(argnode);
|
||||||
while (IS_DASH(*argdata)) {
|
while (IS_DASH(*argdata)) {
|
||||||
/* Just to be definite, stop on single "-", too, */
|
/* Just to be definite, stop on single "-", too, */
|
||||||
if (!argdata[1] ||
|
if (!argdata[1] ||
|
||||||
|
|
@ -2812,25 +2861,30 @@ execcmd_exec(Estate state, Execcmd_params eparams,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oldnode = argnode;
|
||||||
argnode = nextnode(argnode);
|
argnode = nextnode(argnode);
|
||||||
if (!argnode)
|
if (!argnode) {
|
||||||
break;
|
execcmd_getargs(preargs, args, eparams->htok);
|
||||||
|
if (!(argnode = nextnode(oldnode)))
|
||||||
|
break;
|
||||||
|
}
|
||||||
argdata = (char *) getdata(argnode);
|
argdata = (char *) getdata(argnode);
|
||||||
}
|
}
|
||||||
if (has_vV) {
|
if (has_vV) {
|
||||||
/* Leave everything alone, dispatch to whence */
|
/*
|
||||||
|
* Leave everything alone, dispatch to whence.
|
||||||
|
* We need to put the name back in the list.
|
||||||
|
*/
|
||||||
|
pushnode(preargs, "command");
|
||||||
hn = &commandbn.node;
|
hn = &commandbn.node;
|
||||||
is_builtin = 1;
|
is_builtin = 1;
|
||||||
break;
|
break;
|
||||||
} else if (has_p) {
|
} else if (has_p) {
|
||||||
/* Use default path; absorb command and option. */
|
/* Use default path */
|
||||||
uremnode(args, firstnode(args));
|
|
||||||
use_defpath = 1;
|
use_defpath = 1;
|
||||||
if ((argnode = nextnode(firstnode(args))))
|
|
||||||
argdata = (char *) getdata(argnode);
|
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Else just absorb command and any trailing
|
* Else just any trailing
|
||||||
* end-of-options marker. This can only occur
|
* end-of-options marker. This can only occur
|
||||||
* if we just had -p or something including more
|
* if we just had -p or something including more
|
||||||
* than just -p, -v and -V, in which case we behave
|
* than just -p, -v and -V, in which case we behave
|
||||||
|
|
@ -2838,16 +2892,16 @@ execcmd_exec(Estate state, Execcmd_params eparams,
|
||||||
* isn't a good place for standard option handling.
|
* isn't a good place for standard option handling.
|
||||||
*/
|
*/
|
||||||
if (IS_DASH(argdata[0]) && IS_DASH(argdata[1]) && !argdata[2])
|
if (IS_DASH(argdata[0]) && IS_DASH(argdata[1]) && !argdata[2])
|
||||||
uremnode(args, firstnode(args));
|
uremnode(preargs, argnode);
|
||||||
}
|
} else if (cflags & BINF_EXEC) {
|
||||||
if ((cflags & BINF_EXEC) && nextnode(firstnode(args))) {
|
|
||||||
/*
|
/*
|
||||||
* Check for compatibility options to exec builtin.
|
* Check for compatibility options to exec builtin.
|
||||||
* It would be nice to do these more generically,
|
* It would be nice to do these more generically,
|
||||||
* but currently we don't have a mechanism for
|
* but currently we don't have a mechanism for
|
||||||
* precommand modifiers.
|
* precommand modifiers.
|
||||||
*/
|
*/
|
||||||
char *next = (char *) getdata(nextnode(firstnode(args)));
|
LinkNode argnode = firstnode(preargs), oldnode;
|
||||||
|
char *argdata = (char *) getdata(argnode);
|
||||||
char *cmdopt, *exec_argv0 = NULL;
|
char *cmdopt, *exec_argv0 = NULL;
|
||||||
/*
|
/*
|
||||||
* Careful here: we want to make sure a final dash
|
* Careful here: we want to make sure a final dash
|
||||||
|
|
@ -2857,17 +2911,23 @@ execcmd_exec(Estate state, Execcmd_params eparams,
|
||||||
* people aren't likely to mix the option style
|
* people aren't likely to mix the option style
|
||||||
* with the zsh style.
|
* with the zsh style.
|
||||||
*/
|
*/
|
||||||
while (next && IS_DASH(*next) && strlen(next) >= 2) {
|
while (argdata && IS_DASH(*argdata) && strlen(argdata) >= 2) {
|
||||||
if (!firstnode(args)) {
|
oldnode = argnode;
|
||||||
|
argnode = nextnode(oldnode);
|
||||||
|
if (!argnode) {
|
||||||
|
execcmd_getargs(preargs, args, eparams->htok);
|
||||||
|
argnode = nextnode(oldnode);
|
||||||
|
}
|
||||||
|
if (!argnode) {
|
||||||
zerr("exec requires a command to execute");
|
zerr("exec requires a command to execute");
|
||||||
lastval = 1;
|
lastval = 1;
|
||||||
errflag |= ERRFLAG_ERROR;
|
errflag |= ERRFLAG_ERROR;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
uremnode(args, firstnode(args));
|
uremnode(preargs, oldnode);
|
||||||
if (IS_DASH(next[0]) && IS_DASH(next[1]) && !next[2])
|
if (IS_DASH(argdata[0]) && IS_DASH(argdata[1]) && !argdata[2])
|
||||||
break;
|
break;
|
||||||
for (cmdopt = &next[1]; *cmdopt; ++cmdopt) {
|
for (cmdopt = &argdata[1]; *cmdopt; ++cmdopt) {
|
||||||
switch (*cmdopt) {
|
switch (*cmdopt) {
|
||||||
case 'a':
|
case 'a':
|
||||||
/* argument is ARGV0 string */
|
/* argument is ARGV0 string */
|
||||||
|
|
@ -2876,21 +2936,25 @@ execcmd_exec(Estate state, Execcmd_params eparams,
|
||||||
/* position on last non-NULL character */
|
/* position on last non-NULL character */
|
||||||
cmdopt += strlen(cmdopt+1);
|
cmdopt += strlen(cmdopt+1);
|
||||||
} else {
|
} else {
|
||||||
if (!firstnode(args)) {
|
if (!argnode) {
|
||||||
zerr("exec requires a command to execute");
|
zerr("exec requires a command to execute");
|
||||||
lastval = 1;
|
lastval = 1;
|
||||||
errflag |= ERRFLAG_ERROR;
|
errflag |= ERRFLAG_ERROR;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (!nextnode(firstnode(args))) {
|
if (!nextnode(argnode))
|
||||||
|
execcmd_getargs(preargs, args,
|
||||||
|
eparams->htok);
|
||||||
|
if (!nextnode(argnode)) {
|
||||||
zerr("exec flag -a requires a parameter");
|
zerr("exec flag -a requires a parameter");
|
||||||
lastval = 1;
|
lastval = 1;
|
||||||
errflag |= ERRFLAG_ERROR;
|
errflag |= ERRFLAG_ERROR;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
exec_argv0 = (char *)
|
exec_argv0 = (char *) getdata(argnode);
|
||||||
getdata(nextnode(firstnode(args)));
|
oldnode = argnode;
|
||||||
uremnode(args, firstnode(args));
|
argnode = nextnode(argnode);
|
||||||
|
uremnode(args, oldnode);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'c':
|
case 'c':
|
||||||
|
|
@ -2906,8 +2970,9 @@ execcmd_exec(Estate state, Execcmd_params eparams,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (firstnode(args) && nextnode(firstnode(args)))
|
if (!argnode)
|
||||||
next = (char *) getdata(nextnode(firstnode(args)));
|
break;
|
||||||
|
argdata = (char *) getdata(argnode);
|
||||||
}
|
}
|
||||||
if (exec_argv0) {
|
if (exec_argv0) {
|
||||||
char *str, *s;
|
char *str, *s;
|
||||||
|
|
@ -2919,12 +2984,14 @@ execcmd_exec(Estate state, Execcmd_params eparams,
|
||||||
zputenv(str);
|
zputenv(str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uremnode(args, firstnode(args));
|
|
||||||
hn = NULL;
|
hn = NULL;
|
||||||
if ((cflags & BINF_COMMAND) && unset(POSIXBUILTINS))
|
if ((cflags & BINF_COMMAND) && unset(POSIXBUILTINS))
|
||||||
break;
|
break;
|
||||||
|
if (!nonempty(preargs))
|
||||||
|
execcmd_getargs(preargs, args, eparams->htok);
|
||||||
}
|
}
|
||||||
}
|
} else
|
||||||
|
preargs = NULL;
|
||||||
|
|
||||||
/* if we get this far, it is OK to pay attention to lastval again */
|
/* if we get this far, it is OK to pay attention to lastval again */
|
||||||
if (noerrexit == 2 && !is_shfunc)
|
if (noerrexit == 2 && !is_shfunc)
|
||||||
|
|
@ -2946,8 +3013,12 @@ execcmd_exec(Estate state, Execcmd_params eparams,
|
||||||
esprefork = (magic_assign ||
|
esprefork = (magic_assign ||
|
||||||
(isset(MAGICEQUALSUBST) && type != WC_TYPESET)) ?
|
(isset(MAGICEQUALSUBST) && type != WC_TYPESET)) ?
|
||||||
PREFORK_TYPESET : 0;
|
PREFORK_TYPESET : 0;
|
||||||
if (args && eparams->htok)
|
if (args) {
|
||||||
prefork(args, esprefork, NULL);
|
if (eparams->htok)
|
||||||
|
prefork(args, esprefork, NULL);
|
||||||
|
if (preargs)
|
||||||
|
args = joinlists(preargs, args);
|
||||||
|
}
|
||||||
|
|
||||||
if (type == WC_SIMPLE || type == WC_TYPESET) {
|
if (type == WC_SIMPLE || type == WC_TYPESET) {
|
||||||
int unglobbed = 0;
|
int unglobbed = 0;
|
||||||
|
|
|
||||||
|
|
@ -347,6 +347,35 @@ newsizedlist(int size)
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Join two linked lists. Neither may be null, though either
|
||||||
|
* may be empty.
|
||||||
|
*
|
||||||
|
* It is assumed the pieces come from the heap, but if not it is
|
||||||
|
* safe to free LinkList second.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**/
|
||||||
|
mod_export LinkList
|
||||||
|
joinlists(LinkList first, LinkList second)
|
||||||
|
{
|
||||||
|
LinkNode moveme = firstnode(second);
|
||||||
|
if (moveme) {
|
||||||
|
if (firstnode(first)) {
|
||||||
|
LinkNode anchor = lastnode(first);
|
||||||
|
anchor->next = moveme;
|
||||||
|
moveme->prev = anchor;
|
||||||
|
} else {
|
||||||
|
first->list.first = moveme;
|
||||||
|
moveme->prev = &first->node;
|
||||||
|
}
|
||||||
|
first->list.last = second->list.last;
|
||||||
|
|
||||||
|
second->list.first = second->list.last = NULL;
|
||||||
|
}
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return the node whose data is the pointer "dat", else NULL.
|
* Return the node whose data is the pointer "dat", else NULL.
|
||||||
* Can be used as a boolean test.
|
* Can be used as a boolean test.
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,13 @@
|
||||||
0:`exec' with -a option, no space
|
0:`exec' with -a option, no space
|
||||||
>/bin/SPLOOSH
|
>/bin/SPLOOSH
|
||||||
|
|
||||||
|
(
|
||||||
|
opts=(-a /bin/WHOOOSH)
|
||||||
|
exec $opts /bin/sh -c 'echo $0'
|
||||||
|
)
|
||||||
|
0:`exec' with -a option from expansion
|
||||||
|
>/bin/WHOOOSH
|
||||||
|
|
||||||
(export FOO=bar; exec -c /bin/sh -c 'echo x${FOO}x')
|
(export FOO=bar; exec -c /bin/sh -c 'echo x${FOO}x')
|
||||||
0:`exec' with -c option
|
0:`exec' with -c option
|
||||||
>xx
|
>xx
|
||||||
|
|
@ -121,6 +128,21 @@
|
||||||
*>*/cat
|
*>*/cat
|
||||||
>echo
|
>echo
|
||||||
>cat is /*/cat
|
>cat is /*/cat
|
||||||
|
>echo is a shell builtin
|
||||||
|
|
||||||
|
args=(
|
||||||
|
'command -pv cat'
|
||||||
|
'command -pv echo'
|
||||||
|
'command -p -V cat'
|
||||||
|
'command -p -V -- echo'
|
||||||
|
)
|
||||||
|
for arg in $args; do
|
||||||
|
${=arg}
|
||||||
|
done
|
||||||
|
0:command -p in combination, using expansion
|
||||||
|
*>*/cat
|
||||||
|
>echo
|
||||||
|
>cat is /*/cat
|
||||||
>echo is a shell builtin
|
>echo is a shell builtin
|
||||||
|
|
||||||
cd() { echo Not cd at all; }
|
cd() { echo Not cd at all; }
|
||||||
|
|
|
||||||
|
|
@ -804,6 +804,20 @@
|
||||||
>print is a shell builtin
|
>print is a shell builtin
|
||||||
?(eval):8: command not found: print
|
?(eval):8: command not found: print
|
||||||
|
|
||||||
|
(
|
||||||
|
setopt posixbuiltins
|
||||||
|
opts=()
|
||||||
|
command $opts print foo
|
||||||
|
opts=(-v)
|
||||||
|
command $opts print
|
||||||
|
opts=(-V)
|
||||||
|
command $opts print
|
||||||
|
)
|
||||||
|
0:command with options from expansion
|
||||||
|
>foo
|
||||||
|
>print
|
||||||
|
>print is a shell builtin
|
||||||
|
|
||||||
# With non-special command: original value restored
|
# With non-special command: original value restored
|
||||||
# With special builtin: new value kept
|
# With special builtin: new value kept
|
||||||
# With special builtin preceeded by "command": original value restored.
|
# With special builtin preceeded by "command": original value restored.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue