xref: /llvm-project/flang/runtime/file.cpp (revision 874a3ba868e3738d6ee21bfe032c89c6c8be3969)
1651f58bfSDiana Picus //===-- runtime/file.cpp --------------------------------------------------===//
2352d347aSAlexis Perry //
3352d347aSAlexis Perry // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4352d347aSAlexis Perry // See https://llvm.org/LICENSE.txt for license information.
5352d347aSAlexis Perry // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6352d347aSAlexis Perry //
7352d347aSAlexis Perry //===----------------------------------------------------------------------===//
8352d347aSAlexis Perry 
9352d347aSAlexis Perry #include "file.h"
108ebf7411SSlava Zakharin #include "tools.h"
11830c0b90SPeter Klausler #include "flang/Runtime/magic-numbers.h"
12830c0b90SPeter Klausler #include "flang/Runtime/memory.h"
131b183918STim Keith #include <algorithm>
14352d347aSAlexis Perry #include <cerrno>
15352d347aSAlexis Perry #include <cstring>
16352d347aSAlexis Perry #include <fcntl.h>
17352d347aSAlexis Perry #include <stdlib.h>
188cfe9c02SMehdi Chinoune #include <sys/stat.h>
19ddb68d24SIsuru Fernando #ifdef _WIN32
20864d2531SSlava Zakharin #include "flang/Common/windows-include.h"
21ddb68d24SIsuru Fernando #include <io.h>
22ddb68d24SIsuru Fernando #else
23352d347aSAlexis Perry #include <unistd.h>
24ddb68d24SIsuru Fernando #endif
25352d347aSAlexis Perry 
26352d347aSAlexis Perry namespace Fortran::runtime::io {
27352d347aSAlexis Perry 
2895696d56Speter klausler void OpenFile::set_path(OwningPtr<char> &&path, std::size_t bytes) {
2995696d56Speter klausler   path_ = std::move(path);
3095696d56Speter klausler   pathLength_ = bytes;
31352d347aSAlexis Perry }
3295696d56Speter klausler 
33ddb68d24SIsuru Fernando static int openfile_mkstemp(IoErrorHandler &handler) {
34ddb68d24SIsuru Fernando #ifdef _WIN32
35ddb68d24SIsuru Fernando   const unsigned int uUnique{0};
366cf7fe4aSMuhammad Omair Javaid   // GetTempFileNameA needs a directory name < MAX_PATH-14 characters in length.
376cf7fe4aSMuhammad Omair Javaid   // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettempfilenamea
386cf7fe4aSMuhammad Omair Javaid   char tempDirName[MAX_PATH - 14];
396cf7fe4aSMuhammad Omair Javaid   char tempFileName[MAX_PATH];
40ddb68d24SIsuru Fernando   unsigned long nBufferLength{sizeof(tempDirName)};
416cf7fe4aSMuhammad Omair Javaid   nBufferLength = ::GetTempPathA(nBufferLength, tempDirName);
42ddb68d24SIsuru Fernando   if (nBufferLength > sizeof(tempDirName) || nBufferLength == 0) {
43ddb68d24SIsuru Fernando     return -1;
44ddb68d24SIsuru Fernando   }
456cf7fe4aSMuhammad Omair Javaid   if (::GetTempFileNameA(tempDirName, "Fortran", uUnique, tempFileName) == 0) {
46ddb68d24SIsuru Fernando     return -1;
47ddb68d24SIsuru Fernando   }
486cf7fe4aSMuhammad Omair Javaid   int fd{::_open(tempFileName, _O_CREAT | _O_BINARY | _O_TEMPORARY | _O_RDWR,
49991696c2SPeter Klausler       _S_IREAD | _S_IWRITE)};
50ddb68d24SIsuru Fernando #else
51ddb68d24SIsuru Fernando   char path[]{"/tmp/Fortran-Scratch-XXXXXX"};
52ddb68d24SIsuru Fernando   int fd{::mkstemp(path)};
53ddb68d24SIsuru Fernando #endif
54ddb68d24SIsuru Fernando   if (fd < 0) {
55ddb68d24SIsuru Fernando     handler.SignalErrno();
56ddb68d24SIsuru Fernando   }
57ddb68d24SIsuru Fernando #ifndef _WIN32
58ddb68d24SIsuru Fernando   ::unlink(path);
59ddb68d24SIsuru Fernando #endif
60ddb68d24SIsuru Fernando   return fd;
61ddb68d24SIsuru Fernando }
62ddb68d24SIsuru Fernando 
6371e0261fSSlava Zakharin void OpenFile::Open(OpenStatus status, Fortran::common::optional<Action> action,
64ea4758a1Speter klausler     Position position, IoErrorHandler &handler) {
65ea4758a1Speter klausler   if (fd_ >= 0 &&
66ea4758a1Speter klausler       (status == OpenStatus::Old || status == OpenStatus::Unknown)) {
67352d347aSAlexis Perry     return;
68352d347aSAlexis Perry   }
693f6dbf1aSPeter Klausler   CloseFd(handler);
70ea4758a1Speter klausler   if (status == OpenStatus::Scratch) {
71352d347aSAlexis Perry     if (path_.get()) {
723b635714Speter klausler       handler.SignalError("FILE= must not appear with STATUS='SCRATCH'");
73352d347aSAlexis Perry       path_.reset();
74352d347aSAlexis Perry     }
75ea4758a1Speter klausler     if (!action) {
76ea4758a1Speter klausler       action = Action::ReadWrite;
77ea4758a1Speter klausler     }
78ddb68d24SIsuru Fernando     fd_ = openfile_mkstemp(handler);
79ea4758a1Speter klausler   } else {
80352d347aSAlexis Perry     if (!path_.get()) {
81d8334c43Speter klausler       handler.SignalError("FILE= is required");
823b635714Speter klausler       return;
83352d347aSAlexis Perry     }
84ea4758a1Speter klausler     int flags{0};
85991696c2SPeter Klausler #ifdef _WIN32
86991696c2SPeter Klausler     // We emit explicit CR+LF line endings and cope with them on input
87991696c2SPeter Klausler     // for formatted files, since we can't yet always know now at OPEN
88991696c2SPeter Klausler     // time whether the file is formatted or not.
89991696c2SPeter Klausler     flags |= O_BINARY;
90991696c2SPeter Klausler #endif
91ea4758a1Speter klausler     if (status != OpenStatus::Old) {
92ea4758a1Speter klausler       flags |= O_CREAT;
93ea4758a1Speter klausler     }
94ea4758a1Speter klausler     if (status == OpenStatus::New) {
95ea4758a1Speter klausler       flags |= O_EXCL;
96ea4758a1Speter klausler     } else if (status == OpenStatus::Replace) {
97ea4758a1Speter klausler       flags |= O_TRUNC;
98ea4758a1Speter klausler     }
99ea4758a1Speter klausler     if (!action) {
100e5a4f730SPeter Klausler       // Try to open read/write, back off to read-only or even write-only
101e5a4f730SPeter Klausler       // on failure
102ea4758a1Speter klausler       fd_ = ::open(path_.get(), flags | O_RDWR, 0600);
103ea4758a1Speter klausler       if (fd_ >= 0) {
104ea4758a1Speter klausler         action = Action::ReadWrite;
105ea4758a1Speter klausler       } else {
106e5a4f730SPeter Klausler         fd_ = ::open(path_.get(), flags | O_RDONLY, 0600);
107e5a4f730SPeter Klausler         if (fd_ >= 0) {
108ea4758a1Speter klausler           action = Action::Read;
109e5a4f730SPeter Klausler         } else {
110e5a4f730SPeter Klausler           action = Action::Write;
111e5a4f730SPeter Klausler         }
112ea4758a1Speter klausler       }
113ea4758a1Speter klausler     }
114ea4758a1Speter klausler     if (fd_ < 0) {
115ea4758a1Speter klausler       switch (*action) {
116ea4758a1Speter klausler       case Action::Read:
117ea4758a1Speter klausler         flags |= O_RDONLY;
118ea4758a1Speter klausler         break;
119ea4758a1Speter klausler       case Action::Write:
120ea4758a1Speter klausler         flags |= O_WRONLY;
121ea4758a1Speter klausler         break;
122ea4758a1Speter klausler       case Action::ReadWrite:
123ea4758a1Speter klausler         flags |= O_RDWR;
124ea4758a1Speter klausler         break;
125ea4758a1Speter klausler       }
126352d347aSAlexis Perry       fd_ = ::open(path_.get(), flags, 0600);
127352d347aSAlexis Perry       if (fd_ < 0) {
128352d347aSAlexis Perry         handler.SignalErrno();
129352d347aSAlexis Perry       }
130ea4758a1Speter klausler     }
131ea4758a1Speter klausler   }
132ea4758a1Speter klausler   RUNTIME_CHECK(handler, action.has_value());
133352d347aSAlexis Perry   pending_.reset();
134a8f2d185SPeter Klausler   if (fd_ >= 0 && position == Position::Append && !RawSeekToEnd()) {
1359a163ffeSPeter Klausler     handler.SignalError(IostatOpenBadAppend);
13695696d56Speter klausler   }
137*874a3ba8SPeter Klausler   isTerminal_ = fd_ >= 0 && IsATerminal(fd_);
138ea4758a1Speter klausler   mayRead_ = *action != Action::Write;
139ea4758a1Speter klausler   mayWrite_ = *action != Action::Read;
140ea4758a1Speter klausler   if (status == OpenStatus::Old || status == OpenStatus::Unknown) {
141ea4758a1Speter klausler     knownSize_.reset();
142d8334c43Speter klausler #ifndef _WIN32
143d8334c43Speter klausler     struct stat buf;
144a8f2d185SPeter Klausler     if (fd_ >= 0 && ::fstat(fd_, &buf) == 0) {
145d8334c43Speter klausler       mayPosition_ = S_ISREG(buf.st_mode);
146d8334c43Speter klausler       knownSize_ = buf.st_size;
147d8334c43Speter klausler     }
148d8334c43Speter klausler #else // TODO: _WIN32
149d8334c43Speter klausler     mayPosition_ = true;
150d8334c43Speter klausler #endif
151ea4758a1Speter klausler   } else {
152ea4758a1Speter klausler     knownSize_ = 0;
153d8334c43Speter klausler     mayPosition_ = true;
154ea4758a1Speter klausler   }
15580cdf0dbSPeter Klausler   openPosition_ = position; // for INQUIRE(POSITION=)
156352d347aSAlexis Perry }
157352d347aSAlexis Perry 
158f7be2518Speter klausler void OpenFile::Predefine(int fd) {
159f7be2518Speter klausler   fd_ = fd;
160f7be2518Speter klausler   path_.reset();
161f7be2518Speter klausler   pathLength_ = 0;
162f7be2518Speter klausler   position_ = 0;
163f7be2518Speter klausler   knownSize_.reset();
164f7be2518Speter klausler   nextId_ = 0;
165f7be2518Speter klausler   pending_.reset();
166*874a3ba8SPeter Klausler   isTerminal_ = fd == 2 || IsATerminal(fd_);
167ea4758a1Speter klausler   mayRead_ = fd == 0;
168ea4758a1Speter klausler   mayWrite_ = fd != 0;
169ea4758a1Speter klausler   mayPosition_ = false;
170991696c2SPeter Klausler #ifdef _WIN32
171991696c2SPeter Klausler   isWindowsTextFile_ = true;
172991696c2SPeter Klausler #endif
173f7be2518Speter klausler }
174f7be2518Speter klausler 
17595696d56Speter klausler void OpenFile::Close(CloseStatus status, IoErrorHandler &handler) {
176352d347aSAlexis Perry   pending_.reset();
177352d347aSAlexis Perry   knownSize_.reset();
17895696d56Speter klausler   switch (status) {
1791f879005STim Keith   case CloseStatus::Keep:
1801f879005STim Keith     break;
18195696d56Speter klausler   case CloseStatus::Delete:
182352d347aSAlexis Perry     if (path_.get()) {
183352d347aSAlexis Perry       ::unlink(path_.get());
184352d347aSAlexis Perry     }
185352d347aSAlexis Perry     break;
186352d347aSAlexis Perry   }
187352d347aSAlexis Perry   path_.reset();
1883f6dbf1aSPeter Klausler   CloseFd(handler);
189352d347aSAlexis Perry }
190352d347aSAlexis Perry 
191f7be2518Speter klausler std::size_t OpenFile::Read(FileOffset at, char *buffer, std::size_t minBytes,
192352d347aSAlexis Perry     std::size_t maxBytes, IoErrorHandler &handler) {
193352d347aSAlexis Perry   if (maxBytes == 0) {
194352d347aSAlexis Perry     return 0;
195352d347aSAlexis Perry   }
196352d347aSAlexis Perry   CheckOpen(handler);
197352d347aSAlexis Perry   if (!Seek(at, handler)) {
198352d347aSAlexis Perry     return 0;
199352d347aSAlexis Perry   }
2007926969aSpeter klausler   minBytes = std::min(minBytes, maxBytes);
201352d347aSAlexis Perry   std::size_t got{0};
202352d347aSAlexis Perry   while (got < minBytes) {
203352d347aSAlexis Perry     auto chunk{::read(fd_, buffer + got, maxBytes - got)};
204352d347aSAlexis Perry     if (chunk == 0) {
205352d347aSAlexis Perry       break;
2067926969aSpeter klausler     } else if (chunk < 0) {
207352d347aSAlexis Perry       auto err{errno};
208352d347aSAlexis Perry       if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) {
209352d347aSAlexis Perry         handler.SignalError(err);
210352d347aSAlexis Perry         break;
211352d347aSAlexis Perry       }
212352d347aSAlexis Perry     } else {
21380cdf0dbSPeter Klausler       SetPosition(position_ + chunk);
214352d347aSAlexis Perry       got += chunk;
215352d347aSAlexis Perry     }
216352d347aSAlexis Perry   }
217352d347aSAlexis Perry   return got;
218352d347aSAlexis Perry }
219352d347aSAlexis Perry 
220f7be2518Speter klausler std::size_t OpenFile::Write(FileOffset at, const char *buffer,
221f7be2518Speter klausler     std::size_t bytes, IoErrorHandler &handler) {
222352d347aSAlexis Perry   if (bytes == 0) {
223352d347aSAlexis Perry     return 0;
224352d347aSAlexis Perry   }
225352d347aSAlexis Perry   CheckOpen(handler);
226352d347aSAlexis Perry   if (!Seek(at, handler)) {
227352d347aSAlexis Perry     return 0;
228352d347aSAlexis Perry   }
229352d347aSAlexis Perry   std::size_t put{0};
230352d347aSAlexis Perry   while (put < bytes) {
231352d347aSAlexis Perry     auto chunk{::write(fd_, buffer + put, bytes - put)};
232352d347aSAlexis Perry     if (chunk >= 0) {
23380cdf0dbSPeter Klausler       SetPosition(position_ + chunk);
234352d347aSAlexis Perry       put += chunk;
235352d347aSAlexis Perry     } else {
236352d347aSAlexis Perry       auto err{errno};
237352d347aSAlexis Perry       if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) {
238352d347aSAlexis Perry         handler.SignalError(err);
239352d347aSAlexis Perry         break;
240352d347aSAlexis Perry       }
241352d347aSAlexis Perry     }
242352d347aSAlexis Perry   }
243352d347aSAlexis Perry   if (knownSize_ && position_ > *knownSize_) {
244352d347aSAlexis Perry     knownSize_ = position_;
245352d347aSAlexis Perry   }
246352d347aSAlexis Perry   return put;
247352d347aSAlexis Perry }
248352d347aSAlexis Perry 
249ddb68d24SIsuru Fernando inline static int openfile_ftruncate(int fd, OpenFile::FileOffset at) {
250ddb68d24SIsuru Fernando #ifdef _WIN32
251cbad5761SMichael Kruse   return ::_chsize(fd, at);
252ddb68d24SIsuru Fernando #else
253ddb68d24SIsuru Fernando   return ::ftruncate(fd, at);
254ddb68d24SIsuru Fernando #endif
255ddb68d24SIsuru Fernando }
256ddb68d24SIsuru Fernando 
257f7be2518Speter klausler void OpenFile::Truncate(FileOffset at, IoErrorHandler &handler) {
258352d347aSAlexis Perry   CheckOpen(handler);
259352d347aSAlexis Perry   if (!knownSize_ || *knownSize_ != at) {
260ddb68d24SIsuru Fernando     if (openfile_ftruncate(fd_, at) != 0) {
261352d347aSAlexis Perry       handler.SignalErrno();
262352d347aSAlexis Perry     }
263352d347aSAlexis Perry     knownSize_ = at;
264352d347aSAlexis Perry   }
265352d347aSAlexis Perry }
266352d347aSAlexis Perry 
267352d347aSAlexis Perry // The operation is performed immediately; the results are saved
268352d347aSAlexis Perry // to be claimed by a later WAIT statement.
269352d347aSAlexis Perry // TODO: True asynchronicity
270352d347aSAlexis Perry int OpenFile::ReadAsynchronously(
271f7be2518Speter klausler     FileOffset at, char *buffer, std::size_t bytes, IoErrorHandler &handler) {
272352d347aSAlexis Perry   CheckOpen(handler);
273352d347aSAlexis Perry   int iostat{0};
274352d347aSAlexis Perry   for (std::size_t got{0}; got < bytes;) {
275352d347aSAlexis Perry #if _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L
276352d347aSAlexis Perry     auto chunk{::pread(fd_, buffer + got, bytes - got, at)};
277352d347aSAlexis Perry #else
278f7be2518Speter klausler     auto chunk{Seek(at, handler) ? ::read(fd_, buffer + got, bytes - got) : -1};
279352d347aSAlexis Perry #endif
280352d347aSAlexis Perry     if (chunk == 0) {
281352d347aSAlexis Perry       iostat = FORTRAN_RUNTIME_IOSTAT_END;
282352d347aSAlexis Perry       break;
283352d347aSAlexis Perry     }
284352d347aSAlexis Perry     if (chunk < 0) {
285352d347aSAlexis Perry       auto err{errno};
286352d347aSAlexis Perry       if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) {
287352d347aSAlexis Perry         iostat = err;
288352d347aSAlexis Perry         break;
289352d347aSAlexis Perry       }
290352d347aSAlexis Perry     } else {
291352d347aSAlexis Perry       at += chunk;
292352d347aSAlexis Perry       got += chunk;
293352d347aSAlexis Perry     }
294352d347aSAlexis Perry   }
295352d347aSAlexis Perry   return PendingResult(handler, iostat);
296352d347aSAlexis Perry }
297352d347aSAlexis Perry 
298352d347aSAlexis Perry // TODO: True asynchronicity
299f7be2518Speter klausler int OpenFile::WriteAsynchronously(FileOffset at, const char *buffer,
300f7be2518Speter klausler     std::size_t bytes, IoErrorHandler &handler) {
301352d347aSAlexis Perry   CheckOpen(handler);
302352d347aSAlexis Perry   int iostat{0};
303352d347aSAlexis Perry   for (std::size_t put{0}; put < bytes;) {
304352d347aSAlexis Perry #if _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L
305352d347aSAlexis Perry     auto chunk{::pwrite(fd_, buffer + put, bytes - put, at)};
306352d347aSAlexis Perry #else
307f7be2518Speter klausler     auto chunk{
308f7be2518Speter klausler         Seek(at, handler) ? ::write(fd_, buffer + put, bytes - put) : -1};
309352d347aSAlexis Perry #endif
310352d347aSAlexis Perry     if (chunk >= 0) {
311352d347aSAlexis Perry       at += chunk;
312352d347aSAlexis Perry       put += chunk;
313352d347aSAlexis Perry     } else {
314352d347aSAlexis Perry       auto err{errno};
315352d347aSAlexis Perry       if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) {
316352d347aSAlexis Perry         iostat = err;
317352d347aSAlexis Perry         break;
318352d347aSAlexis Perry       }
319352d347aSAlexis Perry     }
320352d347aSAlexis Perry   }
321352d347aSAlexis Perry   return PendingResult(handler, iostat);
322352d347aSAlexis Perry }
323352d347aSAlexis Perry 
324352d347aSAlexis Perry void OpenFile::Wait(int id, IoErrorHandler &handler) {
32571e0261fSSlava Zakharin   Fortran::common::optional<int> ioStat;
326352d347aSAlexis Perry   Pending *prev{nullptr};
327352d347aSAlexis Perry   for (Pending *p{pending_.get()}; p; p = (prev = p)->next.get()) {
328352d347aSAlexis Perry     if (p->id == id) {
329352d347aSAlexis Perry       ioStat = p->ioStat;
330352d347aSAlexis Perry       if (prev) {
331352d347aSAlexis Perry         prev->next.reset(p->next.release());
332352d347aSAlexis Perry       } else {
333352d347aSAlexis Perry         pending_.reset(p->next.release());
334352d347aSAlexis Perry       }
335352d347aSAlexis Perry       break;
336352d347aSAlexis Perry     }
337352d347aSAlexis Perry   }
338352d347aSAlexis Perry   if (ioStat) {
339352d347aSAlexis Perry     handler.SignalError(*ioStat);
340352d347aSAlexis Perry   }
341352d347aSAlexis Perry }
342352d347aSAlexis Perry 
343352d347aSAlexis Perry void OpenFile::WaitAll(IoErrorHandler &handler) {
344352d347aSAlexis Perry   while (true) {
345352d347aSAlexis Perry     int ioStat;
346352d347aSAlexis Perry     if (pending_) {
347352d347aSAlexis Perry       ioStat = pending_->ioStat;
348352d347aSAlexis Perry       pending_.reset(pending_->next.release());
349352d347aSAlexis Perry     } else {
350352d347aSAlexis Perry       return;
351352d347aSAlexis Perry     }
352352d347aSAlexis Perry     handler.SignalError(ioStat);
353352d347aSAlexis Perry   }
354352d347aSAlexis Perry }
355352d347aSAlexis Perry 
35680cdf0dbSPeter Klausler Position OpenFile::InquirePosition() const {
35780cdf0dbSPeter Klausler   if (openPosition_) { // from OPEN statement
35880cdf0dbSPeter Klausler     return *openPosition_;
35980cdf0dbSPeter Klausler   } else { // unit has been repositioned since opening
36080cdf0dbSPeter Klausler     if (position_ == knownSize_.value_or(position_ + 1)) {
36180cdf0dbSPeter Klausler       return Position::Append;
36280cdf0dbSPeter Klausler     } else if (position_ == 0 && mayPosition_) {
36380cdf0dbSPeter Klausler       return Position::Rewind;
36480cdf0dbSPeter Klausler     } else {
36580cdf0dbSPeter Klausler       return Position::AsIs; // processor-dependent & no common behavior
36680cdf0dbSPeter Klausler     }
36780cdf0dbSPeter Klausler   }
36880cdf0dbSPeter Klausler }
36980cdf0dbSPeter Klausler 
37095696d56Speter klausler void OpenFile::CheckOpen(const Terminator &terminator) {
371352d347aSAlexis Perry   RUNTIME_CHECK(terminator, fd_ >= 0);
372352d347aSAlexis Perry }
373352d347aSAlexis Perry 
374f7be2518Speter klausler bool OpenFile::Seek(FileOffset at, IoErrorHandler &handler) {
375352d347aSAlexis Perry   if (at == position_) {
376352d347aSAlexis Perry     return true;
377352d347aSAlexis Perry   } else if (RawSeek(at)) {
37880cdf0dbSPeter Klausler     SetPosition(at);
379352d347aSAlexis Perry     return true;
380352d347aSAlexis Perry   } else {
38103c066abSPeter Klausler     handler.SignalError(IostatCannotReposition);
382352d347aSAlexis Perry     return false;
383352d347aSAlexis Perry   }
384352d347aSAlexis Perry }
385352d347aSAlexis Perry 
386f7be2518Speter klausler bool OpenFile::RawSeek(FileOffset at) {
387352d347aSAlexis Perry #ifdef _LARGEFILE64_SOURCE
38895696d56Speter klausler   return ::lseek64(fd_, at, SEEK_SET) == at;
389352d347aSAlexis Perry #else
39095696d56Speter klausler   return ::lseek(fd_, at, SEEK_SET) == at;
391352d347aSAlexis Perry #endif
392352d347aSAlexis Perry }
393352d347aSAlexis Perry 
39495696d56Speter klausler bool OpenFile::RawSeekToEnd() {
39595696d56Speter klausler #ifdef _LARGEFILE64_SOURCE
39695696d56Speter klausler   std::int64_t at{::lseek64(fd_, 0, SEEK_END)};
39795696d56Speter klausler #else
39895696d56Speter klausler   std::int64_t at{::lseek(fd_, 0, SEEK_END)};
39995696d56Speter klausler #endif
40095696d56Speter klausler   if (at >= 0) {
40195696d56Speter klausler     knownSize_ = at;
40295696d56Speter klausler     return true;
40395696d56Speter klausler   } else {
40495696d56Speter klausler     return false;
40595696d56Speter klausler   }
40695696d56Speter klausler }
40795696d56Speter klausler 
40895696d56Speter klausler int OpenFile::PendingResult(const Terminator &terminator, int iostat) {
409352d347aSAlexis Perry   int id{nextId_++};
41098d576c7Speter klausler   pending_ = New<Pending>{terminator}(id, iostat, std::move(pending_));
411352d347aSAlexis Perry   return id;
412352d347aSAlexis Perry }
4137926969aSpeter klausler 
4143f6dbf1aSPeter Klausler void OpenFile::CloseFd(IoErrorHandler &handler) {
4153f6dbf1aSPeter Klausler   if (fd_ >= 0) {
4163f6dbf1aSPeter Klausler     if (fd_ <= 2) {
4173f6dbf1aSPeter Klausler       // don't actually close a standard file descriptor, we might need it
4183f6dbf1aSPeter Klausler     } else {
4193f6dbf1aSPeter Klausler       if (::close(fd_) != 0) {
4203f6dbf1aSPeter Klausler         handler.SignalErrno();
4213f6dbf1aSPeter Klausler       }
4223f6dbf1aSPeter Klausler     }
4233f6dbf1aSPeter Klausler     fd_ = -1;
4243f6dbf1aSPeter Klausler   }
4253f6dbf1aSPeter Klausler }
4263f6dbf1aSPeter Klausler 
4278ebf7411SSlava Zakharin #if !defined(RT_DEVICE_COMPILATION)
4287926969aSpeter klausler bool IsATerminal(int fd) { return ::isatty(fd); }
429d8334c43Speter klausler 
430770685e2SMartin Storsjö #if defined(_WIN32) && !defined(F_OK)
431bcd05599SMichael Kruse // Access flags are normally defined in unistd.h, which unavailable under
432bcd05599SMichael Kruse // Windows. Instead, define the flags as documented at
433bcd05599SMichael Kruse // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/access-waccess
434770685e2SMartin Storsjö // On Mingw, io.h does define these same constants - so check whether they
435770685e2SMartin Storsjö // already are defined before defining these.
436bcd05599SMichael Kruse #define F_OK 00
437bcd05599SMichael Kruse #define W_OK 02
438bcd05599SMichael Kruse #define R_OK 04
439bcd05599SMichael Kruse #endif
440bcd05599SMichael Kruse 
441d8334c43Speter klausler bool IsExtant(const char *path) { return ::access(path, F_OK) == 0; }
442d8334c43Speter klausler bool MayRead(const char *path) { return ::access(path, R_OK) == 0; }
443d8334c43Speter klausler bool MayWrite(const char *path) { return ::access(path, W_OK) == 0; }
444d8334c43Speter klausler bool MayReadAndWrite(const char *path) {
445d8334c43Speter klausler   return ::access(path, R_OK | W_OK) == 0;
446d8334c43Speter klausler }
4479878facfSPeter Klausler 
4489878facfSPeter Klausler std::int64_t SizeInBytes(const char *path) {
4499878facfSPeter Klausler #ifndef _WIN32
4509878facfSPeter Klausler   struct stat buf;
4519878facfSPeter Klausler   if (::stat(path, &buf) == 0) {
4529878facfSPeter Klausler     return buf.st_size;
4539878facfSPeter Klausler   }
4549878facfSPeter Klausler #else // TODO: _WIN32
4559878facfSPeter Klausler #endif
4569878facfSPeter Klausler   // No Fortran compiler signals an error
4579878facfSPeter Klausler   return -1;
4589878facfSPeter Klausler }
4598ebf7411SSlava Zakharin #else // defined(RT_DEVICE_COMPILATION)
4603ba07918SSlava Zakharin RT_API_ATTRS bool IsATerminal(int fd) {
4618ebf7411SSlava Zakharin   Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
4628ebf7411SSlava Zakharin }
4633ba07918SSlava Zakharin RT_API_ATTRS bool IsExtant(const char *path) {
4648ebf7411SSlava Zakharin   Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
4658ebf7411SSlava Zakharin }
4663ba07918SSlava Zakharin RT_API_ATTRS bool MayRead(const char *path) {
4678ebf7411SSlava Zakharin   Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
4688ebf7411SSlava Zakharin }
4693ba07918SSlava Zakharin RT_API_ATTRS bool MayWrite(const char *path) {
4708ebf7411SSlava Zakharin   Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
4718ebf7411SSlava Zakharin }
4723ba07918SSlava Zakharin RT_API_ATTRS bool MayReadAndWrite(const char *path) {
4738ebf7411SSlava Zakharin   Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
4748ebf7411SSlava Zakharin }
4753ba07918SSlava Zakharin RT_API_ATTRS std::int64_t SizeInBytes(const char *path) {
4768ebf7411SSlava Zakharin   Terminator{__FILE__, __LINE__}.Crash("%s: unsupported", RT_PRETTY_FUNCTION);
4778ebf7411SSlava Zakharin }
4788ebf7411SSlava Zakharin #endif // defined(RT_DEVICE_COMPILATION)
4799878facfSPeter Klausler 
4801f879005STim Keith } // namespace Fortran::runtime::io
481