1 //===-- runtime/command.cpp -----------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "flang/Runtime/command.h"
10 #include "environment.h"
11 #include "stat.h"
12 #include "terminator.h"
13 #include "tools.h"
14 #include "flang/Runtime/descriptor.h"
15 #include <cstdlib>
16 #include <limits>
17 
18 namespace Fortran::runtime {
RTNAME(ArgumentCount)19 std::int32_t RTNAME(ArgumentCount)() {
20   int argc{executionEnvironment.argc};
21   if (argc > 1) {
22     // C counts the command name as one of the arguments, but Fortran doesn't.
23     return argc - 1;
24   }
25   return 0;
26 }
27 
28 // Returns the length of the \p string. Assumes \p string is valid.
StringLength(const char * string)29 static std::int64_t StringLength(const char *string) {
30   std::size_t length{std::strlen(string)};
31   if constexpr (sizeof(std::size_t) < sizeof(std::int64_t)) {
32     return static_cast<std::int64_t>(length);
33   } else {
34     std::size_t max{std::numeric_limits<std::int64_t>::max()};
35     return length > max ? 0 // Just fail.
36                         : static_cast<std::int64_t>(length);
37   }
38 }
39 
RTNAME(ArgumentLength)40 std::int64_t RTNAME(ArgumentLength)(std::int32_t n) {
41   if (n < 0 || n >= executionEnvironment.argc ||
42       !executionEnvironment.argv[n]) {
43     return 0;
44   }
45 
46   return StringLength(executionEnvironment.argv[n]);
47 }
48 
IsValidCharDescriptor(const Descriptor * value)49 static bool IsValidCharDescriptor(const Descriptor *value) {
50   return value && value->IsAllocated() &&
51       value->type() == TypeCode(TypeCategory::Character, 1) &&
52       value->rank() == 0;
53 }
54 
IsValidIntDescriptor(const Descriptor * length)55 static bool IsValidIntDescriptor(const Descriptor *length) {
56   auto typeCode{length->type().GetCategoryAndKind()};
57   // Check that our descriptor is allocated and is a scalar integer with
58   // kind != 1 (i.e. with a large enough decimal exponent range).
59   return length->IsAllocated() && length->rank() == 0 &&
60       length->type().IsInteger() && typeCode && typeCode->second != 1;
61 }
62 
FillWithSpaces(const Descriptor & value,std::size_t offset=0)63 static void FillWithSpaces(const Descriptor &value, std::size_t offset = 0) {
64   if (offset < value.ElementBytes()) {
65     std::memset(
66         value.OffsetElement(offset), ' ', value.ElementBytes() - offset);
67   }
68 }
69 
CopyToDescriptor(const Descriptor & value,const char * rawValue,std::int64_t rawValueLength,const Descriptor * errmsg,std::size_t offset=0)70 static std::int32_t CopyToDescriptor(const Descriptor &value,
71     const char *rawValue, std::int64_t rawValueLength, const Descriptor *errmsg,
72     std::size_t offset = 0) {
73 
74   std::int64_t toCopy{std::min(rawValueLength,
75       static_cast<std::int64_t>(value.ElementBytes() - offset))};
76   if (toCopy < 0) {
77     return ToErrmsg(errmsg, StatValueTooShort);
78   }
79 
80   std::memcpy(value.OffsetElement(offset), rawValue, toCopy);
81 
82   if (rawValueLength > toCopy) {
83     return ToErrmsg(errmsg, StatValueTooShort);
84   }
85 
86   return StatOk;
87 }
88 
CheckAndCopyToDescriptor(const Descriptor * value,const char * rawValue,const Descriptor * errmsg,std::size_t & offset)89 static std::int32_t CheckAndCopyToDescriptor(const Descriptor *value,
90     const char *rawValue, const Descriptor *errmsg, std::size_t &offset) {
91   bool haveValue{IsValidCharDescriptor(value)};
92 
93   std::int64_t len{StringLength(rawValue)};
94   if (len <= 0) {
95     if (haveValue) {
96       FillWithSpaces(*value);
97     }
98     return ToErrmsg(errmsg, StatMissingArgument);
99   }
100 
101   std::int32_t stat{StatOk};
102   if (haveValue) {
103     stat = CopyToDescriptor(*value, rawValue, len, errmsg, offset);
104   }
105 
106   offset += len;
107   return stat;
108 }
109 
RTNAME(ArgumentValue)110 std::int32_t RTNAME(ArgumentValue)(
111     std::int32_t n, const Descriptor *value, const Descriptor *errmsg) {
112   if (IsValidCharDescriptor(value)) {
113     FillWithSpaces(*value);
114   }
115 
116   if (n < 0 || n >= executionEnvironment.argc) {
117     return ToErrmsg(errmsg, StatInvalidArgumentNumber);
118   }
119 
120   if (IsValidCharDescriptor(value)) {
121     const char *arg{executionEnvironment.argv[n]};
122     std::int64_t argLen{StringLength(arg)};
123     if (argLen <= 0) {
124       return ToErrmsg(errmsg, StatMissingArgument);
125     }
126 
127     return CopyToDescriptor(*value, arg, argLen, errmsg);
128   }
129 
130   return StatOk;
131 }
132 
133 template <int KIND> struct FitsInIntegerKind {
operator ()Fortran::runtime::FitsInIntegerKind134   bool operator()(std::int64_t value) {
135     return value <= std::numeric_limits<Fortran::runtime::CppTypeFor<
136                         Fortran::common::TypeCategory::Integer, KIND>>::max();
137   }
138 };
139 
RTNAME(GetCommand)140 std::int32_t RTNAME(GetCommand)(const Descriptor *value,
141     const Descriptor *length, const Descriptor *errmsg, const char *sourceFile,
142     int line) {
143   Terminator terminator{sourceFile, line};
144 
145   auto storeLength = [&](std::int64_t value) {
146     auto typeCode{length->type().GetCategoryAndKind()};
147     int kind{typeCode->second};
148     Fortran::runtime::ApplyIntegerKind<Fortran::runtime::StoreIntegerAt, void>(
149         kind, terminator, *length, /* atIndex = */ 0, value);
150   };
151 
152   if (value) {
153     RUNTIME_CHECK(terminator, IsValidCharDescriptor(value));
154   }
155 
156   // Store 0 in case we error out later on.
157   if (length) {
158     RUNTIME_CHECK(terminator, IsValidIntDescriptor(length));
159     storeLength(0);
160   }
161 
162   auto shouldContinue = [&](std::int32_t stat) -> bool {
163     // We continue as long as everything is ok OR the value descriptor is
164     // too short, but we still need to compute the length.
165     return stat == StatOk || (length && stat == StatValueTooShort);
166   };
167 
168   std::size_t offset{0};
169 
170   if (executionEnvironment.argc == 0) {
171     return CheckAndCopyToDescriptor(value, "", errmsg, offset);
172   }
173 
174   // value = argv[0]
175   std::int32_t stat{CheckAndCopyToDescriptor(
176       value, executionEnvironment.argv[0], errmsg, offset)};
177   if (!shouldContinue(stat)) {
178     return stat;
179   }
180 
181   // value += " " + argv[1:n]
182   for (std::int32_t i{1}; i < executionEnvironment.argc; ++i) {
183     stat = CheckAndCopyToDescriptor(value, " ", errmsg, offset);
184     if (!shouldContinue(stat)) {
185       return stat;
186     }
187 
188     stat = CheckAndCopyToDescriptor(
189         value, executionEnvironment.argv[i], errmsg, offset);
190     if (!shouldContinue(stat)) {
191       return stat;
192     }
193   }
194 
195   auto fitsInLength = [&](std::int64_t value) -> bool {
196     auto typeCode{length->type().GetCategoryAndKind()};
197     int kind{typeCode->second};
198     return Fortran::runtime::ApplyIntegerKind<FitsInIntegerKind, bool>(
199         kind, terminator, value);
200   };
201 
202   if (length && fitsInLength(offset)) {
203     storeLength(offset);
204   }
205 
206   // value += spaces for padding
207   if (value) {
208     FillWithSpaces(*value, offset);
209   }
210 
211   return stat;
212 }
213 
LengthWithoutTrailingSpaces(const Descriptor & d)214 static std::size_t LengthWithoutTrailingSpaces(const Descriptor &d) {
215   std::size_t s{d.ElementBytes() - 1};
216   while (*d.OffsetElement(s) == ' ') {
217     --s;
218   }
219   return s + 1;
220 }
221 
GetEnvVariableValue(const Descriptor & name,bool trim_name,const char * sourceFile,int line)222 static const char *GetEnvVariableValue(
223     const Descriptor &name, bool trim_name, const char *sourceFile, int line) {
224   std::size_t nameLength{
225       trim_name ? LengthWithoutTrailingSpaces(name) : name.ElementBytes()};
226   if (nameLength == 0) {
227     return nullptr;
228   }
229 
230   Terminator terminator{sourceFile, line};
231   const char *value{executionEnvironment.GetEnv(
232       name.OffsetElement(), nameLength, terminator)};
233   return value;
234 }
235 
RTNAME(EnvVariableValue)236 std::int32_t RTNAME(EnvVariableValue)(const Descriptor &name,
237     const Descriptor *value, bool trim_name, const Descriptor *errmsg,
238     const char *sourceFile, int line) {
239   if (IsValidCharDescriptor(value)) {
240     FillWithSpaces(*value);
241   }
242 
243   const char *rawValue{GetEnvVariableValue(name, trim_name, sourceFile, line)};
244   if (!rawValue) {
245     return ToErrmsg(errmsg, StatMissingEnvVariable);
246   }
247 
248   if (IsValidCharDescriptor(value)) {
249     return CopyToDescriptor(*value, rawValue, StringLength(rawValue), errmsg);
250   }
251 
252   return StatOk;
253 }
254 
RTNAME(EnvVariableLength)255 std::int64_t RTNAME(EnvVariableLength)(
256     const Descriptor &name, bool trim_name, const char *sourceFile, int line) {
257   const char *value{GetEnvVariableValue(name, trim_name, sourceFile, line)};
258   if (!value) {
259     return 0;
260   }
261   return StringLength(value);
262 }
263 } // namespace Fortran::runtime
264