diff --git a/flang/runtime/edit-input.cpp b/flang/runtime/edit-input.cpp --- a/flang/runtime/edit-input.cpp +++ b/flang/runtime/edit-input.cpp @@ -719,7 +719,7 @@ } // When the field is wider than the variable, we drop the leading // characters. When the variable is wider than the field, there can be - // trailing padding. + // trailing padding or an EOR condition. const char *input{nullptr}; std::size_t ready{0}; // Skip leading bytes. @@ -729,11 +729,18 @@ while (remaining > 0) { if (ready == 0) { ready = io.GetNextInputBytes(input); - if (ready == 0) { - if (io.CheckForEndOfRecord()) { - std::fill_n(x, length, ' '); // PAD='YES' + if (ready == 0 || (ready < remaining && edit.modes.nonAdvancing)) { + if (io.CheckForEndOfRecord(ready)) { + if (ready == 0) { + // PAD='YES' and no more data + std::fill_n(x, length, ' '); + return !io.GetIoErrorHandler().InError(); + } else { + // Do partial read(s) then pad on last iteration + } + } else { + return !io.GetIoErrorHandler().InError(); } - return !io.GetIoErrorHandler().InError(); } } std::size_t chunk; 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 @@ -169,7 +169,7 @@ // Detect and signal any end-of-record condition after input. // Returns true if at EOR and remaining input should be padded with blanks. - bool CheckForEndOfRecord(); + bool CheckForEndOfRecord(std::size_t afterReading); // Skips spaces, advances records, and ignores NAMELIST comments std::optional GetNextNonBlank(std::size_t &byteCount) { 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 @@ -589,7 +589,7 @@ GotChar(byteCount); return next; } - if (CheckForEndOfRecord()) { // do padding + if (CheckForEndOfRecord(0)) { // do padding --*remaining; return std::optional{' '}; } @@ -597,11 +597,13 @@ return std::nullopt; } -bool IoStatementState::CheckForEndOfRecord() { +bool IoStatementState::CheckForEndOfRecord(std::size_t afterReading) { const ConnectionState &connection{GetConnectionState()}; if (!connection.IsAtEOF()) { if (auto length{connection.EffectiveRecordLength()}) { - if (connection.positionInRecord >= *length) { + if (connection.positionInRecord + + static_cast(afterReading) >= + *length) { IoErrorHandler &handler{GetIoErrorHandler()}; const auto &modes{mutableModes()}; if (modes.nonAdvancing) { diff --git a/flang/unittests/Runtime/ExternalIOTest.cpp b/flang/unittests/Runtime/ExternalIOTest.cpp --- a/flang/unittests/Runtime/ExternalIOTest.cpp +++ b/flang/unittests/Runtime/ExternalIOTest.cpp @@ -525,35 +525,64 @@ struct TestItems { std::string item; int expectedIoStat; - std::string expectedItemValue; + std::string expectedItemValue[2]; }; // Actual non advancing input IO test TestItems inputItems[]{ - {std::string(4, '+'), IostatOk, "ABCD"}, - {std::string(4, '+'), IostatOk, "EFGH"}, - {std::string(4, '+'), IostatEor, " "}, - {std::string(2, '+'), IostatOk, "IJ"}, - {std::string(8, '+'), IostatEor, "KLMNOP "}, - {std::string(10, '+'), IostatEor, "QRSTUVWX "}, + {std::string(4, '+'), IostatOk, "ABCD", "ABCD"}, + {std::string(4, '+'), IostatOk, "EFGH", "EFGH"}, + {std::string(4, '+'), IostatEor, "++++", " "}, + {std::string(2, '+'), IostatOk, "IJ", "IJ"}, + {std::string(8, '+'), IostatEor, "++++++++", "KLMNOP "}, + {std::string(10, '+'), IostatEor, "++++++++++", "QRSTUVWX "}, }; + // Test with PAD='NO' int j{0}; for (auto &inputItem : inputItems) { - // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', IOSTAT=iostat) inputItem + // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', PAD='NO', IOSTAT=iostat) inputItem io = IONAME(BeginExternalFormattedInput)( fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__); IONAME(EnableHandlers)(io, true, false, false, false, false); ASSERT_TRUE(IONAME(SetAdvance)(io, "NO", 2)) << "SetAdvance(NO)" << j; + ASSERT_TRUE(IONAME(SetPad)(io, "NO", 2)) << "SetPad(NO)" << j; bool result{ IONAME(InputAscii)(io, inputItem.item.data(), inputItem.item.length())}; ASSERT_EQ(result, inputItem.expectedIoStat == IostatOk) << "InputAscii() " << j; ASSERT_EQ(IONAME(EndIoStatement)(io), inputItem.expectedIoStat) << "EndIoStatement() for Read " << j; - ASSERT_EQ(inputItem.item, inputItem.expectedItemValue) + ASSERT_EQ(inputItem.item, inputItem.expectedItemValue[0]) + << "Input-item value after non advancing read " << j; + j++; + } + + // REWIND(UNIT=unit) + io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); + ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) + << "EndIoStatement() for Rewind"; + + // Test again with PAD='YES' + j = 0; + for (auto &inputItem : inputItems) { + // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', PAD='YES', IOSTAT=iostat) + // inputItem + io = IONAME(BeginExternalFormattedInput)( + fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__); + IONAME(EnableHandlers)(io, true, false, false, false, false); + ASSERT_TRUE(IONAME(SetAdvance)(io, "NO", 2)) << "SetAdvance(NO)" << j; + ASSERT_TRUE(IONAME(SetPad)(io, "YES", 3)) << "SetPad(YES)" << j; + bool result{ + IONAME(InputAscii)(io, inputItem.item.data(), inputItem.item.length())}; + ASSERT_EQ(result, inputItem.expectedIoStat == IostatOk) + << "InputAscii() " << j; + ASSERT_EQ(IONAME(EndIoStatement)(io), inputItem.expectedIoStat) + << "EndIoStatement() for Read " << j; + ASSERT_EQ(inputItem.item, inputItem.expectedItemValue[1]) << "Input-item value after non advancing read " << j; j++; } + // CLOSE(UNIT=unit) io = IONAME(BeginClose)(unit, __FILE__, __LINE__); ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)