diff --git a/flang/runtime/command.cpp b/flang/runtime/command.cpp --- a/flang/runtime/command.cpp +++ b/flang/runtime/command.cpp @@ -10,6 +10,7 @@ #include "environment.h" #include "stat.h" #include "flang/Runtime/descriptor.h" +#include #include namespace Fortran::runtime { @@ -79,4 +80,27 @@ return StatOk; } + +static std::size_t LengthWithoutTrailingSpaces(const Descriptor &d) { + std::size_t s{d.ElementBytes() - 1}; + while (*d.OffsetElement(s) == ' ') { + --s; + } + return s + 1; +} + +std::int64_t RTNAME(EnvVariableLength)(const Descriptor &name, bool trim_name) { + std::size_t nameLength{ + trim_name ? LengthWithoutTrailingSpaces(name) : name.ElementBytes()}; + if (nameLength == 0) { + return 0; + } + + const char *value{ + executionEnvironment.GetEnv(name.OffsetElement(), nameLength)}; + if (!value) { + return 0; + } + return std::strlen(value); +} } // namespace Fortran::runtime diff --git a/flang/runtime/environment.h b/flang/runtime/environment.h --- a/flang/runtime/environment.h +++ b/flang/runtime/environment.h @@ -29,6 +29,7 @@ struct ExecutionEnvironment { void Configure(int argc, const char *argv[], const char *envp[]); + const char *GetEnv(const char *name, std::size_t name_length); 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 @@ -68,4 +68,28 @@ // TODO: Set RP/ROUND='PROCESSOR_DEFINED' from environment } + +const char *ExecutionEnvironment::GetEnv( + const char *name, std::size_t name_length) { + if (!envp) { + // TODO: Ask std::getenv. + return nullptr; + } + + // 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; +} } // 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 @@ -24,12 +24,25 @@ return descriptor; } +static OwningPtr CharDescriptor(const char *value) { + std::size_t n{std::strlen(value)}; + OwningPtr descriptor{Descriptor::Create( + sizeof(char), n, nullptr, 0, nullptr, CFI_attribute_allocatable)}; + if (descriptor->Allocate() != 0) { + return nullptr; + } + std::memcpy(descriptor->OffsetElement(), value, n); + return descriptor; +} + class CommandFixture : public ::testing::Test { protected: CommandFixture(int argc, const char *argv[]) { 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"); @@ -175,3 +188,20 @@ EXPECT_GT(RTNAME(ArgumentValue)(-1, nullptr, errMsg.get()), 0); CheckDescriptorEqStr(errMsg.get(), "Inv"); } + +static const char *env[]{"NAME=value", nullptr}; +class EnvironmentVariables : public CommandFixture { +protected: + EnvironmentVariables() : CommandFixture(env) {} +}; + +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(" "))); +}