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 = &in;
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