| //===-- runtime/command.cpp -----------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "flang/Runtime/command.h" |
| #include "environment.h" |
| #include "stat.h" |
| #include "terminator.h" |
| #include "tools.h" |
| #include "flang/Runtime/descriptor.h" |
| #include <cstdlib> |
| #include <limits> |
| |
| #ifdef _WIN32 |
| #include "flang/Common/windows-include.h" |
| #include <direct.h> |
| #define getcwd _getcwd |
| #define PATH_MAX MAX_PATH |
| |
| // On Windows GetCurrentProcessId returns a DWORD aka uint32_t |
| #include <processthreadsapi.h> |
| inline pid_t getpid() { return GetCurrentProcessId(); } |
| #else |
| #include <unistd.h> //getpid() |
| |
| #ifndef PATH_MAX |
| #define PATH_MAX 4096 |
| #endif |
| #endif |
| |
| namespace Fortran::runtime { |
| std::int32_t RTNAME(ArgumentCount)() { |
| int argc{executionEnvironment.argc}; |
| if (argc > 1) { |
| // C counts the command name as one of the arguments, but Fortran doesn't. |
| return argc - 1; |
| } |
| return 0; |
| } |
| |
| pid_t RTNAME(GetPID)() { return getpid(); } |
| |
| // Returns the length of the \p string. Assumes \p string is valid. |
| static std::int64_t StringLength(const char *string) { |
| std::size_t length{std::strlen(string)}; |
| if (length <= std::numeric_limits<std::int64_t>::max()) |
| return static_cast<std::int64_t>(length); |
| return 0; |
| } |
| |
| static void FillWithSpaces(const Descriptor &value, std::size_t offset = 0) { |
| if (offset < value.ElementBytes()) { |
| std::memset( |
| value.OffsetElement(offset), ' ', value.ElementBytes() - offset); |
| } |
| } |
| |
| static std::int32_t CheckAndCopyCharsToDescriptor(const Descriptor *value, |
| const char *rawValue, const Descriptor *errmsg, std::size_t &offset) { |
| bool haveValue{IsValidCharDescriptor(value)}; |
| |
| std::int64_t len{StringLength(rawValue)}; |
| if (len <= 0) { |
| if (haveValue) { |
| FillWithSpaces(*value); |
| } |
| return ToErrmsg(errmsg, StatMissingArgument); |
| } |
| |
| std::int32_t stat{StatOk}; |
| if (haveValue) { |
| stat = CopyCharsToDescriptor(*value, rawValue, len, errmsg, offset); |
| } |
| |
| offset += len; |
| return stat; |
| } |
| |
| template <int KIND> struct FitsInIntegerKind { |
| bool operator()([[maybe_unused]] std::int64_t value) { |
| if constexpr (KIND >= 8) { |
| return true; |
| } else { |
| return value <= std::numeric_limits<Fortran::runtime::CppTypeFor< |
| Fortran::common::TypeCategory::Integer, KIND>>::max(); |
| } |
| } |
| }; |
| |
| static bool FitsInDescriptor( |
| const Descriptor *length, std::int64_t value, Terminator &terminator) { |
| auto typeCode{length->type().GetCategoryAndKind()}; |
| int kind{typeCode->second}; |
| return Fortran::runtime::ApplyIntegerKind<FitsInIntegerKind, bool>( |
| kind, terminator, value); |
| } |
| |
| std::int32_t RTNAME(GetCommandArgument)(std::int32_t n, const Descriptor *value, |
| const Descriptor *length, const Descriptor *errmsg, const char *sourceFile, |
| int line) { |
| Terminator terminator{sourceFile, line}; |
| |
| if (value) { |
| RUNTIME_CHECK(terminator, IsValidCharDescriptor(value)); |
| FillWithSpaces(*value); |
| } |
| |
| // Store 0 in case we error out later on. |
| if (length) { |
| RUNTIME_CHECK(terminator, IsValidIntDescriptor(length)); |
| StoreIntToDescriptor(length, 0, terminator); |
| } |
| |
| if (n < 0 || n >= executionEnvironment.argc) { |
| return ToErrmsg(errmsg, StatInvalidArgumentNumber); |
| } |
| |
| const char *arg{executionEnvironment.argv[n]}; |
| std::int64_t argLen{StringLength(arg)}; |
| if (argLen <= 0) { |
| return ToErrmsg(errmsg, StatMissingArgument); |
| } |
| |
| if (length && FitsInDescriptor(length, argLen, terminator)) { |
| StoreIntToDescriptor(length, argLen, terminator); |
| } |
| |
| if (value) { |
| return CopyCharsToDescriptor(*value, arg, argLen, errmsg); |
| } |
| |
| return StatOk; |
| } |
| |
| std::int32_t RTNAME(GetCommand)(const Descriptor *value, |
| const Descriptor *length, const Descriptor *errmsg, const char *sourceFile, |
| int line) { |
| Terminator terminator{sourceFile, line}; |
| |
| if (value) { |
| RUNTIME_CHECK(terminator, IsValidCharDescriptor(value)); |
| } |
| |
| // Store 0 in case we error out later on. |
| if (length) { |
| RUNTIME_CHECK(terminator, IsValidIntDescriptor(length)); |
| StoreIntToDescriptor(length, 0, terminator); |
| } |
| |
| auto shouldContinue = [&](std::int32_t stat) -> bool { |
| // We continue as long as everything is ok OR the value descriptor is |
| // too short, but we still need to compute the length. |
| return stat == StatOk || (length && stat == StatValueTooShort); |
| }; |
| |
| std::size_t offset{0}; |
| |
| if (executionEnvironment.argc == 0) { |
| return CheckAndCopyCharsToDescriptor(value, "", errmsg, offset); |
| } |
| |
| // value = argv[0] |
| std::int32_t stat{CheckAndCopyCharsToDescriptor( |
| value, executionEnvironment.argv[0], errmsg, offset)}; |
| if (!shouldContinue(stat)) { |
| return stat; |
| } |
| |
| // value += " " + argv[1:n] |
| for (std::int32_t i{1}; i < executionEnvironment.argc; ++i) { |
| stat = CheckAndCopyCharsToDescriptor(value, " ", errmsg, offset); |
| if (!shouldContinue(stat)) { |
| return stat; |
| } |
| |
| stat = CheckAndCopyCharsToDescriptor( |
| value, executionEnvironment.argv[i], errmsg, offset); |
| if (!shouldContinue(stat)) { |
| return stat; |
| } |
| } |
| |
| if (length && FitsInDescriptor(length, offset, terminator)) { |
| StoreIntToDescriptor(length, offset, terminator); |
| } |
| |
| // value += spaces for padding |
| if (value) { |
| FillWithSpaces(*value, offset); |
| } |
| |
| return stat; |
| } |
| |
| static std::size_t LengthWithoutTrailingSpaces(const Descriptor &d) { |
| std::size_t s{d.ElementBytes()}; // This can be 0. |
| while (s != 0 && *d.OffsetElement(s - 1) == ' ') { |
| --s; |
| } |
| return s; |
| } |
| |
| std::int32_t RTNAME(GetEnvVariable)(const Descriptor &name, |
| const Descriptor *value, const Descriptor *length, bool trim_name, |
| const Descriptor *errmsg, const char *sourceFile, int line) { |
| Terminator terminator{sourceFile, line}; |
| |
| if (value) { |
| RUNTIME_CHECK(terminator, IsValidCharDescriptor(value)); |
| FillWithSpaces(*value); |
| } |
| |
| // Store 0 in case we error out later on. |
| if (length) { |
| RUNTIME_CHECK(terminator, IsValidIntDescriptor(length)); |
| StoreIntToDescriptor(length, 0, terminator); |
| } |
| |
| const char *rawValue{nullptr}; |
| std::size_t nameLength{ |
| trim_name ? LengthWithoutTrailingSpaces(name) : name.ElementBytes()}; |
| if (nameLength != 0) { |
| rawValue = executionEnvironment.GetEnv( |
| name.OffsetElement(), nameLength, terminator); |
| } |
| if (!rawValue) { |
| return ToErrmsg(errmsg, StatMissingEnvVariable); |
| } |
| |
| std::int64_t varLen{StringLength(rawValue)}; |
| if (length && FitsInDescriptor(length, varLen, terminator)) { |
| StoreIntToDescriptor(length, varLen, terminator); |
| } |
| |
| if (value) { |
| return CopyCharsToDescriptor(*value, rawValue, varLen, errmsg); |
| } |
| return StatOk; |
| } |
| |
| std::int32_t RTNAME(GetCwd)( |
| const Descriptor &cwd, const char *sourceFile, int line) { |
| Terminator terminator{sourceFile, line}; |
| |
| RUNTIME_CHECK(terminator, IsValidCharDescriptor(&cwd)); |
| |
| char *buf{(char *)AllocateMemoryOrCrash(terminator, PATH_MAX)}; |
| |
| if (!getcwd(buf, PATH_MAX)) { |
| return StatMissingCurrentWorkDirectory; |
| } |
| |
| std::int64_t strLen{StringLength(buf)}; |
| std::int32_t status{CopyCharsToDescriptor(cwd, buf, strLen)}; |
| |
| std::free(buf); |
| return status; |
| } |
| |
| } // namespace Fortran::runtime |