[flang] runtime: Read environment variables directly
Add support for reading environment variables directly, via std::getenv.
This needs to allocate a C-style string to pass into std::getenv. If the
memory allocation for that fails, we terminate.
This also changes the interface for EnvVariableLength to receive the
source file and line so we can crash gracefully.
Note that we are now completely ignoring the envp pointer passed into
ProgramStart, since that could go stale if the environment is modified
during execution.
Differential Revision: https://reviews.llvm.org/D111785
diff --git a/flang/include/flang/Runtime/command.h b/flang/include/flang/Runtime/command.h
index 45a9f112..f664599 100644
--- a/flang/include/flang/Runtime/command.h
+++ b/flang/include/flang/Runtime/command.h
@@ -48,8 +48,8 @@
// Try to get the significant length of the environment variable specified by
// NAME. Returns 0 if it doesn't manage.
-std::int64_t RTNAME(EnvVariableLength)(
- const Descriptor &name, bool trim_name = true);
+std::int64_t RTNAME(EnvVariableLength)(const Descriptor &name,
+ bool trim_name = true, const char *sourceFile = nullptr, int line = 0);
}
} // namespace Fortran::runtime
diff --git a/flang/runtime/command.cpp b/flang/runtime/command.cpp
index 81daf64..325fc27 100644
--- a/flang/runtime/command.cpp
+++ b/flang/runtime/command.cpp
@@ -9,6 +9,7 @@
#include "flang/Runtime/command.h"
#include "environment.h"
#include "stat.h"
+#include "terminator.h"
#include "flang/Runtime/descriptor.h"
#include <cstdlib>
#include <limits>
@@ -89,15 +90,17 @@
return s + 1;
}
-std::int64_t RTNAME(EnvVariableLength)(const Descriptor &name, bool trim_name) {
+std::int64_t RTNAME(EnvVariableLength)(
+ const Descriptor &name, bool trim_name, const char *sourceFile, int line) {
std::size_t nameLength{
trim_name ? LengthWithoutTrailingSpaces(name) : name.ElementBytes()};
if (nameLength == 0) {
return 0;
}
- const char *value{
- executionEnvironment.GetEnv(name.OffsetElement(), nameLength)};
+ Terminator terminator{sourceFile, line};
+ const char *value{executionEnvironment.GetEnv(
+ name.OffsetElement(), nameLength, terminator)};
if (!value) {
return 0;
}
diff --git a/flang/runtime/environment.cpp b/flang/runtime/environment.cpp
index 2ba8faf..491906b 100644
--- a/flang/runtime/environment.cpp
+++ b/flang/runtime/environment.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "environment.h"
+#include "memory.h"
#include "tools.h"
#include <cstdio>
#include <cstdlib>
@@ -70,26 +71,13 @@
}
const char *ExecutionEnvironment::GetEnv(
- const char *name, std::size_t name_length) {
- if (!envp) {
- // TODO: Ask std::getenv.
- return nullptr;
- }
+ const char *name, std::size_t name_length, const Terminator &terminator) {
+ RUNTIME_CHECK(terminator, name && name_length);
- // envp is an array of strings of the form "name=value".
- for (const char **var{envp}; *var != nullptr; ++var) {
- const char *eq{std::strchr(*var, '=')};
- if (!eq) {
- // Found a malformed environment string, just ignore it.
- continue;
- }
- if (static_cast<std::size_t>(eq - *var) != name_length) {
- continue;
- }
- if (std::memcmp(*var, name, name_length) == 0) {
- return eq + 1;
- }
- }
- return nullptr;
+ OwningPtr<char> cStyleName{
+ SaveDefaultCharacter(name, name_length, terminator)};
+ RUNTIME_CHECK(terminator, cStyleName);
+
+ return std::getenv(cStyleName.get());
}
} // namespace Fortran::runtime
diff --git a/flang/runtime/environment.h b/flang/runtime/environment.h
index 1d04cb1..bc3a4e3 100644
--- a/flang/runtime/environment.h
+++ b/flang/runtime/environment.h
@@ -14,6 +14,8 @@
namespace Fortran::runtime {
+class Terminator;
+
#if FLANG_BIG_ENDIAN
constexpr bool isHostLittleEndian{false};
#elif FLANG_LITTLE_ENDIAN
@@ -29,7 +31,8 @@
struct ExecutionEnvironment {
void Configure(int argc, const char *argv[], const char *envp[]);
- const char *GetEnv(const char *name, std::size_t name_length);
+ const char *GetEnv(
+ const char *name, std::size_t name_length, const Terminator &terminator);
int argc;
const char **argv;
diff --git a/flang/runtime/tools.cpp b/flang/runtime/tools.cpp
index f281f13..f9b804e 100644
--- a/flang/runtime/tools.cpp
+++ b/flang/runtime/tools.cpp
@@ -10,6 +10,7 @@
#include "terminator.h"
#include <algorithm>
#include <cstdint>
+#include <cstdlib>
#include <cstring>
namespace Fortran::runtime {
diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp
index 194860f..cfbbd8a 100644
--- a/flang/unittests/Runtime/CommandTest.cpp
+++ b/flang/unittests/Runtime/CommandTest.cpp
@@ -11,6 +11,7 @@
#include "gtest/gtest.h"
#include "flang/Runtime/descriptor.h"
#include "flang/Runtime/main.h"
+#include <cstdlib>
using namespace Fortran::runtime;
@@ -41,8 +42,6 @@
RTNAME(ProgramStart)(argc, argv, {});
}
- CommandFixture(const char *envp[]) { RTNAME(ProgramStart)(0, nullptr, envp); }
-
std::string GetPaddedStr(const char *text, std::size_t len) const {
std::string res{text};
assert(res.length() <= len && "No room to pad");
@@ -189,19 +188,46 @@
CheckDescriptorEqStr(errMsg.get(), "Inv");
}
-static const char *env[]{"NAME=value", nullptr};
class EnvironmentVariables : public CommandFixture {
protected:
- EnvironmentVariables() : CommandFixture(env) {}
+ EnvironmentVariables() : CommandFixture(0, nullptr) {
+ SetEnv("NAME", "VALUE");
+ }
+
+ // If we have access to setenv, we can run some more fine-grained tests.
+ template <typename ParamType = char>
+ void SetEnv(const ParamType *name, const ParamType *value,
+ decltype(setenv(name, value, 1)) *Enabled = nullptr) {
+ ASSERT_EQ(0, setenv(name, value, /*overwrite=*/1));
+ canSetEnv = true;
+ }
+
+ // Fallback method if setenv is not available.
+ template <typename Unused = void> void SetEnv(const void *, const void *) {}
+
+ bool EnableFineGrainedTests() const { return canSetEnv; }
+
+private:
+ bool canSetEnv{false};
};
TEST_F(EnvironmentVariables, Length) {
- EXPECT_EQ(5, RTNAME(EnvVariableLength)(*CharDescriptor("NAME")));
EXPECT_EQ(0, RTNAME(EnvVariableLength)(*CharDescriptor("DOESNT_EXIST")));
- EXPECT_EQ(5, RTNAME(EnvVariableLength)(*CharDescriptor("NAME ")));
- EXPECT_EQ(0,
- RTNAME(EnvVariableLength)(*CharDescriptor("NAME "), /*trim_name=*/false));
-
EXPECT_EQ(0, RTNAME(EnvVariableLength)(*CharDescriptor(" ")));
+ EXPECT_EQ(0, RTNAME(EnvVariableLength)(*CharDescriptor("")));
+
+ // Test a variable that's expected to exist in the environment.
+ char *path{std::getenv("PATH")};
+ auto expectedLen{static_cast<int64_t>(std::strlen(path))};
+ EXPECT_EQ(expectedLen, RTNAME(EnvVariableLength)(*CharDescriptor("PATH")));
+
+ if (EnableFineGrainedTests()) {
+ EXPECT_EQ(5, RTNAME(EnvVariableLength)(*CharDescriptor("NAME")));
+
+ EXPECT_EQ(5, RTNAME(EnvVariableLength)(*CharDescriptor("NAME ")));
+ EXPECT_EQ(0,
+ RTNAME(EnvVariableLength)(
+ *CharDescriptor("NAME "), /*trim_name=*/false));
+ }
}