Index: flang/include/flang/Runtime/command.h =================================================================== --- flang/include/flang/Runtime/command.h +++ 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 Index: flang/runtime/command.cpp =================================================================== --- flang/runtime/command.cpp +++ 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; } Index: flang/runtime/environment.h =================================================================== --- flang/runtime/environment.h +++ 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; @@ -37,6 +40,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,26 @@ return eq + 1; } } + return nullptr; } + +const char *ExecutionEnvironment::GetEnv( + const char *name, std::size_t name_length, const Terminator &terminator) { + 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{ + SaveDefaultCharacter(name, name_length, terminator)}; + assert(cStyleName && "Expected non-null pointer"); + + return std::getenv(cStyleName.get()); +} } // namespace Fortran::runtime Index: flang/runtime/tools.cpp =================================================================== --- flang/runtime/tools.cpp +++ flang/runtime/tools.cpp @@ -10,6 +10,7 @@ #include "terminator.h" #include #include +#include #include 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"))); }