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