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