1 //===-- runtime/command.cpp -----------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "flang/Runtime/command.h"
10 #include "environment.h"
12 #include "terminator.h"
14 #include "flang/Runtime/descriptor.h"
19 #include "flang/Common/windows-include.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(); }
28 #include <unistd.h> //getpid()
35 namespace Fortran::runtime
{
36 std::int32_t RTNAME(ArgumentCount
)() {
37 int argc
{executionEnvironment
.argc
};
39 // C counts the command name as one of the arguments, but Fortran doesn't.
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
);
55 static void FillWithSpaces(const Descriptor
&value
, std::size_t offset
= 0) {
56 if (offset
< value
.ElementBytes()) {
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
)};
69 FillWithSpaces(*value
);
71 return ToErrmsg(errmsg
, StatMissingArgument
);
74 std::int32_t stat
{StatOk
};
76 stat
= CopyCharsToDescriptor(*value
, rawValue
, len
, errmsg
, offset
);
83 template <int KIND
> struct FitsInIntegerKind
{
84 bool operator()([[maybe_unused
]] std::int64_t value
) {
85 if constexpr (KIND
>= 8) {
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
,
105 Terminator terminator
{sourceFile
, line
};
108 RUNTIME_CHECK(terminator
, IsValidCharDescriptor(value
));
109 FillWithSpaces(*value
);
112 // Store 0 in case we error out later on.
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
)};
125 return ToErrmsg(errmsg
, StatMissingArgument
);
128 if (length
&& FitsInDescriptor(length
, argLen
, terminator
)) {
129 StoreIntToDescriptor(length
, argLen
, terminator
);
133 return CopyCharsToDescriptor(*value
, arg
, argLen
, errmsg
);
139 std::int32_t RTNAME(GetCommand
)(const Descriptor
*value
,
140 const Descriptor
*length
, const Descriptor
*errmsg
, const char *sourceFile
,
142 Terminator terminator
{sourceFile
, line
};
145 RUNTIME_CHECK(terminator
, IsValidCharDescriptor(value
));
148 // Store 0 in case we error out later on.
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
);
167 std::int32_t stat
{CheckAndCopyCharsToDescriptor(
168 value
, executionEnvironment
.argv
[0], errmsg
, offset
)};
169 if (!shouldContinue(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
)) {
180 stat
= CheckAndCopyCharsToDescriptor(
181 value
, executionEnvironment
.argv
[i
], errmsg
, offset
);
182 if (!shouldContinue(stat
)) {
187 if (length
&& FitsInDescriptor(length
, offset
, terminator
)) {
188 StoreIntToDescriptor(length
, offset
, terminator
);
191 // value += spaces for padding
193 FillWithSpaces(*value
, offset
);
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) == ' ') {
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
};
213 RUNTIME_CHECK(terminator
, IsValidCharDescriptor(value
));
214 FillWithSpaces(*value
);
217 // Store 0 in case we error out later on.
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
);
231 return ToErrmsg(errmsg
, StatMissingEnvVariable
);
234 std::int64_t varLen
{StringLength(rawValue
)};
235 if (length
&& FitsInDescriptor(length
, varLen
, terminator
)) {
236 StoreIntToDescriptor(length
, varLen
, terminator
);
240 return CopyCharsToDescriptor(*value
, rawValue
, varLen
, errmsg
);
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
)};
264 } // namespace Fortran::runtime