If list-directed input contains a repeated item ("20*123.0") that is not
fully consumed by the READ statement's data item list, the end of that
READ statement was repositioning the input to the repeated value
("123.0"), leading to later confusion. Cancel the input item repetition
during EndIoStatement() processing to prevent this misbehavior.
Fixes llvm-test-suite/Fortran/gfortran/regression/list_read_4.f90.
124 lines
4.7 KiB
C++
124 lines
4.7 KiB
C++
//===-- runtime/connection.h ------------------------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Fortran I/O connection state (abstracted over internal & external units)
|
|
|
|
#ifndef FORTRAN_RUNTIME_IO_CONNECTION_H_
|
|
#define FORTRAN_RUNTIME_IO_CONNECTION_H_
|
|
|
|
#include "format.h"
|
|
#include <cinttypes>
|
|
#include <optional>
|
|
|
|
namespace Fortran::runtime::io {
|
|
|
|
class IoStatementState;
|
|
|
|
enum class Direction { Output, Input };
|
|
enum class Access { Sequential, Direct, Stream };
|
|
|
|
// These characteristics of a connection are immutable after being
|
|
// established in an OPEN statement.
|
|
struct ConnectionAttributes {
|
|
Access access{Access::Sequential}; // ACCESS='SEQUENTIAL', 'DIRECT', 'STREAM'
|
|
std::optional<bool> isUnformatted; // FORM='UNFORMATTED' if true
|
|
bool isUTF8{false}; // ENCODING='UTF-8'
|
|
unsigned char internalIoCharKind{0}; // 0->external, 1/2/4->internal
|
|
std::optional<std::int64_t> openRecl; // RECL= on OPEN
|
|
|
|
bool IsRecordFile() const {
|
|
// Formatted stream files are viewed as having records, at least on input
|
|
return access != Access::Stream || !isUnformatted.value_or(true);
|
|
}
|
|
|
|
template <typename CHAR = char> constexpr bool useUTF8() const {
|
|
// For wide CHARACTER kinds, always use UTF-8 for formatted I/O.
|
|
// For single-byte CHARACTER, encode characters >= 0x80 with
|
|
// UTF-8 iff the mode is set.
|
|
return internalIoCharKind == 0 && (sizeof(CHAR) > 1 || isUTF8);
|
|
}
|
|
};
|
|
|
|
struct ConnectionState : public ConnectionAttributes {
|
|
bool IsAtEOF() const; // true when read has hit EOF or endfile record
|
|
bool IsAfterEndfile() const; // true after ENDFILE until repositioned
|
|
|
|
// All positions and measurements are always in units of bytes,
|
|
// not characters. Multi-byte character encodings are possible in
|
|
// both internal I/O (when the character kind of the variable is 2 or 4)
|
|
// and external formatted I/O (when the encoding is UTF-8).
|
|
std::size_t RemainingSpaceInRecord() const;
|
|
bool NeedAdvance(std::size_t) const;
|
|
void HandleAbsolutePosition(std::int64_t);
|
|
void HandleRelativePosition(std::int64_t);
|
|
|
|
void BeginRecord() {
|
|
positionInRecord = 0;
|
|
furthestPositionInRecord = 0;
|
|
unterminatedRecord = false;
|
|
}
|
|
|
|
std::optional<std::int64_t> EffectiveRecordLength() const {
|
|
// When an input record is longer than an explicit RECL= from OPEN
|
|
// it is effectively truncated on input.
|
|
return openRecl && recordLength && *openRecl < *recordLength ? openRecl
|
|
: recordLength;
|
|
}
|
|
|
|
std::optional<std::int64_t> recordLength;
|
|
|
|
std::int64_t currentRecordNumber{1}; // 1 is first
|
|
|
|
// positionInRecord is the 0-based bytes offset in the current record
|
|
// to/from which the next data transfer will occur. It can be past
|
|
// furthestPositionInRecord if moved by an X or T or TR control edit
|
|
// descriptor.
|
|
std::int64_t positionInRecord{0};
|
|
|
|
// furthestPositionInRecord is the 0-based byte offset of the greatest
|
|
// position in the current record to/from which any data transfer has
|
|
// occurred, plus one. It can be viewed as a count of bytes processed.
|
|
std::int64_t furthestPositionInRecord{0}; // max(position+bytes)
|
|
|
|
// Set at end of non-advancing I/O data transfer
|
|
std::optional<std::int64_t> leftTabLimit; // offset in current record
|
|
|
|
// currentRecordNumber value captured after ENDFILE/REWIND/BACKSPACE statement
|
|
// or an end-of-file READ condition on a sequential access file
|
|
std::optional<std::int64_t> endfileRecordNumber;
|
|
|
|
// Mutable modes set at OPEN() that can be overridden in READ/WRITE & FORMAT
|
|
MutableModes modes; // BLANK=, DECIMAL=, SIGN=, ROUND=, PAD=, DELIM=, kP
|
|
|
|
// 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.
|
|
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.
|
|
class SavedPosition {
|
|
public:
|
|
explicit SavedPosition(IoStatementState &);
|
|
~SavedPosition();
|
|
void Cancel() { cancelled_ = true; }
|
|
|
|
private:
|
|
IoStatementState &io_;
|
|
ConnectionState saved_;
|
|
bool cancelled_{false};
|
|
};
|
|
|
|
} // namespace Fortran::runtime::io
|
|
#endif // FORTRAN_RUNTIME_IO_CONNECTION_H_
|