(root)/
tar-1.35/
gnu/
setenv.c
       1  /* Copyright (C) 1992, 1995-2003, 2005-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     This file is free software: you can redistribute it and/or modify
       5     it under the terms of the GNU Lesser General Public License as
       6     published by the Free Software Foundation; either version 2.1 of the
       7     License, or (at your option) any later version.
       8  
       9     This file is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  #if !_LIBC
      18  /* Don't use __attribute__ __nonnull__ in this compilation unit.  Otherwise gcc
      19     optimizes away the name == NULL test below.  */
      20  # define _GL_ARG_NONNULL(params)
      21  
      22  # define _GL_USE_STDLIB_ALLOC 1
      23  # include <config.h>
      24  #endif
      25  
      26  #include <alloca.h>
      27  
      28  /* Specification.  */
      29  #include <stdlib.h>
      30  
      31  #include <errno.h>
      32  #ifndef __set_errno
      33  # define __set_errno(ev) ((errno) = (ev))
      34  #endif
      35  
      36  #include <string.h>
      37  #if _LIBC || HAVE_UNISTD_H
      38  # include <unistd.h>
      39  #endif
      40  
      41  #if !_LIBC
      42  # include "malloca.h"
      43  #endif
      44  
      45  #if _LIBC || !HAVE_SETENV
      46  
      47  #if !_LIBC
      48  # define __environ      environ
      49  #endif
      50  
      51  #if _LIBC
      52  /* This lock protects against simultaneous modifications of 'environ'.  */
      53  # include <bits/libc-lock.h>
      54  __libc_lock_define_initialized (static, envlock)
      55  # define LOCK   __libc_lock_lock (envlock)
      56  # define UNLOCK __libc_lock_unlock (envlock)
      57  #else
      58  # define LOCK
      59  # define UNLOCK
      60  #endif
      61  
      62  /* In the GNU C library we must keep the namespace clean.  */
      63  #ifdef _LIBC
      64  # define setenv __setenv
      65  # define clearenv __clearenv
      66  # define tfind __tfind
      67  # define tsearch __tsearch
      68  #endif
      69  
      70  /* In the GNU C library implementation we try to be more clever and
      71     allow arbitrarily many changes of the environment given that the used
      72     values are from a small set.  Outside glibc this will eat up all
      73     memory after a while.  */
      74  #if defined _LIBC || (defined HAVE_SEARCH_H && defined HAVE_TSEARCH \
      75                        && (defined __GNUC__ || defined __clang__))
      76  # define USE_TSEARCH    1
      77  # include <search.h>
      78  typedef int (*compar_fn_t) (const void *, const void *);
      79  
      80  /* This is a pointer to the root of the search tree with the known
      81     values.  */
      82  static void *known_values;
      83  
      84  # define KNOWN_VALUE(Str) \
      85    ({                                                                          \
      86      void *value = tfind (Str, &known_values, (compar_fn_t) strcmp);           \
      87      value != NULL ? *(char **) value : NULL;                                  \
      88    })
      89  # define STORE_VALUE(Str) \
      90    tsearch (Str, &known_values, (compar_fn_t) strcmp)
      91  
      92  #else
      93  # undef USE_TSEARCH
      94  
      95  # define KNOWN_VALUE(Str) NULL
      96  # define STORE_VALUE(Str) do { } while (0)
      97  
      98  #endif
      99  
     100  
     101  /* If this variable is not a null pointer we allocated the current
     102     environment.  */
     103  static char **last_environ;
     104  
     105  
     106  /* This function is used by 'setenv' and 'putenv'.  The difference between
     107     the two functions is that for the former must create a new string which
     108     is then placed in the environment, while the argument of 'putenv'
     109     must be used directly.  This is all complicated by the fact that we try
     110     to reuse values once generated for a 'setenv' call since we can never
     111     free the strings.  */
     112  int
     113  __add_to_environ (const char *name, const char *value, const char *combined,
     114                    int replace)
     115  {
     116    char **ep;
     117    size_t size;
     118    const size_t namelen = strlen (name);
     119    const size_t vallen = value != NULL ? strlen (value) + 1 : 0;
     120  
     121    LOCK;
     122  
     123    /* We have to get the pointer now that we have the lock and not earlier
     124       since another thread might have created a new environment.  */
     125    ep = __environ;
     126  
     127    size = 0;
     128    if (ep != NULL)
     129      {
     130        for (; *ep != NULL; ++ep)
     131          if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=')
     132            break;
     133          else
     134            ++size;
     135      }
     136  
     137    if (ep == NULL || *ep == NULL)
     138      {
     139        char **new_environ;
     140  #ifdef USE_TSEARCH
     141        char *new_value;
     142  #endif
     143  
     144        /* We allocated this space; we can extend it.  */
     145        new_environ =
     146          (char **) (last_environ == NULL
     147                     ? malloc ((size + 2) * sizeof (char *))
     148                     : realloc (last_environ, (size + 2) * sizeof (char *)));
     149        if (new_environ == NULL)
     150          {
     151            /* It's easier to set errno to ENOMEM than to rely on the
     152               'malloc-posix' and 'realloc-posix' gnulib modules.  */
     153            __set_errno (ENOMEM);
     154            UNLOCK;
     155            return -1;
     156          }
     157  
     158        /* If the whole entry is given add it.  */
     159        if (combined != NULL)
     160          /* We must not add the string to the search tree since it belongs
     161             to the user.  */
     162          new_environ[size] = (char *) combined;
     163        else
     164          {
     165            /* See whether the value is already known.  */
     166  #ifdef USE_TSEARCH
     167  # ifdef _LIBC
     168            new_value = (char *) alloca (namelen + 1 + vallen);
     169            __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
     170                       value, vallen);
     171  # else
     172            new_value = (char *) malloca (namelen + 1 + vallen);
     173            if (new_value == NULL)
     174              {
     175                __set_errno (ENOMEM);
     176                UNLOCK;
     177                return -1;
     178              }
     179            memcpy (new_value, name, namelen);
     180            new_value[namelen] = '=';
     181            memcpy (&new_value[namelen + 1], value, vallen);
     182  # endif
     183  
     184            new_environ[size] = KNOWN_VALUE (new_value);
     185            if (new_environ[size] == NULL)
     186  #endif
     187              {
     188                new_environ[size] = (char *) malloc (namelen + 1 + vallen);
     189                if (new_environ[size] == NULL)
     190                  {
     191  #if defined USE_TSEARCH && !defined _LIBC
     192                    freea (new_value);
     193  #endif
     194                    __set_errno (ENOMEM);
     195                    UNLOCK;
     196                    return -1;
     197                  }
     198  
     199  #ifdef USE_TSEARCH
     200                memcpy (new_environ[size], new_value, namelen + 1 + vallen);
     201  #else
     202                memcpy (new_environ[size], name, namelen);
     203                new_environ[size][namelen] = '=';
     204                memcpy (&new_environ[size][namelen + 1], value, vallen);
     205  #endif
     206                /* And save the value now.  We cannot do this when we remove
     207                   the string since then we cannot decide whether it is a
     208                   user string or not.  */
     209                STORE_VALUE (new_environ[size]);
     210              }
     211  #if defined USE_TSEARCH && !defined _LIBC
     212            freea (new_value);
     213  #endif
     214          }
     215  
     216        if (__environ != last_environ)
     217          memcpy ((char *) new_environ, (char *) __environ,
     218                  size * sizeof (char *));
     219  
     220        new_environ[size + 1] = NULL;
     221  
     222        last_environ = __environ = new_environ;
     223      }
     224    else if (replace)
     225      {
     226        char *np;
     227  
     228        /* Use the user string if given.  */
     229        if (combined != NULL)
     230          np = (char *) combined;
     231        else
     232          {
     233  #ifdef USE_TSEARCH
     234            char *new_value;
     235  # ifdef _LIBC
     236            new_value = alloca (namelen + 1 + vallen);
     237            __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
     238                       value, vallen);
     239  # else
     240            new_value = malloca (namelen + 1 + vallen);
     241            if (new_value == NULL)
     242              {
     243                __set_errno (ENOMEM);
     244                UNLOCK;
     245                return -1;
     246              }
     247            memcpy (new_value, name, namelen);
     248            new_value[namelen] = '=';
     249            memcpy (&new_value[namelen + 1], value, vallen);
     250  # endif
     251  
     252            np = KNOWN_VALUE (new_value);
     253            if (np == NULL)
     254  #endif
     255              {
     256                np = (char *) malloc (namelen + 1 + vallen);
     257                if (np == NULL)
     258                  {
     259  #if defined USE_TSEARCH && !defined _LIBC
     260                    freea (new_value);
     261  #endif
     262                    __set_errno (ENOMEM);
     263                    UNLOCK;
     264                    return -1;
     265                  }
     266  
     267  #ifdef USE_TSEARCH
     268                memcpy (np, new_value, namelen + 1 + vallen);
     269  #else
     270                memcpy (np, name, namelen);
     271                np[namelen] = '=';
     272                memcpy (&np[namelen + 1], value, vallen);
     273  #endif
     274                /* And remember the value.  */
     275                STORE_VALUE (np);
     276              }
     277  #if defined USE_TSEARCH && !defined _LIBC
     278            freea (new_value);
     279  #endif
     280          }
     281  
     282        *ep = np;
     283      }
     284  
     285    UNLOCK;
     286  
     287    return 0;
     288  }
     289  
     290  int
     291  setenv (const char *name, const char *value, int replace)
     292  {
     293    if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
     294      {
     295        __set_errno (EINVAL);
     296        return -1;
     297      }
     298  
     299    return __add_to_environ (name, value, NULL, replace);
     300  }
     301  
     302  /* The 'clearenv' was planned to be added to POSIX.1 but probably
     303     never made it.  Nevertheless the POSIX.9 standard (POSIX bindings
     304     for Fortran 77) requires this function.  */
     305  int
     306  clearenv (void)
     307  {
     308    LOCK;
     309  
     310    if (__environ == last_environ && __environ != NULL)
     311      {
     312        /* We allocated this environment so we can free it.  */
     313        free (__environ);
     314        last_environ = NULL;
     315      }
     316  
     317    /* Clear the environment pointer removes the whole environment.  */
     318    __environ = NULL;
     319  
     320    UNLOCK;
     321  
     322    return 0;
     323  }
     324  
     325  #ifdef _LIBC
     326  static void
     327  free_mem (void)
     328  {
     329    /* Remove all traces.  */
     330    clearenv ();
     331  
     332    /* Now remove the search tree.  */
     333    __tdestroy (known_values, free);
     334    known_values = NULL;
     335  }
     336  text_set_element (__libc_subfreeres, free_mem);
     337  
     338  
     339  # undef setenv
     340  # undef clearenv
     341  weak_alias (__setenv, setenv)
     342  weak_alias (__clearenv, clearenv)
     343  #endif
     344  
     345  #endif /* _LIBC || !HAVE_SETENV */
     346  
     347  /* The rest of this file is called into use when replacing an existing
     348     but buggy setenv.  Known bugs include failure to diagnose invalid
     349     name, and consuming a leading '=' from value.  */
     350  #if HAVE_SETENV
     351  
     352  # undef setenv
     353  # if !HAVE_DECL_SETENV
     354  extern int setenv (const char *, const char *, int);
     355  # endif
     356  # define STREQ(a, b) (strcmp (a, b) == 0)
     357  
     358  int
     359  rpl_setenv (const char *name, const char *value, int replace)
     360  {
     361    int result;
     362    if (!name || !*name || strchr (name, '='))
     363      {
     364        errno = EINVAL;
     365        return -1;
     366      }
     367    /* Call the real setenv even if replace is 0, in case implementation
     368       has underlying data to update, such as when environ changes.  */
     369    result = setenv (name, value, replace);
     370    if (result == 0 && replace && *value == '=')
     371      {
     372        char *tmp = getenv (name);
     373        if (!STREQ (tmp, value))
     374          {
     375            int saved_errno;
     376            size_t len = strlen (value);
     377            tmp = malloca (len + 2);
     378            if (tmp == NULL)
     379              {
     380                errno = ENOMEM;
     381                return -1;
     382              }
     383            /* Since leading '=' is eaten, double it up.  */
     384            *tmp = '=';
     385            memcpy (tmp + 1, value, len + 1);
     386            result = setenv (name, tmp, replace);
     387            saved_errno = errno;
     388            freea (tmp);
     389            errno = saved_errno;
     390          }
     391      }
     392    return result;
     393  }
     394  
     395  #endif /* HAVE_SETENV */