Index: flang/include/flang/Runtime/memory.h =================================================================== --- flang/include/flang/Runtime/memory.h +++ flang/include/flang/Runtime/memory.h @@ -36,6 +36,10 @@ void operator()(A *p) { FreeMemory(p); } }; +template struct OwningPtrDeleter { + void operator()(A *p) { FreeMemory(p); } +}; + template using OwningPtr = std::unique_ptr>; template class SizedNew { Index: flang/runtime/environment.h =================================================================== --- flang/runtime/environment.h +++ flang/runtime/environment.h @@ -37,6 +37,9 @@ int listDirectedOutputLineLengthLimit; enum decimal::FortranRounding defaultOutputRoundingMode; Convert conversion; + +private: + const char *GetEnvFromEnvp(const char *name, std::size_t name_length); }; extern ExecutionEnvironment executionEnvironment; } // namespace Fortran::runtime Index: flang/runtime/environment.cpp =================================================================== --- flang/runtime/environment.cpp +++ flang/runtime/environment.cpp @@ -7,7 +7,9 @@ //===----------------------------------------------------------------------===// #include "environment.h" +#include "memory.h" #include "tools.h" +#include #include #include #include @@ -69,10 +71,9 @@ // TODO: Set RP/ROUND='PROCESSOR_DEFINED' from environment } -const char *ExecutionEnvironment::GetEnv( +const char *ExecutionEnvironment::GetEnvFromEnvp( const char *name, std::size_t name_length) { if (!envp) { - // TODO: Ask std::getenv. return nullptr; } @@ -90,6 +91,39 @@ return eq + 1; } } + return nullptr; } + +static OwningPtr NullTerminatedString( + const char *name, std::size_t name_length) { + OwningPtr result{static_cast(std::malloc(name_length + 1))}; + if (result) { + std::memcpy(result.get(), name, name_length); + result[name_length] = '\0'; + } + + return result; +} + +const char *ExecutionEnvironment::GetEnv( + const char *name, std::size_t name_length) { + assert(name_length && "Invalid environment variable name"); + + const char *value{GetEnvFromEnvp(name, name_length)}; + if (value) { + return value; + } + + // Try to read the environment directly. This is especially useful when + // ProgramStart was not invoked at all, e.g. when the main program is not + // Fortran. + OwningPtr cStyleName{NullTerminatedString(name, name_length)}; + if (!cStyleName) { + std::fputs("Fortran runtime: out of memory for env var name\n", stderr); + return nullptr; + } + + return std::getenv(cStyleName.get()); +} } // namespace Fortran::runtime Index: flang/unittests/Runtime/CommandTest.cpp =================================================================== --- flang/unittests/Runtime/CommandTest.cpp +++ 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; @@ -204,4 +205,18 @@ 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 but isn't + // passed into ProgramStart's envp argument. + char *path{std::getenv("PATH")}; + auto expectedLen{static_cast(std::strlen(path))}; + EXPECT_EQ(expectedLen, RTNAME(EnvVariableLength)(*CharDescriptor("PATH"))); +} + +TEST(EnvironmentVariablesNoProgStart, Length) { + // 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"))); }