[libc++][Android] Allow testing libc++ with clang-r536225 (#116149)
[llvm-project.git] / flang / runtime / command.cpp
bloba555e26f96a66c1fa22565253babf9321603d44d
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 //===----------------------------------------------------------------------===//
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>
18 #ifdef _WIN32
19 #include "flang/Common/windows-include.h"
20 #include <direct.h>
21 #define getcwd _getcwd
22 #define PATH_MAX MAX_PATH
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()
30 #ifndef PATH_MAX
31 #define PATH_MAX 4096
32 #endif
33 #endif
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;
42 return 0;
45 pid_t RTNAME(GetPID)() { return getpid(); }
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;
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);
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)};
66 std::int64_t len{StringLength(rawValue)};
67 if (len <= 0) {
68 if (haveValue) {
69 FillWithSpaces(*value);
71 return ToErrmsg(errmsg, StatMissingArgument);
74 std::int32_t stat{StatOk};
75 if (haveValue) {
76 stat = CopyCharsToDescriptor(*value, rawValue, len, errmsg, offset);
79 offset += len;
80 return stat;
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();
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);
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};
107 if (value) {
108 RUNTIME_CHECK(terminator, IsValidCharDescriptor(value));
109 FillWithSpaces(*value);
112 // Store 0 in case we error out later on.
113 if (length) {
114 RUNTIME_CHECK(terminator, IsValidIntDescriptor(length));
115 StoreIntToDescriptor(length, 0, terminator);
118 if (n < 0 || n >= executionEnvironment.argc) {
119 return ToErrmsg(errmsg, StatInvalidArgumentNumber);
122 const char *arg{executionEnvironment.argv[n]};
123 std::int64_t argLen{StringLength(arg)};
124 if (argLen <= 0) {
125 return ToErrmsg(errmsg, StatMissingArgument);
128 if (length && FitsInDescriptor(length, argLen, terminator)) {
129 StoreIntToDescriptor(length, argLen, terminator);
132 if (value) {
133 return CopyCharsToDescriptor(*value, arg, argLen, errmsg);
136 return StatOk;
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};
144 if (value) {
145 RUNTIME_CHECK(terminator, IsValidCharDescriptor(value));
148 // Store 0 in case we error out later on.
149 if (length) {
150 RUNTIME_CHECK(terminator, IsValidIntDescriptor(length));
151 StoreIntToDescriptor(length, 0, terminator);
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);
160 std::size_t offset{0};
162 if (executionEnvironment.argc == 0) {
163 return CheckAndCopyCharsToDescriptor(value, "", errmsg, offset);
166 // value = argv[0]
167 std::int32_t stat{CheckAndCopyCharsToDescriptor(
168 value, executionEnvironment.argv[0], errmsg, offset)};
169 if (!shouldContinue(stat)) {
170 return stat;
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;
180 stat = CheckAndCopyCharsToDescriptor(
181 value, executionEnvironment.argv[i], errmsg, offset);
182 if (!shouldContinue(stat)) {
183 return stat;
187 if (length && FitsInDescriptor(length, offset, terminator)) {
188 StoreIntToDescriptor(length, offset, terminator);
191 // value += spaces for padding
192 if (value) {
193 FillWithSpaces(*value, offset);
196 return stat;
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;
204 return s;
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};
212 if (value) {
213 RUNTIME_CHECK(terminator, IsValidCharDescriptor(value));
214 FillWithSpaces(*value);
217 // Store 0 in case we error out later on.
218 if (length) {
219 RUNTIME_CHECK(terminator, IsValidIntDescriptor(length));
220 StoreIntToDescriptor(length, 0, terminator);
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);
230 if (!rawValue) {
231 return ToErrmsg(errmsg, StatMissingEnvVariable);
234 std::int64_t varLen{StringLength(rawValue)};
235 if (length && FitsInDescriptor(length, varLen, terminator)) {
236 StoreIntToDescriptor(length, varLen, terminator);
239 if (value) {
240 return CopyCharsToDescriptor(*value, rawValue, varLen, errmsg);
242 return StatOk;
245 std::int32_t RTNAME(GetCwd)(
246 const Descriptor &cwd, const char *sourceFile, int line) {
247 Terminator terminator{sourceFile, line};
249 RUNTIME_CHECK(terminator, IsValidCharDescriptor(&cwd));
251 char *buf{(char *)AllocateMemoryOrCrash(terminator, PATH_MAX)};
253 if (!getcwd(buf, PATH_MAX)) {
254 return StatMissingCurrentWorkDirectory;
257 std::int64_t strLen{StringLength(buf)};
258 std::int32_t status{CopyCharsToDescriptor(cwd, buf, strLen)};
260 std::free(buf);
261 return status;
264 } // namespace Fortran::runtime