[libc++][Android] Allow testing libc++ with clang-r536225 (#116149)
[llvm-project.git] / flang / runtime / io-stmt.cpp
blobf24eb929ce748a26b4613f4f635ccab190ee146e
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 {
24 RT_OFFLOAD_API_GROUP_BEGIN
26 bool IoStatementBase::Emit(const char *, std::size_t, std::size_t) {
27 return false;
30 std::size_t IoStatementBase::GetNextInputBytes(const char *&p) {
31 p = nullptr;
32 return 0;
35 std::size_t IoStatementBase::ViewBytesInRecord(
36 const char *&p, bool forward) const {
37 p = nullptr;
38 return 0;
41 bool IoStatementBase::AdvanceRecord(int) { return false; }
43 void IoStatementBase::BackspaceRecord() {}
45 bool IoStatementBase::Receive(char *, std::size_t, std::size_t) {
46 return false;
49 Fortran::common::optional<DataEdit> IoStatementBase::GetNextDataEdit(
50 IoStatementState &, int) {
51 return Fortran::common::nullopt;
54 bool IoStatementBase::BeginReadingRecord() { return true; }
56 void IoStatementBase::FinishReadingRecord() {}
58 void IoStatementBase::HandleAbsolutePosition(std::int64_t) {}
60 void IoStatementBase::HandleRelativePosition(std::int64_t) {}
62 std::int64_t IoStatementBase::InquirePos() { return 0; }
64 ExternalFileUnit *IoStatementBase::GetExternalFileUnit() const {
65 return nullptr;
68 bool IoStatementBase::Inquire(InquiryKeywordHash, char *, std::size_t) {
69 return false;
72 bool IoStatementBase::Inquire(InquiryKeywordHash, bool &) { return false; }
74 bool IoStatementBase::Inquire(InquiryKeywordHash, std::int64_t, bool &) {
75 return false;
78 bool IoStatementBase::Inquire(InquiryKeywordHash, std::int64_t &) {
79 return false;
82 void IoStatementBase::BadInquiryKeywordHashCrash(InquiryKeywordHash inquiry) {
83 char buffer[16];
84 const char *decode{InquiryKeywordHashDecode(buffer, sizeof buffer, inquiry)};
85 Crash("Bad InquiryKeywordHash 0x%x (%s)", inquiry,
86 decode ? decode : "(cannot decode)");
89 template <Direction DIR>
90 InternalIoStatementState<DIR>::InternalIoStatementState(
91 Buffer scalar, std::size_t length, const char *sourceFile, int sourceLine)
92 : IoStatementBase{sourceFile, sourceLine}, unit_{scalar, length, 1} {}
94 template <Direction DIR>
95 InternalIoStatementState<DIR>::InternalIoStatementState(
96 const Descriptor &d, const char *sourceFile, int sourceLine)
97 : IoStatementBase{sourceFile, sourceLine}, unit_{d, *this} {}
99 template <Direction DIR>
100 bool InternalIoStatementState<DIR>::Emit(
101 const char *data, std::size_t bytes, std::size_t /*elementBytes*/) {
102 if constexpr (DIR == Direction::Input) {
103 Crash("InternalIoStatementState<Direction::Input>::Emit() called");
104 return false;
106 return unit_.Emit(data, bytes, *this);
109 template <Direction DIR>
110 std::size_t InternalIoStatementState<DIR>::GetNextInputBytes(const char *&p) {
111 return unit_.GetNextInputBytes(p, *this);
114 // InternalIoStatementState<DIR>::ViewBytesInRecord() not needed or defined
116 template <Direction DIR>
117 bool InternalIoStatementState<DIR>::AdvanceRecord(int n) {
118 while (n-- > 0) {
119 if (!unit_.AdvanceRecord(*this)) {
120 return false;
123 return true;
126 template <Direction DIR> void InternalIoStatementState<DIR>::BackspaceRecord() {
127 unit_.BackspaceRecord(*this);
130 template <Direction DIR> int InternalIoStatementState<DIR>::EndIoStatement() {
131 auto result{IoStatementBase::EndIoStatement()};
132 if (free_) {
133 FreeMemory(this);
135 return result;
138 template <Direction DIR>
139 void InternalIoStatementState<DIR>::HandleAbsolutePosition(std::int64_t n) {
140 return unit_.HandleAbsolutePosition(n);
143 template <Direction DIR>
144 void InternalIoStatementState<DIR>::HandleRelativePosition(std::int64_t n) {
145 return unit_.HandleRelativePosition(n);
148 template <Direction DIR>
149 std::int64_t InternalIoStatementState<DIR>::InquirePos() {
150 return unit_.InquirePos();
153 template <Direction DIR, typename CHAR>
154 RT_API_ATTRS
155 InternalFormattedIoStatementState<DIR, CHAR>::InternalFormattedIoStatementState(
156 Buffer buffer, std::size_t length, const CharType *format,
157 std::size_t formatLength, const Descriptor *formatDescriptor,
158 const char *sourceFile, int sourceLine)
159 : InternalIoStatementState<DIR>{buffer, length, sourceFile, sourceLine},
160 ioStatementState_{*this},
161 format_{*this, format, formatLength, formatDescriptor} {}
163 template <Direction DIR, typename CHAR>
164 RT_API_ATTRS
165 InternalFormattedIoStatementState<DIR, CHAR>::InternalFormattedIoStatementState(
166 const Descriptor &d, const CharType *format, std::size_t formatLength,
167 const Descriptor *formatDescriptor, const char *sourceFile, int sourceLine)
168 : InternalIoStatementState<DIR>{d, sourceFile, sourceLine},
169 ioStatementState_{*this},
170 format_{*this, format, formatLength, formatDescriptor} {}
172 template <Direction DIR, typename CHAR>
173 void InternalFormattedIoStatementState<DIR, CHAR>::CompleteOperation() {
174 if (!this->completedOperation()) {
175 if constexpr (DIR == Direction::Output) {
176 format_.Finish(*this);
177 unit_.AdvanceRecord(*this);
179 IoStatementBase::CompleteOperation();
183 template <Direction DIR, typename CHAR>
184 int InternalFormattedIoStatementState<DIR, CHAR>::EndIoStatement() {
185 CompleteOperation();
186 return InternalIoStatementState<DIR>::EndIoStatement();
189 template <Direction DIR>
190 InternalListIoStatementState<DIR>::InternalListIoStatementState(
191 Buffer buffer, std::size_t length, const char *sourceFile, int sourceLine)
192 : InternalIoStatementState<DIR>{buffer, length, sourceFile, sourceLine},
193 ioStatementState_{*this} {}
195 template <Direction DIR>
196 InternalListIoStatementState<DIR>::InternalListIoStatementState(
197 const Descriptor &d, const char *sourceFile, int sourceLine)
198 : InternalIoStatementState<DIR>{d, sourceFile, sourceLine},
199 ioStatementState_{*this} {}
201 template <Direction DIR>
202 void InternalListIoStatementState<DIR>::CompleteOperation() {
203 if (!this->completedOperation()) {
204 if constexpr (DIR == Direction::Output) {
205 if (unit_.furthestPositionInRecord > 0) {
206 unit_.AdvanceRecord(*this);
209 IoStatementBase::CompleteOperation();
213 template <Direction DIR>
214 int InternalListIoStatementState<DIR>::EndIoStatement() {
215 CompleteOperation();
216 if constexpr (DIR == Direction::Input) {
217 if (int status{ListDirectedStatementState<DIR>::EndIoStatement()};
218 status != IostatOk) {
219 return status;
222 return InternalIoStatementState<DIR>::EndIoStatement();
225 ExternalIoStatementBase::ExternalIoStatementBase(
226 ExternalFileUnit &unit, const char *sourceFile, int sourceLine)
227 : IoStatementBase{sourceFile, sourceLine}, unit_{unit} {}
229 MutableModes &ExternalIoStatementBase::mutableModes() {
230 if (const ChildIo * child{unit_.GetChildIo()}) {
231 #if !defined(RT_DEVICE_AVOID_RECURSION)
232 return child->parent().mutableModes();
233 #else
234 ReportUnsupportedChildIo();
235 #endif
237 return unit_.modes;
240 ConnectionState &ExternalIoStatementBase::GetConnectionState() { return unit_; }
242 int ExternalIoStatementBase::EndIoStatement() {
243 CompleteOperation();
244 auto result{IoStatementBase::EndIoStatement()};
245 #if !defined(RT_USE_PSEUDO_FILE_UNIT)
246 auto unitNumber{unit_.unitNumber()};
247 unit_.EndIoStatement(); // annihilates *this in unit_.u_
248 if (destroy_) {
249 if (ExternalFileUnit *
250 toClose{ExternalFileUnit::LookUpForClose(unitNumber)}) {
251 toClose->Close(CloseStatus::Delete, *this);
252 toClose->DestroyClosed();
255 #else
256 // Fetch the unit pointer before *this disappears.
257 ExternalFileUnit *unitPtr{&unit_};
258 // The pseudo file units are dynamically allocated
259 // and are not tracked in the unit map.
260 // They have to be destructed and deallocated here.
261 unitPtr->~ExternalFileUnit();
262 FreeMemory(unitPtr);
263 #endif
264 return result;
267 void ExternalIoStatementBase::SetAsynchronous() {
268 asynchronousID_ = unit().GetAsynchronousId(*this);
271 std::int64_t ExternalIoStatementBase::InquirePos() {
272 return unit_.InquirePos();
275 void OpenStatementState::set_path(const char *path, std::size_t length) {
276 pathLength_ = TrimTrailingSpaces(path, length);
277 path_ = SaveDefaultCharacter(path, pathLength_, *this);
280 void OpenStatementState::CompleteOperation() {
281 if (completedOperation()) {
282 return;
284 if (position_) {
285 if (access_ && *access_ == Access::Direct) {
286 SignalError("POSITION= may not be set with ACCESS='DIRECT'");
287 position_.reset();
290 if (status_) { // 12.5.6.10
291 if ((*status_ == OpenStatus::New || *status_ == OpenStatus::Replace) &&
292 !path_.get()) {
293 SignalError("FILE= required on OPEN with STATUS='NEW' or 'REPLACE'");
294 } else if (*status_ == OpenStatus::Scratch && path_.get()) {
295 SignalError("FILE= may not appear on OPEN with STATUS='SCRATCH'");
298 // F'2023 12.5.6.13 - NEWUNIT= requires either FILE= or STATUS='SCRATCH'
299 if (isNewUnit_ && !path_.get() &&
300 status_.value_or(OpenStatus::Unknown) != OpenStatus::Scratch) {
301 SignalError(IostatBadNewUnit);
302 status_ = OpenStatus::Scratch; // error recovery
304 if (path_.get() || wasExtant_ ||
305 (status_ && *status_ == OpenStatus::Scratch)) {
306 if (unit().OpenUnit(status_, action_, position_.value_or(Position::AsIs),
307 std::move(path_), pathLength_, convert_, *this)) {
308 wasExtant_ = false; // existing unit was closed
310 } else {
311 unit().OpenAnonymousUnit(
312 status_, action_, position_.value_or(Position::AsIs), convert_, *this);
314 if (access_) {
315 if (*access_ != unit().access) {
316 if (wasExtant_) {
317 SignalError("ACCESS= may not be changed on an open unit");
318 access_.reset();
321 if (access_) {
322 unit().access = *access_;
325 if (!unit().isUnformatted) {
326 unit().isUnformatted = isUnformatted_;
328 if (isUnformatted_ && *isUnformatted_ != *unit().isUnformatted) {
329 if (wasExtant_) {
330 SignalError("FORM= may not be changed on an open unit");
332 unit().isUnformatted = *isUnformatted_;
334 if (!unit().isUnformatted) {
335 // Set default format (C.7.4 point 2).
336 unit().isUnformatted = unit().access != Access::Sequential;
338 if (!wasExtant_ && InError()) {
339 // Release the new unit on failure
340 set_destroy();
342 IoStatementBase::CompleteOperation();
345 int OpenStatementState::EndIoStatement() {
346 CompleteOperation();
347 return ExternalIoStatementBase::EndIoStatement();
350 int CloseStatementState::EndIoStatement() {
351 CompleteOperation();
352 int result{ExternalIoStatementBase::EndIoStatement()};
353 unit().CloseUnit(status_, *this);
354 unit().DestroyClosed();
355 return result;
358 void NoUnitIoStatementState::CompleteOperation() {
359 SignalPendingError();
360 IoStatementBase::CompleteOperation();
363 int NoUnitIoStatementState::EndIoStatement() {
364 CompleteOperation();
365 auto result{IoStatementBase::EndIoStatement()};
366 FreeMemory(this);
367 return result;
370 template <Direction DIR>
371 ExternalIoStatementState<DIR>::ExternalIoStatementState(
372 ExternalFileUnit &unit, const char *sourceFile, int sourceLine)
373 : ExternalIoStatementBase{unit, sourceFile, sourceLine}, mutableModes_{
374 unit.modes} {
375 if constexpr (DIR == Direction::Output) {
376 // If the last statement was a non-advancing IO input statement, the unit
377 // furthestPositionInRecord was not advanced, but the positionInRecord may
378 // have been advanced. Advance furthestPositionInRecord here to avoid
379 // overwriting the part of the record that has been read with blanks.
380 unit.furthestPositionInRecord =
381 std::max(unit.furthestPositionInRecord, unit.positionInRecord);
385 template <Direction DIR>
386 void ExternalIoStatementState<DIR>::CompleteOperation() {
387 if (completedOperation()) {
388 return;
390 if constexpr (DIR == Direction::Input) {
391 BeginReadingRecord(); // in case there were no I/O items
392 if (mutableModes().nonAdvancing && !InError()) {
393 unit().leftTabLimit = unit().furthestPositionInRecord;
394 } else {
395 FinishReadingRecord();
397 } else { // output
398 if (mutableModes().nonAdvancing) {
399 // Make effects of positioning past the last Emit() visible with blanks.
400 if (unit().positionInRecord > unit().furthestPositionInRecord) {
401 unit().Emit("", 0, 1, *this); // Emit() will pad
403 unit().leftTabLimit = unit().positionInRecord;
404 } else {
405 unit().AdvanceRecord(*this);
407 unit().FlushIfTerminal(*this);
409 return IoStatementBase::CompleteOperation();
412 template <Direction DIR> int ExternalIoStatementState<DIR>::EndIoStatement() {
413 CompleteOperation();
414 return ExternalIoStatementBase::EndIoStatement();
417 template <Direction DIR>
418 bool ExternalIoStatementState<DIR>::Emit(
419 const char *data, std::size_t bytes, std::size_t elementBytes) {
420 if constexpr (DIR == Direction::Input) {
421 Crash("ExternalIoStatementState::Emit(char) called for input statement");
423 return unit().Emit(data, bytes, elementBytes, *this);
426 template <Direction DIR>
427 std::size_t ExternalIoStatementState<DIR>::GetNextInputBytes(const char *&p) {
428 return unit().GetNextInputBytes(p, *this);
431 template <Direction DIR>
432 std::size_t ExternalIoStatementState<DIR>::ViewBytesInRecord(
433 const char *&p, bool forward) const {
434 return unit().ViewBytesInRecord(p, forward);
437 template <Direction DIR>
438 bool ExternalIoStatementState<DIR>::AdvanceRecord(int n) {
439 while (n-- > 0) {
440 if (!unit().AdvanceRecord(*this)) {
441 return false;
444 return true;
447 template <Direction DIR> void ExternalIoStatementState<DIR>::BackspaceRecord() {
448 unit().BackspaceRecord(*this);
451 template <Direction DIR>
452 void ExternalIoStatementState<DIR>::HandleAbsolutePosition(std::int64_t n) {
453 return unit().HandleAbsolutePosition(n);
456 template <Direction DIR>
457 void ExternalIoStatementState<DIR>::HandleRelativePosition(std::int64_t n) {
458 return unit().HandleRelativePosition(n);
461 template <Direction DIR>
462 bool ExternalIoStatementState<DIR>::BeginReadingRecord() {
463 if constexpr (DIR == Direction::Input) {
464 return unit().BeginReadingRecord(*this);
465 } else {
466 Crash("ExternalIoStatementState<Direction::Output>::BeginReadingRecord() "
467 "called");
468 return false;
472 template <Direction DIR>
473 void ExternalIoStatementState<DIR>::FinishReadingRecord() {
474 if constexpr (DIR == Direction::Input) {
475 unit().FinishReadingRecord(*this);
476 } else {
477 Crash("ExternalIoStatementState<Direction::Output>::FinishReadingRecord() "
478 "called");
482 template <Direction DIR, typename CHAR>
483 ExternalFormattedIoStatementState<DIR, CHAR>::ExternalFormattedIoStatementState(
484 ExternalFileUnit &unit, const CHAR *format, std::size_t formatLength,
485 const Descriptor *formatDescriptor, const char *sourceFile, int sourceLine)
486 : ExternalIoStatementState<DIR>{unit, sourceFile, sourceLine},
487 format_{*this, format, formatLength, formatDescriptor} {}
489 template <Direction DIR, typename CHAR>
490 void ExternalFormattedIoStatementState<DIR, CHAR>::CompleteOperation() {
491 if (this->completedOperation()) {
492 return;
494 if constexpr (DIR == Direction::Input) {
495 this->BeginReadingRecord(); // in case there were no I/O items
497 format_.Finish(*this);
498 return ExternalIoStatementState<DIR>::CompleteOperation();
501 template <Direction DIR, typename CHAR>
502 int ExternalFormattedIoStatementState<DIR, CHAR>::EndIoStatement() {
503 CompleteOperation();
504 return ExternalIoStatementState<DIR>::EndIoStatement();
507 Fortran::common::optional<DataEdit> IoStatementState::GetNextDataEdit(int n) {
508 return common::visit(
509 [&](auto &x) { return x.get().GetNextDataEdit(*this, n); }, u_);
512 bool IoStatementState::Emit(
513 const char *data, std::size_t bytes, std::size_t elementBytes) {
514 return common::visit(
515 [=](auto &x) { return x.get().Emit(data, bytes, elementBytes); }, u_);
518 bool IoStatementState::Receive(
519 char *data, std::size_t n, std::size_t elementBytes) {
520 return common::visit(
521 [=](auto &x) { return x.get().Receive(data, n, elementBytes); }, u_);
524 std::size_t IoStatementState::GetNextInputBytes(const char *&p) {
525 return common::visit(
526 [&](auto &x) { return x.get().GetNextInputBytes(p); }, u_);
529 bool IoStatementState::AdvanceRecord(int n) {
530 return common::visit([=](auto &x) { return x.get().AdvanceRecord(n); }, u_);
533 void IoStatementState::BackspaceRecord() {
534 common::visit([](auto &x) { x.get().BackspaceRecord(); }, u_);
537 void IoStatementState::HandleRelativePosition(std::int64_t n) {
538 common::visit([=](auto &x) { x.get().HandleRelativePosition(n); }, u_);
541 void IoStatementState::HandleAbsolutePosition(std::int64_t n) {
542 common::visit([=](auto &x) { x.get().HandleAbsolutePosition(n); }, u_);
545 void IoStatementState::CompleteOperation() {
546 common::visit([](auto &x) { x.get().CompleteOperation(); }, u_);
549 int IoStatementState::EndIoStatement() {
550 return common::visit([](auto &x) { return x.get().EndIoStatement(); }, u_);
553 ConnectionState &IoStatementState::GetConnectionState() {
554 return common::visit(
555 [](auto &x) -> ConnectionState & { return x.get().GetConnectionState(); },
556 u_);
559 MutableModes &IoStatementState::mutableModes() {
560 return common::visit(
561 [](auto &x) -> MutableModes & { return x.get().mutableModes(); }, u_);
564 bool IoStatementState::BeginReadingRecord() {
565 return common::visit(
566 [](auto &x) { return x.get().BeginReadingRecord(); }, u_);
569 IoErrorHandler &IoStatementState::GetIoErrorHandler() const {
570 return common::visit(
571 [](auto &x) -> IoErrorHandler & {
572 return static_cast<IoErrorHandler &>(x.get());
574 u_);
577 ExternalFileUnit *IoStatementState::GetExternalFileUnit() const {
578 return common::visit(
579 [](auto &x) { return x.get().GetExternalFileUnit(); }, u_);
582 Fortran::common::optional<char32_t> IoStatementState::GetCurrentChar(
583 std::size_t &byteCount) {
584 const char *p{nullptr};
585 std::size_t bytes{GetNextInputBytes(p)};
586 if (bytes == 0) {
587 byteCount = 0;
588 return Fortran::common::nullopt;
589 } else {
590 const ConnectionState &connection{GetConnectionState()};
591 if (connection.isUTF8) {
592 std::size_t length{MeasureUTF8Bytes(*p)};
593 if (length <= bytes) {
594 if (auto result{DecodeUTF8(p)}) {
595 byteCount = length;
596 return result;
599 GetIoErrorHandler().SignalError(IostatUTF8Decoding);
600 // Error recovery: return the next byte
601 } else if (connection.internalIoCharKind > 1) {
602 byteCount = connection.internalIoCharKind;
603 if (byteCount == 2) {
604 return *reinterpret_cast<const char16_t *>(p);
605 } else {
606 return *reinterpret_cast<const char32_t *>(p);
609 byteCount = 1;
610 return *p;
614 Fortran::common::optional<char32_t> IoStatementState::NextInField(
615 Fortran::common::optional<int> &remaining, const DataEdit &edit) {
616 std::size_t byteCount{0};
617 if (!remaining) { // Stream, list-directed, or NAMELIST
618 if (auto next{GetCurrentChar(byteCount)}) {
619 if (edit.IsListDirected()) {
620 // list-directed or NAMELIST: check for separators
621 switch (*next) {
622 case ' ':
623 case '\t':
624 case '/':
625 case '(':
626 case ')':
627 case '\'':
628 case '"':
629 case '*':
630 case '\n': // for stream access
631 return Fortran::common::nullopt;
632 case '&':
633 case '$':
634 if (edit.IsNamelist()) {
635 return Fortran::common::nullopt;
637 break;
638 case ',':
639 if (!(edit.modes.editingFlags & decimalComma)) {
640 return Fortran::common::nullopt;
642 break;
643 case ';':
644 if (edit.modes.editingFlags & decimalComma) {
645 return Fortran::common::nullopt;
647 break;
648 default:
649 break;
652 HandleRelativePosition(byteCount);
653 GotChar(byteCount);
654 return next;
656 } else if (*remaining > 0) {
657 if (auto next{GetCurrentChar(byteCount)}) {
658 if (byteCount > static_cast<std::size_t>(*remaining)) {
659 return Fortran::common::nullopt;
661 *remaining -= byteCount;
662 HandleRelativePosition(byteCount);
663 GotChar(byteCount);
664 return next;
666 if (CheckForEndOfRecord(0)) { // do padding
667 --*remaining;
668 return Fortran::common::optional<char32_t>{' '};
671 return Fortran::common::nullopt;
674 bool IoStatementState::CheckForEndOfRecord(std::size_t afterReading) {
675 const ConnectionState &connection{GetConnectionState()};
676 if (!connection.IsAtEOF()) {
677 if (auto length{connection.EffectiveRecordLength()}) {
678 if (connection.positionInRecord +
679 static_cast<std::int64_t>(afterReading) >=
680 *length) {
681 IoErrorHandler &handler{GetIoErrorHandler()};
682 const auto &modes{mutableModes()};
683 if (modes.nonAdvancing) {
684 if (connection.access == Access::Stream &&
685 connection.unterminatedRecord) {
686 // Reading final unterminated record left by a
687 // non-advancing WRITE on a stream file prior to
688 // positioning or ENDFILE.
689 handler.SignalEnd();
690 } else {
691 handler.SignalEor();
693 } else if (!modes.pad) {
694 handler.SignalError(IostatRecordReadOverrun);
696 return modes.pad; // PAD='YES'
700 return false;
703 bool IoStatementState::Inquire(
704 InquiryKeywordHash inquiry, char *out, std::size_t chars) {
705 return common::visit(
706 [&](auto &x) { return x.get().Inquire(inquiry, out, chars); }, u_);
709 bool IoStatementState::Inquire(InquiryKeywordHash inquiry, bool &out) {
710 return common::visit(
711 [&](auto &x) { return x.get().Inquire(inquiry, out); }, u_);
714 bool IoStatementState::Inquire(
715 InquiryKeywordHash inquiry, std::int64_t id, bool &out) {
716 return common::visit(
717 [&](auto &x) { return x.get().Inquire(inquiry, id, out); }, u_);
720 bool IoStatementState::Inquire(InquiryKeywordHash inquiry, std::int64_t &n) {
721 return common::visit(
722 [&](auto &x) { return x.get().Inquire(inquiry, n); }, u_);
725 std::int64_t IoStatementState::InquirePos() {
726 return common::visit([&](auto &x) { return x.get().InquirePos(); }, u_);
729 void IoStatementState::GotChar(int n) {
730 if (auto *formattedIn{
731 get_if<FormattedIoStatementState<Direction::Input>>()}) {
732 formattedIn->GotChar(n);
733 } else {
734 GetIoErrorHandler().Crash("IoStatementState::GotChar() called for "
735 "statement that is not formatted input");
739 std::size_t
740 FormattedIoStatementState<Direction::Input>::GetEditDescriptorChars() const {
741 return chars_;
744 void FormattedIoStatementState<Direction::Input>::GotChar(int n) {
745 chars_ += n;
748 bool ListDirectedStatementState<Direction::Output>::EmitLeadingSpaceOrAdvance(
749 IoStatementState &io, std::size_t length, bool isCharacter) {
750 const ConnectionState &connection{io.GetConnectionState()};
751 int space{connection.positionInRecord == 0 ||
752 !(isCharacter && lastWasUndelimitedCharacter())};
753 set_lastWasUndelimitedCharacter(false);
754 if (connection.NeedAdvance(space + length)) {
755 return io.AdvanceRecord();
757 if (space) {
758 return EmitAscii(io, " ", 1);
760 return true;
763 Fortran::common::optional<DataEdit>
764 ListDirectedStatementState<Direction::Output>::GetNextDataEdit(
765 IoStatementState &io, int maxRepeat) {
766 DataEdit edit;
767 edit.descriptor = DataEdit::ListDirected;
768 edit.repeat = maxRepeat;
769 edit.modes = io.mutableModes();
770 return edit;
773 int ListDirectedStatementState<Direction::Input>::EndIoStatement() {
774 if (repeatPosition_) {
775 repeatPosition_->Cancel();
777 return IostatOk;
780 Fortran::common::optional<DataEdit>
781 ListDirectedStatementState<Direction::Input>::GetNextDataEdit(
782 IoStatementState &io, int maxRepeat) {
783 // N.B. list-directed transfers cannot be nonadvancing (C1221)
784 ConnectionState &connection{io.GetConnectionState()};
785 DataEdit edit;
786 edit.descriptor = DataEdit::ListDirected;
787 edit.repeat = 1; // may be overridden below
788 edit.modes = io.mutableModes();
789 if (hitSlash_) { // everything after '/' is nullified
790 edit.descriptor = DataEdit::ListDirectedNullValue;
791 return edit;
793 char32_t comma{','};
794 if (edit.modes.editingFlags & decimalComma) {
795 comma = ';';
797 std::size_t byteCount{0};
798 if (remaining_ > 0 && !realPart_) { // "r*c" repetition in progress
799 RUNTIME_CHECK(io.GetIoErrorHandler(), repeatPosition_.has_value());
800 repeatPosition_.reset(); // restores the saved position
801 if (!imaginaryPart_) {
802 edit.repeat = std::min<int>(remaining_, maxRepeat);
803 auto ch{io.GetCurrentChar(byteCount)};
804 if (!ch || *ch == ' ' || *ch == '\t' || *ch == comma) {
805 // "r*" repeated null
806 edit.descriptor = DataEdit::ListDirectedNullValue;
809 remaining_ -= edit.repeat;
810 if (remaining_ > 0) {
811 repeatPosition_.emplace(io);
813 if (!imaginaryPart_) {
814 return edit;
817 // Skip separators, handle a "r*c" repeat count; see 13.10.2 in Fortran 2018
818 if (imaginaryPart_) {
819 imaginaryPart_ = false;
820 } else if (realPart_) {
821 realPart_ = false;
822 imaginaryPart_ = true;
823 edit.descriptor = DataEdit::ListDirectedImaginaryPart;
825 auto ch{io.GetNextNonBlank(byteCount)};
826 if (ch && *ch == comma && eatComma_) {
827 // Consume comma & whitespace after previous item.
828 // This includes the comma between real and imaginary components
829 // in list-directed/NAMELIST complex input.
830 // (When DECIMAL='COMMA', the comma is actually a semicolon.)
831 io.HandleRelativePosition(byteCount);
832 ch = io.GetNextNonBlank(byteCount);
834 eatComma_ = true;
835 if (!ch) {
836 return Fortran::common::nullopt;
838 if (*ch == '/') {
839 hitSlash_ = true;
840 edit.descriptor = DataEdit::ListDirectedNullValue;
841 return edit;
843 if (*ch == comma) { // separator: null value
844 edit.descriptor = DataEdit::ListDirectedNullValue;
845 return edit;
847 if (imaginaryPart_) { // can't repeat components
848 return edit;
850 if (*ch >= '0' && *ch <= '9') { // look for "r*" repetition count
851 auto start{connection.positionInRecord};
852 int r{0};
853 do {
854 static auto constexpr clamp{(std::numeric_limits<int>::max() - '9') / 10};
855 if (r >= clamp) {
856 r = 0;
857 break;
859 r = 10 * r + (*ch - '0');
860 io.HandleRelativePosition(byteCount);
861 ch = io.GetCurrentChar(byteCount);
862 } while (ch && *ch >= '0' && *ch <= '9');
863 if (r > 0 && ch && *ch == '*') { // subtle: r must be nonzero
864 io.HandleRelativePosition(byteCount);
865 ch = io.GetCurrentChar(byteCount);
866 if (ch && *ch == '/') { // r*/
867 hitSlash_ = true;
868 edit.descriptor = DataEdit::ListDirectedNullValue;
869 return edit;
871 if (!ch || *ch == ' ' || *ch == '\t' || *ch == comma) { // "r*" null
872 edit.descriptor = DataEdit::ListDirectedNullValue;
874 edit.repeat = std::min<int>(r, maxRepeat);
875 remaining_ = r - edit.repeat;
876 if (remaining_ > 0) {
877 repeatPosition_.emplace(io);
879 } else { // not a repetition count, just an integer value; rewind
880 connection.positionInRecord = start;
883 if (!imaginaryPart_ && ch && *ch == '(') {
884 realPart_ = true;
885 io.HandleRelativePosition(byteCount);
886 edit.descriptor = DataEdit::ListDirectedRealPart;
888 return edit;
891 template <Direction DIR>
892 int ExternalListIoStatementState<DIR>::EndIoStatement() {
893 if constexpr (DIR == Direction::Input) {
894 if (auto status{ListDirectedStatementState<DIR>::EndIoStatement()};
895 status != IostatOk) {
896 return status;
899 return ExternalIoStatementState<DIR>::EndIoStatement();
902 template <Direction DIR>
903 bool ExternalUnformattedIoStatementState<DIR>::Receive(
904 char *data, std::size_t bytes, std::size_t elementBytes) {
905 if constexpr (DIR == Direction::Output) {
906 this->Crash("ExternalUnformattedIoStatementState::Receive() called for "
907 "output statement");
909 return this->unit().Receive(data, bytes, elementBytes, *this);
912 template <Direction DIR>
913 ChildIoStatementState<DIR>::ChildIoStatementState(
914 ChildIo &child, const char *sourceFile, int sourceLine)
915 : IoStatementBase{sourceFile, sourceLine}, child_{child} {}
917 template <Direction DIR>
918 MutableModes &ChildIoStatementState<DIR>::mutableModes() {
919 #if !defined(RT_DEVICE_AVOID_RECURSION)
920 return child_.parent().mutableModes();
921 #else
922 ReportUnsupportedChildIo();
923 #endif
926 template <Direction DIR>
927 ConnectionState &ChildIoStatementState<DIR>::GetConnectionState() {
928 #if !defined(RT_DEVICE_AVOID_RECURSION)
929 return child_.parent().GetConnectionState();
930 #else
931 ReportUnsupportedChildIo();
932 #endif
935 template <Direction DIR>
936 ExternalFileUnit *ChildIoStatementState<DIR>::GetExternalFileUnit() const {
937 #if !defined(RT_DEVICE_AVOID_RECURSION)
938 return child_.parent().GetExternalFileUnit();
939 #else
940 ReportUnsupportedChildIo();
941 #endif
944 template <Direction DIR> int ChildIoStatementState<DIR>::EndIoStatement() {
945 CompleteOperation();
946 auto result{IoStatementBase::EndIoStatement()};
947 child_.EndIoStatement(); // annihilates *this in child_.u_
948 return result;
951 template <Direction DIR>
952 bool ChildIoStatementState<DIR>::Emit(
953 const char *data, std::size_t bytes, std::size_t elementBytes) {
954 #if !defined(RT_DEVICE_AVOID_RECURSION)
955 return child_.parent().Emit(data, bytes, elementBytes);
956 #else
957 ReportUnsupportedChildIo();
958 #endif
961 template <Direction DIR>
962 std::size_t ChildIoStatementState<DIR>::GetNextInputBytes(const char *&p) {
963 #if !defined(RT_DEVICE_AVOID_RECURSION)
964 return child_.parent().GetNextInputBytes(p);
965 #else
966 ReportUnsupportedChildIo();
967 #endif
970 template <Direction DIR>
971 void ChildIoStatementState<DIR>::HandleAbsolutePosition(std::int64_t n) {
972 #if !defined(RT_DEVICE_AVOID_RECURSION)
973 return child_.parent().HandleAbsolutePosition(n);
974 #else
975 ReportUnsupportedChildIo();
976 #endif
979 template <Direction DIR>
980 void ChildIoStatementState<DIR>::HandleRelativePosition(std::int64_t n) {
981 #if !defined(RT_DEVICE_AVOID_RECURSION)
982 return child_.parent().HandleRelativePosition(n);
983 #else
984 ReportUnsupportedChildIo();
985 #endif
988 template <Direction DIR, typename CHAR>
989 ChildFormattedIoStatementState<DIR, CHAR>::ChildFormattedIoStatementState(
990 ChildIo &child, const CHAR *format, std::size_t formatLength,
991 const Descriptor *formatDescriptor, const char *sourceFile, int sourceLine)
992 : ChildIoStatementState<DIR>{child, sourceFile, sourceLine},
993 mutableModes_{child.parent().mutableModes()}, format_{*this, format,
994 formatLength,
995 formatDescriptor} {}
997 template <Direction DIR, typename CHAR>
998 void ChildFormattedIoStatementState<DIR, CHAR>::CompleteOperation() {
999 if (!this->completedOperation()) {
1000 format_.Finish(*this);
1001 ChildIoStatementState<DIR>::CompleteOperation();
1005 template <Direction DIR, typename CHAR>
1006 int ChildFormattedIoStatementState<DIR, CHAR>::EndIoStatement() {
1007 CompleteOperation();
1008 return ChildIoStatementState<DIR>::EndIoStatement();
1011 template <Direction DIR, typename CHAR>
1012 bool ChildFormattedIoStatementState<DIR, CHAR>::AdvanceRecord(int n) {
1013 #if !defined(RT_DEVICE_AVOID_RECURSION)
1014 return this->child().parent().AdvanceRecord(n);
1015 #else
1016 this->ReportUnsupportedChildIo();
1017 #endif
1020 template <Direction DIR>
1021 bool ChildUnformattedIoStatementState<DIR>::Receive(
1022 char *data, std::size_t bytes, std::size_t elementBytes) {
1023 #if !defined(RT_DEVICE_AVOID_RECURSION)
1024 return this->child().parent().Receive(data, bytes, elementBytes);
1025 #else
1026 this->ReportUnsupportedChildIo();
1027 #endif
1030 template <Direction DIR> int ChildListIoStatementState<DIR>::EndIoStatement() {
1031 if constexpr (DIR == Direction::Input) {
1032 if (int status{ListDirectedStatementState<DIR>::EndIoStatement()};
1033 status != IostatOk) {
1034 return status;
1037 return ChildIoStatementState<DIR>::EndIoStatement();
1040 template class InternalIoStatementState<Direction::Output>;
1041 template class InternalIoStatementState<Direction::Input>;
1042 template class InternalFormattedIoStatementState<Direction::Output>;
1043 template class InternalFormattedIoStatementState<Direction::Input>;
1044 template class InternalListIoStatementState<Direction::Output>;
1045 template class InternalListIoStatementState<Direction::Input>;
1046 template class ExternalIoStatementState<Direction::Output>;
1047 template class ExternalIoStatementState<Direction::Input>;
1048 template class ExternalFormattedIoStatementState<Direction::Output>;
1049 template class ExternalFormattedIoStatementState<Direction::Input>;
1050 template class ExternalListIoStatementState<Direction::Output>;
1051 template class ExternalListIoStatementState<Direction::Input>;
1052 template class ExternalUnformattedIoStatementState<Direction::Output>;
1053 template class ExternalUnformattedIoStatementState<Direction::Input>;
1054 template class ChildIoStatementState<Direction::Output>;
1055 template class ChildIoStatementState<Direction::Input>;
1056 template class ChildFormattedIoStatementState<Direction::Output>;
1057 template class ChildFormattedIoStatementState<Direction::Input>;
1058 template class ChildListIoStatementState<Direction::Output>;
1059 template class ChildListIoStatementState<Direction::Input>;
1060 template class ChildUnformattedIoStatementState<Direction::Output>;
1061 template class ChildUnformattedIoStatementState<Direction::Input>;
1063 void ExternalMiscIoStatementState::CompleteOperation() {
1064 if (completedOperation()) {
1065 return;
1067 ExternalFileUnit &ext{unit()};
1068 switch (which_) {
1069 case Flush:
1070 ext.FlushOutput(*this);
1071 #if !defined(RT_DEVICE_COMPILATION)
1072 std::fflush(nullptr); // flushes C stdio output streams (12.9(2))
1073 #endif
1074 break;
1075 case Backspace:
1076 ext.BackspaceRecord(*this);
1077 break;
1078 case Endfile:
1079 ext.Endfile(*this);
1080 break;
1081 case Rewind:
1082 ext.Rewind(*this);
1083 break;
1084 case Wait:
1085 break; // handled in io-api.cpp BeginWait
1087 return IoStatementBase::CompleteOperation();
1090 int ExternalMiscIoStatementState::EndIoStatement() {
1091 CompleteOperation();
1092 return ExternalIoStatementBase::EndIoStatement();
1095 InquireUnitState::InquireUnitState(
1096 ExternalFileUnit &unit, const char *sourceFile, int sourceLine)
1097 : ExternalIoStatementBase{unit, sourceFile, sourceLine} {}
1099 bool InquireUnitState::Inquire(
1100 InquiryKeywordHash inquiry, char *result, std::size_t length) {
1101 if (unit().createdForInternalChildIo()) {
1102 SignalError(IostatInquireInternalUnit,
1103 "INQUIRE of unit created for defined derived type I/O of an internal "
1104 "unit");
1105 return false;
1107 const char *str{nullptr};
1108 switch (inquiry) {
1109 case HashInquiryKeyword("ACCESS"):
1110 if (!unit().IsConnected()) {
1111 str = "UNDEFINED";
1112 } else {
1113 switch (unit().access) {
1114 case Access::Sequential:
1115 str = "SEQUENTIAL";
1116 break;
1117 case Access::Direct:
1118 str = "DIRECT";
1119 break;
1120 case Access::Stream:
1121 str = "STREAM";
1122 break;
1125 break;
1126 case HashInquiryKeyword("ACTION"):
1127 str = !unit().IsConnected() ? "UNDEFINED"
1128 : unit().mayWrite() ? unit().mayRead() ? "READWRITE" : "WRITE"
1129 : "READ";
1130 break;
1131 case HashInquiryKeyword("ASYNCHRONOUS"):
1132 str = !unit().IsConnected() ? "UNDEFINED"
1133 : unit().mayAsynchronous() ? "YES"
1134 : "NO";
1135 break;
1136 case HashInquiryKeyword("BLANK"):
1137 str = !unit().IsConnected() || unit().isUnformatted.value_or(true)
1138 ? "UNDEFINED"
1139 : mutableModes().editingFlags & blankZero ? "ZERO"
1140 : "NULL";
1141 break;
1142 case HashInquiryKeyword("CARRIAGECONTROL"):
1143 str = "LIST";
1144 break;
1145 case HashInquiryKeyword("CONVERT"):
1146 str = unit().swapEndianness() ? "SWAP" : "NATIVE";
1147 break;
1148 case HashInquiryKeyword("DECIMAL"):
1149 str = !unit().IsConnected() || unit().isUnformatted.value_or(true)
1150 ? "UNDEFINED"
1151 : mutableModes().editingFlags & decimalComma ? "COMMA"
1152 : "POINT";
1153 break;
1154 case HashInquiryKeyword("DELIM"):
1155 if (!unit().IsConnected() || unit().isUnformatted.value_or(true)) {
1156 str = "UNDEFINED";
1157 } else {
1158 switch (mutableModes().delim) {
1159 case '\'':
1160 str = "APOSTROPHE";
1161 break;
1162 case '"':
1163 str = "QUOTE";
1164 break;
1165 default:
1166 str = "NONE";
1167 break;
1170 break;
1171 case HashInquiryKeyword("DIRECT"):
1172 str = !unit().IsConnected() ? "UNKNOWN"
1173 : unit().access == Access::Direct ||
1174 (unit().mayPosition() && unit().openRecl)
1175 ? "YES"
1176 : "NO";
1177 break;
1178 case HashInquiryKeyword("ENCODING"):
1179 str = !unit().IsConnected() ? "UNKNOWN"
1180 : unit().isUnformatted.value_or(true) ? "UNDEFINED"
1181 : unit().isUTF8 ? "UTF-8"
1182 : "ASCII";
1183 break;
1184 case HashInquiryKeyword("FORM"):
1185 str = !unit().IsConnected() || !unit().isUnformatted ? "UNDEFINED"
1186 : *unit().isUnformatted ? "UNFORMATTED"
1187 : "FORMATTED";
1188 break;
1189 case HashInquiryKeyword("FORMATTED"):
1190 str = !unit().IsConnected() ? "UNDEFINED"
1191 : !unit().isUnformatted ? "UNKNOWN"
1192 : *unit().isUnformatted ? "NO"
1193 : "YES";
1194 break;
1195 case HashInquiryKeyword("NAME"):
1196 str = unit().path();
1197 if (!str) {
1198 return true; // result is undefined
1200 break;
1201 case HashInquiryKeyword("PAD"):
1202 str = !unit().IsConnected() || unit().isUnformatted.value_or(true)
1203 ? "UNDEFINED"
1204 : mutableModes().pad ? "YES"
1205 : "NO";
1206 break;
1207 case HashInquiryKeyword("POSITION"):
1208 if (!unit().IsConnected() || unit().access == Access::Direct) {
1209 str = "UNDEFINED";
1210 } else {
1211 switch (unit().InquirePosition()) {
1212 case Position::Rewind:
1213 str = "REWIND";
1214 break;
1215 case Position::Append:
1216 str = "APPEND";
1217 break;
1218 case Position::AsIs:
1219 str = "ASIS";
1220 break;
1223 break;
1224 case HashInquiryKeyword("READ"):
1225 str = !unit().IsConnected() ? "UNDEFINED" : unit().mayRead() ? "YES" : "NO";
1226 break;
1227 case HashInquiryKeyword("READWRITE"):
1228 str = !unit().IsConnected() ? "UNDEFINED"
1229 : unit().mayRead() && unit().mayWrite() ? "YES"
1230 : "NO";
1231 break;
1232 case HashInquiryKeyword("ROUND"):
1233 if (!unit().IsConnected() || unit().isUnformatted.value_or(true)) {
1234 str = "UNDEFINED";
1235 } else {
1236 switch (mutableModes().round) {
1237 case decimal::FortranRounding::RoundNearest:
1238 str = "NEAREST";
1239 break;
1240 case decimal::FortranRounding::RoundUp:
1241 str = "UP";
1242 break;
1243 case decimal::FortranRounding::RoundDown:
1244 str = "DOWN";
1245 break;
1246 case decimal::FortranRounding::RoundToZero:
1247 str = "ZERO";
1248 break;
1249 case decimal::FortranRounding::RoundCompatible:
1250 str = "COMPATIBLE";
1251 break;
1254 break;
1255 case HashInquiryKeyword("SEQUENTIAL"):
1256 // "NO" for Direct, since Sequential would not work if
1257 // the unit were reopened without RECL=.
1258 str = !unit().IsConnected() ? "UNKNOWN"
1259 : unit().access == Access::Sequential ? "YES"
1260 : "NO";
1261 break;
1262 case HashInquiryKeyword("SIGN"):
1263 str = !unit().IsConnected() || unit().isUnformatted.value_or(true)
1264 ? "UNDEFINED"
1265 : mutableModes().editingFlags & signPlus ? "PLUS"
1266 : "SUPPRESS";
1267 break;
1268 case HashInquiryKeyword("STREAM"):
1269 str = !unit().IsConnected() ? "UNKNOWN"
1270 : unit().access == Access::Stream ? "YES"
1271 : "NO";
1272 break;
1273 case HashInquiryKeyword("UNFORMATTED"):
1274 str = !unit().IsConnected() || !unit().isUnformatted ? "UNKNOWN"
1275 : *unit().isUnformatted ? "YES"
1276 : "NO";
1277 break;
1278 case HashInquiryKeyword("WRITE"):
1279 str = !unit().IsConnected() ? "UNKNOWN" : unit().mayWrite() ? "YES" : "NO";
1280 break;
1282 if (str) {
1283 ToFortranDefaultCharacter(result, length, str);
1284 return true;
1285 } else {
1286 BadInquiryKeywordHashCrash(inquiry);
1287 return false;
1291 bool InquireUnitState::Inquire(InquiryKeywordHash inquiry, bool &result) {
1292 switch (inquiry) {
1293 case HashInquiryKeyword("EXIST"):
1294 result = true;
1295 return true;
1296 case HashInquiryKeyword("NAMED"):
1297 result = unit().path() != nullptr;
1298 return true;
1299 case HashInquiryKeyword("OPENED"):
1300 result = unit().IsConnected();
1301 return true;
1302 case HashInquiryKeyword("PENDING"):
1303 result = false; // asynchronous I/O is not implemented
1304 return true;
1305 default:
1306 BadInquiryKeywordHashCrash(inquiry);
1307 return false;
1311 bool InquireUnitState::Inquire(
1312 InquiryKeywordHash inquiry, std::int64_t, bool &result) {
1313 switch (inquiry) {
1314 case HashInquiryKeyword("PENDING"):
1315 result = false; // asynchronous I/O is not implemented
1316 return true;
1317 default:
1318 BadInquiryKeywordHashCrash(inquiry);
1319 return false;
1323 bool InquireUnitState::Inquire(
1324 InquiryKeywordHash inquiry, std::int64_t &result) {
1325 switch (inquiry) {
1326 case HashInquiryKeyword("NEXTREC"):
1327 if (unit().access == Access::Direct) {
1328 result = unit().currentRecordNumber;
1330 return true;
1331 case HashInquiryKeyword("NUMBER"):
1332 result = unit().unitNumber();
1333 return true;
1334 case HashInquiryKeyword("POS"):
1335 result = unit().InquirePos();
1336 return true;
1337 case HashInquiryKeyword("RECL"):
1338 if (!unit().IsConnected()) {
1339 result = -1;
1340 } else if (unit().access == Access::Stream) {
1341 result = -2;
1342 } else if (unit().openRecl) {
1343 result = *unit().openRecl;
1344 } else {
1345 result = std::numeric_limits<std::int32_t>::max();
1347 return true;
1348 case HashInquiryKeyword("SIZE"):
1349 result = -1;
1350 if (unit().IsConnected()) {
1351 unit().FlushOutput(*this);
1352 if (auto size{unit().knownSize()}) {
1353 result = *size;
1356 return true;
1357 default:
1358 BadInquiryKeywordHashCrash(inquiry);
1359 return false;
1363 InquireNoUnitState::InquireNoUnitState(
1364 const char *sourceFile, int sourceLine, int badUnitNumber)
1365 : NoUnitIoStatementState{*this, sourceFile, sourceLine, badUnitNumber} {}
1367 bool InquireNoUnitState::Inquire(
1368 InquiryKeywordHash inquiry, char *result, std::size_t length) {
1369 switch (inquiry) {
1370 case HashInquiryKeyword("ACCESS"):
1371 case HashInquiryKeyword("ACTION"):
1372 case HashInquiryKeyword("ASYNCHRONOUS"):
1373 case HashInquiryKeyword("BLANK"):
1374 case HashInquiryKeyword("CARRIAGECONTROL"):
1375 case HashInquiryKeyword("CONVERT"):
1376 case HashInquiryKeyword("DECIMAL"):
1377 case HashInquiryKeyword("DELIM"):
1378 case HashInquiryKeyword("FORM"):
1379 case HashInquiryKeyword("NAME"):
1380 case HashInquiryKeyword("PAD"):
1381 case HashInquiryKeyword("POSITION"):
1382 case HashInquiryKeyword("ROUND"):
1383 case HashInquiryKeyword("SIGN"):
1384 ToFortranDefaultCharacter(result, length, "UNDEFINED");
1385 return true;
1386 case HashInquiryKeyword("DIRECT"):
1387 case HashInquiryKeyword("ENCODING"):
1388 case HashInquiryKeyword("FORMATTED"):
1389 case HashInquiryKeyword("READ"):
1390 case HashInquiryKeyword("READWRITE"):
1391 case HashInquiryKeyword("SEQUENTIAL"):
1392 case HashInquiryKeyword("STREAM"):
1393 case HashInquiryKeyword("WRITE"):
1394 case HashInquiryKeyword("UNFORMATTED"):
1395 ToFortranDefaultCharacter(result, length, "UNKNOWN");
1396 return true;
1397 default:
1398 BadInquiryKeywordHashCrash(inquiry);
1399 return false;
1403 bool InquireNoUnitState::Inquire(InquiryKeywordHash inquiry, bool &result) {
1404 switch (inquiry) {
1405 case HashInquiryKeyword("EXIST"):
1406 result = badUnitNumber() >= 0;
1407 return true;
1408 case HashInquiryKeyword("NAMED"):
1409 case HashInquiryKeyword("OPENED"):
1410 case HashInquiryKeyword("PENDING"):
1411 result = false;
1412 return true;
1413 default:
1414 BadInquiryKeywordHashCrash(inquiry);
1415 return false;
1419 bool InquireNoUnitState::Inquire(
1420 InquiryKeywordHash inquiry, std::int64_t, bool &result) {
1421 switch (inquiry) {
1422 case HashInquiryKeyword("PENDING"):
1423 result = false;
1424 return true;
1425 default:
1426 BadInquiryKeywordHashCrash(inquiry);
1427 return false;
1431 bool InquireNoUnitState::Inquire(
1432 InquiryKeywordHash inquiry, std::int64_t &result) {
1433 switch (inquiry) {
1434 case HashInquiryKeyword("NUMBER"):
1435 result = badUnitNumber();
1436 return true;
1437 case HashInquiryKeyword("NEXTREC"):
1438 case HashInquiryKeyword("POS"):
1439 case HashInquiryKeyword("RECL"):
1440 case HashInquiryKeyword("SIZE"):
1441 result = -1;
1442 return true;
1443 default:
1444 BadInquiryKeywordHashCrash(inquiry);
1445 return false;
1449 InquireUnconnectedFileState::InquireUnconnectedFileState(
1450 OwningPtr<char> &&path, const char *sourceFile, int sourceLine)
1451 : NoUnitIoStatementState{*this, sourceFile, sourceLine}, path_{std::move(
1452 path)} {}
1454 bool InquireUnconnectedFileState::Inquire(
1455 InquiryKeywordHash inquiry, char *result, std::size_t length) {
1456 const char *str{nullptr};
1457 switch (inquiry) {
1458 case HashInquiryKeyword("ACCESS"):
1459 case HashInquiryKeyword("ACTION"):
1460 case HashInquiryKeyword("ASYNCHRONOUS"):
1461 case HashInquiryKeyword("BLANK"):
1462 case HashInquiryKeyword("CARRIAGECONTROL"):
1463 case HashInquiryKeyword("CONVERT"):
1464 case HashInquiryKeyword("DECIMAL"):
1465 case HashInquiryKeyword("DELIM"):
1466 case HashInquiryKeyword("FORM"):
1467 case HashInquiryKeyword("PAD"):
1468 case HashInquiryKeyword("POSITION"):
1469 case HashInquiryKeyword("ROUND"):
1470 case HashInquiryKeyword("SIGN"):
1471 str = "UNDEFINED";
1472 break;
1473 case HashInquiryKeyword("DIRECT"):
1474 case HashInquiryKeyword("ENCODING"):
1475 case HashInquiryKeyword("FORMATTED"):
1476 case HashInquiryKeyword("SEQUENTIAL"):
1477 case HashInquiryKeyword("STREAM"):
1478 case HashInquiryKeyword("UNFORMATTED"):
1479 str = "UNKNOWN";
1480 break;
1481 case HashInquiryKeyword("READ"):
1482 str =
1483 IsExtant(path_.get()) ? MayRead(path_.get()) ? "YES" : "NO" : "UNKNOWN";
1484 break;
1485 case HashInquiryKeyword("READWRITE"):
1486 str = IsExtant(path_.get()) ? MayReadAndWrite(path_.get()) ? "YES" : "NO"
1487 : "UNKNOWN";
1488 break;
1489 case HashInquiryKeyword("WRITE"):
1490 str = IsExtant(path_.get()) ? MayWrite(path_.get()) ? "YES" : "NO"
1491 : "UNKNOWN";
1492 break;
1493 case HashInquiryKeyword("NAME"):
1494 str = path_.get();
1495 if (!str) {
1496 return true; // result is undefined
1498 break;
1500 if (str) {
1501 ToFortranDefaultCharacter(result, length, str);
1502 return true;
1503 } else {
1504 BadInquiryKeywordHashCrash(inquiry);
1505 return false;
1509 bool InquireUnconnectedFileState::Inquire(
1510 InquiryKeywordHash inquiry, bool &result) {
1511 switch (inquiry) {
1512 case HashInquiryKeyword("EXIST"):
1513 result = IsExtant(path_.get());
1514 return true;
1515 case HashInquiryKeyword("NAMED"):
1516 result = true;
1517 return true;
1518 case HashInquiryKeyword("OPENED"):
1519 result = false;
1520 return true;
1521 case HashInquiryKeyword("PENDING"):
1522 result = false;
1523 return true;
1524 default:
1525 BadInquiryKeywordHashCrash(inquiry);
1526 return false;
1530 bool InquireUnconnectedFileState::Inquire(
1531 InquiryKeywordHash inquiry, std::int64_t, bool &result) {
1532 switch (inquiry) {
1533 case HashInquiryKeyword("PENDING"):
1534 result = false;
1535 return true;
1536 default:
1537 BadInquiryKeywordHashCrash(inquiry);
1538 return false;
1542 bool InquireUnconnectedFileState::Inquire(
1543 InquiryKeywordHash inquiry, std::int64_t &result) {
1544 switch (inquiry) {
1545 case HashInquiryKeyword("NEXTREC"):
1546 case HashInquiryKeyword("NUMBER"):
1547 case HashInquiryKeyword("POS"):
1548 case HashInquiryKeyword("RECL"):
1549 result = -1;
1550 return true;
1551 case HashInquiryKeyword("SIZE"):
1552 result = SizeInBytes(path_.get());
1553 return true;
1554 default:
1555 BadInquiryKeywordHashCrash(inquiry);
1556 return false;
1560 InquireIOLengthState::InquireIOLengthState(
1561 const char *sourceFile, int sourceLine)
1562 : NoUnitIoStatementState{*this, sourceFile, sourceLine} {}
1564 bool InquireIOLengthState::Emit(const char *, std::size_t bytes, std::size_t) {
1565 bytes_ += bytes;
1566 return true;
1569 int ErroneousIoStatementState::EndIoStatement() {
1570 SignalPendingError();
1571 if (unit_) {
1572 unit_->EndIoStatement();
1574 return IoStatementBase::EndIoStatement();
1577 RT_OFFLOAD_API_GROUP_END
1578 } // namespace Fortran::runtime::io