ash: fix handling of duplicate "local"

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2015-04-18 19:36:38 +02:00
parent 63f4d32c98
commit 0a0acb55db
4 changed files with 42 additions and 17 deletions

View File

@ -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");
} }

View File

@ -1 +1 @@
heredoc1.tests: line 3: syntax error: unexpected "then" ./heredoc1.tests: line 3: syntax error: unexpected "then"

View File

@ -0,0 +1,5 @@
1
1
0

View 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