Please use GitHub pull requests for new patches. Phabricator shutdown timeline
Changeset View
Changeset View
Standalone View
Standalone View
flang/runtime/command.cpp
//===-- runtime/command.cpp -----------------------------------------------===// | //===-- runtime/command.cpp -----------------------------------------------===// | ||||
// | // | ||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||||
// See https://llvm.org/LICENSE.txt for license information. | // See https://llvm.org/LICENSE.txt for license information. | ||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||||
// | // | ||||
//===----------------------------------------------------------------------===// | //===----------------------------------------------------------------------===// | ||||
#include "flang/Runtime/command.h" | #include "flang/Runtime/command.h" | ||||
#include "environment.h" | #include "environment.h" | ||||
#include "stat.h" | #include "stat.h" | ||||
#include "terminator.h" | #include "terminator.h" | ||||
#include "tools.h" | |||||
#include "flang/Runtime/descriptor.h" | #include "flang/Runtime/descriptor.h" | ||||
#include <cstdlib> | #include <cstdlib> | ||||
#include <limits> | #include <limits> | ||||
namespace Fortran::runtime { | namespace Fortran::runtime { | ||||
std::int32_t RTNAME(ArgumentCount)() { | std::int32_t RTNAME(ArgumentCount)() { | ||||
int argc{executionEnvironment.argc}; | int argc{executionEnvironment.argc}; | ||||
if (argc > 1) { | if (argc > 1) { | ||||
Show All 25 Lines | |||||
} | } | ||||
static bool IsValidCharDescriptor(const Descriptor *value) { | static bool IsValidCharDescriptor(const Descriptor *value) { | ||||
return value && value->IsAllocated() && | return value && value->IsAllocated() && | ||||
value->type() == TypeCode(TypeCategory::Character, 1) && | value->type() == TypeCode(TypeCategory::Character, 1) && | ||||
value->rank() == 0; | value->rank() == 0; | ||||
} | } | ||||
static void FillWithSpaces(const Descriptor *value) { | static bool IsValidIntDescriptor(const Descriptor *length) { | ||||
std::memset(value->OffsetElement(), ' ', value->ElementBytes()); | auto typeCode{length->type().GetCategoryAndKind()}; | ||||
// Check that our descriptor is allocated and is a scalar integer with | |||||
// kind != 1 (i.e. with a large enough decimal exponent range). | |||||
return length->IsAllocated() && length->rank() == 0 && | |||||
length->type().IsInteger() && typeCode && typeCode->second != 1; | |||||
} | |||||
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 CopyToDescriptor(const Descriptor &value, | static std::int32_t CopyToDescriptor(const Descriptor &value, | ||||
const char *rawValue, std::int64_t rawValueLength, | const char *rawValue, std::int64_t rawValueLength, const Descriptor *errmsg, | ||||
const Descriptor *errmsg) { | std::size_t offset = 0) { | ||||
std::int64_t toCopy{std::min( | |||||
rawValueLength, static_cast<std::int64_t>(value.ElementBytes()))}; | std::int64_t toCopy{std::min(rawValueLength, | ||||
std::memcpy(value.OffsetElement(), rawValue, toCopy); | static_cast<std::int64_t>(value.ElementBytes() - offset))}; | ||||
if (toCopy < 0) { | |||||
return ToErrmsg(errmsg, StatValueTooShort); | |||||
} | |||||
std::memcpy(value.OffsetElement(offset), rawValue, toCopy); | |||||
if (rawValueLength > toCopy) { | if (rawValueLength > toCopy) { | ||||
return ToErrmsg(errmsg, StatValueTooShort); | return ToErrmsg(errmsg, StatValueTooShort); | ||||
} | } | ||||
return StatOk; | return StatOk; | ||||
} | } | ||||
static std::int32_t CheckAndCopyToDescriptor(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 = CopyToDescriptor(*value, rawValue, len, errmsg, offset); | |||||
} | |||||
offset += len; | |||||
return stat; | |||||
} | |||||
std::int32_t RTNAME(ArgumentValue)( | std::int32_t RTNAME(ArgumentValue)( | ||||
std::int32_t n, const Descriptor *value, const Descriptor *errmsg) { | std::int32_t n, const Descriptor *value, const Descriptor *errmsg) { | ||||
if (IsValidCharDescriptor(value)) { | if (IsValidCharDescriptor(value)) { | ||||
FillWithSpaces(value); | FillWithSpaces(*value); | ||||
} | } | ||||
if (n < 0 || n >= executionEnvironment.argc) { | if (n < 0 || n >= executionEnvironment.argc) { | ||||
return ToErrmsg(errmsg, StatInvalidArgumentNumber); | return ToErrmsg(errmsg, StatInvalidArgumentNumber); | ||||
} | } | ||||
if (IsValidCharDescriptor(value)) { | if (IsValidCharDescriptor(value)) { | ||||
const char *arg{executionEnvironment.argv[n]}; | const char *arg{executionEnvironment.argv[n]}; | ||||
std::int64_t argLen{StringLength(arg)}; | std::int64_t argLen{StringLength(arg)}; | ||||
if (argLen <= 0) { | if (argLen <= 0) { | ||||
return ToErrmsg(errmsg, StatMissingArgument); | return ToErrmsg(errmsg, StatMissingArgument); | ||||
} | } | ||||
return CopyToDescriptor(*value, arg, argLen, errmsg); | return CopyToDescriptor(*value, arg, argLen, errmsg); | ||||
} | } | ||||
return StatOk; | return StatOk; | ||||
} | } | ||||
template <int KIND> struct FitsInIntegerKind { | |||||
bool operator()(std::int64_t value) { | |||||
return value <= std::numeric_limits<Fortran::runtime::CppTypeFor< | |||||
Fortran::common::TypeCategory::Integer, KIND>>::max(); | |||||
} | |||||
}; | |||||
std::int32_t RTNAME(GetCommand)(const Descriptor *value, | |||||
const Descriptor *length, const Descriptor *errmsg, const char *sourceFile, | |||||
int line) { | |||||
Terminator terminator{sourceFile, line}; | |||||
auto storeLength = [&](std::int64_t value) { | |||||
auto typeCode{length->type().GetCategoryAndKind()}; | |||||
int kind{typeCode->second}; | |||||
Fortran::runtime::ApplyIntegerKind<Fortran::runtime::StoreIntegerAt, void>( | |||||
kind, terminator, *length, /* atIndex = */ 0, value); | |||||
}; | |||||
if (value) { | |||||
RUNTIME_CHECK(terminator, IsValidCharDescriptor(value)); | |||||
} | |||||
// Store 0 in case we error out later on. | |||||
if (length) { | |||||
RUNTIME_CHECK(terminator, IsValidIntDescriptor(length)); | |||||
storeLength(0); | |||||
} | |||||
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 CheckAndCopyToDescriptor(value, "", errmsg, offset); | |||||
} | |||||
// value = argv[0] | |||||
std::int32_t stat{CheckAndCopyToDescriptor( | |||||
value, executionEnvironment.argv[0], errmsg, offset)}; | |||||
if (!shouldContinue(stat)) { | |||||
return stat; | |||||
} | |||||
// value += " " + argv[1:n] | |||||
jeanPerier: should this be `max(0, executionEnvironment.argc - 1)` ? I think it is highly unlikely that… | |||||
for (std::int32_t i{1}; i < executionEnvironment.argc; ++i) { | |||||
stat = CheckAndCopyToDescriptor(value, " ", errmsg, offset); | |||||
if (!shouldContinue(stat)) { | |||||
return stat; | |||||
} | |||||
stat = CheckAndCopyToDescriptor( | |||||
value, executionEnvironment.argv[i], errmsg, offset); | |||||
Not Done ReplyInline ActionsCould this be moved right after stat = CopyToDescriptor(*value, " ", 1, errmsg, offset); on line 184 ? jeanPerier: Could this be moved right after `stat = CopyToDescriptor(*value, " ", 1, errmsg, offset);` on… | |||||
if (!shouldContinue(stat)) { | |||||
return stat; | |||||
} | |||||
} | |||||
auto fitsInLength = [&](std::int64_t value) -> bool { | |||||
auto typeCode{length->type().GetCategoryAndKind()}; | |||||
int kind{typeCode->second}; | |||||
return Fortran::runtime::ApplyIntegerKind<FitsInIntegerKind, bool>( | |||||
kind, terminator, value); | |||||
}; | |||||
if (length && fitsInLength(offset)) { | |||||
storeLength(offset); | |||||
} | |||||
// value += spaces for padding | |||||
if (value) { | |||||
FillWithSpaces(*value, offset); | |||||
} | |||||
return stat; | |||||
} | |||||
static std::size_t LengthWithoutTrailingSpaces(const Descriptor &d) { | static std::size_t LengthWithoutTrailingSpaces(const Descriptor &d) { | ||||
std::size_t s{d.ElementBytes() - 1}; | std::size_t s{d.ElementBytes() - 1}; | ||||
while (*d.OffsetElement(s) == ' ') { | while (*d.OffsetElement(s) == ' ') { | ||||
--s; | --s; | ||||
} | } | ||||
return s + 1; | return s + 1; | ||||
} | } | ||||
Show All 10 Lines | const char *value{executionEnvironment.GetEnv( | ||||
name.OffsetElement(), nameLength, terminator)}; | name.OffsetElement(), nameLength, terminator)}; | ||||
return value; | return value; | ||||
} | } | ||||
std::int32_t RTNAME(EnvVariableValue)(const Descriptor &name, | std::int32_t RTNAME(EnvVariableValue)(const Descriptor &name, | ||||
const Descriptor *value, bool trim_name, const Descriptor *errmsg, | const Descriptor *value, bool trim_name, const Descriptor *errmsg, | ||||
const char *sourceFile, int line) { | const char *sourceFile, int line) { | ||||
if (IsValidCharDescriptor(value)) { | if (IsValidCharDescriptor(value)) { | ||||
FillWithSpaces(value); | FillWithSpaces(*value); | ||||
} | } | ||||
const char *rawValue{GetEnvVariableValue(name, trim_name, sourceFile, line)}; | const char *rawValue{GetEnvVariableValue(name, trim_name, sourceFile, line)}; | ||||
if (!rawValue) { | if (!rawValue) { | ||||
return ToErrmsg(errmsg, StatMissingEnvVariable); | return ToErrmsg(errmsg, StatMissingEnvVariable); | ||||
} | } | ||||
if (IsValidCharDescriptor(value)) { | if (IsValidCharDescriptor(value)) { | ||||
Show All 15 Lines |
should this be max(0, executionEnvironment.argc - 1) ? I think it is highly unlikely that argc be zero, but looking at some stack overflow threads, that does not look impossible.