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"
10830c0b90SPeter Klausler #include "flang/Runtime/magic-numbers.h"
11830c0b90SPeter Klausler #include "flang/Runtime/memory.h"
121b183918STim Keith #include <algorithm>
13352d347aSAlexis Perry #include <cerrno>
14352d347aSAlexis Perry #include <cstring>
15352d347aSAlexis Perry #include <fcntl.h>
16352d347aSAlexis Perry #include <stdlib.h>
178cfe9c02SMehdi Chinoune #include <sys/stat.h>
18ddb68d24SIsuru Fernando #ifdef _WIN32
19e3d38b7bSMichael Kruse #define NOMINMAX
20ddb68d24SIsuru Fernando #include <io.h>
21ddb68d24SIsuru Fernando #include <windows.h>
22ddb68d24SIsuru Fernando #else
23352d347aSAlexis Perry #include <unistd.h>
24ddb68d24SIsuru Fernando #endif
25352d347aSAlexis Perry
26352d347aSAlexis Perry namespace Fortran::runtime::io {
27352d347aSAlexis Perry
set_path(OwningPtr<char> && path,std::size_t bytes)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
openfile_mkstemp(IoErrorHandler & handler)33ddb68d24SIsuru Fernando static int openfile_mkstemp(IoErrorHandler &handler) {
34ddb68d24SIsuru Fernando #ifdef _WIN32
35ddb68d24SIsuru Fernando const unsigned int uUnique{0};
36ddb68d24SIsuru Fernando // GetTempFileNameA needs a directory name < MAX_PATH-14 characters in length.
37ddb68d24SIsuru Fernando // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettempfilenamea
38ddb68d24SIsuru Fernando char tempDirName[MAX_PATH - 14];
39ddb68d24SIsuru Fernando char tempFileName[MAX_PATH];
40ddb68d24SIsuru Fernando unsigned long nBufferLength{sizeof(tempDirName)};
41ddb68d24SIsuru Fernando nBufferLength = ::GetTempPathA(nBufferLength, tempDirName);
42ddb68d24SIsuru Fernando if (nBufferLength > sizeof(tempDirName) || nBufferLength == 0) {
43ddb68d24SIsuru Fernando return -1;
44ddb68d24SIsuru Fernando }
45ddb68d24SIsuru Fernando if (::GetTempFileNameA(tempDirName, "Fortran", uUnique, tempFileName) == 0) {
46ddb68d24SIsuru Fernando return -1;
47ddb68d24SIsuru Fernando }
48991696c2SPeter Klausler 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
Open(OpenStatus status,std::optional<Action> action,Position position,IoErrorHandler & handler)63ea4758a1Speter klausler void OpenFile::Open(OpenStatus status, std::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();
13495696d56Speter klausler if (position == Position::Append && !RawSeekToEnd()) {
1359a163ffeSPeter Klausler handler.SignalError(IostatOpenBadAppend);
13695696d56Speter klausler }
13795696d56Speter klausler isTerminal_ = ::isatty(fd_) == 1;
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;
144d8334c43Speter klausler if (::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
Predefine(int fd)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();
166ea4758a1Speter klausler mayRead_ = fd == 0;
167ea4758a1Speter klausler mayWrite_ = fd != 0;
168ea4758a1Speter klausler mayPosition_ = false;
169991696c2SPeter Klausler #ifdef _WIN32
170991696c2SPeter Klausler isWindowsTextFile_ = true;
171991696c2SPeter Klausler #endif
172f7be2518Speter klausler }
173f7be2518Speter klausler
Close(CloseStatus status,IoErrorHandler & handler)17495696d56Speter klausler void OpenFile::Close(CloseStatus status, IoErrorHandler &handler) {
175352d347aSAlexis Perry pending_.reset();
176352d347aSAlexis Perry knownSize_.reset();
17795696d56Speter klausler switch (status) {
1781f879005STim Keith case CloseStatus::Keep:
1791f879005STim Keith break;
18095696d56Speter klausler case CloseStatus::Delete:
181352d347aSAlexis Perry if (path_.get()) {
182352d347aSAlexis Perry ::unlink(path_.get());
183352d347aSAlexis Perry }
184352d347aSAlexis Perry break;
185352d347aSAlexis Perry }
186352d347aSAlexis Perry path_.reset();
1873f6dbf1aSPeter Klausler CloseFd(handler);
188352d347aSAlexis Perry }
189352d347aSAlexis Perry
Read(FileOffset at,char * buffer,std::size_t minBytes,std::size_t maxBytes,IoErrorHandler & handler)190f7be2518Speter klausler std::size_t OpenFile::Read(FileOffset at, char *buffer, std::size_t minBytes,
191352d347aSAlexis Perry std::size_t maxBytes, IoErrorHandler &handler) {
192352d347aSAlexis Perry if (maxBytes == 0) {
193352d347aSAlexis Perry return 0;
194352d347aSAlexis Perry }
195352d347aSAlexis Perry CheckOpen(handler);
196352d347aSAlexis Perry if (!Seek(at, handler)) {
197352d347aSAlexis Perry return 0;
198352d347aSAlexis Perry }
1997926969aSpeter klausler minBytes = std::min(minBytes, maxBytes);
200352d347aSAlexis Perry std::size_t got{0};
201352d347aSAlexis Perry while (got < minBytes) {
202352d347aSAlexis Perry auto chunk{::read(fd_, buffer + got, maxBytes - got)};
203352d347aSAlexis Perry if (chunk == 0) {
204352d347aSAlexis Perry break;
2057926969aSpeter klausler } else if (chunk < 0) {
206352d347aSAlexis Perry auto err{errno};
207352d347aSAlexis Perry if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) {
208352d347aSAlexis Perry handler.SignalError(err);
209352d347aSAlexis Perry break;
210352d347aSAlexis Perry }
211352d347aSAlexis Perry } else {
21280cdf0dbSPeter Klausler SetPosition(position_ + chunk);
213352d347aSAlexis Perry got += chunk;
214352d347aSAlexis Perry }
215352d347aSAlexis Perry }
216352d347aSAlexis Perry return got;
217352d347aSAlexis Perry }
218352d347aSAlexis Perry
Write(FileOffset at,const char * buffer,std::size_t bytes,IoErrorHandler & handler)219f7be2518Speter klausler std::size_t OpenFile::Write(FileOffset at, const char *buffer,
220f7be2518Speter klausler std::size_t bytes, IoErrorHandler &handler) {
221352d347aSAlexis Perry if (bytes == 0) {
222352d347aSAlexis Perry return 0;
223352d347aSAlexis Perry }
224352d347aSAlexis Perry CheckOpen(handler);
225352d347aSAlexis Perry if (!Seek(at, handler)) {
226352d347aSAlexis Perry return 0;
227352d347aSAlexis Perry }
228352d347aSAlexis Perry std::size_t put{0};
229352d347aSAlexis Perry while (put < bytes) {
230352d347aSAlexis Perry auto chunk{::write(fd_, buffer + put, bytes - put)};
231352d347aSAlexis Perry if (chunk >= 0) {
23280cdf0dbSPeter Klausler SetPosition(position_ + chunk);
233352d347aSAlexis Perry put += chunk;
234352d347aSAlexis Perry } else {
235352d347aSAlexis Perry auto err{errno};
236352d347aSAlexis Perry if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) {
237352d347aSAlexis Perry handler.SignalError(err);
238352d347aSAlexis Perry break;
239352d347aSAlexis Perry }
240352d347aSAlexis Perry }
241352d347aSAlexis Perry }
242352d347aSAlexis Perry if (knownSize_ && position_ > *knownSize_) {
243352d347aSAlexis Perry knownSize_ = position_;
244352d347aSAlexis Perry }
245352d347aSAlexis Perry return put;
246352d347aSAlexis Perry }
247352d347aSAlexis Perry
openfile_ftruncate(int fd,OpenFile::FileOffset at)248ddb68d24SIsuru Fernando inline static int openfile_ftruncate(int fd, OpenFile::FileOffset at) {
249ddb68d24SIsuru Fernando #ifdef _WIN32
250cbad5761SMichael Kruse return ::_chsize(fd, at);
251ddb68d24SIsuru Fernando #else
252ddb68d24SIsuru Fernando return ::ftruncate(fd, at);
253ddb68d24SIsuru Fernando #endif
254ddb68d24SIsuru Fernando }
255ddb68d24SIsuru Fernando
Truncate(FileOffset at,IoErrorHandler & handler)256f7be2518Speter klausler void OpenFile::Truncate(FileOffset at, IoErrorHandler &handler) {
257352d347aSAlexis Perry CheckOpen(handler);
258352d347aSAlexis Perry if (!knownSize_ || *knownSize_ != at) {
259ddb68d24SIsuru Fernando if (openfile_ftruncate(fd_, at) != 0) {
260352d347aSAlexis Perry handler.SignalErrno();
261352d347aSAlexis Perry }
262352d347aSAlexis Perry knownSize_ = at;
263352d347aSAlexis Perry }
264352d347aSAlexis Perry }
265352d347aSAlexis Perry
266352d347aSAlexis Perry // The operation is performed immediately; the results are saved
267352d347aSAlexis Perry // to be claimed by a later WAIT statement.
268352d347aSAlexis Perry // TODO: True asynchronicity
ReadAsynchronously(FileOffset at,char * buffer,std::size_t bytes,IoErrorHandler & handler)269352d347aSAlexis Perry int OpenFile::ReadAsynchronously(
270f7be2518Speter klausler FileOffset at, char *buffer, std::size_t bytes, IoErrorHandler &handler) {
271352d347aSAlexis Perry CheckOpen(handler);
272352d347aSAlexis Perry int iostat{0};
273352d347aSAlexis Perry for (std::size_t got{0}; got < bytes;) {
274352d347aSAlexis Perry #if _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L
275352d347aSAlexis Perry auto chunk{::pread(fd_, buffer + got, bytes - got, at)};
276352d347aSAlexis Perry #else
277f7be2518Speter klausler auto chunk{Seek(at, handler) ? ::read(fd_, buffer + got, bytes - got) : -1};
278352d347aSAlexis Perry #endif
279352d347aSAlexis Perry if (chunk == 0) {
280352d347aSAlexis Perry iostat = FORTRAN_RUNTIME_IOSTAT_END;
281352d347aSAlexis Perry break;
282352d347aSAlexis Perry }
283352d347aSAlexis Perry if (chunk < 0) {
284352d347aSAlexis Perry auto err{errno};
285352d347aSAlexis Perry if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) {
286352d347aSAlexis Perry iostat = err;
287352d347aSAlexis Perry break;
288352d347aSAlexis Perry }
289352d347aSAlexis Perry } else {
290352d347aSAlexis Perry at += chunk;
291352d347aSAlexis Perry got += chunk;
292352d347aSAlexis Perry }
293352d347aSAlexis Perry }
294352d347aSAlexis Perry return PendingResult(handler, iostat);
295352d347aSAlexis Perry }
296352d347aSAlexis Perry
297352d347aSAlexis Perry // TODO: True asynchronicity
WriteAsynchronously(FileOffset at,const char * buffer,std::size_t bytes,IoErrorHandler & handler)298f7be2518Speter klausler int OpenFile::WriteAsynchronously(FileOffset at, const char *buffer,
299f7be2518Speter klausler std::size_t bytes, IoErrorHandler &handler) {
300352d347aSAlexis Perry CheckOpen(handler);
301352d347aSAlexis Perry int iostat{0};
302352d347aSAlexis Perry for (std::size_t put{0}; put < bytes;) {
303352d347aSAlexis Perry #if _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200809L
304352d347aSAlexis Perry auto chunk{::pwrite(fd_, buffer + put, bytes - put, at)};
305352d347aSAlexis Perry #else
306f7be2518Speter klausler auto chunk{
307f7be2518Speter klausler Seek(at, handler) ? ::write(fd_, buffer + put, bytes - put) : -1};
308352d347aSAlexis Perry #endif
309352d347aSAlexis Perry if (chunk >= 0) {
310352d347aSAlexis Perry at += chunk;
311352d347aSAlexis Perry put += chunk;
312352d347aSAlexis Perry } else {
313352d347aSAlexis Perry auto err{errno};
314352d347aSAlexis Perry if (err != EAGAIN && err != EWOULDBLOCK && err != EINTR) {
315352d347aSAlexis Perry iostat = err;
316352d347aSAlexis Perry break;
317352d347aSAlexis Perry }
318352d347aSAlexis Perry }
319352d347aSAlexis Perry }
320352d347aSAlexis Perry return PendingResult(handler, iostat);
321352d347aSAlexis Perry }
322352d347aSAlexis Perry
Wait(int id,IoErrorHandler & handler)323352d347aSAlexis Perry void OpenFile::Wait(int id, IoErrorHandler &handler) {
324352d347aSAlexis Perry std::optional<int> ioStat;
325352d347aSAlexis Perry Pending *prev{nullptr};
326352d347aSAlexis Perry for (Pending *p{pending_.get()}; p; p = (prev = p)->next.get()) {
327352d347aSAlexis Perry if (p->id == id) {
328352d347aSAlexis Perry ioStat = p->ioStat;
329352d347aSAlexis Perry if (prev) {
330352d347aSAlexis Perry prev->next.reset(p->next.release());
331352d347aSAlexis Perry } else {
332352d347aSAlexis Perry pending_.reset(p->next.release());
333352d347aSAlexis Perry }
334352d347aSAlexis Perry break;
335352d347aSAlexis Perry }
336352d347aSAlexis Perry }
337352d347aSAlexis Perry if (ioStat) {
338352d347aSAlexis Perry handler.SignalError(*ioStat);
339352d347aSAlexis Perry }
340352d347aSAlexis Perry }
341352d347aSAlexis Perry
WaitAll(IoErrorHandler & handler)342352d347aSAlexis Perry void OpenFile::WaitAll(IoErrorHandler &handler) {
343352d347aSAlexis Perry while (true) {
344352d347aSAlexis Perry int ioStat;
345352d347aSAlexis Perry if (pending_) {
346352d347aSAlexis Perry ioStat = pending_->ioStat;
347352d347aSAlexis Perry pending_.reset(pending_->next.release());
348352d347aSAlexis Perry } else {
349352d347aSAlexis Perry return;
350352d347aSAlexis Perry }
351352d347aSAlexis Perry handler.SignalError(ioStat);
352352d347aSAlexis Perry }
353352d347aSAlexis Perry }
354352d347aSAlexis Perry
InquirePosition() const35580cdf0dbSPeter Klausler Position OpenFile::InquirePosition() const {
35680cdf0dbSPeter Klausler if (openPosition_) { // from OPEN statement
35780cdf0dbSPeter Klausler return *openPosition_;
35880cdf0dbSPeter Klausler } else { // unit has been repositioned since opening
35980cdf0dbSPeter Klausler if (position_ == knownSize_.value_or(position_ + 1)) {
36080cdf0dbSPeter Klausler return Position::Append;
36180cdf0dbSPeter Klausler } else if (position_ == 0 && mayPosition_) {
36280cdf0dbSPeter Klausler return Position::Rewind;
36380cdf0dbSPeter Klausler } else {
36480cdf0dbSPeter Klausler return Position::AsIs; // processor-dependent & no common behavior
36580cdf0dbSPeter Klausler }
36680cdf0dbSPeter Klausler }
36780cdf0dbSPeter Klausler }
36880cdf0dbSPeter Klausler
CheckOpen(const Terminator & terminator)36995696d56Speter klausler void OpenFile::CheckOpen(const Terminator &terminator) {
370352d347aSAlexis Perry RUNTIME_CHECK(terminator, fd_ >= 0);
371352d347aSAlexis Perry }
372352d347aSAlexis Perry
Seek(FileOffset at,IoErrorHandler & handler)373f7be2518Speter klausler bool OpenFile::Seek(FileOffset at, IoErrorHandler &handler) {
374352d347aSAlexis Perry if (at == position_) {
375352d347aSAlexis Perry return true;
376352d347aSAlexis Perry } else if (RawSeek(at)) {
37780cdf0dbSPeter Klausler SetPosition(at);
378352d347aSAlexis Perry return true;
379352d347aSAlexis Perry } else {
380*03c066abSPeter Klausler handler.SignalError(IostatCannotReposition);
381352d347aSAlexis Perry return false;
382352d347aSAlexis Perry }
383352d347aSAlexis Perry }
384352d347aSAlexis Perry
RawSeek(FileOffset at)385f7be2518Speter klausler bool OpenFile::RawSeek(FileOffset at) {
386352d347aSAlexis Perry #ifdef _LARGEFILE64_SOURCE
38795696d56Speter klausler return ::lseek64(fd_, at, SEEK_SET) == at;
388352d347aSAlexis Perry #else
38995696d56Speter klausler return ::lseek(fd_, at, SEEK_SET) == at;
390352d347aSAlexis Perry #endif
391352d347aSAlexis Perry }
392352d347aSAlexis Perry
RawSeekToEnd()39395696d56Speter klausler bool OpenFile::RawSeekToEnd() {
39495696d56Speter klausler #ifdef _LARGEFILE64_SOURCE
39595696d56Speter klausler std::int64_t at{::lseek64(fd_, 0, SEEK_END)};
39695696d56Speter klausler #else
39795696d56Speter klausler std::int64_t at{::lseek(fd_, 0, SEEK_END)};
39895696d56Speter klausler #endif
39995696d56Speter klausler if (at >= 0) {
40095696d56Speter klausler knownSize_ = at;
40195696d56Speter klausler return true;
40295696d56Speter klausler } else {
40395696d56Speter klausler return false;
40495696d56Speter klausler }
40595696d56Speter klausler }
40695696d56Speter klausler
PendingResult(const Terminator & terminator,int iostat)40795696d56Speter klausler int OpenFile::PendingResult(const Terminator &terminator, int iostat) {
408352d347aSAlexis Perry int id{nextId_++};
40998d576c7Speter klausler pending_ = New<Pending>{terminator}(id, iostat, std::move(pending_));
410352d347aSAlexis Perry return id;
411352d347aSAlexis Perry }
4127926969aSpeter klausler
CloseFd(IoErrorHandler & handler)4133f6dbf1aSPeter Klausler void OpenFile::CloseFd(IoErrorHandler &handler) {
4143f6dbf1aSPeter Klausler if (fd_ >= 0) {
4153f6dbf1aSPeter Klausler if (fd_ <= 2) {
4163f6dbf1aSPeter Klausler // don't actually close a standard file descriptor, we might need it
4173f6dbf1aSPeter Klausler } else {
4183f6dbf1aSPeter Klausler if (::close(fd_) != 0) {
4193f6dbf1aSPeter Klausler handler.SignalErrno();
4203f6dbf1aSPeter Klausler }
4213f6dbf1aSPeter Klausler }
4223f6dbf1aSPeter Klausler fd_ = -1;
4233f6dbf1aSPeter Klausler }
4243f6dbf1aSPeter Klausler }
4253f6dbf1aSPeter Klausler
IsATerminal(int fd)4267926969aSpeter klausler bool IsATerminal(int fd) { return ::isatty(fd); }
427d8334c43Speter klausler
428bcd05599SMichael Kruse #ifdef WIN32
429bcd05599SMichael Kruse // Access flags are normally defined in unistd.h, which unavailable under
430bcd05599SMichael Kruse // Windows. Instead, define the flags as documented at
431bcd05599SMichael Kruse // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/access-waccess
432bcd05599SMichael Kruse #define F_OK 00
433bcd05599SMichael Kruse #define W_OK 02
434bcd05599SMichael Kruse #define R_OK 04
435bcd05599SMichael Kruse #endif
436bcd05599SMichael Kruse
IsExtant(const char * path)437d8334c43Speter klausler bool IsExtant(const char *path) { return ::access(path, F_OK) == 0; }
MayRead(const char * path)438d8334c43Speter klausler bool MayRead(const char *path) { return ::access(path, R_OK) == 0; }
MayWrite(const char * path)439d8334c43Speter klausler bool MayWrite(const char *path) { return ::access(path, W_OK) == 0; }
MayReadAndWrite(const char * path)440d8334c43Speter klausler bool MayReadAndWrite(const char *path) {
441d8334c43Speter klausler return ::access(path, R_OK | W_OK) == 0;
442d8334c43Speter klausler }
4439878facfSPeter Klausler
SizeInBytes(const char * path)4449878facfSPeter Klausler std::int64_t SizeInBytes(const char *path) {
4459878facfSPeter Klausler #ifndef _WIN32
4469878facfSPeter Klausler struct stat buf;
4479878facfSPeter Klausler if (::stat(path, &buf) == 0) {
4489878facfSPeter Klausler return buf.st_size;
4499878facfSPeter Klausler }
4509878facfSPeter Klausler #else // TODO: _WIN32
4519878facfSPeter Klausler #endif
4529878facfSPeter Klausler // No Fortran compiler signals an error
4539878facfSPeter Klausler return -1;
4549878facfSPeter Klausler }
4559878facfSPeter Klausler
4561f879005STim Keith } // namespace Fortran::runtime::io
457