From 0a0acb55db8d7c4dec445573f1b0528d126b9e1f Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 18 Apr 2015 19:36:38 +0200 Subject: [PATCH] ash: fix handling of duplicate "local" Signed-off-by: Denys Vlasenko --- shell/ash.c | 51 ++++++++++++++++------- shell/ash_test/ash-heredoc/heredoc1.right | 2 +- shell/ash_test/ash-vars/var3.right | 5 +++ shell/ash_test/ash-vars/var3.tests | 1 + 4 files changed, 42 insertions(+), 17 deletions(-) create mode 100644 shell/ash_test/ash-vars/var3.right create mode 100755 shell/ash_test/ash-vars/var3.tests diff --git a/shell/ash.c b/shell/ash.c index b568013b4..697a64fea 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -2030,7 +2030,7 @@ varcmp(const char *p, const char *q) int c, d; while ((c = *p) == (d = *q)) { - if (!c || c == '=') + if (c == '\0' || c == '=') goto out; p++; q++; @@ -2247,7 +2247,7 @@ setvar(const char *name, const char *val, int flags) } static void FAST_FUNC -setvar2(const char *name, const char *val) +setvar0(const char *name, const char *val) { setvar(name, val, 0); } @@ -2310,7 +2310,7 @@ unsetvar(const char *s) free(vp); INT_ON; } else { - setvar2(s, 0); + setvar0(s, NULL); vp->flags &= ~VEXPORT; } ok: @@ -5505,7 +5505,7 @@ ash_arith(const char *s) arith_t result; math_state.lookupvar = lookupvar; - math_state.setvar = setvar2; + math_state.setvar = setvar0; //math_state.endofname = endofname; INT_OFF; @@ -6360,7 +6360,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype, switch (subtype) { case VSASSIGN: - setvar2(varname, startp); + setvar0(varname, startp); amount = startp - expdest; STADJUST(amount, expdest); return startp; @@ -8591,7 +8591,7 @@ evalfor(union node *n, int flags) loopnest++; flags &= EV_TESTED; for (sp = arglist.list; sp; sp = sp->next) { - setvar2(n->nfor.var, sp->text); + setvar0(n->nfor.var, sp->text); evaltree(n->nfor.body, flags); if (evalskip) { if (evalskip == SKIPCONT && --skipcount <= 0) { @@ -8970,21 +8970,37 @@ mklocal(char *name) struct localvar *lvp; struct var **vpp; struct var *vp; + char *eq = strchr(name, '='); INT_OFF; - lvp = ckzalloc(sizeof(struct localvar)); + /* Cater for duplicate "local". Examples: + * x=0; f() { local x=1; echo $x; local x; echo $x; }; f; echo $x + * x=0; f() { local x=1; echo $x; local x=2; echo $x; }; f; echo $x + */ + lvp = localvars; + while (lvp) { + if (varcmp(lvp->vp->var_text, name) == 0) { + if (eq) + setvareq(name, 0); + /* else: + * it's a duplicate "local VAR" declaration, do nothing + */ + return; + } + lvp = lvp->next; + } + + lvp = ckzalloc(sizeof(*lvp)); if (LONE_DASH(name)) { char *p; p = ckmalloc(sizeof(optlist)); lvp->text = memcpy(p, optlist, sizeof(optlist)); vp = NULL; } else { - char *eq; - vpp = hashvar(name); vp = *findvar(vpp, name); - eq = strchr(name, '='); if (vp == NULL) { + /* variable did not exist yet */ if (eq) setvareq(name, VSTRFIXED); else @@ -8994,12 +9010,15 @@ mklocal(char *name) } else { lvp->text = vp->var_text; lvp->flags = vp->flags; + /* make sure neither "struct var" nor string gets freed + * during (un)setting: + */ vp->flags |= VSTRFIXED|VTEXTFIXED; if (eq) setvareq(name, 0); else /* "local VAR" unsets VAR: */ - setvar(name, NULL, 0); + setvar0(name, NULL); } } lvp->vp = vp; @@ -9491,7 +9510,7 @@ evalcommand(union node *cmd, int flags) * '_' in 'vi' command mode during line editing... * However I implemented that within libedit itself. */ - setvar2("_", lastarg); + setvar0("_", lastarg); } popstackmark(&smark); } @@ -12885,7 +12904,7 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) * to jump out of it. */ INT_OFF; - r = shell_builtin_read(setvar2, + r = shell_builtin_read(setvar0, argptr, bltinlookup("IFS"), /* can be NULL */ read_flags, @@ -13046,14 +13065,14 @@ init(void) } } - setvar2("PPID", utoa(getppid())); + setvar0("PPID", utoa(getppid())); #if ENABLE_ASH_BASH_COMPAT p = lookupvar("SHLVL"); setvar("SHLVL", utoa((p ? atoi(p) : 0) + 1), VEXPORT); if (!lookupvar("HOSTNAME")) { struct utsname uts; uname(&uts); - setvar2("HOSTNAME", uts.nodename); + setvar0("HOSTNAME", uts.nodename); } #endif p = lookupvar("PWD"); @@ -13309,7 +13328,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv) hp = lookupvar("HOME"); if (hp) { hp = concat_path_file(hp, ".ash_history"); - setvar2("HISTFILE", hp); + setvar0("HISTFILE", hp); free((char*)hp); hp = lookupvar("HISTFILE"); } diff --git a/shell/ash_test/ash-heredoc/heredoc1.right b/shell/ash_test/ash-heredoc/heredoc1.right index 895f5ee80..40aa5a5fe 100644 --- a/shell/ash_test/ash-heredoc/heredoc1.right +++ b/shell/ash_test/ash-heredoc/heredoc1.right @@ -1 +1 @@ -heredoc1.tests: line 3: syntax error: unexpected "then" +./heredoc1.tests: line 3: syntax error: unexpected "then" diff --git a/shell/ash_test/ash-vars/var3.right b/shell/ash_test/ash-vars/var3.right new file mode 100644 index 000000000..8eb0e3337 --- /dev/null +++ b/shell/ash_test/ash-vars/var3.right @@ -0,0 +1,5 @@ +1 +1 + + +0 diff --git a/shell/ash_test/ash-vars/var3.tests b/shell/ash_test/ash-vars/var3.tests new file mode 100755 index 000000000..97b102cbe --- /dev/null +++ b/shell/ash_test/ash-vars/var3.tests @@ -0,0 +1 @@ +x=0; f() { local x=1; echo $x; local x; echo $x; unset x; echo $x; local x; echo $x; }; f; echo $x -- 2.25.1