void BeginRecord() {
positionInRecord = 0;
furthestPositionInRecord = 0;
+ unterminatedRecord = false;
}
std::optional<std::int64_t> EffectiveRecordLength() const {
// so that backspacing to the beginning of the repeated item doesn't require
// repositioning the external storage medium when that's impossible.
bool pinnedFrame{false};
+
+ // Set when the last record of a file is not properly terminated
+ // so that a non-advancing READ will not signal EOR.
+ bool unterminatedRecord{false};
};
// Utility class for capturing and restoring a position in an input stream.
}
unit().leftTabLimit = unit().furthestPositionInRecord;
} else {
- unit().leftTabLimit.reset();
unit().AdvanceRecord(*this);
}
unit().FlushIfTerminal(*this);
if (connection.positionInRecord >= *length) {
IoErrorHandler &handler{GetIoErrorHandler()};
if (mutableModes().nonAdvancing) {
- handler.SignalEor();
+ if (connection.access == Access::Stream &&
+ connection.unterminatedRecord) {
+ // Reading final unterminated record left by a
+ // non-advancing WRITE on a stream file prior to
+ // positioning or ENDFILE.
+ handler.SignalEnd();
+ } else {
+ handler.SignalEor();
+ }
} else if (!connection.modes.pad) {
handler.SignalError(IostatRecordReadOverrun);
}
return true;
} else {
handler.SignalEnd();
- if (access == Access::Sequential) {
+ if (IsRecordFile() && access != Access::Direct) {
endfileRecordNumber = currentRecordNumber;
}
return false;
return Frame() + at;
}
handler.SignalEnd();
- if (access == Access::Sequential) {
+ if (IsRecordFile() && access != Access::Direct) {
endfileRecordNumber = currentRecordNumber;
}
}
#endif
ok = ok && Emit(lineEnding, lineEndingBytes, 1, handler);
}
+ leftTabLimit.reset();
if (IsAfterEndfile()) {
return false;
}
// ENDFILE after ENDFILE
} else {
DoEndfile(handler);
- if (access == Access::Sequential) {
+ if (IsRecordFile() && access != Access::Direct) {
// Explicit ENDFILE leaves position *after* the endfile record
RUNTIME_CHECK(handler, endfileRecordNumber.has_value());
currentRecordNumber = *endfileRecordNumber + 1;
if (length > 0) {
// final record w/o \n
recordLength = length;
+ unterminatedRecord = true;
} else {
handler.SignalEnd();
}
}
void ExternalFileUnit::DoEndfile(IoErrorHandler &handler) {
- if (IsRecordFile()) {
+ if (IsRecordFile() && access != Access::Direct) {
+ if (furthestPositionInRecord > 0) {
+ // Last write was non-advancing, so AdvanceRecord() was not called.
+ leftTabLimit.reset();
+ ++currentRecordNumber;
+ }
endfileRecordNumber = currentRecordNumber;
}
FlushOutput(handler);
- Truncate(frameOffsetInFile_ + recordOffsetInFrame_, handler);
+ Truncate(frameOffsetInFile_ + recordOffsetInFrame_ + furthestPositionInRecord,
+ handler);
BeginRecord();
impliedEndfile_ = false;
}