/* ========================================================================== */
/*! \file
 * \brief Thread safe replacement functions
 *
 * Copyright (c) 2012-2022 by the developers. See the LICENSE file for details.
 * If nothing else is specified, functions return zero to indicate success
 * and a negative value to indicate an error.
 */


/* ========================================================================== */
/* Include headers */

#include "posix.h"  /* Include this first because of feature test macros */

#include "main.h"


/* ========================================================================== */
/*! \addtogroup MAIN
 *
 * The POSIX \c getenv() function is not required to be thread safe. Therefore
 * we must provide our own. It was considered to clone the getenv_r() function
 * from BSD, but this function has no buffer management and is inconvenient to
 * use. The function \c ts_getenv() dynamically allocate or resize the thread
 * local buffer internally.
 *
 * \note
 * A copy of the enviroment is used, therefore all modifications of the
 * enviroment at runtime are invisible via \c ts_getenv(). \c ts_environ_init()
 * and \c ts_environ_exit() must be called to create and destroy this copy of
 * the enviroment.
 */
/*! @{ */


/* ========================================================================== */
/* Constants */

/*! \brief Message prefix for MAIN module */
#define MAIN_ERR_PREFIX  "MAIN: "


/* ========================================================================== */
/* Variables */

static char**  environ_copy = NULL;
static char*  environ_data = NULL;


/* ========================================================================== */
/*! \brief Copy environment variables
 *
 * Must be called once before \c ts_getenv() is used and before additional
 * threads are created.
 */

void  ts_environ_init(void)
{
   size_t  i = 0;
   size_t  di;
   size_t  len = 0;
   size_t  alen;

   /* Calculate size of strings */
   while(NULL != posix_environ[i])
   {
      len += strlen(posix_environ[i]) + (size_t) 1;
      ++i;
   }
   /* Calculate size of NULL-terminated pointer array */
   alen = sizeof(char*) * ++i;

   /* Allocate memory for pointer array */
   environ_copy = (char**) malloc(alen);
   if(NULL != environ_copy)
   {
      environ_copy[0] = NULL;

      /* Allocate memory for string data */
      environ_data = (char*) malloc(len);
      if(NULL != environ_data)
      {
         i = 0;
         di = 0;
         while(NULL != posix_environ[i])
         {
            strcpy(&environ_data[di], posix_environ[i]);
            environ_copy[i] = &environ_data[di];
            di += strlen(posix_environ[i]) + (size_t) 1;
            ++i;
         }
         environ_copy[i] = NULL;
      }
   }

   return;
}


/* ========================================================================== */
/*! \brief Destroy copy of environment variables
 *
 * Call this function once after last use of \c ts_getenv() .
 */

void  ts_environ_exit(void)
{
   posix_free((void*) environ_data);
   posix_free((void*) environ_copy);

   return;
}


/* ========================================================================== */
/*! \brief Thread safe replacement for \c getenv()
 *
 * \param[in]     name  Name of the requested environment variable
 * \param[in,out] buf   Pointer to pointer to buffer for result
 *
 * Dereferenced \e buf must be either \c NULL or an existing dynamically
 * allocated buffer that can be resized with \c realloc() if required.
 *
 * The buffer pointed to by \e buf is created or resized so that it can hold the
 * result and the value of the environment variable \e name is copied to it.
 *
 * The caller is responsible to free the memory allocated for the buffer on
 * success.
 *
 * \return
 * - 0 on success
 * - Negative value on error
 */

int  ts_getenv(const char*  name, const char**  buf)
{
   int  res = -1;
   size_t  i;
   size_t  len;
   char*  content;
   char*  tmp;

   if(NULL != environ_copy && NULL != buf)
   {
      /* Search for variable in copy of environment */
      i = 0;
      while(NULL != environ_copy[i])
      {
         len = strlen(name);
         if(!strncmp(environ_copy[i], name, len))
         {
            if('=' == environ_copy[i][len])
            {
               /* Found => Copy content to target buffer */
               content = &environ_copy[i][++len];
               len = strlen(content);
               tmp = (char*) posix_realloc((void*) *buf, ++len);
               if(NULL != tmp)
               {
                  strcpy(tmp, content);
                  *buf = tmp;
                  res = 0;
               }
               break;
            }
         }
         ++i;
      }
      /* Check for error */
      if(res)
      {
         posix_free((void*) *buf);
         *buf = NULL;
      }
   }

   return(res);
}


/*! @} */

/* EOF */
