brintos

brintos / llvm-project-archived public Read only

0
0
Text · 52.5 KiB · a88fbe6 Raw
1708 lines · cpp
1//===-- lib/runtime/io-stmt.cpp ---------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "flang-rt/runtime/io-stmt.h"10#include "unit.h"11#include "flang-rt/runtime/connection.h"12#include "flang-rt/runtime/emit-encoded.h"13#include "flang-rt/runtime/format.h"14#include "flang-rt/runtime/memory.h"15#include "flang-rt/runtime/tools.h"16#include "flang-rt/runtime/utf.h"17#include <algorithm>18#include <cstdio>19#include <cstring>20#include <limits>21#include <type_traits>22 23namespace Fortran::runtime::io {24RT_OFFLOAD_API_GROUP_BEGIN25 26bool IoStatementBase::Emit(const char *, std::size_t, std::size_t) {27  return false;28}29 30std::size_t IoStatementBase::GetNextInputBytes(const char *&p) {31  p = nullptr;32  return 0;33}34 35std::size_t IoStatementBase::ViewBytesInRecord(36    const char *&p, bool forward) const {37  p = nullptr;38  return 0;39}40 41bool IoStatementBase::AdvanceRecord(int) { return false; }42 43void IoStatementBase::BackspaceRecord() {}44 45bool IoStatementBase::Receive(char *, std::size_t, std::size_t) {46  return false;47}48 49common::optional<DataEdit> IoStatementBase::GetNextDataEdit(50    IoStatementState &, int) {51  return common::nullopt;52}53 54bool IoStatementBase::BeginReadingRecord() { return true; }55 56void IoStatementBase::FinishReadingRecord() {}57 58void IoStatementBase::HandleAbsolutePosition(std::int64_t) {}59 60void IoStatementBase::HandleRelativePosition(std::int64_t) {}61 62std::int64_t IoStatementBase::InquirePos() { return 0; }63 64ExternalFileUnit *IoStatementBase::GetExternalFileUnit() const {65  return nullptr;66}67 68bool IoStatementBase::Inquire(InquiryKeywordHash, char *, std::size_t) {69  return false;70}71 72bool IoStatementBase::Inquire(InquiryKeywordHash, bool &) { return false; }73 74bool IoStatementBase::Inquire(InquiryKeywordHash, std::int64_t, bool &) {75  return false;76}77 78bool IoStatementBase::Inquire(InquiryKeywordHash, std::int64_t &) {79  return false;80}81 82RT_API_ATTRS static const char *InquiryKeywordHashDecode(83    char *buffer, std::size_t n, InquiryKeywordHash hash) {84  if (n < 1) {85    return nullptr;86  }87  char *p{buffer + n};88  *--p = '\0';89  while (hash > 1) {90    if (p < buffer) {91      return nullptr;92    }93    *--p = 'A' + (hash % 26);94    hash /= 26;95  }96  return hash == 1 ? p : nullptr;97}98 99void IoStatementBase::BadInquiryKeywordHashCrash(InquiryKeywordHash inquiry) {100  char buffer[16];101  const char *decode{InquiryKeywordHashDecode(buffer, sizeof buffer, inquiry)};102  Crash("Bad InquiryKeywordHash 0x%x (%s)", inquiry,103      decode ? decode : "(cannot decode)");104}105 106template <Direction DIR>107InternalIoStatementState<DIR>::InternalIoStatementState(108    Buffer scalar, std::size_t length, const char *sourceFile, int sourceLine)109    : IoStatementBase{sourceFile, sourceLine}, unit_{scalar, length, 1} {}110 111template <Direction DIR>112InternalIoStatementState<DIR>::InternalIoStatementState(113    const Descriptor &d, const char *sourceFile, int sourceLine)114    : IoStatementBase{sourceFile, sourceLine}, unit_{d, *this} {}115 116template <Direction DIR>117bool InternalIoStatementState<DIR>::Emit(118    const char *data, std::size_t bytes, std::size_t /*elementBytes*/) {119  if constexpr (DIR == Direction::Input) {120    Crash("InternalIoStatementState<Direction::Input>::Emit() called");121    return false;122  }123  return unit_.Emit(data, bytes, *this);124}125 126template <Direction DIR>127std::size_t InternalIoStatementState<DIR>::GetNextInputBytes(const char *&p) {128  return unit_.GetNextInputBytes(p, *this);129}130 131// InternalIoStatementState<DIR>::ViewBytesInRecord() not needed or defined132 133template <Direction DIR>134bool InternalIoStatementState<DIR>::AdvanceRecord(int n) {135  while (n-- > 0) {136    if (!unit_.AdvanceRecord(*this)) {137      return false;138    }139  }140  return true;141}142 143template <Direction DIR> void InternalIoStatementState<DIR>::BackspaceRecord() {144  unit_.BackspaceRecord(*this);145}146 147template <Direction DIR> int InternalIoStatementState<DIR>::EndIoStatement() {148  auto result{IoStatementBase::EndIoStatement()};149  if (free_) {150    FreeMemory(this);151  }152  return result;153}154 155template <Direction DIR>156void InternalIoStatementState<DIR>::HandleAbsolutePosition(std::int64_t n) {157  return unit_.HandleAbsolutePosition(n);158}159 160template <Direction DIR>161void InternalIoStatementState<DIR>::HandleRelativePosition(std::int64_t n) {162  return unit_.HandleRelativePosition(n);163}164 165template <Direction DIR>166std::int64_t InternalIoStatementState<DIR>::InquirePos() {167  return unit_.InquirePos();168}169 170template <Direction DIR, typename CHAR>171RT_API_ATTRS172InternalFormattedIoStatementState<DIR, CHAR>::InternalFormattedIoStatementState(173    Buffer buffer, std::size_t length, const CharType *format,174    std::size_t formatLength, const Descriptor *formatDescriptor,175    const char *sourceFile, int sourceLine)176    : InternalIoStatementState<DIR>{buffer, length, sourceFile, sourceLine},177      ioStatementState_{*this},178      format_{*this, format, formatLength, formatDescriptor} {}179 180template <Direction DIR, typename CHAR>181RT_API_ATTRS182InternalFormattedIoStatementState<DIR, CHAR>::InternalFormattedIoStatementState(183    const Descriptor &d, const CharType *format, std::size_t formatLength,184    const Descriptor *formatDescriptor, const char *sourceFile, int sourceLine)185    : InternalIoStatementState<DIR>{d, sourceFile, sourceLine},186      ioStatementState_{*this},187      format_{*this, format, formatLength, formatDescriptor} {}188 189template <Direction DIR, typename CHAR>190void InternalFormattedIoStatementState<DIR, CHAR>::CompleteOperation() {191  if (!this->completedOperation()) {192    if constexpr (DIR == Direction::Output) {193      format_.Finish(*this);194      unit_.AdvanceRecord(*this);195    }196    IoStatementBase::CompleteOperation();197  }198}199 200template <Direction DIR, typename CHAR>201int InternalFormattedIoStatementState<DIR, CHAR>::EndIoStatement() {202  CompleteOperation();203  return InternalIoStatementState<DIR>::EndIoStatement();204}205 206template <Direction DIR>207InternalListIoStatementState<DIR>::InternalListIoStatementState(208    Buffer buffer, std::size_t length, const char *sourceFile, int sourceLine)209    : InternalIoStatementState<DIR>{buffer, length, sourceFile, sourceLine},210      ioStatementState_{*this} {}211 212template <Direction DIR>213InternalListIoStatementState<DIR>::InternalListIoStatementState(214    const Descriptor &d, const char *sourceFile, int sourceLine)215    : InternalIoStatementState<DIR>{d, sourceFile, sourceLine},216      ioStatementState_{*this} {}217 218template <Direction DIR>219void InternalListIoStatementState<DIR>::CompleteOperation() {220  if (!this->completedOperation()) {221    if constexpr (DIR == Direction::Output) {222      if (unit_.furthestPositionInRecord > 0) {223        unit_.AdvanceRecord(*this);224      }225    }226    IoStatementBase::CompleteOperation();227  }228}229 230template <Direction DIR>231int InternalListIoStatementState<DIR>::EndIoStatement() {232  CompleteOperation();233  if constexpr (DIR == Direction::Input) {234    if (int status{ListDirectedStatementState<DIR>::EndIoStatement()};235        status != IostatOk) {236      return status;237    }238  }239  return InternalIoStatementState<DIR>::EndIoStatement();240}241 242ExternalIoStatementBase::ExternalIoStatementBase(243    ExternalFileUnit &unit, const char *sourceFile, int sourceLine)244    : IoStatementBase{sourceFile, sourceLine}, unit_{unit} {}245 246MutableModes &ExternalIoStatementBase::mutableModes() {247  if (const ChildIo * child{unit_.GetChildIo()}) {248#if !defined(RT_DEVICE_AVOID_RECURSION)249    return child->parent().mutableModes();250#else251    ReportUnsupportedChildIo();252#endif253  }254  return unit_.modes;255}256 257ConnectionState &ExternalIoStatementBase::GetConnectionState() { return unit_; }258 259int ExternalIoStatementBase::EndIoStatement() {260  CompleteOperation();261  auto result{IoStatementBase::EndIoStatement()};262#if !defined(RT_USE_PSEUDO_FILE_UNIT)263  auto unitNumber{unit_.unitNumber()};264  unit_.EndIoStatement(); // annihilates *this in unit_.u_265  if (destroy_) {266    if (ExternalFileUnit *267        toClose{ExternalFileUnit::LookUpForClose(unitNumber)}) {268      toClose->Close(CloseStatus::Delete, *this);269      toClose->DestroyClosed();270    }271  }272#else273  // Fetch the unit pointer before *this disappears.274  ExternalFileUnit *unitPtr{&unit_};275  // The pseudo file units are dynamically allocated276  // and are not tracked in the unit map.277  // They have to be destructed and deallocated here.278  unitPtr->~ExternalFileUnit();279  FreeMemory(unitPtr);280#endif281  return result;282}283 284void ExternalIoStatementBase::SetAsynchronous() {285  asynchronousID_ = unit().GetAsynchronousId(*this);286}287 288std::int64_t ExternalIoStatementBase::InquirePos() {289  return unit_.InquirePos();290}291 292void OpenStatementState::set_path(const char *path, std::size_t length) {293  pathLength_ = TrimTrailingSpaces(path, length);294  path_ = SaveDefaultCharacter(path, pathLength_, *this);295}296 297void OpenStatementState::CompleteOperation() {298  if (completedOperation()) {299    return;300  }301  if (position_) {302    if (access_ && *access_ == Access::Direct) {303      SignalError("POSITION= may not be set with ACCESS='DIRECT'");304      position_.reset();305    }306  }307  if (status_) { // 12.5.6.10308    if ((*status_ == OpenStatus::New || *status_ == OpenStatus::Replace) &&309        !path_.get()) {310      SignalError("FILE= required on OPEN with STATUS='NEW' or 'REPLACE'");311    } else if (*status_ == OpenStatus::Scratch && path_.get()) {312      SignalError("FILE= may not appear on OPEN with STATUS='SCRATCH'");313    }314  }315  // F'2023 12.5.6.13 - NEWUNIT= requires either FILE= or STATUS='SCRATCH'316  if (isNewUnit_ && !path_.get() &&317      status_.value_or(OpenStatus::Unknown) != OpenStatus::Scratch) {318    SignalError(IostatBadNewUnit);319    status_ = OpenStatus::Scratch; // error recovery320  }321  if (path_.get() || wasExtant_ ||322      (status_ && *status_ == OpenStatus::Scratch)) {323    if (unit().OpenUnit(status_, action_, position_.value_or(Position::AsIs),324            std::move(path_), pathLength_, convert_, *this)) {325      wasExtant_ = false; // existing unit was closed326    }327  } else {328    unit().OpenAnonymousUnit(329        status_, action_, position_.value_or(Position::AsIs), convert_, *this);330  }331  if (access_) {332    if (*access_ != unit().access) {333      if (wasExtant_) {334        SignalError("ACCESS= may not be changed on an open unit");335        access_.reset();336      }337    }338    if (access_) {339      unit().access = *access_;340    }341  }342  if (!unit().isUnformatted) {343    unit().isUnformatted = isUnformatted_;344  }345  if (isUnformatted_ && *isUnformatted_ != *unit().isUnformatted) {346    if (wasExtant_) {347      SignalError("FORM= may not be changed on an open unit");348    }349    unit().isUnformatted = *isUnformatted_;350  }351  if (!unit().isUnformatted) {352    // Set default format (C.7.4 point 2).353    unit().isUnformatted = unit().access != Access::Sequential;354  }355  if (unit().isUnformatted.value_or(false) && mustBeFormatted_) {356    // This is an unformatted unit, but the OPEN statement contained at least357    // one specifier that is not permitted unless the unit is formatted358    // (e.g., BLANK=).  Programs that want to detect this error (i.e., tests)359    // should be informed about it, but don't crash the program otherwise360    // since most other compilers let it slide.361    if (HasErrorRecovery()) {362      SignalError("FORM='UNFORMATTED' is not allowed with OPEN specifiers that "363                  "apply only to formatted units");364    }365  }366  if (!wasExtant_ && InError()) {367    // Release the new unit on failure368    set_destroy();369  }370  IoStatementBase::CompleteOperation();371}372 373int OpenStatementState::EndIoStatement() {374  CompleteOperation();375  return ExternalIoStatementBase::EndIoStatement();376}377 378int CloseStatementState::EndIoStatement() {379  CompleteOperation();380  int result{ExternalIoStatementBase::EndIoStatement()};381  unit().CloseUnit(status_, *this);382  unit().DestroyClosed();383  return result;384}385 386void NoUnitIoStatementState::CompleteOperation() {387  SignalPendingError();388  IoStatementBase::CompleteOperation();389}390 391int NoUnitIoStatementState::EndIoStatement() {392  CompleteOperation();393  auto result{IoStatementBase::EndIoStatement()};394  FreeMemory(this);395  return result;396}397 398template <Direction DIR>399ExternalIoStatementState<DIR>::ExternalIoStatementState(400    ExternalFileUnit &unit, const char *sourceFile, int sourceLine)401    : ExternalIoStatementBase{unit, sourceFile, sourceLine}, mutableModes_{402                                                                 unit.modes} {403  if constexpr (DIR == Direction::Output) {404    // If the last statement was a non-advancing IO input statement, the unit405    // furthestPositionInRecord was not advanced, but the positionInRecord may406    // have been advanced. Advance furthestPositionInRecord here to avoid407    // overwriting the part of the record that has been read with blanks.408    unit.furthestPositionInRecord =409        std::max(unit.furthestPositionInRecord, unit.positionInRecord);410  }411}412 413template <Direction DIR>414void ExternalIoStatementState<DIR>::CompleteOperation() {415  if (completedOperation()) {416    return;417  }418  if constexpr (DIR == Direction::Input) {419    BeginReadingRecord(); // in case there were no I/O items420    if (mutableModes().nonAdvancing && !InError()) {421      unit().leftTabLimit = unit().furthestPositionInRecord;422    } else {423      FinishReadingRecord();424    }425  } else { // output426    if (mutableModes().nonAdvancing) {427      // Make effects of positioning past the last Emit() visible with blanks.428      if (unit().positionInRecord > unit().furthestPositionInRecord) {429        unit().Emit("", 0, 1, *this); // Emit() will pad430      }431      unit().leftTabLimit = unit().positionInRecord;432    } else {433      unit().AdvanceRecord(*this);434    }435    unit().FlushIfTerminal(*this);436  }437  return IoStatementBase::CompleteOperation();438}439 440template <Direction DIR> int ExternalIoStatementState<DIR>::EndIoStatement() {441  CompleteOperation();442  return ExternalIoStatementBase::EndIoStatement();443}444 445template <Direction DIR>446bool ExternalIoStatementState<DIR>::Emit(447    const char *data, std::size_t bytes, std::size_t elementBytes) {448  if constexpr (DIR == Direction::Input) {449    Crash("ExternalIoStatementState::Emit(char) called for input statement");450  }451  return unit().Emit(data, bytes, elementBytes, *this);452}453 454template <Direction DIR>455std::size_t ExternalIoStatementState<DIR>::GetNextInputBytes(const char *&p) {456  return unit().GetNextInputBytes(p, *this);457}458 459template <Direction DIR>460std::size_t ExternalIoStatementState<DIR>::ViewBytesInRecord(461    const char *&p, bool forward) const {462  return unit().ViewBytesInRecord(p, forward);463}464 465template <Direction DIR>466bool ExternalIoStatementState<DIR>::AdvanceRecord(int n) {467  while (n-- > 0) {468    if (!unit().AdvanceRecord(*this)) {469      return false;470    }471  }472  return true;473}474 475template <Direction DIR> void ExternalIoStatementState<DIR>::BackspaceRecord() {476  unit().BackspaceRecord(*this);477}478 479template <Direction DIR>480void ExternalIoStatementState<DIR>::HandleAbsolutePosition(std::int64_t n) {481  return unit().HandleAbsolutePosition(n);482}483 484template <Direction DIR>485void ExternalIoStatementState<DIR>::HandleRelativePosition(std::int64_t n) {486  return unit().HandleRelativePosition(n);487}488 489template <Direction DIR>490bool ExternalIoStatementState<DIR>::BeginReadingRecord() {491  if constexpr (DIR == Direction::Input) {492    return unit().BeginReadingRecord(*this);493  } else {494    Crash("ExternalIoStatementState<Direction::Output>::BeginReadingRecord() "495          "called");496    return false;497  }498}499 500template <Direction DIR>501void ExternalIoStatementState<DIR>::FinishReadingRecord() {502  if constexpr (DIR == Direction::Input) {503    unit().FinishReadingRecord(*this);504  } else {505    Crash("ExternalIoStatementState<Direction::Output>::FinishReadingRecord() "506          "called");507  }508}509 510template <Direction DIR, typename CHAR>511ExternalFormattedIoStatementState<DIR, CHAR>::ExternalFormattedIoStatementState(512    ExternalFileUnit &unit, const CHAR *format, std::size_t formatLength,513    const Descriptor *formatDescriptor, const char *sourceFile, int sourceLine)514    : ExternalIoStatementState<DIR>{unit, sourceFile, sourceLine},515      format_{*this, format, formatLength, formatDescriptor} {}516 517template <Direction DIR, typename CHAR>518void ExternalFormattedIoStatementState<DIR, CHAR>::CompleteOperation() {519  if (this->completedOperation()) {520    return;521  }522  if constexpr (DIR == Direction::Input) {523    this->BeginReadingRecord(); // in case there were no I/O items524  }525  format_.Finish(*this);526  return ExternalIoStatementState<DIR>::CompleteOperation();527}528 529template <Direction DIR, typename CHAR>530int ExternalFormattedIoStatementState<DIR, CHAR>::EndIoStatement() {531  CompleteOperation();532  return ExternalIoStatementState<DIR>::EndIoStatement();533}534 535common::optional<DataEdit> IoStatementState::GetNextDataEdit(int n) {536  return common::visit(537      [&](auto &x) { return x.get().GetNextDataEdit(*this, n); }, u_);538}539 540const NonTbpDefinedIoTable *IoStatementState::nonTbpDefinedIoTable() const {541  return common::visit(542      [&](auto &x) { return x.get().nonTbpDefinedIoTable(); }, u_);543}544 545void IoStatementState::set_nonTbpDefinedIoTable(546    const NonTbpDefinedIoTable *table) {547  common::visit(548      [&](auto &x) { return x.get().set_nonTbpDefinedIoTable(table); }, u_);549}550 551bool IoStatementState::Emit(552    const char *data, std::size_t bytes, std::size_t elementBytes) {553  return common::visit(554      [=](auto &x) { return x.get().Emit(data, bytes, elementBytes); }, u_);555}556 557bool IoStatementState::Receive(558    char *data, std::size_t n, std::size_t elementBytes) {559  return common::visit(560      [=](auto &x) { return x.get().Receive(data, n, elementBytes); }, u_);561}562 563std::size_t IoStatementState::GetNextInputBytes(const char *&p) {564  return common::visit(565      [&](auto &x) { return x.get().GetNextInputBytes(p); }, u_);566}567 568bool IoStatementState::AdvanceRecord(int n) {569  return common::visit([=](auto &x) { return x.get().AdvanceRecord(n); }, u_);570}571 572void IoStatementState::BackspaceRecord() {573  common::visit([](auto &x) { x.get().BackspaceRecord(); }, u_);574}575 576void IoStatementState::HandleRelativePosition(std::int64_t n) {577  common::visit([=](auto &x) { x.get().HandleRelativePosition(n); }, u_);578}579 580void IoStatementState::HandleAbsolutePosition(std::int64_t n) {581  common::visit([=](auto &x) { x.get().HandleAbsolutePosition(n); }, u_);582}583 584void IoStatementState::CompleteOperation() {585  common::visit([](auto &x) { x.get().CompleteOperation(); }, u_);586}587 588int IoStatementState::EndIoStatement() {589  return common::visit([](auto &x) { return x.get().EndIoStatement(); }, u_);590}591 592ConnectionState &IoStatementState::GetConnectionState() {593  return common::visit(594      [](auto &x) -> ConnectionState & { return x.get().GetConnectionState(); },595      u_);596}597 598MutableModes &IoStatementState::mutableModes() {599  return common::visit(600      [](auto &x) -> MutableModes & { return x.get().mutableModes(); }, u_);601}602 603bool IoStatementState::BeginReadingRecord() {604  return common::visit(605      [](auto &x) { return x.get().BeginReadingRecord(); }, u_);606}607 608IoErrorHandler &IoStatementState::GetIoErrorHandler() const {609  return common::visit(610      [](auto &x) -> IoErrorHandler & {611        return static_cast<IoErrorHandler &>(x.get());612      },613      u_);614}615 616ExternalFileUnit *IoStatementState::GetExternalFileUnit() const {617  return common::visit(618      [](auto &x) { return x.get().GetExternalFileUnit(); }, u_);619}620 621common::optional<char32_t> IoStatementState::GetCurrentCharSlow(622    std::size_t &byteCount) {623  const char *p{nullptr};624  std::size_t bytes{GetNextInputBytes(p)};625  if (bytes == 0) {626    byteCount = 0;627    return common::nullopt;628  } else {629    const ConnectionState &connection{GetConnectionState()};630    if (connection.isUTF8) {631      std::size_t length{MeasureUTF8Bytes(*p)};632      if (length <= bytes) {633        if (auto result{DecodeUTF8(p)}) {634          byteCount = length;635          return result;636        }637      }638      GetIoErrorHandler().SignalError(IostatUTF8Decoding);639      // Error recovery: return the next byte640    } else if (connection.internalIoCharKind > 1) {641      byteCount = connection.internalIoCharKind;642      if (byteCount == 2) {643        return *reinterpret_cast<const char16_t *>(p);644      } else {645        return *reinterpret_cast<const char32_t *>(p);646      }647    }648    byteCount = 1;649    return *p;650  }651}652 653IoStatementState::FastAsciiField IoStatementState::GetUpcomingFastAsciiField() {654  ConnectionState &connection{GetConnectionState()};655  if (!connection.isUTF8 && connection.internalIoCharKind <= 1) {656    const char *p{nullptr};657    if (std::size_t bytes{GetNextInputBytes(p)}) {658      return FastAsciiField{connection, p, bytes};659    }660  }661  return FastAsciiField{connection};662}663 664common::optional<char32_t> IoStatementState::NextInField(665    common::optional<int> &remaining, const DataEdit &edit,666    FastAsciiField *field) {667  std::size_t byteCount{0};668  if (!remaining) { // Stream, list-directed, NAMELIST, &c.669    if (auto next{GetCurrentChar(byteCount, field)}) {670      if ((*next < '0' || *next == ';') && edit.width.value_or(0) == 0) {671        // list-directed, NAMELIST, I0 &c., or width-free I/G:672        // check for separator character673        switch (*next) {674        case ' ':675        case '\t':676        case '/':677        case '(':678        case ')':679        case '\'':680        case '"':681        case '*':682        case '\n': // for stream access683          return common::nullopt;684        case '&':685        case '$':686          if (edit.IsNamelist()) {687            return common::nullopt;688          }689          break;690        case ',':691          if (!(edit.modes.editingFlags & decimalComma)) {692            return common::nullopt;693          }694          break;695        case ';':696          if (edit.modes.editingFlags & decimalComma) {697            return common::nullopt;698          }699          break;700        default:701          break;702        }703      }704      if (field) {705        field->Advance(1, byteCount);706      } else {707        HandleRelativePosition(byteCount);708        GotChar(byteCount);709      }710      return next;711    }712  } else if (*remaining > 0) {713    if (auto next{GetCurrentChar(byteCount, field)}) {714      if (byteCount > static_cast<std::size_t>(*remaining)) {715        return common::nullopt;716      }717      *remaining -= byteCount;718      if (field) {719        field->Advance(1, byteCount);720      } else {721        HandleRelativePosition(byteCount);722        GotChar(byteCount);723      }724      return next;725    }726    if (CheckForEndOfRecord(0,727            field ? field->connection() : GetConnectionState())) { // do padding728      --*remaining;729      return common::optional<char32_t>{' '};730    }731  }732  return common::nullopt;733}734 735bool IoStatementState::CheckForEndOfRecord(736    std::size_t afterReading, const ConnectionState &connection) {737  if (!connection.IsAtEOF()) {738    if (auto length{connection.EffectiveRecordLength()}) {739      if (connection.positionInRecord +740              static_cast<std::int64_t>(afterReading) >=741          *length) {742        IoErrorHandler &handler{GetIoErrorHandler()};743        const auto &modes{mutableModes()};744        if (modes.nonAdvancing) {745          if (connection.access == Access::Stream &&746              connection.unterminatedRecord) {747            // Reading final unterminated record left by a748            // non-advancing WRITE on a stream file prior to749            // positioning or ENDFILE.750            handler.SignalEnd();751          } else {752            handler.SignalEor();753          }754        } else if (!modes.pad) {755          handler.SignalError(IostatRecordReadOverrun);756        }757        return modes.pad; // PAD='YES'758      }759    }760  }761  return false;762}763 764bool IoStatementState::Inquire(765    InquiryKeywordHash inquiry, char *out, std::size_t chars) {766  return common::visit(767      [&](auto &x) { return x.get().Inquire(inquiry, out, chars); }, u_);768}769 770bool IoStatementState::Inquire(InquiryKeywordHash inquiry, bool &out) {771  return common::visit(772      [&](auto &x) { return x.get().Inquire(inquiry, out); }, u_);773}774 775bool IoStatementState::Inquire(776    InquiryKeywordHash inquiry, std::int64_t id, bool &out) {777  return common::visit(778      [&](auto &x) { return x.get().Inquire(inquiry, id, out); }, u_);779}780 781bool IoStatementState::Inquire(InquiryKeywordHash inquiry, std::int64_t &n) {782  return common::visit(783      [&](auto &x) { return x.get().Inquire(inquiry, n); }, u_);784}785 786std::int64_t IoStatementState::InquirePos() {787  return common::visit([&](auto &x) { return x.get().InquirePos(); }, u_);788}789 790void IoStatementState::GotChar(int n) {791  if (auto *formattedIn{792          get_if<FormattedIoStatementState<Direction::Input>>()}) {793    formattedIn->GotChar(n);794  } else {795    GetIoErrorHandler().Crash("IoStatementState::GotChar() called for "796                              "statement that is not formatted input");797  }798}799 800std::size_t801FormattedIoStatementState<Direction::Input>::GetEditDescriptorChars() const {802  return chars_;803}804 805void FormattedIoStatementState<Direction::Input>::GotChar(int n) {806  chars_ += n;807}808 809bool ListDirectedStatementState<Direction::Output>::EmitLeadingSpaceOrAdvance(810    IoStatementState &io, std::size_t length, bool isCharacter) {811  const ConnectionState &connection{io.GetConnectionState()};812  int space{connection.positionInRecord == 0 ||813      !(isCharacter && lastWasUndelimitedCharacter())};814  set_lastWasUndelimitedCharacter(false);815  if (connection.NeedAdvance(space + length)) {816    return io.AdvanceRecord();817  }818  if (space) {819    return EmitAscii(io, " ", 1);820  }821  return true;822}823 824common::optional<DataEdit>825ListDirectedStatementState<Direction::Output>::GetNextDataEdit(826    IoStatementState &io, int maxRepeat) {827  DataEdit edit;828  edit.descriptor = DataEdit::ListDirected;829  edit.repeat = maxRepeat;830  edit.modes = io.mutableModes();831  return edit;832}833 834int ListDirectedStatementState<Direction::Input>::EndIoStatement() {835  if (repeatPosition_) {836    repeatPosition_->Cancel();837  }838  return IostatOk;839}840 841common::optional<DataEdit>842ListDirectedStatementState<Direction::Input>::GetNextDataEdit(843    IoStatementState &io, int maxRepeat) {844  // N.B. list-directed transfers cannot be nonadvancing (C1221)845  DataEdit edit;846  edit.descriptor = DataEdit::ListDirected;847  edit.repeat = 1; // may be overridden below848  edit.modes = io.mutableModes();849  if (hitSlash_) { // everything after '/' is nullified850    edit.descriptor = DataEdit::ListDirectedNullValue;851    return edit;852  }853  const char32_t comma{edit.modes.GetSeparatorChar()};854  std::size_t byteCount{0};855  if (remaining_ > 0 && !realPart_) { // "r*c" repetition in progress856    RUNTIME_CHECK(io.GetIoErrorHandler(), repeatPosition_.has_value());857    repeatPosition_.reset(); // restores the saved position858    if (!imaginaryPart_) {859      edit.repeat = std::min<int>(remaining_, maxRepeat);860      auto ch{io.GetCurrentChar(byteCount)};861      if (!ch || *ch == ' ' || *ch == '\t' || *ch == comma) {862        // "r*" repeated null863        edit.descriptor = DataEdit::ListDirectedNullValue;864      }865    }866    remaining_ -= edit.repeat;867    if (remaining_ > 0) {868      repeatPosition_.emplace(io);869    }870    if (!imaginaryPart_) {871      return edit;872    }873  }874  // Skip separators, handle a "r*c" repeat count; see 13.10.2 in Fortran 2018875  if (imaginaryPart_) {876    imaginaryPart_ = false;877  } else if (realPart_) {878    realPart_ = false;879    imaginaryPart_ = true;880    edit.descriptor = DataEdit::ListDirectedImaginaryPart;881  }882  auto fastField{io.GetUpcomingFastAsciiField()};883  // Reaching EOF is okay when peeking at list-directed defined input;884  // pretend that there's an END= in that case.885  bool oldHasEnd{maxRepeat == 0 && !io.GetIoErrorHandler().SetHasEnd()};886  auto ch{io.GetNextNonBlank(byteCount, &fastField)};887  if (ch && *ch == comma && eatComma_) {888    // Consume comma & whitespace after previous item.889    // This includes the comma between real and imaginary components890    // in list-directed/NAMELIST complex input.891    // (When DECIMAL='COMMA', the comma is actually a semicolon.)892    fastField.Advance(0, byteCount);893    ch = io.GetNextNonBlank(byteCount, &fastField);894  }895  eatComma_ = true;896  if (maxRepeat == 0 && !oldHasEnd) {897    io.GetIoErrorHandler().SetHasEnd(false);898  }899  if (!ch) { // EOF900    if (maxRepeat == 0) {901      return edit; // DataEdit::ListDirected for look-ahead902    } else {903      return common::nullopt;904    }905  } else if (*ch == '/') {906    hitSlash_ = true;907    edit.descriptor = DataEdit::ListDirectedNullValue;908    return edit;909  } else if (*ch == comma) { // separator: null value910    edit.descriptor = DataEdit::ListDirectedNullValue;911    return edit;912  } else if (imaginaryPart_) { // can't repeat components913    return edit;914  }915  if (*ch >= '0' && *ch <= '9' && fastField.MightBeRepetitionCount()) {916    // There's decimal digits followed by '*'.917    auto start{fastField.connection().positionInRecord};918    int r{0};919    do {920      static auto constexpr clamp{(std::numeric_limits<int>::max() - '9') / 10};921      if (r >= clamp) {922        r = 0;923        break;924      }925      r = 10 * r + (*ch - '0');926      fastField.Advance(0, byteCount);927      ch = io.GetCurrentChar(byteCount, &fastField);928    } while (ch && *ch >= '0' && *ch <= '9');929    if (r > 0 && ch && *ch == '*') { // subtle: r must be nonzero930      fastField.Advance(0, byteCount);931      ch = io.GetCurrentChar(byteCount, &fastField);932      if (ch && *ch == '/') { // r*/933        hitSlash_ = true;934        edit.descriptor = DataEdit::ListDirectedNullValue;935        return edit;936      }937      if (!ch || *ch == ' ' || *ch == '\t' || *ch == comma) { // "r*" null938        edit.descriptor = DataEdit::ListDirectedNullValue;939      }940      edit.repeat = std::min<int>(r, maxRepeat);941      remaining_ = r - edit.repeat;942      if (remaining_ > 0) {943        repeatPosition_.emplace(io);944      }945    } else { // not a repetition count, just an integer value; rewind946      fastField.connection().positionInRecord = start;947    }948  }949  if (!imaginaryPart_ && edit.descriptor == DataEdit::ListDirected && ch &&950      *ch == '(') {951    if (maxRepeat > 0) { // not being peeked at fram DefinedFormattedIo()952      realPart_ = true;953      fastField.connection().HandleRelativePosition(byteCount);954    }955    edit.descriptor = DataEdit::ListDirectedRealPart;956  }957  return edit;958}959 960template <Direction DIR>961int ExternalListIoStatementState<DIR>::EndIoStatement() {962  if constexpr (DIR == Direction::Input) {963    if (auto status{ListDirectedStatementState<DIR>::EndIoStatement()};964        status != IostatOk) {965      return status;966    }967  }968  return ExternalIoStatementState<DIR>::EndIoStatement();969}970 971template <Direction DIR>972bool ExternalUnformattedIoStatementState<DIR>::Receive(973    char *data, std::size_t bytes, std::size_t elementBytes) {974  if constexpr (DIR == Direction::Output) {975    this->Crash("ExternalUnformattedIoStatementState::Receive() called for "976                "output statement");977  }978  return this->unit().Receive(data, bytes, elementBytes, *this);979}980 981template <Direction DIR>982ChildIoStatementState<DIR>::ChildIoStatementState(983    ChildIo &child, const char *sourceFile, int sourceLine)984    : IoStatementBase{sourceFile, sourceLine}, child_{child},985      mutableModes_{child.parent().mutableModes()} {}986 987template <Direction DIR>988const NonTbpDefinedIoTable *989ChildIoStatementState<DIR>::nonTbpDefinedIoTable() const {990#if !defined(RT_DEVICE_AVOID_RECURSION)991  return child_.parent().nonTbpDefinedIoTable();992#else993  ReportUnsupportedChildIo();994#endif995}996 997template <Direction DIR>998void ChildIoStatementState<DIR>::set_nonTbpDefinedIoTable(999    const NonTbpDefinedIoTable *table) {1000#if !defined(RT_DEVICE_AVOID_RECURSION)1001  child_.parent().set_nonTbpDefinedIoTable(table);1002#else1003  ReportUnsupportedChildIo();1004#endif1005}1006 1007template <Direction DIR>1008ConnectionState &ChildIoStatementState<DIR>::GetConnectionState() {1009#if !defined(RT_DEVICE_AVOID_RECURSION)1010  return child_.parent().GetConnectionState();1011#else1012  ReportUnsupportedChildIo();1013#endif1014}1015 1016template <Direction DIR>1017ExternalFileUnit *ChildIoStatementState<DIR>::GetExternalFileUnit() const {1018#if !defined(RT_DEVICE_AVOID_RECURSION)1019  return child_.parent().GetExternalFileUnit();1020#else1021  ReportUnsupportedChildIo();1022#endif1023}1024 1025template <Direction DIR> int ChildIoStatementState<DIR>::EndIoStatement() {1026  CompleteOperation();1027  auto result{IoStatementBase::EndIoStatement()};1028  child_.EndIoStatement(); // annihilates *this in child_.u_1029  return result;1030}1031 1032template <Direction DIR>1033bool ChildIoStatementState<DIR>::Emit(1034    const char *data, std::size_t bytes, std::size_t elementBytes) {1035#if !defined(RT_DEVICE_AVOID_RECURSION)1036  return child_.parent().Emit(data, bytes, elementBytes);1037#else1038  ReportUnsupportedChildIo();1039#endif1040}1041 1042template <Direction DIR>1043std::size_t ChildIoStatementState<DIR>::GetNextInputBytes(const char *&p) {1044#if !defined(RT_DEVICE_AVOID_RECURSION)1045  return child_.parent().GetNextInputBytes(p);1046#else1047  ReportUnsupportedChildIo();1048#endif1049}1050 1051template <Direction DIR>1052void ChildIoStatementState<DIR>::HandleAbsolutePosition(std::int64_t n) {1053#if !defined(RT_DEVICE_AVOID_RECURSION)1054  return child_.parent().HandleAbsolutePosition(n);1055#else1056  ReportUnsupportedChildIo();1057#endif1058}1059 1060template <Direction DIR>1061void ChildIoStatementState<DIR>::HandleRelativePosition(std::int64_t n) {1062#if !defined(RT_DEVICE_AVOID_RECURSION)1063  return child_.parent().HandleRelativePosition(n);1064#else1065  ReportUnsupportedChildIo();1066#endif1067}1068 1069template <Direction DIR, typename CHAR>1070ChildFormattedIoStatementState<DIR, CHAR>::ChildFormattedIoStatementState(1071    ChildIo &child, const CHAR *format, std::size_t formatLength,1072    const Descriptor *formatDescriptor, const char *sourceFile, int sourceLine)1073    : ChildIoStatementState<DIR>{child, sourceFile, sourceLine},1074      format_{*this, format, formatLength, formatDescriptor} {}1075 1076template <Direction DIR, typename CHAR>1077void ChildFormattedIoStatementState<DIR, CHAR>::CompleteOperation() {1078  if (!this->completedOperation()) {1079    format_.Finish(*this);1080    ChildIoStatementState<DIR>::CompleteOperation();1081  }1082}1083 1084template <Direction DIR, typename CHAR>1085int ChildFormattedIoStatementState<DIR, CHAR>::EndIoStatement() {1086  if constexpr (DIR == Direction::Input) {1087    if (auto *listInput{this->child()1088                .parent()1089                .template get_if<1090                    ListDirectedStatementState<Direction::Input>>()}) {1091      listInput->set_eatComma(false);1092    }1093  }1094  CompleteOperation();1095  return ChildIoStatementState<DIR>::EndIoStatement();1096}1097 1098template <Direction DIR, typename CHAR>1099bool ChildFormattedIoStatementState<DIR, CHAR>::AdvanceRecord(int n) {1100#if !defined(RT_DEVICE_AVOID_RECURSION)1101  return this->child().parent().AdvanceRecord(n);1102#else1103  this->ReportUnsupportedChildIo();1104#endif1105}1106 1107template <Direction DIR>1108ChildListIoStatementState<DIR>::ChildListIoStatementState(1109    ChildIo &child, const char *sourceFile, int sourceLine)1110    : ChildIoStatementState<DIR>{child, sourceFile, sourceLine} {1111#if !defined(RT_DEVICE_AVOID_RECURSION)1112  if (const auto *listParent{1113          child.parent().get_if<ListDirectedStatementState<DIR>>()}) {1114    if constexpr (DIR == Direction::Input) {1115      this->set_eatComma(listParent->eatComma());1116      this->namelistGroup_ = listParent->namelistGroup();1117    }1118    if (auto *childListParent{1119            child.parent().get_if<ChildListIoStatementState<DIR>>()}) {1120      // Child list I/O whose parent is child list I/O: can advance1121      // if the parent can.1122      this->canAdvance_ = childListParent->CanAdvance();1123    } else {1124      // Child list I/O of top-level list I/O: can advance.1125      this->canAdvance_ = true;1126    }1127  }1128#else1129  this->ReportUnsupportedChildIo();1130#endif1131}1132 1133template <Direction DIR>1134bool ChildListIoStatementState<DIR>::AdvanceRecord(int n) {1135#if !defined(RT_DEVICE_AVOID_RECURSION)1136  return this->CanAdvance() && this->child().parent().AdvanceRecord(n);1137#else1138  this->ReportUnsupportedChildIo();1139#endif1140}1141 1142template <Direction DIR> int ChildListIoStatementState<DIR>::EndIoStatement() {1143  if constexpr (DIR == Direction::Input) {1144    if (auto *listInput{this->child()1145                .parent()1146                .template get_if<1147                    ListDirectedStatementState<Direction::Input>>()}) {1148      listInput->set_eatComma(this->eatComma());1149      listInput->set_hitSlash(this->hitSlash());1150    }1151    if (int status{ListDirectedStatementState<DIR>::EndIoStatement()};1152        status != IostatOk) {1153      return status;1154    }1155  }1156  return ChildIoStatementState<DIR>::EndIoStatement();1157}1158 1159template <Direction DIR>1160bool ChildUnformattedIoStatementState<DIR>::Receive(1161    char *data, std::size_t bytes, std::size_t elementBytes) {1162#if !defined(RT_DEVICE_AVOID_RECURSION)1163  return this->child().parent().Receive(data, bytes, elementBytes);1164#else1165  this->ReportUnsupportedChildIo();1166#endif1167}1168 1169template class InternalIoStatementState<Direction::Output>;1170template class InternalIoStatementState<Direction::Input>;1171template class InternalFormattedIoStatementState<Direction::Output>;1172template class InternalFormattedIoStatementState<Direction::Input>;1173template class InternalListIoStatementState<Direction::Output>;1174template class InternalListIoStatementState<Direction::Input>;1175template class ExternalIoStatementState<Direction::Output>;1176template class ExternalIoStatementState<Direction::Input>;1177template class ExternalFormattedIoStatementState<Direction::Output>;1178template class ExternalFormattedIoStatementState<Direction::Input>;1179template class ExternalListIoStatementState<Direction::Output>;1180template class ExternalListIoStatementState<Direction::Input>;1181template class ExternalUnformattedIoStatementState<Direction::Output>;1182template class ExternalUnformattedIoStatementState<Direction::Input>;1183template class ChildIoStatementState<Direction::Output>;1184template class ChildIoStatementState<Direction::Input>;1185template class ChildFormattedIoStatementState<Direction::Output>;1186template class ChildFormattedIoStatementState<Direction::Input>;1187template class ChildListIoStatementState<Direction::Output>;1188template class ChildListIoStatementState<Direction::Input>;1189template class ChildUnformattedIoStatementState<Direction::Output>;1190template class ChildUnformattedIoStatementState<Direction::Input>;1191 1192void ExternalMiscIoStatementState::CompleteOperation() {1193  if (completedOperation()) {1194    return;1195  }1196  ExternalFileUnit &ext{unit()};1197  switch (which_) {1198  case Flush:1199    ext.FlushOutput(*this);1200#if !defined(RT_DEVICE_COMPILATION)1201    std::fflush(nullptr); // flushes C stdio output streams (12.9(2))1202#endif1203    break;1204  case Backspace:1205    ext.BackspaceRecord(*this);1206    break;1207  case Endfile:1208    ext.Endfile(*this);1209    break;1210  case Rewind:1211    ext.Rewind(*this);1212    break;1213  case Wait:1214    break; // handled in io-api.cpp BeginWait1215  }1216  return IoStatementBase::CompleteOperation();1217}1218 1219int ExternalMiscIoStatementState::EndIoStatement() {1220  CompleteOperation();1221  return ExternalIoStatementBase::EndIoStatement();1222}1223 1224InquireUnitState::InquireUnitState(1225    ExternalFileUnit &unit, const char *sourceFile, int sourceLine)1226    : ExternalIoStatementBase{unit, sourceFile, sourceLine} {}1227 1228bool InquireUnitState::Inquire(1229    InquiryKeywordHash inquiry, char *result, std::size_t length) {1230  if (unit().createdForInternalChildIo()) {1231    SignalError(IostatInquireInternalUnit,1232        "INQUIRE of unit created for defined derived type I/O of an internal "1233        "unit");1234    return false;1235  }1236  const char *str{nullptr};1237  switch (inquiry) {1238  case HashInquiryKeyword("ACCESS"):1239    if (!unit().IsConnected()) {1240      str = "UNDEFINED";1241    } else {1242      switch (unit().access) {1243      case Access::Sequential:1244        str = "SEQUENTIAL";1245        break;1246      case Access::Direct:1247        str = "DIRECT";1248        break;1249      case Access::Stream:1250        str = "STREAM";1251        break;1252      }1253    }1254    break;1255  case HashInquiryKeyword("ACTION"):1256    str = !unit().IsConnected() ? "UNDEFINED"1257        : unit().mayWrite()     ? unit().mayRead() ? "READWRITE" : "WRITE"1258                                : "READ";1259    break;1260  case HashInquiryKeyword("ASYNCHRONOUS"):1261    str = !unit().IsConnected()    ? "UNDEFINED"1262        : unit().mayAsynchronous() ? "YES"1263                                   : "NO";1264    break;1265  case HashInquiryKeyword("BLANK"):1266    str = !unit().IsConnected() || unit().isUnformatted.value_or(true)1267        ? "UNDEFINED"1268        : mutableModes().editingFlags & blankZero ? "ZERO"1269                                                  : "NULL";1270    break;1271  case HashInquiryKeyword("CARRIAGECONTROL"):1272    str = "LIST";1273    break;1274  case HashInquiryKeyword("CONVERT"):1275    str = unit().swapEndianness() ? "SWAP" : "NATIVE";1276    break;1277  case HashInquiryKeyword("DECIMAL"):1278    str = !unit().IsConnected() || unit().isUnformatted.value_or(true)1279        ? "UNDEFINED"1280        : mutableModes().editingFlags & decimalComma ? "COMMA"1281                                                     : "POINT";1282    break;1283  case HashInquiryKeyword("DELIM"):1284    if (!unit().IsConnected() || unit().isUnformatted.value_or(true)) {1285      str = "UNDEFINED";1286    } else {1287      switch (mutableModes().delim) {1288      case '\'':1289        str = "APOSTROPHE";1290        break;1291      case '"':1292        str = "QUOTE";1293        break;1294      default:1295        str = "NONE";1296        break;1297      }1298    }1299    break;1300  case HashInquiryKeyword("DIRECT"):1301    str = !unit().IsConnected() ? "UNKNOWN"1302        : unit().access == Access::Direct ||1303            (unit().mayPosition() && unit().openRecl)1304        ? "YES"1305        : "NO";1306    break;1307  case HashInquiryKeyword("ENCODING"):1308    str = !unit().IsConnected()               ? "UNKNOWN"1309        : unit().isUnformatted.value_or(true) ? "UNDEFINED"1310        : unit().isUTF8                       ? "UTF-8"1311                                              : "ASCII";1312    break;1313  case HashInquiryKeyword("FORM"):1314    str = !unit().IsConnected() || !unit().isUnformatted ? "UNDEFINED"1315        : *unit().isUnformatted                          ? "UNFORMATTED"1316                                                         : "FORMATTED";1317    break;1318  case HashInquiryKeyword("FORMATTED"):1319    str = !unit().IsConnected() ? "UNDEFINED"1320        : !unit().isUnformatted ? "UNKNOWN"1321        : *unit().isUnformatted ? "NO"1322                                : "YES";1323    break;1324  case HashInquiryKeyword("NAME"):1325    str = unit().path();1326    if (!str) {1327      return true; // result is undefined1328    }1329    break;1330  case HashInquiryKeyword("PAD"):1331    str = !unit().IsConnected() || unit().isUnformatted.value_or(true)1332        ? "UNDEFINED"1333        : mutableModes().pad ? "YES"1334                             : "NO";1335    break;1336  case HashInquiryKeyword("POSITION"):1337    if (!unit().IsConnected() || unit().access == Access::Direct) {1338      str = "UNDEFINED";1339    } else {1340      switch (unit().InquirePosition()) {1341      case Position::Rewind:1342        str = "REWIND";1343        break;1344      case Position::Append:1345        str = "APPEND";1346        break;1347      case Position::AsIs:1348        str = "ASIS";1349        break;1350      }1351    }1352    break;1353  case HashInquiryKeyword("READ"):1354    str = !unit().IsConnected() ? "UNDEFINED" : unit().mayRead() ? "YES" : "NO";1355    break;1356  case HashInquiryKeyword("READWRITE"):1357    str = !unit().IsConnected()                 ? "UNDEFINED"1358        : unit().mayRead() && unit().mayWrite() ? "YES"1359                                                : "NO";1360    break;1361  case HashInquiryKeyword("ROUND"):1362    if (!unit().IsConnected() || unit().isUnformatted.value_or(true)) {1363      str = "UNDEFINED";1364    } else {1365      switch (mutableModes().round) {1366      case decimal::FortranRounding::RoundNearest:1367        str = "NEAREST";1368        break;1369      case decimal::FortranRounding::RoundUp:1370        str = "UP";1371        break;1372      case decimal::FortranRounding::RoundDown:1373        str = "DOWN";1374        break;1375      case decimal::FortranRounding::RoundToZero:1376        str = "ZERO";1377        break;1378      case decimal::FortranRounding::RoundCompatible:1379        str = "COMPATIBLE";1380        break;1381      }1382    }1383    break;1384  case HashInquiryKeyword("SEQUENTIAL"):1385    // "NO" for Direct, since Sequential would not work if1386    // the unit were reopened without RECL=.1387    str = !unit().IsConnected()               ? "UNKNOWN"1388        : unit().access == Access::Sequential ? "YES"1389                                              : "NO";1390    break;1391  case HashInquiryKeyword("SIGN"):1392    str = !unit().IsConnected() || unit().isUnformatted.value_or(true)1393        ? "UNDEFINED"1394        : mutableModes().editingFlags & signPlus ? "PLUS"1395                                                 : "SUPPRESS";1396    break;1397  case HashInquiryKeyword("STREAM"):1398    str = !unit().IsConnected()           ? "UNKNOWN"1399        : unit().access == Access::Stream ? "YES"1400                                          : "NO";1401    break;1402  case HashInquiryKeyword("UNFORMATTED"):1403    str = !unit().IsConnected() || !unit().isUnformatted ? "UNKNOWN"1404        : *unit().isUnformatted                          ? "YES"1405                                                         : "NO";1406    break;1407  case HashInquiryKeyword("WRITE"):1408    str = !unit().IsConnected() ? "UNKNOWN" : unit().mayWrite() ? "YES" : "NO";1409    break;1410  }1411  if (str) {1412    ToFortranDefaultCharacter(result, length, str);1413    return true;1414  } else {1415    BadInquiryKeywordHashCrash(inquiry);1416    return false;1417  }1418}1419 1420bool InquireUnitState::Inquire(InquiryKeywordHash inquiry, bool &result) {1421  switch (inquiry) {1422  case HashInquiryKeyword("EXIST"):1423    result = true;1424    return true;1425  case HashInquiryKeyword("NAMED"):1426    result = unit().path() != nullptr;1427    return true;1428  case HashInquiryKeyword("OPENED"):1429    result = unit().IsConnected();1430    return true;1431  case HashInquiryKeyword("PENDING"):1432    result = false; // asynchronous I/O is not implemented1433    return true;1434  default:1435    BadInquiryKeywordHashCrash(inquiry);1436    return false;1437  }1438}1439 1440bool InquireUnitState::Inquire(1441    InquiryKeywordHash inquiry, std::int64_t, bool &result) {1442  switch (inquiry) {1443  case HashInquiryKeyword("PENDING"):1444    result = false; // asynchronous I/O is not implemented1445    return true;1446  default:1447    BadInquiryKeywordHashCrash(inquiry);1448    return false;1449  }1450}1451 1452bool InquireUnitState::Inquire(1453    InquiryKeywordHash inquiry, std::int64_t &result) {1454  switch (inquiry) {1455  case HashInquiryKeyword("NEXTREC"):1456    if (unit().access == Access::Direct) {1457      result = unit().currentRecordNumber;1458    }1459    return true;1460  case HashInquiryKeyword("NUMBER"):1461    result = unit().unitNumber();1462    return true;1463  case HashInquiryKeyword("POS"):1464    result = unit().InquirePos();1465    return true;1466  case HashInquiryKeyword("RECL"):1467    if (!unit().IsConnected()) {1468      result = -1;1469    } else if (unit().access == Access::Stream) {1470      result = -2;1471    } else if (unit().openRecl) {1472      result = *unit().openRecl;1473    } else {1474      result = std::numeric_limits<std::int32_t>::max();1475    }1476    return true;1477  case HashInquiryKeyword("SIZE"):1478    result = -1;1479    if (unit().IsConnected()) {1480      unit().FlushOutput(*this);1481      if (auto size{unit().knownSize()}) {1482        result = *size;1483      }1484    }1485    return true;1486  default:1487    BadInquiryKeywordHashCrash(inquiry);1488    return false;1489  }1490}1491 1492InquireNoUnitState::InquireNoUnitState(1493    const char *sourceFile, int sourceLine, int badUnitNumber)1494    : NoUnitIoStatementState{*this, sourceFile, sourceLine, badUnitNumber} {}1495 1496bool InquireNoUnitState::Inquire(1497    InquiryKeywordHash inquiry, char *result, std::size_t length) {1498  switch (inquiry) {1499  case HashInquiryKeyword("ACCESS"):1500  case HashInquiryKeyword("ACTION"):1501  case HashInquiryKeyword("ASYNCHRONOUS"):1502  case HashInquiryKeyword("BLANK"):1503  case HashInquiryKeyword("CARRIAGECONTROL"):1504  case HashInquiryKeyword("CONVERT"):1505  case HashInquiryKeyword("DECIMAL"):1506  case HashInquiryKeyword("DELIM"):1507  case HashInquiryKeyword("FORM"):1508  case HashInquiryKeyword("NAME"):1509  case HashInquiryKeyword("PAD"):1510  case HashInquiryKeyword("POSITION"):1511  case HashInquiryKeyword("ROUND"):1512  case HashInquiryKeyword("SIGN"):1513    ToFortranDefaultCharacter(result, length, "UNDEFINED");1514    return true;1515  case HashInquiryKeyword("DIRECT"):1516  case HashInquiryKeyword("ENCODING"):1517  case HashInquiryKeyword("FORMATTED"):1518  case HashInquiryKeyword("READ"):1519  case HashInquiryKeyword("READWRITE"):1520  case HashInquiryKeyword("SEQUENTIAL"):1521  case HashInquiryKeyword("STREAM"):1522  case HashInquiryKeyword("WRITE"):1523  case HashInquiryKeyword("UNFORMATTED"):1524    ToFortranDefaultCharacter(result, length, "UNKNOWN");1525    return true;1526  default:1527    BadInquiryKeywordHashCrash(inquiry);1528    return false;1529  }1530}1531 1532bool InquireNoUnitState::Inquire(InquiryKeywordHash inquiry, bool &result) {1533  switch (inquiry) {1534  case HashInquiryKeyword("EXIST"):1535    result = badUnitNumber() >= 0;1536    return true;1537  case HashInquiryKeyword("NAMED"):1538  case HashInquiryKeyword("OPENED"):1539  case HashInquiryKeyword("PENDING"):1540    result = false;1541    return true;1542  default:1543    BadInquiryKeywordHashCrash(inquiry);1544    return false;1545  }1546}1547 1548bool InquireNoUnitState::Inquire(1549    InquiryKeywordHash inquiry, std::int64_t, bool &result) {1550  switch (inquiry) {1551  case HashInquiryKeyword("PENDING"):1552    result = false;1553    return true;1554  default:1555    BadInquiryKeywordHashCrash(inquiry);1556    return false;1557  }1558}1559 1560bool InquireNoUnitState::Inquire(1561    InquiryKeywordHash inquiry, std::int64_t &result) {1562  switch (inquiry) {1563  case HashInquiryKeyword("NUMBER"):1564    result = badUnitNumber();1565    return true;1566  case HashInquiryKeyword("NEXTREC"):1567  case HashInquiryKeyword("POS"):1568  case HashInquiryKeyword("RECL"):1569  case HashInquiryKeyword("SIZE"):1570    result = -1;1571    return true;1572  default:1573    BadInquiryKeywordHashCrash(inquiry);1574    return false;1575  }1576}1577 1578InquireUnconnectedFileState::InquireUnconnectedFileState(1579    OwningPtr<char> &&path, const char *sourceFile, int sourceLine)1580    : NoUnitIoStatementState{*this, sourceFile, sourceLine}, path_{std::move(1581                                                                 path)} {}1582 1583bool InquireUnconnectedFileState::Inquire(1584    InquiryKeywordHash inquiry, char *result, std::size_t length) {1585  const char *str{nullptr};1586  switch (inquiry) {1587  case HashInquiryKeyword("ACCESS"):1588  case HashInquiryKeyword("ACTION"):1589  case HashInquiryKeyword("ASYNCHRONOUS"):1590  case HashInquiryKeyword("BLANK"):1591  case HashInquiryKeyword("CARRIAGECONTROL"):1592  case HashInquiryKeyword("CONVERT"):1593  case HashInquiryKeyword("DECIMAL"):1594  case HashInquiryKeyword("DELIM"):1595  case HashInquiryKeyword("FORM"):1596  case HashInquiryKeyword("PAD"):1597  case HashInquiryKeyword("POSITION"):1598  case HashInquiryKeyword("ROUND"):1599  case HashInquiryKeyword("SIGN"):1600    str = "UNDEFINED";1601    break;1602  case HashInquiryKeyword("DIRECT"):1603  case HashInquiryKeyword("ENCODING"):1604  case HashInquiryKeyword("FORMATTED"):1605  case HashInquiryKeyword("SEQUENTIAL"):1606  case HashInquiryKeyword("STREAM"):1607  case HashInquiryKeyword("UNFORMATTED"):1608    str = "UNKNOWN";1609    break;1610  case HashInquiryKeyword("READ"):1611    str =1612        IsExtant(path_.get()) ? MayRead(path_.get()) ? "YES" : "NO" : "UNKNOWN";1613    break;1614  case HashInquiryKeyword("READWRITE"):1615    str = IsExtant(path_.get()) ? MayReadAndWrite(path_.get()) ? "YES" : "NO"1616                                : "UNKNOWN";1617    break;1618  case HashInquiryKeyword("WRITE"):1619    str = IsExtant(path_.get()) ? MayWrite(path_.get()) ? "YES" : "NO"1620                                : "UNKNOWN";1621    break;1622  case HashInquiryKeyword("NAME"):1623    str = path_.get();1624    if (!str) {1625      return true; // result is undefined1626    }1627    break;1628  }1629  if (str) {1630    ToFortranDefaultCharacter(result, length, str);1631    return true;1632  } else {1633    BadInquiryKeywordHashCrash(inquiry);1634    return false;1635  }1636}1637 1638bool InquireUnconnectedFileState::Inquire(1639    InquiryKeywordHash inquiry, bool &result) {1640  switch (inquiry) {1641  case HashInquiryKeyword("EXIST"):1642    result = IsExtant(path_.get());1643    return true;1644  case HashInquiryKeyword("NAMED"):1645    result = true;1646    return true;1647  case HashInquiryKeyword("OPENED"):1648    result = false;1649    return true;1650  case HashInquiryKeyword("PENDING"):1651    result = false;1652    return true;1653  default:1654    BadInquiryKeywordHashCrash(inquiry);1655    return false;1656  }1657}1658 1659bool InquireUnconnectedFileState::Inquire(1660    InquiryKeywordHash inquiry, std::int64_t, bool &result) {1661  switch (inquiry) {1662  case HashInquiryKeyword("PENDING"):1663    result = false;1664    return true;1665  default:1666    BadInquiryKeywordHashCrash(inquiry);1667    return false;1668  }1669}1670 1671bool InquireUnconnectedFileState::Inquire(1672    InquiryKeywordHash inquiry, std::int64_t &result) {1673  switch (inquiry) {1674  case HashInquiryKeyword("NEXTREC"):1675  case HashInquiryKeyword("NUMBER"):1676  case HashInquiryKeyword("POS"):1677  case HashInquiryKeyword("RECL"):1678    result = -1;1679    return true;1680  case HashInquiryKeyword("SIZE"):1681    result = SizeInBytes(path_.get());1682    return true;1683  default:1684    BadInquiryKeywordHashCrash(inquiry);1685    return false;1686  }1687}1688 1689InquireIOLengthState::InquireIOLengthState(1690    const char *sourceFile, int sourceLine)1691    : NoUnitIoStatementState{*this, sourceFile, sourceLine} {}1692 1693bool InquireIOLengthState::Emit(const char *, std::size_t bytes, std::size_t) {1694  bytes_ += bytes;1695  return true;1696}1697 1698int ErroneousIoStatementState::EndIoStatement() {1699  SignalPendingError();1700  if (unit_) {1701    unit_->EndIoStatement();1702  }1703  return IoStatementBase::EndIoStatement();1704}1705 1706RT_OFFLOAD_API_GROUP_END1707} // namespace Fortran::runtime::io1708