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