xref: /llvm-project/flang/runtime/extensions.cpp (revision ecc71de53f8786269ce089501432ee555f98f55b)
1627a8ac7SPeter Klausler //===-- runtime/extensions.cpp --------------------------------------------===//
2627a8ac7SPeter Klausler //
3627a8ac7SPeter Klausler // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4627a8ac7SPeter Klausler // See https://llvm.org/LICENSE.txt for license information.
5627a8ac7SPeter Klausler // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6627a8ac7SPeter Klausler //
7627a8ac7SPeter Klausler //===----------------------------------------------------------------------===//
8627a8ac7SPeter Klausler 
9627a8ac7SPeter Klausler // These C-coded entry points with Fortran-mangled names implement legacy
10627a8ac7SPeter Klausler // extensions that will eventually be implemented in Fortran.
11627a8ac7SPeter Klausler 
12627a8ac7SPeter Klausler #include "flang/Runtime/extensions.h"
13959a430aSYi Wu #include "terminator.h"
1418af032cSYi Wu #include "tools.h"
151d4238beSPeixin-Qiao #include "flang/Runtime/command.h"
161d4238beSPeixin-Qiao #include "flang/Runtime/descriptor.h"
17b64c26f3STom Eccles #include "flang/Runtime/entry-names.h"
18627a8ac7SPeter Klausler #include "flang/Runtime/io-api.h"
19b64c26f3STom Eccles #include <chrono>
20668a58b8STom Eccles #include <cstring>
21959a430aSYi Wu #include <ctime>
22afa52de9STom Eccles #include <signal.h>
23b64c26f3STom Eccles #include <thread>
24959a430aSYi Wu 
25959a430aSYi Wu #ifdef _WIN32
26227fe1c1SDavid Truby #define WIN32_LEAN_AND_MEAN
27227fe1c1SDavid Truby #define NOMINMAX
28227fe1c1SDavid Truby #include <windows.h>
29227fe1c1SDavid Truby 
30227fe1c1SDavid Truby #include <synchapi.h>
31227fe1c1SDavid Truby 
32959a430aSYi Wu inline void CtimeBuffer(char *buffer, size_t bufsize, const time_t cur_time,
33959a430aSYi Wu     Fortran::runtime::Terminator terminator) {
34959a430aSYi Wu   int error{ctime_s(buffer, bufsize, &cur_time)};
35959a430aSYi Wu   RUNTIME_CHECK(terminator, error == 0);
36959a430aSYi Wu }
37959a430aSYi Wu #elif _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE || \
388b6b882fSKelvin Li     defined(_POSIX_SOURCE)
39959a430aSYi Wu inline void CtimeBuffer(char *buffer, size_t bufsize, const time_t cur_time,
40959a430aSYi Wu     Fortran::runtime::Terminator terminator) {
41959a430aSYi Wu   const char *res{ctime_r(&cur_time, buffer)};
42959a430aSYi Wu   RUNTIME_CHECK(terminator, res != nullptr);
43959a430aSYi Wu }
44959a430aSYi Wu #else
45959a430aSYi Wu inline void CtimeBuffer(char *buffer, size_t bufsize, const time_t cur_time,
46959a430aSYi Wu     Fortran::runtime::Terminator terminator) {
47959a430aSYi Wu   buffer[0] = '\0';
48959a430aSYi Wu   terminator.Crash("fdate is not supported.");
49959a430aSYi Wu }
50959a430aSYi Wu #endif
51627a8ac7SPeter Klausler 
520cee8943STom Eccles #ifndef _WIN32
530cee8943STom Eccles // posix-compliant and has getlogin_r and F_OK
5418af032cSYi Wu #include <unistd.h>
555a34e6fdSJean-Didier PAILLEUX #else
565a34e6fdSJean-Didier PAILLEUX #include <direct.h>
5718af032cSYi Wu #endif
5818af032cSYi Wu 
59627a8ac7SPeter Klausler extern "C" {
60627a8ac7SPeter Klausler 
611d4238beSPeixin-Qiao namespace Fortran::runtime {
6218af032cSYi Wu 
63856c38d5SDavid Truby gid_t RTNAME(GetGID)() {
64856c38d5SDavid Truby #ifdef _WIN32
65856c38d5SDavid Truby   // Group IDs don't exist on Windows, return 1 to avoid errors
66856c38d5SDavid Truby   return 1;
67856c38d5SDavid Truby #else
68856c38d5SDavid Truby   return getgid();
69856c38d5SDavid Truby #endif
70856c38d5SDavid Truby }
71856c38d5SDavid Truby 
72856c38d5SDavid Truby uid_t RTNAME(GetUID)() {
73856c38d5SDavid Truby #ifdef _WIN32
74856c38d5SDavid Truby   // User IDs don't exist on Windows, return 1 to avoid errors
75856c38d5SDavid Truby   return 1;
76856c38d5SDavid Truby #else
77856c38d5SDavid Truby   return getuid();
78856c38d5SDavid Truby #endif
79856c38d5SDavid Truby }
80856c38d5SDavid Truby 
814aa04245SPeter Klausler void GetUsernameEnvVar(const char *envName, char *arg, std::int64_t length) {
8218af032cSYi Wu   Descriptor name{*Descriptor::Create(
8318af032cSYi Wu       1, std::strlen(envName) + 1, const_cast<char *>(envName), 0)};
8418af032cSYi Wu   Descriptor value{*Descriptor::Create(1, length, arg, 0)};
8518af032cSYi Wu 
8618af032cSYi Wu   RTNAME(GetEnvVariable)
8718af032cSYi Wu   (name, &value, nullptr, false, nullptr, __FILE__, __LINE__);
8818af032cSYi Wu }
89856c38d5SDavid Truby 
901d4238beSPeixin-Qiao namespace io {
91627a8ac7SPeter Klausler // SUBROUTINE FLUSH(N)
92627a8ac7SPeter Klausler //   FLUSH N
93627a8ac7SPeter Klausler // END
941d4238beSPeixin-Qiao void FORTRAN_PROCEDURE_NAME(flush)(const int &unit) {
95627a8ac7SPeter Klausler   Cookie cookie{IONAME(BeginFlush)(unit, __FILE__, __LINE__)};
96627a8ac7SPeter Klausler   IONAME(EndIoStatement)(cookie);
97627a8ac7SPeter Klausler }
981d4238beSPeixin-Qiao } // namespace io
991d4238beSPeixin-Qiao 
100959a430aSYi Wu // CALL FDATE(DATE)
101959a430aSYi Wu void FORTRAN_PROCEDURE_NAME(fdate)(char *arg, std::int64_t length) {
102959a430aSYi Wu   // Day Mon dd hh:mm:ss yyyy\n\0 is 26 characters, e.g.
103959a430aSYi Wu   // Tue May 26 21:51:03 2015\n\0
104959a430aSYi Wu   char str[26];
105959a430aSYi Wu   // Insufficient space, fill with spaces and return.
106959a430aSYi Wu   if (length < 24) {
107959a430aSYi Wu     std::memset(arg, ' ', length);
108959a430aSYi Wu     return;
109959a430aSYi Wu   }
110959a430aSYi Wu 
111959a430aSYi Wu   Terminator terminator{__FILE__, __LINE__};
112959a430aSYi Wu   std::time_t current_time;
113959a430aSYi Wu   std::time(&current_time);
114959a430aSYi Wu   CtimeBuffer(str, sizeof(str), current_time, terminator);
115959a430aSYi Wu 
116959a430aSYi Wu   // Pad space on the last two byte `\n\0`, start at index 24 included.
117959a430aSYi Wu   CopyAndPad(arg, str, length, 24);
118959a430aSYi Wu }
119959a430aSYi Wu 
12078ccffc0SDavid Truby std::intptr_t RTNAME(Malloc)(std::size_t size) {
12178ccffc0SDavid Truby   return reinterpret_cast<std::intptr_t>(std::malloc(size));
12278ccffc0SDavid Truby }
12378ccffc0SDavid Truby 
1241d4238beSPeixin-Qiao // RESULT = IARGC()
1251d4238beSPeixin-Qiao std::int32_t FORTRAN_PROCEDURE_NAME(iargc)() { return RTNAME(ArgumentCount)(); }
1261d4238beSPeixin-Qiao 
1271d4238beSPeixin-Qiao // CALL GETARG(N, ARG)
1281d4238beSPeixin-Qiao void FORTRAN_PROCEDURE_NAME(getarg)(
1294aa04245SPeter Klausler     std::int32_t &n, char *arg, std::int64_t length) {
1301d4238beSPeixin-Qiao   Descriptor value{*Descriptor::Create(1, length, arg, 0)};
1311d4238beSPeixin-Qiao   (void)RTNAME(GetCommandArgument)(
1321d4238beSPeixin-Qiao       n, &value, nullptr, nullptr, __FILE__, __LINE__);
1331d4238beSPeixin-Qiao }
13418af032cSYi Wu 
13518af032cSYi Wu // CALL GETLOG(USRNAME)
1364aa04245SPeter Klausler void FORTRAN_PROCEDURE_NAME(getlog)(char *arg, std::int64_t length) {
13718af032cSYi Wu #if _REENTRANT || _POSIX_C_SOURCE >= 199506L
1384aa04245SPeter Klausler   if (length >= 1 && getlogin_r(arg, length) == 0) {
1394aa04245SPeter Klausler     auto loginLen{std::strlen(arg)};
1404aa04245SPeter Klausler     std::memset(
1414aa04245SPeter Klausler         arg + loginLen, ' ', static_cast<std::size_t>(length) - loginLen);
1424aa04245SPeter Klausler     return;
14318af032cSYi Wu   }
1444aa04245SPeter Klausler #endif
1454aa04245SPeter Klausler #if _WIN32
14618af032cSYi Wu   GetUsernameEnvVar("USERNAME", arg, length);
14718af032cSYi Wu #else
14818af032cSYi Wu   GetUsernameEnvVar("LOGNAME", arg, length);
14918af032cSYi Wu #endif
15018af032cSYi Wu }
15118af032cSYi Wu 
15278ccffc0SDavid Truby void RTNAME(Free)(std::intptr_t ptr) {
15378ccffc0SDavid Truby   std::free(reinterpret_cast<void *>(ptr));
15478ccffc0SDavid Truby }
15578ccffc0SDavid Truby 
156afa52de9STom Eccles std::int64_t RTNAME(Signal)(std::int64_t number, void (*handler)(int)) {
157afa52de9STom Eccles   // using auto for portability:
158afa52de9STom Eccles   // on Windows, this is a void *
159afa52de9STom Eccles   // on POSIX, this has the same type as handler
160afa52de9STom Eccles   auto result = signal(number, handler);
161afa52de9STom Eccles 
162afa52de9STom Eccles   // GNU defines the intrinsic as returning an integer, not a pointer. So we
163afa52de9STom Eccles   // have to reinterpret_cast
164afa52de9STom Eccles   return static_cast<int64_t>(reinterpret_cast<std::uintptr_t>(result));
165afa52de9STom Eccles }
166afa52de9STom Eccles 
167b64c26f3STom Eccles // CALL SLEEP(SECONDS)
168b64c26f3STom Eccles void RTNAME(Sleep)(std::int64_t seconds) {
169b64c26f3STom Eccles   // ensure that conversion to unsigned makes sense,
170b64c26f3STom Eccles   // sleep(0) is an immidiate return anyway
171b64c26f3STom Eccles   if (seconds < 1) {
172b64c26f3STom Eccles     return;
173b64c26f3STom Eccles   }
174227fe1c1SDavid Truby #if _WIN32
175227fe1c1SDavid Truby   Sleep(seconds * 1000);
176227fe1c1SDavid Truby #else
177227fe1c1SDavid Truby   sleep(seconds);
178227fe1c1SDavid Truby #endif
179b64c26f3STom Eccles }
180b64c26f3STom Eccles 
181668a58b8STom Eccles // TODO: not supported on Windows
182668a58b8STom Eccles #ifndef _WIN32
183668a58b8STom Eccles std::int64_t FORTRAN_PROCEDURE_NAME(access)(const char *name,
184668a58b8STom Eccles     std::int64_t nameLength, const char *mode, std::int64_t modeLength) {
185668a58b8STom Eccles   std::int64_t ret{-1};
186668a58b8STom Eccles   if (nameLength <= 0 || modeLength <= 0 || !name || !mode) {
187668a58b8STom Eccles     return ret;
188668a58b8STom Eccles   }
189668a58b8STom Eccles 
190668a58b8STom Eccles   // ensure name is null terminated
191668a58b8STom Eccles   char *newName{nullptr};
192668a58b8STom Eccles   if (name[nameLength - 1] != '\0') {
193668a58b8STom Eccles     newName = static_cast<char *>(std::malloc(nameLength + 1));
194668a58b8STom Eccles     std::memcpy(newName, name, nameLength);
195668a58b8STom Eccles     newName[nameLength] = '\0';
196668a58b8STom Eccles     name = newName;
197668a58b8STom Eccles   }
198668a58b8STom Eccles 
199668a58b8STom Eccles   // calculate mode
200668a58b8STom Eccles   bool read{false};
201668a58b8STom Eccles   bool write{false};
202668a58b8STom Eccles   bool execute{false};
203668a58b8STom Eccles   bool exists{false};
204668a58b8STom Eccles   int imode{0};
205668a58b8STom Eccles 
206668a58b8STom Eccles   for (std::int64_t i = 0; i < modeLength; ++i) {
207668a58b8STom Eccles     switch (mode[i]) {
208668a58b8STom Eccles     case 'r':
209668a58b8STom Eccles       read = true;
210668a58b8STom Eccles       break;
211668a58b8STom Eccles     case 'w':
212668a58b8STom Eccles       write = true;
213668a58b8STom Eccles       break;
214668a58b8STom Eccles     case 'x':
215668a58b8STom Eccles       execute = true;
216668a58b8STom Eccles       break;
217668a58b8STom Eccles     case ' ':
218668a58b8STom Eccles       exists = true;
219668a58b8STom Eccles       break;
220668a58b8STom Eccles     default:
221668a58b8STom Eccles       // invalid mode
222668a58b8STom Eccles       goto cleanup;
223668a58b8STom Eccles     }
224668a58b8STom Eccles   }
225668a58b8STom Eccles   if (!read && !write && !execute && !exists) {
226668a58b8STom Eccles     // invalid mode
227668a58b8STom Eccles     goto cleanup;
228668a58b8STom Eccles   }
229668a58b8STom Eccles 
230668a58b8STom Eccles   if (!read && !write && !execute) {
231668a58b8STom Eccles     imode = F_OK;
232668a58b8STom Eccles   } else {
233668a58b8STom Eccles     if (read) {
234668a58b8STom Eccles       imode |= R_OK;
235668a58b8STom Eccles     }
236668a58b8STom Eccles     if (write) {
237668a58b8STom Eccles       imode |= W_OK;
238668a58b8STom Eccles     }
239668a58b8STom Eccles     if (execute) {
240668a58b8STom Eccles       imode |= X_OK;
241668a58b8STom Eccles     }
242668a58b8STom Eccles   }
243668a58b8STom Eccles   ret = access(name, imode);
244668a58b8STom Eccles 
245668a58b8STom Eccles cleanup:
246668a58b8STom Eccles   if (newName) {
247668a58b8STom Eccles     free(newName);
248668a58b8STom Eccles   }
249668a58b8STom Eccles   return ret;
250668a58b8STom Eccles }
251668a58b8STom Eccles #endif
252668a58b8STom Eccles 
2535a34e6fdSJean-Didier PAILLEUX // CHDIR(DIR)
2545a34e6fdSJean-Didier PAILLEUX int RTNAME(Chdir)(const char *name) {
2555a34e6fdSJean-Didier PAILLEUX // chdir alias seems to be deprecated on Windows.
2565a34e6fdSJean-Didier PAILLEUX #ifndef _WIN32
2575a34e6fdSJean-Didier PAILLEUX   return chdir(name);
2585a34e6fdSJean-Didier PAILLEUX #else
2595a34e6fdSJean-Didier PAILLEUX   return _chdir(name);
2605a34e6fdSJean-Didier PAILLEUX #endif
2615a34e6fdSJean-Didier PAILLEUX }
2625a34e6fdSJean-Didier PAILLEUX 
263*ecc71de5SJean-Didier PAILLEUX int FORTRAN_PROCEDURE_NAME(ierrno)() { return errno; }
264*ecc71de5SJean-Didier PAILLEUX 
2651d4238beSPeixin-Qiao } // namespace Fortran::runtime
266627a8ac7SPeter Klausler } // extern "C"
267