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