OSDN Git Service

libc: build abort with unwind-info for backtrace
[uclinux-h8/uClibc.git] / libc / stdlib / setenv.c
index 6628e4c..8da2fae 100644 (file)
-/* Copyright (C) 1992, 1995 Free Software Foundation, Inc.
-This file is part of the GNU C Library.
+/* Copyright (C) 1992,95,96,97,98,99,2000,2001 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 free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 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.
+   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
+   Lesser 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., 675 Mass Ave,
-Cambridge, MA 02139, USA.  */
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.
 
+   modified for uClibc by Erik Andersen <andersen@codepoet.org>
+*/
+
+#include <features.h>
+#include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#include <errno.h>
 
-#if !defined(HAVE_GNU_LD) && !defined (__ELF__)
-#define        __environ       environ
-#endif
 
-#if defined(_REENTRENT) || defined(_THREAD_SAFE)
-# include <pthread.h>
+#include <bits/uClibc_mutex.h>
+__UCLIBC_MUTEX_STATIC(mylock, PTHREAD_MUTEX_INITIALIZER);
 
-/* We need to initialize the mutex.  For this we use a method provided
-   by pthread function 'pthread_once'.  For this we need a once block.  */
-static pthread_once__t _once_block = pthread_once_init;
 
-/* This is the mutex which protects the global environment of simultaneous
-   modifications.  */
-static pthread_mutex_t _setenv_mutex;
+/* If this variable is not a null pointer we allocated the current
+   environment.  */
+static char **last_environ;
 
-static void
-DEFUN_VOID(_init_setenv_mutex)
+
+/* 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. [in uclibc, we do not]  */
+static int __add_to_environ(const char *name, const char *value,
+               int replace)
 {
-  pthread_mutex_init(&_setenv_mutex, pthread_mutexattr_default);
-}
+       register char **ep;
+       register size_t size;
+       char *var_val;
+       char **new_environ;
+       /* name may come from putenv() and thus may contain "=VAL" part */
+       const size_t namelen = strchrnul(name, '=') - name;
+       int rv = -1;
+
+       __UCLIBC_MUTEX_LOCK(mylock);
+
+       /* 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;
+       if (ep != NULL) {
+               while (*ep != NULL) {
+                       if (!strncmp(*ep, name, namelen) && (*ep)[namelen] == '=') {
+                               /* Found */
+                               if (!replace)
+                                       goto DONE_OK;
+                               goto REPLACE;
+                       }
+                       ++size;
+                       ++ep;
+               }
+       }
 
-# define LOCK() \
-   do { pthread_once(&_once_block, _init_setenv_mutex);
-        pthread_mutex_lock(&_setenv_mutex); } while (0)
-# define UNLOCK() pthread_mutex_unlock(&_setenv_mutex)
+       /* Not found, add at the end */
 
-#else /* !_REENTRENT && !_THREAD_SAFE */
+       /* We allocated this space; we can extend it.  */
+       new_environ = realloc(last_environ, (size + 2) * sizeof(char *));
+       if (new_environ == NULL) {
+               __set_errno(ENOMEM);
+               goto DONE;
+       }
+       if (__environ != last_environ) {
+               memcpy(new_environ, __environ, size * sizeof(char *));
+       }
+       last_environ = __environ = new_environ;
+
+       ep = &new_environ[size];
+       /* Ensure env is NULL terminated in case malloc below fails */
+       ep[0] = NULL;
+       ep[1] = NULL;
+
+ REPLACE:
+       var_val = (char*) name;
+       /* Build VAR=VAL if we called by setenv, not putenv.  */
+       if (value != NULL) {
+               const size_t vallen = strlen(value) + 1;
+
+               var_val = malloc(namelen + 1 + vallen);
+               if (var_val == NULL) {
+                       __set_errno(ENOMEM);
+                       goto DONE;
+               }
+               memcpy(var_val, name, namelen);
+               var_val[namelen] = '=';
+               memcpy(&var_val[namelen + 1], value, vallen);
+       }
+       *ep = var_val;
 
-# define LOCK()
-# define UNLOCK()
+ DONE_OK:
+       rv = 0;
 
-#endif /* _REENTRENT || _THREAD_SAFE */
+ DONE:
+       __UCLIBC_MUTEX_UNLOCK(mylock);
+       return rv;
+}
 
 int setenv(const char *name, const char *value, int replace)
 {
-  register char **ep;
-  register size_t size;
-  const size_t namelen = strlen (name);
-  const size_t vallen = strlen (value);
-  int result = 0;
-
-  LOCK();
-
-  size = 0;
-  for (ep = __environ; *ep != NULL; ++ep)
-    if (!memcmp (*ep, name, namelen) && (*ep)[namelen] == '=')
-      break;
-    else
-      ++size;
-  
-  if (*ep == NULL)
-    {
-      static char **last_environ = NULL;
-      char **new_environ = (char **) malloc((size + 2) * sizeof(char *));
-      if (new_environ == NULL)
-       {
-         result = -1;
-         goto do_return;
-       }
-      (void) memcpy((__ptr_t) new_environ, (__ptr_t) __environ, size * sizeof(char *));
-
-      new_environ[size] = malloc (namelen + 1 + vallen + 1);
-      if (new_environ[size] == NULL)
-       {
-         free (new_environ);
-         errno = ENOMEM;
-         result = -1;
-         goto do_return;
+       /* NB: setenv("VAR", NULL, 1) inserts "VAR=" string */
+       return __add_to_environ(name, value ? value : "", replace);
+}
+libc_hidden_def(setenv)
+
+int unsetenv(const char *name)
+{
+       const char *eq;
+       size_t len;
+       char **ep;
+
+       if (name == NULL || *name == '\0'
+        || *(eq = strchrnul(name, '=')) == '='
+       ) {
+               __set_errno(EINVAL);
+               return -1;
        }
-      memcpy (new_environ[size], name, namelen);
-      new_environ[size][namelen] = '=';
-      memcpy (&new_environ[size][namelen + 1], value, vallen + 1);
-
-      new_environ[size + 1] = NULL;
-
-      if (last_environ != NULL)
-       free ((__ptr_t) last_environ);
-      last_environ = new_environ;
-      __environ = new_environ;
-    }
-  else if (replace)
-    {
-      size_t len = strlen (*ep);
-      if (len < namelen + 1 + vallen)
-       {
-         char *new = malloc (namelen + 1 + vallen + 1);
-         if (new == NULL)
-           {
-             result = -1;
-             goto do_return;
-           }
-         *ep = new;
-         memcpy (*ep, name, namelen);
-         (*ep)[namelen] = '=';
+       len = eq - name; /* avoiding strlen this way */
+
+       __UCLIBC_MUTEX_LOCK(mylock);
+       ep = __environ;
+       /* NB: clearenv(); unsetenv("foo"); should not segfault */
+       if (ep) 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;
+               }
        }
-      memcpy (&(*ep)[namelen + 1], value, vallen + 1);
-    }
-
-do_return:
-  UNLOCK();
-  return result;
+       __UCLIBC_MUTEX_UNLOCK(mylock);
+       return 0;
 }
+libc_hidden_def(unsetenv)
 
-
-void unsetenv(const char *name)
+/* 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(void)
 {
-  register char **ep;
-  register char **dp;
-  const size_t namelen = strlen (name);
-
-  LOCK();
-
-  for (dp = ep = __environ; *ep != NULL; ++ep)
-    if (memcmp (*ep, name, namelen) || (*ep)[namelen] != '=')
-      {
-       *dp = *ep;
-       ++dp;
-      }
-  *dp = NULL;
+       __UCLIBC_MUTEX_LOCK(mylock);
+       /* If we allocated this environment we can free it.
+        * If we did not allocate this environment, it's NULL already
+        * and is safe to free().  */
+       free(last_environ);
+       last_environ = NULL;
+       /* Clearing environ removes the whole environment.  */
+       __environ = NULL;
+       __UCLIBC_MUTEX_UNLOCK(mylock);
+       return 0;
+}
 
-  UNLOCK();
+/* Put STRING, which is of the form "NAME=VALUE", in the environment.  */
+int putenv(char *string)
+{
+       if (strchr(string, '=') != NULL) {
+               return __add_to_environ(string, NULL, 1);
+       }
+       return unsetenv(string);
 }