summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjilles <jilles@FreeBSD.org>2016-01-10 16:31:28 +0000
committerjilles <jilles@FreeBSD.org>2016-01-10 16:31:28 +0000
commitd42a26ab2089d53213cf28c22508023ea16e7efb (patch)
tree0b136c34e36d2b2c085d1618158aaa9df9b91c19
parent21632a9bd96fd68b505602bfdd4850ab1e18574a (diff)
downloadFreeBSD-src-d42a26ab2089d53213cf28c22508023ea16e7efb.zip
FreeBSD-src-d42a26ab2089d53213cf28c22508023ea16e7efb.tar.gz
sh: Update associated state when restoring locals while leaving a function.
Some variables like PATH call a function when modified. Make sure to call this also when leaving a function where such a variable was made local. Make sure to restore local variables before shellparam, so getopts state is not clobbered.
-rw-r--r--bin/sh/eval.c4
-rw-r--r--bin/sh/tests/builtins/Makefile1
-rw-r--r--bin/sh/tests/builtins/local5.015
-rw-r--r--bin/sh/var.c11
4 files changed, 29 insertions, 2 deletions
diff --git a/bin/sh/eval.c b/bin/sh/eval.c
index 5a3f8e7..949157d 100644
--- a/bin/sh/eval.c
+++ b/bin/sh/eval.c
@@ -1039,12 +1039,12 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
reffunc(cmdentry.u.func);
savehandler = handler;
if (setjmp(jmploc.loc)) {
- freeparam(&shellparam);
- shellparam = saveparam;
popredir();
unreffunc(cmdentry.u.func);
poplocalvars();
localvars = savelocalvars;
+ freeparam(&shellparam);
+ shellparam = saveparam;
funcnest--;
handler = savehandler;
longjmp(handler->loc, 1);
diff --git a/bin/sh/tests/builtins/Makefile b/bin/sh/tests/builtins/Makefile
index 11240ca..1511f70 100644
--- a/bin/sh/tests/builtins/Makefile
+++ b/bin/sh/tests/builtins/Makefile
@@ -111,6 +111,7 @@ FILES+= local1.0
FILES+= local2.0
FILES+= local3.0
FILES+= local4.0
+FILES+= local5.0
.if ${MK_NLS} != "no"
FILES+= locale1.0
.endif
diff --git a/bin/sh/tests/builtins/local5.0 b/bin/sh/tests/builtins/local5.0
new file mode 100644
index 0000000..2f2a14e
--- /dev/null
+++ b/bin/sh/tests/builtins/local5.0
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+f() {
+ local PATH IFS elem
+ IFS=:
+ for elem in ''$PATH''; do
+ PATH=/var/empty/$elem:$PATH
+ done
+ ls -d / >/dev/null
+}
+
+p1=$(command -v ls)
+f
+p2=$(command -v ls)
+[ "$p1" = "$p2" ]
diff --git a/bin/sh/var.c b/bin/sh/var.c
index d401361..3af7dbe 100644
--- a/bin/sh/var.c
+++ b/bin/sh/var.c
@@ -791,6 +791,7 @@ poplocalvars(void)
{
struct localvar *lvp;
struct var *vp;
+ int islocalevar;
INTOFF;
while ((lvp = localvars) != NULL) {
@@ -803,10 +804,20 @@ poplocalvars(void)
} else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
(void)unsetvar(vp->text);
} else {
+ islocalevar = (vp->flags | lvp->flags) & VEXPORT &&
+ localevar(lvp->text);
if ((vp->flags & VTEXTFIXED) == 0)
ckfree(vp->text);
vp->flags = lvp->flags;
vp->text = lvp->text;
+ if (vp->func)
+ (*vp->func)(vp->text + vp->name_len + 1);
+ if (islocalevar) {
+ change_env(vp->text, vp->flags & VEXPORT &&
+ (vp->flags & VUNSET) == 0);
+ setlocale(LC_ALL, "");
+ updatecharset();
+ }
}
ckfree(lvp);
}
OpenPOWER on IntegriCloud