1651f58bfSDiana Picus //===-- runtime/unit.cpp --------------------------------------------------===//
2f7be2518Speter klausler //
3f7be2518Speter klausler // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4f7be2518Speter klausler // See https://llvm.org/LICENSE.txt for license information.
5f7be2518Speter klausler // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6f7be2518Speter klausler //
7f7be2518Speter klausler //===----------------------------------------------------------------------===//
8f7be2518Speter klausler
9f7be2518Speter klausler #include "unit.h"
103b635714Speter klausler #include "io-error.h"
11f7be2518Speter klausler #include "lock.h"
123b635714Speter klausler #include "unit-map.h"
13bd43fa29Speter klausler #include <cstdio>
140f140ce3SThorsten Schütt #include <limits>
158f2c5c43Speter klausler #include <utility>
16f7be2518Speter klausler
17f7be2518Speter klausler namespace Fortran::runtime::io {
18f7be2518Speter klausler
193b635714Speter klausler // The per-unit data structures are created on demand so that Fortran I/O
203b635714Speter klausler // should work without a Fortran main program.
213b635714Speter klausler static Lock unitMapLock;
223b635714Speter klausler static UnitMap *unitMap{nullptr};
23a62b6016SPeter Klausler static ExternalFileUnit *defaultInput{nullptr}; // unit 5
24a62b6016SPeter Klausler static ExternalFileUnit *defaultOutput{nullptr}; // unit 6
25a62b6016SPeter Klausler static ExternalFileUnit *errorOutput{nullptr}; // unit 0 extension
26f7be2518Speter klausler
FlushOutputOnCrash(const Terminator & terminator)2795696d56Speter klausler void FlushOutputOnCrash(const Terminator &terminator) {
28a62b6016SPeter Klausler if (!defaultOutput && !errorOutput) {
293b635714Speter klausler return;
303b635714Speter klausler }
3195696d56Speter klausler IoErrorHandler handler{terminator};
3295696d56Speter klausler handler.HasIoStat(); // prevent nested crash if flush has error
33a62b6016SPeter Klausler CriticalSection critical{unitMapLock};
34a62b6016SPeter Klausler if (defaultOutput) {
35cd0a1226Speter klausler defaultOutput->FlushOutput(handler);
3695696d56Speter klausler }
37a62b6016SPeter Klausler if (errorOutput) {
38a62b6016SPeter Klausler errorOutput->FlushOutput(handler);
39a62b6016SPeter Klausler }
4095696d56Speter klausler }
4195696d56Speter klausler
LookUp(int unit)4295696d56Speter klausler ExternalFileUnit *ExternalFileUnit::LookUp(int unit) {
433b635714Speter klausler return GetUnitMap().LookUp(unit);
44f7be2518Speter klausler }
45f7be2518Speter klausler
LookUpOrCreate(int unit,const Terminator & terminator,bool & wasExtant)46cfbde714SPeter Klausler ExternalFileUnit *ExternalFileUnit::LookUpOrCreate(
47bd43fa29Speter klausler int unit, const Terminator &terminator, bool &wasExtant) {
483b635714Speter klausler return GetUnitMap().LookUpOrCreate(unit, terminator, wasExtant);
49f7be2518Speter klausler }
50f7be2518Speter klausler
LookUpOrCreateAnonymous(int unit,Direction dir,std::optional<bool> isUnformatted,const Terminator & terminator)51cfbde714SPeter Klausler ExternalFileUnit *ExternalFileUnit::LookUpOrCreateAnonymous(int unit,
52199a623eSpeter klausler Direction dir, std::optional<bool> isUnformatted,
53199a623eSpeter klausler const Terminator &terminator) {
54bd43fa29Speter klausler bool exists{false};
55cfbde714SPeter Klausler ExternalFileUnit *result{
56bd43fa29Speter klausler GetUnitMap().LookUpOrCreate(unit, terminator, exists)};
57cfbde714SPeter Klausler if (result && !exists) {
58bd43fa29Speter klausler IoErrorHandler handler{terminator};
59cfbde714SPeter Klausler result->OpenAnonymousUnit(
60675ad1bcSpeter klausler dir == Direction::Input ? OpenStatus::Unknown : OpenStatus::Replace,
61675ad1bcSpeter klausler Action::ReadWrite, Position::Rewind, Convert::Native, handler);
62cfbde714SPeter Klausler result->isUnformatted = isUnformatted;
63bd43fa29Speter klausler }
64bd43fa29Speter klausler return result;
65bd43fa29Speter klausler }
66bd43fa29Speter klausler
LookUp(const char * path,std::size_t pathLen)6703c066abSPeter Klausler ExternalFileUnit *ExternalFileUnit::LookUp(
6803c066abSPeter Klausler const char *path, std::size_t pathLen) {
6903c066abSPeter Klausler return GetUnitMap().LookUp(path, pathLen);
70675ad1bcSpeter klausler }
71675ad1bcSpeter klausler
CreateNew(int unit,const Terminator & terminator)72bd43fa29Speter klausler ExternalFileUnit &ExternalFileUnit::CreateNew(
73bd43fa29Speter klausler int unit, const Terminator &terminator) {
74bd43fa29Speter klausler bool wasExtant{false};
75cfbde714SPeter Klausler ExternalFileUnit *result{
76bd43fa29Speter klausler GetUnitMap().LookUpOrCreate(unit, terminator, wasExtant)};
77cfbde714SPeter Klausler RUNTIME_CHECK(terminator, result && !wasExtant);
78cfbde714SPeter Klausler return *result;
79bd43fa29Speter klausler }
80bd43fa29Speter klausler
LookUpForClose(int unit)813b635714Speter klausler ExternalFileUnit *ExternalFileUnit::LookUpForClose(int unit) {
823b635714Speter klausler return GetUnitMap().LookUpForClose(unit);
833b635714Speter klausler }
843b635714Speter klausler
NewUnit(const Terminator & terminator,bool forChildIo)8543fadefbSpeter klausler ExternalFileUnit &ExternalFileUnit::NewUnit(
8643fadefbSpeter klausler const Terminator &terminator, bool forChildIo) {
8743fadefbSpeter klausler ExternalFileUnit &unit{GetUnitMap().NewUnit(terminator)};
8843fadefbSpeter klausler unit.createdForInternalChildIo_ = forChildIo;
8943fadefbSpeter klausler return unit;
9095696d56Speter klausler }
9195696d56Speter klausler
OpenUnit(std::optional<OpenStatus> status,std::optional<Action> action,Position position,OwningPtr<char> && newPath,std::size_t newPathLength,Convert convert,IoErrorHandler & handler)92f4ecd5a1Speter klausler void ExternalFileUnit::OpenUnit(std::optional<OpenStatus> status,
93f4ecd5a1Speter klausler std::optional<Action> action, Position position, OwningPtr<char> &&newPath,
94f4ecd5a1Speter klausler std::size_t newPathLength, Convert convert, IoErrorHandler &handler) {
958f2c5c43Speter klausler if (executionEnvironment.conversion != Convert::Unknown) {
968f2c5c43Speter klausler convert = executionEnvironment.conversion;
978f2c5c43Speter klausler }
988f2c5c43Speter klausler swapEndianness_ = convert == Convert::Swap ||
998f2c5c43Speter klausler (convert == Convert::LittleEndian && !isHostLittleEndian) ||
1008f2c5c43Speter klausler (convert == Convert::BigEndian && isHostLittleEndian);
10180cdf0dbSPeter Klausler if (IsConnected()) {
102f4ecd5a1Speter klausler bool isSamePath{newPath.get() && path() && pathLength() == newPathLength &&
103f4ecd5a1Speter klausler std::memcmp(path(), newPath.get(), newPathLength) == 0};
104f4ecd5a1Speter klausler if (status && *status != OpenStatus::Old && isSamePath) {
105f4ecd5a1Speter klausler handler.SignalError("OPEN statement for connected unit may not have "
106f4ecd5a1Speter klausler "explicit STATUS= other than 'OLD'");
107f4ecd5a1Speter klausler return;
108f4ecd5a1Speter klausler }
109f4ecd5a1Speter klausler if (!newPath.get() || isSamePath) {
110f4ecd5a1Speter klausler // OPEN of existing unit, STATUS='OLD' or unspecified, not new FILE=
11195696d56Speter klausler newPath.reset();
11295696d56Speter klausler return;
11395696d56Speter klausler }
11495696d56Speter klausler // Otherwise, OPEN on open unit with new FILE= implies CLOSE
1150006354cSpeter klausler DoImpliedEndfile(handler);
116cd0a1226Speter klausler FlushOutput(handler);
117*ae1d5f4dSPeter Klausler TruncateFrame(0, handler);
11895696d56Speter klausler Close(CloseStatus::Keep, handler);
11995696d56Speter klausler }
12003c066abSPeter Klausler if (newPath.get() && newPathLength > 0) {
12103c066abSPeter Klausler if (const auto *already{
12203c066abSPeter Klausler GetUnitMap().LookUp(newPath.get(), newPathLength)}) {
12303c066abSPeter Klausler handler.SignalError(IostatOpenAlreadyConnected,
12403c066abSPeter Klausler "OPEN(UNIT=%d,FILE='%.*s'): file is already connected to unit %d",
12503c066abSPeter Klausler unitNumber_, static_cast<int>(newPathLength), newPath.get(),
12603c066abSPeter Klausler already->unitNumber_);
12703c066abSPeter Klausler return;
12803c066abSPeter Klausler }
12903c066abSPeter Klausler }
13095696d56Speter klausler set_path(std::move(newPath), newPathLength);
131f4ecd5a1Speter klausler Open(status.value_or(OpenStatus::Unknown), action, position, handler);
1320006354cSpeter klausler auto totalBytes{knownSize()};
1330006354cSpeter klausler if (access == Access::Direct) {
13406ca9f24SPeter Klausler if (!openRecl) {
1350006354cSpeter klausler handler.SignalError(IostatOpenBadRecl,
1360006354cSpeter klausler "OPEN(UNIT=%d,ACCESS='DIRECT'): record length is not known",
1370006354cSpeter klausler unitNumber());
13806ca9f24SPeter Klausler } else if (*openRecl <= 0) {
1390006354cSpeter klausler handler.SignalError(IostatOpenBadRecl,
1400006354cSpeter klausler "OPEN(UNIT=%d,ACCESS='DIRECT',RECL=%jd): record length is invalid",
14106ca9f24SPeter Klausler unitNumber(), static_cast<std::intmax_t>(*openRecl));
14206ca9f24SPeter Klausler } else if (totalBytes && (*totalBytes % *openRecl != 0)) {
1439a163ffeSPeter Klausler handler.SignalError(IostatOpenBadRecl,
1440006354cSpeter klausler "OPEN(UNIT=%d,ACCESS='DIRECT',RECL=%jd): record length is not an "
1450006354cSpeter klausler "even divisor of the file size %jd",
14606ca9f24SPeter Klausler unitNumber(), static_cast<std::intmax_t>(*openRecl),
1470006354cSpeter klausler static_cast<std::intmax_t>(*totalBytes));
1480006354cSpeter klausler }
14906ca9f24SPeter Klausler recordLength = openRecl;
1500006354cSpeter klausler }
151e29c9d77Speter klausler endfileRecordNumber.reset();
152e29c9d77Speter klausler currentRecordNumber = 1;
15306ca9f24SPeter Klausler if (totalBytes && access == Access::Direct && openRecl.value_or(0) > 0) {
15406ca9f24SPeter Klausler endfileRecordNumber = 1 + (*totalBytes / *openRecl);
155e29c9d77Speter klausler }
1569a163ffeSPeter Klausler if (position == Position::Append) {
1579a163ffeSPeter Klausler if (totalBytes) {
1589a163ffeSPeter Klausler frameOffsetInFile_ = *totalBytes;
1599a163ffeSPeter Klausler }
1609a163ffeSPeter Klausler if (access != Access::Stream) {
161e29c9d77Speter klausler if (!endfileRecordNumber) {
1620006354cSpeter klausler // Fake it so that we can backspace relative from the end
163e29c9d77Speter klausler endfileRecordNumber = std::numeric_limits<std::int64_t>::max() - 2;
1640006354cSpeter klausler }
1650006354cSpeter klausler currentRecordNumber = *endfileRecordNumber;
1660006354cSpeter klausler }
16795696d56Speter klausler }
1689a163ffeSPeter Klausler }
16995696d56Speter klausler
OpenAnonymousUnit(std::optional<OpenStatus> status,std::optional<Action> action,Position position,Convert convert,IoErrorHandler & handler)170f4ecd5a1Speter klausler void ExternalFileUnit::OpenAnonymousUnit(std::optional<OpenStatus> status,
171675ad1bcSpeter klausler std::optional<Action> action, Position position, Convert convert,
172675ad1bcSpeter klausler IoErrorHandler &handler) {
173675ad1bcSpeter klausler // I/O to an unconnected unit reads/creates a local file, e.g. fort.7
174675ad1bcSpeter klausler std::size_t pathMaxLen{32};
175675ad1bcSpeter klausler auto path{SizedNew<char>{handler}(pathMaxLen)};
176675ad1bcSpeter klausler std::snprintf(path.get(), pathMaxLen, "fort.%d", unitNumber_);
177675ad1bcSpeter klausler OpenUnit(status, action, position, std::move(path), std::strlen(path.get()),
178675ad1bcSpeter klausler convert, handler);
179675ad1bcSpeter klausler }
180675ad1bcSpeter klausler
CloseUnit(CloseStatus status,IoErrorHandler & handler)18195696d56Speter klausler void ExternalFileUnit::CloseUnit(CloseStatus status, IoErrorHandler &handler) {
1820006354cSpeter klausler DoImpliedEndfile(handler);
183cd0a1226Speter klausler FlushOutput(handler);
18495696d56Speter klausler Close(status, handler);
18595696d56Speter klausler }
1863b635714Speter klausler
DestroyClosed()1873b635714Speter klausler void ExternalFileUnit::DestroyClosed() {
1883b635714Speter klausler GetUnitMap().DestroyClosed(*this); // destroys *this
189f7be2518Speter klausler }
190f7be2518Speter klausler
SetDirection(Direction direction)191df38f35aSPeter Klausler Iostat ExternalFileUnit::SetDirection(Direction direction) {
1920006354cSpeter klausler if (direction == Direction::Input) {
1930006354cSpeter klausler if (mayRead()) {
1940006354cSpeter klausler direction_ = Direction::Input;
195df38f35aSPeter Klausler return IostatOk;
1960006354cSpeter klausler } else {
197df38f35aSPeter Klausler return IostatReadFromWriteOnly;
1980006354cSpeter klausler }
1990006354cSpeter klausler } else {
2000006354cSpeter klausler if (mayWrite()) {
2010006354cSpeter klausler direction_ = Direction::Output;
202df38f35aSPeter Klausler return IostatOk;
2030006354cSpeter klausler } else {
204df38f35aSPeter Klausler return IostatWriteToReadOnly;
2050006354cSpeter klausler }
2060006354cSpeter klausler }
2070006354cSpeter klausler }
2080006354cSpeter klausler
GetUnitMap()2093b635714Speter klausler UnitMap &ExternalFileUnit::GetUnitMap() {
2103b635714Speter klausler if (unitMap) {
2113b635714Speter klausler return *unitMap;
2123b635714Speter klausler }
2133b635714Speter klausler CriticalSection critical{unitMapLock};
2143b635714Speter klausler if (unitMap) {
2153b635714Speter klausler return *unitMap;
2163b635714Speter klausler }
2173b635714Speter klausler Terminator terminator{__FILE__, __LINE__};
2180006354cSpeter klausler IoErrorHandler handler{terminator};
2194f41994cSpeter klausler UnitMap *newUnitMap{New<UnitMap>{terminator}().release()};
220a62b6016SPeter Klausler
2214f41994cSpeter klausler bool wasExtant{false};
222cfbde714SPeter Klausler ExternalFileUnit &out{*newUnitMap->LookUpOrCreate(6, terminator, wasExtant)};
2234f41994cSpeter klausler RUNTIME_CHECK(terminator, !wasExtant);
224f7be2518Speter klausler out.Predefine(1);
225df38f35aSPeter Klausler handler.SignalError(out.SetDirection(Direction::Output));
226d6b7576fSPeter Klausler out.isUnformatted = false;
22795696d56Speter klausler defaultOutput = &out;
228a62b6016SPeter Klausler
229cfbde714SPeter Klausler ExternalFileUnit &in{*newUnitMap->LookUpOrCreate(5, terminator, wasExtant)};
2304f41994cSpeter klausler RUNTIME_CHECK(terminator, !wasExtant);
231f7be2518Speter klausler in.Predefine(0);
232df38f35aSPeter Klausler handler.SignalError(in.SetDirection(Direction::Input));
233d6b7576fSPeter Klausler in.isUnformatted = false;
2340006354cSpeter klausler defaultInput = ∈
235a62b6016SPeter Klausler
236cfbde714SPeter Klausler ExternalFileUnit &error{
237cfbde714SPeter Klausler *newUnitMap->LookUpOrCreate(0, terminator, wasExtant)};
238a62b6016SPeter Klausler RUNTIME_CHECK(terminator, !wasExtant);
239a62b6016SPeter Klausler error.Predefine(2);
240df38f35aSPeter Klausler handler.SignalError(error.SetDirection(Direction::Output));
241a62b6016SPeter Klausler error.isUnformatted = false;
242a62b6016SPeter Klausler errorOutput = &error;
243a62b6016SPeter Klausler
2444f41994cSpeter klausler unitMap = newUnitMap;
2453b635714Speter klausler return *unitMap;
246f7be2518Speter klausler }
247f7be2518Speter klausler
CloseAll(IoErrorHandler & handler)24895696d56Speter klausler void ExternalFileUnit::CloseAll(IoErrorHandler &handler) {
2493b635714Speter klausler CriticalSection critical{unitMapLock};
2503b635714Speter klausler if (unitMap) {
2513b635714Speter klausler unitMap->CloseAll(handler);
2523b635714Speter klausler FreeMemoryAndNullify(unitMap);
2533b635714Speter klausler }
25495696d56Speter klausler defaultOutput = nullptr;
255a62b6016SPeter Klausler defaultInput = nullptr;
256a62b6016SPeter Klausler errorOutput = nullptr;
257f7be2518Speter klausler }
258f7be2518Speter klausler
FlushAll(IoErrorHandler & handler)2590006354cSpeter klausler void ExternalFileUnit::FlushAll(IoErrorHandler &handler) {
2600006354cSpeter klausler CriticalSection critical{unitMapLock};
2610006354cSpeter klausler if (unitMap) {
2620006354cSpeter klausler unitMap->FlushAll(handler);
2630006354cSpeter klausler }
2640006354cSpeter klausler }
2650006354cSpeter klausler
SwapEndianness(char * data,std::size_t bytes,std::size_t elementBytes)2668f2c5c43Speter klausler static void SwapEndianness(
2678f2c5c43Speter klausler char *data, std::size_t bytes, std::size_t elementBytes) {
2688f2c5c43Speter klausler if (elementBytes > 1) {
2698f2c5c43Speter klausler auto half{elementBytes >> 1};
2708f2c5c43Speter klausler for (std::size_t j{0}; j + elementBytes <= bytes; j += elementBytes) {
2718f2c5c43Speter klausler for (std::size_t k{0}; k < half; ++k) {
2728f2c5c43Speter klausler std::swap(data[j + k], data[j + elementBytes - 1 - k]);
2738f2c5c43Speter klausler }
2748f2c5c43Speter klausler }
2758f2c5c43Speter klausler }
2768f2c5c43Speter klausler }
2778f2c5c43Speter klausler
Emit(const char * data,std::size_t bytes,std::size_t elementBytes,IoErrorHandler & handler)2788f2c5c43Speter klausler bool ExternalFileUnit::Emit(const char *data, std::size_t bytes,
2798f2c5c43Speter klausler std::size_t elementBytes, IoErrorHandler &handler) {
28095696d56Speter klausler auto furthestAfter{std::max(furthestPositionInRecord,
28195696d56Speter klausler positionInRecord + static_cast<std::int64_t>(bytes))};
28206ca9f24SPeter Klausler if (openRecl) {
28306ca9f24SPeter Klausler // Check for fixed-length record overrun, but allow for
284f651bbeaSPeter Klausler // sequential record termination.
285f651bbeaSPeter Klausler int extra{0};
286f651bbeaSPeter Klausler int header{0};
287f651bbeaSPeter Klausler if (access == Access::Sequential) {
288f651bbeaSPeter Klausler if (isUnformatted.value_or(false)) {
289f651bbeaSPeter Klausler // record header + footer
290f651bbeaSPeter Klausler header = static_cast<int>(sizeof(std::uint32_t));
291f651bbeaSPeter Klausler extra = 2 * header;
292f651bbeaSPeter Klausler } else {
293991696c2SPeter Klausler #ifdef _WIN32
294991696c2SPeter Klausler if (!isWindowsTextFile()) {
295991696c2SPeter Klausler ++extra; // carriage return (CR)
296991696c2SPeter Klausler }
297991696c2SPeter Klausler #endif
298991696c2SPeter Klausler ++extra; // newline (LF)
299f651bbeaSPeter Klausler }
300f651bbeaSPeter Klausler }
301f651bbeaSPeter Klausler if (furthestAfter > extra + *openRecl) {
3020006354cSpeter klausler handler.SignalError(IostatRecordWriteOverrun,
303b232a88cSpeter klausler "Attempt to write %zd bytes to position %jd in a fixed-size record "
304b232a88cSpeter klausler "of %jd bytes",
305f651bbeaSPeter Klausler bytes, static_cast<std::intmax_t>(positionInRecord - header),
30606ca9f24SPeter Klausler static_cast<std::intmax_t>(*openRecl));
3073b635714Speter klausler return false;
3083b635714Speter klausler }
309ef7f6f7cSPeter Klausler }
310ef7f6f7cSPeter Klausler if (recordLength) {
31106ca9f24SPeter Klausler // It is possible for recordLength to have a value now for a
31206ca9f24SPeter Klausler // variable-length output record if the previous operation
31306ca9f24SPeter Klausler // was a BACKSPACE or non advancing input statement.
31406ca9f24SPeter Klausler recordLength.reset();
31506ca9f24SPeter Klausler beganReadingRecord_ = false;
316b232a88cSpeter klausler }
317ac4202feSPeter Klausler if (IsAfterEndfile()) {
318ac4202feSPeter Klausler handler.SignalError(IostatWriteAfterEndfile);
319ac4202feSPeter Klausler return false;
320ac4202feSPeter Klausler }
321507f7317SPeter Klausler CheckDirectAccess(handler);
3223b635714Speter klausler WriteFrame(frameOffsetInFile_, recordOffsetInFrame_ + furthestAfter, handler);
32327505565Speter klausler if (positionInRecord > furthestPositionInRecord) {
3240006354cSpeter klausler std::memset(Frame() + recordOffsetInFrame_ + furthestPositionInRecord, ' ',
32527505565Speter klausler positionInRecord - furthestPositionInRecord);
32627505565Speter klausler }
3278f2c5c43Speter klausler char *to{Frame() + recordOffsetInFrame_ + positionInRecord};
3288f2c5c43Speter klausler std::memcpy(to, data, bytes);
3298f2c5c43Speter klausler if (swapEndianness_) {
3308f2c5c43Speter klausler SwapEndianness(to, bytes, elementBytes);
3318f2c5c43Speter klausler }
332f7be2518Speter klausler positionInRecord += bytes;
333f7be2518Speter klausler furthestPositionInRecord = furthestAfter;
334f7be2518Speter klausler return true;
335f7be2518Speter klausler }
336f7be2518Speter klausler
Receive(char * data,std::size_t bytes,std::size_t elementBytes,IoErrorHandler & handler)3378f2c5c43Speter klausler bool ExternalFileUnit::Receive(char *data, std::size_t bytes,
3388f2c5c43Speter klausler std::size_t elementBytes, IoErrorHandler &handler) {
3390006354cSpeter klausler RUNTIME_CHECK(handler, direction_ == Direction::Input);
3400006354cSpeter klausler auto furthestAfter{std::max(furthestPositionInRecord,
3410006354cSpeter klausler positionInRecord + static_cast<std::int64_t>(bytes))};
3420006354cSpeter klausler if (furthestAfter > recordLength.value_or(furthestAfter)) {
3430006354cSpeter klausler handler.SignalError(IostatRecordReadOverrun,
3440006354cSpeter klausler "Attempt to read %zd bytes at position %jd in a record of %jd bytes",
3450006354cSpeter klausler bytes, static_cast<std::intmax_t>(positionInRecord),
3460006354cSpeter klausler static_cast<std::intmax_t>(*recordLength));
3470006354cSpeter klausler return false;
3480006354cSpeter klausler }
3490006354cSpeter klausler auto need{recordOffsetInFrame_ + furthestAfter};
3500006354cSpeter klausler auto got{ReadFrame(frameOffsetInFile_, need, handler)};
3510006354cSpeter klausler if (got >= need) {
3520006354cSpeter klausler std::memcpy(data, Frame() + recordOffsetInFrame_ + positionInRecord, bytes);
3538f2c5c43Speter klausler if (swapEndianness_) {
3548f2c5c43Speter klausler SwapEndianness(data, bytes, elementBytes);
3558f2c5c43Speter klausler }
3560006354cSpeter klausler positionInRecord += bytes;
3570006354cSpeter klausler furthestPositionInRecord = furthestAfter;
3580006354cSpeter klausler return true;
3590006354cSpeter klausler } else {
3608527f9e4SPeter Klausler HitEndOnRead(handler);
3610006354cSpeter klausler return false;
3620006354cSpeter klausler }
3630006354cSpeter klausler }
3640006354cSpeter klausler
GetNextInputBytes(const char * & p,IoErrorHandler & handler)365da25f968SPeter Klausler std::size_t ExternalFileUnit::GetNextInputBytes(
366da25f968SPeter Klausler const char *&p, IoErrorHandler &handler) {
367da25f968SPeter Klausler RUNTIME_CHECK(handler, direction_ == Direction::Input);
368991696c2SPeter Klausler std::size_t length{1};
369991696c2SPeter Klausler if (auto recl{EffectiveRecordLength()}) {
370991696c2SPeter Klausler if (positionInRecord < *recl) {
371991696c2SPeter Klausler length = *recl - positionInRecord;
372991696c2SPeter Klausler } else {
373991696c2SPeter Klausler p = nullptr;
374991696c2SPeter Klausler return 0;
375991696c2SPeter Klausler }
376991696c2SPeter Klausler }
377991696c2SPeter Klausler p = FrameNextInput(handler, length);
378991696c2SPeter Klausler return p ? length : 0;
379da25f968SPeter Klausler }
380da25f968SPeter Klausler
FrameNextInput(IoErrorHandler & handler,std::size_t bytes)3810006354cSpeter klausler const char *ExternalFileUnit::FrameNextInput(
3820006354cSpeter klausler IoErrorHandler &handler, std::size_t bytes) {
383199a623eSpeter klausler RUNTIME_CHECK(handler, isUnformatted.has_value() && !*isUnformatted);
3840006354cSpeter klausler if (static_cast<std::int64_t>(positionInRecord + bytes) <=
3850006354cSpeter klausler recordLength.value_or(positionInRecord + bytes)) {
3860006354cSpeter klausler auto at{recordOffsetInFrame_ + positionInRecord};
3870006354cSpeter klausler auto need{static_cast<std::size_t>(at + bytes)};
3880006354cSpeter klausler auto got{ReadFrame(frameOffsetInFile_, need, handler)};
389991696c2SPeter Klausler SetVariableFormattedRecordLength();
3900006354cSpeter klausler if (got >= need) {
3910006354cSpeter klausler return Frame() + at;
3920006354cSpeter klausler }
3938527f9e4SPeter Klausler HitEndOnRead(handler);
394991696c2SPeter Klausler }
3950006354cSpeter klausler return nullptr;
3960006354cSpeter klausler }
3970006354cSpeter klausler
SetVariableFormattedRecordLength()398991696c2SPeter Klausler bool ExternalFileUnit::SetVariableFormattedRecordLength() {
399991696c2SPeter Klausler if (recordLength || access == Access::Direct) {
4000006354cSpeter klausler return true;
401b43e083bSpeter klausler } else if (FrameLength() > recordOffsetInFrame_) {
4020006354cSpeter klausler const char *record{Frame() + recordOffsetInFrame_};
403b43e083bSpeter klausler std::size_t bytes{FrameLength() - recordOffsetInFrame_};
404b43e083bSpeter klausler if (const char *nl{
405b43e083bSpeter klausler reinterpret_cast<const char *>(std::memchr(record, '\n', bytes))}) {
4060006354cSpeter klausler recordLength = nl - record;
4070006354cSpeter klausler if (*recordLength > 0 && record[*recordLength - 1] == '\r') {
4080006354cSpeter klausler --*recordLength;
4090006354cSpeter klausler }
4100006354cSpeter klausler return true;
4110006354cSpeter klausler }
412b43e083bSpeter klausler }
413dfea011aSpeter klausler return false;
414dfea011aSpeter klausler }
4153b635714Speter klausler
BeginReadingRecord(IoErrorHandler & handler)416e81c96d6Speter klausler bool ExternalFileUnit::BeginReadingRecord(IoErrorHandler &handler) {
4170006354cSpeter klausler RUNTIME_CHECK(handler, direction_ == Direction::Input);
418e81c96d6Speter klausler if (!beganReadingRecord_) {
419e24f0ac7Speter klausler beganReadingRecord_ = true;
42006ca9f24SPeter Klausler if (access == Access::Direct) {
421507f7317SPeter Klausler CheckDirectAccess(handler);
42206ca9f24SPeter Klausler auto need{static_cast<std::size_t>(recordOffsetInFrame_ + *openRecl)};
4230006354cSpeter klausler auto got{ReadFrame(frameOffsetInFile_, need, handler)};
42406ca9f24SPeter Klausler if (got >= need) {
42506ca9f24SPeter Klausler recordLength = openRecl;
42606ca9f24SPeter Klausler } else {
42706ca9f24SPeter Klausler recordLength.reset();
4288527f9e4SPeter Klausler HitEndOnRead(handler);
4290006354cSpeter klausler }
430991696c2SPeter Klausler } else {
43106ca9f24SPeter Klausler recordLength.reset();
432ac4202feSPeter Klausler if (IsAtEOF()) {
43306ca9f24SPeter Klausler handler.SignalEnd();
434199a623eSpeter klausler } else {
435199a623eSpeter klausler RUNTIME_CHECK(handler, isUnformatted.has_value());
436991696c2SPeter Klausler if (*isUnformatted) {
437991696c2SPeter Klausler if (access == Access::Sequential) {
4380006354cSpeter klausler BeginSequentialVariableUnformattedInputRecord(handler);
439991696c2SPeter Klausler }
440991696c2SPeter Klausler } else { // formatted sequential or stream
441991696c2SPeter Klausler BeginVariableFormattedInputRecord(handler);
4420006354cSpeter klausler }
4430006354cSpeter klausler }
4440006354cSpeter klausler }
445199a623eSpeter klausler }
446e81c96d6Speter klausler RUNTIME_CHECK(handler,
447991696c2SPeter Klausler recordLength.has_value() || !IsRecordFile() || handler.InError());
448e81c96d6Speter klausler return !handler.InError();
449e81c96d6Speter klausler }
4500006354cSpeter klausler
FinishReadingRecord(IoErrorHandler & handler)451e24f0ac7Speter klausler void ExternalFileUnit::FinishReadingRecord(IoErrorHandler &handler) {
452e24f0ac7Speter klausler RUNTIME_CHECK(handler, direction_ == Direction::Input && beganReadingRecord_);
453e24f0ac7Speter klausler beganReadingRecord_ = false;
4546ce0fba0SPeter Klausler if (handler.GetIoStat() == IostatEnd ||
4556ce0fba0SPeter Klausler (IsRecordFile() && !recordLength.has_value())) {
456991696c2SPeter Klausler // Avoid bogus crashes in END/ERR circumstances; but
457991696c2SPeter Klausler // still increment the current record number so that
458991696c2SPeter Klausler // an attempted read of an endfile record, followed by
459991696c2SPeter Klausler // a BACKSPACE, will still be at EOF.
460991696c2SPeter Klausler ++currentRecordNumber;
461991696c2SPeter Klausler } else if (IsRecordFile()) {
462e7823608SPeter Klausler recordOffsetInFrame_ += *recordLength;
463991696c2SPeter Klausler if (access != Access::Direct) {
464199a623eSpeter klausler RUNTIME_CHECK(handler, isUnformatted.has_value());
465e7823608SPeter Klausler recordLength.reset();
466199a623eSpeter klausler if (isUnformatted.value_or(false)) {
4670006354cSpeter klausler // Retain footer in frame for more efficient BACKSPACE
468e7823608SPeter Klausler frameOffsetInFile_ += recordOffsetInFrame_;
4690006354cSpeter klausler recordOffsetInFrame_ = sizeof(std::uint32_t);
4700006354cSpeter klausler } else { // formatted
471e7823608SPeter Klausler if (FrameLength() > recordOffsetInFrame_ &&
472e7823608SPeter Klausler Frame()[recordOffsetInFrame_] == '\r') {
4730006354cSpeter klausler ++recordOffsetInFrame_;
4740006354cSpeter klausler }
475991696c2SPeter Klausler if (FrameLength() > recordOffsetInFrame_ &&
476e7823608SPeter Klausler Frame()[recordOffsetInFrame_] == '\n') {
477b43e083bSpeter klausler ++recordOffsetInFrame_;
478b43e083bSpeter klausler }
479e7823608SPeter Klausler if (!pinnedFrame || mayPosition()) {
480e7823608SPeter Klausler frameOffsetInFile_ += recordOffsetInFrame_;
481dfea011aSpeter klausler recordOffsetInFrame_ = 0;
482dfea011aSpeter klausler }
483f7be2518Speter klausler }
4843b635714Speter klausler }
4854e53e283SAndrzej Warzynski ++currentRecordNumber;
486991696c2SPeter Klausler } else { // unformatted stream
487991696c2SPeter Klausler furthestPositionInRecord =
488991696c2SPeter Klausler std::max(furthestPositionInRecord, positionInRecord);
489991696c2SPeter Klausler frameOffsetInFile_ += recordOffsetInFrame_ + furthestPositionInRecord;
490991696c2SPeter Klausler }
491e24f0ac7Speter klausler BeginRecord();
492e24f0ac7Speter klausler }
493e24f0ac7Speter klausler
AdvanceRecord(IoErrorHandler & handler)494e24f0ac7Speter klausler bool ExternalFileUnit::AdvanceRecord(IoErrorHandler &handler) {
495e24f0ac7Speter klausler if (direction_ == Direction::Input) {
496e24f0ac7Speter klausler FinishReadingRecord(handler);
497e81c96d6Speter klausler return BeginReadingRecord(handler);
4980006354cSpeter klausler } else { // Direction::Output
499e81c96d6Speter klausler bool ok{true};
500199a623eSpeter klausler RUNTIME_CHECK(handler, isUnformatted.has_value());
501991696c2SPeter Klausler positionInRecord = furthestPositionInRecord;
502991696c2SPeter Klausler if (access == Access::Direct) {
503991696c2SPeter Klausler if (furthestPositionInRecord <
504991696c2SPeter Klausler openRecl.value_or(furthestPositionInRecord)) {
505a94d943fSpeter klausler // Pad remainder of fixed length record
506f651bbeaSPeter Klausler WriteFrame(
507f651bbeaSPeter Klausler frameOffsetInFile_, recordOffsetInFrame_ + *openRecl, handler);
5083b635714Speter klausler std::memset(Frame() + recordOffsetInFrame_ + furthestPositionInRecord,
509199a623eSpeter klausler isUnformatted.value_or(false) ? 0 : ' ',
51006ca9f24SPeter Klausler *openRecl - furthestPositionInRecord);
51106ca9f24SPeter Klausler furthestPositionInRecord = *openRecl;
5123b635714Speter klausler }
513991696c2SPeter Klausler } else if (*isUnformatted) {
514991696c2SPeter Klausler if (access == Access::Sequential) {
515a94d943fSpeter klausler // Append the length of a sequential unformatted variable-length record
516a94d943fSpeter klausler // as its footer, then overwrite the reserved first four bytes of the
517a94d943fSpeter klausler // record with its length as its header. These four bytes were skipped
518a94d943fSpeter klausler // over in BeginUnformattedIO<Output>().
519a94d943fSpeter klausler // TODO: Break very large records up into subrecords with negative
520a94d943fSpeter klausler // headers &/or footers
521a94d943fSpeter klausler std::uint32_t length;
522a94d943fSpeter klausler length = furthestPositionInRecord - sizeof length;
5236a1c3efaSpeter klausler ok = ok &&
5246a1c3efaSpeter klausler Emit(reinterpret_cast<const char *>(&length), sizeof length,
525a94d943fSpeter klausler sizeof length, handler);
526a94d943fSpeter klausler positionInRecord = 0;
5276a1c3efaSpeter klausler ok = ok &&
5286a1c3efaSpeter klausler Emit(reinterpret_cast<const char *>(&length), sizeof length,
529a94d943fSpeter klausler sizeof length, handler);
530a94d943fSpeter klausler } else {
531991696c2SPeter Klausler // Unformatted stream: nothing to do
5323b635714Speter klausler }
5331fe7a187SPeter Klausler } else if (handler.GetIoStat() != IostatOk &&
5341fe7a187SPeter Klausler furthestPositionInRecord == 0) {
5351fe7a187SPeter Klausler // Error in formatted variable length record, and no output yet; do
5361fe7a187SPeter Klausler // nothing, like most other Fortran compilers do.
5371fe7a187SPeter Klausler return true;
538991696c2SPeter Klausler } else {
539991696c2SPeter Klausler // Terminate formatted variable length record
540991696c2SPeter Klausler const char *lineEnding{"\n"};
541991696c2SPeter Klausler std::size_t lineEndingBytes{1};
542991696c2SPeter Klausler #ifdef _WIN32
543991696c2SPeter Klausler if (!isWindowsTextFile()) {
544991696c2SPeter Klausler lineEnding = "\r\n";
545991696c2SPeter Klausler lineEndingBytes = 2;
546991696c2SPeter Klausler }
547991696c2SPeter Klausler #endif
548991696c2SPeter Klausler ok = ok && Emit(lineEnding, lineEndingBytes, 1, handler);
5494e53e283SAndrzej Warzynski }
55036771bbaSPeter Klausler leftTabLimit.reset();
551ac4202feSPeter Klausler if (IsAfterEndfile()) {
552ac4202feSPeter Klausler return false;
553ac4202feSPeter Klausler }
554cd0a1226Speter klausler CommitWrites();
555f7be2518Speter klausler ++currentRecordNumber;
556991696c2SPeter Klausler if (access != Access::Direct) {
557991696c2SPeter Klausler impliedEndfile_ = IsRecordFile();
558ac4202feSPeter Klausler if (IsAtEOF()) {
5594d42e16eSpeter klausler endfileRecordNumber.reset();
5604d42e16eSpeter klausler }
561991696c2SPeter Klausler }
562f7be2518Speter klausler return ok;
563f7be2518Speter klausler }
564e81c96d6Speter klausler }
565f7be2518Speter klausler
BackspaceRecord(IoErrorHandler & handler)5663b635714Speter klausler void ExternalFileUnit::BackspaceRecord(IoErrorHandler &handler) {
567991696c2SPeter Klausler if (access == Access::Direct || !IsRecordFile()) {
5680006354cSpeter klausler handler.SignalError(IostatBackspaceNonSequential,
569991696c2SPeter Klausler "BACKSPACE(UNIT=%d) on direct-access file or unformatted stream",
570991696c2SPeter Klausler unitNumber());
5713b635714Speter klausler } else {
572ac4202feSPeter Klausler if (IsAfterEndfile()) {
5734d42e16eSpeter klausler // BACKSPACE after explicit ENDFILE
5744d42e16eSpeter klausler currentRecordNumber = *endfileRecordNumber;
57572831a59SPeter Klausler } else if (leftTabLimit) {
57672831a59SPeter Klausler // BACKSPACE after non-advancing I/O
57772831a59SPeter Klausler leftTabLimit.reset();
578e29c9d77Speter klausler } else {
5790006354cSpeter klausler DoImpliedEndfile(handler);
580e29c9d77Speter klausler if (frameOffsetInFile_ + recordOffsetInFrame_ > 0) {
5810006354cSpeter klausler --currentRecordNumber;
58206ca9f24SPeter Klausler if (openRecl && access == Access::Direct) {
5830006354cSpeter klausler BackspaceFixedRecord(handler);
584199a623eSpeter klausler } else {
585199a623eSpeter klausler RUNTIME_CHECK(handler, isUnformatted.has_value());
586199a623eSpeter klausler if (isUnformatted.value_or(false)) {
5870006354cSpeter klausler BackspaceVariableUnformattedRecord(handler);
5883b635714Speter klausler } else {
5890006354cSpeter klausler BackspaceVariableFormattedRecord(handler);
5903b635714Speter klausler }
5910006354cSpeter klausler }
592f7be2518Speter klausler }
593199a623eSpeter klausler }
594e29c9d77Speter klausler BeginRecord();
595e29c9d77Speter klausler }
596e29c9d77Speter klausler }
597f7be2518Speter klausler
FlushOutput(IoErrorHandler & handler)598cd0a1226Speter klausler void ExternalFileUnit::FlushOutput(IoErrorHandler &handler) {
599cd0a1226Speter klausler if (!mayPosition()) {
600cd0a1226Speter klausler auto frameAt{FrameAt()};
601cd0a1226Speter klausler if (frameOffsetInFile_ >= frameAt &&
602cd0a1226Speter klausler frameOffsetInFile_ <
603cd0a1226Speter klausler static_cast<std::int64_t>(frameAt + FrameLength())) {
604cd0a1226Speter klausler // A Flush() that's about to happen to a non-positionable file
605cd0a1226Speter klausler // needs to advance frameOffsetInFile_ to prevent attempts at
606cd0a1226Speter klausler // impossible seeks
607cd0a1226Speter klausler CommitWrites();
608cd0a1226Speter klausler }
609cd0a1226Speter klausler }
610cd0a1226Speter klausler Flush(handler);
611cd0a1226Speter klausler }
612cd0a1226Speter klausler
FlushIfTerminal(IoErrorHandler & handler)61395696d56Speter klausler void ExternalFileUnit::FlushIfTerminal(IoErrorHandler &handler) {
61495696d56Speter klausler if (isTerminal()) {
615cd0a1226Speter klausler FlushOutput(handler);
61695696d56Speter klausler }
61795696d56Speter klausler }
61895696d56Speter klausler
Endfile(IoErrorHandler & handler)6190006354cSpeter klausler void ExternalFileUnit::Endfile(IoErrorHandler &handler) {
620991696c2SPeter Klausler if (access == Access::Direct) {
621991696c2SPeter Klausler handler.SignalError(IostatEndfileDirect,
622991696c2SPeter Klausler "ENDFILE(UNIT=%d) on direct-access file", unitNumber());
6230006354cSpeter klausler } else if (!mayWrite()) {
6240006354cSpeter klausler handler.SignalError(IostatEndfileUnwritable,
6250006354cSpeter klausler "ENDFILE(UNIT=%d) on read-only file", unitNumber());
626ac4202feSPeter Klausler } else if (IsAfterEndfile()) {
627e29c9d77Speter klausler // ENDFILE after ENDFILE
6280006354cSpeter klausler } else {
6290006354cSpeter klausler DoEndfile(handler);
63036771bbaSPeter Klausler if (IsRecordFile() && access != Access::Direct) {
6314d42e16eSpeter klausler // Explicit ENDFILE leaves position *after* the endfile record
6324d42e16eSpeter klausler RUNTIME_CHECK(handler, endfileRecordNumber.has_value());
6334d42e16eSpeter klausler currentRecordNumber = *endfileRecordNumber + 1;
6340006354cSpeter klausler }
6350006354cSpeter klausler }
636991696c2SPeter Klausler }
6370006354cSpeter klausler
Rewind(IoErrorHandler & handler)6380006354cSpeter klausler void ExternalFileUnit::Rewind(IoErrorHandler &handler) {
6390006354cSpeter klausler if (access == Access::Direct) {
6400006354cSpeter klausler handler.SignalError(IostatRewindNonSequential,
6410006354cSpeter klausler "REWIND(UNIT=%d) on non-sequential file", unitNumber());
6420006354cSpeter klausler } else {
643991696c2SPeter Klausler SetPosition(0, handler);
6440006354cSpeter klausler currentRecordNumber = 1;
6456963fb7dSPeter Klausler leftTabLimit.reset();
6460006354cSpeter klausler }
6470006354cSpeter klausler }
6480006354cSpeter klausler
SetPosition(std::int64_t pos,IoErrorHandler & handler)649991696c2SPeter Klausler void ExternalFileUnit::SetPosition(std::int64_t pos, IoErrorHandler &handler) {
650991696c2SPeter Klausler DoImpliedEndfile(handler);
651991696c2SPeter Klausler frameOffsetInFile_ = pos;
652991696c2SPeter Klausler recordOffsetInFrame_ = 0;
653507f7317SPeter Klausler if (access == Access::Direct) {
654507f7317SPeter Klausler directAccessRecWasSet_ = true;
655507f7317SPeter Klausler }
656991696c2SPeter Klausler BeginRecord();
657991696c2SPeter Klausler }
658991696c2SPeter Klausler
SetStreamPos(std::int64_t oneBasedPos,IoErrorHandler & handler)659d771245aSPeter Klausler bool ExternalFileUnit::SetStreamPos(
660d771245aSPeter Klausler std::int64_t oneBasedPos, IoErrorHandler &handler) {
661d771245aSPeter Klausler if (access != Access::Stream) {
662d771245aSPeter Klausler handler.SignalError("POS= may not appear unless ACCESS='STREAM'");
663d771245aSPeter Klausler return false;
664d771245aSPeter Klausler }
665d771245aSPeter Klausler if (oneBasedPos < 1) { // POS=1 is beginning of file (12.6.2.11)
666d771245aSPeter Klausler handler.SignalError(
667d771245aSPeter Klausler "POS=%zd is invalid", static_cast<std::intmax_t>(oneBasedPos));
668d771245aSPeter Klausler return false;
669d771245aSPeter Klausler }
670d771245aSPeter Klausler SetPosition(oneBasedPos - 1, handler);
671d771245aSPeter Klausler // We no longer know which record we're in. Set currentRecordNumber to
672d771245aSPeter Klausler // a large value from whence we can both advance and backspace.
673d771245aSPeter Klausler currentRecordNumber = std::numeric_limits<std::int64_t>::max() / 2;
674d771245aSPeter Klausler endfileRecordNumber.reset();
675d771245aSPeter Klausler return true;
676d771245aSPeter Klausler }
677d771245aSPeter Klausler
SetDirectRec(std::int64_t oneBasedRec,IoErrorHandler & handler)678d771245aSPeter Klausler bool ExternalFileUnit::SetDirectRec(
679d771245aSPeter Klausler std::int64_t oneBasedRec, IoErrorHandler &handler) {
680d771245aSPeter Klausler if (access != Access::Direct) {
681d771245aSPeter Klausler handler.SignalError("REC= may not appear unless ACCESS='DIRECT'");
682d771245aSPeter Klausler return false;
683d771245aSPeter Klausler }
684d771245aSPeter Klausler if (!openRecl) {
685d771245aSPeter Klausler handler.SignalError("RECL= was not specified");
686d771245aSPeter Klausler return false;
687d771245aSPeter Klausler }
688d771245aSPeter Klausler if (oneBasedRec < 1) {
689d771245aSPeter Klausler handler.SignalError(
690d771245aSPeter Klausler "REC=%zd is invalid", static_cast<std::intmax_t>(oneBasedRec));
691d771245aSPeter Klausler return false;
692d771245aSPeter Klausler }
693d771245aSPeter Klausler currentRecordNumber = oneBasedRec;
694d771245aSPeter Klausler SetPosition((oneBasedRec - 1) * *openRecl, handler);
695d771245aSPeter Klausler return true;
696d771245aSPeter Klausler }
697d771245aSPeter Klausler
EndIoStatement()69895696d56Speter klausler void ExternalFileUnit::EndIoStatement() {
69995696d56Speter klausler io_.reset();
700f7be2518Speter klausler u_.emplace<std::monostate>();
7013b635714Speter klausler lock_.Drop();
7023b635714Speter klausler }
7033b635714Speter klausler
BeginSequentialVariableUnformattedInputRecord(IoErrorHandler & handler)7040006354cSpeter klausler void ExternalFileUnit::BeginSequentialVariableUnformattedInputRecord(
7053b635714Speter klausler IoErrorHandler &handler) {
7063b635714Speter klausler std::int32_t header{0}, footer{0};
7070006354cSpeter klausler std::size_t need{recordOffsetInFrame_ + sizeof header};
7080006354cSpeter klausler std::size_t got{ReadFrame(frameOffsetInFile_, need, handler)};
7093b635714Speter klausler // Try to emit informative errors to help debug corrupted files.
7103b635714Speter klausler const char *error{nullptr};
7113b635714Speter klausler if (got < need) {
7120006354cSpeter klausler if (got == recordOffsetInFrame_) {
7138527f9e4SPeter Klausler HitEndOnRead(handler);
7143b635714Speter klausler } else {
7150006354cSpeter klausler error = "Unformatted variable-length sequential file input failed at "
7160006354cSpeter klausler "record #%jd (file offset %jd): truncated record header";
7173b635714Speter klausler }
7183b635714Speter klausler } else {
7190006354cSpeter klausler std::memcpy(&header, Frame() + recordOffsetInFrame_, sizeof header);
7200006354cSpeter klausler recordLength = sizeof header + header; // does not include footer
7210006354cSpeter klausler need = recordOffsetInFrame_ + *recordLength + sizeof footer;
7220006354cSpeter klausler got = ReadFrame(frameOffsetInFile_, need, handler);
7233b635714Speter klausler if (got < need) {
7240006354cSpeter klausler error = "Unformatted variable-length sequential file input failed at "
7250006354cSpeter klausler "record #%jd (file offset %jd): hit EOF reading record with "
7260006354cSpeter klausler "length %jd bytes";
7273b635714Speter klausler } else {
7280006354cSpeter klausler std::memcpy(&footer, Frame() + recordOffsetInFrame_ + *recordLength,
7290006354cSpeter klausler sizeof footer);
7303b635714Speter klausler if (footer != header) {
7310006354cSpeter klausler error = "Unformatted variable-length sequential file input failed at "
7320006354cSpeter klausler "record #%jd (file offset %jd): record header has length %jd "
7330006354cSpeter klausler "that does not match record footer (%jd)";
7343b635714Speter klausler }
7353b635714Speter klausler }
7363b635714Speter klausler }
7373b635714Speter klausler if (error) {
7383b635714Speter klausler handler.SignalError(error, static_cast<std::intmax_t>(currentRecordNumber),
7393b635714Speter klausler static_cast<std::intmax_t>(frameOffsetInFile_),
7403b635714Speter klausler static_cast<std::intmax_t>(header), static_cast<std::intmax_t>(footer));
7410006354cSpeter klausler // TODO: error recovery
7423b635714Speter klausler }
7433b635714Speter klausler positionInRecord = sizeof header;
7443b635714Speter klausler }
7453b635714Speter klausler
BeginVariableFormattedInputRecord(IoErrorHandler & handler)746991696c2SPeter Klausler void ExternalFileUnit::BeginVariableFormattedInputRecord(
7473b635714Speter klausler IoErrorHandler &handler) {
748a62b6016SPeter Klausler if (this == defaultInput) {
749a62b6016SPeter Klausler if (defaultOutput) {
750cd0a1226Speter klausler defaultOutput->FlushOutput(handler);
7510006354cSpeter klausler }
752a62b6016SPeter Klausler if (errorOutput) {
753a62b6016SPeter Klausler errorOutput->FlushOutput(handler);
754a62b6016SPeter Klausler }
755a62b6016SPeter Klausler }
7563b635714Speter klausler std::size_t length{0};
7570006354cSpeter klausler do {
758dfea011aSpeter klausler std::size_t need{length + 1};
759dfea011aSpeter klausler length =
760dfea011aSpeter klausler ReadFrame(frameOffsetInFile_, recordOffsetInFrame_ + need, handler) -
761dfea011aSpeter klausler recordOffsetInFrame_;
7620006354cSpeter klausler if (length < need) {
763dfea011aSpeter klausler if (length > 0) {
764dfea011aSpeter klausler // final record w/o \n
765dfea011aSpeter klausler recordLength = length;
76636771bbaSPeter Klausler unterminatedRecord = true;
767dfea011aSpeter klausler } else {
7688527f9e4SPeter Klausler HitEndOnRead(handler);
769dfea011aSpeter klausler }
7703b635714Speter klausler break;
7713b635714Speter klausler }
772991696c2SPeter Klausler } while (!SetVariableFormattedRecordLength());
7733b635714Speter klausler }
7740006354cSpeter klausler
BackspaceFixedRecord(IoErrorHandler & handler)7750006354cSpeter klausler void ExternalFileUnit::BackspaceFixedRecord(IoErrorHandler &handler) {
77606ca9f24SPeter Klausler RUNTIME_CHECK(handler, openRecl.has_value());
77706ca9f24SPeter Klausler if (frameOffsetInFile_ < *openRecl) {
7780006354cSpeter klausler handler.SignalError(IostatBackspaceAtFirstRecord);
7790006354cSpeter klausler } else {
78006ca9f24SPeter Klausler frameOffsetInFile_ -= *openRecl;
7813b635714Speter klausler }
7823b635714Speter klausler }
7833b635714Speter klausler
BackspaceVariableUnformattedRecord(IoErrorHandler & handler)7840006354cSpeter klausler void ExternalFileUnit::BackspaceVariableUnformattedRecord(
7853b635714Speter klausler IoErrorHandler &handler) {
7863b635714Speter klausler std::int32_t header{0}, footer{0};
7870006354cSpeter klausler auto headerBytes{static_cast<std::int64_t>(sizeof header)};
7880006354cSpeter klausler frameOffsetInFile_ += recordOffsetInFrame_;
7890006354cSpeter klausler recordOffsetInFrame_ = 0;
7900006354cSpeter klausler if (frameOffsetInFile_ <= headerBytes) {
7910006354cSpeter klausler handler.SignalError(IostatBackspaceAtFirstRecord);
7920006354cSpeter klausler return;
7930006354cSpeter klausler }
7943b635714Speter klausler // Error conditions here cause crashes, not file format errors, because the
7953b635714Speter klausler // validity of the file structure before the current record will have been
7960006354cSpeter klausler // checked informatively in NextSequentialVariableUnformattedInputRecord().
7973b635714Speter klausler std::size_t got{
7980006354cSpeter klausler ReadFrame(frameOffsetInFile_ - headerBytes, headerBytes, handler)};
799461b6fe4SPeter Klausler if (static_cast<std::int64_t>(got) < headerBytes) {
800461b6fe4SPeter Klausler handler.SignalError(IostatShortRead);
801461b6fe4SPeter Klausler return;
802461b6fe4SPeter Klausler }
8033b635714Speter klausler std::memcpy(&footer, Frame(), sizeof footer);
8043b635714Speter klausler recordLength = footer;
805461b6fe4SPeter Klausler if (frameOffsetInFile_ < *recordLength + 2 * headerBytes) {
806461b6fe4SPeter Klausler handler.SignalError(IostatBadUnformattedRecord);
807461b6fe4SPeter Klausler return;
808461b6fe4SPeter Klausler }
8090006354cSpeter klausler frameOffsetInFile_ -= *recordLength + 2 * headerBytes;
8100006354cSpeter klausler if (frameOffsetInFile_ >= headerBytes) {
8110006354cSpeter klausler frameOffsetInFile_ -= headerBytes;
8120006354cSpeter klausler recordOffsetInFrame_ = headerBytes;
8130006354cSpeter klausler }
8140006354cSpeter klausler auto need{static_cast<std::size_t>(
8150006354cSpeter klausler recordOffsetInFrame_ + sizeof header + *recordLength)};
8160006354cSpeter klausler got = ReadFrame(frameOffsetInFile_, need, handler);
817461b6fe4SPeter Klausler if (got < need) {
818461b6fe4SPeter Klausler handler.SignalError(IostatShortRead);
819461b6fe4SPeter Klausler return;
820461b6fe4SPeter Klausler }
8210006354cSpeter klausler std::memcpy(&header, Frame() + recordOffsetInFrame_, sizeof header);
822461b6fe4SPeter Klausler if (header != *recordLength) {
823461b6fe4SPeter Klausler handler.SignalError(IostatBadUnformattedRecord);
824461b6fe4SPeter Klausler return;
825461b6fe4SPeter Klausler }
8263b635714Speter klausler }
8273b635714Speter klausler
8283b635714Speter klausler // There's no portable memrchr(), unfortunately, and strrchr() would
8293b635714Speter klausler // fail on a record with a NUL, so we have to do it the hard way.
FindLastNewline(const char * str,std::size_t length)8303b635714Speter klausler static const char *FindLastNewline(const char *str, std::size_t length) {
8313b635714Speter klausler for (const char *p{str + length}; p-- > str;) {
8323b635714Speter klausler if (*p == '\n') {
8333b635714Speter klausler return p;
8343b635714Speter klausler }
8353b635714Speter klausler }
8363b635714Speter klausler return nullptr;
8373b635714Speter klausler }
8383b635714Speter klausler
BackspaceVariableFormattedRecord(IoErrorHandler & handler)8390006354cSpeter klausler void ExternalFileUnit::BackspaceVariableFormattedRecord(
8403b635714Speter klausler IoErrorHandler &handler) {
8410006354cSpeter klausler // File offset of previous record's newline
8420006354cSpeter klausler auto prevNL{
8430006354cSpeter klausler frameOffsetInFile_ + static_cast<std::int64_t>(recordOffsetInFrame_) - 1};
8440006354cSpeter klausler if (prevNL < 0) {
8450006354cSpeter klausler handler.SignalError(IostatBackspaceAtFirstRecord);
8460006354cSpeter klausler return;
8470006354cSpeter klausler }
8483b635714Speter klausler while (true) {
8490006354cSpeter klausler if (frameOffsetInFile_ < prevNL) {
8503b635714Speter klausler if (const char *p{
8510006354cSpeter klausler FindLastNewline(Frame(), prevNL - 1 - frameOffsetInFile_)}) {
8523b635714Speter klausler recordOffsetInFrame_ = p - Frame() + 1;
853cbad5761SMichael Kruse recordLength = prevNL - (frameOffsetInFile_ + recordOffsetInFrame_);
8543b635714Speter klausler break;
8553b635714Speter klausler }
8563b635714Speter klausler }
8570006354cSpeter klausler if (frameOffsetInFile_ == 0) {
8580006354cSpeter klausler recordOffsetInFrame_ = 0;
859cbad5761SMichael Kruse recordLength = prevNL;
8600006354cSpeter klausler break;
8613b635714Speter klausler }
8620006354cSpeter klausler frameOffsetInFile_ -= std::min<std::int64_t>(frameOffsetInFile_, 1024);
8630006354cSpeter klausler auto need{static_cast<std::size_t>(prevNL + 1 - frameOffsetInFile_)};
8640006354cSpeter klausler auto got{ReadFrame(frameOffsetInFile_, need, handler)};
865461b6fe4SPeter Klausler if (got < need) {
866461b6fe4SPeter Klausler handler.SignalError(IostatShortRead);
867461b6fe4SPeter Klausler return;
8683b635714Speter klausler }
869461b6fe4SPeter Klausler }
870461b6fe4SPeter Klausler if (Frame()[recordOffsetInFrame_ + *recordLength] != '\n') {
871461b6fe4SPeter Klausler handler.SignalError(IostatMissingTerminator);
872461b6fe4SPeter Klausler return;
873461b6fe4SPeter Klausler }
8743b635714Speter klausler if (*recordLength > 0 &&
8753b635714Speter klausler Frame()[recordOffsetInFrame_ + *recordLength - 1] == '\r') {
8763b635714Speter klausler --*recordLength;
8773b635714Speter klausler }
878f7be2518Speter klausler }
8790006354cSpeter klausler
DoImpliedEndfile(IoErrorHandler & handler)8800006354cSpeter klausler void ExternalFileUnit::DoImpliedEndfile(IoErrorHandler &handler) {
8810006354cSpeter klausler if (impliedEndfile_) {
8820006354cSpeter klausler impliedEndfile_ = false;
883991696c2SPeter Klausler if (access != Access::Direct && IsRecordFile() && mayPosition()) {
8840006354cSpeter klausler DoEndfile(handler);
8850006354cSpeter klausler }
8860006354cSpeter klausler }
8870006354cSpeter klausler }
8880006354cSpeter klausler
DoEndfile(IoErrorHandler & handler)8890006354cSpeter klausler void ExternalFileUnit::DoEndfile(IoErrorHandler &handler) {
89036771bbaSPeter Klausler if (IsRecordFile() && access != Access::Direct) {
8910508fd59SPeter Klausler furthestPositionInRecord =
8920508fd59SPeter Klausler std::max(positionInRecord, furthestPositionInRecord);
89336771bbaSPeter Klausler if (furthestPositionInRecord > 0) {
8940508fd59SPeter Klausler // Last read/write was non-advancing, so AdvanceRecord() was not called.
89536771bbaSPeter Klausler leftTabLimit.reset();
89636771bbaSPeter Klausler ++currentRecordNumber;
89736771bbaSPeter Klausler }
8980006354cSpeter klausler endfileRecordNumber = currentRecordNumber;
899991696c2SPeter Klausler }
9000508fd59SPeter Klausler frameOffsetInFile_ += recordOffsetInFrame_ + furthestPositionInRecord;
9010508fd59SPeter Klausler recordOffsetInFrame_ = 0;
9021a65d09dSPeter Klausler FlushOutput(handler);
9030508fd59SPeter Klausler Truncate(frameOffsetInFile_, handler);
9041a65d09dSPeter Klausler TruncateFrame(frameOffsetInFile_, handler);
9050006354cSpeter klausler BeginRecord();
9060006354cSpeter klausler impliedEndfile_ = false;
9070006354cSpeter klausler }
90843fadefbSpeter klausler
CommitWrites()909cd0a1226Speter klausler void ExternalFileUnit::CommitWrites() {
910cd0a1226Speter klausler frameOffsetInFile_ +=
911cd0a1226Speter klausler recordOffsetInFrame_ + recordLength.value_or(furthestPositionInRecord);
912cd0a1226Speter klausler recordOffsetInFrame_ = 0;
913cd0a1226Speter klausler BeginRecord();
914cd0a1226Speter klausler }
915cd0a1226Speter klausler
CheckDirectAccess(IoErrorHandler & handler)916507f7317SPeter Klausler bool ExternalFileUnit::CheckDirectAccess(IoErrorHandler &handler) {
917507f7317SPeter Klausler if (access == Access::Direct) {
918507f7317SPeter Klausler RUNTIME_CHECK(handler, openRecl);
919507f7317SPeter Klausler if (!directAccessRecWasSet_) {
920e3550f19SPeter Steinfeld handler.SignalError(
921e3550f19SPeter Steinfeld "No REC= was specified for a data transfer with ACCESS='DIRECT'");
922507f7317SPeter Klausler return false;
923507f7317SPeter Klausler }
924507f7317SPeter Klausler }
925507f7317SPeter Klausler return true;
926507f7317SPeter Klausler }
927507f7317SPeter Klausler
HitEndOnRead(IoErrorHandler & handler)9288527f9e4SPeter Klausler void ExternalFileUnit::HitEndOnRead(IoErrorHandler &handler) {
9298527f9e4SPeter Klausler handler.SignalEnd();
9308527f9e4SPeter Klausler if (IsRecordFile() && access != Access::Direct) {
9318527f9e4SPeter Klausler endfileRecordNumber = currentRecordNumber;
9328527f9e4SPeter Klausler }
9338527f9e4SPeter Klausler }
9348527f9e4SPeter Klausler
PushChildIo(IoStatementState & parent)93543fadefbSpeter klausler ChildIo &ExternalFileUnit::PushChildIo(IoStatementState &parent) {
93643fadefbSpeter klausler OwningPtr<ChildIo> current{std::move(child_)};
93743fadefbSpeter klausler Terminator &terminator{parent.GetIoErrorHandler()};
93843fadefbSpeter klausler OwningPtr<ChildIo> next{New<ChildIo>{terminator}(parent, std::move(current))};
93943fadefbSpeter klausler child_.reset(next.release());
94043fadefbSpeter klausler return *child_;
94143fadefbSpeter klausler }
94243fadefbSpeter klausler
PopChildIo(ChildIo & child)94343fadefbSpeter klausler void ExternalFileUnit::PopChildIo(ChildIo &child) {
94443fadefbSpeter klausler if (child_.get() != &child) {
94543fadefbSpeter klausler child.parent().GetIoErrorHandler().Crash(
94643fadefbSpeter klausler "ChildIo being popped is not top of stack");
94743fadefbSpeter klausler }
94843fadefbSpeter klausler child_.reset(child.AcquirePrevious().release()); // deletes top child
94943fadefbSpeter klausler }
95043fadefbSpeter klausler
GetAsynchronousId(IoErrorHandler & handler)951166d6ed5SPeter Klausler int ExternalFileUnit::GetAsynchronousId(IoErrorHandler &handler) {
952166d6ed5SPeter Klausler if (!mayAsynchronous()) {
953166d6ed5SPeter Klausler handler.SignalError(IostatBadAsynchronous);
954166d6ed5SPeter Klausler return -1;
955166d6ed5SPeter Klausler } else if (auto least{asyncIdAvailable_.LeastElement()}) {
956166d6ed5SPeter Klausler asyncIdAvailable_.reset(*least);
957166d6ed5SPeter Klausler return static_cast<int>(*least);
958166d6ed5SPeter Klausler } else {
959166d6ed5SPeter Klausler handler.SignalError(IostatTooManyAsyncOps);
960166d6ed5SPeter Klausler return -1;
961166d6ed5SPeter Klausler }
962166d6ed5SPeter Klausler }
963166d6ed5SPeter Klausler
Wait(int id)964166d6ed5SPeter Klausler bool ExternalFileUnit::Wait(int id) {
96517853928SPeter Klausler if (static_cast<std::size_t>(id) >= asyncIdAvailable_.size() ||
96617853928SPeter Klausler asyncIdAvailable_.test(id)) {
967166d6ed5SPeter Klausler return false;
968166d6ed5SPeter Klausler } else {
96917853928SPeter Klausler if (id == 0) { // means "all IDs"
970166d6ed5SPeter Klausler asyncIdAvailable_.set();
97117853928SPeter Klausler asyncIdAvailable_.reset(0);
972166d6ed5SPeter Klausler } else {
973166d6ed5SPeter Klausler asyncIdAvailable_.set(id);
974166d6ed5SPeter Klausler }
975166d6ed5SPeter Klausler return true;
976166d6ed5SPeter Klausler }
977166d6ed5SPeter Klausler }
978166d6ed5SPeter Klausler
EndIoStatement()97943fadefbSpeter klausler void ChildIo::EndIoStatement() {
98043fadefbSpeter klausler io_.reset();
98143fadefbSpeter klausler u_.emplace<std::monostate>();
98243fadefbSpeter klausler }
98343fadefbSpeter klausler
CheckFormattingAndDirection(bool unformatted,Direction direction)984df38f35aSPeter Klausler Iostat ChildIo::CheckFormattingAndDirection(
985df38f35aSPeter Klausler bool unformatted, Direction direction) {
98643fadefbSpeter klausler bool parentIsInput{!parent_.get_if<IoDirectionState<Direction::Output>>()};
9874393e377Speter klausler bool parentIsFormatted{parentIsInput
9884393e377Speter klausler ? parent_.get_if<FormattedIoStatementState<Direction::Input>>() !=
9894393e377Speter klausler nullptr
9904393e377Speter klausler : parent_.get_if<FormattedIoStatementState<Direction::Output>>() !=
9914393e377Speter klausler nullptr};
9924393e377Speter klausler bool parentIsUnformatted{!parentIsFormatted};
99343fadefbSpeter klausler if (unformatted != parentIsUnformatted) {
994df38f35aSPeter Klausler return unformatted ? IostatUnformattedChildOnFormattedParent
995df38f35aSPeter Klausler : IostatFormattedChildOnUnformattedParent;
99643fadefbSpeter klausler } else if (parentIsInput != (direction == Direction::Input)) {
997df38f35aSPeter Klausler return parentIsInput ? IostatChildOutputToInputParent
998df38f35aSPeter Klausler : IostatChildInputFromOutputParent;
99943fadefbSpeter klausler } else {
1000df38f35aSPeter Klausler return IostatOk;
100143fadefbSpeter klausler }
100243fadefbSpeter klausler }
100343fadefbSpeter klausler
10041f879005STim Keith } // namespace Fortran::runtime::io
1005