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#ifdef _WIN32
19#include "flang/Common/windows-include.h"
20
21// On Windows GetCurrentProcessId returns a DWORD aka uint32_t
22#include <processthreadsapi.h>
23inline pid_t getpid() { return GetCurrentProcessId(); }
24#else
25#include <unistd.h> //getpid()
26#endif
27
28namespace Fortran::runtime {
29std::int32_t RTNAME(ArgumentCount)() {
30 int argc{executionEnvironment.argc};
31 if (argc > 1) {
32 // C counts the command name as one of the arguments, but Fortran doesn't.
33 return argc - 1;
34 }
35 return 0;
36}
37
38pid_t RTNAME(GetPID)() { return getpid(); }
39
40// Returns the length of the \p string. Assumes \p string is valid.
41static std::int64_t StringLength(const char *string) {
42 std::size_t length{std::strlen(s: string)};
43 if constexpr (sizeof(std::size_t) < sizeof(std::int64_t)) {
44 return static_cast<std::int64_t>(length);
45 } else {
46 std::size_t max{std::numeric_limits<std::int64_t>::max()};
47 return length > max ? 0 // Just fail.
48 : static_cast<std::int64_t>(length);
49 }
50}
51
52static void FillWithSpaces(const Descriptor &value, std::size_t offset = 0) {
53 if (offset < value.ElementBytes()) {
54 std::memset(
55 s: value.OffsetElement(offset), c: ' ', n: value.ElementBytes() - offset);
56 }
57}
58
59static std::int32_t CheckAndCopyCharsToDescriptor(const Descriptor *value,
60 const char *rawValue, const Descriptor *errmsg, std::size_t &offset) {
61 bool haveValue{IsValidCharDescriptor(value)};
62
63 std::int64_t len{StringLength(rawValue)};
64 if (len <= 0) {
65 if (haveValue) {
66 FillWithSpaces(value: *value);
67 }
68 return ToErrmsg(errmsg, StatMissingArgument);
69 }
70
71 std::int32_t stat{StatOk};
72 if (haveValue) {
73 stat = CopyCharsToDescriptor(*value, rawValue, len, errmsg, offset);
74 }
75
76 offset += len;
77 return stat;
78}
79
80template <int KIND> struct FitsInIntegerKind {
81 bool operator()([[maybe_unused]] std::int64_t value) {
82 if constexpr (KIND >= 8) {
83 return true;
84 } else {
85 return value <= std::numeric_limits<Fortran::runtime::CppTypeFor<
86 Fortran::common::TypeCategory::Integer, KIND>>::max();
87 }
88 }
89};
90
91static bool FitsInDescriptor(
92 const Descriptor *length, std::int64_t value, Terminator &terminator) {
93 auto typeCode{length->type().GetCategoryAndKind()};
94 int kind{typeCode->second};
95 return Fortran::runtime::ApplyIntegerKind<FitsInIntegerKind, bool>(
96 kind, terminator, value);
97}
98
99std::int32_t RTNAME(GetCommandArgument)(std::int32_t n, const Descriptor *value,
100 const Descriptor *length, const Descriptor *errmsg, const char *sourceFile,
101 int line) {
102 Terminator terminator{sourceFile, line};
103
104 if (value) {
105 RUNTIME_CHECK(terminator, IsValidCharDescriptor(value));
106 FillWithSpaces(*value);
107 }
108
109 // Store 0 in case we error out later on.
110 if (length) {
111 RUNTIME_CHECK(terminator, IsValidIntDescriptor(length));
112 StoreIntToDescriptor(length, 0, terminator);
113 }
114
115 if (n < 0 || n >= executionEnvironment.argc) {
116 return ToErrmsg(errmsg, StatInvalidArgumentNumber);
117 }
118
119 const char *arg{executionEnvironment.argv[n]};
120 std::int64_t argLen{StringLength(arg)};
121 if (argLen <= 0) {
122 return ToErrmsg(errmsg, StatMissingArgument);
123 }
124
125 if (length && FitsInDescriptor(length, argLen, terminator)) {
126 StoreIntToDescriptor(length, argLen, terminator);
127 }
128
129 if (value) {
130 return CopyCharsToDescriptor(*value, arg, argLen, errmsg);
131 }
132
133 return StatOk;
134}
135
136std::int32_t RTNAME(GetCommand)(const Descriptor *value,
137 const Descriptor *length, const Descriptor *errmsg, const char *sourceFile,
138 int line) {
139 Terminator terminator{sourceFile, line};
140
141 if (value) {
142 RUNTIME_CHECK(terminator, IsValidCharDescriptor(value));
143 }
144
145 // Store 0 in case we error out later on.
146 if (length) {
147 RUNTIME_CHECK(terminator, IsValidIntDescriptor(length));
148 StoreIntToDescriptor(length, 0, terminator);
149 }
150
151 auto shouldContinue = [&](std::int32_t stat) -> bool {
152 // We continue as long as everything is ok OR the value descriptor is
153 // too short, but we still need to compute the length.
154 return stat == StatOk || (length && stat == StatValueTooShort);
155 };
156
157 std::size_t offset{0};
158
159 if (executionEnvironment.argc == 0) {
160 return CheckAndCopyCharsToDescriptor(value, "", errmsg, offset);
161 }
162
163 // value = argv[0]
164 std::int32_t stat{CheckAndCopyCharsToDescriptor(
165 value, executionEnvironment.argv[0], errmsg, offset)};
166 if (!shouldContinue(stat)) {
167 return stat;
168 }
169
170 // value += " " + argv[1:n]
171 for (std::int32_t i{1}; i < executionEnvironment.argc; ++i) {
172 stat = CheckAndCopyCharsToDescriptor(value, " ", errmsg, offset);
173 if (!shouldContinue(stat)) {
174 return stat;
175 }
176
177 stat = CheckAndCopyCharsToDescriptor(
178 value, executionEnvironment.argv[i], errmsg, offset);
179 if (!shouldContinue(stat)) {
180 return stat;
181 }
182 }
183
184 if (length && FitsInDescriptor(length, offset, terminator)) {
185 StoreIntToDescriptor(length, offset, terminator);
186 }
187
188 // value += spaces for padding
189 if (value) {
190 FillWithSpaces(*value, offset);
191 }
192
193 return stat;
194}
195
196static std::size_t LengthWithoutTrailingSpaces(const Descriptor &d) {
197 std::size_t s{d.ElementBytes()}; // This can be 0.
198 while (s != 0 && *d.OffsetElement(s - 1) == ' ') {
199 --s;
200 }
201 return s;
202}
203
204std::int32_t RTNAME(GetEnvVariable)(const Descriptor &name,
205 const Descriptor *value, const Descriptor *length, bool trim_name,
206 const Descriptor *errmsg, const char *sourceFile, int line) {
207 Terminator terminator{sourceFile, line};
208
209 if (value) {
210 RUNTIME_CHECK(terminator, IsValidCharDescriptor(value));
211 FillWithSpaces(*value);
212 }
213
214 // Store 0 in case we error out later on.
215 if (length) {
216 RUNTIME_CHECK(terminator, IsValidIntDescriptor(length));
217 StoreIntToDescriptor(length, 0, terminator);
218 }
219
220 const char *rawValue{nullptr};
221 std::size_t nameLength{
222 trim_name ? LengthWithoutTrailingSpaces(name) : name.ElementBytes()};
223 if (nameLength != 0) {
224 rawValue = executionEnvironment.GetEnv(
225 name.OffsetElement(), nameLength, terminator);
226 }
227 if (!rawValue) {
228 return ToErrmsg(errmsg, StatMissingEnvVariable);
229 }
230
231 std::int64_t varLen{StringLength(rawValue)};
232 if (length && FitsInDescriptor(length, varLen, terminator)) {
233 StoreIntToDescriptor(length, varLen, terminator);
234 }
235
236 if (value) {
237 return CopyCharsToDescriptor(*value, rawValue, varLen, errmsg);
238 }
239 return StatOk;
240}
241
242} // namespace Fortran::runtime
243

source code of flang/runtime/command.cpp