diff --git a/flang/runtime/connection.h b/flang/runtime/connection.h --- a/flang/runtime/connection.h +++ b/flang/runtime/connection.h @@ -25,7 +25,8 @@ inline bool IsRecordFile(Access a) { return a != Access::Stream; } // These characteristics of a connection are immutable after being -// established in an OPEN statement. +// established in an OPEN statement, except for recordLength, +// which is immutable only when isFixedRecordLength is true. struct ConnectionAttributes { Access access{Access::Sequential}; // ACCESS='SEQUENTIAL', 'DIRECT', 'STREAM' std::optional isUnformatted; // FORM='UNFORMATTED' if true diff --git a/flang/runtime/file.h b/flang/runtime/file.h --- a/flang/runtime/file.h +++ b/flang/runtime/file.h @@ -39,7 +39,7 @@ bool isTerminal() const { return isTerminal_; } std::optional knownSize() const { return knownSize_; } - bool IsOpen() const { return fd_ >= 0; } + bool IsConnected() const { return fd_ >= 0; } void Open(OpenStatus, std::optional, Position, IoErrorHandler &); void Predefine(int fd); void Close(CloseStatus, IoErrorHandler &); @@ -66,6 +66,9 @@ void Wait(int id, IoErrorHandler &); void WaitAll(IoErrorHandler &); + // INQUIRE(POSITION=) + Position InquirePosition() const; + private: struct Pending { int id; @@ -78,6 +81,10 @@ bool RawSeek(FileOffset); bool RawSeekToEnd(); int PendingResult(const Terminator &, int); + void SetPosition(FileOffset pos) { + position_ = pos; + openPosition_.reset(); + } int fd_{-1}; OwningPtr path_; @@ -86,6 +93,7 @@ bool mayWrite_{false}; bool mayPosition_{false}; bool mayAsynchronous_{false}; + std::optional openPosition_; // from Open(); reset after positioning FileOffset position_{0}; std::optional knownSize_; bool isTerminal_{false}; diff --git a/flang/runtime/file.cpp b/flang/runtime/file.cpp --- a/flang/runtime/file.cpp +++ b/flang/runtime/file.cpp @@ -149,6 +149,7 @@ knownSize_ = 0; mayPosition_ = true; } + openPosition_ = position; // for INQUIRE(POSITION=) } void OpenFile::Predefine(int fd) { @@ -208,7 +209,7 @@ break; } } else { - position_ += chunk; + SetPosition(position_ + chunk); got += chunk; } } @@ -228,7 +229,7 @@ while (put < bytes) { auto chunk{::write(fd_, buffer + put, bytes - put)}; if (chunk >= 0) { - position_ += chunk; + SetPosition(position_ + chunk); put += chunk; } else { auto err{errno}; @@ -351,6 +352,20 @@ } } +Position OpenFile::InquirePosition() const { + if (openPosition_) { // from OPEN statement + return *openPosition_; + } else { // unit has been repositioned since opening + if (position_ == knownSize_.value_or(position_ + 1)) { + return Position::Append; + } else if (position_ == 0 && mayPosition_) { + return Position::Rewind; + } else { + return Position::AsIs; // processor-dependent & no common behavior + } + } +} + void OpenFile::CheckOpen(const Terminator &terminator) { RUNTIME_CHECK(terminator, fd_ >= 0); } @@ -359,7 +374,7 @@ if (at == position_) { return true; } else if (RawSeek(at)) { - position_ = at; + SetPosition(at); return true; } else { handler.SignalErrno(); diff --git a/flang/runtime/io-stmt.cpp b/flang/runtime/io-stmt.cpp --- a/flang/runtime/io-stmt.cpp +++ b/flang/runtime/io-stmt.cpp @@ -824,26 +824,35 @@ const char *str{nullptr}; switch (inquiry) { case HashInquiryKeyword("ACCESS"): - switch (unit().access) { - case Access::Sequential: - str = "SEQUENTIAL"; - break; - case Access::Direct: - str = "DIRECT"; - break; - case Access::Stream: - str = "STREAM"; - break; + if (!unit().IsConnected()) { + str = "UNDEFINED"; + } else { + switch (unit().access) { + case Access::Sequential: + str = "SEQUENTIAL"; + break; + case Access::Direct: + str = "DIRECT"; + break; + case Access::Stream: + str = "STREAM"; + break; + } } break; case HashInquiryKeyword("ACTION"): - str = unit().mayWrite() ? unit().mayRead() ? "READWRITE" : "WRITE" : "READ"; + str = !unit().IsConnected() ? "UNDEFINED" + : unit().mayWrite() ? unit().mayRead() ? "READWRITE" : "WRITE" + : "READ"; break; case HashInquiryKeyword("ASYNCHRONOUS"): - str = unit().mayAsynchronous() ? "YES" : "NO"; + str = !unit().IsConnected() ? "UNDEFINED" + : unit().mayAsynchronous() ? "YES" + : "NO"; break; case HashInquiryKeyword("BLANK"): - str = unit().isUnformatted.value_or(true) ? "UNDEFINED" + str = !unit().IsConnected() || unit().isUnformatted.value_or(true) + ? "UNDEFINED" : unit().modes.editingFlags & blankZero ? "ZERO" : "NULL"; break; @@ -854,12 +863,13 @@ str = unit().swapEndianness() ? "SWAP" : "NATIVE"; break; case HashInquiryKeyword("DECIMAL"): - str = unit().isUnformatted.value_or(true) ? "UNDEFINED" + str = !unit().IsConnected() || unit().isUnformatted.value_or(true) + ? "UNDEFINED" : unit().modes.editingFlags & decimalComma ? "COMMA" : "POINT"; break; case HashInquiryKeyword("DELIM"): - if (unit().isUnformatted.value_or(true)) { + if (!unit().IsConnected() || unit().isUnformatted.value_or(true)) { str = "UNDEFINED"; } else { switch (unit().modes.delim) { @@ -876,23 +886,26 @@ } break; case HashInquiryKeyword("DIRECT"): - str = unit().access == Access::Direct || + str = !unit().IsConnected() ? "UNKNOWN" + : unit().access == Access::Direct || (unit().mayPosition() && unit().isFixedRecordLength) ? "YES" : "NO"; break; case HashInquiryKeyword("ENCODING"): - str = unit().isUnformatted.value_or(true) ? "UNDEFINED" + str = !unit().IsConnected() ? "UNKNOWN" + : unit().isUnformatted.value_or(true) ? "UNDEFINED" : unit().isUTF8 ? "UTF-8" : "ASCII"; break; case HashInquiryKeyword("FORM"): - str = !unit().isUnformatted ? "UNDEFINED" - : *unit().isUnformatted ? "UNFORMATTED" - : "FORMATTED"; + str = !unit().IsConnected() || !unit().isUnformatted ? "UNDEFINED" + : *unit().isUnformatted ? "UNFORMATTED" + : "FORMATTED"; break; case HashInquiryKeyword("FORMATTED"): - str = !unit().isUnformatted ? "UNKNOWN" + str = !unit().IsConnected() ? "UNDEFINED" + : !unit().isUnformatted ? "UNKNOWN" : *unit().isUnformatted ? "NO" : "YES"; break; @@ -903,33 +916,38 @@ } break; case HashInquiryKeyword("PAD"): - str = unit().isUnformatted.value_or(true) ? "UNDEFINED" - : unit().modes.pad ? "YES" - : "NO"; + str = !unit().IsConnected() || unit().isUnformatted.value_or(true) + ? "UNDEFINED" + : unit().modes.pad ? "YES" + : "NO"; break; case HashInquiryKeyword("POSITION"): - if (unit().access == Access::Direct) { + if (!unit().IsConnected() || unit().access == Access::Direct) { str = "UNDEFINED"; } else { - auto size{unit().knownSize()}; - auto pos{unit().position()}; - if (pos == size.value_or(pos + 1)) { - str = "APPEND"; - } else if (pos == 0 && unit().mayPosition()) { + switch (unit().InquirePosition()) { + case Position::Rewind: str = "REWIND"; - } else { - str = "ASIS"; // processor-dependent & no common behavior + break; + case Position::Append: + str = "APPEND"; + break; + case Position::AsIs: + str = "ASIS"; + break; } } break; case HashInquiryKeyword("READ"): - str = unit().mayRead() ? "YES" : "NO"; + str = !unit().IsConnected() ? "UNDEFINED" : unit().mayRead() ? "YES" : "NO"; break; case HashInquiryKeyword("READWRITE"): - str = unit().mayRead() && unit().mayWrite() ? "YES" : "NO"; + str = !unit().IsConnected() ? "UNDEFINED" + : unit().mayRead() && unit().mayWrite() ? "YES" + : "NO"; break; case HashInquiryKeyword("ROUND"): - if (unit().isUnformatted.value_or(true)) { + if (!unit().IsConnected() || unit().isUnformatted.value_or(true)) { str = "UNDEFINED"; } else { switch (unit().modes.round) { @@ -954,23 +972,28 @@ case HashInquiryKeyword("SEQUENTIAL"): // "NO" for Direct, since Sequential would not work if // the unit were reopened without RECL=. - str = unit().access == Access::Sequential ? "YES" : "NO"; + str = !unit().IsConnected() ? "UNKNOWN" + : unit().access == Access::Sequential ? "YES" + : "NO"; break; case HashInquiryKeyword("SIGN"): - str = unit().isUnformatted.value_or(true) ? "UNDEFINED" + str = !unit().IsConnected() || unit().isUnformatted.value_or(true) + ? "UNDEFINED" : unit().modes.editingFlags & signPlus ? "PLUS" : "SUPPRESS"; break; case HashInquiryKeyword("STREAM"): - str = unit().access == Access::Stream ? "YES" : "NO"; - break; - case HashInquiryKeyword("WRITE"): - str = unit().mayWrite() ? "YES" : "NO"; + str = !unit().IsConnected() ? "UNKNOWN" + : unit().access == Access::Stream ? "YES" + : "NO"; break; case HashInquiryKeyword("UNFORMATTED"): - str = !unit().isUnformatted ? "UNKNOWN" - : *unit().isUnformatted ? "YES" - : "NO"; + str = !unit().IsConnected() || !unit().isUnformatted ? "UNKNOWN" + : *unit().isUnformatted ? "YES" + : "NO"; + break; + case HashInquiryKeyword("WRITE"): + str = !unit().IsConnected() ? "UNKNOWN" : unit().mayWrite() ? "YES" : "NO"; break; } if (str) { @@ -991,7 +1014,7 @@ result = unit().path() != nullptr; return true; case HashInquiryKeyword("OPENED"): - result = true; + result = unit().IsConnected(); return true; case HashInquiryKeyword("PENDING"): result = false; // asynchronous I/O is not implemented @@ -1023,13 +1046,15 @@ } return true; case HashInquiryKeyword("NUMBER"): - result = unit().unitNumber(); + result = unit().IsConnected() ? unit().unitNumber() : -1; return true; case HashInquiryKeyword("POS"): result = unit().position(); return true; case HashInquiryKeyword("RECL"): - if (unit().access == Access::Stream) { + if (!unit().IsConnected()) { + result = -1; + } else if (unit().access == Access::Stream) { result = -2; } else if (unit().isFixedRecordLength && unit().recordLength) { result = *unit().recordLength; @@ -1038,10 +1063,11 @@ } return true; case HashInquiryKeyword("SIZE"): - if (auto size{unit().knownSize()}) { - result = *size; - } else { - result = -1; + result = -1; + if (unit().IsConnected()) { + if (auto size{unit().knownSize()}) { + result = *size; + } } return true; default: diff --git a/flang/runtime/unit.h b/flang/runtime/unit.h --- a/flang/runtime/unit.h +++ b/flang/runtime/unit.h @@ -35,6 +35,8 @@ public FileFrame { public: explicit ExternalFileUnit(int unitNumber) : unitNumber_{unitNumber} {} + ~ExternalFileUnit() {} + int unitNumber() const { return unitNumber_; } bool swapEndianness() const { return swapEndianness_; } bool createdForInternalChildIo() const { return createdForInternalChildIo_; } diff --git a/flang/runtime/unit.cpp b/flang/runtime/unit.cpp --- a/flang/runtime/unit.cpp +++ b/flang/runtime/unit.cpp @@ -107,7 +107,7 @@ swapEndianness_ = convert == Convert::Swap || (convert == Convert::LittleEndian && !isHostLittleEndian) || (convert == Convert::BigEndian && isHostLittleEndian); - if (IsOpen()) { + if (IsConnected()) { bool isSamePath{newPath.get() && path() && pathLength() == newPathLength && std::memcmp(path(), newPath.get(), newPathLength) == 0}; if (status && *status != OpenStatus::Old && isSamePath) {