setenv and unsetenv.
authorBruno Haible <bruno@clisp.org>
Wed, 11 Dec 2002 20:31:00 +0000 (20:31 +0000)
committerBruno Haible <bruno@clisp.org>
Wed, 11 Dec 2002 20:31:00 +0000 (20:31 +0000)
lib/ChangeLog
lib/setenv.c
lib/unsetenv.c [new file with mode: 0644]

index 633316b..02fa197 100644 (file)
@@ -1,3 +1,39 @@
+2002-12-11  Bruno Haible  <bruno@clisp.org>
+
+       * setenv.h: Rewritten to cope with systems that have setenv() but not
+       unsetenv().
+       * setenv.c, unsetenv.c: Taken from glibc-2.2.4 with the following
+       modifications:
+
+       2002-12-11  Bruno Haible  <bruno@clisp.org>
+
+               * setenv.c (alloca): Fall back to malloc.
+               (freea): New macro.
+               (setenv): Use freea() to free memory allocated with alloca().
+
+       2002-11-13  Bruno Haible  <bruno@clisp.org>
+
+               * setenv.c (compar_fn_t, __add_to_environ, setenv): Use ANSI C
+               function declarations.
+               * unsetenv.c (unsetenv): Likewise.
+
+       2002-03-04  Bruno Haible  <bruno@clisp.org>
+
+               Portability to AIX 4.3.3.
+               * unsetenv.c: New file, extracted from setenv.c.
+               * setenv.c: Move the unsetenv() function to unsetenv.c.
+
+       2001-12-20  Bruno Haible  <bruno@clisp.org>
+
+               * setenv.c (__add_to_environ): Don't call realloc(NULL,...),
+               use malloc instead. For SunOS4.
+
+       2001-12-11  Bruno Haible  <bruno@clisp.org>
+
+               * setenv.c: Declare alloca.
+               (compar_fn_t): New typedef.
+               (KNOWN_VALUE, STORE_VALUE): Use it.
+
 2002-12-10  Paul Eggert  <eggert@twinsun.com>
 
        Port exclude.c and exclude.h to more non-GNU systems, e.g. Solaris 7.
index 3ed901b..57e6dd9 100644 (file)
@@ -1,28 +1,63 @@
-/* Copyright (C) 1992, 1995, 2000 Free Software Foundation, Inc.
+/* Copyright (C) 1992,1995-1999,2000-2002 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
 
-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.
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
 
-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
-Free Software Foundation; either version 2, or (at your option) any
-later version.
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
 
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
 
-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.  */
-
-#ifdef HAVE_CONFIG_H
+#if HAVE_CONFIG_H
 # include <config.h>
 #endif
 
+#ifdef __GNUC__
+# ifndef alloca
+#  define alloca __builtin_alloca
+# endif
+#else
+# ifdef _MSC_VER
+#  include <malloc.h>
+#  define alloca _alloca
+# else
+#  if HAVE_ALLOCA_H
+#   include <alloca.h>
+#  else
+#   ifdef _AIX
+ #pragma alloca
+#   else
+#    ifdef __hpux /* This section must match that of bison generated files. */
+#     ifdef __cplusplus
+extern "C" void *alloca (unsigned int);
+#     else /* not __cplusplus */
+void *alloca ();
+#     endif /* not __cplusplus */
+#    else /* not __hpux */
+#     ifndef alloca
+char *alloca ();
+#     endif
+#    endif /* __hpux */
+#   endif
+#  endif
+# endif
+#endif
+
 #include <errno.h>
+#if !_LIBC
+# if !defined errno && !defined HAVE_ERRNO_DECL
+extern int errno;
+# endif
+# define __set_errno(ev) ((errno) = (ev))
+#endif
 
 #if _LIBC || HAVE_STDLIB_H
 # include <stdlib.h>
@@ -34,96 +69,287 @@ USA.  */
 # include <unistd.h>
 #endif
 
-#ifndef        HAVE_GNU_LD
+/* For those losing systems which don't have 'alloca' we have to add
+   some additional code emulating it.  */
+#if _LIBC || HAVE_ALLOCA
+# define freea(p) /* nothing */
+#else
+# define alloca(n) malloc (n)
+# define freea(p) free (p)
+#endif
+
+#if !_LIBC
 # define __environ     environ
+# ifndef HAVE_ENVIRON_DECL
+extern char **environ;
+# endif
+#endif
+
+#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
+
+/* In the GNU C library we must keep the namespace clean.  */
+#ifdef _LIBC
+# define setenv __setenv
+# define clearenv __clearenv
+# define tfind __tfind
+# define tsearch __tsearch
+#endif
+
+/* In the GNU C library implementation we try to be more clever and
+   allow arbitrarily many changes of the environment given that the used
+   values are from a small set.  Outside glibc this will eat up all
+   memory after a while.  */
+#if defined _LIBC || (defined HAVE_SEARCH_H && defined HAVE_TSEARCH \
+                     && defined __GNUC__)
+# define USE_TSEARCH   1
+# include <search.h>
+typedef int (*compar_fn_t) (const void *, const void *);
+
+/* This is a pointer to the root of the search tree with the known
+   values.  */
+static void *known_values;
+
+# define KNOWN_VALUE(Str) \
+  ({                                                                         \
+    void *value = tfind (Str, &known_values, (compar_fn_t) strcmp);          \
+    value != NULL ? *(char **) value : NULL;                                 \
+  })
+# define STORE_VALUE(Str) \
+  tsearch (Str, &known_values, (compar_fn_t) strcmp)
+
+#else
+# undef USE_TSEARCH
+
+# define KNOWN_VALUE(Str) NULL
+# define STORE_VALUE(Str) do { } while (0)
+
 #endif
 
+
+/* If this variable is not a null pointer we allocated the current
+   environment.  */
+static char **last_environ;
+
+
+/* This function is used by `setenv' and `putenv'.  The difference between
+   the two functions is that for the former must create a new string which
+   is then placed in the environment, while the argument of `putenv'
+   must be used directly.  This is all complicated by the fact that we try
+   to reuse values once generated for a `setenv' call since we can never
+   free the strings.  */
 int
-setenv (name, value, replace)
-     const char *name;
-     const char *value;
-     int replace;
+__add_to_environ (const char *name, const char *value, const char *combined,
+                 int replace)
 {
   register char **ep;
   register size_t size;
   const size_t namelen = strlen (name);
-  const size_t vallen = strlen (value) + 1;
+  const size_t vallen = value != NULL ? strlen (value) + 1 : 0;
+
+  LOCK;
+
+  /* We have to get the pointer now that we have the lock and not earlier
+     since another thread might have created a new environment.  */
+  ep = __environ;
 
   size = 0;
-  for (ep = __environ; *ep != NULL; ++ep)
-    if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=')
-      break;
-    else
-      ++size;
+  if (ep != NULL)
+    {
+      for (; *ep != NULL; ++ep)
+       if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=')
+         break;
+       else
+         ++size;
+    }
 
-  if (*ep == NULL)
+  if (ep == NULL || *ep == NULL)
     {
-      static char **last_environ;
       char **new_environ;
-      if (__environ == last_environ)
-       /* We allocated this space; we can extend it.  */
-       new_environ = (char **) realloc (last_environ,
-                                        (size + 2) * sizeof (char *));
-      else
-       new_environ = (char **) malloc ((size + 2) * sizeof (char *));
+#ifdef USE_TSEARCH
+      char *new_value;
+#endif
 
+      /* We allocated this space; we can extend it.  */
+      new_environ =
+       (char **) (last_environ == NULL
+                  ? malloc ((size + 2) * sizeof (char *))
+                  : realloc (last_environ, (size + 2) * sizeof (char *)));
       if (new_environ == NULL)
-       return -1;
-
-      new_environ[size] = malloc (namelen + 1 + vallen);
-      if (new_environ[size] == NULL)
        {
-         free ((char *) new_environ);
-         errno = ENOMEM;
+         UNLOCK;
          return -1;
        }
 
+      /* If the whole entry is given add it.  */
+      if (combined != NULL)
+       /* We must not add the string to the search tree since it belongs
+          to the user.  */
+       new_environ[size] = (char *) combined;
+      else
+       {
+         /* See whether the value is already known.  */
+#ifdef USE_TSEARCH
+         new_value = (char *) alloca (namelen + 1 + vallen);
+# ifdef _LIBC
+         __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
+                    value, vallen);
+# else
+         memcpy (new_value, name, namelen);
+         new_value[namelen] = '=';
+         memcpy (&new_value[namelen + 1], value, vallen);
+# endif
+
+         new_environ[size] = KNOWN_VALUE (new_value);
+         if (new_environ[size] == NULL)
+#endif
+           {
+             new_environ[size] = (char *) malloc (namelen + 1 + vallen);
+             if (new_environ[size] == NULL)
+               {
+#ifdef USE_TSEARCH
+                 freea (new_value);
+#endif
+                 __set_errno (ENOMEM);
+                 UNLOCK;
+                 return -1;
+               }
+
+#ifdef USE_TSEARCH
+             memcpy (new_environ[size], new_value, namelen + 1 + vallen);
+#else
+             memcpy (new_environ[size], name, namelen);
+             new_environ[size][namelen] = '=';
+             memcpy (&new_environ[size][namelen + 1], value, vallen);
+#endif
+             /* And save the value now.  We cannot do this when we remove
+                the string since then we cannot decide whether it is a
+                user string or not.  */
+             STORE_VALUE (new_environ[size]);
+           }
+#ifdef USE_TSEARCH
+         freea (new_value);
+#endif
+       }
+
       if (__environ != last_environ)
        memcpy ((char *) new_environ, (char *) __environ,
                size * sizeof (char *));
 
-      memcpy (new_environ[size], name, namelen);
-      new_environ[size][namelen] = '=';
-      memcpy (&new_environ[size][namelen + 1], value, vallen);
-
       new_environ[size + 1] = NULL;
 
       last_environ = __environ = new_environ;
     }
   else if (replace)
     {
-      size_t len = strlen (*ep);
-      if (len + 1 < namelen + 1 + vallen)
+      char *np;
+
+      /* Use the user string if given.  */
+      if (combined != NULL)
+       np = (char *) combined;
+      else
        {
-         /* The existing string is too short; malloc a new one.  */
-         char *new = malloc (namelen + 1 + vallen);
-         if (new == NULL)
-           return -1;
-         *ep = new;
+#ifdef USE_TSEARCH
+         char *new_value = alloca (namelen + 1 + vallen);
+# ifdef _LIBC
+         __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
+                    value, vallen);
+# else
+         memcpy (new_value, name, namelen);
+         new_value[namelen] = '=';
+         memcpy (&new_value[namelen + 1], value, vallen);
+# endif
+
+         np = KNOWN_VALUE (new_value);
+         if (np == NULL)
+#endif
+           {
+             np = malloc (namelen + 1 + vallen);
+             if (np == NULL)
+               {
+#ifdef USE_TSEARCH
+                 freea (new_value);
+#endif
+                 UNLOCK;
+                 return -1;
+               }
+
+#ifdef USE_TSEARCH
+             memcpy (np, new_value, namelen + 1 + vallen);
+#else
+             memcpy (np, name, namelen);
+             np[namelen] = '=';
+             memcpy (&np[namelen + 1], value, vallen);
+#endif
+             /* And remember the value.  */
+             STORE_VALUE (np);
+           }
+#ifdef USE_TSEARCH
+         freea (new_value);
+#endif
        }
-      memcpy (*ep, name, namelen);
-      (*ep)[namelen] = '=';
-      memcpy (&(*ep)[namelen + 1], value, vallen);
+
+      *ep = np;
     }
 
+  UNLOCK;
+
   return 0;
 }
 
-void
-unsetenv (name)
-     const char *name;
+int
+setenv (const char *name, const char *value, int replace)
 {
-  const size_t len = strlen (name);
-  char **ep;
-
-  for (ep = __environ; *ep; ++ep)
-    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.  */
-      }
+  return __add_to_environ (name, value, NULL, replace);
 }
+
+/* The `clearenv' was planned to be added to POSIX.1 but probably
+   never made it.  Nevertheless the POSIX.9 standard (POSIX bindings
+   for Fortran 77) requires this function.  */
+int
+clearenv ()
+{
+  LOCK;
+
+  if (__environ == last_environ && __environ != NULL)
+    {
+      /* We allocated this environment so we can free it.  */
+      free (__environ);
+      last_environ = NULL;
+    }
+
+  /* Clear the environment pointer removes the whole environment.  */
+  __environ = NULL;
+
+  UNLOCK;
+
+  return 0;
+}
+
+#ifdef _LIBC
+static void
+free_mem (void)
+{
+  /* Remove all traces.  */
+  clearenv ();
+
+  /* Now remove the search tree.  */
+  __tdestroy (known_values, free);
+  known_values = NULL;
+}
+text_set_element (__libc_subfreeres, free_mem);
+
+
+# undef setenv
+# undef clearenv
+weak_alias (__setenv, setenv)
+weak_alias (__clearenv, clearenv)
+#endif
diff --git a/lib/unsetenv.c b/lib/unsetenv.c
new file mode 100644 (file)
index 0000000..734589d
--- /dev/null
@@ -0,0 +1,104 @@
+/* Copyright (C) 1992,1995-1999,2000-2002 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#if !_LIBC
+# if !defined errno && !defined HAVE_ERRNO_DECL
+extern int errno;
+# endif
+# define __set_errno(ev) ((errno) = (ev))
+#endif
+
+#if _LIBC || HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#if _LIBC || HAVE_STRING_H
+# include <string.h>
+#endif
+#if _LIBC || HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#if !_LIBC
+# define __environ     environ
+# ifndef HAVE_ENVIRON_DECL
+extern char **environ;
+# endif
+#endif
+
+#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
+
+/* In the GNU C library we must keep the namespace clean.  */
+#ifdef _LIBC
+# define unsetenv __unsetenv
+#endif
+
+
+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;
+}
+
+#ifdef _LIBC
+# undef unsetenv
+weak_alias (__unsetenv, unsetenv)
+#endif