[AArch64,ELF] Restrict MOVZ/MOVK to non-PIC large code model (#70178)
[llvm-project.git] / flang / runtime / io-stmt.cpp
blobdedf1f8364ad373b6a3a87d8ab1ce122e799c720
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 // F'2023 12.5.6.13 - NEWUNIT= requires either FILE= or STATUS='SCRATCH'
244 if (isNewUnit_ && !path_.get() &&
245 status_.value_or(OpenStatus::Unknown) != OpenStatus::Scratch) {
246 SignalError(IostatBadNewUnit);
247 status_ = OpenStatus::Scratch; // error recovery
249 if (path_.get() || wasExtant_ ||
250 (status_ && *status_ == OpenStatus::Scratch)) {
251 if (unit().OpenUnit(status_, action_, position_.value_or(Position::AsIs),
252 std::move(path_), pathLength_, convert_, *this)) {
253 wasExtant_ = false; // existing unit was closed
255 } else {
256 unit().OpenAnonymousUnit(
257 status_, action_, position_.value_or(Position::AsIs), convert_, *this);
259 if (access_) {
260 if (*access_ != unit().access) {
261 if (wasExtant_) {
262 SignalError("ACCESS= may not be changed on an open unit");
263 access_.reset();
266 if (access_) {
267 unit().access = *access_;
270 if (!unit().isUnformatted) {
271 unit().isUnformatted = isUnformatted_;
273 if (isUnformatted_ && *isUnformatted_ != *unit().isUnformatted) {
274 if (wasExtant_) {
275 SignalError("FORM= may not be changed on an open unit");
277 unit().isUnformatted = *isUnformatted_;
279 if (!unit().isUnformatted) {
280 // Set default format (C.7.4 point 2).
281 unit().isUnformatted = unit().access != Access::Sequential;
283 if (!wasExtant_ && InError()) {
284 // Release the new unit on failure
285 unit().CloseUnit(CloseStatus::Delete, *this);
286 unit().DestroyClosed();
288 IoStatementBase::CompleteOperation();
291 int OpenStatementState::EndIoStatement() {
292 CompleteOperation();
293 return ExternalIoStatementBase::EndIoStatement();
296 int CloseStatementState::EndIoStatement() {
297 CompleteOperation();
298 int result{ExternalIoStatementBase::EndIoStatement()};
299 unit().CloseUnit(status_, *this);
300 unit().DestroyClosed();
301 return result;
304 void NoUnitIoStatementState::CompleteOperation() {
305 SignalPendingError();
306 IoStatementBase::CompleteOperation();
309 int NoUnitIoStatementState::EndIoStatement() {
310 CompleteOperation();
311 auto result{IoStatementBase::EndIoStatement()};
312 FreeMemory(this);
313 return result;
316 template <Direction DIR>
317 ExternalIoStatementState<DIR>::ExternalIoStatementState(
318 ExternalFileUnit &unit, const char *sourceFile, int sourceLine)
319 : ExternalIoStatementBase{unit, sourceFile, sourceLine}, mutableModes_{
320 unit.modes} {
321 if constexpr (DIR == Direction::Output) {
322 // If the last statement was a non-advancing IO input statement, the unit
323 // furthestPositionInRecord was not advanced, but the positionInRecord may
324 // have been advanced. Advance furthestPositionInRecord here to avoid
325 // overwriting the part of the record that has been read with blanks.
326 unit.furthestPositionInRecord =
327 std::max(unit.furthestPositionInRecord, unit.positionInRecord);
331 template <Direction DIR>
332 void ExternalIoStatementState<DIR>::CompleteOperation() {
333 if (completedOperation()) {
334 return;
336 if constexpr (DIR == Direction::Input) {
337 BeginReadingRecord(); // in case there were no I/O items
338 if (mutableModes().nonAdvancing && !InError()) {
339 unit().leftTabLimit = unit().furthestPositionInRecord;
340 } else {
341 FinishReadingRecord();
343 } else { // output
344 if (mutableModes().nonAdvancing) {
345 // Make effects of positioning past the last Emit() visible with blanks.
346 if (unit().positionInRecord > unit().furthestPositionInRecord) {
347 unit().Emit("", 0, 1, *this); // Emit() will pad
349 unit().leftTabLimit = unit().positionInRecord;
350 } else {
351 unit().AdvanceRecord(*this);
353 unit().FlushIfTerminal(*this);
355 return IoStatementBase::CompleteOperation();
358 template <Direction DIR> int ExternalIoStatementState<DIR>::EndIoStatement() {
359 CompleteOperation();
360 return ExternalIoStatementBase::EndIoStatement();
363 template <Direction DIR>
364 bool ExternalIoStatementState<DIR>::Emit(
365 const char *data, std::size_t bytes, std::size_t elementBytes) {
366 if constexpr (DIR == Direction::Input) {
367 Crash("ExternalIoStatementState::Emit(char) called for input statement");
369 return unit().Emit(data, bytes, elementBytes, *this);
372 template <Direction DIR>
373 std::size_t ExternalIoStatementState<DIR>::GetNextInputBytes(const char *&p) {
374 return unit().GetNextInputBytes(p, *this);
377 template <Direction DIR>
378 bool ExternalIoStatementState<DIR>::AdvanceRecord(int n) {
379 while (n-- > 0) {
380 if (!unit().AdvanceRecord(*this)) {
381 return false;
384 return true;
387 template <Direction DIR> void ExternalIoStatementState<DIR>::BackspaceRecord() {
388 unit().BackspaceRecord(*this);
391 template <Direction DIR>
392 void ExternalIoStatementState<DIR>::HandleAbsolutePosition(std::int64_t n) {
393 return unit().HandleAbsolutePosition(n);
396 template <Direction DIR>
397 void ExternalIoStatementState<DIR>::HandleRelativePosition(std::int64_t n) {
398 return unit().HandleRelativePosition(n);
401 template <Direction DIR>
402 bool ExternalIoStatementState<DIR>::BeginReadingRecord() {
403 if constexpr (DIR == Direction::Input) {
404 return unit().BeginReadingRecord(*this);
405 } else {
406 Crash("ExternalIoStatementState<Direction::Output>::BeginReadingRecord() "
407 "called");
408 return false;
412 template <Direction DIR>
413 void ExternalIoStatementState<DIR>::FinishReadingRecord() {
414 if constexpr (DIR == Direction::Input) {
415 unit().FinishReadingRecord(*this);
416 } else {
417 Crash("ExternalIoStatementState<Direction::Output>::FinishReadingRecord() "
418 "called");
422 template <Direction DIR, typename CHAR>
423 ExternalFormattedIoStatementState<DIR, CHAR>::ExternalFormattedIoStatementState(
424 ExternalFileUnit &unit, const CHAR *format, std::size_t formatLength,
425 const Descriptor *formatDescriptor, const char *sourceFile, int sourceLine)
426 : ExternalIoStatementState<DIR>{unit, sourceFile, sourceLine},
427 format_{*this, format, formatLength, formatDescriptor} {}
429 template <Direction DIR, typename CHAR>
430 void ExternalFormattedIoStatementState<DIR, CHAR>::CompleteOperation() {
431 if (this->completedOperation()) {
432 return;
434 if constexpr (DIR == Direction::Input) {
435 this->BeginReadingRecord(); // in case there were no I/O items
437 format_.Finish(*this);
438 return ExternalIoStatementState<DIR>::CompleteOperation();
441 template <Direction DIR, typename CHAR>
442 int ExternalFormattedIoStatementState<DIR, CHAR>::EndIoStatement() {
443 CompleteOperation();
444 return ExternalIoStatementState<DIR>::EndIoStatement();
447 std::optional<DataEdit> IoStatementState::GetNextDataEdit(int n) {
448 return common::visit(
449 [&](auto &x) { return x.get().GetNextDataEdit(*this, n); }, u_);
452 bool IoStatementState::Emit(
453 const char *data, std::size_t bytes, std::size_t elementBytes) {
454 return common::visit(
455 [=](auto &x) { return x.get().Emit(data, bytes, elementBytes); }, u_);
458 bool IoStatementState::Receive(
459 char *data, std::size_t n, std::size_t elementBytes) {
460 return common::visit(
461 [=](auto &x) { return x.get().Receive(data, n, elementBytes); }, u_);
464 std::size_t IoStatementState::GetNextInputBytes(const char *&p) {
465 return common::visit(
466 [&](auto &x) { return x.get().GetNextInputBytes(p); }, u_);
469 bool IoStatementState::AdvanceRecord(int n) {
470 return common::visit([=](auto &x) { return x.get().AdvanceRecord(n); }, u_);
473 void IoStatementState::BackspaceRecord() {
474 common::visit([](auto &x) { x.get().BackspaceRecord(); }, u_);
477 void IoStatementState::HandleRelativePosition(std::int64_t n) {
478 common::visit([=](auto &x) { x.get().HandleRelativePosition(n); }, u_);
481 void IoStatementState::HandleAbsolutePosition(std::int64_t n) {
482 common::visit([=](auto &x) { x.get().HandleAbsolutePosition(n); }, u_);
485 void IoStatementState::CompleteOperation() {
486 common::visit([](auto &x) { x.get().CompleteOperation(); }, u_);
489 int IoStatementState::EndIoStatement() {
490 return common::visit([](auto &x) { return x.get().EndIoStatement(); }, u_);
493 ConnectionState &IoStatementState::GetConnectionState() {
494 return common::visit(
495 [](auto &x) -> ConnectionState & { return x.get().GetConnectionState(); },
496 u_);
499 MutableModes &IoStatementState::mutableModes() {
500 return common::visit(
501 [](auto &x) -> MutableModes & { return x.get().mutableModes(); }, u_);
504 bool IoStatementState::BeginReadingRecord() {
505 return common::visit(
506 [](auto &x) { return x.get().BeginReadingRecord(); }, u_);
509 IoErrorHandler &IoStatementState::GetIoErrorHandler() const {
510 return common::visit(
511 [](auto &x) -> IoErrorHandler & {
512 return static_cast<IoErrorHandler &>(x.get());
514 u_);
517 ExternalFileUnit *IoStatementState::GetExternalFileUnit() const {
518 return common::visit(
519 [](auto &x) { return x.get().GetExternalFileUnit(); }, u_);
522 std::optional<char32_t> IoStatementState::GetCurrentChar(
523 std::size_t &byteCount) {
524 const char *p{nullptr};
525 std::size_t bytes{GetNextInputBytes(p)};
526 if (bytes == 0) {
527 byteCount = 0;
528 return std::nullopt;
529 } else {
530 const ConnectionState &connection{GetConnectionState()};
531 if (connection.isUTF8) {
532 std::size_t length{MeasureUTF8Bytes(*p)};
533 if (length <= bytes) {
534 if (auto result{DecodeUTF8(p)}) {
535 byteCount = length;
536 return result;
539 GetIoErrorHandler().SignalError(IostatUTF8Decoding);
540 // Error recovery: return the next byte
541 } else if (connection.internalIoCharKind > 1) {
542 byteCount = connection.internalIoCharKind;
543 if (byteCount == 2) {
544 return *reinterpret_cast<const char16_t *>(p);
545 } else {
546 return *reinterpret_cast<const char32_t *>(p);
549 byteCount = 1;
550 return *p;
554 std::optional<char32_t> IoStatementState::NextInField(
555 std::optional<int> &remaining, const DataEdit &edit) {
556 std::size_t byteCount{0};
557 if (!remaining) { // Stream, list-directed, or NAMELIST
558 if (auto next{GetCurrentChar(byteCount)}) {
559 if (edit.IsListDirected()) {
560 // list-directed or NAMELIST: check for separators
561 switch (*next) {
562 case ' ':
563 case '\t':
564 case '/':
565 case '(':
566 case ')':
567 case '\'':
568 case '"':
569 case '*':
570 case '\n': // for stream access
571 return std::nullopt;
572 case ',':
573 if (!(edit.modes.editingFlags & decimalComma)) {
574 return std::nullopt;
576 break;
577 case ';':
578 if (edit.modes.editingFlags & decimalComma) {
579 return std::nullopt;
581 break;
582 default:
583 break;
586 HandleRelativePosition(byteCount);
587 GotChar(byteCount);
588 return next;
590 } else if (*remaining > 0) {
591 if (auto next{GetCurrentChar(byteCount)}) {
592 if (byteCount > static_cast<std::size_t>(*remaining)) {
593 return std::nullopt;
595 *remaining -= byteCount;
596 HandleRelativePosition(byteCount);
597 GotChar(byteCount);
598 return next;
600 if (CheckForEndOfRecord(0)) { // do padding
601 --*remaining;
602 return std::optional<char32_t>{' '};
605 return std::nullopt;
608 bool IoStatementState::CheckForEndOfRecord(std::size_t afterReading) {
609 const ConnectionState &connection{GetConnectionState()};
610 if (!connection.IsAtEOF()) {
611 if (auto length{connection.EffectiveRecordLength()}) {
612 if (connection.positionInRecord +
613 static_cast<std::int64_t>(afterReading) >=
614 *length) {
615 IoErrorHandler &handler{GetIoErrorHandler()};
616 const auto &modes{mutableModes()};
617 if (modes.nonAdvancing) {
618 if (connection.access == Access::Stream &&
619 connection.unterminatedRecord) {
620 // Reading final unterminated record left by a
621 // non-advancing WRITE on a stream file prior to
622 // positioning or ENDFILE.
623 handler.SignalEnd();
624 } else {
625 handler.SignalEor();
627 } else if (!modes.pad) {
628 handler.SignalError(IostatRecordReadOverrun);
630 return modes.pad; // PAD='YES'
634 return false;
637 bool IoStatementState::Inquire(
638 InquiryKeywordHash inquiry, char *out, std::size_t chars) {
639 return common::visit(
640 [&](auto &x) { return x.get().Inquire(inquiry, out, chars); }, u_);
643 bool IoStatementState::Inquire(InquiryKeywordHash inquiry, bool &out) {
644 return common::visit(
645 [&](auto &x) { return x.get().Inquire(inquiry, out); }, u_);
648 bool IoStatementState::Inquire(
649 InquiryKeywordHash inquiry, std::int64_t id, bool &out) {
650 return common::visit(
651 [&](auto &x) { return x.get().Inquire(inquiry, id, out); }, u_);
654 bool IoStatementState::Inquire(InquiryKeywordHash inquiry, std::int64_t &n) {
655 return common::visit(
656 [&](auto &x) { return x.get().Inquire(inquiry, n); }, u_);
659 std::int64_t IoStatementState::InquirePos() {
660 return common::visit([&](auto &x) { return x.get().InquirePos(); }, u_);
663 void IoStatementState::GotChar(int n) {
664 if (auto *formattedIn{
665 get_if<FormattedIoStatementState<Direction::Input>>()}) {
666 formattedIn->GotChar(n);
667 } else {
668 GetIoErrorHandler().Crash("IoStatementState::GotChar() called for "
669 "statement that is not formatted input");
673 std::size_t
674 FormattedIoStatementState<Direction::Input>::GetEditDescriptorChars() const {
675 return chars_;
678 void FormattedIoStatementState<Direction::Input>::GotChar(int n) {
679 chars_ += n;
682 bool ListDirectedStatementState<Direction::Output>::EmitLeadingSpaceOrAdvance(
683 IoStatementState &io, std::size_t length, bool isCharacter) {
684 if (length == 0) {
685 return true;
687 const ConnectionState &connection{io.GetConnectionState()};
688 int space{connection.positionInRecord == 0 ||
689 !(isCharacter && lastWasUndelimitedCharacter())};
690 set_lastWasUndelimitedCharacter(false);
691 if (connection.NeedAdvance(space + length)) {
692 return io.AdvanceRecord();
694 if (space) {
695 return EmitAscii(io, " ", 1);
697 return true;
700 std::optional<DataEdit>
701 ListDirectedStatementState<Direction::Output>::GetNextDataEdit(
702 IoStatementState &io, int maxRepeat) {
703 DataEdit edit;
704 edit.descriptor = DataEdit::ListDirected;
705 edit.repeat = maxRepeat;
706 edit.modes = io.mutableModes();
707 return edit;
710 std::optional<DataEdit>
711 ListDirectedStatementState<Direction::Input>::GetNextDataEdit(
712 IoStatementState &io, int maxRepeat) {
713 // N.B. list-directed transfers cannot be nonadvancing (C1221)
714 ConnectionState &connection{io.GetConnectionState()};
715 DataEdit edit;
716 edit.descriptor = DataEdit::ListDirected;
717 edit.repeat = 1; // may be overridden below
718 edit.modes = io.mutableModes();
719 if (hitSlash_) { // everything after '/' is nullified
720 edit.descriptor = DataEdit::ListDirectedNullValue;
721 return edit;
723 char32_t comma{','};
724 if (edit.modes.editingFlags & decimalComma) {
725 comma = ';';
727 std::size_t byteCount{0};
728 if (remaining_ > 0 && !realPart_) { // "r*c" repetition in progress
729 RUNTIME_CHECK(io.GetIoErrorHandler(), repeatPosition_.has_value());
730 repeatPosition_.reset(); // restores the saved position
731 if (!imaginaryPart_) {
732 edit.repeat = std::min<int>(remaining_, maxRepeat);
733 auto ch{io.GetCurrentChar(byteCount)};
734 if (!ch || *ch == ' ' || *ch == '\t' || *ch == comma) {
735 // "r*" repeated null
736 edit.descriptor = DataEdit::ListDirectedNullValue;
739 remaining_ -= edit.repeat;
740 if (remaining_ > 0) {
741 repeatPosition_.emplace(io);
743 if (!imaginaryPart_) {
744 return edit;
747 // Skip separators, handle a "r*c" repeat count; see 13.10.2 in Fortran 2018
748 if (imaginaryPart_) {
749 imaginaryPart_ = false;
750 } else if (realPart_) {
751 realPart_ = false;
752 imaginaryPart_ = true;
753 edit.descriptor = DataEdit::ListDirectedImaginaryPart;
755 auto ch{io.GetNextNonBlank(byteCount)};
756 if (ch && *ch == comma && eatComma_) {
757 // Consume comma & whitespace after previous item.
758 // This includes the comma between real and imaginary components
759 // in list-directed/NAMELIST complex input.
760 // (When DECIMAL='COMMA', the comma is actually a semicolon.)
761 io.HandleRelativePosition(byteCount);
762 ch = io.GetNextNonBlank(byteCount);
764 eatComma_ = true;
765 if (!ch) {
766 return std::nullopt;
768 if (*ch == '/') {
769 hitSlash_ = true;
770 edit.descriptor = DataEdit::ListDirectedNullValue;
771 return edit;
773 if (*ch == comma) { // separator: null value
774 edit.descriptor = DataEdit::ListDirectedNullValue;
775 return edit;
777 if (imaginaryPart_) { // can't repeat components
778 return edit;
780 if (*ch >= '0' && *ch <= '9') { // look for "r*" repetition count
781 auto start{connection.positionInRecord};
782 int r{0};
783 do {
784 static auto constexpr clamp{(std::numeric_limits<int>::max() - '9') / 10};
785 if (r >= clamp) {
786 r = 0;
787 break;
789 r = 10 * r + (*ch - '0');
790 io.HandleRelativePosition(byteCount);
791 ch = io.GetCurrentChar(byteCount);
792 } while (ch && *ch >= '0' && *ch <= '9');
793 if (r > 0 && ch && *ch == '*') { // subtle: r must be nonzero
794 io.HandleRelativePosition(byteCount);
795 ch = io.GetCurrentChar(byteCount);
796 if (ch && *ch == '/') { // r*/
797 hitSlash_ = true;
798 edit.descriptor = DataEdit::ListDirectedNullValue;
799 return edit;
801 if (!ch || *ch == ' ' || *ch == '\t' || *ch == comma) { // "r*" null
802 edit.descriptor = DataEdit::ListDirectedNullValue;
804 edit.repeat = std::min<int>(r, maxRepeat);
805 remaining_ = r - edit.repeat;
806 if (remaining_ > 0) {
807 repeatPosition_.emplace(io);
809 } else { // not a repetition count, just an integer value; rewind
810 connection.positionInRecord = start;
813 if (!imaginaryPart_ && ch && *ch == '(') {
814 realPart_ = true;
815 io.HandleRelativePosition(byteCount);
816 edit.descriptor = DataEdit::ListDirectedRealPart;
818 return edit;
821 template <Direction DIR>
822 bool ExternalUnformattedIoStatementState<DIR>::Receive(
823 char *data, std::size_t bytes, std::size_t elementBytes) {
824 if constexpr (DIR == Direction::Output) {
825 this->Crash("ExternalUnformattedIoStatementState::Receive() called for "
826 "output statement");
828 return this->unit().Receive(data, bytes, elementBytes, *this);
831 template <Direction DIR>
832 ChildIoStatementState<DIR>::ChildIoStatementState(
833 ChildIo &child, const char *sourceFile, int sourceLine)
834 : IoStatementBase{sourceFile, sourceLine}, child_{child} {}
836 template <Direction DIR>
837 MutableModes &ChildIoStatementState<DIR>::mutableModes() {
838 return child_.parent().mutableModes();
841 template <Direction DIR>
842 ConnectionState &ChildIoStatementState<DIR>::GetConnectionState() {
843 return child_.parent().GetConnectionState();
846 template <Direction DIR>
847 ExternalFileUnit *ChildIoStatementState<DIR>::GetExternalFileUnit() const {
848 return child_.parent().GetExternalFileUnit();
851 template <Direction DIR> int ChildIoStatementState<DIR>::EndIoStatement() {
852 CompleteOperation();
853 auto result{IoStatementBase::EndIoStatement()};
854 child_.EndIoStatement(); // annihilates *this in child_.u_
855 return result;
858 template <Direction DIR>
859 bool ChildIoStatementState<DIR>::Emit(
860 const char *data, std::size_t bytes, std::size_t elementBytes) {
861 return child_.parent().Emit(data, bytes, elementBytes);
864 template <Direction DIR>
865 std::size_t ChildIoStatementState<DIR>::GetNextInputBytes(const char *&p) {
866 return child_.parent().GetNextInputBytes(p);
869 template <Direction DIR>
870 void ChildIoStatementState<DIR>::HandleAbsolutePosition(std::int64_t n) {
871 return child_.parent().HandleAbsolutePosition(n);
874 template <Direction DIR>
875 void ChildIoStatementState<DIR>::HandleRelativePosition(std::int64_t n) {
876 return child_.parent().HandleRelativePosition(n);
879 template <Direction DIR, typename CHAR>
880 ChildFormattedIoStatementState<DIR, CHAR>::ChildFormattedIoStatementState(
881 ChildIo &child, const CHAR *format, std::size_t formatLength,
882 const Descriptor *formatDescriptor, const char *sourceFile, int sourceLine)
883 : ChildIoStatementState<DIR>{child, sourceFile, sourceLine},
884 mutableModes_{child.parent().mutableModes()}, format_{*this, format,
885 formatLength,
886 formatDescriptor} {}
888 template <Direction DIR, typename CHAR>
889 void ChildFormattedIoStatementState<DIR, CHAR>::CompleteOperation() {
890 if (!this->completedOperation()) {
891 format_.Finish(*this);
892 ChildIoStatementState<DIR>::CompleteOperation();
896 template <Direction DIR, typename CHAR>
897 int ChildFormattedIoStatementState<DIR, CHAR>::EndIoStatement() {
898 CompleteOperation();
899 return ChildIoStatementState<DIR>::EndIoStatement();
902 template <Direction DIR, typename CHAR>
903 bool ChildFormattedIoStatementState<DIR, CHAR>::AdvanceRecord(int n) {
904 return this->child().parent().AdvanceRecord(n);
907 template <Direction DIR>
908 bool ChildUnformattedIoStatementState<DIR>::Receive(
909 char *data, std::size_t bytes, std::size_t elementBytes) {
910 return this->child().parent().Receive(data, bytes, elementBytes);
913 template class InternalIoStatementState<Direction::Output>;
914 template class InternalIoStatementState<Direction::Input>;
915 template class InternalFormattedIoStatementState<Direction::Output>;
916 template class InternalFormattedIoStatementState<Direction::Input>;
917 template class InternalListIoStatementState<Direction::Output>;
918 template class InternalListIoStatementState<Direction::Input>;
919 template class ExternalIoStatementState<Direction::Output>;
920 template class ExternalIoStatementState<Direction::Input>;
921 template class ExternalFormattedIoStatementState<Direction::Output>;
922 template class ExternalFormattedIoStatementState<Direction::Input>;
923 template class ExternalListIoStatementState<Direction::Output>;
924 template class ExternalListIoStatementState<Direction::Input>;
925 template class ExternalUnformattedIoStatementState<Direction::Output>;
926 template class ExternalUnformattedIoStatementState<Direction::Input>;
927 template class ChildIoStatementState<Direction::Output>;
928 template class ChildIoStatementState<Direction::Input>;
929 template class ChildFormattedIoStatementState<Direction::Output>;
930 template class ChildFormattedIoStatementState<Direction::Input>;
931 template class ChildListIoStatementState<Direction::Output>;
932 template class ChildListIoStatementState<Direction::Input>;
933 template class ChildUnformattedIoStatementState<Direction::Output>;
934 template class ChildUnformattedIoStatementState<Direction::Input>;
936 void ExternalMiscIoStatementState::CompleteOperation() {
937 if (completedOperation()) {
938 return;
940 ExternalFileUnit &ext{unit()};
941 switch (which_) {
942 case Flush:
943 ext.FlushOutput(*this);
944 std::fflush(nullptr); // flushes C stdio output streams (12.9(2))
945 break;
946 case Backspace:
947 ext.BackspaceRecord(*this);
948 break;
949 case Endfile:
950 ext.Endfile(*this);
951 break;
952 case Rewind:
953 ext.Rewind(*this);
954 break;
955 case Wait:
956 break; // handled in io-api.cpp BeginWait
958 return IoStatementBase::CompleteOperation();
961 int ExternalMiscIoStatementState::EndIoStatement() {
962 CompleteOperation();
963 return ExternalIoStatementBase::EndIoStatement();
966 InquireUnitState::InquireUnitState(
967 ExternalFileUnit &unit, const char *sourceFile, int sourceLine)
968 : ExternalIoStatementBase{unit, sourceFile, sourceLine} {}
970 bool InquireUnitState::Inquire(
971 InquiryKeywordHash inquiry, char *result, std::size_t length) {
972 if (unit().createdForInternalChildIo()) {
973 SignalError(IostatInquireInternalUnit,
974 "INQUIRE of unit created for defined derived type I/O of an internal "
975 "unit");
976 return false;
978 const char *str{nullptr};
979 switch (inquiry) {
980 case HashInquiryKeyword("ACCESS"):
981 if (!unit().IsConnected()) {
982 str = "UNDEFINED";
983 } else {
984 switch (unit().access) {
985 case Access::Sequential:
986 str = "SEQUENTIAL";
987 break;
988 case Access::Direct:
989 str = "DIRECT";
990 break;
991 case Access::Stream:
992 str = "STREAM";
993 break;
996 break;
997 case HashInquiryKeyword("ACTION"):
998 str = !unit().IsConnected() ? "UNDEFINED"
999 : unit().mayWrite() ? unit().mayRead() ? "READWRITE" : "WRITE"
1000 : "READ";
1001 break;
1002 case HashInquiryKeyword("ASYNCHRONOUS"):
1003 str = !unit().IsConnected() ? "UNDEFINED"
1004 : unit().mayAsynchronous() ? "YES"
1005 : "NO";
1006 break;
1007 case HashInquiryKeyword("BLANK"):
1008 str = !unit().IsConnected() || unit().isUnformatted.value_or(true)
1009 ? "UNDEFINED"
1010 : mutableModes().editingFlags & blankZero ? "ZERO"
1011 : "NULL";
1012 break;
1013 case HashInquiryKeyword("CARRIAGECONTROL"):
1014 str = "LIST";
1015 break;
1016 case HashInquiryKeyword("CONVERT"):
1017 str = unit().swapEndianness() ? "SWAP" : "NATIVE";
1018 break;
1019 case HashInquiryKeyword("DECIMAL"):
1020 str = !unit().IsConnected() || unit().isUnformatted.value_or(true)
1021 ? "UNDEFINED"
1022 : mutableModes().editingFlags & decimalComma ? "COMMA"
1023 : "POINT";
1024 break;
1025 case HashInquiryKeyword("DELIM"):
1026 if (!unit().IsConnected() || unit().isUnformatted.value_or(true)) {
1027 str = "UNDEFINED";
1028 } else {
1029 switch (mutableModes().delim) {
1030 case '\'':
1031 str = "APOSTROPHE";
1032 break;
1033 case '"':
1034 str = "QUOTE";
1035 break;
1036 default:
1037 str = "NONE";
1038 break;
1041 break;
1042 case HashInquiryKeyword("DIRECT"):
1043 str = !unit().IsConnected() ? "UNKNOWN"
1044 : unit().access == Access::Direct ||
1045 (unit().mayPosition() && unit().openRecl)
1046 ? "YES"
1047 : "NO";
1048 break;
1049 case HashInquiryKeyword("ENCODING"):
1050 str = !unit().IsConnected() ? "UNKNOWN"
1051 : unit().isUnformatted.value_or(true) ? "UNDEFINED"
1052 : unit().isUTF8 ? "UTF-8"
1053 : "ASCII";
1054 break;
1055 case HashInquiryKeyword("FORM"):
1056 str = !unit().IsConnected() || !unit().isUnformatted ? "UNDEFINED"
1057 : *unit().isUnformatted ? "UNFORMATTED"
1058 : "FORMATTED";
1059 break;
1060 case HashInquiryKeyword("FORMATTED"):
1061 str = !unit().IsConnected() ? "UNDEFINED"
1062 : !unit().isUnformatted ? "UNKNOWN"
1063 : *unit().isUnformatted ? "NO"
1064 : "YES";
1065 break;
1066 case HashInquiryKeyword("NAME"):
1067 str = unit().path();
1068 if (!str) {
1069 return true; // result is undefined
1071 break;
1072 case HashInquiryKeyword("PAD"):
1073 str = !unit().IsConnected() || unit().isUnformatted.value_or(true)
1074 ? "UNDEFINED"
1075 : mutableModes().pad ? "YES"
1076 : "NO";
1077 break;
1078 case HashInquiryKeyword("POSITION"):
1079 if (!unit().IsConnected() || unit().access == Access::Direct) {
1080 str = "UNDEFINED";
1081 } else {
1082 switch (unit().InquirePosition()) {
1083 case Position::Rewind:
1084 str = "REWIND";
1085 break;
1086 case Position::Append:
1087 str = "APPEND";
1088 break;
1089 case Position::AsIs:
1090 str = "ASIS";
1091 break;
1094 break;
1095 case HashInquiryKeyword("READ"):
1096 str = !unit().IsConnected() ? "UNDEFINED" : unit().mayRead() ? "YES" : "NO";
1097 break;
1098 case HashInquiryKeyword("READWRITE"):
1099 str = !unit().IsConnected() ? "UNDEFINED"
1100 : unit().mayRead() && unit().mayWrite() ? "YES"
1101 : "NO";
1102 break;
1103 case HashInquiryKeyword("ROUND"):
1104 if (!unit().IsConnected() || unit().isUnformatted.value_or(true)) {
1105 str = "UNDEFINED";
1106 } else {
1107 switch (mutableModes().round) {
1108 case decimal::FortranRounding::RoundNearest:
1109 str = "NEAREST";
1110 break;
1111 case decimal::FortranRounding::RoundUp:
1112 str = "UP";
1113 break;
1114 case decimal::FortranRounding::RoundDown:
1115 str = "DOWN";
1116 break;
1117 case decimal::FortranRounding::RoundToZero:
1118 str = "ZERO";
1119 break;
1120 case decimal::FortranRounding::RoundCompatible:
1121 str = "COMPATIBLE";
1122 break;
1125 break;
1126 case HashInquiryKeyword("SEQUENTIAL"):
1127 // "NO" for Direct, since Sequential would not work if
1128 // the unit were reopened without RECL=.
1129 str = !unit().IsConnected() ? "UNKNOWN"
1130 : unit().access == Access::Sequential ? "YES"
1131 : "NO";
1132 break;
1133 case HashInquiryKeyword("SIGN"):
1134 str = !unit().IsConnected() || unit().isUnformatted.value_or(true)
1135 ? "UNDEFINED"
1136 : mutableModes().editingFlags & signPlus ? "PLUS"
1137 : "SUPPRESS";
1138 break;
1139 case HashInquiryKeyword("STREAM"):
1140 str = !unit().IsConnected() ? "UNKNOWN"
1141 : unit().access == Access::Stream ? "YES"
1142 : "NO";
1143 break;
1144 case HashInquiryKeyword("UNFORMATTED"):
1145 str = !unit().IsConnected() || !unit().isUnformatted ? "UNKNOWN"
1146 : *unit().isUnformatted ? "YES"
1147 : "NO";
1148 break;
1149 case HashInquiryKeyword("WRITE"):
1150 str = !unit().IsConnected() ? "UNKNOWN" : unit().mayWrite() ? "YES" : "NO";
1151 break;
1153 if (str) {
1154 ToFortranDefaultCharacter(result, length, str);
1155 return true;
1156 } else {
1157 BadInquiryKeywordHashCrash(inquiry);
1158 return false;
1162 bool InquireUnitState::Inquire(InquiryKeywordHash inquiry, bool &result) {
1163 switch (inquiry) {
1164 case HashInquiryKeyword("EXIST"):
1165 result = true;
1166 return true;
1167 case HashInquiryKeyword("NAMED"):
1168 result = unit().path() != nullptr;
1169 return true;
1170 case HashInquiryKeyword("OPENED"):
1171 result = unit().IsConnected();
1172 return true;
1173 case HashInquiryKeyword("PENDING"):
1174 result = false; // asynchronous I/O is not implemented
1175 return true;
1176 default:
1177 BadInquiryKeywordHashCrash(inquiry);
1178 return false;
1182 bool InquireUnitState::Inquire(
1183 InquiryKeywordHash inquiry, std::int64_t, bool &result) {
1184 switch (inquiry) {
1185 case HashInquiryKeyword("PENDING"):
1186 result = false; // asynchronous I/O is not implemented
1187 return true;
1188 default:
1189 BadInquiryKeywordHashCrash(inquiry);
1190 return false;
1194 bool InquireUnitState::Inquire(
1195 InquiryKeywordHash inquiry, std::int64_t &result) {
1196 switch (inquiry) {
1197 case HashInquiryKeyword("NEXTREC"):
1198 if (unit().access == Access::Direct) {
1199 result = unit().currentRecordNumber;
1201 return true;
1202 case HashInquiryKeyword("NUMBER"):
1203 result = unit().unitNumber();
1204 return true;
1205 case HashInquiryKeyword("POS"):
1206 result = unit().InquirePos();
1207 return true;
1208 case HashInquiryKeyword("RECL"):
1209 if (!unit().IsConnected()) {
1210 result = -1;
1211 } else if (unit().access == Access::Stream) {
1212 result = -2;
1213 } else if (unit().openRecl) {
1214 result = *unit().openRecl;
1215 } else {
1216 result = std::numeric_limits<std::int32_t>::max();
1218 return true;
1219 case HashInquiryKeyword("SIZE"):
1220 result = -1;
1221 if (unit().IsConnected()) {
1222 if (auto size{unit().knownSize()}) {
1223 result = *size;
1226 return true;
1227 default:
1228 BadInquiryKeywordHashCrash(inquiry);
1229 return false;
1233 InquireNoUnitState::InquireNoUnitState(
1234 const char *sourceFile, int sourceLine, int badUnitNumber)
1235 : NoUnitIoStatementState{*this, sourceFile, sourceLine, badUnitNumber} {}
1237 bool InquireNoUnitState::Inquire(
1238 InquiryKeywordHash inquiry, char *result, std::size_t length) {
1239 switch (inquiry) {
1240 case HashInquiryKeyword("ACCESS"):
1241 case HashInquiryKeyword("ACTION"):
1242 case HashInquiryKeyword("ASYNCHRONOUS"):
1243 case HashInquiryKeyword("BLANK"):
1244 case HashInquiryKeyword("CARRIAGECONTROL"):
1245 case HashInquiryKeyword("CONVERT"):
1246 case HashInquiryKeyword("DECIMAL"):
1247 case HashInquiryKeyword("DELIM"):
1248 case HashInquiryKeyword("FORM"):
1249 case HashInquiryKeyword("NAME"):
1250 case HashInquiryKeyword("PAD"):
1251 case HashInquiryKeyword("POSITION"):
1252 case HashInquiryKeyword("ROUND"):
1253 case HashInquiryKeyword("SIGN"):
1254 ToFortranDefaultCharacter(result, length, "UNDEFINED");
1255 return true;
1256 case HashInquiryKeyword("DIRECT"):
1257 case HashInquiryKeyword("ENCODING"):
1258 case HashInquiryKeyword("FORMATTED"):
1259 case HashInquiryKeyword("READ"):
1260 case HashInquiryKeyword("READWRITE"):
1261 case HashInquiryKeyword("SEQUENTIAL"):
1262 case HashInquiryKeyword("STREAM"):
1263 case HashInquiryKeyword("WRITE"):
1264 case HashInquiryKeyword("UNFORMATTED"):
1265 ToFortranDefaultCharacter(result, length, "UNKNOWN");
1266 return true;
1267 default:
1268 BadInquiryKeywordHashCrash(inquiry);
1269 return false;
1273 bool InquireNoUnitState::Inquire(InquiryKeywordHash inquiry, bool &result) {
1274 switch (inquiry) {
1275 case HashInquiryKeyword("EXIST"):
1276 result = badUnitNumber() >= 0;
1277 return true;
1278 case HashInquiryKeyword("NAMED"):
1279 case HashInquiryKeyword("OPENED"):
1280 case HashInquiryKeyword("PENDING"):
1281 result = false;
1282 return true;
1283 default:
1284 BadInquiryKeywordHashCrash(inquiry);
1285 return false;
1289 bool InquireNoUnitState::Inquire(
1290 InquiryKeywordHash inquiry, std::int64_t, bool &result) {
1291 switch (inquiry) {
1292 case HashInquiryKeyword("PENDING"):
1293 result = false;
1294 return true;
1295 default:
1296 BadInquiryKeywordHashCrash(inquiry);
1297 return false;
1301 bool InquireNoUnitState::Inquire(
1302 InquiryKeywordHash inquiry, std::int64_t &result) {
1303 switch (inquiry) {
1304 case HashInquiryKeyword("NUMBER"):
1305 result = badUnitNumber();
1306 return true;
1307 case HashInquiryKeyword("NEXTREC"):
1308 case HashInquiryKeyword("POS"):
1309 case HashInquiryKeyword("RECL"):
1310 case HashInquiryKeyword("SIZE"):
1311 result = -1;
1312 return true;
1313 default:
1314 BadInquiryKeywordHashCrash(inquiry);
1315 return false;
1319 InquireUnconnectedFileState::InquireUnconnectedFileState(
1320 OwningPtr<char> &&path, const char *sourceFile, int sourceLine)
1321 : NoUnitIoStatementState{*this, sourceFile, sourceLine}, path_{std::move(
1322 path)} {}
1324 bool InquireUnconnectedFileState::Inquire(
1325 InquiryKeywordHash inquiry, char *result, std::size_t length) {
1326 const char *str{nullptr};
1327 switch (inquiry) {
1328 case HashInquiryKeyword("ACCESS"):
1329 case HashInquiryKeyword("ACTION"):
1330 case HashInquiryKeyword("ASYNCHRONOUS"):
1331 case HashInquiryKeyword("BLANK"):
1332 case HashInquiryKeyword("CARRIAGECONTROL"):
1333 case HashInquiryKeyword("CONVERT"):
1334 case HashInquiryKeyword("DECIMAL"):
1335 case HashInquiryKeyword("DELIM"):
1336 case HashInquiryKeyword("FORM"):
1337 case HashInquiryKeyword("PAD"):
1338 case HashInquiryKeyword("POSITION"):
1339 case HashInquiryKeyword("ROUND"):
1340 case HashInquiryKeyword("SIGN"):
1341 str = "UNDEFINED";
1342 break;
1343 case HashInquiryKeyword("DIRECT"):
1344 case HashInquiryKeyword("ENCODING"):
1345 case HashInquiryKeyword("FORMATTED"):
1346 case HashInquiryKeyword("SEQUENTIAL"):
1347 case HashInquiryKeyword("STREAM"):
1348 case HashInquiryKeyword("UNFORMATTED"):
1349 str = "UNKNONN";
1350 break;
1351 case HashInquiryKeyword("READ"):
1352 str =
1353 IsExtant(path_.get()) ? MayRead(path_.get()) ? "YES" : "NO" : "UNKNOWN";
1354 break;
1355 case HashInquiryKeyword("READWRITE"):
1356 str = IsExtant(path_.get()) ? MayReadAndWrite(path_.get()) ? "YES" : "NO"
1357 : "UNKNOWN";
1358 break;
1359 case HashInquiryKeyword("WRITE"):
1360 str = IsExtant(path_.get()) ? MayWrite(path_.get()) ? "YES" : "NO"
1361 : "UNKNOWN";
1362 break;
1363 case HashInquiryKeyword("NAME"):
1364 str = path_.get();
1365 if (!str) {
1366 return true; // result is undefined
1368 break;
1370 if (str) {
1371 ToFortranDefaultCharacter(result, length, str);
1372 return true;
1373 } else {
1374 BadInquiryKeywordHashCrash(inquiry);
1375 return false;
1379 bool InquireUnconnectedFileState::Inquire(
1380 InquiryKeywordHash inquiry, bool &result) {
1381 switch (inquiry) {
1382 case HashInquiryKeyword("EXIST"):
1383 result = IsExtant(path_.get());
1384 return true;
1385 case HashInquiryKeyword("NAMED"):
1386 result = true;
1387 return true;
1388 case HashInquiryKeyword("OPENED"):
1389 result = false;
1390 return true;
1391 case HashInquiryKeyword("PENDING"):
1392 result = false;
1393 return true;
1394 default:
1395 BadInquiryKeywordHashCrash(inquiry);
1396 return false;
1400 bool InquireUnconnectedFileState::Inquire(
1401 InquiryKeywordHash inquiry, std::int64_t, bool &result) {
1402 switch (inquiry) {
1403 case HashInquiryKeyword("PENDING"):
1404 result = false;
1405 return true;
1406 default:
1407 BadInquiryKeywordHashCrash(inquiry);
1408 return false;
1412 bool InquireUnconnectedFileState::Inquire(
1413 InquiryKeywordHash inquiry, std::int64_t &result) {
1414 switch (inquiry) {
1415 case HashInquiryKeyword("NEXTREC"):
1416 case HashInquiryKeyword("NUMBER"):
1417 case HashInquiryKeyword("POS"):
1418 case HashInquiryKeyword("RECL"):
1419 result = -1;
1420 return true;
1421 case HashInquiryKeyword("SIZE"):
1422 result = SizeInBytes(path_.get());
1423 return true;
1424 default:
1425 BadInquiryKeywordHashCrash(inquiry);
1426 return false;
1430 InquireIOLengthState::InquireIOLengthState(
1431 const char *sourceFile, int sourceLine)
1432 : NoUnitIoStatementState{*this, sourceFile, sourceLine} {}
1434 bool InquireIOLengthState::Emit(const char *, std::size_t bytes, std::size_t) {
1435 bytes_ += bytes;
1436 return true;
1439 int ErroneousIoStatementState::EndIoStatement() {
1440 SignalPendingError();
1441 if (unit_) {
1442 unit_->EndIoStatement();
1444 return IoStatementBase::EndIoStatement();
1447 } // namespace Fortran::runtime::io