From: Joel Rosdahl Date: Thu, 17 Oct 2024 18:44:31 +0000 (+0200) Subject: enhance: Add util::getenv_path and util::getenv_path_list X-Git-Tag: v4.11~65 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=b1ef9e1e601091f883e8bfde0804cf8e7d4681f4;p=thirdparty%2Fccache.git enhance: Add util::getenv_path and util::getenv_path_list --- diff --git a/src/ccache/util/environment.cpp b/src/ccache/util/environment.cpp index 5b1862aa..f3c01009 100644 --- a/src/ccache/util/environment.cpp +++ b/src/ccache/util/environment.cpp @@ -18,9 +18,13 @@ #include "environment.hpp" +#include #include +#include #include +namespace fs = util::filesystem; + namespace util { tl::expected @@ -79,6 +83,91 @@ expand_environment_variables(const std::string& str) return result; } +#ifdef _WIN32 + +static std::wstring +wide_getenv(const char* name) +{ + std::vector wname(strlen(name) + 1); + size_t n = mbstowcs(wname.data(), name, wname.size()); + if (n == static_cast(-1)) { + return {}; + } + + std::vector value(1024); + auto len = GetEnvironmentVariableW(wname.data(), value.data(), value.size()); + if (len == 0) { + // Variable not set. + return {}; + } + if (len >= value.size()) { + // len is the number of needed characters including the terminating null. + value.resize(len); + len = GetEnvironmentVariableW(wname.data(), value.data(), value.size()); + } + // len is the number of characters excluding the terminating null + return std::wstring(value.data(), len); +} + +fs::path +getenv_path(const char* name) +{ + return fs::path(wide_getenv(name)); +} + +std::vector +getenv_path_list(const char* name) +{ + std::wstring value = wide_getenv(name); + if (value.empty()) { + return {}; + } + + std::vector result; + std::wstring_view view(value); + size_t left = 0; + while (left < view.size()) { + size_t right = view.find(';', left); + if (right == std::wstring_view::npos) { + right = view.length(); + } + std::wstring_view path = view.substr(left, right - left); + if (!path.empty()) { + result.push_back(fs::path(path)); + } + if (right == std::wstring_view::npos) { + break; + } + left = right + 1; + } + return result; +} + +#else // _WIN32 + +fs::path +getenv_path(const char* name) +{ + const char* value = getenv(name); + return value ? value : ""; +} + +std::vector +getenv_path_list(const char* name) +{ + const char* value = getenv(name); + if (!value) { + return {}; + } + + auto strings = split_into_views(value, ":"); + std::vector paths; + std::copy(strings.cbegin(), strings.cend(), std::back_inserter(paths)); + return paths; +} + +#endif //_WIN32 + void setenv(const std::string& name, const std::string& value) { diff --git a/src/ccache/util/environment.hpp b/src/ccache/util/environment.hpp index 771c2138..252fea9e 100644 --- a/src/ccache/util/environment.hpp +++ b/src/ccache/util/environment.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 Joel Rosdahl and other contributors +// Copyright (C) 2023-2024 Joel Rosdahl and other contributors // // See doc/AUTHORS.adoc for a complete list of contributors. // @@ -20,7 +20,9 @@ #include +#include #include +#include namespace util { @@ -29,6 +31,13 @@ namespace util { tl::expected expand_environment_variables(const std::string& str); +// Get value of environment variable `name` as a path. +std::filesystem::path getenv_path(const char* name); + +// Get value of environment variable `name` as a vector of paths where the value +// is delimited by ';' on Windows and ':' on other systems.. +std::vector getenv_path_list(const char* name); + // Set environment variable `name` to `value`. void setenv(const std::string& name, const std::string& value); diff --git a/unittest/test_util_environment.cpp b/unittest/test_util_environment.cpp index 2d60a707..4a4fa030 100644 --- a/unittest/test_util_environment.cpp +++ b/unittest/test_util_environment.cpp @@ -17,11 +17,98 @@ // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include +#include #include +#include + +namespace fs = util::filesystem; + TEST_SUITE_BEGIN("util"); +#ifdef _WIN32 + +TEST_CASE("util::getenv_path_list") +{ + SUBCASE("unset") + { + util::unsetenv("test"); + std::vector expected; + CHECK(util::getenv_path_list("test") == expected); + } + + SUBCASE("empty") + { + util::setenv("test", ""); + std::vector expected = {}; + CHECK(util::getenv_path_list("test") == expected); + } + + SUBCASE("delimiters") + { + util::setenv("test", ";;"); + std::vector expected = {}; + CHECK(util::getenv_path_list("test") == expected); + } + + SUBCASE("multiple") + { + util::setenv("test", "c:\\foo;/bar"); + std::vector expected = {"c:\\foo", "/bar"}; + CHECK(util::getenv_path_list("test") == expected); + } + + SUBCASE("delimiters around") + { + util::setenv("test", ";c:\\foo;"); + std::vector expected = {"c:\\foo"}; + CHECK(util::getenv_path_list("test") == expected); + } +} + +#else + +TEST_CASE("util::getenv_path_list") +{ + SUBCASE("unset") + { + util::unsetenv("test"); + std::vector expected; + CHECK(util::getenv_path_list("test") == expected); + } + + SUBCASE("empty") + { + util::setenv("test", ""); + std::vector expected = {}; + CHECK(util::getenv_path_list("test") == expected); + } + + SUBCASE("delimiters") + { + util::setenv("test", "::"); + std::vector expected = {}; + CHECK(util::getenv_path_list("test") == expected); + } + + SUBCASE("multiple") + { + util::setenv("test", "/foo:/bar"); + std::vector expected = {"/foo", "/bar"}; + CHECK(util::getenv_path_list("test") == expected); + } + + SUBCASE("delimiters around") + { + util::setenv("test", ":/foo:"); + std::vector expected = {"/foo"}; + CHECK(util::getenv_path_list("test") == expected); + } +} + +#endif + TEST_CASE("util::expand_environment_variables") { util::setenv("FOO", "bar");