1 //===-- runtime/io-stmt.cpp -----------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
10 #include "connection.h"
11 #include "emit-encoded.h"
16 #include "flang/Runtime/memory.h"
21 #include <type_traits>
23 namespace Fortran::runtime::io
{
25 bool IoStatementBase::Emit(const char *, std::size_t, std::size_t) {
29 std::size_t IoStatementBase::GetNextInputBytes(const char *&p
) {
34 bool IoStatementBase::AdvanceRecord(int) { return false; }
36 void IoStatementBase::BackspaceRecord() {}
38 bool IoStatementBase::Receive(char *, std::size_t, std::size_t) {
42 std::optional
<DataEdit
> IoStatementBase::GetNextDataEdit(
43 IoStatementState
&, int) {
47 ExternalFileUnit
*IoStatementBase::GetExternalFileUnit() const {
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) {
63 bool IoStatementBase::Inquire(InquiryKeywordHash
, bool &) { return false; }
65 bool IoStatementBase::Inquire(InquiryKeywordHash
, std::int64_t, bool &) {
69 bool IoStatementBase::Inquire(InquiryKeywordHash
, std::int64_t &) {
73 std::int64_t IoStatementBase::InquirePos() { return 0; }
75 void IoStatementBase::BadInquiryKeywordHashCrash(InquiryKeywordHash inquiry
) {
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");
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
) {
110 if (!unit_
.AdvanceRecord(*this)) {
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()};
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
,
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
,
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() {
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();
203 ConnectionState
&ExternalIoStatementBase::GetConnectionState() { return unit_
; }
205 int ExternalIoStatementBase::EndIoStatement() {
207 auto result
{IoStatementBase::EndIoStatement()};
208 unit_
.EndIoStatement(); // annihilates *this in unit_.u_
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()) {
230 if (access_
&& *access_
== Access::Direct
) {
231 SignalError("POSITION= may not be set with ACCESS='DIRECT'");
235 if (status_
) { // 12.5.6.10
236 if ((*status_
== OpenStatus::New
|| *status_
== OpenStatus::Replace
) &&
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
256 unit().OpenAnonymousUnit(
257 status_
, action_
, position_
.value_or(Position::AsIs
), convert_
, *this);
260 if (*access_
!= unit().access
) {
262 SignalError("ACCESS= may not be changed on an open unit");
267 unit().access
= *access_
;
270 if (!unit().isUnformatted
) {
271 unit().isUnformatted
= isUnformatted_
;
273 if (isUnformatted_
&& *isUnformatted_
!= *unit().isUnformatted
) {
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() {
293 return ExternalIoStatementBase::EndIoStatement();
296 int CloseStatementState::EndIoStatement() {
298 int result
{ExternalIoStatementBase::EndIoStatement()};
299 unit().CloseUnit(status_
, *this);
300 unit().DestroyClosed();
304 void NoUnitIoStatementState::CompleteOperation() {
305 SignalPendingError();
306 IoStatementBase::CompleteOperation();
309 int NoUnitIoStatementState::EndIoStatement() {
311 auto result
{IoStatementBase::EndIoStatement()};
316 template <Direction
DIR>
317 ExternalIoStatementState
<DIR>::ExternalIoStatementState(
318 ExternalFileUnit
&unit
, const char *sourceFile
, int sourceLine
)
319 : ExternalIoStatementBase
{unit
, sourceFile
, sourceLine
}, mutableModes_
{
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()) {
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
;
341 FinishReadingRecord();
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
;
351 unit().AdvanceRecord(*this);
353 unit().FlushIfTerminal(*this);
355 return IoStatementBase::CompleteOperation();
358 template <Direction
DIR> int ExternalIoStatementState
<DIR>::EndIoStatement() {
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
) {
380 if (!unit().AdvanceRecord(*this)) {
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);
406 Crash("ExternalIoStatementState<Direction::Output>::BeginReadingRecord() "
412 template <Direction
DIR>
413 void ExternalIoStatementState
<DIR>::FinishReadingRecord() {
414 if constexpr (DIR == Direction::Input
) {
415 unit().FinishReadingRecord(*this);
417 Crash("ExternalIoStatementState<Direction::Output>::FinishReadingRecord() "
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()) {
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() {
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(); },
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());
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
)};
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
)}) {
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
);
546 return *reinterpret_cast<const char32_t
*>(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
570 case '\n': // for stream access
573 if (!(edit
.modes
.editingFlags
& decimalComma
)) {
578 if (edit
.modes
.editingFlags
& decimalComma
) {
586 HandleRelativePosition(byteCount
);
590 } else if (*remaining
> 0) {
591 if (auto next
{GetCurrentChar(byteCount
)}) {
592 if (byteCount
> static_cast<std::size_t>(*remaining
)) {
595 *remaining
-= byteCount
;
596 HandleRelativePosition(byteCount
);
600 if (CheckForEndOfRecord(0)) { // do padding
602 return std::optional
<char32_t
>{' '};
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
) >=
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.
627 } else if (!modes
.pad
) {
628 handler
.SignalError(IostatRecordReadOverrun
);
630 return modes
.pad
; // PAD='YES'
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
);
668 GetIoErrorHandler().Crash("IoStatementState::GotChar() called for "
669 "statement that is not formatted input");
674 FormattedIoStatementState
<Direction::Input
>::GetEditDescriptorChars() const {
678 void FormattedIoStatementState
<Direction::Input
>::GotChar(int n
) {
682 bool ListDirectedStatementState
<Direction::Output
>::EmitLeadingSpaceOrAdvance(
683 IoStatementState
&io
, std::size_t length
, bool isCharacter
) {
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();
695 return EmitAscii(io
, " ", 1);
700 std::optional
<DataEdit
>
701 ListDirectedStatementState
<Direction::Output
>::GetNextDataEdit(
702 IoStatementState
&io
, int maxRepeat
) {
704 edit
.descriptor
= DataEdit::ListDirected
;
705 edit
.repeat
= maxRepeat
;
706 edit
.modes
= io
.mutableModes();
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()};
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
;
724 if (edit
.modes
.editingFlags
& decimalComma
) {
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_
) {
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_
) {
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
);
770 edit
.descriptor
= DataEdit::ListDirectedNullValue
;
773 if (*ch
== comma
) { // separator: null value
774 edit
.descriptor
= DataEdit::ListDirectedNullValue
;
777 if (imaginaryPart_
) { // can't repeat components
780 if (*ch
>= '0' && *ch
<= '9') { // look for "r*" repetition count
781 auto start
{connection
.positionInRecord
};
784 static auto constexpr clamp
{(std::numeric_limits
<int>::max() - '9') / 10};
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*/
798 edit
.descriptor
= DataEdit::ListDirectedNullValue
;
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
== '(') {
815 io
.HandleRelativePosition(byteCount
);
816 edit
.descriptor
= DataEdit::ListDirectedRealPart
;
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 "
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() {
853 auto result
{IoStatementBase::EndIoStatement()};
854 child_
.EndIoStatement(); // annihilates *this in child_.u_
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
,
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() {
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()) {
940 ExternalFileUnit
&ext
{unit()};
943 ext
.FlushOutput(*this);
944 std::fflush(nullptr); // flushes C stdio output streams (12.9(2))
947 ext
.BackspaceRecord(*this);
956 break; // handled in io-api.cpp BeginWait
958 return IoStatementBase::CompleteOperation();
961 int ExternalMiscIoStatementState::EndIoStatement() {
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 "
978 const char *str
{nullptr};
980 case HashInquiryKeyword("ACCESS"):
981 if (!unit().IsConnected()) {
984 switch (unit().access
) {
985 case Access::Sequential
:
997 case HashInquiryKeyword("ACTION"):
998 str
= !unit().IsConnected() ? "UNDEFINED"
999 : unit().mayWrite() ? unit().mayRead() ? "READWRITE" : "WRITE"
1002 case HashInquiryKeyword("ASYNCHRONOUS"):
1003 str
= !unit().IsConnected() ? "UNDEFINED"
1004 : unit().mayAsynchronous() ? "YES"
1007 case HashInquiryKeyword("BLANK"):
1008 str
= !unit().IsConnected() || unit().isUnformatted
.value_or(true)
1010 : mutableModes().editingFlags
& blankZero
? "ZERO"
1013 case HashInquiryKeyword("CARRIAGECONTROL"):
1016 case HashInquiryKeyword("CONVERT"):
1017 str
= unit().swapEndianness() ? "SWAP" : "NATIVE";
1019 case HashInquiryKeyword("DECIMAL"):
1020 str
= !unit().IsConnected() || unit().isUnformatted
.value_or(true)
1022 : mutableModes().editingFlags
& decimalComma
? "COMMA"
1025 case HashInquiryKeyword("DELIM"):
1026 if (!unit().IsConnected() || unit().isUnformatted
.value_or(true)) {
1029 switch (mutableModes().delim
) {
1042 case HashInquiryKeyword("DIRECT"):
1043 str
= !unit().IsConnected() ? "UNKNOWN"
1044 : unit().access
== Access::Direct
||
1045 (unit().mayPosition() && unit().openRecl
)
1049 case HashInquiryKeyword("ENCODING"):
1050 str
= !unit().IsConnected() ? "UNKNOWN"
1051 : unit().isUnformatted
.value_or(true) ? "UNDEFINED"
1052 : unit().isUTF8
? "UTF-8"
1055 case HashInquiryKeyword("FORM"):
1056 str
= !unit().IsConnected() || !unit().isUnformatted
? "UNDEFINED"
1057 : *unit().isUnformatted
? "UNFORMATTED"
1060 case HashInquiryKeyword("FORMATTED"):
1061 str
= !unit().IsConnected() ? "UNDEFINED"
1062 : !unit().isUnformatted
? "UNKNOWN"
1063 : *unit().isUnformatted
? "NO"
1066 case HashInquiryKeyword("NAME"):
1067 str
= unit().path();
1069 return true; // result is undefined
1072 case HashInquiryKeyword("PAD"):
1073 str
= !unit().IsConnected() || unit().isUnformatted
.value_or(true)
1075 : mutableModes().pad
? "YES"
1078 case HashInquiryKeyword("POSITION"):
1079 if (!unit().IsConnected() || unit().access
== Access::Direct
) {
1082 switch (unit().InquirePosition()) {
1083 case Position::Rewind
:
1086 case Position::Append
:
1089 case Position::AsIs
:
1095 case HashInquiryKeyword("READ"):
1096 str
= !unit().IsConnected() ? "UNDEFINED" : unit().mayRead() ? "YES" : "NO";
1098 case HashInquiryKeyword("READWRITE"):
1099 str
= !unit().IsConnected() ? "UNDEFINED"
1100 : unit().mayRead() && unit().mayWrite() ? "YES"
1103 case HashInquiryKeyword("ROUND"):
1104 if (!unit().IsConnected() || unit().isUnformatted
.value_or(true)) {
1107 switch (mutableModes().round
) {
1108 case decimal::FortranRounding::RoundNearest
:
1111 case decimal::FortranRounding::RoundUp
:
1114 case decimal::FortranRounding::RoundDown
:
1117 case decimal::FortranRounding::RoundToZero
:
1120 case decimal::FortranRounding::RoundCompatible
:
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"
1133 case HashInquiryKeyword("SIGN"):
1134 str
= !unit().IsConnected() || unit().isUnformatted
.value_or(true)
1136 : mutableModes().editingFlags
& signPlus
? "PLUS"
1139 case HashInquiryKeyword("STREAM"):
1140 str
= !unit().IsConnected() ? "UNKNOWN"
1141 : unit().access
== Access::Stream
? "YES"
1144 case HashInquiryKeyword("UNFORMATTED"):
1145 str
= !unit().IsConnected() || !unit().isUnformatted
? "UNKNOWN"
1146 : *unit().isUnformatted
? "YES"
1149 case HashInquiryKeyword("WRITE"):
1150 str
= !unit().IsConnected() ? "UNKNOWN" : unit().mayWrite() ? "YES" : "NO";
1154 ToFortranDefaultCharacter(result
, length
, str
);
1157 BadInquiryKeywordHashCrash(inquiry
);
1162 bool InquireUnitState::Inquire(InquiryKeywordHash inquiry
, bool &result
) {
1164 case HashInquiryKeyword("EXIST"):
1167 case HashInquiryKeyword("NAMED"):
1168 result
= unit().path() != nullptr;
1170 case HashInquiryKeyword("OPENED"):
1171 result
= unit().IsConnected();
1173 case HashInquiryKeyword("PENDING"):
1174 result
= false; // asynchronous I/O is not implemented
1177 BadInquiryKeywordHashCrash(inquiry
);
1182 bool InquireUnitState::Inquire(
1183 InquiryKeywordHash inquiry
, std::int64_t, bool &result
) {
1185 case HashInquiryKeyword("PENDING"):
1186 result
= false; // asynchronous I/O is not implemented
1189 BadInquiryKeywordHashCrash(inquiry
);
1194 bool InquireUnitState::Inquire(
1195 InquiryKeywordHash inquiry
, std::int64_t &result
) {
1197 case HashInquiryKeyword("NEXTREC"):
1198 if (unit().access
== Access::Direct
) {
1199 result
= unit().currentRecordNumber
;
1202 case HashInquiryKeyword("NUMBER"):
1203 result
= unit().unitNumber();
1205 case HashInquiryKeyword("POS"):
1206 result
= unit().InquirePos();
1208 case HashInquiryKeyword("RECL"):
1209 if (!unit().IsConnected()) {
1211 } else if (unit().access
== Access::Stream
) {
1213 } else if (unit().openRecl
) {
1214 result
= *unit().openRecl
;
1216 result
= std::numeric_limits
<std::int32_t>::max();
1219 case HashInquiryKeyword("SIZE"):
1221 if (unit().IsConnected()) {
1222 if (auto size
{unit().knownSize()}) {
1228 BadInquiryKeywordHashCrash(inquiry
);
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
) {
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");
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");
1268 BadInquiryKeywordHashCrash(inquiry
);
1273 bool InquireNoUnitState::Inquire(InquiryKeywordHash inquiry
, bool &result
) {
1275 case HashInquiryKeyword("EXIST"):
1276 result
= badUnitNumber() >= 0;
1278 case HashInquiryKeyword("NAMED"):
1279 case HashInquiryKeyword("OPENED"):
1280 case HashInquiryKeyword("PENDING"):
1284 BadInquiryKeywordHashCrash(inquiry
);
1289 bool InquireNoUnitState::Inquire(
1290 InquiryKeywordHash inquiry
, std::int64_t, bool &result
) {
1292 case HashInquiryKeyword("PENDING"):
1296 BadInquiryKeywordHashCrash(inquiry
);
1301 bool InquireNoUnitState::Inquire(
1302 InquiryKeywordHash inquiry
, std::int64_t &result
) {
1304 case HashInquiryKeyword("NUMBER"):
1305 result
= badUnitNumber();
1307 case HashInquiryKeyword("NEXTREC"):
1308 case HashInquiryKeyword("POS"):
1309 case HashInquiryKeyword("RECL"):
1310 case HashInquiryKeyword("SIZE"):
1314 BadInquiryKeywordHashCrash(inquiry
);
1319 InquireUnconnectedFileState::InquireUnconnectedFileState(
1320 OwningPtr
<char> &&path
, const char *sourceFile
, int sourceLine
)
1321 : NoUnitIoStatementState
{*this, sourceFile
, sourceLine
}, path_
{std::move(
1324 bool InquireUnconnectedFileState::Inquire(
1325 InquiryKeywordHash inquiry
, char *result
, std::size_t length
) {
1326 const char *str
{nullptr};
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"):
1343 case HashInquiryKeyword("DIRECT"):
1344 case HashInquiryKeyword("ENCODING"):
1345 case HashInquiryKeyword("FORMATTED"):
1346 case HashInquiryKeyword("SEQUENTIAL"):
1347 case HashInquiryKeyword("STREAM"):
1348 case HashInquiryKeyword("UNFORMATTED"):
1351 case HashInquiryKeyword("READ"):
1353 IsExtant(path_
.get()) ? MayRead(path_
.get()) ? "YES" : "NO" : "UNKNOWN";
1355 case HashInquiryKeyword("READWRITE"):
1356 str
= IsExtant(path_
.get()) ? MayReadAndWrite(path_
.get()) ? "YES" : "NO"
1359 case HashInquiryKeyword("WRITE"):
1360 str
= IsExtant(path_
.get()) ? MayWrite(path_
.get()) ? "YES" : "NO"
1363 case HashInquiryKeyword("NAME"):
1366 return true; // result is undefined
1371 ToFortranDefaultCharacter(result
, length
, str
);
1374 BadInquiryKeywordHashCrash(inquiry
);
1379 bool InquireUnconnectedFileState::Inquire(
1380 InquiryKeywordHash inquiry
, bool &result
) {
1382 case HashInquiryKeyword("EXIST"):
1383 result
= IsExtant(path_
.get());
1385 case HashInquiryKeyword("NAMED"):
1388 case HashInquiryKeyword("OPENED"):
1391 case HashInquiryKeyword("PENDING"):
1395 BadInquiryKeywordHashCrash(inquiry
);
1400 bool InquireUnconnectedFileState::Inquire(
1401 InquiryKeywordHash inquiry
, std::int64_t, bool &result
) {
1403 case HashInquiryKeyword("PENDING"):
1407 BadInquiryKeywordHashCrash(inquiry
);
1412 bool InquireUnconnectedFileState::Inquire(
1413 InquiryKeywordHash inquiry
, std::int64_t &result
) {
1415 case HashInquiryKeyword("NEXTREC"):
1416 case HashInquiryKeyword("NUMBER"):
1417 case HashInquiryKeyword("POS"):
1418 case HashInquiryKeyword("RECL"):
1421 case HashInquiryKeyword("SIZE"):
1422 result
= SizeInBytes(path_
.get());
1425 BadInquiryKeywordHashCrash(inquiry
);
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) {
1439 int ErroneousIoStatementState::EndIoStatement() {
1440 SignalPendingError();
1442 unit_
->EndIoStatement();
1444 return IoStatementBase::EndIoStatement();
1447 } // namespace Fortran::runtime::io