root/maint/gnulib/lib/setenv.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. __add_to_environ
  2. setenv
  3. clearenv
  4. free_mem
  5. rpl_setenv

   1 /* Copyright (C) 1992, 1995-2003, 2005-2021 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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           /* Since leading '=' is eaten, double it up.  */
 379           *tmp = '=';
 380           memcpy (tmp + 1, value, len + 1);
 381           result = setenv (name, tmp, replace);
 382           saved_errno = errno;
 383           freea (tmp);
 384           errno = saved_errno;
 385         }
 386     }
 387   return result;
 388 }
 389 
 390 #endif /* HAVE_SETENV */

/* [previous][next][first][last][top][bottom][index][help] */