ash: eval: Add assignment built-in support again
Upstream commit: Date: Sat, 19 May 2018 02:39:52 +0800 eval: Add assignment built-in support again This patch adds assignment built-in support that used to exist in dash prior to 0.3.8-15. This is because it will soon be part of POSIX, and the semantics are now much better defined. Recognition is done at execution time, so even "command -- export" or "var=export; command $var" should work. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
c91950f315
commit
7eb8eecbbc
172
shell/ash.c
172
shell/ash.c
@ -8112,7 +8112,7 @@ struct cmdentry {
|
||||
#define DO_ABS 0x02 /* checks absolute paths */
|
||||
#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
|
||||
#define DO_ALTPATH 0x08 /* using alternate path */
|
||||
#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
|
||||
#define DO_REGBLTIN 0x10 /* regular built-ins and functions only */
|
||||
|
||||
static void find_command(char *, struct cmdentry *, int, const char *);
|
||||
|
||||
@ -8718,24 +8718,43 @@ typecmd(int argc UNUSED_PARAM, char **argv)
|
||||
}
|
||||
|
||||
#if ENABLE_ASH_CMDCMD
|
||||
/* Is it "command [-p] PROG ARGS" bltin, no other opts? Return ptr to "PROG" if yes */
|
||||
static char **
|
||||
parse_command_args(char **argv, const char **path)
|
||||
static struct strlist *
|
||||
fill_arglist(struct arglist *arglist, union node **argpp)
|
||||
{
|
||||
struct strlist **lastp = arglist->lastp;
|
||||
union node *argp;
|
||||
|
||||
while ((argp = *argpp) != NULL) {
|
||||
expandarg(argp, arglist, EXP_FULL | EXP_TILDE);
|
||||
*argpp = argp->narg.next;
|
||||
if (*lastp)
|
||||
break;
|
||||
}
|
||||
|
||||
return *lastp;
|
||||
}
|
||||
|
||||
/* Is it "command [-p] PROG ARGS" bltin, no other opts? Return ptr to "PROG" if yes */
|
||||
static int
|
||||
parse_command_args(struct arglist *arglist, union node **argpp, const char **path)
|
||||
{
|
||||
struct strlist *sp = arglist->list;
|
||||
char *cp, c;
|
||||
|
||||
for (;;) {
|
||||
cp = *++argv;
|
||||
if (!cp)
|
||||
return NULL;
|
||||
sp = sp->next ? sp->next : fill_arglist(arglist, argpp);
|
||||
if (!sp)
|
||||
return 0;
|
||||
cp = sp->text;
|
||||
if (*cp++ != '-')
|
||||
break;
|
||||
c = *cp++;
|
||||
if (!c)
|
||||
break;
|
||||
if (c == '-' && !*cp) {
|
||||
if (!*++argv)
|
||||
return NULL;
|
||||
if (!sp->next && !fill_arglist(arglist, argpp))
|
||||
return 0;
|
||||
sp = sp->next;
|
||||
break;
|
||||
}
|
||||
do {
|
||||
@ -8745,12 +8764,14 @@ parse_command_args(char **argv, const char **path)
|
||||
break;
|
||||
default:
|
||||
/* run 'typecmd' for other options */
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
c = *cp++;
|
||||
} while (c);
|
||||
}
|
||||
return argv;
|
||||
|
||||
arglist->list = sp;
|
||||
return DO_NOFUNC;
|
||||
}
|
||||
|
||||
static int FAST_FUNC
|
||||
@ -10124,7 +10145,7 @@ static int
|
||||
evalcommand(union node *cmd, int flags)
|
||||
{
|
||||
static const struct builtincmd null_bltin = {
|
||||
"\0\0", bltincmd /* why three NULs? */
|
||||
BUILTIN_REGULAR "", bltincmd
|
||||
};
|
||||
struct localvar_list *localvar_stop;
|
||||
struct parsefile *file_stop;
|
||||
@ -10134,12 +10155,14 @@ evalcommand(union node *cmd, int flags)
|
||||
struct arglist varlist;
|
||||
char **argv;
|
||||
int argc;
|
||||
struct strlist *osp;
|
||||
const struct strlist *sp;
|
||||
struct cmdentry cmdentry;
|
||||
struct job *jp;
|
||||
char *lastarg;
|
||||
const char *path;
|
||||
int spclbltin;
|
||||
int cmd_flag;
|
||||
int status;
|
||||
char **nargv;
|
||||
smallint cmd_is_exec;
|
||||
@ -10161,26 +10184,46 @@ evalcommand(union node *cmd, int flags)
|
||||
arglist.lastp = &arglist.list;
|
||||
*arglist.lastp = NULL;
|
||||
|
||||
cmd_flag = 0;
|
||||
cmd_is_exec = 0;
|
||||
spclbltin = -1;
|
||||
path = NULL;
|
||||
|
||||
argc = 0;
|
||||
if (cmd->ncmd.args) {
|
||||
struct builtincmd *bcmd;
|
||||
smallint pseudovarflag;
|
||||
argp = cmd->ncmd.args;
|
||||
osp = fill_arglist(&arglist, &argp);
|
||||
if (osp) {
|
||||
int pseudovarflag = 0;
|
||||
|
||||
bcmd = find_builtin(cmd->ncmd.args->narg.text);
|
||||
pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
|
||||
for (;;) {
|
||||
find_command(arglist.list->text, &cmdentry,
|
||||
cmd_flag | DO_REGBLTIN, pathval());
|
||||
|
||||
for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
|
||||
struct strlist **spp;
|
||||
/* implement bltin and command here */
|
||||
if (cmdentry.cmdtype != CMDBUILTIN)
|
||||
break;
|
||||
|
||||
spp = arglist.lastp;
|
||||
if (pseudovarflag && isassignment(argp->narg.text))
|
||||
expandarg(argp, &arglist, EXP_VARTILDE);
|
||||
else
|
||||
expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
|
||||
|
||||
for (sp = *spp; sp; sp = sp->next)
|
||||
argc++;
|
||||
pseudovarflag = IS_BUILTIN_ASSIGN(cmdentry.u.cmd);
|
||||
if (spclbltin < 0) {
|
||||
spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
|
||||
}
|
||||
cmd_is_exec = cmdentry.u.cmd == EXECCMD;
|
||||
if (cmdentry.u.cmd != COMMANDCMD)
|
||||
break;
|
||||
|
||||
cmd_flag = parse_command_args(&arglist, &argp, &path);
|
||||
if (!cmd_flag)
|
||||
break;
|
||||
}
|
||||
|
||||
for (; argp; argp = argp->narg.next)
|
||||
expandarg(argp, &arglist,
|
||||
pseudovarflag &&
|
||||
isassignment(argp->narg.text) ?
|
||||
EXP_VARTILDE : EXP_FULL | EXP_TILDE);
|
||||
|
||||
for (sp = arglist.list; sp; sp = sp->next)
|
||||
argc++;
|
||||
}
|
||||
|
||||
/* Reserve one extra spot at the front for shellexec. */
|
||||
@ -10210,23 +10253,13 @@ evalcommand(union node *cmd, int flags)
|
||||
}
|
||||
status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
|
||||
|
||||
path = vpath.var_text;
|
||||
for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
|
||||
struct strlist **spp;
|
||||
char *p;
|
||||
|
||||
spp = varlist.lastp;
|
||||
expandarg(argp, &varlist, EXP_VARTILDE);
|
||||
|
||||
mklocal((*spp)->text);
|
||||
|
||||
/*
|
||||
* Modify the command lookup path, if a PATH= assignment
|
||||
* is present
|
||||
*/
|
||||
p = (*spp)->text;
|
||||
if (varcmp(p, path) == 0)
|
||||
path = p;
|
||||
}
|
||||
|
||||
/* Print the command if xflag is set. */
|
||||
@ -10265,47 +10298,17 @@ evalcommand(union node *cmd, int flags)
|
||||
safe_write(preverrout_fd, "\n", 1);
|
||||
}
|
||||
|
||||
cmd_is_exec = 0;
|
||||
spclbltin = -1;
|
||||
|
||||
/* Now locate the command. */
|
||||
if (argc) {
|
||||
int cmd_flag = DO_ERR;
|
||||
#if ENABLE_ASH_CMDCMD
|
||||
const char *oldpath = path + 5;
|
||||
#endif
|
||||
path += 5;
|
||||
for (;;) {
|
||||
find_command(argv[0], &cmdentry, cmd_flag, path);
|
||||
if (cmdentry.cmdtype != CMDBUILTIN
|
||||
|| !(IS_BUILTIN_REGULAR(cmdentry.u.cmd))
|
||||
) {
|
||||
find_command(argv[0], &cmdentry, cmd_flag | DO_ERR,
|
||||
path ? path : pathval());
|
||||
if (cmdentry.cmdtype == CMDUNKNOWN) {
|
||||
flush_stdout_stderr();
|
||||
status = 127;
|
||||
flush_stdout_stderr();
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* implement bltin and command here */
|
||||
if (cmdentry.cmdtype != CMDBUILTIN)
|
||||
break;
|
||||
if (spclbltin < 0)
|
||||
spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
|
||||
if (cmdentry.u.cmd == EXECCMD)
|
||||
cmd_is_exec = 1;
|
||||
#if ENABLE_ASH_CMDCMD
|
||||
if (cmdentry.u.cmd == COMMANDCMD) {
|
||||
path = oldpath;
|
||||
nargv = parse_command_args(argv, &path);
|
||||
if (!nargv)
|
||||
break;
|
||||
/* It's "command [-p] PROG ARGS" (that is, no -Vv).
|
||||
* nargv => "PROG". path is updated if -p.
|
||||
*/
|
||||
argc -= nargv - argv;
|
||||
argv = nargv;
|
||||
cmd_flag |= DO_NOFUNC;
|
||||
} else
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (status) {
|
||||
@ -10383,6 +10386,7 @@ evalcommand(union node *cmd, int flags)
|
||||
/* fall through to exec'ing external program */
|
||||
}
|
||||
listsetvar(varlist.list, VEXPORT|VSTACK);
|
||||
path = path ? path : pathval();
|
||||
shellexec(argv[0], argv, path, cmdentry.u.index);
|
||||
/* NOTREACHED */
|
||||
} /* default */
|
||||
@ -13538,11 +13542,8 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
|
||||
/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
|
||||
|
||||
updatetbl = (path == pathval());
|
||||
if (!updatetbl) {
|
||||
if (!updatetbl)
|
||||
act |= DO_ALTPATH;
|
||||
if (strstr(path, "%builtin") != NULL)
|
||||
act |= DO_ALTBLTIN;
|
||||
}
|
||||
|
||||
/* If name is in the table, check answer will be ok */
|
||||
cmdp = cmdlookup(name, 0);
|
||||
@ -13555,16 +13556,19 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
|
||||
abort();
|
||||
#endif
|
||||
case CMDNORMAL:
|
||||
bit = DO_ALTPATH;
|
||||
bit = DO_ALTPATH | DO_REGBLTIN;
|
||||
break;
|
||||
case CMDFUNCTION:
|
||||
bit = DO_NOFUNC;
|
||||
break;
|
||||
case CMDBUILTIN:
|
||||
bit = IS_BUILTIN_REGULAR(cmdp->param.cmd) ? 0 : DO_ALTBLTIN;
|
||||
bit = IS_BUILTIN_REGULAR(cmdp->param.cmd) ? 0 : DO_REGBLTIN;
|
||||
break;
|
||||
}
|
||||
if (act & bit) {
|
||||
if (act & bit & DO_REGBLTIN)
|
||||
goto fail;
|
||||
|
||||
updatetbl = 0;
|
||||
cmdp = NULL;
|
||||
} else if (cmdp->rehash == 0)
|
||||
@ -13577,13 +13581,14 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
|
||||
if (bcmd) {
|
||||
if (IS_BUILTIN_REGULAR(bcmd))
|
||||
goto builtin_success;
|
||||
if (act & DO_ALTPATH) {
|
||||
if (!(act & DO_ALTBLTIN))
|
||||
if (act & DO_ALTPATH)
|
||||
goto builtin_success;
|
||||
} else if (builtinloc <= 0) {
|
||||
if (builtinloc <= 0)
|
||||
goto builtin_success;
|
||||
}
|
||||
}
|
||||
|
||||
if (act & DO_REGBLTIN)
|
||||
goto fail;
|
||||
|
||||
#if ENABLE_FEATURE_SH_STANDALONE
|
||||
{
|
||||
@ -13688,6 +13693,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
|
||||
#endif
|
||||
ash_msg("%s: %s", name, errmsg(e, "not found"));
|
||||
}
|
||||
fail:
|
||||
entry->cmdtype = CMDUNKNOWN;
|
||||
return;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user