summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/eval.c7
-rw-r--r--src/var.c50
-rw-r--r--src/var.h1
3 files changed, 47 insertions, 11 deletions
diff --git a/src/eval.c b/src/eval.c
index 62d9d5d..8d2767c 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -928,20 +928,17 @@ STATIC int
 evalfun(struct funcnode *func, int argc, char **argv, int flags)
 {
 	volatile struct shparam saveparam;
-	struct localvar *volatile savelocalvars;
 	struct jmploc *volatile savehandler;
 	struct jmploc jmploc;
 	int e;
 
 	saveparam = shellparam;
-	savelocalvars = localvars;
 	if ((e = setjmp(jmploc.loc))) {
 		goto funcdone;
 	}
 	INTOFF;
 	savehandler = handler;
 	handler = &jmploc;
-	localvars = NULL;
 	shellparam.malloc = 0;
 	func->count++;
 	funcnest++;
@@ -950,13 +947,13 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
 	shellparam.p = argv + 1;
 	shellparam.optind = 1;
 	shellparam.optoff = -1;
+	pushlocalvars();
 	evaltree(&func->n, flags & EV_TESTED);
+	poplocalvars();
 funcdone:
 	INTOFF;
 	funcnest--;
 	freefunc(func);
-	poplocalvars();
-	localvars = savelocalvars;
 	freeparam(&shellparam);
 	shellparam = saveparam;
 	handler = savehandler;
diff --git a/src/var.c b/src/var.c
index 2737fb1..de1a5f5 100644
--- a/src/var.c
+++ b/src/var.c
@@ -64,7 +64,12 @@
 #define VTABSIZE 39
 
 
-struct localvar *localvars;
+struct localvar_list {
+	struct localvar_list *next;
+	struct localvar *lv;
+};
+
+MKINIT struct localvar_list *localvar_stack;
 
 const char defpathvar[] =
 	"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
@@ -139,6 +144,11 @@ INIT {
 			p = 0;
 	setpwd(p, 0);
 }
+
+RESET {
+	while (localvar_stack)
+		poplocalvars();
+}
 #endif
 
 
@@ -446,6 +456,9 @@ localcmd(int argc, char **argv)
 {
 	char *name;
 
+	if (!localvar_stack)
+		sh_error("not in a function");
+
 	argv = argptr;
 	while ((name = *argv++) != NULL) {
 		mklocal(name);
@@ -497,8 +510,8 @@ mklocal(char *name)
 		}
 	}
 	lvp->vp = vp;
-	lvp->next = localvars;
-	localvars = lvp;
+	lvp->next = localvar_stack->lv;
+	localvar_stack->lv = lvp;
 	INTON;
 }
 
@@ -511,11 +524,19 @@ mklocal(char *name)
 void
 poplocalvars(void)
 {
-	struct localvar *lvp;
+	struct localvar_list *ll;
+	struct localvar *lvp, *next;
 	struct var *vp;
 
-	while ((lvp = localvars) != NULL) {
-		localvars = lvp->next;
+	INTOFF;
+	ll = localvar_stack;
+	localvar_stack = ll->next;
+
+	next = ll->lv;
+	ckfree(ll);
+
+	while ((lvp = next) != NULL) {
+		next = lvp->next;
 		vp = lvp->vp;
 		TRACE(("poplocalvar %s", vp ? vp->text : "-"));
 		if (vp == NULL) {	/* $- saved */
@@ -534,6 +555,23 @@ poplocalvars(void)
 		}
 		ckfree(lvp);
 	}
+	INTON;
+}
+
+
+/*
+ * Create a new localvar environment.
+ */
+void pushlocalvars(void)
+{
+	struct localvar_list *ll;
+
+	INTOFF;
+	ll = ckmalloc(sizeof(*ll));
+	ll->lv = NULL;
+	ll->next = localvar_stack;
+	localvar_stack = ll;
+	INTON;
 }
 
 
diff --git a/src/var.h b/src/var.h
index e4e2cff..32b0dde 100644
--- a/src/var.h
+++ b/src/var.h
@@ -139,6 +139,7 @@ char **listvars(int, int, char ***);
 int showvars(const char *, int, int);
 int exportcmd(int, char **);
 int localcmd(int, char **);
+void pushlocalvars(void);
 void poplocalvars(void);
 int unsetcmd(int, char **);
 int unsetvar(const char *);