Skip to content

Commit ea3675f

Browse files
authored
Add new API to set envar while specifying overwrite (#473)
* Add rcutils_set_env_overwrite api Signed-off-by: Yadunund <[email protected]> * Add some tests Signed-off-by: Yadunund <[email protected]> --------- Signed-off-by: Yadunund <[email protected]>
1 parent 7fdf30c commit ea3675f

File tree

3 files changed

+75
-12
lines changed

3 files changed

+75
-12
lines changed

include/rcutils/env.h

+30
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,36 @@ RCUTILS_WARN_UNUSED
5555
bool
5656
rcutils_set_env(const char * env_name, const char * env_value);
5757

58+
/// Set or un-set a process-scoped environment variable while specifying overwrite behavior.
59+
/**
60+
* This function modifies the environment variables for the current process by
61+
* copying given string values into the process' global environment variable
62+
* store.
63+
*
64+
* \par Thread Safety:
65+
* This function is not thread-safe. Take care not to modify the environment variables while
66+
* another thread might be reading or writing environment variables.
67+
*
68+
* \par Platform Consistency:
69+
* The behavior when setting a variable to an empty string (`""`) differs
70+
* between platforms. On Windows, the variable is un-set (as if \p env_value was
71+
* `NULL`), while on other platforms the variable is set to an empty string as
72+
* expected.
73+
*
74+
* \param[in] env_name Name of the environment variable to modify.
75+
* \param[in] env_value Value to set the environment variable to, or `NULL` to
76+
* un-set.
77+
* \param[in] overwrite If true, the environemnt variable value will not be overwritten
78+
* if previously set.
79+
* \return `true` if success, or
80+
* \return `false` if env_name is invalid or NULL, or
81+
* \return `false` on failure.
82+
*/
83+
RCUTILS_PUBLIC
84+
RCUTILS_WARN_UNUSED
85+
bool
86+
rcutils_set_env_overwrite(const char * env_name, const char * env_value, bool overwrite);
87+
5888
/// Retrieve the value of the given environment variable if it exists, or "".
5989
/** The c-string which is returned in the env_value output parameter is only
6090
* valid until the next time this function is called, because it is a direct

src/env.c

+19-12
Original file line numberDiff line numberDiff line change
@@ -26,34 +26,41 @@ extern "C"
2626

2727
bool
2828
rcutils_set_env(const char * env_name, const char * env_value)
29+
{
30+
return rcutils_set_env_overwrite(env_name, env_value, true);
31+
}
32+
33+
bool
34+
rcutils_set_env_overwrite(const char * env_name, const char * env_value, bool overwrite)
2935
{
3036
RCUTILS_CAN_RETURN_WITH_ERROR_OF(false);
3137

3238
RCUTILS_CHECK_FOR_NULL_WITH_MSG(
3339
env_name, "env_name is null", return false);
3440

41+
if ((int)overwrite == 0 && getenv(env_name) != NULL) {
42+
return true;
43+
}
44+
45+
int set_ret;
3546
#ifdef _WIN32
3647
if (NULL == env_value) {
3748
env_value = "";
3849
}
39-
if (0 != _putenv_s(env_name, env_value)) {
40-
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING("_putenv_s failed: %d", errno);
41-
return false;
42-
}
50+
set_ret = _putenv_s(env_name, env_value);
4351
#else
4452
if (NULL == env_value) {
45-
if (0 != unsetenv(env_name)) {
46-
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING("unsetenv failed: %d", errno);
47-
return false;
48-
}
53+
set_ret = unsetenv(env_name);
4954
} else {
50-
if (0 != setenv(env_name, env_value, 1)) {
51-
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING("setenv failed: %d", errno);
52-
return false;
53-
}
55+
set_ret = setenv(env_name, env_value, (int) overwrite);
5456
}
5557
#endif
5658

59+
if (set_ret != 0) {
60+
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING("setting environment variable failed: %d", errno);
61+
return false;
62+
}
63+
5764
return true;
5865
}
5966

test/test_env.cpp

+26
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,32 @@ TEST(TestEnv, test_get_env) {
8484
EXPECT_STREQ("", env);
8585
}
8686

87+
/* Tests rcutils_get_env.
88+
*
89+
* Expected environment variables must be set by the calling code:
90+
*
91+
* - EMPTY_TEST=
92+
* - NORMAL_TEST=foo
93+
*
94+
* These are set in the call to `ament_add_gtest()` in the `CMakeLists.txt`.
95+
*/
96+
TEST(TestEnv, test_set_env_overwrite) {
97+
const char * env;
98+
const char * ret;
99+
100+
// Do not overwrite environment variable if preset if overwrite is set false.
101+
EXPECT_TRUE(rcutils_set_env_overwrite("NORMAL_TEST", "NewEnvValue", false));
102+
ret = rcutils_get_env("NORMAL_TEST", &env);
103+
EXPECT_TRUE(NULL == ret);
104+
EXPECT_STREQ("foo", env);
105+
106+
// Overwrite environment variable if present if overwrite is set true.
107+
EXPECT_TRUE(rcutils_set_env_overwrite("NORMAL_TEST", "NewEnvValue", true));
108+
ret = rcutils_get_env("NORMAL_TEST", &env);
109+
EXPECT_TRUE(NULL == ret);
110+
EXPECT_STREQ("NewEnvValue", env);
111+
}
112+
87113
TEST(TestEnv, test_get_home) {
88114
EXPECT_STRNE(NULL, rcutils_get_home_dir());
89115
const char * home = NULL;

0 commit comments

Comments
 (0)