Blame gnulib-tests/setenv.c

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