about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog10
-rw-r--r--include/stdlib.h3
-rw-r--r--sysdeps/generic/putenv.c4
-rw-r--r--sysdeps/generic/setenv.c152
4 files changed, 96 insertions, 73 deletions
diff --git a/ChangeLog b/ChangeLog
index 2208c604af..61b75f3825 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,15 @@
 1999-07-29  Ulrich Drepper  <drepper@cygnus.com>
 
+	* sysdeps/generic/setenv.c: Move setenv code in new function
+	__add_to_environ.  Add new parameter specifying already
+	constructed string for the environment.
+	(setenv): Call __add_to_environ with new parameter set to NULL.
+	(unsetenv): Really test all elements for duplicated name.  Missed those
+	cases where the two are following each other.
+	* sysdeps/generic/putenv.c: Use __add_to_environ instead of setenv.
+	* include/stdlib.h: Add prototype of __add_to_environ.
+	* stdlib/tst-environ.c: New file.
+
 	* sysdeps/i386/bits/string.h (__memset_cc): Fix typo in last patch.
 
 	* nis/nss_nis/nis-initgroups.c (_nss_nis_initgroups): Correct size
diff --git a/include/stdlib.h b/include/stdlib.h
index c15d364c6e..187c4585d8 100644
--- a/include/stdlib.h
+++ b/include/stdlib.h
@@ -46,6 +46,9 @@ extern char *__canonicalize_file_name __P ((__const char *__name));
 extern char *__realpath __P ((__const char *__name, char *__resolved));
 extern int __ptsname_r __P ((int __fd, char *__buf, size_t __buflen));
 extern int __getpt __P ((void));
+
+extern int __add_to_environ (const char *name, const char *value,
+			     const char *combines, int replace);
 #endif
 #undef __Need_M_And_C
 
diff --git a/sysdeps/generic/putenv.c b/sysdeps/generic/putenv.c
index 3fcd5f93a8..75fafad06d 100644
--- a/sysdeps/generic/putenv.c
+++ b/sysdeps/generic/putenv.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991, 94, 95, 96, 97, 98 Free Software Foundation, Inc.
+/* Copyright (C) 1991, 94, 95, 96, 97, 98, 99 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
@@ -64,7 +64,7 @@ putenv (string)
       memcpy (name, string, name_end - string);
       name[name_end - string] = '\0';
 #endif
-      return __setenv (name, name_end + 1, 1);
+      return __add_to_environ (name, NULL, string, 1);
     }
 
   __unsetenv (string);
diff --git a/sysdeps/generic/setenv.c b/sysdeps/generic/setenv.c
index a5818d03bf..fc58e9b346 100644
--- a/sysdeps/generic/setenv.c
+++ b/sysdeps/generic/setenv.c
@@ -16,23 +16,6 @@
    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.  */
 
-/* Issues:
-
-   1. putenv must not use setenv since the string provided by the user
-      must be used, not a copy
-
-   2. a common function should determine the place where to insert the
-      new entry and if necessary take care of extending the array
-
-   3. It must be kept track of whether an entry was inserted via putenv
-      or setenv.  In the former case the entry must not be put into
-      the search tree since removing it could mean it will not be
-      available anymore (e.g., when allocated on the stack)
-
-      To handle this an array parallel to the __environ array must specify
-      whether the entry was added via putenv or not
-*/
-
 #if HAVE_CONFIG_H
 # include <config.h>
 #endif
@@ -117,16 +100,23 @@ static void *known_values;
 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)
+__add_to_environ (name, value, combined, replace)
      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;
 
@@ -156,37 +146,49 @@ setenv (name, value, replace)
 	  return -1;
 	}
 
-      /* See whether the value is already known.  */
+      /* 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 = alloca (namelen + 1 + vallen);
+	  new_value = (char *) alloca (namelen + 1 + vallen);
 # ifdef _LIBC
-      __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
-		 value, vallen);
+	  __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);
+	  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] = malloc (namelen + 1 + vallen);
+	  new_environ[size] = KNOWN_VALUE (new_value);
 	  if (new_environ[size] == NULL)
+#endif
 	    {
-	      __set_errno (ENOMEM);
-	      UNLOCK;
-	      return -1;
-	    }
+	      new_environ[size] = (char *) malloc (namelen + 1 + vallen);
+	      if (new_environ[size] == NULL)
+		{
+		  __set_errno (ENOMEM);
+		  UNLOCK;
+		  return -1;
+		}
 
 #ifdef USE_TSEARCH
-	  memcpy (new_environ[size], new_value, namelen + 1 + vallen);
+	      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);
+	      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]);
+	    }
 	}
 
       if (__environ != last_environ)
@@ -199,43 +201,47 @@ setenv (name, value, replace)
     }
   else if (replace)
     {
-      char *new_value;
       char *np;
 
-      /* The existing string is too short; malloc a new one.  */
+      /* Use the user string if given.  */
+      if (combined != NULL)
+	np = (char *) combined;
+      else
+	{
 #ifdef USE_TSEARCH
-      new_value = alloca (namelen + 1 + vallen);
+	  char *new_value = alloca (namelen + 1 + vallen);
 # ifdef _LIBC
-      __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
-		 value, vallen);
+	  __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);
+	  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);
+	  np = KNOWN_VALUE (new_value);
 	  if (np == NULL)
+#endif
 	    {
-	      UNLOCK;
-	      return -1;
-	    }
+	      np = malloc (namelen + 1 + vallen);
+	      if (np == NULL)
+		{
+		  UNLOCK;
+		  return -1;
+		}
 
 #ifdef USE_TSEARCH
-	  memcpy (np, new_value, namelen + 1 + vallen);
+	      memcpy (np, new_value, namelen + 1 + vallen);
 #else
-	  memcpy (np, name, namelen);
-	  np[namelen] = '=';
-	  memcpy (&np[namelen + 1], value, vallen);
+	      memcpy (np, name, namelen);
+	      np[namelen] = '=';
+	      memcpy (&np[namelen + 1], value, vallen);
 #endif
+	      /* And remember the value.  */
+	      STORE_VALUE (np);
+	    }
 	}
 
-      /* Keep the old value around.  */
-      STORE_VALUE (*ep);
       *ep = np;
     }
 
@@ -244,6 +250,15 @@ setenv (name, value, replace)
   return 0;
 }
 
+int
+setenv (name, value, replace)
+     const char *name;
+     const char *value;
+     int replace;
+{
+  return __add_to_environ (name, value, NULL, replace);
+}
+
 void
 unsetenv (name)
      const char *name;
@@ -253,20 +268,20 @@ unsetenv (name)
 
   LOCK;
 
-  for (ep = __environ; *ep != NULL; ++ep)
+  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;
 
-	/* Store the value so that we can reuse it later.  */
-	STORE_VALUE (*ep);
-
 	do
 	  dp[0] = dp[1];
 	while (*dp++);
 	/* Continue the loop in case NAME appears again.  */
       }
+    else
+      ++ep;
 
   UNLOCK;
 }
@@ -281,12 +296,7 @@ clearenv ()
 
   if (__environ == last_environ && __environ != NULL)
     {
-      /* We allocated this environment so we can free it.  Store all the
-         strings.  */
-      char **ep = __environ;
-      while (*ep != NULL)
-	STORE_VALUE (*ep++);
-
+      /* We allocated this environment so we can free it.  */
       free (__environ);
       last_environ = NULL;
     }