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:
Denys Vlasenko 2020-02-19 15:15:13 +01:00
parent c91950f315
commit 7eb8eecbbc

View File

@ -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);
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;
for (sp = *spp; sp; sp = sp->next)
argc++;
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,46 +10298,16 @@ 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 == CMDUNKNOWN) {
flush_stdout_stderr();
status = 127;
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 (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) {
status = 127;
flush_stdout_stderr();
goto bail;
}
}
@ -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,14 +13581,15 @@ 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))
goto builtin_success;
} else if (builtinloc <= 0) {
if (act & DO_ALTPATH)
goto builtin_success;
if (builtinloc <= 0)
goto builtin_success;
}
}
if (act & DO_REGBLTIN)
goto fail;
#if ENABLE_FEATURE_SH_STANDALONE
{
int applet_no = find_applet_by_name(name);
@ -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;