core: enable environment variable for Windows

This commit is contained in:
Peter D. Barnes, Jr
2023-01-20 14:06:57 -08:00
committed by Gabriel Ferreira
parent 74035411bd
commit ce4026523a
3 changed files with 117 additions and 9 deletions

View File

@@ -21,15 +21,78 @@
#include "ns3/string.h"
#include <cstdlib> // getenv
#include <cstdlib> // std::getenv
#include <cstring> // strlen
#include <iostream> // clog
#include <stdlib.h> // Global functions setenv, unsetenv
/**
* \file
* \ingroup core-environ
* Class EnvironmentVariable implementation.
*/
#ifdef __WIN32__
#include <cerrno>
/**
* Windows implementation of the POSIX function `setenv()`
*
* \param [in] var_name The environment variable to set.
* Must not be a null-pointer, and must not contain `=`.
* \param [in] new_value The new value to set \p var_name to.
* Must not by a null pointer or empty.
* \param [in] change_flag Must be non-zero to actually change the environment.
* \returns 0 if successful, -1 if failed.
*/
int
setenv(const char* var_name, const char* new_value, int change_flag)
{
std::string variable{var_name};
std::string value{new_value};
// In case arguments are null pointers, return invalid error
// Windows does not accept empty environment variables
if (variable.empty() || value.empty())
{
errno = EINVAL;
return -1;
}
// Posix does not accept '=', so impose that here
if (variable.find('=') != std::string::npos)
{
errno = EINVAL;
return -1;
}
// Change flag equals to zero preserves a pre-existing value
if (change_flag == 0)
{
char* old_value = std::getenv(var_name);
if (old_value != nullptr)
{
return 0;
}
}
// Write new value for the environment variable
return _putenv_s(var_name, new_value);
}
/**
* Windows implementation of the POSIX function `unsetenv()`
* \param [in] var_name The environment variable to unset and remove from the environment.
* \returns 0 if successful, -1 if failed.
*/
int
unsetenv(const char* var_name)
{
return _putenv_s(var_name, "");
}
#endif // __WIN32__
namespace ns3
{
@@ -114,6 +177,22 @@ EnvironmentVariable::Get(const std::string& envvar,
return dict->Get(key);
}
/* static */
bool
EnvironmentVariable::Set(const std::string& variable, const std::string& value)
{
int fail = setenv(variable.c_str(), value.c_str(), 1);
return !fail;
}
/* static */
bool
EnvironmentVariable::Unset(const std::string& variable)
{
int fail = unsetenv(variable.c_str());
return !fail;
}
EnvironmentVariable::KeyFoundType
EnvironmentVariable::Dictionary::Get(const std::string& key) const
{

View File

@@ -167,6 +167,28 @@ class EnvironmentVariable
}; // class Dictionary
/**
* Set an environment variable.
*
* To set a variable to the empty string use `Set(variable, "")`.
* Note: empty environment variables are not portable (unsupported on Windows).
*
* \param [in] variable The environment variable to set. Note this may not contain the `=`
* character. \param [in] value The value to set. Note this must not be an empty string on
* Windows. \returns \c true if the variable was set successfully
*/
static bool Set(const std::string& variable, const std::string& value);
/**
* Unset an environment variable.
* This removes the variable from the environment.
* To set a variable to the empty string use `Set(variable, "")`.
*
* \param [in] variable The environment variable to unset. Note this may not contain the `=`
* character. \returns \c true if the variable was unset successfully.
*/
static bool Unset(const std::string& variable);
/**
* \name Singleton
*

View File

@@ -20,8 +20,7 @@
#include "ns3/environment-variable.h"
#include "ns3/test.h"
#include <cstdlib> // setenv, unsetenv
#include <stdlib.h> // getenv
#include <cstdlib> // getenv
namespace ns3
{
@@ -52,7 +51,7 @@ class EnvVarTestCase : public TestCase
EnvVarTestCase();
/** Destructor */
~EnvVarTestCase() override = default;
~EnvVarTestCase() override;
private:
/** Run the tests */
@@ -129,12 +128,17 @@ EnvVarTestCase::EnvVarTestCase()
{
}
EnvVarTestCase::~EnvVarTestCase()
{
UnsetVariable("destructor");
}
void
EnvVarTestCase::SetVariable(const std::string& where, const std::string& value)
{
EnvironmentVariable::Clear();
int ok = setenv(m_variable.c_str(), value.c_str(), 1);
NS_TEST_EXPECT_MSG_EQ(ok, 0, where << ": failed to set variable");
bool ok = EnvironmentVariable::Set(m_variable, value);
NS_TEST_EXPECT_MSG_EQ(ok, true, where << ": failed to set variable");
// Double check
const char* envCstr = std::getenv(m_variable.c_str());
@@ -146,8 +150,8 @@ void
EnvVarTestCase::UnsetVariable(const std::string& where)
{
EnvironmentVariable::Clear();
int ok = unsetenv(m_variable.c_str());
NS_TEST_EXPECT_MSG_EQ(ok, 0, where << ": failed to unset variable");
bool ok = EnvironmentVariable::Unset(where);
NS_TEST_EXPECT_MSG_EQ(ok, true, where << ": failed to unset variable");
}
void
@@ -250,7 +254,10 @@ EnvVarTestCase::DoRun()
NS_TEST_EXPECT_MSG_EQ(value.empty(), true, "unset: non-empty value from unset variable");
// Variable set but empty
#ifndef __WIN32__
// Windows doesn't support environment variables with empty values
SetCheckAndGet("empty", "", {}, "", {true, ""});
#endif
// Key not in variable
SetCheckAndGet("no-key",
@@ -307,7 +314,7 @@ EnvVarTestCase::DoRun()
/**
* \ingroup environ-var-tests
*
* TypeId test suites.
* Environment variable handling test suite.
*/
class EnvironmentVariableTestSuite : public TestSuite
{