Blame gl/tests/setenv.c

Packit Service 991b93
/* Copyright (C) 1992, 1995-2003, 2005-2020 Free Software Foundation, Inc.
Packit aea12f
   This file is part of the GNU C Library.
Packit aea12f
Packit aea12f
   This program is free software: you can redistribute it and/or modify
Packit aea12f
   it under the terms of the GNU General Public License as published by
Packit aea12f
   the Free Software Foundation; either version 3 of the License, or
Packit aea12f
   (at your option) any later version.
Packit aea12f
Packit aea12f
   This program is distributed in the hope that it will be useful,
Packit aea12f
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit aea12f
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit aea12f
   GNU General Public License for more details.
Packit aea12f
Packit aea12f
   You should have received a copy of the GNU General Public License
Packit aea12f
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
Packit aea12f
Packit aea12f
#if !_LIBC
Packit aea12f
/* Don't use __attribute__ __nonnull__ in this compilation unit.  Otherwise gcc
Packit aea12f
   optimizes away the name == NULL test below.  */
Packit aea12f
# define _GL_ARG_NONNULL(params)
Packit aea12f
Packit aea12f
# define _GL_USE_STDLIB_ALLOC 1
Packit aea12f
# include <config.h>
Packit aea12f
#endif
Packit aea12f
Packit aea12f
#include <alloca.h>
Packit aea12f
Packit aea12f
/* Specification.  */
Packit aea12f
#include <stdlib.h>
Packit aea12f
Packit aea12f
#include <errno.h>
Packit aea12f
#ifndef __set_errno
Packit aea12f
# define __set_errno(ev) ((errno) = (ev))
Packit aea12f
#endif
Packit aea12f
Packit aea12f
#include <string.h>
Packit aea12f
#if _LIBC || HAVE_UNISTD_H
Packit aea12f
# include <unistd.h>
Packit aea12f
#endif
Packit aea12f
Packit aea12f
#if !_LIBC
Packit aea12f
# include "malloca.h"
Packit aea12f
#endif
Packit aea12f
Packit aea12f
#if _LIBC || !HAVE_SETENV
Packit aea12f
Packit aea12f
#if !_LIBC
Packit aea12f
# define __environ      environ
Packit aea12f
#endif
Packit aea12f
Packit aea12f
#if _LIBC
Packit aea12f
/* This lock protects against simultaneous modifications of 'environ'.  */
Packit aea12f
# include <bits/libc-lock.h>
Packit aea12f
__libc_lock_define_initialized (static, envlock)
Packit aea12f
# define LOCK   __libc_lock_lock (envlock)
Packit aea12f
# define UNLOCK __libc_lock_unlock (envlock)
Packit aea12f
#else
Packit aea12f
# define LOCK
Packit aea12f
# define UNLOCK
Packit aea12f
#endif
Packit aea12f
Packit aea12f
/* In the GNU C library we must keep the namespace clean.  */
Packit aea12f
#ifdef _LIBC
Packit aea12f
# define setenv __setenv
Packit aea12f
# define clearenv __clearenv
Packit aea12f
# define tfind __tfind
Packit aea12f
# define tsearch __tsearch
Packit aea12f
#endif
Packit aea12f
Packit aea12f
/* In the GNU C library implementation we try to be more clever and
Packit aea12f
   allow arbitrarily many changes of the environment given that the used
Packit aea12f
   values are from a small set.  Outside glibc this will eat up all
Packit aea12f
   memory after a while.  */
Packit aea12f
#if defined _LIBC || (defined HAVE_SEARCH_H && defined HAVE_TSEARCH \
Packit aea12f
                      && defined __GNUC__)
Packit aea12f
# define USE_TSEARCH    1
Packit aea12f
# include <search.h>
Packit aea12f
typedef int (*compar_fn_t) (const void *, const void *);
Packit aea12f
Packit aea12f
/* This is a pointer to the root of the search tree with the known
Packit aea12f
   values.  */
Packit aea12f
static void *known_values;
Packit aea12f
Packit aea12f
# define KNOWN_VALUE(Str) \
Packit aea12f
  ({                                                                          \
Packit aea12f
    void *value = tfind (Str, &known_values, (compar_fn_t) strcmp);           \
Packit aea12f
    value != NULL ? *(char **) value : NULL;                                  \
Packit aea12f
  })
Packit aea12f
# define STORE_VALUE(Str) \
Packit aea12f
  tsearch (Str, &known_values, (compar_fn_t) strcmp)
Packit aea12f
Packit aea12f
#else
Packit aea12f
# undef USE_TSEARCH
Packit aea12f
Packit aea12f
# define KNOWN_VALUE(Str) NULL
Packit aea12f
# define STORE_VALUE(Str) do { } while (0)
Packit aea12f
Packit aea12f
#endif
Packit aea12f
Packit aea12f
Packit aea12f
/* If this variable is not a null pointer we allocated the current
Packit aea12f
   environment.  */
Packit aea12f
static char **last_environ;
Packit aea12f
Packit aea12f
Packit aea12f
/* This function is used by 'setenv' and 'putenv'.  The difference between
Packit aea12f
   the two functions is that for the former must create a new string which
Packit aea12f
   is then placed in the environment, while the argument of 'putenv'
Packit aea12f
   must be used directly.  This is all complicated by the fact that we try
Packit aea12f
   to reuse values once generated for a 'setenv' call since we can never
Packit aea12f
   free the strings.  */
Packit aea12f
int
Packit aea12f
__add_to_environ (const char *name, const char *value, const char *combined,
Packit aea12f
                  int replace)
Packit aea12f
{
Packit aea12f
  char **ep;
Packit aea12f
  size_t size;
Packit aea12f
  const size_t namelen = strlen (name);
Packit aea12f
  const size_t vallen = value != NULL ? strlen (value) + 1 : 0;
Packit aea12f
Packit aea12f
  LOCK;
Packit aea12f
Packit aea12f
  /* We have to get the pointer now that we have the lock and not earlier
Packit aea12f
     since another thread might have created a new environment.  */
Packit aea12f
  ep = __environ;
Packit aea12f
Packit aea12f
  size = 0;
Packit aea12f
  if (ep != NULL)
Packit aea12f
    {
Packit aea12f
      for (; *ep != NULL; ++ep)
Packit aea12f
        if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=')
Packit aea12f
          break;
Packit aea12f
        else
Packit aea12f
          ++size;
Packit aea12f
    }
Packit aea12f
Packit aea12f
  if (ep == NULL || *ep == NULL)
Packit aea12f
    {
Packit aea12f
      char **new_environ;
Packit aea12f
#ifdef USE_TSEARCH
Packit aea12f
      char *new_value;
Packit aea12f
#endif
Packit aea12f
Packit aea12f
      /* We allocated this space; we can extend it.  */
Packit aea12f
      new_environ =
Packit aea12f
        (char **) (last_environ == NULL
Packit aea12f
                   ? malloc ((size + 2) * sizeof (char *))
Packit aea12f
                   : realloc (last_environ, (size + 2) * sizeof (char *)));
Packit aea12f
      if (new_environ == NULL)
Packit aea12f
        {
Packit aea12f
          /* It's easier to set errno to ENOMEM than to rely on the
Packit aea12f
             'malloc-posix' and 'realloc-posix' gnulib modules.  */
Packit aea12f
          __set_errno (ENOMEM);
Packit aea12f
          UNLOCK;
Packit aea12f
          return -1;
Packit aea12f
        }
Packit aea12f
Packit aea12f
      /* If the whole entry is given add it.  */
Packit aea12f
      if (combined != NULL)
Packit aea12f
        /* We must not add the string to the search tree since it belongs
Packit aea12f
           to the user.  */
Packit aea12f
        new_environ[size] = (char *) combined;
Packit aea12f
      else
Packit aea12f
        {
Packit aea12f
          /* See whether the value is already known.  */
Packit aea12f
#ifdef USE_TSEARCH
Packit aea12f
# ifdef _LIBC
Packit aea12f
          new_value = (char *) alloca (namelen + 1 + vallen);
Packit aea12f
          __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
Packit aea12f
                     value, vallen);
Packit aea12f
# else
Packit aea12f
          new_value = (char *) malloca (namelen + 1 + vallen);
Packit aea12f
          if (new_value == NULL)
Packit aea12f
            {
Packit aea12f
              __set_errno (ENOMEM);
Packit aea12f
              UNLOCK;
Packit aea12f
              return -1;
Packit aea12f
            }
Packit aea12f
          memcpy (new_value, name, namelen);
Packit aea12f
          new_value[namelen] = '=';
Packit aea12f
          memcpy (&new_value[namelen + 1], value, vallen);
Packit aea12f
# endif
Packit aea12f
Packit aea12f
          new_environ[size] = KNOWN_VALUE (new_value);
Packit aea12f
          if (new_environ[size] == NULL)
Packit aea12f
#endif
Packit aea12f
            {
Packit aea12f
              new_environ[size] = (char *) malloc (namelen + 1 + vallen);
Packit aea12f
              if (new_environ[size] == NULL)
Packit aea12f
                {
Packit aea12f
#if defined USE_TSEARCH && !defined _LIBC
Packit aea12f
                  freea (new_value);
Packit aea12f
#endif
Packit aea12f
                  __set_errno (ENOMEM);
Packit aea12f
                  UNLOCK;
Packit aea12f
                  return -1;
Packit aea12f
                }
Packit aea12f
Packit aea12f
#ifdef USE_TSEARCH
Packit aea12f
              memcpy (new_environ[size], new_value, namelen + 1 + vallen);
Packit aea12f
#else
Packit aea12f
              memcpy (new_environ[size], name, namelen);
Packit aea12f
              new_environ[size][namelen] = '=';
Packit aea12f
              memcpy (&new_environ[size][namelen + 1], value, vallen);
Packit aea12f
#endif
Packit aea12f
              /* And save the value now.  We cannot do this when we remove
Packit aea12f
                 the string since then we cannot decide whether it is a
Packit aea12f
                 user string or not.  */
Packit aea12f
              STORE_VALUE (new_environ[size]);
Packit aea12f
            }
Packit aea12f
#if defined USE_TSEARCH && !defined _LIBC
Packit aea12f
          freea (new_value);
Packit aea12f
#endif
Packit aea12f
        }
Packit aea12f
Packit aea12f
      if (__environ != last_environ)
Packit aea12f
        memcpy ((char *) new_environ, (char *) __environ,
Packit aea12f
                size * sizeof (char *));
Packit aea12f
Packit aea12f
      new_environ[size + 1] = NULL;
Packit aea12f
Packit aea12f
      last_environ = __environ = new_environ;
Packit aea12f
    }
Packit aea12f
  else if (replace)
Packit aea12f
    {
Packit aea12f
      char *np;
Packit aea12f
Packit aea12f
      /* Use the user string if given.  */
Packit aea12f
      if (combined != NULL)
Packit aea12f
        np = (char *) combined;
Packit aea12f
      else
Packit aea12f
        {
Packit aea12f
#ifdef USE_TSEARCH
Packit aea12f
          char *new_value;
Packit aea12f
# ifdef _LIBC
Packit aea12f
          new_value = alloca (namelen + 1 + vallen);
Packit aea12f
          __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
Packit aea12f
                     value, vallen);
Packit aea12f
# else
Packit aea12f
          new_value = malloca (namelen + 1 + vallen);
Packit aea12f
          if (new_value == NULL)
Packit aea12f
            {
Packit aea12f
              __set_errno (ENOMEM);
Packit aea12f
              UNLOCK;
Packit aea12f
              return -1;
Packit aea12f
            }
Packit aea12f
          memcpy (new_value, name, namelen);
Packit aea12f
          new_value[namelen] = '=';
Packit aea12f
          memcpy (&new_value[namelen + 1], value, vallen);
Packit aea12f
# endif
Packit aea12f
Packit aea12f
          np = KNOWN_VALUE (new_value);
Packit aea12f
          if (np == NULL)
Packit aea12f
#endif
Packit aea12f
            {
Packit aea12f
              np = (char *) malloc (namelen + 1 + vallen);
Packit aea12f
              if (np == NULL)
Packit aea12f
                {
Packit aea12f
#if defined USE_TSEARCH && !defined _LIBC
Packit aea12f
                  freea (new_value);
Packit aea12f
#endif
Packit aea12f
                  __set_errno (ENOMEM);
Packit aea12f
                  UNLOCK;
Packit aea12f
                  return -1;
Packit aea12f
                }
Packit aea12f
Packit aea12f
#ifdef USE_TSEARCH
Packit aea12f
              memcpy (np, new_value, namelen + 1 + vallen);
Packit aea12f
#else
Packit aea12f
              memcpy (np, name, namelen);
Packit aea12f
              np[namelen] = '=';
Packit aea12f
              memcpy (&np[namelen + 1], value, vallen);
Packit aea12f
#endif
Packit aea12f
              /* And remember the value.  */
Packit aea12f
              STORE_VALUE (np);
Packit aea12f
            }
Packit aea12f
#if defined USE_TSEARCH && !defined _LIBC
Packit aea12f
          freea (new_value);
Packit aea12f
#endif
Packit aea12f
        }
Packit aea12f
Packit aea12f
      *ep = np;
Packit aea12f
    }
Packit aea12f
Packit aea12f
  UNLOCK;
Packit aea12f
Packit aea12f
  return 0;
Packit aea12f
}
Packit aea12f
Packit aea12f
int
Packit aea12f
setenv (const char *name, const char *value, int replace)
Packit aea12f
{
Packit aea12f
  if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
Packit aea12f
    {
Packit aea12f
      __set_errno (EINVAL);
Packit aea12f
      return -1;
Packit aea12f
    }
Packit aea12f
Packit aea12f
  return __add_to_environ (name, value, NULL, replace);
Packit aea12f
}
Packit aea12f
Packit aea12f
/* The 'clearenv' was planned to be added to POSIX.1 but probably
Packit aea12f
   never made it.  Nevertheless the POSIX.9 standard (POSIX bindings
Packit aea12f
   for Fortran 77) requires this function.  */
Packit aea12f
int
Packit aea12f
clearenv (void)
Packit aea12f
{
Packit aea12f
  LOCK;
Packit aea12f
Packit aea12f
  if (__environ == last_environ && __environ != NULL)
Packit aea12f
    {
Packit aea12f
      /* We allocated this environment so we can free it.  */
Packit aea12f
      free (__environ);
Packit aea12f
      last_environ = NULL;
Packit aea12f
    }
Packit aea12f
Packit aea12f
  /* Clear the environment pointer removes the whole environment.  */
Packit aea12f
  __environ = NULL;
Packit aea12f
Packit aea12f
  UNLOCK;
Packit aea12f
Packit aea12f
  return 0;
Packit aea12f
}
Packit aea12f
Packit aea12f
#ifdef _LIBC
Packit aea12f
static void
Packit aea12f
free_mem (void)
Packit aea12f
{
Packit aea12f
  /* Remove all traces.  */
Packit aea12f
  clearenv ();
Packit aea12f
Packit aea12f
  /* Now remove the search tree.  */
Packit aea12f
  __tdestroy (known_values, free);
Packit aea12f
  known_values = NULL;
Packit aea12f
}
Packit aea12f
text_set_element (__libc_subfreeres, free_mem);
Packit aea12f
Packit aea12f
Packit aea12f
# undef setenv
Packit aea12f
# undef clearenv
Packit aea12f
weak_alias (__setenv, setenv)
Packit aea12f
weak_alias (__clearenv, clearenv)
Packit aea12f
#endif
Packit aea12f
Packit aea12f
#endif /* _LIBC || !HAVE_SETENV */
Packit aea12f
Packit aea12f
/* The rest of this file is called into use when replacing an existing
Packit aea12f
   but buggy setenv.  Known bugs include failure to diagnose invalid
Packit aea12f
   name, and consuming a leading '=' from value.  */
Packit aea12f
#if HAVE_SETENV
Packit aea12f
Packit aea12f
# undef setenv
Packit aea12f
# if !HAVE_DECL_SETENV
Packit aea12f
extern int setenv (const char *, const char *, int);
Packit aea12f
# endif
Packit aea12f
# define STREQ(a, b) (strcmp (a, b) == 0)
Packit aea12f
Packit aea12f
int
Packit aea12f
rpl_setenv (const char *name, const char *value, int replace)
Packit aea12f
{
Packit aea12f
  int result;
Packit aea12f
  if (!name || !*name || strchr (name, '='))
Packit aea12f
    {
Packit aea12f
      errno = EINVAL;
Packit aea12f
      return -1;
Packit aea12f
    }
Packit aea12f
  /* Call the real setenv even if replace is 0, in case implementation
Packit aea12f
     has underlying data to update, such as when environ changes.  */
Packit aea12f
  result = setenv (name, value, replace);
Packit aea12f
  if (result == 0 && replace && *value == '=')
Packit aea12f
    {
Packit aea12f
      char *tmp = getenv (name);
Packit aea12f
      if (!STREQ (tmp, value))
Packit aea12f
        {
Packit aea12f
          int saved_errno;
Packit aea12f
          size_t len = strlen (value);
Packit aea12f
          tmp = malloca (len + 2);
Packit aea12f
          /* Since leading '=' is eaten, double it up.  */
Packit aea12f
          *tmp = '=';
Packit aea12f
          memcpy (tmp + 1, value, len + 1);
Packit aea12f
          result = setenv (name, tmp, replace);
Packit aea12f
          saved_errno = errno;
Packit aea12f
          freea (tmp);
Packit aea12f
          errno = saved_errno;
Packit aea12f
        }
Packit aea12f
    }
Packit aea12f
  return result;
Packit aea12f
}
Packit aea12f
Packit aea12f
#endif /* HAVE_SETENV */