10c375296SDiana Picus //===-- runtime/command.cpp -----------------------------------------------===//
20c375296SDiana Picus //
30c375296SDiana Picus // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40c375296SDiana Picus // See https://llvm.org/LICENSE.txt for license information.
50c375296SDiana Picus // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60c375296SDiana Picus //
70c375296SDiana Picus //===----------------------------------------------------------------------===//
80c375296SDiana Picus
9830c0b90SPeter Klausler #include "flang/Runtime/command.h"
100c375296SDiana Picus #include "environment.h"
1137089baeSDiana Picus #include "stat.h"
12824bf908SDiana Picus #include "terminator.h"
13873f081eSDiana Picus #include "tools.h"
1437089baeSDiana Picus #include "flang/Runtime/descriptor.h"
15fc2ba5e5SDiana Picus #include <cstdlib>
16af63d179SDiana Picus #include <limits>
170c375296SDiana Picus
180c375296SDiana Picus namespace Fortran::runtime {
RTNAME(ArgumentCount)19af63d179SDiana Picus std::int32_t RTNAME(ArgumentCount)() {
200c375296SDiana Picus int argc{executionEnvironment.argc};
210c375296SDiana Picus if (argc > 1) {
220c375296SDiana Picus // C counts the command name as one of the arguments, but Fortran doesn't.
230c375296SDiana Picus return argc - 1;
240c375296SDiana Picus }
250c375296SDiana Picus return 0;
260c375296SDiana Picus }
27af63d179SDiana Picus
289df0ba59SDiana Picus // Returns the length of the \p string. Assumes \p string is valid.
StringLength(const char * string)299df0ba59SDiana Picus static std::int64_t StringLength(const char *string) {
309df0ba59SDiana Picus std::size_t length{std::strlen(string)};
31*c531171dSserge-sans-paille if constexpr (sizeof(std::size_t) < sizeof(std::int64_t)) {
32af63d179SDiana Picus return static_cast<std::int64_t>(length);
33af63d179SDiana Picus } else {
34af63d179SDiana Picus std::size_t max{std::numeric_limits<std::int64_t>::max()};
35af63d179SDiana Picus return length > max ? 0 // Just fail.
36af63d179SDiana Picus : static_cast<std::int64_t>(length);
37af63d179SDiana Picus }
38af63d179SDiana Picus }
3937089baeSDiana Picus
RTNAME(ArgumentLength)4037089baeSDiana Picus std::int64_t RTNAME(ArgumentLength)(std::int32_t n) {
419df0ba59SDiana Picus if (n < 0 || n >= executionEnvironment.argc ||
429df0ba59SDiana Picus !executionEnvironment.argv[n]) {
4337089baeSDiana Picus return 0;
4437089baeSDiana Picus }
4537089baeSDiana Picus
469df0ba59SDiana Picus return StringLength(executionEnvironment.argv[n]);
4737089baeSDiana Picus }
4837089baeSDiana Picus
IsValidCharDescriptor(const Descriptor * value)4937089baeSDiana Picus static bool IsValidCharDescriptor(const Descriptor *value) {
5037089baeSDiana Picus return value && value->IsAllocated() &&
5137089baeSDiana Picus value->type() == TypeCode(TypeCategory::Character, 1) &&
5237089baeSDiana Picus value->rank() == 0;
5337089baeSDiana Picus }
5437089baeSDiana Picus
IsValidIntDescriptor(const Descriptor * length)55873f081eSDiana Picus static bool IsValidIntDescriptor(const Descriptor *length) {
56873f081eSDiana Picus auto typeCode{length->type().GetCategoryAndKind()};
57873f081eSDiana Picus // Check that our descriptor is allocated and is a scalar integer with
58873f081eSDiana Picus // kind != 1 (i.e. with a large enough decimal exponent range).
59873f081eSDiana Picus return length->IsAllocated() && length->rank() == 0 &&
60873f081eSDiana Picus length->type().IsInteger() && typeCode && typeCode->second != 1;
61873f081eSDiana Picus }
62873f081eSDiana Picus
FillWithSpaces(const Descriptor & value,std::size_t offset=0)63873f081eSDiana Picus static void FillWithSpaces(const Descriptor &value, std::size_t offset = 0) {
64873f081eSDiana Picus if (offset < value.ElementBytes()) {
65873f081eSDiana Picus std::memset(
66873f081eSDiana Picus value.OffsetElement(offset), ' ', value.ElementBytes() - offset);
67873f081eSDiana Picus }
6837089baeSDiana Picus }
6937089baeSDiana Picus
CopyToDescriptor(const Descriptor & value,const char * rawValue,std::int64_t rawValueLength,const Descriptor * errmsg,std::size_t offset=0)709df0ba59SDiana Picus static std::int32_t CopyToDescriptor(const Descriptor &value,
71873f081eSDiana Picus const char *rawValue, std::int64_t rawValueLength, const Descriptor *errmsg,
72873f081eSDiana Picus std::size_t offset = 0) {
73873f081eSDiana Picus
74873f081eSDiana Picus std::int64_t toCopy{std::min(rawValueLength,
75873f081eSDiana Picus static_cast<std::int64_t>(value.ElementBytes() - offset))};
76873f081eSDiana Picus if (toCopy < 0) {
77873f081eSDiana Picus return ToErrmsg(errmsg, StatValueTooShort);
78873f081eSDiana Picus }
79873f081eSDiana Picus
80873f081eSDiana Picus std::memcpy(value.OffsetElement(offset), rawValue, toCopy);
819df0ba59SDiana Picus
829df0ba59SDiana Picus if (rawValueLength > toCopy) {
839df0ba59SDiana Picus return ToErrmsg(errmsg, StatValueTooShort);
849df0ba59SDiana Picus }
859df0ba59SDiana Picus
869df0ba59SDiana Picus return StatOk;
879df0ba59SDiana Picus }
889df0ba59SDiana Picus
CheckAndCopyToDescriptor(const Descriptor * value,const char * rawValue,const Descriptor * errmsg,std::size_t & offset)89873f081eSDiana Picus static std::int32_t CheckAndCopyToDescriptor(const Descriptor *value,
90873f081eSDiana Picus const char *rawValue, const Descriptor *errmsg, std::size_t &offset) {
91873f081eSDiana Picus bool haveValue{IsValidCharDescriptor(value)};
92873f081eSDiana Picus
93873f081eSDiana Picus std::int64_t len{StringLength(rawValue)};
94873f081eSDiana Picus if (len <= 0) {
95873f081eSDiana Picus if (haveValue) {
96873f081eSDiana Picus FillWithSpaces(*value);
97873f081eSDiana Picus }
98873f081eSDiana Picus return ToErrmsg(errmsg, StatMissingArgument);
99873f081eSDiana Picus }
100873f081eSDiana Picus
101873f081eSDiana Picus std::int32_t stat{StatOk};
102873f081eSDiana Picus if (haveValue) {
103873f081eSDiana Picus stat = CopyToDescriptor(*value, rawValue, len, errmsg, offset);
104873f081eSDiana Picus }
105873f081eSDiana Picus
106873f081eSDiana Picus offset += len;
107873f081eSDiana Picus return stat;
108873f081eSDiana Picus }
109873f081eSDiana Picus
RTNAME(ArgumentValue)11037089baeSDiana Picus std::int32_t RTNAME(ArgumentValue)(
11137089baeSDiana Picus std::int32_t n, const Descriptor *value, const Descriptor *errmsg) {
11237089baeSDiana Picus if (IsValidCharDescriptor(value)) {
113873f081eSDiana Picus FillWithSpaces(*value);
11437089baeSDiana Picus }
11537089baeSDiana Picus
11637089baeSDiana Picus if (n < 0 || n >= executionEnvironment.argc) {
11737089baeSDiana Picus return ToErrmsg(errmsg, StatInvalidArgumentNumber);
11837089baeSDiana Picus }
11937089baeSDiana Picus
12037089baeSDiana Picus if (IsValidCharDescriptor(value)) {
1219df0ba59SDiana Picus const char *arg{executionEnvironment.argv[n]};
1229df0ba59SDiana Picus std::int64_t argLen{StringLength(arg)};
12337089baeSDiana Picus if (argLen <= 0) {
12437089baeSDiana Picus return ToErrmsg(errmsg, StatMissingArgument);
12537089baeSDiana Picus }
12637089baeSDiana Picus
1279df0ba59SDiana Picus return CopyToDescriptor(*value, arg, argLen, errmsg);
12837089baeSDiana Picus }
12937089baeSDiana Picus
13037089baeSDiana Picus return StatOk;
13137089baeSDiana Picus }
132fc2ba5e5SDiana Picus
133873f081eSDiana Picus template <int KIND> struct FitsInIntegerKind {
operator ()Fortran::runtime::FitsInIntegerKind134873f081eSDiana Picus bool operator()(std::int64_t value) {
135873f081eSDiana Picus return value <= std::numeric_limits<Fortran::runtime::CppTypeFor<
136873f081eSDiana Picus Fortran::common::TypeCategory::Integer, KIND>>::max();
137873f081eSDiana Picus }
138873f081eSDiana Picus };
139873f081eSDiana Picus
RTNAME(GetCommand)140873f081eSDiana Picus std::int32_t RTNAME(GetCommand)(const Descriptor *value,
141873f081eSDiana Picus const Descriptor *length, const Descriptor *errmsg, const char *sourceFile,
142873f081eSDiana Picus int line) {
143873f081eSDiana Picus Terminator terminator{sourceFile, line};
144873f081eSDiana Picus
145873f081eSDiana Picus auto storeLength = [&](std::int64_t value) {
146873f081eSDiana Picus auto typeCode{length->type().GetCategoryAndKind()};
147873f081eSDiana Picus int kind{typeCode->second};
148873f081eSDiana Picus Fortran::runtime::ApplyIntegerKind<Fortran::runtime::StoreIntegerAt, void>(
149873f081eSDiana Picus kind, terminator, *length, /* atIndex = */ 0, value);
150873f081eSDiana Picus };
151873f081eSDiana Picus
152873f081eSDiana Picus if (value) {
153873f081eSDiana Picus RUNTIME_CHECK(terminator, IsValidCharDescriptor(value));
154873f081eSDiana Picus }
155873f081eSDiana Picus
156873f081eSDiana Picus // Store 0 in case we error out later on.
157873f081eSDiana Picus if (length) {
158873f081eSDiana Picus RUNTIME_CHECK(terminator, IsValidIntDescriptor(length));
159873f081eSDiana Picus storeLength(0);
160873f081eSDiana Picus }
161873f081eSDiana Picus
162873f081eSDiana Picus auto shouldContinue = [&](std::int32_t stat) -> bool {
163873f081eSDiana Picus // We continue as long as everything is ok OR the value descriptor is
164873f081eSDiana Picus // too short, but we still need to compute the length.
165873f081eSDiana Picus return stat == StatOk || (length && stat == StatValueTooShort);
166873f081eSDiana Picus };
167873f081eSDiana Picus
168873f081eSDiana Picus std::size_t offset{0};
169873f081eSDiana Picus
170873f081eSDiana Picus if (executionEnvironment.argc == 0) {
171873f081eSDiana Picus return CheckAndCopyToDescriptor(value, "", errmsg, offset);
172873f081eSDiana Picus }
173873f081eSDiana Picus
174873f081eSDiana Picus // value = argv[0]
175873f081eSDiana Picus std::int32_t stat{CheckAndCopyToDescriptor(
176873f081eSDiana Picus value, executionEnvironment.argv[0], errmsg, offset)};
177873f081eSDiana Picus if (!shouldContinue(stat)) {
178873f081eSDiana Picus return stat;
179873f081eSDiana Picus }
180873f081eSDiana Picus
181873f081eSDiana Picus // value += " " + argv[1:n]
182873f081eSDiana Picus for (std::int32_t i{1}; i < executionEnvironment.argc; ++i) {
183873f081eSDiana Picus stat = CheckAndCopyToDescriptor(value, " ", errmsg, offset);
184873f081eSDiana Picus if (!shouldContinue(stat)) {
185873f081eSDiana Picus return stat;
186873f081eSDiana Picus }
187873f081eSDiana Picus
188873f081eSDiana Picus stat = CheckAndCopyToDescriptor(
189873f081eSDiana Picus value, executionEnvironment.argv[i], errmsg, offset);
190873f081eSDiana Picus if (!shouldContinue(stat)) {
191873f081eSDiana Picus return stat;
192873f081eSDiana Picus }
193873f081eSDiana Picus }
194873f081eSDiana Picus
195873f081eSDiana Picus auto fitsInLength = [&](std::int64_t value) -> bool {
196873f081eSDiana Picus auto typeCode{length->type().GetCategoryAndKind()};
197873f081eSDiana Picus int kind{typeCode->second};
198873f081eSDiana Picus return Fortran::runtime::ApplyIntegerKind<FitsInIntegerKind, bool>(
199873f081eSDiana Picus kind, terminator, value);
200873f081eSDiana Picus };
201873f081eSDiana Picus
202873f081eSDiana Picus if (length && fitsInLength(offset)) {
203873f081eSDiana Picus storeLength(offset);
204873f081eSDiana Picus }
205873f081eSDiana Picus
206873f081eSDiana Picus // value += spaces for padding
207873f081eSDiana Picus if (value) {
208873f081eSDiana Picus FillWithSpaces(*value, offset);
209873f081eSDiana Picus }
210873f081eSDiana Picus
211873f081eSDiana Picus return stat;
212873f081eSDiana Picus }
213873f081eSDiana Picus
LengthWithoutTrailingSpaces(const Descriptor & d)214fc2ba5e5SDiana Picus static std::size_t LengthWithoutTrailingSpaces(const Descriptor &d) {
215fc2ba5e5SDiana Picus std::size_t s{d.ElementBytes() - 1};
216fc2ba5e5SDiana Picus while (*d.OffsetElement(s) == ' ') {
217fc2ba5e5SDiana Picus --s;
218fc2ba5e5SDiana Picus }
219fc2ba5e5SDiana Picus return s + 1;
220fc2ba5e5SDiana Picus }
221fc2ba5e5SDiana Picus
GetEnvVariableValue(const Descriptor & name,bool trim_name,const char * sourceFile,int line)2229df0ba59SDiana Picus static const char *GetEnvVariableValue(
223824bf908SDiana Picus const Descriptor &name, bool trim_name, const char *sourceFile, int line) {
224fc2ba5e5SDiana Picus std::size_t nameLength{
225fc2ba5e5SDiana Picus trim_name ? LengthWithoutTrailingSpaces(name) : name.ElementBytes()};
226fc2ba5e5SDiana Picus if (nameLength == 0) {
2279df0ba59SDiana Picus return nullptr;
228fc2ba5e5SDiana Picus }
229fc2ba5e5SDiana Picus
230824bf908SDiana Picus Terminator terminator{sourceFile, line};
231824bf908SDiana Picus const char *value{executionEnvironment.GetEnv(
232824bf908SDiana Picus name.OffsetElement(), nameLength, terminator)};
2339df0ba59SDiana Picus return value;
2349df0ba59SDiana Picus }
2359df0ba59SDiana Picus
RTNAME(EnvVariableValue)2369df0ba59SDiana Picus std::int32_t RTNAME(EnvVariableValue)(const Descriptor &name,
2379df0ba59SDiana Picus const Descriptor *value, bool trim_name, const Descriptor *errmsg,
2389df0ba59SDiana Picus const char *sourceFile, int line) {
2399df0ba59SDiana Picus if (IsValidCharDescriptor(value)) {
240873f081eSDiana Picus FillWithSpaces(*value);
2419df0ba59SDiana Picus }
2429df0ba59SDiana Picus
2439df0ba59SDiana Picus const char *rawValue{GetEnvVariableValue(name, trim_name, sourceFile, line)};
2449df0ba59SDiana Picus if (!rawValue) {
2459df0ba59SDiana Picus return ToErrmsg(errmsg, StatMissingEnvVariable);
2469df0ba59SDiana Picus }
2479df0ba59SDiana Picus
2489df0ba59SDiana Picus if (IsValidCharDescriptor(value)) {
2499df0ba59SDiana Picus return CopyToDescriptor(*value, rawValue, StringLength(rawValue), errmsg);
2509df0ba59SDiana Picus }
2519df0ba59SDiana Picus
2529df0ba59SDiana Picus return StatOk;
2539df0ba59SDiana Picus }
2549df0ba59SDiana Picus
RTNAME(EnvVariableLength)2559df0ba59SDiana Picus std::int64_t RTNAME(EnvVariableLength)(
2569df0ba59SDiana Picus const Descriptor &name, bool trim_name, const char *sourceFile, int line) {
2579df0ba59SDiana Picus const char *value{GetEnvVariableValue(name, trim_name, sourceFile, line)};
258fc2ba5e5SDiana Picus if (!value) {
259fc2ba5e5SDiana Picus return 0;
260fc2ba5e5SDiana Picus }
2619df0ba59SDiana Picus return StringLength(value);
262fc2ba5e5SDiana Picus }
2630c375296SDiana Picus } // namespace Fortran::runtime
264