# HG changeset patch # User Bruno Haible # Date 1039638660 0 # Node ID d064e5107035f27986c15cdcc134809b32920078 # Parent 59e0c3ba95bb9bed732c837288ca393a2ffab4af setenv and unsetenv. diff --git a/lib/ChangeLog b/lib/ChangeLog --- a/lib/ChangeLog +++ b/lib/ChangeLog @@ -1,3 +1,39 @@ +2002-12-11 Bruno Haible + + * 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 + + * setenv.c (alloca): Fall back to malloc. + (freea): New macro. + (setenv): Use freea() to free memory allocated with alloca(). + + 2002-11-13 Bruno Haible + + * setenv.c (compar_fn_t, __add_to_environ, setenv): Use ANSI C + function declarations. + * unsetenv.c (unsetenv): Likewise. + + 2002-03-04 Bruno Haible + + 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 + + * setenv.c (__add_to_environ): Don't call realloc(NULL,...), + use malloc instead. For SunOS4. + + 2001-12-11 Bruno Haible + + * setenv.c: Declare alloca. + (compar_fn_t): New typedef. + (KNOWN_VALUE, STORE_VALUE): Use it. + 2002-12-10 Paul Eggert Port exclude.c and exclude.h to more non-GNU systems, e.g. Solaris 7. diff --git a/lib/setenv.c b/lib/setenv.c --- a/lib/setenv.c +++ b/lib/setenv.c @@ -1,28 +1,63 @@ -/* Copyright (C) 1992, 1995, 2000 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. +/* Copyright (C) 1992,1995-1999,2000-2002 Free Software Foundation, Inc. + This file is part of the GNU C Library. -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 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 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. + 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 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. */ + 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. */ -#ifdef HAVE_CONFIG_H +#if HAVE_CONFIG_H # include #endif +#ifdef __GNUC__ +# ifndef alloca +# define alloca __builtin_alloca +# endif +#else +# ifdef _MSC_VER +# include +# define alloca _alloca +# else +# if HAVE_ALLOCA_H +# include +# 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 +#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 @@ -34,96 +69,287 @@ # include #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 +__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 +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; + { + 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] = malloc (namelen + 1 + vallen); - if (new_environ[size] == NULL) - { - free ((char *) new_environ); - errno = ENOMEM; - return -1; + 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) +{ + 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 () { - const size_t len = strlen (name); - char **ep; + 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; - 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. */ - } + 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 --- /dev/null +++ b/lib/unsetenv.c @@ -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 +#endif + +#include +#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 +#endif +#if _LIBC || HAVE_STRING_H +# include +#endif +#if _LIBC || HAVE_UNISTD_H +# include +#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 +__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