posix_spawn_file_actions_addclose: Work around Solaris 11 2011-11 bug.
[gnulib.git] / lib / putenv.c
index 58a498f..3c0f7ea 100644 (file)
@@ -1,11 +1,12 @@
-/* Copyright (C) 1991, 1994, 1997, 1998, 2000 Free Software Foundation, Inc.
+/* Copyright (C) 1991, 1994, 1997-1998, 2000, 2003-2012 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 
-#if HAVE_CONFIG_H
-# include <config.h>
-#endif
+#include <config.h>
+
+/* Specification.  */
+#include <stdlib.h>
 
 
-#include <sys/types.h>
+#include <stddef.h>
 
 /* Include errno.h *after* sys/types.h to work around header problems
    on AIX 3.2.5.  */
 #include <errno.h>
 
 /* Include errno.h *after* sys/types.h to work around header problems
    on AIX 3.2.5.  */
 #include <errno.h>
-
-/* Don't include stdlib.h because some (e.g., Solaris 2.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).  */
-
-char *malloc ();
-void free ();
-
-#if defined (__GNU_LIBRARY__) || defined (HAVE_STRING_H)
-# include <string.h>
-#endif
-#if defined (__GNU_LIBRARY__) || defined (HAVE_UNISTD_H)
-# include <unistd.h>
+#ifndef __set_errno
+# define __set_errno(ev) ((errno) = (ev))
 #endif
 
 #endif
 
-#if !defined (__GNU_LIBRARY__) && !defined (HAVE_STRCHR)
-# define strchr index
-#endif
-#if !defined (__GNU_LIBRARY__) && !defined (HAVE_MEMCPY)
-# define memcpy(d,s,n) bcopy ((s), (d), (n))
-#endif
+#include <string.h>
+#include <unistd.h>
 
 
-#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
 
 #endif
 
-#ifndef NULL
-# define NULL 0
+#if _LIBC
+/* This lock protects against simultaneous modifications of 'environ'.  */
+# include <bits/libc-lock.h>
+__libc_lock_define_initialized (static, envlock)
+# define LOCK   __libc_lock_lock (envlock)
+# define UNLOCK __libc_lock_unlock (envlock)
+#else
+# define LOCK
+# define UNLOCK
 #endif
 
 #endif
 
+static int
+_unsetenv (const char *name)
+{
+  size_t len;
+  char **ep;
+
+  if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
+    {
+      __set_errno (EINVAL);
+      return -1;
+    }
+
+  len = strlen (name);
+
+  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;
+
+        do
+          dp[0] = dp[1];
+        while (*dp++);
+        /* Continue the loop in case NAME appears again.  */
+      }
+    else
+      ++ep;
+
+  UNLOCK;
+
+  return 0;
+}
+
 
 
-/* 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.  */
 int
 int
-rpl_putenv (const char *string)
+putenv (char *string)
 {
   const char *const name_end = strchr (string, '=');
   register size_t size;
 {
   const char *const name_end = strchr (string, '=');
   register size_t size;
@@ -70,24 +102,13 @@ rpl_putenv (const char *string)
   if (name_end == NULL)
     {
       /* Remove the variable from the environment.  */
   if (name_end == NULL)
     {
       /* Remove the variable from the environment.  */
-      size = strlen (string);
-      for (ep = environ; *ep != NULL; ++ep)
-       if (!strncmp (*ep, string, size) && (*ep)[size] == '=')
-         {
-           while (ep[1] != NULL)
-             {
-               ep[0] = ep[1];
-               ++ep;
-             }
-           *ep = NULL;
-           return 0;
-         }
+      return _unsetenv (string);
     }
 
   size = 0;
   for (ep = environ; *ep != NULL; ++ep)
     if (!strncmp (*ep, string, name_end - string) &&
     }
 
   size = 0;
   for (ep = environ; *ep != NULL; ++ep)
     if (!strncmp (*ep, string, name_end - string) &&
-       (*ep)[name_end - string] == '=')
+        (*ep)[name_end - string] == '=')
       break;
     else
       ++size;
       break;
     else
       ++size;
@@ -97,18 +118,17 @@ rpl_putenv (const char *string)
       static char **last_environ = NULL;
       char **new_environ = (char **) malloc ((size + 2) * sizeof (char *));
       if (new_environ == NULL)
       static char **last_environ = NULL;
       char **new_environ = (char **) malloc ((size + 2) * sizeof (char *));
       if (new_environ == NULL)
-       return -1;
+        return -1;
       (void) memcpy ((void *) new_environ, (void *) environ,
       (void) memcpy ((void *) new_environ, (void *) environ,
-                    size * sizeof (char *));
+                     size * sizeof (char *));
       new_environ[size] = (char *) string;
       new_environ[size + 1] = NULL;
       new_environ[size] = (char *) string;
       new_environ[size + 1] = NULL;
-      if (last_environ != NULL)
-       free ((void *) last_environ);
+      free (last_environ);
       last_environ = new_environ;
       environ = new_environ;
     }
   else
       last_environ = new_environ;
       environ = new_environ;
     }
   else
-    *ep = (char *) string;
+    *ep = string;
 
   return 0;
 }
 
   return 0;
 }