#include "tools.h"
#include "unit.h"
#include <algorithm>
+#include <cstdio>
#include <cstring>
#include <limits>
}
template <Direction DIR> int ExternalIoStatementState<DIR>::EndIoStatement() {
+ if (!unit().nonAdvancing) {
+ unit().AdvanceRecord(*this);
+ }
if constexpr (DIR == Direction::Output) {
- if (!unit().nonAdvancing) {
- unit().AdvanceRecord(*this);
- }
unit().FlushIfTerminal(*this);
}
return ExternalIoStatementBase::EndIoStatement();
}
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() {
}
}
-void IoStatementState::SkipSpaces(std::optional<int> &remaining) {
- if (!remaining || *remaining > 0) {
- for (auto ch{GetCurrentChar()}; ch && ch == ' '; ch = GetCurrentChar()) {
+std::optional<char32_t> IoStatementState::SkipSpaces(
+ std::optional<int> &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<char32_t> IoStatementState::NextInField(
case '\'':
case '"':
case '*':
+ case '\n': // for stream access
break;
default:
HandleRelativePosition(1);
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;
}
template <Direction DIR>
+bool UnformattedIoStatementState<DIR>::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 <Direction DIR>
int UnformattedIoStatementState<DIR>::EndIoStatement() {
- auto &ext{static_cast<ExternalIoStatementState<DIR> &>(*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<DIR>::EndIoStatement();
}
template class InternalIoStatementState<Direction::Output>;
template class ExternalListIoStatementState<Direction::Input>;
template class UnformattedIoStatementState<Direction::Output>;
template class UnformattedIoStatementState<Direction::Input>;
+
+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
class ExternalFormattedIoStatementState;
template <Direction> class ExternalListIoStatementState;
template <Direction> class UnformattedIoStatementState;
+class ExternalMiscIoStatementState;
// The Cookie type in the I/O API is a pointer (for C) to this class.
class IoStatementState {
bool EmitRepeated(char, std::size_t);
bool EmitField(const char *, std::size_t length, std::size_t width);
- void SkipSpaces(std::optional<int> &remaining);
+
+ std::optional<char32_t> SkipSpaces(std::optional<int> &remaining);
std::optional<char32_t> NextInField(std::optional<int> &remaining);
std::optional<char32_t> GetNextNonBlank(); // can advance record
std::reference_wrapper<ExternalListIoStatementState<Direction::Output>>,
std::reference_wrapper<ExternalListIoStatementState<Direction::Input>>,
std::reference_wrapper<UnformattedIoStatementState<Direction::Output>>,
- std::reference_wrapper<UnformattedIoStatementState<Direction::Input>>>
+ std::reference_wrapper<UnformattedIoStatementState<Direction::Input>>,
+ std::reference_wrapper<ExternalMiscIoStatementState>>
u_;
};
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<char32_t> GetCurrentChar();
class UnformattedIoStatementState : public ExternalIoStatementState<DIR> {
public:
using ExternalIoStatementState<DIR>::ExternalIoStatementState;
+ bool Receive(char *, std::size_t);
int EndIoStatement();
};
extern template class FormatControl<
ExternalFormattedIoStatementState<Direction::Input>>;
+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_