From c91950f31532febe34e00ab20d3e5f462d9daa52 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 19 Feb 2020 12:10:41 +0100 Subject: ash,hush: testcase for "exit" without arguments in a trap hush fails this one Signed-off-by: Denys Vlasenko --- shell/ash_test/ash-misc/exitcode_trap2.right | 1 + shell/ash_test/ash-misc/exitcode_trap2.tests | 9 +++++++++ shell/hush_test/hush-misc/exitcode_trap2.right | 1 + shell/hush_test/hush-misc/exitcode_trap2.tests | 9 +++++++++ 4 files changed, 20 insertions(+) create mode 100644 shell/ash_test/ash-misc/exitcode_trap2.right create mode 100755 shell/ash_test/ash-misc/exitcode_trap2.tests create mode 100644 shell/hush_test/hush-misc/exitcode_trap2.right create mode 100755 shell/hush_test/hush-misc/exitcode_trap2.tests (limited to 'shell') diff --git a/shell/ash_test/ash-misc/exitcode_trap2.right b/shell/ash_test/ash-misc/exitcode_trap2.right new file mode 100644 index 000000000..6644d86bf --- /dev/null +++ b/shell/ash_test/ash-misc/exitcode_trap2.right @@ -0,0 +1 @@ +42:42 diff --git a/shell/ash_test/ash-misc/exitcode_trap2.tests b/shell/ash_test/ash-misc/exitcode_trap2.tests new file mode 100755 index 000000000..f259774bf --- /dev/null +++ b/shell/ash_test/ash-misc/exitcode_trap2.tests @@ -0,0 +1,9 @@ +# "exit" in trap should not use last command's exitcode, +# but exitcode on entering the trap. +$THIS_SH -c ' + trap "false;exit" term + kill $$ & + (exit 42) + wait +' +echo 42:$? diff --git a/shell/hush_test/hush-misc/exitcode_trap2.right b/shell/hush_test/hush-misc/exitcode_trap2.right new file mode 100644 index 000000000..6644d86bf --- /dev/null +++ b/shell/hush_test/hush-misc/exitcode_trap2.right @@ -0,0 +1 @@ +42:42 diff --git a/shell/hush_test/hush-misc/exitcode_trap2.tests b/shell/hush_test/hush-misc/exitcode_trap2.tests new file mode 100755 index 000000000..f259774bf --- /dev/null +++ b/shell/hush_test/hush-misc/exitcode_trap2.tests @@ -0,0 +1,9 @@ +# "exit" in trap should not use last command's exitcode, +# but exitcode on entering the trap. +$THIS_SH -c ' + trap "false;exit" term + kill $$ & + (exit 42) + wait +' +echo 42:$? -- cgit v1.2.3-55-g6feb 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(-) (limited to 'shell') 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 { #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.2.3-55-g6feb From 54bef2a8efd21e9992a9d26cf30c5b26cbc0e21a Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 19 Feb 2020 15:30:20 +0100 Subject: ash: eval: Fail immediately with redirections errors for simple command Upstream commit: Date: Sat, 19 May 2018 02:39:54 +0800 eval: Fail immediately with redirections errors for simple command Previously, dash would continue to perform variable expansions even if a redirection error occured. This patch changes it so that it fails immediately. Signed-off-by: Herbert Xu Signed-off-by: Denys Vlasenko --- shell/ash.c | 32 +++++++++++++++--------------- shell/ash_test/ash-redir/redir_exec1.right | 1 - 2 files changed, 16 insertions(+), 17 deletions(-) (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index 145896229..5570057e9 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -10253,6 +10253,17 @@ evalcommand(union node *cmd, int flags) } status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2); + if (status) { + bail: + exitstatus = status; + + /* We have a redirection error. */ + if (spclbltin > 0) + raise_exception(EXERROR); + + goto out; + } + for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) { struct strlist **spp; @@ -10304,28 +10315,17 @@ evalcommand(union node *cmd, int flags) ) { find_command(argv[0], &cmdentry, cmd_flag | DO_ERR, path ? path : pathval()); - if (cmdentry.cmdtype == CMDUNKNOWN) { - status = 127; - flush_stdout_stderr(); - goto bail; - } - } - - if (status) { - bail: - exitstatus = status; - - /* We have a redirection error. */ - if (spclbltin > 0) - raise_exception(EXERROR); - - goto out; } jp = NULL; /* Execute the command. */ switch (cmdentry.cmdtype) { + case CMDUNKNOWN: + status = 127; + flush_stdout_stderr(); + goto bail; + default: { #if ENABLE_FEATURE_SH_STANDALONE \ diff --git a/shell/ash_test/ash-redir/redir_exec1.right b/shell/ash_test/ash-redir/redir_exec1.right index c98455bf5..26a664edc 100644 --- a/shell/ash_test/ash-redir/redir_exec1.right +++ b/shell/ash_test/ash-redir/redir_exec1.right @@ -1,2 +1 @@ ./redir_exec1.tests: line 1: can't create /cant/be/created: nonexistent directory -First -- cgit v1.2.3-55-g6feb From 3e729102a86b1fb536f61c6ebcd95321ba98528d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 19 Feb 2020 17:33:44 +0100 Subject: ash: eval: Replace with listsetvar with mklocal/setvareq Upstream commit: Date: Sat, 19 May 2018 02:39:55 +0800 eval: Replace with listsetvar with mklocal/setvareq This patch replaces listsetvar with mklocal/setvareq. As we now determine special built-in status prior to variable assignment, we no longer have to do a second pass listsetvar. Instead we will call setvareq directly instead of mklocal when necessary. In order to do this mklocal can now take a flag in order to mark a variable for export. Signed-off-by: Herbert Xu Signed-off-by: Denys Vlasenko --- shell/ash.c | 63 +++++++++++++++++++++++++++---------------------------------- 1 file changed, 28 insertions(+), 35 deletions(-) (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index 5570057e9..5bb10e5cb 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -2476,24 +2476,6 @@ unsetvar(const char *s) setvar(s, NULL, 0); } -/* - * Process a linked list of variable assignments. - */ -static void -listsetvar(struct strlist *list_set_var, int flags) -{ - struct strlist *lp = list_set_var; - - if (!lp) - return; - INT_OFF; - do { - setvareq(lp->text, flags); - lp = lp->next; - } while (lp); - INT_ON; -} - /* * Generate a list of variables satisfying the given conditions. */ @@ -9810,7 +9792,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) * (options will be restored on return from the function). */ static void -mklocal(char *name) +mklocal(char *name, int flags) { struct localvar *lvp; struct var **vpp; @@ -9847,9 +9829,9 @@ mklocal(char *name) if (vp == NULL) { /* variable did not exist yet */ if (eq) - vp = setvareq(name, VSTRFIXED); + vp = setvareq(name, VSTRFIXED | flags); else - vp = setvar(name, NULL, VSTRFIXED); + vp = setvar(name, NULL, VSTRFIXED | flags); lvp->flags = VUNSET; } else { lvp->text = vp->var_text; @@ -9859,7 +9841,7 @@ mklocal(char *name) */ vp->flags |= VSTRFIXED|VTEXTFIXED; if (eq) - setvareq(name, 0); + setvareq(name, flags); else /* "local VAR" unsets VAR: */ setvar0(name, NULL); @@ -9885,7 +9867,7 @@ localcmd(int argc UNUSED_PARAM, char **argv) argv = argptr; while ((name = *argv++) != NULL) { - mklocal(name); + mklocal(name, 0); } return 0; } @@ -10166,6 +10148,8 @@ evalcommand(union node *cmd, int flags) int status; char **nargv; smallint cmd_is_exec; + int vflags; + int vlocal; errlinno = lineno = cmd->ncmd.linno; if (funcline) @@ -10173,7 +10157,6 @@ evalcommand(union node *cmd, int flags) /* First expand the arguments. */ TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); - localvar_stop = pushlocalvars(); file_stop = g_parsefile; back_exitstatus = 0; @@ -10187,6 +10170,8 @@ evalcommand(union node *cmd, int flags) cmd_flag = 0; cmd_is_exec = 0; spclbltin = -1; + vflags = 0; + vlocal = 0; path = NULL; argc = 0; @@ -10199,6 +10184,8 @@ evalcommand(union node *cmd, int flags) find_command(arglist.list->text, &cmdentry, cmd_flag | DO_REGBLTIN, pathval()); + vlocal++; + /* implement bltin and command here */ if (cmdentry.cmdtype != CMDBUILTIN) break; @@ -10206,6 +10193,7 @@ evalcommand(union node *cmd, int flags) pseudovarflag = IS_BUILTIN_ASSIGN(cmdentry.u.cmd); if (spclbltin < 0) { spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd); + vlocal = !spclbltin; } cmd_is_exec = cmdentry.u.cmd == EXECCMD; if (cmdentry.u.cmd != COMMANDCMD) @@ -10224,6 +10212,9 @@ evalcommand(union node *cmd, int flags) for (sp = arglist.list; sp; sp = sp->next) argc++; + + if (cmd_is_exec && argc > 1) + vflags = VEXPORT; } /* Reserve one extra spot at the front for shellexec. */ @@ -10254,6 +10245,7 @@ evalcommand(union node *cmd, int flags) status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2); if (status) { + vlocal = 0; bail: exitstatus = status; @@ -10264,13 +10256,20 @@ evalcommand(union node *cmd, int flags) goto out; } + localvar_stop = NULL; + if (vlocal) + localvar_stop = pushlocalvars(); + for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) { struct strlist **spp; spp = varlist.lastp; expandarg(argp, &varlist, EXP_VARTILDE); - mklocal((*spp)->text); + if (vlocal) + mklocal((*spp)->text, VEXPORT); + else + setvareq((*spp)->text, vflags); } /* Print the command if xflag is set. */ @@ -10313,8 +10312,8 @@ evalcommand(union node *cmd, int flags) if (cmdentry.cmdtype != CMDBUILTIN || !(IS_BUILTIN_REGULAR(cmdentry.u.cmd)) ) { - find_command(argv[0], &cmdentry, cmd_flag | DO_ERR, - path ? path : pathval()); + path = path ? path : pathval(); + find_command(argv[0], &cmdentry, cmd_flag | DO_ERR, path); } jp = NULL; @@ -10385,17 +10384,10 @@ evalcommand(union node *cmd, int flags) FORCE_INT_ON; /* 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 */ case CMDBUILTIN: - if (spclbltin > 0 || argc == 0) { - poplocalvars(1); - if (cmd_is_exec && argc > 1) - listsetvar(varlist.list, VEXPORT); - } if (evalbltin(cmdentry.u.cmd, argc, argv, flags) && !(exception_type == EXERROR && spclbltin <= 0) ) { @@ -10418,7 +10410,8 @@ evalcommand(union node *cmd, int flags) popredir(/*drop:*/ cmd_is_exec); unwindredir(redir_stop); unwindfiles(file_stop); - unwindlocalvars(localvar_stop); + if (vlocal) + unwindlocalvars(localvar_stop); if (lastarg) { /* dsl: I think this is intended to be used to support * '_' in 'vi' command mode during line editing... -- cgit v1.2.3-55-g6feb From e2dd2afc8e4dbcf1061818adc68d2e74a1fa64d3 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 20 Feb 2020 10:33:38 +0100 Subject: ash: eval: Always set localvar_stop Upstream commit: Date: Thu, 31 May 2018 01:15:34 +0800 eval: Always set localvar_stop The variable localvar_stop is set iff vlocal is true. gcc doesn't get this so we get a spurious warning. This patch fixes this by always calling pushlocalvars with vlocal and making it only actually do the push if vlocal is non-zero. Signed-off-by: Herbert Xu Signed-off-by: Denys Vlasenko --- shell/ash.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'shell') diff --git a/shell/ash.c b/shell/ash.c index 5bb10e5cb..81d2422d6 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -9722,18 +9722,23 @@ poplocalvars(int keep) * Create a new localvar environment. */ static struct localvar_list * -pushlocalvars(void) +pushlocalvars(int push) { struct localvar_list *ll; + struct localvar_list *top; + + top = localvar_stack; + if (!push) + goto out; INT_OFF; ll = ckzalloc(sizeof(*ll)); /*ll->lv = NULL; - zalloc did it */ - ll->next = localvar_stack; + ll->next = top; localvar_stack = ll; INT_ON; - - return ll->next; + out: + return top; } static void @@ -10217,6 +10222,8 @@ evalcommand(union node *cmd, int flags) vflags = VEXPORT; } + localvar_stop = pushlocalvars(vlocal); + /* Reserve one extra spot at the front for shellexec. */ nargv = stalloc(sizeof(char *) * (argc + 2)); argv = ++nargv; @@ -10245,7 +10252,6 @@ evalcommand(union node *cmd, int flags) status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2); if (status) { - vlocal = 0; bail: exitstatus = status; @@ -10256,10 +10262,6 @@ evalcommand(union node *cmd, int flags) goto out; } - localvar_stop = NULL; - if (vlocal) - localvar_stop = pushlocalvars(); - for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) { struct strlist **spp; @@ -10410,8 +10412,7 @@ evalcommand(union node *cmd, int flags) popredir(/*drop:*/ cmd_is_exec); unwindredir(redir_stop); unwindfiles(file_stop); - if (vlocal) - unwindlocalvars(localvar_stop); + unwindlocalvars(localvar_stop); if (lastarg) { /* dsl: I think this is intended to be used to support * '_' in 'vi' command mode during line editing... -- cgit v1.2.3-55-g6feb