(root)/
gettext-0.22.4/
gettext-tools/
gnulib-lib/
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    __extension__                                                               \
      86    ({                                                                          \
      87      void *value = tfind (Str, &known_values, (compar_fn_t) strcmp);           \
      88      value != NULL ? *(char **) value : NULL;                                  \
      89    })
      90  # define STORE_VALUE(Str) \
      91    tsearch (Str, &known_values, (compar_fn_t) strcmp)
      92  
      93  #else
      94  # undef USE_TSEARCH
      95  
      96  # define KNOWN_VALUE(Str) NULL
      97  # define STORE_VALUE(Str) do { } while (0)
      98  
      99  #endif
     100  
     101  
     102  /* If this variable is not a null pointer we allocated the current
     103     environment.  */
     104  static char **last_environ;
     105  
     106  
     107  /* This function is used by 'setenv' and 'putenv'.  The difference between
     108     the two functions is that for the former must create a new string which
     109     is then placed in the environment, while the argument of 'putenv'
     110     must be used directly.  This is all complicated by the fact that we try
     111     to reuse values once generated for a 'setenv' call since we can never
     112     free the strings.  */
     113  int
     114  __add_to_environ (const char *name, const char *value, const char *combined,
     115                    int replace)
     116  {
     117    char **ep;
     118    size_t size;
     119    const size_t namelen = strlen (name);
     120    const size_t vallen = value != NULL ? strlen (value) + 1 : 0;
     121  
     122    LOCK;
     123  
     124    /* We have to get the pointer now that we have the lock and not earlier
     125       since another thread might have created a new environment.  */
     126    ep = __environ;
     127  
     128    size = 0;
     129    if (ep != NULL)
     130      {
     131        for (; *ep != NULL; ++ep)
     132          if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=')
     133            break;
     134          else
     135            ++size;
     136      }
     137  
     138    if (ep == NULL || *ep == NULL)
     139      {
     140        char **new_environ;
     141  #ifdef USE_TSEARCH
     142        char *new_value;
     143  #endif
     144  
     145        /* We allocated this space; we can extend it.  */
     146        new_environ =
     147          (char **) (last_environ == NULL
     148                     ? malloc ((size + 2) * sizeof (char *))
     149                     : realloc (last_environ, (size + 2) * sizeof (char *)));
     150        if (new_environ == NULL)
     151          {
     152            /* It's easier to set errno to ENOMEM than to rely on the
     153               'malloc-posix' and 'realloc-posix' gnulib modules.  */
     154            __set_errno (ENOMEM);
     155            UNLOCK;
     156            return -1;
     157          }
     158  
     159        /* If the whole entry is given add it.  */
     160        if (combined != NULL)
     161          /* We must not add the string to the search tree since it belongs
     162             to the user.  */
     163          new_environ[size] = (char *) combined;
     164        else
     165          {
     166            /* See whether the value is already known.  */
     167  #ifdef USE_TSEARCH
     168  # ifdef _LIBC
     169            new_value = (char *) alloca (namelen + 1 + vallen);
     170            __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
     171                       value, vallen);
     172  # else
     173            new_value = (char *) malloca (namelen + 1 + vallen);
     174            if (new_value == NULL)
     175              {
     176                __set_errno (ENOMEM);
     177                UNLOCK;
     178                return -1;
     179              }
     180            memcpy (new_value, name, namelen);
     181            new_value[namelen] = '=';
     182            memcpy (&new_value[namelen + 1], value, vallen);
     183  # endif
     184  
     185            new_environ[size] = KNOWN_VALUE (new_value);
     186            if (new_environ[size] == NULL)
     187  #endif
     188              {
     189                new_environ[size] = (char *) malloc (namelen + 1 + vallen);
     190                if (new_environ[size] == NULL)
     191                  {
     192  #if defined USE_TSEARCH && !defined _LIBC
     193                    freea (new_value);
     194  #endif
     195                    __set_errno (ENOMEM);
     196                    UNLOCK;
     197                    return -1;
     198                  }
     199  
     200  #ifdef USE_TSEARCH
     201                memcpy (new_environ[size], new_value, namelen + 1 + vallen);
     202  #else
     203                memcpy (new_environ[size], name, namelen);
     204                new_environ[size][namelen] = '=';
     205                memcpy (&new_environ[size][namelen + 1], value, vallen);
     206  #endif
     207                /* And save the value now.  We cannot do this when we remove
     208                   the string since then we cannot decide whether it is a
     209                   user string or not.  */
     210                STORE_VALUE (new_environ[size]);
     211              }
     212  #if defined USE_TSEARCH && !defined _LIBC
     213            freea (new_value);
     214  #endif
     215          }
     216  
     217        if (__environ != last_environ)
     218          memcpy ((char *) new_environ, (char *) __environ,
     219                  size * sizeof (char *));
     220  
     221        new_environ[size + 1] = NULL;
     222  
     223        last_environ = __environ = new_environ;
     224      }
     225    else if (replace)
     226      {
     227        char *np;
     228  
     229        /* Use the user string if given.  */
     230        if (combined != NULL)
     231          np = (char *) combined;
     232        else
     233          {
     234  #ifdef USE_TSEARCH
     235            char *new_value;
     236  # ifdef _LIBC
     237            new_value = alloca (namelen + 1 + vallen);
     238            __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
     239                       value, vallen);
     240  # else
     241            new_value = malloca (namelen + 1 + vallen);
     242            if (new_value == NULL)
     243              {
     244                __set_errno (ENOMEM);
     245                UNLOCK;
     246                return -1;
     247              }
     248            memcpy (new_value, name, namelen);
     249            new_value[namelen] = '=';
     250            memcpy (&new_value[namelen + 1], value, vallen);
     251  # endif
     252  
     253            np = KNOWN_VALUE (new_value);
     254            if (np == NULL)
     255  #endif
     256              {
     257                np = (char *) malloc (namelen + 1 + vallen);
     258                if (np == NULL)
     259                  {
     260  #if defined USE_TSEARCH && !defined _LIBC
     261                    freea (new_value);
     262  #endif
     263                    __set_errno (ENOMEM);
     264                    UNLOCK;
     265                    return -1;
     266                  }
     267  
     268  #ifdef USE_TSEARCH
     269                memcpy (np, new_value, namelen + 1 + vallen);
     270  #else
     271                memcpy (np, name, namelen);
     272                np[namelen] = '=';
     273                memcpy (&np[namelen + 1], value, vallen);
     274  #endif
     275                /* And remember the value.  */
     276                STORE_VALUE (np);
     277              }
     278  #if defined USE_TSEARCH && !defined _LIBC
     279            freea (new_value);
     280  #endif
     281          }
     282  
     283        *ep = np;
     284      }
     285  
     286    UNLOCK;
     287  
     288    return 0;
     289  }
     290  
     291  int
     292  setenv (const char *name, const char *value, int replace)
     293  {
     294    if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
     295      {
     296        __set_errno (EINVAL);
     297        return -1;
     298      }
     299  
     300    return __add_to_environ (name, value, NULL, replace);
     301  }
     302  
     303  /* The 'clearenv' was planned to be added to POSIX.1 but probably
     304     never made it.  Nevertheless the POSIX.9 standard (POSIX bindings
     305     for Fortran 77) requires this function.  */
     306  int
     307  clearenv (void)
     308  {
     309    LOCK;
     310  
     311    if (__environ == last_environ && __environ != NULL)
     312      {
     313        /* We allocated this environment so we can free it.  */
     314        free (__environ);
     315        last_environ = NULL;
     316      }
     317  
     318    /* Clear the environment pointer removes the whole environment.  */
     319    __environ = NULL;
     320  
     321    UNLOCK;
     322  
     323    return 0;
     324  }
     325  
     326  #ifdef _LIBC
     327  static void
     328  free_mem (void)
     329  {
     330    /* Remove all traces.  */
     331    clearenv ();
     332  
     333    /* Now remove the search tree.  */
     334    __tdestroy (known_values, free);
     335    known_values = NULL;
     336  }
     337  text_set_element (__libc_subfreeres, free_mem);
     338  
     339  
     340  # undef setenv
     341  # undef clearenv
     342  weak_alias (__setenv, setenv)
     343  weak_alias (__clearenv, clearenv)
     344  #endif
     345  
     346  #endif /* _LIBC || !HAVE_SETENV */
     347  
     348  /* The rest of this file is called into use when replacing an existing
     349     but buggy setenv.  Known bugs include failure to diagnose invalid
     350     name, and consuming a leading '=' from value.  */
     351  #if HAVE_SETENV
     352  
     353  # undef setenv
     354  # if !HAVE_DECL_SETENV
     355  extern int setenv (const char *, const char *, int);
     356  # endif
     357  # define STREQ(a, b) (strcmp (a, b) == 0)
     358  
     359  int
     360  rpl_setenv (const char *name, const char *value, int replace)
     361  {
     362    int result;
     363    if (!name || !*name || strchr (name, '='))
     364      {
     365        errno = EINVAL;
     366        return -1;
     367      }
     368    /* Call the real setenv even if replace is 0, in case implementation
     369       has underlying data to update, such as when environ changes.  */
     370    result = setenv (name, value, replace);
     371    if (result == 0 && replace && *value == '=')
     372      {
     373        char *tmp = getenv (name);
     374        if (!STREQ (tmp, value))
     375          {
     376            int saved_errno;
     377            size_t len = strlen (value);
     378            tmp = malloca (len + 2);
     379            if (tmp == NULL)
     380              {
     381                errno = ENOMEM;
     382                return -1;
     383              }
     384            /* Since leading '=' is eaten, double it up.  */
     385            *tmp = '=';
     386            memcpy (tmp + 1, value, len + 1);
     387            result = setenv (name, tmp, replace);
     388            saved_errno = errno;
     389            freea (tmp);
     390            errno = saved_errno;
     391          }
     392      }
     393    return result;
     394  }
     395  
     396  #endif /* HAVE_SETENV */