diff --git a/flang/include/flang/Runtime/command.h b/flang/include/flang/Runtime/command.h --- 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 --- 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 #include @@ -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.h b/flang/runtime/environment.h --- 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/environment.cpp b/flang/runtime/environment.cpp --- a/flang/runtime/environment.cpp +++ b/flang/runtime/environment.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "environment.h" +#include "memory.h" #include "tools.h" #include #include @@ -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(eq - *var) != name_length) { - continue; - } - if (std::memcmp(*var, name, name_length) == 0) { - return eq + 1; - } - } - return nullptr; + OwningPtr cStyleName{ + SaveDefaultCharacter(name, name_length, terminator)}; + RUNTIME_CHECK(terminator, cStyleName); + + return std::getenv(cStyleName.get()); } } // namespace Fortran::runtime diff --git a/flang/runtime/tools.cpp b/flang/runtime/tools.cpp --- a/flang/runtime/tools.cpp +++ b/flang/runtime/tools.cpp @@ -10,6 +10,7 @@ #include "terminator.h" #include #include +#include #include namespace Fortran::runtime { diff --git a/flang/unittests/Runtime/CommandTest.cpp b/flang/unittests/Runtime/CommandTest.cpp --- 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 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 + 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 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(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)); + } }