1 //===-- runtime/extensions.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 // These C-coded entry points with Fortran-mangled names implement legacy 10 // extensions that will eventually be implemented in Fortran. 11 12 #include "flang/Runtime/extensions.h" 13 #include "terminator.h" 14 #include "tools.h" 15 #include "flang/Runtime/command.h" 16 #include "flang/Runtime/descriptor.h" 17 #include "flang/Runtime/entry-names.h" 18 #include "flang/Runtime/io-api.h" 19 #include <chrono> 20 #include <cstring> 21 #include <ctime> 22 #include <signal.h> 23 #include <thread> 24 25 #ifdef _WIN32 26 #define WIN32_LEAN_AND_MEAN 27 #define NOMINMAX 28 #include <windows.h> 29 30 #include <synchapi.h> 31 32 inline void CtimeBuffer(char *buffer, size_t bufsize, const time_t cur_time, 33 Fortran::runtime::Terminator terminator) { 34 int error{ctime_s(buffer, bufsize, &cur_time)}; 35 RUNTIME_CHECK(terminator, error == 0); 36 } 37 #elif _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE || \ 38 defined(_POSIX_SOURCE) 39 inline void CtimeBuffer(char *buffer, size_t bufsize, const time_t cur_time, 40 Fortran::runtime::Terminator terminator) { 41 const char *res{ctime_r(&cur_time, buffer)}; 42 RUNTIME_CHECK(terminator, res != nullptr); 43 } 44 #else 45 inline void CtimeBuffer(char *buffer, size_t bufsize, const time_t cur_time, 46 Fortran::runtime::Terminator terminator) { 47 buffer[0] = '\0'; 48 terminator.Crash("fdate is not supported."); 49 } 50 #endif 51 52 #ifndef _WIN32 53 // posix-compliant and has getlogin_r and F_OK 54 #include <unistd.h> 55 #else 56 #include <direct.h> 57 #endif 58 59 extern "C" { 60 61 namespace Fortran::runtime { 62 63 gid_t RTNAME(GetGID)() { 64 #ifdef _WIN32 65 // Group IDs don't exist on Windows, return 1 to avoid errors 66 return 1; 67 #else 68 return getgid(); 69 #endif 70 } 71 72 uid_t RTNAME(GetUID)() { 73 #ifdef _WIN32 74 // User IDs don't exist on Windows, return 1 to avoid errors 75 return 1; 76 #else 77 return getuid(); 78 #endif 79 } 80 81 void GetUsernameEnvVar(const char *envName, char *arg, std::int64_t length) { 82 Descriptor name{*Descriptor::Create( 83 1, std::strlen(envName) + 1, const_cast<char *>(envName), 0)}; 84 Descriptor value{*Descriptor::Create(1, length, arg, 0)}; 85 86 RTNAME(GetEnvVariable) 87 (name, &value, nullptr, false, nullptr, __FILE__, __LINE__); 88 } 89 90 namespace io { 91 // SUBROUTINE FLUSH(N) 92 // FLUSH N 93 // END 94 void FORTRAN_PROCEDURE_NAME(flush)(const int &unit) { 95 Cookie cookie{IONAME(BeginFlush)(unit, __FILE__, __LINE__)}; 96 IONAME(EndIoStatement)(cookie); 97 } 98 } // namespace io 99 100 // CALL FDATE(DATE) 101 void FORTRAN_PROCEDURE_NAME(fdate)(char *arg, std::int64_t length) { 102 // Day Mon dd hh:mm:ss yyyy\n\0 is 26 characters, e.g. 103 // Tue May 26 21:51:03 2015\n\0 104 char str[26]; 105 // Insufficient space, fill with spaces and return. 106 if (length < 24) { 107 std::memset(arg, ' ', length); 108 return; 109 } 110 111 Terminator terminator{__FILE__, __LINE__}; 112 std::time_t current_time; 113 std::time(¤t_time); 114 CtimeBuffer(str, sizeof(str), current_time, terminator); 115 116 // Pad space on the last two byte `\n\0`, start at index 24 included. 117 CopyAndPad(arg, str, length, 24); 118 } 119 120 std::intptr_t RTNAME(Malloc)(std::size_t size) { 121 return reinterpret_cast<std::intptr_t>(std::malloc(size)); 122 } 123 124 // RESULT = IARGC() 125 std::int32_t FORTRAN_PROCEDURE_NAME(iargc)() { return RTNAME(ArgumentCount)(); } 126 127 // CALL GETARG(N, ARG) 128 void FORTRAN_PROCEDURE_NAME(getarg)( 129 std::int32_t &n, char *arg, std::int64_t length) { 130 Descriptor value{*Descriptor::Create(1, length, arg, 0)}; 131 (void)RTNAME(GetCommandArgument)( 132 n, &value, nullptr, nullptr, __FILE__, __LINE__); 133 } 134 135 // CALL GETLOG(USRNAME) 136 void FORTRAN_PROCEDURE_NAME(getlog)(char *arg, std::int64_t length) { 137 #if _REENTRANT || _POSIX_C_SOURCE >= 199506L 138 if (length >= 1 && getlogin_r(arg, length) == 0) { 139 auto loginLen{std::strlen(arg)}; 140 std::memset( 141 arg + loginLen, ' ', static_cast<std::size_t>(length) - loginLen); 142 return; 143 } 144 #endif 145 #if _WIN32 146 GetUsernameEnvVar("USERNAME", arg, length); 147 #else 148 GetUsernameEnvVar("LOGNAME", arg, length); 149 #endif 150 } 151 152 void RTNAME(Free)(std::intptr_t ptr) { 153 std::free(reinterpret_cast<void *>(ptr)); 154 } 155 156 std::int64_t RTNAME(Signal)(std::int64_t number, void (*handler)(int)) { 157 // using auto for portability: 158 // on Windows, this is a void * 159 // on POSIX, this has the same type as handler 160 auto result = signal(number, handler); 161 162 // GNU defines the intrinsic as returning an integer, not a pointer. So we 163 // have to reinterpret_cast 164 return static_cast<int64_t>(reinterpret_cast<std::uintptr_t>(result)); 165 } 166 167 // CALL SLEEP(SECONDS) 168 void RTNAME(Sleep)(std::int64_t seconds) { 169 // ensure that conversion to unsigned makes sense, 170 // sleep(0) is an immidiate return anyway 171 if (seconds < 1) { 172 return; 173 } 174 #if _WIN32 175 Sleep(seconds * 1000); 176 #else 177 sleep(seconds); 178 #endif 179 } 180 181 // TODO: not supported on Windows 182 #ifndef _WIN32 183 std::int64_t FORTRAN_PROCEDURE_NAME(access)(const char *name, 184 std::int64_t nameLength, const char *mode, std::int64_t modeLength) { 185 std::int64_t ret{-1}; 186 if (nameLength <= 0 || modeLength <= 0 || !name || !mode) { 187 return ret; 188 } 189 190 // ensure name is null terminated 191 char *newName{nullptr}; 192 if (name[nameLength - 1] != '\0') { 193 newName = static_cast<char *>(std::malloc(nameLength + 1)); 194 std::memcpy(newName, name, nameLength); 195 newName[nameLength] = '\0'; 196 name = newName; 197 } 198 199 // calculate mode 200 bool read{false}; 201 bool write{false}; 202 bool execute{false}; 203 bool exists{false}; 204 int imode{0}; 205 206 for (std::int64_t i = 0; i < modeLength; ++i) { 207 switch (mode[i]) { 208 case 'r': 209 read = true; 210 break; 211 case 'w': 212 write = true; 213 break; 214 case 'x': 215 execute = true; 216 break; 217 case ' ': 218 exists = true; 219 break; 220 default: 221 // invalid mode 222 goto cleanup; 223 } 224 } 225 if (!read && !write && !execute && !exists) { 226 // invalid mode 227 goto cleanup; 228 } 229 230 if (!read && !write && !execute) { 231 imode = F_OK; 232 } else { 233 if (read) { 234 imode |= R_OK; 235 } 236 if (write) { 237 imode |= W_OK; 238 } 239 if (execute) { 240 imode |= X_OK; 241 } 242 } 243 ret = access(name, imode); 244 245 cleanup: 246 if (newName) { 247 free(newName); 248 } 249 return ret; 250 } 251 #endif 252 253 // CHDIR(DIR) 254 int RTNAME(Chdir)(const char *name) { 255 // chdir alias seems to be deprecated on Windows. 256 #ifndef _WIN32 257 return chdir(name); 258 #else 259 return _chdir(name); 260 #endif 261 } 262 263 int FORTRAN_PROCEDURE_NAME(ierrno)() { return errno; } 264 265 } // namespace Fortran::runtime 266 } // extern "C" 267