From 7eb8eecbbc752d04381f35d196bbdf1e3b17e2d1 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 19 Feb 2020 15:15:13 +0100 Subject: 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 Signed-off-by: Denys Vlasenko --- shell/ash.c | 176 +++++++++++++++++++++++++++++++----------------------------- 1 file changed, 91 insertions(+), 85 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index fea4b10..1458962 100644 --- a/shell/ash.c +++ b/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 +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 char ** -parse_command_args(char **argv, const char **path) +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; -- cgit v1.1