Index: flang/runtime/command.cpp =================================================================== --- flang/runtime/command.cpp +++ 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,23 @@ 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()}; + const char *value{ + executionEnvironment.GetEnv(name.OffsetElement(), nameLength)}; + if (!value) { + return 0; + } + return std::strlen(value); +} } // namespace Fortran::runtime Index: flang/runtime/environment.h =================================================================== --- flang/runtime/environment.h +++ 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; Index: flang/runtime/environment.cpp =================================================================== --- flang/runtime/environment.cpp +++ flang/runtime/environment.cpp @@ -68,4 +68,23 @@ // TODO: Set RP/ROUND='PROCESSOR_DEFINED' from environment } + +const char *ExecutionEnvironment::GetEnv( + const char *name, std::size_t 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; +} } // namespace Fortran::runtime Index: flang/unittests/Runtime/CommandTest.cpp =================================================================== --- flang/unittests/Runtime/CommandTest.cpp +++ 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,22 @@ 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"), /*trim_name=*/true)); + EXPECT_EQ(0, + RTNAME(EnvVariableLength)( + *CharDescriptor("DOESNT_EXIST"), /*trim_name=*/true)); + + EXPECT_EQ(5, + RTNAME(EnvVariableLength)(*CharDescriptor("NAME "), /*trim_name=*/true)); + EXPECT_EQ(0, + RTNAME(EnvVariableLength)(*CharDescriptor("NAME "), /*trim_name=*/false)); +}