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 \