#include <folly/portability/Stdlib.h>
#include <folly/portability/Unistd.h>
-namespace folly {
-namespace test {
+using namespace folly;
+using namespace folly::experimental;
-static std::map<std::string, std::string> getEnvVarMap() {
- std::map<std::string, std::string> data;
- for (auto it = environ; *it != nullptr; ++it) {
+EnvironmentState EnvironmentState::fromCurrentEnvironment() {
+ std::unordered_map<std::string, std::string> data;
+ for (auto it = environ; it && *it; ++it) {
std::string key, value;
- split("=", *it, key, value);
- if (key.empty()) {
- continue;
+ folly::StringPiece entry(*it);
+ auto equalsPosition = entry.find('=');
+ if (equalsPosition == entry.npos) {
+ throw MalformedEnvironment{to<std::string>(
+ "Environment contains an non key-value-pair string \"", entry, "\"")};
}
- CHECK(!data.count(key)) << "already contains: " << key;
- data.emplace(move(key), move(value));
+ key = entry.subpiece(0, equalsPosition).toString();
+ value = entry.subpiece(equalsPosition + 1).toString();
+ if (data.count(key)) {
+ throw MalformedEnvironment{to<std::string>(
+ "Environment contains duplicate value for \"", key, "\"")};
+ }
+ data.emplace(std::move(key), std::move(value));
}
- return data;
-}
-
-EnvVarSaver::EnvVarSaver() {
- saved_ = getEnvVarMap();
+ return EnvironmentState{std::move(data)};
}
-EnvVarSaver::~EnvVarSaver() {
- for (const auto& kvp : getEnvVarMap()) {
- if (saved_.count(kvp.first)) {
- continue;
- }
- PCHECK(0 == unsetenv(kvp.first.c_str()));
- }
- for (const auto& kvp : saved_) {
+void EnvironmentState::setAsCurrentEnvironment() {
+ PCHECK(0 == clearenv());
+ for (const auto& kvp : env_) {
PCHECK(0 == setenv(kvp.first.c_str(), kvp.second.c_str(), (int)true));
}
}
+
+std::vector<std::string> EnvironmentState::toVector() const {
+ std::vector<std::string> result;
+ for (auto const& pair : env_) {
+ result.emplace_back(to<std::string>(pair.first, "=", pair.second));
+ }
+ return result;
}
+
+std::unique_ptr<char*, void (*)(char**)> EnvironmentState::toPointerArray()
+ const {
+ size_t totalStringLength{};
+ for (auto const& pair : env_) {
+ totalStringLength += pair.first.size() + pair.second.size() +
+ 2 /* intermediate '=' and the terminating NUL */;
+ }
+ size_t allocationRequired =
+ (totalStringLength / sizeof(char*) + 1) + env_.size() + 1;
+ char** raw = new char*[allocationRequired];
+ char** ptrBase = raw;
+ char* stringBase = reinterpret_cast<char*>(&raw[env_.size() + 1]);
+ char* const stringEnd = reinterpret_cast<char*>(&raw[allocationRequired]);
+ for (auto const& pair : env_) {
+ std::string const& key = pair.first;
+ std::string const& value = pair.second;
+ *ptrBase = stringBase;
+ size_t lengthIncludingNullTerminator = key.size() + 1 + value.size() + 1;
+ CHECK_GT(stringEnd - lengthIncludingNullTerminator, stringBase);
+ memcpy(stringBase, key.c_str(), key.size());
+ stringBase += key.size();
+ *stringBase++ = '=';
+ memcpy(stringBase, value.c_str(), value.size() + 1);
+ stringBase += value.size() + 1;
+ ++ptrBase;
+ }
+ *ptrBase = nullptr;
+ CHECK_EQ(env_.size(), ptrBase - raw);
+ return {raw, [](char** ptr) { delete[] ptr; }};
}