diff --git a/flang/runtime/io-stmt.h b/flang/runtime/io-stmt.h --- a/flang/runtime/io-stmt.h +++ b/flang/runtime/io-stmt.h @@ -36,6 +36,7 @@ class ExternalFormattedIoStatementState; template class ExternalListIoStatementState; template class UnformattedIoStatementState; +class ExternalMiscIoStatementState; // The Cookie type in the I/O API is a pointer (for C) to this class. class IoStatementState { @@ -73,7 +74,8 @@ bool EmitRepeated(char, std::size_t); bool EmitField(const char *, std::size_t length, std::size_t width); - void SkipSpaces(std::optional &remaining); + + std::optional SkipSpaces(std::optional &remaining); std::optional NextInField(std::optional &remaining); std::optional GetNextNonBlank(); // can advance record @@ -94,7 +96,8 @@ std::reference_wrapper>, std::reference_wrapper>, std::reference_wrapper>, - std::reference_wrapper>> + std::reference_wrapper>, + std::reference_wrapper> u_; }; @@ -235,7 +238,7 @@ public: using ExternalIoStatementBase::ExternalIoStatementBase; int EndIoStatement(); - bool Emit(const char *, std::size_t chars /* not bytes */); + bool Emit(const char *, std::size_t); bool Emit(const char16_t *, std::size_t chars /* not bytes */); bool Emit(const char32_t *, std::size_t chars /* not bytes */); std::optional GetCurrentChar(); @@ -280,6 +283,7 @@ class UnformattedIoStatementState : public ExternalIoStatementState { public: using ExternalIoStatementState::ExternalIoStatementState; + bool Receive(char *, std::size_t); int EndIoStatement(); }; @@ -353,5 +357,17 @@ extern template class FormatControl< ExternalFormattedIoStatementState>; +class ExternalMiscIoStatementState : public ExternalIoStatementBase { +public: + enum Which { Flush, Backspace, Endfile, Rewind }; + ExternalMiscIoStatementState(ExternalFileUnit &unit, Which which, + const char *sourceFile = nullptr, int sourceLine = 0) + : ExternalIoStatementBase{unit, sourceFile, sourceLine}, which_{which} {} + int EndIoStatement(); + +private: + Which which_; +}; + } // namespace Fortran::runtime::io #endif // FORTRAN_RUNTIME_IO_STMT_H_ 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 @@ -13,6 +13,7 @@ #include "tools.h" #include "unit.h" #include +#include #include #include @@ -182,10 +183,10 @@ } template int ExternalIoStatementState::EndIoStatement() { + if (!unit().nonAdvancing) { + unit().AdvanceRecord(*this); + } if constexpr (DIR == Direction::Output) { - if (!unit().nonAdvancing) { - unit().AdvanceRecord(*this); - } unit().FlushIfTerminal(*this); } return ExternalIoStatementBase::EndIoStatement(); @@ -291,7 +292,7 @@ } void IoStatementState::HandleRelativePosition(std::int64_t n) { - return std::visit([=](auto &x) { x.get().HandleRelativePosition(n); }, u_); + std::visit([=](auto &x) { x.get().HandleRelativePosition(n); }, u_); } int IoStatementState::EndIoStatement() { @@ -347,15 +348,22 @@ } } -void IoStatementState::SkipSpaces(std::optional &remaining) { - if (!remaining || *remaining > 0) { - for (auto ch{GetCurrentChar()}; ch && ch == ' '; ch = GetCurrentChar()) { +std::optional IoStatementState::SkipSpaces( + std::optional &remaining) { + while (!remaining || *remaining > 0) { + if (auto ch{GetCurrentChar()}) { + if (*ch != ' ') { + return ch; + } HandleRelativePosition(1); - if (remaining && !--*remaining) { - break; + if (remaining) { + --*remaining; } + } else { + break; } } + return std::nullopt; } std::optional IoStatementState::NextInField( @@ -372,6 +380,7 @@ case '\'': case '"': case '*': + case '\n': // for stream access break; default: HandleRelativePosition(1); @@ -385,7 +394,8 @@ return next; } const ConnectionState &connection{GetConnectionState()}; - if (!connection.IsAtEOF() && connection.recordLength && + if (!connection.IsAtEOF() && connection.isFixedRecordLength && + connection.recordLength && connection.positionInRecord >= *connection.recordLength) { if (connection.modes.pad) { // PAD='YES' --*remaining; @@ -554,28 +564,38 @@ } template +bool UnformattedIoStatementState::Receive(char *data, std::size_t bytes) { + if constexpr (DIR == Direction::Output) { + this->Crash( + "UnformattedIoStatementState::Receive() called for output statement"); + } + return this->unit().Receive(data, bytes, *this); +} + +template int UnformattedIoStatementState::EndIoStatement() { - auto &ext{static_cast &>(*this)}; - ExternalFileUnit &unit{ext.unit()}; - if (unit.access == Access::Sequential && !unit.recordLength.has_value()) { - // Overwrite the first four bytes of the record with its length, - // and also append the length. These four bytes were skipped over - // in BeginUnformattedOutput(). - // TODO: Break very large records up into subrecords with negative - // headers &/or footers - union { - std::uint32_t u; - char c[sizeof u]; - } u; - u.u = unit.furthestPositionInRecord - sizeof u.c; - // TODO: Convert record length to little-endian on big-endian host? - if (!(ext.Emit(u.c, sizeof u.c) && - (ext.HandleAbsolutePosition(0), ext.Emit(u.c, sizeof u.c)) && - ext.AdvanceRecord())) { - return false; + ExternalFileUnit &unit{this->unit()}; + if constexpr (DIR == Direction::Output) { + if (unit.access == Access::Sequential && !unit.isFixedRecordLength) { + // Append the length of a sequential unformatted variable-length record + // as its footer, then overwrite the reserved first four bytes of the + // record with its length as its header. These four bytes were skipped + // over in BeginUnformattedOutput(). + // TODO: Break very large records up into subrecords with negative + // headers &/or footers + union { + std::uint32_t u; + char c[sizeof u]; + } u; + u.u = unit.furthestPositionInRecord - sizeof u; + // TODO: Convert record length to little-endian on big-endian host? + if (!(this->Emit(u.c, sizeof u) && + (this->HandleAbsolutePosition(0), this->Emit(u.c, sizeof u)))) { + return false; + } } } - return ext.EndIoStatement(); + return ExternalIoStatementState::EndIoStatement(); } template class InternalIoStatementState; @@ -592,4 +612,25 @@ template class ExternalListIoStatementState; template class UnformattedIoStatementState; template class UnformattedIoStatementState; + +int ExternalMiscIoStatementState::EndIoStatement() { + ExternalFileUnit &ext{unit()}; + switch (which_) { + case Flush: + ext.Flush(*this); + std::fflush(nullptr); // flushes C stdio output streams (12.9(2)) + break; + case Backspace: + ext.BackspaceRecord(*this); + break; + case Endfile: + ext.Endfile(*this); + break; + case Rewind: + ext.Rewind(*this); + break; + } + return ExternalIoStatementBase::EndIoStatement(); +} + } // namespace Fortran::runtime::io diff --git a/flang/runtime/unit.h b/flang/runtime/unit.h --- a/flang/runtime/unit.h +++ b/flang/runtime/unit.h @@ -106,7 +106,8 @@ ExternalListIoStatementState, ExternalListIoStatementState, UnformattedIoStatementState, - UnformattedIoStatementState> + UnformattedIoStatementState, + ExternalMiscIoStatementState> u_; // Points to the active alternative (if any) in u_ for use as a Cookie