NEWS.stable: log cherry-pick [e446f25]->[c092018] relocatable-shell: Update suggested...
[gnulib.git] / lib / putenv.c
index 6acefaa..b9f5079 100644 (file)
@@ -1,11 +1,12 @@
-/* Copyright (C) 1991, 1994, 1997, 1998, 2000, 2003, 2004 Free Software Foundation, Inc.
+/* Copyright (C) 1991, 1994, 1997-1998, 2000, 2003-2014 Free Software
+   Foundation, Inc.
 
    NOTE: The canonical source of this file is maintained with the GNU C
    Library.  Bugs can be reported to bug-glibc@prep.ai.mit.edu.
 
 
    NOTE: The canonical source of this file is maintained with the GNU C
    Library.  Bugs can be reported to bug-glibc@prep.ai.mit.edu.
 
-   This program is free software; you can redistribute it and/or modify it
+   This program is free software: you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the
    under the terms of the GNU General Public License as published by the
-   Free Software Foundation; either version 2, or (at your option) any
+   Free Software Foundation; either version 3 of the License, or any
    later version.
 
    This program is distributed in the hope that it will be useful,
    later version.
 
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software Foundation,
-   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
+#include <config.h>
+
+/* Specification.  */
+#include <stdlib.h>
 
 #include <stddef.h>
 
 
 #include <stddef.h>
 
 # define __set_errno(ev) ((errno) = (ev))
 #endif
 
 # define __set_errno(ev) ((errno) = (ev))
 #endif
 
-/* Don't include stdlib.h because some (e.g., Solaris 7) declare putenv
-   with a non-const argument.  That would conflict with the declaration of
-   rpl_putenv below (due to the #define putenv rpl_putenv from config.h).  */
-
-void *malloc ();
-void free ();
-
 #include <string.h>
 #include <string.h>
+#include <unistd.h>
 
 
-#if defined (__GNU_LIBRARY__) || defined (HAVE_UNISTD_H)
-# include <unistd.h>
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
 #endif
 
 #endif
 
-#if HAVE_GNU_LD
-# define environ __environ
-#else
+#if _LIBC
+# if HAVE_GNU_LD
+#  define environ __environ
+# else
 extern char **environ;
 extern char **environ;
+# endif
 #endif
 
 #if _LIBC
 #endif
 
 #if _LIBC
-/* This lock protects against simultaneous modifications of `environ'.  */
+/* This lock protects against simultaneous modifications of 'environ'.  */
 # include <bits/libc-lock.h>
 __libc_lock_define_initialized (static, envlock)
 # include <bits/libc-lock.h>
 __libc_lock_define_initialized (static, envlock)
-# define LOCK  __libc_lock_lock (envlock)
-# define UNLOCK        __libc_lock_unlock (envlock)
+# define LOCK   __libc_lock_lock (envlock)
+# define UNLOCK __libc_lock_unlock (envlock)
 #else
 # define LOCK
 # define UNLOCK
 #endif
 
 static int
 #else
 # define LOCK
 # define UNLOCK
 #endif
 
 static int
-unsetenv (const char *name)
+_unsetenv (const char *name)
 {
   size_t len;
 {
   size_t len;
+#if !HAVE_DECL__PUTENV
   char **ep;
   char **ep;
+#endif
 
   if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
     {
 
   if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
     {
@@ -74,19 +74,34 @@ unsetenv (const char *name)
 
   len = strlen (name);
 
 
   len = strlen (name);
 
+#if HAVE_DECL__PUTENV
+  {
+    int putenv_result, putenv_errno;
+    char *name_ = malloc (len + 2);
+    memcpy (name_, name, len);
+    name_[len] = '=';
+    name_[len + 1] = 0;
+    putenv_result = _putenv (name_);
+    putenv_errno = errno;
+    free (name_);
+    __set_errno (putenv_errno);
+    return putenv_result;
+  }
+#else
+
   LOCK;
 
   ep = environ;
   while (*ep != NULL)
     if (!strncmp (*ep, name, len) && (*ep)[len] == '=')
       {
   LOCK;
 
   ep = environ;
   while (*ep != NULL)
     if (!strncmp (*ep, name, len) && (*ep)[len] == '=')
       {
-       /* Found it.  Remove this pointer by moving later ones back.  */
-       char **dp = ep;
+        /* Found it.  Remove this pointer by moving later ones back.  */
+        char **dp = ep;
 
 
-       do
-         dp[0] = dp[1];
-       while (*dp++);
-       /* Continue the loop in case NAME appears again.  */
+        do
+          dp[0] = dp[1];
+        while (*dp++);
+        /* Continue the loop in case NAME appears again.  */
       }
     else
       ++ep;
       }
     else
       ++ep;
@@ -94,49 +109,86 @@ unsetenv (const char *name)
   UNLOCK;
 
   return 0;
   UNLOCK;
 
   return 0;
+#endif
 }
 
 
 /* Put STRING, which is of the form "NAME=VALUE", in the environment.
 }
 
 
 /* Put STRING, which is of the form "NAME=VALUE", in the environment.
-   If STRING contains no `=', then remove STRING from the environment.  */
+   If STRING contains no '=', then remove STRING from the environment.  */
 int
 int
-rpl_putenv (const char *string)
+putenv (char *string)
 {
 {
-  const char *const name_end = strchr (string, '=');
-  register size_t size;
-  register char **ep;
+  const char *name_end = strchr (string, '=');
+  char **ep;
 
   if (name_end == NULL)
     {
       /* Remove the variable from the environment.  */
 
   if (name_end == NULL)
     {
       /* Remove the variable from the environment.  */
-      return unsetenv (string);
+      return _unsetenv (string);
     }
 
     }
 
-  size = 0;
-  for (ep = environ; *ep != NULL; ++ep)
-    if (!strncmp (*ep, string, name_end - string) &&
-       (*ep)[name_end - string] == '=')
+#if HAVE_DECL__PUTENV
+  /* Rely on _putenv to allocate the new environment.  If other
+     parts of the application use _putenv, the !HAVE_DECL__PUTENV code
+     would fight over who owns the environ vector, causing a crash.  */
+  if (name_end[1])
+    return _putenv (string);
+  else
+    {
+      /* _putenv ("NAME=") unsets NAME, so invoke _putenv ("NAME= ")
+         to allocate the environ vector and then replace the new
+         entry with "NAME=".  */
+      int putenv_result, putenv_errno;
+      char *name_x = malloc (name_end - string + sizeof "= ");
+      if (!name_x)
+        return -1;
+      memcpy (name_x, string, name_end - string + 1);
+      name_x[name_end - string + 1] = ' ';
+      name_x[name_end - string + 2] = 0;
+      putenv_result = _putenv (name_x);
+      putenv_errno = errno;
+      for (ep = environ; *ep; ep++)
+        if (strcmp (*ep, name_x) == 0)
+          {
+            *ep = string;
+            break;
+          }
+# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+      if (putenv_result == 0)
+        {
+          /* _putenv propagated "NAME= " into the subprocess environment;
+             fix that by calling SetEnvironmentVariable directly.  */
+          name_x[name_end - string] = 0;
+          putenv_result = SetEnvironmentVariable (name_x, "") ? 0 : -1;
+          putenv_errno = ENOMEM; /* ENOMEM is the only way to fail.  */
+        }
+# endif
+      free (name_x);
+      __set_errno (putenv_errno);
+      return putenv_result;
+    }
+#else
+  for (ep = environ; *ep; ep++)
+    if (strncmp (*ep, string, name_end - string) == 0
+        && (*ep)[name_end - string] == '=')
       break;
       break;
-    else
-      ++size;
 
 
-  if (*ep == NULL)
+  if (*ep)
+    *ep = string;
+  else
     {
       static char **last_environ = NULL;
     {
       static char **last_environ = NULL;
-      char **new_environ = (char **) malloc ((size + 2) * sizeof (char *));
-      if (new_environ == NULL)
-       return -1;
-      (void) memcpy ((void *) new_environ, (void *) environ,
-                    size * sizeof (char *));
-      new_environ[size] = (char *) string;
-      new_environ[size + 1] = NULL;
-      if (last_environ != NULL)
-       free (last_environ);
+      size_t size = ep - environ;
+      char **new_environ = malloc ((size + 2) * sizeof *new_environ);
+      if (! new_environ)
+        return -1;
+      new_environ[0] = string;
+      memcpy (new_environ + 1, environ, (size + 1) * sizeof *new_environ);
+      free (last_environ);
       last_environ = new_environ;
       environ = new_environ;
     }
       last_environ = new_environ;
       environ = new_environ;
     }
-  else
-    *ep = (char *) string;
 
   return 0;
 
   return 0;
+#endif
 }
 }