#include "environment.hpp"
+#include <ccache/util/filesystem.hpp>
#include <ccache/util/format.hpp>
+#include <ccache/util/string.hpp>
#include <ccache/util/wincompat.hpp>
+namespace fs = util::filesystem;
+
namespace util {
tl::expected<std::string, std::string>
return result;
}
+#ifdef _WIN32
+
+static std::wstring
+wide_getenv(const char* name)
+{
+ std::vector<wchar_t> wname(strlen(name) + 1);
+ size_t n = mbstowcs(wname.data(), name, wname.size());
+ if (n == static_cast<size_t>(-1)) {
+ return {};
+ }
+
+ std::vector<wchar_t> 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<fs::path>
+getenv_path_list(const char* name)
+{
+ std::wstring value = wide_getenv(name);
+ if (value.empty()) {
+ return {};
+ }
+
+ std::vector<fs::path> 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<fs::path>
+getenv_path_list(const char* name)
+{
+ const char* value = getenv(name);
+ if (!value) {
+ return {};
+ }
+
+ auto strings = split_into_views(value, ":");
+ std::vector<fs::path> 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)
{
-// 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.
//
#include <tl/expected.hpp>
+#include <filesystem>
#include <string>
+#include <vector>
namespace util {
tl::expected<std::string, std::string>
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<std::filesystem::path> getenv_path_list(const char* name);
+
// Set environment variable `name` to `value`.
void setenv(const std::string& name, const std::string& value);
// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#include <ccache/util/environment.hpp>
+#include <ccache/util/filesystem.hpp>
#include <doctest/doctest.h>
+#include <vector>
+
+namespace fs = util::filesystem;
+
TEST_SUITE_BEGIN("util");
+#ifdef _WIN32
+
+TEST_CASE("util::getenv_path_list")
+{
+ SUBCASE("unset")
+ {
+ util::unsetenv("test");
+ std::vector<fs::path> expected;
+ CHECK(util::getenv_path_list("test") == expected);
+ }
+
+ SUBCASE("empty")
+ {
+ util::setenv("test", "");
+ std::vector<fs::path> expected = {};
+ CHECK(util::getenv_path_list("test") == expected);
+ }
+
+ SUBCASE("delimiters")
+ {
+ util::setenv("test", ";;");
+ std::vector<fs::path> expected = {};
+ CHECK(util::getenv_path_list("test") == expected);
+ }
+
+ SUBCASE("multiple")
+ {
+ util::setenv("test", "c:\\foo;/bar");
+ std::vector<fs::path> expected = {"c:\\foo", "/bar"};
+ CHECK(util::getenv_path_list("test") == expected);
+ }
+
+ SUBCASE("delimiters around")
+ {
+ util::setenv("test", ";c:\\foo;");
+ std::vector<fs::path> expected = {"c:\\foo"};
+ CHECK(util::getenv_path_list("test") == expected);
+ }
+}
+
+#else
+
+TEST_CASE("util::getenv_path_list")
+{
+ SUBCASE("unset")
+ {
+ util::unsetenv("test");
+ std::vector<fs::path> expected;
+ CHECK(util::getenv_path_list("test") == expected);
+ }
+
+ SUBCASE("empty")
+ {
+ util::setenv("test", "");
+ std::vector<fs::path> expected = {};
+ CHECK(util::getenv_path_list("test") == expected);
+ }
+
+ SUBCASE("delimiters")
+ {
+ util::setenv("test", "::");
+ std::vector<fs::path> expected = {};
+ CHECK(util::getenv_path_list("test") == expected);
+ }
+
+ SUBCASE("multiple")
+ {
+ util::setenv("test", "/foo:/bar");
+ std::vector<fs::path> expected = {"/foo", "/bar"};
+ CHECK(util::getenv_path_list("test") == expected);
+ }
+
+ SUBCASE("delimiters around")
+ {
+ util::setenv("test", ":/foo:");
+ std::vector<fs::path> expected = {"/foo"};
+ CHECK(util::getenv_path_list("test") == expected);
+ }
+}
+
+#endif
+
TEST_CASE("util::expand_environment_variables")
{
util::setenv("FOO", "bar");