[flang] Accept polymorphic component element in storage_size
[llvm-project.git] / flang / runtime / io-stmt.cpp
blob9467822d8e4108bef5571b779ef9c73b0fb29d46
1 //===-- runtime/io-stmt.cpp -----------------------------------------------===//
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-exception
6 //
7 //===----------------------------------------------------------------------===//
9 #include "io-stmt.h"
10 #include "connection.h"
11 #include "emit-encoded.h"
12 #include "format.h"
13 #include "tools.h"
14 #include "unit.h"
15 #include "utf.h"
16 #include "flang/Runtime/memory.h"
17 #include <algorithm>
18 #include <cstdio>
19 #include <cstring>
20 #include <limits>
21 #include <type_traits>
23 namespace Fortran::runtime::io {
25 bool IoStatementBase::Emit(const char *, std::size_t, std::size_t) {
26 return false;
29 std::size_t IoStatementBase::GetNextInputBytes(const char *&p) {
30 p = nullptr;
31 return 0;
34 bool IoStatementBase::AdvanceRecord(int) { return false; }
36 void IoStatementBase::BackspaceRecord() {}
38 bool IoStatementBase::Receive(char *, std::size_t, std::size_t) {
39 return false;
42 std::optional<DataEdit> IoStatementBase::GetNextDataEdit(
43 IoStatementState &, int) {
44 return std::nullopt;
47 ExternalFileUnit *IoStatementBase::GetExternalFileUnit() const {
48 return nullptr;
51 bool IoStatementBase::BeginReadingRecord() { return true; }
53 void IoStatementBase::FinishReadingRecord() {}
55 void IoStatementBase::HandleAbsolutePosition(std::int64_t) {}
57 void IoStatementBase::HandleRelativePosition(std::int64_t) {}
59 bool IoStatementBase::Inquire(InquiryKeywordHash, char *, std::size_t) {
60 return false;
63 bool IoStatementBase::Inquire(InquiryKeywordHash, bool &) { return false; }
65 bool IoStatementBase::Inquire(InquiryKeywordHash, std::int64_t, bool &) {
66 return false;
69 bool IoStatementBase::Inquire(InquiryKeywordHash, std::int64_t &) {
70 return false;
73 std::int64_t IoStatementBase::InquirePos() { return 0; }
75 void IoStatementBase::BadInquiryKeywordHashCrash(InquiryKeywordHash inquiry) {
76 char buffer[16];
77 const char *decode{InquiryKeywordHashDecode(buffer, sizeof buffer, inquiry)};
78 Crash("Bad InquiryKeywordHash 0x%x (%s)", inquiry,
79 decode ? decode : "(cannot decode)");
82 template <Direction DIR>
83 InternalIoStatementState<DIR>::InternalIoStatementState(
84 Buffer scalar, std::size_t length, const char *sourceFile, int sourceLine)
85 : IoStatementBase{sourceFile, sourceLine}, unit_{scalar, length, 1} {}
87 template <Direction DIR>
88 InternalIoStatementState<DIR>::InternalIoStatementState(
89 const Descriptor &d, const char *sourceFile, int sourceLine)
90 : IoStatementBase{sourceFile, sourceLine}, unit_{d, *this} {}
92 template <Direction DIR>
93 bool InternalIoStatementState<DIR>::Emit(
94 const char *data, std::size_t bytes, std::size_t /*elementBytes*/) {
95 if constexpr (DIR == Direction::Input) {
96 Crash("InternalIoStatementState<Direction::Input>::Emit() called");
97 return false;
99 return unit_.Emit(data, bytes, *this);
102 template <Direction DIR>
103 std::size_t InternalIoStatementState<DIR>::GetNextInputBytes(const char *&p) {
104 return unit_.GetNextInputBytes(p, *this);
107 template <Direction DIR>
108 bool InternalIoStatementState<DIR>::AdvanceRecord(int n) {
109 while (n-- > 0) {
110 if (!unit_.AdvanceRecord(*this)) {
111 return false;
114 return true;
117 template <Direction DIR> void InternalIoStatementState<DIR>::BackspaceRecord() {
118 unit_.BackspaceRecord(*this);
121 template <Direction DIR> int InternalIoStatementState<DIR>::EndIoStatement() {
122 if constexpr (DIR == Direction::Output) {
123 unit_.EndIoStatement(); // fill
125 auto result{IoStatementBase::EndIoStatement()};
126 if (free_) {
127 FreeMemory(this);
129 return result;
132 template <Direction DIR>
133 void InternalIoStatementState<DIR>::HandleAbsolutePosition(std::int64_t n) {
134 return unit_.HandleAbsolutePosition(n);
137 template <Direction DIR>
138 void InternalIoStatementState<DIR>::HandleRelativePosition(std::int64_t n) {
139 return unit_.HandleRelativePosition(n);
142 template <Direction DIR>
143 std::int64_t InternalIoStatementState<DIR>::InquirePos() {
144 return unit_.InquirePos();
147 template <Direction DIR, typename CHAR>
148 InternalFormattedIoStatementState<DIR, CHAR>::InternalFormattedIoStatementState(
149 Buffer buffer, std::size_t length, const CharType *format,
150 std::size_t formatLength, const Descriptor *formatDescriptor,
151 const char *sourceFile, int sourceLine)
152 : InternalIoStatementState<DIR>{buffer, length, sourceFile, sourceLine},
153 ioStatementState_{*this}, format_{*this, format, formatLength,
154 formatDescriptor} {}
156 template <Direction DIR, typename CHAR>
157 InternalFormattedIoStatementState<DIR, CHAR>::InternalFormattedIoStatementState(
158 const Descriptor &d, const CharType *format, std::size_t formatLength,
159 const Descriptor *formatDescriptor, const char *sourceFile, int sourceLine)
160 : InternalIoStatementState<DIR>{d, sourceFile, sourceLine},
161 ioStatementState_{*this}, format_{*this, format, formatLength,
162 formatDescriptor} {}
164 template <Direction DIR, typename CHAR>
165 void InternalFormattedIoStatementState<DIR, CHAR>::CompleteOperation() {
166 if (!this->completedOperation()) {
167 if constexpr (DIR == Direction::Output) {
168 format_.Finish(*this); // ignore any remaining input positioning actions
170 IoStatementBase::CompleteOperation();
174 template <Direction DIR, typename CHAR>
175 int InternalFormattedIoStatementState<DIR, CHAR>::EndIoStatement() {
176 CompleteOperation();
177 return InternalIoStatementState<DIR>::EndIoStatement();
180 template <Direction DIR>
181 InternalListIoStatementState<DIR>::InternalListIoStatementState(
182 Buffer buffer, std::size_t length, const char *sourceFile, int sourceLine)
183 : InternalIoStatementState<DIR>{buffer, length, sourceFile, sourceLine},
184 ioStatementState_{*this} {}
186 template <Direction DIR>
187 InternalListIoStatementState<DIR>::InternalListIoStatementState(
188 const Descriptor &d, const char *sourceFile, int sourceLine)
189 : InternalIoStatementState<DIR>{d, sourceFile, sourceLine},
190 ioStatementState_{*this} {}
192 ExternalIoStatementBase::ExternalIoStatementBase(
193 ExternalFileUnit &unit, const char *sourceFile, int sourceLine)
194 : IoStatementBase{sourceFile, sourceLine}, unit_{unit} {}
196 MutableModes &ExternalIoStatementBase::mutableModes() {
197 if (const ChildIo * child{unit_.GetChildIo()}) {
198 return child->parent().mutableModes();
200 return unit_.modes;
203 ConnectionState &ExternalIoStatementBase::GetConnectionState() { return unit_; }
205 int ExternalIoStatementBase::EndIoStatement() {
206 CompleteOperation();
207 auto result{IoStatementBase::EndIoStatement()};
208 unit_.EndIoStatement(); // annihilates *this in unit_.u_
209 return result;
212 void ExternalIoStatementBase::SetAsynchronous() {
213 asynchronousID_ = unit().GetAsynchronousId(*this);
216 std::int64_t ExternalIoStatementBase::InquirePos() {
217 return unit_.InquirePos();
220 void OpenStatementState::set_path(const char *path, std::size_t length) {
221 pathLength_ = TrimTrailingSpaces(path, length);
222 path_ = SaveDefaultCharacter(path, pathLength_, *this);
225 void OpenStatementState::CompleteOperation() {
226 if (completedOperation()) {
227 return;
229 if (position_) {
230 if (access_ && *access_ == Access::Direct) {
231 SignalError("POSITION= may not be set with ACCESS='DIRECT'");
232 position_.reset();
235 if (status_) { // 12.5.6.10
236 if ((*status_ == OpenStatus::New || *status_ == OpenStatus::Replace) &&
237 !path_.get()) {
238 SignalError("FILE= required on OPEN with STATUS='NEW' or 'REPLACE'");
239 } else if (*status_ == OpenStatus::Scratch && path_.get()) {
240 SignalError("FILE= may not appear on OPEN with STATUS='SCRATCH'");
243 if (path_.get() || wasExtant_ ||
244 (status_ && *status_ == OpenStatus::Scratch)) {
245 unit().OpenUnit(status_, action_, position_.value_or(Position::AsIs),
246 std::move(path_), pathLength_, convert_, *this);
247 } else {
248 unit().OpenAnonymousUnit(
249 status_, action_, position_.value_or(Position::AsIs), convert_, *this);
251 if (access_) {
252 if (*access_ != unit().access) {
253 if (wasExtant_) {
254 SignalError("ACCESS= may not be changed on an open unit");
255 access_.reset();
258 if (access_) {
259 unit().access = *access_;
262 if (!unit().isUnformatted) {
263 unit().isUnformatted = isUnformatted_;
265 if (isUnformatted_ && *isUnformatted_ != *unit().isUnformatted) {
266 if (wasExtant_) {
267 SignalError("FORM= may not be changed on an open unit");
269 unit().isUnformatted = *isUnformatted_;
271 if (!unit().isUnformatted) {
272 // Set default format (C.7.4 point 2).
273 unit().isUnformatted = unit().access != Access::Sequential;
275 if (!wasExtant_ && InError()) {
276 // Release the new unit on failure
277 unit().CloseUnit(CloseStatus::Delete, *this);
278 unit().DestroyClosed();
280 IoStatementBase::CompleteOperation();
283 int OpenStatementState::EndIoStatement() {
284 CompleteOperation();
285 return ExternalIoStatementBase::EndIoStatement();
288 int CloseStatementState::EndIoStatement() {
289 CompleteOperation();
290 int result{ExternalIoStatementBase::EndIoStatement()};
291 unit().CloseUnit(status_, *this);
292 unit().DestroyClosed();
293 return result;
296 void NoUnitIoStatementState::CompleteOperation() {
297 SignalPendingError();
298 IoStatementBase::CompleteOperation();
301 int NoUnitIoStatementState::EndIoStatement() {
302 CompleteOperation();
303 auto result{IoStatementBase::EndIoStatement()};
304 FreeMemory(this);
305 return result;
308 template <Direction DIR>
309 ExternalIoStatementState<DIR>::ExternalIoStatementState(
310 ExternalFileUnit &unit, const char *sourceFile, int sourceLine)
311 : ExternalIoStatementBase{unit, sourceFile, sourceLine}, mutableModes_{
312 unit.modes} {
313 if constexpr (DIR == Direction::Output) {
314 // If the last statement was a non-advancing IO input statement, the unit
315 // furthestPositionInRecord was not advanced, but the positionInRecord may
316 // have been advanced. Advance furthestPositionInRecord here to avoid
317 // overwriting the part of the record that has been read with blanks.
318 unit.furthestPositionInRecord =
319 std::max(unit.furthestPositionInRecord, unit.positionInRecord);
323 template <Direction DIR>
324 void ExternalIoStatementState<DIR>::CompleteOperation() {
325 if (completedOperation()) {
326 return;
328 if constexpr (DIR == Direction::Input) {
329 BeginReadingRecord(); // in case there were no I/O items
330 if (mutableModes().nonAdvancing && !InError()) {
331 unit().leftTabLimit = unit().furthestPositionInRecord;
332 } else {
333 FinishReadingRecord();
335 } else { // output
336 if (mutableModes().nonAdvancing) {
337 // Make effects of positioning past the last Emit() visible with blanks.
338 std::int64_t n{unit().positionInRecord - unit().furthestPositionInRecord};
339 while (n-- > 0 && unit().Emit(" ", 1, 1, *this)) {
341 unit().leftTabLimit = unit().positionInRecord;
342 } else {
343 unit().AdvanceRecord(*this);
345 unit().FlushIfTerminal(*this);
347 return IoStatementBase::CompleteOperation();
350 template <Direction DIR> int ExternalIoStatementState<DIR>::EndIoStatement() {
351 CompleteOperation();
352 return ExternalIoStatementBase::EndIoStatement();
355 template <Direction DIR>
356 bool ExternalIoStatementState<DIR>::Emit(
357 const char *data, std::size_t bytes, std::size_t elementBytes) {
358 if constexpr (DIR == Direction::Input) {
359 Crash("ExternalIoStatementState::Emit(char) called for input statement");
361 return unit().Emit(data, bytes, elementBytes, *this);
364 template <Direction DIR>
365 std::size_t ExternalIoStatementState<DIR>::GetNextInputBytes(const char *&p) {
366 return unit().GetNextInputBytes(p, *this);
369 template <Direction DIR>
370 bool ExternalIoStatementState<DIR>::AdvanceRecord(int n) {
371 while (n-- > 0) {
372 if (!unit().AdvanceRecord(*this)) {
373 return false;
376 return true;
379 template <Direction DIR> void ExternalIoStatementState<DIR>::BackspaceRecord() {
380 unit().BackspaceRecord(*this);
383 template <Direction DIR>
384 void ExternalIoStatementState<DIR>::HandleAbsolutePosition(std::int64_t n) {
385 return unit().HandleAbsolutePosition(n);
388 template <Direction DIR>
389 void ExternalIoStatementState<DIR>::HandleRelativePosition(std::int64_t n) {
390 return unit().HandleRelativePosition(n);
393 template <Direction DIR>
394 bool ExternalIoStatementState<DIR>::BeginReadingRecord() {
395 if constexpr (DIR == Direction::Input) {
396 return unit().BeginReadingRecord(*this);
397 } else {
398 Crash("ExternalIoStatementState<Direction::Output>::BeginReadingRecord() "
399 "called");
400 return false;
404 template <Direction DIR>
405 void ExternalIoStatementState<DIR>::FinishReadingRecord() {
406 if constexpr (DIR == Direction::Input) {
407 unit().FinishReadingRecord(*this);
408 } else {
409 Crash("ExternalIoStatementState<Direction::Output>::FinishReadingRecord() "
410 "called");
414 template <Direction DIR, typename CHAR>
415 ExternalFormattedIoStatementState<DIR, CHAR>::ExternalFormattedIoStatementState(
416 ExternalFileUnit &unit, const CHAR *format, std::size_t formatLength,
417 const Descriptor *formatDescriptor, const char *sourceFile, int sourceLine)
418 : ExternalIoStatementState<DIR>{unit, sourceFile, sourceLine},
419 format_{*this, format, formatLength, formatDescriptor} {}
421 template <Direction DIR, typename CHAR>
422 void ExternalFormattedIoStatementState<DIR, CHAR>::CompleteOperation() {
423 if (this->completedOperation()) {
424 return;
426 if constexpr (DIR == Direction::Input) {
427 this->BeginReadingRecord(); // in case there were no I/O items
429 format_.Finish(*this);
430 return ExternalIoStatementState<DIR>::CompleteOperation();
433 template <Direction DIR, typename CHAR>
434 int ExternalFormattedIoStatementState<DIR, CHAR>::EndIoStatement() {
435 CompleteOperation();
436 return ExternalIoStatementState<DIR>::EndIoStatement();
439 std::optional<DataEdit> IoStatementState::GetNextDataEdit(int n) {
440 return common::visit(
441 [&](auto &x) { return x.get().GetNextDataEdit(*this, n); }, u_);
444 bool IoStatementState::Emit(
445 const char *data, std::size_t bytes, std::size_t elementBytes) {
446 return common::visit(
447 [=](auto &x) { return x.get().Emit(data, bytes, elementBytes); }, u_);
450 bool IoStatementState::Receive(
451 char *data, std::size_t n, std::size_t elementBytes) {
452 return common::visit(
453 [=](auto &x) { return x.get().Receive(data, n, elementBytes); }, u_);
456 std::size_t IoStatementState::GetNextInputBytes(const char *&p) {
457 return common::visit(
458 [&](auto &x) { return x.get().GetNextInputBytes(p); }, u_);
461 bool IoStatementState::AdvanceRecord(int n) {
462 return common::visit([=](auto &x) { return x.get().AdvanceRecord(n); }, u_);
465 void IoStatementState::BackspaceRecord() {
466 common::visit([](auto &x) { x.get().BackspaceRecord(); }, u_);
469 void IoStatementState::HandleRelativePosition(std::int64_t n) {
470 common::visit([=](auto &x) { x.get().HandleRelativePosition(n); }, u_);
473 void IoStatementState::HandleAbsolutePosition(std::int64_t n) {
474 common::visit([=](auto &x) { x.get().HandleAbsolutePosition(n); }, u_);
477 void IoStatementState::CompleteOperation() {
478 common::visit([](auto &x) { x.get().CompleteOperation(); }, u_);
481 int IoStatementState::EndIoStatement() {
482 return common::visit([](auto &x) { return x.get().EndIoStatement(); }, u_);
485 ConnectionState &IoStatementState::GetConnectionState() {
486 return common::visit(
487 [](auto &x) -> ConnectionState & { return x.get().GetConnectionState(); },
488 u_);
491 MutableModes &IoStatementState::mutableModes() {
492 return common::visit(
493 [](auto &x) -> MutableModes & { return x.get().mutableModes(); }, u_);
496 bool IoStatementState::BeginReadingRecord() {
497 return common::visit(
498 [](auto &x) { return x.get().BeginReadingRecord(); }, u_);
501 IoErrorHandler &IoStatementState::GetIoErrorHandler() const {
502 return common::visit(
503 [](auto &x) -> IoErrorHandler & {
504 return static_cast<IoErrorHandler &>(x.get());
506 u_);
509 ExternalFileUnit *IoStatementState::GetExternalFileUnit() const {
510 return common::visit(
511 [](auto &x) { return x.get().GetExternalFileUnit(); }, u_);
514 std::optional<char32_t> IoStatementState::GetCurrentChar(
515 std::size_t &byteCount) {
516 const char *p{nullptr};
517 std::size_t bytes{GetNextInputBytes(p)};
518 if (bytes == 0) {
519 byteCount = 0;
520 return std::nullopt;
521 } else {
522 const ConnectionState &connection{GetConnectionState()};
523 if (connection.isUTF8) {
524 std::size_t length{MeasureUTF8Bytes(*p)};
525 if (length <= bytes) {
526 if (auto result{DecodeUTF8(p)}) {
527 byteCount = length;
528 return result;
531 GetIoErrorHandler().SignalError(IostatUTF8Decoding);
532 // Error recovery: return the next byte
533 } else if (connection.internalIoCharKind > 1) {
534 byteCount = connection.internalIoCharKind;
535 if (byteCount == 2) {
536 return *reinterpret_cast<const char16_t *>(p);
537 } else {
538 return *reinterpret_cast<const char32_t *>(p);
541 byteCount = 1;
542 return *p;
546 std::optional<char32_t> IoStatementState::NextInField(
547 std::optional<int> &remaining, const DataEdit &edit) {
548 std::size_t byteCount{0};
549 if (!remaining) { // Stream, list-directed, or NAMELIST
550 if (auto next{GetCurrentChar(byteCount)}) {
551 if (edit.IsListDirected()) {
552 // list-directed or NAMELIST: check for separators
553 switch (*next) {
554 case ' ':
555 case '\t':
556 case '/':
557 case '(':
558 case ')':
559 case '\'':
560 case '"':
561 case '*':
562 case '\n': // for stream access
563 return std::nullopt;
564 case ',':
565 if (!(edit.modes.editingFlags & decimalComma)) {
566 return std::nullopt;
568 break;
569 case ';':
570 if (edit.modes.editingFlags & decimalComma) {
571 return std::nullopt;
573 break;
574 default:
575 break;
578 HandleRelativePosition(byteCount);
579 GotChar(byteCount);
580 return next;
582 } else if (*remaining > 0) {
583 if (auto next{GetCurrentChar(byteCount)}) {
584 if (byteCount > static_cast<std::size_t>(*remaining)) {
585 return std::nullopt;
587 *remaining -= byteCount;
588 HandleRelativePosition(byteCount);
589 GotChar(byteCount);
590 return next;
592 if (CheckForEndOfRecord()) { // do padding
593 --*remaining;
594 return std::optional<char32_t>{' '};
597 return std::nullopt;
600 bool IoStatementState::CheckForEndOfRecord() {
601 const ConnectionState &connection{GetConnectionState()};
602 if (!connection.IsAtEOF()) {
603 if (auto length{connection.EffectiveRecordLength()}) {
604 if (connection.positionInRecord >= *length) {
605 IoErrorHandler &handler{GetIoErrorHandler()};
606 const auto &modes{mutableModes()};
607 if (modes.nonAdvancing) {
608 if (connection.access == Access::Stream &&
609 connection.unterminatedRecord) {
610 // Reading final unterminated record left by a
611 // non-advancing WRITE on a stream file prior to
612 // positioning or ENDFILE.
613 handler.SignalEnd();
614 } else {
615 handler.SignalEor();
617 } else if (!modes.pad) {
618 handler.SignalError(IostatRecordReadOverrun);
620 return modes.pad; // PAD='YES'
624 return false;
627 bool IoStatementState::Inquire(
628 InquiryKeywordHash inquiry, char *out, std::size_t chars) {
629 return common::visit(
630 [&](auto &x) { return x.get().Inquire(inquiry, out, chars); }, u_);
633 bool IoStatementState::Inquire(InquiryKeywordHash inquiry, bool &out) {
634 return common::visit(
635 [&](auto &x) { return x.get().Inquire(inquiry, out); }, u_);
638 bool IoStatementState::Inquire(
639 InquiryKeywordHash inquiry, std::int64_t id, bool &out) {
640 return common::visit(
641 [&](auto &x) { return x.get().Inquire(inquiry, id, out); }, u_);
644 bool IoStatementState::Inquire(InquiryKeywordHash inquiry, std::int64_t &n) {
645 return common::visit(
646 [&](auto &x) { return x.get().Inquire(inquiry, n); }, u_);
649 std::int64_t IoStatementState::InquirePos() {
650 return common::visit([&](auto &x) { return x.get().InquirePos(); }, u_);
653 void IoStatementState::GotChar(int n) {
654 if (auto *formattedIn{
655 get_if<FormattedIoStatementState<Direction::Input>>()}) {
656 formattedIn->GotChar(n);
657 } else {
658 GetIoErrorHandler().Crash("IoStatementState::GotChar() called for "
659 "statement that is not formatted input");
663 std::size_t
664 FormattedIoStatementState<Direction::Input>::GetEditDescriptorChars() const {
665 return chars_;
668 void FormattedIoStatementState<Direction::Input>::GotChar(int n) {
669 chars_ += n;
672 bool ListDirectedStatementState<Direction::Output>::EmitLeadingSpaceOrAdvance(
673 IoStatementState &io, std::size_t length, bool isCharacter) {
674 if (length == 0) {
675 return true;
677 const ConnectionState &connection{io.GetConnectionState()};
678 int space{connection.positionInRecord == 0 ||
679 !(isCharacter && lastWasUndelimitedCharacter())};
680 set_lastWasUndelimitedCharacter(false);
681 if (connection.NeedAdvance(space + length)) {
682 return io.AdvanceRecord();
684 if (space) {
685 return EmitAscii(io, " ", 1);
687 return true;
690 std::optional<DataEdit>
691 ListDirectedStatementState<Direction::Output>::GetNextDataEdit(
692 IoStatementState &io, int maxRepeat) {
693 DataEdit edit;
694 edit.descriptor = DataEdit::ListDirected;
695 edit.repeat = maxRepeat;
696 edit.modes = io.mutableModes();
697 return edit;
700 std::optional<DataEdit>
701 ListDirectedStatementState<Direction::Input>::GetNextDataEdit(
702 IoStatementState &io, int maxRepeat) {
703 // N.B. list-directed transfers cannot be nonadvancing (C1221)
704 ConnectionState &connection{io.GetConnectionState()};
705 DataEdit edit;
706 edit.descriptor = DataEdit::ListDirected;
707 edit.repeat = 1; // may be overridden below
708 edit.modes = io.mutableModes();
709 if (hitSlash_) { // everything after '/' is nullified
710 edit.descriptor = DataEdit::ListDirectedNullValue;
711 return edit;
713 char32_t comma{','};
714 if (edit.modes.editingFlags & decimalComma) {
715 comma = ';';
717 std::size_t byteCount{0};
718 if (remaining_ > 0 && !realPart_) { // "r*c" repetition in progress
719 RUNTIME_CHECK(io.GetIoErrorHandler(), repeatPosition_.has_value());
720 repeatPosition_.reset(); // restores the saved position
721 if (!imaginaryPart_) {
722 edit.repeat = std::min<int>(remaining_, maxRepeat);
723 auto ch{io.GetCurrentChar(byteCount)};
724 if (!ch || *ch == ' ' || *ch == '\t' || *ch == comma) {
725 // "r*" repeated null
726 edit.descriptor = DataEdit::ListDirectedNullValue;
729 remaining_ -= edit.repeat;
730 if (remaining_ > 0) {
731 repeatPosition_.emplace(io);
733 if (!imaginaryPart_) {
734 return edit;
737 // Skip separators, handle a "r*c" repeat count; see 13.10.2 in Fortran 2018
738 if (imaginaryPart_) {
739 imaginaryPart_ = false;
740 } else if (realPart_) {
741 realPart_ = false;
742 imaginaryPart_ = true;
743 edit.descriptor = DataEdit::ListDirectedImaginaryPart;
745 auto ch{io.GetNextNonBlank(byteCount)};
746 if (ch && *ch == comma && eatComma_) {
747 // Consume comma & whitespace after previous item.
748 // This includes the comma between real and imaginary components
749 // in list-directed/NAMELIST complex input.
750 // (When DECIMAL='COMMA', the comma is actually a semicolon.)
751 io.HandleRelativePosition(byteCount);
752 ch = io.GetNextNonBlank(byteCount);
754 eatComma_ = true;
755 if (!ch) {
756 return std::nullopt;
758 if (*ch == '/') {
759 hitSlash_ = true;
760 edit.descriptor = DataEdit::ListDirectedNullValue;
761 return edit;
763 if (*ch == comma) { // separator: null value
764 edit.descriptor = DataEdit::ListDirectedNullValue;
765 return edit;
767 if (imaginaryPart_) { // can't repeat components
768 return edit;
770 if (*ch >= '0' && *ch <= '9') { // look for "r*" repetition count
771 auto start{connection.positionInRecord};
772 int r{0};
773 do {
774 static auto constexpr clamp{(std::numeric_limits<int>::max() - '9') / 10};
775 if (r >= clamp) {
776 r = 0;
777 break;
779 r = 10 * r + (*ch - '0');
780 io.HandleRelativePosition(byteCount);
781 ch = io.GetCurrentChar(byteCount);
782 } while (ch && *ch >= '0' && *ch <= '9');
783 if (r > 0 && ch && *ch == '*') { // subtle: r must be nonzero
784 io.HandleRelativePosition(byteCount);
785 ch = io.GetCurrentChar(byteCount);
786 if (ch && *ch == '/') { // r*/
787 hitSlash_ = true;
788 edit.descriptor = DataEdit::ListDirectedNullValue;
789 return edit;
791 if (!ch || *ch == ' ' || *ch == '\t' || *ch == comma) { // "r*" null
792 edit.descriptor = DataEdit::ListDirectedNullValue;
794 edit.repeat = std::min<int>(r, maxRepeat);
795 remaining_ = r - edit.repeat;
796 if (remaining_ > 0) {
797 repeatPosition_.emplace(io);
799 } else { // not a repetition count, just an integer value; rewind
800 connection.positionInRecord = start;
803 if (!imaginaryPart_ && ch && *ch == '(') {
804 realPart_ = true;
805 io.HandleRelativePosition(byteCount);
806 edit.descriptor = DataEdit::ListDirectedRealPart;
808 return edit;
811 template <Direction DIR>
812 bool ExternalUnformattedIoStatementState<DIR>::Receive(
813 char *data, std::size_t bytes, std::size_t elementBytes) {
814 if constexpr (DIR == Direction::Output) {
815 this->Crash("ExternalUnformattedIoStatementState::Receive() called for "
816 "output statement");
818 return this->unit().Receive(data, bytes, elementBytes, *this);
821 template <Direction DIR>
822 ChildIoStatementState<DIR>::ChildIoStatementState(
823 ChildIo &child, const char *sourceFile, int sourceLine)
824 : IoStatementBase{sourceFile, sourceLine}, child_{child} {}
826 template <Direction DIR>
827 MutableModes &ChildIoStatementState<DIR>::mutableModes() {
828 return child_.parent().mutableModes();
831 template <Direction DIR>
832 ConnectionState &ChildIoStatementState<DIR>::GetConnectionState() {
833 return child_.parent().GetConnectionState();
836 template <Direction DIR>
837 ExternalFileUnit *ChildIoStatementState<DIR>::GetExternalFileUnit() const {
838 return child_.parent().GetExternalFileUnit();
841 template <Direction DIR> int ChildIoStatementState<DIR>::EndIoStatement() {
842 CompleteOperation();
843 auto result{IoStatementBase::EndIoStatement()};
844 child_.EndIoStatement(); // annihilates *this in child_.u_
845 return result;
848 template <Direction DIR>
849 bool ChildIoStatementState<DIR>::Emit(
850 const char *data, std::size_t bytes, std::size_t elementBytes) {
851 return child_.parent().Emit(data, bytes, elementBytes);
854 template <Direction DIR>
855 std::size_t ChildIoStatementState<DIR>::GetNextInputBytes(const char *&p) {
856 return child_.parent().GetNextInputBytes(p);
859 template <Direction DIR>
860 void ChildIoStatementState<DIR>::HandleAbsolutePosition(std::int64_t n) {
861 return child_.parent().HandleAbsolutePosition(n);
864 template <Direction DIR>
865 void ChildIoStatementState<DIR>::HandleRelativePosition(std::int64_t n) {
866 return child_.parent().HandleRelativePosition(n);
869 template <Direction DIR, typename CHAR>
870 ChildFormattedIoStatementState<DIR, CHAR>::ChildFormattedIoStatementState(
871 ChildIo &child, const CHAR *format, std::size_t formatLength,
872 const Descriptor *formatDescriptor, const char *sourceFile, int sourceLine)
873 : ChildIoStatementState<DIR>{child, sourceFile, sourceLine},
874 mutableModes_{child.parent().mutableModes()}, format_{*this, format,
875 formatLength,
876 formatDescriptor} {}
878 template <Direction DIR, typename CHAR>
879 void ChildFormattedIoStatementState<DIR, CHAR>::CompleteOperation() {
880 if (!this->completedOperation()) {
881 format_.Finish(*this);
882 ChildIoStatementState<DIR>::CompleteOperation();
886 template <Direction DIR, typename CHAR>
887 int ChildFormattedIoStatementState<DIR, CHAR>::EndIoStatement() {
888 CompleteOperation();
889 return ChildIoStatementState<DIR>::EndIoStatement();
892 template <Direction DIR, typename CHAR>
893 bool ChildFormattedIoStatementState<DIR, CHAR>::AdvanceRecord(int n) {
894 return this->child().parent().AdvanceRecord(n);
897 template <Direction DIR>
898 bool ChildUnformattedIoStatementState<DIR>::Receive(
899 char *data, std::size_t bytes, std::size_t elementBytes) {
900 return this->child().parent().Receive(data, bytes, elementBytes);
903 template class InternalIoStatementState<Direction::Output>;
904 template class InternalIoStatementState<Direction::Input>;
905 template class InternalFormattedIoStatementState<Direction::Output>;
906 template class InternalFormattedIoStatementState<Direction::Input>;
907 template class InternalListIoStatementState<Direction::Output>;
908 template class InternalListIoStatementState<Direction::Input>;
909 template class ExternalIoStatementState<Direction::Output>;
910 template class ExternalIoStatementState<Direction::Input>;
911 template class ExternalFormattedIoStatementState<Direction::Output>;
912 template class ExternalFormattedIoStatementState<Direction::Input>;
913 template class ExternalListIoStatementState<Direction::Output>;
914 template class ExternalListIoStatementState<Direction::Input>;
915 template class ExternalUnformattedIoStatementState<Direction::Output>;
916 template class ExternalUnformattedIoStatementState<Direction::Input>;
917 template class ChildIoStatementState<Direction::Output>;
918 template class ChildIoStatementState<Direction::Input>;
919 template class ChildFormattedIoStatementState<Direction::Output>;
920 template class ChildFormattedIoStatementState<Direction::Input>;
921 template class ChildListIoStatementState<Direction::Output>;
922 template class ChildListIoStatementState<Direction::Input>;
923 template class ChildUnformattedIoStatementState<Direction::Output>;
924 template class ChildUnformattedIoStatementState<Direction::Input>;
926 void ExternalMiscIoStatementState::CompleteOperation() {
927 if (completedOperation()) {
928 return;
930 ExternalFileUnit &ext{unit()};
931 switch (which_) {
932 case Flush:
933 ext.FlushOutput(*this);
934 std::fflush(nullptr); // flushes C stdio output streams (12.9(2))
935 break;
936 case Backspace:
937 ext.BackspaceRecord(*this);
938 break;
939 case Endfile:
940 ext.Endfile(*this);
941 break;
942 case Rewind:
943 ext.Rewind(*this);
944 break;
945 case Wait:
946 break; // handled in io-api.cpp BeginWait
948 return IoStatementBase::CompleteOperation();
951 int ExternalMiscIoStatementState::EndIoStatement() {
952 CompleteOperation();
953 return ExternalIoStatementBase::EndIoStatement();
956 InquireUnitState::InquireUnitState(
957 ExternalFileUnit &unit, const char *sourceFile, int sourceLine)
958 : ExternalIoStatementBase{unit, sourceFile, sourceLine} {}
960 bool InquireUnitState::Inquire(
961 InquiryKeywordHash inquiry, char *result, std::size_t length) {
962 if (unit().createdForInternalChildIo()) {
963 SignalError(IostatInquireInternalUnit,
964 "INQUIRE of unit created for defined derived type I/O of an internal "
965 "unit");
966 return false;
968 const char *str{nullptr};
969 switch (inquiry) {
970 case HashInquiryKeyword("ACCESS"):
971 if (!unit().IsConnected()) {
972 str = "UNDEFINED";
973 } else {
974 switch (unit().access) {
975 case Access::Sequential:
976 str = "SEQUENTIAL";
977 break;
978 case Access::Direct:
979 str = "DIRECT";
980 break;
981 case Access::Stream:
982 str = "STREAM";
983 break;
986 break;
987 case HashInquiryKeyword("ACTION"):
988 str = !unit().IsConnected() ? "UNDEFINED"
989 : unit().mayWrite() ? unit().mayRead() ? "READWRITE" : "WRITE"
990 : "READ";
991 break;
992 case HashInquiryKeyword("ASYNCHRONOUS"):
993 str = !unit().IsConnected() ? "UNDEFINED"
994 : unit().mayAsynchronous() ? "YES"
995 : "NO";
996 break;
997 case HashInquiryKeyword("BLANK"):
998 str = !unit().IsConnected() || unit().isUnformatted.value_or(true)
999 ? "UNDEFINED"
1000 : mutableModes().editingFlags & blankZero ? "ZERO"
1001 : "NULL";
1002 break;
1003 case HashInquiryKeyword("CARRIAGECONTROL"):
1004 str = "LIST";
1005 break;
1006 case HashInquiryKeyword("CONVERT"):
1007 str = unit().swapEndianness() ? "SWAP" : "NATIVE";
1008 break;
1009 case HashInquiryKeyword("DECIMAL"):
1010 str = !unit().IsConnected() || unit().isUnformatted.value_or(true)
1011 ? "UNDEFINED"
1012 : mutableModes().editingFlags & decimalComma ? "COMMA"
1013 : "POINT";
1014 break;
1015 case HashInquiryKeyword("DELIM"):
1016 if (!unit().IsConnected() || unit().isUnformatted.value_or(true)) {
1017 str = "UNDEFINED";
1018 } else {
1019 switch (mutableModes().delim) {
1020 case '\'':
1021 str = "APOSTROPHE";
1022 break;
1023 case '"':
1024 str = "QUOTE";
1025 break;
1026 default:
1027 str = "NONE";
1028 break;
1031 break;
1032 case HashInquiryKeyword("DIRECT"):
1033 str = !unit().IsConnected() ? "UNKNOWN"
1034 : unit().access == Access::Direct ||
1035 (unit().mayPosition() && unit().openRecl)
1036 ? "YES"
1037 : "NO";
1038 break;
1039 case HashInquiryKeyword("ENCODING"):
1040 str = !unit().IsConnected() ? "UNKNOWN"
1041 : unit().isUnformatted.value_or(true) ? "UNDEFINED"
1042 : unit().isUTF8 ? "UTF-8"
1043 : "ASCII";
1044 break;
1045 case HashInquiryKeyword("FORM"):
1046 str = !unit().IsConnected() || !unit().isUnformatted ? "UNDEFINED"
1047 : *unit().isUnformatted ? "UNFORMATTED"
1048 : "FORMATTED";
1049 break;
1050 case HashInquiryKeyword("FORMATTED"):
1051 str = !unit().IsConnected() ? "UNDEFINED"
1052 : !unit().isUnformatted ? "UNKNOWN"
1053 : *unit().isUnformatted ? "NO"
1054 : "YES";
1055 break;
1056 case HashInquiryKeyword("NAME"):
1057 str = unit().path();
1058 if (!str) {
1059 return true; // result is undefined
1061 break;
1062 case HashInquiryKeyword("PAD"):
1063 str = !unit().IsConnected() || unit().isUnformatted.value_or(true)
1064 ? "UNDEFINED"
1065 : mutableModes().pad ? "YES"
1066 : "NO";
1067 break;
1068 case HashInquiryKeyword("POSITION"):
1069 if (!unit().IsConnected() || unit().access == Access::Direct) {
1070 str = "UNDEFINED";
1071 } else {
1072 switch (unit().InquirePosition()) {
1073 case Position::Rewind:
1074 str = "REWIND";
1075 break;
1076 case Position::Append:
1077 str = "APPEND";
1078 break;
1079 case Position::AsIs:
1080 str = "ASIS";
1081 break;
1084 break;
1085 case HashInquiryKeyword("READ"):
1086 str = !unit().IsConnected() ? "UNDEFINED" : unit().mayRead() ? "YES" : "NO";
1087 break;
1088 case HashInquiryKeyword("READWRITE"):
1089 str = !unit().IsConnected() ? "UNDEFINED"
1090 : unit().mayRead() && unit().mayWrite() ? "YES"
1091 : "NO";
1092 break;
1093 case HashInquiryKeyword("ROUND"):
1094 if (!unit().IsConnected() || unit().isUnformatted.value_or(true)) {
1095 str = "UNDEFINED";
1096 } else {
1097 switch (mutableModes().round) {
1098 case decimal::FortranRounding::RoundNearest:
1099 str = "NEAREST";
1100 break;
1101 case decimal::FortranRounding::RoundUp:
1102 str = "UP";
1103 break;
1104 case decimal::FortranRounding::RoundDown:
1105 str = "DOWN";
1106 break;
1107 case decimal::FortranRounding::RoundToZero:
1108 str = "ZERO";
1109 break;
1110 case decimal::FortranRounding::RoundCompatible:
1111 str = "COMPATIBLE";
1112 break;
1115 break;
1116 case HashInquiryKeyword("SEQUENTIAL"):
1117 // "NO" for Direct, since Sequential would not work if
1118 // the unit were reopened without RECL=.
1119 str = !unit().IsConnected() ? "UNKNOWN"
1120 : unit().access == Access::Sequential ? "YES"
1121 : "NO";
1122 break;
1123 case HashInquiryKeyword("SIGN"):
1124 str = !unit().IsConnected() || unit().isUnformatted.value_or(true)
1125 ? "UNDEFINED"
1126 : mutableModes().editingFlags & signPlus ? "PLUS"
1127 : "SUPPRESS";
1128 break;
1129 case HashInquiryKeyword("STREAM"):
1130 str = !unit().IsConnected() ? "UNKNOWN"
1131 : unit().access == Access::Stream ? "YES"
1132 : "NO";
1133 break;
1134 case HashInquiryKeyword("UNFORMATTED"):
1135 str = !unit().IsConnected() || !unit().isUnformatted ? "UNKNOWN"
1136 : *unit().isUnformatted ? "YES"
1137 : "NO";
1138 break;
1139 case HashInquiryKeyword("WRITE"):
1140 str = !unit().IsConnected() ? "UNKNOWN" : unit().mayWrite() ? "YES" : "NO";
1141 break;
1143 if (str) {
1144 ToFortranDefaultCharacter(result, length, str);
1145 return true;
1146 } else {
1147 BadInquiryKeywordHashCrash(inquiry);
1148 return false;
1152 bool InquireUnitState::Inquire(InquiryKeywordHash inquiry, bool &result) {
1153 switch (inquiry) {
1154 case HashInquiryKeyword("EXIST"):
1155 result = true;
1156 return true;
1157 case HashInquiryKeyword("NAMED"):
1158 result = unit().path() != nullptr;
1159 return true;
1160 case HashInquiryKeyword("OPENED"):
1161 result = unit().IsConnected();
1162 return true;
1163 case HashInquiryKeyword("PENDING"):
1164 result = false; // asynchronous I/O is not implemented
1165 return true;
1166 default:
1167 BadInquiryKeywordHashCrash(inquiry);
1168 return false;
1172 bool InquireUnitState::Inquire(
1173 InquiryKeywordHash inquiry, std::int64_t, bool &result) {
1174 switch (inquiry) {
1175 case HashInquiryKeyword("PENDING"):
1176 result = false; // asynchronous I/O is not implemented
1177 return true;
1178 default:
1179 BadInquiryKeywordHashCrash(inquiry);
1180 return false;
1184 bool InquireUnitState::Inquire(
1185 InquiryKeywordHash inquiry, std::int64_t &result) {
1186 switch (inquiry) {
1187 case HashInquiryKeyword("NEXTREC"):
1188 if (unit().access == Access::Direct) {
1189 result = unit().currentRecordNumber;
1191 return true;
1192 case HashInquiryKeyword("NUMBER"):
1193 result = unit().unitNumber();
1194 return true;
1195 case HashInquiryKeyword("POS"):
1196 result = unit().InquirePos();
1197 return true;
1198 case HashInquiryKeyword("RECL"):
1199 if (!unit().IsConnected()) {
1200 result = -1;
1201 } else if (unit().access == Access::Stream) {
1202 result = -2;
1203 } else if (unit().openRecl) {
1204 result = *unit().openRecl;
1205 } else {
1206 result = std::numeric_limits<std::int32_t>::max();
1208 return true;
1209 case HashInquiryKeyword("SIZE"):
1210 result = -1;
1211 if (unit().IsConnected()) {
1212 if (auto size{unit().knownSize()}) {
1213 result = *size;
1216 return true;
1217 default:
1218 BadInquiryKeywordHashCrash(inquiry);
1219 return false;
1223 InquireNoUnitState::InquireNoUnitState(
1224 const char *sourceFile, int sourceLine, int badUnitNumber)
1225 : NoUnitIoStatementState{*this, sourceFile, sourceLine, badUnitNumber} {}
1227 bool InquireNoUnitState::Inquire(
1228 InquiryKeywordHash inquiry, char *result, std::size_t length) {
1229 switch (inquiry) {
1230 case HashInquiryKeyword("ACCESS"):
1231 case HashInquiryKeyword("ACTION"):
1232 case HashInquiryKeyword("ASYNCHRONOUS"):
1233 case HashInquiryKeyword("BLANK"):
1234 case HashInquiryKeyword("CARRIAGECONTROL"):
1235 case HashInquiryKeyword("CONVERT"):
1236 case HashInquiryKeyword("DECIMAL"):
1237 case HashInquiryKeyword("DELIM"):
1238 case HashInquiryKeyword("FORM"):
1239 case HashInquiryKeyword("NAME"):
1240 case HashInquiryKeyword("PAD"):
1241 case HashInquiryKeyword("POSITION"):
1242 case HashInquiryKeyword("ROUND"):
1243 case HashInquiryKeyword("SIGN"):
1244 ToFortranDefaultCharacter(result, length, "UNDEFINED");
1245 return true;
1246 case HashInquiryKeyword("DIRECT"):
1247 case HashInquiryKeyword("ENCODING"):
1248 case HashInquiryKeyword("FORMATTED"):
1249 case HashInquiryKeyword("READ"):
1250 case HashInquiryKeyword("READWRITE"):
1251 case HashInquiryKeyword("SEQUENTIAL"):
1252 case HashInquiryKeyword("STREAM"):
1253 case HashInquiryKeyword("WRITE"):
1254 case HashInquiryKeyword("UNFORMATTED"):
1255 ToFortranDefaultCharacter(result, length, "UNKNOWN");
1256 return true;
1257 default:
1258 BadInquiryKeywordHashCrash(inquiry);
1259 return false;
1263 bool InquireNoUnitState::Inquire(InquiryKeywordHash inquiry, bool &result) {
1264 switch (inquiry) {
1265 case HashInquiryKeyword("EXIST"):
1266 result = true;
1267 return true;
1268 case HashInquiryKeyword("NAMED"):
1269 case HashInquiryKeyword("OPENED"):
1270 case HashInquiryKeyword("PENDING"):
1271 result = false;
1272 return true;
1273 default:
1274 BadInquiryKeywordHashCrash(inquiry);
1275 return false;
1279 bool InquireNoUnitState::Inquire(
1280 InquiryKeywordHash inquiry, std::int64_t, bool &result) {
1281 switch (inquiry) {
1282 case HashInquiryKeyword("PENDING"):
1283 result = false;
1284 return true;
1285 default:
1286 BadInquiryKeywordHashCrash(inquiry);
1287 return false;
1291 bool InquireNoUnitState::Inquire(
1292 InquiryKeywordHash inquiry, std::int64_t &result) {
1293 switch (inquiry) {
1294 case HashInquiryKeyword("NUMBER"):
1295 result = badUnitNumber();
1296 return true;
1297 case HashInquiryKeyword("NEXTREC"):
1298 case HashInquiryKeyword("POS"):
1299 case HashInquiryKeyword("RECL"):
1300 case HashInquiryKeyword("SIZE"):
1301 result = -1;
1302 return true;
1303 default:
1304 BadInquiryKeywordHashCrash(inquiry);
1305 return false;
1309 InquireUnconnectedFileState::InquireUnconnectedFileState(
1310 OwningPtr<char> &&path, const char *sourceFile, int sourceLine)
1311 : NoUnitIoStatementState{*this, sourceFile, sourceLine}, path_{std::move(
1312 path)} {}
1314 bool InquireUnconnectedFileState::Inquire(
1315 InquiryKeywordHash inquiry, char *result, std::size_t length) {
1316 const char *str{nullptr};
1317 switch (inquiry) {
1318 case HashInquiryKeyword("ACCESS"):
1319 case HashInquiryKeyword("ACTION"):
1320 case HashInquiryKeyword("ASYNCHRONOUS"):
1321 case HashInquiryKeyword("BLANK"):
1322 case HashInquiryKeyword("CARRIAGECONTROL"):
1323 case HashInquiryKeyword("CONVERT"):
1324 case HashInquiryKeyword("DECIMAL"):
1325 case HashInquiryKeyword("DELIM"):
1326 case HashInquiryKeyword("FORM"):
1327 case HashInquiryKeyword("PAD"):
1328 case HashInquiryKeyword("POSITION"):
1329 case HashInquiryKeyword("ROUND"):
1330 case HashInquiryKeyword("SIGN"):
1331 str = "UNDEFINED";
1332 break;
1333 case HashInquiryKeyword("DIRECT"):
1334 case HashInquiryKeyword("ENCODING"):
1335 case HashInquiryKeyword("FORMATTED"):
1336 case HashInquiryKeyword("SEQUENTIAL"):
1337 case HashInquiryKeyword("STREAM"):
1338 case HashInquiryKeyword("UNFORMATTED"):
1339 str = "UNKNONN";
1340 break;
1341 case HashInquiryKeyword("READ"):
1342 str = MayRead(path_.get()) ? "YES" : "NO";
1343 break;
1344 case HashInquiryKeyword("READWRITE"):
1345 str = MayReadAndWrite(path_.get()) ? "YES" : "NO";
1346 break;
1347 case HashInquiryKeyword("WRITE"):
1348 str = MayWrite(path_.get()) ? "YES" : "NO";
1349 break;
1350 case HashInquiryKeyword("NAME"):
1351 str = path_.get();
1352 if (!str) {
1353 return true; // result is undefined
1355 break;
1357 if (str) {
1358 ToFortranDefaultCharacter(result, length, str);
1359 return true;
1360 } else {
1361 BadInquiryKeywordHashCrash(inquiry);
1362 return false;
1366 bool InquireUnconnectedFileState::Inquire(
1367 InquiryKeywordHash inquiry, bool &result) {
1368 switch (inquiry) {
1369 case HashInquiryKeyword("EXIST"):
1370 result = IsExtant(path_.get());
1371 return true;
1372 case HashInquiryKeyword("NAMED"):
1373 result = true;
1374 return true;
1375 case HashInquiryKeyword("OPENED"):
1376 result = false;
1377 return true;
1378 case HashInquiryKeyword("PENDING"):
1379 result = false;
1380 return true;
1381 default:
1382 BadInquiryKeywordHashCrash(inquiry);
1383 return false;
1387 bool InquireUnconnectedFileState::Inquire(
1388 InquiryKeywordHash inquiry, std::int64_t, bool &result) {
1389 switch (inquiry) {
1390 case HashInquiryKeyword("PENDING"):
1391 result = false;
1392 return true;
1393 default:
1394 BadInquiryKeywordHashCrash(inquiry);
1395 return false;
1399 bool InquireUnconnectedFileState::Inquire(
1400 InquiryKeywordHash inquiry, std::int64_t &result) {
1401 switch (inquiry) {
1402 case HashInquiryKeyword("NEXTREC"):
1403 case HashInquiryKeyword("NUMBER"):
1404 case HashInquiryKeyword("POS"):
1405 case HashInquiryKeyword("RECL"):
1406 result = -1;
1407 return true;
1408 case HashInquiryKeyword("SIZE"):
1409 result = SizeInBytes(path_.get());
1410 return true;
1411 default:
1412 BadInquiryKeywordHashCrash(inquiry);
1413 return false;
1417 InquireIOLengthState::InquireIOLengthState(
1418 const char *sourceFile, int sourceLine)
1419 : NoUnitIoStatementState{*this, sourceFile, sourceLine} {}
1421 bool InquireIOLengthState::Emit(const char *, std::size_t bytes, std::size_t) {
1422 bytes_ += bytes;
1423 return true;
1426 int ErroneousIoStatementState::EndIoStatement() {
1427 SignalPendingError();
1428 if (unit_) {
1429 unit_->EndIoStatement();
1431 return IoStatementBase::EndIoStatement();
1434 } // namespace Fortran::runtime::io