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 //===----------------------------------------------------------------------===// 800f3454bSSlava Zakharin // 900f3454bSSlava Zakharin // Implementation of ExternalFileUnit common for both 1000f3454bSSlava Zakharin // RT_USE_PSEUDO_FILE_UNIT=0 and RT_USE_PSEUDO_FILE_UNIT=1. 1100f3454bSSlava Zakharin // 1200f3454bSSlava Zakharin //===----------------------------------------------------------------------===// 13f7be2518Speter klausler #include "unit.h" 143b635714Speter klausler #include "io-error.h" 15f7be2518Speter klausler #include "lock.h" 1652a0b02cSPeter Klausler #include "tools.h" 170f140ce3SThorsten Schütt #include <limits> 188f2c5c43Speter klausler #include <utility> 19f7be2518Speter klausler 20f7be2518Speter klausler namespace Fortran::runtime::io { 21f7be2518Speter klausler 221563a875SSlava Zakharin #ifndef FLANG_RUNTIME_NO_GLOBAL_VAR_DEFS 238ebf7411SSlava Zakharin RT_OFFLOAD_VAR_GROUP_BEGIN 248ebf7411SSlava Zakharin RT_VAR_ATTRS ExternalFileUnit *defaultInput{nullptr}; // unit 5 258ebf7411SSlava Zakharin RT_VAR_ATTRS ExternalFileUnit *defaultOutput{nullptr}; // unit 6 268ebf7411SSlava Zakharin RT_VAR_ATTRS ExternalFileUnit *errorOutput{nullptr}; // unit 0 extension 278ebf7411SSlava Zakharin RT_OFFLOAD_VAR_GROUP_END 281563a875SSlava Zakharin #endif // FLANG_RUNTIME_NO_GLOBAL_VAR_DEFS 290006354cSpeter klausler 308ebf7411SSlava Zakharin RT_OFFLOAD_API_GROUP_BEGIN 318ebf7411SSlava Zakharin 328ebf7411SSlava Zakharin static inline RT_API_ATTRS void SwapEndianness( 338f2c5c43Speter klausler char *data, std::size_t bytes, std::size_t elementBytes) { 348f2c5c43Speter klausler if (elementBytes > 1) { 358f2c5c43Speter klausler auto half{elementBytes >> 1}; 368f2c5c43Speter klausler for (std::size_t j{0}; j + elementBytes <= bytes; j += elementBytes) { 378f2c5c43Speter klausler for (std::size_t k{0}; k < half; ++k) { 388ebf7411SSlava Zakharin RT_DIAG_PUSH 398ebf7411SSlava Zakharin RT_DIAG_DISABLE_CALL_HOST_FROM_DEVICE_WARN 408f2c5c43Speter klausler std::swap(data[j + k], data[j + elementBytes - 1 - k]); 418ebf7411SSlava Zakharin RT_DIAG_POP 428f2c5c43Speter klausler } 438f2c5c43Speter klausler } 448f2c5c43Speter klausler } 458f2c5c43Speter klausler } 468f2c5c43Speter klausler 478f2c5c43Speter klausler bool ExternalFileUnit::Emit(const char *data, std::size_t bytes, 488f2c5c43Speter klausler std::size_t elementBytes, IoErrorHandler &handler) { 4995696d56Speter klausler auto furthestAfter{std::max(furthestPositionInRecord, 5095696d56Speter klausler positionInRecord + static_cast<std::int64_t>(bytes))}; 5106ca9f24SPeter Klausler if (openRecl) { 5206ca9f24SPeter Klausler // Check for fixed-length record overrun, but allow for 53f651bbeaSPeter Klausler // sequential record termination. 54f651bbeaSPeter Klausler int extra{0}; 55f651bbeaSPeter Klausler int header{0}; 56f651bbeaSPeter Klausler if (access == Access::Sequential) { 57f651bbeaSPeter Klausler if (isUnformatted.value_or(false)) { 58f651bbeaSPeter Klausler // record header + footer 59f651bbeaSPeter Klausler header = static_cast<int>(sizeof(std::uint32_t)); 60f651bbeaSPeter Klausler extra = 2 * header; 61f651bbeaSPeter Klausler } else { 62991696c2SPeter Klausler #ifdef _WIN32 63991696c2SPeter Klausler if (!isWindowsTextFile()) { 64991696c2SPeter Klausler ++extra; // carriage return (CR) 65991696c2SPeter Klausler } 66991696c2SPeter Klausler #endif 67991696c2SPeter Klausler ++extra; // newline (LF) 68f651bbeaSPeter Klausler } 69f651bbeaSPeter Klausler } 70f651bbeaSPeter Klausler if (furthestAfter > extra + *openRecl) { 710006354cSpeter klausler handler.SignalError(IostatRecordWriteOverrun, 72b232a88cSpeter klausler "Attempt to write %zd bytes to position %jd in a fixed-size record " 73b232a88cSpeter klausler "of %jd bytes", 74f651bbeaSPeter Klausler bytes, static_cast<std::intmax_t>(positionInRecord - header), 7506ca9f24SPeter Klausler static_cast<std::intmax_t>(*openRecl)); 763b635714Speter klausler return false; 773b635714Speter klausler } 78ef7f6f7cSPeter Klausler } 79ef7f6f7cSPeter Klausler if (recordLength) { 8006ca9f24SPeter Klausler // It is possible for recordLength to have a value now for a 8106ca9f24SPeter Klausler // variable-length output record if the previous operation 8206ca9f24SPeter Klausler // was a BACKSPACE or non advancing input statement. 8306ca9f24SPeter Klausler recordLength.reset(); 8406ca9f24SPeter Klausler beganReadingRecord_ = false; 85b232a88cSpeter klausler } 86ac4202feSPeter Klausler if (IsAfterEndfile()) { 87ac4202feSPeter Klausler handler.SignalError(IostatWriteAfterEndfile); 88ac4202feSPeter Klausler return false; 89ac4202feSPeter Klausler } 90507f7317SPeter Klausler CheckDirectAccess(handler); 913b635714Speter klausler WriteFrame(frameOffsetInFile_, recordOffsetInFrame_ + furthestAfter, handler); 9227505565Speter klausler if (positionInRecord > furthestPositionInRecord) { 930006354cSpeter klausler std::memset(Frame() + recordOffsetInFrame_ + furthestPositionInRecord, ' ', 9427505565Speter klausler positionInRecord - furthestPositionInRecord); 9527505565Speter klausler } 968f2c5c43Speter klausler char *to{Frame() + recordOffsetInFrame_ + positionInRecord}; 978f2c5c43Speter klausler std::memcpy(to, data, bytes); 988f2c5c43Speter klausler if (swapEndianness_) { 998f2c5c43Speter klausler SwapEndianness(to, bytes, elementBytes); 1008f2c5c43Speter klausler } 101f7be2518Speter klausler positionInRecord += bytes; 102f7be2518Speter klausler furthestPositionInRecord = furthestAfter; 103c94f7804SPeter Klausler anyWriteSinceLastPositioning_ = true; 104f7be2518Speter klausler return true; 105f7be2518Speter klausler } 106f7be2518Speter klausler 1078f2c5c43Speter klausler bool ExternalFileUnit::Receive(char *data, std::size_t bytes, 1088f2c5c43Speter klausler std::size_t elementBytes, IoErrorHandler &handler) { 1090006354cSpeter klausler RUNTIME_CHECK(handler, direction_ == Direction::Input); 1100006354cSpeter klausler auto furthestAfter{std::max(furthestPositionInRecord, 1110006354cSpeter klausler positionInRecord + static_cast<std::int64_t>(bytes))}; 1120006354cSpeter klausler if (furthestAfter > recordLength.value_or(furthestAfter)) { 1130006354cSpeter klausler handler.SignalError(IostatRecordReadOverrun, 1140006354cSpeter klausler "Attempt to read %zd bytes at position %jd in a record of %jd bytes", 1150006354cSpeter klausler bytes, static_cast<std::intmax_t>(positionInRecord), 1160006354cSpeter klausler static_cast<std::intmax_t>(*recordLength)); 1170006354cSpeter klausler return false; 1180006354cSpeter klausler } 1190006354cSpeter klausler auto need{recordOffsetInFrame_ + furthestAfter}; 1200006354cSpeter klausler auto got{ReadFrame(frameOffsetInFile_, need, handler)}; 1210006354cSpeter klausler if (got >= need) { 1220006354cSpeter klausler std::memcpy(data, Frame() + recordOffsetInFrame_ + positionInRecord, bytes); 1238f2c5c43Speter klausler if (swapEndianness_) { 1248f2c5c43Speter klausler SwapEndianness(data, bytes, elementBytes); 1258f2c5c43Speter klausler } 1260006354cSpeter klausler positionInRecord += bytes; 1270006354cSpeter klausler furthestPositionInRecord = furthestAfter; 1280006354cSpeter klausler return true; 1290006354cSpeter klausler } else { 1308527f9e4SPeter Klausler HitEndOnRead(handler); 1310006354cSpeter klausler return false; 1320006354cSpeter klausler } 1330006354cSpeter klausler } 1340006354cSpeter klausler 135da25f968SPeter Klausler std::size_t ExternalFileUnit::GetNextInputBytes( 136da25f968SPeter Klausler const char *&p, IoErrorHandler &handler) { 137da25f968SPeter Klausler RUNTIME_CHECK(handler, direction_ == Direction::Input); 138991696c2SPeter Klausler std::size_t length{1}; 139991696c2SPeter Klausler if (auto recl{EffectiveRecordLength()}) { 140991696c2SPeter Klausler if (positionInRecord < *recl) { 141991696c2SPeter Klausler length = *recl - positionInRecord; 142991696c2SPeter Klausler } else { 143991696c2SPeter Klausler p = nullptr; 144991696c2SPeter Klausler return 0; 145991696c2SPeter Klausler } 146991696c2SPeter Klausler } 147991696c2SPeter Klausler p = FrameNextInput(handler, length); 148991696c2SPeter Klausler return p ? length : 0; 149da25f968SPeter Klausler } 150da25f968SPeter Klausler 151*c2a95ad2SPeter Klausler std::size_t ExternalFileUnit::ViewBytesInRecord( 152*c2a95ad2SPeter Klausler const char *&p, bool forward) const { 153*c2a95ad2SPeter Klausler p = nullptr; 154*c2a95ad2SPeter Klausler auto recl{recordLength.value_or(positionInRecord)}; 155*c2a95ad2SPeter Klausler if (forward) { 156*c2a95ad2SPeter Klausler if (positionInRecord < recl) { 157*c2a95ad2SPeter Klausler p = Frame() + recordOffsetInFrame_ + positionInRecord; 158*c2a95ad2SPeter Klausler return recl - positionInRecord; 159*c2a95ad2SPeter Klausler } 160*c2a95ad2SPeter Klausler } else { 161*c2a95ad2SPeter Klausler if (positionInRecord <= recl) { 162*c2a95ad2SPeter Klausler p = Frame() + recordOffsetInFrame_ + positionInRecord; 163*c2a95ad2SPeter Klausler } 164*c2a95ad2SPeter Klausler return positionInRecord - leftTabLimit.value_or(0); 165*c2a95ad2SPeter Klausler } 166*c2a95ad2SPeter Klausler return 0; 167*c2a95ad2SPeter Klausler } 168*c2a95ad2SPeter Klausler 1690006354cSpeter klausler const char *ExternalFileUnit::FrameNextInput( 1700006354cSpeter klausler IoErrorHandler &handler, std::size_t bytes) { 171199a623eSpeter klausler RUNTIME_CHECK(handler, isUnformatted.has_value() && !*isUnformatted); 1720006354cSpeter klausler if (static_cast<std::int64_t>(positionInRecord + bytes) <= 1730006354cSpeter klausler recordLength.value_or(positionInRecord + bytes)) { 1740006354cSpeter klausler auto at{recordOffsetInFrame_ + positionInRecord}; 1750006354cSpeter klausler auto need{static_cast<std::size_t>(at + bytes)}; 1760006354cSpeter klausler auto got{ReadFrame(frameOffsetInFile_, need, handler)}; 177991696c2SPeter Klausler SetVariableFormattedRecordLength(); 1780006354cSpeter klausler if (got >= need) { 1790006354cSpeter klausler return Frame() + at; 1800006354cSpeter klausler } 1818527f9e4SPeter Klausler HitEndOnRead(handler); 182991696c2SPeter Klausler } 1830006354cSpeter klausler return nullptr; 1840006354cSpeter klausler } 1850006354cSpeter klausler 186991696c2SPeter Klausler bool ExternalFileUnit::SetVariableFormattedRecordLength() { 187991696c2SPeter Klausler if (recordLength || access == Access::Direct) { 1880006354cSpeter klausler return true; 189b43e083bSpeter klausler } else if (FrameLength() > recordOffsetInFrame_) { 1900006354cSpeter klausler const char *record{Frame() + recordOffsetInFrame_}; 191b43e083bSpeter klausler std::size_t bytes{FrameLength() - recordOffsetInFrame_}; 19252a0b02cSPeter Klausler if (const char *nl{FindCharacter(record, '\n', bytes)}) { 1930006354cSpeter klausler recordLength = nl - record; 1940006354cSpeter klausler if (*recordLength > 0 && record[*recordLength - 1] == '\r') { 1950006354cSpeter klausler --*recordLength; 1960006354cSpeter klausler } 1970006354cSpeter klausler return true; 1980006354cSpeter klausler } 199b43e083bSpeter klausler } 200dfea011aSpeter klausler return false; 201dfea011aSpeter klausler } 2023b635714Speter klausler 203e81c96d6Speter klausler bool ExternalFileUnit::BeginReadingRecord(IoErrorHandler &handler) { 2040006354cSpeter klausler RUNTIME_CHECK(handler, direction_ == Direction::Input); 205e81c96d6Speter klausler if (!beganReadingRecord_) { 206e24f0ac7Speter klausler beganReadingRecord_ = true; 207bf1c89c3SPeter Klausler // Don't use IsAtEOF() to check for an EOF condition here, just detect 208bf1c89c3SPeter Klausler // it from a failed or short read from the file. IsAtEOF() could be 209bf1c89c3SPeter Klausler // wrong for formatted input if actual newline characters had been 210bf1c89c3SPeter Klausler // written in-band by previous WRITEs before a REWIND. In fact, 211bf1c89c3SPeter Klausler // now that we know that the unit is being used for input (again), 212bf1c89c3SPeter Klausler // it's best to reset endfileRecordNumber and ensure IsAtEOF() will 213bf1c89c3SPeter Klausler // now be true on return only if it gets set by HitEndOnRead(). 214bf1c89c3SPeter Klausler endfileRecordNumber.reset(); 21506ca9f24SPeter Klausler if (access == Access::Direct) { 216507f7317SPeter Klausler CheckDirectAccess(handler); 21706ca9f24SPeter Klausler auto need{static_cast<std::size_t>(recordOffsetInFrame_ + *openRecl)}; 2180006354cSpeter klausler auto got{ReadFrame(frameOffsetInFile_, need, handler)}; 21906ca9f24SPeter Klausler if (got >= need) { 22006ca9f24SPeter Klausler recordLength = openRecl; 22106ca9f24SPeter Klausler } else { 22206ca9f24SPeter Klausler recordLength.reset(); 2238527f9e4SPeter Klausler HitEndOnRead(handler); 2240006354cSpeter klausler } 225991696c2SPeter Klausler } else { 226c94f7804SPeter Klausler if (anyWriteSinceLastPositioning_ && access == Access::Sequential) { 227c94f7804SPeter Klausler // Most Fortran implementations allow a READ after a WRITE; 228c94f7804SPeter Klausler // the read then just hits an EOF. 2292b86fb21SSlava Zakharin DoEndfile<false, Direction::Input>(handler); 230c94f7804SPeter Klausler } 23106ca9f24SPeter Klausler recordLength.reset(); 232199a623eSpeter klausler RUNTIME_CHECK(handler, isUnformatted.has_value()); 233991696c2SPeter Klausler if (*isUnformatted) { 234991696c2SPeter Klausler if (access == Access::Sequential) { 2350006354cSpeter klausler BeginSequentialVariableUnformattedInputRecord(handler); 236991696c2SPeter Klausler } 237991696c2SPeter Klausler } else { // formatted sequential or stream 238991696c2SPeter Klausler BeginVariableFormattedInputRecord(handler); 2390006354cSpeter klausler } 2400006354cSpeter klausler } 2410006354cSpeter klausler } 242e81c96d6Speter klausler RUNTIME_CHECK(handler, 243991696c2SPeter Klausler recordLength.has_value() || !IsRecordFile() || handler.InError()); 244e81c96d6Speter klausler return !handler.InError(); 245e81c96d6Speter klausler } 2460006354cSpeter klausler 247e24f0ac7Speter klausler void ExternalFileUnit::FinishReadingRecord(IoErrorHandler &handler) { 248e24f0ac7Speter klausler RUNTIME_CHECK(handler, direction_ == Direction::Input && beganReadingRecord_); 249e24f0ac7Speter klausler beganReadingRecord_ = false; 2506ce0fba0SPeter Klausler if (handler.GetIoStat() == IostatEnd || 2516ce0fba0SPeter Klausler (IsRecordFile() && !recordLength.has_value())) { 252991696c2SPeter Klausler // Avoid bogus crashes in END/ERR circumstances; but 253991696c2SPeter Klausler // still increment the current record number so that 254991696c2SPeter Klausler // an attempted read of an endfile record, followed by 255991696c2SPeter Klausler // a BACKSPACE, will still be at EOF. 256991696c2SPeter Klausler ++currentRecordNumber; 257991696c2SPeter Klausler } else if (IsRecordFile()) { 258e7823608SPeter Klausler recordOffsetInFrame_ += *recordLength; 259991696c2SPeter Klausler if (access != Access::Direct) { 260199a623eSpeter klausler RUNTIME_CHECK(handler, isUnformatted.has_value()); 261e7823608SPeter Klausler recordLength.reset(); 262199a623eSpeter klausler if (isUnformatted.value_or(false)) { 2630006354cSpeter klausler // Retain footer in frame for more efficient BACKSPACE 264e7823608SPeter Klausler frameOffsetInFile_ += recordOffsetInFrame_; 2650006354cSpeter klausler recordOffsetInFrame_ = sizeof(std::uint32_t); 2660006354cSpeter klausler } else { // formatted 267e7823608SPeter Klausler if (FrameLength() > recordOffsetInFrame_ && 268e7823608SPeter Klausler Frame()[recordOffsetInFrame_] == '\r') { 2690006354cSpeter klausler ++recordOffsetInFrame_; 2700006354cSpeter klausler } 271991696c2SPeter Klausler if (FrameLength() > recordOffsetInFrame_ && 272e7823608SPeter Klausler Frame()[recordOffsetInFrame_] == '\n') { 273b43e083bSpeter klausler ++recordOffsetInFrame_; 274b43e083bSpeter klausler } 275e7823608SPeter Klausler if (!pinnedFrame || mayPosition()) { 276e7823608SPeter Klausler frameOffsetInFile_ += recordOffsetInFrame_; 277dfea011aSpeter klausler recordOffsetInFrame_ = 0; 278dfea011aSpeter klausler } 279f7be2518Speter klausler } 2803b635714Speter klausler } 2814e53e283SAndrzej Warzynski ++currentRecordNumber; 282991696c2SPeter Klausler } else { // unformatted stream 283991696c2SPeter Klausler furthestPositionInRecord = 284991696c2SPeter Klausler std::max(furthestPositionInRecord, positionInRecord); 285991696c2SPeter Klausler frameOffsetInFile_ += recordOffsetInFrame_ + furthestPositionInRecord; 286975579bdSPeter Klausler recordOffsetInFrame_ = 0; 287991696c2SPeter Klausler } 288e24f0ac7Speter klausler BeginRecord(); 28920278570SPeter Klausler leftTabLimit.reset(); 290e24f0ac7Speter klausler } 291e24f0ac7Speter klausler 292e24f0ac7Speter klausler bool ExternalFileUnit::AdvanceRecord(IoErrorHandler &handler) { 293e24f0ac7Speter klausler if (direction_ == Direction::Input) { 294e24f0ac7Speter klausler FinishReadingRecord(handler); 295e81c96d6Speter klausler return BeginReadingRecord(handler); 2960006354cSpeter klausler } else { // Direction::Output 297e81c96d6Speter klausler bool ok{true}; 298199a623eSpeter klausler RUNTIME_CHECK(handler, isUnformatted.has_value()); 299991696c2SPeter Klausler positionInRecord = furthestPositionInRecord; 300991696c2SPeter Klausler if (access == Access::Direct) { 301991696c2SPeter Klausler if (furthestPositionInRecord < 302991696c2SPeter Klausler openRecl.value_or(furthestPositionInRecord)) { 303a94d943fSpeter klausler // Pad remainder of fixed length record 304f651bbeaSPeter Klausler WriteFrame( 305f651bbeaSPeter Klausler frameOffsetInFile_, recordOffsetInFrame_ + *openRecl, handler); 3063b635714Speter klausler std::memset(Frame() + recordOffsetInFrame_ + furthestPositionInRecord, 307199a623eSpeter klausler isUnformatted.value_or(false) ? 0 : ' ', 30806ca9f24SPeter Klausler *openRecl - furthestPositionInRecord); 30906ca9f24SPeter Klausler furthestPositionInRecord = *openRecl; 3103b635714Speter klausler } 311991696c2SPeter Klausler } else if (*isUnformatted) { 312991696c2SPeter Klausler if (access == Access::Sequential) { 313a94d943fSpeter klausler // Append the length of a sequential unformatted variable-length record 314a94d943fSpeter klausler // as its footer, then overwrite the reserved first four bytes of the 315a94d943fSpeter klausler // record with its length as its header. These four bytes were skipped 316a94d943fSpeter klausler // over in BeginUnformattedIO<Output>(). 317a94d943fSpeter klausler // TODO: Break very large records up into subrecords with negative 318a94d943fSpeter klausler // headers &/or footers 319a94d943fSpeter klausler std::uint32_t length; 320a94d943fSpeter klausler length = furthestPositionInRecord - sizeof length; 3216a1c3efaSpeter klausler ok = ok && 3226a1c3efaSpeter klausler Emit(reinterpret_cast<const char *>(&length), sizeof length, 323a94d943fSpeter klausler sizeof length, handler); 324a94d943fSpeter klausler positionInRecord = 0; 3256a1c3efaSpeter klausler ok = ok && 3266a1c3efaSpeter klausler Emit(reinterpret_cast<const char *>(&length), sizeof length, 327a94d943fSpeter klausler sizeof length, handler); 328a94d943fSpeter klausler } else { 329991696c2SPeter Klausler // Unformatted stream: nothing to do 3303b635714Speter klausler } 3311fe7a187SPeter Klausler } else if (handler.GetIoStat() != IostatOk && 3321fe7a187SPeter Klausler furthestPositionInRecord == 0) { 3331fe7a187SPeter Klausler // Error in formatted variable length record, and no output yet; do 3341fe7a187SPeter Klausler // nothing, like most other Fortran compilers do. 3351fe7a187SPeter Klausler return true; 336991696c2SPeter Klausler } else { 337991696c2SPeter Klausler // Terminate formatted variable length record 338991696c2SPeter Klausler const char *lineEnding{"\n"}; 339991696c2SPeter Klausler std::size_t lineEndingBytes{1}; 340991696c2SPeter Klausler #ifdef _WIN32 341991696c2SPeter Klausler if (!isWindowsTextFile()) { 342991696c2SPeter Klausler lineEnding = "\r\n"; 343991696c2SPeter Klausler lineEndingBytes = 2; 344991696c2SPeter Klausler } 345991696c2SPeter Klausler #endif 346991696c2SPeter Klausler ok = ok && Emit(lineEnding, lineEndingBytes, 1, handler); 3474e53e283SAndrzej Warzynski } 34836771bbaSPeter Klausler leftTabLimit.reset(); 349ac4202feSPeter Klausler if (IsAfterEndfile()) { 350ac4202feSPeter Klausler return false; 351ac4202feSPeter Klausler } 352cd0a1226Speter klausler CommitWrites(); 353f7be2518Speter klausler ++currentRecordNumber; 354991696c2SPeter Klausler if (access != Access::Direct) { 355991696c2SPeter Klausler impliedEndfile_ = IsRecordFile(); 356ac4202feSPeter Klausler if (IsAtEOF()) { 3574d42e16eSpeter klausler endfileRecordNumber.reset(); 3584d42e16eSpeter klausler } 359991696c2SPeter Klausler } 360f7be2518Speter klausler return ok; 361f7be2518Speter klausler } 362e81c96d6Speter klausler } 363f7be2518Speter klausler 3643b635714Speter klausler void ExternalFileUnit::BackspaceRecord(IoErrorHandler &handler) { 365991696c2SPeter Klausler if (access == Access::Direct || !IsRecordFile()) { 3660006354cSpeter klausler handler.SignalError(IostatBackspaceNonSequential, 367991696c2SPeter Klausler "BACKSPACE(UNIT=%d) on direct-access file or unformatted stream", 368991696c2SPeter Klausler unitNumber()); 3693b635714Speter klausler } else { 370ac4202feSPeter Klausler if (IsAfterEndfile()) { 3714d42e16eSpeter klausler // BACKSPACE after explicit ENDFILE 3724d42e16eSpeter klausler currentRecordNumber = *endfileRecordNumber; 373b6260538SPeter Klausler } else if (leftTabLimit && direction_ == Direction::Input) { 374b6260538SPeter Klausler // BACKSPACE after non-advancing input 37572831a59SPeter Klausler leftTabLimit.reset(); 376e29c9d77Speter klausler } else { 3770006354cSpeter klausler DoImpliedEndfile(handler); 378e29c9d77Speter klausler if (frameOffsetInFile_ + recordOffsetInFrame_ > 0) { 3790006354cSpeter klausler --currentRecordNumber; 38006ca9f24SPeter Klausler if (openRecl && access == Access::Direct) { 3810006354cSpeter klausler BackspaceFixedRecord(handler); 382199a623eSpeter klausler } else { 383199a623eSpeter klausler RUNTIME_CHECK(handler, isUnformatted.has_value()); 384199a623eSpeter klausler if (isUnformatted.value_or(false)) { 3850006354cSpeter klausler BackspaceVariableUnformattedRecord(handler); 3863b635714Speter klausler } else { 3870006354cSpeter klausler BackspaceVariableFormattedRecord(handler); 3883b635714Speter klausler } 3890006354cSpeter klausler } 390f7be2518Speter klausler } 391199a623eSpeter klausler } 392e29c9d77Speter klausler BeginRecord(); 393c94f7804SPeter Klausler anyWriteSinceLastPositioning_ = false; 394e29c9d77Speter klausler } 395e29c9d77Speter klausler } 396f7be2518Speter klausler 397cd0a1226Speter klausler void ExternalFileUnit::FlushOutput(IoErrorHandler &handler) { 398cd0a1226Speter klausler if (!mayPosition()) { 399cd0a1226Speter klausler auto frameAt{FrameAt()}; 400cd0a1226Speter klausler if (frameOffsetInFile_ >= frameAt && 401cd0a1226Speter klausler frameOffsetInFile_ < 402cd0a1226Speter klausler static_cast<std::int64_t>(frameAt + FrameLength())) { 403cd0a1226Speter klausler // A Flush() that's about to happen to a non-positionable file 404cd0a1226Speter klausler // needs to advance frameOffsetInFile_ to prevent attempts at 405cd0a1226Speter klausler // impossible seeks 406cd0a1226Speter klausler CommitWrites(); 40752a0b02cSPeter Klausler leftTabLimit.reset(); 408cd0a1226Speter klausler } 409cd0a1226Speter klausler } 410cd0a1226Speter klausler Flush(handler); 411cd0a1226Speter klausler } 412cd0a1226Speter klausler 41395696d56Speter klausler void ExternalFileUnit::FlushIfTerminal(IoErrorHandler &handler) { 41495696d56Speter klausler if (isTerminal()) { 415cd0a1226Speter klausler FlushOutput(handler); 41695696d56Speter klausler } 41795696d56Speter klausler } 41895696d56Speter klausler 4190006354cSpeter klausler void ExternalFileUnit::Endfile(IoErrorHandler &handler) { 420991696c2SPeter Klausler if (access == Access::Direct) { 421991696c2SPeter Klausler handler.SignalError(IostatEndfileDirect, 422991696c2SPeter Klausler "ENDFILE(UNIT=%d) on direct-access file", unitNumber()); 4230006354cSpeter klausler } else if (!mayWrite()) { 4240006354cSpeter klausler handler.SignalError(IostatEndfileUnwritable, 4250006354cSpeter klausler "ENDFILE(UNIT=%d) on read-only file", unitNumber()); 426ac4202feSPeter Klausler } else if (IsAfterEndfile()) { 427e29c9d77Speter klausler // ENDFILE after ENDFILE 4280006354cSpeter klausler } else { 4290006354cSpeter klausler DoEndfile(handler); 43036771bbaSPeter Klausler if (IsRecordFile() && access != Access::Direct) { 4314d42e16eSpeter klausler // Explicit ENDFILE leaves position *after* the endfile record 4324d42e16eSpeter klausler RUNTIME_CHECK(handler, endfileRecordNumber.has_value()); 4334d42e16eSpeter klausler currentRecordNumber = *endfileRecordNumber + 1; 4340006354cSpeter klausler } 4350006354cSpeter klausler } 436991696c2SPeter Klausler } 4370006354cSpeter klausler 4380006354cSpeter klausler void ExternalFileUnit::Rewind(IoErrorHandler &handler) { 4390006354cSpeter klausler if (access == Access::Direct) { 4400006354cSpeter klausler handler.SignalError(IostatRewindNonSequential, 4410006354cSpeter klausler "REWIND(UNIT=%d) on non-sequential file", unitNumber()); 4420006354cSpeter klausler } else { 44396b17043SPeter Klausler DoImpliedEndfile(handler); 444991696c2SPeter Klausler SetPosition(0, handler); 4450006354cSpeter klausler currentRecordNumber = 1; 4466963fb7dSPeter Klausler leftTabLimit.reset(); 447c94f7804SPeter Klausler anyWriteSinceLastPositioning_ = false; 4480006354cSpeter klausler } 4490006354cSpeter klausler } 4500006354cSpeter klausler 451991696c2SPeter Klausler void ExternalFileUnit::SetPosition(std::int64_t pos, IoErrorHandler &handler) { 452991696c2SPeter Klausler frameOffsetInFile_ = pos; 453991696c2SPeter Klausler recordOffsetInFrame_ = 0; 454507f7317SPeter Klausler if (access == Access::Direct) { 455507f7317SPeter Klausler directAccessRecWasSet_ = true; 456507f7317SPeter Klausler } 457991696c2SPeter Klausler BeginRecord(); 458991696c2SPeter Klausler } 459991696c2SPeter Klausler 460d771245aSPeter Klausler bool ExternalFileUnit::SetStreamPos( 461d771245aSPeter Klausler std::int64_t oneBasedPos, IoErrorHandler &handler) { 462d771245aSPeter Klausler if (access != Access::Stream) { 463d771245aSPeter Klausler handler.SignalError("POS= may not appear unless ACCESS='STREAM'"); 464d771245aSPeter Klausler return false; 465d771245aSPeter Klausler } 466d771245aSPeter Klausler if (oneBasedPos < 1) { // POS=1 is beginning of file (12.6.2.11) 467d771245aSPeter Klausler handler.SignalError( 468d771245aSPeter Klausler "POS=%zd is invalid", static_cast<std::intmax_t>(oneBasedPos)); 469d771245aSPeter Klausler return false; 470d771245aSPeter Klausler } 47196b17043SPeter Klausler // A backwards POS= implies truncation after writing, at least in 47296b17043SPeter Klausler // Intel and NAG. 47396b17043SPeter Klausler if (static_cast<std::size_t>(oneBasedPos - 1) < 47496b17043SPeter Klausler frameOffsetInFile_ + recordOffsetInFrame_) { 47596b17043SPeter Klausler DoImpliedEndfile(handler); 47696b17043SPeter Klausler } 477d771245aSPeter Klausler SetPosition(oneBasedPos - 1, handler); 478d771245aSPeter Klausler // We no longer know which record we're in. Set currentRecordNumber to 479d771245aSPeter Klausler // a large value from whence we can both advance and backspace. 480d771245aSPeter Klausler currentRecordNumber = std::numeric_limits<std::int64_t>::max() / 2; 481d771245aSPeter Klausler endfileRecordNumber.reset(); 482d771245aSPeter Klausler return true; 483d771245aSPeter Klausler } 484d771245aSPeter Klausler 485d771245aSPeter Klausler bool ExternalFileUnit::SetDirectRec( 486d771245aSPeter Klausler std::int64_t oneBasedRec, IoErrorHandler &handler) { 487d771245aSPeter Klausler if (access != Access::Direct) { 488d771245aSPeter Klausler handler.SignalError("REC= may not appear unless ACCESS='DIRECT'"); 489d771245aSPeter Klausler return false; 490d771245aSPeter Klausler } 491d771245aSPeter Klausler if (!openRecl) { 492d771245aSPeter Klausler handler.SignalError("RECL= was not specified"); 493d771245aSPeter Klausler return false; 494d771245aSPeter Klausler } 495d771245aSPeter Klausler if (oneBasedRec < 1) { 496d771245aSPeter Klausler handler.SignalError( 497d771245aSPeter Klausler "REC=%zd is invalid", static_cast<std::intmax_t>(oneBasedRec)); 498d771245aSPeter Klausler return false; 499d771245aSPeter Klausler } 500d771245aSPeter Klausler currentRecordNumber = oneBasedRec; 501d771245aSPeter Klausler SetPosition((oneBasedRec - 1) * *openRecl, handler); 502d771245aSPeter Klausler return true; 503d771245aSPeter Klausler } 504d771245aSPeter Klausler 50595696d56Speter klausler void ExternalFileUnit::EndIoStatement() { 50695696d56Speter klausler io_.reset(); 507f7be2518Speter klausler u_.emplace<std::monostate>(); 50800a1c6d0SPeter Klausler lock_.Drop(); 5093b635714Speter klausler } 5103b635714Speter klausler 5110006354cSpeter klausler void ExternalFileUnit::BeginSequentialVariableUnformattedInputRecord( 5123b635714Speter klausler IoErrorHandler &handler) { 513bf1c89c3SPeter Klausler RUNTIME_CHECK(handler, access == Access::Sequential); 5143b635714Speter klausler std::int32_t header{0}, footer{0}; 5150006354cSpeter klausler std::size_t need{recordOffsetInFrame_ + sizeof header}; 5160006354cSpeter klausler std::size_t got{ReadFrame(frameOffsetInFile_, need, handler)}; 5173b635714Speter klausler // Try to emit informative errors to help debug corrupted files. 5183b635714Speter klausler const char *error{nullptr}; 5193b635714Speter klausler if (got < need) { 5200006354cSpeter klausler if (got == recordOffsetInFrame_) { 5218527f9e4SPeter Klausler HitEndOnRead(handler); 5223b635714Speter klausler } else { 5230006354cSpeter klausler error = "Unformatted variable-length sequential file input failed at " 5240006354cSpeter klausler "record #%jd (file offset %jd): truncated record header"; 5253b635714Speter klausler } 5263b635714Speter klausler } else { 527c7fe5e83SPeter Klausler header = ReadHeaderOrFooter(recordOffsetInFrame_); 5280006354cSpeter klausler recordLength = sizeof header + header; // does not include footer 5290006354cSpeter klausler need = recordOffsetInFrame_ + *recordLength + sizeof footer; 5300006354cSpeter klausler got = ReadFrame(frameOffsetInFile_, need, handler); 5313b635714Speter klausler if (got < need) { 5320006354cSpeter klausler error = "Unformatted variable-length sequential file input failed at " 5330006354cSpeter klausler "record #%jd (file offset %jd): hit EOF reading record with " 5340006354cSpeter klausler "length %jd bytes"; 5353b635714Speter klausler } else { 536c7fe5e83SPeter Klausler footer = ReadHeaderOrFooter(recordOffsetInFrame_ + *recordLength); 5373b635714Speter klausler if (footer != header) { 5380006354cSpeter klausler error = "Unformatted variable-length sequential file input failed at " 5390006354cSpeter klausler "record #%jd (file offset %jd): record header has length %jd " 5400006354cSpeter klausler "that does not match record footer (%jd)"; 5413b635714Speter klausler } 5423b635714Speter klausler } 5433b635714Speter klausler } 5443b635714Speter klausler if (error) { 5453b635714Speter klausler handler.SignalError(error, static_cast<std::intmax_t>(currentRecordNumber), 5463b635714Speter klausler static_cast<std::intmax_t>(frameOffsetInFile_), 5473b635714Speter klausler static_cast<std::intmax_t>(header), static_cast<std::intmax_t>(footer)); 5480006354cSpeter klausler // TODO: error recovery 5493b635714Speter klausler } 5503b635714Speter klausler positionInRecord = sizeof header; 5513b635714Speter klausler } 5523b635714Speter klausler 553991696c2SPeter Klausler void ExternalFileUnit::BeginVariableFormattedInputRecord( 5543b635714Speter klausler IoErrorHandler &handler) { 555a62b6016SPeter Klausler if (this == defaultInput) { 556a62b6016SPeter Klausler if (defaultOutput) { 557cd0a1226Speter klausler defaultOutput->FlushOutput(handler); 5580006354cSpeter klausler } 559a62b6016SPeter Klausler if (errorOutput) { 560a62b6016SPeter Klausler errorOutput->FlushOutput(handler); 561a62b6016SPeter Klausler } 562a62b6016SPeter Klausler } 5633b635714Speter klausler std::size_t length{0}; 5640006354cSpeter klausler do { 565dfea011aSpeter klausler std::size_t need{length + 1}; 566dfea011aSpeter klausler length = 567dfea011aSpeter klausler ReadFrame(frameOffsetInFile_, recordOffsetInFrame_ + need, handler) - 568dfea011aSpeter klausler recordOffsetInFrame_; 5690006354cSpeter klausler if (length < need) { 570dfea011aSpeter klausler if (length > 0) { 571dfea011aSpeter klausler // final record w/o \n 572dfea011aSpeter klausler recordLength = length; 57336771bbaSPeter Klausler unterminatedRecord = true; 574dfea011aSpeter klausler } else { 5758527f9e4SPeter Klausler HitEndOnRead(handler); 576dfea011aSpeter klausler } 5773b635714Speter klausler break; 5783b635714Speter klausler } 579991696c2SPeter Klausler } while (!SetVariableFormattedRecordLength()); 5803b635714Speter klausler } 5810006354cSpeter klausler 5820006354cSpeter klausler void ExternalFileUnit::BackspaceFixedRecord(IoErrorHandler &handler) { 58306ca9f24SPeter Klausler RUNTIME_CHECK(handler, openRecl.has_value()); 58406ca9f24SPeter Klausler if (frameOffsetInFile_ < *openRecl) { 5850006354cSpeter klausler handler.SignalError(IostatBackspaceAtFirstRecord); 5860006354cSpeter klausler } else { 58706ca9f24SPeter Klausler frameOffsetInFile_ -= *openRecl; 5883b635714Speter klausler } 5893b635714Speter klausler } 5903b635714Speter klausler 5910006354cSpeter klausler void ExternalFileUnit::BackspaceVariableUnformattedRecord( 5923b635714Speter klausler IoErrorHandler &handler) { 593c7fe5e83SPeter Klausler std::int32_t header{0}; 5940006354cSpeter klausler auto headerBytes{static_cast<std::int64_t>(sizeof header)}; 5950006354cSpeter klausler frameOffsetInFile_ += recordOffsetInFrame_; 5960006354cSpeter klausler recordOffsetInFrame_ = 0; 5970006354cSpeter klausler if (frameOffsetInFile_ <= headerBytes) { 5980006354cSpeter klausler handler.SignalError(IostatBackspaceAtFirstRecord); 5990006354cSpeter klausler return; 6000006354cSpeter klausler } 6013b635714Speter klausler // Error conditions here cause crashes, not file format errors, because the 6023b635714Speter klausler // validity of the file structure before the current record will have been 6030006354cSpeter klausler // checked informatively in NextSequentialVariableUnformattedInputRecord(). 6043b635714Speter klausler std::size_t got{ 6050006354cSpeter klausler ReadFrame(frameOffsetInFile_ - headerBytes, headerBytes, handler)}; 606461b6fe4SPeter Klausler if (static_cast<std::int64_t>(got) < headerBytes) { 607461b6fe4SPeter Klausler handler.SignalError(IostatShortRead); 608461b6fe4SPeter Klausler return; 609461b6fe4SPeter Klausler } 610c7fe5e83SPeter Klausler recordLength = ReadHeaderOrFooter(0); 611461b6fe4SPeter Klausler if (frameOffsetInFile_ < *recordLength + 2 * headerBytes) { 612461b6fe4SPeter Klausler handler.SignalError(IostatBadUnformattedRecord); 613461b6fe4SPeter Klausler return; 614461b6fe4SPeter Klausler } 6150006354cSpeter klausler frameOffsetInFile_ -= *recordLength + 2 * headerBytes; 6160006354cSpeter klausler auto need{static_cast<std::size_t>( 6170006354cSpeter klausler recordOffsetInFrame_ + sizeof header + *recordLength)}; 6180006354cSpeter klausler got = ReadFrame(frameOffsetInFile_, need, handler); 619461b6fe4SPeter Klausler if (got < need) { 620461b6fe4SPeter Klausler handler.SignalError(IostatShortRead); 621461b6fe4SPeter Klausler return; 622461b6fe4SPeter Klausler } 623c7fe5e83SPeter Klausler header = ReadHeaderOrFooter(recordOffsetInFrame_); 624461b6fe4SPeter Klausler if (header != *recordLength) { 625461b6fe4SPeter Klausler handler.SignalError(IostatBadUnformattedRecord); 626461b6fe4SPeter Klausler return; 627461b6fe4SPeter Klausler } 6283b635714Speter klausler } 6293b635714Speter klausler 6303b635714Speter klausler // There's no portable memrchr(), unfortunately, and strrchr() would 6313b635714Speter klausler // fail on a record with a NUL, so we have to do it the hard way. 6328ebf7411SSlava Zakharin static RT_API_ATTRS const char *FindLastNewline( 6338ebf7411SSlava Zakharin const char *str, std::size_t length) { 63416e61eb0SPeter Klausler for (const char *p{str + length}; p >= str; p--) { 6353b635714Speter klausler if (*p == '\n') { 6363b635714Speter klausler return p; 6373b635714Speter klausler } 6383b635714Speter klausler } 6393b635714Speter klausler return nullptr; 6403b635714Speter klausler } 6413b635714Speter klausler 6420006354cSpeter klausler void ExternalFileUnit::BackspaceVariableFormattedRecord( 6433b635714Speter klausler IoErrorHandler &handler) { 6440006354cSpeter klausler // File offset of previous record's newline 6450006354cSpeter klausler auto prevNL{ 6460006354cSpeter klausler frameOffsetInFile_ + static_cast<std::int64_t>(recordOffsetInFrame_) - 1}; 6470006354cSpeter klausler if (prevNL < 0) { 6480006354cSpeter klausler handler.SignalError(IostatBackspaceAtFirstRecord); 6490006354cSpeter klausler return; 6500006354cSpeter klausler } 6513b635714Speter klausler while (true) { 6520006354cSpeter klausler if (frameOffsetInFile_ < prevNL) { 6533b635714Speter klausler if (const char *p{ 6540006354cSpeter klausler FindLastNewline(Frame(), prevNL - 1 - frameOffsetInFile_)}) { 6553b635714Speter klausler recordOffsetInFrame_ = p - Frame() + 1; 656cbad5761SMichael Kruse recordLength = prevNL - (frameOffsetInFile_ + recordOffsetInFrame_); 6573b635714Speter klausler break; 6583b635714Speter klausler } 6593b635714Speter klausler } 6600006354cSpeter klausler if (frameOffsetInFile_ == 0) { 6610006354cSpeter klausler recordOffsetInFrame_ = 0; 662cbad5761SMichael Kruse recordLength = prevNL; 6630006354cSpeter klausler break; 6643b635714Speter klausler } 6650006354cSpeter klausler frameOffsetInFile_ -= std::min<std::int64_t>(frameOffsetInFile_, 1024); 6660006354cSpeter klausler auto need{static_cast<std::size_t>(prevNL + 1 - frameOffsetInFile_)}; 6670006354cSpeter klausler auto got{ReadFrame(frameOffsetInFile_, need, handler)}; 668461b6fe4SPeter Klausler if (got < need) { 669461b6fe4SPeter Klausler handler.SignalError(IostatShortRead); 670461b6fe4SPeter Klausler return; 6713b635714Speter klausler } 672461b6fe4SPeter Klausler } 673461b6fe4SPeter Klausler if (Frame()[recordOffsetInFrame_ + *recordLength] != '\n') { 674461b6fe4SPeter Klausler handler.SignalError(IostatMissingTerminator); 675461b6fe4SPeter Klausler return; 676461b6fe4SPeter Klausler } 6773b635714Speter klausler if (*recordLength > 0 && 6783b635714Speter klausler Frame()[recordOffsetInFrame_ + *recordLength - 1] == '\r') { 6793b635714Speter klausler --*recordLength; 6803b635714Speter klausler } 681f7be2518Speter klausler } 6820006354cSpeter klausler 6830006354cSpeter klausler void ExternalFileUnit::DoImpliedEndfile(IoErrorHandler &handler) { 684b6260538SPeter Klausler if (access != Access::Direct) { 685b6260538SPeter Klausler if (!impliedEndfile_ && leftTabLimit && direction_ == Direction::Output) { 686b6260538SPeter Klausler // Flush a partial record after non-advancing output 687b6260538SPeter Klausler impliedEndfile_ = true; 688472913c7SPeter Klausler } 689b6260538SPeter Klausler if (impliedEndfile_ && mayPosition()) { 6900006354cSpeter klausler DoEndfile(handler); 6910006354cSpeter klausler } 6920006354cSpeter klausler } 693b6260538SPeter Klausler impliedEndfile_ = false; 6940006354cSpeter klausler } 6950006354cSpeter klausler 6962b86fb21SSlava Zakharin template <bool ANY_DIR, Direction DIR> 6970006354cSpeter klausler void ExternalFileUnit::DoEndfile(IoErrorHandler &handler) { 69836771bbaSPeter Klausler if (IsRecordFile() && access != Access::Direct) { 6990508fd59SPeter Klausler furthestPositionInRecord = 7000508fd59SPeter Klausler std::max(positionInRecord, furthestPositionInRecord); 701b6260538SPeter Klausler if (leftTabLimit) { // last I/O was non-advancing 702b6260538SPeter Klausler if (access == Access::Sequential && direction_ == Direction::Output) { 7032b86fb21SSlava Zakharin if constexpr (ANY_DIR || DIR == Direction::Output) { 7042b86fb21SSlava Zakharin // When DoEndfile() is called from BeginReadingRecord(), 7052b86fb21SSlava Zakharin // this call to AdvanceRecord() may appear as a recursion 7062b86fb21SSlava Zakharin // though it may never happen. Expose the call only 7072b86fb21SSlava Zakharin // under the constexpr direction check. 708b6260538SPeter Klausler AdvanceRecord(handler); 7092b86fb21SSlava Zakharin } else { 7102b86fb21SSlava Zakharin // This check always fails if we are here. 7112b86fb21SSlava Zakharin RUNTIME_CHECK(handler, direction_ != Direction::Output); 7122b86fb21SSlava Zakharin } 713b6260538SPeter Klausler } else { // Access::Stream or input 71436771bbaSPeter Klausler leftTabLimit.reset(); 71536771bbaSPeter Klausler ++currentRecordNumber; 71636771bbaSPeter Klausler } 717b6260538SPeter Klausler } 7180006354cSpeter klausler endfileRecordNumber = currentRecordNumber; 719991696c2SPeter Klausler } 7200508fd59SPeter Klausler frameOffsetInFile_ += recordOffsetInFrame_ + furthestPositionInRecord; 7210508fd59SPeter Klausler recordOffsetInFrame_ = 0; 7221a65d09dSPeter Klausler FlushOutput(handler); 7230508fd59SPeter Klausler Truncate(frameOffsetInFile_, handler); 7241a65d09dSPeter Klausler TruncateFrame(frameOffsetInFile_, handler); 7250006354cSpeter klausler BeginRecord(); 7260006354cSpeter klausler impliedEndfile_ = false; 727c94f7804SPeter Klausler anyWriteSinceLastPositioning_ = false; 7280006354cSpeter klausler } 72943fadefbSpeter klausler 7302b86fb21SSlava Zakharin template void ExternalFileUnit::DoEndfile(IoErrorHandler &handler); 7312b86fb21SSlava Zakharin template void ExternalFileUnit::DoEndfile<false, Direction::Output>( 7322b86fb21SSlava Zakharin IoErrorHandler &handler); 7332b86fb21SSlava Zakharin template void ExternalFileUnit::DoEndfile<false, Direction::Input>( 7342b86fb21SSlava Zakharin IoErrorHandler &handler); 7352b86fb21SSlava Zakharin 736cd0a1226Speter klausler void ExternalFileUnit::CommitWrites() { 737cd0a1226Speter klausler frameOffsetInFile_ += 738cd0a1226Speter klausler recordOffsetInFrame_ + recordLength.value_or(furthestPositionInRecord); 739cd0a1226Speter klausler recordOffsetInFrame_ = 0; 740cd0a1226Speter klausler BeginRecord(); 741cd0a1226Speter klausler } 742cd0a1226Speter klausler 743507f7317SPeter Klausler bool ExternalFileUnit::CheckDirectAccess(IoErrorHandler &handler) { 744507f7317SPeter Klausler if (access == Access::Direct) { 745507f7317SPeter Klausler RUNTIME_CHECK(handler, openRecl); 746507f7317SPeter Klausler if (!directAccessRecWasSet_) { 747e3550f19SPeter Steinfeld handler.SignalError( 748e3550f19SPeter Steinfeld "No REC= was specified for a data transfer with ACCESS='DIRECT'"); 749507f7317SPeter Klausler return false; 750507f7317SPeter Klausler } 751507f7317SPeter Klausler } 752507f7317SPeter Klausler return true; 753507f7317SPeter Klausler } 754507f7317SPeter Klausler 7558527f9e4SPeter Klausler void ExternalFileUnit::HitEndOnRead(IoErrorHandler &handler) { 7568527f9e4SPeter Klausler handler.SignalEnd(); 7578527f9e4SPeter Klausler if (IsRecordFile() && access != Access::Direct) { 7588527f9e4SPeter Klausler endfileRecordNumber = currentRecordNumber; 7598527f9e4SPeter Klausler } 7608527f9e4SPeter Klausler } 7618527f9e4SPeter Klausler 76243fadefbSpeter klausler ChildIo &ExternalFileUnit::PushChildIo(IoStatementState &parent) { 76343fadefbSpeter klausler OwningPtr<ChildIo> current{std::move(child_)}; 76443fadefbSpeter klausler Terminator &terminator{parent.GetIoErrorHandler()}; 76543fadefbSpeter klausler OwningPtr<ChildIo> next{New<ChildIo>{terminator}(parent, std::move(current))}; 76643fadefbSpeter klausler child_.reset(next.release()); 76743fadefbSpeter klausler return *child_; 76843fadefbSpeter klausler } 76943fadefbSpeter klausler 77043fadefbSpeter klausler void ExternalFileUnit::PopChildIo(ChildIo &child) { 77143fadefbSpeter klausler if (child_.get() != &child) { 77243fadefbSpeter klausler child.parent().GetIoErrorHandler().Crash( 77343fadefbSpeter klausler "ChildIo being popped is not top of stack"); 77443fadefbSpeter klausler } 77543fadefbSpeter klausler child_.reset(child.AcquirePrevious().release()); // deletes top child 77643fadefbSpeter klausler } 77743fadefbSpeter klausler 778c7fe5e83SPeter Klausler std::int32_t ExternalFileUnit::ReadHeaderOrFooter(std::int64_t frameOffset) { 779c7fe5e83SPeter Klausler std::int32_t word; 780c7fe5e83SPeter Klausler char *wordPtr{reinterpret_cast<char *>(&word)}; 781c7fe5e83SPeter Klausler std::memcpy(wordPtr, Frame() + frameOffset, sizeof word); 782c7fe5e83SPeter Klausler if (swapEndianness_) { 783c7fe5e83SPeter Klausler SwapEndianness(wordPtr, sizeof word, sizeof word); 784c7fe5e83SPeter Klausler } 785c7fe5e83SPeter Klausler return word; 786c7fe5e83SPeter Klausler } 787c7fe5e83SPeter Klausler 78843fadefbSpeter klausler void ChildIo::EndIoStatement() { 78943fadefbSpeter klausler io_.reset(); 79043fadefbSpeter klausler u_.emplace<std::monostate>(); 79143fadefbSpeter klausler } 79243fadefbSpeter klausler 793df38f35aSPeter Klausler Iostat ChildIo::CheckFormattingAndDirection( 794df38f35aSPeter Klausler bool unformatted, Direction direction) { 79543fadefbSpeter klausler bool parentIsInput{!parent_.get_if<IoDirectionState<Direction::Output>>()}; 7964393e377Speter klausler bool parentIsFormatted{parentIsInput 7974393e377Speter klausler ? parent_.get_if<FormattedIoStatementState<Direction::Input>>() != 7984393e377Speter klausler nullptr 7994393e377Speter klausler : parent_.get_if<FormattedIoStatementState<Direction::Output>>() != 8004393e377Speter klausler nullptr}; 8014393e377Speter klausler bool parentIsUnformatted{!parentIsFormatted}; 80243fadefbSpeter klausler if (unformatted != parentIsUnformatted) { 803df38f35aSPeter Klausler return unformatted ? IostatUnformattedChildOnFormattedParent 804df38f35aSPeter Klausler : IostatFormattedChildOnUnformattedParent; 80543fadefbSpeter klausler } else if (parentIsInput != (direction == Direction::Input)) { 806df38f35aSPeter Klausler return parentIsInput ? IostatChildOutputToInputParent 807df38f35aSPeter Klausler : IostatChildInputFromOutputParent; 80843fadefbSpeter klausler } else { 809df38f35aSPeter Klausler return IostatOk; 81043fadefbSpeter klausler } 81143fadefbSpeter klausler } 81243fadefbSpeter klausler 8138ebf7411SSlava Zakharin RT_OFFLOAD_API_GROUP_END 8141f879005STim Keith } // namespace Fortran::runtime::io 815