aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorGravatar Denys Vlasenko <vda.linux@googlemail.com>2015-04-18 19:36:38 +0200
committerGravatar Denys Vlasenko <vda.linux@googlemail.com>2015-04-18 19:36:38 +0200
commit0a0acb55db8d7c4dec445573f1b0528d126b9e1f (patch)
tree6947e42871e5360c8ff07e57127e1f65cfc08e6e /shell
parent63f4d32c9859c1ed341debefddad4b9c0ae944cc (diff)
downloadbusybox-0a0acb55db8d7c4dec445573f1b0528d126b9e1f.tar.gz
busybox-0a0acb55db8d7c4dec445573f1b0528d126b9e1f.tar.bz2
ash: fix handling of duplicate "local"
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'shell')
-rw-r--r--shell/ash.c51
-rw-r--r--shell/ash_test/ash-heredoc/heredoc1.right2
-rw-r--r--shell/ash_test/ash-vars/var3.right5
-rwxr-xr-xshell/ash_test/ash-vars/var3.tests1
4 files changed, 42 insertions, 17 deletions
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