xref: /llvm-project/flang/runtime/extensions.cpp (revision ecc71de53f8786269ce089501432ee555f98f55b)
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(&current_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