diff --git a/flang/runtime/connection.h b/flang/runtime/connection.h --- a/flang/runtime/connection.h +++ b/flang/runtime/connection.h @@ -57,6 +57,12 @@ // or an end-of-file READ condition on a sequential access file std::optional endfileRecordNumber; + // Set when processing repeated items during list-directed & NAMELIST input + // in order to keep a span of records in frame on a non-positionable file, + // so that backspacing to the beginning of the repeated item doesn't require + // repositioning the external storage medium when that's impossible. + std::optional resumptionRecordNumber; + // Mutable modes set at OPEN() that can be overridden in READ/WRITE & FORMAT MutableModes modes; // BLANK=, DECIMAL=, SIGN=, ROUND=, PAD=, DELIM=, kP }; 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 @@ -229,7 +229,6 @@ private: int remaining_{0}; // for "r*" repetition - std::int64_t repeatRecordNumber_; std::int64_t repeatPositionInRecord_; bool eatComma_{false}; // consume comma after previously read item bool hitSlash_{false}; // once '/' is seen, nullify further items 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 @@ -653,7 +653,11 @@ comma = ';'; } if (remaining_ > 0 && !realPart_) { // "r*c" repetition in progress - while (connection.currentRecordNumber > repeatRecordNumber_) { + RUNTIME_CHECK( + io.GetIoErrorHandler(), connection.resumptionRecordNumber.has_value()); + while (connection.currentRecordNumber > + connection.resumptionRecordNumber.value_or( + connection.currentRecordNumber)) { io.BackspaceRecord(); } connection.HandleAbsolutePosition(repeatPositionInRecord_); @@ -666,6 +670,9 @@ } } remaining_ -= edit.repeat; + if (remaining_ <= 0) { + connection.resumptionRecordNumber.reset(); + } return edit; } // Skip separators, handle a "r*c" repeat count; see 13.10.2 in Fortran 2018 @@ -726,7 +733,11 @@ } edit.repeat = std::min(r, maxRepeat); remaining_ = r - edit.repeat; - repeatRecordNumber_ = connection.currentRecordNumber; + if (remaining_ > 0) { + connection.resumptionRecordNumber = connection.currentRecordNumber; + } else { + connection.resumptionRecordNumber.reset(); + } repeatPositionInRecord_ = connection.positionInRecord; } else { // not a repetition count, just an integer value; rewind connection.positionInRecord = start; diff --git a/flang/runtime/unit.cpp b/flang/runtime/unit.cpp --- a/flang/runtime/unit.cpp +++ b/flang/runtime/unit.cpp @@ -350,13 +350,10 @@ if (*recordLength > 0 && record[*recordLength - 1] == '\r') { --*recordLength; } - } else { - recordLength = bytes; // final record w/o \n + return true; } - return true; - } else { - return false; } + return false; } void ExternalFileUnit::SetLeftTabLimit() { @@ -421,7 +418,10 @@ Frame()[recordOffsetInFrame_ + *recordLength] == '\n') { ++recordOffsetInFrame_; } - recordOffsetInFrame_ += *recordLength; + if (!resumptionRecordNumber || mayPosition()) { + frameOffsetInFile_ += recordOffsetInFrame_ + *recordLength; + recordOffsetInFrame_ = 0; + } recordLength.reset(); } } @@ -612,10 +612,17 @@ } std::size_t length{0}; do { - std::size_t need{recordOffsetInFrame_ + length + 1}; - length = ReadFrame(frameOffsetInFile_, need, handler); + std::size_t need{length + 1}; + length = + ReadFrame(frameOffsetInFile_, recordOffsetInFrame_ + need, handler) - + recordOffsetInFrame_; if (length < need) { - handler.SignalEnd(); + if (length > 0) { + // final record w/o \n + recordLength = length; + } else { + handler.SignalEnd(); + } break; } } while (!SetSequentialVariableFormattedRecordLength());