diff -u Src/exec.c Src/exec.c
--- Src/exec.c	2007-02-14 10:11:19.000000000 -0600
+++ Src/exec.c	2007-07-31 08:49:13.000000000 -0500
@@ -501,7 +501,16 @@
      * that as argv[0] for this external command       */
     if (unset(RESTRICTED) && (z = zgetenv("ARGV0"))) {
 	setdata(firstnode(args), (void *) ztrdup(z));
+	/*
+	 * Note we don't do anything with the parameter structure
+	 * for ARGV0: that's OK since we're about to exec or exit
+	 * on failure.
+	 */
+#ifdef USE_SET_UNSET_ENV
+	unsetenv("ARGV0");
+#else
 	delenvvalue(z - 6);
+#endif
     } else if (dash) {
     /* Else if the pre-command `-' was given, we add `-' *
      * to the front of argv[0] for this command.         */
diff -u Src/params.c Src/params.c
--- Src/params.c	2007-04-13 06:40:27.000000000 -0500
+++ Src/params.c	2007-07-31 08:49:13.000000000 -0500
@@ -606,7 +606,7 @@
 createparamtable(void)
 {
     Param ip, pm;
-#ifndef HAVE_PUTENV
+#if !defined(HAVE_PUTENV) && !defined(USE_SET_UNSET_ENV)
     char **new_environ;
     int  envsize;
 #endif
@@ -661,7 +661,7 @@
 
     setsparam("LOGNAME", ztrdup((str = getlogin()) && *str ? str : cached_username));
 
-#ifndef HAVE_PUTENV
+#if !defined(HAVE_PUTENV) && !defined(USE_SET_UNSET_ENV)
     /* Copy the environment variables we are inheriting to dynamic *
      * memory, so we can do mallocs and frees on it.               */
     envsize = sizeof(char *)*(1 + arrlen(environ));
@@ -3727,6 +3727,30 @@
 int
 zputenv(char *str)
 {
+#ifdef USE_SET_UNSET_ENV
+    /*
+     * If we are using unsetenv() to remove values from the
+     * environment, which is the safe thing to do, we
+     * need to use setenv() to put them there in the first place.
+     * Unfortunately this is a slightly different interface
+     * from what zputenv() assumes.
+     */
+    char *ptr;
+    int ret;
+
+    for (ptr = str; *ptr && *ptr != '='; ptr++)
+	;
+    if (*ptr) {
+	*ptr = '\0';
+	ret = setenv(str, ptr+1, 1);
+	*ptr = '=';
+    } else {
+	/* safety first */
+	DPUTS(1, "bad environment string");
+	ret = setenv(str, ptr, 1);
+    }
+    return ret;
+#else
 #ifdef HAVE_PUTENV
     return putenv(str);
 #else
@@ -3750,9 +3774,12 @@
     }
     return 0;
 #endif
+#endif
 }
 
 /**/
+#ifndef USE_SET_UNSET_ENV
+/**/
 static int
 findenv(char *name, int *pos)
 {
@@ -3771,6 +3798,8 @@
     
     return 0;
 }
+/**/
+#endif
 
 /* Given *name = "foo", it searches the environment for string *
  * "foo=bar", and returns a pointer to the beginning of "bar"  */
@@ -3811,14 +3840,18 @@
 void
 addenv(Param pm, char *value)
 {
-    char *oldenv = 0, *newenv = 0, *env = 0;
+    char *newenv = 0;
+#ifndef USE_SET_UNSET_ENV
+    char *oldenv = 0, *env = 0;
     int pos;
 
-    /* First check if there is already an environment *
-     * variable matching string `name'. If not, and   *
-     * we are not requested to add new, return        */
+    /*
+     * First check if there is already an environment
+     * variable matching string `name'.
+     */
     if (findenv(pm->node.nam, &pos))
 	oldenv = environ[pos];
+#endif
 
      newenv = mkenvstr(pm->node.nam, value, pm->node.flags);
      if (zputenv(newenv)) {
@@ -3826,6 +3859,19 @@
 	pm->env = NULL;
 	return;
     }
+#ifdef USE_SET_UNSET_ENV
+     /*
+      * If we are using setenv/unsetenv to manage the environment,
+      * we simply store the string we created in pm->env since
+      * memory management of the environment is handled entirely
+      * by the system.
+      *
+      * TODO: is this good enough to fix problem cases from
+      * the other branch?  If so, we don't actually need to
+      * store pm->env at all, just a flag that the value was set.
+      */
+     pm->env = newenv;
+#else
     /*
      * Under Cygwin we must use putenv() to maintain consistency.
      * Unfortunately, current version (1.1.2) copies argument and may
@@ -3845,6 +3891,7 @@
 
     DPUTS(1, "addenv should never reach the end");
     pm->env = NULL;
+#endif
 }
 
 
@@ -3875,6 +3922,7 @@
  * string.                                         */
 
 
+#ifndef USE_SET_UNSET_ENV
 /**/
 void
 delenvvalue(char *x)
@@ -3890,6 +3938,8 @@
     }
     zsfree(x);
 }
+#endif
+
 
 /* Delete a pointer from the list of pointers to environment *
  * variables by shifting all the other pointers up one slot. */
@@ -3898,7 +3948,12 @@
 void
 delenv(Param pm)
 {
+#ifdef USE_SET_UNSET_ENV
+    unsetenv(pm->node.nam);
+    zsfree(pm->env);
+#else
     delenvvalue(pm->env);
+#endif
     pm->env = NULL;
     /*
      * Note we don't remove PM_EXPORT from the flags.  This
diff -u Src/system.h Src/system.h
--- Src/system.h	2007-04-13 05:11:31.000000000 -0500
+++ Src/system.h	2007-07-31 08:49:13.000000000 -0500
@@ -693,6 +693,15 @@
 
 extern char **environ;
 
+/*
+ * We always need setenv and unsetenv in pairs, because
+ * we don't know how to do memory management on the values set.
+ */
+#if defined(HAVE_SETENV) && defined(HAVE_UNSETENV)
+# define USE_SET_UNSET_ENV
+#endif
+
+
 /* These variables are sometimes defined in, *
  * and needed by, the termcap library.       */
 #if MUST_DEFINE_OSPEED
diff -u Test/B02typeset.ztst Test/B02typeset.ztst
--- Test/B02typeset.ztst	2006-06-26 13:17:32.000000000 -0500
+++ Test/B02typeset.ztst	2007-07-31 08:49:13.000000000 -0500
@@ -379,3 +379,31 @@
 >integer local i
 >local tagged scalar
 >preserved
+
+ export ENVFOO=bar
+ print ENVFOO in environment
+ env | grep '^ENVFOO'
+ print Changing ENVFOO
+ ENVFOO="not bar any more"
+ env | grep '^ENVFOO'
+ unset ENVFOO
+ print ENVFOO no longer in environment
+ env | grep '^ENVFOO'
+1:Adding and removing values to and from the environment
+>ENVFOO in environment
+>ENVFOO=bar
+>Changing ENVFOO
+>ENVFOO=not bar any more
+>ENVFOO no longer in environment
+
+ (export FOOENV=BAR
+ env | grep '^FOOENV'
+ print Exec
+ exec $ZTST_testdir/../Src/zsh -c '
+ print Unset
+ unset FOOENV
+ env | grep "^FOOENV"')
+1:Can unset environment variables after exec
+>FOOENV=BAR
+>Exec
+>Unset
diff -u configure configure
--- configure	2007-01-18 10:33:17.000000000 -0600
+++ configure	2007-07-31 08:49:06.000000000 -0500
@@ -10263,7 +10263,7 @@
 	       setlocale \
 	       uname \
 	       signgam \
-	       putenv getenv \
+	       putenv getenv setenv unsetenv xw \
 	       brk sbrk \
 	       pathconf sysconf \
 	       tgetent tigetflag tigetnum tigetstr setupterm \
diff -u configure.ac configure.ac
--- configure.ac	2007-01-05 07:58:04.000000000 -0600
+++ configure.ac	2007-07-31 08:49:06.000000000 -0500
@@ -1126,7 +1126,7 @@
 	       setlocale \
 	       uname \
 	       signgam \
-	       putenv getenv \
+	       putenv getenv setenv unsetenv xw\
 	       brk sbrk \
 	       pathconf sysconf \
 	       tgetent tigetflag tigetnum tigetstr setupterm \
