diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2020-02-19 15:15:13 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2020-02-20 09:36:51 +0100 |
commit | 7eb8eecbbc752d04381f35d196bbdf1e3b17e2d1 (patch) | |
tree | 52c34b1ccd069bc6155ca213388877fb442a97ce | |
parent | c91950f31532febe34e00ab20d3e5f462d9daa52 (diff) | |
download | busybox-w32-7eb8eecbbc752d04381f35d196bbdf1e3b17e2d1.tar.gz busybox-w32-7eb8eecbbc752d04381f35d196bbdf1e3b17e2d1.tar.bz2 busybox-w32-7eb8eecbbc752d04381f35d196bbdf1e3b17e2d1.zip |
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>
-rw-r--r-- | shell/ash.c | 176 |
1 files changed, 91 insertions, 85 deletions
diff --git a/shell/ash.c b/shell/ash.c index fea4b10a7..145896229 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -8112,7 +8112,7 @@ struct cmdentry { | |||
8112 | #define DO_ABS 0x02 /* checks absolute paths */ | 8112 | #define DO_ABS 0x02 /* checks absolute paths */ |
8113 | #define DO_NOFUNC 0x04 /* don't return shell functions, for command */ | 8113 | #define DO_NOFUNC 0x04 /* don't return shell functions, for command */ |
8114 | #define DO_ALTPATH 0x08 /* using alternate path */ | 8114 | #define DO_ALTPATH 0x08 /* using alternate path */ |
8115 | #define DO_ALTBLTIN 0x20 /* %builtin in alt. path */ | 8115 | #define DO_REGBLTIN 0x10 /* regular built-ins and functions only */ |
8116 | 8116 | ||
8117 | static void find_command(char *, struct cmdentry *, int, const char *); | 8117 | static void find_command(char *, struct cmdentry *, int, const char *); |
8118 | 8118 | ||
@@ -8718,24 +8718,43 @@ typecmd(int argc UNUSED_PARAM, char **argv) | |||
8718 | } | 8718 | } |
8719 | 8719 | ||
8720 | #if ENABLE_ASH_CMDCMD | 8720 | #if ENABLE_ASH_CMDCMD |
8721 | static struct strlist * | ||
8722 | fill_arglist(struct arglist *arglist, union node **argpp) | ||
8723 | { | ||
8724 | struct strlist **lastp = arglist->lastp; | ||
8725 | union node *argp; | ||
8726 | |||
8727 | while ((argp = *argpp) != NULL) { | ||
8728 | expandarg(argp, arglist, EXP_FULL | EXP_TILDE); | ||
8729 | *argpp = argp->narg.next; | ||
8730 | if (*lastp) | ||
8731 | break; | ||
8732 | } | ||
8733 | |||
8734 | return *lastp; | ||
8735 | } | ||
8736 | |||
8721 | /* Is it "command [-p] PROG ARGS" bltin, no other opts? Return ptr to "PROG" if yes */ | 8737 | /* Is it "command [-p] PROG ARGS" bltin, no other opts? Return ptr to "PROG" if yes */ |
8722 | static char ** | 8738 | static int |
8723 | parse_command_args(char **argv, const char **path) | 8739 | parse_command_args(struct arglist *arglist, union node **argpp, const char **path) |
8724 | { | 8740 | { |
8741 | struct strlist *sp = arglist->list; | ||
8725 | char *cp, c; | 8742 | char *cp, c; |
8726 | 8743 | ||
8727 | for (;;) { | 8744 | for (;;) { |
8728 | cp = *++argv; | 8745 | sp = sp->next ? sp->next : fill_arglist(arglist, argpp); |
8729 | if (!cp) | 8746 | if (!sp) |
8730 | return NULL; | 8747 | return 0; |
8748 | cp = sp->text; | ||
8731 | if (*cp++ != '-') | 8749 | if (*cp++ != '-') |
8732 | break; | 8750 | break; |
8733 | c = *cp++; | 8751 | c = *cp++; |
8734 | if (!c) | 8752 | if (!c) |
8735 | break; | 8753 | break; |
8736 | if (c == '-' && !*cp) { | 8754 | if (c == '-' && !*cp) { |
8737 | if (!*++argv) | 8755 | if (!sp->next && !fill_arglist(arglist, argpp)) |
8738 | return NULL; | 8756 | return 0; |
8757 | sp = sp->next; | ||
8739 | break; | 8758 | break; |
8740 | } | 8759 | } |
8741 | do { | 8760 | do { |
@@ -8745,12 +8764,14 @@ parse_command_args(char **argv, const char **path) | |||
8745 | break; | 8764 | break; |
8746 | default: | 8765 | default: |
8747 | /* run 'typecmd' for other options */ | 8766 | /* run 'typecmd' for other options */ |
8748 | return NULL; | 8767 | return 0; |
8749 | } | 8768 | } |
8750 | c = *cp++; | 8769 | c = *cp++; |
8751 | } while (c); | 8770 | } while (c); |
8752 | } | 8771 | } |
8753 | return argv; | 8772 | |
8773 | arglist->list = sp; | ||
8774 | return DO_NOFUNC; | ||
8754 | } | 8775 | } |
8755 | 8776 | ||
8756 | static int FAST_FUNC | 8777 | static int FAST_FUNC |
@@ -10124,7 +10145,7 @@ static int | |||
10124 | evalcommand(union node *cmd, int flags) | 10145 | evalcommand(union node *cmd, int flags) |
10125 | { | 10146 | { |
10126 | static const struct builtincmd null_bltin = { | 10147 | static const struct builtincmd null_bltin = { |
10127 | "\0\0", bltincmd /* why three NULs? */ | 10148 | BUILTIN_REGULAR "", bltincmd |
10128 | }; | 10149 | }; |
10129 | struct localvar_list *localvar_stop; | 10150 | struct localvar_list *localvar_stop; |
10130 | struct parsefile *file_stop; | 10151 | struct parsefile *file_stop; |
@@ -10134,12 +10155,14 @@ evalcommand(union node *cmd, int flags) | |||
10134 | struct arglist varlist; | 10155 | struct arglist varlist; |
10135 | char **argv; | 10156 | char **argv; |
10136 | int argc; | 10157 | int argc; |
10158 | struct strlist *osp; | ||
10137 | const struct strlist *sp; | 10159 | const struct strlist *sp; |
10138 | struct cmdentry cmdentry; | 10160 | struct cmdentry cmdentry; |
10139 | struct job *jp; | 10161 | struct job *jp; |
10140 | char *lastarg; | 10162 | char *lastarg; |
10141 | const char *path; | 10163 | const char *path; |
10142 | int spclbltin; | 10164 | int spclbltin; |
10165 | int cmd_flag; | ||
10143 | int status; | 10166 | int status; |
10144 | char **nargv; | 10167 | char **nargv; |
10145 | smallint cmd_is_exec; | 10168 | smallint cmd_is_exec; |
@@ -10161,26 +10184,46 @@ evalcommand(union node *cmd, int flags) | |||
10161 | arglist.lastp = &arglist.list; | 10184 | arglist.lastp = &arglist.list; |
10162 | *arglist.lastp = NULL; | 10185 | *arglist.lastp = NULL; |
10163 | 10186 | ||
10187 | cmd_flag = 0; | ||
10188 | cmd_is_exec = 0; | ||
10189 | spclbltin = -1; | ||
10190 | path = NULL; | ||
10191 | |||
10164 | argc = 0; | 10192 | argc = 0; |
10165 | if (cmd->ncmd.args) { | 10193 | argp = cmd->ncmd.args; |
10166 | struct builtincmd *bcmd; | 10194 | osp = fill_arglist(&arglist, &argp); |
10167 | smallint pseudovarflag; | 10195 | if (osp) { |
10196 | int pseudovarflag = 0; | ||
10168 | 10197 | ||
10169 | bcmd = find_builtin(cmd->ncmd.args->narg.text); | 10198 | for (;;) { |
10170 | pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd); | 10199 | find_command(arglist.list->text, &cmdentry, |
10200 | cmd_flag | DO_REGBLTIN, pathval()); | ||
10171 | 10201 | ||
10172 | for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) { | 10202 | /* implement bltin and command here */ |
10173 | struct strlist **spp; | 10203 | if (cmdentry.cmdtype != CMDBUILTIN) |
10204 | break; | ||
10174 | 10205 | ||
10175 | spp = arglist.lastp; | 10206 | pseudovarflag = IS_BUILTIN_ASSIGN(cmdentry.u.cmd); |
10176 | if (pseudovarflag && isassignment(argp->narg.text)) | 10207 | if (spclbltin < 0) { |
10177 | expandarg(argp, &arglist, EXP_VARTILDE); | 10208 | spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd); |
10178 | else | 10209 | } |
10179 | expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); | 10210 | cmd_is_exec = cmdentry.u.cmd == EXECCMD; |
10211 | if (cmdentry.u.cmd != COMMANDCMD) | ||
10212 | break; | ||
10180 | 10213 | ||
10181 | for (sp = *spp; sp; sp = sp->next) | 10214 | cmd_flag = parse_command_args(&arglist, &argp, &path); |
10182 | argc++; | 10215 | if (!cmd_flag) |
10216 | break; | ||
10183 | } | 10217 | } |
10218 | |||
10219 | for (; argp; argp = argp->narg.next) | ||
10220 | expandarg(argp, &arglist, | ||
10221 | pseudovarflag && | ||
10222 | isassignment(argp->narg.text) ? | ||
10223 | EXP_VARTILDE : EXP_FULL | EXP_TILDE); | ||
10224 | |||
10225 | for (sp = arglist.list; sp; sp = sp->next) | ||
10226 | argc++; | ||
10184 | } | 10227 | } |
10185 | 10228 | ||
10186 | /* Reserve one extra spot at the front for shellexec. */ | 10229 | /* Reserve one extra spot at the front for shellexec. */ |
@@ -10210,23 +10253,13 @@ evalcommand(union node *cmd, int flags) | |||
10210 | } | 10253 | } |
10211 | status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2); | 10254 | status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2); |
10212 | 10255 | ||
10213 | path = vpath.var_text; | ||
10214 | for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) { | 10256 | for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) { |
10215 | struct strlist **spp; | 10257 | struct strlist **spp; |
10216 | char *p; | ||
10217 | 10258 | ||
10218 | spp = varlist.lastp; | 10259 | spp = varlist.lastp; |
10219 | expandarg(argp, &varlist, EXP_VARTILDE); | 10260 | expandarg(argp, &varlist, EXP_VARTILDE); |
10220 | 10261 | ||
10221 | mklocal((*spp)->text); | 10262 | mklocal((*spp)->text); |
10222 | |||
10223 | /* | ||
10224 | * Modify the command lookup path, if a PATH= assignment | ||
10225 | * is present | ||
10226 | */ | ||
10227 | p = (*spp)->text; | ||
10228 | if (varcmp(p, path) == 0) | ||
10229 | path = p; | ||
10230 | } | 10263 | } |
10231 | 10264 | ||
10232 | /* Print the command if xflag is set. */ | 10265 | /* Print the command if xflag is set. */ |
@@ -10265,46 +10298,16 @@ evalcommand(union node *cmd, int flags) | |||
10265 | safe_write(preverrout_fd, "\n", 1); | 10298 | safe_write(preverrout_fd, "\n", 1); |
10266 | } | 10299 | } |
10267 | 10300 | ||
10268 | cmd_is_exec = 0; | ||
10269 | spclbltin = -1; | ||
10270 | |||
10271 | /* Now locate the command. */ | 10301 | /* Now locate the command. */ |
10272 | if (argc) { | 10302 | if (cmdentry.cmdtype != CMDBUILTIN |
10273 | int cmd_flag = DO_ERR; | 10303 | || !(IS_BUILTIN_REGULAR(cmdentry.u.cmd)) |
10274 | #if ENABLE_ASH_CMDCMD | 10304 | ) { |
10275 | const char *oldpath = path + 5; | 10305 | find_command(argv[0], &cmdentry, cmd_flag | DO_ERR, |
10276 | #endif | 10306 | path ? path : pathval()); |
10277 | path += 5; | 10307 | if (cmdentry.cmdtype == CMDUNKNOWN) { |
10278 | for (;;) { | 10308 | status = 127; |
10279 | find_command(argv[0], &cmdentry, cmd_flag, path); | 10309 | flush_stdout_stderr(); |
10280 | if (cmdentry.cmdtype == CMDUNKNOWN) { | 10310 | goto bail; |
10281 | flush_stdout_stderr(); | ||
10282 | status = 127; | ||
10283 | goto bail; | ||
10284 | } | ||
10285 | |||
10286 | /* implement bltin and command here */ | ||
10287 | if (cmdentry.cmdtype != CMDBUILTIN) | ||
10288 | break; | ||
10289 | if (spclbltin < 0) | ||
10290 | spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd); | ||
10291 | if (cmdentry.u.cmd == EXECCMD) | ||
10292 | cmd_is_exec = 1; | ||
10293 | #if ENABLE_ASH_CMDCMD | ||
10294 | if (cmdentry.u.cmd == COMMANDCMD) { | ||
10295 | path = oldpath; | ||
10296 | nargv = parse_command_args(argv, &path); | ||
10297 | if (!nargv) | ||
10298 | break; | ||
10299 | /* It's "command [-p] PROG ARGS" (that is, no -Vv). | ||
10300 | * nargv => "PROG". path is updated if -p. | ||
10301 | */ | ||
10302 | argc -= nargv - argv; | ||
10303 | argv = nargv; | ||
10304 | cmd_flag |= DO_NOFUNC; | ||
10305 | } else | ||
10306 | #endif | ||
10307 | break; | ||
10308 | } | 10311 | } |
10309 | } | 10312 | } |
10310 | 10313 | ||
@@ -10383,6 +10386,7 @@ evalcommand(union node *cmd, int flags) | |||
10383 | /* fall through to exec'ing external program */ | 10386 | /* fall through to exec'ing external program */ |
10384 | } | 10387 | } |
10385 | listsetvar(varlist.list, VEXPORT|VSTACK); | 10388 | listsetvar(varlist.list, VEXPORT|VSTACK); |
10389 | path = path ? path : pathval(); | ||
10386 | shellexec(argv[0], argv, path, cmdentry.u.index); | 10390 | shellexec(argv[0], argv, path, cmdentry.u.index); |
10387 | /* NOTREACHED */ | 10391 | /* NOTREACHED */ |
10388 | } /* default */ | 10392 | } /* default */ |
@@ -13538,11 +13542,8 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) | |||
13538 | /* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */ | 13542 | /* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */ |
13539 | 13543 | ||
13540 | updatetbl = (path == pathval()); | 13544 | updatetbl = (path == pathval()); |
13541 | if (!updatetbl) { | 13545 | if (!updatetbl) |
13542 | act |= DO_ALTPATH; | 13546 | act |= DO_ALTPATH; |
13543 | if (strstr(path, "%builtin") != NULL) | ||
13544 | act |= DO_ALTBLTIN; | ||
13545 | } | ||
13546 | 13547 | ||
13547 | /* If name is in the table, check answer will be ok */ | 13548 | /* If name is in the table, check answer will be ok */ |
13548 | cmdp = cmdlookup(name, 0); | 13549 | cmdp = cmdlookup(name, 0); |
@@ -13555,16 +13556,19 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) | |||
13555 | abort(); | 13556 | abort(); |
13556 | #endif | 13557 | #endif |
13557 | case CMDNORMAL: | 13558 | case CMDNORMAL: |
13558 | bit = DO_ALTPATH; | 13559 | bit = DO_ALTPATH | DO_REGBLTIN; |
13559 | break; | 13560 | break; |
13560 | case CMDFUNCTION: | 13561 | case CMDFUNCTION: |
13561 | bit = DO_NOFUNC; | 13562 | bit = DO_NOFUNC; |
13562 | break; | 13563 | break; |
13563 | case CMDBUILTIN: | 13564 | case CMDBUILTIN: |
13564 | bit = IS_BUILTIN_REGULAR(cmdp->param.cmd) ? 0 : DO_ALTBLTIN; | 13565 | bit = IS_BUILTIN_REGULAR(cmdp->param.cmd) ? 0 : DO_REGBLTIN; |
13565 | break; | 13566 | break; |
13566 | } | 13567 | } |
13567 | if (act & bit) { | 13568 | if (act & bit) { |
13569 | if (act & bit & DO_REGBLTIN) | ||
13570 | goto fail; | ||
13571 | |||
13568 | updatetbl = 0; | 13572 | updatetbl = 0; |
13569 | cmdp = NULL; | 13573 | cmdp = NULL; |
13570 | } else if (cmdp->rehash == 0) | 13574 | } else if (cmdp->rehash == 0) |
@@ -13577,14 +13581,15 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) | |||
13577 | if (bcmd) { | 13581 | if (bcmd) { |
13578 | if (IS_BUILTIN_REGULAR(bcmd)) | 13582 | if (IS_BUILTIN_REGULAR(bcmd)) |
13579 | goto builtin_success; | 13583 | goto builtin_success; |
13580 | if (act & DO_ALTPATH) { | 13584 | if (act & DO_ALTPATH) |
13581 | if (!(act & DO_ALTBLTIN)) | 13585 | goto builtin_success; |
13582 | goto builtin_success; | 13586 | if (builtinloc <= 0) |
13583 | } else if (builtinloc <= 0) { | ||
13584 | goto builtin_success; | 13587 | goto builtin_success; |
13585 | } | ||
13586 | } | 13588 | } |
13587 | 13589 | ||
13590 | if (act & DO_REGBLTIN) | ||
13591 | goto fail; | ||
13592 | |||
13588 | #if ENABLE_FEATURE_SH_STANDALONE | 13593 | #if ENABLE_FEATURE_SH_STANDALONE |
13589 | { | 13594 | { |
13590 | int applet_no = find_applet_by_name(name); | 13595 | int applet_no = find_applet_by_name(name); |
@@ -13688,6 +13693,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) | |||
13688 | #endif | 13693 | #endif |
13689 | ash_msg("%s: %s", name, errmsg(e, "not found")); | 13694 | ash_msg("%s: %s", name, errmsg(e, "not found")); |
13690 | } | 13695 | } |
13696 | fail: | ||
13691 | entry->cmdtype = CMDUNKNOWN; | 13697 | entry->cmdtype = CMDUNKNOWN; |
13692 | return; | 13698 | return; |
13693 | 13699 | ||