ash: fix handling of duplicate "local"
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
63f4d32c98
commit
0a0acb55db
51
shell/ash.c
51
shell/ash.c
@ -2030,7 +2030,7 @@ varcmp(const char *p, const char *q)
|
|||||||
int c, d;
|
int c, d;
|
||||||
|
|
||||||
while ((c = *p) == (d = *q)) {
|
while ((c = *p) == (d = *q)) {
|
||||||
if (!c || c == '=')
|
if (c == '\0' || c == '=')
|
||||||
goto out;
|
goto out;
|
||||||
p++;
|
p++;
|
||||||
q++;
|
q++;
|
||||||
@ -2247,7 +2247,7 @@ setvar(const char *name, const char *val, int flags)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void FAST_FUNC
|
static void FAST_FUNC
|
||||||
setvar2(const char *name, const char *val)
|
setvar0(const char *name, const char *val)
|
||||||
{
|
{
|
||||||
setvar(name, val, 0);
|
setvar(name, val, 0);
|
||||||
}
|
}
|
||||||
@ -2310,7 +2310,7 @@ unsetvar(const char *s)
|
|||||||
free(vp);
|
free(vp);
|
||||||
INT_ON;
|
INT_ON;
|
||||||
} else {
|
} else {
|
||||||
setvar2(s, 0);
|
setvar0(s, NULL);
|
||||||
vp->flags &= ~VEXPORT;
|
vp->flags &= ~VEXPORT;
|
||||||
}
|
}
|
||||||
ok:
|
ok:
|
||||||
@ -5505,7 +5505,7 @@ ash_arith(const char *s)
|
|||||||
arith_t result;
|
arith_t result;
|
||||||
|
|
||||||
math_state.lookupvar = lookupvar;
|
math_state.lookupvar = lookupvar;
|
||||||
math_state.setvar = setvar2;
|
math_state.setvar = setvar0;
|
||||||
//math_state.endofname = endofname;
|
//math_state.endofname = endofname;
|
||||||
|
|
||||||
INT_OFF;
|
INT_OFF;
|
||||||
@ -6360,7 +6360,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
|
|||||||
|
|
||||||
switch (subtype) {
|
switch (subtype) {
|
||||||
case VSASSIGN:
|
case VSASSIGN:
|
||||||
setvar2(varname, startp);
|
setvar0(varname, startp);
|
||||||
amount = startp - expdest;
|
amount = startp - expdest;
|
||||||
STADJUST(amount, expdest);
|
STADJUST(amount, expdest);
|
||||||
return startp;
|
return startp;
|
||||||
@ -8591,7 +8591,7 @@ evalfor(union node *n, int flags)
|
|||||||
loopnest++;
|
loopnest++;
|
||||||
flags &= EV_TESTED;
|
flags &= EV_TESTED;
|
||||||
for (sp = arglist.list; sp; sp = sp->next) {
|
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);
|
evaltree(n->nfor.body, flags);
|
||||||
if (evalskip) {
|
if (evalskip) {
|
||||||
if (evalskip == SKIPCONT && --skipcount <= 0) {
|
if (evalskip == SKIPCONT && --skipcount <= 0) {
|
||||||
@ -8970,21 +8970,37 @@ mklocal(char *name)
|
|||||||
struct localvar *lvp;
|
struct localvar *lvp;
|
||||||
struct var **vpp;
|
struct var **vpp;
|
||||||
struct var *vp;
|
struct var *vp;
|
||||||
|
char *eq = strchr(name, '=');
|
||||||
|
|
||||||
INT_OFF;
|
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)) {
|
if (LONE_DASH(name)) {
|
||||||
char *p;
|
char *p;
|
||||||
p = ckmalloc(sizeof(optlist));
|
p = ckmalloc(sizeof(optlist));
|
||||||
lvp->text = memcpy(p, optlist, sizeof(optlist));
|
lvp->text = memcpy(p, optlist, sizeof(optlist));
|
||||||
vp = NULL;
|
vp = NULL;
|
||||||
} else {
|
} else {
|
||||||
char *eq;
|
|
||||||
|
|
||||||
vpp = hashvar(name);
|
vpp = hashvar(name);
|
||||||
vp = *findvar(vpp, name);
|
vp = *findvar(vpp, name);
|
||||||
eq = strchr(name, '=');
|
|
||||||
if (vp == NULL) {
|
if (vp == NULL) {
|
||||||
|
/* variable did not exist yet */
|
||||||
if (eq)
|
if (eq)
|
||||||
setvareq(name, VSTRFIXED);
|
setvareq(name, VSTRFIXED);
|
||||||
else
|
else
|
||||||
@ -8994,12 +9010,15 @@ mklocal(char *name)
|
|||||||
} else {
|
} else {
|
||||||
lvp->text = vp->var_text;
|
lvp->text = vp->var_text;
|
||||||
lvp->flags = vp->flags;
|
lvp->flags = vp->flags;
|
||||||
|
/* make sure neither "struct var" nor string gets freed
|
||||||
|
* during (un)setting:
|
||||||
|
*/
|
||||||
vp->flags |= VSTRFIXED|VTEXTFIXED;
|
vp->flags |= VSTRFIXED|VTEXTFIXED;
|
||||||
if (eq)
|
if (eq)
|
||||||
setvareq(name, 0);
|
setvareq(name, 0);
|
||||||
else
|
else
|
||||||
/* "local VAR" unsets VAR: */
|
/* "local VAR" unsets VAR: */
|
||||||
setvar(name, NULL, 0);
|
setvar0(name, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lvp->vp = vp;
|
lvp->vp = vp;
|
||||||
@ -9491,7 +9510,7 @@ evalcommand(union node *cmd, int flags)
|
|||||||
* '_' in 'vi' command mode during line editing...
|
* '_' in 'vi' command mode during line editing...
|
||||||
* However I implemented that within libedit itself.
|
* However I implemented that within libedit itself.
|
||||||
*/
|
*/
|
||||||
setvar2("_", lastarg);
|
setvar0("_", lastarg);
|
||||||
}
|
}
|
||||||
popstackmark(&smark);
|
popstackmark(&smark);
|
||||||
}
|
}
|
||||||
@ -12885,7 +12904,7 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
|
|||||||
* to jump out of it.
|
* to jump out of it.
|
||||||
*/
|
*/
|
||||||
INT_OFF;
|
INT_OFF;
|
||||||
r = shell_builtin_read(setvar2,
|
r = shell_builtin_read(setvar0,
|
||||||
argptr,
|
argptr,
|
||||||
bltinlookup("IFS"), /* can be NULL */
|
bltinlookup("IFS"), /* can be NULL */
|
||||||
read_flags,
|
read_flags,
|
||||||
@ -13046,14 +13065,14 @@ init(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setvar2("PPID", utoa(getppid()));
|
setvar0("PPID", utoa(getppid()));
|
||||||
#if ENABLE_ASH_BASH_COMPAT
|
#if ENABLE_ASH_BASH_COMPAT
|
||||||
p = lookupvar("SHLVL");
|
p = lookupvar("SHLVL");
|
||||||
setvar("SHLVL", utoa((p ? atoi(p) : 0) + 1), VEXPORT);
|
setvar("SHLVL", utoa((p ? atoi(p) : 0) + 1), VEXPORT);
|
||||||
if (!lookupvar("HOSTNAME")) {
|
if (!lookupvar("HOSTNAME")) {
|
||||||
struct utsname uts;
|
struct utsname uts;
|
||||||
uname(&uts);
|
uname(&uts);
|
||||||
setvar2("HOSTNAME", uts.nodename);
|
setvar0("HOSTNAME", uts.nodename);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
p = lookupvar("PWD");
|
p = lookupvar("PWD");
|
||||||
@ -13309,7 +13328,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
|
|||||||
hp = lookupvar("HOME");
|
hp = lookupvar("HOME");
|
||||||
if (hp) {
|
if (hp) {
|
||||||
hp = concat_path_file(hp, ".ash_history");
|
hp = concat_path_file(hp, ".ash_history");
|
||||||
setvar2("HISTFILE", hp);
|
setvar0("HISTFILE", hp);
|
||||||
free((char*)hp);
|
free((char*)hp);
|
||||||
hp = lookupvar("HISTFILE");
|
hp = lookupvar("HISTFILE");
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
heredoc1.tests: line 3: syntax error: unexpected "then"
|
./heredoc1.tests: line 3: syntax error: unexpected "then"
|
||||||
|
5
shell/ash_test/ash-vars/var3.right
Normal file
5
shell/ash_test/ash-vars/var3.right
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
1
|
||||||
|
1
|
||||||
|
|
||||||
|
|
||||||
|
0
|
1
shell/ash_test/ash-vars/var3.tests
Executable file
1
shell/ash_test/ash-vars/var3.tests
Executable file
@ -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
|
Loading…
Reference in New Issue
Block a user